9999import re
100100import json
101101import platform
102+ import struct
102103from subprocess import check_output , DEVNULL
103104from glob import glob
104105from os .path import join as pjoin
@@ -562,14 +563,20 @@ def match(self, data):
562563 out = data
563564 if self .regex is not None :
564565 regex = re .compile (self .regex )
566+ m = None
565567 for l in NEWLINE_REGEX .split (data ):
566568 m = regex .match (l )
567569 if m :
568570 out = m .group (1 )
571+ break
569572 else :
570573 m = regex .search (l )
571574 if m :
572575 out = m .group (1 )
576+ break
577+ if not m :
578+ out = ""
579+ self .parser = None
573580 return out
574581 def parse (self , data ):
575582 out = data
@@ -1418,20 +1425,45 @@ def __init__(self, extended=False, anonymous=False):
14181425class CpuInfoMacOS (InfoGroup ):
14191426 def __init__ (self , extended = False , anonymous = False ):
14201427 super (CpuInfoMacOS , self ).__init__ (name = "CpuInfo" , extended = extended , anonymous = anonymous )
1421- self .const ("MachineType" , platform .machine ())
1422- self .addc ("Vendor" , "sysctl" , "-a" , r"machdep.cpu.vendor: (.*)" )
1423- self .addc ("Name" , "sysctl" , "-a" , r"machdep.cpu.brand_string: (.*)" )
1424- self .addc ("Family" , "sysctl" , "-a" , r"machdep.cpu.family: (\d+)" , int )
1425- self .addc ("Model" , "sysctl" , "-a" , r"machdep.cpu.model: (\d+)" , int )
1426- self .addc ("Stepping" , "sysctl" , "-a" , r"machdep.cpu.stepping: (\d+)" , int )
1427- if extended :
1428- self .addc ("Flags" , "sysctl" , "-a" , r"machdep.cpu.features: (.*)" , tostrlist )
1429- self .addc ("ExtFlags" , "sysctl" , "-a" , r"machdep.cpu.extfeatures: (.*)" , tostrlist )
1430- self .addc ("Leaf7Flags" , "sysctl" , "-a" , r"machdep.cpu.leaf7_features: (.*)" , tostrlist )
1431- self .addc ("Microcode" , "sysctl" , "-a" , r"machdep.cpu.microcode_version: (.*)" )
1432- self .addc ("ExtFamily" , "sysctl" , "-a" , r"machdep.cpu.extfamily: (\d+)" , int )
1433- self .addc ("ExtModel" , "sysctl" , "-a" , r"machdep.cpu.extmodel: (\d+)" , int )
1434- self .required (["Vendor" , "Family" , "Model" , "Stepping" ])
1428+ march = platform .machine ()
1429+ self .const ("MachineType" , march )
1430+ if march in ["x86_64" ]:
1431+ self .addc ("Vendor" , "sysctl" , "-a" , r"machdep.cpu.vendor: (.*)" )
1432+ self .addc ("Name" , "sysctl" , "-a" , r"machdep.cpu.brand_string: (.*)" )
1433+ self .addc ("Family" , "sysctl" , "-a" , r"machdep.cpu.family: (\d+)" , int )
1434+ self .addc ("Model" , "sysctl" , "-a" , r"machdep.cpu.model: (\d+)" , int )
1435+ self .addc ("Stepping" , "sysctl" , "-a" , r"machdep.cpu.stepping: (\d+)" , int )
1436+ if extended :
1437+ self .addc ("Flags" , "sysctl" , "-a" , r"machdep.cpu.features: (.*)" , tostrlist )
1438+ self .addc ("ExtFlags" , "sysctl" , "-a" , r"machdep.cpu.extfeatures: (.*)" , tostrlist )
1439+ self .addc ("Leaf7Flags" , "sysctl" , "-a" , r"machdep.cpu.leaf7_features: (.*)" , tostrlist )
1440+ self .addc ("Microcode" , "sysctl" , "-a" , r"machdep.cpu.microcode_version: (.*)" )
1441+ self .addc ("ExtFamily" , "sysctl" , "-a" , r"machdep.cpu.extfamily: (\d+)" , int )
1442+ self .addc ("ExtModel" , "sysctl" , "-a" , r"machdep.cpu.extmodel: (\d+)" , int )
1443+ self .required (["Vendor" , "Family" , "Model" , "Stepping" ])
1444+ elif march in ["arm64" ]:
1445+ # TODO: Is there a way to get Vendor?
1446+ self .const ("Vendor" , "Apple" )
1447+ self .addc ("Name" , "sysctl" , "-a" , r"machdep.cpu.brand_string: (.*)" )
1448+ self .addc ("Family" , "sysctl" , "-a" , r"hw.cpufamily: (\d+)" , int )
1449+ self .addc ("Model" , "sysctl" , "-a" , r"hw.cputype: (\d+)" , int )
1450+ self .addc ("Stepping" , "sysctl" , "-a" , r"hw.cpusubtype: (\d+)" , int )
1451+ if extended :
1452+ self .addc ("Flags" , "sysctl" , "-a hw.optional" , parse = CpuInfoMacOS .getflags_arm64 )
1453+ self .required (["Vendor" , "Family" , "Model" , "Stepping" ])
1454+
1455+ @staticmethod
1456+ def getflags_arm64 (string ):
1457+ outlist = []
1458+ for line in string .split ("\n " ):
1459+ key , value = [
1460+ field .split (":" ) for field in line .split ("hw.optional." ) if len (field )
1461+ ][0 ]
1462+ if int (value ):
1463+ key = key .replace ("arm." , "" )
1464+ outlist .append (key )
1465+ return outlist
1466+
14351467
14361468class CpuInfo (InfoGroup ):
14371469 def __init__ (self , extended = False , anonymous = False ):
@@ -1670,8 +1702,25 @@ class CpuFrequencyMacOsCpu(InfoGroup):
16701702 def __init__ (self , extended = False , anonymous = False ):
16711703 super (CpuFrequencyMacOsCpu , self ).__init__ (extended = extended , anonymous = anonymous )
16721704 self .name = "Cpus"
1673- self .addc ("MaxFreq" , "sysctl" , "-a" , r"hw.cpufrequency_max: (\d+)" , int )
1674- self .addc ("MinFreq" , "sysctl" , "-a" , r"hw.cpufrequency_min: (\d+)" , int )
1705+ if platform .machine () == "x86_64" :
1706+ self .addc ("MaxFreq" , "sysctl" , "-a" , r"hw.cpufrequency_max: (\d+)" , int )
1707+ self .addc ("MinFreq" , "sysctl" , "-a" , r"hw.cpufrequency_min: (\d+)" , int )
1708+ elif platform .machine () == "arm64" :
1709+ # AppleSilicon
1710+ self .addc ("Freqs-E-Core" , "ioreg" , "-k voltage-states1-sram | grep voltage-states1-sram" , parse = CpuFrequencyMacOsCpu .get_ioreg_states )
1711+ self .addc ("Freqs-P-Core" , "ioreg" , "-k voltage-states5-sram | grep voltage-states5-sram" , parse = CpuFrequencyMacOsCpu .get_ioreg_states )
1712+
1713+ @staticmethod
1714+ def get_ioreg_states (string ):
1715+ bytestr = re .match (r".*<([0-9A-Fa-f]+)>" , string )
1716+ if bytestr :
1717+ bytestr = bytestr .group (1 )
1718+ # numbers consecutive in 4-byte little-endian
1719+ states_int = struct .unpack ("<" + int (len (bytestr )/ 8 ) * "I" , bytes .fromhex (bytestr ))
1720+ # voltage states are in pairs of (freq, voltage)
1721+ states_int = [x for x in states_int if states_int .index (x )% 2 == 0 ]
1722+ return states_int
1723+ return string
16751724
16761725class CpuFrequencyMacOsBus (InfoGroup ):
16771726 def __init__ (self , extended = False , anonymous = False ):
@@ -1684,7 +1733,12 @@ class CpuFrequencyMacOs(MultiClassInfoGroup):
16841733 def __init__ (self , extended = False , anonymous = False ):
16851734 super (CpuFrequencyMacOs , self ).__init__ (extended = extended , anonymous = anonymous )
16861735 self .name = "CpuFrequency"
1687- self .classlist = [CpuFrequencyMacOsCpu , CpuFrequencyMacOsBus ]
1736+ # Apple Silicon with MacOS does not easily print out CPU/BUS freqs, see
1737+ # https://github.com/giampaolo/psutil/issues/1892
1738+ if platform .machine () == "x86_64" :
1739+ self .classlist = [CpuFrequencyMacOsCpu , CpuFrequencyMacOsBus ]
1740+ elif platform .machine () == "arm64" :
1741+ self .classlist = [CpuFrequencyMacOsCpu ]
16881742 self .classargs = [{} for c in self .classlist ]
16891743 self .addc ("TimerFreq" , "sysctl" , "-a" , r"hw.tbfrequency: (\d+)" , int )
16901744
@@ -1837,8 +1891,8 @@ def getcpulist(arg):
18371891 ncpus = process_cmd (("sysctl" , "-n hw.ncpu" , r"(\d+)" , int ))
18381892 cconfig = process_cmd (("sysctl" , "-n hw.cacheconfig" , r"([\d\s]+)" , tointlist ))
18391893 if cconfig and ncpus :
1840- if len (cconfig ) > int (level ):
1841- sharedbycount = int ( cconfig [ int ( level )])
1894+ sharedbycount = int (cconfig [ int (level )])
1895+ if sharedbycount :
18421896 for i in range (ncpus // sharedbycount ):
18431897 clist .append (list (range (i * sharedbycount , (i + 1 )* sharedbycount )))
18441898 return clist
@@ -1848,8 +1902,12 @@ def getcpulist(arg):
18481902class CacheTopologyMacOS (ListInfoGroup ):
18491903 def __init__ (self , extended = False , anonymous = False ):
18501904 super (CacheTopologyMacOS , self ).__init__ (anonymous = anonymous , extended = extended )
1905+ march = platform .machine ()
18511906 self .name = "CacheTopology"
1852- self .userlist = ["l1i" , "l1d" , "l2" , "l3" ]
1907+ if march in ["x86_64" ]:
1908+ self .userlist = ["l1i" , "l1d" , "l2" , "l3" ]
1909+ elif march in ["arm64" ]:
1910+ self .userlist = ["l1i" , "l1d" , "l2" ]
18531911 self .subclass = CacheTopologyMacOSClass
18541912
18551913
@@ -3356,6 +3414,8 @@ def __init__(self, platform, extended=False, anonymous=False, clinfo_path=""):
33563414 self .addc ("Vendor" , clcmd , cmdopts , r"\s+CL_PLATFORM_VENDOR\s+(.+)" , str )
33573415 #self.commands["IcdSuffix"] = (clcmd, cmdopts, r"\s+CL_PLATFORM_ICD_SUFFIX_KHR\s+(.+)", str)
33583416 suffix = process_cmd ((clcmd , cmdopts , r"\s+CL_PLATFORM_ICD_SUFFIX_KHR\s+(.+)" , str ))
3417+ if " " in suffix :
3418+ suffix = "P0"
33593419 self .const ("IcdSuffix" , suffix )
33603420 num_devs = process_cmd ((clcmd , cmdopts , r".*{}.*#DEVICES\s*(\d+)" .format (suffix ), int ))
33613421 if num_devs and num_devs > 0 :
0 commit comments