//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

#include <bldver.h>
#include "osaxs_p.h"

// Nkshx.h defines these, so get rid of them before including DwCeDump.h
#ifdef ProcessorLevel
#undef ProcessorLevel
#endif
#ifdef ProcessorRevision
#undef ProcessorRevision
#endif

#include "DwCeDump.h"
#define  WATSON_BINARY_DUMP TRUE
#include "DwPublic.h"
#include "DwPrivate.h"
#include "DwDmpGen.h"

enum
{
    STACK_FRAMES_BUFFERED = 20,
    MAX_STREAMS           = (ceStreamLastStream - ceStreamNull),
    MAX_THREAD_ENTRIES    = FLEX_PTMI_BUFFER_SIZE / sizeof (WORD),
};

enum
{
    VIRTUAL_MEMORY  = TRUE,
    PHYSICAL_MEMORY = FALSE
};

enum
{
#ifdef ARM
    // The ARM architecture is special in that each page directory entry actually points to 1MB instead of 4MB for the other
    // architectures. This means that each page table takes up 4 page directory entries.
    VM_PD_SHIFT = 20,   // ARM actually does a >> 22 << 2, make sure this isn't messed up on arm...
#else
    VM_PD_SHIFT = 22,   // Seems to be the case for all platforms
#endif
    VM_PT_SHIFT = 12,
    VM_PD_SIZE  = (VM_PAGE_OFST_MASK | PG_SECTION_OFST_MSAK) + 1,
};


typedef struct _MEMORY_BLOCK 
{
    DWORD   dwMemoryStart;
    DWORD   dwMemorySize;
    PROCESS *VMProcess;
} MEMORY_BLOCK;

MINIDUMP_HEADER         g_ceDmpHeader;
MINIDUMP_DIRECTORY      g_ceDmpDirectory[MAX_STREAMS];
CEDUMP_SYSTEM_INFO      g_ceDmpSystemInfo;
CEDUMP_EXCEPTION_STREAM g_ceDmpExceptionStream;
CEDUMP_EXCEPTION        g_ceDmpException;
CEDUMP_ELEMENT_LIST     g_ceDmpExceptionContext;
CEDUMP_ELEMENT_LIST     g_ceDmpModuleList;
CEDUMP_ELEMENT_LIST     g_ceDmpProcessList;
CEDUMP_ELEMENT_LIST     g_ceDmpThreadList;
CEDUMP_ELEMENT_LIST     g_ceDmpThreadContextList;
CEDUMP_ELEMENT_LIST     g_ceDmpProcessModuleList;
CEDUMP_THREAD_CALL_STACK_LIST   g_ceDmpThreadCallStackList;
CEDUMP_MEMORY_LIST              g_ceDmpMemoryVirtualList;
CEDUMP_MEMORY_LIST              g_ceDmpMemoryPhysicalList;
CEDUMP_BUCKET_PARAMETERS        g_ceDmpBucketParameters;

WATSON_DUMP_SETTINGS g_watsonDumpSettings = {0};

BYTE         g_bBuffer[FLEX_PTMI_BUFFER_SIZE];
MEMORY_BLOCK g_memoryVirtualBlocks[MAX_MEMORY_VIRTUAL_BLOCKS];
DWORD        g_dwMemoryVirtualBlocksTotal = 0;
DWORD        g_dwMemoryVirtualSizeTotal = 0;
MEMORY_BLOCK g_memoryPhysicalBlocks[MAX_MEMORY_PHYSICAL_BLOCKS];
DWORD        g_dwMemoryPhysicalBlocksTotal = 0;
DWORD        g_dwMemoryPhysicalSizeTotal = 0;
BYTE         g_bBufferAlignment[CEDUMP_ALIGNMENT] = {0};

DWORD g_dwReservedMemorySize = 0;
DWORD g_dwSegmentIdx = 0;
DWORD g_dwWrittenOffset;
DWORD g_dwWrittenCRC;
DWORD g_dwReadOffset;
DWORD g_dwReadCRC;
LONG  g_lThreadsInPostMortemCapture = 0;
BOOL  g_fDumpHeapData = FALSE;
BOOL  g_fDumpNkGlobals = FALSE;
BOOL  g_fNkGlobalsDumped = FALSE;
BOOL  g_fCaptureDumpFileOnDeviceCalled = FALSE;
BOOL  g_fReportFaultCalled = FALSE;
BOOL  g_fStackBufferOverrun = FALSE;
DWORD g_dwLastError;
DWORD g_dwDumpFileSize = 0;
CEDUMP_TYPE g_ceDumpType = ceDumpTypeUndefined;

OSAXS_KDBG_RESPONSE_FUNC g_pKdpSendOsAxsResponse = NULL;
BYTE *g_pKdpBuffer = NULL;
DWORD g_dwKdpBufferSize = 0;
DWORD g_dwKdpBufferOffset = 0;

// See if g_pDmpProc needs to be split into g_pDmpActvProc and g_pDmpVMProc
PTHREAD g_pDmpThread = NULL;
PPROCESS g_pDmpProc = NULL;

HANDLE g_hEventDumpFileReady = NULL;

WCHAR g_wzOEMString[MAX_PATH];
DWORD g_dwOEMStringSize = 0;
WCHAR g_wzPlatformType[MAX_PATH];
DWORD g_dwPlatformTypeSize = 0;
PLATFORMVERSION g_platformVersion[4];
DWORD g_dwPlatformVersionSize = 0;

// Local copy of exception record and context record when using ReportFault
// This is to make sure untrusted apps do not change the contents after they have
// been validated
EXCEPTION_RECORD g_exceptionRecordReportFault = {0};
CONTEXT g_contextReportFault = {0};


static void __forceinline DisableFaults ()
{
    UTlsPtr () [TLSSLOT_KERNEL] |= (TLSKERN_NOFAULTMSG | TLSKERN_NOFAULT);
}

static void __forceinline EnableFaults ()
{
    UTlsPtr () [TLSSLOT_KERNEL] &= ~(TLSKERN_NOFAULTMSG|TLSKERN_NOFAULT);
}

static void __forceinline SetFrameCount (DWORD ThreadCount, DWORD FrameCount)
{
    if (ThreadCount < MAX_THREAD_ENTRIES)
        reinterpret_cast<WORD*>(g_bBuffer)[ThreadCount] = static_cast<WORD> (FrameCount);
}

static bool __forceinline CheckFrameCount (DWORD ThreadCount, DWORD FrameCount)
{
    if (ThreadCount < MAX_THREAD_ENTRIES)
        return reinterpret_cast<WORD*>(g_bBuffer)[ThreadCount] == static_cast<WORD> (FrameCount);
    return true;
}

static DWORD __forceinline GetFrameCount (DWORD ThreadCount)
{
    if (ThreadCount < MAX_THREAD_ENTRIES)
        return reinterpret_cast<WORD*>(g_bBuffer)[ThreadCount];
    return -1;
}


static BOOL __forceinline WINAPI EventModify(HANDLE hEvent, DWORD func)
{
    // Used by SetEvent, ResetEvent, and PulseEvent
    return g_OsaxsData.pEventModify(hEvent, func);
}

static void __forceinline SafeSetEvent(HANDLE Event)
{
    
    PPROCESS PreviousProcess = pActvProc;

    if (pActvProc != g_pprcNK)
    {
        // Switch to NK to fire the event if necessary.
        PreviousProcess = g_OsaxsData.pfnSwitchActiveProcess (g_pprcNK);
    }
    
    SetEvent (Event);

    if (pActvProc != PreviousProcess)
    {
        // Switch back to the previous active process if necessary.
        g_OsaxsData.pfnSwitchActiveProcess (PreviousProcess);
    }
}

/*----------------------------------------------------------------------------
    WatsonWriteKDBG

    Write out byte of data to KDBG buffer and send when full.
----------------------------------------------------------------------------*/
static HRESULT WatsonWriteKDBG(BYTE bDataOut, BOOL fFlush)
{
    HRESULT hRes = E_FAIL;
    
    if (!g_dwKdpBufferSize)
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WatsonWriteKDBG: g_dwKdpBufferSize is zero\r\n"));
        hRes = E_FAIL;
        goto Exit;
    }
    
    if (g_dwKdpBufferOffset >= g_dwKdpBufferSize)
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WatsonWriteKDBG: g_dwKdpBufferOffset (%u) >= g_dwKdpBufferSize (%u)\r\n", g_dwKdpBufferOffset, g_dwKdpBufferSize));
        hRes = E_FAIL; 
        goto Exit;
    }

    if (!fFlush)
    {
        g_pKdpBuffer[g_dwKdpBufferOffset] = bDataOut;
        ++ g_dwKdpBufferOffset;
    }

    if ((g_dwKdpBufferOffset >= g_dwKdpBufferSize) || fFlush)
    {
        STRING ResponseHeader;
        STRING AdditionalData;
        OSAXS_COMMAND OsAxsCmd;

        OsAxsCmd.dwApi = OSAXS_API_GET_WATSON_DUMP_FILE;
        OsAxsCmd.dwVersion = OSAXS_PROTOCOL_LATEST_VERSION;
        OsAxsCmd.hr = S_OK;
        OsAxsCmd.u.GetWatsonOTF.dwDumpType = (DWORD)g_ceDumpType;
        OsAxsCmd.u.GetWatsonOTF.dw64MaxDumpFileSize = g_dwDumpFileSize;
        OsAxsCmd.u.GetWatsonOTF.dwSegmentBufferSize = g_dwKdpBufferOffset;
        OsAxsCmd.u.GetWatsonOTF.dw64SegmentIndex = g_dwSegmentIdx;
        g_dwSegmentIdx += g_dwKdpBufferOffset;
        OsAxsCmd.u.GetWatsonOTF.fDumpfileComplete = FALSE;
        
        ResponseHeader.Length = sizeof (OsAxsCmd);
        ResponseHeader.Buffer = (PCHAR) &OsAxsCmd;
        AdditionalData.Length = (USHORT) g_dwKdpBufferOffset;
        AdditionalData.Buffer = (PCHAR) g_pKdpBuffer;
        
        g_dwKdpBufferOffset = 0;
        
        BOOL fContinue = g_pKdpSendOsAxsResponse (&ResponseHeader, &AdditionalData);
        if (!fContinue)
        {
            DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WatsonWriteData: g_pKdpSendOsAxsResponse returned false, aborting ... \r\n"));
            hRes = E_FAIL; 
            g_dwKdpBufferSize = 0;
            goto Exit;
        }
    }

    hRes = S_OK;

Exit:

    return hRes;
}

/*----------------------------------------------------------------------------
    WatsonReadData

    Reads data from the input stream, either reserved memory or KDBG.
----------------------------------------------------------------------------*/
static HRESULT WatsonReadData(DWORD dwOffset, PVOID pDataBuffer, DWORD dwDataSize, BOOL fCalculateCRC)
{
    HRESULT hRes = E_FAIL;
    DWORD dwCRCLoop;
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WatsonReadData: Enter\r\n"));

    if (((dwOffset + dwDataSize) > g_dwReservedMemorySize) || // Check for buffer overflow
        (dwOffset > (dwOffset + dwDataSize)))                 // Check for integer overflow
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WatsonReadData: Trying to read beyond size of reserved memory, Reserved Size=0x%08X, dwOffset=0x%08X, dwDataSize=0x%08X\r\n", g_dwReservedMemorySize, dwOffset, dwDataSize));
        hRes = E_OUTOFMEMORY; 
        goto Exit;
    }
    
    if (pfnNKKernelLibIoControl((HANDLE) KMOD_CORE, IOCTL_KLIB_READWATSON, NULL, dwOffset, pDataBuffer, dwDataSize, NULL) != dwDataSize)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WatsonReadData: KernelLibIoControl failed, pDataBuffer=0x%08X, dwOffset=0x%08X, dwDataSize=0x%08X\r\n", pDataBuffer, dwOffset, dwDataSize));
        hRes = E_FAIL; 
        goto Exit;
    }

    if (fCalculateCRC)
    {
        // Make sure we are reading in sequentially
        KD_ASSERT(g_dwReadOffset == dwOffset);
        if (g_dwReadOffset != dwOffset)
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WatsonReadData: Dump file not read sequentially, Expected Offset=%u, Actual Offset=%u\r\n", g_dwReadOffset, dwOffset));
            hRes = E_FAIL; 
            goto Exit;
        }

        for (dwCRCLoop = 0; dwCRCLoop < dwDataSize; ++dwCRCLoop)
        {
            // Add up all the bytes in the dump file (Simple CRC)
            g_dwReadCRC += *((BYTE *)pDataBuffer + dwCRCLoop);
        }

        g_dwReadOffset += dwDataSize;
    }
    
    hRes = S_OK;

Exit:

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WatsonReadData: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WatsonWriteData

    Writes the data to the output stream, either reserved memory or KDBG.
----------------------------------------------------------------------------*/
static HRESULT WatsonWriteData(DWORD dwOffset, PVOID pDataBuffer, DWORD dwDataSize, BOOL fCalculateCRC)
{
    HRESULT hRes = E_FAIL;
    DWORD dwWriteLoop;
    BYTE  bDataOut;
    BOOL  fExceptionEncountered = FALSE;
    DWORD dwWriteLoopError;
    DWORD dwExceptionCount = 0;
    PVOID pAddr;
    
    if (((dwOffset + dwDataSize) > g_dwReservedMemorySize) || // Check for buffer overflow
        (dwOffset > (dwOffset + dwDataSize)))                 // Check for integer overflow
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WatsonWriteData: Trying to write beyond size of reserved memory, Reserved Size=0x%08X, dwOffset=0x%08X, dwDataSize=0x%08X\r\n", g_dwReservedMemorySize, dwOffset, dwDataSize));
        hRes = E_OUTOFMEMORY;
        goto Exit;
    }
    
    if (fCalculateCRC)
    {
        // Make sure we are writing out sequentially
        KD_ASSERT(g_dwWrittenOffset == dwOffset);
        if (g_dwWrittenOffset != dwOffset)
        {
            DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WatsonWriteData: Data not written sequentially, Expected Offset=%u, Actual Offset=%u\r\n", g_dwWrittenOffset, dwOffset));
            hRes = E_FAIL; 
            goto Exit;
        }
        g_dwWrittenOffset += dwDataSize;
    }
    

    for (dwWriteLoop = 0; dwWriteLoop < dwDataSize; ++dwWriteLoop)
    {
        pAddr = (PVOID)((BYTE *)pDataBuffer + dwWriteLoop);

#ifdef DEBUG        
        // At this point we should only have valid addresses that are currently paged in
        if ((0 == dwWriteLoop) || ((DWORD)pAddr & (VM_PAGE_SIZE-1)))
        {
            // Validate the page is paged in each time we move to a new page
            KD_ASSERT(NULL != MapToDebuggeeCtxKernEquivIfAcc (NULL, pAddr, TRUE /* Probe Only */));
        }
#endif // DEBUG
        
        // Just in case we may be reading from invalid memory, catch possible exceptions

        // Prevent hitting exceptions in the debugger
        DisableFaults ();
        __try
        {
            bDataOut = *((BYTE *)pAddr);
        }
        __except (EXCEPTION_EXECUTE_HANDLER)
        {
            if (FALSE == fExceptionEncountered)
            {
                dwWriteLoopError = dwWriteLoop;
                fExceptionEncountered = TRUE;
            }
            bDataOut = 0;
            ++ dwExceptionCount;
        }
        // Enable hitting exceptions in the debugger
        EnableFaults ();

        if (g_pKdpSendOsAxsResponse)
        {
            hRes = WatsonWriteKDBG(bDataOut, FALSE);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WatsonWriteData: WatsonWriteKDBG failed, hRes=0x%08X, pDataBuffer=0x%08X, dwOffset=0x%08X, dwWriteLoop=0x%08X\r\n", hRes, pDataBuffer, dwOffset, dwWriteLoop));
                hRes = E_FAIL;
                goto Exit;
            }
        }
        else
        {
            // Write out byte of memory to reserved memory location
            if (pfnNKKernelLibIoControl((HANDLE) KMOD_CORE, IOCTL_KLIB_WRITEWATSON, &bDataOut, 1, NULL, (dwOffset+dwWriteLoop), NULL) != 1)
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WatsonWriteData: KernelLibIoControl failed, pDataBuffer=0x%08X, dwOffset=0x%08X, dwWriteLoop=0x%08X\r\n", pDataBuffer, dwOffset, dwWriteLoop));
                hRes = E_FAIL; 
                goto Exit;
            }
        }

        if (fCalculateCRC)
        {
            // Add up all the bytes written so far (Simple CRC)
            g_dwWrittenCRC += bDataOut;
        }
    }
    
    if (TRUE == fExceptionEncountered)
    {
        // This is not an error, just a warning (We should try avoid reading invalid memory)
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WatsonWriteData: Exception encountered while reading memory, pDataBuffer=0x%08X, First Exception Offset=0x%08X, Exception Count=%u\r\n", 
                                  pDataBuffer, dwWriteLoopError, dwExceptionCount));
    }
        
    hRes = S_OK;

Exit:

    return hRes;
}


static HRESULT WatsonWriteData(DWORD dwOffset, PPROCESS VMProcess, PVOID pDataBuffer, DWORD dwDataSize, BOOL fCalculateCRC)
{
    PPROCESS PreviousVM = pVMProc;

    DEBUGGERMSG (OXZONE_DWDMPGEN, (L"++DwDmpGen!WatsonWriteData: %08X(%s) %08X, %d bytes\r\n", VMProcess,
                                   VMProcess? VMProcess->lpszProcName : L"(no proc)",
                                   pDataBuffer,
                                   dwDataSize));

    // If necessary, switch the VM to the VMProcess
    // It is interesting to note that despite the fact that we will restore the VM afterwards,
    // this operation is potentially destructive to the state of the OS.  Basically, if the 
    // previous pVMProc was null, then on restore, g_pKData->pVMPrc will not be updated.
    if (VMProcess != pVMProc && reinterpret_cast<DWORD> (pDataBuffer) < VM_KMODE_BASE)
    {
        DEBUGGERMSG (OXZONE_DWDMPGEN, (L"  DwDmpGen!WatsonWriteData: Switch VM from 0x%08x(%s) to 0x%08X(%s)\r\n", 
                                       pVMProc, pVMProc? pVMProc->lpszProcName:L"noname",
                                       VMProcess, VMProcess->lpszProcName));
        PreviousVM = g_OsaxsData.pfnSwitchVM (VMProcess);
    }

    // Do the memory access / copy.
    HRESULT hr = WatsonWriteData (dwOffset, pDataBuffer, dwDataSize, fCalculateCRC);

    // If necessary, switch the VM back.
    if (PreviousVM != pVMProc && reinterpret_cast<DWORD> (pDataBuffer) < VM_KMODE_BASE)
    {
        DEBUGGERMSG (OXZONE_DWDMPGEN, (L"  DwDmpGen!WatsonWriteData: Restore VM from 0x%08X(%s) to 0x%08x(%s)\r\n",
                                       pVMProc, pVMProc? pVMProc->lpszProcName:L"noname",
                                       PreviousVM, PreviousVM->lpszProcName));
        g_OsaxsData.pfnSwitchVM (PreviousVM);
    }

    return hr;
}


static DWORD __forceinline DWGetBucketEventType ()
{
    if (g_pKdpSendOsAxsResponse)
    {
        return (DWORD)WATSON_HOSTCAPTURE_EVENT;
    }
    else if (g_fCaptureDumpFileOnDeviceCalled)
    {
        return (DWORD)WATSON_SNAPSHOT_EVENT;
    }
    else if (g_fStackBufferOverrun)
    {
        return (DWORD)WATSON_BEX_EVENT;
    }

    return (DWORD)WATSON_EXCEPTION_EVENT;
}


static BOOL DWWalkDownStack (CallSnapshot3 const *csexFrames, DWORD &dwFrameCount, DWORD const dwFramesReturned,
    DWORD const dwImageBase, DWORD const dwImageSize, DWORD &dwPC, PPROCESS &pProcess)
{
    BOOL fFramesLeft = TRUE;
    
    while ((dwImageBase <= dwPC) && (dwPC < (dwImageBase + dwImageSize)) && (dwImageBase < (dwImageBase + dwImageSize)) && (fFramesLeft))
    {
        if (dwFrameCount < dwFramesReturned)
        {
            dwPC = csexFrames[dwFrameCount].dwReturnAddr;
            pProcess = OSAXSHandleToProcess((HANDLE)csexFrames[dwFrameCount].dwVMProc);
            ++ dwFrameCount;
        }
        else
        {
            fFramesLeft = FALSE;
        }
    }

    return fFramesLeft;
}


/*----------------------------------------------------------------------------
    GetBucketParameters

    Get the bucket parameters from exception information.
----------------------------------------------------------------------------*/
static HRESULT GetBucketParameters(PCEDUMP_BUCKET_PARAMETERS pceDmpBucketParameters)
{
    HRESULT hRes = E_FAIL;

    DWORD dwPC, dwExcAddr, dwPCOriginal;
    PPROCESS pVMProcessOriginal, pVMProcess;
    PTHREAD  pThread;
    BOOL fFoundMod = FALSE;
    VS_FIXEDFILEINFO vsFixedInfo = {0};
    CallSnapshot3 csexFrames[STACK_FRAMES_BUFFERED] = {0};
    DWORD dwFramesReturned, dwFrameCount;
    BOOL  fDifferentPCorProcess = FALSE;
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!GetBucketParameters: Enter\r\n"));

    pceDmpBucketParameters->SizeOfHeader = sizeof(CEDUMP_BUCKET_PARAMETERS);
    pceDmpBucketParameters->EventType = DWGetBucketEventType ();

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!GetBucketParameters: hCurProc=0x%08X, hCurThread=0x%08X, hOwnerProc=0x%08X\r\n",
                                g_pKData->pCurPrc->dwId, g_pKData->pCurThd->dwId, (DWORD)((PROCESS *)(g_pKData->pCurThd->pprcOwner)->dwId)));

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!GetBucketParameters: hDmpProc=0x%08X, hDmpThread=0x%08X, hOwnerProc=0x%08X\r\n",
                                g_pDmpProc->dwId, g_pDmpThread->dwId, (DWORD)((PROCESS *)(g_pDmpThread->pprcOwner)->dwId)));

    pVMProcessOriginal = pVMProcess = g_pDmpProc;
    pThread = g_pDmpThread;

    if (pThread == pCurThread)
    {
        // Default to the Exception PC
        dwPCOriginal = dwPC = CONTEXT_TO_PROGRAM_COUNTER (g_pexceptionInfo->ExceptionPointers.ContextRecord);
        dwExcAddr = (DWORD) g_pexceptionInfo->ExceptionPointers.ExceptionRecord->ExceptionAddress;
        KD_ASSERT (pVMProcess == pVMProc);
        
#ifdef DEBUG
        if ((dwExcAddr < dwPC) || (dwExcAddr > (dwPC+4)))
        {
            DEBUGGERMSG (OXZONE_ALERT,(L"  DwDmpGen!GetBucketParameters: WARNING: Exception frame PC (0x%08X) is different than Exception address (0x%08X).\r\n", dwPC, dwExcAddr));
        }
#endif // DEBUG
        
        // Check if CaptureDumpFileOnDevice API called for current thread
        if (g_fCaptureDumpFileOnDeviceCalled)
        {
            // Unwind two frames from point of exception (CaptureDumpFileOnDevice API + Calling function)
            dwFramesReturned = GetCallStack(pThread, 2, csexFrames, STACKSNAP_NEW_VM, 0);
            if (dwFramesReturned != 2)
            {
                DEBUGGERMSG (OXZONE_ALERT,(L"  DwDmpGen!GetBucketParameters: GetCallStack did not unwind 2 frames for CaptureDumpFileOnDevice API, dwFramesReturned=%u.\r\n", dwFramesReturned));
                // Don't fail, just go with the exception PC

            }
            else
            {
                // Get the PC and process of the frame calling CaptureDumpFileOnDevice API
                // this will provide bucketing data for the module that called the API
                dwFrameCount = 1;
                dwPC = csexFrames[dwFrameCount].dwReturnAddr;
                ++ dwFrameCount;
            }
        }
    }
    else
    {
        // We should only get here when CaptureDumpFileOnDevice API is called for another thread
        KD_ASSERT(g_fCaptureDumpFileOnDeviceCalled);

        dwPCOriginal = dwPC = GetThreadIP(pThread);

        // Check if PC in Nk.exe (Process 0) & Nk.exe not owner of thread
        // If PC is in Nk.exe then we try unwind out of NK.exe & Coredll.dll
        // This should give us better bucket parameters, otherwise we always get Nk.EXE + KCall offset for buckets
        DWORD const NKImageBase = reinterpret_cast<DWORD> (g_pprcNK->BasePtr);
        DWORD const NKImageSize = g_pprcNK->e32.e32_vsize;
        DWORD const NKImageEnd = NKImageBase + NKImageSize;

        // Make sure that the dump process matches the dump thread's vm.
        KD_ASSERT (pVMProcess == pThread->pprcVM);

        if (NKImageBase <= dwPC && dwPC < NKImageEnd && NKImageBase < NKImageEnd && pThread->pprcOwner != g_pprcNK)
        {
            // Unwind frames from current PC 
            dwFramesReturned = GetCallStack (pThread, STACK_FRAMES_BUFFERED, csexFrames, STACKSNAP_NEW_VM, 0);
            if (dwFramesReturned <= 0)
            {
                DEBUGGERMSG (OXZONE_ALERT,(L"  DwDmpGen!GetBucketParameters: GetCallStack did not unwind frames from Nk.exe, dwFramesReturned=%u.\r\n", dwFramesReturned));
                // Don't fail, just go with the NK.exe PC
            }
            else
            {
                BOOL fFramesLeft = TRUE;

                // Walk down frames until we are out of nk.exe (Process 0)
                
                // Start at top frame
                dwFrameCount = 0;

                fFramesLeft = DWWalkDownStack (csexFrames, dwFrameCount, dwFramesReturned, NKImageBase, NKImageSize, dwPC, pVMProcess);

                // Now Walk down frames until we are out of coredll.dll 
                // If we don't have any in coredll.dll, then we still continue
                // since coredll.dll may not always be in the call stack, 
                // especially with retail x86.
                if (fFramesLeft)
                {
                    PMODULELIST pModList = reinterpret_cast<PMODULELIST> (g_pprcNK->modList.pFwd);
                    
                    while (pModList && pModList != reinterpret_cast<PMODULELIST> (&g_pprcNK->modList))
                    {
                        if (pModList->pMod && pModList->pMod == pModList->pMod->lpSelf)
                        {
                            if (wcsnicmp(pModList->pMod->lpszModName, L"coredll.dll", wcslen(pModList->pMod->lpszModName)) == 0)
                            {
                                // Coredll.dll found
                                fFramesLeft = DWWalkDownStack (csexFrames, dwFrameCount, dwFramesReturned,
                                                               reinterpret_cast<DWORD> (pModList->pMod->BasePtr), pModList->pMod->e32.e32_vsize,
                                                               dwPC, pVMProcess);
                                break;
                            }
                        }
                        else
                        {
                            DEBUGGERMSG (OXZONE_ALERT, (L"  DwDmpGen!GetBucketParameters: Invalid module? 0X%08X on 0X%08X\r\n", pModList->pMod, pModList));
                        }
                        pModList = reinterpret_cast<PMODULELIST> (pModList->link.pFwd);
                    }
                }

                if (!fFramesLeft)
                {
                    // Ran out of frames, in which case revert to the original PC & Process
                    DEBUGGERMSG (OXZONE_ALERT,(L"  DwDmpGen!GetBucketParameters: WARNING: Walking out of Nk.exe walked to many frames, reverting to orignal PC, dwFrameCount=%u.\r\n", dwFrameCount));
                    dwPC = dwPCOriginal;
                    pVMProcess = pVMProcessOriginal;
                }
            }
        }
    }

    if ((dwPC != dwPCOriginal) || (pVMProcess != pVMProcessOriginal))
    {
        DEBUGGERMSG (OXZONE_DWDMPGEN, (L"  DwDmpGen!GetBucketParameters: CaptureDumpFileOnDevice API called, using different PC or Process\r\n"));
        DEBUGGERMSG (OXZONE_DWDMPGEN, (L"  DwDmpGen!GetBucketParameters: New PC=0x%08X, Original PC=0x%08X, New Process=0x%08X, Original Process=0x%08X\r\n",
                                      dwPC, dwPCOriginal, (pVMProcess ? pVMProcess->dwId: 0), pVMProcessOriginal->dwId));
        fDifferentPCorProcess = TRUE;
    }

    while (!fFoundMod)
    {
        if (dwPC && pVMProcess)
        {
            DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!GetBucketParameters: PC=0x%08X\r\n", dwPC));

            // Get the bucket parameters for the Application
            pceDmpBucketParameters->AppName  = (DWORD)pVMProcess->lpszProcName;
            pceDmpBucketParameters->AppStamp = pVMProcess->e32.e32_timestamp;

            hRes = GetVersionInfo(pVMProcess, FALSE, &vsFixedInfo);
            if (SUCCEEDED(hRes))
            {
                pceDmpBucketParameters->AppVerMS = vsFixedInfo.dwFileVersionMS;
                pceDmpBucketParameters->AppVerLS = vsFixedInfo.dwFileVersionLS;
                pceDmpBucketParameters->fDebug   = (vsFixedInfo.dwFileFlags & VS_FF_DEBUG)?1:0;
            }
            else
            {
                pceDmpBucketParameters->AppVerMS = (pVMProcess->e32.e32_cevermajor << 16) | (pVMProcess->e32.e32_ceverminor & 0xFFFF);
                pceDmpBucketParameters->AppVerLS = 0;
                pceDmpBucketParameters->fDebug   = 0;
            }

            DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!GetBucketParameters: AppName=%s, AppStamp=0x%08X, AppVer=%u.%u.%u.%u, fDebug=%u\r\n", 
                                        (LPWSTR)pceDmpBucketParameters->AppName,
                                        pceDmpBucketParameters->AppStamp,
                                        (WORD)(pceDmpBucketParameters->AppVerMS >> 16),
                                        (WORD)(pceDmpBucketParameters->AppVerMS & 0xFFFF),
                                        (WORD)(pceDmpBucketParameters->AppVerLS >> 16),
                                        (WORD)(pceDmpBucketParameters->AppVerLS & 0xFFFF),
                                        pceDmpBucketParameters->fDebug));

            // Get the bucket parameters for the Module, may be in a process or a DLL
            if (dwPC < VM_DLL_BASE)
            {
                DWORD const ImageBase = reinterpret_cast<DWORD> (pVMProcess->BasePtr);
                DWORD const ImageSize = pVMProcess->e32.e32_vsize;
                DWORD const ImageEnd  = ImageBase + ImageSize;
        
                if ((ImageBase <= dwPC) && (dwPC < ImageEnd) && (ImageBase < ImageEnd))
                {
                    // Correct process found
                    // Exception occured within process, so exception module is a process
                    fFoundMod = TRUE;
                    pceDmpBucketParameters->ModName  = reinterpret_cast<DWORD> (pVMProcess->lpszProcName);
                    pceDmpBucketParameters->ModStamp = pVMProcess->e32.e32_timestamp;
                    pceDmpBucketParameters->Offset = dwPC - ImageBase;

                    hRes = GetVersionInfo (pVMProcess, FALSE, &vsFixedInfo);
                    if (SUCCEEDED(hRes))
                    {
                        pceDmpBucketParameters->ModVerMS = vsFixedInfo.dwFileVersionMS;
                        pceDmpBucketParameters->ModVerLS = vsFixedInfo.dwFileVersionLS;
                    }
                    else
                    {
                        pceDmpBucketParameters->ModVerMS = (pVMProcess->e32.e32_cevermajor << 16) 
                                                         | (pVMProcess->e32.e32_ceverminor & 0xFFFF);
                        pceDmpBucketParameters->ModVerLS = 0;
                    }
                }
            }
            else
            {
                // Exception occured in a loaded DLL, find correct module
                PMODULELIST pModList = reinterpret_cast<PMODULELIST> (g_pprcNK->modList.pFwd);

                while (pModList && (pModList != reinterpret_cast<PMODULELIST> (&g_pprcNK->modList)))
                {
                    if (pModList->pMod && pModList->pMod == pModList->pMod->lpSelf)
                    {
                        DWORD const ImageBase = reinterpret_cast<DWORD> (pModList->pMod->BasePtr);
                        DWORD const ImageSize = static_cast<DWORD> (pModList->pMod->e32.e32_vsize);
                        DWORD const ImageEnd = ImageBase + ImageSize;
                        
                        if (ImageBase <= dwPC && dwPC < ImageEnd && ImageBase < ImageEnd)
                        {
                            // Correct module found
                            fFoundMod = TRUE;
                            pceDmpBucketParameters->ModName  = (DWORD)pModList->pMod->lpszModName;
                            pceDmpBucketParameters->ModStamp = pModList->pMod->e32.e32_timestamp;
                            pceDmpBucketParameters->Offset = dwPC - ImageBase;
                            hRes = GetVersionInfo(pModList->pMod, TRUE, &vsFixedInfo);
                            if (SUCCEEDED(hRes))
                            {
                                pceDmpBucketParameters->ModVerMS = vsFixedInfo.dwFileVersionMS;
                                pceDmpBucketParameters->ModVerLS = vsFixedInfo.dwFileVersionLS;
                            }
                            else
                            {
                                pceDmpBucketParameters->ModVerMS = (pModList->pMod->e32.e32_cevermajor << 16) 
                                                                   | (pModList->pMod->e32.e32_ceverminor & 0xFFFF);
                                pceDmpBucketParameters->ModVerLS = 0;
                            }
                            break;
                        }
                    }

                    pModList = reinterpret_cast<PMODULELIST> (pModList->link.pFwd);
                }
            }
        }

        if (fFoundMod)
        {
            // Module found
            DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!GetBucketParameters: ModName=%s, ModStamp=0x%08X, ModVer=%u.%u.%u.%u, Offset=0x%08X\r\n", 
                                        (LPWSTR)pceDmpBucketParameters->ModName,
                                        pceDmpBucketParameters->ModStamp,
                                        (WORD)(pceDmpBucketParameters->ModVerMS >> 16),
                                        (WORD)(pceDmpBucketParameters->ModVerMS & 0xFFFF),
                                        (WORD)(pceDmpBucketParameters->ModVerLS >> 16),
                                        (WORD)(pceDmpBucketParameters->ModVerLS & 0xFFFF),
                                        pceDmpBucketParameters->Offset));
        }
        else
        {
            // No module found
            if (fDifferentPCorProcess)
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!GetBucketParameters: Could not find module where exception occured, trying original PC and Process\r\n"));
                dwPC = dwPCOriginal;
                pVMProcess = pVMProcessOriginal;
                fDifferentPCorProcess = FALSE;
            }
            else
            {
                // Still no module found, error
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!GetBucketParameters: Could not find module where exception occured\r\n"));
                hRes = E_FAIL;
                goto Exit;
            }
        }
    }

    // Get the bucket parameters for the Owner Application
    pVMProcess = pThread->pprcOwner;
    pceDmpBucketParameters->OwnerName  = (DWORD)pVMProcess->lpszProcName;
    pceDmpBucketParameters->OwnerStamp = pVMProcess->e32.e32_timestamp;
    
    hRes = GetVersionInfo(pVMProcess, FALSE, &vsFixedInfo);
    if (SUCCEEDED(hRes))
    {
        pceDmpBucketParameters->OwnerVerMS = vsFixedInfo.dwFileVersionMS;
        pceDmpBucketParameters->OwnerVerLS = vsFixedInfo.dwFileVersionLS;
    }
    else
    {
        pceDmpBucketParameters->OwnerVerMS = (pVMProcess->e32.e32_cevermajor << 16) | (pVMProcess->e32.e32_ceverminor & 0xFFFF);
        pceDmpBucketParameters->OwnerVerLS = 0;
    }
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!GetBucketParameters: OwnerName=%s, OwnerStamp=0x%08X, OwnerVer=%u.%u.%u.%u\r\n", 
                                (LPWSTR)pceDmpBucketParameters->OwnerName,
                                pceDmpBucketParameters->OwnerStamp,
                                (WORD)(pceDmpBucketParameters->OwnerVerMS >> 16),
                                (WORD)(pceDmpBucketParameters->OwnerVerMS & 0xFFFF),
                                (WORD)(pceDmpBucketParameters->OwnerVerLS >> 16),
                                (WORD)(pceDmpBucketParameters->OwnerVerLS & 0xFFFF)));
    
    hRes = S_OK;

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!GetBucketParameters: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    GetCaptureDumpFileParameters

    Get Parameters passed to the CaptureDumpFileOnDevice API.
----------------------------------------------------------------------------*/
static HRESULT GetCaptureDumpFileParameters(EXCEPTION_INFO * pexceptionInfo)
{
    HRESULT hRes = E_FAIL;
    HANDLE hDumpProc;
    HANDLE hDumpThread;
    BOOL   fTrustedApp;
    BYTE   bTrustLevel;
    PEXCEPTION_RECORD pExceptionRecord = pexceptionInfo->ExceptionPointers.ExceptionRecord;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!GetCaptureDumpFileParameters: Enter\r\n"));

    // Check if CaptureDumpFileOnDevice API called
    if (g_fCaptureDumpFileOnDeviceCalled)
    {
        // Get the process and thread to dump 
        hDumpProc   = reinterpret_cast<HANDLE> (pExceptionRecord->ExceptionInformation[0]);
        hDumpThread = reinterpret_cast<HANDLE> (pExceptionRecord->ExceptionInformation[1]);
        bTrustLevel = static_cast<BYTE> (pExceptionRecord->ExceptionInformation[3]);

        if (hDumpProc == reinterpret_cast<HANDLE> (-1) && hDumpThread == reinterpret_cast<HANDLE> (-1))
        {
            // When Process ID & Thread ID are both -1 then we are passing in exception pointers from ReportFault
            g_fCaptureDumpFileOnDeviceCalled = FALSE;
            g_fReportFaultCalled = TRUE;

            PEXCEPTION_POINTERS pep;
            PEXCEPTION_RECORD pPepExceptionRecord;
            PCONTEXT pPepContextRecord;
            PVOID    pIP;
                
            // Validate exception pointers pointer
            pep = OSAXSMapTypePtr (NULL, reinterpret_cast<PEXCEPTION_POINTERS> (pExceptionRecord->ExceptionInformation[2]), TRUE);
            if (NULL == pep)
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Exception Pointers Pointer (0x%08X) NULL or unsafe\r\n", pep));
                hRes = E_INVALIDARG;
                goto Exit;
            }

            DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: API call to ReportFault detected, Exception Pointers=0x%08X\r\n", pep));

            // Validate exception record pointer
            pPepExceptionRecord = OSAXSMapTypePtr (NULL, reinterpret_cast<PEXCEPTION_RECORD> (pep->ExceptionRecord), TRUE);
            if (NULL == pPepExceptionRecord)
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Exception Record Pointer (0x%08X) NULL or unsafe\r\n", pep->ExceptionRecord));
                hRes = E_INVALIDARG;
                goto Exit;
            }
            
            // Validate context record pointer
            pPepContextRecord = OSAXSMapTypePtr (NULL, reinterpret_cast<PCONTEXT> (pep->ContextRecord), TRUE);
            if (NULL == pPepContextRecord)
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Context Record Pointer (0x%08X) NULL or unsafe\r\n", pep->ContextRecord));
                hRes = E_INVALIDARG;
                goto Exit;
            }

            // Create local copy of exception & context records (memory violations will be caught by __try __except in CaptureDumpFile)
            memcpy(&g_exceptionRecordReportFault, pPepExceptionRecord, sizeof(g_exceptionRecordReportFault));
            memcpy(&g_contextReportFault, pPepContextRecord, sizeof(g_contextReportFault));
            
            // Point to local copy of exception & context records for use in rest of dump file processing
            pexceptionInfo->ExceptionPointers.ExceptionRecord = &g_exceptionRecordReportFault;
            pexceptionInfo->ExceptionPointers.ContextRecord = &g_contextReportFault;

            // Validate the Instruction Pointer from the context record is within the applications valid memory space            
            pIP = (LPVOID) CONTEXT_TO_PROGRAM_COUNTER (pexceptionInfo->ExceptionPointers.ContextRecord);
            if (NULL == pIP)
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Instruction Pointer (0x%08X) NULL or unsafe\r\n", 
                                          CONTEXT_TO_PROGRAM_COUNTER (pexceptionInfo->ExceptionPointers.ContextRecord)));
                hRes = E_INVALIDARG;
                goto Exit;
            }

            g_fStackBufferOverrun = (STATUS_STACK_BUFFER_OVERRUN == pexceptionInfo->ExceptionPointers.ExceptionRecord->ExceptionCode);

            if (g_fStackBufferOverrun)
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Security check called ReportFault, creating %s event type\r\n",
                                          WATSON_BEX_EVENT));
            }

            hRes = S_OK;
            goto Exit;
        }

        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: API call to CaptureDumpFileOnDevice, dwProcessId=0x%08X, dwThreadId=0x%08X\r\n",
                                  hDumpProc, hDumpThread));

//        KD_ASSERT(bTrustLevel == g_pKData->pCurPrc->bTrustLevel);

//        if ((KERN_TRUST_FULL != bTrustLevel) || (KERN_TRUST_FULL != g_pKData->pCurPrc->bTrustLevel))
        if (0)
        {   
            // If untrusted apps call the CaptureDumpFileOnDevice API the requested process defaults to 0 (current process)
            fTrustedApp = FALSE;
            hDumpProc = 0;
            DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Untrusted API call to CaptureDumpFileOnDevice, dwProcessId forced to zero\r\n",
                                      hDumpProc, hDumpThread));
        }
        else
        {
            fTrustedApp = TRUE;
        }

        // Default to current process if the process ID was not set
        if (0 == hDumpProc)
        {
            hDumpProc = (HANDLE) g_pKData->pCurPrc->dwId;
        }

        // Validate the passed in thread ID, if set
        if (hDumpThread != 0)
        {
            g_pDmpThread = OSAXSHandleToThread(hDumpThread);
            if (NULL == g_pDmpThread)
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Invalid Thread Id passed to CaptureDumpFileOnDevice, dwThreadId=0x%08X\r\n", hDumpThread));
                hRes = E_INVALIDARG;
                goto Exit;
            }
        }

        // Validate the passed in Process ID
        g_pDmpProc = OSAXSHandleToProcess(hDumpProc);
        if (NULL == g_pDmpProc)
        {
            DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Invalid Process Id passed to CaptureDumpFileOnDevice, dwProcessId=0x%08X\r\n", hDumpProc));
            hRes = E_INVALIDARG;
            goto Exit;
        }

        // Check if a thread was specified
        if (hDumpThread > 0)
        {
            // Make sure thread is owned by the specified process for untrusted apps
            if (g_pDmpThread->pprcOwner != g_pDmpProc)
            {
                if (!fTrustedApp)
                {
                    DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Un-Trusted Process does not own Thread passed to CaptureDumpFileOnDevice, dwProcessId=0x%08X, dwThreadId=0x%08X\r\n",
                                              hDumpProc, hDumpThread));
                    hRes = E_ACCESSDENIED;
                    goto Exit;
                }
                
                // Make sure thread is at least running in specified process for trusted apps
                if (g_pDmpThread->pprcVM != g_pDmpProc)
                {
                    DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Thread is not owned by or running in Process passed to CaptureDumpFileOnDevice, dwProcessId=0x%08X, dwThreadId=0x%08X\r\n",
                                              hDumpProc, hDumpThread));
                    hRes = E_INVALIDARG;
                    goto Exit;
                }
            }
        }
        else
        {
            // No thread specified
            if (g_pDmpProc == g_pKData->pCurPrc)
            {
                // For the current process use the current thread
                g_pDmpThread = pCurThread;
            }
            else
            {
                // For any other process use the last created thread
                g_pDmpThread = reinterpret_cast<PTHREAD> (g_pDmpProc->thrdList.pFwd);

                // Process with no thread.  This is bad, very bad.
                KD_ASSERT (g_pDmpThread != reinterpret_cast<PTHREAD> (&g_pDmpProc->thrdList));
            }
        }

        // Now make sure we have the current process for thread (may be different to owner)
        // Note: We may need to introduce the g_pDmpVMProc and g_pDmpActvProc...
        if (g_pDmpThread->pprcVM)
        {
            g_pDmpProc = g_pDmpThread->pprcVM;
        }
        else if (g_pDmpThread->pprcActv)
        {
            g_pDmpProc = g_pDmpThread->pprcActv;
        }
        else if (g_pDmpThread->pprcOwner)
        {
            g_pDmpProc = g_pDmpThread->pprcOwner;
        }
        else
        {
            DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Dump Thread may be invalid, no process available.\r\n"));
            g_pDmpProc = g_pprcNK;
        }

        // Check if pointer to Extra Upload Files passed in
        if (pExceptionRecord->ExceptionInformation[2])
        {
            LPWSTR pwzExtraFilesPath = (LPWSTR)MapToDebuggeeCtxKernEquivIfAcc (pVMProc,
                                               reinterpret_cast<void*> (pExceptionRecord->ExceptionInformation[2]),
                                               TRUE);

            if (!pwzExtraFilesPath)
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Extra Files Path Pointer (0x%08X) NULL or unsafe\r\n", pwzExtraFilesPath));
                hRes = E_INVALIDARG;
                goto Exit;
            }
            
            DWORD dwExtraFilesDirectoryLen = wcslen(g_watsonDumpSettings.wzExtraFilesDirectory);
            DWORD dwExtraFilesPathLen = wcslen(pwzExtraFilesPath);

            if (dwExtraFilesDirectoryLen && g_watsonDumpSettings.wzExtraFilesDirectory[dwExtraFilesDirectoryLen-1] == L'\\')
            {
                // Compare without the final delimiter
                --dwExtraFilesDirectoryLen;
            }

            // Validate & copy path for extra upload files passed to CaptureDumpFileOnDevice API
            if ((dwExtraFilesDirectoryLen > 0) &&                                    // Check if allowed extra files
                (dwExtraFilesPathLen < MAX_PATH) &&                                  // Make sure path specified by user is valid length
                (dwExtraFilesPathLen >= dwExtraFilesDirectoryLen+2) &&               // Make sure path specified by user contains allowed path + delimiter + extra character
                (wcsnicmp(pwzExtraFilesPath,                                     // Make sure start of path is same as allowed
                              g_watsonDumpSettings.wzExtraFilesDirectory,
                              dwExtraFilesDirectoryLen) == 0) &&
                (pwzExtraFilesPath[dwExtraFilesDirectoryLen] == L'\\'))              // Make sure the delimiter is in the correct place
            {
                // Store this in the dump settings for use by DwXfer.dll 
                // DwXfer.dll will re-validate the path using CeGetCanonicalPathNameW
                memcpy(g_watsonDumpSettings.wzExtraFilesPath, 
                       (PVOID)pwzExtraFilesPath,   
                       ((dwExtraFilesPathLen+1) * sizeof(WCHAR)));

                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: API call to CaptureDumpFileOnDevice, pwzExtraFilesPath='%s'\r\n",
                                          pwzExtraFilesPath));
            }
            else
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!GetCaptureDumpFileParameters: Invalid path passed to CaptureDumpFileOnDevice, pwzExtraFilesPath='%s', Allowed Path='%s\\[FolderName]'\r\n",
                                          pwzExtraFilesPath, g_watsonDumpSettings.wzExtraFilesDirectory));
                g_dwLastError = ERROR_BAD_PATHNAME;
                hRes = E_FAIL;
                goto Exit;
            }
        }
    }
    
    hRes = S_OK;

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!GetCaptureDumpFileParameters: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}


// Scan through the memory block list and return the index of the first one that
// is free.  By design, all of the free blocks should be contiguous at the end of
// the list of blocks.
static DWORD DWFirstFreeMemoryBlock (MEMORY_BLOCK *MemoryBlocks, DWORD NumBlocks)
{
    for (DWORD i = 0; i < NumBlocks; ++i)
    {
        if (MemoryBlocks[i].dwMemorySize == EMPTY_BLOCK)
        {
            return i;
        }
    }

    return NumBlocks;
}


// Given a memory range (in NewBlock) find a block in the current list of memory
// blocks that overlaps with the new block.  The definition of overlap is the newblock
// starts within the range of an existing block.
static DWORD DWFindOverlapBlock (MEMORY_BLOCK *MemoryBlocks, DWORD LastBlock, MEMORY_BLOCK const &NewBlock)
{
    DWORD const MemStart = NewBlock.dwMemoryStart;
    DWORD const MemSize  = NewBlock.dwMemorySize;
    DWORD const MemEnd   = MemStart + MemSize;
    
    for (DWORD i = 0; i < LastBlock; ++i)
    {
        DWORD const BlockStart = MemoryBlocks[i].dwMemoryStart;
        DWORD const BlockSize  = MemoryBlocks[i].dwMemorySize;
        DWORD const BlockEnd   = BlockStart + BlockSize;
        
        if (BlockSize != EMPTY_BLOCK)
        {
            if (NewBlock.VMProcess == MemoryBlocks[i].VMProcess && BlockStart <= MemStart && MemStart <= BlockEnd)
            {
                // Found the block.
                return i;
            }
        }
        else
        {
            // Should have been filtered out already...
            KD_ASSERT (FALSE);
        }
    }
    return LastBlock;
}


// This scans through the entire list to make sure that as a result of updating the memory range
// to see if any existing block overlaps.  If there is an overlap, the existing block is removed
// and the new block gets updated to cover the total range.
//
// This function makes sure that all of the free blocks are contiguous at the end of the block
// array by swapping in a working block into the hole opened up by the block update.
static void DWMemoryBlockOverlap (MEMORY_BLOCK *MemoryBlocks, DWORD NewBlock, DWORD EndBlock,
                                  DWORD &TotalMemorySize, DWORD &TotalMemoryBlocks)
{
    DWORD MemoryStart = MemoryBlocks[NewBlock].dwMemoryStart;
    DWORD MemorySize  = MemoryBlocks[NewBlock].dwMemorySize;
    DWORD MemoryEnd   = MemoryStart + MemorySize;
    DWORD CurrentBlock = 0;
    
    while (CurrentBlock < EndBlock)
    {
        // Don't check for overlap with self
        if (CurrentBlock != NewBlock)
        {
            // Get info for current block
            DWORD const BlockSize  = MemoryBlocks[CurrentBlock].dwMemorySize;
            DWORD const BlockStart = MemoryBlocks[CurrentBlock].dwMemoryStart;
            DWORD const BlockEnd   = BlockStart + BlockSize;
        
            // All blocks less than dwFirstEmpty should be in use
            KD_ASSERT (EMPTY_BLOCK != BlockSize);
            
            // Check if start of current block is within new/updated block (or immediately follows)
            if (MemoryBlocks[NewBlock].VMProcess == MemoryBlocks[CurrentBlock].VMProcess 
                && MemoryStart <= BlockStart && BlockStart <= MemoryEnd)
            {
                // Get the combined memory end position
                MemoryEnd = max (MemoryEnd, BlockEnd);
    
                // Remove the added size + current size from total
                TotalMemorySize -= (MemorySize + BlockStart);
                
                // Calculate the new combined size
                MemorySize = MemoryEnd - MemoryStart;
    
                // Set the new combined size
                MemoryBlocks[NewBlock].dwMemorySize = MemorySize;
    
                // Add new combined size to total
                TotalMemorySize += MemorySize;
                
                // We must now remove the current block 
                --TotalMemoryBlocks;

                // Move last block to current position (Thereby removing current block)
                DWORD const LastBlock  = EndBlock - 1;
                MemoryBlocks[CurrentBlock].dwMemoryStart= MemoryBlocks[LastBlock].dwMemoryStart;
                MemoryBlocks[CurrentBlock].dwMemorySize = MemoryBlocks[LastBlock].dwMemorySize;

                // Invalidate last block
                MemoryBlocks[LastBlock].dwMemoryStart = 0;
                MemoryBlocks[LastBlock].dwMemorySize = EMPTY_BLOCK;

                // Update the end pointer
                -- EndBlock;
                
                // Check if the new added block was the last, in which case it is now moved
                if (NewBlock == EndBlock)
                {
                    NewBlock = CurrentBlock;
                }
            }
        }
        ++CurrentBlock;
    }
}


/*----------------------------------------------------------------------------
    MemoryBlocksAdd

    Add a new memory block for saving to dump file.
    
    This function assumes the global 'g_memoryBlocks' has been initialized 
    to all 0xFF before starting a new dump.

    TODO: Consider probing memory to make sure that the dump code can access
    the data (might be a non-issue)?
----------------------------------------------------------------------------*/
static HRESULT MemoryBlocksAdd(PROCESS *VMProcess, DWORD dwMemoryStartAdd, DWORD dwMemorySizeAdd, BOOL fVirtual, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;
    DWORD dwFirstEmpty;
    DWORD dwBlockAdded;
    DWORD dwMemoryEndAdd;
    DWORD dwMemoryBlocksMax;
    MEMORY_BLOCK *pmemoryBlocks;
    DWORD *pdwMemorySizeTotal;
    DWORD *pdwMemoryBlocksTotal;

    MEMORY_BLOCK NewBlock;

    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"++DwDmpGen!MemoryBlocksAdd: Enter, fVirtual=%u, Start=0x%08X, End=0x%08X, Size=0x%08X\r\n", 
                                 fVirtual, dwMemoryStartAdd, (dwMemoryStartAdd + dwMemorySizeAdd - 1) ,dwMemorySizeAdd));

    if (fVirtual)
    {
        // All virtual memory better be qualified.
        KD_ASSERT (VMProcess);
    }

    if (dwMemoryStartAdd >= VM_KMODE_BASE)
    {
        KD_ASSERT (VMProcess == g_pprcNK);
        VMProcess = g_pprcNK;
    }

    // Make sure we only add these during the optimization phase, not the write phase
    KD_ASSERT(FALSE == fWrite);
    if (fWrite)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!MemoryBlocksAdd: Memory blocks should not be added during the write phase\r\n"));
        hRes = E_FAIL;
        goto Exit;
    }

    if (TRUE == fVirtual)
    {
        dwMemoryBlocksMax = MAX_MEMORY_VIRTUAL_BLOCKS;
        pmemoryBlocks = g_memoryVirtualBlocks;
        pdwMemorySizeTotal = &g_dwMemoryVirtualSizeTotal;
        pdwMemoryBlocksTotal = &g_dwMemoryVirtualBlocksTotal;
    }
    else
    {
        dwMemoryBlocksMax = MAX_MEMORY_PHYSICAL_BLOCKS;
        pmemoryBlocks = g_memoryPhysicalBlocks;
        pdwMemorySizeTotal = &g_dwMemoryPhysicalSizeTotal;
        pdwMemoryBlocksTotal = &g_dwMemoryPhysicalBlocksTotal;
    }

    dwFirstEmpty = DWFirstFreeMemoryBlock (pmemoryBlocks, dwMemoryBlocksMax);

    NewBlock.dwMemoryStart = dwMemoryStartAdd;
    NewBlock.dwMemorySize  = dwMemorySizeAdd;
    NewBlock.VMProcess     = VMProcess;
    
    dwBlockAdded = DWFindOverlapBlock (pmemoryBlocks, dwFirstEmpty, NewBlock);
    if (dwBlockAdded < dwFirstEmpty)
    {
        // Overlaps with existing block
        DWORD const BlockStart = pmemoryBlocks[dwBlockAdded].dwMemoryStart;
        DWORD const BlockSize  = pmemoryBlocks[dwBlockAdded].dwMemorySize;
        DWORD const BlockEnd   = BlockStart + BlockSize;
        
        // Get the combined memory end position
        DWORD const BlockNewEnd = max(BlockEnd, (dwMemoryStartAdd + dwMemorySizeAdd));

        // Remove the old size from total
        *pdwMemorySizeTotal -= BlockSize;
        
        // Calculate the new size
        DWORD const BlockNewSize  = BlockNewEnd - BlockStart;

        // Set the new size
        pmemoryBlocks[dwBlockAdded].dwMemorySize = BlockNewSize;

        // Add new size to total
        *pdwMemorySizeTotal += BlockNewSize;

        KD_ASSERT(BlockNewSize == BlockNewEnd - BlockStart);

        DWMemoryBlockOverlap (pmemoryBlocks, dwBlockAdded, dwFirstEmpty, *pdwMemorySizeTotal, *pdwMemoryBlocksTotal);
        hRes = S_OK;
    }
    else if (dwFirstEmpty < dwMemoryBlocksMax)
    {
        // New Block
        pmemoryBlocks[dwFirstEmpty].dwMemoryStart = dwMemoryStartAdd;
        pmemoryBlocks[dwFirstEmpty].dwMemorySize  = dwMemorySizeAdd;
        pmemoryBlocks[dwFirstEmpty].VMProcess     = VMProcess;
        dwMemoryEndAdd = dwMemoryStartAdd + dwMemorySizeAdd;
        
        ++ (*pdwMemoryBlocksTotal);
        *pdwMemorySizeTotal += dwMemorySizeAdd;

        // Save where block added
        dwBlockAdded = dwFirstEmpty;
        
        ++ dwFirstEmpty;
        DWMemoryBlockOverlap (pmemoryBlocks, dwBlockAdded, dwFirstEmpty, *pdwMemorySizeTotal, *pdwMemoryBlocksTotal);
        hRes = S_OK;
    }
    else
    {
        // Out of room.
        
        // If we get here, then we have reached MAX_MEMORY_BLOCKS
        KD_ASSERT(dwFirstEmpty == dwMemoryBlocksMax);
        
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!MemoryBlocksAdd: No free memory block found (Total blocks=%u)\r\n", dwFirstEmpty));
        hRes = E_FAIL;
        goto Exit;
    }

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!MemoryBlocksAdd: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    MemoryAddAllVirtual

    Add all virtual memory sections for entire device.
----------------------------------------------------------------------------*/
static HRESULT MemoryAddAllVirtual(BOOL fWrite)
{
    HRESULT hRes = E_FAIL;
    DWORD dwMemoryStart = 0;
    DWORD dwMemorySize = 0;
    DWORD dwSection;
    MEMORYINFO *pMemoryInfo = reinterpret_cast<MEMORYINFO*> (g_pKData->aInfo[KINX_MEMINFO]);
    const ROMHDR *pTOC = g_OsaxsData.pRomHdr;
    fslog_t *pLogPtr = g_OsaxsData.pLogPtr;
    DWORD MainMemoryEndAddress;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!MemoryAddAllVirtual: Enter\r\n"));

    if (!pTOC)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!MemoryAddAllVirtual: FetchTOC returned NULL\r\n"));
        hRes = E_FAIL;
        goto Exit;
    }

    if (!pMemoryInfo)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!MemoryAddAllVirtual: FetchMemoryInfo returned NULL\r\n"));
        hRes = E_FAIL;
        goto Exit;
    }

    if (!pLogPtr)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!MemoryAddAllVirtual: FetchLogPtr returned NULL\r\n"));
        hRes = E_FAIL;
        goto Exit;
    }

#if defined(ARM)
    // Dump Arm FirstPT memory
    dwMemoryStart = (DWORD)ArmHigh->firstPT;
    dwMemorySize  = sizeof(ArmHigh->firstPT);

    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!MemoryAddAllVirtual: Adding Arm FirstPT memory block\r\n"));
    
    hRes = MemoryBlocksAdd(g_pprcNK, dwMemoryStart, dwMemorySize, VIRTUAL_MEMORY, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!MemoryAddAllVirtual: MemoryBlocksAdd failed adding Arm FirstPT memory block, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }
#endif

    // Dump Nk.bin Code memory
    dwMemoryStart = pTOC->physfirst;
    dwMemorySize  = pTOC->physlast - pTOC->physfirst;

    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!MemoryAddAllVirtual: Adding nk.bin memory block\r\n"));
    
    hRes = MemoryBlocksAdd(g_pprcNK, dwMemoryStart, dwMemorySize, VIRTUAL_MEMORY, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!MemoryAddAllVirtual: MemoryBlocksAdd failed adding nk.bin memory block, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Dump base RAM section
    KD_ASSERT(pLogPtr->fsmemblk[0].startptr == PAGEALIGN_UP(pTOC->ulRAMFree+MemForPT) + 4096);
    KD_ASSERT((pTOC->ulRAMStart <= pTOC->ulRAMFree) && (pTOC->ulRAMFree < pTOC->ulRAMEnd));
   
    MainMemoryEndAddress = pLogPtr->fsmemblk[0].startptr + pLogPtr->fsmemblk[0].extension + pLogPtr->fsmemblk[0].length;
    dwMemoryStart = pTOC->ulRAMStart;
    dwMemorySize  = MainMemoryEndAddress - pTOC->ulRAMStart;

    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!MemoryAddAllVirtual: Adding base RAM memory block\r\n"));
    
    hRes = MemoryBlocksAdd(g_pprcNK, dwMemoryStart, dwMemorySize, VIRTUAL_MEMORY, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!MemoryAddAllVirtual: MemoryBlocksAdd failed adding base RAM memory block, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Dump any extension RAM sections    
    for (dwSection = 1; dwSection < pMemoryInfo->cFi; ++ dwSection)
    {
        dwMemoryStart = (DWORD) (pLogPtr->fsmemblk[dwSection].startptr);
        dwMemorySize  = (DWORD) (pLogPtr->fsmemblk[dwSection].extension + pLogPtr->fsmemblk[dwSection].length);
        if (dwMemorySize > 0)
        {
            DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!MemoryAddAllVirtual: Adding extension RAM memory block%u\r\n", dwSection));
            
            hRes = MemoryBlocksAdd(g_pprcNK, dwMemoryStart, dwMemorySize, VIRTUAL_MEMORY, fWrite);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!MemoryAddAllVirtual: MemoryBlocksAdd failed adding extension RAM memory block%u, hRes=0x%08X\r\n", dwSection, hRes));
                goto Exit;
            }
        }
    }        

    hRes = S_OK;

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!MemoryAddAllVirtual: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    MemoryAddProcessHeap

    Add all R/W sections for passed in process.
----------------------------------------------------------------------------*/
static HRESULT MemoryAddProcessHeap(PPROCESS pProcess, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;
    DWORD MaxPDIndex = 0;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!MemoryAddProcessHeap: Enter\r\n"));

    KD_ASSERT(NULL != pProcess);
    if (NULL == pProcess)
    {
        hRes = E_INVALIDARG;
        goto Exit;
    }

    if (!OSAXSIsProcessValid(pProcess))
    {
        DEBUGGERMSG (OXZONE_ALERT, (L"  MemoryAddProcessHeap: Process 0x%08x not processed, bState = %d\r\n", pProcess->bState));
        hRes = S_OK;
        goto Exit;
    }

    if (!pProcess->ppdir)
    {
        DEBUGGERMSG (OXZONE_ALERT, (L"  MemoryAddProcessHeap: Process 0x%08x lacks a pagetable directory!\r\n", pProcess->bState));
        hRes = E_FAIL;
        goto Exit;
    }

    // If the scanned process is a usermode process, then this loop only needs to
    // iterate over 00000000 to 7FFFFFFF.  Stop the scan at the start of the
    // kernel mode base.  This is to avoid the ability of ARMV6 to automagically
    // switch to the kernel pagetable entries for VA with the MSB set.
    //
    // Otherwise, just scan the entire page directory.
    if (pProcess != g_pprcNK)
    {
        MaxPDIndex = VA2PDIDX(VM_KMODE_BASE);
    }
    else
    {
        MaxPDIndex = VM_NUM_PD_ENTRIES;
    }


    // Not a standard ++ on increment because ARM needs +=4 when traversing
    // the page directory entry array.
    for (DWORD ipd = 0; ipd < MaxPDIndex; ipd = NextPDEntry(ipd))
    {
        DWORD const pde = pProcess->ppdir->pte[ipd];
        if (IsPDEntryValid(pde))
        {
            if (IsSectionMapped (pde))
            {
                // Large page mapped
                DWORD pfn = PFNfromEntry (pde);
                if (IsPageReadable (pfn) && IsPageWritable (pfn))
                {
                    DWORD va = (ipd << VM_PD_SHIFT);
                    hRes = MemoryBlocksAdd (pProcess, va, VM_PD_SIZE, VIRTUAL_MEMORY, fWrite);
                    if (FAILED(hRes))
                    {
                        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!MemoryAddProcessHeap: MemoryBlocksAdd failed adding '%s' heap memory (large page), ProcessID=0x%08X, hRes=0x%08X\r\n", 
                                                  pProcess->lpszProcName, pProcess->dwId, hRes));
                        goto Exit;
                    }
                }
            }
            else
            {
                // Go to second level page table.
                PPAGETABLE ppt = Entry2PTBL (pde);
                if (ppt)
                {
                    for (DWORD ipt = 0; ipt < VM_NUM_PT_ENTRIES; ++ipt)
                    {
                        DWORD pfn = PFNfromEntry (ppt->pte[ipt]);
                        if (IsPageReadable (pfn) && IsPageWritable (pfn))
                        {
                            DWORD va = (ipd << VM_PD_SHIFT) | (ipt << VM_PT_SHIFT);
                            hRes = MemoryBlocksAdd (pProcess, va, VM_PAGE_SIZE, VIRTUAL_MEMORY, fWrite);
                            if (FAILED(hRes))
                            {
                                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!MemoryAddProcessHeap: MemoryBlocksAdd failed adding '%s' heap memory (normal page), ProcessID=0x%08X, hRes=0x%08X\r\n", 
                                                          pProcess->lpszProcName, pProcess->dwId, hRes));
                                goto Exit;
                            }
                        }
                    }
                }

            }
        }
    }

    hRes = S_OK;

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!MemoryAddProcessHeap: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    MemoryAddProcessGlobals

    Add the global memory sections for passed in process.
----------------------------------------------------------------------------*/
static HRESULT MemoryAddProcessGlobals(PPROCESS pProcess, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;


    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!MemoryAddProcessGlobals: Enter\r\n"));

    KD_ASSERT(NULL != pProcess);
    if (NULL == pProcess)
    {
        hRes = E_INVALIDARG;
        goto Exit;
    }

    if (pProcess == g_pprcNK)
    {
        if (TRUE == g_fDumpNkGlobals)
        {
            ROMHDR const *pTOC = g_OsaxsData.pRomHdr;
            if (pTOC)
            {
                COPYentry *pce = reinterpret_cast<COPYentry*> (pTOC->ulCopyOffset);
                if (pce)
                {
                    // Indicate that nk.exe globals dumped (optimization needs to know)
                    g_fNkGlobalsDumped = TRUE;
                    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!MemoryAddProcessGlobals: Adding '%s' global variable memory -> ProcessID=0x%08X\r\n", 
                                                 pProcess->lpszProcName, pProcess->dwId));
                    
                    hRes = MemoryBlocksAdd(g_pprcNK, pce->ulDest, pce->ulDestLen, VIRTUAL_MEMORY, fWrite);
                    if (FAILED(hRes))
                    {
                        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!MemoryAddProcessGlobals: MemoryBlocksAdd failed adding '%s' global variable memory -> ProcessID=0x%08X, hRes=0x%08X\r\n", 
                                                  pProcess->lpszProcName, pProcess->dwId, hRes));
                        goto Exit;
                    }
                }
            }
        }
    }
    else
    {
        // All other processes
        for (DWORD dwO32count=0; dwO32count < pProcess->e32.e32_objcnt; dwO32count++)
        {
            if (pProcess->o32_ptr && (pProcess->o32_ptr[dwO32count].o32_flags & IMAGE_SCN_MEM_WRITE))
            {
                // Found a writable memory section
                DWORD const dwMemoryStart = (DWORD) pProcess->o32_ptr[dwO32count].o32_realaddr;
                DWORD const dwMemorySize = pProcess->o32_ptr[dwO32count].o32_vsize;
                DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!MemoryAddProcessGlobals: Adding '%s' global variable memory%u -> ProcessID=0x%08X\r\n", 
                                             pProcess->lpszProcName, dwO32count, pProcess->dwId));
                
                hRes = MemoryBlocksAdd(pProcess, dwMemoryStart, dwMemorySize, VIRTUAL_MEMORY, fWrite);
                if (FAILED(hRes))
                {
                    DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!MemoryAddProcessGlobals: MemoryBlocksAdd failed adding '%s' global variable memory%u -> ProcessID=0x%08X, hRes=0x%08X\r\n", 
                                              pProcess->lpszProcName, dwO32count, pProcess->dwId, hRes));
                    goto Exit;
                }
            }
        }
    }

    hRes = S_OK;

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!MemoryAddProcessGlobals: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    MemoryAddDllGlobals

    Add the global memory sections for passed in process.
----------------------------------------------------------------------------*/
static HRESULT MemoryAddDllGlobals(PMODULE pMod, HPROCESS hFrameProcess, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;
    PPROCESS pProcess = NULL;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!MemoryAddDllGlobals: Enter\r\n"));

    KD_ASSERT(pMod);
    if (!pMod)
    {
        hRes = E_INVALIDARG;
        goto Exit;
    }

    pProcess = OSAXSHandleToProcess (hFrameProcess);
    if (!pProcess)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!MemoryAddDllGlobals: HandleToProc returned NULL\r\n"));
        hRes = E_FAIL;
        goto Exit;
    }

    for (DWORD dwO32count=0; dwO32count < pMod->e32.e32_objcnt; dwO32count++)
    {
        if (pMod->o32_ptr && (pMod->o32_ptr[dwO32count].o32_flags & IMAGE_SCN_MEM_WRITE))
        {
            // Found a writable memory section
            DWORD const dwMemoryStart = (DWORD) pMod->o32_ptr[dwO32count].o32_realaddr;
            DWORD const dwMemorySize = pMod->o32_ptr[dwO32count].o32_vsize;
            DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!MemoryAddDllGlobals: Adding '%s' global variable memory%u for '%s'\r\n", 
                                         pMod->lpszModName, dwO32count, pProcess->lpszProcName));
            
            hRes = MemoryBlocksAdd(pProcess, dwMemoryStart, dwMemorySize, VIRTUAL_MEMORY, fWrite);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!MemoryAddDllGlobals: MemoryBlocksAdd failed adding '%s' global variable memory%u for '%s', hRes=0x%08X\r\n", 
                                          pMod->lpszModName, dwO32count, pProcess->lpszProcName, hRes));
                goto Exit;
            }
        }
    }

    hRes = S_OK;

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!MemoryAddDllGlobals: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}


static HRESULT DWAddAnyGlobal (DWORD ReturnAddress, HANDLE hProc, BOOL fWrite)
{
    HRESULT hr;
    PROCESS *pProcess;

    pProcess = OSAXSHandleToProcess (hProc);
    if (!pProcess)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DWAddAnyGlobal: Invalid Process Handle 0x%08x\r\n", hProc));
        hr = E_FAIL;
        goto Exit;
    }

    if (ReturnAddress < VM_DLL_BASE)
    {
        DWORD const ExeBase = reinterpret_cast<DWORD> (pProcess->BasePtr);
        DWORD const ExeSize = pProcess->e32.e32_vsize;
        DWORD const ExeEnd  = ExeBase + ExeSize;

        if (ExeBase <= ReturnAddress && ReturnAddress < ExeEnd && ExeBase < ExeEnd)
        {
            hr = MemoryAddProcessGlobals (pProcess, fWrite);
            if (FAILED (hr))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DWAddAnyGlobal: MemoryAddProcessGlobals failed adding Process Global memory, hRes=0x%08X\r\n", hr));
                goto Exit;
            }

            hr = S_OK;
            goto Exit;
        }
    }

    PMODULELIST pModList = reinterpret_cast<PMODULELIST> (g_pprcNK->modList.pFwd);
    while (pModList && pModList != reinterpret_cast<PMODULELIST> (&g_pprcNK->modList))
    {
        if (pModList->pMod && pModList->pMod == pModList->pMod->lpSelf)
        {
            DWORD const DllBase = reinterpret_cast<DWORD> (pModList->pMod->BasePtr);
            DWORD const DllSize = pModList->pMod->e32.e32_vsize;
            DWORD const DllEnd  = DllBase + DllSize;

            if (DllBase <= ReturnAddress && ReturnAddress < DllEnd && DllBase < DllEnd)
            {
                hr = MemoryAddDllGlobals (pModList->pMod, hProc, fWrite);
                if (FAILED (hr))
                {
                    DEBUGGERMSG(OXZONE_ALERT,(L"  DWAddAnyGlobal: MemoryAddDllGlobals failed adding Module Global memory, hRes=0x%08X\r\n", hr));
                    goto Exit;
                }

                hr = S_OK;
                goto Exit;
            }
        }

        pModList = reinterpret_cast<PMODULELIST> (pModList->link.pFwd);
    }

    DEBUGGERMSG(OXZONE_ALERT,(L"  DWAddAnyGlobal: No global memory added for Return Address=0x%08X\r\n", ReturnAddress));
    // Don't fail, just display the alert
    hr = S_OK;

Exit:
    return hr;
}


/*----------------------------------------------------------------------------
    MemoryAddGlobals

    Locate the process or module associated with the Return Address and
    add the global memory for that process or module.
----------------------------------------------------------------------------*/
static HRESULT MemoryAddGlobals(CEDUMP_TYPE ceDumpType, DWORD dwReturnAddress, HPROCESS hFrameProcess, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!MemoryAddGlobals: Enter\r\n"));

    switch (ceDumpType)
    {
    case ceDumpTypeComplete:
    case ceDumpTypeContext:
        // No globals added for these yet
        hRes = S_OK;
        break;
        
    case ceDumpTypeSystem:
        // Add global for any module or process found that matches Return Address
        hRes = DWAddAnyGlobal (dwReturnAddress, hFrameProcess, fWrite);
        break;
        
    default:
        // This should not happen
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!MemoryAddGlobals: Bad Dump Type detected, ceDumpType=%u\r\n", ceDumpType));
        KD_ASSERT(FALSE); 
        hRes = E_FAIL;
        goto Exit;
    }

Exit:
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!MemoryAddGlobals: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}


/*----------------------------------------------------------------------------
    MemoryAddIPCode

    Add code surrounding the Instruction Pointer (IP). Limit the address 
    range to pages that are currently paged in.
----------------------------------------------------------------------------*/
static HRESULT MemoryAddIPCode(DWORD dwMemoryIPBefore, DWORD dwMemoryIPAfter, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;
    DWORD dwMemoryStart;
    DWORD dwMemorySize;
    DWORD dwMemoryEnd;
    DWORD dwIP;
    DWORD dwIPCheck;
    PROCESS *VMProcess;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!MemoryAddIPCode: Enter\r\n"));

    if (g_pDmpThread == pCurThread)
    {
        dwIP = CONTEXT_TO_PROGRAM_COUNTER (g_pexceptionInfo->ExceptionPointers.ContextRecord);
        dwIP = (DWORD) dwIP;
    }
    else
    {
        dwIP = GetThreadIP (g_pDmpThread);
        dwIP = (DWORD) dwIP;
    }

    if (dwIP >= VM_KMODE_BASE)
    {
        VMProcess = g_pprcNK;
    }
    else
    {
        VMProcess = g_pDmpThread->pprcVM;
    }
    
    // Address range wanted
    dwMemoryStart = dwIP - dwMemoryIPBefore;
    dwMemorySize = dwMemoryIPBefore + dwMemoryIPAfter;
    dwMemoryEnd = dwMemoryStart + dwMemorySize - 1;

    // Find lower limit for which range is valid
    dwIPCheck = dwIP;

    while (dwIPCheck >= dwMemoryStart)
    {
        if (NULL != MapToDebuggeeCtxKernEquivIfAcc (VMProcess, (PVOID)dwIPCheck, TRUE /* Probe Only */))
        {
            -- dwIPCheck;
        }
        else
        {
            dwMemoryStart = dwIPCheck+1;
        }
    }

    // Find upper limit for which range is valid
    dwIPCheck = dwIP;

    while (dwIPCheck <= dwMemoryEnd)
    {
        if (NULL != MapToDebuggeeCtxKernEquivIfAcc (VMProcess, (PVOID)dwIPCheck, TRUE /* Probe Only */))
        {
            ++ dwIPCheck;
        }
        else
        {
            dwMemoryEnd = dwIPCheck-1;
        }
    }

    if (dwMemoryStart <= dwMemoryEnd)
    {
        dwMemorySize = dwMemoryEnd - dwMemoryStart + 1;
        
        DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!MemoryAddIPCode: Add memory surrounding Instruction Pointer -> IP=0x%08X\r\n", dwIP));
        
        hRes = MemoryBlocksAdd(VMProcess, dwMemoryStart, dwMemorySize, VIRTUAL_MEMORY, fWrite);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!MemoryAddIPCode: MemoryBlocksAdd failed adding memory surrounding Instruction Pointer, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
    }

    hRes = S_OK;

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!MemoryAddIPCode: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}


static HRESULT __forceinline DWAlignFile (DWORD &FileOffset, BOOL fWrite)
{
    DWORD Alignment = FileOffset % CEDUMP_ALIGNMENT;
    if (Alignment > 0)
    {
        Alignment = CEDUMP_ALIGNMENT - Alignment;
        if (fWrite)
        {
            HRESULT hr = WatsonWriteData(FileOffset, &g_bBufferAlignment, Alignment, TRUE);
            if (FAILED (hr))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamThreadCallStackList: WatsonWriteData failed writing %u alignment bytes, hRes=0x%08X\r\n", Alignment, hr));
                return hr;
            }
        }
        FileOffset += Alignment;
    }
    return S_OK;
}


/*----------------------------------------------------------------------------
    WriteCeHeader

    Write the dump file header.
----------------------------------------------------------------------------*/
static HRESULT WriteCeHeader(DWORD dwNumberOfStreams, CEDUMP_TYPE ceDumpType, DWORD *pdwFileOffset, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteCeHeader: Enter\r\n"));

    g_ceDmpHeader.NumberOfStreams = dwNumberOfStreams;
    g_ceDmpHeader.StreamDirectoryRva = *pdwFileOffset + sizeof(MINIDUMP_HEADER);

    if (fWrite)
    {
        switch(ceDumpType)
        {
            case ceDumpTypeComplete: 
                g_ceDmpHeader.Signature = CEDUMP_SIGNATURE_COMPLETE; 
                break;
            case ceDumpTypeSystem:
                g_ceDmpHeader.Signature = CEDUMP_SIGNATURE_SYSTEM; 
                break;
            case ceDumpTypeContext:
                g_ceDmpHeader.Signature = CEDUMP_SIGNATURE_CONTEXT; 
                break;
            case ceDumpTypeUndefined:
            default:
                // This should not happen
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeHeader: Bad Dump Type detected, ceDumpType=%u\r\n", ceDumpType));
                KD_ASSERT(FALSE); 
                hRes = E_FAIL;
                goto Exit;
        }
        g_ceDmpHeader.Version = CEDUMP_VERSION;

        hRes = WatsonWriteData(*pdwFileOffset, &g_ceDmpHeader, sizeof(MINIDUMP_HEADER), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeHeader: WatsonWriteData failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
    }

    *pdwFileOffset += sizeof(MINIDUMP_HEADER);

    hRes = S_OK;

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteCeHeader: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteCeDirectory

    Write the dump file directory.
----------------------------------------------------------------------------*/
static HRESULT WriteCeDirectory(DWORD *pdwFileOffset, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteCeDirectory: Enter\r\n"));

    // Make sure stream is correctly aligned
    KD_ASSERT(((*pdwFileOffset)%CEDUMP_ALIGNMENT) == 0);
    if (((*pdwFileOffset)%CEDUMP_ALIGNMENT) != 0)
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeDirectory: File offset (%u) not %u byte aligned\r\n",
                                  *pdwFileOffset, CEDUMP_ALIGNMENT));
        hRes = E_FAIL;
        goto Exit;
    }

    if (fWrite)
    {
        hRes = WatsonWriteData(*pdwFileOffset, &g_ceDmpDirectory, (g_ceDmpHeader.NumberOfStreams * sizeof(MINIDUMP_DIRECTORY)), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeDirectory: WatsonWriteData failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
    }

    *pdwFileOffset += (g_ceDmpHeader.NumberOfStreams * sizeof(MINIDUMP_DIRECTORY));

    hRes = DWAlignFile (*pdwFileOffset, fWrite);

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteCeDirectory: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteCeStreamElementList

    Write the Element List Stream to the dump file.
    This is used to write out the list of Modules, Process, or Threads.
----------------------------------------------------------------------------*/
static HRESULT WriteCeStreamElementList(DWORD dwStreamType, CEDUMP_TYPE ceDumpType, MINIDUMP_DIRECTORY *pceDmpDirectory, DWORD *pdwFileOffset, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;
    DWORD dwWriteOffset;
    DWORD dwFieldInfoCount;
    DWORD dwElementCount;
    DWORD dwElementSize = 0;
    PCEDUMP_ELEMENT_LIST pceDmpElementList = NULL;
    PCEDUMP_FIELD_INFO pceDmpFieldInfo = NULL;
    FLEXI_REQUEST flexiRequest = {0};
    FLEXI_RANGE*  pflexiRange = NULL;
    DWORD dwRequestHdr = 0;
    DWORD dwRequestDesc = 0;
    DWORD dwRequestData = 0;
    DWORD dwRequestFilter = 0;
    DWORD dwFieldStringsSize = 0;
    DWORD dwFieldInfoTotalSize = 0;
    DWORD dwFlexiSizeRead = 0;
    PVOID pElementData = NULL;
    BOOL  fExceptionContext = FALSE;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteCeStreamElementList: Enter\r\n"));

    // Make sure stream is correctly aligned
    KD_ASSERT(((*pdwFileOffset)%CEDUMP_ALIGNMENT) == 0);
    if (((*pdwFileOffset)%CEDUMP_ALIGNMENT) != 0)
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamElementList: File offset (%u) not %u byte aligned\r\n",
                                  *pdwFileOffset, CEDUMP_ALIGNMENT));
        hRes = E_FAIL;
        goto Exit;
    }

    // On write pass *pdwFileOffset should match Location.Rva
    KD_ASSERT(fWrite ? pceDmpDirectory->Location.Rva == *pdwFileOffset : TRUE);
    if (fWrite && (pceDmpDirectory->Location.Rva != *pdwFileOffset))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamElementList: File offset (%u) on write pass does not match expected value (%u)\r\n",
                                  *pdwFileOffset, pceDmpDirectory->Location.Rva));
        hRes = E_FAIL;
        goto Exit;
    }

    switch (ceDumpType)
    {
        case ceDumpTypeSystem:
            // Include data for all process, threads, and modules
            dwRequestFilter = 0;
            break;
            
        case ceDumpTypeComplete:
            // ** Fall through **
        case ceDumpTypeContext:
            if (ceStreamThreadContextList == dwStreamType)
            {
                // Only include context of dump thread
                dwRequestFilter = FLEXI_FILTER_THREAD_HANDLE;
                flexiRequest.dwHThread = (DWORD)g_pDmpThread->dwId;
            }
            else
            {
                // Only include owner process, threads for owner process, and modules for owner process
                dwRequestFilter = FLEXI_FILTER_PROCESS_HANDLE | FLEXI_FILTER_INC_PROC;
                flexiRequest.dwHProc = (DWORD)g_pDmpThread->pprcOwner->dwId;
                // Also include the VM proc.
                if (g_pDmpThread->pprcVM)
                {
                    flexiRequest.dwHMod = (DWORD)g_pDmpThread->pprcVM->dwId;
                }
            }
            break;
            
        default:
            // This should not happen
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: Bad Dump Type detected, ceDumpType=%u\r\n", ceDumpType));
            KD_ASSERT(FALSE); 
            hRes = E_FAIL;
            goto Exit;
    }

    switch (dwStreamType)
    {
        case ceStreamModuleList:
            DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteCeStreamElementList: Generating Module List\r\n"));
            pceDmpElementList = &g_ceDmpModuleList;
            dwRequestHdr  = FLEXI_REQ_MODULE_HDR;
            dwRequestDesc = FLEXI_REQ_MODULE_DESC;
            dwRequestData = FLEXI_REQ_MODULE_DATA;
            flexiRequest.Mod.dwStart = 0;
            flexiRequest.Mod.dwEnd   = -1;
            pflexiRange = &flexiRequest.Mod;
            // Always include nk.exe and dump thread's current vm process in modules list, even for context dumps
            dwRequestFilter |= (FLEXI_FILTER_INC_NK | FLEXI_FILTER_INC_PROC);
            if (g_pDmpThread->pprcVM)
            {
                flexiRequest.dwHMod = (DWORD)g_pDmpThread->pprcVM->dwId;
            }
            break;

        case ceStreamProcessList:
            DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteCeStreamElementList: Generating Process List\r\n"));
            pceDmpElementList = &g_ceDmpProcessList;
            dwRequestHdr  = FLEXI_REQ_PROCESS_HDR;
            dwRequestDesc = FLEXI_REQ_PROCESS_DESC;
            dwRequestData = FLEXI_REQ_PROCESS_DATA;
            flexiRequest.Proc.dwStart = 0;
            flexiRequest.Proc.dwEnd   = -1;
            pflexiRange = &flexiRequest.Proc;
            // Always include nk.exe and dump thread's current vm process in process list, even for context dumps
            dwRequestFilter |= (FLEXI_FILTER_INC_NK | FLEXI_FILTER_INC_PROC);
            if (g_pDmpThread->pprcVM)
            {
                flexiRequest.dwHMod = (DWORD)g_pDmpThread->pprcVM->dwId;
            }
            break;

        case ceStreamThreadList:
            DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteCeStreamElementList: Generating Thread List\r\n"));
            pceDmpElementList = &g_ceDmpThreadList;
            dwRequestHdr  = FLEXI_REQ_THREAD_HDR;
            dwRequestDesc = FLEXI_REQ_THREAD_DESC;
            dwRequestData = FLEXI_REQ_THREAD_DATA;
            flexiRequest.Proc.dwStart = 0;
            flexiRequest.Proc.dwEnd   = -1;
            flexiRequest.Thread.dwStart = 0;
            flexiRequest.Thread.dwEnd   = -1;
            pflexiRange = &flexiRequest.Thread;
            break;

        case ceStreamThreadContextList:
            DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteCeStreamElementList: Generating Thread Context List\r\n"));
            pceDmpElementList = &g_ceDmpThreadContextList;
            dwRequestHdr  = FLEXI_REQ_CONTEXT_HDR;
            dwRequestDesc = FLEXI_REQ_CONTEXT_DESC;
            dwRequestData = FLEXI_REQ_CONTEXT_DATA;
            flexiRequest.Proc.dwStart = 0;
            flexiRequest.Proc.dwEnd   = -1;
            flexiRequest.Thread.dwStart = 0;
            flexiRequest.Thread.dwEnd   = -1;
            pflexiRange = &flexiRequest.Thread;
            break;

        case ceStreamProcessModuleMap:
            DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  WriteCeStreamElementList: Generating Process-Module Map\r\n"));
            pceDmpElementList         = &g_ceDmpProcessModuleList;
            dwRequestHdr              = FLEXI_REQ_MODPROC_HDR;
            dwRequestDesc             = FLEXI_REQ_MODPROC_DESC;
            dwRequestData             = FLEXI_REQ_MODPROC_DATA;
            flexiRequest.Proc.dwStart = 0;
            flexiRequest.Proc.dwEnd   = -1;
            flexiRequest.Mod.dwStart  = 0;
            flexiRequest.Mod.dwEnd    = -1;
            pflexiRange = &flexiRequest.Mod;
            break;

        case ceStreamException:
            DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteCeStreamElementList: Generating Thread Context for Exception\r\n"));
            pceDmpElementList = &g_ceDmpExceptionContext;
            if (g_pDmpThread == pCurThread)
            {
                // Write exception context when current thread (even if CaptureDumpFileOnDevice called)
                dwRequestHdr  = GET_CTX_HDR;
                dwRequestDesc = GET_CTX_DESC;
                dwRequestData = GET_CTX;
                pflexiRange = &flexiRequest.Thread;
                fExceptionContext = TRUE;
            }
            else
            {
                KD_ASSERT(g_fCaptureDumpFileOnDeviceCalled);
                // Write context data for focus thread when CaptureDumpFileOnDevice called
                dwRequestHdr  = FLEXI_REQ_CONTEXT_HDR;
                dwRequestDesc = FLEXI_REQ_CONTEXT_DESC;
                dwRequestData = FLEXI_REQ_CONTEXT_DATA;
                flexiRequest.Proc.dwStart = 0;
                flexiRequest.Proc.dwEnd   = -1;
                flexiRequest.Thread.dwStart = 0;
                flexiRequest.Thread.dwEnd   = -1;
                pflexiRange = &flexiRequest.Thread;
                // Only include context of dump thread
                dwRequestFilter = FLEXI_FILTER_THREAD_HANDLE;
                flexiRequest.dwHThread = (DWORD)g_pDmpThread->dwId;
            }
            break;
            
        default:
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: Unknown stream type=0x%08X\r\n", dwStreamType));
            hRes = E_FAIL;
            goto Exit;
    }

    KD_ASSERT((NULL != pceDmpElementList) && (NULL != pflexiRange));
    if ((NULL == pceDmpElementList) || (NULL == pflexiRange))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: Pointer to element list or flexi range not initialized, dwStreamType=0x%08X\r\n", dwStreamType));
        hRes = E_FAIL;
        goto Exit;
    }

    // Get the number of Elements & field info from FPTMI
    flexiRequest.dwRequest = dwRequestHdr | dwRequestDesc | dwRequestFilter;
    dwFlexiSizeRead = sizeof(g_bBuffer);

    if (!fExceptionContext)
    {
        hRes = GetFPTMI(&flexiRequest, &dwFlexiSizeRead, g_bBuffer);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: GetFPTMI failed getting header info, hRes=0x%08X, dwStreamType=0x%08X\r\n", hRes, dwStreamType));
            goto Exit;
        }
    }
    else
    {
        hRes = GetExceptionContext(flexiRequest.dwRequest, &dwFlexiSizeRead, g_bBuffer);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: GetExceptionContext failed getting header info, hRes=0x%08X, dwStreamType=0x%08X\r\n", hRes, dwStreamType));
            goto Exit;
        }
    }

    // Transfer header info to element list struct
    memcpy(pceDmpElementList, g_bBuffer, sizeof(CEDUMP_ELEMENT_LIST));

    // Make sure header info is correct
    if ((pceDmpElementList->SizeOfHeader != sizeof(CEDUMP_ELEMENT_LIST)) ||
        (pceDmpElementList->SizeOfFieldInfo != sizeof(CEDUMP_FIELD_INFO)))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: Element list SizeOfHeader (%u vs %u) or SizeOfFieldInfo (%u vs %u) is incorrect\r\n", 
                                 pceDmpElementList->SizeOfHeader, 
                                 sizeof(CEDUMP_ELEMENT_LIST),
                                 pceDmpElementList->SizeOfFieldInfo,
                                 sizeof(CEDUMP_FIELD_INFO)));
        hRes = E_FAIL;
        goto Exit;
    }

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteCeStreamElementList: Number of FieldInfo=%u, Number of Elements=%u\r\n", 
                             pceDmpElementList->NumberOfFieldInfo, 
                             pceDmpElementList->NumberOfElements));

    // Init the directory info
    pceDmpDirectory->StreamType        = dwStreamType;
    pceDmpDirectory->Location.DataSize = sizeof(CEDUMP_ELEMENT_LIST) + pceDmpElementList->NumberOfFieldInfo * sizeof(CEDUMP_FIELD_INFO);
    pceDmpDirectory->Location.Rva      = *pdwFileOffset;

    // Fixup the Field Info RVAs & calculate element size
    pceDmpFieldInfo = (PCEDUMP_FIELD_INFO)&g_bBuffer[sizeof(CEDUMP_ELEMENT_LIST)];
    
    for (dwFieldInfoCount = 0; dwFieldInfoCount < pceDmpElementList->NumberOfFieldInfo; ++dwFieldInfoCount)
    {
        // Check to make sure the Field info is within the buffer range
        if ((BYTE*)(pceDmpFieldInfo + 1) > ((BYTE*)g_bBuffer + sizeof(g_bBuffer)))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: FieldInfo pointer is beyond buffer, FieldInfoCount=%u, NumberOfFieldInfo=%u\r\n", 
                                     dwFieldInfoCount, pceDmpElementList->NumberOfFieldInfo));
            hRes = E_FAIL;
            goto Exit;
        }

        // Check to make sure the FieldLabel RVA is in the expected location
        if (pceDmpFieldInfo->FieldLabel != (dwFieldStringsSize + pceDmpDirectory->Location.DataSize))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: FieldLabel RVA (%u) does not match expected RVA (%u), FieldInfoCount=%u\r\n", 
                                     pceDmpFieldInfo->FieldLabel, (dwFieldStringsSize + pceDmpDirectory->Location.DataSize), dwFieldInfoCount));
            hRes = E_FAIL;
            goto Exit;
        }

        // Make sure the FieldLabel RVA is within the buffer
        if (pceDmpFieldInfo->FieldLabel >= sizeof(g_bBuffer))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: FieldLabel RVA (%u) points beyond end of buffer (%u)\r\n", 
                                     pceDmpFieldInfo->FieldLabel, sizeof(g_bBuffer)));
            hRes = E_FAIL;
            goto Exit;
        }
        dwFieldStringsSize += 4 + ((wcslen((WCHAR*)&g_bBuffer[pceDmpFieldInfo->FieldLabel+4]) + 1) * sizeof(WCHAR));
        pceDmpFieldInfo->FieldLabel += pceDmpDirectory->Location.Rva;
        
        // Check to make sure the FieldFormat RVA is in the expected location
        if (pceDmpFieldInfo->FieldFormat != (dwFieldStringsSize + pceDmpDirectory->Location.DataSize))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: FieldFormat RVA (%u) does not match expected RVA (%u), FieldInfoCount=%u\r\n", 
                                     pceDmpFieldInfo->FieldFormat, (dwFieldStringsSize + pceDmpDirectory->Location.DataSize), dwFieldInfoCount));
            hRes = E_FAIL;
            goto Exit;
        }

        // Make sure the FieldFormat RVA is within the buffer
        if (pceDmpFieldInfo->FieldFormat >= sizeof(g_bBuffer))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: FieldFormat RVA (%u) points beyond end of buffer (%u)\r\n", 
                                     pceDmpFieldInfo->FieldFormat, sizeof(g_bBuffer)));
            hRes = E_FAIL;
            goto Exit;
        }
        dwFieldStringsSize += 4 + ((wcslen((WCHAR*)&g_bBuffer[pceDmpFieldInfo->FieldFormat+4]) + 1) * sizeof(WCHAR));
        pceDmpFieldInfo->FieldFormat += pceDmpDirectory->Location.Rva;

        // Increment the element size with the size of the field
        dwElementSize += pceDmpFieldInfo->FieldSize;
        
        pceDmpFieldInfo += 1;
    }

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteCeStreamElementList: Size of each Element=%u\r\n", dwElementSize));

    KD_ASSERT(dwFlexiSizeRead == (pceDmpDirectory->Location.DataSize + dwFieldStringsSize));
    
    // Write out the Element list header info
    if (fWrite)
    {
        dwWriteOffset = 0;
        
        // Write out Element List Stream header
        pceDmpElementList->Elements = pceDmpDirectory->Location.Rva + pceDmpDirectory->Location.DataSize + dwFieldStringsSize;
        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, pceDmpElementList, sizeof(CEDUMP_ELEMENT_LIST), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: WatsonWriteData failed writing Element List Stream header, hRes=0x%08X, dwStreamType=0x%08X\r\n", hRes, dwStreamType));
            goto Exit;
        }
        dwWriteOffset += sizeof(CEDUMP_ELEMENT_LIST);

        // Write out Element List Field Info descriptors
        pceDmpFieldInfo = (PCEDUMP_FIELD_INFO)&g_bBuffer[sizeof(CEDUMP_ELEMENT_LIST)];
        dwFieldInfoTotalSize = (pceDmpElementList->NumberOfFieldInfo * sizeof(CEDUMP_FIELD_INFO)) + dwFieldStringsSize;

        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, pceDmpFieldInfo, dwFieldInfoTotalSize, TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: WatsonWriteData failed writing Element Field Info, hRes=0x%08X, dwStreamType=0x%08X\r\n", hRes, dwStreamType));
            goto Exit;
        }
        dwWriteOffset += dwFieldInfoTotalSize;

        KD_ASSERT(dwWriteOffset == (pceDmpDirectory->Location.DataSize + dwFieldStringsSize));
    }

    *pdwFileOffset += (pceDmpDirectory->Location.DataSize + dwFieldStringsSize);

    // Write out the list of Elements
    if (TRUE == fWrite)
    {
        // Make sure the element data will fit in the buffer
        if (dwElementSize > sizeof(g_bBuffer))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: Element size (%u) is larger than buffer (%u), dwStreamType=0x%08X\r\n", 
                                     dwElementSize, sizeof(g_bBuffer), dwStreamType));
            hRes = E_FAIL;
            goto Exit;
        }
        
        dwWriteOffset = 0;

        // Write out list of Elements
        for (dwElementCount = 0; dwElementCount < pceDmpElementList->NumberOfElements; ++dwElementCount)
        {
            // Get the number of Elements & field info from FPTMI
            flexiRequest.dwRequest = dwRequestHdr | dwRequestData | dwRequestFilter;
            dwFlexiSizeRead = sizeof(g_bBuffer);
            
            if (!fExceptionContext)
            {
                pflexiRange->dwStart = dwElementCount;
                pflexiRange->dwEnd   = dwElementCount;

                hRes = GetFPTMI(&flexiRequest, &dwFlexiSizeRead, g_bBuffer);
                if (FAILED(hRes))
                {
                    DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: GetFPTMI failed getting element data, hRes=0x%08X, dwStreamType=0x%08X, Element=%u\r\n", hRes, dwStreamType, dwElementCount));
                    goto Exit;
                }
            }
            else
            {
                hRes = GetExceptionContext(flexiRequest.dwRequest, &dwFlexiSizeRead, g_bBuffer);
                if (FAILED(hRes))
                {
                    DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: GetExceptionContext failed getting element data, hRes=0x%08X, dwStreamType=0x%08X\r\n", hRes, dwStreamType));
                    goto Exit;
                }
            }

            KD_ASSERT(dwFlexiSizeRead == dwElementSize + sizeof(CEDUMP_ELEMENT_LIST));

            pElementData = (PVOID)&g_bBuffer[sizeof(CEDUMP_ELEMENT_LIST)];
            hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, pElementData, dwElementSize, TRUE);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamElementList: WatsonWriteData failed writing list of Elements, hRes=0x%08X, dwStreamType=0x%08X\r\n", hRes, dwStreamType));
                goto Exit;
            }
            dwWriteOffset += dwElementSize;
        }

        KD_ASSERT(dwWriteOffset == (pceDmpElementList->NumberOfElements * dwElementSize));
    }

    *pdwFileOffset += (pceDmpElementList->NumberOfElements * dwElementSize);

    hRes = DWAlignFile (*pdwFileOffset, fWrite);

Exit:
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteCeStreamElementList: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteCeStreamSystemInfo

    Write the System Information Stream to the dump file.
----------------------------------------------------------------------------*/
static HRESULT WriteCeStreamSystemInfo(MINIDUMP_DIRECTORY *pceDmpDirectory, DWORD *pdwFileOffset, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;
    SYSTEM_INFO sysi;
    RVA rvaData;
    DWORD dwWriteOffset;
    const ROMHDR *pTOC = g_OsaxsData.pRomHdr;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteCeStreamSystemInfo: Enter\r\n"));

    // Make sure stream is correctly aligned
    KD_ASSERT(((*pdwFileOffset)%CEDUMP_ALIGNMENT) == 0);
    if (((*pdwFileOffset)%CEDUMP_ALIGNMENT) != 0)
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamSystemInfo: File offset (%u) not %u byte aligned\r\n",
                                  *pdwFileOffset, CEDUMP_ALIGNMENT));
        hRes = E_FAIL;
        goto Exit;
    }

    // On write pass *pdwFileOffset should match Location.Rva
    KD_ASSERT(fWrite ? pceDmpDirectory->Location.Rva == *pdwFileOffset : TRUE);
    if (fWrite && (pceDmpDirectory->Location.Rva != *pdwFileOffset))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamSystemInfo: File offset (%u) on write pass does not match expected value (%u)\r\n",
                                  *pdwFileOffset, pceDmpDirectory->Location.Rva));
        hRes = E_FAIL;
        goto Exit;
    }

    if (!pTOC)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamSystemInfo: FetchTOC returned NULL\r\n"));
        hRes = E_FAIL;
        goto Exit;
    }

    pceDmpDirectory->StreamType        = ceStreamSystemInfo;
    pceDmpDirectory->Location.DataSize = sizeof(CEDUMP_SYSTEM_INFO);
    pceDmpDirectory->Location.Rva      = *pdwFileOffset;

    // Location for first RVA Data
    rvaData = pceDmpDirectory->Location.Rva + pceDmpDirectory->Location.DataSize;

    // Setup OEM String RVA if valid
    if (g_dwOEMStringSize >= sizeof(WCHAR))
    {
        // Setup OEM String RVA 
        g_ceDmpSystemInfo.OEMStringRva = rvaData;
        rvaData += sizeof(g_dwOEMStringSize) + g_dwOEMStringSize;
    }
    else
    {
        g_ceDmpSystemInfo.OEMStringRva = 0;
    }

    // Setup PlatformType String RVA if valid
    if (g_dwPlatformTypeSize >= sizeof(WCHAR))
    {
        g_ceDmpSystemInfo.PlatformTypeRva = rvaData;
        rvaData += sizeof(g_dwPlatformTypeSize) + g_dwPlatformTypeSize;
    }
    else
    {
        g_ceDmpSystemInfo.PlatformTypeRva = 0;
    }

    // Setup PlatformVersion Data RVA if valid
    if (g_dwPlatformVersionSize)
    {
        g_ceDmpSystemInfo.Platforms = g_dwPlatformVersionSize / sizeof(PLATFORMVERSION);
        g_ceDmpSystemInfo.PlatformVersion = rvaData;
        rvaData += g_dwPlatformVersionSize;
    }
    else
    {
        g_ceDmpSystemInfo.Platforms = 0;
        g_ceDmpSystemInfo.PlatformVersion = 0;
    }
    
    if (fWrite)
    {
        dwWriteOffset = 0;
        
        GetSystemInfo (&sysi);
        g_ceDmpSystemInfo.SizeOfHeader          = sizeof(CEDUMP_SYSTEM_INFO);
        g_ceDmpSystemInfo.ProcessorArchitecture = sysi.wProcessorArchitecture;
        g_ceDmpSystemInfo.NumberOfProcessors    = sysi.dwNumberOfProcessors;
        g_ceDmpSystemInfo.ProcessorType         = sysi.dwProcessorType;
        g_ceDmpSystemInfo.ProcessorLevel        = sysi.wProcessorLevel;
        g_ceDmpSystemInfo.ProcessorRevision     = sysi.wProcessorRevision;
        g_ceDmpSystemInfo.ProcessorFamily       = TARGET_CODE_CPU;
        g_ceDmpSystemInfo.MajorVersion          = CE_MAJOR_VER;
        g_ceDmpSystemInfo.MinorVersion          = CE_MINOR_VER;
        g_ceDmpSystemInfo.BuildNumber           = CE_BUILD_VER;
        g_ceDmpSystemInfo.PlatformId            = VER_PLATFORM_WIN32_CE; 
        g_ceDmpSystemInfo.LCID                  = (UserKInfo[KINX_NLS_SYSLOC]);
        g_ceDmpSystemInfo.Machine               = pTOC->usCPUType;
        g_ceDmpSystemInfo.InstructionSet        = g_OsaxsData.dwCEInstructionSet;
        g_ceDmpSystemInfo.PtrKData              = (DWORD)g_pKData;

        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &g_ceDmpSystemInfo, sizeof(CEDUMP_SYSTEM_INFO), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamSystemInfo: WatsonWriteData failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += pceDmpDirectory->Location.DataSize;

        if (g_ceDmpSystemInfo.OEMStringRva)
        {
            DWORD dwOEMStringSizeBStr = g_dwOEMStringSize - sizeof(WCHAR);
            
            // Write OEM string size without terminating NULL
            hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &dwOEMStringSizeBStr, sizeof(dwOEMStringSizeBStr), TRUE);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamSystemInfo: WatsonWriteData failed writing OEM string size, hRes=0x%08X\r\n", hRes));
                goto Exit;
            }
            dwWriteOffset += sizeof(dwOEMStringSizeBStr);

            // Write OEM string
            hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, g_wzOEMString, g_dwOEMStringSize, TRUE);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamSystemInfo: WatsonWriteData failed writing OEM string, hRes=0x%08X\r\n", hRes));
                goto Exit;
            }
            dwWriteOffset += g_dwOEMStringSize;
        }


        if (g_ceDmpSystemInfo.PlatformTypeRva)
        {
            DWORD dwPlatformTypeSizeBStr = g_dwPlatformTypeSize - sizeof(WCHAR);

            // Write PlatformType string size without terminating NULL
            hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &dwPlatformTypeSizeBStr, sizeof(dwPlatformTypeSizeBStr), TRUE);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamSystemInfo: WatsonWriteData failed writing PlatformType string size, hRes=0x%08X\r\n", hRes));
                goto Exit;
            }
            dwWriteOffset += sizeof(dwPlatformTypeSizeBStr);

            // Write PlatformType string
            hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, g_wzPlatformType, g_dwPlatformTypeSize, TRUE);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamSystemInfo: WatsonWriteData failed writing PlatformType string, hRes=0x%08X\r\n", hRes));
                goto Exit;
            }
            dwWriteOffset += g_dwPlatformTypeSize;
        }

        if (g_ceDmpSystemInfo.PlatformVersion)
        {
            // Write PlatformVersion data
            hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &g_platformVersion, g_dwPlatformVersionSize, TRUE);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamSystemInfo: WatsonWriteData failed writing PlatformVersion data, hRes=0x%08X\r\n", hRes));
                goto Exit;
            }
            dwWriteOffset += g_dwPlatformVersionSize;
        }
        
        KD_ASSERT(rvaData == ((*pdwFileOffset) + dwWriteOffset));
    }
    
    *pdwFileOffset = rvaData;

    hRes = DWAlignFile (*pdwFileOffset, fWrite);

Exit:

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteCeStreamSystemInfo: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteCeStreamException

    Write the Exception Stream to the dump file.
----------------------------------------------------------------------------*/
static HRESULT WriteCeStreamException(CEDUMP_TYPE ceDumpType, MINIDUMP_DIRECTORY *pceDmpDirectory, DWORD *pdwFileOffset, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;
    DWORD dwWriteOffset;
    MINIDUMP_DIRECTORY ceDmpDirectory;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteCeStreamException: Enter\r\n"));

    // Make sure stream is correctly aligned
    KD_ASSERT(((*pdwFileOffset)%CEDUMP_ALIGNMENT) == 0);
    if (((*pdwFileOffset)%CEDUMP_ALIGNMENT) != 0)
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamException: File offset (%u) not %u byte aligned\r\n",
                                  *pdwFileOffset, CEDUMP_ALIGNMENT));
        hRes = E_FAIL;
        goto Exit;
    }

    // On write pass *pdwFileOffset should match Location.Rva
    KD_ASSERT(fWrite ? pceDmpDirectory->Location.Rva == *pdwFileOffset : TRUE);
    if (fWrite && (pceDmpDirectory->Location.Rva != *pdwFileOffset))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamException: File offset (%u) on write pass does not match expected value (%u)\r\n",
                                  *pdwFileOffset, pceDmpDirectory->Location.Rva));
        hRes = E_FAIL;
        goto Exit;
    }

    pceDmpDirectory->StreamType        = ceStreamException;
    pceDmpDirectory->Location.DataSize = sizeof(CEDUMP_EXCEPTION_STREAM) +
                                         sizeof(CEDUMP_EXCEPTION);
    pceDmpDirectory->Location.Rva      = *pdwFileOffset;

    if (fWrite)
    {
        BOOL fExceptionMatchesFocusThread;
        BOOL fFirstChanceException = (!g_pexceptionInfo->SecondChance) && ((!g_fCaptureDumpFileOnDeviceCalled) && (!g_fReportFaultCalled));
        dwWriteOffset = 0;

        memset (&g_ceDmpExceptionStream, 0, sizeof (g_ceDmpExceptionStream));
        
        // Write out Exception Stream header
        g_ceDmpExceptionStream.SizeOfHeader = sizeof (g_ceDmpExceptionStream);
        g_ceDmpExceptionStream.SizeOfException = sizeof(CEDUMP_EXCEPTION);
        g_ceDmpExceptionStream.SizeOfThreadContext = sizeof(CEDUMP_ELEMENT_LIST);
        g_ceDmpExceptionStream.Flags  = (fFirstChanceException            ? CEDUMP_EXCEPTION_FIRST_CHANCE : 0);          
        g_ceDmpExceptionStream.Flags |= (g_fCaptureDumpFileOnDeviceCalled ? CEDUMP_EXCEPTION_CAPTURE_DUMP_FILE_API : 0); 
        g_ceDmpExceptionStream.Flags |= (g_fReportFaultCalled             ? CEDUMP_EXCEPTION_REPORT_FAULT_API : 0);     
        g_ceDmpExceptionStream.Flags |= (g_fStackBufferOverrun            ? CEDUMP_EXCEPTION_STACK_BUFFER_OVERRUN : 0);


        // Capture the Exception Record first.
        // The information for all of the Exception entries can be derived from g_pDmpThread for exception capture
        // as well as user CaptureDumpFileOnDevice().
        //
        // Using thread structure because Kernel API's SwitchVM() and SwitchActiveProcess() aren't completely 
        // reversible operations. For example, for a kernel thread without a usermode VM, a SwitchVM call will result
        // in pVMProc becoming non-null, even after a second call to SwitchVM to restore the VM.  Both SwitchVM and
        // SwitchActiveProcess will properly restore null pointers to the current thread, so we're better off trusting
        // the thread more than KData.
        if (!g_fCaptureDumpFileOnDeviceCalled)
        {
            KD_ASSERT(pCurThread == g_pDmpThread);
        }

        if (g_pDmpThread)
        {
            if (g_pDmpThread->pprcVM)
            {
                g_ceDmpExceptionStream.VMProcessId              = static_cast<ULONG32> (g_pDmpThread->pprcVM->dwId);
            }
            if (g_pDmpThread->pprcActv)
            {
                g_ceDmpExceptionStream.ActvProcessId            = static_cast<ULONG32> (g_pDmpThread->pprcActv->dwId); 
            }
            g_ceDmpExceptionStream.ThreadId                     = static_cast<ULONG32> (g_pDmpThread->dwId);
            if (g_pDmpThread->pprcOwner)
            {
                g_ceDmpExceptionStream.OwnerProcessId           = static_cast<ULONG32> (g_pDmpThread->pprcOwner->dwId);
            }
        }

        // If CaptureDumpFileOnDevice() is called, capture details of the current thread.
        if (g_fCaptureDumpFileOnDeviceCalled)
        {
            if (pCurThread)
            {
                g_ceDmpExceptionStream.CaptureAPIThreadId           = static_cast<ULONG32> (pCurThread->dwId);
                if (pCurThread->pprcVM)
                {
                    g_ceDmpExceptionStream.CaptureAPIVMProcessId    = static_cast<ULONG32> (pCurThread->pprcVM->dwId);
                }
                if (pCurThread->pprcActv)
                {
                    g_ceDmpExceptionStream.CaptureAPIActvProcessId  = static_cast<ULONG32> (pCurThread->pprcActv->dwId);
                }
                if (pCurThread->pprcOwner)
                {
                    g_ceDmpExceptionStream.CaptureAPIOwnerProcessId = static_cast<ULONG32> (pCurThread->pprcOwner->dwId); 
                }
            }
            else
            {
                DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteCeStreamException: pCurThread == NULL\r\n"));
            }
        }


        fExceptionMatchesFocusThread = (g_ceDmpExceptionStream.ThreadId == g_pDmpThread->dwId);
        g_ceDmpExceptionStream.Flags |= (fExceptionMatchesFocusThread ? CEDUMP_EXCEPTION_MATCHES_FOCUS_THREAD : 0);  

        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &g_ceDmpExceptionStream, sizeof(CEDUMP_EXCEPTION_STREAM), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamException: WatsonWriteData failed writing Exception Stream header, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += sizeof(CEDUMP_EXCEPTION_STREAM);

        // Write out Exception record
        KD_ASSERT(sizeof(EXCEPTION_RECORD) == sizeof(CEDUMP_EXCEPTION));
        if (sizeof(EXCEPTION_RECORD) != sizeof(CEDUMP_EXCEPTION))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamException: Exception record size does not much dump record size\r\n"));
            hRes = E_FAIL;
            goto Exit;
        }

        if (!g_fCaptureDumpFileOnDeviceCalled)
        {
            // Copy over exception record for exception dumps
            memcpy(&g_ceDmpException, g_pexceptionInfo->ExceptionPointers.ExceptionRecord, sizeof(CEDUMP_EXCEPTION));
        }
        else
        {
            // Fake a breakpoint exception when CaptureDumpFileOnDevice called
            memset(&g_ceDmpException, 0, sizeof(CEDUMP_EXCEPTION));
            g_ceDmpException.ExceptionCode = STATUS_BREAKPOINT;
            if (g_pDmpThread == pCurThread)
            {
                // For the current thread we still use the exception address
                g_ceDmpException.ExceptionAddress = (ULONG32)g_pexceptionInfo->ExceptionPointers.ExceptionRecord->ExceptionAddress;
            }
            else
            {
                g_ceDmpException.ExceptionAddress = GetThreadIP(g_pDmpThread);
            }
        }

        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &g_ceDmpException, sizeof(CEDUMP_EXCEPTION), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamException: WatsonWriteData failed writing Exception Record, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += sizeof(CEDUMP_EXCEPTION);

        KD_ASSERT(dwWriteOffset == pceDmpDirectory->Location.DataSize);
    }

    *pdwFileOffset += pceDmpDirectory->Location.DataSize;

    // We need to set this before calling WriteCeStreamElementList to avoid an error
    // with the offset check
    ceDmpDirectory.Location.Rva = *pdwFileOffset;
    
    // Write out Context of faulting thread
    hRes = WriteCeStreamElementList(ceStreamException, ceDumpType, &ceDmpDirectory, pdwFileOffset, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamException: WriteCeStreamElementList failed writing Thread Context for exception, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Add the Header Data size for the Context list
    pceDmpDirectory->Location.DataSize += ceDmpDirectory.Location.DataSize;
    hRes = DWAlignFile (*pdwFileOffset, fWrite);

Exit:

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteCeStreamException: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteCeStreamThreadCallStackList

    Write the list of Thread Call Stacks.
----------------------------------------------------------------------------*/
static HRESULT WriteCeStreamThreadCallStackList(CEDUMP_TYPE ceDumpType, MINIDUMP_DIRECTORY *pceDmpDirectory, DWORD *pdwFileOffset, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;
    DWORD dwWriteOffset = 0;
    DWORD dwFileOffsetStackFrames = 0;
    DWORD dwNumberOfThreads = 0;
    DWORD dwThreadCount = 0;
    DWORD dwNumberOfFrames = 0;
    DWORD dwNumberOfProcesses = 0;
    DWORD dwFrameCount = 0;
    CEDUMP_THREAD_CALL_STACK ceDumpThreadCallStack = {0};
    CEDUMP_THREAD_CALL_STACK_FRAME ceDumpThreadCallStackFrame = {0};
    CallSnapshot3 csexFrames[STACK_FRAMES_BUFFERED] = {0};

    PPROCESS pFirstProcess, pProcess;
    PTHREAD pThread = NULL;
    DWORD dwProcessNumber = 0;
    DWORD dwMaxStackFrames = 0;
    BOOL  fCurrentThreadOnly = FALSE;

    DWORD const dwGetCallStackFlags = STACKSNAP_NEW_VM | STACKSNAP_RETURN_FRAMES_ON_ERROR;
    DWORD dwSkipFrames = 0;
    DWORD dwFramesReturned = 0;
    DWORD dwParams = 0;
    DWORD dwMaxThreadEntries = sizeof(g_bBuffer)/sizeof(WORD);

    // Used to find frame count mismatch between writting headers and frames
    memset(g_bBuffer, 0, sizeof(g_bBuffer));

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteCeStreamThreadCallStackList: Enter\r\n"));

    // Make sure stream is correctly aligned
    KD_ASSERT(((*pdwFileOffset)%CEDUMP_ALIGNMENT) == 0);
    if (((*pdwFileOffset)%CEDUMP_ALIGNMENT) != 0)
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamThreadCallStackList: File offset (%u) not %u byte aligned\r\n",
                                  *pdwFileOffset, CEDUMP_ALIGNMENT));
        hRes = E_FAIL;
        goto Exit;
    }

    // On write pass *pdwFileOffset should match Location.Rva
    KD_ASSERT(fWrite ? pceDmpDirectory->Location.Rva == *pdwFileOffset : TRUE);
    if (fWrite && (pceDmpDirectory->Location.Rva != *pdwFileOffset))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamThreadCallStackList: File offset (%u) on write pass does not match expected value (%u)\r\n",
                                  *pdwFileOffset, pceDmpDirectory->Location.Rva));
        hRes = E_FAIL;
        goto Exit;
    }

    switch (ceDumpType)
    {
    case ceDumpTypeComplete:
    case ceDumpTypeContext:
        // We only include the current thread call stack for Complete & Context dumps
        fCurrentThreadOnly = TRUE;
        dwNumberOfProcesses = 1;
        dwMaxStackFrames = MAX_STACK_FRAMES_CONTEXT;
        pFirstProcess = g_pDmpProc;
        dwNumberOfThreads = 1;
        break;
        
    case ceDumpTypeSystem:
        {
            // Include all call stacks from all threads in all processes
            dwMaxStackFrames = MAX_STACK_FRAMES_SYSTEM;
            PROCESS *pProcess = g_pprcNK;
            do
            {
                ++dwNumberOfProcesses;

                if (IsProcessAlive (pProcess))
                {
                    THREAD *pthd = reinterpret_cast<THREAD*> (pProcess->thrdList.pFwd);
                    while (pthd && pthd != reinterpret_cast<THREAD*> (&pProcess->thrdList))
                    {
                        ++dwNumberOfThreads;
                        pthd = reinterpret_cast<THREAD*> (pthd->thLink.pFwd);
                    }
                }
                pProcess = reinterpret_cast<PROCESS*> (pProcess->prclist.pFwd);
            } while (pProcess != g_pprcNK);

            // Reset the pProcess to g_pprcNK;
            pFirstProcess = g_pprcNK;
            break;
        }
    
    default:
        // This should not happen
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamThreadCallStackList: Bad Dump Type detected, ceDumpType=%u\r\n", ceDumpType));
        KD_ASSERT(FALSE); 
        hRes = E_FAIL;
        goto Exit;
        break;
    }

    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!WriteCeStreamThreadCallStackList: Generating Thread Call Stack List\r\n"));
    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!WriteCeStreamThreadCallStackList: Number of processes=%u, Number of Threads=%u, Max Frames=%u\r\n",
                                 dwNumberOfProcesses, dwNumberOfThreads, dwMaxStackFrames));

    // We should only have WORDs worth of frames
    KD_ASSERT((dwMaxStackFrames >> 16) == 0);
    dwMaxStackFrames = (WORD)dwMaxStackFrames;

    pceDmpDirectory->StreamType        = ceStreamThreadCallStackList;
    pceDmpDirectory->Location.DataSize = sizeof(CEDUMP_THREAD_CALL_STACK_LIST) +
                                         (sizeof(CEDUMP_THREAD_CALL_STACK) * dwNumberOfThreads); 
    pceDmpDirectory->Location.Rva      = *pdwFileOffset;

    // Offset to where stack frames are written
    dwFileOffsetStackFrames = (*pdwFileOffset) + pceDmpDirectory->Location.DataSize;

    // Write out Thread Call Stack List header info
    if (fWrite)
    {
        dwWriteOffset = 0;
        
        // Write out the Thread Context List Stream header
        g_ceDmpThreadCallStackList.SizeOfHeader = sizeof(CEDUMP_THREAD_CALL_STACK_LIST);
        g_ceDmpThreadCallStackList.SizeOfEntry = sizeof(CEDUMP_THREAD_CALL_STACK);
        g_ceDmpThreadCallStackList.NumberOfEntries = dwNumberOfThreads;

        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &g_ceDmpThreadCallStackList, sizeof(CEDUMP_THREAD_CALL_STACK_LIST), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamThreadCallStackList: WatsonWriteData failed writing Thread Call Stack List Stream header, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += sizeof(CEDUMP_THREAD_CALL_STACK_LIST);
    }

    // Write out List of Thread Call Stack headers by iterating through all processes and threads
    pProcess = pFirstProcess;
    dwProcessNumber = 0;
    while (dwProcessNumber < dwNumberOfProcesses && pProcess)
    {
        if (fCurrentThreadOnly)
        {
            // We are only doing the current Thread's call stack
            KD_ASSERT(1 == dwNumberOfThreads);
            KD_ASSERT(1 == dwNumberOfProcesses);
            pThread = g_pDmpThread; // Point to the Dump thread
        }
        else
        {
            pThread = reinterpret_cast<PTHREAD> (pProcess->thrdList.pFwd);
        }

        while (pThread && pThread != reinterpret_cast<PTHREAD> (&pProcess->thrdList))
        {
            // Valid thread found, increment the thread count
            ++ dwThreadCount;

            dwNumberOfFrames = 0;
            dwFramesReturned = STACK_FRAMES_BUFFERED;
            
            // Repeatedly collect frames until either max reached or no more frames available
            while ((dwNumberOfFrames < dwMaxStackFrames) && (STACK_FRAMES_BUFFERED == dwFramesReturned))
            {
                dwFramesReturned = GetCallStack(pThread, STACK_FRAMES_BUFFERED, csexFrames, dwGetCallStackFlags, dwNumberOfFrames);
                dwNumberOfFrames += dwFramesReturned;
            }

            // Limit number of frames to the max allowed
            dwNumberOfFrames = min (dwNumberOfFrames, dwMaxStackFrames);

            DEBUGGERMSG (OXZONE_DWDMPGEN, (L"  DwDmpGen!WriteCeStreamThreadCallStackList: Got %d frames for thread 0x%08x\r\n",
                                           dwNumberOfFrames, pThread));
            if (fWrite)
            {
                ceDumpThreadCallStack.ProcessId      = (ULONG32)pThread->pprcOwner->dwId;
                ceDumpThreadCallStack.ThreadId       = (ULONG32)pThread->dwId;
                ceDumpThreadCallStack.SizeOfFrame    = sizeof(CEDUMP_THREAD_CALL_STACK_FRAME);
                ceDumpThreadCallStack.NumberOfFrames = (WORD)dwNumberOfFrames;
                ceDumpThreadCallStack.StackFrames    = dwFileOffsetStackFrames;

                hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &ceDumpThreadCallStack, sizeof(ceDumpThreadCallStack), TRUE);
                if (FAILED(hRes))
                {
                    DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamThreadCallStackList: WatsonWriteData failed writing Thread Call Stack header, hRes=0x%08X\r\n", hRes));
                    goto Exit;
                }
                dwWriteOffset += sizeof(ceDumpThreadCallStack);
            }

            SetFrameCount (dwThreadCount, dwNumberOfFrames);

            dwFileOffsetStackFrames += (sizeof(CEDUMP_THREAD_CALL_STACK_FRAME) * dwNumberOfFrames);

            // Check if we are only doing the current thread
            if (fCurrentThreadOnly)
            {
                // Only doing current thread, so break out of while loop
                pThread = NULL; 
            }
            else
            {
                // Doing all threads, so point to next one
                pThread = reinterpret_cast<PTHREAD> (pThread->thLink.pFwd);
            }
        }

        ++dwProcessNumber;
        pProcess = reinterpret_cast<PPROCESS> (pProcess->prclist.pFwd);
    }

    KD_ASSERT(dwThreadCount == dwNumberOfThreads);
    KD_ASSERT(fWrite ? (dwWriteOffset == pceDmpDirectory->Location.DataSize) : TRUE);

    *pdwFileOffset += pceDmpDirectory->Location.DataSize;

    dwThreadCount = 0;
    dwWriteOffset = 0;

    // Write out all Thread Call Stack Frames by iterating through all processes and threads
    pProcess = pFirstProcess;
    dwProcessNumber = 0;
    while (dwProcessNumber < dwNumberOfProcesses && pProcess)
    {
        if (fCurrentThreadOnly)
        {
            // We are only doing the current Thread's call stack
            KD_ASSERT(1 == dwNumberOfThreads);
            KD_ASSERT(1 == dwNumberOfProcesses);
            pThread = g_pDmpThread; // Point to the Dump thread
        }
        else
        {
            pThread = reinterpret_cast<PTHREAD> (pProcess->thrdList.pFwd);
        }

        while (pThread && pThread != reinterpret_cast<PTHREAD> (&pProcess->thrdList))
        {
            // Valid thread found, increment the thread count
            ++ dwThreadCount;

            dwNumberOfFrames = 0;
            dwFramesReturned = STACK_FRAMES_BUFFERED;
            
            // Repeatedly write frames until either max reached or no more frames available
            while ((dwNumberOfFrames < dwMaxStackFrames) && (STACK_FRAMES_BUFFERED == dwFramesReturned))
            {
                dwFramesReturned = GetCallStack(pThread, STACK_FRAMES_BUFFERED, csexFrames, dwGetCallStackFlags, dwNumberOfFrames);
                DWORD dwGetCallStackError = GetLastError();

                for (dwFrameCount = 0;  (dwFrameCount < dwFramesReturned) && (dwNumberOfFrames < dwMaxStackFrames); ++ dwFrameCount)
                {
                    if (fWrite)
                    {
                        memset(&ceDumpThreadCallStackFrame, 0, sizeof(ceDumpThreadCallStackFrame));
                        ceDumpThreadCallStackFrame.ReturnAddr    = csexFrames[dwFrameCount].dwReturnAddr;
                        ceDumpThreadCallStackFrame.FramePtr      = csexFrames[dwFrameCount].dwFramePtr;
                        ceDumpThreadCallStackFrame.VMProcessId   = csexFrames[dwFrameCount].dwVMProc;
                        ceDumpThreadCallStackFrame.ActvProcessId = csexFrames[dwFrameCount].dwActvProc;
                        ceDumpThreadCallStackFrame.Flags         = (dwGetCallStackError ? CEDUMP_CALL_STACK_ERROR_DETECTED : 0);

                        for (dwParams = 0; dwParams < 4; ++dwParams)
                        {
                            ceDumpThreadCallStackFrame.Params[dwParams] = csexFrames[dwFrameCount].dwParams[dwParams];
                        }

                        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &ceDumpThreadCallStackFrame, sizeof(ceDumpThreadCallStackFrame),
                           TRUE);
                        if (FAILED(hRes))
                        {
                            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamThreadCallStackList: WatsonWriteData failed writing Thread Call Stack Frame data, hRes=0x%08X\r\n", hRes));
                            goto Exit;
                        }
                    }

                    // Add global memory for processes & modules in the call stack of the Dump thread
                    if ((pThread == g_pDmpThread) && (FALSE == fWrite))
                    {
                        // Add global memory of process / module associated with Return Address (Depends on CeDumpType)
                        hRes = MemoryAddGlobals(ceDumpType, csexFrames[dwFrameCount].dwReturnAddr, (HPROCESS)csexFrames[dwFrameCount].dwVMProc, fWrite);
                        if (FAILED(hRes))
                        {
                            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamThreadCallStackList: MemoryAddGlobals failed adding global memory for ReturnAddr=0x%08X, hRes=0x%08X\r\n", hRes));
                            goto Exit;
                        }
                    }

                    ++ dwNumberOfFrames;
                    dwWriteOffset += sizeof (ceDumpThreadCallStackFrame);
                }
            }

            if (!CheckFrameCount (dwThreadCount, dwNumberOfFrames))
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamThreadCallStackList: Frame Count Check failure, Thread=%08X, ThreadID=0x%08X, ExpectedFrames=%u, ActualFrames=%u\r\n", 
                                           pThread, pThread->dwId, GetFrameCount (dwThreadCount), dwNumberOfFrames));
                hRes = E_FAIL;
                goto Exit;
            }

            // Check if we are only doing the current thread
            if (fCurrentThreadOnly)
            {
                // Only doing current thread, so break out of while loop
                pThread = NULL; 
            }
            else
            {
                // Doing all threads, so point to next one
                pThread = reinterpret_cast<PTHREAD> (pThread->thLink.pFwd);
            }
        }

        ++dwProcessNumber;
        pProcess = reinterpret_cast<PPROCESS> (pProcess->prclist.pFwd);
    }

    DEBUGGERMSG (OXZONE_DWDMPGEN, (L"  WriteCeStreamThreadCallStackList: Number of Threads = %d, Expected = %d\r\n", dwThreadCount, dwNumberOfThreads));
    KD_ASSERT(dwThreadCount == dwNumberOfThreads);
    KD_ASSERT(dwFileOffsetStackFrames == (*pdwFileOffset) + dwWriteOffset);

    *pdwFileOffset += dwWriteOffset;

    // Make sure we are aligned on exit
    hRes = DWAlignFile(*pdwFileOffset, fWrite);

Exit:

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteCeStreamThreadCallStackList: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteCeStreamMemoryList

    Write the list of Memory dumps.
----------------------------------------------------------------------------*/
static HRESULT WriteCeStreamMemoryList(CEDUMP_TYPE ceDumpType, MINIDUMP_DIRECTORY *pceDmpDirectory, DWORD *pdwFileOffset, BOOL fVirtual, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;
    DWORD dwWriteOffset;
    DWORD dwFileOffsetMemoryDumps;
    DWORD dwMemoryBlockCount;
    DWORD dwMemoryBlock;
    DWORD dwMemorySize;
    DWORD dwMemorySizeTotal;
    CEDUMP_MEMORY_DESCRIPTOR ceDumpMemoryDescriptor;
    DWORD dwMemoryBlocksMax;
    MEMORY_BLOCK *pmemoryBlocks;
    DWORD *pdwMemoryBlocksTotal;
    DWORD *pdwMemorySizeTotal;
    DWORD dwStreamType;
    PCEDUMP_MEMORY_LIST pceDmpMemoryList;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteCeStreamMemoryList: Enter, fVirtual=%u\r\n", fVirtual));

    // Make sure stream is correctly aligned
    KD_ASSERT(((*pdwFileOffset)%CEDUMP_ALIGNMENT) == 0);
    if (((*pdwFileOffset)%CEDUMP_ALIGNMENT) != 0)
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamMemoryList: File offset (%u) not %u byte aligned\r\n",
                                  *pdwFileOffset, CEDUMP_ALIGNMENT));
        hRes = E_FAIL;
        goto Exit;
    }

    // On write pass *pdwFileOffset should match Location.Rva
    KD_ASSERT(fWrite ? pceDmpDirectory->Location.Rva == *pdwFileOffset : TRUE);
    if (fWrite && (pceDmpDirectory->Location.Rva != *pdwFileOffset))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamMemoryList: File offset (%u) on write pass does not match expected value (%u)\r\n",
                                  *pdwFileOffset, pceDmpDirectory->Location.Rva));
        hRes = E_FAIL;
        goto Exit;
    }

    if (TRUE == fVirtual)
    {
        // Virutal Memory
        dwMemoryBlocksMax = MAX_MEMORY_VIRTUAL_BLOCKS;
        pmemoryBlocks = g_memoryVirtualBlocks;
        pdwMemorySizeTotal = &g_dwMemoryVirtualSizeTotal;
        pdwMemoryBlocksTotal = &g_dwMemoryVirtualBlocksTotal;
        dwStreamType = ceStreamMemoryVirtualList;
        pceDmpMemoryList = &g_ceDmpMemoryVirtualList;
    }
    else
    {
        // Physical Memory
        dwMemoryBlocksMax = MAX_MEMORY_PHYSICAL_BLOCKS;
        pmemoryBlocks = g_memoryPhysicalBlocks;
        pdwMemorySizeTotal = &g_dwMemoryPhysicalSizeTotal;
        pdwMemoryBlocksTotal = &g_dwMemoryPhysicalBlocksTotal;
        dwStreamType = ceStreamMemoryPhysicalList;
        pceDmpMemoryList = &g_ceDmpMemoryPhysicalList;
    }

    dwMemoryBlockCount = 0;
    dwMemorySizeTotal = 0;
    
    for (dwMemoryBlock = 0; dwMemoryBlock < dwMemoryBlocksMax; ++dwMemoryBlock)
    {
        if (pmemoryBlocks[dwMemoryBlock].dwMemorySize != EMPTY_BLOCK)
        {
            ++ dwMemoryBlockCount;
            dwMemorySizeTotal += pmemoryBlocks[dwMemoryBlock].dwMemorySize;
        }
    }

    KD_ASSERT(dwMemoryBlockCount == (*pdwMemoryBlocksTotal));
    KD_ASSERT(dwMemorySizeTotal == (*pdwMemorySizeTotal));
    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!WriteCeStreamMemoryList: Writing %u memory blocks for a total of %u bytes\r\n", dwMemoryBlockCount, dwMemorySizeTotal));

    pceDmpDirectory->StreamType        = dwStreamType;
    pceDmpDirectory->Location.DataSize = sizeof (CEDUMP_MEMORY_LIST) +
                                         (sizeof (ceDumpMemoryDescriptor) * (*pdwMemoryBlocksTotal)); 
    pceDmpDirectory->Location.Rva      = *pdwFileOffset;

    // Write out Memory List header info
    if (fWrite)
    {
        dwWriteOffset = 0;
        dwFileOffsetMemoryDumps = (*pdwFileOffset) + pceDmpDirectory->Location.DataSize;
        
        // Write out the Memory List Stream header
        pceDmpMemoryList->SizeOfHeader = sizeof (*pceDmpMemoryList);
        pceDmpMemoryList->SizeOfEntry  = sizeof (ceDumpMemoryDescriptor);
        pceDmpMemoryList->NumberOfEntries = (*pdwMemoryBlocksTotal);

        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, pceDmpMemoryList, sizeof(CEDUMP_MEMORY_LIST), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamMemoryList: WatsonWriteData failed writing Memory List Stream header, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += sizeof(CEDUMP_MEMORY_LIST);
    }

    // Write out Memory Descriptors
    dwMemoryBlockCount = 0;

    for (dwMemoryBlock = 0; (dwMemoryBlock < dwMemoryBlocksMax) && (dwMemoryBlockCount < (*pdwMemoryBlocksTotal)); ++dwMemoryBlock)
    {
        dwMemorySize = pmemoryBlocks[dwMemoryBlock].dwMemorySize;
        
        if (dwMemorySize != EMPTY_BLOCK)
        {
            if (fWrite)
            {
                ceDumpMemoryDescriptor.StartOfMemoryRange = (DWORD64)pmemoryBlocks[dwMemoryBlock].dwMemoryStart;
                ceDumpMemoryDescriptor.Memory.DataSize = dwMemorySize;
                ceDumpMemoryDescriptor.Memory.Rva = dwFileOffsetMemoryDumps;
                ceDumpMemoryDescriptor.VMProcessID = pmemoryBlocks[dwMemoryBlock].VMProcess?
                                                        pmemoryBlocks[dwMemoryBlock].VMProcess->dwId : 0;

                hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &ceDumpMemoryDescriptor, sizeof (ceDumpMemoryDescriptor), TRUE);
                if (FAILED(hRes))
                {
                    DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamMemoryList: WatsonWriteData failed writing memory descriptor, Index=%u, Count=%u, hRes=0x%08X\r\n", 
                                              dwMemoryBlock, dwMemoryBlockCount, hRes));
                    goto Exit;
                }
                dwWriteOffset += sizeof (ceDumpMemoryDescriptor);
            }
            dwFileOffsetMemoryDumps += dwMemorySize;
            ++ dwMemoryBlockCount;
        }
    }

    KD_ASSERT(fWrite ? (dwWriteOffset == pceDmpDirectory->Location.DataSize) : TRUE);

    *pdwFileOffset += pceDmpDirectory->Location.DataSize;

    // Write out Memory Blocks 
    dwWriteOffset = 0;
    dwMemoryBlockCount = 0;

    for (dwMemoryBlock = 0; (dwMemoryBlock < dwMemoryBlocksMax) && (dwMemoryBlockCount < (*pdwMemoryBlocksTotal)); ++dwMemoryBlock)
    {
        dwMemorySize = pmemoryBlocks[dwMemoryBlock].dwMemorySize;
        if (dwMemorySize != EMPTY_BLOCK)
        {
            if (fWrite)
            {
                DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!WriteCeStreamMemoryList: Writing memory block %u, Start=0x%08X, Size=0x%08X\r\n", 
                                             dwMemoryBlock, pmemoryBlocks[dwMemoryBlock].dwMemoryStart, dwMemorySize));

                if (fVirtual)
                {
                    hRes = WatsonWriteData ((*pdwFileOffset) + dwWriteOffset,
                                            pmemoryBlocks[dwMemoryBlock].VMProcess,
                                            (PVOID)pmemoryBlocks[dwMemoryBlock].dwMemoryStart,
                                            dwMemorySize,
                                            TRUE);
                }
                else
                {
                    hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, (PVOID)pmemoryBlocks[dwMemoryBlock].dwMemoryStart, dwMemorySize, TRUE);
                }
                if (FAILED(hRes))
                {
                    DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamMemoryList: WatsonWriteData failed writing memory block, Index=%u, Count=%u, hRes=0x%08X\r\n", 
                                              dwMemoryBlock, dwMemoryBlockCount, hRes));
                    goto Exit;
                }
            }
            dwWriteOffset += dwMemorySize;
            ++ dwMemoryBlockCount;
        }
    }

    KD_ASSERT(fWrite ? (dwFileOffsetMemoryDumps == (*pdwFileOffset) + dwWriteOffset) : TRUE);

    *pdwFileOffset += dwWriteOffset;
    hRes = DWAlignFile (*pdwFileOffset, fWrite);

Exit:

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteCeStreamMemoryList: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteCeStreamMemoryVirtualList

    Write the list of Virtual Memory dumps.
----------------------------------------------------------------------------*/
static HRESULT WriteCeStreamMemoryVirtualList(CEDUMP_TYPE ceDumpType, MINIDUMP_DIRECTORY *pceDmpDirectory, DWORD *pdwFileOffset, BOOL fWrite, DWORD dwMaxDumpFileSize)
{
    HRESULT hRes = E_FAIL;
    DWORD dwMemoryStart;
    DWORD dwMemorySize;
    BOOL fCurrentProcessGlobals = FALSE;
    BOOL fCurrentProcessIPMemory = FALSE;
    BOOL fCurrentThreadStackMemory = FALSE;
    BOOL fCurrentProcessHeap = FALSE;
    BOOL fAllVirtualMemory = FALSE;
    BOOL fKData = FALSE;
    DWORD dwStackBase, dwStackSize, dwStackBound, dwStackTop;
    DWORD dwMemoryIPBefore;
    DWORD dwMemoryIPAfter;
    DWORD dwFileOffsetEstimate;
    DWORD dwFileSpaceLeft; 
    DWORD dwMaxFileSize = (-1);

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteCeStreamMemoryVirtualList: Enter\r\n"));

    switch (ceDumpType)
    {
        case ceDumpTypeSystem:
            // Include heap data of current process
            fCurrentProcessHeap = g_fDumpHeapData;
            
            // Include global variables of current process 
            fCurrentProcessGlobals = TRUE;

            // Amount of memory to save before and after Instruction Pointer
            fCurrentProcessIPMemory = TRUE;
            dwMemoryIPBefore = MEMORY_IP_BEFORE_SYSTEM;
            dwMemoryIPAfter = MEMORY_IP_AFTER_SYSTEM;

            // Include KData section
            fKData = TRUE;

            // Include stack memory of current thread (faulting thread)
            // This will also include the TLS data which is on the stack
            fCurrentThreadStackMemory = TRUE;

            break;
            
        case ceDumpTypeComplete:
            // The complete dump includes all Virtual memory
            fAllVirtualMemory = TRUE;

            // ** Fall through **

        case ceDumpTypeContext:
            // Amount of memory to save before and after Instruction Pointer
            fCurrentProcessIPMemory = TRUE;
            dwMemoryIPBefore = MEMORY_IP_BEFORE_CONTEXT;
            dwMemoryIPAfter = MEMORY_IP_AFTER_CONTEXT;

            // Include KData section
            fKData = TRUE;

            // Include stack memory of current thread (faulting thread)
            // This will also include the TLS data which is on the stack
            fCurrentThreadStackMemory = TRUE;

            dwMaxFileSize = dwMaxDumpFileSize;
            
            break;
            
        default:
            // This should not happen
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamMemoryVirtualList: Bad Dump Type detected, ceDumpType=%u\r\n", ceDumpType));
            KD_ASSERT(FALSE); 
            hRes = E_FAIL;
            goto Exit;
    }

    if (!fWrite)
    {
        if (fCurrentProcessHeap)
        {
            hRes = MemoryAddProcessHeap(g_pDmpProc, fWrite);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamMemoryVirtualList: MemoryAddProcessHeap failed adding Process Heap memory, hRes=0x%08X\r\n", hRes));
                goto Exit;
            }
        }

        if (fCurrentProcessGlobals)
        {
            hRes = MemoryAddProcessGlobals(g_pDmpProc, fWrite);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamMemoryVirtualList: MemoryAddProcessGlobals failed adding Process Global memory, hRes=0x%08X\r\n", hRes));
                goto Exit;
            }
        }

        if (fCurrentProcessIPMemory)
        {
            hRes = MemoryAddIPCode(dwMemoryIPBefore, dwMemoryIPAfter, fWrite);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamMemoryVirtualList: MemoryAddIPCode failed adding memory surrounding Instruction Pointer, hRes=0x%08X\r\n", hRes));
                goto Exit;
            }
        }

        if (fKData)
        {
            dwMemoryStart = (DWORD)g_pKData;
            dwMemorySize  = sizeof(KDataStruct);
            
            DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!WriteCeStreamMemoryVirtualList: Adding KData memory block\r\n"));
            
            hRes = MemoryBlocksAdd (g_pprcNK, dwMemoryStart, dwMemorySize, VIRTUAL_MEMORY, fWrite);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamMemoryVirtualList: MemoryBlocksAdd failed adding KData memory block, hRes=0x%08X\r\n", hRes));
                goto Exit;
            }
        }

        // **********************************************************************
        // *** The stack memory must be the last virtual memory added, since  ***
        // *** it may be sized down to fit in the Context dump size limit.    ***
        // *** The only exception is fAllVirtualMemory, since it is only      ***
        // *** for complete dumps.                                            ***
        // **********************************************************************

        if (fCurrentThreadStackMemory)
        {
            
            // Calculate offset to include Memory List header and Memory descriptors 
            dwFileOffsetEstimate = (*pdwFileOffset) + 
                                   (sizeof(CEDUMP_MEMORY_LIST) * 2) + // Virtual + Physical list
                                   (sizeof(CEDUMP_MEMORY_DESCRIPTOR) * (g_dwMemoryVirtualBlocksTotal+1)); // +1 for non-secure stack memory descriptor
                                   
            // Calculate offset to include total memory added so far                       
            dwFileOffsetEstimate += g_dwMemoryVirtualSizeTotal;
            
            // Calculate file space left for adding non-secure stack memory 
            dwFileSpaceLeft = (dwMaxFileSize > dwFileOffsetEstimate) ? dwMaxFileSize - dwFileOffsetEstimate : 0;

            // Memory for non-secure stack (may be a fiber)
            if (g_pDmpThread->pprcVM != 0)
            {
                if (dwFileSpaceLeft != 0)
                {
                    // If the thread does not have a VM, then it is a NK.EXE/kernel only thread and will not
                    // have a non-secure stack / tlsNonSecure.
                 
                    if (OsAxsReadMemory(&dwStackBase, g_pDmpThread->pprcVM,
                                        &g_pDmpThread->tlsNonSecure[PRETLS_STACKBASE], sizeof(dwStackBase)) == sizeof(dwStackBase) &&
                        OsAxsReadMemory(&dwStackBound, g_pDmpThread->pprcVM,
                                        &g_pDmpThread->tlsNonSecure[PRETLS_STACKBOUND], sizeof(dwStackBound)) == sizeof(dwStackBound))
                    {
                        BOOL WriteNonSecureStack = TRUE;

                        if (dwStackBase == g_pDmpThread->dwOrigBase)
                        {
                            // Normal thread
                            dwStackSize = g_pDmpThread->dwOrigStkSize;
                        }
                        else
                        {
                            // Fiber thread
                            if (OsAxsReadMemory(&dwStackSize, g_pDmpThread->pprcVM,
                                    &g_pDmpThread->tlsNonSecure[PRETLS_STACKSIZE], sizeof(dwStackSize)) != sizeof(dwStackSize))
                            {
                                DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  Thread %08X failed to read tlsNonSecure(%08X) in VM(%08X) for fiber\r\n", g_pDmpThread,
                                            g_pDmpThread->tlsNonSecure, g_pDmpThread->pprcVM));
                                WriteNonSecureStack = FALSE;
                            }
                            else
                            {
                                KD_ASSERT(dwStackSize == g_pDmpThread->pprcOwner->e32.e32_stackmax);
                            }
                        }

                        if (WriteNonSecureStack)
                        {
                            dwStackTop = (dwStackBase + dwStackSize);

                            // Save the stack between the Bound and Top
                            dwMemoryStart = dwStackBound;
                            dwMemorySize = dwStackTop - dwStackBound;

                            // Limit memory size to amount of file space left
                            if (dwMemorySize > dwFileSpaceLeft)
                            {
                                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamMemoryVirtualList: Limiting Non-secure Stack memory to fit in dump file -> Old Size=0x%08X, New Size=0x%08X\r\n", dwMemorySize, dwFileSpaceLeft));
                                dwMemorySize = dwFileSpaceLeft;
                            }

                            DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!WriteCeStreamMemoryVirtualList: Add Non-secure Stack memory\r\n"));

                            hRes = MemoryBlocksAdd(g_pDmpThread->pprcVM, dwMemoryStart, dwMemorySize, VIRTUAL_MEMORY, fWrite);
                            if (FAILED(hRes))
                            {
                                DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamMemoryVirtualList: MemoryBlocksAdd failed adding non-secure Stack memory, hRes=0x%08X\r\n", hRes));
                                goto Exit;
                            }
                        }
                    }
                    else
                    {
                        DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  Thread %08X failed to read tlsNonSecure(%08X) in VM(%08X)\r\n", g_pDmpThread,
                                    g_pDmpThread->tlsNonSecure, g_pDmpThread->pprcVM));
                    }
                }
                else
                {
                    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  No space left for Non-secure Stack memory, skip\r\n"));
                }
            }
            else
            {
                DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  Thread %08X does not have a VM\r\n", g_pDmpThread));
            }

            if (g_pDmpThread->tlsNonSecure != g_pDmpThread->tlsSecure)
            {
                // Calculate new File space left to limit size of secure stack
                // We don't simply add the previous memory block size, since it may have been
                // combined with another, so we work it out again from the begining
                
                // Calculate offset to include Memory List header and Memory descriptors 
                dwFileOffsetEstimate = (*pdwFileOffset) + 
                                       (sizeof(CEDUMP_MEMORY_LIST) * 2) + // Virtual + Physical list
                                       (sizeof(CEDUMP_MEMORY_DESCRIPTOR) * (g_dwMemoryVirtualBlocksTotal+1)); // +1 for secure stack memory descriptor
                                       
                // Calculate offset to include total memory added so far (This will now include the non-secure stack)                       
                dwFileOffsetEstimate += g_dwMemoryVirtualSizeTotal;
                
                // Calculate file space left for adding secure stack memory 
                dwFileSpaceLeft = (dwMaxFileSize > dwFileOffsetEstimate) ? dwMaxFileSize - dwFileOffsetEstimate : 0;

                if (dwFileSpaceLeft != 0)
                {
                    // Write the decriptor for secure stack (may be a fiber?)
                    dwStackBase = g_pDmpThread->tlsSecure[PRETLS_STACKBASE];
                    dwStackBound = g_pDmpThread->tlsSecure[PRETLS_STACKBOUND]; // Memory is commited to this point

                    // Secure stack size is always fixed
                    dwStackSize = KRN_STACK_SIZE;
                    
                    dwStackTop = (dwStackBase + dwStackSize);

                    // Save the stack between the Bound and Top
                    if (dwStackBound)
                    {
                        dwMemoryStart = dwStackBound;
                        dwMemorySize = dwStackTop - dwStackBound;
                    }
                    else
                    {
                        dwMemoryStart = dwStackBase;
                        dwMemorySize  = dwStackSize;
                    }

                    // Limit memory size to amount of file space left
                    if (dwMemorySize > dwFileSpaceLeft)
                    {
                        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamMemoryVirtualList: Limiting Secure Stack memory to fit in dump file -> Old Size=0x%08X, New Size=0x%08X\r\n", dwMemorySize, dwFileSpaceLeft));
                        dwMemorySize = dwFileSpaceLeft;
                    }

                    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!WriteCeStreamMemoryVirtualList: Add Secure Stack memory\r\n"));

                    PPROCESS SecureStackVM;
                    if (dwMemoryStart < VM_KMODE_BASE)
                    {
                        DEBUGGERMSG (OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamMemoryVirtualList: Secure Stack for thread in user mode?\r\n"));
                        SecureStackVM = g_pDmpThread->pprcVM;
                    }
                    else
                    {
                        SecureStackVM = g_pprcNK;
                    }
                    
                    hRes = MemoryBlocksAdd (SecureStackVM, dwMemoryStart, dwMemorySize, VIRTUAL_MEMORY, fWrite);
                    if (FAILED(hRes))
                    {
                        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamMemoryVirtualList: MemoryBlocksAdd failed adding secure Stack memory, hRes=0x%08X\r\n", hRes));
                        goto Exit;
                    }
                }
                else
                {
                    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  No space left for Secure Stack memory, skip\r\n"));
                }
            }
        }
        
        if (fAllVirtualMemory)
        {
            hRes = MemoryAddAllVirtual(fWrite);
            if (FAILED(hRes))
            {
                DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamMemoryVirtualList: MemoryAddAllVirtual failed, hRes=0x%08X\r\n", hRes));
                goto Exit;
            }
        }
    }

    hRes = WriteCeStreamMemoryList(ceDumpType, pceDmpDirectory, pdwFileOffset, VIRTUAL_MEMORY, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamMemoryVirtualList: WriteCeStreamMemoryList failed, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    hRes = S_OK;

Exit:

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteCeStreamMemoryVirtualList: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteCeStreamMemoryPhysicalList

    Write the list of Physical Memory dumps.
----------------------------------------------------------------------------*/
static HRESULT WriteCeStreamMemoryPhysicalList(CEDUMP_TYPE ceDumpType, MINIDUMP_DIRECTORY *pceDmpDirectory, DWORD *pdwFileOffset, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;
    BOOL  fAllPhysicalMemory = FALSE;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteCeStreamMemoryPhysicalList: Enter\r\n"));

    switch (ceDumpType)
    {
        case ceDumpTypeComplete:
            // The complete dump includes all physical memory
            fAllPhysicalMemory = TRUE;
            break;
            
        case ceDumpTypeSystem:
            break;
            
        case ceDumpTypeContext:
            break;
            
        default:
            // This should not happen
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamMemoryPhysicalList: Bad Dump Type detected, ceDumpType=%u\r\n", ceDumpType));
            KD_ASSERT(FALSE); 
            hRes = E_FAIL;
            goto Exit;
    }

    if (!fWrite)
    {
        if (fAllPhysicalMemory)
        {
            // We currently don't write any physical memory to the dump file.
        }
    }

    hRes = WriteCeStreamMemoryList(ceDumpType, pceDmpDirectory, pdwFileOffset, PHYSICAL_MEMORY, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamMemoryPhysicalList: WriteCeStreamMemoryList failed, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    hRes = S_OK;

Exit:

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteCeStreamMemoryPhysicalList: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteCeStreamBucketParameters

    Write the Bucket Parameters Stream to the dump file.
----------------------------------------------------------------------------*/
static HRESULT WriteCeStreamBucketParameters(MINIDUMP_DIRECTORY *pceDmpDirectory, DWORD *pdwFileOffset, BOOL fWrite)
{
    HRESULT hRes = E_FAIL;
    DWORD dwWriteOffset;
    LPWSTR lpwzEventType;
    LPWSTR lpwzAppName;
    LPWSTR lpwzModName;
    LPWSTR lpwzOwnerName;
    RVA    rvaString;
    DWORD  dwStringSize;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteCeStreamBucketParameters: Enter\r\n"));

    // Make sure stream is correctly aligned
    KD_ASSERT(((*pdwFileOffset)%CEDUMP_ALIGNMENT) == 0);
    if (((*pdwFileOffset)%CEDUMP_ALIGNMENT) != 0)
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamBucketParameters: File offset (%u) not %u byte aligned\r\n",
                                  *pdwFileOffset, CEDUMP_ALIGNMENT));
        hRes = E_FAIL;
        goto Exit;
    }

    // On write pass *pdwFileOffset should match Location.Rva
    KD_ASSERT(fWrite ? pceDmpDirectory->Location.Rva == *pdwFileOffset : TRUE);
    if (fWrite && (pceDmpDirectory->Location.Rva != *pdwFileOffset))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!WriteCeStreamBucketParameters: File offset (%u) on write pass does not match expected value (%u)\r\n",
                                  *pdwFileOffset, pceDmpDirectory->Location.Rva));
        hRes = E_FAIL;
        goto Exit;
    }

    pceDmpDirectory->StreamType        = ceStreamBucketParameters;
    pceDmpDirectory->Location.DataSize = sizeof(CEDUMP_BUCKET_PARAMETERS);
    pceDmpDirectory->Location.Rva      = *pdwFileOffset;

    hRes = GetBucketParameters(&g_ceDmpBucketParameters);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamBucketParameters: GetBucketParameters failed, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Convert String pointers to RVAs
    rvaString = pceDmpDirectory->Location.Rva + pceDmpDirectory->Location.DataSize;

    // Event Type 
    lpwzEventType = (LPWSTR)g_ceDmpBucketParameters.EventType;
    g_ceDmpBucketParameters.EventType = rvaString;
    dwStringSize = wcslen(lpwzEventType) * sizeof(WCHAR);
    rvaString += sizeof(dwStringSize) + dwStringSize + (1 * sizeof(WCHAR));

    // App Name
    lpwzAppName = (LPWSTR)g_ceDmpBucketParameters.AppName;
    g_ceDmpBucketParameters.AppName = rvaString;
    dwStringSize = wcslen(lpwzAppName) * sizeof(WCHAR);
    rvaString += sizeof(dwStringSize) + dwStringSize + (1 * sizeof(WCHAR));

    // Mod Name
    lpwzModName = (LPWSTR)g_ceDmpBucketParameters.ModName;
    g_ceDmpBucketParameters.ModName = rvaString;
    dwStringSize = wcslen(lpwzModName) * sizeof(WCHAR);
    rvaString += sizeof(dwStringSize) + dwStringSize + (1 * sizeof(WCHAR));

    // Owner Name
    lpwzOwnerName = (LPWSTR)g_ceDmpBucketParameters.OwnerName;
    g_ceDmpBucketParameters.OwnerName = rvaString;
    dwStringSize = wcslen(lpwzOwnerName) * sizeof(WCHAR);
    rvaString += sizeof(dwStringSize) + dwStringSize + (1 * sizeof(WCHAR));
    
    if (fWrite)
    {
        dwWriteOffset = 0;

        // Write Bucket Parameters stream header
        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &g_ceDmpBucketParameters, sizeof(CEDUMP_BUCKET_PARAMETERS), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamBucketParameters: WatsonWriteData failed writing Bucket Parameters stream header, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += pceDmpDirectory->Location.DataSize;

        // Write Event Type string size
        dwStringSize =wcslen(lpwzEventType) * sizeof(WCHAR);
        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &dwStringSize, sizeof(dwStringSize), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamBucketParameters: WatsonWriteData failed writing Event Type string size, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += sizeof(dwStringSize);

        // Write Event Type string
        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, lpwzEventType, (dwStringSize + (1 * sizeof(WCHAR))), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamBucketParameters: WatsonWriteData failed writing Event Type string, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += (dwStringSize + (1 * sizeof(WCHAR)));

        // Write App Name string size
        dwStringSize = wcslen(lpwzAppName) * sizeof(WCHAR);
        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &dwStringSize, sizeof(dwStringSize), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamBucketParameters: WatsonWriteData failed writing App Name string size, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += sizeof(dwStringSize);

        // Write App Name string
        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, lpwzAppName, (dwStringSize + (1 * sizeof(WCHAR))), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamBucketParameters: WatsonWriteData failed writing App Name string, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += (dwStringSize + (1 * sizeof(WCHAR)));

        // Write Mod Name string size
        dwStringSize = wcslen(lpwzModName) * sizeof(WCHAR);
        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &dwStringSize, sizeof(dwStringSize), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamBucketParameters: WatsonWriteData failed writing Mod Name string size, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += sizeof(dwStringSize);

        // Write Mod Name string
        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, lpwzModName, (dwStringSize + (1 * sizeof(WCHAR))), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamBucketParameters: WatsonWriteData failed writing Mod Name string, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += (dwStringSize + (1 * sizeof(WCHAR)));

        // Write Owner Name string size
        dwStringSize = wcslen(lpwzOwnerName) * sizeof(WCHAR);
        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, &dwStringSize, sizeof(dwStringSize), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamBucketParameters: WatsonWriteData failed writing Owner Name string size, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += sizeof(dwStringSize);

        // Write Owner Name string
        hRes = WatsonWriteData((*pdwFileOffset) + dwWriteOffset, lpwzOwnerName, (dwStringSize + (1 * sizeof(WCHAR))), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteCeStreamBucketParameters: WatsonWriteData failed writing Owner Name string, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += (dwStringSize + (1 * sizeof(WCHAR)));

        KD_ASSERT(rvaString == ((*pdwFileOffset) + dwWriteOffset));
    }

    *pdwFileOffset = rvaString;
    hRes = DWAlignFile (*pdwFileOffset, fWrite);

Exit:

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteCeStreamBucketParameters: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteTinyDumpFileContents

    When all else fails, try writing a tiny exception context dump
    with which we can at least make this event known.   
----------------------------------------------------------------------------*/
static HRESULT WriteTinyDumpFileContents()
{
    HRESULT hRes = E_FAIL;
    DWORD   dwNumberOfStreams = 2;
    DWORD   dwFileOffset;
    DWORD   dwStream;
    BOOL    fWrite;
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteTinyDumpFileContents: Enter\r\n"));

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteTinyDumpFileContents: *************************\r\n"));
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteTinyDumpFileContents: *******  Tiny Dump ******\r\n"));
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteTinyDumpFileContents: *************************\r\n"));
    
    DrWatsonClear();
    
    fWrite = FALSE;

    // Need an optimize/precalc phase and a generation phase
    for ( int phase=1; phase<=2; phase++ )
    {
        g_dwWrittenOffset = 0;
        g_dwWrittenCRC = 0;

        dwFileOffset = 0;
        
        // Add the dump header
        hRes = WriteCeHeader(dwNumberOfStreams, ceDumpTypeContext, &dwFileOffset, fWrite);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteTinyDumpFileContents: WriteCeHeader failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        
        // Add the directory
        hRes = WriteCeDirectory(&dwFileOffset, fWrite);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteTinyDumpFileContents: WriteCeDirectory failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        
        // Add the SystemInfo stream (First stream)
        dwStream = 0;

        hRes = WriteCeStreamSystemInfo(&g_ceDmpDirectory[dwStream], &dwFileOffset, fWrite);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteTinyDumpFileContents: WriteCeStreamSystemInfo failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }

        // Add the Exception stream (Must be second stream)
        // *** EXCEPTION STREAM MUST BE 2ND STREAM, It may change the primary process and thread *** 
        ++dwStream;

        hRes = WriteCeStreamException(ceDumpTypeContext, &g_ceDmpDirectory[dwStream], &dwFileOffset, fWrite);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteTinyDumpFileContents: WriteCeStreamException failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        
        fWrite = TRUE; // Second phase is dump generation
    }
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteTinyDumpFileContents: Leave\r\n"));
    
    hRes = S_OK;
    
Exit:

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteTinyDumpFileContents: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteDumpFileContents

    Write the contents of the dump file.
----------------------------------------------------------------------------*/
static HRESULT WriteDumpFileContents(CEDUMP_TYPE ceDumpType, DWORD *pdwFileOffset, BOOL fWrite, DWORD dwMaxDumpFileSize)
{
    HRESULT hRes = E_FAIL;
    DWORD   dwNumberOfStreams = MAX_STREAMS;
    DWORD   dwStream = 0;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteDumpFileContents: Enter, ceDumpType=%u\r\n", ceDumpType));

    if (!fWrite)
    {
        DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteDumpFileContents: *************************\r\n"));
        DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteDumpFileContents: *** Optimization Pass ***\r\n"));
        DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteDumpFileContents: *************************\r\n"));
        // Clear these only during optimization pass 
        memset(&g_ceDmpHeader, 0, sizeof(g_ceDmpHeader));
        memset(&g_ceDmpDirectory, 0, sizeof(g_ceDmpDirectory));
        memset(&g_ceDmpSystemInfo, 0, sizeof(g_ceDmpSystemInfo));
        memset(&g_ceDmpExceptionStream, 0, sizeof(g_ceDmpExceptionStream));
        memset(&g_ceDmpException, 0, sizeof(g_ceDmpException));
        memset(&g_ceDmpExceptionContext, 0, sizeof(g_ceDmpExceptionContext));
        memset(&g_ceDmpModuleList, 0, sizeof(g_ceDmpModuleList));
        memset(&g_ceDmpProcessList, 0, sizeof(g_ceDmpProcessList));
        memset(&g_ceDmpThreadList, 0, sizeof(g_ceDmpThreadList));
        memset(&g_ceDmpThreadContextList, 0, sizeof(g_ceDmpThreadContextList));
        memset(&g_ceDmpThreadCallStackList, 0, sizeof(g_ceDmpThreadCallStackList));
        memset(&g_ceDmpMemoryVirtualList, 0, sizeof(g_ceDmpMemoryVirtualList));
        memset(&g_ceDmpMemoryPhysicalList, 0, sizeof(g_ceDmpMemoryPhysicalList));
        memset(&g_ceDmpBucketParameters, 0, sizeof(g_ceDmpBucketParameters));
        memset(&g_memoryVirtualBlocks, 0xFF, sizeof(g_memoryVirtualBlocks));
        memset (&g_ceDmpProcessModuleList, 0, sizeof (g_ceDmpProcessModuleList));
        g_dwMemoryVirtualBlocksTotal = 0;
        g_dwMemoryVirtualSizeTotal = 0;
        memset(&g_memoryPhysicalBlocks, 0xFF, sizeof(g_memoryPhysicalBlocks));
        g_dwMemoryPhysicalBlocksTotal = 0;
        g_dwMemoryPhysicalSizeTotal = 0;
    }
    else
    {
        DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteDumpFileContents: *************************\r\n"));
        DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteDumpFileContents: ***  Generation Pass  ***\r\n"));
        DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!WriteDumpFileContents: *************************\r\n"));
        // Initialize the Dump File CRC only during write pass
        g_dwWrittenOffset = 0;
        g_dwWrittenCRC = 0;
    }

    // Add the dump header
    hRes = WriteCeHeader(dwNumberOfStreams, ceDumpType, pdwFileOffset, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: WriteCeHeader failed, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Add the directory
    hRes = WriteCeDirectory(pdwFileOffset, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: WriteCeDirectory failed, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Add the SystemInfo stream (First stream)
    dwStream = 0;

    hRes = WriteCeStreamSystemInfo(&g_ceDmpDirectory[dwStream], pdwFileOffset, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: WriteCeStreamSystemInfo failed, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Add the Exception stream (Must be second stream)
    // *** EXCEPTION STREAM MUST BE 2ND STREAM, It may change the primary process and thread *** 
    if (++dwStream >= MAX_STREAMS) goto ErrMaxStreams;

    hRes = WriteCeStreamException(ceDumpType, &g_ceDmpDirectory[dwStream], pdwFileOffset, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: WriteCeStreamException failed, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Add the Module List stream
    if (++dwStream >= MAX_STREAMS) goto ErrMaxStreams;

    hRes = WriteCeStreamElementList(ceStreamModuleList, ceDumpType, &g_ceDmpDirectory[dwStream], pdwFileOffset, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: WriteCeStreamElementList failed writing Module list, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Add the Process List stream
    if (++dwStream >= MAX_STREAMS) goto ErrMaxStreams;

    hRes = WriteCeStreamElementList(ceStreamProcessList, ceDumpType, &g_ceDmpDirectory[dwStream], pdwFileOffset, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: WriteCeStreamElementList failed writing Process list, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Add the Thread List stream
    if (++dwStream >= MAX_STREAMS) goto ErrMaxStreams;

    hRes = WriteCeStreamElementList(ceStreamThreadList, ceDumpType, &g_ceDmpDirectory[dwStream], pdwFileOffset, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: WriteCeStreamElementList failed writing Thread list, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Add the Thread Context List stream
    if (++dwStream >= MAX_STREAMS) goto ErrMaxStreams;

    hRes = WriteCeStreamElementList(ceStreamThreadContextList, ceDumpType, &g_ceDmpDirectory[dwStream], pdwFileOffset, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: WriteCeStreamElementList failed writing Thread Context list, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Add the Thread Call Stack List stream
    if (++dwStream >= MAX_STREAMS) goto ErrMaxStreams;

    hRes = WriteCeStreamThreadCallStackList(ceDumpType, &g_ceDmpDirectory[dwStream], pdwFileOffset, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: WriteCeStreamThreadCallStackList failed, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Add the Bucket Parameters stream
    if (++dwStream >= MAX_STREAMS) goto ErrMaxStreams;

    hRes = WriteCeStreamBucketParameters(&g_ceDmpDirectory[dwStream], pdwFileOffset, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: WriteCeStreamBucketParameters failed, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Add the Process-Module Map stream.
    if (++dwStream >= MAX_STREAMS) goto ErrMaxStreams;

    hRes = WriteCeStreamElementList (ceStreamProcessModuleMap, ceDumpType, &g_ceDmpDirectory[dwStream], pdwFileOffset, fWrite);
    if (FAILED (hRes))
    {
        DEBUGGERMSG (OXZONE_ALERT, (L"  WriteDumpFileContents: WriteCeStreamElementList failed writing Process-Module map, hRes=0x%08x\r\n", hRes));
        goto Exit;
    }

    // Add the Memory Virtual List stream 
    // *** MEMORY VIRTUAL LIST MUST BE 2ND LAST STREAM, Context/Complete dump will trim the virtual memory list to fit into 64K limit *** 
    if (++dwStream >= MAX_STREAMS) goto ErrMaxStreams;

    hRes = WriteCeStreamMemoryVirtualList(ceDumpType, &g_ceDmpDirectory[dwStream], pdwFileOffset, fWrite, dwMaxDumpFileSize);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: WriteCeStreamMemoryVirtualList failed, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Add the Memory Physical List stream 
    // *** MEMORY PHYSICAL LIST MUST BE LAST STREAM, Complete dump needs the above virtual memory list to fit into initial 64K limit *** 
    if (++dwStream >= MAX_STREAMS) goto ErrMaxStreams;

    hRes = WriteCeStreamMemoryPhysicalList(ceDumpType, &g_ceDmpDirectory[dwStream], pdwFileOffset, fWrite);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: WriteCeStreamMemoryPhysicalList failed, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    KD_ASSERT((dwStream+1) == dwNumberOfStreams);
    if ((dwStream+1) != dwNumberOfStreams)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: Incorrect number of streams written, Expected=%u, Actual=%u\r\n", dwNumberOfStreams, dwStream+1));
        hRes = E_FAIL;
        goto Exit;
    }

    hRes = S_OK;

    goto Exit;

ErrMaxStreams:

    KD_ASSERT(FALSE);  // dwStream >= MAX_STREAMS 
    DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFileContents: Exceeded max streams allowed, Max Streams=%u, Stream No=%u\r\n", MAX_STREAMS, dwStream));
    hRes = E_FAIL;
    
Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteDumpFileContents: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    OptimizeDumpFileContents

    Optimize the contents of the dump file.
----------------------------------------------------------------------------*/
static HRESULT OptimizeDumpFileContents(CEDUMP_TYPE *pceDumpType, DWORD dwMaxDumpFileSize)
{
    HRESULT hRes = E_FAIL;
    DWORD   dwFileOffset = 0;
    BOOL    fFileOptimized = FALSE;
    CEDUMP_TYPE ceDumpTypeOptimized = *pceDumpType;
    DWORD   dwMaxDumpFileSizeForType;

    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"++DwDmpGen!OptimizeDumpFileContents: Enter\r\n"));

    // For System dump type, first try with heap data + nk.exe globals (if in call stack), then without
    g_fDumpHeapData = TRUE;
    g_fDumpNkGlobals = TRUE;

    while ((FALSE == fFileOptimized) && (ceDumpTypeOptimized != ceDumpTypeUndefined))
    {
        // Calculate the size of the dump file contents (Call write routine with fWrite=FALSE)
        dwFileOffset = 0;

        // Set to false for each dump attempt, so we can see if this was added during pass
        g_fNkGlobalsDumped = FALSE;

        // Init to no error for each optimization pass
        g_dwLastError = ERROR_SUCCESS;
        
        hRes = WriteDumpFileContents(ceDumpTypeOptimized, &dwFileOffset, FALSE /* fWrite */, dwMaxDumpFileSize);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: WriteDumpFileContents failed, will try smaller dump type, hRes=0x%08X\r\n", hRes));

            // This will force a smaller dump type to be tried
            dwFileOffset = (-1);
        }

        dwMaxDumpFileSizeForType = dwMaxDumpFileSize;

        if (dwFileOffset <= dwMaxDumpFileSizeForType)
        {
            fFileOptimized = TRUE;
        }
        else
        {
            switch(ceDumpTypeOptimized)
            {
                case ceDumpTypeComplete:
                    ceDumpTypeOptimized = ceDumpTypeSystem;
                    DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: Complete Dump too large (%u), Max Dump Size = %u\r\n", dwFileOffset, dwMaxDumpFileSizeForType));
                    DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: Switching to System Dump.\r\n"));
                    break;
                case ceDumpTypeSystem:
                    if (TRUE == g_fDumpHeapData)
                    {
                        // If we had heap data, try system dump again without heap data
                        g_fDumpHeapData = FALSE;

                        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: System Dump with heap data too large (%u), Max Dump Size = %u\r\n", dwFileOffset, dwMaxDumpFileSizeForType));
                        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: Removing heap data from System Dump.\r\n"));
                    }
                    else if ((TRUE == g_fDumpNkGlobals) && (TRUE == g_fNkGlobalsDumped))
                    {
                        // If we added nk.exe globals, try system dump again without nk.exe globals 
                        g_fDumpNkGlobals = FALSE;

                        // Add heap data again, it may fit this time
                        g_fDumpHeapData = TRUE;
                        
                        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: System Dump with nk.exe globals too large (%u), Max Dump Size = %u\r\n", dwFileOffset, dwMaxDumpFileSizeForType));
                        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: Removing nk.exe globals & adding heap data back to System Dump.\r\n"));
                    }
                    else
                    {
                        ceDumpTypeOptimized = ceDumpTypeContext;
                        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: System Dump too large (%u), Max Dump Size = %u\r\n", dwFileOffset, dwMaxDumpFileSizeForType));
                        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: Switching to Context Dump.\r\n"));
                    }
                    break;
                case ceDumpTypeContext:
                    // We should not get here, since the Context dump should always limit itself to WATSON_MINIMUM_DUMPFILE_SIZE
                    KD_ASSERT(dwFileOffset <= dwMaxDumpFileSizeForType); 
                    ceDumpTypeOptimized = ceDumpTypeUndefined;
                    DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: Context Dump too large (%u), Max Dump Size = %u\r\n", dwFileOffset, dwMaxDumpFileSizeForType));
                    DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: Switching to Undefined Dump (None).\r\n"));
                    break;
                case ceDumpTypeUndefined:
                default:
                    // This should not happen
                    DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!OptimizeDumpFileContents: Bad Optimized Dump Type detected, ceDumpType=%u\r\n", ceDumpTypeOptimized));
                    KD_ASSERT(FALSE); 
                    hRes = E_FAIL;
                    goto Exit;
            }
        }
    }

    if (FALSE == fFileOptimized)
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: Dump file optimization failed, Max Size=%u, Optimized Size = %u\r\n", dwMaxDumpFileSize, dwFileOffset));
        if (SUCCEEDED(hRes))
        {
            // Only set this if not already set to a failure condition
            hRes = E_FAIL;
        }
        goto Exit;
    }

    DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: Dump File optimized, Requested Type = %u, Optimized Type = %u\r\n", *pceDumpType, ceDumpTypeOptimized));
    DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!OptimizeDumpFileContents: Dump File optimized, Max Size = %u, Optimized Size = %u\r\n", dwMaxDumpFileSize, dwFileOffset));

    *pceDumpType = ceDumpTypeOptimized;
    g_ceDumpType = ceDumpTypeOptimized;
    g_dwDumpFileSize = dwFileOffset;

    hRes = S_OK;
    
Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN, (L"--DwDmpGen!OptimizeDumpFileContents: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteDumpFile

    Write out the entire dump file.
----------------------------------------------------------------------------*/
static HRESULT WriteDumpFile(CEDUMP_TYPE ceDumpType, DWORD dwMaxDumpFileSize, BOOL fOnTheFly)
{
    HRESULT hRes = E_FAIL;
    DWORD dwFileOffset = 0;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteDumpFile: Enter\r\n"));

    // Optimize dump file contents, this may change the type
    hRes = OptimizeDumpFileContents(&ceDumpType, dwMaxDumpFileSize);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFile: OptimizeDumpFileContents failed, hRes=0x%08X\r\n", hRes));
    }
    else
    {
        // Write dump file contents (Call write routine with fWrite=TRUE)
        dwFileOffset = 0;
        hRes = WriteDumpFileContents(ceDumpType, &dwFileOffset, TRUE /* fWrite */, dwMaxDumpFileSize);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFile: WriteDumpFileContents failed, hRes=0x%08X\r\n", hRes));
        }
    }
    
    if (FAILED(hRes))
    {
        DBGRETAILMSG(1,(L"  DwDmpGen!WriteDumpFile: Watson fault - trying tiny dump instead\r\n"));
        WriteTinyDumpFileContents();
    }

    if (fOnTheFly)
    {
        // Flush any remaining data to host
        hRes = WatsonWriteKDBG(0, TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteDumpFile: WatsonWriteKDBG failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
    }
    
    hRes = S_OK;

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteDumpFile: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    ReadSettings

    Read registry settings stored in reserved memory.

    Return Values:
        S_OK    - No problems.
        E_XXX   - Errors.
----------------------------------------------------------------------------*/
static HRESULT ReadSettings(CEDUMP_TYPE *pceDumpType, DWORD *pdwMaxDumpFileSize)
{
    HRESULT hRes = E_FAIL;
    DWORD dwMaxDumpFileSize;
    DWORD dwSettingsOffset;
    DWORD dwSettingsCRC;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!ReadSettings: Enter\r\n"));

    KD_ASSERT((NULL != pceDumpType) && (NULL != pdwMaxDumpFileSize));
    if ((NULL == pceDumpType) && (NULL == pdwMaxDumpFileSize))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!ReadSettings: Bad parameter passed in\r\n"));
        hRes = E_INVALIDARG;
        goto Exit;
    }

    // Get the amount of memory reserved for dump files
    g_dwReservedMemorySize = pfnNKKernelLibIoControl((HANDLE) KMOD_CORE, IOCTL_KLIB_GETWATSONSIZE, NULL, 0, NULL, 0, NULL);
    if (g_dwReservedMemorySize < WATSON_MINIMUM_RESERVED_MEMORY)
    {
        // Reserved memory is allocated by adding 'dwNKDrWatsonSize = [size to reserve];' to OEMInit().
        // The size of memory to reserve must be a multiple of VM_PAGE_SIZE bytes
        // WATSON_MINIMUM_RESERVED_MEMORY_RECOMMENDED is minimum recommended, however
        // absolute minimum to reserve is WATSON_MINIMUM_RESERVED_MEMORY
        DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!ReadSettings: Insufficient memory reserved for dump capture! Currently Reserved = %u, Minimum Required = %u\r\n", 
                                     g_dwReservedMemorySize,
                                     WATSON_MINIMUM_RESERVED_MEMORY));
        hRes = E_OUTOFMEMORY;
        goto Exit;
    }
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!ReadSettings: Reserved Memory Size=%u\r\n", g_dwReservedMemorySize));

    // Maximum dump file size is the reserved memory less the space for registry settings
    dwMaxDumpFileSize = g_dwReservedMemorySize - WATSON_REGISTRY_SETTINGS_SIZE;

    // Make sure the settings will fit in the space reserved for the settings
    KD_ASSERT((sizeof(WATSON_DUMP_SETTINGS) + sizeof(DWORD))<= WATSON_REGISTRY_SETTINGS_SIZE);

    // The Watson dump settings are at the end of the reserved memory
    dwSettingsOffset = g_dwReservedMemorySize - WATSON_REGISTRY_SETTINGS_SIZE;

    // Reset the read offset and CRC
    g_dwReadOffset = dwSettingsOffset;
    g_dwReadCRC = 0;

    // Read in the dump settings
    hRes = WatsonReadData(dwSettingsOffset, &g_watsonDumpSettings, sizeof(WATSON_DUMP_SETTINGS), TRUE /* Calculate CRC */);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!ReadSettings: WatsonReadData failed reading dump settings, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    dwSettingsOffset += sizeof (WATSON_DUMP_SETTINGS);
    
    // Read in the CRC to make sure the settings are valid
    hRes = WatsonReadData(dwSettingsOffset, &dwSettingsCRC, sizeof(DWORD), FALSE /* Don't calculate CRC */);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!ReadSettings: WatsonReadData failed getting dump settings CRC, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    // Check if Header is correct size and CRC is valid
    if ((dwSettingsCRC != g_dwReadCRC) || (g_watsonDumpSettings.dwSizeOfHeader != sizeof(WATSON_DUMP_SETTINGS)))
    {
        DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!ReadSettings: Dump Settings CRC or size failure!, Expected CRC=0x%08X, Actual CRC=0x%08X, Expected Size=%u, Actual Size=%u\r\n", 
                                     dwSettingsCRC, g_dwReadCRC, sizeof(WATSON_DUMP_SETTINGS), g_watsonDumpSettings.dwSizeOfHeader));
        g_dwLastError = ERROR_CRC;
        hRes = E_FAIL;
        goto Exit;
    }

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!ReadSettings: Dump Settings validated, CRC=0x%08X, Header Size=%u, Max Dump Size=%u\r\n", 
                                dwSettingsCRC, g_watsonDumpSettings.dwSizeOfHeader, g_watsonDumpSettings.dwMaxDumpFileSize));
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!ReadSettings: Dump Settings validated, Dump CRC=0x%08X, Dump Size=%u\r\n", 
                                g_watsonDumpSettings.dwDumpFileCRC, g_watsonDumpSettings.dwDumpWritten));

    if (!g_watsonDumpSettings.fDumpEnabled)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!ReadSettings: Dump generation disabled, quiting ... \r\n"));
        g_dwLastError = ERROR_SERVICE_DISABLED;
        hRes = E_FAIL;
        goto Exit;
    }

    if (g_watsonDumpSettings.dwDumpWritten > 0)
    {
        // We check for > 0, not >= WATSON_MINIMUM_DUMPFILE_SIZE, since the written size could be smaller than the defined minimum
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!ReadSettings: Dump file (%u Bytes) already in reserved memory, quiting ... \r\n", g_watsonDumpSettings.dwDumpWritten));
        if (g_hEventDumpFileReady)
        {
            // Tell DwXfer.dll that a dump file is ready, just in case it missed the event last time?
            DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!ReadSettings: Set 'Dump File Ready' Event\r\n"));
            SafeSetEvent(g_hEventDumpFileReady);
        }
        g_dwLastError = ERROR_ALREADY_EXISTS;
        hRes = E_FAIL;
        goto Exit;
    }

    // Use the registry setting for Dump Type if it is valid, otherwise default
    if ((g_watsonDumpSettings.ceDumpType >= ceDumpTypeContext) && (g_watsonDumpSettings.ceDumpType <= ceDumpTypeComplete))
    {
        *pceDumpType = g_watsonDumpSettings.ceDumpType;
    }
    else
    {
        // Default setting
        *pceDumpType = (CEDUMP_TYPE)WATSON_REGVALUE_DUMP_TYPE_DEFAULT_DATA;
        DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!ReadSettings: Invalid Dump Type (%u) specified, using default System Dump (%u)\r\n", g_watsonDumpSettings.ceDumpType, *pceDumpType));
    }
    
    // Use the registry setting for Dump File Size if it is valid, otherwise default
    if ((g_watsonDumpSettings.dwMaxDumpFileSize >= WATSON_CONTEXT_DUMPFILE_SIZE) && (g_watsonDumpSettings.dwMaxDumpFileSize <= dwMaxDumpFileSize))
    {
        *pdwMaxDumpFileSize = g_watsonDumpSettings.dwMaxDumpFileSize;
    }
    else
    {
        // Default to max size allowed by reserved memory
        *pdwMaxDumpFileSize = dwMaxDumpFileSize;
        DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!ReadSettings: Invalid Max Dump File Size (%u) specified, using default (%u)\r\n", g_watsonDumpSettings.dwMaxDumpFileSize, (*pdwMaxDumpFileSize)));
    }
    
    hRes = S_OK;
    
Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!ReadSettings: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    WriteSettings

    Write settings to reserved memory.
----------------------------------------------------------------------------*/
static HRESULT WriteSettings()
{
    HRESULT hRes = E_FAIL;
    DWORD dwSettingsOffset;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!WriteSettings: Enter\r\n"));

    // g_dwReservedMemorySize should already be initialized correctly
    KD_ASSERT(g_dwReservedMemorySize >= WATSON_MINIMUM_RESERVED_MEMORY);
    if (g_dwReservedMemorySize < WATSON_MINIMUM_RESERVED_MEMORY)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteSettings: Reserved memory variable not initialized correctly! Current Value = %u, Minimum Required = %u\r\n", 
                                 g_dwReservedMemorySize,
                                 WATSON_MINIMUM_RESERVED_MEMORY));
        hRes = E_FAIL;
        goto Exit;
    }

    // Make sure the settings will fit in the space reserved for the settings
    KD_ASSERT((sizeof(WATSON_DUMP_SETTINGS) + sizeof(DWORD)) <= WATSON_REGISTRY_SETTINGS_SIZE);

    // The Watson dump settings are at the end of the reserved memory
    dwSettingsOffset = g_dwReservedMemorySize - WATSON_REGISTRY_SETTINGS_SIZE;

    // Reset the write offset and CRC
    g_dwWrittenOffset = dwSettingsOffset;
    g_dwWrittenCRC = 0;

    // Write the dump settings
    hRes = WatsonWriteData(dwSettingsOffset, &g_watsonDumpSettings, sizeof(WATSON_DUMP_SETTINGS), TRUE /* Calculate CRC */);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteSettings: WatsonWriteData failed writing dump settings, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }

    dwSettingsOffset += sizeof (WATSON_DUMP_SETTINGS);
    
    // Write the CRC to make sure the settings are valid
    hRes = WatsonWriteData(dwSettingsOffset, &g_dwWrittenCRC, sizeof(DWORD), FALSE /* Don't calculate CRC */);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!WriteSettings: WatsonWriteData failed writing dump settings CRC, hRes=0x%08X\r\n", hRes));
        goto Exit;
    }
    
    hRes = S_OK;
    
Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!WriteSettings: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    Initialize

    Initialize any variables, read registry, etc.

    Return Values:
        S_OK    - No problems.
        E_XXX   - Errors.
----------------------------------------------------------------------------*/
static HRESULT Initialize(CEDUMP_TYPE *pceDumpType, DWORD *pdwMaxDumpFileSize, BOOL fOnTheFly)
{
    HRESULT hRes = E_FAIL;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!Initialize: Enter\r\n"));

    // Initialise global dump parameters to current process and thread
    g_pDmpThread = pCurThread;
    g_pDmpProc = g_pKData->pCurPrc;

    if (fOnTheFly)
    {
        // This indicates the max we can write out, in this case the max size to send back to host
        g_dwReservedMemorySize = *pdwMaxDumpFileSize;
    }
    else
    {
        hRes = ReadSettings(pceDumpType, pdwMaxDumpFileSize);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!Initialize: ReadSettings failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
    }
    
    hRes = S_OK;

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!Initialize: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}

/*----------------------------------------------------------------------------
    Shutdown

    Do any cleanup required.
----------------------------------------------------------------------------*/
static HRESULT Shutdown(BOOL fOnTheFly)
{
    HRESULT hRes = E_FAIL;

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!Shutdown: Enter\r\n"));

    if (fOnTheFly)
    {
        DWORD dwWriteOffset = 0;
        DWORD dwDumpFileSize = g_dwWrittenOffset;
        DWORD dwDumpFileCRC = g_dwWrittenCRC;
        
        // Reset the write offset and CRC
        g_dwWrittenOffset = 0;
        g_dwWrittenCRC = 0;

        // Reset the KDBG buffer offset
        g_dwKdpBufferOffset = 0;

        hRes = WatsonWriteData(dwWriteOffset, &dwDumpFileSize, sizeof(dwDumpFileSize), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!Shutdown: WatsonWriteData failed writing dwDumpFileSize, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += sizeof(dwDumpFileSize);
        
        hRes = WatsonWriteData(dwWriteOffset, &dwDumpFileCRC, sizeof(dwDumpFileCRC), TRUE);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!Shutdown: WatsonWriteData failed writing dwDumpFileCRC, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += sizeof(dwDumpFileCRC);
        
        // Write the CRC to make sure the settings are valid
        hRes = WatsonWriteData(dwWriteOffset, &g_dwWrittenCRC, sizeof(DWORD), FALSE /* Don't calculate CRC */);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!Shutdown: WatsonWriteData failed writing CRC, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
        dwWriteOffset += sizeof(DWORD);

        g_dwKdpBufferSize = dwWriteOffset;  // Amount of result data to return to host
    }
    else
    {
        g_watsonDumpSettings.dwDumpWritten = g_dwWrittenOffset;
        g_watsonDumpSettings.dwDumpFileCRC = g_dwWrittenCRC;

        hRes = WriteSettings();
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!Shutdown: WriteSettings failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }
    }

    hRes = S_OK;

Exit:
    
    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!Shutdown: Leave, hRes=0x%08X\r\n", hRes));
    return hRes;
}


/*----------------------------------------------------------------------------
    CaptureDumpFile

    Capture dump file to persitent storage
----------------------------------------------------------------------------*/
BOOL CaptureDumpFile(PEXCEPTION_RECORD pExceptionRecord, PCONTEXT pContextRecord, BOOLEAN fSecondChance)
{
    HRESULT hRes = E_FAIL;
    DWORD dwMaxDumpFileSize = 0;
    CEDUMP_TYPE ceDumpType = ceDumpTypeUndefined;
    BOOL fCallOAL = FALSE;
    BOOL fHandled = FALSE;
    BOOL fPassedInterLock = FALSE;
    SAVED_THREAD_STATE svdThread = {0};
    EXCEPTION_INFO exceptionInfo = {0};

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"++DwDmpGen!CaptureDumpFile: Enter\r\n"));

    // This must be first check, otherwise g_lThreadsInPostMortemCapture will get out of sync
    if (InterlockedIncrement(&g_lThreadsInPostMortemCapture) > 1)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!CaptureDumpFile: Another thread already running Post Portem Capture code (Threads = %u)\r\n", g_lThreadsInPostMortemCapture));
        hRes = E_FAIL;
        goto Exit;
    }

    // Indicate we passed the InterlockedIncrement check
    fPassedInterLock = TRUE;

    // Global variable initialization must be done after the InterlockedIncrement to prevent changing by re-entrancy

    // Init to no error, will be set during processing
    g_dwLastError = ERROR_SUCCESS;

    // This should always be NULL for device capture, but make sure
    KD_ASSERT(!g_pKdpSendOsAxsResponse);
    g_pKdpSendOsAxsResponse = NULL;
   
    // Prevent other threads from running
    svdThread.bCPrio = (BYTE)GET_CPRIO (pCurThread);
    svdThread.bBPrio = (BYTE)GET_BPRIO (pCurThread);   // Save this for threads list, but don't change value for Watson
    svdThread.dwQuantum = pCurThread->dwQuantum;
    svdThread.fSaved = TRUE;

    pCurThread->dwQuantum = 0;
    SET_CPRIO (pCurThread, 0);

    // Get original exception info, may be changed later by GetCaptureDumpFileParameters()
    exceptionInfo.ExceptionPointers.ExceptionRecord = pExceptionRecord;
    exceptionInfo.ExceptionPointers.ContextRecord = pContextRecord;
    exceptionInfo.SecondChance = fSecondChance;
    
    __try
    {
        if ((NULL == pExceptionRecord) || (NULL == pContextRecord))
        {
            DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!CaptureDumpFile: Invalid parameter\r\n"));
            hRes = E_INVALIDARG;
            goto Exit;
        }

        // Save the pointers to the exception info & thread state, this will update g_psvdThread & g_pexceptionInfo
        hRes = SaveExceptionState(&exceptionInfo, &svdThread, NULL, NULL);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!CaptureDumpFile: SaveExceptionState(&exceptionInfo, ...) failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }

        // Don't create dumps for debug breaks
        if (STATUS_BREAKPOINT == pExceptionRecord->ExceptionCode)
        {
            DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!CaptureDumpFile: Breakpoint exception @0x%08X, exiting ...\r\n", CONTEXT_TO_PROGRAM_COUNTER (g_pexceptionInfo->ExceptionPointers.ContextRecord)));
            hRes = E_FAIL;
            goto Exit;
        }

        hRes = Initialize(&ceDumpType, &dwMaxDumpFileSize, FALSE /* fOnTheFly */);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!CaptureDumpFile: Initialize failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }

        // Indicate if CaptureDumpFileOnDevice API called
        g_fCaptureDumpFileOnDeviceCalled = CAPTUREDUMPFILEONDEVICE_CALLED(pExceptionRecord, *ppCaptureDumpFileOnDevice,
                                                                          *g_OsaxsData.ppKCaptureDumpFileOnDevice);

        if (!fSecondChance)
        {
            // 1st chance exception
            if (g_fCaptureDumpFileOnDeviceCalled)
            {
                // We only handle the exception when CaptureDumpFileOnDevice called & 1st chance
                fHandled = TRUE;
            }
            else
            {
                // This is a real first chance exception, which means it is the first time we've seen this
                // exception in any form.  Reset the dump state on this thread.
                pCurThread->tlsPtr[TLSSLOT_KERNEL] &= ~TLSKERN_CAPTURED_DUMP;

                if (!g_watsonDumpSettings.fDumpFirstChance)
                {
                    // 1st chance exception dump disabled
                    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!CaptureDumpFile: First chance exception, exiting ...\r\n"));
                    hRes = E_FAIL;
                    goto Exit;
                }
            }
        }
        else
        {
            // 2nd chance exception
            if (g_fCaptureDumpFileOnDeviceCalled)
            {
                // We should not get a second chance exception when calling CaptureDumpFileOnDevice
                KD_ASSERT(!g_fCaptureDumpFileOnDeviceCalled /* Should be false when fSecondChance==TRUE */);

                // Treat a 2nd chance in CaptureDumpFileOnDevice like a normal crash
                g_fCaptureDumpFileOnDeviceCalled = FALSE;
            }
            else if (g_watsonDumpSettings.fDumpFirstChance)
            {
                // 2nd chance exception with 1st chance exception dump enabled
                DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!CaptureDumpFile: Second chance exception with first chance dump enabled, exiting ...\r\n"));
                hRes = E_FAIL;
                goto Exit;
            }
        }
        // If a dump has already been generated on this thread, then do not dump again.
        if ((pCurThread->tlsPtr[TLSSLOT_KERNEL] & TLSKERN_CAPTURED_DUMP) != 0)
        {
            RETAILMSG(1,(L"  DwDmpGen!CaptureDumpFile: Repeated exception, exiting ...\r\n"));
            hRes = E_FAIL;
            goto Exit;
        }

        // Now that we are prepared to dump, mark this thread as having generated a dump.
        pCurThread->tlsPtr[TLSSLOT_KERNEL] |= TLSKERN_CAPTURED_DUMP;

        // At this point, fairly certain that dump is about to happen. Trigger a cache flush
        // to make sure that all aliased memory is properly propagated.
        // This is especially important on ARM, where most ARM hardware is running with
        // a virtual tagged cache and it's easy to get interesting problems.
        FlushCache();

        if (pfnOEMKDIoControl != NULL)
        {
            // Let KDIoControl know dump is about to start
            KCall ((PKFN)pfnOEMKDIoControl, KD_IOCTL_DMPGEN_START, NULL, g_dwReservedMemorySize);

            // Make sure we call again to say we are finished
            fCallOAL = TRUE;
        }

        // Set the process ID and Thread ID for dumping
        // This function may change the state of g_fCaptureDumpFileOnDeviceCalled
        hRes = GetCaptureDumpFileParameters(&exceptionInfo);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!CaptureDumpFile: GetCaptureDumpFileParameters failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }

        // Flush the FPU registers before writing the dump file
        KdpFlushExtendedContext (pContextRecord);

        // Write the dump file to reserved memory
        hRes = WriteDumpFile(ceDumpType, dwMaxDumpFileSize, FALSE /* fOnTheFly */);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!CaptureDumpFile: WriteDumpFile failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }

        hRes = Shutdown(FALSE /* fOnTheFly */);
        if (FAILED(hRes))
        {
            DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!CaptureDumpFile: Shutdown failed, hRes=0x%08X\r\n", hRes));
            goto Exit;
        }

        DBGRETAILMSG(1, (L"  DwDmpGen!CaptureDumpFile: Dump File captured in reserved memory\r\n"));
        if (g_hEventDumpFileReady)
        {
            // Tell DwXfer.dll that a dump file is ready
            DEBUGGERMSG(OXZONE_DWDMPGEN,(L"  DwDmpGen!CaptureDumpFile: Set 'Dump File Ready' Event\r\n"));

            // Need to switch to NK.EXE to make sure the right handle table is used for set event.
            SafeSetEvent(g_hEventDumpFileReady);
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!CaptureDumpFile: Exception occured capturing dump file, quiting ...\r\n"));
        hRes = E_FAIL;
        goto Exit;
    }

    hRes = S_OK;

Exit:    

    if (fCallOAL && (pfnOEMKDIoControl != NULL))
    {
        KCall((PKFN)pfnOEMKDIoControl, KD_IOCTL_DMPGEN_END, NULL, hRes);
    }

    if (fPassedInterLock)
    {
        // Set global pointers to back to NULL
        HRESULT hRes2 = SaveExceptionState(NULL, NULL, NULL, NULL);
        if (FAILED(hRes2))
        {
            DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!CaptureDumpFile: SaveExceptionState(NULL, ...) failed, hRes=0x%08X\r\n", hRes));
            // Continue to restore device state
        }

        // Allow other threads to run again
        pCurThread->dwQuantum = svdThread.dwQuantum;
        SET_CPRIO (pCurThread, svdThread.bCPrio);
        svdThread.fSaved = FALSE;
    }

    InterlockedDecrement(&g_lThreadsInPostMortemCapture);

    DEBUGGERMSG(OXZONE_DWDMPGEN,(L"--DwDmpGen!CaptureDumpFile: Leave, hRes=0x%08X\r\n", hRes));

    // Check for errors when using CaptureDumpFileOnDevice API
    if ((g_fCaptureDumpFileOnDeviceCalled || g_fReportFaultCalled) && fHandled && (hRes != S_OK))
    {
        if (ERROR_SUCCESS == g_dwLastError)
        {
            // If Last Error not already set, then set last error according to hRes
            switch (hRes)
            {
                case E_INVALIDARG:   g_dwLastError = ERROR_BAD_ARGUMENTS;  break;
                case E_OUTOFMEMORY:  g_dwLastError = ERROR_OUTOFMEMORY;    break;
                case E_ACCESSDENIED: g_dwLastError = ERROR_ACCESS_DENIED;  break;
                
                default:
                    g_dwLastError = ERROR_GEN_FAILURE;
                    break;
            }
        }
        SetLastError(g_dwLastError);
        DBGRETAILMSG(1, (L"  DwDmpGen!CaptureDumpFile: Error detected during capture, hRes=0x%08X, SetLastError=%u\r\n", hRes, g_dwLastError));
    }

    g_fStackBufferOverrun = FALSE;
    g_fCaptureDumpFileOnDeviceCalled = FALSE;
    g_fReportFaultCalled = FALSE;

    return ((S_OK == hRes) ? fHandled : FALSE);
}


/*++

Routine Name:

    CaptureDumpFileOnTheFly

Routine Description:

    Capture Dump File On-The-Fly request and serialize the via the caller specified buffer.

Arguments:

    pWatsonOtfRequest                - IN Request structure
    pbOutBuffer                      - IN/OUT The response buffer.
    pdwBufferSize                    - IN/OUT The size of the buffer / The size consumed by response.
    pKdpSendOsAxsResponse            - IN pointer to KDBG response sending function for serialized response

Return Value:

    S_OK : success,
    E_FAIL : general failure,
    E_INVALIDARG : Invalid argument to function

--*/

HRESULT CaptureDumpFileOnTheFly (OSAXS_API_WATSON_DUMP_OTF *pWatsonOtfRequest, BYTE *pbOutBuffer, DWORD *pdwBufferSize, OSAXS_KDBG_RESPONSE_FUNC pKdpSendOsAxsResponse)
{
    HRESULT hRes = E_FAIL;
    DWORD cbOutBuffer = pdwBufferSize ? *pdwBufferSize : 0;
    CEDUMP_TYPE ceDumpType = ceDumpTypeUndefined;
    DWORD dwMaxDumpFileSize = 0;
    DWORD dwDumpFileSize = 0;
    BOOL passedPmdGuard = FALSE;

    DEBUGGERMSG (OXZONE_DWDMPGEN, (L"++DwDmpGen!CaptureDumpFileOnTheFly: ReplyBufSize = %u\r\n", cbOutBuffer));
 
    // Even though CaptureDumpFileOnTheFly is only invoked when interactively debugging, technically
    // there is a thread now in Post Mortem Capture.
    if (InterlockedIncrement(&g_lThreadsInPostMortemCapture) > 1)
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!CaptureDumpFileOnTheFly: Another thread already running Post Portem Capture code (Threads = %u)\r\n", g_lThreadsInPostMortemCapture));
        hRes = E_FAIL;
        goto Exit;
    }

    if (!pWatsonOtfRequest || !pbOutBuffer || !pdwBufferSize || !pKdpSendOsAxsResponse ||
        !(*pdwBufferSize))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!CaptureDumpFileOnTheFly: Invalid parameter\r\n"));
        hRes = E_INVALIDARG;
        goto Exit;
    }

    if ((g_fStackBufferOverrun) ||
        (g_fCaptureDumpFileOnDeviceCalled) ||
        (g_fReportFaultCalled))
    {
        DEBUGGERMSG(OXZONE_ALERT, (L"  DwDmpGen!CaptureDumpFileOnTheFly: Can not capture dump file on-the-fly while device capture in progress!\r\n"));
        hRes = E_FAIL;
        goto Exit;
    }

    passedPmdGuard = TRUE;

    ceDumpType = (CEDUMP_TYPE)pWatsonOtfRequest->dwDumpType;
    dwMaxDumpFileSize = (pWatsonOtfRequest->dw64MaxDumpFileSize > 0xFFFFFFFF) ? 0xFFFFFFFF : (DWORD) pWatsonOtfRequest->dw64MaxDumpFileSize;
    g_pKdpSendOsAxsResponse = pKdpSendOsAxsResponse;
    g_pKdpBuffer = pbOutBuffer;
    g_dwKdpBufferSize = *pdwBufferSize;
    g_dwKdpBufferOffset = 0;
    g_dwSegmentIdx = 0;

    hRes = Initialize(&ceDumpType, &dwMaxDumpFileSize, TRUE /* fOnTheFly */);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_DWDMPGEN, (L"  DwDmpGen!CaptureDumpFileOnTheFly: Initialize failed, hRes=0x%08X\r\n", hRes));
        hRes = E_FAIL;
        goto Exit;
    }

    // Write the dump file to host via g_pKdpSendOsAxsResponse
    hRes = WriteDumpFile(ceDumpType, dwMaxDumpFileSize, TRUE /* fOnTheFly */);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!CaptureDumpFileOnTheFly: WriteDumpFile failed, hRes=0x%08X\r\n", hRes));
        hRes = E_FAIL;
        goto Exit;
    }

    // The dump file size should be the same as current written offset
    KD_ASSERT(g_dwDumpFileSize == g_dwWrittenOffset);

    pWatsonOtfRequest->dw64MaxDumpFileSize = g_dwDumpFileSize;

    // Will place some result data at g_pKdpBuffer and set g_dwKdpBufferSize to the size of data
    hRes = Shutdown(TRUE /* fOnTheFly */);
    if (FAILED(hRes))
    {
        DEBUGGERMSG(OXZONE_ALERT,(L"  DwDmpGen!CaptureDumpFileOnTheFly: Shutdown failed, hRes=0x%08X\r\n", hRes));
        hRes = E_FAIL;
        goto Exit;
    }

    pWatsonOtfRequest->dw64SegmentIndex = g_dwSegmentIdx; // Should be Unchanged
    pWatsonOtfRequest->fDumpfileComplete = TRUE;
    pWatsonOtfRequest->dwSegmentBufferSize = g_dwKdpBufferSize;

    DBGRETAILMSG(1, (L"  DwDmpGen!CaptureDumpFileOnTheFly: Dump File transfer complete, %u bytes\r\n", g_dwDumpFileSize));

    *pdwBufferSize = g_dwKdpBufferSize; // Return result data

Exit:

    if (passedPmdGuard)
    {
        g_pKdpSendOsAxsResponse = NULL;
        g_pKdpBuffer = NULL;
        g_dwKdpBufferSize = 0;
    }

    InterlockedDecrement(&g_lThreadsInPostMortemCapture);
    
    DEBUGGERMSG (OXZONE_DWDMPGEN || FAILED (hRes), (L"--DwDmpGen!CaptureDumpFileOnTheFly: hRes=0x%08x\r\n", hRes));
    return hRes;
}


