mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-05-05 21:01:27 +08:00
teach chatgpt for perf event
This commit is contained in:
@@ -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.
|
||||
245
6-sigsnoop/app.c
245
6-sigsnoop/app.c
@@ -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;
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user