kernel BUG_ON macro的实现以及brk指令触发异常后的异常处理callstack
kernel里的两个macro
BUG_ON(condition),如果condition条件满足,判断为真,则会造成一个debug exception;
BUG(),这个没有条件判断,调用它则会直接造成一个debug exception,可以在code里在某种error的情形下调用。BUG_ON()也是调用的这个macro,只是多一个判断条件。
BUG() macro的实现
arch/arm64/include/asm/asm-bug.h
#ifdef CONFIG_DEBUG_BUGVERBOSE #define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line) #define __BUGVERBOSE_LOCATION(file, line) \ .pushsection .rodata.str,"aMS",@progbits,1; \ 14472: .string file; \ #这里.string存储__FILE__ string,会自动以/0字符结尾 .popsection; \ \ .long 14472b - 14470b; \ #这里存储的是上面.string字串在bug_entry struct里的offset,后面根据bug_entry struct的起始地址再加上这个offset即可以得到这个string的起始地址 .short line; #存储的是__LINE__ #else #define _BUGVERBOSE_LOCATION(file, line) #endif #ifdef CONFIG_GENERIC_BUG #define __BUG_ENTRY(flags) \ .pushsection __bug_table,"aw"; \ .align 2; \ 14470: .long 14471f - 14470b; \ #这里是两个label地址相减,这个对应bug_entry struct里的bug_addr_disp,即这个struct的size _BUGVERBOSE_LOCATION(__FILE__, __LINE__) \ .short flags; \ .popsection; \ 14471: #else #define __BUG_ENTRY(flags) #endif #define ASM_BUG_FLAGS(flags) \ __BUG_ENTRY(flags) \ brk BUG_BRK_IMM #define ASM_BUG() ASM_BUG_FLAGS(0)
注意上面__BUG_ENTRY macro里看起来是有5个field,但是它是对应如下struct bug_entry的,这个struct里只define了4个field,所以看起来上面__BUG_ENTRY里的.string是不包含在struct bug_entry里的
include/asm-generic/bug.h
#ifdef CONFIG_GENERIC_BUG struct bug_entry { #ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS unsigned long bug_addr; #else signed int bug_addr_disp; #endif #ifdef CONFIG_DEBUG_BUGVERBOSE #ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS const char *file; #else signed int file_disp; #endif unsigned short line; #endif unsigned short flags; }; #endif /* CONFIG_GENERIC_BUG */
arch/arm64/kernel/traps.c
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, 0); 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: */ arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE); return DBG_HOOK_HANDLED; }
4.19/lib/bug.c
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 (file) pr_crit("kernel BUG at %s:%u!\n", file, line); else pr_crit("Kernel BUG at %pB [verbose debug info unavailable]\n", (void *)bugaddr);
4.19/lib/bug.c
static inline unsigned long bug_addr(const struct bug_entry *bug) { #ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS return bug->bug_addr; #else return (unsigned long)bug + bug->bug_addr_disp; #endif }
ASM_BUG_FLAGS里包含了__BUG_ENTRY(flags)以及一条brk BUG_BRK_IMM指令,
__BUG_ENTRY相当于define了一个struct bug_entry,所以是在这个struct后面紧跟了一个brk BUG_BRK_IMM指令,所以上述(unsigned long)bug + bug->bug_addr_disp即表示这条brk指令的地址,这个地址和发生brk异常时的PC值比较,如果相等,则找到了对应的bug_entry struct,并将调用BUG_ON()所在文件以及行数打印出来
看一下BUG_ON() 是如何实现的
include/asm-generic/bug.h
#ifndef HAVE_ARCH_BUG_ON #define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while (0) #endif
4.19/arch/arm64/include/asm/bug.h
#define BUG() do { \ __BUG_FLAGS(0); \ unreachable(); \ } while (0)
#define __BUG_FLAGS(flags) \ asm volatile (__stringify(ASM_BUG_FLAGS(flags)));
所以每一个BUG_ON最终的编译结果会包含一个brk指令,如果BUG_ON的条件成立(条件判断为1),则会执行到这条brk指令触发异常,处理这个异常的callstack如下:
<2>[ 1409.592923] kernel BUG at block/blk-core.c:3238! <0>[ 1409.592929] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP KERNEL-PANIC dump process path [HwBinder:2904_2, 3479] dump backtrace [<0000000080c43ddd>] kmsg_dump+0xa4/0x284 [<000000000affdfa0>] panic+0x1c0/0x4e4 [<000000009de7d86d>] die+0x5d8/0x5ec [<000000000b44dbbb>] bug_handler+0x50/0xa4 [<0000000014428671>] brk_handler+0xe0/0x21c [<0000000056de13f5>] do_debug_exception+0x154/0x2a4 [<000000006312a774>] el1_dbg+0x18/0xa8 [<00000000723f061b>] blk_finish_request+0x22c/0x230 [<0000000036ccd665>] scsi_end_request+0x288/0x588 [<000000002223f37c>] scsi_io_completion+0x8c/0x90c [<00000000404d6d57>] scsi_finish_command+0x12c/0x184 [<00000000323c7119>] scsi_softirq_done+0x118/0x14c [<000000007f25ecd3>] blk_done_softirq+0xcc/0x130 [<00000000414d4713>] __do_softirq+0x1f8/0x490 [<000000003feb2504>] irq_exit+0x1d4/0x244 [<00000000df9c9f02>] handle_IPI+0x2cc/0x6b8 [<000000005f9611a3>] gic_handle_irq+0xa4/0xbc [<00000000fe8e935c>] el1_irq+0xe8/0x190 [<00000000f48fe71e>] binder_ioctl_write_read+0x2868/0x3214 [<000000000c8156f9>] binder_ioctl+0x370/0xd30 [<0000000047fd89c2>] do_vfs_ioctl+0x718/0xf0c [<0000000072e174c9>] __arm64_sys_ioctl+0xcc/0x104 [<00000000b02b22b0>] el0_svc_common+0xb8/0x1b8 [<0000000016fc6b90>] el0_svc_handler+0x74/0x90 [<00000000255fbc3e>] el0_svc+0x8/0x340 [<0000000074c12ba5>] 0xffffffffffffffff