Chromium Code Reviews| Index: appengine/swarming/swarming_bot/api/platforms/osx.py |
| diff --git a/appengine/swarming/swarming_bot/api/platforms/osx.py b/appengine/swarming/swarming_bot/api/platforms/osx.py |
| index 27b51cbb107c359a7a6fc3b6ea7424e475a4aa83..1700e6b588547c931966ccbda6430053cd591d1e 100644 |
| --- a/appengine/swarming/swarming_bot/api/platforms/osx.py |
| +++ b/appengine/swarming/swarming_bot/api/platforms/osx.py |
| @@ -9,12 +9,12 @@ import ctypes |
| import logging |
| import os |
| import platform |
| +import plistlib |
| import re |
| +import struct |
| import subprocess |
| import time |
| -import plistlib |
| - |
| from utils import tools |
| import common |
| @@ -23,6 +23,156 @@ import gpu |
| ## Private stuff. |
| +iokit = ctypes.CDLL( |
| + '/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit') |
| +# https://developer.apple.com/documentation/iokit/1514274-ioconnectcallstructmethod |
| +iokit.IOConnectCallStructMethod.argtypes = [ |
| + ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p, ctypes.c_ulonglong, |
| + ctypes.c_void_p, ctypes.POINTER(ctypes.c_ulonglong), |
| +] |
| +iokit.IOConnectCallStructMethod.restype = ctypes.c_int |
| + |
| +# https://developer.apple.com/documentation/iokit/1514515-ioserviceopen |
| +iokit.IOServiceOpen.argtypes = [ |
| + ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint), |
| +] |
| +iokit.IOServiceOpen.restype = ctypes.c_int |
| + |
| +# https://developer.apple.com/documentation/iokit/1514687-ioservicematching |
| +iokit.IOServiceMatching.restype = ctypes.c_void_p |
| + |
| +# https://developer.apple.com/documentation/iokit/1514494-ioservicegetmatchingservices |
| +iokit.IOServiceGetMatchingServices.argtypes = [ |
| + ctypes.c_uint, ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint), |
| +] |
| +iokit.IOServiceGetMatchingServices.restype = ctypes.c_int |
| + |
| +# https://developer.apple.com/documentation/iokit/1514741-ioiteratornext |
| +iokit.IOIteratorNext.argtypes = [ctypes.c_uint] |
| +iokit.IOIteratorNext.restype = ctypes.c_uint |
| + |
| +# https://developer.apple.com/documentation/iokit/1514627-ioobjectrelease |
| +iokit.IOObjectRelease.argtypes = [ctypes.c_uint] |
| +iokit.IOObjectRelease.restype = ctypes.c_int |
| + |
| + |
| +libkern = ctypes.CDLL('/usr/lib/system/libsystem_kernel.dylib') |
| +libkern.mach_task_self.restype = ctypes.c_uint |
| + |
| + |
| +class SMCKeyData_vers_t(ctypes.Structure): |
|
Ken Russell (switch to Gerrit)
2017/07/28 18:22:01
How about a pointer to https://github.com/lavoiesl
M-A Ruel
2017/07/28 20:41:57
It is not the original source either. I found sour
|
| + _fields_ = [ |
| + ('major', ctypes.c_char), |
| + ('minor', ctypes.c_char), |
| + ('build', ctypes.c_char), |
| + ('reserved', ctypes.c_char), |
| + ('release', ctypes.c_ushort), |
| + ] |
| + |
| + |
| +class SMCKeyData_pLimitData_t(ctypes.Structure): |
| + _fields_ = [ |
| + ('version', ctypes.c_ushort), |
| + ('length', ctypes.c_ushort), |
| + ('cpuPLimit', ctypes.c_uint), |
| + ('gpuPLimit', ctypes.c_uint), |
| + ('memPLimit', ctypes.c_uint), |
| + ] |
| + |
| + |
| +class SMCKeyData_keyInfo_t(ctypes.Structure): |
| + _fields_ = [ |
| + ('dataSize', ctypes.c_uint), |
| + ('dataType', ctypes.c_uint), |
| + ('dataAttributes', ctypes.c_char), |
| + ] |
| + |
| + |
| +class SMCKeyData_t(ctypes.Structure): |
| + _fields_ = [ |
| + ('key', ctypes.c_uint), |
| + ('vers', SMCKeyData_vers_t), |
| + ('pLimitData', SMCKeyData_pLimitData_t), |
| + ('keyInfo', SMCKeyData_keyInfo_t), |
| + ('result', ctypes.c_char), |
| + ('status', ctypes.c_char), |
| + ('data8', ctypes.c_char), |
| + ('data32', ctypes.c_uint), |
| + ('bytes', ctypes.c_ubyte * 32), |
| + ] |
| + |
| + |
| +class SMCVal_t(ctypes.Structure): |
| + _fields_ = [ |
| + ('key', (ctypes.c_ubyte * 5)), |
| + ('dataSize', ctypes.c_uint), |
| + ('dataType', (ctypes.c_ubyte * 5)), |
| + ('bytes', ctypes.c_ubyte * 32), |
| + ] |
| + |
| + |
| +@tools.cached |
| +def SMC_open(): |
| + itr = ctypes.c_uint() |
| + result = iokit.IOServiceGetMatchingServices( |
| + 0, iokit.IOServiceMatching('AppleSMC'), ctypes.byref(itr)) |
| + if result: |
| + logging.error('failed to get AppleSMC (%d)', result) |
| + return None |
| + dev = iokit.IOIteratorNext(itr) |
| + iokit.IOObjectRelease(itr) |
| + if not dev: |
| + logging.error('no SMC found') |
| + return None |
| + conn = ctypes.c_uint() |
| + if iokit.IOServiceOpen(dev, libkern.mach_task_self(), 0, ctypes.byref(conn)): |
|
Vadim Sh.
2017/07/28 18:15:56
I assume all such connections are automatically re
Vadim Sh.
2017/07/28 18:25:32
The code kbr@ linked to releases the device after
M-A Ruel
2017/07/28 20:41:57
Yes, I've did not implement code to close the hand
|
| + logging.error('failed to open AppleSMC (%d)', result) |
| + return None |
| + return conn |
| + |
| + |
| +def SMC_call(conn, index, indata, outdata): |
| + return iokit.IOConnectCallStructMethod( |
| + conn, |
| + ctypes.c_uint(index), |
| + ctypes.cast(ctypes.pointer(indata), ctypes.c_void_p), |
| + ctypes.c_ulonglong(ctypes.sizeof(SMCKeyData_t)), |
| + ctypes.cast(ctypes.pointer(outdata), ctypes.c_void_p), |
| + ctypes.pointer(ctypes.c_ulonglong(ctypes.sizeof(SMCKeyData_t)))) |
| + |
| + |
| +def SMC_read_key(conn, key): |
| + KERNEL_INDEX_SMC = 2 |
| + |
| + # Call with SMC_CMD_READ_KEYINFO. |
| + indata = SMCKeyData_t(key=struct.unpack('>i', key)[0], data8='\x09') |
| + outdata = SMCKeyData_t() |
| + if SMC_call(conn, KERNEL_INDEX_SMC, indata, outdata): |
| + logging.error('SMC call to get key info failed') |
| + return None |
| + |
| + # Call with SMC_CMD_READ_BYTES. |
|
Vadim Sh.
2017/07/28 18:15:56
this looks horrifying :(
M-A Ruel
2017/07/28 20:41:57
Na, I've done much worse recently. 💣
|
| + val = SMCVal_t(dataSize=outdata.keyInfo.dataSize) |
| + for i, x in enumerate(struct.pack('>i', outdata.keyInfo.dataType)): |
| + val.dataType[i] = ord(x) |
| + # pylint: disable=attribute-defined-outside-init |
| + indata.data8 = '\x05' |
| + indata.keyInfo.dataSize = val.dataSize |
| + if SMC_call(conn, KERNEL_INDEX_SMC, indata, outdata): |
| + logging.error('SMC call to get data info failed') |
| + return None |
| + val.bytes = outdata.bytes |
| + return val |
| + |
| + |
| +def SMC_get_temperature(conn, key): |
|
Ken Russell (switch to Gerrit)
2017/07/28 18:22:01
Comment that this is in Celsius.
M-A Ruel
2017/07/28 20:41:57
Celsius or Death. Well it could have been Kelvin.
|
| + val = SMC_read_key(conn, key) |
| + if not val: |
| + return None |
| + if val.dataSize != 2 or 'sp78\0' != ''.join(map(chr, val.dataType)): |
| + return None |
| + return (val.bytes[0] * 256 + val.bytes[1]) / 256. |
| + |
| @tools.cached |
| def _get_system_profiler(data_type): |
| @@ -172,7 +322,8 @@ def get_hardware_model_string(): |
| A string like Macmini5,3 or MacPro6,1. |
| """ |
| try: |
| - return subprocess.check_output(['sysctl', '-n', 'hw.model']).rstrip() |
| + return unicode( |
| + subprocess.check_output(['sysctl', '-n', 'hw.model']).rstrip()) |
| except (OSError, subprocess.CalledProcessError): |
| return None |
| @@ -260,6 +411,14 @@ def get_cpuinfo(): |
| } |
| +def get_cpu_temperature(): |
| + """Returns the CPU temperature via AppleSMC.""" |
| + conn = SMC_open() |
| + if not conn: |
| + return None |
| + return SMC_get_temperature(conn, 'TC0P') |
| + |
| + |
| @tools.cached |
| def get_monitor_hidpi(): |
| """Returns True if the monitor is hidpi. |
| @@ -284,12 +443,13 @@ def get_monitor_hidpi(): |
| is_hidpi(card['spdisplays_ndrvs']) |
| for card in _get_system_profiler('SPDisplaysDataType') |
| if 'spdisplays_ndrvs' in card) |
| - return str(int(hidpi)) |
| + return unicode(int(hidpi)) |
| def get_xcode_versions(): |
| """Returns all Xcode versions installed.""" |
| - return sorted(xcode['version'] for xcode in get_xcode_state().itervalues()) |
| + return sorted( |
| + unicode(xcode['version']) for xcode in get_xcode_state().itervalues()) |
| @tools.cached |