u-boot 实现 backtrace

u-boot 没有 backtrace() 函数, 调试时想跟踪调用关系, 没 jtag 就比较困难了.

这里通过获取 sp 指针的地址, 分析地址指令打印了栈中的调用地址. 这些地址可以在 u-boot 编译生成的 System.map 中查找, 就可以获得对应函数地址.


extern char __image_copy_start[];
extern char __image_copy_end[];
#define MAX_CALL_STACK_LEVEL 16

// 转换成 System.map 中的地址
static unsigned long convert_address(unsigned long address)
{
    return CONFIG_SYS_TEXT_BASE + (address - (unsigned long)__image_copy_start);
}

static void print_stack_trace(unsigned long *sp)
{
    int count = 0;
    while((unsigned long)sp < CONFIG_SYS_INIT_SP_ADDR) {
        unsigned long address = *sp;
        if ((0 == ((address) & 0x0003))
            && (address >= (unsigned long)__image_copy_start &&
                address <= (unsigned long)__image_copy_end)
            )
        {
            ulong prev = address - 4;
            ulong opcode = *(ulong*)prev;
            // BL or BLX
            if (((opcode & 0x0f000000) == 0x0b000000) ||
                ((opcode & 0x0ffffff0) == 0x012FFFF10))
            {
                printf("%08lx\n", convert_address(address));
                if (++count > MAX_CALL_STACK_LEVEL) {
                    break;
                }
            }
            
        }
        sp++;
    }
    printf("----------------------------\n");
}

void dprint_stack_trace(void)
{
    unsigned long *sp;
    asm volatile("mov %0, sp":"=r"(sp));
    printf("stack trace, sp = %08lx: \n", (unsigned long)sp);
    printf("----------------------------\n");
    print_stack_trace(sp);
}

注: 
    u-boot 运行时, 运行地址有可能重新分配了, 因此显示的地址需要根据  __image_copy_start  转换.
    以上代码最多显示 16 个地址, 如果不够用, 可以修改  MAX_CALL_STACK_LEVEL  定义.
    显示的地址是根据判断所存的数据是不是 bl 或 blx 指令, 不排除堆栈中的数据符合条件, 因此显示的地址中会出现无效的地址, 还需要人为根据函数的调用关系判断过滤无效地址.


以下程序可以用于在 System.map 中查找 dprint_stack_trace() 打印的地址, 并显示函数名.
可存成源文件 taddr.c , 通过以下命令编译生成
 gcc -o taddr ./taddr.c
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#define MAX_NAME_LEN    128
typedef struct _addr_pair_t
{
    unsigned long addr;
    char name[MAX_NAME_LEN];
}addr_pair_t;
static addr_pair_t *pair_buf = NULL;
static int pair_count = 0, pair_buf_len = 0;
static void inc_pair_buf(void)
{
    pair_buf_len += 128;
    pair_buf = realloc(pair_buf, pair_buf_len * sizeof(addr_pair_t));
}
static int load_system_map(void)
{
    FILE *fp = fopen("System.map", "rb");
    if (fp != NULL)
    {
        char *line = NULL;
        size_t s = 0;
        ssize_t c;
        char fmt[128];
        sprintf(fmt,"%%lx %%c %%%ds", MAX_NAME_LEN - 1);
        inc_pair_buf();
        while (!feof(fp))
        {
            unsigned long addr;
            char flag;
            char name[MAX_NAME_LEN];
            c = getline(&line,&s,fp);
            if (c > 0)
            {
                if (3 == sscanf(line,fmt,&addr,&flag,name))
                {
                    if (flag == 'T' || flag == 't')
                    {
                        name[MAX_NAME_LEN - 1] = 0;
                        if (pair_count >= pair_buf_len)
                        {
                            inc_pair_buf();
                        }
                        pair_buf[pair_count].addr = addr;
                        strcpy(pair_buf[pair_count].name,name);
                        pair_count ++;
                    }
                }
            }
        }
        if ( line != NULL) {
            free(line);
        }
        fclose(fp);
    }
    else
    {
        fprintf(stderr, "System.map not exist!\n");
    }
    
}
static void print_addr(unsigned long addr)
{
     
    if ((addr >= pair_buf[0].addr) && (addr <= pair_buf[pair_count - 1].addr))
    {
        int c = 0;
        int top = pair_count;
        int bottom = 0;
        while (1)
        {
            int index = bottom + (top - bottom) / 2;
            
            if ((index == (pair_count - 1)) || 
                (pair_buf[index].addr <= addr && pair_buf[index + 1].addr >= addr))
            {
                printf("%08lx: %s + 0x%lx\n",addr, pair_buf[index].name ,addr - pair_buf[index].addr);
                break;
            }
            if (addr < pair_buf[index].addr)
            {
                top = index;
            }
            else if(addr > pair_buf[index].addr)
            {
                bottom = index;
            }
        }
    }
    
}
#define MAX_ADDR_COUNT    128
void main(int argc, const char ** argv)
{
    load_system_map();
    if (pair_count > 0)
    {
        unsigned long addr_buf[MAX_ADDR_COUNT];
        int addr_count = 0;
        unsigned long addr;
        char str[128];
        int i;
        while (1 == scanf("%lx",&addr))
        {
            addr_buf[addr_count ++] = addr;
            if (addr_count >= MAX_ADDR_COUNT)
            {
                break;
            }
        }
        for (i = 0; i < addr_count; ++i)
        {
            print_addr(addr_buf[i]);
        }
        printf("end!\n");
    }
    free(pair_buf);
}

用法: 在 u-boot 目录下运行 taddr (System.map 需要存在), 将串口中打印的堆栈地址, 包含最后的 '-' 号:
(以下蓝色字为粘贴的内容)

u-boot$ ./taddr

60012194

60012b28

60010e00

600178f0

60006510

60000d34

600049d8

6000048c

60049120

60049120

60049120

60024148

60024148

600262ec

600262d8

600262c4

----------------------------

60012194: sys_boot + 0x344

60012b28: myboot + 0x110

60010e00: board_late_init + 0x8

600178f0: do_go + 0x54

60006510: clbss_l + 0x18

60000d34: invalidate_dcache_range + 0x24

600049d8: rk_pl330_rq + 0xa4

6000048c: irq + 0x2c

60049120: __udivdi3 + 0x18b38

60049120: __udivdi3 + 0x18b38

60049120: __udivdi3 + 0x18b38

60024148: rk_lcdc_load_screen + 0x1cc

60024148: rk_lcdc_load_screen + 0x1cc

600262ec: rk_mipi_dsi_init + 0x36c

600262d8: rk_mipi_dsi_init + 0x358

600262c4: rk_mipi_dsi_init + 0x344

end!

以下正确的调用堆栈为 do_go -> board_late_init-> myboot-> sys_boot, 其它为无效地址


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值