runtime

Debugging CoreCLR

These instructions will lead you through debugging CoreCLR.

As an initial note, SOS (the main plugin to aid with CoreCLR debugging) no longer resides here. For more information on how to get it, installing it, and how to use it, check it out in its new home, the diagnostics repo.

Debugging CoreCLR on Windows

In order to start debugging CoreCLR, it is highly recommended to at least build the clr subset under the Debug configuration, in order to generate the necessary artifact files for the best debugging experience:

.\build.cmd -s clr -c Debug

Note that you can omit the -c Debug flag, since it’s the default one when none other is specified. We are leaving it here in this doc for the sake of clarity.

If for some reason System.Private.CoreLib.dll is missing, you can rebuild it with the following command, instead of having to go through the whole build again:

.\build.cmd -s clr.corelib+clr.nativecorelib -c Debug

NOTE: When debugging with CORE_LIBRARIES, the libs subset must also be built prior to attempting any debugging.

Using Visual Studio

Visual Studio’s capabilities as a full IDE provide a lot of help making the runtime debugging more amiable.

  1. Run .\build.cmd clr.nativeprereqs -a <architecture> -c <configuration>. This will build some of the tools requiremented for the native build. This step only needs to be run once as long you don’t clean the artifacts directory.
  2. Open the CoreCLR solution (coreclr.sln) in Visual Studio.
    • Method 1: Use the build scripts to open the solution:
      1. Run .\build.cmd -vs coreclr.sln -a <architecture> -c <configuration>. This will create and launch the CoreCLR solution in VS for the specified architecture and configuration. By default, this will be x64 Debug.
    • Method 2: Manually build and open the solution:
      1. Perform a build of the repo with the -msbuild flag.
      2. Open solution path\to\runtime\artifacts\obj\coreclr\windows.<architecture>.<configuration>\ide\CoreCLR.sln in Visual Studio. As in the previous method, the architecture and configuration by default are x64 and Debug, unless explicitly stated otherwise.
  3. Right-click the INSTALL project and choose Set as StartUp Project.
  4. Bring up the properties page for the INSTALL project.
  5. Select Configuration Properties -> Debugging from the left side tree control.
  6. Set Command=$(SolutionDir)\..\..\..\..\bin\coreclr\windows.$(Platform).$(Configuration)\corerun.exe. This points to the folder where the built runtime binaries are present.
  7. Set Command Arguments=<managed app you wish to run> (e.g. HelloWorld.dll).
  8. Set Working Directory=$(SolutionDir)\..\..\..\..\bin\coreclr\windows.$(Platform).$(Configuration). This points to the folder containing CoreCLR binaries.
  9. Set Environment=CORE_LIBRARIES=$(SolutionDir)\..\..\..\..\bin\runtime\<target-framework>-windows-$(Configuration)-$(Platform), where ‘<target-framework>’ is the target framework of current branch; for example net6.0, net8.0 or net9.0. A few notes on this step:
  1. Right-click the INSTALL project and choose Build. This will load necessary information from CMake to Visual Studio.
  2. Press F11 to start debugging at wmain in corerun, or set a breakpoint in source and press F5 to run to it. As an example, set a breakpoint for the EEStartup() function in ceemain.cpp to break into CoreCLR startup.

Steps 1-9 only need to be done once as long as there’s been no changes to the CMake files in the repository. Afterwards, step 10 can be repeated whenever you want to start debugging. As of now, it is highly recommended to use Visual Studio 2022 or Visual Studio 2019.

Using Visual Studio Open Folder with CMake

  1. Open the dotnet/runtime repository in Visual Studio using the open folder feature. When opening the repository root, Visual Studio will prompt about finding CMake files. Select src\coreclr\CMakeList.txt as the CMake workspace.
  2. Set the corerun project as startup project. When using the folder view instead of the CMake targets view, right click coreclr\hosts\corerun\CMakeLists.txt to set as startup project, or select it from debug target dropdown of Visual Studio.
  3. Right click the corerun project and open the Debug configuration. You can also click on Debug -> Debug and Launch Configuration from the Visual Studio main bar menu.
  4. In the opened launch.vs.json, set following properties to the configuration of corerun:
    {
      "type": "default",
      "project": "CMakeLists.txt",
      "projectTarget": "corerun.exe (hosts\\corerun\\corerun.exe)",
      "name": "corerun.exe (hosts\\corerun\\corerun.exe)",
      "environment": [
        {
          "name": "CORE_ROOT",
          "value": "${cmake.installRoot}"
        },
        {
          "name": "CORE_LIBRARIES",
          // for example net9.0-windows-Debug-x64
          "value": "${cmake.installRoot}\\..\\..\\runtime\\<tfm>-windows-<configuration>-<arch>\\"
        }
      ],
      "args": [
        // path to a managed application to debug
        // remember to use double backslashes (\\)
        "HelloWorld.dll"
      ]
    }

NOTE: For Visual Studio 17.3, changing the location of launched executable doesn’t work, so the CORE_ROOT is necessary.

  1. Right click the CoreCLR project or coreclr\CMakeLists.txt in the folder view, and then invoke the Install command.
  2. Press F10 or F11 to start debugging at main, or set a breakpoint and press F5.

Whenever you make changes to the CoreCLR source code, don’t forget to invoke the Install command again to have them set in place.

Using Visual Studio Code

It will be very nice to be able to achieve all this using Visual Studio Code as well, since it’s the editor of choice for lots of developers.

Visual Studio Code instructions coming soon!

Using SOS with Windbg or Cdb on Windows

Under normal circumstances, SOS usually comes shipped with Windbg, so no additional installation is required. However, if this is not the case for you, you want to use another version, or any other circumstance that requires you to install it separately/additionally, here are two links with useful information on how to get it set up:

For more information on SOS commands click here.

Debugging CoreCLR on Linux and macOS

Very similarly to Windows, Linux and macOS also require to have at least the clr subset built prior to attempting to debug, most preferably under the Debug configuration:

./build.sh -s clr -c Debug

Note that you can omit the -c Debug flag, since it’s the default one when none other is specified. We are leaving it here in this doc for the sake of clarity.

If for some reason System.Private.CoreLib.dll is missing, you can rebuild it with the following command, instead of having to go through the whole build again:

./build.sh -s clr.corelib+clr.nativecorelib -c Debug

NOTE: When debugging with CORE_LIBRARIES, the libs subset must also be built prior to attempting any debugging.

SOS on Unix

For Linux and macOS, you have to install SOS by yourself, as opposed to Windows’ Windbg. The instructions are very similar however, and you can find them on these two links:

It might also be the case that you would need the latest changes in SOS, or you’re working with a not-officially-supported scenario that actually works. The most common occurrence of this scenario is when using macOS Arm64. In this case, you have to build SOS from the diagnostics repo (linked above). Once you have it done, then simply load it to your lldb. More details in the following section.

Debugging CoreCLR with lldb

NOTE: Only lldb is supported to use with SOS. You can also use gdb, cgdb, or other debuggers, but you might not have access to SOS.

  1. Perform a build of the clr subset of the runtime repo.
  2. Start lldb passing corerun, the app to run (e.g. HelloWorld.dll), and any arguments this app might need: lldb -- /path/to/corerun /path/to/app.dll <app args go here>
  3. If you’re using the installed version of SOS, you can skip this step. If you built SOS manually, you have to load it before starting the debugging session: plugin load /path/to/built/sos/libsosplugin.so. Note that .so is for Linux, and .dylib is for macOS. You can find more information in the diagnostics repo private sos build doc.
  4. Launch program: process launch -s
  5. To stop breaks on SIGUSR1 signals used by the runtime run the following command: process handle -s false SIGUSR1
  6. Set a breakpoint where CoreCLR is initialized, as it’s the most stable point to begin debugging: breakpoint set -n coreclr_execute_assembly.
  7. Get to that point by issuing process continue after setting the breakpoint.
  8. Now, you’re ready to begin your debugging session. You can set breakpoints or run SOS commands like clrstack or sos VerifyHeap. Note that SOS command names are case sensitive.

Disabling Managed Attach/Debugging

The DOTNET_EnableDiagnostics environment variable can be used to disable managed debugging. This prevents the various OS artifacts used for debugging, such as named pipes and semaphores on Linux and macOS, from being created.

export DOTNET_EnableDiagnostics=0

Debugging core dumps with lldb

Our friends at the diagnostics repo have a very detailed guide on core dumps debugging here in their repo.

Debugging AOT compilers

Debugging AOT compilers is described in its related document.

Debugging Managed Code

Native C++ code is not everything in our runtime. Nowadays, there are lots of stuff to debug that stay in the higher C# managed code level.

Using Visual Studio Code for Managed Code

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "My Configuration", // Any identifiable name you might like.
      "type": "coreclr", // We want to debug a CoreCLR app.
      "request": "launch", // Start the app with the debugger attached.
      "program": "/path/to/corerun", // Point to your 'corerun', in order to run the app using your build.
      "args": ["app-to-debug.dll", "app arg1", "app arg2"], // First argument is your app, second and on are the app's arguments.
      "cwd": "/path/to/app-to-debug", // Can be anywhere. For simplicity, choose where your app is stationed. Otherwise, you have to adjust paths in the other parameters.
      "stopAtEntry": true, // This can be either. Keeping it to 'true' allows you to see when the debugger is ready.
      "console": "internalConsole", // Use VSCode's internal console instead of launching more terminals.
      "justMyCode": false, // Be able to debug into native assemblies.
      "enableStepFiltering": false, // Be able to debug into class initializations, field accessors, etc.
    }
  ]
}

Using Visual Studio for Managed Code

Resolving Signature Validation Errors in Visual Studio

Starting with Visual Studio 2022 version 17.5, Visual Studio will validate that the debugging libraries that shipped with the .NET Runtime are correctly signed before loading them. If they are unsigned, Visual Studio will show an error like:

Unable to attach to CoreCLR. Signature validation failed for a .NET Runtime Debugger library because the file is unsigned.

This error is expected if you are working with non-official releases of .NET (example: daily builds from https://github.com/dotnet/sdk). See https://aka.ms/vs/unsigned-dotnet-debugger-lib for more information.

If the target process is using a .NET Runtime that is either from a daily build, or one that you built on your own computer, this error will show up. NOTE: This error should never happen for official builds of the .NET Runtime from Microsoft. So don’t disable the validation if you expect to be using a .NET Runtime supported by Microsoft.

There are three ways to configure Visual Studio to disable signature validation:

  1. The DOTNET_ROOT environment variable: if Visual Studio is started from a command prompt where DOTNET_ROOT is set, it will ignore unsigned .NET runtime debugger libraries which are under the DOTNET_ROOT directory.
  2. The VSDebugger_ValidateDotnetDebugLibSignatures environment variable: If you want to temporarily disable signature validation, run set VSDebugger_ValidateDotnetDebugLibSignatures=0 in a command prompt, and start Visual Studio (devenv.exe) from this command prompt.
  3. Set the ValidateDotnetDebugLibSignatures registry key: To disable signature validation on a more permanent basis, you can set the VS registry key to turn it off. To do so, open a Developer Command Prompt, and run Common7\IDE\VsRegEdit.exe set local HKCU Debugger\EngineSwitches ValidateDotnetDebugLibSignatures dword 0