未验证 提交 f44b0ba1 编写于 作者: M Michal Strehovský 提交者: GitHub

Add docs around NativeAOT workflows (#62837)

上级 0c5e9e56
此差异已折叠。
digraph "SimpleDependencyGraph" {
ordering=out;
rankdir=LR;
node [shape=box];
Code_Program_Main[label="Program::Main"];
Code_Bar__ctor[label="Bar::.ctor"];
Code_Bar_VirtualMethod[label="Bar::VirtualMethod"];
Code_Bar_UnusedVirtualMethod[label="Bar::UnusedVirtualMethod"];
Code_Foo_UnusedVirtualMethod[label="Foo::UnusedVirtualMethod"];
Code_Foo__ctor[label="Foo::.ctor"];
Code_Object__ctor[label="Object::.ctor"];
node [shape=ellipse];
Type_Bar[label="Bar"];
Type_Foo[label="Foo"];
Type_Object[label="Object"];
node [shape=ellipse, style=dotted]
Type_Baz[label="Baz"]
node [shape=box, style=dashed];
Virtual_Foo_VirtualMethod[label="Foo::VirtualMethod"];
Code_Program_Main -> Code_Bar__ctor;
Code_Program_Main -> Type_Bar;
Code_Program_Main -> Virtual_Foo_VirtualMethod;
Code_Program_Main -> Type_Baz;
Type_Baz -> Type_Foo;
Type_Bar -> Type_Foo;
Type_Foo -> Type_Object;
Type_Bar -> Code_Bar_VirtualMethod[label="Foo::VirtualMethod is used", style=dashed];
Type_Bar -> Code_Bar_UnusedVirtualMethod[label="Foo::UnusedVirtualMethod is used", style=dashed];
Type_Foo -> Code_Foo_UnusedVirtualMethod[label="Foo::UnusedVirtualMethod is used", style=dashed];
Code_Bar__ctor -> Code_Foo__ctor;
Code_Foo__ctor -> Code_Object__ctor;
overlap=false;
fontsize=12;
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
-->
<!-- Title: SimpleDependencyGraph Pages: 1 -->
<svg width="1067pt" height="236pt"
viewBox="0.00 0.00 1067.00 236.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 232)">
<title>SimpleDependencyGraph</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-232 1063,-232 1063,4 -4,4"/>
<!-- Code_Program_Main -->
<g id="node1" class="node"><title>Code_Program_Main</title>
<polygon fill="none" stroke="black" points="98,-109 0,-109 0,-73 98,-73 98,-109"/>
<text text-anchor="middle" x="49" y="-87.3" font-family="Times New Roman,serif" font-size="14.00">Program::Main</text>
</g>
<!-- Code_Bar__ctor -->
<g id="node2" class="node"><title>Code_Bar__ctor</title>
<polygon fill="none" stroke="black" points="232,-200 164,-200 164,-164 232,-164 232,-200"/>
<text text-anchor="middle" x="198" y="-178.3" font-family="Times New Roman,serif" font-size="14.00">Bar::.ctor</text>
</g>
<!-- Code_Program_Main&#45;&gt;Code_Bar__ctor -->
<g id="edge1" class="edge"><title>Code_Program_Main&#45;&gt;Code_Bar__ctor</title>
<path fill="none" stroke="black" d="M72.272,-109.083C88.9615,-122.169 112.672,-139.802 135,-153 141.259,-156.7 148.068,-160.288 154.807,-163.606"/>
<polygon fill="black" stroke="black" points="153.362,-166.794 163.894,-167.949 156.38,-160.479 153.362,-166.794"/>
</g>
<!-- Type_Bar -->
<g id="node8" class="node"><title>Type_Bar</title>
<ellipse fill="none" stroke="black" cx="198" cy="-126" rx="27" ry="18"/>
<text text-anchor="middle" x="198" y="-122.3" font-family="Times New Roman,serif" font-size="14.00">Bar</text>
</g>
<!-- Code_Program_Main&#45;&gt;Type_Bar -->
<g id="edge2" class="edge"><title>Code_Program_Main&#45;&gt;Type_Bar</title>
<path fill="none" stroke="black" d="M98.1333,-102.46C119.142,-107.462 143.303,-113.215 162.482,-117.781"/>
<polygon fill="black" stroke="black" points="161.753,-121.206 172.292,-120.117 163.375,-114.396 161.753,-121.206"/>
</g>
<!-- Type_Baz -->
<g id="node11" class="node"><title>Type_Baz</title>
<ellipse fill="none" stroke="black" stroke-dasharray="1,5" cx="198" cy="-18" rx="27" ry="18"/>
<text text-anchor="middle" x="198" y="-14.3" font-family="Times New Roman,serif" font-size="14.00">Baz</text>
</g>
<!-- Code_Program_Main&#45;&gt;Type_Baz -->
<g id="edge4" class="edge"><title>Code_Program_Main&#45;&gt;Type_Baz</title>
<path fill="none" stroke="black" d="M81.9536,-72.8704C97.7508,-64.1232 117.201,-53.6724 135,-45 144.736,-40.2562 155.506,-35.4252 165.301,-31.1859"/>
<polygon fill="black" stroke="black" points="166.877,-34.3185 174.696,-27.169 164.125,-27.8822 166.877,-34.3185"/>
</g>
<!-- Virtual_Foo_VirtualMethod -->
<g id="node12" class="node"><title>Virtual_Foo_VirtualMethod</title>
<polygon fill="none" stroke="black" stroke-dasharray="5,2" points="261,-90 135,-90 135,-54 261,-54 261,-90"/>
<text text-anchor="middle" x="198" y="-68.3" font-family="Times New Roman,serif" font-size="14.00">Foo::VirtualMethod</text>
</g>
<!-- Code_Program_Main&#45;&gt;Virtual_Foo_VirtualMethod -->
<g id="edge3" class="edge"><title>Code_Program_Main&#45;&gt;Virtual_Foo_VirtualMethod</title>
<path fill="none" stroke="black" d="M98.1333,-84.7787C106.651,-83.6778 115.686,-82.51 124.697,-81.3453"/>
<polygon fill="black" stroke="black" points="125.363,-84.7884 134.832,-80.0353 124.466,-77.8461 125.363,-84.7884"/>
</g>
<!-- Code_Foo__ctor -->
<g id="node6" class="node"><title>Code_Foo__ctor</title>
<polygon fill="none" stroke="black" points="611.5,-228 539.5,-228 539.5,-192 611.5,-192 611.5,-228"/>
<text text-anchor="middle" x="575.5" y="-206.3" font-family="Times New Roman,serif" font-size="14.00">Foo::.ctor</text>
</g>
<!-- Code_Bar__ctor&#45;&gt;Code_Foo__ctor -->
<g id="edge11" class="edge"><title>Code_Bar__ctor&#45;&gt;Code_Foo__ctor</title>
<path fill="none" stroke="black" d="M232.323,-184.485C299.78,-189.515 452.571,-200.908 529.075,-206.613"/>
<polygon fill="black" stroke="black" points="529.114,-210.125 539.347,-207.379 529.635,-203.145 529.114,-210.125"/>
</g>
<!-- Code_Bar_VirtualMethod -->
<g id="node3" class="node"><title>Code_Bar_VirtualMethod</title>
<polygon fill="none" stroke="black" points="636.5,-120 514.5,-120 514.5,-84 636.5,-84 636.5,-120"/>
<text text-anchor="middle" x="575.5" y="-98.3" font-family="Times New Roman,serif" font-size="14.00">Bar::VirtualMethod</text>
</g>
<!-- Code_Bar_UnusedVirtualMethod -->
<g id="node4" class="node"><title>Code_Bar_UnusedVirtualMethod</title>
<polygon fill="none" stroke="black" points="658,-66 493,-66 493,-30 658,-30 658,-66"/>
<text text-anchor="middle" x="575.5" y="-44.3" font-family="Times New Roman,serif" font-size="14.00">Bar::UnusedVirtualMethod</text>
</g>
<!-- Code_Foo_UnusedVirtualMethod -->
<g id="node5" class="node"><title>Code_Foo_UnusedVirtualMethod</title>
<polygon fill="none" stroke="black" points="1059,-120 890,-120 890,-84 1059,-84 1059,-120"/>
<text text-anchor="middle" x="974.5" y="-98.3" font-family="Times New Roman,serif" font-size="14.00">Foo::UnusedVirtualMethod</text>
</g>
<!-- Code_Object__ctor -->
<g id="node7" class="node"><title>Code_Object__ctor</title>
<polygon fill="none" stroke="black" points="1017.5,-228 931.5,-228 931.5,-192 1017.5,-192 1017.5,-228"/>
<text text-anchor="middle" x="974.5" y="-206.3" font-family="Times New Roman,serif" font-size="14.00">Object::.ctor</text>
</g>
<!-- Code_Foo__ctor&#45;&gt;Code_Object__ctor -->
<g id="edge12" class="edge"><title>Code_Foo__ctor&#45;&gt;Code_Object__ctor</title>
<path fill="none" stroke="black" d="M611.731,-210C681.795,-210 838.931,-210 921.129,-210"/>
<polygon fill="black" stroke="black" points="921.252,-213.5 931.252,-210 921.252,-206.5 921.252,-213.5"/>
</g>
<!-- Type_Bar&#45;&gt;Code_Bar_VirtualMethod -->
<g id="edge8" class="edge"><title>Type_Bar&#45;&gt;Code_Bar_VirtualMethod</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M225.106,-124.331C282.243,-120.68 420.002,-111.875 504.47,-106.476"/>
<polygon fill="black" stroke="black" points="504.742,-109.966 514.498,-105.835 504.295,-102.98 504.742,-109.966"/>
<text text-anchor="middle" x="377" y="-123.8" font-family="Times New Roman,serif" font-size="14.00">Foo::VirtualMethod is used</text>
</g>
<!-- Type_Bar&#45;&gt;Code_Bar_UnusedVirtualMethod -->
<g id="edge9" class="edge"><title>Type_Bar&#45;&gt;Code_Bar_UnusedVirtualMethod</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M221.222,-116.66C233.094,-111.586 247.918,-105.114 261,-99 269.103,-95.2131 270.462,-92.6639 279,-90 345.518,-69.2474 423.688,-58.7081 482.916,-53.3784"/>
<polygon fill="black" stroke="black" points="483.338,-56.855 492.999,-52.5047 482.734,-49.8811 483.338,-56.855"/>
<text text-anchor="middle" x="377" y="-93.8" font-family="Times New Roman,serif" font-size="14.00">Foo::UnusedVirtualMethod is used</text>
</g>
<!-- Type_Foo -->
<g id="node9" class="node"><title>Type_Foo</title>
<ellipse fill="none" stroke="black" cx="575.5" cy="-156" rx="27" ry="18"/>
<text text-anchor="middle" x="575.5" y="-152.3" font-family="Times New Roman,serif" font-size="14.00">Foo</text>
</g>
<!-- Type_Bar&#45;&gt;Type_Foo -->
<g id="edge6" class="edge"><title>Type_Bar&#45;&gt;Type_Foo</title>
<path fill="none" stroke="black" d="M224.13,-130.77C239.844,-133.581 260.537,-136.974 279,-139 372.096,-149.214 482.409,-153.501 538.239,-155.123"/>
<polygon fill="black" stroke="black" points="538.242,-158.625 548.335,-155.405 538.437,-151.627 538.242,-158.625"/>
</g>
<!-- Type_Foo&#45;&gt;Code_Foo_UnusedVirtualMethod -->
<g id="edge10" class="edge"><title>Type_Foo&#45;&gt;Code_Foo_UnusedVirtualMethod</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M601.358,-150.741C621.434,-146.618 650.45,-140.937 676,-137 744.023,-126.518 821.223,-117.519 879.932,-111.272"/>
<polygon fill="black" stroke="black" points="880.354,-114.747 889.931,-110.216 879.618,-107.786 880.354,-114.747"/>
<text text-anchor="middle" x="774" y="-140.8" font-family="Times New Roman,serif" font-size="14.00">Foo::UnusedVirtualMethod is used</text>
</g>
<!-- Type_Object -->
<g id="node10" class="node"><title>Type_Object</title>
<ellipse fill="none" stroke="black" cx="974.5" cy="-156" rx="35.194" ry="18"/>
<text text-anchor="middle" x="974.5" y="-152.3" font-family="Times New Roman,serif" font-size="14.00">Object</text>
</g>
<!-- Type_Foo&#45;&gt;Type_Object -->
<g id="edge7" class="edge"><title>Type_Foo&#45;&gt;Type_Object</title>
<path fill="none" stroke="black" d="M602.548,-156C669.133,-156 846.153,-156 929.253,-156"/>
<polygon fill="black" stroke="black" points="929.384,-159.5 939.384,-156 929.384,-152.5 929.384,-159.5"/>
</g>
<!-- Type_Baz&#45;&gt;Type_Foo -->
<g id="edge5" class="edge"><title>Type_Baz&#45;&gt;Type_Foo</title>
<path fill="none" stroke="black" d="M224.132,-12.4854C277.675,-2.33781 403.894,12.8265 475,-51 501.476,-74.7654 469.041,-102.699 493,-129 504.61,-141.745 522.366,-148.485 538.344,-152.044"/>
<polygon fill="black" stroke="black" points="538.03,-155.547 548.505,-153.955 539.324,-148.667 538.03,-155.547"/>
</g>
</g>
</svg>
# Native AOT Developer Workflow
The Native AOT toolchain can be currently built for Linux (x64/arm64), macOS (x64) and Windows (x64/arm64).
## High Level Overview
Native AOT is a stripped down version of the CoreCLR runtime specialized for ahead of time compilation, with an accompanying ahead of time compiler.
The main components of the toolchain are:
* The AOT compiler (ILC/ILCompiler) built on a shared codebase with crossgen2 (src/coreclr/tools/aot). Where crossgen2 generates ReadyToRun modules that contain code and data structures for the CoreCLR runtime, ILC generates code and self-describing datastructures for a stripped down version of CoreCLR into object files. The object files use platform specific file formats (COFF with CodeView on Windows, ELF with DWARF on Linux, and Mach-O with DWARF on macOS).
* The stripped down CoreCLR runtime (NativeAOT specific files in src/coreclr/nativeaot/Runtime, the rest included from the src/coreclr). The stripped down runtime is built into a static library that is linked with object file generated by the AOT compiler using a platform-specific linker (link.exe on Windows, ld on Linux/macOS) to form a standalone executable.
* The bootstrapper library (src/coreclr/nativeaot/Bootstrap). This is a small native library that contains the actual native `main()` entrypoint and bootstraps the runtime and dispatches to managed code. Two flavors of the bootstrapper are built - one for executables, and another for dynamic libraries.
* The core libraries (src/coreclr/nativeaot): System.Private.CoreLib (corelib), System.Private.Reflection.* (the implementation of reflection), System.Private.TypeLoader (ability to load new types that were not generated statically).
* The dotnet integration (src/coreclr/nativeaot/BuildIntegration). This is a set of .targets/.props files that hook into `dotnet publish` to run the AOT compiler and execute the platform linker.
The AOT compiler typically takes the app, core libraries, and framework libraries as input. It then compiles the whole program into a single object file. Then the object file is linked to form a runnable executable. The executable is standalone (doesn't require a runtime), modulo any managed DllImports.
The executable looks like a native executable, in the sense that it can be debugged with native debuggers and have full-fidelity access to locals, and stepping information.
## Building
- [Install pre-requisites](../../README.md#build-requirements)
- Run `build[.cmd|.sh] clr+libs -rc [Debug|Release] -lc Release` from the repo root. This will restore nuget packages required for building and build the parts of the repo required for general CoreCLR development. Alternatively, instead of specifying `clr+libs`, you can specify `clr.jit+clr.tools+clr.nativeaotlibs+libs` which is more targeted and builds faster. Replace `clr.jit` with `clr.alljits` if you need to crosscompile.
- [NOT PORTED OVER YET] The build will place the toolchain packages at `artifacts\packages\[Debug|Release]\Shipping`. To publish your project using these packages:
- [NOT PORTED OVER YET] Add the package directory to your `nuget.config` file. For example, replace `dotnet-experimental` line in `samples\HelloWorld\nuget.config` with `<add key="local" value="C:\runtimelab\artifacts\packages\Debug\Shipping" />`
- [NOT PORTED OVER YET] Run `dotnet publish --packages pkg -r [win-x64|linux-x64|osx-64] -c [Debug|Release]` to publish your project. `--packages pkg` option restores the package into a local directory that is easy to cleanup once you are done. It avoids polluting the global nuget cache with your locally built dev package.
- *Optional*. The ObjWriter component of the AOT compiler is not built by default. If you're working on ObjWriter or bringing up a new platform that doesn't have ObjWriter packages yet, as additional pre-requiresites you need to run `build[.cmd|.sh] clr.objwriter` from the repo root before building the product.
## Visual Studio Solutions
The repository has a number of Visual Studio Solutions files (`*.sln`) that are useful for editing parts of the repository. Build the repo from command line first before building using the solution files. Remember to select the appropriate configuration that you built. By default, `build.cmd` builds Debug x64 and so `Debug` and `x64` must be selected in the solution build configuration drop downs.
- `src\coreclr\nativeaot\nativeaot.sln`. This solution is for the runtime libraries.
- `src\coreclr\tools\aot\ilc.sln`. This solution is for the compiler.
Typical workflow for working on the compiler:
- Open `ilc.sln` in Visual Studio
- Set "ILCompiler" project in solution explorer as your startup project
- Set Working directory in the project Debug options to your test project directory, e.g. `C:\runtimelab\samples\HelloWorld`
- Set Application arguments in the project Debug options to the response file that was generated by regular native aot publishing of your test project, e.g. `@obj\Release\net6.0\win-x64\native\HelloWorld.ilc.rsp`
- Build & run using **F5**
## Convenience Visual Studio "repro" project
Typical native AOT runtime developer scenario workflow is to native AOT compile a short piece of C# and run it. The repo contains helper projects that make debugging the AOT compiler and the runtime easier.
The workflow looks like this:
- Build the repo using the Building instructions above
- Open the ilc.sln solution described above. This solution contains the compiler, but also an unrelated project named "repro". This repro project is a small Hello World. You can place any piece of C# you would like to compile in it. Building the project will compile the source code into IL, but also generate a response file that is suitable to pass to the AOT compiler.
- Make sure you set the solution configuration in VS to the configuration you just built (e.g. x64 Debug).
- In the ILCompiler project properties, on the Debug tab, set the "Application arguments" to the generated response file. This will be a file such as "C:\runtime\artifacts\bin\repro\x64\Debug\compile-with-Release-libs.rsp". Prefix the path to the file with "@" to indicate this is a response file so that the "Application arguments" field looks like "@some\path\to\file.rsp".
- Build & run ILCompiler using **F5**. This will compile the repro project into an `.obj` file. You can debug the compiler and set breakpoints in it at this point.
- The last step is linking the object file into an executable so that we can launch the result of the AOT compilation.
- Open the src\coreclr\tools\aot\ILCompiler\reproNative\reproNative.vcxproj project in Visual Studio. This project is configured to pick up the `.obj` file we just compiled and link it with the rest of the runtime.
- Set the solution configuration to the tuple you've been using so far (e.g. x64 Debug)
- Build & run using **F5**. This will run the platform linker to link the obj file with the runtime and launch it. At this point you can debug the runtime and the various System.Private libraries.
## Running tests
If you haven't built the tests yet, run `src\tests\build[.cmd|.sh] nativeaot [Debug|Release] tree nativeaot`. This will build the smoke tests only - they usually suffice to ensure the runtime and compiler is in a workable shape. To build all Pri-0 tests, drop the `tree nativeaot` parameter. The `Debug`/`Release` parameter should match the build configuration you used to build the runtime.
To run all the tests that got built, run `src\tests\run.cmd runnativeaottests [Debug|Release]` on Windows, or `src/tests/run.sh --runnativeaottests [Debug|Release]` on Linux. The `Debug`/`Release` flag should match the flag that was passed to `build.cmd` in the previous step.
To run an individual test (after it was built), navigate to the `artifacts\tests\coreclr\[Windows|Linux|OSX[.x64.[Debug|Release]\$path_to_test` directory. `$path_to_test` matches the subtree of `src\tests`. You should see a `[.cmd|.sh]` file there. This file is a script that will compile and launch the individual test for you. Before invoking the script, set the following environment variables:
* CORE_ROOT=$repo_root\artifacts\tests\coreclr\[Windows|Linux|OSX[.x64.[Debug|Release]\Tests\Core_Root
* RunNativeAot=1
* __TestDotNetCmd=$repo_root\dotnet[.cmd|.sh]
`$repo_root` is the root of your clone of the repo.
By default the test suite will delete the build artifacts (Native AOT images and response files) if the test compiled successfully. If you want to keep these files instead of deleting them after test run, set the following environment variables and make sure you'll have enough disk space (tens of MB per test):
* CLRTestNoCleanup=1
For more advanced scenarios, look for at [Building Test Subsets](../../testing/coreclr/windows-test-instructions.md#building-test-subsets) and [Generating Core_Root](../../testing/coreclr/windows-test-instructions.md#generating-core_root)
## Design Documentation
- [ILC Compiler Architecture](../../../design/coreclr/botr/ilc-architecture.md)
- [Managed Type System](../../../design/coreclr/botr/managed-type-system.md)
How to Debug Crossgen2
How to Debug CoreCLR AOT Compilers
=================
Crossgen2 brings with it a number of new challenges for debugging the compilation process. Fortunately, in addition to challenges, crossgen2 is designed to enhance various parts of the debugging experience.
CoreCLR comes with two AOT compilers that are built around a shared C# codebase - crossgen2 and ilc. Crossgen2 generates ReadyToRun images that are loadable into the JIT-based CoreCLR runtime. ILC generates platform-specific object files (COFF on Windows, ELF on Linux, Mach-O on macOS) that can be linked with the NativeAOT flavor of the CoreCLR runtime to form a self-contained executable or a shared library.
Important concerns to be aware of when debugging Crossgen2
The managed AOT compilers bring with them a number of new challenges for debugging the compilation process. Fortunately, in addition to challenges, the compilers are designed to enhance various parts of the debugging experience.
Important concerns to be aware of when debugging the managed compilers
---------------------------------
- Other than the JIT, Crossgen2 is a managed application
- By default Crossgen2 uses a multi-core compilation strategy
- A Crossgen2 process will have 2 copies of the JIT in the process at the same time, the one used to compile the target, and the one used to compile Crossgen2 itself.
- Crossgen2 does not parse environment variables for controlling the JIT (or any other behavior), all behavior is controlled via the command line
- The Crossgen2 command line as generated by the project system is quite complex
- Other than the JIT, the AOT compilers are managed applications
- By default the AOT compilers use a multi-core compilation strategy
- A compilation process will have 2 copies of the JIT in the process at the same time, the one used to compile the target, and the one used to compile compiler itself.
- The compilers don't parse environment variables for controlling the JIT (or any other behavior), all behavior is controlled via the command line
- The AOT compiler command line as generated by the project system is quite complex
Built in debugging aids in Crossgen2
Built in debugging aids in the managed compilers
---------------------------------
- When debugging a multi-threaded component of Crossgen2 and not investigating a multi-threading issue itself, it is generally advisable to disable the use of multiple threads.
To do this use the `--parallelism 1` switch to specify that the maximum parallelism of the process shall be 1.
- When debugging a multi-threaded component of the compiler and not investigating a multi-threading issue itself, it is generally advisable to disable the use of multiple threads.
To do this use the `--parallelism 1` switch (for crossgen2) or `--singlethreaded` (for ILC) to specify that the maximum parallelism of the process shall be 1.
- When debugging the behavior of compiling a single method, the compiler may be instructed to only compile a single method. This is done via the various --singlemethod options
......@@ -25,13 +27,13 @@ To do this use the `--parallelism 1` switch to specify that the maximum parallel
- `--singlemethodindex` is used in cases where the method signature is the only distinguishing factor about the method. An index is used instead of a series of descriptive arguments, as specifying a signature exactly is extraordinarily complicated.
- Repro args will look like the following ``--singlemethodtypename "Internal.Runtime.CompilerServices.Unsafe" --singlemethodname As --singlemethodindex 2 --singlemethodgenericarg "System.Runtime.Intrinsics.Vector256`1[[System.SByte]]" --singlemethodgenericarg "System.Runtime.Intrinsics.Vector256`1[[System.Double]]"``
- Since Crossgen2 is by default multi-threaded, it produces results fairly quickly even when compiling using a Debug variant of the JIT. In general, when debugging JIT issues we recommend using the debug JIT regardless of which environment caused a problem.
- Since the compilers are by default multi-threaded, they produce results fairly quickly even when compiling using a Debug variant of the JIT. In general, when debugging JIT issues we recommend using the debug JIT regardless of which environment caused a problem.
- Crossgen2 supports nearly arbitrary cross-targetting, including OS and architecture cross targeting. The only restriction is that 32bit architecture cannot compile targetting a 64bit architecture. This allows the use of the debugging environment most convenient to the developer. In particular, if there is an issue which crosses the managed/native boundary, it is often convenient to debug using the mixed mode debugger on Windows X64.
- If the correct set of assemblies/command line arguments are passed to the compiler Crossgen2 should produce binary identical output on all platforms.
- The compilers support nearly arbitrary cross-targetting, including OS and architecture cross targeting. The only restriction is that 32bit architecture cannot compile targetting a 64bit architecture. This allows the use of the debugging environment most convenient to the developer. In particular, if there is an issue which crosses the managed/native boundary, it is often convenient to debug using the mixed mode debugger on Windows X64.
- If the correct set of assemblies/command line arguments are passed to the compiler, it should produce binary identical output on all platforms.
- The compiler does not check the OS/Architecture specified for input assemblies, which allows compiling using a non-architecture/OS matched version of the framework to target an arbitrary target. While this isn't useful for producing the diagnosing all issues, it can be cheaply used to identify the general behavior of a change on the full swath of supported architectures.
Control compilation behavior by using the `--targetos` and `--targetarch` switches. The default behavior is to target the crossgen2's own OS/Arch pair, but all 64bit versions of crossgen2 are capable of targetting arbitrary OS/Arch combinations.
Control compilation behavior by using the `--targetos` and `--targetarch` switches. The default behavior is to target the compiler's own OS/Arch pair, but all 64bit versions of the compilers are capable of targetting arbitrary OS/Arch combinations.
At the time of writing the current supported sets of valid arguments are:
| Command line arguments
| --- |
......@@ -49,28 +51,38 @@ At the time of writing the current supported sets of valid arguments are:
- When using the NgenDump feature of the JIT, disable parallelism as described above or specify a single method to be compiled. Otherwise, output from multiple functions will be interleaved and inscrutable.
- Since there are 2 jits in the process, when debugging in the JIT, if the source files match up, there is a decent chance that a native debugger will stop at unfortunate and unexpected locations. This is extremely annoying, and to combat this, we generally recommend making a point of using a runtime which doesn't exactly match that of the crossgen2 in use. However, if that isn't feasible, it is also possible to disable symbol loading in most native debuggers. For instance, in Visual Studio, one would use the "Specify excluded modules" feature.
- Since there are 2 jits in the process, when debugging in the JIT, if the source files match up, there is a decent chance that a native debugger will stop at unfortunate and unexpected locations. This is extremely annoying, and to combat this, we generally recommend making a point of using a runtime which doesn't exactly match that of the compiler in use. However, if that isn't feasible, it is also possible to disable symbol loading in most native debuggers. For instance, in Visual Studio, one would use the "Specify excluded modules" feature.
- Crossgen2 identifies the JIT to use by the means of a naming convention. By default it will use a JIT located in the same directory as the crossgen2.dll file. In addition there is support for a `--jitpath` switch to use a specific JIT. This option is intended to support A/B testing by the JIT team. The `--jitpath` option should only be used if the jit interface has not been changed. The JIT specified by the `--jitpath` switch must be compatible with the current settings of the `--targetos` and `--targetarch` switches.
- The compiler identifies the JIT to use by the means of a naming convention. By default it will use a JIT located in the same directory as the crossgen2.dll file. In addition there is support for a `--jitpath` switch to use a specific JIT. This option is intended to support A/B testing by the JIT team. The `--jitpath` option should only be used if the jit interface has not been changed. The JIT specified by the `--jitpath` switch must be compatible with the current settings of the `--targetos` and `--targetarch` switches.
- In parallel to the crossgen2 project, there is a tool known as r2rdump. This tool can be used to dump the contents of a produced image to examine what was actually produced in the final binary. It has a large multitude of options to control exactly what is dumped, but in general it is able to dump any image produced by crossgen2, and display its contents in a human readable fashion. Specify `--disasm` to display disassembly.
- In parallel to the crossgen2 project, there is a tool known as r2rdump. This tool can be used to dump the contents of a produced ReadyToRun image to examine what was actually produced in the final binary. It has a large multitude of options to control exactly what is dumped, but in general it is able to dump any image produced by crossgen2, and display its contents in a human readable fashion. Specify `--disasm` to display disassembly.
- If there is a need to debug the dependency graph of crossgen2 (which is a very rare need at this time), there is a visualization tool located in the corert repo. https://github.com/dotnet/corert/tree/master/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer To use that tool, get the sources from the CoreRT repo, compile it, and run it on Windows before the crossgen2 compilation begins. It will present a live view of the graph as it is generated and allow for exploration to determine why some node is in the graph. Every node in the graph has a unique id that is visible to this tool, and it can be used in parallel with a debugger to understand what is happening in the crossgen2 process. Changes to move this tool to a more commonly built location and improve the fairly horrible UI are encouraged.
- If there is a need to debug the dependency graph of the compiler (which is a very rare need at this time), there is a visualization tool located in src\coreclr\tools\aot\DependencyGraphViewer. To use that tool, compile it, and run it on Windows before the compilation begins. It will present a live view of the graph as it is generated and allow for exploration to determine why some node is in the graph. Every node in the graph has a unique id that is visible to this tool, and it can be used in parallel with a debugger to understand what is happening in the compilation process. Changes to improve the fairly horrible UI are encouraged.
- When used in the official build system, the set of arguments passed to crossgen2 is extremely complex, especially with regards to the set of reference paths (each assembly is specified individually). To make it easier to use crossgen2 from the command line manually the tool will accept wildcards in its parsing of references. Please note that on Unix that the shell will happily expand these arguments by itself, which will not work correctly. In those situations enclose the argument in quotes to prevent the shell expansion.
- When used in the official build system, the set of arguments passed to the compiler are extremely complex, especially with regards to the set of reference paths (each assembly is specified individually). To make it easier to use crossgen2 from the command line manually the tool will accept wildcards in its parsing of references. Please note that on Unix that the shell will happily expand these arguments by itself, which will not work correctly. In those situations enclose the argument in quotes to prevent the shell expansion.
- Crossgen2 supports a `--map` and `--mapcsv` arguments to produce map files of the produced output. These are primarily used for diagnosing size issues, as they describe the generated file in fairly high detail, as well as providing a number of interesting statistics about the produced output.
- ILC also supports the `--map` argument but the format is different from crossgen2 because the output format is different too.
- Diagnosing why a specific method failed to compile in crossgen2 can be done by passing the `--verbose` switch to crossgen2. This will print many things, but in particular it will print the reason why a compilation was aborted due to an R2R format limitation.
- Crossgen2 can use either the version of dotnet that is used to build the product (as found by the dotnet.cmd or dotnet.sh script found in the root of the runtime repo) or it can use a sufficiently recent corerun.exe produced by constructing a test build. It is strongly recommended if using corerun.exe to use a release build of corerun for this purpose, as crossgen2 runs a very large amount of managed code. The version of corerun used does not need to come from the same build as the crossgen2.dll that is being debugging. In fact, I would recommnend using a different enlistment to build that corerun to avoid any confusion.
- The compilers can use either the version of dotnet that is used to build the product (as found by the dotnet.cmd or dotnet.sh script found in the root of the runtime repo) or it can use a sufficiently recent corerun.exe produced by constructing a test build. It is strongly recommended if using corerun.exe to use a release build of corerun for this purpose, as crossgen2 runs a very large amount of managed code. The version of corerun used does not need to come from the same build as the crossgen2.dll/ilc.dll that is being debugging. In fact, I would recommnend using a different enlistment to build that corerun to avoid any confusion.
- In the runtime testbed, each test can be commanded to compile with crossgen2 by using environment variables. Just set the `RunCrossgen2` variable to 1, and optionally set the `CompositeBuildMode` variable to 1 if you wish to see the R2R behavior with composite image creation.
- In the runtime testbed, each test can be commanded to compile with crossgen2 by using environment variables. Just set the `RunCrossgen2` variable to 1, and optionally set the `CompositeBuildMode` variable to 1 if you wish to see the R2R behavior with composite image creation. By default this will simply use `dotnet` to run crossgen2. If you run the test batch script from the root of the enlistment on Windows this will just work; otherwise, you must set the `__TestDotNetCmd` environment variable to point at copy of `dotnet` or `corerun` that can run crossgen2. This is often the easiest way to run a simple test with crossgen2 for developers practiced in the CoreCLR testbed. See the example below of various techniques to use when diagnosing issues under crossgen2.
- By default the runtime test bed will simply use `dotnet` to run the managed compiler. If you run the test batch script from the root of the enlistment on Windows this will just work; otherwise, you must set the `__TestDotNetCmd` environment variable to point at copy of `dotnet` or `corerun` that can run the compiler. This is often the easiest way to run a simple test with the AOT compiler for developers practiced in the CoreCLR testbed. See the example below of various techniques to use when diagnosing issues under crossgen2.
- When attempting to build crossgen2, you must build the clr.tools subset. If rebuilding a component of the JIT and wanting to use that in your inner loop, you must build as well with either the clr.jit or clr.alljits subsets. If the jit interface is changed, the clr.runtime subset must also be rebuilt.
- After completion of a product build, a functional copy of crossgen2.dll will be located in a bin directory in a path like `bin\coreclr\windows.x64.Debug\crossgen2`. After creating a test native layout via a command such as `src\tests\build generatelayoutonly` then there will be a copy of crossgen2 located in the `%CORE_ROOT%\crossgen2` directory. The version of crossgen2 in the test core_root directory will have the appropriate files for running under either an x64 dotnet.exe or under the target architecture. This was done to make it somewhat easier to do cross platform development, and assumes the primary development machine is x64,
- The object files generated by the ILC compiler contain debug information for method bodies and types in the platform specific format (CodeView on Windows, DWARF elsewhere). They also contain unwinding information in the platform format. As a result of that, NativeAOT executables can be debugged with platform debuggers (VS, WinDbg, GDB, LLDB) without any SOS-like extensions. They can also be inspected using disassemblers and tools that deal with native code (dumpbin, Ghidra, IDA). Make sure to pass `-g` command line argument to enable debug info generation.
- The ILC compiler typically compiles the whole program - it loosely corresponds to the composite mode of crossgen2. There is a multifile mode, where each managed assembly corresponds to a single object file, but this mode is not shipping.
- The object files generated by the ILC compiler are written out using an LLVM-based object writer (src\coreclr\tools\aot\ObjWriter). The object writer uses the LLVM assembler APIs (APIs meant to be used by tools that convert textual assembly into machine code) to emit object files in PE/ELF/Mach-O formats. Normally the object writer is not built as part of the repo, but gets downloaded through NuGet. If you need to debug the object writer, you can build it by specifying `clr.objwriter` subset to the root build script. It takes about 5 minutes to compile the object writer.
Example of debugging a test application in Crossgen2
================================================
This example is to demonstrate debugging of a simple test in the CoreCLR testbed.
......
......@@ -73,10 +73,10 @@ The "COMPlus_EnableDiagnostics" environment variable can be used to disable mana
export COMPlus_EnableDiagnostics=0
Debugging Crossgen2
Debugging AOT compilers
==================================
Debugging Crossgen2 is described in [this](debugging-crossgen2.md) document.
Debugging AOT compilers is described in [this](debugging-aot-compilers.md) document.
Using Visual Studio Code
========================
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册