內容簡介
本書堪稱是軟體調試的“百科全書”。作者圍繞軟體調試的“生態”系統(ecosystem)、異常(exception)和調試器 3 條主線,介紹軟體調試的相關原理和機制,探討可調試性(debuggability)的內涵、意義以及實現軟體可調試性的原則和方法,總結軟體調試的方法和技巧。
第1卷主要圍繞硬體技術展開介紹。全書分為4篇,共16章。第一篇“緒論”(第1章),介紹了軟體調試的概念、基本過程、分類和簡要歷史,並綜述了本書後面將詳細介紹的主要調試技術。第二篇“CPU及其調試設施”(第2~7章),以英特爾和
ARM架構的CPU為例系統描述了CPU的調試支持。第三篇“GPU及其調試設施”(第8~14章),深入探討了Nvidia、AMD、英特爾、ARM和Imagination 這五大廠商的GPU。第四篇“可調試性”(第15~16章),介紹了提高軟體可調試性的意義、基本原則、實例和需要注意的問題,並討論了如何在軟體開發實踐中實現可調試性。
本書理論與實踐緊密結合,既涵蓋了相關的技術背景知識,又針對大量具有代表性和普遍意義的技術細節進行了討論,是學習軟體調試技術的寶貴資料。本書適合所有從事軟體開發工作的讀者閱讀,特別適合從事軟體開發、測試、支持的技術人員,從事反病毒、網路安全、著作權保護等工作的技術人員,以及高等院校相關專業的教師和學生學習參考。
圖書目錄
第 一篇 緒論
第 1章 軟體調試基礎 3
1.1 簡介 3
1.1.1 定義 3
1.1.2 基本過程 5
1.2 基本特徵 5
1.2.1 難度大 6
1.2.2 難以估計完成時間 7
1.2.3 廣泛的關聯性 7
1.3 簡要歷史 8
1.3.1 單步執行 8
1.3.2 斷點指令 10
1.3.3 分支監視 11
1.4 分類 12
1.4.1 按調試目標的系統環境分類 12
1.4.2 按目標代碼的執行方式分類 12
1.4.3 按目標代碼的執行模式分類 13
1.4.4 按軟體所處的階段分類 13
1.4.5 按調試器與調試目標的相對位置分類 14
1.4.6 按調試目標的活動性分類 14
1.4.7 按調試工具分類 15
1.5 調試技術概覽 15
1.5.1 斷點 15
1.5.2 單步執行 16
1.5.3 輸出調試信息 17
1.5.4 日誌 17
1.5.5 事件追蹤 17
1.5.6 轉儲檔案 18
1.5.7 棧回溯 18
1.5.8 反彙編 18
1.5.9 觀察和修改記憶體數據 19
1.5.10 控制被調試進程和執行緒 19
1.6 錯誤與缺欠 19
1.6.1 內因與表象 19
1.6.2 誰的bug 20
1.6.3 bug的生命周期 21
1.6.4 軟體錯誤的開支曲線 21
1.7 重要性 23
1.7.1 調試與編碼的關係 23
1.7.2 調試與測試的關係 24
1.7.3 調試與逆向工程的關係 24
1.7.4 學習調試技術的意義 25
1.7.5 調試技術尚未得到應有的重視 25
1.8 本章小結 26
參考資料 26
第二篇 CPU及其調試設施
第 2章 CPU基礎 29
2.1 指令和指令集 29
2.1.1 基本特徵 30
2.1.2 定址方式 31
2.1.3 指令的執行過程 32
2.2.1 80386處理器 34
2.2.2 80486處理器 34
2.2.3 奔騰處理器 35
2.2.4 P6系列處理器 36
2.2.6 Core 2系列處理器 38
2.2.7 Nehalem微架構 39
2.2.8 Sandy Bridge微架構 39
2.2.9 Ivy Bridge微架構 40
2.2.10 Haswell微架構 40
2.2.11 Broadwell微架構 41
2.2.12 Skylake微架構 41
2.2.13 Kaby Lake微架構 41
2.3 CPU的操作模式 42
2.4 暫存器 44
2.4.3 MSR暫存器 46
2.4.4 控制暫存器 46
2.4.5 其他暫存器 48
2.4.6 64位模式時的暫存器 49
2.5 理解保護模式 50
2.5.1 任務間的保護機制 50
2.5.2 任務內的保護 51
2.5.3 特權級 52
2.5.4 特權指令 53
2.6 段機制 54
2.6.1 段描述符 54
2.6.2 描述符表 56
2.6.3 段選擇子 56
2.6.4 觀察段暫存器 57
2.7 分頁機制 59
2.7.1 32位經典分頁 60
2.7.2 PAE分頁 66
2.7.3 IA-32e分頁 68
2.7.4 大記憶體頁 71
2.7.5 WinDBG的有關命令 72
2.8 PC系統概貌 73
2.9.1 ARM的多重含義 75
2.9.2 主要版本 76
2.9.3 操作模式和狀態 78
2.9.4 32位架構核心暫存器 80
2.9.5 協處理器 82
2.9.7 偽段支持 87
2.9.8 64位ARM架構 88
2.10 本章小結 90
參考資料 90
第3章 中斷和異常 91
3.1 概念和差異 91
3.1.1 中斷 91
3.1.2 異常 93
3.1.3 比較 93
3.2 異常的分類 93
3.2.1 錯誤類異常 93
3.2.2 陷阱類異常 94
3.2.3 中止類異常 95
3.3 異常例析 95
3.3.1 列表 95
3.3.2 錯誤代碼 97
3.3.3 示例 97
3.4 中斷/異常的優先權 99
3.5 中斷/異常處理 100
3.5.1 實模式 100
3.5.2 保護模式 101
3.5.3 IA-32e模式 109
3.6 ARM架構中的異常機制 110
3.7 本章小結 112
參考資料 113
第4章 斷點和單步執行 114
4.1 軟體斷點 114
4.1.1 INT 3 114
4.1.2 在調試器中設定斷點 115
4.1.3 斷點命中 116
4.1.4 恢復執行 118
4.1.5 特殊用途 118
4.1.6 斷點API 119
4.1.7 系統對INT 3的優待 119
4.1.8 觀察調試器寫入的INT 3指令 121
4.1.9 歸納和提示 122
4.2 硬體斷點 123
4.2.1 調試暫存器概覽 123
4.2.3 調試控制暫存器 124
4.2.4 指令斷點 127
4.2.5 調試異常 127
4.2.7 示例 129
4.2.8 硬體斷點的設定方法 132
4.2.9 歸納 134
4.3 陷阱標誌 135
4.3.1 單步執行標誌 135
4.3.2 高級語言的單步執行 136
4.3.3 任務狀態段陷阱標誌 138
4.3.4 按分支單步執行標誌 138
4.4 實模式調試器例析 140
4.4.1 Debug.exe 140
4.4.2 8086 Monitor 142
4.4.3 關鍵實現 143
4.5 反調試示例 145
4.6 ARM架構的斷點支持 147
4.6.1 斷點指令 148
4.6.2 斷點暫存器 149
4.6.3 監視點暫存器 153
4.6.4 單步跟蹤 155
4.7 本章小結 156
參考資料 157
第5章 分支記錄和性能監視 158
5.1 分支監視概覽 159
5.2 使用暫存器的分支記錄 159
5.2.1 LBR 160
5.2.2 LBR棧 161
5.2.3 示例 161
5.2.4 在Windows作業系統中的套用 165
5.3 使用記憶體的分支記錄 166
5.3.1 DS區 166
5.3.2 啟用DS機制 168
5.3.3 調試控制暫存器 168
5.4 DS示例:CpuWhere 169
5.4.1 驅動程式 170
5.4.2 套用界面 173
5.4.3 2.0版本 175
5.4.4 局限性和擴展建議 178
5.4.5 Linux核心中的BTS驅動 179
5.5 性能監視 180
5.5.1 奔騰處理器的性能監視機制 181
5.5.2 P6處理器的性能監視機制 182
5.5.3 P4處理器的性能監視 183
5.5.4 架構性的性能監視機制 186
5.5.5
酷睿微架構處理器的性能監視機制 187
5.5.6 資源 188
5.6 實時指令追蹤 188
5.6.1 工作原理 189
5.6.2 RTIT數據包 190
5.6.3 Linux支持 191
5.7 ARM架構的性能監視設施 192
5.7.1 PMUv1和PMUv2 192
5.7.2 PMUv3 194
5.7.3 示例 194
5.7.4 CoreSight 195
5.8 本章小結 195
參考資料 195
第6章 機器檢查架構 196
6.1 奔騰處理器的機器檢查機制 196
6.2 MCA 198
6.2.1 概覽 198
6.2.2 MCA的全局暫存器 199
6.2.3 MCA的錯誤報告暫存器 201
6.2.5 MCA錯誤編碼 203
6.3 編寫MCA軟體 205
6.3.1 基本算法 205
6.3.2 示例 207
6.3.3 在Windows系統中的套用 208
6.3.4 在Linux系統中的套用 210
6.4 本章小結 212
參考資料 212
第7章 JTAG調試 213
7.1 簡介 213
7.1.1 ICE 213
7.1.2 JTAG 214
7.2 JTAG原理 215
7.2.1 邊界掃描鏈路 215
7.2.2 TAP信號 216
7.2.3 TAP暫存器 217
7.2.4 TAP控制器 217
7.2.5 TAP指令 218
7.3 JTAG套用 219
7.3.1 JTAG調試 220
7.3.2 調試連線埠 221
7.4 IA處理器的JTAG支持 221
7.4.1 P6處理器的JTAG實現 221
7.4.2 探測模式 223
7.4.3 ITP接口 223
7.4.4 XDP連線埠 225
7.4.5 ITP-XDP調試儀 226
7.4.6 直接連線接口 226
7.4.7 典型套用 227
7.5.1 ARM調試接口 228
7.5.2 調試連線埠 228
7.5.3 訪問連線埠 229
7.5.4 被調試器件 229
7.5.5 調試接插口 229
7.5.6 硬體調試器 231
7.5.7 DS-5 232
7.6 本章小結 232
參考資料 233
第三篇 GPU及其調試設施
第8章 GPU基礎 237
8.1 GPU簡史 237
8.1.1 從顯示卡說起 237
8.1.3 可程式和通用化 240
8.1.4 三輪演進 242
8.2 設備身份 243
8.2.1 “餵模式” 243
8.2.2 記憶體複製 243
8.2.3 逾時檢測和復位 243
8.2.4 與CPU之並立 243
8.3 軟體接口 244
8.3.1 設備暫存器 244
8.3.2 批命令緩衝區 245
8.3.3 狀態模型 245
8.4 GPU驅動模型 247
8.4.1 WDDM 247
8.4.2 DRI和DRM 249
8.5 編程技術 250
8.5.1 著色器 250
8.5.2 Brook和CUDA 251
8.5.3 OpenCL 252
8.6 調試設施 252
8.6.1 輸出調試信息 253
8.6.2 發布斷點 253
8.6.3 其他斷點 254
8.6.4 單步執行 254
8.6.5 觀察程式狀態 254
8.7 本章小結 254
參考資料 255
第9章 Nvidia GPU及其調試設施 256
9.1 概要 256
9.1.1 一套微架構 256
9.1.2 三條產品線 256
9.1.3 封閉 257
9.2 微架構 257
9.2.1 G80(特斯拉1.0微架構) 257
9.2.2 GT200(特斯拉2.0微架構) 259
9.2.3 GF100(費米微架構) 260
9.2.4 GK110(克卜勒微架構) 261
9.2.5 GM107(麥斯威爾微架構) 263
9.2.6 GP104(帕斯卡微架構) 263
9.2.7 GV100(伏特微架構) 265
9.2.8 持續改進 267
9.3 硬體指令集 268
9.3.1 SASS 269
9.3.2 指令格式 270
9.3.3 謂詞執行 270
9.3.4 計算能力 271
9.3.5 GT200的指令集 271
9.3.6 GV100的指令集 274
9.4 PTX指令集 279
9.4.1 彙編和反彙編 280
9.4.2 狀態空間 282
9.4.3 虛擬暫存器 283
9.4.4 數據類型 284
9.4.5 指令格式 284
9.4.6 內嵌彙編 285
9.5 CUDA 286
9.5.1 源於Brook 286
9.5.2 算核 286
9.5.3 執行配置 288
9.5.4 內置變數 290
9.5.5 Warp 291
9.5.6 顯式並行 292
9.6 異常和陷阱 293
9.6.1 陷阱指令 293
9.6.2 陷阱後綴 293
9.6.3 陷阱處理 293
9.7 系統調用 296
9.7.1 vprintf 296
9.7.2 malloc和free 297
9.7.3 __assertfail 298
9.8 斷點指令 299
9.8.1 PTX的斷點指令 299
9.8.2 硬體的斷點指令 300
9.9 Nsight的斷點功能 301
9.9.1 原始碼斷點 301
9.9.2 函式斷點 301
9.9.3 根據執行緒組和執行緒編號設定條件斷點 302
9.10 數據斷點 304
9.10.1 設定方法 304
9.10.2 命中 304
9.10.3 數量限制 306
9.10.4 設定時機 306
9.11 調試符號 306
9.11.1 編譯選項 306
9.11.2 ELF載體 306
9.11.3 DWARF 307
9.12 CUDA GDB 307
9.12.1 通用命令 307
9.12.2 擴展 308
9.12.3 局限 308
9.13 CUDA調試器API 308
9.13.1 頭檔案 309
9.13.2 調試事件 309
9.13.3 工作原理 310
9.14 本章小結 312
參考資料 312
第 10章 AMD GPU及其調試設施 314
10.1 演進簡史 314
10.1.1 三個發展階段 314
10.1.2 兩種產品形態 315
10.2 Terascale微架構 315
10.2.1 總體結構 315
10.2.2 SIMD核心 317
10.2.3 VLIW 317
10.2.4 四類指令 318
10.3 GCN微架構 318
10.3.1 邏輯結構 319
10.3.2 CU和波陣 319
10.3.3 記憶體層次結構 321
10.3.4 工作組 321
10.3.5 多執行引擎 323
10.4 GCN指令集 323
10.4.1 7種指令類型 323
10.4.2 指令格式 324
10.4.3 不再是VLIW指令 324
10.4.4 指令手冊 324
10.5 編程模型 325
10.5.1 地幔 325
10.5.2 HSA 326
10.5.3 ROCm 326
10.5.4 Stream SDK和APP SDK 327
10.5.5 Linux系統的驅動 327
10.6 異常和陷阱 327
10.6.1 9種異常 328
10.6.2 啟用 328
10.6.3 陷阱狀態暫存器 328
10.6.4 陷阱處理器基地址 329
10.6.5 陷阱處理過程 329
10.7 控制波陣的調試接口 330
10.7.1 5種操作 330
10.7.2 指定目標 330
10.7.3 傳送接口 331
10.7.4 限制 332
10.8 地址監視 332
10.8.1 4種監視模式 332
10.8.2 數量限制 333
10.8.3 報告命中 333
10.8.4 暫存器接口 333
10.8.5 用戶空間接口 333
10.9 單步調試支持 333
10.9.1 單步調試模式 334
10.9.2 控制方法 334
10.10 根據調試條件實現分支跳轉的指令 335
10.10.1 兩個條件標誌 335
10.10.2 4條指令 335
10.11 代碼斷點 335
10.11.1 陷阱指令 336
10.11.2 在GPU調試SDK中的使用 336
10.12 GPU調試模型和開發套件 337
10.12.1 組成 337
10.12.2 進程內調試模型 337
10.12.3 面向事件的調試接口 339
10.13 ROCm-GDB 340
10.13.1 原始碼 340
10.13.2 安裝和編譯 340
10.13.3 常用命令 340
10.14 本章小結 341
參考資料 342
第 11章 英特爾GPU及其調試設施 343
11.1 演進簡史 343
11.1.1 i740 343
11.1.2 集成顯示卡 344
11.1.3 G965 345
11.1.4 Larabee 345
11.1.5 GPU 346
11.1.6 第三輪努力 347
11.1.7 公開文檔 347
11.2 GEN微架構 348
11.2.1 總體架構 349
11.2.2 片區布局 350
11.2.3 子片布局 351
11.2.4 EU 352
11.2.5 經典架構圖 353
11.3 暫存器接口 354
11.3.1 兩大類暫存器 354
11.3.2 顯示功能的暫存器 355
11.4 命令流和環形緩衝區 357
11.4.1 命令 357
11.4.2 環形緩衝區 358
11.4.3 環形緩衝區暫存器 359
11.5 邏輯環上下文和執行列表 360
11.5.1 LRC 360
11.5.2 執行鍊表提交連線埠 362
11.5.3 理解LRC的提交和執行過程 362
11.6 GuC和通過GuC提交任務 365
11.6.1 載入固件和啟動GuC 365
11.6.2 以MMIO方式通信 366
11.6.3 基於共享記憶體的命令傳遞機制 367
11.6.4 提交工作任務 367
11.7 媒體流水線 368
11.7.1 G965的媒體流水線 368
11.7.2 MFX引擎 370
11.7.3 狀態模型 370
11.7.4 多種計算方式 371
11.8 EU指令集 372
11.8.1 暫存器 372
11.8.2 暫存器區塊 373
11.8.3 指令語法 375
11.8.4 VLIW和指令級別並行 375
11.9 記憶體管理 377
11.9.1 GGTT 377
11.9.2 PPGTT 378
11.9.3 I915和GMMLIB 379
11.10 異常 379
11.10.1 異常類型 379
11.10.2 系統過程 380
11.11 斷點支持 381
11.11.1 調試控制位 381
11.11.2 操作碼匹配斷點 381
11.11.3 IP匹配斷點 381
11.11.4 初始斷點 382
11.12 單步執行 382
11.13 GT調試器 382
11.13.1 架構 382
11.13.2 調試事件 384
11.13.3 符號管理 385
11.13.4 主要功能 385
11.13.5 不足 385
11.14 本章小結 386
參考資料 386
第 12章 Mali GPU及其調試設施 387
12.1 概況 387
12.1.1 源於挪威 387
12.1.2 納入ARM 387
12.1.3 三代微架構 388
12.1.5 精悍的團隊 389
12.1.6 封閉的技術文檔 389
12.1.7 單元化設計 389
12.2 Midgard微架構 389
12.2.1 邏輯結構 390
12.2.2 三流水線著色器核心 390
12.2.3 VLIW指令集 392
12.3 Bifrost微架構 393
12.3.1 邏輯結構 393
12.3.2 執行核心 394
12.3.3 標量指令集和Warp 395
12.4 Mali圖形調試器 395
12.4.1 雙機模式 395
12.4.2 面向幀調試 396
12.5 Gator 396
12.5.1 Gator核心模組(gator.ko) 397
12.5.2 Gator檔案系統(gatorfs) 397
12.5.3 Gator後台服務(gatord) 398
12.5.4 Kbase驅動中的gator支持 399
12.5.5 含義 399
12.6 Kbase驅動的調試設施 399
12.6.1 GPU版本報告 399
12.6.2 編譯選項 400
12.6.3 DebugFS下的虛擬檔案 401
12.6.4 SysFS下的虛擬檔案 401
12.6.5 基於ftrace的追蹤設施 401
12.6.6 Kbase的追蹤設施 402
12.7 其他調試設施 403
12.7.1 Caiman 403
12.7.2 devlib 404
12.7.3 Mali離線編譯器 404
12.8 缺少的調試設施 405
12.8.1 GPGPU調試器 405
12.8.2 GPU調試SDK 406
12.8.3 反彙編器 406
12.8.4 ISA文檔 406
12.9 本章小結 406
參考資料 406
第 13章 PowerVR GPU及其調試設施 407
13.1 概要 407
13.1.1 發展簡史 407
13.1.2 兩條產品線 409
13.1.3 基於圖塊延遲渲染 409
13.1.4 Intel GMA 409
13.1.5 開放性 410
13.2 Rogue微架構 410
13.2.1 總體結構 410
13.2.2 USC 411
13.2.3 ALU流水線 412
13.3 參考指令集 413
13.3.1 暫存器 414
13.3.2 指令組 414
13.3.3 指令修飾符 415
13.3.4 指令類型 415
13.3.5 標量指令 416
13.3.6 並行模式 416
13.4 軟體模型和微核心 417
13.4.1 軟體模型 417
13.4.2 微核心的主要功能 417
13.4.3 優點 418
13.4.4 存在的問題 418
13.5 斷點支持 418
13.5.1 bpret指令 419
13.5.2 數據斷點 419
13.5.3 ISP斷點 420
13.6 離線編譯和反彙編 420
13.6.1 離線編譯 420
13.6.2 反彙編 421
13.7 PVR-GDB 421
13.7.1 跟蹤調試 421
13.7.2 暫存器訪問 422
13.7.3 其他功能 422
13.7.4 全局斷點和局限性 422
13.8 本章小結 423
參考資料 423
第 14章 GPU綜述 424
14.1 比較 424
14.1.1 開放性 424
14.1.2 工具鏈 425
14.1.3 開發者文檔 425
14.2 主要矛盾 425
14.2.1 專用性和通用性 426
14.2.2 強硬體和弱軟體 426
14.3 發展趨勢 426
14.3.1 從固定功能單元到通用執行引擎 426
14.3.2 從向量指令到標量指令 427
14.3.3 從指令並行到執行緒並行 427
14.4 其他GPU 427
14.4.1 Adreno 428
14.4.2 VideoCore 428
14.4.3 圖芯GPU 429
14.4.4 TI TMS34010 429
14.5 學習資料和工具 430
14.5.1 文檔 430
14.5.2 原始碼 430
14.5.3 工具 431
14.6 本章小結 432
參考資料 432
第四篇 可調試性
第 15章 可調試性概覽 435
15.1 簡介 435
15.2 觀止和未雨綢繆 436
15.2.1 NT 3.1的故事 436
15.2.2 未雨綢繆 438
15.3 基本原則 439
15.3.1 最短距離原則 439
15.3.2 最小範圍原則 439
15.3.3 立刻終止原則 440
15.3.4 可追溯原則 441
15.3.5 可控制原則 442
15.3.6 可重複原則 442
15.3.7 可觀察原則 442
15.3.8 易辨識原則 443
15.3.9 低海森伯效應原則 443
15.4 不可調試代碼 444
15.4.1 系統的異常分發函式 444
15.4.2 提供調試功能的系統函式 444
15.4.3 對調試器敏感的函式 445
15.4.4 反跟蹤和調試的程式 445
15.4.5 時間敏感的代碼 446
15.4.6 應對措施 446
15.5 可調試性例析 446
15.5.1 健康性檢查和BSOD 447
15.5.2 可控制性 447
15.5.3 公開的符號檔案 448
15.5.4 WER 448
15.5.5 ETW和日誌 448
15.5.6 性能計數器 449
15.5.7 內置的核心調試引擎 449
15.5.8 手動觸發崩潰 449
15.6 與安全、商業秘密和性能的關係 449
15.6.1 可調試性與安全性 450
15.6.2 可調試性與商業秘密 450
15.6.3 可調試性與性能 450
15.7 本章小結 450
參考資料 451
第 16章 可調試性的實現 452
16.1 角色和職責 452
16.1.1 架構師 452
16.1.2 程式設計師 453
16.1.3 測試人員 453
16.1.4 產品維護和技術支持工程師 454
16.1.5 管理者 454
16.2 可調試架構 455
16.2.1 日誌 455
16.2.2 輸出調試信息 456
16.2.3 轉儲 457
16.2.4 基類 458
16.2.5 調試模型 458
16.3 通過棧回溯實現可追溯性 459
16.3.1 棧回溯的基本原理 459
16.3.2 利用DbgHelp函式館回溯棧 461
16.3.3 利用RTL函式回溯棧 465
16.4 數據的可追溯性 466
16.4.1 基於數據斷點的方法 466
16.4.2 使用對象封裝技術來追蹤數據變化 471
16.5 可觀察性的實現 472
16.5.1 狀態查詢 472
16.5.2 WMI 473
16.5.3 性能計數器 475
16.5.4 轉儲 478
16.5.5 列印或者輸出調試信息 479
16.5.6 日誌 480
16.6 自檢和自動報告 480
16.6.1 BIST 480
16.6.2 軟體自檢 481
16.6.3 自動報告 482
16.7 本章小結 482
參考資料 483