内核中dump_stack的实现原理(1) —— 栈回溯
环境
概述
正文
原理
#include <stdio.h> int func3(int b)
{
int a = ;
printf("a = %d\n", a + b);
return a;
} int func2(int d)
{
int b; b = func3(d);
printf("b = %d\n", b + d);
return b;
} int func1(int a)
{
int d; d = func2(a);
printf("d = %d\n", d);
return d;
} int main(int argc, const char *argv[])
{
int a = ; func1(a);
return ;
}
然后我们对其进行编译和反汇编:
aarch64-linux-gnu-gcc a.c -o main
aarch64-linux-gnu-objdump -D main > main.S
000000000040055c <func3>:
40055c: a9bd7bfd stp x29, x30, [sp, #-]!
: 910003fd mov x29, sp
: b9001fa0 str w0, [x29, #]
: mov w0, #0xa // #
40056c: b9002fa0 str w0, [x29, #]
: b9402fa1 ldr w1, [x29, #]
: b9401fa0 ldr w0, [x29, #]
: 0b000021 add w1, w1, w0
40057c: adrp x0, <_init-0x3e8>
: 911b8000 add x0, x0, #0x6e0
: 97ffffb3 bl <printf@plt>
: b9402fa0 ldr w0, [x29, #]
40058c: a8c37bfd ldp x29, x30, [sp], #
: d65f03c0 ret <func2>:
: a9bd7bfd stp x29, x30, [sp, #-]!
: 910003fd mov x29, sp
40059c: b9001fa0 str w0, [x29, #]
4005a0: b9401fa0 ldr w0, [x29, #]
4005a4: 97ffffee bl 40055c <func3>
4005a8: b9002fa0 str w0, [x29, #]
4005ac: b9402fa1 ldr w1, [x29, #]
4005b0: b9401fa0 ldr w0, [x29, #]
4005b4: 0b000021 add w1, w1, w0
4005b8: adrp x0, <_init-0x3e8>
4005bc: 911ba000 add x0, x0, #0x6e8
4005c0: 97ffffa4 bl <printf@plt>
4005c4: b9402fa0 ldr w0, [x29, #]
4005c8: a8c37bfd ldp x29, x30, [sp], #
4005cc: d65f03c0 ret 00000000004005d0 <func1>:
4005d0: a9bd7bfd stp x29, x30, [sp, #-]!
4005d4: 910003fd mov x29, sp
4005d8: b9001fa0 str w0, [x29, #]
4005dc: b9401fa0 ldr w0, [x29, #]
4005e0: 97ffffed bl <func2>
4005e4: b9002fa0 str w0, [x29, #]
4005e8: adrp x0, <_init-0x3e8>
4005ec: 911bc000 add x0, x0, #0x6f0
4005f0: b9402fa1 ldr w1, [x29, #]
4005f4: 97ffff97 bl <printf@plt>
4005f8: b9402fa0 ldr w0, [x29, #]
4005fc: a8c37bfd ldp x29, x30, [sp], #
: d65f03c0 ret <main>:
: a9bd7bfd stp x29, x30, [sp, #-]!
: 910003fd mov x29, sp
40060c: b9001fa0 str w0, [x29, #]
: f9000ba1 str x1, [x29, #]
: mov w0, #0xa // #
: b9002fa0 str w0, [x29, #]
40061c: b9402fa0 ldr w0, [x29, #]
: 97ffffec bl 4005d0 <func1>
: mov w0, #0x0 // #
: a8c37bfd ldp x29, x30, [sp], #
40062c: d65f03c0 ret
使用API进行栈回溯
#include <execinfo.h>
int backtrace(void **buffer, int size);
char **backtrace_symbols(void *const *buffer, int size);
在func3使用上面的两个函数回溯一下:
#include <execinfo.h> int func3(int b)
{
int a = , n, i;
void *buffer[];
char **strings; n = backtrace(buffer, );
strings = backtrace_symbols(buffer, n); for (i = ; i < n; i++)
printf("%s\n", strings[i]); printf("a = %\n", a + b); return a;
}
编译:
aarch64-linux-gnu-gcc -funwind-tables -rdynamic -O0 -g a.c -o main
[root@aarch64 ~]# ./main
./main(func3+0x20) [0x400a1c]
./main(func2+0x14) [0x400aa4]
./main(func1+0x14) [0x400ae0]
./main(main+0x20) [0x400b20]
/lib/libc.so.(__libc_start_main+0xec) [0xffffb8d732c8]
a =
b =
d =
使用内敛汇编进行栈回溯
#include <execinfo.h> typedef struct {
unsigned long x29;
unsigned long x30;
} node_t; void back(void)
{
node_t *addr; printf("\nBackstrace not use api:\n"); asm volatile("mov %0, x29\n\t":"=r"(addr)::);
while(addr && addr->x30 && addr->x29) {
printf("\t%p\n", addr->x30);
addr = (node_t *)addr->x29;
}
} int func3(int b)
{
int a = , n, i;
void *buffer[];
char **strings; n = backtrace(buffer, );
strings = backtrace_symbols(buffer, n); printf("\nBackstrace use api:\n");
for (i = ; i < n; i++)
printf("\t%s\n", strings[i]); back(); printf("a = %d\n", a + b);
return a;
}
[root@aarch64 ~]# ./main Backstrace use api:
./main(func3+0x20) [0x400ab4]
./main(func2+0x14) [0x400b54]
./main(func1+0x14) [0x400b90]
./main(main+0x20) [0x400bd0]
/lib/libc.so.(__libc_start_main+0xec) [0xffff993d12c8] Backstrace not use api:
0x400b1c
0x400b54
0x400b90
0x400bd0
0xffff993d12c8
a =
b =
d =
使用GCC的内部函数进行栈回溯
#include <execinfo.h> typedef struct {
unsigned long x29;
unsigned long x30;
} node_t; void back(void)
{
node_t *addr; printf("\nBackstrace not use api:\n"); asm volatile("mov %0, x29\n\t":"=r"(addr)::);
while(addr && addr->x30 && addr->x29) {
printf("\t%p\n", addr->x30);
addr = (node_t *)addr->x29;
}
} void back_builtin(void)
{
node_t *addr; printf("\nBackstrace using builtin func:\n"); printf("the return address of back_builtin: %p\n",
__builtin_return_address());
addr = __builtin_frame_address();
while(addr && addr->x30 && addr->x29) {
printf("\t%p\n", addr->x30);
addr = (node_t *)addr->x29;
}
} int func3(int b)
{
int a = , n, i;
void *buffer[];
char **strings; n = backtrace(buffer, );
strings = backtrace_symbols(buffer, n); printf("\nBackstrace use api:\n");
for (i = ; i < n; i++)
printf("\t%s\n", strings[i]); back(); back_builtin(); printf("a = %d\n", a + b);
return a;
}
[root@aarch64 ~]# ./main Backstrace use api:
./main(func3+0x20) [0x400b74]
./main(func2+0x14) [0x400c18]
./main(func1+0x14) [0x400c54]
./main(main+0x20) [0x400c94]
/lib/libc.so.(__libc_start_main+0xec) [0xffffb92632c8] Backstrace not use api:
0x400bdc
0x400c18
0x400c54
0x400c94
0xffffb92632c8 Backstrace using builtin func:
the return address of back_builtin: 0x400be0
0x400be0
0x400c18
0x400c54
0x400c94
0xffffb92632c8
a =
b =
d =
不使用API进行栈回溯的缺点是只能打印出地址,但是却无法打印出具体的符号名字。
内核的栈回溯
[ 20.419376] ------------[ cut here ]------------
[ 20.420062] WARNING: CPU: PID: at /home/pengdonglin/disk_ext/Qemu/aarch64/demo_driver/demo.c: demo_init+0xc/0x1000 [demo]
[ 20.420546] Modules linked in: demo(O+)
[ 20.421004] CPU: PID: Comm: insmod Tainted: G O 4.14.-ga06114e5 #
[ 20.421371] Hardware name: linux,dummy-virt (DT)
[ 20.421600] task: ffff80000831af00 task.stack: ffff00000aef8000
[ 20.421899] PC is at demo_init+0xc/0x1000 [demo]
[ 20.422142] LR is at do_one_initcall+0x44/0x130
[ 20.422360] pc : [<ffff000000c5500c>] lr : [<ffff000008083cc4>] pstate:
[ 20.422657] sp : ffff00000aefbc40
[ 20.422853] x29: ffff00000aefbc40 x28: ffff000000c520d0
[ 20.423140] x27: 00000000014000c0 x26: ffff80000836d800
[ 20.423367] x25: ffff0000081b3848 x24: ffff800079596080
[ 20.423570] x23: x22: ffff800079596200
[ 20.423787] x21: ffff80000831af00 x20:
[ 20.423989] x19: ffff000000c55000 x18:
[ 20.424189] x17: x16:
[ 20.424390] x15: ffffffffffffffff x14: ffffffffffffffff
[ 20.424591] x13: ffffffffffffffff x12: 00000000a49cf051
[ 20.424791] x11: ffff80000831b7f0 x10:
[ 20.425019] x9 : ffff00000aefba10 x8 : ffff0000091fc000
[ 20.425227] x7 : ffff000008246790 x6 :
[ 20.425428] x5 : x4 :
[ 20.425629] x3 : x2 :
[ 20.425828] x1 : ffff80000831af00 x0 :
[ 20.426106]
[ 20.426106] X1: 0xffff80000831ae80:
[ 20.426292] ae80
[ 20.426774] aea0
[ 20.427177] aec0
[ 20.427568] aee0
[ 20.427973] af00 ffffffff ffffffff
[ 20.428389] af20 0aef8000 ffff0000
[ 20.428899] af40 fffeee58 7c082f00 ffff8000
[ 20.429415] af60 08c60b88 ffff0000
[ 20.429962]
[ 20.429962] X11: 0xffff80000831b770:
[ 20.430199] b770
[ 20.430706] b790 00000a50 082c2790 ffff0000 080816dc ffff0000
[ 20.431223] b7b0 00000a4f 00000a50 080ed6bc ffff0000 08081f18 ffff0000
[ 20.431743] b7d0 000009b8
[ 20.432262] b7f0 08175f18 ffff0000 09198b88 ffff0000
[ 20.432779] b810 000c0001 683da59d 64dfae01 08175c7c ffff0000 09198b88 ffff0000
[ 20.433297] b830 000c0001 4ccad968 18f18caf 08175e48 ffff0000
[ 20.433813] b850 09198cb8 ffff0000 000c0005 e69cf65f 335c3458
[ 20.434334]
[ 20.434334] X21: 0xffff80000831ae80:
[ 20.434561] ae80
[ 20.435073] aea0
[ 20.435598] aec0
[ 20.436228] aee0
[ 20.436756] af00 ffffffff ffffffff
[ 20.437271] af20 0aef8000 ffff0000
[ 20.437789] af40 fffeee58 7c082f00 ffff8000
[ 20.438466] af60 08c60b88 ffff0000
[ 20.439138]
[ 20.439138] X22: 0xffff800079596180:
[ 20.439386] 746f6e2e 6e672e65 75622e75 2d646c69
[ 20.440006] 61a0
[ 20.440529] 61c0
[ 20.441098] 61e0
[ 20.441608] ffff8000 6f6d6564 00c55000 ffff0000
[ 20.442065]
[ 20.442529]
[ 20.443078]
[ 20.443652]
[ 20.443652] X24: 0xffff800079596000:
[ 20.443856] 7274732e
[ 20.444321]
[ 20.444809]
[ 20.445273]
[ 20.445707] ffff8000 7a34a700 ffff8000
[ 20.446208] 60a0 09e94940 ffff0000 00c51000 ffff0000
[ 20.446775] 60c0 081b3720 ffff0000
[ 20.447339] 60e0
[ 20.447919]
[ 20.447919] X26: 0xffff80000836d780:
[ 20.448134] d780
[ 20.448637] d7a0
[ 20.449144] d7c0
[ 20.449641] d7e0
[ 20.450148] d800 08eee848 ffff0000 0836dca8 ffff8000
[ 20.450646] d820 0000000d 7a34a700 ffff8000
[ 20.451149] d840 09e94938 ffff0000 081b3848 ffff0000
[ 20.451652] d860 7a34a700 ffff8000
[ 20.452138]
[ 20.452334] Call trace:
[ 20.452522] Exception stack(0xffff00000aefbb00 to 0xffff00000aefbc40)
[ 20.452823] bb00: ffff80000831af00
[ 20.453076] bb20: ffff000008246790
[ 20.453321] bb40: ffff0000091fc000 ffff00000aefba10 ffff80000831b7f0
[ 20.453544] bb60: 00000000a49cf051 ffffffffffffffff ffffffffffffffff ffffffffffffffff
[ 20.453762] bb80: ffff000000c55000
[ 20.453979] bba0: ffff80000831af00 ffff800079596200
[ 20.454199] bbc0: ffff800079596080 ffff0000081b3848 ffff80000836d800 00000000014000c0
[ 20.454419] bbe0: ffff000000c520d0 ffff00000aefbc40 ffff000008083cc4 ffff00000aefbc40
[ 20.454639] bc00: ffff000000c5500c ffff0000081b951c
[ 20.454880] bc20: 0000ffffffffffff ffff00000818d420 ffff00000aefbc40 ffff000000c5500c
[ 20.455190] [<ffff000000c5500c>] demo_init+0xc/0x1000 [demo]
[ 20.455419] [<ffff000008083cc4>] do_one_initcall+0x44/0x130
[ 20.455645] [<ffff0000081b9548>] do_init_module+0x64/0x1d4
[ 20.455859] [<ffff0000081b809c>] load_module+0x1e1c/0x24f0
[ 20.456096] [<ffff0000081b88f0>] SyS_init_module+0x180/0x218
[ 20.456299] Exception stack(0xffff00000aefbec0 to 0xffff00000aefc000)
[ 20.456522] bec0: 0000ffffa3ec4010 000000000003b850 00000000005cad68
[ 20.456776] bee0: 00000000ffffffff 000000001bd34680 00000000005e2bf8
[ 20.457026] bf00: 00000000005e4dc0
[ 20.457288] bf20: 00000000005e9000 00000000005e3000
[ 20.457539] bf40:
[ 20.457791] bf60: 0000fffff0d52878 0000fffff0d52880
[ 20.458052] bf80: 0000fffff0d52888 00000000005cad68 00000000005b9b24
[ 20.458295] bfa0: 0000fffff0d52720 000000000045bc74 0000fffff0d52440
[ 20.458546] bfc0: 0000000000409bf8 0000ffffa3ec4010
[ 20.458776] bfe0:
[ 20.459002] [<ffff000008083ac0>] el0_svc_naked+0x34/0x38
[ 20.459211] ---[ end trace 1d225ceb44d51601 ]---
分析:
include/asm-generic/bug.h: #define WARN_ON(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN(); \
unlikely(__ret_warn_on); \
})
当给WARN_ON传递非0的参数时,就会调用__WARN()打印栈:
// 定义在include/asm-generic/bug.h中
#define __WARN() __WARN_TAINT(TAINT_WARN) // TAINT_WARN是9
#define __WARN_TAINT(taint) __WARN_FLAGS(BUGFLAG_TAINT(taint)) // 定义在arch/arm64/include/asm/bug.h中
#define __WARN_FLAGS(flags) __BUG_FLAGS(BUGFLAG_WARNING|(flags)) // flags是0x900
#define __BUG_FLAGS(flags) \
asm volatile (__stringify(ASM_BUG_FLAGS(flags))); // flags是0x901 #define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line)
#define __BUGVERBOSE_LOCATION(file, line) \
.pushsection .rodata.str,"aMS",@progbits,; \
: .string file; \
.popsection; \
\
.long 2b - 0b; \
.short line; #define __BUG_ENTRY(flags) \
.pushsection __bug_table,"aw"; \
.align ; \
: .long 1f - 0b; \
_BUGVERBOSE_LOCATION(__FILE__, __LINE__) \
.short flags; \
.popsection; \
: #define ASM_BUG_FLAGS(flags) \
__BUG_ENTRY(flags) \
brk BUG_BRK_IMM
第29行,在__bug_table中增加一个段,其中存放的数据(如brk指令的地址,以及文件名和行号等)在异常处理程序中会被访问,可以参考find_bug函数
/*
* #imm16 values used for BRK instruction generation
* Allowed values for kgdb are 0x400 - 0x7ff
* 0x100: for triggering a fault on purpose (reserved)
* 0x400: for dynamic BRK instruction
* 0x401: for compile time BRK instruction
* 0x800: kernel-mode BUG() and WARN() traps
*/
#define FAULT_BRK_IMM 0x100
#define KGDB_DYN_DBG_BRK_IMM 0x400
#define KGDB_COMPILED_DBG_BRK_IMM 0x401
#define BUG_BRK_IMM 0x800
也就是在WARN_ON的最后会执行brk 0x800,这条指令会触发一个debug同步异常:
/*
* EL1 mode handlers.
*/
.align
el1_sync:
kernel_entry
mrs x1, esr_el1 // read the syndrome register
lsr x24, x1, #ESR_ELx_EC_SHIFT // exception class
cmp x24, #ESR_ELx_EC_DABT_CUR // data abort in EL1
b.eq el1_da
cmp x24, #ESR_ELx_EC_IABT_CUR // instruction abort in EL1
b.eq el1_ia
cmp x24, #ESR_ELx_EC_SYS64 // configurable trap
b.eq el1_undef
cmp x24, #ESR_ELx_EC_SP_ALIGN // stack alignment exception
b.eq el1_sp_pc
cmp x24, #ESR_ELx_EC_PC_ALIGN // pc alignment exception
b.eq el1_sp_pc
cmp x24, #ESR_ELx_EC_UNKNOWN // unknown exception in EL1
b.eq el1_undef
cmp x24, #ESR_ELx_EC_BREAKPT_CUR // debug exception in EL1
b.ge el1_dbg
b el1_inv ... ... el1_dbg:
/*
* Debug exception handling
*/
cmp x24, #ESR_ELx_EC_BRK64 // if BRK64
cinc x24, x24, eq // set bit ''
tbz x24, #, el1_inv // EL1 only
mrs x0, far_el1
mov x2, sp // struct pt_regs
bl do_debug_exception
kernel_exit
/*
* This struct defines the way the registers are stored on the stack during an
* exception. Note that sizeof(struct pt_regs) has to be a multiple of 16 (for
* stack alignment). struct user_pt_regs must form a prefix of struct pt_regs.
*/
struct pt_regs {
union {
struct user_pt_regs user_regs;
struct {
u64 regs[]; // 存放发生异常时X0~X30的值
u64 sp; // 存放struct pt_regs的首地址
u64 pc; // 用于存放发生异常的指令的地址(后面在bug_handler中会加4来修正,就可以从发生异常的指令的下一条指令继续执行)
u64 pstate; // 用于存放异常发生时的PSTATE的状态
};
};
u64 orig_x0;
#ifdef __AARCH64EB__
u32 unused2;
s32 syscallno;
#else
s32 syscallno;
u32 unused2;
#endif u64 orig_addr_limit; // 用于备份异常进程的thread_info.addr_limit
u64 unused; // maintain 16 byte alignment
u64 stackframe[]; // 用于存放x29和异常指令的地址
};
mrs x22, elr_el1
mrs x23, spsr_el1
stp lr, x21, [sp, #S_LR] /*
* In order to be able to dump the contents of struct pt_regs at the
* time the exception was taken (in case we attempt to walk the call
* stack later), chain it together with the stack frames.
*/
.if \el ==
stp xzr, xzr, [sp, #S_STACKFRAME]
.else
stp x29, x22, [sp, #S_STACKFRAME]
.endif
add x29, sp, #S_STACKFRAME
下面分析do_debug_exception:
asmlinkage int __exception do_debug_exception(unsigned long addr,
unsigned int esr,
struct pt_regs *regs)
{
const struct fault_info *inf = debug_fault_info + DBG_ESR_EVT(esr);
struct siginfo info;
int rv; /*
* Tell lockdep we disabled irqs in entry.S. Do nothing if they were
* already disabled to preserve the last enabled/disabled addresses.
*/
if (interrupts_enabled(regs))
trace_hardirqs_off(); if (user_mode(regs) && instruction_pointer(regs) > TASK_SIZE)
arm64_apply_bp_hardening(); if (!inf->fn(addr, esr, regs)) {
rv = ;
} else {
pr_alert("Unhandled debug exception: %s (0x%08x) at 0x%016lx\n",
inf->name, esr, addr); info.si_signo = inf->sig;
info.si_errno = ;
info.si_code = inf->code;
info.si_addr = (void __user *)addr;
arm64_notify_die("", regs, &info, );
rv = ;
} if (interrupts_enabled(regs))
trace_hardirqs_on(); return rv;
}
/*
* Initial handler for AArch64 BRK exceptions
* This handler only used until debug_traps_init().
*/
int __init early_brk64(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
return bug_handler(regs, esr) != DBG_HOOK_HANDLED;
}
static int bug_handler(struct pt_regs *regs, unsigned int esr)
{
if (user_mode(regs))
return DBG_HOOK_ERROR; switch (report_bug(regs->pc, regs)) {
case BUG_TRAP_TYPE_BUG:
die("Oops - BUG", regs, );
break; case BUG_TRAP_TYPE_WARN:
break; default:
/* unknown/unrecognised bug trap type */
return DBG_HOOK_ERROR;
} /* If thread survives, skip over the BUG instruction and continue: */
regs->pc += AARCH64_INSN_SIZE; /* skip BRK and resume */
return DBG_HOOK_HANDLED;
}
enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs)
{
struct bug_entry *bug;
const char *file;
unsigned line, warning, once, done; if (!is_valid_bugaddr(bugaddr))
return BUG_TRAP_TYPE_NONE; bug = find_bug(bugaddr);
if (!bug)
return BUG_TRAP_TYPE_NONE; file = NULL;
line = ;
warning = ; if (bug) {
#ifdef CONFIG_DEBUG_BUGVERBOSE
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
file = bug->file;
#else
file = (const char *)bug + bug->file_disp;
#endif
line = bug->line;
#endif
warning = (bug->flags & BUGFLAG_WARNING) != ;
once = (bug->flags & BUGFLAG_ONCE) != ;
done = (bug->flags & BUGFLAG_DONE) != ; if (warning && once) {
if (done)
return BUG_TRAP_TYPE_WARN; /*
* Since this is the only store, concurrency is not an issue.
*/
bug->flags |= BUGFLAG_DONE;
}
} if (warning) {
/* this is a WARN_ON rather than BUG/BUG_ON */
__warn(file, line, (void *)bugaddr, BUG_GET_TAINT(bug), regs,
NULL);
return BUG_TRAP_TYPE_WARN;
} printk(KERN_DEFAULT "------------[ cut here ]------------\n"); if (file)
pr_crit("kernel BUG at %s:%u!\n", file, line);
else
pr_crit("Kernel BUG at %p [verbose debug info unavailable]\n",
(void *)bugaddr); return BUG_TRAP_TYPE_BUG;
}
第10行,在__bug_table中找到bugaddr对应的那一项bug_entry,每个WARN_ON都会增加一项,这个函数首先在kernel的__bug_table段进行搜索,如果没有找到的话,就会在module的bug_table中进行搜索,这个很好理解,如果是静态编译到内核里的,那么就会在kernel的__bug_table里找到,如果编译到了内核模块中,那么就会在module的bug_table中找到。可以参考前面对__BUG_ENTRY的分析
void __warn(const char *file, int line, void *caller, unsigned taint,
struct pt_regs *regs, struct warn_args *args)
{
disable_trace_on_warning(); pr_warn("------------[ cut here ]------------\n"); if (file)
pr_warn("WARNING: CPU: %d PID: %d at %s:%d %pS\n",
raw_smp_processor_id(), current->pid, file, line,
caller);
else
pr_warn("WARNING: CPU: %d PID: %d at %pS\n",
raw_smp_processor_id(), current->pid, caller); if (args)
vprintk(args->fmt, args->args); if (panic_on_warn) {
/*
* This thread may hit another WARN() in the panic path.
* Resetting this prevents additional WARN() from panicking the
* system on this thread. Other threads are blocked by the
* panic_mutex in panic().
*/
panic_on_warn = ;
panic("panic_on_warn set ...\n");
} print_modules(); if (regs)
show_regs(regs);
else
dump_stack(); print_oops_end_marker(); /* Just a warning, don't kill lockdep. */
add_taint(taint, LOCKDEP_STILL_OK);
}
void show_regs(struct pt_regs * regs)
{
__show_regs(regs);
dump_backtrace(regs, NULL);
}
void __show_regs(struct pt_regs *regs)
{
int i, top_reg;
u64 lr, sp; if (compat_user_mode(regs)) {
lr = regs->compat_lr;
sp = regs->compat_sp;
top_reg = ;
} else {
lr = regs->regs[];
sp = regs->sp;
top_reg = ;
} show_regs_print_info(KERN_DEFAULT);
print_symbol("PC is at %s\n", instruction_pointer(regs));
print_symbol("LR is at %s\n", lr);
printk("pc : [<%016llx>] lr : [<%016llx>] pstate: %08llx\n",
regs->pc, lr, regs->pstate);
printk("sp : %016llx\n", sp); i = top_reg; while (i >= ) {
printk("x%-2d: %016llx ", i, regs->regs[i]);
i--; if (i % == ) {
pr_cont("x%-2d: %016llx ", i, regs->regs[i]);
i--;
} pr_cont("\n");
}
if (!user_mode(regs))
show_extra_register_data(regs, );
printk("\n");
}
/* Architecturally defined mapping between AArch32 and AArch64 registers */
#define compat_fp regs[11]
#define compat_sp regs[13]
#define compat_lr regs[14]
void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
{
struct stackframe frame;
int skip; pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); if (!tsk)
tsk = current; if (!try_get_task_stack(tsk))
return; if (tsk == current) {
frame.fp = (unsigned long)__builtin_frame_address();
frame.pc = (unsigned long)dump_backtrace;
} else {
/*
* task blocked in __switch_to
*/
frame.fp = thread_saved_fp(tsk);
frame.pc = thread_saved_pc(tsk);
}
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
frame.graph = tsk->curr_ret_stack;
#endif skip = !!regs;
printk("Call trace:\n");
while () {
unsigned long stack;
int ret; /* skip until specified stack frame */
if (!skip) {
dump_backtrace_entry(frame.pc);
} else if (frame.fp == regs->regs[]) {
skip = ;
/*
* Mostly, this is the case where this function is
* called in panic/abort. As exception handler's
* stack frame does not contain the corresponding pc
* at which an exception has taken place, use regs->pc
* instead.
*/
dump_backtrace_entry(regs->pc);
}
ret = unwind_frame(tsk, &frame);
if (ret < )
break;
if (in_entry_text(frame.pc)) {
stack = frame.fp - offsetof(struct pt_regs, stackframe); if (on_accessible_stack(tsk, stack))
dump_mem("", "Exception stack", stack,
stack + sizeof(struct pt_regs));
}
} put_task_stack(tsk);
}
[ 16.097764] [<ffff000000c5500c>] demo_init+0xc/0x1000 [demo]
[ 16.098018] [<ffff00000808642c>] show_regs+0x2c/0x38
[ 16.098212] [<ffff0000080e4a7c>] __warn+0xb4/0x118
[ 16.098410] [<ffff000008c1c37c>] report_bug+0xbc/0x140
[ 16.098681] [<ffff00000808c034>] bug_handler.part.+0x24/0x78
[ 16.098893] [<ffff00000808c0c4>] bug_handler+0x3c/0x48
[ 16.099087] [<ffff0000080847f0>] brk_handler+0xe0/0x1a0
[ 16.099289] [<ffff0000080816bc>] do_debug_exception+0xa4/0x15c
[ 16.102487] [<ffff0000080830f0>] el1_dbg+0x18/0x74
[ 16.102699] [<ffff000000c5500c>] demo_init+0xc/0x1000 [demo]
[ 16.102915] [<ffff000008083cc4>] do_one_initcall+0x44/0x130
[ 16.103125] [<ffff0000081b9548>] do_init_module+0x64/0x1d4
[ 16.103322] [<ffff0000081b809c>] load_module+0x1e1c/0x24f0
[ 16.103535] [<ffff0000081b88f0>] SyS_init_module+0x180/0x218
上面的栈回溯信息中,我们只关心从demo_init开始的:
[ 16.102699] [<ffff000000c5500c>] demo_init+0xc/0x1000 [demo]
[ 16.102915] [<ffff000008083cc4>] do_one_initcall+0x44/0x130
[ 16.103125] [<ffff0000081b9548>] do_init_module+0x64/0x1d4
[ 16.103322] [<ffff0000081b809c>] load_module+0x1e1c/0x24f0
[ 16.103535] [<ffff0000081b88f0>] SyS_init_module+0x180/0x218
static void dump_backtrace_entry(unsigned long where)
{
/*
* Note that 'where' can have a physical address, but it's not handled.
*/
print_ip_sym(where);
} static inline void print_ip_sym(unsigned long ip)
{
printk("[<%p>] %pS\n", (void *) ip, (void *) ip);
}
上面%pS的作用就是将地址转换成内核符号。
内核中dump_stack的实现原理(1) —— 栈回溯的更多相关文章
- 内核中dump_stack的实现原理(3) —— 内核函数printk的实现
参考内核文档: Documentation/printk-formats.txt 在内核中使用dump_stack的时候可以看到如下用法: static inline void print_i ...
- 内核中dump_stack的实现原理(2) —— symbol
环境 Linux-4.14 Aarch64 正文 在前面的分析中调用print_symbol("PC is at %s\n", instruction_pointer(regs ...
- 内核中dump_stack()的实现,并在用户态模拟dump_stack()【转】
转自:https://blog.csdn.net/jasonchen_gbd/article/details/44066815?utm_source=blogxgwz8 版权声明:本文为博主原创文章, ...
- linux中oops信息的调试及栈回溯【转】
本文转载自:http://blog.csdn.net/kangear/article/details/8217329 ========================================= ...
- linux中Oops信息的调试及栈回溯
Oops 信息来源及格式 Oops 这个单词含义为“惊讶” ,当内核出错时(比如访问非法地址)打印出来的信息被 称为 Oops 信息. Oops 信息包含以下几部分内容. 1 一段文本描述信息. 比如 ...
- linux内核中打印栈回溯信息 - dump_stack()函数分析【转】
转自:http://blog.csdn.net/jasonchen_gbd/article/details/45585133 版权声明:本文为博主原创文章,转载请附上原博链接. 目录(?)[-] ...
- Linux内核中的中断栈与内核栈的补充说明【转】
转自:http://blog.chinaunix.net/uid-12461657-id-3487463.html 原文地址:Linux内核中的中断栈与内核栈的补充说明 作者:MagicBoy2010 ...
- Openvswitch原理与代码分析(5): 内核中的流表flow table操作
当一个数据包到达网卡的时候,首先要经过内核Openvswitch.ko,流表Flow Table在内核中有一份,通过key查找内核中的flow table,即可以得到action,然后执行acti ...
- linux内核中异步通信机制--信号处理机制【转】
转自:http://blog.csdn.net/lu_embedded/article/details/51131663 什么是异步通信?很简单,一旦设备准备好,就主动通知应用程序,这种情况下应用程序 ...
随机推荐
- java连接redis中的数据查、增、改、删操作的方法
package com.lml.redis; import java.util.HashMap;import java.util.Iterator;import java.util.Map;impor ...
- 超强在线考试系统源码(私有部署&二次开发)
随着信息化技术的发展,考试系统也在进行着深入的变革.从传统的纸质考试人工评分到现在的在线考试自动评分. 在线考试系统的应用场景也在逐渐扩宽,例如:学校的学生考试.员工培训考试.招聘考试.职称考试等等. ...
- c++开发遇到的错误和引用配置
1. libcurl引入的时候必须要加载下面三个库 #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "wl ...
- 阿里云 API 签名机制的 Python 实现
在调用阿里云 API 的时候,最让人头疼的就是 API 的签名(Signature)机制,阿里云在通用文档中也有专项说明,但是仅仅有基于 Java 的实现代码示例.所以这里基于 Python 来分析下 ...
- phpcms新建模板页教程
phpcms新建模板页教程1 直接去template文件夹里的复制的模板页 比方说list1.html2 去后台 界面模板风格 default 默认模板 点击详情列表 找到list1.htm 设置中文 ...
- hanlp进行命名实体识别
需要安装jpype先,这个是python调用java库的桥梁. # -*- coding: utf-8 -*- """ Created on Thu May 10 09: ...
- kafka topic查看删除
1,查看kafka topic列表,使用--list参数 >bin/kafka-topics.sh --zookeeper 127.0.0.1:2181 --list __consumer_of ...
- 全国自考C++程序设计
一.单项选择题(本大题共20小题,每小题1分,共20分)在每小题列出的四个备选项中 只有一个是符合题目要求的,请将其代码填写在题后的括号内.错选.多选或未选均无 分. 1. 编写C++程序一般需经过的 ...
- Linux下常用目录有哪些?分别有什么作用?
/boot:这个目录是用来存放与系统启动相关的文件 /root:root用户的家目录 /bin:存放大部分的二进制的可执行文件,也就是大部分的linux命令. /tmp:这个文件目录一般是公共的,也就 ...
- SpringBoot扩展点之三:SpringBootServletInitializer扩展
SpringBootServletInitializer 熟悉了SpringApplication的原理之后,我们再来了解SpringBootServletInitializer的原理就比较容易了. ...