From 6e093562b248d568dc6dc5dea7ed1f8c5bf1faab Mon Sep 17 00:00:00 2001 From: Frank Gorgenyi Date: Thu, 2 May 2019 07:43:44 -0700 Subject: [PATCH 1/6] Create GetMemoryDump.ps1 --- bluetooth/tracing/GetMemoryDump.ps1 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 bluetooth/tracing/GetMemoryDump.ps1 diff --git a/bluetooth/tracing/GetMemoryDump.ps1 b/bluetooth/tracing/GetMemoryDump.ps1 new file mode 100644 index 0000000..aa71a9f --- /dev/null +++ b/bluetooth/tracing/GetMemoryDump.ps1 @@ -0,0 +1,25 @@ +$ServiceName = 'BthServ' + +Trap [System.Management.Automation.ParameterBindingException] { + Write-Host "Could not find $ServiceName, it is likely not running." -ForegroundColor Red + Break} +$Process = Get-Process -Id (Get-WmiObject -Class Win32_Service -Filter "Name LIKE '$ServiceName'" | Select-Object -ExpandProperty ProcessId) + +$DumpFilePath = Join-Path $pwd "$($ServiceName)_$($Process.Id).dmp" +$DumpFile = New-Object IO.FileStream($DumpFilePath, [IO.FileMode]::Create) + +$WER = [PSObject].Assembly.GetType('System.Management.Automation.WindowsErrorReporting') +$NativeMethods = $WER.GetNestedType('NativeMethods', 'NonPublic') +$MiniDump = $NativeMethods.GetMethod('MiniDumpWriteDump', ([Reflection.BindingFlags]'NonPublic, Static')) + +$Result = $MiniDump.Invoke($null, @($Process.Handle, $Process.Id, $DumpFile.SafeFileHandle, [UInt32]0x2, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero)) + +$DumpFile.Close() + +if(-not $Result) { + Write-Host "Failed to write dump file for service $ServiceName with PID $Process.Id." + Trap {Continue} Remove-Item $DumpFilePath + return +} else { + Write-Host "Dump successfully collected in $DumpFilePath." +} From 859ccde0064c646c5140c9ec77e085d7edfbd5b1 Mon Sep 17 00:00:00 2001 From: Frank Gorgenyi Date: Thu, 2 May 2019 07:52:33 -0700 Subject: [PATCH 2/6] Update readme.md --- bluetooth/tracing/readme.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bluetooth/tracing/readme.md b/bluetooth/tracing/readme.md index 44cfa05..fa55f00 100644 --- a/bluetooth/tracing/readme.md +++ b/bluetooth/tracing/readme.md @@ -51,3 +51,10 @@ wpr.exe /? will also give you more information. From PowerShell execute: wget https://github.com/Microsoft/busiotools/raw/master/bluetooth/tracing/GetBluetoothRadioInfo.ps1 -UseBasicParsing | iex + +# How to collect memory dump + +From elevated PowerShell execute: + + wget https://github.com/Microsoft/busiotools/raw/master/bluetooth/tracing/GetMemoryDump.ps1 -UseBasicParsing | iex + From d78bbaff52be2f70aea4f41105df04cb1c794a88 Mon Sep 17 00:00:00 2001 From: Frank Gorgenyi Date: Thu, 2 May 2019 07:53:57 -0700 Subject: [PATCH 3/6] Update GetMemoryDump.ps1 --- bluetooth/tracing/GetMemoryDump.ps1 | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/bluetooth/tracing/GetMemoryDump.ps1 b/bluetooth/tracing/GetMemoryDump.ps1 index aa71a9f..73bc9c5 100644 --- a/bluetooth/tracing/GetMemoryDump.ps1 +++ b/bluetooth/tracing/GetMemoryDump.ps1 @@ -2,7 +2,7 @@ $ServiceName = 'BthServ' Trap [System.Management.Automation.ParameterBindingException] { Write-Host "Could not find $ServiceName, it is likely not running." -ForegroundColor Red - Break} + Break } $Process = Get-Process -Id (Get-WmiObject -Class Win32_Service -Filter "Name LIKE '$ServiceName'" | Select-Object -ExpandProperty ProcessId) $DumpFilePath = Join-Path $pwd "$($ServiceName)_$($Process.Id).dmp" @@ -16,10 +16,11 @@ $Result = $MiniDump.Invoke($null, @($Process.Handle, $Process.Id, $DumpFile.Safe $DumpFile.Close() -if(-not $Result) { +if (-not $Result) { Write-Host "Failed to write dump file for service $ServiceName with PID $Process.Id." - Trap {Continue} Remove-Item $DumpFilePath - return -} else { - Write-Host "Dump successfully collected in $DumpFilePath." -} + Break } + +Compress-Archive $DumpFilePath "$DumpFilePath.zip" -CompressionLevel Optimal -Force +Trap {Continue} Remove-Item $DumpFilePath + +Write-Host "Dump successfully collected in $DumpFilePath.zip." From 11c1767d6ad718404ee5c0b7d44811b7d155d669 Mon Sep 17 00:00:00 2001 From: Frank Gorgenyi Date: Thu, 2 May 2019 07:55:40 -0700 Subject: [PATCH 4/6] Update GetMemoryDump.ps1 --- bluetooth/tracing/GetMemoryDump.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bluetooth/tracing/GetMemoryDump.ps1 b/bluetooth/tracing/GetMemoryDump.ps1 index 73bc9c5..758f89b 100644 --- a/bluetooth/tracing/GetMemoryDump.ps1 +++ b/bluetooth/tracing/GetMemoryDump.ps1 @@ -5,7 +5,7 @@ Trap [System.Management.Automation.ParameterBindingException] { Break } $Process = Get-Process -Id (Get-WmiObject -Class Win32_Service -Filter "Name LIKE '$ServiceName'" | Select-Object -ExpandProperty ProcessId) -$DumpFilePath = Join-Path $pwd "$($ServiceName)_$($Process.Id).dmp" +$DumpFilePath = Join-Path $Env:Temp "$($ServiceName)_$($Process.Id).dmp" $DumpFile = New-Object IO.FileStream($DumpFilePath, [IO.FileMode]::Create) $WER = [PSObject].Assembly.GetType('System.Management.Automation.WindowsErrorReporting') From 729b9fc9cae97d08ae5991239d94cc7c742bc31f Mon Sep 17 00:00:00 2001 From: Frank Gorgenyi Date: Thu, 2 May 2019 09:05:44 -0700 Subject: [PATCH 5/6] Add support for multiple processes --- bluetooth/tracing/GetMemoryDump.ps1 | 51 +++++++++++++++++++---------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/bluetooth/tracing/GetMemoryDump.ps1 b/bluetooth/tracing/GetMemoryDump.ps1 index 758f89b..79855aa 100644 --- a/bluetooth/tracing/GetMemoryDump.ps1 +++ b/bluetooth/tracing/GetMemoryDump.ps1 @@ -1,26 +1,41 @@ -$ServiceName = 'BthServ' +$WerNativeMethods = [PSObject].Assembly.GetType('System.Management.Automation.WindowsErrorReporting').GetNestedType('NativeMethods', 'NonPublic') +$MiniDumpWriteDump = $WerNativeMethods.GetMethod('MiniDumpWriteDump', ([Reflection.BindingFlags]'NonPublic, Static')) -Trap [System.Management.Automation.ParameterBindingException] { - Write-Host "Could not find $ServiceName, it is likely not running." -ForegroundColor Red - Break } -$Process = Get-Process -Id (Get-WmiObject -Class Win32_Service -Filter "Name LIKE '$ServiceName'" | Select-Object -ExpandProperty ProcessId) +$ServiceNames = @('BthServ', 'Bluetooth%', 'BTAGService', 'BthAvctpSvc') -$DumpFilePath = Join-Path $Env:Temp "$($ServiceName)_$($Process.Id).dmp" -$DumpFile = New-Object IO.FileStream($DumpFilePath, [IO.FileMode]::Create) +If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(` -$WER = [PSObject].Assembly.GetType('System.Management.Automation.WindowsErrorReporting') -$NativeMethods = $WER.GetNestedType('NativeMethods', 'NonPublic') -$MiniDump = $NativeMethods.GetMethod('MiniDumpWriteDump', ([Reflection.BindingFlags]'NonPublic, Static')) + [Security.Principal.WindowsBuiltInRole] “Administrator”)) +{ + Write-Error "Administrator rights are required to collect dumps." + Break -$Result = $MiniDump.Invoke($null, @($Process.Handle, $Process.Id, $DumpFile.SafeFileHandle, [UInt32]0x2, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero)) +} -$DumpFile.Close() +$DumpFolder = Join-Path ($Env:Temp) (Get-Random -Minimum 1000) +New-Item -Path $DumpFolder -Type Directory | Out-Null -if (-not $Result) { - Write-Host "Failed to write dump file for service $ServiceName with PID $Process.Id." - Break } +foreach ( $ServiceName in $ServiceNames ) { + Trap [System.Management.Automation.ParameterBindingException] { + Write-Warning "Could not find $ServiceName, it is likely not running." + Break } + $Process = Get-Process -Id (Get-WmiObject -Class Win32_Service -Filter "Name LIKE '$ServiceName'" | Select-Object -ExpandProperty ProcessId) -Compress-Archive $DumpFilePath "$DumpFilePath.zip" -CompressionLevel Optimal -Force -Trap {Continue} Remove-Item $DumpFilePath + $DumpFilePath = Join-Path $DumpFolder "$($ServiceName)_$($Process.Id).dmp" + $DumpFile = New-Object IO.FileStream($DumpFilePath, [IO.FileMode]::Create) -Write-Host "Dump successfully collected in $DumpFilePath.zip." + Write-Host "Dumping service $ServiceName with PID $($Process.Id)..." + $Result = $MiniDumpWriteDump.Invoke($null, @($Process.Handle, $Process.Id, $DumpFile.SafeFileHandle, [UInt32]0x2, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero)) + + $DumpFile.Close() + + if (-not $Result) { + Write-Error "Failed to write dump file for service $ServiceName with PID $($Process.Id)." + Break } +} + +if ((gci $DumpFolder).Count -gt 1) { + Compress-Archive $DumpFolder "$DumpFolder.zip" -CompressionLevel Optimal -Force + Write-Host "Dumps successfully stored in $DumpFolder.zip." +} +Trap {Continue} Remove-Item $DumpFolder -Force -Recurse From 6178d655acdb67304627050b3f63ddcb652849ed Mon Sep 17 00:00:00 2001 From: Frank Gorgenyi Date: Thu, 2 May 2019 09:13:40 -0700 Subject: [PATCH 6/6] Improved error handling --- bluetooth/tracing/GetMemoryDump.ps1 | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/bluetooth/tracing/GetMemoryDump.ps1 b/bluetooth/tracing/GetMemoryDump.ps1 index 79855aa..cc1c0ea 100644 --- a/bluetooth/tracing/GetMemoryDump.ps1 +++ b/bluetooth/tracing/GetMemoryDump.ps1 @@ -5,11 +5,9 @@ $ServiceNames = @('BthServ', 'Bluetooth%', 'BTAGService', 'BthAvctpSvc') If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(` - [Security.Principal.WindowsBuiltInRole] “Administrator”)) -{ + [Security.Principal.WindowsBuiltInRole] “Administrator”)) { Write-Error "Administrator rights are required to collect dumps." Break - } $DumpFolder = Join-Path ($Env:Temp) (Get-Random -Minimum 1000) @@ -18,24 +16,28 @@ New-Item -Path $DumpFolder -Type Directory | Out-Null foreach ( $ServiceName in $ServiceNames ) { Trap [System.Management.Automation.ParameterBindingException] { Write-Warning "Could not find $ServiceName, it is likely not running." - Break } + Continue } + $Process = $null #ensure null on failure below $Process = Get-Process -Id (Get-WmiObject -Class Win32_Service -Filter "Name LIKE '$ServiceName'" | Select-Object -ExpandProperty ProcessId) + + if ($Process) { + $DumpFilePath = Join-Path $DumpFolder "$($ServiceName)_$($Process.Id).dmp" + $DumpFile = New-Object IO.FileStream($DumpFilePath, [IO.FileMode]::Create) - $DumpFilePath = Join-Path $DumpFolder "$($ServiceName)_$($Process.Id).dmp" - $DumpFile = New-Object IO.FileStream($DumpFilePath, [IO.FileMode]::Create) - - Write-Host "Dumping service $ServiceName with PID $($Process.Id)..." - $Result = $MiniDumpWriteDump.Invoke($null, @($Process.Handle, $Process.Id, $DumpFile.SafeFileHandle, [UInt32]0x2, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero)) + Write-Host "Dumping service $ServiceName with PID $($Process.Id)..." + $Result = $MiniDumpWriteDump.Invoke($null, @($Process.Handle, $Process.Id, $DumpFile.SafeFileHandle, [UInt32]0x2, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero)) - $DumpFile.Close() + $DumpFile.Close() - if (-not $Result) { - Write-Error "Failed to write dump file for service $ServiceName with PID $($Process.Id)." - Break } + if (-not $Result) { + Write-Error "Failed to write dump file for service $ServiceName with PID $($Process.Id)." + Break } + } } if ((gci $DumpFolder).Count -gt 1) { Compress-Archive $DumpFolder "$DumpFolder.zip" -CompressionLevel Optimal -Force - Write-Host "Dumps successfully stored in $DumpFolder.zip." + Write-Host "Dumps stored in $DumpFolder.zip." } + Trap {Continue} Remove-Item $DumpFolder -Force -Recurse