sdk-hwV1.3/lichee/melis-v3.0/source/ekernel/arch/riscv/compressed/backtrace.c

883 lines
21 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
*
* Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
* the the People's Republic of China and other countries.
* All Allwinner Technology Co.,Ltd. trademarks are used with permission.
*
* DISCLAIMER
* THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
* IF YOU NEED TO INTEGRATE THIRD PARTYS TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
* IN ALLWINNERSSDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
* ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
* ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
* COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
* YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTYS TECHNOLOGY.
*
*
* THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
* PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
* THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
* OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* IN NO EVENT SHALL ALLWINNER 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 <stddef.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <excep.h>
//#define BACKTRACE_DEBUG 1
extern void putstr(const char *ptr);
#define print_func putstr
#ifndef BACKTRACE_DEBUG
#define backtrace_debug(fmt, ...)
#else
#define backtrace_debug(fmt, ...) \
printf("[%s:%d]:"fmt, __func__, __LINE__, ##__VA_ARGS__)
#endif
#define BT_SCAN_MAX_LIMIT 0x2000
#define BT_LEVEL_LIMIT 64
#define PC2ADDR(pc) ((char *)(((uintptr_t)(pc)) & 0xfffffffe))
#define IS_VALID_TEXT_ADDRESS(pc) backtrace_check_address((uintptr_t)(pc))
int32_t check_virtual_address(uintptr_t vaddr);
int backtrace_check_address(uintptr_t pc)
{
#ifdef CONFIG_SOC_SUN20IW1P1
if (pc < (CONFIG_DRAM_PHYBASE + 64 * 1024))
{
return 0;
}
if (pc > (0xFFFFFFFF))
{
return 0;
}
return check_virtual_address(pc);
#else
if ((pc > CONFIG_DRAM_PHYBASE) && (pc < (CONFIG_DRAM_PHYBASE + CONFIG_DRAM_SIZE)))
{
return 1;
}
return 0;
#endif
}
#define IS_COMPREEES_ADDR(pc) ((uintptr_t)(pc) & 0x1)
#define MAKE_COMPRESSED_ADDR(pc) do{(pc) = (void *)((uintptr_t)(pc) | 0x1);}while(0)
#define insn_length(x) \
(((x) & 0x03) < 0x03 ? 2 : \
((x) & 0x1f) < 0x1f ? 4 : \
((x) & 0x3f) < 0x3f ? 6 : \
8)
#define BITS(x, high, low) ((x) & (((1<<((high)-(low)+1))-1) << (low)))
#define BITS_SHIFT(x, high, low) (((x) >> (low)) & ((1<<((high)-(low)+1))-1))
#define SIGN_EXTEND(val, topbit) (BIT(val, topbit) ? ((val) | (0xffffffff << (topbit))) : (val))
typedef struct
{
switch_ctx_regs_t regs_ctx;
} switch_context_t;
enum task_states
{
TASK_SUSPEND = 0,
TASK_READY,
TASK_INTERRUPTED,
TASK_RUNNING,
};
extern unsigned long riscv_cpu_handle_interrupt(unsigned long scause, unsigned long sepc, unsigned long stval, irq_regs_t *regs);
/*
* convert long to string
*/
static char *long2str(long num, char *str)
{
char index[] = "0123456789ABCDEF";
unsigned long usnum = (unsigned long)num;
str[7] = index[usnum % 16];
usnum /= 16;
str[6] = index[usnum % 16];
usnum /= 16;
str[5] = index[usnum % 16];
usnum /= 16;
str[4] = index[usnum % 16];
usnum /= 16;
str[3] = index[usnum % 16];
usnum /= 16;
str[2] = index[usnum % 16];
usnum /= 16;
str[1] = index[usnum % 16];
usnum /= 16;
str[0] = index[usnum % 16];
usnum /= 16;
return str;
}
static int find_lr_offset(char *LR)
{
char *LR_fixed;
unsigned short ins16;
char backtrace_output_buf[] = "backtrace : 0X \r\n";
int offset = 4;
#ifdef CONFIG_SOC_SUN20IW1
unsigned long *irq_entry = (unsigned long *)&riscv_cpu_handle_interrupt;
#else
unsigned long *irq_entry = NULL;
#endif
irq_entry ++;
LR_fixed = LR;
/* meet irq_entry, it is irq handler exit address. */
if (LR_fixed == PC2ADDR(irq_entry))
{
long2str((long)irq_entry, &backtrace_output_buf[14]);
print_func(backtrace_output_buf);
return 0;
}
if (IS_VALID_TEXT_ADDRESS(LR_fixed - 4) == 0)
{
return 0;
}
ins16 = *(unsigned short *)(LR_fixed - 4);
offset = insn_length(ins16);
long2str((long)LR_fixed - offset, &backtrace_output_buf[14]);
print_func(backtrace_output_buf);
return offset;
}
int riscv_ins32_get_push_lr_framesize(unsigned int inst, int *offset)
{
int ret = -1;
backtrace_debug("inst:0x%x\n", inst);
if ((inst & 0x01FFF07F) == 0x113023)
{
/* sd ra, (offset)sp */
int immed = (inst & 0xF80);
immed >>= 7;
immed |= ((inst & 0xFE000000) >> 25) << 5;
if (((immed >> 11) & 0x01) != 0)
{
immed = 0xFFF - immed + 1;
}
*offset = immed / sizeof(long);
ret = -1;
}
else if ((inst & 0x000FFFFF) == 0x10113)
{
/* addi sp, sp, #imm */
int immed = BITS(inst, 31, 20);
immed >>= 20;
immed &= 0xFFF;
if ((immed >> 11) != 0)
{
immed = 0xFFF - immed + 1;
ret = 0;
}
else
{
ret = -1;
}
}
else if ((inst & 0x000FFFFF) == 0x1011B)
{
/* addiw sp, sp, #imm */
int immed = BITS(inst, 31, 20);
immed >>= 20;
immed &= 0xFFF;
if ((immed >> 11) != 0)
{
immed = 0xFFF - immed + 1;
ret = 0;
}
else
{
ret = -1;
}
}
return ret;
}
static int riscv_ins16_get_push_lr_framesize(unsigned short inst, int *offset)
{
int ret = -1;
backtrace_debug("inst:0x%x: ", inst);
if ((inst & 0xE07E) == 0xE006)
{
/* c.sdsp ra, (offset)sp */
int immed_6_8 = (inst >> 7) & 0x07 ;
int immed_3_5 = (inst >> 10) & 0x07 ;
int immed = immed_6_8 << 6 | immed_3_5 << 3;
*offset = immed / sizeof(long);
backtrace_debug("immed=%d ", immed);
ret = -1;
}
else if ((inst & 0xE07E) == 0xC006)
{
/* c.swsp ra, (offset)sp */
int immed_6_7 = (inst >> 7) & 0x03 ;
int immed_2_5 = (inst >> 9) & 0x0f ;
int immed = immed_6_7 << 6 | immed_2_5 << 2;
*offset = immed / sizeof(long);
backtrace_debug("immed=%d ", immed);
ret = -1;
}
else if ((inst & 0xEF83) == 0x6101)
{
/* c.addi16sp #imm */
int immed_5 = (inst >> 2) & 0x01 ;
int immed_7_8 = (inst >> 3) & 0x3 ;
int immed_6 = (inst >> 5) & 0x1 ;
int immed_4 = (inst >> 6) & 0x1 ;
int immed_9 = (inst >> 12) & 0x1 ;
int immed = immed_5 << 5 | immed_7_8 << 7 | immed_6 << 6 | immed_4 << 4 | immed_9 << 9;
backtrace_debug("immed=%d ", immed);
if ((immed >> 9) != 0)
{
immed = 0x3FF - immed + 1;
ret = 0;
}
else
{
ret = -1;
}
}
else if ((inst & 0xEF03) == 0x101)
{
/* c.addi sp, sp, #imm */
int immed_0_4 = (inst >> 2) & 0x1F ;
int immed_5 = (inst >> 12) & 0x1 ;
int immed = immed_5 << 5 | immed_0_4;
backtrace_debug("immed=%d ", immed);
if ((immed >> 5) != 0)
{
immed = 0x3F - immed + 1;
ret = 0;
}
else
{
ret = -1;
}
}
else if ((inst & 0xEF03) == 0x2101)
{
/* c.addiw sp, #imm */
int immed_0_4 = (inst >> 2) & 0x1F ;
int immed_5 = (inst >> 12) & 0x1 ;
int immed = immed_5 << 5 | immed_0_4;
backtrace_debug("immed=%d ", immed);
if ((immed >> 5) != 0)
{
immed = 0x3F - immed + 1;
ret = 0;
}
else
{
ret = -1;
}
}
#if 0
else if ((inst & 0xE01F) == 0x0)
{
/* c.addi4spn #imm */
return 0;
}
#endif
backtrace_debug("ret = %d\n", ret);
return ret;
}
static int riscv_ins32_backtrace_return_pop(unsigned int inst)
{
backtrace_debug("inst:0x%x\n", inst);
#if 0
/* addi sp, sp, #imm */
if ((inst & 0x000FFFFF) == 0x10113)
{
int immed = BITS(inst, 31, 20);
immed >>= 20;
immed &= 0xFFF;
if ((immed >> 11) != 0)
{
immed = 0xFFF - immed + 1;
}
return immed / sizeof(long);
}
#endif
/* ret */
if ((inst) == 0x00008067)
{
return 0;
}
return -1;
}
static int riscv_ins16_backtrace_return_pop(unsigned short inst)
{
int ret = -1;
if ((inst) == 0x8082)
{
ret = 0;;
}
backtrace_debug("inst:0x%x, ret = %d\n", inst);
return ret;
}
int riscv_ins32_backtrace_stask_push(unsigned int inst)
{
int ret = -1;
if ((inst & 0x000FFFFF) == 0x10113)
{
/* addi sp, sp, #imm */
int immed = BITS(inst, 31, 20);
immed >>= 20;
immed &= 0xFFF;
if ((immed >> 11) != 0)
{
immed = 0xFFF - immed + 1;
ret = immed / sizeof(long);
}
else
{
ret = -1;
}
}
else if ((inst & 0x000FFFFF) == 0x1011B)
{
/* addiw sp, sp, #imm */
int immed = BITS(inst, 31, 20);
immed >>= 20;
immed &= 0xFFF;
if ((immed >> 11) != 0)
{
immed = 0xFFF - immed + 1;
ret = immed / sizeof(long);
}
else
{
ret = -1;
}
}
backtrace_debug("inst:0x%x, ret = %d\n", inst, ret);
return ret;
}
static int riscv_ins16_backtrace_stask_push(unsigned int inst)
{
int ret = -1;
if ((inst & 0xEF83) == 0x6101)
{
/* c.addi16sp #imm */
int immed_4 = (inst >> 6) & 0x01;
int immed_5 = (inst >> 2) & 0x01;
int immed_6 = (inst >> 5) & 0x01;
int immed_7_8 = (inst >> 3) & 0x3;
int immed_9 = (inst >> 12) & 0x1;
int immed = (immed_4 << 4) | (immed_5 << 5) | (immed_6 << 6) | (immed_7_8 << 7) | (immed_9 << 9);
if ((immed >> 9) != 0)
{
immed = 0x3FF - immed + 1;
ret = immed / sizeof(long);
}
else
{
ret = -1;
}
}
else if ((inst & 0xEF03) == 0x101)
{
/* c.addi sp, sp, #imm */
int immed_5 = (inst >> 12) & 0x01;
int immed_0_4 = (inst >> 2) & 0x1F;
int immed = (immed_0_4) | (immed_5 << 5);
if ((immed >> 5) != 0)
{
immed = 0x3F - immed + 1;
ret = immed / sizeof(long);
}
else
{
ret = -1;
}
}
else if ((inst & 0xEF03) == 0x2101)
{
/* c.addiw sp, #imm */
int immed_5 = (inst >> 12) & 0x01;
int immed_0_4 = (inst >> 2) & 0x1F;
int immed = (immed_0_4) | (immed_5 << 5);
if ((immed >> 5) != 0)
{
immed = 0x3F - immed + 1;
ret = immed / sizeof(long);
}
else
{
ret = -1;
}
}
#if 0
else if ((inst & 0xE01F) == 0x0)
{
/* c.addi4spn #imm */
return 0;
}
#endif
backtrace_debug("inst:0x%x, ret = %d\n", inst, ret);
return ret;
}
static int riscv_ins32_backtrace_stack_pop(unsigned int inst)
{
int ret = -1;
/* addi sp, sp, #imm */
if ((inst & 0x000FFFFF) == 0x10113)
{
int immed = BITS(inst, 31, 20);
immed >>= 20;
immed &= 0xFFF;
if ((immed >> 11) != 0)
{
ret = -1;
}
else
{
immed = 0xFFF - immed + 1;
ret = immed / sizeof(long);
}
}
else if ((inst & 0x000FFFFF) == 0x1011B)
{
/* addiw sp, sp, #imm */
int immed = BITS(inst, 31, 20);
immed >>= 20;
immed &= 0xFFF;
if ((immed >> 11) != 0)
{
ret = -1;
}
else
{
immed = 0xFFF - immed + 1;
ret = immed / sizeof(long);
}
}
backtrace_debug("inst:0x%x\n", inst);
return ret;
}
static int riscv_ins16_backtrace_stack_pop(unsigned short inst)
{
int ret = -1;
backtrace_debug("inst:0x%x\n", inst);
if ((inst & 0xEF83) == 0x6101)
{
/* c.addi16sp #imm */
int immed_4 = (inst >> 6) & 0x01;
int immed_5 = (inst >> 2) & 0x01;
int immed_6 = (inst >> 5) & 0x01;
int immed_7_8 = (inst >> 3) & 0x3;
int immed_9 = (inst >> 12) & 0x1;
int immed = (immed_4 << 4) | (immed_5 << 5) | (immed_6 << 6) | (immed_7_8 << 7) | (immed_9 << 9);
if ((immed >> 9) != 0)
{
immed = 0x3FF - immed + 1;
}
ret = immed / sizeof(long);
}
else if ((inst & 0xEF03) == 0x101)
{
/* c.addi sp, sp, #imm */
int immed_5 = (inst >> 12) & 0x01;
int immed_0_4 = (inst >> 2) & 0x1F;
int immed = (immed_0_4) | (immed_5 << 5);
if ((immed >> 5) != 0)
{
ret = -1;
}
else
{
immed = 0x3F - immed + 1;
}
ret = immed / sizeof(long);
}
else if ((inst & 0xEF03) == 0x2101)
{
/* c.addiw sp, #imm */
int immed_5 = (inst >> 12) & 0x01;
int immed_0_4 = (inst >> 2) & 0x1F;
int immed = (immed_0_4) | (immed_5 << 5);
if ((immed >> 5) != 0)
{
ret = -1;
}
else
{
immed = 0x3F - immed + 1;
}
ret = immed / sizeof(long);
}
#if 0
else if ((inst & 0xE01F) == 0x0)
{
/* c.addi4spn #imm */
return 0;
}
#endif
backtrace_debug("inst:0x%x\n", inst);
return ret;
}
static int riscv_backtrace_from_stack(long **pSP, char **pPC)
{
char *parse_addr = NULL;
long *SP = *pSP;
char *PC = *pPC;
char *LR;
int i;
int temp;
int framesize = 0;
int offset = 0;
unsigned int ins32 = 0;
unsigned short ins16 = 0;
unsigned short ins16_h = 0;
unsigned short ins16_l = 0;
char backtrace_output_buf[] = "backtrace : 0X \r\n";
for (i = 2; i < BT_SCAN_MAX_LIMIT; i += 2)
{
int result = 0;
parse_addr = PC - i;
if (IS_VALID_TEXT_ADDRESS(parse_addr) == 0)
{
print_func("backtrace fail!\r\n");
return -1;
}
ins16_h = *(unsigned short *)parse_addr;
if (IS_VALID_TEXT_ADDRESS(parse_addr - 2) == 0)
{
print_func("backtrace fail!\r\n");
return -1;
}
ins16_l = *(unsigned short *)(parse_addr - 2);
backtrace_debug("parse_addr = %p:", parse_addr);
if (insn_length(ins16_l) == 4)
{
ins32 = (ins16_h << 16) | ins16_l;
result = riscv_ins32_get_push_lr_framesize(ins32, &offset);
i += 2;
}
else
{
ins16 = ins16_h;
result = riscv_ins16_get_push_lr_framesize(ins16, &offset);
}
if (result >= 0)
{
break;
}
}
parse_addr = PC - i;
backtrace_debug("i = %d, parse_addr = %p, PC = %p, offset = %d\n", i, parse_addr, PC, offset);
if (i == BT_SCAN_MAX_LIMIT)
{
print_func("backtrace fail!\r\n");
return -1;
}
for (i = 0; parse_addr + i < PC; i += 2)
{
if (IS_VALID_TEXT_ADDRESS(parse_addr + i) == 0)
{
print_func("backtrace fail!\r\n");
return -1;
}
ins16_l = *(unsigned short *)(parse_addr + i);
if (IS_VALID_TEXT_ADDRESS(parse_addr + i + 2) == 0)
{
print_func("backtrace fail!\r\n");
return -1;
}
ins16_h = *(unsigned short *)(parse_addr + i + 2);
if (insn_length(ins16_l) == 4 || ins16_l == 0)
{
ins32 = (ins16_h << 16) | ins16_l;
temp = riscv_ins32_backtrace_stask_push(ins32);
i += 2;
}
else
{
ins16 = ins16_l;
temp = riscv_ins16_backtrace_stask_push(ins16);
}
if (temp >= 0)
{
framesize += temp;
}
}
backtrace_debug("i = %d, framesize = %d, SP = %p\n", i, framesize, SP);
if (!offset)
{
return -1;
}
if (IS_VALID_TEXT_ADDRESS(SP + offset) == 0)
print_func("backtrace : invalid lr\r\n");
LR = (char *) * (SP + offset);
if (IS_VALID_TEXT_ADDRESS(LR) == 0)
{
print_func("backtrace : invalid lr\r\n");
return -1;
}
*pSP = SP + framesize;
offset = find_lr_offset(LR);
*pPC = LR - offset;
backtrace_debug("*pSP = %p, offset = %d, *pPC = %p\n", *pSP, offset, *pPC);
return offset == 0 ? 1 : 0;
}
static int riscv_backtrace_from_lr(long **pSP, char **pPC, char *LR)
{
long *SP = *pSP;
char *PC = *pPC;
char *parse_addr = NULL;
int i;
int temp;
int framesize = 0;
int offset;
int result = 0;
unsigned int ins32 = 0;
unsigned short ins16 = 0;
unsigned short ins16_h = 0;
unsigned short ins16_l = 0;
if (IS_VALID_TEXT_ADDRESS(PC) == 0)
{
if (IS_VALID_TEXT_ADDRESS(LR) == 0)
{
print_func("backtrace : invalid lr\r\n");
return -1;
}
offset = find_lr_offset(LR);
PC = LR - offset;
*pPC = PC;
return offset == 0 ? 1 : 0;
}
for (i = 0; i < BT_SCAN_MAX_LIMIT; i += 2)
{
parse_addr = PC + i;
if (IS_VALID_TEXT_ADDRESS(parse_addr) == 0)
{
print_func("backtrace fail!\r\n");
return -1;
}
if (IS_VALID_TEXT_ADDRESS(parse_addr + 2) == 0)
{
print_func("backtrace fail!\r\n");
return -1;
}
ins16_l = *(unsigned short *)parse_addr;
ins16_h = *(unsigned short *)(parse_addr + 2);
if (insn_length(ins16_l) == 4 || ins16_l == 0)
{
ins32 = (ins16_h << 16) | ins16_l;
result = riscv_ins32_backtrace_return_pop(ins32);
i += 2;
parse_addr -= 4;
}
else
{
ins16 = ins16_l;
result = riscv_ins16_backtrace_return_pop(ins16);
parse_addr -= 2;
}
if (result >= 0)
{
break;
}
}
backtrace_debug("i = %d, parse_addr = %p, PC = %p, framesize = %d\n", i, parse_addr, PC, framesize);
framesize = result;
if (i == BT_SCAN_MAX_LIMIT)
{
print_func("Backtrace fail!\r\n");
return -1;
}
for (i = 0; parse_addr - i >= PC; i += 2)
{
if (IS_VALID_TEXT_ADDRESS(parse_addr - i) == 0)
{
print_func("Backtrace fail!\r\n");
return -1;
}
if (IS_VALID_TEXT_ADDRESS(parse_addr - i - 2) == 0)
{
print_func("Backtrace fail!\r\n");
return -1;
}
ins16_l = *(unsigned short *)(parse_addr - i - 2);
ins16_h = *(unsigned short *)(parse_addr - i);
if (insn_length(ins16_l) == 4)
{
ins32 = (ins16_h << 16) | ins16_l;
temp = riscv_ins32_backtrace_stack_pop(ins32);
i += 2;
}
else
{
ins16 = ins16_h;
temp = riscv_ins16_backtrace_stack_pop(ins16);
}
if (temp >= 0)
{
backtrace_debug("framesize add %d\n", temp);
framesize += temp;
}
}
backtrace_debug("i = %d, parse_addr = %p, PC = %p, SP = %p, framesize = %d\n", i, parse_addr, PC, framesize);
if (IS_VALID_TEXT_ADDRESS(LR) == 0)
{
print_func("backtrace : invalid lr\r\n");
return -1;
}
*pSP = SP + framesize;
offset = find_lr_offset(LR);
*pPC = LR - offset;
backtrace_debug("*pSP = %p, offset = %d, *pPC = %p\n", *pSP, offset, *pPC);
return offset == 0 ? 1 : 0;
}
static int backtrace_from_stack(long **pSP, char **pPC)
{
if (IS_VALID_TEXT_ADDRESS(*pPC) == 0)
{
return -1;
}
return riscv_backtrace_from_stack(pSP, pPC);
}
static int backtrace_from_lr(long **pSP, char **pPC, char *LR)
{
return riscv_backtrace_from_lr(pSP, pPC, LR);
}
/*
* backtrace function
*/
int backtrace(void)
{
char *PC = NULL;
long *SP = NULL;
char *saved_pc;
long *saved_sp;
unsigned long CPSR = 0;
int level;
int ret;
char *LR = NULL;
char backtrace_output_buf[] = "backtrace : 0X \r\n";
__asm__ volatile("mv %0, sp\n" : "=r"(SP));
__asm__ volatile("auipc %0, 0\n" : "=r"(PC));
__asm__ volatile("mv %0, ra\n" : "=r"(LR));
if (SP == NULL)
return 0;
ret = -1;
level = 0;
long2str((long)PC, &backtrace_output_buf[14]);
print_func(backtrace_output_buf);
for (level = 1; level < BT_LEVEL_LIMIT; level++)
{
ret = backtrace_from_stack(&SP, &PC);
if (ret != 0)
break;
}
out:
return level < 0 ? 0 : level ;
}