Skip to content

Commit 74bddcb

Browse files
Rtoaxyonghong-song
authored andcommitted
libbpf-tools/filelife: support full-path
To support full-path, the following operations are performed: 1. First, to support the transmission of larger data, perf-buffer is replaced with ring-buff. 2. Then, before this patch, events are constructed in kprobe/unlink, and events of successful unlink are submitted in kretprobe/unlink. However, the reservation and submission of ring-buff cannot be separated, so the struct unlink_event small structure is introduced for transfer, and the reservation and submission of events are in kretprobe/unlink. 3. In order to record the dentry and vfsmount structures of cwd when the file is created, the structure struct create_arg{} is introduced. Note: cannot obtain the dentry and vfsmount structures of cwd in kretprobe/unlink. Example: # Test commands $ cd /home/sda/git-repos/ $ touch a.out && sleep 0.2 && rm a.out # before $ sudo ./filelife Tracing the lifespan of short-lived files ... Hit Ctrl-C to end. TIME PID COMM AGE(s) FILE 17:27:05 458702 rm 0.21 a.out # after $ sudo ./filelife -F Tracing the lifespan of short-lived files ... Hit Ctrl-C to end. TIME PID COMM AGE(s) FILE 17:26:58 458692 rm 0.21 /home/sda/git-repos/a.out ^^^^^^^^^^^^^^^^^^^^ full-path Signed-off-by: Rong Tao <rongtao@cestc.cn>
1 parent ab8e061 commit 74bddcb

File tree

3 files changed

+105
-52
lines changed

3 files changed

+105
-52
lines changed

libbpf-tools/filelife.bpf.c

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,62 @@
55
#include <bpf/bpf_core_read.h>
66
#include <bpf/bpf_tracing.h>
77
#include "filelife.h"
8+
#include "compat.bpf.h"
89
#include "core_fixes.bpf.h"
10+
#include "path_helpers.bpf.h"
911

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

1315
const volatile pid_t targ_tgid = 0;
16+
const volatile bool full_path = false;
17+
18+
struct create_arg {
19+
u64 ts;
20+
struct dentry *cwd_dentry;
21+
struct vfsmount *cwd_vfsmnt;
22+
};
1423

1524
struct {
1625
__uint(type, BPF_MAP_TYPE_HASH);
1726
__uint(max_entries, 8192);
1827
__type(key, struct dentry *);
19-
__type(value, u64);
28+
__type(value, struct create_arg);
2029
} start SEC(".maps");
2130

22-
struct {
23-
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
24-
__uint(key_size, sizeof(u32));
25-
__uint(value_size, sizeof(u32));
26-
} events SEC(".maps");
31+
struct unlink_event {
32+
__u64 delta_ns;
33+
pid_t tgid;
34+
struct dentry *dentry;
35+
struct dentry *cwd_dentry;
36+
struct vfsmount *cwd_vfsmnt;
37+
};
2738

2839
struct {
2940
__uint(type, BPF_MAP_TYPE_HASH);
3041
__uint(max_entries, 8192);
3142
__type(key, u32); /* tid */
32-
__type(value, struct event);
43+
__type(value, struct unlink_event);
3344
} currevent SEC(".maps");
3445

3546
static __always_inline int
3647
probe_create(struct dentry *dentry)
3748
{
3849
u64 id = bpf_get_current_pid_tgid();
3950
u32 tgid = id >> 32;
40-
u64 ts;
51+
struct create_arg arg = {};
52+
struct task_struct *task;
4153

4254
if (targ_tgid && targ_tgid != tgid)
4355
return 0;
4456

45-
ts = bpf_ktime_get_ns();
46-
bpf_map_update_elem(&start, &dentry, &ts, 0);
57+
task = (struct task_struct *)bpf_get_current_task_btf();
58+
59+
arg.ts = bpf_ktime_get_ns();
60+
arg.cwd_dentry = BPF_CORE_READ(task, fs, pwd.dentry);
61+
arg.cwd_vfsmnt = BPF_CORE_READ(task, fs, pwd.mnt);
62+
63+
bpf_map_update_elem(&start, &dentry, &arg, 0);
4764
return 0;
4865
}
4966

@@ -102,33 +119,29 @@ SEC("kprobe/vfs_unlink")
102119
int BPF_KPROBE(vfs_unlink, void *arg0, void *arg1, void *arg2)
103120
{
104121
u64 id = bpf_get_current_pid_tgid();
105-
struct event event = {};
106-
const u8 *qs_name_ptr;
122+
struct unlink_event unlink_event = {};
123+
struct create_arg *arg;
107124
u32 tgid = id >> 32;
108125
u32 tid = (u32)id;
109-
u64 *tsp, delta_ns;
126+
u64 delta_ns;
110127
bool has_arg = renamedata_has_old_mnt_userns_field()
111128
|| renamedata_has_new_mnt_idmap_field();
112129

113-
tsp = has_arg
130+
arg = has_arg
114131
? bpf_map_lookup_elem(&start, &arg2)
115132
: bpf_map_lookup_elem(&start, &arg1);
116-
if (!tsp)
133+
if (!arg)
117134
return 0; // missed entry
118135

119-
delta_ns = bpf_ktime_get_ns() - *tsp;
136+
delta_ns = bpf_ktime_get_ns() - arg->ts;
120137

121-
qs_name_ptr = has_arg
122-
? BPF_CORE_READ((struct dentry *)arg2, d_name.name)
123-
: BPF_CORE_READ((struct dentry *)arg1, d_name.name);
138+
unlink_event.delta_ns = delta_ns;
139+
unlink_event.tgid = tgid;
140+
unlink_event.dentry = has_arg ? arg2 : arg1;
141+
unlink_event.cwd_dentry = arg->cwd_dentry;
142+
unlink_event.cwd_vfsmnt = arg->cwd_vfsmnt;
124143

125-
bpf_probe_read_kernel_str(&event.file, sizeof(event.file), qs_name_ptr);
126-
bpf_get_current_comm(&event.task, sizeof(event.task));
127-
event.delta_ns = delta_ns;
128-
event.tgid = tgid;
129-
event.dentry = has_arg ? arg2 : arg1;
130-
131-
bpf_map_update_elem(&currevent, &tid, &event, BPF_ANY);
144+
bpf_map_update_elem(&currevent, &tid, &unlink_event, BPF_ANY);
132145
return 0;
133146
}
134147

@@ -138,22 +151,46 @@ int BPF_KRETPROBE(vfs_unlink_ret)
138151
u64 id = bpf_get_current_pid_tgid();
139152
u32 tid = (u32)id;
140153
int ret = PT_REGS_RC(ctx);
141-
struct event *event;
154+
struct unlink_event *unlink_event;
155+
struct event *eventp;
156+
struct dentry *dentry;
157+
const u8 *qs_name_ptr;
142158

143-
event = bpf_map_lookup_elem(&currevent, &tid);
144-
if (!event)
159+
unlink_event = bpf_map_lookup_elem(&currevent, &tid);
160+
if (!unlink_event)
145161
return 0;
146162
bpf_map_delete_elem(&currevent, &tid);
147163

148164
/* skip failed unlink */
149165
if (ret)
150166
return 0;
151167

152-
bpf_map_delete_elem(&start, &event->dentry);
168+
eventp = reserve_buf(sizeof(*eventp));
169+
if (!eventp)
170+
return 0;
171+
172+
eventp->tgid = unlink_event->tgid;
173+
eventp->delta_ns = unlink_event->delta_ns;
174+
bpf_get_current_comm(&eventp->task, sizeof(eventp->task));
175+
176+
dentry = unlink_event->dentry;
177+
qs_name_ptr = BPF_CORE_READ(dentry, d_name.name);
178+
bpf_probe_read_kernel_str(&eventp->fname.pathes, sizeof(eventp->fname.pathes),
179+
qs_name_ptr);
180+
eventp->fname.depth = 0;
181+
182+
/* get full-path */
183+
if (full_path && eventp->fname.pathes[0] != '/')
184+
bpf_dentry_full_path(eventp->fname.pathes + NAME_MAX, NAME_MAX,
185+
MAX_PATH_DEPTH - 1,
186+
unlink_event->cwd_dentry,
187+
unlink_event->cwd_vfsmnt,
188+
&eventp->fname.failed, &eventp->fname.depth);
189+
190+
bpf_map_delete_elem(&start, &unlink_event->dentry);
153191

154192
/* output */
155-
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU,
156-
event, sizeof(*event));
193+
submit_buf(ctx, eventp, sizeof(*eventp));
157194
return 0;
158195
}
159196

libbpf-tools/filelife.c

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@
1515
#include <time.h>
1616
#include <bpf/libbpf.h>
1717
#include <bpf/bpf.h>
18+
#include "compat.h"
1819
#include "filelife.h"
1920
#include "filelife.skel.h"
2021
#include "btf_helpers.h"
2122
#include "trace_helpers.h"
2223

23-
#define PERF_BUFFER_PAGES 16
24-
#define PERF_POLL_TIMEOUT_MS 100
2524

2625
static volatile sig_atomic_t exiting = 0;
2726

2827
static struct env {
2928
pid_t pid;
29+
bool full_path;
3030
bool verbose;
3131
} env = { };
3232

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

4546
static const struct argp_option opts[] = {
4647
{ "pid", 'p', "PID", 0, "Process PID to trace", 0 },
48+
{ "full-path", 'F', NULL, 0, "Show full path", 0 },
4749
{ "verbose", 'v', NULL, 0, "Verbose debug output", 0 },
4850
{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help", 0 },
4951
{},
@@ -69,6 +71,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
6971
}
7072
env.pid = pid;
7173
break;
74+
case 'F':
75+
env.full_path = true;
76+
break;
7277
default:
7378
return ARGP_ERR_UNKNOWN;
7479
}
@@ -87,7 +92,7 @@ static void sig_int(int signo)
8792
exiting = 1;
8893
}
8994

90-
void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
95+
int handle_event(void *ctx, void *data, size_t data_sz)
9196
{
9297
struct event e;
9398
struct tm *tm;
@@ -96,17 +101,22 @@ void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
96101

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

104109
time(&t);
105110
tm = localtime(&t);
106111
strftime(ts, sizeof(ts), "%H:%M:%S", tm);
107-
printf("%-8s %-6d %-16s %-7.2f %s\n",
108-
ts, e.tgid, e.task, (double)e.delta_ns / 1000000000,
109-
e.file);
112+
printf("%-8s %-6d %-16s %-7.2f ",
113+
ts, e.tgid, e.task, (double)e.delta_ns / 1000000000);
114+
if (env.full_path) {
115+
print_full_path(&e.fname);
116+
printf("\n");
117+
} else
118+
printf("%s\n", e.fname.pathes);
119+
return 0;
110120
}
111121

112122
void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
@@ -122,7 +132,7 @@ int main(int argc, char **argv)
122132
.parser = parse_arg,
123133
.doc = argp_program_doc,
124134
};
125-
struct perf_buffer *pb = NULL;
135+
struct bpf_buffer *buf = NULL;
126136
struct filelife_bpf *obj;
127137
int err;
128138

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

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

150161
if (!kprobe_exists("security_inode_create"))
151162
bpf_program__set_autoload(obj->progs.security_inode_create, false);
@@ -165,11 +176,17 @@ int main(int argc, char **argv)
165176
printf("Tracing the lifespan of short-lived files ... Hit Ctrl-C to end.\n");
166177
printf("%-8s %-6s %-16s %-7s %s\n", "TIME", "PID", "COMM", "AGE(s)", "FILE");
167178

168-
pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES,
169-
handle_event, handle_lost_events, NULL, NULL);
170-
if (!pb) {
179+
buf = bpf_buffer__new(obj->maps.events, obj->maps.heap);
180+
if (!buf) {
181+
err = -errno;
182+
fprintf(stderr, "failed to create ring/perf buffer: %d", err);
183+
goto cleanup;
184+
}
185+
186+
err = bpf_buffer__open(buf, handle_event, handle_lost_events, NULL);
187+
if (err) {
171188
err = -errno;
172-
fprintf(stderr, "failed to open perf buffer: %d\n", err);
189+
fprintf(stderr, "failed to open ring/perf buffer: %d\n", err);
173190
goto cleanup;
174191
}
175192

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

182199
while (!exiting) {
183-
err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS);
200+
err = bpf_buffer__poll(buf, POLL_TIMEOUT_MS);
184201
if (err < 0 && err != -EINTR) {
185-
fprintf(stderr, "error polling perf buffer: %s\n", strerror(-err));
202+
fprintf(stderr, "error polling ring/perf buffer: %s\n", strerror(-err));
186203
goto cleanup;
187204
}
188205
/* reset err to return 0 if exiting */
189206
err = 0;
190207
}
191208

192209
cleanup:
193-
perf_buffer__free(pb);
210+
bpf_buffer__free(buf);
194211
filelife_bpf__destroy(obj);
195212
cleanup_core_btf(&open_opts);
196213

libbpf-tools/filelife.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22
#ifndef __FILELIFE_H
33
#define __FILELIFE_H
44

5-
#define DNAME_INLINE_LEN 32
5+
#include "path_helpers.h"
6+
67
#define TASK_COMM_LEN 16
78

89
struct event {
9-
char file[DNAME_INLINE_LEN];
10+
struct full_path fname;
1011
char task[TASK_COMM_LEN];
1112
__u64 delta_ns;
1213
pid_t tgid;
13-
/* private */
14-
void *dentry;
1514
};
1615

1716
#endif /* __FILELIFE_H */

0 commit comments

Comments
 (0)