Commit cfc31503 authored by mcaleavya's avatar mcaleavya

Migrated filelife to bpf_perf_event

parent b1178f6f
...@@ -13,6 +13,10 @@ This works by tracing the kernel vfs_create() and vfs_delete() functions using ...@@ -13,6 +13,10 @@ This works by tracing the kernel vfs_create() and vfs_delete() functions using
dynamic tracing, and will need updating to match any changes to these dynamic tracing, and will need updating to match any changes to these
functions. functions.
This makes use of a Linux 4.5 feature (bpf_perf_event_output());
for kernels older than 4.5, see the version under tools/old,
which uses an older mechanism.
Since this uses BPF, only the root user can use this tool. Since this uses BPF, only the root user can use this tool.
.SH REQUIREMENTS .SH REQUIREMENTS
CONFIG_BPF and bcc. CONFIG_BPF and bcc.
......
...@@ -15,11 +15,13 @@ ...@@ -15,11 +15,13 @@
# Licensed under the Apache License, Version 2.0 (the "License") # Licensed under the Apache License, Version 2.0 (the "License")
# #
# 08-Feb-2015 Brendan Gregg Created this. # 08-Feb-2015 Brendan Gregg Created this.
# 17-Feb-2016 Allan McAleavy updated for BPF_PERF_OUTPUT
from __future__ import print_function from __future__ import print_function
from bcc import BPF from bcc import BPF
import argparse import argparse
from time import strftime from time import strftime
import ctypes as ct
# arguments # arguments
examples = """examples: examples = """examples:
...@@ -39,8 +41,17 @@ debug = 0 ...@@ -39,8 +41,17 @@ debug = 0
bpf_text = """ bpf_text = """
#include <uapi/linux/ptrace.h> #include <uapi/linux/ptrace.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/sched.h>
struct data_t {
u32 pid;
u64 delta;
char comm[TASK_COMM_LEN];
char fname[DNAME_INLINE_LEN];
};
BPF_HASH(birth, struct dentry *); BPF_HASH(birth, struct dentry *);
BPF_PERF_OUTPUT(events);
// trace file creation time // trace file creation time
int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
...@@ -57,7 +68,9 @@ int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) ...@@ -57,7 +68,9 @@ int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
// trace file deletion and output details // trace file deletion and output details
int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
{ {
struct data_t data = {};
u32 pid = bpf_get_current_pid_tgid(); u32 pid = bpf_get_current_pid_tgid();
FILTER FILTER
u64 *tsp, delta; u64 *tsp, delta;
...@@ -65,17 +78,36 @@ int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) ...@@ -65,17 +78,36 @@ int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
if (tsp == 0) { if (tsp == 0) {
return 0; // missed create return 0; // missed create
} }
delta = (bpf_ktime_get_ns() - *tsp) / 1000000; delta = (bpf_ktime_get_ns() - *tsp) / 1000000;
birth.delete(&dentry); birth.delete(&dentry);
if (dentry->d_iname[0] == 0) if (dentry->d_iname[0] == 0)
return 0; return 0;
bpf_trace_printk("%d %s\\n", delta, dentry->d_iname); if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) {
data.pid = pid;
data.delta = delta;
bpf_probe_read(&data.fname, sizeof(data.fname), dentry->d_iname);
}
events.perf_submit(ctx, &data, sizeof(data));
return 0; return 0;
} }
""" """
TASK_COMM_LEN = 16 # linux/sched.h
DNAME_INLINE_LEN = 255 # linux/dcache.h
class Data(ct.Structure):
_fields_ = [
("pid", ct.c_ulonglong),
("delta", ct.c_ulonglong),
("comm", ct.c_char * TASK_COMM_LEN),
("fname", ct.c_char * DNAME_INLINE_LEN)
]
if args.pid: if args.pid:
bpf_text = bpf_text.replace('FILTER', bpf_text = bpf_text.replace('FILTER',
'if (pid != %s) { return 0; }' % args.pid) 'if (pid != %s) { return 0; }' % args.pid)
...@@ -92,13 +124,12 @@ b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink") ...@@ -92,13 +124,12 @@ b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink")
# header # header
print("%-8s %-6s %-16s %-7s %s" % ("TIME", "PID", "COMM", "AGE(s)", "FILE")) print("%-8s %-6s %-16s %-7s %s" % ("TIME", "PID", "COMM", "AGE(s)", "FILE"))
start_ts = 0 # process event
def print_event(cpu, data, size):
event = ct.cast(data, ct.POINTER(Data)).contents
print("%-8s %-6d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), event.pid,
event.comm, float(event.delta) / 1000, event.fname))
# format output b["events"].open_perf_buffer(print_event)
while 1: while 1:
(task, pid, cpu, flags, ts, msg) = b.trace_fields() b.kprobe_poll()
(delta, filename) = msg.split(" ", 1)
# print columns
print("%-8s %-6d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), pid, task,
float(delta) / 1000, filename))
#!/usr/bin/python
# @lint-avoid-python-3-compatibility-imports
#
# filelife Trace the lifespan of short-lived files.
# For Linux, uses BCC, eBPF. Embedded C.
#
# This traces the creation and deletion of files, providing information
# on who deleted the file, the file age, and the file name. The intent is to
# provide information on short-lived files, for debugging or performance
# analysis.
#
# USAGE: filelife [-h] [-p PID]
#
# Copyright 2016 Netflix, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")
#
# 08-Feb-2015 Brendan Gregg Created this.
from __future__ import print_function
from bcc import BPF
import argparse
from time import strftime
# arguments
examples = """examples:
./filelife # trace all stat() syscalls
./filelife -p 181 # only trace PID 181
"""
parser = argparse.ArgumentParser(
description="Trace stat() syscalls",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=examples)
parser.add_argument("-p", "--pid",
help="trace this PID only")
args = parser.parse_args()
debug = 0
# define BPF program
bpf_text = """
#include <uapi/linux/ptrace.h>
#include <linux/fs.h>
BPF_HASH(birth, struct dentry *);
// trace file creation time
int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
{
u32 pid = bpf_get_current_pid_tgid();
FILTER
u64 ts = bpf_ktime_get_ns();
birth.update(&dentry, &ts);
return 0;
};
// trace file deletion and output details
int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
{
u32 pid = bpf_get_current_pid_tgid();
FILTER
u64 *tsp, delta;
tsp = birth.lookup(&dentry);
if (tsp == 0) {
return 0; // missed create
}
delta = (bpf_ktime_get_ns() - *tsp) / 1000000;
birth.delete(&dentry);
if (dentry->d_iname[0] == 0)
return 0;
bpf_trace_printk("%d %s\\n", delta, dentry->d_iname);
return 0;
}
"""
if args.pid:
bpf_text = bpf_text.replace('FILTER',
'if (pid != %s) { return 0; }' % args.pid)
else:
bpf_text = bpf_text.replace('FILTER', '')
if debug:
print(bpf_text)
# initialize BPF
b = BPF(text=bpf_text)
b.attach_kprobe(event="vfs_create", fn_name="trace_create")
b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink")
# header
print("%-8s %-6s %-16s %-7s %s" % ("TIME", "PID", "COMM", "AGE(s)", "FILE"))
start_ts = 0
# format output
while 1:
(task, pid, cpu, flags, ts, msg) = b.trace_fields()
(delta, filename) = msg.split(" ", 1)
# print columns
print("%-8s %-6d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), pid, task,
float(delta) / 1000, filename))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment