diff options
Diffstat (limited to 'src/debug/inc/dbgipcevents.h')
-rw-r--r-- | src/debug/inc/dbgipcevents.h | 2360 |
1 files changed, 2360 insertions, 0 deletions
diff --git a/src/debug/inc/dbgipcevents.h b/src/debug/inc/dbgipcevents.h new file mode 100644 index 0000000000..6ace36e011 --- /dev/null +++ b/src/debug/inc/dbgipcevents.h @@ -0,0 +1,2360 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* ------------------------------------------------------------------------- * + * DbgIPCEvents.h -- header file for private Debugger data shared by various +// + + * debugger components. + * ------------------------------------------------------------------------- */ + +#ifndef _DbgIPCEvents_h_ +#define _DbgIPCEvents_h_ + +#include <new.hpp> +#include <cor.h> +#include <cordebug.h> +#include <corjit.h> // for ICorDebugInfo::VarLocType & VarLoc +#include <specstrings.h> + +#include "dbgtargetcontext.h" + + +// Get version numbers for IPCHeader stamp +#include "ndpversion.h" + +#include "dbgappdomain.h" + +#include "./common.h" + +//----------------------------------------------------------------------------- +// V3 additions to IPC protocol between LS and RS. +//----------------------------------------------------------------------------- + +// Special Exception code for LS to communicate with RS. +// LS will raise this exception to communicate managed debug events to the RS. +// Exception codes can't use bit 0x10000000, that's reserved by OS. +#define CLRDBG_NOTIFICATION_EXCEPTION_CODE ((DWORD) 0x04242420) + +// This is exception argument 0 included in debugger notification events. +// The debugger uses this as a sanity check. +// This could be very volatile data that changes between builds. +#define CLRDBG_EXCEPTION_DATA_CHECKSUM ((DWORD) 0x31415927) + + +// Reasons for hijack. +namespace EHijackReason +{ + enum EHijackReason + { + kUnhandledException = 1, + kM2UHandoff = 2, + kFirstChanceSuspend = 3, + kGenericHijack = 4, + kMax + }; + inline bool IsValid(EHijackReason value) + { + SUPPORTS_DAC; + return (value > 0) && (value < kMax); + } +} + + + +#define MAX_LOG_SWITCH_NAME_LEN 256 + +//----------------------------------------------------------------------------- +// Versioning note: +// This file describes the IPC communication protocol between the LS (mscorwks) +// and the RS (mscordbi). For Desktop builds, it is private and can change on a +// daily basis. The version of the LS will always match the version of the RS +// (but see the discussion of CoreCLR below). They are like a single conceptual +// DLL split across 2 processes. +// The only restriction is that it should be flavor agnostic - so don't change +// layout based off '#ifdef DEBUG'. This lets us drop a Debug flavor RS onto +// a retail installation w/o any further installation woes. That's very useful +// for debugging. +//----------------------------------------------------------------------------- + + +// We want this available for DbgInterface.h - put it here. +typedef enum +{ + IPC_TARGET_OUTOFPROC, + IPC_TARGET_COUNT, +} IpcTarget; + +// +// Names of the setup sync event and shared memory used for IPC between the Left Side and the Right Side. NOTE: these +// names must include a %d for the process id. The process id used is the process id of the debuggee. +// + +#define CorDBIPCSetupSyncEventName W("CorDBIPCSetupSyncEvent_%d") + +// +// This define controls whether we always pass first chance exceptions to the in-process first chance hijack filter +// during interop debugging or if we try to short-circuit and make the decision out-of-process as much as possible. +// +#define CorDB_Short_Circuit_First_Chance_Ownership 1 + +// +// Defines for current version numbers for the left and right sides +// +#define CorDB_LeftSideProtocolCurrent 2 +#define CorDB_LeftSideProtocolMinSupported 2 +#define CorDB_RightSideProtocolCurrent 2 +#define CorDB_RightSideProtocolMinSupported 2 + +// +// The remaining data structures in this file can be shared between two processes and for network transport +// based debugging this can mean two different platforms as well. The two platforms that can share these +// data structures must have identical layouts for them (each field must lie at the same offset and have the +// same length). The MSLAYOUT macro should be applied to each structure to avoid any compiler packing differences. +// + +// +// DebuggerIPCRuntimeOffsets contains addresses and offsets of important global variables, functions, and fields in +// Runtime objects. This is populated during Left Side initialization and is read by the Right Side. This struct is +// mostly to facilitate unmanaged debugging support, but it may have some small uses for managed debugging. +// +struct MSLAYOUT DebuggerIPCRuntimeOffsets +{ +#ifdef FEATURE_INTEROP_DEBUGGING + void *m_genericHijackFuncAddr; + void *m_signalHijackStartedBPAddr; + void *m_excepForRuntimeHandoffStartBPAddr; + void *m_excepForRuntimeHandoffCompleteBPAddr; + void *m_signalHijackCompleteBPAddr; + void *m_excepNotForRuntimeBPAddr; + void *m_notifyRSOfSyncCompleteBPAddr; + void *m_raiseExceptionAddr; // The address of kernel32!RaiseException in the debuggee +#endif // FEATURE_INTEROP_DEBUGGING + SIZE_T m_TLSIndex; // The TLS index the CLR is using to hold Thread objects + SIZE_T m_TLSIsSpecialIndex; // The index into the Predef block of the the "IsSpecial" status for a thread. + SIZE_T m_TLSCantStopIndex; // The index into the Predef block of the the Can't-Stop count. + SIZE_T m_TLSIndexOfPredefs; // The TLS index of the Predef block. + SIZE_T m_EEThreadStateOffset; // Offset of m_state in a Thread + SIZE_T m_EEThreadStateNCOffset; // Offset of m_stateNC in a Thread + SIZE_T m_EEThreadPGCDisabledOffset; // Offset of the bit for whether PGC is disabled or not in a Thread + DWORD m_EEThreadPGCDisabledValue; // Value at m_EEThreadPGCDisabledOffset that equals "PGC disabled". + SIZE_T m_EEThreadDebuggerWordOffset; // Offset of debugger word in a Thread + SIZE_T m_EEThreadFrameOffset; // Offset of the Frame ptr in a Thread + SIZE_T m_EEThreadMaxNeededSize; // Max memory to read to get what we need out of a Thread object + DWORD m_EEThreadSteppingStateMask; // Mask for Thread::TSNC_DebuggerIsStepping + DWORD m_EEMaxFrameValue; // The max Frame value + SIZE_T m_EEThreadDebuggerFilterContextOffset; // Offset of debugger's filter context within a Thread Object. + SIZE_T m_EEThreadCantStopOffset; // Offset of the can't stop count in a Thread + SIZE_T m_EEFrameNextOffset; // Offset of the next ptr in a Frame + DWORD m_EEIsManagedExceptionStateMask; // Mask for Thread::TSNC_DebuggerIsManagedException + void *m_pPatches; // Addr of patch table + BOOL *m_pPatchTableValid; // Addr of g_patchTableValid + SIZE_T m_offRgData; // Offset of m_pcEntries + SIZE_T m_offCData; // Offset of count of m_pcEntries + SIZE_T m_cbPatch; // Size per patch entry + SIZE_T m_offAddr; // Offset within patch of target addr + SIZE_T m_offOpcode; // Offset within patch of target opcode + SIZE_T m_cbOpcode; // Max size of opcode + SIZE_T m_offTraceType; // Offset of the trace.type within a patch + DWORD m_traceTypeUnmanaged; // TRACE_UNMANAGED + + DebuggerIPCRuntimeOffsets() + { + ZeroMemory(this, sizeof(DebuggerIPCRuntimeOffsets)); + } +}; + +// +// The size of the send and receive IPC buffers. +// These must be big enough to fit a DebuggerIPCEvent. Also, the bigger they are, the fewer events +// it takes to send variable length stuff like the stack trace. +// But for perf reasons, they need to be small enough to not just push us over a page boundary in an IPC block. +// Unfortunately, there's a lot of other goo in the IPC block, so we can't use some clean formula. So we +// have to resort to just tuning things. +// + +// When using a network transport rather than shared memory buffers CorDBIPC_BUFFER_SIZE is the upper bound +// for a single DebuggerIPCEvent structure. This now relates to the maximal size of a network message and is +// orthogonal to the host's page size. Because of this we defer definition of CorDBIPC_BUFFER_SIZE until we've +// declared DebuggerIPCEvent at the end of this header (and we can do so because in the transport case there +// aren't any embedded buffers in the DebuggerIPCControlBlock). + +#if defined(DBG_TARGET_X86) || defined(DBG_TARGET_ARM) +#define CorDBIPC_BUFFER_SIZE (2088) // hand tuned to ensure that ipc block in IPCHeader.h fits in 1 page. +#else // !_TARGET_X86_ && !_TARGET_ARM_ +// This is the size of a DebuggerIPCEvent. You will hit an assert in Cordb::Initialize() (DI\process.cpp) +// if this is not defined correctly. AMD64 actually has a page size of 0x1000, not 0x2000. +#define CorDBIPC_BUFFER_SIZE 4016 // (4016 + 6) * 2 + 148 = 8192 (two (DebuggerIPCEvent + alignment padding) + + // other fields = page size) +#endif // DBG_TARGET_X86 || DBG_TARGET_ARM + +// +// DebuggerIPCControlBlock describes the layout of the shared memory shared between the Left Side and the Right +// Side. This includes error information, handles for the IPC channel, and space for the send/receive buffers. +// +struct MSLAYOUT DebuggerIPCControlBlock +{ + // Version data should be first in the control block to ensure that we can read it even if the control block + // changes. + SIZE_T m_DCBSize; // note this field is used as a semaphore to indicate the DCB is initialized + ULONG m_verMajor; // CLR build number for the Left Side. + ULONG m_verMinor; // CLR build number for the Left Side. + + // This next stuff fits in a DWORD. + bool m_checkedBuild; // CLR build type for the Left Side. + // using the first padding byte to indicate if hosted in fiber mode. + // We actually just need one bit. So if needed, can turn this to a bit. + // BYTE padding1; + bool m_bHostingInFiber; + BYTE padding2; + BYTE padding3; + + ULONG m_leftSideProtocolCurrent; // Current protocol version for the Left Side. + ULONG m_leftSideProtocolMinSupported; // Minimum protocol the Left Side can support. + + ULONG m_rightSideProtocolCurrent; // Current protocol version for the Right Side. + ULONG m_rightSideProtocolMinSupported; // Minimum protocol the Right Side requires. + + HRESULT m_errorHR; + unsigned int m_errorCode; + +#if defined(DBG_TARGET_WIN64) + // 64-bit needs this padding to make the handles after this aligned. + // But x86 can't have this padding b/c it breaks binary compatibility between v1.1 and v2.0. + ULONG padding4; +#endif // DBG_TARGET_WIN64 + + + RemoteHANDLE m_rightSideEventAvailable; + RemoteHANDLE m_rightSideEventRead; + + // @dbgtodo inspection - this is where LSEA and LSER used to be. We need to the padding to maintain binary compatibility. + // Eventually, we expect to remove this whole block. + RemoteHANDLE m_paddingObsoleteLSEA; + RemoteHANDLE m_paddingObsoleteLSER; + + RemoteHANDLE m_rightSideProcessHandle; + + //............................................................................. + // Everything above this point must have the exact same binary layout as v1.1. + // See protocol details below. + //............................................................................. + + RemoteHANDLE m_leftSideUnmanagedWaitEvent; + + + + // This is set immediately when the helper thread is created. + // This will be set even if there's a temporary helper thread or if the real helper + // thread is not yet pumping (eg, blocked on a loader lock). + DWORD m_realHelperThreadId; + + // This is only published once the helper thread starts running in its main loop. + // Thus we can use this field to see if the real helper thread is actually pumping. + DWORD m_helperThreadId; + + // This is non-zero if the LS has a temporary helper thread. + DWORD m_temporaryHelperThreadId; + + // ID of the Helper's canary thread. + DWORD m_CanaryThreadId; + + DebuggerIPCRuntimeOffsets *m_pRuntimeOffsets; + void *m_helperThreadStartAddr; + void *m_helperRemoteStartAddr; + DWORD *m_specialThreadList; + + BYTE m_receiveBuffer[CorDBIPC_BUFFER_SIZE]; + BYTE m_sendBuffer[CorDBIPC_BUFFER_SIZE]; + + DWORD m_specialThreadListLength; + bool m_shutdownBegun; + bool m_rightSideIsWin32Debugger; // RS status + bool m_specialThreadListDirty; + + bool m_rightSideShouldCreateHelperThread; + + // NOTE The Init method works since there are no virtual functions - don't add any virtual functions without + // changing this! + // Only initialized by the LS, opened by the RS. + HRESULT Init( + HANDLE rsea, + HANDLE rser, + HANDLE lsea, + HANDLE lser, + HANDLE lsuwe + ); + +}; + +#if defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI) + +// We need an alternate definition for the control block if using the transport, because the control block has to be sent over the transport +// In particular we can't nest the send/receive buffers inside of it and we don't use any of the remote handles + +struct MSLAYOUT DebuggerIPCControlBlockTransport +{ + // Version data should be first in the control block to ensure that we can read it even if the control block + // changes. + SIZE_T m_DCBSize; // note this field is used as a semaphore to indicate the DCB is initialized + ULONG m_verMajor; // CLR build number for the Left Side. + ULONG m_verMinor; // CLR build number for the Left Side. + + // This next stuff fits in a DWORD. + bool m_checkedBuild; // CLR build type for the Left Side. + // using the first padding byte to indicate if hosted in fiber mode. + // We actually just need one bit. So if needed, can turn this to a bit. + // BYTE padding1; + bool m_bHostingInFiber; + BYTE padding2; + BYTE padding3; + + ULONG m_leftSideProtocolCurrent; // Current protocol version for the Left Side. + ULONG m_leftSideProtocolMinSupported; // Minimum protocol the Left Side can support. + + ULONG m_rightSideProtocolCurrent; // Current protocol version for the Right Side. + ULONG m_rightSideProtocolMinSupported; // Minimum protocol the Right Side requires. + + HRESULT m_errorHR; + unsigned int m_errorCode; + +#if defined(DBG_TARGET_WIN64) + // 64-bit needs this padding to make the handles after this aligned. + // But x86 can't have this padding b/c it breaks binary compatibility between v1.1 and v2.0. + ULONG padding4; +#endif // DBG_TARGET_WIN64 + + // This is set immediately when the helper thread is created. + // This will be set even if there's a temporary helper thread or if the real helper + // thread is not yet pumping (eg, blocked on a loader lock). + DWORD m_realHelperThreadId; + + // This is only published once the helper thread starts running in its main loop. + // Thus we can use this field to see if the real helper thread is actually pumping. + DWORD m_helperThreadId; + + // This is non-zero if the LS has a temporary helper thread. + DWORD m_temporaryHelperThreadId; + + // ID of the Helper's canary thread. + DWORD m_CanaryThreadId; + + DebuggerIPCRuntimeOffsets *m_pRuntimeOffsets; + void *m_helperThreadStartAddr; + void *m_helperRemoteStartAddr; + DWORD *m_specialThreadList; + + DWORD m_specialThreadListLength; + bool m_shutdownBegun; + bool m_rightSideIsWin32Debugger; // RS status + bool m_specialThreadListDirty; + + bool m_rightSideShouldCreateHelperThread; + + // NOTE The Init method works since there are no virtual functions - don't add any virtual functions without + // changing this! + // Only initialized by the LS, opened by the RS. + HRESULT Init(); + +}; + +#endif // defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI) + +#if defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI) +#include "dbgtransportsession.h" +#endif // defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI) + +#if defined(DBG_TARGET_X86) && !defined(FEATURE_CORESYSTEM) +// We have an versioning requirement. +// Certain portions of the v1.0 and v1.1 IPC block are shared. This is b/c a v1.1 debugger needs to be able +// to look at a v2.0 app enough to recognize the version mismatch. +// This check is only necessary for platforms that ran on v1.1 (Win32-x86) + +// Just to catch any potential illegal change in the IPC block, we assert the offsets against the offsets from v1.1. +// The constants here are pulled from v1.1. +// The RS will look at these versioning fields, so they absolutely must line up. +static_assert_no_msg(offsetof(DebuggerIPCControlBlock, m_leftSideProtocolCurrent) == 0x10); +static_assert_no_msg(offsetof(DebuggerIPCControlBlock, m_leftSideProtocolMinSupported) == 0x14); +static_assert_no_msg(offsetof(DebuggerIPCControlBlock, m_rightSideProtocolCurrent) == 0x18); +static_assert_no_msg(offsetof(DebuggerIPCControlBlock, m_rightSideProtocolMinSupported) == 0x1c); + +// Unfortunately, on detecting such failure, v1.1 will also null out LSEA, LSER and RSPH. +// If these get adjusted, a version-mismatch attach will effectively null out random fields. +static_assert_no_msg(offsetof(DebuggerIPCControlBlock, m_paddingObsoleteLSEA) == 0x30); +static_assert_no_msg(offsetof(DebuggerIPCControlBlock, m_paddingObsoleteLSER) == 0x34); +static_assert_no_msg(offsetof(DebuggerIPCControlBlock, m_rightSideProcessHandle) == 0x38); + + + +#endif + +#define INITIAL_APP_DOMAIN_INFO_LIST_SIZE 16 + + +//----------------------------------------------------------------------------- +// Provide some Type-safety in the IPC block when we pass remote pointers around. +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// This is the same in both the LS & RS. +// Definitions on the LS & RS should be binary compatible. So all storage is +// declared in GeneralLsPointer, and then the Ls & RS each have their own +// derived accessors. +//----------------------------------------------------------------------------- +class MSLAYOUT GeneralLsPointer +{ +protected: + friend ULONG_PTR LsPtrToCookie(GeneralLsPointer p); + void * m_ptr; + +public: + bool IsNull() { return m_ptr == NULL; } +}; + +class MSLAYOUT GeneralRsPointer +{ +protected: + UINT m_data; + +public: + bool IsNull() { return m_data == 0; } +}; + +// In some cases, we need to get a uuid from a pointer (ie, in a hash) +inline ULONG_PTR LsPtrToCookie(GeneralLsPointer p) { + return (ULONG_PTR) p.m_ptr; +} +#define VmPtrToCookie(vm) LsPtrToCookie((vm).ToLsPtr()) + + +#ifdef RIGHT_SIDE_COMPILE +//----------------------------------------------------------------------------- +// Infrasturcture for RS Definitions +//----------------------------------------------------------------------------- + +// On the RS, we don't have the LS classes defined, so we can't templatize that +// in terms of <class T>, but we still want things to be unique. +// So we create an empty enum for each LS type and then templatize it in terms +// of the enum. +template <typename T> +class MSLAYOUT LsPointer : public GeneralLsPointer +{ +public: + void Set(void * p) + { + m_ptr = p; + } + void * UnsafeGet() + { + return m_ptr; + } + + static LsPointer<T> NullPtr() + { + return MakePtr(NULL); + } + + static LsPointer<T> MakePtr(T* p) + { +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:6001) // PREfast warning: Using uninitialize memory 't' +#endif // _PREFAST_ + + LsPointer<T> t; + t.Set(p); + return t; + +#ifdef _PREFAST_ +#pragma warning(pop) +#endif // _PREFAST_ + } + + bool operator!= (void * p) { return m_ptr != p; } + bool operator== (void * p) { return m_ptr == p; } + bool operator==(LsPointer<T> p) { return p.m_ptr == this->m_ptr; } + + // We should never UnWrap() them in the RS, so we don't define that here. +}; + +class CordbProcess; +template <class T> UINT AllocCookie(CordbProcess * pProc, T * p); +template <class T> T * UnwrapCookie(CordbProcess * pProc, UINT cookie); + +UINT AllocCookieCordbEval(CordbProcess * pProc, class CordbEval * p); +class CordbEval * UnwrapCookieCordbEval(CordbProcess * pProc, UINT cookie); + +template <class CordbEval> UINT AllocCookie(CordbProcess * pProc, CordbEval * p) +{ + return AllocCookieCordbEval(pProc, p); +} +template <class CordbEval> CordbEval * UnwrapCookie(CordbProcess * pProc, UINT cookie) +{ + return UnwrapCookieCordbEval(pProc, cookie); +} + + + +// This is how the RS sees the pointers in the IPC block. +template<class T> +class MSLAYOUT RsPointer : public GeneralRsPointer +{ +public: + // Since we're being used inside a union, we can't have a ctor. + + static RsPointer<T> NullPtr() + { + RsPointer<T> t; + t.m_data = 0; + return t; + } + + bool AllocHandle(CordbProcess *pProc, T* p) + { + // This will force validation. + m_data = AllocCookie<T>(pProc, p); + return (m_data != 0); + } + + bool operator==(RsPointer<T> p) { return p.m_data == this->m_data; } + + T* UnWrapAndRemove(CordbProcess *pProc) + { + return UnwrapCookie<T>(pProc, m_data); + } + +protected: +}; + +// Forward declare a class so that each type of LS pointer can have +// its own type. We use the real class name to be compatible with VMPTRs. +#define DEFINE_LSPTR_TYPE(ls_type, ptr_name) \ + ls_type; \ + typedef LsPointer<ls_type> ptr_name; + + +#define DEFINE_RSPTR_TYPE(rs_type, ptr_name) \ + class rs_type; \ + typedef RsPointer<rs_type> ptr_name; + +#else // !RIGHT_SIDE_COMPILE +//----------------------------------------------------------------------------- +// Infrastructure for LS Definitions +//----------------------------------------------------------------------------- + +// This is how the LS sees the pointers in the IPC block. +template<typename T> +class MSLAYOUT LsPointer : public GeneralLsPointer +{ +public: + // Since we're being used inside a union, we can't have a ctor. + //LsPointer() { } + + static LsPointer<T> NullPtr() + { + return MakePtr(NULL); + } + + static LsPointer<T> MakePtr(T * p) + { +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:6001) // PREfast warning: Using uninitialize memory 't' +#endif // _PREFAST_ + + LsPointer<T> t; + t.Set(p); + return t; + +#ifdef _PREFAST_ +#pragma warning(pop) +#endif // _PREFAST_ + } + + bool operator!= (void * p) { return m_ptr != p; } + bool operator== (void * p) { return m_ptr == p; } + bool operator==(LsPointer<T> p) { return p.m_ptr == this->m_ptr; } + + // @todo - we want to be able to swap out Set + Unwrap functions + void Set(T * p) + { + SUPPORTS_DAC; + // We could validate the pointer here. + m_ptr = p; + } + + T * UnWrap() + { + // If we wanted to validate the pointer, here's our chance. + return static_cast<T*>(m_ptr); + } +}; + +template <class n> +class MSLAYOUT RsPointer : public GeneralRsPointer +{ +public: + static RsPointer<n> NullPtr() + { + RsPointer<n> t; + t.m_data = 0; + return t; + } + + bool operator==(RsPointer<n> p) { return p.m_data == this->m_data; } + + // We should never UnWrap() them in the LS, so we don't define that here. +}; + +#define DEFINE_LSPTR_TYPE(ls_type, ptr_name) \ + ls_type; \ + typedef LsPointer<ls_type> ptr_name; + +#define DEFINE_RSPTR_TYPE(rs_type, ptr_name) \ + enum __RS__##rs_type { }; \ + typedef RsPointer<__RS__##rs_type> ptr_name; + +#endif // !RIGHT_SIDE_COMPILE + +// We must be binary compatible w/ a pointer. +static_assert_no_msg(sizeof(LsPointer<void>) == sizeof(GeneralLsPointer)); + +static_assert_no_msg(sizeof(void*) == sizeof(GeneralLsPointer)); + + + +//----------------------------------------------------------------------------- +// Definitions for Left-Side ptrs. +// NOTE: Use VMPTR instead of LSPTR. Don't add new LSPTR types. +// +//----------------------------------------------------------------------------- + + + +DEFINE_LSPTR_TYPE(class Assembly, LSPTR_ASSEMBLY); +DEFINE_LSPTR_TYPE(class DebuggerJitInfo, LSPTR_DJI); +DEFINE_LSPTR_TYPE(class DebuggerMethodInfo, LSPTR_DMI); +DEFINE_LSPTR_TYPE(class MethodDesc, LSPTR_METHODDESC); +DEFINE_LSPTR_TYPE(class DebuggerBreakpoint, LSPTR_BREAKPOINT); +DEFINE_LSPTR_TYPE(class DebuggerEval, LSPTR_DEBUGGEREVAL); +DEFINE_LSPTR_TYPE(class DebuggerStepper, LSPTR_STEPPER); + +// Need to be careful not to annoy the compiler here since DT_CONTEXT is a typedef, not a struct. +#if defined(RIGHT_SIDE_COMPILE) +typedef LsPointer<DT_CONTEXT> LSPTR_CONTEXT; +#else // RIGHT_SIDE_COMPILE +typedef LsPointer<DT_CONTEXT> LSPTR_CONTEXT; +#endif // RIGHT_SIDE_COMPILE + +DEFINE_LSPTR_TYPE(struct OBJECTHANDLE__, LSPTR_OBJECTHANDLE); +DEFINE_LSPTR_TYPE(class TypeHandleDummyPtr, LSPTR_TYPEHANDLE); // TypeHandle in the LS is not a direct pointer. + +//----------------------------------------------------------------------------- +// Definitions for Right-Side ptrs. +//----------------------------------------------------------------------------- +DEFINE_RSPTR_TYPE(CordbEval, RSPTR_CORDBEVAL); + + +//--------------------------------------------------------------------------------------- +// VMPTR_Base is the base type for an abstraction over pointers into the VM so +// that DBI can treat them as opaque handles. Classes will derive from it to +// provide type-safe Target pointers, which ICD will view as opaque handles. +// +// Lifetimes: +// VMPTR_ objects survive across flushing the DAC cache. Therefore, the underlying +// storage must be a target-pointer (and not a marshalled host pointer). +// The RS must ensure they're still in sync with the LS (eg, by +// tracking unload events). +// +// +// Assumptions: +// These handles are TADDR pointers and must not require any cleanup from DAC/DBI. +// For direct untyped pointers into the VM, use CORDB_ADDRESS. +// +// Notes: +// 1. This helps enforce that DBI goes through the primitives interface +// for all access (and that it doesn't accidentally start calling +// dac-ized methods on the objects) +// 2. This isolates DBI from VM headers. +// 3. This isolates DBI from the dac implementation (of DAC_Ptr) +// 4. This is distinct from LSPTR because LSPTRs are truly opaque handles, whereas VMPtrs +// move across VM, DAC, and DBI, exposing proper functionality in each component. +// 5. VMPTRs are blittable because they are Target Addresses which act as opaque +// handles outside of the Target / Dac-marshaller. +// +//--------------------------------------------------------------------------------------- + + +template <typename TTargetPtr, typename TDacPtr> +class MSLAYOUT VMPTR_Base +{ + // Underlying pointer into Target address space. + // Target pointers are blittable. + // - In Target: can be used as normal local pointers. + // - In DAC: must be marshalled to a host-pointer and then they can be used via DAC + // - In RS: opaque handles. +private: + TADDR m_addr; + +public: + typedef VMPTR_Base<TTargetPtr,TDacPtr> VMPTR_This; + + // For DBI, VMPTRs are opaque handles. + // But the DAC side is allowed to inspect the handles to get at the raw pointer. +#if defined(ALLOW_VMPTR_ACCESS) + // + // Case 1: Using in DAcDbi implementation + // + + // DAC accessor + TDacPtr GetDacPtr() const + { + SUPPORTS_DAC; + return TDacPtr(m_addr); + } + + + // This will initialize the handle to a given target-pointer. + // We choose TADDR to make it explicit that it's a target pointer and avoid the risk + // of it accidentally getting marshalled to a host pointer. + void SetDacTargetPtr(TADDR addr) + { + SUPPORTS_DAC; + m_addr = addr; + } + + void SetHostPtr(const TTargetPtr * pObject) + { + SUPPORTS_DAC; + m_addr = PTR_HOST_TO_TADDR(pObject); + } + + +#elif !defined(RIGHT_SIDE_COMPILE) + // + // Case 2: Used in Left-side. Can get/set from local pointers. + // + + // This will set initialize from a Target pointer. Since this is happening in the + // Left-side (Target), the pointer is local. + // This is commonly used by the Left-side to create a VMPTR_ for a notification event. + void SetRawPtr(TTargetPtr * ptr) + { + m_addr = reinterpret_cast<TADDR>(ptr); + } + + // This will get the raw underlying target pointer. + // This can be used by inproc Left-side code to unwrap a VMPTR (Eg, for a func-eval + // hijack or in-proc worker threads) + TTargetPtr * GetRawPtr() + { + return reinterpret_cast<TTargetPtr*>(m_addr); + } + + // Convenience for converting TTargetPtr --> VMPTR + static VMPTR_This MakePtr(TTargetPtr * ptr) + { +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:6001) // PREfast warning: Using uninitialize memory 't' +#endif // _PREFAST_ + + VMPTR_This t; + t.SetRawPtr(ptr); + return t; + +#ifdef _PREFAST_ +#pragma warning(pop) +#endif // _PREFAST_ + } + + +#else + // + // Case 3: Used in RS. Opaque handles only. + // +#endif + + +#ifndef DACCESS_COMPILE + // For compatibility, these can be converted to LSPTRs on the RS or LS (case 2 and 3). We don't allow + // this in the DAC case because it's a cast between address spaces which we're trying to eliminate + // in the DAC code. + // @dbgtodo inspection: LSPTRs will go away entirely once we've moved completely over to DAC + LsPointer<TTargetPtr> ToLsPtr() + { + return LsPointer<TTargetPtr>::MakePtr( reinterpret_cast<TTargetPtr *>(m_addr)); + } +#endif + + // + // Operators to emulate Pointer semantics. + // + bool IsNull() { SUPPORTS_DAC; return m_addr == NULL; } + + static VMPTR_This NullPtr() + { + SUPPORTS_DAC; + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:6001) // PREfast warning: Using uninitialize memory 't' +#endif // _PREFAST_ + + VMPTR_This dummy; + dummy.m_addr = NULL; + return dummy; + +#ifdef _PREFAST_ +#pragma warning(pop) +#endif // _PREFAST_ + } + + bool operator!= (VMPTR_This vmOther) const { SUPPORTS_DAC; return this->m_addr != vmOther.m_addr; } + bool operator== (VMPTR_This vmOther) const { SUPPORTS_DAC; return this->m_addr == vmOther.m_addr; } +}; + +#if defined(ALLOW_VMPTR_ACCESS) +// Helper macro to define a VMPTR. +// This is used in the DAC case, so this definition connects the pointers up to their DAC values. +#define DEFINE_VMPTR(ls_type, dac_ptr_type, ptr_name) \ + ls_type; \ + typedef VMPTR_Base<ls_type, dac_ptr_type> ptr_name; + +#else +// Helper macro to define a VMPTR. +// This is used in the Right-side and Left-side (but not DAC) case. +// This definition explicitly ignores dac_ptr_type to prevent accidental DAC usage. +#define DEFINE_VMPTR(ls_type, dac_ptr_type, ptr_name) \ + ls_type; \ + typedef VMPTR_Base<ls_type, void> ptr_name; + +#endif + +// Declare VMPTRs. +// The naming convention for instantiating a VMPTR is a 'vm' prefix. +// +// VM definition, DAC definition, pretty name for VMPTR +DEFINE_VMPTR(class AppDomain, PTR_AppDomain, VMPTR_AppDomain); + +// Need to be careful not to annoy the compiler here since DT_CONTEXT is a typedef, not a struct. +// DEFINE_VMPTR(struct _CONTEXT, PTR_CONTEXT, VMPTR_CONTEXT); +#if defined(ALLOW_VMPTR_ACCESS) +typedef VMPTR_Base<DT_CONTEXT, PTR_CONTEXT> VMPTR_CONTEXT; +#else +typedef VMPTR_Base<DT_CONTEXT, void > VMPTR_CONTEXT; +#endif + +// DomainFile is a base-class for a CLR module, with app-domain affinity. +// For domain-neutral modules (like mscorlib), there is a DomainFile instance +// for each appdomain the module lives in. +// This is the canonical handle ICorDebug uses to a CLR module. +DEFINE_VMPTR(class DomainFile, PTR_DomainFile, VMPTR_DomainFile); +DEFINE_VMPTR(class Module, PTR_Module, VMPTR_Module); + +// DomainAssembly derives from DomainFile and represents a manifest module. +DEFINE_VMPTR(class DomainAssembly, PTR_DomainAssembly, VMPTR_DomainAssembly); +DEFINE_VMPTR(class Assembly, PTR_Assembly, VMPTR_Assembly); + +DEFINE_VMPTR(class PEFile, PTR_PEFile, VMPTR_PEFile); +DEFINE_VMPTR(class MethodDesc, PTR_MethodDesc, VMPTR_MethodDesc); +DEFINE_VMPTR(class FieldDesc, PTR_FieldDesc, VMPTR_FieldDesc); + +// ObjectHandle is a safe way to represent an object into the GC heap. It gets updated +// when a GC occurs. +DEFINE_VMPTR(struct OBJECTHANDLE__, TADDR, VMPTR_OBJECTHANDLE); + +DEFINE_VMPTR(class TypeHandle, PTR_TypeHandle, VMPTR_TypeHandle); + +// A VMPTR_Thread represents a thread that has entered the runtime at some point. +// It may or may not have executed managed code yet; and it may or may not have managed code +// on its callstack. +DEFINE_VMPTR(class Thread, PTR_Thread, VMPTR_Thread); + +DEFINE_VMPTR(class Object, PTR_Object, VMPTR_Object); + +DEFINE_VMPTR(class CrstBase, PTR_Crst, VMPTR_Crst); +DEFINE_VMPTR(class SimpleRWLock, PTR_SimpleRWLock, VMPTR_SimpleRWLock); +DEFINE_VMPTR(class SimpleRWLock, PTR_SimpleRWLock, VMPTR_RWLock); +DEFINE_VMPTR(struct ReJitInfo, PTR_ReJitInfo, VMPTR_ReJitInfo); +DEFINE_VMPTR(struct SharedReJitInfo, PTR_SharedReJitInfo, VMPTR_SharedReJitInfo); + + +typedef CORDB_ADDRESS GENERICS_TYPE_TOKEN; + + +//----------------------------------------------------------------------------- +// We pass some fixed size strings in the IPC block. +// Helper class to wrap the buffer and protect against buffer overflows. +// This should be binary compatible w/ a wchar[] array. +//----------------------------------------------------------------------------- + +template <int nMaxLengthIncludingNull> +class MSLAYOUT EmbeddedIPCString +{ +public: + // Set, caller responsibility that wcslen(pData) < nMaxLengthIncludingNull + void SetString(const WCHAR * pData) + { + // If the string doesn't fit into the buffer, that's an issue (and so this is a real + // assert, not just a simplifying assumption). To fix it, either: + // - make the buffer larger + // - don't pass the string as an embedded string in the IPC block. + // This will truncate (rather than AV on the RS). + int ret; + ret = SafeCopy(pData); + + // See comment above - caller should guarantee that buffer is large enough. + _ASSERTE(ret != STRUNCATE); + } + + // Set a string from a substring. This will truncate if necessary. + void SetStringTruncate(const WCHAR * pData) + { + // ignore return value because truncation is ok. + SafeCopy(pData); + } + + const WCHAR * GetString() + { + // For a null-termination just in case an issue in the debuggee process + // yields a malformed string. + m_data[nMaxLengthIncludingNull - 1] = W('\0'); + return &m_data[0]; + } + int GetMaxSize() const { return nMaxLengthIncludingNull; } + +private: + int SafeCopy(const WCHAR * pData) + { + return wcsncpy_s( + m_data, nMaxLengthIncludingNull, + pData, _TRUNCATE); + } + WCHAR m_data[nMaxLengthIncludingNull]; +}; + +// +// Types of events that can be sent between the Runtime Controller and +// the Debugger Interface. Some of these events are one way only, while +// others go both ways. The grouping of the event numbers is an attempt +// to show this distinction and perhaps even allow generic operations +// based on the type of the event. +// +enum DebuggerIPCEventType +{ +#define IPC_EVENT_TYPE0(type, val) type = val, +#define IPC_EVENT_TYPE1(type, val) type = val, +#define IPC_EVENT_TYPE2(type, val) type = val, +#include "dbgipceventtypes.h" +#undef IPC_EVENT_TYPE2 +#undef IPC_EVENT_TYPE1 +#undef IPC_EVENT_TYPE0 +}; + +#ifdef _DEBUG + +// This is a static debugging structure to help breaking at the right place. +// Debug only. This is to track the number of events that have been happened so far. +// User can choose to set break point base on the number of events. +// Variables are named as the event name with prefix m_iDebugCount. For example +// m_iDebugCount_DB_IPCE_BREAKPOINT if for event DB_IPCE_BREAKPOINT. +struct MSLAYOUT DebugEventCounter +{ +// we don't need the event type 0 +#define IPC_EVENT_TYPE0(type, val) +#define IPC_EVENT_TYPE1(type, val) int m_iDebugCount_##type; +#define IPC_EVENT_TYPE2(type, val) int m_iDebugCount_##type; +#include "dbgipceventtypes.h" +#undef IPC_EVENT_TYPE2 +#undef IPC_EVENT_TYPE1 +#undef IPC_EVENT_TYPE0 +}; +#endif // _DEBUG + + +#if !defined(DACCESS_COMPILE) + +struct MSLAYOUT IPCEventTypeNameMapping + { + DebuggerIPCEventType eventType; + const char * eventName; +}; + +extern const IPCEventTypeNameMapping DECLSPEC_SELECTANY DbgIPCEventTypeNames[] = +{ + #define IPC_EVENT_TYPE0(type, val) { type, #type }, + #define IPC_EVENT_TYPE1(type, val) { type, #type }, + #define IPC_EVENT_TYPE2(type, val) { type, #type }, + #include "dbgipceventtypes.h" + #undef IPC_EVENT_TYPE2 + #undef IPC_EVENT_TYPE1 + #undef IPC_EVENT_TYPE0 + { DB_IPCE_INVALID_EVENT, "DB_IPCE_Error" } +}; + +const size_t nameCount = sizeof(DbgIPCEventTypeNames) / sizeof(DbgIPCEventTypeNames[0]); + + +struct MSLAYOUT IPCENames // We use a class/struct so that the function can remain in a shared header file +{ + static const DebuggerIPCEventType GetEventType(__in_z char * strEventType) + { + // pass in the string of event name and find the matching enum value + // This is a linear search which is pretty slow. However, this is only used + // at startup time when debug assert is turn on and with registry key set. So it is not that bad. + // + for (size_t i = 0; i < nameCount; i++) + { + if (_stricmp(DbgIPCEventTypeNames[i].eventName, strEventType) == 0) + return DbgIPCEventTypeNames[i].eventType; + } + return DB_IPCE_INVALID_EVENT; + } + static const char * GetName(DebuggerIPCEventType eventType) + { + + enum DbgIPCEventTypeNum + { + #define IPC_EVENT_TYPE0(type, val) type##_Num, + #define IPC_EVENT_TYPE1(type, val) type##_Num, + #define IPC_EVENT_TYPE2(type, val) type##_Num, + #include "dbgipceventtypes.h" + #undef IPC_EVENT_TYPE2 + #undef IPC_EVENT_TYPE1 + #undef IPC_EVENT_TYPE0 + }; + + unsigned int i, lim; + + if (eventType < DB_IPCE_DEBUGGER_FIRST) + { + i = DB_IPCE_RUNTIME_FIRST_Num + 1; + lim = DB_IPCE_DEBUGGER_FIRST_Num; + } + else + { + i = DB_IPCE_DEBUGGER_FIRST_Num + 1; + lim = nameCount; + } + + for (/**/; i < lim; i++) + { + if (DbgIPCEventTypeNames[i].eventType == eventType) + return DbgIPCEventTypeNames[i].eventName; + } + + return DbgIPCEventTypeNames[nameCount - 1].eventName; + } +}; + +#endif // !DACCESS_COMPILE + +// +// NOTE: CPU-specific values below! +// +// DebuggerREGDISPLAY is very similar to the EE REGDISPLAY structure. It holds +// register values that can be saved over calls for each frame in a stack +// trace. +// +// DebuggerIPCE_FloatCount is the number of doubles in the processor's +// floating point stack. +// +// <TODO>Note: We used to just pass the values of the registers for each frame to the Right Side, but I had to add in the +// address of each register, too, to support using enregistered variables on non-leaf frames as args to a func eval. Its +// very, very possible that we would rework the entire code base to just use the register's address instead of passing +// both, but its way, way too late in V1 to undertake that, so I'm just using these addresses to suppport our one func +// eval case. Clearly, this needs to be cleaned up post V1. +// +// -- Fri Feb 09 11:21:24 2001</TODO> +// + +struct MSLAYOUT DebuggerREGDISPLAY +{ +#if defined(DBG_TARGET_X86) + #define DebuggerIPCE_FloatCount 8 + + SIZE_T Edi; + void *pEdi; + SIZE_T Esi; + void *pEsi; + SIZE_T Ebx; + void *pEbx; + SIZE_T Edx; + void *pEdx; + SIZE_T Ecx; + void *pEcx; + SIZE_T Eax; + void *pEax; + SIZE_T FP; + void *pFP; + SIZE_T SP; + SIZE_T PC; + +#elif defined(DBG_TARGET_AMD64) + #define DebuggerIPCE_FloatCount 16 + + SIZE_T Rax; + void *pRax; + SIZE_T Rcx; + void *pRcx; + SIZE_T Rdx; + void *pRdx; + SIZE_T Rbx; + void *pRbx; + SIZE_T Rbp; + void *pRbp; + SIZE_T Rsi; + void *pRsi; + SIZE_T Rdi; + void *pRdi; + + SIZE_T R8; + void *pR8; + SIZE_T R9; + void *pR9; + SIZE_T R10; + void *pR10; + SIZE_T R11; + void *pR11; + SIZE_T R12; + void *pR12; + SIZE_T R13; + void *pR13; + SIZE_T R14; + void *pR14; + SIZE_T R15; + void *pR15; + + SIZE_T SP; + SIZE_T PC; +#elif defined(DBG_TARGET_ARM) + #define DebuggerIPCE_FloatCount 32 + + SIZE_T R0; + void *pR0; + SIZE_T R1; + void *pR1; + SIZE_T R2; + void *pR2; + SIZE_T R3; + void *pR3; + SIZE_T R4; + void *pR4; + SIZE_T R5; + void *pR5; + SIZE_T R6; + void *pR6; + SIZE_T R7; + void *pR7; + SIZE_T R8; + void *pR8; + SIZE_T R9; + void *pR9; + SIZE_T R10; + void *pR10; + SIZE_T R11; + void *pR11; + SIZE_T R12; + void *pR12; + SIZE_T SP; + void *pSP; + SIZE_T LR; + void *pLR; + SIZE_T PC; + void *pPC; +#elif defined(DBG_TARGET_ARM64) + #define DebuggerIPCE_FloatCount 32 + + SIZE_T X[29]; + SIZE_T SP; + SIZE_T FP; + SIZE_T LR; + SIZE_T PC; +#else + #define DebuggerIPCE_FloatCount 1 + + SIZE_T PC; + SIZE_T FP; + SIZE_T SP; + void *pFP; +#endif +}; + +inline LPVOID GetSPAddress(const DebuggerREGDISPLAY * display) +{ + return (LPVOID)&display->SP; +} + +#if !defined(DBG_TARGET_AMD64) && !defined(DBG_TARGET_ARM) +inline LPVOID GetFPAddress(const DebuggerREGDISPLAY * display) +{ + return (LPVOID)&display->FP; +} +#endif // !DBG_TARGET_AMD64 + + +class MSLAYOUT FramePointer +{ +friend bool IsCloserToLeaf(FramePointer fp1, FramePointer fp2); +friend bool IsCloserToRoot(FramePointer fp1, FramePointer fp2); +friend bool IsEqualOrCloserToLeaf(FramePointer fp1, FramePointer fp2); +friend bool IsEqualOrCloserToRoot(FramePointer fp1, FramePointer fp2); + +public: + + static FramePointer MakeFramePointer(LPVOID sp) + { + LIMITED_METHOD_DAC_CONTRACT; + FramePointer fp; + fp.m_sp = sp; + return fp; + } + + static FramePointer MakeFramePointer(UINT_PTR sp) + { + SUPPORTS_DAC; + return MakeFramePointer((LPVOID)sp); + } + + inline bool operator==(FramePointer fp) + { + return (m_sp == fp.m_sp); + } + + inline bool operator!=(FramePointer fp) + { + return !(*this == fp); + } + + // This is needed because on the RS, the m_id values of CordbFrame and + // CordbChain are really FramePointers. + LPVOID GetSPValue() const + { + return m_sp; + } + + +private: + // Declare some private constructors which signatures matching common usage of FramePointer + // to prevent people from accidentally assigning a pointer to a FramePointer(). + FramePointer &operator=(LPVOID sp); + FramePointer &operator=(BYTE* sp); + FramePointer &operator=(const BYTE* sp); + + LPVOID m_sp; +}; + +// For non-IA64 platforms, we use stack pointers as frame pointers. +// (Stack grows towards smaller address.) +#define LEAF_MOST_FRAME FramePointer::MakeFramePointer((LPVOID)NULL) +#define ROOT_MOST_FRAME FramePointer::MakeFramePointer((LPVOID)-1) + +static_assert_no_msg(sizeof(FramePointer) == sizeof(void*)); + + +inline bool IsCloserToLeaf(FramePointer fp1, FramePointer fp2) +{ + return (fp1.m_sp < fp2.m_sp); +} + +inline bool IsCloserToRoot(FramePointer fp1, FramePointer fp2) +{ + return (fp1.m_sp > fp2.m_sp); +} + +inline bool IsEqualOrCloserToLeaf(FramePointer fp1, FramePointer fp2) +{ + return !IsCloserToRoot(fp1, fp2); +} + +inline bool IsEqualOrCloserToRoot(FramePointer fp1, FramePointer fp2) +{ + return !IsCloserToLeaf(fp1, fp2); +} + + +// struct DebuggerIPCE_FuncData: DebuggerIPCE_FuncData holds data +// to describe a given function, its +// class, and a little bit about the code for the function. This is used +// in the stack trace result data to pass function information back that +// may be needed. Its also used when getting data about a specific function. +// +// void* nativeStartAddressPtr: Ptr to CORDB_ADDRESS, which is +// the address of the real start address of the native code. +// This field will be NULL only if the method hasn't been JITted +// yet (and thus no code is available). Otherwise, it will be +// the adress of a CORDB_ADDRESS in the remote memory. This +// CORDB_ADDRESS may be NULL, in which case the code is unavailable +// has been pitched (return CORDBG_E_CODE_NOT_AVAILABLE) +// +// SIZE_T nVersion: The version of the code that this instance of the +// function is using. +struct MSLAYOUT DebuggerIPCE_FuncData +{ + mdMethodDef funcMetadataToken; + VMPTR_DomainFile vmDomainFile; + + mdTypeDef classMetadataToken; + + void* ilStartAddress; + SIZE_T ilSize; + + SIZE_T currentEnCVersion; + + mdSignature localVarSigToken; + + +}; + +// struct DebuggerIPCE_JITFuncData: DebuggerIPCE_JITFuncData holds +// a little bit about the JITted code for the function. +// +// void* nativeStartAddressPtr: Ptr to CORDB_ADDRESS, which is +// the address of the real start address of the native code. +// This field will be NULL only if the method hasn't been JITted +// yet (and thus no code is available). Otherwise, it will be +// the address of a CORDB_ADDRESS in the remote memory. This +// CORDB_ADDRESS may be NULL, in which case the code is unavailable +// or has been pitched (return CORDBG_E_CODE_NOT_AVAILABLE) +// +// SIZE_T nativeSize: Size of the native code. +// +// SIZE_T nativeOffset: Offset from the beginning of the function, +// in bytes. This may be non-zero even when nativeStartAddressPtr +// is NULL +// void * nativeCodeJITInfoToken: An opaque value to hand back to the left +// side when fetching the JITInfo for the native code, i.e. the +// IL->native maps for the variables. This may be NULL if no JITInfo is available. +// void * nativeCodeMethodDescToken: An opaque value to hand back to the left +// side when fetching the code. In addition this token can act as the +// unique identity for the native code in the case where there are +// multiple blobs of native code per IL method (i.e. if the method is +// generic code of some kind) +// BOOL isInstantiatedGeneric: Indicates if the method is +// generic code of some kind. +// BOOL jsutAfterILThrow: indicates that code just threw a software exception and +// nativeOffset points to an instruction just after [call IL_Throw]. +// This is being used to figure out a real offset of the exception origin. +// By subtracting STACKWALK_CONTROLPC_ADJUST_OFFSET from nativeOffset you can get +// an address somewhere inside [call IL_Throw] instruction. +// void *ilToNativeMapAddr etc.: If nativeCodeJITInfoToken is not NULL then these +// specify the table giving the mapping of IPs. +struct MSLAYOUT DebuggerIPCE_JITFuncData +{ + TADDR nativeStartAddressPtr; + SIZE_T nativeHotSize; + + // If we have a cold region, need its size & the pointer to where starts. + TADDR nativeStartAddressColdPtr; + SIZE_T nativeColdSize; + + + SIZE_T nativeOffset; + LSPTR_DJI nativeCodeJITInfoToken; + VMPTR_MethodDesc vmNativeCodeMethodDescToken; + +#if defined(DBG_TARGET_WIN64) || defined(DBG_TARGET_ARM) + BOOL fIsFilterFrame; + SIZE_T parentNativeOffset; + FramePointer fpParentOrSelf; +#endif // DBG_TARGET_WIN64 || DBG_TARGET_ARM + + // indicates if the MethodDesc is a generic function or a method inside a generic class (or + // both!). + BOOL isInstantiatedGeneric; + + // this is the version of the jitted code + SIZE_T enCVersion; + + BOOL jsutAfterILThrow; +}; + +// +// DebuggerIPCE_STRData holds data for each stack frame or chain. This data is passed +// from the RC to the DI during a stack walk. +// +#if defined(_MSC_VER) +#pragma warning( push ) +#pragma warning( disable:4324 ) // the compiler pads a structure to comply with alignment requirements +#endif // ARM context structures have a 16-byte alignment requirement +struct MSLAYOUT DebuggerIPCE_STRData +{ + FramePointer fp; + // @dbgtodo stackwalker/shim- Ideally we should be able to get rid of the DebuggerREGDISPLAY and just use the CONTEXT. + DT_CONTEXT ctx; + DebuggerREGDISPLAY rd; + bool quicklyUnwound; + + VMPTR_AppDomain vmCurrentAppDomainToken; + + + enum EType + { + cMethodFrame = 0, + cChain, + cStubFrame, + cRuntimeNativeFrame + } eType; + + union MSLAYOUT + { + // Data for a chain + struct MSLAYOUT + { + CorDebugChainReason chainReason; + bool managed; + } u; + + // Data for a Method + struct MSLAYOUT + { + struct DebuggerIPCE_FuncData funcData; + struct DebuggerIPCE_JITFuncData jitFuncData; + SIZE_T ILOffset; + CorDebugMappingResult mapping; + + bool fVarArgs; + + // Indicates whether the managed method has any metadata. + // Some dynamic methods such as IL stubs and LCG methods don't have any metadata. + // This is used only by the V3 stackwalker, not the V2 one, because we only + // expose dynamic methods as real stack frames in V3. + bool fNoMetadata; + + TADDR taAmbientESP; + + GENERICS_TYPE_TOKEN exactGenericArgsToken; + DWORD dwExactGenericArgsTokenIndex; + + } v; + + // Data for an Stub Frame. + struct MSLAYOUT + { + mdMethodDef funcMetadataToken; + VMPTR_DomainFile vmDomainFile; + VMPTR_MethodDesc vmMethodDesc; + CorDebugInternalFrameType frameType; + } stubFrame; + + }; +}; +#if defined(_MSC_VER) +#pragma warning( pop ) +#endif + +// +// DebuggerIPCE_BasicTypeData and DebuggerIPCE_ExpandedTypeData +// hold data for each type sent across the +// boundary, whether it be a constructed type List<String> or a non-constructed +// type such as String, Foo or Object. +// +// Logically speaking DebuggerIPCE_BasicTypeData might just be "typeHandle", as +// we could then send further events to ask what the elementtype, typeToken and moduleToken +// are for the type handle. But as +// nearly all types are non-generic we send across even the basic type information in +// the slightly expanded form shown below, sending the element type and the +// tokens with the type handle itself. The fields debuggerModuleToken, metadataToken and typeHandle +// are only used as follows: +// elementType debuggerModuleToken metadataToken typeHandle +// E_T_INT8 : E_T_INT8 No No No +// Boxed E_T_INT8: E_T_CLASS No No No +// E_T_CLASS, non-generic class: E_T_CLASS Yes Yes No +// E_T_VALUETYPE, non-generic: E_T_VALUETYPE Yes Yes No +// E_T_CLASS, generic class: E_T_CLASS Yes Yes Yes +// E_T_VALUETYPE, generic class: E_T_VALUETYPE Yes Yes Yes +// E_T_BYREF : E_T_BYREF No No Yes +// E_T_PTR : E_T_PTR No No Yes +// E_T_ARRAY etc. : E_T_ARRAY No No Yes +// E_T_FNPTR etc. : E_T_FNPTR No No Yes +// This allows us to always set "typeHandle" to NULL except when dealing with highly nested +// types or function-pointer types (the latter are too complexe to transfer over in one hit). +// + +struct MSLAYOUT DebuggerIPCE_BasicTypeData +{ + CorElementType elementType; + mdTypeDef metadataToken; + VMPTR_Module vmModule; + VMPTR_DomainFile vmDomainFile; + VMPTR_TypeHandle vmTypeHandle; +}; + +// DebuggerIPCE_ExpandedTypeData contains more information showing further +// details for array types, byref types etc. +// Whenever you fetch type information from the left-side +// you get back one of these. These in turn contain further +// DebuggerIPCE_BasicTypeData's and typeHandles which you can +// then query to get further information about the type parameters. +// This copes with the nested cases, e.g. jagged arrays, +// String ****, &(String*), Pair<String,Pair<String>> +// and so on. +// +// So this type information is not "fully expanded", it's just a little +// more detail then DebuggerIPCE_BasicTypeData. For type +// instantiatons (e.g. List<int>) and +// function pointer types you will need to make further requests for +// information about the type parameters. +// For array types there is always only one type parameter so +// we include that as part of the expanded data. +// +// +struct MSLAYOUT DebuggerIPCE_ExpandedTypeData +{ + CorElementType elementType; // Note this is _never_ E_T_VAR, E_T_WITH or E_T_MVAR + union MSLAYOUT + { + // used for E_T_CLASS and E_T_VALUECLASS, E_T_PTR, E_T_BYREF etc. + // For non-constructed E_T_CLASS or E_T_VALUECLASS the tokens will be set and the typeHandle will be NULL + // For constructed E_T_CLASS or E_T_VALUECLASS the tokens will be set and the typeHandle will be non-NULL + // For E_T_PTR etc. the tokens will be NULL and the typeHandle will be non-NULL. + struct MSLAYOUT + { + mdTypeDef metadataToken; + VMPTR_Module vmModule; + VMPTR_DomainFile vmDomainFile; + VMPTR_TypeHandle typeHandle; // if non-null then further fetches will be needed to get type arguments + } ClassTypeData; + + // used for E_T_PTR, E_T_BYREF etc. + struct MSLAYOUT + { + DebuggerIPCE_BasicTypeData unaryTypeArg; // used only when sending back to debugger + } UnaryTypeData; + + + // used for E_T_ARRAY etc. + struct MSLAYOUT + { + DebuggerIPCE_BasicTypeData arrayTypeArg; // used only when sending back to debugger + DWORD arrayRank; + } ArrayTypeData; + + // used for E_T_FNPTR + struct MSLAYOUT + { + VMPTR_TypeHandle typeHandle; // if non-null then further fetches needed to get type arguments + } NaryTypeData; + + }; +}; + +// DebuggerIPCE_TypeArgData is used when sending type arguments +// across to a funceval. It contains the DebuggerIPCE_ExpandedTypeData describing the +// essence of the type, but the typeHandle and other +// BasicTypeData fields should be zero and will be ignored. +// The DebuggerIPCE_ExpandedTypeData is then followed +// by the required number of type arguments, each of which +// will be a further DebuggerIPCE_TypeArgData record in the stream of +// flattened type argument data. +struct MSLAYOUT DebuggerIPCE_TypeArgData +{ + DebuggerIPCE_ExpandedTypeData data; + unsigned int numTypeArgs; // number of immediate children on the type tree +}; + + +// +// DebuggerIPCE_ObjectData holds the results of a +// GetAndSendObjectInfo, i.e., all the info about an object that the +// Right Side would need to access it. (This include array, string, +// and nstruct info.) +// +struct MSLAYOUT DebuggerIPCE_ObjectData +{ + void *objRef; + bool objRefBad; + SIZE_T objSize; + + // Offset from the beginning of the object to the beginning of the first field + SIZE_T objOffsetToVars; + + // The type of the object.... + struct DebuggerIPCE_ExpandedTypeData objTypeData; + + union MSLAYOUT + { + struct MSLAYOUT + { + SIZE_T length; + SIZE_T offsetToStringBase; + } stringInfo; + + struct MSLAYOUT + { + SIZE_T rank; + SIZE_T offsetToArrayBase; + SIZE_T offsetToLowerBounds; // 0 if not present + SIZE_T offsetToUpperBounds; // 0 if not present + SIZE_T componentCount; + SIZE_T elementSize; + } arrayInfo; + + struct MSLAYOUT + { + struct DebuggerIPCE_BasicTypeData typedByrefType; // the type of the thing contained in a typedByref... + } typedByrefInfo; + }; +}; + +// +// Remote enregistered info used by CordbValues and for passing +// variable homes between the left and right sides during a func eval. +// + +enum RemoteAddressKind +{ + RAK_NONE = 0, + RAK_REG, + RAK_REGREG, + RAK_REGMEM, + RAK_MEMREG, + RAK_FLOAT, + RAK_END +}; + +const CORDB_ADDRESS kLeafFrameRegAddr = 0; +const CORDB_ADDRESS kNonLeafFrameRegAddr = (CORDB_ADDRESS)(-1); + +struct MSLAYOUT RemoteAddress +{ + RemoteAddressKind kind; + void *frame; + + CorDebugRegister reg1; + void *reg1Addr; + SIZE_T reg1Value; // this is the actual value of the register + + union MSLAYOUT + { + struct MSLAYOUT + { + CorDebugRegister reg2; + void *reg2Addr; + SIZE_T reg2Value; // this is the actual value of the register + } u; + + CORDB_ADDRESS addr; + DWORD floatIndex; + }; +}; + +// +// DebuggerIPCE_FuncEvalType specifies the type of a function +// evaluation that will occur. +// +enum DebuggerIPCE_FuncEvalType +{ + DB_IPCE_FET_NORMAL, + DB_IPCE_FET_NEW_OBJECT, + DB_IPCE_FET_NEW_OBJECT_NC, + DB_IPCE_FET_NEW_STRING, + DB_IPCE_FET_NEW_ARRAY, + DB_IPCE_FET_RE_ABORT +}; + + +enum NameChangeType +{ + APP_DOMAIN_NAME_CHANGE, + THREAD_NAME_CHANGE +}; + +// +// DebuggerIPCE_FuncEvalArgData holds data for each argument to a +// function evaluation. +// +struct MSLAYOUT DebuggerIPCE_FuncEvalArgData +{ + RemoteAddress argHome; // enregistered variable home + void *argAddr; // address if not enregistered + CorElementType argElementType; + unsigned int fullArgTypeNodeCount; // Pointer to LS (DebuggerIPCE_TypeArgData *) buffer holding full description of the argument type (if needed - only needed for struct types) + void *fullArgType; // Pointer to LS (DebuggerIPCE_TypeArgData *) buffer holding full description of the argument type (if needed - only needed for struct types) + BYTE argLiteralData[8]; // copy of generic value data + bool argIsLiteral; // true if value is in argLiteralData + bool argIsHandleValue; // true if argAddr is OBJECTHANDLE +}; + + +// +// DebuggerIPCE_FuncEvalInfo holds info necessary to setup a func eval +// operation. +// +struct MSLAYOUT DebuggerIPCE_FuncEvalInfo +{ + VMPTR_Thread vmThreadToken; + DebuggerIPCE_FuncEvalType funcEvalType; + mdMethodDef funcMetadataToken; + mdTypeDef funcClassMetadataToken; + VMPTR_DomainFile vmDomainFile; + RSPTR_CORDBEVAL funcEvalKey; + bool evalDuringException; + + unsigned int argCount; + unsigned int genericArgsCount; + unsigned int genericArgsNodeCount; + + SIZE_T stringSize; + + SIZE_T arrayRank; +}; + + +// +// Used in DebuggerIPCFirstChanceData. This tells the LS what action to take within the hijack +// +enum HijackAction +{ + HIJACK_ACTION_EXIT_UNHANDLED, + HIJACK_ACTION_EXIT_HANDLED, + HIJACK_ACTION_WAIT +}; + +// +// DebuggerIPCFirstChanceData holds info communicated from the LS to the RS when signaling that an exception does not +// belong to the runtime from a first chance hijack. This is used when Win32 debugging only. +// +struct MSLAYOUT DebuggerIPCFirstChanceData +{ + LSPTR_CONTEXT pLeftSideContext; + HijackAction action; + UINT debugCounter; +}; + +// +// DebuggerIPCSecondChanceData holds info communicated from the RS +// to the LS when setting up a second chance exception hijack. This is +// used when Win32 debugging only. +// +struct MSLAYOUT DebuggerIPCSecondChanceData +{ + DT_CONTEXT threadContext; +}; + + + +//----------------------------------------------------------------------------- +// This struct holds pointer from the LS and needs to copy to +// the RS. We have to free the memory on the RS. +// The transfer function is called when the RS first reads the event. At this point, +// the LS is stopped while sending the event. Thus the LS pointers only need to be +// valid while the LS is in SendIPCEvent. +// +// Since this data is in an IPC/Marshallable block, it can't have any Ctors (holders) +// in it. +//----------------------------------------------------------------------------- +struct MSLAYOUT Ls_Rs_BaseBuffer +{ +#ifdef RIGHT_SIDE_COMPILE +protected: + // copy data can happen on both LS and RS. In LS case, + // ReadProcessMemory is really reading from its own process memory. + // + void CopyLSDataToRSWorker(ICorDebugDataTarget * pTargethProcess); + + // retrieve the RS data and own it + BYTE *TransferRSDataWorker() + { + BYTE *pbRS = m_pbRS; + m_pbRS = NULL; + return pbRS; + } +public: + + + void CleanUp() + { + if (m_pbRS != NULL) + { + delete [] m_pbRS; + m_pbRS = NULL; + } + } +#else +public: + // Only LS can call this API + void SetLsData(BYTE *pbLS, DWORD cbSize) + { + m_pbRS = NULL; + m_pbLS = pbLS; + m_cbSize = cbSize; + } +#endif // RIGHT_SIDE_COMPILE + +public: + // Common APIs. + DWORD GetSize() { return m_cbSize; } + + + +protected: + // Size of data in bytes + DWORD m_cbSize; + + // If this is non-null, pointer into LS for buffer. + // LS can free this after the debug event is continued. + BYTE *m_pbLS; // @dbgtodo cross-plat- for cross-platform purposes, this should be a TADDR + + // If this is non-null, pointer into RS for buffer. RS must then free this. + // This buffer was copied from the LS (via CopyLSDataToRSWorker). + BYTE *m_pbRS; +}; + +//----------------------------------------------------------------------------- +// Byte wrapper around the buffer. +//----------------------------------------------------------------------------- +struct MSLAYOUT Ls_Rs_ByteBuffer : public Ls_Rs_BaseBuffer +{ +#ifdef RIGHT_SIDE_COMPILE + BYTE *GetRSPointer() + { + return m_pbRS; + } + + void CopyLSDataToRS(ICorDebugDataTarget * pTarget); + BYTE *TransferRSData() + { + return TransferRSDataWorker(); + } +#endif +}; + +//----------------------------------------------------------------------------- +// Wrapper around a Ls_rS_Buffer to get it as a string. +// This can also do some sanity checking. +//----------------------------------------------------------------------------- +struct MSLAYOUT Ls_Rs_StringBuffer : public Ls_Rs_BaseBuffer +{ +#ifdef RIGHT_SIDE_COMPILE + const WCHAR * GetString() + { + return reinterpret_cast<const WCHAR*> (m_pbRS); + } + + // Copy over the string. + void CopyLSDataToRS(ICorDebugDataTarget * pTarget); + + // Caller will pick up ownership. + // Since caller will delete this data, we can't give back a constant pointer. + WCHAR * TransferStringData() + { + return reinterpret_cast<WCHAR*> (TransferRSDataWorker()); + } +#endif +}; + + +// Data for an Managed Debug Assistant Probe (MDA). +struct MSLAYOUT DebuggerMDANotification +{ + Ls_Rs_StringBuffer szName; + Ls_Rs_StringBuffer szDescription; + Ls_Rs_StringBuffer szXml; + DWORD dwOSThreadId; + CorDebugMDAFlags flags; +}; + + +// The only remaining problem is that register number mappings are different for each platform. It turns out +// that the debugger only uses REGNUM_SP and REGNUM_AMBIENT_SP though, so we can just virtualize these two for +// the target platform. +// Keep this is sync with the definitions in inc/corinfo.h. +#if defined(DBG_TARGET_X86) +#define DBG_TARGET_REGNUM_SP 4 +#define DBG_TARGET_REGNUM_AMBIENT_SP 9 +#ifdef _TARGET_X86_ +static_assert_no_msg(DBG_TARGET_REGNUM_SP == ICorDebugInfo::REGNUM_SP); +static_assert_no_msg(DBG_TARGET_REGNUM_AMBIENT_SP == ICorDebugInfo::REGNUM_AMBIENT_SP); +#endif // _TARGET_X86_ +#elif defined(DBG_TARGET_AMD64) +#define DBG_TARGET_REGNUM_SP 4 +#define DBG_TARGET_REGNUM_AMBIENT_SP 17 +#ifdef _TARGET_AMD64_ +static_assert_no_msg(DBG_TARGET_REGNUM_SP == ICorDebugInfo::REGNUM_SP); +static_assert_no_msg(DBG_TARGET_REGNUM_AMBIENT_SP == ICorDebugInfo::REGNUM_AMBIENT_SP); +#endif // _TARGET_AMD64_ +#elif defined(DBG_TARGET_ARM) +#define DBG_TARGET_REGNUM_SP 13 +#define DBG_TARGET_REGNUM_AMBIENT_SP 17 +#ifdef _TARGET_ARM_ +C_ASSERT(DBG_TARGET_REGNUM_SP == ICorDebugInfo::REGNUM_SP); +C_ASSERT(DBG_TARGET_REGNUM_AMBIENT_SP == ICorDebugInfo::REGNUM_AMBIENT_SP); +#endif // _TARGET_ARM_ +#elif defined(DBG_TARGET_ARM64) +#define DBG_TARGET_REGNUM_SP 31 +#define DBG_TARGET_REGNUM_AMBIENT_SP 34 +#ifdef _TARGET_ARM64_ +C_ASSERT(DBG_TARGET_REGNUM_SP == ICorDebugInfo::REGNUM_SP); +C_ASSERT(DBG_TARGET_REGNUM_AMBIENT_SP == ICorDebugInfo::REGNUM_AMBIENT_SP); +#endif // _TARGET_ARM64_ +#else +#error Target registers are not defined for this platform +#endif + + +// +// Event structure that is passed between the Runtime Controller and the +// Debugger Interface. Some types of events are a fixed size and have +// entries in the main union, while others are variable length and have +// more specialized data structures that are attached to the end of this +// structure. +// +struct MSLAYOUT DebuggerIPCEvent +{ + DebuggerIPCEvent* next; + DebuggerIPCEventType type; + DWORD processId; + VMPTR_AppDomain vmAppDomain; + VMPTR_Thread vmThread; + + HRESULT hr; + bool replyRequired; + bool asyncSend; + + union MSLAYOUT + { + struct MSLAYOUT + { + // Pointer to a BOOL in the target. + CORDB_ADDRESS pfBeingDebugged; + } LeftSideStartupData; + + struct MSLAYOUT + { + // Module whos metadata is being updated + // This tells the RS that the metadata for that module has become invalid. + VMPTR_DomainFile vmDomainFile; + + } MetadataUpdateData; + + struct MSLAYOUT + { + // Handle to CLR's internal appdomain object. + VMPTR_AppDomain vmAppDomain; + } AppDomainData; + + struct MSLAYOUT + { + VMPTR_DomainAssembly vmDomainAssembly; + } AssemblyData; + +#ifdef TEST_DATA_CONSISTENCY + // information necessary for testing whether the LS holds a lock on data + // the RS needs to inspect. See code:DataTest::TestDataSafety and + // code:IDacDbiInterface::TestCrst for more information + struct MSLAYOUT + { + // the lock to be tested + VMPTR_Crst vmCrst; + // indicates whether the LS holds the lock + bool fOkToTake; + } TestCrstData; + + // information necessary for testing whether the LS holds a lock on data + // the RS needs to inspect. See code:DataTest::TestDataSafety and + // code:IDacDbiInterface::TestCrst for more information + struct MSLAYOUT + { + // the lock to be tested + VMPTR_SimpleRWLock vmRWLock; + // indicates whether the LS holds the lock + bool fOkToTake; + } TestRWLockData; +#endif // TEST_DATA_CONSISTENCY + + // Debug event that a module has been loaded + struct MSLAYOUT + { + // Module that was just loaded. + VMPTR_DomainFile vmDomainFile; + }LoadModuleData; + + + struct MSLAYOUT + { + VMPTR_DomainFile vmDomainFile; + LSPTR_ASSEMBLY debuggerAssemblyToken; + } UnloadModuleData; + + + // The given module's pdb has been updated. + // Queury PDB from OOP + struct MSLAYOUT + { + VMPTR_DomainFile vmDomainFile; + } UpdateModuleSymsData; + + DebuggerMDANotification MDANotification; + + struct MSLAYOUT + { + LSPTR_BREAKPOINT breakpointToken; + mdMethodDef funcMetadataToken; + VMPTR_DomainFile vmDomainFile; + bool isIL; + SIZE_T offset; + SIZE_T encVersion; + LSPTR_METHODDESC nativeCodeMethodDescToken; // points to the MethodDesc if !isIL + } BreakpointData; + + struct MSLAYOUT + { + LSPTR_BREAKPOINT breakpointToken; + } BreakpointSetErrorData; + + struct MSLAYOUT + { + LSPTR_STEPPER stepperToken; + VMPTR_Thread vmThreadToken; + FramePointer frameToken; + bool stepIn; + bool rangeIL; + bool IsJMCStop; + unsigned int totalRangeCount; + CorDebugStepReason reason; + CorDebugUnmappedStop rgfMappingStop; + CorDebugIntercept rgfInterceptStop; + unsigned int rangeCount; + COR_DEBUG_STEP_RANGE range; //note that this is an array + } StepData; + + struct MSLAYOUT + { + // An unvalidated GC-handle + VMPTR_OBJECTHANDLE GCHandle; + } GetGCHandleInfo; + + struct MSLAYOUT + { + // An unvalidated GC-handle for which we're returning the results + LSPTR_OBJECTHANDLE GCHandle; + + // The following are initialized by the LS in response to our query: + VMPTR_AppDomain vmAppDomain; // AD that handle is in (only applicable if fValid). + bool fValid; // Did the LS determine the GC handle to be valid? + } GetGCHandleInfoResult; + + // Allocate memory on the left-side + struct MSLAYOUT + { + ULONG bufSize; // number of bytes to allocate + } GetBuffer; + + // Memory allocated on the left-side + struct MSLAYOUT + { + void *pBuffer; // LS pointer to the buffer allocated + HRESULT hr; // success / failure + } GetBufferResult; + + // Free a buffer allocated on the left-side with GetBuffer + struct MSLAYOUT + { + void *pBuffer; // Pointer previously returned in GetBufferResult + } ReleaseBuffer; + + struct MSLAYOUT + { + HRESULT hr; + } ReleaseBufferResult; + + // Apply an EnC edit + struct MSLAYOUT + { + VMPTR_DomainFile vmDomainFile; // Module to edit + DWORD cbDeltaMetadata; // size of blob pointed to by pDeltaMetadata + CORDB_ADDRESS pDeltaMetadata; // pointer to delta metadata in debuggee + // it's the RS's responsibility to allocate and free + // this (and pDeltaIL) using GetBuffer / ReleaseBuffer + CORDB_ADDRESS pDeltaIL; // pointer to delta IL in debugee + DWORD cbDeltaIL; // size of blob pointed to by pDeltaIL + } ApplyChanges; + + struct MSLAYOUT + { + HRESULT hr; + } ApplyChangesResult; + + struct MSLAYOUT + { + mdTypeDef classMetadataToken; + VMPTR_DomainFile vmDomainFile; + LSPTR_ASSEMBLY classDebuggerAssemblyToken; + } LoadClass; + + struct MSLAYOUT + { + mdTypeDef classMetadataToken; + VMPTR_DomainFile vmDomainFile; + LSPTR_ASSEMBLY classDebuggerAssemblyToken; + } UnloadClass; + + struct MSLAYOUT + { + VMPTR_DomainFile vmDomainFile; + bool flag; + } SetClassLoad; + + struct MSLAYOUT + { + VMPTR_OBJECTHANDLE vmExceptionHandle; + bool firstChance; + bool continuable; + } Exception; + + struct MSLAYOUT + { + VMPTR_Thread vmThreadToken; + } ClearException; + + struct MSLAYOUT + { + void *address; + } IsTransitionStub; + + struct MSLAYOUT + { + bool isStub; + } IsTransitionStubResult; + + struct MSLAYOUT + { + CORDB_ADDRESS startAddress; + bool fCanSetIPOnly; + VMPTR_Thread vmThreadToken; + VMPTR_DomainFile vmDomainFile; + mdMethodDef mdMethod; + VMPTR_MethodDesc vmMethodDesc; + SIZE_T offset; + bool fIsIL; + void * firstExceptionHandler; + } SetIP; // this is also used for CanSetIP + + struct MSLAYOUT + { + int iLevel; + + EmbeddedIPCString<MAX_LOG_SWITCH_NAME_LEN + 1> szCategory; + Ls_Rs_StringBuffer szContent; + } FirstLogMessage; + + struct MSLAYOUT + { + int iLevel; + int iReason; + + EmbeddedIPCString<MAX_LOG_SWITCH_NAME_LEN + 1> szSwitchName; + EmbeddedIPCString<MAX_LOG_SWITCH_NAME_LEN + 1> szParentSwitchName; + } LogSwitchSettingMessage; + + // information needed to send to the RS as part of a custom notification from the target + struct MSLAYOUT + { + // Domain file for the domain in which the notification occurred + VMPTR_DomainFile vmDomainFile; + + // metadata token for the type of the CustomNotification object's type + mdTypeDef classToken; + } CustomNotification; + + struct MSLAYOUT + { + VMPTR_Thread vmThreadToken; + CorDebugThreadState debugState; + } SetAllDebugState; + + DebuggerIPCE_FuncEvalInfo FuncEval; + + struct MSLAYOUT + { + CORDB_ADDRESS argDataArea; + LSPTR_DEBUGGEREVAL debuggerEvalKey; + } FuncEvalSetupComplete; + + struct MSLAYOUT + { + RSPTR_CORDBEVAL funcEvalKey; + bool successful; + bool aborted; + void *resultAddr; + + // AppDomain that the result is in. + VMPTR_AppDomain vmAppDomain; + + VMPTR_OBJECTHANDLE vmObjectHandle; + DebuggerIPCE_ExpandedTypeData resultType; + } FuncEvalComplete; + + struct MSLAYOUT + { + LSPTR_DEBUGGEREVAL debuggerEvalKey; + } FuncEvalAbort; + + struct MSLAYOUT + { + LSPTR_DEBUGGEREVAL debuggerEvalKey; + } FuncEvalRudeAbort; + + struct MSLAYOUT + { + LSPTR_DEBUGGEREVAL debuggerEvalKey; + } FuncEvalCleanup; + + struct MSLAYOUT + { + void *objectRefAddress; + VMPTR_OBJECTHANDLE vmObjectHandle; + void *newReference; + } SetReference; + + struct MSLAYOUT + { + NameChangeType eventType; + VMPTR_AppDomain vmAppDomain; + VMPTR_Thread vmThread; + } NameChange; + + struct MSLAYOUT + { + VMPTR_DomainFile vmDomainFile; + BOOL fAllowJitOpts; + BOOL fEnableEnC; + } JitDebugInfo; + + // EnC Remap opportunity + struct MSLAYOUT + { + VMPTR_DomainFile vmDomainFile; + mdMethodDef funcMetadataToken ; // methodDef of function with remap opportunity + SIZE_T currentVersionNumber; // version currently executing + SIZE_T resumeVersionNumber; // latest version + SIZE_T currentILOffset; // the IL offset of the current IP + SIZE_T *resumeILOffset; // pointer into left-side where an offset to resume + // to should be written if remap is desired. + } EnCRemap; + + // EnC Remap has taken place + struct MSLAYOUT + { + VMPTR_DomainFile vmDomainFile; + mdMethodDef funcMetadataToken; // methodDef of function that was remapped + } EnCRemapComplete; + + // Notification that the LS is about to update a CLR data structure to account for a + // specific edit made by EnC (function add/update or field add). + struct MSLAYOUT + { + VMPTR_DomainFile vmDomainFile; + mdToken memberMetadataToken; // Either a methodDef token indicating the function that + // was updated/added, or a fieldDef token indicating the + // field which was added. + mdTypeDef classMetadataToken; // TypeDef token of the class in which the update was made + SIZE_T newVersionNumber; // The new function/module version + } EnCUpdate; + + struct MSLAYOUT + { + void *oldData; + void *newData; + DebuggerIPCE_BasicTypeData type; + } SetValueClass; + + + // Event used to tell LS if a single function is user or non-user code. + // Same structure used to get function status. + // @todo - Perhaps we can bundle these up so we can set multiple funcs w/ 1 event? + struct MSLAYOUT + { + VMPTR_DomainFile vmDomainFile; + mdMethodDef funcMetadataToken; + DWORD dwStatus; + } SetJMCFunctionStatus; + + struct MSLAYOUT + { + TASKID taskid; + } GetThreadForTaskId; + + struct MSLAYOUT + { + VMPTR_Thread vmThreadToken; + } GetThreadForTaskIdResult; + + struct MSLAYOUT + { + CONNID connectionId; + } ConnectionChange; + + struct MSLAYOUT + { + CONNID connectionId; + EmbeddedIPCString<MAX_LONGPATH> wzConnectionName; + } CreateConnection; + + struct MSLAYOUT + { + void *objectToken; + BOOL fStrong; + } CreateHandle; + + struct MSLAYOUT + { + VMPTR_OBJECTHANDLE vmObjectHandle; + } CreateHandleResult; + + // used in DB_IPCE_DISPOSE_HANDLE event + struct MSLAYOUT + { + VMPTR_OBJECTHANDLE vmObjectHandle; + BOOL fStrong; + } DisposeHandle; + + struct MSLAYOUT + { + FramePointer framePointer; + SIZE_T nOffset; + CorDebugExceptionCallbackType eventType; + DWORD dwFlags; + VMPTR_OBJECTHANDLE vmExceptionHandle; + } ExceptionCallback2; + + struct MSLAYOUT + { + CorDebugExceptionUnwindCallbackType eventType; + DWORD dwFlags; + } ExceptionUnwind; + + struct MSLAYOUT + { + VMPTR_Thread vmThreadToken; + FramePointer frameToken; + } InterceptException; + + struct MSLAYOUT + { + VMPTR_Module vmModule; + void * pMetadataStart; + ULONG nMetadataSize; + } MetadataUpdateRequest; + + }; +}; + + +// When using a network transport rather than shared memory buffers CorDBIPC_BUFFER_SIZE is the upper bound +// for a single DebuggerIPCEvent structure. This now relates to the maximal size of a network message and is +// orthogonal to the host's page size. Round the buffer size up to a multiple of 8 since MSVC seems more +// aggressive in this regard than gcc. +#define CorDBIPC_TRANSPORT_BUFFER_SIZE (((sizeof(DebuggerIPCEvent) + 7) / 8) * 8) + +// A DebuggerIPCEvent must fit in the send & receive buffers, which are CorDBIPC_BUFFER_SIZE bytes. +static_assert_no_msg(sizeof(DebuggerIPCEvent) <= CorDBIPC_BUFFER_SIZE); +static_assert_no_msg(CorDBIPC_TRANSPORT_BUFFER_SIZE <= CorDBIPC_BUFFER_SIZE); + +// 2*sizeof(WCHAR) for the two string terminating characters in the FirstLogMessage +#define LOG_MSG_PADDING 4 + +#endif /* _DbgIPCEvents_h_ */ |