Collect WSLg logs and crash dumps in collect-wsl-logs.ps1 (#40913)

* Collect WSLg logs in collect-wsl-logs.ps1 Gather WSLg graphical/audio logs into a wslg/ folder of the log archive: weston.log, pulseaudio.log, wlog.log, stderr.log and versions.txt from /mnt/wslg, plus crash dumps from %TEMP%\wsl-crashes (and legacy /mnt/wslg/dumps). Logs are copied inside WSL as the uid=0 user so binary dumps are preserved and root-only logs like pulseaudio.log are readable. Document WSLg log collection and analysis in CONTRIBUTING.md, doc/docs/debugging.md, and a new .github/copilot/wslg-logs.md guide, including references to the microsoft/wslg repository. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Pass WSLg dest path as sh argument to handle quotes safely Addresses PR review: a destination path containing a single quote (e.g. C:\Users\O'Connor\...) would break the single-quoted sh -c string. Pass the path as $1 and reference it via "$1" inside the script instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review: timeout, --system, and gate WSLg dumps behind -Dump - Collect WSLg logs inside a background job with a 60s timeout so a wedged WSL service cannot hang log collection. - Use wsl.exe --system to reach /mnt/wslg, which works even when the default distro is WSL1 or is not running, and runs as the wslg user that owns the logs (removes the id -nu 0 super-user detection). - Only collect WSLg crash dumps (/mnt/wslg/dumps and %TEMP%\wsl-crashes) when -Dump is passed, since users may not expect dumps by default. Docs updated accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use --system --user root for WSLg log collection Run the WSLg collection as root in the system distro so root-owned logs are guaranteed readable, removing ambiguity about required privileges. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * collect-wsl-logs: address WSLg review feedback - Trim wslpath output before the null/whitespace check - Explain the sh -c arg0/\ idiom used to pass the destination safely - Lower the WSLg collection timeout from 60s to 20s Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * collect-wsl-logs: use New-Item -Force for the wslg folder instead of mkdir -p Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Ben Hillis committed Jul 9, 2026 at 09:34 UTC 95cdd1cf310ceaae76f6193afa78a5bda2af9f9e
5 files changed +150
.github/copilot-instructions.md
+1
@@ -347,6 +347,7 @@ wpr -stop logs.ETL
347 ### Log Analysis Tools
348 - Use WPA (Windows Performance Analyzer) for ETL traces
349 - Key providers: `Microsoft.Windows.Lxss.Manager`, `Microsoft.Windows.Subsystem.Lxss`
350 +- For graphical/audio (WSLg) issues, see `.github/copilot/wslg-logs.md`. `collect-wsl-logs.ps1` gathers WSLg logs (`/mnt/wslg`: weston.log, pulseaudio.log, wlog.log, stderr.log, versions.txt) into a `wslg/` folder using `wsl.exe --system --user root`; crash dumps (`%TEMP%\wsl-crashes`, legacy `/mnt/wslg/dumps`) are only collected with `-Dump`. WSLg code lives in https://github.com/microsoft/wslg, not this repo.
351
352 ### Debug Console (Linux)
353 Add to `%USERPROFILE%\.wslconfig`:
.github/copilot/wslg-logs.md new
+79
@@ -0,0 +1,79 @@
1 +# Analyzing WSLg logs
2 +
3 +WSLg is the component that runs graphical (GUI) and audio Linux applications on WSL. Its
4 +source lives in a separate repository: https://github.com/microsoft/wslg. The canonical list
5 +of diagnostics to request for a WSLg bug is the WSLg repo bug report template
6 +(`.github/ISSUE_TEMPLATE/bug_report.yml` in microsoft/wslg).
7 +
8 +`diagnostics/collect-wsl-logs.ps1` collects the logs into a `wslg/` subfolder of the log archive
9 +(via `wsl.exe --system --user root`, with a timeout so a wedged service can't hang collection), so a
10 +standard WSL log collection already contains the log files below. Crash dumps (`dumps/`,
11 +`wsl-crashes/`) are only collected when the script is run with `-Dump`.
12 +
13 +## Architecture (just enough to read the logs)
14 +
15 +WSLg runs a **system distro** (a small Azure Linux VM, separate from the user distro) that hosts:
16 +- **weston** (Wayland compositor) with the `rdp-backend.so` / `rdprail-shell.so` modules
17 +- **Xwayland** for X11 apps
18 +- **pulseaudio** with an RDP sink/source for audio
19 +- **FreeRDP** to stream the surfaces over an hvsocket to the Windows RDP client (`msrdc.exe`)
20 +
21 +`weston.log` and `wlog.log` are written by weston/FreeRDP, `pulseaudio.log` by pulseaudio, and
22 +`stderr.log` is the combined stderr of `WSLGd` and the processes it launches.
23 +
24 +## Files (in the `wslg/` folder, sourced from `/mnt/wslg`)
25 +
26 +| File | Contents |
27 +|---|---|
28 +| `versions.txt` | WSLg version, architecture, build date, component git hashes (weston, FreeRDP, mesa, pulseaudio) |
29 +| `weston.log` | Weston compositor + RDP backend log |
30 +| `wlog.log` | FreeRDP (WLog) log |
31 +| `pulseaudio.log` | PulseAudio log |
32 +| `stderr.log` | `WSLGd` and child-process stderr |
33 +| `dumps/` | Legacy WSLg crash dumps (older builds only; collected with `-Dump`) |
34 +| `wsl-crashes/` | Host-side WSLg crash dumps copied from `%TEMP%\wsl-crashes` (newer builds, e.g. `core.weston`; collected with `-Dump`) |
35 +
36 +## Reading tips and common signatures
37 +
38 +- **`weston.log` is truncated on every boot.** Weston is started with `--log=/mnt/wslg/weston.log`
39 + and opens it truncating, so the file only ever contains the **latest** system-distro boot. To
40 + tell whether weston restarted, compare the boot timestamp on the first line (`weston 9.0.0 ...`)
41 + across snapshots, or correlate with `dmesg`.
42 +
43 +- **System distro cycling / teardown.** In `dmesg` (collected via the ETL `GuestLog` events or
44 + the debug console), this pair means the WSLg system distro's `init` exited and the distro was
45 + torn down (idle timeout or shutdown), which also restarts weston:
46 + ```
47 + Exception: Operation canceled @p9io.cpp:258 (AcceptAsync)
48 + WSL (1 - init()) ERROR: InitEntryUtilityVm:2551: Init has exited. Terminating distribution
49 + ```
50 + Repeating every ~25-60s with no GUI client attached is usually normal idle teardown, not a crash.
51 + A genuine weston crash instead leaves a core dump in `wsl-crashes/` (or `dumps/`).
52 +
53 +- **No GPU acceleration (software rendering).** This means the virtual GPU (`/dev/dxg`, d3d12 mesa)
54 + is not usable, so rendering falls back to CPU:
55 + ```
56 + Xwayland glamor: GBM Wayland interfaces not available
57 + Failed to initialize glamor, falling back to sw
58 + ```
59 + Investigate GPU driver presence, `/dev/dxg`, and the `mesa`/`d3d12` stack if the user reports
60 + black windows, slow rendering, or missing 3D.
61 +
62 +- **Audio sink not connected (usually benign at boot).** Expected until an RDP/audio client attaches:
63 + ```
64 + [rdp-sink] RDP Sink - Trying to connect to /mnt/wslg/PulseAudioRDPSink
65 + [rdp-sink] Connected failed
66 + ```
67 + If it persists while audio is actively broken, look at the RDP client (`msrdc`) audio channel.
68 +
69 +- **Mostly-benign startup noise** (not a root cause on its own):
70 + - `WSLGd: Exception: No such file or directory @FontMonitor.cpp:280` - font share not mounted.
71 + - `XDG_RUNTIME_DIR "/mnt/wslg/runtime-dir" ... mode 040777` (should be 0700) - common warning.
72 + - `dbus: Unknown username "pulse"` / `Option "-listen" is deprecated` - harmless.
73 +
74 +## When to escalate to the WSLg repo
75 +
76 +WSLg code is not in this repository. Fixes to weston, FreeRDP, pulseaudio, or the RDP backend
77 +belong in https://github.com/microsoft/wslg. Use a commit-pinned permalink when referencing files
78 +there so the link does not drift, e.g.
79 +`https://github.com/microsoft/wslg/blob/<commit-sha>/<path>`.
CONTRIBUTING.md
+2
@@ -90,6 +90,8 @@ Set-ExecutionPolicy Bypass -Scope Process -Force
90 ```
91 The script will output the path of the log file once done.
92
93 +The collected logs include WSLg (graphical and audio application) logs from `/mnt/wslg`, so the same script covers graphical-app issues. WSLg crash dumps are included when the script is run with `-Dump`.
94 +
95 For specific scenarios, you can use different log profiles:
96 - `.\collect-wsl-logs.ps1 -LogProfile storage` - Enhanced storage tracing
97 - `.\collect-wsl-logs.ps1 -LogProfile networking` - Comprehensive networking tracing (includes packet capture, tcpdump, etc.)
diagnostics/collect-wsl-logs.ps1
+44
@@ -341,6 +341,50 @@ if ($LogProfile -eq "networking")
341 Remove-Item $networkingBashScript
342 }
343
344 +# Collect WSLg logs (https://github.com/microsoft/wslg)
345 +$wslgFolder = "$folder/wslg"
346 +New-Item -ItemType Directory -Force -Path $wslgFolder | Out-Null
347 +
348 +# Run in a job with a timeout so a wedged WSL service can't hang collection. --system --user root
349 +# reaches /mnt/wslg even when the default distro is WSL1 or isn't running, and can read root-only logs.
350 +$wslgJob = Start-Job -ScriptBlock {
351 + param($DestFull, $CollectDumps)
352 +
353 + $destWsl = "$(& wsl.exe --system --user root -e wslpath -u "$DestFull" 2>$null)".Trim()
354 + if ([string]::IsNullOrWhiteSpace($destWsl)) { return }
355 +
356 + # Destination is passed as $1 so paths containing a single quote are handled safely. In
357 + # `sh -c '<script>' sh <arg>`, the token after the script becomes $0 (here "sh") and the
358 + # next becomes $1 (the destination path).
359 + & wsl.exe --system --user root -e sh -c 'cp /mnt/wslg/pulseaudio.log /mnt/wslg/weston.log /mnt/wslg/wlog.log /mnt/wslg/stderr.log /mnt/wslg/versions.txt "$1/" 2>/dev/null; exit 0' sh "$destWsl"
360 +
361 + if ($CollectDumps)
362 + {
363 + & wsl.exe --system --user root -e sh -c '[ -d /mnt/wslg/dumps ] && cp -r /mnt/wslg/dumps "$1/dumps"; exit 0' sh "$destWsl"
364 + }
365 +} -ArgumentList (Resolve-Path $wslgFolder).Path, ([bool]$Dump)
366 +
367 +if (Wait-Job $wslgJob -Timeout 20)
368 +{
369 + Receive-Job $wslgJob | Out-Null
370 +}
371 +else
372 +{
373 + Write-Host -ForegroundColor Yellow "WSLg log collection timed out and was skipped."
374 + Stop-Job $wslgJob
375 +}
376 +Remove-Job $wslgJob -Force
377 +
378 +# Crash dumps are only collected with -Dump, since users may not expect dumps to be published by default.
379 +if ($Dump)
380 +{
381 + $wslCrashes = "$env:TEMP\wsl-crashes"
382 + if (Test-Path $wslCrashes)
383 + {
384 + Copy-Item $wslCrashes "$wslgFolder/wsl-crashes" -Recurse -ErrorAction Ignore
385 + }
386 +}
387 +
388 if ($Dump)
389 {
390 $Assembly = [PSObject].Assembly.GetType('System.Management.Automation.WindowsErrorReporting')
doc/docs/debugging.md
+24
@@ -52,6 +52,30 @@ debugConsole=true
52 to `%USERPROFILE%/.wslconfig` and restarting WSL
53
54
55 +## WSLg (graphical and audio applications) logs
56 +
57 +[WSLg](https://github.com/microsoft/wslg) runs graphical and audio Linux applications. It runs a
58 +system distro that hosts the `weston` Wayland compositor (with an RDP backend), `Xwayland`,
59 +`pulseaudio`, and `FreeRDP`. WSLg's source lives in a separate repository:
60 +[microsoft/wslg](https://github.com/microsoft/wslg).
61 +
62 +WSLg writes its logs to `/mnt/wslg` (accessible from Windows via `\\wsl$\<Distro>\mnt\wslg`):
63 +
64 +- `weston.log` - Weston compositor and RDP backend log
65 +- `wlog.log` - FreeRDP log
66 +- `pulseaudio.log` - PulseAudio log
67 +- `stderr.log` - `WSLGd` and child-process stderr
68 +- `versions.txt` - WSLg version and component git hashes
69 +
70 +Crash dumps (e.g. `core.weston`) are written to `%TEMP%\wsl-crashes` on newer builds, or
71 +`/mnt/wslg/dumps` on older ones.
72 +
73 +The [collect-wsl-logs.ps1](https://github.com/microsoft/WSL/blob/master/diagnostics/collect-wsl-logs.ps1)
74 +script gathers the logs above automatically into a `wslg/` folder (crash dumps are included when run
75 +with `-Dump`). Note that `weston.log` is truncated on every system-distro boot, so it only contains
76 +the most recent boot.
77 +
78 +
79 ## Attaching debuggers
80
81 Usermode can be attached to WSL Windows processes (wsl.exe, wslservice.exe, wslrelay.exe, ...). The symbols are available under the `bin/<platform>/<target>` folder.