teach chatgpt for perf event

This commit is contained in:
yunwei37
2022-12-05 19:47:51 +08:00
parent f05a840fce
commit 0fd76b02b8
26 changed files with 335 additions and 587 deletions

View File

@@ -1,15 +1,98 @@
---
layout: post
title: sigsnoop
date: 2022-10-10 16:18
category: bpftools
author: yunwei37
tags: [bpftools, syscall, kprobe, tracepoint]
summary: Trace signals generated system wide, from syscalls and others.
---
# eBPF 入门开发实践指南六:捕获进程发送信号的系统调用集合,使用 hash map 保存状态
## sigsnoop
```c
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2021~2022 Hengqi Chen */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include "sigsnoop.h"
#define MAX_ENTRIES 10240
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_ENTRIES);
__type(key, __u32);
__type(value, struct event);
} values SEC(".maps");
## origin
static int probe_entry(pid_t tpid, int sig)
{
struct event event = {};
__u64 pid_tgid;
__u32 pid, tid;
pid_tgid = bpf_get_current_pid_tgid();
pid = pid_tgid >> 32;
event.pid = pid;
event.tpid = tpid;
event.sig = sig;
bpf_get_current_comm(event.comm, sizeof(event.comm));
bpf_map_update_elem(&values, &tid, &event, BPF_ANY);
return 0;
}
static int probe_exit(void *ctx, int ret)
{
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 tid = (__u32)pid_tgid;
struct event *eventp;
eventp = bpf_map_lookup_elem(&values, &tid);
if (!eventp)
return 0;
eventp->ret = ret;
bpf_printk("PID %d (%s) sent signal %d to PID %d, ret = %d",
eventp->pid, eventp->comm, eventp->sig, eventp->tpid, eventp->ret);
cleanup:
bpf_map_delete_elem(&values, &tid);
return 0;
}
SEC("tracepoint/syscalls/sys_enter_kill")
int kill_entry(struct trace_event_raw_sys_enter *ctx)
{
pid_t tpid = (pid_t)ctx->args[0];
int sig = (int)ctx->args[1];
return probe_entry(tpid, sig);
}
SEC("tracepoint/syscalls/sys_exit_kill")
int kill_exit(struct trace_event_raw_sys_exit *ctx)
{
return probe_exit(ctx, ctx->ret);
}
SEC("tracepoint/syscalls/sys_enter_tkill")
int tkill_entry(struct trace_event_raw_sys_enter *ctx)
{
pid_t tpid = (pid_t)ctx->args[0];
int sig = (int)ctx->args[1];
return probe_entry(tpid, sig);
}
SEC("tracepoint/syscalls/sys_exit_tkill")
int tkill_exit(struct trace_event_raw_sys_exit *ctx)
{
return probe_exit(ctx, ctx->ret);
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
```
上面的代码定义了一个 eBPF 程序,用于捕获进程发送信号的系统调用,包括 kill、tkill 和 tgkill。它通过使用 tracepoint 来捕获系统调用的进入和退出事件,并在这些事件发生时执行指定的探针函数,例如 probe_entry 和 probe_exit。
在探针函数中,我们使用 bpf_map 存储捕获的事件信息,包括发送信号的进程 ID、接收信号的进程 ID、信号值和系统调用的返回值。在系统调用退出时我们将获取存储在 bpf_map 中的事件信息,并使用 bpf_printk 打印进程 ID、进程名称、发送的信号和系统调用的返回值。
最后,我们还需要使用 SEC 宏来定义探针,并指定要捕获的系统调用的名称,以及要执行的探针函数。
origin from:
@@ -60,96 +143,3 @@ Optional arguments:
Built with eunomia-bpf framework.
See https://github.com/eunomia-bpf/eunomia-bpf for more information.
```
## WASM example
Generate WASM skel:
```shell
docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest gen-wasm-skel
```
> The skel is generated and commit, so you don't need to generate it again.
> skel includes:
>
> - eunomia-include: include headers for WASM
> - app.c: the WASM app. all library is header only.
Build WASM module
```shell
docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest build-wasm
```
Run:
```console
$ sudo ./ecli run app.wasm -h
Usage: sigsnoop [-h] [-x] [-k] [-n] [-p PID] [-s SIGNAL]
Trace standard and real-time signals.
-h, --help show this help message and exit
-x, --failed failed signals only
-k, --killed kill only
-p, --pid=<int> target pid
-s, --signal=<int> target signal
$ sudo ./ecli run app.wasm
running and waiting for the ebpf events from perf event...
{"pid":185539,"tpid":185538,"sig":17,"ret":0,"comm":"cat","sig_name":"SIGCHLD"}
{"pid":185540,"tpid":185538,"sig":17,"ret":0,"comm":"grep","sig_name":"SIGCHLD"}
$ sudo ./ecli run app.wasm -p 1641
running and waiting for the ebpf events from perf event...
{"pid":1641,"tpid":2368,"sig":23,"ret":0,"comm":"YDLive","sig_name":"SIGURG"}
{"pid":1641,"tpid":2368,"sig":23,"ret":0,"comm":"YDLive","sig_name":"SIGURG"}
```
## details in bcc
Demonstrations of sigsnoop.
This traces signals generated system wide. For example:
```console
# ./sigsnoop -n
TIME PID COMM SIG TPID RESULT
19:56:14 3204808 a.out SIGSEGV 3204808 0
19:56:14 3204808 a.out SIGPIPE 3204808 0
19:56:14 3204808 a.out SIGCHLD 3204722 0
```
The first line showed that a.out (a test program) deliver a SIGSEGV signal.
The result, 0, means success.
The second and third lines showed that a.out also deliver SIGPIPE/SIGCHLD
signals successively.
USAGE message:
```console
# ./sigsnoop -h
Usage: sigsnoop [OPTION...]
Trace standard and real-time signals.
USAGE: sigsnoop [-h] [-x] [-k] [-n] [-p PID] [-s SIGNAL]
EXAMPLES:
sigsnoop # trace signals system-wide
sigsnoop -k # trace signals issued by kill syscall only
sigsnoop -x # trace failed signals only
sigsnoop -p 1216 # only trace PID 1216
sigsnoop -s 9 # only trace signal 9
-k, --kill Trace signals issued by kill syscall only.
-n, --name Output signal name instead of signal number.
-p, --pid=PID Process ID to trace
-s, --signal=SIGNAL Signal to trace.
-x, --failed Trace failed signals only.
-?, --help Give this help list
--usage Give a short usage message
-V, --version Print program version
```
Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.
Report bugs to https://github.com/iovisor/bcc/tree/master/libbpf-tools.

View File

@@ -1,245 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>
#include "eunomia-include/wasm-app.h"
#include "eunomia-include/entry.h"
#include "eunomia-include/argp.h"
#include "sigsnoop.bpf.h"
#include "ewasm-skel.h"
#include "eunomia-include/sigsnoop.skel.h"
#define PERF_BUFFER_PAGES 16
#define PERF_POLL_TIMEOUT_MS 100
#define warn(...) printf(__VA_ARGS__)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
static volatile int exiting = 0;
static int target_pid = 0;
static int target_signal = 0;
static bool failed_only = false;
static bool kill_only = false;
static bool signal_name = false;
static bool verbose = false;
static const char *sig_name[] = {
[0] = "N/A",
[1] = "SIGHUP",
[2] = "SIGINT",
[3] = "SIGQUIT",
[4] = "SIGILL",
[5] = "SIGTRAP",
[6] = "SIGABRT",
[7] = "SIGBUS",
[8] = "SIGFPE",
[9] = "SIGKILL",
[10] = "SIGUSR1",
[11] = "SIGSEGV",
[12] = "SIGUSR2",
[13] = "SIGPIPE",
[14] = "SIGALRM",
[15] = "SIGTERM",
[16] = "SIGSTKFLT",
[17] = "SIGCHLD",
[18] = "SIGCONT",
[19] = "SIGSTOP",
[20] = "SIGTSTP",
[21] = "SIGTTIN",
[22] = "SIGTTOU",
[23] = "SIGURG",
[24] = "SIGXCPU",
[25] = "SIGXFSZ",
[26] = "SIGVTALRM",
[27] = "SIGPROF",
[28] = "SIGWINCH",
[29] = "SIGIO",
[30] = "SIGPWR",
[31] = "SIGSYS",
};
const char *argp_program_version = "sigsnoop 0.1";
const char *argp_program_bug_address =
"https://github.com/iovisor/bcc/tree/master/libbpf-tools";
const char argp_program_doc[] =
"Trace standard and real-time signals.\n"
"\n"
"USAGE: sigsnoop [-h] [-x] [-k] [-n] [-p PID] [-s SIGNAL]\n"
"\n"
"EXAMPLES:\n"
" sigsnoop # trace signals system-wide\n"
" sigsnoop -k # trace signals issued by kill syscall only\n"
" sigsnoop -x # trace failed signals only\n"
" sigsnoop -p 1216 # only trace PID 1216\n"
" sigsnoop -s 9 # only trace signal 9\n";
static const struct argp_option opts[] = {
{ "failed", 'x', NULL, 0, "Trace failed signals only." },
{ "kill", 'k', NULL, 0, "Trace signals issued by kill syscall only." },
{ "pid", 'p', "PID", 0, "Process ID to trace" },
{ "signal", 's', "SIGNAL", 0, "Signal to trace." },
{ "name", 'n', NULL, 0, "Output signal name instead of signal number." },
{ "verbose", 'v', NULL, 0, "Verbose debug output" },
{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
{},
};
static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
long pid, sig;
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 's':
errno = 0;
sig = strtol(arg, NULL, 10);
if (errno || sig <= 0) {
warn("Invalid SIGNAL: %s\n", arg);
argp_usage(state);
}
target_signal = sig;
break;
case 'n':
signal_name = true;
break;
case 'x':
failed_only = true;
break;
case 'k':
kill_only = true;
break;
case 'v':
verbose = true;
break;
case 'h':
argp_state_help(state, ARGP_HELP_STD_HELP);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static int libbpf_print_fn(const char *format, va_list args)
{
if (!verbose)
return 0;
return printf(format, args);
}
static void alias_parse(char *prog)
{
char *name = prog;
if (!strcmp(name, "killsnoop")) {
kill_only = true;
}
}
static void sig_int(int signo)
{
exiting = 1;
}
static void handle_event(void *ctx, int cpu, void *data, unsigned int data_sz)
{
struct event *e = data;
char ts[32] = "12:47:32";
if (signal_name && e->sig < ARRAY_SIZE(sig_name))
printf("%-8s %-7d %-16s %-9s %-7d %-6d\n",
ts, e->pid, e->comm, sig_name[e->sig], e->tpid, e->ret);
else
printf("%-8s %-7d %-16s %-9d %-7d %-6d\n",
ts, e->pid, e->comm, e->sig, e->tpid, e->ret);
}
static void handle_lost_events(void *ctx, int cpu, unsigned long long 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 *pb = NULL;
struct sigsnoop_bpf *obj;
int err;
alias_parse(argv[0]);
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
if (err)
return err;
obj = sigsnoop_bpf__open();
if (!obj) {
warn("failed to open BPF object\n");
return 1;
}
obj->rodata->filtered_pid = target_pid;
obj->rodata->target_signal = target_signal;
obj->rodata->failed_only = failed_only;
if (kill_only) {
bpf_program__set_autoload(obj->progs.sig_trace, false);
} else {
bpf_program__set_autoload(obj->progs.kill_entry, false);
bpf_program__set_autoload(obj->progs.kill_exit, false);
bpf_program__set_autoload(obj->progs.tkill_entry, false);
bpf_program__set_autoload(obj->progs.tkill_exit, false);
bpf_program__set_autoload(obj->progs.tgkill_entry, false);
bpf_program__set_autoload(obj->progs.tgkill_exit, false);
}
err = sigsnoop_bpf__load(obj);
if (err) {
warn("failed to load BPF object: %d\n", err);
goto cleanup;
}
err = sigsnoop_bpf__attach(obj);
if (err) {
warn("failed to attach BPF programs: %d\n", err);
goto cleanup;
}
pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES,
handle_event, handle_lost_events, NULL, NULL);
if (!pb) {
warn("failed to open perf buffer: %d\n", err);
goto cleanup;
}
printf("%-8s %-7s %-16s %-9s %-7s %-6s\n",
"TIME", "PID", "COMM", "SIG", "TPID", "RESULT");
while (!exiting) {
err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS);
if (err < 0 && err != -EINTR) {
warn("error polling perf buffer: %s\n", strerror(-err));
goto cleanup;
}
/* reset err to return 0 if exiting */
err = 0;
}
cleanup:
perf_buffer__free(pb);
sigsnoop_bpf__destroy(obj);
return err != 0;
}

View File

@@ -6,10 +6,6 @@
#define MAX_ENTRIES 10240
const volatile pid_t filtered_pid = 0;
const volatile int target_signal = 0;
const volatile bool failed_only = false;
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_ENTRIES);
@@ -17,11 +13,6 @@ struct {
__type(value, struct event);
} values SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(__u32));
} events SEC(".maps");
static int probe_entry(pid_t tpid, int sig)
{
@@ -29,15 +20,8 @@ static int probe_entry(pid_t tpid, int sig)
__u64 pid_tgid;
__u32 pid, tid;
if (target_signal && sig != target_signal)
return 0;
pid_tgid = bpf_get_current_pid_tgid();
pid = pid_tgid >> 32;
tid = (__u32)pid_tgid;
if (filtered_pid && pid != filtered_pid)
return 0;
event.pid = pid;
event.tpid = tpid;
event.sig = sig;
@@ -56,11 +40,9 @@ static int probe_exit(void *ctx, int ret)
if (!eventp)
return 0;
if (failed_only && ret >= 0)
goto cleanup;
eventp->ret = ret;
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, eventp, sizeof(*eventp));
bpf_printk("PID %d (%s) sent signal %d to PID %d, ret = %d",
eventp->pid, eventp->comm, eventp->sig, eventp->tpid, eventp->ret);
cleanup:
bpf_map_delete_elem(&values, &tid);
@@ -112,34 +94,4 @@ int tgkill_exit(struct trace_event_raw_sys_exit *ctx)
return probe_exit(ctx, ctx->ret);
}
SEC("tracepoint/signal/signal_generate")
int sig_trace(struct trace_event_raw_signal_generate *ctx)
{
struct event event = {};
pid_t tpid = ctx->pid;
int ret = ctx->errno;
int sig = ctx->sig;
__u64 pid_tgid;
__u32 pid;
if (failed_only && ret == 0)
return 0;
if (target_signal && sig != target_signal)
return 0;
pid_tgid = bpf_get_current_pid_tgid();
pid = pid_tgid >> 32;
if (filtered_pid && pid != filtered_pid)
return 0;
event.pid = pid;
event.tpid = tpid;
event.sig = sig;
event.ret = ret;
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";