From 25d9f3d20de8d42eb6a026995249b05e690bfdd2 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Tue, 28 Jan 2020 18:00:29 -0700 Subject: [PATCH 1/7] Updated Mac sensor names --- scripts/wk/cfg/hw.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/scripts/wk/cfg/hw.py b/scripts/wk/cfg/hw.py index f97e3acc..26163047 100644 --- a/scripts/wk/cfg/hw.py +++ b/scripts/wk/cfg/hw.py @@ -63,27 +63,27 @@ SMC_IDS = { # Sources: https://github.com/beltex/SMCKit/blob/master/SMCKit/SMC.swift # http://www.opensource.apple.com/source/net_snmp/ # https://github.com/jedda/OSX-Monitoring-Tools - 'TA0P': {'CPU Temp': False, 'Source': 'Ambient temp'}, + 'TA0P': {'CPU Temp': False, 'Source': 'Ambient'}, 'TA0S': {'CPU Temp': False, 'Source': 'PCIE Slot 1 Ambient'}, - 'TA1P': {'CPU Temp': False, 'Source': 'Ambient temp'}, + 'TA1P': {'CPU Temp': False, 'Source': 'Ambient'}, 'TA1S': {'CPU Temp': False, 'Source': 'PCIE Slot 1 PCB'}, 'TA2S': {'CPU Temp': False, 'Source': 'PCIE Slot 2 Ambient'}, 'TA3S': {'CPU Temp': False, 'Source': 'PCIE Slot 2 PCB'}, - 'TC0C': {'CPU Temp': True, 'Source': 'CPU Core 0'}, - 'TC0D': {'CPU Temp': True, 'Source': 'CPU die temp'}, - 'TC0H': {'CPU Temp': True, 'Source': 'CPU heatsink temp'}, - 'TC0P': {'CPU Temp': True, 'Source': 'CPU Ambient 1'}, - 'TC1C': {'CPU Temp': True, 'Source': 'CPU Core 1'}, - 'TC1P': {'CPU Temp': True, 'Source': 'CPU Ambient 2'}, - 'TC2C': {'CPU Temp': True, 'Source': 'CPU B Core 0'}, - 'TC2P': {'CPU Temp': True, 'Source': 'CPU B Ambient 1'}, - 'TC3C': {'CPU Temp': True, 'Source': 'CPU B Core 1'}, - 'TC3P': {'CPU Temp': True, 'Source': 'CPU B Ambient 2'}, + 'TC0C': {'CPU Temp': True, 'Source': 'CPU Core 1'}, + 'TC0D': {'CPU Temp': True, 'Source': 'CPU Diode'}, + 'TC0H': {'CPU Temp': True, 'Source': 'CPU Heatsink'}, + 'TC0P': {'CPU Temp': True, 'Source': 'CPU Proximity'}, + 'TC1C': {'CPU Temp': True, 'Source': 'CPU Core 2'}, + 'TC1P': {'CPU Temp': True, 'Source': 'CPU Proximity 2'}, + 'TC2C': {'CPU Temp': True, 'Source': 'CPU Core 3'}, + 'TC2P': {'CPU Temp': True, 'Source': 'CPU Proximity 3'}, + 'TC3C': {'CPU Temp': True, 'Source': 'CPU Core 4'}, + 'TC3P': {'CPU Temp': True, 'Source': 'CPU Proximity 4'}, 'TCAC': {'CPU Temp': True, 'Source': 'CPU core from PCECI'}, 'TCAH': {'CPU Temp': True, 'Source': 'CPU HeatSink'}, 'TCBC': {'CPU Temp': True, 'Source': 'CPU B core from PCECI'}, 'TCBH': {'CPU Temp': True, 'Source': 'CPU HeatSink'}, - 'Te1P': {'CPU Temp': False, 'Source': 'PCIE ambient temp'}, + 'Te1P': {'CPU Temp': False, 'Source': 'PCIE Ambient'}, 'Te1S': {'CPU Temp': False, 'Source': 'PCIE slot 1'}, 'Te2S': {'CPU Temp': False, 'Source': 'PCIE slot 2'}, 'Te3S': {'CPU Temp': False, 'Source': 'PCIE slot 3'}, From 7fd3e3bada8d2c1c50a9d88fb28e947221e690fd Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Tue, 28 Jan 2020 18:04:21 -0700 Subject: [PATCH 2/7] Added Mac fan control sections --- scripts/wk/os/__init__.py | 3 +- scripts/wk/os/mac.py | 82 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 scripts/wk/os/mac.py diff --git a/scripts/wk/os/__init__.py b/scripts/wk/os/__init__.py index 1b9b21bd..dff18ac2 100644 --- a/scripts/wk/os/__init__.py +++ b/scripts/wk/os/__init__.py @@ -3,7 +3,8 @@ import platform -#if platform.system() == 'Darwin': +if platform.system() == 'Darwin': + from wk.os import mac if platform.system() == 'Linux': from wk.os import linux if platform.system() == 'Windows': diff --git a/scripts/wk/os/mac.py b/scripts/wk/os/mac.py new file mode 100644 index 00000000..5c812826 --- /dev/null +++ b/scripts/wk/os/mac.py @@ -0,0 +1,82 @@ +"""WizardKit: macOS Functions""" +# vim: sts=2 sw=2 ts=2 + +import logging +import re + +from wk.exe import run_program + + +# STATIC VARIABLES +LOG = logging.getLogger(__name__) +REGEX_FANS = re.compile(r'^.*\(bytes (?P.*)\)$') + + +# Functions +def decode_smc_bytes(text): + """Decode SMC bytes, returns int.""" + result = None + + # Get bytes + match = REGEX_FANS.match(text) + if not match: + LOG.error('Failed to decode smc output: %s', text) + raise ValueError(f'Failed to decocde smc output: {text}') + + # Convert to text + result = match.group('bytes') + result = result.replace(' ', '') + result = int(result, 16) + + # Done + return result + + +def set_fans(mode): + """Set fans to auto or max.""" + if mode == 'auto': + set_fans_auto() + elif mode == 'max': + set_fans_max() + else: + raise RuntimeError(f'Invalid fan mode: {mode}') + + +def set_fans_auto(): + """Set fans to auto.""" + LOG.info('Setting fans to auto') + cmd = ['sudo', 'smc', '-k', 'FS! ', '-w', '0000'] + run_program(cmd) + + +def set_fans_max(): + """Set fans to their max speeds.""" + LOG.info('Setting fans to max') + num_fans = 0 + + # Get number of fans + cmd = ['smc', '-k', 'FNum', '-r'] + proc = run_program(cmd) + num_fans = decode_smc_bytes(proc.stdout) + LOG.info('Found %s fans', num_fans) + + # Set all fans to forced speed + ## NOTE: mask is bit mask from right to left enabling fans + ## e.g. bit 1 is fan 0, bit 2 is fan 1, etc + ## So the mask for two fans is 0b11 or 0x3, four would be 0b111 or 0x7 + mask = f'{hex(2**num_fans - 1)[2:]:0>4}' + cmd = ['sudo', 'smc', '-k', 'FS! ', '-w', mask] + run_program(cmd) + + # Set all fans to their max speed + for fan in range(num_fans): + cmd = ['smc', '-k', f'F{fan}Mx', '-r'] + proc = run_program(cmd) + max_temp = decode_smc_bytes(proc.stdout) + LOG.info('Setting fan #%s to %s RPM', fan, str(max_temp >> 2)) + cmd = ['sudo', 'smc', '-k', f'F{fan}Tg', '-w', hex(max_temp)[2:]] + run_program(cmd) + + +if __name__ == '__main__': + print("This file is not meant to be called directly.") From fe50ce8994ae11ba7319afcba249e363d0e50561 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Tue, 28 Jan 2020 18:08:26 -0700 Subject: [PATCH 3/7] Use new Mac fan sections in HW Diags --- scripts/wk/hw/diags.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/scripts/wk/hw/diags.py b/scripts/wk/hw/diags.py index 6890056e..a0ea9f19 100644 --- a/scripts/wk/hw/diags.py +++ b/scripts/wk/hw/diags.py @@ -14,6 +14,7 @@ from collections import OrderedDict from docopt import docopt from wk import cfg, debug, exe, graph, log, net, std, tmux +from wk import os as wk_os from wk.hw import obj as hw_obj from wk.hw import sensors as hw_sensors @@ -1304,12 +1305,17 @@ def set_apple_fan_speed(speed): raise RuntimeError(f'Invalid speed {speed}') # Set cmd - if PLATFORM == 'Linux': + if PLATFORM == 'Darwin': + try: + wk_os.mac.set_fans(speed) + except (RuntimeError, ValueError, subprocess.CalledProcessError) as err: + LOG.error('Failed to set fans to %s', speed) + LOG.error('Error: %s', err) + std.print_error(f'Failed to set fans to {speed}') + for line in str(err).splitlines(): + std.print_warning(f' {line.strip()}') + elif PLATFORM == 'Linux': cmd = ['apple-fans', speed] - #TODO: Add method for use under macOS - - # Run cmd - if cmd: exe.run_program(cmd, check=False) From cc8c0992f659c89127c24baf420313d0a9f3decc Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Tue, 28 Jan 2020 18:09:33 -0700 Subject: [PATCH 4/7] Avoid crash during SMART self-test --- scripts/wk/hw/obj.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/wk/hw/obj.py b/scripts/wk/hw/obj.py index d934b30d..df07d618 100644 --- a/scripts/wk/hw/obj.py +++ b/scripts/wk/hw/obj.py @@ -368,9 +368,13 @@ class Disk(BaseObj): try: details = self.smartctl['ata_smart_data']['self_test'] except (KeyError, TypeError): - # Assuming disk lacks SMART support, ignore and return empty dict. + # Assuming disk lacks SMART support, ignore and return nearly empty dict. pass + # Ensure status is present even if empty + if 'status' not in details: + details['status'] = {} + # Done return details From 0c8701e3e40662e37775d94224beab7f8a975261 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Tue, 28 Jan 2020 18:10:10 -0700 Subject: [PATCH 5/7] Support get_disks_macOS() on older macOS versions --- scripts/wk/hw/obj.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/scripts/wk/hw/obj.py b/scripts/wk/hw/obj.py index df07d618..eca9a4db 100644 --- a/scripts/wk/hw/obj.py +++ b/scripts/wk/hw/obj.py @@ -727,7 +727,13 @@ def get_disks_macos(): disks = [] # Get info from diskutil - proc = run_program(cmd, encoding=None, errors=None) + proc = run_program(cmd, encoding=None, errors=None, check=False) + if proc.returncode != 0: + # Assuming we're running on an older macOS version + cmd.pop(-1) + proc = run_program(cmd, encoding=None, errors=None, check=False) + + # Parse plist data try: plist_data = plistlib.loads(proc.stdout) except (TypeError, ValueError): @@ -739,6 +745,11 @@ def get_disks_macos(): for disk in plist_data['WholeDisks']: disks.append(Disk(f'/dev/{disk}')) + # Remove virtual disks + disks = [ + d for d in disks if d.details.get('VirtualOrPhysical') == 'Physical' + ] + # Done return disks From 5cc0456f4032f2547012725092bf042752c96345 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Tue, 28 Jan 2020 18:12:53 -0700 Subject: [PATCH 6/7] Improve performance under macOS by using rdisks --- scripts/wk/hw/diags.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/wk/hw/diags.py b/scripts/wk/hw/diags.py index a0ea9f19..92462251 100644 --- a/scripts/wk/hw/diags.py +++ b/scripts/wk/hw/diags.py @@ -762,6 +762,7 @@ def disk_io_benchmark(state, test_objects, skip_usb=True): if PLATFORM == 'Darwin': # Use "RAW" disks under macOS dev_path = dev_path.with_name(f'r{dev_path.name}') + LOG.info(f'Using {dev_path} for better performance') offset = 0 read_rates = [] test_obj.report.append(std.color_string('I/O Benchmark', 'BLUE')) @@ -979,6 +980,11 @@ def disk_surface_scan(state, test_objects): """Run surface scan and handle exceptions.""" block_size = '1024' dev = test_obj.dev + dev_path = test_obj.dev.path + if PLATFORM == 'Darwin': + # Use "RAW" disks under macOS + dev_path = dev_path.with_name(f'r{dev_path.name}') + LOG.info(f'Using {dev_path} for better performance') test_obj.report.append(std.color_string('badblocks', 'BLUE')) test_obj.set_status('Working') @@ -988,7 +994,7 @@ def disk_surface_scan(state, test_objects): block_size = '4096' # Start scan - cmd = ['sudo', 'badblocks', '-sv', '-b', block_size, '-e', '1', dev.path] + cmd = ['sudo', 'badblocks', '-sv', '-b', block_size, '-e', '1', dev_path] with open(log_path, 'a') as _f: size_str = std.bytes_to_string(dev.details["size"], use_binary=False) _f.write( From f14f5e0d72f4f4b59c5303bd425e859e333b6852 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Tue, 28 Jan 2020 18:21:24 -0700 Subject: [PATCH 7/7] Adjusted logging --- scripts/wk/hw/diags.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/wk/hw/diags.py b/scripts/wk/hw/diags.py index 92462251..03e9628a 100644 --- a/scripts/wk/hw/diags.py +++ b/scripts/wk/hw/diags.py @@ -762,7 +762,7 @@ def disk_io_benchmark(state, test_objects, skip_usb=True): if PLATFORM == 'Darwin': # Use "RAW" disks under macOS dev_path = dev_path.with_name(f'r{dev_path.name}') - LOG.info(f'Using {dev_path} for better performance') + LOG.info('Using %s for better performance', dev_path) offset = 0 read_rates = [] test_obj.report.append(std.color_string('I/O Benchmark', 'BLUE')) @@ -984,7 +984,7 @@ def disk_surface_scan(state, test_objects): if PLATFORM == 'Darwin': # Use "RAW" disks under macOS dev_path = dev_path.with_name(f'r{dev_path.name}') - LOG.info(f'Using {dev_path} for better performance') + LOG.info('Using %s for better performance', dev_path) test_obj.report.append(std.color_string('badblocks', 'BLUE')) test_obj.set_status('Working')