Skip to content
Merged
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
1 change: 1 addition & 0 deletions libbpf-tools/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ COMMON_OBJ = \
$(OUTPUT)/uprobe_helpers.o \
$(OUTPUT)/btf_helpers.o \
$(OUTPUT)/compat.o \
$(OUTPUT)/path_helpers.o \
$(if $(ENABLE_MIN_CORE_BTFS),$(OUTPUT)/min_core_btf_tar.o) \
#

Expand Down
101 changes: 69 additions & 32 deletions libbpf-tools/filelife.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,62 @@
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
#include "filelife.h"
#include "compat.bpf.h"
#include "core_fixes.bpf.h"
#include "path_helpers.bpf.h"

/* linux: include/linux/fs.h */
#define FMODE_CREATED 0x100000

const volatile pid_t targ_tgid = 0;
const volatile bool full_path = false;

struct create_arg {
u64 ts;
struct dentry *cwd_dentry;
struct vfsmount *cwd_vfsmnt;
};

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 8192);
__type(key, struct dentry *);
__type(value, u64);
__type(value, struct create_arg);
} 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");
struct unlink_event {
__u64 delta_ns;
pid_t tgid;
struct dentry *dentry;
struct dentry *cwd_dentry;
struct vfsmount *cwd_vfsmnt;
};

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 8192);
__type(key, u32); /* tid */
__type(value, struct event);
__type(value, struct unlink_event);
} currevent SEC(".maps");

static __always_inline int
probe_create(struct dentry *dentry)
{
u64 id = bpf_get_current_pid_tgid();
u32 tgid = id >> 32;
u64 ts;
struct create_arg arg = {};
struct task_struct *task;

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

ts = bpf_ktime_get_ns();
bpf_map_update_elem(&start, &dentry, &ts, 0);
task = (struct task_struct *)bpf_get_current_task_btf();

arg.ts = bpf_ktime_get_ns();
arg.cwd_dentry = BPF_CORE_READ(task, fs, pwd.dentry);
arg.cwd_vfsmnt = BPF_CORE_READ(task, fs, pwd.mnt);

bpf_map_update_elem(&start, &dentry, &arg, 0);
return 0;
}

Expand Down Expand Up @@ -102,33 +119,29 @@ SEC("kprobe/vfs_unlink")
int BPF_KPROBE(vfs_unlink, void *arg0, void *arg1, void *arg2)
{
u64 id = bpf_get_current_pid_tgid();
struct event event = {};
const u8 *qs_name_ptr;
struct unlink_event unlink_event = {};
struct create_arg *arg;
u32 tgid = id >> 32;
u32 tid = (u32)id;
u64 *tsp, delta_ns;
u64 delta_ns;
bool has_arg = renamedata_has_old_mnt_userns_field()
|| renamedata_has_new_mnt_idmap_field();

tsp = has_arg
arg = has_arg
? bpf_map_lookup_elem(&start, &arg2)
: bpf_map_lookup_elem(&start, &arg1);
if (!tsp)
if (!arg)
return 0; // missed entry

delta_ns = bpf_ktime_get_ns() - *tsp;
delta_ns = bpf_ktime_get_ns() - arg->ts;

qs_name_ptr = has_arg
? BPF_CORE_READ((struct dentry *)arg2, d_name.name)
: BPF_CORE_READ((struct dentry *)arg1, d_name.name);
unlink_event.delta_ns = delta_ns;
unlink_event.tgid = tgid;
unlink_event.dentry = has_arg ? arg2 : arg1;
unlink_event.cwd_dentry = arg->cwd_dentry;
unlink_event.cwd_vfsmnt = arg->cwd_vfsmnt;

bpf_probe_read_kernel_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;
event.dentry = has_arg ? arg2 : arg1;

bpf_map_update_elem(&currevent, &tid, &event, BPF_ANY);
bpf_map_update_elem(&currevent, &tid, &unlink_event, BPF_ANY);
return 0;
}

Expand All @@ -138,22 +151,46 @@ int BPF_KRETPROBE(vfs_unlink_ret)
u64 id = bpf_get_current_pid_tgid();
u32 tid = (u32)id;
int ret = PT_REGS_RC(ctx);
struct event *event;
struct unlink_event *unlink_event;
struct event *eventp;
struct dentry *dentry;
const u8 *qs_name_ptr;

event = bpf_map_lookup_elem(&currevent, &tid);
if (!event)
unlink_event = bpf_map_lookup_elem(&currevent, &tid);
if (!unlink_event)
return 0;
bpf_map_delete_elem(&currevent, &tid);

/* skip failed unlink */
if (ret)
return 0;

bpf_map_delete_elem(&start, &event->dentry);
eventp = reserve_buf(sizeof(*eventp));
if (!eventp)
return 0;

eventp->tgid = unlink_event->tgid;
eventp->delta_ns = unlink_event->delta_ns;
bpf_get_current_comm(&eventp->task, sizeof(eventp->task));

dentry = unlink_event->dentry;
qs_name_ptr = BPF_CORE_READ(dentry, d_name.name);
bpf_probe_read_kernel_str(&eventp->fname.pathes, sizeof(eventp->fname.pathes),
qs_name_ptr);
eventp->fname.depth = 0;

/* get full-path */
if (full_path && eventp->fname.pathes[0] != '/')
bpf_dentry_full_path(eventp->fname.pathes + NAME_MAX, NAME_MAX,
MAX_PATH_DEPTH - 1,
unlink_event->cwd_dentry,
unlink_event->cwd_vfsmnt,
&eventp->fname.failed, &eventp->fname.depth);

bpf_map_delete_elem(&start, &unlink_event->dentry);

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

Expand Down
49 changes: 33 additions & 16 deletions libbpf-tools/filelife.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@
#include <time.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "compat.h"
#include "filelife.h"
#include "filelife.skel.h"
#include "btf_helpers.h"
#include "trace_helpers.h"

#define PERF_BUFFER_PAGES 16
#define PERF_POLL_TIMEOUT_MS 100

static volatile sig_atomic_t exiting = 0;

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

Expand All @@ -40,10 +40,12 @@ const char argp_program_doc[] =
"\n"
"EXAMPLES:\n"
" filelife # trace all events\n"
" filelife -F # trace full-path of file\n"
" filelife -p 123 # trace pid 123\n";

static const struct argp_option opts[] = {
{ "pid", 'p', "PID", 0, "Process PID to trace", 0 },
{ "full-path", 'F', NULL, 0, "Show full path", 0 },
{ "verbose", 'v', NULL, 0, "Verbose debug output", 0 },
{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help", 0 },
{},
Expand All @@ -69,6 +71,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
}
env.pid = pid;
break;
case 'F':
env.full_path = true;
break;
default:
return ARGP_ERR_UNKNOWN;
}
Expand All @@ -87,7 +92,7 @@ static void sig_int(int signo)
exiting = 1;
}

void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
int handle_event(void *ctx, void *data, size_t data_sz)
{
struct event e;
struct tm *tm;
Expand All @@ -96,17 +101,22 @@ void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)

if (data_sz < sizeof(e)) {
printf("Error: packet too small\n");
return;
return -EINVAL;
}
/* Copy data as alignment in the perf buffer isn't guaranteed. */
/* Copy data as alignment in the ring buffer isn't guaranteed. */
memcpy(&e, data, sizeof(e));

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);
printf("%-8s %-6d %-16s %-7.2f ",
ts, e.tgid, e.task, (double)e.delta_ns / 1000000000);
if (env.full_path) {
print_full_path(&e.fname);
printf("\n");
} else
printf("%s\n", e.fname.pathes);
return 0;
}

void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
Expand All @@ -122,7 +132,7 @@ int main(int argc, char **argv)
.parser = parse_arg,
.doc = argp_program_doc,
};
struct perf_buffer *pb = NULL;
struct bpf_buffer *buf = NULL;
struct filelife_bpf *obj;
int err;

Expand All @@ -146,6 +156,7 @@ int main(int argc, char **argv)

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

if (!kprobe_exists("security_inode_create"))
bpf_program__set_autoload(obj->progs.security_inode_create, false);
Expand All @@ -165,11 +176,17 @@ int main(int argc, char **argv)
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 = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES,
handle_event, handle_lost_events, NULL, NULL);
if (!pb) {
buf = bpf_buffer__new(obj->maps.events, obj->maps.heap);
if (!buf) {
err = -errno;
fprintf(stderr, "failed to create ring/perf buffer: %d", err);
goto cleanup;
}

err = bpf_buffer__open(buf, handle_event, handle_lost_events, NULL);
if (err) {
err = -errno;
fprintf(stderr, "failed to open perf buffer: %d\n", err);
fprintf(stderr, "failed to open ring/perf buffer: %d\n", err);
goto cleanup;
}

Expand All @@ -180,17 +197,17 @@ int main(int argc, char **argv)
}

while (!exiting) {
err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS);
err = bpf_buffer__poll(buf, POLL_TIMEOUT_MS);
if (err < 0 && err != -EINTR) {
fprintf(stderr, "error polling perf buffer: %s\n", strerror(-err));
fprintf(stderr, "error polling ring/perf buffer: %s\n", strerror(-err));
goto cleanup;
}
/* reset err to return 0 if exiting */
err = 0;
}

cleanup:
perf_buffer__free(pb);
bpf_buffer__free(buf);
filelife_bpf__destroy(obj);
cleanup_core_btf(&open_opts);

Expand Down
7 changes: 3 additions & 4 deletions libbpf-tools/filelife.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
#ifndef __FILELIFE_H
#define __FILELIFE_H

#define DNAME_INLINE_LEN 32
#include "path_helpers.h"

#define TASK_COMM_LEN 16

struct event {
char file[DNAME_INLINE_LEN];
struct full_path fname;
char task[TASK_COMM_LEN];
__u64 delta_ns;
pid_t tgid;
/* private */
void *dentry;
};

#endif /* __FILELIFE_H */
Loading
Loading