xref: /aosp_15_r20/external/bcc/libbpf-tools/softirqs.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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