From 101304bb771b2d98e34e3957851fd2f77ec0c35b Mon Sep 17 00:00:00 2001 From: Hengqi Chen Date: Sun, 8 Aug 2021 11:15:56 +0800 Subject: [PATCH] libbpf-tools: add exitsnoop (#3564) add exitsnoop libbpf tool. Signed-off-by: Hengqi Chen --- libbpf-tools/.gitignore | 1 + libbpf-tools/Makefile | 1 + libbpf-tools/exitsnoop.bpf.c | 51 ++++++++ libbpf-tools/exitsnoop.c | 218 +++++++++++++++++++++++++++++++++++ libbpf-tools/exitsnoop.h | 18 +++ 5 files changed, 289 insertions(+) create mode 100644 libbpf-tools/exitsnoop.bpf.c create mode 100644 libbpf-tools/exitsnoop.c create mode 100644 libbpf-tools/exitsnoop.h diff --git a/libbpf-tools/.gitignore b/libbpf-tools/.gitignore index 2e9583634a5c..cd79bf29ccc9 100644 --- a/libbpf-tools/.gitignore +++ b/libbpf-tools/.gitignore @@ -12,6 +12,7 @@ /cpufreq /drsnoop /execsnoop +/exitsnoop /ext4dist /ext4slower /filelife diff --git a/libbpf-tools/Makefile b/libbpf-tools/Makefile index c6745042b608..21fd5127bfeb 100644 --- a/libbpf-tools/Makefile +++ b/libbpf-tools/Makefile @@ -27,6 +27,7 @@ APPS = \ cpufreq \ drsnoop \ execsnoop \ + exitsnoop \ filelife \ filetop \ fsdist \ diff --git a/libbpf-tools/exitsnoop.bpf.c b/libbpf-tools/exitsnoop.bpf.c new file mode 100644 index 000000000000..342fdc23c8d9 --- /dev/null +++ b/libbpf-tools/exitsnoop.bpf.c @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +/* Copyright (c) 2021 Hengqi Chen */ +#include +#include +#include +#include "exitsnoop.h" + +const volatile pid_t target_pid = 0; +const volatile bool trace_failed_only = false; +const volatile bool trace_by_process = true; + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); +} events SEC(".maps"); + +SEC("tracepoint/sched/sched_process_exit") +int sched_process_exit(void *ctx) +{ + __u64 pid_tgid = bpf_get_current_pid_tgid(); + __u32 pid = pid_tgid >> 32; + __u32 tid = (__u32)pid_tgid; + int exit_code; + struct task_struct *task; + struct event event = {}; + + if (target_pid && target_pid != pid) + return 0; + + if (trace_by_process && pid != tid) + return 0; + + task = (struct task_struct *)bpf_get_current_task(); + exit_code = BPF_CORE_READ(task, exit_code); + if (trace_failed_only && exit_code == 0) + return 0; + + event.start_time = BPF_CORE_READ(task, start_time); + event.exit_time = bpf_ktime_get_ns(); + event.pid = pid; + event.tid = tid; + event.ppid = BPF_CORE_READ(task, real_parent, tgid); + event.sig = exit_code & 0xff; + event.exit_code = exit_code >> 8; + bpf_get_current_comm(event.comm, sizeof(event.comm)); + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)); + return 0; +} + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; diff --git a/libbpf-tools/exitsnoop.c b/libbpf-tools/exitsnoop.c new file mode 100644 index 000000000000..8ef8584f00eb --- /dev/null +++ b/libbpf-tools/exitsnoop.c @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ + +/* + * exitsnoop Trace process termination. + * + * Copyright (c) 2021 Hengqi Chen + * + * Based on exitsnoop(8) from BCC by Arturo Martin-de-Nicolas & Jeroen Soeters. + * 05-Aug-2021 Hengqi Chen Created this. + */ +#include +#include +#include +#include +#include + +#include +#include +#include "exitsnoop.h" +#include "exitsnoop.skel.h" +#include "trace_helpers.h" + +#define PERF_BUFFER_PAGES 16 +#define PERF_POLL_TIMEOUT_MS 100 +#define warn(...) fprintf(stderr, __VA_ARGS__) + +static volatile sig_atomic_t exiting = 0; + +static bool emit_timestamp = false; +static pid_t target_pid = 0; +static bool trace_failed_only = false; +static bool trace_by_process = true; + +const char *argp_program_version = "exitsnoop 0.1"; +const char *argp_program_bug_address = + "https://github.com/iovisor/bcc/tree/master/libbpf-tools"; +const char argp_program_doc[] = +"Trace process termination.\n" +"\n" +"USAGE: exitsnoop [-h] [-t] [-x] [-p PID] [-T]\n" +"\n" +"EXAMPLES:\n" +" exitsnoop # trace process exit events\n" +" exitsnoop -t # include timestamps\n" +" exitsnoop -x # trace error exits only\n" +" exitsnoop -p 1216 # only trace PID 1216\n" +" exitsnoop -T # trace by thread\n"; + +static const struct argp_option opts[] = { + { "timestamp", 't', NULL, 0, "Include timestamp on output" }, + { "failed", 'x', NULL, 0, "Trace error exits only." }, + { "pid", 'p', "PID", 0, "Process ID to trace" }, + { "threaded", 'T', NULL, 0, "Trace by thread." }, + { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" }, + {}, +}; + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + long pid; + + switch (key) { + case 'p': + errno = 0; + pid = strtol(arg, NULL, 10); + if (errno || pid <= 0) { + warn("Invalid PID: %s\n", arg); + argp_usage(state); + } + target_pid = pid; + break; + case 't': + emit_timestamp = true; + break; + case 'x': + trace_failed_only = true; + break; + case 'T': + trace_by_process = false; + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static void sig_int(int signo) +{ + exiting = 1; +} + +static void handle_event(void *ctx, int cpu, void *data, __u32 data_sz) +{ + struct event *e = data; + time_t t; + struct tm *tm; + char ts[32]; + double age; + int sig, coredump; + + if (emit_timestamp) { + time(&t); + tm = localtime(&t); + strftime(ts, sizeof(ts), "%H:%M:%S", tm); + printf("%8s ", ts); + } + + age = (e->exit_time - e->start_time) / 1e9; + printf("%-16s %-7d %-7d %-7d %-7.2f ", + e->comm, e->pid, e->ppid, e->tid, age); + + if (!e->sig) { + if (!e->exit_code) + printf("0\n"); + else + printf("code %d\n", e->exit_code); + } else { + sig = e->sig & 0x7f; + coredump = e->sig & 0x80; + if (sig) + printf("signal %d (%s)", sig, strsignal(sig)); + if (coredump) + printf(", core dumped"); + printf("\n"); + } +} + +static void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) +{ + warn("lost %llu events on CPU #%d\n", lost_cnt, cpu); +} + +int main(int argc, char **argv) +{ + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, + }; + struct perf_buffer_opts pb_opts; + struct perf_buffer *pb = NULL; + struct exitsnoop_bpf *obj; + int err; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + err = bump_memlock_rlimit(); + if (err) { + warn("failed to increase rlimit: %d\n", err); + return 1; + } + + obj = exitsnoop_bpf__open(); + if (!obj) { + warn("failed to open BPF object\n"); + return 1; + } + + obj->rodata->target_pid = target_pid; + obj->rodata->trace_failed_only = trace_failed_only; + obj->rodata->trace_by_process = trace_by_process; + + err = exitsnoop_bpf__load(obj); + if (err) { + warn("failed to load BPF object: %d\n", err); + goto cleanup; + } + + err = exitsnoop_bpf__attach(obj); + if (err) { + warn("failed to attach BPF programs: %d\n", err); + goto cleanup; + } + + pb_opts.sample_cb = handle_event; + pb_opts.lost_cb = handle_lost_events; + pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES, &pb_opts); + err = libbpf_get_error(pb); + if (err) { + warn("failed to open perf buffer: %d\n", err); + goto cleanup; + } + + if (signal(SIGINT, sig_int) == SIG_ERR) { + warn("can't set signal handler: %s\n", strerror(-errno)); + goto cleanup; + } + + if (emit_timestamp) + printf("%-8s ", "TIME(s)"); + printf("%-16s %-7s %-7s %-7s %-7s %-s\n", + "PCOMM", "PID", "PPID", "TID", "AGE(s)", "EXIT_CODE"); + + while (1) { + err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS); + if (err == -EINTR) { + err = 0; + goto cleanup; + } + + if (err < 0) + break; + if (exiting) + goto cleanup; + } + warn("error polling perf buffer: %d\n", err); + +cleanup: + perf_buffer__free(pb); + exitsnoop_bpf__destroy(obj); + + return err != 0; +} diff --git a/libbpf-tools/exitsnoop.h b/libbpf-tools/exitsnoop.h new file mode 100644 index 000000000000..e42d156f8d6d --- /dev/null +++ b/libbpf-tools/exitsnoop.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __EXITSNOOP_H +#define __EXITSNOOP_H + +#define TASK_COMM_LEN 16 + +struct event { + __u64 start_time; + __u64 exit_time; + __u32 pid; + __u32 tid; + __u32 ppid; + __u32 sig; + int exit_code; + char comm[TASK_COMM_LEN]; +}; + +#endif /* __EXITSNOOP_H */