Skip to content

Commit f88fabb

Browse files
committed
examples/c: add bootstrap_legacy for compatibility with older kernels
Signed-off-by: Runlong Lin <[email protected]>
1 parent 37c1135 commit f88fabb

File tree

4 files changed

+291
-1
lines changed

4 files changed

+291
-1
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,22 @@ TIME EVENT COMM PID PPID FILENAME/EXIT CODE
108108
...
109109
```
110110

111+
## bootstrap_legacy
112+
113+
`bootstrap_legacy` is a version of `bootstrap` modified to run on older kernels that do not support BPF ring buffer maps (BPF_MAP_TYPE_RINGBUF) introduced in kernel 5.8. `bootstrap_legacy` replaces the ring buffer maps with perf event array (BPF_MAP_TYPE_PERF_EVENT_ARRAY) for compatibility with older kernel versions.
114+
115+
```shell
116+
$ cd examples/c
117+
$ make bootstrap_legacy
118+
$ sudo ./bootstrap_legacy
119+
TIME EVENT COMM PID PPID FILENAME/EXIT CODE
120+
10:26:32 EXEC sh 4101543 4054526 /bin/sh
121+
10:26:32 EXEC python 4101545 4101543 /usr/bin/python
122+
10:26:32 EXIT python 4101545 4101543 [0] (9ms)
123+
10:26:32 EXIT sh 4101543 4054526 [0] (10ms)
124+
...
125+
```
126+
111127
## uprobe
112128

113129
`uprobe` is an example of dealing with user-space entry and exit (return) probes,

examples/c/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBB
2424
CFLAGS := -g -Wall
2525
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)
2626

27-
APPS = minimal minimal_legacy minimal_ns bootstrap uprobe kprobe fentry \
27+
APPS = minimal minimal_legacy minimal_ns bootstrap bootstrap_legacy uprobe kprobe fentry \
2828
usdt sockfilter tc ksyscall task_iter lsm
2929

3030
CARGO ?= $(shell which cargo)

examples/c/bootstrap_legacy.bpf.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2+
/* Copyright (c) 2020 Facebook */
3+
#include "vmlinux.h"
4+
#include <bpf/bpf_helpers.h>
5+
#include <bpf/bpf_tracing.h>
6+
#include <bpf/bpf_core_read.h>
7+
#include "bootstrap.h"
8+
9+
char LICENSE[] SEC("license") = "Dual BSD/GPL";
10+
11+
struct {
12+
__uint(type, BPF_MAP_TYPE_HASH);
13+
__uint(max_entries, 8192);
14+
__type(key, pid_t);
15+
__type(value, u64);
16+
} exec_start SEC(".maps");
17+
18+
struct {
19+
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
20+
__uint(key_size, sizeof(u32));
21+
__uint(value_size, sizeof(u32));
22+
} pe SEC(".maps");
23+
24+
const volatile unsigned long long min_duration_ns = 0;
25+
26+
SEC("tp/sched/sched_process_exec")
27+
int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
28+
{
29+
struct task_struct *task;
30+
unsigned fname_off;
31+
struct event *e = &(struct event){};
32+
pid_t pid;
33+
u64 ts;
34+
35+
/* remember time exec() was executed for this PID */
36+
pid = bpf_get_current_pid_tgid() >> 32;
37+
ts = bpf_ktime_get_ns();
38+
bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY);
39+
40+
/* don't emit exec events when minimum duration is specified */
41+
if (min_duration_ns)
42+
return 0;
43+
44+
/* fill out the sample with data */
45+
task = (struct task_struct *)bpf_get_current_task();
46+
47+
e->exit_event = false;
48+
e->pid = pid;
49+
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
50+
bpf_get_current_comm(&e->comm, sizeof(e->comm));
51+
52+
fname_off = ctx->__data_loc_filename & 0xFFFF;
53+
bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + fname_off);
54+
55+
/* use perf event output to send data to user-space for post-processing */
56+
bpf_perf_event_output(ctx, &pe, BPF_F_CURRENT_CPU, e, sizeof(*e));
57+
return 0;
58+
}
59+
60+
SEC("tp/sched/sched_process_exit")
61+
int handle_exit(struct trace_event_raw_sched_process_template *ctx)
62+
{
63+
struct task_struct *task;
64+
struct event *e = &(struct event){};
65+
pid_t pid, tid;
66+
u64 id, ts, *start_ts, duration_ns = 0;
67+
68+
/* get PID and TID of exiting thread/process */
69+
id = bpf_get_current_pid_tgid();
70+
pid = id >> 32;
71+
tid = (u32)id;
72+
73+
/* ignore thread exits */
74+
if (pid != tid)
75+
return 0;
76+
77+
/* if we recorded start of the process, calculate lifetime duration */
78+
start_ts = bpf_map_lookup_elem(&exec_start, &pid);
79+
if (start_ts)
80+
duration_ns = bpf_ktime_get_ns() - *start_ts;
81+
else if (min_duration_ns)
82+
return 0;
83+
bpf_map_delete_elem(&exec_start, &pid);
84+
85+
/* if process didn't live long enough, return early */
86+
if (min_duration_ns && duration_ns < min_duration_ns)
87+
return 0;
88+
89+
/* fill out the sample with data */
90+
task = (struct task_struct *)bpf_get_current_task();
91+
92+
e->exit_event = true;
93+
e->duration_ns = duration_ns;
94+
e->pid = pid;
95+
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
96+
e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff;
97+
bpf_get_current_comm(&e->comm, sizeof(e->comm));
98+
99+
/* use perf event output to send data to user-space for post-processing */
100+
bpf_perf_event_output(ctx, &pe, BPF_F_CURRENT_CPU, e, sizeof(*e));
101+
102+
return 0;
103+
}

examples/c/bootstrap_legacy.c

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2+
/* Copyright (c) 2020 Facebook */
3+
#include <argp.h>
4+
#include <signal.h>
5+
#include <stdio.h>
6+
#include <time.h>
7+
#include <sys/resource.h>
8+
#include <bpf/libbpf.h>
9+
#include "bootstrap.h"
10+
#include "bootstrap_legacy.skel.h"
11+
12+
static struct env {
13+
bool verbose;
14+
long min_duration_ms;
15+
} env;
16+
17+
const char *argp_program_version = "bootstrap 0.0";
18+
const char *argp_program_bug_address = "<[email protected]>";
19+
const char argp_program_doc[] = "BPF bootstrap demo application.\n"
20+
"\n"
21+
"It traces process start and exits and shows associated \n"
22+
"information (filename, process duration, PID and PPID, etc).\n"
23+
"\n"
24+
"USAGE: ./bootstrap [-d <min-duration-ms>] [-v]\n";
25+
26+
static const struct argp_option opts[] = {
27+
{ "verbose", 'v', NULL, 0, "Verbose debug output" },
28+
{ "duration", 'd', "DURATION-MS", 0, "Minimum process duration (ms) to report" },
29+
{},
30+
};
31+
32+
static error_t parse_arg(int key, char *arg, struct argp_state *state)
33+
{
34+
switch (key) {
35+
case 'v':
36+
env.verbose = true;
37+
break;
38+
case 'd':
39+
errno = 0;
40+
env.min_duration_ms = strtol(arg, NULL, 10);
41+
if (errno || env.min_duration_ms <= 0) {
42+
fprintf(stderr, "Invalid duration: %s\n", arg);
43+
argp_usage(state);
44+
}
45+
break;
46+
case ARGP_KEY_ARG:
47+
argp_usage(state);
48+
break;
49+
default:
50+
return ARGP_ERR_UNKNOWN;
51+
}
52+
return 0;
53+
}
54+
55+
static const struct argp argp = {
56+
.options = opts,
57+
.parser = parse_arg,
58+
.doc = argp_program_doc,
59+
};
60+
61+
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
62+
{
63+
if (level == LIBBPF_DEBUG && !env.verbose)
64+
return 0;
65+
return vfprintf(stderr, format, args);
66+
}
67+
68+
static volatile bool exiting = false;
69+
70+
static void sig_handler(int sig)
71+
{
72+
exiting = true;
73+
}
74+
75+
static void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
76+
{
77+
const struct event *e = data;
78+
struct tm *tm;
79+
char ts[32];
80+
time_t t;
81+
82+
time(&t);
83+
tm = localtime(&t);
84+
strftime(ts, sizeof(ts), "%H:%M:%S", tm);
85+
86+
if (e->exit_event) {
87+
printf("%-8s %-5s %-16s %-7d %-7d [%u]", ts, "EXIT", e->comm, e->pid, e->ppid,
88+
e->exit_code);
89+
if (e->duration_ns)
90+
printf(" (%llums)", e->duration_ns / 1000000);
91+
printf("\n");
92+
} else {
93+
printf("%-8s %-5s %-16s %-7d %-7d %s\n", ts, "EXEC", e->comm, e->pid, e->ppid,
94+
e->filename);
95+
}
96+
}
97+
98+
int main(int argc, char **argv)
99+
{
100+
// struct ring_buffer *rb = NULL;
101+
struct perf_buffer *pe = NULL;
102+
struct bootstrap_legacy_bpf *skel;
103+
int err;
104+
105+
/* Parse command line arguments */
106+
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
107+
if (err)
108+
return err;
109+
110+
/* Set up libbpf errors and debug info callback */
111+
libbpf_set_print(libbpf_print_fn);
112+
113+
/* Cleaner handling of Ctrl-C */
114+
signal(SIGINT, sig_handler);
115+
signal(SIGTERM, sig_handler);
116+
117+
/* Load and verify BPF application */
118+
skel = bootstrap_legacy_bpf__open();
119+
if (!skel) {
120+
fprintf(stderr, "Failed to open and load BPF skeleton\n");
121+
return 1;
122+
}
123+
124+
/* Parameterize BPF code with minimum duration parameter */
125+
skel->rodata->min_duration_ns = env.min_duration_ms * 1000000ULL;
126+
127+
/* Load & verify BPF programs */
128+
err = bootstrap_legacy_bpf__load(skel);
129+
if (err) {
130+
fprintf(stderr, "Failed to load and verify BPF skeleton\n");
131+
goto cleanup;
132+
}
133+
134+
/* Attach tracepoints */
135+
err = bootstrap_legacy_bpf__attach(skel);
136+
if (err) {
137+
fprintf(stderr, "Failed to attach BPF skeleton\n");
138+
goto cleanup;
139+
}
140+
141+
/* Set up perf buffer polling */
142+
pe = perf_buffer__new(bpf_map__fd(skel->maps.pe), 8, handle_event, NULL, NULL, NULL);
143+
if (!pe) {
144+
err = -1;
145+
fprintf(stderr, "Failed to create perf event buffer\n");
146+
goto cleanup;
147+
}
148+
149+
/* Process events */
150+
printf("%-8s %-5s %-16s %-7s %-7s %s\n", "TIME", "EVENT", "COMM", "PID", "PPID",
151+
"FILENAME/EXIT CODE");
152+
while (!exiting) {
153+
err = perf_buffer__poll(pe, 100 /* timeout, ms */);
154+
/* Ctrl-C will cause -EINTR */
155+
if (err == -EINTR) {
156+
err = 0;
157+
break;
158+
}
159+
if (err < 0) {
160+
printf("Error polling perf buffer: %d\n", err);
161+
break;
162+
}
163+
}
164+
165+
cleanup:
166+
/* Clean up */
167+
perf_buffer__free(pe);
168+
bootstrap_legacy_bpf__destroy(skel);
169+
170+
return err < 0 ? -err : 0;
171+
}

0 commit comments

Comments
 (0)