| Alexey Ivanov | cc01a9c | 2019-01-16 09:50:46 -0800 | [diff] [blame] | 1 | #!/usr/bin/python |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 2 | # @lint-avoid-python-3-compatibility-imports |
| 3 | # |
| 4 | # filelife Trace the lifespan of short-lived files. |
| 5 | # For Linux, uses BCC, eBPF. Embedded C. |
| 6 | # |
| 7 | # This traces the creation and deletion of files, providing information |
| 8 | # on who deleted the file, the file age, and the file name. The intent is to |
| 9 | # provide information on short-lived files, for debugging or performance |
| 10 | # analysis. |
| 11 | # |
| 12 | # USAGE: filelife [-h] [-p PID] |
| 13 | # |
| 14 | # Copyright 2016 Netflix, Inc. |
| 15 | # Licensed under the Apache License, Version 2.0 (the "License") |
| 16 | # |
| 17 | # 08-Feb-2015 Brendan Gregg Created this. |
| mcaleavya | cfc3150 | 2016-02-19 17:59:23 +0000 | [diff] [blame] | 18 | # 17-Feb-2016 Allan McAleavy updated for BPF_PERF_OUTPUT |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 19 | |
| 20 | from __future__ import print_function |
| 21 | from bcc import BPF |
| 22 | import argparse |
| 23 | from time import strftime |
| 24 | |
| 25 | # arguments |
| 26 | examples = """examples: |
| 27 | ./filelife # trace all stat() syscalls |
| 28 | ./filelife -p 181 # only trace PID 181 |
| 29 | """ |
| 30 | parser = argparse.ArgumentParser( |
| 31 | description="Trace stat() syscalls", |
| 32 | formatter_class=argparse.RawDescriptionHelpFormatter, |
| 33 | epilog=examples) |
| 34 | parser.add_argument("-p", "--pid", |
| 35 | help="trace this PID only") |
| Nathan Scott | cf0792f | 2018-02-02 16:56:50 +1100 | [diff] [blame] | 36 | parser.add_argument("--ebpf", action="store_true", |
| 37 | help=argparse.SUPPRESS) |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 38 | args = parser.parse_args() |
| 39 | debug = 0 |
| 40 | |
| 41 | # define BPF program |
| 42 | bpf_text = """ |
| 43 | #include <uapi/linux/ptrace.h> |
| 44 | #include <linux/fs.h> |
| mcaleavya | cfc3150 | 2016-02-19 17:59:23 +0000 | [diff] [blame] | 45 | #include <linux/sched.h> |
| 46 | |
| 47 | struct data_t { |
| 48 | u32 pid; |
| 49 | u64 delta; |
| 50 | char comm[TASK_COMM_LEN]; |
| 51 | char fname[DNAME_INLINE_LEN]; |
| 52 | }; |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 53 | |
| 54 | BPF_HASH(birth, struct dentry *); |
| mcaleavya | cfc3150 | 2016-02-19 17:59:23 +0000 | [diff] [blame] | 55 | BPF_PERF_OUTPUT(events); |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 56 | |
| 57 | // trace file creation time |
| 58 | int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) |
| 59 | { |
| DavadDi | 15d7955 | 2020-04-06 20:22:29 +0800 | [diff] [blame] | 60 | u32 pid = bpf_get_current_pid_tgid() >> 32; |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 61 | FILTER |
| 62 | |
| 63 | u64 ts = bpf_ktime_get_ns(); |
| 64 | birth.update(&dentry, &ts); |
| 65 | |
| 66 | return 0; |
| 67 | }; |
| 68 | |
| 69 | // trace file deletion and output details |
| 70 | int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) |
| 71 | { |
| mcaleavya | cfc3150 | 2016-02-19 17:59:23 +0000 | [diff] [blame] | 72 | struct data_t data = {}; |
| DavadDi | 15d7955 | 2020-04-06 20:22:29 +0800 | [diff] [blame] | 73 | u32 pid = bpf_get_current_pid_tgid() >> 32; |
| mcaleavya | cfc3150 | 2016-02-19 17:59:23 +0000 | [diff] [blame] | 74 | |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 75 | FILTER |
| 76 | |
| 77 | u64 *tsp, delta; |
| 78 | tsp = birth.lookup(&dentry); |
| 79 | if (tsp == 0) { |
| 80 | return 0; // missed create |
| 81 | } |
| mcaleavya | cfc3150 | 2016-02-19 17:59:23 +0000 | [diff] [blame] | 82 | |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 83 | delta = (bpf_ktime_get_ns() - *tsp) / 1000000; |
| 84 | birth.delete(&dentry); |
| 85 | |
| Paul Chaignon | f86f7e8 | 2018-06-14 02:20:03 +0200 | [diff] [blame] | 86 | struct qstr d_name = dentry->d_name; |
| 87 | if (d_name.len == 0) |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 88 | return 0; |
| 89 | |
| mcaleavya | cfc3150 | 2016-02-19 17:59:23 +0000 | [diff] [blame] | 90 | if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) { |
| 91 | data.pid = pid; |
| 92 | data.delta = delta; |
| Sumanth Korikkar | 7f6066d | 2020-05-20 10:49:56 -0500 | [diff] [blame^] | 93 | bpf_probe_read_kernel(&data.fname, sizeof(data.fname), d_name.name); |
| mcaleavya | cfc3150 | 2016-02-19 17:59:23 +0000 | [diff] [blame] | 94 | } |
| 95 | |
| 96 | events.perf_submit(ctx, &data, sizeof(data)); |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 97 | |
| 98 | return 0; |
| 99 | } |
| 100 | """ |
| mcaleavya | cfc3150 | 2016-02-19 17:59:23 +0000 | [diff] [blame] | 101 | |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 102 | if args.pid: |
| 103 | bpf_text = bpf_text.replace('FILTER', |
| 104 | 'if (pid != %s) { return 0; }' % args.pid) |
| 105 | else: |
| 106 | bpf_text = bpf_text.replace('FILTER', '') |
| Nathan Scott | cf0792f | 2018-02-02 16:56:50 +1100 | [diff] [blame] | 107 | if debug or args.ebpf: |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 108 | print(bpf_text) |
| Nathan Scott | cf0792f | 2018-02-02 16:56:50 +1100 | [diff] [blame] | 109 | if args.ebpf: |
| 110 | exit() |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 111 | |
| 112 | # initialize BPF |
| 113 | b = BPF(text=bpf_text) |
| 114 | b.attach_kprobe(event="vfs_create", fn_name="trace_create") |
| Brendan Gregg | ba404cf | 2016-10-04 18:17:16 -0700 | [diff] [blame] | 115 | # newer kernels (say, 4.8) may don't fire vfs_create, so record (or overwrite) |
| 116 | # the timestamp in security_inode_create(): |
| 117 | b.attach_kprobe(event="security_inode_create", fn_name="trace_create") |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 118 | b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink") |
| 119 | |
| 120 | # header |
| 121 | print("%-8s %-6s %-16s %-7s %s" % ("TIME", "PID", "COMM", "AGE(s)", "FILE")) |
| 122 | |
| mcaleavya | cfc3150 | 2016-02-19 17:59:23 +0000 | [diff] [blame] | 123 | # process event |
| 124 | def print_event(cpu, data, size): |
| Xiaozhou Liu | 51d62d3 | 2019-02-15 13:03:05 +0800 | [diff] [blame] | 125 | event = b["events"].event(data) |
| mcaleavya | cfc3150 | 2016-02-19 17:59:23 +0000 | [diff] [blame] | 126 | print("%-8s %-6d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), event.pid, |
| jeromemarchand | b96ebcd | 2018-10-10 01:58:15 +0200 | [diff] [blame] | 127 | event.comm.decode('utf-8', 'replace'), float(event.delta) / 1000, |
| 128 | event.fname.decode('utf-8', 'replace'))) |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 129 | |
| mcaleavya | cfc3150 | 2016-02-19 17:59:23 +0000 | [diff] [blame] | 130 | b["events"].open_perf_buffer(print_event) |
| Brendan Gregg | dc642c5 | 2016-02-09 00:32:51 -0800 | [diff] [blame] | 131 | while 1: |
| Jerome Marchand | 5167127 | 2018-12-19 01:57:24 +0100 | [diff] [blame] | 132 | try: |
| 133 | b.perf_buffer_poll() |
| 134 | except KeyboardInterrupt: |
| 135 | exit() |