1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 // Copyright (c) 2020 Wenbo Zhang
3 //
4 // Based on softirq(8) from BCC by Brendan Gregg & Sasha Goldshtein.
5 // 15-Aug-2020 Wenbo Zhang Created this.
6 #include <argp.h>
7 #include <signal.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <time.h>
12 #include <unistd.h>
13 #include <bpf/libbpf.h>
14 #include <bpf/bpf.h>
15 #include "softirqs.h"
16 #include "softirqs.skel.h"
17 #include "trace_helpers.h"
18
19 struct env {
20 bool distributed;
21 bool nanoseconds;
22 bool count;
23 time_t interval;
24 int times;
25 bool timestamp;
26 bool verbose;
27 } env = {
28 .interval = 99999999,
29 .times = 99999999,
30 .count = false,
31 };
32
33 static volatile bool exiting;
34
35 const char *argp_program_version = "softirqs 0.1";
36 const char *argp_program_bug_address =
37 "https://github.com/iovisor/bcc/tree/master/libbpf-tools";
38 const char argp_program_doc[] =
39 "Summarize soft irq event time as histograms.\n"
40 "\n"
41 "USAGE: softirqs [--help] [-T] [-N] [-d] [interval] [count]\n"
42 "\n"
43 "EXAMPLES:\n"
44 " softirqs # sum soft irq event time\n"
45 " softirqs -d # show soft irq event time as histograms\n"
46 " softirqs 1 10 # print 1 second summaries, 10 times\n"
47 " softirqs -NT 1 # 1s summaries, nanoseconds, and timestamps\n";
48
49 static const struct argp_option opts[] = {
50 { "distributed", 'd', NULL, 0, "Show distributions as histograms" },
51 { "timestamp", 'T', NULL, 0, "Include timestamp on output" },
52 { "nanoseconds", 'N', NULL, 0, "Output in nanoseconds" },
53 { "count", 'C', NULL, 0, "Show event counts with timing" },
54 { "verbose", 'v', NULL, 0, "Verbose debug output" },
55 { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
56 {},
57 };
58
parse_arg(int key,char * arg,struct argp_state * state)59 static error_t parse_arg(int key, char *arg, struct argp_state *state)
60 {
61 static int pos_args;
62
63 switch (key) {
64 case 'h':
65 argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
66 break;
67 case 'v':
68 env.verbose = true;
69 break;
70 case 'd':
71 env.distributed = true;
72 break;
73 case 'N':
74 env.nanoseconds = true;
75 break;
76 case 'T':
77 env.timestamp = true;
78 break;
79 case 'C':
80 env.count = true;
81 break;
82 case ARGP_KEY_ARG:
83 errno = 0;
84 if (pos_args == 0) {
85 env.interval = strtol(arg, NULL, 10);
86 if (errno) {
87 fprintf(stderr, "invalid internal\n");
88 argp_usage(state);
89 }
90 } else if (pos_args == 1) {
91 env.times = strtol(arg, NULL, 10);
92 if (errno) {
93 fprintf(stderr, "invalid times\n");
94 argp_usage(state);
95 }
96 } else {
97 fprintf(stderr,
98 "unrecognized positional argument: %s\n", arg);
99 argp_usage(state);
100 }
101 pos_args++;
102 break;
103 default:
104 return ARGP_ERR_UNKNOWN;
105 }
106 return 0;
107 }
108
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)109 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
110 {
111 if (level == LIBBPF_DEBUG && !env.verbose)
112 return 0;
113 return vfprintf(stderr, format, args);
114 }
115
sig_handler(int sig)116 static void sig_handler(int sig)
117 {
118 exiting = true;
119 }
120
121 enum {
122 HI_SOFTIRQ = 0,
123 TIMER_SOFTIRQ = 1,
124 NET_TX_SOFTIRQ = 2,
125 NET_RX_SOFTIRQ = 3,
126 BLOCK_SOFTIRQ = 4,
127 IRQ_POLL_SOFTIRQ = 5,
128 TASKLET_SOFTIRQ = 6,
129 SCHED_SOFTIRQ = 7,
130 HRTIMER_SOFTIRQ = 8,
131 RCU_SOFTIRQ = 9,
132 NR_SOFTIRQS = 10,
133 };
134
135 static char *vec_names[] = {
136 [HI_SOFTIRQ] = "hi",
137 [TIMER_SOFTIRQ] = "timer",
138 [NET_TX_SOFTIRQ] = "net_tx",
139 [NET_RX_SOFTIRQ] = "net_rx",
140 [BLOCK_SOFTIRQ] = "block",
141 [IRQ_POLL_SOFTIRQ] = "irq_poll",
142 [TASKLET_SOFTIRQ] = "tasklet",
143 [SCHED_SOFTIRQ] = "sched",
144 [HRTIMER_SOFTIRQ] = "hrtimer",
145 [RCU_SOFTIRQ] = "rcu",
146 };
147
print_count(struct softirqs_bpf__bss * bss)148 static int print_count(struct softirqs_bpf__bss *bss)
149 {
150 const char *units = env.nanoseconds ? "nsecs" : "usecs";
151 __u64 count, time;
152 __u32 vec;
153
154 printf("%-16s %-6s%-5s %-11s\n", "SOFTIRQ", "TOTAL_",
155 units, env.count?"TOTAL_count":"");
156
157 for (vec = 0; vec < NR_SOFTIRQS; vec++) {
158 time = __atomic_exchange_n(&bss->time[vec], 0,
159 __ATOMIC_RELAXED);
160 count = __atomic_exchange_n(&bss->counts[vec], 0,
161 __ATOMIC_RELAXED);
162 if (count > 0) {
163 printf("%-16s %11llu", vec_names[vec], time);
164 if (env.count) {
165 printf(" %11llu", count);
166 }
167 printf("\n");
168 }
169 }
170
171 return 0;
172 }
173
174 static struct hist zero;
175
print_hist(struct softirqs_bpf__bss * bss)176 static int print_hist(struct softirqs_bpf__bss *bss)
177 {
178 const char *units = env.nanoseconds ? "nsecs" : "usecs";
179 __u32 vec;
180
181 for (vec = 0; vec < NR_SOFTIRQS; vec++) {
182 struct hist hist = bss->hists[vec];
183
184 bss->hists[vec] = zero;
185 if (!memcmp(&zero, &hist, sizeof(hist)))
186 continue;
187 printf("softirq = %s\n", vec_names[vec]);
188 print_log2_hist(hist.slots, MAX_SLOTS, units);
189 printf("\n");
190 }
191
192 return 0;
193 }
194
main(int argc,char ** argv)195 int main(int argc, char **argv)
196 {
197 static const struct argp argp = {
198 .options = opts,
199 .parser = parse_arg,
200 .doc = argp_program_doc,
201 };
202 struct softirqs_bpf *obj;
203 struct tm *tm;
204 char ts[32];
205 time_t t;
206 int err;
207
208 err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
209 if (err)
210 return err;
211
212 libbpf_set_print(libbpf_print_fn);
213
214 obj = softirqs_bpf__open();
215 if (!obj) {
216 fprintf(stderr, "failed to open BPF object\n");
217 return 1;
218 }
219
220 if (probe_tp_btf("softirq_entry")) {
221 bpf_program__set_autoload(obj->progs.softirq_entry, false);
222 bpf_program__set_autoload(obj->progs.softirq_exit, false);
223 } else {
224 bpf_program__set_autoload(obj->progs.softirq_entry_btf, false);
225 bpf_program__set_autoload(obj->progs.softirq_exit_btf, false);
226 }
227
228 /* initialize global data (filtering options) */
229 obj->rodata->targ_dist = env.distributed;
230 obj->rodata->targ_ns = env.nanoseconds;
231
232 err = softirqs_bpf__load(obj);
233 if (err) {
234 fprintf(stderr, "failed to load BPF object: %d\n", err);
235 goto cleanup;
236 }
237
238 if (!obj->bss) {
239 fprintf(stderr, "Memory-mapping BPF maps is supported starting from Linux 5.7, please upgrade.\n");
240 goto cleanup;
241 }
242
243 err = softirqs_bpf__attach(obj);
244 if (err) {
245 fprintf(stderr, "failed to attach BPF programs\n");
246 goto cleanup;
247 }
248
249 signal(SIGINT, sig_handler);
250
251 printf("Tracing soft irq event time... Hit Ctrl-C to end.\n");
252
253 /* main: poll */
254 while (1) {
255 sleep(env.interval);
256 printf("\n");
257
258 if (env.timestamp) {
259 time(&t);
260 tm = localtime(&t);
261 strftime(ts, sizeof(ts), "%H:%M:%S", tm);
262 printf("%-8s\n", ts);
263 }
264
265 if (!env.distributed)
266 err = print_count(obj->bss);
267 else
268 err = print_hist(obj->bss);
269 if (err)
270 break;
271
272 if (exiting || --env.times == 0)
273 break;
274 }
275
276 cleanup:
277 softirqs_bpf__destroy(obj);
278
279 return err != 0;
280 }
281