diff --git a/sosviz/collect/irq.py b/sosviz/collect/irq.py index 9779e91..64c7b4f 100644 --- a/sosviz/collect/irq.py +++ b/sosviz/collect/irq.py @@ -18,12 +18,20 @@ def parse_report(path: pathlib.Path, data: D): if not f.is_file(): return + cpu_ids = [] + for cpu in path.glob("sys/devices/system/cpu/cpu[0-9]*"): + cpu_ids.append(int(re.match(r"cpu(\d+)", cpu.name).group(1))) + counters_len = max(*cpu_ids) + 1 + for match in INTERRUPT_RE.finditer(f.read_text()): - irq, counters, desc = match.groups() + irq = match.group(1) + counters = [0] * counters_len + for i, c in enumerate(match.group(2).split()): + counters[cpu_ids[i]] = int(c) irqs[irq] = D( irq=irq, - desc=re.sub(r"\s+", " ", desc.strip()), - counters=[int(c) for c in counters.split()], + desc=re.sub(r"\s+", " ", match.group(3).strip()), + counters=counters, ) try: irqs[irq].requested_affinity = parse_cpu_set( diff --git a/sosviz/collect/topo.py b/sosviz/collect/topo.py index c8252c0..ccb651a 100644 --- a/sosviz/collect/topo.py +++ b/sosviz/collect/topo.py @@ -9,7 +9,7 @@ def parse_report(path: pathlib.Path, data: D): - nodes = list(path.glob("sys/devices/system/node/node*")) + nodes = list(path.glob("sys/devices/system/node/node[0-9]*")) for node in nodes: match = re.match(r"^node(\d+)$", node.name) if not match: @@ -38,25 +38,26 @@ def parse_report(path: pathlib.Path, data: D): size = int(match.group(1)) * 1024 numa.setdefault("hugepages", D())[size] = int(huge.read_text()) - for cpu in path.glob("sys/devices/system/cpu/cpu*/topology"): + offline_cpus = set() + for cpu in path.glob("sys/devices/system/cpu/cpu[0-9]*"): + if not (cpu / f"node{numa_id}").is_dir(): + continue + cpu_id = int(re.match(r"cpu(\d+)", cpu.name).group(1)) + online = cpu / "online" + if online.is_file() and online.read_text().strip() == "0": + offline_cpus.add(cpu_id) + continue + topo = cpu / "topology" try: - package_id = int((cpu / "physical_package_id").read_text()) - if package_id != numa_id: - continue - except FileNotFoundError: - cpu_id = int(re.match(r"cpu(\d+)", cpu.parent.name).group(1)) - if cpu_id not in cpus: - continue - try: - threads = parse_cpu_set((cpu / "thread_siblings_list").read_text()) + threads = parse_cpu_set((topo / "thread_siblings_list").read_text()) except FileNotFoundError: try: - threads = parse_cpu_set((cpu / "core_cpus_list").read_text()) + threads = parse_cpu_set((topo / "core_cpus_list").read_text()) except FileNotFoundError: # hyperthreading disabled continue - cpus.update(threads) for t in threads: siblings[t] = threads - {t} numa.cpus = cpus + numa.offline_cpus = offline_cpus numa.thread_siblings = siblings diff --git a/sosviz/output/dot.py b/sosviz/output/dot.py index dbb110f..7d26b5c 100644 --- a/sosviz/output/dot.py +++ b/sosviz/output/dot.py @@ -659,6 +659,16 @@ def phy_numa(self, numa: D): tooltip=self.irq_counters_tooltip(housekeeping_cpus), color="blue", ) + if numa.offline_cpus: + self.node( + f"phy_cpus_offline_{numa.id}", + [ + 'Offline', + f'CPUs {bit_list(numa.offline_cpus)}', + ], + tooltip=self.irq_counters_tooltip(numa.offline_cpus), + color="gray", + ) labels = [f"memory {human_readable(numa.total_memory, 1024)}"] for size, num in numa.get("hugepages", {}).items():