How to dump the output of !dumpheap into a file, w

2019-06-07 14:45发布

问题:

Please, observe:

0:000> !dumpheap -stat
Statistics:
              MT    Count    TotalSize Class Name
000007fefa9c8c58        1           24 System.ServiceProcess.Res
000007fef99d3de8        1           24 System.Collections.Generic.GenericArraySortHelper`1[[System.DateTime, mscorlib]]
000007fef99cf8e8        1           24 System.Collections.Generic.GenericComparer`1[[System.Decimal, mscorlib]]
...
000007fef8f02090    47295    141585288 System.Char[]
000007fe9b1cbe20  1064155    229857480 Xyz.DataServices.Platform.BalanceTransactionForAccrualByOrg
000007fef8f06888    21401    238104833 System.Byte[]
000007fe9cd2efb8  1211503    358604888 Xyz.DataServices.Platform.AEBalanceTransaction
0000000000386b30 11759620   1701611244      Free
000007fef8e94918   777049   2352540408 System.Object[]
000007fe9c4370e8 10571587   2621753576 Xyz.DB.DL.HREEmployeeManager
000007fef8f00e08 131198125   1859306044 System.String
Total 163792779 objects
Fragmented blocks larger than 0.5 MB:
            Addr     Size      Followed by
...

Which means I have tens (hundreds?) of millions of objects on the heap. I would like to dump the information about them into a file.

So How to redirect windbg command to a file without echoing the output on the windbg console? tells me the approach, but is it the best we can get when we are talking about tens (hundreds?) of millions of rows?

I was wondering whether there is a programmatic API against which I could program to extract the data from the dump directly, rather than through WinDBG user interface. Or maybe there a plugin out there that already does it...

回答1:

At the end of the day I have written a powershell script to run a single debugger command using CDB. Then the output is saved to the file.

Here it is:

param(
  [Parameter(Mandatory=$true, Position = 0)][ValidateScript({Test-Path $_ })]
  $dump,
  [Parameter(ParameterSetName='cmd', Mandatory=$true, Position = 1)][ValidateNotNullOrEmpty()]
  $command,
  [Parameter(ParameterSetName='script', Mandatory=$true)][ValidateNotNullOrEmpty()]
  $scriptFile,
  [Parameter(ParameterSetName='init', Mandatory=$true)][ValidateNotNullOrEmpty()][switch]
  $init,
  [Parameter()]
  $imagePath,
  [Parameter()]
  $sosexPath = "e:\utils\sosex\64\sosex.dll",
  [Parameter()][switch]
  $ScanForImages,
  [Parameter()][switch]
  $NoSymbolServer,
  [Parameter()][switch]
  $NoExit
)
$dump = (dir $dump).FullName

$cdb = "C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x64\cdb.exe"
$InternalScriptFile = [io.path]::GetTempFileName()
$log = [io.path]::GetTempFileName()

if ($ScanForImages)
{
  $ImgScan = ".imgscan /l"
}

Set-Content $InternalScriptFile @"
.logopen `"${log}.init`"
.load $sosexPath
"@

if ($init)
{
  $CDBOutputDevice = "Out-Default"
  Add-Content $InternalScriptFile @"
.logclose
!bhi
"@
}
elseif ($command)
{
  $CDBOutputDevice = "Out-Null"
  Add-Content $InternalScriptFile @"
!lhi
$ImgScan
.logclose
.logopen `"${log}`"
$command
.logclose
"@
}
else
{
  $CDBOutputDevice = "Out-Null"
  Add-Content $InternalScriptFile @"
!lhi
$ImgScan
.logclose
.logopen `"${log}`"
"@
  Get-Content $scriptFile | Out-File -FilePath $InternalScriptFile -Encoding ASCII -Append
  Add-Content $InternalScriptFile @"
.logclose
"@
}

if ($NoExit)
{
  $CDBOutputDevice = "Out-Default"
}
else
{
  Add-Content $InternalScriptFile @"
q
"@
}

if ($NoSymbolServer)
{
  $symbols = "e:\Symbols"
}
else
{
  $symbols = "cache*e:\Symbols;srv*http://localhost:33417;srv*http://msdl.microsoft.com/download/symbols"
}
if (Test-Path $imagePath -PathType Container)
{
  $symbols = "$imagePath;$symbols"
}

&$cdb -cf $InternalScriptFile -z $dump -y $symbols | &$CDBOutputDevice
if (!$init -and !$NoExit)
{
  cat $log
}

Not ideal - I am open to improvements. But it works.



标签: windbg