.NET CoreCLR AOT 编译器调试完全指南:Crossgen2 与 ILC 的调试技巧、单方法复现与依赖图分析 📅 发布时间:2026/9/20 3:08:36 👁 浏览次数: .NET CoreCLR AOT 编译器调试完全指南Crossgen2 与 ILC 的调试技巧、单方法复现与依赖图分析【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtimeCoreCLR 自带两套围绕同一份 C# 代码库构建的 AOT 编译器——crossgen2 与 ILC。前者生成可被基于 JIT 的 CoreCLR 运行时加载的 ReadyToRunR2R映像后者生成平台相关的目标文件Windows 上为 COFF、Linux 上为 ELF、macOS 上为 Mach-O可与 NativeAOT 形态的 CoreCLR 运行时链接成自包含可执行文件或共享库。调试这两套托管编译器与调试普通运行时组件有着显著不同的挑战与技巧。本文将结合 runtime 仓库源码系统讲解托管 AOT 编译器的调试要点、内置调试辅助开关、单方法复现技术、跨目标编译能力以及依赖图的可视化分析方法帮助你快速定位 JIT 与编译器层面的问题。AOT 编译器全景基于共享代码库的两套编译器在 runtime 仓库中crossgen2 与 ILC 共享大量 C# 代码二者核心的区别在于输出形态crossgen2生成 ReadyToRun 映像运行时仍可回退到 JIT 编译适合提升启动速度与减少 JIT 开销ILC编译整个程序与 crossgen2 的 composite 模式大致对应直接产出目标平台的目标文件形成无需额外运行时组件的 NativeAOT 可执行文件。两套编译器的入口与命令行解析位于 src/coreclr/tools/aot/crossgen2/crossgen2 的Program.cs、Crossgen2RootCommand.csILC 相关实现则分布在 src/coreclr/tools/aot/ILCompiler.Compiler/ 等工程中。共同的基础依赖分析框架位于 src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/。调试托管 AOT 编译器前必须知道的五个关键事实与调试 CoreCLR 原生组件不同调试这些托管编译器时首先需要建立正确的心理模型除 JIT 之外AOT 编译器本身是托管应用。因此你既可以进行原生调试也可以进行托管调试managed/mixed mode。默认采用多核编译策略。编译器默认会利用多核并行编译这在调试单方法问题时会造成输出交错。进程内同时存在两份 JIT一份用于编译目标代码另一份用于编译编译器自身。这会让原生调试器在符号匹配时停在意外位置。编译器不解析环境变量来控制 JIT或其他行为所有行为均通过命令行控制。这与运行时不同意味着复现问题必须完整保留命令行。项目系统生成的 AOT 编译器命令行相当复杂尤其是引用路径部分通常需要借助响应文件.rsp或通配符来简化手工操作。源码层面crossgen2 的命令行选项在 Crossgen2RootCommand.cs 中集中声明所有行为确实只来源于命令行参数解析结果没有任何环境变量分支。内置调试辅助从并行度到单方法复现关闭并行--parallelism 1调试多线程相关组件但并非排查多线程问题本身时建议将进程的最大并行度限制为 1crossgen2 ... --parallelism 1在源码中--parallelism选项通过MakeParallelism自定义解析器处理见 Crossgen2RootCommand.cs默认并行度上限为 24Math.Min(24, Environment.ProcessorCount)因为编译过程中大量环节是单线程的过高的并行度只会增加内存消耗而在 32 位平台上虚拟地址空间受限并行度会被进一步限制到最多 4。单方法编译--singlemethod*选项族当需要调试单个方法的编译行为时可以指示编译器只编译一个方法。这一组选项通过“类型 方法名 泛型参数 索引”来唯一定位方法类型采用托管Type.GetType(string)相同的格式描述可参考 .NET 官方文档由于该格式可能非常冗长编译器提供--print-repro-instructions开关直接在控制台打印编译某个函数所需的参数。--singlemethodindex用于方法签名是唯一区分因素的情况——因为精确描述一个签名极其复杂所以用索引替代一系列描述性参数。典型复现参数形如--singlemethodtypename Internal.Runtime.CompilerServices.Unsafe --singlemethodname As --singlemethodindex 2 --singlemethodgenericarg System.Runtime.Intrinsics.Vector2561[[System.SByte]] --singlemethodgenericarg System.Runtime.Intrinsics.Vector2561[[System.Double]]源码佐证--print-repro-instructions的开启会设置nodeFactoryFlags.PrintReproArgs见 Program.cs并通过CreateReproArgumentString生成上述参数Program.cs。该方法内部正是按“类型名 → 方法名 → 同名方法索引 → 泛型实参”的顺序拼接参数--singlemethodindex的定位逻辑位于 Program.cs当同名方法不止一个且未提供索引时会明确报错提示SingleMethodIndexNeeded。此外在 DEBUG 构建下代码生成失败CodeGenerationFailedException时编译器会自动打印复现参数DumpReproArguments见 Crossgen2RootCommand.cs。使用 Debug 版 JIT由于编译器默认多线程即便使用Debug变体的 JIT 编译产出也相当快。因此调试 JIT 问题时无论问题最初出现在哪个环境都建议使用DebugJIT以换取更丰富的内部状态信息。跨目标编译--targetos与--targetarch编译器支持几乎任意的跨目标OS 与架构编译唯一限制是32 位架构不能编译面向 64 位架构的目标。这让你可以在最顺手的调试环境中工作——例如当问题跨越托管/原生边界时Windows x64 上的混合模式调试器mixed mode debugger往往是最方便的。只要传入正确的程序集集合与命令行参数编译器在所有平台上应产出二进制一致binary identical的输出。编译器不会校验输入程序集的 OS/架构因此可以用非匹配版本的框架编译任意目标——虽然不一定能用于诊断所有问题但可以廉价地观察一个改动在所有受支持架构上的大致行为。默认目标是编译器自身的 OS/架构组合所有 64 位版本的编译器都能面向任意 OS/架构组合。当前受支持的合法参数组合如下命令行参数--targetos windows --targetarch x86--targetos windows --targetarch x64--targetos windows --targetarch arm--targetos windows --targetarch arm64--targetos linux --targetarch x64--targetos linux --targetarch arm--targetos linux --targetarch arm64--targetos osx --targetarch x64--targetos osx --targetarch arm64源码中--targetos与--targetarch的声明以及合法取值Helpers.ValidOS、Helpers.ValidArchitectures可参见 Crossgen2RootCommand.cs 与扩展帮助输出Crossgen2RootCommand.cs。传递 JIT 行为开关--codegenopt向编译器传递特殊 JIT 行为标志需通过--codegenopt开关。例如同时开启 tailcall 循环优化并 dump 所有编译代码--codegenopt JitDump* --codegenopt TailCallLoopOpt1注意使用 JIT 的 JitDump 功能时必须像前文所述禁用并行度或指定单方法编译否则多函数的输出会交错混杂、难以阅读。指定 JIT--jitpath与 JIT 命名约定编译器通过命名约定识别要使用的 JIT默认使用位于 crossgen2.dll 同目录下的 JIT。此外支持--jitpath开关指定特定 JIT该选项主要为 JIT 团队的 A/B 测试设计仅在 JIT 接口jit interface未改变时才应使用--jitpath--jitpath指定的 JIT 必须与当前--targetos、--targetarch设置兼容。双 JIT 进程带来的调试干扰由于进程内存在两份 JIT当源码文件匹配时原生调试器很可能在不幸且意外的地方停下来。对策有两种优先使用与编译器所用运行时不完全匹配的运行时若不可行可在多数原生调试器中禁用符号加载例如 Visual Studio 中通过 Specify excluded modules指定排除模块功能实现。检查产物r2rdump 与 map 文件与 crossgen2 项目并行的有一个名为r2rdump的工具用于 dump ReadyToRun 映像的内容以检查最终二进制中实际产出了什么。它拥有大量选项控制 dump 的粒度能够以人类可读的方式展示 crossgen2 产出的任意映像指定--disasm可显示反汇编。从源码可见r2rdump 的命令行选项R2RDumpRootCommand.cs包括--in/-i输入的 ReadyToRun 映像文件--out/-o输出文件路径--rawdump 各 section 或 runtime function 的原始字节--headerdump R2R 头部--disasm/-d显示方法或 runtime function 的反汇编--query/-q按精确名称、签名、行 ID 或 token 查询方法--keyword/-k按关键字搜索方法--runtimefunction/-f按 id 或相对虚拟地址获取单个 runtime function--section/-s按关键字获取 section--unwinddump unwindInfo--gcdump gcInfo 与 slot 表--pgodump 内嵌的 PGO 插桩数据--entrypoints/-edump 文件中方法/实例入口点列表--normalize/-n规范化排序各表与方法默认为文件顺序--verbose一并 dump 反汇编、unwindInfo、gcInfo 与 section 内容--diff比较两个 R2R 映像--create-pdb与--create-perfmap创建 PDB / PerfMap 文件。此外crossgen2 支持--map与--mapcsv参数生成产物 map 文件主要用于诊断体积问题它们以较高细节描述生成的文件并提供大量关于产物的统计信息。ILC 也支持--map但由于输出格式不同其 map 文件格式与 crossgen2 不同。定位失败原因--verbose与--waitfordebugger诊断 crossgen2 中某个具体方法为何编译失败可传递--verbose开关——它会打印大量信息尤其是当编译因 R2R 格式限制被中止时的具体原因。调试场景下还有一个值得注意的开关--waitfordebugger声明于 Crossgen2RootCommand.cs在 Program.cs 中被消费启动后等待调试器附加便于从编译一开始就介入观察。运行环境与测试床dotnet、corerun 与 RunCrossgen2编译器既可以使用构建产品所用的 dotnet 版本即 runtime 仓库根目录的 dotnet.cmd / dotnet.sh 脚本找到的版本也可以使用测试构建产生的足够新的 corerun.exe。若使用 corerun强烈建议使用 Release 构建因为 crossgen2 运行着大量托管代码。corerun 的版本不必与正在调试的 crossgen2.dll/ilc.dll 来自同一构建——实际上建议使用另一份 enlistment 构建该 corerun以免混淆。在 runtime 测试床中可通过环境变量命令每个测试用 crossgen2 编译设置RunCrossgen2为 1可选设置CompositeBuildMode为 1以观察 R2R 复合映像composite image创建行为。默认情况下测试床直接用dotnet运行托管编译器。若在 Windows 上从 enlistment 根目录运行测试批处理脚本这通常开箱即用否则必须将__TestDotNetCmd环境变量指向可运行编译器的dotnet或corerun。对熟悉 CoreCLR 测试床的开发者而言这通常是运行 AOT 编译器简单测试的最便捷方式。构建相关子集与产物位置构建 crossgen2 必须构建clr.tools子集若重编译了 JIT 的某部分并希望在内循环中使用还需以clr.jit或clr.alljits子集构建若 JIT 接口发生改变还必须重建clr.runtime子集。产品构建完成后可用的 crossgen2.dll 会位于类似bin\coreclr\windows.x64.Debug\crossgen2的 bin 目录。执行类似src\tests\build generatelayoutonly的命令创建测试原生布局后%CORE_ROOT%\crossgen2目录中会有一份 crossgen2——该版本包含在 x64 dotnet.exe 或目标架构下运行所需的恰当文件这在一定程度上简化了跨平台开发假定主开发机为 x64。调试 NativeAOT 产物原生调试器直接可用ILC 编译器生成的目标文件包含方法体与类型的调试信息格式为平台特定格式Windows 上为 CodeView其他平台为 DWARF并包含平台格式的展开unwinding信息。因此NativeAOT 可执行文件可以用平台调试器VS、WinDbg、GDB、LLDB直接调试无需任何 SOS 类扩展也可以使用面向原生代码的反汇编器与工具dumpbin、Ghidra、IDA检查。注意编译时需要传-g命令行参数以启用调试信息生成。ILC 通常编译整个程序大致对应 crossgen2 的 composite 模式还存在多文件模式——每个托管程序集对应单个目标文件——但该模式不随产品发布。ILC 支持的目标文件格式为 PE/ELF/Mach-O。实战示例在 Crossgen2 中调试一个测试应用下面以 CoreCLR 测试床中的一个简单测试为例演示完整的调试流程。示例假定CORE_ROOT已正确设置且__TestDotNetCmd已指向可用的dotnet或corerun。第一步让测试用 crossgen2 编译设置RunCrossgen21测试批处理脚本会调用 crossgen2 处理输入二进制。注意它同时会创建输入二进制的一份副本若你修改了测试需要删除测试二进制所在目录并重新构建测试。C:\git2\runtimeset RunCrossgen21 C:\git2\runtimec:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\Complex1.cmd BEGIN EXECUTION Complex1.dll 1 file(s) copied. Could Not Find c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\Complex1.dll.rsp Response file: c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\Complex1.dll -o:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll --targetarch:x64 --verify-type-and-field-layout -O -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\System.*.dll -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\Microsoft.*.dll -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\mscorlib.dll -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\netstandard.dll dotnet c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\crossgen2\crossgen2.dll c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\*.dll Emitting R2R PE file: c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\corerun.exe Complex1.dll Starting... Everything Worked! Expected: 100 Actual: 100 END EXECUTION - PASSED PASSED从该输出可以看到crossgen2 通过一个包含全部引用细节参数的响应文件.rsp启动。你可以手动运行实际的 crossgen2 命令——命令前缀正是__TestDotNetCmd环境变量的值。例如看到上述输出后直接复制最后一条命令并运行C:\git2\runtimedotnet c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\crossgen2\crossgen2.dll c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\*.dll C:\git2\runtime\.dotnet Emitting R2R PE file: c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll第二步单方法复现想要调试单个方法的编译时加上--print-repro-instructions开关C:\git2\runtimedotnet c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\crossgen2\crossgen2.dll c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\*.dll --print-repro-instructions C:\git2\runtime\.dotnet Single method repro args:--singlemethodtypename Complex,Complex1 --singlemethodname mul_em --singlemethodindex 1 Single method repro args:--singlemethodtypename Complex_Array_Test,Complex1 --singlemethodname .ctor --singlemethodindex 1 Single method repro args:--singlemethodtypename Complex_Array_Test,Complex1 --singlemethodname Main --singlemethodindex 1 Emitting R2R PE file: c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll可以看到每个方法的完整复现参数都被打印到控制台。第三步查看 JIT 细节输出想看 JIT 的更多细节时可以追加--codegenopt开关。这里为了保持示例精简只使用JitOrder1JIT 开发者通常更会使用JitDump*C:\git2\runtimedotnet c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\crossgen2\crossgen2.dll c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\*.dll --print-repro-instructions --singlemethodtypename Complex_Array_Test,Complex1 --singlemethodname Main --singlemethodindex 1 --codegenopt JitOrder1 C:\git2\runtime\.dotnet Single method repro args:--singlemethodtypename Complex_Array_Test,Complex1 --singlemethodname Main --singlemethodindex 1 | Profiled | Method | Method has | calls | Num |LclV |AProp| CSE | Perf |bytes | x64 codesize| mdToken | CNT | RGN | Hash | EH | FRM | LOOP | NRM | IND | BBs | Cnt | Cnt | Cnt | Score | IL | HOT | CLD | method name ------------------------------------------------------------------------------------------------------- 06000002 | | | f656934b | | rsp | LOOP | 3 | 0 | 35 | 56 | 48 | 7 | 43056 | 490 | 761 | 0 | Complex_Array_Test:Main(System.String[]):int Emitting R2R PE file: c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll第四步跨架构探索由于最后一个--targetarch与--targetos开关生效只需追加--targetarch arm64即可临时切换目标架构做探索C:\git2\runtimedotnet c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\crossgen2\crossgen2.dll c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\*.dll --print-repro-instructions --singlemethodtypename Complex_Array_Test,Complex1 --singlemethodname Main --singlemethodindex 1 --codegenopt JitOrder1 --targetarch arm64 C:\git2\runtime\.dotnet Single method repro args:--singlemethodtypename Complex_Array_Test,Complex1 --singlemethodname Main --singlemethodindex 1 | Profiled | Method | Method has | calls | Num |LclV |AProp| CSE | Perf |bytes | arm64 codesize| mdToken | CNT | RGN | Hash | EH | FRM | LOOP | NRM | IND | BBs | Cnt | Cnt | Cnt | Score | IL | HOT | CLD | method name ------------------------------------------------------------------------------------------------------- 06000002 | | | f656934b | | fp | LOOP | 3 | 0 | 35 | 59 | 48 | 10 | 63828 | 490 | 1048 | 0 | Complex_Array_Test:Main(System.String[]):int Emitting R2R PE file: c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll注意命令行唯一的变化只是追加了--targetarch arm64JIT 随即以 arm64 方式编译该方法——这正是前文“跨目标编译”能力的直接体现。第五步附加调试器由于示例用dotnet作为__TestDotNetCmd你需要调试c:\git2\runtime\.dotnet\dotnet.exe进程devenv /debugexe C:\git2\runtime\.dotnet\dotnet.exe c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\crossgen2\crossgen2.dll c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\*.dll --print-repro-instructions --singlemethodtypename Complex_Array_Test,Complex1 --singlemethodname Main --singlemethodindex 1 --codegenopt JitOrder1 --targetarch arm64这会启动 Visual Studio 调试器并建立一个用于调试 dotnet.exe 进程的解决方案。默认情况下该解决方案只调试进程的原生代码若要调试托管组件请编辑解决方案属性将Debugger Type调试器类型设为Managed (.NET Core, .NET 5)或Mixed (.NET Core, .NET 5)。调试编译依赖图DependencyGraphViewer 与 DGMLAOT 编译由一张依赖图dependency graph驱动。若要排查依赖图本身的问题某个节点为何被生成或未被生成可参考 调试编译器依赖分析指南。核心思路是先识别图中缺失或多余出现的节点再调整依赖分析逻辑。主要的分析手段有三种DependencyGraphViewer 工具Windows 上可用位于 src/coreclr/tools/aot/DependencyGraphViewer。其 README 说明它支持通过 ETW 日志NativeAOT 场景需以管理员权限运行以采集 ETW 事件或 DGML 文件查看依赖图。这是唯一能在调试编译器的同时方便地观察图的方式基于 WinForms目前仅限 Windows。依赖图不支持多个日志设施同时开启因此不要同时设置IlcGenerateDgmlFile或开启 DGML 生成。生成 DGML 文件编译器通过命令行开关生成依赖图 DGML 文件与查看器工具展示的是同一份数据但为文本 XML 格式。生成方式NativeAOT在项目文件中设置IlcGenerateDgmlFiletrue/IlcGenerateDgmlFile并配合PublishAottrue/PublishAotDGML 生成在obj目录通常为obj\Configuration\Target_Framework\RID\native\可用dir /s *.dgml.xml定位R2Rcrossgen2在命令行加--dgmllog对应 Crossgen2RootCommand.cs 中的--dgmllog选项保存依赖分析结果IL Linker使用--dump-dependencies --dependencies-file-format dgml。插桩编译器的依赖分析在查看器无法提供足够信息、需要弄清楚图为何如此构成时使用。依赖图的基础设施位于 ILCompiler.DependencyAnalysisFrameworkDependencyNodeCore定义了节点的抽象每个节点通过GetName提供名称见 DependencyNodeCore.csDgmlWriter负责将图写入 DGMLDgmlWriter.cs。在 ILC 侧编译完成时通过DgmlWriter.WriteDependencyGraphToStream输出图见 Compilation.cs。图中的每个节点都有唯一 id可被查看器工具识别并能与调试器并行使用以理解编译过程中正在发生什么。原文档还提到依赖图查看器的 UI 相当简陋欢迎提交改进——这从 DependencyGraphViewer 下的 WinForms 源码结构DependencyGraphsForm、NodeForm、SingleDependencyGraphForm等可以得到印证。总结AOT 编译器调试的核心工作流调试 CoreCLR AOT 编译器时建议遵循以下工作流复现通过测试床RunCrossgen21或手工命令行善用响应文件与通配符引用稳定复现问题必要时在命令行前加--waitfordebugger从启动即介入。收窄用--print-repro-instructions拿到单方法复现参数配合--singlemethod*只编译目标方法同时用--parallelism 1关闭并行避免输出交错。诊断用--codegenopt传递 JIT 调试开关如JitDump*、JitOrder1用--verbose定位 R2R 格式限制导致的编译中止原因用--targetos/--targetarch跨架构探索用--jitpath做 JIT A/B 测试。验证产物用 r2rdump--disasm等检查 R2R 映像实际内容用--map/--mapcsv分析体积与布局NativeAOT 产物则直接用平台调试器与反汇编工具检查。深入依赖图需要弄清节点为何存在时用 DependencyGraphViewer 或 DGML 文件分析依赖图必要时插桩依赖分析逻辑。掌握这些技巧后无论是 JIT 行为异常、R2R 格式限制还是 NativeAOT 产物问题你都能以最短路径定位并验证修复。【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考