Analyser un dump de worker process avec PowerShell / Worker process dump analysis with PowerShell

English version

 

[MAJ 24/06/2016] Il y a quelques semaines un de mes collègues (Merci Julien !), m'a fait découvrir PowerDbg. PowerDbg est une librairie PowerShell vous permettant de créer facilement des scripts PowersShell pour automatiser des sessions de debugging WinDbg / CDB (plus de détails ici). Ce collègue m'envoya un exemple d'utilisation de PowerDbg pour le debug d'une application ASP.Net au sein d'un worker processus IIS - w3wp.exe). J'ai décidé de développer une version "full" PowerShell de cet exemple (J'ai supprimé des dépences externes de la version originale) que je vous propose dans cet article (le fichier source est disponible ici). Dans cet exemple je charge seulement 2 extensions - SOS and CLR - et je lance seulement deux commandes - !pe and !ClrStack. Vous pouvez personnaliser ou ajouter les extensions que vous voulez charger et les commandes associées. Le résultat est un fichier CSV ressemblant à peu près à celui-ci (sortie volontairement tronquée) :

powerdbg

Si vous désirez générer des dumps de worker processus vous pouvez jeter un coup d'oeil à ces deux ressources :


Version française

[Updated 06/24/2016] Some weeks ago a colleague of mine (Thanks Julien !),pointed me to PowerDbg. PowerDbg is a PowerShell library that enables you to easily create PowerShell scripts to automate a WinDbg / CDB debugging session (further details here). This colleague sent to me a PowerDbg use case for debugging an ASP.Net application (inside an IIS worker process - w3wp.exe). I decided to build a full PowerShell version of this example (I removed some external depencies of the original version) and I propose it to you in this article (the source file is available here). In this example I load only 2 extensions - SOS and CLR - and I run only two commands - !pe and !ClrStack. You can add or customize the extensions you want to load and the related commands. The result is a CSV file looking like this one (output was truncated):

 powerdbg

If you want generate worker process dumps you can take a look at this two resources :

 #requires -version 2

#requires -Module PowerDbg
Import-Module -Name PowerDbg

#region My function definitions
function New-PDBGCrashAnalysis
{
    <#
            .SYNOPSIS
            Runs a debugging session for the dump(s) passed as parameter(s)

            .DESCRIPTION
            Runs a debugging session for the dump(s) passed as parameter(s)

            .PARAMETER FullName
            The full file path of the dump(s) to analyze

            .EXAMPLE
            New-PDBGCrashAnalysis -FullName "C:\Tools\Dumps\w3wp.exe_160518_000457.dmp", "C:\Tools\Dumps\w3wp.exe_160518_002041.dmp" -Verbose
            Runs a debugging session for the two specified worker process dumps ("C:\Tools\Dumps\w3wp.exe_160518_000457.dmp", "C:\Tools\Dumps\w3wp.exe_160518_002041.dmp" )

            .EXAMPLE
            Get-ChildItem -Path 'C:\Temp' -Filter 'w3wp*.dmp' -Recurse | New-PDBGCrashAnalysis -Verbose
            Returns all worker processes dumps in the C:\Temp folder (and its subfolders) and runs a debugging session for all of them.
    #>
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
        #The BLG File to convert : Checking if the file exists and has the .dmp extension
        [ValidateScript({
                    (Test-Path -Path $_ -PathType Leaf) -and ($_ -match '\.dmp$')
        })]
        [alias('Source', 'Dump')]
        [String[]]$FullName
    )
    begin
    {
        #Array for storing the results
        $Analyses = @()
    }
    process
    {
        #For all files passed as argument outside a pipeline context
        Foreach ($CurrentFullName in $FullName)
        {
            Write-Verbose -Message "Processing $CurrentFullName ..."
            
            # Close proactively any existing debugger session if already connected for some reason
            Exit-DbgSession #Disconnect-Windbg
            Write-Verbose -Message 'Closing proactively any existing debugger session if already connected for some reason ...'

            Write-Verbose -Message 'Creating new DBG Session.'
            New-DbgSession -Dump $CurrentFullName

            Write-Verbose -Message 'Loading sos and clr extensions'
            Load-DbgExtension sos
            Load-DbgExtension clr

            #Invoke-DbgCommand !pe2
            Write-Verbose -Message 'Invoking DBG command : !pe'
            $Exception = Invoke-DbgCommand !pe
            Write-Verbose -Message 'Invoking DBG command : !ClrStack'
            $ClrStack = Invoke-DbgCommand !ClrStack

            Write-Verbose -Message "Exception : $Exception "
            Write-Verbose -Message "ClrStack : $ClrStack "

            $Analyses += New-Object -TypeName PSObject -Property @{
                FullName  = $CurrentFullName
                Exception = $Exception[1]
                Message   = $Exception[2]
                ClrStack  = $ClrStack -join "`r`n"
            }

            Write-Verbose -Message 'Disconnecting new DBG Session'
            Exit-DbgSession #Disconnect-Windbg
        }
    }
    end
    {
        #returning the data array
        return $Analyses
    }
}
#endregion

Clear-Host
# Getting the this script path
$CurrentScript = $MyInvocation.MyCommand.Path
# Getting the directory of this script
$CurrentDir = Split-Path -Path $CurrentScript -Parent

# Creating CSV file name based on the script full file path and by appending the timestamp at the end of the file name
$CSVFile = $CurrentScript.replace((Get-Item -Path $CurrentScript).Extension, '_'+$(Get-Date  -Format 'yyyyMMddTHHmmss')+'.csv')

# Without pipeline use
#$Analyses = New-PDBGCrashAnalysis -FullName "C:\Tools\Dumps\w3wp.exe_160518_000457.dmp", "C:\Tools\Dumps\w3wp.exe_160518_002041.dmp" -Verbose
# With pipeline use
# Looking for dumps in the script folder (and subfolders)
$Analyses = Get-ChildItem -Path $CurrentDir -Filter 'w3wp*.dmp' -Recurse | New-PDBGCrashAnalysis -Verbose
$Analyses |
Group-Object -Property Message |
Sort-Object -Property Count -Descending |
Select-Object -Property @{
    Name       = 'Message'
    Expression = {
        $_.Name
    }
}, Count
#$Analyses | Format-List * -Force
$Analyses | Export-Csv -Path $CSVFile -Force -NoTypeInformation
Write-Host -Object "Results are available in '$CSVFile'"

Laurent.