blob: 410659d0110dfbba70e29df3b1743eb9805f18db [file] [log] [blame]
Brendan Greggdc642c52016-02-09 00:32:51 -08001#!/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.
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
mcaleavyacfc31502016-02-19 17:59:23 +000024import ctypes as ct
Brendan Greggdc642c52016-02-09 00:32:51 -080025
26# arguments
27examples = """examples:
28 ./filelife # trace all stat() syscalls
29 ./filelife -p 181 # only trace PID 181
30"""
31parser = argparse.ArgumentParser(
32 description="Trace stat() syscalls",
33 formatter_class=argparse.RawDescriptionHelpFormatter,
34 epilog=examples)
35parser.add_argument("-p", "--pid",
36 help="trace this PID only")
Nathan Scottcf0792f2018-02-02 16:56:50 +110037parser.add_argument("--ebpf", action="store_true",
38 help=argparse.SUPPRESS)
Brendan Greggdc642c52016-02-09 00:32:51 -080039args = parser.parse_args()
40debug = 0
41
42# define BPF program
43bpf_text = """
44#include <uapi/linux/ptrace.h>
45#include <linux/fs.h>
mcaleavyacfc31502016-02-19 17:59:23 +000046#include <linux/sched.h>
47
48struct data_t {
49 u32 pid;
50 u64 delta;
51 char comm[TASK_COMM_LEN];
52 char fname[DNAME_INLINE_LEN];
53};
Brendan Greggdc642c52016-02-09 00:32:51 -080054
55BPF_HASH(birth, struct dentry *);
mcaleavyacfc31502016-02-19 17:59:23 +000056BPF_PERF_OUTPUT(events);
Brendan Greggdc642c52016-02-09 00:32:51 -080057
58// trace file creation time
59int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
60{
61 u32 pid = bpf_get_current_pid_tgid();
62 FILTER
63
64 u64 ts = bpf_ktime_get_ns();
65 birth.update(&dentry, &ts);
66
67 return 0;
68};
69
70// trace file deletion and output details
71int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
72{
mcaleavyacfc31502016-02-19 17:59:23 +000073 struct data_t data = {};
Brendan Greggdc642c52016-02-09 00:32:51 -080074 u32 pid = bpf_get_current_pid_tgid();
mcaleavyacfc31502016-02-19 17:59:23 +000075
Brendan Greggdc642c52016-02-09 00:32:51 -080076 FILTER
77
78 u64 *tsp, delta;
79 tsp = birth.lookup(&dentry);
80 if (tsp == 0) {
81 return 0; // missed create
82 }
mcaleavyacfc31502016-02-19 17:59:23 +000083
Brendan Greggdc642c52016-02-09 00:32:51 -080084 delta = (bpf_ktime_get_ns() - *tsp) / 1000000;
85 birth.delete(&dentry);
86
Paul Chaignonf86f7e82018-06-14 02:20:03 +020087 struct qstr d_name = dentry->d_name;
88 if (d_name.len == 0)
Brendan Greggdc642c52016-02-09 00:32:51 -080089 return 0;
90
mcaleavyacfc31502016-02-19 17:59:23 +000091 if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) {
92 data.pid = pid;
93 data.delta = delta;
Paul Chaignonf86f7e82018-06-14 02:20:03 +020094 bpf_probe_read(&data.fname, sizeof(data.fname), d_name.name);
mcaleavyacfc31502016-02-19 17:59:23 +000095 }
96
97 events.perf_submit(ctx, &data, sizeof(data));
Brendan Greggdc642c52016-02-09 00:32:51 -080098
99 return 0;
100}
101"""
mcaleavyacfc31502016-02-19 17:59:23 +0000102
103TASK_COMM_LEN = 16 # linux/sched.h
104DNAME_INLINE_LEN = 255 # linux/dcache.h
105
106class Data(ct.Structure):
107 _fields_ = [
Nan Xiaoe12f55a2017-08-17 10:56:36 +0800108 ("pid", ct.c_uint),
mcaleavyacfc31502016-02-19 17:59:23 +0000109 ("delta", ct.c_ulonglong),
110 ("comm", ct.c_char * TASK_COMM_LEN),
111 ("fname", ct.c_char * DNAME_INLINE_LEN)
112 ]
113
Brendan Greggdc642c52016-02-09 00:32:51 -0800114if args.pid:
115 bpf_text = bpf_text.replace('FILTER',
116 'if (pid != %s) { return 0; }' % args.pid)
117else:
118 bpf_text = bpf_text.replace('FILTER', '')
Nathan Scottcf0792f2018-02-02 16:56:50 +1100119if debug or args.ebpf:
Brendan Greggdc642c52016-02-09 00:32:51 -0800120 print(bpf_text)
Nathan Scottcf0792f2018-02-02 16:56:50 +1100121 if args.ebpf:
122 exit()
Brendan Greggdc642c52016-02-09 00:32:51 -0800123
124# initialize BPF
125b = BPF(text=bpf_text)
126b.attach_kprobe(event="vfs_create", fn_name="trace_create")
Brendan Greggba404cf2016-10-04 18:17:16 -0700127# newer kernels (say, 4.8) may don't fire vfs_create, so record (or overwrite)
128# the timestamp in security_inode_create():
129b.attach_kprobe(event="security_inode_create", fn_name="trace_create")
Brendan Greggdc642c52016-02-09 00:32:51 -0800130b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink")
131
132# header
133print("%-8s %-6s %-16s %-7s %s" % ("TIME", "PID", "COMM", "AGE(s)", "FILE"))
134
mcaleavyacfc31502016-02-19 17:59:23 +0000135# process event
136def print_event(cpu, data, size):
137 event = ct.cast(data, ct.POINTER(Data)).contents
138 print("%-8s %-6d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), event.pid,
jeromemarchandb96ebcd2018-10-10 01:58:15 +0200139 event.comm.decode('utf-8', 'replace'), float(event.delta) / 1000,
140 event.fname.decode('utf-8', 'replace')))
Brendan Greggdc642c52016-02-09 00:32:51 -0800141
mcaleavyacfc31502016-02-19 17:59:23 +0000142b["events"].open_perf_buffer(print_event)
Brendan Greggdc642c52016-02-09 00:32:51 -0800143while 1:
Teng Qindbf00292018-02-28 21:47:50 -0800144 b.perf_buffer_poll()