Skip to content

Enrich winapp run --debug-output crash stacks for WinUI apps #549

@nmetulev

Description

@nmetulev

What

When winapp run --debug-output captures a crash dump from a WinUI app, the resulting stack is fairly thin — typically the user's faulting frame plus a couple of cswinrt ABI shims. There's no visibility into the XAML dispatcher / CoreMessaging / runtime chain that actually led to the failure, and no breakdown of stowed exceptions (0xC000027B) which are the most common WinUI crash shape.

Why

Most WinUI crashes originate inside a XAML event handler and surface as a stowed exception wrapping a managed exception. The minimum context a developer needs to diagnose these is:

  • The originating HRESULT and its ErrorContext chain
  • The full native dispatch stack (Microsoft.UI.Xaml → CXcpDispatcher → CoreMessagingXP → CLR host)
  • The managed user frame that threw

Today only the last one is reliably produced.

Potential approach

The WinUI team ships a WinDbg JavaScript extension (winui-dbgext.js in microsoft/microsoft-ui-xaml) that already produces exactly this output via !xamltriage and !xamlstowed. It runs against a minidump and works well with public symbols.

A viable integration shape that avoids requiring a WinDbg install:

  1. Host DbgEng directly via the Microsoft.Debugging.Platform.DbgEng NuGet package (winapp already uses Microsoft.Diagnostics.Runtime.Utilities for its native fallback, so the API surface is familiar).
  2. Add the Microsoft.Debugging.Platform.SymSrv package and co-locate symsrv.dll next to dbgeng.dll — without this, srv* paths silently no-op and symbols never download. This is the main gotcha.
  3. Add Microsoft.Debugging.Platform.DbgX for dbgmodel.dll / msdia140.dll.
  4. Acquire JsProvider.dll (the JS scripting host for .scriptload) — this is not distributed on NuGet; it ships only with Debugging Tools for Windows. Options: bundle it, or download a WinDbg/SDK package on first use.
  5. Fetch winui-dbgext.js from the public WinUI repo (pin a SHA).
  6. After the existing ClrMD pass, run .scriptload winui-dbgext.js; !xamlstowed; !xamltriage against the same dump and append the output to the debug log.

Gating it behind a flag (or auto-enabling when Microsoft.UI.Xaml.dll is in the module list) keeps it scoped to WinUI apps.

Notes for whoever picks this up

  • !xamlstowed produces the most valuable output and works fully with the NuGet + JsProvider setup above.
  • !xamltriage's managed-frame walker depends on SOS / CLR data access being loaded in the session; without that it logs CORDBG_E_MISSING_METADATA for those frames. The headline summary (error code, message, user frames, ErrorContext) still comes through. Easiest path is probably to combine winapp's existing ClrMD managed stack with the extension's native output.
  • Relevant existing code: src/winapp-CLI/WinApp.Cli/Services/CrashDumpService.cs (AnalyzeWithDbgEng) is where the new pass would slot in.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions