Skip to content

Commit e7b7cb6

Browse files
committed
libbpf-tools/offcputime: Add multi process/thread support
This is a test example. # ./offcputime -p 16,48 Tracing off-CPU time (us) of PID [16, 48]... Hit Ctrl-C to end. bpf_prog_a42aae11c0bc18f2_sched_switch bpf_prog_a42aae11c0bc18f2_sched_switch bpf_trace_run4 __traceiter_sched_switch __schedule schedule worker_thread kthread ret_from_fork ret_from_fork_asm - kworker/2:1 (48) 3353019 bpf_prog_a42aae11c0bc18f2_sched_switch bpf_prog_a42aae11c0bc18f2_sched_switch bpf_trace_run4 __traceiter_sched_switch __schedule schedule rcu_gp_kthread kthread ret_from_fork ret_from_fork_asm - rcu_preempt (16) 1720974
1 parent 9f3d0df commit e7b7cb6

File tree

3 files changed

+136
-31
lines changed

3 files changed

+136
-31
lines changed

libbpf-tools/offcputime.bpf.c

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ const volatile bool kernel_threads_only = false;
1414
const volatile bool user_threads_only = false;
1515
const volatile __u64 max_block_ns = -1;
1616
const volatile __u64 min_block_ns = 1;
17-
const volatile pid_t targ_tgid = -1;
18-
const volatile pid_t targ_pid = -1;
17+
const volatile bool filter_by_tgid = false;
18+
const volatile bool filter_by_pid = false;
1919
const volatile long state = -1;
2020

2121
struct internal_key {
@@ -42,15 +42,30 @@ struct {
4242
__uint(max_entries, MAX_ENTRIES);
4343
} info SEC(".maps");
4444

45+
struct {
46+
__uint(type, BPF_MAP_TYPE_HASH);
47+
__type(key, u32);
48+
__type(value, u8);
49+
__uint(max_entries, MAX_PID_NR);
50+
} tgids SEC(".maps");
51+
52+
struct {
53+
__uint(type, BPF_MAP_TYPE_HASH);
54+
__type(key, u32);
55+
__type(value, u8);
56+
__uint(max_entries, MAX_TID_NR);
57+
} pids SEC(".maps");
58+
4559
static bool allow_record(struct task_struct *t)
4660
{
47-
if (targ_tgid != -1 && targ_tgid != t->tgid)
48-
return false;
49-
if (targ_pid != -1 && targ_pid != t->pid)
61+
u32 tgid = t->tgid;
62+
u32 pid = t->pid;
63+
64+
if (filter_by_tgid && !bpf_map_lookup_elem(&tgids, &tgid))
5065
return false;
51-
if (user_threads_only && t->flags & PF_KTHREAD)
66+
if (filter_by_pid && !bpf_map_lookup_elem(&pids, &pid))
5267
return false;
53-
else if (kernel_threads_only && !(t->flags & PF_KTHREAD))
68+
if (kernel_threads_only && !(t->flags & PF_KTHREAD))
5469
return false;
5570
if (state != -1 && get_task_state(t) != state)
5671
return false;

libbpf-tools/offcputime.c

Lines changed: 112 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
#include "trace_helpers.h"
1717

1818
static struct env {
19-
pid_t pid;
20-
pid_t tid;
19+
pid_t pids[MAX_PID_NR];
20+
pid_t tids[MAX_TID_NR];
2121
bool user_threads_only;
2222
bool kernel_threads_only;
2323
int stack_storage_size;
@@ -28,8 +28,6 @@ static struct env {
2828
int duration;
2929
bool verbose;
3030
} env = {
31-
.pid = -1,
32-
.tid = -1,
3331
.stack_storage_size = 1024,
3432
.perf_max_stack_depth = 127,
3533
.min_block_time = 1,
@@ -52,8 +50,8 @@ const char argp_program_doc[] =
5250
" offcputime 5 # trace for 5 seconds only\n"
5351
" offcputime -m 1000 # trace only events that last more than 1000 usec\n"
5452
" offcputime -M 10000 # trace only events that last less than 10000 usec\n"
55-
" offcputime -p 185 # only trace threads for PID 185\n"
56-
" offcputime -t 188 # only trace thread 188\n"
53+
" offcputime -p 185,175,165 # only trace threads for PID 185,175,165\n"
54+
" offcputime -t 188,120,134 # only trace threads 188,120,134\n"
5755
" offcputime -u # only trace user threads (no kernel)\n"
5856
" offcputime -k # only trace kernel threads (no user)\n";
5957

@@ -62,8 +60,8 @@ const char argp_program_doc[] =
6260
#define OPT_STATE 3 /* --state */
6361

6462
static const struct argp_option opts[] = {
65-
{ "pid", 'p', "PID", 0, "Trace this PID only", 0 },
66-
{ "tid", 't', "TID", 0, "Trace this TID only", 0 },
63+
{ "pid", 'p', "PID", 0, "Trace these PIDs only, comma-separated list", 0 },
64+
{ "tid", 't', "TID", 0, "Trace these TIDs only, comma-separated list", 0 },
6765
{ "user-threads-only", 'u', NULL, 0,
6866
"User threads only (no kernel threads)", 0 },
6967
{ "kernel-threads-only", 'k', NULL, 0,
@@ -82,9 +80,31 @@ static const struct argp_option opts[] = {
8280
{},
8381
};
8482

83+
static int split_pidstr(char *s, char* delim, int max_split, pid_t *pids)
84+
{
85+
char *pid;
86+
int nr = 0;
87+
88+
errno = 0;
89+
pid = strtok(s, delim);
90+
while (pid) {
91+
if (nr >= max_split)
92+
return -ENOBUFS;
93+
94+
pids[nr++] = strtol(pid, NULL, 10);
95+
if (errno)
96+
return -errno;
97+
98+
pid = strtok(NULL, delim);
99+
}
100+
101+
return 0;
102+
}
103+
85104
static error_t parse_arg(int key, char *arg, struct argp_state *state)
86105
{
87106
static int pos_args;
107+
int ret;
88108

89109
switch (key) {
90110
case 'h':
@@ -94,18 +114,26 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
94114
env.verbose = true;
95115
break;
96116
case 'p':
97-
errno = 0;
98-
env.pid = strtol(arg, NULL, 10);
99-
if (errno) {
100-
fprintf(stderr, "invalid PID: %s\n", arg);
117+
ret = split_pidstr(strdup(arg), ",", MAX_PID_NR, env.pids);
118+
if (ret) {
119+
if (ret == -ENOBUFS)
120+
fprintf(stderr, "the number of pid is too big, please "
121+
"increase MAX_PID_NR's value and recompile\n");
122+
else
123+
fprintf(stderr, "invalid PID: %s\n", arg);
124+
101125
argp_usage(state);
102126
}
103127
break;
104128
case 't':
105-
errno = 0;
106-
env.tid = strtol(arg, NULL, 10);
107-
if (errno || env.tid <= 0) {
108-
fprintf(stderr, "Invalid TID: %s\n", arg);
129+
ret = split_pidstr(strdup(arg), ",", MAX_TID_NR, env.tids);
130+
if (ret) {
131+
if (ret == -ENOBUFS)
132+
fprintf(stderr, "the number of tid is too big, please "
133+
"increase MAX_TID_NR's value and recompile\n");
134+
else
135+
fprintf(stderr, "invalid TID: %s\n", arg);
136+
109137
argp_usage(state);
110138
}
111139
break;
@@ -281,6 +309,41 @@ static void print_map(struct ksyms *ksyms, struct syms_cache *syms_cache,
281309
free(ip);
282310
}
283311

312+
static bool print_header_threads()
313+
{
314+
int i;
315+
bool printed = false;
316+
317+
if (env.pids[0]) {
318+
printf(" PID [");
319+
for (i = 0; i < MAX_PID_NR && env.pids[i]; i++)
320+
printf("%d%s", env.pids[i], (i < MAX_PID_NR - 1 && env.pids[i + 1]) ? ", " : "]");
321+
printed = true;
322+
}
323+
324+
if (env.tids[0]) {
325+
printf(" TID [");
326+
for (i = 0; i < MAX_TID_NR && env.tids[i]; i++)
327+
printf("%d%s", env.tids[i], (i < MAX_TID_NR - 1 && env.tids[i + 1]) ? ", " : "]");
328+
printed = true;
329+
}
330+
331+
return printed;
332+
}
333+
334+
static void print_headers()
335+
{
336+
printf("Tracing off-CPU time (us) of");
337+
338+
if (!print_header_threads())
339+
printf(" all threads");
340+
341+
if (env.duration < 99999999)
342+
printf(" for %d secs.\n", env.duration);
343+
else
344+
printf("... Hit Ctrl-C to end.\n");
345+
}
346+
284347
int main(int argc, char **argv)
285348
{
286349
static const struct argp argp = {
@@ -291,7 +354,9 @@ int main(int argc, char **argv)
291354
struct syms_cache *syms_cache = NULL;
292355
struct ksyms *ksyms = NULL;
293356
struct offcputime_bpf *obj;
294-
int err;
357+
int pids_fd, tids_fd;
358+
int err, i;
359+
__u8 val = 0;
295360

296361
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
297362
if (err)
@@ -314,14 +379,18 @@ int main(int argc, char **argv)
314379
}
315380

316381
/* initialize global data (filtering options) */
317-
obj->rodata->targ_tgid = env.pid;
318-
obj->rodata->targ_pid = env.tid;
319382
obj->rodata->user_threads_only = env.user_threads_only;
320383
obj->rodata->kernel_threads_only = env.kernel_threads_only;
321384
obj->rodata->state = env.state;
322385
obj->rodata->min_block_ns = env.min_block_time;
323386
obj->rodata->max_block_ns = env.max_block_time;
324387

388+
/* User space PID and TID correspond to TGID and PID in the kernel, respectively */
389+
if (env.pids[0])
390+
obj->rodata->filter_by_tgid = true;
391+
if (env.tids[0])
392+
obj->rodata->filter_by_pid = true;
393+
325394
bpf_map__set_value_size(obj->maps.stackmap,
326395
env.perf_max_stack_depth * sizeof(unsigned long));
327396
bpf_map__set_max_entries(obj->maps.stackmap, env.stack_storage_size);
@@ -331,6 +400,28 @@ int main(int argc, char **argv)
331400
fprintf(stderr, "failed to load BPF programs\n");
332401
goto cleanup;
333402
}
403+
404+
if (env.pids[0]) {
405+
/* User pids_fd points to the tgids map in the BPF program */
406+
pids_fd = bpf_map__fd(obj->maps.tgids);
407+
for (i = 0; i < MAX_PID_NR && env.pids[i]; i++) {
408+
if (bpf_map_update_elem(pids_fd, &(env.pids[i]), &val, BPF_ANY) != 0) {
409+
fprintf(stderr, "failed to init pids map: %s\n", strerror(errno));
410+
goto cleanup;
411+
}
412+
}
413+
}
414+
if (env.tids[0]) {
415+
/* User tids_fd points to the pids map in the BPF program */
416+
tids_fd = bpf_map__fd(obj->maps.pids);
417+
for (i = 0; i < MAX_TID_NR && env.tids[i]; i++) {
418+
if (bpf_map_update_elem(tids_fd, &(env.tids[i]), &val, BPF_ANY) != 0) {
419+
fprintf(stderr, "failed to init tids map: %s\n", strerror(errno));
420+
goto cleanup;
421+
}
422+
}
423+
}
424+
334425
ksyms = ksyms__load();
335426
if (!ksyms) {
336427
fprintf(stderr, "failed to load kallsyms\n");
@@ -349,11 +440,8 @@ int main(int argc, char **argv)
349440

350441
signal(SIGINT, sig_handler);
351442

352-
printf("Tracing off-CPU time (us)");
353-
if (env.duration < 99999999)
354-
printf(" for %d secs.\n", env.duration);
355-
else
356-
printf("... Hit Ctrl-C to end.\n");
443+
print_headers();
444+
357445
/*
358446
* We'll get sleep interrupted when someone presses Ctrl-C (which will
359447
* be "handled" with noop by sig_handler).

libbpf-tools/offcputime.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#define __OFFCPUTIME_H
44

55
#define TASK_COMM_LEN 16
6+
#define MAX_PID_NR 30
7+
#define MAX_TID_NR 30
68

79
struct key_t {
810
__u32 pid;

0 commit comments

Comments
 (0)