「调试」ftrace(二)新增跟踪点

内核的各个子系统已经有大量的跟踪点,如果这些跟踪点无法满足工作中的需求,可以自己手动添加跟踪点。

添加跟踪点有两种方式,一种是仿照`events/`目录下的跟踪点,使用`TRACE_EVENT()` 宏添加。另一种是参考内核目录`samples/trace_events`添加。本文对这两种方式分别进行介绍。

## 使用 TRACE_EVENT 定义 tracepoint

我们仿照`events/timer/timer_start`,添加一个`timer_stat`的跟踪点,获取`start_pid`和`slack`参数。

首先,需要在`include/trace/events/timer.h`头文件种添加名为`timer_stat`的跟踪点。

```c

/**

* timer_stat - ftrace interface timer_stat

* @timer: pointer to struct timer_list

*/

TRACE_EVENT(timer_stat,

TP_PROTO(struct timer_list *timer),

TP_ARGS(timer),

TP_STRUCT__entry(

__field( void *, timer )

__field( int, start_pid )

__field( int, slack)

),

TP_fast_assign(

__entry->timer = timer;

__entry->start_pid = timer->start_pid;

__entry->slack = timer->slack;

),

TP_printk("ftrace interface timer_stat:timer=%p pid=%d slack=%d ",

__entry->timer,__entry->start_pid,__entry->slack)

);

```

`TRACE_EVENT()`宏如下

```c

#define TRACE_EVENT(name, proto, args, struct, assign, print)

DEFINE_TRACE(name)

```

- name:表示跟踪点的名字,如上面的timer_stat。

- proto:表示跟踪点调用的入参的原型,比如`timer`类型为`struct timer_list *`。

- args:表示参数。

- struct:定义跟踪器内部使用的`__entry`数据结构。

- assign:把参数复制到`__entry`数据结构中。

- print:定义输出的格式。

接着在`kernel/kernel/time/timer.c` `debug_activate()`添加`trace_timer_stat()`。

```c

static inline void

debug_activate(struct timer_list *timer, unsigned long expires)

{

debug_timer_activate(timer);

trace_timer_start(timer, expires, timer->flags);

trace_timer_stat(timer);

}

```

重新编译内核后,烧写到设备中,即可看到`sys`节点已经有了新增的跟踪点。

![](http://linuxdriver.top/Blog/2023/202301261736287.png)

使能跟踪点后,查看trace点的输出。

![](http://linuxdriver.top/Blog/2023/202301261736280.png)

## 编译为独立的ko文件

内核还提供了一个跟踪点的例子,在`samples/trace_events` 目录下。

`trace_event_init()`创建内核线程一个名为`event-sample`内核线程。

```c

static int __init trace_event_init(void)

{

simple_tsk = kthread_run(simple_thread, NULL, "event-sample");

if (IS_ERR(simple_tsk))

return -1;

return 0;

}

```

`kthread_should_stop()`用于创建的线程检查结束标志,并决定是否退出。

```c

static int simple_thread(void *arg)

{

int cnt = 0;

while (!kthread_should_stop())

simple_thread_func(cnt++);

return 0;

}

```

`set_current_state()` 来设置进程的状态,设置为`TASK_INTERRUPTIBLE`表示是可以被信号和`wake_up()`唤醒的,当信号到来时,进程会被设置为可运行。

`schedule_timeout()`将当前task调度出cpu,重新调度间隔为`HZ`。接着`trace_`开头的函数就会依次打印跟踪点的信息。

```c

static void simple_thread_func(int cnt)

{

int array[6];

int len = cnt % 5;

int i;

set_current_state(TASK_INTERRUPTIBLE);

schedule_timeout(HZ);

for (i = 0; i < len; i++)

array[i] = i + 1;

array[i] = 0;

/* Silly tracepoints */

trace_foo_bar("hello", cnt, array, random_strings[len],

tsk_cpus_allowed(current));

trace_foo_with_template_simple("HELLO", cnt);

trace_foo_bar_with_cond("Some times print", cnt);

trace_foo_with_template_cond("prints other times", cnt);

trace_foo_with_template_print("I have to be different", cnt);

}

```

`trace_foo_with_template_simple`跟踪点的实现方式也是使用的`TRACE_EVENT ()`宏,这里不再赘述。

最后将文件编译为ko拷贝到设备上`insmod`后,即可看到`sys`目录下已经有新增的节点。

```bash

cd /home/zhongyi/code/rk3399_linux_release_v2.5.1_20210301/kernel/samples/trace_events

make -C /home/zhongyi/code/rk3399_linux_release_v2.5.1_20210301/kernel/ M=$(pwd) modules

```

```bash

root@firefly:/sys/kernel/debug/tracing# cat available_events | grep sample

sample-trace:foo_bar

sample-trace:foo_bar_with_cond

race:foo_bar_with_fn

sample-trace:foo_with_template_simple

sample-trace:foo_with_template_cond

sample-trace:foo_with_template_fn

sample-trace:foo_with_template_print

power:pstate_sample

```

```bash

root@firefly:/sys/kernel/debug/tracing# cd events/sample-trace/

root@firefly:/sys/kernel/debug/tracing/events/sample-trace# ls

enable foo_bar_with_cond foo_with_template_fn

filter foo_bar_with_fn foo_with_template_print

foo_bar foo_with_template_cond foo_with_templ_simple

root@firefly:/sys/kernel/debug/tracing/events/sample-trace# echo 1 > enable

root@firefly:/sys/kernel/debug/tracing/events/sample-trace# cat /sys/kernel/debug/tracing/trace

```

![](http://linuxdriver.top/Blog/2023/202301261507400.png)

## TRACE_EVENT_CONDITION()

在某些情况下,跟踪点只有在某个条件发生时才会被调用,类似于

```c

if (cond)

trace_foo();

```

`TRACE_EVENT_CONDITION()`宏就是这个作用,它和`TRACE_EVENT()`相比只是在参数中多加了一个cond条件。`TP_CONDITION()`会对条件做个判断。

```c

TRACE_EVENT(name, proto, args, struct, assign, printk)

TRACE_EVENT_CONDITION(name, proto, args, cond, struct, assign, printk)

```

详细使用方法可以参考`trace-events-sample.h`。

## TRACE_EVENT_FN()

`TRACE_EVENT_FN()`是在跟踪点使能前和使能后分别打印一些信息。相比于`TRACE_EVENT()`,`TRACE_EVENT_FN()`多了两个参数`reg`和`unreg`,

```c

TRACE_EVENT(name, proto, args, struct, assign, printk)

TRACE_EVENT_FN( name, proto, args, struct, assign, printk, reg, unreg)

```

`reg` 和`unreg`原型为

```c

void reg(void)

```

`reg`函数在跟踪点使能前打印,`unreg`函数在跟踪点使能后打印。`reg` 和`unreg`可以根据实际情况置其中一个为NULL,也可以全部置为NULL。

详细使用方法可以参考`trace-events-sample.h`。

## 本文参考

samples/trace_events

展开阅读全文

页面更新:2024-05-14

标签:数据结构   原型   线程   内核   使用方法   函数   进程   定义   条件   参数

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top