1 #!/usr/bin/python 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. 18 19 from __future__ import print_function 20 from bcc import BPF 21 import argparse 22 from time import strftime 23 24 # arguments 25 examples = """examples: 26 ./filelife # trace all stat() syscalls 27 ./filelife -p 181 # only trace PID 181 28 """ 29 parser = argparse.ArgumentParser( 30 description="Trace stat() syscalls", 31 formatter_class=argparse.RawDescriptionHelpFormatter, 32 epilog=examples) 33 parser.add_argument("-p", "--pid", 34 help="trace this PID only") 35 args = parser.parse_args() 36 debug = 0 37 38 # define BPF program 39 bpf_text = """ 40 #include <uapi/linux/ptrace.h> 41 #include <linux/fs.h> 42 43 BPF_HASH(birth, struct dentry *); 44 45 // trace file creation time 46 int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) 47 { 48 u32 pid = bpf_get_current_pid_tgid(); 49 FILTER 50 51 u64 ts = bpf_ktime_get_ns(); 52 birth.update(&dentry, &ts); 53 54 return 0; 55 }; 56 57 // trace file deletion and output details 58 int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) 59 { 60 u32 pid = bpf_get_current_pid_tgid(); 61 FILTER 62 63 u64 *tsp, delta; 64 tsp = birth.lookup(&dentry); 65 if (tsp == 0) { 66 return 0; // missed create 67 } 68 delta = (bpf_ktime_get_ns() - *tsp) / 1000000; 69 birth.delete(&dentry); 70 71 if (dentry->d_iname[0] == 0) 72 return 0; 73 74 bpf_trace_printk("%d %s\\n", delta, dentry->d_iname); 75 76 return 0; 77 } 78 """ 79 if args.pid: 80 bpf_text = bpf_text.replace('FILTER', 81 'if (pid != %s) { return 0; }' % args.pid) 82 else: 83 bpf_text = bpf_text.replace('FILTER', '') 84 if debug: 85 print(bpf_text) 86 87 # initialize BPF 88 b = BPF(text=bpf_text) 89 b.attach_kprobe(event="vfs_create", fn_name="trace_create") 90 b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink") 91 92 # header 93 print("%-8s %-6s %-16s %-7s %s" % ("TIME", "PID", "COMM", "AGE(s)", "FILE")) 94 95 start_ts = 0 96 97 # format output 98 while 1: 99 (task, pid, cpu, flags, ts, msg) = b.trace_fields() 100 (delta, filename) = msg.split(" ", 1) 101 102 # print columns 103 print("%-8s %-6d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), pid, task, 104 float(delta) / 1000, filename)) 105