共用方式為


分析概觀

分析工具是一種工具,可監視其他應用程式的執行狀況。 Common Language Runtime (CLR) 分析工具屬於一種動態連結程式庫 (DLL),是由使用分析 API 於 CLR 中接收和傳送訊息的函式所組成。 CLR 會在執行階段時載入分析工具 DLL。

傳統程式碼剖析工具著重在測量應用程式的執行。 也就是說,分析工具會測量每個函式所耗費的時間,或者一段時間之後應用程式的記憶體用量。 分析 API 的目標在於更廣泛的診斷工具類別,例如程式碼涵蓋範圍的公用程式,甚至進階偵錯輔助等。 這些用法本質上都是診斷的一種。 分析 API 不僅會測量,也會監視應用程式的執行狀況。 因此,應用程式本身絕對不應該使用分析 API,而且應用程式的執行也不應該取決於分析工具 (或受到影響)。

分析 CLR 應用程式所需要的支援,會比傳統上分析編譯之機器碼 (Machine Code) 還來得多。 這是因為 CLR 引入了如應用程式定義域、記憶體回收、Managed 例外狀況處理、Just-in-Time (JIT) 程式碼編譯 (將 Microsoft Intermediate Language (MSIL) 程式碼轉換為原生機器碼) 等的類似功能概念。 傳統分析機制無法識別或提供有關這些功能的有用資訊。 分析 API 則會有效提供此遺漏的資訊,並在效能上對 CLR 和所分析的應用程式影響最小。

執行階段進行的 JIT 編譯則提供分析的良好機會。 分析 API 可讓分析工具在 JIT 編譯程式碼之前,變更常式的記憶體中 MSIL 程式碼資料流。 在這個方法中,分析工具會動態地將檢測程式碼加入至需要更深入調查的特定常式。 雖然在傳統案例中也可能使用這個方法,不過使用分析 API 來實作 CLR 一定更加容易。

本概觀包含下列各節:

  • 分析 API

  • 支援的功能

  • 通知執行緒

  • 安全性

  • 在程式碼剖析工具中結合 Managed 和 Unmanaged 程式碼

  • 分析 Unmanaged 程式碼

  • 使用 COM

  • 回呼

  • 回呼和堆疊深度

  • 相關主題

分析 API

一般來說,分析 API 會用來撰寫「程式碼分析工具」(Code Profiler),這是會監督 Managed 應用程式執行狀況的程式。

分析 API 是由分析工具 DLL 所使用,此 DLL 會載入與已分析之應用程式相同的處理序中。 分析工具 DLL 會實作回呼介面 (在 .NET Framework 1.0 版和 1.1 版中是 ICorProfilerCallback,在 2.0 (含) 以後版本中則是 ICorProfilerCallback2)。 CLR 會呼叫該介面中的方法,告知分析工具已分析之處理序中所發生的事件。 分析工具可以使用 ICorProfilerInfoICorProfilerInfo2 介面中的方法回呼至執行階段,以取得與所分析之應用程式狀態相關的資訊,

注意事項注意事項

只有分析工具解決方案的資料收集部分,才應該與所分析的應用程式於相同的處理序中執行。所有使用者介面和資料分析都應該在不同的處理序中執行。

下圖說明分析工具 DLL 與正在進行分析之應用程式以及 CLR 互動的方式。

分析架構

剖析架構

告知介面

ICorProfilerCallbackICorProfilerCallback2 可以視為告知介面。 這些介面是由如 ClassLoadStartedClassLoadFinishedJITCompilationStarted 之類的方法所組成。 每次 CLR 載入或卸載類別、編譯函式等時,它會呼叫分析工具之 ICorProfilerCallbackICorProfilerCallback2 介面中對應的方法。

例如,分析工具可以透過兩個告知函式來測量程式碼效能:FunctionEnter2FunctionLeave2。 它會在每個告知上加上時間戳記、彙總結果,並輸出清單,此清單會指出在應用程式執行期間,哪些函式消耗最多的 CPU 或時間。

資訊擷取介面

其他與分析相關的主要介面為 ICorProfilerInfoICorProfilerInfo2。 分析工具會在需要時呼叫這些介面,以取得更多資訊協助進行分析。 例如,每次 CLR 呼叫 FunctionEnter2 函式時,它就會提供函式識別項。 分析工具可以經由呼叫 ICorProfilerInfo2::GetFunctionInfo2 方法而取得該函式的詳細資訊,以探索函式的父類別、名稱等資訊。

回到頁首

支援的功能

分析 API 會提供在 Common Language Runtime 中發生之各種事件和動作的相關資訊。 您可以使用這些資訊來監視處理序的內部工作,並分析 .NET Framework 應用程式的效能。

分析 API 會擷取在 CLR 中發生之下列動作和事件的相關資訊:

  • CLR 啟動和關閉事件。

  • 應用程式定義域建立和關閉事件。

  • 組件載入與卸載事件。

  • 模組載入與卸載事件。

  • COM vtable 建立與解構事件。

  • Just-in-Time (JIT) 編譯與程式碼定位事件。

  • 類別載入與卸載事件。

  • 執行緒建立與解構事件。

  • 函式進入與結束事件。

  • 例外狀況。

  • Managed 和 Unmanaged 程式碼執行之間的轉換。

  • 不同執行階段內容之間的轉換。

  • 執行階段暫止的相關資訊。

  • 執行階段記憶體堆積 (Heap) 和記憶體回收活動的相關資訊。

分析 API 可以透過任何與 (非 Managed) COM 相容的語言來呼叫。

API 的效率與 CPU 和記憶體消耗有關。 分析不會涉及已分析應用程式的變更,這些變更因為太明顯而會導致錯誤的結果。

對於取樣和非取樣分析工具來說,分析 API 十分有幫助。 「取樣分析工具」(Sampling Profiler) 每 5 毫秒會檢查一次設定。 當執行緒導致事件發生時,該事件會同步告知「非取樣分析工具」(Non-sampling Profiler)。

不支援的功能

分析 API 不支援下列功能:

  • 必須使用慣用的 Win32 方法進行分析的 Unmanaged 程式碼。 不過,CLR 分析工具包含轉換事件,可判斷 Managed 和 Unmanaged 程式碼之間的界線。

  • 為了方面導向程式設計 (Aspect Oriented Programming,AOP) 而修改本身程式碼的自我修改應用程式。

  • 界限檢查,因為分析 API 不會提供這項資訊。 CLR 會對所有 Managed 程式碼的繫結檢查提供內建支援。

  • 遠端分析,不支援的原因如下:

    • 遠端分析會增加執行時間。 使用分析介面時,您必須使執行時間降至最低,這樣才不至於過度影響分析結果。 尤其在監視執行效能時更是如此。 不過,當使用分析介面來監視記憶體使用量,或取得堆疊框架、物件等的執行階段資訊時,遠端分析就不會成為限制。

    • 在執行已分析之應用程式的本機電腦上,CLR 程式碼分析工具必須以執行階段註冊一個或多個回呼介面。 這樣會限制建立遠端程式碼分析工具的能力。

  • 在具有高可用性需求的實際執行環境中進行分析。 會建立分析 API 以支援開發階段診斷。 尚未經歷支援實際執行環境所需的精確測試。

回到頁首

通知執行緒

在大多數情況下,產生事件的執行緒也會執行通知。 如這類通知 (例如,FunctionEnterFunctionLeave) 不需要提供明確 ThreadID。 同時,分析工具可能會根據受影響執行緒的 ThreadID,決定使用執行緒區域儲存區 (Thread Local Storage) 來儲存及更新其分析區塊,而不是在全域儲存區中對分析區塊進行索引。

請注意,這些回呼並未序列化。 使用者必須建立安全執行緒 (Thread-Safe) 的資料結構並鎖定必須的分析工具程式碼以保護其程式碼,進而防止多個執行緒的平行存取。 因此,在特定情況下,您會接收到不尋常的回呼序列。 例如,假設 Managed 應用程式正在繁衍 (Spawn) 兩個執行相同程式碼的執行緒。 在這個情況下,可能會從某個執行緒中接收某些函式的 ICorProfilerCallback::JITCompilationStarted 事件,並從其他執行緒接收 FunctionEnter 回呼,然後才會接收 ICorProfilerCallback::JITCompilationFinished 回呼。 此時,使用者在尚未完全以 just-in-time (JIT) 編譯的函式中會接收 FunctionEnter 回呼。

回到頁首

安全性

分析工具 DLL 是 Unmanaged DLL,會當做 Common Language Runtime 執行引擎的一部分來執行。 因此,分析工具 DLL 中的程式碼不會受制於 Managed 程式碼存取安全性的限制。 分析工具 DLL 上的唯一限制,是由作業程式對執行已分析之應用程式的使用者所加諸的限制。

分析工具作者應該採取適當的預防措施,以避免發生與安全性相關的問題。 例如在安裝期間,應該將分析工具 DLL 加入至存取控制清單 (ACL),如此惡意使用者就無法修改。

回到頁首

在程式碼剖析工具中結合 Managed 和 Unmanaged 程式碼

不正確撰寫的分析工具可能會造成它本身的循環參考,因此產生無法預期的行為。

檢視 CLR 分析 API,您可能產生下列印象:可撰寫包含 Managed 和 Unmanaged 元件的分析工具,並透過 COM Interop 或間接呼叫,讓這些元件彼此呼叫。

雖然從設計觀點來看這是可行的,但分析 API 並不支援 Managed 元件。 CLR 分析工具必須是完全 Unmanaged。 嘗試在 CLR 分析工具中結合 Managed 和 Unmanaged 程式碼,可能會造成存取違規、程式失敗或死結。 分析工具的 Managed 元件會對其 Unmanaged 元件引發事件,接著這些 Unmanaged 元件會再次呼叫 Managed 元件,因此產生循環參考。

CLR 分析工具可安全呼叫 Managed 程式碼的唯一位置,是在方法的 Microsoft Intermediate Language (MSIL) 主體中。 在完成函式的 Just-In-Time (JIT) 編譯之前,分析工具可以在方法的 MSIL 主體中插入 Managed 呼叫,接著對它進行 JIT 編譯 (請參閱 ICorProfilerInfo::GetILFunctionBody 方法)。 這個技巧可以成功用於選擇性檢測 Managed 程式碼,或收集 JIT 的統計資料和效能資料。

或者,程式碼分析工具可以在呼叫 Unmanaged 程式碼的每個 Managed 函式的 MSIL 主體中插入原生攔截程序。 這個技巧可用於檢測和涵蓋範圍。 例如,程式碼分析工具可以在每個 MSIL 區塊後插入檢測攔截程序,以確定已執行區塊。 修改方法的 MSIL 主體是非常精細的作業,有許多因素應納入考量。

回到頁首

分析 Unmanaged 程式碼

Common Language Runtime (CLR) 分析 API 會提供分析 Unmanaged 程式碼的最小支援。 提供的功能如下:

  • 堆疊鏈結的列舉型別。 這個功能可讓程式碼分析工具判斷 Managed 程式碼和 Unmanaged 程式碼之間的界限。

  • 判斷堆疊鏈結是對應至 Managed 程式碼或機器碼。

在 .NET Framework 1.0 版和 1.1 版中,透過 CLR 偵錯 API 的同處理序 (In-Process) 子集,即可使用這些方法。 這些方法是在 CorDebug.idl 檔案中定義,並在 CLR 偵錯概觀中說明。

在 .NET Framework 2.0 (含) 以後版本中,您可以使用 ICorProfilerInfo2::DoStackSnapshot 方法來執行此功能。

回到頁首

使用 COM

雖然分析介面已定義為 COM 介面,但 Common Language Runtime (CLR) 實際上不會初始化 COM 以使用這些介面。 原因是為了避免在 Managed 應用程式有機會指定其所需的執行緒模型之前,便必須使用 CoInitialize 函式來設定執行緒模型。 同樣地,分析工具本身不應呼叫 CoInitialize,因為它可能會挑選與要分析之應用程式不相容的執行緒模型,並造成應用程式失敗。

回到頁首

呼叫堆疊

分析 API 提供了兩個取得呼叫堆疊的方式:啟用疏鬆收集呼叫堆疊的堆疊快照方法,以及在每個時刻追蹤呼叫堆疊的陰影堆疊方法。

堆疊快照

堆疊快照是追蹤某個時刻的執行緒堆疊。 分析 API 支援追蹤堆疊上的 Managed 函式,但會將 Unmanaged 函式追蹤作業交給分析工具自己的堆疊查核器。

如需如何用程式設計分析工具來查核 Managed 堆疊的詳細資訊,請參閱本文件集的 ICorProfilerInfo2::DoStackSnapshot 方法,以及 MSDN Library 中的 .NET Framework 2.0 中分析工具堆疊查核行程:基礎與進階 (英文)。

陰影堆疊

太常使用快照方法,可能會造成效能問題。 如果您想要經常執行堆疊追蹤,分析工具應改用 FunctionEnter2FunctionLeave2FunctionTailcall2ICorProfilerCallback2 例外狀況回呼,來建立陰影堆疊。 陰影堆疊一定是最新的,而且在需要堆疊快照時,可以快速複製到儲存體。

陰影堆疊可以取得函式引數、傳回值和泛型執行個體化資訊。 只有透過陰影堆疊,才能使用此資訊,並在控制權交給函式時,才能取得此資訊。 不過,稍後當函式執行時,便無法使用此資訊。

回到頁首

回呼和堆疊深度

分析工具回呼可能會在極限制堆疊的情況下發出,而分析工具回呼中的堆疊溢位 (Stack Overflow) 則會導致處理序立即結束。 分析工具在回應回呼時,應該確定盡可能使用少量的堆疊。 如果要針對要確實防止堆疊溢位的處理序使用分析工具,分析工具本身也應該避免觸發堆疊溢位。

回到頁首

相關主題

標題

說明

.NET Framework 2.0 中的程式碼剖析

描述在 .NET Framework 2.0 (含) 以後版本中,分析程序的變更與改進。

設定程式碼剖析環境

說明如何初始化分析工具、設定事件告知以及分析 Windows 服務。

分析 API 中的載入器回呼

討論回呼 (Callback),發出回呼的目的是為了載入應用程式定義域、組件 (Assembly)、模組和類別而。

分析 API 的記憶體回收

解釋如何觸發、偵測及封鎖記憶體回收。

分析 API 的物件追蹤

解釋在記憶體回收期間移動之物件的追蹤方式。

分析 API 的物件檢查

解釋分析工具可如何使用中繼資料 (Metadata) 來取得物件的相關資訊。

分析 API 的例外處理

討論分析工具會如何監視例外狀況 (Exception) 事件。

分析 API 中的程式碼產生

描述分析工具 (Profiler) 如何控制自動和手動程式碼產生。

分析和執行階段通知識別碼

討論由 Common Language Runtime 傳遞至分析工具的類別、執行緒和應用程式定義域 ID。

程式碼剖析 API 方法慣例

討論 HRESULT 傳回值、如何配置傳回緩衝區供分析 API 使用,以及如何使用選擇性輸出參數。

分析介面

描述分析 API 使用的 Unmanaged 介面。

分析全域靜態函式

描述分析 API 使用的 Unmanaged 全域靜態函式。

分析列舉

描述分析 API 使用的 Unmanaged 列舉型別。

分析結構

描述分析 API 使用的 Unmanaged 結構。

回到頁首