Commit c419edf4 authored by 4ast's avatar 4ast

Merge pull request #555 from markdrayton/execsnoop

execsnoop: use BPF_PERF_OUTPUT, add PPID
parents 8084f8c1 bfdb3d48
...@@ -19,7 +19,10 @@ ...@@ -19,7 +19,10 @@
from __future__ import print_function from __future__ import print_function
from bcc import BPF from bcc import BPF
import argparse import argparse
import ctypes as ct
import re import re
import time
from collections import defaultdict
# arguments # arguments
examples = """examples: examples = """examples:
...@@ -47,65 +50,91 @@ bpf_text = """ ...@@ -47,65 +50,91 @@ bpf_text = """
#include <linux/fs.h> #include <linux/fs.h>
#define MAXARG 20 #define MAXARG 20
#define ARGSIZE 64 #define ARGSIZE 128
static int print_arg(void *ptr) { enum event_type {
// Fetch an argument, and print using bpf_trace_printk(). This is a work EVENT_ARG,
// around until we have a binary trace interface for passing event data to EVENT_RET,
// bcc. Since exec()s should be low frequency, the additional overhead in };
// this case should not be a problem.
const char *argp = NULL;
char buf[ARGSIZE] = {};
bpf_probe_read(&argp, sizeof(argp), ptr); struct data_t {
if (argp == NULL) return 0; u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel)
char comm[TASK_COMM_LEN];
enum event_type type;
char argv[ARGSIZE];
int retval;
};
bpf_probe_read(&buf, sizeof(buf), (void *)(argp)); BPF_PERF_OUTPUT(events);
bpf_trace_printk("ARG %s\\n", buf);
static int __submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data)
{
bpf_probe_read(data->argv, sizeof(data->argv), ptr);
events.perf_submit(ctx, data, sizeof(struct data_t));
return 1; return 1;
} }
static int submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data)
{
const char *argp = NULL;
bpf_probe_read(&argp, sizeof(argp), ptr);
if (argp) {
return __submit_arg(ctx, (void *)(argp), data);
}
return 0;
}
int kprobe__sys_execve(struct pt_regs *ctx, struct filename *filename, int kprobe__sys_execve(struct pt_regs *ctx, struct filename *filename,
const char __user *const __user *__argv, const char __user *const __user *__argv,
const char __user *const __user *__envp) const char __user *const __user *__envp)
{ {
char fname[ARGSIZE] = {}; // create data here and pass to submit_arg to save space on the stack (#555)
bpf_probe_read(&fname, sizeof(fname), (void *)(filename)); struct data_t data = {};
bpf_trace_printk("ARG %s\\n", fname); data.pid = bpf_get_current_pid_tgid() >> 32;
bpf_get_current_comm(&data.comm, sizeof(data.comm));
data.type = EVENT_ARG;
int i = 1; // skip first arg, as we printed fname __submit_arg(ctx, (void *)filename, &data);
// unrolled loop to walk argv[] (MAXARG) int i = 1; // skip first arg, as we submitted filename
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++; // X
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++;
if (print_arg((void *)&__argv[i]) == 0) goto out; i++; // XX
bpf_trace_printk("ARG ...\\n"); // truncated
// unrolled loop to walk argv[] (MAXARG)
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++; // X
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++;
if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; i++; // XX
// handle truncated argument list
char ellipsis[] = "...";
__submit_arg(ctx, (void *)ellipsis, &data);
out: out:
return 0; return 0;
} }
int kretprobe__sys_execve(struct pt_regs *ctx) int kretprobe__sys_execve(struct pt_regs *ctx)
{ {
bpf_trace_printk("RET %d\\n", PT_REGS_RC(ctx)); struct data_t data = {};
data.pid = bpf_get_current_pid_tgid() >> 32;
bpf_get_current_comm(&data.comm, sizeof(data.comm));
data.type = EVENT_RET;
data.retval = PT_REGS_RC(ctx);
events.perf_submit(ctx, &data, sizeof(data));
return 0; return 0;
} }
""" """
...@@ -116,51 +145,65 @@ b = BPF(text=bpf_text) ...@@ -116,51 +145,65 @@ b = BPF(text=bpf_text)
# header # header
if args.timestamp: if args.timestamp:
print("%-8s" % ("TIME(s)"), end="") print("%-8s" % ("TIME(s)"), end="")
print("%-16s %-6s %3s %s" % ("PCOMM", "PID", "RET", "ARGS")) print("%-16s %-6s %-6s %3s %s" % ("PCOMM", "PID", "PPID", "RET", "ARGS"))
start_ts = 0 TASK_COMM_LEN = 16 # linux/sched.h
cmd = {} ARGSIZE = 128 # should match #define in C above
pcomm = {}
class Data(ct.Structure):
# format output _fields_ = [
while 1: ("pid", ct.c_uint),
(task, pid, cpu, flags, ts, msg) = b.trace_fields() ("comm", ct.c_char * TASK_COMM_LEN),
("type", ct.c_int),
("argv", ct.c_char * ARGSIZE),
("retval", ct.c_int),
]
class EventType(object):
EVENT_ARG = 0
EVENT_RET = 1
start_ts = time.time()
argv = defaultdict(list)
# TODO: This is best-effort PPID matching. Short-lived processes may exit
# before we get a chance to read the PPID. This should be replaced with fetching
# PPID via C when available (#364).
def get_ppid(pid):
try: try:
(type, arg) = msg.split(" ", 1) with open("/proc/%d/status" % pid) as status:
except ValueError: for line in status:
continue if line.startswith("PPid:"):
return int(line.split()[1])
if start_ts == 0: except IOError:
start_ts = ts pass
return 0
if type == "RET":
if pid not in cmd: # process event
# zero args def print_event(cpu, data, size):
cmd[pid] = "" event = ct.cast(data, ct.POINTER(Data)).contents
pcomm[pid] = ""
skip = False
skip = 0
if args.name: if event.type == EventType.EVENT_ARG:
if not re.search(args.name, cmd[pid]): argv[event.pid].append(event.argv)
skip = 1 elif event.type == EventType.EVENT_RET:
if not args.fails and int(arg) < 0: if args.fails and event.retval == 0:
skip = 1 skip = True
if skip: if args.name and not re.search(args.name, event.comm):
del cmd[pid] skip = True
del pcomm[pid]
continue if not skip:
if args.timestamp:
# output print("%-8.3f" % (time.time() - start_ts), end="")
if args.timestamp: ppid = get_ppid(event.pid)
print("%-8.3f" % (ts - start_ts), end="") print("%-16s %-6s %-6s %3s %s" % (event.comm, event.pid,
print("%-16s %-6s %3s %s" % (pcomm[pid], pid, arg, cmd[pid])) ppid if ppid > 0 else "?", event.retval,
del cmd[pid] ' '.join(argv[event.pid])))
del pcomm[pid]
else: del(argv[event.pid])
# build command line string
if pid in cmd: # loop with callback to print_event
cmd[pid] = cmd[pid] + " " + arg b["events"].open_perf_buffer(print_event)
else: while 1:
cmd[pid] = arg b.kprobe_poll()
if pid not in pcomm:
pcomm[pid] = task
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