第1章 比工具、技巧和经验都重要的是你的思考——从四个风格迥异的案例说起 1
1.1 绝望的性能问题:ADO.NET 2.0竟然比1.0要慢 1
1.1.1 问题描述 1
1.1.2 悲观和绝望 2
1.1.3 换位思考 3
1.1.4 排错 3
1.1.5 结论和收获 5
1.1.6 题外话和相关讨论 6
Safehandle的更多讨论 6
平衡、取舍、双赢和RFC 1925 6
Profiler的下载地址和相关资源 7
1.2 不可思议:一个API同时打开了两个文件 7
1.2.1 问题描述 7
1.2.2 第一印象 7
1.2.3 深入分析 8
1.2.4 革命尚未成功 9
1.2.5 结论 12
1.2.6 题外话和相关讨论 12
MSDN是最值得信赖的吗 12
你敢说CPU坏了 12
DWORD和文件长度 13
程序输出Oxcdcdcdcd,想到了什么 13
1.3简单的问题最棘手:稀疏平常的ASP.NET Session Lost问题 14
1.3.1 问题描述 14
1.3.2 制定策略 14
1.3.3 具体操作和结论 15
1.3.4 题外话和相关讨论 16
排查session lost的经验 16
1.4 本可以做得更好:SharePoint中文界面变英文 17
1.4.1 问题描述 17
1.4.2 排错步骤 17
1.4.3 错过的线索 19
第2章 汇编、异常、内存、同步和调试器——重要的知识点和神兵利器 21
2.1 排错的工具:调试器Windbg 21
2.1.1 调试器的功能:检查代码和资料,保存dump文件, 断点控制程序的执行 22
2.1.2 符号文件(Symbol file),把二进制和源代码对应起来 23
2.1.3 一个简单的上手程序 24
2.1.4 用Internet Explorer来操练调试器的基本命令 32
vertarget检查进程概况 32
!peb显示Process Environment Block 33
lmvm检查模块的加载信息 33
.reload/!sym加载符号文件 33
lmf列出当前进程中加载的所有模块 34
r,d,e寄存器,内存的检查和修改 34
!address显示内存页信息 35
S搜索内存 35
!runaway检查线程的CPU消耗 36
~切换目标线程 37
k,kb,kp,kv,kn检查call stack 37
u反汇编 38
x查找符号的二进制地址 38
dds对应二进制地址的符号 38
2.1.5 检查程序资料的小例子 40
.frame在栈中切换以便检查局部变量 41
dt格式化显示资料 41
2.1.6 用Windbg控制程序进行实时调试(Live Debug) 42
Wt Watch and Trace,跟踪执行的强大命令 42
断点和条件断点(condition breakpoint),高效地控制观测目标 44
伪寄存器,帮助保存调试的中间信息 46
Step Out的实现 47
2.1.7 远程调试(Remote debug) 47
2.1.8 如何通过Windbg命令行让中文魔兽争霸运行在英文系统上 48
2.1.9 Dump文件 49
2.1.10 CDB、NTSD和重定向到Kernel Debugging 49
2.1.11 Debugger Extension,扩展Windbg的功能 50
2.2 读懂机器的语言:汇编,CPU执行指令的最小单元 50
2.2.1 需要用汇编来排错的常见情况 50
案例分析:用汇编读懂VC编译器的优化 51
问题描述 51
我的分析 51
案例分析:VC2003编译器的bug、debug模式正常,release模式会崩溃 52
例子程序 52
跟踪汇编指令来分析 53
案例分析:臭名昭著的DLL Hell如何导致ASP.NET出现Server Unavailable 54
2.2.2 题外话和相关讨论 55
Release比Debug快吗 55
2.3 理解操作系统对程序的反馈:异常(Exception)和通知(Debug Event) 56
2.3.1 异常(Exception)的方方面面和一篇字字珠玑的文章 56
案例分析:如何让C++像C#一样打印出函数调用栈(callstack) 58
2.3.2 Adplus,抓取dump的方便工具 58
案例分析:华生医生(Dr.Watson)在什么情况下不能记录Dump文件 59
问题描述 59
背景知识 59
问题分析 60
新的做法 61
问题解决了,可是为什么华生医生(Dr.Watson)抓不到dump呢 61
2.3.3 通知(Debug Event)是操作系统跟调试器交流的一种方法 63
案例分析:VB6的版本问题 64
2.3.4 题外话和相关讨论 65
错过第一现场后还从dump中分析出线索吗 65
Adplus,天天都用的工具 67
未处理异常发生后的主动退出 67
如何调试UnhandledExceptionFilter 68
2.4 平坦内存空间中的层次结构:Heap和Stack 69
2.4.1 Heap是对平坦空间的高效管理和利用 69
2.4.2 PageHeap,调试Heap问题的工具 71
简单例子的多种情况 71
Heap上的内存泄漏和内存碎片 73
2.4.3 Stack overrun/corruption 74
2.4.4 题外话和相关讨论 76
PageHeap的/unaligned参数 76
Heap trace,系统帮你记录下每次Heap的操作 76
为何才分配了300MB内存,就报告Out of memory 79
2.5 找准排查问题的对应层次 80
2.5.1 从C运行库看层次 80
2.5.2 简单的_CRTDBG_MAP_ALLOC定义就可以让内存泄漏无可遁形 81
2.5.3 BSTR Cache,建立在Heap之上的COM字符串内存管理 82
2.5.4 题外话和相关讨论 83
CRT Debug Heap一定对Debug有帮助吗 83
C++中new操作符的尴尬 83
2.6 理清多个线程对资源的竞争:同步和锁 84
2.6.1 句柄泄漏、死锁和线程争用,三个典型问题 84
句柄泄漏(Handle Leak) 84
死锁(Deadlock) 84
线程争用(contention) 86
2.6.2 Windbg中的对应排错 87
!handle检查句柄信息 87
!htrace检查操作句柄的历史记录 88
!cs列出CriticalSection的详细信息 89
排查CriticalSection leak(Orphan CriticalSection) 90
Invalid handle exception 92
案例分析:ArrayList.Add的时候发生IndexOutOfRangeException 93
问题描述 93
这个异常不简单 93
具体操作 94
结论 95
2.7 调试和设计 96
2.7.1 一位热心朋友的提问 96
案例分析:反被聪明误 98
第3章 .NET Framework的原理和SOS调试——剖析CLR程序和CLR本身 101
3.1 MetaData、JIT、GC和Exception的关键点 101
3.1.1 MetaData(元资料)和引擎初始化 101
3.1.2 JIT动态编译 102
3.1.3 GC内存管理 103
3.1.4 Exception Handling异常处理 104
3.2 用Windbg探索CLR的实现 105
3.2.1 开源的CLR实现:Rotor 105
3.2.2 对一个Hello world的WinForm程序庖丁解牛 106
mscoree!_CorExeMain CLR引擎的入口 106
EEStartupHelper重要的引擎初始化函数 107
mscorwks!SystemDomain∷ExecuteMainMethod执行托管代码的入口 108
CallDescr/MakeJitWorker Jit引擎发动的地方 109
NtUserWaitMessage托管程序完成加载 111
gc_heap∷allocate_more_space/GCHeap∷GarbageCollect通过GC管理内存的分配和释放 113
AppDomain,ThreadPool,Exception,StackWalk,Security都是有趣的话题 115
3.3 通过SOS快捷方便地调试托管程序 116
3.3.1 CLR让托管程序的调试变得非常简单 116
3.3.2 SOS的命令介绍 117
3.4 用简单的程序演示SOS的常见操作 121
3.4.1 .load SOS加载SOS到Windbg 121
3.4.2 !dumpheap统计托管内存使用信息 122
3.4.3 !do显示托管对象的详细信息 122
3.4.4 !gcroot查找托管对象的引用关系 123
案例分析:ASP.NET High CPU和更多的CLR命令演示 124
!threads查看托管线程 125
!tp查看线程池和CPU占用率 125
!SyncBlk查看托管线程的lock 127
!ip2md映像内存地址到托管函数名 128
!savemodule保存模块到本地以便用reflector分析 128
著名的blog:If broken it is,fix it you should 129
3.5 题外话和相关讨论 129
3.5.1 ReleaseCOMObject释放COM对象时候的两难困境 129
3.5.2 PInvoke应该Pin住内存防止崩溃 130
3.5.3 Pin住内存又会导致内存碎片 131
3.5.4 臭名昭著的mixed DLL loading deadlock 131
3.5.5 有趣且有用的练习和更多的资料 132
第4章 崩溃,性能和资源泄漏——分享一些经验 133
4.1 排错开始前的准备工作 133
4.1.1 用正确的态度对待问题 133
4.1.2 用简单的提问缩小排错的范围 134
4.1.3 通过MPS REPORT获取系统的详细信息 135
4.1.4 通过简单的Dump分析获取基本信息 136
4.2 崩溃(Crash) 137
4.2.1 崩溃的万千种不同死相 137
4.2.2 准确获取Dump 141
Adplus:最容易上手的dump脚本 141
华生医生(dr Watson) 143
通过Image File Execution Options让调试器随目标程序一起启动 143
COM+和ASP.NET的dump获取需要特殊配置 144
4.2.3 crash dump中需要重点关注的信息 144
案例分析:VC程序的崩溃 145
问题描述 145
MessageBox嵌套调用 146
从源代码中发现的疑点 148
从This指针找崩溃的根源 149
结论 152
4.2.4 小结和更多的资源 153
4.2.5 题外话和相关讨论 153
HeapCorruption 153
StackCorruption 153
4.3 性能(Performance) 154
4.3.1 “你真牛,不如你再给我缩短10秒吧!”不是想要多快就能调到多快 155
4.3.2 性能调优的步骤,CPU利用率是关键 156
4.3.3 无所不知的性能监视器 157
使用性能监视器的基本步骤 158
重要的计数器 159
案例分析:博客园的性能问题 163
案例分析:堵塞在SqlCommand.ExecuteReader上就一定在等sql吗 168
问题背景 168
案例分析:堵塞在Assembly.Load上的deadlock 172
问题背景 172
案例分析:196个线程织成的一张网 180
问题背景 180
小结 191
4.3.4 用Profiler精确定位性能瓶颈 191
案例分析:DataTable中foreach和for loop性能差了50% 192
问题背景 192
4.3.5 题外话和相关讨论 197
Task manager跟performance monitor的差别 197
性能监视器的超级用法 197
C++跟C#到底谁快 199
没有profiler怎么办 199
4.4 资源泄漏(Resource Leak) 201
4.4.1 资源泄漏分轻重缓急 201
4.4.2 内存泄漏排错的基本步骤 203
泄漏了什么,谁分配的,为什么无法释放 203
定位泄漏内存的类型和增长趋势 203
区分managed heap leak和native leak 204
案例分析:IE7的内存泄漏 205
问题描述 205
重现问题和基本分析 205
用传统的Pageheap+UMDH找到问题根源 207
方便强大的IIS Diagnostics工具 213
结论 222
分析IIS Diag 222
4.4.3 托管内存泄漏 223
案例分析:object chain让排错简单明了 223
问题背景 223
案例分析:一个bt的案例 229
碎片的其他原因 231
4.4.4 句柄泄漏(Handle Leak) 231
4.4.5 题外话和相关讨论 233
GDI Leak 233
Desktop heap issue 233
更多的资源 235