<# crash-report.ps1 - CrashDecoder crash log reader https://crashdecoder.com/tools/ Reads the Windows event log and tells you, in plain English, what has been crashing this machine - then links each finding to the page explaining it. HOW TO USE 1. Right-click Start, choose "Terminal (Admin)" or "Windows PowerShell (Admin)". 2. cd to wherever you saved this file, for example: cd $env:USERPROFILE\Downloads 3. Run: powershell -ExecutionPolicy Bypass -File .\crash-report.ps1 OPTIONS -Days 90 How far back to look. Default 90. -Export Write the readout to a text file you can attach to a forum post. -Path Where to write that file. Defaults to your Desktop. WHAT IT DOES NOT DO It reads your event log and prints what it found. It sends nothing anywhere, changes nothing, installs nothing, and needs no internet connection. Every URL it prints is text on your screen - nothing is opened or contacted. Works on Windows 10 and 11, PowerShell 5.1 or later. #> [CmdletBinding()] param( [int]$Days = 90, [switch]$Export, [string]$Path = [Environment]::GetFolderPath('Desktop') ) $ErrorActionPreference = 'Continue' $SITE = 'https://crashdecoder.com' $since = (Get-Date).AddDays(-$Days) $report = New-Object System.Collections.Generic.List[string] function Say { param([string]$Text = '', [string]$Colour = 'Gray') Write-Host $Text -ForegroundColor $Colour $report.Add($Text) } function Rule { Say ('-' * 68) 'DarkGray' } function Head { param([string]$Text) Say '' Say $Text 'White' Rule } # --------------------------------------------------------------- stop code map # Keyed by the 8-digit uppercase hex STRING, not by a numeric literal. PowerShell # parses 0xC000021A in a hashtable literal as a signed Int32 (-1073741286), which # then never matches the unsigned value read from the log - and throws on the cast. # Formatting both sides to hex sidesteps signedness completely, and means 0x3B, # 0x0000003B and decimal 59 all normalise to the same key. $CODES = @{ '0000000A' = @{ n = 'IRQL_NOT_LESS_OR_EQUAL'; s = '0x0000000a-irql-not-less-or-equal' } '00000019' = @{ n = 'BAD_POOL_HEADER'; s = '0x00000019-bad-pool-header' } '0000001A' = @{ n = 'MEMORY_MANAGEMENT'; s = '0x0000001a-memory-management' } '0000001E' = @{ n = 'KMODE_EXCEPTION_NOT_HANDLED'; s = '0x0000001e-kmode-exception-not-handled' } '00000024' = @{ n = 'NTFS_FILE_SYSTEM'; s = '0x00000024-ntfs-file-system' } '0000003B' = @{ n = 'SYSTEM_SERVICE_EXCEPTION'; s = '0x0000003b-system-service-exception' } '00000050' = @{ n = 'PAGE_FAULT_IN_NONPAGED_AREA'; s = '0x00000050-page-fault-in-nonpaged-area' } '0000007A' = @{ n = 'KERNEL_DATA_INPAGE_ERROR'; s = '0x0000007a-kernel-data-inpage-error' } '0000007B' = @{ n = 'INACCESSIBLE_BOOT_DEVICE'; s = '0x0000007b-inaccessible-boot-device' } '0000007E' = @{ n = 'SYSTEM_THREAD_EXCEPTION_NOT_HANDLED'; s = '0x0000007e-system-thread-exception-not-handled' } '0000009C' = @{ n = 'MACHINE_CHECK_EXCEPTION'; s = '0x0000009c-machine-check-exception' } '0000009F' = @{ n = 'DRIVER_POWER_STATE_FAILURE'; s = '0x0000009f-driver-power-state-failure' } '000000C2' = @{ n = 'BAD_POOL_CALLER'; s = '0x000000c2-bad-pool-caller' } '000000D1' = @{ n = 'DRIVER_IRQL_NOT_LESS_OR_EQUAL'; s = '0x000000d1-driver-irql-not-less-or-equal' } '000000EA' = @{ n = 'THREAD_STUCK_IN_DEVICE_DRIVER'; s = '0x000000ea-thread-stuck-in-device-driver' } '000000EF' = @{ n = 'CRITICAL_PROCESS_DIED'; s = '0x000000ef-critical-process-died' } '000000F4' = @{ n = 'CRITICAL_OBJECT_TERMINATION'; s = '0x000000f4-critical-object-termination' } '00000101' = @{ n = 'CLOCK_WATCHDOG_TIMEOUT'; s = '0x00000101-clock-watchdog-timeout' } '00000109' = @{ n = 'CRITICAL_STRUCTURE_CORRUPTION'; s = '0x00000109-critical-structure-corruption' } '00000116' = @{ n = 'VIDEO_TDR_FAILURE'; s = '0x00000116-video-tdr-failure' } '00000124' = @{ n = 'WHEA_UNCORRECTABLE_ERROR'; s = '0x00000124-whea-uncorrectable-error' } '0000012B' = @{ n = 'FAULTY_HARDWARE_CORRUPTED_PAGE'; s = '0x0000012b-faulty-hardware-corrupted-page' } '00000133' = @{ n = 'DPC_WATCHDOG_VIOLATION'; s = '0x00000133-dpc-watchdog-violation' } '00000139' = @{ n = 'KERNEL_SECURITY_CHECK_FAILURE'; s = '0x00000139-kernel-security-check-failure' } '0000013A' = @{ n = 'KERNEL_MODE_HEAP_CORRUPTION'; s = '0x0000013a-kernel-mode-heap-corruption' } '00000154' = @{ n = 'UNEXPECTED_STORE_EXCEPTION'; s = '0x00000154-unexpected-store-exception' } 'C000021A' = @{ n = 'WINLOGON_FATAL_ERROR'; s = '0xc000021a-status-system-process-terminated' } } function Resolve-Code { param([uint32]$Value) $key = '{0:X8}' -f $Value if ($CODES.ContainsKey($key)) { return $CODES[$key] } return $null } function Show-Code { param([uint32]$Value, [int]$Count, [datetime]$Last) $hex = '0x{0:X8}' -f $Value $info = Resolve-Code -Value $Value if ($info) { $name = $info.n } else { $name = 'not in this tool''s list' } Say (" {0} {1}" -f $hex, $name) 'Yellow' Say (" seen {0} time(s), most recently {1}" -f $Count, $Last.ToString('yyyy-MM-dd HH:mm')) if ($info) { Say (" {0}/stop-codes/{1}/" -f $SITE, $info.s) 'Cyan' } else { Say (" {0}/stop-codes/" -f $SITE) 'Cyan' } } # ------------------------------------------------------------------- preamble Clear-Host Say '' Say ' CrashDecoder crash log reader' 'White' Say (" {0}/tools/" -f $SITE) 'DarkGray' Rule Say (" Machine : {0}" -f $env:COMPUTERNAME) Say (" Windows : {0}" -f (Get-CimInstance Win32_OperatingSystem).Caption) Say (" Looking back {0} days (since {1})" -f $Days, $since.ToString('yyyy-MM-dd')) $admin = ([Security.Principal.WindowsPrincipal] ` [Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $admin) { Say '' Say ' Note: not running as Administrator. Some entries may be hidden.' 'Yellow' Say ' For the full picture, re-run from an Administrator terminal.' 'Yellow' } # Only query providers this machine actually has. A provider name that does not # exist here would otherwise fail the whole query rather than return nothing. $available = @() try { $available = (Get-WinEvent -ListLog System -ErrorAction Stop).ProviderNames } catch {} function Have-Provider { param([string]$Name) return ($available -contains $Name) } # How far the log ACTUALLY goes back. Without this the tool reports "none found" # for a window the log never covered, which reads as "you are fine" when the # truth is "there is no evidence either way". The System log is a fixed-size # ring buffer - on a busy machine it can hold days rather than months. $logStart = $null try { $logStart = (Get-WinEvent -LogName System -Oldest -MaxEvents 1 -ErrorAction Stop).TimeCreated } catch {} $truncated = $false if ($logStart -and $logStart -gt $since) { $truncated = $true } if ($truncated) { $covered = [int]((Get-Date) - $logStart).TotalDays Say '' Say ' ! Your System log does not go back far enough to answer that.' 'Red' Say (" It starts {0} ({1} day(s) ago), but you asked for {2} days." -f ` $logStart.ToString('yyyy-MM-dd HH:mm'), $covered, $Days) 'Red' Say ' Anything older has already been overwritten. "None found" below means' 'Red' Say ' "no record survives", NOT "it never happened".' 'Red' } function No-Findings { param([string]$Text) if ($truncated) { Say (" None in the {0} the log still covers." -f $(if ($covered -eq 1) { 'day' } else { "$covered days" })) 'Yellow' } else { Say $Text 'Green' } } function Get-Events { param([hashtable]$Filter) $Filter['StartTime'] = $since try { return @(Get-WinEvent -FilterHashtable $Filter -ErrorAction Stop) } catch { return @() } } $findings = 0 # ------------------------------------------------------------ bugchecks (1001) Head '1. Blue screens recorded by Windows' $bug = Get-Events @{ LogName = 'System'; Id = 1001 } $bug = @($bug | Where-Object { $_.Message -match 'bugcheck was' }) if ($bug.Count -eq 0) { No-Findings ' None found. No recorded blue screens in this window.' } else { $findings++ $groups = @{} foreach ($e in $bug) { if ($e.Message -match '(?i)bugcheck was:\s*0x([0-9a-f]+)') { $v = [uint32]('0x' + $matches[1]) if (-not $groups.ContainsKey($v)) { $groups[$v] = @{ c = 0; last = $e.TimeCreated } } $groups[$v].c++ if ($e.TimeCreated -gt $groups[$v].last) { $groups[$v].last = $e.TimeCreated } } } Say (" {0} crash(es), {1} distinct stop code(s):" -f $bug.Count, $groups.Count) Say '' foreach ($v in ($groups.Keys | Sort-Object { -$groups[$_].c })) { Show-Code -Value $v -Count $groups[$v].c -Last $groups[$v].last Say '' } if ($groups.Count -ge 3) { Say ' Several different stop codes on one machine more often means memory or' 'Yellow' Say ' storage than several unrelated driver bugs. Test RAM before chasing drivers.' 'Yellow' Say (" {0}/guides/how-to-test-your-ram/" -f $SITE) 'Cyan' } } # --------------------------------------------------- unclean shutdowns (41) Head '2. Unclean shutdowns (Kernel-Power 41)' $kp = @() if (Have-Provider 'Microsoft-Windows-Kernel-Power') { $kp = Get-Events @{ LogName = 'System'; ProviderName = 'Microsoft-Windows-Kernel-Power'; Id = 41 } } if ($kp.Count -eq 0) { No-Findings ' None found. Every shutdown in this window was clean.' } else { $findings++ Say (" {0} unclean shutdown(s). Most recent: {1}" -f $kp.Count, $kp[0].TimeCreated.ToString('yyyy-MM-dd HH:mm')) Say '' $withCode = 0 $noCode = 0 foreach ($e in $kp) { $code = $null try { $x = [xml]$e.ToXml() foreach ($d in $x.Event.EventData.Data) { if ($d.Name -eq 'BugcheckCode') { $code = $d.'#text' } } } catch {} if ($code -and [uint32]$code -ne 0) { $withCode++ } else { $noCode++ } } # This field is DECIMAL here and hexadecimal everywhere else. 59 means 0x3B. if ($withCode -gt 0) { Say (" {0} of these carried a stop code (already listed above)." -f $withCode) } if ($noCode -gt 0) { Say (" {0} had no stop code at all - the machine died without recording one." -f $noCode) 'Yellow' Say ' That pattern points at power, thermals or hardware rather than a driver.' 'Yellow' Say (" {0}/hardware-errors/kernel-power-event-41-unexpected-shutdown/" -f $SITE) 'Cyan' } } # ------------------------------------------------------------- WHEA hardware Head '3. Hardware errors reported by the platform (WHEA)' $whea = @() if (Have-Provider 'Microsoft-Windows-WHEA-Logger') { $whea = Get-Events @{ LogName = 'System'; ProviderName = 'Microsoft-Windows-WHEA-Logger' } } if ($whea.Count -eq 0) { No-Findings ' None found. The platform has not reported a hardware error.' } else { $findings++ $byId = $whea | Group-Object Id | Sort-Object Count -Descending Say (" {0} WHEA event(s) in this window." -f $whea.Count) 'Yellow' Say '' $map = @{ 17 = @{ t = 'Corrected hardware error (usually PCIe)'; s = 'whea-logger-event-17-corrected-hardware-error' } 18 = @{ t = 'FATAL hardware error'; s = 'whea-logger-event-18-fatal-hardware-error' } 19 = @{ t = 'Corrected hardware error'; s = 'whea-logger-event-19-corrected-hardware-error' } 47 = @{ t = 'Corrected MEMORY error'; s = 'whea-logger-event-47-corrected-memory-error' } } foreach ($g in $byId) { $id = [int]$g.Name $last = ($g.Group | Sort-Object TimeCreated -Descending)[0].TimeCreated if ($map.ContainsKey($id)) { Say (" Event {0} x{1} - {2}" -f $id, $g.Count, $map[$id].t) 'Yellow' Say (" most recent {0}" -f $last.ToString('yyyy-MM-dd HH:mm')) Say (" {0}/hardware-errors/{1}/" -f $SITE, $map[$id].s) 'Cyan' } else { Say (" Event {0} x{1} - most recent {2}" -f $id, $g.Count, $last.ToString('yyyy-MM-dd HH:mm')) 'Yellow' Say (" {0}/hardware-errors/" -f $SITE) 'Cyan' } Say '' } Say ' "Corrected" means the hardware caught and fixed it this time. A steady' 'Yellow' Say ' stream of corrected errors is a component telling you it is degrading.' 'Yellow' } # ----------------------------------------------------------- display timeouts Head '4. Display driver timeouts' $disp = @() if (Have-Provider 'Display') { $disp = Get-Events @{ LogName = 'System'; ProviderName = 'Display'; Id = 4101 } } if ($disp.Count -eq 0) { No-Findings ' None found.' } else { $findings++ $last = $disp[0].TimeCreated Say (" {0} timeout(s). Most recent: {1}" -f $disp.Count, $last.ToString('yyyy-MM-dd HH:mm')) 'Yellow' $drivers = @{} foreach ($e in $disp) { if ($e.Message -match '(?i)display driver (\S+?) stopped responding') { $d = $matches[1] if (-not $drivers.ContainsKey($d)) { $drivers[$d] = 0 } $drivers[$d]++ } } foreach ($d in $drivers.Keys) { Say (" {0} x{1}" -f $d, $drivers[$d]) } Say (" {0}/hardware-errors/display-event-4101-driver-stopped-responding/" -f $SITE) 'Cyan' if ($disp.Count -ge 5) { Say ' Recovered timeouts increasing over time often precede a GPU that stops' 'Yellow' Say ' recovering at all. Worth acting on before it becomes a stop code.' 'Yellow' } } # ------------------------------------------------------------- disk / storage Head '5. Disk and file system errors' $diskCount = 0 foreach ($p in @('disk', 'Ntfs', 'volmgr')) { if (-not (Have-Provider $p)) { continue } $ev = Get-Events @{ LogName = 'System'; ProviderName = $p; Level = @(1, 2) } if ($ev.Count -gt 0) { $diskCount += $ev.Count Say (" {0}: {1} error(s), most recent {2}" -f $p, $ev.Count, $ev[0].TimeCreated.ToString('yyyy-MM-dd HH:mm')) 'Yellow' } $dumpFail = @($ev | Where-Object { $_.Id -eq 46 }) if ($p -eq 'volmgr' -and $dumpFail.Count -gt 0) { Say ' volmgr Event 46 - crash dump initialization failed. This is why you may' 'Yellow' Say ' have no dump files despite crashing. Check the page file settings.' 'Yellow' } } if ($diskCount -eq 0) { No-Findings ' None found.' } else { $findings++ Say (" {0}/hardware-errors/disk-and-ntfs-storage-error-events/" -f $SITE) 'Cyan' } # ------------------------------------------------------------------ dump files Head '6. Crash dump files on this machine' $mini = @() if (Test-Path "$env:SystemRoot\Minidump") { $mini = @(Get-ChildItem "$env:SystemRoot\Minidump\*.dmp" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending) } $full = Test-Path "$env:SystemRoot\MEMORY.DMP" if ($mini.Count -eq 0 -and -not $full) { Say ' No dump files found.' 'Yellow' if ($bug.Count -gt 0) { Say ' You have recorded crashes but no dumps, which is worth fixing before the' 'Yellow' Say ' next one: Win+R, sysdm.cpl, Advanced, Startup and Recovery, Settings.' 'Yellow' Say ' Set "Write debugging information" to "Automatic memory dump".' 'Yellow' } } else { Say (" {0} minidump(s) in {1}\Minidump" -f $mini.Count, $env:SystemRoot) if ($mini.Count -gt 0) { foreach ($m in ($mini | Select-Object -First 5)) { Say (" {0} {1}" -f $m.LastWriteTime.ToString('yyyy-MM-dd HH:mm'), $m.Name) } } if ($full) { Say (" Full dump present: {0}\MEMORY.DMP" -f $env:SystemRoot) } Say (" {0}/guides/how-to-read-a-dump-file/" -f $SITE) 'Cyan' } # ---------------------------------------------------------------------- close Head 'What to do next' if ($findings -eq 0 -and $truncated) { Say ' Nothing found - but the log only covers a fraction of the window you asked' 'Yellow' Say ' for, so this is not a clean bill of health. If the machine is crashing,' 'Yellow' Say ' the evidence has been overwritten. To catch the next one, enlarge the log:' 'Yellow' Say '' Say ' wevtutil sl System /ms:104857600 (100 MB, needs Administrator)' 'Cyan' Say '' Say ' Then check back after it next misbehaves.' } elseif ($findings -eq 0) { Say ' Nothing found in this window. If the machine is misbehaving anyway, try a' 'Green' Say ' longer window: .\crash-report.ps1 -Days 365' 'Green' } else { Say ' Open the links above for the entries that matter to you. If you are not' Say ' sure where to start, work down this order:' Say '' Say ' 1. WHEA errors - the platform saying hardware misbehaved' Say ' 2. Repeated crashes - the same stop code more than once' Say ' 3. Disk errors - these corrupt data as well as crash the machine' Say ' 4. Display timeouts - usually the GPU or its driver' Say '' Say (" Not sure what your stop code means? {0}/stop-codes/" -f $SITE) 'Cyan' Say (" No code at all? {0}/guides/how-to-find-your-stop-code/" -f $SITE) 'Cyan' } Say '' Rule Say ' Nothing was sent anywhere. This tool reads your event log and prints it.' 'DarkGray' Say '' if ($Export) { $file = Join-Path $Path ("crash-report-{0}.txt" -f (Get-Date -Format 'yyyyMMdd-HHmmss')) try { $report | Out-File -FilePath $file -Encoding utf8 Write-Host (" Saved to {0}" -f $file) -ForegroundColor Green Write-Host ' Safe to attach to a forum post - it contains no personal data' -ForegroundColor DarkGray Write-Host ' beyond your computer name and Windows version.' -ForegroundColor DarkGray Write-Host '' } catch { Write-Host (" Could not write to {0}: {1}" -f $Path, $_.Exception.Message) -ForegroundColor Red } }