Skip to content

feat: support Android and iOS simulator startup profiling in maui profile#60

Merged
jfversluis merged 23 commits into
dotnet:mainfrom
simonrozsival:feature/maui-profile-command
Apr 16, 2026
Merged

feat: support Android and iOS simulator startup profiling in maui profile#60
jfversluis merged 23 commits into
dotnet:mainfrom
simonrozsival:feature/maui-profile-command

Conversation

@simonrozsival
Copy link
Copy Markdown
Member

@simonrozsival simonrozsival commented Apr 2, 2026

Closes #54

Summary

This PR adds a new maui profile startup flow for collecting startup traces from MAUI apps.

The goal is to make startup profiling easy to use from the CLI: build the app, launch it on the selected target, collect the trace, and save the result in a format you can inspect right away.

How to use it

Interactive:

maui profile startup

Similarly to dotnet run, this will show pickers for the TFM, target device and the output format.

Android:

maui profile startup \
  --project MyApp.csproj \
  --framework net11.0-android \
  --device <device-id> \
  --duration 00:00:10 \
  --format nettrace \
  --output ./artifacts/startup.nettrace

iOS simulator:

maui profile startup \
  --project MyApp.csproj \
  --framework net11.0-ios \
  --device <simulator-udid> \
  --duration 00:00:10 \
  --format speedscope \
  --output ./artifacts/startup.speedscope.json

What this PR adds

  • maui profile startup support for MAUI app startup traces
  • Android support
  • iOS simulator support
  • nettrace and speedscope output options
  • cleaner device routing and trace collection behavior
  • improved handling for startup trace output and failure cases

Notes

  • This is focused on the startup-tracing command experience itself.
  • The command defaults to a Release build.
  • Physical iOS devices and Mac Catalyst are not being presented as fully covered in this PR.

simonrozsival and others added 6 commits April 2, 2026 18:49
Adds a new top-level `maui profile` command that collects a startup
.nettrace for a .NET MAUI Android app using dotnet-trace + dotnet-dsrouter.

## What's new

### `maui profile` command (ProfileCommand.cs)
- Two-phase build: compile-only phase 1, then deploy+launch in phase 2
  so the APK is built once and the trace collector starts before the app.
- Starts dotnet-trace with `--dsrouter android-emu` (emulator) or
  `--dsrouter android` (physical device) for startup tracing.
- Passes `DiagnosticSuspend=true` and `--resume-runtime` so the
  beginning of app startup is never missed.
- Supports `--stopping-event-provider-name` / `--stopping-event-event-name`
  for automatic stop when the app signals startup is complete.
- Automatically injects `dotnet-common,dotnet-sampled-thread-time` profiles
  alongside the stopping event provider so runtime and CPU-sampling events
  are still collected (specifying `--providers` alone would suppress the
  dotnet-trace defaults).
- Clears inherited MSBuild SDK env vars before spawning child builds to
  prevent SDK version pollution when invoked via `dotnet run`.
- Resolves macOS `/tmp` symlinks to avoid MAUI SourceGen MAUIG1001 errors.

### `Microsoft.Maui.StartupProfiling` helper package
- Tiny opt-in assembly: add the NuGet reference and call
  `StartupProfilingMarker.Complete()` at the logical end of startup.
- `[ModuleInitializer]` pre-registers the EventSource provider so
  dotnet-trace sees it before any user code runs.
- `MAUI_STARTUP_PROFILING_AUTO_EXIT=1` env var causes the app to call
  `Environment.Exit(0)` after emitting the marker — useful in CI.
- `IsProfilingSession` property lets apps skip work that skews traces.

### ProcessRunner changes
- Added `environmentVariablesToRemove` parameter to strip inherited env
  vars before spawning subprocesses.

### Diagnostic improvements
- `SpectreOutputFormatter` now shows native error output in error messages.
- New error codes for prerequisite and config validation failures.

## Key technical notes
- `--profile cpu-sampling` is incompatible with `--dsrouter` (Linux kernel
  perf only); `dotnet-sampled-thread-time` uses EventPipe and works.
- `--stopping-event-provider-name` configures the stop condition but does
  NOT add the provider to the EventPipe session; `--providers` must also
  include it explicitly for events to be delivered.
- Default collection (dotnet-common + dotnet-sampled-thread-time) must be
  injected explicitly whenever `--providers` is specified, because any
  `--providers` arg suppresses the implicit defaults.

Closes dotnet#54

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…SDK)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
eng/Common.props packs the repo-root README into every package at '/README.md'.
The StartupProfiling project also includes its own README at the same path,
causing NU5118 (treated as error in CI via --ci). Suppress NU5118 so the
package-specific README is used.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival marked this pull request as ready for review April 10, 2026 18:13
Copilot AI review requested due to automatic review settings April 10, 2026 18:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an Android-first maui profile workflow to the CLI, including a build-time injected startup marker helper and orchestration of dotnet-dsrouter + dotnet-trace for startup tracing output (nettrace / speedscope).

Changes:

  • Introduces a new profile top-level CLI command with Android device selection, Release-by-default builds, and trace collection orchestration.
  • Adds a new Microsoft.Maui.StartupProfiling helper package plus MSBuild injection assets to auto-register an EventSource and emit a startup-complete marker.
  • Improves CLI error presentation by printing captured native/tool output when available, and extends ProcessRunner to remove inherited environment variables.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/Cli/Microsoft.Maui.StartupProfiling/StartupProfilingMarker.cs Adds public marker API, module initializer, diagnostics logging, and exit-control channel logic.
src/Cli/Microsoft.Maui.StartupProfiling/StartupProfilingEventSource.cs Adds EventSource provider and events used for stopping-event integration.
src/Cli/Microsoft.Maui.StartupProfiling/README.md Documents helper package usage and environment variables.
src/Cli/Microsoft.Maui.StartupProfiling/Microsoft.Maui.StartupProfiling.csproj New packable helper library project for the injected marker.
src/Cli/Microsoft.Maui.Cli/Utils/ProcessRunner.cs Adds support for removing inherited environment variables before process start.
src/Cli/Microsoft.Maui.Cli/Program.cs Registers the new profile command at the CLI root.
src/Cli/Microsoft.Maui.Cli/Output/SpectreOutputFormatter.cs Displays NativeError details under CLI errors when present.
src/Cli/Microsoft.Maui.Cli/Microsoft.Maui.Cli.csproj References the helper project and ships MSBuild injection assets alongside the tool.
src/Cli/Microsoft.Maui.Cli/Errors/ErrorCodes.cs Adds a new diagnostics tooling error code.
src/Cli/Microsoft.Maui.Cli/Commands/ProfileCommand.cs Implements the maui profile command, device/TFM/format resolution, and trace/dsrouter orchestration.
src/Cli/Microsoft.Maui.Cli/Build/MauiStartupProfilingInjection.targets MSBuild targets to inject the helper assembly/source and Android env vars into the app build.
src/Cli/Microsoft.Maui.Cli/Build/MauiStartupProfiling.AutoInitialize.cs Injected bootstrap that detects first UI readiness and signals startup complete.
src/Cli/Microsoft.Maui.Cli.UnitTests/ProfileCommandTests.cs Adds unit tests for option defaults, argument building, and resolver behaviors.
src/Cli/Cli.slnf Adds the new helper project to the CLI solution filter.
MauiLabs.slnx Adds the new helper project to the repo solution.

Comment thread src/Cli/Microsoft.Maui.StartupProfiling/StartupProfilingMarker.cs Outdated
Comment thread src/Cli/Microsoft.Maui.StartupProfiling/StartupProfilingEventSource.cs Outdated
Comment thread src/Cli/Microsoft.Maui.Cli/Commands/ProfileCommand.cs Outdated
Comment thread src/Cli/Microsoft.Maui.Cli/Build/MauiStartupProfilingInjection.targets Outdated
simonrozsival and others added 5 commits April 10, 2026 23:37
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add elapsed timers to Spectre status spinners, route cancellations through a shared handler so Ctrl+C reports as Cancelled, and keep trace stderr quiet unless verbose mode is enabled.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Enable iOS simulator device discovery and startup tracing in the profile command, add retry handling for iOS diagnostics startup races, and default simulator runs to Debug because it produces reliable traces while Release remains experimental.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore Release as the default iOS profiling configuration and prefer installed dotnet-trace/dotnet-dsrouter tools when available so simulator tracing follows the MAUI guidance again.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Allow maui profile validation to accept setups where one required diagnostics tool is installed globally and the other is resolved from the NuGet cache, matching the actual runtime resolution behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival changed the title feat: add maui profile command for Android startup tracing feat: add maui profile command for startup tracing Apr 11, 2026
simonrozsival and others added 3 commits April 12, 2026 01:25
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival changed the title feat: add maui profile command for startup tracing feat: support iOS simulator startup profiling in maui profile Apr 12, 2026
@simonrozsival simonrozsival changed the title feat: support iOS simulator startup profiling in maui profile feat: support Android and iOS simulator startup profiling in maui profile Apr 12, 2026
simonrozsival and others added 2 commits April 14, 2026 12:16
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Member

@jonathanpeppers jonathanpeppers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplification suggestions for the profiling commands:

  1. Use dotnet-trace --dsrouter android|android-emu|ios|ios-sim instead of manually launching dotnet-dsrouter as a separate process. This should allow removing DotnetDsrouterRunner.cs, ProfileTransportConfiguration, the server-server/server-client branching in ResolveProfileTransport(), dsrouter PID tracking, and the StartTraceAfterLaunch retry logic that differs between Android and iOS.

  2. Drop -p:AndroidEnableProfiler=true — the code already passes -p:EnableDiagnostics=true (cross-platform) in AppendDiagnosticArguments, so the Android-specific property in BuildLaunchArguments is redundant.

  3. Use -p:Device={device.Id} instead of the platform-specific -p:AdbTarget=-s {device.Id} (Android) and -p:_DeviceName=:v2:udid={device.Id} (iOS). -p:Device is cross-platform and initializes the platform-specific properties automatically.

simonrozsival and others added 6 commits April 15, 2026 14:42
Use runtime-owned EventPipe for Android/CoreCLR startup runs, simplify the attach path with dotnet-trace --dsrouter, and align the flow with the verified TestDummy MIBC results.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve device-manager conflicts and keep the startup profiling changes aligned with the latest main branch updates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jfversluis jfversluis merged commit 1aed357 into dotnet:main Apr 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Proposal] Add startup profiling capabilities

4 participants