From a8ca555ef6414f2ce6ed9e929f517e2a0c78685b Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Thu, 11 Jun 2026 10:07:18 -0700 Subject: [PATCH 1/5] diagnostics: record capture profile in collected logs collect-wsl-logs.ps1 did not record which WPR profile was used for a capture. When analyzing an archive (e.g. a networking-only capture that lacks the WSL core trace providers), there was no way to tell which profile produced it without inferring it from the provider mix. Write a collection-info.txt into the log folder capturing the selected LogProfile, the mapped WPRP profile and file, the Dump and RestartWslReproMode switches, and the collection timestamp. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- diagnostics/collect-wsl-logs.ps1 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/diagnostics/collect-wsl-logs.ps1 b/diagnostics/collect-wsl-logs.ps1 index fa271f9b28..054f07df98 100644 --- a/diagnostics/collect-wsl-logs.ps1 +++ b/diagnostics/collect-wsl-logs.ps1 @@ -124,6 +124,20 @@ else } } +# Record how these logs were collected so that whoever analyzes the archive +# knows which profile was used (a networking-only capture, for example, will not +# contain the WSL core trace providers). +$logProfileDisplay = if ($LogProfile -eq $null) { "default" } else { $LogProfile } +$wprpProfileDisplay = if ($wprpProfile -ne $null) { $wprpProfile } else { "(default profile in $wprpFile)" } +@" +LogProfile : $logProfileDisplay +WPRP profile : $wprpProfileDisplay +WPRP file : $wprpFile +Dump : $Dump +RestartWslReproMode : $RestartWslReproMode +Collected : $(Get-Date -Format "yyyy-MM-dd HH:mm:ss K") +"@ | Out-File -FilePath "$folder/collection-info.txt" -Encoding utf8 + # Networking-specific setup if ($LogProfile -eq "networking") { From 0abf142c0f218dc0c6cbc8e9e03e79a7fc6cbae3 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Thu, 11 Jun 2026 10:11:58 -0700 Subject: [PATCH 2/5] diagnostics: collect WSL version, guest state, and drop empty dumps Add three further debugging improvements to collect-wsl-logs.ps1: - Collect wsl --version / --status / --list --verbose into wsl-info.txt instead of forcing analyzers to infer the version and distro layout from the appx package and registry. - Collect guest-side state (dmesg, free, uptime, ulimit, pid_max, threads-max, process/thread counts, top RSS) into linux_diagnostics.log after the repro. This is the data needed to diagnose in-distro failures such as 'Resource temporarily unavailable' (EAGAIN) from resource limits. - Remove 0-byte dump files left behind when MiniDumpWriteDump fails, so the archive only contains real dumps. Also set WSL_UTF8 and the console output encoding so wsl.exe output is captured to the log files readably. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- diagnostics/collect-wsl-logs.ps1 | 55 ++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/diagnostics/collect-wsl-logs.ps1 b/diagnostics/collect-wsl-logs.ps1 index 054f07df98..f4090118d4 100644 --- a/diagnostics/collect-wsl-logs.ps1 +++ b/diagnostics/collect-wsl-logs.ps1 @@ -9,6 +9,11 @@ Param ( Set-StrictMode -Version Latest +# Make wsl.exe emit UTF-8 (instead of UTF-16) and have the console decode it as +# such, so output captured from wsl.exe below is saved to log files readably. +$env:WSL_UTF8 = "1" +try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} + function Test-WslApplication { param ( $Name @@ -80,6 +85,41 @@ function Collect-WindowsNetworkState { try { Get-NetUdpEndpoint | Out-File -FilePath "$Folder/Get-NetUdpEndpoint_$ReproStep.log" -Append } catch {} } +function Collect-LinuxDiagnostics { + param ( + $Folder + ) + + # Collect guest-side state that is useful for diagnosing in-distro failures + # (e.g. "Resource temporarily unavailable" / EAGAIN from hitting process, + # thread, file-descriptor or memory limits). Best-effort: skipped if WSL is + # not installed or no distribution is available. + try + { + # Detect the super user (uid=0, not necessarily named "root" - see #11693) + $superUser = & wsl.exe -- id -nu 0 2>$null + if ([string]::IsNullOrWhiteSpace($superUser)) + { + return + } + + $script = @( + 'echo "=== uname -a ==="; uname -a', + 'echo "=== uptime ==="; uptime', + 'echo "=== free -m ==="; free -m', + 'echo "=== ulimit -a ==="; ulimit -a', + 'echo "=== /proc/sys/kernel/pid_max ==="; cat /proc/sys/kernel/pid_max', + 'echo "=== /proc/sys/kernel/threads-max ==="; cat /proc/sys/kernel/threads-max', + 'echo "=== process/thread count (ps -eLf | wc -l) ==="; ps -eLf | wc -l', + 'echo "=== top processes by RSS ==="; ps -eo pid,ppid,rss,comm --sort=-rss | head -n 20', + 'echo "=== dmesg ==="; dmesg' + ) -join '; ' + + & wsl.exe -u $superUser -e sh -lc $script 2>&1 | Out-File -FilePath "$Folder/linux_diagnostics.log" -Encoding utf8 + } + catch {} +} + $folder = "WslLogs-" + (Get-Date -Format "yyyy-MM-dd_HH-mm-ss") mkdir -p $folder | Out-Null @@ -185,6 +225,14 @@ Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Get-Service wslservice -ErrorAction Ignore | Format-list * -Force > $folder/wslservice.txt +# Collect WSL version and distribution state. This is more reliable and readable +# than inferring the version/distro layout from the appx package and registry. +& wsl.exe --version > $folder/wsl-info.txt 2>&1 +"`n=== wsl --status ===" | Out-File -FilePath $folder/wsl-info.txt -Append +& wsl.exe --status 2>&1 | Out-File -FilePath $folder/wsl-info.txt -Append +"`n=== wsl --list --verbose ===" | Out-File -FilePath $folder/wsl-info.txt -Append +& wsl.exe --list --verbose 2>&1 | Out-File -FilePath $folder/wsl-info.txt -Append + $wslconfig = "$env:USERPROFILE/.wslconfig" if (Test-Path $wslconfig) { @@ -316,6 +364,10 @@ finally wpr.exe -stop $folder/logs.etl 2>&1 >> $wprOutputLog } +# Collect guest-side diagnostics after the repro (dmesg retains history, so this +# captures kernel messages emitted during the repro). +Collect-LinuxDiagnostics -Folder $folder + # Networking-specific post-repro collection if ($LogProfile -eq "networking") { @@ -383,6 +435,9 @@ if ($Dump) if (-not $Result) { Write-Host "Failed to write dump for: $($dumpFile)" + # Remove the empty file so the archive isn't littered with 0-byte + # dumps that look like real (but truncated) captures. + Remove-Item $dumpFile -ErrorAction Ignore } } } From 42cd56db283e4501ef1ed8f5d82399923068e96b Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Thu, 11 Jun 2026 10:18:53 -0700 Subject: [PATCH 3/5] diagnostics: add summary.json and README.md to log archive Make collected log archives easier to analyze (by a human or an agent) without having to run tools or infer state from individual artifacts: - summary.json: machine-readable overview of the capture - profile, WSL/Windows versions, networking mode, installed distributions and their state, .wslconfig presence, and an inventory of non-empty dumps. - README.md: an index of the archive contents describing each file, plus a note on how to decode logs.etl and how to tell when a non-default log profile was used. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- diagnostics/collect-wsl-logs.ps1 | 116 +++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/diagnostics/collect-wsl-logs.ps1 b/diagnostics/collect-wsl-logs.ps1 index f4090118d4..fc42b249cb 100644 --- a/diagnostics/collect-wsl-logs.ps1 +++ b/diagnostics/collect-wsl-logs.ps1 @@ -442,6 +442,122 @@ if ($Dump) } } +# --------------------------------------------------------------------------- +# Write a machine-readable summary and a human/agent-readable index so that +# whoever (or whatever) analyzes the archive has an immediate overview without +# having to run tools or infer state from the individual artifacts. +# --------------------------------------------------------------------------- +function Get-Prop +{ + param ($Object, $Name) + if ($Object -ne $null -And ($Object.PSObject.Properties.Name -contains $Name)) + { + return $Object.$Name + } + return $null +} + +# Enumerate installed distributions from the registry. +$distributions = @() +$lxssKey = "Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lxss" +if (Test-Path $lxssKey) +{ + foreach ($subKey in (Get-ChildItem $lxssKey -ErrorAction Ignore)) + { + $props = Get-ItemProperty $subKey.PSPath -ErrorAction Ignore + $name = Get-Prop $props "DistributionName" + if ($name -ne $null) + { + $distributions += [ordered]@{ + name = $name + state = Get-Prop $props "State" + version = Get-Prop $props "Version" + flavor = Get-Prop $props "Flavor" + modern = Get-Prop $props "Modern" + } + } + } +} + +# Inventory the dumps that were actually written (empty ones were removed above). +$dumps = @() +$dumpFolder = Join-Path $folder "dumps" +if (Test-Path $dumpFolder) +{ + foreach ($dump in (Get-ChildItem $dumpFolder -Filter *.dmp -ErrorAction Ignore)) + { + $dumps += [ordered]@{ name = $dump.Name; sizeBytes = $dump.Length } + } +} + +# Determine the networking mode (defaults to NAT unless overridden in .wslconfig). +$networkingMode = "default (NAT)" +if (Test-Path $wslconfig) +{ + $match = Select-String -Path $wslconfig -Pattern '^\s*networkingMode\s*=\s*(\S+)' -ErrorAction Ignore | Select-Object -First 1 + if ($match -ne $null) + { + $networkingMode = $match.Matches[0].Groups[1].Value + } +} + +$appx = Get-AppxPackage MicrosoftCorporationII.WindowsSubsystemforLinux -ErrorAction Ignore | Select-Object -First 1 +$winCV = Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -ErrorAction Ignore + +$summary = [ordered]@{ + collectedAt = (Get-Date -Format "o") + logProfile = $logProfileDisplay + wprpProfile = $wprpProfileDisplay + dump = [bool]$Dump + restartWslReproMode = [bool]$RestartWslReproMode + wslVersion = (Get-Prop $appx "Version") + windows = [ordered]@{ + build = "$(Get-Prop $winCV 'CurrentBuild').$(Get-Prop $winCV 'UBR')" + displayVersion = Get-Prop $winCV "DisplayVersion" + edition = Get-Prop $winCV "EditionID" + } + networkingMode = $networkingMode + wslConfigPresent = [bool](Test-Path $wslconfig) + distributions = $distributions + dumps = $dumps +} +$summary | ConvertTo-Json -Depth 5 | Out-File -FilePath "$folder/summary.json" -Encoding utf8 + +# Human/agent-readable index of the archive contents. +@" +# WSL log collection + +Start with ``summary.json`` (machine-readable overview) and +``collection-info.txt`` (how these logs were captured). + +> NOTE: ``logs.etl`` is a binary ETW trace. The WSL trace providers are +> TraceLogging (self-describing); decode it with WPA or PerfView for full +> payloads (``tracerpt logs.etl -o logs.csv -of CSV`` works for a quick text +> dump but renders some TraceLogging payloads as raw bytes). If the relevant +> WSL providers are missing, the capture likely used a non-default +> ``-LogProfile`` (see ``collection-info.txt``). + +## Key files + +| File | What it is | +|------|------------| +| summary.json | Machine-readable overview: profile, versions, distros, networking mode, dumps. | +| collection-info.txt | Which WPR profile / switches were used for this capture. | +| logs.etl | ETW trace captured during the repro (binary; see note above). | +| wsl-info.txt | Output of ``wsl --version`` / ``--status`` / ``--list --verbose``. | +| linux_diagnostics.log | Guest-side state (dmesg, free, ulimit, pid_max, thread/process counts). Useful for in-distro errors such as "Resource temporarily unavailable" (EAGAIN). | +| dumps/ | Process minidumps (only present with ``-Dump``; empty dumps are removed). | +| HKCU.txt / HKLM.txt | Lxss registry state (distributions, NAT config). | +| windows-version.txt | Windows build / edition. | +| .wslconfig | User's WSL configuration (only if present). | +| wsl-install-log*.txt / wsl-install-logs.txt | Install logs (only if present). | +| wpr.txt | Output from the WPR start/stop commands. | + +Networking-profile captures additionally contain ``tcpdump.log``, +``pktmon.etl``, ``wfpdiag.cab``, ``hns_events.log`` and various +``*_before_repro`` / ``*_after_repro`` network state logs. +"@ | Out-File -FilePath "$folder/README.md" -Encoding utf8 + $logArchive = "$(Resolve-Path $folder).tar.gz" tar.exe -czf $logArchive $folder if ($LASTEXITCODE -eq 0) From db3e2b515a3b4e314a6c1348d208c28a60c5a271 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Thu, 11 Jun 2026 13:28:14 -0700 Subject: [PATCH 4/5] diagnostics: address PR feedback (utf8 wsl-info, wsl.exe timeouts, slimmer summary.json) - Write wsl-info.txt as UTF-8 instead of the PS5.1 default UTF-16LE. - Guard every newly-added wsl.exe call (wsl-info and guest diagnostics) with a timeout via a background job so a deadlocked service or bad VM state cannot hang log collection. - Drop the duplicated distro-registry enumeration and .wslconfig networkingMode parsing from summary.json; that state is readily derived from HKCU.txt and the archived .wslconfig. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- diagnostics/collect-wsl-logs.ps1 | 150 ++++++++++++++++++------------- 1 file changed, 87 insertions(+), 63 deletions(-) diff --git a/diagnostics/collect-wsl-logs.ps1 b/diagnostics/collect-wsl-logs.ps1 index fc42b249cb..81fdf39ba5 100644 --- a/diagnostics/collect-wsl-logs.ps1 +++ b/diagnostics/collect-wsl-logs.ps1 @@ -14,6 +14,44 @@ Set-StrictMode -Version Latest $env:WSL_UTF8 = "1" try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} +function Invoke-WslWithTimeout { + param ( + [string[]]$Arguments, + [int]$TimeoutSeconds = 30, + [switch]$IncludeStderr + ) + + # Invoke wsl.exe with a timeout so log collection never hangs if the WSL + # service is deadlocked or the VM is in a bad state. The call runs in a + # background job (preserving native argument handling) and is stopped if it + # exceeds the timeout. Returns an object with the captured output (UTF-8) + # and whether the call timed out. + $job = Start-Job -ScriptBlock { + param ($WslArgs, $IncludeStderr) + $env:WSL_UTF8 = "1" + if ($IncludeStderr) + { + $output = & wsl.exe @WslArgs 2>&1 + } + else + { + $output = & wsl.exe @WslArgs 2>$null + } + return ($output | Out-String) + } -ArgumentList (, $Arguments), ([bool]$IncludeStderr) + + if (Wait-Job $job -Timeout $TimeoutSeconds) + { + $output = Receive-Job $job + Remove-Job $job -Force -ErrorAction Ignore + return [PSCustomObject]@{ TimedOut = $false; Output = ($output | Out-String) } + } + + Stop-Job $job -ErrorAction Ignore + Remove-Job $job -Force -ErrorAction Ignore + return [PSCustomObject]@{ TimedOut = $true; Output = "" } +} + function Test-WslApplication { param ( $Name @@ -93,31 +131,34 @@ function Collect-LinuxDiagnostics { # Collect guest-side state that is useful for diagnosing in-distro failures # (e.g. "Resource temporarily unavailable" / EAGAIN from hitting process, # thread, file-descriptor or memory limits). Best-effort: skipped if WSL is - # not installed or no distribution is available. - try - { - # Detect the super user (uid=0, not necessarily named "root" - see #11693) - $superUser = & wsl.exe -- id -nu 0 2>$null - if ([string]::IsNullOrWhiteSpace($superUser)) - { - return - } + # not installed or no distribution is available. Every wsl.exe call is + # timeout-guarded so a deadlocked service / bad VM state cannot hang here. - $script = @( - 'echo "=== uname -a ==="; uname -a', - 'echo "=== uptime ==="; uptime', - 'echo "=== free -m ==="; free -m', - 'echo "=== ulimit -a ==="; ulimit -a', - 'echo "=== /proc/sys/kernel/pid_max ==="; cat /proc/sys/kernel/pid_max', - 'echo "=== /proc/sys/kernel/threads-max ==="; cat /proc/sys/kernel/threads-max', - 'echo "=== process/thread count (ps -eLf | wc -l) ==="; ps -eLf | wc -l', - 'echo "=== top processes by RSS ==="; ps -eo pid,ppid,rss,comm --sort=-rss | head -n 20', - 'echo "=== dmesg ==="; dmesg' - ) -join '; ' + # Detect the super user (uid=0, not necessarily named "root" - see #11693) + $superUserResult = Invoke-WslWithTimeout -Arguments @("--", "id", "-nu", "0") + $superUser = $superUserResult.Output.Trim() + if ($superUserResult.TimedOut -or [string]::IsNullOrWhiteSpace($superUser)) + { + return + } - & wsl.exe -u $superUser -e sh -lc $script 2>&1 | Out-File -FilePath "$Folder/linux_diagnostics.log" -Encoding utf8 + $script = @( + 'echo "=== uname -a ==="; uname -a', + 'echo "=== uptime ==="; uptime', + 'echo "=== free -m ==="; free -m', + 'echo "=== ulimit -a ==="; ulimit -a', + 'echo "=== /proc/sys/kernel/pid_max ==="; cat /proc/sys/kernel/pid_max', + 'echo "=== /proc/sys/kernel/threads-max ==="; cat /proc/sys/kernel/threads-max', + 'echo "=== process/thread count (ps -eLf | wc -l) ==="; ps -eLf | wc -l', + 'echo "=== top processes by RSS ==="; ps -eo pid,ppid,rss,comm --sort=-rss | head -n 20', + 'echo "=== dmesg ==="; dmesg' + ) -join '; ' + + $result = Invoke-WslWithTimeout -Arguments @("-u", $superUser, "-e", "sh", "-lc", $script) -TimeoutSeconds 60 -IncludeStderr + if (-not $result.TimedOut) + { + $result.Output | Out-File -FilePath "$Folder/linux_diagnostics.log" -Encoding utf8 } - catch {} } $folder = "WslLogs-" + (Get-Date -Format "yyyy-MM-dd_HH-mm-ss") @@ -227,11 +268,25 @@ Get-Service wslservice -ErrorAction Ignore | Format-list * -Force > $folder/wsl # Collect WSL version and distribution state. This is more reliable and readable # than inferring the version/distro layout from the appx package and registry. -& wsl.exe --version > $folder/wsl-info.txt 2>&1 -"`n=== wsl --status ===" | Out-File -FilePath $folder/wsl-info.txt -Append -& wsl.exe --status 2>&1 | Out-File -FilePath $folder/wsl-info.txt -Append -"`n=== wsl --list --verbose ===" | Out-File -FilePath $folder/wsl-info.txt -Append -& wsl.exe --list --verbose 2>&1 | Out-File -FilePath $folder/wsl-info.txt -Append +# Each wsl.exe call is timeout-guarded so a deadlocked service / bad VM state +# cannot hang collection, and output is written as UTF-8 for cross-platform tools. +$wslInfoFile = "$folder/wsl-info.txt" +foreach ($entry in @( + @{ Header = "=== wsl --version ==="; Arguments = @("--version") }, + @{ Header = "=== wsl --status ==="; Arguments = @("--status") }, + @{ Header = "=== wsl --list --verbose ==="; Arguments = @("--list", "--verbose") })) +{ + $entry.Header | Out-File -FilePath $wslInfoFile -Append -Encoding utf8 + $result = Invoke-WslWithTimeout -Arguments $entry.Arguments -IncludeStderr + if ($result.TimedOut) + { + "(wsl.exe $($entry.Arguments -join ' ') timed out)" | Out-File -FilePath $wslInfoFile -Append -Encoding utf8 + } + else + { + $result.Output | Out-File -FilePath $wslInfoFile -Append -Encoding utf8 + } +} $wslconfig = "$env:USERPROFILE/.wslconfig" if (Test-Path $wslconfig) @@ -457,28 +512,6 @@ function Get-Prop return $null } -# Enumerate installed distributions from the registry. -$distributions = @() -$lxssKey = "Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lxss" -if (Test-Path $lxssKey) -{ - foreach ($subKey in (Get-ChildItem $lxssKey -ErrorAction Ignore)) - { - $props = Get-ItemProperty $subKey.PSPath -ErrorAction Ignore - $name = Get-Prop $props "DistributionName" - if ($name -ne $null) - { - $distributions += [ordered]@{ - name = $name - state = Get-Prop $props "State" - version = Get-Prop $props "Version" - flavor = Get-Prop $props "Flavor" - modern = Get-Prop $props "Modern" - } - } - } -} - # Inventory the dumps that were actually written (empty ones were removed above). $dumps = @() $dumpFolder = Join-Path $folder "dumps" @@ -490,20 +523,13 @@ if (Test-Path $dumpFolder) } } -# Determine the networking mode (defaults to NAT unless overridden in .wslconfig). -$networkingMode = "default (NAT)" -if (Test-Path $wslconfig) -{ - $match = Select-String -Path $wslconfig -Pattern '^\s*networkingMode\s*=\s*(\S+)' -ErrorAction Ignore | Select-Object -First 1 - if ($match -ne $null) - { - $networkingMode = $match.Matches[0].Groups[1].Value - } -} - $appx = Get-AppxPackage MicrosoftCorporationII.WindowsSubsystemforLinux -ErrorAction Ignore | Select-Object -First 1 $winCV = Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -ErrorAction Ignore +# Keep this summary basic: profile, versions and the dump inventory. State that +# can be readily derived from the rest of the archive (the installed +# distributions in HKCU.txt, the networking mode in .wslconfig) is intentionally +# not duplicated here. $summary = [ordered]@{ collectedAt = (Get-Date -Format "o") logProfile = $logProfileDisplay @@ -516,9 +542,7 @@ $summary = [ordered]@{ displayVersion = Get-Prop $winCV "DisplayVersion" edition = Get-Prop $winCV "EditionID" } - networkingMode = $networkingMode wslConfigPresent = [bool](Test-Path $wslconfig) - distributions = $distributions dumps = $dumps } $summary | ConvertTo-Json -Depth 5 | Out-File -FilePath "$folder/summary.json" -Encoding utf8 @@ -541,7 +565,7 @@ Start with ``summary.json`` (machine-readable overview) and | File | What it is | |------|------------| -| summary.json | Machine-readable overview: profile, versions, distros, networking mode, dumps. | +| summary.json | Machine-readable overview: profile, versions, dumps. | | collection-info.txt | Which WPR profile / switches were used for this capture. | | logs.etl | ETW trace captured during the repro (binary; see note above). | | wsl-info.txt | Output of ``wsl --version`` / ``--status`` / ``--list --verbose``. | From dc9dea31b4b01b765b987dca95e8f02ae49768a1 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Thu, 11 Jun 2026 14:13:05 -0700 Subject: [PATCH 5/5] diagnostics: drop wsl-info.txt and guest diagnostics collection Remove the unconditional wsl.exe calls this PR introduced (wsl-info.txt and linux_diagnostics.log) along with the now-unused timeout helper, per review feedback that the log collection script should not call wsl.exe (which can hang if the service is deadlocked or the VM is in a bad state). Pre-existing networking-profile wsl.exe calls are left untouched. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- diagnostics/collect-wsl-logs.ps1 | 104 ------------------------------- 1 file changed, 104 deletions(-) diff --git a/diagnostics/collect-wsl-logs.ps1 b/diagnostics/collect-wsl-logs.ps1 index 81fdf39ba5..ee694d5922 100644 --- a/diagnostics/collect-wsl-logs.ps1 +++ b/diagnostics/collect-wsl-logs.ps1 @@ -14,44 +14,6 @@ Set-StrictMode -Version Latest $env:WSL_UTF8 = "1" try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} -function Invoke-WslWithTimeout { - param ( - [string[]]$Arguments, - [int]$TimeoutSeconds = 30, - [switch]$IncludeStderr - ) - - # Invoke wsl.exe with a timeout so log collection never hangs if the WSL - # service is deadlocked or the VM is in a bad state. The call runs in a - # background job (preserving native argument handling) and is stopped if it - # exceeds the timeout. Returns an object with the captured output (UTF-8) - # and whether the call timed out. - $job = Start-Job -ScriptBlock { - param ($WslArgs, $IncludeStderr) - $env:WSL_UTF8 = "1" - if ($IncludeStderr) - { - $output = & wsl.exe @WslArgs 2>&1 - } - else - { - $output = & wsl.exe @WslArgs 2>$null - } - return ($output | Out-String) - } -ArgumentList (, $Arguments), ([bool]$IncludeStderr) - - if (Wait-Job $job -Timeout $TimeoutSeconds) - { - $output = Receive-Job $job - Remove-Job $job -Force -ErrorAction Ignore - return [PSCustomObject]@{ TimedOut = $false; Output = ($output | Out-String) } - } - - Stop-Job $job -ErrorAction Ignore - Remove-Job $job -Force -ErrorAction Ignore - return [PSCustomObject]@{ TimedOut = $true; Output = "" } -} - function Test-WslApplication { param ( $Name @@ -123,44 +85,6 @@ function Collect-WindowsNetworkState { try { Get-NetUdpEndpoint | Out-File -FilePath "$Folder/Get-NetUdpEndpoint_$ReproStep.log" -Append } catch {} } -function Collect-LinuxDiagnostics { - param ( - $Folder - ) - - # Collect guest-side state that is useful for diagnosing in-distro failures - # (e.g. "Resource temporarily unavailable" / EAGAIN from hitting process, - # thread, file-descriptor or memory limits). Best-effort: skipped if WSL is - # not installed or no distribution is available. Every wsl.exe call is - # timeout-guarded so a deadlocked service / bad VM state cannot hang here. - - # Detect the super user (uid=0, not necessarily named "root" - see #11693) - $superUserResult = Invoke-WslWithTimeout -Arguments @("--", "id", "-nu", "0") - $superUser = $superUserResult.Output.Trim() - if ($superUserResult.TimedOut -or [string]::IsNullOrWhiteSpace($superUser)) - { - return - } - - $script = @( - 'echo "=== uname -a ==="; uname -a', - 'echo "=== uptime ==="; uptime', - 'echo "=== free -m ==="; free -m', - 'echo "=== ulimit -a ==="; ulimit -a', - 'echo "=== /proc/sys/kernel/pid_max ==="; cat /proc/sys/kernel/pid_max', - 'echo "=== /proc/sys/kernel/threads-max ==="; cat /proc/sys/kernel/threads-max', - 'echo "=== process/thread count (ps -eLf | wc -l) ==="; ps -eLf | wc -l', - 'echo "=== top processes by RSS ==="; ps -eo pid,ppid,rss,comm --sort=-rss | head -n 20', - 'echo "=== dmesg ==="; dmesg' - ) -join '; ' - - $result = Invoke-WslWithTimeout -Arguments @("-u", $superUser, "-e", "sh", "-lc", $script) -TimeoutSeconds 60 -IncludeStderr - if (-not $result.TimedOut) - { - $result.Output | Out-File -FilePath "$Folder/linux_diagnostics.log" -Encoding utf8 - } -} - $folder = "WslLogs-" + (Get-Date -Format "yyyy-MM-dd_HH-mm-ss") mkdir -p $folder | Out-Null @@ -266,28 +190,6 @@ Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Get-Service wslservice -ErrorAction Ignore | Format-list * -Force > $folder/wslservice.txt -# Collect WSL version and distribution state. This is more reliable and readable -# than inferring the version/distro layout from the appx package and registry. -# Each wsl.exe call is timeout-guarded so a deadlocked service / bad VM state -# cannot hang collection, and output is written as UTF-8 for cross-platform tools. -$wslInfoFile = "$folder/wsl-info.txt" -foreach ($entry in @( - @{ Header = "=== wsl --version ==="; Arguments = @("--version") }, - @{ Header = "=== wsl --status ==="; Arguments = @("--status") }, - @{ Header = "=== wsl --list --verbose ==="; Arguments = @("--list", "--verbose") })) -{ - $entry.Header | Out-File -FilePath $wslInfoFile -Append -Encoding utf8 - $result = Invoke-WslWithTimeout -Arguments $entry.Arguments -IncludeStderr - if ($result.TimedOut) - { - "(wsl.exe $($entry.Arguments -join ' ') timed out)" | Out-File -FilePath $wslInfoFile -Append -Encoding utf8 - } - else - { - $result.Output | Out-File -FilePath $wslInfoFile -Append -Encoding utf8 - } -} - $wslconfig = "$env:USERPROFILE/.wslconfig" if (Test-Path $wslconfig) { @@ -419,10 +321,6 @@ finally wpr.exe -stop $folder/logs.etl 2>&1 >> $wprOutputLog } -# Collect guest-side diagnostics after the repro (dmesg retains history, so this -# captures kernel messages emitted during the repro). -Collect-LinuxDiagnostics -Folder $folder - # Networking-specific post-repro collection if ($LogProfile -eq "networking") { @@ -568,8 +466,6 @@ Start with ``summary.json`` (machine-readable overview) and | summary.json | Machine-readable overview: profile, versions, dumps. | | collection-info.txt | Which WPR profile / switches were used for this capture. | | logs.etl | ETW trace captured during the repro (binary; see note above). | -| wsl-info.txt | Output of ``wsl --version`` / ``--status`` / ``--list --verbose``. | -| linux_diagnostics.log | Guest-side state (dmesg, free, ulimit, pid_max, thread/process counts). Useful for in-distro errors such as "Resource temporarily unavailable" (EAGAIN). | | dumps/ | Process minidumps (only present with ``-Dump``; empty dumps are removed). | | HKCU.txt / HKLM.txt | Lxss registry state (distributions, NAT config). | | windows-version.txt | Windows build / edition. |