diff --git a/zh-cn/device-dev/kernel/kernel-mini-memory-trace.md b/zh-cn/device-dev/kernel/kernel-mini-memory-trace.md index b725f81ef57d7690bc24b49f890b1f36779de19e..0b25cc98ef8b9d8d2a8f718fa19bd3d58b35cf59 100644 --- a/zh-cn/device-dev/kernel/kernel-mini-memory-trace.md +++ b/zh-cn/device-dev/kernel/kernel-mini-memory-trace.md @@ -1,196 +1,351 @@ -# Trace调测 +# Trace调测 -- [基本概念](#section44851752123712) -- [运行机制](#section5282148123813) -- [接口说明](#section16304193215387) -- [开发指导](#section498695853819) - - [开发流程](#section1875652316393) - - [编程实例](#section0403134913395) - - [示例代码](#section1492711418400) - - [结果验证](#section869613984012) +- [基本概念](#section1) +- [运行机制](#section2) -## 基本概念 +- [接口说明](#section3) +- [开发指导](#section4) + + - [开发流程](#section4.1) + + - [编程实例](#section4.2) + + - [实例代码](#section4.3) + + - [结果验证](#section4.4) + +## 基本概念 Trace调测旨在帮助开发者获取内核的运行流程,各个模块、任务的执行顺序,从而可以辅助开发者定位一些时序问题或者了解内核的代码运行过程。 -## 运行机制 +## 运行机制 +内核提供一套Hook框架,将Hook点预埋在各个模块的主要流程中, 在内核启动初期完成Trace功能的初始化,并注册Trace的处理函数到Hook中。 + +当系统触发到一个Hook点时,Trace模块会对输入信息进行封装,添加Trace帧头信息,包含事件类型、运行的cpuid、运行的任务id、运行的相对时间戳等信息; -内核提供一套Hook框架,将Hook点预埋在各个模块的主要流程中,开发者通过注册的形式在自己所需的Hook点上注册回调函数,当内核运行至对应流程中会由内核主动调用Hook函数,将当前流程的关键数据传递给开发者。 +Trace提供2种工作模式,离线模式和在线模式。 -## 接口说明 +离线模式会将trace frame记录到预先申请好的循环buffer中。如果循环buffer记录的frame过多则可能出现翻转,会覆盖之前的记录,故保持记录的信息始终是最新的信息。Trace循环buffer的数据可以通过shell命令导出进行详细分析,导出信息已按照时间戳信息完成排序。 -OpenHarmony LiteOS-M内核的Trace模块提供下面几种功能,接口详细信息可以查看API参考。 +![](figure/zh-cn_image_0000001127390512.png) -**表 1** Trace模块接口说明 +在线模式需要配合IDE使用,实时将trace frame记录发送给IDE,IDE端进行解析并可视化展示。 - -

功能分类

+## 接口说明 +OpenHarmony LiteOS-M内核的Trace模块提供下面几种功能,接口详细信息可以查看[API](https://gitee.com/openharmony/kernel_liteos_m/blob/master/components/trace/los_trace.h)参考。 + +**表 1** Trace模块接口说明 + + - - - + + + + + + + + + + + + + + + + + - - - - - + + + + + + +

功能分类

接口名

+

接口名

描述

+

描述

Hook注册接口

+

启停控制

+

LOS_TraceStart

+

启动Trace

+

LOS_TraceStop

+

停止Trace

+

操作Trace记录的数据

+

LOS_TraceRecordDump

+

输出Trace缓冲区数据

+

LOS_TraceRecordGet

+

获取Trace缓冲区的首地址

+

LOS_TraceReset

+

清除Trace缓冲区中的事件

+

过滤Trace记录的数据

LOS_HookReg

+

LOS_TraceEventMaskSet

向指定Hook点注册回调函数

+

设置事件掩码,仅记录某些模块的事件

Hook解注册接口

+

屏蔽某些中断号事件

LOS_HookUnReg

+

LOS_TraceHwiFilterHookReg

解注册当前Hook点的回调函数

+

注册过滤特定中断号事件的钩子函数

+

插桩函数

+

LOS_TRACE_EASY

+

简易插桩

+

LOS_TRACE

+

标准插桩

-## 开发指导 -### 开发流程 +1. 当用户需要针对自定义事件进行追踪时,可按规则在目标源代码中进行插桩,系统提供如下2种插桩接口: -开启Trace调测的典型流程如下: ++ LOS_TRACE_EASY(TYPE, IDENTITY, params...) 简易插桩。 + + - 一句话插桩,用户在目标源代码中插入该接口即可。 -1. 配置Trace模块相关宏。 + - TYPE有效取值范围为[0, 0xF],表示不同的事件类型,不同取值表示的含义由用户自定义。 - 需要在target\_config.h头文件中修改配置: + - IDENTITY类型UINTPTR,表示事件操作的主体对象。 - - - - - - - - - - - -

配置项

-

含义

-

设置值

-

LOSCFG_DEBUG_HOOK

-

Trace功能的开关

-

0:关闭;1:打开

-
+ - Params类型UINTPTR,表示事件的参数。 -2. 选择想要注册的Hook点,实现对应的回调函数,Hook类型清单见liteos\_m/utils/internal/los\_hook\_types.h。 -3. 调用LOS\_HookReg进行函数注册。 + - 示例: -### 编程实例 + ``` + 假设需要新增对文件(fd1、fd2)读写操作的简易插桩, + 自定义读操作为type:1, 写操作为type:2,则 + 在读fd1文件的适当位置插入: + LOS_TRACE_EASY(1, fd1, flag, size); + 在读fd2文件的适当位置插入: + LOS_TRACE_EASY(1, fd2, flag, size); + 在写fd1文件的适当位置插入: + LOS_TRACE_EASY(2, fd1, flag, size); + 在写fd2文件的适当位置插入: + LOS_TRACE_EASY(2, fd2,flag, size); + ``` -本实例实现功能:模拟运行时malloc、free不同大小的内存,记录每次malloc,free的行为及时序。 ++ LOS_TRACE(TYPE, IDENTITY, params...) 标准插桩。 -### 示例代码 + - 相比简易插桩,支持动态过滤事件和参数裁剪,但使用上需要用户按规则来扩展。 -示例代码如下: + - TYPE用于设置具体的事件类型,可以在头文件los_trace.h中的enum LOS_TRACE_TYPE中自定义事件类型。定义方法和规则可以参考其他事件类型。 + - IDENTITY和Params的类型及含义同简易插桩。 + + - 示例: +``` + 1.在enum LOS_TRACE_MASK中定义事件掩码,即模块级别的事件类型。 + 定义规范为TRACE_#MOD#_FLAG,#MOD#表示模块名,例如: + TRACE_FS_FLAG = 0x4000 + 2.在enum LOS_TRACE_TYPE中定义具体事件类型。定义规范为#TYPE# = TRACE_#MOD#_FLAG | NUMBER,例如: + FS_READ = TRACE_FS_FLAG | 0; // 读文件 + FS_WRITE = TRACE_FS_FLAG | 1; // 写文件 + 3.定义事件参数。定义规范为#TYPE#_PARAMS(IDENTITY, parma1...) IDENTITY, ... + 其中的#TYPE#就是上面2中的#TYPE#,例如: + #define FS_READ_PARAMS(fp, fd, flag, size) fp, fd, flag, size + 宏定义的参数对应于Trace缓冲区中记录的事件参数,用户可对任意参数字段进行裁剪: + 当定义为空时,表示不追踪该类型事件: + #define FS_READ_PARAMS(fp, fd, flag, size) // 不追踪文件读事件 + 4.在适当位置插入代码桩。定义规范为LOS_TRACE(#TYPE#, #TYPE#_PARAMS(IDENTITY, parma1...)) + LOS_TRACE(FS_READ, fp, fd, flag, size); // 读文件的代码桩, + #TYPE#之后的入参就是上面3中的FS_READ_PARAMS函数的入参 ``` -#include "stdio.h" -#include "stdlib.h" -#include "string.h" -#include "los_hook.h" +2. 预置的Trace事件及参数均可以通过上述方式进行裁剪,参数详见kernel\include\los_trace.h。 -#define SIZE_512 512 -#define SIZE_1K 1024 -#define SIZE_2K 2048 +3. 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); -/* 回调时打印malloc出来的大小 */ -void MallocRecord(void *pool, unsigned int size) -{ - printf("malloc size = %u\n", size); - return; -} +4. 如果仅需要过滤简易插桩事件,通过设置Trace Mask为TRACE_MAX_FLAG即可。 -/* 回调时打印free的指针地址 */ -void FreeRecord(void *pool, void *ptr) -{ - printf("free pool = 0x%x ptr = 0x%x\n", pool, ptr); - return; -} +5. Trace缓冲区有限,事件写满之后会覆盖写,用户可通过LOS_TraceRecordDump中打印的CurEvtIndex识别最新记录。 -void TestTrace(void) +6. Trace的典型操作流程为:LOS_TraceStart、 LOS_TraceStop、 LOS_TraceRecordDump. + +7. 针对中断事件的Trace, 提供中断号过滤,用于解决某些场景下特定中断号频繁触发导致其他事件被覆盖的情况,用户可自定义中断过滤的规则, + + 示例如下: +``` +BOOL Example_HwiNumFilter(UINT32 hwiNum) { - char *pool1 = NULL; - char *pool2 = NULL; - char *pool3 = NULL; - char *retptr = NULL; - /* 分别为pool1,pool2,pool3分配不同大小的内存,用于区分验证 */ - pool1 = (char *)malloc(SIZE_512); - if (pool1 == NULL) { - printf("pool1 malloc failed!\n"); - return; + if ((hwi == TIMER_INT) || (hwi == DMA_INT)) { + return TRUE; } - retptr = memset(pool1, 'a', SIZE_512); - if (retptr == NULL) { - printf("pool1 memset failed!\n"); - return; - } - printf("pool1 addr = 0x%x *pool1[0] = %c\n", pool1, *pool1); + return FALSE; +} +LOS_TraceHwiFilterHookReg(Example_HwiNumFilter); +``` +则当中断号为TIMER_INT 或者DMA_INT时,不记录中断事件。 - pool2 = (char *)malloc(SIZE_1K); - if (pool2 == NULL) { - printf("pool2 malloc failed!\n"); - return; - } - retptr = memset(pool2, 'b', SIZE_1K); - if (retptr == NULL) { - printf("pool2 memset failed!\n"); - return; - } - printf("pool2 addr = 0x%x *pool2[0] = %c\n", pool2, *pool2); +## 开发指导 - pool3 = (char *)malloc(SIZE_2K); - if (pool3 == NULL) { - printf("pool3 malloc failed!\n"); - return; - } - retptr = memset(pool3, 'c', SIZE_2K); - if (retptr == NULL) { - printf("pool3 memset failed!\n"); - return; - } - printf("pool3 addr = 0x%x *pool3[0] = %c\n", pool3, *pool3); +### 开发流程 + +开启Trace调测的典型流程如下: + +1. 配置Trace模块相关宏。 + +需要在target_config.h头文件中修改配置: + +| 配置项 | 含义 | 设置值 | +| ------------------------------ | ------------------------------------------------------------ | ------ | +| LOSCFG_KERNEL_TRACE | Trace模块的裁剪开关 | YES/NO | +| LOSCFG_RECORDER_MODE_OFFLINE | Trace工作模式为离线模式 | YES/NO | +| LOSCFG_RECORDER_MODE_ONLINE | Trace工作模式为在线模式 | YES/NO | +| LOSCFG_TRACE_CLIENT_INTERACT | 使能与Trace IDE (dev tools)的交互,包括数据可视化和流程控制 | YES/NO | +| LOSCFG_TRACE_FRAME_CORE_MSG | 记录CPUID、中断状态、锁任务状态 | YES/NO | +| LOSCFG_TRACE_FRAME_EVENT_COUNT | 记录事件的次序编号 | YES/NO | +| LOSCFG_TRACE_FRAME_MAX_PARAMS | 配置记录事件的最大参数个数 | INT | +| LOSCFG_TRACE_BUFFER_SIZE | 配置Trace的缓冲区大小 | INT | + +2. (可选)预置事件参数和事件桩(或使用系统默认的事件参数配置和事件桩)。 + +3. (可选)调用LOS_TraceStop停止Trace后,清除缓冲区LOS_TraceReset(系统默认已启动trace)。 + +4. (可选)调用LOS_TraceEventMaskSet设置需要追踪的事件掩码(系统默认的事件掩码仅使能中断与任务事件),事件掩码参见los_trace.h 中的LOS_TRACE_MASK定义。 + +5. 在需要记录事件的代码起始点调用LOS_TraceStart。 + +6. 在需要记录事件的代码结束点调用LOS_TraceStop。 + +7. 调用LOS_TraceRecordDump输出缓冲区数据(函数的入参为布尔型,FALSE表示格式化输出,TRUE表示输出到windows客户端)。 + + +上述第3-7步中的接口,均封装有对应的shell命令,对应关系如下 + +- LOS_TraceReset —— trace_reset + +- LOS_TraceEventMaskSet —— trace_mask + +- LOS_TraceStart —— trace_start + +- LOS_TraceStop —— trace_stop + +- LOS_TraceRecordDump —— trace_dump + +### 编程实例 + + 本实例实现如下功能: + + 1. 创建一个用于Trace测试的任务。 - /* 按pool3, pool1, pool2的顺序释放,来检验回调函数的时序 */ - free(pool3); - free(pool1); - free(pool2); + 2. 设置事件掩码。 - return; + 3. 启动trace。 + + 4. 停止trace。 + + 5. 格式化输出trace数据。 + +### 示例代码 + +实例代码如下: + +```C +#include "los_trace.h" +UINT32 g_traceTestTaskId; +VOID Example_Trace(VOID) +{ + UINT32 ret; + LOS_TaskDelay(10); + /* 开启trace */ + ret = LOS_TraceStart(); + if (ret != LOS_OK) { + dprintf("trace start error\n"); + return; + } + /* 触发任务切换的事件 */ + LOS_TaskDelay(1); + LOS_TaskDelay(1); + LOS_TaskDelay(1); + /* 停止trace */ + LOS_TraceStop(); + LOS_TraceRecordDump(FALSE); } -/* 在使用Trace模块功能之前,首先进行回调函数的注册,注意回调函数的返回值都为void */ -void InitTest(void) -{ - printf("init hook\n"); - /* 根据想要获取的Trace信息选择对应的Hook类型进行注册,具体可选Hook类型清单见liteos_m/utils/internal/los_hook_types.h */ - LOS_HookReg(LOS_HOOK_TYPE_MEM_ALLOC, MallocRecord); - LOS_HookReg(LOS_HOOK_TYPE_MEM_FREE, FreeRecord); - return; + +UINT32 Example_Trace_test(VOID){ + UINT32 ret; + TSK_INIT_PARAM_S traceTestTask; + /* 创建用于trace测试的任务 */ + memset(&traceTestTask, 0, sizeof(TSK_INIT_PARAM_S)); + traceTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Trace; + traceTestTask.pcName = "TestTraceTsk"; /* 测试任务名称 */ + traceTestTask.uwStackSize = 0x800; + traceTestTask.usTaskPrio = 5; + traceTestTask.uwResved = LOS_TASK_STATUS_DETACHED; + ret = LOS_TaskCreate(&g_traceTestTaskId, &traceTestTask); + if(ret != LOS_OK){ + dprintf("TraceTestTask create failed .\n"); + return LOS_NOK; + } + /* 系统默认情况下已启动trace, 因此可先关闭trace后清除缓存区后,再重启trace */ + LOS_TraceStop(); + LOS_TraceReset(); + /* 开启任务模块事件记录 */ + LOS_TraceEventMaskSet(TRACE_TASK_FLAG); + return LOS_OK; } ``` -### 结果验证 +### 结果验证 输出结果如下: +```c +*******TraceInfo begin******* +clockFreq = 50000000 +CurEvtIndex = 7 +Index Time(cycles) EventType CurTask Identity params +0 0x366d5e88 0x45 0x1 0x0 0x1f 0x4 0x0 +1 0x366d74ae 0x45 0x0 0x1 0x0 0x8 0x1f +2 0x36940da6 0x45 0x1 0xc 0x1f 0x4 0x9 +3 0x3694337c 0x45 0xc 0x1 0x9 0x8 0x1f +4 0x36eea56e 0x45 0x1 0xc 0x1f 0x4 0x9 +5 0x36eec810 0x45 0xc 0x1 0x9 0x8 0x1f +6 0x3706f804 0x45 0x1 0x0 0x1f 0x4 0x0 +7 0x37070e59 0x45 0x0 0x1 0x0 0x8 0x1f +*******TraceInfo end******* ``` -init hook -malloc size = 512 -pool1 addr = 0x20002f44 *pool1[0] = a -malloc size = 1024 -pool2 addr = 0x2000314c *pool2[0] = b -malloc size = 2048 -pool3 addr = 0x20003554 *pool3[0] = c -free pool = 0x200002a4 ptr = 0x20003554 -free pool = 0x200002a4 ptr = 0x20002f44 -free pool = 0x200002a4 ptr = 0x2000314c + +输出的事件信息包括:发生时间、事件类型、事件发生在哪个任务中、事件操作的主体对象、事件的其他参数。 + +- EventType:表示的具体事件可查阅头文件los_trace.h中的enum LOS_TRACE_TYPE。 + +- CurrentTask:表示当前运行在哪个任务中,值为taskid。 + +- Identity:表示事件操作的主体对象,可查阅头文件los_trace.h中的#TYPE#_PARAMS。 + +- params:表示的事件参数可查阅头文件los_trace.h中的#TYPE#_PARAMS。 + +下面以序号为0的输出项为例,进行说明。 + +``` +Index Time(cycles) EventType CurTask Identity params +0 0x366d5e88 0x45 0x1 0x0 0x1f 0x4 ``` -根据地址信息,可以看到free的顺序为pool3,pool1,pool2。 +- Time cycles可换算成时间,换算公式为cycles/clockFreq,单位为s。 + +- 0x45为TASK_SWITCH即任务切换事件,当前运行的任务taskid为0x1。 + +- Identity和params的含义需要查看TASK_SWITCH_PARAMS宏定义: + +```c +#define TASK_SWITCH_PARAMS(taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus) \ +taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus +``` + +因为#TYPE#_PARAMS(IDENTITY, parma1...) IDENTITY, ...,所以Identity为taskId(0x0),第一个参数为oldPriority(0x1f) + +> ![](../public_sys-resources/icon-note.gif)**说明** + + > params的个数由LOSCFG_TRACE_FRAME_MAX_PARAMS配置,默认为3,超出的参数不会被记录,用户应自行合理配置该值。 + + 综上所述,任务由0x1切换到0x0,0x1任务的优先级为0x1f,状态为0x4,0x0任务的优先级为0x0。 diff --git a/zh-cn/device-dev/kernel/kernel-small-debug-trace.md b/zh-cn/device-dev/kernel/kernel-small-debug-trace.md index 0d1b3b0684b86180d484c8f2f076bcaa0dc78c99..ffd5cfafd3daac7db6c4d5094a4b09e8a906c0de 100644 --- a/zh-cn/device-dev/kernel/kernel-small-debug-trace.md +++ b/zh-cn/device-dev/kernel/kernel-small-debug-trace.md @@ -1,102 +1,513 @@ -# Trace +# Trace调测 -- [基本概念](#section531482192018) -- [运行机制](#section5125124532010) -- [使用指导](#section1381714413216) +- [基本概念](#section1) -## 基本概念 +- [运行机制](#section2) -Trace是内核提供的一个跟踪模块关键流程的功能框架,各个模块根据机制需要,可以在关键位置通过Trace进行相关信息记录,再通过文件或者其他方式导出,对数据进行分析。 +- [接口说明](#section3) -Trace的记录要求: + - [内核态](#section3.1) -1. 需要尽快的完成信息记录,对系统时序的影响降低到最小; -2. 需要尽可能的用小的空间存储大的数据量,减少内存消耗。 + - [用户态](#section3.2) -## 运行机制 +- [开发指导](#section4) -在内核启动初期完成Trace功能的初始化,并且在支持Trace功能的模块进行初始化时,完成模块相应的Trace类型及回调函数的注册,触发Trace记录的trace point的具体位置可由各模块自行决定。 + - [内核态开发流程](#section4.1.1) -当系统触发到一个trace point时,Trace模块会对输入信息进行封装,添加Trace帧头信息,包含Trace类型、运行的cpuid、运行的任务id、运行的相对时间戳以及运行的进程id;再将trace frame记录到预先申请好的循环buffer中。 + - [内核态编程实例](#section4.1.2) -如果循环buffer记录的frame过多则可能出现翻转,会覆盖之前的记录,故保持记录的信息始终是最新的信息。Trace循环buffer的数据可以通过文件dump等形式导出进行详细分析,导出信息已按照时间戳信息完成排序。 + - [内核态实例代码](#section4.1.3) -![](figure/zh-cn_image_0000001127390512.png) + - [内核态结果验证](#section4.1.4) + + - [用户态开发流程](#section4.2.1) + + - [用户态编程实例](#section4.2.2) + + - [用户态实例代码](#section4.2.3) + + - [用户态结果验证](#section4.2.4) + +## 基本概念 +Trace调测旨在帮助开发者获取内核的运行流程,各个模块、任务的执行顺序,从而可以辅助开发者定位一些时序问题或者了解内核的代码运行过程。 + +## 运行机制 +内核提供一套Hook框架,将Hook点预埋在各个模块的主要流程中, 在内核启动初期完成Trace功能的初始化,并注册Trace的处理函数到Hook中。 + +当系统触发到一个Hook点时,Trace模块会对输入信息进行封装,添加Trace帧头信息,包含事件类型、运行的cpuid、运行的任务id、运行的相对时间戳等信息; -## 使用指导 +Trace提供2种工作模式,离线模式和在线模式。 -1. 配置Trace控制宏LOSCFG\_KERNEL\_TRACE,默认关,通过在menuconfig内核配置"Kernel-\>Enable Trace Feature"中打开。 -2. 内核启动完成Trace初始化,对应接口OsTraceInit。 -3. 统一在los\_trace\_frame.h定义模块内traceframe结构体,并在los\_trace\_frame.c中实现模块Trace回调函数的实现。 -4. 需要跟踪的模块在初始化或者更早阶段完成Trace注册,对应接口LOS\_TraceReg。 -5. 在需要开始跟踪的位置打开Trace开关进行记录,结束的位置关闭Trace开关停止记录。 -6. 通过LOS\_Trace2File接口导出到文件。 -7. 根据帧头及帧体数据结构,对数据进行二次分析。 +离线模式会将trace frame记录到预先申请好的循环buffer中。如果循环buffer记录的frame过多则可能出现翻转,会覆盖之前的记录,故保持记录的信息始终是最新的信息。Trace循环buffer的数据可以通过shell命令导出进行详细分析,导出信息已按照时间戳信息完成排序。 -**接口说明** +![](figure/zh-cn_image_0000001127390512.png) + +在线模式需要配合IDE使用,实时将trace frame记录发送给IDE,IDE端进行解析并可视化展示。 -OpenHarmony LiteOS-A内核的Trace框架提供下面几种功能,接口详细信息可以查看API参考。 +## 接口说明 -**表 1** Trace接口说明 +### 内核态 - -

功能分类

+OpenHarmony LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以查看[API](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_trace.h)参考。 + +**表 1** Trace模块接口说明 + + - - - + - - + + - + + - + - - - - - - - + - - - - - -

功能分类

接口名称

+

接口名

描述

+

描述

打桩记录

+

启停控制

+

LOS_TraceStart

LOS_Trace

+

启动Trace

Trace信息记录打桩

+

LOS_TraceStop

+

停止Trace

注册解注册

+

操作Trace记录的数据

+

LOS_TraceRecordDump

+

输出Trace缓冲区数据

LOS_TraceReg

+

LOS_TraceRecordGet

Trace类型注册

+

获取Trace缓冲区的首地址

LOS_TraceUnreg

+

LOS_TraceReset

Trace类型解注册

+

清除Trace缓冲区中的事件

开关

+

过滤Trace记录的数据

LOS_TraceTypeSwitch

+

LOS_TraceEventMaskSet

Trace分类开关

+

设置事件掩码,仅记录某些模块的事件

LOS_TraceSwitch

+

屏蔽某些中断号事件

+

LOS_TraceHwiFilterHookReg

Trace总开关

+

注册过滤特定中断号事件的钩子函数

数据导出

+

插桩函数

LOS_TraceBufDataGet

+

LOS_TRACE_EASY

Trace数据获取到堆缓存(缓存空间内部申请,使用完需显式释放)

+

简易插桩

LOS_Trace2File

+

LOS_TRACE

Trace数据存储到文件系统(依赖文件系统)

+

标准插桩

->![](../public_sys-resources/icon-note.gif) **说明:** ->- frame buffer是个循环buffer,如果记录过长时间会覆盖旧的信息,该buffer的大小可以在los\_config.h中LOS\_TRACE\_BUFFER\_SIZE宏配置,默认配置512KiB。 ->- 尽管Trace已经尽快的完成了组装及记录,但是仍旧会对系统整体性能产生不可避免的影响。 ->- Trace支持多个模块的Trace同时记录,根据需要开关各个模块的Trace,有助于降低数据分析量。 ->- 当前内核的task、memory、liteipc模块支持Trace记录功能。 +1. 当用户需要针对自定义事件进行追踪时,可按规则在目标源代码中进行插桩,系统提供如下2种插桩接口: + ++ LOS_TRACE_EASY(TYPE, IDENTITY, params...) 简易插桩。 + + - 一句话插桩,用户在目标源代码中插入该接口即可。 + + - TYPE有效取值范围为[0, 0xF],表示不同的事件类型,不同取值表示的含义由用户自定义。 + + - IDENTITY类型UINTPTR,表示事件操作的主体对象。 + + - Params类型UINTPTR,表示事件的参数。 + + - 示例: + + ``` + 假设需要新增对文件(fd1、fd2)读写操作的简易插桩, + 自定义读操作为type:1, 写操作为type:2,则 + 在读fd1文件的适当位置插入: + LOS_TRACE_EASY(1, fd1, flag, size); + 在读fd2文件的适当位置插入: + LOS_TRACE_EASY(1, fd2, flag, size); + 在写fd1文件的适当位置插入: + LOS_TRACE_EASY(2, fd1, flag, size); + 在写fd2文件的适当位置插入: + LOS_TRACE_EASY(2, fd2,flag, size); + ``` + ++ LOS_TRACE(TYPE, IDENTITY, params...) 标准插桩。 + + - 相比简易插桩,支持动态过滤事件和参数裁剪,但使用上需要用户按规则来扩展。 + + - TYPE用于设置具体的事件类型,可以在头文件los_trace.h中的enum LOS_TRACE_TYPE中自定义事件类型。定义方法和规则可以参考其他事件类型。 + + - IDENTITY和Params的类型及含义同简易插桩。 + + - 示例: +``` + 1.在enum LOS_TRACE_MASK中定义事件掩码,即模块级别的事件类型。 + 定义规范为TRACE_#MOD#_FLAG,#MOD#表示模块名,例如: + TRACE_FS_FLAG = 0x4000 + 2.在enum LOS_TRACE_TYPE中定义具体事件类型。定义规范为#TYPE# = TRACE_#MOD#_FLAG | NUMBER,例如: + FS_READ = TRACE_FS_FLAG | 0; // 读文件 + FS_WRITE = TRACE_FS_FLAG | 1; // 写文件 + 3.定义事件参数。定义规范为#TYPE#_PARAMS(IDENTITY, parma1...) IDENTITY, ... + 其中的#TYPE#就是上面2中的#TYPE#,例如: + #define FS_READ_PARAMS(fp, fd, flag, size) fp, fd, flag, size + 宏定义的参数对应于Trace缓冲区中记录的事件参数,用户可对任意参数字段进行裁剪: + 当定义为空时,表示不追踪该类型事件: + #define FS_READ_PARAMS(fp, fd, flag, size) // 不追踪文件读事件 + 4.在适当位置插入代码桩。定义规范为LOS_TRACE(#TYPE#, #TYPE#_PARAMS(IDENTITY, parma1...)) + LOS_TRACE(FS_READ, fp, fd, flag, size); // 读文件的代码桩, + #TYPE#之后的入参就是上面3中的FS_READ_PARAMS函数的入参 +``` +2. 预置的Trace事件及参数均可以通过上述方式进行裁剪,参数详见kernel\include\los_trace.h。 + +3. 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); + +4. 如果仅需要过滤简易插桩事件,通过设置Trace Mask为TRACE_MAX_FLAG即可。 + +5. Trace缓冲区有限,事件写满之后会覆盖写,用户可通过LOS_TraceRecordDump中打印的CurEvtIndex识别最新记录。 + +6. Trace的典型操作流程为:LOS_TraceStart、 LOS_TraceStop、 LOS_TraceRecordDump. + +7. 针对中断事件的Trace, 提供中断号过滤,用于解决某些场景下特定中断号频繁触发导致其他事件被覆盖的情况,用户可自定义中断过滤的规则, + + 示例如下: +``` +BOOL Example_HwiNumFilter(UINT32 hwiNum) +{ + if ((hwi == TIMER_INT) || (hwi == DMA_INT)) { + return TRUE; + } + return FALSE; +} +LOS_TraceHwiFilterHookReg(Example_HwiNumFilter); +``` +则当中断号为TIMER_INT 或者DMA_INT时,不记录中断事件。 + +### 用户态 + +新增trace字符设备,位于"/dev/trace",通过对设备节点的read\write\ioctl,实现用户态trace的读写和控制: + +- read: 用户态读取Trace记录数据 + +- write: 用户态事件写入 + +- ioctl: 用户态Trace控制操作,包括 +```C +#define TRACE_IOC_MAGIC 'T' +#define TRACE_START _IO(TRACE_IOC_MAGIC, 1) +#define TRACE_STOP _IO(TRACE_IOC_MAGIC, 2) +#define TRACE_RESET _IO(TRACE_IOC_MAGIC, 3) +#define TRACE_DUMP _IO(TRACE_IOC_MAGIC, 4) +#define TRACE_SET_MASK _IO(TRACE_IOC_MAGIC, 5) +``` +分别对应Trace启动(LOS_TraceStart)、停止(LOS_TraceStop)、清除记录(LOS_TraceReset)、dump记录(LOS_TraceRecordDump)、设置事件过滤掩码(LOS_TraceEventMaskSet) + +具体的使用方法参见[用户态编程实例](#section4.2.2) + + + +## 开发指导 + +### 内核态开发流程 + +开启Trace调测的典型流程如下: + +1. 配置Trace模块相关宏。 + +配置Trace控制宏LOSCFG_KERNEL_TRACE,默认关,在kernel/liteos_a目录下执行 make update_config命令配置"Kernel->Enable Hook Feature->Enable Trace Feature"中打开: + +| 配置项 | menuconfig选项 | 含义 | 设置值 | +| ------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------ | +| LOSCFG_KERNEL_TRACE | Enable Trace Feature | Trace模块的裁剪开关 | YES/NO | +| LOSCFG_RECORDER_MODE_OFFLINE | Trace work mode ->Offline mode | Trace工作模式为离线模式 | YES/NO | +| LOSCFG_RECORDER_MODE_ONLINE | Trace work mode ->Online mode | Trace工作模式为在线模式 | YES/NO | +| LOSCFG_TRACE_CLIENT_INTERACT | Enable Trace Client Visualization and Control | 使能与Trace IDE (dev tools)的交互,包括数据可视化和流程控制 | YES/NO | +| LOSCFG_TRACE_FRAME_CORE_MSG | Enable Record more extended content ->Record cpuid, hardware interrupt status, task lock status | 记录CPUID、中断状态、锁任务状态 | YES/NO | +| LOSCFG_TRACE_FRAME_EVENT_COUNT | Enable Record more extended content ->Record event count, which indicate the sequence of happend events | 记录事件的次序编号 | YES/NO | +| LOSCFG_TRACE_FRAME_MAX_PARAMS | Record max params | 配置记录事件的最大参数个数 | INT | +| LOSCFG_TRACE_BUFFER_SIZE | Trace record buffer size | 配置Trace的缓冲区大小 | INT | + +2. (可选)预置事件参数和事件桩(或使用系统默认的事件参数配置和事件桩)。 + +3. (可选)调用LOS_TraceStop停止Trace后,清除缓冲区LOS_TraceReset(系统默认已启动trace)。 + +4. (可选)调用LOS_TraceEventMaskSet设置需要追踪的事件掩码(系统默认的事件掩码仅使能中断与任务事件),事件掩码参见los_trace.h 中的LOS_TRACE_MASK定义。 + +5. 在需要记录事件的代码起始点调用LOS_TraceStart。 + +6. 在需要记录事件的代码结束点调用LOS_TraceStop。 + +7. 调用LOS_TraceRecordDump输出缓冲区数据(函数的入参为布尔型,FALSE表示格式化输出,TRUE表示输出到Trace IDE)。 + + +上述第3-7步中的接口,均封装有对应的shell命令,开启shell后可执行相应的命令,对应关系如下: + +- LOS_TraceReset —— trace_reset + +- LOS_TraceEventMaskSet —— trace_mask + +- LOS_TraceStart —— trace_start + +- LOS_TraceStop —— trace_stop + +- LOS_TraceRecordDump —— trace_dump + +### 内核态编程实例 + + 本实例实现如下功能: + + 1. 创建一个用于Trace测试的任务。 + + 2. 设置事件掩码。 + + 3. 启动trace。 + + 4. 停止trace。 + + 5. 格式化输出trace数据。 + +### 内核态示例代码 + +实例代码如下: + +```C +#include "los_trace.h" +UINT32 g_traceTestTaskId; +VOID Example_Trace(VOID) +{ + UINT32 ret; + LOS_TaskDelay(10); + /* 开启trace */ + ret = LOS_TraceStart(); + if (ret != LOS_OK) { + dprintf("trace start error\n"); + return; + } + /* 触发任务切换的事件 */ + LOS_TaskDelay(1); + LOS_TaskDelay(1); + LOS_TaskDelay(1); + /* 停止trace */ + LOS_TraceStop(); + LOS_TraceRecordDump(FALSE); +} + +UINT32 Example_Trace_test(VOID){ + UINT32 ret; + TSK_INIT_PARAM_S traceTestTask; + /* 创建用于trace测试的任务 */ + memset(&traceTestTask, 0, sizeof(TSK_INIT_PARAM_S)); + traceTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Trace; + traceTestTask.pcName = "TestTraceTsk"; /* 测试任务名称 */ + traceTestTask.uwStackSize = 0x800; + traceTestTask.usTaskPrio = 5; + traceTestTask.uwResved = LOS_TASK_STATUS_DETACHED; + ret = LOS_TaskCreate(&g_traceTestTaskId, &traceTestTask); + if(ret != LOS_OK){ + dprintf("TraceTestTask create failed .\n"); + return LOS_NOK; + } + /* 系统默认情况下已启动trace, 因此可先关闭trace后清除缓存区后,再重启trace */ + LOS_TraceStop(); + LOS_TraceReset(); + /* 开启任务模块事件记录 */ + LOS_TraceEventMaskSet(TRACE_TASK_FLAG); + return LOS_OK; +} +LOS_MODULE_INIT(Example_Trace_test, LOS_INIT_LEVEL_KMOD_EXTENDED); +``` + +### 内核态结果验证 + +输出结果如下: + +```c +*******TraceInfo begin******* +clockFreq = 50000000 +CurEvtIndex = 7 +Index Time(cycles) EventType CurTask Identity params +0 0x366d5e88 0x45 0x1 0x0 0x1f 0x4 0x0 +1 0x366d74ae 0x45 0x0 0x1 0x0 0x8 0x1f +2 0x36940da6 0x45 0x1 0xc 0x1f 0x4 0x9 +3 0x3694337c 0x45 0xc 0x1 0x9 0x8 0x1f +4 0x36eea56e 0x45 0x1 0xc 0x1f 0x4 0x9 +5 0x36eec810 0x45 0xc 0x1 0x9 0x8 0x1f +6 0x3706f804 0x45 0x1 0x0 0x1f 0x4 0x0 +7 0x37070e59 0x45 0x0 0x1 0x0 0x8 0x1f +*******TraceInfo end******* +``` + +输出的事件信息包括:发生时间、事件类型、事件发生在哪个任务中、事件操作的主体对象、事件的其他参数。 + +- EventType:表示的具体事件可查阅头文件los_trace.h中的enum LOS_TRACE_TYPE。 + +- CurrentTask:表示当前运行在哪个任务中,值为taskid。 + +- Identity:表示事件操作的主体对象,可查阅头文件los_trace.h中的#TYPE#_PARAMS。 + +- params:表示的事件参数可查阅头文件los_trace.h中的#TYPE#_PARAMS。 + +下面以序号为0的输出项为例,进行说明。 + +``` +Index Time(cycles) EventType CurTask Identity params +0 0x366d5e88 0x45 0x1 0x0 0x1f 0x4 +``` + +- Time cycles可换算成时间,换算公式为cycles/clockFreq,单位为s。 + +- 0x45为TASK_SWITCH即任务切换事件,当前运行的任务taskid为0x1。 + +- Identity和params的含义需要查看TASK_SWITCH_PARAMS宏定义: + +```c +#define TASK_SWITCH_PARAMS(taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus) \ +taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus +``` + +因为#TYPE#_PARAMS(IDENTITY, parma1...) IDENTITY, ...,所以Identity为taskId(0x0),第一个参数为oldPriority(0x1f) + +> ![](../public_sys-resources/icon-note.gif)**说明** + + > params的个数由menuconfig中Enable Trace Feature --> Record max params配置,默认为3,超出的参数不会被记录,用户应自行合理配置该值。 + + 综上所述,任务由0x1切换到0x0,0x1任务的优先级为0x1f,状态为0x4,0x0任务的优先级为0x0。 + +### 用户态开发流程 +通过在menuconfig配置"Driver->Enable TRACE DRIVER",开启Trace驱动。该配置仅在内核Enable Trace Feature后,才可在Driver的选项中可见。 +1. 打开“/dev/trace”字符文件,进行读写和IOCTL操作; +2. 系统提供用户态的trace命令,该命令位于/bin目录下,cd bin 后可执行如下命令: +- ./trace reset 清除Trace记录 + +- ./trace mask num 设置Trace事件过滤掩码 + +- ./trace start 启动Trace + +- ./trace stop 停止Trace + +- ./trace dump 0/1 格式化输出Trace记录 + +- ./trace read nBytes 读取Trace记录 + +- ./trace write type id params... 写用户态事件 + 如:./trace write 0x1 0x1001 0x2 0x3 则写入一条用户态事件,其事件类型为0x1, id为0x1001,参数有2个,分别是0x2和0x3. + + 用户态命令行的典型使用方法如下: + + ./trace start + + ./trace write 0x1 0x1001 0x2 0x3 + + ./trace stop + + ./trace dump 0 + +### 用户态编程实例 + + 本实例实现如下功能: + + 1. 打开trace字符设备。 + + 2. 设置事件掩码。 + + 3. 启动trace。 + + 4. 写trace事件。 + + 5. 停止trace。 + + 6. 格式化输出trace数据。 + +### 用户态示例代码 +实例代码如下 +```c +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TRACE_IOC_MAGIC 'T' +#define TRACE_START _IO(TRACE_IOC_MAGIC, 1) +#define TRACE_STOP _IO(TRACE_IOC_MAGIC, 2) +#define TRACE_RESET _IO(TRACE_IOC_MAGIC, 3) +#define TRACE_DUMP _IO(TRACE_IOC_MAGIC, 4) +#define TRACE_SET_MASK _IO(TRACE_IOC_MAGIC, 5) +#define TRACE_USR_MAX_PARAMS 3 + +typedef struct { + unsigned int eventType; + uintptr_t identity; + uintptr_t params[TRACE_USR_MAX_PARAMS]; +} UsrEventInfo; + +int main(int argc, char **argv) +{ + size_t mask; + + int fd = open("/dev/trace", O_RDWR); + if (fd == -1) { + printf("Trace open failed.\n"); + exit(EXIT_FAILURE); + } + + ioctl(fd, TRACE_STOP, NULL); + ioctl(fd, TRACE_RESET, NULL); /* clear all events */ + + mask = 0x10000; /* filter kernel events */ + ioctl(fd, TRACE_SET_MASK, mask); + + ioctl(fd, TRACE_START, NULL); /* start trace */ + sleep(1); + + UsrEventInfo info = { + .eventType = 0x1, + .identity = 0x0001, + .params = {1, 2, 3}, + }; + (void)write(fd, &info, sizeof(UsrEventInfo)); + + info.eventType = 0x2; + info.identity = 0x0002; + (void)write(fd, &info, sizeof(UsrEventInfo)); + + ioctl(fd, TRACE_STOP, NULL); + ioctl(fd, TRACE_DUMP, 0); + + close(fd); + return 0; +} +``` +### 用户态结果验证 +输出结果如下: + +```c +*******TraceInfo begin******* +clockFreq = 50000000 +CurEvtIndex = 2 +Index Time(cycles) EventType CurTask Identity params +0 0x366d5e88 0xfffffff1 0x1 0x1 0x1 0x2 0x3 +1 0x366d74ae 0xfffffff2 0x1 0x2 0x1 0x2 0x3 +*******TraceInfo end******* +``` +示例代码中调用了2次write,对应产生2条Trace记录; +用户层事件的EventType高28位固定均为1(即0xfffffff0),仅低4位表示具体的事件类型。 \ No newline at end of file