RISC-V backtrace实现原理

start_kernel
    addi sp,sp,-16                      ---① 分配栈帧sp=sp-16,sp指向栈顶
    sw ra,12(sp)                        ---② 将ra存储到sp+12的位置
    sw s0,8(sp)

    xxxxxx
    jal ra, 6000dba2 <backtrace>
    xxxxxx

    lw s0,8(sp)
    lw ra,12(sp)
    addi sp,sp,16

_backtrace
    for(level = 1; level < BT_LEVEL_LIMIT; level++)
        backtrace_from_stack(&SP, &PC, &LR, print_func, 1);
            for(i = 2; i < BT_SCAN_MAX_LIMIT; i += 2)
                riscv_ins16_get_push_lr_framesize(ins16, &offset);
            //获取到分配栈帧的位置,地址为parse_addr,该位置也是函数的起始位置,如上的①
            //同时也获取到ra的在栈中的偏移位置offset,通过类似sw ra,12(sp)计算得出。

            for(i = 0; parse_addr + i < PC; i += 2)
                riscv_ins16_backtrace_stask_push(ins16);
            //计算函数的起始位置到当前运行的PC位置,使用了多少栈空间。存储到framesize中。

            LR  = (char *) * (SP + offset);
            //计算LR的值,如上②,RA是存储到SP+offset的位置。

            *pSP   = SP + framesize;
            //*pSP即为栈底位置,即为上一个函数的栈顶,为下一次遍历做准备

            offset = find_lr_offset(LR, print_func);
                print_backtrace(print_func, (unsigned long)LR_fixed - offset);//打印位置
            *pPC   = LR - offset;
            //因为RA存储的是跳转指令的下一条指令,所以需要减去一条指令就得到跳转的位置。
            //到这里*pSP、*pPC就更新到上一个函数的值,再接着继续遍历即可。