/*
    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();
}