diff --git a/monitor-interrupts-by-type.py b/monitor-interrupts-by-type.py new file mode 100755 index 0000000..9f13745 --- /dev/null +++ b/monitor-interrupts-by-type.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +"""Monitor per-second interrupt rates for one CPU (or TOTAL), sorted by type.""" + +import sys +import time + + +def parse_interrupts(): + """Parse /proc/interrupts. + + Returns (cpu_names, rows) where rows maps label -> (counts, description). + counts is a list of per-CPU integers; description is the trailing text + (e.g. "IO-APIC 2-edge timer" or "Local timer interrupts"). + """ + rows = {} + with open("/proc/interrupts") as f: + cpu_names = f.readline().split() + num_cpus = len(cpu_names) + for line in f: + parts = line.split() + if not parts: + continue + label = parts[0].rstrip(":") + counts = [] + i = 1 + while i < len(parts) and len(counts) < num_cpus: + try: + counts.append(int(parts[i])) + except ValueError: + break + i += 1 + if not counts: + continue + description = " ".join(parts[i:]) + rows[label] = (counts, description) + return cpu_names, rows + + +def resolve_cpu(arg, cpu_names): + """Resolve a CPU argument to an index, or None for TOTAL.""" + if arg.upper() == "TOTAL": + return None + name = arg if arg.upper().startswith("CPU") else f"CPU{arg}" + name = name.upper() + for idx, cpu in enumerate(cpu_names): + if cpu.upper() == name: + return idx + print(f"Unknown CPU '{arg}'. Available: {', '.join(cpu_names)}, TOTAL", file=sys.stderr) + sys.exit(1) + + +def value_for(counts, cpu_idx): + """Return the count for the selected CPU, or the sum for TOTAL.""" + if cpu_idx is None: + return sum(counts) + if cpu_idx < len(counts): + return counts[cpu_idx] + return 0 + + +def main(): + if len(sys.argv) < 2: + print(f"Usage: {sys.argv[0]} [interval-secs]", file=sys.stderr) + sys.exit(1) + + cpu_names, prev_rows = parse_interrupts() + cpu_idx = resolve_cpu(sys.argv[1], cpu_names) + interval = float(sys.argv[2]) if len(sys.argv) > 2 else 1.0 + + target = "TOTAL (all CPUs)" if cpu_idx is None else cpu_names[cpu_idx] + print(f"Monitoring interrupts on {target} every {interval:g}s, sorted by rate (Ctrl-C to stop)\n") + + prev = {label: value_for(counts, cpu_idx) for label, (counts, _) in prev_rows.items()} + + try: + while True: + time.sleep(interval) + _, curr_rows = parse_interrupts() + deltas = [] + for label, (counts, description) in curr_rows.items(): + curr = value_for(counts, cpu_idx) + delta = curr - prev.get(label, curr) + if delta > 0: + deltas.append((delta, label, description)) + prev[label] = curr + + deltas.sort(reverse=True) + total = sum(d for d, _, _ in deltas) + print(f"===== {total} interrupts/{interval:g}s on {target} =====") + print(f"{'RATE':>10} {'IRQ':<8} {'TYPE'}") + print("-" * 60) + for delta, label, description in deltas: + print(f"{delta:>10} {label:<8} {description}") + print() + except KeyboardInterrupt: + print() + + +if __name__ == "__main__": + main()