363 lines
12 KiB
C++
363 lines
12 KiB
C++
//--------------------------------------------------------------------------------------
|
|
// CallStackDisplay.cpp
|
|
//
|
|
// This sample illustrates how to do symbol lookup on your PC. Run the
|
|
// CallStackRecording sample first and copy the callstacks.dat file from the devkit
|
|
// to the CallStackDisplay directory. You can optionally copy the pdb file from the
|
|
// CallStackRecording sample to the CallStackDisplay directory.
|
|
//
|
|
// DbgHelp will be used to locate system symbols in the symbol server, and DIA2 will
|
|
// be used to do symbol loading and lookups.
|
|
//
|
|
// Microsoft Game Technology Group.
|
|
// Copyright (C) Microsoft Corporation. All rights reserved.
|
|
//--------------------------------------------------------------------------------------
|
|
#include "stdafx.h"
|
|
#include "SymbolHelper.h"
|
|
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <map>
|
|
#include <time.h>
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name of the file to load call stack information from.
|
|
//--------------------------------------------------------------------------------------
|
|
const CHAR* g_filename = "E:\\dump.dat";
|
|
|
|
|
|
typedef unsigned __int64 u64;
|
|
typedef unsigned int u32;
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 6384) // warning C6384: Dividing sizeof a pointer by another value
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: ByteReverse
|
|
// Desc: Template function to byte-reverse in place any arbitrary block of data.
|
|
//--------------------------------------------------------------------------------------
|
|
template <typename T> void ByteReverse( T& data )
|
|
{
|
|
BYTE* pData = ( BYTE* )&data;
|
|
for( int i = 0; i < sizeof( data ) / 2; ++i )
|
|
{
|
|
std::swap( pData[i], pData[sizeof( data ) - 1 - i] );
|
|
}
|
|
}
|
|
|
|
#pragma warning(pop)
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: ReadFileOrAbort
|
|
// Desc: Read data from a file and abort the program if the requested data is not read.
|
|
//--------------------------------------------------------------------------------------
|
|
VOID ReadFileOrAbort(
|
|
__in HANDLE hFile,
|
|
__out_bcount_part(nNumberOfBytesToRead, *lpNumberOfBytesRead) LPVOID lpBuffer,
|
|
__in DWORD nNumberOfBytesToRead,
|
|
__out_opt LPDWORD lpNumberOfBytesRead,
|
|
__inout_opt LPOVERLAPPED lpOverlapped
|
|
)
|
|
{
|
|
// Make sure we have a bytes-read pointer.
|
|
DWORD bytesRead;
|
|
if( !lpNumberOfBytesRead )
|
|
lpNumberOfBytesRead = &bytesRead;
|
|
|
|
BOOL result = ReadFile( hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped );
|
|
|
|
if( !result || nNumberOfBytesToRead != *lpNumberOfBytesRead )
|
|
{
|
|
printf( "Error reading from file--aborting\n" );
|
|
abort();
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: ProcessBinaryFile
|
|
// Desc: Process a file containing binary data describing the loaded modules and the
|
|
// addresses to look up.
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
const DWORD maxBackTrace = 20;
|
|
|
|
class TraceInfo
|
|
{
|
|
public:
|
|
DWORD m_attributes;
|
|
DWORD m_stack[maxBackTrace];
|
|
};
|
|
|
|
|
|
typedef struct _XALLOC_ATTRIBUTES {
|
|
DWORD dwObjectType : 13;
|
|
DWORD dwHeapTracksAttributes : 1;
|
|
DWORD dwMustSucceed : 1;
|
|
DWORD dwFixedSize : 1;
|
|
DWORD dwAllocatorId : 8;
|
|
DWORD dwAlignment : 4;
|
|
DWORD dwMemoryProtect : 2;
|
|
DWORD dwZeroInitialize : 1;
|
|
DWORD dwMemoryType : 1;
|
|
} XALLOC_ATTRIBUTES, *PXALLOC_ATTRIBUTES;
|
|
|
|
|
|
struct symbolInfo
|
|
{
|
|
int m_iLine;
|
|
std::string m_szSymbolTable;
|
|
std::string m_szFileName;
|
|
};
|
|
|
|
typedef std::map<DWORD,symbolInfo> SymbolTable;
|
|
|
|
#define MAKE_MEMCHUNCK(__size,__crc) (u64)((((u64)(__size) << 32) | __crc))
|
|
#define GET_SIZECHUNCK(__chunck) (u32)((__chunck >> 32))
|
|
#define GET_CRCCHUNCK(__chunck) (u32)((__chunck & 0xFFFFFFFF))
|
|
|
|
|
|
VOID ProcessBinaryFile( HANDLE hFile, SymbolHelper& symbols )
|
|
{
|
|
DWORD readCount = 0;
|
|
|
|
// Loop through all of the module information, loading symbols for
|
|
// each module.
|
|
for(; ; )
|
|
{
|
|
// Each module is prefixed by a magic number, and the list of modules is
|
|
// terminated with a DWORD zero.
|
|
const DWORD modulePrefixID = 0xABABCDCD;
|
|
// Read in the ID. If it is zero then we are at the end of the list of
|
|
// modules.
|
|
DWORD id = 0;
|
|
ReadFileOrAbort( hFile, &id, sizeof( id ), &readCount, 0 );
|
|
ByteReverse( id );
|
|
if( id != modulePrefixID )
|
|
{
|
|
if( id != 0 )
|
|
{
|
|
printf( "Unexpected data in '%s'--aborting\n", g_filename );
|
|
return;
|
|
}
|
|
|
|
// We've hit a modulePrefixID of zero, which tells us we have reached
|
|
// the end of the list of modules, so we exit the loop.
|
|
break;
|
|
}
|
|
|
|
// Read in the data that describes this module.
|
|
|
|
// Read the module address and byte reverse it.
|
|
VOID* baseAddress = 0;
|
|
ReadFileOrAbort( hFile, &baseAddress, sizeof( baseAddress ), &readCount, 0 );
|
|
ByteReverse( baseAddress );
|
|
|
|
// Read the module size and byte reverse it.
|
|
size_t size = 0;
|
|
ReadFileOrAbort( hFile, &size, sizeof( size ), &readCount, 0 );
|
|
ByteReverse( size );
|
|
|
|
// Read the module timestamp and byte reverse it.
|
|
DWORD timeStamp = 0;
|
|
ReadFileOrAbort( hFile, &timeStamp, sizeof( timeStamp ), &readCount, 0 );
|
|
ByteReverse( timeStamp );
|
|
|
|
// Read the module name.
|
|
CHAR name[MAX_PATH] = {0};
|
|
ReadFileOrAbort( hFile, &name, sizeof( name ), &readCount, 0 );
|
|
|
|
// Read the module PDB signature.
|
|
DM_PDB_SIGNATURE signature = {0};
|
|
ReadFileOrAbort( hFile, &signature, sizeof( signature ), &readCount, 0 );
|
|
|
|
// Load the symbols for this module.
|
|
bool result = symbols.LoadSymbolsForModule( baseAddress,
|
|
size, timeStamp, &signature );
|
|
printf( "Symbols loaded %s for %s\n",
|
|
result ? "successfully" : "unsuccessfully", name );
|
|
}
|
|
|
|
// Loop through all of the callstacks.
|
|
DWORD numStack = 0;
|
|
ReadFile( hFile, &numStack, sizeof( numStack ), &readCount, 0 );
|
|
ByteReverse( numStack );
|
|
|
|
FILE* fileDump;
|
|
fopen_s(&fileDump,"c:\\dump.bin","wb");
|
|
|
|
fwrite(&numStack,sizeof(numStack),1,fileDump);
|
|
|
|
|
|
std::string _symbolName;
|
|
std::string _fileName;
|
|
int _lineNumber = 0;
|
|
|
|
SymbolTable mSymbolTable;
|
|
|
|
for(DWORD indexStack = 0;indexStack<numStack;++indexStack)
|
|
{
|
|
// Load the count of how many addresses follow.
|
|
DWORD numEntries = 0;
|
|
DWORD attributes = 0;
|
|
DWORD crc = 0;
|
|
|
|
BOOL result = ReadFile( hFile, &crc, sizeof( crc ), &readCount, 0 );
|
|
if( !result)
|
|
break;
|
|
ByteReverse( crc );
|
|
|
|
result = ReadFile( hFile, &numEntries, sizeof( numEntries ), &readCount, 0 );
|
|
// When we reach the end of the file the read of numEntries will fail.
|
|
// Elsewhere I omit checking for read failures, to simplify the code.
|
|
if( !result || readCount != sizeof( numEntries ) )
|
|
break;
|
|
ByteReverse( numEntries );
|
|
|
|
// Check for bogus values.
|
|
if( numEntries > 100 )
|
|
break;
|
|
|
|
result = ReadFile( hFile, &attributes, sizeof( attributes ), &readCount, 0 );
|
|
if( !result)
|
|
break;
|
|
|
|
ByteReverse( attributes );
|
|
const XALLOC_ATTRIBUTES& AllocAttributes = *( XALLOC_ATTRIBUTES* )&attributes;
|
|
|
|
//Loop through doing symbol lookups on all of the addresses.
|
|
//printf( "\nCall stack with %u entries:\n", numEntries );
|
|
|
|
fwrite(&crc,sizeof(crc),1,fileDump);
|
|
fwrite(&numEntries,sizeof(numEntries),1,fileDump);
|
|
fwrite(&attributes,sizeof(attributes),1,fileDump);
|
|
|
|
|
|
for( DWORD i = 0; i < numEntries; ++i )
|
|
{
|
|
// Read the address and byte reverse it.
|
|
DWORD address = 0;
|
|
ReadFileOrAbort( hFile, &address, sizeof( address ), &readCount, 0 );
|
|
ByteReverse( address );
|
|
|
|
|
|
fwrite(&address,sizeof(address),1,fileDump);
|
|
|
|
|
|
SymbolTable::const_iterator iterAddress = mSymbolTable.find(address);
|
|
// Look up the symbol information for this address and print it.
|
|
if (iterAddress == mSymbolTable.end() && symbols.PrintSymbolSummary( address,_symbolName,_fileName,_lineNumber))
|
|
{
|
|
//record them in the symbolTable
|
|
symbolInfo info;
|
|
info.m_iLine = _lineNumber;
|
|
info.m_szFileName = _fileName;
|
|
info.m_szSymbolTable = _symbolName;
|
|
|
|
mSymbolTable[address] = info;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//Save the symbol table;
|
|
DWORD symbolTableSize = mSymbolTable.size();
|
|
|
|
fwrite(&symbolTableSize,sizeof(symbolTableSize),1,fileDump);
|
|
|
|
for (SymbolTable::iterator iterAddress = mSymbolTable.begin();iterAddress != mSymbolTable.end();++iterAddress)
|
|
{
|
|
DWORD address = (*iterAddress).first;
|
|
const symbolInfo& info = (*iterAddress).second;
|
|
|
|
fwrite(&address,sizeof(address),1,fileDump);
|
|
fwrite(&info.m_iLine,sizeof(info.m_iLine),1,fileDump);
|
|
|
|
DWORD len = info.m_szFileName.length();
|
|
fwrite(&len,sizeof(len),1,fileDump);
|
|
fwrite(info.m_szFileName.c_str(),len,1,fileDump);
|
|
|
|
|
|
len = info.m_szSymbolTable.length();
|
|
fwrite(&len,sizeof(len),1,fileDump);
|
|
fwrite(info.m_szSymbolTable.c_str(),len,1,fileDump);
|
|
|
|
}
|
|
|
|
|
|
DWORD numAllocation = 0;
|
|
ReadFile( hFile, &numAllocation, sizeof( numAllocation ), &readCount, 0 );
|
|
ByteReverse( numAllocation );
|
|
|
|
fwrite(&numAllocation,sizeof(numAllocation),1,fileDump);
|
|
|
|
for (DWORD allocationIndex = 0;allocationIndex<numAllocation;++allocationIndex)
|
|
{
|
|
u32 memaddress;
|
|
ReadFile( hFile, &memaddress, sizeof( memaddress ), &readCount, 0 );
|
|
ByteReverse( memaddress );
|
|
fwrite(&memaddress,sizeof(memaddress),1,fileDump);
|
|
|
|
u64 memchk;
|
|
ReadFile( hFile, &memchk, sizeof( memchk ), &readCount, 0 );
|
|
ByteReverse( memchk );
|
|
fwrite(&memchk,sizeof(memchk),1,fileDump);
|
|
/*
|
|
DWORD ss = GET_SIZECHUNCK(memchk);
|
|
DWORD crc = GET_CRCCHUNCK(memchk);
|
|
*/
|
|
}
|
|
|
|
|
|
fclose(fileDump);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Main
|
|
// Desc: Entry point for program
|
|
//--------------------------------------------------------------------------------------
|
|
int main( int/*argc*/, CHAR* /*argv[]*/)
|
|
{
|
|
// Declare a symbol helper object. This should be a local variable rather than
|
|
// a global variable to avoid problems caused by heavyweight construction going
|
|
// on during process load.
|
|
SymbolHelper symbols;
|
|
|
|
// Open the data file containing module information and addresses.
|
|
HANDLE hFile = CreateFile( g_filename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, 0, NULL );
|
|
if( hFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
printf( "Can't open '%s'\n", g_filename );
|
|
return 0;
|
|
}
|
|
|
|
// Check and see if the file is a text or binary data file. The "BINR" comment
|
|
// is placed at the beginning of all binary format files created by CallStackRecording.
|
|
/*const CHAR* binaryID = "BINR";
|
|
CHAR fileID[4] = {0};
|
|
ReadFileOrAbort( hFile, &fileID, sizeof( fileID ), 0, 0 );*/
|
|
|
|
ProcessBinaryFile(hFile, symbols );
|
|
/*
|
|
// Process the file as binary or as text.
|
|
if( memcmp( binaryID, &fileID, sizeof( fileID ) ) == 0 )
|
|
{
|
|
printf( "Processing binary file.\n" );
|
|
ProcessBinaryFile( hFile, symbols );
|
|
}
|
|
else
|
|
{
|
|
printf( "Processing text file.\n" );
|
|
ProcessTextFile( g_filename, symbols );
|
|
}*/
|
|
|
|
// Clean up and exit.
|
|
CloseHandle( hFile );
|
|
|
|
return 0;
|
|
}
|