Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 50 additions & 39 deletions docs/core/deploying/native-aot/diagnostics.md
Original file line number Diff line number Diff line change
@@ -1,70 +1,81 @@
---
title: AOT diagnostics
description: Learn about diagnostics in native AOT applications
title: Diagnostics and instrumentation
description: Learn about diagnostics in Native AOT applications
author: lakshanf
ms.author: lakshanf
ms.date: 08/07/2023
---
# Diagnostics in native AOT applications

The long-term goal for native AOT application diagnostics is to provide the rich diagnostic experience that developers expect out of a .NET application. The .NET diagnostic experience was built over multiple releases, and a critical part of that journey was customer feedback. Native AOT diagnostics will follow a similar path where the right diagnostic experience is built from prioritized customer feedback in multiple releases.
# Diagnostics and instrumentation

## Native AOT application features
Native AOT shares some, but not all, diagnostics and instrumentation capabilities with CoreCLR. Because of CoreCLR's rich selection of diagnostic utilities, it's sometimes appropriate to diagnose and debug problems in CoreCLR. Apps that are [trim-compatible](../trimming/prepare-libraries-for-trimming.md) should not have behavioral differences, so investigations often apply to both runtimes. Nonetheless, some information can only be gathered after publishing, so Native AOT also provides post-publish diagnostic tooling.

Native AOT applications have the following characteristics that are important to keep in mind in diagnostic scenarios.
## .NET 8 Native AOT diagnostic support

### Trimming as a first-class feature
The following table summarizes diagnostic features supported for Native AOT deployments:

Unused code in a native AOT application is stripped out of the final binary. Any unbounded reflection used by the application, including its dependent libraries, is trimmed. The native AOT compiler will generate warnings in such cases, and it's critical that these warnings are fixed. For example, if you suppress any warnings without careful analysis, it can lead to hard-to-debug production failures.
| Feature | Fully supported | Partially supported | Not supported |
| - | - | - | - |
| [Observability and telemetry](#observability-and-telemetry) | | <span aria-hidden="true">✔️</span><span class="visually-hidden">Partially supported</span> | |
| [Development-time diagnostics](#development-time-diagnostics) | <span aria-hidden="true">✔️</span><span class="visually-hidden">Fully supported</span> | | |
| [Native debugging](#native-debugging) | | <span aria-hidden="true">✔️</span><span class="visually-hidden">Partially supported</span> | |
| [CPU Profiling](#cpu-profiling) | | <span aria-hidden="true">✔️</span><span class="visually-hidden">Partially supported</span> | |
| [Heap analysis](#heap-analysis) | | | <span aria-hidden="true">❌</span><span class="visually-hidden">Not supported</span> |

### Importance of the symbol file
## Observability and telemetry

In native AOT, symbol-file-dependent diagnostics (such as debugging [PerfView](https://github.com/microsoft/perfview) callstacks) don't work at all unless the diagnostic tool has access to the monolithic PDB that was generated when the application was compiled. In .NET, symbol-file-dependent diagnostics generally work just fine, or even great, even if the diagnostic tool has no access to any PDBs that were generated when the application was compiled. That is, symbols for the .NET runtime and the .NET libraries can be pulled from symbol servers, and diagnostic tools can still show function names and accurate call stacks even without access to the compile-time PDBs.
As of .NET 8, the Native AOT runtime supports [EventPipe](../../diagnostics/eventpipe.md), 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](../../diagnostics/observability-with-otel.md). EventPipe support also allows .NET diagnostic tools like [dotnet-trace](../../diagnostics/dotnet-trace.md), [dotnet-counter](../../diagnostics/dotnet-counters.md), and [dotnet-monitor](../../diagnostics/dotnet-monitor.md) to work seamlessly with Native AOT or CoreCLR applications. EventPipe is an optional component in Native AOT. To include EventPipe support, set the `EventSourceSupport` MSBuild property to `true`.

### Application type
```xml
<PropertyGroup>
<EventSourceSupport>true</EventSourceSupport>
</PropertyGroup>
```

Native AOT apps aren't typical managed applications (for example, no JIT). They also aren't typical native applications (for example, they have full GC support). The intent for diagnostics is to meet a reasonable expectation of a product in the .NET family, but the experience should also feel familiar to those using production diagnostics on C++ apps.
Native AOT provides partial support for some [well-known event providers](../../diagnostics/well-known-event-providers.md). Not all [runtime events](../../../fundamentals/diagnostics/runtime-events.md) are supported in Native AOT.

## .NET 8 native AOT diagnostic support
## Development-time diagnostics

The following table summarizes diagnostic features supported for native AOT deployments:
The .NET CLI tooling (`dotnet` SDK) and Visual Studio offer separate commands for `build` and
`publish`. `build` (or `Start` in Visual Studio) uses CoreCLR. 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, this means that not all diagnostic
tools will 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.

| Feature | Fully supported | Partially supported | Not supported |
| - | - | - | - |
| Build (Inner dev loop) diagnostics | <span aria-hidden="true">✔️</span><span class="visually-hidden">Fully supported</span> | | |
| Observability | | <span aria-hidden="true">✔️</span><span class="visually-hidden">Partially supported</span> | |
| CPU Profiling | | <span aria-hidden="true">✔️</span><span class="visually-hidden">Partially supported</span> | |
| Production debugging | | <span aria-hidden="true">✔️</span><span class="visually-hidden">Partially supported</span> | |
| Heap analysis | | | <span aria-hidden="true">❌</span><span class="visually-hidden">Not supported</span> |
## Native debugging

### Build (inner dev loop) diagnostics
When you run your app during development, like inside Visual Studio, or with `dotnet run`, `dotnet build`, or `dotnet test`, it runs on CoreCLR by default. However, if `PublishAot` is present in the project file, the behavior should be the same between CoreCLR and Native AOT. This allows you to use the standard Visual Studio managed debugging engine for development and testing.

Publishing your app as native AOT produces an app that has been ahead-of-time (AOT) compiled to native code. As mentioned above, this means that not all diagnostic tools will 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 publish the working app with native AOT as one of the last steps.
After publishing, Native AOT applications are true native binaries. The managed debugger will not work on them. However, the Native AOT compiler generates fully native executable files that can be debugged by native debuggers on your platform of choice (for example, WinDbg or Visual Studio on Windows and gdb or lldb on Unix-like systems).

### Observability
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.

The native AOT runtime supports [EventPipe](../../diagnostics/eventpipe.md), which allows native AOT apps to easily trace their applications. EventPipe support also allows most .NET diagnostic tools, like [dotnet-trace](../../diagnostics/dotnet-trace.md), [dotnet-counter](../../diagnostics/dotnet-counters.md), and [dotnet-monitor](../../diagnostics/dotnet-monitor.md), to work seamlessly with native AOT applications. EventPipe is an optional component in native AOT. To include EventPipe support, set the `EventSourceSupport` MSBuild property to `true`.
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.

```xml
<PropertyGroup>
<EventSourceSupport>true</EventSourceSupport>
</PropertyGroup>
```
Collecting a [dump](../../diagnostics/dumps.md) file for a Native AOT application involves some manual steps in .NET 8.

### Visual Studio-specific notes

[OpenTelemetry](../../diagnostics/observability-with-otel.md) is expected to support native AOT in the key three pillars of observability&mdash;logging, tracing, and metrics&mdash;in .NET 8. Native AOT provides partial support for some [well-known event providers](../../diagnostics/well-known-event-providers.md). Not all [runtime events](../../../fundamentals/diagnostics/runtime-events.md) are supported in native AOT.
You can launch a Native AOT-compiled executable under the Visual Studio debugger by opening it in the Visual Studio IDE. You will need to [open the executable itself in Visual Studio](/visualstudio/debugger/how-to-debug-an-executable-not-part-of-a-visual-studio-solution).

### CPU profiling
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#).

Platform-specific tools like [PerfView](https://github.com/microsoft/perfview) and [Perf](https://perf.wiki.kernel.org/index.php/Main_Page) can be used to collect CPU samples of a native AOT application.
To see what exception was thrown, start debugging (**Debug > Start Debugging** or <kbd>F5</kbd>), 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

### Production debugging
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 is not present, you may have degraded or broken results.

Typical production debugging scenarios are done through logging and tracing and are [supported](#observability) in native AOT. Low-level debugging, using platform debuggers like WinDbg or Visual Studio on Windows, and gdb or lldb on Unix-like systems, can be used in native AOT. For this case, it's critical that the corresponding symbol file for the application is available.
For information about the name and location of the symbol file, see [Native debug information](index.md#native-aot-deployment).

The native AOT compiler generates information about line numbers, types, locals, and parameters. The native debugger allows inspecting stack trace and variables, stepping into or over source lines, or setting line breakpoints.
## CPU profiling

Collecting a [dump](../../diagnostics/dumps.md) file for a native AOT application involves some manual steps in .NET 8.
Platform-specific tools like [PerfView](https://github.com/microsoft/perfview) and [Perf](https://perf.wiki.kernel.org/index.php/Main_Page) can be used to collect CPU samples of a Native AOT application.

### Heap analysis
## Heap analysis

Managed heap analysis is not currently supported in native AOT. Heap analysis tools like [dotnet-gcdump](../../diagnostics/dotnet-gcdump.md), [PerfView](https://github.com/microsoft/perfview), and Visual Studio heap analysis tools don't work in native AOT in .NET 8.
Managed heap analysis is not currently supported in Native AOT. Heap analysis tools like [dotnet-gcdump](../../diagnostics/dotnet-gcdump.md), [PerfView](https://github.com/microsoft/perfview), and Visual Studio heap analysis tools don't work in Native AOT in .NET 8.
14 changes: 7 additions & 7 deletions docs/core/deploying/native-aot/fixing-warnings.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
---
title: Introduction to AOT warnings
description: Learn why warnings might be produced when publishing an application as native AOT, how to address them, and how to make the application "AOT compatible."
description: Learn why warnings might be produced when publishing an application as Native AOT, how to address them, and how to make the application "AOT compatible."
author: MichalStrehovsky
ms.author: michals
ms.date: 09/06/2022
---
# Introduction to AOT warnings

When publishing your application as native AOT, the build process produces all the native code and data structures required to support the application at run time. This is different from non-native deployments, which execute the application from formats that describe the application in abstract terms (a program for a virtual machine) and create native representations on demand at run time.
When publishing your application as Native AOT, the build process produces all the native code and data structures required to support the application at run time. This is different from non-native deployments, which execute the application from formats that describe the application in abstract terms (a program for a virtual machine) and create native representations on demand at run time.

The abstract representations of program parts don't have a one-to-one mapping to native representation. For example, the abstract description of the generic `List<T>.Add` method maps to potentially infinite native method bodies that need to be specialized for the given `T` (for example, `List<int>.Add` and `List<double>.Add`).

Because the relationship of abstract code to native code is not one-to-one, the build process needs to create a complete list of native code bodies and data structures at build time. It can be difficult to create this list at build time for some of the .NET APIs. If the API is used in a way that wasn't anticipated at build time, an exception will be thrown at run time.

To prevent changes in behavior when deploying as native AOT, the .NET SDK provides static analysis of AOT compatibility through "AOT warnings." AOT warnings are produced when the build finds code that may not be compatible with AOT. Code that's not AOT-compatible may produce behavioral changes or even crashes in an application after it's been built as native AOT. Ideally, all applications that use native AOT should have no AOT warnings. If there are any AOT warnings, ensure there are no behavior changes by thoroughly testing your app after building as native AOT.
To prevent changes in behavior when deploying as Native AOT, the .NET SDK provides static analysis of AOT compatibility through "AOT warnings." AOT warnings are produced when the build finds code that may not be compatible with AOT. Code that's not AOT-compatible may produce behavioral changes or even crashes in an application after it's been built as Native AOT. Ideally, all applications that use Native AOT should have no AOT warnings. If there are any AOT warnings, ensure there are no behavior changes by thoroughly testing your app after building as Native AOT.

## Examples of AOT warnings

Expand All @@ -30,9 +30,9 @@ while (true)
struct GenericType<T> { }
```

While the above program is not very useful, it represents an extreme case that requires an infinite number of generic types to be created when building the application as native AOT. Without native AOT, the program would run until it runs out of memory. With native AOT, we would not be able to even build it if we were to generate all the necessary types (the infinite number of them).
While the above program is not very useful, it represents an extreme case that requires an infinite number of generic types to be created when building the application as Native AOT. Without Native AOT, the program would run until it runs out of memory. With Native AOT, we would not be able to even build it if we were to generate all the necessary types (the infinite number of them).

In this case, native AOT build issues the following warning on the `MakeGenericType` line:
In this case, Native AOT build issues the following warning on the `MakeGenericType` line:

```
AOT analysis warning IL3050: Program.<Main>$(String[]): Using member 'System.Type.MakeGenericType(Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.
Expand All @@ -42,7 +42,7 @@ At run time, the application will indeed throw an exception from the `MakeGeneri

## React to AOT warnings

The AOT warnings are meant to bring predictability to native AOT builds. A majority of AOT warnings are about possible run-time exception in situations when native code wasn't generated to support the scenario. The broadest category is `RequiresDynamicCodeAttribute`.
The AOT warnings are meant to bring predictability to Native AOT builds. A majority of AOT warnings are about possible run-time exception in situations when native code wasn't generated to support the scenario. The broadest category is `RequiresDynamicCodeAttribute`.

### RequiresDynamicCode

Expand All @@ -60,7 +60,7 @@ void TestMethod()
}
```

There aren't many workarounds for `RequiresDynamicCode`. The best fix is to avoid calling the method at all when building as native AOT and use something else that's AOT compatible. If you're writing a library and it's not in your control whether or not to call the method, you can also add `RequiresDynamicCode` to your own method. This will annotate your method as not AOT compatible. Adding `RequiresDynamicCode` silences all AOT warnings in the annotated method but will produce a warning whenever someone else calls it. For this reason, it's mostly useful to library authors to "bubble up" the warning to a public API.
There aren't many workarounds for `RequiresDynamicCode`. The best fix is to avoid calling the method at all when building as Native AOT and use something else that's AOT compatible. If you're writing a library and it's not in your control whether or not to call the method, you can also add `RequiresDynamicCode` to your own method. This will annotate your method as not AOT compatible. Adding `RequiresDynamicCode` silences all AOT warnings in the annotated method but will produce a warning whenever someone else calls it. For this reason, it's mostly useful to library authors to "bubble up" the warning to a public API.

If you can somehow determine that the call is safe, and all native code will be available at run time, you can also suppress the warning using <xref:System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute>. For example:

Expand Down
Loading