// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
//*****************************************************************************
// File: debugger.cpp
//

//
// Debugger runtime controller routines.
//
//*****************************************************************************

#include "stdafx.h"
#include "debugdebugger.h"
#include "../inc/common.h"
#include "eeconfig.h" // This is here even for retail & free builds...
#include "../../dlls/mscorrc/resource.h"

#include "vars.hpp"
#include <limits.h>
#include "ilformatter.h"
#include "typeparse.h"
#include "debuginfostore.h"
#include "generics.h"
#include "../../vm/methoditer.h"
#include "../../vm/encee.h"
#include "../../vm/dwreport.h"
#include "../../vm/eepolicy.h"
#include "../../vm/excep.h"
#if defined(FEATURE_DBGIPC_TRANSPORT_VM)
#include "dbgtransportsession.h"
#endif // FEATURE_DBGIPC_TRANSPORT_VM

#ifdef TEST_DATA_CONSISTENCY
#include "datatest.h"
#endif // TEST_DATA_CONSISTENCY

#include "dbgenginemetrics.h"

#include "../../vm/rejit.h"

#include "threadsuspend.h"


#ifdef DEBUGGING_SUPPORTED

#ifdef _DEBUG
// Reg key. We can set this and then any debugger-lazy-init code will assert.
// This helps track down places where we're caching in debugger stuff in a
// non-debugger scenario.
bool g_DbgShouldntUseDebugger = false;
#endif


/* ------------------------------------------------------------------------ *
 * Global variables
 * ------------------------------------------------------------------------ */

GPTR_IMPL(Debugger,         g_pDebugger);
GPTR_IMPL(EEDebugInterface, g_pEEInterface);
SVAL_IMPL_INIT(BOOL, Debugger, s_fCanChangeNgenFlags, TRUE);

// This is a public export so debuggers can read and determine if the coreclr
// process is waiting for JIT debugging attach.
GVAL_IMPL_INIT(ULONG, CLRJitAttachState, 0);

// The following instances are used for invoking overloaded new/delete
InteropSafe interopsafe;

#ifndef DACCESS_COMPILE

DebuggerRCThread        *g_pRCThread = NULL;

#ifndef _PREFAST_
// Do some compile time checking on the events in DbgIpcEventTypes.h
// No one ever calls this. But the compiler should still compile it,
// and that should be sufficient.
void DoCompileTimeCheckOnDbgIpcEventTypes()
{
    _ASSERTE(!"Don't call this function. It just does compile time checking\n");

    // We use the C_ASSERT macro here to get a compile-time assert.

    // Make sure we don't have any duplicate numbers.
    // The switch statements in the main loops won't always catch this
    // since we may not switch on all events.

    // store Type-0 in const local vars, so we can use them for bounds checking
    // Create local vars with the val from Type1 & Type2. If there are any
    // collisions, then the variables' names will collide at compile time.
    #define IPC_EVENT_TYPE0(type, val)  const int e_##type = val;
    #define IPC_EVENT_TYPE1(type, val)  int T_##val; T_##val = 0;
    #define IPC_EVENT_TYPE2(type, val)  int T_##val; T_##val = 0;
    #include "dbgipceventtypes.h"
    #undef IPC_EVENT_TYPE2
    #undef IPC_EVENT_TYPE1
    #undef IPC_EVENT_TYPE0

    // Ensure that all identifiers are unique and are matched with
    // integer values.
    #define IPC_EVENT_TYPE0(type, val)  int T2_##type; T2_##type = val;
    #define IPC_EVENT_TYPE1(type, val)  int T2_##type; T2_##type = val;
    #define IPC_EVENT_TYPE2(type, val)  int T2_##type; T2_##type = val;
    #include "dbgipceventtypes.h"
    #undef IPC_EVENT_TYPE2
    #undef IPC_EVENT_TYPE1
    #undef IPC_EVENT_TYPE0

    // Make sure all values are subset of the bits specified by DB_IPCE_TYPE_MASK
    #define IPC_EVENT_TYPE0(type, val)
    #define IPC_EVENT_TYPE1(type, val)  C_ASSERT((val & e_DB_IPCE_TYPE_MASK) == val);
    #define IPC_EVENT_TYPE2(type, val)  C_ASSERT((val & e_DB_IPCE_TYPE_MASK) == val);
    #include "dbgipceventtypes.h"
    #undef IPC_EVENT_TYPE2
    #undef IPC_EVENT_TYPE1
    #undef IPC_EVENT_TYPE0

    // Make sure that no value is DB_IPCE_INVALID_EVENT
    #define IPC_EVENT_TYPE0(type, val)
    #define IPC_EVENT_TYPE1(type, val)  C_ASSERT(val != e_DB_IPCE_INVALID_EVENT);
    #define IPC_EVENT_TYPE2(type, val)  C_ASSERT(val != e_DB_IPCE_INVALID_EVENT);
    #include "dbgipceventtypes.h"
    #undef IPC_EVENT_TYPE2
    #undef IPC_EVENT_TYPE1
    #undef IPC_EVENT_TYPE0

    // Make sure first-last values are well structured.
    static_assert_no_msg(e_DB_IPCE_RUNTIME_FIRST < e_DB_IPCE_RUNTIME_LAST);
    static_assert_no_msg(e_DB_IPCE_DEBUGGER_FIRST < e_DB_IPCE_DEBUGGER_LAST);

    // Make sure that event ranges don't overlap.
    // This check is simplified because L->R events come before R<-L
    static_assert_no_msg(e_DB_IPCE_RUNTIME_LAST < e_DB_IPCE_DEBUGGER_FIRST);


    // Make sure values are in the proper ranges
    // Type1 should be in the Runtime range, Type2 in the Debugger range.
    #define IPC_EVENT_TYPE0(type, val)
    #define IPC_EVENT_TYPE1(type, val)  C_ASSERT((e_DB_IPCE_RUNTIME_FIRST <= val) && (val < e_DB_IPCE_RUNTIME_LAST));
    #define IPC_EVENT_TYPE2(type, val)  C_ASSERT((e_DB_IPCE_DEBUGGER_FIRST <= val) && (val < e_DB_IPCE_DEBUGGER_LAST));
    #include "dbgipceventtypes.h"
    #undef IPC_EVENT_TYPE2
    #undef IPC_EVENT_TYPE1
    #undef IPC_EVENT_TYPE0

    // Make sure that events are in increasing order
    // It's ok if the events skip numbers.
    // This is a more specific check than the range check above.

    /* Expands to look like this:
    const bool f = (
    first <=
    10) && (10 <
    11) && (11 <
    12) && (12 <
    last)
    static_assert_no_msg(f);
    */

    const bool f1 = (
        (e_DB_IPCE_RUNTIME_FIRST <=
        #define IPC_EVENT_TYPE0(type, val)
        #define IPC_EVENT_TYPE1(type, val)  val) && (val <
        #define IPC_EVENT_TYPE2(type, val)
        #include "dbgipceventtypes.h"
        #undef IPC_EVENT_TYPE2
        #undef IPC_EVENT_TYPE1
        #undef IPC_EVENT_TYPE0
        e_DB_IPCE_RUNTIME_LAST)
    );
    static_assert_no_msg(f1);

    const bool f2 = (
        (e_DB_IPCE_DEBUGGER_FIRST <=
        #define IPC_EVENT_TYPE0(type, val)
        #define IPC_EVENT_TYPE1(type, val)
        #define IPC_EVENT_TYPE2(type, val) val) && (val <
        #include "dbgipceventtypes.h"
        #undef IPC_EVENT_TYPE2
        #undef IPC_EVENT_TYPE1
        #undef IPC_EVENT_TYPE0
        e_DB_IPCE_DEBUGGER_LAST)
    );
    static_assert_no_msg(f2);

} // end checks
#endif // _PREFAST_

//-----------------------------------------------------------------------------
// Ctor for AtSafePlaceHolder
AtSafePlaceHolder::AtSafePlaceHolder(Thread * pThread)
{
    _ASSERTE(pThread != NULL);
    if (!g_pDebugger->IsThreadAtSafePlace(pThread))
    {
        m_pThreadAtUnsafePlace = pThread;
        g_pDebugger->IncThreadsAtUnsafePlaces();
    }
    else
    {
        m_pThreadAtUnsafePlace = NULL;
    }
}

//-----------------------------------------------------------------------------
// Dtor for AtSafePlaceHolder
AtSafePlaceHolder::~AtSafePlaceHolder()
{
    Clear();
}

//-----------------------------------------------------------------------------
// Returns true if this adjusted the unsafe counter
bool AtSafePlaceHolder::IsAtUnsafePlace()
{
    return m_pThreadAtUnsafePlace != NULL;
}

//-----------------------------------------------------------------------------
// Clear the holder.
// Notes:
//    This can be called multiple times.
//    Calling this makes the dtor a nop.
void AtSafePlaceHolder::Clear()
{
    if (m_pThreadAtUnsafePlace != NULL)
    {
        // The thread is still at an unsafe place.
        // We're clearing the flag to avoid the Dtor() calling DecThreads again.
        m_pThreadAtUnsafePlace = NULL;
        g_pDebugger->DecThreadsAtUnsafePlaces();
    }
}

//-----------------------------------------------------------------------------
// Is the guard page missing on this thread?
// Should only be called for managed threads handling a managed exception.
// If we're handling a stack overflow (ie, missing guard page), then another
// stack overflow will instantly terminate the process. In that case, do stack
// intensive stuff on the helper thread (which has lots of stack space). Only
// problem is that if the faulting thread has a lock, the helper thread may
// get stuck.
// Serves as a hint whether we want to do a favor on the
// faulting thread (preferred) or the helper thread (if low stack).
// See whidbey issue 127436.
//-----------------------------------------------------------------------------
bool IsGuardPageGone()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    Thread * pThread = g_pEEInterface->GetThread();

    // We're not going to be called for a unmanaged exception.
    // Should always have a managed thread, but just in case something really
    // strange happens, it's not worth an AV. (since this is just being used as a hint)
    if (pThread == NULL)
    {
        return false;
    }

    // Don't use pThread->IsGuardPageGone(), it's not accurate here.
    bool fGuardPageGone = (pThread->DetermineIfGuardPagePresent() == FALSE);
    LOG((LF_CORDB, LL_INFO1000000, "D::IsGuardPageGone=%d\n", fGuardPageGone));
    return fGuardPageGone;
}

//-----------------------------------------------------------------------------
// LSPTR_XYZ is a type-safe wrapper around an opaque reference type XYZ in the left-side.
// But TypeHandles are value-types that can't be directly converted into a pointer.
// Thus converting between LSPTR_XYZ and TypeHandles requires some extra glue.
// The following conversions are valid:
//      LSPTR_XYZ <--> XYZ*   (via Set/UnWrap methods)
//      TypeHandle <--> void* (via AsPtr() and FromPtr()).
// so we can't directly convert between LSPTR_TYPEHANDLE and TypeHandle.
// We must do:  TypeHandle <--> void* <--> XYZ <--> LSPTR_XYZ
// So LSPTR_TYPEHANDLE is actually for TypeHandleDummyPtr, and then we unsafe cast
// that to a void* to use w/ AsPtr() and FromPtr() to convert to TypeHandles.
// @todo- it would be nice to have these happen automatically w/ Set & UnWrap.
//-----------------------------------------------------------------------------

// helper class to do conversion above.
class TypeHandleDummyPtr
{
private:
    TypeHandleDummyPtr() { }; // should never actually create this.
    void * data;
};

// Convert: VMPTR_TYPEHANDLE --> TypeHandle
TypeHandle GetTypeHandle(VMPTR_TypeHandle ptr)
{
    return TypeHandle::FromPtr(ptr.GetRawPtr());
}

// Convert: TypeHandle --> LSPTR_TYPEHANDLE
VMPTR_TypeHandle WrapTypeHandle(TypeHandle th)
{
    return VMPTR_TypeHandle::MakePtr(reinterpret_cast<TypeHandle *> (th.AsPtr()));
}

extern void WaitForEndOfShutdown();


// Get the Canary structure which can sniff if the helper thread is safe to run.
HelperCanary * Debugger::GetCanary()
{
    return g_pRCThread->GetCanary();
}

// IMPORTANT!!!!!
// Do not call Lock and Unlock directly. Because you might not unlock
// if exception takes place. Use DebuggerLockHolder instead!!!
// Only AcquireDebuggerLock can call directly.
//
void Debugger::DoNotCallDirectlyPrivateLock(void)
{
    WRAPPER_NO_CONTRACT;

    LOG((LF_CORDB,LL_INFO10000, "D::Lock acquire attempt by 0x%x\n",
        GetCurrentThreadId()));

    // Debugger lock is larger than both Controller & debugger-data locks.
    // So we should never try to take the D lock if we hold either of the others.


    // Lock becomes no-op in late shutdown.
    if (IsAtProcessExit())
    {
        return;
    }


    //
    // If the debugger has been disabled by the runtime, this means that it should block
    // all threads that are trying to travel thru the debugger.  We do this by blocking
    // threads as they try and take the debugger lock.
    //
    if (m_fDisabled)
    {
        __SwitchToThread(INFINITE, CALLER_LIMITS_SPINNING);
        _ASSERTE (!"Can not reach here");
    }

    m_mutex.Enter();

    //
    // If we were blocked on the lock and the debugging facilities got disabled
    // while we were waiting, release the lock and park this thread.
    //
    if (m_fDisabled)
    {
        m_mutex.Leave();
        __SwitchToThread(INFINITE, CALLER_LIMITS_SPINNING);
        _ASSERTE (!"Can not reach here");
    }

    //
    // Now check if we are in a shutdown case...
    //
    Thread * pThread;
    bool fIsCooperative;

    pThread = g_pEEInterface->GetThread();
    fIsCooperative = (pThread != NULL) && (pThread->PreemptiveGCDisabled());

    if (m_fShutdownMode && !fIsCooperative)
    {
        // The big fear is that some other random thread will take the debugger-lock and then block on something else,
        // and thus prevent the helper/finalizer threads from taking the debugger-lock in shutdown scenarios.
        //
        // If we're in shutdown mode, then some locks (like the Thread-Store-Lock) get special semantics.
        // Only helper / finalizer / shutdown threads can actually take these locks.
        // Other threads that try to take them will just get parked and block forever.
        // This is ok b/c the only threads that need to run at this point are the Finalizer and Helper threads.
        //
        // We need to be in preemptive to block for shutdown, so we don't do this block in Coop mode.
        // Fortunately, it's safe to take this lock in coop mode because we know the thread can't block
        // on anything interesting because we're in a GC-forbid region (see crst flags).
        m_mutex.ReleaseAndBlockForShutdownIfNotSpecialThread();
    }



#ifdef _DEBUG
    _ASSERTE(m_mutexCount >= 0);

    if (m_mutexCount>0)
    {
        if (pThread)
        {
            // mamaged thread
            _ASSERTE(m_mutexOwner == GetThreadIdHelper(pThread));
        }
        else
        {
            // unmanaged thread
            _ASSERTE(m_mutexOwner == GetCurrentThreadId());
        }
    }

    m_mutexCount++;
    if (pThread)
    {
        m_mutexOwner = GetThreadIdHelper(pThread);
    }
    else
    {
        // unmanaged thread
        m_mutexOwner = GetCurrentThreadId();
    }

    if (m_mutexCount == 1)
    {
        LOG((LF_CORDB,LL_INFO10000, "D::Lock acquired by 0x%x\n", m_mutexOwner));
    }
#endif

}

// See comment above.
// Only ReleaseDebuggerLock can call directly.
void Debugger::DoNotCallDirectlyPrivateUnlock(void)
{
    WRAPPER_NO_CONTRACT;

    // Controller lock is "smaller" than debugger lock.


    if (!IsAtProcessExit())
    {
#ifdef _DEBUG
        if (m_mutexCount == 1)
            LOG((LF_CORDB,LL_INFO10000, "D::Unlock released by 0x%x\n",
                m_mutexOwner));

        if(0 == --m_mutexCount)
            m_mutexOwner = 0;

        _ASSERTE( m_mutexCount >= 0);
#endif
        m_mutex.Leave();

        //
        // If the debugger has been disabled by the runtime, this means that it should block
        // all threads that are trying to travel thru the debugger.  We do this by blocking
        // threads also as they leave the debugger lock.
        //
        if (m_fDisabled)
        {
            __SwitchToThread(INFINITE, CALLER_LIMITS_SPINNING);
            _ASSERTE (!"Can not reach here");
        }

    }
}

#ifdef TEST_DATA_CONSISTENCY

// ---------------------------------------------------------------------------------
// Implementations for DataTest member functions
// ---------------------------------------------------------------------------------

// Send an event to the RS to signal that it should test to determine if a crst is held.
// This is for testing purposes only.
// Arguments:
//     input:  pCrst     - the lock to test
//             fOkToTake - true iff the LS does NOT currently hold the lock
//     output: none
// Notes: The RS will throw if the lock is held. The code that tests the lock will catch the
//        exception and assert if throwing was not the correct thing to do (determined via the
//        boolean). See the case for DB_IPCE_TEST_CRST in code:CordbProcess::RawDispatchEvent.
//
void DataTest::SendDbgCrstEvent(Crst * pCrst, bool fOkToTake)
{
    DebuggerIPCEvent * pLockEvent = g_pDebugger->m_pRCThread->GetIPCEventSendBuffer();

    g_pDebugger->InitIPCEvent(pLockEvent, DB_IPCE_TEST_CRST);

    pLockEvent->TestCrstData.vmCrst.SetRawPtr(pCrst);
    pLockEvent->TestCrstData.fOkToTake = fOkToTake;

    g_pDebugger->SendRawEvent(pLockEvent);

} // DataTest::SendDbgCrstEvent

// Send an event to the RS to signal that it should test to determine if a SimpleRWLock is held.
// This is for testing purposes only.
// Arguments:
//     input:  pRWLock   - the lock to test
//             fOkToTake - true iff the LS does NOT currently hold the lock
//     output: none
// Note:  The RS will throw if the lock is held. The code that tests the lock will catch the
//        exception and assert if throwing was not the correct thing to do (determined via the
//        boolean). See the case for DB_IPCE_TEST_RWLOCK in code:CordbProcess::RawDispatchEvent.
//
void DataTest::SendDbgRWLockEvent(SimpleRWLock * pRWLock, bool okToTake)
{
    DebuggerIPCEvent * pLockEvent = g_pDebugger->m_pRCThread->GetIPCEventSendBuffer();

    g_pDebugger->InitIPCEvent(pLockEvent, DB_IPCE_TEST_RWLOCK);

    pLockEvent->TestRWLockData.vmRWLock.SetRawPtr(pRWLock);
    pLockEvent->TestRWLockData.fOkToTake = okToTake;

    g_pDebugger->SendRawEvent(pLockEvent);
} // DataTest::SendDbgRWLockEvent

// Takes a series of locks in various ways and signals the RS to test the locks at interesting
// points to ensure we reliably detect when the LS holds a lock. If in the course of inspection, the
// DAC needs to execute a code path where the LS holds a lock, we assume that the locked data is in
// an inconsistent state. In this situation, we don't want to report information about this data, so
// we throw an exception.
// This is for testing purposes only.
//
// Arguments: none
// Return Value: none
// Notes: See code:CordbProcess::RawDispatchEvent for the RS part of this test and code:Debugger::Startup
//        for the LS invocation of the test.
//        The environment variable TestDataConsistency must be set to 1 to make this test run.
void DataTest::TestDataSafety()
{
    const bool okToTake = true;

    SendDbgCrstEvent(&m_crst1, okToTake);
    {
        CrstHolder ch1(&m_crst1);
        SendDbgCrstEvent(&m_crst1, !okToTake);
        {
            CrstHolder ch2(&m_crst2);
            SendDbgCrstEvent(&m_crst2, !okToTake);
            SendDbgCrstEvent(&m_crst1, !okToTake);
        }
        SendDbgCrstEvent(&m_crst2, okToTake);
        SendDbgCrstEvent(&m_crst1, !okToTake);
    }
    SendDbgCrstEvent(&m_crst1, okToTake);

    {
        SendDbgRWLockEvent(&m_rwLock, okToTake);
        SimpleReadLockHolder readLock(&m_rwLock);
        SendDbgRWLockEvent(&m_rwLock, okToTake);
    }
    SendDbgRWLockEvent(&m_rwLock, okToTake);
    {
        SimpleWriteLockHolder readLock(&m_rwLock);
        SendDbgRWLockEvent(&m_rwLock, !okToTake);
    }

} // DataTest::TestDataSafety

#endif // TEST_DATA_CONSISTENCY

#if _DEBUG
static DebugEventCounter g_debugEventCounter;
static int g_iDbgRuntimeCounter[DBG_RUNTIME_MAX];
static int g_iDbgDebuggerCounter[DBG_DEBUGGER_MAX];

void DoAssertOnType(DebuggerIPCEventType event, int count)
{
    WRAPPER_NO_CONTRACT;

    // check to see if we need fire the assertion or not.
    if ((event & 0x0300) == 0x0100)
    {
        // use the Runtime array
        if (g_iDbgRuntimeCounter[event & 0x00ff] == count)
        {
            char        tmpStr[256];
            _snprintf_s(tmpStr, ARRAY_SIZE(tmpStr), _TRUNCATE, "%s == %d, break now!",
                        IPCENames::GetName(event), count);

            // fire the assertion
            DbgAssertDialog(__FILE__, __LINE__, tmpStr);
        }
    }
    // check to see if we need fire the assertion or not.
    else if ((event & 0x0300) == 0x0200)
    {
        // use the Runtime array
        if (g_iDbgDebuggerCounter[event & 0x00ff] == count)
        {
            char        tmpStr[256];
            _snprintf_s(tmpStr, ARRAY_SIZE(tmpStr), _TRUNCATE, "%s == %d, break now!",
                        IPCENames::GetName(event), count);

            // fire the assertion
            DbgAssertDialog(__FILE__, __LINE__, tmpStr);
        }
    }

}
void DbgLogHelper(DebuggerIPCEventType event)
{
    WRAPPER_NO_CONTRACT;

    switch (event)
    {
// we don't need to handle event type 0
#define IPC_EVENT_TYPE0(type, val)
#define IPC_EVENT_TYPE1(type, val)  case type: {\
                                        g_debugEventCounter.m_iDebugCount_##type++; \
                                        DoAssertOnType(type, g_debugEventCounter.m_iDebugCount_##type); \
                                        break; \
                                    }
#define IPC_EVENT_TYPE2(type, val)  case type: { \
                                        g_debugEventCounter.m_iDebugCount_##type++; \
                                        DoAssertOnType(type, g_debugEventCounter.m_iDebugCount_##type); \
                                        break; \
                                    }
#include "dbgipceventtypes.h"
#undef IPC_EVENT_TYPE2
#undef IPC_EVENT_TYPE1
#undef IPC_EVENT_TYPE0
            default:
                break;
    }
}
#endif // _DEBUG









/* ------------------------------------------------------------------------ *
 * DLL export routine
 * ------------------------------------------------------------------------ */

Debugger *CreateDebugger(void)
{
    Debugger *pDebugger = NULL;

    EX_TRY
    {
        pDebugger = new (nothrow) Debugger();
    }
    EX_CATCH
    {
        if (pDebugger != NULL)
        {
            delete pDebugger;
            pDebugger = NULL;
        }
    }
    EX_END_CATCH(RethrowTerminalExceptions);

    return pDebugger;
}

//
// CorDBGetInterface is exported to the Runtime so that it can call
// the Runtime Controller.
//
extern "C"{
HRESULT __cdecl CorDBGetInterface(DebugInterface** rcInterface)
{
    CONTRACT(HRESULT)
    {
        NOTHROW; // use HRESULTS instead
        GC_NOTRIGGER;
        POSTCONDITION(FAILED(RETVAL) || (rcInterface == NULL) || (*rcInterface != NULL));
    }
    CONTRACT_END;

    HRESULT hr = S_OK;

    if (rcInterface != NULL)
    {
        if (g_pDebugger == NULL)
        {
            LOG((LF_CORDB, LL_INFO10,
                 "CorDBGetInterface: initializing debugger.\n"));

            g_pDebugger = CreateDebugger();
            TRACE_ALLOC(g_pDebugger);

            if (g_pDebugger == NULL)
                hr = E_OUTOFMEMORY;
        }

        *rcInterface = g_pDebugger;
    }

    RETURN hr;
}
}

//-----------------------------------------------------------------------------
// Send a pre-init IPC event and block.
// We assume the IPC event has already been initialized. There's nothing special
// here; it just used the standard formula for sending an IPC event to the RS.
// This should match up w/ the description in SENDIPCEVENT_BEGIN.
//-----------------------------------------------------------------------------
void Debugger::SendSimpleIPCEventAndBlock()
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    // BEGIN will acquire the lock (END will release it). While blocking, the
    // debugger may have detached though, so we need to check for that.
    _ASSERTE(ThreadHoldsLock());

    if (CORDebuggerAttached())
    {
        m_pRCThread->SendIPCEvent();

        // Stop all Runtime threads
        this->TrapAllRuntimeThreads();
    }
}

//-----------------------------------------------------------------------------
// Get context from a thread in managed code.
// See header for exact semantics.
//-----------------------------------------------------------------------------
CONTEXT * GetManagedStoppedCtx(Thread * pThread)
{
    WRAPPER_NO_CONTRACT;

    _ASSERTE(pThread != NULL);

    // We may be stopped or live.

    // If we're stopped at an interop-hijack, we'll have a filter context,
    // but we'd better not be redirected for a managed-suspension hijack.
    if (pThread->GetInteropDebuggingHijacked())
    {
        _ASSERTE(!ISREDIRECTEDTHREAD(pThread));
        return NULL;
    }

    // Check if we have a filter ctx. This should only be for managed-code.
    // We're stopped at some exception (likely an int3 or single-step).
    // Can't have both filter ctx + redirected ctx.
    CONTEXT *pCtx = g_pEEInterface->GetThreadFilterContext(pThread);
    if (pCtx != NULL)
    {
        _ASSERTE(!ISREDIRECTEDTHREAD(pThread));
        return pCtx;
    }

    if (ISREDIRECTEDTHREAD(pThread))
    {
        pCtx = GETREDIRECTEDCONTEXT(pThread);
        _ASSERTE(pCtx != NULL);
        return pCtx;
    }

    // Not stopped somewhere in managed code.
    return NULL;
}

//-----------------------------------------------------------------------------
// See header for exact semantics.
// Never NULL. (Caller guarantees this is active.)
//-----------------------------------------------------------------------------
CONTEXT * GetManagedLiveCtx(Thread * pThread)
{
    LIMITED_METHOD_CONTRACT;

    _ASSERTE(pThread != NULL);

    // We should never be on the helper thread, we should only be inspecting our own thread.
    // We're in some Controller's Filter after hitting an exception.
    // We're not stopped.
    //_ASSERTE(!g_pDebugger->IsStopped()); <-- @todo - this fires, need to find out why.
    _ASSERTE(GetThreadNULLOk() == pThread);

    CONTEXT *pCtx = g_pEEInterface->GetThreadFilterContext(pThread);

    // Note that we may be in a M2U hijack. So we can't assert !pThread->GetInteropDebuggingHijacked()
    _ASSERTE(!ISREDIRECTEDTHREAD(pThread));
    _ASSERTE(pCtx);

    return pCtx;
}

// Attempt to validate a GC handle.
HRESULT ValidateGCHandle(OBJECTHANDLE oh)
{
    // The only real way to do this is to Enumerate all GC handles in the handle table.
    // That's too expensive. So we'll use a similar workaround that we use in ValidateObject.
    // This will err on the side off returning True for invalid handles.

    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    HRESULT hr = S_OK;

    EX_TRY
    {
        // Use AVInRuntimeImplOkHolder.
        AVInRuntimeImplOkayHolder AVOkay;

        // This may throw if the Object Handle is invalid.
        Object * objPtr = *((Object**) oh);

        // NULL is certinally valid...
        if (objPtr != NULL)
        {
            if (!objPtr->ValidateObjectWithPossibleAV())
            {
                LOG((LF_CORDB, LL_INFO10000, "GAV: object methodtable-class invariant doesn't hold.\n"));
                hr = E_INVALIDARG;
                goto LExit;
            }
        }

    LExit: ;
    }
    EX_CATCH
    {
        LOG((LF_CORDB, LL_INFO10000, "GAV: exception indicated ref is bad.\n"));
        hr = E_INVALIDARG;
    }
    EX_END_CATCH(SwallowAllExceptions);

    return hr;
}


// Validate an object. Returns E_INVALIDARG or S_OK.
HRESULT ValidateObject(Object *objPtr)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    HRESULT hr = S_OK;

    EX_TRY
    {
        // Use AVInRuntimeImplOkHolder.
        AVInRuntimeImplOkayHolder AVOkay;

        // NULL is certinally valid...
        if (objPtr != NULL)
        {
            if (!objPtr->ValidateObjectWithPossibleAV())
            {
                LOG((LF_CORDB, LL_INFO10000, "GAV: object methodtable-class invariant doesn't hold.\n"));
                hr = E_INVALIDARG;
                goto LExit;
            }
        }

    LExit: ;
    }
    EX_CATCH
    {
        LOG((LF_CORDB, LL_INFO10000, "GAV: exception indicated ref is bad.\n"));
        hr = E_INVALIDARG;
    }
    EX_END_CATCH(SwallowAllExceptions);

    return hr;
}   // ValidateObject


#ifdef FEATURE_DBGIPC_TRANSPORT_VM
void
ShutdownTransport()
{
    if (g_pDbgTransport != NULL)
    {
        g_pDbgTransport->Shutdown();
        g_pDbgTransport = NULL;
    }
}
#endif // FEATURE_DBGIPC_TRANSPORT_VM


/* ------------------------------------------------------------------------ *
 * Debugger routines
 * ------------------------------------------------------------------------ */

//
// a Debugger object represents the global state of the debugger program.
//

//
// Constructor & Destructor
//

/******************************************************************************
 *
 ******************************************************************************/
Debugger::Debugger()
  :
    m_fLeftSideInitialized(FALSE),
#ifdef _DEBUG
    m_mutexCount(0),
#endif //_DEBUG
    m_pRCThread(NULL),
    m_trappingRuntimeThreads(FALSE),
    m_stopped(FALSE),
    m_unrecoverableError(FALSE),
    m_ignoreThreadDetach(FALSE),
    m_pMethodInfos(NULL),
    m_pForceCatchHandlerFoundEventsTable(NULL),
    m_pCustomNotificationTable(NULL),
    m_mutex(CrstDebuggerMutex, (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_REENTRANCY | CRST_DEBUGGER_THREAD)),
#ifdef _DEBUG
    m_mutexOwner(0),
    m_tidLockedForEventSending(0),
#endif //_DEBUG
    m_threadsAtUnsafePlaces(0),
    m_jitAttachInProgress(FALSE),
    m_launchingDebugger(FALSE),
    m_LoggingEnabled(TRUE),
    m_pAppDomainCB(NULL),
    m_dClassLoadCallbackCount(0),
    m_pModules(NULL),
    m_RSRequestedSync(FALSE),
    m_sendExceptionsOutsideOfJMC(TRUE),
    m_forceNonInterceptable(FALSE),
    m_pLazyData(NULL),
    m_defines(_defines),
    m_isSuspendedForGarbageCollection(FALSE),
    m_isBlockedOnGarbageCollectionEvent(FALSE),
    m_willBlockOnGarbageCollectionEvent(FALSE),
    m_isGarbageCollectionEventsEnabled(FALSE),
    m_isGarbageCollectionEventsEnabledLatch(FALSE)
{
    CONTRACTL
    {
        WRAPPER(THROWS);
        WRAPPER(GC_TRIGGERS);
        CONSTRUCTOR_CHECK;
    }
    CONTRACTL_END;

    m_fShutdownMode = false;
    m_fDisabled = false;
    m_rgHijackFunction = NULL;

#ifdef _DEBUG
    InitDebugEventCounting();
#endif

    m_processId = GetCurrentProcessId();

    m_pForceCatchHandlerFoundEventsTable = new ForceCatchHandlerFoundTable();
    m_pCustomNotificationTable = new CustomNotificationTable();

    //------------------------------------------------------------------------------
    // Metadata data structure version numbers
    //
    // 1 - initial state of the layouts ( .NET Framework 4.5.2 )
    //
    // as data structure layouts change, add a new version number
    // and comment the changes
    m_mdDataStructureVersion = 1;
    m_fOutOfProcessSetContextEnabled =
#if defined(OUT_OF_PROCESS_SETTHREADCONTEXT) && !defined(DACCESS_COMPILE)
        Thread::AreShadowStacksEnabled() || CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_OutOfProcessSetContext) != 0;
#else
        FALSE;
#endif
}

/******************************************************************************
 *
 ******************************************************************************/
Debugger::~Debugger()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        DESTRUCTOR_CHECK;
    }
    CONTRACTL_END;

    // We explicitly leak the debugger object on shutdown. See Debugger::StopDebugger for details.
    _ASSERTE(!"Debugger dtor should not be called.");
}

#if defined(FEATURE_HIJACK) && !defined(TARGET_UNIX)

// Given the start address and the end address of a function, return a MemoryRange for the function.
inline MemoryRange GetMemoryRangeForFunction(void *pfnStart, void *pfnEnd)
{
    PCODE pfnStartAddress = (PCODE)GetEEFuncEntryPoint(pfnStart);
    PCODE pfnEndAddress   = (PCODE)GetEEFuncEntryPoint(pfnEnd);
    return MemoryRange(dac_cast<PTR_VOID>(pfnStartAddress), (pfnEndAddress - pfnStartAddress));
}

// static
MemoryRange Debugger::s_hijackFunction[kMaxHijackFunctions] =
    {GetMemoryRangeForFunction(ExceptionHijack, ExceptionHijackEnd),
     GetMemoryRangeForFunction(RedirectedHandledJITCaseForGCThreadControl_Stub,
                               RedirectedHandledJITCaseForGCThreadControl_StubEnd),
     GetMemoryRangeForFunction(RedirectedHandledJITCaseForDbgThreadControl_Stub,
                               RedirectedHandledJITCaseForDbgThreadControl_StubEnd),
     GetMemoryRangeForFunction(RedirectedHandledJITCaseForUserSuspend_Stub,
                               RedirectedHandledJITCaseForUserSuspend_StubEnd)
#if defined(HAVE_GCCOVER) && defined(TARGET_AMD64) && defined(USE_REDIRECT_FOR_GCSTRESS)
     ,
     GetMemoryRangeForFunction(RedirectedHandledJITCaseForGCStress_Stub,
                               RedirectedHandledJITCaseForGCStress_StubEnd)
#endif // HAVE_GCCOVER && TARGET_AMD64
#ifdef FEATURE_SPECIAL_USER_MODE_APC
     ,
     GetMemoryRangeForFunction(ApcActivationCallbackStub,
                               ApcActivationCallbackStubEnd)
#endif // FEATURE_SPECIAL_USER_MODE_APC
    };
#endif // FEATURE_HIJACK && !TARGET_UNIX

// Save the necessary information for the debugger to recognize an IP in one of the thread redirection
// functions.
void Debugger::InitializeHijackFunctionAddress()
{
#if defined(FEATURE_HIJACK) && !defined(TARGET_UNIX)
    // Advertise hijack address for the DD Hijack primitive
    m_rgHijackFunction = Debugger::s_hijackFunction;
#endif // FEATURE_HIJACK && !TARGET_UNIX
}

// For debug-only builds, we'll have a debugging feature to count
// the number of ipc events and break on a specific number.
// Initialize the stuff to do that.
void Debugger::InitDebugEventCounting()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;
#ifdef _DEBUG
    // initialize the debug event counter structure to zero
    memset(&g_debugEventCounter, 0, sizeof(DebugEventCounter));
    memset(&g_iDbgRuntimeCounter, 0, DBG_RUNTIME_MAX*sizeof(int));
    memset(&g_iDbgDebuggerCounter, 0, DBG_DEBUGGER_MAX*sizeof(int));

    // retrieve the possible counter for break point
    CLRConfigStringHolder wstrValue = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DebuggerBreakPoint);
    // The string value is of the following format
    // <Event Name>=Count;<Event Name>=Count;....;
    // The string must end with ;
    if (wstrValue != NULL)
    {
        LPSTR   strValue;
        int     cbReq;
        cbReq = WideCharToMultiByte(CP_UTF8, 0, wstrValue,-1, 0,0, 0,0);

        strValue = new (nothrow) char[cbReq+1];
        // This is a debug only thingy, if it fails, not worth taking
        // down the process.
        if (strValue == NULL)
            return;


        // now translate the unicode to ansi string
        WideCharToMultiByte(CP_UTF8, 0, wstrValue, -1, strValue, cbReq+1, 0,0);
        char *szEnd = (char *)strchr(strValue, ';');
        char *szStart = strValue;
        while (szEnd != NULL)
        {
            // Found a key value
            char    *szNameEnd = strchr(szStart, '=');
            int     iCount;
            DebuggerIPCEventType eventType;
            if (szNameEnd != NULL)
            {
                // This is a well form key
                *szNameEnd = '\0';
                *szEnd = '\0';

                // now szStart is the key name null terminated. Translate the counter into integer.
                iCount = atoi(szNameEnd+1);
                if (iCount != 0)
                {
                    eventType = IPCENames::GetEventType(szStart);

                    if (eventType < DB_IPCE_DEBUGGER_FIRST)
                    {
                        // use the runtime one
                        g_iDbgRuntimeCounter[eventType & 0x00ff] = iCount;
                    }
                    else if (eventType < DB_IPCE_DEBUGGER_LAST)
                    {
                        // use the debugger one
                        g_iDbgDebuggerCounter[eventType & 0x00ff] = iCount;
                    }
                    else
                        _ASSERTE(!"Unknown Event Type");
                }
            }
            szStart = szEnd + 1;
            // try to find next key value
            szEnd = (char *)strchr(szStart, ';');
        }

        // free the ansi buffer
        delete [] strValue;
    }
#endif // _DEBUG
}

// Checks if the MethodInfos table has been allocated, and if not does so.
// Throw on failure, so we always return
HRESULT Debugger::CheckInitMethodInfoTable()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    if (m_pMethodInfos == NULL)
    {
        DebuggerMethodInfoTable *pMethodInfos = NULL;

        EX_TRY
        {
            pMethodInfos = new (interopsafe) DebuggerMethodInfoTable();
        }
        EX_CATCH
        {
            pMethodInfos = NULL;
        }
        EX_END_CATCH(RethrowTerminalExceptions);


        if (pMethodInfos == NULL)
        {
            return E_OUTOFMEMORY;
        }

        if (InterlockedCompareExchangeT(&m_pMethodInfos, pMethodInfos, NULL) != NULL)
        {
            DeleteInteropSafe(pMethodInfos);
        }
    }

    return S_OK;
}

// Checks if the m_pModules table has been allocated, and if not does so.
HRESULT Debugger::CheckInitModuleTable()
{
    CONTRACT(HRESULT)
    {
        NOTHROW;
        GC_NOTRIGGER;
        POSTCONDITION(m_pModules != NULL);
    }
    CONTRACT_END;

    if (m_pModules == NULL)
    {
        DebuggerModuleTable *pModules = new (interopsafe, nothrow) DebuggerModuleTable();

        if (pModules == NULL)
        {
            RETURN (E_OUTOFMEMORY);
        }

        if (InterlockedCompareExchangeT(&m_pModules, pModules, NULL) != NULL)
        {
            DeleteInteropSafe(pModules);
        }
    }

    RETURN (S_OK);
}

// Checks if the m_pModules table has been allocated, and if not does so.
HRESULT Debugger::CheckInitPendingFuncEvalTable()
{
    CONTRACT(HRESULT)
    {
        NOTHROW;
        GC_NOTRIGGER;
        POSTCONDITION(GetPendingEvals() != NULL);
    }
    CONTRACT_END;

#ifndef DACCESS_COMPILE

    if (GetPendingEvals() == NULL)
    {
        DebuggerPendingFuncEvalTable *pPendingEvals = new (interopsafe, nothrow) DebuggerPendingFuncEvalTable();

        if (pPendingEvals == NULL)
        {
            RETURN(E_OUTOFMEMORY);
        }

        // Since we're setting, we need an LValue and not just an accessor.
        if (InterlockedCompareExchangeT(&(GetLazyData()->m_pPendingEvals), pPendingEvals, NULL) != NULL)
        {
            DeleteInteropSafe(pPendingEvals);
        }
    }
#endif

    RETURN (S_OK);
}


#ifdef _DEBUG_DMI_TABLE
// Returns the number of (official) entries in the table
ULONG DebuggerMethodInfoTable::CheckDmiTable(void)
{
    LIMITED_METHOD_CONTRACT;

    ULONG cApparent = 0;
    ULONG cOfficial = 0;

    if (NULL != m_pcEntries)
    {
        DebuggerMethodInfoEntry *dcp;
        int i = 0;
        while (i++ <m_iEntries)
        {
            dcp = (DebuggerMethodInfoEntry*)&(((DebuggerMethodInfoEntry *)m_pcEntries)[i]);
            if(dcp->pFD != 0 &&
               dcp->pFD != (MethodDesc*)0xcdcdcdcd &&
               dcp->mi != NULL)
            {
                cApparent++;

                _ASSERTE( dcp->pFD == dcp->mi->m_fd );
                LOG((LF_CORDB, LL_INFO1000, "DMIT::CDT:Entry:0x%p mi:0x%p\nPrevs:\n",
                    dcp, dcp->mi));
                DebuggerMethodInfo *dmi = dcp->mi->m_prevMethodInfo;

                while(dmi != NULL)
                {
                    LOG((LF_CORDB, LL_INFO1000, "\t0x%p\n", dmi));
                    dmi = dmi->m_prevMethodInfo;
                }
                dmi = dcp->mi->m_nextMethodInfo;

                LOG((LF_CORDB, LL_INFO1000, "Nexts:\n", dmi));
                while(dmi != NULL)
                {
                    LOG((LF_CORDB, LL_INFO1000, "\t0x%p\n", dmi));
                    dmi = dmi->m_nextMethodInfo;
                }

                LOG((LF_CORDB, LL_INFO1000, "DMIT::CDT:DONE\n",
                    dcp, dcp->mi));
            }
        }

        if (m_piBuckets == 0)
        {
            LOG((LF_CORDB, LL_INFO1000, "DMIT::CDT: The table is officially empty!\n"));
            return cOfficial;
        }

        LOG((LF_CORDB, LL_INFO1000, "DMIT::CDT:Looking for official entries:\n"));

        ULONG iNext = m_piBuckets[0];
        ULONG iBucket = 1;
        HASHENTRY   *psEntry = NULL;
        while (TRUE)
        {
            while (iNext != UINT32_MAX)
            {
                cOfficial++;

                psEntry = EntryPtr(iNext);
                dcp = ((DebuggerMethodInfoEntry *)psEntry);

                LOG((LF_CORDB, LL_INFO1000, "\tEntry:0x%p mi:0x%p @idx:0x%x @bucket:0x%x\n",
                    dcp, dcp->mi, iNext, iBucket));

                iNext = psEntry->iNext;
            }

            // Advance to the next bucket.
            if (iBucket < m_iBuckets)
                iNext = m_piBuckets[iBucket++];
            else
                break;
        }

        LOG((LF_CORDB, LL_INFO1000, "DMIT::CDT:Finished official entries: ****************"));
    }

    return cOfficial;
}
#endif // _DEBUG_DMI_TABLE


//---------------------------------------------------------------------------------------
//
// Class constructor for DebuggerEval.  This is the supporting data structure for
// func-eval tracking.
//
// Arguments:
//      pContext - The context to return to when done with this eval.
//      pEvalInfo - Contains all the important information, such as parameters, type args, method.
//      fInException - TRUE if the thread for the eval is currently in an exception notification.
//      bpInfoSegmentRX - bpInfoSegmentRX is an InteropSafe allocation allocated by the caller.
//                        (Caller allocated as there is no way to fail the allocation without
//                        throwing, and this function is called in a NOTHROW region)
//
DebuggerEval::DebuggerEval(CONTEXT * pContext, DebuggerIPCE_FuncEvalInfo * pEvalInfo, bool fInException, DebuggerEvalBreakpointInfoSegment* bpInfoSegmentRX)
{
    WRAPPER_NO_CONTRACT;

#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) && defined(HOST_OSX) && defined(HOST_ARM64)
    ExecutableWriterHolder<DebuggerEvalBreakpointInfoSegment> bpInfoSegmentWriterHolder(bpInfoSegmentRX, sizeof(DebuggerEvalBreakpointInfoSegment));
    DebuggerEvalBreakpointInfoSegment *bpInfoSegmentRW = bpInfoSegmentWriterHolder.GetRW();
#else // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX && HOST_ARM64
    DebuggerEvalBreakpointInfoSegment *bpInfoSegmentRW = bpInfoSegmentRX;
#endif // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX && HOST_ARM64
    new (bpInfoSegmentRW) DebuggerEvalBreakpointInfoSegment(this);
    m_bpInfoSegment = bpInfoSegmentRX;

    // This must be non-zero so that the saved opcode is non-zero, and on IA64 we want it to be 0x16
    // so that we can have a breakpoint instruction in any slot in the bundle.
    bpInfoSegmentRW->m_breakpointInstruction[0] = 0x16;
#if defined(TARGET_ARM)
    USHORT *bp = (USHORT*)&m_bpInfoSegment->m_breakpointInstruction;
    *bp = CORDbg_BREAK_INSTRUCTION;
#endif // TARGET_ARM
    m_thread = pEvalInfo->vmThreadToken.GetRawPtr();
    m_evalType = pEvalInfo->funcEvalType;
    m_methodToken = pEvalInfo->funcMetadataToken;
    m_classToken = pEvalInfo->funcClassMetadataToken;

    // Note: we can't rely on just the DebuggerModule* or AppDomain* because the AppDomain
    // could get unloaded between now and when the funceval actually starts.  So we stash an
    // AppDomain ID which is safe to use after the AD is unloaded.  It's only safe to
    // use the DebuggerModule* after we've verified the ADID is still valid (i.e. by entering that domain).
    m_debuggerModule = g_pDebugger->LookupOrCreateModule(pEvalInfo->vmDomainAssembly);
    m_funcEvalKey = pEvalInfo->funcEvalKey;
    m_argCount = pEvalInfo->argCount;
    m_targetCodeAddr = (TADDR)NULL;
    m_stringSize = pEvalInfo->stringSize;
    m_arrayRank = pEvalInfo->arrayRank;
    m_genericArgsCount = pEvalInfo->genericArgsCount;
    m_genericArgsNodeCount = pEvalInfo->genericArgsNodeCount;
    m_successful = false;
    m_argData = NULL;
    memset(m_result, 0, sizeof(m_result));
    m_md = NULL;
    m_resultType = TypeHandle();
    m_aborting = FE_ABORT_NONE;
    m_aborted = false;
    m_completed = false;
    m_evalDuringException = fInException;
    m_retValueBoxing = Debugger::NoValueTypeBoxing;
    m_vmObjectHandle = VMPTR_OBJECTHANDLE::NullPtr();

    // Copy the thread's context.
    if (pContext == NULL)
    {
        memset(&m_context, 0, sizeof(m_context));
    }
    else
    {
        memcpy(&m_context, pContext, sizeof(m_context));
    }
}

#ifdef _DEBUG
// Thread proc for interop stress coverage. Have an unmanaged thread
// that just loops throwing native exceptions. This can test corner cases
// such as getting an native exception while the runtime is synced.
DWORD WINAPI DbgInteropStressProc(void * lpParameter)
{
    LIMITED_METHOD_CONTRACT;

    int i = 0;
    int zero = 0;


    // This will ensure that the compiler doesn't flag our 1/0 exception below at compile-time.
    if (lpParameter != NULL)
    {
        zero = 1;
    }

    // Note that this thread is a non-runtime thread. So it can't take any CLR locks
    // or do anything else that may block the helper thread.
    // (Log statements take CLR locks).
    while(true)
    {
        i++;

        if ((i % 10) != 0)
        {
            // Generate an in-band event.
            PAL_CPP_TRY
            {
                // Throw a handled exception. Don't use an AV since that's pretty special.
                *(int*)lpParameter = 1 / zero;
            }
            PAL_CPP_CATCH_ALL
            {
            }
            PAL_CPP_ENDTRY
        }
        else
        {
            // Generate the occasional oob-event.
            OutputDebugString(W("Ping from DbgInteropStressProc"));
        }

        // This helps parallelize if we have a lot of threads, and keeps us from
        // chewing too much CPU time.
        ClrSleepEx(2000,FALSE);
        ClrSleepEx(GetRandomInt(1000), FALSE);
    }

    return 0;
}

// ThreadProc that does everything in a can't stop region.
DWORD WINAPI DbgInteropCantStopStressProc(void * lpParameter)
{
    WRAPPER_NO_CONTRACT;

    // This will mark us as a can't stop region.
    ClrFlsSetThreadType (ThreadType_DbgHelper);

    return DbgInteropStressProc(lpParameter);
}

// Generate lots of OOB events.
DWORD WINAPI DbgInteropDummyStressProc(void * lpParameter)
{
    LIMITED_METHOD_CONTRACT;

    ClrSleepEx(1,FALSE);
    return 0;
}

DWORD WINAPI DbgInteropOOBStressProc(void * lpParameter)
{
    WRAPPER_NO_CONTRACT;

    int i = 0;
    while(true)
    {
        i++;
        if (i % 10 == 1)
        {
            // Create a dummy thread. That generates 2 oob events
            // (1 for create, 1 for destroy)
            DWORD id;
            ::CreateThread(NULL, 0, DbgInteropDummyStressProc, NULL, 0, &id);
        }
        else
        {
            // Generate the occasional oob-event.
            OutputDebugString(W("OOB ping from "));
        }

        ClrSleepEx(3000, FALSE);
    }

    return 0;
}

// List of the different possible stress procs.
LPTHREAD_START_ROUTINE g_pStressProcs[] =
{
    DbgInteropOOBStressProc,
    DbgInteropCantStopStressProc,
    DbgInteropStressProc
};
#endif


DebuggerHeap * Debugger::GetInteropSafeHeap()
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // Lazily initialize our heap.
    if (!m_heap.IsInit())
    {
        _ASSERTE(!"InteropSafe Heap should have already been initialized in LazyInit");

        // Just in case we miss it in retail, convert to OOM here:
        ThrowOutOfMemory();
    }

    return &m_heap;
}

DebuggerHeap * Debugger::GetInteropSafeHeap_NoThrow()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // Lazily initialize our heap.
    if (!m_heap.IsInit())
    {
        _ASSERTE(!"InteropSafe Heap should have already been initialized in LazyInit");

        // Just in case we miss it in retail, convert to OOM here:
        return NULL;
    }
    return &m_heap;
}

DebuggerHeap * Debugger::GetInteropSafeExecutableHeap()
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // Lazily initialize our heap.
    if (!m_executableHeap.IsInit())
    {
        _ASSERTE(!"InteropSafe Executable Heap should have already been initialized in LazyInit");

        // Just in case we miss it in retail, convert to OOM here:
        ThrowOutOfMemory();
    }

    return &m_executableHeap;
}

DebuggerHeap * Debugger::GetInteropSafeExecutableHeap_NoThrow()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // Lazily initialize our heap.
    if (!m_executableHeap.IsInit())
    {
        _ASSERTE(!"InteropSafe Executable Heap should have already been initialized in LazyInit");

        // Just in case we miss it in retail, convert to OOM here:
        return NULL;
    }
    return &m_executableHeap;
}

//---------------------------------------------------------------------------------------
//
// Notify potential debugger that the runtime has started up
//
//
// Assumptions:
//    Called during startup path
//
// Notes:
//    If no debugger is attached, this does nothing.
//
//---------------------------------------------------------------------------------------
void Debugger::RaiseStartupNotification()
{
    // Right-side will read this field from OOP via DAC-primitive to determine attach or launch case.
    // We do an interlocked increment to guarantee this is an atomic memory write, and to ensure
    // that it's flushed from any CPU cache into memory.
    InterlockedIncrement(&m_fLeftSideInitialized);

#ifndef FEATURE_DBGIPC_TRANSPORT_VM
    // If we are remote debugging, don't send the event now if a debugger is not attached.  No one will be
    // listening, and we will fail.  However, we still want to initialize the variable above.
    DebuggerIPCEvent startupEvent;
    InitIPCEvent(&startupEvent, DB_IPCE_LEFTSIDE_STARTUP, NULL, VMPTR_AppDomain::NullPtr());

    SendRawEvent(&startupEvent);

    // RS will set flags from OOP while we're stopped at the event if it wants to attach.
#endif // FEATURE_DBGIPC_TRANSPORT_VM
}


//---------------------------------------------------------------------------------------
//
// Sends a raw managed debug event to the debugger.
//
// Arguments:
//      pManagedEvent - managed debug event
//
//
// Notes:
//    This can be called even if a debugger is not attached.
//    The entire process will get frozen by the debugger once we send.  The debugger
//    needs to resume the process. It may detach as well.
//    See code:IsEventDebuggerNotification for decoding this event. These methods must stay in sync.
//    The debugger process reads the events via code:CordbProcess.CopyManagedEventFromTarget.
//
//---------------------------------------------------------------------------------------
void Debugger::SendRawEvent(const DebuggerIPCEvent * pManagedEvent)
{
#if defined(FEATURE_DBGIPC_TRANSPORT_VM)
    HRESULT hr = g_pDbgTransport->SendDebugEvent(const_cast<DebuggerIPCEvent *>(pManagedEvent));

    if (FAILED(hr))
    {
        _ASSERTE(!"Failed to send debugger event");

        STRESS_LOG1(LF_CORDB, LL_INFO1000, "D::SendIPCEvent Error on Send with 0x%x\n", hr);
        UnrecoverableError(hr,
            0,
            FILE_DEBUG,
            LINE_DEBUG,
            false);

        // @dbgtodo  Mac - what can we do here?
    }
#else
    // We get to send an array of ULONG_PTRs as data with the notification.
    // The debugger can then use ReadProcessMemory to read through this array.
    ULONG_PTR rgData [] = {
        CLRDBG_EXCEPTION_DATA_CHECKSUM,
        (ULONG_PTR)GetClrModuleBase(),
        (ULONG_PTR) pManagedEvent
    };

    // If no debugger attached, then don't bother raising a 1st-chance exception because nobody will sniff it.
    // @dbgtodo iDNA: in iDNA case, the recorder may sniff it.
    if (!IsDebuggerPresent())
    {
        return;
    }

    //
    // Physically send the event via an OS Exception. We're using exceptions as a notification
    // mechanism on top of the OS native debugging pipeline.
    // @dbgtodo  cross-plat - this needs to be cross-plat.
    //
    EX_TRY
    {
        const DWORD dwFlags = 0; // continuable (eg, Debugger can continue GH)
        RaiseException(CLRDBG_NOTIFICATION_EXCEPTION_CODE, dwFlags, ARRAY_SIZE(rgData), rgData);

        // If debugger continues "GH" (DBG_CONTINUE), then we land here.
        // This is the expected path for a well-behaved ICorDebug debugger.
    }
    EX_CATCH
    {
        // If no debugger is attached, or if the debugger continues "GN" (DBG_EXCEPTION_NOT_HANDLED), then we land here.
        // A naive (not-ICorDebug aware) native-debugger won't handle the exception and so land us here.
        // We may also get here if a debugger detaches at the Exception notification
        // (and thus implicitly continues GN).
    }
    EX_END_CATCH(SwallowAllExceptions);
#endif // FEATURE_DBGIPC_TRANSPORT_VM
}

//---------------------------------------------------------------------------------------
// Send a createProcess event to give the RS a chance to do SetDesiredNGENFlags
//
// Arguments:
//    pDbgLockHolder - lock holder.
//
// Assumptions:
//    Lock is initially held. This will toggle the lock to send an IPC event.
//    This will start a synchronization.
//
// Notes:
//    In V2, this also gives the RS a chance to initialize the IPC protocol.
//    Spefically, this needs to be sent before the LS can send a sync-complete.
//---------------------------------------------------------------------------------------
void Debugger::SendCreateProcess(DebuggerLockHolder * pDbgLockHolder)
{
    pDbgLockHolder->Release();

    // Encourage helper thread to spin up so that we're in a consistent state.
    PollWaitingForHelper();

    // we don't need to use SENDIPCEVENT_BEGIN/END macros that perform the debug-suspend aware checks,
    // as this code executes on the startup path...
    SENDIPCEVENT_RAW_BEGIN(pDbgLockHolder);

    // Send a CreateProcess event.
    // @dbgtodo  pipeline - eliminate these reasons for needing a CreateProcess event (part of pipeline feature crew)
    // This will let the RS know that the IPC block is up + ready, and then the RS can read it.
    // The RS will then update the DCB with enough information so that we can send the sync-complete.
    // (such as letting us know whether we're interop-debugging or not).
    DebuggerIPCEvent event;
    InitIPCEvent(&event, DB_IPCE_CREATE_PROCESS, NULL, VMPTR_AppDomain::NullPtr());
    SendRawEvent(&event);

    // @dbgtodo  inspection- it doesn't really make sense to sync on a CreateProcess. We only have 1 thread
    // in the CLR and we know exactly what state we're in and we can ensure that we're synchronized.
    // For V3,RS should be able to treat a CreateProcess like a synchronized.
    // Remove this in V3 as we make SetDesiredNgenFlags operate OOP.
    TrapAllRuntimeThreads();

    // Must have a thread object so that we ensure that we will actually block here.
    // This ensures the debuggee is actually stopped at startup, and
    // this gives the debugger a chance to call SetDesiredNGENFlags before we
    // set s_fCanChangeNgenFlags to FALSE.
    _ASSERTE(GetThreadNULLOk() != NULL);
    SENDIPCEVENT_RAW_END;

    pDbgLockHolder->Acquire();
}

#if !defined(TARGET_UNIX)

HANDLE g_hContinueStartupEvent = INVALID_HANDLE_VALUE;

CLR_ENGINE_METRICS g_CLREngineMetrics = {
    sizeof(CLR_ENGINE_METRICS),
    CorDebugVersion_4_0,
    &g_hContinueStartupEvent};

HANDLE OpenStartupNotificationEvent()
{
    // "Telesto" is the legacy name for the start up event. Changing this name
    // would impact multiple repos and not worth the effort at present.
    // This event is used by https://github.com/dotnet/diagnostics/blob/main/src/dbgshim/dbgshim.cpp
    WCHAR prefix[] = W("TelestoStartupEvent_");
    WCHAR eventName[ARRAY_SIZE(prefix) + Max32BitHexString] = { W('\0') };
    wcscat_s(eventName, ARRAY_SIZE(eventName), prefix);

    DWORD debuggeePID = GetCurrentProcessId();
    FormatInteger(eventName + ARRAY_SIZE(prefix) - 1, ARRAY_SIZE(eventName) - ARRAY_SIZE(prefix), "%08x", debuggeePID);
    return OpenEvent(MAXIMUM_ALLOWED | SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, eventName);
}

void NotifyDebuggerOfStartup()
{
    // Create the continue event first so that we guarantee that any
    // enumeration of this process will get back a valid continue event
    // the instant we signal the startup notification event.

    CONSISTENCY_CHECK(INVALID_HANDLE_VALUE == g_hContinueStartupEvent);
    g_hContinueStartupEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    CONSISTENCY_CHECK(INVALID_HANDLE_VALUE != g_hContinueStartupEvent); // we reserve this value for error conditions in EnumerateCLRs

    HANDLE startupEvent = OpenStartupNotificationEvent();
    if (startupEvent != NULL)
    {
        // signal notification event
        SetEvent(startupEvent);
        CloseHandle(startupEvent);
        startupEvent = NULL;

        // wait on continue startup event
        // The debugger may attach to us while we're blocked here.
        WaitForSingleObject(g_hContinueStartupEvent, INFINITE);
    }

    CloseHandle(g_hContinueStartupEvent);
    g_hContinueStartupEvent = NULL;
}

#endif // !TARGET_UNIX

void Debugger::CleanupTransportSocket(void)
{
#if defined(TARGET_UNIX) && defined(FEATURE_DBGIPC_TRANSPORT_VM)
    if (g_pDbgTransport != NULL)
    {
        g_pDbgTransport->AbortConnection();
    }
#endif // TARGET_UNIX && FEATURE_DBGIPC_TRANSPORT_VM
}

//---------------------------------------------------------------------------------------
//
// Initialize Left-Side debugger object
//
// Return Value:
//    S_OK on success. May also throw.
//
// Assumptions:
//    This is called in the startup path.
//
// Notes:
// Startup initializes any necessary debugger objects, including creating
// and starting the Runtime Controller thread. Once the RC thread is started
// and we return successfully, the Debugger object can expect to have its
// event handlers called.
//
//---------------------------------------------------------------------------------------
HRESULT Debugger::Startup(void)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
    }
    CONTRACTL_END;

    HRESULT hr = S_OK;

    _ASSERTE(g_pEEInterface != NULL);

#if !defined(TARGET_UNIX)
    // This may block while an attach occurs.
    NotifyDebuggerOfStartup();
#endif // !TARGET_UNIX
    {
        DebuggerLockHolder dbgLockHolder(this);

        // We can get extra Interop-debugging test coverage by having some auxiliary unmanaged
        // threads running and throwing debug events. Keep these stress procs separate so that
        // we can focus on certain problem areas.
    #ifdef _DEBUG
        g_DbgShouldntUseDebugger = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgNoDebugger) != 0;


        // Creates random thread procs.
        DWORD dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreads);
        DWORD dwId;
        DWORD i;

        if (dwRegVal > 0)
        {
            for (i = 0; i < dwRegVal; i++)
            {
                int iProc = GetRandomInt(ARRAY_SIZE(g_pStressProcs));
                LPTHREAD_START_ROUTINE pStartRoutine = g_pStressProcs[iProc];
                ::CreateThread(NULL, 0, pStartRoutine, NULL, 0, &dwId);
                LOG((LF_CORDB, LL_INFO1000, "Created random thread (%d) with tid=0x%x\n", i, dwId));
            }
        }

        dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsIB);
        if (dwRegVal > 0)
        {
            for (i = 0; i < dwRegVal; i++)
            {
                ::CreateThread(NULL, 0, DbgInteropStressProc, NULL, 0, &dwId);
                LOG((LF_CORDB, LL_INFO1000, "Created extra thread (%d) with tid=0x%x\n", i, dwId));
            }
        }

        dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsCantStop);
        if (dwRegVal > 0)
        {
            for (i = 0; i < dwRegVal; i++)
            {
                ::CreateThread(NULL, 0, DbgInteropCantStopStressProc, NULL, 0, &dwId);
                LOG((LF_CORDB, LL_INFO1000, "Created extra thread 'can't-stop' (%d) with tid=0x%x\n", i, dwId));
            }
        }

        dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsOOB);
        if (dwRegVal > 0)
        {
            for (i = 0; i < dwRegVal; i++)
            {
                ::CreateThread(NULL, 0, DbgInteropOOBStressProc, NULL, 0, &dwId);
                LOG((LF_CORDB, LL_INFO1000, "Created extra thread OOB (%d) with tid=0x%x\n", i, dwId));
            }
        }
    #endif

        // Lazily initialize the interop-safe heap

        // Must be done before the RC thread is initialized.
        // @dbgtodo  - In V2, LS was lazily initialized; but was eagerly pre-initialized if launched by debugger.
        // (This was for perf reasons). But we don't want Launch vs. Attach checks in the LS, so we now always
        // init. As we move more to OOP, this init will become cheaper.
        {
            LazyInit();
            DebuggerController::Initialize();
        }

        InitializeHijackFunctionAddress();

        // Also initialize the AppDomainEnumerationIPCBlock
    #if !defined(FEATURE_IPCMAN) || defined(FEATURE_DBGIPC_TRANSPORT_VM)
        m_pAppDomainCB = new (nothrow) AppDomainEnumerationIPCBlock();
    #else
        m_pAppDomainCB = g_pIPCManagerInterface->GetAppDomainBlock();
    #endif

        if (m_pAppDomainCB == NULL)
        {
            LOG((LF_CORDB, LL_INFO100, "D::S: Failed to get AppDomain IPC block from IPCManager.\n"));
            ThrowHR(E_FAIL);
        }

        hr = InitAppDomainIPC();
        _ASSERTE(SUCCEEDED(hr)); // throws on error.

        // Allows the debugger (and profiler) diagnostics to be disabled so resources like
        // the named pipes and semaphores are not created.
        if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_EnableDiagnostics) == 0)
        {
            LOG((LF_CORDB, LL_INFO10, "Debugging disabled via EnableDiagnostics config.\n"));

            return S_OK;
        }
        if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_EnableDiagnostics_Debugger) == 0)
        {
            LOG((LF_CORDB, LL_INFO10, "Debugging disabled via EnableDiagnostics_Debugger config.\n"));

            return S_OK;
        }

        // Create the runtime controller thread, a.k.a, the debug helper thread.
        // Don't use the interop-safe heap b/c we don't want to lazily create it.
        m_pRCThread = new DebuggerRCThread(this);
        _ASSERTE(m_pRCThread != NULL); // throws on oom
        TRACE_ALLOC(m_pRCThread);

        hr = m_pRCThread->Init();
        _ASSERTE(SUCCEEDED(hr)); // throws on error

    #if defined(FEATURE_DBGIPC_TRANSPORT_VM)
         // Create transport session and initialize it.
        g_pDbgTransport = new DbgTransportSession();
        hr = g_pDbgTransport->Init(m_pRCThread->GetDCB(), m_pAppDomainCB);
        if (FAILED(hr))
        {
            ShutdownTransport();
            STRESS_LOG0(LF_CORDB, LL_ERROR, "D::S: The debugger pipe failed to initialize in /tmp or $TMPDIR.\n");
            return S_OK; // we do not want debugger IPC to block runtime initialization
        }
    #endif // FEATURE_DBGIPC_TRANSPORT_VM

        RaiseStartupNotification();

        // See if we need to spin up the helper thread now, rather than later.
        DebuggerIPCControlBlock* pIPCControlBlock = m_pRCThread->GetDCB();
        (void)pIPCControlBlock; //prevent "unused variable" error from GCC

        _ASSERTE(pIPCControlBlock != NULL);
        _ASSERTE(!pIPCControlBlock->m_rightSideShouldCreateHelperThread);
        {
            // Create the win32 thread for the helper and let it run free.
            hr = m_pRCThread->Start();

            // convert failure to exception as with old contract
            if (FAILED(hr))
            {
                ThrowHR(hr);
            }

            LOG((LF_CORDB, LL_EVERYTHING, "Start was successful\n"));
        }

    #ifdef TEST_DATA_CONSISTENCY
        // if we have set the environment variable TestDataConsistency, run the data consistency test.
        // See code:DataTest::TestDataSafety for more information
        if ((g_pConfig != NULL) && (g_pConfig->TestDataConsistency() == true))
        {
            DataTest dt;
            dt.TestDataSafety();
        }
    #endif
    }

#ifdef TARGET_UNIX
    // Signal the debugger (via dbgshim) and wait until it is ready for us to
    // continue. This needs to be outside the lock and after the transport is
    // initialized.
    if (PAL_NotifyRuntimeStarted())
    {
        // The runtime was successfully launched and attached so mark it now
        // so no notifications are missed especially the initial module load
        // which would cause debuggers problems with reliable setting breakpoints
        // in startup code or Main.
       MarkDebuggerAttachedInternal();
    }
#endif // TARGET_UNIX

    // We don't bother changing this process's permission.
    // A managed debugger will have the SE_DEBUG permission which will allow it to open our process handle,
    // even if we're a guest account.

    return hr;
}

//---------------------------------------------------------------------------------------
// Finishes startup once we have a Thread object.
//
// Arguments:
//    pThread - the current thread. Must be non-null
//
// Notes:
//    Most debugger initialization is done in code:Debugger.Startup,
//    However, debugger can't block on synchronization without a Thread object,
//    so sending IPC events must wait until after we have a thread object.
//---------------------------------------------------------------------------------------
HRESULT Debugger::StartupPhase2(Thread * pThread)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
    }
    CONTRACTL_END;

    HRESULT hr = S_OK;

    // Must have a thread so that we can block
    _ASSERTE(pThread != NULL);

    DebuggerLockHolder dbgLockHolder(this);

    // @dbgtodo  - This may need to change when we remove SetupSyncEvent...
    // If we're launching, then sync now so that the RS gets an early chance to dispatch the CreateProcess event.
    // This is especially important b/c certain portions of the ICorDebugAPI (like setting ngen flags) are only
    // valid during the CreateProcess callback in the launch case.
    // We need to send the callback early enough so those APIs can set the flags before they're actually used.
    // We also ensure the debugger is actually attached.
    if (SUCCEEDED(hr) && CORDebuggerAttached())
    {
        StartCanaryThread();
        SendCreateProcess(&dbgLockHolder); // toggles lock
    }

    // After returning from debugger startup we assume that the runtime might start using the NGEN flags to make
    // binding decisions. From now on the debugger can not influence NGEN binding policy
    // Use volatile store to guarantee make the value visible to the DAC (the store can be optimized out otherwise)
    VolatileStoreWithoutBarrier(&s_fCanChangeNgenFlags, FALSE);

    // Must release the lock (which would be done at the end of this method anyways) so that
    // the helper thread can do the jit-attach.
    dbgLockHolder.Release();


#ifdef _DEBUG
    // Give chance for stress harnesses to launch a managed debugger when a managed app starts up.
    // This lets us run a set of managed apps under a debugger.
    if (!CORDebuggerAttached())
    {
        #define DBG_ATTACH_ON_STARTUP_ENV_VAR W("DOTNET_DbgAttachOnStartup")
        PathString temp;
        // We explicitly just check the env because we don't want a switch this invasive to be global.
        DWORD fAttach = WszGetEnvironmentVariable(DBG_ATTACH_ON_STARTUP_ENV_VAR, temp) > 0;

        if (fAttach)
        {
            // Remove the env var from our process so that the debugger we spin up won't inherit it.
            // Else, if the debugger is managed, we'll have an infinite recursion.
            BOOL fOk = SetEnvironmentVariable(DBG_ATTACH_ON_STARTUP_ENV_VAR, NULL);

            if (fOk)
            {
                // We've already created the helper thread (which can service the attach request)
                // So just do a normal jit-attach now.

                SString szName(W("DebuggerStressStartup"));
                SString szDescription(W("MDA used for debugger-stress scenario. This is fired to trigger a jit-attach")
                    W("to allow us to attach a debugger to any managed app that starts up.")
                    W("This MDA is only fired when the 'DbgAttachOnStartup' COM+ knob/reg-key is set on checked builds."));
                SString szXML(W("<xml>See the description</xml>"));

                SendMDANotification(
                    NULL, // NULL b/c we don't have a thread yet
                    &szName,
                    &szDescription,
                    &szXML,
                    ((CorDebugMDAFlags) 0 ),
                    TRUE // this will force the jit-attach
                );
            }
        }
    }
#endif


    return hr;
}


//---------------------------------------------------------------------------------------
//
// Public entrypoint into the debugger to force the lazy data to be initialized at a
// controlled point in time. This is useful for those callers into the debugger (e.g.,
// ETW rundown) that know they will need the lazy data initialized but cannot afford to
// have it initialized unpredictably or inside a lock.
//
// This may be called more than once, and will know to initialize the lazy data only
// once.
//

void Debugger::InitializeLazyDataIfNecessary()
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
    }
    CONTRACTL_END;

    if (!HasLazyData())
    {
        DebuggerLockHolder lockHolder(this);
        LazyInit(); // throws
    }
}


/******************************************************************************
Lazy initialize stuff once we know we are debugging.
This reduces the startup cost in the non-debugging case.

We can do this at a bunch of random strategic places.
 ******************************************************************************/

HRESULT Debugger::LazyInitWrapper()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(ThisMaybeHelperThread());
    }
    CONTRACTL_END;

    HRESULT hr = S_OK;

    // Do lazy initialization now.
    EX_TRY
    {
        LazyInit(); // throws on errors.
    }
    EX_CATCH
    {
        Exception *_ex = GET_EXCEPTION();
        hr = _ex->GetHR();
        STRESS_LOG1(LF_CORDB, LL_ALWAYS, "LazyInit failed w/ hr:0x%08x\n", hr);
    }
    EX_END_CATCH(SwallowAllExceptions);

    return hr;
}

void Debugger::LazyInit()
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        PRECONDITION(ThreadHoldsLock()); // ensure we're serialized, requires GC_NOTRIGGER

        PRECONDITION(ThisMaybeHelperThread());
    }
    CONTRACTL_END;

    // Have knob that catches places where we lazy init.
    _ASSERTE(!g_DbgShouldntUseDebugger);

    // If we're already init, then bail.
    if (m_pLazyData != NULL)
    {
        return;
    }




    // Lazily create our heap.
    HRESULT hr = m_heap.Init(FALSE);
    IfFailThrow(hr);

    hr = m_executableHeap.Init(TRUE);
    IfFailThrow(hr);

    m_pLazyData = new (interopsafe) DebuggerLazyInit();
    _ASSERTE(m_pLazyData != NULL); // throws on oom.

    m_pLazyData->Init();

}

HelperThreadFavor::HelperThreadFavor() :
    m_fpFavor(NULL),
    m_pFavorData(NULL),
    m_FavorReadEvent(NULL),
    m_FavorLock(CrstDebuggerFavorLock, CRST_DEFAULT),
    m_FavorAvailableEvent(NULL)
{
}

void HelperThreadFavor::Init()
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        PRECONDITION(ThisMaybeHelperThread());
    }
    CONTRACTL_END;

    // Create events for managing favors.
    m_FavorReadEvent      = CreateWin32EventOrThrow(NULL, kAutoResetEvent, FALSE);
    m_FavorAvailableEvent = CreateWin32EventOrThrow(NULL, kAutoResetEvent, FALSE);
}



DebuggerLazyInit::DebuggerLazyInit() :
    m_pPendingEvals(NULL),
    // @TODO: a-meicht
    // Major clean up needed for giving the right flag
    // There are cases where DebuggerDataLock is taken by managed thread and unmanaged trhead is also trying to take it.
    // It could cause deadlock if we toggle GC upon taking lock.
    // Unfortunately UNSAFE_COOPGC is not enough. There is a code path in Jit comipling that we are in GC Preemptive
    // enabled. workaround by orring the unsafe_anymode flag. But we really need to do proper clean up.
    //
    // NOTE: If this ever gets fixed, you should replace CALLED_IN_DEBUGGERDATALOCK_HOLDER_SCOPE_MAY_GC_TRIGGERS_CONTRACT
    // with appropriate contracts at each site.
    //
    m_DebuggerDataLock(CrstDebuggerJitInfo, (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_REENTRANCY | CRST_DEBUGGER_THREAD)),
    m_CtrlCMutex(NULL),
    m_exAttachEvent(NULL),
    m_exUnmanagedAttachEvent(NULL),
    m_garbageCollectionBlockerEvent(NULL),
    m_DebuggerHandlingCtrlC(FALSE)
{
}

void DebuggerLazyInit::Init()
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        PRECONDITION(ThisMaybeHelperThread());
    }
    CONTRACTL_END;

    // Caller ensures this isn't double-called.

    // This event is only used in the unmanaged attach case.  We must mark this event handle as inheritable.
    // Otherwise, the unmanaged debugger won't be able to notify us.
    //
    // Note that PAL currently doesn't support specifying the security attributes when creating an event, so
    // unmanaged attach for unhandled exceptions is broken on PAL.
    SECURITY_ATTRIBUTES* pSA = NULL;
    SECURITY_ATTRIBUTES secAttrib;
    secAttrib.nLength              = sizeof(secAttrib);
    secAttrib.lpSecurityDescriptor = NULL;
    secAttrib.bInheritHandle       = TRUE;

    pSA = &secAttrib;

    // Create some synchronization events...
    // these events stay signaled all the time except when an attach is in progress
    m_exAttachEvent          = CreateWin32EventOrThrow(NULL, kManualResetEvent, TRUE);
    m_exUnmanagedAttachEvent = CreateWin32EventOrThrow(pSA,  kManualResetEvent, TRUE);

    m_CtrlCMutex             = CreateWin32EventOrThrow(NULL, kAutoResetEvent, FALSE);
    m_DebuggerHandlingCtrlC  = FALSE;

    m_garbageCollectionBlockerEvent = CreateEventW(NULL, TRUE, FALSE, NULL);

    // Let the helper thread lazy init stuff too.
    m_RCThread.Init();
}


DebuggerLazyInit::~DebuggerLazyInit()
{
    {
        USHORT cBlobs = m_pMemBlobs.Count();
        void **rgpBlobs = m_pMemBlobs.Table();

        for (int i = 0; i < cBlobs; i++)
        {
            g_pDebugger->ReleaseRemoteBuffer(rgpBlobs[i], false);
        }
    }

    if (m_pPendingEvals)
    {
        DeleteInteropSafe(m_pPendingEvals);
        m_pPendingEvals = NULL;
    }

    if (m_CtrlCMutex != NULL)
    {
        CloseHandle(m_CtrlCMutex);
    }

    if (m_exAttachEvent != NULL)
    {
        CloseHandle(m_exAttachEvent);
    }

    if (m_exUnmanagedAttachEvent != NULL)
    {
        CloseHandle(m_exUnmanagedAttachEvent);
    }

    if (m_garbageCollectionBlockerEvent != NULL)
    {
        CloseHandle(m_garbageCollectionBlockerEvent);
    }
}


//
// RequestFavor gets the debugger helper thread to call a function. It's
// typically called when the current thread can't call the function directly,
// e.g, there isn't enough stack space.
//
// RequestFavor can be called in stack-overflow scenarios and thus explicitly
// avoids any lazy initialization.
// It blocks until the favor callback completes.
//
// Parameters:
//   fp    - a non-null Favour callback function
//   pData - the parameter passed to the favor callback function. This can be any value.
//
// Return values:
//   S_OK if the function succeeds, else a failure HRESULT
//

HRESULT Debugger::RequestFavor(FAVORCALLBACK fp, void * pData)
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
        PRECONDITION(fp != NULL);
    }
    CONTRACTL_END;

    if (m_pRCThread == NULL ||
        m_pRCThread->GetRCThreadId() == GetCurrentThreadId())
    {
        // Since favors are only used internally, we know that the helper should always be up and ready
        // to handle them. Also, since favors can be used in low-stack scenarios, there's not any
        // extra initialization needed for them.
        _ASSERTE(!"Helper not initialized for favors.");
        return E_UNEXPECTED;
    }

    m_pRCThread->DoFavor(fp, pData);
    return S_OK;
}

/******************************************************************************
// Called to set the interface that the Runtime exposes to us.
 ******************************************************************************/
void Debugger::SetEEInterface(EEDebugInterface* i)
{
    LIMITED_METHOD_CONTRACT;

    // @@@

    // Implements DebugInterface API

    g_pEEInterface = i;

}


/******************************************************************************
// Called to shut down the debugger. This stops the RC thread and cleans
// the object up.
 ******************************************************************************/
void Debugger::StopDebugger(void)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // Leak almost everything on process exit. The OS will clean it up anyways and trying to
    // clean it up ourselves is just one more place we may AV / deadlock.

#if defined(FEATURE_DBGIPC_TRANSPORT_VM)
    ShutdownTransport();
#endif // FEATURE_DBGIPC_TRANSPORT_VM

    // Ping the helper thread to exit. This will also prevent the helper from servicing new requests.
    if (m_pRCThread != NULL)
    {
        m_pRCThread->AsyncStop();
    }

    // Also clean up the AppDomain stuff since this is cross-process.
    TerminateAppDomainIPC ();

    //
    // Tell the VM to clear out all references to the debugger before we start cleaning up,
    // so that nothing will reference (accidentally) through the partially cleaned up debugger.
    //
    // NOTE: we cannot clear out g_pDebugger before the delete call because the
    // stuff in delete (particularly deleteinteropsafe) needs to look at it.
    //
    g_pEEInterface->ClearAllDebugInterfaceReferences();
    g_pDebugger = NULL;
}


/* ------------------------------------------------------------------------ *
 * JIT Interface routines
 * ------------------------------------------------------------------------ */


/******************************************************************************
 *
 ******************************************************************************/
DebuggerMethodInfo *Debugger::CreateMethodInfo(Module *module, mdMethodDef md)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;

        PRECONDITION(HasDebuggerDataLock());
    }
    CONTRACTL_END;


    // <TODO>@todo perf: creating these on the heap is slow. We should use a
    // pool and create them out of there since we never free them
    // until the AD is unloaded.</TODO>
    //
    DebuggerMethodInfo *mi = new (interopsafe) DebuggerMethodInfo(module, md);
    _ASSERTE(mi != NULL); // throws on oom error

    TRACE_ALLOC(mi);

    LOG((LF_CORDB, LL_INFO100000, "D::CreateMethodInfo module=%p, token=0x%08x, info=%p\n",
        module, md, mi));

    //
    // Lock a mutex when changing the table.
    //
    //@TODO : _ASSERTE(EnC);
    HRESULT hr;
    hr =InsertToMethodInfoList(mi);

    if (FAILED(hr))
    {
        LOG((LF_CORDB, LL_EVERYTHING, "IAHOL Failed!!\n"));
        DeleteInteropSafe(mi);
        return NULL;
    }
    return mi;

}

/******************************************************************************
// void Debugger::JITComplete():   JITComplete is called by
// the jit interface when the JIT completes, successfully or not.
//
// MethodDesc* fd:  MethodDesc of the code that's been JITted
// BYTE* newAddress:  The address of that the method begins at.
//          If newAddress is NULL then the JIT failed. Remember that this
//          gets called before the start address of the MethodDesc gets set,
//          and so methods like GetFunctionAddress & GetFunctionSize won't work.
//
// <TODO>@Todo If we're passed 0 for the newAddress param, the jit has been
//      cancelled & should be undone.</TODO>
 ******************************************************************************/
void Debugger::JITComplete(NativeCodeVersion nativeCodeVersion, TADDR newAddress)
{

    CONTRACTL
    {
        THROWS;
        PRECONDITION(!HasDebuggerDataLock());
        PRECONDITION(newAddress != NULL);
        CALLED_IN_DEBUGGERDATALOCK_HOLDER_SCOPE_MAY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    MethodDesc* fd = nativeCodeVersion.GetMethodDesc();

    LOG((LF_CORDB, LL_INFO100000, "D::JITComplete: md:%p (%s::%s), address:%p.\n",
        fd, fd->m_pszDebugClassName, fd->m_pszDebugMethodName, newAddress));

#ifdef TARGET_ARM
    newAddress = newAddress|THUMB_CODE;
#endif

    // @@@
    // Can be called on managed thread only
    // This API Implements DebugInterface

    if (CORDebuggerAttached())
    {
        // Populate the debugger's cache of DJIs. Normally we can do this lazily,
        // the only reason we do it here is b/c the MethodDesc is not yet officially marked as "jitted",
        // and so we can't lazily create it yet. Furthermore, the binding operations may need the DJIs.
        //
        // This also gives the debugger a chance to know if new JMC methods are coming.
        DebuggerMethodInfo * dmi = GetOrCreateMethodInfo(fd->GetModule(), fd->GetMemberDef());
        if (dmi == NULL)
        {
            goto Exit;
        }
        BOOL jiWasCreated = FALSE;
        DebuggerJitInfo * dji = dmi->CreateInitAndAddJitInfo(nativeCodeVersion, newAddress, &jiWasCreated);
        if (!jiWasCreated)
        {
            // we've already been notified about this code, no work remains.
            // The JIT is occasionally asked to generate code for the same
            // method on two threads. When this occurs both threads will
            // return the same code pointer and this callback is invoked
            // multiple times.
            LOG((LF_CORDB, LL_INFO1000000, "D::JITComplete: md:%p (%s::%s), address:%p. Already created\n",
                fd, fd->m_pszDebugClassName, fd->m_pszDebugMethodName, newAddress));
            goto Exit;
        }

        LOG((LF_CORDB, LL_INFO1000000, "D::JITComplete: md:%p (%s::%s), address:%p. Created dji:%p\n",
            fd, fd->m_pszDebugClassName, fd->m_pszDebugMethodName, newAddress, dji));

        // Bind any IL patches to the newly jitted native code.
        HRESULT hr;
        hr = MapAndBindFunctionPatches(dji, fd, (CORDB_ADDRESS_TYPE *)newAddress);
        _ASSERTE(SUCCEEDED(hr));
    }

    LOG((LF_CORDB, LL_EVERYTHING, "JitComplete completed successfully\n"));

Exit:
    ;
}

/******************************************************************************
// Get the number of fixed arguments to a function, i.e., the explicit args and the "this" pointer.
// This does not include other implicit arguments or varargs. This is used to compute a variable ID
// (see comment in CordbJITILFrame::ILVariableToNative for more detail)
// fVarArg is not used when this is called by Debugger::GetAndSendJITInfo, thus it has a default value.
// The return value is not used when this is called by Debugger::getVars.
 ******************************************************************************/
SIZE_T Debugger::GetArgCount(MethodDesc *fd,BOOL *fVarArg /* = NULL */)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // Create a MetaSig for the given method's sig. (Easier than
    // picking the sig apart ourselves.)
    PCCOR_SIGNATURE pCallSig;
    DWORD cbCallSigSize;

    fd->GetSig(&pCallSig, &cbCallSigSize);

    if (pCallSig == NULL)
    {
        // Sig should only be null if the image is corrupted. (Even for lightweight-codegen)
        // We expect the jit+verifier to catch this, so that we never land here.
        // But just in case ...
        CONSISTENCY_CHECK_MSGF(false, ("Corrupted image, null sig.(%s::%s)", fd->m_pszDebugClassName, fd->m_pszDebugMethodName));
        return 0;
    }

    MetaSig msig(pCallSig, cbCallSigSize, g_pEEInterface->MethodDescGetModule(fd), NULL, MetaSig::sigMember);

    // Get the arg count.
    UINT32 NumArguments = msig.NumFixedArgs();

    // Account for the 'this' argument.
    if (!(g_pEEInterface->MethodDescIsStatic(fd)))
        NumArguments++;

    // Is this a VarArg's function?
    if (msig.IsVarArg() && fVarArg != NULL)
    {
        *fVarArg = true;
    }

    return NumArguments;
}

#endif // #ifndef DACCESS_COMPILE





/******************************************************************************
    DebuggerJitInfo * Debugger::GetJitInfo():   GetJitInfo
    will return a pointer to a DebuggerJitInfo.  If the DJI
    doesn't exist, or it does exist, but the method has actually
    been pitched (and the caller wants pitched methods filtered out),
    then we'll return NULL.

    Note: This will also create a DMI for if one does not exist for this DJI.

    MethodDesc* fd:  MethodDesc for the method we're interested in.
    CORDB_ADDRESS_TYPE * pbAddr:  Address within the code, to indicate which
            version we want.  If this is NULL, then we want the
            head of the DebuggerJitInfo list, whether it's been
            JITted or not.
 ******************************************************************************/


// Get a DJI from an address.
DebuggerJitInfo *Debugger::GetJitInfoFromAddr(TADDR addr)
{
    WRAPPER_NO_CONTRACT;

    MethodDesc *fd;
    fd = g_pEEInterface->GetNativeCodeMethodDesc(addr);
    _ASSERTE(fd);

    return GetJitInfo(fd, (const BYTE*) addr, NULL);
}

// Get a DJI for a Native MD (MD for a native function).
// In the EnC scenario, the MethodDesc refers to the most recent method.
// This is very dangerous since there may be multiple versions alive at the same time.
// This will give back the wrong DJI if we're looking for a stale method desc.
// @todo - can a caller possibly use this correctly?
DebuggerJitInfo *Debugger::GetLatestJitInfoFromMethodDesc(MethodDesc * pMethodDesc)
{
    WRAPPER_NO_CONTRACT;

    _ASSERTE(pMethodDesc != NULL);
    // We'd love to assert that we're jitted; but since this may be in the JitComplete
    // callback path, we can't be sure.

    return GetJitInfoWorker(pMethodDesc, NULL, NULL);
}


DebuggerJitInfo *Debugger::GetJitInfo(MethodDesc *fd, const BYTE *pbAddr, DebuggerMethodInfo **pMethInfo )
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        PRECONDITION(!g_pDebugger->HasDebuggerDataLock());
    }
    CONTRACTL_END;

    // Address should be non-null and in range of MethodDesc. This lets us tell which EnC version.
    _ASSERTE(pbAddr != NULL);

    return GetJitInfoWorker(fd, pbAddr, pMethInfo);

}

// Internal worker to GetJitInfo. Doesn't validate parameters.
DebuggerJitInfo *Debugger::GetJitInfoWorker(MethodDesc *fd, const BYTE *pbAddr, DebuggerMethodInfo **pMethInfo)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        PRECONDITION(!g_pDebugger->HasDebuggerDataLock());
    }
    CONTRACTL_END;

    DebuggerMethodInfo *dmi = NULL;
    DebuggerJitInfo *dji = NULL;

    if (pMethInfo)
    {
        *pMethInfo = NULL;
    }

    // If we have a null MethodDesc - we're not going to get a jit-info. Do this check once at the top
    // rather than littered throughout the rest of this function.
    if (fd == NULL)
    {
        LOG((LF_CORDB, LL_EVERYTHING, "D::GJIW: addr=0x%p - null fd - returning null\n", pbAddr));
        return NULL;
    }

    CONSISTENCY_CHECK_MSGF(!fd->IsWrapperStub(), ("Can't get Jit-info for wrapper MDesc,'%s'", fd->m_pszDebugMethodName));

    // The debugger doesn't track dynamic methods b/c they have no metadata.
    if (fd->IsDynamicMethod())
    {
        return NULL;
    }

    // Find the DJI via the DMI
    //
    // One way to improve the perf, both in terms of memory usage, number of allocations
    // and lookup speeds would be to have the first JitInfo inline in the MethodInfo
    // struct.  After all, we never want to have a MethodInfo in the table without an
    // associated JitInfo, and this should bring us back very close to the old situation
    // in terms of perf.  But correctness comes first, and perf later...
    dmi = GetOrCreateMethodInfo(fd->GetModule(), fd->GetMemberDef());
    if (dmi == NULL)
    {
        // If we can't create the DMI, we won't be able to create the DJI.
        return NULL;
    }

    if (pbAddr == NULL)
    {
        dji = dmi->GetLatestJitInfo(fd);
    }
#ifndef DACCESS_COMPILE
    else
    {
        PCODE startAddr = g_pEEInterface->GetNativeCodeStartAddress((PCODE)pbAddr);
        if (startAddr == (PCODE)NULL)
        {
            LOG((LF_CORDB,LL_INFO1000,"D::GJIW: Couldn't find a DJI by address 0x%p, "
                "so it might be a stub or thunk\n", pbAddr));

            // if the address wasn't in jitted code we'll also check to see if it is a stub that leads to jitted code
            TraceDestination trace;
            (void)g_pEEInterface->TraceStub(pbAddr, &trace);
            if(trace.GetTraceType() == TRACE_MANAGED && (PCODE)pbAddr != trace.GetAddress())
            {
                startAddr = trace.GetAddress();
                LOG((LF_CORDB,LL_INFO1000,"D::GJIW: Address thru thunk: 0x%p\n", startAddr));
            }
#ifdef LOGGING
            else
            {
                _ASSERTE(trace.GetTraceType() != TRACE_UNJITTED_METHOD || (fd == trace.GetMethodDesc()));
                LOG((LF_CORDB,LL_INFO1000,"D::GJIW: Address not thunked - "
                    "must be to unJITted method, or normal managed "
                    "method lacking a DJI!\n"));
            }
#endif // LOGGING
        }

        if (startAddr != (PCODE)NULL)
        {
            dji = dmi->FindOrCreateInitAndAddJitInfo(fd, startAddr);
        }
    }
#endif // !DACCESS_COMPILE

    if (pMethInfo)
    {
        *pMethInfo = dmi;
    }

    return dji;
}

DebuggerMethodInfo *Debugger::GetOrCreateMethodInfo(Module *pModule, mdMethodDef token)
{
    CONTRACTL
    {
        SUPPORTS_DAC;
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    DebuggerMethodInfo *info = NULL;

    // When dump debugging, we don't expect to have a lock,
    // nor would it be useful for anything.
    ALLOW_DATATARGET_MISSING_MEMORY(
        // In case we don't have already, take it now.
        DebuggerDataLockHolder debuggerDataLockHolder(this);
    );

    if (m_pMethodInfos != NULL)
    {
        info = m_pMethodInfos->GetMethodInfo(pModule, token);
    }

    // dac checks ngen'ed image content first, so
    // if we didn't find information it doesn't exist.
#ifndef DACCESS_COMPILE
    if (info == NULL)
    {
        info = CreateMethodInfo(pModule, token);

        LOG((LF_CORDB, LL_INFO1000, "D::GOCMI: created DMI for mdToken:0x%x, dmi:0x%p\n",
            token, info));
    }
#endif // #ifndef DACCESS_COMPILE


    if (info == NULL)
    {
        // This should only happen in an oom scenario. It would be nice to throw here.
        STRESS_LOG2(LF_CORDB, LL_EVERYTHING, "OOM - Failed to allocate DJI (0x%p, 0x%x)\n", pModule, token);
    }

    // DebuggerDataLockHolder out of scope - release implied
    return info;
}


#ifndef DACCESS_COMPILE

// Helper to use w/ the debug stores.
BYTE* InteropSafeNoThrowNew(void*, size_t cBytes)
{
    BYTE* p = new (interopsafe, nothrow) BYTE[cBytes];
    return p;
}

/******************************************************************************
 * GetILToNativeMapping returns a map from IL offsets to native
 * offsets for this code. An array of COR_PROF_IL_TO_NATIVE_MAP
 * structs will be returned, and some of the ilOffsets in this array
 * may be the values specified in CorDebugIlToNativeMappingTypes.
 ******************************************************************************/
HRESULT Debugger::GetILToNativeMapping(PCODE pNativeCodeStartAddress, ULONG32 cMap,
                                       ULONG32 *pcMap, COR_DEBUG_IL_TO_NATIVE_MAP map[])
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS_FROM_GETJITINFO;
    }
    CONTRACTL_END;

    _ASSERTE(pNativeCodeStartAddress != (PCODE)NULL);

#ifdef PROFILING_SUPPORTED
    // At this point, we're pulling in the debugger.
    if (!HasLazyData())
    {
        DebuggerLockHolder lockHolder(this);
        LazyInit(); // throws
    }

    // Get the JIT info by functionId.

    // This function is unsafe to use during EnC because the MethodDesc doesn't tell
    // us which version is being requested.
    // However, this function is only used by the profiler, and you can't profile with EnC,
    // which means that getting the latest jit-info is still correct.
#if defined(PROFILING_SUPPORTED)
    _ASSERTE(CORProfilerPresent());
#endif // PROFILING_SUPPORTED

    MethodDesc *fd = g_pEEInterface->GetNativeCodeMethodDesc(pNativeCodeStartAddress);
    if (fd == NULL || fd->IsWrapperStub())
    {
        return E_FAIL;
    }

    if (fd->IsDynamicMethod())
    {
        if (g_pConfig->GetTrackDynamicMethodDebugInfo())
        {
            DebugInfoRequest diq;
            diq.InitFromStartingAddr(fd, pNativeCodeStartAddress);

            if (cMap == 0)
            {
                if (DebugInfoManager::GetBoundariesAndVars(diq, nullptr, nullptr, pcMap, nullptr, nullptr, nullptr))
                {
                    return S_OK;
                }

                return E_FAIL;
            }

            ICorDebugInfo::OffsetMapping* pMap = nullptr;
            if (DebugInfoManager::GetBoundariesAndVars(diq, InteropSafeNoThrowNew, nullptr, pcMap, &pMap, nullptr, nullptr))
            {
                for (ULONG32 i = 0; i < cMap; ++i)
                {
                    map[i].ilOffset = pMap[i].ilOffset;
                    map[i].nativeStartOffset = pMap[i].nativeOffset;
                    if (i > 0)
                    {
                        map[i - 1].nativeEndOffset = map[i].nativeStartOffset;
                    }
                }

                DeleteInteropSafe(pMap);

                return S_OK;
            }

            return E_FAIL;
        }
        else
        {
            return E_FAIL;
        }
    }

    DebuggerMethodInfo *pDMI = GetOrCreateMethodInfo(fd->GetModule(), fd->GetMemberDef());
    if (pDMI == NULL)
    {
        return E_FAIL;
    }

    DebuggerJitInfo *pDJI = pDMI->FindOrCreateInitAndAddJitInfo(fd, pNativeCodeStartAddress);

    // Dunno what went wrong
    if (pDJI == NULL)
        return (E_FAIL);

    // If they gave us space to copy into...
    if (map != NULL)
    {
        // Only copy as much as either they gave us or we have to copy.
        ULONG32 cpyCount = min(cMap, pDJI->GetSequenceMapCount());

        // Read the map right out of the Left Side.
        if (cpyCount > 0)
            ExportILToNativeMap(cpyCount,
                        map,
                        pDJI->GetSequenceMap(),
                        pDJI->m_sizeOfCode);
    }

    // Return the true count of entries
    if (pcMap)
    {
        *pcMap = pDJI->GetSequenceMapCount();
    }

    return (S_OK);
#else
    return E_NOTIMPL;
#endif
}


//---------------------------------------------------------------------------------------
//
// This is morally the same as GetILToNativeMapping, except the output is in a different
// format, to better facilitate sending the ETW ILToNativeMap events.
//
// Arguments:
//      pMD - MethodDesc whose IL-to-native map will be returned
//      cMapMax - Max number of map entries to return.  Although
//                this function handles the allocation of the returned
//                array, the caller still wants to limit how big this
//                can get, since ETW itself has limits on how big
//                events can get
//      pcMap - [out] Number of entries returned in each output parallel array (next
//                    two parameters).
//      prguiILOffset - [out] Array of IL offsets.  This function allocates, caller must free.
//      prguiNativeOffset - [out] Array of the starting native offsets that correspond
//                                to each (*prguiILOffset)[i].  This function allocates,
//                                caller must free.
//
// Return Value:
//      HRESULT indicating success or failure.
//
// Notes:
//     * This function assumes lazy data has already been initialized (in order to
//         ensure that this doesn't trigger or take the large debugger mutex).  So
//         callers must guarantee they call InitializeLazyDataIfNecessary() first.
//     * Either this function fails, and (*prguiILOffset) & (*prguiNativeOffset) will be
//         untouched OR this function succeeds and (*prguiILOffset) & (*prguiNativeOffset)
//         will both be non-NULL, set to the parallel arrays this function allocated.
//     *  If this function returns success, then the caller must free (*prguiILOffset) and
//         (*prguiNativeOffset)
//     * (*prguiILOffset) and (*prguiNativeOffset) are parallel arrays, such that
//         (*prguiILOffset)[i] corresponds to (*prguiNativeOffset)[i] for each 0 <= i < *pcMap
//     * If EnC is enabled, this function will return the IL-to-native mapping for the latest
//         EnC version of the function.  This may not be what the profiler wants, but EnC
//         + ETW-map events is not a typical combination, and this is consistent with
//         other ETW events like JittingStarted or MethodLoad, which also fire multiple
//         events for the same MethodDesc (each time it's EnC'd), with each event
//         corresponding to the most recent EnC version at the time.
//

HRESULT Debugger::GetILToNativeMappingIntoArrays(
    MethodDesc * pMethodDesc,
    PCODE pNativeCodeStartAddress,
    USHORT cMapMax,
    USHORT * pcMap,
    UINT ** prguiILOffset,
    UINT ** prguiNativeOffset)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    _ASSERTE(pMethodDesc != NULL);
    _ASSERTE(pcMap != NULL);
    _ASSERTE(prguiILOffset != NULL);
    _ASSERTE(prguiNativeOffset != NULL);
    _ASSERTE(pNativeCodeStartAddress != (PCODE)NULL);

    // Any caller of GetILToNativeMappingIntoArrays had better call
    // InitializeLazyDataIfNecessary first!
    _ASSERTE(HasLazyData());

    // Get the JIT info by functionId.

    if (pMethodDesc->IsWrapperStub() || pMethodDesc->IsDynamicMethod())
    {
        return E_FAIL;
    }

    DebuggerMethodInfo *pDMI = GetOrCreateMethodInfo(pMethodDesc->GetModule(), pMethodDesc->GetMemberDef());
    if (pDMI == NULL)
    {
        return E_FAIL;
    }

    DebuggerJitInfo *pDJI = pDMI->FindOrCreateInitAndAddJitInfo(pMethodDesc, pNativeCodeStartAddress);

    // Dunno what went wrong
    if (pDJI == NULL)
        return E_FAIL;

    ULONG32 cMap = min((ULONG32)cMapMax, pDJI->GetSequenceMapCount());
    DebuggerILToNativeMap * rgMapInt = pDJI->GetSequenceMap();

    NewArrayHolder<UINT> rguiILOffsetTemp = new (nothrow) UINT[cMap];
    if (rguiILOffsetTemp == NULL)
        return E_OUTOFMEMORY;

    NewArrayHolder<UINT> rguiNativeOffsetTemp = new (nothrow) UINT[cMap];
    if (rguiNativeOffsetTemp == NULL)
        return E_OUTOFMEMORY;

    for (ULONG32 iMap=0; iMap < cMap; iMap++)
    {
        rguiILOffsetTemp[iMap] = rgMapInt[iMap].ilOffset;
        rguiNativeOffsetTemp[iMap] = rgMapInt[iMap].nativeStartOffset;
    }

    // Since cMap is the min of cMapMax (and something else) and cMapMax is a USHORT,
    // then cMap must fit in a USHORT as well
    _ASSERTE(FitsIn<USHORT>(cMap));
    *pcMap = (USHORT) cMap;
    *prguiILOffset = rguiILOffsetTemp.Extract();
    *prguiNativeOffset = rguiNativeOffsetTemp.Extract();

    return S_OK;
}




#endif // #ifndef DACCESS_COMPILE

/******************************************************************************
 *
 ******************************************************************************/
CodeRegionInfo CodeRegionInfo::GetCodeRegionInfo(DebuggerJitInfo *dji, MethodDesc *md, PTR_CORDB_ADDRESS_TYPE addr)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        SUPPORTS_DAC;
        MODE_ANY;
    }
    CONTRACTL_END;

    if (dji && dji->m_addrOfCode)
    {
        LOG((LF_CORDB, LL_INFO10000, "CRI::GCRI: simple case: CodeRegionInfo* %p\n", &dji->m_codeRegionInfo));
        return dji->m_codeRegionInfo;
    }
    else
    {
        LOG((LF_CORDB, LL_INFO10000, "CRI::GCRI: more complex case\n"));
        CodeRegionInfo codeRegionInfo;

        // Use method desc from dji if present
        if (dji && dji->m_nativeCodeVersion.GetMethodDesc())
        {
            _ASSERTE(!md || md == dji->m_nativeCodeVersion.GetMethodDesc());
            md = dji->m_nativeCodeVersion.GetMethodDesc();
        }

        if (!addr)
        {
            _ASSERTE(md);
            addr = dac_cast<PTR_CORDB_ADDRESS_TYPE>(g_pEEInterface->GetFunctionAddress(md));
        }
        else
        {
            _ASSERTE(!md ||
                     (addr == dac_cast<PTR_CORDB_ADDRESS_TYPE>(g_pEEInterface->GetFunctionAddress(md))));
        }

        LOG((LF_CORDB, LL_INFO10000, "CRI::GCRI: Initializing CodeRegionInfo from %p, md=%p\n", addr, md));
        if (addr)
        {
            PCODE pCode = PINSTRToPCODE(dac_cast<TADDR>(addr));
            codeRegionInfo.InitializeFromStartAddress(pCode);
        }

        return codeRegionInfo;
    }
}


#ifndef DACCESS_COMPILE
/******************************************************************************
//  Helper function for getBoundaries to get around AMD64 compiler and
// contract holders with PAL_TRY in the same function.
 ******************************************************************************/
void Debugger::getBoundariesHelper(MethodDesc * md,
                                   unsigned int *cILOffsets,
                                   DWORD **pILOffsets)
{
    //
    // CANNOT ADD A CONTRACT HERE.  Contract is in getBoundaries
    //

    //
    // Grab the JIT info struct for this method.  Create if needed, as this
    // may be called before JITComplete.
    //
    DebuggerMethodInfo *dmi = NULL;
    dmi = GetOrCreateMethodInfo(md->GetModule(), md->GetMemberDef());

    if (dmi != NULL)
    {
        LOG((LF_CORDB,LL_INFO10000,"De::NGB: Got dmi %p\n",dmi));

#if defined(FEATURE_ISYM_READER)
        // Note: we need to make sure to enable preemptive GC here just in case we block in the symbol reader.
        GCX_PREEMP_EEINTERFACE();

        Module *pModule = md->GetModule();
        (void)pModule; //prevent "unused variable" error from GCC
        _ASSERTE(pModule != NULL);

        SafeComHolder<ISymUnmanagedReader> pReader(pModule->GetISymUnmanagedReader());

        // If we got a reader, use it.
        if (pReader != NULL)
        {
            // Grab the sym reader's method.
            ISymUnmanagedMethod *pISymMethod;

            HRESULT hr = pReader->GetMethod(md->GetMemberDef(),
                                            &pISymMethod);

            ULONG32 n = 0;

            if (SUCCEEDED(hr))
            {
                // Get the count of sequence points.
                hr = pISymMethod->GetSequencePointCount(&n);
                _ASSERTE(SUCCEEDED(hr));


                LOG((LF_CORDB, LL_INFO100000,
                     "D::NGB: Reader seq pt count is %u\n", n));

                ULONG32 *p;

                if (n > 0)
                {
                    ULONG32 dummy;

                    p = new ULONG32[n];
                    _ASSERTE(p != NULL); // throws on oom error

                    hr = pISymMethod->GetSequencePoints(n, &dummy,
                                                        p, NULL, NULL, NULL,
                                                        NULL, NULL);
                    _ASSERTE(SUCCEEDED(hr));
                    _ASSERTE(dummy == n);

                    *pILOffsets = (DWORD*)p;

                    // Translate the IL offsets based on an
                    // instrumented IL map if one exists.
                    if (dmi->HasInstrumentedILMap())
                    {
                        InstrumentedILOffsetMapping mapping =
                            dmi->GetRuntimeModule()->GetInstrumentedILOffsetMapping(dmi->m_token);

                        for (SIZE_T i = 0; i < n; i++)
                        {
                            int origOffset = *p;

                            *p = dmi->TranslateToInstIL(
                                                  &mapping,
                                                  origOffset,
                                                  bOriginalToInstrumented);

                            LOG((LF_CORDB, LL_INFO100000,
                                 "D::NGB: 0x%04x (Real IL:0x%x)\n",
                                 origOffset, *p));

                            p++;
                        }
                    }
#ifdef LOGGING
                    else
                    {
                        for (SIZE_T i = 0; i < n; i++)
                        {
                            LOG((LF_CORDB, LL_INFO100000,
                                 "D::NGB: 0x%04x \n", *p));
                            p++;
                        }
                    }
#endif
                }
                else
                    *pILOffsets = NULL;

                pISymMethod->Release();
            }
            else
            {

                *pILOffsets = NULL;

                LOG((LF_CORDB, LL_INFO10000,
                     "De::NGB: failed to find method 0x%x in sym reader.\n",
                     md->GetMemberDef()));
            }

            *cILOffsets = n;
        }
        else
        {
            LOG((LF_CORDB, LL_INFO100000, "D::NGB: no reader.\n"));
        }

#else // FEATURE_ISYM_READER
        // We don't have ISymUnmanagedReader.  Pretend there are no sequence points.
        *cILOffsets = 0;
#endif // FEATURE_ISYM_READER
    }

    LOG((LF_CORDB, LL_INFO100000, "D::NGB: cILOffsets=%d\n", *cILOffsets));
    return;
}
#endif

/******************************************************************************
// Use an ISymUnmanagedReader to get method sequence points.
 ******************************************************************************/
void Debugger::getBoundaries(MethodDesc * md,
                             unsigned int *cILOffsets,
                             DWORD **pILOffsets,
                             ICorDebugInfo::BoundaryTypes *implicitBoundaries)
{
#ifndef DACCESS_COMPILE
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
    }
    CONTRACTL_END;

    // May be here even when a debugger is not attached.

    // @@@
    // Implements DebugInterface API

    *cILOffsets = 0;
    *pILOffsets = NULL;
    *implicitBoundaries = ICorDebugInfo::DEFAULT_BOUNDARIES;
    // If there has been an unrecoverable Left Side error, then we
    // just pretend that there are no boundaries.
    if (CORDBUnrecoverableError(this))
    {
        return;
    }

    // LCG methods have their own resolution scope that is separate from a module
    // so they shouldn't have their symbols looked up in the module PDB. Right now
    // LCG methods have no symbols so we can just early out, but if they ever
    // had some symbols attached we would need a different way of getting to them.
    // See Dev10 issue 728519
    if(md->IsLCGMethod())
    {
        return;
    }

    // If JIT optimizations are allowed for the module this function
    // lives in, then don't grab specific boundaries from the symbol
    // store since any boundaries we give the JIT will be pretty much
    // ignored anyway.
    if (!CORDisableJITOptimizations(md->GetModule()->GetDebuggerInfoBits()))
    {
        *implicitBoundaries  = ICorDebugInfo::BoundaryTypes(ICorDebugInfo::STACK_EMPTY_BOUNDARIES |
                                         ICorDebugInfo::CALL_SITE_BOUNDARIES);

        return;
    }

    Module* pModule = md->GetModule();
    DWORD dwBits = pModule->GetDebuggerInfoBits();
    if ((dwBits & DACF_IGNORE_PDBS) != 0)
    {
        //
        // If told to explicitly ignore PDBs for this function, then bail now.
        //
        return;
    }

    if( !pModule->IsSymbolReadingEnabled() )
    {
        // Symbol reading is disabled for this module, so bail out early (for efficiency only)
        return;
    }

    if (pModule == SystemDomain::SystemModule())
    {
        // We don't look up PDBs for CoreLib.  This is not quite right, but avoids
        // a bootstrapping problem.  When an EXE loads, it has the option of setting
        // the COM apartment model to STA if we need to.  It is important that no
        // other Coinitialize happens before this.  Since loading the PDB reader uses
        // com we can not come first.  However managed code IS run before the COM
        // apartment model is set, and thus we have a problem since this code is
        // called for when JITTing managed code.    We avoid the problem by just
        // bailing for CoreLib.
        return;
    }

        // At this point, we're pulling in the debugger.
    if (!HasLazyData())
    {
        DebuggerLockHolder lockHolder(this);
        LazyInit(); // throws
    }

    getBoundariesHelper(md, cILOffsets, pILOffsets);

#else
    DacNotImpl();
#endif // #ifndef DACCESS_COMPILE
}


/******************************************************************************
 *
 ******************************************************************************/
void Debugger::getVars(MethodDesc * md, ULONG32 *cVars, ICorDebugInfo::ILVarInfo **vars,
                       bool *extendOthers)
{
#ifndef DACCESS_COMPILE
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS_FROM_GETJITINFO;
        PRECONDITION(!ThisIsHelperThreadWorker());
    }
    CONTRACTL_END;



    // At worst return no information
    *cVars = 0;
    *vars = NULL;

    // Just tell the JIT to extend everything.
    // Note that if optimizations are enabled, the native compilers are
    // free to ignore *extendOthers
    *extendOthers = true;

    DWORD bits = md->GetModule()->GetDebuggerInfoBits();

    if (CORDBUnrecoverableError(this))
        goto Exit;

    if (CORDisableJITOptimizations(bits))
//    if (!CORDebuggerAllowJITOpts(bits))
    {
        //
        // @TODO: Do we really need this code since *extendOthers==true?
        //

        // Is this a vararg function?
        BOOL fVarArg = false;
        GetArgCount(md, &fVarArg);

        if (fVarArg)
        {
            COR_ILMETHOD *ilMethod = g_pEEInterface->MethodDescGetILHeader(md);

            if (ilMethod)
            {
                // It is, so we need to tell the JIT to give us the
                // varags handle.
                ICorDebugInfo::ILVarInfo *p = new ICorDebugInfo::ILVarInfo[1];
                _ASSERTE(p != NULL); // throws on oom error

                COR_ILMETHOD_DECODER header(ilMethod);
                unsigned int ilCodeSize = header.GetCodeSize();

                p->startOffset = 0;
                p->endOffset = ilCodeSize;
                p->varNumber = (DWORD) ICorDebugInfo::VARARGS_HND_ILNUM;

                *cVars = 1;
                *vars = p;
            }
        }
    }

    LOG((LF_CORDB, LL_INFO100000, "D::gV: cVars=%d, extendOthers=%d\n",
         *cVars, *extendOthers));

Exit:
    ;
#else
    DacNotImpl();
#endif // #ifndef DACCESS_COMPILE
}


#ifndef DACCESS_COMPILE

// If we have a varargs function, we can't set the IP (we don't know how to pack/unpack the arguments), so if we
// call SetIP with fCanSetIPOnly = true, we need to check for that.
// Arguments:
//     input:  nEntries      - number of entries in varNativeInfo
//             varNativeInfo - array of entries describing the args and locals for the function
//     output: true iff the function has varargs
BOOL Debugger::IsVarArgsFunction(unsigned int nEntries, PTR_NativeVarInfo varNativeInfo)
{
    for (unsigned int i = 0; i < nEntries; ++i)
    {
        if (varNativeInfo[i].loc.vlType == ICorDebugInfo::VLT_FIXED_VA)
        {
            return TRUE;
        }
    }
    return FALSE;
}

// We want to keep the 'worst' HRESULT - if one has failed (..._E_...) & the
// other hasn't, take the failing one.  If they've both/neither failed, then
// it doesn't matter which we take.
// Note that this macro favors retaining the first argument
#define WORST_HR(hr1,hr2) (FAILED(hr1)?hr1:hr2)
/******************************************************************************
 *
 ******************************************************************************/
HRESULT Debugger::SetIP( bool fCanSetIPOnly, Thread *thread,Module *module,
                         mdMethodDef mdMeth, DebuggerJitInfo* dji,
                         SIZE_T offsetILTo, BOOL fIsIL)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(CheckPointer(thread));
        PRECONDITION(CheckPointer(module));
        PRECONDITION(mdMeth != mdMethodDefNil);
    }
    CONTRACTL_END;

#ifdef _DEBUG
    static ConfigDWORD breakOnSetIP;
    if (breakOnSetIP.val(CLRConfig::INTERNAL_DbgBreakOnSetIP)) _ASSERTE(!"DbgBreakOnSetIP");
#endif

    HRESULT hr = S_OK;
    HRESULT hrAdvise = S_OK;

    DWORD offsetILFrom;
    CorDebugMappingResult map;
    DWORD whichIgnore;

    ControllerStackInfo csi;

    BOOL exact;
    SIZE_T offsetNatTo;

    PCODE    pbDest = (PCODE)NULL;
    BYTE    *pbBase = NULL;
    CONTEXT *pCtx   = NULL;
    DWORD    dwSize = 0;
    SIZE_T  *rgVal1 = NULL;
    SIZE_T  *rgVal2 = NULL;
    BYTE **pVCs   = NULL;

    LOG((LF_CORDB, LL_INFO1000, "D::SIP: In SetIP ==> fCanSetIPOnly:0x%x <==!\n", fCanSetIPOnly));

    CodeVersionManager *pCodeVersionManager = module->GetCodeVersionManager();
    {
        CodeVersionManager::LockHolder codeVersioningLockHolder;
        ILCodeVersion ilCodeVersion = pCodeVersionManager->GetActiveILCodeVersion(module, mdMeth);
        if (!ilCodeVersion.IsDefaultVersion())
        {
            return CORDBG_E_SET_IP_IMPOSSIBLE;
        }
    }

    pCtx = GetManagedStoppedCtx(thread);

    // If we can't get a context, then we can't possibly be a in a good place
    // to do a setip.
    if (pCtx == NULL)
    {
        return CORDBG_S_BAD_START_SEQUENCE_POINT;
    }

    // Implicit Caveat: We need to be the active frame.
    // We can safely take a stack trace because the thread is synchronized.
    StackTraceTicket ticket(thread);
    csi.GetStackInfo(ticket, thread, LEAF_MOST_FRAME, NULL);

    ULONG offsetNatFrom = csi.m_activeFrame.relOffset;
#if defined(FEATURE_EH_FUNCLETS)
    if (csi.m_activeFrame.IsFuncletFrame())
    {
        offsetNatFrom = (ULONG)((SIZE_T)GetControlPC(&(csi.m_activeFrame.registers)) -
                                (SIZE_T)(dji->m_addrOfCode));
    }
#endif // FEATURE_EH_FUNCLETS

    _ASSERTE(dji != NULL);

    // On WIN64 platforms, it's important to use the total size of the
    // parent method and the funclets below (i.e. m_sizeOfCode).  Don't use
    // the size of the individual funclets or the parent method.
    pbBase = (BYTE*)CORDB_ADDRESS_TO_PTR(dji->m_addrOfCode);
    dwSize = (DWORD)dji->m_sizeOfCode;
#if defined(FEATURE_EH_FUNCLETS)
    // Currently, method offsets are not bigger than 4 bytes even on WIN64.
    // Assert that it is so here.
    _ASSERTE((SIZE_T)dwSize == dji->m_sizeOfCode);
#endif // FEATURE_EH_FUNCLETS


    // Create our structure for analyzing this.
    // <TODO>@PERF: optimize - hold on to this so we don't rebuild it for both
    // CanSetIP & SetIP.</TODO>
    int           cFunclet  = 0;
    const DWORD * rgFunclet = NULL;
#if defined(FEATURE_EH_FUNCLETS)
    cFunclet  = dji->GetFuncletCount();
    rgFunclet = dji->m_rgFunclet;
#endif // FEATURE_EH_FUNCLETS

    EHRangeTree* pEHRT = new (nothrow) EHRangeTree(csi.m_activeFrame.pIJM,
                                                   csi.m_activeFrame.MethodToken,
                                                   dwSize,
                                                   cFunclet,
                                                   rgFunclet);

    // To maintain the current semantics, we will check the following right before SetIPFromSrcToDst() is called
    // (instead of checking them now):
    // 1) pEHRT == NULL
    // 2) FAILED(pEHRT->m_hrInit)


    {
        LOG((LF_CORDB, LL_INFO1000, "D::SIP:Got version info fine\n"));

        // Caveat: we need to start from a sequence point
        offsetILFrom = dji->MapNativeOffsetToIL(offsetNatFrom,
                                                &map, &whichIgnore);
        if ( !(map & MAPPING_EXACT) )
        {
            LOG((LF_CORDB, LL_INFO1000, "D::SIP:Starting native offset is bad!\n"));
            hrAdvise = WORST_HR(hrAdvise, CORDBG_S_BAD_START_SEQUENCE_POINT);
        }
        else
        {   // exact IL mapping

            if (!(dji->GetSrcTypeFromILOffset(offsetILFrom) & ICorDebugInfo::STACK_EMPTY))
            {
                LOG((LF_CORDB, LL_INFO1000, "D::SIP:Starting offset isn't stack empty!\n"));
                hrAdvise = WORST_HR(hrAdvise, CORDBG_S_BAD_START_SEQUENCE_POINT);
            }
        }

        // Caveat: we need to go to a sequence point
        if (fIsIL )
        {
#if defined(FEATURE_EH_FUNCLETS)
            int funcletIndexFrom = dji->GetFuncletIndex((CORDB_ADDRESS)offsetNatFrom, DebuggerJitInfo::GFIM_BYOFFSET);
            offsetNatTo = dji->MapILOffsetToNativeForSetIP(offsetILTo, funcletIndexFrom, pEHRT, &exact);
#else  // FEATURE_EH_FUNCLETS
            DebuggerJitInfo::ILToNativeOffsetIterator it;
            dji->InitILToNativeOffsetIterator(it, offsetILTo);
            offsetNatTo = it.CurrentAssertOnlyOne(&exact);
#endif // FEATURE_EH_FUNCLETS

            if (!exact)
            {
                LOG((LF_CORDB, LL_INFO1000, "D::SIP:Dest (via IL offset) is bad!\n"));
                hrAdvise = WORST_HR(hrAdvise, CORDBG_S_BAD_END_SEQUENCE_POINT);
            }
        }
        else
        {
            offsetNatTo = offsetILTo;
            LOG((LF_CORDB, LL_INFO1000, "D::SIP:Dest of 0x%p (via native "
                "offset) is fine!\n", offsetNatTo));
        }

        CorDebugMappingResult mapping;
        DWORD which;
        offsetILTo = dji->MapNativeOffsetToIL(offsetNatTo, &mapping, &which);

        // We only want to perhaps return CORDBG_S_BAD_END_SEQUENCE_POINT if
        // we're not already returning CORDBG_S_BAD_START_SEQUENCE_POINT.
        if (hr != CORDBG_S_BAD_START_SEQUENCE_POINT)
        {
            if ( !(mapping & MAPPING_EXACT) )
            {
                LOG((LF_CORDB, LL_INFO1000, "D::SIP:Ending native offset is bad!\n"));
                hrAdvise = WORST_HR(hrAdvise, CORDBG_S_BAD_END_SEQUENCE_POINT);
            }
            else
            {
                // <NOTE WIN64>
                // All duplicate sequence points (ones with the same IL offset) should have the same SourceTypes.
                // </NOTE WIN64>
                if (!(dji->GetSrcTypeFromILOffset(offsetILTo) & ICorDebugInfo::STACK_EMPTY))
                {
                    LOG((LF_CORDB, LL_INFO1000, "D::SIP:Ending offset isn't a sequence"
                                                " point, or not stack empty!\n"));
                    hrAdvise = WORST_HR(hrAdvise, CORDBG_S_BAD_END_SEQUENCE_POINT);
                }
            }
        }

        // Once we finally have a native offset, it had better be in range.
        if (offsetNatTo >= dwSize)
        {
            LOG((LF_CORDB, LL_INFO1000, "D::SIP:Code out of range! offsetNatTo = 0x%x, dwSize=0x%x\n", offsetNatTo, dwSize));
            hrAdvise = E_INVALIDARG;
            goto LExit;
        }

        pbDest = CodeRegionInfo::GetCodeRegionInfo(dji).OffsetToAddress(offsetNatTo);
        LOG((LF_CORDB, LL_INFO1000, "D::SIP:Dest is 0x%p\n", pbDest));

        // Don't allow SetIP if the source or target is cold (SetIPFromSrcToDst does not
        // correctly handle this case).
        if (!CodeRegionInfo::GetCodeRegionInfo(dji).IsOffsetHot(offsetNatTo) ||
            !CodeRegionInfo::GetCodeRegionInfo(dji).IsOffsetHot(offsetNatFrom))
        {
            hrAdvise = WORST_HR(hrAdvise, CORDBG_E_SET_IP_IMPOSSIBLE);
            goto LExit;
        }
    }

    if (!fCanSetIPOnly)
    {
        hr = ShuffleVariablesGet(dji,
                                 offsetNatFrom,
                                 pCtx,
                                 &rgVal1,
                                 &rgVal2,
                                 &pVCs);
        LOG((LF_CORDB|LF_ENC,
             LL_INFO10000,
             "D::SIP: rgVal1 0x%X, rgVal2 0x%X\n",
             rgVal1,
             rgVal2));

        if (FAILED(hr))
        {
            // This will only fail fatally, so exit.
            hrAdvise = WORST_HR(hrAdvise, hr);
            goto LExit;
        }
    }
    else // fCanSetIPOnly
    {
        if (IsVarArgsFunction(dji->GetVarNativeInfoCount(), dji->GetVarNativeInfo()))
        {
            hrAdvise = E_INVALIDARG;
            goto LExit;
        }
    }


    if (pEHRT == NULL)
    {
        hr = E_OUTOFMEMORY;
    }
    else if (FAILED(pEHRT->m_hrInit))
    {
        hr = pEHRT->m_hrInit;
    }
    else
    {
        //
        // This is a known, ok, violation.  END_EXCEPTION_GLUE has a call to GetThrowable in it, but
        // we will never hit it because we are passing in NULL below.  This is to satisfy the static
        // contract analyzer.
        //
        CONTRACT_VIOLATION(GCViolation);

        EX_TRY
        {
            hr =g_pEEInterface->SetIPFromSrcToDst(thread,
                                                  pbBase,
                                                  offsetNatFrom,
                                                  (DWORD)offsetNatTo,
                                                  fCanSetIPOnly,
                                                  &(csi.m_activeFrame.registers),
                                                  pCtx,
                                                  (void *)dji,
                                                  pEHRT);
        }
        EX_CATCH
        {
        }
        EX_END_CATCH(SwallowAllExceptions);

    }

    // Get the return code, if any
    if (hr != S_OK)
    {
        hrAdvise = WORST_HR(hrAdvise, hr);
        goto LExit;
    }

    // If we really want to do this, we'll have to put the
    // variables into their new locations.
    if (!fCanSetIPOnly && !FAILED(hrAdvise))
    {
        // TODO: We should zero out any registers which have now become live GC roots,
        // but which aren't tracked variables (i.e. they are JIT temporaries).  Such registers may
        // have garbage left over in them, and we don't want the GC to try and dereference them
        // as object references.  However, we can't easily tell here which of the callee-saved regs
        // are used in this method and therefore safe to clear.
        //

        hr = ShuffleVariablesSet(dji,
                            offsetNatTo,
                            pCtx,
                            &rgVal1,
                            &rgVal2,
                            pVCs);


        if (hr != S_OK)
        {
            hrAdvise = WORST_HR(hrAdvise, hr);
            goto LExit;
        }

        _ASSERTE(pbDest != 0);

        ::SetIP(pCtx, pbDest);

        LOG((LF_CORDB, LL_INFO1000, "D::SIP:Set IP to be 0x%p\n", GetIP(pCtx)));
    }


LExit:
    if (rgVal1 != NULL)
    {
        DeleteInteropSafe(rgVal1);
    }

    if (rgVal2 != NULL)
    {
        DeleteInteropSafe(rgVal2);
    }

    if (pEHRT != NULL)
    {
        delete pEHRT;
    }

    LOG((LF_CORDB, LL_INFO1000, "D::SIP:Returning 0x%x\n", hr));
    return hrAdvise;
}

#include "nativevaraccessors.h"

/******************************************************************************
 *
 ******************************************************************************/

HRESULT Debugger::ShuffleVariablesGet(DebuggerJitInfo  *dji,
                                      SIZE_T            offsetFrom,
                                      CONTEXT          *pCtx,
                                      SIZE_T          **prgVal1,
                                      SIZE_T          **prgVal2,
                                      BYTE           ***prgpVCs)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(CheckPointer(dji));
        PRECONDITION(CheckPointer(pCtx));
        PRECONDITION(CheckPointer(prgVal1));
        PRECONDITION(CheckPointer(prgVal2));
        PRECONDITION(dji->m_sizeOfCode >= offsetFrom);
    }
    CONTRACTL_END;

    LONG cVariables = 0;
    DWORD i;

    //
    // Find the largest variable number
    //
    for (i = 0; i < dji->GetVarNativeInfoCount(); i++)
    {
        if ((LONG)(dji->GetVarNativeInfo()[i].varNumber) > cVariables)
        {
            cVariables = (LONG)(dji->GetVarNativeInfo()[i].varNumber);
        }
    }

    HRESULT hr = S_OK;

    //
    // cVariables is a zero-based count of the number of variables.  Increment it.
    //
    cVariables++;

    SIZE_T *rgVal1 = new (interopsafe, nothrow) SIZE_T[cVariables + unsigned(-ICorDebugInfo::UNKNOWN_ILNUM)];

    SIZE_T *rgVal2 = NULL;

    if (rgVal1 == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto LExit;
    }

    rgVal2 = new (interopsafe, nothrow) SIZE_T[cVariables + unsigned(-ICorDebugInfo::UNKNOWN_ILNUM)];

    if (rgVal2 == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto LExit;
    }

    memset(rgVal1, 0, sizeof(SIZE_T) * (cVariables + unsigned(-ICorDebugInfo::UNKNOWN_ILNUM)));
    memset(rgVal2, 0, sizeof(SIZE_T) * (cVariables + unsigned(-ICorDebugInfo::UNKNOWN_ILNUM)));

    LOG((LF_CORDB|LF_ENC,
         LL_INFO10000,
         "D::SVG cVariables %d, hiddens %d, rgVal1 0x%X, rgVal2 0x%X\n",
         cVariables,
         unsigned(-ICorDebugInfo::UNKNOWN_ILNUM),
         rgVal1,
         rgVal2));

    GetVariablesFromOffset(dji->m_nativeCodeVersion.GetMethodDesc(),
                           dji->GetVarNativeInfoCount(),
                           dji->GetVarNativeInfo(),
                           offsetFrom,
                           pCtx,
                           rgVal1,
                           rgVal2,
                           cVariables + unsigned(-ICorDebugInfo::UNKNOWN_ILNUM),
                           prgpVCs);


LExit:
    if (!FAILED(hr))
    {
        (*prgVal1) = rgVal1;
        (*prgVal2) = rgVal2;
    }
    else
    {
        LOG((LF_CORDB, LL_INFO100, "D::SVG: something went wrong hr=0x%x!", hr));

        (*prgVal1) = NULL;
        (*prgVal2) = NULL;

        if (rgVal1 != NULL)
            delete[] rgVal1;

        if (rgVal2 != NULL)
            delete[] rgVal2;
    }

    return hr;
}

/******************************************************************************
 *
 ******************************************************************************/
HRESULT Debugger::ShuffleVariablesSet(DebuggerJitInfo  *dji,
                                   SIZE_T            offsetTo,
                                   CONTEXT          *pCtx,
                                   SIZE_T          **prgVal1,
                                   SIZE_T          **prgVal2,
                                   BYTE            **rgpVCs)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(CheckPointer(dji));
        PRECONDITION(CheckPointer(pCtx));
        PRECONDITION(CheckPointer(prgVal1));
        PRECONDITION(CheckPointer(prgVal2));
        PRECONDITION(dji->m_sizeOfCode >= offsetTo);
    }
    CONTRACTL_END;

    LOG((LF_CORDB|LF_ENC,
         LL_INFO10000,
         "D::SVS: rgVal1 0x%X, rgVal2 0x%X\n",
         (*prgVal1),
         (*prgVal2)));

    HRESULT hr = SetVariablesAtOffset(dji->m_nativeCodeVersion.GetMethodDesc(),
                                      dji->GetVarNativeInfoCount(),
                                      dji->GetVarNativeInfo(),
                                      offsetTo,
                                      pCtx,
                                      *prgVal1,
                                      *prgVal2,
                                      rgpVCs);

    LOG((LF_CORDB|LF_ENC,
         LL_INFO100000,
         "D::SVS deleting rgVal1 0x%X, rgVal2 0x%X\n",
         (*prgVal1),
         (*prgVal2)));

    DeleteInteropSafe(*prgVal1);
    (*prgVal1) = NULL;
    DeleteInteropSafe(*prgVal2);
    (*prgVal2) = NULL;
    return hr;
}

//
// This class is used by Get and SetVariablesFromOffsets to manage a frameHelper
// list for the arguments and locals corresponding to each varNativeInfo. The first
// four are hidden args, but the remainder will all have a corresponding entry
// in the argument or local signature list.
//
// The structure of the array varNativeInfo contains home information for each variable
// at various points in the function.  Thus, you have to search for the proper native offset
// (IP) in the varNativeInfo, and then find the correct varNumber in that native offset to
// find the correct home information.
//
// Important to note is that the JIT has hidden args that have varNumbers that are negative.
// Thus we cannot use varNumber as a strict index into our holder arrays, and instead shift
// indexes before indexing into our holder arrays.
//
// The hidden args are a fixed-sized array given by the value of 0-UNKNOWN_ILNUM. These are used
// to pass cookies about the arguments (var args, generics, retarg buffer etc.) to the function.
// The real arguments and locals are as one would expect.
//

class GetSetFrameHelper
{
public:
    GetSetFrameHelper();
    ~GetSetFrameHelper();

    HRESULT Init(MethodDesc* pMD);

    bool GetValueClassSizeOfVar(int varNum, ICorDebugInfo::VarLocType varType, SIZE_T* pSize);
    int ShiftIndexForHiddens(int varNum);

private:
    MethodDesc*     m_pMD;
    SIZE_T*         m_rgSize;
    CorElementType* m_rgElemType;
    ULONG           m_numArgs;
    ULONG           m_numTotalVars;

    SIZE_T  GetValueClassSize(MetaSig* pSig);

    static SIZE_T  GetSizeOfElement(CorElementType cet);
};

//
// GetSetFrameHelper::GetSetFrameHelper()
//
// This is the constructor.  It just initailizes all member variables.
//
// parameters: none
//
// return value: none
//
GetSetFrameHelper::GetSetFrameHelper() : m_pMD(NULL), m_rgSize(NULL), m_rgElemType(NULL),
                                         m_numArgs(0), m_numTotalVars(0)
{
    LIMITED_METHOD_CONTRACT;
}

//
// GetSetFrameHelper::Init()
//
// This method extracts the element type and the size of the arguments and locals of the method we are doing
// the SetIP on and stores this information in instance variables.
//
// parameters:   pMD - MethodDesc of the method we are doing the SetIP on
//
// return value: S_OK or E_OUTOFMEMORY
//
HRESULT
GetSetFrameHelper::Init(MethodDesc *pMD)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
        PRECONDITION(CheckPointer(pMD));
    }
    CONTRACTL_END;

    HRESULT hr = S_OK;
    COR_ILMETHOD* pILHeader = NULL;
    m_pMD = pMD;
    MetaSig *pLocSig = NULL;
    MetaSig *pArgSig = NULL;

    m_rgSize = NULL;
    m_rgElemType = NULL;

    // Initialize decoderOldIL before checking the method argument signature.
    EX_TRY
    {
        pILHeader = pMD->GetILHeader();
    }
    EX_CATCH_HRESULT(hr);
    if (FAILED(hr))
        return hr;

    COR_ILMETHOD_DECODER decoderOldIL(pILHeader);
    mdSignature mdLocalSig = (decoderOldIL.GetLocalVarSigTok()) ? (decoderOldIL.GetLocalVarSigTok()):
                                                                  (mdSignatureNil);

    PCCOR_SIGNATURE pLocalSig = NULL;
    ULONG cbLocalSigSize = 0;

    PCCOR_SIGNATURE pCallSig;
    DWORD cbCallSigSize;

    pMD->GetSig(&pCallSig, &cbCallSigSize);
    if (pCallSig != NULL)
    {
        // Yes, we do need to pass in the text because this might be generic function!
        SigTypeContext tmpContext(pMD);

        pArgSig = new (interopsafe, nothrow) MetaSig(pCallSig,
                                                     cbCallSigSize,
                                                     pMD->GetModule(),
                                                     &tmpContext,
                                                     MetaSig::sigMember);

        if (pArgSig == NULL)
        {
            IfFailGo(E_OUTOFMEMORY);
        }

        m_numArgs = pArgSig->NumFixedArgs();

        if (pArgSig->HasThis())
        {
            m_numArgs++;
        }

        // <TODO>
        // What should we do in this case?
        // </TODO>
        /*
        if (argSig.IsVarArg())
            m_numArgs++;
        */
    }

    // allocation of pArgSig succeeded
    if (mdLocalSig != mdSignatureNil)
    {
        IfFailGo(pMD->GetModule()->GetMDImport()->GetSigFromToken(mdLocalSig, &cbLocalSigSize, &pLocalSig));
    }
    if (pLocalSig != NULL)
    {
        SigTypeContext tmpContext(pMD);
        pLocSig = new (interopsafe, nothrow) MetaSig(pLocalSig,
                                                     cbLocalSigSize,
                                                     pMD->GetModule(),
                                                     &tmpContext,
                                                     MetaSig::sigLocalVars);

        if (pLocSig == NULL)
        {
            IfFailGo(E_OUTOFMEMORY);
        }
    }

    // allocation of pLocalSig succeeded
    m_numTotalVars = m_numArgs + (pLocSig != NULL ? pLocSig->NumFixedArgs() : 0);

    if (m_numTotalVars > 0)
    {
        m_rgSize     = new (interopsafe, nothrow) SIZE_T[m_numTotalVars];
        m_rgElemType = new (interopsafe, nothrow) CorElementType[m_numTotalVars];

        if ((m_rgSize == NULL) || (m_rgElemType == NULL))
        {
            IfFailGo(E_OUTOFMEMORY);
        }
        else
        {
            // allocation of m_rgSize and m_rgElemType succeeded
            for (ULONG i = 0; i < m_numTotalVars; i++)
            {
                // Choose the correct signature to walk.
                MetaSig *pCur = NULL;
                if (i < m_numArgs)
                {
                    pCur = pArgSig;
                }
                else
                {
                    pCur = pLocSig;
                }

                // The "this" argument isn't stored in the signature, so we have to
                // check for it manually.
                if (i == 0 && pCur->HasThis())
                {
                    _ASSERTE(pCur == pArgSig);

                    m_rgElemType[i] = ELEMENT_TYPE_CLASS;
                    m_rgSize[i]     = sizeof(SIZE_T);
                }
                else
                {
                    m_rgElemType[i] = pCur->NextArg();

                    if (m_rgElemType[i] == ELEMENT_TYPE_VALUETYPE)
                    {
                        m_rgSize[i] = GetValueClassSize(pCur);
                    }
                    else
                    {
                        m_rgSize[i] = GetSetFrameHelper::GetSizeOfElement(m_rgElemType[i]);
                    }

                    LOG((LF_CORDB, LL_INFO10000, "GSFH::I: var 0x%x is of type %x, size:0x%x\n",
                         i, m_rgElemType[i], m_rgSize[i]));
                }
            }
        } // allocation of m_rgSize and m_rgElemType succeeded
    }   // if there are variables to take care of

ErrExit:
    // clean up
    if (pArgSig != NULL)
    {
        DeleteInteropSafe(pArgSig);
    }

    if (pLocSig != NULL)
    {
        DeleteInteropSafe(pLocSig);
    }

    if (FAILED(hr))
    {
        if (m_rgSize != NULL)
        {
            DeleteInteropSafe(m_rgSize);
        }

        if (m_rgElemType != NULL)
        {
            DeleteInteropSafe((int*)m_rgElemType);
        }
    }

    return hr;
} // GetSetFrameHelper::Init

//
// GetSetFrameHelper::~GetSetFrameHelper()
//
// This is the destructor.  It checks the two arrays we have allocated and frees the memory accordingly.
//
// parameters:   none
//
// return value: none
//
GetSetFrameHelper::~GetSetFrameHelper()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    if (m_rgSize)
    {
        DeleteInteropSafe(m_rgSize);
    }

    if (m_rgElemType)
    {
        DeleteInteropSafe((int*)m_rgElemType);
    }
}

//
// GetSetFrameHelper::GetSizeOfElement()
//
// Given a CorElementType, this function returns the size of this type.
// Note that this function doesn't handle ELEMENT_TYPE_VALUETYPE.  Use GetValueClassSize() instead.
//
// parameters:   cet - the CorElementType of the argument/local we are dealing with
//
// return value: the size of the argument/local
//
// static
SIZE_T GetSetFrameHelper::GetSizeOfElement(CorElementType cet)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
        PRECONDITION(cet != ELEMENT_TYPE_VALUETYPE);
    }
    CONTRACTL_END;

    if (!CorIsPrimitiveType(cet))
    {
        return sizeof(SIZE_T);
    }
    else
    {
        switch (cet)
        {
        case ELEMENT_TYPE_I8:
        case ELEMENT_TYPE_U8:
#if defined(HOST_64BIT)
        case ELEMENT_TYPE_I:
        case ELEMENT_TYPE_U:
#endif // HOST_64BIT
        case ELEMENT_TYPE_R8:
               return 8;

        case ELEMENT_TYPE_I4:
        case ELEMENT_TYPE_U4:
#if !defined(HOST_64BIT)
        case ELEMENT_TYPE_I:
        case ELEMENT_TYPE_U:
#endif // !HOST_64BIT
        case ELEMENT_TYPE_R4:
            return 4;

        case ELEMENT_TYPE_I2:
        case ELEMENT_TYPE_U2:
        case ELEMENT_TYPE_CHAR:
            return 2;

        case ELEMENT_TYPE_I1:
        case ELEMENT_TYPE_U1:
        case ELEMENT_TYPE_BOOLEAN:
            return 1;

        case ELEMENT_TYPE_VOID:
        case ELEMENT_TYPE_END:
            _ASSERTE(!"debugger.cpp - Check this code path\n");
            return 0;

        case ELEMENT_TYPE_STRING:
            return sizeof(SIZE_T);

        default:
            _ASSERTE(!"debugger.cpp - Check this code path\n");
            return sizeof(SIZE_T);
        }
    }
}

//
// GetSetFrameHelper::GetValueClassSize()
//
// Given a MetaSig pointer to the signature of a value type, this function returns its size.
//
// parameters:   pSig - MetaSig pointer to the signature of a value type
//
// return value: the size of this value type
//
SIZE_T GetSetFrameHelper::GetValueClassSize(MetaSig* pSig)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(CheckPointer(pSig));
    }
    CONTRACTL_END;

    // We need to determine the number of bytes for this value-type.
    SigPointer sp = pSig->GetArgProps();

    TypeHandle vcType = TypeHandle();
    {
        // Lookup operations run the class loader in non-load mode.
        ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();

        // This will return Null if type is not restored
        // @todo : is this what we want?
        SigTypeContext typeContext(m_pMD);
        vcType = sp.GetTypeHandleThrowing(m_pMD->GetModule(),
                                          &typeContext,
                                          // == FailIfNotLoaded
                                          ClassLoader::DontLoadTypes);
    }
    // We need to know the size of the class in bytes. This means:
    // - we need a specific instantiation (since that affects size)
    // - but we don't care if it's shared (since it will be the same size either way)
    _ASSERTE(!vcType.IsNull() && vcType.IsValueType());

    return (vcType.GetMethodTable()->GetNumInstanceFieldBytes());
}

//
// GetSetFrameHelper::GetValueClassSizeOfVar()
//
// This method retrieves the size of the variable saved in the array m_rgSize.  Also, it returns true
// if the variable is a value type.
//
// parameters:   varNum  - the variable number (arguments come before locals)
//               varType - the type of variable home
//               pSize   - [out] the size
//
// return value: whether this variable is a value type
//
bool GetSetFrameHelper::GetValueClassSizeOfVar(int varNum, ICorDebugInfo::VarLocType varType, SIZE_T* pSize)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
        PRECONDITION(varType != ICorDebugInfo::VLT_FIXED_VA);
        PRECONDITION(pSize != NULL);
    }
    CONTRACTL_END;

    // preliminary checking
    if (varNum < 0)
    {
        // Make sure this is one of the secret parameters (e.g. VASigCookie, generics context, etc.).
        _ASSERTE(varNum > (int)ICorDebugInfo::MAX_ILNUM);

        *pSize = sizeof(LPVOID);
        return false;
    }

    // This check is only safe after we make sure that varNum is not negative.
    if ((UINT)varNum >= m_numTotalVars)
    {
        _ASSERTE(!"invalid variable index encountered during setip");
        *pSize = 0;
        return false;
    }

    CorElementType cet = m_rgElemType[varNum];
    *pSize = m_rgSize[varNum];

    if ((cet != ELEMENT_TYPE_VALUETYPE) ||
        (varType == ICorDebugInfo::VLT_REG) ||
        (varType == ICorDebugInfo::VLT_REG_REG) ||
        (varType == ICorDebugInfo::VLT_REG_STK) ||
        (varType == ICorDebugInfo::VLT_STK_REG))
    {
        return false;
    }
    else
    {
        return true;
    }
}

int GetSetFrameHelper::ShiftIndexForHiddens(int varNum)
{
    LIMITED_METHOD_CONTRACT;

    //
    // Need to shift them up so are appropriate index for rgVal arrays
    //
    return varNum - ICorDebugInfo::UNKNOWN_ILNUM;
}

// Helper method pair to grab all, then set all, variables at a given
// point in a routine.
// NOTE: GetVariablesFromOffset and SetVariablesAtOffset are
// very similar - modifying one will probably need to be reflected in the other...
// rgVal1 and rgVal2 are preallocated by callers with estimated size.
// We pass in the size of the allocation in rRgValeSize. The safe index will be rgVal1[0..uRgValSize - 1]
//
HRESULT Debugger::GetVariablesFromOffset(MethodDesc  *pMD,
                                         UINT varNativeInfoCount,
                                         ICorDebugInfo::NativeVarInfo *varNativeInfo,
                                         SIZE_T offsetFrom,
                                         CONTEXT *pCtx,
                                         SIZE_T  *rgVal1,
                                         SIZE_T  *rgVal2,
                                         UINT    uRgValSize, // number of elements of the preallocated rgVal1 and rgVal2
                                         BYTE ***rgpVCs)
{
    // @todo - convert this to throwing w/ holders. It will be cleaner.
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(CheckPointer(rgpVCs));
        PRECONDITION(CheckPointer(pCtx));
        PRECONDITION(varNativeInfoCount == 0 || CheckPointer(varNativeInfo));
        PRECONDITION(varNativeInfoCount == 0 || CheckPointer(rgVal1));
        PRECONDITION(varNativeInfoCount == 0 || CheckPointer(rgVal2));
        // This may or may not be called on the helper thread.
    }
    CONTRACTL_END;

    *rgpVCs = NULL;
    // if there are no locals, well, we are done!

    if (varNativeInfoCount == 0)
    {
        return S_OK;
    }

    memset( rgVal1, 0, sizeof(SIZE_T)*uRgValSize);
    memset( rgVal2, 0, sizeof(SIZE_T)*uRgValSize);

    LOG((LF_CORDB|LF_ENC, LL_INFO10000, "D::GVFO: %s::%s, infoCount:0x%x, from: %p\n",
         pMD->m_pszDebugClassName,
         pMD->m_pszDebugMethodName,
         varNativeInfoCount,
         offsetFrom));

    GetSetFrameHelper frameHelper;
    HRESULT hr = frameHelper.Init(pMD);
    if (FAILED(hr))
    {
        return hr;
    }
    // preallocate enough to hold all possible valueclass args & locals
    // sure this is more than we need, but not a big deal and better
    // than having to crawl through the frameHelper and count
    ULONG cValueClasses = 0;
    BYTE **rgpValueClasses = new (interopsafe, nothrow)  BYTE *[varNativeInfoCount];
    if (rgpValueClasses == NULL)
    {
        return E_OUTOFMEMORY;
    }
    memset(rgpValueClasses, 0, sizeof(BYTE *)*varNativeInfoCount);

    hr = S_OK;

    LOG((LF_CORDB|LF_ENC, LL_INFO10000, "D::GVFO rgVal1 %p, rgVal2 %p\n",
         rgVal1,
         rgVal2));

    // Now go through the full array and save off each arg and local
    for (UINT i = 0; i< varNativeInfoCount;i++)
    {
        // Ignore variables not live at offsetFrom
        //
        // #VarLife
        //
        // The condition below is a little strange. If a var is alive when this is true:
        //
        // startOffset <= offsetFrom < endOffset
        //
        // Then you'd expect the negated expression below to be:
        //
        // startOffset > offsetFrom || endOffset <= offsetFrom
        //
        // instead of what we're doing ("<" instead of "<="):
        //
        // startOffset > offsetFrom || endOffset < offsetFrom
        //
        // I'm not sure if the condition below is a mistake, or if it's intentionally
        // mirroring a workaround from FindNativeInfoInILVariableArray() (Debug\DI\module.cpp)
        // to deal with optimized code. So I'm leaving it alone for now. See
        // code:FindNativeInfoInILVariableArray for more info on this workaround.
        if ((varNativeInfo[i].startOffset > offsetFrom) ||
            (varNativeInfo[i].endOffset < offsetFrom) ||
            (varNativeInfo[i].loc.vlType == ICorDebugInfo::VLT_INVALID))
        {
            LOG((LF_CORDB|LF_ENC,LL_INFO10000, "D::GVFO [%2u] invalid\n", i));
            continue;
        }

        SIZE_T cbClass;
        bool isVC = frameHelper.GetValueClassSizeOfVar(varNativeInfo[i].varNumber,
                                                       varNativeInfo[i].loc.vlType,
                                                       &cbClass);

        if (!isVC)
        {
            int rgValIndex = frameHelper.ShiftIndexForHiddens(varNativeInfo[i].varNumber);

            _ASSERTE(rgValIndex >= 0 && rgValIndex < (int)uRgValSize);

            BOOL res = GetNativeVarVal(varNativeInfo[i].loc,
                                       pCtx,
                                       rgVal1 + rgValIndex,
                                       rgVal2 + rgValIndex
                                       BIT64_ARG(cbClass));

            LOG((LF_CORDB|LF_ENC,LL_INFO10000,
                 "D::GVFO [%2u] varnum %u, nonVC type %x, addr %p: %zx;%zx\n",
                 i,
                 varNativeInfo[i].varNumber,
                 varNativeInfo[i].loc.vlType,
                 NativeVarStackAddr(varNativeInfo[i].loc, pCtx),
                 rgVal1[rgValIndex],
                 rgVal2[rgValIndex]));

            if (res == TRUE)
            {
                continue;
            }

            _ASSERTE(res == TRUE);
            hr = E_FAIL;
            break;
        }

        // it's definately a value class
        // Make space for it - note that it uses the VC index, NOT the variable index
        _ASSERTE(cbClass != 0);
        rgpValueClasses[cValueClasses] = new (interopsafe, nothrow) BYTE[cbClass];
        if (rgpValueClasses[cValueClasses] == NULL)
        {
            hr = E_OUTOFMEMORY;
            break;
        }
        memcpy(rgpValueClasses[cValueClasses],
               NativeVarStackAddr(varNativeInfo[i].loc, pCtx),
               cbClass);

        // Move index up.
        cValueClasses++;
#ifdef _DEBUG
        LOG((LF_CORDB|LF_ENC,LL_INFO10000,
             "D::GVFO [%2u] varnum %d, VC len %d, addr %p, sample: %8.8x%8.8x\n",
             i,
             varNativeInfo[i].varNumber,
             cbClass,
             NativeVarStackAddr(varNativeInfo[i].loc, pCtx),
             (rgpValueClasses[cValueClasses-1])[0], (rgpValueClasses[cValueClasses-1])[1]));
#endif
    }

    LOG((LF_CORDB|LF_ENC, LL_INFO10000, "D::GVFO: returning 0x%x\n", hr));
    if (SUCCEEDED(hr))
    {
        (*rgpVCs) = rgpValueClasses;
        return hr;
    }

    // We failed for some reason
    if (rgpValueClasses != NULL)
    {   // free any memory we allocated for VCs here
        while(cValueClasses > 0)
        {
            --cValueClasses;
            DeleteInteropSafe(rgpValueClasses[cValueClasses]);  // OK to delete NULL
        }
        DeleteInteropSafe(rgpValueClasses);
        rgpValueClasses = NULL;
    }
    return hr;
}

// NOTE: GetVariablesFromOffset and SetVariablesAtOffset are
// very similar - modifying one will probably need to be reflected in the other...
HRESULT Debugger::SetVariablesAtOffset(MethodDesc  *pMD,
                                       UINT varNativeInfoCount,
                                       ICorDebugInfo::NativeVarInfo *varNativeInfo,
                                       SIZE_T offsetTo,
                                       CONTEXT *pCtx,
                                       SIZE_T  *rgVal1,
                                       SIZE_T  *rgVal2,
                                       BYTE **rgpVCs)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(CheckPointer(pCtx));
        PRECONDITION(varNativeInfoCount == 0 || CheckPointer(rgpVCs));
        PRECONDITION(varNativeInfoCount == 0 || CheckPointer(varNativeInfo));
        PRECONDITION(varNativeInfoCount == 0 || CheckPointer(rgVal1));
        PRECONDITION(varNativeInfoCount == 0 || CheckPointer(rgVal2));
        // This may or may not be called on the helper thread.
    }
    CONTRACTL_END;

    LOG((LF_CORDB|LF_ENC, LL_INFO10000, "D::SVAO: %s::%s, infoCount:0x%x, to:0x%p\n",
         pMD->m_pszDebugClassName,
         pMD->m_pszDebugMethodName,
         varNativeInfoCount,
         offsetTo));

    if (varNativeInfoCount == 0)
    {
        return S_OK;
    }

    GetSetFrameHelper frameHelper;
    HRESULT hr = frameHelper.Init(pMD);
    if (FAILED(hr))
    {
        return hr;
    }

    ULONG iVC = 0;
    hr = S_OK;

    // Note that since we obtain all the variables in the first loop, we
    // can now splatter those variables into their new locations
    // willy-nilly, without the fear that variable locations that have
    // been swapped might accidentally overwrite a variable value.
    for (UINT i = 0;i< varNativeInfoCount;i++)
    {
        // Ignore variables not live at offsetTo
        //
        // If this IF condition looks wrong to you, see
        // code:Debugger::GetVariablesFromOffset#VarLife for more info
        if ((varNativeInfo[i].startOffset > offsetTo) ||
            (varNativeInfo[i].endOffset < offsetTo) ||
            (varNativeInfo[i].loc.vlType == ICorDebugInfo::VLT_INVALID))
        {
            LOG((LF_CORDB|LF_ENC,LL_INFO10000, "D::SVAO [%2d] invalid\n", i));
            continue;
        }

        SIZE_T cbClass;
        bool isVC = frameHelper.GetValueClassSizeOfVar(varNativeInfo[i].varNumber,
                                                       varNativeInfo[i].loc.vlType,
                                                       &cbClass);

        if (!isVC)
        {
            int rgValIndex = frameHelper.ShiftIndexForHiddens(varNativeInfo[i].varNumber);

            _ASSERTE(rgValIndex >= 0);

            BOOL res = SetNativeVarVal(varNativeInfo[i].loc,
                                       pCtx,
                                       rgVal1[rgValIndex],
                                       rgVal2[rgValIndex]
                                       BIT64_ARG(cbClass));

            LOG((LF_CORDB|LF_ENC,LL_INFO10000,
                 "D::SVAO [%2d] varnum %d, nonVC type %x, addr %8.8x: %8.8x;%8.8x\n",
                 i,
                 varNativeInfo[i].varNumber,
                 varNativeInfo[i].loc.vlType,
                 NativeVarStackAddr(varNativeInfo[i].loc, pCtx),
                 rgVal1[rgValIndex],
                 rgVal2[rgValIndex]));

            if (res == TRUE)
            {
                continue;
            }
            _ASSERTE(res == TRUE);
            hr = E_FAIL;
            break;
        }

        // It's definately a value class.
        _ASSERTE(cbClass != 0);
        if (rgpVCs[iVC] == NULL)
        {
            // it's new in scope, so just clear it
            memset(NativeVarStackAddr(varNativeInfo[i].loc, pCtx), 0, cbClass);
            LOG((LF_CORDB|LF_ENC,LL_INFO10000, "D::SVAO [%2d] varnum %d, new VC len %d, addr %8.8x\n",
                 i,
                 varNativeInfo[i].varNumber,
                 cbClass,
                 NativeVarStackAddr(varNativeInfo[i].loc, pCtx)));
            continue;
        }
        // it's a pre-existing VC, so copy it
        memmove(NativeVarStackAddr(varNativeInfo[i].loc, pCtx), rgpVCs[iVC], cbClass);
#ifdef _DEBUG
        LOG((LF_CORDB|LF_ENC,LL_INFO10000,
             "D::SVAO [%2d] varnum %d, VC len %d, addr: %8.8x sample: %8.8x%8.8x\n",
             i,
             varNativeInfo[i].varNumber,
             cbClass,
             NativeVarStackAddr(varNativeInfo[i].loc, pCtx),
             rgpVCs[iVC][0],
             rgpVCs[iVC][1]));
#endif
        // Now get rid of the memory
        DeleteInteropSafe(rgpVCs[iVC]);
        rgpVCs[iVC] = NULL;
        iVC++;
    }

    LOG((LF_CORDB|LF_ENC, LL_INFO10000, "D::SVAO: returning %8.8x\n", hr));

    if (rgpVCs != NULL)
    {
        DeleteInteropSafe(rgpVCs);
    }

    return hr;
}

static BOOL IsDuplicatePatch(SIZE_T_UNORDERED_ARRAY* unorderedArray, SIZE_T Entry)
{
    LIMITED_METHOD_CONTRACT;

    SIZE_T* entries = unorderedArray->Table();
    ULONG count = unorderedArray->Count();
    for (ULONG i = 0; i < count; ++i)
    {
        if (entries[i] == Entry)
            return TRUE;
    }
    return FALSE;
}


/******************************************************************************
// HRESULT Debugger::MapAndBindFunctionBreakpoints():  For each breakpoint
//      that we've set in any version of the existing function,
//      set a corresponding breakpoint in the new function if we haven't moved
//      the patch to the new version already.
//
//      This must be done _AFTER_ the MethodDesc has been updated
//      with the new address (ie, when GetFunctionAddress pFD returns
//      the address of the new EnC code)
//
// Parameters:
// djiNew - this is the DJI created in D::JitComplete.
// fd - the method desc that we're binding too.
// addrOfCode - address of the native blob of code we just jitted
//
//  <TODO>@todo Replace array with hashtable for improved efficiency</TODO>
//  <TODO>@todo Need to factor code,so that we can selectively map forward DFK(ilOFfset) BPs</TODO>
 ******************************************************************************/
HRESULT Debugger::MapAndBindFunctionPatches(DebuggerJitInfo *djiNew,
                                            MethodDesc * fd,
                                            CORDB_ADDRESS_TYPE *addrOfCode)
{
    // @@@
    // Internal helper API. Can be called from Debugger or Controller.
    //

    CONTRACTL
    {
        THROWS;
        CALLED_IN_DEBUGGERDATALOCK_HOLDER_SCOPE_MAY_GC_TRIGGERS_CONTRACT;
        PRECONDITION(djiNew != NULL);
        PRECONDITION(djiNew->m_nativeCodeVersion.GetMethodDesc() == fd);
    }
    CONTRACTL_END;

    HRESULT     hr =                S_OK;
    HASHFIND    hf;
    SIZE_T      *pidTableEntry =    NULL;
    SIZE_T      pidInCaseTableMoves;
    Module      *pModule =          g_pEEInterface->MethodDescGetModule(fd);
    mdMethodDef md =                fd->GetMemberDef();

    LOG((LF_CORDB,LL_INFO10000,"D::MABFP: All BPs will be mapped to encVersion: %zx (DJI:%p)\n",
        djiNew->m_methodInfo->GetCurrentEnCVersion(), djiNew));

    // We need to traverse the patch list while under the controller lock (small lock).
    // But we can only send BreakpointSetErrors while under the debugger lock (big lock).
    // So to avoid a lock violation, we queue any errors we find under the small lock,
    // and then send the whole list when under the big lock.
    PATCH_UNORDERED_ARRAY listUnbindablePatches;

    // First lock the patch table so it doesn't move while we're
    //  examining it.
    LOG((LF_CORDB,LL_INFO10000, "D::MABFP: About to lock patch table\n"));
    {
        DebuggerController::ControllerLockHolder ch;

        // Manipulate tables AFTER lock's been acquired.
        DebuggerPatchTable *pPatchTable = DebuggerController::GetPatchTable();
        GetBPMappingDuplicates()->Clear(); //dups are tracked per-version

        for (DebuggerControllerPatch *dcp = pPatchTable->GetFirstPatch(&hf);
             dcp != NULL;
             dcp = pPatchTable->GetNextPatch( &hf ))
        {
            LOG((LF_CORDB, LL_INFO10000, "D::MABFP: Got patch %p, patchId:0x%zx\n", dcp, dcp->patchId));

            // Only copy over breakpoints that are in this method
            // Ideally we'd have a per-method index since there can be a lot of patches
            // when the EnCBreakpoint patches are included.
            if (dcp->key.module != pModule || dcp->key.md != md)
            {
                LOG((LF_CORDB, LL_INFO10000, "D::MABFP: Patch not in this method\n"));
                continue;
            }

            // If the patch only applies in certain generic instances, don't bind it
            // elsewhere.
            if(dcp->pMethodDescFilter != NULL && dcp->pMethodDescFilter != djiNew->m_nativeCodeVersion.GetMethodDesc())
            {
                LOG((LF_CORDB, LL_INFO10000, "D::MABFP: Patch not in this generic instance, filter %p\n", dcp->pMethodDescFilter));
                continue;
            }

            // Do not copy over replica breakpoint patches.  Instead place a new replica
            // based off the primary.
            if (dcp->IsILReplicaPatch())
            {
                LOG((LF_CORDB, LL_INFO10000, "D::MABFP: Not copying over replica breakpoint patch\n"));
                continue;
            }

            // If the patch is already bound, then we don't want to try to rebind it.
            // Eg. It may be bound to a different generic method instantiation.
            if (dcp->IsBound())
            {
                LOG((LF_CORDB, LL_INFO10000, "D::MABFP: Skipping already bound patch\n"));
                continue;
            }

            // Only apply breakpoint and EnC remap patches that are for this version.
            // If the patches doesn't have a particular EnCVersion available from its data then
            // we're (probably) not tracking JIT info.
            if ((dcp->IsBreakpointPatch() || dcp->IsEnCRemapPatch())
                && dcp->HasEnCVersion() && dcp->GetEnCVersion() != djiNew->m_encVersion)
            {
                LOG((LF_CORDB, LL_INFO10000, "Not applying breakpoint or EnC remap patch to new version\n"));
                continue;
            }

            // Only apply breakpoint and stepper patches
            //
            // The DJI gets deleted as part of the Unbind/Rebind process in MovedCode.
            // This is to signal that we should not skip here.
            // <NICE> under exactly what scenarios (EnC, code pitching etc.) will this apply?... </NICE>
            // <NICE> can't we be a little clearer about why we don't want to bind the patch in this arcane situation?</NICE>
            if (dcp->HasDJI() && !dcp->IsBreakpointPatch() && !dcp->IsStepperPatch())
            {
                LOG((LF_CORDB, LL_INFO10000, "D::MABFP: Neither stepper nor BP but we have valid a DJI (i.e. the DJI hasn't been deleted as part of the Unbind/MovedCode/Rebind mess)! - getting next patch!\n"));
                continue;
            }

            pidInCaseTableMoves = dcp->patchId;

            // If we've already mapped this one to the current version,
            //  don't map it again.
            LOG((LF_CORDB,LL_INFO10000,"D::MABFP: Checking if 0x%zx is a dup... ",
                pidInCaseTableMoves));

            if ( IsDuplicatePatch(GetBPMappingDuplicates(), pidInCaseTableMoves) )
            {
                LOG((LF_CORDB,LL_INFO10000,"it is!\n"));
                continue;
            }
            LOG((LF_CORDB,LL_INFO10000,"nope, applying.\n"));
            dcp->LogInstance();

            // Attempt mapping from patch to new version of code, and
            // we don't care if it turns out that there isn't a mapping.
            // <TODO>@todo-postponed: EnC: Make sure that this doesn't cause
            // the patch-table to shift.</TODO>
            hr = MapPatchToDJI( dcp, djiNew );
            if (CORDBG_E_CODE_NOT_AVAILABLE == hr )
            {
                *(listUnbindablePatches.AppendThrowing()) = dcp;
                hr = S_OK;
            }

            if (FAILED(hr))
                break;

            //Remember the patch id to prevent duplication later
            pidTableEntry = GetBPMappingDuplicates()->Append();
            if (NULL == pidTableEntry)
            {
                hr = E_OUTOFMEMORY;
                break;
            }

            *pidTableEntry = pidInCaseTableMoves;
            LOG((LF_CORDB,LL_INFO10000,"D::MABFP Adding 0x%zx to list of already mapped patches\n",
                pidInCaseTableMoves));
        }

        // unlock controller lock before sending events.
    }
    LOG((LF_CORDB,LL_INFO10000, "D::MABFP: Unlocked patch table\n"));

    // Now send any Breakpoint bind error events.
    if (listUnbindablePatches.Count() > 0)
        LockAndSendBreakpointSetError(&listUnbindablePatches);

    return hr;
}

/******************************************************************************
// HRESULT Debugger::MapPatchToDJI():  Maps the given
//  patch to the corresponding location at the new address.
//  We assume that the new code has been JITTed.
// Returns:  CORDBG_E_CODE_NOT_AVAILABLE - Indicates that a mapping wasn't
//  available, and thus no patch was placed.  The caller may or may
//  not care.
 ******************************************************************************/
HRESULT Debugger::MapPatchToDJI(DebuggerControllerPatch *dcp, DebuggerJitInfo *djiTo)
{
    CONTRACTL
    {
        THROWS;
        CALLED_IN_DEBUGGERDATALOCK_HOLDER_SCOPE_MAY_GC_TRIGGERS_CONTRACT;
        PRECONDITION(djiTo != NULL);
        PRECONDITION(djiTo->m_jitComplete == true);
    }
    CONTRACTL_END;

    _ASSERTE(DebuggerController::HasLock());
#ifdef _DEBUG
    static BOOL shouldBreak = -1;
    if (shouldBreak == -1)
        shouldBreak = UnsafeGetConfigDWORD(CLRConfig::INTERNAL_DbgBreakOnMapPatchToDJI);

    if (shouldBreak > 0) {
        _ASSERTE(!"DbgBreakOnMatchPatchToDJI");
    }
#endif

    LOG((LF_CORDB, LL_EVERYTHING, "D::MPTDJI Begin %p, patchId:0x%zx\n", dcp, dcp->patchId));

    // We shouldn't have been asked to map an already bound patch
    _ASSERTE( !dcp->IsBound() );

    // If the patch has no DJI then we're doing a UnbindFunctionPatches/RebindFunctionPatches.  Either
    // way, we simply want the most recent version.  In the absence of EnC we should have djiCur == djiTo.
    DebuggerJitInfo *djiCur = dcp->HasDJI() ? dcp->GetDJI() : djiTo;
    PREFIX_ASSUME(djiCur != NULL);

    // If the source and destination are the same version, then this method
    // decays into BindFunctionPatch's BindPatch function
    if (djiCur->m_encVersion == djiTo->m_encVersion)
    {
        // If the patch is a "primary" then make a new "replica" patch instead of
        // binding the old one.  This is to stop us mucking with the primary breakpoint patch
        // which we may need to bind several times for generic code.
        if (dcp->IsILPrimaryPatch())
        {
            LOG((LF_CORDB, LL_EVERYTHING, "D::MPTDJI Add, Bind, Activate new patch from primary patch\n"));
            if (dcp->controller->AddBindAndActivateILReplicaPatch(dcp, djiTo))
            {
                LOG((LF_CORDB, LL_INFO1000, "D::MPTDJI Applied from primary patch!\n" ));
                return S_OK;
            }
            else
            {
                LOG((LF_CORDB, LL_INFO1000, "D::MPTDJI Didn't work for some reason!\n"));

                // Caller can track this HR and send error.
                return CORDBG_E_CODE_NOT_AVAILABLE;
            }
        }
        else
        {
            // <TODO>
            // We could actually have a native managed patch here.  This patch is probably added
            // as a result of tracing a patch.  See if we can eliminate the need for this code path
            // </TODO>
            _ASSERTE( dcp->GetKind() == PATCH_KIND_NATIVE_MANAGED );

            // We have an unbound native patch (eg. for PatchTrace), lets try to bind and activate it
            dcp->SetDJI(djiTo);
            LOG((LF_CORDB, LL_EVERYTHING, "D::MPTDJI trying to bind patch... could be problem\n"));
            if (DebuggerController::BindPatch(dcp, djiTo->m_nativeCodeVersion.GetMethodDesc(), NULL))
            {
                DebuggerController::ActivatePatch(dcp);
                LOG((LF_CORDB, LL_INFO1000, "D::MPTDJI Binding went fine!\n" ));
                return S_OK;
            }
            else
            {
                LOG((LF_CORDB, LL_INFO1000, "D::MPTDJI Binding failed for some reason!\n"));

                // Caller can track this HR and send error.
                return CORDBG_E_CODE_NOT_AVAILABLE;
            }
        }
    }

    // Breakpoint patches never get mapped over
    _ASSERTE(!dcp->IsBreakpointPatch());

    return S_OK;
}


/* ------------------------------------------------------------------------ *
 * EE Interface routines
 * ------------------------------------------------------------------------ */

//
// SendSyncCompleteIPCEvent sends a Sync Complete event to the Right Side.
//
void Debugger::SendSyncCompleteIPCEvent(bool isEESuspendedForGC)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(ThreadHoldsLock());

        // Anyone sending the synccomplete must hold the TSL.
        PRECONDITION(ThreadStore::HoldingThreadStore() || IsAtProcessExit());

        // The sync complete is now only sent on a helper thread.
        if (!isEESuspendedForGC)
        {
            PRECONDITION(ThisIsHelperThreadWorker());
        }
        MODE_COOPERATIVE;

        // We had better be trapping Runtime threads and not stopped yet.
        if (isEESuspendedForGC)
        {
            PRECONDITION(m_stopped);
        }
        else
        {
            PRECONDITION(m_stopped && m_trappingRuntimeThreads);
        }
    }
    CONTRACTL_END;

    // @@@
    // Internal helper API.
    // This is to send Sync Complete event to RightSide.
    // We should have hold the debugger lock
    //

    STRESS_LOG0(LF_CORDB, LL_INFO10000, "D::SSCIPCE: sync complete.\n");

    // Synchronizing during the rude shutdown should be extremely rare b/c we don't
    // TART in rude shutdown. Shutdown must have started after we started to sync.
    // We know we're not on the shutdown thread here.
    // And we also know we can't block the shutdown thread (b/c it has the TSL and will
    // get a free pass through the GC toggles that normally block threads for debugging).
    if (IsAtProcessExit())
    {
        STRESS_LOG0(LF_CORDB, LL_INFO10000, "D::SSCIPCE: Skipping for shutdown.\n");
        return;
    }

    // If we're not marked as attached yet, then do that now.
    // This can be safely called multiple times.
    // This can happen in the normal attach case. The Right-side sends an async-break,
    // but we don't want to be considered attach until we've actually gotten our first synchronization.
    // Else threads may slip forward during attach and send debug events while we're tyring to attach.
    MarkDebuggerAttachedInternal();

    DebuggerIPCControlBlock * pDCB;
    pDCB = m_pRCThread->GetDCB();
    (void)pDCB; //prevent "unused variable" error from GCC

    PREFIX_ASSUME(pDCB != NULL); // must have DCB by the time we're sending IPC events.
#ifdef FEATURE_INTEROP_DEBUGGING
    // The synccomplete can't be the first IPC event over. That's b/c the LS needs to know
    // if we're interop-debugging and the RS needs to know special addresses for interop-debugging
    // (like flares). All of this info is in the DCB.
    if (pDCB->m_rightSideIsWin32Debugger)
    {

        // If the Right Side is the win32 debugger of this process, then we need to throw a special breakpoint exception
        // here instead of sending the sync complete event. The Right Side treats this the same as a sync complete
        // event, but its also able to suspend unmanaged threads quickly.
        // This also prevents races between sending the sync-complete and getting a native debug event
        // (since the sync-complete becomes a native debug event, and all native debug events are serialized).
        //
        // Note: we reset the syncThreadIsLockFree event before sending the sync complete flare. This thread will set
        // this event once its released the debugger lock. This will prevent the Right Side from suspending this thread
        // until it has released the debugger lock.
        Debugger::NotifyRightSideOfSyncComplete();
    }
    else
#endif // FEATURE_INTEROP_DEBUGGING
    {
        STRESS_LOG0(LF_CORDB, LL_EVERYTHING, "GetIPCEventSendBuffer called in SendSyncCompleteIPCEvent\n");
        // Send the Sync Complete event to the Right Side
        DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
        InitIPCEvent(ipce, DB_IPCE_SYNC_COMPLETE);

        m_pRCThread->SendIPCEvent();
    }
}

//
// Lookup or create a DebuggerModule for the given pDomainAssembly.
//
// Arguments:
//    pDomainAssembly - non-null domain file.
//
// Returns:
//   DebuggerModule instance for the given domain file. May be lazily created.
//
// Notes:
//  @dbgtodo JMC - this should go away when we get rid of DebuggerModule.
//

DebuggerModule * Debugger::LookupOrCreateModule(DomainAssembly * pDomainAssembly)
{
    _ASSERTE(pDomainAssembly != NULL);
    LOG((LF_CORDB, LL_INFO1000, "D::LOCM df=%p\n", pDomainAssembly));
    DebuggerModule * pDModule = LookupOrCreateModule(pDomainAssembly->GetModule(), AppDomain::GetCurrentDomain());
    LOG((LF_CORDB, LL_INFO1000, "D::LOCM m=%p ad=%p -> dm=%p\n", pDomainAssembly->GetModule(), AppDomain::GetCurrentDomain(), pDModule));
    _ASSERTE(pDModule != NULL);
    _ASSERTE(pDModule->GetDomainAssembly() == pDomainAssembly);

    return pDModule;
}

// Overloaded Wrapper around for VMPTR_DomainAssembly-->DomainAssembly*
//
// Arguments:
//    vmDomainAssembly - VMPTR cookie for a domain file. This can be NullPtr().
//
// Returns:
//    Debugger Module instance for the given domain file. May be lazily created.
//
// Notes:
//    VMPTR comes from IPC events
DebuggerModule * Debugger::LookupOrCreateModule(VMPTR_DomainAssembly vmDomainAssembly)
{
    DomainAssembly * pDomainAssembly = vmDomainAssembly.GetRawPtr();
    if (pDomainAssembly == NULL)
    {
        return NULL;
    }
    return LookupOrCreateModule(pDomainAssembly);
}

// Lookup or create a DebuggerModule for the given (Module, AppDomain) pair.
//
// Arguments:
//    pModule - required runtime module. May be domain netural.
//    pAppDomain - required appdomain that the module is in.
//
// Returns:
//    Debugger Module isntance for the given domain file. May be lazily created.
//
DebuggerModule* Debugger::LookupOrCreateModule(Module* pModule, AppDomain *pAppDomain)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO1000, "D::LOCM m=%p ad=%p\n", pModule, pAppDomain));

    // DebuggerModules are relative to a specific AppDomain so we should always be looking up a module /
    // AppDomain pair.
    _ASSERTE( pModule != NULL );
    _ASSERTE( pAppDomain != NULL );

    // This is called from all over. We just need to lock in order to lookup. We don't need
    // the lock when actually using the DebuggerModule (since it won't be unloaded as long as there is a thread
    // in that appdomain). Many of our callers already have this lock, many don't.
    // We can take the lock anyways because it's reentrant.
    DebuggerDataLockHolder ch(g_pDebugger); // need to traverse module list

    // if this is a module belonging to the system assembly, then scan
    // the complete list of DebuggerModules looking for the one
    // with a matching appdomain id
    // it.

    DebuggerModule* dmod = NULL;

    if (m_pModules != NULL)
    {
        dmod = m_pModules->GetModule(pModule);
    }

    // If it doesn't exist, create it.
    if (dmod == NULL)
    {
        LOG((LF_CORDB, LL_INFO1000, "D::LOCM dmod for m=%p ad=%p not found, creating.\n", pModule, pAppDomain));
        HRESULT hr = S_OK;
        EX_TRY
        {
            DomainAssembly * pDomainAssembly = pModule->GetDomainAssembly();
            SIMPLIFYING_ASSUMPTION(pDomainAssembly != NULL);
            dmod = AddDebuggerModule(pDomainAssembly); // throws
        }
        EX_CATCH_HRESULT(hr);
        SIMPLIFYING_ASSUMPTION(dmod != NULL); // may not be true in OOM cases; but LS doesn't handle OOM.
    }

    // The module must be in the AppDomain that was requested
    _ASSERTE( (dmod == NULL) || (dmod->GetAppDomain() == pAppDomain) );

    LOG((LF_CORDB, LL_INFO1000, "D::LOCM m=%p ad=%p -> dm=%p(Mod=%p, DomFile=%p, AD=%p)\n",
        pModule, pAppDomain, dmod, dmod->GetRuntimeModule(), dmod->GetDomainAssembly(), dmod->GetAppDomain()));
    return dmod;
}

// Create a new DebuggerModule object
//
// Arguments:
//    pDomainAssembly-  runtime domain file to create debugger module object around
//
// Returns:
//    New instnace of a DebuggerModule. Throws on failure.
//
DebuggerModule* Debugger::AddDebuggerModule(DomainAssembly * pDomainAssembly)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO1000, "D::ADM df=0x%x\n", pDomainAssembly));
    DebuggerDataLockHolder chInfo(this);

    Module *     pRuntimeModule = pDomainAssembly->GetModule();
    AppDomain *  pAppDomain     = AppDomain::GetCurrentDomain();

    HRESULT hr = CheckInitModuleTable();
    IfFailThrow(hr);

    DebuggerModule* pModule = new (interopsafe) DebuggerModule(pRuntimeModule, pDomainAssembly, pAppDomain);
    _ASSERTE(pModule != NULL); // throws on oom

    TRACE_ALLOC(pModule);

    m_pModules->AddModule(pModule); // throws
    // @dbgtodo  inspection/exceptions - this may leak module in OOM case. LS is not OOM resilient; and we
    // expect to get rid of DebuggerModule anyways.

    LOG((LF_CORDB, LL_INFO1000, "D::ADM df=0x%x -> dm=0x%x\n", pDomainAssembly, pModule));
    return pModule;
}

//
// TrapAllRuntimeThreads causes every Runtime thread that is executing
// in the EE to trap and send the at safe point event to the RC thread as
// soon as possible. It also sets the EE up so that Runtime threads that
// are outside of the EE will trap when they try to re-enter.
//
// @TODO::
// Neither pDbgLockHolder nor pAppDomain are used.
void Debugger::TrapAllRuntimeThreads()
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;

        // We acquired the lock b/c we're in a scope between LFES & UFES.
        PRECONDITION(ThreadHoldsLock());

        // This should never be called on a Temporary Helper thread.
        PRECONDITION(IsDbgHelperSpecialThread() ||
                     (g_pEEInterface->GetThread() == NULL) ||
                     !g_pEEInterface->IsPreemptiveGCDisabled());
    }
    CONTRACTL_END;

#if !defined(FEATURE_DBGIPC_TRANSPORT_VM)
    // Only sync if RS requested it.
    if (!m_RSRequestedSync)
    {
        return;
    }
    m_RSRequestedSync = FALSE;
#endif

    // If we're doing shutdown, then don't bother trying to communicate w/ the RS.
    // If we're not the thread doing shutdown, then we may be asynchronously killed by the OS.
    // If we are the thread in shutdown, don't TART b/c that may block and do complicated stuff.
    if (IsAtProcessExit())
    {
        STRESS_LOG0(LF_CORDB, LL_INFO10000, "D::TART: Skipping for shutdown.\n");
        return;
    }


    // Only try to start trapping if we're not already trapping.
    if (m_trappingRuntimeThreads == FALSE)
    {
        bool fSuspended;

        STRESS_LOG0(LF_CORDB, LL_INFO10000, "D::TART: Trapping all Runtime threads.\n");

        // There's no way that we should be stopped and still trying to call this function.
        _ASSERTE(!m_stopped);

        // Mark that we're trapping now.
        m_trappingRuntimeThreads = TRUE;

        // Take the thread store lock.
        assert(ThreadStore::HoldingThreadStore());

        // We start the suspension here, and let the helper thread finish it.
        // If there's no helper thread, then we need to do helper duty.
        {
            SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
            fSuspended = g_pEEInterface->StartSuspendForDebug(NULL, TRUE);
        }

        // We tell the RC Thread to check for other threads now and then and help them get synchronized. (This
        // is similar to what is done when suspending threads for GC with the HandledJITCase() function.)

        // This does not block.
        // Pinging this will waken the helper thread (or temp H. thread) and tell it to sweep & send
        // the sync complete.
        m_pRCThread->WatchForStragglers();

        // It's possible we may not have a real helper thread.
        // - on startup in dllmain, helper is blocked on DllMain loader lock.
        // - on shutdown, helper has been removed on us.
        // In those cases, we need somebody to send the sync-complete, and handle
        // managed events, and wait for the continue. So we pretend to be the helper thread.
        STRESS_LOG0(LF_CORDB, LL_EVERYTHING, "D::SSCIPCE: Calling IsRCThreadReady()\n");

        // We must check the helper thread status while under the lock.
        _ASSERTE(ThreadHoldsLock());
        // If we failed to suspend, then that means we must have multiple managed threads.
        // That means that our helper is not blocked on starting up, thus we can wait infinite on it.
        // Thus we don't need to do helper duty if the suspend fails.
        bool fShouldDoHelperDuty = !m_pRCThread->IsRCThreadReady() && fSuspended;
        if (fShouldDoHelperDuty && !IsAtProcessExit())
        {
            // In V1.0, we had the assumption that if the helper thread isn't ready yet, then we're in
            // a state that SuspendForDebug will succeed on the first try, and thus we'll
            // never call Sweep when doing helper thread duty.
            _ASSERTE(fSuspended);

            // This call will do a ton of work, it will toggle the lock,
            // and it will block until we receive a continue!
            DoHelperThreadDuty();

            // We will have released the TSL after the call to continue.
        }
        _ASSERTE(ThreadHoldsLock()); // still hold the lock. (though it may have been toggled)
    }
}


//
// ReleaseAllRuntimeThreads releases all Runtime threads that may be
// stopped after trapping and sending the at safe point event.
//
void Debugger::ReleaseAllRuntimeThreads(AppDomain *pAppDomain)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;

        // We acquired the lock b/c we're in a scope between LFES & UFES.
        PRECONDITION(ThreadHoldsLock());

        // Currently, this is only done on a helper thread.
        PRECONDITION(ThisIsHelperThreadWorker());

        // Make sure that we were stopped...
        PRECONDITION(m_trappingRuntimeThreads && m_stopped);
    }
    CONTRACTL_END;

    //<TODO>@todo APPD if we want true isolation, remove this & finish the work</TODO>
    pAppDomain = NULL;

    STRESS_LOG1(LF_CORDB, LL_INFO10000, "D::RART: Releasing all Runtime threads "
        "for AppD 0x%x.\n", pAppDomain);

    // Mark that we're on our way now...
    m_trappingRuntimeThreads = FALSE;
    m_stopped = FALSE;

    // Go ahead and resume the Runtime threads.
    g_pEEInterface->ResumeFromDebug(pAppDomain);
}

bool Debugger::IsJMCMethod(Module* pModule, mdMethodDef tkMethod)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        MODE_ANY;
        PRECONDITION(CORDebuggerAttached());
    }
    CONTRACTL_END;

#ifdef _DEBUG
    Crst crstDbg(CrstIsJMCMethod, CRST_UNSAFE_ANYMODE);
    PRECONDITION(crstDbg.IsSafeToTake());
#endif

    DebuggerMethodInfo *pInfo = GetOrCreateMethodInfo(pModule, tkMethod);

    if (pInfo == NULL)
        return false;

    return pInfo->IsJMCFunction();
}

/******************************************************************************
 * Called by Runtime when on a 1st chance Native Exception.
 * This is likely when we hit a breakpoint / single-step.
 * This is called for all native exceptions (except COM+) on managed threads,
 * regardless of whether the debugger is attached.
 ******************************************************************************/
bool Debugger::FirstChanceNativeException(EXCEPTION_RECORD *exception,
                                          CONTEXT *context,
                                          DWORD code,
                                          Thread *thread,
                                          BOOL fIsVEH)
{

    // @@@
    // Implement DebugInterface
    // Can be called from EE exception code. Or from our M2UHandoffHijackFilter
    // must be on managed thread.

    CONTRACTL
    {
        NOTHROW;

        // No clear GC_triggers semantics here. See DispatchNativeException.
        WRAPPER(GC_TRIGGERS);
        MODE_ANY;

        PRECONDITION(CheckPointer(exception));
        PRECONDITION(CheckPointer(context));
        PRECONDITION(CheckPointer(thread));
    }
    CONTRACTL_END;


    // Ignore any notification exceptions sent from code:Debugger.SendRawEvent.
    // This is not a common case, but could happen in some cases described
    // in SendRawEvent. Either way, Left-Side and VM should just ignore these.
    if (IsEventDebuggerNotification(exception, PTR_TO_CORDB_ADDRESS(GetClrModuleBase())))
    {
        return true;
    }

    bool retVal;
#ifdef OUT_OF_PROCESS_SETTHREADCONTEXT
    DebuggerSteppingInfo debuggerSteppingInfo;
#endif

    {
        // Don't stop for native debugging anywhere inside our inproc-Filters.
        CantStopHolder hHolder;

        if (!CORDBUnrecoverableError(this))
        {
            retVal = DebuggerController::DispatchNativeException(exception, context,
                                                               code, thread
#ifdef OUT_OF_PROCESS_SETTHREADCONTEXT
                                                               ,&debuggerSteppingInfo
#endif
                                                               );
        }
        else
        {
            retVal = false;
        }
    }

#if defined(OUT_OF_PROCESS_SETTHREADCONTEXT) && !defined(DACCESS_COMPILE)
    if (retVal && fIsVEH)
    {
        // This does not return. Out-of-proc debugger will update the thread context
        // within this call.
        SendSetThreadContextNeeded(context, &debuggerSteppingInfo);
    }
#endif
    return retVal;
}

/******************************************************************************
 *
 ******************************************************************************/
PRD_TYPE Debugger::GetPatchedOpcode(CORDB_ADDRESS_TYPE *ip)
{
    WRAPPER_NO_CONTRACT;

    if (!CORDBUnrecoverableError(this))
    {
        return DebuggerController::GetPatchedOpcode(ip);
    }
    else
    {
        PRD_TYPE mt;
        InitializePRD(&mt);
        return mt;
    }
}

/******************************************************************************
 *
 ******************************************************************************/
BOOL Debugger::CheckGetPatchedOpcode(CORDB_ADDRESS_TYPE *address, /*OUT*/ PRD_TYPE *pOpcode)
{
    WRAPPER_NO_CONTRACT;
    CONSISTENCY_CHECK(CheckPointer(address));
    CONSISTENCY_CHECK(CheckPointer(pOpcode));

    if (CORDebuggerAttached() && !CORDBUnrecoverableError(this))
    {
        return DebuggerController::CheckGetPatchedOpcode(address, pOpcode);
    }
    else
    {
        InitializePRD(pOpcode);
        return FALSE;
    }
}

/******************************************************************************
 *
 ******************************************************************************/
void Debugger::TraceCall(const BYTE *code)
{
    CONTRACTL
    {
        // We're being called right before we call managed code. Can't trigger
        // because there may be unprotected args on the stack.
        MODE_COOPERATIVE;
        GC_NOTRIGGER;

        NOTHROW;
    }
    CONTRACTL_END;


    Thread * pCurThread = g_pEEInterface->GetThread();
    // Ensure we never even think about running managed code on the helper thread.
    _ASSERTE(!ThisIsHelperThreadWorker() || !"You're running managed code on the helper thread");

    // One threat is that our helper thread may be forced to execute a managed DLL main.
    // In that case, it's before the helper thread proc is even executed, so our conventional
    // IsHelperThread() checks are inadequate.
    _ASSERTE((GetCurrentThreadId() != g_pRCThread->m_DbgHelperThreadOSTid) || !"You're running managed code on the helper thread");

    _ASSERTE((g_pEEInterface->GetThreadFilterContext(pCurThread) == NULL) || !"Shouldn't run managed code w/ Filter-Context set");

    if (!CORDBUnrecoverableError(this))
    {
        // There are situations where our callers can't tolerate us throwing.
        EX_TRY
        {
            // Since we have a try catch and the debugger code can deal properly with
            // faults occurring inside DebuggerController::DispatchTraceCall, we can safely
            // establish a FAULT_NOT_FATAL region. This is required since some callers can't
            // tolerate faults.
            FAULT_NOT_FATAL();

            DebuggerController::DispatchTraceCall(pCurThread, code);
        }
        EX_CATCH
        {
            // We're being called for our benefit, not our callers. So if we fail,
            // they don't care.
            // Failure for us means that some steppers may miss their notification
            // for entering managed code.
            LOG((LF_CORDB, LL_INFO10000, "Debugger::TraceCall - inside catch, %p\n", code));
        }
        EX_END_CATCH(SwallowAllExceptions);
    }
}

/******************************************************************************
 * For Just-My-Code (aka Just-User-Code).
 * Invoked from a probe in managed code when we enter a user method and
 * the flag (set by GetJMCFlagAddr) for that method is != 0.
 * pIP - the ip within the method, right after the prolog.
  ******************************************************************************/
void Debugger::OnMethodEnter(void * pIP)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO1000000, "D::OnMethodEnter(ip=%p)\n", pIP));

    if (!CORDebuggerAttached())
    {
        LOG((LF_CORDB, LL_INFO1000000, "D::OnMethodEnter returning since debugger not attached.\n"));
        return;
    }
    FramePointer fp = LEAF_MOST_FRAME;
    DebuggerController::DispatchMethodEnter(pIP, fp);
}
/******************************************************************************
 * GetJMCFlagAddr
 * Provide an address of the flag that the JMC probes use to decide whether
 * or not to call TriggerMethodEnter.
 * Called for each method that we jit.
 * md - method desc for the JMC probe
 * returns an address of a flag that the probe can use.
 ******************************************************************************/
DWORD* Debugger::GetJMCFlagAddr(Module * pModule)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(CheckPointer(pModule));
    }
    CONTRACTL_END;

    // This callback will be invoked whenever we jit debuggable code.
    // A debugger may not be attached yet, but we still need someplace
    // to store this dword.
    // Use the EE's module, because it's always around, even if a debugger
    // is attached or not.
    return &(pModule->m_dwDebuggerJMCProbeCount);
}

/******************************************************************************
 * Updates the JMC flag on all the EE modules.
 * We can do this as often as we'd like - though it's a perf hit.
 ******************************************************************************/
void Debugger::UpdateAllModuleJMCFlag(bool fStatus)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO1000000, "D::UpdateModuleJMCFlag to %d\n", fStatus));

    _ASSERTE(HasDebuggerDataLock());

    // Loop through each module.
    // The module table is lazily allocated. As soon as we set JMC status on any module, that will cause an
    // allocation of the module table. So if the table isn't allocated no module has JMC set,
    // and so there is nothing to update.
    if (m_pModules != NULL)
    {
        HASHFIND f;
        for (DebuggerModule * m = m_pModules->GetFirstModule(&f);
             m != NULL;
             m = m_pModules->GetNextModule(&f))
        {
            // the primary module may get called multiple times, but that's ok.
            UpdateModuleJMCFlag(m->GetRuntimeModule(), fStatus);
        } // end for all modules.
    }
}

/******************************************************************************
 * Updates the JMC flag on the given Primary module
 * We can do this as often as we'd like - though it's a perf hit.
 * If we've only changed methods in a single module, then we can just call this.
 * If we do a more global thing (Such as enable MethodEnter), then that could
 * affect all modules, so we use the UpdateAllModuleJMCFlag helper.
 ******************************************************************************/
void Debugger::UpdateModuleJMCFlag(Module * pRuntimeModule, bool fStatus)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    _ASSERTE(HasDebuggerDataLock());


    DWORD * pFlag = &(pRuntimeModule->m_dwDebuggerJMCProbeCount);
    _ASSERTE(pFlag != NULL);

    if (pRuntimeModule->HasAnyJMCFunctions())
    {
        // If this is a user-code module, then update the JMC flag
        // the probes look at so that we get MethodEnter callbacks.
        *pFlag = fStatus;

        LOG((LF_CORDB, LL_EVERYTHING, "D::UpdateModuleJMCFlag, module %p is user code\n", pRuntimeModule));
    } else {
        LOG((LF_CORDB, LL_EVERYTHING, "D::UpdateModuleJMCFlag, module %p is not-user code\n", pRuntimeModule));

        // if non-user code, flag should be 0 so that we don't waste
        // cycles in the callbacks.
        _ASSERTE(*pFlag == 0);
    }
}

// This sets the JMC status for the entire module.
// fStatus - default status for whole module
void Debugger::SetModuleDefaultJMCStatus(Module * pRuntimeModule, bool fStatus)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(ThisIsHelperThreadWorker());
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO100000, "DM::SetJMCStatus, status=%d, this=%p\n", fStatus, this));

    // Ensure that all active DMIs have our status.
    // All new DMIs can lookup their status from us.
    // This should also update the module count of active JMC DMI's.
    DebuggerMethodInfoTable * pTable = g_pDebugger->GetMethodInfoTable();

    if (pTable != NULL)
    {
        Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger);
        HASHFIND info;

        for (DebuggerMethodInfo *dmi = pTable->GetFirstMethodInfo(&info);
            dmi != NULL;
            dmi = pTable->GetNextMethodInfo(&info))
        {
            if (dmi->GetRuntimeModule() == pRuntimeModule)
            {
                // This DMI is in this module, so update its status
                dmi->SetJMCStatus(fStatus);
            }
        }
    }

    pRuntimeModule->SetJMCStatus(fStatus);

#ifdef _DEBUG
    // If we're disabling JMC in this module, then we shouldn't
    // have any active JMC functions.
    if (!fStatus)
    {
        _ASSERTE(!pRuntimeModule->HasAnyJMCFunctions());
    }
#endif
}

/******************************************************************************
 * Called by GC to determine if it's safe to do a GC.
 ******************************************************************************/
bool Debugger::ThreadsAtUnsafePlaces(void)
{
    LIMITED_METHOD_CONTRACT;

    // If we're in shutdown mode, then all other threads are parked.
    // Even if they claim to be at unsafe regions, they're still safe to do a GC. They won't touch
    // their stacks.
    if (m_fShutdownMode)
    {
        if (m_threadsAtUnsafePlaces > 0)
        {
            STRESS_LOG1(LF_CORDB, LL_INFO10000, "D::TAUP: Claiming safety in shutdown mode.%d\n", m_threadsAtUnsafePlaces);
        }
        return false;
    }


    return (m_threadsAtUnsafePlaces != 0);
}

void Debugger::SuspendForGarbageCollectionStarted()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    this->m_isGarbageCollectionEventsEnabledLatch = this->m_isGarbageCollectionEventsEnabled;
    this->m_willBlockOnGarbageCollectionEvent = this->m_isGarbageCollectionEventsEnabledLatch;
}

void Debugger::SuspendForGarbageCollectionCompleted()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    this->m_isSuspendedForGarbageCollection = TRUE;

    if (!CORDebuggerAttached() || !this->m_isGarbageCollectionEventsEnabledLatch)
    {
        return;
    }
    this->m_isBlockedOnGarbageCollectionEvent = TRUE;

    Thread* pThread = GetThread();

    if (CORDBUnrecoverableError(this))
        return;

    {
        Debugger::DebuggerLockHolder dbgLockHolder(this);

        DebuggerIPCEvent* ipce1 = m_pRCThread->GetIPCEventSendBuffer();
        InitIPCEvent(ipce1,
            DB_IPCE_BEFORE_GARBAGE_COLLECTION,
            pThread,
            AppDomain::GetCurrentDomain());

        m_pRCThread->SendIPCEvent();
        this->SuspendComplete(true);
    }

    WaitForSingleObject(this->GetGarbageCollectionBlockerEvent(), INFINITE);
    ResetEvent(this->GetGarbageCollectionBlockerEvent());
}

void Debugger::ResumeForGarbageCollectionStarted()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    this->m_isSuspendedForGarbageCollection = FALSE;

    if (!CORDebuggerAttached() || !this->m_isGarbageCollectionEventsEnabledLatch)
    {
        return;
    }

    Thread* pThread = GetThread();

    if (CORDBUnrecoverableError(this))
        return;

    {
        Debugger::DebuggerLockHolder dbgLockHolder(this);

        DebuggerIPCEvent* ipce1 = m_pRCThread->GetIPCEventSendBuffer();
        InitIPCEvent(ipce1,
            DB_IPCE_AFTER_GARBAGE_COLLECTION,
            pThread,
            AppDomain::GetCurrentDomain());

        m_pRCThread->SendIPCEvent();
        this->SuspendComplete(true);
    }

    WaitForSingleObject(this->GetGarbageCollectionBlockerEvent(), INFINITE);
    ResetEvent(this->GetGarbageCollectionBlockerEvent());
    this->m_isBlockedOnGarbageCollectionEvent = FALSE;
    this->m_willBlockOnGarbageCollectionEvent = FALSE;
}

#ifdef FEATURE_DATABREAKPOINT
void Debugger::SendDataBreakpoint(Thread *thread, CONTEXT *context,
    DebuggerDataBreakpoint *breakpoint)
{
    CONTRACTL
    {
        NOTHROW;
    GC_NOTRIGGER;
    }
    CONTRACTL_END;

    if (CORDBUnrecoverableError(this))
        return;

#ifdef _DEBUG
    static BOOL shouldBreak = -1;
    if (shouldBreak == -1)
        shouldBreak = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgBreakOnSendBreakpoint);

    if (shouldBreak > 0) {
        _ASSERTE(!"DbgBreakOnSendBreakpoint");
    }
#endif

    LOG((LF_CORDB, LL_INFO10000, "D::SDB: breakpoint BP:0x%x\n", breakpoint));

    _ASSERTE((g_pEEInterface->GetThread() &&
        !g_pEEInterface->GetThread()->m_fPreemptiveGCDisabled) ||
        g_fInControlC);

    _ASSERTE(ThreadHoldsLock());

    // Send a breakpoint event to the Right Side
    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
    memcpy(&(ipce->DataBreakpointData.context), context, sizeof(CONTEXT));
    InitIPCEvent(ipce,
        DB_IPCE_DATA_BREAKPOINT,
        thread,
        AppDomain::GetCurrentDomain());
    //_ASSERTE(breakpoint->m_pAppDomain == ipce->vmAppDomain.GetRawPtr());

    m_pRCThread->SendIPCEvent();
}
#endif

//
// SendBreakpoint is called by Runtime threads to send that they've
// hit a breakpoint to the Right Side.
//
void Debugger::SendBreakpoint(Thread *thread, CONTEXT *context,
                              DebuggerBreakpoint *breakpoint)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    if (CORDBUnrecoverableError(this))
        return;

#ifdef _DEBUG
    static BOOL shouldBreak = -1;
    if (shouldBreak == -1)
        shouldBreak = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgBreakOnSendBreakpoint);

    if (shouldBreak > 0) {
        _ASSERTE(!"DbgBreakOnSendBreakpoint");
    }
#endif

    LOG((LF_CORDB, LL_INFO10000, "D::SB: breakpoint BP:0x%x\n", breakpoint));

    _ASSERTE((g_pEEInterface->GetThread() &&
             !g_pEEInterface->GetThread()->m_fPreemptiveGCDisabled) ||
             g_fInControlC);

    _ASSERTE(ThreadHoldsLock());

    // Send a breakpoint event to the Right Side
    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(ipce,
                 DB_IPCE_BREAKPOINT,
                 thread,
                 AppDomain::GetCurrentDomain());
    ipce->BreakpointData.breakpointToken.Set(breakpoint);
    _ASSERTE( breakpoint->m_pAppDomain == ipce->vmAppDomain.GetRawPtr());

    m_pRCThread->SendIPCEvent();
}


//---------------------------------------------------------------------------------------
// Send a user breakpoint event for this thread and sycnhronize the process.
//
// Arguments:
//     pThread - non-null thread to send user breakpoint event for.
//
// Notes:
//     Can't assume that a debugger is attached (since it may detach before we get the lock).
void Debugger::SendUserBreakpointAndSynchronize(Thread * pThread)
{
    AtSafePlaceHolder unsafePlaceHolder(pThread);

    SENDIPCEVENT_BEGIN(this, pThread);

    // Actually send the event
    if (CORDebuggerAttached())
    {
        SendRawUserBreakpoint(pThread);
        TrapAllRuntimeThreads();
    }

    SENDIPCEVENT_END;
}

//---------------------------------------------------------------------------------------
//
// SendRawUserBreakpoint is called by Runtime threads to send that
// they've hit a user breakpoint to the Right Side. This is the event
// send only part, since it can be called from a few different places.
//
// Arguments:
//    pThread - [in] managed thread where user break point takes place.
//        mus be curernt thread.
//
void Debugger::SendRawUserBreakpoint(Thread * pThread)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_PREEMPTIVE;

        PRECONDITION(pThread == GetThreadNULLOk());

        PRECONDITION(ThreadHoldsLock());

        // Debugger must have been attached to get us to this point.
        // We hold the Debugger-lock, so debugger could not have detached from
        // underneath us either.
        PRECONDITION(CORDebuggerAttached());
    }
    CONTRACTL_END;

    if (CORDBUnrecoverableError(this))
        return;

    LOG((LF_CORDB, LL_INFO10000, "D::SRUB: user breakpoint\n"));



    // Send a breakpoint event to the Right Side
    DebuggerIPCEvent* pEvent = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(pEvent,
                 DB_IPCE_USER_BREAKPOINT,
                 pThread,
                 AppDomain::GetCurrentDomain());

    m_pRCThread->SendIPCEvent();
}

//
// SendInterceptExceptionComplete is called by Runtime threads to send that
// they've completed intercepting an exception to the Right Side. This is the event
// send only part, since it can be called from a few different places.
//
void Debugger::SendInterceptExceptionComplete(Thread *thread)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    if (CORDBUnrecoverableError(this))
        return;

    LOG((LF_CORDB, LL_INFO10000, "D::SIEC: breakpoint\n"));

    _ASSERTE(!g_pEEInterface->IsPreemptiveGCDisabled());
    _ASSERTE(ThreadHoldsLock());

    // Send a breakpoint event to the Right Side
    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(ipce,
                 DB_IPCE_INTERCEPT_EXCEPTION_COMPLETE,
                 thread,
                 AppDomain::GetCurrentDomain());

    m_pRCThread->SendIPCEvent();
}



//
// SendStep is called by Runtime threads to send that they've
// completed a step to the Right Side.
//
void Debugger::SendStep(Thread *thread, CONTEXT *context,
                        DebuggerStepper *stepper,
                        CorDebugStepReason reason)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    if (CORDBUnrecoverableError(this))
        return;

    LOG((LF_CORDB, LL_INFO10000, "D::SS: step:token:0x%p reason:0x%x\n",
        stepper, reason));

    _ASSERTE((g_pEEInterface->GetThread() &&
             !g_pEEInterface->GetThread()->m_fPreemptiveGCDisabled) ||
             g_fInControlC);

    _ASSERTE(ThreadHoldsLock());

    // Send a step event to the Right Side
    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(ipce,
                 DB_IPCE_STEP_COMPLETE,
                 thread,
                 AppDomain::GetCurrentDomain());
    ipce->StepData.stepperToken.Set(stepper);
    ipce->StepData.reason = reason;
    m_pRCThread->SendIPCEvent();
}

//-------------------------------------------------------------------------------------------------
// Send an EnC remap opportunity and block until it is continued.
//
// dji - current method information
// currentIP - IL offset within that method
// resumeIP - address of a SIZE_T that the RS will write to cross-process if they take the
//  remap opportunity. *resumeIP is untouched if the RS does not remap.
//-------------------------------------------------------------------------------------------------
void Debugger::LockAndSendEnCRemapEvent(DebuggerJitInfo * dji, SIZE_T currentIP, SIZE_T *resumeIP)
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS; // From SendIPCEvent
        PRECONDITION(dji != NULL);
    }
    CONTRACTL_END;


    LOG((LF_CORDB, LL_INFO10000, "D::LASEnCRE:\n"));

    if (CORDBUnrecoverableError(this))
        return;

    MethodDesc * pMD = dji->m_nativeCodeVersion.GetMethodDesc();

    // Note that the debugger lock is reentrant, so we may or may not hold it already.
    Thread *thread = g_pEEInterface->GetThread();
    SENDIPCEVENT_BEGIN(this, thread);

    // Send an EnC remap event to the Right Side.
    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(ipce,
                 DB_IPCE_ENC_REMAP,
                 thread,
                 AppDomain::GetCurrentDomain());

    ipce->EnCRemap.currentVersionNumber = dji->m_encVersion;
    ipce->EnCRemap.resumeVersionNumber = dji->m_methodInfo->GetCurrentEnCVersion();;
    ipce->EnCRemap.currentILOffset = currentIP;
    ipce->EnCRemap.resumeILOffset = resumeIP;
    ipce->EnCRemap.funcMetadataToken = pMD->GetMemberDef();

    LOG((LF_CORDB, LL_INFO10000, "D::LASEnCRE: methodDef 0x%x, from version %zx to %zx\n",
    ipce->EnCRemap.funcMetadataToken, ipce->EnCRemap.currentVersionNumber, ipce->EnCRemap.resumeVersionNumber));

    Module *pRuntimeModule = pMD->GetModule();

    DebuggerModule * pDModule = LookupOrCreateModule(pRuntimeModule, AppDomain::GetCurrentDomain());
    ipce->EnCRemap.vmDomainAssembly.SetRawPtr((pDModule ? pDModule->GetDomainAssembly() : NULL));

    LOG((LF_CORDB, LL_INFO10000, "D::LASEnCRE: %s::%s dmod:%p\n",
        pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, pDModule));

    // IPC event is now initialized, so we can send it over.
    SendSimpleIPCEventAndBlock();

    // This will block on the continue
    SENDIPCEVENT_END;

    LOG((LF_CORDB, LL_INFO10000, "D::LASEnCRE: done\n"));
}

// Send the RemapComplete event and block until the debugger Continues
// pMD - specifies the method in which we've remapped into
void Debugger::LockAndSendEnCRemapCompleteEvent(MethodDesc *pMD)
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "D::LASEnCRE:\n"));

    if (CORDBUnrecoverableError(this))
        return;

    Thread *thread = g_pEEInterface->GetThread();
    // Note that the debugger lock is reentrant, so we may or may not hold it already.
    SENDIPCEVENT_BEGIN(this, thread);
    // Send an EnC remap complete event to the Right Side.
    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(ipce,
                 DB_IPCE_ENC_REMAP_COMPLETE,
                 thread,
                 AppDomain::GetCurrentDomain());

    ipce->EnCRemapComplete.funcMetadataToken = pMD->GetMemberDef();

    Module *pRuntimeModule = pMD->GetModule();

    DebuggerModule * pDModule = LookupOrCreateModule(pRuntimeModule, AppDomain::GetCurrentDomain());
    ipce->EnCRemapComplete.vmDomainAssembly.SetRawPtr((pDModule ? pDModule->GetDomainAssembly() : NULL));

    LOG((LF_CORDB, LL_INFO10000, "D::LASEnCRE: %s::%s dmod:%p, methodDef:0x%08x \n",
        pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName,
        pDModule,
        ipce->EnCRemap.funcMetadataToken));

    // IPC event is now initialized, so we can send it over.
    SendSimpleIPCEventAndBlock();

    // This will block on the continue
    SENDIPCEVENT_END;

    LOG((LF_CORDB, LL_INFO10000, "D::LASEnCRE: done\n"));

}
//
// This function sends a notification to the RS about a specific update that has occurred as part of
// applying an Edit and Continue.  We send notification only for function add/update and field add.
// At this point, the EE is already stopped for handling an EnC ApplyChanges operation, so no need
// to take locks etc.
//
void Debugger::SendEnCUpdateEvent(DebuggerIPCEventType eventType,
                                  Module * pModule,
                                  mdToken memberToken,
                                  mdTypeDef classToken,
                                  SIZE_T enCVersion)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "D::SEnCUE: type: 0x%x module: %p tkMember: 0x%08x tkType: 0x%08x, ver: %zu\n",
        eventType, pModule, memberToken, classToken, enCVersion));

    _ASSERTE(eventType == DB_IPCE_ENC_UPDATE_FUNCTION ||
                      eventType == DB_IPCE_ENC_ADD_FUNCTION ||
                      eventType== DB_IPCE_ENC_ADD_FIELD);

    if (CORDBUnrecoverableError(this))
        return;

    // Send an EnC UpdateFunction event to the Right Side.
    DebuggerIPCEvent* event = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(event,
                 eventType,
                 NULL,
                 NULL);

    event->EnCUpdate.newVersionNumber = enCVersion;
    event->EnCUpdate.memberMetadataToken = memberToken;
    // we have to pass the class token across to the RS because we cannot look it up over
    // there based on the added field/method because the metadata on the RS will not yet
    // have the changes applied, so the token will not exist in its metadata and we have
    // no way to find it.
    event->EnCUpdate.classMetadataToken = classToken;

    _ASSERTE(pModule);

    DebuggerModule * pDModule = LookupOrCreateModule(pModule, AppDomain::GetCurrentDomain());
    event->EnCUpdate.vmDomainAssembly.SetRawPtr((pDModule ? pDModule->GetDomainAssembly() : NULL));

    m_pRCThread->SendIPCEvent();

    LOG((LF_CORDB, LL_INFO10000, "D::SEnCUE: done\n"));
}


//
// Send a BreakpointSetError event to the Right Side if the given patch is for a breakpoint. Note: we don't care if this
// fails, there is nothing we can do about it anyway, and the breakpoint just wont hit.
//
void Debugger::LockAndSendBreakpointSetError(PATCH_UNORDERED_ARRAY * listUnbindablePatches)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        GC_TRIGGERS;
    }
    CONTRACTL_END;

    _ASSERTE(listUnbindablePatches != NULL);

    if (CORDBUnrecoverableError(this))
        return;

    ULONG count = listUnbindablePatches->Count();
    _ASSERTE(count > 0); // must send at least 1 event.
    LOG((LF_CORDB, LL_INFO10000, "D::LASBSE: Failed to bind %u patch(s)\n", count));

    Thread *thread = g_pEEInterface->GetThread();
    // Note that the debugger lock is reentrant, so we may or may not hold it already.
    SENDIPCEVENT_BEGIN(this, thread);

    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();

    for(ULONG i = 0; i < count; i++)
    {
        DebuggerControllerPatch *patch = listUnbindablePatches->Table()[i];
        _ASSERTE(patch != NULL);

        // Only do this for breakpoint controllers
        DebuggerController *controller = patch->controller;

        if (controller->GetDCType() != DEBUGGER_CONTROLLER_BREAKPOINT)
            continue;

        LOG((LF_CORDB, LL_INFO10000, "D::LASBSE: Breakpoint binding failure (%u/%u) %p type: %u\n",
            i+1, count, controller, controller->GetDCType()));

        // Send a breakpoint set error event to the Right Side.
        InitIPCEvent(ipce, DB_IPCE_BREAKPOINT_SET_ERROR, thread, AppDomain::GetCurrentDomain());

        ipce->BreakpointSetErrorData.breakpointToken.Set(static_cast<DebuggerBreakpoint*> (controller));

        // IPC event is now initialized, so we can send it over.
        m_pRCThread->SendIPCEvent();
    }

    // Stop all Runtime threads
    TrapAllRuntimeThreads();

    // This will block on the continue
    SENDIPCEVENT_END;

}

//
// Called from the controller to lock the debugger for event
// sending. This is called before controller events are sent, like
// breakpoint, step complete, and thread started.
//
// Note that it's possible that the debugger detached (and destroyed our IPC
// events) while we're waiting for our turn.
// So Callers should check for that case.
void Debugger::LockForEventSending(DebuggerLockHolder *dbgLockHolder)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_PREEMPTIVE;
    }
    CONTRACTL_END;

    // @todo - Force our parents to bump up the stop-count. That way they can
    // guarantee it's balanced.
    IncCantStopCount();
    _ASSERTE(IsInCantStopRegion());

    // What we need is for caller to get the debugger lock
    if (dbgLockHolder != NULL)
    {
        dbgLockHolder->Acquire();
    }

#ifdef _DEBUG
     // Track our TID. We're not re-entrant.
    //_ASSERTE(m_tidLockedForEventSending == 0);
    m_tidLockedForEventSending = GetCurrentThreadId();
#endif

}

//
// Called from the controller to unlock the debugger from event
// sending. This is called after controller events are sent, like
// breakpoint, step complete, and thread started.
//
void Debugger::UnlockFromEventSending(DebuggerLockHolder *dbgLockHolder)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_PREEMPTIVE;
    }
    CONTRACTL_END;

#ifdef _DEBUG
    //_ASSERTE(m_tidLockedForEventSending == GetCurrentThreadId());
    m_tidLockedForEventSending = 0;
#endif
    if (dbgLockHolder != NULL)
    {
        dbgLockHolder->Release();
    }
    // @todo - Force our parents to bump up the stop-count. That way they can
    // guarantee it's balanced.
    _ASSERTE(IsInCantStopRegion());
    DecCantStopCount();
}


//
// Called from the controller after all events have been sent for a
// thread to sync the process.
//
void Debugger::SyncAllThreads(DebuggerLockHolder *dbgLockHolder)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    if (CORDBUnrecoverableError(this))
        return;

    STRESS_LOG0(LF_CORDB, LL_INFO10000, "D::SAT: sync all threads.\n");

    Thread *pThread = g_pEEInterface->GetThread();
    (void)pThread; //prevent "unused variable" error from GCC
    _ASSERTE((pThread &&
             !pThread->m_fPreemptiveGCDisabled) ||
              g_fInControlC);

    _ASSERTE(ThreadHoldsLock());

    // Stop all Runtime threads
    TrapAllRuntimeThreads();
}

//---------------------------------------------------------------------------------------
// Launch a debugger and then trigger a breakpoint (either managed or native)
//
// Arguments:
//    useManagedBPForManagedAttach - TRUE if we should stop with a managed breakpoint
//                                   when managed attached, FALSE if we should always
//                                   stop with a native breakpoint
//    pThread - the managed thread that attempts to launch the registered debugger
//    pExceptionInfo - the unhandled exception info
//    explicitUserRequest - TRUE if this attach is caused by a call to the Debugger.Launch() API.
//
// Returns:
//    S_OK on success. Else failure.
//
// Notes:
//    This function doesn't try to stop the launched native debugger by calling DebugBreak().
//    It sends a breakpoint event only for managed debuggers.
//
HRESULT Debugger::LaunchDebuggerForUser(Thread * pThread, EXCEPTION_POINTERS * pExceptionInfo,
                                        BOOL useManagedBPForManagedAttach, BOOL explicitUserRequest)
{
    WRAPPER_NO_CONTRACT;

    LOG((LF_CORDB, LL_INFO10000, "D::LDFU: Attaching Debugger.\n"));

    //
    // Initiate a jit attach
    //
    JitAttach(pThread, pExceptionInfo, useManagedBPForManagedAttach, explicitUserRequest);

    if (useManagedBPForManagedAttach)
    {
        if(CORDebuggerAttached() && (g_pEEInterface->GetThread() != NULL))
        {
            //
            // Send a managed-breakpoint.
            //
            SendUserBreakpointAndSynchronize(g_pEEInterface->GetThread());
        }
        else if (!CORDebuggerAttached() && IsDebuggerPresent())
        {
            //
            // If the registered debugger is not a managed debugger, send a native breakpoint
            //
            DebugBreak();
        }
    }
    else if(!useManagedBPForManagedAttach)
    {
        //
        // Send a native breakpoint
        //
        DebugBreak();
    }

    if (!IsDebuggerPresent())
    {
        LOG((LF_CORDB, LL_ERROR, "D::LDFU: Failed to launch the debugger.\n"));
    }

    return S_OK;
}


// The following JDI structures will be passed to a debugger on Vista.  Because we do not know when the debugger
// will be done looking at them, and there is at most one debugger attaching to the process, we always set them
// once and leave them set without the risk of clobbering something we care about.
JIT_DEBUG_INFO   Debugger::s_DebuggerLaunchJitInfo = {0};
EXCEPTION_RECORD Debugger::s_DebuggerLaunchJitInfoExceptionRecord = {0};
CONTEXT          Debugger::s_DebuggerLaunchJitInfoContext = {};

//----------------------------------------------------------------------------
//
// InitDebuggerLaunchJitInfo - initialize JDI structure on Vista
//
// Arguments:
//    pThread - the managed thread with the unhandled exception
//    pExceptionInfo - unhandled exception info
//
// Return Value:
//    None
//
//----------------------------------------------------------------------------
void Debugger::InitDebuggerLaunchJitInfo(Thread * pThread, EXCEPTION_POINTERS * pExceptionInfo)
{
    LIMITED_METHOD_CONTRACT;

    _ASSERTE((pExceptionInfo != NULL) &&
             (pExceptionInfo->ContextRecord != NULL) &&
             (pExceptionInfo->ExceptionRecord != NULL));

    if ((pExceptionInfo == NULL) || (pExceptionInfo->ContextRecord == NULL) || (pExceptionInfo->ExceptionRecord == NULL))
    {
        return;
    }

    s_DebuggerLaunchJitInfoExceptionRecord = *pExceptionInfo->ExceptionRecord;
    s_DebuggerLaunchJitInfoContext = *pExceptionInfo->ContextRecord;

    s_DebuggerLaunchJitInfo.dwSize = sizeof(s_DebuggerLaunchJitInfo);
    s_DebuggerLaunchJitInfo.dwThreadID = pThread == NULL ? GetCurrentThreadId() : pThread->GetOSThreadId();
    s_DebuggerLaunchJitInfo.lpExceptionRecord = reinterpret_cast<ULONG64>(&s_DebuggerLaunchJitInfoExceptionRecord);
    s_DebuggerLaunchJitInfo.lpContextRecord = reinterpret_cast<ULONG64>(&s_DebuggerLaunchJitInfoContext);
    s_DebuggerLaunchJitInfo.lpExceptionAddress = s_DebuggerLaunchJitInfoExceptionRecord.ExceptionAddress != NULL ?
        reinterpret_cast<ULONG64>(s_DebuggerLaunchJitInfoExceptionRecord.ExceptionAddress) :
        reinterpret_cast<ULONG64>(reinterpret_cast<PVOID>(GetIP(pExceptionInfo->ContextRecord)));

#if defined(HOST_WINDOWS)
#if defined(TARGET_X86)
    s_DebuggerLaunchJitInfo.dwProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
#elif defined(TARGET_AMD64)
    s_DebuggerLaunchJitInfo.dwProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
#elif defined(TARGET_ARM)
    s_DebuggerLaunchJitInfo.dwProcessorArchitecture = PROCESSOR_ARCHITECTURE_ARM;
#elif defined(TARGET_ARM64)
    s_DebuggerLaunchJitInfo.dwProcessorArchitecture = PROCESSOR_ARCHITECTURE_ARM64;
#else
#error Unknown processor.
#endif
#endif
}


//----------------------------------------------------------------------------
//
// GetDebuggerLaunchJitInfo - retrieve the initialized JDI structure on Vista
//
// Arguments:
//    None
//
// Return Value:
//    JIT_DEBUG_INFO * - pointer to JDI structure
//
//----------------------------------------------------------------------------
JIT_DEBUG_INFO * Debugger::GetDebuggerLaunchJitInfo(void)
{
    LIMITED_METHOD_CONTRACT;

    _ASSERTE((s_DebuggerLaunchJitInfo.lpExceptionAddress != 0) &&
             (s_DebuggerLaunchJitInfo.lpExceptionRecord != 0) &&
             (s_DebuggerLaunchJitInfo.lpContextRecord != 0) &&
             (((EXCEPTION_RECORD *)(s_DebuggerLaunchJitInfo.lpExceptionRecord))->ExceptionAddress != NULL));

    return &s_DebuggerLaunchJitInfo;
}
#endif // !DACCESS_COMPILE


// This function checks the registry for the debug launch setting upon encountering an exception or breakpoint.
DebuggerLaunchSetting Debugger::GetDbgJITDebugLaunchSetting()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

#if TARGET_UNIX
    DebuggerLaunchSetting setting = DLS_ATTACH_DEBUGGER;
#else
    BOOL bAuto = FALSE;

    DebuggerLaunchSetting setting = DLS_ASK_USER;

    DWORD cchDbgFormat = MAX_LONGPATH;
    INDEBUG(DWORD cchOldDbgFormat = cchDbgFormat);

#if defined(DACCESS_COMPILE)
    WCHAR * wszDbgFormat = new (nothrow) WCHAR[cchDbgFormat];
#else
    WCHAR * wszDbgFormat = new (interopsafe, nothrow) WCHAR[cchDbgFormat];
#endif // DACCESS_COMPILE

    if (wszDbgFormat == NULL)
    {
        return setting;
    }

    HRESULT hr = GetDebuggerSettingInfoWorker(wszDbgFormat, &cchDbgFormat, &bAuto);
    while (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
    {
        _ASSERTE(cchDbgFormat > cchOldDbgFormat);
        INDEBUG(cchOldDbgFormat = cchDbgFormat);

#if defined(DACCESS_COMPILE)
        delete [] wszDbgFormat;
        wszDbgFormat = new (nothrow) WCHAR[cchDbgFormat];
#else
        DeleteInteropSafe(wszDbgFormat);
        wszDbgFormat = new (interopsafe, nothrow) WCHAR[cchDbgFormat];
#endif // DACCESS_COMPILE

        if (wszDbgFormat == NULL)
        {
            return setting;
        }

        hr = GetDebuggerSettingInfoWorker(wszDbgFormat, &cchDbgFormat, &bAuto);
    }

#if defined(DACCESS_COMPILE)
    delete [] wszDbgFormat;
#else
    DeleteInteropSafe(wszDbgFormat);
#endif // DACCESS_COMPILE

    if (SUCCEEDED(hr) && bAuto)
    {
        setting = DLS_ATTACH_DEBUGGER;
    }
#endif // TARGET_UNIX

    return setting;
}

// Returns a bitfield reflecting the managed debugging state at the time of
// the jit attach.
CLR_DEBUGGING_PROCESS_FLAGS Debugger::GetAttachStateFlags()
{
    LIMITED_METHOD_DAC_CONTRACT;
    ULONG flags = CLRJitAttachState;
    return (CLR_DEBUGGING_PROCESS_FLAGS)flags;
}

#ifndef DACCESS_COMPILE
//-----------------------------------------------------------------------------
// Get the full launch string for a jit debugger.
//
// If a jit-debugger is registed, then writes string into pStrArgsBuf and
//   return true.
//
// If no jit-debugger is registered, then return false.
//
// Throws on error (like OOM).
//-----------------------------------------------------------------------------
bool Debugger::GetCompleteDebuggerLaunchString(SString * pStrArgsBuf)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

#ifndef TARGET_UNIX
    DWORD pid = GetCurrentProcessId();

    SString ssDebuggerString;
    GetDebuggerSettingInfo(ssDebuggerString, NULL);

    if (ssDebuggerString.IsEmpty())
    {
        // No jit-debugger available. Don't make one up.
        return false;
    }

    // There is no security concern to expect that the debug string we retrieve from HKLM follows a certain
    // format because changing HKLM keys requires admin priviledge.  Padding with zeros is not a security mitigation,
    // but rather a forward looking compatibility measure.  If future versions of Windows introduces more parameters for
    // JIT debugger launch, it is preferrable to pass zeros than other random values for those unsupported parameters.
    pStrArgsBuf->Printf(ssDebuggerString.GetUTF8(), pid, GetUnmanagedAttachEvent(), GetDebuggerLaunchJitInfo(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

    return true;
#else // !TARGET_UNIX
    return false;
#endif // !TARGET_UNIX
}

// Proxy code for EDA
struct EnsureDebuggerAttachedParams
{
    Debugger *                  m_pThis;
    HRESULT                     m_retval;
    PROCESS_INFORMATION *       m_pProcessInfo;
    EnsureDebuggerAttachedParams() :
        m_pThis(NULL), m_retval(E_FAIL), m_pProcessInfo(NULL) {LIMITED_METHOD_CONTRACT; }
};

// This is called by the helper thread
void EDAHelperStub(EnsureDebuggerAttachedParams * p)
{
    WRAPPER_NO_CONTRACT;

    p->m_retval = p->m_pThis->EDAHelper(p->m_pProcessInfo);
}

// This gets called just like the normal version, but it sends the call over to the helper thread
HRESULT Debugger::EDAHelperProxy(PROCESS_INFORMATION * pProcessInfo)
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
    }
    CONTRACTL_END;

    _ASSERTE(!ThisIsHelperThreadWorker());
    _ASSERTE(ThreadHoldsLock());

    HRESULT hr = LazyInitWrapper();
    if (FAILED(hr))
    {
        // We already stress logged this case.
        return hr;
    }


    if (!IsGuardPageGone())
    {
        return EDAHelper(pProcessInfo);
    }

    EnsureDebuggerAttachedParams p;
    p.m_pThis = this;
    p.m_pProcessInfo = pProcessInfo;

    LOG((LF_CORDB, LL_INFO1000000, "D::EDAHelperProxy\n"));
    m_pRCThread->DoFavor((FAVORCALLBACK) EDAHelperStub, &p);
    LOG((LF_CORDB, LL_INFO1000000, "D::EDAHelperProxy return\n"));

    return p.m_retval;
}

//   E_ABORT - if the attach was declined
//   S_OK    - Jit-attach successfully started
HRESULT Debugger::EDAHelper(PROCESS_INFORMATION *pProcessInfo)
{
    CONTRACTL
    {
        NOTHROW;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;

        PRECONDITION(ThisMaybeHelperThread()); // on helper if stackoverflow.
    }
    CONTRACTL_END;

#ifndef TARGET_UNIX
    LOG((LF_CORDB, LL_INFO10000, "D::EDA: thread 0x%x is launching the debugger.\n", GetCurrentThreadId()));

    _ASSERTE(HasLazyData());

    // Another potential hang. This may get run on the helper if we have a stack overflow.
    // Hopefully the odds of 1 thread hitting a stack overflow while another is stuck holding the heap
    // lock is very small.
    SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;

    BOOL fCreateSucceeded = FALSE;

    StackSString strDbgCommand;
    const WCHAR * wszDbgCommand = NULL;

    EX_TRY
    {

        // Get the debugger to launch.  The returned string is via the strDbgCommand out param. Throws on error.
        bool fHasDebugger = GetCompleteDebuggerLaunchString(&strDbgCommand);
        if (fHasDebugger)
        {
            LOG((LF_CORDB, LL_INFO10000, "D::EDA: launching with command [%s]\n", strDbgCommand.GetUTF8()));

            wszDbgCommand = strDbgCommand.GetUnicode();
            _ASSERTE(wszDbgCommand != NULL); // would have thrown on oom.
        }
    }
    EX_CATCH
    {
    }
    EX_END_CATCH(SwallowAllExceptions);

    STARTUPINFOW startupInfo = {0};
    startupInfo.cb = sizeof(STARTUPINFOW);

    DWORD errCreate = 0;

    if (wszDbgCommand != NULL)
    {
        // Create the debugger process
        // When we are launching an debugger, we need to let the child process inherit our handles.
        // This is necessary for the debugger to signal us that the attach is complete.
        fCreateSucceeded = WszCreateProcess(NULL, const_cast<WCHAR*> (wszDbgCommand),
                               NULL, NULL,
                               TRUE,
                               CREATE_NEW_CONSOLE,
                               NULL,
                               NULL,
                               &startupInfo,
                               pProcessInfo);
        errCreate = GetLastError();
    }

    if (!fCreateSucceeded)
    {
        LOG((LF_CORDB, LL_INFO10000, "D::EDA: debugger did not launch successfully.\n"));
        return E_ABORT;
    }

    LOG((LF_CORDB, LL_INFO10000, "D::EDA: debugger launched successfully.\n"));
    return S_OK;
#else // !TARGET_UNIX
    return E_ABORT;
#endif // !TARGET_UNIX
}

// ---------------------------------------------------------------------------------------------------------------------
// This function decides who wins the race for any jit attach and marks the appropriate state that a jit
// attach is in progress.
//
// Arguments
//  willSendManagedEvent - indicates whether or not we plan to send a managed debug event after the jit attach
//  explicitUserRequest - TRUE if this attach is caused by a call to the Debugger.Launch() API.
//
// Returns
//    TRUE - if some other thread already has jit attach in progress -> this thread should block until that is complete
//    FALSE - this is the first thread to jit attach -> this thread should launch the debugger
//
//
BOOL Debugger::PreJitAttach(BOOL willSendManagedEvent, BOOL willLaunchDebugger, BOOL explicitUserRequest)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_PREEMPTIVE;
        PRECONDITION(!ThisIsHelperThreadWorker());
    }
    CONTRACTL_END;

    LOG( (LF_CORDB, LL_INFO10000, "D::PreJA: Entering\n") );

    if (m_fShutdownMode)
    {
        // Trying to JitAttach because of some failure at shutdown is likely to block because we
        // would suspend any thread that isn't the finalizer or debugger helper thread when it tries to
        // take the debugger lock below. By bailing out here we won't do the JitAttach as we normally
        // would, but at least the app should exit with an unhandled exception or FailFast rather
        // entering a hung state. We believe this is very rare scenario so mitigating the problem in
        // this way is sufficient.
        LOG( (LF_CORDB, LL_INFO10000, "D::PreJA: Leaving - shutdown case\n") );
        return FALSE;
    }

    // Multiple threads may be calling this, so need to take the lock.
    if(!m_jitAttachInProgress)
    {
        // TODO:  This is a known deadlock!  Debugger::PreJitAttach is called during WatsonLastChance.
        //        If the event (exception/crash) happens while this thread is holding the ThreadStore
        //        lock, we may deadlock if another thread holds the DebuggerMutex and is waiting on
        //        the ThreadStore lock.  The DebuggerMutex has to be broken into two smaller locks
        //        so that you can take that lock here when holding the ThreadStore lock.
        DebuggerLockHolder dbgLockHolder(this);

        if (!m_jitAttachInProgress)
        {
            m_jitAttachInProgress = TRUE;
            m_launchingDebugger = willLaunchDebugger;
            CLRJitAttachState = (willSendManagedEvent ? CLR_DEBUGGING_MANAGED_EVENT_PENDING : 0) | (explicitUserRequest ? CLR_DEBUGGING_MANAGED_EVENT_DEBUGGER_LAUNCH : 0);
            ResetEvent(GetUnmanagedAttachEvent());
            ResetEvent(GetAttachEvent());
            LOG( (LF_CORDB, LL_INFO10000, "D::PreJA: Leaving - first thread\n") );
            return TRUE;
        }
    }

    LOG( (LF_CORDB, LL_INFO10000, "D::PreJA: Leaving - following thread\n") );
    return FALSE;
}

//---------------------------------------------------------------------------------------------------------------------
// This function gets the jit debugger launched and waits for the native attach to complete
// Make sure you called PreJitAttach and it returned TRUE before you call this
//
// Arguments:
//    pThread - the managed thread with the unhandled exception
//    pExceptionInfo - the unhandled exception info
//
// Returns:
//   S_OK if the debugger was launched successfully and a failing HRESULT otherwise
//
HRESULT Debugger::LaunchJitDebuggerAndNativeAttach(Thread * pThread, EXCEPTION_POINTERS * pExceptionInfo)
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
        MODE_PREEMPTIVE;
        PRECONDITION(!ThisIsHelperThreadWorker());
    }
    CONTRACTL_END;

    // You need to have called PreJitAttach first to determine which thread gets to launch the debugger
    _ASSERTE(m_jitAttachInProgress);

    LOG( (LF_CORDB, LL_INFO10000, "D::LJDANA: Entering\n") );
    PROCESS_INFORMATION processInfo = {0};
    DebuggerLockHolder dbgLockHolder(this);

    // <TODO>
    // If the JIT debugger failed to launch or if there is no JIT debugger, EDAHelperProxy will
    // switch to preemptive GC mode to display a dialog to the user indicating the JIT debugger
    // was unavailable. There are some rare cases where this could cause a deadlock with the
    // debugger lock; however these are rare enough that fixing this doesn't meet the bar for
    // Whidbey at this point. We might want to revisit this later however.
    // </TODO>
    CONTRACT_VIOLATION(GCViolation);

    {
        LOG((LF_CORDB, LL_INFO1000, "D::EDA: Initialize JDI.\n"));

        EXCEPTION_POINTERS exceptionPointer;
        EXCEPTION_RECORD   exceptionRecord;
        CONTEXT context;

        if (pExceptionInfo == NULL)
        {
            ZeroMemory(&exceptionPointer, sizeof(exceptionPointer));
            ZeroMemory(&exceptionRecord, sizeof(exceptionRecord));
            ZeroMemory(&context, sizeof(context));

            context.ContextFlags = CONTEXT_CONTROL;
            ClrCaptureContext(&context);

            exceptionRecord.ExceptionAddress = reinterpret_cast<PVOID>(GetIP(&context));
            exceptionPointer.ContextRecord   = &context;
            exceptionPointer.ExceptionRecord = &exceptionRecord;

            pExceptionInfo = &exceptionPointer;
        }

        InitDebuggerLaunchJitInfo(pThread, pExceptionInfo);
    }

    // This will make the CreateProcess call to create the debugger process.
    // We then expect that the debugger process will turn around and attach to us.
    HRESULT hr = EDAHelperProxy(&processInfo);
    if(FAILED(hr))
    {
        return hr;
    }

    LOG((LF_CORDB, LL_INFO10000, "D::LJDANA: waiting on m_exUnmanagedAttachEvent and debugger's process handle\n"));
    DWORD  dwHandles = 2;
    HANDLE arrHandles[2];
    arrHandles[0] = GetUnmanagedAttachEvent();
    arrHandles[1] = processInfo.hProcess;

    // Let the helper thread do the attach logic for us and wait for the
    // attach event.  Must release the lock before blocking on a wait.
    dbgLockHolder.Release();

    // Wait for one or the other to be set. Multiple threads could be waiting here.
    // The events are manual events, so when they go high, all threads will be released.
    DWORD res = WaitForMultipleObjectsEx(dwHandles, arrHandles, FALSE, INFINITE, FALSE);

    // We no long need to keep handles to the debugger process.
    CloseHandle(processInfo.hProcess);
    CloseHandle(processInfo.hThread);

    // Indicate to the caller that the attach was aborted
    if (res == WAIT_OBJECT_0 + 1)
    {
        LOG((LF_CORDB, LL_INFO10000, "D::LJDANA: Debugger process is unexpectedly terminated!\n"));
        return E_FAIL;
    }

    // Otherwise, attach was successful (Note, only native attach is done so far)
    _ASSERTE((res == WAIT_OBJECT_0) && "WaitForMultipleObjectsEx failed!");
    LOG( (LF_CORDB, LL_INFO10000, "D::LJDANA: Leaving\n") );
    return S_OK;

}

// Blocks until the debugger completes jit attach
void Debugger::WaitForDebuggerAttach()
{
    LIMITED_METHOD_CONTRACT;

    LOG( (LF_CORDB, LL_INFO10000, "D::WFDA:Entering\n") );

    // if this thread previously called LaunchDebuggerAndNativeAttach then this wait is spurious,
    // the event is still set and it continues immediately. If this is an auxilliary thread however
    // then the wait is necessary
    // If we are not launching the debugger (e.g. unhandled exception on Win7), then we should not
    // wait on the unmanaged attach event.  If the debugger is launched by the OS, then the unmanaged
    // attach event passed to the debugger is created by the OS, not by us, so our event will never
    // be signaled.
    if (m_launchingDebugger)
    {
        WaitForSingleObject(GetUnmanagedAttachEvent(), INFINITE);
    }

    // Wait until the pending managed debugger attach is completed
    if (CORDebuggerPendingAttach() && !CORDebuggerAttached())
    {
        LOG( (LF_CORDB, LL_INFO10000, "D::WFDA: Waiting for managed attach too\n") );
        WaitForSingleObject(GetAttachEvent(), INFINITE);
    }

    // We can't reset the event here because some threads may
    // be just about to wait on it. If we reset it before the
    // other threads hit the wait, they'll block.

    // We have an innate race here that can't easily fix. The best
    // we can do is have a super small window (by moving the reset as
    // far out this making it very unlikely that a thread will
    // hit the window.

    LOG( (LF_CORDB, LL_INFO10000, "D::WFDA: Leaving\n") );
}

// Cleans up after jit attach is complete
void Debugger::PostJitAttach()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_PREEMPTIVE;
        PRECONDITION(!ThisIsHelperThreadWorker());
    }
    CONTRACTL_END;

    LOG( (LF_CORDB, LL_INFO10000, "D::PostJA: Entering\n") );
    // Multiple threads may be calling this, so need to take the lock.
    DebuggerLockHolder dbgLockHolder(this);

    // clear the attaching flags which allows other threads to initiate jit attach if needed
    m_jitAttachInProgress = FALSE;
    m_launchingDebugger = FALSE;
    CLRJitAttachState = 0;

    // set the attaching events to unblock other threads waiting on this attach
    // regardless of whether or not it completed
    SetEvent(GetUnmanagedAttachEvent());
    SetEvent(GetAttachEvent());
    LOG( (LF_CORDB, LL_INFO10000, "D::PostJA: Leaving\n") );
}

//---------------------------------------------------------------------------------------
// Launches a debugger and blocks waiting for it to either attach or abort the attach.
//
// Arguments:
//    pThread - the managed thread with the unhandled exception
//    pExceptionInfo - the unhandled exception info
//    willSendManagedEvent - TRUE if after getting attached we will send a managed debug event
//    explicitUserRequest - TRUE if this attach is caused by a call to the Debugger.Launch() API.
//
// Returns:
//     None. Callers can requery if a debugger is attached.
//
// Assumptions:
//     This may be called by multiple threads, each firing their own debug events. This function will handle locking.
//     Thus this could block for an arbitrary length of time:
//     - may need to prompt the user to decide if an attach occurs.
//     - may block waiting for a debugger to attach.
//
// Notes:
//     The launch string is retrieved from code:GetDebuggerSettingInfo.
//     This will not do a sync-complete. Instead, the caller can send a debug event (the jit-attach
//     event, such as a User-breakpoint or unhandled exception) and that can send a sync-complete,
//     just as if the debugger was always attached. This ensures that the jit-attach event is in the
//     same callback queue as any faked-up events that the Right-side Shim creates.
//
void Debugger::JitAttach(Thread * pThread, EXCEPTION_POINTERS * pExceptionInfo, BOOL willSendManagedEvent, BOOL explicitUserRequest)
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
        MODE_ANY;

        PRECONDITION(!ThisIsHelperThreadWorker()); // Must be a managed thread
    }
    CONTRACTL_END;

    // Don't do anything if there is a native debugger already attached or the debugging support has been disabled.
    if (IsDebuggerPresent() || m_pRCThread == NULL)
        return;

    GCX_PREEMP_EEINTERFACE_TOGGLE_IFTHREAD();

    EnsureDebuggerAttached(pThread, pExceptionInfo, willSendManagedEvent, explicitUserRequest);
}

//-----------------------------------------------------------------------------
// Ensure that a debugger is attached. Will jit-attach if needed.
//
// Arguments
//    pThread - the managed thread with the unhandled exception
//    pExceptionInfo - the unhandled exception info
//    willSendManagedEvent - true if after getting (or staying) attached we will send
//                           a managed debug event
//    explicitUserRequest - true if this attach is caused by a call to the
//                          Debugger.Launch() API.
//
// Returns:
//   None. Either a debugger is attached or it is not.
//
// Notes:
//   There are several intermediate possible outcomes:
//   - Debugger already attached before this was called.
//   - JIT-atttach debugger spawned, and attached successfully.
//   - JIT-attach debugger spawned, but declined to attach.
//   - Failed to spawn jit-attach debugger.
//
//   Ultimately, the only thing that matters at the end is whether a debugger
//   is now attached, which is retrieved via CORDebuggerAttached().
//-----------------------------------------------------------------------------
void Debugger::EnsureDebuggerAttached(Thread * pThread, EXCEPTION_POINTERS * pExceptionInfo, BOOL willSendManagedEvent, BOOL explicitUserRequest)
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
        MODE_PREEMPTIVE;
        PRECONDITION(!ThisIsHelperThreadWorker());
    }
    CONTRACTL_END;

    LOG( (LF_CORDB,LL_INFO10000,"D::EDA\n") );

    HRESULT hr = S_OK;

    // We could be in three states:
    // 1) no debugger attached
    // 2) native attached but not managed (yet?)
    // 3) native attached and managed


    // There is a race condition here that can be hit if multiple threads
    // were to trigger jit attach at the right time
    // Thread 1 starts jit attach
    // Thread 2 also starts jit attach and gets to waiting for the attach complete
    // Thread 1 rapidly completes the jit attach then starts it again
    // Thread 2 may still be waiting from the first jit attach at this point
    //
    // Note that this isn't all that bad because if the debugger hasn't actually detached
    // in the middle then the second jit attach will complete almost instantly and thread 2
    // is unblocked. If the debugger did detach in the middle then it seems reasonable for
    // thread 2 to continue to wait until until the debugger is attached once again for the
    // second attach. Basically if one jit attach completes and restarts fast enough it might
    // just go unnoticed by some threads and it will be as if it never happened. Doesn't seem
    // that bad as long as we know another jit attach is again in progress.

    BOOL startedJitAttach = FALSE;

    // First check to see if we need to launch the debugger ourselves
    if(PreJitAttach(willSendManagedEvent, TRUE, explicitUserRequest))
    {
        // if the debugger is already attached then we can't launch one
        // and whatever attach state we are in is just what we get
        if(IsDebuggerPresent())
        {
            // unblock other threads waiting on our attach and clean up
            PostJitAttach();
            return;
        }
        else
        {
            hr = LaunchJitDebuggerAndNativeAttach(pThread, pExceptionInfo);
            if(FAILED(hr))
            {
                // unblock other threads waiting on our attach and clean up
                PostJitAttach();
                return;
            }
        }
        startedJitAttach = TRUE;
    }

    // at this point someone should have launched the native debugger and
    // it is somewhere between not attached and attach complete
    // (it might have even been completely attached before this function even started)
    // step 2 - wait for the attach to complete
    WaitForDebuggerAttach();

    // step 3 - if we initiated then we also cleanup
    if(startedJitAttach)
        PostJitAttach();
    LOG( (LF_CORDB, LL_INFO10000, "D::EDA:Leaving\n") );
}


// Proxy code for AttachDebuggerForBreakpoint
// Structure used in the proxy function callback
struct SendExceptionOnHelperThreadParams
{
    Debugger        *m_pThis;
    HRESULT         m_retval;
    Thread          *m_pThread;
    OBJECTHANDLE    m_exceptionHandle;
    bool            m_continuable;
    FramePointer    m_framePointer;
    SIZE_T          m_nOffset;
    CorDebugExceptionCallbackType m_eventType;
    DWORD           m_dwFlags;


    SendExceptionOnHelperThreadParams() :
        m_pThis(NULL),
        m_retval(S_OK),
        m_pThread(NULL)
        {LIMITED_METHOD_CONTRACT; }
};

//**************************************************************************
// This function sends Exception and ExceptionCallback2 event.
//
// Arguments:
//   pThread : managed thread which exception takes place
//   exceptionHandle : handle to the managed exception object (usually
//       something derived from System.Exception)
//   fContinuable : true iff continuable
//   framePointer : frame pointer associated with callback.
//   nOffset : il offset associated with callback.
//   eventType : type of callback
//   dwFlags : additional flags (see CorDebugExceptionFlags).
//
// Returns:
//    S_OK on success. Else some error. May also throw.
//
// Notes:
//    This is a helper for code:Debugger.SendExceptionEventsWorker.
//    See code:Debugger.SendException for more details about parameters.
//    This is always called on a managed thread (never the helper thread)
//    This will synchronize and block.
//**************************************************************************
HRESULT Debugger::SendExceptionHelperAndBlock(
    Thread      *pThread,
    OBJECTHANDLE exceptionHandle,
    bool        fContinuable,
    FramePointer framePointer,
    SIZE_T      nOffset,
    CorDebugExceptionCallbackType eventType,
    DWORD       dwFlags)

{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_ANY;

        PRECONDITION(CheckPointer(pThread));
    }
    CONTRACTL_END;

    HRESULT     hr = S_OK;

    // This is a normal event to send from LS to RS
    SENDIPCEVENT_BEGIN(this, pThread);

    // This function can be called on helper thread or managed thread.
    // However, we should be holding locks upon entry

    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();

    //
    // Send pre-Whidbey EXCEPTION IPC event.
    //
    InitIPCEvent(ipce, DB_IPCE_EXCEPTION, pThread, AppDomain::GetCurrentDomain());

    ipce->Exception.vmExceptionHandle.SetRawPtr(exceptionHandle);
    ipce->Exception.firstChance = (eventType == DEBUG_EXCEPTION_FIRST_CHANCE);
    ipce->Exception.continuable = fContinuable;
    hr = m_pRCThread->SendIPCEvent();

    _ASSERTE(SUCCEEDED(hr) && "D::SE: Send ExceptionCallback event failed.");

    //
    // Send Whidbey EXCEPTION IPC event.
    //
    InitIPCEvent(ipce, DB_IPCE_EXCEPTION_CALLBACK2, pThread, AppDomain::GetCurrentDomain());

    ipce->ExceptionCallback2.framePointer = framePointer;
    ipce->ExceptionCallback2.eventType = eventType;
    ipce->ExceptionCallback2.nOffset = nOffset;
    ipce->ExceptionCallback2.dwFlags = dwFlags;
    ipce->ExceptionCallback2.vmExceptionHandle.SetRawPtr(exceptionHandle);

    LOG((LF_CORDB, LL_INFO10000, "D::SE: sending ExceptionCallback2 event"));
    hr = m_pRCThread->SendIPCEvent();

    if (eventType == DEBUG_EXCEPTION_FIRST_CHANCE)
    {
        pThread->GetExceptionState()->GetFlags()->SetSentDebugFirstChance();
    }
    else
    {
        _ASSERTE(eventType == DEBUG_EXCEPTION_UNHANDLED);
    }

    _ASSERTE(SUCCEEDED(hr) && "D::SE: Send ExceptionCallback2 event failed.");

    if (SUCCEEDED(hr))
    {
        // Stop all Runtime threads
        TrapAllRuntimeThreads();
    }

    // Let other Runtime threads handle their events.
    SENDIPCEVENT_END;

    return hr;

}

// Send various first-chance / unhandled exception events.
//
// Assumptions:
//    Caller has already determined that we want to send exception events.
//
// Notes:
//    This is a helper function for code:Debugger.SendException
void Debugger::SendExceptionEventsWorker(
    Thread * pThread,
    bool fFirstChance,
    bool fIsInterceptable,
    bool fContinuable,
    SIZE_T currentIP,
    FramePointer framePointer,
    bool atSafePlace)
{
    HRESULT hr = S_OK;

    ThreadExceptionState* pExState = pThread->GetExceptionState();
    //
    // Figure out parameters to the IPC events.
    //
    const BYTE *ip;

    SIZE_T nOffset = (SIZE_T)ICorDebugInfo::NO_MAPPING;
    DebuggerMethodInfo *pDebugMethodInfo = NULL;

    // If we're passed a zero IP or SP, then go to the ThreadExceptionState on the thread to get the data. Note:
    // we can only do this if there is a context in the pExState. There are cases (most notably the
    // EEPolicy::HandleFatalError case) where we don't have that. So we just leave the IP/SP 0.
    if ((currentIP == 0) && (pExState->GetContextRecord() != NULL))
    {
        ip = (BYTE *)GetIP(pExState->GetContextRecord());
    }
    else
    {
        ip = (BYTE *)currentIP;
    }

    if (g_pEEInterface->IsManagedNativeCode(ip))
    {

        MethodDesc *pMethodDesc = g_pEEInterface->GetNativeCodeMethodDesc(PCODE(ip));
        _ASSERTE(pMethodDesc != NULL);

        if (pMethodDesc != NULL)
        {
            DebuggerJitInfo *pDebugJitInfo = GetJitInfo(pMethodDesc, ip, &pDebugMethodInfo);

            if (pDebugJitInfo != NULL)
            {
                SIZE_T nativeOffset = CodeRegionInfo::GetCodeRegionInfo(pDebugJitInfo, pMethodDesc).AddressToOffset(ip);
                CorDebugMappingResult mapResult;
                DWORD which;

                nOffset = pDebugJitInfo->MapNativeOffsetToIL(nativeOffset, &mapResult, &which);
            }
        }
    }

    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();

    if (fFirstChance)
    {
        // We can call into this method when there is no exception in progress to alert
        // the debugger to a stack overflow, however that case should never specify first
        // chance. An exception must be in progress to check the flags on the exception state
        _ASSERTE(pThread->IsExceptionInProgress());

        //
        // Send the first chance exception if we have not already and if it is not suppressed
        //
        if (m_sendExceptionsOutsideOfJMC && !pExState->GetFlags()->SentDebugFirstChance())
        {
            // Blocking here is especially important so that the debugger can mark any code as JMC.
            hr = SendExceptionHelperAndBlock(
                pThread,
                g_pEEInterface->GetThreadException(pThread),
                fContinuable,
                framePointer,
                nOffset,
                DEBUG_EXCEPTION_FIRST_CHANCE,
                fIsInterceptable ? DEBUG_EXCEPTION_CAN_BE_INTERCEPTED : 0);

            {
                // Toggle GC into COOP to block this thread.
                GCX_COOP_EEINTERFACE();

                //
                // If we weren't at a safe place when we enabled PGC, then go ahead and unmark that fact now that we've successfully
                // disabled.
                //
                if (!atSafePlace)
                {
                    g_pDebugger->DecThreadsAtUnsafePlaces();
                }

                ProcessAnyPendingEvals(pThread);

                //
                // If we weren't at a safe place, increment the unsafe count before we enable preemptive mode.
                //
                if (!atSafePlace)
                {
                    g_pDebugger->IncThreadsAtUnsafePlaces();
                }
            } // end of GCX_CCOP_EEINTERFACE();

        } //end if (m_sendExceptionsOutsideOfJMC && !SentDebugFirstChance())
        //
        // If this is a JMC function, then we send a USER's first chance as well.
        //
        if ((pDebugMethodInfo != NULL) &&
            pDebugMethodInfo->IsJMCFunction() &&
            !pExState->GetFlags()->SentDebugUserFirstChance())
        {
            SENDIPCEVENT_BEGIN(this, pThread);

            InitIPCEvent(ipce, DB_IPCE_EXCEPTION_CALLBACK2, pThread, AppDomain::GetCurrentDomain());

            ipce->ExceptionCallback2.framePointer = framePointer;
            ipce->ExceptionCallback2.eventType = DEBUG_EXCEPTION_USER_FIRST_CHANCE;
            ipce->ExceptionCallback2.nOffset = nOffset;
            ipce->ExceptionCallback2.dwFlags = fIsInterceptable ? DEBUG_EXCEPTION_CAN_BE_INTERCEPTED : 0;
            ipce->ExceptionCallback2.vmExceptionHandle.SetRawPtr(g_pEEInterface->GetThreadException(pThread));

            LOG((LF_CORDB, LL_INFO10000, "D::SE: sending ExceptionCallback2 (USER FIRST CHANCE)"));
            hr = m_pRCThread->SendIPCEvent();

            _ASSERTE(SUCCEEDED(hr) && "D::SE: Send ExceptionCallback2 (User) event failed.");

            if (SUCCEEDED(hr))
            {
                // Stop all Runtime threads
                TrapAllRuntimeThreads();
            }

            pExState->GetFlags()->SetSentDebugUserFirstChance();

            // Let other Runtime threads handle their events.
            SENDIPCEVENT_END;

        } // end if (!SentDebugUserFirstChance)

    } // end if (firstChance)
    else
    {
        // unhandled exception case
        // if there is no exception in progress then we are sending a fake exception object
        //   as an indication of a fatal error (stack overflow). In this case it is illegal
        //   to read GetFlags() from the exception state.
        // else if there is an exception in progress we only want to send the notification if
        //   we did not already send a CHF, previous unhandled, or unwind begin notification
        BOOL sendNotification = TRUE;
        if(pThread->IsExceptionInProgress())
        {
            sendNotification = !pExState->GetFlags()->DebugCatchHandlerFound() &&
                               !pExState->GetFlags()->SentDebugUnhandled() &&
                               !pExState->GetFlags()->SentDebugUnwindBegin();
        }

        if(sendNotification)
        {
            hr = SendExceptionHelperAndBlock(
                pThread,
                g_pEEInterface->GetThreadException(pThread),
                fContinuable,
                LEAF_MOST_FRAME,
                (SIZE_T)ICorDebugInfo::NO_MAPPING,
                DEBUG_EXCEPTION_UNHANDLED,
                fIsInterceptable ? DEBUG_EXCEPTION_CAN_BE_INTERCEPTED : 0);

            if(pThread->IsExceptionInProgress())
            {
                pExState->GetFlags()->SetSentDebugUnhandled();
            }
        }

    } // end if (!firstChance)
}

//
// SendException is called by Runtime threads to send that they've hit an Managed exception to the Right Side.
// This may block this thread and suspend the debuggee, and let the debugger inspect us.
//
// The thread's throwable should be set so that the debugger can inspect the current exception.
// It does not report native exceptions in native code (which is consistent because those don't have a
// managed exception object).
//
// This may kick off a jit-attach (in which case fAttaching==true), and so may be called even when no debugger
// is yet involved.
//
// Parameters:
//    pThread - the thread throwing the exception.
//    fFirstChance - true if this is a first chance exception. False if this is an unhandled exception.
//    currentIP - absolute native address of the exception if it is from managed code. If this is 0, we try to find it
//                based off the thread's current exception state.
//    currentSP - stack pointer of the exception. This will get converted into a FramePointer and then used by the debugger
//                to identify which stack frame threw the exception.
//    currentBSP - additional information for IA64 only to identify the stack frame.
//    fContinuable - not used.
//    fAttaching - true iff this exception may initiate a jit-attach. In the common case, if this is true, then
//                 CorDebuggerAttached() is false. However, since a debugger can attach at any time, it's possible
//                 for another debugger to race against the jit-attach and win. Thus this may err on the side of being true.
//    fForceNonInterceptable - This is used to determine if the exception is continuable (ie "Interceptible",
//                  we can handle a DB_IPCE_INTERCEPT_EXCEPTION event for it). If true, then the exception can not be continued.
//                  If false, we get continuation status from the exception properties of the current thread.
//
// Returns:
//    S_OK on success (common case by far).
//    propagates other errors.
//
HRESULT Debugger::SendException(Thread *pThread,
                                bool fFirstChance,
                                SIZE_T currentIP,
                                SIZE_T currentSP,
                                bool fContinuable, // not used by RS.
                                bool fAttaching,
                                bool fForceNonInterceptable,
                                EXCEPTION_POINTERS * pExceptionInfo)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;

        MODE_ANY;

        PRECONDITION(HasLazyData());
        PRECONDITION(CheckPointer(pThread));
        PRECONDITION((pThread->GetFilterContext() == NULL) || !fFirstChance);
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "D::SendException\n"));

    if (CORDBUnrecoverableError(this))
    {
        return (E_FAIL);
    }

    // Mark if we're at an unsafe place.
    AtSafePlaceHolder unsafePlaceHolder(pThread);

    // Grab the exception name from the current exception object to pass to the JIT attach.
    bool fIsInterceptable;

    if (fForceNonInterceptable)
    {
        fIsInterceptable = false;
        m_forceNonInterceptable = true;
    }
    else
    {
        fIsInterceptable = IsInterceptableException(pThread);
        m_forceNonInterceptable = false;
    }

    ThreadExceptionState* pExState = pThread->GetExceptionState();
    BOOL managedEventNeeded = ((!fFirstChance) ||
            (fFirstChance && (!pExState->GetFlags()->SentDebugFirstChance() || !pExState->GetFlags()->SentDebugUserFirstChance())));

    // There must be a managed exception object to send a managed exception event
    if (g_pEEInterface->IsThreadExceptionNull(pThread) && (pThread->LastThrownObjectHandle() == NULL))
    {
        managedEventNeeded = FALSE;
    }

    if (fAttaching)
    {
        JitAttach(pThread, pExceptionInfo, managedEventNeeded, FALSE);
        // If the jit-attach occurred, CORDebuggerAttached() may now be true and we can
        // just act as if a debugger was always attached.
    }

    if(managedEventNeeded)
    {
        {
            // We have to send enabled, so enable now.
            GCX_PREEMP_EEINTERFACE();

            // Send the exception events. Even in jit-attach case, we should now be fully attached.
            if (CORDebuggerAttached())
            {
                // Initialize frame-pointer associated with exception notification.
                LPVOID stackPointer;
                if ((currentSP == 0) && (pExState->GetContextRecord() != NULL))
                {
                    stackPointer = dac_cast<PTR_VOID>(GetSP(pExState->GetContextRecord()));
                }
                else
                {
                    stackPointer = (LPVOID)currentSP;
                }
                FramePointer framePointer = FramePointer::MakeFramePointer(stackPointer);


                // Do the real work of sending the events
                SendExceptionEventsWorker(
                    pThread,
                    fFirstChance,
                    fIsInterceptable,
                    fContinuable,
                    currentIP,
                    framePointer,
                    !unsafePlaceHolder.IsAtUnsafePlace());
            }
            else
            {
                LOG((LF_CORDB,LL_INFO100, "D:SE: Skipping SendIPCEvent because not supposed to send anything, or RS detached.\n"));
            }
        }

        // If we weren't at a safe place when we switched to PREEMPTIVE, then go ahead and unmark that fact now
        // that we're successfully back in COOPERATIVE mode.
        unsafePlaceHolder.Clear();

        {
            GCX_COOP_EEINTERFACE();
            ProcessAnyPendingEvals(pThread);
        }
    }

    if (CORDebuggerAttached())
    {
        return S_FALSE;
    }
    else
    {
        return S_OK;
    }
}


/*
 * ProcessAnyPendingEvals
 *
 * This function checks for, and then processes, any pending func-evals.
 *
 * Parameters:
 *   pThread - The thread to process.
 *
 * Returns:
 *   None.
 *
 */
void Debugger::ProcessAnyPendingEvals(Thread *pThread)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_COOPERATIVE;
    }
    CONTRACTL_END;

#ifndef DACCESS_COMPILE

    // If no debugger is attached, then no evals to process.
    // We may get here in oom situations during jit-attach, so we'll check now and be safe.
    if (!CORDebuggerAttached())
    {
        return;
    }

    //
    // Note: if there is a filter context installed, we may need remove it, do the eval, then put it back. I'm not 100%
    // sure which yet... it kinda depends on whether or not we really need the filter context updated due to a
    // collection during the func eval...
    //
    // If we need to do a func eval on this thread, then there will be a pending eval registered for this thread. We'll
    // loop so long as there are pending evals registered. We block in FuncEvalHijackWorker after sending up the
    // FuncEvalComplete event, so if the user asks for another func eval then there will be a new pending eval when we
    // loop and check again.
    //
    DebuggerPendingFuncEval *pfe;

    while (GetPendingEvals() != NULL && (pfe = GetPendingEvals()->GetPendingEval(pThread)) != NULL)
    {
        DebuggerEval *pDE = pfe->pDE;

        _ASSERTE(pDE->m_evalDuringException);
        _ASSERTE(pDE->m_thread == GetThreadNULLOk());

        // Remove the pending eval from the hash. This ensures that if we take a first chance exception during the eval
        // that we can do another nested eval properly.
        GetPendingEvals()->RemovePendingEval(pThread);

        // Go ahead and do the pending func eval. pDE is invalid after this.
        void *ret;
        ret = ::FuncEvalHijackWorker(pDE);


        // The return value should be NULL when FuncEvalHijackWorker is called as part of an exception.
        _ASSERTE(ret == NULL);
    }

#endif

}


/*
 * FirstChanceManagedException is called by Runtime threads when crawling the managed stack frame
 * for a handler for the exception.  It is called for each managed call on the stack.
 *
 * Parameters:
 *   pThread - The thread the exception is occurring on.
 *   currentIP - the IP in the current stack frame.
 *   currentSP - the SP in the current stack frame.
 *
 * Returns:
 *   Always FALSE.
 *
 */
bool Debugger::FirstChanceManagedException(Thread *pThread, SIZE_T currentIP, SIZE_T currentSP)
{

    // @@@
    // Implement DebugInterface
    // Can only be called from EE/exception
    // must be on managed thread.

    CONTRACTL
    {
        THROWS;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;

        PRECONDITION(CORDebuggerAttached());
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "D::FCE: First chance exception, TID:0x%x, \n", GetThreadIdHelper(pThread)));

    _ASSERTE(GetThreadNULLOk() != NULL);

#ifdef _DEBUG
    static ConfigDWORD d_fce;
    if (d_fce.val(CLRConfig::INTERNAL_D__FCE))
        _ASSERTE(!"Stop in Debugger::FirstChanceManagedException?");
#endif

    SendException(pThread, TRUE, currentIP, currentSP, FALSE, FALSE, FALSE, NULL);

    return false;
}


/*
 * FirstChanceManagedExceptionCatcherFound is called by Runtime threads when crawling the
 * managed stack frame and a handler for the exception is found.
 *
 * Parameters:
 *   pThread - The thread the exception is occurring on.
 *   pTct - Contains the function information that has the catch clause.
 *   pEHClause - Contains the native offset information of the catch clause.
 *
 * Returns:
 *   None.
 *
 */
void Debugger::FirstChanceManagedExceptionCatcherFound(Thread *pThread,
                                                       MethodDesc *pMD, TADDR pMethodAddr,
                                                       BYTE *currentSP,
                                                       EE_ILEXCEPTION_CLAUSE *pEHClause)
{

    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS_FROM_GETJITINFO;
        MODE_ANY;
    }
    CONTRACTL_END;

    // @@@
    // Implements DebugInterface
    // Call by EE/exception. Must be on managed thread
    _ASSERTE(GetThreadNULLOk() != NULL);
    _ASSERTE(pMethodAddr != (TADDR)NULL);

    // Quick check.
    if (!CORDebuggerAttached())
    {
        return;
    }

    // Compute the offset

    DWORD nOffset = (DWORD)(SIZE_T)ICorDebugInfo::NO_MAPPING;
    DebuggerMethodInfo *pDebugMethodInfo = NULL;
    DebuggerJitInfo *pDebugJitInfo = NULL;
    bool isInJMCFunction = false;

    if (pMD != NULL)
    {
        _ASSERTE(!pMD->IsILStub());

        pDebugJitInfo = GetJitInfo(pMD, (const BYTE *) pMethodAddr, &pDebugMethodInfo);
        if (pDebugMethodInfo != NULL)
        {
            isInJMCFunction = pDebugMethodInfo->IsJMCFunction();
        }
    }

    BOOL forceSendCatchHandlerFound = FALSE;
    {
        GCX_COOP_EEINTERFACE();
        forceSendCatchHandlerFound = ShouldSendCatchHandlerFound(pThread);
    }
    // Here we check if debugger opted-out of receiving exception related events from outside of JMC methods
    // or this exception ever crossed JMC frame (in this case we have already sent user first chance event)
    if (isInJMCFunction || forceSendCatchHandlerFound)
    {
        if (pDebugJitInfo != NULL)
        {
            CorDebugMappingResult mapResult;
            DWORD which;

            // Map the native instruction to the IL instruction.
            // Be sure to skip past the prolog on amd64/arm to get the right IL
            // instruction (on x86 there will not be a prolog as x86 does not use
            // funclets).
            nOffset = pDebugJitInfo->MapNativeOffsetToIL(
                pEHClause->HandlerStartPC,
                &mapResult,
                &which,
                TRUE
                );
        }

        bool fIsInterceptable = IsInterceptableException(pThread);
        m_forceNonInterceptable = false;
        DWORD dwFlags = fIsInterceptable ? DEBUG_EXCEPTION_CAN_BE_INTERCEPTED : 0;

        FramePointer fp = FramePointer::MakeFramePointer(currentSP);
        SendCatchHandlerFound(pThread, fp, nOffset, dwFlags);
    }

    // flag that we catch handler found so that we won't send other mutually exclusive events
    // such as unwind begin or unhandled
    pThread->GetExceptionState()->GetFlags()->SetDebugCatchHandlerFound();
}

// Filter to trigger CHF callback
// Notify of a catch-handler found callback.
LONG Debugger::NotifyOfCHFFilter(EXCEPTION_POINTERS* pExceptionPointers, PVOID pData)
{
    CONTRACTL
    {
        if ((GetThreadNULLOk() == NULL) || g_pEEInterface->IsThreadExceptionNull(GetThread()))
        {
            NOTHROW;
            GC_NOTRIGGER;
        }
        else
        {
            THROWS;
            MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
        }
        MODE_ANY;
    }
    CONTRACTL_END;

    SCAN_IGNORE_TRIGGER; // Scan can't handle conditional contracts.

    // @@@
    // Implements DebugInterface
    // Can only be called from EE

    // If no debugger is attached, then don't bother sending the events.
    // This can't kick off a jit-attach.
    if (!CORDebuggerAttached())
    {
        return EXCEPTION_CONTINUE_SEARCH;
    }

    //
    // If this exception has never bubbled thru to managed code, then there is no
    // useful information for the debugger and, in fact, it may be a completely
    // internally handled runtime exception, so we should do nothing.
    //
    if ((GetThreadNULLOk() == NULL) || g_pEEInterface->IsThreadExceptionNull(GetThread()))
    {
        return EXCEPTION_CONTINUE_SEARCH;
    }

    // Caller must pass in the stack address. This should match up w/ a Frame.
    BYTE * pCatcherStackAddr = (BYTE*) pData;

    // If we don't have any catcher frame, then use ebp from the context.
    if (pData == NULL)
    {
        pCatcherStackAddr = (BYTE*) GetFP(pExceptionPointers->ContextRecord);
    }
    else
    {
#ifdef _DEBUG
        _ASSERTE(pData != NULL);
        {
            // We want the CHF stack addr to match w/ the Internal Frame Cordbg sees
            // in the stacktrace.
            // The Internal Frame comes from an EE Frame. This means that the CHF stack
            // addr must match that EE Frame exactly. Let's check that now.

            Frame * pFrame = reinterpret_cast<Frame*>(pData);
            // Calling a virtual method will enforce that we have a valid Frame. ;)
            // If we got passed in a random catch address, then when we cast to a Frame
            // the vtable pointer will be bogus and this call will AV.
            Frame::ETransitionType e;
            e = pFrame->GetTransitionType();
        }
#endif
    }

    // Stubs don't have an IL offset.
    const SIZE_T offset = (SIZE_T)ICorDebugInfo::NO_MAPPING;
    Thread *pThread = GetThread();
    DWORD dwFlags = IsInterceptableException(pThread) ? DEBUG_EXCEPTION_CAN_BE_INTERCEPTED : 0;
    m_forceNonInterceptable = false;

    FramePointer fp = FramePointer::MakeFramePointer(pCatcherStackAddr);

    //
    // If we have not sent a first-chance notification, do so now.
    //
    ThreadExceptionState* pExState = pThread->GetExceptionState();

    if (!pExState->GetFlags()->SentDebugFirstChance())
    {
        SendException(pThread,
                      TRUE, // first-chance
                      (SIZE_T)(GetIP(pExceptionPointers->ContextRecord)), // IP
                      (SIZE_T)pCatcherStackAddr, // SP
                      FALSE, // fContinuable
                      FALSE, // attaching
                      TRUE,  // ForceNonInterceptable since we are transition stub, the first and last place
                             // that will see this exception.
                      pExceptionPointers);
    }

    BOOL forceSendCatchHandlerFound = FALSE;
    {
        GCX_COOP_EEINTERFACE();
        forceSendCatchHandlerFound = ShouldSendCatchHandlerFound(pThread);
    }

    // Here we check if debugger opted-out of receiving exception related events from outside of JMC methods
    // or this exception ever crossed JMC frame (in this case we have already sent user first chance event)
    if (forceSendCatchHandlerFound)
    {
        SendCatchHandlerFound(pThread, fp, offset, dwFlags);
    }

    // flag that we catch handler found so that we won't send other mutually exclusive events
    // such as unwind begin or unhandled
    pExState->GetFlags()->SetDebugCatchHandlerFound();

#ifdef DEBUGGING_SUPPORTED
#ifdef DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED
    if ( (pThread != NULL) &&
         (pThread->IsExceptionInProgress()) &&
         (pThread->GetExceptionState()->GetFlags()->DebuggerInterceptInfo()) )
    {
        //
        // The debugger wants to intercept this exception.  It may return in a failure case,
        // in which case we want to continue thru this path.
        //
        ClrDebuggerDoUnwindAndIntercept(X86_FIRST_ARG(EXCEPTION_CHAIN_END) pExceptionPointers->ExceptionRecord);
    }
#endif // DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED
#endif // DEBUGGING_SUPPORTED

    return EXCEPTION_CONTINUE_SEARCH;
}

BOOL Debugger::ShouldSendCatchHandlerFound(Thread* pThread)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        MODE_COOPERATIVE;
    }
    CONTRACTL_END;

    ThreadExceptionState* pExState = pThread->GetExceptionState();
    if (m_sendExceptionsOutsideOfJMC || pExState->GetFlags()->SentDebugUserFirstChance())
    {
        return TRUE;
    }
    else
    {
        BOOL forceSendCatchHandlerFound = FALSE;
        OBJECTHANDLE objHandle = pThread->GetThrowableAsHandle();
        OBJECTHANDLE retrievedHandle = m_pForceCatchHandlerFoundEventsTable->Lookup(objHandle); //destroy handle
        if (retrievedHandle != NULL)
        {
            forceSendCatchHandlerFound = TRUE;
        }
        return forceSendCatchHandlerFound;
    }
}

BOOL Debugger::ShouldSendCustomNotification(DomainAssembly *pAssembly, mdTypeDef typeDef)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    Module *pModule = pAssembly->GetModule();
    TypeInModule tim(pModule, typeDef);
    return !(m_pCustomNotificationTable->Lookup(tim).IsNull());
}

// Actually send the catch handler found event.
// This can be used to send CHF for both regular managed catchers as well
// as stubs that catch (Func-eval, COM-Interop, AppDomains)
void Debugger::SendCatchHandlerFound(
    Thread * pThread,
    FramePointer fp,
    SIZE_T   nOffset,
    DWORD    dwFlags
)
{

    CONTRACTL
    {
        THROWS;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
        MODE_ANY;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "D::FirstChanceManagedExceptionCatcherFound\n"));

    if (pThread == NULL)
    {
        _ASSERTE(!"Bad parameter");
        LOG((LF_CORDB, LL_INFO10000, "D::FirstChanceManagedExceptionCatcherFound - Bad parameter.\n"));
        return;
    }

    if (CORDBUnrecoverableError(this))
    {
        return;
    }

    //
    // Mark if we're at an unsafe place.
    //
    AtSafePlaceHolder unsafePlaceHolder(pThread);

    {
        GCX_COOP_EEINTERFACE();

        {
            SENDIPCEVENT_BEGIN(this, pThread);

            if (CORDebuggerAttached() &&
                !pThread->GetExceptionState()->GetFlags()->DebugCatchHandlerFound() &&
                !pThread->GetExceptionState()->GetFlags()->SentDebugUnhandled() &&
                !pThread->GetExceptionState()->GetFlags()->SentDebugUnwindBegin())
            {
                HRESULT hr;

                //
                // Figure out parameters to the IPC events.
                //
                DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();

                //
                // Send Whidbey EXCEPTION IPC event.
                //
                InitIPCEvent(ipce, DB_IPCE_EXCEPTION_CALLBACK2, pThread, AppDomain::GetCurrentDomain());

                ipce->ExceptionCallback2.framePointer = fp;
                ipce->ExceptionCallback2.eventType = DEBUG_EXCEPTION_CATCH_HANDLER_FOUND;
                ipce->ExceptionCallback2.nOffset = nOffset;
                ipce->ExceptionCallback2.dwFlags = dwFlags;
                ipce->ExceptionCallback2.vmExceptionHandle.SetRawPtr(g_pEEInterface->GetThreadException(pThread));

                LOG((LF_CORDB, LL_INFO10000, "D::FCMECF: sending ExceptionCallback2"));
                hr = m_pRCThread->SendIPCEvent();

                _ASSERTE(SUCCEEDED(hr) && "D::FCMECF: Send ExceptionCallback2 event failed.");

                //
                // Stop all Runtime threads
                //
                TrapAllRuntimeThreads();

            } // end if (!Attached)
            else
            {
                LOG((LF_CORDB,LL_INFO1000, "D:FCMECF: Skipping SendIPCEvent because RS detached.\n"));
            }

            //
            // Let other Runtime threads handle their events.
            //
            SENDIPCEVENT_END;
        }

        //
        // If we weren't at a safe place when we enabled PGC, then go ahead and unmark that fact now that we've successfully
        // disabled.
        //
        unsafePlaceHolder.Clear();

        ProcessAnyPendingEvals(pThread);
    } // end of GCX_COOP_EEINTERFACE();

    return;
}

/*
 * ManagedExceptionUnwindBegin is called by Runtime threads when crawling the
 * managed stack frame and unwinding them.
 *
 * Parameters:
 *   pThread - The thread the unwind is occurring on.
 *
 * Returns:
 *   None.
 *
 */
void Debugger::ManagedExceptionUnwindBegin(Thread *pThread)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    // @@@
    // Implements DebugInterface
    // Can only be called on managed threads
    //

    LOG((LF_CORDB, LL_INFO10000, "D::ManagedExceptionUnwindBegin\n"));

    if (pThread == NULL)
    {
        _ASSERTE(!"Bad parameter");
        LOG((LF_CORDB, LL_INFO10000, "D::ManagedExceptionUnwindBegin - Bad parameter.\n"));
        return;
    }

    if (CORDBUnrecoverableError(this))
    {
        return;
    }

    //
    // Mark if we're at an unsafe place.
    //
    AtSafePlaceHolder unsafePlaceHolder(pThread);
    {
        GCX_COOP_EEINTERFACE();

        {
            SENDIPCEVENT_BEGIN(this, pThread);

            if (CORDebuggerAttached() &&
                !pThread->GetExceptionState()->GetFlags()->SentDebugUnwindBegin() &&
                !pThread->GetExceptionState()->GetFlags()->DebugCatchHandlerFound() &&
                !pThread->GetExceptionState()->GetFlags()->SentDebugUnhandled())
            {
                HRESULT hr;

                DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();

                //
                // Send Whidbey EXCEPTION IPC event.
                //
                InitIPCEvent(ipce, DB_IPCE_EXCEPTION_UNWIND, pThread, AppDomain::GetCurrentDomain());

                ipce->ExceptionUnwind.eventType = DEBUG_EXCEPTION_UNWIND_BEGIN;
                ipce->ExceptionUnwind.dwFlags = 0;

                        LOG((LF_CORDB, LL_INFO10000, "D::MEUB: sending ExceptionUnwind event"));
                        hr = m_pRCThread->SendIPCEvent();

                _ASSERTE(SUCCEEDED(hr) && "D::MEUB: Send ExceptionUnwind event failed.");

                pThread->GetExceptionState()->GetFlags()->SetSentDebugUnwindBegin();

                //
                // Stop all Runtime threads
                //
                TrapAllRuntimeThreads();

            } // end if (!Attached)

            //
            // Let other Runtime threads handle their events.
            //
            SENDIPCEVENT_END;
        }

    //
    // If we weren't at a safe place when we enabled PGC, then go ahead and unmark that fact now that we've successfully
    // disabled.
    //
        unsafePlaceHolder.Clear();
    }

    return;
}

/*
 * DeleteInterceptContext
 *
 * This function is called by the VM to release any debugger specific information for an
 * exception object.  It is called when the VM releases its internal exception stuff, i.e.
 * ExInfo on X86 and ExceptionTracker on WIN64.
 *
 *
 * Parameters:
 *   pContext - Debugger specific context.
 *
 * Returns:
 *   None.
 *
 * Notes:
 *   pContext is just a pointer to a DebuggerContinuableExceptionBreakpoint.
 *
 */
void Debugger::DeleteInterceptContext(void *pContext)
{
    LIMITED_METHOD_CONTRACT;

    DebuggerContinuableExceptionBreakpoint *pBp = (DebuggerContinuableExceptionBreakpoint *)pContext;

    if (pBp != NULL)
    {
        DeleteInteropSafe(pBp);
    }
}


// Get the frame point for an exception handler
FramePointer GetHandlerFramePointer(BYTE *pStack)
{
    FramePointer handlerFP;

#if !defined(TARGET_ARM) && !defined(TARGET_ARM64)
    // Refer to the comment in DispatchUnwind() to see why we have to add
    // sizeof(LPVOID) to the handler ebp.
    handlerFP = FramePointer::MakeFramePointer(LPVOID(pStack + sizeof(void*)));
#else
    // ARM is similar to IA64 in that it uses the establisher frame as the
    // handler. in this case we don't need to add sizeof(void*) to the FP.
    handlerFP = FramePointer::MakeFramePointer((LPVOID)pStack);
#endif // TARGET_ARM

    return handlerFP;
}

//
// ExceptionFilter is called by the Runtime threads when an exception
// is being processed.
// - fd - MethodDesc of filter function
// - pMethodAddr - any address inside of the method. This lets us resolve exactly which version
//                 of the method is being executed (for EnC)
// - offset - native offset to handler.
// - pStack, pBStore - stack pointers.
//
void Debugger::ExceptionFilter(MethodDesc *fd, TADDR pMethodAddr, SIZE_T offset, BYTE *pStack)
{
    CONTRACTL
    {
        MODE_COOPERATIVE;
        NOTHROW;
        GC_NOTRIGGER;

        PRECONDITION(!IsDbgHelperSpecialThread());
    }
    CONTRACTL_END;

    LOG((LF_CORDB,LL_INFO10000, "D::EF: pStack:0x%x MD: %s::%s, offset:0x%x\n",
        pStack, fd->m_pszDebugClassName, fd->m_pszDebugMethodName, offset));

    //
    // !!! Need to think through logic for when to step through filter code -
    // perhaps only during a "step in".
    //

    //
    // !!! Eventually there may be some weird mechanics introduced for
    // returning from the filter that we have to understand.  For now we should
    // be able to proceed normally.
    //

    FramePointer handlerFP;
    handlerFP = GetHandlerFramePointer(pStack);

    DebuggerJitInfo * pDJI = NULL;
    EX_TRY
    {
        pDJI = GetJitInfo(fd, (const BYTE *) pMethodAddr);
    }
    EX_CATCH
    {
    }
    EX_END_CATCH(SwallowAllExceptions);

    if (!fd->IsDynamicMethod() && (pDJI == NULL))
    {
        // The only way we shouldn't have a DJI is from a dynamic method or from oom (which the LS doesn't handle).
        _ASSERTE(!"Debugger doesn't support OOM scenarios.");
        return;
    }

    DebuggerController::DispatchUnwind(g_pEEInterface->GetThread(),
                                       fd, pDJI, offset, handlerFP, STEP_EXCEPTION_FILTER);
}


//
// ExceptionHandle is called by Runtime threads when an exception is
// being handled.
// - fd - MethodDesc of filter function
// - pMethodAddr - any address inside of the method. This lets us resolve exactly which version
//                 of the method is being executed (for EnC)
// - offset - native offset to handler.
// - pStack, pBStore - stack pointers.
//
void Debugger::ExceptionHandle(MethodDesc *fd, TADDR pMethodAddr, SIZE_T offset, BYTE *pStack)
{
    CONTRACTL
    {
        MODE_COOPERATIVE;
        NOTHROW;
        GC_NOTRIGGER;

        PRECONDITION(!IsDbgHelperSpecialThread());
    }
    CONTRACTL_END;


    FramePointer handlerFP;
    handlerFP = GetHandlerFramePointer(pStack);

    DebuggerJitInfo * pDJI = NULL;
    EX_TRY
    {
        pDJI = GetJitInfo(fd, (const BYTE *) pMethodAddr);
    }
    EX_CATCH
    {
    }
    EX_END_CATCH(SwallowAllExceptions);

    if (!fd->IsDynamicMethod() && (pDJI == NULL))
    {
        // The only way we shouldn't have a DJI is from a dynamic method or from oom (which the LS doesn't handle).
        _ASSERTE(!"Debugger doesn't support OOM scenarios.");
        return;
    }


    DebuggerController::DispatchUnwind(g_pEEInterface->GetThread(),
                                       fd, pDJI, offset, handlerFP, STEP_EXCEPTION_HANDLER);
}

BOOL Debugger::ShouldAutoAttach()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    _ASSERTE(!CORDebuggerAttached());

    // We're relying on the caller to determine the

    LOG((LF_CORDB, LL_INFO1000000, "D::SAD\n"));

    // Check if the user has specified a setting in the registry about what he
    // wants done when an unhandled exception occurs.
    DebuggerLaunchSetting dls = GetDbgJITDebugLaunchSetting();

    return (dls == DLS_ATTACH_DEBUGGER);

    // @TODO cache the debugger launch setting.

}

BOOL Debugger::FallbackJITAttachPrompt()
{
    _ASSERTE(!CORDebuggerAttached());
    return (ATTACH_YES == this->ShouldAttachDebuggerProxy(false));
}

void Debugger::MarkDebuggerAttachedInternal()
{
    LIMITED_METHOD_CONTRACT;

    // Attach is complete now.
    LOG((LF_CORDB, LL_INFO10000, "D::FEDA: Attach Complete!\n"));
    g_pEEInterface->MarkDebuggerAttached();

    _ASSERTE(HasLazyData());
}
void Debugger::MarkDebuggerUnattachedInternal()
{
    LIMITED_METHOD_CONTRACT;

    _ASSERTE(HasLazyData());

    g_pEEInterface->MarkDebuggerUnattached();
}

//-----------------------------------------------------------------------------
// Favor to do lazy initialization on helper thread.
// This is needed to allow lazy initialization in Stack Overflow scenarios.
// We may or may not already be initialized.
//-----------------------------------------------------------------------------
void LazyInitFavor(void *)
{
    CONTRACTL
    {
        NOTHROW;
        MODE_ANY;
    }
    CONTRACTL_END;
    Debugger::DebuggerLockHolder dbgLockHolder(g_pDebugger);
    HRESULT hr;
    hr = g_pDebugger->LazyInitWrapper();
    (void)hr; //prevent "unused variable" error from GCC

    // On checked builds, warn that we're hitting a scenario that debugging doesn't support.
    _ASSERTE(SUCCEEDED(hr) || !"Couldn't initialize lazy data for LastChanceManagedException");
}

/******************************************************************************
 *
 ******************************************************************************/
LONG Debugger::LastChanceManagedException(EXCEPTION_POINTERS * pExceptionInfo,
                                          Thread *pThread,
                                          BOOL jitAttachRequested)
{
    CONTRACTL
    {
        NOTHROW;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
        MODE_ANY;
    }
    CONTRACTL_END;

    // @@@
    // Implements DebugInterface.
    // Can be run only on managed thread.

    LOG((LF_CORDB, LL_INFO10000, "D::LastChanceManagedException\n"));

    // Don't stop for native debugging anywhere inside our inproc-Filters.
    CantStopHolder hHolder;

    EXCEPTION_RECORD * pExceptionRecord = pExceptionInfo->ExceptionRecord;
    CONTEXT * pContext = pExceptionInfo->ContextRecord;

    // You're allowed to call this function with a NULL exception record and context. If you do, then its assumed
    // that we want to head right down to asking the user if they want to attach a debugger. No need to try to
    // dispatch the exception to the debugger controllers. You have to pass NULL for both the exception record and
    // the context, though. They're a pair. Both have to be NULL, or both have to be valid.
    _ASSERTE(((pExceptionRecord != NULL) && (pContext != NULL)) ||
             ((pExceptionRecord == NULL) && (pContext == NULL)));

    if (CORDBUnrecoverableError(this))
    {
        return ExceptionContinueSearch;
    }

    // We don't do anything on the second pass
    if ((pExceptionRecord != NULL) && ((pExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING) != 0))
    {
        return ExceptionContinueSearch;
    }

    // Let the controllers have a chance at it - this may be the only handler which can catch the exception if this
    // is a native patch.

    if ((pThread != NULL) &&
        (pContext != NULL) &&
        CORDebuggerAttached() &&
        DebuggerController::DispatchNativeException(pExceptionRecord,
                                                    pContext,
                                                    pExceptionRecord->ExceptionCode,
                                                    pThread))
    {
        return ExceptionContinueExecution;
    }

    // Otherwise, run our last chance exception logic
    ATTACH_ACTION action;
    action = ATTACH_NO;

    if (CORDebuggerAttached() || jitAttachRequested)
    {
        LOG((LF_CORDB, LL_INFO10000, "D::BEH ... debugger attached.\n"));

        Thread *thread = g_pEEInterface->GetThread();
        _ASSERTE((thread != NULL) && (thread == pThread));

        // ExceptionFlags is 0 for continuable, EXCEPTION_NONCONTINUABLE otherwise. Note that if we don't have an
        // exception record, then we assume this is a non-continuable exception.
        bool continuable = (pExceptionRecord != NULL) && (pExceptionRecord->ExceptionFlags == 0);

        LOG((LF_CORDB, LL_INFO10000, "D::BEH ... sending exception.\n"));

        HRESULT hr = E_FAIL;

        // In the jit-attach case, lazy-init. We may be in a stack-overflow, so do it via a favor to avoid
        // using this thread's stack space.
        if (jitAttachRequested)
        {
            m_pRCThread->DoFavor((FAVORCALLBACK) LazyInitFavor, NULL);
        }

        // The only way we don't have lazy data at this point is in an OOM scenario, which
        // the debugger doesn't support.
        if (!HasLazyData())
        {
            return ExceptionContinueSearch;
        }


        // In Whidbey, we used to set the filter CONTEXT when we hit an unhandled exception while doing
        // mixed-mode debugging.  This helps the debugger walk the stack since it can skip the leaf
        // portion of the stack (including stack frames in the runtime) and start the stackwalk at the
        // faulting stack frame.  The code to set the filter CONTEXT is in a hijack function which is only
        // used during mixed-mode debugging.
        if (m_pRCThread->GetDCB()->m_rightSideIsWin32Debugger)
        {
            GCX_COOP();

            _ASSERTE(thread->GetFilterContext() == NULL);
            thread->SetFilterContext(pExceptionInfo->ContextRecord);
        }
        EX_TRY
        {
            // We pass the attaching status to SendException so that it knows
            // whether to attach a debugger or not. We should really do the
            // attach stuff out here and not bother with the flag.
            hr = SendException(thread,
                          FALSE,
                          ((pContext != NULL) ? (SIZE_T)GetIP(pContext) : 0),
                          ((pContext != NULL) ? (SIZE_T)GetSP(pContext) : 0),
                          continuable,
                          !!jitAttachRequested,  // If we are JIT attaching on an unhandled exceptioin, we force
                          !!jitAttachRequested,  // the exception to be uninterceptable.
                          pExceptionInfo);
        }
        EX_CATCH
        {
        }
        EX_END_CATCH(SwallowAllExceptions);
        if (m_pRCThread->GetDCB()->m_rightSideIsWin32Debugger)
        {
            GCX_COOP();

            thread->SetFilterContext(NULL);
        }
    }
    else
    {
        // Note: we don't do anything on NO or TERMINATE. We just return to the exception logic, which will abort the
        // app or not depending on what the CLR impl decides is appropriate.
        _ASSERTE(action == ATTACH_TERMINATE || action == ATTACH_NO);
    }

    return ExceptionContinueSearch;
}

// Forward declaration
int NotifyUserOfFaultMessageBox(
    LPCWSTR title,       // Title
    LPCWSTR message,     // Text message
    UINT uType);        // Style of MessageBox

//
// NotifyUserOfFault notifies the user of a fault (unhandled exception
// or user breakpoint) in the process, giving them the option to
// attach a debugger or terminate the application.
//
int Debugger::NotifyUserOfFault(bool userBreakpoint, DebuggerLaunchSetting dls)
{
    LOG((LF_CORDB, LL_INFO1000000, "D::NotifyUserOfFault\n"));

    CONTRACTL
    {
        NOTHROW;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;;
        MODE_PREEMPTIVE;
    }
    CONTRACTL_END;

    int result = IDCANCEL;

    if (!CORDebuggerAttached())
    {
        DWORD pid;
        DWORD tid;

        pid = GetCurrentProcessId();
        tid = GetCurrentThreadId();

        DWORD flags = 0;

        CHAR const* msg;
        if (userBreakpoint)
        {
            msg = "Application has encountered a user-defined breakpoint.\n\nProcess ID=0x%x (%d), Thread ID=0x%x (%d).\n\nClick ABORT to terminate the application.\nClick RETRY to debug the application.\nClick IGNORE to ignore the breakpoint.";
            flags |= MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION;
        }
        else
        {
            msg = "Application has generated an exception that could not be handled.\n\nProcess ID=0x%x (%d), Thread ID=0x%x (%d).\n\nClick OK to terminate the application.\nClick CANCEL to debug the application.";
            flags |= MB_OKCANCEL | MB_ICONEXCLAMATION;
        }

        // Format message string using optional parameters
        StackSString formattedMessage;
        formattedMessage.Printf(msg, pid, pid, tid, tid);

        {
            // Another potential hang. This may get run on the helper if we have a stack overflow.
            // Hopefully the odds of 1 thread hitting a stack overflow while another is stuck holding the heap
            // lock is very small.
            SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;

            result = NotifyUserOfFaultMessageBox(W("Application Error"), (LPWSTR)formattedMessage.GetUnicode(), flags);
        }
    }

    LOG((LF_CORDB, LL_INFO1000000, "D::NotifyUserOfFault left\n"));
    return result;
}


// Proxy for ShouldAttachDebugger
struct ShouldAttachDebuggerParams {
    Debugger*                   m_pThis;
    bool                        m_fIsUserBreakpoint;
    Debugger::ATTACH_ACTION     m_retval;
};

// This is called by the helper thread
void ShouldAttachDebuggerStub(ShouldAttachDebuggerParams * p)
{
    WRAPPER_NO_CONTRACT;

    p->m_retval = p->m_pThis->ShouldAttachDebugger(p->m_fIsUserBreakpoint);
}

// This gets called just like the normal version, but it sends the call over to the helper thread
Debugger::ATTACH_ACTION Debugger::ShouldAttachDebuggerProxy(bool fIsUserBreakpoint)
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
    }
    CONTRACTL_END;

    if (!HasLazyData())
    {
        DebuggerLockHolder lockHolder(this);
        HRESULT hr = LazyInitWrapper();
        if (FAILED(hr))
        {
            // We already stress logged this case.
            return ATTACH_NO;
        }
    }


    if (!IsGuardPageGone())
        return ShouldAttachDebugger(fIsUserBreakpoint);

    ShouldAttachDebuggerParams p;
    p.m_pThis = this;
    p.m_fIsUserBreakpoint = fIsUserBreakpoint;

    LOG((LF_CORDB, LL_INFO1000000, "D::SADProxy\n"));
    m_pRCThread->DoFavor((FAVORCALLBACK) ShouldAttachDebuggerStub, &p);
    LOG((LF_CORDB, LL_INFO1000000, "D::SADProxy return %d\n", p.m_retval));

    return p.m_retval;
}

//---------------------------------------------------------------------------------------
// Do policy to determine if we should attach a debugger.
//
// Arguments:
//    fIsUserBreakpoint - true iff this is in response to a user-breakpoint, else false.
//
// Returns:
//    Action to perform based off policy.
//    ATTACH_NO if a debugger is already attached.
Debugger::ATTACH_ACTION Debugger::ShouldAttachDebugger(bool fIsUserBreakpoint)
{
    CONTRACTL
    {
        NOTHROW;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
        MODE_ANY;
    }
    CONTRACTL_END;


    LOG((LF_CORDB, LL_INFO1000000, "D::SAD\n"));

    // If the debugger is already attached, not necessary to re-attach
    if (CORDebuggerAttached())
    {
        return ATTACH_NO;
    }

    // Check if the user has specified a setting in the registry about what he wants done when an unhandled exception
    // occurs.
    DebuggerLaunchSetting dls = GetDbgJITDebugLaunchSetting();


    if (dls == DLS_ATTACH_DEBUGGER)
    {
        return ATTACH_YES;
    }
    else
    {
        // Only ask the user once if they wish to attach a debugger.  This is because LastChanceManagedException can be called
        // twice, which causes ShouldAttachDebugger to be called twice, which causes the user to have to answer twice.
        static BOOL s_fHasAlreadyAsked = FALSE;
        static ATTACH_ACTION s_action;


        // This lock is also part of the above workaround.
        // Must go to preemptive to take this lock since we'll trigger down the road.
        GCX_PREEMP();
        DebuggerLockHolder lockHolder(this);

        // We always want to ask about user breakpoints!
        if (!s_fHasAlreadyAsked || fIsUserBreakpoint)
        {
            if (!fIsUserBreakpoint)
                s_fHasAlreadyAsked = TRUE;

            // While we could theoretically run into a deadlock if another thread
            // which acquires the debugger lock in cooperative GC mode is blocked
            // on this thread while it is running arbitrary user code out of the
            // MessageBox message pump, given that this codepath will only be used
            // on Win9x and that the chances of this happenning are quite slim,
            // for Whidbey a GCViolation is acceptable.
            CONTRACT_VIOLATION(GCViolation);

            // Ask the user if they want to attach
            int iRes = NotifyUserOfFault(fIsUserBreakpoint, dls);

            // If it's a user-defined breakpoint, they must hit Retry to launch
            // the debugger.  If it's an unhandled exception, user must press
            // Cancel to attach the debugger.
            if ((iRes == IDCANCEL) || (iRes == IDRETRY))
                s_action = ATTACH_YES;

            else if ((iRes == IDABORT) || (iRes == IDOK))
                s_action = ATTACH_TERMINATE;

            else
                s_action = ATTACH_NO;
        }

        // dbgLockHolder goes out of scope - implicit Release
        return s_action;
    }
}


//---------------------------------------------------------------------------------------
// SendUserBreakpoint is called by Runtime threads to send that they've hit
// a user breakpoint to the Right Side.
//
// Parameters:
//    thread - managed thread that the breakpoint is on
//
// Notes:
//    A user breakpoint is generally triggered by a call to System.Diagnostics.Debugger.Break.
//    This can be very common. VB's 'stop' statement compiles to a Debugger.Break call.
//    Some other CLR facilities (MDAs) may call this directly too.
//
//    This may trigger a Jit attach.
//    If the debugger is already attached, this will issue a step-out so that the UserBreakpoint
//    appears to come from the callsite.
void Debugger::SendUserBreakpoint(Thread * thread)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_ANY;

        PRECONDITION(thread != NULL);
        PRECONDITION(thread == ::GetThreadNULLOk());
    }
    CONTRACTL_END;


#ifdef _DEBUG
    // For testing Watson, we want a consistent way to be able to generate a
    // Fatal Execution Error
    // So we have a debug-only knob in this particular managed call that can be used
    // to artificially inject the error.
    // This is only for testing.
    static int fDbgInjectFEE = -1;

    if (fDbgInjectFEE == -1)
        fDbgInjectFEE = UnsafeGetConfigDWORD(CLRConfig::INTERNAL_DbgInjectFEE);

    if (fDbgInjectFEE)
    {
        STRESS_LOG0(LF_CORDB, LL_INFO10000, "Debugger posting bogus FEE b/c knob DbgInjectFEE is set.\n");
        EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
        // These never return.
    }
#endif

    if (CORDBUnrecoverableError(this))
    {
        return;
    }

    // UserBreakpoint behaves differently if we're under a debugger vs. a jit-attach.
    // If we're under the debugger, it does an additional step-out to get us back to the call site.

    // If already attached, then do a step-out and send the userbreak event.
    if (CORDebuggerAttached())
    {
        // A debugger is already attached, so setup a DebuggerUserBreakpoint controller to get us out of the helper
        // that got us here. The DebuggerUserBreakpoint will call AttachDebuggerForBreakpoint for us when we're out
        // of the helper. The controller will delete itself when its done its work.
        DebuggerUserBreakpoint::HandleDebugBreak(thread);
        return;
     }

    ATTACH_ACTION dbgAction = ShouldAttachDebugger(true);

    // No debugger is attached. Consider a JIT attach.
    // This will do ShouldAttachDebugger() and wait for the results.
    // - It may terminate if the user requested that.
    // - It may do a full jit-attach.
    if (dbgAction == ATTACH_YES)
    {
        JitAttach(thread, NULL, TRUE, FALSE);
    }
    else if (dbgAction == ATTACH_TERMINATE)
    {
        // ATTACH_TERMINATE indicates the user wants to terminate the app.
        LOG((LF_CORDB, LL_INFO10000, "D::SUB: terminating this process due to user request\n"));

        // Should this go through the host?
        TerminateProcess(GetCurrentProcess(), 0);
        _ASSERTE(!"Should never reach this point.");
    }
    else
    {
        _ASSERTE(dbgAction == ATTACH_NO);
    }

    if (CORDebuggerAttached())
    {
        // On jit-attach, we just send the UserBreak event. Don't do an extra step-out.
        SendUserBreakpointAndSynchronize(thread);
    }
    else if (IsDebuggerPresent())
    {
        DebugBreak();
    }
}


// void Debugger::ThreadCreated():  ThreadCreated is called when
// a new Runtime thread has been created, but before its ever seen
// managed code.  This is a callback invoked by the EE into the Debugger.
// This will create a DebuggerThreadStarter patch, which will set
// a patch at the first instruction in the managed code.  When we hit
// that patch, the DebuggerThreadStarter will invoke ThreadStarted, below.
//
// Thread* pRuntimeThread:  The EE Thread object representing the
//      runtime thread that has just been created.
void Debugger::ThreadCreated(Thread* pRuntimeThread)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // @@@
    // This function implements the DebugInterface. But it is also called from Attach
    // logic internally.
    //

    if (CORDBUnrecoverableError(this))
        return;

    LOG((LF_CORDB, LL_INFO100, "D::TC: thread created for 0x%x. ******\n",
         GetThreadIdHelper(pRuntimeThread)));

    // Sanity check the thread.
    _ASSERTE(pRuntimeThread != NULL);
    _ASSERTE(pRuntimeThread->GetThreadId() != 0);


    // Create a thread starter and enable its WillEnterManaged code
    // callback. This will cause the starter to trigger once the
    // thread has hit managed code, which will cause
    // Debugger::ThreadStarted() to be called.  NOTE: the starter will
    // be deleted automatically when its done its work.
    DebuggerThreadStarter *starter = new (interopsafe, nothrow) DebuggerThreadStarter(pRuntimeThread);

    if (starter == NULL)
    {
        CORDBDebuggerSetUnrecoverableWin32Error(this, 0, false);
        return;
    }

    starter->EnableTraceCall(LEAF_MOST_FRAME);
}


// void Debugger::ThreadStarted():  ThreadStarted is called when
// a new Runtime thread has reached its first managed code. This is
// called by the DebuggerThreadStarter patch's SendEvent method.
//
// Thread* pRuntimeThread:  The EE Thread object representing the
//      runtime thread that has just hit managed code.
void Debugger::ThreadStarted(Thread* pRuntimeThread)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // @@@
    // This method implemented DebugInterface but it is also called from Controller

    if (CORDBUnrecoverableError(this))
        return;

    LOG((LF_CORDB, LL_INFO100, "D::TS: thread attach : ID=%#x AD:%#x\n",
         GetThreadIdHelper(pRuntimeThread), AppDomain::GetCurrentDomain()));

    // We just need to send a VMPTR_Thread. The RS will get everything else it needs from DAC.
    //

        _ASSERTE((g_pEEInterface->GetThread() &&
                 !g_pEEInterface->GetThread()->m_fPreemptiveGCDisabled) ||
                 g_fInControlC);
        _ASSERTE(ThreadHoldsLock());

    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(ipce,
                 DB_IPCE_THREAD_ATTACH,
                 pRuntimeThread,
                 AppDomain::GetCurrentDomain());


    m_pRCThread->SendIPCEvent();

        //
        // Well, if this thread got created _after_ we started sync'ing
        // then its Runtime thread flags don't have the fact that there
        // is a debug suspend pending. We need to call over to the
        // Runtime and set the flag in the thread now...
        //
        if (m_trappingRuntimeThreads)
    {
            g_pEEInterface->MarkThreadForDebugSuspend(pRuntimeThread);
    }
}


//---------------------------------------------------------------------------------------
//
// DetachThread is called by Runtime threads when they are completing
// their execution and about to be destroyed.
//
// Arguments:
//    pRuntimeThread - Pointer to the runtime's thread object to detach.
//
// Return Value:
//    None
//
//---------------------------------------------------------------------------------------
void Debugger::DetachThread(Thread *pRuntimeThread)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    if (CORDBUnrecoverableError(this))
    {
        return;
    }

    if (m_ignoreThreadDetach)
    {
        return;
    }

    _ASSERTE (pRuntimeThread != NULL);


    LOG((LF_CORDB, LL_INFO100, "D::DT: thread detach : ID=%#x AD:%#x.\n",
         GetThreadIdHelper(pRuntimeThread), AppDomain::GetCurrentDomain()));


    // We may be killing a thread before the Thread-starter fired.
    // So check (and cancel) any outstanding thread-starters.
    // If we don't, this old thread starter may conflict w/ a new thread-starter
    // if AppDomains or EE Thread's get recycled.
    DebuggerController::CancelOutstandingThreadStarter(pRuntimeThread);

    // Controller lock is bigger than debugger lock.
    // Don't take debugger lock before the CancelOutStandingThreadStarter function.
    SENDIPCEVENT_BEGIN(this, pRuntimeThread);

    if (CORDebuggerAttached())
    {
        // Send a detach thread event to the Right Side.
        DebuggerIPCEvent * pEvent = m_pRCThread->GetIPCEventSendBuffer();

        InitIPCEvent(pEvent,
                     DB_IPCE_THREAD_DETACH,
                     pRuntimeThread,
                     AppDomain::GetCurrentDomain());

        m_pRCThread->SendIPCEvent();

        // Stop all Runtime threads
        TrapAllRuntimeThreads();

        // This prevents a race condition where we blocked on the Lock()
        // above while another thread was sending an event and while we
        // were blocked the debugger suspended us and so we wouldn't be
        // resumed after the suspension about to happen below.
        pRuntimeThread->ResetThreadStateNC(Thread::TSNC_DebuggerUserSuspend);
    }
    else
    {
        LOG((LF_CORDB,LL_INFO1000, "D::DT: Skipping SendIPCEvent because RS detached."));
    }

    SENDIPCEVENT_END;
}


//
// SuspendComplete is called when the last Runtime thread reaches a safe point in response to having its trap flags set.
// This may be called on either the real helper thread or someone doing helper thread duty.
//
// It could also be called for sending garbage collection events (see DebuggerRCThread::SendIPCEvent for more about the
// thread mode associated with the events)
//
BOOL Debugger::SuspendComplete(bool isEESuspendedForGC)
{
    CONTRACTL
    {
        NOTHROW;
        if (isEESuspendedForGC) { GC_NOTRIGGER; } else { GC_TRIGGERS; }
        // This will is conceptually mode-cooperative.
        // But we haven't marked the runtime as stopped yet (m_stopped), so the contract
        // subsystem doesn't realize it yet.
        DISABLED(MODE_COOPERATIVE);
    }
    CONTRACTL_END;

    // @@@
    // Call from RCThread::MainLoop and TemporaryHelperThreadMainLoop.
    // when all threads suspended. Can happen on managed thread or helper thread.
    // If happen on managed thread, it must be doing the helper thread duty.
    //

    _ASSERTE(ThreadStore::HoldingThreadStore() || IsAtProcessExit());

    // We should be holding debugger lock m_mutex.
    _ASSERTE(ThreadHoldsLock());

    // We can't throw here (we're in the middle of the runtime suspension logic).
    // But things below us throw. So we catch the exception, but then what state are we in?

    if (!isEESuspendedForGC) {_ASSERTE((!g_pEEInterface->GetThread() || !g_pEEInterface->GetThread()->m_fPreemptiveGCDisabled) || g_fInControlC); }
    if (!isEESuspendedForGC) { _ASSERTE(ThisIsHelperThreadWorker()); }

    STRESS_LOG0(LF_CORDB, LL_INFO10000, "D::SC: suspension complete\n");

    // We have suspended runtime.

    // We're stopped now. Marking m_stopped allows us to use MODE_COOPERATIVE contracts.
    if (isEESuspendedForGC)
    {
        _ASSERTE(!m_stopped);
    }
    else
    {
        _ASSERTE(!m_stopped && m_trappingRuntimeThreads);
    }
    m_stopped = true;


    // Send the sync complete event to the Right Side.
    {
        // If we fail to send the SyncComplete, what do we do?
        CONTRACT_VIOLATION(ThrowsViolation);

        SendSyncCompleteIPCEvent(isEESuspendedForGC); // sets m_stopped = true...
    }

    // Everything in the next scope is meant to mimic what we do UnlockForEventSending minus EnableEventHandling.
    // We do the EEH part when we get the Continue event.
    {
#ifdef _DEBUG
        //_ASSERTE(m_tidLockedForEventSending == GetCurrentThreadId());
        m_tidLockedForEventSending = 0;
#endif

        //
        // Event handling is re-enabled by the RCThread in response to a
        // continue message from the Right Side.

    }

    // @todo - what should we do if this function failed?
    return TRUE;
}




//---------------------------------------------------------------------------------------
//
// Debugger::SendCreateAppDomainEvent - notify the RS of an AppDomain
//
// Arguments:
//    pRuntimeAppdomain - pointer to the AppDomain
//
// Return Value:
//    None
//
// Notes:
//    This is used to notify the debugger of either a newly created
//    AppDomain (when fAttaching is FALSE) or of existing AppDomains
//    at attach time (fAttaching is TRUE).  In both cases, this should
//    be called before any LoadModule/LoadAssembly events are sent for
//    this domain.  Otherwise the RS will get an event for an AppDomain
//    it doesn't recognize and ASSERT.
//
//    For the non-attach case this means there is no need to enumerate
//    the assemblies/modules in an AppDomain after sending this event
//    because we know there won't be any.
//

void Debugger::SendCreateAppDomainEvent(AppDomain * pRuntimeAppDomain)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;

        MODE_COOPERATIVE;
    }
    CONTRACTL_END;

    if (CORDBUnrecoverableError(this))
    {
        return;
    }

    STRESS_LOG1(LF_CORDB, LL_INFO10000, "D::SCADE: AppDomain creation:%#08x\n",
            pRuntimeAppDomain);



    Thread *pThread = g_pEEInterface->GetThread();
    SENDIPCEVENT_BEGIN(this, pThread);



    // We may have detached while waiting in LockForEventSending,
    // in which case we can't send the event.
    if (CORDebuggerAttached())
    {
        // Send a create appdomain event to the Right Side.
        DebuggerIPCEvent * pEvent = m_pRCThread->GetIPCEventSendBuffer();

        InitIPCEvent(pEvent,
                     DB_IPCE_CREATE_APP_DOMAIN,
                     pThread,
                     pRuntimeAppDomain);

        // Only send a pointer to the AppDomain, the RS will get everything else via DAC.
        pEvent->AppDomainData.vmAppDomain.SetRawPtr(pRuntimeAppDomain);
        m_pRCThread->SendIPCEvent();

        TrapAllRuntimeThreads();
    }

    // Let other Runtime threads handle their events.
    SENDIPCEVENT_END;

}


//
// LoadAssembly is called when a new Assembly gets loaded.
//
void Debugger::LoadAssembly(DomainAssembly * pDomainAssembly)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    if (CORDBUnrecoverableError(this))
        return;

    LOG((LF_CORDB, LL_INFO100, "D::LA: Load Assembly Asy:0x%p AD:0x%p which:%s\n",
        pDomainAssembly, AppDomain::GetCurrentDomain(), pDomainAssembly->GetAssembly()->GetDebugName() ));

    if (!CORDebuggerAttached())
    {
        return;
    }

    Thread *pThread = g_pEEInterface->GetThread();
    SENDIPCEVENT_BEGIN(this, pThread)


    if (CORDebuggerAttached())
    {
        // Send a load assembly event to the Right Side.
        DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
        InitIPCEvent(ipce,
                     DB_IPCE_LOAD_ASSEMBLY,
                     pThread,
                     AppDomain::GetCurrentDomain());

        ipce->AssemblyData.vmDomainAssembly.SetRawPtr(pDomainAssembly);

        m_pRCThread->SendIPCEvent();
    }
    else
    {
        LOG((LF_CORDB,LL_INFO1000, "D::LA: Skipping SendIPCEvent because RS detached."));
    }

    // Stop all Runtime threads
    if (CORDebuggerAttached())
    {
        TrapAllRuntimeThreads();
    }

    SENDIPCEVENT_END;
}



//
// UnloadAssembly is called when a Runtime thread unloads an assembly.
//
void Debugger::UnloadAssembly(DomainAssembly * pDomainAssembly)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    if (CORDBUnrecoverableError(this))
        return;

    LOG((LF_CORDB, LL_INFO100, "D::UA: Unload Assembly Asy:0x%p AD:0x%p which:%s\n",
         pDomainAssembly, AppDomain::GetCurrentDomain(), pDomainAssembly->GetAssembly()->GetDebugName() ));

    Thread *thread = g_pEEInterface->GetThread();
    // Note that the debugger lock is reentrant, so we may or may not hold it already.
    SENDIPCEVENT_BEGIN(this, thread);

    // Send the unload assembly event to the Right Side.
    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();

    InitIPCEvent(ipce,
                 DB_IPCE_UNLOAD_ASSEMBLY,
                 thread,
                 AppDomain::GetCurrentDomain());
    ipce->AssemblyData.vmDomainAssembly.SetRawPtr(pDomainAssembly);

    SendSimpleIPCEventAndBlock();

    // This will block on the continue
    SENDIPCEVENT_END;
}

//
// LoadModule is called when a Runtime thread loads a new module and a debugger
// is attached.  This also includes when a domain-neutral module is "loaded" into
// a new domain.
//
// TODO: remove pszModuleName and perhaps other args.
void Debugger::LoadModule(Module* pRuntimeModule,
                          LPCWSTR pszModuleName, // module file name.
                          DWORD dwModuleName, // length of pszModuleName in chars, not including null.
                          Assembly *pAssembly,
                          AppDomain *pAppDomain,
                          DomainAssembly *  pDomainAssembly,
                          BOOL fAttaching)
{

    CONTRACTL
    {
        NOTHROW; // not protected for Throws.
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    // @@@@
    // Implement DebugInterface but can be called internally as well.
    // This can be called by EE loading module or when we are attaching called by IteratingAppDomainForAttaching
    //
    _ASSERTE(!fAttaching);

    if (CORDBUnrecoverableError(this))
        return;

    DebuggerModule * module = NULL;

    Thread *pThread = g_pEEInterface->GetThread();
    SENDIPCEVENT_BEGIN(this, pThread);

    DebuggerIPCEvent* ipce = NULL;

    // Don't create new record if already loaded. We do still want to send the ModuleLoad event, however.
    // The RS has logic to ignore duplicate ModuleLoad events. We have to send what could possibly be a dup, though,
    // due to some really nasty issues with getting proper assembly and module load events from the loader when dealing
    // with shared assemblies.
    module = LookupOrCreateModule(pDomainAssembly);
    _ASSERTE(module != NULL);


    // During a real LoadModule event, debugger can change jit flags.
    // Can't do this during a fake event sent on attach.
    // This is cleared after we send the LoadModule event.
    module->SetCanChangeJitFlags(true);


    // @dbgtodo  inspection - Check whether the DomainAssembly we get is consistent with the Module and AppDomain we get.
    // We should simply things when we actually get rid of DebuggerModule, possibly by just passing the
    // DomainAssembly around.
    _ASSERTE(module->GetDomainAssembly()    == pDomainAssembly);
    _ASSERTE(module->GetAppDomain()     == AppDomain::GetCurrentDomain());
    _ASSERTE(module->GetRuntimeModule() == pDomainAssembly->GetModule());

    // Send a load module event to the Right Side.
    ipce = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(ipce,DB_IPCE_LOAD_MODULE, pThread, pAppDomain);

    ipce->LoadModuleData.vmDomainAssembly.SetRawPtr(pDomainAssembly);

    m_pRCThread->SendIPCEvent();

    {
        // Stop all Runtime threads
        HRESULT hr = S_OK;
        EX_TRY
        {
            TrapAllRuntimeThreads();
        }
        EX_CATCH_HRESULT(hr); // @dbgtodo  synchronization - catch exception and go on to restore state.
        // Synchronization feature crew needs to figure out what happens to TrapAllRuntimeThreads().
    }

    SENDIPCEVENT_END;

    // Now that we're done with the load module event, can no longer change Jit flags.
    module->SetCanChangeJitFlags(false);
}

// Send the raw event for Updating symbols. Debugger must query for contents from out-of-process
//
// Arguments:
//   pRuntimeModule - required, module to send symbols for. May be domain neutral.
//   pAppDomain - required, appdomain that module is in.
//
// Notes:
//   This is just a ping event. Debugger must query for actual symbol contents.
//   This keeps the launch + attach cases identical.
//   This just sends the raw event and does not synchronize the runtime.
//   Use code:Debugger.SendUpdateModuleSymsEventAndBlock for that.
void Debugger::SendRawUpdateModuleSymsEvent(Module *pRuntimeModule, AppDomain *pAppDomain)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_PREEMPTIVE;

        PRECONDITION(ThreadHoldsLock());

        // Debugger must have been attached to get us to this point.
        // We hold the Debugger-lock, so debugger could not have detached from
        // underneath us either.
        PRECONDITION(CORDebuggerAttached());
    }
    CONTRACTL_END;

    if (CORDBUnrecoverableError(this))
        return;

    // This event is used to trigger the ICorDebugManagedCallback::UpdateModuleSymbols
    // callback.  That callback is defined to pass a PDB stream, and so we still use this
    // only for legacy compatibility reasons when we've actually got PDB symbols.
    // New clients know they must request a new symbol reader after ClassLoad events.
    if (pRuntimeModule->GetInMemorySymbolStream() == NULL)
        return; // Non-PDB symbols

    DebuggerModule* module = LookupOrCreateModule(pRuntimeModule, pAppDomain);
    PREFIX_ASSUME(module != NULL);

    DebuggerIPCEvent* ipce = NULL;
    ipce = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(ipce, DB_IPCE_UPDATE_MODULE_SYMS,
                 g_pEEInterface->GetThread(),
                 pAppDomain);

    ipce->UpdateModuleSymsData.vmDomainAssembly.SetRawPtr((module ? module->GetDomainAssembly() : NULL));

    m_pRCThread->SendIPCEvent();
}

//
// UpdateModuleSyms is called when the symbols for a module need to be
// sent to the Right Side because they've changed.
//
// Arguments:
//   pRuntimeModule - required, module to send symbols for. May be domain neutral.
//   pAppDomain - required, appdomain that module is in.
//
//
// Notes:
//    This will send the event (via code:Debugger.SendRawUpdateModuleSymsEvent) and then synchronize
//    the runtime waiting for a continue.
//
//    This should only be called in cases where we reasonably expect to send symbols.
//    However, this may not send symbols if the symbols aren't available.
void Debugger::SendUpdateModuleSymsEventAndBlock(Module* pRuntimeModule, AppDomain *pAppDomain)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_ANY;
    }
    CONTRACTL_END;

    if (CORDBUnrecoverableError(this) || !CORDebuggerAttached())
    {
        return;
    }

    CGrowableStream * pStream = pRuntimeModule->GetInMemorySymbolStream();
    LOG((LF_CORDB, LL_INFO10000, "D::UMS: update module syms RuntimeModule:0x%08x CGrowableStream:0x%08x\n", pRuntimeModule, pStream));
    if (pStream == NULL)
    {
        // No in-memory Pdb available.
        STRESS_LOG1(LF_CORDB, LL_INFO10000, "No syms available %p", pRuntimeModule);
        return;
    }

    SENDIPCEVENT_BEGIN(this, g_pEEInterface->GetThread()); // toggles to preemptive

    // Actually send the event
    if (CORDebuggerAttached())
    {
        SendRawUpdateModuleSymsEvent(pRuntimeModule, pAppDomain);
        TrapAllRuntimeThreads();
    }

    SENDIPCEVENT_END;
}


//
// UnloadModule is called by the Runtime for each module (including shared ones)
// in an AppDomain that is being unloaded, when a debugger is attached.
// In the EE, a module may be domain-neutral and therefore shared across all AppDomains.
// We abstract this detail away in the Debugger and consider each such EE module to correspond
// to multiple "Debugger Module" instances (one per AppDomain).
// Therefore, this doesn't necessarily mean the runtime is unloading the module, just
// that the Debugger should consider it's (per-AppDomain) DebuggerModule to be unloaded.
//
void Debugger::UnloadModule(Module* pRuntimeModule,
                            AppDomain *pAppDomain)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    // @@@@
    // implements DebugInterface.
    // can only called by EE on Module::NotifyDebuggerUnload
    //

    if (CORDBUnrecoverableError(this))
        return;



    LOG((LF_CORDB, LL_INFO100, "D::UM: unload module Mod:%#08x AD:%#08x runtimeMod:%#08x modName:%s\n",
         LookupOrCreateModule(pRuntimeModule, pAppDomain), pAppDomain, pRuntimeModule, pRuntimeModule->GetDebugName()));


    Thread *thread = g_pEEInterface->GetThread();
    SENDIPCEVENT_BEGIN(this, thread);

    if (CORDebuggerAttached())
    {

        DebuggerModule* module = LookupOrCreateModule(pRuntimeModule, pAppDomain);
        if (module == NULL)
        {
            LOG((LF_CORDB, LL_INFO100, "D::UM: module already unloaded AD:%#08x runtimeMod:%#08x modName:%s\n",
                 pAppDomain, pRuntimeModule, pRuntimeModule->GetDebugName()));
            goto LExit;
        }
        _ASSERTE(module != NULL);

        STRESS_LOG6(LF_CORDB, LL_INFO10000,
            "D::UM: Unloading RTMod:%#08x (DomFile: %#08x, IsISStream:%#08x); DMod:%#08x(RTMod:%#08x DomFile: %#08x)\n",
            pRuntimeModule, pRuntimeModule->GetDomainAssembly(), false,
            module, module->GetRuntimeModule(), module->GetDomainAssembly());

        // Note: the appdomain the module was loaded in must match the appdomain we're unloading it from. If it doesn't,
        // then we've either found the wrong DebuggerModule in LookupModule or we were passed bad data.
        _ASSERTE(module->GetAppDomain() == pAppDomain);

        // Send the unload module event to the Right Side.
        DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
        InitIPCEvent(ipce, DB_IPCE_UNLOAD_MODULE, thread, pAppDomain);
        ipce->UnloadModuleData.vmDomainAssembly.SetRawPtr((module ? module->GetDomainAssembly() : NULL));
        ipce->UnloadModuleData.debuggerAssemblyToken.Set(pRuntimeModule->GetClassLoader()->GetAssembly());
        m_pRCThread->SendIPCEvent();

        //
        // Cleanup the module (only for resources consumed when a debugger is attached)
        //

        // Remove all patches that apply to this module/AppDomain combination
        AppDomain* domainToRemovePatchesIn = NULL;  // all domains by default

        // Note that we'll explicitly NOT delete DebuggerControllers, so that
        // the Right Side can delete them later.
        DebuggerController::RemovePatchesFromModule(pRuntimeModule, domainToRemovePatchesIn);

        // Deactivate all JMC functions in this module.  We don't do this for shared assemblies
        // because JMC status is not maintained on a per-AppDomain basis and we don't
        // want to change the JMC behavior of the module in other domains.
        LOG((LF_CORDB, LL_EVERYTHING, "Setting all JMC methods to false:\n"));
        DebuggerDataLockHolder debuggerDataLockHolder(this);
        DebuggerMethodInfoTable * pTable = GetMethodInfoTable();
        if (pTable != NULL)
        {
            HASHFIND info;

            for (DebuggerMethodInfo *dmi = pTable->GetFirstMethodInfo(&info);
                dmi != NULL;
                dmi = pTable->GetNextMethodInfo(&info))
            {
                if (dmi->m_module == pRuntimeModule)
                {
                    dmi->SetJMCStatus(false);
                }
            }
        }
        LOG((LF_CORDB, LL_EVERYTHING, "Done clearing JMC methods!\n"));

        // Delete the Left Side representation of the module.
        if (m_pModules != NULL)
        {
            DebuggerDataLockHolder chInfo(this);
            m_pModules->RemoveModule(pRuntimeModule, pAppDomain);
        }

        // Stop all Runtime threads
        TrapAllRuntimeThreads();
    }
    else
    {
        LOG((LF_CORDB,LL_INFO1000, "D::UM: Skipping SendIPCEvent because RS detached."));
    }

LExit:
    SENDIPCEVENT_END;
}

// Called when this module is completely gone from ALL AppDomains, regardless of
// whether a debugger is attached.
// This is normally not called only domain-neutral assemblies because they can't be unloaded.
// However, it may be called if the loader fails to completely load a domain-neutral assembly.
void Debugger::DestructModule(Module *pModule)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO100, "D::DM: destruct module runtimeMod:%#08x modName:%s\n",
         pModule, pModule->GetDebugName()));

    // @@@
    // Implements DebugInterface.
    // It is called for Module::Destruct. We do not need to send any IPC event.

    DebuggerLockHolder dbgLockHolder(this);

    // We should have removed all patches at AD unload time (or detach time if the
    // debugger detached).
    _ASSERTE( !DebuggerController::ModuleHasPatches(pModule) );

    // Do module clean-up that applies even when no debugger is attached.
    // Ideally, we might like to do this cleanup more eagerly and detministically,
    // but we don't currently get any early AD unload callback from the loader
    // when no debugger is attached.  Perhaps we should make the loader
    // call this callback earlier.
    RemoveModuleReferences(pModule);
}


// Internal helper to remove all the DJIs / DMIs and other references for a given Module.
// If we don't remove the DJIs / DMIs, then we're subject to recycling bugs because the underlying
// MethodDescs will get removed. Thus we'll look up a new MD and it will pull up an old DMI that matched
// the old MD. Now the DMI and MD are out of sync and it's downhill from there.
// Note that DMIs may be used (and need cleanup) even when no debugger is attached.
void Debugger::RemoveModuleReferences( Module* pModule )
{
    _ASSERTE( ThreadHoldsLock() );

    // We want to remove all references to the module from the various
    // tables.  It's not just possible, but probable, that the module
    // will be re-loaded at the exact same address, and in that case,
    // we'll have piles of entries in our DJI table that mistakenly
    // match this new module.
    // Note that this doesn't apply to domain neutral assemblies, that only
    // get unloaded when the process dies.  We won't be reclaiming their
    // DJIs/patches b/c the process is going to die, so we'll reclaim
    // the memory when the various hashtables are unloaded.

    if (m_pMethodInfos != NULL)
    {
        HRESULT hr = S_OK;
        if (!HasLazyData())
        {
            hr = LazyInitWrapper();
        }

        if (SUCCEEDED(hr))
        {
            DebuggerDataLockHolder debuggerDataLockHolder(this);

            m_pMethodInfos->ClearMethodsOfModule(pModule);

            // DebuggerDataLockHolder out of scope - release implied
        }
    }
}

//---------------------------------------------------------------------------------------
//
// SendClassLoadUnloadEvent - notify the RS of a class either loading or unloading.
//
// Arguments:
//
//    fAttaching - true if a debugger is in the process of attaching
//
// Return Value:
//    None
//
//---------------------------------------------------------------------------------------
void Debugger::SendClassLoadUnloadEvent (mdTypeDef classMetadataToken,
                                         DebuggerModule * pClassDebuggerModule,
                                         Assembly *pAssembly,
                                         AppDomain *pAppDomain,
                                         BOOL fIsLoadEvent)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;


    LOG((LF_CORDB,LL_INFO10000, "D::SCLUE: Tok:0x%x isLoad:0x%x Mod:%#08x AD:%#08x\n",
        classMetadataToken, fIsLoadEvent, pClassDebuggerModule, pAppDomain));

    DebuggerIPCEvent * pEvent = m_pRCThread->GetIPCEventSendBuffer();

    BOOL fIsReflection = pClassDebuggerModule->GetRuntimeModule()->IsReflectionEmit();

    if (fIsLoadEvent == TRUE)
    {
        // We need to update Metadata before Symbols (since symbols depend on metadata)
        // It's debatable which needs to come first: Class Load or Sym update.
        // V1.1 sent Sym Update first so that binding at the class load has the latest symbols.
        // However, The Class Load may need to be in sync with updating new metadata,
        // and that has to come before the Sym update.
        InitIPCEvent(pEvent, DB_IPCE_LOAD_CLASS, g_pEEInterface->GetThread(), pAppDomain);

        pEvent->LoadClass.classMetadataToken = classMetadataToken;
        pEvent->LoadClass.vmDomainAssembly.SetRawPtr((pClassDebuggerModule ? pClassDebuggerModule->GetDomainAssembly() : NULL));
        pEvent->LoadClass.classDebuggerAssemblyToken.Set(pAssembly);


        // For class loads in dynamic modules, RS knows that the metadata has now grown and is invalid.
        // RS will re-fetch new metadata from out-of-process.
    }
    else
    {
        InitIPCEvent(pEvent, DB_IPCE_UNLOAD_CLASS, g_pEEInterface->GetThread(), pAppDomain);

        pEvent->UnloadClass.classMetadataToken = classMetadataToken;
        pEvent->UnloadClass.vmDomainAssembly.SetRawPtr((pClassDebuggerModule ? pClassDebuggerModule->GetDomainAssembly() : NULL));
        pEvent->UnloadClass.classDebuggerAssemblyToken.Set(pAssembly);
    }

    m_pRCThread->SendIPCEvent();

    if (fIsLoadEvent && fIsReflection)
    {
        // Send the raw event, but don't actually sync and block the runtime.
        SendRawUpdateModuleSymsEvent(pClassDebuggerModule->GetRuntimeModule(), pAppDomain);
    }

}



/******************************************************************************
 *
 ******************************************************************************/
BOOL Debugger::SendSystemClassLoadUnloadEvent(mdTypeDef classMetadataToken,
                                              Module *classModule,
                                              BOOL fIsLoadEvent)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    if (!m_dClassLoadCallbackCount)
    {
        return FALSE;
    }

    BOOL fRetVal = FALSE;

    Assembly *pAssembly = classModule->GetAssembly();

    if (!m_pAppDomainCB->Lock())
        return (FALSE);

    AppDomainInfo *pADInfo = m_pAppDomainCB->FindFirst();

    while (pADInfo != NULL)
    {
        AppDomain *pAppDomain = pADInfo->m_pAppDomain;
        _ASSERTE(pAppDomain != NULL);

        // Only notify for app domains where the module has been fully loaded already
        // We used to make a different check here domain->ContainsAssembly() but that
        // triggers too early in the loading process. FindDomainAssembly will not become
        // non-NULL until the module is fully loaded into the domain which is what we
        // want.
        if (classModule->GetDomainAssembly() != NULL )
        {
            // Find the Left Side module that this class belongs in.
            DebuggerModule* pModule = LookupOrCreateModule(classModule, pAppDomain);
            _ASSERTE(pModule != NULL);

            // Only send a class load event if they're enabled for this module.
            if (pModule && pModule->ClassLoadCallbacksEnabled())
            {
                SendClassLoadUnloadEvent(classMetadataToken,
                                         pModule,
                                         pAssembly,
                                         pAppDomain,
                                         fIsLoadEvent);
                fRetVal = TRUE;
            }
        }

        pADInfo = m_pAppDomainCB->FindNext(pADInfo);
    }

    m_pAppDomainCB->Unlock();

    return fRetVal;
}


//
// LoadClass is called when a Runtime thread loads a new Class.
// Returns TRUE if an event is sent, FALSE otherwise
BOOL  Debugger::LoadClass(TypeHandle th,
                          mdTypeDef  classMetadataToken,
                          Module    *classModule,
                          AppDomain *pAppDomain)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    // @@@
    // Implements DebugInterface
    // This can be called by EE/Loader when class is loaded.
    //

    BOOL fRetVal = FALSE;

    if (CORDBUnrecoverableError(this))
        return FALSE;

    // Note that pAppDomain may be null.  The AppDomain isn't used here, and doesn't make a lot of sense since
    // we may be delivering the notification for a class in an assembly which is loaded into multiple AppDomains.  We
    // handle this in SendSystemClassLoadUnloadEvent below by looping through all AppDomains and dispatching
    // events for each that contain this assembly.

    LOG((LF_CORDB, LL_INFO10000, "D::LC: load class Tok:%#08x Mod:%#08x AD:%#08x classMod:%#08x modName:%s\n",
         classMetadataToken, (pAppDomain == NULL) ? NULL : LookupOrCreateModule(classModule, pAppDomain),
         pAppDomain, classModule, classModule->GetDebugName()));

    //
    // If we're attaching, then we only need to send the event. We
    // don't need to disable event handling or lock the debugger
    // object.
    //
    SENDIPCEVENT_BEGIN(this, g_pEEInterface->GetThread());

    if (CORDebuggerAttached())
    {
        fRetVal = SendSystemClassLoadUnloadEvent(classMetadataToken, classModule, TRUE);

        if (fRetVal == TRUE)
        {
            // Stop all Runtime threads
            TrapAllRuntimeThreads();
        }
    }
    else
    {
        LOG((LF_CORDB,LL_INFO1000, "D::LC: Skipping SendIPCEvent because RS detached."));
    }

    SENDIPCEVENT_END;

    return fRetVal;
}


//
// UnloadClass is called when a Runtime thread unloads a Class.
//
void Debugger::UnloadClass(mdTypeDef classMetadataToken,
                           Module *classModule,
                           AppDomain *pAppDomain)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    // @@@
    // Implements DebugInterface
    // Can only be called from EE

    if (CORDBUnrecoverableError(this))
    {
        return;
    }

    LOG((LF_CORDB, LL_INFO10000, "D::UC: unload class Tok:0x%08x Mod:%#08x AD:%#08x runtimeMod:%#08x modName:%s\n",
         classMetadataToken, LookupOrCreateModule(classModule, pAppDomain), pAppDomain, classModule, classModule->GetDebugName()));

    Assembly *pAssembly = classModule->GetClassLoader()->GetAssembly();
    DebuggerModule *pModule = LookupOrCreateModule(classModule, pAppDomain);

    if ((pModule == NULL) || !pModule->ClassLoadCallbacksEnabled())
    {
        return;
    }

    SENDIPCEVENT_BEGIN(this, g_pEEInterface->GetThread());

    if (CORDebuggerAttached())
    {
        _ASSERTE((pAppDomain != NULL) && (pAssembly != NULL) && (pModule != NULL));

        SendClassLoadUnloadEvent(classMetadataToken, pModule, pAssembly, pAppDomain, FALSE);

        // Stop all Runtime threads
        TrapAllRuntimeThreads();
    }
    else
    {
        LOG((LF_CORDB,LL_INFO1000, "D::UC: Skipping SendIPCEvent because RS detached."));
    }

    // Let other Runtime threads handle their events.
    SENDIPCEVENT_END;

}

/******************************************************************************
 *
 ******************************************************************************/
void Debugger::FuncEvalComplete(Thread* pThread, DebuggerEval *pDE)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

#ifndef DACCESS_COMPILE

    if (CORDBUnrecoverableError(this))
        return;

    LOG((LF_CORDB, LL_INFO1000, "D::FEC: func eval complete pDE:%p evalType:%d %s %s\n",
        pDE, pDE->m_evalType, pDE->m_successful ? "Success" : "Fail", pDE->m_aborted ? "Abort" : "Completed"));


    _ASSERTE(pDE->m_completed);
    _ASSERTE((g_pEEInterface->GetThread() && !g_pEEInterface->GetThread()->m_fPreemptiveGCDisabled) || g_fInControlC);
    _ASSERTE(ThreadHoldsLock());

    //
    // Get the domain that the result is valid in. The RS will cache this in the ICorDebugValue
    // Note: it's possible that the AppDomain has (or is about to be) unloaded, which could lead to a
    // crash when we use the DebuggerModule.  Ideally we'd only be using AppDomain IDs here.
    // We can't easily convert our ADID to an AppDomain* (SystemDomain::GetAppDomainFromId)
    // because we can't prove that the AppDomain* would be valid (not unloaded).
    //
    AppDomain *pDomain = AppDomain::GetCurrentDomain();
    AppDomain *pResultDomain = ((pDE->m_debuggerModule == NULL) ? pDomain : pDE->m_debuggerModule->GetAppDomain());

    // Send a func eval complete event to the Right Side.
    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(ipce, DB_IPCE_FUNC_EVAL_COMPLETE, pThread, pDomain);

    ipce->FuncEvalComplete.funcEvalKey = pDE->m_funcEvalKey;
    ipce->FuncEvalComplete.successful = pDE->m_successful;
    ipce->FuncEvalComplete.aborted = pDE->m_aborted;
    ipce->FuncEvalComplete.resultAddr = pDE->m_result;
    ipce->FuncEvalComplete.vmAppDomain.SetRawPtr(pResultDomain);
    ipce->FuncEvalComplete.vmObjectHandle = pDE->m_vmObjectHandle;

    LOG((LF_CORDB, LL_INFO1000, "D::FEC: TypeHandle is %p\n", pDE->m_resultType.AsPtr()));

    Debugger::TypeHandleToExpandedTypeInfo(pDE->m_retValueBoxing, // whether return values get boxed or not depends on the particular FuncEval we're doing...
                                           pResultDomain,
                                           pDE->m_resultType,
                                           &ipce->FuncEvalComplete.resultType);

    _ASSERTE(ipce->FuncEvalComplete.resultType.elementType != ELEMENT_TYPE_VALUETYPE);

    // We must adjust the result address to point to the right place
    ipce->FuncEvalComplete.resultAddr = ArgSlotEndiannessFixup((ARG_SLOT*)ipce->FuncEvalComplete.resultAddr,
        GetSizeForCorElementType(ipce->FuncEvalComplete.resultType.elementType));

    LOG((LF_CORDB, LL_INFO1000, "D::FEC: returned el %04x resultAddr %p\n",
        ipce->FuncEvalComplete.resultType.elementType, ipce->FuncEvalComplete.resultAddr));

    m_pRCThread->SendIPCEvent();

#endif
}

/******************************************************************************
 *
 ******************************************************************************/
bool Debugger::ResumeThreads(AppDomain* pAppDomain)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(ThisIsHelperThreadWorker());
    }
    CONTRACTL_END;

    // Okay, mark that we're not stopped anymore and let the
    // Runtime threads go...
    ReleaseAllRuntimeThreads(pAppDomain);

    // Return that we've continued the process.
    return true;
}


class CodeBuffer
{
public:

    BYTE *getCodeBuffer(DebuggerJitInfo *dji)
    {
        CONTRACTL
        {
            NOTHROW;
            GC_NOTRIGGER;
        }
        CONTRACTL_END;

        CodeRegionInfo codeRegionInfo = CodeRegionInfo::GetCodeRegionInfo(dji);

        if (codeRegionInfo.getAddrOfColdCode())
        {
            _ASSERTE(codeRegionInfo.getSizeOfHotCode() != 0);
            _ASSERTE(codeRegionInfo.getSizeOfColdCode() != 0);
            S_SIZE_T totalSize = S_SIZE_T( codeRegionInfo.getSizeOfHotCode() ) +
                                                S_SIZE_T( codeRegionInfo.getSizeOfColdCode() );
            if ( totalSize.IsOverflow() )
            {
                _ASSERTE(0 && "Buffer overflow error in getCodeBuffer");
                return NULL;
            }

            BYTE *code = (BYTE *) buffer.AllocNoThrow( totalSize.Value() );
            if (code)
            {
                memcpy(code,
                       (void *) codeRegionInfo.getAddrOfHotCode(),
                       codeRegionInfo.getSizeOfHotCode());

                memcpy(code + codeRegionInfo.getSizeOfHotCode(),
                       (void *) codeRegionInfo.getAddrOfColdCode(),
                       codeRegionInfo.getSizeOfColdCode());

                // Now patch the control transfer instructions
            }

            return code;
        }
        else
        {
            return dac_cast<PTR_BYTE>(codeRegionInfo.getAddrOfHotCode());
        }
    }
private:

    CQuickBytes buffer;
};


//---------------------------------------------------------------------------------------
//
// Called on the helper thread to serialize metadata so it can be read out-of-process.
//
// Arguments:
//    pModule - module that needs metadata serialization
//    countBytes - out value, holds the number of bytes which were allocated in the
//                 serialized buffer
//
// Return Value:
//    A pointer to a serialized buffer of metadata. The caller should free this bufer using
//    DeleteInteropSafe
//
// Assumptions:
//    This is called on the helper-thread, or a thread pretending to be the helper-thread.
//    For any synchronous message, the debuggee should be synchronized. The only async
//    messages are Attach and Async-Break.
//
//
//---------------------------------------------------------------------------------------
BYTE* Debugger::SerializeModuleMetaData(Module * pModule, DWORD * countBytes)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "Debugger::SMMD called\n"));

    // Do not release the emitter. This is a weak reference.
    IMetaDataEmit *pEmitter = pModule->GetEmitter();
    _ASSERTE(pEmitter != NULL);

    HRESULT hr;
    BYTE* metadataBuffer = NULL;
    ReleaseHolder<IMDInternalEmit> pInternalEmitter;
    ULONG originalUpdateMode;
    hr = pEmitter->QueryInterface(IID_IMDInternalEmit, (void **)&pInternalEmitter);
    if(FAILED(hr))
    {
        LOG((LF_CORDB, LL_INFO10, "Debugger::SMMD pEmitter doesn't support IID_IMDInternalEmit hr=0x%x\n", hr));
        ThrowHR(hr);
    }
    _ASSERTE(pInternalEmitter != NULL);

    hr = pInternalEmitter->SetMDUpdateMode(MDUpdateExtension, &originalUpdateMode);
    if(FAILED(hr))
    {
        LOG((LF_CORDB, LL_INFO10, "Debugger::SMMD SetMDUpdateMode failed hr=0x%x\n", hr));
        ThrowHR(hr);
    }
    _ASSERTE(originalUpdateMode == MDUpdateFull);

    hr = pEmitter->GetSaveSize(cssQuick, countBytes);
    if(FAILED(hr))
    {
        LOG((LF_CORDB, LL_INFO10, "Debugger::SMMD GetSaveSize failed hr=0x%x\n", hr));
        pInternalEmitter->SetMDUpdateMode(originalUpdateMode, NULL);
        ThrowHR(hr);
    }

    EX_TRY
    {
        metadataBuffer = new (interopsafe) BYTE[*countBytes];
    }
    EX_CATCH
    {
        LOG((LF_CORDB, LL_INFO10, "Debugger::SMMD Allocation failed\n"));
        pInternalEmitter->SetMDUpdateMode(originalUpdateMode, NULL);
        EX_RETHROW;
    }
    EX_END_CATCH(SwallowAllExceptions);
    _ASSERTE(metadataBuffer != NULL); // allocation would throw first

    // Caller ensures serialization that guarantees that the metadata doesn't grow underneath us.
    hr = pEmitter->SaveToMemory(metadataBuffer, *countBytes);
    if(FAILED(hr))
    {
        LOG((LF_CORDB, LL_INFO10, "Debugger::SMMD SaveToMemory failed hr=0x%x\n", hr));
        DeleteInteropSafe(metadataBuffer);
        pInternalEmitter->SetMDUpdateMode(originalUpdateMode, NULL);
        ThrowHR(hr);
    }

    pInternalEmitter->SetMDUpdateMode(originalUpdateMode, NULL);
    LOG((LF_CORDB, LL_INFO10000, "Debugger::SMMD exiting\n"));
    return metadataBuffer;
}

//---------------------------------------------------------------------------------------
//
// Handle an IPC event from the Debugger.
//
// Arguments:
//    event - IPC event to handle.
//
// Return Value:
//    True if the event was a continue. Else false.
//
// Assumptions:
//    This is called on the helper-thread, or a thread pretending to be the helper-thread.
//    For any synchronous message, the debuggee should be synchronized. The only async
//    messages are Attach and Async-Break.
//
// Notes:
// HandleIPCEvent is called by the RC thread in response to an event
// from the Debugger Interface. No other IPC events, nor any Runtime
// events will come in until this method returns. Returns true if this
// was a Continue event.
//
// If this function is called on native debugger helper thread, we will
// handle everything. However if this is called on managed thread doing
// helper thread duty, we will fail on operation since we are mainly
// waiting for CONTINUE message from the RS.
//
//
//---------------------------------------------------------------------------------------

#ifdef _PREFAST_
#pragma warning(push)
#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
#endif
bool Debugger::HandleIPCEvent(DebuggerIPCEvent * pEvent)
{
    CONTRACTL
    {
        THROWS;
        if (g_pEEInterface->GetThread() != NULL) { GC_TRIGGERS; } else { GC_NOTRIGGER; }

        PRECONDITION(ThisIsHelperThreadWorker());

        if (m_stopped)
        {
            MODE_COOPERATIVE;
        }
        else
        {
            MODE_ANY;
        }
    }
    CONTRACTL_END;

    // If we're the temporary helper thread, then we may reject certain operations.
    bool temporaryHelp = ThisIsTempHelperThread();


#ifdef _DEBUG
    // This reg key allows us to test our unhandled event filter installed in HandleIPCEventWrapper
    // to make sure it works properly.
    static int s_fDbgFaultInHandleIPCEvent = -1;
    if (s_fDbgFaultInHandleIPCEvent == -1)
    {
        s_fDbgFaultInHandleIPCEvent = UnsafeGetConfigDWORD(CLRConfig::INTERNAL_DbgFaultInHandleIPCEvent);
    }

    // If we need to fault, let's generate an access violation.
    if (s_fDbgFaultInHandleIPCEvent)
    {
        *((volatile BYTE *)0) = 0;
    }
#endif

    BOOL fSuccess;
    bool fContinue = false;
    HRESULT hr = S_OK;

    LOG((LF_CORDB, LL_INFO10000, "D::HIPCE: got %s\n", IPCENames::GetName(pEvent->type)));
    DbgLog((DebuggerIPCEventType)(pEvent->type & DB_IPCE_TYPE_MASK));

    // As for runtime is considered stopped, it means that managed threads will not
    // execute anymore managed code. However, these threads may be still running for
    // unmanaged code. So it is not true that we do not need to hold the lock while processing
    // synchrnoized event.
    //
    // The worst of all, it is the special case where user break point and exception can
    // be sent as part of attach if debugger was launched by managed app.
    //
    DebuggerLockHolder dbgLockHolder(this, FALSE);
    bool lockedThreadStore = false;

    if ((pEvent->type & DB_IPCE_TYPE_MASK) == DB_IPCE_ASYNC_BREAK ||
        (pEvent->type & DB_IPCE_TYPE_MASK) == DB_IPCE_ATTACHING ||
        this->m_willBlockOnGarbageCollectionEvent)
    {
        if (!this->m_willBlockOnGarbageCollectionEvent && !this->m_stopped)
        {
            lockedThreadStore = true;
            ThreadSuspend::LockThreadStore(ThreadSuspend::SUSPEND_FOR_DEBUGGER);
        }
        dbgLockHolder.Acquire();
    }
    else
    {
        _ASSERTE(m_stopped);
        _ASSERTE(ThreadHoldsLock());
    }


    switch (pEvent->type & DB_IPCE_TYPE_MASK)
    {

    case DB_IPCE_ATTACHING:
        // In V3, Attach is atomic, meaning that there isn't a complex handshake back and forth between LS + RS.
        // the RS sends a single-attaching event and attaches at the first response from the Left-side.
        StartCanaryThread();

        if (m_jitAttachInProgress)
        {
            // For jit-attach, mark that we're attached now.
            // This lets callers to code:Debugger.JitAttach check the flag and
            // send the jit-attach event just like a normal event.
            MarkDebuggerAttachedInternal();

            // set the managed attach event so that waiting threads can continue
            VERIFY(SetEvent(GetAttachEvent()));
            break;
        }

        VERIFY(SetEvent(GetAttachEvent()));

        //
        // For regular (non-jit) attach, fall through to do an async break.
        //
        FALLTHROUGH;

    case DB_IPCE_ASYNC_BREAK:
        {
            if (temporaryHelp)
            {
                // Don't support async break on temporary helper thread.
                // Well, this function does not return HR. So this means that
                // ASYNC_BREAK event will be catching silently while we are
                // doing helper thread duty!
                //
                hr = CORDBG_E_NOTREADY;
            }
            else
            {
                // not synchronized. We get debugger lock upon the function entry
                _ASSERTE(ThreadHoldsLock());

                // Simply trap all Runtime threads if we're not already trying to.
                if (!m_willBlockOnGarbageCollectionEvent && !m_trappingRuntimeThreads)
                {
                    // If the RS sent an Async-break, then that's an explicit request.
                    m_RSRequestedSync = TRUE;
                    TrapAllRuntimeThreads(); // Non-blocking...
                }
            }
            break;
        }

    case DB_IPCE_CONTINUE:
        {
            if (this->m_isBlockedOnGarbageCollectionEvent)
            {
                this->m_stopped = false;
                SetEvent(this->GetGarbageCollectionBlockerEvent());
            }
            else
            {
                fContinue = ResumeThreads(pEvent->vmAppDomain.GetRawPtr());

                //
                // Go ahead and release the TSL now that we're continuing. This ensures that we've held
                // the thread store lock the entire time the Runtime was just stopped.
                //
                ThreadSuspend::UnlockThreadStore(FALSE, ThreadSuspend::SUSPEND_FOR_DEBUGGER);
            }
            GetCanary()->ClearCache();
            break;
        }

    case DB_IPCE_DISABLE_OPTS:
        {
            Module *pModule = pEvent->DisableOptData.pModule.GetRawPtr();
            mdToken methodDef = pEvent->DisableOptData.funcMetadataToken;
            _ASSERTE(TypeFromToken(methodDef) == mdtMethodDef);

            HRESULT hr = E_INVALIDARG;
            EX_TRY
            {
                hr = DeoptimizeMethod(pModule, methodDef);
            }
            EX_CATCH_HRESULT(hr);

            DebuggerIPCEvent * pIPCResult = m_pRCThread->GetIPCEventReceiveBuffer();

            InitIPCEvent(pIPCResult,
                         DB_IPCE_DISABLE_OPTS_RESULT,
                         g_pEEInterface->GetThread(),
                         pEvent->vmAppDomain);

            pIPCResult->hr = hr;

            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_FORCE_CATCH_HANDLER_FOUND:
        {
            BOOL enableEvents = pEvent->ForceCatchHandlerFoundData.enableEvents;
            AppDomain *pAppDomain = pEvent->vmAppDomain.GetRawPtr();
            OBJECTREF exObj = ObjectToOBJECTREF(pEvent->ForceCatchHandlerFoundData.vmObj.GetRawPtr());
            HRESULT hr = E_INVALIDARG;

            hr = UpdateForceCatchHandlerFoundTable(enableEvents, exObj, pAppDomain);

            DebuggerIPCEvent * pIPCResult = m_pRCThread->GetIPCEventReceiveBuffer();
            InitIPCEvent(pIPCResult,
                         DB_IPCE_CATCH_HANDLER_FOUND_RESULT,
                         g_pEEInterface->GetThread(),
                         pEvent->vmAppDomain);

            pIPCResult->hr = hr;

            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_SET_ENABLE_CUSTOM_NOTIFICATION:
        {
            Module * pModule = pEvent->CustomNotificationData.vmModule.GetRawPtr();
            mdTypeDef classToken = pEvent->CustomNotificationData.classMetadataToken;
            BOOL enabled = pEvent->CustomNotificationData.Enabled;

            HRESULT hr = UpdateCustomNotificationTable(pModule, classToken, enabled);

            DebuggerIPCEvent * pIPCResult = m_pRCThread->GetIPCEventReceiveBuffer();
            InitIPCEvent(pIPCResult,
                         DB_IPCE_SET_ENABLE_CUSTOM_NOTIFICATION_RESULT,
                         g_pEEInterface->GetThread(),
                         pEvent->vmAppDomain);

            pIPCResult->hr = hr;

            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_BREAKPOINT_ADD:
        {

            //
            // Currently, we can't create a breakpoint before a
            // function desc is available.
            // Also, we can't know if a breakpoint is ok
            // prior to the method being JITted.
            //

            _ASSERTE(hr == S_OK);
            DebuggerBreakpoint * pDebuggerBP = NULL;

            DebuggerModule * pDebuggerModule = LookupOrCreateModule(pEvent->BreakpointData.vmDomainAssembly);
            Module * pModule = pDebuggerModule->GetRuntimeModule();
            DebuggerMethodInfo * pDMI = GetOrCreateMethodInfo(pModule, pEvent->BreakpointData.funcMetadataToken);
            MethodDesc * pMethodDesc = pEvent->BreakpointData.nativeCodeMethodDescToken.UnWrap();

            DebuggerJitInfo * pDJI =  NULL;
            if ((pMethodDesc != NULL) && (pDMI != NULL))
            {
                pDJI = pDMI->FindOrCreateInitAndAddJitInfo(pMethodDesc, PINSTRToPCODE(dac_cast<TADDR>(pEvent->BreakpointData.codeStartAddress)));
            }

            {
                // If we haven't been either JITted or EnC'd yet, then
                // we'll put a patch in by offset, implicitly relative
                // to the first version of the code.
                fSuccess = FALSE;
                pDebuggerBP = new (interopsafe, nothrow) DebuggerBreakpoint(pModule,
                                                                            pEvent->BreakpointData.funcMetadataToken,
                                                                            pEvent->vmAppDomain.GetRawPtr(),
                                                                            pEvent->BreakpointData.offset,
                                                                            !pEvent->BreakpointData.isIL,
                                                                            pEvent->BreakpointData.encVersion,
                                                                            pMethodDesc,
                                                                            pDJI,
                                                                            pEvent->BreakpointData.nativeCodeMethodDescToken == NULL,
                                                                            &fSuccess);

                TRACE_ALLOC(pDebuggerBP);

                if ((pDebuggerBP != NULL) && !fSuccess)
                {
                    DeleteInteropSafe(pDebuggerBP);
                    pDebuggerBP = NULL;
                    hr = CORDBG_E_UNABLE_TO_SET_BREAKPOINT;
                }
            }

            if ((pDebuggerBP == NULL) && !FAILED(hr))
            {
                hr = E_OUTOFMEMORY;
            }

            LOG((LF_CORDB,LL_INFO10000,"\tBP Add: BPTOK:"
                "0x%x, tok=0x%08x, offset=0x%x, isIL=%d dm=0x%x m=0x%x\n",
                 pDebuggerBP,
                 pEvent->BreakpointData.funcMetadataToken,
                 pEvent->BreakpointData.offset,
                 pEvent->BreakpointData.isIL,
                 pDebuggerModule,
                 pModule));

            //
            // We're using a two-way event here, so we place the
            // result event into the _receive_ buffer, not the send
            // buffer.
            //

            DebuggerIPCEvent * pIPCResult = m_pRCThread->GetIPCEventReceiveBuffer();

            InitIPCEvent(pIPCResult,
                         DB_IPCE_BREAKPOINT_ADD_RESULT,
                         g_pEEInterface->GetThread(),
                         pEvent->vmAppDomain);

            pIPCResult->BreakpointData.breakpointToken.Set(pDebuggerBP);
            pIPCResult->hr = hr;

            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_STEP:
        {
            LOG((LF_CORDB,LL_INFO10000, "D::HIPCE: frame SP:%p "
                "StepIn:%s RangeIL:%s RangeCount:%u MapStop:0x%x "
                "InterceptStop:0x%x AppD:%p\n",
                pEvent->StepData.frameToken.GetSPValue(),
                (pEvent->StepData.stepIn ? "true" : "false"),
                (pEvent->StepData.rangeIL ? "true" : "false"),
                pEvent->StepData.rangeCount,
                pEvent->StepData.rgfMappingStop,
                pEvent->StepData.rgfInterceptStop,
                pEvent->vmAppDomain.GetRawPtr()));

            // <TODO>@todo memory allocation - bad if we're synced</TODO>
            Thread * pThread = pEvent->StepData.vmThreadToken.GetRawPtr();
            AppDomain * pAppDomain = pEvent->vmAppDomain.GetRawPtr();

            DebuggerIPCEvent * pIPCResult = m_pRCThread->GetIPCEventReceiveBuffer();

            InitIPCEvent(pIPCResult,
                         DB_IPCE_STEP_RESULT,
                         pThread,
                         pEvent->vmAppDomain);

            if (temporaryHelp)
            {
                // Can't step on the temporary helper thread.
                pIPCResult->hr = CORDBG_E_NOTREADY;
            }
            else
            {
                DebuggerStepper * pStepper;

                if (pEvent->StepData.IsJMCStop)
                {
                    pStepper = new (interopsafe, nothrow) DebuggerJMCStepper(pThread,
                                                                             pEvent->StepData.rgfMappingStop,
                                                                             pEvent->StepData.rgfInterceptStop,
                                                                             pAppDomain);
                }
                else
                {
                    pStepper = new (interopsafe, nothrow) DebuggerStepper(pThread,
                                                                          pEvent->StepData.rgfMappingStop,
                                                                          pEvent->StepData.rgfInterceptStop,
                                                                           pAppDomain);
                }

                if (pStepper == NULL)
                {
                    pIPCResult->hr = E_OUTOFMEMORY;

                    m_pRCThread->SendIPCReply();

                    break;
                }
                TRACE_ALLOC(pStepper);

                unsigned int cRanges = pEvent->StepData.totalRangeCount;

                _ASSERTE(cRanges == 0 || ((cRanges > 0) && (cRanges == pEvent->StepData.rangeCount)));

                if (!pStepper->Step(pEvent->StepData.frameToken,
                                    pEvent->StepData.stepIn,
                                    &(pEvent->StepData.range),
                                    cRanges,
                                    ((cRanges > 0) ? pEvent->StepData.rangeIL : false)))
                {
                    pIPCResult->hr = E_OUTOFMEMORY;

                    m_pRCThread->SendIPCReply();

                    DeleteInteropSafe(pStepper);
                    break;
                }

                pIPCResult->StepData.stepperToken.Set(pStepper);


            } // end normal step case.


            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_STEP_OUT:
        {
            // <TODO>@todo memory allocation - bad if we're synced</TODO>
            Thread * pThread = pEvent->StepData.vmThreadToken.GetRawPtr();
            AppDomain * pAppDomain = pEvent->vmAppDomain.GetRawPtr();

            DebuggerIPCEvent * pIPCResult = m_pRCThread->GetIPCEventReceiveBuffer();

            InitIPCEvent(pIPCResult,
                         DB_IPCE_STEP_RESULT,
                         pThread,
                         pAppDomain);

            if (temporaryHelp)
            {
                // Can't step on the temporary helper thread.
                pIPCResult->hr = CORDBG_E_NOTREADY;
            }
            else
            {
                DebuggerStepper * pStepper;

                if (pEvent->StepData.IsJMCStop)
                {
                    pStepper = new (interopsafe, nothrow) DebuggerJMCStepper(pThread,
                                                                             pEvent->StepData.rgfMappingStop,
                                                                             pEvent->StepData.rgfInterceptStop,
                                                                             pAppDomain);
                }
                else
                {
                    pStepper = new (interopsafe, nothrow) DebuggerStepper(pThread,
                                                                          pEvent->StepData.rgfMappingStop,
                                                                          pEvent->StepData.rgfInterceptStop,
                                                                          pAppDomain);
                }


                if (pStepper == NULL)
                {
                    pIPCResult->hr = E_OUTOFMEMORY;
                    m_pRCThread->SendIPCReply();

                    break;
                }

                TRACE_ALLOC(pStepper);

                // Safe to stack trace b/c we're stopped.
                StackTraceTicket ticket(pThread);

                pStepper->StepOut(pEvent->StepData.frameToken, ticket);

                pIPCResult->StepData.stepperToken.Set(pStepper);
            }

            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_BREAKPOINT_REMOVE:
        {
            // <TODO>@todo memory allocation - bad if we're synced</TODO>

            DebuggerBreakpoint * pDebuggerBP = pEvent->BreakpointData.breakpointToken.UnWrap();

            pDebuggerBP->Delete();
        }
        break;

    case DB_IPCE_STEP_CANCEL:
        {
            // <TODO>@todo memory allocation - bad if we're synced</TODO>
            LOG((LF_CORDB,LL_INFO10000, "D:HIPCE:Got STEP_CANCEL for stepper 0x%p\n",
                 pEvent->StepData.stepperToken.UnWrap()));

            DebuggerStepper * pStepper = pEvent->StepData.stepperToken.UnWrap();

            pStepper->Delete();
        }
        break;

    case DB_IPCE_SET_ALL_DEBUG_STATE:
        {
            Thread * pThread = pEvent->SetAllDebugState.vmThreadToken.GetRawPtr();
            CorDebugThreadState debugState = pEvent->SetAllDebugState.debugState;

            LOG((LF_CORDB,LL_INFO10000,"HandleIPCE: SetAllDebugState: except thread 0x%08x (ID:0x%x) to state 0x%x\n",
                 pThread,
                 (pThread != NULL) ? GetThreadIdHelper(pThread) : 0,
                 debugState));

            if (!IsAtProcessExit())
            {
                g_pEEInterface->SetAllDebugState(pThread, debugState);
            }

            STRESS_LOG1(LF_CORDB,LL_INFO10000,"HandleIPC: Got 0x%x back from SetAllDebugState\n", hr);

            // Just send back an HR.
            DebuggerIPCEvent * pIPCResult = m_pRCThread->GetIPCEventReceiveBuffer();

            PREFIX_ASSUME(pIPCResult != NULL);

            InitIPCEvent(pIPCResult, DB_IPCE_SET_DEBUG_STATE_RESULT, NULL, NULL);

            pIPCResult->hr = S_OK;

            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_GET_GCHANDLE_INFO:
        // Given an unvalidated GC-handle, find out all the info about it to view the object
        // at the other end
        {
            OBJECTHANDLE objectHandle = pEvent->GetGCHandleInfo.GCHandle.GetRawPtr();

            DebuggerIPCEvent * pIPCResult = m_pRCThread->GetIPCEventReceiveBuffer();

            PREFIX_ASSUME(pIPCResult != NULL);

            InitIPCEvent(pIPCResult, DB_IPCE_GET_GCHANDLE_INFO_RESULT, NULL, NULL);

            bool fValid = SUCCEEDED(ValidateGCHandle(objectHandle));

            AppDomain * pAppDomain = NULL;

            if(fValid)
            {
                // Get the appdomain
                pAppDomain = AppDomain::GetCurrentDomain();

                _ASSERTE(pAppDomain != NULL);
            }

            pIPCResult->hr = S_OK;
            pIPCResult->GetGCHandleInfoResult.vmAppDomain.SetRawPtr(pAppDomain);
            pIPCResult->GetGCHandleInfoResult.fValid = fValid;

            m_pRCThread->SendIPCReply();

        }
        break;

    case DB_IPCE_GET_BUFFER:
        {
            GetAndSendBuffer(m_pRCThread, pEvent->GetBuffer.bufSize);
        }
        break;

    case DB_IPCE_RELEASE_BUFFER:
        {
            SendReleaseBuffer(m_pRCThread, pEvent->ReleaseBuffer.pBuffer);
        }
        break;
#ifdef FEATURE_METADATA_UPDATER
    case DB_IPCE_APPLY_CHANGES:
        {
            LOG((LF_ENC, LL_INFO100, "D::HIPCE: DB_IPCE_APPLY_CHANGES 1\n"));

            DebuggerModule * pDebuggerModule = LookupOrCreateModule(pEvent->ApplyChanges.vmDomainAssembly);
            //
            // @todo handle error.
            //
            hr = ApplyChangesAndSendResult(pDebuggerModule,
                                           pEvent->ApplyChanges.cbDeltaMetadata,
                                           (BYTE*) CORDB_ADDRESS_TO_PTR(pEvent->ApplyChanges.pDeltaMetadata),
                                           pEvent->ApplyChanges.cbDeltaIL,
                                           (BYTE*) CORDB_ADDRESS_TO_PTR(pEvent->ApplyChanges.pDeltaIL));

            LOG((LF_ENC, LL_INFO100, "D::HIPCE: DB_IPCE_APPLY_CHANGES 2\n"));
        }
        break;
#endif // FEATURE_METADATA_UPDATER

    case DB_IPCE_SET_CLASS_LOAD_FLAG:
        {
            DebuggerModule *pDebuggerModule = LookupOrCreateModule(pEvent->SetClassLoad.vmDomainAssembly);

            _ASSERTE(pDebuggerModule != NULL);

            LOG((LF_CORDB, LL_INFO10000,
                 "D::HIPCE: class load flag is %s for module %p\n",
                 (pEvent->SetClassLoad.flag ? "true" : "false"),
                 pDebuggerModule));

            pDebuggerModule->EnableClassLoadCallbacks((BOOL)pEvent->SetClassLoad.flag);
        }
        break;

    case DB_IPCE_IS_TRANSITION_STUB:
        GetAndSendTransitionStubInfo((CORDB_ADDRESS_TYPE*)pEvent->IsTransitionStub.address);
        break;

    case DB_IPCE_MODIFY_LOGSWITCH:
        g_pEEInterface->DebuggerModifyingLogSwitch (pEvent->LogSwitchSettingMessage.iLevel,
                                                    pEvent->LogSwitchSettingMessage.szSwitchName.GetString());

        break;

    case DB_IPCE_ENABLE_LOG_MESSAGES:
        {
            bool fOnOff = pEvent->LogSwitchSettingMessage.iLevel ? true : false;
            EnableLogMessages (fOnOff);
        }
        break;

    case DB_IPCE_SET_IP:

        {
            // This is a synchronous event (reply required)
            DebuggerIPCEvent * pIPCResult = m_pRCThread->GetIPCEventReceiveBuffer();

            // Don't have an explicit reply msg
            InitIPCReply(pIPCResult, DB_IPCE_SET_IP);

            if (temporaryHelp)
            {
                pIPCResult->hr = CORDBG_E_NOTREADY;
            }
            else if (!IsAtProcessExit())
            {
                //
                // Since this pointer is coming from the RS, it may be NULL or something
                // unexpected in an OOM situation.  Quickly just sanity check them.
                //
                Thread * pThread = pEvent->SetIP.vmThreadToken.GetRawPtr();
                Module * pModule = pEvent->SetIP.vmDomainAssembly.GetRawPtr()->GetModule();

                // Get the DJI for this function
                DebuggerMethodInfo * pDMI = GetOrCreateMethodInfo(pModule, pEvent->SetIP.mdMethod);
                DebuggerJitInfo * pDJI = NULL;
                if (pDMI != NULL)
                {
                    // In the EnC case, if we look for an older version, we need to find the DJI by starting
                    // address, rather than just by MethodDesc. In the case of generics, we may need to create a DJI, so we
                    pDJI = pDMI->FindOrCreateInitAndAddJitInfo(pEvent->SetIP.vmMethodDesc.GetRawPtr(),
                                                               PINSTRToPCODE((TADDR)pEvent->SetIP.startAddress));
                }

                if ((pDJI != NULL) && (pThread != NULL) && (pModule != NULL))
                {
                    CHECK_IF_CAN_TAKE_HELPER_LOCKS_IN_THIS_SCOPE(&(pIPCResult->hr), GetCanary());

                    if (SUCCEEDED(pIPCResult->hr))
                    {
                        pIPCResult->hr = SetIP(pEvent->SetIP.fCanSetIPOnly,
                                          pThread,
                                          pModule,
                                               pEvent->SetIP.mdMethod,
                                               pDJI,
                                               pEvent->SetIP.offset,
                                               pEvent->SetIP.fIsIL
                                               );
                    }
                }
                else
                {
                    pIPCResult->hr = E_INVALIDARG;
                }
            }
            else
            {
                pIPCResult->hr = S_OK;
            }

            // Send the result
            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_DETACH_FROM_PROCESS:
        LOG((LF_CORDB, LL_INFO10000, "Detaching from process!\n"));

        // Delete all controllers (remove patches etc.)
        DebuggerController::DeleteAllControllers();
        // Note that we'd like to be able to do this assert here
        //      _ASSERTE(DebuggerController::GetNumberOfPatches() == 0);
        // However controllers may get queued for deletion if there is outstanding
        // work and so we can't guarantee the deletion will complete now.
        // @dbgtodo  inspection: This shouldn't be an issue in the complete V3 architecture

        MarkDebuggerUnattachedInternal();

        m_pRCThread->RightSideDetach();


        // Clear JMC status
        {
            LOG((LF_CORDB, LL_EVERYTHING, "Setting all JMC methods to false:\n"));
            // On detach, set all DMI's JMC status to false.
            // We have to do this b/c we clear the DebuggerModules and allocated
            // new ones on re-attach; and the DMI & DM need to be in sync
            // (in this case, agreeing that JMC-status = false).
            // This also syncs the EE modules and disables all JMC probes.
            DebuggerMethodInfoTable * pMethodInfoTable = g_pDebugger->GetMethodInfoTable();

            if (pMethodInfoTable != NULL)
            {
                HASHFIND hashFind;
                DebuggerDataLockHolder debuggerDataLockHolder(this);

                for (DebuggerMethodInfo * pMethodInfo = pMethodInfoTable->GetFirstMethodInfo(&hashFind);
                    pMethodInfo != NULL;
                    pMethodInfo = pMethodInfoTable->GetNextMethodInfo(&hashFind))
                {
                    pMethodInfo->SetJMCStatus(false);
                }
            }
            LOG((LF_CORDB, LL_EVERYTHING, "Done clearing JMC methods!\n"));
        }

        // Clean up the hash of DebuggerModules
        // This method is overridden to also free all DebuggerModule objects
        if (m_pModules != NULL)
        {

            // Removes all DebuggerModules
            DebuggerDataLockHolder ch(this);
            m_pModules->Clear();

        }

        // Reply to the detach message before we release any Runtime threads. This ensures that the debugger will get
        // the detach reply before the process exits if the main thread is near exiting.
        m_pRCThread->SendIPCReply();

        if (this->m_isBlockedOnGarbageCollectionEvent)
        {
            this->m_stopped = FALSE;
            SetEvent(this->GetGarbageCollectionBlockerEvent());
        }
        else
        {
            // Let the process run free now... there is no debugger to bother it anymore.
            fContinue = ResumeThreads(pEvent->vmAppDomain.GetRawPtr());

            //
            // Go ahead and release the TSL now that we're continuing. This ensures that we've held
            // the thread store lock the entire time the Runtime was just stopped.
            //
            ThreadSuspend::UnlockThreadStore(FALSE, ThreadSuspend::SUSPEND_FOR_DEBUGGER);
        }

        break;

#ifndef DACCESS_COMPILE

    case DB_IPCE_FUNC_EVAL:
        {
            // This is a synchronous event (reply required)
            pEvent = m_pRCThread->GetIPCEventReceiveBuffer();

            Thread * pThread = pEvent->FuncEval.vmThreadToken.GetRawPtr();

            InitIPCEvent(pEvent, DB_IPCE_FUNC_EVAL_SETUP_RESULT, pThread, AppDomain::GetCurrentDomain());

            BYTE * pbArgDataArea = NULL;
            DebuggerEval * pDebuggerEvalKey = NULL;

            pEvent->hr = FuncEvalSetup(&(pEvent->FuncEval), &pbArgDataArea, &pDebuggerEvalKey);

            // Send the result of how the func eval setup went.
            pEvent->FuncEvalSetupComplete.argDataArea = PTR_TO_CORDB_ADDRESS(pbArgDataArea);
            pEvent->FuncEvalSetupComplete.debuggerEvalKey.Set(pDebuggerEvalKey);

            m_pRCThread->SendIPCReply();
        }

        break;

#endif

    case DB_IPCE_SET_REFERENCE:
        {
            // This is a synchronous event (reply required)
            pEvent = m_pRCThread->GetIPCEventReceiveBuffer();

            InitIPCReply(pEvent, DB_IPCE_SET_REFERENCE_RESULT);

            pEvent->hr = SetReference(pEvent->SetReference.objectRefAddress,
                                      pEvent->SetReference.vmObjectHandle,
                                      pEvent->SetReference.newReference);

            // Send the result of how the set reference went.
            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_SET_VALUE_CLASS:
        {
            // This is a synchronous event (reply required)
            pEvent = m_pRCThread->GetIPCEventReceiveBuffer();

            InitIPCReply(pEvent, DB_IPCE_SET_VALUE_CLASS_RESULT);

            pEvent->hr = SetValueClass(pEvent->SetValueClass.oldData,
                                       pEvent->SetValueClass.newData,
                                       &pEvent->SetValueClass.type);

            // Send the result of how the set reference went.
            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_GET_THREAD_FOR_TASKID:
        {
             Thread *pThreadRet = NULL;

             // This is a synchronous event (reply required)
             pEvent = m_pRCThread->GetIPCEventReceiveBuffer();

             InitIPCReply(pEvent, DB_IPCE_GET_THREAD_FOR_TASKID_RESULT);

             pEvent->GetThreadForTaskIdResult.vmThreadToken.SetRawPtr(pThreadRet);
             pEvent->hr = S_OK;

             m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_CREATE_HANDLE:
        {
             Object * pObject = (Object*)pEvent->CreateHandle.objectToken;
             OBJECTREF objref = ObjectToOBJECTREF(pObject);
             AppDomain * pAppDomain = pEvent->vmAppDomain.GetRawPtr();
             CorDebugHandleType handleType = pEvent->CreateHandle.handleType;
             OBJECTHANDLE objectHandle;

             // This is a synchronous event (reply required)
             pEvent = m_pRCThread->GetIPCEventReceiveBuffer();

             InitIPCReply(pEvent, DB_IPCE_CREATE_HANDLE_RESULT);

             {
                 // Handle creation may need to allocate memory.
                 // The API specifically limits the number of handls Cordbg can create,
                 // so we could preallocate and fail allocating anything beyond that.
                 CHECK_IF_CAN_TAKE_HELPER_LOCKS_IN_THIS_SCOPE(&(pEvent->hr), GetCanary());

                 if (SUCCEEDED(pEvent->hr))
                 {
                    switch (handleType)
                    {
                    case HANDLE_STRONG:
                        // create strong handle
                        objectHandle = pAppDomain->CreateStrongHandle(objref);
                        break;
                    case HANDLE_WEAK_TRACK_RESURRECTION:
                         // create the weak long handle
                         objectHandle = pAppDomain->CreateLongWeakHandle(objref);
                        break;
                    case HANDLE_PINNED:
                        // create pinning handle
                        objectHandle = pAppDomain->CreatePinningHandle(objref);
                        break;
                    default:
                        objectHandle = NULL;
                        pEvent->hr = E_INVALIDARG;
                    }

                    if (SUCCEEDED(pEvent->hr))
                    {
                        pEvent->CreateHandleResult.vmObjectHandle.SetRawPtr(objectHandle);
                    }
                 }
             }

             m_pRCThread->SendIPCReply();
             break;
        }

    case DB_IPCE_DISPOSE_HANDLE:
        {
            // DISPOSE an object handle
            OBJECTHANDLE objectHandle = pEvent->DisposeHandle.vmObjectHandle.GetRawPtr();
            CorDebugHandleType handleType = pEvent->DisposeHandle.handleType;

            switch (handleType)
            {
            case HANDLE_STRONG:
                DestroyStrongHandle(objectHandle);
                break;
            case HANDLE_WEAK_TRACK_RESURRECTION:
                DestroyLongWeakHandle(objectHandle);
                break;
            case HANDLE_PINNED:
                DestroyPinningHandle(objectHandle);
                break;
            default:
                pEvent->hr = E_INVALIDARG;
            }
            break;
        }

#ifndef DACCESS_COMPILE

    case DB_IPCE_FUNC_EVAL_ABORT:
        {
            LOG((LF_CORDB, LL_INFO1000, "D::HIPCE: Got FuncEvalAbort for pDE:%p\n",
                pEvent->FuncEvalAbort.debuggerEvalKey.UnWrap()));

            // This is a synchronous event (reply required)

            pEvent = m_pRCThread->GetIPCEventReceiveBuffer();
            InitIPCReply(pEvent,DB_IPCE_FUNC_EVAL_ABORT_RESULT);

            pEvent->hr = FuncEvalAbort(pEvent->FuncEvalAbort.debuggerEvalKey.UnWrap());

            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_FUNC_EVAL_RUDE_ABORT:
        {
            LOG((LF_CORDB, LL_INFO1000, "D::HIPCE: Got FuncEvalRudeAbort for pDE:%p\n",
                pEvent->FuncEvalRudeAbort.debuggerEvalKey.UnWrap()));

            // This is a synchronous event (reply required)

            pEvent = m_pRCThread->GetIPCEventReceiveBuffer();

            InitIPCReply(pEvent, DB_IPCE_FUNC_EVAL_RUDE_ABORT_RESULT);

            pEvent->hr = FuncEvalRudeAbort(pEvent->FuncEvalRudeAbort.debuggerEvalKey.UnWrap());

            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_FUNC_EVAL_CLEANUP:

        // This is a synchronous event (reply required)

        pEvent = m_pRCThread->GetIPCEventReceiveBuffer();

        InitIPCReply(pEvent,DB_IPCE_FUNC_EVAL_CLEANUP_RESULT);

        pEvent->hr = FuncEvalCleanup(pEvent->FuncEvalCleanup.debuggerEvalKey.UnWrap());

        m_pRCThread->SendIPCReply();

        break;

#endif

    case DB_IPCE_CONTROL_C_EVENT_RESULT:
        {
            // store the result of whether the event has been handled by the debugger and
            // wake up the thread waiting for the result
            SetDebuggerHandlingCtrlC(pEvent->hr == S_OK);
            VERIFY(SetEvent(GetCtrlCMutex()));
        }
        break;

    // Set the JMC status on individual methods
    case DB_IPCE_SET_METHOD_JMC_STATUS:
        {
            // Get the info out of the event
            DebuggerModule * pDebuggerModule = LookupOrCreateModule(pEvent->SetJMCFunctionStatus.vmDomainAssembly);
            Module * pModule = pDebuggerModule->GetRuntimeModule();

            bool fStatus = (pEvent->SetJMCFunctionStatus.dwStatus != 0);

            mdMethodDef token = pEvent->SetJMCFunctionStatus.funcMetadataToken;

            // Prepare reply
            pEvent = m_pRCThread->GetIPCEventReceiveBuffer();

            InitIPCEvent(pEvent, DB_IPCE_SET_METHOD_JMC_STATUS_RESULT, NULL, NULL);

            pEvent->hr = S_OK;

            if (pDebuggerModule->HasAnyOptimizedCode() && fStatus)
            {
                // If there's optimized code, then we can't be set JMC status to true.
                // That's because JMC probes are not injected in optimized code, and we
                // need a JMC probe to have a JMC function.
                pEvent->hr = CORDBG_E_CANT_SET_TO_JMC;
            }
            else
            {
                DebuggerDataLockHolder debuggerDataLockHolder(this);
                // This may be called on an unjitted method, so we may
                // have to create the MethodInfo.
                DebuggerMethodInfo * pMethodInfo = GetOrCreateMethodInfo(pModule, token);

                if (pMethodInfo == NULL)
                {
                    pEvent->hr = E_OUTOFMEMORY;
                }
                else
                {
                    // Update the storage on the LS
                    pMethodInfo->SetJMCStatus(fStatus);
                }
            }

            // Send reply
            m_pRCThread->SendIPCReply();
        }
        break;

    // Get the JMC status on a given function
    case DB_IPCE_GET_METHOD_JMC_STATUS:
        {
            // Get the method
            DebuggerModule * pDebuggerModule = LookupOrCreateModule(pEvent->SetJMCFunctionStatus.vmDomainAssembly);

            Module * pModule = pDebuggerModule->GetRuntimeModule();

            mdMethodDef token = pEvent->SetJMCFunctionStatus.funcMetadataToken;

            // Init reply
            pEvent = m_pRCThread->GetIPCEventReceiveBuffer();
            InitIPCEvent(pEvent, DB_IPCE_GET_METHOD_JMC_STATUS_RESULT, NULL, NULL);

            //
            // This may be called on an unjitted method, so we may
            // have to create the MethodInfo.
            //
            DebuggerMethodInfo * pMethodInfo = GetOrCreateMethodInfo(pModule, token);

            if (pMethodInfo == NULL)
            {
                pEvent->hr = E_OUTOFMEMORY;
            }
            else
            {
                bool fStatus = pMethodInfo->IsJMCFunction();
                pEvent->SetJMCFunctionStatus.dwStatus = fStatus;
                pEvent->hr = S_OK;
            }

            m_pRCThread->SendIPCReply();
        }
        break;

    case DB_IPCE_SET_MODULE_JMC_STATUS:
        {
            // Get data out of event
            DebuggerModule * pDebuggerModule = LookupOrCreateModule(pEvent->SetJMCFunctionStatus.vmDomainAssembly);

            bool fStatus = (pEvent->SetJMCFunctionStatus.dwStatus != 0);

            // Prepare reply
            pEvent = m_pRCThread->GetIPCEventReceiveBuffer();

            InitIPCReply(pEvent, DB_IPCE_SET_MODULE_JMC_STATUS_RESULT);

            pEvent->hr = S_OK;

            if (pDebuggerModule->HasAnyOptimizedCode() && fStatus)
            {
                // If there's optimized code, then we can't be set JMC status to true.
                // That's because JMC probes are not injected in optimized code, and we
                // need a JMC probe to have a JMC function.
                pEvent->hr = CORDBG_E_CANT_SET_TO_JMC;
            }
            else
            {
                g_pDebugger->SetModuleDefaultJMCStatus(pDebuggerModule->GetRuntimeModule(), fStatus);
            }



            // Send reply
            m_pRCThread->SendIPCReply();
        }
        break;


    case DB_IPCE_INTERCEPT_EXCEPTION:
        GetAndSendInterceptCommand(pEvent);
        break;

    case DB_IPCE_RESOLVE_UPDATE_METADATA_1:
        {

            LOG((LF_CORDB, LL_INFO10000, "D::HIPCE Handling DB_IPCE_RESOLVE_UPDATE_METADATA_1\n"));
            // This isn't ideal - Making SerializeModuleMetaData not call new is hard,
            // but the odds of trying to load a module after a thread is stopped w/
            // the heap lock should be pretty low.
            // All of the metadata calls can violate this and call new.
            SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;

            Module * pModule = pEvent->MetadataUpdateRequest.vmModule.GetRawPtr();
            LOG((LF_CORDB, LL_INFO100000, "D::HIPCE Got module 0x%x\n", pModule));

            DWORD countBytes = 0;

            // This will allocate memory. Debugger will then copy from here and send a
            // DB_IPCE_RESOLVE_UPDATE_METADATA_2 to free this memory.
            BYTE* pData = NULL;
            EX_TRY
            {
                LOG((LF_CORDB, LL_INFO100000, "D::HIPCE Calling SerializeModuleMetaData\n"));
                pData = SerializeModuleMetaData(pModule, &countBytes);

            }
            EX_CATCH_HRESULT(hr);

            LOG((LF_CORDB, LL_INFO100000, "D::HIPCE hr is 0x%x\n", hr));

            DebuggerIPCEvent * pResult = m_pRCThread->GetIPCEventReceiveBuffer();
            InitIPCEvent(pResult, DB_IPCE_RESOLVE_UPDATE_METADATA_1_RESULT, NULL, NULL);

            pResult->MetadataUpdateRequest.pMetadataStart = pData;
            pResult->MetadataUpdateRequest.nMetadataSize = countBytes;
            pResult->hr = hr;
            LOG((LF_CORDB, LL_INFO1000000, "D::HIPCE metadataStart=0x%x, nMetadataSize=0x%x\n", pData, countBytes));

            m_pRCThread->SendIPCReply();
            LOG((LF_CORDB, LL_INFO1000000, "D::HIPCE reply sent\n"));
        }
        break;

    case DB_IPCE_RESOLVE_UPDATE_METADATA_2:
        {
            // Delete memory allocated with DB_IPCE_RESOLVE_UPDATE_METADATA_1.
            BYTE * pData = (BYTE *) pEvent->MetadataUpdateRequest.pMetadataStart;
            DeleteInteropSafe(pData);

            DebuggerIPCEvent * pResult = m_pRCThread->GetIPCEventReceiveBuffer();
            InitIPCEvent(pResult, DB_IPCE_RESOLVE_UPDATE_METADATA_2_RESULT, NULL, NULL);
            pResult->hr = S_OK;
            m_pRCThread->SendIPCReply();
        }

        break;

    default:
        // We should never get an event that we don't know about.
        CONSISTENCY_CHECK_MSGF(false, ("Unknown Debug-Event on LS:id=0x%08x.", pEvent->type));
        LOG((LF_CORDB, LL_INFO10000, "Unknown event type: 0x%08x\n",
             pEvent->type));
    }

    STRESS_LOG0(LF_CORDB, LL_INFO10000, "D::HIPCE: finished handling event\n");

    if (lockedThreadStore)
    {
        ThreadSuspend::UnlockThreadStore(FALSE, ThreadSuspend::SUSPEND_FOR_DEBUGGER);
    }
    // dbgLockHolder goes out of scope - implicit Release
    return fContinue;
}
#ifdef _PREFAST_
#pragma warning(pop)
#endif

/*
 * GetAndSendInterceptCommand
 *
 * This function processes an INTERCEPT_EXCEPTION IPC event, sending the appropriate response.
 *
 * Parameters:
 *   event - the event to process.
 *
 * Returns:
 *   hr - HRESULT.
 *
 */
HRESULT Debugger::GetAndSendInterceptCommand(DebuggerIPCEvent *event)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS_FROM_GETJITINFO;
    }
    CONTRACTL_END;

    HRESULT hr = S_OK;

    _ASSERTE((event->type & DB_IPCE_TYPE_MASK) == DB_IPCE_INTERCEPT_EXCEPTION);

    //
    // Simple state validation first.
    //
    Thread *pThread = event->InterceptException.vmThreadToken.GetRawPtr();

    if ((pThread != NULL) &&
        !m_forceNonInterceptable &&
        IsInterceptableException(pThread))
    {
        ThreadExceptionState* pExState = pThread->GetExceptionState();

        // We can only have one interception going on at any given time.
        if (!pExState->GetFlags()->DebuggerInterceptInfo())
        {
            //
            // Now start processing the parameters from the event.
            //
            FramePointer targetFramePointer = event->InterceptException.frameToken;

            ControllerStackInfo csi;

            // Safe because we're stopped.
            StackTraceTicket ticket(pThread);
            csi.GetStackInfo(ticket, pThread, targetFramePointer, NULL);

            if (csi.m_targetFrameFound)
            {
                //
                // If the target frame is below the point where the current exception was
                // thrown from, then we should reject this interception command.  This
                // can happen in a func-eval during an exception callback, or during a
                // breakpoint in a filter function.  Or it can just be a user error.
                //
                CONTEXT* pContext = pExState->GetContextRecord();

                // This is an approximation on IA64, where we should use the caller SP instead of
                // the current SP.  However, if the targetFramePointer is valid, the comparison should
                // still work.  targetFramePointer should be valid because it ultimately comes from a
                // full stackwalk.
                FramePointer excepFramePointer = FramePointer::MakeFramePointer(GetSP(pContext));

                if (IsCloserToRoot(excepFramePointer, targetFramePointer))
                {
                    hr = CORDBG_E_CURRENT_EXCEPTION_IS_OUTSIDE_CURRENT_EXECUTION_SCOPE;
                    goto LSendResponse;
                }


                //
                // If the instruction that faulted is not in this managed code, at the leaf
                // frame, then the IP is actually the return address from the managed or
                // unmanaged function that really did fault.  Thus, we actually want the
                // IP of the call instruction.  I fake this by simply subtracting 1 from
                // the IP, which is close enough approximation for the search below.
                //
                if (pExState->GetContextRecord() != NULL)
                {
                    // If the faulting instruction is not in managed code, then the interception frame
                    // must be non-leaf.
                    if (!g_pEEInterface->IsManagedNativeCode((BYTE *)(GetIP(pExState->GetContextRecord()))))
                    {
                        csi.m_activeFrame.relOffset--;
                    }
                    else
                    {
                        MethodDesc *pMethodDesc = g_pEEInterface->GetNativeCodeMethodDesc(dac_cast<PCODE>(GetIP(pExState->GetContextRecord())));

                        // check if the interception frame is the leaf frame
                        if ((pMethodDesc == NULL) ||
                            (pMethodDesc != csi.m_activeFrame.md) ||
                            (GetSP(pExState->GetContextRecord()) != GetRegdisplaySP(&(csi.m_activeFrame.registers))))
                        {
                            csi.m_activeFrame.relOffset--;
                        }
                    }
                }

                //
                // Now adjust the IP to be the previous zero-stack depth sequence point.
                //
                SIZE_T foundOffset = 0;
                DebuggerJitInfo *pJitInfo = csi.m_activeFrame.GetJitInfoFromFrame();

                if (pJitInfo != NULL)
                {
                    ICorDebugInfo::SourceTypes src;

                    ULONG relOffset = csi.m_activeFrame.relOffset;

#if defined(FEATURE_EH_FUNCLETS)
                    int funcletIndex = PARENT_METHOD_INDEX;

                    // For funclets, we need to make sure that the stack empty sequence point we use is
                    // in the same funclet as the current offset.
                    if (csi.m_activeFrame.IsFuncletFrame())
                    {
                        funcletIndex = pJitInfo->GetFuncletIndex(relOffset, DebuggerJitInfo::GFIM_BYOFFSET);
                    }

                    // Refer to the loop using pMap below.
                    DebuggerILToNativeMap* pMap = NULL;
#endif // FEATURE_EH_FUNCLETS

                    for (unsigned int i = 0; i < pJitInfo->GetSequenceMapCount(); i++)
                    {
                        SIZE_T startOffset = pJitInfo->GetSequenceMap()[i].nativeStartOffset;

                        if (DbgIsSpecialILOffset(pJitInfo->GetSequenceMap()[i].ilOffset))
                        {
                            LOG((LF_CORDB, LL_INFO10000,
                                    "D::HIPCE: not placing breakpoint at special offset 0x%zx\n", startOffset));
                            continue;
                        }

                        if ((i >= 1) && (startOffset == pJitInfo->GetSequenceMap()[i-1].nativeStartOffset))
                        {
                            LOG((LF_CORDB, LL_INFO10000,
                                 "D::HIPCE: not placing redundant breakpoint at duplicate offset 0x%zx\n", startOffset));
                            continue;
                        }

                        if (startOffset > relOffset)
                        {
                            LOG((LF_CORDB, LL_INFO10000,
                                 "D::HIPCE: Stopping scan for breakpoint at offset 0x%zx\n", startOffset));
                            continue;
                        }

                        src = pJitInfo->GetSequenceMap()[i].source;

                        if (!(src & ICorDebugInfo::STACK_EMPTY))
                        {
                            LOG((LF_CORDB, LL_INFO10000, "D::HIPCE: not placing EnC breakpoint at offset "
                                    "0x%zx b/c not STACK_EMPTY:it's 0x%x\n", startOffset, src));
                            continue;
                        }

                        if ((foundOffset < startOffset) && (startOffset <= relOffset)
#if defined(FEATURE_EH_FUNCLETS)
                            // Check if we are still in the same funclet.
                            && (funcletIndex == pJitInfo->GetFuncletIndex(startOffset, DebuggerJitInfo::GFIM_BYOFFSET))
#endif // FEATURE_EH_FUNCLETS
                           )
                        {
                            LOG((LF_CORDB, LL_INFO10000, "D::HIPCE: updating breakpoint at native offset 0x%zx\n",
                                 startOffset));
                            foundOffset = startOffset;
#if defined(FEATURE_EH_FUNCLETS)
                            // Save the map entry for modification later.
                            pMap = &(pJitInfo->GetSequenceMap()[i]);
#endif // FEATURE_EH_FUNCLETS
                        }
                    }

#if defined(FEATURE_EH_FUNCLETS)
                    // This is nasty.  Starting recently we could have multiple sequence points with the same IL offset
                    // in the SAME funclet/parent method (previously different sequence points with the same IL offset
                    // imply that they are in different funclet/parent method).  Fortunately, we only run into this
                    // if we have a loop which throws a range check failed exception.  The code for throwing the
                    // exception executes out of line (this is JIT-specific, of course).  The following loop makes sure
                    // that when we interecept the exception, we intercept it at the smallest native offset instead
                    // of intercepting it right before we throw the exception.
                    for (/* no initialization */; pMap > pJitInfo->GetSequenceMap() ; pMap--)
                    {
                        if (pMap->ilOffset == (pMap-1)->ilOffset)
                        {
                            foundOffset = (pMap-1)->nativeStartOffset;
                        }
                        else
                        {
                            break;
                        }
                    }
                    _ASSERTE(foundOffset < relOffset);
#endif // FEATURE_EH_FUNCLETS

                    //
                    // Set up a breakpoint on the intercept IP
                    //
                    DebuggerContinuableExceptionBreakpoint *pBreakpoint;

                    pBreakpoint = new (interopsafe, nothrow) DebuggerContinuableExceptionBreakpoint(pThread,
                                                                                                    foundOffset,
                                                                                                    pJitInfo,
                                                                                                    csi.m_activeFrame.currentAppDomain
                                                                                                   );

                    if (pBreakpoint != NULL)
                    {
                        //
                        // Set up the VM side of intercepting.
                        //
                        StackFrame sfInterceptFramePointer;
                        if (g_isNewExceptionHandlingEnabled)
                        {
                            sfInterceptFramePointer = StackFrame::FromRegDisplay(&(csi.m_activeFrame.registers));
                        }
                        else
                        {
#if defined (TARGET_ARM )|| defined (TARGET_ARM64 )
                            // ARM requires the caller stack pointer, not the current stack pointer
                            sfInterceptFramePointer = CallerStackFrame::FromRegDisplay(&(csi.m_activeFrame.registers));
#else
                            sfInterceptFramePointer = StackFrame::FromRegDisplay(&(csi.m_activeFrame.registers));
#endif
                        }
                        if (pExState->GetDebuggerState()->SetDebuggerInterceptInfo(csi.m_activeFrame.pIJM,
                                                              pThread,
                                                              csi.m_activeFrame.MethodToken,
                                                              csi.m_activeFrame.md,
                                                              foundOffset,
                                                              sfInterceptFramePointer,
                                                              pExState->GetFlags()
                                                             ))
                        {
                            //
                            // Make sure no more exception callbacks come thru.
                            //
                            pExState->GetFlags()->SetSentDebugFirstChance();
                            pExState->GetFlags()->SetSentDebugUserFirstChance();
                            pExState->GetFlags()->SetSentDebugUnwindBegin();

                            //
                            // Save off this breakpoint, so that if the exception gets unwound before we hit
                            // the breakpoint - the exception info can call back to remove it.
                            //
                            pExState->GetDebuggerState()->SetDebuggerInterceptContext((void *)pBreakpoint);

                            hr = S_OK;
                        }
                        else // VM could not set up for intercept
                        {
                            DeleteInteropSafe(pBreakpoint);
                            hr = E_INVALIDARG;
                        }

                    }
                    else // could not allocate for breakpoint
                    {
                        hr = E_OUTOFMEMORY;
                    }

                }
                else // could not get JitInfo
                {
                    hr = E_FAIL;
                }

            }
            else // target frame not found.
            {
                hr = E_INVALIDARG;
            }

        }
        else // already set up for an intercept.
        {
            hr = CORDBG_E_INTERCEPT_FRAME_ALREADY_SET;
        }

    }
    else if (pThread == NULL)
    {
        hr = E_INVALIDARG; // pThread is NULL.
    }
    else
    {
        hr = CORDBG_E_NONINTERCEPTABLE_EXCEPTION;
    }

LSendResponse:

    //
    // Prepare reply
    //
    event = m_pRCThread->GetIPCEventReceiveBuffer();
    InitIPCReply(event, DB_IPCE_INTERCEPT_EXCEPTION_RESULT);
    event->hr = hr;

    //
    // Send reply
    //
    m_pRCThread->SendIPCReply();

    return hr;
}

// Poll & wait for the real helper thread to come up.
// It's possible that the helper thread  is blocked by DllMain, and so we can't
// Wait infinite. If this poll does timeout, then it just means we're likely
// go do helper duty instead of have the real helper do it.
void Debugger::PollWaitingForHelper()
{

    LOG((LF_CORDB, LL_INFO10000, "PollWaitingForHelper() start\n"));

    DebuggerIPCControlBlock * pDCB = g_pRCThread->GetDCB();

    PREFIX_ASSUME(pDCB != NULL);

    int nTotalMSToWait = 8 * 1000;

    // Spin waiting for either the real helper thread or a temp. to be ready.
    // This should never timeout unless the helper is blocked on the loader lock.
    while (!pDCB->m_helperThreadId && !pDCB->m_temporaryHelperThreadId)
    {
        STRESS_LOG1(LF_CORDB,LL_INFO1000, "PollWaitForHelper. %d\n", nTotalMSToWait);

        // If we hold the lock, we'll block the helper thread and this poll is not useful
        _ASSERTE(!ThreadHoldsLock());

        const DWORD dwTime = 50;
        ClrSleepEx(dwTime, FALSE);
        nTotalMSToWait -= dwTime;

        if (nTotalMSToWait <= 0)
        {
            LOG((LF_CORDB, LL_INFO10000, "PollWaitingForHelper() timeout\n"));
            return;
        }
    }

    LOG((LF_CORDB, LL_INFO10000, "PollWaitingForHelper() succeed\n"));
    return;
}




void Debugger::TypeHandleToBasicTypeInfo(AppDomain *pAppDomain, TypeHandle th, DebuggerIPCE_BasicTypeData *res)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "D::THTBTI: converting left-side type handle to basic right-side type info, ELEMENT_TYPE: %d.\n", th.GetSignatureCorElementType()));
    // GetSignatureCorElementType returns E_T_CLASS for E_T_STRING... :-(
    if (th.IsNull())
    {
        res->elementType = ELEMENT_TYPE_VOID;
    }
    else if (th.GetMethodTable() == g_pObjectClass)
    {
        res->elementType = ELEMENT_TYPE_OBJECT;
    }
    else if (th.GetMethodTable() == g_pStringClass)
    {
        res->elementType = ELEMENT_TYPE_STRING;
    }
    else
    {
        res->elementType = th.GetSignatureCorElementType();
    }

    switch (res->elementType)
    {
    case ELEMENT_TYPE_ARRAY:
    case ELEMENT_TYPE_SZARRAY:
    case ELEMENT_TYPE_PTR:
    case ELEMENT_TYPE_FNPTR:
    case ELEMENT_TYPE_BYREF:
        res->vmTypeHandle = WrapTypeHandle(th);
        res->metadataToken = mdTokenNil;
        res->vmDomainAssembly.SetRawPtr(NULL);
        break;

    case ELEMENT_TYPE_CLASS:
    case ELEMENT_TYPE_VALUETYPE:
        {
            res->vmTypeHandle = th.HasInstantiation() ? WrapTypeHandle(th) : VMPTR_TypeHandle::NullPtr();
                                                                             // only set if instantiated
            res->metadataToken = th.GetCl();
            DebuggerModule * pDModule = LookupOrCreateModule(th.GetModule(), pAppDomain);
            res->vmDomainAssembly.SetRawPtr((pDModule ? pDModule->GetDomainAssembly() : NULL));
            break;
        }

    default:
        res->vmTypeHandle = VMPTR_TypeHandle::NullPtr();
        res->metadataToken = mdTokenNil;
        res->vmDomainAssembly.SetRawPtr(NULL);
        break;
    }
    return;
}

void Debugger::TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed,
                                            AppDomain *pAppDomain,
                                            TypeHandle th,
                                            DebuggerIPCE_ExpandedTypeData *res)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    if (th.IsNull())
    {
        res->elementType = ELEMENT_TYPE_VOID;
    }
    else if (th.GetMethodTable() == g_pObjectClass)
    {
        res->elementType = ELEMENT_TYPE_OBJECT;
    }
    else if (th.GetMethodTable() == g_pStringClass)
    {
        res->elementType = ELEMENT_TYPE_STRING;
    }
    else
    {
    LOG((LF_CORDB, LL_INFO10000, "D::THTETI: converting left-side type handle to expanded right-side type info, ELEMENT_TYPE: %d.\n", th.GetSignatureCorElementType()));
    // GetSignatureCorElementType returns E_T_CLASS for E_T_STRING... :-(
        res->elementType = th.GetSignatureCorElementType();
    }

    switch (res->elementType)
    {
    case ELEMENT_TYPE_ARRAY:
    case ELEMENT_TYPE_SZARRAY:
        _ASSERTE(th.IsArray());
        res->ArrayTypeData.arrayRank = th.GetRank();
        TypeHandleToBasicTypeInfo(pAppDomain,
                                  th.GetArrayElementTypeHandle(),
                                  &(res->ArrayTypeData.arrayTypeArg));
        break;

    case ELEMENT_TYPE_PTR:
    case ELEMENT_TYPE_BYREF:
        if (boxed == AllBoxed)
        {
            res->elementType = ELEMENT_TYPE_CLASS;
            goto treatAllValuesAsBoxed;
        }
        _ASSERTE(th.IsTypeDesc());
        TypeHandleToBasicTypeInfo(pAppDomain,
                                  th.AsTypeDesc()->GetTypeParam(),
                                  &(res->UnaryTypeData.unaryTypeArg));
        break;

    case ELEMENT_TYPE_VALUETYPE:
        if (boxed == OnlyPrimitivesUnboxed || boxed == AllBoxed)
            res->elementType = ELEMENT_TYPE_CLASS;
        FALLTHROUGH;

    case ELEMENT_TYPE_CLASS:
        {
treatAllValuesAsBoxed:
            res->ClassTypeData.typeHandle = th.HasInstantiation() ? WrapTypeHandle(th) : VMPTR_TypeHandle::NullPtr(); // only set if instantiated
            res->ClassTypeData.metadataToken = th.GetCl();
            DebuggerModule * pModule = LookupOrCreateModule(th.GetModule(), pAppDomain);
            res->ClassTypeData.vmDomainAssembly.SetRawPtr((pModule ? pModule->GetDomainAssembly() : NULL));
            _ASSERTE(!res->ClassTypeData.vmDomainAssembly.IsNull());
            break;
        }

    case ELEMENT_TYPE_FNPTR:
        {
            if (boxed == AllBoxed)
            {
                res->elementType = ELEMENT_TYPE_CLASS;
                goto treatAllValuesAsBoxed;
            }
            res->NaryTypeData.typeHandle = WrapTypeHandle(th);
            break;
        }
    default:
        // The element type is sufficient, unless the type is effectively a "boxed"
        // primitive value type...
        if (boxed == AllBoxed)
        {
            res->elementType = ELEMENT_TYPE_CLASS;
            goto treatAllValuesAsBoxed;
        }
        break;
    }
    LOG((LF_CORDB, LL_INFO10000, "D::THTETI: converted left-side type handle to expanded right-side type info, res->ClassTypeData.typeHandle = 0x%08x.\n", res->ClassTypeData.typeHandle.GetRawPtr()));
    return;
}


HRESULT Debugger::BasicTypeInfoToTypeHandle(DebuggerIPCE_BasicTypeData *data, TypeHandle *pRes)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "D::BTITTH: expanding basic right-side type to left-side type, ELEMENT_TYPE: %d.\n", data->elementType));
    *pRes = TypeHandle();
    TypeHandle th;
    switch (data->elementType)
    {
    case ELEMENT_TYPE_ARRAY:
    case ELEMENT_TYPE_SZARRAY:
    case ELEMENT_TYPE_PTR:
    case ELEMENT_TYPE_BYREF:
        _ASSERTE(!data->vmTypeHandle.IsNull());
        th = GetTypeHandle(data->vmTypeHandle);
        break;

    case ELEMENT_TYPE_CLASS:
    case ELEMENT_TYPE_VALUETYPE:
        {
            if (!data->vmTypeHandle.IsNull())
            {
                th = GetTypeHandle(data->vmTypeHandle);
            }
            else
            {
                DebuggerModule *pDebuggerModule = g_pDebugger->LookupOrCreateModule(data->vmDomainAssembly);

                th = g_pEEInterface->FindLoadedClass(pDebuggerModule->GetRuntimeModule(), data->metadataToken);
            if (th.IsNull())
            {
                LOG((LF_CORDB, LL_INFO10000, "D::ETITTH: class isn't loaded.\n"));
                    return CORDBG_E_CLASS_NOT_LOADED;
            }

            _ASSERTE(th.GetNumGenericArgs() == 0);
            }
            break;
        }

    case ELEMENT_TYPE_FNPTR:
        {
            _ASSERTE(!data->vmTypeHandle.IsNull());
            th = GetTypeHandle(data->vmTypeHandle);
            break;
        }

    default:
        th = g_pEEInterface->FindLoadedElementType(data->elementType);
        break;
    }
    if (th.IsNull())
        return CORDBG_E_CLASS_NOT_LOADED;
    *pRes = th;
    return S_OK;
}

// Iterate through the type argument data, creating type handles as we go.
void Debugger::TypeDataWalk::ReadTypeHandles(unsigned int nTypeArgs, TypeHandle *ppResults)
{
    WRAPPER_NO_CONTRACT;

    for (unsigned int i = 0; i < nTypeArgs; i++)
        ppResults[i] = ReadTypeHandle();
    }

TypeHandle Debugger::TypeDataWalk::ReadInstantiation(Module *pModule, mdTypeDef tok, unsigned int nTypeArgs)
{
    WRAPPER_NO_CONTRACT;

    DWORD dwAllocSize;
    if (!ClrSafeInt<DWORD>::multiply(nTypeArgs, sizeof(TypeHandle), dwAllocSize))
    {
        ThrowHR(COR_E_OVERFLOW);
    }
    TypeHandle * inst = (TypeHandle *) _alloca(dwAllocSize);
    ReadTypeHandles(nTypeArgs, inst) ;
    TypeHandle th = g_pEEInterface->LoadInstantiation(pModule, tok, nTypeArgs, inst);
    if (th.IsNull())
      COMPlusThrow(kArgumentException, W("Argument_InvalidGenericArg"));
    return th;
}

TypeHandle Debugger::TypeDataWalk::ReadTypeHandle()
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
    }
    CONTRACTL_END;

    DebuggerIPCE_TypeArgData * data = ReadOne();
    if (!data)
      COMPlusThrow(kArgumentException, W("Argument_InvalidGenericArg"));

    LOG((LF_CORDB, LL_INFO10000, "D::ETITTH: expanding right-side type to left-side type, ELEMENT_TYPE: %d.\n", data->data.elementType));

    TypeHandle th;
    CorElementType et = data->data.elementType;
    switch (et)
    {
    case ELEMENT_TYPE_ARRAY:
    case ELEMENT_TYPE_SZARRAY:
    case ELEMENT_TYPE_PTR:
    case ELEMENT_TYPE_BYREF:
        if(data->numTypeArgs == 1)
        {
            TypeHandle typar = ReadTypeHandle();
            switch (et)
            {
            case ELEMENT_TYPE_ARRAY:
            case ELEMENT_TYPE_SZARRAY:
                th = g_pEEInterface->LoadArrayType(data->data.elementType, typar, data->data.ArrayTypeData.arrayRank);
                break;
            case ELEMENT_TYPE_PTR:
            case ELEMENT_TYPE_BYREF:
                th = g_pEEInterface->LoadPointerOrByrefType(data->data.elementType, typar);
                break;
            default:
                _ASSERTE(0);
        }
        }
        break;

    case ELEMENT_TYPE_CLASS:
    case ELEMENT_TYPE_VALUETYPE:
        {
            DebuggerModule *pDebuggerModule = g_pDebugger->LookupOrCreateModule(data->data.ClassTypeData.vmDomainAssembly);
            th = ReadInstantiation(pDebuggerModule->GetRuntimeModule(), data->data.ClassTypeData.metadataToken, data->numTypeArgs);
            break;
        }

    case ELEMENT_TYPE_FNPTR:
        {
            SIZE_T cbAllocSize;
            if ((!ClrSafeInt<SIZE_T>::multiply(data->numTypeArgs, sizeof(TypeHandle), cbAllocSize)) ||
                (cbAllocSize != (size_t)(cbAllocSize)))
            {
                _ASSERTE(COR_E_OVERFLOW);
                cbAllocSize = UINT_MAX;
            }
            TypeHandle * inst = (TypeHandle *) _alloca(cbAllocSize);
            ReadTypeHandles(data->numTypeArgs, inst) ;
            th = g_pEEInterface->LoadFnptrType(inst, data->numTypeArgs);
            break;
        }

    default:
        th = g_pEEInterface->LoadElementType(data->data.elementType);
        break;
    }
    if (th.IsNull())
      COMPlusThrow(kArgumentNullException, W("ArgumentNull_Type"));
    return th;

}

//
// GetAndSendTransitionStubInfo figures out if an address is a stub
// address and sends the result back to the right side.
//
void Debugger::GetAndSendTransitionStubInfo(CORDB_ADDRESS_TYPE *stubAddress)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "D::GASTSI: IsTransitionStub. Addr=0x%08x\n", stubAddress));

    bool result = false;

    result = g_pEEInterface->IsStub((const BYTE *)stubAddress);


    // If its not a stub, then maybe its an address in mscoree?
    if (result == false)
    {
        result = (IsIPInModule(GetClrModuleBase(), (PCODE)stubAddress) == TRUE);
    }

    // This is a synchronous event (reply required)
    DebuggerIPCEvent *event = m_pRCThread->GetIPCEventReceiveBuffer();
    InitIPCEvent(event, DB_IPCE_IS_TRANSITION_STUB_RESULT, NULL, NULL);
    event->IsTransitionStubResult.isStub = result;

    // Send the result
    m_pRCThread->SendIPCReply();
}

/*
 * A generic request for a buffer in the left-side for use by the right-side
 *
 * This is a synchronous event (reply required).
 */
HRESULT Debugger::GetAndSendBuffer(DebuggerRCThread* rcThread, ULONG bufSize)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // This is a synchronous event (reply required)
    DebuggerIPCEvent* event = rcThread->GetIPCEventReceiveBuffer();
    PREFIX_ASSUME(event != NULL);
    InitIPCEvent(event, DB_IPCE_GET_BUFFER_RESULT, NULL, NULL);

    // Allocate the buffer
    event->GetBufferResult.hr = AllocateRemoteBuffer( bufSize, &event->GetBufferResult.pBuffer );

    // Send the result
    return rcThread->SendIPCReply();
}

/*
 * Allocate a buffer in the left-side for use by the right-side
 */
HRESULT Debugger::AllocateRemoteBuffer( ULONG bufSize, void **ppBuffer )
    {
    CONTRACTL
        {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // The call to Append below will call CUnorderedArray, which will call unsafe New.
    HRESULT hr;
    CHECK_IF_CAN_TAKE_HELPER_LOCKS_IN_THIS_SCOPE(&hr, GetCanary());
    if( FAILED(hr) )
            {
        return hr;
            }

    // Actually allocate the buffer
    BYTE* pBuffer = new (interopsafe, nothrow) BYTE[bufSize];

    LOG((LF_CORDB, LL_EVERYTHING, "D::ARB: new'd 0x%x\n", *ppBuffer));

    // Check for out of memory error
    if (pBuffer == NULL)
            {
        return E_OUTOFMEMORY;
        }

    // Track the allocation so we can free it later
    void **ppNextBlob = GetMemBlobs()->Append();
    if( ppNextBlob == NULL )
    {
        DeleteInteropSafe( pBuffer );
        return E_OUTOFMEMORY;
    }
   *ppNextBlob = pBuffer;

   // Return the allocated memory
   *ppBuffer = pBuffer;
   return S_OK;
}

/*
 * Used to release a previously-requested buffer
 *
 * This is a synchronous event (reply required).
 */
HRESULT Debugger::SendReleaseBuffer(DebuggerRCThread* rcThread, void *pBuffer)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB,LL_INFO10000, "D::SRB: Buffer %p\n", pBuffer));

    // This is a synchronous event (reply required)
    DebuggerIPCEvent* event = rcThread->GetIPCEventReceiveBuffer();
    PREFIX_ASSUME(event != NULL);
    InitIPCEvent(event, DB_IPCE_RELEASE_BUFFER_RESULT, NULL, NULL);

    _ASSERTE(pBuffer != NULL);

    // Free the memory
    ReleaseRemoteBuffer(pBuffer, true);

    // Indicate success in reply
    event->ReleaseBufferResult.hr = S_OK;

    // Send the result
    return rcThread->SendIPCReply();
}


//
// Used to delete the buffer previously-requested  by the right side.
// We've factored the code since both the ~Debugger and SendReleaseBuffer
// methods do this.
//
HRESULT Debugger::ReleaseRemoteBuffer(void *pBuffer, bool removeFromBlobList)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_EVERYTHING, "D::RRB: Releasing RS-alloc'd buffer %p\n", pBuffer));

    // Remove the buffer from the blob list if necessary.
    if (removeFromBlobList)
    {
        USHORT cBlobs = GetMemBlobs()->Count();
        void **rgpBlobs = GetMemBlobs()->Table();

        USHORT i;
        for (i = 0; i < cBlobs; i++)
        {
            if (rgpBlobs[i] == pBuffer)
            {
                GetMemBlobs()->DeleteByIndex(i);
                break;
            }
        }

        // We should have found a match.  All buffers passed to ReleaseRemoteBuffer
        // should have been allocated with AllocateRemoteBuffer and not yet freed.
        _ASSERTE( i < cBlobs );
    }

    // Delete the buffer. (Need cast for GCC template support)
    DeleteInteropSafe( (BYTE*)pBuffer );

    return S_OK;
}

#ifndef DACCESS_COMPILE
HRESULT Debugger::DeoptimizeMethodHelper(Module* pModule, mdMethodDef methodDef)
{
    CONTRACTL
    {
        THROWS;
        CAN_TAKE_LOCK;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    _ASSERTE(!CodeVersionManager::IsLockOwnedByCurrentThread());
    HRESULT hr = S_OK;
    ILCodeVersion ilCodeVersion;
    CodeVersionManager *pCodeVersionManager = pModule->GetCodeVersionManager();

    LOG((LF_TIEREDCOMPILATION, LL_INFO100, "Debugger::DeoptimizeMethodHelper Module=%p Method=0x%08x\n",
        pModule, methodDef));

    {
        CodeVersionManager::LockHolder codeVersioningLockHolder;
        if (FAILED(hr = pCodeVersionManager->AddILCodeVersion(pModule, methodDef, &ilCodeVersion, TRUE)))
        {
            LOG((LF_TIEREDCOMPILATION, LL_INFO100, "Debugger::DeoptimizeMethodHelper AddILCodeVersion returned hr 0x%x\n", hr));
            return hr;
        }

        // We are using the profiler ReJIT infrastructure to trigger a new jit. We don't want to modify the IL or
        // call back in to anything so set it all here to match the original IL and debug codegen flags
        ilCodeVersion.SetIL(ILCodeVersion(pModule, methodDef).GetIL());
        ilCodeVersion.SetJitFlags(COR_PRF_CODEGEN_DISABLE_ALL_OPTIMIZATIONS | COR_PRF_CODEGEN_DEBUG_INFO);
        ilCodeVersion.SetRejitState(ILCodeVersion::kStateActive);
        ilCodeVersion.SetEnableReJITCallback(false);
    }

    _ASSERTE(!ilCodeVersion.IsNull());
    {
        if (FAILED(hr = pCodeVersionManager->SetActiveILCodeVersions(&ilCodeVersion, 1, NULL)))
        {
            LOG((LF_TIEREDCOMPILATION, LL_INFO100, "Debugger::DeoptimizeMethodHelper SetActiveILCodeVersions returned hr 0x%x\n",
                hr));
            return hr;
        }
    }

    return hr;
}

HRESULT Debugger::DeoptimizeMethod(Module* pModule, mdMethodDef methodDef)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // First deoptimize the method itself
    HRESULT hr = DeoptimizeMethodHelper(pModule, methodDef);
    if (FAILED(hr))
    {
        LOG((LF_TIEREDCOMPILATION, LL_INFO100, "Debugger::DeoptimizeMethod Module=%p Method=0x%08x, initial ReJIT returned hr 0x%x, aborting\n",
            pModule, methodDef, hr));
        return hr;
    }

    // Now deoptimize anything that has inlined it in a R2R method
    AppDomain::AssemblyIterator domainAssemblyIterator = SystemDomain::System()->DefaultDomain()->IterateAssembliesEx((AssemblyIterationFlags) (kIncludeLoaded | kIncludeExecution));
    CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
    NativeImageInliningIterator inlinerIter;
    while (domainAssemblyIterator.Next(pDomainAssembly.This()))
    {
        Module *pCandidateModule = pDomainAssembly->GetModule();
        if (pCandidateModule->HasReadyToRunInlineTrackingMap())
        {
            inlinerIter.Reset(pCandidateModule, MethodInModule(pModule, methodDef));

            while (inlinerIter.Next())
            {
                MethodInModule inliner = inlinerIter.GetMethod();
                _ASSERTE(TypeFromToken(inliner.m_methodDef) == mdtMethodDef);
                DeoptimizeMethodHelper(inliner.m_module, inliner.m_methodDef);
            }
        }
    }

    // Next any JIT methods
    MethodDesc *pMethodDesc = pModule->LookupMethodDef(methodDef);
    if (pMethodDesc != NULL && pModule->HasJitInlineTrackingMap())
    {
        InlineSArray<MethodDesc *, 10> inliners;
        auto lambda = [&inliners](MethodDesc *inliner, MethodDesc *inlinee)
        {
            _ASSERTE(!inliner->IsNoMetadata());

            if (inliner->IsIL())
            {
                inliners.Append(inliner);
            }

            // Keep going
            return true;
        };

        JITInlineTrackingMap *pMap = pModule->GetJitInlineTrackingMap();
        pMap->VisitInliners(pMethodDesc, lambda);

        for (auto it = inliners.Begin(); it != inliners.End(); ++it)
        {
            Module *inlinerModule = (*it)->GetModule();
            mdMethodDef inlinerMethodDef = (*it)->GetMemberDef();
            _ASSERTE(TypeFromToken(inlinerMethodDef) == mdtMethodDef);
            DeoptimizeMethodHelper(inlinerModule, inlinerMethodDef);
        }
    }

    return hr;
}
#endif //DACCESS_COMPILE

HRESULT Debugger::IsMethodDeoptimized(Module *pModule, mdMethodDef methodDef, BOOL *pResult)
{
    CONTRACTL
    {
        NOTHROW;
        CAN_TAKE_LOCK;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    if (pModule == NULL || pResult == NULL || TypeFromToken(methodDef) != mdtMethodDef)
    {
        return E_INVALIDARG;
    }

    {
        CodeVersionManager::LockHolder codeVersioningLockHolder;
        CodeVersionManager *pCodeVersionManager = pModule->GetCodeVersionManager();
        ILCodeVersion activeILVersion = pCodeVersionManager->GetActiveILCodeVersion(pModule, methodDef);
        *pResult = activeILVersion.IsDeoptimized();
    }

    return S_OK;
}

HRESULT Debugger::UpdateCustomNotificationTable(Module *pModule, mdTypeDef classToken, BOOL enabled)
{
    CONTRACTL
    {
        THROWS;
        CAN_TAKE_LOCK;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    TypeInModule tim(pModule, classToken);
    if (enabled)
    {
        if (m_pCustomNotificationTable->Lookup(tim).IsNull())
        {
            m_pCustomNotificationTable->Add(tim);
        }
    }
    else
    {
        if (!(m_pCustomNotificationTable->Lookup(tim).IsNull()))
        {
            m_pCustomNotificationTable->Remove(tim);
        }
    }

    return S_OK;
}

HRESULT Debugger::UpdateForceCatchHandlerFoundTable(BOOL enableEvents, OBJECTREF exObj, AppDomain *pAppDomain)
{
    CONTRACTL
    {
        THROWS;
        CAN_TAKE_LOCK;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    OBJECTHANDLE objHandle = pAppDomain->CreateLongWeakHandle(exObj);
    if (objHandle == NULL)
    {
        return E_INVALIDARG;
    }
    if (enableEvents)
    {
        OBJECTHANDLE objHandleFound = m_pForceCatchHandlerFoundEventsTable->Lookup(objHandle);
        if (objHandleFound == NULL)
        {
            m_pForceCatchHandlerFoundEventsTable->Add(objHandle);
        }
        else
        {
            DestroyLongWeakHandle(objHandle);
        }
    }
    else
    {
        if (m_pForceCatchHandlerFoundEventsTable->Lookup(objHandle) != NULL)
        {
            m_pForceCatchHandlerFoundEventsTable->Remove(objHandle);
        }
        DestroyLongWeakHandle(objHandle);
    }
    return S_OK;
}

//
// UnrecoverableError causes the Left Side to enter a state where no more
// debugging can occur and we leave around enough information for the
// Right Side to tell what happened.
//
void Debugger::UnrecoverableError(HRESULT errorHR,
                                  unsigned int errorCode,
                                  const char *errorFile,
                                  unsigned int errorLine,
                                  bool exitThread)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10,
         "Unrecoverable error: hr=0x%08x, code=%d, file=%s, line=%d\n",
         errorHR, errorCode, errorFile, errorLine));

    //
    // Setting this will ensure that not much else happens...
    //
    m_unrecoverableError = TRUE;

    //
    // Fill out the control block with the error.
    // in-proc will find out when the function fails
    //
    DebuggerIPCControlBlock *pDCB = m_pRCThread->GetDCB();

    PREFIX_ASSUME(pDCB != NULL);

    pDCB->m_errorHR = errorHR;
    pDCB->m_errorCode = errorCode;

    //
    // If we're told to, exit the thread.
    //
    if (exitThread)
    {
        LOG((LF_CORDB, LL_INFO10,
             "Thread exiting due to unrecoverable error.\n"));
        ExitThread(errorHR);
    }
}

//
// Callback for IsThreadAtSafePlace's stack walk.
//
StackWalkAction Debugger::AtSafePlaceStackWalkCallback(CrawlFrame *pCF,
                                                       VOID* data)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;

        PRECONDITION(CheckPointer(pCF));
        PRECONDITION(CheckPointer(data));
    }
    CONTRACTL_END;

    bool *atSafePlace = (bool*)data;
    LOG((LF_CORDB, LL_INFO100000, "D:AtSafePlaceStackWalkCallback\n"));

    if (pCF->IsFrameless() && pCF->IsActiveFunc())
    {
        LOG((LF_CORDB, LL_INFO1000000, "D:AtSafePlaceStackWalkCallback, IsFrameLess() and IsActiveFunc()\n"));
        if (g_pEEInterface->CrawlFrameIsGcSafe(pCF))
        {
            LOG((LF_CORDB, LL_INFO1000000, "D:AtSafePlaceStackWalkCallback - TRUE: CrawlFrameIsGcSafe()\n"));
            *atSafePlace = true;
        }
    }
    return SWA_ABORT;
}

//
// Determine, via a quick one frame stack walk, if a given thread is
// in a gc safe place.
//
bool Debugger::IsThreadAtSafePlaceWorker(Thread *thread)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;

        PRECONDITION(CheckPointer(thread));
    }
    CONTRACTL_END;

    bool atSafePlace = false;

    // Setup our register display.
    REGDISPLAY rd;
    CONTEXT *context = g_pEEInterface->GetThreadFilterContext(thread);

    _ASSERTE(!(g_pEEInterface->GetThreadFilterContext(thread) && ISREDIRECTEDTHREAD(thread)));
    if (context != NULL)
    {
        g_pEEInterface->InitRegDisplay(thread, &rd, context, TRUE);
    }
    else
    {
        CONTEXT ctx;
        ZeroMemory(&rd, sizeof(rd));
        ZeroMemory(&ctx, sizeof(ctx));
#if defined(TARGET_X86) && !defined(FEATURE_EH_FUNCLETS)
        rd.ControlPC = ctx.Eip;
        rd.PCTAddr = (TADDR)&(ctx.Eip);
#else
        FillRegDisplay(&rd, &ctx);
#endif

        if (ISREDIRECTEDTHREAD(thread))
        {
            thread->GetFrame()->UpdateRegDisplay(&rd);
        }
    }

    // Do the walk. If it fails, we don't care, because we default
    // atSafePlace to false.
    g_pEEInterface->StackWalkFramesEx(
                                 thread,
                                 &rd,
                                 Debugger::AtSafePlaceStackWalkCallback,
                                 (VOID*)(&atSafePlace),
                                 QUICKUNWIND | HANDLESKIPPEDFRAMES |
                                 DISABLE_MISSING_FRAME_DETECTION | SKIP_GSCOOKIE_CHECK);

#ifdef LOGGING
    if (!atSafePlace)
        LOG((LF_CORDB | LF_GC, LL_INFO1000,
             "Thread 0x%x is not at a safe place.\n",
             GetThreadIdHelper(thread)));
#endif

    return atSafePlace;
}

bool Debugger::IsThreadAtSafePlace(Thread *thread)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;

        PRECONDITION(CheckPointer(thread));
    }
    CONTRACTL_END;


    if (m_fShutdownMode)
    {
        return true;
    }

    // <TODO>
    //
    // Make sure this fix is evaluated when doing real work for debugging SO handling.
    //
    // On the Stack Overflow code path calling IsThreadAtSafePlaceWorker as it is
    // currently implemented is way too stack intensive. For now we cheat and just
    // say that if a thread is in the middle of handling a SO it is NOT at a safe
    // place. This is a reasonably safe assumption to make and hopefully shouldn't
    // result in deadlocking the debugger.
    if ( (thread->IsExceptionInProgress()) &&
         (g_pEEInterface->GetThreadException(thread) == CLRException::GetPreallocatedStackOverflowExceptionHandle()) )
    {
        return false;
    }
    // </TODO>
    else
    {
        return IsThreadAtSafePlaceWorker(thread);
    }
}

//-----------------------------------------------------------------------------
// Get the complete user state flags.
// This will collect flags both from the EE and from the LS.
// This is the real implementation of the RS's ICorDebugThread::GetUserState().
//
// Parameters:
//    pThread - non-null thread to get state for.
//
// Returns: a CorDebugUserState flags enum describing state.
//-----------------------------------------------------------------------------
CorDebugUserState Debugger::GetFullUserState(Thread *pThread)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(CheckPointer(pThread));
    }
    CONTRACTL_END;

    CorDebugUserState state = g_pEEInterface->GetPartialUserState(pThread);

    bool fSafe = IsThreadAtSafePlace(pThread);
    if (!fSafe)
    {
        state = (CorDebugUserState) (state | USER_UNSAFE_POINT);
    }

    return state;
}

/******************************************************************************
 *
 * Helper for debugger to get an unique thread id
 *
 ******************************************************************************/
DWORD Debugger::GetThreadIdHelper(Thread *pThread)
{
    WRAPPER_NO_CONTRACT;

    return pThread->GetOSThreadId();
}

//-----------------------------------------------------------------------------
// Called by EnC during remapping to get information about the local vars.
// EnC will then use this to set values in the new version to their corresponding
// values from the old version.
//
// Returns a pointer to the debugger's copies of the maps. Caller
// does not own the memory provided via vars outparameter.
//-----------------------------------------------------------------------------
void Debugger::GetVarInfo(MethodDesc *       fd,   // [IN] method of interest
                    CORDB_ADDRESS nativeCodeAddress,    // [IN] which edit version
                    SIZE_T *           cVars,      // [OUT] size of 'vars'
                    const ICorDebugInfo::NativeVarInfo **vars     // [OUT] map telling where local vars are stored
                    )
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS_FROM_GETJITINFO;
    }
    CONTRACTL_END;

    DebuggerJitInfo * ji = g_pDebugger->GetJitInfo(fd, (const BYTE *)nativeCodeAddress);

    // If we didn't supply a DJI, then we're asking for the most recent version.
    if (ji == NULL)
    {
        ji = GetLatestJitInfoFromMethodDesc(fd);
    }
    _ASSERTE(fd == ji->m_nativeCodeVersion.GetMethodDesc());

    PREFIX_ASSUME(ji != NULL);

    *vars = ji->GetVarNativeInfo();
    *cVars = ji->GetVarNativeInfoCount();
}

#include "openum.h"

#ifdef FEATURE_METADATA_UPDATER

//---------------------------------------------------------------------------------------
//
// Apply an EnC edit to the CLR datastructures and send the result event to the
// debugger right-side.
//
// Arguments:
//    pDebuggerModule  - the module in which the edit should occur
//    cbMetadata       - the number of bytes in pMetadata
//    pMetadata        - pointer to the delta metadata
//    cbIL             - the number of bytes in pIL
//    pIL              - pointer to the delta IL
//
// Return Value:
//
// Assumptions:
//
// Notes:
//
// This is just the first half of processing an EnC request (hot swapping).  This updates
// the metadata and other CLR data structures to reflect the edit, but does not directly
// affect code which is currently running.  In order to achieve on-stack replacement
// (remap of running code), we mine all old methods with "EnC remap breakpoints"
// (instances of DebuggerEnCBreakpoint) at many sequence points.  When one of those
// breakpoints is hit, we give the debugger a RemapOpportunity event and give it a
// chance to remap the execution to the new version of the method.
//

HRESULT Debugger::ApplyChangesAndSendResult(DebuggerModule * pDebuggerModule,
                                            DWORD cbMetadata,
                                            BYTE *pMetadata,
                                            DWORD cbIL,
                                            BYTE *pIL)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // @todo - if EnC never works w/ interop, caller New on the helper thread may be ok.
    SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;

    HRESULT hr = S_OK;

    LOG((LF_ENC, LL_INFO100, "Debugger::ApplyChangesAndSendResult\n"));

    Module *pModule = pDebuggerModule->GetRuntimeModule();
    if (! pModule->IsEditAndContinueEnabled())
    {
        hr =  CORDBG_E_ENC_MODULE_NOT_ENC_ENABLED;
    }
    else
    {
        // Violation with the following call stack:
        //                CONTRACT in MethodTableBuilder::InitMethodDesc
        //                CONTRACT in EEClass::AddMethod
        //                CONTRACT in EditAndContinueModule::AddMethod
        //                CONTRACT in EditAndContinueModule::ApplyEditAndContinue
        //                CONTRACT in EEDbgInterfaceImpl::EnCApplyChanges
        //   VIOLATED-->  CONTRACT in Debugger::ApplyChangesAndSendResult
        CONTRACT_VIOLATION(GCViolation);

        // Tell the VM to apply the edit
        hr = g_pEEInterface->EnCApplyChanges(
            (EditAndContinueModule*)pModule, cbMetadata, pMetadata, cbIL, pIL);
    }

    LOG((LF_ENC, LL_INFO100, "Debugger::ApplyChangesAndSendResult 2\n"));

    DebuggerIPCEvent* event = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(event,
                 DB_IPCE_APPLY_CHANGES_RESULT,
                 NULL,
                 NULL);

    event->ApplyChangesResult.hr = hr;

    // Send the result
    return m_pRCThread->SendIPCEvent();
}

//
// This structure is used to hold a list of the sequence points in a function and
// determine which should have remap breakpoints applied to them for EnC
//
class EnCSequencePointHelper
{
public:
    // Calculates remap info given the supplied JitInfo
    EnCSequencePointHelper(DebuggerJitInfo *pJitInfo);
    ~EnCSequencePointHelper();

    // Returns true if the specified sequence point (given by it's index in the
    // sequence point table in the JitInfo) should get an EnC remap breakpoint.
    BOOL ShouldSetRemapBreakpoint(unsigned int offsetIndex);

private:
    DebuggerJitInfo *m_pJitInfo;

    DebugOffsetToHandlerInfo *m_pOffsetToHandlerInfo;
};

//
// Goes through the list of sequence points for a function and determines whether or not each
// is a valid Remap Breakpoint location (not in a special offset, must be empty stack, and not in a handler.
//
EnCSequencePointHelper::EnCSequencePointHelper(DebuggerJitInfo *pJitInfo)
    : m_pJitInfo(pJitInfo),
    m_pOffsetToHandlerInfo(NULL)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    if (m_pJitInfo->GetSequenceMapCount() == 0)
    {
        return;
    }

    // Construct a list of native offsets we may want to place EnC breakpoints at
    m_pOffsetToHandlerInfo = new DebugOffsetToHandlerInfo[m_pJitInfo->GetSequenceMapCount()];
    for (unsigned int i = 0; i < m_pJitInfo->GetSequenceMapCount(); i++)
    {
        // By default this slot is unused.  We want the indexes in m_pOffsetToHandlerInfo
        // to correspond to the indexes of m_pJitInfo->GetSequenceMapCount, so we rely
        // on a -1 offset to indicate that a DebuggerOffsetToHandlerInfo is unused.
        // However, it would be cleaner and permit a simpler API to the EE if we just
        // had an array mapping the offsets instead.
        m_pOffsetToHandlerInfo[i].offset = (SIZE_T) -1;
        m_pOffsetToHandlerInfo[i].isInFilterOrHandler = FALSE;

        SIZE_T offset = m_pJitInfo->GetSequenceMap()[i].nativeStartOffset;

        // Check if this is a "special" IL offset, such as representing the prolog or eppilog,
        // or other region not directly mapped to native code.
        if (DbgIsSpecialILOffset(pJitInfo->GetSequenceMap()[i].ilOffset))
        {
            LOG((LF_ENC, LL_INFO10000,
                 "D::UF: not placing EnC breakpoint at special offset 0x%zx (IL: 0x%x)\n",
                 offset, m_pJitInfo->GetSequenceMap()[i].ilOffset));
            continue;
        }

        // Skip duplicate sequence points
        if (i >=1 && offset == pJitInfo->GetSequenceMap()[i-1].nativeStartOffset)
        {
            LOG((LF_ENC, LL_INFO10000,
                 "D::UF: not placing redundant EnC breakpoint at duplicate offset 0x%zx (IL: 0x%x)\n",
                 offset, m_pJitInfo->GetSequenceMap()[i].ilOffset));
            continue;
        }

        // Skip sequence points that aren't due to the evaluation stack being empty
        // We can only remap at stack-empty points (since we don't have a mapping for
        // contents of the evaluation stack).
        if (!(pJitInfo->GetSequenceMap()[i].source & ICorDebugInfo::STACK_EMPTY))
        {
            LOG((LF_ENC, LL_INFO10000,
                 "D::UF: not placing EnC breakpoint at offset 0x%zx (IL: 0x%x) b/c not STACK_EMPTY:it's 0x%x\n",
                 offset, m_pJitInfo->GetSequenceMap()[i].ilOffset, pJitInfo->GetSequenceMap()[i].source));
            continue;
        }

        // So far this sequence point looks good, so store it's native offset so we can get
        // EH information about it from the EE.
        LOG((LF_ENC, LL_INFO10000,
             "D::UF: possibly placing EnC breakpoint at offset 0x%zx (IL: 0x%x)\n",
             offset, m_pJitInfo->GetSequenceMap()[i].ilOffset));
        m_pOffsetToHandlerInfo[i].offset = m_pJitInfo->GetSequenceMap()[i].nativeStartOffset;
    }

    // Ask the EE to fill in the isInFilterOrHandler bit for the native offsets we're interested in
    g_pEEInterface->DetermineIfOffsetsInFilterOrHandler(
        (BYTE *)pJitInfo->m_addrOfCode, m_pOffsetToHandlerInfo, m_pJitInfo->GetSequenceMapCount());
}

EnCSequencePointHelper::~EnCSequencePointHelper()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;
    delete[] m_pOffsetToHandlerInfo;
}

//
// Returns if we should set a remap breakpoint at a given offset.  We only set them at 0-depth stack
// and not when inside a handler, either finally, filter, or catch
//
BOOL EnCSequencePointHelper::ShouldSetRemapBreakpoint(unsigned int offsetIndex)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
        CANNOT_TAKE_LOCK;
    }
    CONTRACTL_END;

    {
        // GetSequenceMapCount calls LazyInitBounds() which can eventually
        // call ExecutionManager::IncrementReader
        CONTRACT_VIOLATION(TakesLockViolation);
        _ASSERTE(offsetIndex <= m_pJitInfo->GetSequenceMapCount());
    }

    // If this slot is unused (offset -1), we excluded it early
    if (m_pOffsetToHandlerInfo[offsetIndex].offset == (SIZE_T) -1)
    {
        return FALSE;
    }

    // Otherwise, check the isInFilterOrHandler bit
    if (m_pOffsetToHandlerInfo[offsetIndex].isInFilterOrHandler)
    {
        LOG((LF_ENC, LL_INFO10000,
             "D::UF: not placing EnC breakpoint in filter/handler at offset 0x%zx\n",
             m_pOffsetToHandlerInfo[offsetIndex].offset));
        return FALSE;
    }

    return TRUE;
}


//-----------------------------------------------------------------------------
// For each function that's EnC-ed, the EE will call either UpdateFunction
// (if the function already is loaded + jitted) or AddFunction
//
// This is called before the EE updates the MethodDesc, so pMD does not yet
// point to the version we'll be remapping to.
//-----------------------------------------------------------------------------
HRESULT Debugger::UpdateFunction(MethodDesc* pMD, SIZE_T encVersion)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS_FROM_GETJITINFO;
        PRECONDITION(ThisIsHelperThread()); // guarantees we're serialized.
        PRECONDITION(IsStopped());
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "D::UF: updating %s::%s to encVersion %zx\n",
        pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, encVersion));

    // tell the RS that this function has been updated so that it can create new CorDBFunction
    Module *pModule = g_pEEInterface->MethodDescGetModule(pMD);
    _ASSERTE(pModule != NULL);
    mdToken methodDef = pMD->GetMemberDef();
    SendEnCUpdateEvent(DB_IPCE_ENC_UPDATE_FUNCTION,
                       pModule,
                       methodDef,
                       pMD->GetMethodTable()->GetCl(),
                       encVersion);

    DebuggerMethodInfo *dmi = GetOrCreateMethodInfo(pModule, methodDef);
    if (dmi == NULL)
    {
        return E_OUTOFMEMORY;
    }

    // The DMI always holds the most current EnC version number. We always JIT the most
    // current version of the function, so when we do see a JitBegin we will create a new
    // dji for it and stash the current version there. We don't want to change the current
    // jit info because it has to maintain the version for the code it corresponds to.
    dmi->SetCurrentEnCVersion(encVersion);

    // This is called before the MethodDesc is updated to point to the new function.
    // So this call will get the most recent old function.
    DebuggerJitInfo *pJitInfo = GetLatestJitInfoFromMethodDesc(pMD);

    // We only place the patches if we have jit info for this
    // function, i.e., its already been jitted. Otherwise, the EE will
    // pickup the new method on the next JIT anyway.
    if (pJitInfo == NULL)
    {
        LOG((LF_CORDB,LL_INFO10000,"D::UF: JITted version number (it hasn't been jitted yet), which is fine\n"));
        return S_OK;
    }

    //
    // Mine the old version of the method with patches so that we can provide
    // remap opportunities whenever the old version of the method is executed.
    //

    if (pJitInfo->m_encBreakpointsApplied)
    {
        LOG((LF_CORDB,LL_INFO10000,"D::UF: Breakpoints already applied\n"));
        return S_OK;
    }

    LOG((LF_CORDB,LL_INFO10000,"D::UF: Applying breakpoints\n"));

    EnCSequencePointHelper sequencePointHelper(pJitInfo);

    // For each offset in the IL->Native map, set a new EnC breakpoint on the
    // ones that we know could be remap points.

    // Depending on which DJI was picked, the code might compute different IL offsets. The JIT may not guarantee it produces
    // the same set of sequence points for every generic instantiation.
    // Inside ENCSequencePointHelper there is logic that skips IL offsets that map to the same native offset.
    // Its possible that one version of the code maps two IL offsets to the same native offset but another version of the code maps them to different offsets.
    PTR_DebuggerILToNativeMap seqMap = pJitInfo->GetSequenceMap();
    for (unsigned int i = 0; i < pJitInfo->GetSequenceMapCount(); i++)
    {
        // Skip if this isn't a valid remap point (eg. is in an exception handler)
        if (! sequencePointHelper.ShouldSetRemapBreakpoint(i))
        {
            continue;
        }

        SIZE_T offset = seqMap[i].ilOffset;
        LOG((LF_CORDB, LL_INFO10000, "D::UF: placing EnC breakpoint at offset 0x%x (IL: 0x%x)\n",
            seqMap[i].nativeStartOffset, seqMap[i].ilOffset));

        DebuggerEnCBreakpoint *bp;

        // Create and activate a new EnC remap breakpoint here in the old version of the method
        bp = new (interopsafe) DebuggerEnCBreakpoint( offset,
                                                      pJitInfo,
                                                      DebuggerEnCBreakpoint::REMAP_PENDING,
                                                      AppDomain::GetCurrentDomain());

        _ASSERTE(bp != NULL);
    }

    pJitInfo->m_encBreakpointsApplied = true;

    return S_OK;
}

// Called to update a function that hasn't yet been loaded (and so we don't have a MethodDesc).
// This may be updating an existing function  on a type that hasn't been loaded
// or adding a new function to a type that hasn't been loaded.
// We need to notify the debugger so that it can properly track version info.
HRESULT Debugger::UpdateNotYetLoadedFunction(mdMethodDef token, Module * pModule, SIZE_T encVersion)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;

        PRECONDITION(ThisIsHelperThread());
        PRECONDITION(ThreadHoldsLock()); // must have lock since we're on helper and stopped.
    }
    CONTRACTL_END;

    DebuggerMethodInfo *dmi = GetOrCreateMethodInfo(pModule, token);
    if (! dmi)
    {
        return E_OUTOFMEMORY;
    }
    dmi->SetCurrentEnCVersion(encVersion);


    // Must tell the RS that this function has been added so that it can create new CorDBFunction.
    mdTypeDef classToken = 0;

    HRESULT hr = pModule->GetMDImport()->GetParentToken(token, &classToken);
    if (FAILED(hr))
    {
        // We never expect this to actually fail, but just in case it does for some other strange reason,
        // we'll return before we AV.
        CONSISTENCY_CHECK_MSGF(false, ("Class lookup failed:mdToken:0x%08x, pModule=%p. hr=0x%08x\n", token, pModule, hr));
        return hr;
    }

    SendEnCUpdateEvent(DB_IPCE_ENC_ADD_FUNCTION, pModule, token, classToken, encVersion);


    return S_OK;
}

// Called to add a new function when the type has been loaded already.
// This is effectively the same as above, except that we're given a
// MethodDesc instead of a module and token.
// This should probably be merged into a single method since the caller
// should always have a module and token available in both cases.
HRESULT Debugger::AddFunction(MethodDesc* pMD, SIZE_T encVersion)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;

        PRECONDITION(ThisIsHelperThread());
        PRECONDITION(ThreadHoldsLock()); // must have lock since we're on helper and stopped.
    }
    CONTRACTL_END;

    DebuggerDataLockHolder debuggerDataLockHolder(this);

    LOG((LF_CORDB, LL_INFO10000, "D::AF: adding "
         "%s::%s to version %d\n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, encVersion));

    _ASSERTE(pMD != NULL);
    Module *pModule = g_pEEInterface->MethodDescGetModule(pMD);
    _ASSERTE(pModule != NULL);
    mdToken methodDef = pMD->GetMemberDef();

    // tell the RS that this function has been added so that it can create new CorDBFunction
    SendEnCUpdateEvent( DB_IPCE_ENC_ADD_FUNCTION,
                        pModule,
                        methodDef,
                        pMD->GetMethodTable()->GetCl(),
                        encVersion);

    DebuggerMethodInfo *dmi = CreateMethodInfo(pModule, methodDef);
    if (! dmi)
    {
        return E_OUTOFMEMORY;
    }
    dmi->SetCurrentEnCVersion(encVersion);

    return S_OK;
}

// Invoke when a field is added to a class using EnC
HRESULT Debugger::AddField(FieldDesc* pFD, SIZE_T encVersion)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "D::AFld: adding "
         "TypeDef: 0x%08x FieldDef: 0x%08x to version %zx\n", pFD->GetApproxEnclosingMethodTable()->GetCl(), pFD->GetMemberDef(), encVersion));

    // tell the RS that this field has been added so that it can update it's structures
    SendEnCUpdateEvent( DB_IPCE_ENC_ADD_FIELD,
                        pFD->GetModule(),
                        pFD->GetMemberDef(),
                        pFD->GetApproxEnclosingMethodTable()->GetCl(),
                        encVersion);

    return S_OK;
}

//
// RemapComplete is called when we are just about to resume into
// the function so that we can setup our breakpoint to trigger
// a call to the RemapComplete callback once the function is actually
// on the stack. We need to wait until the function is jitted before
// we can add the trigger, which doesn't happen until we call
// ResumeInUpdatedFunction in the VM
//
// addr is address within the given function, which we use to determine
// exact EnC version.
//
HRESULT Debugger::RemapComplete(MethodDesc* pMD, TADDR addr, SIZE_T nativeOffset)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS_FROM_GETJITINFO;
        PRECONDITION(pMD != NULL);
        PRECONDITION(addr != NULL);
    }
    CONTRACTL_END;

    DebuggerJitInfo *pJitInfo = GetJitInfo(pMD, (const BYTE *) addr);
    if (pJitInfo == NULL)
    {
        _ASSERTE(!"Debugger doesn't handle OOM");
        return E_OUTOFMEMORY;
    }

    LOG((LF_CORDB, LL_INFO10000, "D::RC: installed remap complete patch for %s::%s to encVersion %zx\n",
        pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, pJitInfo->m_encVersion));

    _ASSERTE(pJitInfo->m_addrOfCode + nativeOffset == addr);

    DebuggerEnCBreakpoint *bp;

    // Create and activate a new REMAP_COMPLETE EnC breakpoint to let us know when
    // the EE has completed the remap process.
    // This will be deleted when the patch is hit.
    bp = new (interopsafe, nothrow) DebuggerEnCBreakpoint( nativeOffset,
                                                           pJitInfo,
                                                           DebuggerEnCBreakpoint::REMAP_COMPLETE,
                                                           AppDomain::GetCurrentDomain());
    if (bp == NULL)
    {
        _ASSERTE(!"Debugger doesn't handle OOM");
        return E_OUTOFMEMORY;
    }

    return S_OK;
}

//-----------------------------------------------------------------------------
// Called by EnC stuff to map an IL offset to a native offset for the given
// method described by (pMD, nativeFnxStart).
//
// pMD - methoddesc for method being remapped
// ilOffset - incoming offset in old method to remap.
// nativeFnxStart - address of new function. This can be used to find the DJI
//   for the new method.
// nativeOffset - outparameter for native linear offset relative to start address.
//-----------------------------------------------------------------------------

HRESULT Debugger::MapILInfoToCurrentNative(MethodDesc *pMD,
                                           SIZE_T ilOffset,
                                           TADDR nativeFnxStart,
                                           SIZE_T *nativeOffset)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS_FROM_GETJITINFO;
        PRECONDITION(nativeOffset != NULL);
        PRECONDITION(CheckPointer(pMD));
        PRECONDITION(nativeFnxStart != NULL);
    }
    CONTRACTL_END;

    _ASSERTE(HasLazyData()); // only used for EnC, should have already inited.

    LOG((LF_CORDB, LL_INFO1000000, "D::MILITCN: %s::%s ilOff:0x%zx, natFnx:%p\n",
        pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, ilOffset, nativeFnxStart));

    *nativeOffset = 0;
    DebuggerJitInfo *djiTo = GetJitInfo( pMD, (const BYTE *)nativeFnxStart);
    if (djiTo == NULL)
    {
        _ASSERTE(!"No DJI in EnC case: should only happen on oom. Debugger doesn't support OOM.");
        return E_FAIL;
    }

    DebuggerJitInfo::ILToNativeOffsetIterator it;
    djiTo->InitILToNativeOffsetIterator(it, ilOffset);
    *nativeOffset = it.CurrentAssertOnlyOne(NULL);
    return S_OK;
}

#endif // FEATURE_METADATA_UPDATER

//---------------------------------------------------------------------------------------
// Hijack worker stub called from asm stub. This can then delegate to other hijacks.
//
// Arguments:
//     pContext - context from which we were hijacked. Always non-null.
//     pRecord - exception record if hijacked from an exception event.
//              Else null (if hijacked from a managed IP).
//     reason - hijack reason. Use this to delegate to the proper hijack stub.
//     pData   - arbitrary data for the hijack to use. (eg, such as a DebuggerEval object)
//
// Returns:
//     This does not return. Instead it restores this threads context to pContext.
//
// Assumptions:
//     If hijacked at an exception event, the debugger must have cleared the exception.
//
// Notes:
//     The debugger hijacked the thread to get us here via the DacDbi Hijack primitive.
//     This is called from a hand coded asm stub.
//
void STDCALL ExceptionHijackWorker(
    CONTEXT * pContext,
    EXCEPTION_RECORD * pRecord,
    EHijackReason::EHijackReason reason,
    void * pData)
{
    STRESS_LOG0(LF_CORDB,LL_INFO100, "D::EHW: Enter ExceptionHijackWorker\n");

    // We could have many different reasons for hijacking. Switch and invoke the proper hijacker.
    switch(reason)
    {
        case EHijackReason::kUnhandledException:
            STRESS_LOG0(LF_CORDB,LL_INFO10, "D::EHW: Calling g_pDebugger->UnhandledHijackWorker()\n");
            _ASSERTE(pData == NULL);
            g_pDebugger->UnhandledHijackWorker(pContext, pRecord);
            break;
#ifdef FEATURE_INTEROP_DEBUGGING
    case EHijackReason::kM2UHandoff:
            _ASSERTE(pData == NULL);
            g_pDebugger->M2UHandoffHijackWorker(pContext, pRecord);
            break;
    case EHijackReason::kFirstChanceSuspend:
            _ASSERTE(pData == NULL);
            g_pDebugger->FirstChanceSuspendHijackWorker(pContext, pRecord, FALSE);
            break;
    case EHijackReason::kGenericHijack:
            _ASSERTE(pData == NULL);
            g_pDebugger->GenericHijackFunc();
            break;
#endif
    default:
            CONSISTENCY_CHECK_MSGF(false, ("Unrecognized Hijack code: %d", reason));
    }

    // Currently, no Hijack actually returns yet.
    UNREACHABLE();

    // If we return to this point, then we'll restore ourselves.
    // We've got the context that we were hijacked from, so we should be able to just
    // call SetThreadContext on ourself to fix us.
}

#if defined(FEATURE_EH_FUNCLETS) && !defined(TARGET_UNIX)

#if defined(TARGET_AMD64)
// ----------------------------------------------------------------------------
// EmptyPersonalityRoutine
//
// Description:
//    This personality routine is used to work around a limitation of the OS unwinder when we return
//    ExceptionCollidedUnwind.
//    See code:ExceptionHijackPersonalityRoutine for more information.
//
// Arguments:
//    Standard personality routine signature.
//
// Return Value:
//    Always return ExceptionContinueSearch.
//

EXCEPTION_DISPOSITION EmptyPersonalityRoutine(IN     PEXCEPTION_RECORD   pExceptionRecord,
                                              IN     PVOID               pEstablisherFrame,
                                              IN OUT PCONTEXT            pContextRecord,
                                              IN OUT PDISPATCHER_CONTEXT pDispatcherContext)
{
    LIMITED_METHOD_CONTRACT;
    return ExceptionContinueSearch;
}
#endif // TARGET_AMD64

//---------------------------------------------------------------------------------------
// Personality routine for unwinder the assembly hijack stub on 64-bit.
//
// Arguments:
//    Standard personality routine signature.
//
// Assumptions:
//    This is caleld by the OS exception logic during exception handling.
//
// Notes:
//    We just need 1 personality routine for the tiny assembly hijack stub.
//    All the C++ code invoked by the stub is ok.
//
//    This needs to fetch the original context that this thread was hijacked from
//    (which the hijack pushed onto the stack) and pass that back to the OS. This lets
//    ths OS unwind out of the hijack.
//
//    This function should only be executed if an unhandled exception is intercepted by a managed debugger.
//    Otherwise there should never be a 2nd pass exception dispatch crossing the hijack stub.
//
//    The basic idea here is straightforward.  The OS does an exception dispatch and hit our hijack stub.
//    Since the hijack stub is not unwindable, we need a personality routine to restore the CONTEXT and
//    tell the OS to continue the dispatch with that CONTEXT by returning ExceptionCollidedUnwind.
//
//    However, empricially, the OS expects that when we return ExceptionCollidedUnwind, the function
//    represented by the CONTEXT has a personality routine.  The OS will actually AV if we return a NULL
//    personality routine.
//
//    On AMD64, we work around this by using an empty personality routine.

EXTERN_C EXCEPTION_DISPOSITION
ExceptionHijackPersonalityRoutine(IN     PEXCEPTION_RECORD   pExceptionRecord,
                                  IN     PVOID               pEstablisherFrame,
                                  IN OUT PCONTEXT            pContextRecord,
                                  IN OUT PDISPATCHER_CONTEXT pDispatcherContext
                                 )
{
#if defined(TARGET_AMD64)
    CONTEXT * pHijackContext = NULL;

    // Get the 1st parameter (the Context) from hijack worker.
    // EstablisherFrame points to the stack slot 8 bytes above the
    // return address to the ExceptionHijack. This would contain the
    // parameters passed to ExceptionHijackWorker, which is marked
    // STDCALL, but the x64 calling convention lets the
    // ExceptionHijackWorker use that stack space, resulting in the
    // context being overwritten. Instead, we get the context from the
    // previous stack frame, which contains the arguments to
    // ExceptionHijack, placed there by the debugger in
    // DacDbiInterfaceImpl::Hijack. This works because ExceptionHijack
    // allocates exactly 4 stack slots.
    pHijackContext = *reinterpret_cast<CONTEXT **>(pDispatcherContext->EstablisherFrame + 0x20);

    // This copies pHijackContext into pDispatcherContext, which the OS can then
    // use to walk the stack.
    FixupDispatcherContext(pDispatcherContext, pHijackContext, (PEXCEPTION_ROUTINE)EmptyPersonalityRoutine);
#else
    _ASSERTE(!"NYI - ExceptionHijackPersonalityRoutine()");
#endif

    // Returning ExceptionCollidedUnwind will cause the OS to take our new context record and
    // dispatcher context and restart the exception dispatching on this call frame, which is
    // exactly the behavior we want.
    return ExceptionCollidedUnwind;
}
#endif // FEATURE_EH_FUNCLETS && !TARGET_UNIX


// UEF Prototype from excep.cpp
LONG InternalUnhandledExceptionFilter_Worker(EXCEPTION_POINTERS *pExceptionInfo);

//---------------------------------------------------------------------------------------
// Hijack for a 2nd-chance exception. Will invoke the CLR's UEF.
//
// Arguments:
//     pContext - context that this thread was hijacked from.
//     pRecord - exception record of the exception that this was hijacked at.
//     pData - random data.
// Notes:
// When under a native-debugger, the OS does not invoking the Unhandled Exception Filter (UEF).
// It dispatches a 2nd-chance Exception event instead.
// However, the CLR's UEF does lots of useful work (like dispatching the 2nd-chance managed exception,
// allowing func-eval on 2nd-chance, and allowing intercepting unhandled exceptions).
// So we'll emulate the OS behavior here by invoking the CLR's UEF directly.
//
void Debugger::UnhandledHijackWorker(CONTEXT * pContext, EXCEPTION_RECORD * pRecord)
{
    CONTRACTL
    {
        // The ultimate protection shield is that this hijack can be executed under the same circumstances
        // as a top-level UEF that pinvokes into managed code
        // - That means we're GC-triggers safe
        // - that means that we can crawl the stack. (1st-pass EH logic ensures this).
        // We need to be GC-triggers because this may invoke a func-eval.
        GC_TRIGGERS;

        // Don't throw out of a hijack! There's nobody left to catch this.
        NOTHROW;

        // We expect to always be in preemptive here by the time we get this unhandled notification.
        // We know this is true because a native UEF is preemptive.
        // More detail:
        //   1) If we got here from a software exception (eg, Throw from C#), then the jit helper
        //       toggled us to preemptive before calling RaiseException().
        //   2) If we got here from a hardware exception in managed code, then the 1st-pass already did
        //       some magic to get us into preemptive. On x86, this is magic. On 64-bit, it did some magic
        //       to push a Faulting-Exception-Frame and rethrow the exception as a software exception.
        MODE_PREEMPTIVE;


        PRECONDITION(CheckPointer(pContext));
        PRECONDITION(CheckPointer(pRecord));
    }
    CONTRACTL_END;

    EXCEPTION_POINTERS exceptionInfo;
    exceptionInfo.ContextRecord = pContext;
    exceptionInfo.ExceptionRecord = pRecord;

    // Snag the Runtime thread. Since we're hijacking a managed exception, we should always have one.
    Thread * pThread = g_pEEInterface->GetThread();
    (void)pThread; //prevent "unused variable" error from GCC
    _ASSERTE(pThread != NULL);

    BOOL fSOException = FALSE;

    if ((pRecord != NULL) &&
        (pRecord->ExceptionCode == STATUS_STACK_OVERFLOW))
    {
        fSOException = TRUE;
    }

    // because we hijack here during jit attach invoked by the OS we need to make sure that the debugger is completely
    // attached before continuing. If we ever hijacked here when an attach was not in progress this function returns
    // immediately so no problems there.
    WaitForDebuggerAttach();
    PostJitAttach();

    // On Win7 WatsonLastChance returns CONTINUE_SEARCH for unhandled exceptions execpt stack overflow, and
    // lets OS launch debuggers for us.  Before the unhandled exception reaches the OS, CLR UEF has already
    // processed this unhandled exception.  Thus, we should not call into CLR UEF again if it is the case.
    if (pThread &&
        (pThread->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException) ||
         fSOException))
    {

        FrameWithCookie<FaultingExceptionFrame> fef;
#if defined(FEATURE_EH_FUNCLETS)
        *((&fef)->GetGSCookiePtr()) = GetProcessGSCookie();
#endif // FEATURE_EH_FUNCLETS
        if ((pContext != NULL) && fSOException)
        {
            GCX_COOP();     // Must be cooperative to modify frame chain.

            // EEPolicy::HandleFatalStackOverflow pushes a FaultingExceptionFrame on the stack after SO
            // exception.  Our hijack code runs in the exception context, and overwrites the stack space
            // after SO exception, so this frame was popped out before invoking RaiseFailFast.  We need to
            // put it back here for running func-eval code.
            // This cumbersome code should be removed once SO synchronization is moved to be completely
            // out-of-process.
            fef.InitAndLink(pContext);
        }

        STRESS_LOG0(LF_CORDB, LL_INFO10, "D::EHW: Calling NotifyDebuggerLastChance\n");
        NotifyDebuggerLastChance(pThread, &exceptionInfo, TRUE);

        // Continuing from a second chance managed exception causes the process to exit.
        TerminateProcess(GetCurrentProcess(), 0);
    }

    // Since this is a unhandled managed exception:
    // - we always have a Thread* object.
    // - we always have a throwable
    // - we executed through the 1st-pass of the EH logic. This means the 1st-pass could do work
    //   to enforce certain invariants (like the ones listed here, or ensuring the thread can be crawled)

    // Need to call the CLR's UEF. This will do all the key work including:
    // - send the managed 2nd-chance exception event.
    // - deal with synchronization.
    // - allow func-evals.
    // - deal with interception.

    // If intercepted, then this never returns. It will manually invoke the unwinders and fix the context.

    // InternalUnhandledExceptionFilter_Worker has a throws contract, but should not be throwing in any
    // conditions we care about. This hijack should never throw, so catch everything.
    HRESULT hrIgnore;
    EX_TRY
    {
        InternalUnhandledExceptionFilter_Worker(&exceptionInfo);
    }
    EX_CATCH_HRESULT(hrIgnore);

    // Continuing from a second chance managed exception causes the process to exit.
    TerminateProcess(GetCurrentProcess(), 0);
}

#ifdef FEATURE_INTEROP_DEBUGGING
//
// This is the handler function that is put in place of a thread's top-most SEH handler function when it is hijacked by
// the Right Side during an unmanaged first chance exception.
//
typedef EXCEPTION_DISPOSITION (__cdecl *SEHHandler)(EXCEPTION_RECORD *pExceptionRecord,
                             EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame,
                             CONTEXT *pContext,
                             void *DispatcherContext);
#define DOSPEW 0

#if DOSPEW
#define SPEW(s) s
#else
#define SPEW(s)
#endif




//-----------------------------------------------------------------------------
// Hijack when we have a M2U handoff.
// This happens when we do a step-out from Managed-->Unmanaged, and so we hit a managed patch in Native code.
// This also happens when a managed stepper does a step-in to unmanaged code.
// Since we're in native code, there's no CPFH, and so we have to hijack.
// @todo-  could this be removed? Step-out to native is illegal in v2.0, and do existing
// CLR filters catch the step-in patch?
// @dbgtodo  controller/stepping - this will be completely unneeded in V3 when all stepping is oop
//-----------------------------------------------------------------------------
VOID Debugger::M2UHandoffHijackWorker(CONTEXT *pContext,
                                      EXCEPTION_RECORD *pExceptionRecord)
{
    // We must use a static contract here because the function does not return normally
    STATIC_CONTRACT_NOTHROW;
    STATIC_CONTRACT_GC_TRIGGERS; // from sending managed event
    STATIC_CONTRACT_MODE_PREEMPTIVE; // we're in umanaged code.

    LOG((LF_CORDB, LL_INFO1000, "D::M2UHHW: Context=0x%p exception record=0x%p\n",
        pContext, pExceptionRecord));

    // We should only be here for a BP
    _ASSERTE(pExceptionRecord->ExceptionCode == STATUS_BREAKPOINT);

    // Get the current runtime thread. This is only an optimized TLS access.
    // Since we're coming off a managed-step, we should always have a thread.
    Thread *pEEThread = g_pEEInterface->GetThread();
    _ASSERTE(pEEThread != NULL);

    _ASSERTE(!pEEThread->GetInteropDebuggingHijacked());
    pEEThread->SetInteropDebuggingHijacked(TRUE);

    //win32 has a weird property where EIP points after the BP in the debug event
    //so we are adjusting it to point at the BP
    CORDbgAdjustPCForBreakInstruction((DT_CONTEXT*)pContext);
    LOG((LF_CORDB, LL_INFO1000, "D::M2UHHW: Context ip set to 0x%p\n", GetIP(pContext)));

    _ASSERTE(!ISREDIRECTEDTHREAD(pEEThread));

    // Don't bother setting FilterContext here because we already pass it to FirstChanceNativeException.
    // Shortcut right to our dispatch native exception logic, there may be no COMPlusFrameHandler in place!
    EX_TRY
    {
        LOG((LF_CORDB, LL_INFO1000, "D::M2UHHW: Calling FirstChanceNativeException\n"));
        bool okay;
        okay = g_pDebugger->FirstChanceNativeException(pExceptionRecord,
            pContext,
            pExceptionRecord->ExceptionCode,
            pEEThread,
            FALSE);
        _ASSERTE(okay == true);
        LOG((LF_CORDB, LL_INFO1000, "D::M2UHHW: FirstChanceNativeException returned\n"));
    }
    EX_CATCH
    {
        // It would be really bad if somebody threw here. We're actually outside of managed code,
        // so there's not a lot we can do besides just swallow the exception and hope for the best.
        LOG((LF_CORDB, LL_INFO1000, "D::M2UHHW: ERROR! FirstChanceNativeException threw an exception\n"));
    }
    EX_END_CATCH(SwallowAllExceptions);

    _ASSERTE(!ISREDIRECTEDTHREAD(pEEThread));
    _ASSERTE(pEEThread->GetInteropDebuggingHijacked());
    pEEThread->SetInteropDebuggingHijacked(FALSE);

    // This signal will be received by the RS and it will use SetThreadContext
    // to clear away the entire hijack frame. This function does not return.
    LOG((LF_CORDB, LL_INFO1000, "D::M2UHHW: Flaring hijack complete\n"));
    SignalHijackComplete();

    _ASSERTE(!"UNREACHABLE");
}

//-----------------------------------------------------------------------------
// This hijack is run after receiving an IB event that we don't know how the
// debugger will want to continue. Under the covers we clear the event and divert
// execution here where we block until the debugger decides whether or not to clear
// the event. At that point we exit this hijack and the LS diverts execution back
// to the offending instruction.
// We don't know:
// - whether we have an EE-thread?
// - how we're going to continue this (handled / not-handled).
//
// But we do know that:
// - this exception does not belong to the CLR.
// - this thread is not in cooperative mode.
//-----------------------------------------------------------------------------
LONG Debugger::FirstChanceSuspendHijackWorker(CONTEXT *pContext,
                                              EXCEPTION_RECORD *pExceptionRecord,
                                              BOOL fIsVEH)
{
    // if we aren't set up to do interop debugging this function should just bail out
    if(m_pRCThread == NULL)
        return EXCEPTION_CONTINUE_SEARCH;

    DebuggerIPCControlBlock *pDCB = m_pRCThread->GetDCB();
    if(pDCB == NULL)
        return EXCEPTION_CONTINUE_SEARCH;

    if (!pDCB->m_rightSideIsWin32Debugger)
        return EXCEPTION_CONTINUE_SEARCH;

    // at this point we know that there is an interop debugger attached. This makes it safe to send
    // flares
#if DOSPEW
    DWORD tid = GetCurrentThreadId();
#endif

    SPEW(fprintf(stderr, "0x%x D::FCHF: in first chance hijack filter.\n", tid));
    SPEW(fprintf(stderr, "0x%x D::FCHF: pExceptionRecord=0x%p (%d), pContext=0x%p (%d)\n", tid, pExceptionRecord, sizeof(EXCEPTION_RECORD),
        pContext, sizeof(CONTEXT)));
#if defined(TARGET_AMD64)
    SPEW(fprintf(stderr, "0x%x D::FCHF: code=0x%08x, addr=0x%p, Rip=0x%p, Rsp=0x%p, EFlags=0x%08x\n",
        tid, pExceptionRecord->ExceptionCode, pExceptionRecord->ExceptionAddress, pContext->Rip, pContext->Rsp,
        pContext->EFlags));
#elif defined(TARGET_X86)
    SPEW(fprintf(stderr, "0x%x D::FCHF: code=0x%08x, addr=0x%08x, Eip=0x%08x, Esp=0x%08x, EFlags=0x%08x\n",
        tid, pExceptionRecord->ExceptionCode, pExceptionRecord->ExceptionAddress, pContext->Eip, pContext->Esp,
        pContext->EFlags));
#elif defined(TARGET_ARM64)
    SPEW(fprintf(stderr, "0x%x D::FCHF: code=0x%08x, addr=0x%08x, Pc=0x%p, Sp=0x%p, EFlags=0x%08x\n",
        tid, pExceptionRecord->ExceptionCode, pExceptionRecord->ExceptionAddress, pContext->Pc, pContext->Sp,
        pContext->EFlags));
#elif defined(TARGET_LOONGARCH64)
    SPEW(fprintf(stderr, "0x%x D::FCHF: code=0x%08x, addr=0x%08x, Pc=0x%p, Sp=0x%p\n",
        tid, pExceptionRecord->ExceptionCode, pExceptionRecord->ExceptionAddress, pContext->Pc, pContext->Sp));
#endif

    // This memory is used as IPC during the hijack. We will place a pointer to this in
    // the EE debugger word (a TLS slot that works even on the debugger break-in thread)
    // and then the RS can write info into the memory.
    DebuggerIPCFirstChanceData fcd;

    // Accessing through the volatile pointer to fend off some potential compiler optimizations.
    // If the debugger changes that data from OOP we need to see those updates
    volatile DebuggerIPCFirstChanceData* pFcd = &fcd;

    // The Windows native break in thread does not have TLS storage allocated.
    bool debuggerBreakInThread = (NtCurrentTeb()->ThreadLocalStoragePointer == NULL);
    {
        // Hijack filters are always in the can't stop range.
        // The RS knows this b/c it knows which threads it hijacked.
        // Bump up the CS counter so that any further calls in the LS can see this too.
        // (This makes places where we assert that we're in a CS region happy).
        CantStopHolder hCantStop(!debuggerBreakInThread);

        // Get the current runtime thread. This is only an optimized TLS access.
        Thread *pEEThread = debuggerBreakInThread ? NULL : g_pEEInterface->GetThread();

        // Hook up the memory so RS can get to it
        fcd.pLeftSideContext.Set((DT_CONTEXT*)pContext);
        fcd.action = HIJACK_ACTION_EXIT_UNHANDLED;
        fcd.debugCounter = 0;

        SPEW(fprintf(stderr, "0x%x D::FCHF: Set debugger word to 0x%p.\n", tid, pFcd));
        g_pEEInterface->SetThreadDebuggerWord((VOID*)pFcd);

        // Signal the RS to tell us what to do
        SPEW(fprintf(stderr, "0x%x D::FCHF: Signaling hijack started.\n", tid));
        SignalHijackStarted();
        SPEW(fprintf(stderr, "0x%x D::FCHF: Signaling hijack started complete. DebugCounter=0x%x\n", tid, pFcd->debugCounter));

        if (pFcd->action == HIJACK_ACTION_WAIT)
        {
            // This exception does NOT belong to the CLR.
            // If we belong to the CLR, then we either:
            // - were a  M2U transition, in which case we should be in a different Hijack
            // - were a CLR exception in CLR code, in which case we should have continued and let the inproc handlers get it.
            SPEW(fprintf(stderr, "0x%x D::FCHF: exception does not belong to the Runtime, pEEThread=0x%p, pContext=0x%p\n",
                         tid, pEEThread, pContext));

            if (pEEThread != NULL)
            {
                _ASSERTE(!pEEThread->GetInteropDebuggingHijacked()); // hijack is not re-entrant.
                pEEThread->SetInteropDebuggingHijacked(TRUE);

                // Setting the FilterContext must be done in cooperative mode (since it's like pushing a Frame onto the Frame chain).
                // Thus we have a violation. We don't really need the filter context specifically here, we're just using
                // it for legacy purposes as a way to stash the context of the original exception (that this thread was hijacked from).
                // @todo - use another way to store the context independent of the Filter context.
                CONTRACT_VIOLATION(ModeViolation);
                _ASSERTE(g_pEEInterface->GetThreadFilterContext(pEEThread) == NULL);
                g_pEEInterface->SetThreadFilterContext(pEEThread, pContext);
            }

            // Wait for the continue. We may / may not have an EE Thread for this, (and we're definitely
            // not doing fiber-mode debugging), so just use a raw win32 API, and not some fancy fiber-safe call.
            SPEW(fprintf(stderr, "0x%x D::FCHF: waiting for continue.\n", tid));
            DWORD ret = WaitForSingleObject(g_pDebugger->m_pRCThread->GetDCB()->m_leftSideUnmanagedWaitEvent, INFINITE);
            SPEW(fprintf(stderr, "0x%x D::FCHF: waiting for continue complete.\n", tid));

            if (ret != WAIT_OBJECT_0)
            {
                SPEW(fprintf(stderr, "0x%x D::FCHF: wait failed!\n", tid));
            }

            if (pEEThread != NULL)
            {
                _ASSERTE(pEEThread->GetInteropDebuggingHijacked());
                pEEThread->SetInteropDebuggingHijacked(FALSE);
                _ASSERTE(!ISREDIRECTEDTHREAD(pEEThread));

                // See violation above.
                CONTRACT_VIOLATION(ModeViolation);
                g_pEEInterface->SetThreadFilterContext(pEEThread, NULL);
                _ASSERTE(g_pEEInterface->GetThreadFilterContext(pEEThread) == NULL);
            }
        }

        SPEW(fprintf(stderr, "0x%x D::FCHF: signaling HijackComplete.\n", tid));
        SignalHijackComplete();
        SPEW(fprintf(stderr, "0x%x D::FCHF: done signaling HijackComplete. DebugCounter=0x%x\n", tid, pFcd->debugCounter));

        // we should know what we are about to do now
        _ASSERTE(pFcd->action != HIJACK_ACTION_WAIT);

        // cleanup from above
        SPEW(fprintf(stderr, "0x%x D::FCHF: set debugger word = NULL.\n", tid));
        g_pEEInterface->SetThreadDebuggerWord(NULL);

    } // end can't stop region

    if (pFcd->action == HIJACK_ACTION_EXIT_HANDLED)
    {
        SPEW(fprintf(stderr, "0x%x D::FCHF: exiting with CONTINUE_EXECUTION\n", tid));
#if defined(OUT_OF_PROCESS_SETTHREADCONTEXT) && !defined(DACCESS_COMPILE)
        if (fIsVEH)
        {
            SendSetThreadContextNeeded(pContext);
        }
#endif
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    else
    {
        SPEW(fprintf(stderr, "0x%x D::FCHF: exiting with CONTINUE_SEARCH\n", tid));
        _ASSERTE(pFcd->action == HIJACK_ACTION_EXIT_UNHANDLED);
        return EXCEPTION_CONTINUE_SEARCH;
    }
}

#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM64)
void GenericHijackFuncHelper()
{
#if DOSPEW
    DWORD tid = GetCurrentThreadId();
#endif

    // The Windows native break in thread does not have TLS storage allocated.
    bool debuggerBreakInThread = (NtCurrentTeb()->ThreadLocalStoragePointer == NULL);

    // Hijack filters are always in the can't stop range.
    // The RS knows this b/c it knows which threads it hijacked.
    // Bump up the CS counter so that any further calls in the LS can see this too.
    // (This makes places where we assert that we're in a CS region happy).
    CantStopHolder hCantStop(!debuggerBreakInThread);

    SPEW(fprintf(stderr, "0x%x D::GHF: in generic hijack.\n", tid));

    // There is no need to setup any context pointer or interact with the Right Side in anyway. We simply wait for
    // the continue event to be set.
    SPEW(fprintf(stderr, "0x%x D::GHF: waiting for continue.\n", tid));

    // If this thread has an EE thread and that EE thread has preemptive gc disabled, then mark that there is a
    // thread at an unsafe place and enable pgc. This will allow us to sync even with this thread hijacked.
    bool disabled = false;

    Thread *pEEThread = debuggerBreakInThread ? NULL : g_pEEInterface->GetThread();

    if (pEEThread != NULL)
    {
        disabled = g_pEEInterface->IsPreemptiveGCDisabled();
        _ASSERTE(!disabled);

        _ASSERTE(!pEEThread->GetInteropDebuggingHijacked());
        pEEThread->SetInteropDebuggingHijacked(TRUE);
    }

    DWORD ret = WaitForSingleObject(g_pRCThread->GetDCB()->m_leftSideUnmanagedWaitEvent,
                                    INFINITE);

    if (ret != WAIT_OBJECT_0)
    {
        SPEW(fprintf(stderr, "0x%x D::GHF: wait failed!\n", tid));
    }

    // Get the continue type. Non-zero means that the exception was not cleared by the Right Side and therefore has
    // not been handled. Zero means that the exception has been cleared. (Presumably, the debugger altered the
    // thread's context before clearing the exception, so continuing will give a different result.)
    DWORD continueType = 0;

    void* threadDebuggerWord = g_pEEInterface->GetThreadDebuggerWord();

    if (pEEThread != NULL)
    {
        // We've got a Thread ptr, so get the continue type out of the thread's debugger word.
        continueType = (DWORD)(size_t) threadDebuggerWord;

        _ASSERTE(pEEThread->GetInteropDebuggingHijacked());
        pEEThread->SetInteropDebuggingHijacked(FALSE);
    }
    else if (threadDebuggerWord != NULL)
    {
        continueType = 1;
        g_pEEInterface->SetThreadDebuggerWord(NULL);
    }

    SPEW(fprintf(stderr, "0x%x D::GHF: continued with %d.\n", tid, continueType));

    if (continueType)
    {
        SPEW(fprintf(stderr, "0x%x D::GHF: calling ExitProcess\n", tid));

        // Continuing from a second chance exception without clearing the exception causes the process to
        // exit. Note: the continue type will only be non-zero if this hijack was setup for a second chance
        // exception. If the hijack was setup for another type of debug event, then we'll never get here.
        //
        // We explicitly terminate the process directly instead of going through any escalation policy because:
        // 1) that's what a native-only debugger would do. Interop and Native-only should be the same.
        // 2) there's no CLR escalation policy anyways for *native* unhandled exceptions.
        // 3) The escalation policy may do lots of extra confusing work (like fire MDAs) that can only cause
        // us grief.
        TerminateProcess(GetCurrentProcess(), 0);
    }

    SPEW(fprintf(stderr, "0x%x D::GHF: signaling continue...\n", tid));
}
#endif


//
// This is the function that a thread is hijacked to by the Right Side during a variety of debug events. This function
// must be naked.
//
#if defined(TARGET_X86)
#ifdef TARGET_WINDOWS
__declspec(naked)
#else
__attribute__((naked))
#endif
#endif // defined (_x86_)
void Debugger::GenericHijackFunc(void)
{
#if defined(TARGET_X86) || defined(TARGET_AMD64)

#if defined(TARGET_X86)
    _asm
    {
        push ebp
        mov  ebp,esp
        sub  esp,__LOCAL_SIZE
    }
#endif
    // We can't have C++ classes w/ dtors in a declspec naked, so just have call into a helper.
    GenericHijackFuncHelper();

#if defined(TARGET_X86)
    _asm
    {
        mov esp,ebp
        pop ebp
    }
#endif

    // This signals the Right Side that this thread is ready to have its context restored.
    ExceptionNotForRuntime();

#else
    _ASSERTE(!"@todo - port GenericHijackFunc");
#endif // defined (_x86_)

    _ASSERTE(!"Should never get here (Debugger::GenericHijackFunc)");
}




//#ifdef TARGET_X86
//
// This is the function that is called when we determine that a first chance exception hijack has
// begun and memory is prepared for the RS to tell the LS what to do
//
void Debugger::SignalHijackStarted(void)
{
    WRAPPER_NO_CONTRACT;

#if defined(FEATURE_INTEROP_DEBUGGING)
    SignalHijackStartedFlare();
#else
    _ASSERTE(!"@todo - port the flares to the platform your running on.");
#endif
}

//
// This function is called when we determine that a first chance exception really belongs to the Runtime,
// and that the exception is due to a managed->unmanaged transition. This notifies the Right Side which
// fixes up the thread's execution state from there, making sure to remember that it needs to continue
// to hide the hijack state of the thread.
//
void Debugger::ExceptionForRuntimeHandoffStart(void)
{
    WRAPPER_NO_CONTRACT;

#if defined(FEATURE_INTEROP_DEBUGGING)
    ExceptionForRuntimeHandoffStartFlare();
#else
    _ASSERTE(!"@todo - port the flares to the platform your running on.");
#endif

}

//
// This is the function that is called when the original handler returns after we've determined that an exception was
// due to a managed->unmanaged transition. This notifies the Right Side of this and the Right Side fixes up the thread's
// execution state from there, making sure to turn off its flag indicating that the thread's hijack state should still
// be hidden.
//
void Debugger::ExceptionForRuntimeHandoffComplete(void)
{
    WRAPPER_NO_CONTRACT;

#if defined(FEATURE_INTEROP_DEBUGGING)
    ExceptionForRuntimeHandoffCompleteFlare();
#else
    _ASSERTE(!"@todo - port the flares to the platform your running on.");
#endif

}

//
// This signals the RS that a hijack function is ready to return. This will cause the RS to restore
// the thread context
//
void Debugger::SignalHijackComplete(void)
{
    WRAPPER_NO_CONTRACT;

#if defined(FEATURE_INTEROP_DEBUGGING)
    SignalHijackCompleteFlare();
#else
    _ASSERTE(!"@todo - port the flares to the platform your running on.");
#endif

}

//
// This is the function that is called when we determine that a first chance exception does not belong to the
// Runtime. This notifies the Right Side of this and the Right Side fixes up the thread's execution state from there.
//
void Debugger::ExceptionNotForRuntime(void)
{
    WRAPPER_NO_CONTRACT;

#if defined(FEATURE_INTEROP_DEBUGGING)
    ExceptionNotForRuntimeFlare();
#else
    _ASSERTE(!"@todo - port the flares to the platform your running on.");
#endif
}

//
// This is the function that is called when we want to send a sync complete event to the Right Side when it is the Win32
// debugger of this process. This notifies the Right Side of this and the Right Side fixes up the thread's execution
// state from there.
//
void Debugger::NotifyRightSideOfSyncComplete(void)
{
    WRAPPER_NO_CONTRACT;
    STRESS_LOG0(LF_CORDB, LL_INFO100000, "D::NRSOSC: Sending flare...\n");
#if defined(FEATURE_INTEROP_DEBUGGING)
    NotifyRightSideOfSyncCompleteFlare();
#else
    _ASSERTE(!"@todo - port the flares to the platform your running on.");
#endif
    STRESS_LOG0(LF_CORDB, LL_INFO100000, "D::NRSOSC: Flare sent\n");
}

#endif // FEATURE_INTEROP_DEBUGGING

/******************************************************************************
 *
 ******************************************************************************/
bool Debugger::GetILOffsetFromNative (MethodDesc *pFunc, const BYTE *pbAddr,
                                      DWORD nativeOffset, DWORD *ilOffset)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS_FROM_GETJITINFO;
    }
    CONTRACTL_END;

    _ASSERTE(pFunc != NULL);
    _ASSERTE(pbAddr != NULL);

    if (!HasLazyData())
    {
        DebuggerLockHolder dbgLockHolder(this);
        // This is an entry path into the debugger, so make sure we're inited.
        LazyInit();
    }

    // Sometimes we'll get called w/ an instantiating stub MD.
    if (pFunc->IsWrapperStub())
    {
        pFunc = pFunc->GetWrappedMethodDesc();
    }

    if (pFunc->IsDynamicMethod())
    {
        return false;
    }

    DebuggerMethodInfo *methodInfo = GetOrCreateMethodInfo(pFunc->GetModule(), pFunc->GetMemberDef());
    if (methodInfo == NULL)
    {
        return false;
    }

    PCODE methodStartAddress = g_pEEInterface->GetNativeCodeStartAddress((PCODE)pbAddr);
    if (methodStartAddress == (PCODE)NULL)
    {
        return false;
    }

    DebuggerJitInfo *jitInfo = methodInfo->FindOrCreateInitAndAddJitInfo(pFunc, methodStartAddress);
    if (jitInfo == NULL)
    {
        return false;
    }

    CorDebugMappingResult map;
    DWORD whichIDontCare;
    *ilOffset = jitInfo->MapNativeOffsetToIL(
                                    nativeOffset,
                                    &map,
                                    &whichIDontCare);
    return true;
}

/******************************************************************************
 *
 ******************************************************************************/
DWORD Debugger::GetHelperThreadID(void )
{
    LIMITED_METHOD_CONTRACT;

    return m_pRCThread ? m_pRCThread->GetDCB()->m_temporaryHelperThreadId : 0;
}


// HRESULT Debugger::InsertToMethodInfoList():  Make sure
//  that there's only one head of the list of DebuggerMethodInfos
//  for the (implicitly) given MethodDef/Module pair.
HRESULT
Debugger::InsertToMethodInfoList( DebuggerMethodInfo *dmi )
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    LOG((LF_CORDB,LL_INFO10000,"D:IAHOL DMI: dmi:0x%p\n", dmi));

    HRESULT hr = S_OK;

    _ASSERTE(dmi != NULL);

    _ASSERTE(HasDebuggerDataLock());

    //    CHECK_DJI_TABLE_DEBUGGER;

    hr = CheckInitMethodInfoTable();

    if (FAILED(hr)) {
        return (hr);
    }

    DebuggerMethodInfo *dmiPrev = m_pMethodInfos->GetMethodInfo(dmi->m_module, dmi->m_token);

    _ASSERTE((dmiPrev == NULL) || ((dmi->m_token == dmiPrev->m_token) && (dmi->m_module == dmiPrev->m_module)));

    LOG((LF_CORDB,LL_INFO10000,"D:IAHOL: current head of dmi list:0x%p\n",dmiPrev));

    if (dmiPrev != NULL)
    {
        dmi->m_prevMethodInfo = dmiPrev;
        dmiPrev->m_nextMethodInfo = dmi;

        _ASSERTE(dmi->m_module != NULL);
        hr = m_pMethodInfos->OverwriteMethodInfo(dmi->m_module,
                                         dmi->m_token,
                                         dmi,
                                         FALSE);

        LOG((LF_CORDB,LL_INFO10000,"D:IAHOL: DMI version 0x%04x for token 0x%08x\n",
            dmi->GetCurrentEnCVersion(),dmi->m_token));
    }
    else
    {
        LOG((LF_CORDB, LL_EVERYTHING, "D:IAHOL: AddMethodInfo called\n"));
        hr = m_pMethodInfos->AddMethodInfo(dmi->m_module,
                                         dmi->m_token,
                                         dmi);
    }
#ifdef _DEBUG
    dmiPrev = m_pMethodInfos->GetMethodInfo(dmi->m_module, dmi->m_token);
    LOG((LF_CORDB,LL_INFO10000,"D:IAHOL: new head of dmi list:0x%p\n",
        dmiPrev));
#endif //_DEBUG

    // DebuggerDataLockHolder out of scope - release implied
    return hr;
}

//-----------------------------------------------------------------------------
// Helper to get an SString through the IPC buffer.
// We do this by putting the SString data into a LS_RS_buffer object,
// and then the RS reads it out as soon as it's queued.
// It's very very important that the SString's buffer is around while we send the event.
// So we pass the SString by reference in case there's an implicit conversion (because
// we don't want to do the conversion on a temporary object and then lose that object).
//-----------------------------------------------------------------------------
void SetLSBufferFromSString(Ls_Rs_StringBuffer * pBuffer, SString & str)
{
    // Copy string contents (+1 for null terminator) into a LS_RS_Buffer.
    // Then the RS can pull it out as a null-terminated string.
    pBuffer->SetLsData(
        (BYTE*) str.GetUnicode(),
        (str.GetCount() +1)* sizeof(WCHAR)
    );
}

//*************************************************************
// structure that we to marshal MDA Notification event data.
//*************************************************************
struct SendMDANotificationParams
{
    Thread * m_pThread; // may be NULL. Lets us send on behalf of other threads.

    // Pass SStrings by ptr in case to guarantee that they're shared (in case we internally modify their storage).
    SString * m_szName;
    SString * m_szDescription;
    SString * m_szXML;
    CorDebugMDAFlags m_flags;

    SendMDANotificationParams(
        Thread * pThread, // may be NULL. Lets us send on behalf of other threads.
        SString * szName,
        SString * szDescription,
        SString * szXML,
        CorDebugMDAFlags flags
    ) :
        m_pThread(pThread),
        m_szName(szName),
        m_szDescription(szDescription),
        m_szXML(szXML),
        m_flags(flags)
    {
        LIMITED_METHOD_CONTRACT;
    }

};

//-----------------------------------------------------------------------------
// Actually send the MDA event. (Could be on any thread)
// Parameters:
//    params - data to initialize the IPC event.
//-----------------------------------------------------------------------------
void Debugger::SendRawMDANotification(
    SendMDANotificationParams * params
)
{
    // Send the unload assembly event to the Right Side.
    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();

    Thread * pThread = params->m_pThread;
    AppDomain *pAppDomain = (pThread != NULL) ? AppDomain::GetCurrentDomain() : NULL;

    InitIPCEvent(ipce,
                 DB_IPCE_MDA_NOTIFICATION,
                 pThread,
                 pAppDomain);

    SetLSBufferFromSString(&ipce->MDANotification.szName, *(params->m_szName));
    SetLSBufferFromSString(&ipce->MDANotification.szDescription, *(params->m_szDescription));
    SetLSBufferFromSString(&ipce->MDANotification.szXml, *(params->m_szXML));
    ipce->MDANotification.dwOSThreadId = GetCurrentThreadId();
    ipce->MDANotification.flags = params->m_flags;

    m_pRCThread->SendIPCEvent();
}

//-----------------------------------------------------------------------------
// Send an MDA notification. This ultimately translates to an ICorDebugMDA object on the Right-Side.
// Called by EE to send a MDA debug event. This will block on the debug event
// until the RS continues us.
// Debugger may or may not be attached. If bAttached, then this
// will trigger a jitattach as well.
// See MDA documentation for what szName, szDescription + szXML should look like.
// The debugger just passes them through.
//
// Parameters:
//   pThread - thread for debug event.  May be null.
//   szName - short name of MDA.
//   szDescription - full description of MDA.
//   szXML - xml string for MDA.
//   bAttach - do a JIT-attach
//-----------------------------------------------------------------------------
void Debugger::SendMDANotification(
    Thread * pThread, // may be NULL. Lets us send on behalf of other threads.
    SString * szName,
    SString * szDescription,
    SString * szXML,
    CorDebugMDAFlags flags,
    BOOL bAttach
)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_ANY;
    }
    CONTRACTL_END;

    PREFIX_ASSUME(szName != NULL);
    PREFIX_ASSUME(szDescription != NULL);
    PREFIX_ASSUME(szXML != NULL);

    // Note: we normally don't send events like this when there is an unrecoverable error. However,
    // if a host attempts to setup fiber mode on a thread, then we'll set an unrecoverable error
    // and use an MDA to 1) tell the user and 2) get the Right Side to notice the unrecoverable error.
    // Therefore, we'll go ahead and send a MDA event if the unrecoverable error is
    // CORDBG_E_CANNOT_DEBUG_FIBER_PROCESS.
    DebuggerIPCControlBlock *pDCB = m_pRCThread->GetDCB();


    // If the MDA is occurring very early in startup before the DCB is setup, then bail.
    if (pDCB == NULL)
    {
        return;
    }

    if (CORDBUnrecoverableError(this) && (pDCB->m_errorHR != CORDBG_E_CANNOT_DEBUG_FIBER_PROCESS))
    {
        return;
    }

    // Validate flags. Make sure that folks don't start passing flags that we don't handle.
    // If pThread != current thread, caller should either pass in MDA_FLAG_SLIP or guarantee
    // that pThread is not slipping.
    _ASSERTE((flags & ~(MDA_FLAG_SLIP)) == 0);

    // Helper thread should not be triggering MDAs. The helper thread is executing code in a very constrained
    // and controlled region and shouldn't be able to do anything dangerous.
    // If we revise this in the future, we should probably just post the event to the RS w/ use the MDA_FLAG_SLIP flag,
    // and then not bother suspending the runtime. The RS will get it on its next event.
    // The jit-attach logic below assumes we're not on the helper. (If we are on the helper, then a debugger should already
    // be attached)
    if (ThisIsHelperThreadWorker())
    {
        CONSISTENCY_CHECK_MSGF(false, ("MDA '%s' fired on *helper* thread.\r\nDesc:%s",
            szName->GetUnicode(), szDescription->GetUnicode()
        ));

        // If for some reason we're wrong about the assert above, we'll just ignore the MDA (rather than potentially deadlock)
        return;
    }

    // Public entry point into the debugger. May cause a jit-attach, so we may need to be lazily-init.
    if (!HasLazyData())
    {
        DebuggerLockHolder dbgLockHolder(this);
        // This is an entry path into the debugger, so make sure we're inited.
        LazyInit();
    }


    // Cases:
    // 1) Debugger already attached, send event normally (ignore severity)
    // 2) No debugger attached, Non-severe probe - ignore.
    // 3) No debugger attached, Severe-probe - do a jit-attach.
    bool fTryJitAttach = bAttach == TRUE;

    // Check case #2 - no debugger, and no jit-attach. Early opt out.
    if (!CORDebuggerAttached() && !fTryJitAttach)
    {
        return;
    }

    if (pThread == NULL)
    {
        // If there's no thread object, then we're not blocking after the event,
        // and thus this probe may slip.
        flags = (CorDebugMDAFlags) (flags | MDA_FLAG_SLIP);
    }

    {
        GCX_PREEMP_EEINTERFACE_TOGGLE_IFTHREAD();

        // For "Severe" probes, we'll do a jit attach dialog
        if (fTryJitAttach)
        {
            // May return:
            // - S_OK if we do a jit-attach,
            // - S_FALSE if a debugger is already attached.
            // - Error in other cases..

            JitAttach(pThread, NULL, TRUE, FALSE);
        }

        // Debugger may be attached now...
        if (CORDebuggerAttached())
        {
            SendMDANotificationParams params(pThread, szName, szDescription, szXML, flags);

            // Non-attach case. Send like normal event.
            // This includes if someone launch the debugger during the meantime.
            // just send the event
            SENDIPCEVENT_BEGIN(this, pThread);

            // Send Log message event to the Right Side
            SendRawMDANotification(&params);

            // Stop all Runtime threads
            // Even if we don't have a managed thead object, this will catch us at the next good spot.
            TrapAllRuntimeThreads();

            // Let other Runtime threads handle their events.
            SENDIPCEVENT_END;
        }
    } // end of GCX_PREEMP_EEINTERFACE_TOGGLE()
}

//*************************************************************
// This method sends a log message over to the right side for the debugger to log it.
//
// The CLR doesn't assign any semantics to the level or cateogory values.
// The BCL has a level convention (LoggingLevels enum), but this isn't exposed publicly,
// so we shouldn't base our behavior on it in any way.
//*************************************************************
void Debugger::SendLogMessage(int iLevel,
                              SString * pSwitchName,
                              SString * pMessage)
{
    CONTRACTL
    {
        GC_TRIGGERS;
        THROWS;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "D::SLM: Sending log message.\n"));

    // Send the message only if the debugger is attached to this appdomain.
    // Note the debugger may detach at any time, so we'll have to check
    // this again after we get the lock.
    AppDomain *pAppDomain = AppDomain::GetCurrentDomain();

    if (!CORDebuggerAttached())
    {
        return;
    }

    Thread *pThread = g_pEEInterface->GetThread();
    SENDIPCEVENT_BEGIN(this, pThread);

    // Send Log message event to the Right Side
    SendRawLogMessage(
        pThread,
        pAppDomain,
        iLevel,
        pSwitchName,
        pMessage);

    // Stop all Runtime threads
    TrapAllRuntimeThreads();

    // Let other Runtime threads handle their events.
    SENDIPCEVENT_END;
}


//*************************************************************
//
// Helper function to just send LogMessage event. Can be called on either
// helper thread or managed thread.
//
//*************************************************************
void Debugger::SendRawLogMessage(
    Thread                                    *pThread,
    AppDomain                                 *pAppDomain,
    int                                        iLevel,
    SString *   pCategory,
    SString *   pMessage
)
{
    DebuggerIPCEvent* ipce;


    // We should have hold debugger lock
    // This can happen on either native helper thread or managed thread
    _ASSERTE(ThreadHoldsLock());

    // It's possible that the debugger dettached while we were waiting
    // for our lock. Check again and abort the event if it did.
    if (!CORDebuggerAttached())
    {
        return;
    }

    ipce = m_pRCThread->GetIPCEventSendBuffer();

    // Send a LogMessage event to the Right Side
    InitIPCEvent(ipce,
                 DB_IPCE_FIRST_LOG_MESSAGE,
                 pThread,
                 pAppDomain);

    ipce->FirstLogMessage.iLevel = iLevel;
    ipce->FirstLogMessage.szCategory.SetString(pCategory->GetUnicode());
    SetLSBufferFromSString(&ipce->FirstLogMessage.szContent, *pMessage);

    m_pRCThread->SendIPCEvent();
}


// This function sends a message to the right side informing it about
// the creation/modification of a LogSwitch
void Debugger::SendLogSwitchSetting(int iLevel,
                                    int iReason,
                                    _In_z_ LPCWSTR pLogSwitchName,
                                    _In_z_ LPCWSTR pParentSwitchName)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

#ifdef LOGGING
    MAKE_UTF8PTR_FROMWIDE(pLogSwitchNameUtf8, pLogSwitchName);
    MAKE_UTF8PTR_FROMWIDE(pParentSwitchNameUtf8, pParentSwitchName);
    LOG((LF_CORDB, LL_INFO1000, "D::SLSS: Sending log switch message switch=%s parent=%s.\n",
        pLogSwitchNameUtf8, pParentSwitchNameUtf8));
#endif // LOGGING

    // Send the message only if the debugger is attached to this appdomain.
    if (!CORDebuggerAttached())
    {
        return;
    }

    Thread *pThread = g_pEEInterface->GetThread();
    SENDIPCEVENT_BEGIN(this, pThread);

    if (CORDebuggerAttached())
    {
        DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
        InitIPCEvent(ipce,
                     DB_IPCE_LOGSWITCH_SET_MESSAGE,
                     pThread,
                     AppDomain::GetCurrentDomain());

        ipce->LogSwitchSettingMessage.iLevel = iLevel;
        ipce->LogSwitchSettingMessage.iReason = iReason;


        ipce->LogSwitchSettingMessage.szSwitchName.SetString(pLogSwitchName);

        if (pParentSwitchName == NULL)
        {
            pParentSwitchName = W("");
        }

        ipce->LogSwitchSettingMessage.szParentSwitchName.SetString(pParentSwitchName);

        m_pRCThread->SendIPCEvent();

        // Stop all Runtime threads
        TrapAllRuntimeThreads();
    }
    else
    {
        LOG((LF_CORDB,LL_INFO1000, "D::SLSS: Skipping SendIPCEvent because RS detached."));
    }

    SENDIPCEVENT_END;
}

// send a custom debugger notification to the RS
// Arguments:
//     input: pThread    - thread on which the notification occurred
//            pDomain    - domain file for the domain in which the notification occurred
//            classToken - metadata token for the type of the notification object
void Debugger::SendCustomDebuggerNotification(Thread * pThread,
                                              DomainAssembly * pDomain,
                                              mdTypeDef classToken)
{
    CONTRACTL
    {
        GC_TRIGGERS;
        THROWS;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO10000, "D::SLM: Sending log message.\n"));

    // Send the message only if the debugger is attached to this appdomain.
    // Note the debugger may detach at any time, so we'll have to check
    // this again after we get the lock.
    if (!CORDebuggerAttached())
    {
        return;
    }

    Thread *curThread = g_pEEInterface->GetThread();
    SENDIPCEVENT_BEGIN(this, curThread);

    if (CORDebuggerAttached() && ShouldSendCustomNotification(pDomain, classToken))
    {
        DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
        InitIPCEvent(ipce,
                     DB_IPCE_CUSTOM_NOTIFICATION,
                     curThread,
                     AppDomain::GetCurrentDomain());

        VMPTR_DomainAssembly vmDomainAssembly = VMPTR_DomainAssembly::MakePtr(pDomain);

        ipce->CustomNotification.classToken = classToken;
        ipce->CustomNotification.vmDomainAssembly = vmDomainAssembly;


        m_pRCThread->SendIPCEvent();

        // Stop all Runtime threads
        TrapAllRuntimeThreads();
    }
    else
    {
        LOG((LF_CORDB,LL_INFO1000, "D::SCDN: Skipping SendIPCEvent because RS detached."));
    }

    SENDIPCEVENT_END;
}


//-----------------------------------------------------------------------------
//
// Add the AppDomain to the list stored in the IPC block.  It adds the id and
// the name.
//
// Arguments:
//     pAppDomain - The runtime app domain object to add.
//
// Return Value:
//     S_OK on success, else detailed error code.
//
HRESULT Debugger::AddAppDomainToIPC(AppDomain *pAppDomain)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        GC_TRIGGERS;
        MODE_ANY;
    }
    CONTRACTL_END;

    HRESULT hr = S_OK;
    LPCWSTR szName = NULL;

    LOG((LF_CORDB, LL_INFO100, "D::AADTIPC: Executing AADTIPC for AppDomain 0x%08x.\n",
        pAppDomain));

    STRESS_LOG1(LF_CORDB, LL_INFO10000, "D::AADTIPC: AddAppDomainToIPC:%#08x\n",
            pAppDomain);



    _ASSERTE(m_pAppDomainCB->m_iTotalSlots > 0);
    _ASSERTE(m_pAppDomainCB->m_rgListOfAppDomains != NULL);

    {
        //
        // We need to synchronize this routine with the attach logic.  The "normal"
        // attach case uses the HelperThread and TrapAllRuntimeThreads to synchronize
        // the runtime before sending any of the events (including AppDomainCreates)
        // to the right-side.  Thus, we can synchronize with this case by forcing us
        // to go co-operative.  If we were already co-op, then the helper thread will
        // wait to start the attach until all co-op threads are paused.  If we were
        // pre-emptive, then going co-op will suspend us until the HelperThread finishes.
        //
        // The second case is under the IPC event for ATTACHING, which is where there are
        // zero app domains, so it is considered an 'early attach' case.  To synchronize
        // with this we have to grab and hold the AppDomainDB lock.
        //

        GCX_COOP();

        // Lock the list
        if (!m_pAppDomainCB->Lock())
        {
            return E_FAIL;
        }

        // Get a free entry from the list
        AppDomainInfo *pAppDomainInfo = m_pAppDomainCB->GetFreeEntry();

        // Function returns NULL if the list is full and a realloc failed.
        if (!pAppDomainInfo)
        {
            hr = E_OUTOFMEMORY;
            goto LErrExit;
        }

        // Now set the AppDomainName.

        /*
         * TODO :
         *
         * Make sure that returning NULL here does not result in a catastrophic
         * failure.
         *
         * GetFriendlyNameNoThrow may call SetFriendlyName, which may call
         * UpdateAppDomainEntryInIPC. There is no recursive death, however, because
         * the AppDomainInfo object does not contain a pointer to the app domain
         * yet.
         */
        szName = pAppDomain->GetFriendlyNameForDebugger();
        pAppDomainInfo->SetName(szName);

        // Save on to the appdomain pointer
        pAppDomainInfo->m_pAppDomain = pAppDomain;

        // bump the used slot count
        m_pAppDomainCB->m_iNumOfUsedSlots++;

LErrExit:
        // UnLock the list
        m_pAppDomainCB->Unlock();

        // Send event to debugger if one is attached.
        if (CORDebuggerAttached())
        {
            SendCreateAppDomainEvent(pAppDomain);
        }
    }

    return hr;
}


/******************************************************************************
 * Remove the AppDomain from the list stored in the IPC block and send an ExitAppDomain
 * event to the debugger if attached.
 ******************************************************************************/
HRESULT Debugger::RemoveAppDomainFromIPC (AppDomain *pAppDomain)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    HRESULT hr = E_FAIL;

    LOG((LF_CORDB, LL_INFO100, "D::RADFIPC: Executing RADFIPC for AppDomain 0x%08x.\n",
        pAppDomain));

    // if none of the slots are occupied, then simply return.
    if (m_pAppDomainCB->m_iNumOfUsedSlots == 0)
        return hr;

    // Lock the list
    if (!m_pAppDomainCB->Lock())
        return (E_FAIL);


    // Look for the entry
    AppDomainInfo *pADInfo = m_pAppDomainCB->FindEntry(pAppDomain);

    // Shouldn't be trying to remove an appdomain that was never added
    if (!pADInfo)
    {
        // We'd like to assert this, but there is a small window where we may have
        // called AppDomain::Init (and so it's fair game to call Stop, and hence come here),
        // but not yet published the app domain.
        // _ASSERTE(!"D::RADFIPC: trying to remove an AppDomain that was never added");
        hr = (E_FAIL);
        goto ErrExit;
    }

    // Release the entry
    m_pAppDomainCB->FreeEntry(pADInfo);

ErrExit:
    // UnLock the list
    m_pAppDomainCB->Unlock();

    //
    // The Debugger expects to never get an unload event for the default AppDomain.
    //

    return hr;
}

/******************************************************************************
 * Update the AppDomain in the list stored in the IPC block.
 ******************************************************************************/
HRESULT Debugger::UpdateAppDomainEntryInIPC(AppDomain *pAppDomain)
{
    CONTRACTL
    {
        NOTHROW;
        if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
    }
    CONTRACTL_END;

    HRESULT hr = S_OK;
    LPCWSTR szName = NULL;

    LOG((LF_CORDB, LL_INFO100,
         "D::UADEIIPC: Executing UpdateAppDomainEntryInIPC ad:0x%x.\n",
         pAppDomain));

    // if none of the slots are occupied, then simply return.
    if (m_pAppDomainCB->m_iNumOfUsedSlots == 0)
        return (E_FAIL);

    // Lock the list
    if (!m_pAppDomainCB->Lock())
        return (E_FAIL);

    // Look up the info entry
    AppDomainInfo *pADInfo = m_pAppDomainCB->FindEntry(pAppDomain);

    if (!pADInfo)
    {
        hr = E_FAIL;
        goto ErrExit;
    }

    // Update the name only if new name is non-null
    szName = pADInfo->m_pAppDomain->GetFriendlyNameForDebugger();
    pADInfo->SetName(szName);

ErrExit:
    // UnLock the list
    m_pAppDomainCB->Unlock();

    return hr;
}

/******************************************************************************
 *
 ******************************************************************************/
HRESULT Debugger::InitAppDomainIPC(void)
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;

        PRECONDITION(CheckPointer(m_pAppDomainCB));
    }
    CONTRACTL_END;

    // Ensure that if we throw here, the Terminate will get called and cleanup all resources.
    // This will make Init an atomic operation - it either fully inits or fully fails.
    class EnsureCleanup
    {
        Debugger * m_pThis;

    public:
        EnsureCleanup(Debugger * pThis)
        {
            m_pThis = pThis;
        }

        void SuppressCleanup()
        {
            m_pThis = NULL;
        }

        ~EnsureCleanup()
        {
            if (m_pThis != NULL)
            {
                m_pThis->TerminateAppDomainIPC();
            }
        }
    } hEnsureCleanup(this);

    DWORD dwStrLen = 0;
    SString szExeName;
    int i;

    // all fields in the object can be zero initialized.
    // If we throw, before fully initializing this, then cleanup won't try to free
    // uninited values.
    ZeroMemory(m_pAppDomainCB, sizeof(*m_pAppDomainCB));

    // Create a mutex to allow the Left and Right Sides to properly
    // synchronize. The Right Side will spin until m_hMutex is valid,
    // then it will acquire it before accessing the data.
    HandleHolder hMutex(CreateMutex(NULL, TRUE/*hold*/, NULL));
    if (hMutex == NULL)
    {
        ThrowLastError();
    }
    if (!m_pAppDomainCB->m_hMutex.SetLocal(hMutex))
    {
        ThrowLastError();
    }
    hMutex.SuppressRelease();

    m_pAppDomainCB->m_iSizeInBytes = INITIAL_APP_DOMAIN_INFO_LIST_SIZE *
                                                sizeof (AppDomainInfo);

    // Number of slots in AppDomainListElement array
    m_pAppDomainCB->m_rgListOfAppDomains = new AppDomainInfo[INITIAL_APP_DOMAIN_INFO_LIST_SIZE];
    _ASSERTE(m_pAppDomainCB->m_rgListOfAppDomains != NULL); // throws on oom


    m_pAppDomainCB->m_iTotalSlots = INITIAL_APP_DOMAIN_INFO_LIST_SIZE;

    // Initialize each AppDomainListElement
    for (i = 0; i < INITIAL_APP_DOMAIN_INFO_LIST_SIZE; i++)
    {
        m_pAppDomainCB->m_rgListOfAppDomains[i].FreeEntry();
    }

    // also initialize the process name
    dwStrLen = WszGetModuleFileName(NULL,
                                    szExeName);


    // If we couldn't get the name, then use a nice default.
    if (dwStrLen == 0)
    {
        szExeName.Set(W("<NoProcessName>"));
        dwStrLen = szExeName.GetCount();
    }

    // If we got the name, copy it into a buffer. dwStrLen is the
    // count of characters in the name, not including the null
    // terminator.
    m_pAppDomainCB->m_szProcessName = new WCHAR[dwStrLen + 1];
    _ASSERTE(m_pAppDomainCB->m_szProcessName != NULL); // throws on oom

    wcscpy_s(m_pAppDomainCB->m_szProcessName, dwStrLen + 1, szExeName);

    // Add 1 to the string length so the Right Side will copy out the
    // null terminator, too.
    m_pAppDomainCB->m_iProcessNameLengthInBytes = (dwStrLen + 1) * sizeof(WCHAR);

    if (m_pAppDomainCB->m_hMutex != NULL)
    {
        m_pAppDomainCB->Unlock();
    }

    hEnsureCleanup.SuppressCleanup();
    return S_OK;
}

/******************************************************************************
 * Uninitialize the AppDomain IPC block
 * Returns:
 * S_OK -if fully uninitialized
 * E_FAIL - if we can't get ownership of the block, and thus no uninitialization
 *          work is done.
 ******************************************************************************/
HRESULT Debugger::TerminateAppDomainIPC(void)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // If we have no AppDomain block, then we can consider it's already terminated.
    if (m_pAppDomainCB == NULL)
        return S_OK;

    HRESULT hr = S_OK;

    // Lock the list
    // If there's no mutex, then we're in a partially created state.
    // This means InitAppDomainIPC failed halfway through. But we're still thread safe
    // since other threads can't access us if we don't have the mutex.
    if ((m_pAppDomainCB->m_hMutex != NULL) && !m_pAppDomainCB->Lock())
    {
        // The callers don't check our return value, we may want to know when we can't gracefully clean up
       LOG((LF_CORDB, LL_INFO10, "Debugger::TerminateAppDomainIPC: Failed to get AppDomain IPC lock, not cleaning up.\n"));

        // If the lock is valid, but we can't get it, then we can't really
        // uninitialize since someone else is using the block.
        return (E_FAIL);
    }

    // The shared IPC segment could still be around after the debugger
    // object has been destroyed during process shutdown. So, reset
    // the UsedSlots count to 0 so that any out of process clients
    // enumeratingthe app domains in this process see 0 AppDomains.
    m_pAppDomainCB->m_iNumOfUsedSlots = 0;
    m_pAppDomainCB->m_iTotalSlots = 0;

    // Now delete the memory allocated for AppDomainInfo  array
    delete [] m_pAppDomainCB->m_rgListOfAppDomains;
    m_pAppDomainCB->m_rgListOfAppDomains = NULL;

    delete [] m_pAppDomainCB->m_szProcessName;
    m_pAppDomainCB->m_szProcessName = NULL;
    m_pAppDomainCB->m_iProcessNameLengthInBytes = 0;

    // Set the mutex handle to NULL.
    // If the Right Side acquires the mutex, it will verify
    // that the handle is still not NULL. If it is, then it knows it
    // really lost.
    RemoteHANDLE m = m_pAppDomainCB->m_hMutex;
    m_pAppDomainCB->m_hMutex.m_hLocal = NULL;

    // And bring us back to a fully uninitialized state.
    ZeroMemory(m_pAppDomainCB, sizeof(*m_pAppDomainCB));

    // We're done. release and close the mutex.  Note that this must be done
    // after we clear it out above to ensure there is no race condition.
    if( m != NULL )
    {
        VERIFY(ReleaseMutex(m));
        m.Close();
    }

    return hr;
}


#ifndef DACCESS_COMPILE

//
// FuncEvalSetup sets up a function evaluation for the given method on the given thread.
//
HRESULT Debugger::FuncEvalSetup(DebuggerIPCE_FuncEvalInfo *pEvalInfo,
                                BYTE **argDataArea,
                                DebuggerEval **debuggerEvalKey)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    Thread *pThread = pEvalInfo->vmThreadToken.GetRawPtr();


    //
    // If TS_AbortRequested (which may have been set by a pending FuncEvalAbort),
    // we will not be able to do a new func-eval
    //
    // <TODO>@TODO: Remember the current value of m_State, reset m_State as appropriate,
    // do the new func-eval, and then set m_State to the original value</TODO>
    if (pThread->m_State & Thread::TS_AbortRequested)
        return CORDBG_E_FUNC_EVAL_BAD_START_POINT;

    if (IsAtProcessExit())
        return CORDBG_E_FUNC_EVAL_BAD_START_POINT;

    // If there is no guard page on this thread, then we've taken a stack overflow exception and can't run managed
    // code on this thread. Therefore, we can't do a func eval on this thread.
    if (!pThread->DetermineIfGuardPagePresent())
    {
        return CORDBG_E_ILLEGAL_IN_STACK_OVERFLOW;
    }

    bool fInException = pEvalInfo->evalDuringException;

    // The thread has to be at a GC safe place for now, just in case the func eval causes a collection. Processing an
    // exception also counts as a "safe place." Eventually, we'd like to have to avoid this check and eval anyway, but
    // that's a way's off...
    if (!fInException && !g_pDebugger->IsThreadAtSafePlace(pThread))
        return CORDBG_E_ILLEGAL_AT_GC_UNSAFE_POINT;

    // For now, we assume that the target thread must be stopped in managed code due to a single step or a
    // breakpoint. Being stopped while sending a first or second chance exception is also valid, and there may or may
    // not be a filter context when we do a func eval from such places. This will loosen over time, eventually allowing
    // threads that are stopped anywhere in managed code to perform func evals.
    CONTEXT *filterContext = GetManagedStoppedCtx(pThread);

    if (filterContext == NULL && !fInException)
    {
        return CORDBG_E_ILLEGAL_AT_GC_UNSAFE_POINT;
    }

    if (filterContext != NULL && ::GetSP(filterContext) != ALIGN_DOWN(::GetSP(filterContext), STACK_ALIGN_SIZE))
    {
        // SP is not aligned, we cannot do a FuncEval here
        LOG((LF_CORDB, LL_INFO1000, "D::FES SP is unaligned"));
        return CORDBG_E_FUNC_EVAL_BAD_START_POINT;
    }

    // Allocate the breakpoint instruction info for the debugger info in executable memory.
    DebuggerHeap *pHeap = g_pDebugger->GetInteropSafeExecutableHeap_NoThrow();
    if (pHeap == NULL)
    {
        return E_OUTOFMEMORY;
    }

    DebuggerEvalBreakpointInfoSegment *bpInfoSegmentRX = (DebuggerEvalBreakpointInfoSegment*)pHeap->Alloc(sizeof(DebuggerEvalBreakpointInfoSegment));
    if (bpInfoSegmentRX == NULL)
    {
        return E_OUTOFMEMORY;
    }

    // Create a DebuggerEval to hold info about this eval while its in progress. Constructor copies the thread's
    // CONTEXT.
    DebuggerEval *pDE = new (interopsafe, nothrow) DebuggerEval(filterContext, pEvalInfo, fInException, bpInfoSegmentRX);

    if (pDE == NULL)
    {
        return E_OUTOFMEMORY;
    }
    else if (!pDE->Init())
    {
        // We fail to change the m_breakpointInstruction field to PAGE_EXECUTE_READWRITE permission.
        return E_FAIL;
    }

    SIZE_T argDataAreaSize = 0;

    argDataAreaSize += pEvalInfo->genericArgsNodeCount * sizeof(DebuggerIPCE_TypeArgData);

    if ((pEvalInfo->funcEvalType == DB_IPCE_FET_NORMAL) ||
        (pEvalInfo->funcEvalType == DB_IPCE_FET_NEW_OBJECT) ||
        (pEvalInfo->funcEvalType == DB_IPCE_FET_NEW_OBJECT_NC))
        argDataAreaSize += pEvalInfo->argCount * sizeof(DebuggerIPCE_FuncEvalArgData);
    else if (pEvalInfo->funcEvalType == DB_IPCE_FET_NEW_STRING)
        argDataAreaSize += pEvalInfo->stringSize;
    else if (pEvalInfo->funcEvalType == DB_IPCE_FET_NEW_ARRAY)
        argDataAreaSize += pEvalInfo->arrayRank * sizeof(SIZE_T);

    if (argDataAreaSize > 0)
    {
        pDE->m_argData = new (interopsafe, nothrow) BYTE[argDataAreaSize];

        if (pDE->m_argData == NULL)
        {
            DeleteInteropSafeExecutable(pDE);
            return E_OUTOFMEMORY;
        }

        // Pass back the address of the argument data area so the right side can write to it for us.
        *argDataArea = pDE->m_argData;
    }

    // Set the thread's IP (in the filter context) to our hijack function if we're stopped due to a breakpoint or single
    // step.
    if (!fInException)
    {
        _ASSERTE(filterContext != NULL);

        ::SetIP(filterContext, (UINT_PTR)GetEEFuncEntryPoint(::FuncEvalHijack));

        // Don't be fooled into thinking you can push things onto the thread's stack now. If the thread is stopped at a
        // breakpoint or from a single step, then its really suspended in the SEH filter. ESP in the thread's CONTEXT,
        // therefore, points into the middle of the thread's current stack. So we pass things we need in the hijack in
        // the thread's registers.

        // Set the first argument to point to the DebuggerEval.
#if defined(TARGET_X86)
        filterContext->Eax = (DWORD)pDE;
#elif defined(TARGET_AMD64)
#ifdef UNIX_AMD64_ABI
        filterContext->Rdi = (SIZE_T)pDE;
#else // UNIX_AMD64_ABI
        filterContext->Rcx = (SIZE_T)pDE;
#endif // !UNIX_AMD64_ABI
#elif defined(TARGET_ARM)
        filterContext->R0 = (DWORD)pDE;
#elif defined(TARGET_ARM64)
        filterContext->X0 = (SIZE_T)pDE;
#elif defined(TARGET_RISCV64)
        filterContext->A0 = (SIZE_T)pDE;
#elif defined(TARGET_LOONGARCH64)
        filterContext->A0 = (SIZE_T)pDE;
#else
        PORTABILITY_ASSERT("Debugger::FuncEvalSetup is not implemented on this platform.");
#endif

        //
        // To prevent GCs until the func-eval gets a chance to run, we increment the counter here.
        // We only need to do this if we have changed the filter CONTEXT, since the stack will be unwalkable
        // in this case.
        //
        g_pDebugger->IncThreadsAtUnsafePlaces();
    }
    else
    {
        HRESULT hr = CheckInitPendingFuncEvalTable();

        if (FAILED(hr))
        {
            DeleteInteropSafeExecutable(pDE);  // Note this runs the destructor for DebuggerEval, which releases its internal buffers
            return (hr);
        }
        // If we're in an exception, then add a pending eval for this thread. This will cause us to perform the func
        // eval when the user continues the process after the current exception event.
        GetPendingEvals()->AddPendingEval(pDE->m_thread, pDE);
    }


    // Return that all went well. Tracing the stack at this point should not show that the func eval is setup, but it
    // will show a wrong IP, so it shouldn't be done.
    *debuggerEvalKey = pDE;

    LOG((LF_CORDB, LL_INFO100000, "D:FES for pDE:%08x evalType:%d on thread %#x, id=0x%x\n",
        pDE, pDE->m_evalType, pThread, GetThreadIdHelper(pThread)));

    return S_OK;
}

//
// FuncEvalAbort: Does a gentle abort of a func-eval already in progress.
//    Because this type of abort waits for the thread to get to a good state,
//    it may never return, or may time out.
//

//
// Wait at most 0.5 seconds.
//
#define FUNC_EVAL_DEFAULT_TIMEOUT_VALUE 500

HRESULT
Debugger::FuncEvalAbort(
    DebuggerEval *debuggerEvalKey
    )
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    DebuggerEval *pDE = (DebuggerEval*) debuggerEvalKey;
    HRESULT hr = S_OK;
    CHECK_IF_CAN_TAKE_HELPER_LOCKS_IN_THIS_SCOPE(&hr, GetCanary());
    if (FAILED(hr))
    {
        return hr;
    }


    if (pDE->m_aborting == DebuggerEval::FE_ABORT_NONE)
    {
        // Remember that we're aborting this func eval.
        pDE->m_aborting = DebuggerEval::FE_ABORT_NORMAL;

        LOG((LF_CORDB, LL_INFO1000,
             "D::FEA: performing UserAbort on thread %#x, id=0x%x\n",
             pDE->m_thread, GetThreadIdHelper(pDE->m_thread)));

        if (!IsAtProcessExit() && !pDE->m_completed)
        {
            //
            // Perform a stop on the thread that the eval is running on.
            // This will cause a ThreadAbortException to be thrown on the thread.
            //
            EX_TRY
            {
                hr = pDE->m_thread->UserAbort(EEPolicy::TA_Safe, (DWORD)FUNC_EVAL_DEFAULT_TIMEOUT_VALUE);
                if (hr == HRESULT_FROM_WIN32(ERROR_TIMEOUT))
                {
                    hr = S_OK;
                }
            }
            EX_CATCH
            {
                _ASSERTE(!"Unknown exception from UserAbort(), not expected");
            }
            EX_END_CATCH(EX_RETHROW);

        }

        LOG((LF_CORDB, LL_INFO1000, "D::FEA: UserAbort complete.\n"));
    }

    return hr;
}

//
// FuncEvalRudeAbort: Does a rude abort of a func-eval in progress.  This
//     leaves the thread in an undetermined state.
//
HRESULT
Debugger::FuncEvalRudeAbort(
    DebuggerEval *debuggerEvalKey
    )
{
    CONTRACTL
    {
        THROWS;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    HRESULT hr = S_OK;
    CHECK_IF_CAN_TAKE_HELPER_LOCKS_IN_THIS_SCOPE(&hr, GetCanary());
    if (FAILED(hr))
    {
        return hr;
    }


    DebuggerEval *pDE = debuggerEvalKey;


    if (!(pDE->m_aborting & DebuggerEval::FE_ABORT_RUDE))
    {
        //
        // Remember that we're aborting this func eval.
        //
        pDE->m_aborting = (DebuggerEval::FUNC_EVAL_ABORT_TYPE)(pDE->m_aborting | DebuggerEval::FE_ABORT_RUDE);

        LOG((LF_CORDB, LL_INFO1000,
             "D::FEA: performing RudeAbort on thread %#x, id=0x%x\n",
             pDE->m_thread, Debugger::GetThreadIdHelper(pDE->m_thread)));

        if (!IsAtProcessExit() && !pDE->m_completed)
        {
            //
            // Perform a stop on the thread that the eval is running on.
            // This will cause a ThreadAbortException to be thrown on the thread.
            //
            EX_TRY
            {
                hr = pDE->m_thread->UserAbort(EEPolicy::TA_Rude, (DWORD)FUNC_EVAL_DEFAULT_TIMEOUT_VALUE);
                if (hr == HRESULT_FROM_WIN32(ERROR_TIMEOUT))
                {
                    hr = S_OK;
                }
            }
            EX_CATCH
            {
                    _ASSERTE(!"Unknown exception from UserAbort(), not expected");
                    EX_RETHROW;
            }
            EX_END_CATCH(RethrowTerminalExceptions);
        }

        LOG((LF_CORDB, LL_INFO1000, "D::FEA: RudeAbort complete.\n"));
    }

    return hr;
}

//
// FuncEvalCleanup cleans up after a function evaluation is released.
//
HRESULT Debugger::FuncEvalCleanup(DebuggerEval *debuggerEvalKey)
{
    LIMITED_METHOD_CONTRACT;

    DebuggerEval *pDE = debuggerEvalKey;

    _ASSERTE(pDE->m_completed);

    LOG((LF_CORDB, LL_INFO1000, "D::FEC: pDE:%08x 0x%08x, id=0x%x\n",
         pDE, pDE->m_thread, GetThreadIdHelper(pDE->m_thread)));

    DeleteInteropSafeExecutable(pDE->m_bpInfoSegment);
    DeleteInteropSafe(pDE);

    return S_OK;
}

#endif // ifndef DACCESS_COMPILE

//
// SetReference sets an object reference for the Right Side,
// respecting the write barrier for references that are in the heap.
//
HRESULT Debugger::SetReference(void *objectRefAddress,
                               VMPTR_OBJECTHANDLE vmObjectHandle,
                               void *newReference)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    HRESULT     hr = S_OK;

    hr = ValidateObject((Object *)newReference);
    if (FAILED(hr))
    {
        return hr;
    }


    // If the object ref isn't in a handle, then go ahead and use
    // SetObjectReference.
    if (vmObjectHandle.IsNull())
    {
        OBJECTREF *dst = (OBJECTREF*)objectRefAddress;
        OBJECTREF  src = *((OBJECTREF*)&newReference);

        SetObjectReference(dst, src);
    }
    else
    {

            // If the object reference to set is inside of a handle, then
            // fixup the handle.
            OBJECTHANDLE h = vmObjectHandle.GetRawPtr();
            OBJECTREF  src = *((OBJECTREF*)&newReference);

            IGCHandleManager* mgr = GCHandleUtilities::GetGCHandleManager();
            mgr->StoreObjectInHandle(h, OBJECTREFToObject(src));
        }

    return S_OK;
}

//
// SetValueClass sets a value class for the Right Side, respecting the write barrier for references that are embedded
// within in the value class.
//
HRESULT Debugger::SetValueClass(void *oldData, void *newData, DebuggerIPCE_BasicTypeData * type)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    HRESULT hr = S_OK;

    TypeHandle th;
    hr = BasicTypeInfoToTypeHandle(type, &th);

    if (FAILED(hr))
        return CORDBG_E_CLASS_NOT_LOADED;

    // Update the value class.
    CopyValueClass(oldData, newData, th.GetMethodTable());

    // Free the buffer that is holding the new data. This is a buffer that was created in response to a GET_BUFFER
    // message, so we release it with ReleaseRemoteBuffer.
    ReleaseRemoteBuffer((BYTE*)newData, true);

    return hr;
}

/******************************************************************************
 *
 ******************************************************************************/
HRESULT Debugger::SetILInstrumentedCodeMap(MethodDesc *fd,
                                           BOOL fStartJit,
                                           ULONG32 cILMapEntries,
                                           COR_IL_MAP rgILMapEntries[])
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS_FROM_GETJITINFO;
    }
    CONTRACTL_END;

    if (!HasLazyData())
    {
        DebuggerLockHolder dbgLockHolder(this);
        // This is an entry path into the debugger, so make sure we're inited.
        LazyInit();
    }

    DebuggerMethodInfo * dmi = GetOrCreateMethodInfo(fd->GetModule(), fd->GetMemberDef());
    if (dmi == NULL)
    {
        return E_OUTOFMEMORY;
    }

    dmi->SetInstrumentedILMap(rgILMapEntries, cILMapEntries);

    return S_OK;
}

//
// EarlyHelperThreadDeath handles the case where the helper
// thread has been ripped out from underneath of us by
// ExitProcess or TerminateProcess. These calls are bad, whacking
// all threads except the caller in the process. This can happen, for
// instance, when an app calls ExitProcess. All threads are wacked,
// the main thread calls all DLL main's, and the EE starts shutting
// down in its DLL main with the helper thread terminated.
//
void Debugger::EarlyHelperThreadDeath(void)
{
    WRAPPER_NO_CONTRACT;

    if (m_pRCThread)
        m_pRCThread->EarlyHelperThreadDeath();
}

//
// This tells the debugger that shutdown of the in-proc debugging services has begun. We need to know this during
// managed/unmanaged debugging so we can stop doing certain things to the process (like hijacking threads.)
//
void Debugger::ShutdownBegun(void)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;


    // Shouldn't be Debugger-stopped if we're shutting down.
    // However, shutdown can occur in preemptive mode. Thus if the RS does an AsyncBreak late
    // enough, then the LS will appear to be stopped but may still shutdown.
    // Since the debuggee can exit asynchronously at any time (eg, suppose somebody forcefully
    // kills it with taskman), this doesn't introduce a new case.
    // That aside, it would be great to be able to assert this:
    //_ASSERTE(!IsStopped());

    if (m_pRCThread != NULL)
    {
        DebuggerIPCControlBlock *dcb = m_pRCThread->GetDCB();

        if ((dcb != NULL) && (dcb->m_rightSideIsWin32Debugger))
            dcb->m_shutdownBegun = true;
    }
}

/*
 * LockDebuggerForShutdown
 *
 * This routine is used during shutdown to tell the in-process portion of the
 * debugger to synchronize with any threads that are currently using the
 * debugging facilities such that no more threads will run debugging services.
 *
 * This is accomplished by transitioning the debugger lock in to a state where
 * it will block all threads, except for the finalizer, shutdown, and helper thread.
 */
void Debugger::LockDebuggerForShutdown(void)
{
#ifndef DACCESS_COMPILE

    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    DebuggerLockHolder dbgLockHolder(this);

    // Shouldn't be Debugger-stopped if we're shutting down.
    // However, shutdown can occur in preemptive mode. Thus if the RS does an AsyncBreak late
    // enough, then the LS will appear to be stopped but may still shutdown.
    // Since the debuggee can exit asynchronously at any time (eg, suppose somebody forcefully
    // kills it with taskman), this doesn't introduce a new case.
    // That aside, it would be great to be able to assert this:
    //_ASSERTE(!IsStopped());

    // After setting this flag, nonspecial threads will not be able to
    // take the debugger lock.
    m_fShutdownMode = true;

    m_ignoreThreadDetach = TRUE;
#else
    DacNotImpl();
#endif
}


/*
 * DisableDebugger
 *
 * This routine is used by the EE to inform the debugger that it should block all
 * threads from executing as soon as it can.  Any thread entering the debugger can
 * block infinitely, as well.
 *
 * This is accomplished by transitioning the debugger lock into a mode where it will
 * block all threads infinitely rather than taking the lock.
 *
 */
void Debugger::DisableDebugger(void)
{
#ifndef DACCESS_COMPILE

    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        PRECONDITION(ThisMaybeHelperThread());
    }
    CONTRACTL_END;

    m_fDisabled = true;

    CORDBDebuggerSetUnrecoverableError(this, CORDBG_E_DEBUGGING_DISABLED, false);

#else
    DacNotImpl();
#endif
}


/****************************************************************************
 * This will perform the duties of the helper thread if none already exists.
 * This is called in the case that the loader lock is held and so no new
 * threads can be spun up to be the helper thread, so the existing thread
 * must be the helper thread until a new one can spin up.
 * This is also called in the shutdown case (IsAtProcessExit()==true) and our
 * helper may have already been blown away.
 ***************************************************************************/
void Debugger::DoHelperThreadDuty()
{
    CONTRACTL
    {
        THROWS;
        WRAPPER(GC_TRIGGERS);
    }
    CONTRACTL_END;

    // This should not be a real helper thread.
    _ASSERTE(!IsDbgHelperSpecialThread());
    _ASSERTE(ThreadHoldsLock());

    // We may be here in the shutdown case (only if the shutdown started after we got here).
    // We'll get killed randomly anyways, so not much we can do.

    // These assumptions are based off us being called from TART.
    _ASSERTE(ThreadStore::HoldingThreadStore() || IsAtProcessExit()); // got this from TART
    _ASSERTE(m_trappingRuntimeThreads); // We're only called from TART.
    _ASSERTE(!m_stopped); // we haven't sent the sync-complete yet.

    // Can't have 2 threads doing helper duty.
    _ASSERTE(m_pRCThread->GetDCB()->m_temporaryHelperThreadId == 0);

    LOG((LF_CORDB, LL_INFO1000,
         "D::SSCIPCE: helper thread is not ready, doing helper "
         "thread duty...\n"));

    // We're the temporary helper thread now.
    DWORD dwMyTID = GetCurrentThreadId();
    m_pRCThread->GetDCB()->m_temporaryHelperThreadId = dwMyTID;

    // Make sure the helper thread has something to wait on while
    // we're trying to be the helper thread.
    VERIFY(ResetEvent(m_pRCThread->GetHelperThreadCanGoEvent()));

    // We have not sent the sync-complete flare yet.

    // Now that we've synchronized, we'll eventually send the sync-complete. But we're currently within the
    // scope of sombody already sending an event. So unlock from that event so that we can send the sync-complete.
    // Don't release the debugger lock
    //
    UnlockFromEventSending(NULL);

    // We are the temporary helper thread. We will not deal with everything! But just pump for
    // continue.
    //
    m_pRCThread->TemporaryHelperThreadMainLoop();

    // We do not need to relock it since we never release it.
    LockForEventSending(NULL);
    _ASSERTE(ThreadHoldsLock());


    STRESS_LOG1(LF_CORDB, LL_INFO1000,
         "D::SSCIPCE: done doing helper thread duty. "
         "Current helper thread id=0x%x\n",
         m_pRCThread->GetDCB()->m_helperThreadId);

    // We're not the temporary helper thread anymore.
    _ASSERTE(m_pRCThread->GetDCB()->m_temporaryHelperThreadId == dwMyTID);
    m_pRCThread->GetDCB()->m_temporaryHelperThreadId = 0;

    // Let the helper thread go if its waiting on us.
    VERIFY(SetEvent(m_pRCThread->GetHelperThreadCanGoEvent()));
}



// This function is called from the EE to notify the right side
// whenever the name of a thread or AppDomain changes
//
// Notes:
//   This just sends a ping event to notify that the name has been changed.
//   It does not send the actual updated name. Instead, the debugger can query for the name.
//
//   For an AppDomain name change:
//   - pAppDoamin != NULL
//   - name retrieved via ICorDebugAppDomain::GetName
//
//   For a Thread name change:
//   - pAppDomain == NULL, pThread != NULL
//   - name retrieved via a func-eval of Thread::get_Name
HRESULT Debugger::NameChangeEvent(AppDomain *pAppDomain, Thread *pThread)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    // Don't try to send one of these if the thread really isn't setup
    // yet. This can happen when initially setting up an app domain,
    // before the appdomain create event has been sent. Since the app
    // domain create event hasn't been sent yet in this case, its okay
    // to do this...
    if (g_pEEInterface->GetThread() == NULL)
        return S_OK;

    // Skip if thread doesn't yet have native ID.
    // This can easily happen if an app sets Thread.Name before it calls Thread.Start.
    // Since this is just a ping-event, it's ignorable. The debugger can query the thread name at Thread.Start in this case.
    // This emulates whidbey semantics.
    if (pThread != NULL)
    {
        if (pThread->GetOSThreadId() == 0)
        {
            return S_OK;
        }
    }

    LOG((LF_CORDB, LL_INFO1000, "D::NCE: Sending NameChangeEvent 0x%x 0x%x\n",
        pAppDomain, pThread));

    Thread *curThread = g_pEEInterface->GetThread();
    SENDIPCEVENT_BEGIN(this, curThread);

    if (CORDebuggerAttached())
    {

            DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
        InitIPCEvent(ipce,
                     DB_IPCE_NAME_CHANGE,
                     curThread,
                     AppDomain::GetCurrentDomain());

        if (pAppDomain)
        {
            ipce->NameChange.eventType = APP_DOMAIN_NAME_CHANGE;
                ipce->NameChange.vmAppDomain.SetRawPtr(pAppDomain);
        }
        else
        {
            // Thread Name
            ipce->NameChange.eventType = THREAD_NAME_CHANGE;
            _ASSERTE (pThread);
            ipce->NameChange.vmThread.SetRawPtr(pThread);
        }

        m_pRCThread->SendIPCEvent();

        // Stop all Runtime threads
        TrapAllRuntimeThreads();
    }
    else
    {
        LOG((LF_CORDB,LL_INFO1000, "D::NCE: Skipping SendIPCEvent because RS detached."));
    }

    SENDIPCEVENT_END;

    return S_OK;

}

//---------------------------------------------------------------------------------------
//
// Send an event to the RS indicating that there's a Ctrl-C or Ctrl-Break.
//
// Arguments:
//    dwCtrlType - represents the type of the event (Ctrl-C or Ctrl-Break)
//
// Return Value:
//    Return TRUE if the event has been handled by the debugger.
//

BOOL Debugger::SendCtrlCToDebugger(DWORD dwCtrlType)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    LOG((LF_CORDB, LL_INFO1000, "D::SCCTD: Sending CtrlC Event 0x%x\n", dwCtrlType));

    // Prevent other Runtime threads from handling events.
    Thread *pThread = g_pEEInterface->GetThread();
    SENDIPCEVENT_BEGIN(this, pThread);

    if (CORDebuggerAttached())
    {
        DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
        InitIPCEvent(ipce,
                     DB_IPCE_CONTROL_C_EVENT,
                     pThread,
                     NULL);

        // The RS doesn't do anything with dwCtrlType
        m_pRCThread->SendIPCEvent();

        // Stop all Runtime threads
        TrapAllRuntimeThreads();
    }
    else
    {
        LOG((LF_CORDB,LL_INFO1000, "D::SCCTD: Skipping SendIPCEvent because RS detached."));
    }

    SENDIPCEVENT_END;

    // now wait for notification from the right side about whether or not
    // the out-of-proc debugger is handling ControlC events.
    ::WaitForSingleObject(GetCtrlCMutex(), INFINITE);

    return GetDebuggerHandlingCtrlC();
}

// Allows the debugger to keep an up to date list of special threads
HRESULT Debugger::UpdateSpecialThreadList(DWORD cThreadArrayLength,
                                        DWORD *rgdwThreadIDArray)
{
    LIMITED_METHOD_CONTRACT;

    _ASSERTE(g_pRCThread != NULL);

    DebuggerIPCControlBlock *pIPC = g_pRCThread->GetDCB();
    _ASSERTE(pIPC);

    if (!pIPC)
        return (E_FAIL);

    // Save the thread list information, and mark the dirty bit so
    // the right side knows.
    pIPC->m_specialThreadList = rgdwThreadIDArray;
    pIPC->m_specialThreadListLength = cThreadArrayLength;
    pIPC->m_specialThreadListDirty = true;

    return (S_OK);
}

//
// If a thread is Win32 suspended right after hitting a breakpoint instruction, but before the OS has transitioned the
// thread over to the user-level exception dispatching logic, then we may see the IP pointing after the breakpoint
// instruction. There are times when the Runtime will use the IP to try to determine what code as run in the prolog or
// epilog, most notably when unwinding a frame. If the thread is suspended in such a case, then the unwind will believe
// that the instruction that the breakpoint replaced has really been executed, which is not true. This confuses the
// unwinding logic. This function is called from Thread::HandledJITCase() to help us recgonize when this may have
// happened and allow us to skip the unwind and abort the HandledJITCase.
//
// The criteria is this:
//
// 1) If a debugger is attached.
//
// 2) If the instruction before the IP is a breakpoint instruction.
//
// 3) If the IP is in the prolog or epilog of a managed function.
//
BOOL Debugger::IsThreadContextInvalid(Thread *pThread, CONTEXT *pCtx)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    BOOL invalid = FALSE;

    // Get the thread context.
    BOOL success = pCtx != NULL;
    CONTEXT ctx;
    if (!success)
    {
        ctx.ContextFlags = CONTEXT_CONTROL;
        success = pThread->GetThreadContext(&ctx);
        if (success)
        {
            pCtx = &ctx;
        }
    }

    if (success)
    {
        // Check single-step flag
        if (IsSSFlagEnabled(reinterpret_cast<DT_CONTEXT *>(pCtx) ARM_ARG(pThread) ARM64_ARG(pThread) RISCV64_ARG(pThread) LOONGARCH64_ARG(pThread)))
        {
            // Can't hijack a thread whose SS-flag is set. This could lead to races
            // with the thread taking the SS-exception.
            // The debugger's controller filters will poll for GC to avoid starvation.
            STRESS_LOG0(LF_CORDB, LL_EVERYTHING, "HJC - Hardware trace flag applied\n");
            return TRUE;
        }
    }

    if (success)
    {
#ifdef TARGET_X86
        // Grab Eip - 1
        LPVOID address = (((BYTE*)GetIP(pCtx)) - 1);

        EX_TRY
        {
            // Use AVInRuntimeImplOkHolder.
            AVInRuntimeImplOkayHolder AVOkay;

            // Is it a breakpoint?
            if (AddressIsBreakpoint((CORDB_ADDRESS_TYPE*)address))
            {
                size_t prologSize; // Unused...
                if (g_pEEInterface->IsInPrologOrEpilog((BYTE*)GetIP(pCtx), &prologSize))
                {
                    LOG((LF_CORDB, LL_INFO1000, "D::ITCI: thread is after a BP and in prolog or epilog.\n"));
                    invalid = TRUE;
                }
            }
        }
        EX_CATCH
        {
            // If we fault trying to read the byte before EIP, then we know that its not a breakpoint.
            // Do nothing.  The default return value is FALSE.
        }
        EX_END_CATCH(SwallowAllExceptions);
#else // TARGET_X86
        // Non-x86 can detect whether the thread is suspended after an exception is hit but before
        // the kernel has dispatched the exception to user mode by trap frame reporting.
        // See Thread::IsContextSafeToRedirect().
#endif // TARGET_X86
    }
    else
    {
        // If we can't get the context, then its definetly invalid... ;)
        LOG((LF_CORDB, LL_INFO1000, "D::ITCI: couldn't get thread's context!\n"));
        invalid = TRUE;
    }

    return invalid;
}


// notification when a SQL connection begins
void Debugger::CreateConnection(CONNID dwConnectionId, _In_z_ WCHAR *wzName)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    LOG((LF_CORDB,LL_INFO1000, "D::CreateConnection %d\n.", dwConnectionId));

    if (CORDBUnrecoverableError(this))
        return;

    Thread *pThread = g_pEEInterface->GetThread();
    SENDIPCEVENT_BEGIN(this, pThread);

    if (CORDebuggerAttached())
    {
        DebuggerIPCEvent* ipce;

        // Send a update module syns event to the Right Side.
        ipce = m_pRCThread->GetIPCEventSendBuffer();
        InitIPCEvent(ipce, DB_IPCE_CREATE_CONNECTION,
                     pThread,
                     NULL);
        ipce->CreateConnection.connectionId = dwConnectionId;
        _ASSERTE(wzName != NULL);
        ipce->CreateConnection.wzConnectionName.SetString(wzName);

        m_pRCThread->SendIPCEvent();
    }
    else
    {
        LOG((LF_CORDB,LL_INFO1000, "D::CreateConnection: Skipping SendIPCEvent because RS detached."));
    }

    // Stop all Runtime threads if we actually sent an event
    if (CORDebuggerAttached())
    {
        TrapAllRuntimeThreads();
    }

    SENDIPCEVENT_END;
}

// notification when a SQL connection ends
void Debugger::DestroyConnection(CONNID dwConnectionId)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    LOG((LF_CORDB,LL_INFO1000, "D::DestroyConnection %d\n.", dwConnectionId));

    if (CORDBUnrecoverableError(this))
        return;

    Thread *thread = g_pEEInterface->GetThread();
    // Note that the debugger lock is reentrant, so we may or may not hold it already.
    SENDIPCEVENT_BEGIN(this, thread);

    // Send a update module syns event to the Right Side.
    DebuggerIPCEvent* ipce = m_pRCThread->GetIPCEventSendBuffer();
    InitIPCEvent(ipce, DB_IPCE_DESTROY_CONNECTION,
                 thread,
                 NULL);
    ipce->ConnectionChange.connectionId = dwConnectionId;

    // IPC event is now initialized, so we can send it over.
    SendSimpleIPCEventAndBlock();

    // This will block on the continue
    SENDIPCEVENT_END;

}

// notification for SQL connection changes
void Debugger::ChangeConnection(CONNID dwConnectionId)
{
    CONTRACTL
    {
        MAY_DO_HELPER_THREAD_DUTY_THROWS_CONTRACT;
        MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT;
    }
    CONTRACTL_END;

    LOG((LF_CORDB,LL_INFO1000, "D::ChangeConnection %d\n.", dwConnectionId));

    if (CORDBUnrecoverableError(this))
        return;

    Thread *pThread = g_pEEInterface->GetThread();
    SENDIPCEVENT_BEGIN(this, pThread);

    if (CORDebuggerAttached())
    {
        DebuggerIPCEvent* ipce;

        // Send a update module syns event to the Right Side.
        ipce = m_pRCThread->GetIPCEventSendBuffer();
        InitIPCEvent(ipce, DB_IPCE_CHANGE_CONNECTION,
                     pThread,
                     NULL);
        ipce->ConnectionChange.connectionId = dwConnectionId;
        m_pRCThread->SendIPCEvent();
    }
    else
    {
        LOG((LF_CORDB,LL_INFO1000, "D::ChangeConnection: Skipping SendIPCEvent because RS detached."));
    }

    // Stop all Runtime threads if we actually sent an event
    if (CORDebuggerAttached())
    {
        TrapAllRuntimeThreads();
    }

    SENDIPCEVENT_END;
}


//
// Are we the helper thread?
// Some important things about running on the helper thread:
// - there's only 1, so guaranteed to be thread-safe.
// - we'll never run managed code.
// - therefore, Never GC.
// - It listens for events from the RS.
// - It's the only thread to send a sync complete.
//
bool ThisIsHelperThreadWorker(void)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    // This can
    Thread * pThread;
    pThread = GetThreadNULLOk();

    // First check for a real helper thread. This will do a FLS access.
    bool fIsHelperThread = !!IsDbgHelperSpecialThread();
    if (fIsHelperThread)
    {
        // If we're on the real helper thread, we never run managed code
        // and so we'd better not have an EE thread object.
        _ASSERTE((pThread == NULL) || !"The helper thread should not being running managed code.\n"
            "Are you running managed code inside the dllmain? If so, your scenario is invalid and this"
            "assert is only the tip of the iceberg.\n");
        return true;
    }

    // Even if we're not on the real helper thread, we may still be on a thread
    // pretending to be the helper. (Helper Duty, etc).
    DWORD id = GetCurrentThreadId();

    // Check for temporary helper thread.
    if (ThisIsTempHelperThread(id))
    {
        return true;
    }

    return false;
}

//
// Make call to the static method.
// This is exposed to the contracts susbsystem so that the helper thread can call
// things on MODE_COOPERATIVE.
//
bool Debugger::ThisIsHelperThread(void)
{
    WRAPPER_NO_CONTRACT;

    return ThisIsHelperThreadWorker();
}

// Check if we're the temporary helper thread. Have 2 forms of this, 1 that assumes the current
// thread (but has the overhead of an extra call to GetCurrentThreadId() if we laready know the tid.
bool ThisIsTempHelperThread()
{
    WRAPPER_NO_CONTRACT;

    DWORD id = GetCurrentThreadId();
    return ThisIsTempHelperThread(id);
}

bool ThisIsTempHelperThread(DWORD tid)
{
    WRAPPER_NO_CONTRACT;

    // If helper thread class isn't created, then there's no helper thread.
    // No one is doing helper thread duty either.
    // It's also possible we're in a shutdown case and have already deleted the
    // data for the helper thread.
    if (g_pRCThread != NULL)
    {
        // May be the temporary helper thread...
        DebuggerIPCControlBlock * pBlock = g_pRCThread->GetDCB();
        if (pBlock != NULL)
        {
            DWORD idTemp = pBlock->m_temporaryHelperThreadId;

            if (tid == idTemp)
            {
                return true;
            }
        }
    }
    return false;

}


// This function is called when host call ICLRSecurityAttributeManager::setDacl.
// It will redacl our SSE, RSEA, RSER events.
HRESULT Debugger::ReDaclEvents(PSECURITY_DESCRIPTOR securityDescriptor)
{
    WRAPPER_NO_CONTRACT;

    return m_pRCThread->ReDaclEvents(securityDescriptor);
}

/* static */
void Debugger::AcquireDebuggerDataLock(Debugger *pDebugger)
{
    WRAPPER_NO_CONTRACT;

    if (!IsAtProcessExit())
    {
        pDebugger->GetDebuggerDataLock()->Enter();
    }
}

/* static */
void Debugger::ReleaseDebuggerDataLock(Debugger *pDebugger)
{
    WRAPPER_NO_CONTRACT;

    if (!IsAtProcessExit())
    {
        pDebugger->GetDebuggerDataLock()->Leave();
    }
}


#else // DACCESS_COMPILE

// determine whether the LS holds the data lock. If it does, we will assume the locked data is in an
// inconsistent state and will throw an exception. The DAC will execute this if we are executing code
// that takes the lock.
// Arguments: input: pDebugger - the LS debugger data structure
/* static */
void Debugger::AcquireDebuggerDataLock(Debugger *pDebugger)
{
    SUPPORTS_DAC;

    if (pDebugger->GetDebuggerDataLock()->GetEnterCount() != 0)
    {
        ThrowHR(CORDBG_E_PROCESS_NOT_SYNCHRONIZED);
    }
}

void Debugger::ReleaseDebuggerDataLock(Debugger *pDebugger)
{
}
#endif // DACCESS_COMPILE

#ifndef DACCESS_COMPILE
/* ------------------------------------------------------------------------ *
 * Functions for DebuggerHeap executable memory allocations
 * ------------------------------------------------------------------------ */

DebuggerHeapExecutableMemoryAllocator::~DebuggerHeapExecutableMemoryAllocator()
{
    while (m_pages != NULL)
    {
        DebuggerHeapExecutableMemoryPage *temp = m_pages->GetNextPage();

        // Free this page
        INDEBUG(BOOL ret =) VirtualFree(m_pages, 0, MEM_RELEASE);
        ASSERT(ret == TRUE);

        m_pages = temp;
    }

    ASSERT(m_pages == NULL);
}

void* DebuggerHeapExecutableMemoryAllocator::Allocate(DWORD numberOfBytes)
{
    if (numberOfBytes > DBG_MAX_EXECUTABLE_ALLOC_SIZE)
    {
        ASSERT(!"Allocating more than DBG_MAX_EXECUTABLE_ALLOC_SIZE at once is unsupported and breaks our assumptions.");
        return NULL;
    }

    if (numberOfBytes == 0)
    {
        // Should we allocate anything in this case?
        ASSERT(!"Allocate called with 0 for numberOfBytes!");
        return NULL;
    }

    CrstHolder execMemAllocCrstHolder(&m_execMemAllocMutex);

    int chunkToUse = -1;
    DebuggerHeapExecutableMemoryPage *pageToAllocateOn = NULL;
    for (DebuggerHeapExecutableMemoryPage *currPage = m_pages; currPage != NULL; currPage = currPage->GetNextPage())
    {
        if (CheckPageForAvailability(currPage, &chunkToUse))
        {
            pageToAllocateOn = currPage;
            break;
        }
    }

    if (pageToAllocateOn == NULL)
    {
        // No existing page had availability, so create a new page and use that.
        pageToAllocateOn = AddNewPage();
        if (pageToAllocateOn == NULL)
        {
            ASSERT(!"Call to AddNewPage failed!");
            return NULL;
        }

        if (!CheckPageForAvailability(pageToAllocateOn, &chunkToUse))
        {
            ASSERT(!"No availability on new page?");
            return NULL;
        }
    }

    ASSERT(chunkToUse >= 1 && (uint)chunkToUse < CHUNKS_PER_DEBUGGERHEAP);
    return GetPointerToChunkWithUsageUpdate(pageToAllocateOn, chunkToUse, ChangePageUsageAction::ALLOCATE);
}

void DebuggerHeapExecutableMemoryAllocator::Free(void* addr)
{
    ASSERT(addr != NULL);

    CrstHolder execMemAllocCrstHolder(&m_execMemAllocMutex);

    DebuggerHeapExecutableMemoryPage *pageToFreeIn = static_cast<DebuggerHeapExecutableMemoryChunk*>(addr)->data.startOfPage;
    _ASSERTE(pageToFreeIn != NULL);

    int chunkNum = static_cast<DebuggerHeapExecutableMemoryChunk*>(addr)->data.chunkNumber;

    // Sanity check: assert that the address really represents the start of a chunk.
    ASSERT(((uint64_t)addr - (uint64_t)pageToFreeIn) % EXPECTED_CHUNKSIZE == 0);

    GetPointerToChunkWithUsageUpdate(pageToFreeIn, chunkNum, ChangePageUsageAction::FREE);
}

DebuggerHeapExecutableMemoryPage* DebuggerHeapExecutableMemoryAllocator::AddNewPage()
{
    void* newPageAddr = VirtualAlloc(NULL, sizeof(DebuggerHeapExecutableMemoryPage), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    DebuggerHeapExecutableMemoryPage *newPage = new (newPageAddr) DebuggerHeapExecutableMemoryPage;
    CrstHolder execMemAllocCrstHolder(&m_execMemAllocMutex);
    newPage->SetNextPage(m_pages);

    // Add the new page to the linked list of pages
    m_pages = newPage;
    return newPage;
}

bool DebuggerHeapExecutableMemoryAllocator::CheckPageForAvailability(DebuggerHeapExecutableMemoryPage* page, /* _Out_ */ int* chunkToUse)
{
    CrstHolder execMemAllocCrstHolder(&m_execMemAllocMutex);
    uint64_t occupancy = page->GetPageOccupancy();
    bool available = occupancy != MAX_CHUNK_MASK;

    if (!available)
    {
        if (chunkToUse)
        {
            *chunkToUse = -1;
        }

        return false;
    }

    if (chunkToUse)
    {
        // skip the first bit, as that's used by the booking chunk.
        for (int i = CHUNKS_PER_DEBUGGERHEAP - 2; i >= 0; i--)
        {
            uint64_t mask = (1ull << i);
            if ((mask & occupancy) == 0)
            {
                *chunkToUse = CHUNKS_PER_DEBUGGERHEAP - i - 1;
                break;
            }
        }
    }

    return true;
}

void* DebuggerHeapExecutableMemoryAllocator::GetPointerToChunkWithUsageUpdate(DebuggerHeapExecutableMemoryPage* page, int chunkNumber, ChangePageUsageAction action)
{
    ASSERT(action == ChangePageUsageAction::ALLOCATE || action == ChangePageUsageAction::FREE);
    uint64_t mask = 1ull << (CHUNKS_PER_DEBUGGERHEAP - chunkNumber - 1);

    CrstHolder execMemAllocCrstHolder(&m_execMemAllocMutex);
    uint64_t prevOccupancy = page->GetPageOccupancy();
    uint64_t newOccupancy = (action == ChangePageUsageAction::ALLOCATE) ? (prevOccupancy | mask) : (prevOccupancy ^ mask);
    page->SetPageOccupancy(newOccupancy);

    return page->GetPointerToChunk(chunkNumber);
}
#endif // DACCESS_COMPILE

/* ------------------------------------------------------------------------ *
 * DebuggerHeap impl
 * ------------------------------------------------------------------------ */

DebuggerHeap::DebuggerHeap()
{
#ifdef USE_INTEROPSAFE_HEAP
    m_hHeap = NULL;
#endif
    m_fExecutable = FALSE;
}

DebuggerHeap::~DebuggerHeap()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    Destroy();
}

void DebuggerHeap::Destroy()
{
#ifdef USE_INTEROPSAFE_HEAP
    if (IsInit())
    {
        ::HeapDestroy(m_hHeap);
        m_hHeap = NULL;
    }
#endif
#if !defined(HOST_WINDOWS) && !defined(DACCESS_COMPILE)
    if (m_execMemAllocator != NULL)
    {
        delete m_execMemAllocator;
    }
#endif
}

bool DebuggerHeap::IsInit()
{
    LIMITED_METHOD_CONTRACT;
#ifdef USE_INTEROPSAFE_HEAP
    return m_hHeap != NULL;
#else
    return true;
#endif
}

HRESULT DebuggerHeap::Init(BOOL fExecutable)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

#ifndef DACCESS_COMPILE

    // Have knob catch if we don't want to lazy init the debugger.
    _ASSERTE(!g_DbgShouldntUseDebugger);
    m_fExecutable = fExecutable;

#ifdef USE_INTEROPSAFE_HEAP
    // If already inited, then we're done.
    // We normally don't double-init. However, we may oom between when we allocate the heap and when we do other initialization.
    // We don't worry about backout code to free the heap. Rather, we'll just leave it alive and nop if we try to allocate it again.
    if (IsInit())
    {
        return S_OK;
    }

    // Create a standard, grow-able, thread-safe heap.
    DWORD dwFlags = ((fExecutable == TRUE)? HEAP_CREATE_ENABLE_EXECUTE : 0);
    m_hHeap = ::HeapCreate(dwFlags, 0, 0);
    if (m_hHeap == NULL)
    {
        return HRESULT_FROM_GetLastError();
    }
#endif

#ifndef HOST_WINDOWS
    m_execMemAllocator = NULL;
    if (m_fExecutable)
    {
        m_execMemAllocator = new (nothrow) DebuggerHeapExecutableMemoryAllocator();
        ASSERT(m_execMemAllocator != NULL);
        if (m_execMemAllocator == NULL)
        {
            return E_OUTOFMEMORY;
        }
    }
#endif

#endif // !DACCESS_COMPILE

    return S_OK;
}

// Only use canaries on x86 b/c they throw of alignment on Ia64.
#if defined(_DEBUG) && defined(TARGET_X86)
#define USE_INTEROPSAFE_CANARY
#endif

#ifdef USE_INTEROPSAFE_CANARY
// Small header to prefix interop-heap blocks.
// This lets us enforce that we don't delete interopheap data from a non-interop heap.
struct InteropHeapCanary
{
    ULONGLONG m_canary;

    // Raw address - this is what the heap alloc + free routines use.
    // User address - this is what the user sees after we adjust the raw address for the canary

    // Given a raw address to an allocated block, get the canary + mark it.
    static InteropHeapCanary * GetFromRawAddr(void * pStart)
    {
        _ASSERTE(pStart != NULL);
        InteropHeapCanary * p = (InteropHeapCanary*) pStart;
        p->Mark();
        return p;
    }

    // Get the raw address from this canary.
    void * GetRawAddr()
    {
        return (void*) this;
    }

    // Get a canary from a start address.
    static InteropHeapCanary * GetFromUserAddr(void * pStart)
    {
        _ASSERTE(pStart != NULL);
        InteropHeapCanary * p = ((InteropHeapCanary*) pStart)-1;
        p->Check();
        return p;
    }
    void * GetUserAddr()
    {
        this->Check();
        return (void*) (this + 1);
    }

protected:
    void Check()
    {
        CONSISTENCY_CHECK_MSGF((m_canary == kInteropHeapCookie),
            ("Using InteropSafe delete on non-interopsafe allocated memory.\n"));
    }
    void Mark()
    {
        m_canary = kInteropHeapCookie;
    }
    static const ULONGLONG kInteropHeapCookie = 0x12345678;
};
#endif // USE_INTEROPSAFE_CANARY

void *DebuggerHeap::Alloc(DWORD size)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

#ifdef USE_INTEROPSAFE_CANARY
    // Make sure we allocate enough space for the canary at the start.
    size += sizeof(InteropHeapCanary);
#endif

    void *ret = NULL;

#ifndef DACCESS_COMPILE

#ifdef USE_INTEROPSAFE_HEAP
    _ASSERTE(m_hHeap != NULL);
    ret = ::HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, size);
#else // USE_INTEROPSAFE_HEAP

#ifdef HOST_WINDOWS
    HANDLE hExecutableHeap  = ClrGetProcessExecutableHeap();

    if (hExecutableHeap == NULL)
    {
        return NULL;
    }

    ret = ::HeapAlloc(hExecutableHeap, 0, size);
#else // HOST_WINDOWS
    if (m_fExecutable)
    {
        ret = m_execMemAllocator->Allocate(size);
    }
    else
    {
        ret = malloc(size);
    }
#endif // HOST_WINDOWS

#endif // USE_INTEROPSAFE_HEAP

#ifdef USE_INTEROPSAFE_CANARY
    if (ret == NULL)
    {
        return NULL;
    }
    InteropHeapCanary * pCanary = InteropHeapCanary::GetFromRawAddr(ret);
    ret = pCanary->GetUserAddr();
#endif
#endif // !DACCESS_COMPILE
    return ret;
}

// Realloc memory.
// If this fails, the original memory is still valid.
void *DebuggerHeap::Realloc(void *pMem, DWORD newSize, DWORD oldSize)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    _ASSERTE(pMem != NULL);
    _ASSERTE(newSize != 0);
    _ASSERTE(oldSize != 0);

#if defined(USE_INTEROPSAFE_HEAP) && !defined(USE_INTEROPSAFE_CANARY) && defined(HOST_WINDOWS)
    // No canaries in this case.
    // Call into realloc.
    void *ret;

    _ASSERTE(m_hHeap != NULL);
    ret = ::HeapReAlloc(m_hHeap, HEAP_ZERO_MEMORY, pMem, newSize);
#else
    // impl Realloc on top of alloc & free.
    void *ret;

    ret = this->Alloc(newSize);
    if (ret == NULL)
    {
        // Not supposed to free original memory in failure condition.
        return NULL;
    }

    memcpy(ret, pMem, oldSize);
    this->Free(pMem);
#endif

    return ret;
}

void DebuggerHeap::Free(void *pMem)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

#ifndef DACCESS_COMPILE

#ifdef USE_INTEROPSAFE_CANARY
    // Check for canary

    if (pMem != NULL)
    {
        InteropHeapCanary * pCanary = InteropHeapCanary::GetFromUserAddr(pMem);
        pMem = pCanary->GetRawAddr();
    }
#endif

#ifdef USE_INTEROPSAFE_HEAP
    if (pMem != NULL)
    {
        _ASSERTE(m_hHeap != NULL);
        ::HeapFree(m_hHeap, 0, pMem);
    }
#else
    if (pMem != NULL)
    {
#ifdef HOST_WINDOWS
        HANDLE hProcessExecutableHeap  = ClrGetProcessExecutableHeap();
        _ASSERTE(hProcessExecutableHeap != NULL);
        ::HeapFree(hProcessExecutableHeap, NULL, pMem);
#else // HOST_WINDOWS
        if (m_fExecutable)
        {
            m_execMemAllocator->Free(pMem);
        }
        else
        {
            free(pMem);
        }
#endif // HOST_WINDOWS
    }
#endif
#endif // !DACCESS_COMPILE
}

#ifdef DACCESS_COMPILE
void
Debugger::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
{
    DAC_ENUM_VTHIS();
    SUPPORTS_DAC;

    if (flags != CLRDATA_ENUM_MEM_TRIAGE)
    {
        if (m_pMethodInfos.IsValid())
        {
            m_pMethodInfos->EnumMemoryRegions(flags);
        }

        DacEnumMemoryRegion(dac_cast<TADDR>(m_pLazyData), sizeof(DebuggerLazyInit));
    }

    // Needed for stack walking from an initial native context.  If the debugger can find the
    // on-disk image of clr.dll, then this is not necessary.
    if (m_rgHijackFunction.IsValid())
    {
        DacEnumMemoryRegion(dac_cast<TADDR>(m_rgHijackFunction), sizeof(MemoryRange)*kMaxHijackFunctions);
    }
}


// This code doesn't hang out in Frame/TransitionFrame/FuncEvalFrame::EnumMemoryRegions() like it would
// for other normal VM objects because we don't want to have code in VM directly referencing LS types.
// Frames.h's FuncEvalFrame simply does a forward decl of DebuggerEval and gets away with it because it
// never does anything but a cast of a TADDR.
void
Debugger::EnumMemoryRegionsIfFuncEvalFrame(CLRDataEnumMemoryFlags flags, Frame * pFrame)
{
    SUPPORTS_DAC;

    if ((pFrame != NULL) && (pFrame->GetFrameType() == Frame::TYPE_FUNC_EVAL))
    {
        FuncEvalFrame * pFEF = dac_cast<PTR_FuncEvalFrame>(pFrame);
        DebuggerEval * pDE = pFEF->GetDebuggerEval();

        if (pDE != NULL)
        {
            DacEnumMemoryRegion(dac_cast<TADDR>(pDE), sizeof(DebuggerEval), true);

            if (pDE->m_debuggerModule != NULL)
                DacEnumMemoryRegion(dac_cast<TADDR>(pDE->m_debuggerModule), sizeof(DebuggerModule), true);
        }
    }
}

#endif // #ifdef DACCESS_COMPILE

#ifndef DACCESS_COMPILE
void Debugger::StartCanaryThread()
{
     // we need to already have the rcthread running and the pointer stored
    _ASSERTE(m_pRCThread != NULL && g_pRCThread == m_pRCThread);
    _ASSERTE(m_pRCThread->GetDCB() != NULL);
    _ASSERTE(GetCanary() != NULL);

    GetCanary()->Init();
}
#endif // DACCESS_COMPILE

#ifndef DACCESS_COMPILE
#ifdef OUT_OF_PROCESS_SETTHREADCONTEXT
void Debugger::SendSetThreadContextNeeded(CONTEXT *context, DebuggerSteppingInfo *pDebuggerSteppingInfo)
{
    STATIC_CONTRACT_NOTHROW;
    STATIC_CONTRACT_GC_NOTRIGGER;

    if (!m_fOutOfProcessSetContextEnabled)
        return;

#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
    DWORD contextFlags = context->ContextFlags;
    DWORD contextSize = 0;

    // determine the context size
    BOOL success = InitializeContext(NULL, contextFlags, NULL, &contextSize);
    if (success || GetLastError() != ERROR_INSUFFICIENT_BUFFER || contextSize == 0)
    {
        // The initialize call should fail but return contextSize
        _ASSERTE(!"InitializeContext unexpectedly failed\n");
        return;
    }

    // allocate a temp buffer for the context
    BYTE *pBuffer = (BYTE*)_alloca(contextSize);
    if (pBuffer == NULL)
    {
        _ASSERTE(!"Failed to allocate context buffer");
        LOG((LF_CORDB, LL_INFO10000, "D::SSTCN Failed to allocate context buffer\n"));
        return;
    }

    // make a copy of the context
    PCONTEXT pContext = NULL;
    success = InitializeContext(pBuffer, contextFlags, &pContext, &contextSize);
    if (!success)
    {
        _ASSERTE(!"InitializeContext failed");
        LOG((LF_CORDB, LL_INFO10000, "D::SSTCN Unexpected result from InitializeContext (error: %d).\n", GetLastError()));
        return;
    }

    success = CopyContext(pContext, contextFlags, context);
    if (!success)
    {
        _ASSERTE(!"CopyContext failed");
        LOG((LF_CORDB, LL_INFO10000, "D::SSTCN Unexpected result from CopyContext (error: %d).\n", GetLastError()));
        return;
    }

    // adjust context size if the context pointer is not aligned with the buffer we allocated
    contextSize -= (DWORD)((BYTE*)pContext-(BYTE*)pBuffer);

    bool fIsInPlaceSingleStep = pDebuggerSteppingInfo != NULL && pDebuggerSteppingInfo->IsInPlaceSingleStep();
    PRD_TYPE opcode = pDebuggerSteppingInfo != NULL ? pDebuggerSteppingInfo->GetOpcode() : CORDbg_BREAK_INSTRUCTION;

    // send the context to the right side
    LOG((LF_CORDB, LL_INFO10000, "D::SSTCN ContextFlags=0x%X contextSize=%d fIsInPlaceSingleStep=%d opcode=%x\n", contextFlags, contextSize, fIsInPlaceSingleStep, opcode));
    EX_TRY
    {
        SetThreadContextNeededFlare((TADDR)pContext, contextSize, fIsInPlaceSingleStep, opcode);
    }
    EX_CATCH
    {
    }
    EX_END_CATCH(SwallowAllExceptions);
#else
    #error Platform not supported
#endif

    LOG((LF_CORDB, LL_INFO10000, "D::SSTCN SetThreadContextNeededFlare returned\n"));
    _ASSERTE(!"We failed to SetThreadContext from out of process!");
}

BOOL Debugger::IsOutOfProcessSetContextEnabled()
{
    return m_fOutOfProcessSetContextEnabled;
}
#else
void Debugger::SendSetThreadContextNeeded(CONTEXT* context, DebuggerSteppingInfo *pDebuggerSteppingInfo)
{
    _ASSERTE(!"SendSetThreadContextNeeded is not enabled on this platform");
}

BOOL Debugger::IsOutOfProcessSetContextEnabled()
{
    return FALSE;
}
#endif // OUT_OF_PROCESS_SETTHREADCONTEXT
#endif // DACCESS_COMPILE

#endif //DEBUGGING_SUPPORTED

