• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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# 17-Feb-2016   Allan McAleavy updated for BPF_PERF_OUTPUT
19
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")
36parser.add_argument("--ebpf", action="store_true",
37    help=argparse.SUPPRESS)
38args = parser.parse_args()
39debug = 0
40
41# define BPF program
42bpf_text = """
43#include <uapi/linux/ptrace.h>
44#include <linux/fs.h>
45#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};
53
54BPF_HASH(birth, struct dentry *);
55BPF_PERF_OUTPUT(events);
56
57// trace file creation time
58int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
59{
60    u32 pid = bpf_get_current_pid_tgid() >> 32;
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
70int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
71{
72    struct data_t data = {};
73    u32 pid = bpf_get_current_pid_tgid() >> 32;
74
75    FILTER
76
77    u64 *tsp, delta;
78    tsp = birth.lookup(&dentry);
79    if (tsp == 0) {
80        return 0;   // missed create
81    }
82
83    delta = (bpf_ktime_get_ns() - *tsp) / 1000000;
84    birth.delete(&dentry);
85
86    struct qstr d_name = dentry->d_name;
87    if (d_name.len == 0)
88        return 0;
89
90    if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) {
91        data.pid = pid;
92        data.delta = delta;
93        bpf_probe_read_kernel(&data.fname, sizeof(data.fname), d_name.name);
94    }
95
96    events.perf_submit(ctx, &data, sizeof(data));
97
98    return 0;
99}
100"""
101
102if 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', '')
107if debug or args.ebpf:
108    print(bpf_text)
109    if args.ebpf:
110        exit()
111
112# initialize BPF
113b = BPF(text=bpf_text)
114b.attach_kprobe(event="vfs_create", fn_name="trace_create")
115# 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")
118b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink")
119
120# header
121print("%-8s %-7s %-16s %-7s %s" % ("TIME", "PID", "COMM", "AGE(s)", "FILE"))
122
123# process event
124def print_event(cpu, data, size):
125    event = b["events"].event(data)
126    print("%-8s %-7d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), event.pid,
127        event.comm.decode('utf-8', 'replace'), float(event.delta) / 1000,
128        event.fname.decode('utf-8', 'replace')))
129
130b["events"].open_perf_buffer(print_event)
131while 1:
132    try:
133        b.perf_buffer_poll()
134    except KeyboardInterrupt:
135        exit()
136