//----------------------------------------------------------------------------
//
// Stack walking support.
//
// Copyright (C) Microsoft Corporation, 1997-2001.
//
//----------------------------------------------------------------------------

#include "ntsdp.hpp"

IMAGE_IA64_RUNTIME_FUNCTION_ENTRY g_EpcRfeBuffer;
PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY g_EpcRfe;

PFPO_DATA
SynthesizeKnownFpo(PSTR Symbol, ULONG64 OffStart, ULONG64 Disp)
{
    static ULONG64 s_Nr2, s_Lu2, s_Eh3, s_Kuit;

    if (!s_Nr2 || !s_Lu2 || !s_Eh3 || !s_Kuit)
    {
        GetOffsetFromSym("nt!_NLG_Return2", &s_Nr2, NULL);
        GetOffsetFromSym("nt!_local_unwind2", &s_Lu2, NULL);
        GetOffsetFromSym("nt!_except_handler3", &s_Eh3, NULL);
        GetOffsetFromSym("nt!KiUnexpectedInterruptTail", &s_Kuit, NULL);
    }
        
    if (OffStart == s_Nr2 || OffStart == s_Lu2)
    {
        static FPO_DATA s_Lu2Fpo;

        s_Lu2Fpo.ulOffStart = (ULONG)OffStart;
        s_Lu2Fpo.cbProcSize = 0x68;
        s_Lu2Fpo.cdwLocals  = 4;
        s_Lu2Fpo.cdwParams  = 0;
        s_Lu2Fpo.cbProlog   = 0;
        s_Lu2Fpo.cbRegs     = 3;
        s_Lu2Fpo.fHasSEH    = 0;
        s_Lu2Fpo.fUseBP     = 0;
        s_Lu2Fpo.reserved   = 0;
        s_Lu2Fpo.cbFrame    = FRAME_FPO;
        return &s_Lu2Fpo;
    }
    else if (OffStart == s_Eh3)
    {
        static FPO_DATA s_Eh3Fpo;

        s_Eh3Fpo.ulOffStart = (ULONG)OffStart;
        s_Eh3Fpo.cbProcSize = 0xbd;
        s_Eh3Fpo.cdwLocals  = 2;
        s_Eh3Fpo.cdwParams  = 4;
        s_Eh3Fpo.cbProlog   = 3;
        s_Eh3Fpo.cbRegs     = 4;
        s_Eh3Fpo.fHasSEH    = 0;
        s_Eh3Fpo.fUseBP     = 0;
        s_Eh3Fpo.reserved   = 0;
        s_Eh3Fpo.cbFrame    = FRAME_NONFPO;
        return &s_Eh3Fpo;
    }
    else if (OffStart == s_Kuit)
    {
        //
        // KiUnexpectedInterruptTail has three special stubs
        // following it for CommonDispatchException[0-2]Args.
        // These stubs set up for the appropriate number of
        // arguments and then call CommonDispatchException.
        // They do not have symbols or FPO data so fake some
        // up if we're in the region immediately after KUIT.
        //
        
        PFPO_DATA KuitData = (PFPO_DATA)
            SymFunctionTableAccess(g_CurrentProcess->Handle, OffStart);
        if (KuitData != NULL &&
            Disp >= (ULONG64)KuitData->cbProcSize &&
            Disp < (ULONG64)KuitData->cbProcSize + 0x20)
        {
            static FPO_DATA s_CdeStubFpo;
            
            s_CdeStubFpo.ulOffStart = (ULONG)OffStart;
            s_CdeStubFpo.cbProcSize = 0x10;
            s_CdeStubFpo.cdwLocals  = 0;
            s_CdeStubFpo.cdwParams  = 0;
            s_CdeStubFpo.cbProlog   = 0;
            s_CdeStubFpo.cbRegs     = 0;
            s_CdeStubFpo.fHasSEH    = 0;
            s_CdeStubFpo.fUseBP     = 0;
            s_CdeStubFpo.reserved   = 0;
            s_CdeStubFpo.cbFrame    = FRAME_TRAP;
            return &s_CdeStubFpo;
        }
    }

    return NULL;
}
    
PFPO_DATA
SynthesizeFpoDataForModule(DWORD64 PCAddr)
{
    DWORD64     Offset;
    USHORT      StdCallArgs;
    CHAR        symbuf[MAX_SYMBOL_LEN];

    GetSymbolStdCall( PCAddr,
                      symbuf,
                      sizeof(symbuf),
                      &Offset,
                      &StdCallArgs);

    if (Offset == PCAddr)
    {
        // No symbol.
        return NULL;
    }

    PFPO_DATA KnownFpo =
        SynthesizeKnownFpo(symbuf, PCAddr - Offset, Offset);
    if (KnownFpo != NULL)
    {
        return KnownFpo;
    }
    
    if (StdCallArgs == 0xffff)
    {
        return NULL;
    }

    static FPO_DATA s_SynthFpo;
    
    s_SynthFpo.ulOffStart = (ULONG)(PCAddr - Offset);
    s_SynthFpo.cbProcSize = (ULONG)(Offset + 10);
    s_SynthFpo.cdwLocals  = 0;
    s_SynthFpo.cdwParams  = StdCallArgs;
    s_SynthFpo.cbProlog   = 0;
    s_SynthFpo.cbRegs     = 0;
    s_SynthFpo.fHasSEH    = 0;
    s_SynthFpo.fUseBP     = 0;
    s_SynthFpo.reserved   = 0;
    s_SynthFpo.cbFrame    = FRAME_NONFPO;
    return &s_SynthFpo;
}

PFPO_DATA
SynthesizeFpoDataForFastSyscall(ULONG64 Offset)
{
    static FPO_DATA s_FastFpo;
    
    // XXX drewb - Temporary until the fake user-shared
    // module is worked out.
    
    s_FastFpo.ulOffStart = (ULONG)Offset;
    s_FastFpo.cbProcSize = X86_SHARED_SYSCALL_SIZE;
    s_FastFpo.cdwLocals  = 0;
    s_FastFpo.cdwParams  = 0;
    s_FastFpo.cbProlog   = 0;
    s_FastFpo.cbRegs     = 0;
    s_FastFpo.fHasSEH    = 0;
    s_FastFpo.fUseBP     = 0;
    s_FastFpo.reserved   = 0;
    s_FastFpo.cbFrame    = FRAME_FPO;
    return &s_FastFpo;
}    

PFPO_DATA
ModifyFpoRecord(PDEBUG_IMAGE_INFO Image, PFPO_DATA FpoData)
{
    if (FpoData->cdwLocals == 80)
    {
        static ULONG64 s_CommonDispatchException;

        // Some versions of CommonDispatchException have
        // the wrong locals size, which screws up stack
        // traces.  Detect and fix up these problems.
        if (s_CommonDispatchException == 0)
        {
            GetOffsetFromSym("nt!CommonDispatchException",
                             &s_CommonDispatchException,
                             NULL);
        }
                
        if (Image->BaseOfImage + FpoData->ulOffStart ==
            s_CommonDispatchException)
        {
            static FPO_DATA s_CdeFpo;
                    
            s_CdeFpo = *FpoData;
            s_CdeFpo.cdwLocals = 20;
            FpoData = &s_CdeFpo;
        }
    }
    else if (FpoData->cdwLocals == 0 && FpoData->cdwParams == 0 &&
             FpoData->cbRegs == 3)
    {
        static ULONG64 s_KiSwapThread;

        // KiSwapThread has shrink-wrapping so that three registers
        // are pushed in only a portion of the code.  Unfortunately,
        // the most important place in the code -- the call to
        // KiSwapContext -- is outside of this region and therefore
        // the register count is wrong much more often than it's
        // correct.  Switch the register count to two to make it
        // correct more often than wrong.
        if (s_KiSwapThread == 0)
        {
            GetOffsetFromSym("nt!KiSwapThread", &s_KiSwapThread, NULL);
        }

        if (Image->BaseOfImage + FpoData->ulOffStart ==
            s_KiSwapThread)
        {
            static FPO_DATA s_KstFpo;

            s_KstFpo = *FpoData;
            s_KstFpo.cbRegs = 2;
            FpoData = &s_KstFpo;
        }
    }
    else if (FpoData->fHasSEH)
    {
        static FPO_DATA s_SehFpo;

        s_SehFpo = *FpoData;
        s_SehFpo.cbFrame = FRAME_NONFPO;
        FpoData = &s_SehFpo;
    }

    return FpoData;
}

PFPO_DATA
FindFpoDataForModule(DWORD64 PCAddr)
/*++

Routine Description:

    Locates the fpo data structure in the process's linked list for the
    requested module.

Arguments:

    PCAddr        - address contained in the program counter

Return Value:

    null            - could not locate the entry
    valid address   - found the entry at the adress retured

--*/
{
    PPROCESS_INFO Process;
    PDEBUG_IMAGE_INFO Image;
    PFPO_DATA FpoData;

    Process = g_CurrentProcess;
    Image = Process->ImageHead;
    FpoData = 0;
    while (Image)
    {
        if ((PCAddr >= Image->BaseOfImage) &&
            (PCAddr < Image->BaseOfImage + Image->SizeOfImage))
        {
            FpoData = (PFPO_DATA)
                SymFunctionTableAccess(g_CurrentProcess->Handle, PCAddr);
            if (!FpoData)
            {
                FpoData = SynthesizeFpoDataForModule(PCAddr);
            }
            else
            {
                FpoData = ModifyFpoRecord(Image, FpoData);
            }
            
            return FpoData;
        }
        
        Image = Image->Next;
    }

    ULONG64 FscBase;
    
    switch(IsInFastSyscall(PCAddr, &FscBase))
    {
    case FSC_FOUND:
        return SynthesizeFpoDataForFastSyscall(FscBase);
    }
    
    // the function is not part of any known loaded image
    return NULL;
}

LPVOID
SwFunctionTableAccess(
    HANDLE  hProcess,
    ULONG64 AddrBase
    )
{
    static IMAGE_ALPHA_RUNTIME_FUNCTION_ENTRY s_Axp32;
    static IMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY s_Axp64;
    static IMAGE_IA64_RUNTIME_FUNCTION_ENTRY s_Ia64;
    static _IMAGE_RUNTIME_FUNCTION_ENTRY s_Amd64;

    PVOID pife;

    if (g_EffMachine == IMAGE_FILE_MACHINE_I386)
    {
        return (LPVOID)FindFpoDataForModule( AddrBase );
    }

    pife = SymFunctionTableAccess64(hProcess, AddrBase);

    switch (g_EffMachine)
    {
    case IMAGE_FILE_MACHINE_AXP64:
        if (!pife)
        {
            return NULL;
        }

        s_Axp64.BeginAddress =
            ((PIMAGE_FUNCTION_ENTRY64)pife)->StartingAddress;
        s_Axp64.EndAddress =
            ((PIMAGE_FUNCTION_ENTRY64)pife)->EndingAddress;
        s_Axp64.ExceptionHandler = 0;
        s_Axp64.HandlerData = 0;
        s_Axp64.PrologEndAddress =
            ((PIMAGE_FUNCTION_ENTRY64)pife)->EndOfPrologue;
        return &s_Axp64;

    case IMAGE_FILE_MACHINE_ALPHA:
        if (!pife)
        {
            return NULL;
        }

        s_Axp32.BeginAddress =
            ((PIMAGE_FUNCTION_ENTRY)pife)->StartingAddress;
        s_Axp32.EndAddress =
            ((PIMAGE_FUNCTION_ENTRY)pife)->EndingAddress;
        s_Axp32.ExceptionHandler = 0;
        s_Axp32.HandlerData = 0;
        s_Axp32.PrologEndAddress =
            ((PIMAGE_FUNCTION_ENTRY)pife)->EndOfPrologue;
        return &s_Axp32;

    case IMAGE_FILE_MACHINE_IA64:
        if (pife)
        {
            s_Ia64 = *(PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY)pife;
            return &s_Ia64;
        }
        else
        {
            if (IS_KERNEL_TARGET() &&
                (AddrBase >= IA64_MM_EPC_VA) &&
                (AddrBase < (IA64_MM_EPC_VA + IA64_PAGE_SIZE)))
            {
                return g_EpcRfe;
            }
            else
            {
                return NULL;
            }
        }
        break;

    case IMAGE_FILE_MACHINE_AMD64:
        if (pife)
        {
            s_Amd64 = *(_PIMAGE_RUNTIME_FUNCTION_ENTRY)pife;
            return &s_Amd64;
        }
        break;
    }

    return NULL;
}

DWORD64
SwTranslateAddress(
    HANDLE    hProcess,
    HANDLE    hThread,
    LPADDRESS64 lpaddress
    )
{
    //
    // don't support 16bit stacks
    //
    return 0;
}


BOOL
SwReadMemory32(
    HANDLE hProcess,
    ULONG dwBaseAddress,
    LPVOID lpBuffer,
    DWORD nSize,
    LPDWORD lpNumberOfBytesRead
    )
{
    return SwReadMemory(hProcess,
                        EXTEND64(dwBaseAddress),
                        lpBuffer,
                        nSize,
                        lpNumberOfBytesRead);
}

BOOL
SwReadMemory(
    HANDLE  hProcess,
    ULONG64 BaseAddress,
    LPVOID  lpBuffer,
    DWORD   nSize,
    LPDWORD lpNumberOfBytesRead
    )
{
    DBG_ASSERT(hProcess == g_CurrentProcess->Handle);

    if (IS_KERNEL_TARGET())
    {
        DWORD   BytesRead;
        HRESULT Status;

        if ((LONG_PTR)lpNumberOfBytesRead == -1)
        {
            if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386)
            {
                BaseAddress += g_TargetMachine->m_SizeTargetContext;
            }
    
            Status = g_Target->ReadControl(CURRENT_PROC,
                                           (ULONG)BaseAddress,
                                           lpBuffer,
                                           nSize,
                                           &BytesRead);
            return Status == S_OK;
        }
    }

    if (g_Target->ReadVirtual(BaseAddress, lpBuffer, nSize,
                              lpNumberOfBytesRead) != S_OK)
    {
        // Make sure bytes read is zero.
        if (lpNumberOfBytesRead != NULL)
        {
            *lpNumberOfBytesRead = 0;
        }
        return FALSE;
    }
    else
    {
        return TRUE;
    }
}

DWORD64
SwGetModuleBase(
    HANDLE  hProcess,
    ULONG64 Address
    )
{
    PDEBUG_IMAGE_INFO Image = g_CurrentProcess->ImageHead;

    if (g_EffMachine == IMAGE_FILE_MACHINE_IA64 &&
        IS_KERNEL_TARGET() &&
        (Address >= IA64_MM_EPC_VA) &&
        (Address < (IA64_MM_EPC_VA + IA64_PAGE_SIZE)))
    {
        Address -= (IA64_MM_EPC_VA - g_SystemCallVirtualAddress);
    }

    while (Image)
    {
        if ((Address >= Image->BaseOfImage) &&
            (Address < (Image->BaseOfImage + Image->SizeOfImage)))
        {
            return Image->BaseOfImage;
        }
        Image = Image->Next;
    }

    // If no regular module was found we need to look in
    // the dynamic function tables to see if an entry
    // there matches.
    ULONG64 DynBase = g_Target->
        GetDynamicFunctionTableBase(g_Machine, Address);
    if (DynBase)
    {
        return DynBase;
    }
    
    if (IS_KERNEL_TARGET())
    {
        // If no modules have been loaded there's still a possibility
        // of getting a kernel stack trace (without symbols) by going
        // after the module base directly. This also makes it possible
        // to get a stack trace when there are no symbols available.

        if (g_CurrentProcess->ImageHead == NULL)
        {
            return GetKernelModuleBase( Address );
        }
    }

    return 0;
}

DWORD
SwGetModuleBase32(
    HANDLE hProcess,
    DWORD Address
    )
{
    return (DWORD)SwGetModuleBase(hProcess, Address);
}


void
PrintStackTraceHeaderLine(
   ULONG Flags
   )
{
    if ( (Flags & DEBUG_STACK_COLUMN_NAMES) == 0 )
    {
        return;
    }

    StartOutLine(DEBUG_OUTPUT_NORMAL, OUT_LINE_NO_TIMESTAMP);

    if (Flags & DEBUG_STACK_FRAME_NUMBERS)
    {
        dprintf(" # ");
    }
    
    if (Flags & DEBUG_STACK_FRAME_ADDRESSES)
    {
        g_Machine->PrintStackFrameAddressesTitle(Flags);
    }

    if (Flags & DEBUG_STACK_ARGUMENTS)
    {
        g_Machine->PrintStackArgumentsTitle(Flags);
    }

    g_Machine->PrintStackCallSiteTitle(Flags);

    dprintf("\n");
}

VOID
PrintStackFrame(
    PDEBUG_STACK_FRAME StackFrame,
    ULONG              Flags
    )
{
    DWORD64       displacement;
    CHAR          symbuf[MAX_SYMBOL_LEN];
    USHORT        StdCallArgs;
    ULONG64       InstructionOffset = StackFrame->InstructionOffset;

    if (g_EffMachine == IMAGE_FILE_MACHINE_IA64 &&
        IS_KERNEL_TARGET() &&
        (InstructionOffset >= IA64_MM_EPC_VA) &&
        (InstructionOffset < (IA64_MM_EPC_VA + IA64_PAGE_SIZE)))
    {
        InstructionOffset = InstructionOffset -
                            (IA64_MM_EPC_VA - g_SystemCallVirtualAddress);
    }

    GetSymbolStdCall(InstructionOffset,
                     symbuf,
                     sizeof(symbuf),
                     &displacement,
                     &StdCallArgs);

    
    StartOutLine(DEBUG_OUTPUT_NORMAL, OUT_LINE_NO_TIMESTAMP);

    if (Flags & DEBUG_STACK_FRAME_NUMBERS)
    {
        dprintf("%02lx ", StackFrame->FrameNumber);
    }

    if (Flags & DEBUG_STACK_FRAME_ADDRESSES)
    {
        g_Machine->PrintStackFrameAddresses(Flags, StackFrame);
    }
    
    if (Flags & DEBUG_STACK_ARGUMENTS)
    {
        g_Machine->PrintStackArguments(Flags, StackFrame);
    }

    g_Machine->PrintStackCallSite(Flags, StackFrame, 
                                  symbuf, displacement, 
                                  StdCallArgs);

    if (Flags & DEBUG_STACK_SOURCE_LINE)
    {
        OutputLineAddr(InstructionOffset, " [%s @ %d]");
    }

    dprintf( "\n" );
}

VOID
PrintStackTrace(
    ULONG              NumFrames,
    PDEBUG_STACK_FRAME StackFrames,
    ULONG              Flags
    )
{
    ULONG i;

    PrintStackTraceHeaderLine(Flags);

    for (i = 0; i < NumFrames; i++)
    {
        PrintStackFrame(StackFrames + i, Flags);
    }
}

void
GetStkTraceArgsForCurrentScope(
    PULONG64 FramePointer,
    PULONG64 StackPointer,
    PULONG64 InstructionPointer,
    PCROSS_PLATFORM_CONTEXT ContextCopyPointer
    )
{
    PCROSS_PLATFORM_CONTEXT ScopeContext = GetCurrentScopeContext();
    if (ScopeContext != NULL)
    {
        g_Machine->PushContext(ScopeContext);

        switch (g_EffMachine)
        { 
        case IMAGE_FILE_MACHINE_I386:
            if (*InstructionPointer == 0) 
            {
                *InstructionPointer = g_Machine->GetReg64(X86_EIP);
            }
            if (*StackPointer == 0) 
            {
                *StackPointer = g_Machine->GetReg64(X86_ESP);
            }
            if (*FramePointer == 0) 
            {
                *FramePointer = g_Machine->GetReg64(X86_EBP);
            }
            break;

        case IMAGE_FILE_MACHINE_IA64:
            *InstructionPointer = g_Machine->GetReg64(STIIP);
            *StackPointer = g_Machine->GetReg64(INTSP);
            *FramePointer = g_Machine->GetReg64(RSBSP);
            break;

        case IMAGE_FILE_MACHINE_AXP64:
        case IMAGE_FILE_MACHINE_ALPHA:
            *InstructionPointer = g_Machine->GetReg64(ALPHA_FIR);
            *StackPointer = g_Machine->GetReg64(SP_REG);
            *FramePointer = g_Machine->GetReg64(FP_REG);
            break;

        case IMAGE_FILE_MACHINE_AMD64:
            *InstructionPointer = g_Machine->GetReg64(AMD64_RIP);
            *StackPointer = g_Machine->GetReg64(AMD64_RSP);
            *FramePointer = g_Machine->GetReg64(AMD64_RBP);
            break;
            
        default:
            break;
        } 

        if (ContextCopyPointer) 
        {
            *ContextCopyPointer = g_Machine->m_Context;
        }
        g_Machine->PopContext();
    }
    else 
    {
        if (ContextCopyPointer) 
        {
            *ContextCopyPointer = g_Machine->m_Context;
        }
    }
}

DWORD
StackTrace(
    ULONG64            FramePointer,
    ULONG64            StackPointer,
    ULONG64            InstructionPointer,
    PDEBUG_STACK_FRAME StackFrames,
    ULONG              NumFrames,
    ULONG64            ExtThread,
    ULONG              Flags,
    BOOL               EstablishingScope
    )
{
    STACKFRAME64  VirtualFrame;
    DWORD         i;
    CROSS_PLATFORM_CONTEXT Context;
    PVOID FunctionEntry;
    ULONG64 Value;
    ULONG result;
    BOOL SymWarning = FALSE;
    ULONG X86Ebp;

    if (!EstablishingScope)
    {
        RequireCurrentScope();
    }
    
    //
    // let's start clean
    //
    ZeroMemory( StackFrames, sizeof(StackFrames[0]) * NumFrames );
    ZeroMemory( &VirtualFrame, sizeof(VirtualFrame) );

    if (g_Machine->GetContextState(MCTX_FULL) != S_OK)
    {
        return 0;
    }

    ULONG Seg;

    if ((!FramePointer && !InstructionPointer && !StackPointer) ||
        (g_EffMachine == IMAGE_FILE_MACHINE_I386))
    {
        // Do the default stack trace for current debug context
        // For x86, set these if any of them is 0
        GetStkTraceArgsForCurrentScope(&FramePointer, &StackPointer, 
                                       &InstructionPointer, &Context);
    }

    if (IS_KERNEL_TARGET())
    {
        //
        // if debugger was initialized at boot, usermode addresses needed for
        // stack traces on IA64 were not available.  Try it now:
        //

        if (g_EffMachine == IMAGE_FILE_MACHINE_IA64 &&
            !KdDebuggerData.KeUserCallbackDispatcher)
        {
            VerifyKernelBase (FALSE);
        }

        ULONG64 ThreadData;

        // If no explicit thread is given then we use the
        // current thread.  However, the current thread is only
        // valid if the current thread is the event thread since
        // tracing back into user mode requires that the appropriate
        // user-mode memory state be active.
        if (ExtThread != 0)
        {
            ThreadData = ExtThread;
        }
        else if (g_CurrentProcess->CurrentThread != g_EventThread ||
                 g_CurrentProcess != g_EventProcess ||
                 GetImplicitThreadData(&ThreadData) != S_OK)
        {
            ThreadData = 0;
        }

        VirtualFrame.KdHelp.Thread = ThreadData;
        VirtualFrame.KdHelp.ThCallbackStack = ThreadData ?
            KdDebuggerData.ThCallbackStack : 0;
        VirtualFrame.KdHelp.KiCallUserMode = KdDebuggerData.KiCallUserMode;
        VirtualFrame.KdHelp.NextCallback = KdDebuggerData.NextCallback;
        VirtualFrame.KdHelp.KeUserCallbackDispatcher =
            KdDebuggerData.KeUserCallbackDispatcher;
        VirtualFrame.KdHelp.FramePointer = KdDebuggerData.FramePointer;
        VirtualFrame.KdHelp.SystemRangeStart = g_SystemRangeStart;
    }
    
    //
    // setup the program counter
    //
    if (!InstructionPointer)
    {
        if (g_EffMachine == IMAGE_FILE_MACHINE_I386)
        {
            ADDR Addr;

            VirtualFrame.AddrPC.Mode = AddrModeFlat;
            g_Machine->GetPC(&Addr);
            VirtualFrame.AddrPC.Segment = Addr.seg;
            VirtualFrame.AddrPC.Offset = Flat(Addr);
        }
    }
    else
    {
        VirtualFrame.AddrPC.Mode = AddrModeFlat;
        Seg = g_Machine->GetSegRegNum(SEGREG_CODE);
        VirtualFrame.AddrPC.Segment =
            Seg ? (WORD)GetRegVal32(Seg) : 0;
        VirtualFrame.AddrPC.Offset = InstructionPointer;
    }

    //
    // setup the frame pointer
    //
    if (!FramePointer)
    {
        if (g_EffMachine == IMAGE_FILE_MACHINE_I386)
        {
            ADDR Addr;

            VirtualFrame.AddrFrame.Mode = AddrModeFlat;
            g_Machine->GetFP(&Addr);
            VirtualFrame.AddrFrame.Segment = Addr.seg;
            VirtualFrame.AddrFrame.Offset = Flat(Addr);
        }
    }
    else
    {
        VirtualFrame.AddrFrame.Mode = AddrModeFlat;
        Seg = g_Machine->GetSegRegNum(SEGREG_STACK);
        VirtualFrame.AddrFrame.Segment =
            Seg ? (WORD)GetRegVal32(Seg) : 0;
        VirtualFrame.AddrFrame.Offset = FramePointer;
    }
    VirtualFrame.AddrBStore = VirtualFrame.AddrFrame;
    if (g_EffMachine == IMAGE_FILE_MACHINE_I386)
    {
        X86Ebp =  (ULONG) VirtualFrame.AddrFrame.Offset;
    }

    //
    // setup the stack pointer
    //
    if (!StackPointer)
    {
        if (g_EffMachine == IMAGE_FILE_MACHINE_I386)
        {
            ADDR Addr;

            VirtualFrame.AddrStack.Mode = AddrModeFlat;
            g_Machine->GetSP(&Addr);
            VirtualFrame.AddrStack.Segment = Addr.seg;
            VirtualFrame.AddrStack.Offset = Flat(Addr);
        }
    }
    else
    {
        VirtualFrame.AddrStack.Mode = AddrModeFlat;
        Seg = g_Machine->GetSegRegNum(SEGREG_STACK);
        VirtualFrame.AddrStack.Segment =
            Seg ? (WORD)GetRegVal32(Seg) : 0;
        VirtualFrame.AddrStack.Offset = StackPointer;
    }

    if (g_EffMachine == IMAGE_FILE_MACHINE_IA64 &&
        IS_KERNEL_TARGET() &&
        g_SystemCallVirtualAddress)
    {
        PVOID functionEntry;
        ULONG numberOfBytesRead;

        functionEntry = SwFunctionTableAccess (g_CurrentProcess->Handle,
                                               g_SystemCallVirtualAddress);
        if (functionEntry != NULL)
        {
            RtlCopyMemory(&g_EpcRfeBuffer, functionEntry,
                          sizeof(IMAGE_IA64_RUNTIME_FUNCTION_ENTRY));
            g_EpcRfe = &g_EpcRfeBuffer;
        }
        else
        {
            g_EpcRfe = NULL;
        }
    }

    for (i = 0; i < NumFrames; i++)
    {
        // SwReadMemory doesn't currently use the thread handle
        // but send in something reasonable in case of future changes.
        if (!StackWalk64(g_EffMachine,
                         g_CurrentProcess->Handle,
                         OS_HANDLE(g_CurrentProcess->CurrentThread->Handle),
                         &VirtualFrame,
                         &Context,
                         SwReadMemory,
                         SwFunctionTableAccess,
                         SwGetModuleBase,
                         SwTranslateAddress))
        {
            break;
        }

        StackFrames[i].InstructionOffset  = VirtualFrame.AddrPC.Offset;
        StackFrames[i].ReturnOffset       = VirtualFrame.AddrReturn.Offset;
        StackFrames[i].FrameOffset        = VirtualFrame.AddrFrame.Offset;
        StackFrames[i].StackOffset        = VirtualFrame.AddrStack.Offset;
        StackFrames[i].FuncTableEntry     = (ULONG64)VirtualFrame.FuncTableEntry;
        StackFrames[i].Virtual            = VirtualFrame.Virtual;
        StackFrames[i].FrameNumber        = i;

        // NOTE - we have more reserved space in the DEBUG_STACK_FRAME
        memcpy(StackFrames[i].Reserved, VirtualFrame.Reserved,
               sizeof(VirtualFrame.Reserved));
        memcpy(StackFrames[i].Params, VirtualFrame.Params,
               sizeof(VirtualFrame.Params));

        if (g_EffMachine == IMAGE_FILE_MACHINE_IA64 &&
            IS_KERNEL_TARGET())
        {
            if ((VirtualFrame.AddrPC.Offset >= IA64_MM_EPC_VA) &&
                (VirtualFrame.AddrPC.Offset <
                 (IA64_MM_EPC_VA + IA64_PAGE_SIZE)))
            {
                VirtualFrame.AddrPC.Offset -=
                    (IA64_MM_EPC_VA - g_SystemCallVirtualAddress);
            }

            if ((i != 0) &&
                (StackFrames[i - 1].InstructionOffset >= IA64_MM_EPC_VA) &&
                (VirtualFrame.AddrPC.Offset <
                 (IA64_MM_EPC_VA + IA64_PAGE_SIZE)))
            {
                StackFrames[i - 1].ReturnOffset =
                    VirtualFrame.AddrPC.Offset;
            }
        } else if (g_EffMachine == IMAGE_FILE_MACHINE_I386)
        {
            if (StackFrames[i].FuncTableEntry) 
            {
                PFPO_DATA FpoData = (PFPO_DATA)StackFrames[i].FuncTableEntry;
                if (FpoData->cbFrame == FRAME_FPO &&
                    !FpoData->fUseBP)
                {
                    SAVE_EBP(&StackFrames[i]) = (ULONG64) (LONG64) (LONG) X86Ebp;
                }

            }
            X86Ebp = Context.X86Context.Ebp;
        }


        if (Flags && i == 0)
        {
            PrintStackTraceHeaderLine(Flags);
        }

        IMAGEHLP_MODULE64 Mod;
        
        // If the current frame's PC is in a loaded module and
        // that module does not have symbols it's very possible
        // that the stack trace will be incorrect since the
        // debugger has to guess about how to unwind the stack.
        // Non-x86 architectures have unwind info in the images
        // themselves so restrict this check to x86.
        Mod.SizeOfStruct = sizeof(Mod);
        if (!SymWarning &&
            NumFrames > 1 &&
            g_EffMachine == IMAGE_FILE_MACHINE_I386 &&
            StackFrames[i].InstructionOffset != -1 &&
            SymGetModuleInfo64(g_CurrentProcess->Handle,
                               StackFrames[i].InstructionOffset, &Mod) &&
            (Mod.SymType == SymNone || Mod.SymType == SymExport ||
             Mod.SymType == SymDeferred))
        {
            WarnOut("WARNING: Stack unwind information not available. "
                    "Following frames may be wrong.\n");
            // Only show one warning per trace.
            SymWarning = TRUE;
        }
        
        if (Flags)
        {
            PrintStackFrame(StackFrames + i, Flags);
        
            if (Flags & DEBUG_STACK_NONVOLATILE_REGISTERS)
            {
                g_Machine->PrintStackNonvolatileRegisters(Flags, 
                                                          StackFrames + i, 
                                                          &Context, i);
            }
        }
    }

    return i;

}

#define BASIC_STACK \
    (DEBUG_STACK_COLUMN_NAMES | DEBUG_STACK_FRAME_ADDRESSES | \
     DEBUG_STACK_SOURCE_LINE)

ULONG g_StackTraceTypeFlags[STACK_TRACE_TYPE_MAX] =
{
    BASIC_STACK,                                                      // Default
    BASIC_STACK | DEBUG_STACK_ARGUMENTS,                              // kb
    BASIC_STACK | DEBUG_STACK_ARGUMENTS | DEBUG_STACK_FUNCTION_INFO |
        DEBUG_STACK_NONVOLATILE_REGISTERS,                            // kv
    0,                                                                // kd
    BASIC_STACK | DEBUG_STACK_PARAMETERS,                             // kp
    BASIC_STACK | DEBUG_STACK_FRAME_NUMBERS,                          // kn
    BASIC_STACK | DEBUG_STACK_ARGUMENTS | DEBUG_STACK_FRAME_NUMBERS,  // kbn
    BASIC_STACK | DEBUG_STACK_ARGUMENTS | DEBUG_STACK_FUNCTION_INFO | 
        DEBUG_STACK_NONVOLATILE_REGISTERS | DEBUG_STACK_FRAME_NUMBERS,// kvn
    DEBUG_STACK_FRAME_NUMBERS,                                        // kdn
    BASIC_STACK | DEBUG_STACK_PARAMETERS | DEBUG_STACK_FRAME_NUMBERS  // kpn
};

VOID
DoStackTrace(
    ULONG64           FramePointer,
    ULONG64           StackPointer,
    ULONG64           InstructionPointer,
    ULONG             NumFrames,
    STACK_TRACE_TYPE  TraceType
    )
{
    PDEBUG_STACK_FRAME StackFrames;
    ULONG         NumFramesToRead;
    DWORD         FrameCount;

    if (NumFrames == 0)
    {
        NumFrames = g_DefaultStackTraceDepth;
    }

    if (TraceType == STACK_TRACE_TYPE_KD)
    {
        NumFramesToRead = 1;
    }
    else
    {
        NumFramesToRead = NumFrames;
    }

    StackFrames = (PDEBUG_STACK_FRAME)
        malloc( sizeof(StackFrames[0]) * NumFramesToRead );
    if (!StackFrames)
    {
        ErrOut( "could not allocate memory for stack trace\n" );
        return;
    }

    ULONG Flags = g_StackTraceTypeFlags[TraceType];

    if ((TraceType == STACK_TRACE_TYPE_KB) && g_Machine->m_Ptr64)
    {
        Flags |= DEBUG_STACK_FRAME_ADDRESSES_RA_ONLY;
    }

    FrameCount = StackTrace( FramePointer,
                             StackPointer,
                             InstructionPointer,
                             StackFrames,
                             NumFramesToRead,
                             0,
                             Flags,
                             FALSE
                             );

    if (FrameCount == 0)
    {
        ErrOut( "could not fetch any stack frames\n" );
        free(StackFrames);
        return;
    }

    if (TraceType == STACK_TRACE_TYPE_KD)
    {
        // Starting with the stack pointer, dump NumFrames DWORD's
        // and the symbol if possible.

        ADDR startAddr;
        ADDRFLAT(&startAddr, StackFrames[0].FrameOffset);

        fnDumpDwordMemory(&startAddr, NumFrames, TRUE);
    }

    free( StackFrames );
}