Diagnostics and instrumentation
Native AOT shares some, but not all, diagnostics and instrumentation capabilities with the full .NET runtime. Apps that are trim-compatible shouldn't have behavioral differences, so investigations often apply to both runtimes. As such, it's sometimes appropriate to diagnose and debug problems in the full .NET runtime, as it has a rich selection of available diagnostic utilities. Nonetheless, some information can only be gathered after publishing, so Native AOT also provides post-publish diagnostic tooling.
Native AOT diagnostic support
The following table summarizes diagnostic features supported for Native AOT deployments:
Feature | Fully supported | Partially supported | Not supported |
---|---|---|---|
Observability and telemetry | Partially supported | ||
Development-time diagnostics | Fully supported | ||
Native debugging | Partially supported | ||
CPU Profiling | Partially supported | ||
Heap analysis | Not supported |
Observability and telemetry
As of .NET 8, the Native AOT runtime supports EventPipe, which is the base layer used by many logging and tracing libraries. You can interface with EventPipe directly through APIs like EventSource.WriteEvent
or you can use libraries built on top, like OpenTelemetry. EventPipe support also allows .NET diagnostic tools like dotnet-trace, dotnet-counters, and dotnet-monitor to work seamlessly with Native AOT or full .NET runtime applications. EventPipe is an optional component in Native AOT. To include EventPipe support, set the EventSourceSupport
MSBuild property to true
.
<PropertyGroup>
<EventSourceSupport>true</EventSourceSupport>
</PropertyGroup>
Native AOT provides partial support for some well-known event providers. Not all runtime events are supported in Native AOT.
Development-time diagnostics
The .NET CLI tooling (dotnet
SDK) and Visual Studio offer separate commands for build
and
publish
. build
(or Start
in Visual Studio) uses the full .NET runtime. Only publish
creates a
Native AOT application. Publishing your app as Native AOT produces an app that has been
ahead-of-time (AOT) compiled to native code. As mentioned previously, not all diagnostic
tools work seamlessly with published Native AOT applications in .NET 8. However, all .NET
diagnostic tools are available for developers during the application building stage. We recommend
developing, debugging, and testing the applications as usual and publishing the working app with Native AOT as one of the last steps.
Native debugging
When you run your app during development, like inside Visual Studio, or with dotnet run
, dotnet build
, or dotnet test
, it runs on the full .NET runtime by default. However, if PublishAot
is present in the project file, the behavior should be the same between the full .NET runtime and Native AOT. This characteristic allows you to use the standard Visual Studio managed debugging engine for development and testing.
After publishing, Native AOT applications are true native binaries. The managed debugger won't work on them. However, the Native AOT compiler generates fully native executable files that native debuggers can debug on your platform of choice (for example, WinDbg or Visual Studio on Windows and gdb or lldb on Unix-like systems).
The Native AOT compiler generates information about line numbers, types, locals, and parameters. The native debugger lets you inspect stack trace and variables, step into or over source lines, or set line breakpoints.
To debug managed exceptions, set a breakpoint on the RhThrowEx
method, which is called whenever a managed exception is thrown. The exception is stored in the rcx
or x0
register. If your debugger supports viewing C++ objects, you can cast
the register to S_P_CoreLib_System_Exception*
to see more information about the exception.
Collecting a dump file for a Native AOT application involves some manual steps in .NET 8.
Visual Studio-specific notes
You can launch a Native AOT-compiled executable under the Visual Studio debugger by opening it in the Visual Studio IDE. You need to open the executable itself in Visual Studio.
To set a breakpoint that breaks whenever an exception is thrown, choose the Breakpoints option from the Debug > Windows menu. In the new window, select New > Function breakpoint. Specify RhThrowEx
as the Function Name and leave the Language option at All Languages (don't select C#).
To see what exception was thrown, start debugging (Debug > Start Debugging or F5), open the Watch window (Debug > Windows > Watch), and add following expression as one of the watches: (S_P_CoreLib_System_Exception*)@rcx
. This mechanism leverages the fact that at the time RhThrowEx
is called, the x64 CPU register RCX contains the thrown exception. You can also paste the expression into the Immediate window; the syntax is the same as for watches.
Importance of the symbol file
When publishing, the Native AOT compiler produces both an executable and a symbol file. Native debugging, and related activities like profiling, require access to the native symbol file. If this file isn't present, you might have degraded or broken results.
For information about the name and location of the symbol file, see Native debug information.
CPU profiling
Platform-specific tools like PerfView and Perf can be used to collect CPU samples of a Native AOT application.
Heap analysis
Managed heap analysis isn't currently supported in Native AOT. Heap analysis tools like dotnet-gcdump, PerfView, and Visual Studio heap analysis tools don't work in Native AOT in .NET 8.