标签 gdb 下的文章

Credit: Moini

作为一个程序员,我知道我肯定会犯错误——怎么可能不犯错!程序员也是人啊。有的错误能在编码过程中及时发现,而有些却得等到软件测试了才能显露出来。然而,还有一类错误并不能在这两个阶段被解决,这就导致软件不能正常运行,甚至是提前终止。

如果你还没猜出是那种错误,我说的就是和内存相关的错误。手动调试这些错误不仅耗时,而且很难发现并纠正。值得一提的是,这种错误很常见,特别是在用 C/C++ 这类允许手动管理内存的语言编写的软件里。

幸运的是,现在有一些编程工具能够帮你在软件程序中找到这些和内存相关的错误。在这些工具集中,我评估了五款支持 Linux 的、流行的、自由开源的内存调试器: Dmalloc 、 Electric Fence 、 Memcheck 、 Memwatch 以及 Mtrace 。在日常编码中,我已经用过这五个调试器了,所以这些评估是建立在我的实际体验之上的。

Dmalloc

开发者:Gray Watson

评估版本:5.5.2

支持的 Linux 版本:所有种类

许可: CC 3.0

Dmalloc 是 Gray Watson 开发的一款内存调试工具。它是作为库来实现的,封装了标准内存管理函数如malloc() , calloc() , free()等,使程序员得以检测出有问题的代码。

cw dmalloc output

Dmalloc

如同工具的网页所示,这个调试器提供的特性包括内存泄漏跟踪、 重复释放内存 double free 错误跟踪、以及 越界写入 fence-post write 检测。其它特性包括报告错误的文件/行号、通用的数据统计记录。

更新内容

5.5.2 版本是一个 bug 修正发行版,修复了几个有关构建和安装的问题。

有何优点

Dmalloc 最大的优点就是高度可配置性。比如说,你可以配置它以支持 C++ 程序和多线程应用。 Dmalloc 还提供一个有用的功能:运行时可配置,这表示在 Dmalloc 执行时,可以轻易地启用或者禁用它提供的一些特性。

你还可以配合 GNU Project Debugger (GDB)来使用 Dmalloc ,只需要将dmalloc.gdb文件(位于 Dmalloc 源码包中的 contrib 子目录里)的内容添加到你的主目录中的.gdbinit文件里即可。

另外一个让我对 Dmalloc 爱不释手的优点是它有大量的资料文献。前往官网的 Documentation 栏目,可以获取所有关于如何下载、安装、运行、怎样使用库,和 Dmalloc 所提供特性的细节描述,及其生成的输出文件的解释。其中还有一个章节介绍了一般问题的解决方法。

注意事项

跟 Mtrace 一样, Dmalloc 需要程序员改动他们的源代码。比如说你可以(也是必须的)添加头文件dmalloc.h,工具就能汇报产生问题的调用的文件或行号。这个功能非常有用,因为它节省了调试的时间。

除此之外,还需要在编译你的程序时,把 Dmalloc 库(编译 Dmalloc 源码包时产生的)链接进去。

然而,还有点更麻烦的事,需要设置一个环境变量,命名为DMALLOC_OPTION,以供工具在运行时配置内存调试特性,比如定义输出文件的路径。可以手动为该环境变量分配一个值,不过初学者可能会觉得这个过程有点困难,因为该值的一部分用来表示要启用的 Dmalloc 特性——以十六进制值的累加值表示。这里有详细介绍。

一个比较简单方法设置这个环境变量是使用 Dmalloc 实用指令,这是专为这个目的设计的方法。

总结

Dmalloc 真正的优势在于它的可配置选项。而且高度可移植,曾经成功移植到多种操作系统如 AIX 、 BSD/OS 、 DG/UX 、 Free/Net/OpenBSD 、 GNU/Hurd 、 HPUX 、 Irix 、 Linux 、 MS-DOG 、 NeXT 、 OSF 、 SCO 、 Solaris 、 SunOS 、 Ultrix 、 Unixware 甚至 Unicos(运行在 Cray T3E 主机上)。虽然使用 Dmalloc 需要学习许多知识,但是它所提供的特性值得为之付出。

Electric Fence

开发者:Bruce Perens

评估版本:2.2.3

支持的 Linux 版本:所有种类

许可:GPL v2

Electric Fence 是 Bruce Perens 开发的一款内存调试工具,它以库的形式实现,你的程序需要链接它。Electric Fence 能检测出内存溢出和访问已经释放的内存。

cw electric fence output

Electric Fence

顾名思义, Electric Fence 在每个所申请的缓存边界建立了虚拟围栏,这样一来任何非法的内存访问都会导致段错误。这个调试工具同时支持 C 和 C++ 程序。

更新内容

2.2.3 版本修复了工具的构建系统,使得 -fno-builtin-malloc 选项能真正传给 GNU Compiler Collection (GCC)

有何优点

我喜欢 Electric Fence 的首要一点是它不同于 Memwatch 、 Dmalloc 和 Mtrace ,不需要对你的源码做任何的改动,你只需要在编译的时候把它的库链接进你的程序即可。

其次, Electric Fence 的实现保证了产生越界访问的第一个指令就会引起段错误。这比在后面再发现问题要好多了。

不管是否有检测出错误, Electric Fence 都会在输出产生版权信息。这一点非常有用,由此可以确定你所运行的程序已经启用了 Electric Fence 。

注意事项

另一方面,我对 Electric Fence 真正念念不忘的是它检测内存泄漏的能力。内存泄漏是 C/C++ 软件最常见也是最不容易发现的问题之一。不过, Electric Fence 不能检测出栈溢出,而且也不是线程安全的。

由于 Electric Fence 会在用户分配内存区的前后分配禁止访问的虚拟内存页,如果你过多的进行动态内存分配,将会导致你的程序消耗大量的额外内存。

Electric Fence 还有一个局限是不能明确指出错误代码所在的行号。它所能做只是在检测到内存相关错误时产生段错误。想要定位错误的行号,需要借助 GDB这样的调试工具来调试启用了 Electric Fence 的程序。

最后一点,尽管 Electric Fence 能检测出大部分的缓冲区溢出,有一个例外是,如果所申请的缓冲区大小不是系统字长的倍数,这时候溢出(即使只有几个字节)就不能被检测出来。

总结

尽管局限性较大, Electric Fence 的易用性仍然是加分项。只要链接一次程序, Electric Fence 就可以在监测出内存相关问题的时候报警。不过,如同前面所说, Electric Fence 需要配合像 GDB 这样的源码调试器使用。

Memcheck

开发者Valgrind 开发团队

评估版本:3.10.1

支持的 Linux 发行版:所有种类

许可:GPL

Valgrind 是一个提供好几款调试和分析 Linux 程序性能的工具的套件。虽然 Valgrind 能和不同语言——Java 、 Perl 、 Python 、 Assembly code 、 ortran 、 Ada 等——编写的程序一起工作,但是它主要还是针对使用 C/C++ 所编写的程序。

Memcheck ,一款内存错误检测器,是其中最受欢迎的工具。它能够检测出如内存泄漏、无效的内存访问、未定义变量的使用以及堆内存分配和释放相关的问题等诸多问题。

更新内容

工具套件( 3.10.1 )主要修复了 3.10.0 版本发现的 bug 。除此之外,“从主干开发版本向后移植的一些补丁,修复了缺失的 AArch64 ARMv8 指令和系统调用”。

有何优点

同其它所有 Valgrind 工具一样, Memcheck 也是命令行程序。它的操作非常简单:通常我们会使用诸如 prog arg1 arg2 格式的命令来运行程序,而 Memcheck 只要求你多加几个值即可,如 valgrind --leak-check=full prog arg1 arg2

cw memcheck output

Memcheck

(注意:因为 Memcheck 是 Valgrind 的默认工具,所以在命令行执行命令时无需提及 Memcheck。但是,需要在编译程序之初带上 -g 参数选项,这一步会添加调试信息,使得 Memcheck 的错误信息会包含正确的行号。)

我真正倾心于 Memcheck 的是它提供了很多命令行选项(如上所述的--leak-check选项),如此不仅能控制工具运转还可以控制它的输出。

举个例子,可以开启--track-origins选项,以查看程序源码中未初始化的数据;可以开启--show-mismatched-frees选项让 Memcheck 匹配内存的分配和释放技术。对于 C 语言所写的代码, Memcheck 会确保只能使用free()函数来释放内存,malloc()函数来申请内存。而对 C++ 所写的源码, Memcheck 会检查是否使用了deletedelete[]操作符来释放内存,以及new或者new[]来申请内存。

Memcheck 最好的特点,尤其是对于初学者来说,是它会给用户建议使用哪个命令行选项能让输出更加有意义。比如说,如果你不使用基本的--leak-check选项, Memcheck 会在输出时给出建议:“使用 --leak-check=full 重新运行以查看更多泄漏内存细节”。如果程序有未初始化的变量, Memcheck 会产生信息:“使用 --track-origins=yes 以查看未初始化变量的定位”。

Memcheck 另外一个有用的特性是它可以创建 抑制文件 suppression files ,由此可以略过特定的不能修正的错误,这样 Memcheck 运行时就不会每次都报警了。值得一提的是, Memcheck 会去读取默认抑制文件来忽略系统库(比如 C 库)中的报错,这些错误在系统创建之前就已经存在了。可以选择创建一个新的抑制文件,或是编辑现有的文件(通常是/usr/lib/valgrind/default.supp)。

Memcheck 还有高级功能,比如可以使用定制内存分配器检测内存错误。除此之外, Memcheck 提供监控命令,当用到 Valgrind 内置的 gdbserver ,以及客户端请求机制(不仅能把程序的行为告知 Memcheck ,还可以进行查询)时可以使用。

注意事项

毫无疑问, Memcheck 可以节省很多调试时间以及省去很多麻烦。但是它使用了很多内存,导致程序执行变慢(由文档可知,大概会花费 20 至 30 倍时间)。

除此之外, Memcheck 还有其它局限。根据用户评论, Memcheck 很明显不是线程安全的;它不能检测出 静态缓冲区溢出;还有就是,一些 Linux 程序如 GNU Emacs 目前还不能配合 Memcheck 工作。

如果有兴趣,可以在这里查看 Valgrind 局限性的详细说明。

总结

无论是对于初学者还是那些需要高级特性的人来说, Memcheck 都是一款便捷的内存调试工具。如果你仅需要基本调试和错误检查, Memcheck 会非常容易上手。而当你想要使用像抑制文件或者监控指令这样的特性,就需要花一些功夫学习了。

虽然罗列了大量的局限性,但是 Valgrind(包括 Memcheck )在它的网站上声称全球有成千上万程序员使用了此工具。开发团队称收到来自超过 30 个国家的用户反馈,而这些用户的工程代码有的高达两千五百万行。

Memwatch

开发者:Johan Lindh

评估版本:2.71

支持的 Linux 发行版:所有种类

许可:GNU GPL

Memwatch 是由 Johan Lindh 开发的内存调试工具,虽然它扮演的主要角色是内存泄漏检测器,但是(根据网页介绍)它也具有检测其它如内存重复释放和错误释放、缓冲区溢出和下溢、野指针写入等等内存相关问题的能力。

Memwatch 支持用 C 语言所编写的程序。也可以在 C++ 程序中使用它,但是这种做法并不提倡(由 Memwatch 源码包随附的 Q&A 文件中可知)。

更新内容

这个版本添加了ULONG_LONG_MAX以区分 32 位和 64 位程序。

有何优点

跟 Dmalloc 一样, Memwatch 也有优秀的文档资料。参考 USING 文件,可以学习如何使用 Memwatch ,可以了解 Memwatch 是如何初始化、如何清理以及如何进行 I/O 操作,等等。还有一个 FAQ 文件,旨在帮助用户解决使用过程遇到的一般问题。最后还有一个test.c文件提供工作案例参考。

cw memwatch output

Memwatch

不同于 Mtrace , Memwatch 产生的日志文件(通常是memwatch.log)是人类可阅读的格式。而且, Memwatch 每次运行时总会把内存调试结果拼接到输出该文件的末尾。如此便可在需要之时轻松查看之前的输出信息。

同样值得一提的是当你执行了启用 Memwatch 的程序, Memwatch 会在标准输出中产生一个单行输出,告知发现了错误,然后你可以在日志文件中查看输出细节。如果没有产生错误信息,就可以确保日志文件不会写入任何错误,多次运行的话确实能节省时间。

另一个我喜欢的优点是 Memwatch 还提供了在源码中获取其输出信息的方式,你可以获取信息,然后任由你进行处理(参考 Memwatch 源码中的mwSetOutFunc()函数获取更多有关的信息)。

注意事项

跟 Mtrace 和 Dmalloc 一样, Memwatch 也需要你往你的源文件里增加代码:你需要把memwatch.h这个头文件包含进你的代码。而且,编译程序的时候,你需要连同memwatch.c一块编译;或者你可以把已经编译好的目标模块包含起来,然后在命令行定义MEMWATCHMW_STDIO变量。不用说,想要在输出中定位行号, -g 编译器选项也少不了。

此外, Memwatch 缺少一些特性。比如 Memwatch 不能检测出对一块已经被释放的内存进行写入操作,或是在分配的内存块之外的进行读取操作。而且, Memwatch 也不是线程安全的。还有一点,正如我在开始时指出,在 C++ 程序上运行 Memwatch 的结果是不能预料的。

总结

Memcheck 可以检测很多内存相关的问题,在处理 C 程序时是非常便捷的调试工具。因为源码小巧,所以可以从中了解 Memcheck 如何运转,有需要的话可以调试它,甚至可以根据自身需求扩展升级它的功能。

Mtrace

开发者: Roland McGrath 和 Ulrich Drepper

评估版本: 2.21

支持的 Linux 发行版:所有种类

许可:GNU GPL

Mtrace 是 GNU C 库中的一款内存调试工具,同时支持 Linux 上的 C 和 C++ 程序,可以检测由函数malloc()free()不匹配的调用所引起的内存泄漏问题。

cw mtrace output

Mtrace

Mtrace 实际上是实现了一个名为mtrace()的函数,它可以跟踪程序中所有 malloc/free 调用,并在用户指定的文件中记录相关信息。文件以一种机器可读的格式记录数据,所以有一个 Perl 脚本——同样命名为 mtrace ——用来把文件转换并为人类可读格式。

更新内容

Mtrace 源码Perl 文件同 GNU C 库( 2.21 版本)一起释出,除了更新版权日期,其它别无改动。

有何优点

Mtrace 最好的地方是它非常简单易学。你只需要了解在你的源码中如何以及何处添加 mtrace() 及对应的 muntrace() 函数,还有如何使用 Mtrace 的 Perl 脚本。后者非常简单,只需要运行指令mtrace <program-executable> <log-file-generated-upon-program-execution>(例子见开头截图最后一条指令)。

Mtrace 另外一个优点是它的可伸缩性,这体现在不仅可以使用它来调试完整的程序,还可以使用它来检测程序中独立模块的内存泄漏。只需在每个模块里调用mtrace()muntrace()即可。

最后一点,因为 Mtrace 会在mtrace()——在源码中添加的函数——执行时被触发,因此可以很灵活地使用信号动态地(在程序执行时)使能 Mtrace 。

注意事项

因为mtrace()mauntrace()函数 —— 声明在mcheck.h文件中,所以必须在源码中包含此头文件 —— 的调用是 Mtrace 工作的基础(mauntrace()函数并非总是必要),因此 Mtrace 要求程序员至少改动源码一次。

需要注意的是,在编译程序的时候带上 -g 选项( GCCG++ 编译器均有提供),才能使调试工具在输出结果时展示正确的行号。除此之外,有些程序(取决于源码体积有多大)可能会花很长时间进行编译。最后,带 -g 选项编译会增加了可执行文件的大小(因为提供了额外的调试信息),因此记得程序需要在测试结束后,不带 -g 选项重新进行编译。

使用 Mtrace ,你需要掌握 Linux 环境变量的基本知识,因为在程序执行之前,需要把用户把环境变量MALLOC_TRACE的值设为指定的文件(mtrace()函数将会记录全部信息到其中)路径。

Mtrace 在检测内存泄漏和试图释放未经过分配的内存方面存在局限。它不能检测其它内存相关问题如非法内存访问、使用未初始化内存。而且,有人抱怨 Mtrace 不是线程安全的。

总结

不言自明,我在此讨论的每款内存调试器都有其优点和局限。所以,哪一款适合你取决于你所需要的特性,虽然有时候容易安装和使用也是一个决定因素。

要想捕获软件程序中的内存泄漏, Mtrace 最适合不过了。它还可以节省时间。由于 Linux 系统已经预装了此工具,对于不能联网或者不可以下载第三方调试调试工具的情况, Mtrace 也是极有助益的。

另一方面,相比 Mtrace , Dmalloc 不仅能检测更多错误类型,还提供更多特性,比如运行时可配置、 GDB 集成。而且, Dmalloc 不像这里所说的其它工具,它是线程安全的。更不用说它的详细资料了,这让 Dmalloc 成为初学者的理想选择。

虽然 Memwatch 的资料比 Dmalloc 的更加丰富,而且还能检测更多的错误种类,但是你只能在 C 语言写就的程序中使用它。一个让 Memwatch 脱颖而出的特性是它允许在你的程序源码中处理它的输出,这对于想要定制输出格式来说是非常有用的。

如果改动程序源码非你所愿,那么使用 Electric Fence 吧。不过,请记住, Electric Fence 只能检测两种错误类型,而此二者均非内存泄漏。还有就是,需要基本了解 GDB 以最大化发挥这款内存调试工具的作用。

Memcheck 可能是其中综合性最好的了。相比这里提及的其它工具,它能检测更多的错误类型,提供更多的特性,而且不需要你的源码做任何改动。但请注意,基本功能并不难上手,但是想要使用它的高级特性,就必须学习相关的专业知识了。


via: http://www.computerworld.com/article/3003957/linux/review-5-memory-debuggers-for-linux-coding.html

作者:Himanshu Arora 译者:soooogreen 校对:PurlingNayuki,ezio

本文由 LCTT 原创编译,Linux中国 荣誉推出

如果你读过我写的使用GDB命令行调试器调试C/C++程序,你就会明白一个调试器对一段C/C++程序来说有多么的重要和有用。然而,如果一个像GDB这样的命令行对你而言听起来更像一个问题而不是一个解决方案的话,那么你也许会对Nemiver更感兴趣。Nemiver 是一款基于 GTK+ 的用于C/C++程序的图形化的独立调试器,它以GDB作为其后端。最令人赞赏的是其速度和稳定性,Nemiver是一个非常可靠,具备许多优点的调试工具。

Nemiver的安装

基于Debian发行版,它的安装时非常直接简单,如下:

$ sudo apt-get install nemiver 

在Arch Linux中安装如下:

$ sudo pacman -S nemiver 

在Fedora中安装如下:

$ sudo yum install nemiver 

如果你选择自己编译,GNOME 网站上有最新源码包。

最令人欣慰的是,它能够很好地与GNOME环境像结合。

Nemiver的基本用法

启动Nemiver的命令:

$ nemiver 

你也可以通过执行一下命令来启动:

$ nemiver [需要调试的可执行程序的路径] 

注意,如果在调试模式下编译程序(在 GCC 中使用 -g 选项)将会对 nemiver 更有帮助。

还有一个优点是Nemiver的加载很快,所以你马上就可以看到主屏幕的默认布局。

默认情况下,断点通常位于主函数的第一行。这样就可以空出时间让你去认识调试器的基本功能:

  • 执行到下一行 (按键是F6)
  • 执行到函数内部即停止(F7)
  • 执行到函数外部即停止(Shift+F7)

不过我个人喜欢“Run to cursor(运行至光标所在行)”,该选项使你的程序准确的运行至你光标所在行,它的默认按键是F11。

断点是很容易使用的。最快捷的方式是在一行代码上按下F8来设置一个断点。但是Nemiver在“Debug”菜单下也有一个更复杂的菜单,它允许你在一个特定的函数,某一行,二进制文件中的位置,或者类似异常、分支或者exec的事件上设置断点。

你也可以通过追踪来查看一个变量。在“Debug”中,你可以用一个表达式的名字来检查它的值,然后也可以通过将其添加到列表中以方便访问。这可能是最有用的一个功能,虽然我从未有兴趣将鼠标悬停在一个变量来获取它的值。值得注意的是,虽然鼠标悬停可以取到值,如果想要让它更好地工作,Nemiver是可以看到结构并给出所有成员的变量的赋值。

谈到方便地访问信息,我也非常欣赏这个程序的布局。默认情况下,代码在上半部分,功能区标签在下半部分。这可以让你访问终端的输出、上下文追踪器、断点列表、注册器地址、内存映射和变量控制。但是请注意在“Edit”-“Preferences”-“Layout”下你可以选择不同的布局,包括一个可以修改的动态布局。

自然,当你设置了全部断点,观察点和布局,您可以在“File”菜单下很方便地保存该会话,以便你下次打开时恢复。

Nemiver的高级用法

到目前为止,我们讨论的都是Nemiver的基本特征,例如,你马上开始调试一个简单的程序需要了解什么。如果你有更高的需求,特别是对于一些更加复杂的程序,你应该会对接下来提到的这些特征更感兴趣。

调试一个正在运行的进程

Nemiver允许你驳接到一个正在运行的进程进行调试。在“File”菜单,你可以筛选出正在运行的进程,并驳接到某个进程。

通过TCP连接远程调试一个程序

Nemiver支持远程调试,你可以在一台远程机器上设置一个轻量级调试服务器,然后你在另外一台机器上启动 nemiver 去调试运行在调试服务器上的程序。如果出于某些原因,你不能在远程机器上很好地驾驭 Nemiver或者GDB,那么远程调试对于你来说将非常有用。在“File”菜单下,指定二进制文件、共享库位置、远程地址和端口。

使用你的GDB二进制程序进行调试

如果你的Nemiver是自行编译的,你可以在“Edit(编辑)”-“Preferences(首选项)”-“Debug(调试)”下给GDB指定一个新的位置。如果你想在Nemiver下使用定制版本的GDB,那么这个选项对你来说是非常实用的。

跟随一个子进程或者父进程

当你的程序分支时,Nemiver是可以设置为跟随子进程或者父进程的。想激活这个功能,请到“Debugger”下面的“Preferences(首选项)”。

总而言之,Nemiver大概是我最喜欢的不在IDE里面的调试程序。在我看来,它甚至可以击败GDB,它和命令行程序一样深深吸引了我。所以,如果你从未使用过的话,我会强烈推荐你使用。我十分感谢它背后的开发团队给了我这么一个可靠、稳定的程序。

你对Nemiver有什么见解?你是否也考虑它作为独立的调试工具?或者仍然坚持使用IDE?让我们在评论中探讨吧。


via: http://xmodulo.com/debug-program-nemiver-debugger.html

作者:Adrien Brochard 译者:disylee 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出

根据定义,调试工具是那些那些使我们能够监测、控制和纠正其他程序的程序。我们为什么应该用调试工具呢? 在有些情况下,运行一些程序的时候我们会被卡住,我们需要明白究竟发生了什么。 例如,我们正在运行应用程序,它产生了一些错误消息。要修复这些错误,我们应该先找出为什么产生这些错误的消息和这些错误消息从哪里产生的。 一个应用程序可能突然挂起,我们必须了解其他什么进程同时在运行。我们可能还必须弄清楚某个进程挂起的时候在做什么。为了剖析这些细节, 我们需要调试工具的帮助。

(题图来自:axxomovies.org)

有几个Linux下的用户空间调试工具和技术,它们用来分析用户空间的问题相当有用。它们是:

  • 'print' 语句
  • 查询 (/proc, /sys 等)
  • 跟踪 (strace/ltrace)
  • Valgrind (memwatch)
  • GDB

让我们一个个地了解。

1.'print' 语句

这是一个基本的原始的调试问题的方法。 我们可以在程序中插入print语句来了解控制流和变量值。 虽然这是一个简单的技术, 但它有一些缺点。 程序需要进行编辑以添加'print'语句,然后必须重新编译,重新运行来获得输出。 如果要调试的程序相当大,这是一个耗时的方法。

2. 查询

在某些情况下,我们需要弄清楚在一个运行在内核中的进程的状态和内存映射。为了获得这些信息,我们不需要在内核中插入任何代码。 相反,可以用 /proc 文件系统。

/proc 是一个伪文件系统,系统一启动运行就收集着运行时系统的信息 (cpu信息, 内存容量等)。

output of 'ls /proc'

'ls /proc'的输出

正如你看到的, 系统中运行的每一个进程在/proc文件系统中有一个以进程id命名的项。每个进程的细节信息可以在进程id对应的目录下的文件中获得。

output of 'ls /proc/pid'

'ls /proc/pid'的输出

解释/proc文件系统内的所有条目超出了本文的范围。一些有用的列举如下:

  • /proc/cmdline -> 内核命令行
  • /proc/cpuinfo -> 关于处理器的品牌,型号信息等
  • /proc/filesystems -> 文件系统的内核支持的信息
  • /proc//cmdline -> 命令行参数传递到当前进程
  • /proc//mem -> 当前进程持有的内存
  • /proc//status -> 当前进程的状态

3. 跟踪

strace的和ltrace是两个在Linux中用来追踪程序的执行细节的跟踪工具。

strace:

strace拦截和记录系统调用及其接收的信号。对于用户,它显示了系统调用、传递给它们的参数和返回值。strace的可以附着到已在运行的进程或一个新的进程。它作为一个针对开发者和系统管理员的诊断、调试工具是很有用的。它也可以用来当做一个通过跟踪不同的程序调用来了解系统的工具。这个工具的好处是不需要源代码,程序也不需要重新编译。

使用strace的基本语法是:

strace 命令

strace有各种各样的参数。可以检查看strace的手册页来获得更多的细节。

strace的输出非常长,我们通常不会对显示的每一行都感兴趣。我们可以用'-e expr'选项来过滤不想要的数据。

用 '-p pid' 选项来绑到运行中的进程.

用'-o'选项,命令的输出可以被重定向到文件。

output of strace filtering only the open system call

strace过滤成只有系统调用的输出

ltrace:

ltrace跟踪和记录一个进程的动态(运行时)库的调用及其收到的信号。它也可以跟踪一个进程所作的系统调用。它的用法是类似与strace。

ltrace command

'-i' 选项在调用库时打印指令指针。

'-S' 选项被用来现实系统调用和库调用

所有可用的选项请参阅ltrace手册。

output of ltrace capturing 'strcmp' library call

ltrace捕捉'STRCMP'库调用的输出

4. Valgrind

Valgrind是一套调试和分析工具。它的一个被广泛使用的默认工具——'Memcheck'——可以拦截malloc(),new(),free()和delete()调用。换句话说,它在检测下面这些问题非常有用:

  • 内存泄露
  • 重释放
  • 访问越界
  • 使用未初始化的内存
  • 使用已经被释放的内存等。

它直接通过可执行文件运行。

Valgrind也有一些缺点,因为它增加了内存占用,会减慢你的程序。它有时会造成误报和漏报。它不能检测出静态分配的数组的访问越界问题。

为了使用它,首先请下载并安装在你的系统上。可以使用操作系统上的包管理器来安装。

使用命令行安装需要解压缩和解包下载的文件。

tar -xjvf valgring-x.y.z.tar.bz2 (where x.y.z is the version number you are trying to install)

进入新创建的目录(的valgrind-XYZ)内运行以下命令:

./configure
make
make install

让我们通过一个小程序(test.c)来理解valgrind怎么工作的:

#include <stdio.h>

void f(void)

{
int x = malloc(10 * sizeof(int));

x[10] = 0;
}

int main()
{
f();
return 0;
}

编译程序:

gcc -o test -g test.c

现在我们有一个可执行文件叫做'test'。我们现在可以用valgrind来检测内存错误:

valgrind –tool=memcheck –leak-check=yes test

这是valgrind呈现错误的输出:

output of valgrind showing heap block overrun and memory leak

valgrind显示堆溢出和内存泄漏的输出

正如我们在上面看到的消息,我们正在试图访问函数f未分配的内存以及分配尚未释放的内存。

5. GDB

GDB是来自自由软件基金会的调试器。它对定位和修复代码中的问题很有帮助。当被调试的程序运行时,它给用户控制权去执行各种动作, 比如:

  • 启动程序
  • 停在指定位置
  • 停在指定的条件
  • 检查所需信息
  • 改变程序中的数据 等。

你也可以将一个崩溃的程序coredump附着到GDB并分析故障的原因。

GDB提供很多选项来调试程序。 然而,我们将介绍一些重要的选择,来感受如何开始使用GDB。

如果你还没有安装GDB,可以在这里下载:GDB官方网站

编译程序:

为了用GDB调试程序,必须使用gcc的'-g'选项进行编译。这将以操作系统的本地格式产生调试信息,GDB利用这些信息来工作。

下面是一个简单的程序(example1.c)执行被零除用来显示GDB的用法:

#include
int divide()
{
int x=5, y=0;
return x / y;
}

int main()
{
divide();
}

An example showing usage of gdb

展示GDB用法的例子

调用 GDB:

通过在命令行中执行'gdb'来启动gdb:

invoking gdb

调用 gdb

调用后, 它将等待终端命令并执行,直到退出。

如果一个进程已经在运行,你需要将GDB连接到它上面,可以通过指定进程ID来实现。假设程序已经崩溃,要分析问题的原因,则用GDB分析core文件。

启动程序:

一旦你在GDB里面,使用'run'命令来启动程序进行调试。

给程序传参数:

使用'set args'给你的程序传参数,当程序下次运行时将获得该参数。'show args'将显示传递给程序的参数。

检查堆栈:

每当程序停止,任何人想明白的第一件事就是它为什么停止,以及怎么停在那里的。该信息被称为反向跟踪。由程序产生每个函数调用和局部变量,传递的参数,调用位置等信息一起存储在堆栈内的数据块种,被称为一帧。我们可以使用GDB来检查所有这些数据。 GDB从最底层的帧开始给这些帧编号。

  • bt: 打印整个堆栈的回溯
  • bt 打印n个帧的回溯
  • frame : 切换到指定的帧,并打印该帧
  • up : 上移'n'个帧
  • down : 下移'n'个帧 ( n默认是1)

检查数据:

程序的数据可以在里面GDB使用'print'命令进行检查。例如,如果'x'是调试程序内的变量,'print x'会打印x的值。

检查源码:

源码可以在GDB中打印。默认情况下,'list'命令会打印10行代码。

  • list : 列出'linenum'行周围的源码
  • list : 从'function'开始列出源码
  • disas : 显示该函数机器代码

停止和恢复程序:

使用GDB,我们可以在必要的地方设置断点,观察点等来停止程序。

  • break : 在'location'设置一个断点。当在程序执行到这里时断点将被击中,控制权被交给用户。
  • watch : 当'expr'被程序写入而且它的值发生变化时GDB将停止
  • catch : 当'event'发生时GDB停止
  • disable : 禁用指定断点
  • enable : 启用指定断点
  • delete : 删除 断点/观察点/捕获点。 如果没有传递参数默认操作是在所有的断点
  • step: 一步一步执行程序
  • continue: 继续执行程序,直到执行完毕

退出 GDB:

用'quit'命令还从GDB中退出。

GDB还有更多的可用选项。里面GDB使用help选项了解更多详情。

getting help within gdb

在GDB中获得帮助

总结

在这篇文章中,我们已经看到不同类型的Linux用户空间的调试工具。总结以上所有内容,如下是什么时候使用该什么的快速指南:

  • 基本调试,获得关键变量 - print 语句
  • 获取有关文件系统支持,可用内存,CPU,运行程序的内核状态等信息 - 查询 /proc 文件系统
  • 最初的问题诊断,系统调用或库调用的相关问题,了解程序流程 – strace / ltrace
  • 应用程序内存空间的问题 – valgrind
  • 检查应用程序运行时的行为,分析应用程序崩溃 – gdb

via: http://linoxide.com/linux-how-to/user-space-debugging-tools-linux/

作者:B N Poornima 译者:mtunique 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出

没有调试器的情况下编写程序时最糟糕的状况是什么?编译时跪着祈祷不要出错?用血祭召唤恶魔帮你运行程序?或者在每一行代码间添加printf("test")语句来定位错误点?如你所知,编写程序时不使用调试器的话是不方便的。幸好,linux下调试还是很方便的。大多数人使用的IDE都集成了调试器,但 linux 最著名的调试器是命令行形式的C/C++调试器GDB。然而,与其他命令行工具一致,DGB需要一定的练习才能完全掌握。这里,我会告诉你GDB的基本情况及使用方法。

安装GDB

大多数的发行版仓库中都有GDB

Debian 或 Ubuntu

$ sudo apt-get install gdb

Arch Linux

$ sudo pacman -S gdb

Fedora,CentOS 或 RHEL:

$sudo yum install gdb

如果在仓库中找不到的话,可以从官网中下载

示例代码

当学习GDB时,最好有一份代码,动手试验。下列代码是我编写的简单例子,它可以很好的体现GDB的特性。将它拷贝下来并且进行实验——这是最好的方法。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    int i;
    int a=0, b=0, c=0;
    double d;
    for (i=0; i<100; i++)
    {
        a++;
        if (i>97)
            d = i / 2.0;
        b++;
    }
    return 0;
}

GDB的使用

首先最重要的,你需要使用编译器的 “-g“选项来编译程序,这样可执行程序才能通过GDB来运行。通过下列语句开始调试:

$ gdb -tui [可执行程序名]

使用”-tui“选项可以将代码显示在一个漂亮的交互式窗口内(所以被称为“文本用户界面 TUI”),在这个窗口内可以使用光标来操控,同时在下面的GDB shell中输入命令。

现在我们可以在程序的任何地方设置断点。你可以通过下列命令来为当前源文件的某一行设置断点。

break [行号]

或者为一个特定的函数设置断点:

break [函数名]

甚至可以设置条件断点

break [行号] if [条件]

例如,在我们的示例代码中,可以设置如下:

break 11 if i > 97

这样,程序循环97次之后停留在“a++”语句上。这样是非常方便的,避免了我们需要手动循环97次。

最后但也是很重要的是,我们可以设置一个“观察断点”,当这个被观察的变量发生变化时,程序会被停止。

watch [变量]

这里我们可以设置如下:

watch d

当d的值发生变化时程序会停止运行(例如,当i>97为真时)。

当设置断点后,使用"run"命令开始运行程序,或按如下所示:

r [程序的输入参数(如果有的话)]

gdb中,大多数的命令单词都可以简写为一个字母。

不出意外,程序会停留在11行。这里,我们可以做些有趣的事情。下列命令:

bt

回溯功能(backtrace)可以让我们知道程序如何到达这条语句的。

info locals

这条语句会显示所有的局部变量以及它们的值(你可以看到,我没有为d设置初始值,所以它现在的值是任意值)。

当然:

p [变量]

这个命令可以显示特定变量的值,而更进一步:

ptype [变量]

可以显示变量的类型。所以这里可以确定d是double型。

既然已经到这一步了,我么不妨这么做:

set var [变量] = [新的值]

这样会覆盖变量的值。不过需要注意,你不能创建一个新的变量或改变变量的类型。我们可以这样做:

set var a = 0

如其他优秀的调试器一样,我们可以单步调试:

step

使用如上命令,运行到下一条语句,有可能进入到一个函数里面。或者使用:

next

这可以直接运行下一条语句,而不进入子函数内部。

结束测试后,删除断点:

delete [行号]

从当前断点继续运行程序:

continue

退出GDB:

quit

总之,有了GDB,编译时不用祈祷上帝了,运行时不用血祭了,再也不用printf(“test“)了。当然,这里所讲的并不完整,而且GDB的功能远远不止于此。所以我强烈建议你自己更加深入的学习它。我现在感兴趣的是将GDB整合到Vim中。同时,这里有一个备忘录记录了GDB所有的命令行,以供查阅。

你对GDB有什么看法?你会将它与图形调试器对比吗,它有什么优势呢?对于将GDB集成到Vim有什么看法呢?将你的想法写到评论里。


via: http://xmodulo.com/gdb-command-line-debugger.html

作者:Adrien Brochard 译者:SPccman 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出