#!/usr/bin/env python # -*- coding: UTF-8 -*- # Copyright (c) 2014-2016, Matthew Brennan Jones # Py-cpuinfo is a Python module to show the cpuinfo of a processor # It uses a MIT style license # It is hosted at: https://github.com/workhorsy/py-cpuinfo # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import os, sys import re import time import platform import multiprocessing import ctypes import pickle import base64 import subprocess try: import _winreg as winreg except ImportError as err: try: import winreg except ImportError as err: pass PY2 = sys.version_info[0] == 2 class DataSource(object): bits = platform.architecture()[0] cpu_count = multiprocessing.cpu_count() is_windows = platform.system().lower() == 'windows' raw_arch_string = platform.machine() @staticmethod def has_proc_cpuinfo(): return os.path.exists('/proc/cpuinfo') @staticmethod def has_dmesg(): return len(program_paths('dmesg')) > 0 @staticmethod def has_cpufreq_info(): return len(program_paths('cpufreq-info')) > 0 @staticmethod def has_sestatus(): return len(program_paths('sestatus')) > 0 @staticmethod def has_sysctl(): return len(program_paths('sysctl')) > 0 @staticmethod def has_isainfo(): return len(program_paths('isainfo')) > 0 @staticmethod def has_kstat(): return len(program_paths('kstat')) > 0 @staticmethod def has_sysinfo(): return len(program_paths('sysinfo')) > 0 @staticmethod def has_lscpu(): return len(program_paths('lscpu')) > 0 @staticmethod def cat_proc_cpuinfo(): return run_and_get_stdout(['cat', '/proc/cpuinfo']) @staticmethod def cpufreq_info(): return run_and_get_stdout(['cpufreq-info']) @staticmethod def sestatus_allow_execheap(): return run_and_get_stdout(['sestatus', '-b'], ['grep', '-i', '"allow_execheap"'])[1].strip().lower().endswith('on') @staticmethod def sestatus_allow_execmem(): return run_and_get_stdout(['sestatus', '-b'], ['grep', '-i', '"allow_execmem"'])[1].strip().lower().endswith('on') @staticmethod def dmesg_a(): return run_and_get_stdout(['dmesg', '-a']) @staticmethod def sysctl_machdep_cpu_hw_cpufrequency(): return run_and_get_stdout(['sysctl', 'machdep.cpu', 'hw.cpufrequency']) @staticmethod def isainfo_vb(): return run_and_get_stdout(['isainfo', '-vb']) @staticmethod def kstat_m_cpu_info(): return run_and_get_stdout(['kstat', '-m', 'cpu_info']) @staticmethod def sysinfo_cpu(): return run_and_get_stdout(['sysinfo', '-cpu']) @staticmethod def lscpu(): return run_and_get_stdout(['lscpu']) @staticmethod def winreg_processor_brand(): key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0") processor_brand = winreg.QueryValueEx(key, "ProcessorNameString")[0] winreg.CloseKey(key) return processor_brand @staticmethod def winreg_vendor_id(): key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0") vendor_id = winreg.QueryValueEx(key, "VendorIdentifier")[0] winreg.CloseKey(key) return vendor_id @staticmethod def winreg_raw_arch_string(): key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment") raw_arch_string = winreg.QueryValueEx(key, "PROCESSOR_ARCHITECTURE")[0] winreg.CloseKey(key) return raw_arch_string @staticmethod def winreg_hz_actual(): key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0") hz_actual = winreg.QueryValueEx(key, "~Mhz")[0] winreg.CloseKey(key) hz_actual = to_hz_string(hz_actual) return hz_actual @staticmethod def winreg_feature_bits(): key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0") feature_bits = winreg.QueryValueEx(key, "FeatureSet")[0] winreg.CloseKey(key) return feature_bits def obj_to_b64(thing): a = thing b = pickle.dumps(a) c = base64.b64encode(b) d = c.decode('utf8') return d def b64_to_obj(thing): a = base64.b64decode(thing) b = pickle.loads(a) return b def run_and_get_stdout(command, pipe_command=None): if not pipe_command: p1 = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output = p1.communicate()[0] if not PY2: output = output.decode(encoding='UTF-8') return p1.returncode, output else: p1 = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p2 = subprocess.Popen(pipe_command, stdin=p1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p1.stdout.close() output = p2.communicate()[0] if not PY2: output = output.decode(encoding='UTF-8') return p2.returncode, output def program_paths(program_name): paths = [] exts = filter(None, os.environ.get('PATHEXT', '').split(os.pathsep)) path = os.environ['PATH'] for p in os.environ['PATH'].split(os.pathsep): p = os.path.join(p, program_name) if os.access(p, os.X_OK): paths.append(p) for e in exts: pext = p + e if os.access(pext, os.X_OK): paths.append(pext) return paths def _get_field_actual(cant_be_number, raw_string, field_names): for line in raw_string.splitlines(): for field_name in field_names: field_name = field_name.lower() if ':' in line: left, right = line.split(':', 1) left = left.strip().lower() right = right.strip() if left == field_name and len(right) > 0: if cant_be_number: if not right.isdigit(): return right else: return right return None def _get_field(cant_be_number, raw_string, convert_to, default_value, *field_names): retval = _get_field_actual(cant_be_number, raw_string, field_names) # Convert the return value if retval and convert_to: try: retval = convert_to(retval) except: retval = default_value # Return the default if there is no return value if retval is None: retval = default_value return retval def _get_hz_string_from_brand(processor_brand): # Just return 0 if the processor brand does not have the Hz if not 'hz' in processor_brand.lower(): return (1, '0.0') hz_brand = processor_brand.lower() scale = 1 if hz_brand.endswith('mhz'): scale = 6 elif hz_brand.endswith('ghz'): scale = 9 if '@' in hz_brand: hz_brand = hz_brand.split('@')[1] else: hz_brand = hz_brand.rsplit(None, 1)[1] hz_brand = hz_brand.rstrip('mhz').rstrip('ghz').strip() hz_brand = to_hz_string(hz_brand) return (scale, hz_brand) def _get_hz_string_from_beagle_bone(): scale, hz_brand = 1, '0.0' if not DataSource.has_cpufreq_info(): return scale, hz_brand returncode, output = DataSource.cpufreq_info() if returncode != 0: return (scale, hz_brand) hz_brand = output.split('current CPU frequency is')[1].split('.')[0].lower() if hz_brand.endswith('mhz'): scale = 6 elif hz_brand.endswith('ghz'): scale = 9 hz_brand = hz_brand.rstrip('mhz').rstrip('ghz').strip() hz_brand = to_hz_string(hz_brand) return (scale, hz_brand) def _get_hz_string_from_lscpu(): scale, hz_brand = 1, '0.0' if not DataSource.has_lscpu(): return scale, hz_brand returncode, output = DataSource.lscpu() if returncode != 0: return (scale, hz_brand) new_hz = _get_field(False, output, None, None, 'CPU max MHz', 'CPU MHz') if new_hz == None: return (scale, hz_brand) new_hz = to_hz_string(new_hz) scale = 6 return (scale, new_hz) def to_friendly_hz(ticks, scale): # Get the raw Hz as a string left, right = to_raw_hz(ticks, scale) ticks = '{0}.{1}'.format(left, right) # Get the location of the dot, and remove said dot dot_index = ticks.index('.') ticks = ticks.replace('.', '') # Get the Hz symbol and scale symbol = "Hz" scale = 0 if dot_index > 9: symbol = "GHz" scale = 9 elif dot_index > 6: symbol = "MHz" scale = 6 elif dot_index > 3: symbol = "KHz" scale = 3 # Get the Hz with the dot at the new scaled point ticks = '{0}.{1}'.format(ticks[:-scale-1], ticks[-scale-1:]) # Format the ticks to have 4 numbers after the decimal # and remove any superfluous zeroes. ticks = '{0:.4f} {1}'.format(float(ticks), symbol) ticks = ticks.rstrip('0') return ticks def to_raw_hz(ticks, scale): # Scale the numbers ticks = ticks.lstrip('0') old_index = ticks.index('.') ticks = ticks.replace('.', '') ticks = ticks.ljust(scale + old_index+1, '0') new_index = old_index + scale ticks = '{0}.{1}'.format(ticks[:new_index], ticks[new_index:]) left, right = ticks.split('.') left, right = int(left), int(right) return (left, right) def to_hz_string(ticks): # Convert to string ticks = '{0}'.format(ticks) # Add decimal if missing if '.' not in ticks: ticks = '{0}.0'.format(ticks) # Remove trailing zeros ticks = ticks.rstrip('0') # Add one trailing zero for empty right side if ticks.endswith('.'): ticks = '{0}0'.format(ticks) return ticks def parse_arch(raw_arch_string): arch, bits = None, None raw_arch_string = raw_arch_string.lower() # X86 if re.match('^i\d86$|^x86$|^x86_32$|^i86pc$|^ia32$|^ia-32$|^bepc$', raw_arch_string): arch = 'X86_32' bits = 32 elif re.match('^x64$|^x86_64$|^x86_64t$|^i686-64$|^amd64$|^ia64$|^ia-64$', raw_arch_string): arch = 'X86_64' bits = 64 # ARM elif re.match('^armv8-a$', raw_arch_string): arch = 'ARM_8' bits = 64 elif re.match('^armv7$|^armv7[a-z]$|^armv7-[a-z]$|^armv6[a-z]$', raw_arch_string): arch = 'ARM_7' bits = 32 elif re.match('^armv8$|^armv8[a-z]$|^armv8-[a-z]$', raw_arch_string): arch = 'ARM_8' bits = 32 # PPC elif re.match('^ppc32$|^prep$|^pmac$|^powermac$', raw_arch_string): arch = 'PPC_32' bits = 32 elif re.match('^powerpc$|^ppc64$', raw_arch_string): arch = 'PPC_64' bits = 64 # SPARC elif re.match('^sparc32$|^sparc$', raw_arch_string): arch = 'SPARC_32' bits = 32 elif re.match('^sparc64$|^sun4u$|^sun4v$', raw_arch_string): arch = 'SPARC_64' bits = 64 return (arch, bits) def is_bit_set(reg, bit): mask = 1 << bit is_set = reg & mask > 0 return is_set class CPUID(object): def __init__(self): # Figure out if SE Linux is on and in enforcing mode self.is_selinux_enforcing = False # Just return if the SE Linux Status Tool is not installed if not DataSource.has_sestatus(): return # Figure out if we can execute heap and execute memory can_selinux_exec_heap = DataSource.sestatus_allow_execheap() can_selinux_exec_memory = DataSource.sestatus_allow_execmem() self.is_selinux_enforcing = (not can_selinux_exec_heap or not can_selinux_exec_memory) def _asm_func(self, restype=None, argtypes=(), byte_code=[]): byte_code = bytes.join(b'', byte_code) address = None if DataSource.is_windows: # Allocate a memory segment the size of the byte code, and make it executable size = len(byte_code) MEM_COMMIT = ctypes.c_ulong(0x1000) PAGE_EXECUTE_READWRITE = ctypes.c_ulong(0x40) address = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_size_t(size), MEM_COMMIT, PAGE_EXECUTE_READWRITE) if not address: raise Exception("Failed to VirtualAlloc") # Copy the byte code into the memory segment memmove = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t)(ctypes._memmove_addr) if memmove(address, byte_code, size) < 0: raise Exception("Failed to memmove") else: # Allocate a memory segment the size of the byte code size = len(byte_code) address = ctypes.pythonapi.valloc(size) if not address: raise Exception("Failed to valloc") # Mark the memory segment as writeable only if not self.is_selinux_enforcing: WRITE = 0x2 if ctypes.pythonapi.mprotect(address, size, WRITE) < 0: raise Exception("Failed to mprotect") # Copy the byte code into the memory segment if ctypes.pythonapi.memmove(address, byte_code, size) < 0: raise Exception("Failed to memmove") # Mark the memory segment as writeable and executable only if not self.is_selinux_enforcing: WRITE_EXECUTE = 0x2 | 0x4 if ctypes.pythonapi.mprotect(address, size, WRITE_EXECUTE) < 0: raise Exception("Failed to mprotect") # Cast the memory segment into a function functype = ctypes.CFUNCTYPE(restype, *argtypes) fun = functype(address) return fun, address def _run_asm(self, *byte_code): # Convert the byte code into a function that returns an int restype = None if DataSource.bits == '64bit': restype = ctypes.c_uint64 else: restype = ctypes.c_uint32 argtypes = () func, address = self._asm_func(restype, argtypes, byte_code) # Call the byte code like a function retval = func() size = ctypes.c_size_t(len(byte_code)) # Free the function memory segment if DataSource.is_windows: MEM_RELEASE = ctypes.c_ulong(0x8000) ctypes.windll.kernel32.VirtualFree(address, size, MEM_RELEASE) else: # Remove the executable tag on the memory READ_WRITE = 0x1 | 0x2 if ctypes.pythonapi.mprotect(address, size, READ_WRITE) < 0: raise Exception("Failed to mprotect") ctypes.pythonapi.free(address) return retval # FIXME: We should not have to use different instructions to # set eax to 0 or 1, on 32bit and 64bit machines. def _zero_eax(self): if DataSource.bits == '64bit': return ( b"\x66\xB8\x00\x00" # mov eax,0x0" ) else: return ( b"\x31\xC0" # xor ax,ax ) def _one_eax(self): if DataSource.bits == '64bit': return ( b"\x66\xB8\x01\x00" # mov eax,0x1" ) else: return ( b"\x31\xC0" # xor ax,ax b"\x40" # inc ax ) # http://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID def get_vendor_id(self): # EBX ebx = self._run_asm( self._zero_eax(), b"\x0F\xA2" # cpuid b"\x89\xD8" # mov ax,bx b"\xC3" # ret ) # ECX ecx = self._run_asm( self._zero_eax(), b"\x0f\xa2" # cpuid b"\x89\xC8" # mov ax,cx b"\xC3" # ret ) # EDX edx = self._run_asm( self._zero_eax(), b"\x0f\xa2" # cpuid b"\x89\xD0" # mov ax,dx b"\xC3" # ret ) # Each 4bits is a ascii letter in the name vendor_id = [] for reg in [ebx, edx, ecx]: for n in [0, 8, 16, 24]: vendor_id.append(chr((reg >> n) & 0xFF)) vendor_id = ''.join(vendor_id) return vendor_id # http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits def get_info(self): # EAX eax = self._run_asm( self._one_eax(), b"\x0f\xa2" # cpuid b"\xC3" # ret ) # Get the CPU info stepping = (eax >> 0) & 0xF # 4 bits model = (eax >> 4) & 0xF # 4 bits family = (eax >> 8) & 0xF # 4 bits processor_type = (eax >> 12) & 0x3 # 2 bits extended_model = (eax >> 16) & 0xF # 4 bits extended_family = (eax >> 20) & 0xFF # 8 bits return { 'stepping' : stepping, 'model' : model, 'family' : family, 'processor_type' : processor_type, 'extended_model' : extended_model, 'extended_family' : extended_family } # https://en.wikipedia.org/wiki/CPUID#EAX.3D80000000h:_Get_Highest_Extended_Function_Supported def get_max_extension_support(self): # Check for extension support max_extension_support = self._run_asm( b"\xB8\x00\x00\x00\x80" # mov ax,0x80000000 b"\x0f\xa2" # cpuid b"\xC3" # ret ) return max_extension_support # http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits def get_flags(self, max_extension_support): # EDX edx = self._run_asm( self._one_eax(), b"\x0f\xa2" # cpuid b"\x89\xD0" # mov ax,dx b"\xC3" # ret ) # ECX ecx = self._run_asm( self._one_eax(), b"\x0f\xa2" # cpuid b"\x89\xC8" # mov ax,cx b"\xC3" # ret ) # Get the CPU flags flags = { 'fpu' : is_bit_set(edx, 0), 'vme' : is_bit_set(edx, 1), 'de' : is_bit_set(edx, 2), 'pse' : is_bit_set(edx, 3), 'tsc' : is_bit_set(edx, 4), 'msr' : is_bit_set(edx, 5), 'pae' : is_bit_set(edx, 6), 'mce' : is_bit_set(edx, 7), 'cx8' : is_bit_set(edx, 8), 'apic' : is_bit_set(edx, 9), #'reserved1' : is_bit_set(edx, 10), 'sep' : is_bit_set(edx, 11), 'mtrr' : is_bit_set(edx, 12), 'pge' : is_bit_set(edx, 13), 'mca' : is_bit_set(edx, 14), 'cmov' : is_bit_set(edx, 15), 'pat' : is_bit_set(edx, 16), 'pse36' : is_bit_set(edx, 17), 'pn' : is_bit_set(edx, 18), 'clflush' : is_bit_set(edx, 19), #'reserved2' : is_bit_set(edx, 20), 'dts' : is_bit_set(edx, 21), 'acpi' : is_bit_set(edx, 22), 'mmx' : is_bit_set(edx, 23), 'fxsr' : is_bit_set(edx, 24), 'sse' : is_bit_set(edx, 25), 'sse2' : is_bit_set(edx, 26), 'ss' : is_bit_set(edx, 27), 'ht' : is_bit_set(edx, 28), 'tm' : is_bit_set(edx, 29), 'ia64' : is_bit_set(edx, 30), 'pbe' : is_bit_set(edx, 31), 'pni' : is_bit_set(ecx, 0), 'pclmulqdq' : is_bit_set(ecx, 1), 'dtes64' : is_bit_set(ecx, 2), 'monitor' : is_bit_set(ecx, 3), 'ds_cpl' : is_bit_set(ecx, 4), 'vmx' : is_bit_set(ecx, 5), 'smx' : is_bit_set(ecx, 6), 'est' : is_bit_set(ecx, 7), 'tm2' : is_bit_set(ecx, 8), 'ssse3' : is_bit_set(ecx, 9), 'cid' : is_bit_set(ecx, 10), #'reserved3' : is_bit_set(ecx, 11), 'fma' : is_bit_set(ecx, 12), 'cx16' : is_bit_set(ecx, 13), 'xtpr' : is_bit_set(ecx, 14), 'pdcm' : is_bit_set(ecx, 15), #'reserved4' : is_bit_set(ecx, 16), 'pcid' : is_bit_set(ecx, 17), 'dca' : is_bit_set(ecx, 18), 'sse4_1' : is_bit_set(ecx, 19), 'sse4_2' : is_bit_set(ecx, 20), 'x2apic' : is_bit_set(ecx, 21), 'movbe' : is_bit_set(ecx, 22), 'popcnt' : is_bit_set(ecx, 23), 'tscdeadline' : is_bit_set(ecx, 24), 'aes' : is_bit_set(ecx, 25), 'xsave' : is_bit_set(ecx, 26), 'osxsave' : is_bit_set(ecx, 27), 'avx' : is_bit_set(ecx, 28), 'f16c' : is_bit_set(ecx, 29), 'rdrnd' : is_bit_set(ecx, 30), 'hypervisor' : is_bit_set(ecx, 31) } # Get a list of only the flags that are true flags = [k for k, v in flags.items() if v] # Get the Extended CPU flags extended_flags = {} # https://en.wikipedia.org/wiki/CPUID#EAX.3D7.2C_ECX.3D0:_Extended_Features if max_extension_support == 7: pass # FIXME: Are we missing all these flags too? # avx2 et cetera ... # https://en.wikipedia.org/wiki/CPUID#EAX.3D80000001h:_Extended_Processor_Info_and_Feature_Bits if max_extension_support >= 0x80000001: # EBX # FIXME: This may need to be EDX instead ebx = self._run_asm( b"\xB8\x01\x00\x00\x80" # mov ax,0x80000001 b"\x0f\xa2" # cpuid b"\x89\xD8" # mov ax,bx b"\xC3" # ret ) # ECX ecx = self._run_asm( b"\xB8\x01\x00\x00\x80" # mov ax,0x80000001 b"\x0f\xa2" # cpuid b"\x89\xC8" # mov ax,cx b"\xC3" # ret ) # Get the extended CPU flags extended_flags = { 'fpu' : is_bit_set(ebx, 0), 'vme' : is_bit_set(ebx, 1), 'de' : is_bit_set(ebx, 2), 'pse' : is_bit_set(ebx, 3), 'tsc' : is_bit_set(ebx, 4), 'msr' : is_bit_set(ebx, 5), 'pae' : is_bit_set(ebx, 6), 'mce' : is_bit_set(ebx, 7), 'cx8' : is_bit_set(ebx, 8), 'apic' : is_bit_set(ebx, 9), #'reserved' : is_bit_set(ebx, 10), 'syscall' : is_bit_set(ebx, 11), 'mtrr' : is_bit_set(ebx, 12), 'pge' : is_bit_set(ebx, 13), 'mca' : is_bit_set(ebx, 14), 'cmov' : is_bit_set(ebx, 15), 'pat' : is_bit_set(ebx, 16), 'pse36' : is_bit_set(ebx, 17), #'reserved' : is_bit_set(ebx, 18), 'mp' : is_bit_set(ebx, 19), 'nx' : is_bit_set(ebx, 20), #'reserved' : is_bit_set(ebx, 21), 'mmxext' : is_bit_set(ebx, 22), 'mmx' : is_bit_set(ebx, 23), 'fxsr' : is_bit_set(ebx, 24), 'fxsr_opt' : is_bit_set(ebx, 25), 'pdpe1gp' : is_bit_set(ebx, 26), 'rdtscp' : is_bit_set(ebx, 27), #'reserved' : is_bit_set(ebx, 28), 'lm' : is_bit_set(ebx, 29), '3dnowext' : is_bit_set(ebx, 30), '3dnow' : is_bit_set(ebx, 31), 'lahf_lm' : is_bit_set(ecx, 0), 'cmp_legacy' : is_bit_set(ecx, 1), 'svm' : is_bit_set(ecx, 2), 'extapic' : is_bit_set(ecx, 3), 'cr8_legacy' : is_bit_set(ecx, 4), 'abm' : is_bit_set(ecx, 5), 'sse4a' : is_bit_set(ecx, 6), 'misalignsse' : is_bit_set(ecx, 7), '3dnowprefetch' : is_bit_set(ecx, 8), 'osvw' : is_bit_set(ecx, 9), 'ibs' : is_bit_set(ecx, 10), 'xop' : is_bit_set(ecx, 11), 'skinit' : is_bit_set(ecx, 12), 'wdt' : is_bit_set(ecx, 13), #'reserved' : is_bit_set(ecx, 14), 'lwp' : is_bit_set(ecx, 15), 'fma4' : is_bit_set(ecx, 16), 'tce' : is_bit_set(ecx, 17), #'reserved' : is_bit_set(ecx, 18), 'nodeid_msr' : is_bit_set(ecx, 19), #'reserved' : is_bit_set(ecx, 20), 'tbm' : is_bit_set(ecx, 21), 'topoext' : is_bit_set(ecx, 22), 'perfctr_core' : is_bit_set(ecx, 23), 'perfctr_nb' : is_bit_set(ecx, 24), #'reserved' : is_bit_set(ecx, 25), 'dbx' : is_bit_set(ecx, 26), 'perftsc' : is_bit_set(ecx, 27), 'pci_l2i' : is_bit_set(ecx, 28), #'reserved' : is_bit_set(ecx, 29), #'reserved' : is_bit_set(ecx, 30), #'reserved' : is_bit_set(ecx, 31) } # Get a list of only the flags that are true extended_flags = [k for k, v in extended_flags.items() if v] flags += extended_flags flags.sort() return flags # https://en.wikipedia.org/wiki/CPUID#EAX.3D80000002h.2C80000003h.2C80000004h:_Processor_Brand_String def get_processor_brand(self, max_extension_support): processor_brand = "" # Processor brand string if max_extension_support >= 0x80000004: instructions = [ b"\xB8\x02\x00\x00\x80", # mov ax,0x80000002 b"\xB8\x03\x00\x00\x80", # mov ax,0x80000003 b"\xB8\x04\x00\x00\x80" # mov ax,0x80000004 ] for instruction in instructions: # EAX eax = self._run_asm( instruction, # mov ax,0x8000000? b"\x0f\xa2" # cpuid b"\x89\xC0" # mov ax,ax b"\xC3" # ret ) # EBX ebx = self._run_asm( instruction, # mov ax,0x8000000? b"\x0f\xa2" # cpuid b"\x89\xD8" # mov ax,bx b"\xC3" # ret ) # ECX ecx = self._run_asm( instruction, # mov ax,0x8000000? b"\x0f\xa2" # cpuid b"\x89\xC8" # mov ax,cx b"\xC3" # ret ) # EDX edx = self._run_asm( instruction, # mov ax,0x8000000? b"\x0f\xa2" # cpuid b"\x89\xD0" # mov ax,dx b"\xC3" # ret ) # Combine each of the 4 bytes in each register into the string for reg in [eax, ebx, ecx, edx]: for n in [0, 8, 16, 24]: processor_brand += chr((reg >> n) & 0xFF) # Strip off any trailing NULL terminators and white space processor_brand = processor_brand.strip("\0").strip() return processor_brand # https://en.wikipedia.org/wiki/CPUID#EAX.3D80000006h:_Extended_L2_Cache_Features def get_cache(self, max_extension_support): cache_info = {} # Just return if the cache feature is not supported if max_extension_support < 0x80000006: return cache_info # ECX ecx = self._run_asm( b"\xB8\x06\x00\x00\x80" # mov ax,0x80000006 b"\x0f\xa2" # cpuid b"\x89\xC8" # mov ax,cx b"\xC3" # ret ) cache_info = { 'size_kb' : ecx & 0xFF, 'line_size_b' : (ecx >> 12) & 0xF, 'associativity' : (ecx >> 16) & 0xFFFF } return cache_info def get_ticks(self): retval = None if DataSource.bits == '32bit': # Works on x86_32 restype = None argtypes = (ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint)) get_ticks_x86_32, address = self._asm_func(restype, argtypes, [ b"\x55", # push bp b"\x89\xE5", # mov bp,sp b"\x31\xC0", # xor ax,ax b"\x0F\xA2", # cpuid b"\x0F\x31", # rdtsc b"\x8B\x5D\x08", # mov bx,[di+0x8] b"\x8B\x4D\x0C", # mov cx,[di+0xc] b"\x89\x13", # mov [bp+di],dx b"\x89\x01", # mov [bx+di],ax b"\x5D", # pop bp b"\xC3" # ret ] ) high = ctypes.c_uint32(0) low = ctypes.c_uint32(0) get_ticks_x86_32(ctypes.byref(high), ctypes.byref(low)) retval = ((high.value << 32) & 0xFFFFFFFF00000000) | low.value elif DataSource.bits == '64bit': # Works on x86_64 restype = ctypes.c_uint64 argtypes = () get_ticks_x86_64, address = self._asm_func(restype, argtypes, [ b"\x48", # dec ax b"\x31\xC0", # xor ax,ax b"\x0F\xA2", # cpuid b"\x0F\x31", # rdtsc b"\x48", # dec ax b"\xC1\xE2\x20", # shl dx,byte 0x20 b"\x48", # dec ax b"\x09\xD0", # or ax,dx b"\xC3", # ret ] ) retval = get_ticks_x86_64() return retval def get_raw_hz(self): start = self.get_ticks() time.sleep(1) end = self.get_ticks() ticks = (end - start) return ticks def get_cpu_info_from_cpuid(): ''' Returns the CPU info gathered by querying the X86 cpuid register in a new process. Returns None of non X86 cpus. Returns None if SELinux is in enforcing mode. ''' returncode, output = run_and_get_stdout([sys.executable, "-c", "import cpuinfo; print(cpuinfo.actual_get_cpu_info_from_cpuid())"]) if returncode != 0: return None info = b64_to_obj(output) return info def actual_get_cpu_info_from_cpuid(): # Get the CPU arch and bits arch, bits = parse_arch(DataSource.raw_arch_string) # Return none if this is not an X86 CPU if not arch in ['X86_32', 'X86_64']: return None # Return none if SE Linux is in enforcing mode cpuid = CPUID() if cpuid.is_selinux_enforcing: return None # Get the cpu info from the CPUID register max_extension_support = cpuid.get_max_extension_support() cache_info = cpuid.get_cache(max_extension_support) info = cpuid.get_info() processor_brand = cpuid.get_processor_brand(max_extension_support) # Get the Hz and scale hz_actual = cpuid.get_raw_hz() hz_actual = to_hz_string(hz_actual) # Get the Hz and scale scale, hz_advertised = _get_hz_string_from_brand(processor_brand) info = { 'vendor_id' : cpuid.get_vendor_id(), 'hardware' : '', 'brand' : processor_brand, 'hz_advertised' : to_friendly_hz(hz_advertised, scale), 'hz_actual' : to_friendly_hz(hz_actual, 6), 'hz_advertised_raw' : to_raw_hz(hz_advertised, scale), 'hz_actual_raw' : to_raw_hz(hz_actual, 6), 'arch' : arch, 'bits' : bits, 'count' : DataSource.cpu_count, 'raw_arch_string' : DataSource.raw_arch_string, 'l2_cache_size' : cache_info['size_kb'], 'l2_cache_line_size' : cache_info['line_size_b'], 'l2_cache_associativity' : hex(cache_info['associativity']), 'stepping' : info['stepping'], 'model' : info['model'], 'family' : info['family'], 'processor_type' : info['processor_type'], 'extended_model' : info['extended_model'], 'extended_family' : info['extended_family'], 'flags' : cpuid.get_flags(max_extension_support) } return obj_to_b64(info) def get_cpu_info_from_proc_cpuinfo(): ''' Returns the CPU info gathered from /proc/cpuinfo. Will return None if /proc/cpuinfo is not found. ''' try: # Just return None if there is no cpuinfo if not DataSource.has_proc_cpuinfo(): return None returncode, output = DataSource.cat_proc_cpuinfo() if returncode != 0: return None # Various fields vendor_id = _get_field(False, output, None, '', 'vendor_id', 'vendor id', 'vendor') processor_brand = _get_field(True, output, None, None, 'model name','cpu', 'processor') cache_size = _get_field(False, output, None, '', 'cache size') stepping = _get_field(False, output, int, 0, 'stepping') model = _get_field(False, output, int, 0, 'model') family = _get_field(False, output, int, 0, 'cpu family') hardware = _get_field(False, output, None, '', 'Hardware') # Flags flags = _get_field(False, output, None, None, 'flags', 'Features').split() flags.sort() # Convert from MHz string to Hz hz_actual = _get_field(False, output, None, '', 'cpu MHz', 'cpu speed', 'clock') hz_actual = hz_actual.lower().rstrip('mhz').strip() hz_actual = to_hz_string(hz_actual) # Convert from GHz/MHz string to Hz scale, hz_advertised = _get_hz_string_from_brand(processor_brand) # Try getting the Hz for a BeagleBone if hz_advertised == '0.0': scale, hz_advertised = _get_hz_string_from_beagle_bone() hz_actual = hz_advertised # Try getting the Hz for a lscpu if hz_advertised == '0.0': scale, hz_advertised = _get_hz_string_from_lscpu() hz_actual = hz_advertised # Get the CPU arch and bits arch, bits = parse_arch(DataSource.raw_arch_string) return { 'vendor_id' : vendor_id, 'hardware' : hardware, 'brand' : processor_brand, 'hz_advertised' : to_friendly_hz(hz_advertised, scale), 'hz_actual' : to_friendly_hz(hz_actual, 6), 'hz_advertised_raw' : to_raw_hz(hz_advertised, scale), 'hz_actual_raw' : to_raw_hz(hz_actual, 6), 'arch' : arch, 'bits' : bits, 'count' : DataSource.cpu_count, 'raw_arch_string' : DataSource.raw_arch_string, 'l2_cache_size' : cache_size, 'l2_cache_line_size' : 0, 'l2_cache_associativity' : 0, 'stepping' : stepping, 'model' : model, 'family' : family, 'processor_type' : 0, 'extended_model' : 0, 'extended_family' : 0, 'flags' : flags } except: #raise # NOTE: To have this throw on error, uncomment this line return None def get_cpu_info_from_dmesg(): ''' Returns the CPU info gathered from dmesg. Will return None if dmesg is not found or does not have the desired info. ''' try: # Just return None if there is no dmesg if not DataSource.has_dmesg(): return None # If dmesg fails return None returncode, output = DataSource.dmesg_a() if output == None or returncode != 0: return None # Processor Brand long_brand = output.split('CPU: ')[1].split('\n')[0] processor_brand = long_brand.rsplit('(', 1)[0] processor_brand = processor_brand.strip() # Hz scale = 0 hz_actual = long_brand.rsplit('(', 1)[1].split(' ')[0].lower() if hz_actual.endswith('mhz'): scale = 6 elif hz_actual.endswith('ghz'): scale = 9 hz_actual = hz_actual.split('-')[0] hz_actual = to_hz_string(hz_actual) # Various fields fields = output.split('CPU: ')[1].split('\n')[1].split('\n')[0].strip().split(' ') vendor_id = None stepping = None model = None family = None for field in fields: name, value = field.split('=') name = name.strip().lower() value = value.strip() if name == 'origin': vendor_id = value.strip('"') elif name == 'stepping': stepping = int(value) elif name == 'model': model = int(value, 16) elif name == 'family': family = int(value, 16) # Flags flag_lines = [] for category in [' Features=', ' Features2=', ' AMD Features=', ' AMD Features2=']: if category in output: flag_lines.append(output.split(category)[1].split('\n')[0]) flags = [] for line in flag_lines: line = line.split('<')[1].split('>')[0].lower() for flag in line.split(','): flags.append(flag) flags.sort() # Convert from GHz/MHz string to Hz scale, hz_advertised = _get_hz_string_from_brand(processor_brand) # Get the CPU arch and bits arch, bits = parse_arch(DataSource.raw_arch_string) return { 'vendor_id' : vendor_id, 'hardware' : '', 'brand' : processor_brand, 'hz_advertised' : to_friendly_hz(hz_advertised, scale), 'hz_actual' : to_friendly_hz(hz_actual, 6), 'hz_advertised_raw' : to_raw_hz(hz_advertised, scale), 'hz_actual_raw' : to_raw_hz(hz_actual, 6), 'arch' : arch, 'bits' : bits, 'count' : DataSource.cpu_count, 'raw_arch_string' : DataSource.raw_arch_string, 'l2_cache_size' : 0, 'l2_cache_line_size' : 0, 'l2_cache_associativity' : 0, 'stepping' : stepping, 'model' : model, 'family' : family, 'processor_type' : 0, 'extended_model' : 0, 'extended_family' : 0, 'flags' : flags } except: return None def get_cpu_info_from_sysctl(): ''' Returns the CPU info gathered from sysctl. Will return None if sysctl is not found. ''' try: # Just return None if there is no sysctl if not DataSource.has_sysctl(): return None # If sysctl fails return None returncode, output = DataSource.sysctl_machdep_cpu_hw_cpufrequency() if output == None or returncode != 0: return None # Various fields vendor_id = _get_field(False, output, None, None, 'machdep.cpu.vendor') processor_brand = _get_field(True, output, None, None, 'machdep.cpu.brand_string') cache_size = _get_field(False, output, None, None, 'machdep.cpu.cache.size') stepping = _get_field(False, output, int, 0, 'machdep.cpu.stepping') model = _get_field(False, output, int, 0, 'machdep.cpu.model') family = _get_field(False, output, int, 0, 'machdep.cpu.family') # Flags flags = _get_field(False, output, None, None, 'machdep.cpu.features').lower().split() flags.sort() # Convert from GHz/MHz string to Hz scale, hz_advertised = _get_hz_string_from_brand(processor_brand) hz_actual = _get_field(False, output, None, None, 'hw.cpufrequency') hz_actual = to_hz_string(hz_actual) # Get the CPU arch and bits arch, bits = parse_arch(DataSource.raw_arch_string) return { 'vendor_id' : vendor_id, 'hardware' : '', 'brand' : processor_brand, 'hz_advertised' : to_friendly_hz(hz_advertised, scale), 'hz_actual' : to_friendly_hz(hz_actual, 0), 'hz_advertised_raw' : to_raw_hz(hz_advertised, scale), 'hz_actual_raw' : to_raw_hz(hz_actual, 0), 'arch' : arch, 'bits' : bits, 'count' : DataSource.cpu_count, 'raw_arch_string' : DataSource.raw_arch_string, 'l2_cache_size' : cache_size, 'l2_cache_line_size' : 0, 'l2_cache_associativity' : 0, 'stepping' : stepping, 'model' : model, 'family' : family, 'processor_type' : 0, 'extended_model' : 0, 'extended_family' : 0, 'flags' : flags } except: return None def get_cpu_info_from_sysinfo(): ''' Returns the CPU info gathered from sysinfo. Will return None if sysinfo is not found. ''' try: # Just return None if there is no sysinfo if not DataSource.has_sysinfo(): return None # If sysinfo fails return None returncode, output = DataSource.sysinfo_cpu() if output == None or returncode != 0: return None # Various fields vendor_id = '' #_get_field(False, output, None, None, 'CPU #0: ') processor_brand = output.split('CPU #0: "')[1].split('"\n')[0] cache_size = '' #_get_field(False, output, None, None, 'machdep.cpu.cache.size') stepping = int(output.split(', stepping ')[1].split(',')[0].strip()) model = int(output.split(', model ')[1].split(',')[0].strip()) family = int(output.split(', family ')[1].split(',')[0].strip()) # Flags flags = [] for line in output.split('\n'): if line.startswith('\t\t'): for flag in line.strip().lower().split(): flags.append(flag) flags.sort() # Convert from GHz/MHz string to Hz scale, hz_advertised = _get_hz_string_from_brand(processor_brand) hz_actual = hz_advertised # Get the CPU arch and bits arch, bits = parse_arch(DataSource.raw_arch_string) return { 'vendor_id' : vendor_id, 'hardware' : '', 'brand' : processor_brand, 'hz_advertised' : to_friendly_hz(hz_advertised, scale), 'hz_actual' : to_friendly_hz(hz_actual, scale), 'hz_advertised_raw' : to_raw_hz(hz_advertised, scale), 'hz_actual_raw' : to_raw_hz(hz_actual, scale), 'arch' : arch, 'bits' : bits, 'count' : DataSource.cpu_count, 'raw_arch_string' : DataSource.raw_arch_string, 'l2_cache_size' : cache_size, 'l2_cache_line_size' : 0, 'l2_cache_associativity' : 0, 'stepping' : stepping, 'model' : model, 'family' : family, 'processor_type' : 0, 'extended_model' : 0, 'extended_family' : 0, 'flags' : flags } except: return None def get_cpu_info_from_registry(): ''' FIXME: Is missing many of the newer CPU flags like sse3 Returns the CPU info gathered from the Windows Registry. Will return None if not on Windows. ''' try: # Just return None if not on Windows if not DataSource.is_windows: return None # Get the CPU name processor_brand = DataSource.winreg_processor_brand() # Get the CPU vendor id vendor_id = DataSource.winreg_vendor_id() # Get the CPU arch and bits raw_arch_string = DataSource.winreg_raw_arch_string() arch, bits = parse_arch(raw_arch_string) # Get the actual CPU Hz hz_actual = DataSource.winreg_hz_actual() hz_actual = to_hz_string(hz_actual) # Get the advertised CPU Hz scale, hz_advertised = _get_hz_string_from_brand(processor_brand) # Get the CPU features feature_bits = DataSource.winreg_feature_bits() def is_set(bit): mask = 0x80000000 >> bit retval = mask & feature_bits > 0 return retval # http://en.wikipedia.org/wiki/CPUID # http://unix.stackexchange.com/questions/43539/what-do-the-flags-in-proc-cpuinfo-mean # http://www.lohninger.com/helpcsuite/public_constants_cpuid.htm flags = { 'fpu' : is_set(0), # Floating Point Unit 'vme' : is_set(1), # V86 Mode Extensions 'de' : is_set(2), # Debug Extensions - I/O breakpoints supported 'pse' : is_set(3), # Page Size Extensions (4 MB pages supported) 'tsc' : is_set(4), # Time Stamp Counter and RDTSC instruction are available 'msr' : is_set(5), # Model Specific Registers 'pae' : is_set(6), # Physical Address Extensions (36 bit address, 2MB pages) 'mce' : is_set(7), # Machine Check Exception supported 'cx8' : is_set(8), # Compare Exchange Eight Byte instruction available 'apic' : is_set(9), # Local APIC present (multiprocessor operation support) 'sepamd' : is_set(10), # Fast system calls (AMD only) 'sep' : is_set(11), # Fast system calls 'mtrr' : is_set(12), # Memory Type Range Registers 'pge' : is_set(13), # Page Global Enable 'mca' : is_set(14), # Machine Check Architecture 'cmov' : is_set(15), # Conditional MOVe instructions 'pat' : is_set(16), # Page Attribute Table 'pse36' : is_set(17), # 36 bit Page Size Extensions 'serial' : is_set(18), # Processor Serial Number 'clflush' : is_set(19), # Cache Flush #'reserved1' : is_set(20), # reserved 'dts' : is_set(21), # Debug Trace Store 'acpi' : is_set(22), # ACPI support 'mmx' : is_set(23), # MultiMedia Extensions 'fxsr' : is_set(24), # FXSAVE and FXRSTOR instructions 'sse' : is_set(25), # SSE instructions 'sse2' : is_set(26), # SSE2 (WNI) instructions 'ss' : is_set(27), # self snoop #'reserved2' : is_set(28), # reserved 'tm' : is_set(29), # Automatic clock control 'ia64' : is_set(30), # IA64 instructions '3dnow' : is_set(31) # 3DNow! instructions available } # Get a list of only the flags that are true flags = [k for k, v in flags.items() if v] flags.sort() return { 'vendor_id' : vendor_id, 'hardware' : '', 'brand' : processor_brand, 'hz_advertised' : to_friendly_hz(hz_advertised, scale), 'hz_actual' : to_friendly_hz(hz_actual, 6), 'hz_advertised_raw' : to_raw_hz(hz_advertised, scale), 'hz_actual_raw' : to_raw_hz(hz_actual, 6), 'arch' : arch, 'bits' : bits, 'count' : DataSource.cpu_count, 'raw_arch_string' : raw_arch_string, 'l2_cache_size' : 0, 'l2_cache_line_size' : 0, 'l2_cache_associativity' : 0, 'stepping' : 0, 'model' : 0, 'family' : 0, 'processor_type' : 0, 'extended_model' : 0, 'extended_family' : 0, 'flags' : flags } except: return None def get_cpu_info_from_kstat(): ''' Returns the CPU info gathered from isainfo and kstat. Will return None if isainfo or kstat are not found. ''' try: # Just return None if there is no isainfo or kstat if not DataSource.has_isainfo() or not DataSource.has_kstat(): return None # If isainfo fails return None returncode, flag_output = DataSource.isainfo_vb() if flag_output == None or returncode != 0: return None # If kstat fails return None returncode, kstat = DataSource.kstat_m_cpu_info() if kstat == None or returncode != 0: return None # Various fields vendor_id = kstat.split('\tvendor_id ')[1].split('\n')[0].strip() processor_brand = kstat.split('\tbrand ')[1].split('\n')[0].strip() cache_size = 0 stepping = int(kstat.split('\tstepping ')[1].split('\n')[0].strip()) model = int(kstat.split('\tmodel ')[1].split('\n')[0].strip()) family = int(kstat.split('\tfamily ')[1].split('\n')[0].strip()) # Flags flags = flag_output.strip().split('\n')[-1].strip().lower().split() flags.sort() # Convert from GHz/MHz string to Hz scale = 6 hz_advertised = kstat.split('\tclock_MHz ')[1].split('\n')[0].strip() hz_advertised = to_hz_string(hz_advertised) # Convert from GHz/MHz string to Hz hz_actual = kstat.split('\tcurrent_clock_Hz ')[1].split('\n')[0].strip() hz_actual = to_hz_string(hz_actual) # Get the CPU arch and bits arch, bits = parse_arch(DataSource.raw_arch_string) return { 'vendor_id' : vendor_id, 'hardware' : '', 'brand' : processor_brand, 'hz_advertised' : to_friendly_hz(hz_advertised, scale), 'hz_actual' : to_friendly_hz(hz_actual, 0), 'hz_advertised_raw' : to_raw_hz(hz_advertised, scale), 'hz_actual_raw' : to_raw_hz(hz_actual, 0), 'arch' : arch, 'bits' : bits, 'count' : DataSource.cpu_count, 'raw_arch_string' : DataSource.raw_arch_string, 'l2_cache_size' : cache_size, 'l2_cache_line_size' : 0, 'l2_cache_associativity' : 0, 'stepping' : stepping, 'model' : model, 'family' : family, 'processor_type' : 0, 'extended_model' : 0, 'extended_family' : 0, 'flags' : flags } except: return None def get_cpu_info(): info = None # Try the Windows registry if not info: info = get_cpu_info_from_registry() # Try /proc/cpuinfo if not info: info = get_cpu_info_from_proc_cpuinfo() # Try sysctl if not info: info = get_cpu_info_from_sysctl() # Try kstat if not info: info = get_cpu_info_from_kstat() # Try dmesg if not info: info = get_cpu_info_from_dmesg() # Try sysinfo if not info: info = get_cpu_info_from_sysinfo() # Try querying the CPU cpuid register if not info: info = get_cpu_info_from_cpuid() return info # Make sure we are running on a supported system def _check_arch(): arch, bits = parse_arch(DataSource.raw_arch_string) if not arch in ['X86_32', 'X86_64', 'ARM_7', 'ARM_8']: raise Exception("py-cpuinfo currently only works on X86 and some ARM CPUs.") def main(): try: _check_arch() except Exception as err: sys.stderr.write(str(err) + "\n") sys.exit(1) info = get_cpu_info() if info: print('Vendor ID: {0}'.format(info.get('vendor_id', ''))) print('Hardware Raw: {0}'.format(info.get('hardware', ''))) print('Brand: {0}'.format(info.get('brand', ''))) print('Hz Advertised: {0}'.format(info.get('hz_advertised', ''))) print('Hz Actual: {0}'.format(info.get('hz_actual', ''))) print('Hz Advertised Raw: {0}'.format(info.get('hz_advertised_raw', ''))) print('Hz Actual Raw: {0}'.format(info.get('hz_actual_raw', ''))) print('Arch: {0}'.format(info.get('arch', ''))) print('Bits: {0}'.format(info.get('bits', ''))) print('Count: {0}'.format(info.get('count', ''))) print('Raw Arch String: {0}'.format(info.get('raw_arch_string', ''))) print('L2 Cache Size: {0}'.format(info.get('l2_cache_size', ''))) print('L2 Cache Line Size: {0}'.format(info.get('l2_cache_line_size', ''))) print('L2 Cache Associativity: {0}'.format(info.get('l2_cache_associativity', ''))) print('Stepping: {0}'.format(info.get('stepping', ''))) print('Model: {0}'.format(info.get('model', ''))) print('Family: {0}'.format(info.get('family', ''))) print('Processor Type: {0}'.format(info.get('processor_type', ''))) print('Extended Model: {0}'.format(info.get('extended_model', ''))) print('Extended Family: {0}'.format(info.get('extended_family', ''))) print('Flags: {0}'.format(', '.join(info.get('flags', '')))) else: sys.stderr.write("Failed to find cpu info\n") sys.exit(1) if __name__ == '__main__': main() else: _check_arch()