Skip to content

Latest commit

 

History

History
151 lines (132 loc) · 6.63 KB

SymbolValueConversion.md

File metadata and controls

151 lines (132 loc) · 6.63 KB

Custom Symbol Value Conversion

DbgShell lets you get symbol values as nice objects. This is extremely powerful for scripting!

For most objects, the stock object given to you by DbgShell is fine, but some objects are very painful to deal with in "raw" form—notably STL containers. So DbgShell has a generic "custom symbol value conversion" facility, which lets you register some little snippet of script to take a stock symbol value object and turn it into something more useful. You can add properties or methods, or create an entirely new object.

DbgShell comes with a few of these little conversion snippets, including some for dealing with STL containers:

STL symbol value type Gets tranformed into a .NET object of this type
std::string, std::wstring System.String
std::vector<*> System.Collections.ObjectModel.ReadOnlyCollection
std::map<*> System.Collections.ObjectModel.ReadOnlyDictionary
std::set<*> System.Collections.ObjectModel.ReadOnlyCollection

Examples

To give you an idea how these work, here’s the script for dealing with STL vectors:

Register-DbgValueScriptConverter -TypeName '!std::vector<?*>' -Converter {
    try
    {
        # $_ is the symbol
        $stockValue = $_.GetStockValue()

        $val = $null
        [bool] $unallocatedEmpty = $false
        if( $stockValue._Myfirst.DbgIsNull() )
        {
            $unallocatedEmpty = $true
            $val = New-Object 'System.Collections.Generic.List[PSObject]' -ArgumentList @( 0 )
        }
        else
        {
            $unallocatedEmpty = $false
            $elemType = $stockValue._Myfirst.DbgGetSymbol().Type.PointeeType
            $numElements = $stockValue._Mylast - $stockValue._Myfirst # N.B. Pointer arithmetic (takes element size into account)
            $val = New-Object 'System.Collections.Generic.List[PSObject]' -ArgumentList @( $numElements )
            for( [int] $i = 0; $i -lt $numElements; $i++ )
            {
                $elem = Get-DbgSymbolValue -Address ($stockValue._Myfirst + $i) -Type $elemType # N.B. Pointer arithmetic
                $null = $val.Add( $elem )
            }
        }

        $val = $val.AsReadOnly()

        if( $unallocatedEmpty )
        {
            Add-Member -InputObject $val -MemberType ScriptMethod -Name 'capacity' -Value { 0 }
            Add-Member -InputObject $val -MemberType ScriptMethod -Name 'size' -Value { 0 }
        }
        else
        {
            $capacity = $stockValue._MyEnd - $stockValue._Myfirst # N.B. Pointer arithmetic
            Add-Member -InputObject $val -MemberType ScriptMethod -Name 'capacity' -Value { $capacity }.GetNewClosure()
            Add-Member -InputObject $val -MemberType ScriptMethod -Name 'size' -Value { $numElements }.GetNewClosure()
        }

        Write-Collection -Collection $val
    }
    finally
    {
    }
} # end vector converter

That's relatively simple and gives you the general idea. The one for maps is a little more involved, but still not too bad:

Register-DbgValueScriptConverter -TypeName '!std::map<?*>' -Converter {
    try
    {
        # $_ is the symbol
        $stockValue = $_.GetStockValue()

        # This "root" node does not have a value, and is used as a sentinel (sub-tree pointers
        # point to the root to mean "no sub-tree").
        $dummyHead = $stockValue._Myhead
        <#assert#> if( $dummyHead._Isnil -ne 1 ) { throw "Expected dummy root to be 'nil'" }

        if( 0 -eq $stockValue._Mysize )
        {
            # Since the map is empty, we don't know what the key type will be...  but that's
            # okay, since it's empty.
            #
            # (We don't know what the key type will be, because even if we pulled the type
            # from the template parameters, a) that's the type in the target, and b) we don't
            # know what type key values would be after going through value conversion.)
            $d = New-Object 'System.Collections.Generic.Dictionary[PSObject,PSObject]' -ArgumentList @( 0 )
            $d = New-Object 'System.Collections.ObjectModel.ReadOnlyDictionary[PSObject,PSObject]' -ArgumentList @( $d )
            Write-Collection -Collection $d
            return
        }

        $rootNode = $dummyHead._Parent
        $sampleKey = $rootNode._Myval.first
        # Of course, there's no facility to specify a comparer. I suppose if someone has a
        # certain case where they want a special comparer, they could write their own
        # converter, more narrowly specified for just their specific type.
        [string] $keyTypeName = $sampleKey.GetType().FullName
        if( ($sampleKey -is [MS.Dbg.DbgNullValue]) -or
            ($sampleKey -is [MS.Dbg.DbgPointerValue]) )
        {
            # Because we need to be able to handle both DbgPointerValue objects and
            # DbgNullValue objects as keys. Having a separate DbgNullValue type makes most
            # things simpler, but it makes this particular scenario a little more
            # complicated.
            $keyTypeName = 'MS.Dbg.DbgPointerValueBase'
        }
        $d = New-Object "System.Collections.Generic.Dictionary[$($keyTypeName),PSObject]" -ArgumentList @( $stockValue._Mysize )

        # We'll do a recursive traversal of the tree, using this function:
        function ProcessSubtree( $rootNode )
        {
            <#assert#> if( $rootNode._Isnil -ne 0 ) { throw "ProcessSubtree called with invalid node." }
            # We don't preserve the order, but we'll go ahead and process them
            # in the proper order anyway: left subtreee, current node, right
            # subtree.
            if( $dummyHead -ne $rootNode._Left )
            {
                ProcessSubtree $rootNode._Left
            }
            $d.Add( $rootNode._Myval.first, $rootNode._Myval.second )
            if( $dummyHead -ne $rootNode._Right )
            {
                ProcessSubtree $rootNode._Right
            }
        } # end ProcessSubtree

        ProcessSubtree $rootNode

        $d = New-Object "System.Collections.ObjectModel.ReadOnlyDictionary[$($keyTypeName),PSObject]" -ArgumentList @( $d )
        Write-Collection -Collection $d
    }
    finally { }
} # end map converter

More information

More information on various techniques for writing symbol value converters is available from within DbgShell—just run "Get-Help about_HowTo_Write_a_Symbol_Value_Converter". You can also see a bunch of existing converter scripts by running dir Bin:\Debugger\*.converters.*.