Workflow
- Read the WinUI performance guide for capture limits, query options, partial-result handling, privacy and interpretation.
- Use the existing app PID when possible. Start with
winapp perf start --app <pid> --output <empty-directory> --json.--appalso accepts a process name or window title using the same matching rules aswinapp ui. Use the returnedtarget.pidfor subsequent UI actions. For a new launch, usewinapp run <project> --profile <empty-directory> --detach --json. This records after launch, which is enough for navigation, scrolling and other post-launch scenarios. For startup investigations only, add--profile-mode elevated(one UAC prompt for the recorder, which the user must approve) and checkProfile.StartupCoverage; see the guide's startup workflow. Do not add startup delays or replace a generatedMain. - Wait for successful recording readiness. Save the returned capture ID.
- Mark the beginning with
winapp perf mark <id> --name scenario-start --json, drive a short repeatable scenario with verified UI selectors, wait for its visible outcome, then runwinapp perf mark <id> --name scenario-end --json. - Run
winapp perf stop <id> --jsonbefore closing the app. - Start with
winapp perf analyze <directory> --from-marker scenario-start --to-marker scenario-end --jsonto compare mutually exclusive primary-UI-thread activity, the hottest parsed XAML resources, and the detailed operation ranking. Use--view parsingover the same range when the parsing preview needs full paging. Then querywinapp perf analyze <directory> --view hotspots --from-marker scenario-start --to-marker scenario-end --json; its default threshold is 16.67 ms and each row includes dominant direct child operations. Expand a returned operation with--view call --id <call-id> --depth 2 --from-marker scenario-start --to-marker scenario-end --json. Use--view calls --family layoutwith the same marker range when the hotspot evidence points to layout. Query--view gc --sort durationover the same marker range to find the longest collection and suspension intervals, then inspect referenced interval IDs. Use element and event views for source and evidence drill-down. FollownextOffset; do not dump NDJSON or ETL into context. For overall resource cost, use--view resourcesover the same marker range; see the guide's process counters. For app-side composition, query--view calls --family compositionand--view events --event DCompDeviceCommitover the same marker range; see composition submission. - Report observed cost, supporting event IDs, coverage gaps, and one concrete next experiment. Preserve stdout when partial results accompany a nonzero exit code.
Guardrails
- Use
--profile-mode elevatedonly for startup, and tell the user to expect a UAC prompt. If they decline it, do not retry; fall back to the default attach mode and say that early startup may be missing. Otherwise never elevate, change tracing ACLs, or modify app code to make capture work. - Recording is local only. Do not combine
run --profilewith--on sandbox; see the guide for the supported launch and recording commands. - Readiness is not decoded coverage. Missing events are inconclusive.
- For WinUI operations, use elapsed-time terminology; do not claim CPU/GPU attribution, displayed FPS, proven input-to-display latency, or complete startup/visual-tree coverage.
Unclassifiedincludes idle, waits, and uninstrumented work. Do not describe it as app code or CPU use. Off-thread image decode is not UI-thread occupancy.- Process CPU counters include all target threads but do not attribute functions, modules, or waits. Sampled memory/thread maxima can miss brief spikes; missing samples are not zero resource use.
- Trace-local element IDs are not UI Automation selectors.
- Composition calls are elapsed API scopes, not GPU work or display latency.
DComp device commits are notifications, not timed or displayed frames; check
directCompositionProviderbefore interpreting missing events. - Call trees are instrumented operation scopes, not CPU stacks or visual trees. Keep exclusive scope time distinct from element self time.
- GC is requested by default but may be unavailable. Check
gcCoverageseparately; missing events are not zero pauses. Background GC duration is not suspension time, and overlapping runtime suspension is not proof that GC blocked the native UI. - Keep raw captures local unless the user has reviewed and authorized sharing them.
- Use the UI automation skill to interact with the app and troubleshooting/debug output workflows for crashes; neither replaces performance evidence.
