Skip to content

Commit

Permalink
More data for Apple Silicon Macs
Browse files Browse the repository at this point in the history
  • Loading branch information
tuxudo committed Jan 15, 2024
1 parent 41e15f3 commit 9ca8a11
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 126 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Presents information about network, disk, CPU, and GPU activity

This module is only supported on 10.10 and higher

Database:
Table Schema
---
* timestamp - BIGINT - timestamp of when stats were last pulled
* thermal_pressure - varchar(255) - String detailing thermal pressure of the system
* backlight_max - INT(11) - Maximum value of backlight
Expand Down Expand Up @@ -37,5 +38,6 @@ Database:
* gpu_freq_mhz - FLOAT - GPU Speed in megahertz
* gpu_freq_ratio - FLOAT - GPU Fraction of Nominal Speed
* gpu_busy - FLOAT - GPU cycles used
* kern_bootargs - VARCHAT(255) - boot flags used by the kernel on last boot
* kern_bootargs - VARCHAT(255) - Boot flags used by the kernel on last boot
* clusters - mediumtext - JSON of CPU cluster activity, Apple Silicon only

2 changes: 2 additions & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
"wops_per_s_short": "Write Operations",
"wops_diff": "Total Write Operations",
"processor_usage": "Processor Usage",
"cluster_usage": "CPU Cluster Usage",
"average_load": "Average Load",
"package_watts": "CPU Package Watts",
"package_joules": "CPU Package Joules",
"freq_hz": "Current CPU Frequency",
Expand Down
25 changes: 25 additions & 0 deletions migrations/2024_01_16_000001_usage_stats_clusters.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Capsule\Manager as Capsule;

class UsageStatsClusters extends Migration
{
private $tableName = 'usage_stats';

public function up()
{
$capsule = new Capsule();
$capsule::schema()->table($this->tableName, function (Blueprint $table) {
$table->mediumText('clusters')->nullable();
});
}

public function down()
{
$capsule = new Capsule();
$capsule::schema()->table($this->tableName, function (Blueprint $table) {
$table->dropColumn('clusters');
});
}
}
73 changes: 69 additions & 4 deletions scripts/usage_stats
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/local/munkireport/munkireport-python3
# Script by Bochoven
# Script by Bochoven and Tuxudo

import os, sys, plistlib, subprocess, platform
import os, sys, plistlib, subprocess, platform, json

def get_usage_metrics():
cmd = ['/usr/bin/powermetrics', '--show-initial-usage', ' -s', "network,disk", '-f', 'plist', '-n', '0']
Expand Down Expand Up @@ -133,10 +133,26 @@ def parse_usage_plist(plist):
except:
pass
elif item == 'processor':
try:
try: # Intel Macs
usage_info['package_watts'] = plist[item]['package_watts']
except:
pass
try: # Apple Silicon Macs
usage_info['package_watts'] = (plist[item]['combined_power']/1000)
except:
pass
try: # Apple Silicon Macs
usage_info['cpu_watts'] = (plist[item]['cpu_energy']/1000)
except:
pass
try: # Apple Silicon Macs
usage_info['gpu_watts'] = (plist[item]['gpu_energy']/1000)
except:
pass
try: # Apple Silicon Macs
usage_info['clusters'] = json.dumps(process_clusters(plist[item]['clusters']))
except:
pass
try:
usage_info['package_joules'] = plist[item]['package_joules']
except:
Expand Down Expand Up @@ -172,7 +188,9 @@ def parse_usage_plist(plist):
pass
elif item == 'gpu': # Apple Silicon Macs
try:
usage_info['gpu_freq_hz'] = int(plist[item]['freq_hz']*1000000) # This value is in Mhz on AS
usage_info['gpu_freq_mhz'] = int(plist[item]['freq_hz']) # This value is in Mhz on AS
usage_info['gpu_busy'] = (1-plist[item]['idle_ratio'])
except:
pass
elif item == 'kern_bootargs':
Expand Down Expand Up @@ -233,6 +251,53 @@ def get_as_backlight():

return result

def process_clusters(cpu_clusters):
# Lovely lifted from https://github.com/tlkh/asitop

e_core = []
p_core = []
cpu_metric_dict = {}

for cluster in cpu_clusters:
name = cluster["name"]
# cpu_metric_dict[name+"_freq_Mhz"] = int(cluster["freq_hz"]/(1e6))
# cpu_metric_dict[name+"_active"] = int((1 - cluster["idle_ratio"])*100)
for cpu in cluster["cpus"]:
name = 'E-Cluster' if name[0] == 'E' else 'P-Cluster'
core = e_core if name[0] == 'E' else p_core
core.append(cpu["cpu"])
cpu_metric_dict[name + str(cpu["cpu"]) + "_freq_Mhz"] = int(cpu["freq_hz"] / (1e6))
cpu_metric_dict[name + str(cpu["cpu"]) + "_active"] = int((1 - cpu["idle_ratio"]) * 100)

# cpu_metric_dict["e_core"] = e_core
# cpu_metric_dict["p_core"] = p_core
# if "E-Cluster_active" not in cpu_metric_dict:
# # M1 Ultra
# cpu_metric_dict["E-Cluster_active"] = int((cpu_metric_dict["E0-Cluster_active"] + cpu_metric_dict["E1-Cluster_active"])/2)
# if "E-Cluster_freq_Mhz" not in cpu_metric_dict:
# # M1 Ultra
# cpu_metric_dict["E-Cluster_freq_Mhz"] = max(cpu_metric_dict["E0-Cluster_freq_Mhz"], cpu_metric_dict["E1-Cluster_freq_Mhz"])
# if "P-Cluster_active" not in cpu_metric_dict:
# if "P2-Cluster_active" in cpu_metric_dict:
# # M1 Ultra
# cpu_metric_dict["P-Cluster_active"] = int((cpu_metric_dict["P0-Cluster_active"] + cpu_metric_dict["P1-Cluster_active"] +
# cpu_metric_dict["P2-Cluster_active"] + cpu_metric_dict["P3-Cluster_active"]) / 4)
# else:
# cpu_metric_dict["P-Cluster_active"] = int((cpu_metric_dict["P0-Cluster_active"] + cpu_metric_dict["P1-Cluster_active"])/2)
# if "P-Cluster_freq_Mhz" not in cpu_metric_dict:
# if "P2-Cluster_freq_Mhz" in cpu_metric_dict:
# # M1 Ultra
# freqs = [
# cpu_metric_dict["P0-Cluster_freq_Mhz"],
# cpu_metric_dict["P1-Cluster_freq_Mhz"],
# cpu_metric_dict["P2-Cluster_freq_Mhz"],
# cpu_metric_dict["P3-Cluster_freq_Mhz"]]
# cpu_metric_dict["P-Cluster_freq_Mhz"] = max(freqs)
# else:
# cpu_metric_dict["P-Cluster_freq_Mhz"] = max(cpu_metric_dict["P0-Cluster_freq_Mhz"], cpu_metric_dict["P1-Cluster_freq_Mhz"])

return cpu_metric_dict

def getDarwinVersion():
"""Returns the Darwin version."""
# Catalina -> 10.15.7 -> 19.6.0 -> 19
Expand All @@ -245,7 +310,7 @@ def main():
# If less than macOS 10.10 (Darwin) 14, skip and write empty file
if getDarwinVersion() < 14:
result = dict()

else :
# Get results
result = dict()
Expand Down
34 changes: 17 additions & 17 deletions usage_stats_controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@
class Usage_stats_controller extends Module_controller
{

/*** Protect methods with auth! ****/
function __construct()
{
// Store module path
$this->module_path = dirname(__FILE__);
}

/**
* Default method
* @author AvB
*
**/
function index()
{
echo "You've loaded the usage_stats module!";
}
/*** Protect methods with auth! ****/
function __construct()
{
// Store module path
$this->module_path = dirname(__FILE__);
}

/**
* Default method
* @author AvB
*
**/
function index()
{
echo "You've loaded the usage_stats module!";
}

/**
* Get data for scroll widget
Expand All @@ -49,7 +49,7 @@ public function get_scroll_widget($column)
jsonView($queryobj->query($sql));
}

/**
/**
* Retrieve data in json format
*
**/
Expand Down
122 changes: 61 additions & 61 deletions usage_stats_model.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,73 +5,73 @@
class Usage_stats_model extends \Model {

function __construct($serial='')
{
parent::__construct('id', 'usage_stats'); // Primary key, tablename
$this->rs['id'] = '';
$this->rs['serial_number'] = $serial; $this->rt['serial_number'] = 'VARCHAR(255) UNIQUE';
$this->rs['timestamp'] = 0; $this->rt['timestamp'] = 'BIGINT';
$this->rs['thermal_pressure'] = '';
$this->rs['backlight_max'] = 0;
$this->rs['backlight_min'] = 0;
$this->rs['backlight'] = 0;
$this->rs['keyboard_backlight'] = 0;
$this->rs['ibyte_rate'] = 0.0;
$this->rs['ibytes'] = 0.0;
$this->rs['ipacket_rate'] = 0.0;
$this->rs['ipackets'] = 0.0;
$this->rs['obyte_rate'] = 0.0;
$this->rs['obytes'] = 0.0;
$this->rs['opacket_rate'] = 0.0;
$this->rs['opackets'] = 0.0;
$this->rs['rbytes_per_s'] = 0.0;
$this->rs['rops_per_s'] = 0.0;
$this->rs['wbytes_per_s'] = 0.0;
$this->rs['wops_per_s'] = 0.0;
$this->rs['rbytes_diff'] = 0.0;
$this->rs['rops_diff'] = 0.0;
$this->rs['wbytes_diff'] = 0.0;
$this->rs['wops_diff'] = 0.0;
$this->rs['package_watts'] = 0.0;
$this->rs['package_joules'] = 0.0;
$this->rs['freq_hz'] = 0.0; // CPU
$this->rs['freq_ratio'] = 0.0; // CPU
$this->rs['gpu_name'] = '';
$this->rs['gpu_freq_hz'] = 0.0;
$this->rs['gpu_freq_mhz'] = 0.0;
$this->rs['gpu_freq_ratio'] = 0.0;
$this->rs['gpu_busy'] = 0.0;
$this->rs['kern_bootargs'] = "";
{
parent::__construct('id', 'usage_stats'); // Primary key, tablename
$this->rs['id'] = '';
$this->rs['serial_number'] = $serial; $this->rt['serial_number'] = 'VARCHAR(255) UNIQUE';
$this->rs['timestamp'] = 0; $this->rt['timestamp'] = 'BIGINT';
$this->rs['thermal_pressure'] = '';
$this->rs['backlight_max'] = 0;
$this->rs['backlight_min'] = 0;
$this->rs['backlight'] = 0;
$this->rs['keyboard_backlight'] = 0;
$this->rs['ibyte_rate'] = 0.0;
$this->rs['ibytes'] = 0.0;
$this->rs['ipacket_rate'] = 0.0;
$this->rs['ipackets'] = 0.0;
$this->rs['obyte_rate'] = 0.0;
$this->rs['obytes'] = 0.0;
$this->rs['opacket_rate'] = 0.0;
$this->rs['opackets'] = 0.0;
$this->rs['rbytes_per_s'] = 0.0;
$this->rs['rops_per_s'] = 0.0;
$this->rs['wbytes_per_s'] = 0.0;
$this->rs['wops_per_s'] = 0.0;
$this->rs['rbytes_diff'] = 0.0;
$this->rs['rops_diff'] = 0.0;
$this->rs['wbytes_diff'] = 0.0;
$this->rs['wops_diff'] = 0.0;
$this->rs['package_watts'] = 0.0;
$this->rs['package_joules'] = 0.0;
$this->rs['freq_hz'] = 0.0; // CPU
$this->rs['freq_ratio'] = 0.0; // CPU
$this->rs['gpu_name'] = '';
$this->rs['gpu_freq_hz'] = 0.0;
$this->rs['gpu_freq_mhz'] = 0.0;
$this->rs['gpu_freq_ratio'] = 0.0;
$this->rs['gpu_busy'] = 0.0;
$this->rs['kern_bootargs'] = "";
$this->rs['clusters'] = null;

if ($serial) {
$this->retrieve_record($serial);
}

$this->serial_number = $serial;
}

// ------------------------------------------------------------------------


/**
* Process data sent by postflight
*
* @param string data
* @author tuxudo
**/
function process($plist)
{
// Check if we have data
if ( ! $plist){
throw new Exception("Error Processing Request: No property list found", 1);
}
$this->serial_number = $serial;
}

// ------------------------------------------------------------------------

/**
* Process data sent by postflight
*
* @param string data
* @author tuxudo
**/
function process($plist)
{
// Check if we have data
if ( ! $plist){
throw new Exception("Error Processing Request: No property list found", 1);
}

// Process incoming usage_stats.plist
$parser = new CFPropertyList();
$parser->parse($plist, CFPropertyList::FORMAT_XML);
$plist = $parser->toArray();

$fields = array('timestamp','thermal_pressure','backlight_max','backlight_min','backlight','keyboard_backlight','ibyte_rate','ibytes','ipacket_rate','ipackets','obyte_rate','obytes','opacket_rate','opackets','rbytes_per_s','rops_per_s','wbytes_per_s','wops_per_s','rbytes_diff','rops_diff','wbytes_diff','wops_diff','package_watts','package_joules','freq_hz','freq_ratio','gpu_name','gpu_freq_hz','gpu_freq_mhz','gpu_freq_ratio','gpu_busy','kern_bootargs');
$parser = new CFPropertyList();
$parser->parse($plist, CFPropertyList::FORMAT_XML);
$plist = $parser->toArray();

$fields = array('timestamp','thermal_pressure','backlight_max','backlight_min','backlight','keyboard_backlight','ibyte_rate','ibytes','ipacket_rate','ipackets','obyte_rate','obytes','opacket_rate','opackets','rbytes_per_s','rops_per_s','wbytes_per_s','wops_per_s','rbytes_diff','rops_diff','wbytes_diff','wops_diff','package_watts','package_joules','freq_hz','freq_ratio','gpu_name','gpu_freq_hz','gpu_freq_mhz','gpu_freq_ratio','gpu_busy','kern_bootargs','clusters');

foreach ($fields as $field) {
// If key does not exist in $plist, null it
if ( ! array_key_exists($field, $plist)) {
Expand All @@ -80,8 +80,8 @@ function process($plist)
$this->$field = $plist[$field];
}
}

// Save the data
$this->save();
}
}
}
Loading

0 comments on commit 9ca8a11

Please sign in to comment.