# iOS / macOS

Below is a guide for profiling .NET iOS and .NET MacOS applications:

{% embed url="<https://github.com/xamarin/xamarin-macios/wiki/Profiling>" %}

## Profiling in .NET

See [Profiling App Launch](https://github.com/xamarin/xamarin-macios/wiki/Profiling-App-Launch) for information about how to profile the app's launch.

> **Note** Only debug builds have profiling support enabled by default.

#### mlaunch

This document references the `mlaunch` tool, the easiest way to get it is to install Xamarin.iOS if you don't have it installed already. Latest package as of this writing: [xamarin.ios-15.10.0.5.pkg](https://download.visualstudio.microsoft.com/download/pr/09e9572f-1c2a-4d79-a1e8-5469de9410e8/3c7be8df7e8249f2c3142e4c10ae5523/xamarin.ios-15.10.0.5.pkg).

Once installed, the `mlaunch` tool can be found here:

> /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/bin/mlaunch

The `mlaunch` tool is also installed with the iOS workload for .NET, but the location on disk depends on the actual version installed, so it's a bit harder to find.

In any case, creating an alias will allow the instructions below to work:

```
alias mlaunch=/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/bin/mlaunch
```

Otherwise replace `mlaunch` with the full path according to the location on disk on your system.

### Mobile apps

#### Initial configuration

We need the `dotnet-dsrouter` and `dotnet-trace` tools, so let's install them:

```
$ dotnet tool install --global dotnet-dsrouter
$ dotnet tool install --global dotnet-trace
```

> **Note** This pull request for `dotnet-dsrouter` is required for profiling on device to work: <https://github.com/dotnet/diagnostics/pull/3134>

#### Simulator

1. The first step is to launch the tool that provides a connection between the app and the .NET tracing tools:

   ```
   $ dotnet-dsrouter client-server -ipcc ~/my-sim-port -tcps 127.0.0.1:9000
   ```
2. Launch the app and make it suspend upon launch (waiting for the .NET tooling to connect):

   ```
   $ mlaunch --launchsim bin/Debug/net*/*/*.app --device :v2:runtime=com.apple.CoreSimulator.SimRuntime.iOS-15-4,devicetype=com.CoreSimulator.SimDeviceType.iPhone-11 --wait-for-exit --stdout=$(tty) --stderr=$(tty) --argument --connection-mode --argument none '--setenv:DOTNET_DiagnosticPorts=127.0.0.1:9000,suspend'
   ```
3. At this point it's necessary to wait until the following line shows up in the terminal:

   > The runtime has been configured to pause during startup and is awaiting a Diagnostics IPC ResumeStartup command from a Diagnostic Port
4. Once that's printed, go ahead and start profiling:

   ```
   $ dotnet-trace collect --diagnostic-port ~/my-sim-port --format speedscope
   ```

**How to find a simulator to use**

Execute the following to get the list of all available simulators on your machine:

```
$ xcrun simctl list devices
== Devices ==
-- iOS 12.4 --
    iPhone 6 (iOS 12.4) (C49E6049-5F51-40BB-833A-1B36C4EB95A6) (Booted)
-- iOS 16.0 --
    iPhone X (iOS 16.0) (50BCC90D-7E56-4AFB-89C5-3688BF345998) (Booted)
    iPhone SE (3rd generation) - iOS 16.0 (BE619DC5-F728-4E2F-9425-4731778A78FC) (Shutdown)
    iPad Air (5th generation) - iOS 16.0 (E6C465F0-43E5-4066-A2E9-D293254CBD30) (Shutdown)
-- tvOS 16.0 --
    Apple TV (tvOS 16.0) (ED020B77-8E3D-4BB7-B7D7-E94ECCCD62BE) (Shutdown)
-- watchOS 6.0 --
    Apple Watch Series 3 - 38mm (watchOS 6.0) (BB2AB52D-803B-42EA-A456-66ACBF90DF25) (Booted)
-- watchOS 9.0 --
    Apple Watch Series 7 - 41mm (watchOS 9.0) (2FAEA489-CD7F-42D9-9488-9B21858BB30F) (Booted)
```

Then pick an applicable device, and copy the UDID (`50BCC90D-7E56-4AFB-89C5-3688BF345998` would be an example here), and pass the following to `mlaunch`:

```
$ mlaunch ... --device :v2:udid=50BCC90D-7E56-4AFB-89C5-3688BF345998 ...
```

It's also possible to specify a runtime/devicetype combination; the valid values can be queried with `simctl` like this:

```
$ xcrun simctl list devicetypes
== Device Types ==
iPhone 4s (com.apple.CoreSimulator.SimDeviceType.iPhone-4s)
iPhone 5 (com.apple.CoreSimulator.SimDeviceType.iPhone-5)
iPhone 5s (com.apple.CoreSimulator.SimDeviceType.iPhone-5s)
iPhone 6 Plus (com.apple.CoreSimulator.SimDeviceType.iPhone-6-Plus)
iPhone 6 (com.apple.CoreSimulator.SimDeviceType.iPhone-6)
iPhone 6s (com.apple.CoreSimulator.SimDeviceType.iPhone-6s)
iPhone 6s Plus (com.apple.CoreSimulator.SimDeviceType.iPhone-6s-Plus)
iPhone SE (1st generation) (com.apple.CoreSimulator.SimDeviceType.iPhone-SE)
iPhone 7 (com.apple.CoreSimulator.SimDeviceType.iPhone-7)
iPhone 7 Plus (com.apple.CoreSimulator.SimDeviceType.iPhone-7-Plus)
iPhone 8 (com.apple.CoreSimulator.SimDeviceType.iPhone-8)
iPhone 8 Plus (com.apple.CoreSimulator.SimDeviceType.iPhone-8-Plus)
iPhone X (com.apple.CoreSimulator.SimDeviceType.iPhone-X)
iPhone Xs (com.apple.CoreSimulator.SimDeviceType.iPhone-XS)
[...]
```

and

```
$ xcrun simctl list runtimes available
== Runtimes ==
iOS 12.4 (12.4 - 16G73) - com.apple.CoreSimulator.SimRuntime.iOS-12-4
iOS 13.5 (13.5 - 17F61) - com.apple.CoreSimulator.SimRuntime.iOS-13-5
iOS 15.4 (15.4 - 19E240) - com.apple.CoreSimulator.SimRuntime.iOS-15-4
tvOS 12.4 (12.4 - 16M567) - com.apple.CoreSimulator.SimRuntime.tvOS-12-4
tvOS 15.4 (15.4 - 19L439) - com.apple.CoreSimulator.SimRuntime.tvOS-15-4
tvOS 16.0 (16.0 - 20J5299n) - com.apple.CoreSimulator.SimRuntime.tvOS-16-0
tvOS 16.0 (16.0 - 20J5319f) - com.apple.CoreSimulator.SimRuntime.tvOS-16-0
watchOS 6.0 (6.0 - 17R575) - com.apple.CoreSimulator.SimRuntime.watchOS-6-0
watchOS 6.2 (6.2.1 - 17T531) - com.apple.CoreSimulator.SimRuntime.watchOS-6-2
watchOS 8.5 (8.5 - 19T241) - com.apple.CoreSimulator.SimRuntime.watchOS-8-5
watchOS 9.0 (9.0 - 20R5287q) - com.apple.CoreSimulator.SimRuntime.watchOS-9-0
watchOS 9.0 (9.0 - 20R5307f) - com.apple.CoreSimulator.SimRuntime.watchOS-9-0
```

and then finally pass the selected values to `mlaunch` like this:

```
$ mlaunch ... --device :v2:runtime=com.apple.CoreSimulator.SimRuntime.iOS-15-4,devicetype=com.CoreSimulator.SimDeviceType.iPhone-X ...
```

#### Device

> **Note** This pull request for `dotnet-dsrouter` is required for profiling on device to work: <https://github.com/dotnet/diagnostics/pull/3134>

The first step is to connect an iOS device to the Mac using a USB cable.

The process is very similar to the process for the simulator:

1. Launch the tool that bridges the app and the .NET tracing tools:

   ```
   $ dotnet-dsrouter server-client -ipcs ~/my-dev-port -tcpc 127.0.0.1:9001 --forward-port iOS
   ```

   Compared to the simulator, this:

   * Adds `--forward-port iOS`
   * Changes the local ports, both client (`~/my-dev-port` vs `~/my-sim-port`) and server (`:9001` vs `:9000`) - it's easier to debug any problems when using different ports.
2. Install & launch the app and make it suspend upon launch:

   ```
   $ mlaunch --installdev bin/Debug/net*/*/*.app --devname ... 
   $ mlaunch --launchdev bin/Debug/net*/*/*.app --devname ... --wait-for-exit --argument --connection-mode --argument none '--setenv:DOTNET_DiagnosticPorts=127.0.0.1:9001,suspend,listen'
   ```
3. At this point it's necessary to wait until the following line shows up in the terminal:

   > The runtime has been configured to pause during startup and is awaiting a Diagnostics IPC ResumeStartup command from a Diagnostic Port
4. Once that's printed, go ahead and start profiling:

   ```
   $ dotnet-trace collect --diagnostic-port ~/my-dev-port,connect --format speedscope
   ```

#### Desktop

Profiling on the desktop (both macOS and Mac Catalyst apps are the same) is much easier.

1. Launch the executable, passing the `DOTNET_DiagnosticPorts` variable directly:

   ```
   $ DOTNET_DiagnosticPorts=~/my-desktop-port,suspend ./bin/Debug/net6.0-*/*/MyTestApp.app/Contents/MacOS/MyTestApp
   ```
2. At this point it's necessary to wait until the following line shows up in the terminal:

   > The runtime has been configured to pause during startup and is awaiting a Diagnostics IPC ResumeStartup command from a Diagnostic Port
3. Once that's printed, go ahead and start profiling:

   ```
   $ dotnet-trace collect --diagnostic-port ~/my-desktop-port --format speedscope
   ```

#### Analyzing the results

In this guide I've selected to save the profiling results as speedscope files. These can be viewed in various ways, for instance online at [https://speedscope.app](https://speedscope.app/).

#### References

* [https://speedscope.app](https://speedscope.app/)
* <https://devblogs.microsoft.com/dotnet/performance-improvements-in-dotnet-maui/>
* <https://github.com/dotnet/runtime/blob/main/docs/design/mono/diagnostics-tracing.md>
* <https://github.com/xamarin/xamarin-android/blob/main/Documentation/guides/tracing.md>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.wisej.com/hybrid/application-profiling/ios-macos.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
