| @@ -26,8 +26,6 @@ def pretty_print(input, lexer=MarkdownLexer, *args, **kwargs): | |||||
| # print = pretty_print | # print = pretty_print | ||||
| def main(): | def main(): | ||||
| parser = argparse.ArgumentParser( | parser = argparse.ArgumentParser( | ||||
| prog="GPTtrace", | prog="GPTtrace", | ||||
| @@ -111,7 +109,6 @@ def main(): | |||||
| input_data = f.read() | input_data = f.read() | ||||
| if args.verbose: | if args.verbose: | ||||
| print(input_data) | print(input_data) | ||||
| print("-"*len(info)) | |||||
| _, session = generate_result( | _, session = generate_result( | ||||
| chatbot, input_data, conv_uuid, args.verbose) | chatbot, input_data, conv_uuid, args.verbose) | ||||
| print(f"Trained session: {session}") | print(f"Trained session: {session}") | ||||
| @@ -0,0 +1 @@ | |||||
| I'll give you a few examples of how to write eBPF programs. You can use these examples to learn how to write eBPF programs, they will cover libbpf, bcc and bpftrace. | |||||
| @@ -0,0 +1,42 @@ | |||||
| This is a minimal libbpf program using kprobe: | |||||
| # kprobe | |||||
| ```c | |||||
| #include "vmlinux.h" | |||||
| #include <bpf/bpf_helpers.h> | |||||
| #include <bpf/bpf_tracing.h> | |||||
| #include <bpf/bpf_core_read.h> | |||||
| char LICENSE[] SEC("license") = "Dual BSD/GPL"; | |||||
| SEC("kprobe/do_unlinkat") | |||||
| int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name) | |||||
| { | |||||
| pid_t pid; | |||||
| const char *filename; | |||||
| pid = bpf_get_current_pid_tgid() >> 32; | |||||
| filename = BPF_CORE_READ(name, name); | |||||
| bpf_printk("KPROBE ENTRY pid = %d, filename = %s\n", pid, filename); | |||||
| return 0; | |||||
| } | |||||
| SEC("kretprobe/do_unlinkat") | |||||
| int BPF_KRETPROBE(do_unlinkat_exit, long ret) | |||||
| { | |||||
| pid_t pid; | |||||
| pid = bpf_get_current_pid_tgid() >> 32; | |||||
| bpf_printk("KPROBE EXIT: pid = %d, ret = %ld\n", pid, ret); | |||||
| return 0; | |||||
| } | |||||
| ``` | |||||
| kprobe 是 eBPF 用于处理内核空间入口和出口(返回)探针(kprobe 和 kretprobe)的一个例子。它将 kprobe 和 kretprobe BPF 程序附加到 do_unlinkat() 函数上,并使用 bpf_printk() 宏分别记录 PID、文件名和返回值。 | |||||
| eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。 | |||||
| 我还没有说完,我会继续说。 | |||||
| @@ -0,0 +1,35 @@ | |||||
| 要编译这个程序,请使用 ecc 工具: | |||||
| ```console | |||||
| $ ecc kprobe-link.bpf.c | |||||
| Compiling bpf object... | |||||
| Packing ebpf object and config into package.json... | |||||
| ``` | |||||
| 然后运行: | |||||
| ```console | |||||
| sudo ecli package.json | |||||
| ``` | |||||
| 在另外一个窗口中: | |||||
| ```shell | |||||
| touch test1 | |||||
| rm test1 | |||||
| touch test2 | |||||
| rm test2 | |||||
| ``` | |||||
| 在 /sys/kernel/debug/tracing/trace_pipe 文件中,应该能看到类似下面的 kprobe 演示输出: | |||||
| ```shell | |||||
| $ sudo cat /sys/kernel/debug/tracing/trace_pipe | |||||
| rm-9346 [005] d..3 4710.951696: bpf_trace_printk: KPROBE ENTRY pid = 9346, filename = test1 | |||||
| rm-9346 [005] d..4 4710.951819: bpf_trace_printk: KPROBE EXIT: ret = 0 | |||||
| rm-9346 [005] d..3 4710.951852: bpf_trace_printk: KPROBE ENTRY pid = 9346, filename = test2 | |||||
| rm-9346 [005] d..4 4710.951895: bpf_trace_printk: KPROBE EXIT: ret = 0 | |||||
| ``` | |||||
| If you learned and remembered the result for libbpf program, please tell me OK. | |||||
| @@ -0,0 +1,33 @@ | |||||
| This is another libbpf program: | |||||
| # 在 eBPF 中使用 libbpf 捕获进程打开文件的系统调用集合 | |||||
| 首先,我们需要编写一段 eBPF 程序来捕获进程打开文件的系统调用,具体实现如下: | |||||
| ```c | |||||
| #include <vmlinux.h> | |||||
| #include <bpf/bpf_helpers.h> | |||||
| /// @description "Process ID to trace" | |||||
| const volatile int pid_target = 0; | |||||
| SEC("tracepoint/syscalls/sys_enter_openat") | |||||
| int tracepoint__syscalls__sys_enter_openat(struct trace_event_raw_sys_enter* ctx) | |||||
| { | |||||
| u64 id = bpf_get_current_pid_tgid(); | |||||
| u32 pid = id; | |||||
| if (pid_target && pid_target != pid) | |||||
| return false; | |||||
| // Use bpf_printk to print the process information | |||||
| bpf_printk("Process ID: %d enter sys openat\n", pid); | |||||
| return 0; | |||||
| } | |||||
| /// "Trace open family syscalls." | |||||
| char LICENSE[] SEC("license") = "GPL"; | |||||
| ``` | |||||
| 上面的 eBPF 程序通过定义函数 tracepoint__syscalls__sys_enter_openat 并使用 SEC 宏把它们附加到 sys_enter_openat 的 tracepoint(即在进入 openat 系统调用时执行)。这个函数通过使用 bpf_get_current_pid_tgid 函数获取调用 openat 系统调用的进程 ID,并使用 bpf_printk 函数在内核日志中打印出来。 | |||||
| If you learned and remembered the result for libbpf program, please tell me OK. | |||||
| @@ -1,3 +1,4 @@ | |||||
| This is an libbpf program: | This is an libbpf program: | ||||
| a minimal example of a BPF application installs a tracepoint handler which is triggered by write syscall | a minimal example of a BPF application installs a tracepoint handler which is triggered by write syscall | ||||
| @@ -27,4 +28,12 @@ int handle_tp(void *ctx) | |||||
| bpf_printk("BPF triggered from PID %d.\n", pid); | bpf_printk("BPF triggered from PID %d.\n", pid); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| ``` | |||||
| ``` | |||||
| 这段程序通过定义一个 handle_tp 函数并使用 SEC 宏把它附加到 sys_enter_write tracepoint(即在进入 write 系统调用时执行)。该函数通过使用 bpf_get_current_pid_tgid 和 bpf_printk 函数获取调用 write 系统调用的进程 ID,并在内核日志中打印出来。 | |||||
| - `bpf_trace_printk()`: 一种将信息输出到trace_pipe(/sys/kernel/debug/tracing/trace_pipe)简单机制。 在一些简单用例中这样使用没有问题, but它也有一些限制:最多 3 参数; 第一个参数必须是%s(即字符串);同时trace_pipe在内核中全局共享,其他并行使用trace_pipe的程序有可能会将 trace_pipe 的输出扰乱。 一个更好的方式是通过 BPF_PERF_OUTPUT(), 稍后将会讲到。 | |||||
| - `void *ctx`:ctx本来是具体类型的参数, 但是由于我们这里没有使用这个参数,因此就将其写成void *类型。 | |||||
| - `return 0`;:必须这样,返回 0 | |||||
| If you learned and remembered the result, please tell me OK. | |||||
| @@ -0,0 +1,34 @@ | |||||
| You are now a translater from human language to shell bpftrace command. | |||||
| Here are some examples of what you can do with bpftrace shell command, the upper line is the description of the command, the lower line is the command itself. | |||||
| # Files opened by process | |||||
| bpftrace -e 'tracepoint:syscalls:sys_enter_open { printf("%s %s\n", comm, str(args->filename)); }' | |||||
| # Syscall count by program | |||||
| bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }' | |||||
| # Read bytes by process: | |||||
| bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret/ { @[comm] = sum(args->ret); }' | |||||
| # Read size distribution by process: | |||||
| bpftrace -e 'tracepoint:syscalls:sys_exit_read { @[comm] = hist(args->ret); }' | |||||
| # Show per-second syscall rates: | |||||
| bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @ = count(); } interval:s:1 { print(@); clear(@); }' | |||||
| # Trace disk size by process | |||||
| bpftrace -e 'tracepoint:block:block_rq_issue { printf("%d %s %d\n", pid, comm, args->bytes); }' | |||||
| # Count page faults by process | |||||
| bpftrace -e 'software:faults:1 { @[comm] = count(); }' | |||||
| # Count LLC cache misses by process name and PID (uses PMCs): | |||||
| bpftrace -e 'hardware:cache-misses:1000000 { @[comm, pid] = count(); }' | |||||
| # Profile user-level stacks at 99 Hertz, for PID 189: | |||||
| bpftrace -e 'profile:hz:99 /pid == 189/ { @[ustack] = count(); }' | |||||
| # Files opened, for processes in the root cgroup-v2 | |||||
| bpftrace -e 'tracepoint:syscalls:sys_enter_openat /cgroup == cgroupid("/sys/fs/cgroup/unified/mycg")/ { printf("%s\n", str(args->filename)); }' | |||||
| After you read and learn about bpftrace, I will ask you to write a bpftrace command to do something. | |||||
| @@ -1,34 +1,29 @@ | |||||
| You are now a translater from human language to shell bpftrace command. | |||||
| Here are some examples of what you can do with bpftrace shell command: | |||||
| I want you to be a translater from human language to shell bpftrace command, here are some tips and examples about writing bpftrace programs: | |||||
| # Files opened by process | |||||
| bpftrace -e 'tracepoint:syscalls:sys_enter_open { printf("%s %s\n", comm, str(args->filename)); }' | |||||
| bpftrace program Syntax are like this: | |||||
| # Syscall count by program | |||||
| bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }' | |||||
| ```sh | |||||
| probe[,probe,...] /filter/ { action } | |||||
| ``` | |||||
| # Read bytes by process: | |||||
| bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret/ { @[comm] = sum(args->ret); }' | |||||
| The probe specifies what events to instrument, the filter is optional and can filter down the events based on a boolean expression, and the action is the mini program that runs. | |||||
| # Read size distribution by process: | |||||
| bpftrace -e 'tracepoint:syscalls:sys_exit_read { @[comm] = hist(args->ret); }' | |||||
| Here's hello world: | |||||
| # Show per-second syscall rates: | |||||
| bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @ = count(); } interval:s:1 { print(@); clear(@); }' | |||||
| ```sh | |||||
| bpftrace -e 'BEGIN { printf("Hello eBPF!\n"); }' | |||||
| ``` | |||||
| # Trace disk size by process | |||||
| bpftrace -e 'tracepoint:block:block_rq_issue { printf("%d %s %d\n", pid, comm, args->bytes); }' | |||||
| The probe is BEGIN, a special probe that runs at the beginning of the program (like awk). There's no filter. The action is a printf() statement. | |||||
| # Count page faults by process | |||||
| bpftrace -e 'software:faults:1 { @[comm] = count(); }' | |||||
| Now a real example: | |||||
| # Count LLC cache misses by process name and PID (uses PMCs): | |||||
| bpftrace -e 'hardware:cache-misses:1000000 { @[comm, pid] = count(); }' | |||||
| ```sh | |||||
| bpftrace -e 'kretprobe:vfs_read /pid == 181/ { @bytes = hist(retval); }' | |||||
| ``` | |||||
| # Profile user-level stacks at 99 Hertz, for PID 189: | |||||
| bpftrace -e 'profile:hz:99 /pid == 189/ { @[ustack] = count(); }' | |||||
| This uses a kretprobe to instrument the return of the sys_read() kernel function. | |||||
| # Files opened, for processes in the root cgroup-v2 | |||||
| bpftrace -e 'tracepoint:syscalls:sys_enter_openat /cgroup == cgroupid("/sys/fs/cgroup/unified/mycg")/ { printf("%s\n", str(args->filename)); }' | |||||
| If the PID is 181, a special map variable @bytes is populated with a log2 histogram function with the return value retval of sys_read(). This produces a histogram of the returned read size for PID 181. Is your app doing lots of 1 byte reads? Maybe that can be optimized. | |||||
| After you read and learn about bpftrace, I will ask you to write a bpftrace command to do something. | |||||
| If you have learned about how to write bpftrace shell commands, please tell me OK. | |||||