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

📄 1161.html

📁 著名的linux英雄站点的文档打包
💻 HTML
📖 第 1 页 / 共 5 页
字号:
[size=18:6ddc15f4ad]示例[/size:6ddc15f4ad]<br>
[code:1:6ddc15f4ad]反汇编从例程 schedule 开始的指令。所显示的行数取决于环境变量 IDCOUNT:<br>
[0]kdb&gt; id schedule <br>
执行指令直到它遇到分支转移条件(在本例中为指令 jne)为止:<br>
[0]kdb&gt; ssb<br>
<br>
0xc0105355 default_idle+0x25: cli<br>
0xc0105356 default_idle+0x26: mov 0x14(%edx),%eax<br>
0xc0105359 default_idle+0x29: test %eax, %eax<br>
0xc010535b default_idle+0x2b: jne 0xc0105361 default_idle+0x31 [/code:1:6ddc15f4ad]<br>
[size=18:6ddc15f4ad]技巧和诀窍[/size:6ddc15f4ad]<br>
调试一个问题涉及到:使用调试器(或任何其它工具)找到问题的根源以及使用源代码来跟踪导致问题的根源。单单使用源代码来确定问题是极其困难的,只有老练的内核黑客才有可能做得到。相反,大多数的新手往往要过多地依靠调试器来修正错误。这种方法可能会产生不正确的问题解决方案。我们担心的是这种方法只会修正表面症状而不能解决真正的问题。此类错误的典型示例是添加错误处理代码以处理 NULL 指针或错误的引用,却没有查出无效引用的真正原因。<br>
结合研究代码和使用调试工具这两种方法是识别和修正问题的最佳方案。<br>
调试器的主要用途是找到错误的位置、确认症状(在某些情况下还有起因)、确定变量的值,以及确定程序是如何出现这种情况的(即,建立调用堆栈)。有经验的黑客会知道对于某种特定的问题应使用哪一个调试器,并且能迅速地根据调试获取必要的信息,然后继续分析代码以识别起因。<br>
因此,这里为您介绍了一些技巧,以便您能使用 KDB 快速地取得上述结果。当然,要记住,调试的速度和精确度来自经验、实践和良好的系统知识(硬件和内核内部机理等)。<br>
[size=18:6ddc15f4ad]技巧 #1[/size:6ddc15f4ad]<br>
在 KDB 中,在提示处输入地址将返回与之最为匹配的符号。这在堆栈分析以及确定全局数据的地址/值和函数地址方面极其有用。同样,输入符号名则返回其虚拟地址。<br>
示例<br>
[code:1:6ddc15f4ad]表明函数 sys_read 从地址 0xc013db4c 开始:<br>
[0]kdb&gt; 0xc013db4c<br>
<br>
0xc013db4c = 0xc013db4c (sys_read) <br>
同样,<br>
同样,表明 sys_write 位于地址 0xc013dcc8:<br>
[0]kdb&gt; sys_write<br>
<br>
sys_write = 0xc013dcc8 (sys_write) <br>
这些有助于在分析堆栈时找到全局数据和函数地址。[/code:1:6ddc15f4ad]<br>
[size=18:6ddc15f4ad]技巧 #2[/size:6ddc15f4ad]在编译带 KDB 的内核时,只要  CONFIG_FRAME_POINTER 选项出现就使用该选项。为此,需要在配置内核时选择“Kernel hacking”部分下面的 “Compile the kernel with frame pointers”选项。这确保了帧指针寄存器将被用作帧指针,从而产生正确的回溯。实际上,您可以手工转储帧指针寄存器的内容并跟踪整个堆栈。例如,在 i386 机器上,%ebp 寄存器可以用来回溯整个堆栈。<br>
例如,在函数 rmqueue() 上执行第一个指令后,堆栈看上去类似于下面这样:<br>
[code:1:6ddc15f4ad][0]kdb&gt; md %ebp<br>
<br>
0xc74c9f38 c74c9f60 c0136c40 000001f0 00000000<br>
0xc74c9f48 08053328 c0425238 c04253a8 00000000<br>
0xc74c9f58 000001f0 00000246 c74c9f6c c0136a25<br>
0xc74c9f68 c74c8000 c74c9f74 c0136d6d c74c9fbc<br>
0xc74c9f78 c014fe45 c74c8000 00000000 08053328<br>
<br>
[0]kdb&gt; 0xc0136c40<br>
<br>
0xc0136c40 = 0xc0136c40 (__alloc_pages +0x44)<br>
<br>
[0]kdb&gt; 0xc0136a25<br>
<br>
0xc0136a25 = 0xc0136a25 (_alloc_pages +0x19)<br>
<br>
[0]kdb&gt; 0xc0136d6d<br>
<br>
0xc0136d6d = 0xc0136d6d (__get_free_pages +0xd)[/code:1:6ddc15f4ad] <br>
我们可以看到 rmqueue() 被 __alloc_pages 调用,后者接下来又被 _alloc_pages 调用,以此类推。<br>
每一帧的第一个双字(double word)指向下一帧,这后面紧跟着调用函数的地址。因此,跟踪堆栈就变成一件轻松的工作了。<br>
[size=18:6ddc15f4ad]技巧 #3[/size:6ddc15f4ad]<br>
go 命令可以有选择地以一个地址作为参数。如果您想在某个特定地址处继续执行,则可以提供该地址作为参数。另一个办法是使用 rm 命令修改指令指针寄存器,然后只要输入 go。如果您想跳过似乎会引起问题的某个特定指令或一组指令,这就会很有用。但是,请注意,该指令使用不慎会造成严重的问题,系统可能会严重崩溃。<br>
[size=18:6ddc15f4ad]技巧 #4[/size:6ddc15f4ad]<br>
您可以利用一个名为 defcmd 的有用命令来定义自己的命令集。例如,每当遇到断点时,您可能希望能同时检查某个特殊变量、检查某些寄存器的内容并转储堆栈。通常,您必须要输入一系列命令,以便能同时执行所有这些工作。defcmd 允许您定义自己的命令,该命令可以包含一个或多个预定义的  KDB 命令。然后只需要用一个命令就可以完成所有这三项工作。其语法如下:<br>
[code:1:6ddc15f4ad][0]kdb&gt; defcmd name "usage" "help"<br>
<br>
[0]kdb&gt; [defcmd] type the commands here<br>
<br>
[0]kdb&gt; [defcmd] endefcmd [/code:1:6ddc15f4ad]<br>
例如,可以定义一个(简单的)新命令 hari,它显示从地址 0xc000000 开始的一行内存、显示寄存器的内容并转储堆栈:<br>
[code:1:6ddc15f4ad][0]kdb&gt; defcmd hari "" "no arguments needed"<br>
<br>
[0]kdb&gt; [defcmd] md 0xc000000 1<br>
<br>
[0]kdb&gt; [defcmd] rd<br>
<br>
[0]kdb&gt; [defcmd] md %ebp 1<br>
<br>
[0]kdb&gt; [defcmd] endefcmd [/code:1:6ddc15f4ad]<br>
该命令的输出会是:<br>
[code:1:6ddc15f4ad][0]kdb&gt; hari<br>
<br>
[hari]kdb&gt; md 0xc000000 1<br>
<br>
0xc000000 00000001 f000e816 f000e2c3 f000e816<br>
<br>
[hari]kdb&gt; rd<br>
<br>
eax = 0x00000000 ebx = 0xc0105330 ecx = 0xc0466000 edx = 0xc0466000<br>
....<br>
...<br>
<br>
[hari]kdb&gt; md %ebp 1<br>
<br>
0xc0467fbc c0467fd0 c01053d2 00000002 000a0200<br>
<br>
[0]kdb&gt; [/code:1:6ddc15f4ad]<br>
[size=18:6ddc15f4ad]技巧 #5[/size:6ddc15f4ad]<br>
可以使用 bph 和 bpha 命令(假如体系结构支持使用硬件寄存器)来应用读写断点。这意味着每当从某个特定地址读取数据或将数据写入该地址时,我们都可以对此进行控制。当调试数据/内存毁坏问题时这可能会极其方便,在这种情况中您可以用它来识别毁坏的代码/进程。<br>
示例<br>
[code:1:6ddc15f4ad]每当将四个字节写入地址 0xc0204060 时就进入内核调试器:<br>
[0]kdb&gt; bph 0xc0204060 dataw 4 <br>
在读取从 0xc000000 开始的至少两个字节的数据时进入内核调试器:<br>
[0]kdb&gt; bph 0xc000000 datar 2[/code:1:6ddc15f4ad] <br>
[size=18:6ddc15f4ad]结束语[/size:6ddc15f4ad]<br>
对于执行内核调试,KDB 是一个方便的且功能强大的工具。它提供了各种选项,并且使我们能够分析内存内容和数据结构。最妙的是,它不需要用另一台机器来执行调试。<br>
[size=18:6ddc15f4ad]参考资料[/size:6ddc15f4ad] <br>
?请在 Documentation/kdb 目录中查找 KDB 手册页。<br>
?有关设置串行控制台的信息,请查找 Documentation 目录中的 serial-console.txt。<br>
?请在 SGI 的内核调试器项目网站上下载 KDB。<br>
?有关几个基于方案的 Linux 调试技术的概述,请阅读“掌握 Linux 调试技术”(developerWorks,2002 年 8 月)。<br>
?教程“编译 Linux 内核”(developerWorks,2000 年 8 月)让您完整地了解配置、编译和安装内核的过程。<br>
?IBM AIX 用户可以在 KDB Kernel Debugger and Command 页面上获取有关用于 AIX 的 KDB 的使用帮助。<br>
?那些寻求有关调试 OS/2 信息的读者应该阅读 IBM 红皮书 The OS/2 Debugging Handbook(共四卷)的第 II 卷。<br>
?在 developerWorks Linux 专区中查找更多针对 Linux 开发人员的参考资料。<br>
<br>
【发表回复】【查看CU论坛原帖】【关闭】<br>
 zhchhui 回复于:2003-09-15 10:38:56<br>
掌握 Linux 调试技术  <br>
内容:<br>
常见调试方法<br>
第 1 种情况:内存调试工具<br>
MEMWATCH<br>
YAMD<br>
Electric Fence<br>
第 2 种情况:使用 strace<br>
第 3 种情况:使用 gdb 和 Oops<br>
kgdb<br>
Oops 分析<br>
kdb<br>
第 4 种情况:使用魔术键控顺序获取反跟踪<br>
结束语<br>
<br>
 zhchhui 回复于:2003-09-15 10:42:18<br>
在 Linux 上找出并解决程序错误的主要方法<br>
Steve Best(sbest@us.ibm.com)<br>
JFS 核心小组成员,IBM<br>
2002 年 8 月<br>
您可以用各种方法来监控运行着的用户空间程序:可以为其运行调试器并单步调试该程序,添加打印语句,或者添加工具来分析程序。本文描述了几种可以用来调试在 Linux 上运行的程序的方法。我们将回顾四种调试问题的情况,这些问题包括段错误,内存溢出和泄漏,还有挂起。<br>
本文讨论了四种调试 Linux 程序的情况。在第 1 种情况中,我们使用了两个有内存分配问题的样本程序,使用 MEMWATCH 和  Yet Another Malloc Debugger(YAMD)工具来调试它们。在第 2 种情况中,我们使用了 Linux 中的  strace 实用程序,它能够跟踪系统调用和信号,从而找出程序发生错误的地方。在第 3 种情况中,我们使用 Linux 内核的 Oops 功能来解决程序的段错误,并向您展示如何设置内核源代码级调试器(kernel source level debugger,kgdb),以使用 GNU  调试器(GNU debugger,gdb)来解决相同的问题;kgdb 程序是使用串行连接的 Linux 内核远程 gdb。在第 4 种情况中,我们使用 Linux 上提供的魔术键控顺序(magic key sequence)来显示引发挂起问题的组件的信息。<br>
[size=18:b0b26de3a8][b:b0b26de3a8]常见调试方法[/b:b0b26de3a8][/size:b0b26de3a8]<br>
当您的程序中包含错误时,很可能在代码中某处有一个条件,您认为它为真(true),但实际上是假(false)。找出错误的过程也就是在找出错误后推翻以前一直确信为真的某个条件过程。<br>
以下几个示例是您可能确信成立的条件的一些类型: <br>
?在源代码中的某处,某变量有特定的值。 <br>
?在给定的地方,某个结构已被正确设置。 <br>
?对于给定的 if-then-else 语句,if 部分就是被执行的路径。 <br>
?当子例程被调用时,该例程正确地接收到了它的参数。 <br>
找出错误也就是要确定上述所有情况是否存在。如果您确信在子例程被调用时某变量应该有特定的值,那么就检查一下情况是否如此。如果您相信 if  结构会被执行,那么也检查一下情况是否如此。通常,您的假设都会是正确的,但最终您会找到与假设不符的情况。结果,您就会找出发生错误的地方。<br>
调试是您无法逃避的任务。进行调试有很多种方法,比如将消息打印到屏幕上、使用调试器,或只是考虑程序执行的情况并仔细地揣摩问题所在。<br>
在修正问题之前,您必须找出它的源头。举例来说,对于段错误,您需要了解段错误发生在代码的哪一行。一旦您发现了代码中出错的行,请确定该方法中变量的值、方法被调用的方式以及关于错误如何发生的详细情况。使用调试器将使找出所有这些信息变得很简单。如果没有调试器可用,您还可以使用其它的工具。(请注意,产品环境中可能并不提供调试器,而且 Linux 内核没有内建的调试器。)<br>
[size=18:b0b26de3a8][b:b0b26de3a8]实用的内存和内核工具[/b:b0b26de3a8][/size:b0b26de3a8]<br>
您可以使用 Linux 上的调试工具,通过各种方式跟踪用户空间和内核问题。请使用下面的工具和技术来构建和调试您的源代码: <br>
[size=18:b0b26de3a8][b:b0b26de3a8]用户空间工具[/b:b0b26de3a8][/size:b0b26de3a8]: <br>
?内存工具:MEMWATCH 和 YAMD <br>
?strace <br>
?GNU 调试器(gdb) <br>
?魔术键控顺序 <br>
[size=18:b0b26de3a8][b:b0b26de3a8]内核工具[/b:b0b26de3a8][/size:b0b26de3a8]: <br>
?内核源代码级调试器(kgdb) <br>
?内建内核调试器(kdb) <br>
?Oops <br>
本文将讨论一类通过人工检查代码不容易找到的问题,而且此类问题只在很少见的情况下存在。内存错误通常在多种情况同时存在时出现,而且您有时只能在部署程序之后才能发现内存错误。<br>
<br>
 zhchhui 回复于:2003-09-15 10:46:42<br>
[size=18:ff78191c7b][b] 第 1 种情况:内存调试工具[/b[/size:ff78191c7b]]<br>
C 语言作为 Linux 系统上标准的编程语言给予了我们对动态内存分配很大的控制权。然而,这种自由可能会导致严重的内存管理问题,而这些问题可能导致程序崩溃或随时间的推移导致性能降级。<br>
内存泄漏(即 malloc() 内存在对应的 free() 调用执行后永不被释放)和缓冲区溢出(例如对以前分配到某数组的内存进行写操作)是一些常见的问题,它们可能很难检测到。这一部分将讨论几个调试工具,它们极大地简化了检测和找出内存问题的过程。<br>
[color=blue:ff78191c7b]MEMWATCH[/color:ff78191c7b]<br>
MEMWATCH 由 Johan Lindh 编写,是一个开放源代码 C 语言内存错误检测工具,您可以自己下载它(请参阅本文后面部分的参考资料)。只要在代码中添加一个头文件并在 gcc 语句中定义了 MEMWATCH 之后,您就可以跟踪程序中的内存泄漏和错误了。MEMWATCH  支持 ANSI C,它提供结果日志纪录,能检测双重释放(double-free)、错误释放(erroneous free)、没有释放的内存(unfreed memory)、溢出和下溢等等。<br>
清单 1. 内存样本(test1.c) <br>
<br>
[code:1:ff78191c7b]#include &lt;stdlib.h&gt;<br>
#include &lt;stdio.h&gt;<br>
#include "memwatch.h"<br>
<br>

⌨️ 快捷键说明

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