• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Trace调测
2
3
4## 基本概念
5
6Trace调测旨在帮助开发者获取内核的运行流程,各个模块、任务的执行顺序,从而可以辅助开发者定位一些时序问题或者了解内核的代码运行过程。
7
8
9## 运行机制
10
11内核提供一套Hook框架,将Hook点预埋在各个模块的主要流程中, 在内核启动初期完成Trace功能的初始化,并注册Trace的处理函数到Hook中。
12
13当系统触发到一个Hook点时,Trace模块会对输入信息进行封装,添加Trace帧头信息,包含事件类型、运行的cpuid、运行的任务id、运行的相对时间戳等信息;
14
15Trace提供2种工作模式,离线模式和在线模式。
16
17离线模式会将trace frame记录到预先申请好的循环buffer中。如果循环buffer记录的frame过多则可能出现翻转,会覆盖之前的记录,故保持记录的信息始终是最新的信息。Trace循环buffer的数据可以通过shell命令导出进行详细分析,导出信息已按照时间戳信息完成排序。
18
19![zh-cn_image_0000001168711762](figures/zh-cn_image_0000001168711762.png)
20
21在线模式需要配合IDE使用,实时将trace frame记录发送给IDE,IDE端进行解析并可视化展示。
22
23
24## 接口说明
25
26OpenHarmony LiteOS-M内核的Trace模块提供下面几种功能,接口详细信息可以查看API参考。
27
28  **表1** Trace模块接口说明
29
30| 功能分类 | 接口名 |
31| -------- | -------- |
32| 启停控制 | -&nbsp;LOS_TraceStart:启动Trace<br/>-&nbsp;LOS_TraceStop:停止Trace |
33| 操作Trace记录的数据 | -&nbsp;LOS_TraceRecordDump:输出Trace缓冲区数据<br/>-&nbsp;LOS_TraceRecordGet:获取Trace缓冲区的首地址<br/>-&nbsp;LOS_TraceReset:清除Trace缓冲区中的事件 |
34| 过滤Trace记录的数据 | LOS_TraceEventMaskSet:设置事件掩码,仅记录某些模块的事件 |
35| 屏蔽某些中断号事件 | LOS_TraceHwiFilterHookReg:注册过滤特定中断号事件的钩子函数 |
36| 插桩函数 | LOS_TRACE_EASY:简易插桩<br/>LOS_TRACE:标准插桩 |
37
38- 当用户需要针对自定义事件进行追踪时,可按规则在目标源代码中进行插桩,系统提供如下2种插桩接口:
39  - LOS_TRACE_EASY(TYPE, IDENTITY, params...) 简易插桩。
40     - 一句话插桩,用户在目标源代码中插入该接口即可。
41     - TYPE有效取值范围为[0, 0xF],表示不同的事件类型,不同取值表示的含义由用户自定义。
42     - IDENTITY类型UINTPTR,表示事件操作的主体对象。
43     - Params类型UINTPTR,表示事件的参数。
44     - 对文件fd读写操作的简易插桩示例:
45
46        ```
47        /* 假设自定义读操作为type: 1, 写操作为type: 2 */
48        LOS_TRACE_EASY(1, fd, flag, size);  /* 在读fd文件的适当位置插入 */
49        LOS_TRACE_EASY(2, fd, flag, size);  /* 在写fd文件的适当位置插入 */
50        ```
51  - LOS_TRACE(TYPE, IDENTITY, params...) 标准插桩。
52     - 相比简易插桩,支持动态过滤事件和参数裁剪,但使用上需要用户按规则来扩展。
53     - TYPE用于设置具体的事件类型,可以在头文件los_trace.h中的enum LOS_TRACE_TYPE中自定义事件类型。定义方法和规则可以参考其他事件类型。
54     - IDENTITY和Params的类型及含义同简易插桩。
55     - 示例:
56          1. 定义FS模块的类型,即FS模块的事件掩码
57
58        ```
59        /* 在enum LOS_TRACE_MASK中定义, 定义规范为TRACE_#MOD#_FLAG, #MOD#表示模块名 */
60        TRACE_FS_FLAG = 0x4000
61        ```
62
63        2. 定义FS模块的具体事件类型
64
65
66        ```
67        /* 定义规范为#TYPE# = TRACE_#MOD#_FLAG | NUMBER,  */
68        FS_READ  = TRACE_FS_FLAG | 0; /* 读文件 */
69        FS_WRITE = TRACE_FS_FLAG | 1; /* 写文件 */
70        ```
71
72        3. 定义事件参数
73
74
75        ```
76        /* 定义规范为#TYPE#_PARAMS(IDENTITY, parma1...) IDENTITY, ... */
77        #define FS_READ_PARAMS(fp, fd, flag, size)  fp, fd, flag, size /* 宏定义的参数对应于Trace缓冲区中记录的事件参数,用户可对任意参数字段进行裁剪 */
78        #define FS_READ_PARAMS(fp, fd, flag, size)                     /* 当定义为空时,表示不追踪该类型事件 */
79        ```
80
81        4. 在目标代码中插桩
82
83
84        ```
85        /* 定义规范为LOS_TRACE(#TYPE#, #TYPE#_PARAMS(IDENTITY, parma1...)) */
86        LOS_TRACE(FS_READ, fp, fd, flag, size); /* 读文件操作的代码桩 */
87        ```
88
89        > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
90        > 预置的Trace事件及参数均可以通过上述方式进行裁剪,参数详见kernel\include\los_trace.h91
92- Trace Mask事件过滤接口LOS_TraceEventMaskSet(UINT32 mask),其入参mask仅高28位生效(对应LOS_TRACE_MASK中某模块的使能位),仅用于模块的过滤,暂不支持针对某个特定事件的细粒度过滤。例如:LOS_TraceEventMaskSet(0x202),则实际设置生效的mask为0x200(TRACE_QUE_FLAG),QUE模块的所有事件均会被采集。一般建议使用的方法为: LOS_TraceEventMaskSet(TRACE_EVENT_FLAG | TRACE_MUX_FLAG | TRACE_SEM_FLAG | TRACE_QUE_FLAG);
93
94- 如果仅需要简易插桩事件,通过设置Trace Mask为TRACE_MAX_FLAG即可。
95
96- Trace缓冲区有限,事件写满之后会覆盖写,用户可通过LOS_TraceRecordDump中打印的CurEvtIndex识别最新记录。
97
98- Trace的典型操作流程为:LOS_TraceStart、 LOS_TraceStop、 LOS_TraceRecordDump.
99
100- 针对中断事件的Trace, 提供中断号过滤,用于解决某些场景下特定中断号频繁触发导致其他事件被覆盖的情况,用户可自定义中断过滤的规则,
101    示例如下:
102
103  ```
104  BOOL Example_HwiNumFilter(UINT32 hwiNum)
105  {
106         if ((hwiNum == TIMER_INT) || (hwiNum == DMA_INT)) {
107          return TRUE;
108      }
109      return FALSE;
110  }
111  LOS_TraceHwiFilterHookReg(Example_HwiNumFilter);
112  ```
113
114  则当中断号为TIMER_INT 或者DMA_INT时,不记录中断事件。
115
116
117## 开发指导
118
119
120### 开发流程
121
122开启Trace调测的典型流程如下:
123
1241. 配置Trace模块相关宏。
125     需要在target_config.h头文件中修改配置:
126     | 配置项 | 含义 | 设置值 |
127   | -------- | -------- | -------- |
128   | LOSCFG_KERNEL_TRACE | Trace模块的裁剪开关 | YES/NO |
129   | LOSCFG_RECORDER_MODE_OFFLINE | Trace工作模式为离线模式 | YES/NO |
130   | LOSCFG_RECORDER_MODE_ONLINE | Trace工作模式为在线模式 | YES/NO |
131   | LOSCFG_TRACE_CLIENT_INTERACT | 使能与Trace&nbsp;IDE&nbsp;(dev&nbsp;tools)的交互,包括数据可视化和流程控制 | YES/NO |
132   | LOSCFG_TRACE_FRAME_CORE_MSG | 记录CPUID、中断状态、锁任务状态 | YES/NO |
133   | LOSCFG_TRACE_FRAME_EVENT_COUNT | 记录事件的次序编号 | YES/NO |
134   | LOSCFG_TRACE_FRAME_MAX_PARAMS | 配置记录事件的最大参数个数 | INT |
135   | LOSCFG_TRACE_BUFFER_SIZE | 配置Trace的缓冲区大小 | INT |
136
1372. (可选)预置事件参数和事件桩(或使用系统默认的事件参数配置和事件桩)。
138
1393. (可选)调用LOS_TraceStop停止Trace后,清除缓冲区LOS_TraceReset(系统默认已启动trace)。
140
1414. (可选)调用LOS_TraceEventMaskSet设置需要追踪的事件掩码(系统默认的事件掩码仅使能中断与任务事件),事件掩码参见los_trace.h 中的LOS_TRACE_MASK定义。
142
1435. 在需要记录事件的代码起始点调用LOS_TraceStart。
144
1456. 在需要记录事件的代码结束点调用LOS_TraceStop。
146
1477. 调用LOS_TraceRecordDump输出缓冲区数据(函数的入参为布尔型,FALSE表示格式化输出,TRUE表示输出到windows客户端)。
148
149上述第3-7步中的接口,均封装有对应的shell命令,对应关系如下
150
151- LOS_TraceReset —— trace_reset
152
153- LOS_TraceEventMaskSet —— trace_mask
154
155- LOS_TraceStart —— trace_start
156
157- LOS_TraceStop —— trace_stop
158
159- LOS_TraceRecordDump —— trace_dump
160
161
162### 编程实例
163
164本实例实现如下功能:
165
1661. 创建一个用于Trace测试的任务。
167
1682. 设置事件掩码。
169
1703. 启动trace。
171
1724. 停止trace。
173
1745. 格式化输出trace数据。
175
176
177### 示例代码
178
179示例代码如下:
180
181本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleTraceTest。
182
183
184```
185#include "los_trace.h"
186UINT32 g_traceTestTaskId;
187VOID Example_Trace(VOID)
188{
189    UINT32 ret;
190    LOS_TaskDelay(10);
191    /* 开启trace */
192    ret = LOS_TraceStart();
193    if (ret != LOS_OK) {
194        dprintf("trace start error\n");
195        return;
196    }
197    /* 触发任务切换的事件 */
198    LOS_TaskDelay(1);
199    LOS_TaskDelay(1);
200    LOS_TaskDelay(1);
201    /* 停止trace */
202    LOS_TraceStop();
203    LOS_TraceRecordDump(FALSE);
204}
205UINT32 ExampleTraceTest(VOID){
206    UINT32 ret;
207    TSK_INIT_PARAM_S traceTestTask = { 0 };
208    /* 创建用于trace测试的任务 */
209    memset(&traceTestTask, 0, sizeof(TSK_INIT_PARAM_S));
210    traceTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Trace;
211    traceTestTask.pcName       = "TestTraceTsk";    /* 测试任务名称 */
212    traceTestTask.uwStackSize  = 0x800;
213    traceTestTask.usTaskPrio   = 5;
214    traceTestTask.uwResved   = LOS_TASK_STATUS_DETACHED;
215    ret = LOS_TaskCreate(&g_traceTestTaskId, &traceTestTask);
216    if(ret != LOS_OK){
217        dprintf("TraceTestTask create failed .\n");
218        return LOS_NOK;
219    }
220    /* 系统默认情况下已启动trace, 因此可先关闭trace后清除缓存区后,再重启trace */
221    LOS_TraceStop();
222    LOS_TraceReset();
223    /* 开启任务模块事件记录 */
224    LOS_TraceEventMaskSet(TRACE_TASK_FLAG);
225    return LOS_OK;
226}
227```
228
229
230### 结果验证
231
232输出结果如下:
233
234
235```
236***TraceInfo begin***
237clockFreq = 50000000
238CurEvtIndex = 7
239Index   Time(cycles)      EventType      CurTask   Identity      params
2400       0x366d5e88        0x45           0x1       0x0           0x1f         0x4       0x0
2411       0x366d74ae        0x45           0x0       0x1           0x0          0x8       0x1f
2422       0x36940da6        0x45           0x1       0xc           0x1f         0x4       0x9
2433       0x3694337c        0x45           0xc       0x1           0x9          0x8       0x1f
2444       0x36eea56e        0x45           0x1       0xc           0x1f         0x4       0x9
2455       0x36eec810        0x45           0xc       0x1           0x9          0x8       0x1f
2466       0x3706f804        0x45           0x1       0x0           0x1f         0x4       0x0
2477       0x37070e59        0x45           0x0       0x1           0x0          0x8       0x1f
248***TraceInfo end***
249
250根据实际运行环境,上文中的数据会有差异,非固定结果
251```
252
253输出的事件信息包括:发生时间、事件类型、事件发生在哪个任务中、事件操作的主体对象、事件的其他参数。
254
255- EventType:表示的具体事件可查阅头文件los_trace.h中的enum LOS_TRACE_TYPE。
256
257- CurrentTask:表示当前运行在哪个任务中,值为taskid。
258
259- Identity:表示事件操作的主体对象,可查阅头文件los_trace.h中的\#TYPE\#_PARAMS。
260
261- params:表示的事件参数可查阅头文件los_trace.h中的\#TYPE\#_PARAMS。
262
263下面以序号为0的输出项为例,进行说明。
264
265
266```
267Index   Time(cycles)      EventType      CurTask   Identity      params
2680       0x366d5e88        0x45           0x1       0x0           0x1f         0x4
269```
270
271- Time cycles可换算成时间,换算公式为cycles/clockFreq,单位为s。
272
273- 0x45为TASK_SWITCH即任务切换事件,当前运行的任务taskid为0x1。
274
275- Identity和params的含义需要查看TASK_SWITCH_PARAMS宏定义:
276
277
278```
279#define TASK_SWITCH_PARAMS(taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus) \
280taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus
281```
282
283  因为\#TYPE\#_PARAMS(IDENTITY, parma1...) IDENTITY, ...,所以Identity为taskId(0x0),第一个参数为oldPriority(0x1f)
284> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
285> params的个数由LOSCFG_TRACE_FRAME_MAX_PARAMS配置,默认为3,超出的参数不会被记录,用户应自行合理配置该值。
286
287综上所述,任务由0x1切换到0x0,0x1任务的优先级为0x1f,状态为0x4,0x0任务的优先级为0x0。
288