xref: /aosp_15_r20/external/bcc/tools/old/filegone.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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
13from __future__ import print_function
14from bcc import BPF
15import argparse
16from time import strftime
17
18# arguments
19examples = """examples:
20    ./filegone           # trace all file gone events
21    ./filegone -p 181    # only trace PID 181
22"""
23parser = argparse.ArgumentParser(
24    description="Trace why file gone (deleted or renamed)",
25    formatter_class=argparse.RawDescriptionHelpFormatter,
26    epilog=examples)
27parser.add_argument("-p", "--pid",
28    help="trace this PID only")
29parser.add_argument("--ebpf", action="store_true",
30    help=argparse.SUPPRESS)
31args = parser.parse_args()
32debug = 0
33
34# define BPF program
35bpf_text = """
36#include <uapi/linux/ptrace.h>
37#include <linux/fs.h>
38#include <linux/sched.h>
39
40struct 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
48BPF_PERF_OUTPUT(events);
49
50// trace file deletion and output details
51#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
52int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
53#else
54int 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)
78int trace_rename(struct pt_regs *ctx, struct inode *old_dir, struct dentry *old_dentry,
79struct inode *new_dir, struct dentry *new_dentry)
80{
81#else
82int 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
110def action2str(action):
111    if chr(action) == 'D':
112        action_str = "DELETE"
113    else:
114        action_str = "RENAME"
115    return action_str
116
117if args.pid:
118    bpf_text = bpf_text.replace('FILTER',
119        'if (pid != %s) { return 0; }' % args.pid)
120else:
121    bpf_text = bpf_text.replace('FILTER', '')
122
123if debug or args.ebpf:
124    print(bpf_text)
125    if args.ebpf:
126        exit()
127
128# initialize BPF
129b = BPF(text=bpf_text)
130b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink")
131b.attach_kprobe(event="vfs_rmdir", fn_name="trace_unlink")
132b.attach_kprobe(event="vfs_rename", fn_name="trace_rename")
133
134# header
135print("%-8s %-7s %-16s %6s %s" % ("TIME", "PID", "COMM", "ACTION", "FILE"))
136
137# process event
138def 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
147b["events"].open_perf_buffer(print_event)
148while 1:
149    try:
150        b.perf_buffer_poll()
151    except KeyboardInterrupt:
152        exit()
153