代码监视
正如我们在前面所提到的,当程序并未按我们预期的那样运行时,重读我们的程序是一个好主意。出于本章的目的,我们假设代码已经进行重新检查,并且明显的错误已经进行了处理。
我们可以使用一些工具来帮助我们进行代码检查,编译器就是明显的一个。如果在我们的程序中存在任何语法错误,编译器可以通知我们。
我们在后面还会提到其他的工具,lint与Splint。与编译器类似,他们会分析代码并且报告不正确的代码。
监视
监视就是为了收集更多程序运行的行为信息而在程序添加的代码。正如在我们的例子中所做的,我们通常会添加printf调用来输出程序运行过程中不同阶段的变量值。我们通常可以添加多个printf调用,但是我们必须清楚的是程序必须经过修改并且在程序修改后要进行编译,当然,当bug被修复后我们需要移除这些代码。
在这里我们有两个监视工具可用。第一种方法使用C预处理器来选择性的包含监视代码,从而我们只需要重新编译程序来包含或是排除调试代码。我们可以使用如下的结构来简单做到:
#ifdef DEBUG
printf(“variable x has value = %d\n”, x);
#endif
我们可以使用编译器选项-DDEBUG编译程序来定义DEBUG符号并且包含这些额外的代码或是不带这个编译选项来排除这些代码。我们可以使用更为复杂的数字调试宏,如下所示:
#define BASIC_DEBUG 1
#define EXTRA_DEBUG 2
#define SUPER_DEBUG 4
#if (DEBUG & EXTRA_DEBUG)
printf...
#endif
在这种情况下,我们必须总是定义DEBUG宏,但是我们可以设置他来代表一个调试信息集合,或者一个详细级别。在这个例子中,编译器选项-DDEBUG=5将会允许BASIC_DEBUG与SUPER_DEBUG,但不是EXTRA_DEBUG。标记-DDEBUG=0将会禁止所有的调试信息。相对应的,包含下面的代码就排除了在不需要调试的情况下在命令行指定DEBUG的需要:
#ifndef DEBUG
#define DEBUG 0
#endif
C预处理器定义的一些宏有助于调试信息。这些宏会进行扩展给出有关当前编译的一些信息。
宏 描述
__LINE__ 表示当前行号的十进制常数
__FILE__ 表示当前文件名的字符串
__DATE__ 以"Mmm dd yyyy"格式表示的当前日期
__TIME__ 以"hh:mm:ss"格式表示的当前时间
注意,这些符号都是以两个下划线为前缀和后缀的。这是标准预处理器的通常做法,而我们应该小心避免选择会造成冲突的符号。在上面描述中的术语"当前"是指预处理器执行的时间,也就是编译器运行与文件处理的时间与日期。
试验--调试信息
下面是程序cinfo.c,这个程序会允许调试的情况下输出其编译信息。
#include <stdio.h>
int main()
{
#ifdef DEBUG
printf(“Compiled: “ __DATE__ “ at “ __TIME__ “\n”);
printf(“This is line %d of file %s\n”, __LINE__, __FILE__);
#endif
printf(“hello world\n”);
exit(0);
}
当我们在打开调试(使用-DDEBUG)的情况下编译这个程序,我们可以看到编译信息。
$ cc -o cinfo -DDEBUG cinfo.c
$ ./cinfo
Compiled: Mar 1 2003 at 18:17:32
This is line 7 of file cinfo.c
hello world
$
工作原理
当编译器编译时,其C预处理器部分会记录当前行号与文件。当遇到__LINE__与__FILE__时会将其替换为当前的变量值。日期与时间的用法与其相类似。因为__DATE__与__TIME__是字符串,我们可以使用printf格式化字符将他们合并,因为ANSI C将合并的字符串看作一个字符串。
不重新编译而调试
在我们继续之前,很值得指出一点:有一个方法可以使用printf函数帮助调试而不使用#ifdef DEBUG技术,而后者需要一个程序在可以使用之前必须进行重新编译。
这个方法是添加一个全局变量作为调试标记,允许在命令行使用-d选项,从而使用用户即使在程序发布之后也可以选择开头调试,并添加一个调试记录函数。现在我们就可以在我们的程序代码中添加如下的代码:
if (debug) {
sprintf(msg, ...)
write_debug(msg)
}
如果程序并不是实际使用我们可以将调试信息输出到stderr,或是使用syslog函数所提供的日志功能。
如果我们添加此类的跟踪代码为解决开发过程中的问题,只需要将他们留下那里就可以了。假如我们多加小心,这是相当安全的。当程序发布时我们就可以感受到这样做的好处;如果用户遇到问题,他们可以使用调试模式来运行,并且为我们诊断错误。与程序仅是输入内存错误信息不同,这样做可以报告程序此时实际做什么,而不仅是用户正是做什么。其中的区别是很明显的。
这个方法有一个明显的缺点;程序要比需要的大得多。在大多数情况下,这是比实际更为明显的一个问题。程序的规模将会大出20%到30%,但是在多数情况下这并不会对性能有什么实际的影响。差的情能来自由功能规模的巨大变化。
执行控制
让我们回到我们的例子程序。我们的程序有一个bug。我们可以修改程序添加一些额外的代码来输出程序运行时的变量值,或是我们可以使用一个调试器来控制程序的执行并且在处理执行时查看其状态。
在商业Unix系统,依据其提供者有大量的调试器可用。通常的调试有adb,sdb,与dbx。更为复杂的调试器允许我们在源代码级别详细的查看程序的状态。对于sdb,dbx是如此,而对于GNU调试器也是如此,后者可以用在Linux系统上。还存在gdb的前端,从而会使得gdb更为友好;xxgdb,tgdb,以及ddd就是这样的程序。一些IDE,例如我们在第9章所看到的,也提供了调试程序或是gdb的前端。Emacs编辑也具有一个实用程序允许我们在程序上运行gdb,设置断点,以及查看当前执行的源代码等。
要准备一个程序用于调试,我们需要使用一个或是多个特殊的编译选项来编译程序。这些选项会指示编译在程序中包含额外的调试信息。这些信息包括符号与行号信息,调试器可以使用这些信息向用户显示程序执行到了何处。
-g标记是编译一个程序用于调试时最常用到的选项。我们必须这个选项来编译每一个需要进行调试的源文件,同时也要用于链接器,从而可以使用标准C库的特殊版本来在库函数中提供调试支持。编译器程序会自动向链接器传递这些信息。调试也可以用于并不是为此目的而编译的库,但是具有更少的灵活性。
调试信息会使用可执行程序时间加倍变长。尽管可执行程序变大(而且需要更多的磁盘空间),程序运行所需要的内存数量是一样的。通常在我们发布程序之前移除这些调试信息是一个好主意,但是只有在我们调试之后才可以这样。
注:我们可以通过运行strip <file>来由一个可执行文件中移除调试信息,而不需要重新编译。
分享到:
相关推荐
《二合一串口中网络调试工具详解》 在现代电子设备的开发与维护中,串口和网络接口的调试是至关重要的环节。为了提高工作效率,一款名为“二合一串口网络调试助手”的工具应运而生,它巧妙地将串口(Serial Port)...
二进制炸弹是一种编程练习,通常在计算机科学教育中用于教授逆向工程、调试和安全相关的概念。这个压缩包包含了“二进制炸弹”代码,一个名为“bomb.exe”的可执行文件,以及GDB(GNU调试器)和objdump工具,这些都...
PE Viewer可以查看和分析PE格式的可执行文件,包括DLL和EXE,对于理解文件结构和调试二进制文件非常有用。 总结,Windows调试工具集是一套全面的诊断工具,它包含多种工具,覆盖了从用户模式到内核模式的调试需求,...
在这里,启动gdbserver的命令形式是通过在目标设备的调试二进制文件输出目录下执行命令行指令,指定IP地址和端口来启动服务。 - 在CodeBlocks中,一旦完成上述所有配置,可以通过点击工具栏上的红色小三角按钮开始...
在Android平台上,动态调试二进制库(如.so文件)是一项关键技能,这对于开发者和安全研究人员来说至关重要。本文将深入探讨Android So动态调试的核心概念、工具和步骤,以帮助逆向工程初学者更好地理解这一技术。 ...
在Release模式下,由于优化和调试信息的缺失,直接调试二进制文件通常比较困难,因此dump文件成为了重要的调试工具。 在VS2017中,我们可以使用"调试器数据模型"(Debugger Data Model,DDM)来加载并分析dump文件...
《网络调试助手二次开发详解》 在信息技术领域,网络调试是至关重要的环节,尤其是在软件开发过程中,对网络通信协议如TCP/IP(传输控制协议/因特网协议)和UDP(用户数据报协议)的深入理解和调试是必不可少的。...
2. **反汇编调试**:即使没有源代码,也能通过反汇编查看程序执行情况,这对于调试二进制程序非常有用。 3. **内存分析**:可以检查进程的内存分配,查找内存泄漏或非法访问等问题。 4. **堆栈跟踪**:能够显示调用...
中介接单统计工具 文章接单 远程调试 二次开发 实时数据更新 中介接单统计工具 文章接单 远程调试 二次开发 实时数据更新 中介接单统计工具 文章接单 远程调试 二次开发 实时数据更新 中介接单统计工具 文章接单 ...
TCPCOM是一款强大的网络与串口调试工具,它集成了网络调试助手和串口调试助手的功能,使得在进行通信协议调试、设备测试以及系统集成等工作时,能够更加便捷高效。这款软件是绿色免安装的,这意味着你可以直接运行,...
有人串口调试助手和网络调试助手合二为一,特别适合调试网络设备。 2. 支持中文和英文双语言,再也不用愁找不到合适的串口调试软件给国际客户用了。 3. 最小化时停留在右下角,不占用任务栏位置,需要时一键调入。 4...
当我们在调试二进制代码时,往往需要将其转换为可读的汇编语言,这正是反汇编的作用。OllyDbg内置了强大的反汇编引擎,可以将机器码转化为汇编指令,使我们能直观地理解程序逻辑。通过反汇编,我们可以更深入地研究...
《TCP和IP网络调试助手与串口调试助手二合一》 在现代电子设备和计算机通信领域,串口通信和TCP/IP网络通信是两种常见的数据传输方式。为了方便开发者进行调试和测试,出现了专为此设计的工具,即"TCP和IP网络调试...
中介接单统计工具 多人操作 实时更新数据 可远程调试 二次开发 中介接单统计工具 多人操作 实时更新数据 可远程调试 二次开发 中介接单统计工具 多人操作 实时更新数据 可远程调试 二次开发 中介接单统计工具 多人...
二、性能分析器 Keil μVison3集成开发环境中,内建了性能分析器。其可以在程序运行时,统计各个函数或者程序模块的执行次数及运行时间。这样,通过性能分析器的统计结果,便可以找到程序最耗时的部分,进行优化。 ...
围绕如何实现高效调试这一主题,本书深入系统地介绍了以调试器为核心的各种软件调试技术。本书共30章,分为6篇。第1篇介绍了软件调试的概况和简要历史。第2篇以英特尔架构(IA)的CPU为例,介绍了计算机系统的硬件...
所以c#目前已成为AutoCAD二次开发的首选编程语言,但是当c#的开发者在享受c#开发便利的时候,又发现了一个严重的缺陷,那就是修改了代码想重新调试时不得不重启AutoCAD,因为.net的机制限制了c#编译的dll只能加载到...
### Creo3.0_VS2012二次开发设置与调试 #### 一、环境配置 在进行Creo3.0(原名ProE)的二次开发之前,需要对开发环境进行适当的配置,确保软件能够顺利运行。本文将详细介绍如何在Visual Studio 2012环境下设置...
LabView2018串口调试助手允许用户以十六进制模式发送和接收数据,这对于分析和调试二进制协议或设备通信非常有用。用户可以直接输入十六进制数值,或者将接收到的数据转换成十六进制进行查看,大大提高了调试效率。 ...