Skip to content

Commit

Permalink
[docs] update notes about dotnet-trace and dotnet-gcdump (#8713)
Browse files Browse the repository at this point in the history
Context: dotnet/diagnostics#4337

Update `Documentation/guides/tracing.md` to be up-to-date for .NET 8.
  • Loading branch information
jonathanpeppers authored Mar 6, 2024
1 parent e41a31b commit 136dc83
Showing 1 changed file with 119 additions and 67 deletions.
186 changes: 119 additions & 67 deletions Documentation/guides/tracing.md
Original file line number Diff line number Diff line change
@@ -1,91 +1,138 @@
# Using a device connected via USB
# Tracing .NET Android Applications

## Startup profiling
### Set up reverse port forwarding:
Attaching `dotnet-trace` to a .NET Android application, allows you to
get profiling information in formats like `.nettrace` and
`.speedscope`. These give you CPU sampling information about the time
spent in each method in your application. This is quite useful for
finding *where* time is spent in the startup or general performance of
your .NET applications.

Note that you can skip this step if the Android application is running on an
Android emulator; it is only required for physical Android devices.
To use `dotnet-trace` on Android, the following tools/components work
together to make this happen:

```sh
$ adb reverse tcp:9000 tcp:9001
```
This will forward port 9000 on device to port 9001.
* [`dotnet-trace`][dotnet-trace] itself is a .NET global tool.

_Alternatively:_
```sh
$ adb reverse tcp:0 tcp:9001
43399
```
This will allocate a random port on remote and forward it to port 9001 on the host. The forwarded port is printed by adb
* [`dotnet-dsrouter`][dotnet-dsrouter] is a .NET global tool that
forwards a connection from a remote Android or iOS device or
emulator to a local port on your development machine.

### Configure the device so that the profiled app suspends until tracing utility connects
* [`dotnet-gcdump`][dotnet-gcdump] is a .NET global tool that can be
used to collect memory dumps of .NET applications.

```sh
$ adb shell setprop debug.mono.profile '127.0.0.1:9000,suspend'
```
* The Mono Diagnostic component, `libmono-component-diagnostics_tracing.so`,
is included in the application and is used to collect the trace data.

See the [`dotnet-trace` documentation][dotnet-trace] for further details about its usage.

### Install `dotnet-dsrouter`
[dotnet-trace]: https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-trace
[dotnet-dsrouter]: https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-dsrouter
[dotnet-gcdump]: https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-gcdump

Generally, you can use a stable `dotnet-dsrouter` from NuGet:
## Install .NET Global Tools

Generally, you can install the required tooling such as:

```sh
$ dotnet tool install -g dotnet-trace
You can invoke the tool using the following command: dotnet-trace
Tool 'dotnet-trace' was successfully installed.
$ dotnet tool install -g dotnet-dsrouter
You can invoke the tool using the following command: dotnet-dsrouter
Tool 'dotnet-dsrouter' was successfully installed.
$ dotnet tool install -g dotnet-gcdump
You can invoke the tool using the following command: dotnet-gcdump
Tool 'dotnet-gcdump' was successfully installed.
```

Or use a build from the nightly feed `https://aka.ms/dotnet-tools/index.json`:
You can also install prerelease builds from the nightly feed
`https://aka.ms/dotnet-tools/index.json`:

```sh
$ dotnet tool install -g dotnet-dsrouter --add-source=https://aka.ms/dotnet-tools/index.json --prerelease
You can invoke the tool using the following command: dotnet-dsrouter
Tool 'dotnet-dsrouter' was successfully installed.
```

### Start the tracing router/proxy on host
## Configuration & Setup

For profiling an Android application running on an Android emulator:
```sh
$ dotnet-dsrouter android-emu --verbose debug
WARNING: dotnet-dsrouter is a development tool not intended for production environments.
### Running `dotnet-dsrouter` on the Host

For profiling an Android application running on an Android *emulator*:

Start an application on android emulator with one of the following environment variables set:
```sh
$ dotnet-dsrouter android-emu
How to connect current dotnet-dsrouter pid=1234 with android emulator and diagnostics tooling.
Start an application on android emulator with ONE of the following environment variables set:
[Default Tracing]
DOTNET_DiagnosticPorts=10.0.2.2:9000,nosuspend,connect
[Startup Tracing]
DOTNET_DiagnosticPorts=10.0.2.2:9000,suspend,connect
Run diagnotic tool connecting application on android emulator through dotnet-dsrouter pid=1234:
dotnet-trace collect -p 1234
See https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-dsrouter for additional details and examples.

info: dotnet-dsrouter-1234[0]
Starting dotnet-dsrouter using pid=1234
info: dotnet-dsrouter-1234[0]
Starting IPC server (dotnet-diagnostic-dsrouter-1234) <--> TCP server (127.0.0.1:9000) router.
```

info: dotnet-dsrouter[0]
Starting dotnet-dsrouter using pid=21352
dbug: dotnet-dsrouter[0]
Using default IPC server path, dotnet-diagnostic-dsrouter-21352.
dbug: dotnet-dsrouter[0]
Attach to default dotnet-dsrouter IPC server using --process-id 21352 diagnostic tooling argument.
info: dotnet-dsrouter[0]
Starting IPC server (dotnet-diagnostic-dsrouter-21352) <--> TCP server (127.0.0.1:9000) router.
dbug: dotnet-dsrouter[0]
Trying to create new router instance.
dbug: dotnet-dsrouter[0]
Waiting for a new TCP connection at endpoint "127.0.0.1:9000".
dbug: dotnet-dsrouter[0]
Waiting for new ipc connection at endpoint "dotnet-diagnostic-dsrouter-21352".
For profiling an Android application running on an Android *device*:

```sh
# `adb reverse` is required when using hardware devices
$ adb reverse tcp:9000 tcp:9001
$ dotnet-dsrouter android
How to connect current dotnet-dsrouter pid=1234 with android device and diagnostics tooling.
Start an application on android device with ONE of the following environment variables set:
[Default Tracing]
DOTNET_DiagnosticPorts=127.0.0.1:9000,nosuspend,connect
[Startup Tracing]
DOTNET_DiagnosticPorts=127.0.0.1:9000,suspend,connect
Run diagnotic tool connecting application on android device through dotnet-dsrouter pid=1234:
dotnet-trace collect -p 1234
...
```

### For Android devices
### Android System Properties

For profiling an Android application running on an Android device:
Note the log message that `dotnet-dsrouter` prints that mentions
`$DOTNET_DiagnosticPorts`. `$DOTNET_DiagnosticPorts` is an environment
variable that could be defined in an `@(AndroidEnvironment)` file, but
it is simpler to use the `debug.mono.profile` Android system property.
Android system properties can be used without rebuilding the app.

For emulators, `$DOTNET_DiagnosticPorts` should specify an IP address
of 10.0.2.2:

```sh
$ adb shell setprop debug.mono.profile '10.0.2.2:9000,suspend,connect'
```
$ dotnet-dsrouter server-server -tcps 127.0.0.1:9001 --verbose debug

For devices, `$DOTNET_DiagnosticPorts` should specify an IP address of
127.0.0.1, and the port number should be the [port used used with adb
reverse](#start-the-tracing-routerproxy-on-host), e.g:

```sh
# `adb reverse` is required when using hardware devices
$ adb reverse tcp:9000 tcp:9001
$ adb shell setprop debug.mono.profile '127.0.0.1:9000,suspend,connect'
```

Eventually, we will be able to simply do `dotnet-dsrouter android` when
[dotnet/diagnostics#4337][4337] is resolved. `adb reverse tcp:9000 tcp:9001` is
also currently required as mentioned above.
`suspend` is useful as it blocks application startup, so you can
actually `dotnet-trace` startup times of the application.

If you are wanting to collect a `gcdump` or just get things working,
try `nosuspend` instead. See the [`dotnet-dsrouter`
documentation][nosuspend] for further information.

[4337]: https://github.com/dotnet/diagnostics/issues/4337
[nosuspend]: https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-dsrouter#collect-a-trace-using-dotnet-trace-from-a-net-application-running-on-android

### Start the tracing client
### Running `dotnet-trace` on the Host

First, run `dotnet-trace ps` to find a list of processes:

```
```sh
> dotnet-trace ps
38604 dotnet-dsrouter C:\Users\myuser\.dotnet\tools\dotnet-dsrouter.exe "C:\Users\myuser\.dotnet\tools\dotnet-dsrouter.exe" android-emu --verbose debug
```
Expand All @@ -95,7 +142,7 @@ connect *through it* appropriately.

Using the process ID from the previous step, run `dotnet-trace collect`:

```
```sh
$ dotnet-trace collect -p 38604 --format speedscope
No profile or providers specified, defaulting to trace profile 'cpu-sampling'

Expand All @@ -107,15 +154,23 @@ Waiting for connection on /tmp/maui-app
Start an application with the following environment variable: DOTNET_DiagnosticPorts=/tmp/maui-app
```

The `--format` argument is optional and it defaults to `nettrace`. However, `nettrace` files can be viewed only with
Perfview on Windows, while the speedscope JSON files can be viewed "on" Unix by uploading them to https://speedscope.app
The `--format` argument is optional and it defaults to `nettrace`.
However, `nettrace` files can be viewed only with Perfview or Visual
Studio on Windows, while the speedscope JSON files can be viewed "on"
Unix by uploading them to [https://speedscope.app/][speedscope].

### Compile and run the application
[speedscope]: https://speedscope.app/

```
### Running the .NET Android Application

`$(AndroidEnableProfiler)` must be set to `true` as it includes the
Mono diagnostic component in the application. This component is the
`libmono-component-diagnostics_tracing.so` native library.

```sh
$ dotnet build -f net8.0-android -t:Run -c Release -p:AndroidEnableProfiler=true
```
_NOTE: `-f net8.0-android` is only needed for projects with multiple `$(TargetFrameworks)`._
*NOTE: `-f net8.0-android` is only needed for projects with multiple `$(TargetFrameworks)`.*

Once the application is installed and started, `dotnet-trace` should show something similar to:

Expand All @@ -141,14 +196,8 @@ directory.

## How to get GC memory dumps?

If running on desktop, you can use the `dotnet-gcdump` global tool.
This can be installed via:

```dotnetcli
$ dotnet tool install --global dotnet-gcdump
```

To use it, for example:
If running on desktop, you can use the `dotnet-gcdump` global tool for
local processes. For example:

```sh
# `hw-readline` is a standard Hello World, with a `Console.ReadLine()` at the end
Expand All @@ -167,6 +216,7 @@ $ dotnet-gcdump collect -p 33972
Writing gcdump to '.../hw-readline/20230314_113922_33972.gcdump'...
Finished writing 5624131 bytes.
```

See the [`dotnet-gcdump` documentation][dotnet-gcdump]
for further details about its usage.

Expand All @@ -188,6 +238,9 @@ $ dotnet-gcdump collect -p 38604

This will create a `*.gcdump` file in the current directory.

Note that using `nosuspend` in the `debug.mono.profile` property is
useful, as it won't block application startup.

## Memory Dumps for Android in .NET 7

In .NET 7, we have to use th older, more complicated method for collecting
Expand Down Expand Up @@ -228,12 +281,11 @@ This saves a `foo.gcdump` that you can open in Visual Studio.
See the [dotnet/runtime documentation][gc-dumps-on-mono] for
additional details.

[dotnet-gcdump]: https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-gcdump
[mono-events]: https://github.com/dotnet/runtime/blob/c887c92d8af4ce65b19962b777f96ae8eb997a42/src/coreclr/vm/ClrEtwAll.man#L7433-L7468
[dotnet-trace-help]: https://github.com/dotnet/diagnostics/blob/6d755e8b5435b1380c118e9d81e075654b0330c9/documentation/dotnet-trace-instructions.md#dotnet-trace-help
[gc-dumps-on-mono]: https://github.com/dotnet/runtime/blob/728fd85bc7ad04f5a0ea2ad0d4d8afe371ff9b64/docs/design/mono/diagnostics-tracing.md#collect-gc-dumps-on-monovm

## How to `dotnet trace` our build?
## How to `dotnet trace` a Build?

Setting this up is easy, the main issue is there end up being
potentially *a lot* of threads (30-40) depending on the build.
Expand Down

0 comments on commit 136dc83

Please sign in to comment.