软件调试基础
软件调试的基本手段有:断点、单步执行、栈回溯等。
其初衷是跟踪和记录CPU执行软件的过程,把动态的瞬间凝固下来供检查和分析
**Bug一词来源:**
“Bug”的创始人格蕾丝·赫柏(Grace Murray Hopper),是一位为美国海军工作的电脑专家,也是最早将人类语言融入到电脑程序的人之一。
而代表电脑程序出错的“bug” 这名字,正是由赫柏所取的。
1947年9月9日,赫柏对Harvard Mark II设置好17000个继电器进行编程后,技术人员正在进行整机运行时,它突然停止了工作。于是他们爬上去找原因,发现这台巨大的计算机内部一组继电器的触点之间有一只飞蛾,这显然是由于飞蛾受光和热的吸引,飞到了触点上,然后被高电压击死。所以在报告中,赫柏用胶条贴上飞蛾,并把“bug”来表示“一个在电脑程序里的错误”,“Bug”这个说法一直沿用到今天。
软件调试:指使用调试工具求解各种软件问题的过程(比如跟踪软件的执行过程、探索软件本身或其配套的其他软件,或者硬件系统的工作原理等),这些过程有可能是为了去除软件缺欠,也可能不是。
软件调试基本过程:(有点像QA提bug,然后开发修复、提测,QA验证的过程)
- 重现故障
- 定位根源(这个通常是最困难也是最关键的步骤)
- 探索和实现解决方案
- 验证方案
软件调试基本特征:
- 难度大
- 难以预估完成时间
- 广泛的关联性
单步调试:
在1978年,x86CPU的第一代8086CPU问世的时候,其标志寄存器(FLAGS)中,专门设计了一个用于软件调试的标志位,叫做TF(Trsce Flag)。
TF位主要是供调试器软件来使用的,当用户需要单步调试时,调试器会设置TF位,当CPU执行完一条指令后会检查TF位,如果这个位为1,那么便会产生一个调试异常(INT1),目的是停止执行当前的程序,中断到调试器中。
断点指令:
- 逗号断点指令(Comma Breakpoint)
分支监视
软件调试分类:
- 按调试目标的系统环境分类(Windows下的软件调试、Linux下的软件调试、Dos下的软件调试等)
- 按目标代码的执行方式分:
脚本程序 – 脚本调试器
执行编译的程序:- 先编译为中间代码,在运行时再动态编译为当前CPU能够执行的目标代码(比如C#开发的.NET程序) – 托管调试
- 直接编译和链接成目标代码的程序(C/C++) – 本地调试
- 兼具以上两种的 – 混合调试
- 按目标代码的执行模式分:用户态调试(User Mode Debugging)、内核态调试(Kernel Mode Debugging)
在Windows这样的多任务操作系统中,作为保证安全和秩序的一个根本措施,系统定义了两种执行模式,即低特权等级的用户模式(User Mode)和高特权等级的内核模式(Kernel Mode)。
应用程序代码是运行在用户模式下的,操作系统的内核、执行体和大多数设备驱动程序是运行在内核模式的。
- 按软件所处的阶段分:开发期调试、产品期调试(分界线是产品的正式发布)
- 按调试器和调试目标的相对位置分:本机提哦啊哈斯、远程调试
- 按调试目标的活动性分:活动目标调试、转储文件调试
- 按调试工具分
调试技术的一些其他知识
断点:
断点是使用调试器进行调试的最常用的调试技术之一。器基本思想是在某一个位置设置一个“陷阱”,当CPU执行到这个位置的时候便停止执行被调试的程序,中断到调试器中,让调试着进行分析和调试。调试着分析结束后,可以让被调试程序恢复执行。
根据断点的设置空间可以把断点分为:
- 代码断点 – 设置在内存空间中的断点,其地址通常为某一段代码的起始处。
- 数据断点 – 设置在内存空间中的断点,其地址一般为要监视的变量(数据)的起始地址。
- I/O断点 – 设置在I/O空间中的断点,其地址为某一I/O地址
输出调试信息:
在windows操作系统中,驱动程序可以使用DbgPrint/DbgPrintEx来输出调试信息;应用程序可以调用OutputDebugString,控制台程序可以直接使用print函数打印信息。
- 优点:实时性
- 缺点:容易丢失和被覆盖,不适用于长期保存和事后分析
日志
事件跟踪:
适用于监视频繁且复杂的软件过程(比如监视文件访问和网络通信等)
ETW(Event Trace for Windows)是Windows系统内建的一种事件跟踪机制,windows内核本身和很多windows下的软件工具间都使用了该机制。
转储文件(dump file)
栈回溯:
栈回溯的基本原理:通过递归方式寻找放在栈上的函数返回地址,便可以追溯到当前线程的函数调用序列。
目前的主流CPU架构都是使用栈来进行函数调用的,栈上记录了函数的返回地址。
栈回溯是记录和探索程序执行踪迹的计价方法,使用这样方法,准确且基本不需要额外的开销。
反汇编:
反汇编就是将目标代码(指令)翻译为汇编代码。
反汇编的依赖性非常小,根据二进制的可执行文件就可以得到汇编语言表示的程序。