tracepoint是驱动编程必学的小技巧,它依附于ftrace系统,本文介绍一个最简单的tracepoint,方便大家在编写subsystem/driver时,为自己的程序安插tracepoint,后面同样会介绍linux中预置的trace event的使用方法。
我们之前聊过blk_update_request
是块设备层的经典函数,在文件系统中,如果读取一个文件,它会先submit_bio
来提交到block层,此后,blk_update_request
用于更新block的request信息后续直接处理IO。
我们以blk_update_request为例,测试一下其本身的tracepoint,如下:
vim block/blk-core.c
先查看头文件
#define CREATE_TRACE_POINTS #include <trace/events/block.h>
此时找到这个h文件,查看如下:
#undef TRACE_SYSTEM #define TRACE_SYSTEM block
然后查看具体函数代码:
bool blk_update_request(struct request *req, blk_status_t error, unsigned int nr_bytes) { int total_bytes; trace_block_rq_complete(req, blk_status_to_errno(error), nr_bytes); if (!req->bio) return false;
我们留意trace_block_rq_complete(req, blk_status_to_errno(error), nr_bytes);
此时我们知道tracepoint是block下的block_rq_complete
根据上面的信息我们找一下路径如下,我们直接进入:
cd /sys/kernel/tracing/events/block/block_rq_complete/
此时我们正常enable即可,
echo 1 > enable
此时我们监听ftrace的日志即可
cat /sys/kernel/debug/tracing/trace_pipe
我们可以看到日志如下:
# cat /sys/kernel/debug/tracing/trace_pipe <idle>-0 [000] ..s. 104.385029: block_rq_complete: 179,0 WS () 13430392 + 96 [0] kworker/3:2H-610 [003] .... 104.385386: block_rq_complete: 179,0 FF () 18446744073709551615 + 0 [0] sshd-3368 [000] ..s. 104.385710: block_rq_complete: 179,0 WFS () 13430488 + 8 [0] sshd-3368 [000] d.s. 104.385733: block_rq_complete: 179,0 WFS () 13430488 + 0 [0] <idle>-0 [000] ..s. 105.877799: block_rq_complete: 179,0 W () 17460720 + 8 [0]
至此tracepoint的演示完成了。其他文章会更详细的演示linux已有的tracepoint
为了在自己的驱动写tracepoint,我们需要注意如下几点:
不清楚的可以参考如下:
https://www.kernel.org/doc/html/latest/trace/tracepoints.html
我们可以编写一个最简单的驱动,使用kthread拉起函数,如下:
#include <linux/init.h> #include <linux/module.h> #include <linux/kthread.h> #include <linux/delay.h> #define CREATE_TRACE_POINTS #include "trace/events/kylin.h" static struct task_struct *thread1; static void test_tracepoint(void) { pr_info("%s starting...\n", __func__); trace_test_kylin("test"); } static int thread_1(void *arg) { while(!kthread_should_stop()){ schedule_timeout_interruptible(msecs_to_jiffies(1000)); test_tracepoint(); } return 0; } static void start_test(void) { thread1 = kthread_run(thread_1, "Thread", "tracepoint-test"); return ; } static int __init test_init(void) { start_test(); return 0; } static void __exit test_exit(void) { kthread_stop(thread1); return; } module_init(test_init); module_exit(test_exit); MODULE_AUTHOR("tangfeng <tangfeng@kylinos.cn>"); MODULE_DESCRIPTION("Test tracepoint"); MODULE_LICENSE("GPL");
这里细节后面可以介绍,先简单看看即可。
vim include/trace/events/kylin.h
#undef TRACE_SYSTEM #define TRACE_SYSTEM kylin #if !defined(_TRACE_KYLIN_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_KYLIN_H #include <linux/tracepoint.h> TRACE_EVENT(test_kylin, TP_PROTO(const char *name), TP_ARGS(name), TP_STRUCT__entry( __field(const char *, name) ), TP_fast_assign( __entry->name = name ), TP_printk("kylin: %s", __entry->name) ); #endif #include <trace/define_trace.h>
我们需要注意以下几点:
关于TRACE_EVENT的编写,参考如下:
https://lwn.net/Articles/379903/ https://lwn.net/Articles/381064/ https://lwn.net/Articles/383362/
我们只需要关注TRACE_EVENT五要素:
对于我们的代码,如下定义:
TRACE_EVENT(test_kylin, TP_PROTO(const char *name), TP_ARGS(name), TP_STRUCT__entry( __field(const char *, name) ), TP_fast_assign( __entry->name = name ), TP_printk("kylin: %s", __entry->name) );
对于prototype:我们使用TP_PROTO的宏定义,传入需要跟踪的函数声明即可,我们打算跟踪的是带字符串的trace如下:
trace_test_kylin("test");
所以我们只需要一个const char* 即可
对于args:我们使用TP_ARGS的宏定义,这里传入参数name
对于struct:我们需要新增一个const char*的成员,故通过TP_STRUCT__entry 定义一个__field即可。
对于assign:我们使用TP_fast_assign来将参数赋值给结构体成员变量
对于print:我们使用TP_printk直接按照printk的方式打印结构体的成员变量
为了使得头文件能够生效,我们定义了TRACE_SYSTEM 为kylin
#undef TRACE_SYSTEM #define TRACE_SYSTEM kylin
这里就要求了头文件的存放位置为include/trace/events
,且名字是kylin
include/trace/events/kylin.h
为了在驱动中使用tracepoint,我们需要在include kylin.h之前添加CREATE_TRACE_POINTS的定义,如下:
#define CREATE_TRACE_POINTS #include "trace/events/kylin.h"
接下来我们就直接使用tracepoint即可,在函数直接安插:
static void test_tracepoint(void) { trace_test_kylin("test"); }
我们写了一个tracepoint,接下来是测试这个tracepoint,当ko加载时,我们可以发现ftrace的events下存在如下文件
# find /sys/kernel/debug/tracing/events/kylin/ /sys/kernel/debug/tracing/events/kylin/ /sys/kernel/debug/tracing/events/kylin/test_kylin /sys/kernel/debug/tracing/events/kylin/test_kylin/format /sys/kernel/debug/tracing/events/kylin/test_kylin/trigger /sys/kernel/debug/tracing/events/kylin/test_kylin/filter /sys/kernel/debug/tracing/events/kylin/test_kylin/id /sys/kernel/debug/tracing/events/kylin/test_kylin/enable /sys/kernel/debug/tracing/events/kylin/enable /sys/kernel/debug/tracing/events/kylin/filter
然后我们准备获取trace的内容,如下:
cat /sys/kernel/debug/tracing/trace_pipe
我们使得我们自己的tracepoint如下:
echo 1 > /sys/kernel/debug/tracing/events/kylin/enable
这样我们trace_pipe可以看到如下日志
# cat /sys/kernel/debug/tracing/trace_pipe | grep test_ thread-141765 [002] .... 9598.458509: test_kylin: kylin: test thread-141765 [002] .... 9599.471840: test_kylin: kylin: test
至此,一个tracepoint的演示完全完成。想必能够有效的帮助大家去编写driver