- 在重新编译程序之前不必退出GDB,在调试会话期间也不用退出再重启文本编辑器。只要将文本编辑器放在一个窗口中,GDB放在另一个窗口中,用第三个窗口调试程序即可。
- 在重新编译代码时,最好不要退出GDB。这样你的断点和建立的其他各种动作都会保留。另外也可将断点和设置的其他命令放在一个GDB启动文件中,每次启动GDB时会自动加载它们。GDB的启动文件默认名为.gdbinit。
- 很多人以为GDB显示的是最后执行的代码行,事实上,它显示的是将要执行的代码行。
- 临时断点是首次到达后就会被自动删除的断点,它使用tbreak命令设置。
- GDB实际上是使用机器语言指令工作的,但是有了增强的符号表的魔力后(-g),GDB表现出了使用源代码行的错觉。
- 在具有多个断点的代码行上,触发中断的断点将是标识符编号最小的那一个。
- 如果在修改和重新编译代码时没有退出GDB,那么在下一次执行GDB的run命令时,GDB会感知到代码已修改,并自动重新加载新版本。
- delete和disable表示删除和禁用断点,enable once命令则会使断点导致GDB暂停执行一次后被禁用,与tbreak类似,但tbreak设置的临时断点是执行一次后被删除了。
- finish命令(简写为fin)指示GDB恢复执行,直到恰好在当前栈帧完成之后为止。比如,当程序处于某个函数中时,我们可以利用finish命令离开此函数。
- until命令与finish类似,但其操作对象变为循环,而不是函数,比如一次执行完某个循环。until的官方定义:执行程序直到到达当前循环体外的下一行源代码。
- 条件断点 vs 监视点。每当(被监视)变量的值发生变化时,监视点都会中断,而条件中断只会在怀疑有问题的代码处当变量呈现该怀疑值时才中断。因此,当完全不知道变量在何处接受了伪值时最好使用监视点,这对于全局变量或者在函数之间连续传递的局部变量来说特别有用。但是在其它大多数情况下,位置放得较好的条件断点更方便。条件断点的语法是 break break-args if (condition)。
- 断点命令列表。如果反复遇到同一断点时需要查看相同的变量,则可以让GDB在每次到达该断点时自动执行一组命令,从而自动完成这一过程。使用commands命令设置命令列表:commands breakpoint-number ... end。可以使用silent命令使GDB更安静地触发断点。
- define命令可用于创建宏,同时可以将宏保存在.gdbinit文件中供其他程序使用。使用show user命令列出所有宏。
- 监视点:watch var。GDB实际上是在var的内存位置改变值时中断。
- 查看动态数组语法:*pointer@number_of_elements。其中pointer为动态分配的数组指针,number_of_elements为数组长度。
- GDB的ptype命令可以很方便地快速浏览类或者结构(struct)的结构(structure)。
- 在GDB中,可以通过调用info locals命令列出当前栈中所有局部变量值。
- 如果我们简单地在GDB中按下回车键而没有发出命令,GDB就会将这一操作看作重复上一命令的请求。
- 在UNIX平台上,为程序分配的虚拟地址布局通常为:.text/.data/.bss/堆/未使用/栈/env,其中虚拟地址0在env端末尾。虚拟地址空间是通过组织成称为页(page)的块来查看的,在Pentium硬件上,默认页大小为4096字节。
- 从概念上讲,进程虚拟地址空间的每个页在页表中都有一个页表项,存储与该页相关的各块信息。与段错误相关的数据是该页的访问权限,它类似于文件访问权限:读、写和执行。在程序执行期间,会检查该页相关信息与请求的操作权限是否一致,如果不一致硬件就会执行内部中断,这样操作系统一般会宣告一个内存访问违例,并停止程序的执行。
- 在UNIX下可以用signal()或sigaction()两个系统调用来重写默认信号处理程序。比如 signal(SIGINT,my_handler)。
- 核心文件(core)包含程序崩溃时对程序状态的详细描述:栈的内容,CPU寄存器的内容,程序的静态分配变量的值,等等。UNIX命令file有助于指出转储这个特定核心文件的可执行文件的名称,比如 file core.2344。
- 核心文件的大小控制。ulimit -c n命令指定核心文件最大大小为nKB,超过此大小则不会生成任何核心文件。ulimit -c命令会显示核心文件上的当前限制。ulimit -c unlimited命令可以取消核心文件的大小限制。
- 如果程序的源代码不可用,或者可执行文件不是用增强的符号表(-g)编译的,甚或当我们没有计划调试可执行文件,核心文件可能都不会有太大的用处。
- 调试核心文件时,使用frame frame_no可用于将当前帧改为frame_no所在的帧。
- strace可跟踪程序做过的所有系统调用。在很多情况下(无论是否与网络连接),strace都是检查系统调用结果的便捷工具。
- 多线程程序的运行具有一定的随机性,调试多线程代码常常需要特别有耐心和创意。
- 与线程相关的GDB命令用法:info threads(给出当前所有线程的信息);thread 3(改为线程3);break 88 thread 3(当线程3到达源代码行88时停止执行);break 88 thread 3 if x==y(当线程3到达88行且x==y时停止执行)。
- 库文件分为两种。当编译调用静态库中函数的代码时,那些函数会变成最终可执行文件的一部分。另一方面,如果库是动态的,那么只有实际执行了程序,这些函数才会真正附加到调用代码上。
- 使用ldd命令检查程序需要哪些库。比如 ldd a.out。
- 了解哪种工具适合于调试哪种程序错误,并识别发生程序错误时使用其中哪个工具可以节省时间和精力。
- 从vim中调用make非常方便,不需要退出,只需要执行:make args即可。
- 应该总是使用编译器的错误检查选项,GCC用户应当总是使用-Wall。
- 系统与库调用的失败通常会设置一个名为errno的全局定义整数变量。在大多数GNU/Linux系统上,errno是在/usr/include/errno.h上声明的。当一个系统或库调用失败时,它将errno一个指示失败类型的值。检查errno的值,并采取适当的动作是你的职责。
- 使用errno最安全的方式如下:(1)执行对库或系统函数的调用;(2)使用函数的返回值判断是否发生了错误;如果发生了错误,使用errno确定为什么发生这个错误。
- 有两个函数使得错误代码的解释更容易:perror()和strerror()。分别为void perror(const char *s);和char *strerror(int errnum)。
- 了解库函数和系统调用之间的区别很重要。库函数是更高级别的,完全在用户空间里运行,并为程序员提供了到做实际工作的函数的更方便的接口。系统调用代表用户以内核模式工作,由操作系统本身的内核提供。
- strace实用程序输出程序进行的各个系统调用及其参数和返回值。比如 strace ./a.out。
- ltrace与strace相似,它显示了库调用,而不是系统调用。
- 当你不具有源代码时,如果要向维护人员发送程序错误报告和诊断信息,strace和ltrace实用程序非常有用,使用这些工具有时比深度调试代码更快。
- 静态代码检查器。不编译代码,仅仅警告错误、可能的错误和严格C语言编码标准的差距。splint的目标是帮助编写最具防御性、安全和无错的程序。
- 检测DAM(动态分配内存)问题。mtrace()和MALLOC_CHECK_两个GNU工具用于向标准libc分配函数中添加钩子,以保持关于当前分配内存的记录。
- Electric Fence(即EFence)用于检查程序是否有越界读/写或者对已经释放/未分配的DAM指针执行读或写操作。它提供了一个名为EF_PROTECT_BELOW的全局int变量,将这个变量设置为1时,EFence仅捕获数组下溢,默认捕获上溢。
- 使用mcheck()用于检测DAM的不一致。在调用任何与堆相关的函数前必须调用mcheck(),否则调用会失败。
- mtrace()工具是GNU C库的一部分,用于捕获C/C++程序中的内存泄露和重复释放。
《软件调试的艺术》之GDB关键点摘记
最新推荐文章于 2023-06-28 17:53:25 发布