summaryrefslogtreecommitdiff
path: root/src/debug/inc
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
commit4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch)
tree98110734c91668dfdbb126fcc0e15ddbd93738ca /src/debug/inc
parentfa45f57ed55137c75ac870356a1b8f76c84b229c (diff)
downloadcoreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/debug/inc')
-rw-r--r--src/debug/inc/.gitmirror1
-rw-r--r--src/debug/inc/amd64/.gitmirror1
-rw-r--r--src/debug/inc/amd64/primitives.h257
-rw-r--r--src/debug/inc/arm/.gitmirror1
-rw-r--r--src/debug/inc/arm/primitives.h179
-rw-r--r--src/debug/inc/arm64/.gitmirror1
-rw-r--r--src/debug/inc/arm64/primitives.h186
-rw-r--r--src/debug/inc/arm_primitives.h113
-rw-r--r--src/debug/inc/common.h323
-rw-r--r--src/debug/inc/coreclrremotedebugginginterfaces.h20
-rw-r--r--src/debug/inc/dacdbiinterface.h2726
-rw-r--r--src/debug/inc/dacdbistructures.h790
-rw-r--r--src/debug/inc/dacdbistructures.inl732
-rw-r--r--src/debug/inc/dbgappdomain.h388
-rw-r--r--src/debug/inc/dbgipcevents.h2360
-rw-r--r--src/debug/inc/dbgipceventtypes.h143
-rw-r--r--src/debug/inc/dbgtargetcontext.h450
-rw-r--r--src/debug/inc/dbgtransportsession.h849
-rw-r--r--src/debug/inc/dbgutil.h93
-rw-r--r--src/debug/inc/ddmarshalutil.h394
-rw-r--r--src/debug/inc/dump/.gitmirror1
-rw-r--r--src/debug/inc/dump/dumpcommon.h108
-rw-r--r--src/debug/inc/eventredirection.h84
-rw-r--r--src/debug/inc/i386/.gitmirror1
-rw-r--r--src/debug/inc/i386/primitives.h223
-rw-r--r--src/debug/inc/readonlydatatargetfacade.h98
-rw-r--r--src/debug/inc/readonlydatatargetfacade.inl139
-rw-r--r--src/debug/inc/stringcopyholder.h59
-rw-r--r--src/debug/inc/twowaypipe.h104
29 files changed, 10824 insertions, 0 deletions
diff --git a/src/debug/inc/.gitmirror b/src/debug/inc/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/debug/inc/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/debug/inc/amd64/.gitmirror b/src/debug/inc/amd64/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/debug/inc/amd64/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/debug/inc/amd64/primitives.h b/src/debug/inc/amd64/primitives.h
new file mode 100644
index 0000000000..0b13670c4a
--- /dev/null
+++ b/src/debug/inc/amd64/primitives.h
@@ -0,0 +1,257 @@
+// 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.
+//*****************************************************************************
+// File: primitives.h
+//
+
+//
+// Platform-specific debugger primitives
+//
+//*****************************************************************************
+
+#ifndef PRIMITIVES_H_
+#define PRIMITIVES_H_
+
+#ifndef CORDB_ADDRESS_TYPE
+typedef const BYTE CORDB_ADDRESS_TYPE;
+typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE;
+#endif
+//This is an abstraction to keep x86/ia64 patch data separate
+#ifndef PRD_TYPE
+#define PRD_TYPE DWORD_PTR
+#endif
+
+typedef M128A FPRegister64;
+
+// From section 1.1 of AMD64 Programmers Manual Vol 3.
+#define MAX_INSTRUCTION_LENGTH 15
+
+// Given a return address retrieved during stackwalk,
+// this is the offset by which it should be decremented to lend somewhere in a call instruction.
+#define STACKWALK_CONTROLPC_ADJUST_OFFSET 1
+
+#define CORDbg_BREAK_INSTRUCTION_SIZE 1
+#define CORDbg_BREAK_INSTRUCTION (BYTE)0xCC
+
+inline CORDB_ADDRESS GetPatchEndAddr(CORDB_ADDRESS patchAddr)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return patchAddr + CORDbg_BREAK_INSTRUCTION_SIZE;
+}
+
+
+#define InitializePRDToBreakInst(_pPRD) *(_pPRD) = CORDbg_BREAK_INSTRUCTION
+#define PRDIsBreakInst(_pPRD) (*(_pPRD) == CORDbg_BREAK_INSTRUCTION)
+
+#define CORDbgGetInstructionEx(_buffer, _requestedAddr, _patchAddr, _dummy1, _dummy2) \
+ CORDbgGetInstruction((CORDB_ADDRESS_TYPE *)((_buffer) + ((_patchAddr) - (_requestedAddr))));
+
+#define CORDbgSetInstructionEx(_buffer, _requestedAddr, _patchAddr, _opcode, _dummy2) \
+ CORDbgSetInstruction((CORDB_ADDRESS_TYPE *)((_buffer) + ((_patchAddr) - (_requestedAddr))), (_opcode));
+
+#define CORDbgInsertBreakpointEx(_buffer, _requestedAddr, _patchAddr, _dummy1, _dummy2) \
+ CORDbgInsertBreakpoint((CORDB_ADDRESS_TYPE *)((_buffer) + ((_patchAddr) - (_requestedAddr))));
+
+
+SELECTANY const CorDebugRegister g_JITToCorDbgReg[] =
+{
+ REGISTER_AMD64_RAX,
+ REGISTER_AMD64_RCX,
+ REGISTER_AMD64_RDX,
+ REGISTER_AMD64_RBX,
+ REGISTER_AMD64_RSP,
+ REGISTER_AMD64_RBP,
+ REGISTER_AMD64_RSI,
+ REGISTER_AMD64_RDI,
+ REGISTER_AMD64_R8,
+ REGISTER_AMD64_R9,
+ REGISTER_AMD64_R10,
+ REGISTER_AMD64_R11,
+ REGISTER_AMD64_R12,
+ REGISTER_AMD64_R13,
+ REGISTER_AMD64_R14,
+ REGISTER_AMD64_R15
+};
+
+//
+// Mapping from ICorDebugInfo register numbers to CorDebugRegister
+// numbers. Note: this must match the order in corinfo.h.
+//
+inline CorDebugRegister ConvertRegNumToCorDebugRegister(ICorDebugInfo::RegNum reg)
+{
+ _ASSERTE(reg >= 0);
+ _ASSERTE(static_cast<size_t>(reg) < _countof(g_JITToCorDbgReg));
+ return g_JITToCorDbgReg[reg];
+}
+
+
+//
+// inline function to access/modify the CONTEXT
+//
+inline LPVOID CORDbgGetIP(DT_CONTEXT* context)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+
+ PRECONDITION(CheckPointer(context));
+ }
+ CONTRACTL_END;
+
+ return (LPVOID) context->Rip;
+}
+
+inline void CORDbgSetIP(DT_CONTEXT* context, LPVOID rip)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+
+ PRECONDITION(CheckPointer(context));
+ }
+ CONTRACTL_END;
+
+ context->Rip = (DWORD64) rip;
+}
+
+inline LPVOID CORDbgGetSP(const DT_CONTEXT * context)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+
+ PRECONDITION(CheckPointer(context));
+ }
+ CONTRACTL_END;
+
+ return (LPVOID)context->Rsp;
+}
+inline void CORDbgSetSP(DT_CONTEXT *context, LPVOID rsp)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+
+ PRECONDITION(CheckPointer(context));
+ }
+ CONTRACTL_END;
+
+ context->Rsp = (UINT_PTR)rsp;
+}
+
+// AMD64 has no frame pointer stored in RBP
+#define CORDbgSetFP(context, rbp)
+#define CORDbgGetFP(context) 0
+
+// compare the RIP, RSP, and RBP
+inline BOOL CompareControlRegisters(const DT_CONTEXT * pCtx1, const DT_CONTEXT * pCtx2)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ if ((pCtx1->Rip == pCtx2->Rip) &&
+ (pCtx1->Rsp == pCtx2->Rsp) &&
+ (pCtx1->Rbp == pCtx2->Rbp))
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* ========================================================================= */
+//
+// Routines used by debugger support functions such as codepatch.cpp or
+// exception handling code.
+//
+// GetInstruction, InsertBreakpoint, and SetInstruction all operate on
+// a _single_ byte of memory. This is really important. If you only
+// save one byte from the instruction stream before placing a breakpoint,
+// you need to make sure to only replace one byte later on.
+//
+
+
+inline PRD_TYPE CORDbgGetInstruction(UNALIGNED CORDB_ADDRESS_TYPE* address)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return *address; // retrieving only one byte is important
+}
+
+inline void CORDbgInsertBreakpoint(UNALIGNED CORDB_ADDRESS_TYPE *address)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ *((unsigned char*)address) = 0xCC; // int 3 (single byte patch)
+ FlushInstructionCache(GetCurrentProcess(), address, 1);
+
+}
+
+inline void CORDbgSetInstruction(UNALIGNED CORDB_ADDRESS_TYPE* address,
+ PRD_TYPE instruction)
+{
+ // In a DAC build, this function assumes the input is an host address.
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ *((unsigned char*)address) =
+ (unsigned char) instruction; // setting one byte is important
+ FlushInstructionCache(GetCurrentProcess(), address, 1);
+
+}
+
+
+inline void CORDbgAdjustPCForBreakInstruction(DT_CONTEXT* pContext)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ pContext->Rip -= 1;
+}
+
+inline bool AddressIsBreakpoint(CORDB_ADDRESS_TYPE *address)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return *address == CORDbg_BREAK_INSTRUCTION;
+}
+
+inline BOOL IsRunningOnWin95() {
+ return false;
+}
+
+inline void SetSSFlag(DT_CONTEXT *pContext)
+{
+ _ASSERTE(pContext != NULL);
+ pContext->EFlags |= 0x100;
+}
+
+inline void UnsetSSFlag(DT_CONTEXT *pContext)
+{
+ _ASSERTE(pContext != NULL);
+ pContext->EFlags &= ~0x100;
+}
+
+inline bool IsSSFlagEnabled(DT_CONTEXT * context)
+{
+ _ASSERTE(context != NULL);
+ return (context->EFlags & 0x100) != 0;
+}
+
+
+inline bool PRDIsEqual(PRD_TYPE p1, PRD_TYPE p2){
+ return p1 == p2;
+}
+inline void InitializePRD(PRD_TYPE *p1) {
+ *p1 = 0;
+}
+
+inline bool PRDIsEmpty(PRD_TYPE p1) {
+ return p1 == 0;
+}
+
+#endif // PRIMITIVES_H_
diff --git a/src/debug/inc/arm/.gitmirror b/src/debug/inc/arm/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/debug/inc/arm/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/debug/inc/arm/primitives.h b/src/debug/inc/arm/primitives.h
new file mode 100644
index 0000000000..0bac542667
--- /dev/null
+++ b/src/debug/inc/arm/primitives.h
@@ -0,0 +1,179 @@
+// 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.
+//*****************************************************************************
+// File: primitives.h
+//
+
+//
+// Platform-specific debugger primitives
+//
+//*****************************************************************************
+
+#ifndef PRIMITIVES_H_
+#define PRIMITIVES_H_
+
+#ifndef THUMB_CODE
+#define THUMB_CODE 1
+#endif
+
+typedef const BYTE CORDB_ADDRESS_TYPE;
+typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE;
+
+//This is an abstraction to keep x86/ia64 patch data separate
+#define PRD_TYPE USHORT
+
+#define MAX_INSTRUCTION_LENGTH 4
+
+// Given a return address retrieved during stackwalk,
+// this is the offset by which it should be decremented to lend somewhere in a call instruction.
+#define STACKWALK_CONTROLPC_ADJUST_OFFSET 2
+
+#define CORDbg_BREAK_INSTRUCTION_SIZE 2
+#define CORDbg_BREAK_INSTRUCTION (USHORT)0xdefe
+
+inline CORDB_ADDRESS GetPatchEndAddr(CORDB_ADDRESS patchAddr)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return patchAddr + CORDbg_BREAK_INSTRUCTION_SIZE;
+}
+
+
+#define InitializePRDToBreakInst(_pPRD) *(_pPRD) = CORDbg_BREAK_INSTRUCTION
+#define PRDIsBreakInst(_pPRD) (*(_pPRD) == CORDbg_BREAK_INSTRUCTION)
+
+template <class T>
+inline T _ClearThumbBit(T addr)
+{
+ return (T)(((DWORD)addr) & ~THUMB_CODE);
+}
+
+
+#define CORDbgGetInstructionEx(_buffer, _requestedAddr, _patchAddr, _dummy1, _dummy2) \
+ CORDbgGetInstructionExImpl((CORDB_ADDRESS_TYPE *)((_buffer) + (_ClearThumbBit(_patchAddr) - (_requestedAddr))));
+
+#define CORDbgSetInstructionEx(_buffer, _requestedAddr, _patchAddr, _opcode, _dummy2) \
+ CORDbgSetInstructionExImpl((CORDB_ADDRESS_TYPE *)((_buffer) + (_ClearThumbBit(_patchAddr) - (_requestedAddr))), (_opcode));
+
+#define CORDbgInsertBreakpointEx(_buffer, _requestedAddr, _patchAddr, _dummy1, _dummy2) \
+ CORDbgInsertBreakpointExImpl((CORDB_ADDRESS_TYPE *)((_buffer) + (_ClearThumbBit(_patchAddr) - (_requestedAddr))));
+
+
+SELECTANY const CorDebugRegister g_JITToCorDbgReg[] =
+{
+ REGISTER_ARM_R0,
+ REGISTER_ARM_R1,
+ REGISTER_ARM_R2,
+ REGISTER_ARM_R3,
+ REGISTER_ARM_R4,
+ REGISTER_ARM_R5,
+ REGISTER_ARM_R6,
+ REGISTER_ARM_R7,
+ REGISTER_ARM_R8,
+ REGISTER_ARM_R9,
+ REGISTER_ARM_R10,
+ REGISTER_ARM_R11,
+ REGISTER_ARM_R12,
+ REGISTER_ARM_SP,
+ REGISTER_ARM_LR,
+ REGISTER_ARM_PC
+};
+
+//
+// inline function to access/modify the CONTEXT
+//
+inline void CORDbgSetIP(DT_CONTEXT *context, LPVOID eip) {
+ LIMITED_METHOD_CONTRACT;
+
+ context->Pc = (UINT32)(size_t)eip;
+}
+
+inline LPVOID CORDbgGetSP(const DT_CONTEXT * context) {
+ LIMITED_METHOD_CONTRACT;
+
+ return (LPVOID)(size_t)(context->Sp);
+}
+
+inline void CORDbgSetSP(DT_CONTEXT *context, LPVOID esp) {
+ LIMITED_METHOD_CONTRACT;
+
+ context->Sp = (UINT32)(size_t)esp;
+}
+
+inline void CORDbgSetFP(DT_CONTEXT *context, LPVOID ebp) {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(FALSE); // @ARMTODO
+}
+inline LPVOID CORDbgGetFP(DT_CONTEXT* context)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(FALSE); // @ARMTODO
+ return (LPVOID)(UINT_PTR)0;
+}
+
+// compare the EIP, ESP, and EBP
+inline BOOL CompareControlRegisters(const DT_CONTEXT * pCtx1, const DT_CONTEXT * pCtx2)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ // @ARMTODO: Sort out frame registers
+
+ if ((pCtx1->Pc == pCtx2->Pc) &&
+ (pCtx1->Sp == pCtx2->Sp))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* ========================================================================= */
+//
+// Routines used by debugger support functions such as codepatch.cpp or
+// exception handling code.
+//
+// GetInstruction, InsertBreakpoint, and SetInstruction all operate on
+// a _single_ PRD_TYPE unit of memory. This is really important. If you only
+// save one PRD_TYPE from the instruction stream before placing a breakpoint,
+// you need to make sure to only replace one PRD_TYPE later on.
+//
+inline PRD_TYPE CORDbgGetInstruction(UNALIGNED CORDB_ADDRESS_TYPE* address)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ ULONG ptraddr = dac_cast<ULONG>(address);
+ _ASSERTE(ptraddr & THUMB_CODE);
+ ptraddr &= ~THUMB_CODE;
+ return *(PRD_TYPE *)ptraddr;
+}
+
+inline void CORDbgSetInstruction(CORDB_ADDRESS_TYPE* address,
+ PRD_TYPE instruction)
+{
+ // In a DAC build, this function assumes the input is an host address.
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ ULONG ptraddr = dac_cast<ULONG>(address);
+ _ASSERTE(ptraddr & THUMB_CODE);
+ ptraddr &= ~THUMB_CODE;
+
+ *(PRD_TYPE *)ptraddr = instruction;
+ FlushInstructionCache(GetCurrentProcess(),
+ address,
+ sizeof(PRD_TYPE));
+}
+
+class Thread;
+// Enable single stepping.
+void SetSSFlag(DT_CONTEXT *pCtx, Thread *pThread);
+
+// Disable single stepping
+void UnsetSSFlag(DT_CONTEXT *pCtx, Thread *pThread);
+
+// Check if single stepping is enabled.
+bool IsSSFlagEnabled(DT_CONTEXT *pCtx, Thread *pThread);
+
+#include "arm_primitives.h"
+#endif // PRIMITIVES_H_
diff --git a/src/debug/inc/arm64/.gitmirror b/src/debug/inc/arm64/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/debug/inc/arm64/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/debug/inc/arm64/primitives.h b/src/debug/inc/arm64/primitives.h
new file mode 100644
index 0000000000..e9e04378f7
--- /dev/null
+++ b/src/debug/inc/arm64/primitives.h
@@ -0,0 +1,186 @@
+// 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.
+//*****************************************************************************
+// File: primitives.h
+//
+
+//
+// Platform-specific debugger primitives
+//
+//*****************************************************************************
+
+#ifndef PRIMITIVES_H_
+#define PRIMITIVES_H_
+
+typedef NEON128 FPRegister64;
+typedef const BYTE CORDB_ADDRESS_TYPE;
+typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE;
+
+#define MAX_INSTRUCTION_LENGTH 4
+
+// Given a return address retrieved during stackwalk,
+// this is the offset by which it should be decremented to land at the call instruction.
+#define STACKWALK_CONTROLPC_ADJUST_OFFSET 4
+
+#define PRD_TYPE LONG
+#define CORDbg_BREAK_INSTRUCTION_SIZE 4
+#define CORDbg_BREAK_INSTRUCTION (LONG)0xD43E0000
+
+#define NZCV_N 0x80000000
+#define NZCV_Z 0x40000000
+#define NZCV_C 0x20000000
+#define NZCV_V 0x10000000
+
+#define NZCV_N_BIT 0x1f
+#define NZCV_Z_BIT 0x1e
+#define NZCV_C_BIT 0x1d
+#define NZCV_V_BIT 0x1c
+
+inline CORDB_ADDRESS GetPatchEndAddr(CORDB_ADDRESS patchAddr)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return patchAddr + CORDbg_BREAK_INSTRUCTION_SIZE;
+}
+
+#define InitializePRDToBreakInst(_pPRD) *(_pPRD) = CORDbg_BREAK_INSTRUCTION
+#define PRDIsBreakInst(_pPRD) (*(_pPRD) == CORDbg_BREAK_INSTRUCTION)
+
+
+#define CORDbgGetInstructionEx(_buffer, _requestedAddr, _patchAddr, _dummy1, _dummy2) \
+ CORDbgGetInstructionExImpl((CORDB_ADDRESS_TYPE *)((_buffer) + (_patchAddr) - (_requestedAddr)));
+
+#define CORDbgSetInstructionEx(_buffer, _requestedAddr, _patchAddr, _opcode, _dummy2) \
+ CORDbgSetInstructionExImpl((CORDB_ADDRESS_TYPE *)((_buffer) + (_patchAddr) - (_requestedAddr)), (_opcode));
+
+#define CORDbgInsertBreakpointEx(_buffer, _requestedAddr, _patchAddr, _dummy1, _dummy2) \
+ CORDbgInsertBreakpointExImpl((CORDB_ADDRESS_TYPE *)((_buffer) + (_patchAddr) - (_requestedAddr)));
+
+
+SELECTANY const CorDebugRegister g_JITToCorDbgReg[] =
+{
+ REGISTER_ARM64_X0,
+ REGISTER_ARM64_X1,
+ REGISTER_ARM64_X2,
+ REGISTER_ARM64_X3,
+ REGISTER_ARM64_X4,
+ REGISTER_ARM64_X5,
+ REGISTER_ARM64_X6,
+ REGISTER_ARM64_X7,
+ REGISTER_ARM64_X8,
+ REGISTER_ARM64_X9,
+ REGISTER_ARM64_X10,
+ REGISTER_ARM64_X11,
+ REGISTER_ARM64_X12,
+ REGISTER_ARM64_X13,
+ REGISTER_ARM64_X14,
+ REGISTER_ARM64_X15,
+ REGISTER_ARM64_X16,
+ REGISTER_ARM64_X17,
+ REGISTER_ARM64_X18,
+ REGISTER_ARM64_X19,
+ REGISTER_ARM64_X20,
+ REGISTER_ARM64_X21,
+ REGISTER_ARM64_X22,
+ REGISTER_ARM64_X23,
+ REGISTER_ARM64_X24,
+ REGISTER_ARM64_X25,
+ REGISTER_ARM64_X26,
+ REGISTER_ARM64_X27,
+ REGISTER_ARM64_X28,
+ REGISTER_ARM64_FP,
+ REGISTER_ARM64_LR,
+ REGISTER_ARM64_SP,
+ REGISTER_ARM64_PC
+};
+
+inline void CORDbgSetIP(DT_CONTEXT *context, LPVOID eip) {
+ LIMITED_METHOD_CONTRACT;
+
+ context->Pc = (DWORD64)eip;
+}
+
+inline LPVOID CORDbgGetSP(const DT_CONTEXT * context) {
+ LIMITED_METHOD_CONTRACT;
+
+ return (LPVOID)(size_t)(context->Sp);
+}
+
+inline void CORDbgSetSP(DT_CONTEXT *context, LPVOID esp) {
+ LIMITED_METHOD_CONTRACT;
+
+ context->Sp = (DWORD64)esp;
+}
+
+inline LPVOID CORDbgGetFP(const DT_CONTEXT * context) {
+ LIMITED_METHOD_CONTRACT;
+
+ return (LPVOID)(size_t)(context->Fp);
+}
+
+inline void CORDbgSetFP(DT_CONTEXT *context, LPVOID fp) {
+ LIMITED_METHOD_CONTRACT;
+
+ context->Fp = (DWORD64)fp;
+}
+
+
+inline BOOL CompareControlRegisters(const DT_CONTEXT * pCtx1, const DT_CONTEXT * pCtx2)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ // @ARMTODO: Sort out frame registers
+
+ if ((pCtx1->Pc == pCtx2->Pc) &&
+ (pCtx1->Sp == pCtx2->Sp) &&
+ (pCtx1->Fp == pCtx2->Fp))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+inline void CORDbgSetInstruction(CORDB_ADDRESS_TYPE* address,
+ PRD_TYPE instruction)
+{
+ // In a DAC build, this function assumes the input is an host address.
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ ULONGLONG ptraddr = dac_cast<ULONGLONG>(address);
+ *(PRD_TYPE *)ptraddr = instruction;
+ FlushInstructionCache(GetCurrentProcess(),
+ address,
+ sizeof(PRD_TYPE));
+}
+
+inline PRD_TYPE CORDbgGetInstruction(UNALIGNED CORDB_ADDRESS_TYPE* address)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ ULONGLONG ptraddr = dac_cast<ULONGLONG>(address);
+ return *(PRD_TYPE *)ptraddr;
+}
+
+
+inline void SetSSFlag(DT_CONTEXT *pContext)
+{
+ _ASSERTE(pContext != NULL);
+ pContext->Cpsr |= 0x00200000;
+}
+
+inline void UnsetSSFlag(DT_CONTEXT *pContext)
+{
+ _ASSERTE(pContext != NULL);
+ pContext->Cpsr &= ~0x00200000;
+}
+
+inline bool IsSSFlagEnabled(DT_CONTEXT * pContext)
+{
+ _ASSERTE(pContext != NULL);
+ return (pContext->Cpsr & 0x00200000) != 0;
+}
+
+
+#include "arm_primitives.h"
+#endif // PRIMITIVES_H_
diff --git a/src/debug/inc/arm_primitives.h b/src/debug/inc/arm_primitives.h
new file mode 100644
index 0000000000..7c536353e5
--- /dev/null
+++ b/src/debug/inc/arm_primitives.h
@@ -0,0 +1,113 @@
+// 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.
+//*****************************************************************************
+// File: arm_primitives.h
+//
+
+//
+// ARM/ARM64-specific debugger primitives
+//
+//*****************************************************************************
+
+#ifndef ARM_PRIMITIVES_H_
+#define ARM_PRIMITIVES_H_
+
+//
+// Mapping from ICorDebugInfo register numbers to CorDebugRegister
+// numbers. Note: this must match the order in corinfo.h.
+//
+inline CorDebugRegister ConvertRegNumToCorDebugRegister(ICorDebugInfo::RegNum reg)
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(reg >= 0);
+ _ASSERTE(static_cast<size_t>(reg) < _countof(g_JITToCorDbgReg));
+ return g_JITToCorDbgReg[reg];
+}
+
+inline LPVOID CORDbgGetIP(DT_CONTEXT *context)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (LPVOID)(size_t)(context->Pc);
+}
+
+inline void CORDbgSetInstructionExImpl(CORDB_ADDRESS_TYPE* address,
+ PRD_TYPE instruction)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ *(PRD_TYPE *)address = instruction;
+ FlushInstructionCache(GetCurrentProcess(),
+ address,
+ sizeof(PRD_TYPE));
+}
+
+inline PRD_TYPE CORDbgGetInstructionExImpl(UNALIGNED CORDB_ADDRESS_TYPE* address)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return *(PRD_TYPE *)address;
+}
+
+inline void CORDbgInsertBreakpoint(UNALIGNED CORDB_ADDRESS_TYPE *address)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ CORDbgSetInstruction(address, CORDbg_BREAK_INSTRUCTION);
+}
+
+inline void CORDbgInsertBreakpointExImpl(UNALIGNED CORDB_ADDRESS_TYPE *address)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ CORDbgSetInstruction(address, CORDbg_BREAK_INSTRUCTION);
+}
+
+// After a breakpoint exception, the CPU points to _after_ the break instruction.
+// Adjust the IP so that it points at the break instruction. This lets us patch that
+// opcode and re-excute what was underneath the bp.
+inline void CORDbgAdjustPCForBreakInstruction(DT_CONTEXT* pContext)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // @ARMTODO: ARM appears to leave the PC at the start of the breakpoint (at least according to Windbg,
+ // which may be adjusting the view).
+ return;
+}
+
+inline bool AddressIsBreakpoint(CORDB_ADDRESS_TYPE* address)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return CORDbgGetInstruction(address) == CORDbg_BREAK_INSTRUCTION;
+}
+
+class Thread;
+// Enable single stepping.
+void SetSSFlag(DT_CONTEXT *pCtx, Thread *pThread);
+
+// Disable single stepping
+void UnsetSSFlag(DT_CONTEXT *pCtx, Thread *pThread);
+
+// Check if single stepping is enabled.
+bool IsSSFlagEnabled(DT_CONTEXT *pCtx, Thread *pThread);
+
+inline bool PRDIsEqual(PRD_TYPE p1, PRD_TYPE p2)
+{
+ return p1 == p2;
+}
+
+inline void InitializePRD(PRD_TYPE *p1)
+{
+ *p1 = 0;
+}
+
+inline bool PRDIsEmpty(PRD_TYPE p1)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return p1 == 0;
+}
+
+#endif // ARM_PRIMITIVES_H_
diff --git a/src/debug/inc/common.h b/src/debug/inc/common.h
new file mode 100644
index 0000000000..77fe27a4b3
--- /dev/null
+++ b/src/debug/inc/common.h
@@ -0,0 +1,323 @@
+// 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.
+
+#ifndef DEBUGGER_COMMON_H
+#define DEBUGGER_COMMON_H
+
+//
+// Conversions between pointers and CORDB_ADDRESS
+// These are 3gb safe - we use zero-extension for CORDB_ADDRESS.
+// Note that this is a different semantics from CLRDATA_ADDRESS which is sign-extended.
+//
+// @dbgtodo : This confuses the host and target address spaces. Ideally we'd have
+// conversions between PTR types (eg. DPTR) and CORDB_ADDRESS, and not need conversions
+// from host pointer types to CORDB_ADDRESS.
+//
+#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
+inline CORDB_ADDRESS PTR_TO_CORDB_ADDRESS(const void* ptr)
+{
+ SUPPORTS_DAC;
+ // Cast a void* to a ULONG is not 64-bit safe and triggers compiler warning C3411.
+ // But this is x86 only, so we know it's ok. Use PtrToUlong to do the conversion
+ // without invoking the error.
+ return (CORDB_ADDRESS)(PtrToUlong(ptr));
+}
+inline CORDB_ADDRESS PTR_TO_CORDB_ADDRESS(UINT_PTR ptr)
+{
+ SUPPORTS_DAC;
+ // PtrToUlong
+ return (CORDB_ADDRESS)(ULONG)(ptr);
+}
+#else
+#define PTR_TO_CORDB_ADDRESS(_ptr) (CORDB_ADDRESS)(ULONG_PTR)(_ptr)
+#endif //_TARGET_X86_ || _TARGET_ARM_
+
+#define CORDB_ADDRESS_TO_PTR(_cordb_addr) ((LPVOID)(SIZE_T)(_cordb_addr))
+
+
+// Determine if an exception record is for a CLR debug event, and get the payload.
+CORDB_ADDRESS IsEventDebuggerNotification(const EXCEPTION_RECORD * pRecord, CORDB_ADDRESS pClrBaseAddress);
+#if defined(FEATURE_DBGIPC_TRANSPORT_DI) || defined(FEATURE_DBGIPC_TRANSPORT_VM)
+struct DebuggerIPCEvent;
+void InitEventForDebuggerNotification(DEBUG_EVENT * pDebugEvent,
+ CORDB_ADDRESS pClrBaseAddress,
+ DebuggerIPCEvent * pIPCEvent);
+#endif // (FEATURE_DBGIPC_TRANSPORT_DI || FEATURE_DBGIPC_TRANSPORT_VM)
+
+
+void GetPidDecoratedName(__out_z __out_ecount(cBufSizeInChars) WCHAR * pBuf,
+ int cBufSizeInChars,
+ const WCHAR * pPrefix,
+ DWORD pid);
+
+
+//
+// This macro is used in CORDbgCopyThreadContext().
+//
+// CORDbgCopyThreadContext() does an intelligent copy
+// from pSrc to pDst, respecting the ContextFlags of both contexts.
+//
+#define CopyContextChunk(_t, _f, _end, _flag) \
+{ \
+ LOG((LF_CORDB, LL_INFO1000000, \
+ "CP::CTC: copying " #_flag ":" FMT_ADDR "<---" FMT_ADDR "(%d)\n", \
+ DBG_ADDR(_t), DBG_ADDR(_f), ((UINT_PTR)(_end) - (UINT_PTR)_t))); \
+ memcpy((_t), (_f), ((UINT_PTR)(_end) - (UINT_PTR)(_t))); \
+}
+
+//
+// CORDbgCopyThreadContext() does an intelligent copy from pSrc to pDst,
+// respecting the ContextFlags of both contexts.
+//
+struct DebuggerREGDISPLAY;
+
+extern void CORDbgCopyThreadContext(DT_CONTEXT* pDst, const DT_CONTEXT* pSrc);
+extern void CORDbgSetDebuggerREGDISPLAYFromContext(DebuggerREGDISPLAY *pDRD,
+ DT_CONTEXT* pContext);
+
+//---------------------------------------------------------------------------------------
+//
+// Return the size of the CONTEXT required for the specified context flags.
+//
+// Arguments:
+// flags - this is the equivalent of the ContextFlags field of a CONTEXT
+//
+// Return Value:
+// size of the CONTEXT required
+//
+// Notes:
+// On WIN64 platforms this function will always return sizeof(CONTEXT).
+//
+
+inline
+ULONG32 ContextSizeForFlags(ULONG32 flags)
+{
+#if defined(CONTEXT_EXTENDED_REGISTERS) && defined(_TARGET_X86_)
+ // Older platforms didn't have extended registers in
+ // the context definition so only enforce that size
+ // if the extended register flag is set.
+ if ((flags & CONTEXT_EXTENDED_REGISTERS) != CONTEXT_EXTENDED_REGISTERS)
+ {
+ return offsetof(T_CONTEXT, ExtendedRegisters);
+ }
+ else
+#endif // _TARGET_X86_
+ {
+ return sizeof(T_CONTEXT);
+ }
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Given the size of a buffer and the context flags, check whether the buffer is sufficient large
+// to hold the CONTEXT.
+//
+// Arguments:
+// size - size of a buffer
+// flags - this is the equivalent of the ContextFlags field of a CONTEXT
+//
+// Return Value:
+// TRUE if the buffer is large enough to hold the CONTEXT
+//
+
+inline
+BOOL CheckContextSizeForFlags(ULONG32 size, ULONG32 flags)
+{
+ return (size >= ContextSizeForFlags(flags));
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Given the size of a buffer and the BYTE array representation of a CONTEXT,
+// check whether the buffer is sufficient large to hold the CONTEXT.
+//
+// Arguments:
+// size - size of a buffer
+// flags - this is the equivalent of the ContextFlags field of a CONTEXT
+//
+// Return Value:
+// TRUE if the buffer is large enough to hold the CONTEXT
+//
+
+inline
+BOOL CheckContextSizeForBuffer(ULONG32 size, const BYTE * pbBuffer)
+{
+ return ( ( size >= (offsetof(T_CONTEXT, ContextFlags) + sizeof(ULONG32)) ) &&
+ CheckContextSizeForFlags(size, (reinterpret_cast<const T_CONTEXT *>(pbBuffer))->ContextFlags) );
+}
+
+/* ------------------------------------------------------------------------- *
+ * Constant declarations
+ * ------------------------------------------------------------------------- */
+
+enum
+{
+ NULL_THREAD_ID = -1,
+ NULL_PROCESS_ID = -1
+};
+
+/* ------------------------------------------------------------------------- *
+ * Macros
+ * ------------------------------------------------------------------------- */
+
+//
+// CANNOT USE IsBad*Ptr() methods here. They are *banned* APIs because of various
+// reasons (see http://winweb/wincet/bannedapis.htm).
+//
+
+#define VALIDATE_POINTER_TO_OBJECT(ptr, type) \
+if ((ptr) == NULL) \
+{ \
+ return E_INVALIDARG; \
+}
+
+#define VALIDATE_POINTER_TO_OBJECT_OR_NULL(ptr, type)
+
+//
+// CANNOT USE IsBad*Ptr() methods here. They are *banned* APIs because of various
+// reasons (see http://winweb/wincet/bannedapis.htm).
+//
+#define VALIDATE_POINTER_TO_OBJECT_ARRAY(ptr, type, cElt, fRead, fWrite) \
+if ((ptr) == NULL) \
+{ \
+ return E_INVALIDARG; \
+}
+
+#define VALIDATE_POINTER_TO_OBJECT_ARRAY_OR_NULL(ptr, type,cElt,fRead,fWrite)
+
+/* ------------------------------------------------------------------------- *
+ * Function Prototypes
+ * ------------------------------------------------------------------------- */
+
+
+
+// Linear search through an array of NativeVarInfos, to find
+// the variable of index dwIndex, valid at the given ip.
+//
+// returns CORDBG_E_IL_VAR_NOT_AVAILABLE if the variable isn't
+// valid at the given ip.
+//
+// This should be inlined
+HRESULT FindNativeInfoInILVariableArray(DWORD dwIndex,
+ SIZE_T ip,
+ ICorDebugInfo::NativeVarInfo **ppNativeInfo,
+ unsigned int nativeInfoCount,
+ ICorDebugInfo::NativeVarInfo *nativeInfo);
+
+
+#define VALIDATE_HEAP
+//HeapValidate(GetProcessHeap(), 0, NULL);
+
+// struct DebuggerILToNativeMap: Holds the IL to Native offset map
+// Great pains are taken to ensure that this each entry corresponds to the
+// first IL instruction in a source line. It isn't actually a mapping
+// of _every_ IL instruction in a method, just those for source lines.
+// SIZE_T ilOffset: IL offset of a source line.
+// SIZE_T nativeStartOffset: Offset within the method where the native
+// instructions corresponding to the IL offset begin.
+// SIZE_T nativeEndOffset: Offset within the method where the native
+// instructions corresponding to the IL offset end.
+//
+// Note: any changes to this struct need to be reflected in
+// COR_DEBUG_IL_TO_NATIVE_MAP in CorDebug.idl. These structs must
+// match exactly.
+//
+struct DebuggerILToNativeMap
+{
+ ULONG ilOffset;
+ ULONG nativeStartOffset;
+ ULONG nativeEndOffset;
+ ICorDebugInfo::SourceTypes source;
+};
+
+void ExportILToNativeMap(ULONG32 cMap,
+ COR_DEBUG_IL_TO_NATIVE_MAP mapExt[],
+ struct DebuggerILToNativeMap mapInt[],
+ SIZE_T sizeOfCode);
+
+#include <primitives.h>
+
+// ----------------------------------------------------------------------------
+// IsPatchInRequestedRange
+//
+// Description:
+// This function checks if a patch falls (fully or partially) in the requested range of memory.
+//
+// Arguments:
+// * requestedAddr - the address of the memory range
+// * requestedSize - the size of the memory range
+// * patchAddr - the address of the patch
+// * pPRD - the opcode of the patch
+//
+// Return Value:
+// Return TRUE if the patch is fully or partially in the requested memory range.
+//
+// Notes:
+// Currently this function is called both from the RS (via code:CordbProcess.ReadMemory and
+// code:CordbProcess.WriteMemory) and from DAC. When we DACize the two functions mentioned above,
+// this function should be called from DAC only, and we should use a MemoryRange here.
+//
+
+inline bool IsPatchInRequestedRange(CORDB_ADDRESS requestedAddr,
+ SIZE_T requestedSize,
+ CORDB_ADDRESS patchAddr)
+{
+ SUPPORTS_DAC;
+
+ if (requestedAddr == 0)
+ return false;
+
+ // Note that patchEnd points to the byte immediately AFTER the patch, so patchEnd is NOT
+ // part of the patch.
+ CORDB_ADDRESS patchEnd = GetPatchEndAddr(patchAddr);
+
+ // We have three cases:
+ // 1) the entire patch is in the requested range
+ // 2) the beginning of the requested range is covered by the patch
+ // 3) the end of the requested range is covered by the patch
+ //
+ // Note that on x86, since the break instruction only takes up one byte, the following condition
+ // degenerates to case 1 only.
+ return (((requestedAddr <= patchAddr) && (patchEnd <= (requestedAddr + requestedSize))) ||
+ ((patchAddr <= requestedAddr) && (requestedAddr < patchEnd)) ||
+ ((patchAddr <= (requestedAddr + requestedSize - 1)) && ((requestedAddr + requestedSize - 1) < patchEnd)));
+}
+
+inline CORDB_ADDRESS ALIGN_ADDRESS( CORDB_ADDRESS val, CORDB_ADDRESS alignment )
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ // alignment must be a power of 2 for this implementation to work (need modulo otherwise)
+ _ASSERTE( 0 == (alignment & (alignment - 1)) );
+ CORDB_ADDRESS result = (val + (alignment - 1)) & ~(alignment - 1);
+ _ASSERTE( result >= val ); // check for overflow
+ return result;
+}
+
+//
+// Whenever a structure is marshalled between different platforms, we need to ensure the
+// layout is the same in both cases. We tell GCC to use the MSVC-style packing with
+// the following attribute. The main thing this appears to control is whether
+// 8-byte values are aligned at 4-bytes (GCC default) or 8-bytes (MSVC default).
+// This attribute affects only the immediate struct it is applied to, you must also apply
+// it to any nested structs if you want their layout affected as well. You also must
+// apply this to unions embedded in other structures, since it can influence the starting
+// alignment.
+//
+// Note that there doesn't appear to be any disadvantage to applying this a little
+// more agressively than necessary, so we generally use it on all classes / structures
+// defined in a file that defines marshalled data types (eg. DacDbiStructures.h)
+// The -mms-bitfields compiler option also does this for the whole file, but we don't
+// want to go changing the layout of, for example, structures defined in OS header files
+// so we explicitly opt-in with this attribute.
+//
+#ifdef __GNUC__
+#define MSLAYOUT __attribute__((__ms_struct__))
+#else
+#define MSLAYOUT
+#endif
+
+#include "dumpcommon.h"
+
+#endif //DEBUGGER_COMMON_H
diff --git a/src/debug/inc/coreclrremotedebugginginterfaces.h b/src/debug/inc/coreclrremotedebugginginterfaces.h
new file mode 100644
index 0000000000..ac7ddb68ab
--- /dev/null
+++ b/src/debug/inc/coreclrremotedebugginginterfaces.h
@@ -0,0 +1,20 @@
+// 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.
+
+
+//
+// Defines interfaces shared between mscordbi and the Visual Studio debugger port supplier plugin that we
+// provide for debugging remote CoreCLR instances on the Mac.
+//
+
+#ifndef __PORT_SUPPLIER_INTERFACES_INCLUDED
+#define __PORT_SUPPLIER_INTERFACES_INCLUDED
+
+
+
+
+class ICoreClrDebugTarget;
+
+
+#endif // __PORT_SUPPLIER_INTERFACES_INCLUDED
diff --git a/src/debug/inc/dacdbiinterface.h b/src/debug/inc/dacdbiinterface.h
new file mode 100644
index 0000000000..fe58724fc5
--- /dev/null
+++ b/src/debug/inc/dacdbiinterface.h
@@ -0,0 +1,2726 @@
+// 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.
+//*****************************************************************************
+// DacDbiInterface.h
+//
+
+//
+// Define the interface between the DAC and DBI.
+//*****************************************************************************
+
+#ifndef _DACDBI_INTERFACE_H_
+#define _DACDBI_INTERFACE_H_
+
+#include <metahost.h>
+
+// The DAC/DBI interface can use structures and LSPTR declarations from the
+// existing V2 interfaces
+#include "dbgipcevents.h"
+
+//-----------------------------------------------------------------------------
+// Deallocation function for memory allocated with the global IAllocator object.
+//
+// Arguments:
+// p - pointer to delete. Allocated with IAllocator::Alloc
+//
+// Notes:
+// This should invoke the dtor and then call IAllocator::Free.
+// In the DAC implementation, this will call via IAllocator.
+// In the DBI implementation, this can directly call delete (assuming the IAllocator::Free
+// directly called new).
+template<class T> void DeleteDbiMemory(T *p);
+// Need a class to serve as a tag that we can use to overload New/Delete.
+class forDbiWorker {};
+#define forDbi (*(forDbiWorker *)NULL)
+extern void * operator new(size_t lenBytes, const forDbiWorker &);
+extern void * operator new[](size_t lenBytes, const forDbiWorker &);
+extern void operator delete(void *p, const forDbiWorker &);
+extern void operator delete[](void *p, const forDbiWorker &);
+
+// The dac exposes a way to walk all GC references in the process. This
+// includes both strong references and weak references. This is done
+// through a referece walk.
+typedef void* * RefWalkHandle;
+
+#include "dacdbistructures.h"
+
+// This is the current format of code:DbiVersion. It needs to be rev'ed when we decide to store something
+// else other than the product version of the DBI in DbiVersion (e.g. a timestamp). See
+// code:CordbProcess::CordbProcess#DBIVersionChecking for more information.
+const DWORD kCurrentDbiVersionFormat = 1;
+
+//-----------------------------------------------------------------------------
+// This is a low-level interface between DAC and DBI.
+// The DAC is the raw DAC-ized code from the EE.
+// DBI is the implementation of ICorDebug on top of that.
+//
+// This interface should be:
+// - Stateless: The DAC component should not have any persistent state. It should not have any resources
+// that it needs to clean up. DBI can store all the state (eg, list of of modules).
+// Using IAllocator/IStringHolder interfaces to allocate data to pass back out is ok because DBI owns
+// the resources, not the DAC layer.
+// - blittable: The types on the interface should be blittable. For example, use TIDs instead of OS Thread handles.
+// Passing pointers to be used as out-parameters is ok.
+// - lightweight: it will inevitably have many methods on it and should be very fluid to use.
+// - very descriptive: heavily call out liabilities on the runtime. For example, don't just have a method like
+// "GetName" where Name is ambiguous. Heavily comment exactly what Name is, when it may fail, if it's 0-length,
+// if it's unique, etc. This serves two purposes:
+// a) it helps ensure the right invariants flow up to the public API level.
+// b) it helps ensure that the debugger is making the right assumptions about the runtime's behavior.
+//
+// #Marshaling:
+// This interface should be marshalable such that the caller (the Right Side) can exist in one
+// process, while the implementation of Dac could be on another machine.
+// - All types need to be marshable.
+// - Use OUT and OPTIONAL as defined in windef.h to guide the marshaler. Here are how types are marshaled:
+// T : value-type, copied on input.
+// T* : will be marshaled as non-null by-ref (copy on input, copy on return),
+// const T*: non-null, copy on input only.
+// OUT T*: non-null copy-on-return only.
+// OPTIONAL T*: by-ref, could be null.
+// - The marshaler has special knowledge of IStringHolder and DacDbiArrayList<T>.
+// - You can write custom marshalers for non-blittable structures defined in DacDbiStructures.h.
+// - There is custom handling for marshalling callbacks.
+//
+//
+// Threading: The interface (and the underlying DataTarget) are free-threaded to leverage
+// concurrency.
+//
+// Allocation:
+// This interface can use IAllocator to allocate objects and hand them back. The allocated objects should be:
+// - closed, serializable object graphs.
+// - should have private fields and public accessors
+// - have dtors that free any allocated the memory via calling DeleteDbiMemory.
+// Objects can be declared in a header and shared between both dbi and dac.
+// Consider using DacDbiArrayList<T> instead of custom allocations.
+
+// Error handling:
+// Any call on the interface may fail. For example, the data-target may not have access to the necessary memory.
+// Methods should throw on error.
+//
+// #Enumeration
+// General rules about Enumerations:
+// - Do not assume that enumerations exposed here are in any particular order.
+// - many enumerations also correspond to Load/Unload events. Since load/unload aren't atomic with publishing
+// in an enumeration, this is a Total Ordering of things:
+// a) object shows up in enumeration
+// b) load event.
+// c) ... steady state ...
+// d) object removed from DacDbi enumeration;
+// Any existing handles we get beyond this are explicitly associated with a Cordb* object; which can be
+// neutered on the unload event by Dbi.
+// e) unload event.
+// - Send after it's reachability from other objects is broken. (Eg, For AppDomain unload
+// means no threads left in that appdomain)
+// - Send before it's deleted (so VMPTR is still valid; not yet recycled).
+// - Send early enough that property access can at least gracefully fail. (eg,
+// Module::GetName should either return the name, or fail)
+//
+// Cordb must neuter any Cordb objects that have any pre-existing handles to the object.
+// After this point, gauranteed that nobody can discover the VMPTR any more:
+// - doesn't show up in enumerations (so can't be discoverered implicitly)
+// - object should not be discoverable by other objects in VM.
+// - any Cordb object that already had it would be neutered by Dbi.
+// - Therefore nothing should even be asking Dac for it.
+// f) object deleted.
+// Think of it like this: The event occurs to let you know that the enumeration has been updated.
+//
+// A robust debugger should not rely on events for correctness. For example,
+// a corrupt debuggee may send:
+// 1) multiple load events. (if target repeats due to an issue)
+// 2) no load event and only an unload event. (if target fails inbetween
+// publish (a) and load (b), and then backout code sends the unload).
+// 3) no unload event. (eg, if target is rudely killed)
+// 4) multiple unload events (if target repeats due to bug)
+//
+// This satisfies the following rules:
+// - once you get the load event, you can find the object via enumeration
+// - once an item is discoverable, it must immediately show up in the enumeration.
+// - once you get the unload event, the object is dead and can't be rediscovered via enumeration.
+//
+// This is an issue even for well-behaved targets. Imagine if a debugger attaches right after
+// an unload event is sent. We don't want the debugger to enumerate and re-discover the
+// unloaded object because now that the unload event is already sent, the debugger won't get
+// any further notification of when the object is deleted in the target.
+// Thus it's valuable for the debugger to have debug-only checks after unload events to assert
+// that the object is no longer discoverable.
+//
+//.............................................................................
+// The purpose of this object is to provide EE funcationality back to
+// the debugger. This represents the entire set of EE functions used
+// by the debugger.
+//
+// We will make this interface larger over time to grow the functionality
+// between the EE and the Debugger.
+//
+//
+//-----------------------------------------------------------------------------
+class IDacDbiInterface
+{
+public:
+ class IStringHolder;
+
+ // The following tag tells the DD-marshalling tool to start scanning.
+ // BEGIN_MARSHAL
+
+ //-----------------------------------------------------------------------------
+ // Functions to control the behavior of the DacDbi implementation itself.
+ //-----------------------------------------------------------------------------
+
+ //
+ // Check whether the version of the DBI matches the version of the runtime.
+ // This is only called when we are remote debugging. On Windows, we should have checked all the
+ // versions before we call any API on the IDacDbiInterface. See
+ // code:CordbProcess::CordbProcess#DBIVersionChecking for more information on version checks.
+ //
+ // Return Value:
+ // S_OK on success.
+ //
+ // Notes:
+ // THIS MUST BE THE FIRST API ON THE INTERFACE!
+ //
+ virtual
+ HRESULT CheckDbiVersion(const DbiVersion * pVersion) = 0;
+
+ //
+ // Flush the DAC cache. This should be called when target memory changes.
+ //
+ //
+ // Return Value:
+ // S_OK on success.
+ //
+ // Notes:
+ // If this fails, the interface is in an undefined state.
+ // This must be called anytime target memory changes, else all other functions
+ // (besides Destroy) may yield out-of-date or semantically incorrect results.
+ //
+ virtual
+ HRESULT FlushCache() = 0;
+
+ //
+ // Control DAC's checking of the target's consistency. Specifically, if this is disabled then
+ // ASSERTs in VM code are ignored. The default is disabled, since DAC should do it's best to
+ // return results even with a corrupt or unsyncrhonized target. See
+ // code:ClrDataAccess::TargetConsistencyAssertsEnabled for more details.
+ //
+ // When testing with a non-corrupt and properly syncrhonized target, this should be enabled to
+ // help catch bugs.
+ //
+ // Arguments:
+ // fEnableAsserts - whether ASSERTs should be raised when consistency checks fail (_DEBUG
+ // builds only)
+ //
+ // Notes:
+ // In the future we may want to extend DAC target consistency checks to be retail checks
+ // (exceptions) as well. We'll also need a mechanism for disabling them (eg. when an advanced
+ // user wants to try to get a result anyway even though the target is inconsistent). In that
+ // case we'll want an additional argument here for enabling/disabling the throwing of
+ // consistency failures exceptions (this is independent from asserts - there are legitimate
+ // scenarios for all 4 combinations).
+ //
+ virtual
+ void DacSetTargetConsistencyChecks(bool fEnableAsserts) = 0;
+
+ //
+ // Destroy the interface object. The client should call this when it's done
+ // with the IDacDbiInterface to free up any resources.
+ //
+ // Return Value:
+ // None.
+ //
+ // Notes:
+ // The client should not call anything else on this interface after Destroy.
+ //
+ virtual
+ void Destroy() = 0;
+
+ //-----------------------------------------------------------------------------
+ // General purpose target inspection functions
+ //-----------------------------------------------------------------------------
+
+ //
+ // Query if Left-side is started up?
+ //
+ //
+ // Return Value:
+ // BOOL whether Left-side is intialized.
+ //
+ // Notes:
+ // If the Left-side is not yet started up, then data in the LS is not yet initialized enough
+ // for us to make meaningful queries, but the runtime will fire "Startup Exception" when it is.
+ //
+ // If the left-side is started up, then data is ready. (Although data may be temporarily inconsistent,
+ // see DataSafe). We may still get a Startup Exception in these cases, but it can be ignored.
+ //
+ virtual
+ BOOL IsLeftSideInitialized() = 0;
+
+
+ //
+ // Get an LS Appdomain via an AppDomain unique ID.
+ // Fails if the AD is not found or if the ID is invalid.
+ //
+ // Arguments:
+ // appdomainId - "unique appdomain ID". Must be a valid Id.
+ //
+ // Return Value:
+ // VMPTR_AppDomain for the corresponding AppDomain ID. Else throws.
+ //
+ // Notes:
+ // This query is based off the lifespan of the AppDomain from the VM's perspective.
+ // The AppDomainId is most likely obtained from an AppDomain-Created debug events.
+ // An AppDomainId is unique for the lifetime of the VM.
+ // This is the inverse function of GetAppDomainId().
+ //
+ virtual
+ VMPTR_AppDomain GetAppDomainFromId(ULONG appdomainId) = 0;
+
+
+ //
+ // Get the AppDomain ID for an AppDomain.
+ //
+ // Arguments:
+ // vmAppDomain - VM pointer to the AppDomain object of interest
+ //
+ // Return Value:
+ // AppDomain ID for appdomain. Else throws.
+ //
+ // Notes:
+ // An AppDomainId is unique for the lifetime of the VM. It is non-zero.
+ //
+ virtual
+ ULONG GetAppDomainId(VMPTR_AppDomain vmAppDomain) = 0;
+
+ //
+ // Get the managed AppDomain object for an AppDomain.
+ //
+ // Arguments:
+ // vmAppDomain - VM pointer to the AppDomain object of interest
+ //
+ // Return Value:
+ // objecthandle for the managed app domain object or the Null VMPTR if there is no
+ // object created yet
+ //
+ // Notes:
+ // The AppDomain managed object is lazily constructed on the AppDomain the first time
+ // it is requested. It may be NULL.
+ //
+ virtual
+ VMPTR_OBJECTHANDLE GetAppDomainObject(VMPTR_AppDomain vmAppDomain) = 0;
+
+ //
+ // Determine if the specified AppDomain is the default domain
+ //
+ // Arguments:
+ // vmAppDomain - VM pointer to the AppDomain ojbect of interest
+ //
+ // Return Value:
+ // TRUE if this is the default appdomain, else FALSE.
+ //
+ // Notes:
+ // The default domain is the only one which cannot be unloaded and exists for the life
+ // of the process.
+ // A well behaved target only has 1 default domain.
+ //
+ virtual
+ BOOL IsDefaultDomain(VMPTR_AppDomain vmAppDomain) = 0;
+
+
+ virtual
+ void GetAssemblyFromDomainAssembly(VMPTR_DomainAssembly vmDomainAssembly, OUT VMPTR_Assembly * vmAssembly) = 0;
+
+ //
+ // Determines whether the runtime security system has assigned full-trust to this assembly.
+ //
+ // Arguments:
+ // vmDomainAssembly - VM pointer to the assembly in question.
+ //
+ // Return Value:
+ // Returns trust status for the assembly.
+ // Throws on error.
+ //
+ // Notes:
+ // Of course trusted malicious code in the process could always cause this API to lie. However,
+ // an assembly loaded without full-trust should have no way of causing this API to return true.
+ //
+ virtual
+ BOOL IsAssemblyFullyTrusted(VMPTR_DomainAssembly vmDomainAssembly) = 0;
+
+
+ //
+ // Get the full AD friendly name for the given EE AppDomain.
+ //
+ // Arguments:
+ // vmAppDomain - VM pointer to the AppDomain.
+ // pStrName - required out parameter where the name will be stored.
+ //
+ // Return Value:
+ // None. On success, sets the string via the holder. Throws on error.
+ // This either sets pStrName or Throws. It won't do both.
+ //
+ // Notes:
+ // AD names have an unbounded length. AppDomain friendly names can also change, and
+ // so callers should be prepared to listen for name-change events and requery.
+ // AD names are specified by the user.
+ //
+ virtual
+ void GetAppDomainFullName(
+ VMPTR_AppDomain vmAppDomain,
+ IStringHolder * pStrName) = 0;
+
+
+ //
+ // #ModuleNames
+ //
+ // Modules / Assemblies have many different naming schemes:
+ //
+ // 1) Metadata Scope name: All modules have metadata, and each metadata scope has a name assigned
+ // by the creator of that scope (eg, the compiler). This usually is similar to the filename, but could
+ // be arbitrary.
+ // eg: "Foo"
+ //
+ // 2) FileRecord: the File record entry in the manifest module's metadata (table 0x26) for this module.
+ // eg: "Foo"
+ //
+ // 3) Managed module path: This is path that the image was loaded from. Eg, "c:\foo.dll". For non-file
+ // based modules (like in-memory, dynamic), there is no file path. The specific path is determined by
+ // fusion / loader policy.
+ // eg: "c:\foo.dll"
+ //
+ // 4) GAC path: If the module is loaded from the GAC, this is the path on disk into the gac cache that
+ // the image was pulled from.
+ // eg: "
+ //
+ // 5) Ngen path: If the module was ngenned, this is the path on disk into the ngen cache that the image
+ // was pulled from.
+ // eg:
+ //
+ // 6) Fully Qualified Assembly Name: this is an abstract name, which the CLR (fusion / loader) will
+ // resolve (to a filename for file-based modules). Managed apps may need to deal in terms of FQN,
+ // but the debugging services generally avoid them.
+ // eg: "Foo, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL".
+ //
+
+
+ //
+ // Get the "simple name" of a module. This is a heuristic within the CLR to return a simple,
+ // not-well-specified, but meaningful, name for a module.
+ //
+ // Arguments:
+ // vmModule - module to query
+ // pStrFileName - string holder to get simple name.
+ //
+ // Return Value:
+ // None, but pStrFilename will be initialized upon return.
+ // Throws if there was a problem reading the data with DAC or if there is an OOM exception,
+ // in which case no string was stored into pStrFilename.
+ //
+ // Notes:
+ // See code:#ModuleNames for an overview on module names.
+ //
+ // This is really just using code:Module::GetSimpleName.
+ // This gives back a meaningful name, which is generally some combination of the metadata
+ // name of the FileRecord name. This is important because it's valid even when a module
+ // doesn't have a filename.
+ //
+ // The simple name does not have any meaning. It is not a filename, does not necessarily have any
+ // relationship to the filename, and it's not necesarily the metadata name.
+ // Do not use the simple name for anything other than as a pretty string to give the an end user.
+ //
+ virtual
+ void GetModuleSimpleName(VMPTR_Module vmModule, IStringHolder * pStrFilename) = 0;
+
+
+ //
+ // Get the full path and file name to the assembly's manifest module.
+ //
+ // Arguments:
+ // vmAssembly - VM pointer to the Assembly.
+ // pStrFilename - required out parameter where the filename will be stored.
+ //
+ // Return Value:
+ // TRUE on success, in which case the filename was stored into pStrFilename
+ // FALSE if the assembly has no filename (eg. for in-memory assemblies), in which
+ // case an empty string was stored into pStrFilename.
+ // Throws if there was a problem reading the data with DAC, in which case
+ // no string was stored into pStrFilename.
+ //
+ // Notes:
+ // See code:#ModuleNames for an overview on module names.
+ //
+ // Normally this is just the filename from which the dll containing the assembly was
+ // loaded. In the case of multi-module assemblies, this is the filename for the
+ // manifest module (the one containing the assembly manifest). For in-memory
+ // assemblies (eg. those loaded from a Byte[], and those created by Reflection.Emit
+ // which will not be saved to disk) there is no filename. In that case this API
+ // returns an empty string.
+ //
+ virtual
+ BOOL GetAssemblyPath(VMPTR_Assembly vmAssembly,
+ IStringHolder * pStrFilename) = 0;
+
+
+ // get a type def resolved across modules
+ // Arguments:
+ // input: pTypeRefInfo - domain file and type ref from the referencing module
+ // output: pTargetRefInfo - domain file and type def from the referenced type (this may
+ // come from a module other than the referencing module)
+ // Note: throws
+ virtual
+ void ResolveTypeReference(const TypeRefData * pTypeRefInfo,
+ TypeRefData * pTargetRefInfo) = 0;
+ //
+ // Get the full path and file name to the module (if any).
+ //
+ // Arguments:
+ // vmModule - VM pointer to the module.
+ // pStrFilename - required out parameter where the filename will be stored.
+ //
+ // Return Value:
+ // TRUE on success, in which case the filename was stored into pStrFilename
+ // FALSE the module has no filename (eg. for in-memory assemblies), in which
+ // case an empty string was stored into pStrFilename.
+ // Throws an exception if there was a problem reading the data with DAC, in which case
+ // no string was stored into pStrFilename.
+ //
+ // Notes:
+ // See code:#ModuleNames for an overview on module names.
+ //
+ // Normally this is just the filename from which the module was loaded.
+ // For in-memory module (eg. those loaded from a Byte[], and those created by Reflection.Emit
+ // which will not be saved to disk) there is no filename. In that case this API
+ // returns an empty string. Consider GetModuleSimpleName in those cases.
+ //
+ // We intentionally don't use the function name "GetModuleFileName" here because
+ // winbase #defines that token (along with many others) to have an A or W suffix.
+ //
+ virtual
+ BOOL GetModulePath(VMPTR_Module vmModule,
+ IStringHolder * pStrFilename) = 0;
+
+
+ //
+ // Get the full path and file name to the ngen image for the module (if any).
+ //
+ // Arguments:
+ // vmModule - VM pointer to the module.
+ // pStrFilename - required out parameter where the filename will be stored.
+ //
+ // Return Value:
+ // TRUE on success, in which case the filename was stored into pStrFilename
+ // FALSE the module has no filename (eg. for in-memory assemblies), in which
+ // case an empty string was stored into pStrFilename.
+ // Throws an exception if there was a problem reading the data with DAC, in which case
+ // no string was stored into pStrFilename.
+ //
+ // Notes:
+ // See code:#ModuleNames for an overview on module names.
+ //
+ virtual
+ BOOL GetModuleNGenPath(VMPTR_Module vmModule,
+ IStringHolder * pStrFilename) = 0;
+
+
+
+ // Get the metadata for the target module
+ //
+ // Arguments:
+ // vmModule - target module to get metadata for.
+ // pTargetBuffer - Out parameter to get target-buffer for metadata. Gauranteed to be non-empty on
+ // return. This will throw CORDBG_E_MISSING_METADATA hr if the buffer is empty.
+ // This does not gaurantee that the buffer is readable. For example, in a minidump, buffer's
+ // memory may not be present.
+ //
+ // Notes:
+ // Each module's metadata exists as a raw buffer in the target. This finds that target buffer and
+ // returns it. The host can then use OpenScopeOnMemory to create an instance of the metadata in
+ // the host process space.
+ //
+ // For dynamic modules, the CLR will eagerly serialize the metadata at "debuggable" points. This
+ // could be after each type is loaded; or after a bulk update.
+ // For non-dynamic modules (both in-memory and file-based), the metadata exists in the PEFile's image.
+ //
+ // Failure cases:
+ // This should succeed in normal, live-debugging scenarios. However, common failure paths here would be:
+ //
+ // 1. Data structures are intact, but Unable to even find the TargetBuffer in the target. In this
+ // case Metadata is truly missing. Likely means:
+ // - target is in the middle of generating metadata for a large bulk operation. (For example, attach
+ // to a TypeLibConverter using Ref.Emit to emit a module for a very large .tlb file).
+ // - corrupted target,
+ // - or the target had some error(out-of-memory?) generating the metadata.
+ // This throws CORDBG_E_MISSING_METADATA.
+ //
+ // 2. Target buffer is found, but memory it describes is not present. Likely means a minidump
+ // scenario with missing memory. Client should use alternative metadata location techniques (such as
+ // an ImagePath to locate the original image and then pulling metadata from that file).
+ //
+ virtual
+ void GetMetadata(VMPTR_Module vmModule, OUT TargetBuffer * pTargetBuffer) = 0;
+
+
+ // Definitions for possible symbol formats
+ // This is equivalent to code:ESymbolFormat in the runtime
+ typedef enum
+ {
+ kSymbolFormatNone, // No symbols available
+ kSymbolFormatPDB, // PDB symbol format - use diasymreader.dll
+ kSymbolFormatILDB, // ILDB symbol format - use ildbsymlib
+ } SymbolFormat;
+
+ //
+ // Get the in-memory symbol (PDB/ILDB) buffer in the target if present.
+ //
+ // Arguments:
+ // vmModule- module to query for.
+ // pTargetBuffer - out parameter to get buffer in target of symbols. If no symbols, pTargetBuffer is empty on return.
+ // pSymbolFormat - out parameter to get the format of the symbols.
+ //
+ // Returns:
+ // 1) If there are in-memory symbols for the given module, pTargetBuffer is set to the buffer describing
+ // the symbols and pSymbolFormat is set to indicate PDB or ILDB format. This buffer can then be read,
+ // converted into an IStream, and passed to ISymUnmanagedBinder::CreateReaderForStream.
+ // 2) If the target is valid, but there is no symbols for the module, then pTargetBuffer->IsEmpty() == true
+ // and *pSymbolFormat == kSymbolFormatNone.
+ // 3) Else, throws exception.
+ //
+ //
+ // Notes:
+ // For file-based modules, PDBs are normally on disk and the debugger retreieves them via a symbol
+ // path without any help from ICorDebug.
+ // However, in some cases, the PDB is stored in-memory and so the debugger needs ICorDebug. Common
+ // cases include:
+ // - dynamic modules generated with reflection-emit.
+ // - in-memory modules loaded by Load(Byte[],Byte[]), which provide the PDB as a byte[].
+ // - hosted modules where the host (such as SQL) store the PDB.
+ //
+ // In all cases, this can commonly fail. Executable code does not need to have a PDB.
+ virtual
+ void GetSymbolsBuffer(VMPTR_Module vmModule, OUT TargetBuffer * pTargetBuffer, OUT SymbolFormat * pSymbolFormat) = 0;
+
+ //
+ // Get properties for a module
+ //
+ // Arguments:
+ // vmModule - vm handle to a module
+ // pData - required out parameter which will be filled out with module properties
+ //
+ // Notes:
+ // See definition of DomainFileInfo for more details about what properties
+ // this gives back.
+ virtual
+ void GetModuleData(VMPTR_Module vmModule, OUT ModuleInfo * pData) = 0;
+
+
+ //
+ // Get properties for a DomainFile
+ //
+ // Arguments:
+ // vmDomainFile - vm handle to a DomainFile
+ // pData - required out parameter which will be filled out with module properties
+ //
+ // Notes:
+ // See definition of DomainFileInfo for more details about what properties
+ // this gives back.
+ virtual
+ void GetDomainFileData(VMPTR_DomainFile vmDomainFile, OUT DomainFileInfo * pData) = 0;
+
+ virtual
+ void GetModuleForDomainFile(VMPTR_DomainFile vmDomainFile, OUT VMPTR_Module * pModule) = 0;
+
+ //.........................................................................
+ // These methods were the methods that DBI was calling from IXClrData in V2.
+ // We imported them over to this V3 interface so that we can sever all ties between DBI and the
+ // old IXClrData.
+ //
+ // The exact semantics of these are whatever their V2 IXClrData counterpart did.
+ // We may eventually migrate these to their real V3 replacements.
+ //.........................................................................
+
+ // "types" of addresses. This is taken exactly from the definition, but renamed to match
+ // CLR coding conventions.
+ typedef enum
+ {
+ kAddressUnrecognized,
+ kAddressManagedMethod,
+ kAddressRuntimeManagedCode,
+ kAddressRuntimeUnmanagedCode,
+ kAddressGcData,
+ kAddressRuntimeManagedStub,
+ kAddressRuntimeUnmanagedStub,
+ } AddressType;
+
+ //
+ // Get the "type" of address.
+ //
+ // Arguments:
+ // address - address to query type.
+ //
+ // Return Value:
+ // Type of address. Throws on error.
+ //
+ // Notes:
+ // This is taken exactly from the IXClrData definition.
+ // This is provided for V3 compatibility to support Interop-debugging.
+ // This should eventually be deprecated.
+ //
+ virtual
+ AddressType GetAddressType(CORDB_ADDRESS address) = 0;
+
+
+ //
+ // Query if address is a CLR stub.
+ //
+ // Arguments:
+ // address - Target address to query for.
+ //
+ //
+ // Return Value:
+ // true if the address is a CLR stub.
+ //
+ // Notes:
+ // This is used to implement ICorDebugProcess::IsTransitionStub
+ // This yields true if the address is claimed by a CLR stub manager, or if the IP is in mscorwks.
+ // Conceptually, This should eventually be merged with GetAddressType().
+ //
+ virtual
+ BOOL IsTransitionStub(CORDB_ADDRESS address) = 0;
+
+ //.........................................................................
+ // Get the values of the JIT Optimization and EnC flags.
+ //
+ // Arguments:
+ // vmDomainFile - (input) VM DomainFile (module) for which we are retrieving flags
+ // pfAllowJITOpts - (mandatory output) true iff this is not compiled for debug,
+ // i.e., without optimization
+ // pfEnableEnc - (mandatory output) true iff this module has EnC enabled
+ //
+ // Return Value:
+ // Returns on success. Throws on failure.
+ //
+ // Notes:
+ // This is used to implement both ICorDebugModule2::GetJitCompilerFlags and
+ // ICorDebugCode2::GetCompilerFlags.
+ //.........................................................................
+
+ virtual
+ void GetCompilerFlags(
+ VMPTR_DomainFile vmDomainFile,
+ OUT BOOL * pfAllowJITOpts,
+ OUT BOOL * pfEnableEnC) = 0;
+
+ //.........................................................................
+ // Set the values of the JIT optimization and EnC flags.
+ //
+ // Arguments:
+ // vmDomainFile - (input) VM DomainFile (module) for which we are retrieving flags
+ // pfAllowJITOpts - (input) true iff this should not be compiled for debug,
+ // i.e., without optimization
+ // pfEnableEnc - (input) true iff this module should have EnC enabled. If this is
+ // false, no change is made to the EnC flags. In other words, once EnC is enabled,
+ // there is no way to disable it.
+ //
+ // Return Value:
+ // S_OK on success and all bits were set.
+ // CORDBG_S_NOT_ALL_BITS_SET - if not all bits are set. Must use GetCompileFlags to
+ // determine which bits were set.
+ // CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE - if module is ngenned.
+ // Throw on other errors.
+ //
+ // Notes:
+ // Caller can only use this at module-load before any methods are jitted.
+ // This may be called multiple times.
+ // This is used to implement both ICorDebugModule2::SetJitCompilerFlags and
+ // ICorDebugModule::EnableJITDebugging.
+ //.........................................................................
+
+ virtual
+ HRESULT SetCompilerFlags(VMPTR_DomainFile vmDomainFile,
+ BOOL fAllowJitOpts,
+ BOOL fEnableEnC) = 0;
+
+ //
+ // Enumerate all AppDomains in the process.
+ //
+ // Arguments:
+ // fpCallback - callback to invoke on each appdomain
+ // pUserData - user data to supply for each callback.
+ //
+ // Return Value:
+ // Returns on success. Throws on error.
+ //
+ // Notes:
+ // Enumerates all appdomains in the process, including the Default-domain.
+ // Appdomains must show up in this list before the AD Load event is sent, and before
+ // that appdomain is discoverable from the debugger.
+ // See enumeration rules for details.
+ //
+ typedef void (*FP_APPDOMAIN_ENUMERATION_CALLBACK)(VMPTR_AppDomain vmAppDomain, CALLBACK_DATA pUserData);
+ virtual
+ void EnumerateAppDomains(FP_APPDOMAIN_ENUMERATION_CALLBACK fpCallback,
+ CALLBACK_DATA pUserData) = 0;
+
+
+ //
+ // Eunmerate all Assemblies in an appdomain. Enumerations is in load-order
+ //
+ // Arguments:
+ // vmAppDomain - domain in which to enumerate
+ // fpCallback - address to query type.
+ // pUserData - required out parameter for type of address.
+ //
+ // Return Value:
+ // Returns on success. Throws on error.
+ //
+ // Notes:
+ // Enumerates all executable assemblies (both shared and unshared) within an appdomain.
+ // This does not include inspection-only assemblies because those are just data and
+ // not executable (eg, they'll never show up on the stack and you can't set a breakpoint in them).
+ // This enumeration needs to be consistent with load/unload events.
+ // See enumeration rules for details.
+ //
+ // The order of the enumeration is the order the assemblies where loaded.
+ // Ultimately, the debugger needs to be able to tell the user the load
+ // order of assemblies (it can do this with native dlls). Since
+ // managed assembliees don't 1:1 correspond to native dlls, debuggers
+ // need this information from the runtime.
+ //
+
+ typedef void (*FP_ASSEMBLY_ENUMERATION_CALLBACK)(VMPTR_DomainAssembly vmDomainAssembly, CALLBACK_DATA pUserData);
+ virtual
+ void EnumerateAssembliesInAppDomain(VMPTR_AppDomain vmAppDomain,
+ FP_ASSEMBLY_ENUMERATION_CALLBACK fpCallback,
+ CALLBACK_DATA pUserData) = 0;
+
+
+
+ //
+ // Callback function for EnumerateModulesInAssembly
+ //
+ // This can throw on error.
+ //
+ // Arguments:
+ // vmModule - new module from the enumeration
+ // pUserData - user data passed to EnumerateModulesInAssembly
+ typedef void (*FP_MODULE_ENUMERATION_CALLBACK)(VMPTR_DomainFile vmModule, CALLBACK_DATA pUserData);
+
+ //
+ // Enumerates all the code Modules in an assembly.
+ //
+ // Arguments:
+ // vmAssembly - assembly to enumerate within
+ // fpCallback - callback function to invoke on each module
+ // pUserData - arbitrary data passed to the callback
+ //
+ // Notes:
+ // This only enumerates "code" modules (ie, modules that have executable code in them). That
+ // includes normal file-based, ngenned, in-memory, and even dynamic modules.
+ // That excludes:
+ // - Resource modules (which have no code or metadata)
+ // - Inspection-only modules. These are viewed as pure data from the debugger's perspective.
+ //
+ virtual
+ void EnumerateModulesInAssembly(
+ VMPTR_DomainAssembly vmAssembly,
+ FP_MODULE_ENUMERATION_CALLBACK fpCallback,
+ CALLBACK_DATA pUserData) = 0;
+
+
+
+ //
+ // When stopped at an event, request a synchronization.
+ //
+ //
+ // Return Value:
+ // Returns on success. Throws on error.
+ //
+ // Notes:
+ // Call this when an event is dispatched (eg, LoadModule) to request the runtime
+ // synchronize. This does a cooperative sync with the LS. This is not an async break
+ // and can not be called at arbitrary points.
+ // This primitive lets the LS always take the V3 codepath and defer decision making to the RS.
+ // The V2 behavior is to call this after every event (Since that's what V2 did).
+ // The V3 behavior is to never call this.
+ //
+ // If this is called, the LS will sync and we will get a SyncComplete.
+ //
+ // This is also like a precursor to "AsyncBreakAllOtherThreads"
+ //
+ virtual
+ void RequestSyncAtEvent() = 0;
+
+ // Sets a flag inside LS.Debugger that indicates that
+ // 1. all "first chance exception" events should not be sent to the debugger
+ // 2. "exception handler found" events for exceptions never crossing JMC frames should not be sent to the debugger
+ //
+ // Arguments:
+ // sendExceptionsOutsideOfJMC - new value for the flag Debugger::m_sendExceptionsOutsideOfJMC.
+ //
+ // Return Value:
+ // Returns error code, never throws.
+ //
+ // Note: This call is used by ICorDebugProcess8.EnableExceptionCallbacksOutsideOfMyCode.
+ virtual
+ HRESULT SetSendExceptionsOutsideOfJMC(BOOL sendExceptionsOutsideOfJMC) = 0;
+
+ //
+ // Notify the debuggee that a debugger atach is pending.
+ //
+ // Arguments:
+ // None
+ //
+ // Return Value:
+ // Returns on success. Throws on error.
+ //
+ // Notes:
+ // Attaching means that CORDebuggerPendingAttach() will now return true.
+ // This doesn't do anything else (eg, no fake events).
+ //
+ // @dbgtodo- still an open Feature-Crew decision how this is exposed publicly.
+ virtual
+ void MarkDebuggerAttachPending() = 0;
+
+ //
+ // Notify the debuggee that a debugger is attached / detached.
+ //
+ // Arguments:
+ // fAttached - true if we're attaching, false if we're detaching.
+ //
+ // Return Value:
+ // Returns on success. Throws on error.
+ //
+ // Notes:
+ // Attaching means that CorDebuggerAttached() will now return true.
+ // This doesn't do anything else (eg, no fake events).
+ // This lets the V3 codepaths invade the LS to subscribe to events.
+ //
+ // @dbgtodo- still an open Feature-Crew decision how this is exposed publicly.
+ virtual
+ void MarkDebuggerAttached(BOOL fAttached) = 0;
+
+
+
+ //
+ // Hijack a thread. This will effectively do a native func-eval of the thread to set the IP
+ // to a hijack stub and push the parameters.
+ //
+ // Arguments:
+ // dwThreadId - OS thread to hijack. This must be consistent with pRecord and pOriginalContext
+ // pRecord - optional pointer to Exception record. Required if this is hijacked at an exception.
+ // NULL if this is hijacked at a managed IP.
+ // pOriginalContext - optional pointer to buffer to receive the context that the thread is hijacked from.
+ // The caller can use this to either restore the hijack or walk the hijack.
+ // cbSizeContext - size in bytes of buffer pointed to by pContext
+ // reason - reason code for the hijack. The hijack stub can then delegate to the proper hijack.
+ // pUserData - arbitrary data passed through to hijack. This is reason-depedendent.
+ // pRemoteContextAddr - If non-NULL this receives the remote address where the CONTEXT was written in the
+ // in the debuggee.
+ //
+ // Assumptions:
+ // Caller must guarantee this is safe.
+ // This is intended to be used at a thread that either just had an exception or is at a managed IP.
+ // If this is hijacked at an exception, client must cancel the exception (gh / DBG_CONTINUE)
+ // so that the OS exception processing doesn't interfere with the hijack.
+ //
+ // Notes:
+ // Hijack is hard, so we want 1 hijack stub that handles all our hijacking needs.
+ // This lets us share:
+ // - assembly stubs (which are very platform specific)
+ // - hijacking / restoration mechanics,
+ // - making the hijack walkable via the stackwalker.
+ //
+ // Hijacking can be used to implement: func-eval, FE abort, Synchronizing,
+ // dispatching Unhandled Exception notifications.
+ //
+ // Nesting: Since Hijacking passes the key state off to the hijacked thread, (such as original
+ // context to be used with restoring the hijack), the raw hijacking nests just like function
+ // calls. However, the client may need to keep additional state to handle nesting. For example,
+ // nested hijacks will require the client to track multiple CONTEXT*.
+ //
+ // If the thread is in jitted code, then the hijack needs to cooperate with the in-process
+ // stackwalker that the GC uses. It must be in cooperative mode, and push a Frame on the
+ // frame chain to protect the managed frames it hijacked from before it goes to preemptive mode.
+
+ virtual
+ void Hijack(
+ VMPTR_Thread vmThread,
+ ULONG32 dwThreadId,
+ const EXCEPTION_RECORD * pRecord,
+ T_CONTEXT * pOriginalContext,
+ ULONG32 cbSizeContext,
+ EHijackReason::EHijackReason reason,
+ void * pUserData,
+ CORDB_ADDRESS * pRemoteContextAddr) = 0;
+
+
+ //
+ // Callback function for connection enumeration.
+ //
+ // Arguments:
+ // id - the connection ID.
+ // pName - the name of the connection.
+ // pUserData - user data supplied to EnumerateConnections
+ typedef void (*FP_CONNECTION_CALLBACK)(DWORD id, LPCWSTR pName, CALLBACK_DATA pUserData);
+
+ //
+ // Enumerate all the Connections in the process.
+ //
+ // Arguments:
+ // fpCallback - callback to invoke for each connection
+ // pUserData - random user data to pass to callback.
+ //
+ // Notes:
+ // This enumerates all the connections. The host notifies the debugger of Connections
+ // via the ICLRDebugManager interface.
+ // ICorDebug has no interest in connections. It's merely the transport between the host and the debugger.
+ // Ideally, that transport would be more general.
+ //
+ // V2 Attach would provide faked up CreateConnection, ChangeConnection events on attach.
+ // This enumeration ability allows V3 to emulate that behavior.
+ //
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ virtual void EnumerateConnections(FP_CONNECTION_CALLBACK fpCallback, CALLBACK_DATA pUserData) = 0;
+#endif //FEATURE_INCLUDE_ALL_INTERFACES
+
+ //
+ // Enumerate all threads in the target.
+ //
+ // Arguments:
+ // fpCallback - callback function to invoke on each thread.
+ // pUserData - arbitrary user data supplied to each callback.
+ //
+ // Notes:
+ // This enumerates the ThreadStore in the target, which is all the Thread* objects.
+ // This includes threads that have entered the runtime. This may include threads
+ // even before that thread has executed IL and after that thread no longer has managed
+ // code on its stack.
+
+ // Callback invoked for each thread.
+ typedef void (*FP_THREAD_ENUMERATION_CALLBACK)(VMPTR_Thread vmThread, CALLBACK_DATA pUserData);
+
+ virtual
+ void EnumerateThreads(FP_THREAD_ENUMERATION_CALLBACK fpCallback, CALLBACK_DATA pUserData) = 0;
+
+
+ // Check if the thread is dead
+ //
+ // Arguments:
+ // vmThread - valid thread to check if it's dead.
+ //
+ // Returns: true if the thread is "dead", which means it can never call managed code again.
+ //
+ // Notes:
+ // #IsThreadMarkedDead
+ // Threads shutdown states are:
+ // 1) Thread is running managed code normally. Thread eventually exits all managed code and
+ // gets to a point where it will never call managed code again.
+ // 2) Thread is marked as dead.
+ // - For threads created outside of the runtime (such as a native thread that wanders into
+ // managed code), this mark can happen in DllMain(ThreadDetach)
+ // - For threads created by the runtime (eg, System.Threading.Thread.Start), this may be done
+ // at the top of the threads stack after it calls the user's Thread-Proc.
+ // 3) MAYBE Native thread exits at this point (or it may not). This would be the common case
+ // for threads created outside the runtime.
+ // 4) Thread exit event is sent.
+ // - For threads created by the runtime, this may be sent at the top of the thread's
+ // stack (or even when we know that the thread will never execute managed code again)
+ // - For threads created outside the runtime, this is more difficult. A thread can
+ // call into managed code and then return, and then call back into managed code at a
+ // later time (The finalizer does this!). So it's not clear when the native thread
+ // actually exits and will never call managed code again. The only hook we have for
+ // this is DllMain(Thread-Detach). We can mark bits in DllMain, but we can't send
+ // debugger notifications (too dangerous from such a restricted context).
+ // So we may mark the thread as dead, but then sweep later (perhaps on the finalizer
+ // thread), and thus send the Exit events later.
+ // 5) Native thread may exit at this point. This is the common case for threads created by
+ // the runtime.
+ //
+ // The underlying native thread may have exited at eitehr #3 or #5. Because of this
+ // flexibility, we don't want to rely on native thread exit events.
+ // This function checks if a Thread is passed state #2 (marked as dead). The key invariant
+ // is that once a thread is marked as dead:
+ // - it can never call managed code again.
+ // - it should not be discoverable by DacDbi enumerations.
+ //
+ // DBI should prefer relying on IsThreadMarkedDead rather than event notifications (either
+ // managed or native) because tracking events requires that DBI maintain state, which means
+ // that attach + dump cases may break. For example, we want a full dump at the ExitThread
+ // event to have the same view as a live process at the ExitThread event.
+ //
+ // We avoid relying on the native thread exit notifications because:
+ // - that's a specific feature of the Win32 debugging API that may not be available on other platforms.
+ // - the only native events the pipeline gets are Exceptions.
+ //
+ // Whether a thread is dead can be inferred from the ICorDebug API. However, we have this
+ // on DacDbi to ensure that this definition is consistent with the other DacDbi methods,
+ // especially the enumeration and discovery rules.
+ virtual
+ bool IsThreadMarkedDead(VMPTR_Thread vmThread) = 0;
+
+
+ //
+ // Return the handle of the specified thread.
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ //
+ // Return Value:
+ // the handle of the specified thread
+ //
+ // @dbgtodo- this should go away in V3. This is useless on a dump.
+
+ virtual
+ HANDLE GetThreadHandle(VMPTR_Thread vmThread) = 0;
+
+ //
+ // Return the object handle for the managed Thread object corresponding to the specified thread.
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ //
+ // Return Value:
+ // This function returns the object handle for the managed Thread object corresponding to the
+ // specified thread. The return value may be NULL if a managed Thread object has not been created
+ // for the specified thread yet.
+ //
+
+ virtual
+ VMPTR_OBJECTHANDLE GetThreadObject(VMPTR_Thread vmThread) = 0;
+
+ //
+ // Set and reset the TSNC_DebuggerUserSuspend bit on the state of the specified thread
+ // according to the CorDebugThreadState.
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ // debugState - the desired CorDebugThreadState
+ //
+
+ virtual
+ void SetDebugState(VMPTR_Thread vmThread,
+ CorDebugThreadState debugState) = 0;
+
+ //
+ // Returns TRUE if this thread has an unhandled exception
+ //
+ // Arguments:
+ // vmThread - the thread to query
+ //
+ // Return Value
+ // TRUE iff this thread has an unhandled exception
+ //
+ virtual
+ BOOL HasUnhandledException(VMPTR_Thread vmThread) = 0;
+
+ //
+ // Return the user state of the specified thread. Most of the state are derived from
+ // the ThreadState of the specified thread, e.g. TS_Background, TS_Unstarted, etc.
+ // The exception is USER_UNSAFE_POINT, which we need to do a one-frame stackwalk to figure out.
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ //
+ // Return Value:
+ // the user state of the specified thread
+ //
+
+ virtual
+ CorDebugUserState GetUserState(VMPTR_Thread vmThread) = 0;
+
+
+ //
+ // Returns most of the user state of the specified thread,
+ // i.e. flags which can be derived from the ThreadState:
+ // USER_STOP_REQUESTED, USER_SUSPEND_REQUESTED, USER_BACKGROUND, USER_UNSTARTED
+ // USER_STOPPED, USER_WAIT_SLEEP_JOIN, USER_SUSPENDED, USER_THREADPOOL
+ //
+ // Only USER_UNSAFE_POINT is always set to 0, since it takes additional stackwalk.
+ // If you need USER_UNSAFE_POINT, use GetUserState(VMPTR_Thread);
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ //
+ // Return Value:
+ // the user state of the specified thread
+ //
+ virtual
+ CorDebugUserState GetPartialUserState(VMPTR_Thread vmThread) = 0;
+
+
+ //
+ // Return the connection ID of the specified thread.
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ //
+ // Return Value:
+ // the connection ID of the specified thread
+ //
+
+ virtual
+ CONNID GetConnectionID(VMPTR_Thread vmThread) = 0;
+
+ //
+ // Return the task ID of the specified thread.
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ //
+ // Return Value:
+ // the task ID of the specified thread
+ //
+
+ virtual
+ TASKID GetTaskID(VMPTR_Thread vmThread) = 0;
+
+ //
+ // Return the OS thread ID of the specified thread
+ //
+ // Arguments:
+ // vmThread - the specified thread; cannot be NULL
+ //
+ // Return Value:
+ // the OS thread ID of the specified thread. Returns 0 if not scheduled.
+ //
+
+ virtual
+ DWORD TryGetVolatileOSThreadID(VMPTR_Thread vmThread) = 0;
+
+ //
+ // Return the unique thread ID of the specified thread. The value used for the thread ID changes
+ // depending on whether the runtime is being hosted. In non-hosted scenarios, a managed thread will
+ // always be associated with the same native thread, and so we can use the OS thread ID as the thread ID
+ // for the managed thread. In hosted scenarios, however, a managed thread may run on multiple native
+ // threads. It may not even have a backing native thread if it's switched out. Therefore, we can't use
+ // the OS thread ID as the thread ID. Instead, we use the internal managed thread ID.
+ //
+ // Arguments:
+ // vmThread - the specified thread; cannot be NULL
+ //
+ // Return Value:
+ // Returns a stable and unique thread ID for the lifetime of the specified managed thread.
+ //
+
+ virtual
+ DWORD GetUniqueThreadID(VMPTR_Thread vmThread) = 0;
+
+ //
+ // Return the object handle to the managed Exception object of the current exception
+ // on the specified thread. The return value could be NULL if there is no current exception.
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ //
+ // Return Value:
+ // This function returns the object handle to the managed Exception object of the current exception.
+ // The return value may be NULL if there is no exception being processed, or if the specified thread
+ // is an unmanaged thread which has entered and exited the runtime.
+ //
+
+ virtual
+ VMPTR_OBJECTHANDLE GetCurrentException(VMPTR_Thread vmThread) = 0;
+
+ //
+ // Return the object handle to the managed object for a given CCW pointer.
+ //
+ // Arguments:
+ // ccwPtr - the specified ccw pointer
+ //
+ // Return Value:
+ // This function returns the object handle to the managed object for a given CCW pointer.
+ //
+
+ virtual
+ VMPTR_OBJECTHANDLE GetObjectForCCW(CORDB_ADDRESS ccwPtr) = 0;
+
+ //
+ // Return the object handle to the managed CustomNotification object of the current notification
+ // on the specified thread. The return value could be NULL if there is no current notification.
+ //
+ // Arguments:
+ // vmThread - the specified thread on which the notification occurred
+ //
+ // Return Value:
+ // This function returns the object handle to the managed CustomNotification object of the current notification.
+ // The return value may be NULL if there is no current notification.
+ //
+
+ virtual
+ VMPTR_OBJECTHANDLE GetCurrentCustomDebuggerNotification(VMPTR_Thread vmThread) = 0;
+
+
+ //
+ // Return the current appdomain the specified thread is in.
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ //
+ // Return Value:
+ // the current appdomain of the specified thread
+ //
+ // Notes:
+ // This function throws if the current appdomain is NULL for whatever reason.
+ //
+
+ virtual
+ VMPTR_AppDomain GetCurrentAppDomain(VMPTR_Thread vmThread) = 0;
+
+
+ //
+ // Resolve an assembly
+ //
+ // Arguments:
+ // vmScope - module containing metadata that the token is scoped to.
+ // tkAssemblyRef - assembly ref token to lookup.
+ //
+ // Returns:
+ // Assembly that the loader/fusion has bound to the given assembly ref.
+ // Returns NULL if the assembly has not yet been loaded (a common case).
+ // Throws on error.
+ //
+ // Notes:
+ // A single module has metadata that specifies references via tokens. The
+ // loader/fusion goes through tremendous and random policy hoops to determine
+ // which specific file actually gets bound to the reference. This policy includes
+ // things like config files, registry settings, and many other knobs.
+ //
+ // The debugger can't duplicate this policy with 100% accuracy, and
+ // so we need DAC to lookup the assembly that was actually loaded.
+ virtual
+ VMPTR_DomainAssembly ResolveAssembly(VMPTR_DomainFile vmScope, mdToken tkAssemblyRef) = 0;
+
+ //-----------------------------------------------------------------------------
+ // Interface for initializing the native/IL sequence points and native var info
+ // for a function.
+ // Arguments:
+ // input:
+ // vmMethodDesc MethodDesc of the function
+ // startAddr starting address of the function--this serves to
+ // differentiate various EnC versions of the function
+ // fCodePitched indicates whether code for the function has been pitched
+ // fJitComplete indicates whether the function has been jitted
+ // output:
+ // pNativeVarData space for the native code offset information for locals
+ // pSequencePoints space for the IL/native sequence points
+ // Return value:
+ // none, but may throw an exception
+ // Assumptions:
+ // vmMethodDesc, pNativeVarInfo and pSequencePoints are non-NULL
+
+ // Notes:
+ //-----------------------------------------------------------------------------
+
+ virtual
+ void GetNativeCodeSequencePointsAndVarInfo(VMPTR_MethodDesc vmMethodDesc,
+ CORDB_ADDRESS startAddress,
+ BOOL fCodeAvailabe,
+ OUT NativeVarData * pNativeVarData,
+ OUT SequencePoints * pSequencePoints) = 0;
+
+ //
+ // Return the filter CONTEXT on the LS. Once we move entirely over to the new managed pipeline
+ // built on top of the Win32 debugging API, this won't be necessary.
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ //
+ // Return Value:
+ // the filter CONTEXT of the specified thread
+ //
+ // Notes:
+ // This function should go away when everything is moved OOP and
+ // we don't have a filter CONTEXT on the LS anymore.
+ //
+
+ virtual
+ VMPTR_CONTEXT GetManagedStoppedContext(VMPTR_Thread vmThread) = 0;
+
+ typedef enum
+ {
+ kInvalid,
+ kManagedStackFrame,
+ kExplicitFrame,
+ kNativeStackFrame,
+ kNativeRuntimeUnwindableStackFrame,
+ kAtEndOfStack,
+ } FrameType;
+
+ // The stackwalker functions allocate persistent state within DDImpl. Clients can hold onto
+ // this via an opaque StackWalkHandle.
+ typedef void* * StackWalkHandle;
+
+ //
+ // Create a stackwalker on the specified thread and return a handle to it.
+ // Initially, the stackwalker is at the filter CONTEXT if there is one.
+ // Otherwise it is at the leaf CONTEXT. It DOES NOT fast forward to the first frame of interest.
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ // pInternalContextBuffer - a CONTEXT buffer for the stackwalker to work with
+ // ppSFIHandle - out parameter; return a handle to the stackwalker
+ //
+ // Notes:
+ // Call DeleteStackWalk() to delete the stackwalk buffer.
+ // This is a special case that violates the 'no state' tenant.
+ //
+
+ virtual
+ void CreateStackWalk(VMPTR_Thread vmThread,
+ DT_CONTEXT * pInternalContextBuffer,
+ OUT StackWalkHandle * ppSFIHandle) = 0;
+
+ // Delete the stackwalk object created from CreateStackWalk.
+ virtual
+ void DeleteStackWalk(StackWalkHandle ppSFIHandle) = 0;
+
+ //
+ // Get the CONTEXT of the current frame where the stackwalker is stopped at.
+ //
+ // Arguments:
+ // pSFIHandle - the handle to the stackwalker
+ // pContext - OUT: the CONTEXT to be filled out. The context control flags are ignored.
+ //
+
+ virtual
+ void GetStackWalkCurrentContext(StackWalkHandle pSFIHandle,
+ DT_CONTEXT * pContext) = 0;
+
+ //
+ // Set the stackwalker to the given CONTEXT. The CorDebugSetContextFlag indicates whether
+ // the CONTEXT is "active", meaning that the IP is point at the current instruction,
+ // not the return address of some function call.
+ //
+ // Arguments:
+ // vmThread - the current thread
+ // pSFIHandle - the handle to the stackwalker
+ // flag - flag to indicate whether the specified CONTEXT is "active"
+ // pContext - the specified CONTEXT. This may make correctional adjustments to the context's IP.
+ //
+
+ virtual
+ void SetStackWalkCurrentContext(VMPTR_Thread vmThread,
+ StackWalkHandle pSFIHandle,
+ CorDebugSetContextFlag flag,
+ DT_CONTEXT * pContext) = 0;
+
+ //
+ // Unwind the stackwalker to the next frame. The next frame could be any actual stack frame,
+ // explicit frame, native marker frame, etc. Call GetStackWalkCurrentFrameInfo() to find out
+ // more about the frame.
+ //
+ // Arguments:
+ // pSFIHandle - the handle to the stackwalker
+ //
+ // Return Value:
+ // Return TRUE if we successfully unwind to the next frame.
+ // Return FALSE if there is no more frames to walk.
+ // Throw on error.
+ //
+
+ virtual
+ BOOL UnwindStackWalkFrame(StackWalkHandle pSFIHandle) = 0;
+
+ //
+ // Check whether the specified CONTEXT is valid. The only check we perform right now is whether the
+ // SP in the specified CONTEXT is in the stack range of the thread.
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ // pContext - the CONTEXT to be checked
+ //
+ // Return Value:
+ // Return S_OK if the CONTEXT passes our checks.
+ // Returns CORDBG_E_NON_MATCHING_CONTEXT if the SP in the specified CONTEXT doesn't fall in the stack
+ // range of the thread.
+ // Throws on error.
+ //
+
+ virtual
+ HRESULT CheckContext(VMPTR_Thread vmThread,
+ const DT_CONTEXT * pContext) = 0;
+
+ //
+ // Fill in the DebuggerIPCE_STRData structure with information about the current frame
+ // where the stackwalker is stopped at.
+ //
+ // Arguments:
+ // pSFIHandle - the handle to the stackwalker
+ // pFrameData - the DebuggerIPCE_STRData to be filled out;
+ // it can be NULL if you just want to know the frame type
+ //
+ // Return Value:
+ // Return the type of the current frame
+ //
+
+ virtual
+ FrameType GetStackWalkCurrentFrameInfo(StackWalkHandle pSFIHandle,
+ OPTIONAL DebuggerIPCE_STRData * pFrameData) = 0;
+
+ //
+ // Return the number of internal frames on the specified thread.
+ //
+ // Arguments:
+ // vmThread - the thread whose internal frames are being retrieved
+ //
+ // Return Value:
+ // Return the number of internal frames.
+ //
+ // Notes:
+ // Explicit frames are "marker objects" the runtime pushes on the stack to mark special places, e.g.
+ // appdomain transition, managed-to- unmanaged transition, etc. Internal frames are only a subset of
+ // explicit frames. Explicit frames which are not interesting to the debugger are not exposed (e.g.
+ // GCFrame). Internal frames are interesting to the debugger if they have a CorDebugInternalFrameType
+ // other than STUBFRAME_NONE.
+ //
+ // The user should call this function before code:IDacDbiInterface::EnumerateInternalFrames to figure
+ // out how many interesting internal frames there are.
+ //
+
+ virtual
+ ULONG32 GetCountOfInternalFrames(VMPTR_Thread vmThread) = 0;
+
+ //
+ // Enumerate the internal frames on the specified thread and invoke the provided callback on each of
+ // them. Information about the internal frame is stored in the DebuggerIPCE_STRData.
+ //
+ // Arguments:
+ // vmThread - the thread to be walked fpCallback - callback function invoked on each internal frame
+ // pUserData - user-specified custom data
+ //
+ // Notes:
+ // The user can call code:IDacDbiInterface::GetCountOfInternalFrames to figure out how many internal
+ // frames are on the thread before calling this function. Also, refer to the comment of that function
+ // to find out more about internal frames.
+ //
+
+ typedef void (*FP_INTERNAL_FRAME_ENUMERATION_CALLBACK)(const DebuggerIPCE_STRData * pFrameData, CALLBACK_DATA pUserData);
+
+ virtual
+ void EnumerateInternalFrames(VMPTR_Thread vmThread,
+ FP_INTERNAL_FRAME_ENUMERATION_CALLBACK fpCallback,
+ CALLBACK_DATA pUserData) = 0;
+
+ //
+ // Given the FramePointer of the parent frame and the FramePointer of the current frame,
+ // check if the current frame is the parent frame. fpParent should have been returned
+ // previously by the DacDbiInterface via GetStackWalkCurrentFrameInfo().
+ //
+ // Arguments:
+ // fpToCheck - the FramePointer of the current frame
+ // fpParent - the FramePointer of the parent frame; should have been returned earlier by the DDI
+ //
+ // Return Value:
+ // Return TRUE if the current frame is indeed the parent frame
+ //
+ // Note:
+ // Because of the complexity involved in checking for the parent frame, we should always
+ // ask the ExceptionTracker to do it.
+ //
+
+ virtual
+ BOOL IsMatchingParentFrame(FramePointer fpToCheck, FramePointer fpParent) = 0;
+
+ //
+ // Return the stack parameter size of a given method. This is necessary on x86 for unwinding.
+ //
+ // Arguments:
+ // controlPC - any address in the specified method; you can use the current PC of the stack frame
+ //
+ // Return Value:
+ // Return the size of the stack parameters of the given method.
+ // Return 0 for vararg methods.
+ //
+ // Assumptions:
+ // The callee stack parameter size is constant throughout a method.
+ //
+
+ virtual
+ ULONG32 GetStackParameterSize(CORDB_ADDRESS controlPC) = 0;
+
+ //
+ // Return the FramePointer of the current frame where the stackwalker is stopped at.
+ //
+ // Arguments:
+ // pSFIHandle - the handle to the stackwalker
+ //
+ // Return Value:
+ // the FramePointer of the current frame
+ //
+ // Notes:
+ // The FramePointer of a stack frame is:
+ // the stack address of the return address on x86,
+ // the current SP on AMD64,
+ //
+ // On x86, to get the stack address of the return address, we need to unwind one more frame
+ // and use the SP of the caller frame as the FramePointer of the callee frame. This
+ // function does NOT do that. It just returns the SP. The caller needs to handle the
+ // unwinding.
+ //
+ // The FramePointer of an explicit frame is just the stack address of the explicit frame.
+ //
+
+ virtual
+ FramePointer GetFramePointer(StackWalkHandle pSFIHandle) = 0;
+
+ //
+ // Check whether the specified CONTEXT is the CONTEXT of the leaf frame. This function doesn't care
+ // whether the leaf frame is native or managed.
+ //
+ // Arguments:
+ // vmThread - the specified thread
+ // pContext - the CONTEXT to check
+ //
+ // Return Value:
+ // Return TRUE if the specified CONTEXT is the leaf CONTEXT.
+ //
+ // Notes:
+ // Currently we check the specified CONTEXT against the filter CONTEXT first.
+ // This will be deprecated in V3.
+ //
+
+ virtual
+ BOOL IsLeafFrame(VMPTR_Thread vmThread,
+ const DT_CONTEXT * pContext) = 0;
+
+ // Get the context for a particular thread of the target process.
+ // Arguments:
+ // input: vmThread - the thread for which the context is required
+ // output: pContextBuffer - the address of the CONTEXT to be initialized.
+ // The memory for this belongs to the caller. It must not be NULL.
+ // Note: throws
+ virtual
+ void GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pContextBuffer) = 0;
+
+ //
+ // This is a simple helper function to convert a CONTEXT to a DebuggerREGDISPLAY. We need to do this
+ // inside DDI because the RS has no notion of REGDISPLAY.
+ //
+ // Arguments:
+ // pInContext - the CONTEXT to be converted
+ // pOutDRD - the converted DebuggerREGDISPLAY
+ // fActive - Indicate whether the CONTEXT is active or not. An active CONTEXT means that the
+ // IP is the next instruction to be executed, not the return address of a function call.
+ // The opposite of an active CONTEXT is an unwind CONTEXT, which is obtained from
+ // unwinding.
+ //
+
+ virtual
+ void ConvertContextToDebuggerRegDisplay(const DT_CONTEXT * pInContext,
+ DebuggerREGDISPLAY * pOutDRD,
+ BOOL fActive) = 0;
+
+ typedef enum
+ {
+ kNone,
+ kILStub,
+ kLCGMethod,
+ } DynamicMethodType;
+
+ //
+ // Check whether the specified method is an IL stub or an LCG method. This answer determines if we
+ // need to expose the method in a V2-style stackwalk.
+ //
+ // Arguments:
+ // vmMethodDesc - the method to be checked
+ //
+ // Return Value:
+ // Return kNone if the method is neither an IL stub or an LCG method.
+ // Return kILStub if the method is an IL stub.
+ // Return kLCGMethod if the method is an LCG method.
+ //
+
+ virtual
+ DynamicMethodType IsILStubOrLCGMethod(VMPTR_MethodDesc vmMethodDesc) = 0;
+
+ //
+ // Return a TargetBuffer for the raw vararg signature.
+ // Also return the address of the first argument in the vararg signature.
+ //
+ // Arguments:
+ // VASigCookieAddr - the target address of the VASigCookie pointer (double indirection)
+ // pArgBase - out parameter; return the target address of the first word of the arguments
+ //
+ // Return Value:
+ // Return a TargetBuffer for the raw vararg signature.
+ //
+ // Notes:
+ // We can't take a VMPTR here because VASigCookieAddr does not come from the DDI. Instead,
+ // we use the native variable information to figure out which stack slot contains the
+ // VASigCookie pointer. So a remote address is all we have got.
+ //
+ // Ideally we should be able to return just a SigParser, but doing so has a not-so-trivial problem.
+ // The memory used for the signature pointed to by the SigParser cannot be allocated in the DAC cache,
+ // since it'll be used by mscordbi. We don't have a clean way to allocate memory in mscordbi without
+ // breaking the Signature abstraction.
+ //
+ // The other option would be to create a new sub-type like "SignatureCopy" which allocates and frees
+ // its own backing memory. Currently we don't want to share heaps between mscordacwks.dll and
+ // mscordbi.dll, and so we would have to jump through some hoops to allocate with an allocator
+ // in mscordbi.dll.
+ //
+
+ virtual
+ TargetBuffer GetVarArgSig(CORDB_ADDRESS VASigCookieAddr,
+ OUT CORDB_ADDRESS * pArgBase) = 0;
+
+ //
+ // Indicates if the specified type requires 8-byte alignment.
+ //
+ // Arguments:
+ // thExact - the exact TypeHandle of the type to query
+ //
+ // Return Value:
+ // TRUE if the type requires 8-byte alignment.
+ //
+
+ virtual
+ BOOL RequiresAlign8(VMPTR_TypeHandle thExact) = 0;
+
+ //
+ // Resolve the raw generics token to the real generics type token. The resolution is based on the
+ // given index. See Notes below.
+ //
+ // Arguments:
+ // dwExactGenericArgsTokenIndex - the variable index of the generics type token
+ // rawToken - the raw token to be resolved
+ //
+ // Return Value:
+ // Return the actual generics type token.
+ //
+ // Notes:
+ // DDI tells the RS which variable stores the generics type token, but DDI doesn't retrieve the value
+ // of the variable itself. Instead, the RS retrieves the value of the variable. However,
+ // in some cases, the variable value is not the generics type token. In this case, we need to
+ // "resolve" the variable value to the generics type token. The RS should call this API to do that.
+ //
+ // If the index is 0, then the generics type token is the MethodTable of the "this" object.
+ // rawToken will be the address of the "this" object.
+ //
+ // If the index is TYPECTXT_ILNUM, the generics type token is a secret argument.
+ // It could be a MethodDesc or a MethodTable, and in this case no resolution is actually necessary.
+ // rawToken will be the actual secret argument, and this API really is just a nop.
+ //
+ // However, we don't want the RS to know all this logic.
+ //
+
+ virtual
+ GENERICS_TYPE_TOKEN ResolveExactGenericArgsToken(DWORD dwExactGenericArgsTokenIndex,
+ GENERICS_TYPE_TOKEN rawToken) = 0;
+
+ //-----------------------------------------------------------------------------
+ // Functions to get information about code objects
+ //-----------------------------------------------------------------------------
+
+ // GetILCodeAndSig returns the function's ILCode and SigToken given
+ // a module and a token. The info will come from a MethodDesc, if
+ // one exists or from metadata.
+ //
+ // Arguments:
+ // Input:
+ // vmDomainFile - module containing metadata for the method
+ // functionToken - metadata token for the function
+ // Output (required):
+ // codeInfo - start address and size of the IL
+ // pLocalSigToken - signature token for the method
+ virtual
+ void GetILCodeAndSig(VMPTR_DomainFile vmDomainFile,
+ mdToken functionToken,
+ OUT TargetBuffer * pCodeInfo,
+ OUT mdToken * pLocalSigToken) = 0;
+
+ // Gets information about a native code blob:
+ // it's method desc, whether it's an instantiated generic, its EnC version number
+ // and hot and cold region information.
+ // Arguments:
+ // Input:
+ // vmDomainFile - module containing metadata for the method
+ // functionToken - token for the function for which we need code info
+ // Output (required):
+ // pCodeInfo - data structure describing the native code regions.
+ // Notes: If the function is unjitted, the method desc will be NULL and the
+ // output parameter will be invalid. In general, if the native start address
+ // is unavailable for any reason, the output parameter will also be
+ // invalid (i.e., pCodeInfo->IsValid is false).
+
+ virtual
+ void GetNativeCodeInfo(VMPTR_DomainFile vmDomainFile,
+ mdToken functionToken,
+ OUT NativeCodeFunctionData * pCodeInfo) = 0;
+
+ // Gets information about a native code blob:
+ // it's method desc, whether it's an instantiated generic, its EnC version number
+ // and hot and cold region information.
+ // This is similar to function above, just works from a different starting point
+ // Also this version can get info for any particular EnC version instance
+ // because they all have different start addresses whereas the above version gets
+ // the most recent one
+ // Arguments:
+ // Input:
+ // hotCodeStartAddr - the beginning of the code hot code region
+ // Output (required):
+ // pCodeInfo - data structure describing the native code regions.
+
+ virtual
+ void GetNativeCodeInfoForAddr(VMPTR_MethodDesc vmMethodDesc,
+ CORDB_ADDRESS hotCodeStartAddr,
+ NativeCodeFunctionData * pCodeInfo) = 0;
+
+ //-----------------------------------------------------------------------------
+ // Functions to get information about types
+ //-----------------------------------------------------------------------------
+
+ // Determine if a type is a ValueType
+ //
+ // Arguments:
+ // input: vmTypeHandle - the type being checked (works even on unrestored types)
+ //
+ // Return:
+ // TRUE iff the type is a ValueType
+
+ virtual
+ BOOL IsValueType (VMPTR_TypeHandle th) = 0;
+
+ // Determine if a type has generic parameters
+ //
+ // Arguments:
+ // input: vmTypeHandle - the type being checked (works even on unrestored types)
+ //
+ // Return:
+ // TRUE iff the type has generic parameters
+
+ virtual
+ BOOL HasTypeParams (VMPTR_TypeHandle th) = 0;
+
+ // Get type information for a class
+ //
+ // Arguments:
+ // input: vmAppDomain - appdomain where we will fetch field data for the type
+ // thExact - exact type handle for type
+ // output:
+ // pData - structure containing information about the class and its
+ // fields
+
+ virtual
+ void GetClassInfo (VMPTR_AppDomain vmAppDomain,
+ VMPTR_TypeHandle thExact,
+ ClassInfo * pData) = 0;
+
+ // get field information and object size for an instantiated generic
+ //
+ // Arguments:
+ // input: vmDomainFile - module containing metadata for the type
+ // thExact - exact type handle for type (may be NULL)
+ // thApprox - approximate type handle for the type
+ // output:
+ // pFieldList - array of structures containing information about the fields. Clears any previous
+ // contents. Allocated and initialized by this function.
+ // pObjectSize - size of the instantiated object
+ //
+ virtual
+ void GetInstantiationFieldInfo (VMPTR_DomainFile vmDomainFile,
+ VMPTR_TypeHandle vmThExact,
+ VMPTR_TypeHandle vmThApprox,
+ OUT DacDbiArrayList<FieldData> * pFieldList,
+ OUT SIZE_T * pObjectSize) = 0;
+
+ // use a type handle to get the information needed to create the corresponding RS CordbType instance
+ //
+ // Arguments:
+ // input: boxed - indicates what, if anything, is boxed. See code:AreValueTypesBoxed for more
+ // specific information
+ // vmAppDomain - module containing metadata for the type
+ // vmTypeHandle - type handle for the type
+ // output: pTypeInfo - holds information needed to build the corresponding CordbType
+ //
+ virtual
+ void TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed,
+ VMPTR_AppDomain vmAppDomain,
+ VMPTR_TypeHandle vmTypeHandle,
+ DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0;
+
+ virtual
+ void GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed,
+ VMPTR_AppDomain vmAppDomain,
+ CORDB_ADDRESS addr,
+ OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0;
+
+
+ virtual
+ void GetObjectExpandedTypeInfoFromID(AreValueTypesBoxed boxed,
+ VMPTR_AppDomain vmAppDomain,
+ COR_TYPEID id,
+ OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0;
+
+
+ // Get type handle for a TypeDef token, if one exists. For generics this returns the open type.
+ // Note there is no guarantee the returned handle will be fully restored (in pre-jit scenarios),
+ // only that it exists. Later functions that use this type handle should fail if they require
+ // information not yet available at the current restoration level
+ //
+ // Arguments:
+ // input: vmModule - the module scope in which to look up the type def
+ // metadataToken - the type definition to retrieve
+ //
+ // Return value: the type handle if it exists or throws CORDBG_E_CLASS_NOT_LOADED if it isn't loaded
+ //
+ virtual
+ VMPTR_TypeHandle GetTypeHandle(VMPTR_Module vmModule,
+ mdTypeDef metadataToken) = 0;
+
+ // Get the approximate type handle for an instantiated type. This may be identical to the exact type handle,
+ // but if we have code sharing for generics, it may differ in that it may have canonical type parameters.
+ // This will occur if we have not yet loaded an exact type but we have loaded the canonical form of the
+ // type.
+ //
+ // Arguments:
+ // input: pTypeData - information needed to get the type handle, this includes a list of type parameters
+ // and the number of entries in the list. Allocated and initialized by the caller.
+ // Return value: the approximate type handle
+ //
+ virtual
+ VMPTR_TypeHandle GetApproxTypeHandle(TypeInfoList * pTypeData) = 0;
+
+ // Get the exact type handle from type data.
+ // Arguments:
+ // input: pTypeData - type information for the type. includes information about
+ // the top-level type as well as information
+ // about the element type for array types, the referent for
+ // pointer types, or actual parameters for generic class or
+ // valuetypes, as appropriate for the top-level type.
+ // pArgInfo - This is preallocated and initialized by the caller and contains two fields:
+ // genericArgsCount - number of type parameters (these may be actual type parameters
+ // for generics or they may represent the element type or referent
+ // type.
+ // pGenericArgData - list of type parameters
+ // vmTypeHandle - the exact type handle derived from the type information
+ // Return Value: an HRESULT indicating the result of the operation
+ virtual
+ HRESULT GetExactTypeHandle(DebuggerIPCE_ExpandedTypeData * pTypeData,
+ ArgInfoList * pArgInfo,
+ VMPTR_TypeHandle& vmTypeHandle) = 0;
+
+ //
+ // Retrieve the generic type params for a given MethodDesc. This function is specifically
+ // for stackwalking because it requires the generic type token on the stack.
+ //
+ // Arguments:
+ // vmAppDomain - the appdomain of the MethodDesc
+ // vmMethodDesc - the method in question
+ // genericsToken - the generic type token in the stack frame owned by the method
+ //
+ // pcGenericClassTypeParams - out parameter; returns the number of type parameters for the class
+ // containing the method in question; must not be NULL
+ // pGenericTypeParams - out parameter; returns an array of type parameters and
+ // the count of the total number of type parameters; must not be NULL
+ //
+ // Notes:
+ // The memory for the array is allocated by this function on the Dbi heap.
+ // The caller is responsible for releasing it.
+ //
+
+ virtual
+ void GetMethodDescParams(VMPTR_AppDomain vmAppDomain,
+ VMPTR_MethodDesc vmMethodDesc,
+ GENERICS_TYPE_TOKEN genericsToken,
+ OUT UINT32 * pcGenericClassTypeParams,
+ OUT TypeParamsList * pGenericTypeParams) = 0;
+
+ // Get the target field address of a context or thread local static.
+ // Arguments:
+ // input: vmField - pointer to the field descriptor for the static field
+ // vmRuntimeThread - thread to which the static field belongs. This must
+ // NOT be NULL
+ // Return Value: The target address of the field if the field is allocated.
+ // NULL if the field storage is not yet allocated.
+ //
+ // Note:
+ // Static field storage is lazily allocated, so this may commonly return NULL.
+ // This is an inspection only method and can not allocate the static storage.
+ // Field storage is constant once allocated, so this value can be cached.
+
+ virtual
+ CORDB_ADDRESS GetThreadOrContextStaticAddress(VMPTR_FieldDesc vmField,
+ VMPTR_Thread vmRuntimeThread) = 0;
+
+ // Get the target field address of a collectible types static.
+ // Arguments:
+ // input: vmField - pointer to the field descriptor for the static field
+ // vmAppDomain - AppDomain to which the static field belongs. This must
+ // NOT be NULL
+ // Return Value: The target address of the field if the field is allocated.
+ // NULL if the field storage is not yet allocated.
+ //
+ // Note:
+ // Static field storage may not exist yet, so this may commonly return NULL.
+ // This is an inspection only method and can not allocate the static storage.
+ // Field storage is not constant once allocated so this value can not be cached
+ // across a Continue
+
+ virtual
+ CORDB_ADDRESS GetCollectibleTypeStaticAddress(VMPTR_FieldDesc vmField,
+ VMPTR_AppDomain vmAppDomain) = 0;
+
+ // Get information about a field added with Edit And Continue.
+ // Arguments:
+ // intput: pEnCFieldInfo - information about the EnC added field including:
+ // object to which it belongs (if this is null the field is static)
+ // the field token
+ // the class token for the class to which the field was added
+ // the offset to the fields
+ // the domain file
+ // an indication of the type: whether it's a class or value type
+ // output: pFieldData - information about the EnC added field
+ // pfStatic - flag to indicate whether the field is static
+ virtual
+ void GetEnCHangingFieldInfo(const EnCHangingFieldInfo * pEnCFieldInfo,
+ OUT FieldData * pFieldData,
+ OUT BOOL * pfStatic) = 0;
+
+
+ // GetTypeHandleParams gets the necessary data for a type handle, i.e. its
+ // type parameters, e.g. "String" and "List<int>" from the type handle
+ // for "Dict<String,List<int>>", and sends it back to the right side.
+ // Arguments:
+ // input: vmAppDomain - app domain to which the type belongs
+ // vmTypeHandle - type handle for the type
+ // output: pParams - list of instances of DebuggerIPCE_ExpandedTypeData,
+ // one for each type parameter. These will be used on the
+ // RS to build up an instantiation which will allow
+ // building an instance of CordbType for the top-level
+ // type. The memory for this list is allocated on the dbi
+ // heap in this function.
+ // This will not fail except for OOM
+
+ virtual
+ void GetTypeHandleParams(VMPTR_AppDomain vmAppDomain,
+ VMPTR_TypeHandle vmTypeHandle,
+ OUT TypeParamsList * pParams) = 0;
+
+ // GetSimpleType
+ // gets the metadata token and domain file corresponding to a simple type
+ // Arguments:
+ // input: vmAppDomain - Appdomain in which simpleType resides
+ // simpleType - CorElementType value corresponding to a simple type
+ // output: pMetadataToken - the metadata token corresponding to simpleType,
+ // in the scope of vmDomainFile.
+ // vmDomainFile - the domainFile for simpleType
+ // Notes:
+ // This is inspection-only. If the type is not yet loaded, it will throw CORDBG_E_CLASS_NOT_LOADED.
+ // It will not try to load a type.
+ // If the type has been loaded, vmDomainFile will be non-null unless the target is somehow corrupted.
+ // In that case, we will throw CORDBG_E_TARGET_INCONSISTENT.
+
+ virtual
+ void GetSimpleType(VMPTR_AppDomain vmAppDomain,
+ CorElementType simpleType,
+ OUT mdTypeDef * pMetadataToken,
+ OUT VMPTR_Module * pVmModule,
+ OUT VMPTR_DomainFile * pVmDomainFile) = 0;
+
+ // for the specified object returns TRUE if the object derives from System.Exception
+ virtual
+ BOOL IsExceptionObject(VMPTR_Object vmObject) = 0;
+
+ // gets the list of raw stack frames for the specified exception object
+ virtual
+ void GetStackFramesFromException(VMPTR_Object vmObject, DacDbiArrayList<DacExceptionCallStackData>& dacStackFrames) = 0;
+
+ // Returns true if the argument is a runtime callable wrapper
+ virtual
+ BOOL IsRcw(VMPTR_Object vmObject) = 0;
+
+ // retrieves the list of COM interfaces implemented by vmObject, as it is known at
+ // the time of the call (the list may change as new interface types become available
+ // in the runtime)
+ virtual
+ void GetRcwCachedInterfaceTypes(
+ VMPTR_Object vmObject,
+ VMPTR_AppDomain vmAppDomain,
+ BOOL bIInspectableOnly,
+ OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pDacInterfaces) = 0;
+
+ // retrieves the list of interfaces pointers implemented by vmObject, as it is known at
+ // the time of the call (the list may change as new interface types become available
+ // in the runtime)
+ virtual
+ void GetRcwCachedInterfacePointers(
+ VMPTR_Object vmObject,
+ BOOL bIInspectableOnly,
+ OUT DacDbiArrayList<CORDB_ADDRESS> * pDacItfPtrs) = 0;
+
+ // retrieves a list of interface types corresponding to the passed in
+ // list of IIDs. the interface types are retrieved from an app domain
+ // IID / Type cache, that is updated as new types are loaded. will
+ // have NULL entries corresponding to unknown IIDs in "iids"
+ virtual
+ void GetCachedWinRTTypesForIIDs(
+ VMPTR_AppDomain vmAppDomain,
+ DacDbiArrayList<GUID> & iids,
+ OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pTypes) = 0;
+
+ // retrieves the whole app domain cache of IID / Type mappings.
+ virtual
+ void GetCachedWinRTTypes(
+ VMPTR_AppDomain vmAppDomain,
+ OUT DacDbiArrayList<GUID> * piids,
+ OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pTypes) = 0;
+
+
+ // ----------------------------------------------------------------------------
+ // functions to get information about reference/handle referents for ICDValue
+ // ----------------------------------------------------------------------------
+
+ // Get object information for a TypedByRef object. Initializes the objRef and typedByRefType fields of
+ // pObjectData (type info for the referent).
+ // Arguments:
+ // input: pTypedByRef - pointer to a TypedByRef struct
+ // vmAppDomain - AppDomain for the type of the object referenced
+ // output: pObjectData - information about the object referenced by pTypedByRef
+ // Note: Throws
+ virtual
+ void GetTypedByRefInfo(CORDB_ADDRESS pTypedByRef,
+ VMPTR_AppDomain vmAppDomain,
+ DebuggerIPCE_ObjectData * pObjectData) = 0;
+
+ // Get the string length and offset to string base for a string object
+ // Arguments:
+ // input: objPtr - address of a string object
+ // output: pObjectData - fills in the string fields stringInfo.offsetToStringBase and
+ // stringInfo.length
+ // Note: throws
+ virtual
+ void GetStringData(CORDB_ADDRESS objectAddress, DebuggerIPCE_ObjectData * pObjectData) = 0;
+
+ // Get information for an array type referent of an objRef, including rank, upper and lower bounds,
+ // element size and type, and the number of elements.
+ // Arguments:
+ // input: objectAddress - the address of an array object
+ // output: pObjectData - fills in the array-related fields:
+ // arrayInfo.offsetToArrayBase,
+ // arrayInfo.offsetToLowerBounds,
+ // arrayInfo.offsetToUpperBounds,
+ // arrayInfo.componentCount,
+ // arrayInfo.rank,
+ // arrayInfo.elementSize,
+ // Note: throws
+ virtual
+ void GetArrayData(CORDB_ADDRESS objectAddress, DebuggerIPCE_ObjectData * pObjectData) = 0;
+
+ // Get information about an object for which we have a reference, including the object size and
+ // type information.
+ // Arguments:
+ // input: objectAddress - address of the object for which we want information
+ // type - the basic type of the object (we may find more specific type
+ // information for the object)
+ // vmAppDomain - the appdomain to which the object belong
+ // output: pObjectData - fills in the size and type information fields
+ // Note: throws
+ virtual
+ void GetBasicObjectInfo(CORDB_ADDRESS objectAddress,
+ CorElementType type,
+ VMPTR_AppDomain vmAppDomain,
+ DebuggerIPCE_ObjectData * pObjectData) = 0;
+
+ // --------------------------------------------------------------------------------------------
+#ifdef TEST_DATA_CONSISTENCY
+ // Determine whether a crst is held by the left side. When the DAC is executing VM code that takes a
+ // lock, we want to know whether the LS already holds that lock. If it does, we will assume the locked
+ // data is in an inconsistent state and will throw an exception, rather than relying on this data. This
+ // function is part of a self-test that will ensure we are correctly detecting when the LS holds a lock
+ // on data the RS is trying to inspect.
+ // Argument:
+ // input: vmCrst - the lock to test
+ // output: none
+ // Notes:
+ // Throws
+ // For this code to run, the environment variable TestDataConsistency must be set to 1.
+ virtual
+ void TestCrst(VMPTR_Crst vmCrst) = 0;
+
+ // Determine whether a crst is held by the left side. When the DAC is executing VM code that takes a
+ // lock, we want to know whether the LS already holds that lock. If it does, we will assume the locked
+ // data is in an inconsistent state and will throw an exception, rather than relying on this data. This
+ // function is part of a self-test that will ensure we are correctly detecting when the LS holds a lock
+ // on data the RS is trying to inspect.
+ // Argument:
+ // input: vmRWLock - the lock to test
+ // output: none
+ // Notes:
+ // Throws
+ // For this code to run, the environment variable TestDataConsistency must be set to 1.
+
+ virtual
+ void TestRWLock(VMPTR_SimpleRWLock vmRWLock) = 0;
+#endif
+ // --------------------------------------------------------------------------------------------
+ // Get the address of the Debugger control block on the helper thread. The debugger control block
+ // contains information about the status of the debugger, handles to various events and space to hold
+ // information sent back and forth between the debugger and the debuggee's helper thread.
+ // Arguments: none
+ // Return Value: The remote address of the Debugger control block allocated on the helper thread
+ // if it has been successfully allocated or NULL otherwise.
+ virtual
+ CORDB_ADDRESS GetDebuggerControlBlockAddress() = 0;
+
+ // Creates a VMPTR of an Object. The Object is found by dereferencing ptr
+ // as though it is a target address to an OBJECTREF. This is similar to
+ // GetObject with another level of indirection.
+ //
+ // Arguments:
+ // ptr - A target address pointing to an OBJECTREF
+ //
+ // Return Value:
+ // A VMPTR to the Object which ptr points to
+ //
+ // Notes:
+ // The VMPTR this produces can be deconstructed by GetObjectContents.
+ // This function will throw if given a NULL or otherwise invalid pointer,
+ // but if given a valid address to an invalid pointer, it will produce
+ // a VMPTR_Object which points to invalid memory.
+ virtual
+ VMPTR_Object GetObjectFromRefPtr(CORDB_ADDRESS ptr) = 0;
+
+ // Creates a VMPTR of an Object. The Object is assumed to be at the target
+ // address supplied by ptr
+ //
+ // Arguments:
+ // ptr - A target address to an Object
+ //
+ // Return Value:
+ // A VMPTR to the Object which was at ptr
+ //
+ // Notes:
+ // The VMPTR this produces can be deconstructed by GetObjectContents.
+ // This will produce a VMPTR_Object regardless of whether the pointer is
+ // valid or not.
+ virtual
+ VMPTR_Object GetObject(CORDB_ADDRESS ptr) = 0;
+
+ // Sets state in the native binder.
+ //
+ // Arguments:
+ // ePolicy - the NGEN policy to change
+ //
+ // Return Value:
+ // HRESULT indicating if the state was successfully updated
+ //
+ virtual
+ HRESULT EnableNGENPolicy(CorDebugNGENPolicy ePolicy) = 0;
+
+ // Sets the NGEN compiler flags. This restricts NGEN to only use images with certain
+ // types of pregenerated code. With respect to debugging this is used to specify that
+ // the NGEN image must be debuggable aka non-optimized code. Note that these flags
+ // are merged with other sources of configuration so it is possible that the final
+ // result retrieved from GetDesiredNGENCompilerFlags does not match what was specfied
+ // in this call.
+ //
+ // If an NGEN image of the appropriate type isn't available then one of two things happens:
+ // a) the NGEN image isn't loaded and CLR loads the MSIL image instead
+ // b) the NGEN image is loaded, but we don't use the pregenerated code it contains
+ // and instead use only the MSIL and metadata
+ //
+ // This function is only legal to call at app startup before any decisions have been
+ // made about NGEN image loading. Once we begin loading this configuration is immutable.
+ //
+ //
+ // Arguments:
+ // dwFlags - the new NGEN compiler flags that should go into effect
+ //
+ // Return Value:
+ // HRESULT indicating if the state was successfully updated. On error the
+ // current flags in effect will not have changed.
+ //
+ virtual
+ HRESULT SetNGENCompilerFlags(DWORD dwFlags) = 0;
+
+ // Gets the NGEN compiler flags currently in effect. This accounts for settings that
+ // were caused by SetDesiredNGENCompilerFlags as well as other configuration sources.
+ // See SetDesiredNGENCompilerFlags for more info
+ //
+ // Arguments:
+ // pdwFlags - the NGEN compiler flags currently in effect
+ //
+ // Return Value:
+ // HRESULT indicating if the state was successfully retrieved.
+ //
+ virtual
+ HRESULT GetNGENCompilerFlags(DWORD *pdwFlags) = 0;
+
+ // Create a VMPTR_OBJECTHANDLE from a CORDB_ADDRESS pointing to an object handle
+ //
+ // Arguments:
+ // handle: target address of a GC handle
+ //
+ // ReturnValue:
+ // returns a VMPTR_OBJECTHANDLE with the handle as the m_addr field
+ //
+ // Notes:
+ // This will produce a VMPTR_OBJECTHANDLE regardless of whether handle is
+ // valid.
+ // Ideally we'd be using only strongly-typed variables on the RS, and then this would be unnecessary
+ virtual
+ VMPTR_OBJECTHANDLE GetVmObjectHandle(CORDB_ADDRESS handleAddress) = 0;
+
+ // Validate that the VMPTR_OBJECTHANDLE refers to a legitimate managed object
+ //
+ // Arguments:
+ // handle: the GC handle to be validated
+ //
+ // Return value:
+ // TRUE if the object appears to be valid (its a heuristic), FALSE if it definately is not valid
+ //
+ virtual
+ BOOL IsVmObjectHandleValid(VMPTR_OBJECTHANDLE vmHandle) = 0;
+
+ // indicates if the specified module is a WinRT module
+ //
+ // Arguments:
+ // vmModule: the module to check
+ // isWinRT: out parameter indicating state of module
+ //
+ // Return value:
+ // S_OK indicating that the operation succeeded
+ //
+ virtual
+ HRESULT IsWinRTModule(VMPTR_Module vmModule, BOOL& isWinRT) = 0;
+
+ // Determines the app domain id for the object refered to by a given VMPTR_OBJECTHANDLE
+ //
+ // Arguments:
+ // handle: the GC handle which refers to the object of interest
+ //
+ // Return value:
+ // The app domain id of the object of interest
+ //
+ // This may throw if the object handle is corrupt (it doesn't refer to a managed object)
+ virtual
+ ULONG GetAppDomainIdFromVmObjectHandle(VMPTR_OBJECTHANDLE vmHandle) = 0;
+
+
+ // Get the target address from a VMPTR_OBJECTHANDLE, i.e., the handle address
+ // Arguments:
+ // vmHandle - (input) the VMPTR_OBJECTHANDLE from which we need the target address
+ // Return value: the target address from the VMPTR_OBJECTHANDLE
+ //
+ virtual
+ CORDB_ADDRESS GetHandleAddressFromVmHandle(VMPTR_OBJECTHANDLE vmHandle) = 0;
+
+ // Given a VMPTR to an Object return the target address
+ //
+ // Arguments:
+ // obj - the Object VMPTR to get the address from
+ //
+ // Return Value:
+ // Return the target address which obj is using
+ //
+ // Notes:
+ // The VMPTR this consumes can be reconstructed using GetObject and
+ // providing the address stored in the returned TargetBuffer. This has
+ // undefined behavior for invalid VMPTR_Objects.
+
+ virtual
+ TargetBuffer GetObjectContents(VMPTR_Object obj) = 0;
+
+ // The callback used to enumerate blocking objects
+ typedef void (*FP_BLOCKINGOBJECT_ENUMERATION_CALLBACK)(DacBlockingObject blockingObject,
+ CALLBACK_DATA pUserData);
+
+ //
+ // Enumerate all monitors blocking a thread
+ //
+ // Arguments:
+ // vmThread - the thread to get monitor data for
+ // fpCallback - callback to invoke on the blocking data for each monitor
+ // pUserData - user data to supply for each callback.
+ //
+ // Return Value:
+ // Returns on success. Throws on error.
+ //
+ //
+ virtual
+ void EnumerateBlockingObjects(VMPTR_Thread vmThread,
+ FP_BLOCKINGOBJECT_ENUMERATION_CALLBACK fpCallback,
+ CALLBACK_DATA pUserData) = 0;
+
+
+
+ //
+ // Returns the thread which owns the monitor lock on an object and the acquisition
+ // count
+ //
+ // Arguments:
+ // vmObject - The object to check for ownership
+
+ //
+ // Return Value:
+ // Throws on error. Inside the structure we have:
+ // pVmThread - the owning or thread or VMPTR_Thread::NullPtr() if unowned
+ // pAcquisitionCount - the number of times the lock would need to be released in
+ // order for it to be unowned
+ //
+ virtual
+ MonitorLockInfo GetThreadOwningMonitorLock(VMPTR_Object vmObject) = 0;
+
+ //
+ // Enumerate all threads waiting on the monitor event for an object
+ //
+ // Arguments:
+ // vmObject - the object whose monitor event we are interested in
+ // fpCallback - callback to invoke on each thread in the queue
+ // pUserData - user data to supply for each callback.
+ //
+ // Return Value:
+ // Returns on success. Throws on error.
+ //
+ //
+ virtual
+ void EnumerateMonitorEventWaitList(VMPTR_Object vmObject,
+ FP_THREAD_ENUMERATION_CALLBACK fpCallback,
+ CALLBACK_DATA pUserData) = 0;
+
+ //
+ // Returns the managed debugging flags for the process (a combination
+ // of the CLR_DEBUGGING_PROCESS_FLAGS flags). This function specifies,
+ // beyond whether or not a managed debug event is pending, also if the
+ // event (if one exists) is caused by a Debugger.Launch(). This is
+ // important b/c Debugger.Launch calls should *NOT* cause the debugger
+ // to terminate the process when the attach is canceled.
+ virtual
+ CLR_DEBUGGING_PROCESS_FLAGS GetAttachStateFlags() = 0;
+
+ virtual
+ bool GetMetaDataFileInfoFromPEFile(VMPTR_PEFile vmPEFile,
+ DWORD & dwTimeStamp,
+ DWORD & dwImageSize,
+ bool & isNGEN,
+ IStringHolder* pStrFilename) = 0;
+
+ virtual
+ bool GetILImageInfoFromNgenPEFile(VMPTR_PEFile vmPEFile,
+ DWORD & dwTimeStamp,
+ DWORD & dwSize,
+ IStringHolder* pStrFilename) = 0;
+
+
+ virtual
+ bool IsThreadSuspendedOrHijacked(VMPTR_Thread vmThread) = 0;
+
+
+ typedef void* * HeapWalkHandle;
+
+ // Returns true if it is safe to walk the heap. If this function returns false,
+ // you could still create a heap walk and attempt to walk it, but there's no
+ // telling how much of the heap will be available.
+ virtual
+ bool AreGCStructuresValid() = 0;
+
+ // Creates a HeapWalkHandle which can be used to walk the managed heap with the
+ // WalkHeap function. Note if this function completes successfully you will need
+ // to delete the handle by passing it into DeleteHeapWalk.
+ //
+ // Arguments:
+ // pHandle - the location to store the heap walk handle in
+ //
+ // Returns:
+ // S_OK on success, an error code on failure.
+ virtual
+ HRESULT CreateHeapWalk(OUT HeapWalkHandle * pHandle) = 0;
+
+
+ // Deletes the give HeapWalkHandle. Note you must call this function if
+ // CreateHeapWalk returns success.
+ virtual
+ void DeleteHeapWalk(HeapWalkHandle handle) = 0;
+
+ // Walks the heap using the given heap walk handle, enumerating objects
+ // on the managed heap. Note that walking the heap requires that the GC
+ // data structures be in a valid state, which you can find by calling
+ // AreGCStructuresValid.
+ //
+ // Arguments:
+ // handle - a HeapWalkHandle obtained from CreateHeapWalk
+ // count - the number of object addresses to obtain; pValues must
+ // be at least as large as count
+ // objects - the location to stuff the object addresses found during
+ // the heap walk; this array should be at least "count" in
+ // length; this field must not be null
+ // pFetched - a location to store the actual number of values filled
+ // into pValues; this field must not be null
+ //
+ // Returns:
+ // S_OK on success, a failure HRESULT otherwise.
+ //
+ // Note:
+ // You should iteratively call WalkHeap requesting more values until
+ // *pFetched != count.. This signifies that we have reached the end
+ // of the heap walk.
+ virtual
+ HRESULT WalkHeap(HeapWalkHandle handle,
+ ULONG count,
+ OUT COR_HEAPOBJECT * objects,
+ OUT ULONG * pFetched) = 0;
+
+ virtual
+ HRESULT GetHeapSegments(OUT DacDbiArrayList<COR_SEGMENT> * pSegments) = 0;
+
+ virtual
+ bool IsValidObject(CORDB_ADDRESS obj) = 0;
+
+ virtual
+ bool GetAppDomainForObject(CORDB_ADDRESS obj, OUT VMPTR_AppDomain * pApp,
+ OUT VMPTR_Module * pModule,
+ OUT VMPTR_DomainFile * pDomainFile) = 0;
+
+
+ // Reference Walking.
+
+ // Creates a reference walk.
+ // Parameters:
+ // pHandle - out - the reference walk handle to create
+ // walkStacks - in - whether or not to report stack references
+ // walkFQ - in - whether or not to report references from the finalizer queue
+ // handleWalkMask - in - the types of handles report (see CorGCReferenceType, cordebug.idl)
+ // Returns:
+ // An HRESULT indicating whether it succeded or failed.
+ // Exceptions:
+ // Does not throw, but does not catch exceptions either.
+ virtual
+ HRESULT CreateRefWalk(OUT RefWalkHandle * pHandle, BOOL walkStacks, BOOL walkFQ, UINT32 handleWalkMask) = 0;
+
+ // Deletes a reference walk.
+ // Parameters:
+ // handle - in - the handle of the reference walk to delete
+ // Excecptions:
+ // Does not throw, but does not catch exceptions either.
+ virtual
+ void DeleteRefWalk(RefWalkHandle handle) = 0;
+
+ // Enumerates GC references in the process based on the parameters passed to CreateRefWalk.
+ // Parameters:
+ // handle - in - the RefWalkHandle to enumerate
+ // count - in - the capacity of "refs"
+ // refs - in/out - an array to write the references to
+ // pFetched - out - the number of references written
+ virtual
+ HRESULT WalkRefs(RefWalkHandle handle, ULONG count, OUT DacGcReference * refs, OUT ULONG * pFetched) = 0;
+
+ virtual
+ HRESULT GetTypeID(CORDB_ADDRESS obj, COR_TYPEID * pType) = 0;
+
+ virtual
+ HRESULT GetObjectFields(COR_TYPEID id, ULONG32 celt, OUT COR_FIELD * layout, OUT ULONG32 * pceltFetched) = 0;
+
+ virtual
+ HRESULT GetTypeLayout(COR_TYPEID id, COR_TYPE_LAYOUT * pLayout) = 0;
+
+ virtual
+ HRESULT GetArrayLayout(COR_TYPEID id, COR_ARRAY_LAYOUT * pLayout) = 0;
+
+ virtual
+ void GetGCHeapInformation(OUT COR_HEAPINFO * pHeapInfo) = 0;
+
+ // If a PEFile has an RW capable IMDInternalImport, this returns the address of the MDInternalRW
+ // object which implements it.
+ //
+ //
+ // Arguments:
+ // vmPEFile - target PEFile to get metadata MDInternalRW for.
+ // pAddrMDInternalRW - If a PEFile has an RW capable IMDInternalImport, this will be set to the address
+ // of the MDInternalRW object which implements it. Otherwise it will be NULL.
+ //
+ virtual
+ HRESULT GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TADDR* pAddrMDInternalRW) = 0;
+
+ // Retrieves the active ReJitInfo for a given module/methodDef, if it exists.
+ // Active is defined as after GetReJitParameters returns from the profiler dll and
+ // no call to Revert has completed yet.
+ //
+ //
+ // Arguments:
+ // vmModule - The module to search in
+ // methodTk - The methodDef token indicates the method within the module to check
+ // pReJitInfo - [out] The RejitInfo request, if any, that is active on this method. If no request
+ // is active this will be pReJitInfo->IsNull() == TRUE.
+ //
+ // Returns:
+ // S_OK regardless of whether a rejit request is active or not, as long as the answer is certain
+ // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible
+ //
+ virtual
+ HRESULT GetReJitInfo(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ReJitInfo* pReJitInfo) = 0;
+
+ // Retrieves the active ReJitInfo for a given MethodDesc/code address, if it exists.
+ // Active is defined as after GetReJitParameters returns from the profiler dll and
+ // no call to Revert has completed yet.
+ //
+ //
+ // Arguments:
+ // vmMethod - The method to look for
+ // codeStartAddress - The code start address disambiguates between multiple rejitted instances
+ // of the method.
+ // pReJitInfo - [out] The RejitInfo request, if any, that is active on this method. If no request
+ // is active this will be pReJitInfo->IsNull() == TRUE.
+ //
+ // Returns:
+ // S_OK regardless of whether a rejit request is active or not, as long as the answer is certain
+ // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible
+ //
+ virtual
+ HRESULT GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_ReJitInfo* pReJitInfo) = 0;
+
+
+ // Retrieves the SharedReJitInfo for a given ReJitInfo.
+ //
+ //
+ // Arguments:
+ // vmReJitInfo - The ReJitInfo to inspect
+ // pSharedReJitInfo - [out] The SharedReJitInfo that is pointed to by vmReJitInfo.
+ //
+ // Returns:
+ // S_OK if no error
+ // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible
+ //
+ virtual
+ HRESULT GetSharedReJitInfo(VMPTR_ReJitInfo vmReJitInfo, VMPTR_SharedReJitInfo* pSharedReJitInfo) = 0;
+
+ // Retrieves useful data from a SharedReJitInfo such as IL code and IL mapping.
+ //
+ //
+ // Arguments:
+ // sharedReJitInfo - The SharedReJitInfo to inspect
+ // pData - [out] Various properties of the SharedReJitInfo such as IL code and IL mapping.
+ //
+ // Returns:
+ // S_OK if no error
+ // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible
+ //
+ virtual
+ HRESULT GetSharedReJitInfoData(VMPTR_SharedReJitInfo sharedReJitInfo, DacSharedReJitInfo* pData) = 0;
+
+ // Retrieves a bit field indicating which defines were in use when clr was built. This only includes
+ // defines that are specified in the Debugger::_Target_Defines enumeration, which is a small subset of
+ // all defines.
+ //
+ //
+ // Arguments:
+ // pDefines - [out] The set of defines clr.dll was built with. Bit offsets are encoded using the
+ // enumeration Debugger::_Target_Defines
+ //
+ // Returns:
+ // S_OK if no error
+ // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible
+ //
+ virtual
+ HRESULT GetDefinesBitField(ULONG32 *pDefines) = 0;
+
+ // Retrieves a version number indicating the shape of the data structures used in the Metadata implementation
+ // inside clr.dll. This number changes anytime a datatype layout changes so that they can be correctly
+ // deserialized from out of process
+ //
+ //
+ // Arguments:
+ // pMDStructuresVersion - [out] The layout version number for metadata data structures. See
+ // Debugger::Debugger() in Debug\ee\Debugger.cpp for a description of the options.
+ //
+ // Returns:
+ // S_OK if no error
+ // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible
+ //
+ virtual
+ HRESULT GetMDStructuresVersion(ULONG32* pMDStructuresVersion) = 0;
+
+ // The following tag tells the DD-marshalling tool to stop scanning.
+ // END_MARSHAL
+
+ //-----------------------------------------------------------------------------
+ // Utility interface used for passing strings out of these APIs. The caller
+ // provides an implementation of this that uses whatever memory allocation
+ // strategy it desires, and IDacDbiInterface APIs will call AssignCopy in order
+ // to pass back the contents of strings.
+ //
+ // This permits the client and implementation of IDacDbiInterface to be in
+ // different DLLs with their own heap allocation mechanism, while avoiding
+ // the ugly and verbose 2-call C-style string passing API pattern.
+ //-----------------------------------------------------------------------------
+ class IStringHolder
+ {
+ public:
+ //
+ // Store a copy of of the provided string.
+ //
+ // Arguments:
+ // psz - The null-terminated unicode string to copy.
+ //
+ // Return Value:
+ // S_OK on success, typical HRESULT return values on failure.
+ //
+ // Notes:
+ // The underlying object is responsible for allocating and freeing the
+ // memory for this copy. The object must not store the value of psz,
+ // it is no longer valid after this call returns.
+ //
+ virtual
+ HRESULT AssignCopy(const WCHAR * psz) = 0;
+ };
+
+
+ //-----------------------------------------------------------------------------
+ // Interface for allocations
+ // This lets DD allocate buffers to pass back to DBI; and thus avoids
+ // the common 2-step (query size/allocate/query data) pattern.
+ //
+ // Note that mscordacwks.dll and clients cannot share the same heap allocator,
+ // DAC statically links the CRT to avoid run-time dependencies on non-OS libraries.
+ //-----------------------------------------------------------------------------
+ class IAllocator
+ {
+ public:
+ // Allocate
+ // Expected to throw on error.
+ virtual
+ void * Alloc(SIZE_T lenBytes) = 0;
+
+ // Free. This shouldn't throw.
+ virtual
+ void Free(void * p) = 0;
+ };
+
+
+ //-----------------------------------------------------------------------------
+ // Callback interface to provide Metadata lookup.
+ //-----------------------------------------------------------------------------
+ class IMetaDataLookup
+ {
+ public:
+ //
+ // Lookup a metadata importer via PEFile.
+ //
+ // Returns:
+ // A IMDInternalImport used by dac-ized VM code. The object is NOT addref-ed. See lifespan notes below.
+ // Returns NULL if no importer is available.
+ // Throws on exceptional circumstances (eg, detects the debuggee is corrupted).
+ //
+ // Notes:
+ // IMDInternalImport is a property of PEFile. The DAC-ized code uses it as a weak reference,
+ // and so we avoid doing an AddRef() here because that would mean we need to add Release() calls
+ // in DAC-only paths.
+ // The metadata importers are not DAC-ized, and thus we have a local copy in the host.
+ // If it was dac-ized, then DAC would get the importer just like any other field.
+ //
+ // lifespan of returned object:
+ // - DBI owns the metadata importers.
+ // - DBI must not free the importer without calling Flush() on DAC first.
+ // - DAC will only invoke this when in a DD primitive, which was in turn invoked by DBI.
+ // - For performance reasons, we want to allow DAC to cache this between Flush() calls.
+ // - If DAC caches the importer, it will only use it when DBI invokes a DD primitive.
+ // - the reference count of the returned object is not adjusted.
+ //
+ virtual
+ IMDInternalImport * LookupMetaData(VMPTR_PEFile addressPEFile, bool &isILMetaDataForNGENImage) = 0;
+ };
+
+}; // end IDacDbiInterface
+
+
+#endif // _DACDBI_INTERFACE_H_
diff --git a/src/debug/inc/dacdbistructures.h b/src/debug/inc/dacdbistructures.h
new file mode 100644
index 0000000000..d6f2c0b943
--- /dev/null
+++ b/src/debug/inc/dacdbistructures.h
@@ -0,0 +1,790 @@
+// 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.
+//*****************************************************************************
+// File: DacDbiStructures.h
+//
+
+//
+// Declarations and inline functions for data structures shared between by the
+// DAC/DBI interface functions and the right side.
+//
+// Note that for MAC these structures are marshalled between Windows and Mac
+// and so their layout and size must be identical in both builds. Use the
+// MSLAYOUT macro on every structure to avoid compiler packing differences.
+//
+//*****************************************************************************
+
+#ifndef DACDBISTRUCTURES_H_
+#define DACDBISTRUCTURES_H_
+
+#include "./common.h"
+
+//-------------------------------------------------------------------------------
+// classes shared by the DAC/DBI interface functions and the right side
+//-------------------------------------------------------------------------------
+
+// DacDbiArrayList encapsulates an array and the number of elements in the array.
+// Notes:
+// - storage is always on the DacDbi heap
+// - this class owns the memory. Its dtor will free.
+// - Operations that initialize list elements use the assignment
+// operator defined for type T. If T is a pointer type or has pointer
+// type components and no assignment operator override, this will make a shallow copy of
+// the element. If T has an assignment operator override that makes a deep copy of pointer
+// types, T must also have a destructor that will deallocate any memory allocated.
+// - this is NOT thread safe!!!
+// - the array elements are always mutable, but the number of elements is fixed between allocations
+// - you can gain access to the array using &(list[0]) but this is NOT safe if the array is empty. You
+// can call IsEmpty to determine if it is safe to access the array portion
+// This list is not designed to have unused elements at the end of the array (extra space) nor to be growable
+
+// usage examples:
+// typedef DacDbiArrayList<Bar> BarList; // handy typedef
+// void GetAListOfBars(BarList * pBarList)
+// {
+// DacDbiArrayList<Foo> fooList; // fooList is an empty array of objects of type Foo
+// int elementCount = GetNumberOfFoos();
+// Bar * pBars = new Bar[elementCount];
+//
+// fooList.Alloc(elementCount); // get space for the list of Foo instances
+// for (int i = 0; i < fooList.Count(); ++i)
+// {
+// fooList[i] = GetNextFoo(); // copy elements into the list
+// }
+// ConvertFoosToBars(pBars, &fooList); // always pass by reference
+// pBarList->Init(pBars, fooList.Count()); // initialize a list
+// }
+//
+// void ConvertFoosToBars(Bar * pBars, DacDbiArrayList<Foo> * pFooList)
+// {
+// for (int i = 0; i < pFooList->Count(); ++i)
+// {
+// if ((*pFooList)[i].IsBaz())
+// pBars [i] = ConvertBazToBar(&(*pFooList)[i]);
+// else pBars [i] = (*pFooList)[i].barPart;
+// }
+// }
+//
+template<class T>
+class MSLAYOUT DacDbiArrayList
+{
+public:
+ // construct an empty list
+ DacDbiArrayList();
+
+ // deep copy constructor
+ DacDbiArrayList(const T * list, int count);
+
+ // destructor--sets list to empty state
+ ~DacDbiArrayList();
+
+ // explicitly deallocate the list and set it back to the empty state
+ void Dealloc();
+
+ // allocate a list with space for nElements items
+ void Alloc(int nElements);
+
+ // allocate and initialize a DacDbiArrayList from an array of type T and a count
+ void Init(const T * list, int count);
+
+ // predicate to indicate if the list is empty
+ bool IsEmpty() { return m_nEntries == 0; }
+
+ // read-only element accessor
+ const T & operator [](int index) const;
+
+ // writeable element accessor
+ T & operator [](int index);
+
+
+ // returns the number of elements in the list
+ int Count() const;
+
+ // @dbgtodo Mac - cleaner way to expose this for serialization?
+ void PrepareForDeserialize()
+ {
+ m_pList = NULL;
+ }
+private:
+ // because these are private (and unimplemented), calls will generate a compiler (or linker) error.
+ // This prevents accidentally invoking the default (shallow) copy ctor or assignment operator.
+ // This prevents having multiple instances point to the same list memory (eg. due to passing by value),
+ // which would result in memory corruption when the first copy is destroyed and the list memory is deallocated.
+ DacDbiArrayList(const DacDbiArrayList<T> & sourceList);
+ T & operator = (const DacDbiArrayList<T> & rhs);
+
+// data members
+protected:
+ T * m_pList; // the list
+
+ // - the count is managed by the member functions and is not settable, so (m_pList == NULL) == (m_nEntries == 0)
+ // is always true.
+ int m_nEntries; // the number of items in the list
+
+};
+
+
+// Describes a buffer in the target
+struct MSLAYOUT TargetBuffer
+{
+ TargetBuffer();
+ TargetBuffer(CORDB_ADDRESS pBuffer, ULONG cbSizeInput);
+
+ // @dbgtodo : This ctor form confuses target and host address spaces. This should probably be PTR_VOID instead of void*
+ TargetBuffer(void * pBuffer, ULONG cbSizeInput);
+
+ //
+ // Helper methods
+ //
+
+ // Return a sub-buffer that's starts at byteOffset within this buffer and runs to the end.
+ TargetBuffer SubBuffer(ULONG byteOffset) const;
+
+ // Return a sub-buffer that starts at byteOffset within this buffer and is byteLength long.
+ TargetBuffer SubBuffer(ULONG byteOffset, ULONG byteLength) const;
+
+ // Returns true if the buffer length is 0.
+ bool IsEmpty() const;
+
+ // Sets address to NULL and size to 0
+ // IsEmpty() will be true after this.
+ void Clear();
+
+ // Initialize fields
+ void Init(CORDB_ADDRESS address, ULONG size);
+
+ // Target address of buffer
+ CORDB_ADDRESS pAddress;
+
+ // Size of buffer in bytes
+ ULONG cbSize;
+};
+
+//===================================================================================
+// Module properties, retrieved by DAC.
+// Describes a VMPTR_DomainFile representing a module.
+// In the VM, a raw Module may be domain neutral and shared by many appdomains.
+// Whereas a DomainFile is like a { AppDomain, Module} pair. DomainFile corresponds
+// much more to ICorDebugModule (which also has appdomain affinity).
+//===================================================================================
+struct MSLAYOUT DomainFileInfo
+{
+ // The appdomain that the DomainFile is associated with.
+ // Although VMPTR_Module may be shared across multiple domains, a DomainFile has appdomain affinity.
+ VMPTR_AppDomain vmAppDomain;
+
+ // The assembly this module belongs to. All modules live in an assembly.
+ VMPTR_DomainAssembly vmDomainAssembly;
+};
+
+struct MSLAYOUT ModuleInfo
+{
+ // The non-domain specific assembly which this module resides in.
+ VMPTR_Assembly vmAssembly;
+
+ // The PE Base address and size of the module. These may be 0 if there is no image
+ // (such as for a dynamic module that's not persisted to disk).
+ CORDB_ADDRESS pPEBaseAddress;
+
+ // The PEFile associated with the module. Every module (even non-file-based ones) has a PEFile.
+ // This is critical because DAC may ask for a metadata importer via PE-file.
+ // a PEFile may have 1 or more PEImage child objects (1 for IL, 1 for native image, etc)
+ VMPTR_PEFile vmPEFile;
+
+ // The PE Base address and size of the module. These may be 0 if there is no image
+ // (such as for a dynamic module that's not persisted to disk).
+ ULONG nPESize;
+
+ // Is this a dynamic (reflection.emit) module?
+ // This means that new classes can be added to the module; and so
+ // the module's metadata and symbols can be updated. Debugger will have to do extra work
+ // to keep up with the updates.
+ // Dynamic modules may be transient (entirely in-memory) or persisted to disk (have a file associated with them).
+ BOOL fIsDynamic;
+
+ // Is this an inmemory module?
+ // Assemblies can be instantiated purely in-memory from just a Byte[].
+ // This means the module (and pdb) are not in files, and thus the debugger
+ // needs to do extra work to retrieve them from the Target's memory.
+ BOOL fInMemory;
+};
+
+// the following two classes track native offsets for local variables and sequence
+// points. This information is initialized on demand.
+
+
+//===================================================================================
+// NativeVarData holds a list of structs that provide the following information for
+// each local variable and fixed argument in a function: the offsets between which the
+// variable or argument lives in a particular location, the location itself, and the
+// variable number (ID). This allows us to determine where a value is at any given IP.
+
+// Lifetime management of the list is the responsibility of the NativeVarData class.
+// Callers that allocate memory for a new list should NOT maintain a separate pointer
+// to the list.
+
+// The arguments we track are the "fixed" arguments, specifically, the explicit arguments
+// that appear in the source code and the "this" pointer for non-static methods.
+// Varargs and other implicit arguments, such as the generic handle are counted in
+// CordbJITILFrame::m_allArgsCount.
+
+// Although logically, we really don't differentiate between arguments and locals when
+// all we want to know is where to find a value, we need to have two
+// separate counts. The full explanation is in the comment in rsthread.cpp in
+// CordbJITILFrame::ILVariableToNative, but the short version is that it allows us to
+// compute the correct ID for a value.
+
+// m_fixedArgsCount, accessed through GetFixedArgCount, is the actual number of fixed
+// arguments.
+// m_allArgsCount, accessed through GetAllArgsCount, is the number of fixed args plus the
+// number of varargs.
+
+// The number of entries in m_offsetInfo, accessed through Count(), is NOT the
+// number of locals, nor the number of locals plus the number of arguments. It is the
+// number of entries in the list. Any particular value may have an arbitrary number of
+// entries, depending on how many different places it is stored during the execution of
+// the method. The list is not sorted, so searches for data within it must be linear.
+//===================================================================================
+class MSLAYOUT NativeVarData
+{
+public:
+ // constructor
+ NativeVarData();
+ // destructor
+ ~NativeVarData();
+
+
+ // initialize the list of native var information structures, including the starting address of the list
+ // (m_pOffsetInfo, the number of entries (m_count) and the number of fixed args (m_fixedArgsCount).
+ // NativeVarData will manage the lifetime of the allocated memory for the list, so the caller should not
+ // hold on to its address.
+ void InitVarDataList(ICorDebugInfo::NativeVarInfo * plistStart, int fixedArgCount, int entryCount);
+
+private:
+ // non-existent copy constructor to disable the (shallow) compiler-generated
+ // one. If you attempt to use this, you will get a compiler or linker error.
+ NativeVarData(const NativeVarData & rhs) {};
+
+ // non-existent assignment operator to disable the (shallow) compiler-generated
+ // one. If you attempt to use this, you will get a compiler or linker error.
+ NativeVarData & operator=(const NativeVarData & rhs);
+
+//----------------------------------------------------------------------------------
+// Accessor Functions
+//----------------------------------------------------------------------------------
+public:
+
+ // get the list of native offset info
+ const DacDbiArrayList<ICorDebugInfo::NativeVarInfo> * GetOffsetInfoList() const
+ {
+ _ASSERTE(m_fInitialized);
+ return &m_offsetInfo;
+ }
+
+ // get the number of explicit arguments for this function--this
+ // includes the fixed arguments for vararg methods, but not the variable ones
+ ULONG32 GetFixedArgCount()
+ {
+ _ASSERTE(IsInitialized());
+ // this count includes explicit arguments plus one for the "this" pointer
+ // but doesn't count varargs
+ return m_fixedArgsCount;
+ }
+
+ // get the number of all arguments, including varargs
+ ULONG32 GetAllArgsCount()
+ {
+ _ASSERTE(IsInitialized());
+ return m_allArgsCount;
+ }
+
+ // set the number of all arguments, including varargs
+ void SetAllArgsCount(ULONG32 count)
+ {
+ m_allArgsCount = count;
+ }
+
+ // determine whether we have successfully initialized this
+ BOOL IsInitialized()
+ {
+ return m_fInitialized == true;
+ }
+
+
+//----------------------------------------------------------------------------------
+// Data Members
+//----------------------------------------------------------------------------------
+
+// @dbgtodo Mac - making this public for serializing for remote DAC on mac. Need to make this private again.
+public:
+ // contains a list of structs providing information about the location of a local
+ // variable or argument between a pair of offsets and the number of entries in the list
+ DacDbiArrayList<ICorDebugInfo::NativeVarInfo> m_offsetInfo;
+
+ // number of fixed arguments to the function i.e., the explicit arguments and "this" pointer
+ ULONG32 m_fixedArgsCount;
+
+ // number of fixed arguments plus number of varargs
+ ULONG32 m_allArgsCount;
+
+ // indicates whether an attempt has been made toinitialize the var data already
+ bool m_fInitialized;
+}; // class NativeVarData
+
+//===================================================================================
+// SequencePoints holds a list of sequence points that map IL offsets to native offsets. In addition,
+// it keeps track of the number of entries in the list and whether the list is sorted.
+//===================================================================================
+class MSLAYOUT SequencePoints
+{
+public:
+ SequencePoints();
+
+ ~SequencePoints();
+
+ // Initialize the m_pMap data member to the address of an allocated chunk
+ // of memory (or to NULL if the count is zero). Set m_count as the
+ // number of entries in the map.
+ void InitSequencePoints(ULONG32 count);
+
+private:
+ // non-existent copy constructor to disable the (shallow) compiler-generated
+ // one. If you attempt to use this, you will get a compiler or linker error.
+ SequencePoints(const SequencePoints & rhs) {};
+
+ // non-existent assignment operator to disable the (shallow) compiler-generated
+ // one. If you attempt to use this, you will get a compiler or linker error.
+ SequencePoints & operator=(const SequencePoints & rhs);
+
+ //----------------------------------------------------------------------------------
+ // class MapSortILMap: A template class that will sort an array of DebuggerILToNativeMap.
+ // This class is intended to be instantiated on the stack / in temporary storage, and used
+ // to reorder the sequence map.
+ //----------------------------------------------------------------------------------
+ class MapSortILMap : public CQuickSort<DebuggerILToNativeMap>
+ {
+ public:
+ //Constructor
+ MapSortILMap(DebuggerILToNativeMap * map,
+ int count)
+ : CQuickSort<DebuggerILToNativeMap>(map, count) {}
+
+ // secondary key comparison--if two IL offsets are the same,
+ // we determine order based on native offset
+ int CompareInternal(DebuggerILToNativeMap * first,
+ DebuggerILToNativeMap * second);
+
+ //Comparison operator
+ int Compare(DebuggerILToNativeMap * first,
+ DebuggerILToNativeMap * second);
+ };
+
+//----------------------------------------------------------------------------------
+// Accessor Functions
+//----------------------------------------------------------------------------------
+public:
+ // @dbgtodo Microsoft inspection: It would be very nice not to need this at all. Ideally,
+ // it would be better to make ExportILToNativeMap expect a DacDbiArrayList instead of the
+ // array and size. At present, there's a call to ExportILToNativeMap in debugger.cpp where
+ // DacDbiArrayLists aren't available, so at present, we need to pass the array and size.
+ // We should be able to eliminate the debugger.cpp call when we get rid of in-proc
+ // inspection. At that point, we can delete this function too, as well as GetEntryCount.
+ // In the meantime, it would be great if no one else took a dependency on this.
+
+ // get value of m_pMap
+ DebuggerILToNativeMap * GetMapAddr()
+ {
+ // Please don't call this function
+ _ASSERTE(m_fInitialized);
+ return &(m_map[0]);
+ }
+
+ // get value of m_count
+ ULONG32 GetEntryCount()
+ {
+ _ASSERTE(m_fInitialized);
+ return m_mapCount;
+ }
+
+ ULONG32 GetCallsiteEntryCount()
+ {
+ _ASSERTE(m_fInitialized);
+ return m_map.Count() - m_mapCount; //m_map.Count();
+ }
+
+ DebuggerILToNativeMap * GetCallsiteMapAddr()
+ {
+ // Please don't call this function
+ _ASSERTE(m_fInitialized);
+
+ if (m_map.Count() == m_mapCount)
+ return NULL;
+
+ return &(m_map[m_mapCount]);
+ }
+
+
+
+ // determine whether we have initialized this
+ BOOL IsInitialized()
+ {
+ return m_fInitialized == true;
+ }
+
+ // Copy data from the VM map data to our own map structure and sort. The
+ // information comes to us in a data structure that differs slightly from the
+ // one we use out of process, so we have to copy it to the right-side struct.
+ void CopyAndSortSequencePoints(const ICorDebugInfo::OffsetMapping mapCopy[]);
+
+
+ // Set the IL offset of the last sequence point before the epilog.
+ // If a native offset maps to the epilog, we will return the this IL offset.
+ void SetLastILOffset(ULONG32 lastILOffset)
+ {
+ _ASSERTE(m_fInitialized);
+ m_lastILOffset = lastILOffset;
+ }
+
+ // Map the given native offset to IL offset. Also return the mapping type.
+ DWORD MapNativeOffsetToIL(DWORD dwNativeOffset,
+ CorDebugMappingResult *pMapType);
+
+//----------------------------------------------------------------------------------
+// Data Members
+//----------------------------------------------------------------------------------
+
+ // @dbgtodo Mac - making this public for serializing for remote DAC on mac. Need to make this private again.
+public:
+
+ // map of IL to native offsets for sequence points
+ DacDbiArrayList<DebuggerILToNativeMap> m_map;
+
+ //
+ ULONG32 m_mapCount;
+
+ // the IL offset of the last sequence point before the epilog
+ ULONG32 m_lastILOffset;
+ // indicates whether an attempt has been made to initialize the sequence points already
+ bool m_fInitialized;
+}; // class SequencePoints
+
+//----------------------------------------------------------------------------------
+// declarations needed for getting native code regions
+//----------------------------------------------------------------------------------
+
+// Code may be split into Hot & Cold regions, so we need an extra address & size.
+// The jitter doesn't do this optimization w/ debuggable code, so we'll
+// rarely see the cold region information as non-null values.
+
+// This enumeration provides symbolic indices into m_rgCodeRegions.
+typedef enum {kHot = 0, kCold, MAX_REGIONS} CodeBlobRegion;
+
+// This contains the information we need to initialize a CordbNativeCode object
+class MSLAYOUT NativeCodeFunctionData
+{
+public:
+ // set all fields to default values (NULL, FALSE, or zero as appropriate)
+ NativeCodeFunctionData();
+
+ // conversion constructor to convert from an instance of DebuggerIPCE_JITFUncData to an instance of
+ // NativeCodeFunctionData.
+ NativeCodeFunctionData(DebuggerIPCE_JITFuncData * source);
+
+ // The hot region start address could be NULL in the following circumstances:
+ // 1. We haven't yet tried to get the information
+ // 2. We tried to get the information, but the function hasn't been jitted yet
+ // 3. We tried to get the information, but the MethodDesc wasn't available yet (very early in
+ // module initialization), which implies that the code isn't available either.
+ // 4. We tried to get the information, but a method edit has reset the MethodDesc, but the
+ // method hasn't been jitted yet.
+ // In all cases, we can check the hot region start address to determine whether the rest of the
+ // the information is valid.
+ BOOL IsValid() { return (m_rgCodeRegions[kHot].pAddress != NULL); }
+ void Clear();
+
+ // data members
+ // start addresses and sizes of hot & cold regions
+ TargetBuffer m_rgCodeRegions[MAX_REGIONS];
+
+ // indicates whether the function is a generic function, or a method inside a generic class (or both).
+ BOOL isInstantiatedGeneric;
+
+ // MethodDesc for the function
+ VMPTR_MethodDesc vmNativeCodeMethodDescToken;
+
+ // EnC version number of the function
+ SIZE_T encVersion;
+};
+
+//----------------------------------------------------------------------------------
+// declarations needed for getting type information
+//----------------------------------------------------------------------------------
+
+// FieldData holds data for each field within a class or type. This data
+// is passed from the DAC to the DI in response to a request for class info.
+// This type is also used by CordbClass and CordbType to hold the list of fields for the
+// class.
+class MSLAYOUT FieldData
+{
+public:
+#ifndef RIGHT_SIDE_COMPILE
+ // initialize various fields of an instance of FieldData from information in a FieldDesc
+ void Initialize(BOOL fIsStatic, BOOL fIsPrimitive, mdFieldDef mdToken);
+#else
+ HRESULT GetFieldSignature(class CordbModule * pModule, /*OUT*/ SigParser * pSigParser);
+#endif
+
+ // clear various fields for a new instance of FieldData
+ void ClearFields();
+
+ // Make sure it's okay to get or set an instance field offset.
+ BOOL OkToGetOrSetInstanceOffset();
+
+ // Make sure it's okay to get or set a static field address.
+ BOOL OkToGetOrSetStaticAddress();
+
+ // If this is an instance field, store its offset
+ void SetInstanceOffset( SIZE_T offset );
+
+ // If this is a "normal" static, store its absolute address
+ void SetStaticAddress( TADDR addr );
+
+ // If this is an instance field, return its offset
+ // Note that this offset is allways a real offset (possibly larger than 22 bits), which isn't
+ // necessarily the same as the overloaded FieldDesc.dwOffset field which can have
+ // some special FIELD_OFFSET tokens.
+ SIZE_T GetInstanceOffset();
+
+ // If this is a "normal" static, get its absolute address
+ // TLS and context-specific statics are "special".
+ TADDR GetStaticAddress();
+
+//
+// Data members
+//
+ mdFieldDef m_fldMetadataToken;
+ // m_fFldStorageAvailable is true whenever the storage for this field is available.
+ // If this is a field that is newly added with EnC and hasn't had any storage
+ // allocated yet, then fldEnCAvailable will be false.
+ BOOL m_fFldStorageAvailable;
+
+ // Bits that specify what type of field this is
+ bool m_fFldIsStatic; // true if static field, false if instance field
+ bool m_fFldIsRVA; // true if static relative to module address
+ bool m_fFldIsTLS; // true if thread-specific static
+ bool m_fFldIsContextStatic; // true if context-specific static
+ bool m_fFldIsPrimitive; // Only true if this is a value type masquerading as a primitive.
+ bool m_fFldIsCollectibleStatic; // true if this is a static field on a collectible type
+
+private:
+ // The m_fldInstanceOffset and m_pFldStaticAddress are mutually exclusive. Only one is ever set at a time.
+ SIZE_T m_fldInstanceOffset; // The offset of a field within an object instance
+ // For EnC fields, this isn't actually within the object instance,
+ // but has been cooked to still be relative to the beginning of
+ // the object.
+ TADDR m_pFldStaticAddress; // The absolute target address of a static field
+
+ PCCOR_SIGNATURE m_fldSignatureCache; // This is passed across as null. It is a RS-only cache, and SHOULD
+ // NEVER BE ACCESSED DIRECTLY!
+ ULONG m_fldSignatureCacheSize; // This is passed across as 0. It is a RS-only cache, and SHOULD
+ // NEVER BE ACCESSED DIRECTLY!
+public:
+ VMPTR_FieldDesc m_vmFieldDesc;
+
+}; // class FieldData
+
+
+// ClassInfo holds information about a type (class or other structured type), including a list of its fields
+class MSLAYOUT ClassInfo
+{
+public:
+ ClassInfo();
+
+ ~ClassInfo();
+
+ void Clear();
+
+ // Size of object in bytes, for non-generic types. Note: this is NOT valid for constructed value types,
+ // e.g. value type Pair<DateTime,int>. Use CordbType::m_objectSize instead.
+ SIZE_T m_objectSize;
+
+ // list of structs containing information about all the fields in this Class, along with the number of entries
+ // in the list. Does not include inherited fields. DON'T KEEP POINTERS TO ELEMENTS OF m_fieldList AROUND!!
+ // This may be deleted if the class gets EnC'd.
+ DacDbiArrayList<FieldData> m_fieldList;
+}; // class ClassInfo
+
+// EnCHangingFieldInfo holds information describing a field added with Edit And Continue. This data
+// is passed from the DAC to the DI in response to a request for EnC field info.
+class MSLAYOUT EnCHangingFieldInfo
+{
+public:
+ // Init will initialize fields, taking into account whether the field is static or not.
+ void Init(VMPTR_Object pObject,
+ SIZE_T offset,
+ mdFieldDef fieldToken,
+ CorElementType elementType,
+ mdTypeDef metadataToken,
+ VMPTR_DomainFile vmDomainFile);
+
+ DebuggerIPCE_BasicTypeData GetObjectTypeData() const { return m_objectTypeData; };
+ mdFieldDef GetFieldToken() const { return m_fldToken; };
+ VMPTR_Object GetVmObject() const { return m_vmObject; };
+ SIZE_T GetOffsetToVars() const { return m_offsetToVars; };
+
+private:
+ DebuggerIPCE_BasicTypeData m_objectTypeData; // type data for the EnC field
+ VMPTR_Object m_vmObject; // object instance to which the field has been added--if the field is
+ // static, this will be NULL instead of pointing to an instance
+ SIZE_T m_offsetToVars; // offset to the beginning of variable storage in the object
+ mdFieldDef m_fldToken; // metadata token for the added field
+
+}; // EnCHangingFieldInfo
+
+// TypeHandleToExpandedTypeInfo returns different DebuggerIPCE_ExpandedTypeData objects
+// depending on whether the object value that the TypeData corresponds to is
+// boxed or not. Different parts of the API transfer objects in slightly different ways.
+// AllBoxed:
+// For GetAndSendObjectData all values are boxed,
+//
+// OnlyPrimitivesUnboxed:
+// When returning results from FuncEval only "true" structs
+// get boxed, i.e. primitives are unboxed.
+//
+// NoValueTypeBoxing:
+// TypeHandleToExpandedTypeInfo is also used to report type parameters,
+// and in this case none of the types are considered boxed (
+enum AreValueTypesBoxed { NoValueTypeBoxing, OnlyPrimitivesUnboxed, AllBoxed };
+
+// TypeRefData is used for resolving a type reference (see code:CordbModule::ResolveTypeRef and
+// code:DacDbiInterfaceImpl::ResolveTypeReference) to store relevant information about the type
+typedef struct MSLAYOUT
+{
+ // domain file for the type
+ VMPTR_DomainFile vmDomainFile;
+ // metadata token for the type. This may be a typeRef (for requests) or a typeDef (for responses).
+ mdToken typeToken;
+} TypeRefData;
+
+// @dbgtodo Microsoft inspection: get rid of IPCE type.
+// TypeInfoList encapsulates a list of type data instances and the length of the list.
+typedef DacDbiArrayList<DebuggerIPCE_TypeArgData> TypeInfoList;
+
+// ArgInfoList encapsulates a list of type data instances for arguments for a top-level
+// type and the length of the list.
+typedef DacDbiArrayList<DebuggerIPCE_BasicTypeData> ArgInfoList;
+
+// TypeParamsList encapsulate a list of type parameters and the length of the list
+typedef DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> TypeParamsList;
+
+// A struct for passing version information from DBI to DAC.
+// See code:CordbProcess::CordbProcess#DBIVersionChecking for more information.
+const DWORD kCurrentDacDbiProtocolBreakingChangeCounter = 1;
+
+struct DbiVersion
+{
+ DWORD m_dwFormat; // the format of this DbiVersion instance
+ DWORD m_dwDbiVersionMS; // version of the DBI DLL, in the convention used by VS_FIXEDFILEINFO
+ DWORD m_dwDbiVersionLS;
+ DWORD m_dwProtocolBreakingChangeCounter; // initially this was reserved and always set to 0
+ // Now we use it as a counter to explicitly introduce breaking changes
+ // between DBI and DAC when we have our IPC transport in the middle
+ // If DBI and DAC don't agree on the same value CheckDbiVersion will return CORDBG_E_INCOMPATIBLE_PROTOCOL
+ // Please document every time this value changes
+ // 0 - initial value
+ // 1 - Indicates that the protocol now supports the GetRemoteInterfaceHashAndTimestamp message
+ // The message must have ID 2, with signature:
+ // OUT DWORD & hash1, OUT DWORD & hash2, OUT DWORD & hash3, OUT DWORD & hash4, OUT DWORD & timestamp1, OUT DWORD & timestamp2
+ // The hash can be used as an indicator of many other breaking changes providing
+ // easier automated enforcement during development. It is NOT recommended to use
+ // the hash as a release versioning mechanism however.
+ DWORD m_dwReservedMustBeZero1; // reserved for future use
+};
+
+// The way in which a thread is blocking on an object
+enum DacBlockingReason
+{
+ DacBlockReason_MonitorCriticalSection,
+ DacBlockReason_MonitorEvent
+};
+
+// Information about an object which is blocking a managed thread
+struct DacBlockingObject
+{
+ VMPTR_Object vmBlockingObject;
+ VMPTR_AppDomain vmAppDomain;
+ DWORD dwTimeout;
+ DacBlockingReason blockingReason;
+};
+
+// Opaque user defined data used in callbacks
+typedef void* CALLBACK_DATA;
+
+struct MonitorLockInfo
+{
+ VMPTR_Thread lockOwner;
+ DWORD acquisitionCount;
+};
+
+struct MSLAYOUT DacGcReference
+{
+ VMPTR_AppDomain vmDomain; // The AppDomain of the handle/object, may be null.
+ union
+ {
+ CORDB_ADDRESS pObject; // A managed object, with the low bit set.
+ VMPTR_OBJECTHANDLE objHnd; // A reference to the object, valid if (pAddress & 1) == 0
+ };
+ DWORD dwType; // Where the root came from.
+
+ /*
+ DependentSource - for HandleDependent
+ RefCount - for HandleStrongRefCount
+ Size - for HandleSizedByref
+ */
+ UINT64 i64ExtraData;
+}; // struct DacGcReference
+
+struct MSLAYOUT DacExceptionCallStackData
+{
+ VMPTR_AppDomain vmAppDomain;
+ VMPTR_DomainFile vmDomainFile;
+ CORDB_ADDRESS ip;
+ mdMethodDef methodDef;
+ BOOL isLastForeignExceptionFrame;
+};
+
+// These represent the various states a SharedReJitInfo can be in.
+enum DacSharedReJitInfoState
+{
+ // The profiler has requested a ReJit, so we've allocated stuff, but we haven't
+ // called back to the profiler to get any info or indicate that the ReJit has
+ // started. (This Info can be 'reused' for a new ReJit if the
+ // profiler calls RequestReJit again before we transition to the next state.)
+ kStateRequested = 0x00000000,
+
+ // We have asked the profiler about this method via ICorProfilerFunctionControl,
+ // and have thus stored the IL and codegen flags the profiler specified. Can only
+ // transition to kStateReverted from this state.
+ kStateActive = 0x00000001,
+
+ // The methoddef has been reverted, but not freed yet. It (or its instantiations
+ // for generics) *MAY* still be active on the stack someplace or have outstanding
+ // memory references.
+ kStateReverted = 0x00000002,
+
+
+ kStateMask = 0x0000000F,
+};
+
+struct MSLAYOUT DacSharedReJitInfo
+{
+ DWORD m_state;
+ CORDB_ADDRESS m_pbIL;
+ DWORD m_dwCodegenFlags;
+ ULONG m_cInstrumentedMapEntries;
+ CORDB_ADDRESS m_rgInstrumentedMapEntries;
+};
+
+#include "dacdbistructures.inl"
+#endif // DACDBISTRUCTURES_H_
diff --git a/src/debug/inc/dacdbistructures.inl b/src/debug/inc/dacdbistructures.inl
new file mode 100644
index 0000000000..48749135f0
--- /dev/null
+++ b/src/debug/inc/dacdbistructures.inl
@@ -0,0 +1,732 @@
+// 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.
+//*****************************************************************************
+
+//
+// File: DacDbiInterface.inl
+//
+// Inline functions for DacDbiStructures.h
+//
+//*****************************************************************************
+
+#ifndef DACDBISTRUCTURES_INL_
+#define DACDBISTRUCTURES_INL_
+
+//-----------------------------------------------------------------------------------
+// DacDbiArrayList member function implementations
+//-----------------------------------------------------------------------------------
+
+// constructor--sets list to empty state
+// Arguments: none
+// Notes: this allocates no memory, so the list will not be ready to use
+template<class T>
+inline
+DacDbiArrayList<T>::DacDbiArrayList():
+ m_pList(NULL),
+ m_nEntries(0)
+ {
+ }
+
+// conversion constructor--takes a list of type T and a count and converts to a
+// DacDbiArrayList
+// Arguments:
+// input: list - a consecutive list (array) of elements of type T
+// count - the number of elements in list
+// Notes: - Allocates memory and copies the elements of list into "this"
+// - It is assumed that the list does NOT already have memory allocated; if it does,
+// calling Init will cause a leak.
+// - the element copy relies on the assignment operator for T
+// - may throw OOM
+template<class T>
+inline
+DacDbiArrayList<T>::DacDbiArrayList(const T * pList, int count):
+ m_pList(NULL),
+ m_nEntries(0)
+{
+ Init(pList, count);
+}
+
+// destructor: deallocates memory and sets list back to empty state
+// Arguments: none
+template<class T>
+inline
+DacDbiArrayList<T>::~DacDbiArrayList()
+{
+ Dealloc();
+}
+
+// explicitly deallocate the list and set it back to the empty state
+// Arguments: none
+// Notes: - Dealloc can be called multiple times without danger, since it
+// checks first that memory has been allocated
+template<class T>
+inline
+void DacDbiArrayList<T>::Dealloc()
+{
+ CONTRACT_VOID
+ {
+ NOTHROW;
+ }
+ CONTRACT_END;
+
+ if (m_pList != NULL)
+ {
+ delete [] m_pList;
+ m_pList = NULL;
+ }
+ m_nEntries = 0;
+ RETURN;
+}
+
+// Alloc and Init are very similar. Both preallocate the array; but Alloc leaves the
+// contents unintialized while Init provides initial values. The array contents are always
+// mutable.
+
+// allocate space for the list--in some instances, we'll know the count first, and then
+// we'll compute the elements one at a time. This (along with the array access operator
+// overload) allows us to handle that situation
+// Arguments:
+// input: nElements - number of elements of type T for which we need space
+// Notes:
+// - Alloc can be called multiple times and will free previous arrays.
+// - May throw OOM
+// - The array is not expandable, so you must allocate for all the elements at once.
+// - requesting an allocation of 0 or fewer bytes will not cause an error, but no memory is
+// allocated
+template<class T>
+inline
+void DacDbiArrayList<T>::Alloc(int nElements)
+{
+ Dealloc();
+ if (nElements > 0)
+ {
+ m_pList = new(forDbi) T[(size_t)nElements];
+ m_nEntries = nElements;
+ }
+}
+
+// allocate and initialize a DacDbiArrayList from a list of type T and a count
+// Arguments:
+// input: list - consecutive list (array) of elements of type T to be copied into
+// "this"
+// count - number of elements in list
+// Notes:
+// - May throw OOM
+// - Can be called multiple times with different lists, since this will deallocate
+// previous arrays.
+template<class T>
+inline
+void DacDbiArrayList<T>::Init(const T * pList, int count)
+{
+ _ASSERTE((m_pList == NULL) && (m_nEntries == 0));
+ if (count > 0)
+ {
+ Alloc(count);
+ m_nEntries = count;
+ for (int index = 0; index < count; ++index)
+ {
+ m_pList[index] = pList[index];
+ }
+ }
+}
+
+// read-only list element access
+template<class T>
+inline
+const T & DacDbiArrayList<T>::operator [](int i) const
+{
+ _ASSERTE(m_pList != NULL);
+ _ASSERTE((i >= 0) && (i < m_nEntries));
+ return m_pList[i];
+}
+
+// writeable list element access
+template<class T>
+inline
+T & DacDbiArrayList<T>::operator [](int i)
+{
+ _ASSERTE(m_pList != NULL);
+ _ASSERTE((i >= 0) && (i < m_nEntries));
+ return m_pList[i];
+}
+
+// get the number of elements in the list
+template<class T>
+inline
+int DacDbiArrayList<T>::Count() const
+{
+ return m_nEntries;
+}
+
+//-----------------------------------------------------------------------------
+// Target Buffer functions
+//-----------------------------------------------------------------------------
+
+// Default ctor
+inline
+TargetBuffer::TargetBuffer()
+{
+ this->pAddress = NULL;
+ this->cbSize = 0;
+}
+
+// Convenience Ctor to initialize around an (Address, size).
+inline
+TargetBuffer::TargetBuffer(CORDB_ADDRESS pBuffer, ULONG cbSizeInput)
+{
+ this->pAddress = pBuffer;
+ this->cbSize = cbSizeInput;
+}
+
+// Convenience Ctor to initialize around an (Address, size).
+inline
+TargetBuffer::TargetBuffer(void * pBuffer, ULONG cbSizeInput)
+{
+ this->pAddress = PTR_TO_CORDB_ADDRESS(pBuffer);
+ this->cbSize = cbSizeInput;
+}
+
+// Return a sub-buffer that's starts at byteOffset within this buffer and runs to the end.
+//
+// Arguments:
+// byteOffset - offset in bytes within this buffer that the new buffer starts at.
+//
+// Returns:
+// A new buffer that's a subset of the existing buffer.
+inline
+TargetBuffer TargetBuffer::SubBuffer(ULONG byteOffset) const
+{
+ _ASSERTE(byteOffset <= cbSize);
+ return TargetBuffer(pAddress + byteOffset, cbSize - byteOffset);
+}
+
+// Return a sub-buffer that starts at byteOffset within this buffer and is byteLength long.
+//
+// Arguments:
+// byteOffset - offset in bytes within this buffer that the new buffer starts at.
+// byteLength - length in bytes of the new buffer.
+//
+// Returns:
+// A new buffer that's a subset of the existing buffer.
+inline
+TargetBuffer TargetBuffer::SubBuffer(ULONG byteOffset, ULONG byteLength) const
+{
+ _ASSERTE(byteOffset + byteLength <= cbSize);
+ return TargetBuffer(pAddress + byteOffset, byteLength);
+}
+
+// Sets address to NULL and size to 0
+inline
+void TargetBuffer::Clear()
+{
+ pAddress = NULL;
+ cbSize = 0;
+}
+
+// Initialize fields
+inline
+void TargetBuffer::Init(CORDB_ADDRESS address, ULONG size)
+{
+ pAddress = address;
+ cbSize = size;
+}
+
+
+// Returns true iff the buffer is empty.
+inline
+bool TargetBuffer::IsEmpty() const
+{
+ return (this->cbSize == 0);
+}
+
+//-----------------------------------------------------------------------------
+// NativeVarData member function implementations
+//-----------------------------------------------------------------------------
+
+// Initialize a new instance of NativeVarData
+inline NativeVarData::NativeVarData() :
+ m_allArgsCount(0),
+ m_fInitialized(false)
+{
+}
+
+// destructor
+inline NativeVarData::~NativeVarData()
+{
+ m_fInitialized = false;
+ }
+
+// initialize the list of native var information structures, including the starting address of the list, the number of
+// entries and the number of fixed args.
+inline void NativeVarData::InitVarDataList(ICorDebugInfo::NativeVarInfo * pListStart,
+ int fixedArgCount,
+ int entryCount)
+{
+ m_offsetInfo.Init(pListStart, entryCount);
+ m_fixedArgsCount = fixedArgCount;
+ m_fInitialized = true;
+}
+
+//-----------------------------------------------------------------------------
+// SequencePoints member function implementations
+//-----------------------------------------------------------------------------
+
+// initializing constructor
+inline SequencePoints::SequencePoints() :
+ m_mapCount(0),
+ m_lastILOffset(0),
+ m_fInitialized(false)
+{
+}
+
+// destructor
+inline SequencePoints::~SequencePoints()
+{
+ m_fInitialized = false;
+ }
+
+// Initialize the m_pMap data member to the address of an allocated chunk
+// of memory (or to NULL if the count is zero). Set m_count as the
+// number of entries in the map.
+inline void SequencePoints::InitSequencePoints(ULONG32 count)
+{
+ m_map.Alloc(count),
+ m_fInitialized = true;
+}
+
+//
+// Map the given native offset to IL offset and return the mapping type.
+//
+// Arguments:
+// dwNativeOffset - the native offset to be mapped
+// pMapType - out parameter; return the mapping type
+//
+// Return Value:
+// Return the IL offset corresponding to the given native offset.
+// For a prolog, return 0.
+// For an epilog, return the IL offset of the last sequence point before the epilog.
+// If we can't map to an IL offset, then return 0, with a mapping type of MAPPING_NO_INFO.
+//
+// Assumptions:
+// The sequence points are sorted.
+//
+
+inline
+DWORD SequencePoints::MapNativeOffsetToIL(DWORD dwNativeOffset,
+ CorDebugMappingResult *pMapType)
+{
+ //_ASSERTE(IsInitialized());
+ if (!IsInitialized())
+ {
+ (*pMapType) = MAPPING_NO_INFO;
+ return 0;
+ }
+
+ _ASSERTE(pMapType != NULL);
+
+ int i;
+
+ for (i = 0; i < (int)m_mapCount; ++i)
+ {
+ // Check to determine if dwNativeOffset is within this sequence point. Checking the lower bound is trivial--
+ // we just make sure that dwNativeOffset >= m_map[i].nativeStartOffset.
+ // Checking to be sure it's before the end of the range is a little trickier. We can have
+ // m_map[i].nativeEndOffset = 0 for two reasons:
+ // 1. We use an end offset of 0 to signify that this end offset is also the end of the method.
+ // 2. We could also have an end offset of 0 if the IL prologue doesn't translate to any native
+ // instructions. Thus, the first native instruction (which will not be in the prologue) is at an offset
+ // of 0. The end offset is always set to the start offset of the next sequence point, so this means
+ // that both the start and end offsets of the (non-existent) native instruction range for the
+ // prologue is also 0.
+ // If the end offset is 0, we want to check if we're in the prologue before concluding that the
+ // value of dwNativeOffset is out of range.
+ if ((dwNativeOffset >= m_map[i].nativeStartOffset) &&
+ (((m_map[i].nativeEndOffset == 0) && (m_map[i].ilOffset != (ULONG)ICorDebugInfo::PROLOG)) ||
+ (dwNativeOffset < m_map[i].nativeEndOffset)))
+ {
+ ULONG uILOffset = m_map[i].ilOffset;
+
+ if (m_map[i].ilOffset == (ULONG)ICorDebugInfo::PROLOG)
+ {
+ uILOffset = 0;
+ (*pMapType) = MAPPING_PROLOG;
+ }
+ else if (m_map[i].ilOffset == (ULONG)ICorDebugInfo::NO_MAPPING)
+ {
+ uILOffset = 0;
+ (*pMapType) = MAPPING_UNMAPPED_ADDRESS;
+ }
+ else if (m_map[i].ilOffset == (ULONG)ICorDebugInfo::EPILOG)
+ {
+ uILOffset = m_lastILOffset;
+ (*pMapType) = MAPPING_EPILOG;
+ }
+ else if (dwNativeOffset == m_map[i].nativeStartOffset)
+ {
+ (*pMapType) = MAPPING_EXACT;
+ }
+ else
+ {
+ (*pMapType) = MAPPING_APPROXIMATE;
+ }
+ return uILOffset;
+ }
+ }
+
+ (*pMapType) = MAPPING_NO_INFO;
+ return 0;
+}
+
+//
+// Copy data from the VM map data to our own map structure and sort. The
+// information comes to us in a data structure that differs slightly from the
+// one we use out of process, so we have to copy it to the right-side struct.
+// Arguments
+// input
+// mapCopy sequence points
+// output
+// pSeqPoints.m_map is initialized with the correct right side representation of sequence points
+
+inline
+void SequencePoints::CopyAndSortSequencePoints(const ICorDebugInfo::OffsetMapping mapCopy[])
+{
+ // copy information to pSeqPoint and set end offsets
+ int i;
+
+ ULONG32 lastILOffset = 0;
+
+ const DWORD call_inst = (DWORD)ICorDebugInfo::CALL_INSTRUCTION;
+ for (i = 0; i < m_map.Count(); i++)
+ {
+ m_map[i].ilOffset = mapCopy[i].ilOffset;
+ m_map[i].nativeStartOffset = mapCopy[i].nativeOffset;
+
+ if (i < m_map.Count() - 1)
+ {
+ // We need to not use CALL_INSTRUCTION's IL start offset.
+ int j = i + 1;
+ while ((mapCopy[j].source & call_inst) == call_inst && j < m_map.Count()-1)
+ j++;
+
+ m_map[i].nativeEndOffset = mapCopy[j].nativeOffset;
+ }
+
+ m_map[i].source = mapCopy[i].source;
+
+ // need to cast the offsets to signed values first because we do actually use
+ // special negative offsets such as ICorDebugInfo::PROLOG
+ if ((m_map[i].source & call_inst) != call_inst)
+ lastILOffset = max((int)lastILOffset, (int)m_map[i].ilOffset);
+ }
+
+ if (m_map.Count() >= 1)
+ {
+ m_map[i - 1].nativeEndOffset = 0;
+ m_map[i - 1].source =
+ (ICorDebugInfo::SourceTypes)(m_map[i - 1].source | ICorDebugInfo::NATIVE_END_OFFSET_UNKNOWN);
+ }
+
+ // sort the map
+ MapSortILMap mapSorter(&m_map[0], m_map.Count());
+ mapSorter.Sort();
+
+
+ m_mapCount = m_map.Count();
+ while (m_mapCount > 0 && (m_map[m_mapCount-1].source & (call_inst)) == call_inst)
+ m_mapCount--;
+
+ SetLastILOffset(lastILOffset);
+} // CopyAndSortSequencePoints
+
+//-----------------------------------------------------------------------------
+// member function implementations for MapSortILMap class to sort sequence points
+// by IL offset
+//-----------------------------------------------------------------------------
+
+// secondary key comparison--if two IL offsets are the same,
+// we determine order based on native offset
+
+inline
+int SequencePoints::MapSortILMap::CompareInternal(DebuggerILToNativeMap *first,
+ DebuggerILToNativeMap *second)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (first->nativeStartOffset == second->nativeStartOffset)
+ return 0;
+ else if (first->nativeStartOffset < second->nativeStartOffset)
+ return -1;
+ else
+ return 1;
+}
+
+//Comparison operator
+inline
+int SequencePoints::MapSortILMap::Compare(DebuggerILToNativeMap * first,
+ DebuggerILToNativeMap * second)
+{
+ LIMITED_METHOD_CONTRACT;
+ const DWORD call_inst = (DWORD)ICorDebugInfo::CALL_INSTRUCTION;
+
+ //PROLOGs go first
+ if (first->ilOffset == (ULONG) ICorDebugInfo::PROLOG &&
+ second->ilOffset == (ULONG) ICorDebugInfo::PROLOG)
+ {
+ return CompareInternal(first, second);
+ }
+ else if (first->ilOffset == (ULONG) ICorDebugInfo::PROLOG)
+ {
+ return -1;
+ }
+ else if (second->ilOffset == (ULONG) ICorDebugInfo::PROLOG)
+ {
+ return 1;
+ }
+ // call_instruction goes at the very very end of the table.
+ else if ((first->source & call_inst) == call_inst
+ && (second->source & call_inst) == call_inst)
+ {
+ return CompareInternal(first, second);
+ } else if ((first->source & call_inst) == call_inst)
+ {
+ return 1;
+ } else if ((second->source & call_inst) == call_inst)
+ {
+ return -1;
+ }
+ //NO_MAPPING go last
+ else if (first->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING &&
+ second->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
+ {
+ return CompareInternal(first, second);
+ }
+ else if (first->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
+ {
+ return 1;
+ }
+ else if (second->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
+ {
+ return -1;
+ }
+ //EPILOGs go next-to-last
+ else if (first->ilOffset == (ULONG) ICorDebugInfo::EPILOG &&
+ second->ilOffset == (ULONG) ICorDebugInfo::EPILOG)
+ {
+ return CompareInternal(first, second);
+ }
+ else if (first->ilOffset == (ULONG) ICorDebugInfo::EPILOG)
+ {
+ return 1;
+ }
+ else if (second->ilOffset == (ULONG) ICorDebugInfo::EPILOG)
+ {
+ return -1;
+ }
+ //normal offsets compared otherwise
+ else if (first->ilOffset < second->ilOffset)
+ {
+ return -1;
+ }
+ else if (first->ilOffset == second->ilOffset)
+ {
+ return CompareInternal(first, second);
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// NativeCodeFunctionData member function implementations
+// (for getting native code regions)
+//-----------------------------------------------------------------------------
+
+inline
+CodeBlobRegion & operator++(CodeBlobRegion & rs)
+{
+ return rs = CodeBlobRegion(rs + 1);
+}
+
+// Convert the data in an instance of DebuggerIPCE_JITFUncData to an instance of NativeCodeFunctionData.
+// We need to have this latter type to look up or create a new CordbNativeCode object, but the stack walker is
+// using the former type to gather information.
+// Arguments:
+// Input:
+// source - an initialized instance of DebuggerIPCE_JITFuncData containing the information to
+// be copied into this instance of NativeCodeFunctionData
+// @dbgtodo dlaw: Once CordbThread::RefreshStack is fully DAC-ized, we can change the data structure that it uses
+// to have a member of type NativeCodeFunctionData which we can pass without copying. At that point,
+// this method can disappear.
+inline
+NativeCodeFunctionData::NativeCodeFunctionData(DebuggerIPCE_JITFuncData * source)
+{
+ // copy the code region information
+ m_rgCodeRegions[kHot].Init(CORDB_ADDRESS(source->nativeStartAddressPtr), (ULONG)source->nativeHotSize);
+ m_rgCodeRegions[kCold].Init(CORDB_ADDRESS(source->nativeStartAddressColdPtr), (ULONG)source->nativeColdSize);
+
+ // copy the other function information
+ isInstantiatedGeneric = source->isInstantiatedGeneric;
+ vmNativeCodeMethodDescToken = source->vmNativeCodeMethodDescToken;
+ encVersion = source->enCVersion;
+}
+
+
+// set all fields to default values (NULL, FALSE, or zero as appropriate)
+inline
+NativeCodeFunctionData::NativeCodeFunctionData()
+{
+ Clear();
+}
+
+inline
+void NativeCodeFunctionData::Clear()
+{
+ isInstantiatedGeneric = FALSE;
+ encVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION;
+ for (CodeBlobRegion region = kHot; region < MAX_REGIONS; ++region)
+ {
+ m_rgCodeRegions[region].Clear();
+ }
+}
+
+//-----------------------------------------------------------------------------------
+// ClassInfo member functions
+//-----------------------------------------------------------------------------------
+
+inline
+ClassInfo::ClassInfo():
+ m_objectSize(0)
+ {}
+
+// clear all fields
+inline
+void ClassInfo::Clear()
+{
+ m_objectSize = 0;
+ m_fieldList.Dealloc();
+}
+
+inline
+ClassInfo::~ClassInfo()
+{
+ m_fieldList.Dealloc();
+}
+
+//-----------------------------------------------------------------------------------
+// FieldData member functions
+//-----------------------------------------------------------------------------------
+#ifndef RIGHT_SIDE_COMPILE
+
+// initialize various fields of an instance of FieldData from information retrieved from a FieldDesc
+inline
+void FieldData::Initialize(BOOL fIsStatic, BOOL fIsPrimitive, mdFieldDef mdToken)
+{
+ ClearFields();
+ m_fFldIsStatic = (fIsStatic == TRUE);
+ m_fFldIsPrimitive = (fIsPrimitive == TRUE);
+ // This is what tells the right side the field is unavailable due to EnC.
+ m_fldMetadataToken = mdToken;
+}
+#endif
+
+// clear various fields for a new instance of FieldData
+inline
+void FieldData::ClearFields()
+{
+ m_fldSignatureCache = NULL;
+ m_fldSignatureCacheSize = 0;
+ m_fldInstanceOffset = 0;
+ m_pFldStaticAddress = NULL;
+}
+
+typedef ULONG_PTR SIZE_T;
+
+inline
+BOOL FieldData::OkToGetOrSetInstanceOffset()
+{
+ return (!m_fFldIsStatic && !m_fFldIsRVA && !m_fFldIsTLS && !m_fFldIsContextStatic &&
+ m_fFldStorageAvailable && (m_pFldStaticAddress == NULL));
+}
+
+// If this is an instance field, store its offset
+inline
+void FieldData::SetInstanceOffset(SIZE_T offset)
+{
+ _ASSERTE(!m_fFldIsStatic);
+ _ASSERTE(!m_fFldIsRVA);
+ _ASSERTE(!m_fFldIsTLS);
+ _ASSERTE(!m_fFldIsContextStatic);
+ _ASSERTE(m_fFldStorageAvailable);
+ _ASSERTE(m_pFldStaticAddress == NULL);
+ m_fldInstanceOffset = offset;
+}
+
+inline
+BOOL FieldData::OkToGetOrSetStaticAddress()
+{
+ return (m_fFldIsStatic && !m_fFldIsTLS && !m_fFldIsContextStatic &&
+ m_fFldStorageAvailable && (m_fldInstanceOffset == 0));
+}
+
+// If this is a "normal" static, store its absolute address
+inline
+void FieldData::SetStaticAddress(TADDR addr)
+{
+ _ASSERTE(m_fFldIsStatic);
+ _ASSERTE(!m_fFldIsTLS);
+ _ASSERTE(!m_fFldIsContextStatic);
+ _ASSERTE(m_fFldStorageAvailable);
+ _ASSERTE(m_fldInstanceOffset == 0);
+ m_pFldStaticAddress = TADDR(addr);
+}
+
+// Get the offset of a field
+inline
+SIZE_T FieldData::GetInstanceOffset()
+{
+ _ASSERTE(!m_fFldIsStatic);
+ _ASSERTE(!m_fFldIsRVA);
+ _ASSERTE(!m_fFldIsTLS);
+ _ASSERTE(!m_fFldIsContextStatic);
+ _ASSERTE(m_fFldStorageAvailable);
+ _ASSERTE(m_pFldStaticAddress == NULL);
+ return m_fldInstanceOffset;
+}
+
+// Get the static address for a field
+inline
+TADDR FieldData::GetStaticAddress()
+{
+ _ASSERTE(m_fFldIsStatic);
+ _ASSERTE(!m_fFldIsTLS);
+ _ASSERTE(!m_fFldIsContextStatic);
+ _ASSERTE(m_fFldStorageAvailable || (m_pFldStaticAddress == NULL));
+ _ASSERTE(m_fldInstanceOffset == 0);
+ return m_pFldStaticAddress;
+}
+
+//-----------------------------------------------------------------------------------
+// EnCHangingFieldInfo member functions
+//-----------------------------------------------------------------------------------
+
+inline
+void EnCHangingFieldInfo::Init(VMPTR_Object pObject,
+ SIZE_T offset,
+ mdFieldDef fieldToken,
+ CorElementType elementType,
+ mdTypeDef metadataToken,
+ VMPTR_DomainFile vmDomainFile)
+ {
+ m_vmObject = pObject;
+ m_offsetToVars = offset;
+ m_fldToken = fieldToken;
+ m_objectTypeData.elementType = elementType;
+ m_objectTypeData.metadataToken = metadataToken;
+ m_objectTypeData.vmDomainFile = vmDomainFile;
+ }
+
+
+
+#endif // DACDBISTRUCTURES_INL_
diff --git a/src/debug/inc/dbgappdomain.h b/src/debug/inc/dbgappdomain.h
new file mode 100644
index 0000000000..70504c09ec
--- /dev/null
+++ b/src/debug/inc/dbgappdomain.h
@@ -0,0 +1,388 @@
+// 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.
+
+#ifndef DbgAppDomain_H
+#define DbgAppDomain_H
+
+// Forward declaration
+class AppDomain;
+
+
+void BeginThreadAffinityHelper();
+void EndThreadAffinityHelper();
+
+
+// AppDomainInfo contains information about an AppDomain
+// All pointers are for the left side, and we do not own any of the memory
+struct AppDomainInfo
+{
+ ULONG m_id; // unique identifier
+ int m_iNameLengthInBytes;
+ LPCWSTR m_szAppDomainName;
+ AppDomain *m_pAppDomain; // only used by LS
+
+ // NOTE: These functions are just helpers and must not add a VTable
+ // to this struct (since we need to read this out-of-proc)
+
+ // Provide a clean definition of an empty entry
+ inline bool IsEmpty() const
+ {
+ return m_szAppDomainName == NULL;
+ }
+
+#ifndef RIGHT_SIDE_COMPILE
+ // Mark this entry as empty.
+ inline void FreeEntry()
+ {
+ m_szAppDomainName = NULL;
+ }
+
+ // Set the string name and length.
+ // If szName is null, it is adjusted to a global constant.
+ // This also causes the entry to be considered valid
+ inline void SetName(LPCWSTR szName)
+ {
+ if (szName != NULL)
+ m_szAppDomainName = szName;
+ else
+ m_szAppDomainName = W("<NoName>");
+
+ m_iNameLengthInBytes = (int) (wcslen(m_szAppDomainName) + 1) * sizeof(WCHAR);
+ }
+#endif
+};
+
+// Enforce the AppDomain IPC block binary layout doesn't change between versions.
+// Only an issue for x86 since that's the only platform w/ multiple versions.
+#if defined(DBG_TARGET_X86)
+static_assert_no_msg(offsetof(AppDomainInfo, m_id) == 0x0);
+static_assert_no_msg(offsetof(AppDomainInfo, m_iNameLengthInBytes) == 0x4);
+static_assert_no_msg(offsetof(AppDomainInfo, m_szAppDomainName) == 0x8);
+static_assert_no_msg(offsetof(AppDomainInfo, m_pAppDomain) == 0xc);
+#endif
+
+
+
+// The RemoteHANDLE encapsulates the PAL specific handling of handles to avoid PAL specific ifdefs
+// everywhere else in the code.
+// There are two common initialization patterns:
+//
+// 1. Publishing of local handle for other processes, the value of the wrapper is a local handle
+// in *this* process at the end:
+// - In this process, call SetLocal(hHandle) to initialize the handle.
+// - In the other processes, call DuplicateToLocalProcess to get a local copy of the handle.
+//
+// 2. Injecting of local handle into other process, the value of the wrapper is a local handle
+// in the *other* process at the end:
+// - In this process, call DuplicateToRemoteProcess(hProcess, hHandle) to initialize the handle.
+// - In the other process, call ImportToOtherProcess() to finish the initialization of the wrapper
+// with a local copy of the handle.
+//
+// Once initialized, the wrapper can be used the same way as a regular HANDLE in the process
+// it was initialized for. There is casting operator HANDLE to achieve that.
+
+struct RemoteHANDLE {
+ HANDLE m_hLocal;
+
+ operator HANDLE& ()
+ {
+ return m_hLocal;
+ }
+
+ void Close()
+ {
+ HANDLE hHandle = m_hLocal;
+ if (hHandle != NULL) {
+ m_hLocal = NULL;
+ CloseHandle(hHandle);
+ }
+ }
+
+ // Sets the local value of the handle. DuplicateToLocalProcess can be used later
+ // by the remote process to acquire the remote handle.
+ BOOL SetLocal(HANDLE hHandle)
+ {
+ m_hLocal = hHandle;
+ return TRUE;
+ }
+
+ // Duplicates the current handle value to remote process. ImportToLocalProcess
+ // should be called in the remote process before the handle is used in the remote process.
+ // NOTE: right now this is used for duplicating the debugger's process handle into the LS so
+ // that the LS can know when the RS has exited; thus we are only specifying SYNCHRONIZE
+ // access to mitigate any security concerns.
+ BOOL DuplicateToRemoteProcess(HANDLE hProcess, HANDLE hHandle)
+ {
+ return DuplicateHandle(GetCurrentProcess(), hHandle, hProcess, &m_hLocal,
+ SYNCHRONIZE, FALSE, 0);
+ }
+
+ // Duplicates the current handle value to local process. To be used in combination with SetLocal.
+ BOOL DuplicateToLocalProcess(HANDLE hProcess, HANDLE* pHandle)
+ {
+ return DuplicateHandle(hProcess, m_hLocal, GetCurrentProcess(), pHandle,
+ NULL, FALSE, DUPLICATE_SAME_ACCESS);
+ }
+
+ void CloseInRemoteProcess(HANDLE hProcess)
+ {
+ HANDLE hHandle = m_hLocal;
+ m_hLocal = NULL;
+
+ HANDLE hTmp;
+ if (DuplicateHandle(hProcess, hHandle, GetCurrentProcess(), &hTmp,
+ NULL, FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
+ {
+ CloseHandle(hTmp);
+ }
+ }
+
+ // Imports the handle to local process. To be used in combination with DuplicateToRemoteProcess.
+ HANDLE ImportToLocalProcess()
+ {
+ return m_hLocal;
+ }
+};
+
+
+// AppDomain publishing server support:
+// Information about all appdomains in the process will be maintained
+// in the shared memory block for use by the debugger, etc.
+// This structure defines the layout of the information that will
+// be maintained.
+struct AppDomainEnumerationIPCBlock
+{
+ // !!! The binary format of this layout must remain the same across versions so that
+ // !!! a V2.0 publisher can inspect a v1.0 app.
+
+ // lock for serialization while manipulating AppDomain list.
+ RemoteHANDLE m_hMutex;
+
+ // Number of slots in AppDomainListElement array
+ int m_iTotalSlots;
+ int m_iNumOfUsedSlots;
+ int m_iLastFreedSlot;
+ int m_iSizeInBytes; // Size of AppDomainInfo in bytes
+
+ // We can use psapi!GetModuleFileNameEx to get the module name.
+ // This provides an alternative.
+ int m_iProcessNameLengthInBytes;
+ WCHAR *m_szProcessName;
+
+ AppDomainInfo *m_rgListOfAppDomains;
+ BOOL m_fLockInvalid;
+
+
+#ifndef RIGHT_SIDE_COMPILE
+ /*************************************************************************
+ * Locks the list
+ *************************************************************************/
+ BOOL Lock()
+ {
+ BeginThreadAffinityHelper();
+
+ DWORD dwRes = WaitForSingleObject(m_hMutex, 3000);
+ if (dwRes == WAIT_TIMEOUT)
+ {
+ // Nobody should get stuck holding this lock.
+ // If we timeout on the wait, then either:
+ // - it's a really bad race and somebody got preempted for a long time
+ // - perhaps somebody's doing a DOS attack and holding onto the mutex.
+ m_fLockInvalid = TRUE;
+ }
+
+
+ // The only time this can happen is if we're in shutdown and a thread
+ // that held this lock is killed. If this happens, assume that this
+ // IPC block is in an invalid state and return FALSE to indicate
+ // that people shouldn't do anything with the block anymore.
+ if (dwRes == WAIT_ABANDONED)
+ {
+ m_fLockInvalid = TRUE;
+ }
+
+ if (m_fLockInvalid)
+ {
+ Unlock();
+ }
+
+ return (dwRes == WAIT_OBJECT_0 && !m_fLockInvalid);
+ }
+
+ /*************************************************************************
+ * Unlocks the list
+ *************************************************************************/
+ void Unlock()
+ {
+ // Lock may or may not be valid at this point. Thus Release may fail,
+ // but we'll just ignore that.
+ ReleaseMutex(m_hMutex);
+ EndThreadAffinityHelper();
+
+ }
+
+ /*************************************************************************
+ * Gets a free AppDomainInfo entry, and will allocate room if there are
+ * no free slots left.
+ *************************************************************************/
+ AppDomainInfo *GetFreeEntry()
+ {
+ // first check to see if there is space available. If not, then realloc.
+ if (m_iNumOfUsedSlots == m_iTotalSlots)
+ {
+ // need to realloc
+ AppDomainInfo *pTemp =
+ new (nothrow) AppDomainInfo [m_iTotalSlots*2];
+
+ if (pTemp == NULL)
+ {
+ return (NULL);
+ }
+
+ memcpy (pTemp, m_rgListOfAppDomains, m_iSizeInBytes);
+
+ delete [] m_rgListOfAppDomains;
+
+ // Initialize the increased portion of the realloced memory
+ int iNewSlotSize = m_iTotalSlots * 2;
+
+ for (int iIndex = m_iTotalSlots; iIndex < iNewSlotSize; iIndex++)
+ pTemp[iIndex].FreeEntry();
+
+ m_rgListOfAppDomains = pTemp;
+ m_iTotalSlots = iNewSlotSize;
+ m_iSizeInBytes *= 2;
+ }
+
+ // Walk the list looking for an empty slot. Start from the last
+ // one which was freed.
+ {
+ int i = m_iLastFreedSlot;
+
+ do
+ {
+ // Pointer to the entry being examined
+ AppDomainInfo *pADInfo = &(m_rgListOfAppDomains[i]);
+
+ // is the slot available?
+ if (pADInfo->IsEmpty())
+ return (pADInfo);
+
+ i = (i + 1) % m_iTotalSlots;
+
+ } while (i != m_iLastFreedSlot);
+ }
+
+ _ASSERTE(!"ADInfo::GetFreeEntry: should never get here.");
+ return (NULL);
+ }
+
+ /*************************************************************************
+ * Returns an AppDomainInfo slot to the free list.
+ *************************************************************************/
+ void FreeEntry(AppDomainInfo *pADInfo)
+ {
+ _ASSERTE(pADInfo >= m_rgListOfAppDomains &&
+ pADInfo < m_rgListOfAppDomains + m_iSizeInBytes);
+ _ASSERTE(((size_t)pADInfo - (size_t)m_rgListOfAppDomains) %
+ sizeof(AppDomainInfo) == 0);
+
+ // Mark this slot as free
+ pADInfo->FreeEntry();
+
+#ifdef _DEBUG
+ memset(pADInfo, 0, sizeof(AppDomainInfo));
+#endif
+
+ // decrement the used slot count
+ m_iNumOfUsedSlots--;
+
+ // Save the last freed slot.
+ m_iLastFreedSlot = (int)((size_t)pADInfo - (size_t)m_rgListOfAppDomains) /
+ sizeof(AppDomainInfo);
+ }
+
+ /*************************************************************************
+ * Finds an AppDomainInfo entry corresponding to the AppDomain pointer.
+ * Returns NULL if no such entry exists.
+ *************************************************************************/
+ AppDomainInfo *FindEntry(AppDomain *pAD)
+ {
+ // Walk the list looking for a matching entry
+ for (int i = 0; i < m_iTotalSlots; i++)
+ {
+ AppDomainInfo *pADInfo = &(m_rgListOfAppDomains[i]);
+
+ if (!pADInfo->IsEmpty() &&
+ pADInfo->m_pAppDomain == pAD)
+ return pADInfo;
+ }
+
+ return (NULL);
+ }
+
+ /*************************************************************************
+ * Returns the first AppDomainInfo entry in the list. Returns NULL if
+ * no such entry exists.
+ *************************************************************************/
+ AppDomainInfo *FindFirst()
+ {
+ // Walk the list looking for a non-empty entry
+ for (int i = 0; i < m_iTotalSlots; i++)
+ {
+ AppDomainInfo *pADInfo = &(m_rgListOfAppDomains[i]);
+
+ if (!pADInfo->IsEmpty())
+ return pADInfo;
+ }
+
+ return (NULL);
+ }
+
+ /*************************************************************************
+ * Returns the next AppDomainInfo entry after pADInfo. Returns NULL if
+ * pADInfo was the last in the list.
+ *************************************************************************/
+ AppDomainInfo *FindNext(AppDomainInfo *pADInfo)
+ {
+ _ASSERTE(pADInfo >= m_rgListOfAppDomains &&
+ pADInfo < m_rgListOfAppDomains + m_iSizeInBytes);
+ _ASSERTE(((size_t)pADInfo - (size_t)m_rgListOfAppDomains) %
+ sizeof(AppDomainInfo) == 0);
+
+ // Walk the list looking for the next non-empty entry
+ for (int i = (int)((size_t)pADInfo - (size_t)m_rgListOfAppDomains)
+ / sizeof(AppDomainInfo) + 1;
+ i < m_iTotalSlots;
+ i++)
+ {
+ AppDomainInfo *pADInfoTemp = &(m_rgListOfAppDomains[i]);
+
+ if (!pADInfoTemp->IsEmpty())
+ return pADInfoTemp;
+ }
+
+ return (NULL);
+ }
+#endif // RIGHT_SIDE_COMPILE
+};
+
+// Enforce the AppDomain IPC block binary layout doesn't change between versions.
+// Only an issue for x86 since that's the only platform w/ multiple versions.
+#if defined(DBG_TARGET_X86)
+static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_hMutex) == 0x0);
+static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_iTotalSlots) == 0x4);
+static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_iNumOfUsedSlots) == 0x8);
+static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_iLastFreedSlot) == 0xc);
+static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_iSizeInBytes) == 0x10);
+static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_iProcessNameLengthInBytes) == 0x14);
+static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_szProcessName) == 0x18);
+static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_rgListOfAppDomains) == 0x1c);
+static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_fLockInvalid) == 0x20);
+#endif
+
+#endif //DbgAppDomain_H
+
+
+
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_ */
diff --git a/src/debug/inc/dbgipceventtypes.h b/src/debug/inc/dbgipceventtypes.h
new file mode 100644
index 0000000000..b538360e68
--- /dev/null
+++ b/src/debug/inc/dbgipceventtypes.h
@@ -0,0 +1,143 @@
+// 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.
+
+//
+// Events that go both ways
+IPC_EVENT_TYPE0(DB_IPCE_INVALID_EVENT ,0x0000)
+IPC_EVENT_TYPE0(DB_IPCE_TYPE_MASK ,0x0FFF)
+
+// Some rules:
+// 1. Type0 is for marking sections in the id range.
+// Type1 is for events that go L->R, Type2 is for events that go R<-L.
+// 2. All non-type 0 events should have a unique identifier & value
+// 3. All type 1 events values should be in range [DB_IPCE_RUNTIME_FIRST, DB_IPCE_RUNTIME_LAST)
+// All type 2 events values should be in range [DB_IPCE_DEBUGGER_FIRST, DB_IPCE_DEBUGGER_LAST)
+// 4. All event values should be monotonically increasing, though we can skip values.
+// 5. All values should be a subset of the bits specified by DB_IPCE_TYPE_MASK.
+//
+// These rules are enforced by a bunch of compile time checks (C_ASSERT) in
+// the function DoCompileTimeCheckOnDbgIpcEventTypes.
+// If you get compiler errors in this file, you are probably violating the rules above.
+
+// Events that travel from the RC to the DI (Left to Right)
+IPC_EVENT_TYPE0(DB_IPCE_RUNTIME_FIRST ,0x0100) // change to TYPE0 because it is not really an event
+IPC_EVENT_TYPE1(DB_IPCE_BREAKPOINT ,0x0100)
+IPC_EVENT_TYPE1(DB_IPCE_SYNC_COMPLETE ,0x0102)
+IPC_EVENT_TYPE1(DB_IPCE_THREAD_ATTACH ,0x0103)
+IPC_EVENT_TYPE1(DB_IPCE_THREAD_DETACH ,0x0104)
+IPC_EVENT_TYPE1(DB_IPCE_LOAD_MODULE ,0x0105)
+IPC_EVENT_TYPE1(DB_IPCE_UNLOAD_MODULE ,0x0106)
+IPC_EVENT_TYPE1(DB_IPCE_LOAD_CLASS ,0x0107)
+IPC_EVENT_TYPE1(DB_IPCE_UNLOAD_CLASS ,0x0108)
+IPC_EVENT_TYPE1(DB_IPCE_EXCEPTION ,0x0109)
+IPC_EVENT_TYPE1(DB_IPCE_UNHANDLED_EXCEPTION ,0x010A)
+IPC_EVENT_TYPE1(DB_IPCE_BREAKPOINT_ADD_RESULT ,0x010D)
+IPC_EVENT_TYPE1(DB_IPCE_STEP_RESULT ,0x010E)
+IPC_EVENT_TYPE1(DB_IPCE_STEP_COMPLETE ,0x010F)
+IPC_EVENT_TYPE1(DB_IPCE_BREAKPOINT_REMOVE_RESULT ,0x0111)
+IPC_EVENT_TYPE1(DB_IPCE_GET_BUFFER_RESULT ,0x0115)
+IPC_EVENT_TYPE1(DB_IPCE_RELEASE_BUFFER_RESULT ,0x0116)
+IPC_EVENT_TYPE1(DB_IPCE_ENC_ADD_FIELD ,0x0117)
+IPC_EVENT_TYPE1(DB_IPCE_APPLY_CHANGES_RESULT ,0x0118)
+IPC_EVENT_TYPE1(DB_IPCE_CUSTOM_NOTIFICATION ,0x011B)
+IPC_EVENT_TYPE1(DB_IPCE_USER_BREAKPOINT ,0x011C)
+IPC_EVENT_TYPE1(DB_IPCE_FIRST_LOG_MESSAGE ,0x011D)
+// DB_IPCE_CONTINUED_LOG_MESSAGE = 0x11E, used to be here in v1.1,
+// But we've removed that remove the v2.0 protocol
+IPC_EVENT_TYPE1(DB_IPCE_LOGSWITCH_SET_MESSAGE ,0x011F)
+IPC_EVENT_TYPE1(DB_IPCE_CREATE_APP_DOMAIN ,0x0120)
+IPC_EVENT_TYPE1(DB_IPCE_EXIT_APP_DOMAIN ,0x0121)
+IPC_EVENT_TYPE1(DB_IPCE_LOAD_ASSEMBLY ,0x0122)
+IPC_EVENT_TYPE1(DB_IPCE_UNLOAD_ASSEMBLY ,0x0123)
+IPC_EVENT_TYPE1(DB_IPCE_SET_DEBUG_STATE_RESULT ,0x0124)
+IPC_EVENT_TYPE1(DB_IPCE_FUNC_EVAL_SETUP_RESULT ,0x0125)
+IPC_EVENT_TYPE1(DB_IPCE_FUNC_EVAL_COMPLETE ,0x0126)
+IPC_EVENT_TYPE1(DB_IPCE_SET_REFERENCE_RESULT ,0x0127)
+IPC_EVENT_TYPE1(DB_IPCE_APP_DOMAIN_NAME_RESULT ,0x0128)
+IPC_EVENT_TYPE1(DB_IPCE_FUNC_EVAL_ABORT_RESULT ,0x0129)
+IPC_EVENT_TYPE1(DB_IPCE_NAME_CHANGE ,0x012a)
+IPC_EVENT_TYPE1(DB_IPCE_UPDATE_MODULE_SYMS ,0x012c)
+IPC_EVENT_TYPE1(DB_IPCE_CONTROL_C_EVENT ,0x012f)
+IPC_EVENT_TYPE1(DB_IPCE_FUNC_EVAL_CLEANUP_RESULT ,0x0130)
+IPC_EVENT_TYPE1(DB_IPCE_ENC_REMAP ,0x0131)
+IPC_EVENT_TYPE1(DB_IPCE_SET_VALUE_CLASS_RESULT ,0x0133)
+IPC_EVENT_TYPE1(DB_IPCE_BREAKPOINT_SET_ERROR ,0x0134)
+IPC_EVENT_TYPE1(DB_IPCE_ENC_UPDATE_FUNCTION ,0x0137)
+IPC_EVENT_TYPE1(DB_IPCE_SET_METHOD_JMC_STATUS_RESULT ,0x013a)
+IPC_EVENT_TYPE1(DB_IPCE_GET_METHOD_JMC_STATUS_RESULT ,0x013b)
+IPC_EVENT_TYPE1(DB_IPCE_SET_MODULE_JMC_STATUS_RESULT ,0x013c)
+IPC_EVENT_TYPE1(DB_IPCE_GET_THREAD_FOR_TASKID_RESULT ,0x013d)
+IPC_EVENT_TYPE1(DB_IPCE_CREATE_CONNECTION ,0x0141)
+IPC_EVENT_TYPE1(DB_IPCE_DESTROY_CONNECTION ,0x0142)
+IPC_EVENT_TYPE1(DB_IPCE_CHANGE_CONNECTION ,0x0143)
+IPC_EVENT_TYPE1(DB_IPCE_FUNC_EVAL_RUDE_ABORT_RESULT ,0x0144)
+IPC_EVENT_TYPE1(DB_IPCE_EXCEPTION_CALLBACK2 ,0x0147)
+IPC_EVENT_TYPE1(DB_IPCE_EXCEPTION_UNWIND ,0x0148)
+IPC_EVENT_TYPE1(DB_IPCE_INTERCEPT_EXCEPTION_RESULT ,0x0149)
+IPC_EVENT_TYPE1(DB_IPCE_CREATE_HANDLE_RESULT ,0x014A)
+IPC_EVENT_TYPE1(DB_IPCE_INTERCEPT_EXCEPTION_COMPLETE ,0x014B)
+IPC_EVENT_TYPE1(DB_IPCE_ENC_REMAP_COMPLETE ,0x014C)
+IPC_EVENT_TYPE1(DB_IPCE_CREATE_PROCESS ,0x014D)
+IPC_EVENT_TYPE1(DB_IPCE_ENC_ADD_FUNCTION ,0x014E)
+IPC_EVENT_TYPE1(DB_IPCE_GET_NGEN_COMPILER_FLAGS_RESULT,0x0151)
+IPC_EVENT_TYPE1(DB_IPCE_SET_NGEN_COMPILER_FLAGS_RESULT,0x0152)
+IPC_EVENT_TYPE1(DB_IPCE_MDA_NOTIFICATION ,0x0156)
+IPC_EVENT_TYPE1(DB_IPCE_GET_GCHANDLE_INFO_RESULT ,0x0157)
+IPC_EVENT_TYPE1(DB_IPCE_TEST_CRST ,0x0158)
+IPC_EVENT_TYPE1(DB_IPCE_TEST_RWLOCK ,0x0159)
+IPC_EVENT_TYPE1(DB_IPCE_LEFTSIDE_STARTUP ,0x015C)
+IPC_EVENT_TYPE1(DB_IPCE_METADATA_UPDATE ,0x015D)
+IPC_EVENT_TYPE1(DB_IPCE_RESOLVE_UPDATE_METADATA_1_RESULT,0x015E)
+IPC_EVENT_TYPE1(DB_IPCE_RESOLVE_UPDATE_METADATA_2_RESULT,0x015F)
+IPC_EVENT_TYPE0(DB_IPCE_RUNTIME_LAST ,0x0160) // The last event from runtime
+
+
+
+// Events that travel from the DI to the RC (Right to Left)
+IPC_EVENT_TYPE0(DB_IPCE_DEBUGGER_FIRST ,0x0200) // change to TYPE0 because it is not really an event
+IPC_EVENT_TYPE2(DB_IPCE_ASYNC_BREAK ,0x0200)
+IPC_EVENT_TYPE2(DB_IPCE_CONTINUE ,0x0201)
+IPC_EVENT_TYPE2(DB_IPCE_LIST_THREADS ,0x0202)
+IPC_EVENT_TYPE2(DB_IPCE_SET_IP ,0x0205)
+IPC_EVENT_TYPE2(DB_IPCE_SUSPEND_THREAD ,0x0206)
+IPC_EVENT_TYPE2(DB_IPCE_RESUME_THREAD ,0x0207)
+IPC_EVENT_TYPE2(DB_IPCE_BREAKPOINT_ADD ,0x0209)
+IPC_EVENT_TYPE2(DB_IPCE_BREAKPOINT_REMOVE ,0x020A)
+IPC_EVENT_TYPE2(DB_IPCE_STEP_CANCEL ,0x020B)
+IPC_EVENT_TYPE2(DB_IPCE_STEP ,0x020C)
+IPC_EVENT_TYPE2(DB_IPCE_STEP_OUT ,0x020D)
+IPC_EVENT_TYPE2(DB_IPCE_GET_BUFFER ,0x0211)
+IPC_EVENT_TYPE2(DB_IPCE_RELEASE_BUFFER ,0x0212)
+IPC_EVENT_TYPE2(DB_IPCE_SET_CLASS_LOAD_FLAG ,0x0217)
+IPC_EVENT_TYPE2(DB_IPCE_CONTINUE_EXCEPTION ,0x0219)
+IPC_EVENT_TYPE2(DB_IPCE_ATTACHING ,0x021A)
+IPC_EVENT_TYPE2(DB_IPCE_APPLY_CHANGES ,0x021B)
+IPC_EVENT_TYPE2(DB_IPCE_SET_NGEN_COMPILER_FLAGS ,0x021F)
+IPC_EVENT_TYPE2(DB_IPCE_GET_NGEN_COMPILER_FLAGS ,0x0220)
+IPC_EVENT_TYPE2(DB_IPCE_IS_TRANSITION_STUB ,0x0221)
+IPC_EVENT_TYPE2(DB_IPCE_IS_TRANSITION_STUB_RESULT ,0x0222)
+IPC_EVENT_TYPE2(DB_IPCE_MODIFY_LOGSWITCH ,0x0223)
+IPC_EVENT_TYPE2(DB_IPCE_ENABLE_LOG_MESSAGES ,0x0224)
+IPC_EVENT_TYPE2(DB_IPCE_FUNC_EVAL ,0x0225)
+IPC_EVENT_TYPE2(DB_IPCE_SET_REFERENCE ,0x0228)
+IPC_EVENT_TYPE2(DB_IPCE_FUNC_EVAL_ABORT ,0x022c)
+IPC_EVENT_TYPE2(DB_IPCE_DETACH_FROM_PROCESS ,0x022f)
+IPC_EVENT_TYPE2(DB_IPCE_CONTROL_C_EVENT_RESULT ,0x0230)
+IPC_EVENT_TYPE2(DB_IPCE_FUNC_EVAL_CLEANUP ,0x0231)
+IPC_EVENT_TYPE2(DB_IPCE_SET_ALL_DEBUG_STATE ,0x0232)
+IPC_EVENT_TYPE2(DB_IPCE_SET_VALUE_CLASS ,0x0234)
+IPC_EVENT_TYPE2(DB_IPCE_SET_METHOD_JMC_STATUS ,0x023a)
+IPC_EVENT_TYPE2(DB_IPCE_GET_METHOD_JMC_STATUS ,0x023b)
+IPC_EVENT_TYPE2(DB_IPCE_SET_MODULE_JMC_STATUS ,0x023c)
+IPC_EVENT_TYPE2(DB_IPCE_GET_THREAD_FOR_TASKID ,0x023d)
+IPC_EVENT_TYPE2(DB_IPCE_FUNC_EVAL_RUDE_ABORT ,0x0241)
+IPC_EVENT_TYPE2(DB_IPCE_CREATE_HANDLE ,0x0244)
+IPC_EVENT_TYPE2(DB_IPCE_DISPOSE_HANDLE ,0x0245)
+IPC_EVENT_TYPE2(DB_IPCE_INTERCEPT_EXCEPTION ,0x0246)
+IPC_EVENT_TYPE2(DB_IPCE_DEBUGGER_INVALID ,0x0249) // An invalid event type
+IPC_EVENT_TYPE2(DB_IPCE_GET_GCHANDLE_INFO ,0x0251)
+IPC_EVENT_TYPE2(DB_IPCE_RESOLVE_UPDATE_METADATA_1 ,0x0256)
+IPC_EVENT_TYPE2(DB_IPCE_RESOLVE_UPDATE_METADATA_2 ,0x0257)
+IPC_EVENT_TYPE0(DB_IPCE_DEBUGGER_LAST ,0x0258) // The last event from the debugger
+
diff --git a/src/debug/inc/dbgtargetcontext.h b/src/debug/inc/dbgtargetcontext.h
new file mode 100644
index 0000000000..22b1c84096
--- /dev/null
+++ b/src/debug/inc/dbgtargetcontext.h
@@ -0,0 +1,450 @@
+// 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.
+
+#ifndef __DBG_TARGET_CONTEXT_INCLUDED
+#define __DBG_TARGET_CONTEXT_INCLUDED
+
+#include <dbgportable.h>
+#include <stddef.h>
+#include "crosscomp.h"
+
+//
+// The right side of the debugger can now be built to target multiple platforms. This means it is no longer
+// safe to use the CONTEXT structure directly: the context of the platform we're building for might not match
+// that of the one the debugger is targetting. So now all right side code will use the DT_CONTEXT abstraction
+// instead. When the debugger target is the local platform this will just resolve back into CONTEXT, but cross
+// platform we'll provide a hand-rolled version.
+//
+
+//
+// For cross platform cases we also need to provide a helper function for byte-swapping a context structure
+// should the endian-ness of the debugger and debuggee platforms differ. This is called ByteSwapContext and is
+// obviously a no-op for those cases where the left and right sides agree on storage format.
+//
+// NOTE: Any changes to the field layout of DT_CONTEXT must be tracked in the associated definition of
+// ByteSwapContext.
+//
+
+// For now, the only cross-platform CONTEXTs we support are x86/PAL and ARM/Win. Look in
+// rotor/pal/inc/rotor_pal.h for the original PAL definitions.
+
+//
+// **** NOTE: Keep these in sync with rotor/pal/inc/rotor_pal.h ****
+//
+
+// This odd define pattern is needed because in DBI we set _TARGET_ to match the host and
+// DBG_TARGET to control our targeting. In x-plat DBI DBG_TARGET won't match _TARGET_ and
+// DBG_TARGET needs to take precedence
+#if defined(DBG_TARGET_X86)
+#define DTCONTEXT_IS_X86
+#elif defined (DBG_TARGET_AMD64)
+#define DTCONTEXT_IS_AMD64
+#elif defined (DBG_TARGET_ARM)
+#define DTCONTEXT_IS_ARM
+#elif defined (DBG_TARGET_ARM64)
+#define DTCONTEXT_IS_ARM64
+#elif defined (_TARGET_X86_)
+#define DTCONTEXT_IS_X86
+#elif defined (_TARGET_AMD64_)
+#define DTCONTEXT_IS_AMD64
+#elif defined (_TARGET_ARM_)
+#define DTCONTEXT_IS_ARM
+#elif defined (_TARGET_ARM64_)
+#define DTCONTEXT_IS_ARM64
+#endif
+
+#if defined(DTCONTEXT_IS_X86)
+
+#define DT_SIZE_OF_80387_REGISTERS 80
+
+#define DT_CONTEXT_i386 0x00010000
+#define DT_CONTEXT_CONTROL (DT_CONTEXT_i386 | 0x00000001L) // SS:SP, CS:IP, FLAGS, BP
+#define DT_CONTEXT_INTEGER (DT_CONTEXT_i386 | 0x00000002L) // AX, BX, CX, DX, SI, DI
+#define DT_CONTEXT_SEGMENTS (DT_CONTEXT_i386 | 0x00000004L)
+#define DT_CONTEXT_FLOATING_POINT (DT_CONTEXT_i386 | 0x00000008L) // 387 state
+#define DT_CONTEXT_DEBUG_REGISTERS (DT_CONTEXT_i386 | 0x00000010L)
+
+#define DT_CONTEXT_FULL (DT_CONTEXT_CONTROL | DT_CONTEXT_INTEGER | DT_CONTEXT_SEGMENTS)
+#define DT_CONTEXT_EXTENDED_REGISTERS (DT_CONTEXT_i386 | 0x00000020L)
+
+#define DT_MAXIMUM_SUPPORTED_EXTENSION 512
+
+typedef struct {
+ DWORD ControlWord;
+ DWORD StatusWord;
+ DWORD TagWord;
+ DWORD ErrorOffset;
+ DWORD ErrorSelector;
+ DWORD DataOffset;
+ DWORD DataSelector;
+ BYTE RegisterArea[DT_SIZE_OF_80387_REGISTERS];
+ DWORD Cr0NpxState;
+} DT_FLOATING_SAVE_AREA;
+
+typedef struct {
+ ULONG ContextFlags;
+
+ ULONG Dr0;
+ ULONG Dr1;
+ ULONG Dr2;
+ ULONG Dr3;
+ ULONG Dr6;
+ ULONG Dr7;
+
+ DT_FLOATING_SAVE_AREA FloatSave;
+
+ ULONG SegGs;
+ ULONG SegFs;
+ ULONG SegEs;
+ ULONG SegDs;
+
+ ULONG Edi;
+ ULONG Esi;
+ ULONG Ebx;
+ ULONG Edx;
+ ULONG Ecx;
+ ULONG Eax;
+
+ ULONG Ebp;
+ ULONG Eip;
+ ULONG SegCs;
+ ULONG EFlags;
+ ULONG Esp;
+ ULONG SegSs;
+
+ UCHAR ExtendedRegisters[DT_MAXIMUM_SUPPORTED_EXTENSION];
+
+} DT_CONTEXT;
+
+// Since the target is little endian in this case we only have to provide a real implementation of
+// ByteSwapContext if the platform we're building on is big-endian.
+#ifdef BIGENDIAN
+inline void ByteSwapContext(DT_CONTEXT *pContext)
+{
+ // Our job is simplified since the context has large contiguous ranges with fields of the same size. Keep
+ // the following logic in sync with the definition of DT_CONTEXT above.
+ BYTE *pbContext = (BYTE*)pContext;
+
+ // The first span consists of 4 byte fields.
+ DWORD cbFields = (offsetof(DT_CONTEXT, FloatSave) + offsetof(DT_FLOATING_SAVE_AREA, RegisterArea)) / 4;
+ for (DWORD i = 0; i < cbFields; i++)
+ {
+ ByteSwapPrimitive(pbContext, pbContext, 4);
+ pbContext += 4;
+ }
+
+ // Then there's a float save area containing 8 byte fields.
+ cbFields = sizeof(pContext->FloatSave.RegisterArea);
+ for (DWORD i = 0; i < cbFields; i++)
+ {
+ ByteSwapPrimitive(pbContext, pbContext, 8);
+ pbContext += 8;
+ }
+
+ // Back to 4 byte fields.
+ cbFields = (offsetof(DT_CONTEXT, ExtendedRegisters) - offsetof(DT_CONTEXT, SegGs)) / 4;
+ for (DWORD i = 0; i < cbFields; i++)
+ {
+ ByteSwapPrimitive(pbContext, pbContext, 4);
+ pbContext += 4;
+ }
+
+ // We don't know the formatting of the extended register area, but the debugger doesn't access this data
+ // on the left side, so just leave it in left-side format for now.
+
+ // Validate that we converted up to where we think we did as a hedge against DT_CONTEXT layout changes.
+ _PASSERT((pbContext - ((BYTE*)pContext)) == (sizeof(DT_CONTEXT) - sizeof(pContext->ExtendedRegisters)));
+}
+#else // BIGENDIAN
+inline void ByteSwapContext(DT_CONTEXT *pContext)
+{
+}
+#endif // BIGENDIAN
+
+#elif defined(DTCONTEXT_IS_AMD64)
+
+#define DT_CONTEXT_AMD64 0x00100000L
+
+#define DT_CONTEXT_CONTROL (DT_CONTEXT_AMD64 | 0x00000001L)
+#define DT_CONTEXT_INTEGER (DT_CONTEXT_AMD64 | 0x00000002L)
+#define DT_CONTEXT_SEGMENTS (DT_CONTEXT_AMD64 | 0x00000004L)
+#define DT_CONTEXT_FLOATING_POINT (DT_CONTEXT_AMD64 | 0x00000008L)
+#define DT_CONTEXT_DEBUG_REGISTERS (DT_CONTEXT_AMD64 | 0x00000010L)
+
+#define DT_CONTEXT_FULL (DT_CONTEXT_CONTROL | DT_CONTEXT_INTEGER | DT_CONTEXT_FLOATING_POINT)
+#define DT_CONTEXT_ALL (DT_CONTEXT_CONTROL | DT_CONTEXT_INTEGER | DT_CONTEXT_SEGMENTS | DT_CONTEXT_FLOATING_POINT | DT_CONTEXT_DEBUG_REGISTERS)
+
+typedef struct {
+ ULONGLONG Low;
+ LONGLONG High;
+} DT_M128A;
+
+typedef struct {
+ WORD ControlWord;
+ WORD StatusWord;
+ BYTE TagWord;
+ BYTE Reserved1;
+ WORD ErrorOpcode;
+ DWORD ErrorOffset;
+ WORD ErrorSelector;
+ WORD Reserved2;
+ DWORD DataOffset;
+ WORD DataSelector;
+ WORD Reserved3;
+ DWORD MxCsr;
+ DWORD MxCsr_Mask;
+ DT_M128A FloatRegisters[8];
+ DT_M128A XmmRegisters[16];
+ BYTE Reserved4[96];
+} DT_XMM_SAVE_AREA32;
+
+typedef struct DECLSPEC_ALIGN(16) {
+
+ DWORD64 P1Home;
+ DWORD64 P2Home;
+ DWORD64 P3Home;
+ DWORD64 P4Home;
+ DWORD64 P5Home;
+ DWORD64 P6Home;
+
+ DWORD ContextFlags;
+ DWORD MxCsr;
+
+ WORD SegCs;
+ WORD SegDs;
+ WORD SegEs;
+ WORD SegFs;
+ WORD SegGs;
+ WORD SegSs;
+ DWORD EFlags;
+
+ DWORD64 Dr0;
+ DWORD64 Dr1;
+ DWORD64 Dr2;
+ DWORD64 Dr3;
+ DWORD64 Dr6;
+ DWORD64 Dr7;
+
+ DWORD64 Rax;
+ DWORD64 Rcx;
+ DWORD64 Rdx;
+ DWORD64 Rbx;
+ DWORD64 Rsp;
+ DWORD64 Rbp;
+ DWORD64 Rsi;
+ DWORD64 Rdi;
+ DWORD64 R8;
+ DWORD64 R9;
+ DWORD64 R10;
+ DWORD64 R11;
+ DWORD64 R12;
+ DWORD64 R13;
+ DWORD64 R14;
+ DWORD64 R15;
+
+ DWORD64 Rip;
+
+ union {
+ DT_XMM_SAVE_AREA32 FltSave;
+ struct {
+ DT_M128A Header[2];
+ DT_M128A Legacy[8];
+ DT_M128A Xmm0;
+ DT_M128A Xmm1;
+ DT_M128A Xmm2;
+ DT_M128A Xmm3;
+ DT_M128A Xmm4;
+ DT_M128A Xmm5;
+ DT_M128A Xmm6;
+ DT_M128A Xmm7;
+ DT_M128A Xmm8;
+ DT_M128A Xmm9;
+ DT_M128A Xmm10;
+ DT_M128A Xmm11;
+ DT_M128A Xmm12;
+ DT_M128A Xmm13;
+ DT_M128A Xmm14;
+ DT_M128A Xmm15;
+ };
+ };
+
+ DT_M128A VectorRegister[26];
+ DWORD64 VectorControl;
+
+ DWORD64 DebugControl;
+ DWORD64 LastBranchToRip;
+ DWORD64 LastBranchFromRip;
+ DWORD64 LastExceptionToRip;
+ DWORD64 LastExceptionFromRip;
+} DT_CONTEXT;
+
+#elif defined(DTCONTEXT_IS_ARM)
+
+#define DT_CONTEXT_ARM 0x00200000L
+
+#define DT_CONTEXT_CONTROL (DT_CONTEXT_ARM | 0x1L)
+#define DT_CONTEXT_INTEGER (DT_CONTEXT_ARM | 0x2L)
+#define DT_CONTEXT_FLOATING_POINT (DT_CONTEXT_ARM | 0x4L)
+#define DT_CONTEXT_DEBUG_REGISTERS (DT_CONTEXT_ARM | 0x8L)
+
+#define DT_CONTEXT_FULL (DT_CONTEXT_CONTROL | DT_CONTEXT_INTEGER | DT_CONTEXT_FLOATING_POINT)
+#define DT_CONTEXT_ALL (DT_CONTEXT_CONTROL | DT_CONTEXT_INTEGER | DT_CONTEXT_FLOATING_POINT | DT_CONTEXT_DEBUG_REGISTERS)
+
+#define DT_ARM_MAX_BREAKPOINTS 8
+#define DT_ARM_MAX_WATCHPOINTS 1
+
+typedef struct {
+ ULONGLONG Low;
+ LONGLONG High;
+} DT_NEON128;
+
+typedef DECLSPEC_ALIGN(8) struct {
+
+ //
+ // Control flags.
+ //
+
+ DWORD ContextFlags;
+
+ //
+ // Integer registers
+ //
+
+ DWORD R0;
+ DWORD R1;
+ DWORD R2;
+ DWORD R3;
+ DWORD R4;
+ DWORD R5;
+ DWORD R6;
+ DWORD R7;
+ DWORD R8;
+ DWORD R9;
+ DWORD R10;
+ DWORD R11;
+ DWORD R12;
+
+ //
+ // Control Registers
+ //
+
+ DWORD Sp;
+ DWORD Lr;
+ DWORD Pc;
+ DWORD Cpsr;
+
+ //
+ // Floating Point/NEON Registers
+ //
+
+ DWORD Fpscr;
+ DWORD Padding;
+ union {
+ DT_NEON128 Q[16];
+ ULONGLONG D[32];
+ DWORD S[32];
+ } DUMMYUNIONNAME;
+
+ //
+ // Debug registers
+ //
+
+ DWORD Bvr[DT_ARM_MAX_BREAKPOINTS];
+ DWORD Bcr[DT_ARM_MAX_BREAKPOINTS];
+ DWORD Wvr[DT_ARM_MAX_WATCHPOINTS];
+ DWORD Wcr[DT_ARM_MAX_WATCHPOINTS];
+
+ DWORD Padding2[2];
+
+} DT_CONTEXT;
+
+#elif defined(DTCONTEXT_IS_ARM64)
+
+#define DT_CONTEXT_ARM64 0x00400000L
+
+#define DT_CONTEXT_CONTROL (DT_CONTEXT_ARM64 | 0x1L)
+#define DT_CONTEXT_INTEGER (DT_CONTEXT_ARM64 | 0x2L)
+#define DT_CONTEXT_FLOATING_POINT (DT_CONTEXT_ARM64 | 0x4L)
+#define DT_CONTEXT_DEBUG_REGISTERS (DT_CONTEXT_ARM64 | 0x8L)
+
+#define DT_CONTEXT_FULL (DT_CONTEXT_CONTROL | DT_CONTEXT_INTEGER | DT_CONTEXT_FLOATING_POINT)
+#define DT_CONTEXT_ALL (DT_CONTEXT_CONTROL | DT_CONTEXT_INTEGER | DT_CONTEXT_FLOATING_POINT | DT_CONTEXT_DEBUG_REGISTERS)
+
+typedef DECLSPEC_ALIGN(16) struct {
+ //
+ // Control flags.
+ //
+
+ /* +0x000 */ DWORD ContextFlags;
+
+ //
+ // Integer registers
+ //
+
+ /* +0x004 */ DWORD Cpsr; // NZVF + DAIF + CurrentEL + SPSel
+ /* +0x008 */ union {
+ struct {
+ DWORD64 X0;
+ DWORD64 X1;
+ DWORD64 X2;
+ DWORD64 X3;
+ DWORD64 X4;
+ DWORD64 X5;
+ DWORD64 X6;
+ DWORD64 X7;
+ DWORD64 X8;
+ DWORD64 X9;
+ DWORD64 X10;
+ DWORD64 X11;
+ DWORD64 X12;
+ DWORD64 X13;
+ DWORD64 X14;
+ DWORD64 X15;
+ DWORD64 X16;
+ DWORD64 X17;
+ DWORD64 X18;
+ DWORD64 X19;
+ DWORD64 X20;
+ DWORD64 X21;
+ DWORD64 X22;
+ DWORD64 X23;
+ DWORD64 X24;
+ DWORD64 X25;
+ DWORD64 X26;
+ DWORD64 X27;
+ DWORD64 X28;
+ };
+ DWORD64 X[29];
+ };
+ /* +0x0f0 */ DWORD64 Fp;
+ /* +0x0f8 */ DWORD64 Lr;
+ /* +0x100 */ DWORD64 Sp;
+ /* +0x108 */ DWORD64 Pc;
+
+ //
+ // Floating Point/NEON Registers
+ //
+
+ /* +0x110 */ NEON128 V[32];
+ /* +0x310 */ DWORD Fpcr;
+ /* +0x314 */ DWORD Fpsr;
+
+ //
+ // Debug registers
+ //
+
+ /* +0x318 */ DWORD Bcr[ARM64_MAX_BREAKPOINTS];
+ /* +0x338 */ DWORD64 Bvr[ARM64_MAX_BREAKPOINTS];
+ /* +0x378 */ DWORD Wcr[ARM64_MAX_WATCHPOINTS];
+ /* +0x380 */ DWORD64 Wvr[ARM64_MAX_WATCHPOINTS];
+ /* +0x390 */
+
+} DT_CONTEXT;
+
+#else
+#error Unsupported platform
+#endif
+
+
+#endif // __DBG_TARGET_CONTEXT_INCLUDED
diff --git a/src/debug/inc/dbgtransportsession.h b/src/debug/inc/dbgtransportsession.h
new file mode 100644
index 0000000000..5187202753
--- /dev/null
+++ b/src/debug/inc/dbgtransportsession.h
@@ -0,0 +1,849 @@
+// 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.
+
+
+#ifndef __DBG_TRANSPORT_SESSION_INCLUDED
+#define __DBG_TRANSPORT_SESSION_INCLUDED
+
+#ifndef RIGHT_SIDE_COMPILE
+#include <utilcode.h>
+#include <crst.h>
+
+#endif // !RIGHT_SIDE_COMPILE
+
+#if defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI)
+
+#include <twowaypipe.h>
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ DbgTransportSession was originally designed around cross-machine debugging via sockets and it is supposed to
+ handle network interruptions. Right now we use pipes (see TwoWaypipe) and don't expect to have connection issues.
+ But there seem to be no good reason to try hard to get rid of existing working protocol even if it's a bit
+ cautious about connection quality. So please KEEP IN MIND THAT SOME COMMENTS REFERING TO NETWORK AND SOCKETS
+ CAN BE OUTDATED.
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+//
+// Provides a robust and secure transport session between a debugger and a debuggee that are potentially on
+// different machines.
+//
+// The following terminology is used for the wire protocol. The smallest meaningful entity written to or read
+// from the connection is a "message". This consists of one or maybe two "blocks" where a block is a
+// contiguous region of memory in the host machine. The first block is always a "message header" which is
+// fixed size (allowing the receiver to know how many bytes to read off the stream oriented connection) and
+// has type codes and other fields which the receiver can use to determine if another block is part of the
+// message (and if so, exactly how large that block is). Many management messages consist only of a message
+// header block, while operations such as sending a debugger event structure involve a message header followed
+// by a block containing the actual event structure.
+//
+// Message acknowledgement (sometimes abbreviated to ack) refers to a system of marking all messages with an
+// ID and noting and reporting which IDs we've seen from our peer. We piggy back the highest seen ID on all
+// outgoing messages and this is used by the infrastructure to communicate the fact that a sender can release
+// its copy of an outbound message since it successfully made it across the communications channel and won't
+// need to be resent in the case of a network failure.
+//
+// This file uses the debugger conventions for naming the two endpoints of the session: the left side or LS is
+// the side with the runtime while the right side (RS) is the side with the debugger.
+//
+
+// The structure of this file necessitates a certain number of forward references (particularly in the
+// comments). If you see a term you don't understand please do a search for it further down the file, where
+// hopefully you will find a detailed definition (and if not, please add one).
+
+struct DebuggerIPCEvent;
+struct DbgEventBufferEntry;
+
+// Some simple ad-hoc debug only transport logging. This output is too chatty for an exisitng CLR logging
+// channel (and we've run out of bits for an additional channel) and is likely to be of limited use to anyone
+// besides the transport developer (and even then only occasionally).
+//
+// To enable use 'set|export COMPlus_DbgTransportLog=X' where X is 1 for RS logging, 2 for LS logging and 3
+// for both (default is disabled). Use 'set|export COMPlus_DbgTransportLogClass=X' where X is the hex
+// representation of one or more DbgTransportLogClass flags defined below (default is all classes enabled).
+// For instance, 'set COMPlus_DbgTransportLogClass=f' will enable only message send and receive logging (for
+// all message types).
+enum DbgTransportLogEnable
+{
+ LE_None = 0x00000000,
+ LE_LeftSide = 0x00000001,
+ LE_RightSide = 0x00000002,
+ LE_Unknown = 0xffffffff,
+};
+
+enum DbgTransportLogClass
+{
+ LC_None = 0x00000000,
+ LC_Events = 0x00000001, // Sending and receiving debugger events
+ LC_Session = 0x00000002, // Sending and receiving session messages
+ LC_Requests = 0x00000004, // Sending requests such as MT_GetDCB and receiving replies
+ LC_EventAcks = 0x00000008, // Sending and receiving debugger event acks (DEPRECATED)
+ LC_NetErrors = 0x00000010, // Network errors
+ LC_FaultInject = 0x00000020, // Artificially injected network faults
+ LC_Proxy = 0x00000040, // Proxy interactions
+ LC_All = 0xffffffff,
+ LC_Always = 0xffffffff, // Always log, regardless of class setting
+};
+
+// Status codes that can be returned by various APIs that indicate some conditions of the error that a caller
+// might usefully pass on to a user (environmental factors that the user might have some control over).
+enum ConnStatus
+{
+ SCS_Success, // The request succeeded
+ SCS_OutOfMemory, // The request failed due to a low memory situation
+ SCS_InvalidConfiguration, // Initialize() failed because the debugger settings were not configured or
+ // have become corrupt
+ SCS_UnknownTarget, // Connect() failed because the remote machine at the given address could not
+ // be found
+ SCS_NoListener, // Connect() failed because the remote machine was not listening for requests
+ // on the given port (most likely the remote machine is not configured for
+ // debugging)
+ SCS_NetworkFailure, // Connect() failed due to miscellaneous network error
+ SCS_MismatchedCerts, // Connect()/Accept() failed because the remote party was using a different
+ // cert
+};
+
+
+// Multiple clients can use a single DbgTransportSession, but only one can act as the debugger.
+// A valid DebugTicket is given to the client who is acting as the debugger.
+struct DebugTicket
+{
+friend class DbgTransportSession;
+
+public:
+ DebugTicket() { m_fValid = false; };
+
+ bool IsValid() { return m_fValid; };
+
+protected:
+ void SetValid() { m_fValid = true; };
+ void SetInvalid() { m_fValid = false; };
+
+private:
+ // Tickets can't be copied around. Hide these definitions so as to enforce that.
+ // We still need the Copy ctor so that it can be passed in as a parameter.
+ void operator=(DebugTicket & other);
+
+ bool m_fValid;
+};
+
+#ifdef RIGHT_SIDE_COMPILE
+#define DBG_TRANSPORT_LOG_THIS_SIDE LE_RightSide
+#define DBG_TRANSPORT_LOG_PREFIX "RS"
+#else // RIGHT_SIDE_COMPILE
+#define DBG_TRANSPORT_LOG_THIS_SIDE LE_LeftSide
+#define DBG_TRANSPORT_LOG_PREFIX "LS"
+#endif // RIGHT_SIDE_COMPILE
+
+// Method used to log an interesting event (of the given class). The message given will have any additional
+// arguments inserted following 'printf' formatiing conventions and will be automatically prepended with a
+// LS/RS indicator and suffixed with a newline.
+inline void DbgTransportLog(DbgTransportLogClass eClass, const char *szFormat, ...)
+{
+#ifdef _DEBUG
+ static DWORD s_dwLoggingEnabled = LE_Unknown;
+ static DWORD s_dwLoggingClass = LC_All;
+
+ if (s_dwLoggingEnabled == LE_Unknown)
+ {
+ s_dwLoggingEnabled = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgTransportLog, LE_None);
+ s_dwLoggingClass = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgTransportLogClass, LC_All);
+ }
+
+ if ((s_dwLoggingEnabled & DBG_TRANSPORT_LOG_THIS_SIDE) &&
+ ((s_dwLoggingClass & eClass) || eClass == LC_Always))
+ {
+ char szOutput[256];
+ va_list args;
+
+ va_start(args, szFormat);
+ vsprintf_s(szOutput, sizeof(szOutput), szFormat, args);
+ va_end(args);
+
+ printf("%s %04x: %s\n", DBG_TRANSPORT_LOG_PREFIX, GetCurrentThreadId(), szOutput);
+ fflush(stdout);
+
+ char szDebugOutput[512];
+ sprintf_s(szDebugOutput, sizeof(szDebugOutput), "%s: %s\n", DBG_TRANSPORT_LOG_PREFIX, szOutput);
+ OutputDebugStringA(szDebugOutput);
+ }
+#endif // _DEBUG
+}
+
+#ifdef _DEBUG
+//
+// Debug-only network fault injection (in order to help test the robust session code). Control is via a single
+// DWORD read from the environment (COMPlus_DbgTransportFaultInject). This DWORD is treated as a set of bit
+// fields as follows:
+//
+// +-------+-------+-------+----------------+-----------+
+// | Side | Op | State | Reserved | Frequency |
+// +-------+-------+-------+----------------+-----------+
+// 31<->28 27<->24 23<->20 19<----------->8 7<------->0
+//
+// The 'Side' field indicates whether the left or right side (or both) should have faults injected. See
+// DbgTransportFaultSide below for values.
+//
+// The 'Op' field indicates which connection methods should simulate failures. See DbgTransportFaultOp.
+//
+// The 'State' field indicates the session states in which faults will be injected. See
+// DbgTransportFaultState. Note that introducing too many failures into the Opening and Opening_NC states will
+// cause the debugger to timeout and fail.
+//
+// The 'Reserved' field has no current meaning and should be left as zero.
+//
+// The 'Frequency' field indicates a percentage failure rate. Valid values are between 0 and 99, values beyond
+// this range will be clamped to 99.
+//
+// For example:
+//
+// export COMPlus_DbgTransportFaultInject=1ff00001
+// --> Fail all network operations on the left side 1% of the time
+//
+// export COMPlus_DbgTransportFaultInject=34200063
+// --> Fail Send() calls on both sides while the session is Open 99% of the time
+//
+
+#define DBG_TRANSPORT_FAULT_RATE_MASK 0x000000ff
+
+// Whether to inject faults to the left, right or both sides.
+enum DbgTransportFaultSide
+{
+ FS_Left = 0x10000000,
+ FS_Right = 0x20000000,
+};
+
+// Network operations which are candiates for fault injection.
+enum DbgTransportFaultOp
+{
+ FO_Connect = 0x01000000,
+ FO_Accept = 0x02000000,
+ FO_Send = 0x04000000,
+ FO_Receive = 0x08000000,
+};
+
+// Session states into which faults should be injected.
+enum DbgTransportFaultState
+{
+ FS_Opening = 0x00100000, // Opening and Opening_NC
+ FS_Open = 0x00200000,
+ FS_Resync = 0x00400000, // Resync and Resync_NC
+};
+
+#ifdef RIGHT_SIDE_COMPILE
+#define DBG_TRANSPORT_FAULT_THIS_SIDE FS_Right
+#else // RIGHT_SIDE_COMPILE
+#define DBG_TRANSPORT_FAULT_THIS_SIDE FS_Left
+#endif // RIGHT_SIDE_COMPILE
+
+// Macro to determine whether a fault should be injected for the given operation.
+#define DBG_TRANSPORT_SHOULD_INJECT_FAULT(_op) DbgTransportShouldInjectFault(FO_##_op, #_op)
+
+#else // _DEBUG
+#define DBG_TRANSPORT_SHOULD_INJECT_FAULT(_op) false
+#endif // _DEBUG
+
+// The PAL doesn't define htons (host-to-network-short) and friends. So provide our own versions here.
+// winsock2.h defines BIGENDIAN to 0x0000 and LITTLEENDIAN to 0x0001, so we need to be careful with the
+// #ifdef.
+#if BIGENDIAN > 0
+#define DBGIPC_HTONS(x) (x)
+#define DBGIPC_NTOHS(x) (x)
+#define DBGIPC_HTONL(x) (x)
+#define DBGIPC_NTOHL(x) (x)
+#else
+inline UINT16 DBGIPC_HTONS(UINT16 x)
+{
+ return (x >> 8) | (x << 8);
+}
+#define DBGIPC_NTOHS(x) DBGIPC_HTONS(x)
+inline UINT32 DBGIPC_HTONL(UINT32 x)
+{
+ return (x >> 24) |
+ ((x >> 8) & 0x0000FF00L) |
+ ((x & 0x0000FF00L) << 8) |
+ (x << 24);
+}
+#define DBGIPC_NTOHL(x) DBGIPC_HTONL(x)
+
+#endif
+
+// Lock abstraction (we can't use the same lock implementation on LS and RS since we really want a Crst on the
+// LS and this isn't available in the RS environment).
+class DbgTransportLock
+{
+public:
+ void Init();
+ void Destroy();
+ void Enter();
+ void Leave();
+
+private:
+#ifdef RIGHT_SIDE_COMPILE
+ CRITICAL_SECTION m_sLock;
+#else // RIGHT_SIDE_COMPILE
+ CrstExplicitInit m_sLock;
+#endif // RIGHT_SIDE_COMPILE
+};
+
+// The transport has only one queue for IPC events, but each IPC event can be marked as one of two types.
+// The transport will signal the handle corresponding to the type of each IPC event. (See
+// code:DbgTransportSession::GetIPCEventReadyEvent and code:DbgTransportSession::GetDebugEventReadyEvent.)
+// This is effectively a basic multiplexing scheme. The old-style IPC event are for all RS-to-LS IPC events
+// and for all LS-to-RS replies. The other type is for LS-to-RS IPC events transported over the native
+// pipeline. For more information, see the comments for the interface code:IEventChannel.
+enum IPCEventType
+{
+ IPCET_OldStyle,
+ IPCET_DebugEvent,
+ IPCET_Max,
+};
+
+// The class that encapsulates all the state for a single session on either the right or left side. The left
+// side supports only one instance of this class for a given runtime. The right side can support several (all
+// connected to different LS instances of course).
+class DbgTransportSession
+{
+public:
+ // No real work done in the constructor. Use Init() instead.
+ DbgTransportSession();
+
+ // Cleanup what is allocated/created in Init()
+ ~DbgTransportSession();
+
+ // Allocates initial resources (including starting the transport thread). The session will start in the
+ // SS_Opening state. That is, the RS will immediately start trying to Connect() a connection while the
+ // LS will perform an Accept() to wait for a connection request. The RS needs an IP address and port
+ // number to initiate connections. These should be given in host byte order. The LS, on the other hand,
+ // requires the addresses of a couple of runtime data structures to service certain debugger requests that
+ // may be delivered once the session is established.
+#ifdef RIGHT_SIDE_COMPILE
+ HRESULT Init(DWORD pid, HANDLE hProcessExited);
+#else
+ HRESULT Init(DebuggerIPCControlBlock * pDCB, AppDomainEnumerationIPCBlock * pADB);
+#endif // RIGHT_SIDE_COMPILE
+
+ // Drive the session to the SS_Closed state, which will deallocate all remaining transport resources
+ // (including terminating the transport thread). If this is the RS and the session state is SS_Open at the
+ // time of this call a graceful disconnect will be attempted (which tells the LS to go back to SS_Opening
+ // to look for a new RS rather than interpreting the disconnection as a temporary error and going into
+ // SS_Resync). On either side the session will no longer be functional after this call returns (though
+ // Init() may be called again to start over from the beginning).
+ void Shutdown();
+
+#ifdef RIGHT_SIDE_COMPILE
+ // Used by debugger side (RS) to cleanup the target (LS) named pipes
+ // and semaphores when the debugger detects the debuggee process exited.
+ void CleanupTargetProcess();
+#else
+ // Cleans up the named pipe connection so no tmp files are left behind. Does only
+ // the minimum and must be safe to call at any time. Called during PAL ExitProcess,
+ // TerminateProcess and for unhandled native exceptions and asserts.
+ void AbortConnection();
+#endif // RIGHT_SIDE_COMPILE
+
+ LONG AddRef()
+ {
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+ }
+
+ LONG Release()
+ {
+ _ASSERTE(m_ref > 0);
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+ }
+
+#ifndef RIGHT_SIDE_COMPILE
+ // API used only by the LS to drive the transport into a state where it won't accept connections. This is
+ // used when no proxy is detected at startup but it's too late to shutdown all of the debugging system
+ // easily. It's mainly paranoia to increase the protection of your system when the proxy isn't started.
+ void Neuter();
+#endif // !RIGHT_SIDE_COMPILE
+
+#ifdef RIGHT_SIDE_COMPILE
+ // On the RS it may be useful to wait and see if the session can reach the SS_Open state. If the target
+ // runtime has terminated for some reason then we'll never reach the open state. So the method below gives
+ // the RS a way to try and establish a connection for a reasonable amount of time and to time out
+ // otherwise. They could then call Shutdown on the session and report an error back to the rest of the
+ // debugger. The method returns true if the session opened within the time given (in milliseconds) and
+ // false otherwise.
+ bool WaitForSessionToOpen(DWORD dwTimeout);
+
+ // A valid ticket is returned if no other client is currently acting as the debugger.
+ bool UseAsDebugger(DebugTicket * pTicket);
+
+ // A valid ticket is required in order for this function to succeed. After this function succeeds,
+ // another client can request to be the debugger.
+ bool StopUsingAsDebugger(DebugTicket * pTicket);
+#endif // RIGHT_SIDE_COMPILE
+
+ // Sends a pre-initialized event to the other side.
+ HRESULT SendEvent(DebuggerIPCEvent * pEvent);
+ HRESULT SendDebugEvent(DebuggerIPCEvent * pEvent);
+
+ // Retrieves the auto-reset handle which is signalled by the session each time a new event is received
+ // from the other side.
+ HANDLE GetIPCEventReadyEvent();
+ HANDLE GetDebugEventReadyEvent();
+
+ // Copies the last event received from the other side into the provided buffer. This should only be called
+ // (once) after the event returned from GetIPCEventReadyEvent()/GetDebugEventReadyEvent() has been signalled.
+ void GetNextEvent(DebuggerIPCEvent *pEvent, DWORD cbEvent);
+
+#ifdef RIGHT_SIDE_COMPILE
+ // Read and write memory on the LS from the RS.
+ HRESULT ReadMemory(PBYTE pbRemoteAddress, PBYTE pbBuffer, SIZE_T cbBuffer);
+ HRESULT WriteMemory(PBYTE pbRemoteAddress, PBYTE pbBuffer, SIZE_T cbBuffer);
+ HRESULT VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context);
+
+ // Read and write the debugger control block on the LS from the RS.
+ HRESULT GetDCB(DebuggerIPCControlBlock *pDCB);
+ HRESULT SetDCB(DebuggerIPCControlBlock *pDCB);
+
+ // Read the AppDomain control block on the LS from the RS.
+ HRESULT GetAppDomainCB(AppDomainEnumerationIPCBlock *pADB);
+
+#endif // RIGHT_SIDE_COMPILE
+
+private:
+
+ // Highest protocol version supported by this side of the session. See the
+ // m_dwMajorVersion/m_dwMinorVersion fields for a detailed explanation and the actual version being used
+ // by the session (if it is formed).
+ static const DWORD kCurrentMajorVersion = 2;
+ static const DWORD kCurrentMinorVersion = 0;
+
+ // Session states. These determine which action is taken on a SendMessage (message is sent, queued or an
+ // error is raised) and which incoming messages are valid.
+ enum SessionState
+ {
+ SS_Closed, // No session and no attempt is being made to form one
+ SS_Opening_NC, // Session is being formed but no connection is established yet
+ SS_Opening, // Session is being formed, the low level connection is in place
+ SS_Open, // Session is fully formed and normal transport messages can be sent and received
+ SS_Resync_NC, // A low level connection error is occurred and we're attempting to re-form the link
+ SS_Resync, // We're trying to resynchronize high level state over the new connection
+ };
+
+ // Types of messages that can be sent over the transport connection.
+ enum MessageType
+ {
+ // Session management operations. These must come first and MT_SessionClose must be last in the group.
+ MT_SessionRequest, // RS -> LS : Request a new session be formed (optionally pass encrypted data key)
+ MT_SessionAccept, // LS -> RS : Accept new session
+ MT_SessionReject, // LS -> RS : Reject new session, give reason
+ MT_SessionResync, // RS <-> LS : Resync broken connection by informing other side which messages must be resent
+ MT_SessionClose, // RS -> LS : Gracefully terminate a session
+
+ // Debugger events.
+ MT_Event, // RS <-> LS : A debugger event is being sent as the data block of the message
+
+ // Misc management operations.
+ MT_ReadMemory, // RS <-> LS : RS wants to read LS memory block (or LS is replying to such a request)
+ MT_WriteMemory, // RS <-> LS : RS wants to write LS memory block (or LS is replying to such a request)
+ MT_VirtualUnwind, // RS <-> LS : RS wants to LS unwind a stack frame (or LS is replying to such a request)
+ MT_GetDCB, // RS <-> LS : RS wants to read LS DCB (or LS is replying to such a request)
+ MT_SetDCB, // RS <-> LS : RS wants to write LS DCB (or LS is replying to such a request)
+ MT_GetAppDomainCB, // RS <-> LS : RS wants to read LS AppDomainCB (or LS is replying to such a request)
+ };
+
+ // Reasons the LS can give for rejecting a session. These codes should *not* be changed other than by
+ // adding reasons to keep versioning possible.
+ enum RejectReason
+ {
+ RR_IncompatibleVersion, // LS doesn't support the major version asked for in the request.
+ RR_AlreadyAttached, // LS already has another session open (LS only supports one session at a time)
+ };
+
+ // Struct that defines the format of a message header block sent on the connection. Note that the size of
+ // this structure and the location/size of the m_eType field must *never* change to allow our versioning
+ // protocol to work properly (in particular any LS must be able to interpret at least the type and version
+ // number of an MT_SessionRequest and reply with a MT_SessionReject that any RS can interpret the type and
+ // version of). To help with this there is a padding field at the end for future expansion (this should be
+ // initialized to zero and not accessed in any other manner).
+ struct MessageHeader
+ {
+ Portable<MessageType> m_eType; // Type of message this is
+ Portable<DWORD> m_cbDataBlock; // Size of data block that immediately follows this header (can be zero)
+ Portable<DWORD> m_dwId; // Message ID assigned by the sender of this message
+ Portable<DWORD> m_dwReplyId; // Message ID that this is a reply to (used by messages such as MT_GetDCB)
+ Portable<DWORD> m_dwLastSeenId; // Message ID last seen by sender (receiver can discard up to here from send queue)
+ Portable<DWORD> m_dwReserved; // Reserved for future expansion (must be initialized to zero and
+ // never read)
+
+ // The rest of the header varies depending on the message type (keep the maximum size of this union
+ // small since all messages will pay the overhead, large message type specific data should go in the
+ // following data block).
+ union
+ {
+ // Used by MT_SessionRequest / MT_SessionAccept.
+ struct
+ {
+ Portable<DWORD> m_dwMajorVersion; // Protocol version requested/accepted
+ Portable<DWORD> m_dwMinorVersion;
+ } VersionInfo;
+
+ // Used by MT_SessionReject.
+ struct
+ {
+ Portable<RejectReason> m_eReason; // Reason for rejection.
+ Portable<DWORD> m_dwMajorVersion; // Highest protocol version the LS supports
+ Portable<DWORD> m_dwMinorVersion;
+ } SessionReject;
+
+ // Used by MT_ReadMemory and MT_WriteMemory.
+ struct
+ {
+ Portable<PBYTE> m_pbLeftSideBuffer; // Address of memory to read/write on the LS
+ Portable<DWORD> m_cbLeftSideBuffer; // Size in bytes of memory to read/write
+ Portable<HRESULT> m_hrResult; // Result from LS (access can fail due to unmapped memory etc.)
+ } MemoryAccess;
+
+ // Used by MT_Event.
+ struct
+ {
+ Portable<IPCEventType> m_eIPCEventType; // multiplexing type of this IPC event
+ Portable<DWORD> m_eType; // Event type (useful for debugging)
+ } Event;
+
+ } TypeSpecificData;
+
+ BYTE m_sMustBeZero[8]; // Set this to zero when initializing and never read the contents
+ };
+
+ // Struct defining the format of the data block sent with a SessionRequest.
+ struct SessionRequestData
+ {
+ GUID m_sSessionID; // Unique session ID. Treated as byte blob so no endian-ness
+ };
+
+ // Struct used to track a message that is being (or will soon be) sent but has not yet been acknowledged.
+ // These are usually found queued on the send queue.
+ struct Message
+ {
+ Message *m_pNext; // Next message in the queue
+ MessageHeader m_sHeader; // Inline message header
+ PBYTE m_pbDataBlock; // Pointer to optional message data block (or NULL)
+ DWORD m_cbDataBlock; // Count of bytes in above block if it's non-NULL
+ HANDLE m_hReplyEvent; // Optional event to signal if this message is replied to (or NULL)
+ PBYTE m_pbReplyBlock; // Optional buffer to place data block from reply into (or NULL)
+ DWORD m_cbReplyBlock; // Size in bytes of the above buffer if it is non-NULL
+ Message *m_pOrigMessage; // Used when we need to find the original message from a copy
+ bool m_fAborted; // True if this send was aborted due to session shutdown
+
+ // Common initialization for messages.
+ void Init(MessageType eType,
+ PBYTE pbBufferIn = NULL,
+ DWORD cbBufferIn = 0,
+ PBYTE pbBufferOut = NULL,
+ DWORD cbBufferOut = 0)
+ {
+ memset(this, 0, sizeof(*this));
+ m_sHeader.m_eType = eType;
+ m_sHeader.m_cbDataBlock = cbBufferIn;
+ m_pbDataBlock = pbBufferIn;
+ m_cbDataBlock = cbBufferIn;
+ m_pbReplyBlock = pbBufferOut;
+ m_cbReplyBlock = cbBufferOut;
+ }
+ };
+
+ // Holder class used to take a transport lock in a given scope and automatically release it once that
+ // scope is exited.
+ class TransportLockHolder
+ {
+ public:
+ TransportLockHolder(DbgTransportLock *pLock)
+ {
+ m_pLock = pLock;
+ m_pLock->Enter();
+ }
+
+ ~TransportLockHolder()
+ {
+ m_pLock->Leave();
+ }
+
+ private:
+ DbgTransportLock *m_pLock;
+ };
+
+#ifdef _DEBUG
+ // Store statistics for various session activities that will be useful for performance analysis and tracking
+ // down bugs.
+ struct DbgStats
+ {
+ // Message type counts for sends.
+ LONG m_cSentSessionRequest;
+ LONG m_cSentSessionAccept;
+ LONG m_cSentSessionReject;
+ LONG m_cSentSessionResync;
+ LONG m_cSentSessionClose;
+ LONG m_cSentEvent;
+ LONG m_cSentReadMemory;
+ LONG m_cSentWriteMemory;
+ LONG m_cSentVirtualUnwind;
+ LONG m_cSentGetDCB;
+ LONG m_cSentSetDCB;
+ LONG m_cSentGetAppDomainCB;
+ LONG m_cSentDDMessage;
+
+ // Message type counts for receives.
+ LONG m_cReceivedSessionRequest;
+ LONG m_cReceivedSessionAccept;
+ LONG m_cReceivedSessionReject;
+ LONG m_cReceivedSessionResync;
+ LONG m_cReceivedSessionClose;
+ LONG m_cReceivedEvent;
+ LONG m_cReceivedReadMemory;
+ LONG m_cReceivedWriteMemory;
+ LONG m_cReceivedVirtualUnwind;
+ LONG m_cReceivedGetDCB;
+ LONG m_cReceivedSetDCB;
+ LONG m_cReceivedGetAppDomainCB;
+ LONG m_cReceivedDDMessage;
+
+ // Low level block counts.
+ LONG m_cSentBlocks;
+ LONG m_cReceivedBlocks;
+
+ // Byte count summaries.
+ LONGLONG m_cbSentBytes;
+ LONGLONG m_cbReceivedBytes;
+
+ // Errors and recovery
+ LONG m_cSendErrors;
+ LONG m_cReceiveErrors;
+ LONG m_cMiscErrors;
+ LONG m_cConnections;
+ LONG m_cResends;
+
+ // Session counts.
+ LONG m_cSessions;
+ };
+
+ DbgStats m_sStats;
+
+ // Macros to update the statistics. The increment version is thread safe, but the add version is assumed to be
+ // externally serialized since the 64-bit Interlocked operations are not available on all platforms and these
+ // stats are used for send and receive byte counts which are updated at locations that are serialized anyway.
+#define DBG_TRANSPORT_INC_STAT(_name) InterlockedIncrement(&m_sStats.m_c##_name)
+#define DBG_TRANSPORT_ADD_STAT(_name, _amount) m_sStats.m_cb##_name += (_amount)
+
+#else // _DEBUG
+
+#define DBG_TRANSPORT_INC_STAT(_name)
+#define DBG_TRANSPORT_ADD_STAT(_name, _amount)
+
+#endif // _DEBUG
+
+ // Reference count
+ LONG m_ref;
+
+ // Some flags used to record how far we got in Init() (used for cleanup in Shutdown()).
+ bool m_fInitStateLock;
+#ifndef RIGHT_SIDE_COMPILE
+ bool m_fInitWSA;
+#endif // !RIGHT_SIDE_COMPILE
+
+ // Protocol version. This consists of two parts. The major version is incremented on incompatible protocol
+ // updates. That is, a session between left and right sides that cannot use a protocol with the exact same
+ // major version cannot be formed. The minor version number is incremented on compatible protocol updates.
+ // These are usually associated with optional extensions to the protocol (e.g. a V1.2 endpoint might set
+ // previously unused fields in a message header to indicate some optional hint about the message that a
+ // V1.1 client won't notice at all).
+ //
+ // The right side has a hard-coded version number it sends in the SessionRequest message. The left side
+ // must support the same major version or reply with a SessionReject message containing the highest
+ // version it does support. For this reason the format of a SessionReject message can never change at all.
+ // On a SessionAccept the left side sends back the version number and can choose to lower the minor
+ // version to the highest it knows about. This gives the right side a hint as to the capabilities of the
+ // left side (though it must be prepared to interact with a left side with any minor version number).
+ //
+ // If necessary (and the SessionReject message sent by an incompatible left side indicates a major version
+ // the right side can also support), the right side can re-attempt a SessionRequest with a lower major
+ // version.
+ DWORD m_dwMajorVersion;
+ DWORD m_dwMinorVersion;
+
+ // Session ID randomly allocated by the right side and sent over in the SessionRequest message. This
+ // serves to disambiguate a re-send of the SessionRequest due to a network error versus a SessionRequest
+ // from a different debugger.
+ GUID m_sSessionID;
+
+ // Lock used to synchronize sending messages and updating the session state. This ensures message bytes
+ // don't become interleaved on the transport connection, the send queue is updated consistently across
+ // multiple threads and that we never attempt to use a connection that is being deallocated on another
+ // thread due to a state change. Receives don't need this since they're performed only on the transport
+ // thread (which is also the only thread allowed to deallocate the connection).
+ DbgTransportLock m_sStateLock;
+
+ // Queue of messages that have been sent over the connection but not acknowledged yet or are waiting to be
+ // sent (because another message is using the connection or we're in a SessionResync state). You must hold
+ // m_sStateLock in order to access this queue.
+ Message *m_pSendQueueFirst;
+ Message *m_pSendQueueLast;
+
+ // Message IDs. These are monotonically increasing numbers starting from 0 that are used to stamp each
+ // non-session management message sent on this session. If a low-level network error occurs and we must
+ // abandon and re-form the underlying transport connection the left and right sides send SessionResync
+ // messages with the ID of the last message they received (and processed). This allows us to determine
+ // which messages we still have in our send queue must be re-sent over the new transport connection.
+ // Allocate a new message ID by post incrementing m_dwNextMessageId under the state lock.
+ DWORD m_dwNextMessageId; // Next ID we'll give to a message we're sending
+ DWORD m_dwLastMessageIdSeen; // Last ID we saw in an incoming, fully received message
+
+ // The current session state. This is updated atomically under m_sStateLock.
+ SessionState m_eState;
+
+#ifdef RIGHT_SIDE_COMPILE
+ // Manual reset event that is signalled whenever the session state is SS_Open or SS_Closed (after waiting
+ // on this event the caller should check to see which state it was).
+ HANDLE m_hSessionOpenEvent;
+#endif // RIGHT_SIDE_COMPILE
+
+ // Thread responsible for initial Connect()/Accept() on a low level transport connection and
+ // subsequently for all message reception on that connection. Any error will cause the thread to reset
+ // back into the Connect()/Accept() phase (along with the resulting session state change).
+ HANDLE m_hTransportThread;
+
+ TwoWayPipe m_pipe;
+
+#ifdef RIGHT_SIDE_COMPILE
+ // On the RS the transport thread needs to know the IP address and port number to Connect() to.
+ DWORD m_pid; // Id of a process we're talking to.
+
+ HANDLE m_hProcessExited; // event which will be signaled when the debuggee is terminated
+
+ bool m_fDebuggerAttached;
+#endif
+
+ // Debugger event handling. To improve performance we allow the debugger to send as many events as it
+ // likes without acknowledgement from its peer. While not strictly adhering to the semantic provided by
+ // the shared memory buffer transport (where the buffer could not be written again until the receiver had
+ // explicitly released it) it turns out that no debugging code relies on this. In particular, the most
+ // common scenario where this makes sense is the left side sending large scale update events (such as the
+ // groups of appdomain create, module load etc. events sent during an attach). Here the right hand side
+ // queues the events for later processing and releases the buffers right away.
+ // We gain performance since its no longer necessary to send (or wait on) event acknowledgment messages.
+ // This lowers both network bandwidth and latency (especially when one side is trying to send a continuous
+ // stream of events).
+ // From the transport standpoint this design mainly impacts event receipt. We maintain a dynamically sized
+ // pool of event receipt buffers (the size is determined by the maximum number of unread events we've seen
+ // at any one time). The buffer is a circular array: clients read from the buffer at head index which is
+ // followed by some number of valid buffers (wrapping around to the start of the array if necessary). New
+ // events are added after these (and grow the array if the tail would touch the head otherwise).
+ DbgEventBufferEntry * m_pEventBuffers; // Pointer to array of incoming debugger events
+ DWORD m_cEventBuffers; // Size of the array above (in events)
+ DWORD m_cValidEventBuffers; // Number of events that actually contain data
+ DWORD m_idxEventBufferHead; // Index of the first valid event
+ DWORD m_idxEventBufferTail; // Index of the first invalid event
+ HANDLE m_rghEventReadyEvent[IPCET_Max]; // The event signalled when a new event arrives
+
+#ifndef RIGHT_SIDE_COMPILE
+ // The LS requires the addresses of a couple of runtime data structures in order to service MT_GetDCB etc.
+ // These are provided by the runtime at intialization time.
+ DebuggerIPCControlBlock *m_pDCB;
+ AppDomainEnumerationIPCBlock *m_pADB;
+#endif // !RIGHT_SIDE_COMPILE
+
+ HRESULT SendEventWorker(DebuggerIPCEvent * pEvent, IPCEventType type);
+
+ // Sends a pre-formatted message (including the data block, if any). The fWaitsForReply indicates whether
+ // the caller is going to block until some sort of reply message is received (for instance an event that
+ // must be ack'd or a request such as MT_GetDCB that needs a reply). SendMessage() uses this to determine
+ // whether it needs to buffer the message before placing it on the send queue (since it may need to resend
+ // the message after a transitory network failure).
+ HRESULT SendMessage(Message *pMessage, bool fWaitsForReply);
+
+ // Helper method for sending messages requiring a reply (such as MT_GetDCB) and waiting on the result.
+ HRESULT SendRequestMessageAndWait(Message *pMessage);
+
+ // Sends a single contiguous buffer of host memory over the connection. The caller is responsible for
+ // holding the state lock and ensuring the session state is SS_Open. Returns false if the send failed (the
+ // error will have already caused the recovery logic to kick in, so handling it is not required, the
+ // boolean is just returned so that any further blocks in the message are not sent).
+ bool SendBlock(PBYTE pbBuffer, DWORD cbBuffer);
+
+ // Receives a single contiguous buffer of host memory over the connection. No state lock needs to be
+ // held (receives are serialized by the fact they're only performed on the transport thread). Returns
+ // false if a network error is encountered (which will automatically transition the session into the
+ // correct retry state).
+ bool ReceiveBlock(PBYTE pbBuffer, DWORD cbBuffer);
+
+ // Called upon encountering a network error (e.g. an error from Send() or Receive()). This handles pushing
+ // the session state into SS_Resync_NC in order to start the recovery process.
+ void HandleNetworkError(bool fCallerHoldsStateLock);
+
+ // Scan the send queue and discard any messages which have been processed by the other side according to
+ // the specified ID). Messages waiting on a reply message (e.g. MT_GetDCB) will be retained until that
+ // reply is processed. FlushSendQueue will take the state lock.
+ void FlushSendQueue(DWORD dwLastProcessedId);
+
+#ifdef RIGHT_SIDE_COMPILE
+ // Perform processing required to complete a request (such as MT_GetDCB) once a reply comes in. This
+ // includes reading data from the connection into the output buffer, removing the original message from
+ // the send queue and signalling the completion event. Returns true if no network error was encountered.
+ bool ProcessReply(MessageHeader *pHeader);
+
+ // Upon receiving a reply message, signal the event on the message to wake up the thread waiting for
+ // the reply message and close the handle to the event.
+ void SignalReplyEvent(Message * pMesssage);
+
+ // Given a message ID, find the matching message in the send queue. If there is no match, return NULL.
+ // If there is a match, remove the message from the send queue and return it.
+ Message * RemoveMessageFromSendQueue(DWORD dwMessageId);
+#endif
+
+#ifndef RIGHT_SIDE_COMPILE
+ // Check read and optionally write memory access to the specified range of bytes. Used to check
+ // ReadProcessMemory and WriteProcessMemory requests.
+ HRESULT CheckBufferAccess(PBYTE pbBuffer, DWORD cbBuffer, bool fWriteAccess);
+#endif // !RIGHT_SIDE_COMPILE
+
+ // Initialize all session state to correct starting values. Used during Init() and on the LS when we
+ // gracefully close one session and prepare for another.
+ void InitSessionState();
+
+ // The entry point of the transport worker thread. This one's static, so we immediately dispatch to an
+ // instance method version defined below for convenience in the implementation.
+ static DWORD WINAPI TransportWorkerStatic(LPVOID pvContext);
+ void TransportWorker();
+
+ // Given a fully initialized debugger event structure, return the size of the structure in bytes (this is
+ // not trivial since DebuggerIPCEvent contains a large union member which can cause the portion containing
+ // significant data to vary wildy from event to event).
+ DWORD GetEventSize(DebuggerIPCEvent *pEvent);
+
+#ifdef _DEBUG
+ // Debug helper which returns the name associated with a MessageType.
+ const char *MessageName(MessageType eType);
+
+ // Debug logging helper which logs an incoming message of any type (as long as logging for that message
+ // class is currently enabled).
+ void DbgTransportLogMessageReceived(MessageHeader *pHeader);
+
+ // Helper method used by the DBG_TRANSPORT_SHOULD_INJECT_FAULT macro.
+ bool DbgTransportShouldInjectFault(DbgTransportFaultOp eOp, const char *szOpName);
+#else // _DEBUG
+#define DbgTransportLogMessageReceived(x)
+#endif // _DEBUG
+};
+
+#ifndef RIGHT_SIDE_COMPILE
+// The one and only transport instance for the left side. Allocated and initialized during EE startup (from
+// Debugger::Startup() in debugger.cpp).
+extern DbgTransportSession *g_pDbgTransport;
+#endif // !RIGHT_SIDE_COMPILE
+
+#define DBG_GET_LAST_WSA_ERROR() WSAGetLastError()
+
+#endif // defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI)
+
+#endif // __DBG_TRANSPORT_SESSION_INCLUDED
diff --git a/src/debug/inc/dbgutil.h b/src/debug/inc/dbgutil.h
new file mode 100644
index 0000000000..8dae6d32ab
--- /dev/null
+++ b/src/debug/inc/dbgutil.h
@@ -0,0 +1,93 @@
+// 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.
+//*****************************************************************************
+// dbgutil.h
+//
+
+//
+//*****************************************************************************
+
+#pragma once
+#include <cor.h>
+#include <cordebug.h>
+#include <metahost.h>
+
+//
+// Various common helpers used by multiple debug components.
+//
+
+// Returns the RVA of the resource section for the module specified by the given data target and module base.
+// Returns failure if the module doesn't have a resource section.
+//
+// Arguments
+// pDataTarget - dataTarget for the process we are inspecting
+// moduleBaseAddress - base address of a module we should inspect
+// pwImageFileMachine - updated with the Machine from the IMAGE_FILE_HEADER
+// pdwResourceSectionRVA - updated with the resultant RVA on success
+HRESULT GetMachineAndResourceSectionRVA(ICorDebugDataTarget* pDataTarget,
+ ULONG64 moduleBaseAddress,
+ WORD* pwImageFileMachine,
+ DWORD* pdwResourceSectionRVA);
+
+HRESULT GetResourceRvaFromResourceSectionRva(ICorDebugDataTarget* pDataTarget,
+ ULONG64 moduleBaseAddress,
+ DWORD resourceSectionRva,
+ DWORD type,
+ DWORD name,
+ DWORD language,
+ DWORD* pResourceRva,
+ DWORD* pResourceSize);
+
+HRESULT GetResourceRvaFromResourceSectionRvaByName(ICorDebugDataTarget* pDataTarget,
+ ULONG64 moduleBaseAddress,
+ DWORD resourceSectionRva,
+ DWORD type,
+ LPCWSTR pwszName,
+ DWORD language,
+ DWORD* pResourceRva,
+ DWORD* pResourceSize);
+
+// Traverses down one level in the PE resource tree structure
+//
+// Arguments:
+// pDataTarget - the data target for inspecting this process
+// id - the id of the next node in the resource tree you want
+// moduleBaseAddress - the base address of the module being inspected
+// resourceDirectoryRVA - the base address of the beginning of the resource directory for this
+// level of the tree
+// pNextLevelRVA - out - The RVA for the next level tree directory or the RVA of the resource entry
+//
+// Returns:
+// S_OK if succesful or an appropriate failing HRESULT
+HRESULT GetNextLevelResourceEntryRVA(ICorDebugDataTarget* pDataTarget,
+ DWORD id,
+ ULONG64 moduleBaseAddress,
+ DWORD resourceDirectoryRVA,
+ DWORD* pNextLevelRVA);
+
+// Traverses down one level in the PE resource tree structure
+//
+// Arguments:
+// pDataTarget - the data target for inspecting this process
+// name - the name of the next node in the resource tree you want
+// moduleBaseAddress - the base address of the module being inspected
+// resourceDirectoryRVA - the base address of the beginning of the resource directory for this
+// level of the tree
+// resourceSectionRVA - the rva of the beginning of the resource section of the PE file
+// pNextLevelRVA - out - The RVA for the next level tree directory or the RVA of the resource entry
+//
+// Returns:
+// S_OK if succesful or an appropriate failing HRESULT
+HRESULT GetNextLevelResourceEntryRVAByName(ICorDebugDataTarget* pDataTarget,
+ LPCWSTR pwzName,
+ ULONG64 moduleBaseAddress,
+ DWORD resourceDirectoryRva,
+ DWORD resourceSectionRva,
+ DWORD* pNextLevelRva);
+
+// A small wrapper that reads from the data target and throws on error
+HRESULT ReadFromDataTarget(ICorDebugDataTarget* pDataTarget,
+ ULONG64 addr,
+ BYTE* pBuffer,
+ ULONG32 bytesToRead);
diff --git a/src/debug/inc/ddmarshalutil.h b/src/debug/inc/ddmarshalutil.h
new file mode 100644
index 0000000000..be24d4c7d5
--- /dev/null
+++ b/src/debug/inc/ddmarshalutil.h
@@ -0,0 +1,394 @@
+// 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.
+
+// DDMarshalUtil.cpp
+
+
+#ifndef _DDMarshal_Util_h
+#define _DDMarshal_Util_h
+
+#include "dacdbiinterface.h"
+typedef IDacDbiInterface::StackWalkHandle StackWalkHandle;
+typedef IDacDbiInterface::HeapWalkHandle HeapWalkHandle;
+typedef IDacDbiInterface::IStringHolder IStringHolder;
+
+#include "stringcopyholder.h"
+
+// @dbgtodo Mac - cleanup the buffer classes here. (are there pre-existing classes we could use instead?)
+// These ultimately get included in the signature for IDacDbiMarshalStub::DoRequest.
+// Key is that this helps serialize remote sized structures like IStringHolder and DacDbiArrayList<T>.
+class BaseBuffer
+{
+public:
+ BaseBuffer()
+ {
+ m_idx = 0;
+ m_pBuffer = NULL;
+ m_size = 0;
+ }
+
+protected:
+ DWORD m_idx;
+ DWORD m_size;
+ BYTE * m_pBuffer;
+};
+
+class WriteBuffer : public BaseBuffer
+{
+public:
+ friend class ReadBuffer;
+
+ WriteBuffer()
+ {
+ // @dbgtodo Mac - do something smarter than always allocate 1000 bytes.
+ m_size = 1000;
+ m_pBuffer = new BYTE[m_size];
+ }
+ ~WriteBuffer()
+ {
+ if (m_pBuffer != NULL)
+ {
+ delete [] m_pBuffer;
+ m_pBuffer = NULL;
+ }
+ }
+
+ // Write to the buffer, and grow if needed.
+ void WriteBlob(const void * pData, DWORD cbLength)
+ {
+ _ASSERTE(m_pBuffer != NULL);
+ EnsureSize(cbLength);
+ memcpy(&m_pBuffer[m_idx], pData, cbLength);
+ m_idx += cbLength;
+ }
+
+ void EnsureSize(DWORD cbLength)
+ {
+ DWORD cbSizeNeeded = m_idx + cbLength;
+ _ASSERTE(cbSizeNeeded <= 128000); // sanity checking...
+ while(cbSizeNeeded >= m_size)
+ {
+ DWORD cbNewSize = (m_size * 3) / 2; // grow by 1.5
+ BYTE * pNewBuffer = new BYTE[cbNewSize];
+ memcpy(pNewBuffer, m_pBuffer, m_idx);
+
+ _ASSERTE(m_pBuffer != NULL);
+ delete [] m_pBuffer;
+
+ m_size = cbNewSize;
+ m_pBuffer = pNewBuffer;
+ }
+ }
+
+ void WriteString(const WCHAR * pString)
+ {
+ bool fIsNull = (pString == NULL ? true : false);
+ EnsureSize(sizeof(fIsNull));
+ memcpy(&m_pBuffer[m_idx], &fIsNull, sizeof(fIsNull));
+ m_idx += sizeof(fIsNull);
+
+ if (!fIsNull)
+ {
+ _ASSERTE(pString != NULL);
+ DWORD len = (DWORD) wcslen(pString);
+ DWORD cbCopy = (len + 1) * sizeof(WCHAR);
+
+ EnsureSize(cbCopy);
+ memcpy(&m_pBuffer[m_idx], pString, cbCopy);
+ m_idx += cbCopy;
+ }
+ }
+
+ // Gets access to the raw buffer - does not transfer ownership of the memory
+ void GetRawPtr(PBYTE * ppBuffer, DWORD * pcbUsed)
+ {
+ *ppBuffer = m_pBuffer;
+ *pcbUsed = m_idx;
+ }
+};
+
+// Read-only stream access to memory blob.
+class ReadBuffer : public BaseBuffer
+{
+public:
+ ReadBuffer()
+ {
+ m_fDeleteOnClose = false;
+ }
+ ~ReadBuffer()
+ {
+ if (m_fDeleteOnClose)
+ {
+ delete [] m_pBuffer;
+ }
+ }
+
+ // Create on existing stream
+ void Open(BYTE * pStream, DWORD cbLength)
+ {
+ _ASSERTE(m_pBuffer == NULL);
+ _ASSERTE(m_idx == 0);
+ m_pBuffer = pStream;
+ m_size = cbLength;
+ }
+ void OpenAndOwn(BYTE * pStream, DWORD cbLength)
+ {
+ Open(pStream, cbLength);
+ m_fDeleteOnClose = true;
+ }
+
+ // Get a reader for the range written by a Writer
+ // This steal's the writer's buffer. The Writer object is dead after this.
+ void Open(WriteBuffer * pBuffer)
+ {
+ _ASSERTE(m_pBuffer == NULL);
+ _ASSERTE(m_idx == 0);
+ m_size = pBuffer->m_idx;
+ m_pBuffer = pBuffer->m_pBuffer;
+
+
+ pBuffer->m_pBuffer = NULL;
+ m_fDeleteOnClose = true;
+ }
+
+ bool IsAtEnd()
+ {
+ return (m_idx == m_size);
+ }
+ void ReadBlob(void * pData, DWORD cbLength)
+ {
+ _ASSERTE(m_idx + cbLength <= m_size);
+ memcpy(pData, &m_pBuffer[m_idx], cbLength);
+ m_idx += cbLength;
+ }
+ DWORD Length()
+ {
+ return m_size;
+ }
+
+ // Return a pointer to the string and mvoe the index.
+ const WCHAR * ReadString()
+ {
+ bool fIsNull = *reinterpret_cast<bool *>(&m_pBuffer[m_idx]);
+ m_idx += sizeof(fIsNull);
+
+ if (fIsNull)
+ {
+ return NULL;
+ }
+ else
+ {
+ const WCHAR * pString = (WCHAR*) &m_pBuffer[m_idx];
+ DWORD len = (DWORD) wcslen(pString);
+ m_idx += (len + 1) * sizeof(WCHAR); // skip past null
+ _ASSERTE(m_idx <= m_size);
+ return pString;
+ }
+ }
+
+protected:
+ bool m_fDeleteOnClose;
+};
+
+
+
+//
+// Writers
+//
+template<class T> inline
+void WriteToBuffer(WriteBuffer * p, T & data)
+{
+ p->WriteBlob(&data, sizeof(T));
+}
+
+inline
+void WriteToBuffer(WriteBuffer * p, StackWalkHandle & h)
+{
+ p->WriteBlob(&h, sizeof(StackWalkHandle));
+}
+
+inline
+void WriteCookie(WriteBuffer * p, BYTE cookie)
+{
+#if _DEBUG
+ WriteToBuffer(p, cookie);
+#endif
+}
+
+
+template<class T> inline
+void WriteToBuffer(WriteBuffer * p, T * pData)
+{
+ p->WriteBlob(pData, sizeof(T));
+}
+
+inline
+void WriteToBuffer(WriteBuffer * p, StringCopyHolder * pString)
+{
+ const WCHAR * pData = NULL;
+ if (pString->IsSet())
+ {
+ pData = *pString; // gets raw data
+ }
+ p->WriteString(pData);
+ WriteCookie(p, 0x1F);
+}
+
+template<class T> inline
+void WriteToBuffer(WriteBuffer * p, DacDbiArrayList<T> * pList)
+{
+ _ASSERTE(pList != NULL);
+ WriteCookie(p, 0xCD);
+
+ int count = pList->Count();
+ WriteToBuffer(p, count);
+
+ if (count == 0) return;
+
+ // Write raw data.
+ for(int i = 0; i < count; i++)
+ {
+ const T * pElement = &((*pList)[i]);
+ WriteToBuffer(p, pElement);
+ }
+ WriteCookie(p, 0xAB);
+}
+
+template<class T> inline
+void WriteToBuffer(WriteBuffer * p, DacDbiArrayList<T> & list)
+{
+ WriteToBuffer(p, &list);
+}
+
+inline
+void WriteToBuffer(WriteBuffer * p, NativeVarData * pData)
+{
+ WriteCookie(p, 0xD1);
+ p->WriteBlob(pData, sizeof(NativeVarData));
+ WriteToBuffer(p, pData->m_offsetInfo);
+}
+
+inline
+void WriteToBuffer(WriteBuffer * p, SequencePoints * pData)
+{
+ WriteCookie(p, 0xD2);
+ p->WriteBlob(pData, sizeof(SequencePoints));
+ WriteToBuffer(p, pData->m_map);
+}
+inline
+void WriteToBuffer(WriteBuffer * p, ClassInfo * pData)
+{
+ WriteCookie(p, 0xD3);
+ p->WriteBlob(pData, sizeof(ClassInfo));
+ WriteToBuffer(p, pData->m_fieldList);
+}
+
+//-----------------------------------------------------------------------------
+//
+// Readers
+//
+template<class T> inline
+void ReadFromBuffer(ReadBuffer * p, T & data)
+{
+ p->ReadBlob(&data, sizeof(T));
+}
+
+inline
+void ReadCookie(ReadBuffer * p, BYTE cookieExpected)
+{
+#if _DEBUG
+ BYTE cookie;
+ ReadFromBuffer(p, cookie);
+ _ASSERTE(cookie = cookieExpected);
+#endif
+}
+
+
+inline
+void ReadFromBuffer(ReadBuffer * p, StackWalkHandle & h)
+{
+ p->ReadBlob(&h, sizeof(StackWalkHandle));
+}
+
+template<class T> inline
+void ReadFromBuffer(ReadBuffer * p, T * pData)
+{
+ // Used to copy-back a By-ref / out parameter
+ p->ReadBlob(pData, sizeof(T));
+}
+
+inline
+void ReadFromBuffer(ReadBuffer * p, IStringHolder * pString)
+{
+ const WCHAR *pData = p->ReadString();
+ // AssignCopy() can handle a NULL string.
+ pString->AssignCopy(pData);
+ ReadCookie(p, 0x1F);
+}
+
+template<class T> inline
+void ReadFromBuffer(ReadBuffer * p, DacDbiArrayList<T> * pList)
+{
+ _ASSERTE(pList != NULL);
+
+ ReadCookie(p, 0xCD);
+
+ // Alloc() will attempt to free the old pointer.
+ // if this was blit copied, the pointer is trashed. So we need to safely clear that
+ // pointer to prepare it to be copied.
+ pList->PrepareForDeserialize();
+
+ int count;
+ ReadFromBuffer(p, count);
+
+ pList->Alloc(count);
+ if (count == 0)
+ {
+ return;
+ }
+
+ // Read raw data.
+ for(int i = 0; i < count; i++)
+ {
+ T * pElement = &((*pList)[i]);
+ ReadFromBuffer(p, pElement);
+ }
+ ReadCookie(p, 0xAB);
+}
+
+template<class T> inline
+void ReadFromBuffer(ReadBuffer * p, DacDbiArrayList<T> & list)
+{
+ ReadFromBuffer(p, &list);
+}
+
+inline
+void ReadFromBuffer(ReadBuffer * p, NativeVarData * pData)
+{
+ ReadCookie(p, 0xD1);
+ p->ReadBlob(pData, sizeof(NativeVarData));
+ ReadFromBuffer(p, &pData->m_offsetInfo);
+}
+
+inline
+void ReadFromBuffer(ReadBuffer * p, SequencePoints * pData)
+{
+ ReadCookie(p, 0xD2);
+ p->ReadBlob(pData, sizeof(SequencePoints));
+ ReadFromBuffer(p, &pData->m_map);
+}
+
+inline
+void ReadFromBuffer(ReadBuffer * p, ClassInfo * pData)
+{
+ ReadCookie(p, 0xD3);
+ p->ReadBlob(pData, sizeof(ClassInfo));
+ ReadFromBuffer(p, &pData->m_fieldList);
+}
+
+
+
+
+#endif // _DDMarshal_Util_h
+
diff --git a/src/debug/inc/dump/.gitmirror b/src/debug/inc/dump/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/debug/inc/dump/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/debug/inc/dump/dumpcommon.h b/src/debug/inc/dump/dumpcommon.h
new file mode 100644
index 0000000000..3e197ce29b
--- /dev/null
+++ b/src/debug/inc/dump/dumpcommon.h
@@ -0,0 +1,108 @@
+// 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.
+
+#ifndef DEBUGGER_DUMPCOMMON_H
+#define DEBUGGER_DUMPCOMMON_H
+
+#if defined(DACCESS_COMPILE) || defined(RIGHT_SIDE_COMPILE)
+
+// When debugging against minidumps, we frequently need to ignore errors
+// due to the dump not having memory content.
+// You should be VERY careful using these macros. Because our code does not
+// distinguish target types, when you allow memory to be missing because a dump
+// target may not have that memory content by-design you are also implicitly
+// allowing that same data to be missing from a live debugging target.
+// Also, be aware that these macros exist in code under vm\. You must be careful to
+// only allow them to change execution for DAC and DBI.
+// Be careful state is such that execution can continue if the target is missing
+// memory.
+// In general, there are two solutions to this problem:
+// a) add the memory to all minidumps
+// b) stop forcing the memory to always be present
+// All decisions between a & b focus on cost. For a, cost is adding the memory & a complete
+// path to locate it to the dump, both in terms of dump generation time and most
+// especially in terms of dump size (we cannot make MiniDumpNormal many MB for trivial
+// apps).
+// For b, cost is that we lose some of our validation when we have to turn off asserts
+// and other checks for targets that should always have the missing memory present
+// because we have no concept of allowing it to be missing only from a dump.
+
+// This seemingly awkward try block starting tag is so that when the macro is used over
+// multiple source lines we don't create a useless try/catch block. This is important
+// when using the macros in vm\ code.
+#define EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY EX_TRY
+#define EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY \
+ EX_CATCH \
+ { \
+ if ((GET_EXCEPTION()->GetHR() != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) && \
+ (GET_EXCEPTION()->GetHR() != CORDBG_E_READVIRTUAL_FAILURE) ) \
+ { \
+ EX_RETHROW; \
+ } \
+ } \
+ EX_END_CATCH(SwallowAllExceptions)
+
+#define EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER EX_TRY
+#define EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER \
+ EX_CATCH \
+ { \
+ if ((GET_EXCEPTION()->GetHR() != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) && \
+ (GET_EXCEPTION()->GetHR() != CORDBG_E_READVIRTUAL_FAILURE) ) \
+ { \
+ EX_RETHROW; \
+ } \
+ else \
+
+#define EX_TRY_ALLOW_DATATARGET_MISSING_OR_INCONSISTENT_MEMORY EX_TRY
+#define EX_END_CATCH_ALLOW_DATATARGET_MISSING_OR_INCONSISTENT_MEMORY \
+ EX_CATCH \
+ { \
+ if ((GET_EXCEPTION()->GetHR() != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) && \
+ (GET_EXCEPTION()->GetHR() != CORDBG_E_READVIRTUAL_FAILURE) && \
+ (GET_EXCEPTION()->GetHR() != CORDBG_E_TARGET_INCONSISTENT)) \
+ { \
+ EX_RETHROW; \
+ } \
+ } \
+ EX_END_CATCH(SwallowAllExceptions)
+
+
+#define EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER \
+ } \
+ EX_END_CATCH(SwallowAllExceptions)
+
+// Only use this version for wrapping single source lines, or you'll make debugging
+// painful.
+#define ALLOW_DATATARGET_MISSING_MEMORY(sourceCode) \
+ EX_TRY \
+ { \
+ sourceCode \
+ } \
+ EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY
+
+#define ALLOW_DATATARGET_MISSING_OR_INCONSISTENT_MEMORY(sourceCode) \
+ EX_TRY \
+ { \
+ sourceCode \
+ } \
+ EX_END_CATCH_ALLOW_DATATARGET_MISSING_OR_INCONSISTENT_MEMORY
+
+#else
+#define EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY
+#define EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY
+#define EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER \
+ #error This macro is only intended for use in DAC code!
+#define EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER \
+ #error This macro is only intended for use in DAC code!
+#define EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER \
+ #error This macro is only intended for use in DAC code!
+
+
+#define ALLOW_DATATARGET_MISSING_MEMORY(sourceCode) \
+ sourceCode
+
+#endif // defined(DACCESS_COMPILE) || defined(RIGHT_SIDE_COMPILE)
+
+
+#endif //DEBUGGER_DUMPCOMMON_H
diff --git a/src/debug/inc/eventredirection.h b/src/debug/inc/eventredirection.h
new file mode 100644
index 0000000000..67ea271ea4
--- /dev/null
+++ b/src/debug/inc/eventredirection.h
@@ -0,0 +1,84 @@
+// 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.
+//*****************************************************************************
+
+//
+// NativePipeline.h
+//
+// define control block for redirecting events.
+//*****************************************************************************
+
+#ifndef _EVENTREDIRECTION_H
+#define _EVENTREDIRECTION_H
+
+//---------------------------------------------------------------------------------------
+// Control block for redirecting events.
+// Motivation here is that only 1 process can be the real OS debugger. So if we want a windbg
+// attached to an ICorDebug debuggee, then that windbg is the real debugger and it forwards events
+// to the mdbg process.
+//
+// Terminology:
+// Server: a windbg extension (StrikeRS) that is the real OS debugger, and it forwards native debug
+// events (just exceptions currently) to the client
+// Client: ICorDebug, which gets events via shimmed call to WaitForDebugEvent, etc.
+//
+// Control block lives in Client's process space. All handles are valid in client.
+// Sever does Read/WriteProcessMemory
+struct RedirectionBlock
+{
+ // Version of the control block. Initialized by client, verified by server.
+ // Latest value is EVENT_REDIRECTION_CURRENT_VERSION
+ DWORD m_versionCookie;
+
+ //
+ // Counters. After each WFDE/CDE pair, these counters should be in sync.
+ //
+
+ // increment after WFDE
+ DWORD m_counterAvailable;
+ DWORD m_counterConsumed;
+
+ //
+ // Data for WaitForDebugEvent. (Server writes; Client reads)
+ //
+ DWORD m_dwProcessId;
+ DWORD m_dwThreadId;
+
+ // Different sizes on different platforms
+ EXCEPTION_RECORD m_record;
+ BOOL m_dwFirstChance;
+
+ //
+ // Data for ContinueDebugEvent. (Client writes, server reads)
+ //
+
+ // Continuation status argument to ContinueDebugEvent
+ DWORD m_ContinuationStatus;
+
+
+ //
+ // Coordination events. These are handles in client space; server duplicates out.
+ //
+
+ // Server signals when WFDE Data is ready.
+ HANDLE m_hEventAvailable;
+
+ // Server signals when CDE data is ready.
+ HANDLE m_hEventConsumed;
+
+ // Client signals before it deletes this block. This corresponds to client calling DebugActiveProcessStop.
+ // Thus server can check if signalled to know if accessing this block (which lives in client space) is safe.
+ // This is Synchronized because client only detaches if the debuggee is stopped, in which case the server
+ // isn't in the middle of sending an event.
+ HANDLE m_hDetachEvent;
+};
+
+
+// Current version.
+#define EVENT_REDIRECTION_CURRENT_VERSION ((DWORD) 4)
+
+
+
+#endif // _EVENTREDIRECTION_H
+
diff --git a/src/debug/inc/i386/.gitmirror b/src/debug/inc/i386/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/debug/inc/i386/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/debug/inc/i386/primitives.h b/src/debug/inc/i386/primitives.h
new file mode 100644
index 0000000000..abad642bbd
--- /dev/null
+++ b/src/debug/inc/i386/primitives.h
@@ -0,0 +1,223 @@
+// 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.
+//*****************************************************************************
+// File: primitives.h
+//
+
+//
+// Platform-specific debugger primitives
+//
+//*****************************************************************************
+
+#ifndef PRIMITIVES_H_
+#define PRIMITIVES_H_
+
+
+typedef const BYTE CORDB_ADDRESS_TYPE;
+typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE;
+
+//This is an abstraction to keep x86/ia64 patch data separate
+#define PRD_TYPE DWORD_PTR
+
+#define MAX_INSTRUCTION_LENGTH 16
+
+// Given a return address retrieved during stackwalk,
+// this is the offset by which it should be decremented to lend somewhere in a call instruction.
+#define STACKWALK_CONTROLPC_ADJUST_OFFSET 1
+
+#define CORDbg_BREAK_INSTRUCTION_SIZE 1
+#define CORDbg_BREAK_INSTRUCTION (BYTE)0xCC
+
+inline CORDB_ADDRESS GetPatchEndAddr(CORDB_ADDRESS patchAddr)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return patchAddr + CORDbg_BREAK_INSTRUCTION_SIZE;
+}
+
+
+#define InitializePRDToBreakInst(_pPRD) *(_pPRD) = CORDbg_BREAK_INSTRUCTION
+#define PRDIsBreakInst(_pPRD) (*(_pPRD) == CORDbg_BREAK_INSTRUCTION)
+
+#define CORDbgGetInstructionEx(_buffer, _requestedAddr, _patchAddr, _dummy1, _dummy2) \
+ CORDbgGetInstruction((CORDB_ADDRESS_TYPE *)((_buffer) + ((_patchAddr) - (_requestedAddr))));
+
+#define CORDbgSetInstructionEx(_buffer, _requestedAddr, _patchAddr, _opcode, _dummy2) \
+ CORDbgSetInstruction((CORDB_ADDRESS_TYPE *)((_buffer) + ((_patchAddr) - (_requestedAddr))), (_opcode));
+
+#define CORDbgInsertBreakpointEx(_buffer, _requestedAddr, _patchAddr, _dummy1, _dummy2) \
+ CORDbgInsertBreakpoint((CORDB_ADDRESS_TYPE *)((_buffer) + ((_patchAddr) - (_requestedAddr))));
+
+
+SELECTANY const CorDebugRegister g_JITToCorDbgReg[] =
+{
+ REGISTER_X86_EAX,
+ REGISTER_X86_ECX,
+ REGISTER_X86_EDX,
+ REGISTER_X86_EBX,
+ REGISTER_X86_ESP,
+ REGISTER_X86_EBP,
+ REGISTER_X86_ESI,
+ REGISTER_X86_EDI
+};
+
+//
+// Mapping from ICorDebugInfo register numbers to CorDebugRegister
+// numbers. Note: this must match the order in corinfo.h.
+//
+inline CorDebugRegister ConvertRegNumToCorDebugRegister(ICorDebugInfo::RegNum reg)
+{
+ return g_JITToCorDbgReg[reg];
+}
+
+
+//
+// inline function to access/modify the CONTEXT
+//
+inline LPVOID CORDbgGetIP(DT_CONTEXT *context) {
+ LIMITED_METHOD_CONTRACT;
+
+ return (LPVOID)(size_t)(context->Eip);
+}
+
+inline void CORDbgSetIP(DT_CONTEXT *context, LPVOID eip) {
+ LIMITED_METHOD_CONTRACT;
+
+ context->Eip = (UINT32)(size_t)eip;
+}
+
+inline LPVOID CORDbgGetSP(const DT_CONTEXT * context) {
+ LIMITED_METHOD_CONTRACT;
+
+ return (LPVOID)(size_t)(context->Esp);
+}
+
+inline void CORDbgSetSP(DT_CONTEXT *context, LPVOID esp) {
+ LIMITED_METHOD_CONTRACT;
+
+ context->Esp = (UINT32)(size_t)esp;
+}
+
+inline void CORDbgSetFP(DT_CONTEXT *context, LPVOID ebp) {
+ LIMITED_METHOD_CONTRACT;
+
+ context->Ebp = (UINT32)(size_t)ebp;
+}
+inline LPVOID CORDbgGetFP(DT_CONTEXT* context)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (LPVOID)(UINT_PTR)context->Ebp;
+}
+
+// compare the EIP, ESP, and EBP
+inline BOOL CompareControlRegisters(const DT_CONTEXT * pCtx1, const DT_CONTEXT * pCtx2)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ if ((pCtx1->Eip == pCtx2->Eip) &&
+ (pCtx1->Esp == pCtx2->Esp) &&
+ (pCtx1->Ebp == pCtx2->Ebp))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* ========================================================================= */
+//
+// Routines used by debugger support functions such as codepatch.cpp or
+// exception handling code.
+//
+// GetInstruction, InsertBreakpoint, and SetInstruction all operate on
+// a _single_ byte of memory. This is really important. If you only
+// save one byte from the instruction stream before placing a breakpoint,
+// you need to make sure to only replace one byte later on.
+//
+
+
+inline PRD_TYPE CORDbgGetInstruction(UNALIGNED CORDB_ADDRESS_TYPE* address)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return *address; // retrieving only one byte is important
+
+}
+
+inline void CORDbgInsertBreakpoint(UNALIGNED CORDB_ADDRESS_TYPE *address)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ *((unsigned char*)address) = 0xCC; // int 3 (single byte patch)
+ FlushInstructionCache(GetCurrentProcess(), address, 1);
+}
+
+inline void CORDbgSetInstruction(CORDB_ADDRESS_TYPE* address,
+ DWORD instruction)
+{
+ // In a DAC build, this function assumes the input is an host address.
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ *((unsigned char*)address)
+ = (unsigned char) instruction; // setting one byte is important
+ FlushInstructionCache(GetCurrentProcess(), address, 1);
+}
+
+// After a breakpoint exception, the CPU points to _after_ the break instruction.
+// Adjust the IP so that it points at the break instruction. This lets us patch that
+// opcode and re-excute what was underneath the bp.
+inline void CORDbgAdjustPCForBreakInstruction(DT_CONTEXT* pContext)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ pContext->Eip -= 1;
+}
+
+inline bool AddressIsBreakpoint(CORDB_ADDRESS_TYPE* address)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return *address == CORDbg_BREAK_INSTRUCTION;
+}
+
+// Set the hardware trace flag.
+inline void SetSSFlag(DT_CONTEXT *context)
+{
+ _ASSERTE(context != NULL);
+ context->EFlags |= 0x100;
+}
+
+// Unset the hardware trace flag.
+inline void UnsetSSFlag(DT_CONTEXT *context)
+{
+ SUPPORTS_DAC;
+ _ASSERTE(context != NULL);
+ context->EFlags &= ~0x100;
+}
+
+// return true if the hardware trace flag applied.
+inline bool IsSSFlagEnabled(DT_CONTEXT * context)
+{
+ _ASSERTE(context != NULL);
+ return (context->EFlags & 0x100) != 0;
+}
+
+
+
+inline bool PRDIsEqual(PRD_TYPE p1, PRD_TYPE p2){
+ return p1 == p2;
+}
+
+// On x86 opcode 0 is an 8-bit version of ADD. Do we really want to use 0 to mean empty? (see issue 366221).
+inline void InitializePRD(PRD_TYPE *p1) {
+ *p1 = 0;
+}
+inline bool PRDIsEmpty(PRD_TYPE p1) {
+ LIMITED_METHOD_CONTRACT;
+
+ return p1 == 0;
+}
+
+
+#endif // PRIMITIVES_H_
diff --git a/src/debug/inc/readonlydatatargetfacade.h b/src/debug/inc/readonlydatatargetfacade.h
new file mode 100644
index 0000000000..95c12928c8
--- /dev/null
+++ b/src/debug/inc/readonlydatatargetfacade.h
@@ -0,0 +1,98 @@
+// 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.
+//*****************************************************************************
+// ReadOnlyDataTargetFacade.h
+//
+
+//
+//*****************************************************************************
+
+#ifndef READONLYDATATARGETFACADE_H_
+#define READONLYDATATARGETFACADE_H_
+
+#include <cordebug.h>
+
+//---------------------------------------------------------------------------------------
+// ReadOnlyDataTargetFacade
+//
+// This class is designed to be used as an ICorDebugMutableDataTarget when none is
+// supplied. All of the write APIs will fail with CORDBG_E_TARGET_READONLY as required
+// by the data target spec when a write operation is invoked on a read-only data target.
+// The desire here is to merge the error code paths for the case when a write fails,
+// and the case when a write is requested but the data target supplied doesn't
+// implement ICorDebugMutableDataTarget.
+//
+// Note that this is intended to be used only for the additional APIs defined by
+// ICorDebugMutableDataTarget. Calling any of the base ICorDebugDataTarget APIs
+// will ASSERT and fail. An alternative design would be to make this class a wrapper
+// class (similar to DataTargetAdapter) over an existing ICorDebugDataTarget interface.
+// In general, we'd like callers of the data target to differentiate between when they're
+// using read-only APIs and mutation APIs since they need to be aware that the latter often
+// won't be supported by the data target. Also, that design would have the draw-back
+// of incuring an extra virtual dispatch on every read API call (makaing debugging more
+// complex and possibly having a performance impact).
+//
+class ReadOnlyDataTargetFacade : public ICorDebugMutableDataTarget
+{
+public:
+ ReadOnlyDataTargetFacade();
+ virtual ~ReadOnlyDataTargetFacade() {}
+
+ //
+ // IUnknown.
+ //
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(
+ REFIID InterfaceId,
+ PVOID* Interface);
+
+ virtual ULONG STDMETHODCALLTYPE AddRef();
+
+ virtual ULONG STDMETHODCALLTYPE Release();
+
+ //
+ // ICorDebugDataTarget.
+ //
+
+ virtual HRESULT STDMETHODCALLTYPE GetPlatform(
+ CorDebugPlatform *pPlatform);
+
+ virtual HRESULT STDMETHODCALLTYPE ReadVirtual(
+ CORDB_ADDRESS address,
+ BYTE * pBuffer,
+ ULONG32 request,
+ ULONG32 * pcbRead);
+
+ virtual HRESULT STDMETHODCALLTYPE GetThreadContext(
+ DWORD dwThreadID,
+ ULONG32 contextFlags,
+ ULONG32 contextSize,
+ BYTE * context);
+
+ //
+ // ICorDebugMutableDataTarget.
+ //
+
+ virtual HRESULT STDMETHODCALLTYPE WriteVirtual(
+ CORDB_ADDRESS address,
+ const BYTE * pBuffer,
+ ULONG32 request);
+
+ virtual HRESULT STDMETHODCALLTYPE SetThreadContext(
+ DWORD dwThreadID,
+ ULONG32 contextSize,
+ const BYTE * context);
+
+ virtual HRESULT STDMETHODCALLTYPE ContinueStatusChanged(
+ DWORD dwThreadId,
+ CORDB_CONTINUE_STATUS dwContinueStatus);
+
+private:
+ // Reference count.
+ LONG m_ref;
+};
+
+#include "readonlydatatargetfacade.inl"
+
+#endif // READONLYDATATARGETFACADE_H_
+
diff --git a/src/debug/inc/readonlydatatargetfacade.inl b/src/debug/inc/readonlydatatargetfacade.inl
new file mode 100644
index 0000000000..df7e1d0e75
--- /dev/null
+++ b/src/debug/inc/readonlydatatargetfacade.inl
@@ -0,0 +1,139 @@
+// 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.
+//*****************************************************************************
+// File: ReadOnlyDataTargetFacade.inl
+//
+
+//
+//*****************************************************************************
+
+
+//---------------------------------------------------------------------------------------
+//
+// Ctor for ReadOnlyDataTargetFacade. Just initializes ref count to 0.
+//
+//---------------------------------------------------------------------------------------
+ReadOnlyDataTargetFacade::ReadOnlyDataTargetFacade()
+ : m_ref(0)
+{
+}
+
+// Standard impl of IUnknown::QueryInterface
+HRESULT STDMETHODCALLTYPE
+ReadOnlyDataTargetFacade::QueryInterface(
+ REFIID InterfaceId,
+ PVOID* pInterface
+ )
+{
+ if (InterfaceId == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugDataTarget *>(this));
+ }
+ else if (InterfaceId == IID_ICorDebugDataTarget)
+ {
+ *pInterface = static_cast<ICorDebugDataTarget *>(this);
+ }
+ else if (InterfaceId == IID_ICorDebugMutableDataTarget)
+ {
+ *pInterface = static_cast<ICorDebugMutableDataTarget *>(this);
+ }
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+// Standard impl of IUnknown::AddRef
+ULONG STDMETHODCALLTYPE
+ReadOnlyDataTargetFacade::AddRef()
+{
+ SUPPORTS_DAC;
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+}
+
+// Standard impl of IUnknown::Release
+ULONG STDMETHODCALLTYPE
+ReadOnlyDataTargetFacade::Release()
+{
+ SUPPORTS_DAC;
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+}
+
+// impl of interface method ICorDebugDataTarget::GetPlatform
+HRESULT STDMETHODCALLTYPE
+ReadOnlyDataTargetFacade::GetPlatform(
+ CorDebugPlatform *pPlatform)
+{
+ SUPPORTS_DAC;
+ _ASSERTE_MSG(false, "Unexpected call to read-API on read-only DataTarget facade");
+ return E_UNEXPECTED;
+}
+
+// impl of interface method ICorDebugDataTarget::ReadVirtual
+HRESULT STDMETHODCALLTYPE
+ReadOnlyDataTargetFacade::ReadVirtual(
+ CORDB_ADDRESS address,
+ PBYTE pBuffer,
+ ULONG32 cbRequestSize,
+ ULONG32 *pcbRead)
+{
+ SUPPORTS_DAC;
+ _ASSERTE_MSG(false, "Unexpected call to read-API on read-only DataTarget facade");
+ return E_UNEXPECTED;
+}
+
+// impl of interface method ICorDebugDataTarget::GetThreadContext
+HRESULT STDMETHODCALLTYPE
+ReadOnlyDataTargetFacade::GetThreadContext(
+ DWORD dwThreadID,
+ ULONG32 contextFlags,
+ ULONG32 contextSize,
+ BYTE * pContext)
+{
+ SUPPORTS_DAC;
+ _ASSERTE_MSG(false, "Unexpected call to read-API on read-only DataTarget facade");
+ return E_UNEXPECTED;
+}
+
+// impl of interface method ICorDebugMutableDataTarget::WriteVirtual
+HRESULT STDMETHODCALLTYPE
+ReadOnlyDataTargetFacade::WriteVirtual(
+ CORDB_ADDRESS pAddress,
+ const BYTE * pBuffer,
+ ULONG32 cbRequestSize)
+{
+ SUPPORTS_DAC;
+ return CORDBG_E_TARGET_READONLY;
+}
+
+// impl of interface method ICorDebugMutableDataTarget::SetThreadContext
+HRESULT STDMETHODCALLTYPE
+ReadOnlyDataTargetFacade::SetThreadContext(
+ DWORD dwThreadID,
+ ULONG32 contextSize,
+ const BYTE * pContext)
+{
+ SUPPORTS_DAC;
+ return CORDBG_E_TARGET_READONLY;
+}
+
+// Public implementation of ICorDebugMutableDataTarget::ContinueStatusChanged
+HRESULT STDMETHODCALLTYPE
+ReadOnlyDataTargetFacade::ContinueStatusChanged(
+ DWORD dwThreadId,
+ CORDB_CONTINUE_STATUS dwContinueStatus)
+{
+ SUPPORTS_DAC;
+ return CORDBG_E_TARGET_READONLY;
+}
diff --git a/src/debug/inc/stringcopyholder.h b/src/debug/inc/stringcopyholder.h
new file mode 100644
index 0000000000..13a89d87fc
--- /dev/null
+++ b/src/debug/inc/stringcopyholder.h
@@ -0,0 +1,59 @@
+// 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.
+
+
+
+#ifndef _StringCopyHolder_h_
+#define _StringCopyHolder_h_
+
+
+//-----------------------------------------------------------------------------
+// Simple holder to keep a copy of a string.
+// Implements IStringHolder so we can pass instances through IDacDbiInterface
+// and have it fill in the contents.
+//-----------------------------------------------------------------------------
+class StringCopyHolder : public IDacDbiInterface::IStringHolder
+{
+public:
+ StringCopyHolder();
+
+ // Free the memory allocated for the string contents
+ ~StringCopyHolder();
+
+ // Make a copy of the provided null-terminated unicode string
+ virtual HRESULT AssignCopy(const WCHAR * pCopy);
+
+ // Reset the string to NULL and free memory
+ void Clear();
+
+ // Returns true if the string has been set to a non-NULL value
+ bool IsSet()
+ {
+ return (m_szData != NULL);
+ }
+
+ // Returns true if an empty string is stored. IsSet must be true to call this.
+ bool IsEmpty()
+ {
+ _ASSERTE(m_szData != NULL);
+ return m_szData[0] == W('\0');
+ }
+
+ // Returns the pointer to the string contents
+ operator WCHAR* () const
+ {
+ return m_szData;
+ }
+
+private:
+ // Disallow copying (to prevent double-free) - no implementation
+ StringCopyHolder( const StringCopyHolder& rhs );
+ StringCopyHolder& operator=( const StringCopyHolder& rhs );
+
+ WCHAR * m_szData;
+
+};
+
+
+#endif // StringCopyHolder
diff --git a/src/debug/inc/twowaypipe.h b/src/debug/inc/twowaypipe.h
new file mode 100644
index 0000000000..6bc0f9f39e
--- /dev/null
+++ b/src/debug/inc/twowaypipe.h
@@ -0,0 +1,104 @@
+// 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.
+
+
+#ifndef TwoWayPipe_H
+#define TwoWayPipe_H
+
+#ifdef FEATURE_PAL
+#define INVALID_PIPE -1
+#else
+#define INVALID_PIPE INVALID_HANDLE_VALUE
+#endif
+
+// This file contains definition of a simple IPC mechanism - bidirectional named pipe.
+// It is implemented on top of two one-directional names pipes (fifos on UNIX)
+
+// One Windows it is possible to ask OS to create a bidirectional pipe, but it is not the case on UNIX.
+// In order to unify implementation we use two pipes on all systems.
+
+// This all methods of this class are *NOT* thread safe: it is assumed the caller provides synchronization at a higher level.
+class TwoWayPipe
+{
+public:
+ enum State
+ {
+ NotInitialized, // Object didn't create or connect to any pipes.
+ Created, // Server side of the pipe has been created, but didn't bind it to a client.
+ ServerConnected, // Server side of the pipe is connected to a client
+ ClientConnected, // Client side of the pipe is connected to a server.
+ };
+
+ TwoWayPipe()
+ :m_state(NotInitialized),
+ m_inboundPipe(INVALID_PIPE),
+ m_outboundPipe(INVALID_PIPE)
+ {}
+
+
+ ~TwoWayPipe()
+ {
+ Disconnect();
+ }
+
+ // Creates a server side of the pipe.
+ // Id is used to create pipes names and uniquely identify the pipe on the machine.
+ // true - success, false - failure (use GetLastError() for more details)
+ bool CreateServer(DWORD id);
+
+ // Connects to a previously opened server side of the pipe.
+ // Id is used to locate the pipe on the machine.
+ // true - success, false - failure (use GetLastError() for more details)
+ bool Connect(DWORD id);
+
+ // Waits for incoming client connections, assumes GetState() == Created
+ // true - success, false - failure (use GetLastError() for more details)
+ bool WaitForConnection();
+
+ // Reads data from pipe. Returns number of bytes read or a negative number in case of an error.
+ // use GetLastError() for more details
+ int Read(void *buffer, DWORD bufferSize);
+
+ // Writes data to pipe. Returns number of bytes written or a negative number in case of an error.
+ // use GetLastError() for more details
+ int Write(const void *data, DWORD dataSize);
+
+ // Disconnects server or client side of the pipe.
+ // true - success, false - failure (use GetLastError() for more details)
+ bool Disconnect();
+
+ State GetState()
+ {
+ return m_state;
+ }
+
+ // Used by debugger side (RS) to cleanup the target (LS) named pipes
+ // and semaphores when the debugger detects the debuggee process exited.
+ void CleanupTargetProcess();
+
+private:
+
+ State m_state;
+
+#ifdef FEATURE_PAL
+
+ int m_id; // id that was passed to CreateServer() or Connect()
+ int m_inboundPipe, m_outboundPipe; // two one sided pipes used for communication
+ char m_inPipeName[MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH]; // filename of the inbound pipe
+ char m_outPipeName[MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH]; // filename of the outbound pipe
+
+#else
+ // Connects to a one sided pipe previously created by CreateOneWayPipe.
+ // In order to successfully connect id and inbound flag should be the same.
+ HANDLE OpenOneWayPipe(DWORD id, bool inbound);
+
+ // Creates a one way pipe, id and inboud flag are used for naming.
+ // Created pipe is supposed to be connected to by OpenOneWayPipe.
+ HANDLE CreateOneWayPipe(DWORD id, bool inbound);
+
+ HANDLE m_inboundPipe, m_outboundPipe; //two one sided pipes used for communication
+#endif //FEATURE_PAL
+};
+
+#endif //TwoWayPipe_H