|
21 | 21 |
|
22 | 22 | from __future__ import print_function |
23 | 23 | from bcc import BPF |
| 24 | +from bcc.utils import printb |
24 | 25 | import argparse |
25 | 26 | from time import strftime |
26 | 27 |
|
|
35 | 36 | epilog=examples) |
36 | 37 | parser.add_argument("-p", "--pid", |
37 | 38 | help="trace this PID only") |
| 39 | +parser.add_argument("-F", "--full-path", action="store_true", |
| 40 | + help="show full path") |
38 | 41 | parser.add_argument("--ebpf", action="store_true", |
39 | 42 | help=argparse.SUPPRESS) |
40 | 43 | args = parser.parse_args() |
|
45 | 48 | #include <uapi/linux/ptrace.h> |
46 | 49 | #include <linux/fs.h> |
47 | 50 | #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 |
48 | 56 |
|
49 | 57 | struct data_t { |
50 | 58 | u32 pid; |
51 | 59 | u64 delta; |
52 | 60 | char comm[TASK_COMM_LEN]; |
| 61 | + u32 path_depth; |
| 62 | +#ifdef FULLPATH |
| 63 | + FULL_PATH_FIELD(fname); |
| 64 | +#else |
53 | 65 | char fname[DNAME_INLINE_LEN]; |
54 | | - /* private */ |
55 | | - void *dentry; |
| 66 | +#endif |
56 | 67 | }; |
57 | 68 |
|
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); |
61 | 86 |
|
62 | 87 | static int probe_dentry(struct pt_regs *ctx, struct dentry *dentry) |
63 | 88 | { |
| 89 | + struct task_struct *task; |
| 90 | + struct fs_struct *fs; |
| 91 | + struct create_arg arg = {}; |
64 | 92 | u32 pid = bpf_get_current_pid_tgid() >> 32; |
65 | 93 | FILTER |
66 | 94 |
|
67 | 95 | 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); |
69 | 104 |
|
70 | 105 | return 0; |
71 | 106 | } |
|
98 | 133 | // trace file deletion and output details |
99 | 134 | TRACE_UNLINK_FUNC |
100 | 135 | { |
101 | | - struct data_t data = {}; |
| 136 | + struct create_arg *arg; |
| 137 | + struct unlink_event event = {}; |
102 | 138 | u64 pid_tgid = bpf_get_current_pid_tgid(); |
103 | 139 | u32 pid = pid_tgid >> 32; |
104 | 140 | u32 tid = (u32)pid_tgid; |
105 | 141 |
|
106 | 142 | FILTER |
107 | 143 |
|
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) { |
111 | 147 | return 0; // missed create |
112 | 148 | } |
113 | 149 |
|
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; |
125 | 151 |
|
126 | 152 | /* 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; |
128 | 158 |
|
129 | | - unlink_data.update(&tid, &data); |
| 159 | + unlink_data.update(&tid, &event); |
130 | 160 | return 0; |
131 | 161 | } |
132 | 162 |
|
133 | 163 | int trace_unlink_ret(struct pt_regs *ctx) |
134 | 164 | { |
135 | 165 | int ret = PT_REGS_RC(ctx); |
| 166 | + struct unlink_event *unlink_event; |
136 | 167 | struct data_t *data; |
137 | 168 | u32 tid = (u32)bpf_get_current_pid_tgid(); |
138 | 169 |
|
139 | | - data = unlink_data.lookup(&tid); |
140 | | - if (!data) |
| 170 | + unlink_event = unlink_data.lookup(&tid); |
| 171 | + if (!unlink_event) |
141 | 172 | return 0; |
142 | 173 |
|
143 | 174 | /* delete it any way */ |
|
147 | 178 | if (ret) |
148 | 179 | return 0; |
149 | 180 |
|
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)); |
152 | 198 |
|
153 | 199 | return 0; |
154 | 200 | } |
|
183 | 229 | 'if (pid != %s) { return 0; }' % args.pid) |
184 | 230 | else: |
185 | 231 | 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', """""") |
190 | 255 |
|
191 | 256 | if BPF.kernel_struct_has_field(b'renamedata', b'new_mnt_idmap') == 1: |
192 | 257 | bpf_text = bpf_text.replace('TRACE_CREATE_FUNC', trace_create_text_3) |
|
198 | 263 | bpf_text = bpf_text.replace('TRACE_CREATE_FUNC', trace_create_text_1) |
199 | 264 | bpf_text = bpf_text.replace('TRACE_UNLINK_FUNC', trace_unlink_text_1) |
200 | 265 |
|
| 266 | +if debug or args.ebpf: |
| 267 | + print(bpf_text) |
| 268 | + if args.ebpf: |
| 269 | + exit() |
| 270 | + |
201 | 271 | # initialize BPF |
202 | 272 | b = BPF(text=bpf_text) |
203 | 273 | b.attach_kprobe(event="vfs_create", fn_name="trace_create") |
|
216 | 286 | # process event |
217 | 287 | def print_event(cpu, data, size): |
218 | 288 | 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) |
224 | 299 | while 1: |
225 | 300 | try: |
226 | | - b.perf_buffer_poll() |
| 301 | + b.ring_buffer_poll() |
227 | 302 | except KeyboardInterrupt: |
228 | 303 | exit() |
0 commit comments