软件是信息时代的主角,它已经对人类社会产生了巨大的影响,而且正在产生更大的 影响。如果你在做软件方面的工作,那么你应该感觉很幸运;如果你在学习软件,那么你选择了前景无限广阔的方向,一定要坚持。

回顾作者学习软件的亲身经历,如果要分享经验,那么第一条经验就是多写代码,多调试。笔者一直觉得自己不是一个聪明的人,如果说作者的软件技术学得还可以,那么靠的就是这条:几乎每一天都写代码,几乎每一天都高度,坚持了二十多年,已经成为了习惯。

如何提高代码编写速度(每天写代码每天调试的他坚持了二十年)(1)

2018年5月,软件调试领域的“百科全书”《软件调试(第2版)卷1》由人民邮电出版社出版,与第一版刚好间隔了10年。

软件调试(第2版)卷1

如何提高代码编写速度(每天写代码每天调试的他坚持了二十年)(2)

本书堪称是软件调试的“百科全书”。作者围绕软件调试的“生态”系统(ecosystem)、异常(exception)和调试器 3 条主线,介绍软件调试的相关原理和机制,探讨可调试性(debuggability)的内涵、意义以及实现软件可调试性的原则和方法,总结软件调试的方法和技巧。

第1卷主要围绕硬件技术展开介绍。全书分为4篇,共16章。第一篇“绪论”(第1章),介绍了软件调试的概念、基本过程、分类和简要历史,并综述了本书后面将详细介绍的主要调试技术。第二篇“CPU及其调试设施”(第2~7章),以英特尔和ARM架构的CPU为例系统描述了CPU的调试支持。第三篇“GPU及其调试设施”(第8~14章),深入探讨了Nvidia、AMD、英特尔、ARM和Imagination 这五大厂商的GPU。第四篇“可调试性”(第15~16章),介绍了提高软件可调试性的意义、基本原则、实例和需要注意的问题,并讨论了如何在软件开发实践中实现可调试性。

本书理论与实践紧密结合,既涵盖了相关的技术背景知识,又针对大量具有代表性和普遍意义的技术细节进行了讨论,是学习软件调试技术的宝贵资料。本书适合所有从事软件开发工作的读者阅读,特别适合从事软件开发、测试、支持的技术人员,从事反病毒、网络安全、版权保护等工作的技术人员,以及高等院校相关专业的教师和学生学习参考。

第2版的卷1交稿后,作者便开始规划卷2。卷2的主题是Windows平台调试。加入英特尔后,虽然作者也时常作一些Linux平台方面的工作,但是Windows平台是“主场”,是作者最熟悉的平台,作者的编程之路从Windows平台开始,毕业之后最初十年里也主是在Windows平台上工作。因为当作者规划卷2时,想写的内容很多。但考虑到篇幅有限制,笔者不得不反复筛选。筛选的最重要原则是“实用性”,特别是如下两个指标。

软件调试 第2版 卷2 Windows平台调试 上、下册

如何提高代码编写速度(每天写代码每天调试的他坚持了二十年)(3)

经过多次调整,最后确定的卷2由五篇组成,共30章。

第一篇(第1—4章)介绍Windows平台的发展历史,进程和线程,架构和系统部件,以及启动过程。目的是把作者对Windows平台的总体认识分享给读者,让读者对Windows平台建立起一个总的认识,也就是大局观。

第二篇(第5—8章)选择Windows系统中非常有特色的几个方面进行深入剖析,分别是特殊的调用过程,包括异步过程调用,延迟过程调用、本地过程调用和过程过程调用,用于解决应用于兼容问题的垫片机制,曾被给予厚望查实际让人失望的.NET技术,以及具有时代特征的Linux子系统。

第三篇(第9—19章)介绍操作系统的调试支持。该篇的目标就是从不同角度呈现Windows平台上的各种调试设施。先从用户空间讲起,介绍支持应用程序调试的调试模型和调试API以及用户态调试过程,异常处理机制以及处置未处理异常的方法和过程。然后过渡到内核空间,介绍硬错误和蓝屏。接着介绍全局性的错误报告、日志、事件追踪和旨在记录硬件错误的Windows硬件错误架构。最后介绍非常强大而且具有特色的内核调试引擎和验证机制。

第四篇(第20—25章)介绍Windows平台上的开发工具的调试支持,首先介绍编译器的编译期检查和运行期检查,然后解析栈的结构和函数调用的细节,最后解析堆的结构和调试支持、异常处理代码的编译过程以及调试符号。

第五篇(第26—30章)介绍调试器。调试器无疑是征服软件世界最有力的武器。该篇首先概览调试器的发展历史和工作模型,然后分别介绍Windows平台上的常用调试器,包括老牌的集成在Visual Studio环境中的VsDebug、新兴的Visual Studio(VS)Code调试扩展,以及著名的WinDBG。

相对于读者熟悉的第1版,第2版的变化主要如下。

既然你选择了这本书,那么你多半是同意笔者的“软件调试方法论”的,剩下的问题就是如何能比较快地学会高度技术。

软件调试 第2版 卷2 Windows平台调试 上、下册

张银奎 著

如何提高代码编写速度(每天写代码每天调试的他坚持了二十年)(4)

作者简介

张银奎,国内知名的调试技术专家。毕业于上海交通大学信息与控制工程系,长期从事软件开发和研究工作,曾在英特尔工作13 年,对 IA-32 架构、操作系统内核、驱动程序,尤其是对软件调试有较深入的研究。著有《软件调试》《格蠹汇编》等畅销、常销技术图书,格蠹科技(xedge.ai)创始人,高端调试网站(advdbg.org)创建者。翻译(合译)作品有《二十一世纪机器人》《观止——微软创建NT 和未来的夺命狂奔》《数据挖掘原理》《机器学习》《人工智能:复杂问题求解的结构和策略》等。

目录

第 一篇 大 局 观

第 1 章 Windows 系统简史

1.1 源于DOS

1.2 功在NT

1.3 Windows 2000 彰显实力

1.4 巅峰之作:Windows XP 和Windows Server 2003

1.5 Windows Vista 折戟沙场

1.6 Windows 7 享利中兴

1.7 Windows 8 革新受挫

1.8 Windows 10 何去何从

1.9 本章总结 17

参考资料

第 2 章 进程和线程

2.1 任务

2.2 进程资源

2.3 进程空间

2.3.1 32 位进程空间

2.4 EPROCESS 结构

2.5 PEB.... 28

2.6 内核模式和用户模式

2.7 线程

2.8 WoW 进程

2.9 创建进程

2.10 最小进程和Pico 进程

2.11 任务管理器

2.12 本章总结

参考资料

第3 章 架构和系统部件

3.1 系统概览

3.2 内核和HAL 模块

3.3 空闲进程

3.4 系统进程

3.5 内核空间的其他模块

3.6 NTDLL.DLL

3.7 环境子系统

3.8 原生进程

3.8.1 特点

3.8.2 SMSS

3.8.3 CSRSS

3.9 本章总结

参考资料

第4 章 启动过程

4.1 BootMgr

4.2 WinLoad

4.3 内核初始化

4.4 执行体的阶段0 初始化

4.5 执行体的阶段1 初始化

4.6 创建用户空间

4.7 本章总结

参考资料

第二篇 探 微

第5 章 特殊的过程调用

5.1 异步过程调用

5.2 中断请求级别

5.3 延迟过程调用

5.4 本地过程调用

5.5 远程过程调用

5.6 本章总结

参考资料

第6 章 垫片

6.1 垫片数据库

6.2 AppHelp

6.3 垫片动态库

6.4 应用程序垫片的工作过程

6.5 内核垫片引擎

6.6 本章总结

参考资料

第7 章 托管世界

7.1 简要历史

7.2 宏伟蓝图

7.3 类和方法表

7.4 辅助调试线程

7.5 CLR4 的调试模型重构

7.6 SOS 扩展

7.7 本章总结

参考资料

第8 章 Linux子系统

8.1 源于Drawbridge

8.2 融入NT

8.3 总体架构

8.4 子系统内核模块

8.5 微软版Linux 内核

8.6 Linux 子系统服务器

8.7 WSL 启动器

8.8 交叉开发

8.9 WSL2

8.10 本章总结

参考资料

第三篇 操作系统的调试支持

第9 章 用户态调试模型

9.1 概览

9.1.1 参与者

9.2 采集调试消息

9.3 发送调试消息

9.4 调试子系统服务器(Windows XP 之后)

9.5 调试子系统服务器(Windows XP 之前)

9.6 比较两种模型

9.7 NTDLL.DLL 中的调试支持例程

9.8 调试API

9.9 本章总结

参考资料

第 10 章 用户态调试过程

10.1 调试器进程

10.2 被调试进程

10.3 从调试器中启动被调试程序

10.4 附加到已经启动的进程中

10.5 处理调试事件

10.6 中断到调试器

10.7 输出调试字符串

10.8 终止调试会话

10.9 本章总结

参考资料

第 11 章 中断和异常管理

11.1 中断描述符表

11.2 异常的描述和登记

11.3 异常分发过程

11.4 结构化异常处理

11.5 向量化异常处理

11.6 本章总结

参考资料

第 12 章 未处理异常和JIT 调试

12.1 简介

12.2 默认的异常处理器

12.3 未处理异常过滤函数

12.4 “应用程序错误”对话框

12.5 JIT 调试和Dr. Watson

12.6 顶层异常过滤函数

12.7 Dr. Watson

12.8 DRWTSN32 的日志文件

12.9 用户态转储文件

12.10 本章总结

参考资料

第 13 章 硬错误和蓝屏

13.1 硬错误提示

13.2 蓝屏终止

13.3 系统转储文件

13.4 分析系统转储文件

13.5 辅助的错误提示方法

13.6 配置错误提示机制

13.7 防止滥用错误提示机制

13.8 本章总结

参考资料

第 14 章 错误报告

14.2 系统错误报告

14.4 WER 2.0

14.5 CER

14.6 本章总结

参考资料

第 15 章 日志

15.1 日志简介

15.2 ELF 的架构

15.3 ELF 的数据组织350

15.4 查看和使用ELF 日志

15.5 CLFS 的组成和原理

15.6 CLFS 的使用方法

15.7 本章总结

参考资料

第 16 章 事件追踪

16.1 简介

16.2 ETW 的架构

16.3 提供ETW消息

16.4 控制ETW会话

16.5 消耗ETW消息

16.6 格式描述

16.7 NT 内核记录器

16.8 Global Logger Session

16.9 Crimson API

16.10 本章总结

参考资料

第 17 章 WHEA

17.1 目标、架构和PSHED.DLL

17.2 错误源

17.3 错误处理过程

17.4 错误持久化

17.5 注入错误

17.6 本章总结

参考资料

第 18 章 内核调试引擎

18.1 概览

18.2 连接

18.3 启用

18.4 初始化

18.5 内核调试协议

18.5.7 KdTalker

18.6 与内核交互

18.7 建立和维持连接

18.8 本地内核调试

18.9 本章总结

参考资料

第 19 章 验证机制

19.1 简介

19.2 驱动验证器的工作原理

19.3 使用驱动验证器

19.4 应用程序验证器的工作原理

19.5 使用应用程序验证器

19.6 本章总结

参考资料

第四篇 编译器的调试支持

第 20 章 编译和编译期检查

20.1 程序的构建过程

20.2 编译

20.3 Visual C 编译器

20.4 编译错误和警告

20.5 编译期检查

20.6 标准标注语言

20.7 本章总结

参考资料.

第 21 章 运行时库和运行期检查

21.1 C/C 运行时库

21.2 链接运行时库

21.3 运行时库的初始化和清理

21.4 运行期检查

21.5 报告运行期检查错误

21.6 本章总结

参考资料

第 22 章 栈和函数调用

22.1 简介

22.2 栈的创建过程

22.3 CALL 和RET 指令

22.4 局部变量和栈帧

22.5 帧指针省略

22.6 栈指针检查

22.7 调用协定

22.8 栈空间的增长和溢出

22.9 栈下溢

22.10 缓冲区溢出

22.11 变量检查

22.12 基于Cookie 的安全检查

22.13 本章总结

参考资料

第 23 章 堆和堆检查

23.1 理解堆

23.2 堆的创建和销毁

23.3 分配和释放堆块

23.4 堆的内部结构

23.5 低碎片堆

23.6 堆的调试支持

23.7 栈回溯数据库

23.8 堆溢出和检测

23.9 页堆

23.10 准页堆

23.11 CRT 堆

23.12 CRT 堆的调试堆块

23.13 CRT 堆的调试功能

23.14 堆块转储

23.15 泄漏转储

23.16 本章总结

参考资料

第 24 章 异常处理代码的编译

24.1 概览

24.2 FS:[0]链条

24.3 遍历FS:[0]链条

24.4 执行异常处理函数

24.5 _ _ try{}_ _ except()结构

24.6 安全问题

24.7 本章总结

参考资料

第 25 章 调试符号

25.1 名称修饰

25.2 调试信息的存储格式

25.3 目标文件中的调试信息

25.4 PE 文件中的调试信息

25.5 DBG 文件

25.6 PDB 文件

25.7 有关的编译和链接选项

25.8 PDB 文件中的数据表

25.9 本章总结

参考资料

第五篇 调 试 器

第 26 章 调试器概览

26.2 小型机和DDT调试器

26.3 个人计算机和它的调试器

26.3.3 CodeView调试器

26.3.4 Turbo Debugger

26.3.5 SoftICE

26.4 调试器的功能

26.5 分类标准

26.6 实现模型

26.7 经典架构

26.8 HPD 标准

26.9 本章总结

参考资料

第 27 章 VsDebug

27.1 架构和调试模型

27.2 VS 调试引擎

27.3 工作过程

27.4 使用断点

27.5 多线程调试

27.6 EnC

27.7 设计期调试

27.8 使用符号服务器

27.9 定制调试事件

27.10 本章总结

参考资料

第 28 章 VS Code 的调试扩展

28.1 简介

28.2 四大技术

28.3 理解“扩展包”

28.4 扩展包API

28.5 调试模型

28.6 调试适配器

28.7 机器接口

28.8 调试Python 程序

28.9 本章总结

参考资料

第 29 章 WinDBG 及其实现

29.1 WinDBG 溯源

29.2 C 阶段的架构

29.3 重构

29.4 调试器引擎的架构

29.5 调试目标

29.6 调试会话

29.7 接收和处理命令

29.8 扩展命令的工作原理

29.9 本章总结

参考资料

第30 章 WinDBG 用法详解

30.1 工作空间

30.2 命令概览

30.3 用户界面

30.4 输入和执行命令

30.5 建立调试会话

30.6 终止调试会话

30.7 理解上下文

30.8 调试符号

30.9 事件处理

30.10 控制调试目标

30.11 单步执行

30.12 使用断点

30.13 控制进程和线程

30.14 观察栈

30.15 分析内存

30.16 遍历链表

30.17 调用目标程序的函数

30.18 命令程序

30.19 本章总结

参考资料

附录A 示例程序列表

附录B WinDBG 标准命令列表

附录C NT 内核部件缩写列表

持之若痴——代跋

,