挂载函数小结
1.bpf_program__attach_kprobe
struct bpf_link *bpf_program__attach_kprobe(const struct bpf_program *prog,
bool retprobe,
const char *func_name)
{
DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, opts,
.retprobe = retprobe,
);
return bpf_program__attach_kprobe_opts(prog, func_name, &opts);
}
这个函数是用来动态挂载函数的, 第一个参数为bpf_program指针类型的,指向了要挂载的bpf程序,第二个参数是一个bool类型,如果为true,则代表是返回探测点kretprobe,否则是kprobe。第三个参数是一个字符串,表示要附加的bpf程序的函数名称。
2.bpf_program__attach_tracepoint
struct bpf_link *bpf_program__attach_tracepoint(const struct bpf_program *prog,
const char *tp_category,
const char *tp_name)
{
return bpf_program__attach_tracepoint_opts(prog, tp_category, tp_name, NULL);
}
这个函数是用来将一个 BPF 程序附加到内核中的一个追踪点,有三个参数,第一个参数为bpf_program指针类型的,指向了要挂载的bpf程序,第二个参数指定追踪点的类别,第三个参数指定给定类别中追踪点的名称。
内核中追踪点类型及名称在/sys/kernel/debug/tracing/events/
下面查看
静态跟踪点(tracepoint),在用户空间也被称为USDT(用户静态定义跟踪)探针,是应用程序感兴趣的特定位置,可以在其中挂载跟踪器来检查数据和代码执行情况。
3.挂载uprobe
//函数原型
struct bpf_link * bpf_program__attach_uprobe (const struct bpf_program *prog, bool retprobe, pid_t pid, const char *binary_path, size_t func_offset)
//描述
bpf_program__attach_uprobe() attaches a BPF program to the userspace function which is found by binary path and offset. You can optionally specify a particular proccess to attach to. You can also optionally attach the program to the function exit instead of entry.
// 参数
prog – BPF program to attach
retprobe – Attach to function exit
pid – Process ID to attach the uprobe to, 0 for self (own process), -1 for all processes
binary_path – Path to binary that contains the function symbol //可通过/proc/pid/maps找到
func_offset – Offset within the binary of the function symbol
//返回值
Reference to the newly created BPF link; or NULL is returned on error, error code is stored in errno
4.挂载usdt探针
定义USDT探针
在C或C++应用程序中,可以使用DTrace或SystemTap的API定义USDT探针,如下通过 sudo apt-get install systemtap-sdt-dev
安装之后,就能通过使用宏来定义usdt,最多可以包含有12个参数。
例子:
// test_usdt.c
#include <stdio.h>
#include <sys/sdt.h>
int main() {
// 触发 USDT 跟踪点
DTRACE_PROBE2(provider_name, probe_name, 123, "Hello");
return 0;
}
USDT动态挂载函数的详细用法
bpf_program__attach_usdt() is just like bpf_program__attach_uprobe_opts() except it covers USDT (User-space Statically Defined Tracepoint) attachment, instead of attaching to user-space function entry or exit.
//函数原型
struct bpf_link *bpf_program__attach_usdt(const struct bpf_program *prog,
pid_t pid, const char *binary_path,
const char *usdt_provider, const char *usdt_name,
const struct bpf_usdt_opts *opts)
//参数
prog – BPF program to attach
pid – Process ID to attach the uprobe to, 0 for self (own process), -1 for all processes
binary_path – Path to binary that contains provided USDT probe
usdt_provider – USDT provider name
usdt_name – USDT probe name
opts – Options for altering program attachment
//返回值
Reference to the newly created BPF link; or NULL is returned on error, error code is stored in errno
provider及name
测试程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
// 线程函数
void *thread_function(void *arg) {
while (1) {
// 打印当前线程的pid
printf("Thread PID: %d\n", getpid());
// 等待一秒
sleep(1);
}
pthread_exit(NULL);
}
// 创建线程的函数
void create_thread() {
pthread_t thread;
int rc;
// 创建线程
rc = pthread_create(&thread, NULL, thread_function, NULL);
if (rc) {
fprintf(stderr, "Error: pthread_create() failed with code %d\n", rc);
exit(EXIT_FAILURE);
}
}
int main() {
// 打印主进程的pid
printf("Main process PID: %d\n", getpid());
// 调用函数创建线程
create_thread();
// 主进程死循环打印pid
while (1) {
printf("Main process PID: %d\n", getpid());
// 等待一秒
sleep(1);
}
return 0;
}
编译并运行上述测试文件pth,其进程号为67709,于是打开了 /proc/67709/maps
文件,该文件包含了进程地址空间的内存映射信息。首先复制其包含.libc.so.6的行,并保存,代码如下,下图中还打印了该二进制路径bin_path。
static int get_path(char *path,int pid)
{
char mode[16], line[128], buf[64];
size_t seg_start, seg_end, seg_off;
FILE *f;
int i = 0;
sprintf(buf, "/proc/%d/maps", pid);
f = fopen(buf, "r");
if (!f)
return -1;
while (fscanf(f, "%zx-%zx %s %zx %*s %*d%[^\n]\n",
&seg_start, &seg_end, mode, &seg_off, line) == 5) {
i = 0;
while (isblank(line[i]))
i++;
if (strstr(line + i, "libc.so.6")) {
break;
}
}
strcpy(path, line + i);
fclose(f);
return 0;
}
接着,通过readelf -n bin_path
来查看链接文件的追踪点
在用户态修改代码为
// probe a USDT tracepoint
char bin_path[128];
std::string func = probe;
int err = get_path(bin_path,pid);
CHECK_ERR(err, "Fail to get lib path");
skel->links.handle_usdt =
bpf_program__attach_usdt(skel->progs.handle_usdt,pid,bin_path,"libc",strList[2].c_str(),NULL);
CHECK_ERR(!skel->links.handle_usdt, "Fail to attach usdt");
return 0;
bpf.c 增加一个挂载usdt的
SEC("usdt")
int handle_usdt(void *ctx)
{
return handle_func(ctx);
}
下面是探测 pthread_create USDT 跟踪点的调用栈等相关信息