翼度科技»论坛 编程开发 .net 查看内容

如何用 WinDbg 调试Linux上的 .NET程序

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
一:背景

1. 讲故事

最新版本 1.2402.24001.0 的WinDbg真的让人很兴奋,可以将自己伪装成 GDB 来和远程的 GDBServer 打通来实现对 Linux 上 .NET程序进行调试,这样就可以继续使用熟悉的WinDbg 命令,在这个版本中我觉得 WinDbg 不再是 WinDbg,而是 XDbg 了,画个简图如下:

简图有了,接下来就要付出实践了。
二:实操 Linux 上 .NET调试

1. 测试程序

本想在 CentOS7 上安装 .NET8,不大好装,这里就用一个现存的 .NETCore 3.1 吧,测试代码如下:
  1.     internal class Program
  2.     {
  3.         static void Main(string[] args)
  4.         {
  5.             while (true)
  6.             {
  7.                 Console.WriteLine($"{DateTime.Now},tid={Thread.CurrentThread.ManagedThreadId}");
  8.                 Thread.Sleep(1000);
  9.             }
  10.         }
  11.     }
复制代码
代码非常简单,就是1s输出一条记录,接下来编译成x64部署到 Centos7 上。
  1. [root@localhost data]# ls
  2. ConsoleApp7  ConsoleApp7.deps.json  ConsoleApp7.dll  ConsoleApp7.pdb  ConsoleApp7.runtimeconfig.json
复制代码
2. 安装GDBServer

在 linux 上安装 gdbserver 比较简单,使用 yum 安装即可 yum install gdb-gdbserver ,输出如下:
  1. [root@localhost data]# yum install gdb-gdbserver
  2. Loaded plugins: fastestmirror, langpacks
  3. Loading mirror speeds from cached hostfile
  4. * base: mirror-hk.koddos.net
  5. * centos-sclo-rh: ftp.sjtu.edu.cn
  6. * centos-sclo-sclo: ftp.sjtu.edu.cn
  7. * epel: mirror.hoster.kz
  8. * extras: ftp.sjtu.edu.cn
  9. * updates: mirror-hk.koddos.net
  10. Package gdb-gdbserver-7.6.1-120.el7.x86_64 already installed and latest version
  11. Nothing to do
  12. [root@localhost data]# gdbserver –version
  13. Usage:        gdbserver [OPTIONS] COMM PROG [ARGS ...]
  14.         gdbserver [OPTIONS] --attach COMM PID
  15.         gdbserver [OPTIONS] --multi COMM
  16. COMM may either be a tty device (for serial debugging), or
  17. HOST:PORT to listen for a TCP connection.
  18. Options:
  19.   --debug               Enable general debugging output.
  20.   --remote-debug        Enable remote protocol debugging output.
  21.   --version             Display version information and exit.
  22.   --wrapper WRAPPER --  Run WRAPPER to start new programs.
  23.   --once                Exit after the first connection has closed.
复制代码
安装好之后,接下来用 gdbserver 来启动我们的程序,并启动调试端口为 1234,参考如下:
  1. [root@localhost data]# gdbserver 192.168.128.130:1234 dotnet ConsoleApp7.dll
  2. Process dotnet created; pid = 3643
  3. Listening on port 1234
复制代码
3. 使用 windbg 连接

打开Windbg后,选择 Connect to remote debugger 选项, 在连接字符串中填入 gdb:server=192.168.128.130,port=1234 即可,截图如下:

连接好之后,会有一个初始中断,直接输入g就好了,输出如下:
  1. 64-bit machine not using 64-bit API
  2. ************* Path validation summary **************
  3. Response                         Time (ms)     Location
  4. Deferred                                       SRV*C:\mysymbols*https://msdl.microsoft.com/download/symbols
  5. Symbol search path is: SRV*C:\mysymbols*https://msdl.microsoft.com/download/symbols
  6. Executable search path is:
  7. Unknown System Version 0 UP Free x64
  8. System Uptime: not available
  9. Process Uptime: not available
  10. Reloading current modules
  11. ModLoad: 00005555`55554000 00005555`555770cd   /usr/share/dotnet/dotnet
  12. ModLoad: 00007fff`f7bbf000 00007fff`f7dda488   /lib64/libpthread.so.0
  13. ModLoad: 00007fff`f79bb000 00007fff`f7bbe130   /lib64/libdl.so.2
  14. ModLoad: 00007fff`f76b3000 00007fff`f79ba420   /lib64/libstdc++.so.6
  15. ModLoad: 00007fff`f73b1000 00007fff`f76b2138   /lib64/libm.so.6
  16. ModLoad: 00007fff`f719b000 00007fff`f73b0400   /lib64/libgcc_s.so.1
  17. ModLoad: 00007fff`f6dcd000 00007fff`f719a200   /lib64/libc.so.6
  18. ModLoad: 00007fff`f7ddb000 00007fff`f7ffe150   /lib64/ld-linux-x86-64.so.2
  19. ModLoad: 00007fff`f7f72000 00007fff`f7fda288   /usr/share/dotnet/host/fxr/6.0.26/libhostfxr.so
  20. ModLoad: 00007fff`f6b7c000 00007fff`f6dcc3b0   /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.32/libhostpolicy.so
  21. ModLoad: 00007fff`f63e7000 00007fff`f6b7bac8   /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.32/libcoreclr.so
  22. ModLoad: 00007fff`f61df000 00007fff`f63e6c38   /lib64/librt.so.1
  23. ModLoad: 00007fff`f57d2000 00007fff`f59dd8c0   /lib64/libnuma.so.1
  24. ModLoad: 00007fff`f3142000 00007fff`f3413dac   /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.32/libclrjit.so
  25. ModLoad: 00007fff`f2f31000 00007fff`f3141468   /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.32/System.Native.so
  26. ModLoad: 00007fff`f2d26000 00007fff`f2f30488   /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.32/System.Globalization.Native.so
  27. ModLoad: 00007fff`f29ad000 00007fff`f2d25fe0   /lib64/libicuuc.so.50
  28. ModLoad: 00007fff`f13da000 00007fff`f29ac030   /lib64/libicudata.so.50
  29. ModLoad: 00007fff`f0fdb000 00007fff`f13d9340   /lib64/libicui18n.so.50
  30. ...................
  31. ReadVirtual() failed in GetXStateConfiguration() first read attempt (error == 0.)
  32. Unable to load image /lib64/libpthread.so.0, Win32 error 0n2
  33. *** WARNING: Unable to verify timestamp for /lib64/libpthread.so.0
  34. Unable to load image /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.32/libcoreclr.so, Win32 error 0n2
  35. *** WARNING: Unable to verify timestamp for /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.32/libcoreclr.so
  36. libpthread_so!_pthread_cond_timedwait+0x132:
  37. 00007fff`f7bcade2 4989c6          mov     r14,rax
  38. 0:000> g
复制代码
有些人可能会好奇,为什么 WinDbg 能伪装成 GDB 来和 GDBServer 来通讯,这其实得益于 WinDbg 是一个宿主,它可以被很多外来的插件无线扩容自己的功能,这和 Linux 的分而治之恰恰相反。。。
接下来可以用 .chain 命令观察插件列表,其中的 GDBServerComposition 和 ELFBinComposition 让这项功能得到实现。
  1. 0:000> .chain
  2. Extension DLL chain:
  3.     GDBServerComposition: image 10.0.27553.1004, API 0.0.0,
  4.         [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\GDBServerComposition.dll]
  5.     ELFBinComposition: image 10.0.27553.1004, API 0.0.0,
  6.         [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\ELFBinComposition.dll]
  7.     dbghelp: image 10.0.27553.1004, API 10.0.6,
  8.         [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\dbghelp.dll]
  9.     uext: image 10.0.27553.1004, API 1.0.0,
  10.         [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\uext.dll]
复制代码
接下来就可以做验证了,研究 coreclr 源码,你会发现在 Linux 上 .NET 的 Sleep 函数是借助于底层的 pthread_cond_timedwait 函数,Linux并没有提供类似Windows 的SleepEx这样的系统调用,这就比较坑了,参考如下:
  1. PAL_ERROR CPalSynchronizationManager::ThreadNativeWait(
  2.     ThreadNativeWaitData* ptnwdNativeWaitData,
  3.     DWORD dwTimeout,
  4.     ThreadWakeupReason* ptwrWakeupReason,
  5.     DWORD* pdwSignaledObject)
  6. {
  7.         //...
  8.     while (FALSE == ptnwdNativeWaitData->iPred)
  9.     {
  10.         if (INFINITE == dwTimeout)
  11.         {
  12.             iWaitRet = pthread_cond_wait(&ptnwdNativeWaitData->cond,
  13.                 &ptnwdNativeWaitData->mutex);
  14.         }
  15.         else
  16.         {
  17.             iWaitRet = pthread_cond_timedwait(&ptnwdNativeWaitData->cond,
  18.                 &ptnwdNativeWaitData->mutex,
  19.                 &tsAbsTmo);
  20.         }
  21.     }
  22.         //...
  23. }
复制代码

不管怎么说,我们用 WinDbg 调试 Linux 的 .NET 程序算是大功告成了。
三:总结

现在的 WinDbg 早已今非昔比,全平台(MacOs,Linux,Windows) 通吃,这也得益于 Windbg 是一个宿主模式的架构体系,给 WinDbg 点赞!


来源:https://www.cnblogs.com/huangxincheng/p/18315571
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具