diff --git a/diagnostics/collect-wsl-logs.ps1 b/diagnostics/collect-wsl-logs.ps1 index fa271f9b2..ee694d592 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 @@ -124,6 +129,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") { @@ -369,10 +388,96 @@ 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 } } } +# --------------------------------------------------------------------------- +# 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 +} + +# 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 } + } +} + +$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 + 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" + } + wslConfigPresent = [bool](Test-Path $wslconfig) + 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, 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). | +| 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)