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