los_trace.c 16.3 KB
Newer Older
1 2 3 4 5 6 7 8
/*!
 * @file    los_trace.c
 * @brief
 * @link kernel-small-debug-trace http://weharmonyos.com/openharmony/zh-cn/device-dev/kernel/kernel-small-debug-trace.html @endlink
   @verbatim
	基本概念
		Trace调测旨在帮助开发者获取内核的运行流程,各个模块、任务的执行顺序,从而可以辅助开发者定位一些时序问题
		或者了解内核的代码运行过程。
9 10 11 12 13 14 15 16 17
	相关宏
		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
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
	运行机制
		内核提供一套Hook框架,将Hook点预埋在各个模块的主要流程中, 在内核启动初期完成Trace功能的初始化,
		并注册Trace的处理函数到Hook中。

		当系统触发到一个Hook点时,Trace模块会对输入信息进行封装,添加Trace帧头信息,包含事件类型、
		运行的cpuid、运行的任务id、运行的相对时间戳等信息;

		Trace提供2种工作模式,离线模式和在线模式。
			在线模式需要配合IDE使用,实时将trace frame记录发送给IDE,IDE端进行解析并可视化展示。
			离线模式会将trace frame记录到预先申请好的循环buffer中。如果循环buffer记录的frame过多则可能出现翻转,
			会覆盖之前的记录,故保持记录的信息始终是最新的信息。Trace循环buffer的数据可以通过shell命令导出进行详细分析,
			导出信息已按照时间戳信息完成排序。
   @endverbatim 
 * @image html https://gitee.com/weharmonyos/resources/raw/master/80/trace.png  
 * @version 
33
 * @author  weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
34 35
 * @date    2021-11-20
 */
36
/*
37
 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
38
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "los_trace_pri.h"
68 69 70
#include "trace_pipeline.h"
#include "los_memory.h"
#include "los_config.h"
71
#include "securec.h"
72
#include "trace_cnv.h"
73
#include "los_init.h"
74 75 76 77 78
#include "los_process.h"

#ifdef LOSCFG_KERNEL_SMP
#include "los_mp.h"
#endif
79

80 81 82 83
#ifdef LOSCFG_SHELL
#include "shcmd.h"
#include "shell.h"
#endif
84

85 86
LITE_OS_SEC_BSS STATIC UINT32 g_traceEventCount;
LITE_OS_SEC_BSS STATIC volatile enum TraceState g_traceState = TRACE_UNINIT;
87 88
LITE_OS_SEC_DATA_INIT STATIC volatile BOOL g_enableTrace = FALSE; ///< trace开关
LITE_OS_SEC_BSS STATIC UINT32 g_traceMask = TRACE_DEFAULT_MASK;	///< 全局变量设置事件掩码,仅记录某些模块的事件
89

90 91
TRACE_EVENT_HOOK g_traceEventHook = NULL;	///< 事件钩子函数
TRACE_DUMP_HOOK g_traceDumpHook = NULL;	///< 输出缓冲区数据
92

93 94 95
#ifdef LOSCFG_TRACE_CONTROL_AGENT
LITE_OS_SEC_BSS STATIC UINT32 g_traceTaskId;
#endif
96

97 98
#define EVENT_MASK            0xFFFFFFF0
#define MIN(x, y)             ((x) < (y) ? (x) : (y))
99

100
LITE_OS_SEC_BSS STATIC TRACE_HWI_FILTER_HOOK g_traceHwiFilterHook = NULL; ///< 用于跟踪硬中断过滤的钩子函数
101

102 103 104
#ifdef LOSCFG_KERNEL_SMP
LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_traceSpin);
#endif
105

106
STATIC_INLINE BOOL OsTraceHwiFilter(UINT32 hwiNum)
107
{
108 109 110 111
    BOOL ret = ((hwiNum == NUM_HAL_INTERRUPT_UART) || (hwiNum == OS_TICK_INT_NUM));
#ifdef LOSCFG_KERNEL_SMP
    ret |= (hwiNum == LOS_MP_IPI_SCHEDULE);
#endif
112 113
    if (g_traceHwiFilterHook != NULL) {
        ret |= g_traceHwiFilterHook(hwiNum);
114 115
    }
    return ret;
116
}
117

118 119
STATIC VOID OsTraceSetFrame(TraceEventFrame *frame, UINT32 eventType, UINTPTR identity, const UINTPTR *params,
    UINT16 paramCount)
120
{
121
    INT32 i;
122 123
    UINT32 intSave;

124 125 126 127 128
    (VOID)memset_s(frame, sizeof(TraceEventFrame), 0, sizeof(TraceEventFrame));

    if (paramCount > LOSCFG_TRACE_FRAME_MAX_PARAMS) {
        paramCount = LOSCFG_TRACE_FRAME_MAX_PARAMS;
    }
129 130

    TRACE_LOCK(intSave);
131 132 133 134 135 136 137 138 139
    frame->curTask   = OsTraceGetMaskTid(LOS_CurTaskIDGet());
    frame->curPid    = LOS_GetCurrProcessID();
    frame->identity  = identity;
    frame->curTime   = HalClockGetCycles();
    frame->eventType = eventType;

#ifdef LOSCFG_TRACE_FRAME_CORE_MSG
    frame->core.cpuId      = ArchCurrCpuid();
    frame->core.hwiActive  = OS_INT_ACTIVE ? TRUE : FALSE;
140
    frame->core.taskLockCnt = MIN(OsPercpuGet()->taskLockCnt, 0xF); /* taskLockCnt is 4 bits, max value = 0xF */
141 142
    frame->core.paramCount = paramCount;
#endif
143

144 145
#ifdef LOS_TRACE_FRAME_LR
    /* Get the linkreg from stack fp and storage to frame */
146
    LOS_RecordLR(frame->linkReg, LOS_TRACE_LR_RECORD, LOS_TRACE_LR_RECORD, LOS_TRACE_LR_IGNORE);
147
#endif
148

149 150 151 152
#ifdef LOSCFG_TRACE_FRAME_EVENT_COUNT
    frame->eventCount = g_traceEventCount;
    g_traceEventCount++;
#endif
153
    TRACE_UNLOCK(intSave);
154

155 156 157
    for (i = 0; i < paramCount; i++) {
        frame->params[i] = params[i];
    }
158 159
}

160
VOID OsTraceSetObj(ObjData *obj, const LosTaskCB *tcb)
161
{
162 163 164 165 166
    errno_t ret;
    (VOID)memset_s(obj, sizeof(ObjData), 0, sizeof(ObjData));

    obj->id   = OsTraceGetMaskTid(tcb->taskID);
    obj->prio = tcb->priority;
167

168 169 170
    ret = strncpy_s(obj->name, LOSCFG_TRACE_OBJ_MAX_NAME_SIZE, tcb->taskName, LOSCFG_TRACE_OBJ_MAX_NAME_SIZE - 1);
    if (ret != EOK) {
        TRACE_ERROR("Task name copy failed!\n");
171
    }
172
}
173

174 175 176 177 178 179 180 181 182 183 184
/*!
 * @brief OsTraceHook	
 * 事件统一处理函数
 * @param eventType	
 * @param identity	
 * @param paramCount	
 * @param params	
 * @return	
 *
 * @see
 */
185 186
VOID OsTraceHook(UINT32 eventType, UINTPTR identity, const UINTPTR *params, UINT16 paramCount)
{
187 188
    TraceEventFrame frame;//离线和在线模式下, trace数据的保存和传送以帧为单位
    if ((eventType == TASK_CREATE) || (eventType == TASK_PRIOSET)) {//创建任务和设置任务优先级
189
        OsTraceObjAdd(eventType, identity); /* handle important obj info, these can not be filtered */
190 191
    }

192
    if ((g_enableTrace == TRUE) && (eventType & g_traceMask)) {//使能跟踪模块且事件未屏蔽
193
        UINTPTR id = identity;
194 195
        if (TRACE_GET_MODE_FLAG(eventType) == TRACE_HWI_FLAG) {//关于硬中断的事件
            if (OsTraceHwiFilter(identity)) {//检查中断号是否过滤掉了,注意:中断控制器本身是可以屏蔽中断号的
196
                return;
197
            }
198 199 200
        } else if (TRACE_GET_MODE_FLAG(eventType) == TRACE_TASK_FLAG) {//关于任务的事件
            id = OsTraceGetMaskTid(identity);//获取任务ID
        } else if (eventType == MEM_INFO_REQ) {//内存信息事件
201
            LOS_MEM_POOL_STATUS status;
202 203
            LOS_MemInfoGet((VOID *)identity, &status);//获取内存各项信息
            LOS_TRACE(MEM_INFO, identity, status.totalUsedSize, status.totalFreeSize);//打印信息
204
            return;
205
        }
206

207 208
        OsTraceSetFrame(&frame, eventType, id, params, paramCount);//创建帧数据
        OsTraceWriteOrSendEvent(&frame);//保存(离线模式下)或者发送(在线模式下)帧数据
209 210
    }
}
211

212
BOOL OsTraceIsEnable(VOID)
213
{
214
    return g_enableTrace;
215
}
216
/// 初始化事件处理函数
217
STATIC VOID OsTraceHookInstall(VOID)
218
{
219 220 221 222
    g_traceEventHook = OsTraceHook;
#ifdef LOSCFG_RECORDER_MODE_OFFLINE
    g_traceDumpHook = OsTraceRecordDump;
#endif
223 224
}

225 226
#ifdef LOSCFG_TRACE_CONTROL_AGENT
STATIC BOOL OsTraceCmdIsValid(const TraceClientCmd *msg)
227
{
228
    return ((msg->end == TRACE_CMD_END_CHAR) && (msg->cmd < TRACE_CMD_MAX_CODE));
229
}
230

231
STATIC VOID OsTraceCmdHandle(const TraceClientCmd *msg)
232
{
233 234 235 236 237 238
    if (!OsTraceCmdIsValid(msg)) {
        return;
    }

    switch (msg->cmd) {
        case TRACE_CMD_START:
鸿蒙内核源码分析's avatar
鸿蒙内核源码分析 已提交
239
            LOS_TraceStart();//启动trace
240 241
            break;
        case TRACE_CMD_STOP:
鸿蒙内核源码分析's avatar
鸿蒙内核源码分析 已提交
242
            LOS_TraceStop();//关闭trace
243
            break;
鸿蒙内核源码分析's avatar
鸿蒙内核源码分析 已提交
244
        case TRACE_CMD_SET_EVENT_MASK://设置事件掩码
245 246 247 248 249 250 251 252
            /* 4 params(UINT8) composition the mask(UINT32) */
            LOS_TraceEventMaskSet(TRACE_MASK_COMBINE(msg->param1, msg->param2, msg->param3, msg->param4));
            break;
        case TRACE_CMD_RECODE_DUMP:
            LOS_TraceRecordDump(TRUE);
            break;
        default:
            break;
253
    }
254
}
鸿蒙内核源码分析's avatar
鸿蒙内核源码分析 已提交
255
///< trace任务的入口函数,接收串口数据
256
VOID TraceAgent(VOID)
257 258
{
    UINT32 ret;
259
    TraceClientCmd msg;
260

261 262
    while (1) {
        (VOID)memset_s(&msg, sizeof(TraceClientCmd), 0, sizeof(TraceClientCmd));
鸿蒙内核源码分析's avatar
鸿蒙内核源码分析 已提交
263
        ret = OsTraceDataWait();//等待数据到来
264 265
        if (ret == LOS_OK) {
            OsTraceDataRecv((UINT8 *)&msg, sizeof(TraceClientCmd), 0);
鸿蒙内核源码分析's avatar
鸿蒙内核源码分析 已提交
266
            OsTraceCmdHandle(&msg);//处理数据
267 268 269
        }
    }
}
270

271 272 273 274 275 276 277
/*!
 * @brief OsCreateTraceAgentTask 创建trace任务	
 * 
 * @return	
 *
 * @see
 */
278
STATIC UINT32 OsCreateTraceAgentTask(VOID)
279
{
280 281 282 283
    UINT32 ret;
    TSK_INIT_PARAM_S taskInitParam;

    (VOID)memset_s((VOID *)(&taskInitParam), sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
284 285 286 287
    taskInitParam.pfnTaskEntry = (TSK_ENTRY_FUNC)TraceAgent;	//任务入口函数
    taskInitParam.usTaskPrio = LOSCFG_TRACE_TASK_PRIORITY;	//任务优先级 2
    taskInitParam.pcName = "TraceAgent";	//任务名称
    taskInitParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; //内核栈大小 16K
288
#ifdef LOSCFG_KERNEL_SMP
289
    taskInitParam.usCpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid());//指定为当前CPU执行
290
#endif
291
    ret = LOS_TaskCreate(&g_traceTaskId, &taskInitParam);//创建任务并产生调度
292
    return ret;
293
}
294
#endif
295
/// 跟踪模块初始化
296
STATIC UINT32 OsTraceInit(VOID)
297
{
298
    UINT32 ret;
299

300 301 302 303
    if (g_traceState != TRACE_UNINIT) {
        TRACE_ERROR("trace has been initialized already, the current state is :%d\n", g_traceState);
        ret = LOS_ERRNO_TRACE_ERROR_STATUS;
        goto LOS_ERREND;
304 305
    }

306 307
#ifdef LOSCFG_TRACE_CLIENT_INTERACT //使能与Trace IDE (dev tools)的交互,包括数据可视化和流程控制
    ret = OsTracePipelineInit();//在线模式(管道模式)的初始化
308 309
    if (ret != LOS_OK) {
        goto LOS_ERREND;
310
    }
311
#endif
312

313
#ifdef LOSCFG_TRACE_CONTROL_AGENT //trace任务代理开关,所谓代理是创建专门的任务来处理 trace
314 315 316 317
    ret = OsCreateTraceAgentTask();
    if (ret != LOS_OK) {
        TRACE_ERROR("trace init create agentTask error :0x%x\n", ret);
        goto LOS_ERREND;
318
    }
319
#endif
320

321 322 323
#ifdef LOSCFG_RECORDER_MODE_OFFLINE //trace离线模式开关
//离线模式会将trace frame记录到预先申请好的循环buffer中。如果循环buffer记录的frame过多则可能出现翻转,
//会覆盖之前的记录,故保持记录的信息始终是最新的信息。
324
    ret = OsTraceBufInit(LOSCFG_TRACE_BUFFER_SIZE); //离线模式下buf 大小,这个大小决定了装多少 ObjData 和 TraceEventFrame
325
    if (ret != LOS_OK) {
326 327 328 329
#ifdef LOSCFG_TRACE_CONTROL_AGENT
        (VOID)LOS_TaskDelete(g_traceTaskId);
#endif
        goto LOS_ERREND;
330
    }
331
#endif
332

333
    OsTraceHookInstall();//安装HOOK框架
334
    OsTraceCnvInit();//将事件处理函数注册到HOOK框架
335 336 337 338 339 340 341 342 343 344 345 346 347

    g_traceEventCount = 0;

#ifdef LOSCFG_RECORDER_MODE_ONLINE  /* Wait trace client to start trace */
    g_enableTrace = FALSE;
    g_traceState = TRACE_INITED;
#else
    g_enableTrace = TRUE;
    g_traceState = TRACE_STARTED;
#endif
    return LOS_OK;
LOS_ERREND:
    return ret;
348
}
349
/// 启动Trace
350
UINT32 LOS_TraceStart(VOID)
351
{
352 353
    UINT32 intSave;
    UINT32 ret = LOS_OK;
354

355 356 357
    TRACE_LOCK(intSave);
    if (g_traceState == TRACE_STARTED) {
        goto START_END;
358 359
    }

360
    if (g_traceState == TRACE_UNINIT) {//必须初始化好
361 362 363
        TRACE_ERROR("trace not inited, be sure LOS_TraceInit excute success\n");
        ret = LOS_ERRNO_TRACE_ERROR_STATUS;
        goto START_END;
364 365
    }

366
    OsTraceNotifyStart();//通知系统开始
367

368 369
    g_enableTrace = TRUE; //使能trace功能
    g_traceState = TRACE_STARTED;//设置状态,已开始
370

371
    TRACE_UNLOCK(intSave);
372
    LOS_TRACE(MEM_INFO_REQ, m_aucSysMem0);//输出日志
373 374 375 376 377
    return ret;
START_END:
    TRACE_UNLOCK(intSave);
    return ret;
}
378
/// 停止Trace(跟踪)
379 380 381
VOID LOS_TraceStop(VOID)
{
    UINT32 intSave;
382

383 384 385
    TRACE_LOCK(intSave);
    if (g_traceState != TRACE_STARTED) {
        goto STOP_END;
386
    }
387 388 389 390 391 392

    g_enableTrace = FALSE;
    g_traceState = TRACE_STOPED;
    OsTraceNotifyStop();
STOP_END:
    TRACE_UNLOCK(intSave);
393
}
394
/// 设置事件掩码,仅记录某些模块的事件
395
VOID LOS_TraceEventMaskSet(UINT32 mask)
396
{
397 398
    g_traceMask = mask & EVENT_MASK;
}
399
/// 输出Trace缓冲区数据
400 401 402 403 404
VOID LOS_TraceRecordDump(BOOL toClient)
{
    if (g_traceState != TRACE_STOPED) {
        TRACE_ERROR("trace dump must after trace stopped , the current state is : %d\n", g_traceState);
        return;
405
    }
406 407
    OsTraceRecordDump(toClient);
}
408
/// 获取Trace缓冲区的首地址
409 410 411
OfflineHead *LOS_TraceRecordGet(VOID)
{
    return OsTraceRecordGet();
412
}
413
/// 清除Trace缓冲区中的事件
414
VOID LOS_TraceReset(VOID)
415
{
416 417 418
    if (g_traceState == TRACE_UNINIT) {
        TRACE_ERROR("trace not inited, be sure LOS_TraceInit excute success\n");
        return;
419
    }
420 421

    OsTraceReset();
422
}
423
/// 注册过滤特定中断号事件的钩子函数
424
VOID LOS_TraceHwiFilterHookReg(TRACE_HWI_FILTER_HOOK hook)
425
{
426 427 428
    UINT32 intSave;

    TRACE_LOCK(intSave);
429
    g_traceHwiFilterHook = hook;// 注册全局钩子函数
430 431 432 433
    TRACE_UNLOCK(intSave);
}

#ifdef LOSCFG_SHELL
434
/// 通过shell命令 设置事件掩码,仅记录某些模块的事件
435 436 437 438 439 440 441 442 443 444 445 446
LITE_OS_SEC_TEXT_MINOR UINT32 OsShellCmdTraceSetMask(INT32 argc, const CHAR **argv)
{
    size_t mask;
    CHAR *endPtr = NULL;

    if (argc >= 2) { /* 2:Just as number of parameters */
        PRINTK("\nUsage: trace_mask or trace_mask ID\n");
        return OS_ERROR;
    }

    if (argc == 0) {
        mask = TRACE_DEFAULT_MASK;
447
    } else {
448
        mask = strtoul(argv[0], &endPtr, 0);
449
    }
450
    LOS_TraceEventMaskSet((UINT32)mask);
451 452
    return LOS_OK;
}
453

454
LITE_OS_SEC_TEXT_MINOR UINT32 OsShellCmdTraceDump(INT32 argc, const CHAR **argv)
455
{
456 457 458 459 460 461 462 463 464 465
    BOOL toClient;
    CHAR *endPtr = NULL;

    if (argc >= 2) { /* 2:Just as number of parameters */
        PRINTK("\nUsage: trace_dump or trace_dump [1/0]\n");
        return OS_ERROR;
    }

    if (argc == 0) {
        toClient = FALSE;
466
    } else {
467
        toClient = strtoul(argv[0], &endPtr, 0) != 0 ? TRUE : FALSE;
468
    }
469
    LOS_TraceRecordDump(toClient);
470 471 472
    return LOS_OK;
}

473
SHELLCMD_ENTRY(tracestart_shellcmd,   CMD_TYPE_EX, "trace_start", 0, (CmdCallBackFunc)LOS_TraceStart);//通过shell 启动trace
474
SHELLCMD_ENTRY(tracestop_shellcmd,    CMD_TYPE_EX, "trace_stop",  0, (CmdCallBackFunc)LOS_TraceStop);
475
SHELLCMD_ENTRY(tracesetmask_shellcmd, CMD_TYPE_EX, "trace_mask",  1, (CmdCallBackFunc)OsShellCmdTraceSetMask);//设置事件掩码,仅记录某些模块的事件
476 477
SHELLCMD_ENTRY(tracereset_shellcmd,   CMD_TYPE_EX, "trace_reset", 0, (CmdCallBackFunc)LOS_TraceReset);
SHELLCMD_ENTRY(tracedump_shellcmd,    CMD_TYPE_EX, "trace_dump", 1, (CmdCallBackFunc)OsShellCmdTraceDump);
478 479
#endif

480
LOS_MODULE_INIT(OsTraceInit, LOS_INIT_LEVEL_KMOD_EXTENDED);