windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/dbgext/dumpoff.cxx

1831 lines
45 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
dumpoff.cxx
Abstract:
Structure dumper
Author:
Bilal Alam (balam) Oct-17-1998 Initial Revision
--*/
#include "inetdbgp.h"
#include "oemdbi.h"
#include "cvinfo.h"
#include "imagehlp.h"
#define INVALID_LENGTH ((DWORD)-1)
#define MAX_MEMBERNAME_LENGTH 256
#define MAX_TYPENAME_LENGTH 256
#define MAX_ARG_SIZE 256
typedef DWORD (*PFN_READ_MEMORY) (
VOID * address,
DWORD cbBytes,
VOID *pBuffer
);
typedef DWORD (*PFN_PRINTF) (
CHAR * pszBuffer,
DWORD cbBytes
);
typedef struct _STRUCTURE_MEMBER {
CHAR achMemberName[ MAX_MEMBERNAME_LENGTH + 1 ];
DWORD cbOffset;
DWORD cbMaxSize;
} STRUCTURE_MEMBER, *PSTRUCTURE_MEMBER;
typedef struct _STRUCTURE_TEMPLATE {
CHAR achName[ MAX_TYPENAME_LENGTH + 1 ];
PSTRUCTURE_MEMBER pMembers;
DWORD cMembers;
DWORD cUseful;
DWORD cbTotalSize;
DWORD Type;
} STRUCTURE_TEMPLATE, *PSTRUCTURE_TEMPLATE;
DWORD
GetOffset(
BYTE * pBuffer,
DWORD * pcbOffset
);
DWORD
ReadFieldList(
TPI * pTypeInterface,
STRUCTURE_TEMPLATE* pStructure,
lfFieldList * pFieldList,
DWORD cbLen,
DWORD dwFlags
);
DWORD
ReadBClass(
TPI * pTypeInterface,
lfBClass * pBClass,
DWORD * pcbReturnSize,
DWORD * pcbOffset,
CHAR * pszBuffer,
DWORD cbBuffer
);
DWORD
ReadMember(
lfMember* pMember,
DWORD * pcbReturnSize,
DWORD * pcbOffset,
CHAR * pszBuffer,
DWORD cbBuffer
);
DWORD
ReadNestType(
lfNestType* pNestType,
DWORD * pcbReturnSize,
DWORD * pcbOffset,
CHAR * pszBuffer,
DWORD cbBuffer
);
DWORD
ReadOneMethod(
lfOneMethod* pOneMethod,
DWORD * pcbReturnSize,
DWORD * pcbOffset,
CHAR * pszBuffer,
DWORD cbBuffer
);
DWORD
ReadMethod(
lfMethod* pMethod,
DWORD * pcbReturnSize,
DWORD * pcbOffset,
CHAR * pszBuffer,
DWORD cbBuffer
);
DWORD
ReadVTable(
lfVFuncTab* pVTable,
DWORD * pcbReturnSize,
DWORD * pcbOffset,
CHAR * pszBuffer,
DWORD cbBuffer
);
DWORD
ReadStaticMember(
lfSTMember* pStaticMember,
DWORD * pcbReturnSize,
DWORD * pcbOffset,
CHAR * pszBuffer,
DWORD cbBuffer
);
DWORD
InitializeStructureTemplate(
PSTRUCTURE_TEMPLATE pTemplate
);
DWORD
TerminateStructureTemplate(
PSTRUCTURE_TEMPLATE pTemplate
);
VOID
DumpoffUsage(
VOID
);
DWORD
OutputTemplate(
STRUCTURE_TEMPLATE * pTemplate,
CHAR * pszMemberName,
DWORD dwFlags,
PVOID pvAddress,
PFN_READ_MEMORY pfnReadMemory,
PFN_PRINTF pfnPrintf
);
DWORD
BuildMemberList(
IN PSTRUCTURE_TEMPLATE pTemplate,
IN TPI * pTypeInterface,
IN TI tiType,
IN BOOL fTypeSizeOnly
);
DWORD
BuildMemberListForTypeName(
IN PSTRUCTURE_TEMPLATE pTemplate,
IN PDB * pPDB,
IN LPSTR pszTypeName
);
DWORD
FindMembersOfTypeSize(
IN PDB * pPDB,
IN DWORD cbSize,
IN PFN_PRINTF pfnPrintf
);
DWORD
OutputTemplate(
STRUCTURE_TEMPLATE * pTemplate,
CHAR * pszMemberName,
DWORD dwFlags,
PVOID pvAddress,
PFN_READ_MEMORY pfnReadMemory,
PFN_PRINTF pfnPrintf
)
/*++
Routine Description:
Output a structure template.
If pvAddress is NULL, then this function will output a general template
of the structure, listing each member along with its offset from the
start of the structure.
If pvAddress is non-NULL, then this function will output the structure
with the memory at pvAddress cast as this type.
If pszMemberName is NULL, then the above two statements apply to all
members of the structure. If pszMember is not NULL, then the statements
apply only the member whose name is pszMember.
Arguments:
pTemplate - Template to dump
pszMemberName - Optional member name filter
dwFlags - Flags describing Output() details. Currently not supported
pvAddress - Optional address of memory to cast as type
pfnReadMemory - Provides read memory functionality
pfnPrintf - Provides printf functionality
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
DWORD cCounter;
BOOL fDidSomething = FALSE;
PBYTE pBuffer = NULL;
DWORD dwError = ERROR_SUCCESS;
CHAR achFormat[ 256 ];
DWORD cbRunningLength;
BOOL fLastBitField = FALSE;
DWORD cBitField = 0;
DWORD dwTotalMask;
DWORD cbSize;
INT i;
pBuffer = (PBYTE) LocalAlloc( LPTR, pTemplate->cbTotalSize );
if ( pBuffer == NULL )
{
dwError = GetLastError();
goto Finished;
}
//
// If address is specified, then read the amount required for this
// structure. Otherwise, we are simply dumping the template and thus
// output the size of the type
//
if ( pvAddress )
{
dwError = pfnReadMemory( pvAddress, pTemplate->cbTotalSize, pBuffer );
if ( dwError != ERROR_SUCCESS )
{
goto Finished;
}
}
else
{
_snprintf( achFormat,
sizeof( achFormat ),
"sizeof( %s %s ) = 0x%X bytes (%d bytes)\n",
pTemplate->Type == LF_CLASS ? "class" : "struct",
pTemplate->achName,
pTemplate->cbTotalSize,
pTemplate->cbTotalSize );
pfnPrintf( achFormat, -1 );
}
//
// Iterate through consequential members of type
//
for( cCounter = 0;
cCounter < pTemplate->cUseful;
cCounter++ )
{
//
// Do filtering on member name if specified
//
if ( pszMemberName )
{
if ( fDidSomething )
{
break;
}
if ( strcmp( pTemplate->pMembers[ cCounter ].achMemberName,
pszMemberName ) )
{
continue;
}
}
//
// Dump member name
//
cbRunningLength = pfnPrintf( pTemplate->pMembers[ cCounter ].achMemberName,
-1 );
//
// Formatting junk
//
for ( i = cbRunningLength;
i < 25;
i++ )
{
cbRunningLength += pfnPrintf( " ", -1 );
}
cbRunningLength += pfnPrintf( " = ", -1 );
achFormat[ 0 ] = '\0';
cbSize = pTemplate->pMembers[ cCounter ].cbMaxSize;
if ( !pvAddress )
{
//
// Just dumping template. Output the offset from the start of
// the type
//
_snprintf( achFormat,
sizeof( achFormat ),
"+%8X",
pTemplate->pMembers[ cCounter ].cbOffset );
}
else if ( !cbSize ||
( ( cbSize == sizeof( DWORD ) ) && fLastBitField ) )
{
//
// If the maxsize is 0, then this must be a bitfield
// If the maxsize is 4, and the last item was a bit field, then
// this must be the last bit of the bit field.
//
// TODO: Need to make this work for bit fields larger than 32
//
if ( !fLastBitField )
{
fLastBitField = TRUE;
}
cBitField++;
dwTotalMask = (DWORD) *(DWORD*) ((PBYTE)pBuffer+
pTemplate->pMembers[ cCounter ].cbOffset);
_snprintf( achFormat,
sizeof( achFormat ),
"%s",
(dwTotalMask & (1 << ( cBitField - 1 ))) ? "TRUE" : "FALSE" );
if ( cbSize == sizeof( DWORD ) )
{
fLastBitField = FALSE;
cBitField = 0;
}
}
else if ( cbSize != sizeof( DWORD ) )
{
//
// If this structure is not a DWORD in size, then assume we don't
// know how to dump it affectly. In this case, we will simply
// dump out the address at which the member data starts
//
_snprintf( achFormat,
sizeof( achFormat ),
"%016p..",
(PBYTE) pvAddress +
pTemplate->pMembers[ cCounter ].cbOffset );
}
else
{
//
// This is a DWORD sized member. We can dump out the value
// effectively -> so we do it
//
_snprintf( achFormat,
sizeof( achFormat ),
"%08X",
(DWORD) *(DWORD*) ((PBYTE)pBuffer+
pTemplate->pMembers[ cCounter ].cbOffset ) );
}
cbRunningLength += pfnPrintf( achFormat, -1 );
//
// Two column display. Given 80 columns, seems like the only
// reasonable setting (maybe 1 column is useful?)
//
if ( pszMemberName ||
( cCounter % 2 ) )
{
pfnPrintf( "\n", -1 );
}
else
{
for ( i = cbRunningLength;
i < 40;
i++ )
{
pfnPrintf( " ", -1 );
}
}
//
// Keep tabs on whether we actually dumped something
//
fDidSomething = TRUE;
}
if ( !fDidSomething )
{
dwError = ERROR_FILE_NOT_FOUND;
}
pfnPrintf( "\n", -1 );
Finished:
if ( pBuffer != NULL )
{
LocalFree( pBuffer );
}
return dwError;
}
DWORD
BuildMemberListForTypeName(
IN PSTRUCTURE_TEMPLATE pTemplate,
IN PDB * pPDB,
IN LPSTR pszTypeName
)
/*++
Routine Description:
Build the structure template for a given type
Arguments:
pTemplate - Template to populate (must have been previously inited)
pPDB - PDB structure opened using MSDBI!PDBOpen
pszTypeName - Name of type
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
TPI * pTypeInterface = NULL;
TI RootTI;
DWORD dwError = ERROR_SUCCESS;
PB pb;
if ( !pTemplate || !pPDB || !pszTypeName )
{
dwError = ERROR_INVALID_PARAMETER;
goto Finished;
}
//
// Get the type interface
//
if ( !PDBOpenTpi( pPDB,
pdbRead,
&pTypeInterface ) )
{
dwError = GetLastError();
goto Finished;
}
//
// Does this PDB have the necessary type information?
//
if ( TypesQueryTiMinEx( pTypeInterface ) ==
TypesQueryTiMacEx( pTypeInterface ) )
{
dwError = ERROR_NOT_SUPPORTED;
goto Finished;
}
//
// Lookup with specified type
//
if ( !TypesQueryTiForUDTEx( pTypeInterface,
pszTypeName,
TRUE,
&RootTI) )
{
dwError = ERROR_FILE_NOT_FOUND;
goto Finished;
}
strncpy( pTemplate->achName,
pszTypeName,
sizeof( pTemplate->achName ) - 1 );
dwError = BuildMemberList( pTemplate,
pTypeInterface,
RootTI,
FALSE );
Finished:
if ( pTypeInterface != NULL )
{
TypesClose( pTypeInterface );
}
return dwError;
}
DWORD
FindMembersOfTypeSize(
IN PDB * pPDB,
IN DWORD cbSize,
IN PFN_PRINTF pfnPrintf
)
/*++
Routine Description:
Find members of a certain size. Output these types
Arguments:
pPDB - PDB structure opened using MSDBI!PDBOpen
cbSize - Size in question
pfnPrintf - Output routine
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
STRUCTURE_TEMPLATE Template;
TPI * pTypeInterface = NULL;
DWORD dwError = ERROR_SUCCESS;
TI tiMin;
TI tiMax;
TI tiCursor;
CHAR achLast[ MAX_TYPENAME_LENGTH ];
CHAR achBuffer[ 256 ];
if ( !pPDB || !pfnPrintf || !cbSize )
{
dwError = ERROR_INVALID_PARAMETER;
goto Finished;
}
//
// Get the type interface
//
if ( !PDBOpenTpi( pPDB,
pdbRead,
&pTypeInterface ) )
{
dwError = GetLastError();
goto Finished;
}
//
// Get min/max type indices
//
tiMin = TypesQueryTiMinEx( pTypeInterface );
tiMax = TypesQueryTiMacEx( pTypeInterface );
if ( tiMin == tiMax )
{
//
// Probably no type info available in PDB
//
dwError = ERROR_NOT_SUPPORTED;
goto Finished;
}
//
// Cursor thru
//
achLast[ 0 ] = '\0';
for ( tiCursor = tiMin;
tiCursor < tiMax;
tiCursor++ )
{
dwError = BuildMemberList( &Template,
pTypeInterface,
tiCursor,
TRUE );
if ( dwError != ERROR_SUCCESS )
{
if ( dwError == ERROR_NOT_SUPPORTED )
{
//
// Not a struct/class. Ignore
//
dwError = ERROR_SUCCESS;
continue;
}
else
{
break;
}
}
if ( Template.cbTotalSize == cbSize &&
strcmp( Template.achName, achLast ) )
{
pfnPrintf( Template.Type == LF_CLASS ? "class " : "struct ", -1 );
pfnPrintf( Template.achName, -1 );
pfnPrintf( "\n", -1 );
strncpy( achLast,
Template.achName,
sizeof( achLast ) );
}
}
Finished:
if ( pTypeInterface != NULL )
{
TypesClose( pTypeInterface );
}
return dwError;
}
DWORD
BuildMemberList(
IN PSTRUCTURE_TEMPLATE pTemplate,
IN TPI * pTypeInterface,
IN TI tiType,
IN BOOL fTypeSizeOnly
)
/*++
Routine Description:
Build a template describing the given type. This template contains
an array of members representing the member of the type.
Arguments:
pTemplate - Template to populate (must have been previously inited)
pTypeInterface - Type interface
tiType - Type ID to retrieve
fStructSizeOnly - TRUE if we only need type size (and not the members)
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
TYPTYPE * pType = NULL;
lfStructure * pStructure;
lfFieldList * pFieldList;
PB pb;
DWORD dwError = ERROR_SUCCESS;
DWORD cbTotalSize = 0;
DWORD cbStructSize = 0;
DWORD cUseful;
DWORD cbNameOffset;
TI RootTI;
if ( !pTypeInterface || !pTemplate )
{
dwError = ERROR_INVALID_PARAMETER;
goto Finished;
}
RootTI = tiType;
//
// Parse root record of the type, verifying that it is of type
// STRUCTURE or CLASS
//
if ( !TypesQueryPbCVRecordForTiEx( pTypeInterface,
RootTI,
&pb ) )
{
dwError = ERROR_FILE_NOT_FOUND;
goto Finished;
}
pType = (TYPTYPE*) pb;
if ( ( pType->leaf != LF_CLASS ) &&
( pType->leaf != LF_STRUCTURE ) )
{
dwError = ERROR_NOT_SUPPORTED;
goto Finished;
}
pTemplate->Type = pType->leaf;
pStructure = (lfStructure*) &(pType->leaf);
cbNameOffset = GetOffset( pStructure->data, &cbStructSize );
//
// If we only need the overall structure size, then we can exit out now
//
if ( fTypeSizeOnly )
{
pTemplate->cbTotalSize = cbStructSize;
memset( pTemplate->achName,
0,
sizeof( pTemplate->achName ) );
strncpy( pTemplate->achName,
(LPSTR) pStructure->data + cbNameOffset + 1,
min( (DWORD) *(CHAR*)(pStructure->data + cbNameOffset),
sizeof( pTemplate->achName ) ) );
goto Finished;
}
//
// In allocating # of members for the structure, get upper bound by
// taking structure member count.
//
// OPTIMIZATION: Dynamically grow the list to avoid gross overestimation.
//
if ( pTemplate->cMembers < pStructure->count )
{
pTemplate->pMembers = (PSTRUCTURE_MEMBER) LocalAlloc( LPTR,
sizeof( STRUCTURE_MEMBER ) *
pStructure->count );
if ( pTemplate->pMembers == NULL )
{
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto Finished;
}
pTemplate->cMembers = pStructure->count;
}
if ( !TypesQueryPbCVRecordForTi( pTypeInterface,
pStructure->field,
&pb ) )
{
dwError = ERROR_FILE_NOT_FOUND;
goto Finished;
}
pType = (TYPTYPE*)pb;
pFieldList = (lfFieldList*) &(pType->leaf);
//
// Read the list of the fields in the type
//
dwError = ReadFieldList( pTypeInterface,
pTemplate,
pFieldList,
pType->len,
0 );
cUseful = pTemplate->cUseful;
if ( cUseful && ( dwError == ERROR_SUCCESS ) )
{
pTemplate->pMembers[ cUseful - 1 ].cbMaxSize =
cbStructSize - pTemplate->pMembers[ cUseful - 1 ].cbOffset;
pTemplate->cbTotalSize = cbStructSize;
}
Finished:
return dwError;
}
DWORD
ReadFieldList(
IN TPI * pTypeInterface,
IN STRUCTURE_TEMPLATE * pTemplate,
IN lfFieldList * pFieldList,
IN DWORD cbLen,
IN DWORD dwFlags
)
/*++
Routine Description:
Read the elements of the field list which represents the class/struct
Arguments:
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
DWORD cbBytes = 0;
PBYTE pBuffer;
DWORD cbReturnSize = 0;
BOOL fExit = FALSE;
CHAR achMemberBuffer[ 256 ];
DWORD cbMemberBuffer;
DWORD dwError = ERROR_SUCCESS;
DWORD cFields = 0;
DWORD cbLastOffset = 0;
while ( cbBytes < cbLen )
{
//
// Account for padding the field list blob
//
for ( ; ; )
{
pBuffer = (PBYTE) pFieldList->data + cbBytes;
if ( *(BYTE*)pBuffer < LF_PAD0 )
{
break;
}
cbBytes++;
}
//
// After each padding block (if any), the first SHORT will contain
// the field type of the next field in the struct/class. Handle
// each type accordingly. If the handle function (Read*) returns
// a cbReturnSize of -1 then this type will not contribute to the
// offsets in the struct/class. For example, member functions.
//
achMemberBuffer[ 0 ] = '\0';
cbReturnSize = 0;
cbMemberBuffer = sizeof( achMemberBuffer );
switch ( *(USHORT*) pBuffer )
{
case LF_BCLASS:
dwError = ReadBClass( pTypeInterface,
(lfBClass*) pBuffer,
&cbReturnSize,
&cbBytes,
achMemberBuffer,
cbMemberBuffer );
break;
case LF_MEMBER:
dwError = ReadMember( (lfMember*) pBuffer,
&cbReturnSize,
&cbBytes,
achMemberBuffer,
cbMemberBuffer );
break;
case LF_NESTTYPE:
dwError = ReadNestType( (lfNestType*) pBuffer,
&cbReturnSize,
&cbBytes,
achMemberBuffer,
cbMemberBuffer );
break;
case LF_ONEMETHOD:
dwError = ReadOneMethod( (lfOneMethod*) pBuffer,
&cbReturnSize,
&cbBytes,
achMemberBuffer,
cbMemberBuffer );
break;
case LF_METHOD:
dwError = ReadMethod( (lfMethod*) pBuffer,
&cbReturnSize,
&cbBytes,
achMemberBuffer,
cbMemberBuffer );
break;
case LF_STMEMBER:
dwError = ReadStaticMember( (lfSTMember*) pBuffer,
&cbReturnSize,
&cbBytes,
achMemberBuffer,
cbMemberBuffer );
break;
case LF_VFUNCTAB:
dwError = ReadVTable( (lfVFuncTab*) pBuffer,
&cbReturnSize,
&cbBytes,
achMemberBuffer,
cbMemberBuffer );
break;
default:
fExit = TRUE;
break;
}
if ( fExit )
{
break;
}
if ( dwError != ERROR_SUCCESS ||
cbReturnSize == INVALID_LENGTH )
{
continue;
}
//
// We got a useful member of the struct/class. Add it to the
// template.
//
pTemplate->cUseful++;
strncpy( pTemplate->pMembers[ cFields ].achMemberName,
achMemberBuffer,
sizeof( pTemplate->pMembers[ cFields ].achMemberName ) - 1 );
pTemplate->pMembers[ cFields ].cbOffset = cbReturnSize;
//
// Calculate the maximum size of the previous member by taking the
// difference between the start offset of the member and the start
// offset of the previous member. Note that this is not necessarily
// the exact size because of potential alignment padding. It is only
// an upper bound on the size of the previous member.
//
if ( cFields )
{
pTemplate->pMembers[ cFields - 1 ].cbMaxSize =
cbReturnSize - cbLastOffset;
}
cbLastOffset = cbReturnSize;
cFields++;
}
return dwError;
}
DWORD
ReadBClass(
IN TPI * pTypeInterface,
IN lfBClass* pBClass,
OUT DWORD * pcbReturnSize,
OUT DWORD * pcbOffset,
OUT CHAR * pszBuffer,
IN DWORD cbBuffer
)
/*++
Routine Description:
Read the type info of base class field
Arguments:
pTypeInterface - MSDBI type interface
pBClass - Points to a base class descriptor
pcbReturnSize - Filled with offset from start of original type
pcbOffset - Filled with new offset to result field traversal in
field list handler
pszBuffer - Filled with name of base class
cbBuffer - Size of pszBuffer
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
PB pb;
DWORD Offset;
TYPTYPE* pType;
lfStructure * pStructure;
DWORD cbUnused;
Offset = GetOffset( pBClass->offset, pcbReturnSize );
*pcbOffset += sizeof( lfBClass ) + Offset;
//
// We have to lookup the name of the base class explicitly by using the
// index type in the base class descriptor
//
if ( !TypesQueryPbCVRecordForTiEx( pTypeInterface,
pBClass->index,
&pb ) )
{
return ERROR_FILE_NOT_FOUND;
}
//
// Process/munge/extract
//
pType = (TYPTYPE*)pb;
pStructure = (lfStructure*) &(pType->leaf );
Offset = GetOffset( pStructure->data, &cbUnused );
memset( pszBuffer, 0, cbBuffer );
memcpy( pszBuffer,
(CHAR*) pStructure->data + Offset + 1,
min( (DWORD) *(CHAR*) ( pStructure->data + Offset ), cbBuffer ) );
return ERROR_SUCCESS;
}
DWORD
ReadMember(
IN lfMember * pMember,
OUT DWORD * pcbReturnSize,
OUT DWORD * pcbOffset,
IN CHAR * pszBuffer,
IN DWORD cbBuffer
)
/*++
Routine Description:
Read the type info of a member
Arguments:
pMember - Points to a member descriptor
pcbReturnSize - Filled with offset from start of original type
pcbOffset - Filled with new offset to result field traversal in
field list handler
pszBuffer - Filled with name of base class
cbBuffer - Size of pszBuffer
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
DWORD Offset = GetOffset( pMember->offset, pcbReturnSize );
memset( pszBuffer, 0, cbBuffer );
memcpy( pszBuffer,
(CHAR*) pMember->offset + Offset + 1,
min( (DWORD) *(CHAR*) ( pMember->offset + Offset ), cbBuffer ) );
*pcbOffset += sizeof( lfMember ) + Offset + pMember->offset[Offset] + 1;
return ERROR_SUCCESS;
}
DWORD
ReadOneMethod(
IN lfOneMethod * pOneMethod,
OUT DWORD * pcbReturnSize,
OUT DWORD * pcbOffset,
IN CHAR * pszBuffer,
IN DWORD cbBuffer
)
/*++
Routine Description:
Read the type info of a non-overloaded member function.
We process this only to up the offset within the field list for
traversal purposes. Member methods themselves have no affect on
the offsets/size of the data structure
Arguments:
pOneMethod - Method type descriptor
pcbReturnSize - Filled with offset from start of original type
pcbOffset - Filled with new offset to result field traversal in
field list handler
pszBuffer - Filled with name of base class
cbBuffer - Size of pszBuffer
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
CHAR * pszSource = NULL;
pszSource = (CHAR*) pOneMethod + sizeof( lfOneMethod );
*pcbOffset += sizeof( lfOneMethod );
if ( ( pOneMethod->attr.mprop == CV_MTintro ) ||
( pOneMethod->attr.mprop == CV_MTpureintro ) )
{
*pcbOffset += sizeof( LONG );
pszSource += sizeof( LONG );
}
*pcbOffset += *(CHAR*)pszSource + 1;
*pcbReturnSize = INVALID_LENGTH;
return ERROR_SUCCESS;
}
DWORD
ReadMethod(
IN lfMethod * pMethod,
OUT DWORD * pcbReturnSize,
OUT DWORD * pcbOffset,
IN CHAR * pszBuffer,
IN DWORD cbBuffer
)
/*++
Routine Description:
Read the type info of a member function. We process this only to
up the offset within the field list for traversal purposes. Member
methods themselves have no affect on the offsets/size of the data
structure
Arguments:
pMethod - Method type descriptor
pcbReturnSize - Filled with offset from start of original type
pcbOffset - Filled with new offset to result field traversal in
field list handler
pszBuffer - Filled with name of base class
cbBuffer - Size of pszBuffer
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
*pcbOffset += sizeof( lfMethod ) + pMethod->Name[ 0 ];
*pcbReturnSize = INVALID_LENGTH;
return ERROR_SUCCESS;
}
DWORD
ReadVTable(
IN lfVFuncTab * pVTable,
OUT DWORD * pcbReturnSize,
OUT DWORD * pcbOffset,
IN CHAR * pszBuffer,
IN DWORD cbBuffer
)
/*++
Routine Description:
Read the vtable of the structure.
Arguments:
pVTable - Vtable type descriptor
pcbReturnSize - Filled with offset from start of original type
pcbOffset - Filled with new offset to result field traversal in
field list handler
pszBuffer - Filled with name of base class
cbBuffer - Size of pszBuffer
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
*pcbOffset += sizeof( lfVFuncTab );
strncpy( pszBuffer,
"'vftable'",
cbBuffer - 1 );
//
// Assume at the beginning of the data structure.
//
*pcbReturnSize = 0;
return ERROR_SUCCESS;
}
DWORD
ReadStaticMember(
IN lfSTMember * pStaticMember,
OUT DWORD * pcbReturnSize,
OUT DWORD * pcbOffset,
IN CHAR * pszBuffer,
IN DWORD cbBuffer
)
{
*pcbOffset += sizeof( lfSTMember ) + pStaticMember->Name[ 0 ];
*pcbReturnSize = INVALID_LENGTH;
return ERROR_SUCCESS;
}
DWORD
ReadNestType(
IN lfNestType * pNestType,
OUT DWORD * pcbReturnSize,
OUT DWORD * pcbOffset,
IN CHAR * pszBuffer,
IN DWORD cbBuffer
)
{
*pcbOffset += sizeof( lfNestType ) + pNestType->Name[ 0 ];
*pcbReturnSize = INVALID_LENGTH;
return ERROR_SUCCESS;
}
DWORD
GetOffset(
BYTE * pBuffer,
DWORD * pcbOffset
)
/*++
Routine Description:
Read the offset for the type record. Then advance the cursor.
Arguments:
pBuffer - Points to current position in field list buffer
pcbOffset - Filled with offset of field member
Return Value:
Amount to advance cursor to next field member
--*/
{
USHORT leaf = *(USHORT*)pBuffer;
if ( leaf < LF_NUMERIC )
{
*pcbOffset = leaf;
return sizeof( leaf );
}
else
{
switch( leaf )
{
case LF_CHAR:
*pcbOffset = *((char*)pBuffer);
return sizeof(leaf) + sizeof(char);
case LF_SHORT:
*pcbOffset = *(short*)pBuffer;
return sizeof(leaf) + sizeof(short);
case LF_USHORT:
*pcbOffset = *(USHORT*)pBuffer;
return sizeof(leaf) + sizeof(USHORT);
case LF_LONG:
*pcbOffset = *(long*)pBuffer;
return sizeof(leaf) + sizeof(long);
case LF_ULONG:
*pcbOffset = *(ULONG*)pBuffer;
return sizeof(leaf) + sizeof(ULONG);
}
}
return 0;
}
DWORD
InitializeStructureTemplate(
IN PSTRUCTURE_TEMPLATE pTemplate
)
/*++
Routine Description:
Initialize structure template
Arguments:
pTemplate - Template buffer to be initialized
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
if ( pTemplate == NULL )
{
return ERROR_INVALID_PARAMETER;
}
pTemplate->pMembers = NULL;
pTemplate->cMembers = 0;
pTemplate->achName[ 0 ] = '\0';
pTemplate->cUseful = 0;
pTemplate->cbTotalSize = 0;
pTemplate->Type = 0xFFFFFFFF;
return ERROR_SUCCESS;
}
DWORD
TerminateStructureTemplate(
IN PSTRUCTURE_TEMPLATE pTemplate
)
/*++
Routine Description:
Terminate structure template
Arguments:
pTemplate - Template buffer to be terminated
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
if ( pTemplate == NULL )
{
return ERROR_INVALID_PARAMETER;
}
if ( pTemplate->pMembers )
{
LocalFree( pTemplate->pMembers );
pTemplate->pMembers = NULL;
pTemplate->cMembers = 0;
}
return ERROR_SUCCESS;
}
DWORD
OutputStructure(
IN PDB * pDebug,
IN CHAR * pszStructureType,
IN CHAR * pszMemberName,
IN DWORD dwFlags,
IN VOID * pvAddress,
IN PFN_READ_MEMORY pfnReadMemory,
IN PFN_PRINTF pfnPrintf
)
/*++
Routine Description:
Top level call to output a structure
Arguments:
pDebug - PDB handle
pszStructureType - Name of structure/class to dump
pszMemberName - (optional) Name of particular member of structure to dump
dwFlags - (not supported)
pvAddress - (optional) Address of start of structure
pfnReadMemory - (optional) Function to read memory. Not needed if
pvAddress==NULL
pfnPrintf - Output function
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
STRUCTURE_TEMPLATE Template;
DWORD dwError;
if ( !pDebug ||
!pszStructureType ||
!pfnReadMemory ||
!pfnPrintf )
{
return ERROR_INVALID_PARAMETER;
}
dwError = InitializeStructureTemplate( &Template );
if ( dwError != ERROR_SUCCESS )
{
return dwError;
}
dwError = BuildMemberListForTypeName( &Template,
pDebug,
pszStructureType );
if ( dwError != ERROR_SUCCESS )
{
return dwError;
}
dwError = OutputTemplate( &Template,
pszMemberName,
dwFlags,
pvAddress,
pfnReadMemory,
pfnPrintf );
//
// CODEWORK: Cache the templates
//
TerminateStructureTemplate( &Template );
return dwError;
}
DWORD
DoPrintf(
CHAR * pszBuffer,
DWORD cbBytes
)
/*++
Routine Description:
Print out buffer
Arguments:
pszBuffer - buffer to print
cbBytes - Bytes to print
Return Value:
Number of bytes printed
--*/
{
dprintf( "%s", pszBuffer );
return strlen( pszBuffer );
}
DWORD
DoReadMemory(
VOID * pvAddress,
DWORD cbBytes,
VOID * pBuffer
)
/*++
Routine Description:
Read debuggee memory into buffer
Arguments:
pvAddress - Address to read
cbBytes - # of bytes to read
pBuffer - Buffer to be filled
Return Value:
If successful ERROR_SUCCESS, else Win32 Error Code
--*/
{
if ( ReadMemory( pvAddress, pBuffer, cbBytes, NULL ) )
{
return ERROR_SUCCESS;
}
else
{
return GetLastError();
}
}
VOID
DumpoffUsage(
VOID
)
/*++
Routine Description:
!dumpoff usage message
Arguments:
None
Return Value:
None
--*/
{
dprintf(
"Usage: !dumpoff <pdb_file>!<type_name>[.<member_name>] [expression]\n"
" !dumpoff -s [<pdb_search_path>]\n"
"\n"
"pdb_file Un-qualified name of PDB file (eg. KERNEL32, NTDLL)\n"
"type_name Name of type (struct/class) to dump, OR \n"
" ==<cbHexSize> to dump struct/classes of size cbHexSize\n"
"member_name (optional) Name of member in type_name to dump\n"
"expression (optional) Address of memory to dump as type_name\n"
" if not present, offset(s) are dumped\n"
"pdb_search_path Set the search path for PDBs\n"
"\n"
"Examples: !dumpoff ntdll!_RTL_CRITICAL_SECTION 14d4d0\n"
" !dumpoff w3svc!HTTP_REQUEST._dwSignature w3svc!g_GlobalObj\n"
" !dumpoff ntdll!_RTL_CRITICAL_SECTION\n"
" !dumpoff w3svc!==840\n"
" !dumpoff -s \\\\mydrive\\pdbs;c:\\local\\pdbs\n"
);
}
#if defined( _X86_ )
CHAR g_achPDBSearchPath[ 1024 ] = "\\\\x86fre\\symbols.pri\\retail\\dll";
#else
CHAR g_achPDBSearchPath[ 1024 ] = "\\\\alphafre\\symbols.pri\\retail\\dll";
#endif
DECLARE_API( dumpoff )
/*++
Routine Description:
This function is called as an NTSD extension to dump a structure
based on debug info in PDB
Arguments:
hCurrentProcess - Supplies a handle to the current process (at the
time the extension was called).
hCurrentThread - Supplies a handle to the current thread (at the
time the extension was called).
CurrentPc - Supplies the current pc at the time the extension is
called.
lpExtensionApis - Supplies the address of the functions callable
by this extension.
lpArgumentString - Supplies the asciiz string that describes the
ansi string to be dumped.
Return Value:
None.
--*/
{
CHAR * pszPdb = NULL;
CHAR * pszType = NULL;
CHAR * pszAddress = NULL;
CHAR * pszMember = NULL;
CHAR * pszCursor = NULL;
CHAR * pszNext = NULL;
CHAR achArg1[ MAX_ARG_SIZE ];
CHAR achArg2[ MAX_ARG_SIZE ];
CHAR achBuffer[ MAX_ARG_SIZE ] = "";
CHAR achFileName[ MAX_PATH + 1 ];
CHAR achFullPath[ MAX_PATH + 1 ];
CHAR achSymPath[ MAX_PATH + 1 ];
BOOL fRet;
EC ec;
PDB * pDebug = NULL;
DWORD dwError;
CHAR * pszError = NULL;
DWORD cArguments;
DWORD cbSize;
BOOL fRetry = TRUE;
INIT_API();
//
// get the debugger symbol path
//
fRet = SymGetSearchPath( hCurrentProcess,
achSymPath,
sizeof( achSymPath ) );
if (!fRet )
{
//
// If we couldn't get the default sym path, just use the SYSTEMROOT
//
dwError = GetEnvironmentVariable( "SYSTEMROOT",
achSymPath,
sizeof( achSymPath ) );
if ( dwError == 0 )
{
_snprintf( achBuffer,
sizeof( achBuffer ),
"Unable to determine symbol path. Error = %d\n",
GetLastError() );
goto Finished;
}
}
//
// parse out the argument style
// <pdbfile>!<type>[.member] [address]
//
cArguments = sscanf( (CHAR*) lpArgumentString,
"%256s%256s",
achArg1,
achArg2 );
if ( cArguments == EOF || cArguments == 0 )
{
DumpoffUsage();
goto Finished;
}
//
// Handle the !dumpoff -s [sympath] case
//
if ( ( achArg1[ 0 ] == '-' || achArg1[ 0 ] == '/' ) &&
( achArg1[ 1 ] == 's' || achArg1[ 1 ] == 'S' ) )
{
if ( cArguments == 2 )
{
strncpy( g_achPDBSearchPath,
achArg2,
sizeof( g_achPDBSearchPath ) );
}
dprintf( "PDB search path set to\n%s%s%s\n",
g_achPDBSearchPath,
*g_achPDBSearchPath ? "\n" : "",
achSymPath );
goto Finished;
}
//
// Parse the regular !dumpoff command
//
pszPdb = achArg1;
pszCursor = strchr( achArg1, '!' );
if ( pszCursor == NULL )
{
DumpoffUsage();
goto Finished;
}
*pszCursor = '\0';
pszType = pszCursor + 1;
pszCursor = strchr( pszType, '.' );
if ( pszCursor != NULL )
{
*pszCursor = '\0';
pszMember = pszCursor + 1;
}
if ( cArguments > 1 )
{
pszAddress = achArg2;
}
//
// done parsing, now get the PDB
//
strncpy( achFileName,
pszPdb,
MAX_ARG_SIZE );
strcat( achFileName,
".pdb");
//
// Look for the PDB file. First in the PDB search path, then sympath
//
pszCursor = g_achPDBSearchPath;
Retry:
while ( pszCursor )
{
pszNext = strchr( pszCursor, ';' );
if ( pszNext != NULL )
{
*pszNext = '\0';
}
fRet = SearchTreeForFile( pszCursor,
achFileName,
achFullPath );
if ( fRet )
{
break;
}
if ( pszNext )
{
pszCursor = pszNext + 1;
}
else
{
pszCursor = NULL;
}
}
if ( !pszCursor && fRetry )
{
fRetry = FALSE;
// now try the debugger sympath
pszCursor = achSymPath;
goto Retry;
}
if ( !pszCursor )
{
_snprintf( achBuffer,
sizeof( achBuffer ),
"Couldn't find PDB file %s\n",
achFileName );
goto Finished;
}
//
// Open the PDB file
//
if ( !PDBOpen( achFullPath,
pdbRead,
0,
&ec,
achBuffer,
&pDebug ) )
{
_snprintf( achBuffer,
sizeof( achBuffer ),
"Error opening PDB file. Error = %d\n",
ec );
goto Finished;
}
if ( pszType[ 0 ] == '=' && pszType[ 1 ] == '=' )
{
//
// Find all types of size after ==
//
cbSize = strtoul( pszType + 2,
NULL,
16 );
dwError = FindMembersOfTypeSize( pDebug,
cbSize,
DoPrintf );
}
else
{
dwError = OutputStructure( pDebug,
pszType,
pszMember,
0,
pszAddress ? (VOID*) GetExpression( pszAddress ) : NULL,
DoReadMemory,
DoPrintf );
}
if ( dwError != ERROR_SUCCESS )
{
switch ( dwError )
{
case ERROR_FILE_NOT_FOUND:
_snprintf( achBuffer,
sizeof( achBuffer ),
"Could not find type '%s' in PDB file '%s'\n",
pszType,
achFullPath );
break;
case ERROR_NOT_SUPPORTED:
_snprintf( achBuffer,
sizeof( achBuffer ),
"PDB file '%s' does not contain necessary type info\n",
achFullPath );
break;
default:
_snprintf( achBuffer,
sizeof( achBuffer ),
"Error dumping structure. Error = %d\n",
dwError );
}
goto Finished;
}
Finished:
if ( achBuffer[ 0 ] )
{
dprintf( "%s", achBuffer );
}
if ( pDebug )
{
PDBClose( pDebug );
}
} // DECLARE_API( dumpoff )