Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/cpuinfo/cpuinfo.py: 38%
1419 statements
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-10 06:15 +0000
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-10 06:15 +0000
1#!/usr/bin/env python
2# -*- coding: UTF-8 -*-
4# Copyright (c) 2014-2022 Matthew Brennan Jones <matthew.brennan.jones@gmail.com>
5# Py-cpuinfo gets CPU info with pure Python
6# It uses the MIT License
7# It is hosted at: https://github.com/workhorsy/py-cpuinfo
8#
9# Permission is hereby granted, free of charge, to any person obtaining
10# a copy of this software and associated documentation files (the
11# "Software"), to deal in the Software without restriction, including
12# without limitation the rights to use, copy, modify, merge, publish,
13# distribute, sublicense, and/or sell copies of the Software, and to
14# permit persons to whom the Software is furnished to do so, subject to
15# the following conditions:
16#
17# The above copyright notice and this permission notice shall be included
18# in all copies or substantial portions of the Software.
19#
20# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28CPUINFO_VERSION = (9, 0, 0)
29CPUINFO_VERSION_STRING = '.'.join([str(n) for n in CPUINFO_VERSION])
31import os, sys
32import platform
33import multiprocessing
34import ctypes
37CAN_CALL_CPUID_IN_SUBPROCESS = True
39g_trace = None
42class Trace(object):
43 def __init__(self, is_active, is_stored_in_string):
44 self._is_active = is_active
45 if not self._is_active:
46 return
48 from datetime import datetime
49 from io import StringIO
51 if is_stored_in_string:
52 self._output = StringIO()
53 else:
54 date = datetime.now().strftime("%Y-%m-%d_%H-%M-%S-%f")
55 self._output = open('cpuinfo_trace_{0}.trace'.format(date), 'w')
57 self._stdout = StringIO()
58 self._stderr = StringIO()
59 self._err = None
61 def header(self, msg):
62 if not self._is_active: return
64 from inspect import stack
65 frame = stack()[1]
66 file = frame[1]
67 line = frame[2]
68 self._output.write("{0} ({1} {2})\n".format(msg, file, line))
69 self._output.flush()
71 def success(self):
72 if not self._is_active: return
74 from inspect import stack
75 frame = stack()[1]
76 file = frame[1]
77 line = frame[2]
79 self._output.write("Success ... ({0} {1})\n\n".format(file, line))
80 self._output.flush()
82 def fail(self, msg):
83 if not self._is_active: return
85 from inspect import stack
86 frame = stack()[1]
87 file = frame[1]
88 line = frame[2]
90 if isinstance(msg, str):
91 msg = ''.join(['\t' + line for line in msg.split('\n')]) + '\n'
93 self._output.write(msg)
94 self._output.write("Failed ... ({0} {1})\n\n".format(file, line))
95 self._output.flush()
96 elif isinstance(msg, Exception):
97 from traceback import format_exc
98 err_string = format_exc()
99 self._output.write("\tFailed ... ({0} {1})\n".format(file, line))
100 self._output.write(''.join(['\t\t{0}\n'.format(n) for n in err_string.split('\n')]) + '\n')
101 self._output.flush()
103 def command_header(self, msg):
104 if not self._is_active: return
106 from inspect import stack
107 frame = stack()[3]
108 file = frame[1]
109 line = frame[2]
110 self._output.write("\t{0} ({1} {2})\n".format(msg, file, line))
111 self._output.flush()
113 def command_output(self, msg, output):
114 if not self._is_active: return
116 self._output.write("\t\t{0}\n".format(msg))
117 self._output.write(''.join(['\t\t\t{0}\n'.format(n) for n in output.split('\n')]) + '\n')
118 self._output.flush()
120 def keys(self, keys, info, new_info):
121 if not self._is_active: return
123 from inspect import stack
124 frame = stack()[2]
125 file = frame[1]
126 line = frame[2]
128 # List updated keys
129 self._output.write("\tChanged keys ({0} {1})\n".format(file, line))
130 changed_keys = [key for key in keys if key in info and key in new_info and info[key] != new_info[key]]
131 if changed_keys:
132 for key in changed_keys:
133 self._output.write('\t\t{0}: {1} to {2}\n'.format(key, info[key], new_info[key]))
134 else:
135 self._output.write('\t\tNone\n')
137 # List new keys
138 self._output.write("\tNew keys ({0} {1})\n".format(file, line))
139 new_keys = [key for key in keys if key in new_info and key not in info]
140 if new_keys:
141 for key in new_keys:
142 self._output.write('\t\t{0}: {1}\n'.format(key, new_info[key]))
143 else:
144 self._output.write('\t\tNone\n')
146 self._output.write('\n')
147 self._output.flush()
149 def write(self, msg):
150 if not self._is_active: return
152 self._output.write(msg + '\n')
153 self._output.flush()
155 def to_dict(self, info, is_fail):
156 return {
157 'output' : self._output.getvalue(),
158 'stdout' : self._stdout.getvalue(),
159 'stderr' : self._stderr.getvalue(),
160 'info' : info,
161 'err' : self._err,
162 'is_fail' : is_fail
163 }
165class DataSource(object):
166 bits = platform.architecture()[0]
167 cpu_count = multiprocessing.cpu_count()
168 is_windows = platform.system().lower() == 'windows'
169 arch_string_raw = platform.machine()
170 uname_string_raw = platform.uname()[5]
171 can_cpuid = True
173 @staticmethod
174 def has_proc_cpuinfo():
175 return os.path.exists('/proc/cpuinfo')
177 @staticmethod
178 def has_dmesg():
179 return len(_program_paths('dmesg')) > 0
181 @staticmethod
182 def has_var_run_dmesg_boot():
183 uname = platform.system().strip().strip('"').strip("'").strip().lower()
184 return 'linux' in uname and os.path.exists('/var/run/dmesg.boot')
186 @staticmethod
187 def has_cpufreq_info():
188 return len(_program_paths('cpufreq-info')) > 0
190 @staticmethod
191 def has_sestatus():
192 return len(_program_paths('sestatus')) > 0
194 @staticmethod
195 def has_sysctl():
196 return len(_program_paths('sysctl')) > 0
198 @staticmethod
199 def has_isainfo():
200 return len(_program_paths('isainfo')) > 0
202 @staticmethod
203 def has_kstat():
204 return len(_program_paths('kstat')) > 0
206 @staticmethod
207 def has_sysinfo():
208 uname = platform.system().strip().strip('"').strip("'").strip().lower()
209 is_beos = 'beos' in uname or 'haiku' in uname
210 return is_beos and len(_program_paths('sysinfo')) > 0
212 @staticmethod
213 def has_lscpu():
214 return len(_program_paths('lscpu')) > 0
216 @staticmethod
217 def has_ibm_pa_features():
218 return len(_program_paths('lsprop')) > 0
220 @staticmethod
221 def has_wmic():
222 returncode, output = _run_and_get_stdout(['wmic', 'os', 'get', 'Version'])
223 return returncode == 0 and len(output) > 0
225 @staticmethod
226 def cat_proc_cpuinfo():
227 return _run_and_get_stdout(['cat', '/proc/cpuinfo'])
229 @staticmethod
230 def cpufreq_info():
231 return _run_and_get_stdout(['cpufreq-info'])
233 @staticmethod
234 def sestatus_b():
235 return _run_and_get_stdout(['sestatus', '-b'])
237 @staticmethod
238 def dmesg_a():
239 return _run_and_get_stdout(['dmesg', '-a'])
241 @staticmethod
242 def cat_var_run_dmesg_boot():
243 return _run_and_get_stdout(['cat', '/var/run/dmesg.boot'])
245 @staticmethod
246 def sysctl_machdep_cpu_hw_cpufrequency():
247 return _run_and_get_stdout(['sysctl', 'machdep.cpu', 'hw.cpufrequency'])
249 @staticmethod
250 def isainfo_vb():
251 return _run_and_get_stdout(['isainfo', '-vb'])
253 @staticmethod
254 def kstat_m_cpu_info():
255 return _run_and_get_stdout(['kstat', '-m', 'cpu_info'])
257 @staticmethod
258 def sysinfo_cpu():
259 return _run_and_get_stdout(['sysinfo', '-cpu'])
261 @staticmethod
262 def lscpu():
263 return _run_and_get_stdout(['lscpu'])
265 @staticmethod
266 def ibm_pa_features():
267 import glob
269 ibm_features = glob.glob('/proc/device-tree/cpus/*/ibm,pa-features')
270 if ibm_features:
271 return _run_and_get_stdout(['lsprop', ibm_features[0]])
273 @staticmethod
274 def wmic_cpu():
275 return _run_and_get_stdout(['wmic', 'cpu', 'get', 'Name,CurrentClockSpeed,L2CacheSize,L3CacheSize,Description,Caption,Manufacturer', '/format:list'])
277 @staticmethod
278 def winreg_processor_brand():
279 processor_brand = _read_windows_registry_key(r"Hardware\Description\System\CentralProcessor\0", "ProcessorNameString")
280 return processor_brand.strip()
282 @staticmethod
283 def winreg_vendor_id_raw():
284 vendor_id_raw = _read_windows_registry_key(r"Hardware\Description\System\CentralProcessor\0", "VendorIdentifier")
285 return vendor_id_raw
287 @staticmethod
288 def winreg_arch_string_raw():
289 arch_string_raw = _read_windows_registry_key(r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", "PROCESSOR_ARCHITECTURE")
290 return arch_string_raw
292 @staticmethod
293 def winreg_hz_actual():
294 hz_actual = _read_windows_registry_key(r"Hardware\Description\System\CentralProcessor\0", "~Mhz")
295 hz_actual = _to_decimal_string(hz_actual)
296 return hz_actual
298 @staticmethod
299 def winreg_feature_bits():
300 feature_bits = _read_windows_registry_key(r"Hardware\Description\System\CentralProcessor\0", "FeatureSet")
301 return feature_bits
304def _program_paths(program_name):
305 paths = []
306 exts = filter(None, os.environ.get('PATHEXT', '').split(os.pathsep))
307 for p in os.environ['PATH'].split(os.pathsep):
308 p = os.path.join(p, program_name)
309 if os.access(p, os.X_OK):
310 paths.append(p)
311 for e in exts:
312 pext = p + e
313 if os.access(pext, os.X_OK):
314 paths.append(pext)
315 return paths
317def _run_and_get_stdout(command, pipe_command=None):
318 from subprocess import Popen, PIPE
320 g_trace.command_header('Running command "' + ' '.join(command) + '" ...')
322 # Run the command normally
323 if not pipe_command:
324 p1 = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE)
325 # Run the command and pipe it into another command
326 else:
327 p2 = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE)
328 p1 = Popen(pipe_command, stdin=p2.stdout, stdout=PIPE, stderr=PIPE)
329 p2.stdout.close()
331 # Get the stdout and stderr
332 stdout_output, stderr_output = p1.communicate()
333 stdout_output = stdout_output.decode(encoding='UTF-8')
334 stderr_output = stderr_output.decode(encoding='UTF-8')
336 # Send the result to the logger
337 g_trace.command_output('return code:', str(p1.returncode))
338 g_trace.command_output('stdout:', stdout_output)
340 # Return the return code and stdout
341 return p1.returncode, stdout_output
343def _read_windows_registry_key(key_name, field_name):
344 g_trace.command_header('Reading Registry key "{0}" field "{1}" ...'.format(key_name, field_name))
346 try:
347 import _winreg as winreg
348 except ImportError as err:
349 try:
350 import winreg
351 except ImportError as err:
352 pass
354 key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_name)
355 value = winreg.QueryValueEx(key, field_name)[0]
356 winreg.CloseKey(key)
357 g_trace.command_output('value:', str(value))
358 return value
360# Make sure we are running on a supported system
361def _check_arch():
362 arch, bits = _parse_arch(DataSource.arch_string_raw)
363 if not arch in ['X86_32', 'X86_64', 'ARM_7', 'ARM_8',
364 'PPC_64', 'S390X', 'MIPS_32', 'MIPS_64',
365 "RISCV_32", "RISCV_64"]:
366 raise Exception("py-cpuinfo currently only works on X86 "
367 "and some ARM/PPC/S390X/MIPS/RISCV CPUs.")
369def _obj_to_b64(thing):
370 import pickle
371 import base64
373 a = thing
374 b = pickle.dumps(a)
375 c = base64.b64encode(b)
376 d = c.decode('utf8')
377 return d
379def _b64_to_obj(thing):
380 import pickle
381 import base64
383 try:
384 a = base64.b64decode(thing)
385 b = pickle.loads(a)
386 return b
387 except Exception:
388 return {}
390def _utf_to_str(input):
391 if isinstance(input, list):
392 return [_utf_to_str(element) for element in input]
393 elif isinstance(input, dict):
394 return {_utf_to_str(key): _utf_to_str(value)
395 for key, value in input.items()}
396 else:
397 return input
399def _copy_new_fields(info, new_info):
400 keys = [
401 'vendor_id_raw', 'hardware_raw', 'brand_raw', 'hz_advertised_friendly', 'hz_actual_friendly',
402 'hz_advertised', 'hz_actual', 'arch', 'bits', 'count',
403 'arch_string_raw', 'uname_string_raw',
404 'l2_cache_size', 'l2_cache_line_size', 'l2_cache_associativity',
405 'stepping', 'model', 'family',
406 'processor_type', 'flags',
407 'l3_cache_size', 'l1_data_cache_size', 'l1_instruction_cache_size'
408 ]
410 g_trace.keys(keys, info, new_info)
412 # Update the keys with new values
413 for key in keys:
414 if new_info.get(key, None) and not info.get(key, None):
415 info[key] = new_info[key]
416 elif key == 'flags' and new_info.get('flags'):
417 for f in new_info['flags']:
418 if f not in info['flags']: info['flags'].append(f)
419 info['flags'].sort()
421def _get_field_actual(cant_be_number, raw_string, field_names):
422 for line in raw_string.splitlines():
423 for field_name in field_names:
424 field_name = field_name.lower()
425 if ':' in line:
426 left, right = line.split(':', 1)
427 left = left.strip().lower()
428 right = right.strip()
429 if left == field_name and len(right) > 0:
430 if cant_be_number:
431 if not right.isdigit():
432 return right
433 else:
434 return right
436 return None
438def _get_field(cant_be_number, raw_string, convert_to, default_value, *field_names):
439 retval = _get_field_actual(cant_be_number, raw_string, field_names)
441 # Convert the return value
442 if retval and convert_to:
443 try:
444 retval = convert_to(retval)
445 except Exception:
446 retval = default_value
448 # Return the default if there is no return value
449 if retval is None:
450 retval = default_value
452 return retval
454def _to_decimal_string(ticks):
455 try:
456 # Convert to string
457 ticks = '{0}'.format(ticks)
458 # Sometimes ',' is used as a decimal separator
459 ticks = ticks.replace(',', '.')
461 # Strip off non numbers and decimal places
462 ticks = "".join(n for n in ticks if n.isdigit() or n=='.').strip()
463 if ticks == '':
464 ticks = '0'
466 # Add decimal if missing
467 if '.' not in ticks:
468 ticks = '{0}.0'.format(ticks)
470 # Remove trailing zeros
471 ticks = ticks.rstrip('0')
473 # Add one trailing zero for empty right side
474 if ticks.endswith('.'):
475 ticks = '{0}0'.format(ticks)
477 # Make sure the number can be converted to a float
478 ticks = float(ticks)
479 ticks = '{0}'.format(ticks)
480 return ticks
481 except Exception:
482 return '0.0'
484def _hz_short_to_full(ticks, scale):
485 try:
486 # Make sure the number can be converted to a float
487 ticks = float(ticks)
488 ticks = '{0}'.format(ticks)
490 # Scale the numbers
491 hz = ticks.lstrip('0')
492 old_index = hz.index('.')
493 hz = hz.replace('.', '')
494 hz = hz.ljust(scale + old_index+1, '0')
495 new_index = old_index + scale
496 hz = '{0}.{1}'.format(hz[:new_index], hz[new_index:])
497 left, right = hz.split('.')
498 left, right = int(left), int(right)
499 return (left, right)
500 except Exception:
501 return (0, 0)
503def _hz_friendly_to_full(hz_string):
504 try:
505 hz_string = hz_string.strip().lower()
506 hz, scale = (None, None)
508 if hz_string.endswith('ghz'):
509 scale = 9
510 elif hz_string.endswith('mhz'):
511 scale = 6
512 elif hz_string.endswith('hz'):
513 scale = 0
515 hz = "".join(n for n in hz_string if n.isdigit() or n=='.').strip()
516 if not '.' in hz:
517 hz += '.0'
519 hz, scale = _hz_short_to_full(hz, scale)
521 return (hz, scale)
522 except Exception:
523 return (0, 0)
525def _hz_short_to_friendly(ticks, scale):
526 try:
527 # Get the raw Hz as a string
528 left, right = _hz_short_to_full(ticks, scale)
529 result = '{0}.{1}'.format(left, right)
531 # Get the location of the dot, and remove said dot
532 dot_index = result.index('.')
533 result = result.replace('.', '')
535 # Get the Hz symbol and scale
536 symbol = "Hz"
537 scale = 0
538 if dot_index > 9:
539 symbol = "GHz"
540 scale = 9
541 elif dot_index > 6:
542 symbol = "MHz"
543 scale = 6
544 elif dot_index > 3:
545 symbol = "KHz"
546 scale = 3
548 # Get the Hz with the dot at the new scaled point
549 result = '{0}.{1}'.format(result[:-scale-1], result[-scale-1:])
551 # Format the ticks to have 4 numbers after the decimal
552 # and remove any superfluous zeroes.
553 result = '{0:.4f} {1}'.format(float(result), symbol)
554 result = result.rstrip('0')
555 return result
556 except Exception:
557 return '0.0000 Hz'
559def _to_friendly_bytes(input):
560 import re
562 if not input:
563 return input
564 input = "{0}".format(input)
566 formats = {
567 r"^[0-9]+B$" : 'B',
568 r"^[0-9]+K$" : 'KB',
569 r"^[0-9]+M$" : 'MB',
570 r"^[0-9]+G$" : 'GB'
571 }
573 for pattern, friendly_size in formats.items():
574 if re.match(pattern, input):
575 return "{0} {1}".format(input[ : -1].strip(), friendly_size)
577 return input
579def _friendly_bytes_to_int(friendly_bytes):
580 input = friendly_bytes.lower()
582 formats = [
583 {'gib' : 1024 * 1024 * 1024},
584 {'mib' : 1024 * 1024},
585 {'kib' : 1024},
587 {'gb' : 1024 * 1024 * 1024},
588 {'mb' : 1024 * 1024},
589 {'kb' : 1024},
591 {'g' : 1024 * 1024 * 1024},
592 {'m' : 1024 * 1024},
593 {'k' : 1024},
594 {'b' : 1},
595 ]
597 try:
598 for entry in formats:
599 pattern = list(entry.keys())[0]
600 multiplier = list(entry.values())[0]
601 if input.endswith(pattern):
602 return int(input.split(pattern)[0].strip()) * multiplier
604 except Exception as err:
605 pass
607 return friendly_bytes
609def _parse_cpu_brand_string(cpu_string):
610 # Just return 0 if the processor brand does not have the Hz
611 if not 'hz' in cpu_string.lower():
612 return ('0.0', 0)
614 hz = cpu_string.lower()
615 scale = 0
617 if hz.endswith('mhz'):
618 scale = 6
619 elif hz.endswith('ghz'):
620 scale = 9
621 if '@' in hz:
622 hz = hz.split('@')[1]
623 else:
624 hz = hz.rsplit(None, 1)[1]
626 hz = hz.rstrip('mhz').rstrip('ghz').strip()
627 hz = _to_decimal_string(hz)
629 return (hz, scale)
631def _parse_cpu_brand_string_dx(cpu_string):
632 import re
634 # Find all the strings inside brackets ()
635 starts = [m.start() for m in re.finditer(r"\(", cpu_string)]
636 ends = [m.start() for m in re.finditer(r"\)", cpu_string)]
637 insides = {k: v for k, v in zip(starts, ends)}
638 insides = [cpu_string[start+1 : end] for start, end in insides.items()]
640 # Find all the fields
641 vendor_id, stepping, model, family = (None, None, None, None)
642 for inside in insides:
643 for pair in inside.split(','):
644 pair = [n.strip() for n in pair.split(':')]
645 if len(pair) > 1:
646 name, value = pair[0], pair[1]
647 if name == 'origin':
648 vendor_id = value.strip('"')
649 elif name == 'stepping':
650 stepping = int(value.lstrip('0x'), 16)
651 elif name == 'model':
652 model = int(value.lstrip('0x'), 16)
653 elif name in ['fam', 'family']:
654 family = int(value.lstrip('0x'), 16)
656 # Find the Processor Brand
657 # Strip off extra strings in brackets at end
658 brand = cpu_string.strip()
659 is_working = True
660 while is_working:
661 is_working = False
662 for inside in insides:
663 full = "({0})".format(inside)
664 if brand.endswith(full):
665 brand = brand[ :-len(full)].strip()
666 is_working = True
668 # Find the Hz in the brand string
669 hz_brand, scale = _parse_cpu_brand_string(brand)
671 # Find Hz inside brackets () after the brand string
672 if hz_brand == '0.0':
673 for inside in insides:
674 hz = inside
675 for entry in ['GHz', 'MHz', 'Hz']:
676 if entry in hz:
677 hz = "CPU @ " + hz[ : hz.find(entry) + len(entry)]
678 hz_brand, scale = _parse_cpu_brand_string(hz)
679 break
681 return (hz_brand, scale, brand, vendor_id, stepping, model, family)
683def _parse_dmesg_output(output):
684 try:
685 # Get all the dmesg lines that might contain a CPU string
686 lines = output.split(' CPU0:')[1:] + \
687 output.split(' CPU1:')[1:] + \
688 output.split(' CPU:')[1:] + \
689 output.split('\nCPU0:')[1:] + \
690 output.split('\nCPU1:')[1:] + \
691 output.split('\nCPU:')[1:]
692 lines = [l.split('\n')[0].strip() for l in lines]
694 # Convert the lines to CPU strings
695 cpu_strings = [_parse_cpu_brand_string_dx(l) for l in lines]
697 # Find the CPU string that has the most fields
698 best_string = None
699 highest_count = 0
700 for cpu_string in cpu_strings:
701 count = sum([n is not None for n in cpu_string])
702 if count > highest_count:
703 highest_count = count
704 best_string = cpu_string
706 # If no CPU string was found, return {}
707 if not best_string:
708 return {}
710 hz_actual, scale, processor_brand, vendor_id, stepping, model, family = best_string
712 # Origin
713 if ' Origin=' in output:
714 fields = output[output.find(' Origin=') : ].split('\n')[0]
715 fields = fields.strip().split()
716 fields = [n.strip().split('=') for n in fields]
717 fields = [{n[0].strip().lower() : n[1].strip()} for n in fields]
719 for field in fields:
720 name = list(field.keys())[0]
721 value = list(field.values())[0]
723 if name == 'origin':
724 vendor_id = value.strip('"')
725 elif name == 'stepping':
726 stepping = int(value.lstrip('0x'), 16)
727 elif name == 'model':
728 model = int(value.lstrip('0x'), 16)
729 elif name in ['fam', 'family']:
730 family = int(value.lstrip('0x'), 16)
732 # Features
733 flag_lines = []
734 for category in [' Features=', ' Features2=', ' AMD Features=', ' AMD Features2=']:
735 if category in output:
736 flag_lines.append(output.split(category)[1].split('\n')[0])
738 flags = []
739 for line in flag_lines:
740 line = line.split('<')[1].split('>')[0].lower()
741 for flag in line.split(','):
742 flags.append(flag)
743 flags.sort()
745 # Convert from GHz/MHz string to Hz
746 hz_advertised, scale = _parse_cpu_brand_string(processor_brand)
748 # If advertised hz not found, use the actual hz
749 if hz_advertised == '0.0':
750 scale = 6
751 hz_advertised = _to_decimal_string(hz_actual)
753 info = {
754 'vendor_id_raw' : vendor_id,
755 'brand_raw' : processor_brand,
757 'stepping' : stepping,
758 'model' : model,
759 'family' : family,
760 'flags' : flags
761 }
763 if hz_advertised and hz_advertised != '0.0':
764 info['hz_advertised_friendly'] = _hz_short_to_friendly(hz_advertised, scale)
765 info['hz_actual_friendly'] = _hz_short_to_friendly(hz_actual, scale)
767 if hz_advertised and hz_advertised != '0.0':
768 info['hz_advertised'] = _hz_short_to_full(hz_advertised, scale)
769 info['hz_actual'] = _hz_short_to_full(hz_actual, scale)
771 return {k: v for k, v in info.items() if v}
772 except Exception as err:
773 g_trace.fail(err)
774 #raise
776 return {}
778def _parse_arch(arch_string_raw):
779 import re
781 arch, bits = None, None
782 arch_string_raw = arch_string_raw.lower()
784 # X86
785 if re.match(r'^i\d86$|^x86$|^x86_32$|^i86pc$|^ia32$|^ia-32$|^bepc$', arch_string_raw):
786 arch = 'X86_32'
787 bits = 32
788 elif re.match(r'^x64$|^x86_64$|^x86_64t$|^i686-64$|^amd64$|^ia64$|^ia-64$', arch_string_raw):
789 arch = 'X86_64'
790 bits = 64
791 # ARM
792 elif re.match(r'^armv8-a|aarch64|arm64$', arch_string_raw):
793 arch = 'ARM_8'
794 bits = 64
795 elif re.match(r'^armv7$|^armv7[a-z]$|^armv7-[a-z]$|^armv6[a-z]$', arch_string_raw):
796 arch = 'ARM_7'
797 bits = 32
798 elif re.match(r'^armv8$|^armv8[a-z]$|^armv8-[a-z]$', arch_string_raw):
799 arch = 'ARM_8'
800 bits = 32
801 # PPC
802 elif re.match(r'^ppc32$|^prep$|^pmac$|^powermac$', arch_string_raw):
803 arch = 'PPC_32'
804 bits = 32
805 elif re.match(r'^powerpc$|^ppc64$|^ppc64le$', arch_string_raw):
806 arch = 'PPC_64'
807 bits = 64
808 # SPARC
809 elif re.match(r'^sparc32$|^sparc$', arch_string_raw):
810 arch = 'SPARC_32'
811 bits = 32
812 elif re.match(r'^sparc64$|^sun4u$|^sun4v$', arch_string_raw):
813 arch = 'SPARC_64'
814 bits = 64
815 # S390X
816 elif re.match(r'^s390x$', arch_string_raw):
817 arch = 'S390X'
818 bits = 64
819 elif arch_string_raw == 'mips':
820 arch = 'MIPS_32'
821 bits = 32
822 elif arch_string_raw == 'mips64':
823 arch = 'MIPS_64'
824 bits = 64
825 # RISCV
826 elif re.match(r'^riscv$|^riscv32$|^riscv32be$', arch_string_raw):
827 arch = 'RISCV_32'
828 bits = 32
829 elif re.match(r'^riscv64$|^riscv64be$', arch_string_raw):
830 arch = 'RISCV_64'
831 bits = 64
833 return (arch, bits)
835def _is_bit_set(reg, bit):
836 mask = 1 << bit
837 is_set = reg & mask > 0
838 return is_set
841def _is_selinux_enforcing(trace):
842 # Just return if the SE Linux Status Tool is not installed
843 if not DataSource.has_sestatus():
844 trace.fail('Failed to find sestatus.')
845 return False
847 # Run the sestatus, and just return if it failed to run
848 returncode, output = DataSource.sestatus_b()
849 if returncode != 0:
850 trace.fail('Failed to run sestatus. Skipping ...')
851 return False
853 # Figure out if explicitly in enforcing mode
854 for line in output.splitlines():
855 line = line.strip().lower()
856 if line.startswith("current mode:"):
857 if line.endswith("enforcing"):
858 return True
859 else:
860 return False
862 # Figure out if we can execute heap and execute memory
863 can_selinux_exec_heap = False
864 can_selinux_exec_memory = False
865 for line in output.splitlines():
866 line = line.strip().lower()
867 if line.startswith("allow_execheap") and line.endswith("on"):
868 can_selinux_exec_heap = True
869 elif line.startswith("allow_execmem") and line.endswith("on"):
870 can_selinux_exec_memory = True
872 trace.command_output('can_selinux_exec_heap:', can_selinux_exec_heap)
873 trace.command_output('can_selinux_exec_memory:', can_selinux_exec_memory)
875 return (not can_selinux_exec_heap or not can_selinux_exec_memory)
877def _filter_dict_keys_with_empty_values(info, acceptable_values = {}):
878 filtered_info = {}
879 for key in info:
880 value = info[key]
882 # Keep if value is acceptable
883 if key in acceptable_values:
884 if acceptable_values[key] == value:
885 filtered_info[key] = value
886 continue
888 # Filter out None, 0, "", (), {}, []
889 if not value:
890 continue
892 # Filter out (0, 0)
893 if value == (0, 0):
894 continue
896 # Filter out -1
897 if value == -1:
898 continue
900 # Filter out strings that start with "0.0"
901 if type(value) == str and value.startswith('0.0'):
902 continue
904 filtered_info[key] = value
906 return filtered_info
908class ASM(object):
909 def __init__(self, restype=None, argtypes=(), machine_code=[]):
910 self.restype = restype
911 self.argtypes = argtypes
912 self.machine_code = machine_code
913 self.prochandle = None
914 self.mm = None
915 self.func = None
916 self.address = None
917 self.size = 0
919 def compile(self):
920 machine_code = bytes.join(b'', self.machine_code)
921 self.size = ctypes.c_size_t(len(machine_code))
923 if DataSource.is_windows:
924 # Allocate a memory segment the size of the machine code, and make it executable
925 size = len(machine_code)
926 # Alloc at least 1 page to ensure we own all pages that we want to change protection on
927 if size < 0x1000: size = 0x1000
928 MEM_COMMIT = ctypes.c_ulong(0x1000)
929 PAGE_READWRITE = ctypes.c_ulong(0x4)
930 pfnVirtualAlloc = ctypes.windll.kernel32.VirtualAlloc
931 pfnVirtualAlloc.restype = ctypes.c_void_p
932 self.address = pfnVirtualAlloc(None, ctypes.c_size_t(size), MEM_COMMIT, PAGE_READWRITE)
933 if not self.address:
934 raise Exception("Failed to VirtualAlloc")
936 # Copy the machine code into the memory segment
937 memmove = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t)(ctypes._memmove_addr)
938 if memmove(self.address, machine_code, size) < 0:
939 raise Exception("Failed to memmove")
941 # Enable execute permissions
942 PAGE_EXECUTE = ctypes.c_ulong(0x10)
943 old_protect = ctypes.c_ulong(0)
944 pfnVirtualProtect = ctypes.windll.kernel32.VirtualProtect
945 res = pfnVirtualProtect(ctypes.c_void_p(self.address), ctypes.c_size_t(size), PAGE_EXECUTE, ctypes.byref(old_protect))
946 if not res:
947 raise Exception("Failed VirtualProtect")
949 # Flush Instruction Cache
950 # First, get process Handle
951 if not self.prochandle:
952 pfnGetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess
953 pfnGetCurrentProcess.restype = ctypes.c_void_p
954 self.prochandle = ctypes.c_void_p(pfnGetCurrentProcess())
955 # Actually flush cache
956 res = ctypes.windll.kernel32.FlushInstructionCache(self.prochandle, ctypes.c_void_p(self.address), ctypes.c_size_t(size))
957 if not res:
958 raise Exception("Failed FlushInstructionCache")
959 else:
960 from mmap import mmap, MAP_PRIVATE, MAP_ANONYMOUS, PROT_WRITE, PROT_READ, PROT_EXEC
962 # Allocate a private and executable memory segment the size of the machine code
963 machine_code = bytes.join(b'', self.machine_code)
964 self.size = len(machine_code)
965 self.mm = mmap(-1, self.size, flags=MAP_PRIVATE | MAP_ANONYMOUS, prot=PROT_WRITE | PROT_READ | PROT_EXEC)
967 # Copy the machine code into the memory segment
968 self.mm.write(machine_code)
969 self.address = ctypes.addressof(ctypes.c_int.from_buffer(self.mm))
971 # Cast the memory segment into a function
972 functype = ctypes.CFUNCTYPE(self.restype, *self.argtypes)
973 self.func = functype(self.address)
975 def run(self):
976 # Call the machine code like a function
977 retval = self.func()
979 return retval
981 def free(self):
982 # Free the function memory segment
983 if DataSource.is_windows:
984 MEM_RELEASE = ctypes.c_ulong(0x8000)
985 ctypes.windll.kernel32.VirtualFree(ctypes.c_void_p(self.address), ctypes.c_size_t(0), MEM_RELEASE)
986 else:
987 self.mm.close()
989 self.prochandle = None
990 self.mm = None
991 self.func = None
992 self.address = None
993 self.size = 0
996class CPUID(object):
997 def __init__(self, trace=None):
998 if trace is None:
999 trace = Trace(False, False)
1001 # Figure out if SE Linux is on and in enforcing mode
1002 self.is_selinux_enforcing = _is_selinux_enforcing(trace)
1004 def _asm_func(self, restype=None, argtypes=(), machine_code=[]):
1005 asm = ASM(restype, argtypes, machine_code)
1006 asm.compile()
1007 return asm
1009 def _run_asm(self, *machine_code):
1010 asm = ASM(ctypes.c_uint32, (), machine_code)
1011 asm.compile()
1012 retval = asm.run()
1013 asm.free()
1014 return retval
1016 # http://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID
1017 def get_vendor_id(self):
1018 # EBX
1019 ebx = self._run_asm(
1020 b"\x31\xC0", # xor eax,eax
1021 b"\x0F\xA2" # cpuid
1022 b"\x89\xD8" # mov ax,bx
1023 b"\xC3" # ret
1024 )
1026 # ECX
1027 ecx = self._run_asm(
1028 b"\x31\xC0", # xor eax,eax
1029 b"\x0f\xa2" # cpuid
1030 b"\x89\xC8" # mov ax,cx
1031 b"\xC3" # ret
1032 )
1034 # EDX
1035 edx = self._run_asm(
1036 b"\x31\xC0", # xor eax,eax
1037 b"\x0f\xa2" # cpuid
1038 b"\x89\xD0" # mov ax,dx
1039 b"\xC3" # ret
1040 )
1042 # Each 4bits is a ascii letter in the name
1043 vendor_id = []
1044 for reg in [ebx, edx, ecx]:
1045 for n in [0, 8, 16, 24]:
1046 vendor_id.append(chr((reg >> n) & 0xFF))
1047 vendor_id = ''.join(vendor_id)
1049 return vendor_id
1051 # http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits
1052 def get_info(self):
1053 # EAX
1054 eax = self._run_asm(
1055 b"\xB8\x01\x00\x00\x00", # mov eax,0x1"
1056 b"\x0f\xa2" # cpuid
1057 b"\xC3" # ret
1058 )
1060 # Get the CPU info
1061 stepping_id = (eax >> 0) & 0xF # 4 bits
1062 model = (eax >> 4) & 0xF # 4 bits
1063 family_id = (eax >> 8) & 0xF # 4 bits
1064 processor_type = (eax >> 12) & 0x3 # 2 bits
1065 extended_model_id = (eax >> 16) & 0xF # 4 bits
1066 extended_family_id = (eax >> 20) & 0xFF # 8 bits
1067 family = 0
1069 if family_id in [15]:
1070 family = extended_family_id + family_id
1071 else:
1072 family = family_id
1074 if family_id in [6, 15]:
1075 model = (extended_model_id << 4) + model
1077 return {
1078 'stepping' : stepping_id,
1079 'model' : model,
1080 'family' : family,
1081 'processor_type' : processor_type
1082 }
1084 # http://en.wikipedia.org/wiki/CPUID#EAX.3D80000000h:_Get_Highest_Extended_Function_Supported
1085 def get_max_extension_support(self):
1086 # Check for extension support
1087 max_extension_support = self._run_asm(
1088 b"\xB8\x00\x00\x00\x80" # mov ax,0x80000000
1089 b"\x0f\xa2" # cpuid
1090 b"\xC3" # ret
1091 )
1093 return max_extension_support
1095 # http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits
1096 def get_flags(self, max_extension_support):
1097 # EDX
1098 edx = self._run_asm(
1099 b"\xB8\x01\x00\x00\x00", # mov eax,0x1"
1100 b"\x0f\xa2" # cpuid
1101 b"\x89\xD0" # mov ax,dx
1102 b"\xC3" # ret
1103 )
1105 # ECX
1106 ecx = self._run_asm(
1107 b"\xB8\x01\x00\x00\x00", # mov eax,0x1"
1108 b"\x0f\xa2" # cpuid
1109 b"\x89\xC8" # mov ax,cx
1110 b"\xC3" # ret
1111 )
1113 # Get the CPU flags
1114 flags = {
1115 'fpu' : _is_bit_set(edx, 0),
1116 'vme' : _is_bit_set(edx, 1),
1117 'de' : _is_bit_set(edx, 2),
1118 'pse' : _is_bit_set(edx, 3),
1119 'tsc' : _is_bit_set(edx, 4),
1120 'msr' : _is_bit_set(edx, 5),
1121 'pae' : _is_bit_set(edx, 6),
1122 'mce' : _is_bit_set(edx, 7),
1123 'cx8' : _is_bit_set(edx, 8),
1124 'apic' : _is_bit_set(edx, 9),
1125 #'reserved1' : _is_bit_set(edx, 10),
1126 'sep' : _is_bit_set(edx, 11),
1127 'mtrr' : _is_bit_set(edx, 12),
1128 'pge' : _is_bit_set(edx, 13),
1129 'mca' : _is_bit_set(edx, 14),
1130 'cmov' : _is_bit_set(edx, 15),
1131 'pat' : _is_bit_set(edx, 16),
1132 'pse36' : _is_bit_set(edx, 17),
1133 'pn' : _is_bit_set(edx, 18),
1134 'clflush' : _is_bit_set(edx, 19),
1135 #'reserved2' : _is_bit_set(edx, 20),
1136 'dts' : _is_bit_set(edx, 21),
1137 'acpi' : _is_bit_set(edx, 22),
1138 'mmx' : _is_bit_set(edx, 23),
1139 'fxsr' : _is_bit_set(edx, 24),
1140 'sse' : _is_bit_set(edx, 25),
1141 'sse2' : _is_bit_set(edx, 26),
1142 'ss' : _is_bit_set(edx, 27),
1143 'ht' : _is_bit_set(edx, 28),
1144 'tm' : _is_bit_set(edx, 29),
1145 'ia64' : _is_bit_set(edx, 30),
1146 'pbe' : _is_bit_set(edx, 31),
1148 'pni' : _is_bit_set(ecx, 0),
1149 'pclmulqdq' : _is_bit_set(ecx, 1),
1150 'dtes64' : _is_bit_set(ecx, 2),
1151 'monitor' : _is_bit_set(ecx, 3),
1152 'ds_cpl' : _is_bit_set(ecx, 4),
1153 'vmx' : _is_bit_set(ecx, 5),
1154 'smx' : _is_bit_set(ecx, 6),
1155 'est' : _is_bit_set(ecx, 7),
1156 'tm2' : _is_bit_set(ecx, 8),
1157 'ssse3' : _is_bit_set(ecx, 9),
1158 'cid' : _is_bit_set(ecx, 10),
1159 #'reserved3' : _is_bit_set(ecx, 11),
1160 'fma' : _is_bit_set(ecx, 12),
1161 'cx16' : _is_bit_set(ecx, 13),
1162 'xtpr' : _is_bit_set(ecx, 14),
1163 'pdcm' : _is_bit_set(ecx, 15),
1164 #'reserved4' : _is_bit_set(ecx, 16),
1165 'pcid' : _is_bit_set(ecx, 17),
1166 'dca' : _is_bit_set(ecx, 18),
1167 'sse4_1' : _is_bit_set(ecx, 19),
1168 'sse4_2' : _is_bit_set(ecx, 20),
1169 'x2apic' : _is_bit_set(ecx, 21),
1170 'movbe' : _is_bit_set(ecx, 22),
1171 'popcnt' : _is_bit_set(ecx, 23),
1172 'tscdeadline' : _is_bit_set(ecx, 24),
1173 'aes' : _is_bit_set(ecx, 25),
1174 'xsave' : _is_bit_set(ecx, 26),
1175 'osxsave' : _is_bit_set(ecx, 27),
1176 'avx' : _is_bit_set(ecx, 28),
1177 'f16c' : _is_bit_set(ecx, 29),
1178 'rdrnd' : _is_bit_set(ecx, 30),
1179 'hypervisor' : _is_bit_set(ecx, 31)
1180 }
1182 # Get a list of only the flags that are true
1183 flags = [k for k, v in flags.items() if v]
1185 # http://en.wikipedia.org/wiki/CPUID#EAX.3D7.2C_ECX.3D0:_Extended_Features
1186 if max_extension_support >= 7:
1187 # EBX
1188 ebx = self._run_asm(
1189 b"\x31\xC9", # xor ecx,ecx
1190 b"\xB8\x07\x00\x00\x00" # mov eax,7
1191 b"\x0f\xa2" # cpuid
1192 b"\x89\xD8" # mov ax,bx
1193 b"\xC3" # ret
1194 )
1196 # ECX
1197 ecx = self._run_asm(
1198 b"\x31\xC9", # xor ecx,ecx
1199 b"\xB8\x07\x00\x00\x00" # mov eax,7
1200 b"\x0f\xa2" # cpuid
1201 b"\x89\xC8" # mov ax,cx
1202 b"\xC3" # ret
1203 )
1205 # Get the extended CPU flags
1206 extended_flags = {
1207 #'fsgsbase' : _is_bit_set(ebx, 0),
1208 #'IA32_TSC_ADJUST' : _is_bit_set(ebx, 1),
1209 'sgx' : _is_bit_set(ebx, 2),
1210 'bmi1' : _is_bit_set(ebx, 3),
1211 'hle' : _is_bit_set(ebx, 4),
1212 'avx2' : _is_bit_set(ebx, 5),
1213 #'reserved' : _is_bit_set(ebx, 6),
1214 'smep' : _is_bit_set(ebx, 7),
1215 'bmi2' : _is_bit_set(ebx, 8),
1216 'erms' : _is_bit_set(ebx, 9),
1217 'invpcid' : _is_bit_set(ebx, 10),
1218 'rtm' : _is_bit_set(ebx, 11),
1219 'pqm' : _is_bit_set(ebx, 12),
1220 #'FPU CS and FPU DS deprecated' : _is_bit_set(ebx, 13),
1221 'mpx' : _is_bit_set(ebx, 14),
1222 'pqe' : _is_bit_set(ebx, 15),
1223 'avx512f' : _is_bit_set(ebx, 16),
1224 'avx512dq' : _is_bit_set(ebx, 17),
1225 'rdseed' : _is_bit_set(ebx, 18),
1226 'adx' : _is_bit_set(ebx, 19),
1227 'smap' : _is_bit_set(ebx, 20),
1228 'avx512ifma' : _is_bit_set(ebx, 21),
1229 'pcommit' : _is_bit_set(ebx, 22),
1230 'clflushopt' : _is_bit_set(ebx, 23),
1231 'clwb' : _is_bit_set(ebx, 24),
1232 'intel_pt' : _is_bit_set(ebx, 25),
1233 'avx512pf' : _is_bit_set(ebx, 26),
1234 'avx512er' : _is_bit_set(ebx, 27),
1235 'avx512cd' : _is_bit_set(ebx, 28),
1236 'sha' : _is_bit_set(ebx, 29),
1237 'avx512bw' : _is_bit_set(ebx, 30),
1238 'avx512vl' : _is_bit_set(ebx, 31),
1240 'prefetchwt1' : _is_bit_set(ecx, 0),
1241 'avx512vbmi' : _is_bit_set(ecx, 1),
1242 'umip' : _is_bit_set(ecx, 2),
1243 'pku' : _is_bit_set(ecx, 3),
1244 'ospke' : _is_bit_set(ecx, 4),
1245 #'reserved' : _is_bit_set(ecx, 5),
1246 'avx512vbmi2' : _is_bit_set(ecx, 6),
1247 #'reserved' : _is_bit_set(ecx, 7),
1248 'gfni' : _is_bit_set(ecx, 8),
1249 'vaes' : _is_bit_set(ecx, 9),
1250 'vpclmulqdq' : _is_bit_set(ecx, 10),
1251 'avx512vnni' : _is_bit_set(ecx, 11),
1252 'avx512bitalg' : _is_bit_set(ecx, 12),
1253 #'reserved' : _is_bit_set(ecx, 13),
1254 'avx512vpopcntdq' : _is_bit_set(ecx, 14),
1255 #'reserved' : _is_bit_set(ecx, 15),
1256 #'reserved' : _is_bit_set(ecx, 16),
1257 #'mpx0' : _is_bit_set(ecx, 17),
1258 #'mpx1' : _is_bit_set(ecx, 18),
1259 #'mpx2' : _is_bit_set(ecx, 19),
1260 #'mpx3' : _is_bit_set(ecx, 20),
1261 #'mpx4' : _is_bit_set(ecx, 21),
1262 'rdpid' : _is_bit_set(ecx, 22),
1263 #'reserved' : _is_bit_set(ecx, 23),
1264 #'reserved' : _is_bit_set(ecx, 24),
1265 #'reserved' : _is_bit_set(ecx, 25),
1266 #'reserved' : _is_bit_set(ecx, 26),
1267 #'reserved' : _is_bit_set(ecx, 27),
1268 #'reserved' : _is_bit_set(ecx, 28),
1269 #'reserved' : _is_bit_set(ecx, 29),
1270 'sgx_lc' : _is_bit_set(ecx, 30),
1271 #'reserved' : _is_bit_set(ecx, 31)
1272 }
1274 # Get a list of only the flags that are true
1275 extended_flags = [k for k, v in extended_flags.items() if v]
1276 flags += extended_flags
1278 # http://en.wikipedia.org/wiki/CPUID#EAX.3D80000001h:_Extended_Processor_Info_and_Feature_Bits
1279 if max_extension_support >= 0x80000001:
1280 # EBX
1281 ebx = self._run_asm(
1282 b"\xB8\x01\x00\x00\x80" # mov ax,0x80000001
1283 b"\x0f\xa2" # cpuid
1284 b"\x89\xD8" # mov ax,bx
1285 b"\xC3" # ret
1286 )
1288 # ECX
1289 ecx = self._run_asm(
1290 b"\xB8\x01\x00\x00\x80" # mov ax,0x80000001
1291 b"\x0f\xa2" # cpuid
1292 b"\x89\xC8" # mov ax,cx
1293 b"\xC3" # ret
1294 )
1296 # Get the extended CPU flags
1297 extended_flags = {
1298 'fpu' : _is_bit_set(ebx, 0),
1299 'vme' : _is_bit_set(ebx, 1),
1300 'de' : _is_bit_set(ebx, 2),
1301 'pse' : _is_bit_set(ebx, 3),
1302 'tsc' : _is_bit_set(ebx, 4),
1303 'msr' : _is_bit_set(ebx, 5),
1304 'pae' : _is_bit_set(ebx, 6),
1305 'mce' : _is_bit_set(ebx, 7),
1306 'cx8' : _is_bit_set(ebx, 8),
1307 'apic' : _is_bit_set(ebx, 9),
1308 #'reserved' : _is_bit_set(ebx, 10),
1309 'syscall' : _is_bit_set(ebx, 11),
1310 'mtrr' : _is_bit_set(ebx, 12),
1311 'pge' : _is_bit_set(ebx, 13),
1312 'mca' : _is_bit_set(ebx, 14),
1313 'cmov' : _is_bit_set(ebx, 15),
1314 'pat' : _is_bit_set(ebx, 16),
1315 'pse36' : _is_bit_set(ebx, 17),
1316 #'reserved' : _is_bit_set(ebx, 18),
1317 'mp' : _is_bit_set(ebx, 19),
1318 'nx' : _is_bit_set(ebx, 20),
1319 #'reserved' : _is_bit_set(ebx, 21),
1320 'mmxext' : _is_bit_set(ebx, 22),
1321 'mmx' : _is_bit_set(ebx, 23),
1322 'fxsr' : _is_bit_set(ebx, 24),
1323 'fxsr_opt' : _is_bit_set(ebx, 25),
1324 'pdpe1gp' : _is_bit_set(ebx, 26),
1325 'rdtscp' : _is_bit_set(ebx, 27),
1326 #'reserved' : _is_bit_set(ebx, 28),
1327 'lm' : _is_bit_set(ebx, 29),
1328 '3dnowext' : _is_bit_set(ebx, 30),
1329 '3dnow' : _is_bit_set(ebx, 31),
1331 'lahf_lm' : _is_bit_set(ecx, 0),
1332 'cmp_legacy' : _is_bit_set(ecx, 1),
1333 'svm' : _is_bit_set(ecx, 2),
1334 'extapic' : _is_bit_set(ecx, 3),
1335 'cr8_legacy' : _is_bit_set(ecx, 4),
1336 'abm' : _is_bit_set(ecx, 5),
1337 'sse4a' : _is_bit_set(ecx, 6),
1338 'misalignsse' : _is_bit_set(ecx, 7),
1339 '3dnowprefetch' : _is_bit_set(ecx, 8),
1340 'osvw' : _is_bit_set(ecx, 9),
1341 'ibs' : _is_bit_set(ecx, 10),
1342 'xop' : _is_bit_set(ecx, 11),
1343 'skinit' : _is_bit_set(ecx, 12),
1344 'wdt' : _is_bit_set(ecx, 13),
1345 #'reserved' : _is_bit_set(ecx, 14),
1346 'lwp' : _is_bit_set(ecx, 15),
1347 'fma4' : _is_bit_set(ecx, 16),
1348 'tce' : _is_bit_set(ecx, 17),
1349 #'reserved' : _is_bit_set(ecx, 18),
1350 'nodeid_msr' : _is_bit_set(ecx, 19),
1351 #'reserved' : _is_bit_set(ecx, 20),
1352 'tbm' : _is_bit_set(ecx, 21),
1353 'topoext' : _is_bit_set(ecx, 22),
1354 'perfctr_core' : _is_bit_set(ecx, 23),
1355 'perfctr_nb' : _is_bit_set(ecx, 24),
1356 #'reserved' : _is_bit_set(ecx, 25),
1357 'dbx' : _is_bit_set(ecx, 26),
1358 'perftsc' : _is_bit_set(ecx, 27),
1359 'pci_l2i' : _is_bit_set(ecx, 28),
1360 #'reserved' : _is_bit_set(ecx, 29),
1361 #'reserved' : _is_bit_set(ecx, 30),
1362 #'reserved' : _is_bit_set(ecx, 31)
1363 }
1365 # Get a list of only the flags that are true
1366 extended_flags = [k for k, v in extended_flags.items() if v]
1367 flags += extended_flags
1369 flags.sort()
1370 return flags
1372 # http://en.wikipedia.org/wiki/CPUID#EAX.3D80000002h.2C80000003h.2C80000004h:_Processor_Brand_String
1373 def get_processor_brand(self, max_extension_support):
1374 processor_brand = ""
1376 # Processor brand string
1377 if max_extension_support >= 0x80000004:
1378 instructions = [
1379 b"\xB8\x02\x00\x00\x80", # mov ax,0x80000002
1380 b"\xB8\x03\x00\x00\x80", # mov ax,0x80000003
1381 b"\xB8\x04\x00\x00\x80" # mov ax,0x80000004
1382 ]
1383 for instruction in instructions:
1384 # EAX
1385 eax = self._run_asm(
1386 instruction, # mov ax,0x8000000?
1387 b"\x0f\xa2" # cpuid
1388 b"\x89\xC0" # mov ax,ax
1389 b"\xC3" # ret
1390 )
1392 # EBX
1393 ebx = self._run_asm(
1394 instruction, # mov ax,0x8000000?
1395 b"\x0f\xa2" # cpuid
1396 b"\x89\xD8" # mov ax,bx
1397 b"\xC3" # ret
1398 )
1400 # ECX
1401 ecx = self._run_asm(
1402 instruction, # mov ax,0x8000000?
1403 b"\x0f\xa2" # cpuid
1404 b"\x89\xC8" # mov ax,cx
1405 b"\xC3" # ret
1406 )
1408 # EDX
1409 edx = self._run_asm(
1410 instruction, # mov ax,0x8000000?
1411 b"\x0f\xa2" # cpuid
1412 b"\x89\xD0" # mov ax,dx
1413 b"\xC3" # ret
1414 )
1416 # Combine each of the 4 bytes in each register into the string
1417 for reg in [eax, ebx, ecx, edx]:
1418 for n in [0, 8, 16, 24]:
1419 processor_brand += chr((reg >> n) & 0xFF)
1421 # Strip off any trailing NULL terminators and white space
1422 processor_brand = processor_brand.strip("\0").strip()
1424 return processor_brand
1426 # http://en.wikipedia.org/wiki/CPUID#EAX.3D80000006h:_Extended_L2_Cache_Features
1427 def get_cache(self, max_extension_support):
1428 cache_info = {}
1430 # Just return if the cache feature is not supported
1431 if max_extension_support < 0x80000006:
1432 return cache_info
1434 # ECX
1435 ecx = self._run_asm(
1436 b"\xB8\x06\x00\x00\x80" # mov ax,0x80000006
1437 b"\x0f\xa2" # cpuid
1438 b"\x89\xC8" # mov ax,cx
1439 b"\xC3" # ret
1440 )
1442 cache_info = {
1443 'size_b' : (ecx & 0xFF) * 1024,
1444 'associativity' : (ecx >> 12) & 0xF,
1445 'line_size_b' : (ecx >> 16) & 0xFFFF
1446 }
1448 return cache_info
1450 def get_ticks_func(self):
1451 retval = None
1453 if DataSource.bits == '32bit':
1454 # Works on x86_32
1455 restype = None
1456 argtypes = (ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint))
1457 get_ticks_x86_32 = self._asm_func(restype, argtypes,
1458 [
1459 b"\x55", # push bp
1460 b"\x89\xE5", # mov bp,sp
1461 b"\x31\xC0", # xor ax,ax
1462 b"\x0F\xA2", # cpuid
1463 b"\x0F\x31", # rdtsc
1464 b"\x8B\x5D\x08", # mov bx,[di+0x8]
1465 b"\x8B\x4D\x0C", # mov cx,[di+0xc]
1466 b"\x89\x13", # mov [bp+di],dx
1467 b"\x89\x01", # mov [bx+di],ax
1468 b"\x5D", # pop bp
1469 b"\xC3" # ret
1470 ]
1471 )
1473 # Monkey patch func to combine high and low args into one return
1474 old_func = get_ticks_x86_32.func
1475 def new_func():
1476 # Pass two uint32s into function
1477 high = ctypes.c_uint32(0)
1478 low = ctypes.c_uint32(0)
1479 old_func(ctypes.byref(high), ctypes.byref(low))
1481 # Shift the two uint32s into one uint64
1482 retval = ((high.value << 32) & 0xFFFFFFFF00000000) | low.value
1483 return retval
1484 get_ticks_x86_32.func = new_func
1486 retval = get_ticks_x86_32
1487 elif DataSource.bits == '64bit':
1488 # Works on x86_64
1489 restype = ctypes.c_uint64
1490 argtypes = ()
1491 get_ticks_x86_64 = self._asm_func(restype, argtypes,
1492 [
1493 b"\x48", # dec ax
1494 b"\x31\xC0", # xor ax,ax
1495 b"\x0F\xA2", # cpuid
1496 b"\x0F\x31", # rdtsc
1497 b"\x48", # dec ax
1498 b"\xC1\xE2\x20", # shl dx,byte 0x20
1499 b"\x48", # dec ax
1500 b"\x09\xD0", # or ax,dx
1501 b"\xC3", # ret
1502 ]
1503 )
1505 retval = get_ticks_x86_64
1506 return retval
1508 def get_raw_hz(self):
1509 from time import sleep
1511 ticks_fn = self.get_ticks_func()
1513 start = ticks_fn.func()
1514 sleep(1)
1515 end = ticks_fn.func()
1517 ticks = (end - start)
1518 ticks_fn.free()
1520 return ticks
1522def _get_cpu_info_from_cpuid_actual():
1523 '''
1524 Warning! This function has the potential to crash the Python runtime.
1525 Do not call it directly. Use the _get_cpu_info_from_cpuid function instead.
1526 It will safely call this function in another process.
1527 '''
1529 from io import StringIO
1531 trace = Trace(True, True)
1532 info = {}
1534 # Pipe stdout and stderr to strings
1535 sys.stdout = trace._stdout
1536 sys.stderr = trace._stderr
1538 try:
1539 # Get the CPU arch and bits
1540 arch, bits = _parse_arch(DataSource.arch_string_raw)
1542 # Return none if this is not an X86 CPU
1543 if not arch in ['X86_32', 'X86_64']:
1544 trace.fail('Not running on X86_32 or X86_64. Skipping ...')
1545 return trace.to_dict(info, True)
1547 # Return none if SE Linux is in enforcing mode
1548 cpuid = CPUID(trace)
1549 if cpuid.is_selinux_enforcing:
1550 trace.fail('SELinux is enforcing. Skipping ...')
1551 return trace.to_dict(info, True)
1553 # Get the cpu info from the CPUID register
1554 max_extension_support = cpuid.get_max_extension_support()
1555 cache_info = cpuid.get_cache(max_extension_support)
1556 info = cpuid.get_info()
1558 processor_brand = cpuid.get_processor_brand(max_extension_support)
1560 # Get the Hz and scale
1561 hz_actual = cpuid.get_raw_hz()
1562 hz_actual = _to_decimal_string(hz_actual)
1564 # Get the Hz and scale
1565 hz_advertised, scale = _parse_cpu_brand_string(processor_brand)
1566 info = {
1567 'vendor_id_raw' : cpuid.get_vendor_id(),
1568 'hardware_raw' : '',
1569 'brand_raw' : processor_brand,
1571 'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale),
1572 'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, 0),
1573 'hz_advertised' : _hz_short_to_full(hz_advertised, scale),
1574 'hz_actual' : _hz_short_to_full(hz_actual, 0),
1576 'l2_cache_size' : cache_info['size_b'],
1577 'l2_cache_line_size' : cache_info['line_size_b'],
1578 'l2_cache_associativity' : cache_info['associativity'],
1580 'stepping' : info['stepping'],
1581 'model' : info['model'],
1582 'family' : info['family'],
1583 'processor_type' : info['processor_type'],
1584 'flags' : cpuid.get_flags(max_extension_support)
1585 }
1587 info = _filter_dict_keys_with_empty_values(info)
1588 trace.success()
1589 except Exception as err:
1590 from traceback import format_exc
1591 err_string = format_exc()
1592 trace._err = ''.join(['\t\t{0}\n'.format(n) for n in err_string.split('\n')]) + '\n'
1593 return trace.to_dict(info, True)
1595 return trace.to_dict(info, False)
1597def _get_cpu_info_from_cpuid_subprocess_wrapper(queue):
1598 orig_stdout = sys.stdout
1599 orig_stderr = sys.stderr
1601 output = _get_cpu_info_from_cpuid_actual()
1603 sys.stdout = orig_stdout
1604 sys.stderr = orig_stderr
1606 queue.put(_obj_to_b64(output))
1608def _get_cpu_info_from_cpuid():
1609 '''
1610 Returns the CPU info gathered by querying the X86 cpuid register in a new process.
1611 Returns {} on non X86 cpus.
1612 Returns {} if SELinux is in enforcing mode.
1613 '''
1615 g_trace.header('Tying to get info from CPUID ...')
1617 from multiprocessing import Process, Queue
1619 # Return {} if can't cpuid
1620 if not DataSource.can_cpuid:
1621 g_trace.fail('Can\'t CPUID. Skipping ...')
1622 return {}
1624 # Get the CPU arch and bits
1625 arch, bits = _parse_arch(DataSource.arch_string_raw)
1627 # Return {} if this is not an X86 CPU
1628 if not arch in ['X86_32', 'X86_64']:
1629 g_trace.fail('Not running on X86_32 or X86_64. Skipping ...')
1630 return {}
1632 try:
1633 if CAN_CALL_CPUID_IN_SUBPROCESS:
1634 # Start running the function in a subprocess
1635 queue = Queue()
1636 p = Process(target=_get_cpu_info_from_cpuid_subprocess_wrapper, args=(queue,))
1637 p.start()
1639 # Wait for the process to end, while it is still alive
1640 while p.is_alive():
1641 p.join(0)
1643 # Return {} if it failed
1644 if p.exitcode != 0:
1645 g_trace.fail('Failed to run CPUID in process. Skipping ...')
1646 return {}
1648 # Return {} if no results
1649 if queue.empty():
1650 g_trace.fail('Failed to get anything from CPUID process. Skipping ...')
1651 return {}
1652 # Return the result, only if there is something to read
1653 else:
1654 output = _b64_to_obj(queue.get())
1655 import pprint
1656 pp = pprint.PrettyPrinter(indent=4)
1657 #pp.pprint(output)
1659 if 'output' in output and output['output']:
1660 g_trace.write(output['output'])
1662 if 'stdout' in output and output['stdout']:
1663 sys.stdout.write('{0}\n'.format(output['stdout']))
1664 sys.stdout.flush()
1666 if 'stderr' in output and output['stderr']:
1667 sys.stderr.write('{0}\n'.format(output['stderr']))
1668 sys.stderr.flush()
1670 if 'is_fail' not in output:
1671 g_trace.fail('Failed to get is_fail from CPUID process. Skipping ...')
1672 return {}
1674 # Fail if there was an exception
1675 if 'err' in output and output['err']:
1676 g_trace.fail('Failed to run CPUID in process. Skipping ...')
1677 g_trace.write(output['err'])
1678 g_trace.write('Failed ...')
1679 return {}
1681 if 'is_fail' in output and output['is_fail']:
1682 g_trace.write('Failed ...')
1683 return {}
1685 if 'info' not in output or not output['info']:
1686 g_trace.fail('Failed to get return info from CPUID process. Skipping ...')
1687 return {}
1689 return output['info']
1690 else:
1691 # FIXME: This should write the values like in the above call to actual
1692 orig_stdout = sys.stdout
1693 orig_stderr = sys.stderr
1695 output = _get_cpu_info_from_cpuid_actual()
1697 sys.stdout = orig_stdout
1698 sys.stderr = orig_stderr
1700 g_trace.success()
1701 return output['info']
1702 except Exception as err:
1703 g_trace.fail(err)
1705 # Return {} if everything failed
1706 return {}
1708def _get_cpu_info_from_proc_cpuinfo():
1709 '''
1710 Returns the CPU info gathered from /proc/cpuinfo.
1711 Returns {} if /proc/cpuinfo is not found.
1712 '''
1714 g_trace.header('Tying to get info from /proc/cpuinfo ...')
1716 try:
1717 # Just return {} if there is no cpuinfo
1718 if not DataSource.has_proc_cpuinfo():
1719 g_trace.fail('Failed to find /proc/cpuinfo. Skipping ...')
1720 return {}
1722 returncode, output = DataSource.cat_proc_cpuinfo()
1723 if returncode != 0:
1724 g_trace.fail('Failed to run cat /proc/cpuinfo. Skipping ...')
1725 return {}
1727 # Various fields
1728 vendor_id = _get_field(False, output, None, '', 'vendor_id', 'vendor id', 'vendor')
1729 processor_brand = _get_field(True, output, None, None, 'model name', 'cpu', 'processor', 'uarch')
1730 cache_size = _get_field(False, output, None, '', 'cache size')
1731 stepping = _get_field(False, output, int, -1, 'stepping')
1732 model = _get_field(False, output, int, -1, 'model')
1733 family = _get_field(False, output, int, -1, 'cpu family')
1734 hardware = _get_field(False, output, None, '', 'Hardware')
1736 # Flags
1737 flags = _get_field(False, output, None, None, 'flags', 'Features', 'ASEs implemented')
1738 if flags:
1739 flags = flags.split()
1740 flags.sort()
1742 # Check for other cache format
1743 if not cache_size:
1744 try:
1745 for i in range(0, 10):
1746 name = "cache{0}".format(i)
1747 value = _get_field(False, output, None, None, name)
1748 if value:
1749 value = [entry.split('=') for entry in value.split(' ')]
1750 value = dict(value)
1751 if 'level' in value and value['level'] == '3' and 'size' in value:
1752 cache_size = value['size']
1753 break
1754 except Exception:
1755 pass
1757 # Convert from MHz string to Hz
1758 hz_actual = _get_field(False, output, None, '', 'cpu MHz', 'cpu speed', 'clock', 'cpu MHz dynamic', 'cpu MHz static')
1759 hz_actual = hz_actual.lower().rstrip('mhz').strip()
1760 hz_actual = _to_decimal_string(hz_actual)
1762 # Convert from GHz/MHz string to Hz
1763 hz_advertised, scale = (None, 0)
1764 try:
1765 hz_advertised, scale = _parse_cpu_brand_string(processor_brand)
1766 except Exception:
1767 pass
1769 info = {
1770 'hardware_raw' : hardware,
1771 'brand_raw' : processor_brand,
1773 'l3_cache_size' : _friendly_bytes_to_int(cache_size),
1774 'flags' : flags,
1775 'vendor_id_raw' : vendor_id,
1776 'stepping' : stepping,
1777 'model' : model,
1778 'family' : family,
1779 }
1781 # Make the Hz the same for actual and advertised if missing any
1782 if not hz_advertised or hz_advertised == '0.0':
1783 hz_advertised = hz_actual
1784 scale = 6
1785 elif not hz_actual or hz_actual == '0.0':
1786 hz_actual = hz_advertised
1788 # Add the Hz if there is one
1789 if _hz_short_to_full(hz_advertised, scale) > (0, 0):
1790 info['hz_advertised_friendly'] = _hz_short_to_friendly(hz_advertised, scale)
1791 info['hz_advertised'] = _hz_short_to_full(hz_advertised, scale)
1792 if _hz_short_to_full(hz_actual, scale) > (0, 0):
1793 info['hz_actual_friendly'] = _hz_short_to_friendly(hz_actual, 6)
1794 info['hz_actual'] = _hz_short_to_full(hz_actual, 6)
1796 info = _filter_dict_keys_with_empty_values(info, {'stepping':0, 'model':0, 'family':0})
1797 g_trace.success()
1798 return info
1799 except Exception as err:
1800 g_trace.fail(err)
1801 #raise # NOTE: To have this throw on error, uncomment this line
1802 return {}
1804def _get_cpu_info_from_cpufreq_info():
1805 '''
1806 Returns the CPU info gathered from cpufreq-info.
1807 Returns {} if cpufreq-info is not found.
1808 '''
1810 g_trace.header('Tying to get info from cpufreq-info ...')
1812 try:
1813 hz_brand, scale = '0.0', 0
1815 if not DataSource.has_cpufreq_info():
1816 g_trace.fail('Failed to find cpufreq-info. Skipping ...')
1817 return {}
1819 returncode, output = DataSource.cpufreq_info()
1820 if returncode != 0:
1821 g_trace.fail('Failed to run cpufreq-info. Skipping ...')
1822 return {}
1824 hz_brand = output.split('current CPU frequency is')[1].split('\n')[0]
1825 i = hz_brand.find('Hz')
1826 assert(i != -1)
1827 hz_brand = hz_brand[0 : i+2].strip().lower()
1829 if hz_brand.endswith('mhz'):
1830 scale = 6
1831 elif hz_brand.endswith('ghz'):
1832 scale = 9
1833 hz_brand = hz_brand.rstrip('mhz').rstrip('ghz').strip()
1834 hz_brand = _to_decimal_string(hz_brand)
1836 info = {
1837 'hz_advertised_friendly' : _hz_short_to_friendly(hz_brand, scale),
1838 'hz_actual_friendly' : _hz_short_to_friendly(hz_brand, scale),
1839 'hz_advertised' : _hz_short_to_full(hz_brand, scale),
1840 'hz_actual' : _hz_short_to_full(hz_brand, scale),
1841 }
1843 info = _filter_dict_keys_with_empty_values(info)
1844 g_trace.success()
1845 return info
1846 except Exception as err:
1847 g_trace.fail(err)
1848 #raise # NOTE: To have this throw on error, uncomment this line
1849 return {}
1851def _get_cpu_info_from_lscpu():
1852 '''
1853 Returns the CPU info gathered from lscpu.
1854 Returns {} if lscpu is not found.
1855 '''
1857 g_trace.header('Tying to get info from lscpu ...')
1859 try:
1860 if not DataSource.has_lscpu():
1861 g_trace.fail('Failed to find lscpu. Skipping ...')
1862 return {}
1864 returncode, output = DataSource.lscpu()
1865 if returncode != 0:
1866 g_trace.fail('Failed to run lscpu. Skipping ...')
1867 return {}
1869 info = {}
1871 new_hz = _get_field(False, output, None, None, 'CPU max MHz', 'CPU MHz')
1872 if new_hz:
1873 new_hz = _to_decimal_string(new_hz)
1874 scale = 6
1875 info['hz_advertised_friendly'] = _hz_short_to_friendly(new_hz, scale)
1876 info['hz_actual_friendly'] = _hz_short_to_friendly(new_hz, scale)
1877 info['hz_advertised'] = _hz_short_to_full(new_hz, scale)
1878 info['hz_actual'] = _hz_short_to_full(new_hz, scale)
1880 new_hz = _get_field(False, output, None, None, 'CPU dynamic MHz', 'CPU static MHz')
1881 if new_hz:
1882 new_hz = _to_decimal_string(new_hz)
1883 scale = 6
1884 info['hz_advertised_friendly'] = _hz_short_to_friendly(new_hz, scale)
1885 info['hz_actual_friendly'] = _hz_short_to_friendly(new_hz, scale)
1886 info['hz_advertised'] = _hz_short_to_full(new_hz, scale)
1887 info['hz_actual'] = _hz_short_to_full(new_hz, scale)
1889 vendor_id = _get_field(False, output, None, None, 'Vendor ID')
1890 if vendor_id:
1891 info['vendor_id_raw'] = vendor_id
1893 brand = _get_field(False, output, None, None, 'Model name')
1894 if brand:
1895 info['brand_raw'] = brand
1896 else:
1897 brand = _get_field(False, output, None, None, 'Model')
1898 if brand and not brand.isdigit():
1899 info['brand_raw'] = brand
1901 family = _get_field(False, output, None, None, 'CPU family')
1902 if family and family.isdigit():
1903 info['family'] = int(family)
1905 stepping = _get_field(False, output, None, None, 'Stepping')
1906 if stepping and stepping.isdigit():
1907 info['stepping'] = int(stepping)
1909 model = _get_field(False, output, None, None, 'Model')
1910 if model and model.isdigit():
1911 info['model'] = int(model)
1913 l1_data_cache_size = _get_field(False, output, None, None, 'L1d cache')
1914 if l1_data_cache_size:
1915 l1_data_cache_size = l1_data_cache_size.split('(')[0].strip()
1916 info['l1_data_cache_size'] = _friendly_bytes_to_int(l1_data_cache_size)
1918 l1_instruction_cache_size = _get_field(False, output, None, None, 'L1i cache')
1919 if l1_instruction_cache_size:
1920 l1_instruction_cache_size = l1_instruction_cache_size.split('(')[0].strip()
1921 info['l1_instruction_cache_size'] = _friendly_bytes_to_int(l1_instruction_cache_size)
1923 l2_cache_size = _get_field(False, output, None, None, 'L2 cache', 'L2d cache')
1924 if l2_cache_size:
1925 l2_cache_size = l2_cache_size.split('(')[0].strip()
1926 info['l2_cache_size'] = _friendly_bytes_to_int(l2_cache_size)
1928 l3_cache_size = _get_field(False, output, None, None, 'L3 cache')
1929 if l3_cache_size:
1930 l3_cache_size = l3_cache_size.split('(')[0].strip()
1931 info['l3_cache_size'] = _friendly_bytes_to_int(l3_cache_size)
1933 # Flags
1934 flags = _get_field(False, output, None, None, 'flags', 'Features', 'ASEs implemented')
1935 if flags:
1936 flags = flags.split()
1937 flags.sort()
1938 info['flags'] = flags
1940 info = _filter_dict_keys_with_empty_values(info, {'stepping':0, 'model':0, 'family':0})
1941 g_trace.success()
1942 return info
1943 except Exception as err:
1944 g_trace.fail(err)
1945 #raise # NOTE: To have this throw on error, uncomment this line
1946 return {}
1948def _get_cpu_info_from_dmesg():
1949 '''
1950 Returns the CPU info gathered from dmesg.
1951 Returns {} if dmesg is not found or does not have the desired info.
1952 '''
1954 g_trace.header('Tying to get info from the dmesg ...')
1956 # Just return {} if this arch has an unreliable dmesg log
1957 arch, bits = _parse_arch(DataSource.arch_string_raw)
1958 if arch in ['S390X']:
1959 g_trace.fail('Running on S390X. Skipping ...')
1960 return {}
1962 # Just return {} if there is no dmesg
1963 if not DataSource.has_dmesg():
1964 g_trace.fail('Failed to find dmesg. Skipping ...')
1965 return {}
1967 # If dmesg fails return {}
1968 returncode, output = DataSource.dmesg_a()
1969 if output is None or returncode != 0:
1970 g_trace.fail('Failed to run \"dmesg -a\". Skipping ...')
1971 return {}
1973 info = _parse_dmesg_output(output)
1974 g_trace.success()
1975 return info
1978# https://openpowerfoundation.org/wp-content/uploads/2016/05/LoPAPR_DRAFT_v11_24March2016_cmt1.pdf
1979# page 767
1980def _get_cpu_info_from_ibm_pa_features():
1981 '''
1982 Returns the CPU info gathered from lsprop /proc/device-tree/cpus/*/ibm,pa-features
1983 Returns {} if lsprop is not found or ibm,pa-features does not have the desired info.
1984 '''
1986 g_trace.header('Tying to get info from lsprop ...')
1988 try:
1989 # Just return {} if there is no lsprop
1990 if not DataSource.has_ibm_pa_features():
1991 g_trace.fail('Failed to find lsprop. Skipping ...')
1992 return {}
1994 # If ibm,pa-features fails return {}
1995 returncode, output = DataSource.ibm_pa_features()
1996 if output is None or returncode != 0:
1997 g_trace.fail('Failed to glob /proc/device-tree/cpus/*/ibm,pa-features. Skipping ...')
1998 return {}
2000 # Filter out invalid characters from output
2001 value = output.split("ibm,pa-features")[1].lower()
2002 value = [s for s in value if s in list('0123456789abcfed')]
2003 value = ''.join(value)
2005 # Get data converted to Uint32 chunks
2006 left = int(value[0 : 8], 16)
2007 right = int(value[8 : 16], 16)
2009 # Get the CPU flags
2010 flags = {
2011 # Byte 0
2012 'mmu' : _is_bit_set(left, 0),
2013 'fpu' : _is_bit_set(left, 1),
2014 'slb' : _is_bit_set(left, 2),
2015 'run' : _is_bit_set(left, 3),
2016 #'reserved' : _is_bit_set(left, 4),
2017 'dabr' : _is_bit_set(left, 5),
2018 'ne' : _is_bit_set(left, 6),
2019 'wtr' : _is_bit_set(left, 7),
2021 # Byte 1
2022 'mcr' : _is_bit_set(left, 8),
2023 'dsisr' : _is_bit_set(left, 9),
2024 'lp' : _is_bit_set(left, 10),
2025 'ri' : _is_bit_set(left, 11),
2026 'dabrx' : _is_bit_set(left, 12),
2027 'sprg3' : _is_bit_set(left, 13),
2028 'rislb' : _is_bit_set(left, 14),
2029 'pp' : _is_bit_set(left, 15),
2031 # Byte 2
2032 'vpm' : _is_bit_set(left, 16),
2033 'dss_2.05' : _is_bit_set(left, 17),
2034 #'reserved' : _is_bit_set(left, 18),
2035 'dar' : _is_bit_set(left, 19),
2036 #'reserved' : _is_bit_set(left, 20),
2037 'ppr' : _is_bit_set(left, 21),
2038 'dss_2.02' : _is_bit_set(left, 22),
2039 'dss_2.06' : _is_bit_set(left, 23),
2041 # Byte 3
2042 'lsd_in_dscr' : _is_bit_set(left, 24),
2043 'ugr_in_dscr' : _is_bit_set(left, 25),
2044 #'reserved' : _is_bit_set(left, 26),
2045 #'reserved' : _is_bit_set(left, 27),
2046 #'reserved' : _is_bit_set(left, 28),
2047 #'reserved' : _is_bit_set(left, 29),
2048 #'reserved' : _is_bit_set(left, 30),
2049 #'reserved' : _is_bit_set(left, 31),
2051 # Byte 4
2052 'sso_2.06' : _is_bit_set(right, 0),
2053 #'reserved' : _is_bit_set(right, 1),
2054 #'reserved' : _is_bit_set(right, 2),
2055 #'reserved' : _is_bit_set(right, 3),
2056 #'reserved' : _is_bit_set(right, 4),
2057 #'reserved' : _is_bit_set(right, 5),
2058 #'reserved' : _is_bit_set(right, 6),
2059 #'reserved' : _is_bit_set(right, 7),
2061 # Byte 5
2062 'le' : _is_bit_set(right, 8),
2063 'cfar' : _is_bit_set(right, 9),
2064 'eb' : _is_bit_set(right, 10),
2065 'lsq_2.07' : _is_bit_set(right, 11),
2066 #'reserved' : _is_bit_set(right, 12),
2067 #'reserved' : _is_bit_set(right, 13),
2068 #'reserved' : _is_bit_set(right, 14),
2069 #'reserved' : _is_bit_set(right, 15),
2071 # Byte 6
2072 'dss_2.07' : _is_bit_set(right, 16),
2073 #'reserved' : _is_bit_set(right, 17),
2074 #'reserved' : _is_bit_set(right, 18),
2075 #'reserved' : _is_bit_set(right, 19),
2076 #'reserved' : _is_bit_set(right, 20),
2077 #'reserved' : _is_bit_set(right, 21),
2078 #'reserved' : _is_bit_set(right, 22),
2079 #'reserved' : _is_bit_set(right, 23),
2081 # Byte 7
2082 #'reserved' : _is_bit_set(right, 24),
2083 #'reserved' : _is_bit_set(right, 25),
2084 #'reserved' : _is_bit_set(right, 26),
2085 #'reserved' : _is_bit_set(right, 27),
2086 #'reserved' : _is_bit_set(right, 28),
2087 #'reserved' : _is_bit_set(right, 29),
2088 #'reserved' : _is_bit_set(right, 30),
2089 #'reserved' : _is_bit_set(right, 31),
2090 }
2092 # Get a list of only the flags that are true
2093 flags = [k for k, v in flags.items() if v]
2094 flags.sort()
2096 info = {
2097 'flags' : flags
2098 }
2099 info = _filter_dict_keys_with_empty_values(info)
2100 g_trace.success()
2101 return info
2102 except Exception as err:
2103 g_trace.fail(err)
2104 return {}
2107def _get_cpu_info_from_cat_var_run_dmesg_boot():
2108 '''
2109 Returns the CPU info gathered from /var/run/dmesg.boot.
2110 Returns {} if dmesg is not found or does not have the desired info.
2111 '''
2113 g_trace.header('Tying to get info from the /var/run/dmesg.boot log ...')
2115 # Just return {} if there is no /var/run/dmesg.boot
2116 if not DataSource.has_var_run_dmesg_boot():
2117 g_trace.fail('Failed to find /var/run/dmesg.boot file. Skipping ...')
2118 return {}
2120 # If dmesg.boot fails return {}
2121 returncode, output = DataSource.cat_var_run_dmesg_boot()
2122 if output is None or returncode != 0:
2123 g_trace.fail('Failed to run \"cat /var/run/dmesg.boot\". Skipping ...')
2124 return {}
2126 info = _parse_dmesg_output(output)
2127 g_trace.success()
2128 return info
2131def _get_cpu_info_from_sysctl():
2132 '''
2133 Returns the CPU info gathered from sysctl.
2134 Returns {} if sysctl is not found.
2135 '''
2137 g_trace.header('Tying to get info from sysctl ...')
2139 try:
2140 # Just return {} if there is no sysctl
2141 if not DataSource.has_sysctl():
2142 g_trace.fail('Failed to find sysctl. Skipping ...')
2143 return {}
2145 # If sysctl fails return {}
2146 returncode, output = DataSource.sysctl_machdep_cpu_hw_cpufrequency()
2147 if output is None or returncode != 0:
2148 g_trace.fail('Failed to run \"sysctl machdep.cpu hw.cpufrequency\". Skipping ...')
2149 return {}
2151 # Various fields
2152 vendor_id = _get_field(False, output, None, None, 'machdep.cpu.vendor')
2153 processor_brand = _get_field(True, output, None, None, 'machdep.cpu.brand_string')
2154 cache_size = _get_field(False, output, int, 0, 'machdep.cpu.cache.size')
2155 stepping = _get_field(False, output, int, 0, 'machdep.cpu.stepping')
2156 model = _get_field(False, output, int, 0, 'machdep.cpu.model')
2157 family = _get_field(False, output, int, 0, 'machdep.cpu.family')
2159 # Flags
2160 flags = _get_field(False, output, None, '', 'machdep.cpu.features').lower().split()
2161 flags.extend(_get_field(False, output, None, '', 'machdep.cpu.leaf7_features').lower().split())
2162 flags.extend(_get_field(False, output, None, '', 'machdep.cpu.extfeatures').lower().split())
2163 flags.sort()
2165 # Convert from GHz/MHz string to Hz
2166 hz_advertised, scale = _parse_cpu_brand_string(processor_brand)
2167 hz_actual = _get_field(False, output, None, None, 'hw.cpufrequency')
2168 hz_actual = _to_decimal_string(hz_actual)
2170 info = {
2171 'vendor_id_raw' : vendor_id,
2172 'brand_raw' : processor_brand,
2174 'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale),
2175 'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, 0),
2176 'hz_advertised' : _hz_short_to_full(hz_advertised, scale),
2177 'hz_actual' : _hz_short_to_full(hz_actual, 0),
2179 'l2_cache_size' : int(cache_size) * 1024,
2181 'stepping' : stepping,
2182 'model' : model,
2183 'family' : family,
2184 'flags' : flags
2185 }
2187 info = _filter_dict_keys_with_empty_values(info)
2188 g_trace.success()
2189 return info
2190 except Exception as err:
2191 g_trace.fail(err)
2192 return {}
2195def _get_cpu_info_from_sysinfo():
2196 '''
2197 Returns the CPU info gathered from sysinfo.
2198 Returns {} if sysinfo is not found.
2199 '''
2201 info = _get_cpu_info_from_sysinfo_v1()
2202 info.update(_get_cpu_info_from_sysinfo_v2())
2203 return info
2205def _get_cpu_info_from_sysinfo_v1():
2206 '''
2207 Returns the CPU info gathered from sysinfo.
2208 Returns {} if sysinfo is not found.
2209 '''
2211 g_trace.header('Tying to get info from sysinfo version 1 ...')
2213 try:
2214 # Just return {} if there is no sysinfo
2215 if not DataSource.has_sysinfo():
2216 g_trace.fail('Failed to find sysinfo. Skipping ...')
2217 return {}
2219 # If sysinfo fails return {}
2220 returncode, output = DataSource.sysinfo_cpu()
2221 if output is None or returncode != 0:
2222 g_trace.fail('Failed to run \"sysinfo -cpu\". Skipping ...')
2223 return {}
2225 # Various fields
2226 vendor_id = '' #_get_field(False, output, None, None, 'CPU #0: ')
2227 processor_brand = output.split('CPU #0: "')[1].split('"\n')[0].strip()
2228 cache_size = '' #_get_field(False, output, None, None, 'machdep.cpu.cache.size')
2229 stepping = int(output.split(', stepping ')[1].split(',')[0].strip())
2230 model = int(output.split(', model ')[1].split(',')[0].strip())
2231 family = int(output.split(', family ')[1].split(',')[0].strip())
2233 # Flags
2234 flags = []
2235 for line in output.split('\n'):
2236 if line.startswith('\t\t'):
2237 for flag in line.strip().lower().split():
2238 flags.append(flag)
2239 flags.sort()
2241 # Convert from GHz/MHz string to Hz
2242 hz_advertised, scale = _parse_cpu_brand_string(processor_brand)
2243 hz_actual = hz_advertised
2245 info = {
2246 'vendor_id_raw' : vendor_id,
2247 'brand_raw' : processor_brand,
2249 'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale),
2250 'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, scale),
2251 'hz_advertised' : _hz_short_to_full(hz_advertised, scale),
2252 'hz_actual' : _hz_short_to_full(hz_actual, scale),
2254 'l2_cache_size' : _to_friendly_bytes(cache_size),
2256 'stepping' : stepping,
2257 'model' : model,
2258 'family' : family,
2259 'flags' : flags
2260 }
2262 info = _filter_dict_keys_with_empty_values(info)
2263 g_trace.success()
2264 return info
2265 except Exception as err:
2266 g_trace.fail(err)
2267 #raise # NOTE: To have this throw on error, uncomment this line
2268 return {}
2270def _get_cpu_info_from_sysinfo_v2():
2271 '''
2272 Returns the CPU info gathered from sysinfo.
2273 Returns {} if sysinfo is not found.
2274 '''
2276 g_trace.header('Tying to get info from sysinfo version 2 ...')
2278 try:
2279 # Just return {} if there is no sysinfo
2280 if not DataSource.has_sysinfo():
2281 g_trace.fail('Failed to find sysinfo. Skipping ...')
2282 return {}
2284 # If sysinfo fails return {}
2285 returncode, output = DataSource.sysinfo_cpu()
2286 if output is None or returncode != 0:
2287 g_trace.fail('Failed to run \"sysinfo -cpu\". Skipping ...')
2288 return {}
2290 # Various fields
2291 vendor_id = '' #_get_field(False, output, None, None, 'CPU #0: ')
2292 processor_brand = output.split('CPU #0: "')[1].split('"\n')[0].strip()
2293 cache_size = '' #_get_field(False, output, None, None, 'machdep.cpu.cache.size')
2294 signature = output.split('Signature:')[1].split('\n')[0].strip()
2295 #
2296 stepping = int(signature.split('stepping ')[1].split(',')[0].strip())
2297 model = int(signature.split('model ')[1].split(',')[0].strip())
2298 family = int(signature.split('family ')[1].split(',')[0].strip())
2300 # Flags
2301 def get_subsection_flags(output):
2302 retval = []
2303 for line in output.split('\n')[1:]:
2304 if not line.startswith(' ') and not line.startswith(' '): break
2305 for entry in line.strip().lower().split(' '):
2306 retval.append(entry)
2307 return retval
2309 flags = get_subsection_flags(output.split('Features: ')[1]) + \
2310 get_subsection_flags(output.split('Extended Features (0x00000001): ')[1]) + \
2311 get_subsection_flags(output.split('Extended Features (0x80000001): ')[1])
2312 flags.sort()
2314 # Convert from GHz/MHz string to Hz
2315 lines = [n for n in output.split('\n') if n]
2316 raw_hz = lines[0].split('running at ')[1].strip().lower()
2317 hz_advertised = raw_hz.rstrip('mhz').rstrip('ghz').strip()
2318 hz_advertised = _to_decimal_string(hz_advertised)
2319 hz_actual = hz_advertised
2321 scale = 0
2322 if raw_hz.endswith('mhz'):
2323 scale = 6
2324 elif raw_hz.endswith('ghz'):
2325 scale = 9
2327 info = {
2328 'vendor_id_raw' : vendor_id,
2329 'brand_raw' : processor_brand,
2331 'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale),
2332 'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, scale),
2333 'hz_advertised' : _hz_short_to_full(hz_advertised, scale),
2334 'hz_actual' : _hz_short_to_full(hz_actual, scale),
2336 'l2_cache_size' : _to_friendly_bytes(cache_size),
2338 'stepping' : stepping,
2339 'model' : model,
2340 'family' : family,
2341 'flags' : flags
2342 }
2344 info = _filter_dict_keys_with_empty_values(info)
2345 g_trace.success()
2346 return info
2347 except Exception as err:
2348 g_trace.fail(err)
2349 #raise # NOTE: To have this throw on error, uncomment this line
2350 return {}
2352def _get_cpu_info_from_wmic():
2353 '''
2354 Returns the CPU info gathered from WMI.
2355 Returns {} if not on Windows, or wmic is not installed.
2356 '''
2357 g_trace.header('Tying to get info from wmic ...')
2359 try:
2360 # Just return {} if not Windows or there is no wmic
2361 if not DataSource.is_windows or not DataSource.has_wmic():
2362 g_trace.fail('Failed to find WMIC, or not on Windows. Skipping ...')
2363 return {}
2365 returncode, output = DataSource.wmic_cpu()
2366 if output is None or returncode != 0:
2367 g_trace.fail('Failed to run wmic. Skipping ...')
2368 return {}
2370 # Break the list into key values pairs
2371 value = output.split("\n")
2372 value = [s.rstrip().split('=') for s in value if '=' in s]
2373 value = {k: v for k, v in value if v}
2375 # Get the advertised MHz
2376 processor_brand = value.get('Name')
2377 hz_advertised, scale_advertised = _parse_cpu_brand_string(processor_brand)
2379 # Get the actual MHz
2380 hz_actual = value.get('CurrentClockSpeed')
2381 scale_actual = 6
2382 if hz_actual:
2383 hz_actual = _to_decimal_string(hz_actual)
2385 # Get cache sizes
2386 l2_cache_size = value.get('L2CacheSize') # NOTE: L2CacheSize is in kilobytes
2387 if l2_cache_size:
2388 l2_cache_size = int(l2_cache_size) * 1024
2390 l3_cache_size = value.get('L3CacheSize') # NOTE: L3CacheSize is in kilobytes
2391 if l3_cache_size:
2392 l3_cache_size = int(l3_cache_size) * 1024
2394 # Get family, model, and stepping
2395 family, model, stepping = '', '', ''
2396 description = value.get('Description') or value.get('Caption')
2397 entries = description.split(' ')
2399 if 'Family' in entries and entries.index('Family') < len(entries)-1:
2400 i = entries.index('Family')
2401 family = int(entries[i + 1])
2403 if 'Model' in entries and entries.index('Model') < len(entries)-1:
2404 i = entries.index('Model')
2405 model = int(entries[i + 1])
2407 if 'Stepping' in entries and entries.index('Stepping') < len(entries)-1:
2408 i = entries.index('Stepping')
2409 stepping = int(entries[i + 1])
2411 info = {
2412 'vendor_id_raw' : value.get('Manufacturer'),
2413 'brand_raw' : processor_brand,
2415 'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale_advertised),
2416 'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, scale_actual),
2417 'hz_advertised' : _hz_short_to_full(hz_advertised, scale_advertised),
2418 'hz_actual' : _hz_short_to_full(hz_actual, scale_actual),
2420 'l2_cache_size' : l2_cache_size,
2421 'l3_cache_size' : l3_cache_size,
2423 'stepping' : stepping,
2424 'model' : model,
2425 'family' : family,
2426 }
2428 info = _filter_dict_keys_with_empty_values(info)
2429 g_trace.success()
2430 return info
2431 except Exception as err:
2432 g_trace.fail(err)
2433 #raise # NOTE: To have this throw on error, uncomment this line
2434 return {}
2436def _get_cpu_info_from_registry():
2437 '''
2438 Returns the CPU info gathered from the Windows Registry.
2439 Returns {} if not on Windows.
2440 '''
2442 g_trace.header('Tying to get info from Windows registry ...')
2444 try:
2445 # Just return {} if not on Windows
2446 if not DataSource.is_windows:
2447 g_trace.fail('Not running on Windows. Skipping ...')
2448 return {}
2450 # Get the CPU name
2451 processor_brand = DataSource.winreg_processor_brand().strip()
2453 # Get the CPU vendor id
2454 vendor_id = DataSource.winreg_vendor_id_raw()
2456 # Get the CPU arch and bits
2457 arch_string_raw = DataSource.winreg_arch_string_raw()
2458 arch, bits = _parse_arch(arch_string_raw)
2460 # Get the actual CPU Hz
2461 hz_actual = DataSource.winreg_hz_actual()
2462 hz_actual = _to_decimal_string(hz_actual)
2464 # Get the advertised CPU Hz
2465 hz_advertised, scale = _parse_cpu_brand_string(processor_brand)
2467 # If advertised hz not found, use the actual hz
2468 if hz_advertised == '0.0':
2469 scale = 6
2470 hz_advertised = _to_decimal_string(hz_actual)
2472 # Get the CPU features
2473 feature_bits = DataSource.winreg_feature_bits()
2475 def is_set(bit):
2476 mask = 0x80000000 >> bit
2477 retval = mask & feature_bits > 0
2478 return retval
2480 # http://en.wikipedia.org/wiki/CPUID
2481 # http://unix.stackexchange.com/questions/43539/what-do-the-flags-in-proc-cpuinfo-mean
2482 # http://www.lohninger.com/helpcsuite/public_constants_cpuid.htm
2483 flags = {
2484 'fpu' : is_set(0), # Floating Point Unit
2485 'vme' : is_set(1), # V86 Mode Extensions
2486 'de' : is_set(2), # Debug Extensions - I/O breakpoints supported
2487 'pse' : is_set(3), # Page Size Extensions (4 MB pages supported)
2488 'tsc' : is_set(4), # Time Stamp Counter and RDTSC instruction are available
2489 'msr' : is_set(5), # Model Specific Registers
2490 'pae' : is_set(6), # Physical Address Extensions (36 bit address, 2MB pages)
2491 'mce' : is_set(7), # Machine Check Exception supported
2492 'cx8' : is_set(8), # Compare Exchange Eight Byte instruction available
2493 'apic' : is_set(9), # Local APIC present (multiprocessor operation support)
2494 'sepamd' : is_set(10), # Fast system calls (AMD only)
2495 'sep' : is_set(11), # Fast system calls
2496 'mtrr' : is_set(12), # Memory Type Range Registers
2497 'pge' : is_set(13), # Page Global Enable
2498 'mca' : is_set(14), # Machine Check Architecture
2499 'cmov' : is_set(15), # Conditional MOVe instructions
2500 'pat' : is_set(16), # Page Attribute Table
2501 'pse36' : is_set(17), # 36 bit Page Size Extensions
2502 'serial' : is_set(18), # Processor Serial Number
2503 'clflush' : is_set(19), # Cache Flush
2504 #'reserved1' : is_set(20), # reserved
2505 'dts' : is_set(21), # Debug Trace Store
2506 'acpi' : is_set(22), # ACPI support
2507 'mmx' : is_set(23), # MultiMedia Extensions
2508 'fxsr' : is_set(24), # FXSAVE and FXRSTOR instructions
2509 'sse' : is_set(25), # SSE instructions
2510 'sse2' : is_set(26), # SSE2 (WNI) instructions
2511 'ss' : is_set(27), # self snoop
2512 #'reserved2' : is_set(28), # reserved
2513 'tm' : is_set(29), # Automatic clock control
2514 'ia64' : is_set(30), # IA64 instructions
2515 '3dnow' : is_set(31) # 3DNow! instructions available
2516 }
2518 # Get a list of only the flags that are true
2519 flags = [k for k, v in flags.items() if v]
2520 flags.sort()
2522 info = {
2523 'vendor_id_raw' : vendor_id,
2524 'brand_raw' : processor_brand,
2526 'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale),
2527 'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, 6),
2528 'hz_advertised' : _hz_short_to_full(hz_advertised, scale),
2529 'hz_actual' : _hz_short_to_full(hz_actual, 6),
2531 'flags' : flags
2532 }
2534 info = _filter_dict_keys_with_empty_values(info)
2535 g_trace.success()
2536 return info
2537 except Exception as err:
2538 g_trace.fail(err)
2539 return {}
2541def _get_cpu_info_from_kstat():
2542 '''
2543 Returns the CPU info gathered from isainfo and kstat.
2544 Returns {} if isainfo or kstat are not found.
2545 '''
2547 g_trace.header('Tying to get info from kstat ...')
2549 try:
2550 # Just return {} if there is no isainfo or kstat
2551 if not DataSource.has_isainfo() or not DataSource.has_kstat():
2552 g_trace.fail('Failed to find isinfo or kstat. Skipping ...')
2553 return {}
2555 # If isainfo fails return {}
2556 returncode, flag_output = DataSource.isainfo_vb()
2557 if flag_output is None or returncode != 0:
2558 g_trace.fail('Failed to run \"isainfo -vb\". Skipping ...')
2559 return {}
2561 # If kstat fails return {}
2562 returncode, kstat = DataSource.kstat_m_cpu_info()
2563 if kstat is None or returncode != 0:
2564 g_trace.fail('Failed to run \"kstat -m cpu_info\". Skipping ...')
2565 return {}
2567 # Various fields
2568 vendor_id = kstat.split('\tvendor_id ')[1].split('\n')[0].strip()
2569 processor_brand = kstat.split('\tbrand ')[1].split('\n')[0].strip()
2570 stepping = int(kstat.split('\tstepping ')[1].split('\n')[0].strip())
2571 model = int(kstat.split('\tmodel ')[1].split('\n')[0].strip())
2572 family = int(kstat.split('\tfamily ')[1].split('\n')[0].strip())
2574 # Flags
2575 flags = flag_output.strip().split('\n')[-1].strip().lower().split()
2576 flags.sort()
2578 # Convert from GHz/MHz string to Hz
2579 scale = 6
2580 hz_advertised = kstat.split('\tclock_MHz ')[1].split('\n')[0].strip()
2581 hz_advertised = _to_decimal_string(hz_advertised)
2583 # Convert from GHz/MHz string to Hz
2584 hz_actual = kstat.split('\tcurrent_clock_Hz ')[1].split('\n')[0].strip()
2585 hz_actual = _to_decimal_string(hz_actual)
2587 info = {
2588 'vendor_id_raw' : vendor_id,
2589 'brand_raw' : processor_brand,
2591 'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale),
2592 'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, 0),
2593 'hz_advertised' : _hz_short_to_full(hz_advertised, scale),
2594 'hz_actual' : _hz_short_to_full(hz_actual, 0),
2596 'stepping' : stepping,
2597 'model' : model,
2598 'family' : family,
2599 'flags' : flags
2600 }
2602 info = _filter_dict_keys_with_empty_values(info)
2603 g_trace.success()
2604 return info
2605 except Exception as err:
2606 g_trace.fail(err)
2607 return {}
2609def _get_cpu_info_from_platform_uname():
2611 g_trace.header('Tying to get info from platform.uname ...')
2613 try:
2614 uname = DataSource.uname_string_raw.split(',')[0]
2616 family, model, stepping = (None, None, None)
2617 entries = uname.split(' ')
2619 if 'Family' in entries and entries.index('Family') < len(entries)-1:
2620 i = entries.index('Family')
2621 family = int(entries[i + 1])
2623 if 'Model' in entries and entries.index('Model') < len(entries)-1:
2624 i = entries.index('Model')
2625 model = int(entries[i + 1])
2627 if 'Stepping' in entries and entries.index('Stepping') < len(entries)-1:
2628 i = entries.index('Stepping')
2629 stepping = int(entries[i + 1])
2631 info = {
2632 'family' : family,
2633 'model' : model,
2634 'stepping' : stepping
2635 }
2636 info = _filter_dict_keys_with_empty_values(info)
2637 g_trace.success()
2638 return info
2639 except Exception as err:
2640 g_trace.fail(err)
2641 return {}
2643def _get_cpu_info_internal():
2644 '''
2645 Returns the CPU info by using the best sources of information for your OS.
2646 Returns {} if nothing is found.
2647 '''
2649 g_trace.write('!' * 80)
2651 # Get the CPU arch and bits
2652 arch, bits = _parse_arch(DataSource.arch_string_raw)
2654 friendly_maxsize = { 2**31-1: '32 bit', 2**63-1: '64 bit' }.get(sys.maxsize) or 'unknown bits'
2655 friendly_version = "{0}.{1}.{2}.{3}.{4}".format(*sys.version_info)
2656 PYTHON_VERSION = "{0} ({1})".format(friendly_version, friendly_maxsize)
2658 info = {
2659 'python_version' : PYTHON_VERSION,
2660 'cpuinfo_version' : CPUINFO_VERSION,
2661 'cpuinfo_version_string' : CPUINFO_VERSION_STRING,
2662 'arch' : arch,
2663 'bits' : bits,
2664 'count' : DataSource.cpu_count,
2665 'arch_string_raw' : DataSource.arch_string_raw,
2666 }
2668 g_trace.write("python_version: {0}".format(info['python_version']))
2669 g_trace.write("cpuinfo_version: {0}".format(info['cpuinfo_version']))
2670 g_trace.write("arch: {0}".format(info['arch']))
2671 g_trace.write("bits: {0}".format(info['bits']))
2672 g_trace.write("count: {0}".format(info['count']))
2673 g_trace.write("arch_string_raw: {0}".format(info['arch_string_raw']))
2675 # Try the Windows wmic
2676 _copy_new_fields(info, _get_cpu_info_from_wmic())
2678 # Try the Windows registry
2679 _copy_new_fields(info, _get_cpu_info_from_registry())
2681 # Try /proc/cpuinfo
2682 _copy_new_fields(info, _get_cpu_info_from_proc_cpuinfo())
2684 # Try cpufreq-info
2685 _copy_new_fields(info, _get_cpu_info_from_cpufreq_info())
2687 # Try LSCPU
2688 _copy_new_fields(info, _get_cpu_info_from_lscpu())
2690 # Try sysctl
2691 _copy_new_fields(info, _get_cpu_info_from_sysctl())
2693 # Try kstat
2694 _copy_new_fields(info, _get_cpu_info_from_kstat())
2696 # Try dmesg
2697 _copy_new_fields(info, _get_cpu_info_from_dmesg())
2699 # Try /var/run/dmesg.boot
2700 _copy_new_fields(info, _get_cpu_info_from_cat_var_run_dmesg_boot())
2702 # Try lsprop ibm,pa-features
2703 _copy_new_fields(info, _get_cpu_info_from_ibm_pa_features())
2705 # Try sysinfo
2706 _copy_new_fields(info, _get_cpu_info_from_sysinfo())
2708 # Try querying the CPU cpuid register
2709 # FIXME: This should print stdout and stderr to trace log
2710 _copy_new_fields(info, _get_cpu_info_from_cpuid())
2712 # Try platform.uname
2713 _copy_new_fields(info, _get_cpu_info_from_platform_uname())
2715 g_trace.write('!' * 80)
2717 return info
2719def get_cpu_info_json():
2720 '''
2721 Returns the CPU info by using the best sources of information for your OS.
2722 Returns the result in a json string
2723 '''
2725 import json
2727 output = None
2729 # If running under pyinstaller, run normally
2730 if getattr(sys, 'frozen', False):
2731 info = _get_cpu_info_internal()
2732 output = json.dumps(info)
2733 output = "{0}".format(output)
2734 # if not running under pyinstaller, run in another process.
2735 # This is done because multiprocesing has a design flaw that
2736 # causes non main programs to run multiple times on Windows.
2737 else:
2738 from subprocess import Popen, PIPE
2740 command = [sys.executable, __file__, '--json']
2741 p1 = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE)
2742 output = p1.communicate()[0]
2744 if p1.returncode != 0:
2745 return "{}"
2747 output = output.decode(encoding='UTF-8')
2749 return output
2751def get_cpu_info():
2752 '''
2753 Returns the CPU info by using the best sources of information for your OS.
2754 Returns the result in a dict
2755 '''
2757 import json
2759 output = get_cpu_info_json()
2761 # Convert JSON to Python with non unicode strings
2762 output = json.loads(output, object_hook = _utf_to_str)
2764 return output
2766def main():
2767 from argparse import ArgumentParser
2768 import json
2770 # Parse args
2771 parser = ArgumentParser(description='Gets CPU info with pure Python')
2772 parser.add_argument('--json', action='store_true', help='Return the info in JSON format')
2773 parser.add_argument('--version', action='store_true', help='Return the version of py-cpuinfo')
2774 parser.add_argument('--trace', action='store_true', help='Traces code paths used to find CPU info to file')
2775 args = parser.parse_args()
2777 global g_trace
2778 g_trace = Trace(args.trace, False)
2780 try:
2781 _check_arch()
2782 except Exception as err:
2783 sys.stderr.write(str(err) + "\n")
2784 sys.exit(1)
2786 info = _get_cpu_info_internal()
2788 if not info:
2789 sys.stderr.write("Failed to find cpu info\n")
2790 sys.exit(1)
2792 if args.json:
2793 print(json.dumps(info))
2794 elif args.version:
2795 print(CPUINFO_VERSION_STRING)
2796 else:
2797 print('Python Version: {0}'.format(info.get('python_version', '')))
2798 print('Cpuinfo Version: {0}'.format(info.get('cpuinfo_version_string', '')))
2799 print('Vendor ID Raw: {0}'.format(info.get('vendor_id_raw', '')))
2800 print('Hardware Raw: {0}'.format(info.get('hardware_raw', '')))
2801 print('Brand Raw: {0}'.format(info.get('brand_raw', '')))
2802 print('Hz Advertised Friendly: {0}'.format(info.get('hz_advertised_friendly', '')))
2803 print('Hz Actual Friendly: {0}'.format(info.get('hz_actual_friendly', '')))
2804 print('Hz Advertised: {0}'.format(info.get('hz_advertised', '')))
2805 print('Hz Actual: {0}'.format(info.get('hz_actual', '')))
2806 print('Arch: {0}'.format(info.get('arch', '')))
2807 print('Bits: {0}'.format(info.get('bits', '')))
2808 print('Count: {0}'.format(info.get('count', '')))
2809 print('Arch String Raw: {0}'.format(info.get('arch_string_raw', '')))
2810 print('L1 Data Cache Size: {0}'.format(info.get('l1_data_cache_size', '')))
2811 print('L1 Instruction Cache Size: {0}'.format(info.get('l1_instruction_cache_size', '')))
2812 print('L2 Cache Size: {0}'.format(info.get('l2_cache_size', '')))
2813 print('L2 Cache Line Size: {0}'.format(info.get('l2_cache_line_size', '')))
2814 print('L2 Cache Associativity: {0}'.format(info.get('l2_cache_associativity', '')))
2815 print('L3 Cache Size: {0}'.format(info.get('l3_cache_size', '')))
2816 print('Stepping: {0}'.format(info.get('stepping', '')))
2817 print('Model: {0}'.format(info.get('model', '')))
2818 print('Family: {0}'.format(info.get('family', '')))
2819 print('Processor Type: {0}'.format(info.get('processor_type', '')))
2820 print('Flags: {0}'.format(', '.join(info.get('flags', ''))))
2823if __name__ == '__main__':
2824 main()
2825else:
2826 g_trace = Trace(False, False)
2827 _check_arch()