前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >linux tracepoint增加

linux tracepoint增加

作者头像
233333
发布于 2024-04-04 01:54:35
发布于 2024-04-04 01:54:35
38400
代码可运行
举报
运行总次数:0
代码可运行

引入tracepoint的背景

当需要获取内核的debug信息时,通常你会通过以下printk的方式打印信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void trace_func()
{
    //……
    printk("输出信息");
    //……
}

缺点:

  • 内核中printk是统一控制的,各个模块的printk都会被打印,无法只打印需要关注的模块
  • 如果需要修改/新增打印信息,需要修改所有受影响的printk语句。这些printk分散在代码多处,每个地方都需要修改
  • 嵌入式系统中,如果printk信息量大,console(如果有)有大量的打印输出,用户无法在console输入命令,影响人机交互

内核解决方案

内核采用“插桩”的方法抓取log,“插桩”也称为Tracepoint,Tracepoint是Linux内核预先定义的静态探测点,它分布于内核的各个子系统中,每种Tracepoint有一个name、一个enable开关、一系列桩函数、注册桩函数的函数、卸载桩函数的函数。“桩函数”功能类似于printk,不过“桩函数”并不会把信息打印到console,而是输出到内核的ring buffer(环形缓冲区),缓冲区中的信息通过debugfs对用户呈现。每个tracepoint提供一个钩子来调用probe函数。一个tracepoint可以打开或关闭。打开时,probe函数关联到tracepoint;关闭时,probe函数不关联到tracepoint。tracepoint关闭时对kernel产生的影响很小,只是增加了极少的时间开销(一个分支条件判断),极小的空间开销(一条函数调用语句和几个数据结构)。只有挂载了钩子函数才会真正启用trace功能。这个钩子函数可以由开发者编写内核module来实现,并且需要在钩子函数中获取我们调试所需要的信息并导出到用户态,这样就可以获取内核运行时的信息了。当一个tracepoint打开时,用户提供的probe函数在每次这个tracepoint执行都会被调用。

直接使用tracepoint并不是那么的容易,因此内核提供了event trace功能。event trace的实现依赖于tracepoint机制,内核提前帮我们实现了钩子函数并挂到tracepoint上,当使能一个event trace时,它会输出内容到ftrace ringbuffer中,这样就可以获取到内核运行信息了。当然有时候event trace并不符合我们的需要,那么此时就只得自己编写module来实现需求了。

查看tracepoint

可通过debugfs查看:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
root: /5g_build/5g_Main/$ ls /sys/kernel/debug/tracing/events/
block             dma_fence   ext4      ftrace        hyperv     irq          libata   mpx   oom      qdisc         rcu     scsi    sunrpc    udp        writeback
bridge            drm         filelock  gpio          i2c        irq_vectors  mce      napi  pagemap  random        regmap  signal  syscalls  vmscan     xen
compaction        enable      filemap   header_event  intel_ish  jbd2         migrate  net   power    ras           rpm     skb     task      vsyscall   xhci-hcd
context_tracking  exceptions  fs_dax    header_page   iommu      kmem         module   nfs   printk   raw_syscalls  sched   sock    timer     workqueue

系统中定义的tracepoint都在该events目录中,比如查看block子系统中的tracepoint:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
root: /5g_build/5g_Main/$ ls /sys/kernel/debug/tracing/events/block/
block_bio_backmerge  block_bio_complete    block_bio_queue  block_dirty_buffer  block_plug      block_rq_complete  block_rq_issue  block_rq_requeue  block_split         block_unplug  filter
block_bio_bounce     block_bio_frontmerge  block_bio_remap  block_getrq         block_rq_abort  block_rq_insert    block_rq_remap  block_sleeprq     block_touch_buffer  enable

还可以通过perf查看:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 root: /5g_build/5g_Main/$ perf list tracepoint
  block:block_bio_backmerge                          [Tracepoint event]
  block:block_bio_bounce                             [Tracepoint event]
  block:block_bio_complete                           [Tracepoint event]
  block:block_bio_frontmerge                         [Tracepoint event]
  block:block_bio_queue                              [Tracepoint event]
  block:block_bio_remap                              [Tracepoint event]
  block:block_dirty_buffer                           [Tracepoint event]
  block:block_getrq                                  [Tracepoint event]
  block:block_plug                                   [Tracepoint event]
  block:block_rq_abort                               [Tracepoint event]
  block:block_rq_complete                            [Tracepoint event]
  block:block_rq_insert                              [Tracepoint event]
  block:block_rq_issue                               [Tracepoint event]
  block:block_rq_remap                               [Tracepoint event]
  block:block_rq_requeue                             [Tracepoint event]
  block:block_sleeprq                                [Tracepoint event]
  block:block_split                                  [Tracepoint event]
  block:block_touch_buffer                           [Tracepoint event]
  block:block_unplug                                 [Tracepoint event]
  bridge:br_fdb_add                                  [Tracepoint event]
  bridge:br_fdb_external_learn_add                   [Tracepoint event]
  bridge:br_fdb_update                               [Tracepoint event]
  bridge:fdb_delete                                  [Tracepoint event]
  compaction:mm_compaction_isolate_freepages         [Tracepoint event]
  compaction:mm_compaction_isolate_migratepages      [Tracepoint event]

Tracepoint数据格式

每个tracepoint都会按照自己定义的格式来输出信息,可以在用户态来查看tracepoint记录的内容格式,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
root: /5g_build/5g_Main/$ cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_open/format
name: sys_enter_open
ID: 484
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:int nr;   offset:8;       size:4; signed:1;
        field:const char * filename;    offset:16;      size:8; signed:0;
        field:int flags;        offset:24;      size:8; signed:0;
        field:umode_t mode;     offset:32;      size:8; signed:0;

print fmt: "filename: 0x%08lx, flags: 0x%08lx, mode: 0x%08lx", ((unsigned long)(REC->filename)), ((unsigned long)(REC->flags)), ((unsigned long)(REC->mode))

格式信息可以用来解析二进制的trace流数据,也可以用这个格式中的内容来做trace filter,利用filter功能指定过滤条件后,将只会看到过滤后的事件数据。格式信息中包括两部分内容:

  • 第一部分是通用的格式,这类通用字段都带有common前缀,这是所有的tracepoint event都具备的字段
  • 第二部分就是各个tracepoint所自定义的格式字段,比如上面的nr,filename等等。格式信息的最后一列是tracepoint的打印内容格式,通过这个可以看到打印数据的字段来源。

如何使用Tracepoint

tracepoint复用了ftrace的ringbuffer,当使能了一个tracepoint之后,可以通过cat /sys/kernel/debug/tracing/trace来查看它的输出。另外上文也有提到,还可以通过设置filter过滤事件。

比如想要过滤所有的openat事件,指定过滤所有打开flags为0的事件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
echo flags==0 > /sys/kernel/debug/tracing/events/syscalls/sys_enter_openat/filter
echo 1 > /sys/kernel/debug/tracing/events/syscalls/sys_enter_openat/enable
cat /sys/kernel/debug/tracing/trace_pipe

如果想要把过滤条件清除,只需要写入0到指定的filter即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
echo 0 > /sys/kernel/debug/tracing/events/syscalls/sys_enter_openat/filter

如果想要跟踪sched_switch这个tracepoint点,设置过滤条件prev_pid和next_pid都为1的进程:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
root: /5g_build/5g_Main/$ echo "(prev_pid == 1 || next_pid == 1)" >  /sys/kernel/debug/tracing/events/sched/sched_switch/filter
root: /5g_build/5g_Main/$ echo 1 > /sys/kernel/debug/tracing/events/sched/sched_switch/enable
root: /5g_build/5g_Main/$ cat /sys/kernel/debug/tracing/trace_pipe
          <idle>-0     [006] d... 2741443.520814: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741443.522103: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741443.522141: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741443.522816: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741443.525867: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741443.526448: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741443.727718: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741443.729145: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741443.729190: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741443.729864: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741443.733095: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741443.733663: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741444.799623: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741444.799777: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741446.049617: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741446.049761: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741446.114969: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741446.116232: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741446.116298: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741446.116756: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741446.119312: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741446.119702: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741446.324820: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741446.326196: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741446.326251: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741446.326945: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [006] d... 2741446.329697: sched_switch: prev_comm=swapper/6 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [006] d... 2741446.330272: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
          <idle>-0     [000] d... 2741446.526418: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [000] d... 2741446.527526: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
          <idle>-0     [000] d... 2741446.527569: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [000] d... 2741446.528133: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
          <idle>-0     [000] d... 2741446.531700: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [000] d... 2741446.532191: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
          <idle>-0     [004] d... 2741446.730287: sched_switch: prev_comm=swapper/4 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [004] d... 2741446.731656: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/4 next_pid=0 next_prio=120
          <idle>-0     [004] d... 2741446.731683: sched_switch: prev_comm=swapper/4 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
         systemd-1     [004] d... 2741446.732349: sched_switch: prev_comm=systemd prev_pid=1 prev_prio=120 prev_state=S ==> next_comm=swapper/4 next_pid=0 next_prio=120
          <idle>-0     [004] d... 2741446.734980: sched_switch: prev_comm=swapper/4 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=systemd next_pid=1 next_prio=120
...

可以看到对应的prev_id或者next_pid为1时才会输出

perf中使用tracepoint

借助perf工具,可以跟踪tracepoint事件,前文已经介绍了如何用perf查看tracepoint,假如想要跟踪网络驱动收发包的情况:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
perf record -e 'net:netif_rx','net:net_dev_queue' -a   -- sleep 10

记录10秒后,使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
perf script

来查看记录到的信息,截取部分示例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
           node  1072 [001] 2741584.673790: net:net_dev_queue: dev=eth0 skbaddr=0xffff92c5bb698af8 len=214
            node  1072 [001] 2741584.673794: net:net_dev_queue: dev=veth_558d087c skbaddr=0xffff92c5bb698af8 len=214
            node  1072 [001] 2741584.673815: net:net_dev_queue: dev=eth0 skbaddr=0xffff92c5bb698af8 len=264
            grep     0 [001] 2741584.680569:      net:netif_rx: dev=veth_558d087c skbaddr=0xffff92ca6e714c00 len=155
            node  1072 [001] 2741584.683656: net:net_dev_queue: dev=eth0 skbaddr=0xffff92c5bb698cf8 len=98
            node  1072 [001] 2741584.683661: net:net_dev_queue: dev=veth_558d087c skbaddr=0xffff92c5bb698cf8 len=98
            node  1072 [001] 2741584.683682: net:net_dev_queue: dev=eth0 skbaddr=0xffff92c5bb698cf8 len=148
            grep     0 [001] 2741584.744696:      net:netif_rx: dev=veth_558d087c skbaddr=0xffff92ca6e715800 len=40
            grep     0 [001] 2741584.744703:      net:netif_rx: dev=veth_558d087c skbaddr=0xffff92ca736b8800 len=40
             awk     0 [006] 2741584.862150: net:net_dev_queue: dev=eth0 skbaddr=0xffff92c5fea64100 len=66
       ovs-ofctl     0 [000] 2741584.882151: net:net_dev_queue: dev=eth0 skbaddr=0xffff92caa3dafef8 len=208
       ovs-ofctl     0 [000] 2741584.883350: net:net_dev_queue: dev=eth0 skbaddr=0xffff92c5d2a8c100 len=66
       ovs-ofctl     0 [000] 2741584.883375: net:net_dev_queue: dev=eth0 skbaddr=0xffff92c5d2a8db00 len=66
       ovs-ofctl     0 [001] 2741584.884316: net:net_dev_queue: dev=eth0 skbaddr=0xffff92c5bb69b6f8 len=263
       ovs-ofctl     0 [001] 2741584.885924: net:net_dev_queue: dev=eth0 skbaddr=0xffff92caa3cf9b00 len=66
       ovs-ofctl     0 [001] 2741584.888257: net:net_dev_queue: dev=eth0 skbaddr=0xffff92caa3cf9a00 len=66
       ovs-ofctl     0 [001] 2741584.888843: net:net_dev_queue: dev=eth0 skbaddr=0xffff92c5bb69b6f8 len=280
       ovs-ofctl     0 [001] 2741584.888924: net:net_dev_queue: dev=eth0 skbaddr=0xffff92c5bb698cf8 len=1107
       ovs-ofctl     0 [001] 2741584.888946: net:net_dev_queue: dev=eth0 skbaddr=0xffff92c5bb6984f8 len=104
       ovs-ofctl     0 [001] 2741584.889618: net:net_dev_queue: dev=eth0 skbaddr=0xffff92caa3cf9a00 len=66
       ovs-ofctl     0 [001] 2741584.892419: net:net_dev_queue: dev=eth0 skbaddr=0xffff92caa3cf9800 len=66
              wc     0 [001] 2741584.985872:      net:netif_rx: dev=veth_558d087c skbaddr=0xffff92caa3cf9a00 len=103
              wc     0 [001] 2741584.986657: net:net_dev_queue: dev=eth0 skbaddr=0xffff92caa3cf8000 len=66
            node  1072 [005] 2741584.989219: net:net_dev_queue: dev=eth0 skbaddr=0xffff92ca6fe748f8 len=109
            node  1072 [005] 2741584.989222: net:net_dev_queue: dev=veth_558d087c skbaddr=0xffff92ca6fe748f8 len=109
            node  1072 [005] 2741584.989244: net:net_dev_queue: dev=eth0 skbaddr=0xffff92ca6fe748f8 len=159
              wc     0 [005] 2741585.001911:      net:netif_rx: dev=veth_558d087c skbaddr=0xffff92caa3cf9b00 len=82
            node  1072 [005] 2741585.004758: net:net_dev_queue: dev=eth0 skbaddr=0xffff92ca6fe75af8 len=99
            node  1072 [005] 2741585.004762: net:net_dev_queue: dev=veth_558d087c skbaddr=0xffff92ca6fe75af8 len=99
            node  1072 [005] 2741585.004798: net:net_dev_queue: dev=eth0 skbaddr=0xffff92ca6fe75af8 len=149
           pidof     0 [005] 2741585.053168:      net:netif_rx: dev=veth_558d087c skbaddr=0xffff92ca702cf400 len=83
            node  1072 [000] 2741585.056084: net:net_dev_queue: dev=eth0 skbaddr=0xffff92caa3dac6f8 len=99
            node  1072 [000] 2741585.056089: net:net_dev_queue: dev=veth_558d087c skbaddr=0xffff92caa3dac6f8 len=99
            node  1072 [000] 2741585.056110: net:net_dev_queue: dev=eth0 skbaddr=0xffff92caa3dac6f8 len=149
             awk     0 [000] 2741585.103222:      net:netif_rx: dev=veth_558d087c skbaddr=0xffff92ca6e615d00 len=40
    cpptools-srv 28070 [000] 2741585.184113:      net:netif_rx: dev=veth_558d087c skbaddr=0xffff92ca6e615600 len=87
            node  1072 [004] 2741585.186539: net:net_dev_queue: dev=eth0 skbaddr=0xffff92c5d2f152f8 len=110

添加Tracepoint

数据结构

代码路径

DEFINE_TRACE(name)DECLARE_TRACE(name, proto, args)

include/linux/tracepoint.h

struct tracepoint

include/linux/tracepoint-defs.h

  1. Tracepoint依次执行桩函数,每个桩函数实现不同的debug功能。内核通过register_trace_##name将桩函数添加到Tracepoint中,通过unregister_trace_##name从trace point中移除。(注:##表示字符串连接)
  2. Tracepoint依次执行桩函数,每个桩函数实现不同的debug功能。内核通过register_trace_##name将桩函数添加到Tracepoint中,通过unregister_trace_##name从trace point中移除。(注:##表示字符串连接)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
tracepoint {
    const char *name;       /* Tracepoint name */
    struct static_key key;
    int (*regfunc)(void);
    void (*unregfunc)(void);
    struct tracepoint_func __rcu *funcs;
};
  • name:Tracepoint的名字,内核中通过hash表管理所有的trace point,找到对应的hash slot后,需要通过name来识别具体的trace_point
  • key:Tracepoint状态,1表示disable,0表示enable
  • regfunc:添加桩函数的函数
  • unregfunc:卸载桩函数的函数
  • funcs:Tracepoint中所有的桩函数链表
  1. 内核通过#define DECLARE_TRACE(name, proto, args)定义trace point用到的函数,定义的函数原型如下(从代码中摘取了几个,不止以下3个):
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args)
extern struct tracepoint __tracepoint_##name;
static inline void trace_##name(proto)
{
    if (static_key_false(&__tracepoint_##name.key))
        __DO_TRACE(&__tracepoint_##name,
        TP_PROTO(data_proto),
        TP_ARGS(data_args),
        TP_CONDITION(cond),,);
}
register_trace_##name(void (*probe)(data_proto), void *data)
{      
    return tracepoint_probe_register(&__tracepoint_##name,
    (void *)probe, data);
}
unregister_trace_##name(void (*probe)(data_proto), void *data)
{
    return tracepoint_probe_unregister(&__tracepoint_##name,(void *)probe, data);
}

第2行声明一个外部trace point变量。然后定义了一些trace point用到的公共函数。

第5行判断trace point是否disable,如果没有disable,那么调用__DO_TRACE遍历执行trace point中的桩函数(通过“函数指针”来实现执行桩函数)。

trace point提供了统一的框架,用void *指向任何函数,所以各个trace point取出桩函数指针后,需要转换成自己的函数指针类型,TP_PROTO(data_proto)传递函数指针类型用于转换,具体的转换在:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define __DO_TRACE(tp, proto, args, cond, rcuidle)
do {
    struct tracepoint_func *it_func_ptr;
    void *it_func;
    void *__data;
    ……
    it_func_ptr = rcu_dereference_raw((tp)->funcs);
    if (it_func_ptr) {
        do {
            it_func = (it_func_ptr)->func;
            __data = (it_func_ptr)->data;
            ((void(*)(proto))(it_func))(args);
        } while ((++it_func_ptr)->func);
    }

    ……
}while(0)
  1. 桩函数的proto的传递的例子
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_write,
    TP_PROTO(struct page *page, struct f2fs_io_info *fio),
    TP_ARGS(page, fio),
    TP_CONDITION(page->mapping)
);

第2行声明了桩函数原型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define DEFINE_EVENT_CONDITION(template, name, proto, args, cond)
     DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args))
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define DEFINE_EVENT_CONDITION(template, name, proto, args, cond)
     DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args))
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define DECLARE_TRACE(name, proto, args)
    __DECLARE_TRACE(name, PARAMS(proto), PARAMS(args),             
    cpu_online(raw_smp_processor_id()),             
    PARAMS(void *__data, proto),                   
    PARAMS(__data, args))

至此执行到__DECLARE_TRACE宏,参考前面说明,提到了何时转换成桩函数指针类型。

从上面可以看出trace point的机制很简单,就是把用于debug的函数指针组织在一个struct trace point变量中,然后依次执行各个函数指针。不过为了避免各个模块重复写代码,内核用了比较复杂的宏而已。

另外也可以发现,使用trace point必须要通过register_trace_##name将桩函数(也就是我们需要的debug函数)添加到trace point中,这个工作只能通过moudule或者修改内核代码实现,对于开发者来说,操作比较麻烦。ftrace开发者们意识到了这点,所以提供了trace event功能,开发者不需要自己去注册桩函数了,易用性较好,后面文章会谈到trace event是如何实现的以及如何使用。

如果用户准备为kernel加入新的tracepoint,每个tracepoint则以下列格式声明:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <linux/tracepoint.h>
DECLARE_TRACE(tracepoint_name,
    TPPROTO(trace_function_prototype),
    TPARGS(trace_function_args));

上面的宏定义了一个新的tracepoint叫tracepoint_name。与这个tracepoint关联的probe函数必须与TPPROTO宏定义的函数prototype一致,probe函数的参数列表必须与TPARGS宏定义的一致。

或许用一个例子来解释会比较容易理解。Kernel里面已经包含了一些tracepoints,其中一个叫做sched_wakeup,这个tracepoint在每次scheduler唤醒一个进程时都会被调用。它是这样定义的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DECLARE_TRACE(sched_wakeup,
    TPPROTO(struct rq *rq, struct task_struct *p),
    TPARGS(rq, p))

实际在kernel中插入这个tracepoint点的是一行如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
trace_sched_wakeup(rq, p);

注意,插入tracepoint的函数名就是将trace_前缀添加到tracepoint_name的前面。除非有一个实际的probe函数关联到这个tracepoint,trace_sched_wakeup()这个只是一个空函数。下面的操作就是将一个probe函数关联到一个tracepoint:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void my_sched_wakeup_tracer(struct rq *rq, struct task_struct *p);
register_trace_sched_wakeup(my_sched_wakeup_tracer);

register_trace_sched_wakeup()函数实际上是DEFINE_TRACE()定义的,它把probe函数my_sched_wakeup_tracer()和tracepoint sched_wakeup关联起来

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

TRACE_EVENT() 宏的剖析

自动跟踪点具有各种必须满足的要求:

  • 它必须创建一个可以放在内核代码中的跟踪点
  • 它必须创建一个可以挂接到此跟踪点的回调函数
  • 回调函数必须能够以最快的方式将传递给它的数据记录到跟踪环缓冲区中
  • 它必须创建一个函数,可以解析记录到环形缓冲区的数据,并将其转换为跟踪程序可以显示给用户的可读格式

为此,TRACE_EVENT() 宏分为六个组件,它们对应于宏的参数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
TRACE_EVENT(name, proto, args,struct, assign, print)
  • name- 要创建的跟踪点的名称
  • prototype- 跟踪点回调函数的原型
  • args- 与函数原型匹配的参数
  • struct - 跟踪点可以使用(并不是必须的)这个结构来存储传入到跟踪点的数据。
  • assign- 将数据分配给结构的类似于C的方法
  • print- 以人类可读的ASCII格式输出结构的方法

举例 这里可以找到sched_switch跟踪点定义的一个很好的例子。下面将使用该定义来描述TRACE_EVENT()宏的每个部分

除第一个参数外,所有参数都用另一个宏(TP_PROTO、TP_ARGS、TP_STRUCT_uentry、TP_fast_assign和TP_printk)封装。这些宏在处理过程中提供了更多的控制,还允许在TRACE_EVENT()宏中使用逗号

Name 第一个参数是名称。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
TRACE_EVENT(sched_switch

这是用于调用此跟踪点的名称。实际使用的跟踪点的名称前面有trace_前缀(即trace_sched_switch)

Prototype 下一个参数是原型。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)

原型的编写就像您要直接声明跟踪点一样:

trace_sched_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next);

它用作添加到内核代码的跟踪点和回调函数的原型。请记住,跟踪点调用回调函数,就好像在跟踪点的位置调用回调函数一样

Arguments 第三个参数是原型使用的参数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
TP_ARGS(rq, prev, next)

这可能看起来很奇怪,但它不仅是TRACE_EVENT()宏所必需的,而且也是下面的跟踪点基础结构所必需的。跟踪点代码在激活时将调用回调函数(可以将多个回调分配给给定的跟踪点)。创建跟踪点的宏必须同时访问原型和参数。下面是跟踪点宏完成此操作所需的示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define TRACE_POINT(name, proto, args) //\
        void trace_##name(proto)            \
        {                                   \
                if (trace_##name##_active)  \
                        callback(args);     \
        }

Structure

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
TP_STRUCT__entry(
	__array(	char,	prev_comm,	TASK_COMM_LEN	)
	__field(	pid_t,	prev_pid			)
	__field(	int,	prev_prio			)
	__field(	long,	prev_state			)
	__array(	char,	next_comm,	TASK_COMM_LEN	)
	__field(	pid_t,	next_pid			)
	__field(	int,	next_prio			)
),

此参数描述将存储在跟踪器环缓冲区中的数据的结构布局。结构的每个元素都由另一个宏定义。这些宏用于自动创建结构,而不是功能类似的。注意宏之间没有任何分隔符(没有逗号或分号)

sched_switch tracepoint使用的宏有:

  • __field(type, name)- 它定义了一个普通的结构元素,比如int var;其中type是int,name是var
  • __array(type, name, len)- 这将定义一个数组项,相当于int name[len];其中type是int,数组的名称是array,数组中的项数是len 还有其他元素宏将在后面的文章中介绍。来自sched_switch tracepoint的定义将生成一个如下所示的结构:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct {
	char   prev_comm[TASK_COMM_LEN];
	pid_t  prev_pid;
	int    prev_prio;
	long   prev_state;
	char   next_comm[TASK_COMM_LEN];
	pid_t  next_pid;
	int    next_prio;
};

Assignment

第五个参数定义了将参数中的数据保存到环形缓冲区的方式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
TP_fast_assign(
		memcpy(__entry->next_comm, next->comm, TASK_COMM_LEN);
		__entry->prev_pid	= prev->pid;
		__entry->prev_prio	= prev->prio;
		__entry->prev_state	= prev->state;
		memcpy(__entry->prev_comm, prev->comm, TASK_COMM_LEN);
		__entry->next_pid	= next->pid;
		__entry->next_prio	= next->prio;
),
  • TP_fast_assign()中的代码是普通的C代码。一个特殊的变量 __entry表示指向由TP_STRUCT__entry项定义的结构类型的指针,并直接指向环缓冲区。TP_fast_assign用于填充TP_STRUCT_entry中创建的所有字段。然后,可以使用TP_PROTO()和TP_ARGS()定义的参数的变量名来将适当的数据分配到该输入结构中

Print

最后一个参数定义如何使用printk()从TP_STRUCT_uentry结构中打印字段

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
TP_printk("prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s ==> " \
 		  "next_comm=%s next_pid=%d next_prio=%d",
		__entry->prev_comm, __entry->prev_pid, __entry->prev_prio,
		__entry->prev_state ?
		  __print_flags(__entry->prev_state, "|",
				{ 1, "S"} , { 2, "D" }, { 4, "T" }, { 8, "t" },
				{ 16, "Z" }, { 32, "X" }, { 64, "x" },
				{ 128, "W" }) : "R",
		__entry->next_comm, __entry->next_pid, __entry->next_prio)

变量 __entry 再次被用来引用包含数据的结构体的指针。Format string 就像其他 printf 的 format 一样。__print_flags() 是 TRACE_EVENT() 附带的一组帮助函数的一部分,不要创建新的跟踪点特定的帮助程序,因为这会混淆用户空间工具,这些工具知道TRACE_EVENT() helper宏,但不知道如何处理为单个跟踪点创建的宏

头文件

TRACE_EVENT()宏不能仅仅放置在期望它能与Ftrace或任何其他跟踪程序一起工作的任何位置。包含TRACE_EVENT()宏的头文件必须遵循特定格式。这些头文件通常位于include/trace/events目录中,但不需要。如果它们不在此目录中,则需要其他配置

TRACE_EVENT()头中的第一行不是普通的ifdef_TRACE_SCHED_H,而是

此示例用于调度程序跟踪事件,其他事件头将使用除sched和trace_sched_H之外的其他内容。trace_HEADER_MULTI_READ test允许此文件包含多次;这对于trace_event()宏的处理非常重要。还必须为文件定义跟踪系统,并且跟踪系统必须在if的保护范围之外。TRACE_系统定义文件中TRACE_EVENT()宏所属的组。这也是事件将在debugfs tracing/events目录中分组的目录名。此分组对Ftrace很重要,因为它允许用户按组启用或禁用事件

然后,该文件包含TRACE_EVENT()宏内容所需的任何头。(例如,include<linux/sched.h>)。tracepoint.h文件是必需的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <linux/tracepoint.h>

现在可以使用trace_EVENT()宏定义所有跟踪事件。请在TRACE_EVENT()宏上方包含描述跟踪点的注释。以include/trace/events/sched.h为例。文件结尾为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#endif /* _TRACE_SCHED_H */

/* This part must be outside protection */
#include <trace/define_trace.h>

define_trace.h是创建跟踪点的所有魔力所在,这个文件必须包含在跟踪头文件的底部,而不受endif的保护

使用tracepoint

如果跟踪点不在任何地方使用,那么定义它就没有意义。要使用跟踪点,必须包含跟踪头,但在包含跟踪之前,还必须定义一个C文件(并且只有一个)的创建跟踪点。这将导致define_trace.h创建生成跟踪事件所需的必要函数。在kernel/sched.c中定义了以下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define CREATE_TRACE_POINTS
#include <trace/events/sched.h>

如果另一个文件需要使用跟踪文件中定义的跟踪点,则它只需要包含跟踪文件,而不需要定义创建跟踪点。为同一头文件定义多次将在生成时导致链接器错误。例如,在kernel/fork.c中,只包含头文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <trace/events/sched.h>

最后,在代码中使用跟踪点,就像在TRACE_EVENT()宏中定义的一样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static inline void
context_switch(struct rq *rq, struct task_struct *prev,
				struct task_struct *next)
{
	struct mm_struct *mm, *oldmm;
	
	prepare_task_switch(rq, prev, next);
	trace_sched_switch(rq, prev, next);
	mm = next->mm;
	oldmm = prev->active_mm;

实际使用

  • module_traceevent.h
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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,
    TP_PROTO(int num),
    TP_ARGS(num),
    TP_STRUCT__entry(
        __field(int, output)
        __field(int, count)
    ),
    TP_fast_assign(
        __entry->count++;
        __entry->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 module_traceevent

#include <trace/define_trace.h>
  • module_entry.c
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>

#define CREATE_TRACE_POINTS
#include "module_traceevent.h"

static int xhr_thread(void *arg)
{
    static unsigned long count;

    while (!kthread_should_stop())
    {
        set_current_state(TASK_INTERRUPTIBLE);
        schedule_timeout(HZ);
        trace_te_test(count);
        count++;
    }

    return 0;
}

static struct task_struct *xhr_task;

static __init int __module_init(void)
{
    printk("Hello, %s.\n", __func__);

    xhr_task = kthread_run(xhr_thread, NULL, "xhr-thread");
    if (IS_ERR(xhr_task))
        return -1;

    return 0;
}
static __exit void __module_exit(void)
{
    kthread_stop(xhr_task);
    printk("Hello, %s.\n", __func__);
    return;
}

module_init(__module_init);
module_exit(__module_exit);
MODULE_LICENSE("GPL");
  • Makefile
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
INCLUDES = -I. -I$(KDIR)/include
MODULE := xhr
obj-m := $(MODULE).o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

CFLAGS_module_entry.o = -I$(src)

.PHONY: all
all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules

.PHONY: clean
clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean

$(MODULE)-objs += module_entry.o
  • user space
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sudo -s
insmod xhr.ko
cd /sys/kernel/debug/tracing/
echo 1 > tracing_on
echo 1 > events/xhr_test/te_test/enable
cat trace

output:
# entries-in-buffer/entries-written: 8/8   #P:8
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
      xhr-thread-3732  [000] ....  1739.034184: te_test: count=1 output=48
      xhr-thread-3732  [000] ....  1740.058510: te_test: count=1 output=49
      xhr-thread-3732  [000] ....  1741.081934: te_test: count=1 output=50
      xhr-thread-3732  [000] ....  1742.106095: te_test: count=1 output=51
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-04-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
【调试】ftrace(三)trace-cmd和kernelshark
之前使用ftrace的时候需要一系列的配置,使用起来有点繁琐,这里推荐一个ftrace的一个前端工具,它就是trace-cmd
嵌入式与Linux那些事
2023/03/24
1.9K0
【调试】ftrace(三)trace-cmd和kernelshark
系统级性能分析工具perf的介绍与使用[转]
测试环境:Ubuntu16.04(在VMWare虚拟机使用perf top存在无法显示问题)
sunsky
2020/08/20
3.5K0
系统级性能分析工具perf的介绍与使用[转]
Linux Process States 与Systrace 中thread states
The normal way to put a process to sleep is to set the process's state to either TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE and call the scheduler's function schedule(). This results in the process getting moved off from the CPU run queue. If the process is sleeping in interruptible mode (by setting its state to TASK_INTERRUPTIBLE), it can be awakened either by an explicit wake-up call (wakeup_process()) or by signals needing processing.
用户9732312
2022/05/13
1.1K0
Linux Process States 与Systrace 中thread states
【调试】ftrace(一)基本使用方法
Ftrace是Linux Kernel的官方tracing系统,支持Function trace、静态tracepoint、动态Tracepoint的跟踪,还提供各种Tracer,用于统计最大irq延迟、最大函数调用栈大小、调度事件等。
嵌入式与Linux那些事
2023/03/24
2.9K0
【调试】ftrace(一)基本使用方法
从Ftrace开始内核探索之旅
操作系统内核对应用开发工程师来说就像一个黑盒,似乎很难窥探到其内部的运行机制。其实Linux内核很早就内置了一个强大的tracing工具:Ftrace,它几乎可以跟踪内核的所有函数,不仅可以用于调试和分析,还可以用于观察学习Linux内核的内部运行。虽然Ftrace在2008年就加入了内核,但很多应用开发工程师仍然不知道它的存在。本文就给你介绍一下Ftrace的基本使用。
mazhen
2023/11/24
6480
从Ftrace开始内核探索之旅
tracepoint类型的ebpf程序是如何被执行的
本文基于libbpf实现的ebpf例子介绍tracepoint类型ebpf程序调用流程,内核实现以5.4版本为例进行介绍。
cdh
2023/09/11
1.7K0
包罗万象-perf命令介绍
大家好,我是程栩,一个专注于性能的大厂程序员,分享包括但不限于计算机体系结构、性能优化、云原生的知识。
程栩的性能优化笔记
2023/11/01
1.1K0
包罗万象-perf命令介绍
Linux 进程管理
在内核层面,每个进程都是由task_struct 描述的,这个结构体非常大,可以粗略看下各主要内容:
一只小虾米
2023/03/09
10.2K0
Linux 进程管理
Linux 内核静态追踪技术的实现
前言:最近在探索 Node.js 调试和诊断方向的内容,因为 Node.js 提供的能力有时候可能无法解决问题,比如堆内存没有变化,但是 rss 一直上涨。所以需要深入一点去了解更多的排查问题方式。而这些方向往往都涉及到底层的东西,所以就自然需要去了解内核提供的一些技术,内核提供的能力,经过多年的发展,可谓是百花齐放,而且非常复杂。本文简单分享一下内核的静态追踪技术的实现。追踪,其实就是收集代码在执行时的一些信息,以便协助排查问题。
theanarkh
2021/11/19
1.9K0
万字长文解读 Linux 内核追踪机制
Linux 存在众多 tracing tools,比如 ftrace、perf,他们可用于内核的调试、提高内核的可观测性。众多的工具也意味着繁杂的概念,诸如 tracepoint、trace events、kprobe、eBPF 等,甚至让人搞不清楚他们到底是干什么的。本文尝试理清这些概念。
刘盼
2023/08/22
2.5K0
万字长文解读 Linux 内核追踪机制
Usleep /timerslack_ns /background thread in android
在android 手机上,如果call usleep(2*1000),结果sleep时间不定,甚至结果sleep了50+ms,是不是有点过分,测试代码如下:各位可以在手机上测试下,特别是把程序放在后台运行的情况下。
用户9732312
2022/05/13
2K0
Usleep /timerslack_ns /background thread in android
linux性能工具--ftrace框架
Ringbuffer是trace32框架的一个基础,所有的trace原始数据都是通过Ring Buffer记录的,其主要有以下几个作用:
233333
2022/09/28
1.2K0
linux性能工具--ftrace框架
原创Paper | 在 Android 中开发 eBPF 程序学习总结(一)
其实是想学习一下ecapture是怎么实现的,但是实际在我xiaomi 10手机上测试的过程中(已经有root权限)发现,并没办法运行,因为ecapture需要内核开启CONFIG_DEBUG_INFO_BTF,这个配置信息可以通过/proc/config.gz中来查看是否开启。
Seebug漏洞平台
2023/01/05
2.2K0
linux内核调度算法(1)–快速找到最高优先级进程
为什么要了解内核的调度策略呢?呵呵,因为它值得我们学习,不算是废话吧。内核调度程序很先进很强大,管理你的Linux上跑的大量的乱七八糟的进程,同时还保持着对用户操作的高灵敏响应,如果可能,为什么不把这种思想放到自己的应用程序里呢?或者,有没有可能更好的实现自己的应用,使得操作系统能够以自己的意志来分配资源给自己的进程?
陶辉
2019/06/21
2.6K0
ftrace(二)新增tracepoint
内核的各个子系统已经有大量的跟踪点,如果这些跟踪点无法满足工作中的需求,可以自己手动添加跟踪点。
嵌入式与Linux那些事
2023/03/24
1.1K0
ftrace(二)新增tracepoint
perf事件统计常用命令
Profiling # Sample on-CPU functions for the specified command, at 99 Hertz: perf record -F 99 command # Sample on-CPU functions for the specified PID, at 99 Hertz, until Ctrl-C: perf record -F 99 -p PID # Sample on-CPU functions for the specified PID, at
mingjie
2022/05/12
5730
BCC(可观测性)
BCC是一个用于跟踪内核和操作程序的工具集,其软件包中包含了一些有用的工具和例子,它扩展了BPF(Berkeley Packet Filters),通常被称为eBPF , 在Linux3.15中首次引入,但大多数BCC的功能需要Libux4.1及以上版本。
charlieroro
2020/07/09
3.3K0
BPF CO-RE 示例代码解析
在BPF的可移植性和CO-RE一文的末尾提到了一个名为runqslower的工具,该工具用于展示在CPU run队列中停留的时间大于某一值的任务。现在以该工具来展示如何使用BPF CO-RE。
charlieroro
2021/02/04
2.6K0
【Linux 内核】进程管理 task_struct 结构体 ① ( task_struct 结构体引入 | task_struct 代码示例 )
在 Linux 操作系统 中 , 进程 作为 调度的实体 , 需要将其抽象为 " 进程控制块 " , 英文全称 " Progress Control Block " , 简称 PCB ;
韩曙亮
2023/03/30
3.7K0
【Linux 内核】进程管理 task_struct 结构体 ① ( task_struct 结构体引入 | task_struct 代码示例 )
eBPF 入门开发实践教程九:捕获进程调度延迟,以直方图方式记录
eBPF (Extended Berkeley Packet Filter) 是 Linux 内核上的一个强大的网络和性能分析工具。它允许开发者在内核运行时动态加载、更新和运行用户定义的代码。
云微
2023/08/14
6080
相关推荐
【调试】ftrace(三)trace-cmd和kernelshark
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • 引入tracepoint的背景
  • 内核解决方案
  • 查看tracepoint
  • Tracepoint数据格式
  • 如何使用Tracepoint
    • perf中使用tracepoint
  • 添加Tracepoint
  • 如何使用 TRACE_EVENT() 宏来创建跟踪点
    • TRACE_EVENT() 宏的剖析
    • 使用tracepoint
    • 实际使用
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档