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| 启停控制 | - LOS_TraceStart:启动Trace<br/>- LOS_TraceStop:停止Trace | 33| 操作Trace记录的数据 | - LOS_TraceRecordDump:输出Trace缓冲区数据<br/>- LOS_TraceRecordGet:获取Trace缓冲区的首地址<br/>- 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.h。 91 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 IDE (dev 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