zoukankan      html  css  js  c++  java
  • kernel 3.10内核源码分析--BUG_ON流程


    http://blog.chinaunix.net/uid-14528823-id-4902857.html


    一、问题
    在dmesg或messages中常见BUG_ON的相关打印,如:
    ------------[ cut here ]------------
    kernel BUG at ...
    也常见其它的异常打印,比如page_fault相关的,softlockup相关的,有时候不太好区分它们之间的差别,但区分它们却是否重要,直接关系着对问题本质的判断。
    这里简单分析了一下BUG_ON在3.10 kernel代码中的实现。

    二、基本原理
    BUG_ON通过BUG宏实现。BUG最终是通过执行ud2汇编指令实现。ud2指令看起来有点陌生,大概就是undefine的意思,是一种让CPU产生invalid opcode异常的软件指令,此时会有相应的异常事件上报,内核捕获相应的异常,由预先注册的异常处理接口进行处理:打印相关错误信息,最终根据配置进行kdump或panic或停止当前进程。

    三、代码分析
    1、BUG_ON宏定义
    BUG_ON宏定义,判断condition是否成立,成立则调用BUG():

    点击(此处)折叠或打开

    1. #define BUG_ON(condition) do { if (unlikely(condition)) BUG(); }

    BUG()宏定义,本质是调用ud2汇编指令

    点击(此处)折叠或打开

    1. #define BUG()    
    2. do {    
    3. asm volatile("1: ud2 "    
    4.     ".pushsection __bug_table,"a" "    
    5.     __BUG_C0    
    6.     " .word %c1, 0 "    
    7.     " .org 2b+%c2 "    
    8.     ".popsection"    
    9.     : : "i" (__FILE__), "i" (__LINE__),    
    10.     "i" (sizeof(struct bug_entry)));    
    11. unreachable();    
    12. } while (0)

    其中,'c' 在gcc中, 叫做operand code, 用在常量变量(constraint表示'i')和条件判断指令中. 作用是将这个常量值打印在指令中,对于常量如果不用‘c’,上述会出问题,不能正常运行。
    其他都是assembler directive,主要目的是将bug相关的信息,比如文件名、行号等保存到预先定义好的bug_table中。 

    2、invalid opcode异常初始化流程
    start_kernel
        ->trap_init
            ->set_intr_gate(X86_TRAP_UD, invalid_op);

    invalid_op由汇编实现,代码在entry_32.S中,最终调用do_invalid_op

    点击(此处)折叠或打开

    1. ENTRY(invalid_op)
    2.     RING0_INT_FRAME
    3.     ASM_CLAC
    4.     pushl_cfi $0
    5.     pushl_cfi $do_invalid_op
    6.     jmp error_code
    7.     CFI_ENDPROC
    8. END(invalid_op)

    do_invalid_op的实现代码中不好找,主要是因为其不是直接实现的,而是通过宏实现,关键字不好搜。内核中这种实现方式还比较多,比如page的几个flag的判断接口。

    点击(此处)折叠或打开

    1. DO_ERROR_INFO(X86_TRAP_UD, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN,
    2.         regs->ip)
    DO_ERROR_INFO宏实现:

    点击(此处)折叠或打开

    1. #define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr)        
    2. dotraplinkage void do_##name(struct pt_regs *regs, long error_code)    
    3. {                                    
    4.     siginfo_t info;                            
    5.     enum ctx_state prev_state;                    
    6.                                     
    7.     info.si_signo = signr;                        
    8.     info.si_errno = 0;                        
    9.     info.si_code = sicode;                        
    10.     info.si_addr = (void __user *)siaddr;                
    11.     prev_state = exception_enter();                    
    12.     if (notify_die(DIE_TRAP, str, regs, error_code,            
    13.             trapnr, signr) == NOTIFY_STOP) {        
    14.         exception_exit(prev_state);                
    15.         return;                            
    16.     }                                
    17.     conditional_sti(regs);                        
    18.     do_trap(trapnr, signr, str, regs, error_code, &info);        
    19.     exception_exit(prev_state);                    
    20. }
    3、invalid opcode异常处理流程
    do_trap流程:
    do_trap()
        ->do_trap_no_signal()
            ->die()
                ->report_bug()
                ->__die()


    do_trap()->do_trap_no_signal():

    点击(此处)折叠或打开

    1. static int __kprobes
    2. do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
    3.          struct pt_regs *regs,    long error_code)
    4. {
    5. #ifdef CONFIG_X86_32
    6.     /*判断是否有VM86标记,如果有的话,则进行相关处理。*/
    7.     if (regs->flags & X86_VM_MASK) {
    8.         /*
    9.          * Traps 0, 1, 3, 4, and 5 should be forwarded to vm86.
    10.          * On nmi (interrupt 2), do_trap should not be called.
    11.          */
    12.         /*当异常号小于6时(不包括2(NMI),NMI不会进入到do_trap流程),进入vm86处理。*/
    13.         if (trapnr < X86_TRAP_UD) {
    14.             if (!handle_vm86_trap((struct kernel_vm86_regs *) regs,
    15.                         error_code, trapnr))
    16.                 return 0;
    17.         }
    18.         return -1;
    19.     }
    20. #endif
    21.     /*是否发生异常时处于内核态?*/
    22.     if (!user_mode(regs)) {
    23.         /*查找fixup表,看是否有预定义好的修正处理,有的话就进行相关处理*/
    24.         if (!fixup_exception(regs)) {
    25.             /*设置错误码和异常号到任务描述符中*/
    26.             tsk->thread.error_code = error_code;
    27.             tsk->thread.trap_nr = trapnr;
    28.             /*调用die,进入"死机"流程*/
    29.             die(str, regs, error_code);
    30.         }
    31.         return 0;
    32.     }

    33.     return -1;
    34. }

    do_trap()->do_trap_no_signal()->die():

    点击(此处)折叠或打开

    1. /*
    2.  * This is gone through when something in the kernel has done something bad
    3.  * and is about to be terminated:
    4.  */
    5. /*内核出问题了,进入终止流程*/
    6. void die(const char *str, struct pt_regs *regs, long err)
    7. {
    8.     /*oops前的相关处理,包括关闭相关trace,获取die相关的锁等(防止死锁)*/
    9.     unsigned long flags = oops_begin();
    10.     int sig = SIGSEGV;
    11.     /*如果是内核态触发,则应该是内核bug了,需要打印相关提示*/
    12.     if (!user_mode_vm(regs))
    13.         /*报告bug,打印相关信息*/
    14.         report_bug(regs->ip, regs);
    15.     /*打印相关信息,包括EIP,堆栈等。*/
    16.     if (__die(str, regs, err))
    17.         sig = 0;
    18.     /*是否die相关的锁,并根据情况进行kdump或panic*/
    19.     oops_end(flags, regs, sig);
    20. }

    do_trap()->do_trap_no_signal()->report_bug():

    点击(此处)折叠或打开

    1. /*报告bug,打印相关信息*/
    2. enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs)
    3. {
    4.     const struct bug_entry *bug;
    5.     const char *file;
    6.     unsigned line, warning;

    7.     if (!is_valid_bugaddr(bugaddr))
    8.         return BUG_TRAP_TYPE_NONE;
    9.     /*通过出错的IP指针从"bug_table"中找到相关的错误信息,包括行号之类的*/
    10.     bug = find_bug(bugaddr);

    11.     file = NULL;
    12.     line = 0;
    13.     warning = 0;

    14.     if (bug) {
    15. #ifdef CONFIG_DEBUG_BUGVERBOSE
    16. #ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
    17.         file = bug->file;
    18. #else
    19.         file = (const char *)bug + bug->file_disp;
    20. #endif
    21.         line = bug->line;
    22. #endif
    23.         warning = (bug->flags & BUGFLAG_WARNING) != 0;
    24.     }

    25.     if (warning) {
    26.         /* this is a WARN_ON rather than BUG/BUG_ON */
    27.         printk(KERN_WARNING "------------[ cut here ]------------ ");

    28.         if (file)
    29.             printk(KERN_WARNING "WARNING: at %s:%u ",
    30.              file, line);
    31.         else
    32.             printk(KERN_WARNING "WARNING: at %p "
    33.              "[verbose debug info unavailable] ",
    34.              (void *)bugaddr);

    35.         print_modules();
    36.         show_regs(regs);
    37.         print_oops_end_marker();
    38.         /* Just a warning, don't kill lockdep. */
    39.         add_taint(BUG_GET_TAINT(bug), LOCKDEP_STILL_OK);
    40.         return BUG_TRAP_TYPE_WARN;
    41.     }

    42.     printk(KERN_DEFAULT "------------[ cut here ]------------ ");

    43.     if (file)
    44.         /*打印bug提示,就是平常常见的打印了*/
    45.         printk(KERN_CRIT "kernel BUG at %s:%u! ",
    46.          file, line);
    47.     else
    48.         printk(KERN_CRIT "Kernel BUG at %p "
    49.          "[verbose debug info unavailable] ",
    50.          (void *)bugaddr);

    51.     return BUG_TRAP_TYPE_BUG;
    52. }

  • 相关阅读:
    数据库-第六章 关系数据理论-6.2.1 函数依赖
    数据库-第六章 关系数据理论-6.1 问题的提出
    编译原理-第四章 语法分析-4.4 自顶向下的语法分析
    计算机组成及系统结构-第五章 指令系统
    编译原理-第四章 语法分析-4.3 设计文法
    Java数据结构之堆和优先队列
    进程与线程杂谈
    Java的多态浅谈
    Java的自定义注解使用实例
    elasticsearch6.6.2在Centos6.9的安装
  • 原文地址:https://www.cnblogs.com/ztguang/p/12645265.html
Copyright © 2011-2022 走看看