Linux tracepoint使用和理解

参考:如何使用 TRACE_EVENT() 宏来创建跟踪点

1.如何查看tracepoint

/sys/kernel/debug/tracing/events/

通过perf list tracepoint查看

2. tracepoint理解

因此tracepoint方式如下:

#undef TRACE_SYSTEM
#define TRACE_SYSTEM xhr_test

#if !defined(_TRACE_TE_TEST_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_TE_TEST_H

#include <linux/tracepoint.h>

TRACE_EVENT(te_test,  // 定义函数trace_te_test
    TP_PROTO(int num), // 函数接收参数 int 类型
    TP_ARGS(num),
    TP_STRUCT__entry(
        __field(int, output)  // 字段名
        __field(int, count)   // 字段名
    ),
    TP_fast_assign(
        __entry->count++;
        __entry->output = num; // output 等于传入的参数 num
    ),
    TP_printk("count=%d output=%d",
        __entry->count, __entry->output)
);

#endif /* _TRACE_TE_TEST_H */
    
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH . //定义路径,找不到你就写绝对路径
#define TRACE_INCLUDE_FILE trace_event //定义文件 trace_event.h

#include <trace/define_trace.h>

因此,只要调用 trace_te_test(int num)即可

3.快速查找tracepoint

grep -rn TRACE_EVENT(name, ...)

4.Android中的tracepoint

使用ebpf扩展内核

下面是官网的例子:

#include <linux/bpf.h>
#include <stdbool.h>
#include <stdint.h>
#include <bpf_helpers.h>

DEFINE_BPF_MAP(cpu_pid_map, ARRAY, int, uint32_t, 1024);

struct switch_args {
    unsigned long long ignore;
    char prev_comm[16];
    int prev_pid;
    int prev_prio;
    long long prev_state;
    char next_comm[16];
    int next_pid;
    int next_prio;
};

DEFINE_BPF_PROG("tracepoint/sched/sched_switch", AID_ROOT, AID_SYSTEM, tp_sched_switch)
(struct switch_args *args) {
    int key;
    uint32_t val;

    key = bpf_get_smp_processor_id();
    val = args->next_pid;

    bpf_cpu_pid_map_update_elem(&key, &val, BPF_ANY);
    return 1; // return 1 to avoid blocking simpleperf from receiving events
}

LICENSE("GPL");

struct switch_args是干嘛的,我可以把结构体改了吗? 假如我使用其他tracepoint,我改怎么定义这个结构体? 网上千篇一律的复制粘贴文章,总算找到个解释的. Android之TRACEPOINT

这里的switch_args结构体,构成可以从/sys/kernel/debug/tracing/events/sched/sched_switch/format得到,现在我们再来看struct switch_args就能理解了

name: sched_switch
ID: 52
format:
	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
	field:unsigned char common_flags;	offset:2;	size:1;	signed:0;
	field:unsigned char common_preempt_count;	offset:3;	size:1;	signed:0;
	field:int common_pid;	offset:4;	size:4;	signed:1;            // 对应到 switch_args.ignore (long long 八个字节)

	field:char prev_comm[16];	offset:8;	size:16;	signed:0;    // 对应到 switch_args.prev_comm 字段 16字节
	field:pid_t prev_pid;	offset:24;	size:4;	signed:1;            // 对应 switch_args.prev_pid  4字节
	field:int prev_prio;	offset:28;	size:4;	signed:1;
	field:long prev_state;	offset:32;	size:8;	signed:1;
	field:char next_comm[16];	offset:40;	size:16;	signed:0;
	field:pid_t next_pid;	offset:56;	size:4;	signed:1;
	field:int next_prio;	offset:60;	size:4;	signed:1;

switch_args.ignorelong long类型,共8个字节,因此对应到 format中的 common_pid,以此类推

5.实战

在设备上随便找了个task_rename跟踪点进行测试,不再是千篇一律的sched_switch
PS: 注意看自己设备有没有这个 tracepoint

# cat /sys/kernel/debug/tracing/events/task/task_rename/format                                                               
name: task_rename
ID: 27
format:
	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
	field:unsigned char common_flags;	offset:2;	size:1;	signed:0;
	field:unsigned char common_preempt_count;	offset:3;	size:1;	signed:0;
	field:int common_pid;	offset:4;	size:4;	signed:1;

	field:pid_t pid;	offset:8;	size:4;	signed:1;
	field:char oldcomm[16];	offset:12;	size:16;	signed:0;
	field:char newcomm[16];	offset:28;	size:16;	signed:0;
	field:short oom_score_adj;	offset:44;	size:2;	signed:1;

print fmt: "pid=%d oldcomm=%s newcomm=%s oom_score_adj=%hd", REC->pid, REC->oldcomm, REC->newcomm, REC->oom_score_adj

对应代码:

// bpf_trace_task_rename.c

#include <linux/bpf.h>
#include <stdbool.h>
#include <stdint.h>
#include <bpf_helpers.h>

#include <string.h>

struct task_rename_args {
    unsigned long long ignore;
    int pid;
    char oldcomm[16];
    char newcomm[16];
    short oom_score_adj;
};

struct data {
    int pid;
    char newcomm[16];
};

DEFINE_BPF_MAP(task_rename_map, ARRAY, int, struct data, 1024);

// SEC("tracepoint/sched/sched_switch")
DEFINE_BPF_PROG("tracepoint/task/task_rename", AID_ROOT, AID_NET_ADMIN, tp_sched_switch) 
(struct task_rename_args* args) {
    int key = 100;
    struct data data_t;
    // 这行打开导致bpfload 加载prog失败,权限不足. 可能是因为我设备特殊性,真机没试过
    //bpf_trace_printk("pid %ld %s %s\n", args->pid, args->oldcomm, args->newcomm);

    data_t.pid = args->pid;
    //data_t.newcomm = args->newcomm;
    memcpy(data_t.newcomm, args->newcomm, 16);
    
    bpf_task_rename_map_update_elem(&key, &data_t, BPF_ANY);
    return 0;
}

// char _license[] SEC("license") = "GPL";
LICENSE("Apache 2.0");

cli程序

// bpf_trace_task_rename_cli.cpp
#include <android-base/macros.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <bpf/BpfMap.h> // system/bpf/libbpf_android/include/bpf/BpfMap.h
#include <bpf/BpfUtils.h>
#include <libbpf_android.h>

struct data {
    int pid;
    char newcomm[16];
};

int main() {
    constexpr const char tp_prog_path[] = "/sys/fs/bpf/prog_bpf_trace_task_rename_tracepoint_task_task_rename";
    constexpr const char tp_map_path[] = "/sys/fs/bpf/map_bpf_trace_task_rename_task_rename_map";

    // Attach tracepoint and wait for 4 seconds
    int mProgFd = bpf_obj_get(tp_prog_path);
    // int mMapFd = bpf_obj_get(tp_map_path);

    bpf_attach_tracepoint(mProgFd, "task", "task_rename");
    sleep(1);
    // 高版本使用fd构造BpfMap
    android::bpf::BpfMap<int, struct data> myMap(tp_map_path);
 
    pid_t pid = getpid();
    printf("pid: %d\n", pid);

    while(1) {
        usleep(40000);
        // android::base::Result<Value> 类型
        data data_t = myMap.readValue(100).value();
        printf("PID %d rename to %s\n", data_t.pid, data_t.newcomm);
    }

    exit(0);
}

bpf {
    name: "bpf_task_rename.o",
    srcs: ["bpf_trace_task_rename.c"],
    cflags: [
        "-Wall",
        "-Werror",
    ],
}

cc_binary {
    name: "bpf_trace_task_rename_cli",

    cflags: [
        "-Wall",
        "-Werror",
        "-Wthread-safety",
    ], 
    clang: true,
    shared_libs: [
        "libcutils",
        "libbpf_android",
        "libbase",
        "liblog",
        "libnetdutils",
        "libbpf",
    ],
    srcs: [
        "bpf_trace_task_rename_cli.cpp",
    ],
}

最后效果

posted @ 2023-12-15 11:48  梦过无声  阅读(1595)  评论(0)    收藏  举报