- 语言运行时
- 标准库
- JIT编译
- 编译器
【免费下载链接】runtime
.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
本指南以 dotnet/runtime 仓库的 debugging-runtime.md 为骨架,系统讲解如何在 Windows(Visual Studio / VS Code / Windbg / Cdb)、Linux 与 macOS(lldb)上调试 CoreCLR 运行时,涵盖构建前的准备工作、SOS 插件获取与加载、AOT 编译器调试入口,以及托管代码(C#)调试的 VS Code / Visual Studio 配置。读完本文,你将能够独立搭建一套"自建 CoreCLR + 源码断点 + SOS 命令"的完整调试环境,并理解corerun宿主、EEStartup、coreclr_execute_assembly等关键符号在调试链路中的位置。
调试前的准备工作:构建 clr 子集
无论是哪个平台,调试 CoreCLR 的第一步都是先构建运行时本身。官方强烈建议至少以Debug 配置构建clr子集,这样才会生成符号文件等调试所需的工件(artifact)。
Windows 上执行:
.\build.cmd -s clr -c DebugLinux / macOS 上执行:
./build.sh -s clr -c Debug说明:
-c Debug实际上是默认配置,省略该参数效果相同,文档中保留它只是为了语义清晰。由于当前仓库采用统一的构建脚本体系(根目录下的 build.cmd 与 build.sh,以及定义子集组合的 Subsets.props),这套命令在三种平台上的语义是一致的。
单独重建 System.Private.CoreLib
如果因为某种原因System.Private.CoreLib.dll缺失,不需要重跑整个构建,可以只重建 CoreLib 相关的两个子集:
.\build.cmd -s clr.corelib+clr.nativecorelib -c Debug./build.sh -s clr.corelib+clr.nativecorelib -c Debug重要注意:当使用CORE_LIBRARIES环境变量进行调试时,libs子集(托管库,如System.Runtime等)也必须在开始调试之前完成构建,否则运行真实应用程序时会因缺少程序集而失败。
在 Windows 上调试 CoreCLR
使用 Visual Studio(推荐方式)
Visual Studio 作为完整 IDE,能显著降低运行时调试的门槛。官方步骤分为两步:
0. 准备原生构建前置工具(只需一次):
.\build.cmd clr.nativeprereqs -a <architecture> -c <configuration>这会构建原生编译所需的部分工具,只要不清理artifacts目录,此步骤只执行一次即可。
1. 打开 CoreCLR 解决方案(coreclr.slnx),有两种方法:
- 方法一(推荐):用构建脚本直接生成并启动解决方案:
.\build.cmd -vs coreclr.slnx -a <architecture> -c <configuration>默认架构与配置为
x64 Debug。 - 方法二(手动):先以
-msbuild标志构建仓库,然后在 Visual Studio 中打开:path\to\runtime\artifacts\obj\coreclr\windows.<architecture>.<configuration>\ide\CoreCLR.slnx
2. 配置 INSTALL 项目为启动项并设置调试参数:
右键INSTALL项目,选择
Set as StartUp Project。打开 INSTALL 项目的属性页。
在左侧树中选择
Configuration Properties -> Debugging。设置
Command=$(SolutionDir)\..\..\..\..\bin\coreclr\windows.$(Platform).$(Configuration)\corerun.exe——指向构建好的运行时二进制目录。设置
Command Arguments=<managed app you wish to run>(例如HelloWorld.dll)。设置
Working Directory=$(SolutionDir)\..\..\..\..\bin\coreclr\windows.$(Platform).$(Configuration)——包含 CoreCLR 二进制文件的目录。设置
Environment=CORE_LIBRARIES=$(SolutionDir)\..\..\..\..\bin\runtime\<target-framework>-windows-$(Configuration)-$(Platform),其中<target-framework>是当前分支的目标框架(当前仓库为net11.0)。此变量的作用是:- 指向除
System.Private.CoreLib之外的核心库所在目录; - 如果只调试仅引用
System.Private.CoreLib的 CLR 测试,此步可跳过;但要调试引用任何其他程序集(包括System.Runtime)的真实应用,则必须配置。
- 指向除
右键INSTALL项目并选择Build,让 Visual Studio 从 CMake 中加载必要信息。
按F11从
corerun的wmain开始单步调试,或在源码中设置断点后按F5。例如,在ceemain.cpp中的EEStartup()函数上设断点,即可在 CoreCLR 启动时中断进入。
步骤 1-9 只需做一次(只要仓库中的 CMake 文件没有变化),之后每次调试直接重复步骤 10 即可。官方建议使用 Visual Studio 2022 或 Visual Studio 2019。
从源码看,EEStartup()是运行时的核心启动函数,定义于 src/coreclr/vm/ceemain.cpp#L1129,它在PAL_TRY保护下调用EEStartupHelper()完成运行时初始化,是观察整个 CoreCLR 引导流程的最稳定切入点。
使用 Visual Studio 的 Open Folder + CMake 模式
- 用open folder功能打开 dotnet/runtime 仓库根目录,Visual Studio 会提示查找 CMake 文件,选择
src\coreclr\CMakeLists.txt作为 CMake 工作区。 - 将
corerun设为启动项目:在文件夹视图(而非 CMake targets 视图)下,右键coreclr\hosts\corerun\CMakeLists.txt设为启动项目,或从调试目标下拉框选择。 - 右键
corerun项目打开Debug配置(也可通过主菜单Debug -> Debug and Launch Configuration)。 - 在打开的
launch.vs.json中为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 net11.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" ] }注意:对于 Visual Studio 17.3,更改已启动可执行文件的位置不起作用,因此必须设置
CORE_ROOT。
- 在文件夹视图中右键 CoreCLR 项目或
coreclr\CMakeLists.txt,执行Install命令。 - 按F10或F11从 main 开始调试,或设置断点后按F5。
每次修改 CoreCLR 源码后,记得重新执行Install命令,让修改生效到安装位置。
使用 Visual Studio 调试 CLI 构建的产物
Visual Studio 同样可以调试由命令行脚本构建的运行时:
- 按构建说明至少构建
clr与libs子集。示例:.\build.cmd -subset clr+libs -configuration Release -runtimeConfiguration Debug - 按测试说明构建 Core_Root,这会生成作为托管代码入口的
corerun.exe,它与所需 dll 一起被放置在 core root 目录内。示例:.\src\tests\build.cmd generatelayoutonly - 启动 Visual Studio 并选择打开现有项目/解决方案,选中
artifacts\tests\coreclr\<OS>.<arch>.<configuration>\Tests\Core_Root\corerun.exe作为项目;或直接用命令行devenv /debugexe artifacts\tests\coreclr\<OS>.<arch>.<configuration>\Tests\Core_Root\corerun.exe启动。 - 在解决方案资源管理器中右键
corerun,选择属性:将Debugger Type设为Native Only,Arguments填要调试的托管应用路径。 - 如需设置断点,可右键解决方案选择Add -> Existing Item添加运行时源文件。
- 设置断点后用F5运行开始调试。
.slnx文件可以保存,其中记录了corerun.exe路径、包含的文件和调试设置;只要路径不变,可重复复用。
使用 Visual Studio Code
文档说明 VS Code 的对应操作步骤"即将推出"(Visual Studio Code instructions coming soon)。就目前而言,在 Windows 上调试 CoreCLR 原生代码优先选择 Visual Studio 或 Windbg/Cdb;VS Code 更常用于托管代码调试,详见下文"使用 Visual Studio Code 调试托管代码"。
在 Windows 上使用 SOS 与 Windbg / Cdb
正常情况下列表 SOS 会随 Windbg 一起分发,无需额外安装。但如果你的 Windbg 中没有 SOS、需要使用其他版本,或出于任何原因需要单独/额外安装,可参考:
- 微软官方 dotnet-sos 文档;
- diagnostics 仓库的 installing-sos-windows-instructions.md。
SOS 命令的详细参考见 diagnostics 仓库的 sos-debugging-extension-windows.md。
说明:SOS(Sonic Of Stacks,运行时调试扩展)目前不再随本仓库分发,其代码与安装/使用文档已迁移至独立的 dotnet/diagnostics 仓库,请在其新家获取。
在 Linux 和 macOS 上调试 CoreCLR
与 Windows 类似,Linux/macOS 也需要至少构建clr子集(最好为 Debug 配置):
./build.sh -s clr -c DebugSystem.Private.CoreLib.dll缺失时同样可以单独重建:
./build.sh -s clr.corelib+clr.nativecorelib -c Debug同样地,使用CORE_LIBRARIES调试时须先构建libs子集。
Unix 上的 SOS
与 Windows 的 Windbg 不同,Linux/macOS 需要自行安装 SOS,安装说明见:
- 微软官方 dotnet-sos 文档;
- diagnostics 仓库的 installing-sos-instructions.md。
如果你的场景需要 SOS 的最新改动,或你正在使用官方未正式支持但实际上可用的环境(最常见的是macOS Arm64),则需要从 diagnostics 仓库自行构建 SOS,然后将其加载到lldb中,具体见下一节。
使用 lldb 调试 CoreCLR
注意:只有
lldb被支持与 SOS 配合使用。你也可以使用gdb、cgdb或其他调试器,但可能无法使用 SOS。
- 先构建 runtime 仓库的
clr子集。 - 启动 lldb,传入
corerun、要运行的应用(如HelloWorld.dll)以及该应用需要的任何参数:lldb -- /path/to/corerun /path/to/app.dll <app args go here> - 如果使用已安装版本的 SOS,可跳过此步;如果是手动构建的 SOS,则需在调试会话开始前加载它:
plugin load /path/to/built/sos/libsosplugin.so注意
.so用于 Linux,macOS 使用.dylib。更多信息见 diagnostics 仓库的 using-sos-private-build.md。 - 启动程序:
process launch -s - 停止在运行时使用的
SIGUSR1信号上中断:process handle -s false SIGUSR1 - 在 CoreCLR 初始化处设置断点——这是开始调试最稳定的点:
breakpoint set -n coreclr_execute_assembly - 设置断点后执行
process continue运行到该点。 - 现在即可开始调试会话:设置断点,或运行 SOS 命令如
clrstack、sos VerifyHeap。注意SOS 命令名区分大小写。
关于第 6 步的断点符号:coreclr_execute_assembly是 CoreCLR 宿主 API 的导出符号,由 src/coreclr/hosts/corerun/corerun.cpp 通过try_get_export(coreclr_mod, "coreclr_execute_assembly", ...)从运行时库解析并调用(该文件还解析了coreclr_initialize、coreclr_shutdown_2等导出符号)。因此在此符号上设断点,恰好命中的是"宿主把托管程序集交给运行时执行"的时刻,是观察托管代码进入执行阶段的最佳位置。而corerun宿主本身则通过读取CORE_ROOT、CORE_LIBRARIES等环境变量来定位运行时二进制与核心库目录(见 src/coreclr/hosts/corerun/corerun.cpp 中相关代码),这也是为什么文档中反复强调这两个变量配置的重要性。
禁用托管附加/调试
环境变量DOTNET_EnableDiagnostics可用于禁用托管调试,这可以阻止运行时创建用于调试的各种操作系统工件(如 Linux/macOS 上的命名管道和信号量):
export DOTNET_EnableDiagnostics=0使用 lldb 调试 core dump
关于 core dump 的调试,diagnostics 仓库提供了非常详尽的指南:debugging-coredump.md。
调试 AOT 编译器
AOT 编译器的调试方式见其专属文档 debugging-aot-compilers.md。本文档只提供指引入口,不展开具体步骤。
调试托管代码(Managed Code)
CoreCLR 并不只有原生 C++ 代码,如今有大量值得调试的内容位于更上层的 C# 托管代码层面。
使用 Visual Studio Code 调试托管代码
- 安装 C# 扩展。
- 在 VS Code 中打开包含要调试源码的文件夹。
- 打开调试窗口:
ctrl-shift-D/cmd-shift-D,或点击左侧的调试按钮。 - 点击顶部的齿轮按钮创建 launch 配置,并在下拉列表中选择
.NET 5+ and .NET Core。 - 它会生成一个
launch.json文件,可在其中配置调试目标与方式。基础模板如下:
{ "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. } ] }- 设置断点并启动调试器,即可检查变量与调用堆栈。
使用 Visual Studio 调试托管代码
- 使用File -> Open Project(注意不是 open file),选择要用作宿主(host)的二进制文件(通常是
dotnet.exe或corerun.exe)。 - 打开刚创建项目的属性,设置以下项:
- Arguments:与命令行中使用的参数一致。例如命令行运行
dotnet.exe exec Foo.dll,则设置arguments = "exec Foo.dll"。注意:务必使用dotnet exec而不是dotnet run,因为 run 动词会以子进程方式启动应用,调试器无法附加到子进程。 - Working Directory:与命令行中使用的工作目录一致。
- Debugger Type:设为
Managed (.NET Core, .NET 5+);若要调试原生 C++ 代码,则选择Native Only。 - Environment:添加命令行中的环境变量。可考虑添加
DOTNET_ReadyToRun=0,它禁用 R2R 预编译并让 JIT 生成可调试代码,从而在运行时框架程序集内部获得更高质量的 C# 调试体验,代价是应用性能有所下降。
- Arguments:与命令行中使用的参数一致。例如命令行运行
- 托管调试时,Debug -> Options的Debugging -> General中还有几个有用的设置:
- 取消勾选Just My Code:允许调试进入框架库。
- 勾选Enable .NET Framework Source Stepping:让调试器自动为运行时框架二进制下载符号与源码。如果是自己构建的框架,可跳过此步。
- 勾选Suppress JIT optimization on module load:让调试器指示 .NET 运行时 JIT 为即使未以 C# 编译器 Debug 配置编译的模块生成可调试代码。该代码运行较慢,但能提供更高保真度的断点、单步执行与局部变量访问——这与调试 .NET 应用时 Debug 项目配置与 Release 项目配置的差异是一样的。
解决 Visual Studio 中的签名验证错误
Visual Studio 2022 17.5 及更高版本会在加载前验证随 .NET Runtime 提供的调试库是否已签名。若未签名,Visual Studio 会显示类似如下错误:
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.
该错误会在目标进程使用每日构建版或自行构建的 .NET Runtime 时出现。注意:使用微软官方发布的 .NET Runtime 版本时绝不会出现此错误;使用官方版本时不要禁用签名验证。
以下方法可配置 Visual Studio 禁用签名验证:
VSDebugger_ValidateDotnetDebugLibSignatures环境变量(最简单且推荐用于临时禁用):- 在命令行执行
set VSDebugger_ValidateDotnetDebugLibSignatures=0,然后从同一个命令提示符启动 Visual Studio(devenv.exe)。 - 该设置仅对从设置该变量的命令提示符启动的那个 Visual Studio 实例生效。
- 在命令行执行
DOTNET_ROOT环境变量:如果从设置了DOTNET_ROOT的命令提示符启动 Visual Studio,它会忽略位于DOTNET_ROOT目录下的未签名 .NET 运行时调试库。- (不推荐)
ValidateDotnetDebugLibSignatures注册表键:若需更持久地禁用,可设置 VS 注册表键Common7\IDE\VsRegEdit.exe set local HKCU Debugger\EngineSwitches ValidateDotnetDebugLibSignatures dword 0。例如打开开发者命令提示符执行:Common7\IDE\VsRegEdit.exe set local HKCU Debugger\EngineSwitches ValidateDotnetDebugLibSignatures dword 0
调试实践小结
综合以上流程,一套完整的 CoreCLR 调试工作流可归纳为四个阶段:
- 构建:至少以 Debug 配置构建
clr子集(.\build.cmd -s clr -c Debug或./build.sh -s clr -c Debug);使用CORE_LIBRARIES时还需构建libs子集。 - 获取宿主与符号:通过
corerun作为宿主进程(其源码见 src/coreclr/hosts/corerun/corerun.cpp),确认CORE_ROOT、CORE_LIBRARIES指向正确的产物目录;在 Linux/macOS 上提前安装或构建 SOS 插件。 - 接入调试器:Windows 上选择 Visual Studio(INSTALL 项目 +
EEStartup断点)、Open Folder 模式或 Windbg/Cdb;Linux/macOS 上使用 lldb 并在coreclr_execute_assembly上设断点,运行process handle -s false SIGUSR1屏蔽信号干扰。 - 托管层面:对框架库内部或应用自身的 C# 代码,使用 VS Code 的
launch.json(type 为coreclr)或 Visual Studio 的 Managed 调试器,配合DOTNET_ReadyToRun=0、关闭 Just My Code 等设置获得最高调试保真度。
掌握这条链路后,无论是追踪运行时启动逻辑、排查 GC/JIT 行为,还是深入框架库实现细节,都能在源码级获得完整的可观测性。
- 语言运行时
- 标准库
- JIT编译
- 编译器
【免费下载链接】runtime
.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
相关推荐
.NET Runtime调试全攻略:从CoreCLR到生产环境故障排查
.NET Runtime调试全攻略:从CoreCLR到生产环境故障排查 你是否曾在调试.NET应用时遭遇"无法命中断点"或"调用栈不完整"的困境?作为跨平台运行
语言运行时标准库JIT编译编译器.NET Runtime 交叉构建实战:在 Windows、macOS、Linux 与 Docker 中构建不同架构的 CoreCLR
.NET Runtime 交叉构建实战:在 Windows、macOS、Linux 与 Docker 中构建不同架构的 CoreCLR 本文为 dotnet/r
语言运行时标准库JIT编译编译器.NET Runtime 在 Linux 上交叉编译原生库与托管库的完整指南
.NET Runtime 在 Linux 上交叉编译原生库与托管库的完整指南 本篇基于 .NET Runtime 仓库的交叉编译文档,讲解如何在 Linux 主
语言运行时标准库JIT编译编译器
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考