用戶如何修復他們的代碼中的 Bug?您設置一些斷點、在調試器下運行程序 、進行一點單步調試 – 並祈求能夠輕而易舉地發現問題,這樣您就能繼續處理 其他事情。
幾乎自 ENIAC 發明以來,我們就一直在進行著同樣方式的調試。這種繁瑣而 耗時的調試方法為我們提供了很好的幫助,但是時候使調試更加輕松了。隨著 Visual Studio 2010 Ultimate 的發布,新的 IntelliTrace 功能使開發人員能 夠更深入地了解應用程序的執行情況,從而使調試進入了 21 世紀。
與其他監視和跟蹤工具(例如 Windows Sysinternals 中的 Process Monitor)非常類似,Visual Studio 2010 在應用程序執行時收集有關應用程序 的數據,來幫助開發人員診斷錯誤。收集的數據稱為 IntelliTrace 事件。這些 事件將在默認調試過程中收集,此外,它們使開發人員能夠進行回溯以查看應用 程序中發生的情形,而不必重新啟動調試器。
在本文中,我將向您介紹 IntelliTrace,並演示它如何在開發人員的日常開 發活動中體現出價值。我將演示 IntelliTrace 如何提供在應用程序執行過程中 所發生事件的時間線,以及開發人員如何能夠使用這些事件來幫助調試。接著, 我將論述一些設置,開發人員可以更改這些設置來收集有關應用程序的一組更深 層的信息,從而獲得完整的執行歷史記錄。最後,我將演示如何使用其他人(測 試人員)創建的以前記錄的 IntelliTrace 文件來調試應用程序,而不必運行應 用程序來重現錯誤。
當 Visual Studio 診斷團隊開始規劃 Visual Studio 2010 時,我們花費了 很多時間與客戶討論,了解客戶如何診斷其應用程序中的問題。盡管每個人都有 不同的方式和喜歡使用的工具集,但有一點是絕對清楚的:傳統的應用程序問題 診斷方法困難、耗時而且成本高昂。開發人員收到的 Bug 報告幾乎從沒有任何 用於重現問題的步驟,並且大部分都是由像“我正在使用程序,突然間程序崩潰 了”這樣的語句組成。即使在極個別的情況下提供了有效的重現步驟,也可能會 在特定的環境中出現 Bug,而這又會導致出現一組需要解決的全新問題。而且, Bug 通常是由於誤解了框架或其他代碼的運行方式而導致的。
考慮到這些難題,我們著手創建了一個新的調試器功能,用於在問題發生時 收集到正確的信息。我們的目標是為開發人員提供准確的重現步驟和系統環境設 置,以及公開他們所使用的框架和代碼的行為,從而大幅提高可診斷性。隨著 Visual Studio 2010 Ultimate 的發布,IntelliTrace 使開發人員能夠更深入 地了解應用程序和框架行為,並能夠打開由測試人員收集的 IntelliTrace 文件 來解決“無法重現”的情況,從而大大改善了調試體驗。
IntelliTrace 簡介
當開發人員需要更深入地了解代碼執行情況時,IntelliTrace 提供了一種“ 加速調試”的方式來收集應用程序的完整執行歷史記錄。
為了闡釋這一點,我將使用 Tailspin Toys 演示應用程序來演示 IntelliTrace 可收集的信息類型。首先,我將在 Visual Studio 中打開解決方 案並啟動調試。當網站啟動時,我將導航到“關於我們”頁,並收到來自服務器 的錯誤。如何才能診斷問題?如果您像我一樣,則您首先想到的是配置 web.config 文件以不顯示自定義錯誤,然後重新啟動調試器。但如果此問題是 間歇性的,又將如何呢?如果您可以在錯誤發生後就在此時進入進程,並從 Visual Studio 中獲得應用程序中所發生情形的歷史記錄,是不是很好?
當您進行調試時,IntelliTrace 將在後台收集有關托管應用程序的數據,其 中包括來自許多框架組件(例如 ADO.NET、ASP.NET 和 System.XML)的信息。 這些 IntelliTrace 事件使開發人員能夠查看先前在執行過程中發生的情況,並 且最重要的是,能夠進行“回溯”以查看應用程序的先前狀態,而不必重新啟動 調試器。當我進入調試器時,我立即看到了按順序列出的以前收集的 IntelliTrace 事件(請參見圖 1)。
圖 1 IntelliTrace 收集的診斷信息
正如您可從圖 1 中看到的,IntelliTrace 事件的列表不僅僅局限於您在 Process Monitor 中看到的文件和注冊表訪問。我們為 Visual Studio 2010 定 義了將近 150 個 IntelliTrace 事件,並計劃隨著時間的推移用其他事件擴充 此列表。圖 2 重點列出了一些 IntelliTrace 所收集事件的類別。
圖 2 IntelliTrace 事件可跨 Microsoft .NET Framework 使用
類別 描述和收集的數據 ADO.NET 與針對 SQL 執行查詢、執行的命令以及連接字符串相關的事件。 MVC 與 ASP.NET 管道以及請求處理和重定向相關的事件。 控制台 控制台輸出。 數據綁定 Windows 窗體數據綁定。 環境變量 對進程中的環境變量進行求值和檢索。 文件 創建、刪除和訪問文件。 手勢 用戶對 Web 窗體、Windows 窗體和 WPF 中的常見控件執行的操作。 除了收集有關與控件的交互的數據之外,單擊其中一個事件還會自動將您重定向 到相應的事件處理程序。 遲緩初始化 初始化延遲加載的變量。 注冊表 創建、刪除和查詢注冊表信息。 服務模型 從 WCF 中進行的 Web 服務調用。 線程處理 用戶工作項和並行計算任務的排隊。 跟蹤 調試器跟蹤輸出和斷言。 用戶提示 顯示 Windows 窗體和 WPF 消息框以及對話框的結果。 工作流 實例化和完成活動。 XML XML 文件加載。
IntelliTrace 窗口允許我按類別(圖 2 中顯示的類別)或按線程對所收集 事件的列表進行篩選。此外,我可以執行基於文本的搜索來查找可快速跳轉到的 重要事件。由於 IntelliTrace 還會收集異常,因此我可以搜索詞條“異常”, 列表將進行篩選以列出導致出現 ASP.NET 錯誤頁的異常,既包括在其中引發的 異常,也包括在其中捕獲的異常。在本例中,錯誤是在分析位於第 10 行位置 53 的實體時由 XMLException 導致的(請參見圖 3)。當我單擊引發的異常事 件時,其他調試器窗口(例如“調用堆棧”和 “監視”窗口)將顯示與事件本 身相關的數據,因此就好像引發異常時您正在進行調試一樣。此外,就像進入調 用堆棧一樣,編輯器將打開相應的源文件,並以橙色突出顯示與事件對應的代碼 行以表示 IntelliTrace。
圖 3 在分析位於第 10 行位置 53 上的實體時引發了 XMLException
IntelliTrace 為我提供了用於診斷問題的一段很有用的信息:加載了 XML 文件,而該 XML 文件中的特定字符為意外字符或不正確。但我仍然不知道訪問 了哪個文件。同樣,IntelliTrace 收集了我所需的信息(即文件訪問)。
再次查看圖 3 中的 IntelliTrace 窗口,我可以看到緊靠異常之前的事件是 “Content\Xml\Ads.xml”的 XML 文件加載事件。此文件肯定是導致錯誤的文件 。我可以輕松地在 Visual Studio 中打開此文件。查看第 10 行的位置 53,我 看到此文件中確實有一個錯誤,即“&b=1”對於 NavigateUrl XML 元素無 效。通過刪除這些無效字符,網站現在應可正常加載。
現在,我希望您考慮一下您用傳統調試技術調試的上一個未處理異常。如果 它是像這一樣的異常,您將會看到異常的發生位置,但肯定看不到確切原因或無 效字符。這就是 IntelliTrace 的關鍵所在 – 它為您提供了更好的信息來更快 捷輕松地診斷問題。您有更重要的事情要做,而不是浪費時間來四處尋找信息。
使用 IntelliTrace 來跟蹤調試器事件
我剛剛向您演示了 IntelliTrace 如何收集在應用程序的執行過程中發生的 異常(已處理異常和未處理異常),以及如何通過跨框架的 IntelliTrace 事件 來深入了解應用程序在後台執行的操作。但 IntelliTrace 的功能並非僅限於此 。IntelliTrace 還可收集調試器所導致的事件,即斷點、跟蹤點和單步執行事 件。
最常見的調試技術之一是在您認為存在問題的位置附近設置斷點,然後單步 執行代碼並監視變量變化。在調試循環步驟、監視變量變為特定值時,這一點特 別有用。遺憾的是,大多數開發人員都沒有耐心,並連續按 F10 鍵來快速單步 執行代碼,結果卻發現他們單步執行過了頭。然後,他們需要重新啟動其調試會 話並重試。利用 IntelliTrace,將會記錄所有斷點和單步執行事件,以及這些 事件的上下文數據,這樣,您就能夠快速導航到之前停止的位置。
如果我單擊其中一個調試器事件,“監視”窗口將顯示我之前查看的所有數 據,其中包括我在“局部變量”、“監視”和“自動變量”窗口中計算的值,以 及“快速監視”和“數據提示”。
通常,以前開發和部署的代碼沒有內置所需的跟蹤來幫助調試可能出現的問 題。利用斷點,可以詳細了解應用程序在後台所執行的操作。但大多數情況下, 開發人員不需要在斷點處停止;而是希望收集某些數據並繼續執行。在您希望記 錄迭代器值而不必在每個迭代處停止的循環內,情況尤為如此。在這些情況下, 跟蹤點是絕佳的替代方案。利用跟蹤點,開發人員能夠讓調試器執行自定義操作 ;也就是說,執行宏或輸出跟蹤消息,而不是中斷執行。利用 IntelliTrace, 將會收集跟蹤點輸出,並可在與其他 IntelliTrace 事件同樣的界面中查看這些 輸出(請參見圖 4)。
圖 4 跟蹤點可向代碼中動態添加跟蹤輸出
加速調試
默認情況下,IntelliTrace 配置為僅收集 IntelliTrace 事件。此解決方案 開銷很低,但不會提供應用程序的完整執行歷史記錄。如果您需要更深層的信息 ,則可以配置 IntelliTrace 來收集更多數據。像其他調試器設置一樣, IntelliTrace 也可通過“選項”對話框(可從“工具”菜單中訪問)進行配置 (請參見圖 5)。
圖 5 可通過“選項”對話框更改 IntelliTrace 設置
通過選擇“IntelliTrace 事件和調用信息”,可將 IntelliTrace 配置為不 僅收集 IntelliTrace 事件,而且在每個方法進入、退出和調用站點時收集調用 信息,例如位於這些位置的參數和返回值。遺憾的是,啟用此模式會導致“編輯 並繼續”在調試會話過程被禁用。很明顯,您選擇收集更多信息意味著應用程序 的開銷更高。我們致力於找到有用的信息和性能之間的平衡點,但我們將完全控 制權給予您。
每個調試會話都會創建一個存儲在磁盤上的 IntelliTrace 文件,當 Visual Studio 關閉時,將會自動清理該文件。利用 IntelliTrace 的“高級”窗格( 通過“選項”對話框訪問),您可以配置要將在調試過程中創建的文件存儲在何 處,以及這些文件的最大文件大小。如果達到最大文件大小,則會使用一個循環 緩沖區來幫助壓縮和截斷存儲在 IntelliTrace 日志中的信息,從而減小日志文 件在磁盤上占用的空間。利用“高級”窗格上的兩項其他設置,您可以隱藏導航 裝訂線,並禁用可用符號的 Team Foundation Server (TFS) 查找。
由於存在性能問題,因此默認情況下只會為集合啟用定義的 IntelliTrace 事件的一個子集。您可能需要考慮啟用的某些事件包括控制台輸出、文件訪問、 遲緩初始化、注冊表訪問以及線程處理事件。您可以從“選項”對話框的 “IntelliTrace 事件”窗格中啟用或禁用整個事件類別或個別事件。
最後一個 IntelliTrace“選項”窗格允許您控制 IntelliTrace 從哪些模塊 中收集數據。默認情況下,將會收集除 Microsoft 作為 Microsoft .NET Framework 和 Visual Studio 一部分提供的模塊之外的所有其他模塊。采用這 種方式收集到的數據可能非常多,因此您可以考慮將此列表更改為一個包含列表 ,並僅指定您關注的模塊。相反,如果您使用某些常見的第三方庫,則可能希望 排除這些模塊,因為它們超出了您的控制范圍。
調查用戶代碼錯誤
現在,我已驗證了“關於我們”頁在演示應用程序中可正常工作,讓我們確 保購物車同樣也工作正常。當我將一架紙飛機添加到購物車進行購買時,我看到 它確實已正常添加到購物車。但是,當我重復此操作將該物品的第二個實例添加 到購物車時,數量仍然顯示為 1,然而我預計數量會顯示為 2。我希望使用調試 器來解決問題而無需重新啟動,但 IntelliTrace 事件的列表在這種情況下可能 無助於事(所有工作都是在我的代碼而不是 .NET Framework 中完成的)。在這 裡,IntelliTrace 的“IntelliTrace 事件和調用信息”模式可以幫助顯示我的 應用程序的執行歷史記錄。讓我們進入調試器,看看我可以使用 IntelliTrace 的哪些其他功能來解決此問題。
我首先假設我的“將物品添加到購物車”邏輯出現了問題。由於此應用程序 是一個模型-視圖 -控制器 (MVC) 應用程序,因此沒有用於按鈕單擊的事件處理 程序,而是會發送一個 POST 消息並由 MVC 處理。此 POST 消息已記錄為一個 IntelliTrace 事件,並且,盡管該事件本身沒有作用,但我使用它作為起點來 進行調查。通過單擊此 IntelliTrace 事件,我可以跳轉到調試會話中“添加物 品”邏輯的開始位置。通過在 IntelliTrace 窗口中搜索單詞“POST”,我可以 快速找到這些 POST 消息。用戶進行了此操作兩次,因此,不出所料,搜索返回 了兩個結果。選擇第二個事件之後,如果清除搜索字段,則會在包含所有已收集 事件的上下文中顯示該事件(請參見圖 6)。
圖 6 “IntelliTrace 事件”視圖可幫助您開始進行調查
既然我有了有關我處於應用程序時間線的何處的上下文,那麼我希望進一步 深入了解所進行的方法調用。從“事件”視圖中,我可以切換視圖以查看執行歷 史記錄。通過單擊窗口頂部的“切換到 IntelliTrace 調用視圖”鏈接,我可以 轉換到“調用”視圖以查看應用程序的完整執行歷史記錄(請參見圖 7)。我可 以隨時通過單擊“切換到 IntelliTrace 事件視圖”鏈接返回到“事件”視圖。
圖 7 IntelliTrace 的“調用”視圖顯示應用程序的執行歷史記錄
用於浏覽執行歷史記錄的一種機制是使用“調用”視圖深入了解您在調查時 感興趣的調用。每次雙擊“調用”視圖下半部分中的某個調用時,該調用將彈出 到視圖的上半部分,並且指令指針將在代碼編輯器中與調用的方法入口點同步, 就像您進入調用堆棧時的實時調試一樣。您可以繼續導航,並采用這種方式在 IntelliTrace 歷史記錄收集的數據中向後和向前浏覽。通過浏覽“調用”視圖 這種機制,可以快速了解執行歷史記錄的概況,並在代碼庫中進行大幅跳轉。
使用“調用”視圖是唯一的導航方法。我還可以通過單步執行代碼來進行導 航。在源代碼窗口的裝訂線中,有一組新的 DVR 樣式的控件,您可以利用這些 控件來單步執行代碼,就像在傳統的調試會話中一樣(請參見圖 8)。由於我處 於 IntelliTrace 調試模式中,因此單步執行由在每次調用站點、函數進入或函 數退出時發生的事件記錄。當然,如果您喜歡使用鍵盤控制,F10/F11 也可按預 期方式工作。
圖 8 導航欄提供了 DVR 樣式的控件,使您能夠單步執行應用程序
這兩種導航方法非常適合於調查,但有時您確切知道將在何處設置斷點。對 於此應用程序,我知道將物品添加到購物車的函數的名稱: Kona.Model.ShoppingCart::AddItem。我真正要做的是跳轉到第二次調用此函數 的位置,並檢查傳入函數和從函數返回的值。更具體地說,我希望搜尋進行 “AdjustQuantity”函數調用的代碼行。當然,IntelliTrace 也支持這種導航 技術。
在編輯器中,我可以右鍵單擊要搜尋的行,並從上下文菜單中選擇“在 IntelliTrace 中搜索此行”(請參見圖 9)。搜索將開始,並且結果將呈現在 編輯器窗口頂部的搜索欄中,從而使我能夠在搜索結果之間導航。與第二個搜索 結果同步後,我的指令指針恰好位於我希望的位置,並且我可以使用其他調試器 窗口來調查調用(請參見圖 10)。
圖 9 搜索功能使我能夠恰好跳轉到特定的函數調用
圖 10 搜索結果顯示在編輯器窗口頂部的搜索結果欄中
此時查看“局部變量”窗口,您可以看到購物車正在被調整,以使購物車中 產品的新數量為 1。這是 Bug;我預期的調整後的數量為 2。查看代碼行,新的 Quantity 參數不僅應考慮“item.Quantity”,而且應考慮傳入函數調用的 “quantity”變量。解決方法是將函數調用更改為:
AdjustQuantity(product, item.Quantity + quantity);
消除令人生畏的“無法重現”情況
測試人員和開發人員經常會遇到“無法重現”情形,在這種情形中,測試人 員會提交指出發生了某種錯誤的 Bug,最終又歸結於一句注解“它沒有在我的計 算機上重現”。開發人員和測試人員都不想遇到這種情形,但他們沒有適當的工 具集來幫助他們傳達出現故障時所發生的情況。我們之所以將系統設計為 IntelliTrace 在調試會話期間向開發人員提供的診斷信息與通過 Microsoft Test Manager (MTM) 執行手動測試時可收集的診斷信息相同,這就是原因所在 。
讓我們重新探討我調試的第一個情況,即“關於我們”頁未能正常呈現的情 況。如果測試人員提交了此 Bug,它的標題將可能是諸如“查看關於頁時發生應 用程序錯誤”之類的標題,或許,如果開發人員幸運的話,還會附上錯誤的屏幕 截圖。如果您當前收到像這樣的 Bug,可能會如何對其進行調試?您很有可能會 從源代碼管理中加載應用程序的解決方案,並在您的開發人員計算機上重現問題 。在本例中,您將能夠調試和解決問題,但想一想這種方法花費了多少時間。或 者,如果問題是由您的計算機和測試人員的計算機之間的配置不同導致的,又該 怎麼辦?就工作效率而言,開發人員在調試問題時的工作效率要比建立重現環境 時高得多。
利用 Visual Studio 2010,MTM 使測試人員能夠方便地創作、管理和執行手 動測試和自動測試。但該工具的功能並非僅限於此。MTM 使測試人員能夠讓診斷 數據適配器在測試執行的同時在後台收集信息。例如,您可以自動收集所執行測 試的錄像,以便開發人員能夠看到與測試人員完全相同的情形。其他收集器能夠 從所測試的框架中收集系統信息和事件日志。
我們增加了 IntelliTrace 診斷數據適配器,以便自動在後台收集 IntelliTrace 文件。當測試人員的某個測試步驟失敗並選擇提交 Bug 時,收集 的 IntelliTrace 文件將自動上載到 TFS 並鏈接到該 Bug。這樣將可為開發人 員提供一組更為豐富的用於調試的信息,而不僅僅是一組重現步驟。當開發人員 打開鏈接到 Bug 的 IntelliTrace 文件時,他將體驗到與調試小型轉儲類似的 感覺,但卻是有關整個應用程序時間線的轉儲,而不僅僅是應用程序崩潰時的轉 儲。可以為本地或遠程測試代理上的任何托管應用程序(包括運行於 IIS 下的 ASP.NET 應用程序)收集 IntelliTrace 數據。
當我打開 TFS Bug 時,MTM 已根據測試人員描述的手動測試步驟自動添加了 重現步驟。更酷的是,如果我查看 Bug 的“所有鏈接”選項卡(請參見圖 11) ,我可以看到同時收集的 IntelliTrace 文件。通過雙擊此文件,我將看到 IntelliTrace 文件的摘要頁視圖(請參見圖 12)。
圖 11 可通過“所有鏈接”選項卡訪問收集的 IntelliTrace 文件
圖 12 IntelliTrace 摘要提供了所收集數據的概覽
摘要頁為我提供了許多有關 IntelliTrace 文件所包含內容的信息。在該頁 的頂部,我可以看到應用程序中各個線程的時間線視圖。如果我展開摘要頁的“ 線程列表”部分,我可以看到以表格格式顯示的相同數據。如果雙擊某個線程, 將會從 IntelliTrace 文件中啟動調試會話,並將我的指令指針置於該線程的開 頭。
我還將在 IntelliTrace 文件中看到收集的所有異常的列表。如果我選擇某 個異常,線程時間線上將出現一條橙色豎線,顯示異常發生的時間點。如果雙擊 某個異常,將會啟動 IntelliTrace 調試會話,並且將在“IntelliTrace 事件 ”視圖中選擇異常事件。在摘要頁上進一步向下看,我可以看到與手動重現步驟 匹配的所有測試事件的列表。還會顯示每個步驟的結果。單擊其中某個事件也會 向線程時間線上放置一條豎線,而雙擊事件將會啟動 IntelliTrace 調試會話, 同時選擇盡可能最近的事件。摘要頁的底部顯示收集的有關計算機(測試的應用 程序在該計算機上運行)的系統信息的列表,該列表下面列出了加載到進程中的 所有模塊及其文件路徑。
您可能會問自己,它是否適合於發布版?當然可以!此外,在收集或查看 IntelliTrace 數據時,您無需訪問符號。符號只有在將收集的數據鏈接到源文 件時才是必要的,以便它們在您打開調試會話時有所幫助。
Visual Studio 2010 和 TFS 還增加了對符號和源服務器的支持,因此,如 果您的應用程序是作為 TFS 生成系統的一部分生成的,則會按照 IntelliTrace 的需要自動從 TFS 下載正確版本的符號和源代碼。您甚至不必知道符號服務器 路徑。
從摘要頁中啟動調試會話後,調試器的工作方式就好像實時調試會話一樣( 請參見圖 13)。您可以使用大多數調試器窗口,並可以導航到 IntelliTrace 收集的任何數據。
圖 13 當調試從摘要頁中啟動時,Visual Studio 的運行方式就像實時調試 會話一樣
總結
在本文中,您了解了 IntelliTrace 如何能夠大幅改善您的日常開發活動, 並提升您快速輕松診斷問題的能力,而不必重新啟動應用程序和使用傳統的“中 斷-單步執行-檢查”技術進行調試。我還向您介紹了組織如何能夠通過在測試過 程中收集 IntelliTrace 數據來減少“無法重現”的 Bug 數,從而使開發人員 能夠脫機調試問題,而無需訪問實時重現。這只是功能的簡要介紹,當您越來越 熟悉 IntelliTrace 的強大功能時,它將開始改變您的調試方式。
代碼示例下載地址: http://code.msdn.microsoft.com/mag201004Debug/Release/ProjectReleases. aspx?ReleaseId=4109