vs2022的一些偵錯技巧——遠端偵錯&執行緒檢查&效能檢查

2023-06-05 18:00:49

visual studio一直都是.net/c#開發人員最受歡迎的編譯器,除了強大的程式碼提示和專案模板,還擁有大量的偵錯工具,這一期我們介紹下code freeze階段的一些偵錯技巧。包括測試環境/生產環境下的遠端偵錯,執行緒偵錯,以及效能監控偵錯。

遠端偵錯

當我們的程式所在的目標機器沒有跑原始碼所需的sdk或者沒辦法安裝vs的時候,我們需要對目標機器上正在執行或者即將執行的應用進行偵錯。我們通過遠端偵錯在本機上對目標機器偵錯。

1 安裝

Remote Debug下載地址:https://visualstudio.microsoft.com/zh-hans/downloads/?rr=https://docs.microsoft.com/en-us/visualstudio/debugger/remote-debugging?view=vs-2019

選擇下載進行安裝,最後管理員執行。

檢視目標機執行所使用的埠號

2.偵錯

1.偵錯->附加程序
修改對應的目標主機的埠和IP地址

2.選中對應的程序點選附加

3.我們貼上一段程式碼,程式碼會死迴圈列印一個自增的數

static void Main(string[] args)
{
    int i = 0;
    while (true) 
    {
        Console.WriteLine(++i);
        Thread.Sleep(1000);
    }
}

我們在目標主機執行起來,然後在本地利用原始碼進行遠端偵錯,並且加上一個斷點。發現斷點進來了,並且程式不再列印,則偵錯成功。

執行緒偵錯

我們可以利用visual studio的執行緒視窗來檢視目前所有的活動執行緒的執行情況以及執行緒目前在程式碼何處執行。該功能配合遠端偵錯可以很好的觀察生產或者測試環境下的死鎖問題。當然開發環境下也一樣。

我們來調查一個死鎖。
先實現一個簡單的死鎖程式:宣告兩個鎖物件,並且用兩個執行緒各自佔用一個鎖,再去索要另外一個鎖,形成死鎖。

private static object _lock = new object();
        private static object _lock_1 = new object();
        static void Main(string[] args)
        {
            var task1 = Task.Run(DeadLock);
            var task2 = Task.Run(DeadLock_1);

            Task.WaitAll(task1,task2);
            Console.WriteLine("Hello, World!");
        }

        private static void DeadLock()
        {
            lock (_lock)
            {
                Thread.Sleep(200);
                lock (_lock_1)
                {
                    Console.WriteLine("DeadLock");
                }
            }
        }

        private static void DeadLock_1()
        {
            lock (_lock_1)
            {
                Thread.Sleep(200);
                lock (_lock)
                {
                    Console.WriteLine("DeadLock_1");
                }
            }
        }

執行程式之後,將程式中斷

點選偵錯->視窗->執行緒
可以看到兩個工作執行緒,雙擊可以檢視該執行緒中斷時所處的位置。從而可以分析執行緒是否阻塞在這裡,是否是形成問題的關鍵。

效能偵錯

VS自帶的效能監控工具提供了一些資料的監控和分析,比較常用的就是CPU使用和記憶體IO的監控。它支援本地啟動專案的監控以及程序和可執行程式的附加。

點選偵錯->效能追蹤(Performance Profiler)

我們來實現一個CPU密集型的程式碼

        static async Task Main(string[] args)
        {
            await MultiRunning();
            SomeThing();
            Console.WriteLine("Hello, World!");
        }

        //啟動20個執行緒
        static async Task MultiRunning()
        {
            Thread[] tasks = new Thread[20];
            foreach (var index in Enumerable.Range(0, tasks.Length))
            {
                tasks[index] = new Thread(() =>
                {
                    while (true)
                    {
                        int a = 1;
                        Thread.Sleep(200);
                    }
                });
            }

            int i = 0;
            await Parallel.ForEachAsync(tasks, (task, source) =>
            {
                i++;
                task.Start();
                return ValueTask.CompletedTask;
            });

            Console.WriteLine("執行了" + i);
        }

        static void SomeThing()
        {
            while (true)
            {
                Console.WriteLine("I AM Alive");
                Thread.Sleep(200);
            }
        }

我們使用Performance Profiler對啟動專案的CPU進行監控。


當我們覺得程式執行的差不多了,我們可以點選停止蒐集,然後visual studio會給我們生成一些分析資料

點選開啟詳情,然後根據函數來分析佔用的CPU,預設也是按照函數所佔用的CPU時間片來進行排序。選擇我們程式的函數,雙擊Main函數,可以看到裡面的方法各自的佔用,從而判斷找出高CPU的函數。

以此類推,如果需要尋找高IO的根源點,也可以使用VS自帶的Performace Profiler來追蹤程式碼的執行效能。