Skip to content

Commit 30dbad2

Browse files
committed
M4 Series Support
WIP, Needs testing on M4 Max and M4 Pro, new dynamic core calculations, fixed mactop title
1 parent 74e32fa commit 30dbad2

File tree

2 files changed

+15
-12
lines changed

2 files changed

+15
-12
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ Use the following keys to interact with the application while its running:
115115
- M3
116116
- M3 Pro
117117
- M3 Max
118+
- M4
119+
- M4 Pro (In Testing, development branch)
120+
- M4 Max (In Testing, development branch)
118121

119122
(If you have a confirmed working M series chip that is not listed, please open an issue, so we may add it here!)
120123

main.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ func main() {
271271
err error
272272
setColor, setInterval bool
273273
)
274-
version := "v0.1.9"
274+
version := "v0.2.0"
275275
for i := 1; i < len(os.Args); i++ {
276276
switch os.Args[i] {
277277
case "--help", "-h":
@@ -471,6 +471,7 @@ func collectMetrics(done chan struct{}, cpumetricsChan chan CPUMetrics, gpumetri
471471
var netdiskMetrics NetDiskMetrics
472472
var processMetrics []ProcessMetrics
473473
cmd := exec.Command("powermetrics", "--samplers", "cpu_power,gpu_power,thermal,network,disk", "--show-process-gpu", "--show-process-energy", "--show-initial-usage", "--show-process-netstats", "-i", strconv.Itoa(updateInterval))
474+
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
474475
stdout, err := cmd.StdoutPipe()
475476
if err != nil {
476477
stderrLogger.Fatalf("failed to get stdout pipe: %v", err)
@@ -593,15 +594,13 @@ func parseProcessMetrics(powermetricsOutput string, processMetrics []ProcessMetr
593594
}
594595
}
595596
}
596-
597597
sort.Slice(processMetrics, func(i, j int) bool {
598598
return processMetrics[i].CPUUsage > processMetrics[j].CPUUsage
599599
})
600600
return processMetrics
601601
}
602602

603603
func parseActivityMetrics(powermetricsOutput string, netdiskMetrics NetDiskMetrics) NetDiskMetrics {
604-
605604
outMatches := outRegex.FindStringSubmatch(powermetricsOutput)
606605
inMatches := inRegex.FindStringSubmatch(powermetricsOutput)
607606
if len(outMatches) == 3 {
@@ -633,13 +632,12 @@ func parseCPUMetrics(powermetricsOutput string, cpuMetrics CPUMetrics, modelName
633632
var eClusterActiveSum, pClusterActiveSum, eClusterFreqSum, pClusterFreqSum float64
634633
var eClusterCount, pClusterCount, eClusterActiveTotal, pClusterActiveTotal, eClusterFreqTotal, pClusterFreqTotal int
635634

636-
if modelName == "Apple M3 Max" || modelName == "Apple M2 Max" { // For the M3/M2 Max, we need to manually parse the CPU Usage from the powermetrics output (as current bug in Apple's powermetrics)
635+
if modelName == "Apple M3 Max" || modelName == "Apple M2 Max" || modelName == "Apple M4 Max" { // For the M3/M2/M4 Max, we need to manually parse the CPU Usage from the powermetrics output (as current bug in Apple's powermetrics)
636+
coreCounts := getCoreCounts()
637+
maxCoresP := coreCounts["hw.perflevel0.logicalcpu"]
638+
maxCoresE := coreCounts["hw.perflevel1.logicalcpu"]
639+
maxCores := maxCoresP + maxCoresE // Determine the total number of cores from getCoreCounts()
637640
for _, line := range lines {
638-
639-
maxCores := 15 // 16 Cores for M3 Max (4+12)
640-
if modelName == "Apple M2 Max" {
641-
maxCores = 11 // 12 Cores M2 Max (4+8)
642-
}
643641
for i := 0; i <= maxCores; i++ {
644642
re := regexp.MustCompile(`CPU ` + strconv.Itoa(i) + ` active residency:\s+(\d+\.\d+)%`)
645643
matches := re.FindStringSubmatch(powermetricsOutput)
@@ -827,11 +825,11 @@ func parseCPUMetrics(powermetricsOutput string, cpuMetrics CPUMetrics, modelName
827825
cpuMetrics.PClusterActive = (cpuMetrics.P0ClusterActive + cpuMetrics.P1ClusterActive + cpuMetrics.P2ClusterActive + cpuMetrics.P3ClusterActive) / 4
828826
cpuMetrics.PClusterFreqMHz = max(cpuMetrics.P0ClusterFreqMHz, cpuMetrics.P1ClusterFreqMHz, cpuMetrics.P2ClusterFreqMHz, cpuMetrics.P3ClusterFreqMHz)
829827
multra = true
830-
} else if cpuMetrics.P1ClusterActive != 0 && !multra { // M1/M2/M3 Max/Pro
828+
} else if cpuMetrics.P1ClusterActive != 0 && !multra { // M1/M2/M3/M4 Max/Pro
831829
cpuMetrics.PClusterActive = (cpuMetrics.P0ClusterActive + cpuMetrics.P1ClusterActive) / 2
832830
cpuMetrics.PClusterFreqMHz = max(cpuMetrics.P0ClusterFreqMHz, cpuMetrics.P1ClusterFreqMHz)
833831
mmax = true
834-
} else if !multra && !mmax { // M1
832+
} else if !multra && !mmax { // M1/M2/M3/M4
835833
cpuMetrics.PClusterActive = cpuMetrics.PClusterActive + cpuMetrics.P0ClusterActive
836834
}
837835
if eClusterCount > 0 && !multra && !mmax { // Calculate average active residency and frequency for E and P clusters
@@ -951,7 +949,9 @@ func getCPUInfo() map[string]string {
951949
}
952950

953951
func getCoreCounts() map[string]int {
954-
out, err := exec.Command("sysctl", "hw.perflevel0.logicalcpu", "hw.perflevel1.logicalcpu").Output()
952+
cmd := exec.Command("sysctl", "hw.perflevel0.logicalcpu", "hw.perflevel1.logicalcpu")
953+
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
954+
out, err := cmd.Output()
955955
if err != nil {
956956
stderrLogger.Fatalf("failed to execute getCoreCounts() sysctl command: %v", err)
957957
}

0 commit comments

Comments
 (0)