Just some glue logic to query progress and results from benchmark. Needed to move files around, so oth standalone and addon are happy.
1876 lines
54 KiB
Python
1876 lines
54 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: UTF-8 -*-
|
|
|
|
# Copyright (c) 2014-2017, Matthew Brennan Jones <matthew.brennan.jones@gmail.com>
|
|
# Py-cpuinfo gets CPU info with pure Python 2 & 3
|
|
# It uses the MIT 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.
|
|
|
|
CPUINFO_VERSION = (3, 3, 0)
|
|
|
|
import os, sys
|
|
import glob
|
|
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()
|
|
can_cpuid = True
|
|
|
|
@staticmethod
|
|
def has_proc_cpuinfo():
|
|
return os.path.exists('/proc/cpuinfo')
|
|
|
|
@staticmethod
|
|
def has_dmesg():
|
|
return len(program_paths('dmesg')) > 0
|
|
|
|
@staticmethod
|
|
def has_var_run_dmesg_boot():
|
|
return os.path.exists('/var/run/dmesg.boot')
|
|
|
|
@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 has_ibm_pa_features():
|
|
return len(program_paths('lsprop')) > 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 cat_var_run_dmesg_boot():
|
|
return run_and_get_stdout(['cat', '/var/run/dmesg.boot'])
|
|
|
|
@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 ibm_pa_features():
|
|
ibm_features = glob.glob('/proc/device-tree/cpus/*/ibm,pa-features')
|
|
if ibm_features:
|
|
return run_and_get_stdout(['lsprop', ibm_features[0]])
|
|
|
|
@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):
|
|
try:
|
|
a = base64.b64decode(thing)
|
|
b = pickle.loads(a)
|
|
return b
|
|
except:
|
|
return {}
|
|
|
|
def run_and_get_stdout(command, pipe_command=None):
|
|
if not pipe_command:
|
|
p1 = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=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, stdin=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 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 to_friendly_bytes(input):
|
|
if not input:
|
|
return input
|
|
|
|
formats = {
|
|
r"^[0-9]+B$" : 'B',
|
|
r"^[0-9]+K$" : 'KB',
|
|
r"^[0-9]+M$" : 'MB',
|
|
r"^[0-9]+G$" : 'GB'
|
|
}
|
|
|
|
for pattern, friendly_size in formats.items():
|
|
if re.match(pattern, input):
|
|
return "{0} {1}".format(input[ : -1].strip(), friendly_size)
|
|
|
|
return input
|
|
|
|
def _parse_cpu_string(cpu_string):
|
|
# Get location of fields at end of string
|
|
fields_index = cpu_string.find('(', cpu_string.find('@'))
|
|
#print(fields_index)
|
|
|
|
# Processor Brand
|
|
processor_brand = cpu_string
|
|
if fields_index != -1:
|
|
processor_brand = cpu_string[0 : fields_index].strip()
|
|
#print('processor_brand: ', processor_brand)
|
|
|
|
fields = None
|
|
if fields_index != -1:
|
|
fields = cpu_string[fields_index : ]
|
|
#print('fields: ', fields)
|
|
|
|
# Hz
|
|
scale, hz_brand = _get_hz_string_from_brand(processor_brand)
|
|
|
|
# Various fields
|
|
vendor_id, stepping, model, family = (None, None, None, None)
|
|
if fields:
|
|
try:
|
|
fields = fields.rsplit('(', 1)[1].split(')')[0].split(',')
|
|
fields = [f.strip().lower() for f in fields]
|
|
fields = [f.split(':') for f in fields]
|
|
fields = [{f[0].strip() : f[1].strip()} for f in fields]
|
|
#print('fields: ', fields)
|
|
for field in fields:
|
|
name = list(field.keys())[0]
|
|
value = list(field.values())[0]
|
|
#print('name:{0}, value:{1}'.format(name, value))
|
|
if name == 'origin':
|
|
vendor_id = value.strip('"')
|
|
elif name == 'stepping':
|
|
stepping = int(value.lstrip('0x'), 16)
|
|
elif name == 'model':
|
|
model = int(value.lstrip('0x'), 16)
|
|
elif name in ['fam', 'family']:
|
|
family = int(value.lstrip('0x'), 16)
|
|
except:
|
|
#raise
|
|
pass
|
|
|
|
return (processor_brand, hz_brand, scale, vendor_id, stepping, model, family)
|
|
|
|
def _parse_dmesg_output(output):
|
|
try:
|
|
# Get all the dmesg lines that might contain a CPU string
|
|
lines = output.split(' CPU0:')[1:] + \
|
|
output.split(' CPU1:')[1:] + \
|
|
output.split(' CPU:')[1:] + \
|
|
output.split('\nCPU0:')[1:] + \
|
|
output.split('\nCPU1:')[1:] + \
|
|
output.split('\nCPU:')[1:]
|
|
lines = [l.split('\n')[0].strip() for l in lines]
|
|
|
|
# Convert the lines to CPU strings
|
|
cpu_strings = [_parse_cpu_string(l) for l in lines]
|
|
|
|
# Find the CPU string that has the most fields
|
|
best_string = None
|
|
highest_count = 0
|
|
for cpu_string in cpu_strings:
|
|
count = sum([n is not None for n in cpu_string])
|
|
if count > highest_count:
|
|
highest_count = count
|
|
best_string = cpu_string
|
|
|
|
# If no CPU string was found, return {}
|
|
if not best_string:
|
|
return {}
|
|
|
|
processor_brand, hz_actual, scale, vendor_id, stepping, model, family = best_string
|
|
|
|
# Origin
|
|
if ' Origin=' in output:
|
|
fields = output[output.find(' Origin=') : ].split('\n')[0]
|
|
fields = fields.strip().split()
|
|
fields = [n.strip().split('=') for n in fields]
|
|
fields = [{n[0].strip().lower() : n[1].strip()} for n in fields]
|
|
#print('fields: ', fields)
|
|
for field in fields:
|
|
name = list(field.keys())[0]
|
|
value = list(field.values())[0]
|
|
#print('name:{0}, value:{1}'.format(name, value))
|
|
if name == 'origin':
|
|
vendor_id = value.strip('"')
|
|
elif name == 'stepping':
|
|
stepping = int(value.lstrip('0x'), 16)
|
|
elif name == 'model':
|
|
model = int(value.lstrip('0x'), 16)
|
|
elif name in ['fam', 'family']:
|
|
family = int(value.lstrip('0x'), 16)
|
|
#print('FIELDS: ', (vendor_id, stepping, model, family))
|
|
|
|
# Features
|
|
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)
|
|
|
|
info = {
|
|
'vendor_id' : vendor_id,
|
|
'brand' : processor_brand,
|
|
|
|
'stepping' : stepping,
|
|
'model' : model,
|
|
'family' : family,
|
|
'flags' : flags
|
|
}
|
|
|
|
if hz_advertised and hz_advertised != '0.0':
|
|
info['hz_advertised'] = to_friendly_hz(hz_advertised, scale)
|
|
info['hz_actual'] = to_friendly_hz(hz_actual, scale)
|
|
|
|
if hz_advertised and hz_advertised != '0.0':
|
|
info['hz_advertised_raw'] = to_raw_hz(hz_advertised, scale)
|
|
info['hz_actual_raw'] = to_raw_hz(hz_actual, scale)
|
|
|
|
return {k: v for k, v in info.items() if v}
|
|
except:
|
|
#raise
|
|
pass
|
|
|
|
return {}
|
|
|
|
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|aarch64$', 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$|^ppc64le$', 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
|
|
}
|
|
|
|
# http://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 = {}
|
|
|
|
# http://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 ...
|
|
|
|
# http://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
|
|
|
|
# http://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
|
|
|
|
# http://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 actual_get_cpu_info_from_cpuid():
|
|
'''
|
|
Warning! This function has the potential to crash the Python runtime.
|
|
Do not call it directly. Use the _get_cpu_info_from_cpuid function instead.
|
|
It will safely call this function in another process.
|
|
'''
|
|
# 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 obj_to_b64({})
|
|
|
|
# Return none if SE Linux is in enforcing mode
|
|
cpuid = CPUID()
|
|
if cpuid.is_selinux_enforcing:
|
|
return obj_to_b64({})
|
|
|
|
# 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),
|
|
|
|
'l2_cache_size' : to_friendly_bytes(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)
|
|
}
|
|
|
|
info = {k: v for k, v in info.items() if v}
|
|
return obj_to_b64(info)
|
|
|
|
def _get_cpu_info_from_cpuid():
|
|
'''
|
|
Returns the CPU info gathered by querying the X86 cpuid register in a new process.
|
|
Returns {} on non X86 cpus.
|
|
Returns {} if SELinux is in enforcing mode.
|
|
'''
|
|
|
|
# Return {} if can't cpuid
|
|
if not DataSource.can_cpuid:
|
|
return {}
|
|
|
|
# Get the CPU arch and bits
|
|
arch, bits = parse_arch(DataSource.raw_arch_string)
|
|
|
|
# Return {} if this is not an X86 CPU
|
|
if not arch in ['X86_32', 'X86_64']:
|
|
return {}
|
|
|
|
returncode, output = run_and_get_stdout([sys.executable, "-c", "import cpuinfo; print(cpuinfo.actual_get_cpu_info_from_cpuid())"])
|
|
if returncode != 0:
|
|
return {}
|
|
info = b64_to_obj(output)
|
|
return info
|
|
|
|
def _get_cpu_info_from_proc_cpuinfo():
|
|
'''
|
|
Returns the CPU info gathered from /proc/cpuinfo.
|
|
Returns {} if /proc/cpuinfo is not found.
|
|
'''
|
|
try:
|
|
# Just return {} if there is no cpuinfo
|
|
if not DataSource.has_proc_cpuinfo():
|
|
return {}
|
|
|
|
returncode, output = DataSource.cat_proc_cpuinfo()
|
|
if returncode != 0:
|
|
return {}
|
|
|
|
# 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')
|
|
if flags:
|
|
flags = flags.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 = (0, None)
|
|
try:
|
|
scale, hz_advertised = _get_hz_string_from_brand(processor_brand)
|
|
except Exception:
|
|
pass
|
|
|
|
info = {
|
|
'hardware' : hardware,
|
|
'brand' : processor_brand,
|
|
|
|
'l3_cache_size' : to_friendly_bytes(cache_size),
|
|
'flags' : flags,
|
|
'vendor_id' : vendor_id,
|
|
'stepping' : stepping,
|
|
'model' : model,
|
|
'family' : family,
|
|
}
|
|
|
|
# Make the Hz the same for actual and advertised if missing any
|
|
if not hz_advertised or hz_advertised == '0.0':
|
|
hz_advertised = hz_actual
|
|
scale = 6
|
|
elif not hz_actual or hz_actual == '0.0':
|
|
hz_actual = hz_advertised
|
|
|
|
# Add the Hz if there is one
|
|
if to_raw_hz(hz_advertised, scale) > (0, 0):
|
|
info['hz_advertised'] = to_friendly_hz(hz_advertised, scale)
|
|
info['hz_advertised_raw'] = to_raw_hz(hz_advertised, scale)
|
|
if to_raw_hz(hz_actual, scale) > (0, 0):
|
|
info['hz_actual'] = to_friendly_hz(hz_actual, 6)
|
|
info['hz_actual_raw'] = to_raw_hz(hz_actual, 6)
|
|
|
|
info = {k: v for k, v in info.items() if v}
|
|
return info
|
|
except:
|
|
#raise # NOTE: To have this throw on error, uncomment this line
|
|
return {}
|
|
|
|
def _get_cpu_info_from_cpufreq_info():
|
|
'''
|
|
Returns the CPU info gathered from cpufreq-info.
|
|
Returns {} if cpufreq-info is not found.
|
|
'''
|
|
try:
|
|
scale, hz_brand = 1, '0.0'
|
|
|
|
if not DataSource.has_cpufreq_info():
|
|
return {}
|
|
|
|
returncode, output = DataSource.cpufreq_info()
|
|
if returncode != 0:
|
|
return {}
|
|
|
|
hz_brand = output.split('current CPU frequency is')[1].split('\n')[0]
|
|
i = hz_brand.find('Hz')
|
|
assert(i != -1)
|
|
hz_brand = hz_brand[0 : i+2].strip().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)
|
|
|
|
info = {
|
|
'hz_advertised' : to_friendly_hz(hz_brand, scale),
|
|
'hz_actual' : to_friendly_hz(hz_brand, scale),
|
|
'hz_advertised_raw' : to_raw_hz(hz_brand, scale),
|
|
'hz_actual_raw' : to_raw_hz(hz_brand, scale),
|
|
}
|
|
|
|
info = {k: v for k, v in info.items() if v}
|
|
return info
|
|
except:
|
|
#raise # NOTE: To have this throw on error, uncomment this line
|
|
return {}
|
|
|
|
def _get_cpu_info_from_lscpu():
|
|
'''
|
|
Returns the CPU info gathered from lscpu.
|
|
Returns {} if lscpu is not found.
|
|
'''
|
|
try:
|
|
if not DataSource.has_lscpu():
|
|
return {}
|
|
|
|
returncode, output = DataSource.lscpu()
|
|
if returncode != 0:
|
|
return {}
|
|
|
|
info = {}
|
|
|
|
new_hz = _get_field(False, output, None, None, 'CPU max MHz', 'CPU MHz')
|
|
if new_hz:
|
|
new_hz = to_hz_string(new_hz)
|
|
scale = 6
|
|
info['hz_advertised'] = to_friendly_hz(new_hz, scale)
|
|
info['hz_actual'] = to_friendly_hz(new_hz, scale)
|
|
info['hz_advertised_raw'] = to_raw_hz(new_hz, scale)
|
|
info['hz_actual_raw'] = to_raw_hz(new_hz, scale)
|
|
|
|
vendor_id = _get_field(False, output, None, None, 'Vendor ID')
|
|
if vendor_id:
|
|
info['vendor_id'] = vendor_id
|
|
|
|
brand = _get_field(False, output, None, None, 'Model name')
|
|
if brand:
|
|
info['brand'] = brand
|
|
|
|
brand = _get_field(False, output, None, None, 'Model name')
|
|
if brand:
|
|
info['brand'] = brand
|
|
|
|
family = _get_field(False, output, None, None, 'CPU family')
|
|
if family and family.isdigit():
|
|
info['family'] = int(family)
|
|
|
|
stepping = _get_field(False, output, None, None, 'Stepping')
|
|
if stepping and stepping.isdigit():
|
|
info['stepping'] = int(stepping)
|
|
|
|
model = _get_field(False, output, None, None, 'Model')
|
|
if model and model.isdigit():
|
|
info['model'] = int(model)
|
|
|
|
l1_data_cache_size = _get_field(False, output, None, None, 'L1d cache')
|
|
if l1_data_cache_size:
|
|
info['l1_data_cache_size'] = to_friendly_bytes(l1_data_cache_size)
|
|
|
|
l1_instruction_cache_size = _get_field(False, output, None, None, 'L1i cache')
|
|
if l1_instruction_cache_size:
|
|
info['l1_instruction_cache_size'] = to_friendly_bytes(l1_instruction_cache_size)
|
|
|
|
l2_cache_size = _get_field(False, output, None, None, 'L2 cache')
|
|
if l2_cache_size:
|
|
info['l2_cache_size'] = to_friendly_bytes(l2_cache_size)
|
|
|
|
l3_cache_size = _get_field(False, output, None, None, 'L3 cache')
|
|
if l3_cache_size:
|
|
info['l3_cache_size'] = to_friendly_bytes(l3_cache_size)
|
|
|
|
# Flags
|
|
flags = _get_field(False, output, None, None, 'flags', 'Features')
|
|
if flags:
|
|
flags = flags.split()
|
|
flags.sort()
|
|
info['flags'] = flags
|
|
|
|
info = {k: v for k, v in info.items() if v}
|
|
return info
|
|
except:
|
|
#raise # NOTE: To have this throw on error, uncomment this line
|
|
return {}
|
|
|
|
def _get_cpu_info_from_dmesg():
|
|
'''
|
|
Returns the CPU info gathered from dmesg.
|
|
Returns {} if dmesg is not found or does not have the desired info.
|
|
'''
|
|
# Just return {} if there is no dmesg
|
|
if not DataSource.has_dmesg():
|
|
return {}
|
|
|
|
# If dmesg fails return {}
|
|
returncode, output = DataSource.dmesg_a()
|
|
if output == None or returncode != 0:
|
|
return {}
|
|
|
|
return _parse_dmesg_output(output)
|
|
|
|
|
|
# https://openpowerfoundation.org/wp-content/uploads/2016/05/LoPAPR_DRAFT_v11_24March2016_cmt1.pdf
|
|
# page 767
|
|
def _get_cpu_info_from_ibm_pa_features():
|
|
'''
|
|
Returns the CPU info gathered from lsprop /proc/device-tree/cpus/*/ibm,pa-features
|
|
Returns {} if lsprop is not found or ibm,pa-features does not have the desired info.
|
|
'''
|
|
try:
|
|
# Just return {} if there is no lsprop
|
|
if not DataSource.has_ibm_pa_features():
|
|
return {}
|
|
|
|
# If ibm,pa-features fails return {}
|
|
returncode, output = DataSource.ibm_pa_features()
|
|
if output == None or returncode != 0:
|
|
return {}
|
|
|
|
# Filter out invalid characters from output
|
|
value = output.split("ibm,pa-features")[1].lower()
|
|
value = [s for s in value if s in list('0123456789abcfed')]
|
|
value = ''.join(value)
|
|
|
|
# Get data converted to Uint32 chunks
|
|
left = int(value[0 : 8], 16)
|
|
right = int(value[8 : 16], 16)
|
|
|
|
# Get the CPU flags
|
|
flags = {
|
|
# Byte 0
|
|
'mmu' : is_bit_set(left, 0),
|
|
'fpu' : is_bit_set(left, 1),
|
|
'slb' : is_bit_set(left, 2),
|
|
'run' : is_bit_set(left, 3),
|
|
#'reserved' : is_bit_set(left, 4),
|
|
'dabr' : is_bit_set(left, 5),
|
|
'ne' : is_bit_set(left, 6),
|
|
'wtr' : is_bit_set(left, 7),
|
|
|
|
# Byte 1
|
|
'mcr' : is_bit_set(left, 8),
|
|
'dsisr' : is_bit_set(left, 9),
|
|
'lp' : is_bit_set(left, 10),
|
|
'ri' : is_bit_set(left, 11),
|
|
'dabrx' : is_bit_set(left, 12),
|
|
'sprg3' : is_bit_set(left, 13),
|
|
'rislb' : is_bit_set(left, 14),
|
|
'pp' : is_bit_set(left, 15),
|
|
|
|
# Byte 2
|
|
'vpm' : is_bit_set(left, 16),
|
|
'dss_2.05' : is_bit_set(left, 17),
|
|
#'reserved' : is_bit_set(left, 18),
|
|
'dar' : is_bit_set(left, 19),
|
|
#'reserved' : is_bit_set(left, 20),
|
|
'ppr' : is_bit_set(left, 21),
|
|
'dss_2.02' : is_bit_set(left, 22),
|
|
'dss_2.06' : is_bit_set(left, 23),
|
|
|
|
# Byte 3
|
|
'lsd_in_dscr' : is_bit_set(left, 24),
|
|
'ugr_in_dscr' : is_bit_set(left, 25),
|
|
#'reserved' : is_bit_set(left, 26),
|
|
#'reserved' : is_bit_set(left, 27),
|
|
#'reserved' : is_bit_set(left, 28),
|
|
#'reserved' : is_bit_set(left, 29),
|
|
#'reserved' : is_bit_set(left, 30),
|
|
#'reserved' : is_bit_set(left, 31),
|
|
|
|
# Byte 4
|
|
'sso_2.06' : is_bit_set(right, 0),
|
|
#'reserved' : is_bit_set(right, 1),
|
|
#'reserved' : is_bit_set(right, 2),
|
|
#'reserved' : is_bit_set(right, 3),
|
|
#'reserved' : is_bit_set(right, 4),
|
|
#'reserved' : is_bit_set(right, 5),
|
|
#'reserved' : is_bit_set(right, 6),
|
|
#'reserved' : is_bit_set(right, 7),
|
|
|
|
# Byte 5
|
|
'le' : is_bit_set(right, 8),
|
|
'cfar' : is_bit_set(right, 9),
|
|
'eb' : is_bit_set(right, 10),
|
|
'lsq_2.07' : is_bit_set(right, 11),
|
|
#'reserved' : is_bit_set(right, 12),
|
|
#'reserved' : is_bit_set(right, 13),
|
|
#'reserved' : is_bit_set(right, 14),
|
|
#'reserved' : is_bit_set(right, 15),
|
|
|
|
# Byte 6
|
|
'dss_2.07' : is_bit_set(right, 16),
|
|
#'reserved' : is_bit_set(right, 17),
|
|
#'reserved' : is_bit_set(right, 18),
|
|
#'reserved' : is_bit_set(right, 19),
|
|
#'reserved' : is_bit_set(right, 20),
|
|
#'reserved' : is_bit_set(right, 21),
|
|
#'reserved' : is_bit_set(right, 22),
|
|
#'reserved' : is_bit_set(right, 23),
|
|
|
|
# Byte 7
|
|
#'reserved' : is_bit_set(right, 24),
|
|
#'reserved' : is_bit_set(right, 25),
|
|
#'reserved' : is_bit_set(right, 26),
|
|
#'reserved' : is_bit_set(right, 27),
|
|
#'reserved' : is_bit_set(right, 28),
|
|
#'reserved' : is_bit_set(right, 29),
|
|
#'reserved' : is_bit_set(right, 30),
|
|
#'reserved' : is_bit_set(right, 31),
|
|
}
|
|
|
|
# Get a list of only the flags that are true
|
|
flags = [k for k, v in flags.items() if v]
|
|
flags.sort()
|
|
|
|
info = {
|
|
'flags' : flags
|
|
}
|
|
info = {k: v for k, v in info.items() if v}
|
|
|
|
return info
|
|
except:
|
|
return {}
|
|
|
|
|
|
def _get_cpu_info_from_cat_var_run_dmesg_boot():
|
|
'''
|
|
Returns the CPU info gathered from /var/run/dmesg.boot.
|
|
Returns {} if dmesg is not found or does not have the desired info.
|
|
'''
|
|
# Just return {} if there is no /var/run/dmesg.boot
|
|
if not DataSource.has_var_run_dmesg_boot():
|
|
return {}
|
|
|
|
# If dmesg.boot fails return {}
|
|
returncode, output = DataSource.cat_var_run_dmesg_boot()
|
|
if output == None or returncode != 0:
|
|
return {}
|
|
|
|
return _parse_dmesg_output(output)
|
|
|
|
|
|
def _get_cpu_info_from_sysctl():
|
|
'''
|
|
Returns the CPU info gathered from sysctl.
|
|
Returns {} if sysctl is not found.
|
|
'''
|
|
try:
|
|
# Just return {} if there is no sysctl
|
|
if not DataSource.has_sysctl():
|
|
return {}
|
|
|
|
# If sysctl fails return {}
|
|
returncode, output = DataSource.sysctl_machdep_cpu_hw_cpufrequency()
|
|
if output == None or returncode != 0:
|
|
return {}
|
|
|
|
# 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, '', 'machdep.cpu.features').lower().split()
|
|
flags.extend(_get_field(False, output, None, '', 'machdep.cpu.leaf7_features').lower().split())
|
|
flags.extend(_get_field(False, output, None, '', 'machdep.cpu.extfeatures').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)
|
|
|
|
info = {
|
|
'vendor_id' : vendor_id,
|
|
'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),
|
|
|
|
'l2_cache_size' : to_friendly_bytes(cache_size),
|
|
|
|
'stepping' : stepping,
|
|
'model' : model,
|
|
'family' : family,
|
|
'flags' : flags
|
|
}
|
|
|
|
info = {k: v for k, v in info.items() if v}
|
|
return info
|
|
except:
|
|
return {}
|
|
|
|
def _get_cpu_info_from_sysinfo():
|
|
'''
|
|
Returns the CPU info gathered from sysinfo.
|
|
Returns {} if sysinfo is not found.
|
|
'''
|
|
try:
|
|
# Just return {} if there is no sysinfo
|
|
if not DataSource.has_sysinfo():
|
|
return {}
|
|
|
|
# If sysinfo fails return {}
|
|
returncode, output = DataSource.sysinfo_cpu()
|
|
if output == None or returncode != 0:
|
|
return {}
|
|
|
|
# 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
|
|
|
|
info = {
|
|
'vendor_id' : vendor_id,
|
|
'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),
|
|
|
|
'l2_cache_size' : to_friendly_bytes(cache_size),
|
|
|
|
'stepping' : stepping,
|
|
'model' : model,
|
|
'family' : family,
|
|
'flags' : flags
|
|
}
|
|
|
|
info = {k: v for k, v in info.items() if v}
|
|
return info
|
|
except:
|
|
return {}
|
|
|
|
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.
|
|
Returns {} if not on Windows.
|
|
'''
|
|
try:
|
|
# Just return {} if not on Windows
|
|
if not DataSource.is_windows:
|
|
return {}
|
|
|
|
# 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()
|
|
|
|
info = {
|
|
'vendor_id' : vendor_id,
|
|
'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),
|
|
|
|
'flags' : flags
|
|
}
|
|
|
|
info = {k: v for k, v in info.items() if v}
|
|
return info
|
|
except:
|
|
return {}
|
|
|
|
def _get_cpu_info_from_kstat():
|
|
'''
|
|
Returns the CPU info gathered from isainfo and kstat.
|
|
Returns {} if isainfo or kstat are not found.
|
|
'''
|
|
try:
|
|
# Just return {} if there is no isainfo or kstat
|
|
if not DataSource.has_isainfo() or not DataSource.has_kstat():
|
|
return {}
|
|
|
|
# If isainfo fails return {}
|
|
returncode, flag_output = DataSource.isainfo_vb()
|
|
if flag_output == None or returncode != 0:
|
|
return {}
|
|
|
|
# If kstat fails return {}
|
|
returncode, kstat = DataSource.kstat_m_cpu_info()
|
|
if kstat == None or returncode != 0:
|
|
return {}
|
|
|
|
# Various fields
|
|
vendor_id = kstat.split('\tvendor_id ')[1].split('\n')[0].strip()
|
|
processor_brand = kstat.split('\tbrand ')[1].split('\n')[0].strip()
|
|
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)
|
|
|
|
info = {
|
|
'vendor_id' : vendor_id,
|
|
'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),
|
|
|
|
'stepping' : stepping,
|
|
'model' : model,
|
|
'family' : family,
|
|
'flags' : flags
|
|
}
|
|
|
|
info = {k: v for k, v in info.items() if v}
|
|
return info
|
|
except:
|
|
return {}
|
|
|
|
def CopyNewFields(info, new_info):
|
|
keys = [
|
|
'vendor_id', 'hardware', 'brand', 'hz_advertised', 'hz_actual',
|
|
'hz_advertised_raw', 'hz_actual_raw', 'arch', 'bits', 'count',
|
|
'raw_arch_string', 'l2_cache_size', 'l2_cache_line_size',
|
|
'l2_cache_associativity', 'stepping', 'model', 'family',
|
|
'processor_type', 'extended_model', 'extended_family', 'flags',
|
|
'l3_cache_size', 'l1_data_cache_size', 'l1_instruction_cache_size'
|
|
]
|
|
|
|
for key in keys:
|
|
if new_info.get(key, None) and not info.get(key, None):
|
|
info[key] = new_info[key]
|
|
|
|
def get_cpu_info():
|
|
'''
|
|
Returns the CPU info by using the best sources of information for your OS.
|
|
Returns {} if nothing is found.
|
|
'''
|
|
|
|
# Get the CPU arch and bits
|
|
arch, bits = parse_arch(DataSource.raw_arch_string)
|
|
|
|
info = {
|
|
'cpuinfo_version' : CPUINFO_VERSION,
|
|
'arch' : arch,
|
|
'bits' : bits,
|
|
'count' : DataSource.cpu_count,
|
|
'raw_arch_string' : DataSource.raw_arch_string,
|
|
}
|
|
|
|
# Try the Windows registry
|
|
CopyNewFields(info, _get_cpu_info_from_registry())
|
|
|
|
# Try /proc/cpuinfo
|
|
CopyNewFields(info, _get_cpu_info_from_proc_cpuinfo())
|
|
|
|
# Try cpufreq-info
|
|
CopyNewFields(info, _get_cpu_info_from_cpufreq_info())
|
|
|
|
# Try LSCPU
|
|
CopyNewFields(info, _get_cpu_info_from_lscpu())
|
|
|
|
# Try sysctl
|
|
CopyNewFields(info, _get_cpu_info_from_sysctl())
|
|
|
|
# Try kstat
|
|
CopyNewFields(info, _get_cpu_info_from_kstat())
|
|
|
|
# Try dmesg
|
|
CopyNewFields(info, _get_cpu_info_from_dmesg())
|
|
|
|
# Try /var/run/dmesg.boot
|
|
CopyNewFields(info, _get_cpu_info_from_cat_var_run_dmesg_boot())
|
|
|
|
# Try lsprop ibm,pa-features
|
|
CopyNewFields(info, _get_cpu_info_from_ibm_pa_features())
|
|
|
|
# Try sysinfo
|
|
CopyNewFields(info, _get_cpu_info_from_sysinfo())
|
|
|
|
# Try querying the CPU cpuid register
|
|
CopyNewFields(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', 'PPC_64']:
|
|
raise Exception("py-cpuinfo currently only works on X86 and some PPC and 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('cpuinfo version: {0}'.format(info.get('cpuinfo_version', '')))
|
|
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('L1 Data Cache Size: {0}'.format(info.get('l1_data_cache_size', '')))
|
|
print('L1 Instruction Cache Size: {0}'.format(info.get('l1_instruction_cache_size', '')))
|
|
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('L3 Cache Size: {0}'.format(info.get('l3_cache_size', '')))
|
|
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()
|