trace-event

trace-event

1 说明背景

内核源码中放置很多静态跟踪节点,这些节点可以被关联到对应的回调函数。当我想要调试某个子系统/模块时,通过debug系统将对应的节点开启(将回调函数挂钩子到静态跟踪点上,与之关联),这样内核在执行到跟踪点位置的时候会调用钩子函数,最终执行结果将被输出写到一个环形日志缓存区里,通过debug系统查看信息。

我把它当作一种内核层面的调试手段。当我想要调试某一个模块,开启对应的节点就好了,trace event基于现有的跟踪节点(一般是写代码的添加好的)效率高些,或者解决新的bug时将关键信息固化到调试系统里。

接下来学习 trace_event 的工作原理和使用方法,希望未来能积累足够多的使用案例。

2 如何使用

mount -t tracefs nodev /sys/kernel/tracing # 挂载tracefs
cat /sys/kernel/debug/tracing/available_events # 查看当前支持的跟踪事件列表
case01: 打开"gsgpu"驱动跟踪节点
echo 1 > /sys/kernel/debug/tracing/events/gsgpu/enable && \
echo 0 > /sys/kernel/debug/tracing/trace && \
/root/run_test.sh ; cp /sys/kernel/debug/tracing/trace /test.txt && \
echo 0 > /sys/kernel/debug/tracing/events/gsgpu/enable
cat /test.txt
case02: 通过filter过滤事件
echo 0 > /sys/kernel/debug/tracing/events/gsgpu/enable && \
echo 1 > /sys/kernel/debug/tracing/events/gsgpu/gsgpu_bo_move/enable && \
echo 0 > /sys/kernel/debug/tracing/trace && \
echo "bo_size >= 50000" > /sys/kernel/debug/tracing/events/gsgpu/gsgpu_bo_move/filter && \
/root/run_test.sh ; cp /sys/kernel/debug/tracing/trace /test.txt && \
echo 0 > /sys/kernel/debug/tracing/events/gsgpu/gsgpu_bo_move/filter
cat /test.txt
case03: 通过trigger查看栈回溯
echo 0 > /sys/kernel/debug/tracing/events/gsgpu/enable && \
echo 1 > /sys/kernel/debug/tracing/events/gsgpu/gsgpu_bo_move/enable && \
echo 0 > /sys/kernel/debug/tracing/trace && \
echo 'stacktrace' > /sys/kernel/debug/tracing/events/gsgpu/gsgpu_bo_move/trigger && \
/root/run_test.sh ; cp /sys/kernel/debug/tracing/trace /test.txt && \
echo '!stacktrace' > /sys/kernel/debug/tracing/events/gsgpu/gsgpu_bo_move/trigger && \
cat /test.txt

3 如何解析

1、调试信息
# tracer: nop
#
# entries-in-buffer/entries-written: 20103/1994095   #P:4  
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay 
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
          <idle>-0     [002] d.h.  6972.008532: gsgpu_iv: client_id:4 src_id:0 ring:0 vmid:0 timestamp: 10376293560622087932 pasid:2415919108 src_data: 00000000 76089440 90000004 00000000
2、事件输出格式定义
root@loongson-pc:/sys/kernel/debug/tracing/events/gsgpu# cat ./gsgpu_iv/format
name: gsgpu_iv
ID: 1903
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:unsigned client_id;	offset:8;	size:4;	signed:0;
	field:unsigned src_id;	offset:12;	size:4;	signed:0;
	field:unsigned ring_id;	offset:16;	size:4;	signed:0;
	field:unsigned vmid;	offset:20;	size:4;	signed:0;
	field:unsigned vmid_src;	offset:24;	size:4;	signed:0;
	field:uint64_t timestamp;	offset:32;	size:8;	signed:0;
	field:unsigned timestamp_src;	offset:40;	size:4;	signed:0;
	field:unsigned pasid;	offset:44;	size:4;	signed:0;
	field:unsigned src_data[4];	offset:48;	size:16;	signed:0;

print fmt: "client_id:%u src_id:%u ring:%u vmid:%u timestamp: %llu pasid:%u src_data: %08x %08x %08x %08x
", REC->client_id, REC->src_id, REC->ring_id, REC->vmid, REC->timestamp, REC->pasid, REC->src_data[0], REC->src_data[1], REC->src_data[2], REC->src_data[3]
3、源码实现(插桩方案)
/* src file: "drivers/gpu/drm/gsgpu/include/gsgpu_trace.h" */

TRACE_EVENT(gsgpu_iv, // 名称
	    TP_PROTO(struct gsgpu_iv_entry *iv), // 原型
	    TP_ARGS(iv), // 原型使用的参数
	    TP_STRUCT__entry(
			     __field(unsigned, client_id)
			     __field(unsigned, src_id)
			     __field(unsigned, ring_id)
			     __field(unsigned, vmid)
			     __field(unsigned, vmid_src)
			     __field(uint64_t, timestamp)
			     __field(unsigned, timestamp_src)
			     __field(unsigned, pasid)
			     __array(unsigned, src_data, 4)
			    ), // 描述将存储在跟踪程序环缓冲区中的数据的结构布局
	    TP_fast_assign(
			   __entry->client_id = iv->client_id;
			   __entry->src_id = iv->src_id;
			   __entry->ring_id = iv->ring_id;
			   __entry->vmid = iv->vmid;
			   __entry->vmid_src = iv->vmid_src;
			   __entry->timestamp = iv->timestamp;
			   __entry->timestamp_src = iv->timestamp_src;
			   __entry->pasid = iv->pasid;
			   __entry->src_data[0] = iv->src_data[0];
			   __entry->src_data[1] = iv->src_data[1];
			   __entry->src_data[2] = iv->src_data[2];
			   __entry->src_data[3] = iv->src_data[3];
			   ), // 定义将来自参数的数据保存到环形缓冲区的方式
	    TP_printk("client_id:%u src_id:%u ring:%u vmid:%u timestamp: %llu pasid:%u src_data: %08x %08x %08x %08x\n",
		      __entry->client_id, __entry->src_id,
		      __entry->ring_id, __entry->vmid,
		      __entry->timestamp, __entry->pasid,
		      __entry->src_data[0], __entry->src_data[1],
		      __entry->src_data[2], __entry->src_data[3]) // 定义了如何使用printk()打印TP_STRUCT__entry结构中的字段
);

/* src file: "drivers/gpu/drm/gsgpu/gpu/gsgpu_irq.c" */
void gsgpu_irq_dispatch(struct gsgpu_device *adev,
			 struct gsgpu_iv_entry *entry)
{
    // .....

    trace_gsgpu_iv(entry); // 插桩:桩函数

     // .....
}

4 如何新增

CASE001:添加trace文件和节点

0、参考内核中已经存在的用例samples/trace_events
1、首先需要在include/trace/events/文件夹下添加一个自己event的头文件

include/trace/events/sched.h

2、在探测点位置中调用桩函数,需要遵循注释的标准格式

kernel/sched/core.c
CASE002:向指定trace文件添加节点
wangjianfeng@dev:~/source/linux-4.19-loongson$ git diff
diff --git a/drivers/gpu/drm/gsgpu/gpu/gfx_v8_0.c b/drivers/gpu/drm/gsgpu/gpu/gfx_v8_0.c
index b6e14e4c5b40..7a02414475f0 100644
--- a/drivers/gpu/drm/gsgpu/gpu/gfx_v8_0.c
+++ b/drivers/gpu/drm/gsgpu/gpu/gfx_v8_0.c
@@ -26,6 +26,7 @@
 #include "gsgpu_gfx.h"
 #include "vi.h"
 #include "gsgpu_cp.h"
+#include "gsgpu_trace.h"
 
 #include "ivsrcid/ivsrcid_vislands30.h"
 
@@ -569,6 +570,7 @@ static void gfx_v8_0_ring_emit_rreg(struct gsgpu_ring *ring, uint32_t reg)
 static void gfx_v8_0_ring_emit_wreg(struct gsgpu_ring *ring, uint32_t reg,
                                  uint32_t val)
 {
+       trace_ring_emit_wreg(ring, reg, val);
        gsgpu_ring_write(ring, GSPKT(GSPKT_WRITE, 2) | WRITE_DST_SEL(0) | WRITE_WAIT);
        gsgpu_ring_write(ring, reg);
        gsgpu_ring_write(ring, val);
diff --git a/drivers/gpu/drm/gsgpu/include/gsgpu_trace.h b/drivers/gpu/drm/gsgpu/include/gsgpu_trace.h
index a9bb78d64a5e..ad8566a2260c 100644
--- a/drivers/gpu/drm/gsgpu/include/gsgpu_trace.h
+++ b/drivers/gpu/drm/gsgpu/include/gsgpu_trace.h
@@ -464,6 +464,25 @@ TRACE_EVENT(gsgpu_bo_move,
                        __entry->new_placement, __entry->bo_size)
 );
 
+TRACE_EVENT(ring_emit_wreg,
+           TP_PROTO(struct gsgpu_ring *ring, uint32_t reg, uint32_t val),
+           TP_ARGS(ring, reg, val),
+           TP_STRUCT__entry(
+                     __field(struct gsgpu_ring *, ring)
+                     __field(uint32_t , reg)
+                     __field(uint32_t , val)
+                 ),
+           TP_fast_assign(
+                     __entry->ring = ring;
+                     __entry->reg = reg;
+                     __entry->val = val;
+                 ),
+           TP_printk("ring=%p, reg=0x%08x, val=0x%08x",
+                     __entry->ring,
+                     __entry->reg,
+                     __entry->val)
+);
+
 #undef GSGPU_JOB_GET_TIMELINE_NAME
 #endif

5 记录问答

1、是什么? => trace event 就是利用 ftrace 框架,实现低性能损耗,对执行流无影响的一种信息输出机制
2、背景和发展历程 => 谁引入?何时引入?用于什么场景?
3、为什么要有这样的机制? => 提供一种内核调试的解决方案,解决了一些printk手段的缺陷
4、代码实现在哪?=> 开源参考samples/trace_events
5、如何使用?=> 参考内核文档
6、应用案例?=> 一次解决Linux内核内存泄漏实战全过程利用Ftrace追踪一次系统调用的时间分布
7、Ftrace和trace event的区别?=> Ftrace是通用的调试框架(提供函数钩子的基础设施;基于 tracefs 文件系统的 trace 框架),trace event是利用 ftrace 框架实现的一种信息输出机制
8、trace event跟踪点的位置如何执行到插桩函数?关键改动有哪些?=> 参考trace event原理与实现的内容
9、trace evnet过滤器filter和触发器trigger都有设置的情况下,哪一个优先级更高?=> 测试结果:触发器处理过滤器之后的信息

References

Doc | Event Tracing
阅码场 | Linux内核tracers的实现原理与应用
泰晓科技 | trace-event 基本用法
pwl999 | Linux ftrace 1.2、trace event
Doc | Using the Linux Kernel Tracepoints
LWN | Using the TRACE_EVENT() macro (Part 1)
SRC | samples/trace_events

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hinzer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值