ARM 架构 dump_stack 实现分析(2.0 调用时序)

本文深入解析Linux内核中堆栈跟踪过程,包括dump_stack、unwind_backtrace、dump_backtrace_entry等核心函数的作用,以及LR寄存器与堆栈保存的重要性。通过代码注释和函数流程图,详细解释了如何获取并打印函数调用栈,同时强调了配置KALLSYMS以查找函数地址对应函数名和偏移量的重要性。

看下具体调用时序:

void dump_stack(void)
{
	dump_backtrace(NULL, NULL);
}


 

dump_stack
    -->dump_backtrace
            -->unwind_backtrace 
                - ->dump_backtrace_entry
                     -->printk(%pS)
                         - ->kallsyms_lookup


 

void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk)
{
	struct stackframe frame;
        ......
        //得到 FP ,SP ,LR ,及 PC的值
        //其中真正用的是: FP及PC
	if (regs) {
		frame.fp = regs->ARM_fp;
		frame.sp = regs->ARM_sp;
		frame.lr = regs->ARM_lr;
		/* PC might be corrupted, use LR in that case. */
		frame.pc = kernel_text_address(regs->ARM_pc)
			 ? regs->ARM_pc : regs->ARM_lr;
	} 
	while (1) {
		int urc;
		unsigned long where = frame.pc;
                //设置上一帧的FP,PC,SP等值
		urc = unwind_frame(&frame);
		if (urc < 0)
			break;
                //打印出对应函数指针的函数名及偏移量
		dump_backtrace_entry(where, frame.pc, frame.sp - 4);
	}
}


 

// 堆栈的大小,FP指针不能超过此值
#define THREAD_SIZE		8192
int notrace unwind_frame(struct stackframe *frame)
{
	unsigned long high, low;
	unsigned long fp = frame->fp;

	//此时,堆栈地址是一步一步往高地址爬的
	low = frame->sp;
	high = ALIGN(low, THREAD_SIZE);

	//确保帧指针是合法的
	if (fp < (low + 12) || fp + 4 >= high)
		return -EINVAL;
         //从堆栈中取出之前备份的值,设置成上一帧的值
         // 为什么是 -12 -8 - 4
         //因为函数调用前大概会执行:
      // ldm  sp, {fp, sp, pc}   把当前fp, sp, pc的值保存到堆栈中
	frame->fp = *(unsigned long *)(fp - 12);
	frame->sp = *(unsigned long *)(fp - 8);
	frame->pc = *(unsigned long *)(fp - 4);

	return 0;
}



大家可以想想,为什么有了LR寄存器,还要保存pc值在堆栈中。
个人LR保存的是调用函数指令,下面的代码地址,不能反映当时真实的函数调用
配合下面这张函数调用堆栈图,可能会更清晰些:

当然编译内核的时候,要打开 CONFIG_KALLSYMS ,这样才能查找找函数地址对应的函数名及偏移量
void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame)
{
#ifdef CONFIG_KALLSYMS
        //记住%pS是关键  printk 与 普通printf最大的不同
        //惭愧啊,现在才知道此选项
	printk("symbol<%08lx>] (%pS) from [<%08lx>] (%pS)\n", where, (void *)where, from, (void *)from);
#else
	printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
#endif
	if (in_exception_text(where))
		dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
}


 

[ 14.391261][T1400506] modprobe: Call trace: [ 14.391788][T1400506] modprobe: dump_backtrace+0xf8/0x148 [ 14.392477][T1400506] modprobe: show_stack+0x18/0x24 [ 14.393112][T1400506] modprobe: dump_stack_lvl+0x60/0x7c [ 14.393790][T1400506] modprobe: dump_stack+0x18/0x38 [ 14.394425][T1400506] modprobe: mrdump_common_die+0x268/0x47c [mrdump] [ 14.395273][T1400506] modprobe: ipanic_die+0x20/0x34 [mrdump] [ 14.396015][T1400506] modprobe: notify_die+0x80/0xd8 [ 14.396652][T1400506] modprobe: die+0xac/0x2e4 [ 14.397221][T1400506] modprobe: bug_handler+0x48/0xec [ 14.397867][T1400506] modprobe: brk_handler+0x94/0x144 [ 14.398524][T1400506] modprobe: do_debug_exception+0xa4/0x148 [ 14.399258][T1400506] modprobe: el1_dbg+0x64/0x7c [ 14.399861][T1400506] modprobe: el1h_64_sync_handler+0x3c/0x90 [ 14.400605][T1400506] modprobe: el1h_64_sync+0x68/0x6c [ 14.401261][T1400506] modprobe: __might_resched+0x1fc/0x2e8 [ 14.401973][T1400506] modprobe: __might_sleep+0x48/0x7c [ 14.402641][T1400506] modprobe: synchronize_irq+0x44/0xc4 [ 14.403330][T1400506] modprobe: disable_irq+0x70/0xa0 [ 14.403976][T1400506] modprobe: cst0xx_probe+0x238/0x464 [hynitron_cst816d] [ 14.404867][T1400506] modprobe: i2c_device_probe+0x240/0x308 [ 14.405590][T1400506] modprobe: really_probe+0x1c8/0x518 [ 14.406270][T1400506] modprobe: __driver_probe_device+0xa4/0x168 [ 14.407036][T1400506] modprobe: driver_probe_device+0x44/0x234 [ 14.407782][T1400506] modprobe: __driver_attach+0x118/0x264 [ 14.408493][T1400506] modprobe: bus_for_each_dev+0x98/0xe8 [ 14.409194][T1400506] modprobe: driver_attach+0x24/0x34 [ 14.409863][T1400506] modprobe: bus_add_driver+0x110/0x220 [ 14.410564][T1400506] modprobe: driver_register+0x7c/0x1b8 [ 14.411266][T1400506] modprobe: i2c_register_driver+0x44/0xc0 [ 14.412000][T1400506] modprobe: cst0xx_ts_probe+0x280/0x620 [hynitron_cst816d] [ 14.412921][T1400506] modprobe: platform_probe+0xc0/0xec [ 14.413601][T1400506] modprobe: really_probe+0x1c8/0x518 [ 14.414281][T1400506] modprobe: __driver_probe_device+0xa4/0x168 [ 14.415047][T1400506] modprobe: driver_probe_device+0x44/0x234 [ 14.415792][T1400506] modprobe: __driver_attach+0x118/0x264 [ 14.416505][T1400506] modprobe: bus_for_each_dev+0x98/0xe8 [ 14.417205][T1400506] modprobe: driver_attach+0x24/0x34 [ 14.417874][T1400506] modprobe: bus_add_driver+0x110/0x220 [ 14.418575][T1400506] modprobe: driver_register+0x7c/0x1b8 [ 14.419276][T1400506] modprobe: __platform_driver_register+0x24/0x34 [ 14.420085][T1400506] modprobe: init_module+0x48/0xfd8 [hynitron_cst816d] [ 14.420952][T1400506] modprobe: do_one_initcall+0x140/0x378 [ 14.421663][T1400506] modprobe: do_init_module+0x48/0x220 [ 14.422353][T1400506] modprobe: load_module+0x16c4/0x1c30 [ 14.423043][T1400506] modprobe: __arm64_sys_finit_module+0xc4/0x13c [ 14.423841][T1400506] modprobe: invoke_syscall+0x58/0x118 [ 14.424531][T1400506] modprobe: el0_svc_common+0xb4/0xf4 [ 14.425210][T1400506] modprobe: do_el0_svc+0x24/0x80 [ 14.425847][T1400506] modprobe: el0_svc+0x2c/0x90 [ 14.426450][T1400506] modprobe: el0t_64_sync_handler+0x68/0xb4 [ 14.427194][T1400506] modprobe: el0t_64_sync+0x1a4/0x1a8
最新发布
11-25
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值