性能分析器、記憶體分析器、代碼覆蓋率工具、錯誤注入器、Flight Recorder、AOP 織入器……這些工具的核心動力均來自 **CLR Profiling API**。本文系統介紹 API 架構、四大主流工具類型實現原理、開發探查器的關鍵技巧與避坑指南。
ICorProfilerCallback (或 v2/v3/v4 介面)。COR_ENABLE_PROFILING=1、COR_PROFILER={CLSID}、COR_PROFILER_PATH=...。| 能力類別 | 典型回調/方法 | 典型工具 |
|---|---|---|
| 生命週期事件 | AppDomainCreationStarted/ShutdownStarted, AssemblyLoadStarted/UnloadStarted, ModuleLoadStarted/UnloadStarted | 所有工具基礎 |
| JIT 編譯攔截 | JITCompilationStarted, GetILFunctionBody, SetILFunctionBody | 覆蓋率、錯誤注入、AOP 織入 |
| 函數進入/離開/尾調用 | FunctionEnter/Leave/Tailcall (需 COR_PRF_ENABLE_FUNCTION_RETVAL) | 追蹤分析器、調用圖構建 |
| 異常處理 | ExceptionThrown/CatcherEnter/Leave, ExceptionOSHandlerEnter/Leave | 異常分析、錯誤注入 |
| 垃圾回收 | GarbageCollectionStarted/Finished, ObjectAllocated, MovedReferences, RootReferences | 記憶體分析器、洩漏檢測 |
| 線程/同步 | ThreadCreated/Destroyed, RuntimeThreadSuspended/Resumed | 並發分析、死鎖檢測 |
| 內存/對象查詢 | GetObjectSize, GetClassLayout, GetTokenAndMetadataFromFunction | 堆快照、對象圖分析 |
| 類型 | 原理 | API 使用 | 優/缺點 |
|---|---|---|---|
| 追蹤 | 記錄每個函數進入/離開時間戳 | FunctionEnter/Leave 回調,或 JIT 時插桿插入日誌代碼 | ✅ 完整調用鏈、精確計時;❌ 巨大開銷、改變時序、需篩選模組 |
| 採樣 | 定期檢查指令指針 (IP),統計函數熱度 | 採樣執行緒讀 IP → GetFunctionFromIP 解析函數;高級採樣走堆棧 | ✅ 低開銷、全局視野;❌ 統計近似、難捕獲極短函數 |
ObjectAllocated 回調獲取對象大小、類型、分配堆棧。MovedReferences (壓縮階段對象移動)、RootReferences (根引用枚舉)、GarbageCollectionStarted/Finished (代/類型統計)。| 問題 | API 方案 |
|---|---|
| 是否正確釋放/終結對象? | 監控 Finalize 調用 vs Dispose,結合 ObjectAllocated 追蹤生命週期 |
| 調用關係圖 | FunctionEnter/Leave 構建有向圖,或 JIT 插桿記錄調用點 |
| 所有被捕獲異常的來源 | ExceptionCatcherEnter 記錄捕獲點,ExceptionThrown 記錄拋出點 |
newobj 改為 throw OutOfMemoryException,網絡調用改 throw TimeoutException。優勢:無需修改源碼、可動態開關、測試難以重現的錯誤路徑。FunctionEnter 統計函數覆蓋;精細版 JIT 插桿在基本塊插入計數器 (如 CoverageEye.NET)。VC++ 專案屬性 → 建置事件 → 建置後事件:regsvr32 /s "$(TargetPath)"。避免手動註冊遺忘。
C++ 專案屬性 → 偵錯 → 偵錯器類型:僅限本機;命令:目標應用路徑 (如 MyApp.exe);環境:COR_ENABLE_PROFILING=1;COR_PROFILER={YOUR_CLSID}。實現「建置/運行/調試」一鍵循環。
探查器代碼運行在 CLR 內部,AccessViolation 等本機異常會表現為 CLR 內部錯誤,導致不可預測行為。
VS → 偵錯 → 視窗 → 例外狀況設置 → 勾選 Win32 例外狀況 → 擲回。一旦探查器內部產生 AV 立即中斷,快速定位 Bug。
ICorProfilerCallback2 (針對 .NET 2.0/3.0/3.5)QueryInterface(IID_ICorProfilerCallback2) 宣告支援,否則 CLR 回退到舊介面,導致泛型相關回調缺失。E_NOTIMPL,也要實現介面並響應 QI。COR_PROFILER 環境變量只能指定一個 CLSID。若需組合功能 (如覆蓋率 + 記憶體),必須在單一 DLL 內實現多介面,或使用支援插件架構的商業工具。
corprof.idl (SDK 內)、MSDN "Profiling API Reference"。CLR Profiler (微軟官方開源)、CoverageEye.NET (GotDotNet)、PerfView (微軟內部工具開源)。CLR Profiling API 是 .NET 運行時暴露的最強大診斷介面,賦予工具「上帝視角」。掌握其架構與核心回調,不僅能理解現有工具原理,更能針對專案特定問題開發專用探查器。關鍵原則:最小化探查器開銷、正確處理併發回調、善用 JIT 插桿而非純回調、嚴格測試多版本 CLR 兼容性。