msiltop 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- msiltop/__init__.py +0 -0
- msiltop/msiltop.py +932 -0
- msiltop/parsers.py +62 -0
- msiltop/soc_info.json +18 -0
- msiltop/utils.py +161 -0
- msiltop-0.2.0.dist-info/METADATA +276 -0
- msiltop-0.2.0.dist-info/RECORD +10 -0
- msiltop-0.2.0.dist-info/WHEEL +4 -0
- msiltop-0.2.0.dist-info/entry_points.txt +2 -0
- msiltop-0.2.0.dist-info/licenses/LICENSE +22 -0
msiltop/parsers.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
def parse_thermal_pressure(powermetrics_parse):
|
|
4
|
+
return powermetrics_parse["thermal_pressure"]
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def parse_cpu_metrics(powermetrics_parse):
|
|
8
|
+
e_core = []
|
|
9
|
+
p_core = []
|
|
10
|
+
cpu_metrics = powermetrics_parse["processor"]
|
|
11
|
+
cpu_metric_dict = {}
|
|
12
|
+
# cpu_clusters
|
|
13
|
+
cpu_clusters = cpu_metrics["clusters"]
|
|
14
|
+
for cluster in cpu_clusters:
|
|
15
|
+
name = cluster["name"]
|
|
16
|
+
cluster_cpus = cluster["cpus"]
|
|
17
|
+
# Calculate total active time across all cores instead of averaging
|
|
18
|
+
# This better matches Activity Monitor's methodology
|
|
19
|
+
total_active = sum(map(lambda x: 1.0 - x["idle_ratio"], cluster_cpus))
|
|
20
|
+
# Express as percentage of total cluster capacity (number of cores = 100% each)
|
|
21
|
+
cpu_metric_dict[name+"_active"] = int(
|
|
22
|
+
100 * total_active / len(cluster_cpus)
|
|
23
|
+
)
|
|
24
|
+
for cpu in cluster["cpus"]:
|
|
25
|
+
name = 'E-Cluster' if name[0] == 'E' else 'P-Cluster'
|
|
26
|
+
core = e_core if name[0] == 'E' else p_core
|
|
27
|
+
core.append(cpu["cpu"])
|
|
28
|
+
cpu_idle_ratio = cpu["idle_ratio"]
|
|
29
|
+
|
|
30
|
+
if math.isnan(cpu_idle_ratio):
|
|
31
|
+
cpu_idle_ratio = 0
|
|
32
|
+
cpu_metric_dict[name + str(cpu["cpu"]) + "_active"] = int((1 - cpu_idle_ratio) * 100)
|
|
33
|
+
|
|
34
|
+
cpu_metric_dict["e_core"] = e_core
|
|
35
|
+
cpu_metric_dict["p_core"] = p_core
|
|
36
|
+
if "E-Cluster_active" not in cpu_metric_dict:
|
|
37
|
+
# M1 Ultra
|
|
38
|
+
cpu_metric_dict["E-Cluster_active"] = int(
|
|
39
|
+
(cpu_metric_dict["E0-Cluster_active"] + cpu_metric_dict["E1-Cluster_active"])/2)
|
|
40
|
+
if "P-Cluster_active" not in cpu_metric_dict:
|
|
41
|
+
if "P2-Cluster_active" in cpu_metric_dict:
|
|
42
|
+
# M1 Ultra
|
|
43
|
+
cpu_metric_dict["P-Cluster_active"] = int((cpu_metric_dict["P0-Cluster_active"] + cpu_metric_dict["P1-Cluster_active"] +
|
|
44
|
+
cpu_metric_dict["P2-Cluster_active"] + cpu_metric_dict["P3-Cluster_active"]) / 4)
|
|
45
|
+
else:
|
|
46
|
+
cpu_metric_dict["P-Cluster_active"] = int(
|
|
47
|
+
(cpu_metric_dict["P0-Cluster_active"] + cpu_metric_dict["P1-Cluster_active"])/2)
|
|
48
|
+
# power
|
|
49
|
+
cpu_metric_dict["ane_W"] = cpu_metrics["ane_energy"]/1000
|
|
50
|
+
#cpu_metric_dict["dram_W"] = cpu_metrics["dram_energy"]/1000
|
|
51
|
+
cpu_metric_dict["cpu_W"] = cpu_metrics["cpu_energy"]/1000
|
|
52
|
+
cpu_metric_dict["gpu_W"] = cpu_metrics["gpu_energy"]/1000
|
|
53
|
+
cpu_metric_dict["package_W"] = cpu_metrics["combined_power"]/1000
|
|
54
|
+
return cpu_metric_dict
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def parse_gpu_metrics(powermetrics_parse):
|
|
58
|
+
gpu_metrics = powermetrics_parse["gpu"]
|
|
59
|
+
gpu_metrics_dict = {
|
|
60
|
+
"active": int((1 - gpu_metrics["idle_ratio"])*100),
|
|
61
|
+
}
|
|
62
|
+
return gpu_metrics_dict
|
msiltop/soc_info.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Apple M1 Max": {"cpu_max_power": 30, "gpu_max_power": 60},
|
|
3
|
+
"Apple M1 Pro": {"cpu_max_power": 30, "gpu_max_power": 30},
|
|
4
|
+
"Apple M1": {"cpu_max_power": 20, "gpu_max_power": 20},
|
|
5
|
+
"Apple M1 Ultra": {"cpu_max_power": 60, "gpu_max_power": 120},
|
|
6
|
+
"Apple M2": {"cpu_max_power": 25, "gpu_max_power": 15},
|
|
7
|
+
"Apple M2 Pro": {"cpu_max_power": 30, "gpu_max_power": 19},
|
|
8
|
+
"Apple M2 Max": {"cpu_max_power": 38, "gpu_max_power": 38},
|
|
9
|
+
"Apple M2 Ultra": {"cpu_max_power": 76, "gpu_max_power": 76},
|
|
10
|
+
"Apple M3": {"cpu_max_power": 22, "gpu_max_power": 13},
|
|
11
|
+
"Apple M3 Pro": {"cpu_max_power": 37, "gpu_max_power": 19},
|
|
12
|
+
"Apple M3 Max": {"cpu_max_power": 54, "gpu_max_power": 47},
|
|
13
|
+
"Apple M3 Ultra": {"cpu_max_power": 108, "gpu_max_power": 94},
|
|
14
|
+
"Apple M4": {"cpu_max_power": 22, "gpu_max_power": 13},
|
|
15
|
+
"Apple M4 Pro": {"cpu_max_power": 42, "gpu_max_power": 23},
|
|
16
|
+
"Apple M4 Max": {"cpu_max_power": 68, "gpu_max_power": 57},
|
|
17
|
+
"Apple M4 Ultra": {"cpu_max_power": 136, "gpu_max_power": 114}
|
|
18
|
+
}
|
msiltop/utils.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import glob
|
|
3
|
+
import subprocess
|
|
4
|
+
from subprocess import PIPE
|
|
5
|
+
import psutil
|
|
6
|
+
from .parsers import *
|
|
7
|
+
import plistlib
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def parse_powermetrics(path='/tmp/msiltop_powermetrics', timecode="0", tail_bytes=1024 * 512):
|
|
12
|
+
try:
|
|
13
|
+
with open(path + timecode, 'rb') as fp:
|
|
14
|
+
fp.seek(0, os.SEEK_END)
|
|
15
|
+
size = fp.tell()
|
|
16
|
+
fp.seek(max(size - tail_bytes, 0))
|
|
17
|
+
data = fp.read()
|
|
18
|
+
parts = data.rsplit(b'\x00', maxsplit=2)
|
|
19
|
+
for part in reversed(parts):
|
|
20
|
+
if part.strip():
|
|
21
|
+
powermetrics_parse = plistlib.loads(part)
|
|
22
|
+
thermal_pressure = parse_thermal_pressure(powermetrics_parse)
|
|
23
|
+
cpu_metrics_dict = parse_cpu_metrics(powermetrics_parse)
|
|
24
|
+
gpu_metrics_dict = parse_gpu_metrics(powermetrics_parse)
|
|
25
|
+
bandwidth_metrics = None
|
|
26
|
+
timestamp = powermetrics_parse["timestamp"]
|
|
27
|
+
return cpu_metrics_dict, gpu_metrics_dict, thermal_pressure, bandwidth_metrics, timestamp
|
|
28
|
+
except Exception:
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def clear_console():
|
|
33
|
+
command = 'clear'
|
|
34
|
+
os.system(command)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def convert_to_GB(value):
|
|
38
|
+
return round(value/1024/1024/1024, 1)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def run_powermetrics_process(timecode, nice=10, interval=1000):
|
|
42
|
+
#ver, *_ = platform.mac_ver()
|
|
43
|
+
#major_ver = int(ver.split(".")[0])
|
|
44
|
+
for tmpf in glob.glob("/tmp/msiltop_powermetrics*"):
|
|
45
|
+
os.remove(tmpf)
|
|
46
|
+
output_file_flag = "-o"
|
|
47
|
+
command = " ".join([
|
|
48
|
+
"sudo nice -n",
|
|
49
|
+
str(nice),
|
|
50
|
+
"powermetrics",
|
|
51
|
+
"--samplers cpu_power,gpu_power,thermal",
|
|
52
|
+
output_file_flag,
|
|
53
|
+
"/tmp/msiltop_powermetrics"+timecode,
|
|
54
|
+
"-f plist",
|
|
55
|
+
"-i",
|
|
56
|
+
str(interval)
|
|
57
|
+
])
|
|
58
|
+
process = subprocess.Popen(
|
|
59
|
+
command.split(" "),
|
|
60
|
+
stdin=PIPE,
|
|
61
|
+
stdout=subprocess.DEVNULL,
|
|
62
|
+
stderr=subprocess.DEVNULL,
|
|
63
|
+
)
|
|
64
|
+
return process
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_ram_metrics_dict():
|
|
68
|
+
ram_metrics = psutil.virtual_memory()
|
|
69
|
+
swap_metrics = psutil.swap_memory()
|
|
70
|
+
total_GB = convert_to_GB(ram_metrics.total)
|
|
71
|
+
free_GB = convert_to_GB(ram_metrics.available)
|
|
72
|
+
used_GB = convert_to_GB(ram_metrics.total-ram_metrics.available)
|
|
73
|
+
swap_total_GB = convert_to_GB(swap_metrics.total)
|
|
74
|
+
swap_used_GB = convert_to_GB(swap_metrics.used)
|
|
75
|
+
swap_free_GB = convert_to_GB(swap_metrics.total-swap_metrics.used)
|
|
76
|
+
if swap_total_GB > 0:
|
|
77
|
+
swap_free_percent = int(100-(swap_free_GB/swap_total_GB*100))
|
|
78
|
+
else:
|
|
79
|
+
swap_free_percent = None
|
|
80
|
+
ram_metrics_dict = {
|
|
81
|
+
"total_GB": round(total_GB, 1),
|
|
82
|
+
"free_GB": round(free_GB, 1),
|
|
83
|
+
"used_GB": round(used_GB, 1),
|
|
84
|
+
"free_percent": int(100-(ram_metrics.available/ram_metrics.total*100)),
|
|
85
|
+
"swap_total_GB": swap_total_GB,
|
|
86
|
+
"swap_used_GB": swap_used_GB,
|
|
87
|
+
"swap_free_GB": swap_free_GB,
|
|
88
|
+
"swap_free_percent": swap_free_percent,
|
|
89
|
+
}
|
|
90
|
+
return ram_metrics_dict
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_cpu_info():
|
|
94
|
+
cpu_info = os.popen('sysctl -a | grep machdep.cpu').read()
|
|
95
|
+
cpu_info_lines = cpu_info.split("\n")
|
|
96
|
+
data_fields = ["machdep.cpu.brand_string", "machdep.cpu.core_count"]
|
|
97
|
+
cpu_info_dict = {}
|
|
98
|
+
for l in cpu_info_lines:
|
|
99
|
+
for h in data_fields:
|
|
100
|
+
if h in l:
|
|
101
|
+
value = l.split(":")[1].strip()
|
|
102
|
+
cpu_info_dict[h] = value
|
|
103
|
+
return cpu_info_dict
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def get_core_counts():
|
|
107
|
+
cores_info = os.popen('sysctl -a | grep hw.perflevel').read()
|
|
108
|
+
cores_info_lines = cores_info.split("\n")
|
|
109
|
+
data_fields = ["hw.perflevel0.logicalcpu", "hw.perflevel1.logicalcpu"]
|
|
110
|
+
cores_info_dict = {}
|
|
111
|
+
for l in cores_info_lines:
|
|
112
|
+
for h in data_fields:
|
|
113
|
+
if h in l:
|
|
114
|
+
value = int(l.split(":")[1].strip())
|
|
115
|
+
cores_info_dict[h] = value
|
|
116
|
+
return cores_info_dict
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_gpu_cores():
|
|
120
|
+
try:
|
|
121
|
+
cores = os.popen(
|
|
122
|
+
"system_profiler -detailLevel basic SPDisplaysDataType | grep 'Total Number of Cores'").read()
|
|
123
|
+
cores = int(cores.split(": ")[-1])
|
|
124
|
+
except:
|
|
125
|
+
cores = "?"
|
|
126
|
+
return cores
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_soc_info():
|
|
130
|
+
cpu_info_dict = get_cpu_info()
|
|
131
|
+
core_counts_dict = get_core_counts()
|
|
132
|
+
try:
|
|
133
|
+
e_core_count = core_counts_dict["hw.perflevel1.logicalcpu"]
|
|
134
|
+
p_core_count = core_counts_dict["hw.perflevel0.logicalcpu"]
|
|
135
|
+
except:
|
|
136
|
+
e_core_count = "?"
|
|
137
|
+
p_core_count = "?"
|
|
138
|
+
soc_info = {
|
|
139
|
+
"name": cpu_info_dict["machdep.cpu.brand_string"],
|
|
140
|
+
"core_count": int(cpu_info_dict["machdep.cpu.core_count"]),
|
|
141
|
+
"cpu_max_power": None,
|
|
142
|
+
"gpu_max_power": None,
|
|
143
|
+
"e_core_count": e_core_count,
|
|
144
|
+
"p_core_count": p_core_count,
|
|
145
|
+
"gpu_core_count": get_gpu_cores()
|
|
146
|
+
}
|
|
147
|
+
# TDP (power)
|
|
148
|
+
soc_info_defaults = {
|
|
149
|
+
"cpu_max_power": 20,
|
|
150
|
+
"gpu_max_power": 20,
|
|
151
|
+
}
|
|
152
|
+
try:
|
|
153
|
+
config_path = os.path.join(os.path.dirname(__file__), "soc_info.json")
|
|
154
|
+
with open(config_path, "r", encoding="utf-8") as config_file:
|
|
155
|
+
soc_info_map = json.load(config_file)
|
|
156
|
+
soc_info_defaults.update(soc_info_map.get(soc_info["name"], {}))
|
|
157
|
+
except Exception:
|
|
158
|
+
pass
|
|
159
|
+
soc_info["cpu_max_power"] = soc_info_defaults["cpu_max_power"]
|
|
160
|
+
soc_info["gpu_max_power"] = soc_info_defaults["gpu_max_power"]
|
|
161
|
+
return soc_info
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: msiltop
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Real-time macOS hardware performance monitoring for Apple Silicon (M1/M2/M3/M4) with AI workload focus - enhanced asitop alternative
|
|
5
|
+
Project-URL: Homepage, https://github.com/pratikdevnani/msiltop
|
|
6
|
+
Project-URL: Repository, https://github.com/pratikdevnani/msiltop
|
|
7
|
+
Project-URL: Issues, https://github.com/pratikdevnani/msiltop/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/pratikdevnani/msiltop#readme
|
|
9
|
+
Project-URL: Changelog, https://github.com/pratikdevnani/msiltop/releases
|
|
10
|
+
Author-email: Fluid Inference <dev@fluidinference.com>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: ai,ane,apple-silicon,artificial-intelligence,asitop,cli,command-line,cpu,gpu,hardware,htop,m1,m2,m3,m4,machine-learning,macos,memory,ml,monitoring,msiltop,neural-engine,nvtop,performance,powermetrics,realtime,system-monitor,terminal,workload
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: Science/Research
|
|
18
|
+
Classifier: Intended Audience :: System Administrators
|
|
19
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
20
|
+
Classifier: Operating System :: MacOS
|
|
21
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
22
|
+
Classifier: Programming Language :: Python :: 3
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
27
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
28
|
+
Classifier: Topic :: Software Development :: Debuggers
|
|
29
|
+
Classifier: Topic :: System :: Hardware
|
|
30
|
+
Classifier: Topic :: System :: Monitoring
|
|
31
|
+
Classifier: Topic :: System :: Systems Administration
|
|
32
|
+
Classifier: Topic :: Utilities
|
|
33
|
+
Requires-Python: >=3.10
|
|
34
|
+
Requires-Dist: click>=8.2.0
|
|
35
|
+
Requires-Dist: psutil>=7.0.0
|
|
36
|
+
Requires-Dist: textual-plotext>=1.0.0
|
|
37
|
+
Requires-Dist: textual>=3.0.0
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# MSILTOP - MacOS Performance Monitor for Local AI
|
|
41
|
+
|
|
42
|
+
[](https://badge.fury.io/py/msiltop)
|
|
43
|
+
[](https://www.python.org/downloads/)
|
|
44
|
+
[](https://www.apple.com/macos/)
|
|
45
|
+
[](https://www.apple.com/mac/)
|
|
46
|
+
[](https://opensource.org/licenses/MIT)
|
|
47
|
+
|
|
48
|
+
**Real-time macOS hardware performance monitoring CLI tool for Apple Silicon Macs (M1/M2/M3/M4+) with AI workload focus**
|
|
49
|
+
|
|
50
|
+
MSILTOP is a Python-based `nvtop`-inspired command line tool specifically designed for Apple Silicon Macs. This enhanced and actively maintained fork of the original [asitop](https://github.com/tlkh/asitop) project provides comprehensive hardware monitoring with additional features, support for newer Apple Silicon chips, and optimizations for modern terminal emulators including Ghostty.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## π¦ Installation & Usage
|
|
54
|
+
|
|
55
|
+
### Quick Start with UV (Recommended)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Install UV package manager (if not already installed)
|
|
59
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Run MSILTOP directly without installation (one-time usage)
|
|
64
|
+
sudo uvx msiltop
|
|
65
|
+
|
|
66
|
+
# Run with custom options
|
|
67
|
+
sudo uvx msiltop --interval 2 --color 5 --avg 60
|
|
68
|
+
|
|
69
|
+
# Or install as a tool for regular usages
|
|
70
|
+
uv tool install msiltop@latest -U
|
|
71
|
+
sudo uv tool run msiltop
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# to update msiltop
|
|
76
|
+
uv tool install msiltop@latest -U
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Run with custom options
|
|
81
|
+
sudo uvx msiltop --interval 2 --color 5 --avg 60
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Install from PyPI
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Install with pip
|
|
88
|
+
pip install msiltop
|
|
89
|
+
|
|
90
|
+
# Run after installation
|
|
91
|
+
sudo msiltop
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## π Key Features & Capabilities
|
|
95
|
+
|
|
96
|
+
### Hardware Monitoring
|
|
97
|
+
* **Real-time CPU monitoring** - Individual core utilization and frequency tracking
|
|
98
|
+
* **GPU performance tracking** - Apple GPU usage, memory, and power consumption
|
|
99
|
+
* **Neural Engine (ANE) monitoring** - AI/ML workload detection and utilization
|
|
100
|
+
* **Memory usage monitoring** - RAM and swap memory tracking
|
|
101
|
+
* **Power consumption analysis** - CPU/GPU power draw with thermal throttling detection
|
|
102
|
+
* **Temperature monitoring** - System thermal state and throttling alerts
|
|
103
|
+
|
|
104
|
+
### Apple Silicon Support
|
|
105
|
+
* **Complete Apple Silicon coverage** - M1, M2, M3, M4, and future chip support
|
|
106
|
+
* **Optimized for modern terminals** - Enhanced Ghostty compatibility and performance
|
|
107
|
+
* **Hardware-specific metrics** - TDP specifications for all variants
|
|
108
|
+
* **Individual core monitoring** - Detailed per-core performance and efficiency tracking
|
|
109
|
+
|
|
110
|
+
### AI & Machine Learning Focus
|
|
111
|
+
* **AI workload detection** - Specialized monitoring for machine learning tasks
|
|
112
|
+
* **Neural Engine utilization** - Track AI inference and training workloads
|
|
113
|
+
* **Memory usage optimization** - Critical for large model performance
|
|
114
|
+
* **Future ML framework integration** - Planned support for popular AI libraries
|
|
115
|
+
|
|
116
|
+
### Development Installation
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Clone repository
|
|
120
|
+
git clone https://github.com/pratikdevnani/msiltop.git
|
|
121
|
+
cd msiltop
|
|
122
|
+
|
|
123
|
+
# Install in development mode
|
|
124
|
+
uv sync
|
|
125
|
+
sudo uv run msiltop
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## ποΈ Command Line Options & Configuration
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
msiltop [OPTIONS]
|
|
132
|
+
|
|
133
|
+
Options:
|
|
134
|
+
--interval INTERVAL Display refresh rate in seconds (default: 1.0)
|
|
135
|
+
--color COLOR Color theme selection 0-8 (default: 2)
|
|
136
|
+
--avg AVG Power averaging window in seconds (default: 30)
|
|
137
|
+
--show_cores Enable individual CPU core monitoring
|
|
138
|
+
--max_count COUNT Restart powermetrics after N samples (stability)
|
|
139
|
+
-h, --help Show help message and exit
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Usage Examples
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Basic monitoring with 1-second refresh
|
|
146
|
+
sudo msiltop
|
|
147
|
+
|
|
148
|
+
# High-frequency monitoring for AI workloads
|
|
149
|
+
sudo msiltop --interval 0.5 --show_cores
|
|
150
|
+
|
|
151
|
+
# Long-term monitoring with 60-second power averaging
|
|
152
|
+
sudo msiltop --avg 60 --max_count 1000
|
|
153
|
+
|
|
154
|
+
# Custom color theme
|
|
155
|
+
sudo msiltop --color 5
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## π§ How MSILTOP Works
|
|
159
|
+
|
|
160
|
+
MSILTOP leverages macOS's built-in [`powermetrics`](https://www.unix.com/man-page/osx/1/powermetrics/) utility to access hardware performance counters with minimal system impact. Root privileges are required due to `powermetrics` security requirements.
|
|
161
|
+
|
|
162
|
+
### Technical Architecture
|
|
163
|
+
|
|
164
|
+
* **CPU/GPU Utilization:** Active residency measurements via `powermetrics`
|
|
165
|
+
* **Power Consumption:** Hardware energy counters and thermal state monitoring
|
|
166
|
+
* **Memory Statistics:** [`psutil`](https://github.com/giampaolo/psutil) virtual memory and swap tracking
|
|
167
|
+
* **System Information:** `sysctl` CPU details and `system_profiler` GPU specifications
|
|
168
|
+
* **Hardware Database:** Built-in TDP specifications for all Apple Silicon variants
|
|
169
|
+
|
|
170
|
+
**Note**: Bandwidth monitoring was removed as macOS 13 deprecated bandwidth support in powermetrics.
|
|
171
|
+
|
|
172
|
+
### System Requirements
|
|
173
|
+
|
|
174
|
+
* **Hardware:** Apple Silicon Mac (M1, M2, M3, M4, or newer)
|
|
175
|
+
* **Operating System:** macOS Monterey (12.0) or later
|
|
176
|
+
* **Python:** Python 3.8+ (automatically managed with UV)
|
|
177
|
+
* **Privileges:** Root access required for `powermetrics`
|
|
178
|
+
|
|
179
|
+
### Why Fork asitop?
|
|
180
|
+
|
|
181
|
+
The original `asitop` provided excellent Apple Silicon monitoring but lacked:
|
|
182
|
+
|
|
183
|
+
- **Modern hardware support** - M3, M4+ compatibility
|
|
184
|
+
- **Terminal compatibility** - Ghostty and modern terminal optimization
|
|
185
|
+
- **AI workload focus** - Machine learning specific monitoring
|
|
186
|
+
- **Active maintenance** - Regular updates and bug fixes
|
|
187
|
+
|
|
188
|
+
### Migration from asitop
|
|
189
|
+
|
|
190
|
+
MSILTOP is a drop-in replacement for asitop with identical command-line interface:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# Replace this:
|
|
194
|
+
sudo asitop
|
|
195
|
+
|
|
196
|
+
# With this:
|
|
197
|
+
sudo msiltop
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## π Documentation
|
|
201
|
+
|
|
202
|
+
For detailed information, troubleshooting, and frequently asked questions, see our comprehensive [FAQ](docs/FAQ.md) which covers:
|
|
203
|
+
|
|
204
|
+
- **Apple Silicon Architecture**: Understanding E-CPU vs P-CPU cores
|
|
205
|
+
- **System Requirements**: Supported chips and compatibility
|
|
206
|
+
- **Monitoring Details**: Power accuracy, Neural Engine tracking, and more
|
|
207
|
+
- **Troubleshooting**: Common issues and solutions
|
|
208
|
+
- **Migration Guide**: Differences from asitop and compatibility
|
|
209
|
+
|
|
210
|
+
## πΊοΈ Roadmap & Development
|
|
211
|
+
|
|
212
|
+
### Completed Features
|
|
213
|
+
- β
Enhanced hardware support (M1-M4+)
|
|
214
|
+
- β
Ghostty terminal optimization
|
|
215
|
+
- β
Improved documentation and user experience
|
|
216
|
+
- β
PyPI publishing and UV integration
|
|
217
|
+
|
|
218
|
+
### In Development
|
|
219
|
+
- π monitor a specific process/application
|
|
220
|
+
- π GUI-less monitoring and data collection
|
|
221
|
+
- π Performance data export capabilities (CSV, JSON)
|
|
222
|
+
- API interface for other Python services
|
|
223
|
+
|
|
224
|
+
## π οΈ Development & Publishing
|
|
225
|
+
|
|
226
|
+
### Automated Release Process
|
|
227
|
+
|
|
228
|
+
MSILTOP uses GitHub Actions to automate the entire release process. No manual steps required!
|
|
229
|
+
|
|
230
|
+
#### π§ͺ Development Workflow
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# Setup development environment
|
|
234
|
+
git clone https://github.com/pratikdevnani/msiltop.git
|
|
235
|
+
cd msiltop
|
|
236
|
+
uv sync
|
|
237
|
+
|
|
238
|
+
# Make changes to code
|
|
239
|
+
# Test changes locally
|
|
240
|
+
sudo uv run msiltop
|
|
241
|
+
|
|
242
|
+
# Run with development options
|
|
243
|
+
sudo uv run msiltop --interval 0.5 --show_cores
|
|
244
|
+
|
|
245
|
+
# Test build (optional)
|
|
246
|
+
uv build
|
|
247
|
+
sudo uv run msiltop --help # Verify build works
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
## π Known Issues & Contributing
|
|
252
|
+
|
|
253
|
+
### Current Issues
|
|
254
|
+
- Chart height doesn't adapt to terminal height (width works correctly)
|
|
255
|
+
- Plot colors don't always respect theme selection
|
|
256
|
+
- Long-running sessions may require periodic restart
|
|
257
|
+
|
|
258
|
+
### Contributing
|
|
259
|
+
- Report bugs and request features via [GitHub Issues](https://github.com/pratikdevnani/msiltop/issues)
|
|
260
|
+
- Submit pull requests for bug fixes and improvements
|
|
261
|
+
- Improve documentation and examples
|
|
262
|
+
- Test on different Apple Silicon variants
|
|
263
|
+
|
|
264
|
+
## π License
|
|
265
|
+
|
|
266
|
+
MIT License - maintaining compatibility with the original asitop project.
|
|
267
|
+
|
|
268
|
+
## π Acknowledgments
|
|
269
|
+
|
|
270
|
+
This project builds upon the excellent foundation created by [Timothy Liu](https://github.com/tlkh) with the original [asitop](https://github.com/tlkh/asitop) project. We extend our gratitude for creating the groundwork for Apple Silicon performance monitoring.
|
|
271
|
+
|
|
272
|
+
This project adds to the MSILTOP project created by [pratikdevnani](https://github.com/pratikdevnani/msiltop).
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
**Keywords:** Apple Silicon monitoring, M1 M2 M3 M4 performance, macOS system monitor, AI workload tracking, Neural Engine monitoring, GPU utilization, real-time hardware stats, terminal performance tool, powermetrics CLI, asitop alternative
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
msiltop/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
msiltop/msiltop.py,sha256=ESm4BlF1QwQ3BmOzvuPNsxPhNTcp9rOYp8joBte35wg,38298
|
|
3
|
+
msiltop/parsers.py,sha256=WTrI09rjKVKP7wxv3dWckn-EgXG3Qr053BTb61iYqV4,2701
|
|
4
|
+
msiltop/soc_info.json,sha256=EPSMp7pGZyhE3Z8Pqc1VOqxTLM3k4tZq3ek_ly2hcwg,990
|
|
5
|
+
msiltop/utils.py,sha256=szm69SZNiu_yH_RaTB1B6CSpdlpo-3UFulqOiHD_K64,5266
|
|
6
|
+
msiltop-0.2.0.dist-info/METADATA,sha256=kGKkpMkxJSLpMaGK6_Vl-sPUBaLQd_xuGEmL_g2R1Go,10327
|
|
7
|
+
msiltop-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
8
|
+
msiltop-0.2.0.dist-info/entry_points.txt,sha256=7q0YR2ZtNUtndkgfCHP8rvO3Elg6nEqGxUXIk-A7zCk,49
|
|
9
|
+
msiltop-0.2.0.dist-info/licenses/LICENSE,sha256=Ks65kWYKLsrlfXMglBPLfcXvRDpNo6miAedt3h-lICs,1103
|
|
10
|
+
msiltop-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Timothy Liu
|
|
4
|
+
Copyright (c) 2025 Fluid Inference
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|