forked from iovisor/bcc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Wenbo Zhang <[email protected]>
- Loading branch information
1 parent
9adda70
commit 85f5e7d
Showing
5 changed files
with
271 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
/.output | ||
/drsnoop | ||
/filelife | ||
/opensnoop | ||
/runqslower | ||
/xfsslower |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 */ |