Commit d3583a8d authored by Tim Douglas's avatar Tim Douglas Committed by yonghong-song

opensnoop: print flags, enable filtering (#2096)

* opensnoop: print flags, enable filtering

* Add docs, extended_fields option; filter flags in-kernel

* Homogenize documentation

* Add FLAGS to the FIELDS man page section
parent 51480d05
...@@ -41,6 +41,12 @@ Total duration of trace in seconds. ...@@ -41,6 +41,12 @@ Total duration of trace in seconds.
.TP .TP
\-n name \-n name
Only print processes where its name partially matches 'name' Only print processes where its name partially matches 'name'
.TP
\-e
Show extended fields.
.TP
\-f FLAG
Filter on open() flags, e.g., O_WRONLY.
.SH EXAMPLES .SH EXAMPLES
.TP .TP
Trace all open() syscalls: Trace all open() syscalls:
...@@ -66,6 +72,14 @@ Trace PID 181 only: ...@@ -66,6 +72,14 @@ Trace PID 181 only:
Trace all open() syscalls from processes where its name partially matches 'ed': Trace all open() syscalls from processes where its name partially matches 'ed':
# #
.B opensnoop \-n ed .B opensnoop \-n ed
.TP
Show extended fields:
#
.B opensnoop \-e
.TP
Only print calls for writing:
#
.B opensnoop \-f O_WRONLY \-f O_RDWR
.SH FIELDS .SH FIELDS
.TP .TP
TIME(s) TIME(s)
...@@ -86,6 +100,9 @@ File descriptor (if success), or -1 (if failed) ...@@ -86,6 +100,9 @@ File descriptor (if success), or -1 (if failed)
ERR ERR
Error number (see the system's errno.h) Error number (see the system's errno.h)
.TP .TP
FLAGS
Flags passed to open(2), in octal
.TP
PATH PATH
Open path Open path
.SH OVERHEAD .SH OVERHEAD
......
...@@ -12,12 +12,14 @@ ...@@ -12,12 +12,14 @@
# 17-Sep-2015 Brendan Gregg Created this. # 17-Sep-2015 Brendan Gregg Created this.
# 29-Apr-2016 Allan McAleavy Updated for BPF_PERF_OUTPUT. # 29-Apr-2016 Allan McAleavy Updated for BPF_PERF_OUTPUT.
# 08-Oct-2016 Dina Goldshtein Support filtering by PID and TID. # 08-Oct-2016 Dina Goldshtein Support filtering by PID and TID.
# 28-Dec-2018 Tim Douglas Print flags argument, enable filtering
from __future__ import print_function from __future__ import print_function
from bcc import ArgString, BPF from bcc import ArgString, BPF
import argparse import argparse
import ctypes as ct import ctypes as ct
from datetime import datetime, timedelta from datetime import datetime, timedelta
import os
# arguments # arguments
examples = """examples: examples = """examples:
...@@ -28,6 +30,8 @@ examples = """examples: ...@@ -28,6 +30,8 @@ examples = """examples:
./opensnoop -t 123 # only trace TID 123 ./opensnoop -t 123 # only trace TID 123
./opensnoop -d 10 # trace for 10 seconds only ./opensnoop -d 10 # trace for 10 seconds only
./opensnoop -n main # only print process names containing "main" ./opensnoop -n main # only print process names containing "main"
./opensnoop -e # show extended fields
./opensnoop -f O_WRONLY -f O_RDWR # only print calls for writing
""" """
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Trace open() syscalls", description="Trace open() syscalls",
...@@ -48,10 +52,22 @@ parser.add_argument("-n", "--name", ...@@ -48,10 +52,22 @@ parser.add_argument("-n", "--name",
help="only print process names containing this name") help="only print process names containing this name")
parser.add_argument("--ebpf", action="store_true", parser.add_argument("--ebpf", action="store_true",
help=argparse.SUPPRESS) help=argparse.SUPPRESS)
parser.add_argument("-e", "--extended_fields", action="store_true",
help="show extended fields")
parser.add_argument("-f", "--flag_filter", action="append",
help="filter on flags argument (e.g., O_WRONLY)")
args = parser.parse_args() args = parser.parse_args()
debug = 0 debug = 0
if args.duration: if args.duration:
args.duration = timedelta(seconds=int(args.duration)) args.duration = timedelta(seconds=int(args.duration))
flag_filter_mask = 0
for flag in args.flag_filter or []:
if not flag.startswith('O_'):
exit("Bad flag: %s" % flag)
try:
flag_filter_mask |= getattr(os, flag)
except AttributeError:
exit("Bad flag: %s" % flag)
# define BPF program # define BPF program
bpf_text = """ bpf_text = """
...@@ -63,6 +79,7 @@ struct val_t { ...@@ -63,6 +79,7 @@ struct val_t {
u64 id; u64 id;
char comm[TASK_COMM_LEN]; char comm[TASK_COMM_LEN];
const char *fname; const char *fname;
int flags; // EXTENDED_STRUCT_MEMBER
}; };
struct data_t { struct data_t {
...@@ -71,22 +88,25 @@ struct data_t { ...@@ -71,22 +88,25 @@ struct data_t {
int ret; int ret;
char comm[TASK_COMM_LEN]; char comm[TASK_COMM_LEN];
char fname[NAME_MAX]; char fname[NAME_MAX];
int flags; // EXTENDED_STRUCT_MEMBER
}; };
BPF_HASH(infotmp, u64, struct val_t); BPF_HASH(infotmp, u64, struct val_t);
BPF_PERF_OUTPUT(events); BPF_PERF_OUTPUT(events);
int trace_entry(struct pt_regs *ctx, int dfd, const char __user *filename) int trace_entry(struct pt_regs *ctx, int dfd, const char __user *filename, int flags)
{ {
struct val_t val = {}; struct val_t val = {};
u64 id = bpf_get_current_pid_tgid(); u64 id = bpf_get_current_pid_tgid();
u32 pid = id >> 32; // PID is higher part u32 pid = id >> 32; // PID is higher part
u32 tid = id; // Cast and get the lower part u32 tid = id; // Cast and get the lower part
FILTER PID_TID_FILTER
FLAGS_FILTER
if (bpf_get_current_comm(&val.comm, sizeof(val.comm)) == 0) { if (bpf_get_current_comm(&val.comm, sizeof(val.comm)) == 0) {
val.id = id; val.id = id;
val.fname = filename; val.fname = filename;
val.flags = flags; // EXTENDED_STRUCT_MEMBER
infotmp.update(&id, &val); infotmp.update(&id, &val);
} }
...@@ -110,6 +130,7 @@ int trace_return(struct pt_regs *ctx) ...@@ -110,6 +130,7 @@ int trace_return(struct pt_regs *ctx)
bpf_probe_read(&data.fname, sizeof(data.fname), (void *)valp->fname); bpf_probe_read(&data.fname, sizeof(data.fname), (void *)valp->fname);
data.id = valp->id; data.id = valp->id;
data.ts = tsp / 1000; data.ts = tsp / 1000;
data.flags = valp->flags; // EXTENDED_STRUCT_MEMBER
data.ret = PT_REGS_RC(ctx); data.ret = PT_REGS_RC(ctx);
events.perf_submit(ctx, &data, sizeof(data)); events.perf_submit(ctx, &data, sizeof(data));
...@@ -119,13 +140,21 @@ int trace_return(struct pt_regs *ctx) ...@@ -119,13 +140,21 @@ int trace_return(struct pt_regs *ctx)
} }
""" """
if args.tid: # TID trumps PID if args.tid: # TID trumps PID
bpf_text = bpf_text.replace('FILTER', bpf_text = bpf_text.replace('PID_TID_FILTER',
'if (tid != %s) { return 0; }' % args.tid) 'if (tid != %s) { return 0; }' % args.tid)
elif args.pid: elif args.pid:
bpf_text = bpf_text.replace('FILTER', bpf_text = bpf_text.replace('PID_TID_FILTER',
'if (pid != %s) { return 0; }' % args.pid) 'if (pid != %s) { return 0; }' % args.pid)
else: else:
bpf_text = bpf_text.replace('FILTER', '') bpf_text = bpf_text.replace('PID_TID_FILTER', '')
if args.flag_filter:
bpf_text = bpf_text.replace('FLAGS_FILTER',
'if (!(flags & %d)) { return 0; }' % flag_filter_mask)
else:
bpf_text = bpf_text.replace('FLAGS_FILTER', '')
if not (args.extended_fields or args.flag_filter):
bpf_text = '\n'.join(x for x in bpf_text.split('\n')
if 'EXTENDED_STRUCT_MEMBER' not in x)
if debug or args.ebpf: if debug or args.ebpf:
print(bpf_text) print(bpf_text)
if args.ebpf: if args.ebpf:
...@@ -145,7 +174,8 @@ class Data(ct.Structure): ...@@ -145,7 +174,8 @@ class Data(ct.Structure):
("ts", ct.c_ulonglong), ("ts", ct.c_ulonglong),
("ret", ct.c_int), ("ret", ct.c_int),
("comm", ct.c_char * TASK_COMM_LEN), ("comm", ct.c_char * TASK_COMM_LEN),
("fname", ct.c_char * NAME_MAX) ("fname", ct.c_char * NAME_MAX),
("flags", ct.c_int),
] ]
initial_ts = 0 initial_ts = 0
...@@ -153,8 +183,11 @@ initial_ts = 0 ...@@ -153,8 +183,11 @@ initial_ts = 0
# header # header
if args.timestamp: if args.timestamp:
print("%-14s" % ("TIME(s)"), end="") print("%-14s" % ("TIME(s)"), end="")
print("%-6s %-16s %4s %3s %s" % print("%-6s %-16s %4s %3s " %
("TID" if args.tid else "PID", "COMM", "FD", "ERR", "PATH")) ("TID" if args.tid else "PID", "COMM", "FD", "ERR"), end="")
if args.extended_fields:
print("%-9s" % ("FLAGS"), end="")
print("PATH")
# process event # process event
def print_event(cpu, data, size): def print_event(cpu, data, size):
...@@ -182,10 +215,14 @@ def print_event(cpu, data, size): ...@@ -182,10 +215,14 @@ def print_event(cpu, data, size):
delta = event.ts - initial_ts delta = event.ts - initial_ts
print("%-14.9f" % (float(delta) / 1000000), end="") print("%-14.9f" % (float(delta) / 1000000), end="")
print("%-6d %-16s %4d %3d %s" % print("%-6d %-16s %4d %3d " %
(event.id & 0xffffffff if args.tid else event.id >> 32, (event.id & 0xffffffff if args.tid else event.id >> 32,
event.comm.decode('utf-8', 'replace'), fd_s, err, event.comm.decode('utf-8', 'replace'), fd_s, err), end="")
event.fname.decode('utf-8', 'replace')))
if args.extended_fields:
print("%08o " % event.flags, end="")
print(event.fname.decode('utf-8', 'replace'))
# loop with callback to print_event # loop with callback to print_event
b["events"].open_perf_buffer(print_event, page_cnt=64) b["events"].open_perf_buffer(print_event, page_cnt=64)
......
...@@ -132,10 +132,40 @@ This caught the 'sed' command because it partially matches 'ed' that's passed ...@@ -132,10 +132,40 @@ This caught the 'sed' command because it partially matches 'ed' that's passed
to the '-n' option. to the '-n' option.
The -e option prints out extra columns; for example, the following output
contains the flags passed to open(2), in octal:
# ./opensnoop -e
PID COMM FD ERR FLAGS PATH
28512 sshd 10 0 00101101 /proc/self/oom_score_adj
28512 sshd 3 0 02100000 /etc/ld.so.cache
28512 sshd 3 0 02100000 /lib/x86_64-linux-gnu/libwrap.so.0
28512 sshd 3 0 02100000 /lib/x86_64-linux-gnu/libaudit.so.1
28512 sshd 3 0 02100000 /lib/x86_64-linux-gnu/libpam.so.0
28512 sshd 3 0 02100000 /lib/x86_64-linux-gnu/libselinux.so.1
28512 sshd 3 0 02100000 /lib/x86_64-linux-gnu/libsystemd.so.0
28512 sshd 3 0 02100000 /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.2
28512 sshd 3 0 02100000 /lib/x86_64-linux-gnu/libutil.so.1
The -f option filters based on flags to the open(2) call, for example:
# ./opensnoop -e -f O_WRONLY -f O_RDWR
PID COMM FD ERR FLAGS PATH
28084 clear_console 3 0 00100002 /dev/tty
28084 clear_console -1 13 00100002 /dev/tty0
28084 clear_console -1 13 00100001 /dev/tty0
28084 clear_console -1 13 00100002 /dev/console
28084 clear_console -1 13 00100001 /dev/console
28051 sshd 8 0 02100002 /var/run/utmp
28051 sshd 7 0 00100001 /var/log/wtmp
USAGE message: USAGE message:
# ./opensnoop -h # ./opensnoop -h
usage: opensnoop [-h] [-T] [-x] [-p PID] [-t TID] [-d DURATION] [-n NAME] usage: opensnoop [-h] [-T] [-x] [-p PID] [-t TID] [-d DURATION] [-n NAME]
[-e] [-f FLAG_FILTER]
Trace open() syscalls Trace open() syscalls
...@@ -148,6 +178,10 @@ optional arguments: ...@@ -148,6 +178,10 @@ optional arguments:
-d DURATION, --duration DURATION -d DURATION, --duration DURATION
total duration of trace in seconds total duration of trace in seconds
-n NAME, --name NAME only print process names containing this name -n NAME, --name NAME only print process names containing this name
-e, --extended_fields
show extended fields
-f FLAG_FILTER, --flag_filter FLAG_FILTER
filter on flags argument (e.g., O_WRONLY)
examples: examples:
./opensnoop # trace all open() syscalls ./opensnoop # trace all open() syscalls
...@@ -157,3 +191,5 @@ examples: ...@@ -157,3 +191,5 @@ examples:
./opensnoop -t 123 # only trace TID 123 ./opensnoop -t 123 # only trace TID 123
./opensnoop -d 10 # trace for 10 seconds only ./opensnoop -d 10 # trace for 10 seconds only
./opensnoop -n main # only print process names containing "main" ./opensnoop -n main # only print process names containing "main"
./opensnoop -e # show extended fields
./opensnoop -f O_WRONLY -f O_RDWR # only print calls for writing
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