Skip to content

Commit

Permalink
libbpf-tools: add CO-RE filelife
Browse files Browse the repository at this point in the history
Signed-off-by: Wenbo Zhang <[email protected]>
  • Loading branch information
ethercflow authored and yonghong-song committed Mar 28, 2020
1 parent 9adda70 commit 85f5e7d
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 1 deletion.
1 change: 1 addition & 0 deletions libbpf-tools/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/.output
/drsnoop
/filelife
/opensnoop
/runqslower
/xfsslower
2 changes: 1 addition & 1 deletion libbpf-tools/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ INCLUDES := -I$(OUTPUT)
CFLAGS := -g -O2 -Wall
ARCH := $(shell uname -m | sed 's/x86_64/x86/')

APPS = drsnoop opensnoop runqslower xfsslower
APPS = drsnoop filelife opensnoop runqslower xfsslower

.PHONY: all
all: $(APPS)
Expand Down
82 changes: 82 additions & 0 deletions libbpf-tools/filelife.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2020 Wenbo Zhang
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
#include "filelife.h"

const volatile pid_t targ_tgid = 0;

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

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

static __always_inline int
probe_create(struct inode *dir, struct dentry *dentry)
{
u64 id = bpf_get_current_pid_tgid();
u32 tgid = id >> 32;
u64 ts;

if (targ_tgid && targ_tgid != tgid)
return 0;

ts = bpf_ktime_get_ns();
bpf_map_update_elem(&start, &dentry, &ts, 0);
return 0;
}

SEC("kprobe/vfs_create")
int BPF_KPROBE(kprobe__vfs_create, struct inode *dir, struct dentry *dentry)
{
return probe_create(dir, dentry);
}

SEC("kprobe/security_inode_create")
int BPF_KPROBE(kprobe__security_inode_create, struct inode *dir,
struct dentry *dentry)
{
return probe_create(dir, dentry);
}

SEC("kprobe/vfs_unlink")
int BPF_KPROBE(kprobe__vfs_unlink, struct inode *dir, struct dentry *dentry)
{
u64 id = bpf_get_current_pid_tgid();
struct event event = {};
const u8 *qs_name_ptr;
u32 tgid = id >> 32;
u64 *tsp, delta_ns;
u32 qs_len;

tsp = bpf_map_lookup_elem(&start, &dentry);
if (!tsp)
return 0; // missed entry

delta_ns = bpf_ktime_get_ns() - *tsp;
bpf_map_delete_elem(&start, &dentry);

qs_name_ptr = BPF_CORE_READ(dentry, d_name.name);
qs_len = BPF_CORE_READ(dentry, d_name.len);
bpf_probe_read_str(&event.file, sizeof(event.file), qs_name_ptr);
bpf_get_current_comm(&event.task, sizeof(event.task));
event.delta_ns = delta_ns;
event.tgid = tgid;

/* output */
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU,
&event, sizeof(event));
return 0;
}

char LICENSE[] SEC("license") = "GPL";
172 changes: 172 additions & 0 deletions libbpf-tools/filelife.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
// Copyright (c) 2020 Wenbo Zhang
//
// Based on filelife(8) from BCC by Brendan Gregg & Allan McAleavy.
// 20-Mar-2020 Wenbo Zhang Created this.
#include <argp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <unistd.h>
#include <time.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "filelife.h"
#include "filelife.skel.h"

#define PERF_BUFFER_PAGES 16
#define PERF_POLL_TIMEOUT_MS 100

static struct env {
pid_t pid;
bool verbose;
} env = { };

const char *argp_program_version = "filelife 0.1";
const char *argp_program_bug_address = "<[email protected]>";
const char argp_program_doc[] =
"Trace the lifespan of short-lived files.\n"
"\n"
"USAGE: filelife [-p PID]\n"
"\n"
"EXAMPLES:\n"
" filelife # trace all events\n"
" filelife -p 123 # trace pid 123\n";

static const struct argp_option opts[] = {
{ "pid", 'p', "PID", 0, "Process PID to trace" },
{ "verbose", 'v', NULL, 0, "Verbose debug output" },
{},
};

static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
int pid;

switch (key) {
case 'v':
env.verbose = true;
break;
case 'p':
errno = 0;
pid = strtol(arg, NULL, 10);
if (errno || pid <= 0) {
fprintf(stderr, "invalid PID: %s\n", arg);
argp_usage(state);
}
env.pid = pid;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}

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 int bump_memlock_rlimit(void)
{
struct rlimit rlim_new = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};

return setrlimit(RLIMIT_MEMLOCK, &rlim_new);
}

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);
printf("%-8s %-6d %-16s %-7.2f %s\n",
ts, e->tgid, e->task, (double)e->delta_ns / 1000000000,
e->file);
}

void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
{
fprintf(stderr, "lost %llu events on CPU #%d\n", lost_cnt, cpu);
}

int main(int argc, char **argv)
{
static const struct argp argp = {
.options = opts,
.parser = parse_arg,
.doc = argp_program_doc,
};
struct perf_buffer_opts pb_opts;
struct perf_buffer *pb = NULL;
struct filelife_bpf *obj;
int err;

err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
if (err)
return err;

libbpf_set_print(libbpf_print_fn);

err = bump_memlock_rlimit();
if (err) {
fprintf(stderr, "failed to increase rlimit: %d\n", err);
return 1;
}

obj = filelife_bpf__open();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF ojbect\n");
return 1;
}

/* initialize global data (filtering options) */
obj->rodata->targ_tgid = env.pid;

err = filelife_bpf__load(obj);
if (err) {
fprintf(stderr, "failed to load BPF object: %d\n", err);
goto cleanup;
}

err = filelife_bpf__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
goto cleanup;
}

printf("Tracing the lifespan of short-lived files ... Hit Ctrl-C to end.\n");
printf("%-8s %-6s %-16s %-7s %s\n", "TIME", "PID", "COMM", "AGE(s)", "FILE");

pb_opts.sample_cb = handle_event;
pb_opts.lost_cb = handle_lost_events;
pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES,
&pb_opts);
err = libbpf_get_error(pb);
if (err) {
pb = NULL;
fprintf(stderr, "failed to open perf buffer: %d\n", err);
goto cleanup;
}

while ((err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS)) >= 0)
;
fprintf(stderr, "error polling perf buffer: %d\n", err);

cleanup:
perf_buffer__free(pb);
filelife_bpf__destroy(obj);

return err != 0;
}
15 changes: 15 additions & 0 deletions libbpf-tools/filelife.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __FILELIFE_H
#define __FILELIFE_H

#define DNAME_INLINE_LEN 32
#define TASK_COMM_LEN 16

struct event {
__u64 delta_ns;
pid_t tgid;
char file[DNAME_INLINE_LEN];
char task[TASK_COMM_LEN];
};

#endif /* __FILELIFE_H */

0 comments on commit 85f5e7d

Please sign in to comment.