- Debugging CoreCLR on Windows
- Debugging CoreCLR on Linux and macOS
- Debugging AOT compilers
- Debugging Managed Code
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.
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.
Visual Studio's capabilities as a full IDE provide a lot of help making the runtime debugging more amiable.
- 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 theartifacts
directory. - Open the CoreCLR solution (coreclr.sln) in Visual Studio.
- Method 1: Use the build scripts to open the solution:
- 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 bex64 Debug
.
- Run
- Method 2: Manually build and open the solution:
- Perform a build of the repo with the
-msbuild
flag. - 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 arex64
andDebug
, unless explicitly stated otherwise.
- Perform a build of the repo with the
- Method 1: Use the build scripts to open the solution:
- Right-click the INSTALL project and choose
Set as StartUp Project
. - Bring up the properties page for the INSTALL project.
- Select Configuration Properties -> Debugging from the left side tree control.
- Set
Command=$(SolutionDir)\..\..\..\..\bin\coreclr\windows.$(Platform).$(Configuration)\corerun.exe
. This points to the folder where the built runtime binaries are present. - Set
Command Arguments=<managed app you wish to run>
(e.g. HelloWorld.dll). - Set
Working Directory=$(SolutionDir)\..\..\..\..\bin\coreclr\windows.$(Platform).$(Configuration)
. This points to the folder containing CoreCLR binaries. - Set
Environment=CORE_LIBRARIES=$(SolutionDir)\..\..\..\..\bin\runtime\<target-framework>-windows-$(Configuration)-$(Platform)
, where '<target-framework>' is the target framework of current branch; for examplenet6.0
,net7.0
ornet8.0
. A few notes on this step:
- This points to the folder containing core libraries except
System.Private.CoreLib
. - This step can be skipped if you are debugging CLR tests that reference only
System.Private.CoreLib
. Otherwise, it's required to debug a real-world application that references anything else, includingSystem.Runtime
.
- Right-click the INSTALL project and choose
Build
. This will load necessary information from CMake to Visual Studio. - 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 theEEStartup()
function inceemain.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.
- 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. - Set the
corerun
project as startup project. When using the folder view instead of the CMake targets view, right clickcoreclr\hosts\corerun\CMakeLists.txt
to set as startup project, or select it from debug target dropdown of Visual Studio. - Right click the
corerun
project and open theDebug
configuration. You can also click on Debug -> Debug and Launch Configuration from the Visual Studio main bar menu. - In the opened
launch.vs.json
, set following properties to the configuration ofcorerun
:
{
"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 net8.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.
- Right click the CoreCLR project or
coreclr\CMakeLists.txt
in the folder view, and then invoke the Install command. - 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.
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!
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:
- The official Microsoft docs on SOS.
- The instructions at the diagnostics repo.
For more information on SOS commands click here.
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.
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:
- The official Microsoft docs on SOS.
- The instructions at the diagnostics repo.
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.
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.
- Perform a build of the clr subset of the runtime repo.
- 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>
- 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. - Launch program:
process launch -s
- To stop breaks on SIGUSR1 signals used by the runtime run the following command:
process handle -s false SIGUSR1
- Set a breakpoint where CoreCLR is initialized, as it's the most stable point to begin debugging:
breakpoint set -n coreclr_execute_assembly
. - Get to that point by issuing
process continue
after setting the breakpoint. - Now, you're ready to begin your debugging session. You can set breakpoints or run SOS commands like
clrstack
orsos VerifyHeap
. Note that SOS command names are case sensitive.
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
Our friends at the diagnostics repo have a very detailed guide on core dumps debugging here in their repo.
Debugging AOT compilers is described in its related document.
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.
- Install the C# Extension.
- Open the folder containing the source you want to debug in VS Code.
- Open the debug window:
ctrl-shift-D
/cmd-shift-D
or click on the button on the left. - Click the gear button at the top to create a launch configuration, and select
.NET 5+ and .NET Core
from the selection dropdown. - It will create a
launch.json
file, where you can configure what and how you want to debug it. Here is a basic template on how to fill it:
{
"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.
}
]
}
- Set a breakpoint and launch the debugger, inspecting variables and call stacks will now work
- Use File -> Open Project (not open file) and select the binary you want to use as your host (typically dotnet.exe or corerun.exe).
- Open the project properties for the new project that was just created and set the following:
- Arguments: Make this match whatever arguments you would have used at the command-line. For example if you would have run
dotnet.exe exec Foo.dll
, then setarguments = "exec Foo.dll"
(NOTE: Make sure you usedotnet exec
instead ofdotnet run
because the run verb command is implemented to launch the app in a child process, and the debugger won't be attached to that child process). - Working Directory: Make this match whatever you would have used on the command-line.
- Debugger Type: Set this to
Managed (.NET Core, .NET 5+)
. If you're going to debug the native C++ code, then you would selectNative Only
instead. - Environment: Add any environment variables you would have added at the command-line. You may also consider adding
DOTNET_ReadyToRun=0
, which disables R2R pre-compilation, and allow the JIT to create debuggable code. This will give you a higher quality C# debugging experience inside the runtime framework assemblies, at the cost of somewhat lower app performance.
- Arguments: Make this match whatever arguments you would have used at the command-line. For example if you would have run
- For managed debugging, there are some additional settings in Debug -> Options, Debugging -> General that might be useful:
- Uncheck
Just My Code
. This will allow you debug into the framework libraries. - Check
Enable .NET Framework Source Stepping
. This will configure the debugger to download symbols and source automatically for runtime framework binaries. If you built the framework yourself, then you can omit this step without any problems. - Check
Suppress JIT optimzation on module load
. This tells the debugger to tell the .NET runtime JIT to generate debuggable code even for modules that may not have been compiled in aDebug
configuration by the C# compiler. This code is slower, but it provides much higher fidelity breakpoints, stepping, and local variable access. It is the same difference you see when debugging .NET apps in theDebug
project configuration vs theRelease
project configuration.
- Uncheck
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:
- The
DOTNET_ROOT
environment variable: if Visual Studio is started from a command prompt whereDOTNET_ROOT
is set, it will ignore unsigned .NET runtime debugger libraries which are under theDOTNET_ROOT
directory. - The
VSDebugger_ValidateDotnetDebugLibSignatures
environment variable: If you want to temporarily disable signature validation, runset VSDebugger_ValidateDotnetDebugLibSignatures=0
in a command prompt, and start Visual Studio (devenv.exe) from this command prompt. - 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 runCommon7\IDE\VsRegEdit.exe set local HKCU Debugger\EngineSwitches ValidateDotnetDebugLibSignatures dword 0