Sorry this one should have been an RFC instead of a patch. I create this new tool to combine some information and use /sys/devices instead. What I was hoping was some of you could try this script and see if it works correctly. Also I was hope to find out if this script is useful and what other features you would like in this type of tool.
I am not a Python coder, so if you have suggestions on the code please let me know. Thanks >The new tool uses /sys/devices instead of /proc directory, which >does not exist on all systems. If the procfs is not available >then memory and huge page information is skipped. > >The tool also can emit a json format in short or long form to >allow for machine readable information. > >Here is the usage information: > >Usage: sys_info.py [options] > >Show the lcores layout with core id and socket(s). > >Options: > --help or -h: > Display the usage information and quit > > --long or -l: > Display the information in a machine readable long format. > > --short or -s: > Display the information in a machine readable short format. > > --pci or -p: > Display all of the Ethernet devices in the system using 'lspci'. > > --version or -v: > Display the current version number of this script. > > --debug or -d: > Output some debug information. > > default: > Display the information in a human readable format. > >Signed-off-by: Keith Wiles <keith.wiles at intel.com> >--- > tools/sys_info.py | 551 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 551 insertions(+) > create mode 100755 tools/sys_info.py > >diff --git a/tools/sys_info.py b/tools/sys_info.py >new file mode 100755 >index 0000000..5e09d12 >--- /dev/null >+++ b/tools/sys_info.py >@@ -0,0 +1,551 @@ >+#! /usr/bin/python >+# >+# BSD LICENSE >+# >+# Copyright(c) 2016 Intel Corporation. All rights reserved. >+# All rights reserved. >+# >+# Redistribution and use in source and binary forms, with or without >+# modification, are permitted provided that the following conditions >+# are met: >+# >+# * Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# * Redistributions in binary form must reproduce the above copyright >+# notice, this list of conditions and the following disclaimer in >+# the documentation and/or other materials provided with the >+# distribution. >+# * Neither the name of Intel Corporation nor the names of its >+# contributors may be used to endorse or promote products derived >+# from this software without specific prior written permission. >+# >+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+# >+ >+import os, sys >+import getopt >+import json >+from os.path import exists, abspath, dirname, basename >+ >+version = "0.1.3" >+ >+# Global lists and flags >+machine_readable = 0 >+show_pci = False >+debug_flag = False >+coremaps_flag = False >+ >+sys_info = {} >+coremaps = {} >+ >+def proc_cpuinfo_path(): >+ '''Return the cpu information from /proc''' >+ return "/proc/cpuinfo" >+ >+def proc_sysinfo_path(): >+ '''Return the system path string from /proc''' >+ return "/proc/sysinfo" >+ >+def proc_meminfo_path(): >+ '''Return the memory information path from /proc''' >+ return "/proc/meminfo" >+ >+def sys_system_path(): >+ '''Return the system path string from /sys''' >+ return "/sys/devices/system" >+ >+def read_file(path, whole_file=False): >+ '''Read the first line of a file''' >+ >+ if os.access(path, os.F_OK) == False: >+ print "Path (%s) Not found" % path >+ return "" >+ >+ fd = open(path) >+ if whole_file == True: >+ lines = fd.readlines() >+ else: >+ line = fd.readline() >+ fd.close() >+ >+ if whole_file == True: >+ return lines >+ >+ return line >+ >+def get_range(line): >+ '''Split a line and convert to low/high values''' >+ >+ line = line.strip() >+ >+ if '-' in line: >+ low, high = line.split("-") >+ elif ',' in line: >+ low, high = line.split(",") >+ else: >+ return [int(line)] >+ >+ return [int(low), int(high)] >+ >+def get_ranges(line): >+ '''Split a set of ranges into first low/high, second low/high value''' >+ >+ line = line.strip() >+ >+ first, second = line.split(',') >+ >+ f = get_range(first) >+ s = get_range(second) >+ >+ return [f, s] >+ >+def get_low(line): >+ '''Return the low value in a range''' >+ range = get_range(line) >+ return int(range[0]) >+ >+def get_high(line): >+ '''Return the high value in a range''' >+ range = get_range(line) >+ >+ if len(range) > 1: >+ return range[1] >+ >+ return range[0] >+ >+def get_value(line): >+ '''Convert a number into a integer value''' >+ return int(line) >+ >+def cpu_path(name): >+ '''Return the cpu path string to a given file name''' >+ return "%s/cpu/%s" % (sys_system_path(), name) >+ >+def topology_path(id, name): >+ '''Return the topology path string to a given file cpu id and file name''' >+ return "%s/cpu/cpu%d/topology/%s" % (sys_system_path(), id, name) >+ >+def get_cpu_info(filename): >+ ''' Read the file using cpu_base_path/name ''' >+ return read_file(cpu_path(filename)) >+ >+def get_topology_info(id, filename): >+ ''' Read the cpu_base_path/cpu(id)/topology/(name) file ''' >+ return read_file(topology_path(id, filename)) >+ >+def cpulist_parse(info): >+ ''' ''' >+ cpuset = [] >+ for c in range(0, 255): >+ cpuset.append(0) >+ >+ ranges = info.split(',') >+ for r in ranges: >+ if not '-' in r: >+ cpuset[int(r)] = 1 >+ else: >+ low, high = get_range(r) >+ for c in range(low, high + 1): >+ cpuset[c] = 1 >+ >+ return cpuset >+ >+def path_read_cpuset(fn, num): >+ ''' ''' >+ info = get_topology_info(num, fn) >+ return cpulist_parse(info.strip()) >+ >+def set_count(arr): >+ cnt = 0 >+ for c in arr: >+ cnt = cnt + c >+ return cnt >+ >+def read_topology(ncpus, cpu): >+ global sys_info >+ >+ path = topology_path(cpu, "thread_siblings_list") >+ if os.access(path, os.F_OK) == False: >+ print "Path (%s) Not found" % path >+ return "" >+ >+ thread_siblings = path_read_cpuset("thread_siblings_list", cpu) >+ core_siblings = path_read_cpuset("core_siblings_list", cpu) >+ >+ path = topology_path(cpu, "book_siblings_list") >+ if os.access(path, os.F_OK): >+ book_siblings = path_read_cpuset("book_siblings_list", cpu) >+ else: >+ book_siblings = [] >+ >+ if not 'nthreads' in sys_info: >+ nthreads = set_count(thread_siblings) >+ if nthreads == 0: >+ nthreads = 1 >+ ncores = set_count(core_siblings)/nthreads >+ if ncores == 0: >+ ncores = 1 >+ nsockets = ncpus / nthreads / ncores >+ if nsockets == 0: >+ nsockets = 1 >+ nbooks = ncpus / nthreads / ncores / nsockets >+ if nbooks == 0: >+ nbooks = 1 >+ nlcores = nbooks * nsockets * ncores * nthreads >+ sys_info['nthreads'] = nthreads >+ sys_info['ncores'] = ncores >+ sys_info['nsockets'] = nsockets >+ sys_info['nbooks'] = nbooks >+ sys_info['nlcores'] = nlcores >+ >+def get_core_ids(): >+ ''' Return the core ids and the lcores for each socket assigned >+ to the core id ''' >+ >+ core_ids = [] >+ >+ # Create cpu id to lcore id list core_ids[lcore] == core_id >+ # if the core_id does not exist in the list already >+ for c in range(0, sys_value('nlcores')): >+ id = get_value(get_topology_info(c, "core_id")) >+ if id not in core_ids: >+ core_ids.append(id) >+ >+ return core_ids >+ >+def get_coremaps(): >+ ''' Return the core ids and the lcores for each socket assigned >+ to the core id ''' >+ global sys_info >+ >+ coremaps = {} >+ >+ for c in range(0, sys_value('nlcores')): >+ id = int(get_topology_info(c, "core_id").strip()) >+ socket = int(get_topology_info(c, "physical_package_id").strip()) >+ ranges = get_range(get_topology_info(c, "thread_siblings_list")) >+ >+ if coremaps.has_key(id): >+ if not socket in coremaps[id]: >+ coremaps[id].update({socket: ranges}) >+ else: >+ coremaps[id] = {socket: ranges} >+ >+ return coremaps >+ >+def get_model_name(): >+ '''Parse up the CPU information into tables. The information >+ is taken from /proc/cpuinfo file. ''' >+ model = "Unknown" >+ >+ if os.access(proc_cpuinfo_path(), os.F_OK) == False: >+ return details >+ >+ lines = read_file(proc_cpuinfo_path(), True) >+ >+ for line in lines: >+ line = line.strip() >+ if len(line) != 0: >+ name, value = line.split(":", 1) >+ name = name.strip() >+ value = value.strip() >+ if 'model name' == name: >+ return value >+ else: >+ break >+ >+ return model >+ >+def parse_meminfo(): >+ ''' Parse up the /proc/meminfo if available and return a dictionary. ''' >+ details = {} >+ >+ if os.access(proc_meminfo_path(), os.F_OK) == False: >+ return details; >+ >+ lines = read_file(proc_meminfo_path(), True) >+ >+ for line in lines: >+ if len(line.strip()) != 0: >+ name, value = line.split(":", 1) >+ details[name.strip()] = value.strip() >+ >+ return details >+ >+def sys_value(key): >+ '''Get a value from the System Info dictionary if available. ''' >+ global sys_info >+ >+ if key in sys_info: >+ return sys_info[key]; >+ >+ for core in sys_info: >+ if key in core: >+ return core[key] >+ >+ return "Not Found" >+ >+def mem_value(key): >+ '''Get a value from the Memory Info dictionary if available. ''' >+ >+ info = sys_info['memory'] >+ >+ if key in info: >+ return info[key] >+ >+ return "Not Found" >+ >+def get_ether_devices(): >+ ''' Read the PCI ethernet list into a table ''' >+ >+ fd = os.popen("lspci | grep Ether", 'r') >+ lines = fd.readlines() >+ fd.close() >+ >+ dict = {} >+ for line in lines: >+ pci, desc = line.rsplit(':',1) >+ pci_id, _ = pci.split(' ', 1) >+ if not 'Null' in desc: >+ dict[pci_id] = desc.strip(); >+ >+ return dict >+ >+def format_cores(val): >+ '''Format a list of lcores into '[%3d,%3d,...]' ''' >+ s = "[" >+ for r in val: >+ s = s + "%3d," % r >+ s = s[:-1] >+ s = s + "]" >+ return s >+ >+def print_core_ids(ids): >+ '''Return the string of a set of lcores for each id''' >+ s = "" >+ for i in ids: >+ s = s + format_cores(ids[i]) + " " >+ >+ return s >+ >+def print_lcore_pairs(): >+ ''' Print out the header line for the socket/lcore data''' >+ >+ nsockets = sys_value('nsockets') >+ >+ print "\nShowing mapping of lcores to core_ids and sockets:" >+ print " Core ID ", >+ for id in range(0, nsockets): >+ print " Socket %d " % id, >+ print "" >+ >+ print " -------", >+ for id in range(0, nsockets): >+ print " ---------", >+ print "" >+ >+ for id in sys_info['core_ids']: >+ vals = sys_info['coremaps'] >+ print " %3d %s" % (id, print_core_ids(vals[id])) >+ >+def usage(): >+ '''Print usage information for the program''' >+ print(""" >+Usage: %s [options] >+ >+Show the lcores layout with core id and socket(s). >+ >+Options: >+ --help or -h: >+ Display the usage information and quit >+ >+ --long or -l: >+ Display the information in a machine readable long format. >+ >+ --short or -s: >+ Display the information in a machine readable short format. >+ >+ --pci or -p: >+ Display all of the Ethernet devices in the system using 'lspci'. >+ >+ --version or -v: >+ Display the current version number of this script. >+ >+ --debug or -d: >+ Output some debug information. >+ >+ default: >+ Display the information in a human readable format. >+ >+ """ % basename(sys.argv[0])) >+ >+def parse_args(): >+ '''Parse the command line arguments''' >+ global machine_readable >+ global show_pci >+ global debug_flag >+ global coremaps_flag >+ global args >+ >+ if len(sys.argv) == 1: >+ return >+ try: >+ opts, args = getopt.getopt(sys.argv[1:], "hslpvdc", >+ ["help", "short", "long", "pci", "version", "debug", >"coremaps"]) >+ >+ except getopt.GetoptError as error: >+ print "** Error ", str(error) >+ usage() >+ sys.exit(1) >+ >+ machine_readable = 0 >+ for opt, arg in opts: >+ if opt == "--help" or opt == "-h": >+ usage() >+ sys.exit(0) >+ if opt == "--short" or opt == "-s": >+ machine_readable = 1 >+ if opt == "--long" or opt == "-l": >+ machine_readable = 2 >+ if opt == "--pci" or opt == "-p": >+ show_pci = True >+ if opt == "--debug" or opt == "-d": >+ debug_flag = True >+ if opt == "--coremaps" or opt == "-c": >+ coremaps_flag = True >+ if opt == "--version" or opt == "-v": >+ print "%s version %s" % (sys.argv[0], version) >+ sys.exit(0) >+ >+def gather_information(): >+ global sys_info >+ >+ sys_info['memory'] = parse_meminfo() >+ >+ sys_info.update({'model name': get_model_name()}) >+ >+ ncpus = get_high(get_cpu_info("present")) + 1 >+ for cpu in range(0, ncpus): >+ read_topology(ncpus, cpu) >+ >+ uname = os.uname() >+ sys_info.update({'sysname': uname[0]}) >+ sys_info.update({'release': uname[2]}) >+ sys_info.update({'version': uname[3]}) >+ sys_info.update({'machine': uname[4]}) >+ >+ sys_info['core_ids'] = get_core_ids() >+ sys_info['coremaps'] = get_coremaps() >+ >+ if show_pci: >+ info = get_ether_devices() >+ sys_info['pci'] = info >+ else: >+ sys_info['pci'] = {} >+ >+ if debug_flag == True: >+ print "sys_info = ", json.dumps(sys_info, indent=4) >+ >+def main(): >+ '''program main function''' >+ global machine_readable >+ global sys_info >+ >+ parse_args() >+ >+ gather_information() >+ >+ if machine_readable > 0: >+ json_details = {'system': { >+ 'sysname': sys_info['sysname'], >+ 'version': sys_info['version'], >+ 'release': sys_info['release'], >+ 'machine': sys_info['machine'], >+ }} >+ >+ cpu_info = {'cpu': { >+ 'model_name': sys_value('model name'), >+ 'nthreads': sys_value('nthreads'), >+ 'ncores': sys_value('ncores'), >+ 'nsockets': sys_value('nsockets'), >+ 'nbooks': sys_value('nbooks'), >+ 'nlcores': sys_value('nlcores'), >+ 'cores_ids': sys_value('core_ids')}} >+ >+ memory = {'memory': { >+ 'total': mem_value('MemTotal'), >+ 'free': mem_value('MemFree'), >+ 'available': mem_value('MemAvailable')}} >+ hugepages = {'hugepages': { >+ 'total': mem_value('HugePages_Total'), >+ 'free': mem_value('HugePages_Free'), >+ 'reserved': mem_value('HugePages_Rsvd'), >+ 'surp': mem_value('HugePages_Surp'), >+ 'page_size': mem_value('Hugepagesize')}} >+ >+ json_details.update(cpu_info) >+ json_details.update(memory) >+ json_details.update(hugepages) >+ json_details.update({'coremaps': sys_value('coremaps')}) >+ if show_pci: >+ json_details.update({'pci': sys_value('pci')}) >+ >+ if machine_readable == 2: >+ print json.dumps(json_details, sort_keys=False, indent=4) >+ else: >+ print json.dumps(json_details, sort_keys=False, separators=(', ', >':')) >+ else: >+ if len(sys_info) > 0: >+ print "System Information:" >+ print " Model Name : %s" % sys_value('model name') >+ print " sysname : %s" % sys_value('sysname') >+ print " release : %s" % sys_value('release') >+ print " version : %s" % sys_value('version') >+ print " machine : %s" % sys_value('machine') >+ print "" >+ >+ print "CPU Information:" >+ print " nthreads : %3d" % sys_value('nthreads') >+ print " ncores : %3d" % sys_value('ncores') >+ print " nsockets : %3d" % sys_value('nsockets') >+ print " nbooks : %3d" % sys_value('nbooks') >+ print " nlcores : %3d" % sys_value('nlcores') >+ >+ print "" >+ if len(sys_info['memory']) > 0: >+ print "Memory:" >+ print " Total : ", mem_value('MemTotal') >+ print " Free : ", mem_value('MemFree') >+ print " Available: ", mem_value('MemAvailable') >+ print "" >+ print "Huge Pages:" >+ print " Total : ", mem_value('HugePages_Total') >+ print " Free : ", mem_value('HugePages_Free') >+ print " Reserved : ", mem_value('HugePages_Rsvd') >+ print " Surp : ", mem_value('HugePages_Surp') >+ print " Page Size: ", mem_value('Hugepagesize') >+ >+ if show_pci: >+ print "\nList of devices labeled as Ethernet on the PCI bus" >+ print " PCI ID Description" >+ info = sys_value('pci') >+ for p in info: >+ print " %s %s" % (p, info[p]) >+ >+ print_lcore_pairs() >+ >+ vals = (sys_value('nlcores'), sys_value('nsockets'), >sys_value('ncores'), sys_value('nthreads')) >+ print "\n%d lcores, %d socket(s), %d cores per socket, %d >hyper-thread(s) per core" % vals >+ >+if __name__ == "__main__": >+ main() >-- >2.8.0.GIT > > Regards, Keith