openharmony_kernel_liteos_m/components/backtrace/los_backtrace.c

278 lines
7.4 KiB
C

/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
*
* 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_backtrace.h"
#include "los_task.h"
#include "los_debug.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
#if (LOSCFG_BACKTRACE_TYPE != 0)
#if (LOSCFG_BACKTRACE_TYPE == 1)
#define OS_BACKTRACE_START 2
/* Thumb instruction, so the pc must be an odd number */
#define OS_IS_THUMB_INSTRUCTION(pc) ((pc & 0x1) == 1)
/* BL or BLX instruction flag. */
#define OS_BL_INS_MASK 0xF800
#define OS_BL_INS_HIGH 0xF800
#define OS_BL_INS_LOW 0xF000
#define OS_BLX_INX_MASK 0xFF00
#define OS_BLX_INX 0x4700
#if defined(__ICCARM__) || defined(__CC_ARM)
STATIC INLINE UINTPTR HalSpGet(VOID)
{
UINTPTR sp;
__asm("mov %0, sp" : "=r" (sp));
return sp;
}
STATIC INLINE UINTPTR HalPspGet(VOID)
{
UINTPTR psp;
__asm("mrs %0, psp" : "=r" (psp));
return psp;
}
STATIC INLINE UINTPTR HalMspGet(VOID)
{
UINTPTR msp;
__asm("mrs %0, msp" : "=r" (msp));
return msp;
}
#elif defined(__CLANG_ARM) || defined(__GNUC__)
STATIC INLINE UINTPTR HalSpGet(VOID)
{
UINTPTR sp;
__asm volatile("mov %0, sp" : "=r" (sp));
return sp;
}
STATIC INLINE UINTPTR HalPspGet(VOID)
{
UINTPTR psp;
__asm volatile("mrs %0, psp" : "=r" (psp));
return psp;
}
STATIC INLINE UINTPTR HalMspGet(VOID)
{
UINTPTR msp;
__asm volatile("mrs %0, msp" : "=r" (msp));
return msp;
}
#else
#error Unknown compiler.
#endif
STATIC INLINE BOOL OsInsIsBlOrBlx(UINTPTR addr)
{
UINT16 ins1 = *((UINT16 *)addr);
UINT16 ins2 = *((UINT16 *)(addr + 2)); /* 2: Thumb instruction is two bytes. */
if (((ins2 & OS_BL_INS_MASK) == OS_BL_INS_HIGH) &&
((ins1 & OS_BL_INS_MASK) == OS_BL_INS_LOW)) {
return TRUE;
} else if ((ins2 & OS_BLX_INX_MASK) == OS_BLX_INX) {
return TRUE;
} else {
return FALSE;
}
}
STATIC INLINE VOID OsStackAddrGet(UINTPTR *stackStart, UINTPTR *stackEnd)
{
if (HalSpGet() != HalPspGet()) {
*stackStart = HalMspGet();
*stackEnd = CSTACK_END_ADDR;
} else {
UINT32 taskID = LOS_CurTaskIDGet();
LosTaskCB *taskCB = OS_TCB_FROM_TID(taskID);
*stackStart = HalSpGet();
*stackEnd = (UINTPTR)taskCB->topOfStack + taskCB->stackSize;
}
}
STATIC INLINE UINTPTR OsAddrIsValid(UINTPTR sp)
{
UINTPTR pc;
BOOL ret;
/* The stack space pointed to by the current SP may store the LR,
so need decrease a word to PC. */
pc = *((UINTPTR *)sp) - sizeof(UINTPTR);
if (!OS_IS_THUMB_INSTRUCTION(pc)) {
return 0;
}
/* PC in thumb mode is an odd number, fix the PC address by decreasing one byte. */
pc = *((UINTPTR *)sp) - 1;
ret = OsStackDataIsCodeAddr(pc);
if (ret == FALSE) {
return 0;
}
ret = OsInsIsBlOrBlx(pc - sizeof(UINTPTR));
if (ret == FALSE) {
return 0;
}
return pc;
}
VOID LOS_RecordLR(UINTPTR *LR, UINT32 LRSize, UINT32 jumpCount)
{
if (LR == NULL) {
return;
}
UINTPTR stackStart;
UINTPTR stackEnd;
UINT32 count = 0;
UINT32 index = 0;
UINTPTR sp;
UINTPTR pc;
OsStackAddrGet(&stackStart, &stackEnd);
/* Traverse the stack space and find the LR address. */
for (sp = stackStart; sp < stackEnd; sp += sizeof(UINTPTR)) {
pc = OsAddrIsValid(sp);
if ((pc != 0) && (count < LRSize)) {
if (index++ < jumpCount) {
continue;
}
LR[count] = pc;
count++;
if (count == LRSize) {
break;
}
}
}
if (count < LRSize) {
LR[count] = 0;
}
}
#elif (LOSCFG_BACKTRACE_TYPE == 2)
STATIC INLINE BOOL OsBackTraceFpCheck(UINT32 value);
#define OS_BACKTRACE_START 1
#define OS_RA_OFFSET 4
#define OS_FP_OFFSET 8
#define OS_FP_ALIGN(value) (((UINT32)(value) & (UINT32)(LOSCFG_STACK_POINT_ALIGN_SIZE - 1)) == 0)
#define OS_FP_CHECK(value) (((UINT32)(value) != FP_INIT_VALUE) && OS_FP_ALIGN(value))
STATIC INLINE UINTPTR OsFpGet(VOID)
{
UINTPTR fp = 0;
__asm volatile("mv %0, s0" : "=r"(fp));
dsb();
return fp;
}
VOID LOS_RecordLR(UINTPTR *LR, UINT32 LRSize, UINT32 jumpCount)
{
UINT32 backFp = OsFpGet();
UINT32 tmpFp;
UINT32 backRa;
UINT32 count = 0;
UINT32 index = 0;
if (LR == NULL) {
return;
}
while (OS_FP_CHECK(backFp)) {
tmpFp = backFp;
backRa = *((UINT32 *)(UINTPTR)(tmpFp - OS_RA_OFFSET));
backFp = *((UINT32 *)(UINTPTR)(tmpFp - OS_FP_OFFSET));
if (index++ < jumpCount) {
continue;
}
LR[count] = backRa;
count++;
if ((count == LRSize) || (backFp == tmpFp) ||
(!OsStackDataIsCodeAddr(backRa))) {
break;
}
}
if (count < LRSize) {
LR[count] = 0;
}
}
#else
#error Unknown backtrace type.
#endif
VOID LOS_BackTrace(VOID)
{
UINTPTR LR[BACKTRACE_MAX_DEPTH] = {0};
UINT32 index;
LOS_RecordLR(LR, BACKTRACE_MAX_DEPTH, OS_BACKTRACE_START);
if (LOS_TaskIsRunning()) {
PRINTK("taskName = %s\n", g_losTask.runTask->taskName);
PRINTK("taskID = %u\n", g_losTask.runTask->taskID);
}
PRINTK("----- traceback start -----\r\n");
for (index = 0; index < BACKTRACE_MAX_DEPTH; index++) {
if (LR[index] == 0) {
break;
}
PRINTK("traceback %d -- lr = 0x%x\r\n", index, LR[index]);
}
PRINTK("----- traceback end -----\r\n");
}
VOID LOS_BackTraceInit(VOID)
{
OsBackTraceHookSet(LOS_RecordLR);
}
#endif
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */