@@ -52,6 +52,14 @@ func setupUI() {
5252 updateHelpText ()
5353 stderrLogger .Printf ("Model: %s\n E-Core Count: %d\n P-Core Count: %d\n GPU Core Count: %d" , modelName , eCoreCount , pCoreCount , gpuCoreCount )
5454
55+ systemInfoGauge .With (prometheus.Labels {
56+ "model" : modelName ,
57+ "core_count" : fmt .Sprintf ("%d" , eCoreCount + pCoreCount ),
58+ "e_core_count" : fmt .Sprintf ("%d" , eCoreCount ),
59+ "p_core_count" : fmt .Sprintf ("%d" , pCoreCount ),
60+ "gpu_core_count" : fmt .Sprintf ("%d" , gpuCoreCount ),
61+ }).Set (1 )
62+
5563 processList = w .NewList ()
5664 processList .Title = "Process List"
5765 processList .TextStyle = ui .NewStyle (ui .ColorGreen )
@@ -91,7 +99,7 @@ func setupUI() {
9199 mainBlock .TitleRight = " " + version + " "
92100 mainBlock .TitleAlignment = ui .AlignLeft
93101 mainBlock .TitleBottomLeft = fmt .Sprintf (" %d/%d layout (%s) " , currentLayoutNum , totalLayouts , currentColorName )
94- mainBlock .TitleBottom = " Help: h | Info: i | Layout: l | Color: c | Exit: q "
102+ mainBlock .TitleBottom = " Info: i | Layout: l | Color: c | BG: b | Exit: q "
95103 mainBlock .TitleBottomAlignment = ui .AlignCenter
96104 mainBlock .TitleBottomRight = fmt .Sprintf (" -/+ %dms " , updateInterval )
97105
@@ -336,13 +344,16 @@ func Run() {
336344 defer logfile .Close ()
337345
338346 flag .StringVar (& prometheusPort , "prometheus" , "" , "Port to run Prometheus metrics server on (e.g. :9090)" )
347+ flag .StringVar (& prometheusPort , "p" , "" , "Port to run Prometheus metrics server on (e.g. :9090)" )
339348 flag .BoolVar (& headless , "headless" , false , "Run in headless mode (no TUI, output JSON to stdout)" )
340349 flag .BoolVar (& headlessPretty , "pretty" , false , "Pretty print JSON output in headless mode" )
341350 flag .IntVar (& headlessCount , "count" , 0 , "Number of samples to collect in headless mode (0 = infinite)" )
342351 flag .IntVar (& updateInterval , "interval" , 1000 , "Update interval in milliseconds" )
352+ flag .IntVar (& updateInterval , "i" , 1000 , "Update interval in milliseconds" )
343353 flag .Bool ("d" , false , "Dump all available IOReport channels and exit" )
344354 flag .Bool ("dump-ioreport" , false , "Dump all available IOReport channels and exit" )
345355 flag .StringVar (& colorName , "color" , "" , "Set the UI color. Options are 'green', 'red', 'blue', 'skyblue', 'magenta', 'yellow', 'gold', 'silver', and 'white'." )
356+ flag .StringVar (& colorName , "c" , "" , "Set the UI color." )
346357 flag .StringVar (& networkUnit , "unit-network" , "auto" , "Network unit: auto, byte, kb, mb, gb" )
347358 flag .StringVar (& diskUnit , "unit-disk" , "auto" , "Disk unit: auto, byte, kb, mb, gb" )
348359 flag .StringVar (& tempUnit , "unit-temp" , "celsius" , "Temperature unit: celsius, fahrenheit" )
@@ -385,13 +396,14 @@ func Run() {
385396 setupUI ()
386397 applyInitialTheme (colorName , setColor , interval , setInterval )
387398 currentColorName = currentConfig .Theme
399+ applyInitialBackground ()
388400 setupGrid ()
389401 termWidth , termHeight := ui .TerminalDimensions ()
390402 mainBlock .SetRect (0 , 0 , termWidth , termHeight )
391403 if termWidth < 93 {
392404 mainBlock .TitleBottom = ""
393405 } else {
394- mainBlock .TitleBottom = " Help: h | Info: i | Layout: l | Color: c | Exit: q "
406+ mainBlock .TitleBottom = " Info: i | Layout: l | Color: c | BG: b | Exit: q "
395407 }
396408 if termWidth > 2 && termHeight > 2 {
397409 grid .SetRect (1 , 1 , termWidth - 1 , termHeight - 1 )
@@ -520,13 +532,37 @@ func updateCPUUI(cpuMetrics CPUMetrics) {
520532 }
521533 totalUsage /= float64 (len (coreUsages ))
522534 cpuGauge .Percent = int (totalUsage )
523- cpuGauge .Title = fmt .Sprintf ("%d Cores (%dE/%dP) %.2f%% (%s)" ,
524- cpuCoreWidget .eCoreCount + cpuCoreWidget .pCoreCount ,
525- cpuCoreWidget .eCoreCount ,
526- cpuCoreWidget .pCoreCount ,
527- totalUsage ,
528- formatTemp (cpuMetrics .CPUTemp ),
529- )
535+
536+ updateCPUGaugeTitles (totalUsage , cpuMetrics )
537+
538+ thermalStr , _ := getThermalStateString ()
539+ updatePowerChartText (cpuMetrics , thermalStr )
540+
541+ memoryMetrics := getMemoryMetrics ()
542+ updateMemoryGaugeTitle (memoryMetrics )
543+ memoryGauge .Percent = int ((float64 (memoryMetrics .Used ) / float64 (memoryMetrics .Total )) * 100 )
544+
545+ ecoreAvg , pcoreAvg := calculateCoreAverages (coreUsages )
546+ updateCPUPrometheusMetrics (totalUsage , ecoreAvg , pcoreAvg , coreUsages , cpuMetrics , memoryMetrics )
547+
548+ // Update gauge colors with dynamic saturation if 1977 theme is active
549+ if currentConfig .Theme == "1977" {
550+ update1977GaugeColors ()
551+ }
552+ }
553+
554+ func updateCPUGaugeTitles (totalUsage float64 , cpuMetrics CPUMetrics ) {
555+ if isCompactLayout () {
556+ cpuGauge .Title = fmt .Sprintf ("CPU %.0f%% %s" , totalUsage , formatTemp (cpuMetrics .CPUTemp ))
557+ } else {
558+ cpuGauge .Title = fmt .Sprintf ("%d Cores (%dE/%dP) %.2f%% (%s)" ,
559+ cpuCoreWidget .eCoreCount + cpuCoreWidget .pCoreCount ,
560+ cpuCoreWidget .eCoreCount ,
561+ cpuCoreWidget .pCoreCount ,
562+ totalUsage ,
563+ formatTemp (cpuMetrics .CPUTemp ),
564+ )
565+ }
530566 cpuCoreWidget .Title = fmt .Sprintf ("%d Cores (%dE/%dP) %.2f%% (%s)" ,
531567 cpuCoreWidget .eCoreCount + cpuCoreWidget .pCoreCount ,
532568 cpuCoreWidget .eCoreCount ,
@@ -535,27 +571,48 @@ func updateCPUUI(cpuMetrics CPUMetrics) {
535571 formatTemp (cpuMetrics .CPUTemp ),
536572 )
537573 aneUtil := float64 (cpuMetrics .ANEW / 1 / 8.0 * 100 )
538- aneGauge .Title = fmt .Sprintf ("ANE Usage: %.2f%% @ %.2f W" , aneUtil , cpuMetrics .ANEW )
574+ if isCompactLayout () {
575+ aneGauge .Title = fmt .Sprintf ("ANE %.1fW" , cpuMetrics .ANEW )
576+ } else {
577+ aneGauge .Title = fmt .Sprintf ("ANE Usage: %.2f%% @ %.2f W" , aneUtil , cpuMetrics .ANEW )
578+ }
539579 aneGauge .Percent = int (aneUtil )
580+ }
540581
541- thermalStr , _ := getThermalStateString ()
542-
582+ func updatePowerChartText (cpuMetrics CPUMetrics , thermalStr string ) {
543583 PowerChart .Title = "Power Usage"
544- PowerChart .Text = fmt .Sprintf ("CPU: %.2f W | GPU: %.2f W\n ANE: %.2f W | DRAM: %.2f W\n System: %.2f W\n Total: %.2f W\n Thermals: %s" ,
545- cpuMetrics .CPUW ,
546- cpuMetrics .GPUW + cpuMetrics .GPUSRAMW ,
547- cpuMetrics .ANEW ,
548- cpuMetrics .DRAMW ,
549- cpuMetrics .SystemW ,
550- cpuMetrics .PackageW ,
551- thermalStr ,
552- )
553- memoryMetrics := getMemoryMetrics ()
554- memoryGauge .Title = fmt .Sprintf ("Memory Usage: %.2f GB / %.2f GB" , float64 (memoryMetrics .Used )/ 1024 / 1024 / 1024 , float64 (memoryMetrics .Total )/ 1024 / 1024 / 1024 )
555- memoryGauge .Percent = int ((float64 (memoryMetrics .Used ) / float64 (memoryMetrics .Total )) * 100 )
556- memoryGauge .TitleRight = fmt .Sprintf ("Swap: %.2f/%.2f GB" , float64 (memoryMetrics .SwapUsed )/ 1024 / 1024 / 1024 , float64 (memoryMetrics .SwapTotal )/ 1024 / 1024 / 1024 )
584+ if isCompactLayout () {
585+ PowerChart .Title = "Power"
586+ PowerChart .Text = fmt .Sprintf ("C:%.1fW G:%.1fW\n A:%.1fW D:%.1fW\n Tot:%.1fW %s" ,
587+ cpuMetrics .CPUW ,
588+ cpuMetrics .GPUW + cpuMetrics .GPUSRAMW ,
589+ cpuMetrics .ANEW ,
590+ cpuMetrics .DRAMW ,
591+ cpuMetrics .PackageW ,
592+ thermalStr ,
593+ )
594+ } else {
595+ PowerChart .Text = fmt .Sprintf ("CPU: %.2f W | GPU: %.2f W\n ANE: %.2f W | DRAM: %.2f W\n System: %.2f W\n Total: %.2f W\n Thermals: %s" ,
596+ cpuMetrics .CPUW ,
597+ cpuMetrics .GPUW + cpuMetrics .GPUSRAMW ,
598+ cpuMetrics .ANEW ,
599+ cpuMetrics .DRAMW ,
600+ cpuMetrics .SystemW ,
601+ cpuMetrics .PackageW ,
602+ thermalStr ,
603+ )
604+ }
605+ }
606+
607+ func updateMemoryGaugeTitle (memoryMetrics MemoryMetrics ) {
608+ if isCompactLayout () {
609+ memoryGauge .Title = fmt .Sprintf ("Mem %.0f/%.0fG" , float64 (memoryMetrics .Used )/ 1024 / 1024 / 1024 , float64 (memoryMetrics .Total )/ 1024 / 1024 / 1024 )
610+ } else {
611+ memoryGauge .Title = fmt .Sprintf ("Memory: %.2f GB / %.2f GB (Swap: %.2f/%.2f GB)" , float64 (memoryMetrics .Used )/ 1024 / 1024 / 1024 , float64 (memoryMetrics .Total )/ 1024 / 1024 / 1024 , float64 (memoryMetrics .SwapUsed )/ 1024 / 1024 / 1024 , float64 (memoryMetrics .SwapTotal )/ 1024 / 1024 / 1024 )
612+ }
613+ }
557614
558- var ecoreAvg , pcoreAvg float64
615+ func calculateCoreAverages ( coreUsages [] float64 ) ( ecoreAvg , pcoreAvg float64 ) {
559616 if cpuCoreWidget .eCoreCount > 0 && len (coreUsages ) >= cpuCoreWidget .eCoreCount {
560617 for i := 0 ; i < cpuCoreWidget .eCoreCount ; i ++ {
561618 ecoreAvg += coreUsages [i ]
@@ -568,7 +625,10 @@ func updateCPUUI(cpuMetrics CPUMetrics) {
568625 }
569626 pcoreAvg /= float64 (cpuCoreWidget .pCoreCount )
570627 }
628+ return ecoreAvg , pcoreAvg
629+ }
571630
631+ func updateCPUPrometheusMetrics (totalUsage , ecoreAvg , pcoreAvg float64 , coreUsages []float64 , cpuMetrics CPUMetrics , memoryMetrics MemoryMetrics ) {
572632 thermalStateVal , _ := getThermalStateString ()
573633 thermalStateNum := 0
574634 switch thermalStateVal {
@@ -599,17 +659,30 @@ func updateCPUUI(cpuMetrics CPUMetrics) {
599659 memoryUsage .With (prometheus.Labels {"type" : "swap_used" }).Set (float64 (memoryMetrics .SwapUsed ) / 1024 / 1024 / 1024 )
600660 memoryUsage .With (prometheus.Labels {"type" : "swap_total" }).Set (float64 (memoryMetrics .SwapTotal ) / 1024 / 1024 / 1024 )
601661
602- // Update gauge colors with dynamic saturation if 1977 theme is active
603- if currentConfig .Theme == "1977" {
604- update1977GaugeColors ()
662+ // Update per-core CPU usage metrics
663+ eCoreCount := cpuCoreWidget .eCoreCount
664+ for i , usage := range coreUsages {
665+ coreType := "p"
666+ if i < eCoreCount {
667+ coreType = "e"
668+ }
669+ cpuCoreUsage .With (prometheus.Labels {"core" : fmt .Sprintf ("%d" , i ), "type" : coreType }).Set (usage )
605670 }
606671}
607672
608673func updateGPUUI (gpuMetrics GPUMetrics ) {
609- if gpuMetrics .Temp > 0 {
610- gpuGauge .Title = fmt .Sprintf ("GPU Usage: %d%% @ %d MHz (%s)" , int (gpuMetrics .ActivePercent ), gpuMetrics .FreqMHz , formatTemp (float64 (gpuMetrics .Temp )))
674+ if isCompactLayout () {
675+ if gpuMetrics .Temp > 0 {
676+ gpuGauge .Title = fmt .Sprintf ("GPU %d%% %s" , int (gpuMetrics .ActivePercent ), formatTemp (float64 (gpuMetrics .Temp )))
677+ } else {
678+ gpuGauge .Title = fmt .Sprintf ("GPU %d%% %dMHz" , int (gpuMetrics .ActivePercent ), gpuMetrics .FreqMHz )
679+ }
611680 } else {
612- gpuGauge .Title = fmt .Sprintf ("GPU Usage: %d%% @ %d MHz" , int (gpuMetrics .ActivePercent ), gpuMetrics .FreqMHz )
681+ if gpuMetrics .Temp > 0 {
682+ gpuGauge .Title = fmt .Sprintf ("GPU Usage: %d%% @ %d MHz (%s)" , int (gpuMetrics .ActivePercent ), gpuMetrics .FreqMHz , formatTemp (float64 (gpuMetrics .Temp )))
683+ } else {
684+ gpuGauge .Title = fmt .Sprintf ("GPU Usage: %d%% @ %d MHz" , int (gpuMetrics .ActivePercent ), gpuMetrics .FreqMHz )
685+ }
613686 }
614687 gpuGauge .Percent = int (gpuMetrics .ActivePercent )
615688
@@ -633,7 +706,11 @@ func updateGPUUI(gpuMetrics GPUMetrics) {
633706
634707 gpuSparkline .Data = gpuValues
635708 gpuSparkline .MaxVal = 100 // GPU usage is 0-100%
636- gpuSparklineGroup .Title = fmt .Sprintf ("GPU History: %d%% (Avg: %.1f%%)" , int (gpuMetrics .ActivePercent ), avgGPU )
709+ if isCompactLayout () {
710+ gpuSparklineGroup .Title = fmt .Sprintf ("GPU %d%% (%.0f%%)" , int (gpuMetrics .ActivePercent ), avgGPU )
711+ } else {
712+ gpuSparklineGroup .Title = fmt .Sprintf ("GPU History: %d%% (Avg: %.1f%%)" , int (gpuMetrics .ActivePercent ), avgGPU )
713+ }
637714
638715 if gpuMetrics .ActivePercent > 0 {
639716 gpuUsage .Set (gpuMetrics .ActivePercent )
@@ -749,4 +826,13 @@ func updateTBNetUI(tbStats []ThunderboltNetStats) {
749826 tbNetSparklineOut .MaxVal = maxValOut * 1.1
750827 }
751828 }
829+
830+ // Update Prometheus metrics for Thunderbolt network and RDMA
831+ tbNetworkSpeed .With (prometheus.Labels {"direction" : "download" }).Set (totalBytesIn )
832+ tbNetworkSpeed .With (prometheus.Labels {"direction" : "upload" }).Set (totalBytesOut )
833+ if rdmaStatus .Available {
834+ rdmaAvailable .Set (1 )
835+ } else {
836+ rdmaAvailable .Set (0 )
837+ }
752838}
0 commit comments