Linux系统中找出并解决程序错误方法( 六 )


首先 , 将 Oops 消息保存在一个文件中以便通过 ksymoops 实用程序运行它 。清单 10 显示了由安装 JFS 文件系统的 mount 命令创建的 Oops 消息 , 问题是由清单 8 中添加到 JFS 安装代码的那三行代码产生的 。

CODE:ksymoops 2.4.0 on i686 2.4.17. Options used
... 15:59:37 sfb1 kernel: Unable to handle kernel NULL pointer dereference at
virtual address 0000000
... 15:59:37 sfb1 kernel: c01588fc
... 15:59:37 sfb1 kernel: *pde = 0000000
... 15:59:37 sfb1 kernel: Oops: 0000
... 15:59:37 sfb1 kernel: CPU: 0
... 15:59:37 sfb1 kernel: EIP: 0010:[jfs_mount 60/704]
... 15:59:37 sfb1 kernel: Call Trace: [jfs_read_super 287/688]
[get_sb_bdev 563/736] [do_kern_mount 189/336] [do_add_mount 35/208]
[do_page_fault 0/1264]
... 15:59:37 sfb1 kernel: Call Trace: []...
... 15:59:37 sfb1 kernel: [>EIP; c01588fc <=====
...
Trace; c0106cf3;
Code; c01588fc;
00000000 <_EIP>:
Code; c01588fc <=====
0: 8b 2d 00 00 00 00 mov 0x0,雙 <=====
Code; c0158902;
6: 55 push 雙

清单 10. ksymoops 处理后的 Oops 消息
接下来 , 您要确定 jfs_mount 中的哪一行代码引起了这个问题 。Oops 消息告诉我们问题是由位于偏移地址 3c 的指令引起的 。做这件事的办法之一是对 jfs_mount.o 文件使用 objdump 实用程序 , 然后查看偏移地址 3c 。Objdump 用来反汇编模块函数 , 看看您的 C 源代码会产生什么汇编指令 。清单 11 显示了使用 objdump 后您将看到的内容 , 接着 , 我们查看 jfs_mount 的 C 代码 , 可以看到空值是第 109 行引起的 。偏移地址 3c 之所以很重要 , 是因为 Oops 消息将该处标识为引起问题的位置 。

CODE:109 printk("%dn",*ptr);
objdump jfs_mount.o
jfs_mount.o: file format elf32-i386
【Linux系统中找出并解决程序错误方法】Disassembly of section .text:
00000000 :
0:55 push 雙
...
2c: e8 cf 03 00 00 call 400;
31: 89 c3 mov 陎,離
33: 58 pop 陎
34: 85 db test 離,離
36: 0f 85 55 02 00 00 jne 291;
3c: 8b 2d 00 00 00 00 mov 0x0,雙 << problem line above
42: 55 push 雙

清单 11. jfs_mount 的汇编程序清单
kdb
Linux 内核调试器(Linux kernel debugger , kdb)是 Linux 内核的补丁 , 它提供了一种在系统能运行时对内核内存和数据结构进行检查的办法 。请注意 , kdb 不需要两台机器 , 不过它也不允许您像 kgdb 那样进行源代码级别上的调试 。您可以添加额外的命令 , 给出该数据结构的标识或地址 , 这些命令便可以格式化和显示基本的系统数据结构 。目前的命令集允许您控制包括以下操作在内的内核操作:
处理器单步执行;
执行到某条特定指令时停止;
当存取(或修改)某个特定的虚拟内存位置时停止;
当存取输入/输出地址空间中的寄存器时停止;
对当前活动的任务和所有其它任务进行堆栈回溯跟踪(通过进程 ID);
对指令进行反汇编 。
追击内存溢出
您肯定不想陷入类似在几千次调用之后发生分配溢出这样的情形 。我们的小组花了许许多多时间来跟踪稀奇古怪的内存错误问题 。应用程序在我们的开发工作站上能运行 , 但在新的产品工作站上 , 这个应用程序在调用 malloc() 两百万次之后就不能运行了 。真正的问题是在大约一百万次调用之后发生了溢出 。新系统之所有存在这个问题 , 是因为被保留的 malloc() 区域的布局有所不同 , 从而这些零散内存被放置在了不同的地方 , 在发生溢出时破坏了一些不同的内容 。
我们用多种不同技术来解决这个问题 , 其中一种是使用调试器 , 另一种是在源代码中添加跟踪功能 。在我职业生涯的大概也是这个时候 , 我便开始关注内存调试工具 , 希望能更快更有效地解决这些类型的问题 。在开始一个新项目时 , 我最先做的事情之一就是运行 MEMWATCH 和 YAMD , 看看它们是不是会指出内存管理方面的问题 。

推荐阅读