Skip to content

Commit bf4668d

Browse files
ethercflowyonghong-song
authored andcommitted
libbpf-tools: add softirqs
Signed-off-by: Wenbo Zhang <[email protected]>
1 parent 3953c60 commit bf4668d

File tree

5 files changed

+335
-0
lines changed

5 files changed

+335
-0
lines changed

libbpf-tools/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
/opensnoop
1313
/readahead
1414
/runqslower
15+
/softirqs
1516
/syscount
1617
/tcpconnect
1718
/tcpconnlat

libbpf-tools/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ APPS = \
2323
opensnoop \
2424
readahead \
2525
runqslower \
26+
softirqs \
2627
syscount \
2728
tcpconnect \
2829
tcpconnlat \

libbpf-tools/softirqs.bpf.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// Copyright (c) 2020 Wenbo Zhang
3+
#include "vmlinux.h"
4+
#include <bpf/bpf_helpers.h>
5+
#include <bpf/bpf_tracing.h>
6+
#include "softirqs.h"
7+
#include "bits.bpf.h"
8+
#include "maps.bpf.h"
9+
10+
const volatile bool targ_dist = false;
11+
const volatile bool targ_ns = false;
12+
13+
struct {
14+
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
15+
__uint(max_entries, 1);
16+
__type(key, u32);
17+
__type(value, u64);
18+
} start SEC(".maps");
19+
20+
__u64 counts[NR_SOFTIRQS];
21+
struct hist hists[NR_SOFTIRQS];
22+
23+
SEC("tp_btf/softirq_entry")
24+
int BPF_PROG(softirq_entry, unsigned int vec_nr)
25+
{
26+
u64 ts = bpf_ktime_get_ns();
27+
u32 key = 0;
28+
29+
bpf_map_update_elem(&start, &key, &ts, 0);
30+
return 0;
31+
}
32+
33+
SEC("tp_btf/softirq_exit")
34+
int BPF_PROG(softirq_exit, unsigned int vec_nr)
35+
{
36+
u32 key = 0;
37+
s64 delta;
38+
u64 *tsp;
39+
40+
if (vec_nr >= NR_SOFTIRQS)
41+
return 0;
42+
tsp = bpf_map_lookup_elem(&start, &key);
43+
if (!tsp || !*tsp)
44+
return 0;
45+
delta = bpf_ktime_get_ns() - *tsp;
46+
if (delta < 0)
47+
return 0;
48+
if (!targ_ns)
49+
delta /= 1000U;
50+
51+
if (!targ_dist) {
52+
__sync_fetch_and_add(&counts[vec_nr], delta);
53+
} else {
54+
struct hist *hist;
55+
u64 slot;
56+
57+
hist = &hists[vec_nr];
58+
slot = log2(delta);
59+
if (slot >= MAX_SLOTS)
60+
slot = MAX_SLOTS - 1;
61+
__sync_fetch_and_add(&hist->slots[slot], 1);
62+
}
63+
64+
return 0;
65+
}
66+
67+
char LICENSE[] SEC("license") = "GPL";

libbpf-tools/softirqs.c

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

libbpf-tools/softirqs.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2+
#ifndef __SOFTIRQS_H
3+
#define __SOFTIRQS_H
4+
5+
#define MAX_SLOTS 20
6+
7+
struct hist {
8+
__u32 slots[MAX_SLOTS];
9+
};
10+
11+
#endif /* __SOFTIRQS_H */

0 commit comments

Comments
 (0)