diff --git a/.DS_Store b/.DS_Store index c8b0e23..31565a1 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/asitop/asitop.py b/asitop/asitop.py index 0845798..12ba450 100644 --- a/asitop/asitop.py +++ b/asitop/asitop.py @@ -2,7 +2,12 @@ import argparse from collections import deque from dashing import VSplit, HSplit, HGauge, HChart, VGauge -from .utils import * +from .utils import (build_enqueue_thread, + clear_console, + get_ram_metrics_dict, + get_soc_info, + parse_powermetrics, + run_powermetrics_process) parser = argparse.ArgumentParser( description='asitop: Performance monitoring CLI tool for Apple Silicon') @@ -14,8 +19,6 @@ help='Interval for averaged values (seconds)') parser.add_argument('--show_cores', type=bool, default=False, help='Choose show cores mode') -parser.add_argument('--max_count', type=int, default=0, - help='Max show count to restart powermetrics') args = parser.parse_args() @@ -142,18 +145,16 @@ def main(): print("\n[2/3] Starting powermetrics process\n") - timecode = str(int(time.time())) - - powermetrics_process = run_powermetrics_process(timecode, - interval=args.interval * 1000) + powermetrics_process = run_powermetrics_process(interval=args.interval * 1000) + queue, _thread = build_enqueue_thread(powermetrics_process.stdout) print("\n[3/3] Waiting for first reading...\n") def get_reading(wait=0.1): - ready = parse_powermetrics(timecode=timecode) + ready = parse_powermetrics(queue) while not ready: time.sleep(wait) - ready = parse_powermetrics(timecode=timecode) + ready = parse_powermetrics(queue) return ready ready = get_reading() @@ -169,18 +170,9 @@ def get_avg(inlist): clear_console() - count=0 try: while True: - if args.max_count > 0: - if count >= args.max_count: - count = 0 - powermetrics_process.terminate() - timecode = str(int(time.time())) - powermetrics_process = run_powermetrics_process( - timecode, interval=args.interval * 1000) - count += 1 - ready = parse_powermetrics(timecode=timecode) + ready = parse_powermetrics(queue) if ready: cpu_metrics_dict, gpu_metrics_dict, thermal_pressure, bandwidth_metrics, timestamp = ready @@ -401,7 +393,6 @@ def get_avg(inlist): ui.display() - time.sleep(args.interval) except KeyboardInterrupt: print("Stopping...") diff --git a/asitop/utils.py b/asitop/utils.py index 1e7b9ce..5558fee 100644 --- a/asitop/utils.py +++ b/asitop/utils.py @@ -1,19 +1,18 @@ import os -import glob import subprocess +from queue import LifoQueue from subprocess import PIPE +from threading import Thread import psutil from .parsers import * import plistlib -def parse_powermetrics(path='/tmp/asitop_powermetrics', timecode="0"): - data = None +def parse_powermetrics(queue): try: - with open(path+timecode, 'rb') as fp: - data = fp.read() - data = data.split(b'\x00') - powermetrics_parse = plistlib.loads(data[-1]) + # a Last in First out queue + data = queue.get() + powermetrics_parse = plistlib.loads(data) thermal_pressure = parse_thermal_pressure(powermetrics_parse) cpu_metrics_dict = parse_cpu_metrics(powermetrics_parse) gpu_metrics_dict = parse_gpu_metrics(powermetrics_parse) @@ -43,20 +42,44 @@ def clear_console(): def convert_to_GB(value): return round(value/1024/1024/1024, 1) - -def run_powermetrics_process(timecode, nice=10, interval=1000): +def enqueue_powermetrics(buffered_reader, queue_in): + """ + a helper to convert the output of `powermetrics` + into list of plist strings. + + buffered_reader: stdout of the `powermetrics` process + queue_in: a LIFO queue, will also be provided to the parser + """ + buffer = b'' + for line in buffered_reader: + # magic string + if line.startswith(b"\x00"): + queue_in.put(buffer) + buffer = line[1:] + else: + buffer += line + +def build_enqueue_thread(powermetrics_stdout): + """ + build a thread to run enqueue_powermetrics() + returns: + queue: the LIFO queue, containing plist strings + equeue_thread: the identifier of the thread + """ + queue = LifoQueue() + enqueue_thread = Thread(target=enqueue_powermetrics, + args=(powermetrics_stdout, queue)) + enqueue_thread.start() + return queue, enqueue_thread + +def run_powermetrics_process(nice=10, interval=1000): #ver, *_ = platform.mac_ver() #major_ver = int(ver.split(".")[0]) - for tmpf in glob.glob("/tmp/asitop_powermetrics*"): - os.remove(tmpf) - output_file_flag = "-o" command = " ".join([ "sudo nice -n", str(nice), "powermetrics", "--samplers cpu_power,gpu_power,thermal", - output_file_flag, - "/tmp/asitop_powermetrics"+timecode, "-f plist", "-i", str(interval)