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.
.TP
\-n 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
.TP
Trace all open() syscalls:
......@@ -66,6 +72,14 @@ Trace PID 181 only:
Trace all open() syscalls from processes where its name partially matches '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
.TP
TIME(s)
......@@ -86,6 +100,9 @@ File descriptor (if success), or -1 (if failed)
ERR
Error number (see the system's errno.h)
.TP
FLAGS
Flags passed to open(2), in octal
.TP
PATH
Open path
.SH OVERHEAD
......
......@@ -12,12 +12,14 @@
# 17-Sep-2015 Brendan Gregg Created this.
# 29-Apr-2016 Allan McAleavy Updated for BPF_PERF_OUTPUT.
# 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 bcc import ArgString, BPF
import argparse
import ctypes as ct
from datetime import datetime, timedelta
import os
# arguments
examples = """examples:
......@@ -28,6 +30,8 @@ examples = """examples:
./opensnoop -t 123 # only trace TID 123
./opensnoop -d 10 # trace for 10 seconds only
./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(
description="Trace open() syscalls",
......@@ -48,10 +52,22 @@ parser.add_argument("-n", "--name",
help="only print process names containing this name")
parser.add_argument("--ebpf", action="store_true",
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()
debug = 0
if 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
bpf_text = """
......@@ -63,6 +79,7 @@ struct val_t {
u64 id;
char comm[TASK_COMM_LEN];
const char *fname;
int flags; // EXTENDED_STRUCT_MEMBER
};
struct data_t {
......@@ -71,22 +88,25 @@ struct data_t {
int ret;
char comm[TASK_COMM_LEN];
char fname[NAME_MAX];
int flags; // EXTENDED_STRUCT_MEMBER
};
BPF_HASH(infotmp, u64, struct val_t);
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 = {};
u64 id = bpf_get_current_pid_tgid();
u32 pid = id >> 32; // PID is higher 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) {
val.id = id;
val.fname = filename;
val.flags = flags; // EXTENDED_STRUCT_MEMBER
infotmp.update(&id, &val);
}
......@@ -110,6 +130,7 @@ int trace_return(struct pt_regs *ctx)
bpf_probe_read(&data.fname, sizeof(data.fname), (void *)valp->fname);
data.id = valp->id;
data.ts = tsp / 1000;
data.flags = valp->flags; // EXTENDED_STRUCT_MEMBER
data.ret = PT_REGS_RC(ctx);
events.perf_submit(ctx, &data, sizeof(data));
......@@ -119,13 +140,21 @@ int trace_return(struct pt_regs *ctx)
}
"""
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)
elif args.pid:
bpf_text = bpf_text.replace('FILTER',
bpf_text = bpf_text.replace('PID_TID_FILTER',
'if (pid != %s) { return 0; }' % args.pid)
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:
print(bpf_text)
if args.ebpf:
......@@ -145,7 +174,8 @@ class Data(ct.Structure):
("ts", ct.c_ulonglong),
("ret", ct.c_int),
("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
......@@ -153,8 +183,11 @@ initial_ts = 0
# header
if args.timestamp:
print("%-14s" % ("TIME(s)"), end="")
print("%-6s %-16s %4s %3s %s" %
("TID" if args.tid else "PID", "COMM", "FD", "ERR", "PATH"))
print("%-6s %-16s %4s %3s " %
("TID" if args.tid else "PID", "COMM", "FD", "ERR"), end="")
if args.extended_fields:
print("%-9s" % ("FLAGS"), end="")
print("PATH")
# process event
def print_event(cpu, data, size):
......@@ -182,10 +215,14 @@ def print_event(cpu, data, size):
delta = event.ts - initial_ts
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.comm.decode('utf-8', 'replace'), fd_s, err,
event.fname.decode('utf-8', 'replace')))
event.comm.decode('utf-8', 'replace'), fd_s, err), end="")
if args.extended_fields:
print("%08o " % event.flags, end="")
print(event.fname.decode('utf-8', 'replace'))
# loop with callback to print_event
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
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:
# ./opensnoop -h
usage: opensnoop [-h] [-T] [-x] [-p PID] [-t TID] [-d DURATION] [-n NAME]
[-e] [-f FLAG_FILTER]
Trace open() syscalls
......@@ -148,6 +178,10 @@ optional arguments:
-d DURATION, --duration DURATION
total duration of trace in seconds
-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:
./opensnoop # trace all open() syscalls
......@@ -157,3 +191,5 @@ examples:
./opensnoop -t 123 # only trace TID 123
./opensnoop -d 10 # trace for 10 seconds only
./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