Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,22 @@ TIME EVENT COMM PID PPID FILENAME/EXIT CODE
...
```

## bootstrap_legacy

`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.

```shell
$ cd examples/c
$ make bootstrap_legacy
$ sudo ./bootstrap_legacy
TIME EVENT COMM PID PPID FILENAME/EXIT CODE
10:26:32 EXEC sh 4101543 4054526 /bin/sh
10:26:32 EXEC python 4101545 4101543 /usr/bin/python
10:26:32 EXIT python 4101545 4101543 [0] (9ms)
10:26:32 EXIT sh 4101543 4054526 [0] (10ms)
...
```

## uprobe

`uprobe` is an example of dealing with user-space entry and exit (return) probes,
Expand Down
2 changes: 1 addition & 1 deletion examples/c/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBB
CFLAGS := -g -Wall
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)

APPS = minimal minimal_legacy minimal_ns bootstrap uprobe kprobe fentry \
APPS = minimal minimal_legacy minimal_ns bootstrap bootstrap_legacy uprobe kprobe fentry \
usdt sockfilter tc ksyscall task_iter lsm

CARGO ?= $(shell which cargo)
Expand Down
103 changes: 103 additions & 0 deletions examples/c/bootstrap_legacy.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2020 Facebook */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include "bootstrap.h"

char LICENSE[] SEC("license") = "Dual BSD/GPL";

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 8192);
__type(key, pid_t);
__type(value, u64);
} exec_start SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(u32));
} pe SEC(".maps");

const volatile unsigned long long min_duration_ns = 0;

SEC("tp/sched/sched_process_exec")
int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
{
struct task_struct *task;
unsigned fname_off;
struct event *e = &(struct event){};
pid_t pid;
u64 ts;

/* remember time exec() was executed for this PID */
pid = bpf_get_current_pid_tgid() >> 32;
ts = bpf_ktime_get_ns();
bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY);

/* don't emit exec events when minimum duration is specified */
if (min_duration_ns)
return 0;

/* fill out the sample with data */
task = (struct task_struct *)bpf_get_current_task();

e->exit_event = false;
e->pid = pid;
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
bpf_get_current_comm(&e->comm, sizeof(e->comm));

fname_off = ctx->__data_loc_filename & 0xFFFF;
bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + fname_off);

/* use perf event output to send data to user-space for post-processing */
bpf_perf_event_output(ctx, &pe, BPF_F_CURRENT_CPU, e, sizeof(*e));
return 0;
}

SEC("tp/sched/sched_process_exit")
int handle_exit(struct trace_event_raw_sched_process_template *ctx)
{
struct task_struct *task;
struct event *e = &(struct event){};
pid_t pid, tid;
u64 id, ts, *start_ts, duration_ns = 0;

/* get PID and TID of exiting thread/process */
id = bpf_get_current_pid_tgid();
pid = id >> 32;
tid = (u32)id;

/* ignore thread exits */
if (pid != tid)
return 0;

/* if we recorded start of the process, calculate lifetime duration */
start_ts = bpf_map_lookup_elem(&exec_start, &pid);
if (start_ts)
duration_ns = bpf_ktime_get_ns() - *start_ts;
else if (min_duration_ns)
return 0;
bpf_map_delete_elem(&exec_start, &pid);

/* if process didn't live long enough, return early */
if (min_duration_ns && duration_ns < min_duration_ns)
return 0;

/* fill out the sample with data */
task = (struct task_struct *)bpf_get_current_task();

e->exit_event = true;
e->duration_ns = duration_ns;
e->pid = pid;
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff;
bpf_get_current_comm(&e->comm, sizeof(e->comm));

/* use perf event output to send data to user-space for post-processing */
bpf_perf_event_output(ctx, &pe, BPF_F_CURRENT_CPU, e, sizeof(*e));

return 0;
}
171 changes: 171 additions & 0 deletions examples/c/bootstrap_legacy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2020 Facebook */
#include <argp.h>
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>
#include "bootstrap.h"
#include "bootstrap_legacy.skel.h"

static struct env {
bool verbose;
long min_duration_ms;
} env;

const char *argp_program_version = "bootstrap 0.0";
const char *argp_program_bug_address = "<[email protected]>";
const char argp_program_doc[] = "BPF bootstrap demo application.\n"
"\n"
"It traces process start and exits and shows associated \n"
"information (filename, process duration, PID and PPID, etc).\n"
"\n"
"USAGE: ./bootstrap [-d <min-duration-ms>] [-v]\n";

static const struct argp_option opts[] = {
{ "verbose", 'v', NULL, 0, "Verbose debug output" },
{ "duration", 'd', "DURATION-MS", 0, "Minimum process duration (ms) to report" },
{},
};

static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
switch (key) {
case 'v':
env.verbose = true;
break;
case 'd':
errno = 0;
env.min_duration_ms = strtol(arg, NULL, 10);
if (errno || env.min_duration_ms <= 0) {
fprintf(stderr, "Invalid duration: %s\n", arg);
argp_usage(state);
}
break;
case ARGP_KEY_ARG:
argp_usage(state);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}

static const struct argp argp = {
.options = opts,
.parser = parse_arg,
.doc = argp_program_doc,
};

static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
if (level == LIBBPF_DEBUG && !env.verbose)
return 0;
return vfprintf(stderr, format, args);
}

static volatile bool exiting = false;

static void sig_handler(int sig)
{
exiting = true;
}

static void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
{
const struct event *e = data;
struct tm *tm;
char ts[32];
time_t t;

time(&t);
tm = localtime(&t);
strftime(ts, sizeof(ts), "%H:%M:%S", tm);

if (e->exit_event) {
printf("%-8s %-5s %-16s %-7d %-7d [%u]", ts, "EXIT", e->comm, e->pid, e->ppid,
e->exit_code);
if (e->duration_ns)
printf(" (%llums)", e->duration_ns / 1000000);
printf("\n");
} else {
printf("%-8s %-5s %-16s %-7d %-7d %s\n", ts, "EXEC", e->comm, e->pid, e->ppid,
e->filename);
}
}

int main(int argc, char **argv)
{
// struct ring_buffer *rb = NULL;
struct perf_buffer *pe = NULL;
struct bootstrap_legacy_bpf *skel;
int err;

/* Parse command line arguments */
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
if (err)
return err;

/* Set up libbpf errors and debug info callback */
libbpf_set_print(libbpf_print_fn);

/* Cleaner handling of Ctrl-C */
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);

/* Load and verify BPF application */
skel = bootstrap_legacy_bpf__open();
if (!skel) {
fprintf(stderr, "Failed to open and load BPF skeleton\n");
return 1;
}

/* Parameterize BPF code with minimum duration parameter */
skel->rodata->min_duration_ns = env.min_duration_ms * 1000000ULL;

/* Load & verify BPF programs */
err = bootstrap_legacy_bpf__load(skel);
if (err) {
fprintf(stderr, "Failed to load and verify BPF skeleton\n");
goto cleanup;
}

/* Attach tracepoints */
err = bootstrap_legacy_bpf__attach(skel);
if (err) {
fprintf(stderr, "Failed to attach BPF skeleton\n");
goto cleanup;
}

/* Set up perf buffer polling */
pe = perf_buffer__new(bpf_map__fd(skel->maps.pe), 8, handle_event, NULL, NULL, NULL);
if (!pe) {
err = -1;
fprintf(stderr, "Failed to create perf event buffer\n");
goto cleanup;
}

/* Process events */
printf("%-8s %-5s %-16s %-7s %-7s %s\n", "TIME", "EVENT", "COMM", "PID", "PPID",
"FILENAME/EXIT CODE");
while (!exiting) {
err = perf_buffer__poll(pe, 100 /* timeout, ms */);
/* Ctrl-C will cause -EINTR */
if (err == -EINTR) {
err = 0;
break;
}
if (err < 0) {
printf("Error polling perf buffer: %d\n", err);
break;
}
}

cleanup:
/* Clean up */
perf_buffer__free(pe);
bootstrap_legacy_bpf__destroy(skel);

return err < 0 ? -err : 0;
}