From: "Wiles, Keith" <keith.wiles@intel.com>
To: "dev@dpdk.org" <dev@dpdk.org>
Subject: Re: [dpdk-dev] [RFC PATCH] tools:new tool for system info CPU, memory and huge pages
Date: Fri, 13 May 2016 15:48:42 +0000 [thread overview]
Message-ID: <9407598A-CC69-4FEA-BB0F-F311E9B4126E@intel.com> (raw)
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@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
next reply other threads:[~2016-05-13 15:48 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-05-13 15:48 Wiles, Keith [this message]
2016-05-16 9:32 ` Thomas Monjalon
2016-05-16 14:11 ` Wiles, Keith
2016-05-16 14:31 ` Thomas Monjalon
2016-05-16 14:38 ` Wiles, Keith
2016-05-16 15:14 ` Richardson, Bruce
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=9407598A-CC69-4FEA-BB0F-F311E9B4126E@intel.com \
--to=keith.wiles@intel.com \
--cc=dev@dpdk.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).