⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 (ldd) ch04-调试技术(转载).txt

📁 献给ARM初学者
💻 TXT
📖 第 1 页 / 共 4 页
字号:
      页边界(Alpha上的页面大小是8KB,缓冲区正好在页面的起始位置附近)。如果在你的
      系统上读取faulty没有产生oops,试试wc,或者给dd显式地指定块大小。
       
      使用ksymoops
      oops消息的最大问题就是十六进制数值对于程序员来说没什么意义;需要将它们解析为
      符号。
       
      内核源码通过其所包含的ksymoops工具帮助开发人员――但是注意,版本1.2的源码中没
      有这个程序。该工具将oops消息中的数值地址解析为内核符号,但只限于PC机产生的oop
      s消息。由于消息本身就是处理器相关的,每一体系结构都有其自身的消息格式。
       
      ksymoops从标准输入获得oops消息,并从命令行内核符号表的名字。符号表通常就是/us

      ksymoops从标准输入获得oops消息,并从命令行内核符号表的名字。符号表通常就是/us
      r/src/linux/System.map。程序以更可读的方式打印调用轨迹和程序代码,而不是最原
      始的oops消息。下面的片断就是用上一节的oops消息得出的结果:
       
      (代码)
       
      由ksymoops反汇编出的代码给出了失效的指令和其后的指令。很明显――对于那些知道
      一点汇编的人――repz movsl指令(REPeat till cx is Zero, MOVe a String of
      Longs)用源索引(esi,是0x202e000)访问了一个未映射页面。用来获得模块信息的ks
      ymoops -m命令给出,模块映射到一个在0x0202dxxx的页面上,这也确认乐esi确实超出
      了范围。
       
      由于faulty模块所占用的内存不在系统表中,被解码的调用轨迹还给出了两个数值地址
      。这些值可以手动补充,或是通过ksyms命令的输出,或是在/proc/ksyms中查询模块的
      名字。
       
      然而对于这个失效,这两个地址并不对应与代码地址。如果你看了arch/i386/kernel/tr
      aps.c,你就发现,调用轨迹是从整个堆栈并利用一些启发式方法区分数据值(本地变量
      和函数参数)和返回地址获得的。调用轨迹中只给出了引用内核代码的地址和引用模块
      的地址。由于模块所占页面既有代码也有数据,错综复杂的栈可能会漏掉启发式信息,
      这就是上面两个0x202xxxx地址的情况。
       
      如果你不愿手动查看模块地址,下面这组管道可以用来创建一个既有内核又有模块符号

      如果你不愿手动查看模块地址,下面这组管道可以用来创建一个既有内核又有模块符号
      的符号表。无论何时你加载模块,你都必须重新创建这个符号表。
       
      (代码)
       
      这个管道将完整的系统表与/proc/ksyms中的公开内核符号混合在一起,后者除了内核符
      号外,还包括了当前内核里的模块符号。这些地址在insmod重定位代码后就出现在/proc
      /ksyms中。由于这两个文件的格式不同,使用了sed和awk将所有的文本行转换为一种合
      适的格式。然后对这张表排序,去除重复部分,这样ksymoops就可以用了。
       
      如果我们重新运行ksymoops,它从新的符号表中截取出如下信息:
       
      (代码)
       
      正如你所见到的,当跟踪与模块有关的oops消息时,创建一个修订的系统表是很有助益
      的:现在ksymoops能够对指令指针解码并完成整个调用轨迹了。还要注意,显式反汇编
      码的格式和objdump所使用的格式一样。objdump也是一个功能强大的工具;如果你需要
      查看失败前的指令,你调用命令objdump –d faulty.o。
       
      在文件的汇编列表中,字串faulty_read+45/60标记为失效行。有关objdump的更多的信
      息和它的命令行选项可以参见该命令的手册。
       
      即便你构建了你自己的修订版符号表,上面提到的有关调用轨迹的问题仍然存在:虽然0

      即便你构建了你自己的修订版符号表,上面提到的有关调用轨迹的问题仍然存在:虽然0
      x202xxxx指针被解码了,但仍然是假的。
       
      学会解码oops消息需要一定的经验,但是确实值得一做。用来学习的时间很快就会有所
      回报。不过由于机器指令的Unix语法与Intel语法不同,唯一的问题在于从哪获得有关汇
      编语言的文档;尽管你了解PC汇编语言,但你的经验都是用Intel语法的编程获得的。在
      参考书目中,我给一些有所补益的书籍。
       
      使用oops
      使用ksymoops有些繁琐。你需要C++编译器编译它,你还要构建你自己的符号表来充分发
      挥程序的能力,你还要将原始消息和ksymoops输出合在一起组成可用的信息。
       
      如果你不想找这么多麻烦,你可以使用oops程序。oops在本书的O’Reilly FTP站点给出
      的源码中。它源自最初的ksymoops工具,现在它的作者已经不维护这个工具了。oops是
      用C语言写成的,而且直接查看/proc/ksyms而无需用户每次加载模块后构建新的符号表
       
      该程序试图解码所有的处理器寄存器并 颜 轨迹解析为符号值。它的缺点是,它要比ksy
      moops罗嗦些,但通常你所有的信息越多,你发现错误也就越快。oops的另一个优点是,
      它可以解析x86,Alpha和Sparc的oops消息。与内核源码相同,这个程序也按GPL发行。
       
      oops产生的输出与ksymoops的类似,但是更完全。这里给出前一个oops输出的开始部分
      —由于在这个oops消息中堆栈没保存什么有用的东西,我不认为应该显示整个 颜 轨迹

      —由于在这个oops消息中堆栈没保存什么有用的东西,我不认为应该显示整个 颜 轨迹
       
      (代码)
       
      当你调试“真正的”模块(faulty太短了,没有什么意义)时,将寄存器和堆栈解码是
      非常有益的,而且如果被调试的所有模块符号都开放出来时更有帮助。在失效时,处理
      器寄存器一般不会指向模块的符号,只有当符号表开放给/proc/ksyms时,你才能输出中
      标别它们。
       
      我们可以用一下步骤制作一张更完整的符号表。首先,我们不应在模块中声明静态变量
      ,否则我们就无法用insmod开放它们了。第二,如下面的截取自scull的init_module函
      数的代码所示,我们可以用#ifdef SCULL_DEBUG或类似的宏屏蔽register_symtab调用。
       
       
      (代码)
       
      我们在第2章“编写和运行模块”的“注册符号表”一节中已经看到了类似内容,那里说
      ,如果模块不注册符号表,所有的全局符号就都开放。尽管这一功能仅在SCULL_DEBUG被
      激活时才有效,为了避免内核中的名字空间污染,所有的全局符号有合适的前缀(参见
      第2章的“模块与应用程序”一节)。
       
      使用klogd

      使用klogd
      klogd守护进程的近期版本可以在oops存放到记录文件前对oops消息解码。解码过程只由
      版本1.3或更新版本的守护进程完成,而且只有将-k /usr/src/linux/System.map做为参
      数传递给守护进程时才解码。(你可以用其他符号表文件代替System.map)
       
      有新的klogd给出的faulty的oops如下所示,它写到了系统记录中:
       
      (代码)
       
      我想能解码的klogd对于调试一般的Linux安装的核心来说是很好的工具。由klogd解码的
      消息包括大部分ksymoops的功能,而且也要求用户编译额外的工具,或是,当系统出现
      故障时,为了给出完整的错误报告而合并两个输出。当oops发生在内核时,守护进程还
      会正确地解码指令指针。它并不反汇编代码,但这不是问题,当错误报告给出消息时,
      二进制数据仍然存在,可以离线反汇编代码。
       
      守护进程的另一个功能就是,如果符号表版本与当前内核不匹配,它会拒绝解析符号。
      如果在系统记录中解析出了符号,你可以确信它是正确的解码。
       
      然而,尽管它对Linux用户很有帮助,这个工具在调试模块时没有什么帮助。我个人没有
      在开放软件的电脑里使用解码选项。klogd的问题是它不解析模块中的符号;因为守护进
      程在程序员加载模块前就已经运行了,即使读了/proc/ksyms也不会有什么帮助。记录文
      件中存在解析后的符号会使oops和ksymoops混淆,造成进一步解析的困难。
       

       
      如果你需要使用klogd调试你的模块,最新版本的守护进程需要加入一些新的特殊支持,
      我期待它的完成,只要给内核打一个小补丁就可以了。
       
      系统挂起
      尽管内核代码中的大多数错误仅会导致一个oops消息,有时它们困难完全将系统挂起。
      如果系统挂起了,没有消息能够打印出来。例如,如果代码遇到一个死循环,内核停止
      了调度过程,系统不会再响应任何动作,包括魔法键Ctrl-Alt-Del组合。
       
      处理系统挂起有两个选择――一个是防范与未然,另一个就是亡羊补牢,在发生挂起后
      调试代码。
       
      通过在策略点上插入schedule调用可以防止死循环。schedule调用(正如你所猜想到的
      )调用调度器,因此允许其他进程偷取当然进程的CPU时间。如果进程因你的驱动程序中
      的错误而在内核空间循环,你可以在跟踪到这种情况后杀掉这个进程。
       
      在驱动程序代码中插入schedule调用会给程序员带来新的“问题”:函数,,以及调用轨
      迹中的所有函数,必须是可重入的。在正常环境下,由于不同的进程可能并发地访问设
      备,驱动程序做为整体是可重入的,但由于Linux内核是不可抢占的,不必每个函数都是
      可重入的。但如果驱动程序函数允许调度器中断当前进程,另一个不同的进程可能会进
      入同一个函数。如果schedule调用仅在调试期间打开,如果你不允许,你可以避免两个
      并发进程访问驱动程序,所以并发性倒不是什么非常重要的问题。在介绍阻塞型操作时
      (第5章的“写可重入代码”)我们再详细介绍并发性问题。

      (第5章的“写可重入代码”)我们再详细介绍并发性问题。
       
      如果要调试死循环,你可以利用Linux键盘的特殊键。默认情况下,如果和修饰键一起按
      了PrScr键(键码是70),系统会向当前控制台打印有关机器状态的有用信息。这一功能
      在x86和Alpha系统都有。Linux的Sparc移植也有同样的功能,但它使用了标记为“Break
      /Scroll Lock”的键(键码是30)。
       
      每一个特殊函数都有一个名字,并如下面所示都有一个按键事件与之对应。组合键之后
      的括号里是函数名。
       
      Shift-PrScr(Show_Memory)
       
      打印若干行关于内存使用的信息,尤其是有关缓冲区高速缓存的使用情况。

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -