868 lines
25 KiB
C++
868 lines
25 KiB
C++
/*
|
|
Copyright 1999 Microsoft Corporation
|
|
|
|
Simplified PCHealth stack trace output
|
|
|
|
Walter Smith (wsmith)
|
|
Rajesh Soy (nsoy) - modified 4/27/2000
|
|
Rajesh Soy (nsoy) - reorganized code and cleaned it up, added comments 06/06/2000
|
|
*/
|
|
|
|
#ifdef THIS_FILE
|
|
#undef THIS_FILE
|
|
#endif
|
|
|
|
static char __szTraceSourceFile[] = __FILE__;
|
|
#define THIS_FILE __szTraceSourceFile
|
|
|
|
#include "stdafx.h"
|
|
|
|
#define NOTRACE
|
|
|
|
#include "logging.h"
|
|
#include <imagehlp.h>
|
|
#include <dbgtrace.h>
|
|
|
|
using namespace std;
|
|
|
|
// Forward declarations
|
|
|
|
struct MODINFO;
|
|
|
|
//
|
|
// This code is largely stolen from PC Health's fault handler.
|
|
// At some point we should probably refactor that and share the code.
|
|
// However, this code has been redone to handle exceptions properly and
|
|
// eliminate useless stuff.
|
|
//
|
|
struct MODINFO {
|
|
TCHAR szFilename[MAX_PATH];
|
|
TCHAR szFileDesc[MAX_PATH];
|
|
TCHAR szVersion[MAX_PATH];
|
|
TCHAR szCreationDate[MAX_PATH];
|
|
TCHAR szMfr[MAX_PATH];
|
|
DWORD dwFilesize;
|
|
DWORD dwCheckSum;
|
|
DWORD dwPDBSignature;
|
|
DWORD dwPDBAge;
|
|
TCHAR szPDBFile[MAX_PATH];
|
|
UINT_PTR BaseAddress;
|
|
};
|
|
typedef MODINFO* PMODINFO;
|
|
|
|
struct MPC_STACKFRAME {
|
|
const MODINFO* pModule;
|
|
DWORD dwSection;
|
|
UINT_PTR Offset;
|
|
};
|
|
typedef MPC_STACKFRAME* PMPC_STACKFRAME;
|
|
|
|
//
|
|
// PDB debug directory structures
|
|
//
|
|
typedef struct NB10I // NB10 debug info
|
|
{
|
|
DWORD nb10; // NB10
|
|
DWORD off; // offset, always 0
|
|
DWORD sig;
|
|
DWORD age;
|
|
} NB10I;
|
|
|
|
typedef struct cvinfo
|
|
{
|
|
NB10I nb10;
|
|
char rgb[0x200 - sizeof(NB10I)];
|
|
} CVINFO;
|
|
|
|
|
|
typedef map<UINT_PTR,MODINFO> MODINFOMap;
|
|
|
|
|
|
|
|
|
|
//
|
|
// Routines defined here
|
|
//
|
|
void GetFileInfo( LPTSTR szFile, LPTSTR szWhat, LPTSTR szValue);
|
|
void GetFileDateAndSize(MODINFO* pModule);
|
|
BOOL DebugDirectoryIsUseful(LPVOID Pointer, ULONG Size);
|
|
void GetPDBDebugInfo(MODINFO* pModule);
|
|
bool GetLogicalAddress(PVOID addr, DWORD* pdwSection, UINT_PTR* pOffset, UINT_PTR* pbaseAddr);
|
|
void AddDLLSubnode(SimpleXMLNode* pParentElt, LPCWSTR pTag, const MODINFO* pInfo);
|
|
void AddFunctionSubnode(SimpleXMLNode* pParentElt, MPC_STACKFRAME* pFrame);
|
|
void GenerateXMLStackTrace(PSTACKTRACEDATA pstd, SimpleXMLNode* pTopElt);
|
|
|
|
|
|
//
|
|
// ModuleCache class: Class to extrace information from binaries
|
|
//
|
|
|
|
class ModuleCache {
|
|
public:
|
|
ModuleCache() { }
|
|
~ModuleCache() { }
|
|
|
|
const MODINFO* GetModuleInfo(UINT_PTR baseAddr);
|
|
const MODINFO* GetEXEInfo();
|
|
void AddDLLInfoSubnode(SimpleXMLNode* pParentElt);
|
|
|
|
private:
|
|
MODINFOMap mimap;
|
|
};
|
|
|
|
|
|
//
|
|
// ModuleCache::GetModuleInfo: Extracts the following information given a base address
|
|
// ModuleName, CompanyName, FileVersion, FileDescription, FileDateAndSize and PDBDebugInfo
|
|
//
|
|
const MODINFO*
|
|
ModuleCache::GetModuleInfo(
|
|
UINT_PTR baseAddr // [in] - baseAddress of binary
|
|
)
|
|
{
|
|
TraceFunctEnter("ModuleCache::GetModuleInfo");
|
|
|
|
//
|
|
// Locate the base address in modinfo map if present
|
|
//
|
|
MODINFOMap::const_iterator it = mimap.find(baseAddr);
|
|
DWORD dwRetVal = 0;
|
|
|
|
if (it == mimap.end())
|
|
{
|
|
MODINFO mi;
|
|
TCHAR szModName[ MAX_PATH ];
|
|
|
|
mi.BaseAddress = baseAddr;
|
|
mi.dwCheckSum = 0;
|
|
|
|
//
|
|
// Obtain the ModuleFileName
|
|
//
|
|
DebugTrace(0, "Calling GetModuleFileName");
|
|
dwRetVal = GetModuleFileName((HMODULE) baseAddr, szModName, MAX_PATH ) ;
|
|
if(0 == dwRetVal)
|
|
{
|
|
FatalTrace(0, "GetModuleFileName failed. Error: %ld", GetLastError());
|
|
ThrowIfZero( dwRetVal );
|
|
}
|
|
|
|
ZeroMemory( mi.szFilename, MAX_PATH );
|
|
|
|
//
|
|
// Parse the Module name. GetModuleFileName returns filepaths of the form \??\filepath
|
|
// if the user is not logged on.
|
|
//
|
|
if(( szModName[0] == '\\')&&( szModName[1] == '?') && ( szModName[2] == '?') && ( szModName[3] == '\\'))
|
|
{
|
|
DebugTrace(0, "Stripping the funny characters from infront of the module name");
|
|
_tcscpy( mi.szFilename, &szModName[4]);
|
|
}
|
|
else
|
|
{
|
|
DebugTrace(0, "Normal module name");
|
|
_tcscpy( mi.szFilename, szModName);
|
|
}
|
|
|
|
DebugTrace(0, "ModuleName: %ls", mi.szFilename);
|
|
|
|
//
|
|
// Obtain the CompanyName
|
|
//
|
|
DebugTrace(0, "Obtaining CompanyName");
|
|
GetFileInfo(mi.szFilename, TEXT("CompanyName"), mi.szMfr);
|
|
|
|
//
|
|
// Obtain FileVersion
|
|
//
|
|
DebugTrace(0, "Obtaining FileVersion");
|
|
GetFileInfo(mi.szFilename, TEXT("FileVersion"), mi.szVersion);
|
|
|
|
//
|
|
// Obtain FileDescription
|
|
//
|
|
DebugTrace(0, "Obtaining FileDescription");
|
|
GetFileInfo(mi.szFilename, TEXT("FileDescription"), mi.szFileDesc);
|
|
|
|
//
|
|
// Obtain FileDateAndSize
|
|
//
|
|
DebugTrace(0, "Calling GetFileDateAndSize");
|
|
GetFileDateAndSize(&mi);
|
|
|
|
//
|
|
// Obtain PDBDebugInfo
|
|
//
|
|
DebugTrace(0, "Calling GetPDBDebugInfo");
|
|
GetPDBDebugInfo(&mi);
|
|
|
|
//
|
|
// Insert MODINFO into the ModInfo map
|
|
//
|
|
DebugTrace(0, "Calling mimap.insert");
|
|
it = mimap.insert(MODINFOMap::value_type(baseAddr, mi)).first;
|
|
}
|
|
|
|
TraceFunctLeave();
|
|
return &(*it).second;
|
|
}
|
|
|
|
//
|
|
// ModuleCache::GetEXEInfo: Obtain information on binaries that are Exes
|
|
//
|
|
const MODINFO*
|
|
ModuleCache::GetEXEInfo()
|
|
{
|
|
return GetModuleInfo((UINT_PTR) GetModuleHandle(NULL));
|
|
}
|
|
|
|
|
|
//
|
|
// ModuleCache::AddDLLInfoSubnode: adds the information contained in the MODInfo Map
|
|
// into a DLLINFO XML subnode
|
|
void
|
|
ModuleCache::AddDLLInfoSubnode(
|
|
SimpleXMLNode* pParentElt // [in] - parent XML node
|
|
)
|
|
{
|
|
_ASSERT(pParentElt != NULL);
|
|
|
|
//
|
|
// Create a DLLINFO subnode
|
|
//
|
|
SimpleXMLNode* pTopElt = pParentElt->AppendChild(wstring(L"DLLINFO"));
|
|
|
|
//
|
|
// Add DLL subnodes under DLLINFO node for each item contained in the MODInfo Map
|
|
//
|
|
for (MODINFOMap::const_iterator it = mimap.begin(); it != mimap.end(); it++)
|
|
{
|
|
AddDLLSubnode(pTopElt, L"DLL", &(*it).second);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// GetFileInfo: This routine uses Version.Dll API to get requested file info
|
|
// info is returned as a string in szValue
|
|
//
|
|
void
|
|
GetFileInfo(
|
|
LPTSTR szFile, // [in] file to get info for
|
|
LPTSTR szWhat, // [in] may specify "FileVersion",
|
|
// "CompanyName" or "FileDescription"
|
|
LPTSTR szValue // [out] file info obtained
|
|
)
|
|
{
|
|
DWORD dwSize;
|
|
DWORD dwScratch;
|
|
DWORD* pdwLang = NULL;
|
|
DWORD dwLang;
|
|
TCHAR szLang[MAX_PATH] = TEXT("");
|
|
TCHAR szLocalValue[MAX_PATH] = TEXT("");
|
|
LPTSTR szLocal;
|
|
|
|
lstrcpy(szValue, TEXT(""));
|
|
|
|
_ASSERT(szFile != NULL);
|
|
_ASSERT(szWhat != NULL);
|
|
_ASSERT(szValue != NULL);
|
|
|
|
//
|
|
// get fileinfo data size
|
|
//
|
|
dwSize = GetFileVersionInfoSize(szFile, &dwScratch);
|
|
if (dwSize == 0)
|
|
return;
|
|
|
|
auto_ptr<BYTE> pFileInfo(new BYTE[dwSize]);
|
|
|
|
//
|
|
// get fileinfo data
|
|
//
|
|
ThrowIfZero(GetFileVersionInfo(szFile, 0, dwSize, (PVOID) pFileInfo.get()));
|
|
|
|
//
|
|
// set default language to english
|
|
//
|
|
dwLang = 0x040904E4;
|
|
pdwLang = &dwLang;
|
|
|
|
//
|
|
// read language identifier and code page of file
|
|
//
|
|
if (VerQueryValue(pFileInfo.get(),
|
|
TEXT("\\VarFileInfo\\Translation"),
|
|
(PVOID *) &pdwLang,
|
|
(UINT *) &dwScratch))
|
|
{
|
|
//
|
|
// prepare query string - specify what we need ("FileVersion",
|
|
// "CompanyName" or "FileDescription")
|
|
//
|
|
_stprintf(szLang,
|
|
TEXT("\\StringFileInfo\\%04X%04X\\%s"),
|
|
LOWORD(*pdwLang),
|
|
HIWORD(*pdwLang),
|
|
szWhat);
|
|
|
|
szLocal = szLocalValue;
|
|
|
|
//
|
|
// query for the value using codepage from file
|
|
//
|
|
if (VerQueryValue(pFileInfo.get(),
|
|
szLang,
|
|
(PVOID *) &szLocal,
|
|
(UINT *) &dwScratch))
|
|
{
|
|
lstrcpy(szValue,szLocal);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if that fails, try Unicode
|
|
//
|
|
_stprintf(szLang,
|
|
TEXT("\\StringFileInfo\\%04X04B0\\%s"),
|
|
GetUserDefaultLangID(),
|
|
szWhat);
|
|
if (!VerQueryValue(pFileInfo.get(),
|
|
szLang,
|
|
(PVOID *) &szLocal,
|
|
(UINT *) &dwScratch))
|
|
{
|
|
//
|
|
// if that fails too, try Multilingual
|
|
//
|
|
_stprintf(szLang,
|
|
TEXT("\\StringFileInfo\\%04X04E4\\%s"),
|
|
GetUserDefaultLangID(),
|
|
szWhat);
|
|
if (!VerQueryValue(pFileInfo.get(),
|
|
szLang,
|
|
(PVOID *) &szLocal,
|
|
(UINT *) &dwScratch))
|
|
{
|
|
//
|
|
// and if that fails as well, try nullPage
|
|
//
|
|
_stprintf(szLang,
|
|
TEXT("\\StringFileInfo\\%04X0000\\%s"),
|
|
GetUserDefaultLangID(),
|
|
szWhat);
|
|
if (!VerQueryValue(pFileInfo.get(),
|
|
szLang,
|
|
(PVOID *) &szLocal,
|
|
(UINT *) &dwScratch))
|
|
{
|
|
// giving up
|
|
szValue[0] = 0;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// successful; copy to return string
|
|
//
|
|
lstrcpy(szValue,szLocal);
|
|
}
|
|
|
|
|
|
//
|
|
// GetFileDateAndSize: get file creation date and size
|
|
//
|
|
void
|
|
GetFileDateAndSize(
|
|
MODINFO* pModule // [in] [out] pointer to module node
|
|
// fills file date and size fields
|
|
)
|
|
{
|
|
TraceFunctEnter("GetFileDateAndSize");
|
|
_ASSERT(pModule != NULL);
|
|
|
|
SYSTEMTIME STCreationTime;
|
|
WIN32_FIND_DATA FindData;
|
|
|
|
lstrcpy(pModule->szCreationDate,TEXT(""));
|
|
pModule->dwFilesize = 0;
|
|
|
|
HANDLE hFind = FindFirstFile(pModule->szFilename,&FindData);
|
|
if(INVALID_HANDLE_VALUE == hFind)
|
|
{
|
|
FatalTrace(0, "FindFirstFile on %ls failed. Error: %ld", pModule->szFilename, GetLastError());
|
|
ThrowIfTrue(hFind == INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
// NO THROWS -- hFind will leak
|
|
|
|
_ASSERT(FindData.ftCreationTime.dwLowDateTime ||
|
|
FindData.ftCreationTime.dwHighDateTime);
|
|
|
|
FileTimeToSystemTime(&(FindData.ftCreationTime),&STCreationTime);
|
|
|
|
_stprintf(pModule->szCreationDate, _T("%d-%02d-%02dT%02d:%02d:%02d.%03d"),
|
|
STCreationTime.wYear, STCreationTime.wMonth, STCreationTime.wDay,
|
|
STCreationTime.wHour, STCreationTime.wMinute, STCreationTime.wSecond,
|
|
STCreationTime.wMilliseconds);
|
|
|
|
pModule->dwFilesize = (FindData.nFileSizeHigh * MAXDWORD) + FindData.nFileSizeLow;
|
|
|
|
FindClose(hFind);
|
|
TraceFunctLeave();
|
|
}
|
|
|
|
|
|
//
|
|
// DebugDirectoryIsUseful: Check if this is an userful debug directory
|
|
//
|
|
BOOL DebugDirectoryIsUseful(LPVOID Pointer, ULONG Size)
|
|
{
|
|
return (Pointer != NULL) &&
|
|
(Size >= sizeof(IMAGE_DEBUG_DIRECTORY)) &&
|
|
((Size % sizeof(IMAGE_DEBUG_DIRECTORY)) == 0);
|
|
}
|
|
|
|
|
|
//
|
|
// GetPDBDebugInfo: Looks up the Debug Directory to get PDB Debug Info
|
|
//
|
|
void
|
|
GetPDBDebugInfo(
|
|
MODINFO* pModule // [in] [out] pointer to module node
|
|
// fills PDB Signature, Age and Filename
|
|
)
|
|
{
|
|
TraceFunctEnter("GetPDBDebugInfo");
|
|
_ASSERT(pModule != NULL);
|
|
|
|
HANDLE hMapping = NULL;
|
|
PIMAGE_NT_HEADERS pntHeaders = NULL;
|
|
void* pvImageBase = NULL;
|
|
|
|
//
|
|
// Open the Binary for reading
|
|
//
|
|
HANDLE hFile = CreateFile(pModule->szFilename,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
if (INVALID_HANDLE_VALUE != hFile )
|
|
{
|
|
//
|
|
// Create a FileMapping
|
|
//
|
|
hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
if (NULL != hMapping)
|
|
{
|
|
//
|
|
// Map view to Memory
|
|
//
|
|
pvImageBase = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
|
|
if (NULL != pvImageBase)
|
|
{
|
|
__try
|
|
{
|
|
//
|
|
// Obtain the NtImage Headers
|
|
//
|
|
pntHeaders = ImageNtHeader(pvImageBase);
|
|
|
|
if(NULL == pntHeaders)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
if (pntHeaders->OptionalHeader.MajorLinkerVersion >= 3 ||
|
|
pntHeaders->OptionalHeader.MinorLinkerVersion >= 5)
|
|
{
|
|
//
|
|
// Find the debug directory entries
|
|
//
|
|
ULONG cbDebugDirectories;
|
|
PIMAGE_DEBUG_DIRECTORY pDebugDirectories = (PIMAGE_DEBUG_DIRECTORY)
|
|
ImageDirectoryEntryToData(pvImageBase, FALSE, IMAGE_DIRECTORY_ENTRY_DEBUG, &cbDebugDirectories);
|
|
|
|
if (DebugDirectoryIsUseful(pDebugDirectories, cbDebugDirectories))
|
|
{
|
|
//
|
|
// Find the codeview information
|
|
//
|
|
ULONG cDebugDirectories = cbDebugDirectories / sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
ULONG iDirectory;
|
|
for (iDirectory=0; iDirectory<cDebugDirectories; iDirectory++)
|
|
{
|
|
PIMAGE_DEBUG_DIRECTORY pDir = &pDebugDirectories[iDirectory];
|
|
if (pDir->Type == IMAGE_DEBUG_TYPE_CODEVIEW)
|
|
{
|
|
LPVOID pv = (PCHAR)pvImageBase + pDir->PointerToRawData;
|
|
ULONG cb = pDir->SizeOfData;
|
|
//
|
|
// Is it the NAME of a .PDB file rather than CV info itself?
|
|
//
|
|
NB10I* pnb10 = (NB10I*)pv;
|
|
|
|
pModule->dwPDBSignature = pnb10->sig;
|
|
pModule->dwPDBAge = pnb10->age;
|
|
|
|
if (pnb10->nb10 == '01BN')
|
|
{
|
|
//
|
|
// Got a PDB name, which immediately follows the NB10I; we take everything.
|
|
//
|
|
mbstowcs(pModule->szPDBFile, (char *)(pnb10+1), MAX_PATH);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Got the PDB Signature and Age
|
|
// This information is used by the backend to
|
|
// locate the PDB symbol file for this binary
|
|
// to resolve symbols
|
|
//
|
|
pModule->dwPDBSignature = pnb10->sig;
|
|
pModule->dwPDBAge = pnb10->age;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
FatalTrace(0, "Unable to extract PDB information from binary");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
|
|
if(NULL != pvImageBase)
|
|
{
|
|
UnmapViewOfFile(pvImageBase);
|
|
pvImageBase = NULL;
|
|
}
|
|
|
|
if(NULL != hMapping)
|
|
{
|
|
CloseHandle(hMapping);
|
|
hMapping = NULL;
|
|
}
|
|
|
|
if(NULL != hFile)
|
|
{
|
|
CloseHandle(hFile);
|
|
hFile = NULL;
|
|
}
|
|
|
|
TraceFunctLeave();
|
|
}
|
|
|
|
|
|
//
|
|
// GetLogicalAddress: converts virtual address to section and offset by searching module's section table
|
|
//
|
|
bool
|
|
GetLogicalAddress(
|
|
PVOID addr, // [in] address to be resolved
|
|
DWORD* pdwSection, // [out] section number
|
|
UINT_PTR* pOffset, // [out] offset in section
|
|
UINT_PTR* pbaseAddr // [out] base address of module
|
|
)
|
|
{
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
UINT_PTR baseAddr; // local base address of module
|
|
IMAGE_DOS_HEADER* pDosHdr; // PE File Headers
|
|
IMAGE_NT_HEADERS* pNTHdr;
|
|
IMAGE_SECTION_HEADER* pSectionHdr;
|
|
UINT_PTR rva; // relative virtual address of "addr"
|
|
UINT_PTR sectionStart;
|
|
UINT_PTR sectionEnd;
|
|
UINT i;
|
|
DWORD dwDump;
|
|
DWORD dwRW;
|
|
DWORD dwRetVQ;
|
|
bool fFound = false;
|
|
static DWORD s_dwSystemPageSize = 0;
|
|
|
|
TraceFunctEnter("GetLogicalAddress");
|
|
|
|
if (s_dwSystemPageSize == 0) {
|
|
SYSTEM_INFO si;
|
|
GetSystemInfo(&si);
|
|
s_dwSystemPageSize = si.dwPageSize;
|
|
}
|
|
|
|
*pdwSection = 0;
|
|
*pOffset = 0;
|
|
*pbaseAddr = 0;
|
|
|
|
//
|
|
// addr should not be NULL
|
|
//
|
|
if(NULL == addr)
|
|
{
|
|
FatalTrace(0, "addr is NULL");
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// get page info for page containing "addr"
|
|
//
|
|
DebugTrace(0, "Calling VirtualQuery");
|
|
dwRetVQ = VirtualQuery(addr, &mbi, sizeof(mbi));
|
|
if(0 == dwRetVQ)
|
|
{
|
|
FatalTrace(0, "dwRetVQ is 0. Error: %ld", GetLastError());
|
|
ThrowIfZero(dwRetVQ);
|
|
}
|
|
|
|
//
|
|
// Just in case this goes wild on us...
|
|
//
|
|
__try {
|
|
baseAddr = (UINT_PTR) mbi.AllocationBase;
|
|
|
|
//
|
|
// get relative virtual address corresponding to addr
|
|
//
|
|
rva = (UINT_PTR) addr - baseAddr;
|
|
|
|
//
|
|
// read Dos header of PE file
|
|
//
|
|
pDosHdr = (IMAGE_DOS_HEADER*) baseAddr;
|
|
|
|
//
|
|
// read NT header of PE file
|
|
//
|
|
pNTHdr = (IMAGE_NT_HEADERS*) (baseAddr + pDosHdr->e_lfanew);
|
|
|
|
//
|
|
// get section header address
|
|
//
|
|
pSectionHdr = (IMAGE_SECTION_HEADER*) ((UINT_PTR) IMAGE_FIRST_SECTION(pNTHdr) -
|
|
(UINT_PTR) pNTHdr +
|
|
baseAddr + pDosHdr->e_lfanew);
|
|
|
|
//
|
|
// step through section table to get to the section containing rva
|
|
//
|
|
DebugTrace(0, "stepping through section table...");
|
|
for (i=0; i< pNTHdr->FileHeader.NumberOfSections; i++)
|
|
{
|
|
//
|
|
// get section boundaries
|
|
//
|
|
sectionStart = pSectionHdr->VirtualAddress;
|
|
sectionEnd = sectionStart +
|
|
max(pSectionHdr->SizeOfRawData, pSectionHdr->Misc.VirtualSize);
|
|
|
|
//
|
|
// check if section envelopes rva
|
|
//
|
|
if ((rva >= sectionStart) && (rva <= sectionEnd))
|
|
{
|
|
*pdwSection = i+1;
|
|
*pOffset = rva-sectionStart;
|
|
*pbaseAddr = baseAddr;
|
|
fFound = true;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// move pointer to next section
|
|
//
|
|
pSectionHdr = pSectionHdr + sizeof(IMAGE_SECTION_HEADER);
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
_ASSERT(0);
|
|
*pbaseAddr = NULL;
|
|
}
|
|
|
|
if (!fFound)
|
|
_ASSERT(0);
|
|
|
|
done:
|
|
TraceFunctLeave();
|
|
return fFound;
|
|
}
|
|
|
|
|
|
//
|
|
// AddDLLSubnode: Inserts a DLL subnode to the given parent XML node
|
|
//
|
|
void
|
|
AddDLLSubnode(
|
|
SimpleXMLNode* pParentElt, // [in] - parent XML node
|
|
LPCWSTR pTag, // [in] - name of subnode tag
|
|
const MODINFO* pInfo // [in] - Module Information
|
|
)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
TraceFunctEnter("AddDLLSubnode");
|
|
|
|
_ASSERT(pParentElt != NULL);
|
|
_ASSERT(pTag != NULL);
|
|
_ASSERT(pInfo != NULL);
|
|
|
|
if(NULL == pInfo)
|
|
{
|
|
FatalTrace(0, "pInfo is NULL");
|
|
throw E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Create the XML subnode
|
|
//
|
|
SimpleXMLNode* pNode = pParentElt->AppendChild(wstring(pTag));
|
|
|
|
//
|
|
// Set the various attributes of the DLL subnode
|
|
//
|
|
pNode->SetAttribute(wstring(L"FILENAME"), wstring(T2CW(pInfo->szFilename)));
|
|
pNode->SetAttribute(wstring(L"VERSION"), wstring(T2CW(pInfo->szVersion)));
|
|
pNode->SetAttribute(wstring(L"CREATIONDATE"), wstring(T2CW(pInfo->szCreationDate)));
|
|
pNode->SetAttribute(wstring(L"CHECKSUM"), Hexify(pInfo->dwCheckSum));
|
|
pNode->SetAttribute(wstring(L"PDBSIGNATURE"), Hexify(pInfo->dwPDBSignature));
|
|
pNode->SetAttribute(wstring(L"PDBAGE"), Hexify(pInfo->dwPDBAge));
|
|
pNode->SetAttribute(wstring(L"PDBFILE"), wstring(T2CW(pInfo->szPDBFile)));
|
|
pNode->SetAttribute(wstring(L"FILESIZE"), Hexify(pInfo->dwFilesize));
|
|
pNode->SetAttribute(wstring(L"BASEADDRESS"), Hexify(pInfo->BaseAddress));
|
|
pNode->SetAttribute(wstring(L"MANUFACTURER"), wstring(T2CW(pInfo->szMfr)));
|
|
pNode->SetAttribute(wstring(L"DESCRIPTION"), wstring(T2CW(pInfo->szFileDesc)));
|
|
|
|
TraceFunctLeave();
|
|
}
|
|
|
|
|
|
//
|
|
// AddFunctionSubnode: Adds a FUNCTION subnode to the given XML node
|
|
//
|
|
void
|
|
AddFunctionSubnode(
|
|
SimpleXMLNode* pParentElt, // [in] - parent XML node
|
|
MPC_STACKFRAME* pFrame // [in] - Stack Frame
|
|
)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
_ASSERT(pParentElt != NULL);
|
|
_ASSERT(pFrame != NULL);
|
|
|
|
//
|
|
// Create the FUNCTION subnode
|
|
//
|
|
SimpleXMLNode* pNode = pParentElt->AppendChild(wstring(L"FUNCTION"));
|
|
|
|
//
|
|
// Add the Attributes of the FUNCTION subnode
|
|
//
|
|
pNode->SetAttribute(wstring(L"FILENAME"), wstring(T2CW(pFrame->pModule->szFilename)));
|
|
pNode->SetAttribute(wstring(L"SECTION"), Decimalify(pFrame->dwSection));
|
|
pNode->SetAttribute(wstring(L"OFFSET"), Decimalify(pFrame->Offset));
|
|
}
|
|
|
|
|
|
//
|
|
// GenerateXMLStackTrace: Generate a stack trace in PCHealth-standard XML format
|
|
//
|
|
void
|
|
GenerateXMLStackTrace(
|
|
PSTACKTRACEDATA pstd, // [in] - pointer to call stack
|
|
SimpleXMLNode* pTopElt // [in] - pointer to STACKTRACE XML node
|
|
)
|
|
{
|
|
TraceFunctEnter("GenerateXMLStackTrace");
|
|
_ASSERT(pTopElt != NULL);
|
|
|
|
ModuleCache modCache;
|
|
|
|
//
|
|
// Set the Tag Name
|
|
//
|
|
pTopElt->tag = wstring(L"STACKTRACE");
|
|
|
|
//
|
|
// Obtain the Info on the binary being commented
|
|
//
|
|
DebugTrace(0, "Calling modCache.GetEXEInfo");
|
|
const MODINFO* pExeInfo = modCache.GetEXEInfo();
|
|
|
|
//
|
|
// Add the info collected as a EXEINFO subnode under STACKTRACE
|
|
//
|
|
DebugTrace(0, "Calling AddDLLSubnode");
|
|
AddDLLSubnode(pTopElt, L"EXEINFO", pExeInfo);
|
|
|
|
//
|
|
// Create a CALLSTACK subnode under STACKTRACE, but only if pstd is not NULL
|
|
//
|
|
|
|
if (pstd != NULL) {
|
|
DebugTrace(0, "Adding CALLSTACK element");
|
|
SimpleXMLNode* pCallStackElt = pTopElt->AppendChild(wstring(L"CALLSTACK"));
|
|
|
|
ULONG_PTR stackBase;
|
|
stackBase = (ULONG_PTR)pstd;
|
|
DWORD dwValue;
|
|
ULONG ulFrames;
|
|
ulFrames = pstd->nCallers;
|
|
|
|
bool fProceed = 1;
|
|
PVOID caller;
|
|
int iFrame = 0;
|
|
|
|
//
|
|
// Step through the call stack
|
|
//
|
|
caller = (int *)pstd->callers;
|
|
while(caller != 0)
|
|
{
|
|
MPC_STACKFRAME frame;
|
|
UINT_PTR modBase;
|
|
|
|
//
|
|
// Obtain the Logical Address for each caller
|
|
//
|
|
DebugTrace(0, "GetLogicalAddress");
|
|
if (GetLogicalAddress(caller, &frame.dwSection, &frame.Offset, &modBase))
|
|
{
|
|
//
|
|
// Get Module Information for this caller
|
|
//
|
|
frame.pModule = modCache.GetModuleInfo(modBase);
|
|
|
|
//
|
|
// Add the ModuleInformation obtained as a FUNCTION subnode under the CALLSTACK node
|
|
//
|
|
DebugTrace(0, "Calling AddFunctionSubnode");
|
|
AddFunctionSubnode(pCallStackElt, &frame);
|
|
}
|
|
|
|
DebugTrace(0, "iFrame: %ld", iFrame);
|
|
caller = pstd->callers[iFrame];
|
|
iFrame++;
|
|
}
|
|
|
|
//
|
|
// Now, add the DLLINFO subnode. This must happen after all the GetModuleInfo calls so the cache
|
|
// is populated with all the necessary info.
|
|
//
|
|
DebugTrace(0, "Calling AddDLLInfoSubnode");
|
|
modCache.AddDLLInfoSubnode(pTopElt);
|
|
}
|
|
|
|
TraceFunctLeave();
|
|
}
|