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