• Linux中oops信息调试【转】


    1、Oops 信息来源及格式

    Oops 这个单词含义为“惊讶”,当内核出错时(比如访问非法地址)打印出来的信息被称为 Oops 信息。

    2、Oops 信息包含以下几部分内容

    2.1 一段文本描述信息。

    比如类似“Unable to handle kernel NULL pointer dereference at virtual address 00000000”的信息,它说明了发生的是哪类错误。

    2.2 Oops 信息的序号。

    比如是第 1 次、第 2 次等。这些信息与下面类似,中括号内的数据表示序号。Internal error: Oops: 805 [#1]

    2.3 内核中加载的模块名称,也可能没有,以下面字样开头。

    Modules linked in:

    2.4 发生错误的 CPU 的序号。

    对于单处理器的系统,序号为 0,比如:CPU: 0Not tainted (2.6.22.6 #36)

    2.5 发生错误时 CPU 的各个寄存器值。

    2.6 当前进程的名字及进程 ID

    比如:
    Process swapper (pid: 1, stack limit = 0xc0480258)
    这并不是说发生错误的是这个进程,而是表示发生错误时,当前进程是它。错误可能发
    生在内核代码、驱动程序,也可能就是这个进程的错误。

    2.7 栈信息。

    2.8 栈回溯信息,可以从中看出函数调用关系,形式如下:

    Backtrace:[<c001a6f4>] (s3c2410fb_probe+0x0/0x560) from [<c01bf4e8>] (platform_drv_probe+0x20/0x24)

    2.9 出错指令附近的指令的机器码,比如(出错指令在小括号里):

    Code: e24cb004 e24dd010 e59f34e0 e3a07000 (e5873000)

    3、配置内核使 Oops 信息的栈回溯信息更直观

    可以通过配置 CONFIG_FRAME_POINTER 来实现。

    4、实例

    使用 Oops 信息调试内核的实例获得 Oops 信息本小节故意修改 LCD 驱动程序 drivers/video/s3c2410fb.c,加入错误代码:在 s3c2410fb_probe 函数的开头增加下面两条代码:int *ptest = NULL;*ptest = 0x1234;重新编译内核,启动后会出错并打印出如下 Oops 信息:

     1 Unable to handle kernel NULL pointer dereference at virtual address 00000000
     2 pgd = c0004000
     3 [00000000] *pgd=00000000
     4 Internal error: Oops: 805 [#1]
     5 Modules linked in:
     6 CPU: 0
     7 Not tainted (2.6.22.6 #36)
     8 PC is at s3c2410fb_probe+0x18/0x560
     9 LR is at platform_drv_probe+0x20/0x24
    10 pc : [<c001a70c>]
    11 lr : [<c01bf4e8>]
    12 psr: a0000013
    13 sp : c0481e64 ip : c0481ea0 fp : c0481e9c
    14 r10: 00000000 r9 : c0024864 r8 : c03c420c
    15 r7 : 00000000 r6 : c0389a3c r5 : 00000000 r4 : c036256c
    16 r3 : 00001234 r2 : 00000001 r1 : c04c0fc4 r0 : c0362564
    17 Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment kernel
    18 Control: c000717f Table: 30004000 DAC: 00000017
    19 Process swapper (pid: 1, stack limit = 0xc0480258)
    20 Stack: (0xc0481e64 to 0xc0482000)
    21 1e60:c02b1f70 00000020 c03625d4 c036256c c036256c 00000000 c0389a3c
    22 1e80: c0389a3c c03c420c c0024864 00000000 c0481eac c0481ea0 c01bf4e8 c001a704
    23 1ea0: c0481ed0 c0481eb0 c01bd5a8 c01bf4d8 c0362644 c036256c c01bd708 c0389a3c
    24 1ec0: 00000000 c0481ee8 c0481ed4 c01bd788 c01bd4d0 00000000 c0481eec c0481f14
    25 1ee0: c0481eec c01bc5a8 c01bd718 c038dac8 c038dac8 c03625b4 00000000 c0389a3c
    26 1f00: c0389a44 c038d9dc c0481f24 c0481f18 c01bd808 c01bc568 c0481f4c c0481f28
    27 1f20: c01bcd78 c01bd7f8 c0389a3c 00000000 00000000 c0480000 c0023ac8 00000000
    28 1f40: c0481f60 c0481f50 c01bdc84 c01bcd0c 00000000 c0481f70 c0481f64 c01bf5fc
    29 1f60: c01bdc14 c0481f80 c0481f74 c019479c c01bf5a0 c0481ff4 c0481f84 c0008c14
    30 1f80: c0194798 e3c338ff e0222423 00000000 00000001 e2844004 00000000 00000000
    31 1fa0: 00000000 c0481fb0 c002bf24 c0041328 00000000 00000000 c0008b40 c00476ec
    32 1fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    33 1fe0: 00000000 00000000 00000000 c0481ff8 c00476ec c0008b50 c03cdf50 c0344178
    34 Backtrace:
    35 [<c001a6f4>] (s3c2410fb_probe+0x0/0x560) from [<c01bf4e8>] (platform_drv_
    36 probe+0x20/0x24)
    37 [<c01bf4c8>] (platform_drv_probe+0x0/0x24) from [<c01bd5a8>] (driver_probe_
    38 device+0xe8/0x18c)
    39 [<c01bd4c0>] (driver_probe_device+0x0/0x18c) from [<c01bd788>] (__driver_
    40 attach+0x80/0xe0)
    41 r8:00000000 r7:c0389a3c r6:c01bd708 r5:c036256c r4:c0362644
    42 [<c01bd708>] (_ _driver_attach+0x0/0xe0) from [<c01bc5a8>] (bus_for_each_
    43 dev+0x50/0x84)
    44 r5:c0481eec r4:00000000
    45 [<c01bc558>] (bus_for_each_dev+0x0/0x84) from [<c01bd808>] (driver_attach+
    46 0x20/0x28)
    47 r7:c038d9dc r6:c0389a44 r5:c0389a3c r4:00000000
    48 [<c01bd7e8>] (driver_attach+0x0/0x28) from [<c01bcd78>] (bus_add_driver+
    49 0x7c/0x1b4)
    50 [<c01bccfc>] (bus_add_driver+0x0/0x1b4) from [<c01bdc84>] (driver_register+
    51 0x80/0x88)
    52 [<c01bdc04>] (driver_register+0x0/0x88) from [<c01bf5fc>] (platform_driver_
    53 register+0x6c/0x88)
    54 r4:00000000
    55 [<c01bf590>] (platform_driver_register+0x0/0x88) from [<c019479c>] (s3c2410fb_
    56 init+0x14/0x1c)
    57 [<c0194788>] (s3c2410fb_init+0x0/0x1c) from [<c0008c14>] (kernel_init+0xd4/
    58 0x28c)
    59 [<c0008b40>] (kernel_init+0x0/0x28c) from [<c00476ec>] (do_exit+0x0/0x760)
    60 Code: e24cb004 e24dd010 e59f34e0 e3a07000 (e5873000)
    61 Kernel panic - not syncing: Attempted to kill init!

     (1)明确出错原因。

    由出错信息“Unable to handle kernel NULL pointer dereference at virtual address 00000000”可知内核是因为非法地址访问出错,使用了空指针。

    (2)根据栈回溯信息找出函数调用关系。

    内核崩溃时,可以从 pc 寄存器得知崩溃发生时的函数、出错指令。但是很多情况下,错误有可能是它的调用者引入的,所以找出函数的调用关系也很重要。
    部分栈回溯信息如下:
    [<c001a6f4>] (s3c2410fb_probe+0x0/0x560) from [<c01bf4e8>] (platform_drv_
    probe+0x20/0x24)
    这行信息分为两部分,
    表示后面的 platform_drv_probe 函数调用了前面的 s3c2410fb_probe
    函数。
    前半部含义为:
    “c001a6f4”是 s3c2410fb_probe 函数首地址偏移 0 的地址,这个函数大
    小为 0x560。
    后半部含义为:
    “c01bf4e8”是 platform_drv_probe 函数首地址偏移 0x20 的地址,这个函
    数大小为 0x24。
    另外,后半部的“[<c01bf4e8>]”表示 s3c2410fb_probe 执行后的返回地址。
    对于类似下面的栈回溯信息,其中是 r8~r4 表示 driver_probe_device 函数刚被调用时这
    些寄存器的值。
    [<c01bd4c0>] (driver_probe_device+0x0/0x18c) from [<c01bd788>] (__driver_
    attach+0x80/0xe0)
    r8:00000000 r7:c0389a3c r6:c01bd708 r5:c036256c r4:c0362644
    从上面的栈回溯信息可以知道内核出错时的函数调用关系如下,
    最后在 s3c2410fb_probe
    函数内部崩溃。
    do_exit ->
    kernel_init ->
    s3c2410fb_init ->
    platform_driver_register ->
    driver_register ->
    bus_add_driver ->
    driver_attach ->
    bus_for_each_dev ->
    __driver_attach ->
    driver_probe_device ->
    platform_drv_probe ->
    s3c2410fb_probe

    (3)根据 pc 寄存器的值确定出错位置。

    上述 Oops 信息中出错时的寄存器值如下:PC is at s3c2410fb_probe+0x18/0x560
    LR is at platform_drv_probe+0x20/0x24
    pc : [<c001a70c>]
    lr : [<c01bf4e8>]
    psr: a0000013
    ...
    “PC is at s3c2410fb_probe+0x18/0x560”表示出错指令为 s3c2410fb_probe 函数中偏移为
    0x18 的指令。
    “pc : [<c001a70c>]”表示出错指令的地址为 c001a70c(十六进制)。

    (4)结合内核源代码和反汇编代码定位问题。

    先生成内核的反汇编代码 vmlinux.dis,执行以下命令:

    $ cd /work/system/linux-2.6.22.6
    $ arm-linux-objdump -D vmlinux > vmlinux.dis
    出错地址 c001a70c 附近的部分汇编代码如下:
    c001a6f4 <s3c2410fb_probe>:
    c001a6f4: e1a0c00d mov ip, sp
    c001a6f8: e92ddff0 stmdb
    c001a6fc: e24cb004 sub fp, ip, #4 ; 0x4
    c001a700: e24dd010 sub sp, sp, #16 ; 0x10
    c001a704: e59f34e0 ldr r3, [pc, #1248] ; c001abec <.init+0x1284c>
    c001a708: e3a07000 mov r7, #0
    c001a70c: e5873000 str r3, [r7]
    c001a710: e59030fc ldr r3, [r0, #252]
    sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr, pc}
    ; 0x0
    <===========出错指令
    出错指令为“str r3, [r7]”
    ,它把 r3 寄存器的值放到内存中,内存地址为 r7 寄存器的值。
    根据 Oops 信息中的寄存器值可知:r3 为 0x00001234,r7 为 0。0 地址不可访问,所以出错。
    s3c2410fb_probe 函数的部分 C 代码如下:
    static int __init s3c2410fb_probe(struct platform_device *pdev)
    {
    struct s3c2410fb_info *info;
    struct fb_info
    *fbinfo;
    struct s3c2410fb_hw *mregs;
    int ret;
    int irq;
    int i;
    u32 lcdcon1;
    int *ptest = NULL;
    *ptest = 0x1234;
    mach_info = pdev->dev.platform_data;
    结合反汇编代码,很容易知道是“*ptest = 0x1234;”导致错误,其中的 ptest 为空。
    对于大多数情况,从反汇编代码定位到 C 代码并不会如此容易,这需要较强的阅读汇编
    程序的能力。通过栈回溯信息知道函数的调用关系,这已经可以帮助定位很多问题了。

  • 相关阅读:
    Perl 常用的小细节总结
    Linux-root管理员创建新用户
    Linux-普通用户和root用户任意切换
    Linux-设置终端界面的字体颜色和自定义常用快捷功能
    Linux命令行好玩的命令
    如何配一副好的眼镜
    linux下面升级 Python版本并修改yum属性信息
    mysql远程登陆
    Linux 如何找到100M以上的大文件
    将raspberry 3B+的apt替换为国内源
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/7762480.html
Copyright © 2020-2023  润新知