9b95aa0a35
Signed-off-by: Dean Troyer <dtroyer@gmail.com>
242 lines
8.3 KiB
Python
Executable File
242 lines
8.3 KiB
Python
Executable File
#!/usr/bin/env python
|
|
################################################################################
|
|
# Copyright (c) 2013 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
################################################################################
|
|
#
|
|
# topology.py -- gives a summary of logical cpu enumeration,
|
|
# sockets, cores per package, threads per core,
|
|
# total memory, and numa nodes
|
|
|
|
import os
|
|
import sys
|
|
import re
|
|
|
|
class Topology(object):
|
|
""" Build up topology information.
|
|
(i.e. logical cpu topology, NUMA nodes, memory)
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.num_cpus = 0
|
|
self.num_nodes = 0
|
|
self.num_sockets = 0
|
|
self.num_cores_per_pkg = 0
|
|
self.num_threads_per_core = 0
|
|
|
|
self.topology = {}
|
|
self.topology_idx = {}
|
|
self.total_memory_MiB = 0
|
|
self.total_memory_nodes_MiB = []
|
|
|
|
self._get_cpu_topology()
|
|
self._get_total_memory_MiB()
|
|
self._get_total_memory_nodes_MiB()
|
|
|
|
def _get_cpu_topology(self):
|
|
'''Enumerate logical cpu topology based on parsing /proc/cpuinfo
|
|
as function of socket_id, core_id, and thread_id. This updates
|
|
topology and reverse index topology_idx mapping.
|
|
|
|
:param self
|
|
:updates self.num_cpus - number of logical cpus
|
|
:updates self.num_nodes - number of sockets; maps to number of numa nodes
|
|
:updates self.topology[socket_id][core_id][thread_id] = cpu
|
|
:updates self.topology_idx[cpu] = {'s': socket_id, 'c': core_id, 't': thread_id}
|
|
:returns None
|
|
'''
|
|
|
|
self.num_cpus = 0
|
|
self.num_nodes = 0
|
|
self.num_sockets = 0
|
|
self.num_cores = 0
|
|
self.num_threads = 0
|
|
self.topology = {}
|
|
self.topology_idx = {}
|
|
|
|
Thread_cnt = {}
|
|
cpu = socket_id = core_id = thread_id = -1
|
|
re_processor = re.compile(r'^[Pp]rocessor\s+:\s+(\d+)')
|
|
re_socket = re.compile(r'^physical id\s+:\s+(\d+)')
|
|
re_core = re.compile(r'^core id\s+:\s+(\d+)')
|
|
|
|
with open('/proc/cpuinfo', 'r') as infile:
|
|
for line in infile:
|
|
|
|
match = re_processor.search(line)
|
|
if match:
|
|
cpu = int(match.group(1))
|
|
socket_id = -1; core_id = -1; thread_id = -1
|
|
self.num_cpus += 1
|
|
continue
|
|
|
|
match = re_socket.search(line)
|
|
if match:
|
|
socket_id = int(match.group(1))
|
|
continue
|
|
|
|
match = re_core.search(line)
|
|
if match:
|
|
core_id = int(match.group(1))
|
|
|
|
if not Thread_cnt.has_key(socket_id):
|
|
Thread_cnt[socket_id] = {}
|
|
if not Thread_cnt[socket_id].has_key(core_id):
|
|
Thread_cnt[socket_id][core_id] = 0
|
|
else:
|
|
Thread_cnt[socket_id][core_id] += 1
|
|
thread_id = Thread_cnt[socket_id][core_id]
|
|
|
|
if not self.topology.has_key(socket_id):
|
|
self.topology[socket_id] = {}
|
|
if not self.topology[socket_id].has_key(core_id):
|
|
self.topology[socket_id][core_id] = {}
|
|
|
|
self.topology[socket_id][core_id][thread_id] = cpu
|
|
self.topology_idx[cpu] = {'s': socket_id, 'c': core_id, 't': thread_id}
|
|
continue
|
|
self.num_nodes = len(self.topology.keys())
|
|
|
|
# In the case topology not detected, hard-code structures
|
|
if self.num_nodes == 0:
|
|
n_sockets, n_cores, n_threads = (1, self.num_cpus, 1)
|
|
self.topology = {}
|
|
for socket_id in range(n_sockets):
|
|
self.topology[socket_id] = {}
|
|
for core_id in range(n_cores):
|
|
self.topology[socket_id][core_id] = {}
|
|
for thread_id in range(n_threads):
|
|
self.topology[socket_id][core_id][thread_id] = 0
|
|
# Define Thread-Socket-Core order for logical cpu enumeration
|
|
self.topology_idx = {}
|
|
cpu = 0
|
|
for thread_id in range(n_threads):
|
|
for socket_id in range(n_sockets):
|
|
for core_id in range(n_cores):
|
|
self.topology[socket_id][core_id][thread_id] = cpu
|
|
self.topology_idx[cpu] = {'s': socket_id, 'c': core_id, 't': thread_id}
|
|
cpu += 1
|
|
self.num_nodes = len(self.topology.keys())
|
|
|
|
self.num_sockets = len(self.topology.keys())
|
|
self.num_cores_per_pkg = len(self.topology[0].keys())
|
|
self.num_threads_per_core = len(self.topology[0][0].keys())
|
|
|
|
return None
|
|
|
|
def _get_total_memory_MiB(self):
|
|
"""Get the total memory for VMs (MiB).
|
|
|
|
:updates: total memory for VMs (MiB)
|
|
|
|
"""
|
|
|
|
self.total_memory_MiB = 0
|
|
|
|
# Total memory
|
|
try:
|
|
m = open('/proc/meminfo').read().split()
|
|
idx_Total = m.index('MemTotal:') + 1
|
|
self.total_memory_MiB = int(m[idx_Total]) / 1024
|
|
except IOError:
|
|
# silently ignore IO errors (eg. file missing)
|
|
pass
|
|
return None
|
|
|
|
def _get_total_memory_nodes_MiB(self):
|
|
"""Get the total memory per numa node for VMs (MiB).
|
|
|
|
:updates: total memory per numa node for VMs (MiB)
|
|
|
|
"""
|
|
|
|
self.total_memory_nodes_MiB = []
|
|
|
|
# Memory of each numa node (MiB)
|
|
for node in range(self.num_nodes):
|
|
Total_MiB = 0
|
|
|
|
meminfo = "/sys/devices/system/node/node%d/meminfo" % node
|
|
try:
|
|
m = open(meminfo).read().split()
|
|
idx_Total = m.index('MemTotal:') + 1
|
|
Total_MiB = int(m[idx_Total]) / 1024
|
|
except IOError:
|
|
# silently ignore IO errors (eg. file missing)
|
|
pass
|
|
|
|
self.total_memory_nodes_MiB.append(Total_MiB)
|
|
return None
|
|
|
|
def _print_cpu_topology(self):
|
|
'''Print logical cpu topology enumeration as function of:
|
|
socket_id, core_id, and thread_id.
|
|
|
|
:param self
|
|
:returns None
|
|
'''
|
|
|
|
cpu_list = self.topology_idx.keys()
|
|
cpu_list.sort()
|
|
total_memory_GiB = self.total_memory_MiB/1024.0
|
|
|
|
print 'TOPOLOGY:'
|
|
print '%16s : %5d' % ('logical cpus', self.num_cpus)
|
|
print '%16s : %5d' % ('sockets', self.num_sockets)
|
|
print '%16s : %5d' % ('cores_per_pkg', self.num_cores_per_pkg)
|
|
print '%16s : %5d' % ('threads_per_core', self.num_threads_per_core)
|
|
print '%16s : %5d' % ('numa_nodes', self.num_nodes)
|
|
print '%16s : %5.2f %s' % ('total_memory', total_memory_GiB, 'GiB')
|
|
print '%16s :' % ('memory_per_node'),
|
|
for node in range(self.num_nodes):
|
|
node_memory_GiB = self.total_memory_nodes_MiB[node]/1024.0
|
|
print '%5.2f' % (node_memory_GiB),
|
|
print '%s' % ('GiB')
|
|
print
|
|
|
|
print 'LOGICAL CPU TOPOLOGY:'
|
|
print "%9s :" % 'cpu_id',
|
|
for cpu in cpu_list:
|
|
print "%3d" % cpu,
|
|
print
|
|
print "%9s :" % 'socket_id',
|
|
for cpu in cpu_list:
|
|
socket_id = self.topology_idx[cpu]['s']
|
|
print "%3d" % socket_id,
|
|
print
|
|
print "%9s :" % 'core_id',
|
|
for cpu in cpu_list:
|
|
core_id = self.topology_idx[cpu]['c']
|
|
print "%3d" % core_id,
|
|
print
|
|
print "%9s :" % 'thread_id',
|
|
for cpu in cpu_list:
|
|
thread_id = self.topology_idx[cpu]['t']
|
|
print "%3d" % thread_id,
|
|
print
|
|
print
|
|
|
|
print 'CORE TOPOLOGY:'
|
|
print "%6s %9s %7s %9s %s" % ('cpu_id', 'socket_id', 'core_id', 'thread_id', 'affinity')
|
|
for cpu in cpu_list:
|
|
affinity = 1<<cpu
|
|
socket_id = self.topology_idx[cpu]['s']
|
|
core_id = self.topology_idx[cpu]['c']
|
|
thread_id = self.topology_idx[cpu]['t']
|
|
print "%6d %9d %7d %9d 0x%x" \
|
|
% (cpu, socket_id, core_id, thread_id, affinity)
|
|
|
|
return None
|
|
|
|
#-------------------------------------------------------------------------------
|
|
''' Main Program
|
|
'''
|
|
|
|
# Get logical cpu topology
|
|
topology = Topology()
|
|
topology._print_cpu_topology()
|
|
|
|
sys.exit(0)
|