• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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