Skip to content

App Profiling & MauiDevFlow Platform Features#108

Closed
Redth wants to merge 67 commits intomainfrom
feature/app-profiling
Closed

App Profiling & MauiDevFlow Platform Features#108
Redth wants to merge 67 commits intomainfrom
feature/app-profiling

Conversation

@Redth
Copy link
Copy Markdown
Owner

@Redth Redth commented Mar 11, 2026

Summary

Adds comprehensive .NET profiling support and a new Platform Features tab to the MauiDevFlow inspector. 116 files changed, ~17k lines added across 67 commits.

Profiling

  • Profiling workspace with session management (list, view, delete captured sessions)
  • CPU tracing via dotnet-trace with on-demand Start/Stop controls
  • GC/Memory dumps via dotnet-gcdump with one-click capture
  • Speedscope viewer — opens .speedscope.json traces in an embedded viewer window
  • GC Dump viewer — custom Blazor-based viewer for .gcdump files showing type statistics, object graphs, and retained memory
  • Capture orchestration — handles dotnet-dsrouter, port forwarding, and platform-specific configuration for Android, iOS, Mac Catalyst, macOS, and Windows
  • Prerequisites checking — validates dotnet-trace, dotnet-gcdump, dotnet-dsrouter are installed
  • Platform-specific native memory labels from MauiDevFlow agent

MauiDevFlow Inspector – Platform Tab

New "Platform" tab with 5 sub-tabs:

  • Info — App Info, Device Info, Display, Battery, Connectivity, Version Tracking (with per-card error handling and friendly permission warnings)
  • Storage — Preferences browser (list/set/delete) and Secure Storage lookup
  • Permissions — Platform permission status grid with refresh
  • Sensors — Start/stop sensors, stream multiple simultaneously with live inline readings and Stop All button
  • Location — Get current geolocation with configurable accuracy

Other Improvements

  • Upgraded MauiDevFlow NuGet packages to 0.23.0
  • Better error messages for permission failures (amber warnings instead of red errors)
  • Fixed cloud keystore deletion bugs
  • Added macOS AppKit ShowInputDialogAsync implementation
  • 756 new test assertions across profiling services

MauiDevFlow Issues/PRs Filed

  • #34 — UIKit thread error in platform handlers → PR #35
  • #37 — Structured error reason codes
  • #39 — Preferences crash with non-string types → PR #40

Redth and others added 30 commits March 10, 2026 16:10
- Refactor Profiling.razor from single scrolling form into 6-step wizard:
  Session Setup → Capture Kinds → Target → Review & Plan → Capture → Results
- Add horizontal step indicator, Next/Back navigation, step validation
- Move advanced session fields into collapsible section
- Auto-run prerequisites and plan generation on entering Review step
- Add placeholder steps for Capture Running and Results (pipeline runner TBD)

- Update ProfilingCaptureOrchestrationService to prefer integrated --dsrouter:
  dotnet-trace/dotnet-gcdump now use --dsrouter android|android-emu|ios|ios-sim
  instead of separate dotnet-dsrouter process step
- Add ProfilingStopTrigger enum and CanRunParallel/StopTrigger to step model
- Set parallel/stop metadata on trace, launch, memory, and log steps
- Keep CreateDsRouterStep as documented fallback

- Update orchestration tests for new --dsrouter integrated commands
- Verify no standalone dsrouter step in mobile plans
- Assert CanRunParallel and StopTrigger on generated steps

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

Replace opaque GUID-based output directories (artifacts/profiling/{guid}/)
with human-readable paths (artifacts/profiling/{ProjectName}/{date}-{run}/).

- Add BuildDefaultOutputDirectory method that derives the project name from
  the project path and auto-increments run numbers per date
- Simplify artifact filenames: trace.nettrace, memory.gcdump, logs.txt
- Update Profiling.razor placeholder text to reflect the new pattern
- Add tests for output directory naming, artifact filenames, and fallback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add IProfilingSessionRunner interface with RunAsync, StopCapture, Cancel
- Add ProfilingSessionRunnerService with dependency-ordered execution,
  parallel step support, long-running process management, SIGINT stop
- Add ProfilingPipelineModels (state enums, step status, pipeline result)
- Remove per-command 'Run in Sherpa' buttons from step 3 (Review & Plan)
- Implement step 4 (Capture): live pipeline status, per-step cards with
  expandable output logs, Stop Capture and Cancel buttons
- Implement step 5 (Results): artifact listing with file sizes, Reveal
  in Finder, Open in speedscope, Copy path, session summary, New Session
- Register IProcessExecutionService as Transient (per-step instances)
- Register IProfilingSessionRunner as Transient

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…cutionService to Transient

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

When both dotnet-trace and dotnet-gcdump are requested on a mobile target,
only one can use --dsrouter at a time. This change:

- Detects when both trace and memory capture are requested on mobile
- Falls back to a standalone dotnet-dsrouter process in that case
- Both tools connect via --diagnostic-port using the IPC socket address
- Introduces isMobileTarget flag to separate mobile detection from dsrouter arg
- Fixes process discovery logic to not trigger for mobile targets
- Adds test for trace-only mobile capture (still uses inline --dsrouter)
- Updates existing mobile tests to expect standalone dsrouter behavior

All 270 tests pass (223 Core + 47 Workloads).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
macOS limits Unix domain socket paths to 104 characters. The previous
naming pattern 'maui-sherpa-profile-{full-guid}.sock' was 108 chars
when combined with the temp directory path, causing dsrouter and
dotnet-trace to crash with ArgumentOutOfRangeException.

Shortened to 'ms-prof-{8-char-guid}.sock' (~75 chars total).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When using --diagnostic-port with a standalone dsrouter, both dotnet-trace
and dotnet-gcdump must connect as clients (not create their own server).
Append ',connect' to the IPC address so tools use connect mode instead
of the default listen mode which tries to bind the same socket.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When multiple Android devices/emulators are connected, adb commands
(used by dsrouter for port forwarding) fail without knowing which
device to target. Set ANDROID_SERIAL environment variable to the
selected device identifier on all dsrouter-related steps (standalone
dsrouter, dotnet-trace, dotnet-gcdump).

Extracted BuildAndroidEnvironment() helper to set both ANDROID_HOME
and ANDROID_SERIAL consistently across all Android profiling steps.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Auto-scroll:
- Created logScrollInterop.js with track/scrollToBottom/untrack methods
- Log containers auto-scroll to bottom as output arrives
- Pauses auto-scroll when user scrolls up manually
- Re-engages when user scrolls back to bottom
- Tracks per-container via element ID

App launch fix:
- Changed SuspendAtStartup default from true to false
- Prevents profiled apps from hanging on splash screen waiting for
  diagnostic tool connection (the previous default suspended the runtime)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When user clicks Stop Capture, all running steps should be treated as
'stopped' (not failed), artifacts should be collected, and the pipeline
should transition to Completed with results.

Changes:
- Added _stopRequested flag to ProfilingSessionRunnerService
- StopCapture() now marks ALL running steps as Stopped (not just
  ManualStop ones) since killing dsrouter cascades to dependent steps
- LaunchStepAsync treats non-zero exits as 'stopped' when _stopRequested
- RunAsync catch block detects stop-during-shutdown and collects
  artifacts instead of reporting failure
- Pipeline transitions to Completed (with partial results) after stop

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create NativeMainPage.cs with full native MAUI controls for all 4 scenarios
  (CPU stress, memory allocation/churn, rendering tile wall + feed, network bursts)
- Add RunNativeNetworkBurstAsync to ProfilingScenarioService (HttpClient-based,
  replaces JS fetch for native mode)
- Fix BlazorMainPage constructor name (was still MainPage after rename)
- Native page includes: metrics strip (6 process/GC labels), scenario cards with
  sliders/buttons/status labels, tile wall with FlexLayout, feed list, network
  results list
- App defaults to native page with toolbar button to switch to Blazor view
- Dark theme styling matching Blazor version

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove --format speedscope from dotnet-trace args — it caused the output
  file to be written as trace.nettrace.speedscope.json while we looked for
  trace.nettrace. Raw .nettrace is the most useful format anyway.
- Fix CollectArtifacts double path nesting — RelativePath already contains
  the full path from CWD (including OutputDirectory), but the old code
  prepended OutputDirectory again, making files unfindable.
- Use Path.GetFullPath for consistent absolute path resolution.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Make OutputDirectory absolute at plan creation time by resolving
  relative paths against the project's working directory. The macOS app
  runs from inside the .app bundle, so relative paths resolved to the
  wrong location when checking for artifacts.
- Write per-step stdout/stderr to {stepId}.log files in the output
  directory. Each log includes a header (command, timestamp) and footer
  (exit code, duration, errors). Logs stream in real-time via AutoFlush.
- Update test to expect absolute output directory path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dotnet-trace needs time to finalize the .nettrace file after receiving
SIGINT. Previously, Cancel() sent SIGINT then immediately cancelled the
linked CancellationTokenSource, which caused WaitForExitAsync to throw
and return before the process had flushed its output. Now Cancel() sends
SIGINT and waits up to 15s for the process to exit before cancelling the
token, giving tools time to write their output files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The core issue: Cancel() sent SIGINT then immediately cancelled the
CancellationToken, which unblocked WaitForExitAsync before the process
had time to flush output files (dotnet-trace needs several seconds to
finalize .nettrace). Now Cancel() sends SIGINT but does NOT cancel the
token — WaitForExitAsync naturally completes when the process exits
after flushing. A 30s safety timeout force-cancels only if the process
hangs. Also made StopCapture async (StopCaptureAsync) on the interface.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace incorrect MSBuild properties (DiagnosticAddress, DiagnosticPort,
DiagnosticListenMode, EnableDiagnostics) with the correct Android profiling
setup:

- Use AndroidEnableProfiler=true to include Mono diagnostic component
- Add adb shell setprop debug.mono.profile step to configure diagnostic port
- Add adb reverse step for physical Android devices
- Set ANDROID_SERIAL environment on launch step for multi-device targeting

The runtime diagnostic port on Android is configured via the
debug.mono.profile system property, not MSBuild properties. This was
confirmed by testing against the dotnet/android documentation and
verified with successful trace captures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Stop Capture button was only shown during the 'Running' state (while
steps are launching) but hidden during 'WaitingForStop' (when capture
tools are actively recording and waiting for user to stop). This meant
the user could only see a Cancel button during recording.

Fix: Show Stop Capture button during WaitingForStop (the active recording
state) and show a launching spinner during Running. Also show elapsed
time during WaitingForStop and update state text to clarify what each
state means.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, auto-conversion

- Bundle speedscope v1.25.0 standalone build in wwwroot/speedscope/
- Add JS interop (speedscopeInterop.js) to load profile data into iframe
- Add inline speedscope viewer in Results step with auto file injection
- Create GcDumpReportParser (Core) to parse dotnet-gcdump report output
- Create GcDumpReportService to shell out to dotnet-gcdump and return data
- Create ProfilingArtifactConverterService for .nettrace to .speedscope.json
- Pipeline runner auto-converts .nettrace to speedscope after capture
- Results step shows 'View Trace' and 'View GC Dump' action buttons
- GC dump viewer: sortable/filterable table with type, count, size, % bar
- Add IGcDumpReportService and IProfilingArtifactConverterService interfaces
- Register new services in both MauiProgram.cs and MacOSMauiProgram.cs
- Add 6 unit tests for GcDumpReportParser (229 total, all passing)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…import/export

- Add ProfilingSessionManifest models for persistent session metadata
- Add ProfilingSessionStorageService for CRUD + import/export of session folders
- Transform Profiling page from one-shot wizard to session library with ViewMode toggle
- Session list shows cards with platform icon, date, duration, artifacts, status badges
- Expandable session cards with inline artifact viewers (speedscope, GC dump)
- Session persistence: captures auto-save session.json with full manifest after pipeline
- Output directory now uses managed storage (AppDataPath/profiling/{session-id}/)
- Toolbar: New Session (+), Import, Search, Refresh buttons
- Session actions: Export (.zip), Open Folder, Delete with confirmation
- Empty state with prominent 'New Profiling Session' call-to-action
- Wizard mode accessible via 'Back to Sessions' navigation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the inline wizard (steps 0-4: Session Setup, Capture Kinds, Target,
Review & Plan, Capture) from Profiling.razor into a new
ProfilingCaptureWizardModal.razor component that uses the established
WizardFormPage pattern for native footer buttons (Back/Next/Start Capture).

- Create ProfilingCaptureWizardModal.razor at /modal/profiling-wizard route
- Wire HybridFormBridge for native Back/Next/Submit button control
- Handle pipeline completion: save session manifest, set bridge.Result
- Hard link already exists for macOS project
- Simplify Profiling.razor to session list only (remove ViewMode enum)
- Add StartNewWizardAsync using IFormModalService.ShowAsync pattern
- Auto-expand new session in list after wizard completes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix GC dump timing: add build-and-run dependency so gcdump waits for app
- Fix nettrace viewer: handle conversion failure with inline error + retry
- Fix artifact card layout: buttons wrap to their own row
- Reduce wizard modal padding for compact layout
- Add squircle border-radius (14px) on all cards/panels
- Fix artifact path overflow: show filename only, full path in tooltip
- Make plan step commands collapsible (collapsed by default)
- Disable Next button during prerequisites check and plan generation
- Simplify prerequisites: green banner when all pass, expandable details
- Fix suspend at startup: default to false, add descriptive help text

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Profiling artifacts (.nettrace, .gcdump, .speedscope.json) show in a
primary always-visible section. Log files (.log) are grouped into a
collapsible section that starts collapsed, keeping the view clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Move Scenario dropdown from Step 0 to Step 1 (Capture Kinds) as preset
- Move Suspend at Startup from Step 0 Advanced to Step 1
- Auto-toggle suspend when Launch scenario selected
- Rename Step 0 from 'Session Setup' to 'Project'
- Reduce outer padding from 0.5rem to 0.25rem

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Toolbar + button already provides the CTA. Also eliminates remaining
excess padding in the wizard step indicator and form-content wrapper.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Already shown on Capture Kinds step; no need to repeat on Target.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Auto-toggle suspend at startup when Startup capture kind is checked
- Remove platform notes info-banner from Target step
- Prerequisites: use FA icon instead of emoji, compact list with inline
  status icons instead of verbose grid cards
- Plan summary: inline key-value rows instead of 4 nested cards
- Commands: compact list with tooltip descriptions instead of card grid
- Move Expected Artifacts before Commands in capture plan
- Clean up unused CSS (prereq cards, summary cards, command cards)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace grid of nested cards with a compact row-per-artifact layout
showing name, filename, and kind inline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Plan preview now pre-computes the session storage directory path
instead of showing the relative artifacts/ path. The actual pipeline
still generates its own session ID at start time.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Long paths no longer cause horizontal scrolling. The path is now in a
wrapping monospace code block below the label.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Redth and others added 28 commits March 10, 2026 16:10
On step 4, the native primary button becomes 'Stop Capture' when the
pipeline is waiting for stop. The native Cancel button cancels the
pipeline and returns to step 3 without closing the modal (via new
PreventClose bridge property). Remove redundant Blazor action buttons.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pass session storage path as OutputDirectory when generating the plan
so commands and artifact RelativePaths are built with the correct path.
Previously the plan was generated with a relative artifacts/ path and
only the top-level OutputDirectory was overridden at pipeline start,
leaving command arguments pointing to the old location.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace large artifact cards with a compact bordered list. Each artifact
is a single row with icon, filename, size, and small action buttons on
the right. View buttons stay labeled; Reveal/Copy are icon-only with
tooltips. Error rows nest below the relevant artifact.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The native Submit button (renamed to 'Stop Capture') always closed the
modal after the handler returned. But HandleStopCapture just signals the
pipeline to stop — the actual completion, artifact collection, and
session saving happen asynchronously in StartPipelineAsync.

Add PreventSubmitClose to HybridFormBridge so the native Submit handler
does not pop the bridge and close the modal while the pipeline is still
active. After the pipeline completes and the session is saved, the
Blazor component calls bridge.RequestClose() to close the modal with
the manifest as the result.

Also adds CloseRequested event to HybridFormBridge for Blazor components
to programmatically close the modal with a result.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The pipeline executor treated a long-running dependency as satisfied the
moment it started. This was fine for dotnet-trace (which itself waits for
the app to connect via the diagnostic port) but broke dotnet-gcdump,
which connects, collects, and exits immediately — before the app had
even finished building.

Changes:
- Add IsReady property to ProfilingStepStatus. Long-running steps signal
  readiness when their output matches a configurable ReadyOutputPattern.
- Add ReadyOutputPattern to ProfilingCommandStep. Set to 'Process' for
  dotnet-trace (emitted when the app connects) and 'Build succeeded' for
  the build-and-run step.
- Refine dependency logic: non-long-running steps (like gcdump) now wait
  for their long-running dependencies to signal IsReady, not just start.
  Long-running steps still proceed as soon as the dependency starts.
- Make capture-memory depend on capture-trace when both are requested, so
  gcdump waits for trace to establish its diagnostic port connection.
- Poll with 500ms delay when waiting for readiness instead of blocking on
  task completion only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move speedscope and GC dump viewers from inline rendering on the
Profiling page to dedicated Blazor pages (SpeedscopeViewer.razor,
GcDumpViewer.razor) that open in their own macOS windows via
ProfilingViewerService. Each viewer type gets its own window; clicking
View again replaces the previous viewer window.

Removes ~200 lines of inline viewer HTML, CSS, and state management
from Profiling.razor. The viewer pages are self-contained with their
own loading, error, and content states.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The start-dsrouter step was long-running but had no ReadyOutputPattern,
so its IsReady flag was never set. Since capture-memory (GC dump) is a
non-long-running step that depends on dsrouter, IsDependencySatisfied
required IsReady == true — causing a permanent deadlock.

dsrouter outputs 'Starting IPC server' when ready; use that as the
readiness pattern.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dotnet-gcdump is a point-in-time heap snapshot, not a continuous
collector. Running it automatically in the pipeline captured the heap
at an arbitrary moment (usually right after app launch, before anything
useful happens).

Now GC dump is triggered on-demand via a 'Collect GC Dump' button in
the capture step UI. Users can take multiple snapshots at specific
moments during their session. Each dump is numbered (memory-1.gcdump,
memory-2.gcdump, etc.) and tracked as a session artifact.

Changes:
- Remove capture-memory from automatic pipeline commands
- Add CollectGcDumpAsync() to IProfilingSessionRunner for on-demand use
- Add Collect GC Dump button in wizard capture step (visible when
  pipeline is in WaitingForStop state and memory capture is selected)
- Update tests to reflect GC dump is no longer an auto pipeline step

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The DialogService.ShowInputDialogAsync was wrapped in #if MACCATALYST
and the #else branch returned null, so the keystore password prompt
never appeared on macOS AppKit builds. Add a MACOSAPP implementation
using NSAlert with an accessory NSTextField (NSSecureTextField for
password prompts).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DeleteKeystoreFromCloudAsync was ignoring the bool return values from
DeleteSecretAsync, so when Infisical delete calls failed, the UI showed
a success toast and optimistically removed the item. On refresh, the
keystore reappeared because it was never actually deleted.

Now checks return values and throws InvalidOperationException with
details on which secrets failed to delete. Also improved Infisical
error logging to include inner exception messages for debugging.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ExtractAliasFromKey strips the last _segment assuming it's the suffix
(_JKS/_PWD/_META). But DeleteFromCloud passed 'KEYSTORE_{alias}' with
no suffix, so for aliases like TEST_SIGNING_KEY it extracted
TEST_SIGNING instead — deleting non-existent keys while the real ones
remained.

Fix: pass the alias directly to DeleteKeystoreFromCloudAsync instead
of wrapping it in KEYSTORE_ prefix and re-extracting. Also filter
Infisical ListSecrets to exclude imported secrets from other paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Configure EnableProfiler, EnableHighLevelUiHooks, and
EnableDetailedUiHooks in the profiling sample app. Update
MauiDevFlow packages from 0.18.0 to 0.20.0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update all MauiDevFlow package references from 0.18.0 to 0.20.0.
Add minimum version check (0.20.0) to the DevFlow profiling tab —
shows a clear message when the connected agent is too old instead
of failing with a capabilities error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dotnet-trace collect was invoked without --profile or --providers,
producing nearly empty speedscope files (9 generic frames, no managed
stacks). Now maps ProfilingCaptureKind to dotnet-trace profiles:
- Cpu/Startup → cpu-sampling (~100Hz kernel sampling)
- Rendering/Network/Energy/SystemTrace → dotnet-common

Also adds --format Speedscope so dotnet-trace emits both .nettrace
and .speedscope.json during capture, eliminating the separate
post-capture conversion step. Converter kept as fallback for
manually imported .nettrace files.

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

cpu-sampling is Linux-only (collect-linux). Use dotnet-sampled-thread-time
which works on all platforms and samples managed stacks at ~100Hz.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The --format flag may cause issues with --dsrouter mode, causing
the trace to end prematurely. Keep using post-capture conversion
via dotnet-trace convert instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove capture-trace from auto-planned pipeline steps
- Add StartTraceAsync/StopTraceAsync to IProfilingSessionRunner
- Add IsTraceActive property for mutual exclusion with gcdump
- Pipeline enters WaitingForStop for on-demand actions even without ManualStop steps
- Always use standalone dsrouter on mobile (needed for on-demand trace/gcdump)
- Add JIT/Loader provider flags (Microsoft-Windows-DotNETRuntime:0x10000018:5) for speedscope symbol resolution
- Unified capture action bar UI with Start Trace, Stop Trace, and GC Dump buttons
- Disable conflicting actions (no gcdump while tracing, no trace while gcdump)
- Stop active trace automatically when user clicks Stop Capture
- Update 4 orchestration tests for on-demand trace pattern

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Scan output directory for on-demand gcdump/nettrace/speedscope files
  not in the expected artifact list (memory-1.gcdump, trace-1.nettrace, etc.)
- Add fallback directory scan in wizard manifest creation for all artifact types
- Fix speedscope iframe to use #localProfilePath=1 hash param so
  window.speedscope.loadFileFromBase64() API is available
- Add polling (up to 5s) for speedscope API initialization with drop fallback
- Hide .nettrace View button on macOS (redundant with .speedscope.json;
  only useful on Windows with PerfView)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Speedscope: Replace cross-frame contentWindow API injection (fails in
WKWebView) with postMessage. Add message listener script to bundled
speedscope/index.html that receives profile data and simulates file
drop within iframe's own JS context. Remove broken #localProfilePath=1
hash that injected failing <script src="file:///1">.

GcDump: Fix parser to handle modern dotnet-gcdump report output format
(Object Bytes/Count/Type header, comma-formatted numbers, no hex MT
prefix). Strip annotation suffixes like (Bytes > 1K) [Module(...)].
Support both modern and legacy formats.

Add test for modern gcdump output format. All 257 tests pass.

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

BlazorWebView strips #hash from iframe src attributes, so speedscope
never saw #localProfilePath and rendered its landing page instead of
activating the loadFileFromBase64 API.

Fix: add inline script BEFORE speedscope bundle that forces
window.location.hash = 'localProfilePath=_'. Also add localStorage
polling as a second communication channel alongside postMessage.

Use OnAfterRenderAsync instead of @onload (doesn't fire on iframes
in Blazor WebView). Send postMessage multiple times at increasing
intervals to handle timing variations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add NativeMemoryKind to DevFlowProfilerSample DTO to match MauiDevFlow
PR #31. Update profiling tab to display platform-aware labels:
- apple.phys-footprint → 'Phys Footprint'
- android.native-heap-allocated → 'Native Heap'
- windows.working-set → 'Working Set'
- process.working-set-minus-managed → 'Native Memory'

Labels appear on metric card, chart header, and capabilities detail.
Raw kind string shown as tooltip and in detail panel.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Upgrade MauiDevFlow NuGet packages and CLI to 0.22.0
- Add DTOs for platform info, storage, permissions, sensors, geolocation
- Add 22 new API methods to DevFlowAgentClient (platform info, preferences
  CRUD, secure storage CRUD, permissions, sensors with WebSocket streaming,
  geolocation)
- Add DeleteAsync helper method to DevFlowAgentClient
- Create DevFlowPlatformTab.razor with 5 sub-tabs:
  - Info: card grid showing app info, device, display, battery, connectivity,
    version tracking
  - Storage: preferences table with inline edit/add/delete + secure storage
    lookup with add/delete
  - Permissions: color-coded status grid for all known permissions
  - Sensors: start/stop toggles with live WebSocket data streaming
  - Location: GPS coordinates with accuracy/timeout controls
- Register platform tab in DevFlowInspector (ValidTabs, tab button, switch)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix permissions: API returns { permissions: [...] } wrapper, not bare
  array. Added DevFlowPermissionsResponse wrapper DTO.
- Fix info cards: show per-card error messages when API returns errors
  (e.g., UIKit thread errors for App Info/Display on iOS simulator)
- Fix battery: show 'N/A' instead of '-100%' when chargeLevel is -1
- Add .info-card-error CSS with red border for failed cards

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Includes fix for UIKit thread errors in AppInfo and DeviceDisplay
platform handlers (MauiDevFlow PR #35).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Agent broadcasts PascalCase properties (X, Y, Z, PressureInHectopascals,
HeadingMagneticNorth) but UI was looking for lowercase (x, y, z, pressure,
heading). Added TryGetDataProperty helper that tries exact, PascalCase,
and camelCase. Also shows raw JSON as fallback when properties don't match.

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

- Use HttpClient.GetAsync instead of GetStringAsync to read error response bodies
- Parse structured error JSON ({success: false, error: '...'}) from non-2xx responses
- Format permission errors as actionable messages ('Missing Android permission: ...')
- Show permission errors as amber warnings (shield icon) instead of red errors
- Add info-card-warning CSS with amber border and text color
- Filed MauiDevFlow issue #37 for structured error reason codes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Refactor DevFlowAgentClient to use Dictionary of per-sensor WebSocket
  connections instead of a single shared WebSocket
- Add StopSensorStream(sensor), StopAllSensorStreams(), IsSensorStreaming(),
  StreamingSensorCount to client API
- Update UI to track readings per sensor in a Dictionary
- Add 'Stop All (N)' button in toolbar when streams are active
- Each sensor can independently start/stop streaming simultaneously

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Place live values between sensor name and action buttons instead of a
separate panel below. Readings sit right-aligned in the card, keeping
the layout compact. Shortened stream button labels to icon-only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Redth
Copy link
Copy Markdown
Owner Author

Redth commented Mar 11, 2026

Closing — all 67 commits were already merged via PR #105. No additional changes on this branch.

@Redth Redth closed this Mar 11, 2026
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.

1 participant