ftrace的trace_marker使用
一、背景
ftrace是linux内核提供的一个基础能力,用于抓系统上的一些内核的事件,抓的的最多的比如sched的事件、irq的事件。有时候,我们需要同时看内核的这些事件的同时也需要知道用户态在做什么样的行为,这时候就可以使用这篇博客里讲到的trace_marker的功能。
ftrace的trace_marker功能用于在系统的ftrace的生成物里也嵌入一些额外的信息,trace_marker主要还是在用户态状态下使用,通过echo一段内容或者直接写内容到trace_marker节点来达到把额外日志信息嵌入到ftrace的trace文件生成物里。
在下面第二章,我们给出参考的源码及脚本及生成物,在第三章里,我们说明注意事项。
二、使用trace_marker的参考源码及相关脚本
2.1 ftrace的启动脚本
#!/bin/bash#echo 1 > /sys/kernel/tracing/events/sched/sched_wakeup_new/enable
#echo 1 > /sys/kernel/tracing/events/sched/sched_process_exec/enable
#echo 1 > /sys/kernel/tracing/events/sched/sched_process_fork/enable#pkill testwakeaffecho 1280 > /sys/kernel/tracing/saved_cmdlines_sizemkdir /sys/kernel/tracing/instances/zhaoxin_instanceecho "mono" > /sys/kernel/tracing/instances/zhaoxin_instance/trace_clockecho 1 > /sys/kernel/tracing/instances/zhaoxin_instance/events/sched/enable
echo 1 > /sys/kernel/tracing/instances/zhaoxin_instance/events/irq/enable
#echo 1 > /sys/kernel/tracing/events/block/enable
#echo 1 > /sys/kernel/tracing/events/writeback/folio_wait_writeback/enable#echo 80960 > /sys/kernel/tracing/buffer_size_kb
echo 65536 > /sys/kernel/tracing/instances/zhaoxin_instance/buffer_size_kb
#echo 65536 > /sys/kernel/tracing/instances/zhaoxin_instance/per_cpu/cpu16/buffer_size_kb
#echo 65536 > /sys/kernel/tracing/instances/zhaoxin_instance/per_cpu/cpu17/buffer_size_kb
echo 1 > /sys/kernel/tracing/instances/zhaoxin_instance/options/record-tgid
#echo 1280 > /sys/kernel/tracing/instances/zhaoxin_instance/saved_cmdlines_size#echo 1 > /sys/kernel/tracing/instances/zhaoxin_instance/options/stacktrace#echo 1 > /sys/kernel/tracing/instances/zhaoxin_instance/options/userstacktrace
#echo 1 > /sys/kernel/tracing/instances/zhaoxin_instance/options/sym-userobj#echo 1 > /sys/kernel/tracing/options/func_stack_traceecho > /sys/kernel/tracing/instances/zhaoxin_instance/traceecho 1 > /sys/kernel/tracing/instances/zhaoxin_instance/tracing_on
2.2 使用trace_marker的demo例子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <iostream>
#include <string>
#include <mutex>
#include <atomic>
#include <fstream>
#include <sstream>int main()
{int fd_mark = -1;fd_mark = open("/sys/kernel/tracing/instances/zhaoxin_instance/trace_marker",O_CREAT||O_RDWR,0666);write(fd_mark, "main start\n", strlen("main start\n"));return 1;
}
也可以通过脚本直接写入这个trace_marker节点来写入ftrace的产出物里:
echo "hello" > /sys/kernel/tracing/instances/zhaoxin_instance/trace_marker
2.3 ftrace的结束脚本
#!/bin/bashecho 0 > /sys/kernel/tracing/instances/zhaoxin_instance/tracing_on
cat /sys/kernel/tracing/instances/zhaoxin_instance/trace > test.log
2.4 ftrace的生成物及如何过滤出写入trace_marker的内容
上面 2.3 里的test.log就是ftrace的生成物文件,它是各个per-cpu上的ftrace事件按照时间进行排序后的生成物
生成的产出物里,如何过滤出写入trace_marker节点的内容:
cat test.log | grep tracing_mark_write
三、注意事项
我们可以用上面 2.2 里的例子的代码放到一个用户态的程序里去插入到ftrace里,比如,你可以用它来调试程序启动时间,也可以用它来标记应用程序里的一些重要的状态变化等等。
3.1 要注意选择一个合适的时间停止trace并解析出trace生成物文件
要注意,我们要选择一个合适的时间来停止ftrace的抓取,ftrace的buffer是脚本里设置的大小,它是有上限的,一直跑不去停会让这个buffer里的内容不断刷新成新的时间的抓取到的内容,这点需要注意。
3.2 生成物trace文件如何转换和分析
对于普通linux平台(非rt-linux),如果用的是上面 2.1 里的ftrace启动脚本,抓到的ftrace是可以直接导入到perfetto里进行分析的。
perfetto的网址:
https://ui.perfetto.dev/
如下图里选择生成物trace文件即可:
3.2.1 如果ftrace启动脚本里打开了stacktrace,使用perfetto打开前需要进行过滤
如果抓取ftrace的文件里开启了stacktrace的话,如下图:
echo 1 > /sys/kernel/tracing/instances/zhaoxin_instance/options/stacktrace
这时候,ftrace的生成物里就会包含内核堆栈信息:
cat test.log | grep -E "\[.*\]" > temp.txt
通过上面的grep命令过滤出带cpu[*]信息的内容到temp.txt里,但是要注意在文件的头部还得再补上头部的内容:
上图是在rt-linux下抓出的文件的头,普通linux没有最后两个preempt-lazy-depth和migrate-disable。rt-linux下抓出的trace文件多出的这两个栏目,需要用 3.2.2 一节里的方法去sed替换成4个项的形式,但是头部这块内容保留rt-linux下6个项的形式也是可以的。这里多出的preempt-lazy-depth是与rt-linux里的preempt-lazy功能有关(见之前的博客 CFS及RT调度整体介绍 里的 1.4.4 一节),migrate-disable是因为rt-linux下的很多spinlock锁由于改成了可睡眠,为了尽量符合原预期的逻辑运行,会禁用迁移,所以这个计数在rt-linux下很重要。
其实,你可以在ftrace的启动脚本里再带上用户栈的信息(但是用户栈的信息只有elf文件及offset,没有解析出符号,需要自己去objdump -t或nm去解析,之前的博客 内核逻辑里抓取用户栈的几种方法 里有更多信息包括用户栈的抓取)
如下图进行设置:
echo 1 > /sys/kernel/tracing/instances/zhaoxin_instance/options/userstacktrace
echo 1 > /sys/kernel/tracing/instances/zhaoxin_instance/options/sym-userobj
但是要注意,有些平台可能没有适配好userstacktrace和sym-userobj,但是用 内核逻辑里抓取用户栈的几种方法 里的用户栈的抓取方法肯定是可以的 。
3.2.2 如果是rt-linux系统,则需要用sed命令把6个项替换成4个项
sed "s/\(\[0[01][0-9]\]\) ....... /\1 .... /" "trace.txt" > "trace_new.txt"
替换后的内容就可以放到perfetto里去显示了。
3.3 使用instance避免当前的ftrace抓取和系统上现有的ftrace抓取可能冲突
可以从上面 2.1 里的ftrace启动脚本看到,我们是使用了ftrace的instance功能。
3.4 大部分配置都可以配置到instance里去,但是小部分配置需要配置在公共里
如下图可以看到,大部分配置都可以配置到具体的一个instance里:
如下图可以看到saved_cmdlines_size的配置需要配置到公共的参数里:
3.5 为了和时间的打印同步,可以设置ftrace的时间类型
当前 2.1 的脚本里我们是用的设置成了mono类型的ftrace时间,这可以和内核代码里通过
ktime_get_ts64
获取的时间对齐
也可以和用户态的
clock_gettime(CLOCK_MONOTONIC, &ts)
获取到的时间对齐。
ftrace所支持的时间种类: