blob: 6fd8049c1e1b2866bd016e8f7b6303a9cd03c928 [file] [log] [blame]
Brendan Greggad341c92016-02-09 00:31:24 -08001#!/usr/bin/python
2# @lint-avoid-python-3-compatibility-imports
3#
4# statsnoop Trace stat() syscalls.
5# For Linux, uses BCC, eBPF. Embedded C.
6#
7# USAGE: statsnoop [-h] [-t] [-x] [-p PID]
8#
9# Copyright 2016 Netflix, Inc.
10# Licensed under the Apache License, Version 2.0 (the "License")
11#
12# 08-Feb-2016 Brendan Gregg Created this.
mcaleavyae14f27f2016-02-17 22:12:44 +000013# 17-Feb-2016 Allan McAleavy updated for BPF_PERF_OUTPUT
Brendan Greggad341c92016-02-09 00:31:24 -080014
15from __future__ import print_function
16from bcc import BPF
17import argparse
mcaleavyae14f27f2016-02-17 22:12:44 +000018import ctypes as ct
Brendan Greggad341c92016-02-09 00:31:24 -080019
20# arguments
21examples = """examples:
22 ./statsnoop # trace all stat() syscalls
23 ./statsnoop -t # include timestamps
24 ./statsnoop -x # only show failed stats
25 ./statsnoop -p 181 # only trace PID 181
26"""
27parser = argparse.ArgumentParser(
28 description="Trace stat() syscalls",
29 formatter_class=argparse.RawDescriptionHelpFormatter,
30 epilog=examples)
31parser.add_argument("-t", "--timestamp", action="store_true",
32 help="include timestamp on output")
33parser.add_argument("-x", "--failed", action="store_true",
34 help="only show failed stats")
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 Greggad341c92016-02-09 00:31:24 -080039args = parser.parse_args()
40debug = 0
41
42# define BPF program
43bpf_text = """
44#include <uapi/linux/ptrace.h>
mcaleavyae14f27f2016-02-17 22:12:44 +000045#include <uapi/linux/limits.h>
46#include <linux/sched.h>
47
48struct val_t {
mcaleavyae14f27f2016-02-17 22:12:44 +000049 const char *fname;
50};
51
52struct data_t {
53 u32 pid;
Brendan Greggf4ce31a2017-01-10 22:31:30 -080054 u64 ts_ns;
mcaleavyae14f27f2016-02-17 22:12:44 +000055 int ret;
56 char comm[TASK_COMM_LEN];
57 char fname[NAME_MAX];
58};
Brendan Greggad341c92016-02-09 00:31:24 -080059
60BPF_HASH(args_filename, u32, const char *);
mcaleavya1a7bebf2016-02-18 22:30:33 +000061BPF_HASH(infotmp, u32, struct val_t);
mcaleavyae14f27f2016-02-17 22:12:44 +000062BPF_PERF_OUTPUT(events);
Brendan Greggad341c92016-02-09 00:31:24 -080063
yonghong-song2da34262018-06-13 06:12:22 -070064int syscall__entry(struct pt_regs *ctx, const char __user *filename)
Brendan Greggad341c92016-02-09 00:31:24 -080065{
mcaleavyae14f27f2016-02-17 22:12:44 +000066 struct val_t val = {};
Brendan Greggad341c92016-02-09 00:31:24 -080067 u32 pid = bpf_get_current_pid_tgid();
68
69 FILTER
Brendan Greggf4ce31a2017-01-10 22:31:30 -080070 val.fname = filename;
71 infotmp.update(&pid, &val);
Brendan Greggad341c92016-02-09 00:31:24 -080072
73 return 0;
74};
75
76int trace_return(struct pt_regs *ctx)
77{
Brendan Greggad341c92016-02-09 00:31:24 -080078 u32 pid = bpf_get_current_pid_tgid();
mcaleavyae14f27f2016-02-17 22:12:44 +000079 struct val_t *valp;
mcaleavyae14f27f2016-02-17 22:12:44 +000080
81 valp = infotmp.lookup(&pid);
82 if (valp == 0) {
Brendan Greggad341c92016-02-09 00:31:24 -080083 // missed entry
84 return 0;
85 }
Brendan Greggf4ce31a2017-01-10 22:31:30 -080086
87 struct data_t data = {.pid = pid};
mcaleavya1a7bebf2016-02-18 22:30:33 +000088 bpf_probe_read(&data.fname, sizeof(data.fname), (void *)valp->fname);
Brendan Greggf4ce31a2017-01-10 22:31:30 -080089 bpf_get_current_comm(&data.comm, sizeof(data.comm));
90 data.ts_ns = bpf_ktime_get_ns();
Naveen N. Rao4afa96a2016-05-03 14:54:21 +053091 data.ret = PT_REGS_RC(ctx);
Brendan Greggad341c92016-02-09 00:31:24 -080092
mcaleavya1a7bebf2016-02-18 22:30:33 +000093 events.perf_submit(ctx, &data, sizeof(data));
mcaleavyae14f27f2016-02-17 22:12:44 +000094 infotmp.delete(&pid);
Brendan Greggad341c92016-02-09 00:31:24 -080095 args_filename.delete(&pid);
96
97 return 0;
98}
99"""
100if args.pid:
101 bpf_text = bpf_text.replace('FILTER',
102 'if (pid != %s) { return 0; }' % args.pid)
103else:
104 bpf_text = bpf_text.replace('FILTER', '')
Nathan Scottcf0792f2018-02-02 16:56:50 +1100105if debug or args.ebpf:
Brendan Greggad341c92016-02-09 00:31:24 -0800106 print(bpf_text)
Nathan Scottcf0792f2018-02-02 16:56:50 +1100107 if args.ebpf:
108 exit()
Brendan Greggad341c92016-02-09 00:31:24 -0800109
110# initialize BPF
111b = BPF(text=bpf_text)
Sandipan Das16523a32017-10-05 12:13:12 +0530112
113# for POSIX compliance, all architectures implement these
114# system calls but the name of the actual entry point may
115# be different for which we must check if the entry points
116# actually exist before attaching the probes
Yonghong Song64335692018-04-25 00:40:13 -0700117syscall_fnname = b.get_syscall_fnname("stat")
118if BPF.ksymname(syscall_fnname) != -1:
yonghong-song2da34262018-06-13 06:12:22 -0700119 b.attach_kprobe(event=syscall_fnname, fn_name="syscall__entry")
Yonghong Song64335692018-04-25 00:40:13 -0700120 b.attach_kretprobe(event=syscall_fnname, fn_name="trace_return")
Sandipan Das16523a32017-10-05 12:13:12 +0530121
Yonghong Song64335692018-04-25 00:40:13 -0700122syscall_fnname = b.get_syscall_fnname("statfs")
123if BPF.ksymname(syscall_fnname) != -1:
yonghong-song2da34262018-06-13 06:12:22 -0700124 b.attach_kprobe(event=syscall_fnname, fn_name="syscall__entry")
Yonghong Song64335692018-04-25 00:40:13 -0700125 b.attach_kretprobe(event=syscall_fnname, fn_name="trace_return")
Sandipan Das16523a32017-10-05 12:13:12 +0530126
Yonghong Song64335692018-04-25 00:40:13 -0700127syscall_fnname = b.get_syscall_fnname("newstat")
128if BPF.ksymname(syscall_fnname) != -1:
yonghong-song2da34262018-06-13 06:12:22 -0700129 b.attach_kprobe(event=syscall_fnname, fn_name="syscall__entry")
Yonghong Song64335692018-04-25 00:40:13 -0700130 b.attach_kretprobe(event=syscall_fnname, fn_name="trace_return")
Brendan Greggad341c92016-02-09 00:31:24 -0800131
mcaleavyae14f27f2016-02-17 22:12:44 +0000132TASK_COMM_LEN = 16 # linux/sched.h
133NAME_MAX = 255 # linux/limits.h
134
135class Data(ct.Structure):
136 _fields_ = [
137 ("pid", ct.c_ulonglong),
Brendan Greggf4ce31a2017-01-10 22:31:30 -0800138 ("ts_ns", ct.c_ulonglong),
mcaleavyae14f27f2016-02-17 22:12:44 +0000139 ("ret", ct.c_int),
140 ("comm", ct.c_char * TASK_COMM_LEN),
141 ("fname", ct.c_char * NAME_MAX)
142 ]
143
144start_ts = 0
145prev_ts = 0
146delta = 0
147
Brendan Greggad341c92016-02-09 00:31:24 -0800148# header
149if args.timestamp:
150 print("%-14s" % ("TIME(s)"), end="")
151print("%-6s %-16s %4s %3s %s" % ("PID", "COMM", "FD", "ERR", "PATH"))
152
mcaleavyae14f27f2016-02-17 22:12:44 +0000153# process event
154def print_event(cpu, data, size):
155 event = ct.cast(data, ct.POINTER(Data)).contents
156 global start_ts
157 global prev_ts
158 global delta
159 global cont
Brendan Greggad341c92016-02-09 00:31:24 -0800160
161 # split return value into FD and errno columns
mcaleavyae14f27f2016-02-17 22:12:44 +0000162 if event.ret >= 0:
163 fd_s = event.ret
Brendan Greggad341c92016-02-09 00:31:24 -0800164 err = 0
165 else:
mcaleavyae14f27f2016-02-17 22:12:44 +0000166 fd_s = -1
167 err = - event.ret
Brendan Greggad341c92016-02-09 00:31:24 -0800168
mcaleavyae14f27f2016-02-17 22:12:44 +0000169 if start_ts == 0:
Brendan Greggf4ce31a2017-01-10 22:31:30 -0800170 start_ts = event.ts_ns
mcaleavyae14f27f2016-02-17 22:12:44 +0000171
Brendan Greggad341c92016-02-09 00:31:24 -0800172 if args.timestamp:
Brendan Greggf4ce31a2017-01-10 22:31:30 -0800173 print("%-14.9f" % (float(event.ts_ns - start_ts) / 1000000000), end="")
mcaleavyae14f27f2016-02-17 22:12:44 +0000174
Rafael F78948e42017-03-26 14:54:25 +0200175 print("%-6d %-16s %4d %3d %s" % (event.pid, event.comm.decode(),
176 fd_s, err, event.fname.decode()))
mcaleavyae14f27f2016-02-17 22:12:44 +0000177
mcaleavyae14f27f2016-02-17 22:12:44 +0000178# loop with callback to print_event
Mark Drayton5f5687e2017-02-20 18:13:03 +0000179b["events"].open_perf_buffer(print_event, page_cnt=64)
mcaleavyae14f27f2016-02-17 22:12:44 +0000180while 1:
Teng Qindbf00292018-02-28 21:47:50 -0800181 b.perf_buffer_poll()