msiltop 0.2.2__py3-none-any.whl → 0.2.3__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/msiltop.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import time
2
2
  import click
3
3
  import asyncio
4
+ import json
4
5
  from collections import deque
5
6
  from typing import Optional
6
7
 
@@ -410,7 +411,7 @@ class FluidTopApp(App):
410
411
 
411
412
  # CSS is set dynamically in _apply_theme method
412
413
 
413
- def __init__(self, interval: int, theme: str, avg: int, max_count: int, show_cores: bool = False):
414
+ def __init__(self, interval: int, theme: str, avg: int, max_count: int, show_cores: bool = False, thermal_bell: bool = False):
414
415
  self.interval = interval
415
416
  # Store theme temporarily, don't assign to self.theme yet
416
417
  theme_value = theme
@@ -425,6 +426,7 @@ class FluidTopApp(App):
425
426
  self.avg = avg
426
427
  self.max_count = max_count
427
428
  self.show_cores = show_cores
429
+ self.thermal_bell = thermal_bell
428
430
 
429
431
  # Initialize metrics storage
430
432
  # No longer tracking averages or peaks
@@ -440,6 +442,19 @@ class FluidTopApp(App):
440
442
  self.timecode = None
441
443
  self.last_timestamp = 0
442
444
  self.count = 0
445
+ self.last_thermal_pressure = None
446
+ self.session_start = time.time()
447
+ self.summary_samples = 0
448
+ self.cpu_usage_sum = 0.0
449
+ self.gpu_usage_sum = 0.0
450
+ self.ram_usage_sum = 0.0
451
+ self.cpu_usage_peak = 0.0
452
+ self.gpu_usage_peak = 0.0
453
+ self.ram_usage_peak = 0.0
454
+ self.cpu_power_peak = 0.0
455
+ self.gpu_power_peak = 0.0
456
+ self.ane_power_peak = 0.0
457
+ self.thermal_pressure_max = "Nominal"
443
458
 
444
459
  # SoC info
445
460
  self.soc_info_dict = get_soc_info()
@@ -587,6 +602,13 @@ class FluidTopApp(App):
587
602
  height: 100%;
588
603
  }}
589
604
 
605
+ #thermal-label {{
606
+ width: auto;
607
+ text-align: right;
608
+ color: $text;
609
+ padding: 0 1;
610
+ }}
611
+
590
612
  #timestamp-label {{
591
613
  width: auto;
592
614
  text-align: right;
@@ -617,6 +639,7 @@ class FluidTopApp(App):
617
639
  yield Label("Initializing...", id="system-info-label")
618
640
  # Timestamp on the right
619
641
  with Horizontal(id="controls-buttons"):
642
+ yield Label("Thermal Pressure: --", id="thermal-label")
620
643
  yield Label("", id="timestamp-label")
621
644
 
622
645
  # Usage Charts section
@@ -752,6 +775,7 @@ class FluidTopApp(App):
752
775
  # Update title to show both CPU types (smoothed values)
753
776
  combined_title = f"E-CPU: {e_cpu_usage}% | P-CPU: {p_cpu_usage}%"
754
777
  cpu_combined_chart.update_title(combined_title)
778
+ combined_cpu_usage = (e_cpu_usage + p_cpu_usage) / 2
755
779
 
756
780
  # Update GPU usage chart
757
781
  gpu_chart = self.query_one("#gpu-usage-chart", UsageChart)
@@ -763,7 +787,7 @@ class FluidTopApp(App):
763
787
  # Update RAM usage chart with swap information
764
788
  ram_metrics_dict = get_ram_metrics_dict()
765
789
  ram_chart = self.query_one("#ram-usage-chart", UsageChart)
766
- ram_usage_percent = 100 - ram_metrics_dict["free_percent"] # Convert from free to used percentage
790
+ ram_usage_percent = ram_metrics_dict["used_percent"]
767
791
 
768
792
  # Include swap information in the title
769
793
  if ram_metrics_dict["swap_total_GB"] < 0.1:
@@ -774,6 +798,14 @@ class FluidTopApp(App):
774
798
  ram_chart.update_title(ram_title)
775
799
  ram_chart.add_data(ram_usage_percent, render=should_render)
776
800
 
801
+ self.summary_samples += 1
802
+ self.cpu_usage_sum += combined_cpu_usage
803
+ self.gpu_usage_sum += gpu_usage
804
+ self.ram_usage_sum += ram_usage_percent
805
+ self.cpu_usage_peak = max(self.cpu_usage_peak, combined_cpu_usage)
806
+ self.gpu_usage_peak = max(self.gpu_usage_peak, gpu_usage)
807
+ self.ram_usage_peak = max(self.ram_usage_peak, ram_usage_percent)
808
+
777
809
  if self.show_cores:
778
810
  await self.update_core_usage_charts(cpu_metrics_dict, should_render)
779
811
 
@@ -817,6 +849,10 @@ class FluidTopApp(App):
817
849
  cpu_power_W = cpu_metrics_dict["cpu_W"]
818
850
  gpu_power_W = cpu_metrics_dict["gpu_W"]
819
851
  ane_power_W = cpu_metrics_dict["ane_W"]
852
+
853
+ self.cpu_power_peak = max(self.cpu_power_peak, cpu_power_W)
854
+ self.gpu_power_peak = max(self.gpu_power_peak, gpu_power_W)
855
+ self.ane_power_peak = max(self.ane_power_peak, ane_power_W)
820
856
 
821
857
  # Update energy consumption for each component (watts * seconds = watt-seconds)
822
858
  self.total_energy_consumed += package_power_W * self.interval
@@ -866,10 +902,19 @@ class FluidTopApp(App):
866
902
  ane_power_chart.add_data(ane_power_percent, render=should_render)
867
903
 
868
904
  # Update system info label with total power and thermal info
869
- thermal_throttle = "no" if thermal_pressure == "Nominal" else "yes"
870
905
  total_energy_display = format_energy(self.total_energy_consumed)
871
- system_info = f"{self.soc_info_dict['name']} ({self.soc_info_dict['e_core_count']}E+{self.soc_info_dict['p_core_count']}P+{self.soc_info_dict['gpu_core_count']}GPU) | Total: {package_power_W:.1f}W ({total_energy_display}) | Throttle: {thermal_throttle}"
906
+ system_info = f"{self.soc_info_dict['name']} ({self.soc_info_dict['e_core_count']}E+{self.soc_info_dict['p_core_count']}P+{self.soc_info_dict['gpu_core_count']}GPU) | Total: {package_power_W:.1f}W ({total_energy_display})"
872
907
  self.query_one("#system-info-label", Label).update(system_info)
908
+ self.query_one("#thermal-label", Label).update(f"Thermal Pressure: {thermal_pressure}")
909
+ thermal_levels = ["Nominal", "Moderate", "Heavy", "Critical"]
910
+ if thermal_pressure in thermal_levels and thermal_levels.index(thermal_pressure) > thermal_levels.index(self.thermal_pressure_max):
911
+ self.thermal_pressure_max = thermal_pressure
912
+ if self.thermal_bell and thermal_pressure != "Nominal" and thermal_pressure != self.last_thermal_pressure:
913
+ try:
914
+ print("\a", end="", flush=True)
915
+ except Exception:
916
+ pass
917
+ self.last_thermal_pressure = thermal_pressure
873
918
 
874
919
  async def update_timestamp(self):
875
920
  """Update the timestamp display"""
@@ -885,6 +930,28 @@ class FluidTopApp(App):
885
930
  self.powermetrics_process.terminate()
886
931
  except:
887
932
  pass
933
+ duration_sec = max(0, time.time() - self.session_start)
934
+ if self.summary_samples > 0:
935
+ avg_cpu = self.cpu_usage_sum / self.summary_samples
936
+ avg_gpu = self.gpu_usage_sum / self.summary_samples
937
+ avg_ram = self.ram_usage_sum / self.summary_samples
938
+ else:
939
+ avg_cpu = avg_gpu = avg_ram = 0.0
940
+ summary = {
941
+ "duration_sec": round(duration_sec, 1),
942
+ "avg_cpu_percent": round(avg_cpu, 2),
943
+ "avg_gpu_percent": round(avg_gpu, 2),
944
+ "avg_ram_percent": round(avg_ram, 2),
945
+ "peak_cpu_percent": round(self.cpu_usage_peak, 2),
946
+ "peak_gpu_percent": round(self.gpu_usage_peak, 2),
947
+ "peak_ram_percent": round(self.ram_usage_peak, 2),
948
+ "peak_cpu_w": round(self.cpu_power_peak, 2),
949
+ "peak_gpu_w": round(self.gpu_power_peak, 2),
950
+ "peak_ane_w": round(self.ane_power_peak, 2),
951
+ "total_energy_wh": round(self.total_energy_consumed / 3600, 3),
952
+ "thermal_pressure_max": self.thermal_pressure_max,
953
+ }
954
+ print("\nSession summary:\n" + json.dumps(summary, indent=2))
888
955
 
889
956
  @click.command()
890
957
  @click.option('--interval', type=float, default=1.0,
@@ -897,19 +964,21 @@ class FluidTopApp(App):
897
964
  help='Max show count to restart powermetrics')
898
965
  @click.option('--show_cores', is_flag=True, default=False,
899
966
  help='Show per-core CPU usage charts')
900
- def main(interval, theme, avg, max_count, show_cores):
967
+ @click.option('--thermal_bell', is_flag=True, default=False,
968
+ help='Ring terminal bell when thermal throttling is detected')
969
+ def main(interval, theme, avg, max_count, show_cores, thermal_bell):
901
970
  """msiltop: Performance monitoring CLI tool for Apple Silicon"""
902
- return _main_logic(interval, theme, avg, max_count, show_cores=show_cores)
971
+ return _main_logic(interval, theme, avg, max_count, show_cores=show_cores, thermal_bell=thermal_bell)
903
972
 
904
973
 
905
- def _main_logic(interval, theme, avg, max_count, show_cores=False):
974
+ def _main_logic(interval, theme, avg, max_count, show_cores=False, thermal_bell=False):
906
975
  """Main logic using Textual app"""
907
976
  print("\nMSILTOP - Performance monitoring CLI tool for Apple Silicon")
908
977
  print("Get help at `https://github.com/pratikdevnani/msiltop`")
909
978
  print("P.S. You are recommended to run MSILTOP with `sudo msiltop`\n")
910
979
 
911
980
  # Create and run the Textual app
912
- app = FluidTopApp(interval, theme, avg, max_count, show_cores=show_cores)
981
+ app = FluidTopApp(interval, theme, avg, max_count, show_cores=show_cores, thermal_bell=thermal_bell)
913
982
  try:
914
983
  app.run()
915
984
  except KeyboardInterrupt:
msiltop/utils.py CHANGED
@@ -81,7 +81,7 @@ def get_ram_metrics_dict():
81
81
  "total_GB": round(total_GB, 1),
82
82
  "free_GB": round(free_GB, 1),
83
83
  "used_GB": round(used_GB, 1),
84
- "free_percent": int(100-(ram_metrics.available/ram_metrics.total*100)),
84
+ "used_percent": int(ram_metrics.percent),
85
85
  "swap_total_GB": swap_total_GB,
86
86
  "swap_used_GB": swap_used_GB,
87
87
  "swap_free_GB": swap_free_GB,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: msiltop
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Real-time macOS hardware performance monitoring for Apple Silicon (M1/M2/M3/M4) with AI workload focus - enhanced asitop alternative
5
5
  Project-URL: Homepage, https://github.com/pratikdevnani/msiltop
6
6
  Project-URL: Repository, https://github.com/pratikdevnani/msiltop
@@ -0,0 +1,10 @@
1
+ msiltop/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ msiltop/msiltop.py,sha256=xokVTnlTRkdciw-DGSPUi6hkkcsJqiHi9Eg2eJBJorc,41574
3
+ msiltop/parsers.py,sha256=WTrI09rjKVKP7wxv3dWckn-EgXG3Qr053BTb61iYqV4,2701
4
+ msiltop/soc_info.json,sha256=EPSMp7pGZyhE3Z8Pqc1VOqxTLM3k4tZq3ek_ly2hcwg,990
5
+ msiltop/utils.py,sha256=HLskmiEQoja8gT59_2VX07poQmCvBvSsL7oLMz5Bg9g,5236
6
+ msiltop-0.2.3.dist-info/METADATA,sha256=iXayAvkLDsm6Le8eByGvCE5ZSZY-gydr_6GeidRmwBw,9485
7
+ msiltop-0.2.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
8
+ msiltop-0.2.3.dist-info/entry_points.txt,sha256=7q0YR2ZtNUtndkgfCHP8rvO3Elg6nEqGxUXIk-A7zCk,49
9
+ msiltop-0.2.3.dist-info/licenses/LICENSE,sha256=Ks65kWYKLsrlfXMglBPLfcXvRDpNo6miAedt3h-lICs,1103
10
+ msiltop-0.2.3.dist-info/RECORD,,
@@ -1,10 +0,0 @@
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.2.dist-info/METADATA,sha256=uqELdaHWVoib2te0lBwTtVnvN57yYDzXsxXaLMDuybI,9485
7
- msiltop-0.2.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
8
- msiltop-0.2.2.dist-info/entry_points.txt,sha256=7q0YR2ZtNUtndkgfCHP8rvO3Elg6nEqGxUXIk-A7zCk,49
9
- msiltop-0.2.2.dist-info/licenses/LICENSE,sha256=Ks65kWYKLsrlfXMglBPLfcXvRDpNo6miAedt3h-lICs,1103
10
- msiltop-0.2.2.dist-info/RECORD,,