Trace调测旨在帮助开发者获取内核的运行流程,各个模块、任务的执行顺序,从而可以辅助开发者定位一些时序问题或者了解内核的代码运行过程。
内核提供一套Hook框架,将Hook点预埋在各个模块的主要流程中, 在内核启动初期完成Trace功能的初始化,并注册Trace的处理函数到Hook中。
当系统触发到一个Hook点时,Trace模块会对输入信息进行封装,添加Trace帧头信息,包含事件类型、运行的cpuid、运行的任务id、运行的相对时间戳等信息;
Trace提供2种工作模式,离线模式和在线模式。
离线模式会将trace frame记录到预先申请好的循环buffer中。如果循环buffer记录的frame过多则可能出现翻转,会覆盖之前的记录,故保持记录的信息始终是最新的信息。Trace循环buffer的数据可以通过shell命令导出进行详细分析,导出信息已按照时间戳信息完成排序。
在线模式需要配合IDE使用,实时将trace frame记录发送给IDE,IDE端进行解析并可视化展示。
OpenHarmony LiteOS-M内核的Trace模块提供下面几种功能,接口详细信息可以查看API参考。
表1 Trace模块接口说明
功能分类 | 接口名 |
---|---|
启停控制 | - LOS_TraceStart:启动Trace - LOS_TraceStop:停止Trace |
操作Trace记录的数据 | - LOS_TraceRecordDump:输出Trace缓冲区数据 - LOS_TraceRecordGet:获取Trace缓冲区的首地址 - LOS_TraceReset:清除Trace缓冲区中的事件 |
过滤Trace记录的数据 | LOS_TraceEventMaskSet:设置事件掩码,仅记录某些模块的事件 |
屏蔽某些中断号事件 | LOS_TraceHwiFilterHookReg:注册过滤特定中断号事件的钩子函数 |
插桩函数 | LOS_TRACE_EASY:简易插桩 LOS_TRACE:标准插桩 |
/* 假设自定义读操作为type: 1, 写操作为type: 2 */
LOS_TRACE_EASY(1, fd, flag, size); /* 在读fd文件的适当位置插入 */
LOS_TRACE_EASY(2, fd, flag, size); /* 在写fd文件的适当位置插入 */
* IDENTITY和Params的类型及含义同简易插桩。
* 示例:
1. 定义FS模块的类型,即FS模块的事件掩码
/* 在enum LOS_TRACE_MASK中定义, 定义规范为TRACE_#MOD#_FLAG, #MOD#表示模块名 */
TRACE_FS_FLAG = 0x4000
2. 定义FS模块的具体事件类型
/* 定义规范为#TYPE# = TRACE_#MOD#_FLAG | NUMBER, */
FS_READ = TRACE_FS_FLAG | 0; /* 读文件 */
FS_WRITE = TRACE_FS_FLAG | 1; /* 写文件 */
3. 定义事件参数
/* 定义规范为#TYPE#_PARAMS(IDENTITY, parma1...) IDENTITY, ... */
#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); /* 读文件操作的代码桩 */
说明: 预置的Trace事件及参数均可以通过上述方式进行裁剪,参数详见kernel\include\los_trace.h。
BOOL Example_HwiNumFilter(UINT32 hwiNum)
{
if ((hwiNum == TIMER_INT) || (hwiNum == DMA_INT)) {
return TRUE;
}
return FALSE;
}
LOS_TraceHwiFilterHookReg(Example_HwiNumFilter);
则当中断号为TIMER_INT 或者DMA_INT时,不记录中断事件。
开发流程
开启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命令,对应关系如下
编程实例
本实例实现如下功能:
示例代码
示例代码如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleTraceTest。
#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 ExampleTraceTest(VOID){
UINT32 ret;
TSK_INIT_PARAM_S traceTestTask = { 0 };
/* 创建用于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;
}
结果验证
输出结果如下:
***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***
根据实际运行环境,上文中的数据会有差异,非固定结果
输出的事件信息包括:发生时间、事件类型、事件发生在哪个任务中、事件操作的主体对象、事件的其他参数。
下面以序号为0的输出项为例,进行说明。
Index Time(cycles) EventType CurTask Identity params
0 0x366d5e88 0x45 0x1 0x0 0x1f 0x4
#define TASK_SWITCH_PARAMS(taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus) \
taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus
因为#TYPE#_PARAMS(IDENTITY, parma1…) IDENTITY, …,所以Identity为taskId(0x0),第一个参数为oldPriority(0x1f)
说明: params的个数由LOSCFG_TRACE_FRAME_MAX_PARAMS配置,默认为3,超出的参数不会被记录,用户应自行合理配置该值。
综上所述,任务由0x1切换到0x0,0x1任务的优先级为0x1f,状态为0x4,0x0任务的优先级为0x0。