Skip to content

Instantly share code, notes, and snippets.

@gfoidl
Last active October 5, 2023 18:07
Show Gist options
  • Save gfoidl/68333a5c5261f393d7d2ca1ba72821fb to your computer and use it in GitHub Desktop.
Save gfoidl/68333a5c5261f393d7d2ca1ba72821fb to your computer and use it in GitHub Desktop.
Debug tipps for .NET with LLDB

Debug

Visual Studio debugging

  • local as usual
  • remote over SSH for Linux possible
  • mixed mode (managed + native) possible, therefore in the managed project in the debug properties the flag must be set, so a launchprofile.json will be created
    {
      "profiles": {
        "MyProject": {
          "commandName"    : "Project",
          "nativeDebugging": true
        }
      }
    }
  • to debug dotnet test on Linux run it with
    export VSTEST_HOST_DEBUG=1
    dotnet test --no-build
    
    # Waiting for debugger to attach...
    # Process Id: 41987, Name: dotnet

LLDB

Create dumps

  • the usual tools (dotnet-dump, etc.) to create a dump at runtime
  • set DOTNET_DbgEnableMiniDump=1 to create a dump when the app crashes

Setup

sudo apt update
sudo apt install lldb
dotnet tool install -g dotnet-symbol
dotnet tool install -g dotnet-sos
dotnet-sos install

Run debug session

# download the needed symbols via 
$ dotnet-symbol $pathToDumpFile --host-only

# start LLDB, -c is shortcut for --core
$ lldb --core $pathToDumpFile

# set symbol-server (not needed for every run, as it's cached) and load the symbol for that session
(lldb) setsymbolserver ~/.dotnet/symbolcache
(lldb) loadsymbols

Now LLDB is setup and LLDB and SOS (sons of strike) commands can be run.

# show list of threads and see in what state they are
(lldb) thread list

# select interesting thread
(lldb) thread select 6

# show backtrace (call stack of active (selected) thread)
(lldb) bt

# show variables on current stack frame
(lldb) frame variables
# shortcut
(lldb) fr v

# Move up/down the stack
(lldb) up
(lldb) down

# Select a stack frame by index from the shown backtract (bt)
(lldb) frame select 10
# shortcut
(lldb) fr s 10
# shorter
(lldb) f 10

# Show value of registers
(lldb) register read

# Disassemble current function on current stack frame
(lldb) disassembly
# or as shortcut
(lldb) di

# Print content of memory at address 0xabcd
# -s1 byte-size
# -fc format as char (cf. help format)
# c32 count is 32
(lldb) memory read -s1 -fc -c32 0xabcd
# or as shortcut
(lldb) me r -s1 -fc -c32 0xabcd

# show managed stacktrace (via SOS)
(lldb) clrstack
# show manages stacktrace with the native stackframes intermixed
(lldb) clrstack -f

# show arguments and locals for all (managed) frames
# note -i is a different view so can be omitted / tried out (better for locals)
(lldb) clrstack -a -i
# show arguments and locals for frame 3
(lldb) clrstack -i -a 3
# based on these value object information etc. can be obtained, e.g. via dumpobj (do shortcut doesn't work in (lldb)

# show exception for that thread (note: pe or printexception)
(lldb) pe

# display objects on current stack
(lldb) dso

# dump object information
(lldb) dumpobj <address>

Run app under debugger

$ lldb $app
(lldb) run

# ...

(lldb) detach
(lldb) exit

where $app is either the native apphost created by the .NET build or dotnet then e.g.

$ lldb dotnet -- test --no-build
(lldb) run

Further info

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment