1#!/usr/bin/env python 2# @lint-avoid-python-3-compatibility-imports 3# 4# hardirqs Summarize hard IRQ (interrupt) event time. 5# For Linux, uses BCC, eBPF. 6# 7# USAGE: hardirqs [-h] [-T] [-N] [-C] [-d] [interval] [outputs] 8# 9# Thanks Amer Ather for help understanding irq behavior. 10# 11# Copyright (c) 2015 Brendan Gregg. 12# Licensed under the Apache License, Version 2.0 (the "License") 13# 14# 19-Oct-2015 Brendan Gregg Created this. 15 16from __future__ import print_function 17from bcc import BPF 18from time import sleep, strftime 19import argparse 20 21# arguments 22examples = """examples: 23 ./hardirqs # sum hard irq event time 24 ./hardirqs -d # show hard irq event time as histograms 25 ./hardirqs 1 10 # print 1 second summaries, 10 times 26 ./hardirqs -NT 1 # 1s summaries, nanoseconds, and timestamps 27""" 28parser = argparse.ArgumentParser( 29 description="Summarize hard irq event time as histograms", 30 formatter_class=argparse.RawDescriptionHelpFormatter, 31 epilog=examples) 32parser.add_argument("-T", "--timestamp", action="store_true", 33 help="include timestamp on output") 34parser.add_argument("-N", "--nanoseconds", action="store_true", 35 help="output in nanoseconds") 36parser.add_argument("-C", "--count", action="store_true", 37 help="show event counts instead of timing") 38parser.add_argument("-d", "--dist", action="store_true", 39 help="show distributions as histograms") 40parser.add_argument("interval", nargs="?", default=99999999, 41 help="output interval, in seconds") 42parser.add_argument("outputs", nargs="?", default=99999999, 43 help="number of outputs") 44parser.add_argument("--ebpf", action="store_true", 45 help=argparse.SUPPRESS) 46args = parser.parse_args() 47countdown = int(args.outputs) 48if args.count and (args.dist or args.nanoseconds): 49 print("The --count option can't be used with time-based options") 50 exit() 51if args.count: 52 factor = 1 53 label = "count" 54elif args.nanoseconds: 55 factor = 1 56 label = "nsecs" 57else: 58 factor = 1000 59 label = "usecs" 60debug = 0 61 62# define BPF program 63bpf_text = """ 64#include <uapi/linux/ptrace.h> 65#include <linux/irq.h> 66#include <linux/irqdesc.h> 67#include <linux/interrupt.h> 68 69typedef struct irq_key { 70 char name[32]; 71 u64 slot; 72} irq_key_t; 73BPF_HASH(start, u32); 74BPF_HASH(irqdesc, u32, struct irq_desc *); 75BPF_HISTOGRAM(dist, irq_key_t); 76 77// count IRQ 78int count_only(struct pt_regs *ctx, struct irq_desc *desc) 79{ 80 u32 pid = bpf_get_current_pid_tgid(); 81 82 struct irqaction *action = desc->action; 83 char *name = (char *)action->name; 84 85 irq_key_t key = {.slot = 0 /* ignore */}; 86 bpf_probe_read(&key.name, sizeof(key.name), name); 87 dist.increment(key); 88 89 return 0; 90} 91 92// time IRQ 93int trace_start(struct pt_regs *ctx, struct irq_desc *desc) 94{ 95 u32 pid = bpf_get_current_pid_tgid(); 96 u64 ts = bpf_ktime_get_ns(); 97 start.update(&pid, &ts); 98 irqdesc.update(&pid, &desc); 99 return 0; 100} 101 102int trace_completion(struct pt_regs *ctx) 103{ 104 u64 *tsp, delta; 105 struct irq_desc **descp; 106 u32 pid = bpf_get_current_pid_tgid(); 107 108 // fetch timestamp and calculate delta 109 tsp = start.lookup(&pid); 110 descp = irqdesc.lookup(&pid); 111 if (tsp == 0 || descp == 0) { 112 return 0; // missed start 113 } 114 struct irq_desc *desc = *descp; 115 struct irqaction *action = desc->action; 116 char *name = (char *)action->name; 117 delta = bpf_ktime_get_ns() - *tsp; 118 119 // store as sum or histogram 120 STORE 121 122 start.delete(&pid); 123 irqdesc.delete(&pid); 124 return 0; 125} 126""" 127 128# code substitutions 129if args.dist: 130 bpf_text = bpf_text.replace('STORE', 131 'irq_key_t key = {.slot = bpf_log2l(delta / %d)};' % factor + 132 'bpf_probe_read(&key.name, sizeof(key.name), name);' + 133 'dist.increment(key);') 134else: 135 bpf_text = bpf_text.replace('STORE', 136 'irq_key_t key = {.slot = 0 /* ignore */};' + 137 'bpf_probe_read(&key.name, sizeof(key.name), name);' + 138 'dist.increment(key, delta);') 139if debug or args.ebpf: 140 print(bpf_text) 141 if args.ebpf: 142 exit() 143 144# load BPF program 145b = BPF(text=bpf_text) 146 147# these should really use irq:irq_handler_entry/exit tracepoints: 148if args.count: 149 b.attach_kprobe(event="handle_irq_event_percpu", fn_name="count_only") 150 print("Tracing hard irq events... Hit Ctrl-C to end.") 151else: 152 b.attach_kprobe(event="handle_irq_event_percpu", fn_name="trace_start") 153 b.attach_kretprobe(event="handle_irq_event_percpu", 154 fn_name="trace_completion") 155 print("Tracing hard irq event time... Hit Ctrl-C to end.") 156 157# output 158exiting = 0 if args.interval else 1 159dist = b.get_table("dist") 160while (1): 161 try: 162 sleep(int(args.interval)) 163 except KeyboardInterrupt: 164 exiting = 1 165 166 print() 167 if args.timestamp: 168 print("%-8s\n" % strftime("%H:%M:%S"), end="") 169 170 if args.dist: 171 dist.print_log2_hist(label, "hardirq") 172 else: 173 print("%-26s %11s" % ("HARDIRQ", "TOTAL_" + label)) 174 for k, v in sorted(dist.items(), key=lambda dist: dist[1].value): 175 print("%-26s %11d" % (k.name.decode('utf-8', 'replace'), v.value / factor)) 176 dist.clear() 177 178 countdown -= 1 179 if exiting or countdown == 0: 180 exit() 181