第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
程序輸出0xcdcdcdcd,想到了什么 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文件, 斷點控制程序的執(zhí)行 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, 跟蹤執(zhí)行的強大命令 42
斷點和條件斷點(condition breakpoint),高效地控制觀測目標 44
偽寄存器,幫助保存調試的中間信息 46
Step Out的實現(xiàn) 47
2.1.7 遠程調試(Remote debug) 47
2.1.8 如何通過Windbg命令行讓中文魔獸爭霸運行在英文系統(tǒng)上 48
2.1.9 Dump文件 49
2.1.10 CDB、NTSD和重定向到Kernel Debugging 49
2.1.11 Debugger Extension,擴展Windbg的功能 50
2.2 讀懂機器的語言:匯編,CPU執(zhí)行指令的最小單元 50
2.2.1 需要用匯編來排錯的常見情況 50
案例分析:用匯編讀懂VC編譯器的優(yōu)化 51
問題描述 51
我的分析 51
案例分析:VC2003 編譯器的bug、debug模式正常,release模式會崩潰 52
例子程序 52
跟蹤匯編指令來分析 53
案例分析:臭名昭著的DLL Hell如何導致ASP.NET出現(xiàn)Server Unavailable 54
2.2.2 題外話和相關討論 55
Release比 Debug快嗎 55
2.3 理解操作系統(tǒng)對程序的反饋:異常(Exception)和通知(Debug Event) 56
2.3.1 異常(Exception)的方方面面和一篇字字珠璣的文章 56
案例分析:如何讓C++像C#一樣打印出函數(shù)調用棧(callstack) 58
2.3.2 Adplus,抓取dump的方便工具 58
案例分析:華生醫(yī)生(Dr. Watson)在什么情況下不能記錄Dump文件 59
問題描述 59
背景知識 59
問題分析 60
新的做法 61
問題解決了,可是為什么華生醫(yī)生(Dr. Watson)抓不到dump呢 61
2.3.3 通知(Debug Event)是操作系統(tǒng)跟調試器交流的一種方法 63
案例分析:VB6的版本問題 64
2.3.4 題外話和相關討論 65
錯過第一現(xiàn)場后還從dump中分析出線索嗎 65
Adplus,天天都用的工具 67
未處理異常發(fā)生后的主動退出 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參數(shù) 76
Heap trace,系統(tǒng)幫你記錄下每次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的時候發(fā)生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動態(tài)編譯 102
3.1.3 GC 內存管理 103
3.1.4 Exception Handling異常處理 104
3.2 用Windbg探索CLR的實現(xiàn) 105
3.2.1 開源的CLR實現(xiàn):Rotor 105
3.2.2 對一個Hello world的WinForm程序庖丁解牛 106
mscoree!_CorExeMain CLR引擎的入口 106
EEStartupHelper 重要的引擎初始化函數(shù) 107
mscorwks!SystemDomain::ExecuteMainMethod 執(zhí)行托管代碼的入口 108
CallDescr /MakeJitWorker Jit引擎發(fā)動的地方 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 統(tǒng)計托管內存使用信息 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 映像內存地址到托管函數(shù)名 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 用正確的態(tài)度對待問題 133
4.1.2 用簡單的提問縮小排錯的范圍 134
4.1.3 通過MPS REPORT獲取系統(tǒng)的詳細信息 135
4.1.4 通過簡單的Dump分析獲取基本信息 136
4.2 崩潰(Crash) 137
4.2.1 崩潰的萬千種不同死相 137
4.2.2 準確獲取Dump 141
Adplus:最容易上手的dump腳本 141
華生醫(yī)生(dr Watson) 143
通過Image File Execution Options讓調試器隨目標程序一起啟動 143
COM+和ASP.NET的dump獲取需要特殊配置 144
4.2.3 crash dump中需要重點關注的信息 144
案例分析:VC程序的崩潰 145
問題描述 145
MessageBox 嵌套調用 146
從源代碼中發(fā)現(xiàn)的疑點 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 性能調優(yōu)的步驟,CPU利用率是關鍵 156
4.3.3 無所不知的性能監(jiān)視器 157
使用性能監(jiān)視器的基本步驟 158
重要的計數(shù)器 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
性能監(jiān)視器的超級用法 197
C++跟C#到底誰快 199
沒有profiler怎么辦 199
4.4 資源泄漏(Resource Leak) 201
4.4.1 資源泄漏分輕重緩急 201
4.4.2 內存泄漏排錯的基本步驟 203
泄漏了什么,誰分配的,為什么無法釋放 203
定位泄漏內存的類型和增長趨勢 203
區(qū)分managed heap leak和native leak 204
案例分析:IE7的內存泄漏 205
問題描述 205
重現(xiàn)問題和基本分析 205
用傳統(tǒng)的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