Source code for crash.types.node
#!/usr/bin/python3
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
"""
The crash.types.node module offers helpers to work with NUMA nodes.
"""
from typing import Iterable, List, Type, TypeVar
import crash
from crash.util.symbols import Symbols, Symvals, Types, SymbolCallbacks
from crash.types.percpu import get_percpu_var
from crash.types.bitmap import for_each_set_bit
from crash.exceptions import DelayedAttributeError
import crash.types.zone
import gdb
symbols = Symbols(['numa_node'])
symvals = Symvals(['numa_cpu_lookup_table', 'node_data'])
types = Types(['pg_data_t', 'struct zone'])
[docs]def numa_node_id(cpu: int) -> int:
"""
Return the NUMA node ID for a given CPU
Args:
cpu: The CPU number to obtain the NUMA node ID
Returns:
:obj:`int`: The NUMA node ID for the specified CPU.
"""
if crash.current_target().arch.name() == "powerpc:common64":
return int(symvals.numa_cpu_lookup_table[cpu])
return int(get_percpu_var(symbols.numa_node, cpu))
NodeType = TypeVar('NodeType', bound='Node')
[docs]class Node:
"""
A wrapper around the Linux kernel 'struct node' structure
"""
[docs] @classmethod
def from_nid(cls: Type[NodeType], nid: int) -> 'Node':
"""
Obtain a Node using the NUMA Node ID (nid)
Args:
nid: The NUMA Node ID
Returns:
:obj:`~crash.types.Node`: the Node wrapper for the struct
node for this NID
"""
return cls(symvals.node_data[nid].dereference())
[docs] def for_each_zone(self) -> Iterable[crash.types.zone.Zone]:
"""
Iterate over each zone contained in this NUMA node
Yields:
:obj:`~crash.types.Zone`: The next Zone in this Node
"""
node_zones = self.gdb_obj["node_zones"]
ptr = int(node_zones[0].address)
(first, last) = node_zones.type.range()
for zid in range(first, last + 1):
# FIXME: gdb seems to lose the alignment padding with plain
# node_zones[zid], so we have to simulate it using zone_type.sizeof
# which appears to be correct
zone = gdb.Value(ptr).cast(types.zone_type.pointer()).dereference()
yield crash.types.zone.Zone(zone, zid)
ptr += types.zone_type.sizeof
def __init__(self, obj: gdb.Value) -> None:
"""
Initialize a Node using the gdb.Value for the struct node
Args:
obj: The node for which to construct a wrapper. The value must be
of type ``struct node``.
"""
self.gdb_obj = obj
[docs]class NodeStates:
"""
A state holder for Node states.
Attributes:
nids_online (:obj:`list` of :obj:`int`): A list of the online node IDs.
nids_possible (:obj:`list` of :obj:`int`): A list of the possible
node IDs.
"""
nids_online: List[int] = list()
nids_possible: List[int] = list()
[docs] @classmethod
def setup_node_states(cls, node_states_sym: gdb.Symbol) -> None:
"""
Detect names of node states and which nodes are possible
and online.
Meant to be used as a SymbolCallback.
Args:
node_states_sym: The ``node_states`` symbol.
"""
node_states = node_states_sym.value()
enum_node_states = gdb.lookup_type("enum node_states")
N_POSSIBLE = enum_node_states["N_POSSIBLE"].enumval
N_ONLINE = enum_node_states["N_ONLINE"].enumval
bits = node_states[N_POSSIBLE]["bits"]
cls.nids_possible = list(for_each_set_bit(bits))
bits = node_states[N_ONLINE]["bits"]
cls.nids_online = list(for_each_set_bit(bits))
[docs] def for_each_nid(self) -> Iterable[int]:
"""
Iterate over each NUMA Node ID
Yields:
:obj:`int`: The next NUMA Node ID
"""
if not self.nids_possible:
raise DelayedAttributeError('node_states')
for nid in self.nids_possible:
yield nid
[docs] def for_each_online_nid(self) -> Iterable[int]:
"""
Iterate over each online NUMA Node ID
Yields:
:obj:`int`: The next NUMA Node ID
"""
if not self.nids_online:
raise DelayedAttributeError('node_states')
for nid in self.nids_online:
yield nid
symbol_cbs = SymbolCallbacks([('node_states', NodeStates.setup_node_states)])
_state = NodeStates()
[docs]def for_each_nid() -> Iterable[int]:
"""
Iterate over each NUMA Node ID
Yields:
:obj:`int`: The next NUMA Node ID
"""
for nid in _state.for_each_nid():
yield nid
[docs]def for_each_online_nid() -> Iterable[int]:
"""
Iterate over each online NUMA Node ID
Yields:
:obj:`int`: The next NUMA Node ID
"""
for nid in _state.for_each_online_nid():
yield nid
[docs]def for_each_node() -> Iterable[Node]:
"""
Iterate over each NUMA Node
Yields:
:obj:`int`: The next NUMA Node
"""
for nid in for_each_nid():
yield Node.from_nid(nid)
[docs]def for_each_online_node() -> Iterable[Node]:
"""
Iterate over each Online NUMA Node
Yields:
:obj:`int`: The next NUMA Node
"""
for nid in for_each_online_nid():
yield Node.from_nid(nid)
[docs]def for_each_zone() -> Iterable[crash.types.zone.Zone]:
for node in for_each_node():
for zone in node.for_each_zone():
yield zone
[docs]def for_each_populated_zone() -> Iterable[crash.types.zone.Zone]:
#TODO: some filter thing?
for zone in for_each_zone():
if zone.is_populated():
yield zone