Location>code7788 >text

How to Debug .NET Programs on Linux with WinDbg

Popularity:854 ℃/2024-07-22 10:20:54

I: Background

1. Storytelling

latest version1.2402.24001.0 WinDbg is really exciting, you can disguise yourself as a GDB to open up the remote GDBServer to debug .NET programs on Linux, so you can continue to use the familiar WinDbg commands, in this version I think WinDbg is no longer WinDbg, but XDbg, draw a sketch as follows:

With the sketch in place, the next step is to put in the practice.

NET Debugging on Linux

1. Testing procedures

NET8 on CentOS7, but it's not very good, so I'll just use an existing .NETCore 3.1, the test code is as follows:


    internal class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                ($"{},tid={}");
                (1000);
            }
        }
    }

The code is very simple, that is, 1s outputs a record, next compiled to x64 deployed to Centos7.


[root@localhost data]# ls
ConsoleApp7        

2. Install GDBServer

Installing gdbserver on linux is easy, just use yum to install it.yum install gdb-gdbserver The output is as follows:


[root@localhost data]# yum install gdb-gdbserver
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: 
 * centos-sclo-rh: 
 * centos-sclo-sclo: 
 * epel: 
 * extras: 
 * updates: 
Package gdb-gdbserver-7.6.1-120.el7.x86_64 already installed and latest version
Nothing to do
[root@localhost data]# gdbserver –version
Usage:	gdbserver [OPTIONS] COMM PROG [ARGS ...]
	gdbserver [OPTIONS] --attach COMM PID
	gdbserver [OPTIONS] --multi COMM

COMM may either be a tty device (for serial debugging), or 
HOST:PORT to listen for a TCP connection.

Options:
  --debug               Enable general debugging output.
  --remote-debug        Enable remote protocol debugging output.
  --version             Display version information and exit.
  --wrapper WRAPPER --  Run WRAPPER to start new programs.
  --once                Exit after the first connection has closed.

After installing it, the next step is to start our program with gdbserver and start the debug port as 1234, refer to the following:


[root@localhost data]# gdbserver 192.168.128.130:1234 dotnet 
Process dotnet created; pid = 3643
Listening on port 1234

3. Connecting with windbg

With Windbg open, selectConnect to remote debugger option, fill in the concatenation string with thegdb:server=192.168.128.130,port=1234 To do so, the screenshot is below:

After connecting, there will be an initial interrupt, just enter g directly and the output will be as follows:


64-bit machine not using 64-bit API

************* Path validation summary **************
Response                         Time (ms)     Location
Deferred                                       SRV*C:\mysymbols*/download/symbols
Symbol search path is: SRV*C:\mysymbols*/download/symbols
Executable search path is: 
Unknown System Version 0 UP Free x64
System Uptime: not available
Process Uptime: not available
Reloading current modules
ModLoad: 00005555`55554000 00005555`555770cd   /usr/share/dotnet/dotnet
ModLoad: 00007fff`f7bbf000 00007fff`f7dda488   /lib64/.0
ModLoad: 00007fff`f79bb000 00007fff`f7bbe130   /lib64/.2
ModLoad: 00007fff`f76b3000 00007fff`f79ba420   /lib64/libstdc++.so.6
ModLoad: 00007fff`f73b1000 00007fff`f76b2138   /lib64/.6
ModLoad: 00007fff`f719b000 00007fff`f73b0400   /lib64/libgcc_s.so.1
ModLoad: 00007fff`f6dcd000 00007fff`f719a200   /lib64/.6
ModLoad: 00007fff`f7ddb000 00007fff`f7ffe150   /lib64/.2
ModLoad: 00007fff`f7f72000 00007fff`f7fda288   /usr/share/dotnet/host/fxr/6.0.26/
ModLoad: 00007fff`f6b7c000 00007fff`f6dcc3b0   /usr/share/dotnet/shared//3.1.32/
ModLoad: 00007fff`f63e7000 00007fff`f6b7bac8   /usr/share/dotnet/shared//3.1.32/
ModLoad: 00007fff`f61df000 00007fff`f63e6c38   /lib64/.1
ModLoad: 00007fff`f57d2000 00007fff`f59dd8c0   /lib64/.1
ModLoad: 00007fff`f3142000 00007fff`f3413dac   /usr/share/dotnet/shared//3.1.32/
ModLoad: 00007fff`f2f31000 00007fff`f3141468   /usr/share/dotnet/shared//3.1.32/
ModLoad: 00007fff`f2d26000 00007fff`f2f30488   /usr/share/dotnet/shared//3.1.32/
ModLoad: 00007fff`f29ad000 00007fff`f2d25fe0   /lib64/.50
ModLoad: 00007fff`f13da000 00007fff`f29ac030   /lib64/.50
ModLoad: 00007fff`f0fdb000 00007fff`f13d9340   /lib64/.50
...................
ReadVirtual() failed in GetXStateConfiguration() first read attempt (error == 0.)
Unable to load image /lib64/.0, Win32 error 0n2
*** WARNING: Unable to verify timestamp for /lib64/.0
Unable to load image /usr/share/dotnet/shared//3.1.32/, Win32 error 0n2
*** WARNING: Unable to verify timestamp for /usr/share/dotnet/shared//3.1.32/
libpthread_so!_pthread_cond_timedwait+0x132:
00007fff`f7bcade2 4989c6          mov     r14,rax
0:000> g

Some people may wonder why WinDbg can masquerade as GDB to communicate with GDBServer. This is actually due to the fact that WinDbg is a host, which can be wirelessly augmented by a number of external plug-ins, which is the opposite of Linux's divide-and-conquer.

This can be done next with the.chain command to observe the list of plug-ins where theGDBServerComposition cap (a poem)ELFBinComposition Let this feature be realized.


0:000> .chain
Extension DLL chain:
    GDBServerComposition: image 10.0.27553.1004, API 0.0.0, 
        [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\]
    ELFBinComposition: image 10.0.27553.1004, API 0.0.0, 
        [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\]
    dbghelp: image 10.0.27553.1004, API 10.0.6, 
        [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\]
    uext: image 10.0.27553.1004, API 1.0.0, 
        [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\]

The next step in the verification process is to study the coreclr source code, and you'll see that the .NET Sleep function on Linux is implemented with the help of the underlyingpthread_cond_timedwait function, Linux does not provide a system call similar to the Windows SleepEx, which is a bit of a pitfall, refer to the following:


PAL_ERROR CPalSynchronizationManager::ThreadNativeWait(
    ThreadNativeWaitData* ptnwdNativeWaitData,
    DWORD dwTimeout,
    ThreadWakeupReason* ptwrWakeupReason,
    DWORD* pdwSignaledObject)
{
	//...
    while (FALSE == ptnwdNativeWaitData->iPred)
    {
        if (INFINITE == dwTimeout)
        {
            iWaitRet = pthread_cond_wait(&ptnwdNativeWaitData->cond,
                &ptnwdNativeWaitData->mutex);
        }
        else
        {
            iWaitRet = pthread_cond_timedwait(&ptnwdNativeWaitData->cond,
                &ptnwdNativeWaitData->mutex,
                &tsAbsTmo);
        }
    }
	//...
}

Anyway, we're done debugging Linux .NET programs with WinDbg.

III: Summary

Now WinDbg is not what it used to be, all platforms (MacOs, Linux, Windows), thanks to Windbg is a host model architecture system, give WinDbg praise!
图片名称