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_0000001214329013](figures/zh-cn_image_0000001214329013.png) 20 21在线模式需要配合IDE使用,实时将trace frame记录发送给IDE,IDE端进行解析并可视化展示。 22 23 24## 接口说明 25 26 27### 内核态 28 29LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以查看[API](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_trace.h)参考。 30 31 **表1** Trace模块接口说明 32 33| 功能分类 | 接口描述 | 34| -------- | -------- | 35| 启停控制 | LOS_TraceStart:启动Trace<br/>LOS_TraceStop:停止Trace | 36| 操作Trace记录的数据 | LOS_TraceRecordDump:输出Trace缓冲区数据<br/>LOS_TraceRecordGet:获取Trace缓冲区的首地址<br/>LOS_TraceReset:清除Trace缓冲区中的事件 | 37| 过滤Trace记录的数据 | LOS_TraceEventMaskSet:设置事件掩码,仅记录某些模块的事件 | 38| 屏蔽某些中断号事件 | LOS_TraceHwiFilterHookReg:注册过滤特定中断号事件的钩子函数 | 39| 插桩函数 | LOS_TRACE_EASY:简易插桩<br/>LOS_TRACE:标准插桩 | 40 41- 当用户需要针对自定义事件进行追踪时,可按规则在目标源代码中进行插桩,系统提供如下2种插桩接口: 42 - LOS_TRACE_EASY(TYPE, IDENTITY, params...) 简易插桩。 43 - 用户在目标源代码中插入该接口即可。 44 - TYPE有效取值范围为[0, 0xF],表示不同的事件类型,不同取值表示的含义由用户自定义。 45 - IDENTITY类型UINTPTR,表示事件操作的主体对象。 46 - Params类型UINTPTR,表示事件的参数。 47 示例: 48 49 ``` 50 假设需要新增对文件(fd1、fd2)读写操作的简易插桩, 51 自定义读操作为type:1, 写操作为type:2,则 52 在读fd1文件的适当位置插入: 53 LOS_TRACE_EASY(1, fd1, flag, size); 54 在读fd2文件的适当位置插入: 55 LOS_TRACE_EASY(1, fd2, flag, size); 56 在写fd1文件的适当位置插入: 57 LOS_TRACE_EASY(2, fd1, flag, size); 58 在写fd2文件的适当位置插入: 59 LOS_TRACE_EASY(2, fd2,flag, size); 60 ``` 61 - LOS_TRACE(TYPE, IDENTITY, params...) 标准插桩。 62 - 相比简易插桩,支持动态过滤事件和参数裁剪,但使用上需要用户按规则来扩展。 63 - TYPE用于设置具体的事件类型,可以在头文件los_trace.h中的enum LOS_TRACE_TYPE中自定义事件类型。定义方法和规则可以参考其他事件类型。 64 - IDENTITY和Params的类型及含义同简易插桩。 65 示例: 66 67 ``` 68 1.在enum LOS_TRACE_MASK中定义事件掩码,即模块级别的事件类型。 69 定义规范为TRACE_#MOD#_FLAG,#MOD#表示模块名。 70 例如: 71 TRACE_FS_FLAG = 0x4000 72 2.在enum LOS_TRACE_TYPE中定义具体事件类型。 73 定义规范为#TYPE# = TRACE_#MOD#_FLAG | NUMBER, 74 例如: 75 FS_READ = TRACE_FS_FLAG | 0; // 读文件 76 FS_WRITE = TRACE_FS_FLAG | 1; // 写文件 77 3.定义事件参数。定义规范为#TYPE#_PARAMS(IDENTITY, parma1...) IDENTITY, ... 78 其中的#TYPE#就是上面2中的#TYPE#, 79 例如: 80 #define FS_READ_PARAMS(fp, fd, flag, size) fp, fd, flag, size 81 宏定义的参数对应于Trace缓冲区中记录的事件参数,用户可对任意参数字段进行裁剪: 82 当定义为空时,表示不追踪该类型事件: 83 #define FS_READ_PARAMS(fp, fd, flag, size) // 不追踪文件读事件 84 4.在适当位置插入代码桩。 85 定义规范为LOS_TRACE(#TYPE#, #TYPE#_PARAMS(IDENTITY, parma1...)) 86 LOS_TRACE(FS_READ, fp, fd, flag, size); // 读文件的代码桩, 87 #TYPE#之后的入参就是上面3中的FS_READ_PARAMS函数的入参 88 ``` 89 90 > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 91 > 预置的Trace事件及参数均可以通过上述方式进行裁剪,参数详见 [kernel\include\los_trace.h](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_trace.h)。 92 93- 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); 94 95- 如果仅需要使能简易插桩事件,通过设置Trace Mask为TRACE_MAX_FLAG即可。 96 97- Trace缓冲区有限,事件写满之后会覆盖写,用户可通过LOS_TraceRecordDump中打印的CurEvtIndex识别最新记录。 98 99- Trace的典型操作流程为:LOS_TraceStart、 LOS_TraceStop、 LOS_TraceRecordDump. 100 101- 针对中断事件的Trace, 提供中断号过滤,用于解决某些场景下特定中断号频繁触发导致其他事件被覆盖的情况,用户可自定义中断过滤的规则, 102 示例如下: 103 104 ```c 105 BOOL Example_HwiNumFilter(UINT32 hwiNum) 106 { 107 if ((hwiNum == TIMER_INT) || (hwiNum == DMA_INT)) { 108 return TRUE; 109 } 110 return FALSE; 111 } 112 LOS_TraceHwiFilterHookReg(Example_HwiNumFilter); 113 ``` 114 115则当中断号为TIMER_INT 或者DMA_INT时,不记录中断事件。 116 117 118### 用户态 119 120新增trace字符设备,位于"/dev/trace",通过对设备节点的read、write、ioctl,实现用户态trace的读写和控制: 121 122- read: 用户态读取Trace记录数据 123 124- write: 用户态事件写入 125 126- ioctl: 用户态Trace控制操作,包括 127 128 129```c 130#define TRACE_IOC_MAGIC 'T' 131#define TRACE_START _IO(TRACE_IOC_MAGIC, 1) 132#define TRACE_STOP _IO(TRACE_IOC_MAGIC, 2) 133#define TRACE_RESET _IO(TRACE_IOC_MAGIC, 3) 134#define TRACE_DUMP _IO(TRACE_IOC_MAGIC, 4) 135#define TRACE_SET_MASK _IO(TRACE_IOC_MAGIC, 5) 136``` 137 138分别对应Trace启动(LOS_TraceStart)、停止(LOS_TraceStop)、清除记录(LOS_TraceReset)、dump记录(LOS_TraceRecordDump)、设置事件过滤掩码(LOS_TraceEventMaskSet) 139 140具体的使用方法参见[用户态编程实例](kernel-small-debug-trace.md#用户态)。 141 142 143## 开发指导 144 145 146### 内核态开发流程 147 148开启Trace调测的典型流程如下: 149 1501. 配置Trace模块相关宏。 151 配置Trace控制宏LOSCFG_KERNEL_TRACE,默认关,在 kernel/liteos_a 目录下执行 make update_config 命令配置 "Kernel->Enable Hook Feature->Enable Trace Feature" 中打开: 152 153 | 配置项 | menuconfig选项 | 含义 | 设置值 | 154 | -------- | -------- | -------- | -------- | 155 | LOSCFG_KERNEL_TRACE | Enable Trace Feature | Trace模块的裁剪开关 | YES/NO | 156 | LOSCFG_RECORDER_MODE_OFFLINE | Trace work mode ->Offline mode | Trace工作模式为离线模式 | YES/NO | 157 | LOSCFG_RECORDER_MODE_ONLINE | Trace work mode ->Online mode | Trace工作模式为在线模式 | YES/NO | 158 | LOSCFG_TRACE_CLIENT_INTERACT | Enable Trace Client Visualization and Control | 使能与Trace IDE (dev tools)的交互,包括数据可视化和流程控制 | YES/NO | 159 | LOSCFG_TRACE_FRAME_CORE_MSG | Enable Record more extended content<br>->Record cpuid, hardware interrupt status, task lock status | 记录CPUID、中断状态、锁任务状态 | YES/NO | 160 | LOSCFG_TRACE_FRAME_EVENT_COUNT | Enable Record more extended content<br> ->Record event count,<br> which indicate the sequence of happend events | 记录事件的次序编号 | YES/NO | 161 | LOSCFG_TRACE_FRAME_MAX_PARAMS | Record max params | 配置记录事件的最大参数个数 | INT | 162 | LOSCFG_TRACE_BUFFER_SIZE | Trace record buffer size | 配置Trace的缓冲区大小 | INT | 163 1642. (可选)预置事件参数和事件桩(或使用系统默认的事件参数配置和事件桩)。 165 1663. (可选)调用LOS_TraceStop停止Trace后,清除缓冲区LOS_TraceReset(系统默认已启动trace)。 167 1684. (可选)调用LOS_TraceEventMaskSet设置需要追踪的事件掩码(系统默认的事件掩码仅使能中断与任务事件),事件掩码参见 [los_trace.h](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_trace.h) 中的LOS_TRACE_MASK定义。 169 1705. 在需要记录事件的代码起始点调用LOS_TraceStart。 171 1726. 在需要记录事件的代码结束点调用LOS_TraceStop。 173 1747. 调用LOS_TraceRecordDump输出缓冲区数据(函数的入参为布尔型,FALSE表示格式化输出,TRUE表示输出到Trace IDE)。 175 176上述第3-7步中的接口,均封装有对应的shell命令,开启shell后可执行相应的命令,对应关系如下: 177 178- LOS_TraceReset —— trace_reset 179 180- LOS_TraceEventMaskSet —— trace_mask 181 182- LOS_TraceStart —— trace_start 183 184- LOS_TraceStop —— trace_stop 185 186- LOS_TraceRecordDump —— trace_dump 187 188 189### 内核态编程实例 190 191本实例实现如下功能: 192 1931. 创建一个用于Trace测试的任务。 194 1952. 设置事件掩码。 196 1973. 启动trace。 198 1994. 停止trace。 200 2015. 格式化输出trace数据。 202 203 204### 内核态示例代码 205 206该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。 207实例代码如下: 208 209 210```c 211#include "los_trace.h" 212UINT32 g_traceTestTaskId; 213VOID Example_Trace(VOID) 214{ 215 UINT32 ret; 216 LOS_TaskDelay(10); 217 /* 开启trace */ 218 ret = LOS_TraceStart(); 219 if (ret != LOS_OK) { 220 dprintf("trace start error\n"); 221 return; 222 } 223 /* 触发任务切换的事件 */ 224 LOS_TaskDelay(1); 225 LOS_TaskDelay(1); 226 LOS_TaskDelay(1); 227 /* 停止trace */ 228 LOS_TraceStop(); 229 LOS_TraceRecordDump(FALSE); 230} 231UINT32 Example_Trace_test(VOID) 232{ 233 UINT32 ret; 234 TSK_INIT_PARAM_S traceTestTask; 235 /* 创建用于trace测试的任务 */ 236 memset(&traceTestTask, 0, sizeof(TSK_INIT_PARAM_S)); 237 traceTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Trace; 238 traceTestTask.pcName = "TestTraceTsk"; /* 测试任务名称 */ 239 traceTestTask.uwStackSize = 0x800; // 0x800: trace test task stacksize 240 traceTestTask.usTaskPrio = 5; // 5: trace test task priority 241 traceTestTask.uwResved = LOS_TASK_STATUS_DETACHED; 242 ret = LOS_TaskCreate(&g_traceTestTaskId, &traceTestTask); 243 if (ret != LOS_OK) { 244 dprintf("TraceTestTask create failed .\n"); 245 return LOS_NOK; 246 } 247 /* 系统默认情况下已启动trace, 因此可先关闭trace后清除缓存区后,再重启trace */ 248 LOS_TraceStop(); 249 LOS_TraceReset(); 250 /* 开启任务模块事件记录 */ 251 LOS_TraceEventMaskSet(TRACE_TASK_FLAG); 252 return LOS_OK; 253} 254LOS_MODULE_INIT(Example_Trace_test, LOS_INIT_LEVEL_KMOD_EXTENDED); 255``` 256 257 258### 结果验证 259 260输出结果如下: 261 262 263``` 264***TraceInfo begin*** 265clockFreq = 50000000 266CurEvtIndex = 7 267Index Time(cycles) EventType CurTask Identity params 2680 0x366d5e88 0x45 0x1 0x0 0x1f 0x4 0x0 2691 0x366d74ae 0x45 0x0 0x1 0x0 0x8 0x1f 2702 0x36940da6 0x45 0x1 0xc 0x1f 0x4 0x9 2713 0x3694337c 0x45 0xc 0x1 0x9 0x8 0x1f 2724 0x36eea56e 0x45 0x1 0xc 0x1f 0x4 0x9 2735 0x36eec810 0x45 0xc 0x1 0x9 0x8 0x1f 2746 0x3706f804 0x45 0x1 0x0 0x1f 0x4 0x0 2757 0x37070e59 0x45 0x0 0x1 0x0 0x8 0x1f 276***TraceInfo end*** 277``` 278 279输出的事件信息包括:发生时间、事件类型、事件发生在哪个任务中、事件操作的主体对象、事件的其他参数。 280 281- EventType:表示的具体事件可查阅头文件 [los_trace.h](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_trace.h) 中的enum LOS_TRACE_TYPE。 282 283- CurrentTask:表示当前运行在哪个任务中,值为taskid。 284 285- Identity:表示事件操作的主体对象,可查阅头文件 [los_trace.h](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_trace.h) 中的\#TYPE\#_PARAMS。 286 287- params:表示的事件参数可查阅头文件 [los_trace.h](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_trace.h) 中的\#TYPE\#_PARAMS。 288 289下面以序号为0的输出项为例,进行说明。 290 291 292``` 293Index Time(cycles) EventType CurTask Identity params 2940 0x366d5e88 0x45 0x1 0x0 0x1f 0x4 295``` 296 297- Time cycles可换算成时间,换算公式为cycles/clockFreq,单位为s。 298 299- 0x45为TASK_SWITCH即任务切换事件,当前运行的任务taskid为0x1。 300 301- Identity和params的含义需要查看TASK_SWITCH_PARAMS宏定义: 302 303```c 304#define TASK_SWITCH_PARAMS(taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus) \ 305taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus 306``` 307 308因为\#TYPE\#_PARAMS(IDENTITY, parma1...) IDENTITY, ...,所以Identity为taskId(0x0),第一个参数为oldPriority(0x1f) 309 310> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 311> params的个数由LOSCFG_TRACE_FRAME_MAX_PARAMS配置,默认为3,超出的参数不会被记录,用户应自行合理配置该值。 312 313综上所述,任务由0x1切换到0x0,0x1任务的优先级为0x1f,状态为0x4,0x0任务的优先级为0x0。 314