This GDB script prints the interrupts in the system in the same way that /proc/interrupts does. This does include the architecture specific part done by arch_show_interrupts() for x86, ARM, ARM64 and MIPS. Example output from an ARM64 system: (gdb) lx-interruptlist CPU0 CPU1 CPU2 CPU3 10: 3167 1225 1276 2629 GICv2 30 Level arch_timer 13: 0 0 0 0 GICv2 36 Level arm-pmu 14: 0 0 0 0 GICv2 37 Level arm-pmu 15: 0 0 0 0 GICv2 38 Level arm-pmu 16: 0 0 0 0 GICv2 39 Level arm-pmu 28: 0 0 0 0 interrupt-controller@8410640 5 Edge brcmstb-gpio-wake 30: 125 0 0 0 GICv2 128 Level ttyS0 31: 0 0 0 0 interrupt-controller@8416000 0 Level mspi_done 32: 0 0 0 0 interrupt-controller@8410640 3 Edge brcmstb-waketimer 33: 0 0 0 0 interrupt-controller@8418580 8 Edge brcmstb-waketimer-rtc 34: 872 0 0 0 GICv2 230 Level brcm_scmi@0 35: 0 0 0 0 interrupt-controller@8410640 10 Edge 8d0f200.usb-phy 37: 0 0 0 0 GICv2 97 Level PCIe PME 42: 0 0 0 0 GICv2 145 Level xhci-hcd:usb1 43: 94 0 0 0 GICv2 71 Level mmc1 44: 0 0 0 0 GICv2 70 Level mmc0 IPI0: 23 666 154 98 Rescheduling interrupts IPI1: 247 1053 1701 634 Function call interrupts IPI2: 0 0 0 0 CPU stop interrupts IPI3: 0 0 0 0 CPU stop (for crash dump) interrupts IPI4: 0 0 0 0 Timer broadcast interrupts IPI5: 7 9 5 0 IRQ work interrupts IPI6: 0 0 0 0 CPU wake-up interrupts ERR: 0
Showing the changes to scripts (3 of 3 files). Show all
Unified · Split
| @@ -15,6 +15,7 @@ · 14 unmodified lines | |||
| 15 | #include <linux/clk-provider.h> | 15 | #include <linux/clk-provider.h> |
| 16 | #include <linux/fs.h> | 16 | #include <linux/fs.h> |
| 17 | #include <linux/hrtimer.h> | 17 | #include <linux/hrtimer.h> |
| 18 | #include <linux/irq.h> | ||
| 18 | #include <linux/mount.h> | 19 | #include <linux/mount.h> |
| 19 | #include <linux/of_fdt.h> | 20 | #include <linux/of_fdt.h> |
| 20 | #include <linux/radix-tree.h> | 21 | #include <linux/radix-tree.h> |
| @@ -57,6 +58,10 @@ LX_VALUE(SB_NODIRATIME) · 36 unmodified lines | |||
| 57 | /* linux/htimer.h */ | 58 | /* linux/htimer.h */ |
| 58 | LX_GDBPARSED(hrtimer_resolution) | 59 | LX_GDBPARSED(hrtimer_resolution) |
| 59 | 60 | ||
| 61 | /* linux/irq.h */ | ||
| 62 | LX_GDBPARSED(IRQD_LEVEL) | ||
| 63 | LX_GDBPARSED(IRQ_HIDDEN) | ||
| 64 | |||
| 60 | /* linux/mount.h */ | 65 | /* linux/mount.h */ |
| 61 | LX_VALUE(MNT_NOSUID) | 66 | LX_VALUE(MNT_NOSUID) |
| 62 | LX_VALUE(MNT_NODEV) | 67 | LX_VALUE(MNT_NODEV) |
| @@ -85,3 +90,12 @@ LX_CONFIG(CONFIG_HIGH_RES_TIMERS) · 22 unmodified lines | |||
| 85 | LX_CONFIG(CONFIG_NR_CPUS) | 90 | LX_CONFIG(CONFIG_NR_CPUS) |
| 86 | LX_CONFIG(CONFIG_OF) | 91 | LX_CONFIG(CONFIG_OF) |
| 87 | LX_CONFIG(CONFIG_TICK_ONESHOT) | 92 | LX_CONFIG(CONFIG_TICK_ONESHOT) |
| 93 | LX_CONFIG(CONFIG_GENERIC_IRQ_SHOW_LEVEL) | ||
| 94 | LX_CONFIG(CONFIG_X86_LOCAL_APIC) | ||
| 95 | LX_CONFIG(CONFIG_SMP) | ||
| 96 | LX_CONFIG(CONFIG_X86_THERMAL_VECTOR) | ||
| 97 | LX_CONFIG(CONFIG_X86_MCE_THRESHOLD) | ||
| 98 | LX_CONFIG(CONFIG_X86_MCE_AMD) | ||
| 99 | LX_CONFIG(CONFIG_X86_MCE) | ||
| 100 | LX_CONFIG(CONFIG_X86_IO_APIC) | ||
| 101 | LX_CONFIG(CONFIG_HAVE_KVM) | ||
| @@ -0,0 +1,232 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 2 | # | ||
| 3 | # Copyright 2023 Broadcom | ||
| 4 | |||
| 5 | import gdb | ||
| 6 | |||
| 7 | from linux import constants | ||
| 8 | from linux import cpus | ||
| 9 | from linux import utils | ||
| 10 | from linux import radixtree | ||
| 11 | |||
| 12 | irq_desc_type = utils.CachedType("struct irq_desc") | ||
| 13 | |||
| 14 | def irq_settings_is_hidden(desc): | ||
| 15 | return desc['status_use_accessors'] & constants.LX_IRQ_HIDDEN | ||
| 16 | |||
| 17 | def irq_desc_is_chained(desc): | ||
| 18 | return desc['action'] and desc['action'] == gdb.parse_and_eval("&chained_action") | ||
| 19 | |||
| 20 | def irqd_is_level(desc): | ||
| 21 | return desc['irq_data']['common']['state_use_accessors'] & constants.LX_IRQD_LEVEL | ||
| 22 | |||
| 23 | def show_irq_desc(prec, irq): | ||
| 24 | text = "" | ||
| 25 | |||
| 26 | desc = radixtree.lookup(gdb.parse_and_eval("&irq_desc_tree"), irq) | ||
| 27 | if desc is None: | ||
| 28 | return text | ||
| 29 | |||
| 30 | desc = desc.cast(irq_desc_type.get_type()) | ||
| 31 | if desc is None: | ||
| 32 | return text | ||
| 33 | |||
| 34 | if irq_settings_is_hidden(desc): | ||
| 35 | return text | ||
| 36 | |||
| 37 | any_count = 0 | ||
| 38 | if desc['kstat_irqs']: | ||
| 39 | for cpu in cpus.each_online_cpu(): | ||
| 40 | any_count += cpus.per_cpu(desc['kstat_irqs'], cpu) | ||
| 41 | |||
| 42 | if (desc['action'] == 0 or irq_desc_is_chained(desc)) and any_count == 0: | ||
| 43 | return text; | ||
| 44 | |||
| 45 | text += "%*d: " % (prec, irq) | ||
| 46 | for cpu in cpus.each_online_cpu(): | ||
| 47 | if desc['kstat_irqs']: | ||
| 48 | count = cpus.per_cpu(desc['kstat_irqs'], cpu) | ||
| 49 | else: | ||
| 50 | count = 0 | ||
| 51 | text += "%10u" % (count) | ||
| 52 | |||
| 53 | name = "None" | ||
| 54 | if desc['irq_data']['chip']: | ||
| 55 | chip = desc['irq_data']['chip'] | ||
| 56 | if chip['name']: | ||
| 57 | name = chip['name'].string() | ||
| 58 | else: | ||
| 59 | name = "-" | ||
| 60 | |||
| 61 | text += " %8s" % (name) | ||
| 62 | |||
| 63 | if desc['irq_data']['domain']: | ||
| 64 | text += " %*lu" % (prec, desc['irq_data']['hwirq']) | ||
| 65 | else: | ||
| 66 | text += " %*s" % (prec, "") | ||
| 67 | |||
| 68 | if constants.LX_CONFIG_GENERIC_IRQ_SHOW_LEVEL: | ||
| 69 | text += " %-8s" % ("Level" if irqd_is_level(desc) else "Edge") | ||
| 70 | |||
| 71 | if desc['name']: | ||
| 72 | text += "-%-8s" % (desc['name'].string()) | ||
| 73 | |||
| 74 | """ Some toolchains may not be able to provide information about irqaction """ | ||
| 75 | try: | ||
| 76 | gdb.lookup_type("struct irqaction") | ||
| 77 | action = desc['action'] | ||
| 78 | if action is not None: | ||
| 79 | text += " %s" % (action['name'].string()) | ||
| 80 | while True: | ||
| 81 | action = action['next'] | ||
| 82 | if action is not None: | ||
| 83 | break | ||
| 84 | if action['name']: | ||
| 85 | text += ", %s" % (action['name'].string()) | ||
| 86 | except: | ||
| 87 | pass | ||
| 88 | |||
| 89 | text += "\n" | ||
| 90 | |||
| 91 | return text | ||
| 92 | |||
| 93 | def show_irq_err_count(prec): | ||
| 94 | cnt = utils.gdb_eval_or_none("irq_err_count") | ||
| 95 | text = "" | ||
| 96 | if cnt is not None: | ||
| 97 | text += "%*s: %10u\n" % (prec, "ERR", cnt['counter']) | ||
| 98 | return text | ||
| 99 | |||
| 100 | def x86_show_irqstat(prec, pfx, field, desc): | ||
| 101 | irq_stat = gdb.parse_and_eval("&irq_stat") | ||
| 102 | text = "%*s: " % (prec, pfx) | ||
| 103 | for cpu in cpus.each_online_cpu(): | ||
| 104 | stat = cpus.per_cpu(irq_stat, cpu) | ||
| 105 | text += "%10u " % (stat[field]) | ||
| 106 | text += " %s\n" % (desc) | ||
| 107 | return text | ||
| 108 | |||
| 109 | def x86_show_mce(prec, var, pfx, desc): | ||
| 110 | pvar = gdb.parse_and_eval(var) | ||
| 111 | text = "%*s: " % (prec, pfx) | ||
| 112 | for cpu in cpus.each_online_cpu(): | ||
| 113 | text += "%10u " % (cpus.per_cpu(pvar, cpu)) | ||
| 114 | text += " %s\n" % (desc) | ||
| 115 | return text | ||
| 116 | |||
| 117 | def x86_show_interupts(prec): | ||
| 118 | text = x86_show_irqstat(prec, "NMI", '__nmi_count', 'Non-maskable interrupts') | ||
| 119 | |||
| 120 | if constants.LX_CONFIG_X86_LOCAL_APIC: | ||
| 121 | text += x86_show_irqstat(prec, "LOC", 'apic_timer_irqs', "Local timer interrupts") | ||
| 122 | text += x86_show_irqstat(prec, "SPU", 'irq_spurious_count', "Spurious interrupts") | ||
| 123 | text += x86_show_irqstat(prec, "PMI", 'apic_perf_irqs', "Performance monitoring interrupts") | ||
| 124 | text += x86_show_irqstat(prec, "IWI", 'apic_irq_work_irqs', "IRQ work interrupts") | ||
| 125 | text += x86_show_irqstat(prec, "RTR", 'icr_read_retry_count', "APIC ICR read retries") | ||
| 126 | if utils.gdb_eval_or_none("x86_platform_ipi_callback") is not None: | ||
| 127 | text += x86_show_irqstat(prec, "PLT", 'x86_platform_ipis', "Platform interrupts") | ||
| 128 | |||
| 129 | if constants.LX_CONFIG_SMP: | ||
| 130 | text += x86_show_irqstat(prec, "RES", 'irq_resched_count', "Rescheduling interrupts") | ||
| 131 | text += x86_show_irqstat(prec, "CAL", 'irq_call_count', "Function call interrupts") | ||
| 132 | text += x86_show_irqstat(prec, "TLB", 'irq_tlb_count', "TLB shootdowns") | ||
| 133 | |||
| 134 | if constants.LX_CONFIG_X86_THERMAL_VECTOR: | ||
| 135 | text += x86_show_irqstat(prec, "TRM", 'irq_thermal_count', "Thermal events interrupts") | ||
| 136 | |||
| 137 | if constants.LX_CONFIG_X86_MCE_THRESHOLD: | ||
| 138 | text += x86_show_irqstat(prec, "THR", 'irq_threshold_count', "Threshold APIC interrupts") | ||
| 139 | |||
| 140 | if constants.LX_CONFIG_X86_MCE_AMD: | ||
| 141 | text += x86_show_irqstat(prec, "DFR", 'irq_deferred_error_count', "Deferred Error APIC interrupts") | ||
| 142 | |||
| 143 | if constants.LX_CONFIG_X86_MCE: | ||
| 144 | text += x86_show_mce(prec, "&mce_exception_count", "MCE", "Machine check exceptions") | ||
| 145 | text == x86_show_mce(prec, "&mce_poll_count", "MCP", "Machine check polls") | ||
| 146 | |||
| 147 | text += show_irq_err_count(prec) | ||
| 148 | |||
| 149 | if constants.LX_CONFIG_X86_IO_APIC: | ||
| 150 | cnt = utils.gdb_eval_or_none("irq_mis_count") | ||
| 151 | if cnt is not None: | ||
| 152 | text += "%*s: %10u\n" % (prec, "MIS", cnt['counter']) | ||
| 153 | |||
| 154 | if constants.LX_CONFIG_HAVE_KVM: | ||
| 155 | text += x86_show_irqstat(prec, "PIN", 'kvm_posted_intr_ipis', 'Posted-interrupt notification event') | ||
| 156 | text += x86_show_irqstat(prec, "NPI", 'kvm_posted_intr_nested_ipis', 'Nested posted-interrupt event') | ||
| 157 | text += x86_show_irqstat(prec, "PIW", 'kvm_posted_intr_wakeup_ipis', 'Posted-interrupt wakeup event') | ||
| 158 | |||
| 159 | return text | ||
| 160 | |||
| 161 | def arm_common_show_interrupts(prec): | ||
| 162 | text = "" | ||
| 163 | nr_ipi = utils.gdb_eval_or_none("nr_ipi") | ||
| 164 | ipi_desc = utils.gdb_eval_or_none("ipi_desc") | ||
| 165 | ipi_types = utils.gdb_eval_or_none("ipi_types") | ||
| 166 | if nr_ipi is None or ipi_desc is None or ipi_types is None: | ||
| 167 | return text | ||
| 168 | |||
| 169 | if prec >= 4: | ||
| 170 | sep = " " | ||
| 171 | else: | ||
| 172 | sep = "" | ||
| 173 | |||
| 174 | for ipi in range(nr_ipi): | ||
| 175 | text += "%*s%u:%s" % (prec - 1, "IPI", ipi, sep) | ||
| 176 | desc = ipi_desc[ipi].cast(irq_desc_type.get_type().pointer()) | ||
| 177 | if desc == 0: | ||
| 178 | continue | ||
| 179 | for cpu in cpus.each_online_cpu(): | ||
| 180 | text += "%10u" % (cpus.per_cpu(desc['kstat_irqs'], cpu)) | ||
| 181 | text += " %s" % (ipi_types[ipi].string()) | ||
| 182 | text += "\n" | ||
| 183 | return text | ||
| 184 | |||
| 185 | def aarch64_show_interrupts(prec): | ||
| 186 | text = arm_common_show_interrupts(prec) | ||
| 187 | text += "%*s: %10lu\n" % (prec, "ERR", gdb.parse_and_eval("irq_err_count")) | ||
| 188 | return text | ||
| 189 | |||
| 190 | def arch_show_interrupts(prec): | ||
| 191 | text = "" | ||
| 192 | if utils.is_target_arch("x86"): | ||
| 193 | text += x86_show_interupts(prec) | ||
| 194 | elif utils.is_target_arch("aarch64"): | ||
| 195 | text += aarch64_show_interrupts(prec) | ||
| 196 | elif utils.is_target_arch("arm"): | ||
| 197 | text += arm_common_show_interrupts(prec) | ||
| 198 | elif utils.is_target_arch("mips"): | ||
| 199 | text += show_irq_err_count(prec) | ||
| 200 | else: | ||
| 201 | raise gdb.GdbError("Unsupported architecture: {}".format(target_arch)) | ||
| 202 | |||
| 203 | return text | ||
| 204 | |||
| 205 | class LxInterruptList(gdb.Command): | ||
| 206 | """Print /proc/interrupts""" | ||
| 207 | |||
| 208 | def __init__(self): | ||
| 209 | super(LxInterruptList, self).__init__("lx-interruptlist", gdb.COMMAND_DATA) | ||
| 210 | |||
| 211 | def invoke(self, arg, from_tty): | ||
| 212 | nr_irqs = gdb.parse_and_eval("nr_irqs") | ||
| 213 | prec = 3 | ||
| 214 | j = 1000 | ||
| 215 | while prec < 10 and j <= nr_irqs: | ||
| 216 | prec += 1 | ||
| 217 | j *= 10 | ||
| 218 | |||
| 219 | gdb.write("%*s" % (prec + 8, "")) | ||
| 220 | for cpu in cpus.each_online_cpu(): | ||
| 221 | gdb.write("CPU%-8d" % cpu) | ||
| 222 | gdb.write("\n") | ||
| 223 | |||
| 224 | if utils.gdb_eval_or_none("&irq_desc_tree") is None: | ||
| 225 | return | ||
| 226 | |||
| 227 | for irq in range(nr_irqs): | ||
| 228 | gdb.write(show_irq_desc(prec, irq)) | ||
| 229 | gdb.write(arch_show_interrupts(prec)) | ||
| 230 | |||
| 231 | |||
| 232 | LxInterruptList() | ||
| @@ -42,3 +42,4 @@ else: · 41 unmodified lines | |||
| 42 | import linux.device | 42 | import linux.device |
| 43 | import linux.mm | 43 | import linux.mm |
| 44 | import linux.radixtree | 44 | import linux.radixtree |
| 45 | import linux.interrupts | ||