Skip to content

Commit 103a5c3

Browse files
committed
tools/filelife: support full-path
Same as libbpf-tools/filelife changes [1], introduce -F argument to support full-path. Example: $ touch a.out && sleep 0.2 && rm a.out # Before $ sudo ./filelife.py TIME PID COMM AGE(s) FILE 18:57:25 343907 rm 0.21 a.out # After $ sudo ./filelife.py -F TIME PID COMM AGE(s) FILE 18:57:35 343917 rm 0.21 /home/sda/git-repos/iovisor/bcc/libbpf-tools/a.out ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [1] iovisor#5340 iovisor@74bddcbe646e Signed-off-by: Rong Tao <[email protected]>
1 parent db8d238 commit 103a5c3

File tree

1 file changed

+112
-37
lines changed

1 file changed

+112
-37
lines changed

tools/filelife.py

Lines changed: 112 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
from __future__ import print_function
2323
from bcc import BPF
24+
from bcc.utils import printb
2425
import argparse
2526
from time import strftime
2627

@@ -35,6 +36,8 @@
3536
epilog=examples)
3637
parser.add_argument("-p", "--pid",
3738
help="trace this PID only")
39+
parser.add_argument("-F", "--full-path", action="store_true",
40+
help="show full path")
3841
parser.add_argument("--ebpf", action="store_true",
3942
help=argparse.SUPPRESS)
4043
args = parser.parse_args()
@@ -45,27 +48,59 @@
4548
#include <uapi/linux/ptrace.h>
4649
#include <linux/fs.h>
4750
#include <linux/sched.h>
51+
#include <linux/fs_struct.h>
52+
#ifdef FULLPATH
53+
INCLUDE_FULL_PATH_H
54+
INCLUDE_PATH_HELPERS_BPF_H
55+
#endif
4856
4957
struct data_t {
5058
u32 pid;
5159
u64 delta;
5260
char comm[TASK_COMM_LEN];
61+
u32 path_depth;
62+
#ifdef FULLPATH
63+
FULL_PATH_FIELD(fname);
64+
#else
5365
char fname[DNAME_INLINE_LEN];
54-
/* private */
55-
void *dentry;
66+
#endif
5667
};
5768
58-
BPF_HASH(birth, struct dentry *);
59-
BPF_HASH(unlink_data, u32, struct data_t);
60-
BPF_PERF_OUTPUT(events);
69+
struct create_arg {
70+
u64 ts;
71+
struct dentry *cwd_dentry;
72+
struct vfsmount *cwd_vfsmnt;
73+
};
74+
75+
struct unlink_event {
76+
u32 tid;
77+
u64 delta;
78+
struct dentry *dentry;
79+
struct dentry *cwd_dentry;
80+
struct vfsmount *cwd_vfsmnt;
81+
};
82+
83+
BPF_HASH(birth, struct dentry *, struct create_arg);
84+
BPF_HASH(unlink_data, u32, struct unlink_event);
85+
BPF_RINGBUF_OUTPUT(events, 64);
6186
6287
static int probe_dentry(struct pt_regs *ctx, struct dentry *dentry)
6388
{
89+
struct task_struct *task;
90+
struct fs_struct *fs;
91+
struct create_arg arg = {};
6492
u32 pid = bpf_get_current_pid_tgid() >> 32;
6593
FILTER
6694
6795
u64 ts = bpf_ktime_get_ns();
68-
birth.update(&dentry, &ts);
96+
task = (struct task_struct *)bpf_get_current_task_btf();
97+
98+
arg.ts = ts;
99+
bpf_probe_read_kernel(&fs, sizeof(fs), &task->fs);
100+
bpf_probe_read_kernel(&arg.cwd_dentry, sizeof(arg.cwd_dentry), &fs->pwd.dentry);
101+
bpf_probe_read_kernel(&arg.cwd_vfsmnt, sizeof(arg.cwd_vfsmnt), &fs->pwd.mnt);
102+
103+
birth.update(&dentry, &arg);
69104
70105
return 0;
71106
}
@@ -98,46 +133,42 @@
98133
// trace file deletion and output details
99134
TRACE_UNLINK_FUNC
100135
{
101-
struct data_t data = {};
136+
struct create_arg *arg;
137+
struct unlink_event event = {};
102138
u64 pid_tgid = bpf_get_current_pid_tgid();
103139
u32 pid = pid_tgid >> 32;
104140
u32 tid = (u32)pid_tgid;
105141
106142
FILTER
107143
108-
u64 *tsp, delta;
109-
tsp = birth.lookup(&dentry);
110-
if (tsp == 0) {
144+
u64 delta;
145+
arg = birth.lookup(&dentry);
146+
if (arg == 0) {
111147
return 0; // missed create
112148
}
113149
114-
delta = (bpf_ktime_get_ns() - *tsp) / 1000000;
115-
116-
struct qstr d_name = dentry->d_name;
117-
if (d_name.len == 0)
118-
return 0;
119-
120-
if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) {
121-
data.pid = pid;
122-
data.delta = delta;
123-
bpf_probe_read_kernel(&data.fname, sizeof(data.fname), d_name.name);
124-
}
150+
delta = (bpf_ktime_get_ns() - arg->ts) / 1000000;
125151
126152
/* record dentry, only delete from birth if unlink successful */
127-
data.dentry = dentry;
153+
event.delta = delta;
154+
event.tid = tid;
155+
event.dentry = dentry;
156+
event.cwd_dentry = arg->cwd_dentry;
157+
event.cwd_vfsmnt = arg->cwd_vfsmnt;
128158
129-
unlink_data.update(&tid, &data);
159+
unlink_data.update(&tid, &event);
130160
return 0;
131161
}
132162
133163
int trace_unlink_ret(struct pt_regs *ctx)
134164
{
135165
int ret = PT_REGS_RC(ctx);
166+
struct unlink_event *unlink_event;
136167
struct data_t *data;
137168
u32 tid = (u32)bpf_get_current_pid_tgid();
138169
139-
data = unlink_data.lookup(&tid);
140-
if (!data)
170+
unlink_event = unlink_data.lookup(&tid);
171+
if (!unlink_event)
141172
return 0;
142173
143174
/* delete it any way */
@@ -147,8 +178,23 @@
147178
if (ret)
148179
return 0;
149180
150-
birth.delete((struct dentry **)&data->dentry);
151-
events.perf_submit(ctx, data, sizeof(*data));
181+
data = events.ringbuf_reserve(sizeof(struct data_t));
182+
if (!data)
183+
return 0;
184+
185+
data->pid = unlink_event->tid;
186+
data->delta = unlink_event->delta;
187+
bpf_get_current_comm(&data->comm, sizeof(data->comm));
188+
189+
data->path_depth = 0;
190+
struct qstr d_name = unlink_event->dentry->d_name;
191+
bpf_probe_read_kernel_str(&data->fname, sizeof(data->fname), d_name.name);
192+
193+
GET_FULL_PATH
194+
195+
birth.delete((struct dentry **)&unlink_event->dentry);
196+
197+
events.ringbuf_submit(data, sizeof(*data));
152198
153199
return 0;
154200
}
@@ -183,10 +229,29 @@
183229
'if (pid != %s) { return 0; }' % args.pid)
184230
else:
185231
bpf_text = bpf_text.replace('FILTER', '')
186-
if debug or args.ebpf:
187-
print(bpf_text)
188-
if args.ebpf:
189-
exit()
232+
233+
if args.full_path:
234+
bpf_text = "#define FULLPATH\n" + bpf_text
235+
236+
bpf_text = bpf_text.replace('GET_FULL_PATH', """
237+
if (data->fname[0] != '/') {
238+
bpf_dentry_full_path(data->fname + NAME_MAX, NAME_MAX, MAX_ENTRIES - 1,
239+
unlink_event->cwd_dentry, unlink_event->cwd_vfsmnt,
240+
&data->path_depth);
241+
}
242+
""")
243+
244+
with open(BPF._find_file("full_path.h")) as fileobj:
245+
progtxt = fileobj.read()
246+
bpf_text = bpf_text.replace('INCLUDE_FULL_PATH_H', progtxt)
247+
248+
with open(BPF._find_file("path_helpers.bpf.c")) as fileobj:
249+
progtxt = fileobj.read()
250+
bpf_text = bpf_text.replace('INCLUDE_PATH_HELPERS_BPF_H', progtxt)
251+
else:
252+
bpf_text = bpf_text.replace('GET_FULL_PATH', """""")
253+
bpf_text = bpf_text.replace('INCLUDE_FULL_PATH_H', """""")
254+
bpf_text = bpf_text.replace('INCLUDE_PATH_HELPERS_BPF_H', """""")
190255

191256
if BPF.kernel_struct_has_field(b'renamedata', b'new_mnt_idmap') == 1:
192257
bpf_text = bpf_text.replace('TRACE_CREATE_FUNC', trace_create_text_3)
@@ -198,6 +263,11 @@
198263
bpf_text = bpf_text.replace('TRACE_CREATE_FUNC', trace_create_text_1)
199264
bpf_text = bpf_text.replace('TRACE_UNLINK_FUNC', trace_unlink_text_1)
200265

266+
if debug or args.ebpf:
267+
print(bpf_text)
268+
if args.ebpf:
269+
exit()
270+
201271
# initialize BPF
202272
b = BPF(text=bpf_text)
203273
b.attach_kprobe(event="vfs_create", fn_name="trace_create")
@@ -216,13 +286,18 @@
216286
# process event
217287
def print_event(cpu, data, size):
218288
event = b["events"].event(data)
219-
print("%-8s %-7d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), event.pid,
220-
event.comm.decode('utf-8', 'replace'), float(event.delta) / 1000,
221-
event.fname.decode('utf-8', 'replace')))
222-
223-
b["events"].open_perf_buffer(print_event)
289+
printb(b"%-8s %-7d %-16s %-7.2f " % (strftime("%H:%M:%S").encode('utf-8', 'replace'),
290+
event.pid, event.comm, float(event.delta) / 1000), nl="")
291+
if args.full_path:
292+
from path_helpers import get_full_path
293+
result = get_full_path(event.fname, event.path_depth)
294+
printb(b"%s" % result.encode("utf-8"))
295+
else:
296+
printb(b"%s" % event.fname)
297+
298+
b["events"].open_ring_buffer(print_event)
224299
while 1:
225300
try:
226-
b.perf_buffer_poll()
301+
b.ring_buffer_poll()
227302
except KeyboardInterrupt:
228303
exit()

0 commit comments

Comments
 (0)