Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5672715
  • 博文数量: 675
  • 博客积分: 20301
  • 博客等级: 上将
  • 技术积分: 7671
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-31 16:15
文章分类

全部博文(675)

文章存档

2012年(1)

2011年(20)

2010年(14)

2009年(63)

2008年(118)

2007年(141)

2006年(318)

分类: LINUX

2008-07-26 10:40:29

(
来自Linus Torvalds的讨论:
[url][/url]
又,[url][/url]
)
     
        例如这样的一个Oops:
                Oops: 0000 [#1] PREEMPT SMP  
                Modules linked in: capidrv kernelcapi isdn slhc ipv6 loop dm_multipath snd_ens1371 gameport snd_rawmidi snd_ac97_codec ac97_bus snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd parport_pc floppy parport pcnet32 soundcore mii pcspkr snd_page_alloc ac i2c_piix4 i2c_core button power_supply sr_mod sg cdrom ata_piix libata dm_snapshot dm_zero dm_mirror dm_mod BusLogic sd_mod scsi_mod ext3 jbd mbcache uhci_hcd ohci_hcd ehci_hcd

                Pid: 1726, comm: kstopmachine Not tainted (2.6.24-rc3-module #2)
                EIP: 0060:[] EFLAGS: 00010092 CPU: 0
                EIP is at list_del+0xa/0x61
                EAX: e0c3cc04 EBX: 00000020 ECX: 0000000e EDX: dec62000
                ESI: df6e8f08 EDI: 000006bf EBP: dec62fb4 ESP: dec62fa4
                 DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
                Process kstopmachine (pid: 1726, ti=dec62000 task=df8d2d40 task.ti=dec62000)
                Stack: 000006bf dec62fb4 c04276c7 00000020 dec62fbc c044ab4c dec62fd0 c045336c
                       df6e8f08 c04532b4 00000000 dec62fe0 c043deb0 c043de75 00000000 00000000
                       c0405cdf df6e8eb4 00000000 00000000 00000000 00000000 00000000
                Call Trace:
                 [] show_trace_log_lvl+0x1a/0x2f
                 [] show_stack_log_lvl+0x9b/0xa3
                 [] show_registers+0xa3/0x1df
                 [] die+0x11f/0x200
                 [] do_page_fault+0x533/0x61a
                 [] error_code+0x72/0x78
                 [] __unlink_module+0xb/0xf
                 [] do_stop+0xb8/0x108
                 [] kthread+0x3b/0x63
                 [] kernel_thread_helper+0x7/0x10
                 =======================
                Code: 6b c0 e8 2e 7e f6 ff e8 d1 16 f2 ff b8 01 00 00 00 e8 aa 1c f4 ff 89 d8 83 c4 10 5b 5d c3 90 90 90 55 89 e5 53 83 ec 0c 8b 48 04 <8b> 11 39 c2 74 18 89 54 24 08 89 44 24 04 c7 04 24 be 32 6b c0  
                EIP: [] list_del+0xa/0x61 SS:ESP 0068:dec62fa4
                note: kstopmachine[1726] exited with preempt_count 1
     
        1, 有自己编译的vmlinux: 使用gdb
     
           编译时打开complie with debug info选项。

           注意这行:
     
                EIP is at list_del+0xa/0x61
     
           这告诉我们,list_del函数有0x61这么大,而Oops发生在0xa处。 那么我们先看一下list_del从哪里开始:

                # grep list_del /boot/System.map-2.6.24-rc3-module
                c10e5234 T plist_del
                c10e53cc T list_del
                c120feb6 T klist_del
                c12d6d34 r __ksymtab_list_del
                c12dadfc r __ksymtab_klist_del
                c12e1abd r __kstrtab_list_del
                c12e9d03 r __kstrtab_klist_del

           于是我们知道,发生Oops时的EIP值是:

                c10e53cc + 0xa  == c10e53d6

           然后用gdb查看:

                # gdb /home/arc/build/linux-2.6/vmlinux
                (gdb) b *0xc10e53d6
                Breakpoint 1 at 0xc10e53d6: file /usr/src/linux-2.6.24-rc3/lib/list_debug.c, line 64.

           看,gdb直接就告诉你在哪个文件、哪一行了。

           gdb中还可以这样:

                # gdb Sources/linux-2.6.24/vmlinux
                (gdb) l *do_fork+0x1f
                0xc102b7ac is in do_fork (kernel/fork.c:1385).
                1380
                1381    static int fork_traceflag(unsigned clone_flags)
                1382    {
                1383            if (clone_flags & CLONE_UNTRACED)
                1384                    return 0;
                1385            else if (clone_flags & CLONE_VFORK) {
                1386                    if (current->ptrace & PT_TRACE_VFORK)
                1387                            return PTRACE_EVENT_VFORK;
                1388            } else if ((clone_flags & CSIGNAL) != SIGCHLD) {
                1389                    if (current->ptrace & PT_TRACE_CLONE)
                (gdb)

            也可以直接知道line number。

            或者:

                (gdb) l *(0xffffffff8023eaf0 + 0xff)  /* 出错函数的地址加上偏移 */



        2, 没有自己编译的vmlinux: TIPS

           如果在lkml或bugzilla上看到一个Oops,而自己不能重现,那就只能反汇编以"Code:"开始的行。 这样可以尝试定位到
           源代码中。

           注意,Oops中的Code:行,会把导致Oops的第一条指令,也就是EIP的值的第一个字节, 用尖括号<>括起来。 但是,有些
           体系结构(例如常见的x86)指令是不等长的(不一样的指令可能有不一样的长度),所以要不断的尝试(trial-and-error)。

           Linus通常使用一个小程序,类似这样:

                const char array[] = "\xnn\xnn\xnn...";
                int main(int argc, char *argv[])
                {
                        printf("%p\n", array);
                        *(int *)0 = 0;
                }

e.g. /*{{{*/ /* 注意, array一共有从array[0]到array[64]这65个元素, 其中出错的那个操作码<8b> == arry[43] */
#include
#include


const char array[] ="\x6b\xc0\xe8\x2e\x7e\xf6\xff\xe8\xd1\x16\xf2\xff\xb8\x01\x00\x00\x00\xe8\xaa\x1c\xf4\xff\x89\xd8\x83\xc4\x10\x5b\x5d\xc3\x90\x90\x90\x55\x89\xe5\x53\x83\xec\x0c\x8b\x48\x04\x8b\x11\x39\xc2\x74\x18\x89\x54\x24\x08\x89\x44\x24\x04\xc7\x04\x24\xbe\x32\x6b\xc0";
int main(int argc, char *argv[])
{
        printf("%p\n", array);
        *(int *)0 = 0;
}
/*}}}*/



           用gcc -g编译,在gdb里运行它:

                [arc@dhcp-cbjs05-218-251 ~]$ gdb hello
                GNU gdb Fedora (6.8-1.fc9)
                Copyright (C) 2008 Free Software Foundation, Inc.
                License GPLv3+: GNU GPL version 3 or later <[url][/url]>
                This is free software: you are free to change and redistribute it.
                There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
                and "show warranty" for details.
                This GDB was configured as "x86_64-redhat-linux-gnu"...
                (no debugging symbols found)
                (gdb) r
                Starting program: /home/arc/hello
                0x80484e0

                Program received signal SIGSEGV, Segmentation fault.

           注意,这时候就可以反汇编0x80484e0这个地址:

                (gdb) disassemble 0x80484e0
                Dump of assembler code for function array:
                0x080484e0 :   imul   $0xffffffe8,%eax,%eax
                0x080484e3 :   jle,pn 0x80484dc <__dso_handle+20>
                0x080484e6 :   ljmp   *
                0x080484e8 :   rcll   (%esi)
                0x080484ea :  repnz (bad)
                0x080484ec :  mov    $0x1,%eax
                0x080484f1 :  call   0x7f8a1a0
                0x080484f6 :  mov    %ebx,%eax
                0x080484f8 :  add    $0x10,%esp
                0x080484fb :  pop    %ebx
                0x080484fc :  pop    %ebp
                0x080484fd :  ret
                0x080484fe :  nop
                0x080484ff :  nop
                0x08048500 :  nop
                0x08048501 :  push   %ebp
                0x08048502 :  mov    %esp,%ebp
                0x08048504 :  push   %ebx
                0x08048505 :  sub    $0xc,%esp
                0x08048508 :  mov    0x4(%eax),%ecx
                0x0804850b :  mov    (%ecx),%edx
                0x0804850d :  cmp    %eax,%edx
                0x0804850f :  je     0x8048529
                0x08048511 :  mov    %edx,0x8(%esp)
                0x08048515 :  mov    %eax,0x4(%esp)
                0x08048519 :  movl   $0xc06b32be,(%esp)
                0x08048520 :  add    %ah,0xa70
                End of assembler dump.
                (gdb)

          OK, 现在你知道出错的那条指令是array[43],也就是mov    (%ecx),%edx,也就是说,(%ecx)指向了一个错误内存地址。

补充:

为了使汇编代码和C代码更好的对应起来, Linux内核的Kbuild子系统提供了这样一个功能: 任何一个C文件都可以单独编译成汇编文件,例如:

make path/to/the/sourcefile.s

例如我想把kernel/sched.c编译成汇编,那么:

make kernel/sched.s V=1

或者:

make kernel/sched.lst V=1

         编译出*.s文件
          
           有时侯需要对*.s文件进行分析,以确定BUG所在的位置。 对任何一个内核build目录下的*.c文件,都可以
           直接编译出*.s文件。

                   # make drivers/net/e100.s V=1
          
           而对于自己写的module,就需要在Makefile中有一个灵活的target写法:
                  
                # cat Makefile
                obj-m := usb-skel.o
                KDIR := /lib/modules/`uname -r`/build
                traget := modules

                default:
                        make -C $(KDIR) M=$(shell pwd) $(target)
                clean:
                        rm -f *.o *.ko .*.cmd *.symvers *.mod.c
                        rm -rf .tmp_versions


                # make target=usb-skel.s V=1
          
           这样,kbuild系统才知道你要make的目标不是modules,而是usb-skel.s。

另外, 内核源代码目录的./scripts/decodecode文件是用来解码Oops的:

./scripts/decodecode < Oops.txt
(我没用过,就只提一下。)

跟踪module中的oops:
如果是module出现了oops,我们没有编译的内核,不好按照上面的方法跟踪。

Aug 15 15:56:11 localhost kernel: BUG: unable to handle kernel NULL pointer dereference at virtual address 00000004
Aug 15 15:56:11 localhost kernel:  printing eip:
Aug 15 15:56:11 localhost kernel: f8922523
Aug 15 15:56:11 localhost kernel: *pde = 00000000
Aug 15 15:56:11 localhost kernel: Oops: 0000 [#1]
Aug 15 15:56:11 localhost kernel: SMP
Aug 15 15:56:11 localhost kernel: last sysfs file: /class/net/eth1/address
Aug 15 15:56:11 localhost kernel: Modules linked in: e1000(U) dum(U) autofs4 hidp rfcomm l2cap bluetooth sunrpc dm_multipath video sbs i2c_ec button battery asus_acpi ac ipv6 parport_pc lp parport intel_rng i2c_i801 tg3 i2c_core serio_raw pcspkr dm_snapshot dm_zero dm_mirror dm_mod ata_piix libata sd_mod scsi_mod ext3 jbd ehci_hcd ohci_hcd uhci_hcd
Aug 15 15:56:11 localhost kernel: CPU:    1
Aug 15 15:56:11 localhost kernel: EIP:    0060:[]    Tainted: GF     VLI
Aug 15 15:56:11 localhost kernel: EFLAGS: 00010246   (2.6.18-1.2798.fc6 #1)
Aug 15 15:56:11 localhost kernel: EIP is at e1000_unmap_and_free_tx_resource+0x3/0x40 [e1000]
Aug 15 15:56:11 localhost kernel: eax: f4458400   ebx: 00000000   ecx: 00000100   edx: 00000000
Aug 15 15:56:11 localhost kernel: esi: f4458400   edi: 00000001   ebp: f4458000   esp: f7feef20
Aug 15 15:56:11 localhost kernel: ds: 007b   es: 007b   ss: 0068
Aug 15 15:56:11 localhost kernel: Process events/1 (pid: 9, ti=f7fee000 task=c2f40070 task.ti=f7fee000)
Aug 15 15:56:11 localhost kernel: Stack: f48fb2c0 f8922584 00000034 00000001 f4458400 f8923d45 f4458400 f445849c
Aug 15 15:56:11 localhost kernel:        c2f29b40 00000282 f8924e99 00000003 00000092 f44584c8 00000282 00000282
Aug 15 15:56:11 localhost kernel:        f4458498 c0433c30 00000000 c2f29b40 c2f29b60 f8924ef0 f4458498 c2f29b60
Aug 15 15:56:11 localhost kernel: Call Trace:
Aug 15 15:56:11 localhost kernel:  [] e1000_clean_tx_ring+0x24/0xa0 [e1000]
Aug 15 15:56:11 localhost kernel:  [] e1000_down+0x125/0x1b0 [e1000]
Aug 15 15:56:11 localhost kernel:  [] e1000_reinit_locked+0x39/0x90 [e1000]
Aug 15 15:56:11 localhost kernel:  [] run_workqueue+0x83/0xc5
Aug 15 15:56:11 localhost kernel:  [] worker_thread+0xd9/0x10d
Aug 15 15:56:11 localhost kernel:  [] kthread+0xc0/0xed
Aug 15 15:56:11 localhost kernel:  [] kernel_thread_helper+0x7/0x10


使用gdb加载e1000.ko:
root@dell-desktop:/home/dell/libppf/e1000-7.6.5/src# gdb ./e1000.ko
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) b *e1000_unmap_and_free_tx_resource+0x3b
Breakpoint 1 at 0x260b: file /home/dell/libppf/e1000-7.6.5/src/e1000_main.c, line 2637.
(gdb) l /home/dell/libppf/e1000-7.6.5/src/e1000_main.c:2637
2632             */
2633    #if 0
2634            dev_kfree_skb_any(buffer_info->skb);
2635    #else
2636            if (XXX) {
2637                YYY ();
2640            } else
2641                dev_kfree_skb_any(buffer_info->skb);

我们就找到了出问题的行号了 :-)
阅读(1749) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~