How to debug dotnet processes using the terminal (on linux)
I live in neovim
and the terminal. So it is natural to want to use the terminal to quickly attach, run and debug some C#
If you want the most control (and power I guess), gdb and lldb (a comparison for more details), have long been the goto solution. But the don't really support the dotnet runtime. Enter netcoredb
In fact many debugger GUI's are build to leverage gdb
or lldb
which have automation modes built-in. netcoredb
is used by neovim
to provide debugger functionality, but you can just use it directly.
Prerequisites
# install https://github.com/Samsung/netcoredbg
yay -S netcoredbg
Debugging a console app
per the docs, it is straight-forward to debug a console app
# assuming some simple exaple code (like https://github.com/guylangston/simple-cs)
netcoredbg --interpreter=cli -- /home/guy/.dotnet/dotnet ./bin/Debug/net8.0/simple-console.dll
ncdb> break Program.cs:11
Breakpoint 1 at Program.cs:11 --pending, warning: No executable code of the debugger's target code type is associated with this line.
ncdb> run
^running
library loaded: /home/guy/.dotnet/shared/Microsoft.NETCore.App/8.0.11/System.Private.CoreLib.dll
no symbols loaded, base address: 0x7fdfa6870000, size: 13100544(0xc7e600)
thread created, id: 32990
library loaded: /home/guy/repo/simple-cs/simple-console/bin/Debug/net8.0/simple-console.dll
symbols loaded, base address: 0x7fe026832000, size: 7680(0x1e00)
breakpoint modified, Breakpoint 1 at /home/guy/repo/simple-cs/simple-console/Program.cs:11
<snip..../>
In
32990
stopped, reason: breakpoint 1 hit, thread id: 32990, stopped threads: all, times= 1, frame={Program.<Main>$() at /home/guy/repo/simple-cs/simple-console/Program.cs:11}
ncdb> bt
#0: 0x00007fdfa7631a65 simple-console.dll` Program.<Main>$() at /home/guy/repo/simple-cs/simple-console/Program.cs:11
ncdb> list
6 Console.ReadLine();
7 }
8 for(int cc=2; cc<20; cc++)
9 {
10 var factors = Factorize(cc).ToArray(); // for easy debugger attach
> 11 Console.WriteLine($"Num: {cc} has Factors: {string.Join(',', factors.Select(x=>x.ToString()))}");
12 }
13 Console.WriteLine("Out");
14 return 0;
15
ncdb> print cc
cc = 2
Debugging a unit test (xunit)
$ VSTEST_HOST_DEBUG=1 dotnet test --filter CanDebug | grep "Process Id: "
Process Id: 34659, Name: dotnet
$ netcoredbg --attach 34659
Note: Adding VSTEST_HOST_DEBUG=1
as a prefix will set the environment variable, which will pause the running at the start of the matching test and display the PID
to allow attaching.
$ netcoredbg --attach 34995
library loaded: /home/guy/.dotnet/shared/Microsoft.NETCore.App/8.0.11/System.Private.CoreLib.dll
no symbols loaded, base address: 0x7fefcac60000, size: 13100544(0xc7e600)
You are debugging a Release build of testhost.dll. Using Just My Code with Release builds using compiler optimizations
<snip.../>
stopped, reason: interrupted, thread id: 34995, stopped threads: all, frame={System.Private.CoreLib.dll` System.Threading.Monitor.Wait()}
ncdb> bt
#0: 0x00007fefcae675be System.Private.CoreLib.dll` System.Threading.Monitor.Wait()
#1: 0x00007fefcae72292 System.Private.CoreLib.dll` System.Threading.ManualResetEventSlim.Wait()
#2: 0x00007fefcae87fd1 System.Private.CoreLib.dll` System.Threading.Tasks.Task.SpinThenBlockingWait()
#3: 0x00007fefcae87d98 System.Private.CoreLib.dll` System.Threading.Tasks.Task.InternalWaitCore()
#4: 0x00007fefcaed5da8 System.Private.CoreLib.dll` System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification()
#5: 0x00007fefcba22abc testhost.dll` Microsoft.VisualStudio.TestPlatform.TestHost.Program.Run() at /_/src/testhost.x86/Program.cs:61
#6: 0x00007fefcba227cf testhost.dll` Microsoft.VisualStudio.TestPlatform.TestHost.Program.Run() at /_/src/testhost.x86/Program.cs:54
#7: 0x00007fefcba21970 testhost.dll` Microsoft.VisualStudio.TestPlatform.TestHost.Program.Main() at /_/src/testhost.x86/Program.cs:37
ncdb> break ExampleXunit.cs:30
Breakpoint 1 at ExampleXunit.cs:30 --pending, warning: No executable code of the debugger's target code type is associated with this line.
ncdb> c
^running
ncdb>
thread exited, id: 35004
thread exited, id: 35017
thread created, id: 35487
You are debugging a Release build of Microsoft.TestPlatform.CrossPlatEngine.dll. Using Just My Code with Release builds using compiler optimizations results in a degraded debugging experience (e.g. breakpoints will not be hit).
library loaded: /home/guy/repo/simple-cs/simple-test/bin/Debug/net8.0/Microsoft.TestPlatform.CrossPlatEngine.dll
symbols loaded, base address: 0x7fefc8166000, size: 315328(0x4cfc0)
<snip..../>
You are debugging a Release build of xunit.assert.dll. Using Just My Code with Release builds using compiler optimizations results in a degraded debugging experience (e.g. breakpoints will not be hit).
library loaded: /home/guy/repo/simple-cs/simple-test/bin/Debug/net8.0/xunit.assert.dll
symbols loaded, base address: 0x7faf14020000, size: 136224(0x21420)
stopped, reason: breakpoint 1 hit, thread id: 35503, stopped threads: all, times= 1, frame={simple_test.ExampleXunit.CanDebug() at /home/guy/repo/simple-cs/simple-test/ExampleXunit.cs:30}
ncdb> print a
a = 10
ncdb> print res
res = 60
ncdb> step
Comments, suggestions, improvements all welcome. Either via mastadon or comment section below.