blob: 9b7562f4c3cd534740360b49e8e7729471638b14 [file] [log] [blame]
Alexey Ivanovcc01a9c2019-01-16 09:50:46 -08001#!/usr/bin/python
Brendan Greggdc642c52016-02-09 00:32:51 -08002# @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.
mcaleavyacfc31502016-02-19 17:59:23 +000018# 17-Feb-2016 Allan McAleavy updated for BPF_PERF_OUTPUT
Brendan Greggdc642c52016-02-09 00:32:51 -080019
20from __future__ import print_function
21from bcc import BPF
22import argparse
23from time import strftime
24
25# arguments
26examples = """examples:
27 ./filelife # trace all stat() syscalls
28 ./filelife -p 181 # only trace PID 181
29"""
30parser = argparse.ArgumentParser(
31 description="Trace stat() syscalls",
32 formatter_class=argparse.RawDescriptionHelpFormatter,
33 epilog=examples)
34parser.add_argument("-p", "--pid",
35 help="trace this PID only")
Nathan Scottcf0792f2018-02-02 16:56:50 +110036parser.add_argument("--ebpf", action="store_true",
37 help=argparse.SUPPRESS)
Brendan Greggdc642c52016-02-09 00:32:51 -080038args = parser.parse_args()
39debug = 0
40
41# define BPF program
42bpf_text = """
43#include <uapi/linux/ptrace.h>
44#include <linux/fs.h>
mcaleavyacfc31502016-02-19 17:59:23 +000045#include <linux/sched.h>
46
47struct data_t {
48 u32 pid;
49 u64 delta;
50 char comm[TASK_COMM_LEN];
51 char fname[DNAME_INLINE_LEN];
52};
Brendan Greggdc642c52016-02-09 00:32:51 -080053
54BPF_HASH(birth, struct dentry *);
mcaleavyacfc31502016-02-19 17:59:23 +000055BPF_PERF_OUTPUT(events);
Brendan Greggdc642c52016-02-09 00:32:51 -080056
57// trace file creation time
58int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
59{
DavadDi15d79552020-04-06 20:22:29 +080060 u32 pid = bpf_get_current_pid_tgid() >> 32;
Brendan Greggdc642c52016-02-09 00:32:51 -080061 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
70int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
71{
mcaleavyacfc31502016-02-19 17:59:23 +000072 struct data_t data = {};
DavadDi15d79552020-04-06 20:22:29 +080073 u32 pid = bpf_get_current_pid_tgid() >> 32;
mcaleavyacfc31502016-02-19 17:59:23 +000074
Brendan Greggdc642c52016-02-09 00:32:51 -080075 FILTER
76
77 u64 *tsp, delta;
78 tsp = birth.lookup(&dentry);
79 if (tsp == 0) {
80 return 0; // missed create
81 }
mcaleavyacfc31502016-02-19 17:59:23 +000082
Brendan Greggdc642c52016-02-09 00:32:51 -080083 delta = (bpf_ktime_get_ns() - *tsp) / 1000000;
84 birth.delete(&dentry);
85
Paul Chaignonf86f7e82018-06-14 02:20:03 +020086 struct qstr d_name = dentry->d_name;
87 if (d_name.len == 0)
Brendan Greggdc642c52016-02-09 00:32:51 -080088 return 0;
89
mcaleavyacfc31502016-02-19 17:59:23 +000090 if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) {
91 data.pid = pid;
92 data.delta = delta;
Sumanth Korikkar7f6066d2020-05-20 10:49:56 -050093 bpf_probe_read_kernel(&data.fname, sizeof(data.fname), d_name.name);
mcaleavyacfc31502016-02-19 17:59:23 +000094 }
95
96 events.perf_submit(ctx, &data, sizeof(data));
Brendan Greggdc642c52016-02-09 00:32:51 -080097
98 return 0;
99}
100"""
mcaleavyacfc31502016-02-19 17:59:23 +0000101
Brendan Greggdc642c52016-02-09 00:32:51 -0800102if args.pid:
103 bpf_text = bpf_text.replace('FILTER',
104 'if (pid != %s) { return 0; }' % args.pid)
105else:
106 bpf_text = bpf_text.replace('FILTER', '')
Nathan Scottcf0792f2018-02-02 16:56:50 +1100107if debug or args.ebpf:
Brendan Greggdc642c52016-02-09 00:32:51 -0800108 print(bpf_text)
Nathan Scottcf0792f2018-02-02 16:56:50 +1100109 if args.ebpf:
110 exit()
Brendan Greggdc642c52016-02-09 00:32:51 -0800111
112# initialize BPF
113b = BPF(text=bpf_text)
114b.attach_kprobe(event="vfs_create", fn_name="trace_create")
Brendan Greggba404cf2016-10-04 18:17:16 -0700115# newer kernels (say, 4.8) may don't fire vfs_create, so record (or overwrite)
116# the timestamp in security_inode_create():
117b.attach_kprobe(event="security_inode_create", fn_name="trace_create")
Brendan Greggdc642c52016-02-09 00:32:51 -0800118b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink")
119
120# header
121print("%-8s %-6s %-16s %-7s %s" % ("TIME", "PID", "COMM", "AGE(s)", "FILE"))
122
mcaleavyacfc31502016-02-19 17:59:23 +0000123# process event
124def print_event(cpu, data, size):
Xiaozhou Liu51d62d32019-02-15 13:03:05 +0800125 event = b["events"].event(data)
mcaleavyacfc31502016-02-19 17:59:23 +0000126 print("%-8s %-6d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), event.pid,
jeromemarchandb96ebcd2018-10-10 01:58:15 +0200127 event.comm.decode('utf-8', 'replace'), float(event.delta) / 1000,
128 event.fname.decode('utf-8', 'replace')))
Brendan Greggdc642c52016-02-09 00:32:51 -0800129
mcaleavyacfc31502016-02-19 17:59:23 +0000130b["events"].open_perf_buffer(print_event)
Brendan Greggdc642c52016-02-09 00:32:51 -0800131while 1:
Jerome Marchand51671272018-12-19 01:57:24 +0100132 try:
133 b.perf_buffer_poll()
134 except KeyboardInterrupt:
135 exit()