Visual Studio高級調試技巧

来源:http://www.cnblogs.com/kekec/archive/2016/10/29/5635854.html
-Advertisement-
Play Games

1. 設置軟體斷點,運行到目標位置啟動調試器 方法①:使用彙編指令(註:x64 c++不支持彙編) 方法②:編譯器提供的方法 方法③:使用windows API WerFault.exe進程(Windows Error Reporting)彈出ConsoleTest.exe已停止工作: 要想出現“調 ...


1. 設置軟體斷點,運行到目標位置啟動調試器

方法①:使用彙編指令(:x64 c++不支持彙編)

_asm int 3

方法②:編譯器提供的方法

__debugbreak();

方法③:使用windows API

DebugBreak();

WerFault.exe進程(Windows Error Reporting)彈出ConsoleTest.exe已停止工作:

要想出現“調試程式”選項,需要將Windows Error Reporting註冊表信息設置成如下圖所示(:特別是紅框的內容)

如果在註冊表AeDebug的Debugger項配置了VSJitDebugger路徑,且VSJitDebugger運行正常

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AeDebug

HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug  // 註:64位系統上的32位程式使用該註冊表項

點擊“調試程式”選項就會彈出Visual Studio實時調試器對話框,選擇對應的調試器後,點擊“是”就可以啟動調試器並中斷到軟體斷點位置了

需要註意的是,軟體斷點也是一種異常,一旦被處理,就不會傳到WerFault.exe進程上,那麼這種方法也就失效了!

下麵兩種情況軟體斷點異常會被處理:

① 被SEH異常捕獲並處理

② 被自定義的全局異常函數處理

:可以將上面兩種情況中的EXCEPTION_EXECUTE_HANDLER修改為EXCEPTION_CONTINUE_SEARCH來指明異常未得到處理

2. 修改變數:在懸停出來小面板、Locals視窗、Autos視窗、Watch視窗、或Quick Watch視窗中進行修改;也可以在Immediate視窗中執行:bFlag=false)

3. 格式化變數

:d,i:有符號的十進位數
      u:無符號的十進位數
      o:無符號的八進位數
      x:十六進位數(字母小寫)
      X:十六進位數(字母大寫)

4. 修改記憶體:在記憶體視窗中,將游標定位到要修改的地方,直接按0-9輸入十六進位;要輸入a-f則需通過右鍵菜單中的“Edit Value”進行輸入)

5. 格式化顯示記憶體

 

6. 設置下一個運行位置:直接拖動黃色箭頭到想要的運行位置)

示例中:傳入的bFlag為true,代碼開始運行到斷點處(43行),然後重新把黃色箭頭拖回39行,此時bFlag的值為false,按F10會進入else分支

:(1)跳過中間所有指令。意味著:printf("True\n")及CTest的析構函數均不會被執行

      (2)當拖動箭頭到一個新的函數中時,vs會將原來的函數從棧中彈出,將新函數壓入棧頂;

             由於新函數與上層函數沒有調用關係,輸出類型的參數及返回值很有可能寫壞上層函數的棧數據

      (3)該調試技巧為一種事後行為,應謹慎使用,最好是只在函數內局部使用

7. 編輯然後繼續運行

(1)不能在64位代碼上使用

(2)使用“Program Database for Edit & Continue (/ZI)”生成pdb文件

(3)僅適用於函數內部改變(若要修改函數原型或增加新函數,只能選擇重啟程式)

8. 變數的一些特殊查看方法

以$和@開頭的偽變數:(:$和@兩個符號是一樣的,隨便用哪個都可以)

$err -- 獲取GetLastError()的返回值

$err,hr -- 獲取GetLastError()的返回值並解釋返回值的含義

@eax -- 查看eax的值(64位為@rax)

@esp+4 -- 函數的第一個參數地址

$handles -- 查看打開的句柄數

$tid  -- 當前線程id

$vframe  -- 當前棧幀的ebp

$clk  -- 以時鐘周期為單位顯示時間

$ReturnValue -- 查看函數的返回值

Message,wm --以windows消息的巨集形式顯示 如:Message為15時,顯示為WM_PAINT(:Message為unsigned int類型)

hResult,hr --hResult為0x80070005時,顯示為E_ACCESSDENIED(:hResult為void*類型)

pArray,10 --從pArray地址起顯示後續10個int類型的數據(:pArray為int*類型)

(pArray+5),3 --從pArray[5]地址起顯示後續3個int類型的數據(:pArray為int*類型)

9. 獲取簡單類型的函數返回值

註1:不能為inline函數

註2:執行函數的下一條語句時,查看eax或偽變數ReturnValue的值

10. 使用指針類型轉換查看某個地址的變數

:有時候,儘管對象仍然存在,在調試符號越界後,watch視窗中的變數是被禁用的,不能再查看(也不能更新)。

      若知道對象的地址,則可以將地址轉換為該對象類型的指針,放在watch窗中來繼續觀察它。

11. Command視窗

通過命令來完成vs中的功能(不僅僅在調試狀態時使用),另外其調試相關命令與windbg保持一致。

? nLocal  //查看變數nLocal的值
?? nLocal //將nLocal添加到Quick Watch視窗中
? nLocal=100 //修改nLocal的值為100
? MySum(20,30) //調用全局函數MySum,並返回結果
k //列印當前線程堆棧
~ //查看線程情況
~*k  //列印出所有線程的堆棧信息
watch //打開watch視窗
memory2 //打開memory2視窗
g //繼續執行,F5功能
q //結束調試

12. 記憶體斷點

(1)在84行斷點停住後,查看&s.Age的地址為0x0042FCEC

(2)點擊"Debug"-"New Breakpoint"-"New Data Breakpoint...",在彈出的對話框Address填入:0x0042FCEC,長度為4即可

(3)當運行到88行時,由於Scores數組越界引發了s.Age的記憶體修改,觸發了記憶體斷點

13. 條件斷點

斷點說明:

(1)設置斷點條件:i>6;且被命中次數>=2時才斷住程式,所以第一次斷住時i=8

(2)命中時,在Output視窗中列印當前函數名及線程ID(也可以列印相關變數的值,詳見"When Breakpoint Is Hit"面板上的說明);在Command視窗中列印出堆棧信息

(3)若不想斷住程式,可以把"When Breakpoint Is Hit"對話框中的"Continue execution"勾選上

註1:對於字元串的條件斷點,不能寫如下條件pStr=="Hello"(pStr為char*類型),應該寫成:pStr[0]=='H' && pStr[1]=='e' && pStr[2]=='l' && pStr[3]=='l' && pStr[4]=='o' && pStr[5]=='\0'

     vs2010及以上版本中,條件斷點中可使用字元串:strcmp(pStr, "Hello")==0

     支持的字元串函數有:strlen, wcslen, strnlen, wcsnlen, strcmp, wcscmp, _stricmp, _wcsicmp, strncmp, wcsncmp, _strnicmp, _wcsnicmp, strchr, wcschr, strstr, wcsstr.

註2:也可以創建自己的巨集,具體方法:"Tools"-"Macros"-"Macro Explorer",然後在下圖:MyMacros-Module1上右鍵快捷菜單中選擇"New macro",

     如ChangeExpression巨集函數會在Output視窗的Debugger過濾器下列印出"Hello World",然後修改變數code的值為1000

     編寫自己的巨集時,可以參考大量vs已有的巨集(見:Samples節點下)

14.在windows API上打斷點

(1)例如:對SetWindowText打斷點。首先當前程式字元集為未設置或多位元組,則SetWindowTextA;為Unicode則為SetWindowTextW。下麵以SetWindowTextA為例。

(2)調試運行程式斷住後,打開Modules視窗可以看到所有已經載入的模塊,找到windows API所在的模塊,右擊滑鼠執行"Load Symbols From" - "Microsoft Symbol Servers"下載並載入對應模塊的pdb

(3)新建一個Break At Function斷點,填入:{,,user32.dll}_SetWindowTextA@8。可以看到,VS裡面的符號跟windbg相比多了一些字元,其中‘_’表示stdcall類型,後面‘@8’表示所有參數的位元組數的和。

       有些函數Symbol Name與導出函數名可能不一致,例如GetDC(HWND),其Symbol Name為NtUserGetDC,最後斷點應填入:{,,user32.dll}_NtUserGetDC@4

       :查找windows API符號名可以使用windbg的x命令或者使用pdb解析工具(symView

也可以直接使用地址對windows API打斷點(這種方式不需要符號的支持):如對GetDC打斷點,可以用Dependency查看其在user32.dll中導出函數地址(Entry Point列):0x000172CC

然後在Modules視窗中獲得user32.dll模塊起始地址0x75840000,最後對兩個值相加後的絕對地址處直接設置斷點:{,,user32.dll}0x758572CC

15. 異常時斷住程式

(1)在"Debug"-"Exceptions...",彈出如下對話框:點擊Add按鈕,新增一個int類型的C++ Exceptions異常,並勾選Thrown

(2)當int、int*、int&的異常被catch到時,會斷住程式進入調試狀態(:以上void類型對應:char*、void*的異常)

16. 單步調試自動跳過不必進入的函數  (:僅適用於Native c++)

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\9.0\NativeDE\StepOver]
"1"="\\scope:CString.*\\:\\:.*=NoStepInto"

註1:如果是32位windows,刪除上面路徑中的Wow6432Node

註2:不進入任何CString的方法(前面的1表示優先順序,該值越大優先順序越高)

++++++++++++++++++++++++++++++

NoStepInto 不可進入匹配函數
StepInto   可進入匹配函數

特殊字元串:

\cid 代表一個C/C++標識符
\funct 代表一個C/C++函數名
\scope 代表一個函數的作用範圍(命名空間+類名 如:ATL::CFoo::CBar::)
\anything 代表一個字元串
\oper 代表一個C/C++操作符

正則表達式:

\ 轉義字元 如:要使用 “\” 本身, 則應該使用“\\”
\: 代表字元:
. 匹配任意字元
* 其左邊的字元被匹配任意次(0次或多次)。如:be*匹配“b”,“be”或“bee”
.* 匹配0個或多個字元

更多例子:

例1:不進入重載操作符函數:
10 \scope:operator\oper:=NoStepInto
例2:除了CComBSTRs的非操作符函數,不進入任何ATL::開頭的函數:
20 ATL\:\:CComBSTR::\funct:=StepInto
10 ATL\:\:.*=NoStepInto
例3:除了全局模版函數外,不進入任何模板函數:
20 \scope:\funct:=StepInto
10 .*[\<\>].NoStepInto

++++++++++++++++++++++++++++++

17. 使用OutputDebugString進行日誌調試

(1)調試狀態時,會將日誌輸出到Debug過濾器的Output視窗中

(2)非調試狀態時,可採用DbgView.exe來捕捉程式日誌

18. 使用autoexp.dat自定義調試時變數的顯示格式

文件所在位置:Microsoft Visual Studio 9.0\Common7\Packages\Debugger\autoexp.dat

在autoexp.dat中的[Visualizer]域可以對各種類型變數的顯示格式進行配置,來優化變數在調試時顯示,提高效率。

:在vs中要讓autoexp.dat生效需要去掉"Tools"-"Options..."對話框中,

     "Debugging"-"General"-"Show raw structure of objects in variables windows"的勾選

(1) STL之string、vector、map

①原始顯示結果:

②配置了autoexp.dat的顯示結果:

-->對應的配置內容如下:

;  std::string -- char
std::basic_string<char,*>{
    preview        ( #if(($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,s]) #else ( [$e._Bx._Ptr,s]))
    stringview    ( #if(($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,sb]) #else ( [$e._Bx._Ptr,sb]))
    children
    (
        #if(($e._Myres) < ($e._BUF_SIZE))
        (
            #([actual members]: [$e,!] , #array( expr: $e._Bx._Buf[$i], size: $e._Mysize))
        )
        #else
        (
            #([actual members]: [$e,!],  #array( expr: $e._Bx._Ptr[$i], size: $e._Mysize))
        )
    )
}
;------------------------------------------------------------------------------
;  std::vector
;------------------------------------------------------------------------------
std::vector<*>{
    children
    (
        #array
        (
            expr :        ($e._Myfirst)[$i],
            size :        $e._Mylast-$e._Myfirst
        )
    )
    preview
    (
        #(
            "[", $e._Mylast - $e._Myfirst , "](",
            #array
            (
                expr :    ($e._Myfirst)[$i],
                size :    $e._Mylast-$e._Myfirst
            ),
            ")"
        )
    )
}
;------------------------------------------------------------------------------
;  std::map
;------------------------------------------------------------------------------
std::map<*>{
    children
    (
        #tree
        (
            head : $e._Myhead->_Parent,
            skip : $e._Myhead,
            size : $e._Mysize,
            left : _Left,
            right : _Right
        ) : $e._Myval
    )
    preview
    (
        #(
            "[", $e._Mysize, "](",
            #tree
            (
                head : $e._Myhead->_Parent,
                skip : $e._Myhead,
                size : $e._Mysize,
                left : _Left,
                right : _Right
            ) : $e._Myval,
            ")"
        )
    )
}

 

(2) 自定義類MyArray

①原始顯示結果:

②配置了autoexp.dat的顯示結果:

-->對應的配置內容如下:

MyArray{
    preview
    (
        #(
            "[size is ", $c.m_nSize, "] m_pData is (",
            #array
            (
                expr: ($c.m_pData)[$i],
                size: $c.m_nSize
            ),
            ")..."
        )
    )
    stringview
    (
        #(
            "Hello MyArray!!!"
        )
    )
    children
    (
        #(  
            #array
            (
                expr: ($c.m_pData)[$i],
                size: $c.m_nSize
            )
        )
    )
}

註1:雙引號中字元串不能含有冒號,如:"[size is "不能寫成"size: "

註2:多個類型使用 | 進行連接。如:MyArray|ArrayEx

註3:preview、stringview及children。對於不需要的部分可以不用定義,且三個部分沒有先後順序之分。

註4:格式的定義的最外層用大括弧{},其中的每個部分使用小括弧()。

註5:格式定義出錯時,運行VS會彈出提示視窗,對於格式配置錯誤的類型,在調試期間無法正常顯示。

註6:最外層的左邊的大括弧{必須緊挨著最後一個類型名,否則無論後面的格式正確與否,都無法正常顯示。

註7:符號;為行註釋符。

註8:$c表示當前所定義數據結構的對象,#array表示用數組形式顯示內容,$i表示數組中的每個元素的索引,$e表示數組中的每個元素的值

註9:array結構必須同時包含expr和size兩個部分,缺少其中一個部分都將導致信息無法正確顯示。

註10:可使用#switch、#if進行條件分支判斷,要註意的是:#switch結構不能用於#array結構中,否則可能導致VS掛死。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ...
  • 學習Java的同學註意了!!! 學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入Java學習交流群,群號碼:279558494 我們一起學Java! 一、基礎篇 1.1 JVM 1.1.1. Java記憶體模型,Java記憶體管理,Java堆和棧,垃圾回收 http://www.jcp.org/e ...
  • 1.參數註入1.1用#{0},#{1}的形式,0代表第一個參數,1代表第二個參數 public List<RecordVo> queryList(String workerId, Integer topNum); <select id="queryList" resultType="com.demo ...
  • Python 2.7.12 下載地址:https://www.python.org/downloads/release/python-2712/ 安裝路徑D:\Program Files\Python27 python環境安裝比較簡單,下載python語言環境中的windows版本的msi格式文件, ...
  • 英文文檔: hash(object)Return the hash value of the object (if it has one). Hash values are integers. They are used to quickly compare dictionary keys duri ...
  • 英文文檔: 2. 函數實際上是調用getattr(object,name)函數,通過是否拋出AttributeError來判斷是否含有屬性。 ...
  • 英文文檔: ...
  • 本文通過從無到有創建一個利用Go語言實現的非常簡單的HttpServer,來讓大家熟悉利用Go語言時的基本流程,工具和代碼的基本佈局,為學習Go語言時碰到的環境問題掃清障礙。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...