Advanced .Net Debugging 3:基本調試任務(上)

来源:https://www.cnblogs.com/PatrickLiu/p/18036453
-Advertisement-
Play Games

概述:該通用單例泛型基類使用C#實現,線程安全,通過泛型參數和Lazy<T>實現簡化的單例模式。優點包括線程安全、泛型通用性、簡化實現、以及延遲載入的特性。 優點: 線程安全: 使用Lazy<T>確保了線程安全的延遲初始化,避免了在多線程環境下可能導致的競態條件問題。 泛型通用性: 通過泛型參數,該 ...


一、簡介
    這是我的《Advanced .Net Debugging》這個系列的第三篇文章。這個系列的每篇文章寫的周期都要很長,因為每篇文章都是原書的一章內容(太長的就會分開寫)。再者說,原書寫的有點早,有些內容還是需要修正的,調試每個案例,這都是需要時間的。今天這篇文章的標題雖然叫做“基本調試任務”,但是這章的內容還是挺多的。我本來想用一篇文章把這個章節寫完,我發現是不可能的,於是就分“上“和”下”用兩篇來寫。既然,我們要調試我們的 .Net 應用程式,那必須掌握一些調試技巧、方法和工具。我們習慣了使用 Visual Studio IDE 的調試技巧,比如:單步調試、下斷點、過程調試等,但是,有些時候,VS 是使用不了的。那我們也必須學習如何使用 Windbg 的命令,在沒有 VS IDE 的情況下,如何調試我們的程式,如何設置斷點、恢復執行、中斷執行、退出調試回話,如何為 JIT 編譯的方法設置斷點,如何為沒有被 JIT 編譯的方法設置斷點,為泛型方法設置斷點等等。如果我們想成為一名合格程式員,這些調試技巧都是必須要掌握的。
    如果在沒有說明的情況下,所有代碼的測試環境都是 Net 8.0,如果有變動,我會在項目章節里進行說明。好了,廢話不多說,開始我們今天的調試工作。

       調試環境我需要進行說明,以防大家不清楚,具體情況我已經羅列出來。
          操作系統:Windows Professional 10
          調試工具:Windbg Preview(Debugger Client:1.2306.1401.0,Debugger engine:10.0.25877.1004)和 NTSD(10.0.22621.2428 AMD64)
          下載地址:可以去Microsoft Store 去下載
          開發工具:Microsoft Visual Studio Community 2022 (64 位) - Current版本 17.8.3
          Net 版本:.Net 8.0
          CoreCLR源碼:源碼下載

      說明一下,這個系列內容安排有些變動,我把基礎知識和眼見為實放在了一起,講什麼內容,立刻就將講的內容做一個眼見為實驗證,這樣做更便於大家理解,我認為這樣會更好一些,不用在文章里來回跑了。
      命令行調試器要想成功使用,必須先安裝 MSVC,想要瞭解詳情,可以去微軟的官網:https://learn.microsoft.com/zh-cn/cpp/build/building-on-the-command-line?view=msvc-170,如果我們使用的 Visual Studio 2022,本身也有命令行工具,我們就可以直接使用。安裝如圖:
      

二、調試源碼
    廢話不多說,本節是調試的源碼部分,沒有代碼,當然就談不上測試了,調試必須有載體。
    2.1、ExampleCore_3_1_1
 1 namespace ExampleCore_3_1_1
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Console.WriteLine("Welcome to Advanced .Net Debugging!");
 8             Console.Read();
 9         }
10     }
11 }

    2.2、ExampleCore_3_1_2
 1 using System.Diagnostics;
 2 
 3 namespace ExampleCore_3_1_2
 4 {
 5     internal class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             Console.WriteLine("第一次執行,並開始中斷執行!");
10             Debugger.Break();
11             Console.WriteLine("第二次執行,並開始中斷執行!");
12             Debugger.Break();
13             Console.WriteLine("第三次執行,並開始中斷執行!");
14             Debugger.Break();
15 
16             Console.WriteLine("恢復執行調試完畢!");
17             Console.ReadLine();
18         }
19     }
20 }

    2.3、ExampleCore_3_1_3
 1 using System.Diagnostics;
 2 
 3 namespace ExampleCore_3_1_3
 4 {
 5     internal class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             Sum1(10);
10             Debugger.Break();
11 
12             int i = 10;
13             int j = 20;
14 
15             var sum = Sum1(i);
16             Console.WriteLine($"sum={sum},i={i},j={j}");
17 
18             Console.ReadLine();
19         }
20 
21         private static int Sum1(int a)
22         {
23             var i = a;
24             var j = 11;
25             int sum = Sum2(i, j);
26 
27             return sum;
28         }
29 
30         private static int Sum2(int a, int b)
31         {
32             var i = a;
33             var j = b;
34             var k = 13;
35 
36             var sum = Sum3(i, j, k);
37             return sum;
38         }
39 
40         private static int Sum3(int i, int j, int k)
41         {
42             return i + j + k;
43         }
44     }
45 }

    2.4、ExampleCore_3_1_4
 1 namespace ExampleCore_3_1_4
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             //第一次調用函數
 8             Console.WriteLine("Press any key(1st instance function)");
 9             Console.ReadKey();
10             BreakPoint bp = new BreakPoint();
11             bp.AddAndPrint(10, 5);
12 
13             //第二次調用函數
14             Console.WriteLine("Press any key(2nd instance function)");
15             Console.ReadKey();
16             bp = new BreakPoint();
17             bp.AddAndPrint(100, 50);
18         }
19     }
20 
21     internal class BreakPoint
22     {
23         public void AddAndPrint(int a, int b)
24         {
25             int res = a + b;
26             Console.WriteLine("Adding {0}+{1}={2}", a, b, res);
27         }
28     }
29 }

    2.5、ExampleCore_3_1_5
 1 using System.Diagnostics;
 2 
 3 namespace ExampleCore_3_1_5
 4 {
 5     internal class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             Debugger.Break();
10 
11             var mylist = new MyList<int>();
12 
13             mylist.Add(10);
14 
15             Console.ReadLine();
16         }
17     }
18 
19     public class MyList<T>
20     {
21         public T[] arr = new T[10];
22 
23         public void Add(T t)
24         {
25             arr[0] = t;
26         }
27     }
28 }

三、基礎知識和眼見為實
    3.1、調試器以及調試目標
        A、知識介紹
            在任何調試中都包含兩個組件:調試器和調試目標。
            調試器:是一個引擎,我們必須通過這個引擎和調試目標進行交互。所有與調試目標之間的交互操作(如:設置斷點、觀察狀態等),都可以通過調試器的命令完成,而調試器將在調試目標的環境中執行這些命令。
            調試目標:一般指我們編寫的程式,對於 .Net程式員來說就是,或者是要調試的程式。
            它們之間的關係,有一張圖可以更好的表現他們之間的關係。如圖:
            
        B、眼見為實
            在【眼見為實】這個章節里,有些調試動作是一樣的,我就不每個節點都寫了。我就寫在這裡了。首先編譯好自己的項目,根據自己的喜好,可以切換到編譯項目目錄下,也可以直接輸入項目所在目錄,接著就可以進行項目調試了。我使用的命令行工具是【Developer Command Prompt for VS 2022】

            1)、使用NTSD調試器
                調試源碼:ExampleCore_3_1_1
                我們命令行工具中輸入命令:ntsd,打開新視窗。效果如圖:
                
                NTSD 的新視窗。

                

                如果沒有指定任何參數,只能顯示一組可用的選項。我們將我們的項目完整路徑和項目名稱作為輸入參數。執行【ntsd E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_1\bin\Debug\net8.0\ExampleCore_3_1_1.exe】,彈出新視窗,如圖:
                

                新的 NTSD 視窗,如圖:
                

                上面這個截圖分三個部分:第一部分是符號文件搜索路徑,如圖:
                

                第二部分:載入的所有模塊,表示應用程式所需要的模塊都已經載入完畢。如圖:
                

                第三部分:中斷指令異常。每當調試器啟動一個進程或者調試器附加到一個進程的時候,調試器都會註入一個中斷指令,這條指令將使調試目標停止運行。斷點指令的作用:使用戶與調試器和調試目標進行交互。這裡是 int 3 中斷。如圖:
                

                調試器的命令提示符是:X:Y>,X 表示當前正在被調試的活動目標(在大多數調試器中,這個值為 0),Y 表示導致調試器中斷的線程 ID。如圖:
                


            2)、使用 NTSD 附加進程
                調試源碼:ExampleCore_3_1_1
                當在調試器下啟動有問題的引用程式的時候,這種調試方式很有作用。如果是引用程式已經運行起來了,那我們該如何調試呢?我們可以通過給【ntsd】命令,加上 -p 命令,就可以附加進程了。比如:你寫的一個 Web 服務已經成功運行起來了。隨著時間的推移,這個 Web 服務開始表現出一些怪怪的行為,你希望當程式具有這總奇怪行為的時候對它進行調試,附加調試就可以大展拳腳了。
                -p 參數告訴調試器希望調試一個正在運行的進程。對於這個參數後,再跟上要調試進程的 Id 就可以了。
                執行命令【ntsd -p 14624】,如圖:
                

                打開新的調試器視窗,如圖:
                

                下麵很有很多內容,就不顯示了。


            3)、使用 TList.exe 顯示進程 Id。
                調試源碼:ExampleCore_3_1_1
                如果我們想獲取一個進程的 id,可以有很多方法,我們可以使用 Windows 調試工具集中的 tlist.exe,tlist.exe 會輸出所有運行的進程名稱和 ID。啟動我們的 ExampleCore_3_1_1.exe,在控制台輸出:Welcome to Advanced .Net Debugging!,我們打開命令工具【Developer Command Prompt for vs 2022】,輸出命令 tlist,顯示如下:
                

                我們進程的信息如下:
                


            4)、使用 Windbg 調試
                調試源碼:ExampleCore_3_1_1
                編譯好我們的項目,打開【Windbg Preview】調試器。依次點擊【文件】--->【Launch executable】載入我們的項目文件:ExampleCore_3_1_1.exe,選擇【打開】按鈕,成功載入併進入調試器界面。
                
                點擊【文件】按鈕,切換界面。

                

                點擊【Launch executable】按鈕打開新視窗。如圖:

                

                進入調試器界面,如圖:
                

                我們就可以在下方的命令框中輸入命令,調試程式了。如圖:
                
                以上是在調試器中啟動有問題的引用程式的流程,如果想使用【Windbg Preview】附加進程該怎麼辦呢?其實,也很簡單,編譯項目,打開調試器,依次點擊【文件】--->【Attach to process】,如圖:
                
                打開選擇進程的視窗,在右側。
                

                之後就是進入調試器界面,使用方法就一樣了。


    3.2、符號
        A、知識介紹
            符號文件:是一種輔助數據,它包含了對引用程式代碼的一些標註信息,這些信息在調試過程中非常有用。如果沒有這些輔助數據,那獲得的信息只有引用程式的二進位文件了。對二進位代碼進行調試是非常困難的,因為你無法看到代碼中的函數名、數據結構名等。符號文件的擴展名通常是 .pdb。
            在符號文件中包含很多重要的信息,例如:行號和局部變數的名稱等,它能極大的提高調試的效率。
            符號文件有兩種類型:私有符號文件(Private)和公有符號文件(Public)。
            私有符號文件:是大多數開發人員在日常工作中使用的符號文件,其中包含了調試會話中所需要的所有符號信息。私有符號文件一般是和我們編譯的程式存放在一起的,調試器在開始調試的時候,會自動載入他們。如圖:
            
            

            公有符號文件:這類型的符號文件只是有選擇的包含了一些符號信息,這會使調試工作困難一點。比如:在 Microsoft 符號伺服器上存儲了一些公有符號文件。每當將調試器指向 Microsoft 符號伺服器時,都可以下載這些符號文件,併在調試會話中使用它們。
            之所以有【私有符號文件】和【公有符號文件】之分,主要是為了保護知識產權。私有符號中包含大量底層技術信息,就很容易對應用程式進行逆向工程。公有符號就不存在這樣的問題,既可以調試,又不會泄露核心技術信息。

            
        B、眼見為實
            .sympath(+) 命令的使用,加號表示不會替換,而是追加。
                調試源碼:ExampleCore_3_1_1
                編譯好我們的項目,打開【Windbg Preview】工具,依次打開【文件】--->【Launch executable】,載入我們的項目文件:ExampleCore_3_1_1.exe,進入調試器。直接執行【.sympath】命令,就可以看到當前的符號文件信息。
1 0:000> .sympath 
2 Symbol search path is: srv*
3 Expanded Symbol search path is: cache*;SRV*https://msdl.microsoft.com/download/symbols
4 
5 ************* Path validation summary **************
6 Response                         Time (ms)     Location
7 Deferred                                       srv*

                【.sympath】命令可以設置符號文件路徑。在命令後跟上具體的符號文件所在的地址。

1 0:007> .sympath E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_1\bin\Debug\net8.0\
2 Symbol search path is: E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_1\bin\Debug\net8.0\
3 Expanded Symbol search path is: e:\visual studio 2022\source\projects\advanceddebug.netframework.test\examplecore_3_1_1\bin\debug\net8.0\
4 
5 ************* Path validation summary **************
6 Response                         Time (ms)     Location
7 OK                                             E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_1\bin\Debug\net8.0\

                雖然,我們設置新的符號文件的路徑,但是並不會從這個路徑中載入任何符號。如果想要載入符號信息,必須執行【.reload】命令。我們還是設置回去吧,防止以後有錯誤。

1 0:007> .sympath srv*
2 Symbol search path is: srv*
3 Expanded Symbol search path is: cache*;SRV*https://msdl.microsoft.com/download/symbols
4 
5 ************* Path validation summary **************
6 Response                         Time (ms)     Location
7 Deferred                                       srv*

                如果我們使用【.sympath】命令設置錯了符號文件地址,使用【.reload】也無法載入符號文件,我們可以使用【.symfix】命令,修複問題就可以了。

 1 0:007> .symfix
 2 DBGHELP: Symbol Search Path: cache*;SRV*https://msdl.microsoft.com/download/symbols
 3 SYMSRV:  BYINDEX: 0x17
 4          C:\ProgramData\Dbg\sym
 5          ntdll.pdb
 6          63E12347526A46144B98F8CF61CDED791
 7 SYMSRV:  PATH: C:\ProgramData\Dbg\sym\ntdll.pdb\63E12347526A46144B98F8CF61CDED791\ntdll.pdb
 8 SYMSRV:  RESULT: 0x00000000
 9 DBGHELP: ntdll - public symbols  
10         C:\ProgramData\Dbg\sym\ntdll.pdb\63E12347526A46144B98F8CF61CDED791\ntdll.pdb
11 SYMSRV:  BYINDEX: 0x18
12          C:\ProgramData\Dbg\sym
13          kernel32.pdb
14          85A257DB4B7B82F2E19AD96AB7BB116A1
15 SYMSRV:  PATH: C:\ProgramData\Dbg\sym\kernel32.pdb\85A257DB4B7B82F2E19AD96AB7BB116A1\kernel32.pdb
16 SYMSRV:  RESULT: 0x00000000
17 DBGHELP: KERNEL32 - public symbols  
18         C:\ProgramData\Dbg\sym\kernel32.pdb\85A257DB4B7B82F2E19AD96AB7BB116A1\kernel32.pdb

                這裡有這麼多輸出,是因為我執行了【!sym noisy】命令,開啟了顯示符號載入的詳細信息,如果不想顯示,可以使用【!sym quiet 】命令。

1 0:007> !sym quiet 
2 quiet mode - symbol prompts on
3 0:007> .symfix
                【.symfix】命令後面可以跟一個參數,就是本地路徑,用來緩存下載的符號文件,就不用調試器每次去下載相同的符號了,可以直接從本地載入。在預設情況下,如果沒有指定本地路徑緩存,那麼調試器將使用調試軟體包安裝的路徑下的 sym 文件夾。

    3.3、控制調試目標的執行
        在任何調試會話中,能夠控制調試目標的執行是非常重要的。我們可以設置斷點,然後恢復程式的執行知道斷點處,在此可以查看應用程式的狀態,單步跟蹤到函數內部,然後在恢復執行等。

        3.3.1、中斷執行
            調試器中斷程式的方式有很多種,我舉三種最常用的中斷執行的方式。
            1)、如果我們使用的命令行調試器,可以使用【ctrol+c】組合鍵手動方式中斷調試目標的執行,例如:調試死鎖問題。
            2)、給我們的應用程式設置斷點來中斷調試目標的執行。通過設置斷點,可以很方便的使調試器在執行流程的任意位置上中斷執行。
            3)、拋出異常可以使調試器中斷執行。          
                
        3.3.2、恢復執行
            A、知識介紹
                當調試器中斷執行時(可能是觸發了斷點或者其他的事件),可以使用【g】命令回覆調試器的執行。如果【g】命令不帶任何參數,只是回覆調試目標的執行,直到下一次發生某個調試事件。
            B、眼見為實
                1)、使用【g】命令恢復執行。
                    調試源碼:ExampleCore_3_1_2
                    編譯好我們的項目,打開我們的命令工具,輸入命令【ntsd E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_2\bin\Debug\net8.0\ExampleCore_3_1_2.exe】,打開調試器新視窗。
                    

                    打開新的調試器視窗,如圖:
                    

                    觸發初始斷點是調試器的預設行為,也是調試人員開始分析應用程式的最早時機。此時,調試目標會停止執行並等待輸入命令。此刻,我們可以輸入【g】命令,恢復執行,調試器輸出如圖:
                    

                2)、禁用初始和退出斷點
                    調試源碼:ExampleCore_3_1_2
                    如果不希望調試器在初始啟動時停止程式的執行,可以在啟動調試器時使用 -g 命令開關,每當調試器退出時,調試器也將停止執行,可以使用 -G(大寫)命令開關,避免在進程結束時觸發最終的斷點。
                    編譯好我們的項目,打開我們的命令行工具,使用【ntsd -g E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_2\bin\Debug\net8.0\ExampleCore_3_1_2.exe】命令,啟動調試器。如圖:
                    

                    打開新的調試器視窗,這次已經輸出“第一次執行,並開始中斷執行!”,如圖:
                    

                    如果我們使用 -G 命令開關,執行命令【ntsd -G E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_2\bin\Debug\net8.0\ExampleCore_3_1_2.exe】,調試器在初始會中斷執行。如圖:
                    

                    如果沒有使用 -G 命令開關,我們還需要輸入命令才能退出。

        3.3.3、單步調試代碼
            我們使用過 VS IDE 的調試功能,快捷鍵有:F10,F11,F9等,調試器也為我們提供了類似的命令。但是,需要註意:如果在調試托管代碼時使用非托管調試器,那麼通常是對 JIT 編譯器產生的機器代碼進行單步調試。
            A、知識介紹
                1)、p 命令
                    p(step):命令其實就是 VS 中的 f10 快捷鍵,單步執行,遇到函數也是當成一條指令執行,不會進入函數體。
                2)、t 命令
                    t(trace):命令其實就是 VS 的 f11 快捷鍵,它是一種進入函數的單步執行調試。
                3)、pc 命令
                    pc(Step to Next Call)    就是一直運行直到遇到 call 為止,不會進入函數體,call 是一個函數調用,彙編指令。
                4)、tc 命令
                    tc(Trace to Next Call)    和 pc 不同的是,tc 會進入方法體,直到遇到 call 為止。
                5)、pt 命令
                    pt(Step to Next Return)    如果有方法會進入方法內部遞歸處理,遇到下一個 ret 為止。  
                6)、tt 命令
                    tt(Trace to Next Return)    會進入函數體直到遇到 ret 為止。遞歸的意思。

            B、眼見為實
                這一節的演示,使用【Windbg Preview】,我沒有使用的是【NSTD】調試器,其他他們是一樣的,只不過一個是由界面的,一個是沒界面的。有些操作是一樣的,我就寫在這裡了。
                編譯好我們的項目,打開【Windbg Preview】,依次點擊【文件】--->【Launch executable】,載入我們的項目文件:ExampleCore_3_1_3.exe,進入調試器。界面的內容太多,我們可以使用【.cls】命令,清空調試器的界面。我們再使用【g】命令,繼續運行調試器,我們現在查看一下托管代碼的調用棧,執行命令【!clrstack】。
0:000> g
ModLoad: 00007ff9`454c0000 00007ff9`454f0000   C:\Windows\System32\IMM32.DLL
ModLoad: 00007ff8`8d8e0000 00007ff8`8d938000   C:\Program Files\dotnet\host\fxr\8.0.0\hostfxr.dll
ModLoad: 00007ff8`812d0000 00007ff8`81334000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.0\hostpolicy.dll
ModLoad: 00007ff8`80de0000 00007ff8`812cb000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.0\coreclr.dll
ModLoad: 00007ff9`454f0000 00007ff9`45619000   C:\Windows\System32\ole32.dll
ModLoad: 00007ff9`44b10000 00007ff9`44e64000   C:\Windows\System32\combase.dll
ModLoad: 00007ff9`45d60000 00007ff9`45e35000   C:\Windows\System32\OLEAUT32.dll
ModLoad: 00007ff9`444f0000 00007ff9`4456f000   C:\Windows\System32\bcryptPrimitives.dll
(3994.1028): Unknown exception - code 04242420 (first chance)
ModLoad: 00007ff8`7fb10000 00007ff8`807a8000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.0\System.Private.CoreLib.dll
ModLoad: 00007ff8`7f950000 00007ff8`7fb08000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.0\clrjit.dll
ModLoad: 00007ff9`44120000 00007ff9`44133000   C:\Windows\System32\kernel.appcore.dll
ModLoad: 0000021a`398c0000 0000021a`398c8000   E:\Visual Studio 2022\.\ExampleCore_3_1_3\bin\Debug\net8.0\ExampleCore_3_1_3.dll
ModLoad: 0000021a`398d0000 0000021a`398de000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.0\System.Runtime.dll
ModLoad: 00007ff8`7f920000 00007ff8`7f948000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.0\System.Console.dll
(3994.1028): Break instruction exception - code 80000003 (first chance)
KERNELBASE!wil::details::DebugBreak+0x2:
00007ff9`44799202 cc              int     3

0:000> !clrstack
OS Thread Id: 0x1028 (0)
        Child SP               IP Call Site
00000020B9B7E6A8 00007ff944799202 [HelperMethodFrame: 00000020b9b7e6a8] System.Diagnostics.Debugger.BreakInternal()
00000020B9B7E7B0 00007ff87ff360aa System.Diagnostics.Debugger.Break() [/_/src/coreclr/./Debugger.cs @ 18]
00000020B9B7E7E0 00007ff8213f197f ExampleCore_3_1_3.Program.Main(System.String[]) [E:\Visual Studio 2022\.\ExampleCore_3_1_3\Program.cs @ 10]

                    我們找到了紅色標註的【Program.Main()】方法的地址:00007ff8213f197f有了這個地址,我們就可以對這個地址下一個斷點。

1 0:000> bp 00007ff8213f197f

                    設置好斷點後,我們就可以使用【g】命令,繼續運行調試器。


                1)、p、pc、pt 命令的使用
                    調試源碼:ExampleCore_3_1_3
                    我們設置好了斷點,就可以開始我們的調試工作了。繼續使用【g】運行調試器,調試器會在【Debugger.Break()】這行代碼暫停,效果如圖:
                    

                    我們可以使用【p】命令,單步執行,到了第15行代碼,會直接跳過而執行,不會進入方法。當然,這個過程要執行多次【p】命令。

 1 0:000> p
 2 ExampleCore_3_1_3!ExampleCore_3_1_3.Program.Main+0x50:
 3 00007ff8`213f1980 c745fc0a000000  mov     dword ptr [rbp-4],0Ah ss:00000020`b9b7e84c=00000000
 4 0:000> p
 5 ExampleCore_3_1_3!ExampleCore_3_1_3.Program.Main+0x57:
 6 00007ff8`213f1987 c745f814000000  mov     dword ptr [rbp-8],14h ss:00000020`b9b7e848=00000000
 7 0:000> p
 8 ExampleCore_3_1_3!ExampleCore_3_1_3.Program.Main+0x5e:
 9 00007ff8`213f198e 8b4dfc          mov     ecx,dword ptr [rbp-4] ss:00000020`b9b7e84c=0000000a
10 0:000> p
11 ExampleCore_3_1_3!ExampleCore_3_1_3.Program.Main+0x61:
12 00007ff8`213f1991 ff1531520a00    call    qword ptr [00007ff8`21496bc8] ds:00007ff8`21496bc8=00007ff8213f1a20
13 0:000> p
14 ExampleCore_3_1_3!ExampleCore_3_1_3.Program.Main+0x67:
15 00007ff8`213f1997 8

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

-Advertisement-
Play Games
更多相關文章
  • 這是我讀大學時的Java知識點總結,還不全面,後續會逐漸增加完善。 知識點集合 實例變數 實例變數是指在類中聲明的變數,其值是針對類的每個實例而獨立存儲的。每個類的實例都有自己的一組實例變數,它們的值可以在對象創建時初始化,併在整個對象的生命周期中保持不變或者隨著對象的狀態而改變。 實例變數也被稱為 ...
  • 本文分享自華為雲社區《深入Python:sys模塊的功能與應用詳解》,作者: 檸檬味擁抱。 在Python的標準庫中,sys 模塊是一個常用而強大的工具,它提供了與Python解釋器交互的函數和變數。本文將介紹sys模塊的一些常用函數和方法,並通過實際的代碼實例來解析它們的用法。 1. sys.ar ...
  • 本文介紹基於Python中ArcPy模塊,實現基於柵格圖像批量裁剪柵格圖像,同時對齊各個柵格圖像的空間範圍,統一其各自行數與列數的方法~ ...
  • 一、摘要 在上一篇文章中,我們講到了使用ReadWriteLock可以解決多線程同時讀,但只有一個線程能寫的問題。 如果繼續深入的分析ReadWriteLock,從鎖的角度分析,會發現它有一個潛在的問題:如果有線程正在讀數據,寫線程準備修改數據的時候,需要等待讀線程釋放鎖後才能獲取寫鎖,簡單的說就是 ...
  • 來源:https://medium.com/@benweidig 儘管 Java 是我使用過的向後相容程度最高的語言和環境之一,但始終存在功能棄用甚至刪除的可能性。Java 21 將棄用兩個功能,這就是我們今天要討論的內容。 推薦一個開源免費的 Spring Boot 實戰項目: https://g ...
  • Pair和Triple類是commons-lang3庫提供的實用工具,可以方便地將多個值組合在一起,實現多個返回值的效果,提高了代碼的可讀性和可維護性,使得處理多值情景更加輕鬆。 ...
  • 拓展閱讀 maven 包管理平臺-01-maven 入門介紹 + Maven、Gradle、Ant、Ivy、Bazel 和 SBT 的詳細對比表格 maven 包管理平臺-02-windows 安裝配置 + mac 安裝配置 maven 包管理平臺-03-maven project maven 項目 ...
  • 在WPF開發中,Binding實現了數據在Source和Target之間的傳遞和流通,就像現實生活中的一條條道路,建立起了城鎮與城鎮之間的銜接,而數據校驗和類型轉換,就像高速公路之間的收費站和安檢站。那在WPF開發中,如何實現數據的校驗和類型轉換呢?本文以一個簡單的小例子,簡述在WPF開發中,實現數... ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...