opensnoop.py 4.36 KB
Newer Older
Brendan Gregg's avatar
Brendan Gregg committed
1
#!/usr/bin/python
2
# @lint-avoid-python-3-compatibility-imports
Brendan Gregg's avatar
Brendan Gregg committed
3
#
4 5
# opensnoop Trace open() syscalls.
#           For Linux, uses BCC, eBPF. Embedded C.
Brendan Gregg's avatar
Brendan Gregg committed
6 7 8 9 10 11
#
# USAGE: opensnoop [-h] [-t] [-x] [-p PID]
#
# Copyright (c) 2015 Brendan Gregg.
# Licensed under the Apache License, Version 2.0 (the "License")
#
12
# 17-Sep-2015   Brendan Gregg   Created this.
13
# 29-Apr-2016   Allan McAleavy updated for BPF_PERF_OUTPUT
Brendan Gregg's avatar
Brendan Gregg committed
14 15 16 17

from __future__ import print_function
from bcc import BPF
import argparse
18
import ctypes as ct
Brendan Gregg's avatar
Brendan Gregg committed
19 20 21 22 23 24 25 26 27

# arguments
examples = """examples:
    ./opensnoop           # trace all open() syscalls
    ./opensnoop -t        # include timestamps
    ./opensnoop -x        # only show failed opens
    ./opensnoop -p 181    # only trace PID 181
"""
parser = argparse.ArgumentParser(
28 29 30
    description="Trace open() syscalls",
    formatter_class=argparse.RawDescriptionHelpFormatter,
    epilog=examples)
Brendan Gregg's avatar
Brendan Gregg committed
31
parser.add_argument("-t", "--timestamp", action="store_true",
32
    help="include timestamp on output")
Brendan Gregg's avatar
Brendan Gregg committed
33
parser.add_argument("-x", "--failed", action="store_true",
34
    help="only show failed opens")
Brendan Gregg's avatar
Brendan Gregg committed
35
parser.add_argument("-p", "--pid",
36
    help="trace this PID only")
Brendan Gregg's avatar
Brendan Gregg committed
37 38 39 40 41 42
args = parser.parse_args()
debug = 0

# define BPF program
bpf_text = """
#include <uapi/linux/ptrace.h>
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
#include <uapi/linux/limits.h>
#include <linux/sched.h>

struct val_t {
    u32 pid;
    u64 ts;
    char comm[TASK_COMM_LEN];
    const char *fname;
};

struct data_t {
    u32 pid;
    u64 ts;
    u64 delta;
    int ret;
    char comm[TASK_COMM_LEN];
    char fname[NAME_MAX];
};
Brendan Gregg's avatar
Brendan Gregg committed
61 62

BPF_HASH(args_filename, u32, const char *);
63 64
BPF_HASH(infotmp, u32, struct val_t);
BPF_PERF_OUTPUT(events);
Brendan Gregg's avatar
Brendan Gregg committed
65

66
int trace_entry(struct pt_regs *ctx, const char __user *filename)
Brendan Gregg's avatar
Brendan Gregg committed
67
{
68
    struct val_t val = {};
69
    u32 pid = bpf_get_current_pid_tgid();
Brendan Gregg's avatar
Brendan Gregg committed
70

71
    FILTER
72 73 74 75 76 77
    if (bpf_get_current_comm(&val.comm, sizeof(val.comm)) == 0) {
        val.pid = bpf_get_current_pid_tgid();
        val.ts = bpf_ktime_get_ns();
        val.fname = filename;
        infotmp.update(&pid, &val);
    }
Brendan Gregg's avatar
Brendan Gregg committed
78

79
    return 0;
Brendan Gregg's avatar
Brendan Gregg committed
80 81
};

82
int trace_return(struct pt_regs *ctx)
Brendan Gregg's avatar
Brendan Gregg committed
83
{
84
    u32 pid = bpf_get_current_pid_tgid();
85 86 87 88
    struct val_t *valp;
    struct data_t data = {};

    u64 tsp = bpf_ktime_get_ns();
Brendan Gregg's avatar
Brendan Gregg committed
89

90 91
    valp = infotmp.lookup(&pid);
    if (valp == 0) {
92 93 94
        // missed entry
        return 0;
    }
95 96 97 98 99
    bpf_probe_read(&data.comm, sizeof(data.comm), valp->comm);
    bpf_probe_read(&data.fname, sizeof(data.fname), (void *)valp->fname);
    data.pid = valp->pid;
    data.delta = tsp - valp->ts;
    data.ts = tsp / 1000;
100
    data.ret = PT_REGS_RC(ctx);
101 102 103

    events.perf_submit(ctx, &data, sizeof(data));
    infotmp.delete(&pid);
104
    args_filename.delete(&pid);
Brendan Gregg's avatar
Brendan Gregg committed
105

106
    return 0;
Brendan Gregg's avatar
Brendan Gregg committed
107 108 109
}
"""
if args.pid:
110 111
    bpf_text = bpf_text.replace('FILTER',
        'if (pid != %s) { return 0; }' % args.pid)
Brendan Gregg's avatar
Brendan Gregg committed
112
else:
113
    bpf_text = bpf_text.replace('FILTER', '')
Brendan Gregg's avatar
Brendan Gregg committed
114
if debug:
115
    print(bpf_text)
Brendan Gregg's avatar
Brendan Gregg committed
116 117 118

# initialize BPF
b = BPF(text=bpf_text)
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
b.attach_kprobe(event="sys_open", fn_name="trace_entry")
b.attach_kretprobe(event="sys_open", fn_name="trace_return")

TASK_COMM_LEN = 16    # linux/sched.h
NAME_MAX = 255        # linux/limits.h

class Data(ct.Structure):
    _fields_ = [
        ("pid", ct.c_ulonglong),
        ("ts", ct.c_ulonglong),
        ("delta", ct.c_ulonglong),
        ("ret", ct.c_int),
        ("comm", ct.c_char * TASK_COMM_LEN),
        ("fname", ct.c_char * NAME_MAX)
    ]

start_ts = 0
prev_ts = 0
delta = 0
Brendan Gregg's avatar
Brendan Gregg committed
138 139 140

# header
if args.timestamp:
141
    print("%-14s" % ("TIME(s)"), end="")
Brendan Gregg's avatar
Brendan Gregg committed
142 143
print("%-6s %-16s %4s %3s %s" % ("PID", "COMM", "FD", "ERR", "PATH"))

144 145 146 147 148 149 150
# process event
def print_event(cpu, data, size):
    event = ct.cast(data, ct.POINTER(Data)).contents
    global start_ts
    global prev_ts
    global delta
    global cont
151 152

    # split return value into FD and errno columns
153 154
    if event.ret >= 0:
        fd_s = event.ret
155 156
        err = 0
    else:
157 158 159 160 161 162 163 164 165 166 167 168 169
        fd_s = -1
        err = - event.ret

    if start_ts == 0:
        prev_ts = start_ts

    if start_ts == 1:
        delta = float(delta) + (event.ts - prev_ts)

    if (args.failed and (event.ret >= 0)):
        start_ts = 1
        prev_ts = event.ts
        return
170 171

    if args.timestamp:
172 173 174 175 176 177 178 179 180 181 182 183
        print("%-14.9f" % (delta / 1000000), end="")

    print("%-6d %-16s %4d %3d %s" % (event.pid, event.comm,
        fd_s, err, event.fname))

    prev_ts = event.ts
    start_ts = 1

# loop with callback to print_event
b["events"].open_perf_buffer(print_event)
while 1:
    b.kprobe_poll()