《软件调试卷一》学习笔记

自动 编程,就是编译。 第一页

 

断点指令 就是 INT 3。

比如Windows本地调试中的软件断点功能通常是依赖于CPU的断点指令(对于x86,即INT 3)的,CPU执行到断点指令时中断下来,并以异常的方式报告给操作系统,再由操作系统将这个事件分发给调试器。

 

调试器的广泛性。

综上所述,软件调试与计算机系统的硬件核心(CPU)和软件核心(操作系统)都有着很紧密的耦合关系,与软件生产的主要机器——编译器也息息相关。

 

断点调试文件系统。牛逼。

比如今日要学习文件系统,那么便把断点设在文件系统的函数上,命中后观察谁在调用它,它又去调用谁,如此坚持不懈,“至于用力之久,而一旦豁然贯通焉,则众物之表里精粗无不到,而吾心之全体大用无不明矣。

 

TF 位,也叫陷阱位,trace,track。

当用户需要单步跟踪时,调试器会设置TF位,当CPU执行完一条指令后会检查TF位,如果这个位为1,那么便会产生一个调试异常(INT 1),目的是停止执行当前的程序,中断到调试器中

 

根据分支设置断点。

英特尔P6系列CPU引入了记录分支、中断和异常的功能,以及针对分支设置断点和单步执行,我们将在第2篇详细介绍这些功能。

 

DOS 居然是一个系统。

我们 可以 把 调试 分为 Windows 下 的 软件 调试、 Linux 下 的 软件 调试、 DOS 下 的 软件 调试, 等等。

 

脚本调试器

脚本程序是由专门的解释程序解释执行的,不需要产生目标代码,与编译执行的程序有很多不同。调试使用脚本语言编写的脚本程序的过程称为脚本调试。所使用的调试器称为脚本调试器。

 

C# .net 的编译过程。托管调试

一类是先编译为中间代码,在运行时再动态编译为当前CPU能够执行的目标代码,典型的代表便是使用C#开发的.NET程序

 

windows 内核调试引擎需要两台机器。

利用Windows内核调试引擎所做的活动内核调试需要使用两台机器,两者之间通过串行接口、1394接口或USB 2.0进行连接。尽管这种调试的调试器和调试目标也在两台机器中,但是通常不将其归入远程调试的范畴。

 

插入 INT 3 指令。

软件断点通常是通过向指定的代码位置插入专用的断点指令来实现的,比如IA32 CPU的INT 3指令(机器码为0xCC)就是断点指令

 

调试寄存器 DR0 ~ DR7

IA32 CPU定义了8个调试寄存器:DR0~DR7,可以同时设置最多4个硬件断点(对于一个调试会话)

 

保存状态,不影响其他任务使用寄存器。

当中断到调试器时,系统或调试器会将被调试程序的状态保存到一个数据结构中——通常称为执行上下文(CONTEXT)

 

分支断点,可以快速找到 if 对应的汇编。

每次执行一个程序分支,又称为分支到分支单步跟踪。设置IA32 CPU的DbgCtl MSR寄存器的BTF(Branch Trap Flag)标志后,再设置TF标志,便可以让CPU执行到下一个分支指令时触发调试异常。WinDBG的tb命令用来执行到下一个分支

 

windows 的打印调试技巧。

比如,在Windows平台上,驱动程序可以使用DbgPrint/DbgPrintEx来输出调试信息,应用程序可以调用OutputDebugString,控制台程序可以直接使用printf系列函数打印信息。在Linux平台上,驱动程序可以使用printk来输出调试信息,应用程序可以使用printf系列函数。

 

windows 跟 linux 记录日志的方法。

Windows Vista 新引入了名为Common Log File System(CLFS.SYS)的内核模块,用于进一步加强日志功能。Syslog是Linux系统下常用的日志设施

 

汇编代码与机器码有着简单的对应关系。

因为汇编代码与机器码有着简单的对应关系,所以反汇编是了解程序目标代码的一种非常直接而且有效的方式。有时我们对高级语言的某一条语句的执行结果百思不得其解,就可以看一下它所对应的汇编代码,这时往往可以更快地发现问题的症结。

 

软件错误的成本。

软件错误被发现和纠正得越早,其开支就越小

 

MIPS 是 CPU每秒能执行的指令数量。

 

处理器种类。

RISC处理器的典型代表有SPARC处理器、PowerPC处理器、惠普公司的PA-RISC处理器、MIPS处理器、Alpha处理器和ARM处理器等。

 

加壳保护。

而上面的例子来自某个做过加壳保护的软件,这样的软件不愿意被反汇编,所以故意在函数的间隙或者某些位置加上0来迷惑反汇编器

 

跳转指令少的优势,可以有效避免分支预测失败。

而ARM处理器只有两条跳转指令(BLNV和BLEQ)。跳转指令对流水线执行很不利,因为一旦遇到跳转指令,CPU就需要做分支预测(branch prediction),而一旦预测失败,就要把已经执行的错误分支结果清理掉,这会降低CPU的执行效率

 

CPU 如何拿到操作数。

指令由操作码(opcode)和0到多个操作数组成。寻址方式定义了得到操作数的方式

 

RISC 只支持简单寻址。

为了避免这样的问题,RISC处理器通常只支持简单的寻址方式,不支持间接寻址,不支持在一条指令中既访问内存又进行数学运算(比如从内存中读出并累加到目标操作数,即ADD AX, [BX])

 

 

IA 原来是 Intel Architecture 的缩写。

 

IA-64 是指安腾架构。

 


从奔腾系列 CPU 开始支持指令级并行。超标量(superscalar)架构

从1993年开始,Pentium(奔腾)处理器陆续推出。奔腾处理器可以说是IA-32处理器新一轮变革的开始,不但从名字上不再沿用80386、80486这一惯例,而且从结构和性能上也有很大的突破。最重要的一点就是引入第二条执行流水线,支持指令级的并行执行(instruction level parallelism)

 

超标量架构。

加入第二条执行流水线(execution pipeline),允许同时有两条指令在解码和执行,因此每个时钟周期最多可以执行的指令数由以前的一条提升为两条,该特征又被称为超标量(superscalar)架构。

 

分支预测是因为超标量架构。

因为指令间的相互依赖性,所以很多时候必须等待前面的指令执行完毕,才能执行后面的指令,也就是两条流水线并不是总能同时在使用。为了提高每条流水线的利用率,奔腾处理器第一次引入了分支预测功能,即预测最可能的分支并提早解码和执行可能的分支

 

 

MMX 的全称是 MultiMedia eXtension 。

于1996年10月推出的带有MMX(MultiMedia eXtensions)功能的奔腾处理器(Intel Pentium with MMX Technology,奔腾MMX)使用的是P55C内核

 

微指令是从 P6 系列处理器出现的。

最值得一提的就是引入了类似RISC指令的微操作(micro-ops):将原本的x86指令先翻译成等长的微操作后再执行。

 

乱序执行。

分发单元的Reservation Station负责监视ROB,将ROB中已经就绪(操作数齐备)的微操作通过5个端口之一分配给执行单元去执行。因为ROB中的微操作只要状态就绪就可以执行,所以指令的执行顺序和程序中的先后顺序可能是不一致的,故这种执行方式被称为乱序执行(out of order execution)

 

内存类型范围寄存器。

引入了内存类型范围寄存器(Memory Type and Range Register,MTRR)。MTRR用以标识某一段内存区(物理内存空间)的内存特征,比如只读、可写、是否应该高速缓存等。它共定义了5种内存类型:分别是Uncacheable,简称UC

 

Intel P6的产品线。

英特尔便将P6处理器划分为3个产品线:针对中高端台式机市场的Pentium II处理器;面向工作站和服务器市场的至强(Xeon)处理器;面向低端台式机市场的赛扬(Celeron)处理器。

 

奔腾四开始有超线程。

超线程(Hyper-Threading,HT),即在一块CPU芯片内实现两个处理器(被称为逻辑处理器)的功能,使两个线程可以同时执行。

 

IA-32e 就是 Intel64 架构。

P4的6xx系列和支持超线程的Extreme版本引入了EM64T(Extended Memory 64 Technology)技术,通过IA-32e模式支持64位计算,IA-32e的正式名字叫Intel 64位架构。

 

IA 编程手册有 4 卷 10册。

该手册的目前版本分为4卷10册。卷1《基本架构》介绍了基本执行环境、数据类型、指令集概要、过程调用、中断和异常、一般编程和使用FPU、MMX、SSE编程等内容。卷2《指令参考》分四册(卷2A、2B、2C和2D)按字母顺序详细地介绍了每一条指令。卷3《系统编程指南》(卷3A、3B、3C和3D)从更深的层次阐述了英特尔架构的关键特征,包括保护模式下的内存管理、保护机制、中断和异常处理、任务管理、多处理器管理、高级可编程中断控制器(APIC)、处理器管理和初始化、高速缓存管理、MMX/SSE/SSE2系统编程、系统管理、MCA、调试和性能监控、8086模拟、混合16位和32位代码,以及IA32兼容性等内容。最后一卷是MSR寄存器的详细描述。