Rebuild profiling on the MAUI CLI - #221
Merged
Merged
Conversation
Sherpa owned a large custom capture stack: prerequisite checks, diagnostic port and dsrouter planning, build/launch command generation, a multi-process runner and a five-step wizard. `maui profile` now owns that lifecycle, so replace the engine with a thin, typed integration over the global tool and keep only what Sherpa uniquely provides: guided UX, session history, artifact import/export, viewers and analysis. Capture engine - Add typed CLI contracts: tool status/update info, device records, profile request/result, lifecycle state and the canonical error envelope. - Add a deterministic command builder and a tolerant JSON stream parser that survives command echo, multi-line documents and unknown/additive fields. - Resolve the global tool shim explicitly before falling back to PATH, since GUI processes do not inherit shell PATH. - Add interactive stdin to IProcessExecutionService so Interaction mode's begin/stop newlines are distinct from cancellation. Capture UX - Replace the wizard with a single Configure/Running/Result modal covering project, mode, running target, output format and collapsed advanced options. - Support Startup and Interaction modes and nettrace/speedscope/mibc output. - Scope targets to running Android devices/emulators and booted iOS simulators. - Add a MAUI CLI status indicator to the Profiling page, mirroring the dotnetup indicator, with a details sheet that installs or updates the tool. Installs pin the resolved version because the package is prerelease-only. Work around an upstream CLI defect `maui profile` writes every artifact and then crashes serializing its own result, because MauiCliJsonContext lacks JsonSerializable for MauiProfileResult. It exits non-zero and prints an E1001 envelope on stdout with an empty stderr, so neither the exit code nor stderr can be trusted. Recover the result from the output folder, but only when the CLI produced no profile and either reported nothing or reported exactly that serialization failure. Genuine failures and cancellations still fail loudly. Reproduced on 0.1.0-preview.12.26368.2, so the recovery is not yet removable. Persistence - Move ProfilingSessionStorageService into Core and treat the CLI's result paths as authoritative instead of scanning for extensions. - Commit a session only after a valid result and a non-empty artifact, and discard pending directories on failure or abort. - Centralize artifact classification, add the MIBC kind and drop the regenerable multi-megabyte .etlx index on save. - Keep the manifest additive so existing sessions and archives still load. Remove the orchestration, runner and prerequisite layers along with their models, requests, handlers, registrations and command-plan tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5c34731b-bd7f-47de-b52b-ad36257ab3b4
MauiSherpa.Cli did not compile against System.CommandLine 2.0.10 (GA), which removed CommandLineConfiguration in favour of ParserConfiguration plus InvocationConfiguration. The entry point is now `command.Parse(args).InvokeAsync()`. Program.cs was the only break; the SetAction-based command definitions already match the GA API. The macOS toolbar's shared "create" button always read "New Secret" regardless of the active page. BlazorContentPage builds a superset of toolbar items once and toggles their visibility per page, so every page inherited the superset's default label. Assigning ToolbarItem.Text after construction has no effect because MAUI's macOS toolbar handler does not observe the property, so UpdateToolbarVisibility now writes the page's registered label straight onto the native NSToolbarItem (Label, PaletteLabel and ToolTip) in the same loop that already toggles enabled state. Verified via the accessibility API across all pages sharing the action: Create Emulator, Create Keystore, Create Certificate, Register Bundle ID, Register Device, Create Profile, New Profile and Capture Profile. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5c34731b-bd7f-47de-b52b-ad36257ab3b4
Both branches independently added standard input support to IProcessExecutionService: main added SendInputAsync for dotnetup's interactive prompts (#218), and this branch added WriteInputAsync to drive the MAUI CLI's start/stop recording prompts. Converged on main's SendInputAsync and removed WriteInputAsync. Main's version is the better contract: it returns false rather than throwing when the process can no longer accept input, requires callers to opt in via ProcessRequest.AcceptsStandardInput, rejects elevated processes, and serializes writes against Cancel() and process disposal so stdin cannot be closed mid-write. MauiProfilingCliService now sets AcceptsStandardInput for interaction captures and sends Environment.NewLine through SendInputAsync, throwing if the CLI has stopped accepting input. ProcessExecutionService keeps the improvements from both sides: main's input serialization plus this branch's output-builder locking and the Cancel() hardening that kills the process tree instead of only cancelling the linked token source. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5c34731b-bd7f-47de-b52b-ad36257ab3b4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sherpa's profiling feature predated
maui profile, so it carried its own capture pipeline: prerequisite probing, a capture planner, dsrouter and dotnet-trace orchestration, and a multi-step wizard to collect everything that pipeline needed. The MAUI CLI now does all of that natively, so most of that code was reimplementing something we can just call.This replaces the custom pipeline with a thin wrapper around
maui profileand collapses the wizard into a single dialog.Approach
The old orchestration layer is gone:
ProfilingCaptureOrchestrationService,ProfilingPrerequisitesService,ProfilingSessionRunnerService, and thePlanProfilingCapture/GetProfilingPrerequisitesrequest pairs. In their place:MauiProfileCommandBuilderturns a capture request into amaui profileargument list. Startup captures get--stopping-event-provider-name Microsoft.Maui.ProfilingHelper --stopping-event-event-name StartupCompleteso the CLI stops on its own.MauiProfilingCliServiceruns the process and streams--jsonevents throughMauiCliJsonStreamParser, which handles the CLI interleaving plain log lines with NDJSON envelopes on the same stream.MauiCliToolServiceresolves the tool (MauiCliExecutableResolver), reports the installed version, and checks NuGet for updates. The Profiling page now shows a MAUI CLI status chip in the top right, matching how the .NET SDK Manager surfacesdotnetup. It offers install when the tool is missing and update when a newer prerelease is available.ProfilingArtifactClassifierandProfilingSessionStorageService(moved into Core so it is testable) handle what lands on disk.The wizard became a single
ProfilingCaptureModal: project, capture mode, target, output format, all on one screen with the CLI status inline.Worth a look
Artifact recovery is a workaround, not a design choice.
maui profilecurrently writes every artifact successfully and then crashes serializing its own result, becauseMauiCliJsonContextis missing[JsonSerializable(typeof(MauiProfileResult))]. It exits 1 and prints anE1001envelope on stdout with empty stderr, so a naive exit-code check throws away a perfectly good capture.MauiProfileArtifactRecoveryinspects the output directory on failure and salvages the session when the artifacts are actually there. Still reproducible in0.1.0-preview.12.26368.2; this should come out once it is fixed upstream.Two unrelated fixes came along in the second commit, both pre-existing:
MauiSherpa.Clidid not compile against System.CommandLine 2.0.10 (GA), which removedCommandLineConfiguration. The entry point is nowroot.Parse(args).InvokeAsync(). One line, andProgram.cswas the only break.createbutton read "New Secret" on every page.BlazorContentPagebuilds one superset of toolbar items and toggles visibility per page, so every page inherited the superset default. AssigningToolbarItem.Textafter construction does nothing because MAUI's macOS handler does not observe it, so the label is now written onto the nativeNSToolbarItem. Verified across all eight pages sharing the action.Testing
498 Core tests passing. The deleted pipeline's tests were replaced with coverage for each new service, including the command builder's argument shaping, the JSON stream parser's mixed-content handling, and artifact recovery against the upstream crash.
Capture was exercised end to end against an Android emulator producing MIBC and speedscope output.