Skip to content

Commit 9154166

Browse files
authored
tools: add filegone to trace why file gone (iovisor#4333)
1 parent 14c5f99 commit 9154166

File tree

4 files changed

+244
-0
lines changed

4 files changed

+244
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pair of .c and .py files, and some are directories of files.
114114
- tools/[ext4dist](tools/ext4dist.py): Summarize ext4 operation latency distribution as a histogram. [Examples](tools/ext4dist_example.txt).
115115
- tools/[ext4slower](tools/ext4slower.py): Trace slow ext4 operations. [Examples](tools/ext4slower_example.txt).
116116
- tools/[filelife](tools/filelife.py): Trace the lifespan of short-lived files. [Examples](tools/filelife_example.txt).
117+
- tools/[filegone](tools/filegone.py): Trace why file gone (deleted or renamed). [Examples](tools/filegone_example.txt).
117118
- tools/[fileslower](tools/fileslower.py): Trace slow synchronous file reads and writes. [Examples](tools/fileslower_example.txt).
118119
- tools/[filetop](tools/filetop.py): File reads and writes by filename and process. Top for files. [Examples](tools/filetop_example.txt).
119120
- tools/[funccount](tools/funccount.py): Count kernel function calls. [Examples](tools/funccount_example.txt).

man/man8/filegone.8

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
.TH filegone 8 "2022-11-18" "USER COMMANDS"
2+
.SH NAME
3+
filegone \- Trace why file gone (deleted or renamed). Uses Linux eBPF/bcc.
4+
.SH SYNOPSIS
5+
.B filegone [\-h] [\-p PID]
6+
.SH DESCRIPTION
7+
This traces why file gone/vanished, providing information on who deleted or
8+
renamed the file.
9+
10+
This works by tracing the kernel vfs_unlink() , vfs_rmdir() , vfs_rename
11+
functions.
12+
13+
Since this uses BPF, only the root user can use this tool.
14+
.SH REQUIREMENTS
15+
CONFIG_BPF and bcc.
16+
.SH OPTIONS
17+
.TP
18+
\-h
19+
Print usage message.
20+
.TP
21+
\-p PID
22+
Trace this process ID only (filtered in-kernel).
23+
.SH EXAMPLES
24+
.TP
25+
Trace all file gone events
26+
#
27+
.B filegone
28+
.TP
29+
Trace file gone events caused by PID 181:
30+
#
31+
.B filegone \-p 181
32+
.SH FIELDS
33+
.TP
34+
TIME
35+
Time of the event.
36+
.TP
37+
PID
38+
Process ID that renamed/deleted the file.
39+
.TP
40+
COMM
41+
Process name for the PID.
42+
.TP
43+
ACTION
44+
action on file: 'DELETE' or 'RENAME'
45+
.TP
46+
FILE
47+
Filename.
48+
.SH OVERHEAD
49+
This traces the kernel VFS file rename and delete functions and prints output
50+
for each event. As the rate of this is generally expected to be low
51+
(< 1000/s), the overhead is also expected to be negligible.
52+
This is from bcc.
53+
.IP
54+
https://github.com/iovisor/bcc
55+
.PP
56+
Also look in the bcc distribution for a companion _examples.txt file containing
57+
example usage, output, and commentary for this tool.
58+
.SH OS
59+
Linux
60+
.SH STABILITY
61+
Unstable - in development.
62+
.SH AUTHOR
63+
Curu Wong
64+
.SH SEE ALSO
65+
filelife(8)

tools/filegone.py

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#!/usr/bin/python
2+
# @lint-avoid-python-3-compatibility-imports
3+
#
4+
# filegone Trace why file gone (deleted or renamed).
5+
# For Linux, uses BCC, eBPF. Embedded C.
6+
#
7+
# USAGE: filegone [-h] [-p PID]
8+
#
9+
# Licensed under the Apache License, Version 2.0 (the "License")
10+
#
11+
# 08-Nov-2022 Curu. modified from filelife
12+
13+
from __future__ import print_function
14+
from bcc import BPF
15+
import argparse
16+
from time import strftime
17+
18+
# arguments
19+
examples = """examples:
20+
./filegone # trace all file gone events
21+
./filegone -p 181 # only trace PID 181
22+
"""
23+
parser = argparse.ArgumentParser(
24+
description="Trace why file gone (deleted or renamed)",
25+
formatter_class=argparse.RawDescriptionHelpFormatter,
26+
epilog=examples)
27+
parser.add_argument("-p", "--pid",
28+
help="trace this PID only")
29+
parser.add_argument("--ebpf", action="store_true",
30+
help=argparse.SUPPRESS)
31+
args = parser.parse_args()
32+
debug = 0
33+
34+
# define BPF program
35+
bpf_text = """
36+
#include <uapi/linux/ptrace.h>
37+
#include <linux/fs.h>
38+
#include <linux/sched.h>
39+
40+
struct data_t {
41+
u32 pid;
42+
u8 action;
43+
char comm[TASK_COMM_LEN];
44+
char fname[DNAME_INLINE_LEN];
45+
char fname2[DNAME_INLINE_LEN];
46+
};
47+
48+
BPF_PERF_OUTPUT(events);
49+
50+
// trace file deletion and output details
51+
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
52+
int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
53+
#else
54+
int trace_unlink(struct pt_regs *ctx, struct user_namespace *ns, struct inode *dir, struct dentry *dentry)
55+
#endif
56+
{
57+
u32 pid = bpf_get_current_pid_tgid() >> 32;
58+
59+
FILTER
60+
61+
struct data_t data = {};
62+
struct qstr d_name = dentry->d_name;
63+
if (d_name.len == 0)
64+
return 0;
65+
66+
bpf_get_current_comm(&data.comm, sizeof(data.comm));
67+
data.pid = pid;
68+
data.action = 'D';
69+
bpf_probe_read(&data.fname, sizeof(data.fname), d_name.name);
70+
71+
events.perf_submit(ctx, &data, sizeof(data));
72+
73+
return 0;
74+
}
75+
76+
// trace file rename
77+
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
78+
int trace_rename(struct pt_regs *ctx, struct inode *old_dir, struct dentry *old_dentry,
79+
struct inode *new_dir, struct dentry *new_dentry)
80+
{
81+
#else
82+
int trace_rename(struct pt_regs *ctx, struct renamedata *rd)
83+
{
84+
struct dentry *old_dentry = rd->old_dentry;
85+
struct dentry *new_dentry = rd->new_dentry;
86+
#endif
87+
88+
u32 pid = bpf_get_current_pid_tgid() >> 32;
89+
90+
FILTER
91+
92+
struct data_t data = {};
93+
struct qstr s_name = old_dentry->d_name;
94+
struct qstr d_name = new_dentry->d_name;
95+
if (s_name.len == 0 || d_name.len == 0)
96+
return 0;
97+
98+
bpf_get_current_comm(&data.comm, sizeof(data.comm));
99+
data.pid = pid;
100+
data.action = 'R';
101+
bpf_probe_read(&data.fname, sizeof(data.fname), s_name.name);
102+
bpf_probe_read(&data.fname2, sizeof(data.fname), d_name.name);
103+
events.perf_submit(ctx, &data, sizeof(data));
104+
105+
return 0;
106+
}
107+
"""
108+
109+
110+
def action2str(action):
111+
if chr(action) == 'D':
112+
action_str = "DELETE"
113+
else:
114+
action_str = "RENAME"
115+
return action_str
116+
117+
if args.pid:
118+
bpf_text = bpf_text.replace('FILTER',
119+
'if (pid != %s) { return 0; }' % args.pid)
120+
else:
121+
bpf_text = bpf_text.replace('FILTER', '')
122+
123+
if debug or args.ebpf:
124+
print(bpf_text)
125+
if args.ebpf:
126+
exit()
127+
128+
# initialize BPF
129+
b = BPF(text=bpf_text)
130+
b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink")
131+
b.attach_kprobe(event="vfs_rmdir", fn_name="trace_unlink")
132+
b.attach_kprobe(event="vfs_rename", fn_name="trace_rename")
133+
134+
# header
135+
print("%-8s %-7s %-16s %6s %s" % ("TIME", "PID", "COMM", "ACTION", "FILE"))
136+
137+
# process event
138+
def print_event(cpu, data, size):
139+
event = b["events"].event(data)
140+
action_str = action2str(event.action)
141+
file_str = event.fname.decode('utf-8', 'replace')
142+
if action_str == "RENAME":
143+
file_str = "%s > %s" % (file_str, event.fname2.decode('utf-8', 'replace'))
144+
print("%-8s %-7d %-16s %6s %s" % (strftime("%H:%M:%S"), event.pid,
145+
event.comm.decode('utf-8', 'replace'), action_str, file_str))
146+
147+
b["events"].open_perf_buffer(print_event)
148+
while 1:
149+
try:
150+
b.perf_buffer_poll()
151+
except KeyboardInterrupt:
152+
exit()

tools/filegone_example.txt

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Demonstrations of filegone, the Linux eBPF/bcc version.
2+
3+
4+
filegone traces why file gone, either been deleted or renamed
5+
For example:
6+
7+
# ./filegone
8+
18:30:56 22905 vim DELETE .fstab.swpx
9+
18:30:56 22905 vim DELETE .fstab.swp
10+
18:31:00 22905 vim DELETE .viminfo
11+
18:31:00 22905 vim RENAME .viminfo.tmp > .viminfo
12+
18:31:00 22905 vim DELETE .fstab.swp
13+
14+
USAGE message:
15+
16+
usage: filegone.py [-h] [-p PID]
17+
18+
Trace why file gone (deleted or renamed)
19+
20+
optional arguments:
21+
-h, --help show this help message and exit
22+
-p PID, --pid PID trace this PID only
23+
24+
examples:
25+
./filegone # trace all file gone events
26+
./filegone -p 181 # only trace PID 181

0 commit comments

Comments
 (0)