Commit 5b47e0f8 authored by Mark Drayton's avatar Mark Drayton

execsnoop: use BPF_PERF_OUTPUT instead of trace pipe

parent 8084f8c1
...@@ -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;
} }
""" """
...@@ -118,49 +147,48 @@ if args.timestamp: ...@@ -118,49 +147,48 @@ 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 %3s %s" % ("PCOMM", "PID", "RET", "ARGS"))
start_ts = 0 TASK_COMM_LEN = 16 # linux/sched.h
cmd = {} ARGSIZE = 128 # should match #define in C above
pcomm = {}
# format output class Data(ct.Structure):
while 1: _fields_ = [
(task, pid, cpu, flags, ts, msg) = b.trace_fields() ("pid", ct.c_uint),
try: ("comm", ct.c_char * TASK_COMM_LEN),
(type, arg) = msg.split(" ", 1) ("type", ct.c_int),
except ValueError: ("argv", ct.c_char * ARGSIZE),
continue ("retval", ct.c_int),
]
if start_ts == 0:
start_ts = ts class EventType(object):
EVENT_ARG = 0
if type == "RET": EVENT_RET = 1
if pid not in cmd:
# zero args start_ts = time.time()
cmd[pid] = "" argv = defaultdict(list)
pcomm[pid] = ""
# process event
skip = 0 def print_event(cpu, data, size):
if args.name: event = ct.cast(data, ct.POINTER(Data)).contents
if not re.search(args.name, cmd[pid]):
skip = 1 skip = False
if not args.fails and int(arg) < 0:
skip = 1 if event.type == EventType.EVENT_ARG:
if skip: argv[event.pid].append(event.argv)
del cmd[pid] elif event.type == EventType.EVENT_RET:
del pcomm[pid] if args.fails and event.retval == 0:
continue skip = True
if args.name and not re.search(args.name, event.comm):
# output skip = True
if not skip:
if args.timestamp: if args.timestamp:
print("%-8.3f" % (ts - start_ts), end="") print("%-8.3f" % (time.time() - start_ts), end="")
print("%-16s %-6s %3s %s" % (pcomm[pid], pid, arg, cmd[pid])) print("%-16s %-6s %3s %s" % (event.comm, event.pid, 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