编辑
2025-01-22
工作知识
0
请注意,本文编写于 135 天前,最后修改于 135 天前,其中某些信息可能已经过时。

目录

一、tracepoint举例
1.1 查看tracepoint代码
1.2 查看trace event
二、写一个tracepoint
2.1 编写驱动
2.2 定义头文件
2.3 存放头文件
2.4 创建tracepoint
2.5 使用tracepoint
三、测试tracepoint

tracepoint是驱动编程必学的小技巧,它依附于ftrace系统,本文介绍一个最简单的tracepoint,方便大家在编写subsystem/driver时,为自己的程序安插tracepoint,后面同样会介绍linux中预置的trace event的使用方法。

一、tracepoint举例

我们之前聊过blk_update_request是块设备层的经典函数,在文件系统中,如果读取一个文件,它会先submit_bio来提交到block层,此后,blk_update_request用于更新block的request信息后续直接处理IO。

我们以blk_update_request为例,测试一下其本身的tracepoint,如下:

1.1 查看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

1.2 查看trace event

根据上面的信息我们找一下路径如下,我们直接进入:

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

为了在自己的驱动写tracepoint,我们需要注意如下几点:

  • 定义一个trace的头文件
  • 为c文件定义一个CREATE_TRACE_POINTS
  • 在代码安插tracepoint

不清楚的可以参考如下:

https://www.kernel.org/doc/html/latest/trace/tracepoints.html

2.1 编写驱动

我们可以编写一个最简单的驱动,使用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");

这里细节后面可以介绍,先简单看看即可。

2.2 定义头文件

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 system定义一个名字,例如:#define TRACE_SYSTEM kylin
  • 定义一个TRACE_EVENT
  • 头文件末尾定义添加#include <trace/define_trace.h>

关于TRACE_EVENT的编写,参考如下:

https://lwn.net/Articles/379903/ https://lwn.net/Articles/381064/ https://lwn.net/Articles/383362/

我们只需要关注TRACE_EVENT五要素:

  • name:给到ftrace的name字段
  • prototype:callback的函数声明类型
  • args:输入的参数
  • struct:构造的结构体
  • assign:用参数给构造的结构体赋值
  • print:ftrace输出格式

对于我们的代码,如下定义:

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的方式打印结构体的成员变量

2.3 存放头文件

为了使得头文件能够生效,我们定义了TRACE_SYSTEM 为kylin

#undef TRACE_SYSTEM #define TRACE_SYSTEM kylin

这里就要求了头文件的存放位置为include/trace/events,且名字是kylin

include/trace/events/kylin.h

2.4 创建tracepoint

为了在驱动中使用tracepoint,我们需要在include kylin.h之前添加CREATE_TRACE_POINTS的定义,如下:

#define CREATE_TRACE_POINTS #include "trace/events/kylin.h"

2.5 使用tracepoint

接下来我们就直接使用tracepoint即可,在函数直接安插:

static void test_tracepoint(void) { trace_test_kylin("test"); }

三、测试tracepoint

我们写了一个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