summaryrefslogtreecommitdiff
path: root/src/debug/di/rspriv.h
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/di/rspriv.h
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/di/rspriv.h')
-rw-r--r--src/debug/di/rspriv.h11756
1 files changed, 11756 insertions, 0 deletions
diff --git a/src/debug/di/rspriv.h b/src/debug/di/rspriv.h
new file mode 100644
index 0000000000..853767804a
--- /dev/null
+++ b/src/debug/di/rspriv.h
@@ -0,0 +1,11756 @@
+// 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.
+//*****************************************************************************
+// rspriv.
+//
+
+//
+// Common include file for right-side of debugger.
+//*****************************************************************************
+
+#ifndef RSPRIV_H
+#define RSPRIV_H
+
+#include <winwrap.h>
+#include <windows.h>
+
+#include <utilcode.h>
+
+
+#ifdef _DEBUG
+#define LOGGING
+#endif
+
+#include <log.h>
+#include <corerror.h>
+
+#include "cor.h"
+
+#include "cordebug.h"
+#include "xcordebug.h"
+#include "cordbpriv.h"
+#include "mscoree.h"
+
+#include <cordbpriv.h>
+#include <dbgipcevents.h>
+
+#if !defined(FEATURE_DBGIPC_TRANSPORT_DI)
+#include <ipcmanagerinterface.h>
+#endif // !FEATURE_DBGIPC_TRANSPORT_DI
+
+#include "common.h"
+#include "primitives.h"
+
+#include "dacdbiinterface.h"
+
+#include "helpers.h"
+
+struct MachineInfo;
+
+#include "nativepipeline.h"
+#include "stringcopyholder.h"
+
+
+#include "eventchannel.h"
+
+#undef ASSERT
+#define CRASH(x) _ASSERTE(!x)
+#define ASSERT(x) _ASSERTE(x)
+
+// We want to keep the 'worst' HRESULT - if one has failed (..._E_...) & the
+// other hasn't, take the failing one. If they've both/neither failed, then
+// it doesn't matter which we take.
+// Note that this macro favors retaining the first argument
+#define WORST_HR(hr1,hr2) (FAILED(hr1)?hr1:hr2)
+
+// #UseDataTarget
+// Forbid usage of OS APIs that we should be using the data-target for
+#define ReadProcessMemory DONT_USE_READPROCESS_MEMORY
+#define WriteProcessMemory DONT_USE_WRITEPROCESS_MEMORY
+
+
+/* ------------------------------------------------------------------------- *
+ * Forward class declarations
+ * ------------------------------------------------------------------------- */
+
+class CordbBase;
+class CordbValue;
+class CordbModule;
+class CordbClass;
+class CordbFunction;
+class CordbCode;
+class CordbFrame;
+class CordbJITILFrame;
+class CordbInternalFrame;
+class CordbContext;
+class CordbThread;
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+class CordbUnmanagedThread;
+struct CordbUnmanagedEvent;
+#endif
+
+class CordbProcess;
+class CordbAppDomain;
+class CordbAssembly;
+class CordbBreakpoint;
+class CordbStepper;
+class Cordb;
+class CordbEnCSnapshot;
+class CordbWin32EventThread;
+class CordbRCEventThread;
+class CordbRegisterSet;
+class CordbNativeFrame;
+class CordbObjectValue;
+class CordbEnCErrorInfo;
+class CordbEnCErrorInfoEnum;
+class Instantiation;
+class CordbType;
+class CordbNativeCode;
+class CordbILCode;
+class CordbReJitILCode;
+class CordbEval;
+
+class CordbMDA;
+
+class CorpubPublish;
+class CorpubProcess;
+class CorpubAppDomain;
+class CorpubProcessEnum;
+class CorpubAppDomainEnum;
+
+
+class RSLock;
+class NeuterList;
+
+class IDacDbiInterface;
+
+#if defined(FEATURE_DBGIPC_TRANSPORT_DI)
+class DbgTransportTarget;
+class DbgTransportSession;
+#endif // FEATURE_DBGIPC_TRANSPORT_DI
+
+// @dbgtodo private shim hook - the RS has private hooks into the shim to help bridge the V2/V3 gap.
+// This helps provide a working dogfooding story throughout our transition.
+// These hooks must be removed before shipping.
+class ShimProcess;
+
+
+#ifndef FEATURE_PAL
+extern HINSTANCE GetModuleInst();
+#endif
+
+
+template <class T>
+class CordbSafeHashTable;
+
+
+//---------------------------------------------------------------------------------------
+//
+// This is an encapsulation of the information necessary to connect to the debugger proxy on a remote machine.
+// It includes the IP address and the port number. The IP address can be set via the env var
+// COMPlus_DbgTransportProxyAddress, and the port number is fixed when Mac debugging is configured.
+//
+
+struct MachineInfo
+{
+public:
+ void Init(DWORD dwIPAddress, USHORT usPort)
+ {
+ m_dwIPAddress = dwIPAddress;
+ m_usPort = usPort;
+ }
+
+ void Clear()
+ {
+ m_dwIPAddress = 0;
+ m_usPort = 0;
+ }
+
+ DWORD GetIPAddress() {return m_dwIPAddress;};
+ USHORT GetPort() {return m_usPort;};
+
+private:
+ DWORD m_dwIPAddress;
+ USHORT m_usPort;
+};
+
+#define forDbi (*(forDbiWorker *)NULL)
+
+// for dbi we just default to new, but we need to have these defined for both dac and dbi
+inline void * operator new(size_t lenBytes, const forDbiWorker &)
+{
+ void * result = new BYTE[lenBytes];
+ if (result == NULL)
+ {
+ ThrowOutOfMemory();
+ }
+ return result;
+}
+
+inline void * operator new[](size_t lenBytes, const forDbiWorker &)
+{
+ void * result = new BYTE[lenBytes];
+ if (result == NULL)
+ {
+ ThrowOutOfMemory();
+ }
+ return result;
+}
+
+// Helper to delete memory used with the IDacDbiInterface::IAllocator interface.
+template<class T> inline
+void DeleteDbiMemory(T *p)
+{
+ delete p;
+}
+
+
+
+//---------------------------------------------------------------------------------------
+//
+// Simple array of holders (either RSSmartPtrs or RSExtSmartPtrs).
+// Holds a reference to each element.
+//
+// Notes:
+// T is the base type and HOLDER_T is the type of the holder. All functions implemented on this base
+// class must work for both RSSmartPtrs and RSExtSmartPtrs. For example, there is no concept of neutering
+// for RSExtSmartPtrs.
+//
+
+template<typename T, typename HOLDER_T>
+class BaseRSPtrArray
+{
+public:
+ BaseRSPtrArray()
+ {
+ m_pArray = NULL;
+ m_cElements = 0;
+ }
+
+ // Is the array emtpy?
+ bool IsEmpty() const
+ {
+ return (m_pArray == NULL);
+ }
+
+ // Allocate an array of ptrs.
+ // Returns false if not enough memory; else true.
+ bool Alloc(unsigned int cElements)
+ {
+ // Caller should have already Neutered
+ _ASSERTE(IsEmpty());
+
+ // It's legal to allocate 0 items. We'll succeed the allocation, but still claim that IsEmpty() == true.
+ if (cElements == 0)
+ {
+ return true;
+ }
+
+ // RSSmartPtr ctor will ensure all elements are null initialized.
+ m_pArray = new (nothrow) HOLDER_T [cElements];
+ if (m_pArray == NULL)
+ {
+ return false;
+ }
+
+ m_cElements = cElements;
+ return true;
+ }
+
+ // Allocate an array of ptrs.
+ // Throw on failure
+ void AllocOrThrow(unsigned int cElements)
+ {
+ if (!Alloc(cElements))
+ {
+ ThrowOutOfMemory();
+ }
+ }
+
+ // Release each element and empty the array.
+ void Clear()
+ {
+ // this Invoke dtors on each element which will release each element
+ delete [] m_pArray;
+
+ m_pArray = NULL;
+ m_cElements = 0;
+ }
+
+ // Array lookup. Caller gaurantees this is in range.
+ // Used for reading
+ T* operator [] (unsigned int index) const
+ {
+ _ASSERTE(m_pArray != NULL);
+ CONSISTENCY_CHECK_MSGF((index <= m_cElements), ("Index out of range. Index=%u, Max=%u\n", index, m_cElements));
+
+ return m_pArray[index];
+ }
+
+ // Assign a given index to the given value. The array holder will increment the internal reference on the value.
+ void Assign(unsigned int index, T* pValue)
+ {
+ _ASSERTE(m_pArray != NULL);
+ CONSISTENCY_CHECK_MSGF((index <= m_cElements), ("Index out of range. Index=%u, Max=%u\n", index, m_cElements));
+
+ m_pArray[index].Assign(pValue);
+ }
+
+ // Get lenght of array in elements.
+ unsigned int Length() const
+ {
+ return m_cElements;
+ }
+
+ // Some things need to get the address of an element in the table.
+ // For example, CordbThreads have an array of CordbFrame objects, and then CordbChains describe a range
+ // or frames via pointers into the CordbThread's array.
+ // This is a dangerous operation because it lets us side-step reference counting and protection.
+ T ** UnsafeGetAddrOfIndex(unsigned int index)
+ {
+ return m_pArray[index].UnsafeGetAddr();
+ }
+
+protected:
+ // Raw array of values.
+ HOLDER_T * m_pArray;
+
+ // Number of elements in m_pArray. Note the following is always true: (m_cElements == 0) == (m_pArray == NULL);
+ unsigned int m_cElements;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+// Simple array holder of RSSmartPtrs (internal pointers).
+// Holds a reference to each element.
+//
+// Notes:
+// This derived class adds the concept of neutering to the base pointer array.
+// Allows automatic Clear()ing; do not use this unless it is safe to do so in
+// all cases - e.g. you're holding a local.
+//
+
+template< typename T, typename HOLDER_T = RSSmartPtr<T> > // We need to use HOLDER_T to make gcc happy.
+class RSPtrArray : public BaseRSPtrArray<T, HOLDER_T>
+{
+private:
+ typedef BaseRSPtrArray<T, HOLDER_T> Super;
+ BOOL m_autoClear;
+
+public:
+ RSPtrArray() : m_autoClear(FALSE)
+ {
+ }
+
+ ~RSPtrArray()
+ {
+ if (m_autoClear)
+ {
+ Super::Clear();
+ }
+ else
+ {
+ // Caller should have already Neutered
+ _ASSERTE(Super::IsEmpty());
+ }
+ }
+
+ void EnableAutoClear()
+ {
+ m_autoClear = TRUE;
+ }
+
+ // Neuter all elements in the array.
+ void NeuterAndClear()
+ {
+ for(unsigned int i = 0; i < Super::m_cElements; i++)
+ {
+ if (Super::m_pArray[i] != NULL)
+ {
+ Super::m_pArray[i]->Neuter();
+ }
+ }
+
+ Super::Clear();
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+//
+// Simple array holder of RSExtSmartPtrs (external pointers).
+// Holds a reference to each element.
+//
+// Notes:
+// This derived class clears the array in its destructor.
+//
+
+template< typename T, typename HOLDER_T = RSExtSmartPtr<T> > // We need to use HOLDER_T to make gcc happy.
+class RSExtPtrArray : public BaseRSPtrArray<T, HOLDER_T>
+{
+private:
+ typedef BaseRSPtrArray<T, HOLDER_T> Super;
+
+public:
+ ~RSExtPtrArray()
+ {
+ Super::Clear();
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Table for RSptrs
+// This lets us map cookies <--> RSPTR_*,
+// Then we just put the cookie in the IPC block instead of the raw RSPTR.
+// This will also adjust the internal-reference count on the T* object.
+// This isolates the RS from bugs in the LS.
+// We templatize by type for type safety.
+// Caller must syncrhonize all access (preferably w/ the stop-go lock).
+//-----------------------------------------------------------------------------
+template <class T>
+class RsPtrTable
+{
+public:
+ RsPtrTable()
+ {
+ m_pTable = NULL;
+ m_cEntries = 0;
+ }
+ ~RsPtrTable()
+ {
+ Clear();
+ }
+ void Clear()
+ {
+ for(UINT i = 0; i < m_cEntries; i++)
+ {
+ if (m_pTable[i])
+ {
+ m_pTable[i]->InternalRelease();
+ }
+ }
+ delete [] m_pTable;
+ m_pTable = NULL;
+ m_cEntries = 0;
+ }
+
+ // Add a value into table. Value can't be NULL.
+ // Returns 0 on failure (such as oom),
+ // Returns a non-zero cookie on success.
+ UINT Add(T* pValue)
+ {
+ _ASSERTE(pValue != NULL);
+ // skip 0 because it's an invalid handle.
+ for(UINT i = 1; ; i++)
+ {
+ // If we've run out of space, allocate new space
+ if( i >= m_cEntries )
+ {
+ if( !Grow() )
+ {
+ return 0; // failed to grow
+ }
+ _ASSERTE( i < m_cEntries );
+ _ASSERTE( m_pTable[i] == NULL );
+ // Since we grew, the next slot should now be open.
+ }
+
+ if (m_pTable[i] == NULL)
+ {
+ m_pTable[i] = pValue;
+ pValue->InternalAddRef();
+ return i;
+ }
+ }
+ UNREACHABLE();
+ }
+
+ // Lookup the value based off the cookie, which was obtained via "Add".
+ // return NULL on error.
+ T* Lookup(UINT cookie)
+ {
+ _ASSERTE(cookie != 0);
+ if (cookie >= m_cEntries)
+ {
+ CONSISTENCY_CHECK_MSGF(false, ("Cookie out of range.Cookie=0x%x. Size=0x%x.\n", cookie, m_cEntries));
+ return NULL;
+ }
+ T* p = m_pTable[cookie];
+ if (p == NULL)
+ {
+ CONSISTENCY_CHECK_MSGF(false, ("Cookie is for empty slot.Cookie=0x%x.\n", cookie));
+ return NULL; // empty!
+ }
+ return p;
+ }
+
+ T* LookupAndRemove(UINT cookie)
+ {
+ _ASSERTE(cookie != 0);
+ T* p = Lookup(cookie);
+ if (p != NULL)
+ {
+ m_pTable[cookie] = NULL;
+ p->InternalRelease();
+ }
+ return p;
+ }
+
+protected:
+ // Resize the m_pTable array.
+ bool Grow()
+ {
+ if (m_pTable == NULL)
+ {
+ _ASSERTE(m_cEntries == 0);
+ size_t cSize = 10;
+ m_pTable = new (nothrow) T*[cSize];
+ if (m_pTable == NULL)
+ {
+ return false;
+ }
+ m_cEntries = cSize;
+ ZeroMemory(m_pTable, sizeof(T*) * m_cEntries);
+ return true;
+ }
+ size_t cNewSize = (m_cEntries * 3 / 2) + 1;
+ _ASSERTE(cNewSize > m_cEntries);
+ T** p = new (nothrow) T*[cNewSize];
+ if (p == NULL)
+ {
+ return false;
+ }
+ ZeroMemory(p, sizeof(T*) * cNewSize);
+
+
+ // Copy over old stuff
+ memcpy(p, m_pTable, sizeof(T*) * m_cEntries);
+ delete [] m_pTable;
+
+ m_pTable = p;
+ m_cEntries = cNewSize;
+ return true;
+ }
+
+ T** m_pTable;
+ size_t m_cEntries;
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Simple Holder for RS object intialization to cooperate with Neutering
+// semantics.
+// The ctor will do an addref.
+// The dtor (invoked in exception) will neuter and release the object. This
+// release will likely be the final release to cause a delete.
+// If the object is created successfully, caller should do a SuppressRelease()
+// to avoid it getting neutered.
+//
+// Example:
+// RSInitHolder<CordbFoo> pFoo(new CordbFoo(x,y,z));
+// pFoo->InitMore(a,b,c);
+// GiveOwnershipToSomebodyElse(pFoo); // now somebody else owns and will clean up
+// pFoo.ClearAndMarkDontNeuter(); // we no longer need to
+//
+// So if an exception is thrown before ClearAndMarkDontNeuter(), the dtor is invoked
+// and the object is properly destroyed (deleted and neutered).
+//
+// Another common pattern is when initializing an object to hand off to an external:
+// RSInitHolder<CordbFoo> pFoo(new CordbFoo(x,y,z));
+// pFoo->InitMore(a,b,c);
+// pFoo.TransferOwnershipExternal(ppOutParameter);
+// TransferOwnershipExternal will assign to ppOutParameter, inc external ref, and
+// call ClearAndMarkDontNeuter()
+//-----------------------------------------------------------------------------
+template<class T>
+class RSInitHolder
+{
+public:
+ // Default ctor. Must call Assign() later.
+ RSInitHolder()
+ {
+ };
+ RSInitHolder(T * pObject)
+ {
+ Assign(pObject);
+ }
+
+ void Assign(T * pObject)
+ {
+ _ASSERTE(m_pObject == NULL); // only assign once.
+ m_pObject.Assign(pObject);
+ }
+ ~RSInitHolder();
+
+ FORCEINLINE operator T *() const
+ {
+ return m_pObject;
+
+ }
+ FORCEINLINE T * operator->()
+ {
+ return m_pObject;
+ }
+
+ // This will null out m_pObject such that the dtor will not neuter it.
+ // This will also release the ref we took in the ctor.
+ // This will clear the current pointer.
+ void ClearAndMarkDontNeuter()
+ {
+ m_pObject.Clear();
+ }
+
+ //
+ // Transfer ownership to a pointer
+ //
+ // Arguments:
+ // ppOutParam - pointer to get ownership. External Reference is incremented.
+ // this pointer should do an external release.
+ //
+ // Notes:
+ // This calls ClearAndMarkDontNeuter(). This holder is Empty after this.
+ template <class TOther>
+ void TransferOwnershipExternal(TOther ** ppOutParam)
+ {
+ *ppOutParam = static_cast<TOther*> (m_pObject);
+ m_pObject->ExternalAddRef();
+
+ ClearAndMarkDontNeuter();
+ }
+
+
+ //
+ // Transfer the ownership of the wrapped object to the given hash table.
+ //
+ // Arguments:
+ // pHashTable - hash table to take ownership.
+ //
+ // Returns:
+ // the contianing object for convenience. Throws on error (particularly
+ // if it fails adding to the hash).
+ //
+ // Notes:
+ // This calls ClearAndMarkDontNeuter(). This holder is Empty after this.
+ T* TransferOwnershipToHash(CordbSafeHashTable<T> * pHashtable)
+ {
+ T* pObject = m_pObject;
+ pHashtable->AddBaseOrThrow(m_pObject);
+ ClearAndMarkDontNeuter();
+ return pObject;
+ }
+
+ //
+ // Used to pass into a function that will assign to us.
+ //
+ // Returns:
+ // Address of this holder. This is like the & operator.
+ // This is provided for consistency with other holders which
+ // override the &operator.
+ RSInitHolder<T> * GetAddr()
+ {
+ return this;
+ }
+
+
+protected:
+ RSSmartPtr<T> m_pObject;
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Have the extra level of indirection is useful for catching Cordbg errors.
+//-----------------------------------------------------------------------------
+#ifdef _DEBUG
+ // On debug, we have an opportunity to catch failing hresults during reproes.
+ #define ErrWrapper(hr) ErrWrapperHelper(hr, __FILE__, __LINE__)
+
+ inline HRESULT ErrWrapperHelper(HRESULT hr, const char * szFile, int line)
+ {
+ if (FAILED(hr))
+ {
+ DWORD dwErr = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgBreakOnErr);
+ if (dwErr)
+ {
+ CONSISTENCY_CHECK_MSGF(false, ("Dbg Error break, hr=0x%08x, '%s':%d", hr, szFile, line));
+ }
+ }
+ return hr;
+ }
+#else
+ // On release, it's just an identity function
+ #define ErrWrapper(hr) (hr)
+#endif
+
+//-----------------------------------------------------------------------------
+// Quick helpers for threading semantics
+//-----------------------------------------------------------------------------
+
+bool IsWin32EventThread(CordbProcess* p);
+bool IsRCEventThread(Cordb* p);
+
+/* ------------------------------------------------------------------------- *
+ * Typedefs
+ * ------------------------------------------------------------------------- */
+
+typedef void* REMOTE_PTR;
+
+
+//-----------------------------------------------------------------------------
+// Wrapper class for locks. This is like Crst on the LS
+//-----------------------------------------------------------------------------
+
+class RSLock
+{
+public:
+ // Attrs, can be bitwise-or together.
+ enum ELockAttr
+ {
+ cLockUninit = 0x00000000,
+ cLockReentrant = 0x00000001,
+ cLockFlat = 0x00000002,
+
+ // (unusual). Not considered a debug API lock, for purposes of deciding whether
+ // to count this lock in m_cTotalDbgApiLocks, which is asserted to be 0 on entry
+ // to public APIs. Example of such a lock: LL_SHIM_PROCESS_DISPOSE_LOCK
+ cLockNonDbgApi = 0x00000004,
+ };
+
+ // To prevent deadlocks, we order all locks.
+ // A thread must acquire higher-numbered locks before lower numbered locks.
+ // These are used as indices into an array, so number them accordingly!
+ enum ERSLockLevel
+ {
+ // Size of the array..
+ LL_MAX = 6,
+
+ // The Stop-Go lock is used to make Stop + Continue be atomic operations.
+ // These methods will toggle the Process-lock b/c they go between multiple threads.
+ // This lock can never be taken on the Win32 ET.
+ LL_STOP_GO_LOCK = 5,
+
+ // The win32-event-thread behaves as if it held a lock at this level.
+ LL_WIN32_EVENT_THREAD = 4,
+
+ // This held for the duration of ShimProcess::Dispose(), and protects
+ // ShimProcess::m_fIsDisposed, so that other ShimProcess functions can
+ // safely execute serially with ShimProcess::Dispose(). This needs to be
+ // a high-level lock, since ShimProcess methods that take this lock also
+ // call into CorDb* objects which take many of the other locks. In contrast,
+ // LL_SHIM_LOCK must remain low-level, as there exists at least one place where
+ // LL_SHIM_LOCK is taken while the CorDbProcess lock is also held (see
+ // CordbThread::GetActiveFunctions which takes the CorDbProcess lock while
+ // calling GetProcess()->GetShim()->LookupOrCreateShimStackWalk(this), which
+ // takes LL_SHIM_LOCK).
+ LL_SHIM_PROCESS_DISPOSE_LOCK = 3,
+
+ // The process lock is the primary lock for a CordbProcess object. It synchronizes
+ // between RCET, W32ET, and user threads.
+ LL_PROCESS_LOCK = 2,
+
+#if defined(FEATURE_DBGIPC_TRANSPORT_DI)
+ LL_DBG_TRANSPORT_MANAGER_LOCK = 1,
+
+ LL_DBG_TRANSPORT_TARGET_LOCK = 0,
+
+ LL_DD_MARSHAL_LOCK = 0,
+#endif // FEATURE_DBGIPC_TRANSPORT_DI
+
+ // These are all leaf locks (they don't take any other lock once they're held).
+ LL_PROCESS_LIST_LOCK = 0,
+
+ // Win32 send lock is shared by all processes accessing a single w32et.
+ LL_WIN32_SEND_LOCK = 0,
+
+ // Small lock around sending IPC events to support workarounds in func-eval abort.
+ // See code:CordbEval::Abort for details.
+ LL_FUNC_EVAL_ABORT_HACK_LOCK = 0,
+
+ // Leaf-level lock used in the shim.
+ LL_SHIM_LOCK = 0
+ };
+
+ // Initialize a lock w/ debugging info. szTag must be a string literal.
+ void Init(const char * szTag, int eAttr, ERSLockLevel level);
+ void Destroy();
+
+ void Lock();
+ void Unlock();
+
+protected:
+ // Accessors for holders.
+ static void HolderEnter(RSLock * pLock)
+ {
+ pLock->Lock();
+ }
+ static void HolderLeave(RSLock * pLock)
+ {
+ pLock->Unlock();
+ }
+
+
+ CRITICAL_SECTION m_lock;
+
+#ifdef _DEBUG
+public:
+ RSLock();
+ ~RSLock();
+
+ const char * Name() { return m_szTag; }
+
+ // Returns true if this thread has the lock.
+ bool HasLock();
+
+ // Returns true if this is safe to take on this thread (ie, this thread
+ // doesn't already hold bigger locks).
+ // bool IsSafeToTake();
+
+ ERSLockLevel GetLevel() { return m_level; }
+
+ // If we're inited, we must have either cLockReentrant or cLockFlat specified.
+ bool IsInit() { return m_eAttr != 0; }
+ bool IsReentrant() { return (m_eAttr & cLockReentrant) == cLockReentrant; }
+ bool IsDbgApiLock() { return ((m_eAttr & cLockNonDbgApi) == 0); }
+
+protected:
+ ERSLockLevel m_level;
+ int m_eAttr; // Bitwise combination of ELockAttr values
+ int m_count;
+ DWORD m_tidOwner;
+ const char * m_szTag;
+
+#endif // #if debug
+
+public:
+ typedef Holder<RSLock *, RSLock::HolderEnter, RSLock::HolderLeave> RSLockHolder;
+ typedef Holder<RSLock *, RSLock::HolderLeave, RSLock::HolderEnter> RSInverseLockHolder;
+
+};
+
+typedef RSLock::RSLockHolder RSLockHolder;
+typedef RSLock::RSInverseLockHolder RSInverseLockHolder;
+
+// In the RS, we should be using RSLocks instead of raw critical sections.
+#define CRITICAL_SECTION USE_RSLOCK_INSTEAD_OF_CRITICAL_SECTION
+
+
+/* ------------------------------------------------------------------------- *
+ * Helper macros. Use the ATT_* macros below instead of these.
+ * ------------------------------------------------------------------------- */
+
+// This serves as glue for exceptions. Eventually, we shouldn't have unrecoverable
+// error, and instead, errors should just propogate up.
+#define SetUnrecoverableIfFailed(__p, __hr) \
+ if (FAILED(__hr)) \
+ { \
+ CORDBSetUnrecoverableError(__p, __hr, 0); \
+ }
+
+#define CORDBSetUnrecoverableError(__p, __hr, __code) \
+ ((__p)->UnrecoverableError((__hr), (__code), __FILE__, __LINE__))
+
+#define _CORDBCheckProcessStateOK(__p) \
+ (!((__p)->m_unrecoverableError) && !((__p)->m_terminated) && !((__p)->m_detached))
+
+#define _CORDBCheckProcessStateOKAndSync(__p, __c) \
+ (!((__p)->m_unrecoverableError) && !((__p)->m_terminated) && !((__p)->m_detached) && \
+ (__p)->GetSynchronized())
+
+// Worker to get failure HR from given state. If not in a failure state, it yields __defaultHR.
+// If a caller knows that we're in a failure state, it can pass in a failure value for __defaultHR.
+#define CORDBHRFromProcessStateWorker(__p, __c, __defaultHR) \
+ ((__p)->m_unrecoverableError ? CORDBG_E_UNRECOVERABLE_ERROR : \
+ ((__p)->m_detached ? CORDBG_E_PROCESS_DETACHED : \
+ ((__p)->m_terminated ? CORDBG_E_PROCESS_TERMINATED : \
+ (!(__p)->GetSynchronized() ? CORDBG_E_PROCESS_NOT_SYNCHRONIZED \
+ : (__defaultHR)))))
+
+#define CORDBHRFromProcessState(__p, __c) \
+ CORDBHRFromProcessStateWorker(__p, __c, S_OK) \
+
+
+// Have a set of helper macros to check the process state and return a failure code.
+// These only should be used at public interface boundaries, in which case we should
+// not be holding the process lock. But we have enough places where we use them internally,
+// so we can't really assert that we're not holding the lock.
+
+// We're very restricted in what APIs we can call on the w32et. Have
+// a convenient check for this.
+// If we have no shim, then nop this check because everything becomes like the w32-event-thread.
+#define CORDBFailOrThrowIfOnWin32EventThread(__p, errorAction) \
+ { \
+ if (((__p)->GetShim() != NULL) && (__p)->IsWin32EventThread()) \
+ { \
+ _ASSERTE(!"Don't call on this thread"); \
+ errorAction(ErrWrapper(CORDBG_E_CANT_CALL_ON_THIS_THREAD)); \
+ } \
+ }
+
+#define CORDBFailIfOnWin32EventThread(__p) CORDBFailOrThrowIfOnWin32EventThread(__p, return)
+
+#define CORDBRequireProcessStateOK(__p) { \
+ if (!_CORDBCheckProcessStateOK(__p)) \
+ return ErrWrapper(CORDBHRFromProcessState(__p, NULL)); }
+
+// If we need to be synced, then we shouldn't be on the win32 Event-Thread.
+#define CORDBRequireProcessStateOKAndSync(__p,__c) { \
+ CORDBFailIfOnWin32EventThread(__p); \
+ if (!_CORDBCheckProcessStateOKAndSync(__p, __c)) \
+ return ErrWrapper(CORDBHRFromProcessState(__p, __c)); }
+
+#define CORDBRequireProcessSynchronized(__p, __c) { \
+ CORDBFailIfOnWin32EventThread(__p); \
+ if (!(__p)->GetSynchronized()) return ErrWrapper(CORDBG_E_PROCESS_NOT_SYNCHRONIZED);}
+
+
+
+
+//-----------------------------------------------------------------------------
+// All public APIS fall into 2 categories regarding their API Threading Type (ATT)
+// We use a standard set of macros to define & enforce each type.
+//
+// (1) ATT_REQUIRE_STOPPED
+// We must be stopped (either synced or at a win32 event) to call this API.
+// - We'll fail if we're not stopped.
+// - If we're stopped, we'll sync. Thus after this API, we're always synced,
+// and Cordbg must call Continue to resume the process.
+// - We'll take the Stop-Go-lock. This prevents another thread from continuing underneath us.
+// - We may send IPC events.
+// Common for APIs like Stacktracing
+//
+// (2) ATT_ALLOW_LIVE
+// We do not have to be stopped to call this API.
+// - We can be live, thus we can not take the stop-go lock (unless it's from a SC-holder).
+// - If we're going to send IPC events, we must use a Stop-Continue holder.
+// - Our stop-status is the same after this API as it was before.
+// Common usage: read-only APIs.
+//
+// (2b) ATT_ALLOW_LIVE_DO_STOPGO.
+// - shortcut macro to do #2, but throw in a stop-continue holder. These really
+// should be in camp #1, but that would require an interface change.
+//-----------------------------------------------------------------------------
+
+// Helper macros for the ATT stuff
+
+// Do checks that need to be done before we take the SG lock. These include checks
+// where if we fail them, taking the SG lock could deadlock (such as being on win32 thread).
+#define DO_PRE_STOP_GO_CHECKS(errorAction) \
+ CORDBFailOrThrowIfOnWin32EventThread(__proc_for_ATT, errorAction) \
+ if ((__proc_for_ATT)->m_unrecoverableError) { errorAction(CORDBG_E_UNRECOVERABLE_ERROR); } \
+
+// Do checks after we take the SG lock. These include checks that rely on state protected
+// by the SG lock.
+#define DO_POST_STOP_GO_CHECKS(errorAction) \
+ _ASSERTE((this->GetProcess() == __proc_for_ATT) || this->IsNeutered()); \
+ if (this->IsNeutered()) { errorAction(CORDBG_E_OBJECT_NEUTERED); } \
+
+// #1
+// The exact details here are rocket-science.
+// We cache the __proc value to a local variable (__proc_for_ATT) so that we don't re-evaluate __proc. (It also forces type-safety).
+// This is essential in case __proc is something like "this->GetProcess()" and which can start returning NULL if 'this'
+// gets neutered underneath us. Caching guarantees that we'll be able to make it to the StopGo-lock.
+//
+// We explicitily check some things before taking the Stop-Go lock:
+// - CORDBG_E_UNRECOVERABLE_ERROR before the lock because if that's set,
+// we may have leaked locks to the outside world, so taking the StopGo lock later could fail.
+// - Are we on the W32et - can't take sg lock if on W32et
+// Then we immediately take the stop-go lock to prevent another thread from continuing underneath us.
+// Then, if we're stopped, we ensure that we're also synced.
+// Stopped includes:
+// - Win32-stopped
+// - fake win32-stopped. Eg, between SuspendUnmanagedThreads & ResumeUnmanagedThreads
+// (one way to get here is getting debug events during the special-deferment region)
+// - synchronized
+// If we're not stopped, then we fail. This macro must never return S_OK.
+//
+// If not-shimmed (using V3 pipeline), then skip all checks about stop-state.
+#define ATT_REQUIRE_STOPPED_MAY_FAIL_OR_THROW(__proc, errorAction) \
+ CordbProcess * __proc_for_ATT = (__proc); \
+ DO_PRE_STOP_GO_CHECKS(errorAction); \
+ RSLockHolder __ch(__proc_for_ATT->GetStopGoLock()); \
+ DO_POST_STOP_GO_CHECKS(errorAction); \
+ if ((__proc_for_ATT)->GetShim() != NULL) { \
+ if (!__proc_for_ATT->m_initialized) { errorAction(CORDBG_E_NOTREADY); } \
+ if ((__proc_for_ATT)->IsStopped()) { \
+ HRESULT _hr2 = (__proc_for_ATT)->StartSyncFromWin32Stop(NULL); \
+ if (FAILED(_hr2)) errorAction(_hr2); \
+ } \
+ if (!_CORDBCheckProcessStateOKAndSync(__proc_for_ATT, NULL)) \
+ errorAction(CORDBHRFromProcessStateWorker(__proc_for_ATT, NULL, E_FAIL)); \
+ }
+
+#define ATT_REQUIRE_STOPPED_MAY_FAIL(__proc)ATT_REQUIRE_STOPPED_MAY_FAIL_OR_THROW(__proc, return)
+
+// #1b - allows it to be non-inited. This should look just like ATT_REQUIRE_STOPPED_MAY_FAIL_OR_THROW
+// except it doesn't do SSFW32Stop and doesn't have the m_initialized check.
+#define ATT_REQUIRE_SYNCED_OR_NONINIT_MAY_FAIL(__proc) \
+ CordbProcess * __proc_for_ATT = (__proc); \
+ DO_PRE_STOP_GO_CHECKS(return); \
+ RSLockHolder __ch(__proc_for_ATT->GetStopGoLock()); \
+ DO_POST_STOP_GO_CHECKS(return); \
+ if ((__proc_for_ATT)->GetShim() != NULL) { \
+ if (!_CORDBCheckProcessStateOKAndSync(__proc_for_ATT, NULL)) \
+ return CORDBHRFromProcessStateWorker(__proc_for_ATT, NULL, E_FAIL); \
+ }
+
+
+
+// Gross variant on #1.
+// This is a very dangerous ATT contract; but we need to support it for backwards compat.
+// Some APIs, like ICDProcess:EnumerateThreads can be used before the process is actually
+// initialized (kind of for interop-debugging).
+// These can't check the m_initialized flag b/c that may not be set yet.
+// They also can't sync the runtime.
+// This should only be used for non-blocking leaf activity.
+#define ATT_EVERETT_HACK_REQUIRE_STOPPED_ALLOW_NONINIT(__proc) \
+ CordbProcess * __proc_for_ATT = (__proc); \
+ DO_PRE_STOP_GO_CHECKS(return); \
+ RSLockHolder __ch(__proc_for_ATT->GetStopGoLock()); \
+ DO_POST_STOP_GO_CHECKS(return); \
+ if (((__proc_for_ATT)->GetShim() != NULL) && !(__proc_for_ATT)->IsStopped()) { return CORDBG_E_PROCESS_NOT_SYNCHRONIZED; } \
+
+
+// #2 - caller may think debuggee is live, but throw in a Stop-Continue holder.
+#define ATT_ALLOW_LIVE_DO_STOPGO(__proc) \
+ CordbProcess * __proc_for_ATT = (__proc); \
+ DO_PRE_STOP_GO_CHECKS(return); \
+ CORDBRequireProcessStateOK(__proc_for_ATT); \
+ RSLockHolder __ch(__proc_for_ATT->GetStopGoLock()); \
+ DO_POST_STOP_GO_CHECKS(return); \
+ StopContinueHolder __hStopGo; \
+ if ((__proc_for_ATT)->GetShim() != NULL) \
+ { \
+ HRESULT _hr2 = __hStopGo.Init(__proc_for_ATT); \
+ if (FAILED(_hr2)) return _hr2; \
+ _ASSERTE((__proc_for_ATT)->GetSynchronized()); \
+ } \
+
+
+
+
+//-----------------------------------------------------------------------------
+// StopContinueHolder. Ensure that we're synced during a certain region.
+// (Particularly when sending an IPCEvent)
+// Calls ICorDebugProcess::Stop & IMDArocess::Continue.
+// Example usage:
+//
+// {
+// StopContinueHolder h;
+// IfFailRet(h.Init(process))
+// SendIPCEvent
+// } // continue automatically called.
+//-----------------------------------------------------------------------------
+
+class CordbProcess;
+class StopContinueHolder
+{
+public:
+ StopContinueHolder() : m_p(NULL) { };
+
+ HRESULT Init(CordbProcess * p);
+ ~StopContinueHolder();
+
+protected:
+ CordbProcess * m_p;
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Base class
+ * ------------------------------------------------------------------------- */
+
+#define COM_METHOD HRESULT STDMETHODCALLTYPE
+
+typedef enum {
+ enumCordbUnknown, // 0
+ enumCordb, // 1 1 [1]x1
+ enumCordbProcess, // 2 1 [1]x1
+ enumCordbAppDomain, // 3 1 [1]x1
+ enumCordbAssembly, // 4
+ enumCordbModule, // 5 15 [27-38,55-57]x1
+ enumCordbClass, // 6
+ enumCordbFunction, // 7
+ enumCordbThread, // 8 2 [4,7]x1
+ enumCordbCode, // 9
+ enumCordbChain, // 10
+ enumCordbChainEnum, // 11
+ enumCordbContext, // 12
+ enumCordbFrame, // 13
+ enumCordbFrameEnum, // 14
+ enumCordbValueEnum, // 15
+ enumCordbRegisterSet, // 16
+ enumCordbJITILFrame, // 17
+ enumCordbBreakpoint, // 18
+ enumCordbStepper, // 19
+ enumCordbValue, // 20
+ enumCordbEnCSnapshot, // 21
+ enumCordbEval, // 22
+ enumCordbUnmanagedThread,// 23
+ enumCorpubPublish, // 24
+ enumCorpubProcess, // 25
+ enumCorpubAppDomain, // 26
+ enumCorpubProcessEnum, // 27
+ enumCorpubAppDomainEnum,// 28
+ enumCordbEnumFilter, // 29
+ enumCordbEnCErrorInfo, // 30
+ enumCordbEnCErrorInfoEnum,//31
+ enumCordbUnmanagedEvent,// 32
+ enumCordbWin32EventThread,//33
+ enumCordbRCEventThread, // 34
+ enumCordbNativeFrame, // 35
+ enumCordbObjectValue, // 36
+ enumCordbType, // 37
+ enumCordbNativeCode, // 38
+ enumCordbILCode, // 39
+ enumCordbEval2, // 40
+ enumCordbMDA, // 41
+ enumCordbHashTableEnum, // 42
+ enumCordbCodeEnum, // 43
+ enumCordbStackWalk, // 44
+ enumCordbEnumerator, // 45
+ enumCordbHeap, // 48
+ enumCordbHeapSegments, // 47
+ enumMaxDerived, //
+ enumMaxThis = 1024
+} enumCordbDerived;
+
+
+
+//-----------------------------------------------------------------------------
+// Support for Native Breakpoints
+//-----------------------------------------------------------------------------
+struct NativePatch
+{
+ void * pAddress; // pointer into the LS address space.
+ PRD_TYPE opcode; // opcode to restore with.
+
+ inline bool operator==(NativePatch p2)
+ {
+ return memcmp(this, &p2, sizeof(p2)) == 0;
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Cross-platform patch operations
+//-----------------------------------------------------------------------------
+
+// Remove the int3 from the remote address
+HRESULT RemoveRemotePatch(CordbProcess * pProcess, const void * pRemoteAddress, PRD_TYPE opcode);
+
+// This flavor is assuming our caller already knows the opcode.
+HRESULT ApplyRemotePatch(CordbProcess * pProcess, const void * pRemoteAddress);
+
+// Apply the patch and get the opcode that we're replacing.
+HRESULT ApplyRemotePatch(CordbProcess * pProcess, const void * pRemoteAddress, PRD_TYPE * pOpcode);
+
+
+class CordbHashTable;
+
+#define CORDB_COMMON_BASE_SIGNATURE 0x0d00d96a
+#define CORDB_COMMON_BASE_SIGNATURE_DEAD 0x0dead0b1
+
+// Common base for both CorPublish + CorDebug objects.
+class CordbCommonBase : public IUnknown
+{
+public:
+ // GENERIC: made this private as I'm changing the use of m_id for CordbClass, and
+ // I want to make sure I catch all the places where m_id is used directly and cast
+ // to/from tokens and/or (void*).
+ UINT_PTR m_id;
+
+#ifdef _DEBUG
+ static LONG m_saDwInstance[enumMaxDerived]; // instance x this
+ static LONG m_saDwAlive[enumMaxDerived];
+ static PVOID m_sdThis[enumMaxDerived][enumMaxThis];
+ DWORD m_dwInstance;
+ enumCordbDerived m_type;
+#endif
+
+
+
+private:
+ DWORD m_signature : 30;
+
+ // Sticky bit set when we neuter an object. All methods (besides AddRef,Release,QI)
+ // should check this bit and fail via the FAIL_IF_NEUTERED macro.
+ DWORD m_fIsNeutered : 1;
+
+ // Mark that this object can be "neutered at will". NeuterList::SweepAllNeuterAtWillObjects
+ // looks at this bit.
+ // For some objects, we don't explicitly mark when the lifetime is up. The only way
+ // we know is when external count goes to 0. This avoids forcing us to do cleanup
+ // in the dtor (which may come at a bad time). Sticky bit set in BaseRelease().
+ DWORD m_fNeuterAtWill : 1;
+public:
+
+ static LONG s_CordbObjectUID; // Unique ID for each object.
+ static LONG s_TotalObjectCount; // total number of outstanding objects.
+
+
+ void ValidateObject()
+ {
+ if( !IsValidObject() )
+ {
+ STRESS_LOG1(LF_ASSERT, LL_ALWAYS, "CordbCommonBase::IsValidObject() failed: %x\n", this);
+ _ASSERTE(!"CordbCommonBase::IsValidObject() failed");
+ FreeBuildDebugBreak();
+ }
+ }
+
+ bool IsValidObject()
+ {
+ return (m_signature == CORDB_COMMON_BASE_SIGNATURE);
+ }
+
+ CordbCommonBase(UINT_PTR id, enumCordbDerived type)
+ {
+ init(id, type);
+ }
+
+ CordbCommonBase(UINT_PTR id)
+ {
+ init(id, enumCordbUnknown);
+ }
+
+ void init(UINT_PTR id, enumCordbDerived type)
+ {
+ // To help us track object leaks, we want to log when we create & destory CordbBase objects.
+#ifdef _DEBUG
+ InterlockedIncrement(&s_TotalObjectCount);
+ InterlockedIncrement(&s_CordbObjectUID);
+
+ LOG((LF_CORDB, LL_EVERYTHING, "Memory: CordbBase object allocated: this=%p, count=%d, id=%p, Type=%d\n", this, s_CordbObjectUID, id, type));
+#endif
+
+ m_signature = CORDB_COMMON_BASE_SIGNATURE;
+ m_fNeuterAtWill = 0;
+ m_fIsNeutered = 0;
+
+ m_id = id;
+ m_RefCount = 0;
+
+#ifdef _DEBUG
+ m_type = type;
+ //m_dwInstance = CordbBase::m_saDwInstance[m_type];
+ //InterlockedIncrement(&CordbBase::m_saDwInstance[m_type]);
+ //InterlockedIncrement(&CordbBase::m_saDwAlive[m_type]);
+ //if (m_dwInstance < enumMaxThis)
+ //{
+ // m_sdThis[m_type][m_dwInstance] = this;
+ //}
+#endif
+ }
+
+ virtual ~CordbCommonBase()
+ {
+ // If we're deleting, we really should have released any outstanding reference.
+ // If we call Release() on a deleted object, we'll av (especially b/c Release
+ // may call delete again).
+ CONSISTENCY_CHECK_MSGF(m_RefCount == 0, ("Deleting w/ non-zero ref count. 0x%08x", m_RefCount));
+
+#ifdef _DEBUG
+ //InterlockedDecrement(&CordbBase::m_saDwAlive[m_type]);
+ //if (m_dwInstance < enumMaxThis)
+ //{
+ // m_sdThis[m_type][m_dwInstance] = NULL;
+ //}
+#endif
+ // To help us track object leaks, we want to log when we create & destory CordbBase objects.
+ LOG((LF_CORDB, LL_EVERYTHING, "Memory: CordbBase object deleted: this=%p, id=%p, Refcount=0x%x\n", this, m_id, m_RefCount));
+
+#ifdef _DEBUG
+ LONG newTotalObjectsCount = InterlockedDecrement(&s_TotalObjectCount);
+ _ASSERTE(newTotalObjectsCount >= 0);
+#endif
+
+ // Don't shutdown logic until everybody is done with it.
+ // If we leak objects, this may mean that we never shutdown logging at all!
+#if defined(_DEBUG) && defined(LOGGING)
+ if (newTotalObjectsCount == 0)
+ {
+ ShutdownLogging();
+ }
+#endif
+ }
+
+ /*
+ Member function behavior of a neutered COM object:
+
+ 1. AddRef(), Release(), QueryInterface() work as normal.
+ a. This gives folks who are responsible for pairing a Release() with
+ an AddRef() a chance to dereference their pointer and call Release()
+ when they are informed, explicitly or implicitly, that the object is neutered.
+
+ 2. Any other member function will return an error code unless documented.
+ a. If a member function returns information when the COM object is
+ neutered then the semantics of that function need to be documented.
+ (ie. If an AppDomain is unloaded and you have a reference to the COM
+ object representing the AppDomain, how _should_ it behave? That behavior
+ should be documented)
+
+
+ Postcondions of Neuter():
+
+ 1. All circular references (aka back-pointers) are "broken". They are broken
+ by calling Release() on all "Weak References" to the object. If you're a purist,
+ these pointers should also be NULLed out.
+ a. Weak References/Strong References:
+ i. If any objects are not "reachable" from the root (ie. stack or from global pointers)
+ they should be reclaimed. If they are not, they are leaked and there is an issue.
+ ii. There must be a partial order on the objects such that if A < B then:
+ 1. A has a reference to B. This reference is a "strong reference"
+ 2. A, and thus B, is reachable from the root
+ iii. If a reference belongs in the partial order then it is a "strong reference" else
+ it is a weak reference.
+ *** 2. Sufficient conditions to ensure no COM objects are leaked: ***
+ a. When Neuter() is invoked:
+ i. Calles Release on all its weak references.
+ ii. Then, for each strong reference:
+ 1. invoke Neuter()
+ 2. invoke Release()
+ iii. If it's derived from a CordbXXX class, call Neuter() on the base class.
+ 1. Sense Neuter() is virtual, use the scope specifier Cordb[BaseClass]::Neuter().
+ 3. All members return error codes, except:
+ a. Members of IUknown, AddRef(), Release(), QueryInterfac()
+ b. Those documented to have functionality when the object is neutered.
+ i. Neuter() still works w/o error. If it is invoke a second time it will have already
+ released all its strong and weak references so it could just return.
+
+
+ Alternate design ideas:
+
+ DESIGN: Note that it's possible for object B to have two parents in the partial order
+ and it must be documented which one is responsible for calling Neuter() on B.
+ 1. For example, CordbCode could reasonably be a sibling of CordbFunction and CordbNativeFrame.
+ Which one should call Release()? For now we have CordbFunction call Release() on CordbCode.
+
+ DESIGN: It is not a necessary condition in that Neuter() invoke Release() on all
+ it's strong references. Instead, it would be sufficient to ensure all object are released, that
+ each object call Release() on all its strong pointers in its destructor.
+ 1. This might be done if its necessary for some member to return "tombstone"
+ information after the object has been netuered() which involves the siblings (wrt poset)
+ of the object. However, no sibling could access a parent (wrt poset) because
+ Neuter called Release() on all its weak pointers.
+
+ DESIGN: Rename Neuter() to some name that more accurately reflect the semantics.
+ 1. The three operations are:
+ a. ReleaseWeakPointers()
+ b. NeuterStrongPointers()
+ c. ReleaseStrongPointers()
+ 1. Assert that it's done after NeuterStrongPointers()
+ 2. That would introduce a bunch of functions... but it would be clear.
+
+ DESIGN: CordbBase could provide a function to register strong and weak references. That way CordbBase
+ could implement a general version of ReleaseWeak/ReleaseStrong/NeuterStrongPointers(). This
+ would provide a very error resistant framework for extending the object model plus it would
+ be very explicit about what is going on.
+ One thing that might trip this is idea up is that if an object has two parents,
+ like the CordbCode might, then either both objects call Neuter or one is reference
+ is made weak.
+
+
+ Our implementation:
+
+ The graph formed by the strong references must remain acyclic.
+ It's up to the developer (YOU!) to ensure that each Neuter
+ function maintains that invariant.
+
+ Here is the current Partial Order on CordbXXX objects. (All these classes
+ eventually chain to CordbBase.Neuter() for completeness.)
+
+ Cordb
+ CordbProcess
+ CordbAppDomain
+ CordbBreakPoints
+ CordbAssembly
+ CordbModule
+ CordbClass
+ CordbFunction
+ CordbCode (Can we assert a thread will not reference
+ the same CordbCode as a CordbFunction?)
+ CordbThread
+ CordbChains
+ CordbNativeFrame -> CordbFrame (Chain to baseClass)
+ CordbJITILFrame
+
+
+ <TODO>TODO: Some Neuter functions have not yet been implemented due to time restrictions.</TODO>
+
+ <TODO>TODO: Some weak references never have AddRef() called on them. If that's cool then
+ it should be stated in the documentation. Else it should be changed.</TODO>
+*/
+
+ virtual void Neuter();
+
+ // Unsafe neuter for an object that's already dead.
+ void UnsafeNeuterDeadObject();
+
+
+#ifdef _DEBUG
+ // For debugging (asserts, logging, etc) provide a pretty name (this is 1:1 w/ the VTable)
+ // We provide a default impl in the base object in case this gets called from a dtor (virtuals
+ // called from dtors use the base version, not the derived). A pure call would AV in that case.
+ virtual const char * DbgGetName() { return "CordbBase"; };
+#endif
+
+ bool IsNeutered() const {LIMITED_METHOD_CONTRACT; return m_fIsNeutered == 1; }
+ bool IsNeuterAtWill() const { LIMITED_METHOD_CONTRACT; return m_fNeuterAtWill == 1; }
+ void MarkNeuterAtWill() { LIMITED_METHOD_CONTRACT; m_fNeuterAtWill = 1; }
+
+ //-----------------------------------------------------------
+ // IUnknown support
+ //----------------------------------------------------------
+
+private:
+ // We maintain both an internal + external refcount. This allows us to catch
+ // if an external caller has too many releases.
+ // low bits are internal count, high bits are external count
+ // so Total count = (m_RefCount & CordbBase_InternalRefCountMask) + (m_RefCount >> CordbBase_ExternalRefCountShift);
+ typedef LONGLONG MixedRefCountSigned;
+ typedef ULONGLONG MixedRefCountUnsigned;
+ typedef LONG ExternalRefCount;
+ MixedRefCountUnsigned m_RefCount;
+public:
+
+ // Adjust the internal ref count.
+ // These aren't available to the external world, so only internal code can manipulate the internal count.
+ void InternalAddRef();
+ void InternalRelease();
+
+ // Derived versions of AddRef / Release will call these.
+ // External AddRef & Release
+ // These do not have any additional Asserts to enforce that we're not manipulating the external count
+ // from internal.
+ ULONG STDMETHODCALLTYPE BaseAddRef();
+ ULONG STDMETHODCALLTYPE BaseRelease();
+
+ // External ref count versions, with extra debug count to enforce that this is done externally.
+ // When derive classes use these versions, it Asserts that we're not adjusting external counts from inside.
+ // Thus we can be confident that we're *never* leaking external refs to these objects.
+ // @todo - eventually everything should use these.
+ ULONG STDMETHODCALLTYPE BaseAddRefEnforceExternal();
+ ULONG STDMETHODCALLTYPE BaseReleaseEnforceExternal();
+
+ // Do an AddRef against the External count. This is a semantics issue.
+ // We use this when an internal component Addrefs out-parameters (which Cordbg will call Release on).
+ // This just does a regular external AddRef().
+ void ExternalAddRef();
+
+protected:
+
+ static void InitializeCommon();
+
+private:
+ static void AddDebugPrivilege();
+};
+
+#define CordbBase_ExternalRefCountShift 32
+#define CordbBase_InternalRefCountMask 0xFFFFFFFF
+#define CordbBase_InternalRefCountMax 0x7FFFFFFF
+
+#ifdef _DEBUG
+// Does the given Cordb object type have affinity to a CordbProcess object?
+// This is only used for certain asserts.
+inline bool DoesCordbObjectTypeHaveProcessPtr(enumCordbDerived type)
+{
+ return
+ (type != enumCordbCodeEnum) &&
+ (type != enumCordb) &&
+ (type != enumCordbHashTableEnum);
+}
+#endif
+
+// Base class specifically for CorDebug objects
+class CordbBase : public CordbCommonBase
+{
+public:
+ CordbBase(CordbProcess * pProcess, UINT_PTR id, enumCordbDerived type) : CordbCommonBase(id, type)
+ {
+ // CordbProcess can't pass 'this' to base class, per error C4355. So we pass null and set later.
+ _ASSERTE((pProcess != NULL) ||
+ ((type) == enumCordbProcess) ||
+ !DoesCordbObjectTypeHaveProcessPtr(type));
+
+ m_pProcess.Assign(pProcess);
+ }
+
+ CordbBase(CordbProcess * pProcess, UINT_PTR id) : CordbCommonBase(id)
+ {
+ _ASSERTE(pProcess != NULL);
+ m_pProcess.Assign(pProcess);
+ }
+
+ virtual ~CordbBase()
+ {
+ // Derived classes should not have cleared out our pointer.
+ // CordbProcess's Neuter explicitly nulls out its pointer to avoid circular reference.
+ _ASSERTE(m_pProcess!= NULL ||
+ (CordbCommonBase::m_type == enumCordbProcess) ||
+ !DoesCordbObjectTypeHaveProcessPtr(CordbCommonBase::m_type));
+
+ // Ideally, all CorDebug objects to be neutered by the time their dtor is called.
+ // @todo - we're still working out neutering semantics for a few remaining objects, so we exclude
+ // those from the assert.
+ _ASSERTE(IsNeutered() ||
+ (m_type == enumCordbBreakpoint) ||
+ (m_type == enumCordbStepper));
+ }
+
+ // Neuter just the right-side state.
+ virtual void Neuter();
+
+ // Neuter both left-side state and right-side state.
+ virtual void NeuterLeftSideResources();
+
+ // Get the CordbProcess object that this CordbBase object is associated with (or NULL if there's none).
+ CordbProcess * GetProcess() const
+ {
+ return m_pProcess;
+ }
+protected:
+ // All objects need a strong pointer back to the process so that they can get access to key locks
+ // held by the process (StopGo lock) so that they can synchronize their operations against neutering.
+ // This pointer is cleared in our dtor, and not when we're neutered. Since we can't control when the
+ // dtor is called (it's controlled by external references), we classify this as an external reference too.
+ //
+ // This is the only "strong" reference backpointer that objects need have. All other backpointers can be weak references
+ // because when a parent object is neutered, it will null out all weak reference pointers in all of its children.
+ // That will also break any potential cycles.
+ RSUnsafeExternalSmartPtr<CordbProcess> m_pProcess;
+
+};
+
+
+
+
+
+//-----------------------------------------------------------------------------
+// Macro to check if a CordbXXX object is neutered, and return a standard
+// error code if it is.
+// We pass the 'this' pointer of the object in because it gives us some extra
+// flexibility and lets us log debug info.
+// It is an API breach to access a neutered object.
+//-----------------------------------------------------------------------------
+#define FAIL_IF_NEUTERED(pThis) \
+int _____Neuter_Status_Already_Marked; \
+_____Neuter_Status_Already_Marked = 0; \
+{\
+ if (pThis->IsNeutered()) { \
+ LOG((LF_CORDB, LL_ALWAYS, "Accessing a neutered object at %p\n", pThis)); \
+ return ErrWrapper(CORDBG_E_OBJECT_NEUTERED); \
+ } \
+}
+
+//-----------------------------------------------------------------------------
+// Macro to check if a CordbXXX object is neutered, and return a standard
+// error code if it is.
+// We pass the 'this' pointer of the object in because it gives us some extra
+// flexibility and lets us log debug info.
+// It is an API breach to access a neutered object.
+//-----------------------------------------------------------------------------
+#define THROW_IF_NEUTERED(pThis) \
+int _____Neuter_Status_Already_Marked; \
+_____Neuter_Status_Already_Marked = 0; \
+{\
+ if (pThis->IsNeutered()) { \
+ LOG((LF_CORDB, LL_ALWAYS, "Accessing a neutered object at %p\n", pThis)); \
+ ThrowHR(CORDBG_E_OBJECT_NEUTERED); \
+ } \
+}
+
+// We have an OK_IF_NEUTERED macro to say that this method can be safely
+// called if we're neutered. Mostly for semantic benefits.
+// Also, if a method is marked OK, then somebody won't go and add a 'fail'
+// This is an extremely dangerous quality because:
+// 1) it means that we have no synchronization (can't take the Stop-Go lock)
+// 2) none of our backpointers are usable (they may be nulled out at anytime by another thread).
+// - this also means we absolutely can't send IPC events (since that requires a CordbProcess)
+// 3) The only safe data are blittalbe embedded fields (eg, a pid or stack range)
+//
+// Any usage of this macro should clearly specify why this is safe.
+#define OK_IF_NEUTERED(pThis) \
+int _____Neuter_Status_Already_Marked; \
+_____Neuter_Status_Already_Marked = 0;
+
+
+//-------------------------------------------------------------------------------
+// Simple COM enumerator pattern on a fixed list of items
+//--------------------------------------------------------------------------------
+template< typename ElemType,
+ typename ElemPublicType,
+ typename EnumInterfaceType,
+ ElemPublicType (*GetPublicType)(ElemType)>
+class CordbEnumerator : public CordbBase, EnumInterfaceType
+{
+private:
+ // the list of items being enumerated over
+ ElemType *m_items;
+ // the number of items in the list
+ DWORD m_countItems;
+ // the index of the next item to be returned in the enumeration
+ DWORD m_nextIndex;
+
+public:
+ // makes a copy of the elements in the "items" array
+ CordbEnumerator(CordbProcess* pProcess, ElemType *items, DWORD elemCount);
+ // assumes ownership of the elements in the "*items" array.
+ // this avoids an extra allocation + copy
+ CordbEnumerator(CordbProcess* pProcess, ElemType **items, DWORD elemCount);
+ ~CordbEnumerator();
+
+// IUnknown interface
+ virtual COM_METHOD QueryInterface(REFIID riid, VOID** ppInterface);
+ virtual ULONG __stdcall AddRef();
+ virtual ULONG __stdcall Release();
+
+// ICorDebugEnum interface
+ virtual COM_METHOD Clone(ICorDebugEnum **ppEnum);
+ virtual COM_METHOD GetCount(ULONG *pcelt);
+ virtual COM_METHOD Reset();
+ virtual COM_METHOD Skip(ULONG celt);
+
+// ICorDebugXXXEnum interface
+ virtual COM_METHOD Next(ULONG celt, ElemPublicType items[], ULONG *pceltFetched);
+
+// CordbBase overrides
+ virtual VOID Neuter();
+};
+
+// Converts T to U* by using QueryInterface
+template<typename T, typename U>
+U* QueryInterfaceConvert(T obj);
+
+// No conversion, just returns the argument
+template<typename T>
+T IdentityConvert(T obj);
+
+// CorDebugGuidToTypeMapping-adapter used by CordbGuidToTypeEnumerator
+// in the CordbEnumerator pattern
+struct RsGuidToTypeMapping
+{
+ GUID iid;
+ RSSmartPtr<CordbType> spType;
+};
+
+inline
+CorDebugGuidToTypeMapping GuidToTypeMappingConvert(RsGuidToTypeMapping m)
+{
+ CorDebugGuidToTypeMapping result;
+ result.iid = m.iid;
+ result.pType = (ICorDebugType*)(m.spType.GetValue());
+ result.pType->AddRef();
+ return result;
+}
+
+//
+// Some useful enumerators
+//
+typedef CordbEnumerator<RSSmartPtr<CordbThread>,
+ ICorDebugThread*,
+ ICorDebugThreadEnum,
+ QueryInterfaceConvert<RSSmartPtr<CordbThread>, ICorDebugThread> > CordbThreadEnumerator;
+
+typedef CordbEnumerator<CorDebugBlockingObject,
+ CorDebugBlockingObject,
+ ICorDebugBlockingObjectEnum,
+ IdentityConvert<CorDebugBlockingObject> > CordbBlockingObjectEnumerator;
+
+// Template classes must be fully defined rather than just declared in the header
+#include "rsenumerator.hpp"
+
+
+typedef CordbEnumerator<COR_SEGMENT,
+ COR_SEGMENT,
+ ICorDebugHeapSegmentEnum,
+ IdentityConvert<COR_SEGMENT> > CordbHeapSegmentEnumerator;
+
+typedef CordbEnumerator<CorDebugExceptionObjectStackFrame,
+ CorDebugExceptionObjectStackFrame,
+ ICorDebugExceptionObjectCallStackEnum,
+ IdentityConvert<CorDebugExceptionObjectStackFrame> > CordbExceptionObjectCallStackEnumerator;
+
+typedef CordbEnumerator<RsGuidToTypeMapping,
+ CorDebugGuidToTypeMapping,
+ ICorDebugGuidToTypeEnum,
+ GuidToTypeMappingConvert > CordbGuidToTypeEnumerator;
+
+// ----------------------------------------------------------------------------
+// Hash table for CordbBase objects.
+// - Uses Internal AddRef/Release (not external)
+// - Templatize for type-safety w/ Cordb objects
+// - Many hashtables are implicitly protected by a lock. For debug-only, we
+// explicitly associate w/ an optional RSLock and assert that lock is held on access.
+// ----------------------------------------------------------------------------
+
+struct CordbHashEntry
+{
+ FREEHASHENTRY entry;
+ CordbBase *pBase;
+};
+
+class CordbHashTable : private CHashTableAndData<CNewDataNoThrow>
+{
+private:
+ bool m_initialized;
+ SIZE_T m_count;
+
+ BOOL Cmp(SIZE_T k1, const HASHENTRY * pc2)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return ((ULONG_PTR)k1) != (reinterpret_cast<const CordbHashEntry *>(pc2))->pBase->m_id;
+ }
+
+ ULONG HASH(ULONG_PTR id)
+ {
+ return (ULONG)(id);
+ }
+
+ SIZE_T KEY(UINT_PTR id)
+ {
+ return (SIZE_T)id;
+ }
+
+public:
+ bool IsInitialized();
+
+#ifndef DACCESS_COMPILE
+ CordbHashTable(ULONG size)
+ : CHashTableAndData<CNewDataNoThrow>(size), m_initialized(false), m_count(0)
+ {
+#ifdef _DEBUG
+ m_pDbgLock = NULL;
+ m_dbgChangeCount = 0;
+#endif
+ }
+ virtual ~CordbHashTable();
+
+#ifdef _DEBUG
+ // CordbHashTables may be protected by a lock. For debug-builds, we can associate
+ // the hash w/ that lock and then assert if it's not held.
+ void DebugSetRSLock(RSLock * pLock)
+ {
+ m_pDbgLock = pLock;
+ }
+ int GetChangeCount() { return m_dbgChangeCount; }
+private:
+ void AssertIsProtected();
+
+ // Increment the Change count. This can be used to check if the hashtable changes while being enumerated.
+ void DbgIncChangeCount() { m_dbgChangeCount++; }
+
+ int m_dbgChangeCount;
+ RSLock * m_pDbgLock;
+#else
+ // RSLock association is a no-op on free builds.
+ void AssertIsProtected() { };
+ void DbgIncChangeCount() { };
+#endif // _DEBUG
+
+public:
+
+
+#endif
+
+ ULONG32 GetCount()
+ {
+ return ((ULONG32)m_count);
+ }
+
+ // These operators are unsafe b/c they have no typesafety.
+ // Use a derived CordbSafeHashTable<T> instead.
+ HRESULT UnsafeAddBase(CordbBase *pBase);
+ HRESULT UnsafeSwapBase(CordbBase* pBaseOld, CordbBase* pBaseNew);
+ CordbBase *UnsafeGetBase(ULONG_PTR id, BOOL fFab = TRUE);
+ CordbBase *UnsafeRemoveBase(ULONG_PTR id);
+
+ CordbBase *UnsafeFindFirst(HASHFIND *find);
+ CordbBase *UnsafeFindNext(HASHFIND *find);
+
+ // Unlocked versions don't assert that the lock us held.
+ CordbBase *UnsafeUnlockedFindFirst(HASHFIND *find);
+ CordbBase *UnsafeUnlockedFindNext(HASHFIND *find);
+
+};
+
+
+// Typesafe wrapper around a normal hash table
+// T is expected to be a derived clas of CordbBase
+// Note that this still isn't fully typesafe. Ideally we'd take a strongly-typed key
+// instead of UINT_PTR (the type could have a fixed relationship to T, or could be
+// an additional template argument like standard template hash tables like std::hash_map<K,V>)
+template <class T>
+class CordbSafeHashTable : public CordbHashTable
+{
+public:
+#ifndef DACCESS_COMPILE
+ CordbSafeHashTable<T>(ULONG size) : CordbHashTable(size)
+ {
+ }
+#endif
+ // Typesafe wrappers
+ HRESULT AddBase(T * pBase) { return UnsafeAddBase(pBase); }
+
+ // Either add (eg, future cals to GetBase will succeed) or throw.
+ void AddBaseOrThrow(T * pBase)
+ {
+ HRESULT hr = AddBase(pBase);
+ IfFailThrow(hr);
+ }
+ HRESULT SwapBase(T* pBaseOld, T* pBaseNew) { return UnsafeSwapBase(pBaseOld, pBaseNew); }
+ // Move the function definition of GetBase to rspriv.inl to work around gcc 2.9.5 warnings
+ T* GetBase(ULONG_PTR id, BOOL fFab = TRUE);
+ T* GetBaseOrThrow(ULONG_PTR id, BOOL fFab = TRUE);
+
+ T* RemoveBase(ULONG_PTR id) { return static_cast<T*>(UnsafeRemoveBase(id)); }
+
+ T* FindFirst(HASHFIND *find) { return static_cast<T*>(UnsafeFindFirst(find)); }
+ T* FindNext(HASHFIND *find) { return static_cast<T*>(UnsafeFindNext(find)); }
+
+ // Neuter all items and clear
+ void NeuterAndClear(RSLock * pLock);
+
+ void CopyToArray(RSPtrArray<T> * pArray);
+ void TransferToArray(RSPtrArray<T> * pArray);
+};
+
+
+class CordbHashTableEnum : public CordbBase,
+public ICorDebugProcessEnum,
+public ICorDebugBreakpointEnum,
+public ICorDebugStepperEnum,
+public ICorDebugThreadEnum,
+public ICorDebugModuleEnum,
+public ICorDebugAppDomainEnum,
+public ICorDebugAssemblyEnum
+{
+ // Private ctors. Use build function to access.
+ CordbHashTableEnum(
+ CordbBase * pOwnerObj,
+ NeuterList * pOwnerList,
+ CordbHashTable *table,
+ const _GUID &id);
+
+public:
+ static void BuildOrThrow(
+ CordbBase * pOwnerObj,
+ NeuterList * pOwnerList,
+ CordbHashTable *table,
+ const _GUID &id,
+ RSInitHolder<CordbHashTableEnum> * pHolder);
+
+ CordbHashTableEnum(CordbHashTableEnum *cloneSrc);
+
+ ~CordbHashTableEnum();
+ virtual void Neuter();
+
+
+#ifdef _DEBUG
+ // For debugging (asserts, logging, etc) provide a pretty name (this is 1:1 w/ the VTable)
+ virtual const char * DbgGetName() { return "CordbHashTableEnum"; };
+#endif
+
+
+ HRESULT Next(ULONG celt, CordbBase *bases[], ULONG *pceltFetched);
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Skip(ULONG celt);
+ COM_METHOD Reset();
+ COM_METHOD Clone(ICorDebugEnum **ppEnum);
+ COM_METHOD GetCount(ULONG *pcelt);
+
+ //-----------------------------------------------------------
+ // ICorDebugProcessEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Next(ULONG celt, ICorDebugProcess *processes[],
+ ULONG *pceltFetched)
+ {
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(processes, ICorDebugProcess *,
+ celt, true, true);
+ VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *);
+
+ return (Next(celt, (CordbBase **)processes, pceltFetched));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugBreakpointEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Next(ULONG celt, ICorDebugBreakpoint *breakpoints[],
+ ULONG *pceltFetched)
+ {
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(breakpoints, ICorDebugBreakpoint *,
+ celt, true, true);
+ VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *);
+
+ return (Next(celt, (CordbBase **)breakpoints, pceltFetched));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugStepperEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Next(ULONG celt, ICorDebugStepper *steppers[],
+ ULONG *pceltFetched)
+ {
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(steppers, ICorDebugStepper *,
+ celt, true, true);
+ VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *);
+
+ return (Next(celt, (CordbBase **)steppers, pceltFetched));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugThreadEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Next(ULONG celt, ICorDebugThread *threads[],
+ ULONG *pceltFetched)
+ {
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(threads, ICorDebugThread *,
+ celt, true, true);
+ VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *);
+
+ return (Next(celt, (CordbBase **)threads, pceltFetched));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugModuleEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Next(ULONG celt, ICorDebugModule *modules[],
+ ULONG *pceltFetched)
+ {
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(modules, ICorDebugModule *,
+ celt, true, true);
+ VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *);
+
+ return (Next(celt, (CordbBase **)modules, pceltFetched));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugAppDomainEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Next(ULONG celt, ICorDebugAppDomain *appdomains[],
+ ULONG *pceltFetched)
+ {
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(appdomains, ICorDebugAppDomain *,
+ celt, true, true);
+ VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *);
+
+ return (Next(celt, (CordbBase **)appdomains, pceltFetched));
+ }
+ //-----------------------------------------------------------
+ // ICorDebugAssemblyEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Next(ULONG celt, ICorDebugAssembly *assemblies[],
+ ULONG *pceltFetched)
+ {
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(assemblies, ICorDebugAssembly *,
+ celt, true, true);
+ VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *);
+
+ return (Next(celt, (CordbBase **)assemblies, pceltFetched));
+ }
+private:
+ // Owning object is our link to the CordbProcess* tree. Never null until we're neutered.
+ // NeuterList is related to the owning object. Need to cache it so that we can pass it on
+ // to our clones.
+ CordbBase * m_pOwnerObj; // provides us w/ a CordbProcess*
+ NeuterList * m_pOwnerNeuterList;
+
+
+ CordbHashTable *m_table;
+ bool m_started;
+ bool m_done;
+ HASHFIND m_hashfind;
+ REFIID m_guid;
+ ULONG m_iCurElt;
+ ULONG m_count;
+ BOOL m_fCountInit;
+
+#ifdef _DEBUG
+ // timestampt of hashtable when we start enumerating it. Useful for detecting if the table
+ // changes underneath us.
+ int m_DbgChangeCount;
+ void AssertValid();
+#else
+ void AssertValid() { }
+#endif
+
+private:
+ //These factor code between Next & Skip
+ HRESULT PrepForEnum(CordbBase **pBase);
+
+ // Note that the set of types advanced by Pre & by Post are disjoint, and
+ // that the union of these two sets are all possible types enuerated by
+ // the CordbHashTableEnum.
+ HRESULT AdvancePreAssign(CordbBase **pBase);
+ HRESULT AdvancePostAssign(CordbBase **pBase,
+ CordbBase **b,
+ CordbBase **bEnd);
+
+ // This factors some code that initializes the module enumerator.
+ HRESULT SetupModuleEnum();
+
+};
+
+
+//-----------------------------------------------------------------------------
+// Neuter List
+// Dtors can be called at any time (whenever Cordbg calls Release, which is outside
+// of our control), so we never want to do significant work in a dtor
+// (this includes sending IPC events + neutering).
+// So objects can queue themselves up to be neutered at a safe time.
+//
+// Items in a NeuterList should only contain state in the Right-Side.
+// If the item holds resources in the left-side, it should be placed on a
+// code:LeftSideResourceCleanupList
+//-----------------------------------------------------------------------------
+class NeuterList
+{
+public:
+ NeuterList();
+ ~NeuterList();
+
+ // Add an object to be neutered.
+ // Anybody calls this to add themselves to the list.
+ // This will add it to the list and maintain an internal reference to it.
+ void Add(CordbProcess * pProcess, CordbBase * pObject);
+
+ // Add w/o checking for safety. Should only be used by Process-list enum.
+ void UnsafeAdd(CordbProcess * pProcess, CordbBase * pObject);
+
+ // Neuter everything on the list.
+ // This should only be called by the "owner", but we can't really enforce that.
+ // This will release all internal references and empty the list.
+ void NeuterAndClear(CordbProcess * pProcess);
+
+ // Sweep for all objects that are marked as 'm_fNeuterAtWill'.
+ // Neuter and remove these.
+ void SweepAllNeuterAtWillObjects(CordbProcess * pProcess);
+
+protected:
+ struct Node
+ {
+ RSSmartPtr<CordbBase> m_pObject;
+ Node * m_pNext;
+ };
+
+ // Manipulating the list is done under the Process lock.
+ Node * m_pHead;
+};
+
+//-----------------------------------------------------------------------------
+// This list is for objects that hold left-side resources.
+// If the object does not hold left-side resources, it can be placed on a
+// code:NeuterList
+//-----------------------------------------------------------------------------
+class LeftSideResourceCleanupList : public NeuterList
+{
+public:
+ // dispose everything contained in the list by calling SafeDispose() on each element
+ void SweepNeuterLeftSideResources(CordbProcess * pProcess);
+ void NeuterLeftSideResourcesAndClear(CordbProcess * pProcess);
+};
+
+//-------------------------------------------------------------------------
+//
+// Optional<T>
+// Stores a value along with a bit indicating whether the value is valid.
+//
+// This is particularly useful for LS data read via DAC. We need to gracefully
+// handle missing data, and we may want to track independent pieces of data
+// separately (often with lazy initialization). It's essential that we can't
+// easily lose track of whether the data has been cached yet or not. So
+// rather than have extra "isValid" bools everywhere, we use this class to
+// encapsulate the validity bit in with the data, and ASSERT that it is true
+// whenever reading out the data.
+// Note that the client must still remember to call GetValue only when HasValue
+// is true. Since C++ doesn't have type-safe sum types, we can't enforce this
+// explicitly at compile time (ML-style datatypes and pattern matching is perfect
+// for this).
+//
+// Note that we could consider adding some operator overloads to make using
+// instances of this class more transparent. Experience will tell if this
+// is a good idea or not.
+//
+template <typename T>
+class Optional
+{
+public:
+ // By default, initialize to invalid
+ Optional() : m_fHasValue(false), m_value(T()) {}
+
+ // Allow implicit initialization from a value (for copyable T)
+ Optional(const T& val) : m_fHasValue(true), m_value(val) {}
+
+ // Returns true if a value has been stored
+ bool HasValue() const { return m_fHasValue; }
+
+ // Extract the value. Can only be called when HasValue is true.
+ const T& GetValue() { _ASSERTE(m_fHasValue); return m_value; }
+
+ // Get a writable pointer to the value structure, for filling in uncopyable data structures
+ T * GetValueAddr() { return &m_value; }
+
+ // Explicitly mark this object as having a value (for use after writing to it directly using
+ // GetValueAddr. Not necessary for simple/primitive types).
+ void SetHasValue() { m_fHasValue = true; }
+
+ // Also gets compiler-default copy constructor and assignment operator if T has them
+
+private:
+ bool m_fHasValue;
+ T m_value;
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Cordb class
+ * ------------------------------------------------------------------------- */
+
+class Cordb : public CordbBase, public ICorDebug, public ICorDebugRemote
+{
+public:
+ Cordb(CorDebugInterfaceVersion iDebuggerVersion);
+ virtual ~Cordb();
+ virtual void Neuter();
+
+
+
+#ifdef _DEBUG_IMPL
+ virtual const char * DbgGetName() { return "Cordb"; }
+
+ // Under Debug, we keep some extra state for tracking leaks. The goal is that
+ // we can assert that we aren't leaking internal refs. We'd like to assert that
+ // we're not leaking external refs, but since we can't force Cordbg to release,
+ // we can't really assert that.
+ // So the idea is that when Cordbg has released its last Cordb object, that
+ // all internal references have been released.
+ // Unfortunately, certain CordbBase objects are unrooted and thus we have no
+ // good time to neuter them and clean up any internal references they may hold.
+ // So we keep count of those guys too.
+ static LONG s_DbgMemTotalOutstandingCordb;
+ static LONG s_DbgMemTotalOutstandingInternalRefs;
+#endif
+
+ //
+ // Turn this on to enable an array which will contain all objects that have
+ // not been completely released.
+ //
+ // #define TRACK_OUTSTANDING_OBJECTS 1
+
+#ifdef TRACK_OUTSTANDING_OBJECTS
+
+#define MAX_TRACKED_OUTSTANDING_OBJECTS 256
+ static void *Cordb::s_DbgMemOutstandingObjects[MAX_TRACKED_OUTSTANDING_OBJECTS];
+ static LONG Cordb::s_DbgMemOutstandingObjectMax;
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebug
+ //-----------------------------------------------------------
+
+#ifdef FEATURE_CORECLR
+ HRESULT SetTargetCLR(HMODULE hmodTargetCLR);
+#endif // FEATURE_CORECLR
+
+ COM_METHOD Initialize();
+ COM_METHOD Terminate();
+ COM_METHOD SetManagedHandler(ICorDebugManagedCallback *pCallback);
+ COM_METHOD SetUnmanagedHandler(ICorDebugUnmanagedCallback *pCallback);
+ COM_METHOD CreateProcess(LPCWSTR lpApplicationName,
+ __in_z LPWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ PVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation,
+ CorDebugCreateProcessFlags debuggingFlags,
+ ICorDebugProcess **ppProcess);
+ COM_METHOD DebugActiveProcess(DWORD dwProcessId, BOOL fWin32Attach, ICorDebugProcess **ppProcess);
+ COM_METHOD EnumerateProcesses(ICorDebugProcessEnum **ppProcess);
+ COM_METHOD GetProcess(DWORD dwProcessId, ICorDebugProcess **ppProcess);
+ COM_METHOD CanLaunchOrAttach(DWORD dwProcessId, BOOL win32DebuggingEnabled);
+
+ //-----------------------------------------------------------
+ // CorDebug
+ //-----------------------------------------------------------
+
+ static COM_METHOD CreateObjectV1(REFIID id, void **object);
+#if defined(FEATURE_DBGIPC_TRANSPORT_DI)
+ static COM_METHOD CreateObjectTelesto(REFIID id, void ** pObject);
+#endif // FEATURE_DBGIPC_TRANSPORT_DI
+ static COM_METHOD CreateObject(CorDebugInterfaceVersion iDebuggerVersion, REFIID id, void **object);
+
+ //-----------------------------------------------------------
+ // ICorDebugRemote
+ //-----------------------------------------------------------
+
+ COM_METHOD CreateProcessEx(ICorDebugRemoteTarget * pRemoteTarget,
+ LPCWSTR lpApplicationName,
+ __in_z LPWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ PVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation,
+ CorDebugCreateProcessFlags debuggingFlags,
+ ICorDebugProcess ** ppProcess);
+
+ COM_METHOD DebugActiveProcessEx(ICorDebugRemoteTarget * pRemoteTarget,
+ DWORD dwProcessId,
+ BOOL fWin32Attach,
+ ICorDebugProcess ** ppProcess);
+
+
+ //-----------------------------------------------------------
+ // Methods not exposed via a COM interface.
+ //-----------------------------------------------------------
+
+ HRESULT CreateProcessCommon(ICorDebugRemoteTarget * pRemoteTarget,
+ LPCWSTR lpApplicationName,
+ __in_z LPWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ PVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation,
+ CorDebugCreateProcessFlags debuggingFlags,
+ ICorDebugProcess **ppProcess);
+
+ HRESULT DebugActiveProcessCommon(ICorDebugRemoteTarget * pRemoteTarget, DWORD id, BOOL win32Attach, ICorDebugProcess **ppProcess);
+
+ void EnsureCanLaunchOrAttach(BOOL fWin32DebuggingEnabled);
+
+ void EnsureAllowAnotherProcess();
+ void AddProcess(CordbProcess* process);
+ void RemoveProcess(CordbProcess* process);
+ CordbSafeHashTable<CordbProcess> *GetProcessList();
+
+ void LockProcessList();
+ void UnlockProcessList();
+
+ #ifdef _DEBUG
+ bool ThreadHasProcessListLock();
+ #endif
+
+
+ HRESULT SendIPCEvent(CordbProcess * pProcess,
+ DebuggerIPCEvent * pEvent,
+ SIZE_T eventSize);
+
+ void ProcessStateChanged();
+
+ HRESULT WaitForIPCEventFromProcess(CordbProcess* process,
+ CordbAppDomain *appDomain,
+ DebuggerIPCEvent* event);
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+public:
+ RSExtSmartPtr<ICorDebugManagedCallback> m_managedCallback;
+ RSExtSmartPtr<ICorDebugManagedCallback2> m_managedCallback2;
+ RSExtSmartPtr<ICorDebugManagedCallback3> m_managedCallback3;
+ RSExtSmartPtr<ICorDebugUnmanagedCallback> m_unmanagedCallback;
+
+ CordbRCEventThread* m_rcEventThread;
+
+ CorDebugInterfaceVersion GetDebuggerVersion() const;
+
+#ifdef FEATURE_CORESYSTEM
+ HMODULE GetTargetCLR() { return m_targetCLR; }
+#endif
+
+private:
+ bool IsCreateProcessSupported();
+ bool IsInteropDebuggingSupported();
+ void CheckCompatibility();
+
+ CordbSafeHashTable<CordbProcess> m_processes;
+
+ // List to track outstanding CordbProcessEnum objects.
+ NeuterList m_pProcessEnumList;
+
+ RSLock m_processListMutex;
+ BOOL m_initialized;
+
+ // This is the version of the ICorDebug APIs that the debugger believes it's consuming.
+ CorDebugInterfaceVersion m_debuggerSpecifiedVersion;
+
+//Note - this code could be useful outside coresystem, but keeping the change localized
+// because we are late in the win8 release
+#ifdef FEATURE_CORESYSTEM
+ HMODULE m_targetCLR;
+#endif
+};
+
+
+
+
+/* ------------------------------------------------------------------------- *
+ * AppDomain class
+ * ------------------------------------------------------------------------- */
+
+// Provides the implementation for ICorDebugAppDomain, ICorDebugAppDomain2,
+// and ICorDebugAppDomain3
+class CordbAppDomain : public CordbBase,
+ public ICorDebugAppDomain,
+ public ICorDebugAppDomain2,
+ public ICorDebugAppDomain3,
+ public ICorDebugAppDomain4
+{
+public:
+ // Create a CordbAppDomain object based on a pointer to the AppDomain instance in the CLR
+ CordbAppDomain(CordbProcess * pProcess,
+ VMPTR_AppDomain vmAppDomain);
+
+ virtual ~CordbAppDomain();
+
+ virtual void Neuter();
+
+ using CordbBase::GetProcess;
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbAppDomain"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugController
+ //-----------------------------------------------------------
+
+ COM_METHOD Stop(DWORD dwTimeout);
+ COM_METHOD Continue(BOOL fIsOutOfBand);
+ COM_METHOD IsRunning(BOOL * pbRunning);
+ COM_METHOD HasQueuedCallbacks(ICorDebugThread * pThread,
+ BOOL * pbQueued);
+ COM_METHOD EnumerateThreads(ICorDebugThreadEnum ** ppThreads);
+ COM_METHOD SetAllThreadsDebugState(CorDebugThreadState state, ICorDebugThread * pExceptThisThread);
+
+ // Deprecated, returns E_NOTIMPL
+ COM_METHOD Detach();
+
+ COM_METHOD Terminate(unsigned int exitCode);
+
+ COM_METHOD CanCommitChanges(
+ ULONG cSnapshots,
+ ICorDebugEditAndContinueSnapshot * pSnapshots[],
+ ICorDebugErrorInfoEnum ** pError);
+
+ COM_METHOD CommitChanges(
+ ULONG cSnapshots,
+ ICorDebugEditAndContinueSnapshot * pSnapshots[],
+ ICorDebugErrorInfoEnum ** pError);
+
+ //-----------------------------------------------------------
+ // ICorDebugAppDomain
+ //-----------------------------------------------------------
+ /*
+ * GetProcess returns the process containing the app domain
+ */
+
+ COM_METHOD GetProcess(ICorDebugProcess ** ppProcess);
+
+ /*
+ * EnumerateAssemblies enumerates all assemblies in the app domain
+ */
+
+ COM_METHOD EnumerateAssemblies(ICorDebugAssemblyEnum ** ppAssemblies);
+
+ COM_METHOD GetModuleFromMetaDataInterface(IUnknown * pIMetaData,
+ ICorDebugModule ** ppModule);
+ /*
+ * EnumerateBreakpoints returns an enum of all active breakpoints
+ * in the app domain. This includes all types of breakpoints :
+ * function breakpoints, data breakpoints, etc.
+ */
+
+ COM_METHOD EnumerateBreakpoints(ICorDebugBreakpointEnum ** ppBreakpoints);
+
+ /*
+ * EnumerateSteppers returns an enum of all active steppers in the app domain.
+ */
+
+ COM_METHOD EnumerateSteppers(ICorDebugStepperEnum ** ppSteppers);
+
+ // Deprecated, always returns true.
+ COM_METHOD IsAttached(BOOL * pfAttached);
+
+ // Returns the friendly name of the AppDomain
+ COM_METHOD GetName(ULONG32 cchName,
+ ULONG32 * pcchName,
+ __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]);
+
+ /*
+ * GetObject returns the runtime app domain object.
+ * Note: This method is not yet implemented.
+ */
+
+ COM_METHOD GetObject(ICorDebugValue ** ppObject);
+
+ // Deprecated, does nothing
+ COM_METHOD Attach();
+ COM_METHOD GetID(ULONG32 * pId);
+
+ //-----------------------------------------------------------
+ // ICorDebugAppDomain2 APIs
+ //-----------------------------------------------------------
+ COM_METHOD GetArrayOrPointerType(CorElementType elementType,
+ ULONG32 nRank,
+ ICorDebugType * pTypeArg,
+ ICorDebugType ** ppResultType);
+
+ COM_METHOD GetFunctionPointerType(ULONG32 cTypeArgs,
+ ICorDebugType * rgpTypeArgs[],
+ ICorDebugType ** ppResultType);
+
+ //-----------------------------------------------------------
+ // ICorDebugAppDomain3 APIs
+ //-----------------------------------------------------------
+ COM_METHOD GetCachedWinRTTypesForIIDs(
+ ULONG32 cGuids,
+ GUID * guids,
+ ICorDebugTypeEnum * * ppTypesEnum);
+
+ COM_METHOD GetCachedWinRTTypes(
+ ICorDebugGuidToTypeEnum * * ppType);
+
+ //-----------------------------------------------------------
+ // ICorDebugAppDomain4
+ //-----------------------------------------------------------
+ COM_METHOD GetObjectForCCW(CORDB_ADDRESS ccwPointer, ICorDebugValue **ppManagedObject);
+
+ // Get the VMPTR for this appdomain.
+ VMPTR_AppDomain GetADToken() { return m_vmAppDomain; }
+
+ // Given a metadata interface, find the module in this appdomain that matches it.
+ CordbModule * GetModuleFromMetaDataInterface(IUnknown *pIMetaData);
+
+ // Lookup a module from the cache. Create and to the cache if needed.
+ CordbModule * LookupOrCreateModule(VMPTR_Module vmModuleToken, VMPTR_DomainFile vmDomainFileToken);
+
+ // Lookup a module from the cache. Create and to the cache if needed.
+ CordbModule * LookupOrCreateModule(VMPTR_DomainFile vmDomainFileToken);
+
+ // Callback from DAC for module enumeration
+ static void ModuleEnumerationCallback(VMPTR_DomainFile vmModule, void * pUserData);
+
+ // Use DAC to add any modules for this assembly.
+ void PrepopulateModules();
+
+ void InvalidateName() { m_strAppDomainName.Clear(); }
+
+public:
+ ULONG m_AppDomainId;
+
+ CordbAssembly * LookupOrCreateAssembly(VMPTR_DomainAssembly vmDomainAssembly);
+ CordbAssembly * LookupOrCreateAssembly(VMPTR_Assembly vmAssembly);
+ void RemoveAssemblyFromCache(VMPTR_DomainAssembly vmDomainAssembly);
+
+
+ CordbSafeHashTable<CordbBreakpoint> m_breakpoints;
+
+ // Unique objects that represent the use of some
+ // basic ELEMENT_TYPE's as type parameters. These
+ // are shared acrosss the entire process. We could
+ // go and try to find the classes corresponding to these
+ // element types but it seems simpler just to keep
+ // them as special cases.
+ CordbSafeHashTable<CordbType> m_sharedtypes;
+
+ CordbAssembly * CacheAssembly(VMPTR_DomainAssembly vmDomainAssembly);
+ CordbAssembly * CacheAssembly(VMPTR_Assembly vmAssembly);
+
+
+ // Cache of modules in this appdomain. In the VM, modules live in an assembly.
+ // This cache lives on the appdomain because we generally want to do appdomain (or process)
+ // wide lookup.
+ // This is indexed by VMPTR_DomainFile, which has appdomain affinity.
+ // This is populated by code:CordbAppDomain::LookupOrCreateModule (which may be invoked
+ // anytime the RS gets hold of a VMPTR), and are removed at the unload event.
+ CordbSafeHashTable<CordbModule> m_modules;
+private:
+ // Cache of assemblies in this appdomain.
+ // This is indexed by VMPTR_DomainAssembly, which has appdomain affinity.
+ // This is populated by code:CordbAppDomain::LookupOrCreateAssembly (which may be invoked
+ // anytime the RS gets hold of a VMPTR), and are removed at the unload event.
+ CordbSafeHashTable<CordbAssembly> m_assemblies;
+
+ static void AssemblyEnumerationCallback(VMPTR_DomainAssembly vmDomainAssembly, void * pThis);
+ void PrepopulateAssembliesOrThrow();
+
+ // Use DAC to refresh our name
+ HRESULT RefreshName();
+
+ StringCopyHolder m_strAppDomainName;
+
+ NeuterList m_TypeNeuterList; // List of types owned by this AppDomain.
+
+ // List of Sweepable objects owned by this AppDomain.
+ // This includes some objects taht hold resources in the left-side (mainly
+ // as CordbHandleValue, see code:CordbHandleValue::Dispose), as well as:
+ // - Cordb*Value objects that survive across continues and have appdomain affinity.
+ LeftSideResourceCleanupList m_SweepableNeuterList;
+
+ VMPTR_AppDomain m_vmAppDomain;
+public:
+ // The "Long" exit list is for items that don't get neutered until the appdomain exits.
+ // The "Sweepable" exit list is for items that may be neuterable sooner than AD exit.
+ // By splitting out the list, we can just try to sweep the "Sweepable" list and we
+ // don't waste any time sweeping things on the "Long" list that aren't neuterable anyways.
+ NeuterList * GetLongExitNeuterList() { return &m_TypeNeuterList; }
+ LeftSideResourceCleanupList * GetSweepableExitNeuterList() { return &m_SweepableNeuterList; }
+
+ void AddToTypeList(CordbBase *pObject);
+
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Assembly class
+ * ------------------------------------------------------------------------- */
+
+class CordbAssembly : public CordbBase, public ICorDebugAssembly, ICorDebugAssembly2
+{
+public:
+ CordbAssembly(CordbAppDomain * pAppDomain,
+ VMPTR_Assembly vmAssembly,
+ VMPTR_DomainAssembly vmDomainAssembly);
+ virtual ~CordbAssembly();
+ virtual void Neuter();
+
+ using CordbBase::GetProcess;
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbAssembly"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugAssembly
+ //-----------------------------------------------------------
+
+ /*
+ * GetProcess returns the process containing the assembly
+ */
+ COM_METHOD GetProcess(ICorDebugProcess ** ppProcess);
+
+ // Gets the AppDomain containing this assembly
+ COM_METHOD GetAppDomain(ICorDebugAppDomain ** ppAppDomain);
+
+ /*
+ * EnumerateModules enumerates all modules in the assembly
+ */
+ COM_METHOD EnumerateModules(ICorDebugModuleEnum ** ppModules);
+
+ /*
+ * GetCodeBase returns the code base used to load the assembly
+ */
+ COM_METHOD GetCodeBase(ULONG32 cchName,
+ ULONG32 * pcchName,
+ __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]);
+
+ // returns the filename of the assembly, or "<unknown>" for in-memory assemblies
+ COM_METHOD GetName(ULONG32 cchName,
+ ULONG32 * pcchName,
+ __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]);
+
+
+ //-----------------------------------------------------------
+ // ICorDebugAssembly2
+ //-----------------------------------------------------------
+
+ /*
+ * IsFullyTrusted returns a flag indicating whether the security system
+ * has granted the assembly full trust.
+ */
+ COM_METHOD IsFullyTrusted(BOOL * pbFullyTrusted);
+
+ //-----------------------------------------------------------
+ // internal accessors
+ //-----------------------------------------------------------
+
+#ifdef _DEBUG
+ void DbgAssertAssemblyDeleted();
+
+ static void DbgAssertAssemblyDeletedCallback(VMPTR_DomainAssembly vmDomainAssembly, void * pUserData);
+#endif // _DEBUG
+
+ CordbAppDomain * GetAppDomain() { return m_pAppDomain; }
+
+ VMPTR_DomainAssembly GetDomainAssemblyPtr() { return m_vmDomainAssembly; }
+private:
+ VMPTR_Assembly m_vmAssembly;
+ VMPTR_DomainAssembly m_vmDomainAssembly;
+ CordbAppDomain * m_pAppDomain;
+
+ StringCopyHolder m_strAssemblyFileName;
+ Optional<BOOL> m_foptIsFullTrust;
+};
+
+
+//-----------------------------------------------------------------------------
+// Describe what to do w/ a win32 debug event
+//-----------------------------------------------------------------------------
+class Reaction
+{
+public:
+ enum Type
+ {
+ // Inband events: Dispatch to Cordbg
+ // safe for stopping the shell and communicating with the runtime
+ cInband,
+
+ // workaround. Inband event, but NewEvent =false
+ cInband_NotNewEvent,
+
+ // This is a debug event that corresponds with getting to the beginning
+ // of a first chance hijack.
+ cFirstChanceHijackStarted,
+
+ // This is the debug event that corresponds with getting to the end of
+ // a hijack. To continue we need to restore an unhijacked context
+ cInbandHijackComplete,
+
+ // This is a debug event which corresponds to re-hiting a previous
+ // IB event after returning from the hijack. Now we have already dispatched it
+ // so we know how the user wants it to be continued
+ // Continue immediately with the previously determined
+ cInbandExceptionRetrigger,
+
+ // This debug event is a breakpoint in unmanaged code that we placed. It will need
+ // the M2UHandoffHijack to run the in process breakpoint handling code.
+ cBreakpointRequiringHijack,
+
+ // Oob events: Dispatch to Cordbg
+ // Not safe stopping events. They must be continued immediately.
+ cOOB,
+
+ // CLR internal exception, Continue(not_handled), don't dispatch
+ // The CLR expects this exception and will deal with it properly.
+ cCLR,
+
+ // Don't dispatch. Continue(DBG_CONTINUE).
+ // Common for flare.
+ cIgnore
+ };
+
+ Type GetType() const { return m_type; };
+
+#ifdef _DEBUG
+ const char * GetReactionName()
+ {
+ switch(m_type)
+ {
+ case cInband: return "cInband";
+ case cInband_NotNewEvent: return "cInband_NotNewEvent";
+ case cInbandHijackComplete: return "cInbandHijackComplete";
+ case cInbandExceptionRetrigger: return "cInbandExceptionRetrigger";
+ case cBreakpointRequiringHijack: return "cBreakpointRequiringHijack";
+ case cOOB: return "cOOB";
+ case cCLR: return "cCLR";
+ case cIgnore: return "cIgnore";
+ default: return "<unknown>";
+ }
+ }
+ int GetLine()
+ {
+ return m_line;
+ }
+#endif
+
+ Reaction(Type t, int line) : m_type(t) {
+#ifdef _DEBUG
+ m_line = line;
+
+ LOG((LF_CORDB, LL_EVERYTHING, "Reaction:%s (determined on line: %d)\n", GetReactionName(), line));
+#endif
+ };
+
+ void operator=(const Reaction & other)
+ {
+ m_type = other.m_type;
+#ifdef _DEBUG
+ m_line = other.m_line;
+#endif
+ }
+
+protected:
+ Type m_type;
+
+#ifdef _DEBUG
+ // Under a debug build, track the line # for where this came from.
+ int m_line;
+#endif
+};
+
+// Macro for creating a Reaction.
+#define REACTION(type) Reaction(Reaction::type, __LINE__)
+
+// Different forms of Unmanaged Continue
+enum EUMContinueType
+{
+ cOobUMContinue,
+ cInternalUMContinue,
+ cRealUMContinue
+};
+
+/* ------------------------------------------------------------------------- *
+ * Process class
+ * ------------------------------------------------------------------------- */
+
+
+#ifdef _DEBUG
+// On debug, we can afford a larger native event queue..
+const int DEBUG_EVENTQUEUE_SIZE = 30;
+#else
+const int DEBUG_EVENTQUEUE_SIZE = 10;
+#endif
+
+void DeleteIPCEventHelper(DebuggerIPCEvent *pDel);
+
+
+// Private interface on CordbProcess that ShimProcess needs to emulate V2 functionality.
+// The fact that we need private hooks means that V3 is not sufficiently finished to allow building
+// a V2 debugger. This interface should shrink over time (and eventually go away) as the functionality gets exposed
+// publicly.
+// CordbProcess calls back into ShimProcess too, so the public surface of code:ShimProcess plus
+// the spots in CordbProcess that call them are additional surface area that may need to addressed
+// to make the shim public.
+class IProcessShimHooks
+{
+public:
+ // Get the OS Process ID of the target.
+ virtual DWORD GetPid() = 0;
+
+ // Request a synchronization for attach.
+ // This essentially just sends an AsyncBreak to the left-side. Once the target is
+ // synchronized, the Shim can use inspection to send all the various fake-attach events.
+ //
+ // Once the shim has a way of requesting a synchronization from out-of-process for an
+ // arbitrary running target that's not stopped at a managed debug event, we can
+ // remove this.
+ virtual void QueueManagedAttachIfNeeded() = 0;
+
+ // Hijack a thread at an unhandled exception to allow us to resume executing the target so
+ // that the helper thread can run and service IPC requests. This is also needed to allow
+ // func-eval at a 2nd-chance exception
+ //
+ // This will require an architectural change to remove. Either:
+ // - actions like func-eval / synchronization may call this directly themselves.
+ // - the CLR's managed Unhandled-exception event is moved out of the native
+ // unhandled-exception event, thus making native unhandled exceptions uninteresting to ICorDebug.
+ // - everything is out-of-process, and so the CLR doesn't need to continue after an unhandled
+ // native exception.
+ virtual BOOL HijackThreadForUnhandledExceptionIfNeeded(DWORD dwThreadId) = 0;
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+ // Private hook to do the bulk of the interop-debugging goo. This includes hijacking inband
+ // events and queueing them so that the helper-thread can run.
+ //
+ // We can remove this once we kill the helper-thread, or after enough functionality is
+ // out-of-process that the debugger doesn't need the helper thread when stopped at an event.
+ virtual void HandleDebugEventForInteropDebugging(const DEBUG_EVENT * pEvent) = 0;
+#endif // FEATURE_INTEROP_DEBUGGING
+
+ // Get the modules in the order that they were loaded. This is needed to send the fake-attach events
+ // for module load in the right order.
+ //
+ // This can be removed once ICorDebug's enumerations are ordered.
+ virtual void GetModulesInLoadOrder(
+ ICorDebugAssembly * pAssembly,
+ RSExtSmartPtr<ICorDebugModule>* pModules,
+ ULONG countModules) = 0;
+
+ // Get the assemblies in the order that they were loaded. This is needed to send the fake-attach events
+ // for assembly load in the right order.
+ //
+ // This can be removed once ICorDebug's enumerations are ordered.
+ virtual void GetAssembliesInLoadOrder(
+ ICorDebugAppDomain * pAppDomain,
+ RSExtSmartPtr<ICorDebugAssembly>* pAssemblies,
+ ULONG countAssemblies) = 0;
+
+ // Queue up fake connection events for attach.
+ // ICorDebug doesn't expose any enumeration for connections, so the shim needs to call into a
+ // private hook to enumerate them for attach.
+ virtual void QueueFakeConnectionEvents() = 0;
+
+ // This finishes initializing the IPC channel between the LS + RS, which includes duplicating
+ // some handles and events.
+ //
+ // This can be removed once the IPC channel is completely gone and all communication goes
+ // soley through the data-target.
+ virtual void FinishInitializeIPCChannel() = 0;
+
+ // Called when stopped at a managed debug event to request a synchronization.
+ // This can be replaced when we expose synchronization from ICorDebug.
+ // The fact that the debuggee is at a managed debug event greatly simplifies the request here
+ // (in contrast to QueueManagedAttachIfNeeded). It means that we can just flip a flag from
+ // out-of-process, and when the debuggee thread resumes, it can check that flag and do the
+ // synchronization from in-process.
+ virtual void RequestSyncAtEvent()= 0;
+
+ virtual bool IsThreadSuspendedOrHijacked(ICorDebugThread * pThread) = 0;
+};
+
+
+// entry for the array of connections in EnumerateConnectionsData
+struct EnumerateConnectionsEntry
+{
+public:
+ StringCopyHolder m_pName; // name of the connection
+ DWORD m_dwID; // ID of the connection
+};
+
+// data structure used in the callback for enumerating connections (code:CordbProcess::QueueFakeConnectionEvents)
+struct EnumerateConnectionsData
+{
+public:
+ ~EnumerateConnectionsData()
+ {
+ if (m_pEntryArray != NULL)
+ {
+ delete [] m_pEntryArray;
+ m_pEntryArray = NULL;
+ }
+ }
+
+ CordbProcess * m_pThis; // the "this" process
+ EnumerateConnectionsEntry * m_pEntryArray; // an array of connections to be filled in
+ UINT32 m_uIndex; // the next entry in the array to be filled
+};
+
+// data structure used in the callback for asserting that an appdomain has been deleted
+// (code:CordbProcess::DbgAssertAppDomainDeleted)
+struct DbgAssertAppDomainDeletedData
+{
+public:
+ CordbProcess * m_pThis;
+ VMPTR_AppDomain m_vmAppDomainDeleted;
+};
+
+class CordbProcess :
+ public CordbBase,
+ public ICorDebugProcess,
+ public ICorDebugProcess2,
+ public ICorDebugProcess3,
+ public ICorDebugProcess4,
+ public ICorDebugProcess5,
+ public ICorDebugProcess7,
+ public ICorDebugProcess8,
+ public IDacDbiInterface::IAllocator,
+ public IDacDbiInterface::IMetaDataLookup,
+ public IProcessShimHooks
+#ifdef FEATURE_LEGACYNETCF_DBG_HOST_CONTROL
+ , public ICorDebugLegacyNetCFHostCallbackInvoker_PrivateWindowsPhoneOnly
+#endif
+{
+ // Ctor is private. Use OpenVirtualProcess instead.
+ CordbProcess(ULONG64 clrInstanceId, IUnknown * pDataTarget, HMODULE hDacModule, Cordb * pCordb, DWORD dwProcessID, ShimProcess * pShim);
+
+public:
+
+ virtual ~CordbProcess();
+ virtual void Neuter();
+
+ // Neuter left-side resources for all children
+ void NeuterChildrenLeftSideResources();
+
+ // Neuter all of all children, but not the actual process object.
+ void NeuterChildren();
+
+
+ // The way to instantiate a new CordbProcess object.
+ // @dbgtodo managed pipeline - this is not fully active in all scenarios yet.
+ static HRESULT OpenVirtualProcess(ULONG64 clrInstanceId,
+ IUnknown * pDataTarget,
+ HMODULE hDacModule,
+ Cordb * pCordb,
+ DWORD dwProcessID,
+ ShimProcess * pShim,
+ CordbProcess ** ppProcess);
+
+ // Helper function to determine whether this ICorDebug is compatibile with a debugger
+ // designed for the specified major version
+ static bool IsCompatibleWith(DWORD clrMajorVersion);
+
+ //-----------------------------------------------------------
+ // IMetaDataLookup
+ // -----------------------------------------------------------
+ IMDInternalImport * LookupMetaData(VMPTR_PEFile vmPEFile, bool &isILMetaDataForNGENImage);
+
+ // Helper functions for LookupMetaData implementation
+ IMDInternalImport * LookupMetaDataFromDebugger(VMPTR_PEFile vmPEFile,
+ bool &isILMetaDataForNGENImage,
+ CordbModule * pModule);
+
+ IMDInternalImport * LookupMetaDataFromDebuggerForSingleFile(CordbModule * pModule,
+ LPCWSTR pwszImagePath,
+ DWORD dwTimeStamp,
+ DWORD dwImageSize);
+
+
+ //-----------------------------------------------------------
+ // IDacDbiInterface::IAllocator
+ //-----------------------------------------------------------
+
+ void * Alloc(SIZE_T lenBytes);
+ void Free(void * p);
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbProcess"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return BaseAddRefEnforceExternal();
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return BaseReleaseEnforceExternal();
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugController
+ //-----------------------------------------------------------
+
+ COM_METHOD Stop(DWORD dwTimeout);
+ COM_METHOD Deprecated_Continue();
+ COM_METHOD IsRunning(BOOL *pbRunning);
+ COM_METHOD HasQueuedCallbacks(ICorDebugThread *pThread, BOOL *pbQueued);
+ COM_METHOD EnumerateThreads(ICorDebugThreadEnum **ppThreads);
+ COM_METHOD SetAllThreadsDebugState(CorDebugThreadState state,
+ ICorDebugThread *pExceptThisThread);
+ COM_METHOD Detach();
+ COM_METHOD Terminate(unsigned int exitCode);
+
+ COM_METHOD CanCommitChanges(
+ ULONG cSnapshots,
+ ICorDebugEditAndContinueSnapshot *pSnapshots[],
+ ICorDebugErrorInfoEnum **pError);
+
+ COM_METHOD CommitChanges(
+ ULONG cSnapshots,
+ ICorDebugEditAndContinueSnapshot *pSnapshots[],
+ ICorDebugErrorInfoEnum **pError);
+
+ COM_METHOD Continue(BOOL fIsOutOfBand);
+ COM_METHOD ThreadForFiberCookie(DWORD fiberCookie,
+ ICorDebugThread **ppThread);
+ COM_METHOD GetHelperThreadID(DWORD *pThreadID);
+
+ //-----------------------------------------------------------
+ // ICorDebugProcess
+ //-----------------------------------------------------------
+
+ COM_METHOD GetID(DWORD *pdwProcessId);
+ COM_METHOD GetHandle(HANDLE *phProcessHandle);
+ COM_METHOD EnableSynchronization(BOOL bEnableSynchronization);
+ COM_METHOD GetThread(DWORD dwThreadId, ICorDebugThread **ppThread);
+ COM_METHOD EnumerateBreakpoints(ICorDebugBreakpointEnum **ppBreakpoints);
+ COM_METHOD EnumerateSteppers(ICorDebugStepperEnum **ppSteppers);
+ COM_METHOD EnumerateObjects(ICorDebugObjectEnum **ppObjects);
+ COM_METHOD IsTransitionStub(CORDB_ADDRESS address, BOOL *pbTransitionStub);
+ COM_METHOD EnumerateModules(ICorDebugModuleEnum **ppModules);
+ COM_METHOD GetModuleFromMetaDataInterface(IUnknown *pIMetaData,
+ ICorDebugModule **ppModule);
+ COM_METHOD SetStopState(DWORD threadID, CorDebugThreadState state);
+ COM_METHOD IsOSSuspended(DWORD threadID, BOOL *pbSuspended);
+ COM_METHOD GetThreadContext(DWORD threadID, ULONG32 contextSize,
+ BYTE context[]);
+ COM_METHOD SetThreadContext(DWORD threadID, ULONG32 contextSize,
+ BYTE context[]);
+ COM_METHOD ReadMemory(CORDB_ADDRESS address, DWORD size, BYTE buffer[],
+ SIZE_T *read);
+ COM_METHOD WriteMemory(CORDB_ADDRESS address, DWORD size, BYTE buffer[],
+ SIZE_T *written);
+
+ COM_METHOD ClearCurrentException(DWORD threadID);
+
+ /*
+ * EnableLogMessages enables/disables sending of log messages to the
+ * debugger for logging.
+ */
+ COM_METHOD EnableLogMessages(BOOL fOnOff);
+
+ /*
+ * ModifyLogSwitch modifies the specified switch's severity level.
+ */
+ COM_METHOD ModifyLogSwitch(__in_z WCHAR *pLogSwitchName, LONG lLevel);
+
+ COM_METHOD EnumerateAppDomains(ICorDebugAppDomainEnum **ppAppDomains);
+ COM_METHOD GetObject(ICorDebugValue **ppObject);
+
+ //-----------------------------------------------------------
+ // ICorDebugProcess2
+ //-----------------------------------------------------------
+
+ COM_METHOD GetThreadForTaskID(TASKID taskId, ICorDebugThread2 ** ppThread);
+ COM_METHOD GetVersion(COR_VERSION* pInfo);
+
+ COM_METHOD SetUnmanagedBreakpoint(CORDB_ADDRESS address, ULONG32 bufsize, BYTE buffer[], ULONG32 * bufLen);
+ COM_METHOD ClearUnmanagedBreakpoint(CORDB_ADDRESS address);
+ COM_METHOD GetCodeAtAddress(CORDB_ADDRESS address, ICorDebugCode ** pCode, ULONG32 * offset);
+
+ COM_METHOD SetDesiredNGENCompilerFlags(DWORD pdwFlags);
+ COM_METHOD GetDesiredNGENCompilerFlags(DWORD *pdwFlags );
+
+ COM_METHOD GetReferenceValueFromGCHandle(UINT_PTR handle, ICorDebugReferenceValue **pOutValue);
+
+ //-----------------------------------------------------------
+ // ICorDebugProcess3
+ //-----------------------------------------------------------
+
+ // enables or disables CustomNotifications of a given type
+ COM_METHOD SetEnableCustomNotification(ICorDebugClass * pClass, BOOL fEnable);
+
+ //-----------------------------------------------------------
+ // ICorDebugProcess4
+ //-----------------------------------------------------------
+ COM_METHOD Filter(
+ const BYTE pRecord[],
+ DWORD countBytes,
+ CorDebugRecordFormat format,
+ DWORD dwFlags,
+ DWORD dwThreadId,
+ ICorDebugManagedCallback *pCallback,
+ DWORD * pContinueStatus);
+
+ COM_METHOD ProcessStateChanged(CorDebugStateChange eChange);
+
+ //-----------------------------------------------------------
+ // ICorDebugProcess5
+ //-----------------------------------------------------------
+ COM_METHOD GetGCHeapInformation(COR_HEAPINFO *pHeapInfo);
+ COM_METHOD EnumerateHeap(ICorDebugHeapEnum **ppObjects);
+ COM_METHOD EnumerateHeapRegions(ICorDebugHeapSegmentEnum **ppRegions);
+ COM_METHOD GetObject(CORDB_ADDRESS addr, ICorDebugObjectValue **pObject);
+ COM_METHOD EnableNGENPolicy(CorDebugNGENPolicy ePolicy);
+ COM_METHOD EnumerateGCReferences(BOOL enumerateWeakReferences, ICorDebugGCReferenceEnum **ppEnum);
+ COM_METHOD EnumerateHandles(CorGCReferenceType types, ICorDebugGCReferenceEnum **ppEnum);
+ COM_METHOD GetTypeID(CORDB_ADDRESS obj, COR_TYPEID *pId);
+ COM_METHOD GetTypeForTypeID(COR_TYPEID id, ICorDebugType **ppType);
+ COM_METHOD GetArrayLayout(COR_TYPEID id, COR_ARRAY_LAYOUT *pLayout);
+ COM_METHOD GetTypeLayout(COR_TYPEID id, COR_TYPE_LAYOUT *pLayout);
+ COM_METHOD GetTypeFields(COR_TYPEID id, ULONG32 celt, COR_FIELD fields[], ULONG32 *pceltNeeded);
+
+ //-----------------------------------------------------------
+ // ICorDebugProcess7
+ //-----------------------------------------------------------
+ COM_METHOD SetWriteableMetadataUpdateMode(WriteableMetadataUpdateMode flags);
+
+ //-----------------------------------------------------------
+ // ICorDebugProcess8
+ //-----------------------------------------------------------
+ COM_METHOD EnableExceptionCallbacksOutsideOfMyCode(BOOL enableExceptionsOutsideOfJMC);
+
+#ifdef FEATURE_LEGACYNETCF_DBG_HOST_CONTROL
+ // ---------------------------------------------------------------
+ // ICorDebugLegacyNetCFHostCallbackInvoker_PrivateWindowsPhoneOnly
+ // ---------------------------------------------------------------
+
+ COM_METHOD InvokePauseCallback();
+ COM_METHOD InvokeResumeCallback();
+
+#endif
+
+ //-----------------------------------------------------------
+ // Methods not exposed via a COM interface.
+ //-----------------------------------------------------------
+
+ HRESULT ContinueInternal(BOOL fIsOutOfBand);
+ HRESULT StopInternal(DWORD dwTimeout, VMPTR_AppDomain pAppDomainToken);
+
+ // Sets an unmanaged breakpoint at the target address
+ HRESULT SetUnmanagedBreakpointInternal(CORDB_ADDRESS address, ULONG32 bufsize, BYTE buffer[], ULONG32 * bufLen);
+
+ // Allocate a buffer within the target and return the range. Throws on error.
+ TargetBuffer GetRemoteBuffer(ULONG cbBuffer); // throws
+
+ // Same as above except also copy-in the contents of a RS buffer using WriteProcessMemory
+ HRESULT GetAndWriteRemoteBuffer(CordbAppDomain *pDomain, unsigned int bufferSize, const void *bufferFrom, void **ppBuffer);
+
+ /*
+ * This will release a previously allocated left side buffer.
+ * Often they are deallocated by the LS itself.
+ */
+ HRESULT ReleaseRemoteBuffer(void **ppBuffer);
+
+
+ void TargetConsistencyCheck(bool fExpression);
+
+ // Activate interop-debugging, after the process has initially been Init()
+ void EnableInteropDebugging();
+
+ HRESULT Init();
+ void DeleteQueuedEvents();
+ void CleanupHalfBakedLeftSide();
+ void Terminating(BOOL fDetach);
+
+ CordbThread * TryLookupThread(VMPTR_Thread vmThread);
+ CordbThread * TryLookupOrCreateThreadByVolatileOSId(DWORD dwThreadId);
+ CordbThread * TryLookupThreadByVolatileOSId(DWORD dwThreadId);
+ CordbThread * LookupOrCreateThread(VMPTR_Thread vmThread);
+
+ void QueueManagedAttachIfNeeded();
+ void QueueManagedAttachIfNeededWorker();
+ HRESULT QueueManagedAttach();
+
+ void DetachShim();
+
+ // Flush for when the process is running.
+ void FlushProcessRunning();
+
+ // Flush all state.
+ void FlushAll();
+
+ BOOL HijackThreadForUnhandledExceptionIfNeeded(DWORD dwThreadId);
+
+ // Filter a CLR notification (subset of exceptions).
+ void FilterClrNotification(
+ DebuggerIPCEvent * pManagedEvent,
+ RSLockHolder * pLockHolder,
+ ICorDebugManagedCallback * pCallback);
+
+ // Wrapper to invoke IClrDataTarget4::ContinueStatusChanged
+ void ContinueStatusChanged(DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus);
+
+
+ // Request a synchronization to occur after a debug event is dispatched.
+ void RequestSyncAtEvent();
+
+ //
+ // Basic managed event plumbing
+ //
+
+ // This is called on the first IPC event from the debuggee. It initializes state.
+ void FinishInitializeIPCChannel();
+ void FinishInitializeIPCChannelWorker();
+
+ // This is called on each IPC event from the debuggee.
+ void HandleRCEvent(DebuggerIPCEvent * pManagedEvent, RSLockHolder * pLockHolder, ICorDebugManagedCallback * pCallback);
+
+ // Queue the RC event.
+ void QueueRCEvent(DebuggerIPCEvent * pManagedEvent);
+
+ // This marshals a managed debug event from the
+ void MarshalManagedEvent(DebuggerIPCEvent * pManagedEvent);
+
+ // This copies a managed debug event from the IPC block and to pManagedEvent.
+ // The event still needs to be marshalled.
+ void CopyRCEventFromIPCBlock(DebuggerIPCEvent * pManagedEvent);
+
+ // This copies a managed debug event out of the Native-Debug event envelope.
+ // The event still needs to be marshalled.
+ bool CopyManagedEventFromTarget(const EXCEPTION_RECORD * pRecord, DebuggerIPCEvent * pLocalManagedEvent);
+
+ // Helper for Filter() to verify parameters and return a type-safe exception record.
+ const EXCEPTION_RECORD * ValidateExceptionRecord(
+ const BYTE pRawRecord[],
+ DWORD countBytes,
+ CorDebugRecordFormat format);
+
+ // Helper to read a structure from the target.
+ template<typename T>
+ HRESULT SafeReadStruct(CORDB_ADDRESS pRemotePtr, T* pLocalBuffer);
+
+ // Helper to write a structure into the target.
+ template<typename T>
+ HRESULT SafeWriteStruct(CORDB_ADDRESS pRemotePtr, const T* pLocalBuffer);
+
+ // Reads a buffer from the target
+ HRESULT SafeReadBuffer(TargetBuffer tb, BYTE * pLocalBuffer, BOOL throwOnError = TRUE);
+
+ // Writes a buffer to the target
+ void SafeWriteBuffer(TargetBuffer tb, const BYTE * pLocalBuffer);
+
+#if defined(FEATURE_INTEROP_DEBUGGING)
+ void DuplicateHandleToLocalProcess(HANDLE * pLocalHandle, RemoteHANDLE * pRemoteHandle);
+#endif // FEATURE_INTEROP_DEBUGGING
+
+ bool IsThreadSuspendedOrHijacked(ICorDebugThread * pICorDebugThread);
+
+ // Helper to get PID internally.
+ DWORD GetPid();
+
+ HRESULT GetRuntimeOffsets();
+
+ // Are we blocked waiting fo ran OOB event to be continue?
+ bool IsWaitingForOOBEvent()
+ {
+#ifdef FEATURE_INTEROP_DEBUGGING
+ return m_outOfBandEventQueue != NULL;
+#else
+ // If no interop, then we're never waiting for an OOB event.
+ return false;
+#endif
+ }
+
+ //
+ // Shim callbacks to simulate fake attach events.
+ //
+
+
+ // Callback for Shim to get the assemblies in load order
+ void GetAssembliesInLoadOrder(
+ ICorDebugAppDomain * pAppDomain,
+ RSExtSmartPtr<ICorDebugAssembly>* pAssemblies,
+ ULONG countAssemblies);
+
+ // Callback for Shim to get the modules in load order
+ void GetModulesInLoadOrder(
+ ICorDebugAssembly * pAssembly,
+ RSExtSmartPtr<ICorDebugModule>* pModules,
+ ULONG countModules);
+
+ // Functions to queue fake Connection events on attach.
+ static void CountConnectionsCallback(DWORD id, LPCWSTR pName, void * pUserData);
+ static void EnumerateConnectionsCallback(DWORD id, LPCWSTR pName, void * pUserData);
+ void QueueFakeConnectionEvents();
+
+
+
+ void DispatchRCEvent();
+
+ // Dispatch a single event via the callbacks.
+ void RawDispatchEvent(
+ DebuggerIPCEvent * pEvent,
+ RSLockHolder * pLockHolder,
+ ICorDebugManagedCallback * pCallback1,
+ ICorDebugManagedCallback2 * pCallback2,
+ ICorDebugManagedCallback3 * pCallback3);
+
+ void MarkAllThreadsDirty();
+
+ bool CheckIfLSExited();
+
+ void Lock()
+ {
+ // Lock Hierarchy - shouldn't have List lock when taking/release the process lock.
+
+ m_processMutex.Lock();
+ LOG((LF_CORDB, LL_EVERYTHING, "P::Lock enter, this=0x%p\n", this));
+ }
+
+ void Unlock()
+ {
+ // Lock Hierarchy - shouldn't have List lock when taking/releasing the process lock.
+
+ LOG((LF_CORDB, LL_EVERYTHING, "P::Lock leave, this=0x%p\n", this));
+ m_processMutex.Unlock();
+ }
+
+#ifdef _DEBUG
+ bool ThreadHoldsProcessLock()
+ {
+ return m_processMutex.HasLock();
+ }
+#endif
+
+ // Expose the process lock.
+ // This is the main lock in V3.
+ RSLock * GetProcessLock()
+ {
+ return &m_processMutex;
+ }
+
+
+ // @dbgtodo synchronization - the SG lock goes away in V3.
+ // Expose the stop-go lock b/c varios Cordb objects in our process tree may need to take it.
+ RSLock * GetStopGoLock()
+ {
+ return &m_StopGoLock;
+ }
+
+
+ void UnrecoverableError(HRESULT errorHR,
+ unsigned int errorCode,
+ const char *errorFile,
+ unsigned int errorLine);
+ HRESULT CheckForUnrecoverableError();
+ void VerifyControlBlock();
+
+ // The implementation of EnumerateThreads without the public API error checks
+ VOID InternalEnumerateThreads(RSInitHolder<CordbHashTableEnum> * ppThreads);
+
+ //-----------------------------------------------------------
+ // Convenience routines
+ //-----------------------------------------------------------
+
+ // Is it safe to send events to the LS?
+ bool IsSafeToSendEvents() { return !m_unrecoverableError && !m_terminated && !m_detached; }
+
+ bool IsWin32EventThread();
+
+ void HandleSyncCompleteRecieved();
+
+ // Send a truly asynchronous IPC event.
+ void SendAsyncIPCEvent(DebuggerIPCEventType t);
+
+ HRESULT SendIPCEvent(DebuggerIPCEvent *event, SIZE_T eventSize)
+ {
+ // @dbgtodo - eventually remove this when all IPC events are gone.
+ // In V3 paths, we can't send IPC events.
+ if (GetShim() == NULL)
+ {
+ STRESS_LOG1(LF_CORDB, LL_INFO1000, "!! Can't send IPC event in V3. %s", IPCENames::GetName(event->type));
+ return E_NOTIMPL;
+ }
+ _ASSERTE(m_cordb != NULL);
+ return (m_cordb->SendIPCEvent(this, event, eventSize));
+ }
+
+ void InitAsyncIPCEvent(DebuggerIPCEvent *ipce,
+ DebuggerIPCEventType type,
+ VMPTR_AppDomain vmAppDomain)
+ {
+ // Async events only allowed for the following:
+ _ASSERTE(type == DB_IPCE_ATTACHING);
+
+ InitIPCEvent(ipce, type, false, vmAppDomain);
+ ipce->asyncSend = true;
+ }
+
+ void InitIPCEvent(DebuggerIPCEvent *ipce,
+ DebuggerIPCEventType type,
+ bool twoWay,
+ VMPTR_AppDomain vmAppDomain
+ )
+ {
+ // zero out the event in case we try and use any uninitialized fields
+ memset( ipce, 0, sizeof(DebuggerIPCEvent) );
+
+ _ASSERTE((!vmAppDomain.IsNull()) ||
+ type == DB_IPCE_GET_GCHANDLE_INFO ||
+ type == DB_IPCE_ENABLE_LOG_MESSAGES ||
+ type == DB_IPCE_MODIFY_LOGSWITCH ||
+ type == DB_IPCE_ASYNC_BREAK ||
+ type == DB_IPCE_CONTINUE ||
+ type == DB_IPCE_GET_BUFFER ||
+ type == DB_IPCE_RELEASE_BUFFER ||
+ type == DB_IPCE_IS_TRANSITION_STUB ||
+ type == DB_IPCE_ATTACHING ||
+ type == DB_IPCE_APPLY_CHANGES ||
+ type == DB_IPCE_CONTROL_C_EVENT_RESULT ||
+ type == DB_IPCE_SET_REFERENCE ||
+ type == DB_IPCE_SET_ALL_DEBUG_STATE ||
+ type == DB_IPCE_GET_THREAD_FOR_TASKID ||
+ type == DB_IPCE_DETACH_FROM_PROCESS ||
+ type == DB_IPCE_INTERCEPT_EXCEPTION ||
+ type == DB_IPCE_GET_NGEN_COMPILER_FLAGS ||
+ type == DB_IPCE_SET_NGEN_COMPILER_FLAGS ||
+ type == DB_IPCE_SET_VALUE_CLASS);
+
+ ipce->type = type;
+ ipce->hr = S_OK;
+ ipce->processId = 0;
+ ipce->vmAppDomain = vmAppDomain;
+ ipce->vmThread = VMPTR_Thread::NullPtr();
+ ipce->replyRequired = twoWay;
+ ipce->asyncSend = false;
+ ipce->next = NULL;
+ }
+
+ // Looks up a previously constructed CordbClass instance without creating. May return NULL if the
+ // CordbClass instance doesn't exist.
+ CordbClass * LookupClass(ICorDebugAppDomain * pAppDomain, VMPTR_DomainFile vmDomainFile, mdTypeDef classToken);
+
+ CordbModule * LookupOrCreateModule(VMPTR_DomainFile vmDomainFile);
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+ CordbUnmanagedThread *GetUnmanagedThread(DWORD dwThreadId)
+ {
+ _ASSERTE(ThreadHoldsProcessLock());
+ return m_unmanagedThreads.GetBase(dwThreadId);
+ }
+#endif // FEATURE_INTEROP_DEBUGGING
+
+ /*
+ * This will cleanup the patch table, releasing memory,etc.
+ */
+ void ClearPatchTable();
+
+ /*
+ * This will grab the patch table from the left side & go through
+ * it to gather info needed for faster access. If address,size,buffer
+ * are passed in, while going through the table we'll undo patches
+ * in buffer at the same time
+ */
+ HRESULT RefreshPatchTable(CORDB_ADDRESS address = NULL, SIZE_T size = NULL, BYTE buffer[] = NULL);
+
+ // Find if a patch exists at a given address.
+ HRESULT FindPatchByAddress(CORDB_ADDRESS address, bool *patchFound, bool *patchIsUnmanaged);
+
+ enum AB_MODE
+ {
+ AB_READ,
+ AB_WRITE
+ };
+
+ /*
+ * Once we've called RefreshPatchTable to get the patch table,
+ * this routine will iterate through the patches & either apply
+ * or unapply the patches to buffer. AB_READ => Replaces patches
+ * in buffer with the original opcode, AB_WRTE => replace opcode
+ * with breakpoint instruction, caller is responsible for
+ * updating the patchtable back to the left side.
+ *
+ * <TODO>@todo Perf Instead of a copy, undo the changes
+ * Since the 'buffer' arg is an [in] param, we're not supposed to
+ * change it. If we do, we'll allocate & copy it to bufferCopy
+ * (we'll also set *pbUpdatePatchTable to true), otherwise we
+ * don't manipuldate bufferCopy (so passing a NULL in for
+ * reading is fine).</TODO>
+ */
+ HRESULT AdjustBuffer(CORDB_ADDRESS address,
+ SIZE_T size,
+ BYTE buffer[],
+ BYTE **bufferCopy,
+ AB_MODE mode,
+ BOOL *pbUpdatePatchTable = NULL);
+
+ /*
+ * AdjustBuffer, above, doesn't actually update the local patch table
+ * if asked to do a write. It stores the changes alongside the table,
+ * and this will cause the changes to be written to the table (for
+ * a range of left-side addresses
+ */
+ void CommitBufferAdjustments(CORDB_ADDRESS start,
+ CORDB_ADDRESS end);
+
+ /*
+ * Clear the stored changes, or they'll sit there until we
+ * accidentally commit them
+ */
+ void ClearBufferAdjustments();
+
+
+
+
+ //-----------------------------------------------------------
+ // Accessors for key synchronization fields.
+ //-----------------------------------------------------------
+
+ // If CAD is NULL, returns true if all appdomains (ie, the entire process)
+ // is synchronized. Otherwise, returns true if the specified appdomain is
+ // synch'd.
+ bool GetSynchronized();
+ void SetSynchronized(bool fSynch);
+
+ void IncStopCount();
+ void DecStopCount();
+
+ // Gets the exact stop count. You need the Proecss lock for this.
+ int GetStopCount();
+
+ // Just gets whether we're stopped or not (m_stopped > 0).
+ // You only need the StopGo lock for this.
+ // This is biases towards returning false.
+ bool IsStopped();
+
+ bool GetSyncCompleteRecv();
+ void SetSyncCompleteRecv(bool fSyncRecv);
+
+
+ // Cordbg may not always continue during a callback; but we really shouldn't do meaningful
+ // work after a callback has returned yet before they've called continue. Thus we may need
+ // to remember some state at the time of dispatch so that we do stuff at continue.
+ // Only example here is neutering... we'd like to Neuter an object X after the ExitX callback,
+ // but we can't neuter it until Continue. So remember X when we dispatch, and neuter this at continue.
+ // Use a smart ptr to keep it alive until we neuter it.
+
+ // Add objects to various neuter lists.
+ // NeuterOnContinue is for all objects that can be neutered once we continue.
+ // NeuterOnExit is for all objects that can survive continues (but are neutered on process shutdown).
+ // If an object's external ref count goes to 0, it gets promoted to the NeuterOnContinue list.
+ void AddToNeuterOnExitList(CordbBase *pObject);
+ void AddToNeuterOnContinueList(CordbBase *pObject);
+
+ NeuterList * GetContinueNeuterList() { return &m_ContinueNeuterList; }
+ NeuterList * GetExitNeuterList() { return &m_ExitNeuterList; }
+
+ void AddToLeftSideResourceCleanupList(CordbBase * pObject);
+
+ // Routines to read and write thread context records between the processes safely.
+ HRESULT SafeReadThreadContext(LSPTR_CONTEXT pRemoteContext, DT_CONTEXT * pCtx);
+ HRESULT SafeWriteThreadContext(LSPTR_CONTEXT pRemoteContext, const DT_CONTEXT * pCtx);
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+ // Record a win32 event for debugging purposes.
+ void DebugRecordWin32Event(const DEBUG_EVENT * pEvent, CordbUnmanagedThread * pUThread);
+#endif // FEATURE_INTEROP_DEBUGGING
+
+ //-----------------------------------------------------------
+ // Interop Helpers
+ //-----------------------------------------------------------
+
+ // Get the DAC interface.
+ IDacDbiInterface * GetDAC();
+
+ // Get the data-target, which provides access to the debuggee.
+ ICorDebugDataTarget * GetDataTarget();
+
+ BOOL IsDacInitialized();
+
+ void ForceDacFlush();
+
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+ // Deal with native debug events for the interop-debugging scenario.
+ void HandleDebugEventForInteropDebugging(const DEBUG_EVENT * pEvent);
+
+ void ResumeHijackedThreads();
+
+ //@todo - We should try to make these all private
+ CordbUnmanagedThread *HandleUnmanagedCreateThread(DWORD dwThreadId, HANDLE hThread, void *lpThreadLocalBase);
+
+ HRESULT ContinueOOB();
+ void QueueUnmanagedEvent(CordbUnmanagedThread *pUThread, const DEBUG_EVENT *pEvent);
+ void DequeueUnmanagedEvent(CordbUnmanagedThread *pUThread);
+ void QueueOOBUnmanagedEvent(CordbUnmanagedThread *pUThread, const DEBUG_EVENT *pEvent);
+ void DequeueOOBUnmanagedEvent(CordbUnmanagedThread *pUThread);
+ void DispatchUnmanagedInBandEvent();
+ void DispatchUnmanagedOOBEvent();
+ bool ExceptionIsFlare(DWORD exceptionCode, const void *exceptionAddress);
+
+ bool IsSpecialStackOverflowCase(CordbUnmanagedThread *pUThread, const DEBUG_EVENT *pEvent);
+
+ HRESULT SuspendUnmanagedThreads();
+ HRESULT ResumeUnmanagedThreads();
+
+ HRESULT HijackIBEvent(CordbUnmanagedEvent * pUnmanagedEvent);
+
+ BOOL HasUndispatchedNativeEvents();
+ BOOL HasUserUncontinuedNativeEvents();
+#endif // FEATURE_INTEROP_DEBUGGING
+
+ HRESULT StartSyncFromWin32Stop(BOOL * pfAsyncBreakSent);
+
+
+ // For interop attach, we first do native, and then once Cordbg continues from
+ // the loader-bp, we kick off the managed attach. This field remembers that
+ // whether we need the managed attach.
+ // @dbgtodo managed pipeline - hoist to shim.
+ bool m_fDoDelayedManagedAttached;
+
+
+
+ // Table of CordbEval objects that we've sent over to the LS.
+ // This is synced via the process lock.
+ RsPtrTable<CordbEval> m_EvalTable;
+
+ void PrepopulateThreadsOrThrow();
+
+ // Lookup or create an appdomain.
+ CordbAppDomain * LookupOrCreateAppDomain(VMPTR_AppDomain vmAppDomain);
+
+ // Get the shared app domain.
+ CordbAppDomain * GetSharedAppDomain();
+
+ // Get metadata dispenser.
+ IMetaDataDispenserEx * GetDispenser();
+
+ // Sets a bitfield reflecting the managed debugging state at the time of
+ // the jit attach.
+ HRESULT GetAttachStateFlags(CLR_DEBUGGING_PROCESS_FLAGS *pFlags);
+
+ HRESULT GetTypeForObject(CORDB_ADDRESS obj, CordbType **ppType, CordbAppDomain **pAppDomain = NULL);
+
+ WriteableMetadataUpdateMode GetWriteableMetadataUpdateMode() { return m_writableMetadataUpdateMode; }
+private:
+
+#ifdef _DEBUG
+ // Assert that vmAppDomainDeleted doesn't show up in dac enumerations
+ void DbgAssertAppDomainDeleted(VMPTR_AppDomain vmAppDomainDeleted);
+
+ // Callback helper for DbgAssertAppDomainDeleted.
+ static void DbgAssertAppDomainDeletedCallback(VMPTR_AppDomain vmAppDomain, void * pUserData);
+#endif // _DEBUG
+
+ static void ThreadEnumerationCallback(VMPTR_Thread vmThread, void * pUserData);
+
+
+ // Callback for AppDomain enumeration
+ static void AppDomainEnumerationCallback(VMPTR_AppDomain vmAppDomain, void * pUserData);
+
+ // Helper to create a new CordbAppDomain around the vmptr and cache it
+ CordbAppDomain * CacheAppDomain(VMPTR_AppDomain vmAppDomain);
+
+ // Helper to traverse Appdomains in target and build up our cache.
+ void PrepopulateAppDomainsOrThrow();
+
+
+ void ProcessFirstLogMessage (DebuggerIPCEvent *event);
+ void ProcessContinuedLogMessage (DebuggerIPCEvent *event);
+
+ void CloseIPCHandles();
+ void UpdateThreadsForAdUnload( CordbAppDomain* pAppDomain );
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+ // Each win32 debug event needs to be triaged to get a Reaction.
+ Reaction TriageBreakpoint(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent);
+ Reaction TriageSyncComplete();
+ Reaction Triage1stChanceNonSpecial(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent);
+ Reaction TriageExcep1stChanceAndInit(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent);
+ Reaction TriageExcep2ndChanceAndInit(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent);
+ Reaction TriageWin32DebugEvent(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent);
+#endif // FEATURE_INTEROP_DEBUGGING
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+public:
+ RSSmartPtr<Cordb> m_cordb;
+
+private:
+ // OS process handle to live process.
+ // @dbgtodo - , Move this into the Shim. This should only be needed in the live-process
+ // case. Get rid of this since it breaks the data-target abstraction.
+ // For Mac debugging, this handle is of course not the real process handle. This is just a handle to
+ // wait on for process termination.
+ HANDLE m_handle;
+
+public:
+ // Wrapper to get the OS process handle. This is unsafe because it breaks the data-target abstraction.
+ // The only things that need this should be calls to DuplicateHandle, and some shimming work.
+ HANDLE UnsafeGetProcessHandle()
+ {
+ return m_handle;
+ }
+
+ // Set when code:CordbProcess::Detach is called.
+ // Public APIs can check this and return CORDBG_E_PROCESS_DETACHED.
+ // @dbgtodo managed pipeline - really could merge this with neuter.
+ bool m_detached;
+
+ // True if we code:CordbProcess::Stop is called before the managed CreateProcess event.
+ // In this case, m_initialized is false, and we can't send an AsyncBreak event to the LS.
+ // (since the LS isn't going to send a SyncComplete event back since the CLR isn't loaded/ready).
+ // @dbgtodo managed pipeline - move into shim, along with Stop/Continue.
+ bool m_uninitializedStop;
+
+
+ // m_exiting is true if we know the LS is starting to exit (if the
+ // RS is telling the LS to exit) or if we know the LS has already exited.
+ bool m_exiting;
+
+
+ // m_terminated can only be set to true if we know 100% the LS has exited (ie, somebody
+ // waited on the LS process handle).
+ bool m_terminated;
+
+ bool m_unrecoverableError;
+
+ bool m_specialDeferment;
+ bool m_helperThreadDead; // flag used for interop
+
+ // This tracks if the loader breakpoint has been received during interop-debugging.
+ // The Loader Breakpoint is an breakpoint event raised by the OS once the debugger is attached.
+ // It comes in both Attach and Launch scenarios.
+ // This is also used in fake-native debugging scenarios.
+ bool m_loaderBPReceived;
+
+
+private:
+
+ // MetaData dispenser.
+ RSExtSmartPtr<IMetaDataDispenserEx> m_pMetaDispenser;
+
+ //
+ // Count of the number of outstanding CordbEvals in the process.
+ //
+ LONG m_cOutstandingEvals;
+
+ // Number of oustanding code:CordbHandleValue objects containing
+ // Left-side resources. This can be used to tell if ICorDebug needs to
+ // cleanup gc handles.
+ LONG m_cOutstandingHandles;
+
+ // Pointer to the CordbModule instance that can currently change the Jit flags.
+ // There can be at most one of these. It will represent a module that has just been loaded, before the
+ // Continue is sent. See code:CordbProcess::RawDispatchEvent and code:CordbProcess::ContinueInternal.
+ CordbModule * m_pModuleThatCanChangeJitFlags;
+
+public:
+ LONG OutstandingEvalCount()
+ {
+ return m_cOutstandingEvals;
+ }
+
+ void IncrementOutstandingEvalCount()
+ {
+ InterlockedIncrement(&m_cOutstandingEvals);
+ }
+
+ void DecrementOutstandingEvalCount()
+ {
+ InterlockedDecrement(&m_cOutstandingEvals);
+ }
+
+ LONG OutstandingHandles();
+ void IncrementOutstandingHandles();
+ void DecrementOutstandingHandles();
+
+ //
+ // Is it OK to detach at this time
+ //
+ HRESULT IsReadyForDetach();
+
+
+private:
+ // This is a target pointer that uniquely identifies the runtime in the target.
+ // This lets ICD discriminate between multiple CLRs within a single process.
+ // On windows, this is the base-address of mscorwks.dll in the target.
+ // If this is 0, then we have V2 semantics where there was only 1 CLR in the target.
+ // In that case, we can lazily initialize it in code:CordbProcess::CopyManagedEventFromTarget.
+ // This is just used for backwards compat.
+ CORDB_ADDRESS m_clrInstanceId;
+
+ // List of things that get neutered on process exit and Continue respectively.
+ NeuterList m_ExitNeuterList;
+ NeuterList m_ContinueNeuterList;
+
+ // List of objects that hold resources into the left-side.
+ // This is currently for funceval, which cleans up resources in code:CordbEval::SendCleanup.
+ // @dbgtodo - , (func-eval feature crew): we can get rid of this
+ // list if we make func-eval not hold resources after it's complete.
+ LeftSideResourceCleanupList m_LeftSideResourceCleanupList;
+
+ // m_stopCount, m_synchronized, & m_syncCompleteReceived are key fields describing
+ // the processes' sync status.
+ DWORD m_stopCount;
+
+ // m_synchronized is the Debugger's view of SyncStatus. It will go high & low for each
+ // callback. Continue() will set this to false.
+ // This flag is true roughly from the time that we've dispatched a managed callback
+ // until the time that it's continued.
+ bool m_synchronized;
+
+ // m_syncCompleteReceived tells us if the runtime is _actually_ sychronized. It goes
+ // high once we get a SyncComplete, and it goes low once we actually send the continue.
+ // This is always set by the thread that receives the sync-complete. In interop, that's the w32et.
+ // Thus this is the most accurate indication of wether the Debuggee is _actually_ synchronized or not.
+ bool m_syncCompleteReceived;
+
+
+ // Back pointer to Shim process. This is used for hooks back into the shim.
+ // If this is Non-null, then we're emulating the V2 case. If this is NULL, then it's the real V3 pipeline.
+ RSExtSmartPtr<ShimProcess> m_pShim;
+
+ CordbSafeHashTable<CordbThread> m_userThreads;
+
+public:
+ ShimProcess* GetShim();
+
+ bool m_oddSync;
+
+
+ void BuildThreadEnum(CordbBase * pOwnerObj, NeuterList * pOwnerList, RSInitHolder<CordbHashTableEnum> * pHolder);
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+ // List of unmanaged threads. This is only populated for interop-debugging.
+ CordbSafeHashTable<CordbUnmanagedThread> m_unmanagedThreads;
+#endif // FEATURE_INTEROP_DEBUGGING
+
+ CordbSafeHashTable<CordbAppDomain> m_appDomains;
+
+ CordbAppDomain * m_sharedAppDomain;
+
+ // Since a stepper can begin in one appdomain, and complete in another,
+ // we put the hashtable here, rather than on specific appdomains.
+ CordbSafeHashTable<CordbStepper> m_steppers;
+
+ // Used to figure out if we have to refresh any reference objects
+ // on the left side. Gets incremented each time a continue is called, or
+ // global debugee state is modified in some other way.
+ UINT m_continueCounter;
+
+ // Used to track whether the DAC cache has been flushed.
+ // We use this information to determine whether CordbStackWalk instances need to
+ // be refreshed.
+ UINT m_flushCounter;
+
+ // The DCB is essentially a buffer area used to temporarily hold information read from the debugger
+ // control block residing on the LS helper thread. We make no assumptions about the validity of this
+ // information over time, so before using a value from it on the RS, we will always update this buffer
+ // with a call to UpdateRightSideDCB. This uses a ReadProcessMemory to get the current information from
+ // the LS DCB.
+ DebuggerIPCControlBlock * GetDCB() {return ((m_pEventChannel == NULL) ? NULL : m_pEventChannel->GetDCB());}
+
+
+ DebuggerIPCRuntimeOffsets m_runtimeOffsets;
+ HANDLE m_leftSideEventAvailable;
+ HANDLE m_leftSideEventRead;
+#if defined(FEATURE_INTEROP_DEBUGGING)
+ HANDLE m_leftSideUnmanagedWaitEvent;
+#endif // FEATURE_INTEROP_DEBUGGING
+
+
+ // This becomes true when the RS receives its first managed event.
+ // This goes false in shutdown cases.
+ // If this is true, we can assume:
+ // - the CLR is loaded.
+ // - the IPC block is opened and initialized.
+ // - DAC is initialized (see code:CordbProcess::IsDacInitialized)
+ //
+ // If this is false, we can assume:
+ // - the CLR may not be loaded into the target process.
+ // - We can't send IPC events to the LS (because we can't expect a response)
+ //
+ // Many APIs can check this bit and return CORDBG_E_NOTREADY if it's false.
+ bool m_initialized;
+
+#ifdef _DEBUG
+ void * m_pDBGLastIPCEventType;
+#endif
+
+ bool m_stopRequested;
+ HANDLE m_stopWaitEvent;
+ RSLock m_processMutex;
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+ // The number of threads which are IsFirstChanceHijacked
+ DWORD m_cFirstChanceHijackedThreads;
+
+ CordbUnmanagedEvent *m_unmanagedEventQueue;
+ CordbUnmanagedEvent *m_lastQueuedUnmanagedEvent;
+ CordbUnmanagedEvent *m_lastQueuedOOBEvent;
+ CordbUnmanagedEvent *m_outOfBandEventQueue;
+
+ CordbUnmanagedEvent *m_lastDispatchedIBEvent;
+ bool m_dispatchingUnmanagedEvent;
+ bool m_dispatchingOOBEvent;
+ bool m_doRealContinueAfterOOBBlock;
+
+ enum
+ {
+ PS_WIN32_STOPPED = 0x0001,
+ PS_HIJACKS_IN_PLACE = 0x0002,
+ PS_SOME_THREADS_SUSPENDED = 0x0004,
+ PS_WIN32_ATTACHED = 0x0008,
+ PS_WIN32_OUTOFBAND_STOPPED = 0x0010,
+ };
+
+ unsigned int m_state;
+#endif // FEATURE_INTEROP_DEBUGGING
+
+ // True if we're interop-debugging, else false.
+ bool IsInteropDebugging();
+
+ DWORD m_helperThreadId; // helper thread ID calculated from sniffing from UM thread-create events.
+
+ // Is the given thread id a helper thread (real or worker?)
+ bool IsHelperThreadWorked(DWORD tid);
+
+ //
+ // We cache the LS patch table on the RS.
+ //
+
+ // The array of entries. (The patchtable is a hash implemented as a single-array)
+ // This array includes empty entries.
+ // There is an auxillary bucket structure used to map hash codes to array indices.
+ // We traverse the array, and we recognize an empty slot
+ // if DebuggerControllerPatch::opcode == 0.
+ // If we haven't gotten the table, then m_pPatchTable is NULL
+ BYTE* m_pPatchTable;
+
+ // The number of entries (both used & unused) in m_pPatchTable.
+ UINT m_cPatch;
+
+ // so we know where to write the changes patchtable back to
+ // This has m_cPatch elements.
+ BYTE *m_rgData;
+
+ // Cached value of iNext entries such that:
+ // m_rgNextPatch[i] = ((DebuggerControllerPatch*)m_pPatchTable)[i]->iNext;
+ // where 0 <= i < m_cPatch
+ // This provides a linked list (via indices) to traverse the used entries of m_pPatchTable.
+ // This has m_cPatch elements.
+ ULONG *m_rgNextPatch;
+
+ // This has m_cPatch elements.
+ PRD_TYPE *m_rgUncommitedOpcode;
+
+ // CORDB_ADDRESS's are UINT_PTR's (64 bit under _WIN64, 32 bit otherwise)
+#if defined(DBG_TARGET_WIN64)
+#define MAX_ADDRESS (_UI64_MAX)
+#else
+#define MAX_ADDRESS (ULONG_MAX)
+#endif
+#define MIN_ADDRESS (0x0)
+ CORDB_ADDRESS m_minPatchAddr; //smallest patch in table
+ CORDB_ADDRESS m_maxPatchAddr;
+
+ // <TODO>@todo port : if slots of CHashTable change, so should these</TODO>
+#define DPT_TERMINATING_INDEX (UINT32_MAX)
+ // Index into m_pPatchTable of the first patch (first used entry).
+ ULONG m_iFirstPatch;
+
+ // Initializes the DAC
+ void InitDac();
+
+ // copy new data from LS DCB to RS buffer
+ void UpdateRightSideDCB();
+
+ // copy new data from RS DCB buffer to LS DCB
+ void UpdateLeftSideDCBField(void * rsFieldAddr, SIZE_T size);
+
+ // allocate and initialize the RS DCB buffer
+ void GetEventBlock(BOOL * pfBlockExists);
+
+ IEventChannel * GetEventChannel();
+
+ bool SupportsVersion(CorDebugInterfaceVersion featureVersion);
+
+ void StartEventDispatch(DebuggerIPCEventType event);
+ void FinishEventDispatch();
+ bool AreDispatchingEvent();
+
+ HANDLE GetHelperThreadHandle() { return m_hHelperThread; }
+
+ CordbAppDomain* GetDefaultAppDomain() { return m_pDefaultAppDomain; }
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+ // Lookup if there's a native BP at the given address. Return NULL not found.
+ NativePatch * GetNativePatch(const void * pAddress);
+#endif // FEATURE_INTEROP_DEBUGGING
+
+ bool IsBreakOpcodeAtAddress(const void * address);
+
+private:
+ //
+ // handle to helper thread. Used for managed debugging.
+ // Initialized only after we get the tid from the DCB.
+ HANDLE m_hHelperThread;
+
+ DebuggerIPCEventType m_dispatchedEvent; // what event are we currently dispatching?
+
+ RSLock m_StopGoLock;
+
+ // Each process has exactly one Default AppDomain
+ // @dbgtodo appdomain : We should try and simplify things by removing this.
+ // At the moment it's necessary for CordbProcess::UpdateThreadsForAdUnload.
+ CordbAppDomain* m_pDefaultAppDomain; // owned by m_appDomains
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+ // Helpers
+ CordbUnmanagedThread * GetUnmanagedThreadFromEvent(const DEBUG_EVENT * pEvent);
+#endif // FEATURE_INTEROP_DEBUGGING
+
+ // Ensure we have a CLR Instance ID to debug
+ HRESULT EnsureClrInstanceIdSet();
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+ // // The full debug event is too large, so we just remember the important stuff.
+ struct MiniDebugEvent
+ {
+ BYTE code; // event code from the debug event
+ CordbUnmanagedThread * pUThread; // unmanaged thread this was on.
+ // @todo - we should have some misc data.
+ union
+ {
+ struct {
+ void * pAddress; // address of an exception
+ DWORD dwCode;
+ } ExceptionData;
+ struct {
+ void * pBaseAddress; // for module load & unload
+ } ModuleData;
+ } u;
+ };
+
+ // Group fields that are just used for debug support here.
+ // Some are included even in retail builds to help debug retail failures.
+ struct DebugSupport
+ {
+ // For debugging, we keep a rolling queue of the last N Win32 debug events.
+ MiniDebugEvent m_DebugEventQueue[DEBUG_EVENTQUEUE_SIZE];
+ int m_DebugEventQueueIdx;
+ int m_TotalNativeEvents;
+
+ // Breakdown of different types of native events
+ int m_TotalIB;
+ int m_TotalOOB;
+ int m_TotalCLR;
+ } m_DbgSupport;
+
+ CUnorderedArray<NativePatch, 10> m_NativePatchList;
+#endif // FEATURE_INTEROP_DEBUGGING
+
+ //
+ // DAC
+ //
+
+ // Try to initalize DAC, may fail
+ BOOL TryInitializeDac();
+
+ // Expect DAC initialize to succeed.
+ void InitializeDac();
+
+
+ void CreateDacDbiInterface();
+
+ // Free DAC.
+ void FreeDac();
+
+
+ HModuleHolder m_hDacModule;
+ RSExtSmartPtr<ICorDebugDataTarget> m_pDACDataTarget;
+
+ // The mutable version of the data target, or null if read-only
+ RSExtSmartPtr<ICorDebugMutableDataTarget> m_pMutableDataTarget;
+
+ RSExtSmartPtr<ICorDebugMetaDataLocator> m_pMetaDataLocator;
+
+ IDacDbiInterface * m_pDacPrimitives;
+
+ IEventChannel * m_pEventChannel;
+
+ // If true, then we'll ASSERT if we detect the target is corrupt or inconsistent
+ // This switch is for diagnostics purposes only and should always be false in retail builds.
+ bool m_fAssertOnTargetInconsistency;
+
+ // When a successful attempt to read runtime offsets from LS occurs, this flag is set.
+ bool m_runtimeOffsetsInitialized;
+
+ // controls how metadata updated in the target is handled
+ WriteableMetadataUpdateMode m_writableMetadataUpdateMode;
+};
+
+// Some IMDArocess APIs are supported as interop-only.
+#define FAIL_IF_MANAGED_ONLY(pProcess) \
+{ CordbProcess * __Proc = pProcess; if (!__Proc->IsInteropDebugging()) return CORDBG_E_MUST_BE_INTEROP_DEBUGGING; }
+
+
+/* ------------------------------------------------------------------------- *
+ * Module class
+ * ------------------------------------------------------------------------- */
+
+class CordbModule : public CordbBase,
+ public ICorDebugModule,
+ public ICorDebugModule2,
+ public ICorDebugModule3
+{
+public:
+ CordbModule(CordbProcess * process,
+ VMPTR_Module vmModule,
+ VMPTR_DomainFile vmDomainFile);
+
+ virtual ~CordbModule();
+ virtual void Neuter();
+
+ using CordbBase::GetProcess;
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbModule"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugModule
+ //-----------------------------------------------------------
+
+ COM_METHOD GetProcess(ICorDebugProcess **ppProcess);
+ COM_METHOD GetBaseAddress(CORDB_ADDRESS *pAddress);
+ COM_METHOD GetAssembly(ICorDebugAssembly **ppAssembly);
+ COM_METHOD GetName(ULONG32 cchName, ULONG32 *pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]);
+ COM_METHOD EnableJITDebugging(BOOL bTrackJITInfo, BOOL bAllowJitOpts);
+ COM_METHOD EnableClassLoadCallbacks(BOOL bClassLoadCallbacks);
+
+ // Gets the latest version of a function given the methodDef token
+ COM_METHOD GetFunctionFromToken(mdMethodDef methodDef,
+ ICorDebugFunction **ppFunction);
+ COM_METHOD GetFunctionFromRVA(CORDB_ADDRESS rva, ICorDebugFunction **ppFunction);
+ COM_METHOD GetClassFromToken(mdTypeDef typeDef,
+ ICorDebugClass **ppClass);
+ COM_METHOD CreateBreakpoint(ICorDebugModuleBreakpoint **ppBreakpoint);
+
+ // Not implemented - legacy
+ COM_METHOD GetEditAndContinueSnapshot(
+ ICorDebugEditAndContinueSnapshot **ppEditAndContinueSnapshot);
+
+ COM_METHOD GetMetaDataInterface(REFIID riid, IUnknown **ppObj);
+ COM_METHOD GetToken(mdModule *pToken);
+ COM_METHOD IsDynamic(BOOL *pDynamic);
+ COM_METHOD GetGlobalVariableValue(mdFieldDef fieldDef,
+ ICorDebugValue **ppValue);
+ COM_METHOD GetSize(ULONG32 *pcBytes);
+ COM_METHOD IsInMemory(BOOL *pInMemory);
+
+ //-----------------------------------------------------------
+ // ICorDebugModule2
+ //-----------------------------------------------------------
+ COM_METHOD SetJMCStatus(
+ BOOL fIsUserCode,
+ ULONG32 cOthers,
+ mdToken others[]);
+
+ // Applies an EnC edit to the module
+ COM_METHOD ApplyChanges(
+ ULONG cbMetaData,
+ BYTE pbMetaData[],
+ ULONG cbIL,
+ BYTE pbIL[]);
+
+ // Resolve an assembly given an AssemblyRef token. Note that
+ // this will not trigger the loading of assembly. If assembly is not yet loaded,
+ // this will return an CORDBG_E_CANNOT_RESOLVE_ASSEMBLY error
+ COM_METHOD ResolveAssembly(mdToken tkAssemblyRef,
+ ICorDebugAssembly **ppAssembly);
+
+ // Sets EnC and optimization flags
+ COM_METHOD SetJITCompilerFlags(DWORD dwFlags);
+
+ // Gets EnC and optimization flags
+ COM_METHOD GetJITCompilerFlags(DWORD *pdwFlags);
+
+ //-----------------------------------------------------------
+ // ICorDebugModule3
+ //-----------------------------------------------------------
+ COM_METHOD CreateReaderForInMemorySymbols(REFIID riid,
+ void** ppObj);
+
+ //-----------------------------------------------------------
+ // Internal members
+ //-----------------------------------------------------------
+
+#ifdef _DEBUG
+ // Debug helper to ensure that module is no longer discoverable
+ void DbgAssertModuleDeleted();
+#endif // _DEBUG
+
+ // Internal help to get the "name" (filename or pretty name) of the module.
+ HRESULT GetNameWorker(ULONG32 cchName, ULONG32 *pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]);
+
+ // Marks that the module's metadata has become invalid and needs to be refetched.
+ void RefreshMetaData();
+
+ // Cache the current continue counter as the one that the LoadEvent is
+ // dispatched in.
+ void SetLoadEventContinueMarker();
+
+ // Return CORDBG_E_MUST_BE_IN_LOAD_MODULE if this module is not in its load callback.
+ HRESULT EnsureModuleIsInLoadCallback();
+
+ BOOL IsDynamic();
+
+ // Gets the latest version of the function for the methodDef, if any
+ CordbFunction * LookupFunctionLatestVersion(mdMethodDef methodToken);
+
+ // Gets the latest version of the function. Creates a new instance if none exists yet.
+ CordbFunction* LookupOrCreateFunctionLatestVersion(mdMethodDef funcMetaDataToken);
+
+ // Finds or creates a function for the first time (not for use on EnC if function doesn't exist yet)
+ CordbFunction * LookupOrCreateFunction(mdMethodDef token, SIZE_T enCVersion);
+
+ // Creates an CordbFunction instances for the first time (not for use on EnC)
+ CordbFunction * CreateFunction(mdMethodDef token, SIZE_T enCVersion);
+
+ // Creates a CordbFunction object to represent the specified EnC version
+ HRESULT UpdateFunction(mdMethodDef token,
+ SIZE_T newEnCVersion,
+ CordbFunction** ppFunction);
+
+ CordbClass* LookupClass(mdTypeDef classToken);
+ HRESULT LookupOrCreateClass(mdTypeDef classToken, CordbClass** ppClass);
+ HRESULT CreateClass(mdTypeDef classToken, CordbClass** ppClass);
+ HRESULT LookupClassByToken(mdTypeDef token, CordbClass **ppClass);
+ HRESULT ResolveTypeRef(mdTypeRef token, CordbClass **ppClass);
+ HRESULT ResolveTypeRefOrDef(mdToken token, CordbClass **ppClass);
+
+ // Sends the event to the left side to apply the changes to the debugee
+ HRESULT ApplyChangesInternal(
+ ULONG cbMetaData,
+ BYTE pbMetaData[],
+ ULONG cbIL,
+ BYTE pbIL[]);
+
+ // Pulls new metadata if needed in order to ensure the availability of
+ // the given token
+ void UpdateMetaDataCacheIfNeeded(mdToken token);
+
+ HRESULT InitPublicMetaDataFromFile(const WCHAR * pszFullPathName, DWORD dwOpenFlags, bool validateFileInfo);
+
+ // Creates a CordbNativeCode (if it's not already created) and adds it to the
+ // hash table of CordbNativeCodes belonging to the module.
+ CordbNativeCode * LookupOrCreateNativeCode(mdMethodDef methodToken,
+ VMPTR_MethodDesc methodDesc,
+ CORDB_ADDRESS startAddress);
+
+private:
+ // Set the metadata (both public and internal) for the module.
+ void InitMetaData(TargetBuffer buffer, BOOL useFileMappingOptimization);
+
+ // Checks if the given token is in the cached metadata
+ BOOL CheckIfTokenInMetaData(mdToken token);
+
+ // Update the public metadata given a buffer in the target.
+ void UpdatePublicMetaDataFromRemote(TargetBuffer bufferRemoteMetaData);
+
+ // Initialize just the public metadata by reading from an on-disk module
+ HRESULT InitPublicMetaDataFromFile();
+ // Initialize just the public metadata by reading new metadata from the buffer
+ void InitPublicMetaData(TargetBuffer buffer);
+
+ // Rebuild the internal metadata given the public one.
+ void UpdateInternalMetaData();
+
+ // Determines whether the on-disk metadata for this module is usable as the
+ // current metadata
+ BOOL IsFileMetaDataValid();
+
+ // Helper to copy metadata buffer from the Target to the host.
+ void CopyRemoteMetaData(TargetBuffer buffer, CoTaskMemHolder<VOID> * pLocalBuffer);
+
+
+ CordbAssembly * ResolveAssemblyInternal(mdToken tkAssemblyRef);
+
+ BOOL IsWinMD();
+
+ //-----------------------------------------------------------
+ // Convenience routines
+ //-----------------------------------------------------------
+
+public:
+ CordbAppDomain *GetAppDomain()
+ {
+ return m_pAppDomain;
+ }
+
+ CordbAssembly * GetCordbAssembly ();
+
+ // Get the module filename, or NULL if none. Throws on error.
+ const WCHAR * GetModulePath();
+
+ const WCHAR * GetNGenImagePath();
+
+ const VMPTR_DomainFile GetRuntimeDomainFile ()
+ {
+ return m_vmDomainFile;
+ }
+
+ const VMPTR_Module GetRuntimeModule()
+ {
+ return m_vmModule;
+ }
+
+ // Get symbol stream for in-memory modules.
+ IDacDbiInterface::SymbolFormat GetInMemorySymbolStream(IStream ** ppStream);
+
+ // accessor for PE file
+ VMPTR_PEFile GetPEFile();
+
+
+ IMetaDataImport * GetMetaDataImporter();
+
+ // accessor for Internal MetaData importer.
+ IMDInternalImport * GetInternalMD();
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+public:
+ CordbAssembly* m_pAssembly;
+ CordbAppDomain* m_pAppDomain;
+ CordbSafeHashTable<CordbClass> m_classes;
+
+ // A collection, indexed by methodDef, of the latest version of functions in this module
+ // The collection is filled lazily by LookupOrCreateFunction
+ CordbSafeHashTable<CordbFunction> m_functions;
+
+ // The real handle into the VM for a module. This is appdomain aware.
+ // This is the primary VM counterpart for the CordbModule.
+ VMPTR_DomainFile m_vmDomainFile;
+
+ VMPTR_Module m_vmModule;
+
+ DWORD m_EnCCount;
+
+private:
+
+ enum ILWinMDState
+ {
+ Uninitialized,
+ False,
+ True
+ };
+
+ // Base Address and size of this module in debuggee's process. Maybe null if unknown.
+ TargetBuffer m_PEBuffer;
+
+ BOOL m_fDynamic; // Dynamic modules can grow (like Reflection Emit)
+ BOOL m_fInMemory; // In memory modules don't have file-backing.
+ ILWinMDState m_isIlWinMD; // WinMD modules don't support all metadata interfaces
+
+ // Indicates that the module must serialize its metadata in process as part of metadata
+ // refresh. This is required for modules updated on the fly by the profiler
+ BOOL m_fForceMetaDataSerialize;
+
+ // Full path to module's image, if any. Empty if none, NULL if not yet set.
+ StringCopyHolder m_strModulePath;
+
+ // Full path to the ngen file. Empty if not ngenned, NULL if not yet set.
+ // This isn't exposed publicly, but we may use it internally for loading metadata.
+ StringCopyHolder m_strNGenImagePath;
+
+ // "Global" class for this module. Global functions + vars exist in this class.
+ RSSmartPtr<CordbClass> m_pClass;
+
+ // Handle to PEFile, useful for metadata lookups.
+ // this should always be non-null.
+ VMPTR_PEFile m_vmPEFile;
+
+
+ // Public metadata importer. This is lazily initialized and accessed from code:GetMetaDataImporter
+ // This is handed out to debugger clients via code:CordbModule::GetMetaDataInterface
+ // This is also tightly coupled to the internal metadata importer, m_pInternalMetaDataImport.
+ RSExtSmartPtr<IMetaDataImport> m_pIMImport;
+
+ // Internal metadata object. This is closely tied to the public metadata object (m_pIMImport).
+ // They share the same backing storage, but expose different interfaces to that storage.
+ // Debugger authors and tools use the public interfaces.
+ // DAC-ized operations in the VM require an IMDInternalImport.
+ // The public and internal must be updated together.
+ // This ultimately gets handed back to DAC via code:CordbProcess::LookupMetaData
+ RSExtSmartPtr<IMDInternalImport> m_pInternalMetaDataImport;
+
+ // Continue counter of when the module was loaded.
+ // See code:CordbModule::SetLoadEventContinueMarker for details
+ UINT m_nLoadEventContinueCounter;
+
+ // This is a table of all NativeCode objects in the module indexed
+ // by start address
+ // The collection is filled lazily by LookupOrCreateNativeCode
+ CordbSafeHashTable<CordbNativeCode> m_nativeCodeTable;
+};
+
+
+//-----------------------------------------------------------------------------
+// Cordb MDA notification
+//-----------------------------------------------------------------------------
+class CordbMDA : public CordbBase, public ICorDebugMDA
+{
+public:
+ CordbMDA(CordbProcess * pProc, DebuggerMDANotification * pData);
+ ~CordbMDA();
+
+ virtual void Neuter();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbMDA"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRefEnforceExternal());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseReleaseEnforceExternal());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugMDA
+ //-----------------------------------------------------------
+
+ // Get the string for the type of the MDA. Never empty.
+ // This is a convenient performant alternative to getting the XML stream and extracting
+ // the type from that based off the schema.
+ COM_METHOD GetName(ULONG32 cchName, ULONG32 * pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]);
+
+ // Get a string description of the MDA. This may be empty (0-length).
+ COM_METHOD GetDescription(ULONG32 cchName, ULONG32 * pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]);
+
+ // Get the full associated XML for the MDA. This may be empty.
+ // This could be a potentially expensive operation if the xml stream is large.
+ // See the MDA documentation for the schema for this XML stream.
+ COM_METHOD GetXML(ULONG32 cchName, ULONG32 * pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]);
+
+ COM_METHOD GetFlags(CorDebugMDAFlags * pFlags);
+
+ // Thread that the MDA is fired on. We use the os tid instead of an ICDThread in case an MDA is fired on a
+ // native thread (or a managed thread that hasn't yet entered managed code and so we don't have a ICDThread
+ // object for it yet)
+ COM_METHOD GetOSThreadId(DWORD * pOsTid);
+
+private:
+ NewArrayHolder<WCHAR> m_szName;
+ NewArrayHolder<WCHAR> m_szDescription;
+ NewArrayHolder<WCHAR> m_szXml;
+
+ DWORD m_dwOSTID;
+ CorDebugMDAFlags m_flags;
+};
+
+
+
+struct CordbHangingField
+{
+ FREEHASHENTRY entry;
+ FieldData data;
+};
+
+// A hashtable for storing EnC hanging field information
+// FieldData.m_fldMetadataToken is the key
+class CordbHangingFieldTable : public CHashTableAndData<CNewDataNoThrow>
+{
+ private:
+
+ BOOL Cmp(SIZE_T k1, const HASHENTRY *pc2)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (ULONG)(UINT_PTR)(k1) !=
+ (reinterpret_cast<const CordbHangingField *>(pc2))->data.m_fldMetadataToken;
+ }
+
+ ULONG HASH(mdFieldDef fldToken)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return fldToken;
+ }
+
+ SIZE_T KEY(mdFieldDef fldToken)
+ {
+ return (SIZE_T)fldToken;
+ }
+
+ public:
+
+#ifndef DACCESS_COMPILE
+
+ CordbHangingFieldTable() : CHashTableAndData<CNewDataNoThrow>(11)
+ {
+ NewInit(11, sizeof(CordbHangingField), 11);
+ }
+
+ FieldData * AddFieldInfo(FieldData * pInfo)
+ {
+ _ASSERTE(pInfo != NULL);
+
+ CordbHangingField *pEntry = (CordbHangingField *)Add(HASH(pInfo->m_fldMetadataToken));
+ pEntry->data = *pInfo; // copy everything over
+
+ // Return a pointer to the data
+ return &(pEntry->data);
+ }
+
+ void RemoveFieldInfo(mdFieldDef fldToken)
+ {
+ CordbHangingField *entry = (CordbHangingField*)Find(HASH(fldToken), KEY(fldToken));
+ _ASSERTE(entry != NULL);
+ Delete(HASH(fldToken), (HASHENTRY*)entry);
+ }
+
+#endif // #ifndef DACCESS_COMPILE
+
+ FieldData * GetFieldInfo(mdFieldDef fldToken)
+ {
+ CordbHangingField * entry = (CordbHangingField *)Find(HASH(fldToken), KEY(fldToken));
+ return (entry!=NULL?&(entry->data):NULL);
+ }
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Instantiation.
+ *
+ * This struct stores a set of type parameters. It is used in
+ * the heap-allocated data structures CordbType and CordbNativeCode.
+ *
+ * CordbType::m_inst. Stores the class type parameters if any,
+ * or the solitary array type parameter, or the solitary parameter
+ * to a byref type.
+ *
+ * CordbJITILFrame::m_genericArgs. Stores exact generic parameters for the generic method frame if available
+ * Need not be identicial if code is shared between generic instantiations.
+ * May be inexact if real instantiation has been optimized away off
+ * the frame (nb this gets reported by the left side)
+ *
+ * This is conceptually an array of Type-parameters, with the split (m_cClassTyPars) between
+ * where the Type's type-parameters end and the Method's type-parameters begin.
+ * ------------------------------------------------------------------------- */
+class Instantiation
+{
+public:
+ // Empty ctor
+ Instantiation()
+ : m_cInst(0), m_ppInst(NULL), m_cClassTyPars (0)
+ { }
+
+ // Instantiation for Type. 0 Method type-parameters.
+ Instantiation(unsigned int _cClassInst, CordbType **_ppClassInst)
+ : m_cInst(_cClassInst), m_ppInst(_ppClassInst), m_cClassTyPars(_cClassInst)
+ {LIMITED_METHOD_CONTRACT; }
+
+ // Instantiation for Type + Function.
+ Instantiation(unsigned int _cInst, CordbType **_ppInst, unsigned int numClassTyPars)
+ : m_cInst(_cInst), m_ppInst(_ppInst),
+ m_cClassTyPars (numClassTyPars)
+ { }
+
+ // Copy constructor.
+ Instantiation(const Instantiation &inst)
+ : m_cInst(inst.m_cInst), m_ppInst(inst.m_ppInst), m_cClassTyPars (inst.m_cClassTyPars)
+ { }
+
+ // Number of elements in array pointed to by m_ppInst
+ unsigned int m_cInst;
+
+ // Pointer to array of CordbType objects. Length of array is m_cInst.
+ // Array is Class Type parameters followed by Function's Type parameters.
+ // Eg, Instantiation for Class<Foo, Goo>::Func<Bar> would be {Foo, Goo, Bar}.
+ // m_cInst = 3, m_cClassTyPars = 2.
+ // In contrast, Instantiation for Class::Func<Foo, Goo, Bar> would have same
+ // array, but m_cClassTyPars = 0.
+ CordbType **m_ppInst;
+
+ // Track the split between Type vs. Method type-params.
+ unsigned int m_cClassTyPars;
+};
+
+//------------------------------------------------------------------------
+// CordbType: replaces the use of signatures.
+//
+// Left Side & Right Side
+// ---------------------------
+// CordbTypes may come from either the Right Side (via being built up from
+// ICorDebug), or from the Left-Side (being handed back from LS operations
+// like getting the type from an Object the LS handed back).
+// The RightSide CordbType corresponds to a Left-Side TypeHandle.
+// CordbTypes are communicated across the LS/RS boundary by marshalling
+// to BasicTypeData + ExpandedTypeData IPC events.
+//
+//
+// Invariants on CordbType
+// ---------------------------
+//
+// The m_elementType is NEVER ELEMENT_TYPE_VAR or ELEMENT_TYPE_MVAR or ELEMENT_TYPE_GENERICINST
+// CordbTypes are always _ground_ types (fully instantiated generics or non-generic types). If
+// they represent an instantiated type like List<int> then m_inst will be non-empty.
+//
+//
+// !!!! The m_elementType is NEVER ELEMENT_TYPE_VALUETYPE !!!!
+// !!!! To find out if it is a value type call CordbType::IsValueType() !!!!
+//
+// Where CordbTypes are stored
+// ---------------------------
+//
+// Because we could have a significant number of different instantiations for a given templated type,
+// we need an efficient way to store and retrieve the CordbType instances for these instantiations.
+// For this reason, we use a tree-like scheme to hash-cons types. To implement this we use the following
+// scheme:
+// - CordbTypes are created for "partially instantiated" types,
+// e.g. CordbTypes exist for "Dict" and "Dict<int>" even if the real
+// type being manipulated by the user is "Dict<int,string>"
+// - Subordinate types (E.g. Dict<int,string> is subordinate to Dict<int>,
+// which is itself subordinate to the type for Dict) get stored
+// in the m_spinetypes hash table of the parent type.
+// - In m_spinetypes the pointers of the CordbTypes themselves
+// are used for the unique ids for entries in the table.
+// Note that CordbType instances that are created for "partially instantiated" types
+// are never used for any purpose other than efficient hashing. Specifically, the debugger will
+// never have reason to expose a partially instantiated type outside of the hashing algorithm.
+//
+// CordbTypes have object identity: if 2 CordbTypes represent the same type (in the same AppDomain),
+// then they will be the same CordbType instance.
+//
+// Thus the representation for "Dict<class String,class Foo, class Foo* >" goes as follows:
+// 1. Assume the type Foo is represented by CordbClass *5678x
+// 1b. Assume the hashtable m_sharedtypes in the AppDomain maps E_T_STRING to the CordbType *0ABCx
+// Assume m_type in class Foo (i.e. CordbClass *5678x) is the CordbType *0DEFx
+// Assume m_type in class Foo maps E_T_PTR to the CordbType *0647x
+// 2. The hash table m_spinetypes in "Dict" maps "0ABCx" to a new CordbType
+// representing Dict<String> (a single type application)
+// 3. The hash table m_spinetypes in this new CordbType maps "0DEFx" to a
+// new CordbType representing Dict<class String,class Foo>
+// 3. The hash table m_spinetypes in this new CordbType maps "0647" to a
+// new CordbType representing Dict<class String,class Foo, class Foo*>
+//
+// This lets us reuse the existing hash table scheme to build
+// up instantiated types of arbitrary size.
+//
+// Array types are similar, excpet that they start with a head type
+// for the "type constructor", e.g. "_ []" is a type constructor with rank 1
+// and m_elementType = ELEMENT_TYPE_SZARRAY. These head constructors are
+// stored in the m_sharedtypes table in the appdomain. The actual instantiations
+// of the array types are then subordinate types to the array constructor type.
+//
+// Other types are simpler, and have unique objects stored in the m_sharedtypes
+// table in the appdomain. This table is indexed by CORDBTYPE_ID in RsType.cpp
+//
+//
+// Memory Management of CordbTypes
+// ---------------------------
+// All CordbTypes are ultimately stored off the CordbAppDomain object.
+// The most common place is in the AppDomain's neuter-list.
+//
+// See definition of ICorDebugType for further invariants on types.
+//
+
+class CordbType : public CordbBase, public ICorDebugType
+{
+public:
+ CordbType(CordbAppDomain *appdomain, CorElementType ty, unsigned int rank);
+ CordbType(CordbAppDomain *appdomain, CorElementType ty, CordbClass *c);
+ CordbType(CordbType *tycon, CordbType *tyarg);
+ virtual ~CordbType();
+ virtual void Neuter();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbType"; }
+#endif
+
+ // If you want to force the init to happen even if we think the class
+ // is up to date, set fForceInit to TRUE
+ HRESULT Init(BOOL fForceInit);
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugType
+ //-----------------------------------------------------------
+
+ COM_METHOD GetType(CorElementType *ty);
+ COM_METHOD GetClass(ICorDebugClass **ppClass);
+ COM_METHOD EnumerateTypeParameters(ICorDebugTypeEnum **ppTyParEnum);
+ COM_METHOD GetFirstTypeParameter(ICorDebugType **ppType);
+ COM_METHOD GetBase(ICorDebugType **ppType);
+ COM_METHOD GetStaticFieldValue(mdFieldDef fieldDef,
+ ICorDebugFrame * pFrame,
+ ICorDebugValue ** ppValue);
+ COM_METHOD GetRank(ULONG32 *pnRank);
+
+ //-----------------------------------------------------------
+ // Non-COM members
+ //-----------------------------------------------------------
+
+ //-----------------------------------------------------------
+ // Basic constructor operations for the algebra of types.
+ // These all create unique objects within an AppDomain.
+ //-----------------------------------------------------------
+
+ // This one is used to create simple types, e.g. int32, int64, typedbyref etc.
+ static HRESULT MkType(CordbAppDomain * pAppDomain,
+ CorElementType elementType,
+ CordbType ** ppResultType);
+
+ // This one is used to create array, pointer and byref types
+ static HRESULT MkType(CordbAppDomain * pAppDomain,
+ CorElementType elementType,
+ ULONG rank,
+ CordbType * pType,
+ CordbType ** ppResultType);
+
+ // This one is used to create function pointer types. et must be ELEMENT_TYPE_FNPTR
+ static HRESULT MkType(CordbAppDomain * pAppDomain,
+ CorElementType elementType,
+ const Instantiation * pInst,
+ CordbType ** ppResultType);
+
+ // This one is used to class and value class types, e.g. "class MyClass" or "class ArrayList<int>"
+ static HRESULT MkType(CordbAppDomain * pAppDomain,
+ CorElementType elementType,
+ CordbClass * pClass,
+ const Instantiation * pInst,
+ CordbType ** ppResultType);
+
+ // Some derived constructors... Use this one if the type is definitely not
+ // a paramterized type, e.g. to implement functions on the API where types cannot
+ // be parameterized.
+ static HRESULT MkUnparameterizedType(CordbAppDomain *appdomain, CorElementType et, CordbClass *cl, CordbType **ppType);
+
+ //-----------------------------------------------------------
+ // Basic destructor operations over the algebra
+ //-----------------------------------------------------------
+ void DestUnaryType(CordbType **pRes) ;
+ void DestConstructedType(CordbClass **pClass, Instantiation *pInst);
+ void DestNaryType(Instantiation *pInst);
+
+ CorElementType GetElementType() { return m_elementType; }
+ VMPTR_DomainFile GetDomainFile();
+ VMPTR_Module GetModule();
+
+ // If this is a ptr type, get the CordbType that it points to.
+ // Eg, for CordbType("Int*"), returns CordbType("Int").
+ // If not a ptr type, returns null.
+ // Since it's all internal, no reference counting.
+ // This is effectively a specialized version of DestUnaryType.
+ CordbType * GetPointerElementType();
+
+
+ // Create a type from metadata
+ static HRESULT SigToType(CordbModule * pModule, SigParser * pSigParser, const Instantiation * pInst, CordbType ** ppResultType);
+
+ // Create a type from from the data received from the left-side
+ static HRESULT TypeDataToType(CordbAppDomain *appdomain, DebuggerIPCE_ExpandedTypeData *data, CordbType **pRes);
+ static HRESULT TypeDataToType(CordbAppDomain *appdomain, DebuggerIPCE_BasicTypeData *data, CordbType **pRes);
+ static HRESULT InstantiateFromTypeHandle(CordbAppDomain * appdomain,
+ VMPTR_TypeHandle vmTypeHandle,
+ CorElementType et,
+ CordbClass * tycon,
+ CordbType ** pRes);
+
+ // Prepare data to send back to left-side during Init() and FuncEval. Fail if the the exact
+ // type data is requested but was not fetched correctly during Init()
+ HRESULT TypeToBasicTypeData(DebuggerIPCE_BasicTypeData *data);
+ void TypeToExpandedTypeData(DebuggerIPCE_ExpandedTypeData *data);
+ void TypeToTypeArgData(DebuggerIPCE_TypeArgData *data);
+
+ void CountTypeDataNodes(unsigned int *count);
+ static void CountTypeDataNodesForInstantiation(unsigned int genericArgsCount, ICorDebugType *genericArgs[], unsigned int *count);
+ static void GatherTypeData(CordbType *type, DebuggerIPCE_TypeArgData **curr_tyargData);
+ static void GatherTypeDataForInstantiation(unsigned int genericArgsCount, ICorDebugType *genericArgs[], DebuggerIPCE_TypeArgData **curr_tyargData);
+
+ HRESULT GetParentType(CordbClass * baseClass, CordbType ** ppRes);
+
+ // These are available after Init() has been called....
+ HRESULT GetUnboxedObjectSize(ULONG32 *res);
+ HRESULT GetFieldInfo(mdFieldDef fldToken, FieldData ** ppFieldData);
+
+ CordbAppDomain *GetAppDomain() { return m_appdomain; }
+
+ bool IsValueType();
+
+ // Is this type a GC-root.
+ bool IsGCRoot();
+
+#ifdef FEATURE_64BIT_ALIGNMENT
+ // checks if the type requires 8-byte alignment.
+ // this is not exposed via ICorDebug at present.
+ HRESULT CordbType::RequiresAlign8(BOOL* isRequired);
+#endif
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+public:
+ // Internal representation of the element type. This may not map exactly to the public element type.
+ // Specifically, m_elementType is NEVER:
+ // ELEMENT_TYPE_VAR, ELEMENT_TYPE_MVAR, ELEMENT_TYPE_GENERICINST,
+ // or ELEMENT_TYPE_VALUETYPE.
+ // To find out if this CordbType corresponds to a value type (instead of Reference type) call CordbType::IsValueType()
+ CorElementType m_elementType;
+
+ // The appdomain that this type lives in. Types (and their type-parameters) are all contained in a single appdomain.
+ // (alhtough the types may be from different modules).
+ // This is valid for all CordbType objects, regardless of m_elementType;
+ CordbAppDomain * m_appdomain;
+
+ // The matching class for this type.
+ // Initially only set for E_T_CLASS, lazily computed for E_T_STRING and E_T_OBJECT if needed
+ CordbClass * m_pClass;
+
+ ULONG m_rank; // Only set for E_T_ARRAY etc.
+
+ // Array of Type Parameters for this Type.
+ Instantiation m_inst;
+
+ // A unique mapping from CordbType objects that are type parameters to CordbType objects. Each mapping
+ // represents the use of the containing type as type constructor. e.g. If the containing type
+ // is CordbType(CordbClass "List") then the table here will map parameters such as (CordbType(CordbClass "String")) to
+ // the constructed type CordbType(CordbClass "List", <CordbType(CordbClass "String")>)
+ // @dbgtodo synchronization - this is currently protected by the Stop-Go lock. Transition to process-lock.
+ CordbSafeHashTable<CordbType> m_spinetypes;
+
+ // Valid after Init(), only for E_T_ARRAY etc.and E_T_CLASS when m_pClass->m_classInfo.m_genericArgsCount > 0.
+ // m_typeHandleExact is the precise Runtime type handle for this type.
+ VMPTR_TypeHandle m_typeHandleExact;
+
+ // Valid after Init(), only for E_T_CLASS, and when m_pClass->m_classInfo.m_genericArgsCount > 0.
+ // May not be set correctly if m_fieldInfoNeedsInit.
+ SIZE_T m_objectSize;
+
+ // DON'T KEEP POINTERS TO ELEMENTS OF m_pFields AROUND!!
+ // This may be deleted if the class gets EnC'd.
+ //
+ // Valid after Init(), only for E_T_CLASS, and when m_pClass->m_classInfo.m_genericArgsCount > 0
+ // All fields will be valid if we have m_typeHandleExact.
+ //
+ // Only some fields will be valid if we have called Init() but still have m_fieldInfoNeedsInit.
+ DacDbiArrayList<FieldData> m_fieldList;
+
+ HRESULT ReturnedByValue();
+
+private:
+ static HRESULT MkTyAppType(CordbAppDomain * pAddDomain,
+ CordbType * pType,
+ const Instantiation * pInst,
+ CordbType ** pResultType);
+
+ BOOL m_fieldInfoNeedsInit;
+
+private:
+ HRESULT InitInstantiationTypeHandle(BOOL fForceInit);
+ HRESULT InitInstantiationFieldInfo(BOOL fForceInit);
+ HRESULT InitStringOrObjectClass(BOOL fForceInit);
+};
+
+/* ------------------------------------------------------------------------- *
+ * Class class
+ * ------------------------------------------------------------------------- */
+
+class CordbClass : public CordbBase, public ICorDebugClass, public ICorDebugClass2
+{
+public:
+ CordbClass(CordbModule* m, mdTypeDef token);
+ virtual ~CordbClass();
+ virtual void Neuter();
+
+ using CordbBase::GetProcess;
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbClass"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugClass
+ //-----------------------------------------------------------
+
+ COM_METHOD GetStaticFieldValue(mdFieldDef fieldDef,
+ ICorDebugFrame *pFrame,
+ ICorDebugValue **ppValue);
+ COM_METHOD GetModule(ICorDebugModule **pModule);
+ COM_METHOD GetToken(mdTypeDef *pTypeDef);
+ //-----------------------------------------------------------
+ // ICorDebugClass2
+ //-----------------------------------------------------------
+ COM_METHOD GetParameterizedType(CorElementType elementType,
+ ULONG32 cTypeArgs,
+ ICorDebugType * rgpTypeArgs[],
+ ICorDebugType ** ppType);
+
+ COM_METHOD SetJMCStatus(BOOL fIsUserCode);
+
+ //-----------------------------------------------------------
+ // Convenience routines and Accessors
+ //-----------------------------------------------------------
+
+ // Helper to get containing module
+ CordbModule * GetModule()
+ {
+ return m_pModule;
+ }
+
+ // get the metadata token for this class
+ mdTypeDef GetToken() { return m_token; }
+
+ // Helper to get the AppDomain the class lives in.
+ CordbAppDomain * GetAppDomain()
+ {
+ return m_pModule->GetAppDomain();
+ }
+
+ // This only very roughly resembles the CLASS_LOAD_LEVEL concept in the VM.
+ // because DBI's needs are far more coarse grained. Also DBI
+ // may contain more, equal, or less information than what is available in
+ // native runtime data structures. We can have less when we are being lazy
+ // and haven't yet fetched it. We can have more if use an independent data
+ // source such as the metadata blob and then compute some type data ourselves
+ typedef enum
+ {
+ // At this state the constructor has been run.
+ // m_module and m_token will be valid
+ Constructed,
+
+ // At this state we have additionally certain to have initialized
+ // m_fIsValueClass and m_fHasTypeParams
+ // Calls to IsValueClass() and HasTypeParams() are valid
+ // This stage should be achievable as long as a runtime type handle
+ // exists, even if it is unrestored
+ BasicInfo,
+
+ //Everything is loaded, or at least anything created lazily from this
+ //point on should be certain to succeed (ie m_type)
+ FullInfo
+ }
+ ClassLoadLevel;
+
+ ClassLoadLevel GetLoadLevel()
+ {
+ return m_loadLevel;
+ }
+
+ // determine if a load event has been sent for this class
+ BOOL LoadEventSent() { return m_fLoadEventSent; }
+
+ // set value of m_fLoadEventSent
+ void SetLoadEventSent(BOOL fEventSent) { m_fLoadEventSent = fEventSent; }
+
+ // determine if the class has been unloaded
+ BOOL HasBeenUnloaded() { return m_fHasBeenUnloaded; }
+
+ // set value of m_fHasBeenUnloaded
+ void SetHasBeenUnloaded(BOOL fUnloaded) { m_fHasBeenUnloaded = (fUnloaded == TRUE); }
+
+ // determine if this is a value class
+ BOOL IsValueClassNoInit() { return m_fIsValueClass; }
+
+ // set value of m_fIsValueClass
+ void SetIsValueClass(BOOL fIsValueClass) { m_fIsValueClass = (fIsValueClass == TRUE); }
+
+ // determine if the value class is known
+ BOOL IsValueClassKnown() { return m_fIsValueClassKnown; }
+
+ // set value of m_fIsValueClassKnown
+ void SetIsValueClassKnown(BOOL fIsValueClassKnown) { m_fIsValueClassKnown = (fIsValueClassKnown == TRUE); }
+
+ // get value of m_type
+ CordbType * GetType() { return m_type; }
+
+ void SetType(CordbType * pType) { m_type.Assign(pType); }
+
+ // get the type parameter count
+ bool HasTypeParams() { _ASSERTE(m_loadLevel >= BasicInfo); return m_fHasTypeParams; }
+
+ // get the object size
+ SIZE_T ObjectSize() { return m_classInfo.m_objectSize; }
+
+ // get the metadata token for this class
+ mdTypeDef MDToken() { return m_token; }
+
+ // get the number of fields
+ unsigned int FieldCount() { return m_classInfo.m_fieldList.Count(); }
+
+ //-----------------------------------------------------------
+ // Functionality shared for CordbType and CordbClass
+ //-----------------------------------------------------------
+
+ static HRESULT SearchFieldInfo(CordbModule * module,
+ DacDbiArrayList<FieldData> * pFieldList,
+ mdTypeDef classToken,
+ mdFieldDef fldToken,
+ FieldData ** ppFieldData);
+
+ static HRESULT GetStaticFieldValue2(CordbModule * pModule,
+ FieldData * pFieldData,
+ BOOL fEnCHangingField,
+ const Instantiation * pInst,
+ ICorDebugFrame * pFrame,
+ ICorDebugValue ** ppValue);
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+
+ // Get information about a field that was added by EnC
+ HRESULT GetEnCHangingField(mdFieldDef fldToken,
+ FieldData ** ppFieldData,
+ CordbObjectValue * pObject);
+
+private:
+ // Get information via the DAC about a field added with Edit and Continue.
+ FieldData * GetEnCFieldFromDac(BOOL fStatic,
+ CordbObjectValue * pObject,
+ mdFieldDef fieldToken);
+
+ // Initialize an instance of EnCHangingFieldInfo.
+ void InitEnCFieldInfo(EnCHangingFieldInfo * pEncField,
+ BOOL fStatic,
+ CordbObjectValue * pObject,
+ mdFieldDef fieldToken,
+ mdTypeDef classToken);
+
+
+public:
+
+ // set or clear the custom notifications flag to control whether we ignore custom debugger notifications
+ void SetCustomNotifications(BOOL fEnable) { m_fCustomNotificationsEnabled = fEnable; }
+ BOOL CustomNotificationsEnabled () { return m_fCustomNotificationsEnabled; }
+
+ HRESULT GetFieldInfo(mdFieldDef fldToken, FieldData ** ppFieldData);
+
+ // If you want to force the init to happen even if we think the class
+ // is up to date, set fForceInit to TRUE
+ void Init(ClassLoadLevel desiredLoadLevel = FullInfo);
+
+ // determine if any fields for a type are unallocated statics
+ BOOL GotUnallocatedStatic(DacDbiArrayList<FieldData> * pFieldList);
+
+ bool IsValueClass();
+ HRESULT GetThisType(const Instantiation * pInst, CordbType ** ppResultType);
+ static HRESULT PostProcessUnavailableHRESULT(HRESULT hr,
+ IMetaDataImport *pImport,
+ mdFieldDef fieldDef);
+ mdTypeDef GetTypeDef() { return (mdTypeDef)m_id; }
+
+#ifdef EnC_SUPPORTED
+ // when we get an added field or method, mark the class to force re-init when we access it
+ void MakeOld()
+ {
+ m_loadLevel = Constructed;
+ }
+#endif // EnC_SUPPORTED
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+private:
+ // contains information about the type: size and
+ // field information
+ ClassInfo m_classInfo;
+
+ ClassLoadLevel m_loadLevel;
+
+ // @dbgtodo managed pipeline - can we get rid of both of these fields?
+ BOOL m_fLoadEventSent;
+ bool m_fHasBeenUnloaded;
+
+ // [m_type] is the type object for when this class is used
+ // as a type. If the class is a value class then it can represent
+ // either the boxed or unboxed type - it depends on the context where the
+ // type is used. For example on a CordbBoxValue it represents the type of the
+ // boxed VC, on a CordbVCObjectValue it represents the type of the unboxed VC.
+ //
+ // The type field starts of NULL as there
+ // is no need to create the type object until it is needed.
+ RSSmartPtr<CordbType> m_type;
+
+ // Module that this Class lives in. Valid at the Constructed type level.
+ CordbModule * m_pModule;
+
+ // the token for the type constructor - m_id cannot be used for constructed types
+ // valid at the Constructed type level
+ mdTypeDef m_token;
+
+ // Whether the class is a VC or not is discovered either by
+ // seeing the class used in a signature after ELEMENT_TYPE_VALUETYPE
+ // or ELEMENT_TYPE_CLASS or by going and asking the EE.
+ bool m_fIsValueClassKnown;
+
+ // Whether the class is a VC or not
+ bool m_fIsValueClass;
+
+ // Whether the class has generic type parameters in its definition
+ bool m_fHasTypeParams;
+
+ // Timestamp from GetProcess()->m_continueCounter, which we can use to tell if
+ // the process has been continued since we last took a snapshot.
+ UINT m_continueCounterLastSync;
+
+ // if we add static fields with EnC after this class is loaded (in the debuggee),
+ // their value will be hung off the FieldDesc. Hold information about such fields here.
+ CordbHangingFieldTable m_hangingFieldsStatic;
+
+ // this indicates whether we should send custom debugger notifications
+ BOOL m_fCustomNotificationsEnabled;
+
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * TypeParameter enumerator class
+ * ------------------------------------------------------------------------- */
+
+class CordbTypeEnum : public CordbBase, public ICorDebugTypeEnum
+{
+public:
+ // Factory method: Create a new instance of this class. Returns NULL on out-of-memory.
+ // On success, returns a new initialized instance of CordbTypeEnum with ref-count 0 (just like a ctor).
+ // the life expectancy of the enumerator varies by caller so we require them to specify the applicable neuter list here.
+ static CordbTypeEnum* Build(CordbAppDomain * pAppDomain, NeuterList * pNeuterList, unsigned int cTypars, CordbType **ppTypars);
+ static CordbTypeEnum* Build(CordbAppDomain * pAppDomain, NeuterList * pNeuterList, unsigned int cTypars, RSSmartPtr<CordbType>*ppTypars);
+
+ virtual ~CordbTypeEnum() ;
+
+ virtual void Neuter();
+
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbTypeEnum"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Skip(ULONG celt);
+ COM_METHOD Reset();
+ COM_METHOD Clone(ICorDebugEnum **ppEnum);
+ COM_METHOD GetCount(ULONG *pcelt);
+
+ //-----------------------------------------------------------
+ // ICorDebugTypeEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Next(ULONG celt, ICorDebugType *Types[], ULONG *pceltFetched);
+
+private:
+ // Private constructor, only partially initializes the object.
+ // Clients should use the 'Build' factory method to create an instance of this class.
+ CordbTypeEnum( CordbAppDomain * pAppDomain, NeuterList * pNeuterList );
+ template<class T> static CordbTypeEnum* BuildImpl(CordbAppDomain * pAppDomain, NeuterList * pNeuterList, unsigned int cTypars, T* ppTypars );
+
+ // Owning object.
+ CordbAppDomain * m_pAppDomain;
+
+ // Array of Types. We own the array, and share refs to the types.
+ // @todo- since these are guaranteed to be kept alive as long as we're not neutered,
+ // we don't need to keep refs to them.
+ RSSmartPtr<CordbType> * m_ppTypars;
+ UINT m_iCurrent;
+ UINT m_iMax;
+};
+
+/* ------------------------------------------------------------------------- *
+ * Code enumerator class
+ * ------------------------------------------------------------------------- */
+
+class CordbCodeEnum : public CordbBase, public ICorDebugCodeEnum
+{
+public:
+ CordbCodeEnum(unsigned int cCode, RSSmartPtr<CordbCode> * ppCode);
+ virtual ~CordbCodeEnum() ;
+
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbCodeEnum"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Skip(ULONG celt);
+ COM_METHOD Reset();
+ COM_METHOD Clone(ICorDebugEnum **ppEnum);
+ COM_METHOD GetCount(ULONG *pcelt);
+
+ //-----------------------------------------------------------
+ // ICorDebugCodeEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Next(ULONG celt, ICorDebugCode *Codes[], ULONG *pceltFetched);
+
+private:
+ // Ptr to an array of CordbCode*
+ // We own the array.
+ RSSmartPtr<CordbCode> * m_ppCodes;
+ UINT m_iCurrent;
+ UINT m_iMax;
+};
+
+
+
+
+
+typedef CUnorderedArray<CordbCode*,11> UnorderedCodeArray;
+//<TODO>@todo port: different SIZE_T size/</TODO>
+const int DMI_VERSION_INVALID = 0;
+const int DMI_VERSION_MOST_RECENTLY_JITTED = 1;
+const int DMI_VERSION_MOST_RECENTLY_EnCED = 2;
+
+
+/* ------------------------------------------------------------------------- *
+ * Function class
+ *
+ * @review . The CordbFunction class now keeps a multiple MethodDescInfo
+ * structures in a hash table indexed by tokens provided by the left-side.
+ * In 99.9% of cases this hash table will only contain one entry - we only
+ * use a hashtable to cover the case where we have multiple JITtings of
+ * a single version of a function, in particular multiple JITtings of generic
+ * code under different instantiations. This will increase space usage.
+ * The way around it is to store one CordbNativeCode in-line in the CordbFunction
+ * class, or at least store one such pointer so no hash table will normally
+ * be needed. This is similar to other cases, e.g. the hash table in
+ * CordbClass used to indicate different CordbTypes made from that class -
+ * again in the normal case these tables will only contain one element.
+ *
+ * However, for the moment I've focused on correctness and we can minimize
+ * this space usage in due course.
+ * ------------------------------------------------------------------------- */
+
+const BOOL bNativeCode = FALSE;
+const BOOL bILCode = TRUE;
+
+//
+// Each E&C version gets its own function object. So the IL that a function
+// is associated w/ does not change.
+// B/C of generics, a single IL function may get jitted multiple times and
+// be associated w/ multiple native code blobs (CordbNativeCode).
+//
+class CordbFunction : public CordbBase, public ICorDebugFunction, public ICorDebugFunction2, public ICorDebugFunction3
+{
+public:
+ //-----------------------------------------------------------
+ // Create from scope and member objects.
+ //-----------------------------------------------------------
+ CordbFunction(CordbModule * m,
+ mdMethodDef token,
+ SIZE_T enCVersion);
+ virtual ~CordbFunction();
+ virtual void Neuter();
+
+
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbFunction"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugFunction
+ //-----------------------------------------------------------
+ COM_METHOD GetModule(ICorDebugModule **pModule);
+ COM_METHOD GetClass(ICorDebugClass **ppClass);
+ COM_METHOD GetToken(mdMethodDef *pMemberDef);
+ COM_METHOD GetILCode(ICorDebugCode **ppCode);
+ COM_METHOD GetNativeCode(ICorDebugCode **ppCode);
+ COM_METHOD CreateBreakpoint(ICorDebugFunctionBreakpoint **ppBreakpoint);
+ COM_METHOD GetLocalVarSigToken(mdSignature *pmdSig);
+ COM_METHOD GetCurrentVersionNumber(ULONG32 *pnCurrentVersion);
+
+ //-----------------------------------------------------------
+ // ICorDebugFunction2
+ //-----------------------------------------------------------
+ COM_METHOD SetJMCStatus(BOOL fIsUserCode);
+ COM_METHOD GetJMCStatus(BOOL * pfIsUserCode);
+ COM_METHOD EnumerateNativeCode(ICorDebugCodeEnum **ppCodeEnum) { return E_NOTIMPL; }
+ COM_METHOD GetVersionNumber(ULONG32 *pnCurrentVersion);
+
+ //-----------------------------------------------------------
+ // ICorDebugFunction3
+ //-----------------------------------------------------------
+ COM_METHOD GetActiveReJitRequestILCode(ICorDebugILCode **ppReJitedILCode);
+
+ //-----------------------------------------------------------
+ // Internal members
+ //-----------------------------------------------------------
+protected:
+ // Returns the function's ILCode and SigToken
+ HRESULT GetILCodeAndSigToken();
+
+ // Get the metadata token for the class to which a function belongs.
+ mdTypeDef InitParentClassOfFunctionHelper(mdToken funcMetaDataToken);
+
+ // Get information about one of the native code blobs for this function
+ HRESULT InitNativeCodeInfo();
+
+public:
+
+ // Get the class to which a given function belongs
+ HRESULT InitParentClassOfFunction();
+
+ void NotifyCodeCreated(CordbNativeCode* nativeCode);
+
+ HRESULT GetSig(SigParser *pMethodSigParser,
+ ULONG *pFunctionArgCount,
+ BOOL *pFunctionIsStatic);
+
+ HRESULT GetArgumentType(DWORD dwIndex, const Instantiation * pInst, CordbType ** ppResultType);
+
+
+ //-----------------------------------------------------------
+ // Internal routines
+ //-----------------------------------------------------------
+
+ // Get the existing IL code object
+ HRESULT GetILCode(CordbILCode ** ppCode);
+
+ // Finds or creates an ILCode for a given rejit request
+ HRESULT LookupOrCreateReJitILCode(VMPTR_SharedReJitInfo vmSharedRejitInfo,
+ CordbReJitILCode** ppILCode);
+
+
+#ifdef EnC_SUPPORTED
+ void MakeOld();
+#endif
+
+ //-----------------------------------------------------------
+ // Accessors
+ //-----------------------------------------------------------
+
+ // Get the AppDomain that this function lives in.
+ CordbAppDomain * GetAppDomain()
+ {
+ return (m_pModule->GetAppDomain());
+ }
+
+ // Get the CordbModule that this Function lives in.
+ CordbModule * GetModule()
+ {
+ return m_pModule;
+ }
+
+ // Get the CordbClass this of which this function is a member
+ CordbClass * GetClass()
+ {
+ return m_pClass;
+ }
+
+ // Get the IL code blob corresponding to this function
+ CordbILCode * GetILCode()
+ {
+ return m_pILCode;
+ }
+
+ // Get metadata token for this function
+ mdMethodDef GetMetadataToken()
+ {
+ return m_MDToken;
+ }
+
+ SIZE_T GetEnCVersionNumber()
+ {
+ return m_dwEnCVersionNumber;
+ }
+
+ CordbFunction * GetPrevVersion()
+ {
+ return m_pPrevVersion;
+ }
+
+ void SetPrevVersion(CordbFunction * prevVersion)
+ {
+ m_pPrevVersion.Assign(prevVersion);
+ }
+
+ typedef enum {kNativeOnly, kHasIL, kUnknownImpl} ImplementationKind;
+ ImplementationKind IsNativeImpl()
+ {
+ return (m_fIsNativeImpl);
+ }
+
+ // determine whether we have a native-only implementation
+ void InitNativeImpl();
+
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+private:
+ // The module that this Function is contained in. It maintains a strong reference to this object
+ // and will neuter this object.
+ CordbModule * m_pModule;
+
+ // The Class that this function is contained in.
+ CordbClass * m_pClass;
+
+ // We only have 1 IL blob associated with a given Function object.
+ RSSmartPtr<CordbILCode> m_pILCode;
+
+
+ // Generics allow a single IL method to be instantiated to multiple native
+ // code blobs. So CordbFunction : CordbNativeCode is 1:n.
+ // This pointer is to arbitrary one of those n code bodies.
+ // Someday we may need to get access to all N of them but not today
+ RSSmartPtr<CordbNativeCode> m_nativeCode;
+
+ // Metadata Token for the IL function. Scoped to m_module.
+ mdMethodDef m_MDToken;
+
+ // EnC version number of this instance
+ SIZE_T m_dwEnCVersionNumber;
+
+ // link to previous version of this function
+ RSSmartPtr<CordbFunction> m_pPrevVersion;
+
+ // Is the function implemented natively in the runtime?? (eg, it has no IL, may be an Ecall/fcall)
+ ImplementationKind m_fIsNativeImpl;
+
+ // True if method signature (argument) values are cached.
+ BOOL m_fCachedMethodValuesValid;
+
+ // Cached SigParser for this Function's argument signature.
+ // Only valid if m_fCachedMethodValuesValid is set.
+ SigParser m_methodSigParserCached;
+
+ // Cached Count of arguments in the argument signature.
+ // Only valid if m_fCachedMethodValuesValid is set.
+ ULONG m_argCountCached;
+
+ // Cached boolean if method is static or instance (part of the argument signature).
+ // Only valid if m_fCachedMethodValuesValid is set.
+ BOOL m_fIsStaticCached;
+
+ // A collection, indexed by VMPTR_SharedReJitInfo, of IL code for rejit requests
+ // The collection is filled lazily by LookupOrCreateReJitILCode
+ CordbSafeHashTable<CordbReJitILCode> m_reJitILCodes;
+};
+
+//-----------------------------------------------------------------------------
+// class CordbCode
+// Represents either IL or Native code blobs associated with a function.
+//
+// See the comments at the ICorDebugCode definition for invariants about Code objects.
+//
+//-----------------------------------------------------------------------------
+class CordbCode : public CordbBase, public ICorDebugCode
+{
+protected:
+ CordbCode(CordbFunction * pFunction, UINT_PTR id, SIZE_T encVersion, BOOL fIsIL);
+
+public:
+ virtual ~CordbCode();
+ virtual void Neuter();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() = 0;
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugCode
+ //-----------------------------------------------------------
+
+ COM_METHOD IsIL(BOOL * pbIL);
+ COM_METHOD GetFunction(ICorDebugFunction ** ppFunction);
+ COM_METHOD GetAddress(CORDB_ADDRESS * pStart) = 0;
+ COM_METHOD GetSize(ULONG32 * pcBytes);
+ COM_METHOD CreateBreakpoint(ULONG32 offset,
+ ICorDebugFunctionBreakpoint ** ppBreakpoint);
+ COM_METHOD GetCode(ULONG32 startOffset, ULONG32 endOffset,
+ ULONG32 cBufferAlloc,
+ BYTE buffer[],
+ ULONG32 * pcBufferSize);
+ COM_METHOD GetVersionNumber( ULONG32 * nVersion);
+ COM_METHOD GetILToNativeMapping(ULONG32 cMap,
+ ULONG32 * pcMap,
+ COR_DEBUG_IL_TO_NATIVE_MAP map[]) = 0;
+ COM_METHOD GetEnCRemapSequencePoints(ULONG32 cMap,
+ ULONG32 * pcMap,
+ ULONG32 offsets[]);
+
+ //-----------------------------------------------------------
+ // Accessors and convenience routines
+ //-----------------------------------------------------------
+
+ // get the CordbFunction instance for this code object
+ CordbFunction * GetFunction();
+
+ // get the actual code bytes for this function
+ virtual HRESULT ReadCodeBytes() = 0;
+
+ // get the size in bytes of this function
+ virtual ULONG32 GetSize() = 0;
+
+
+ // get the metadata token for this code object
+ mdMethodDef GetMetadataToken()
+ {
+ _ASSERTE(m_pFunction != NULL);
+ return (m_pFunction->GetMetadataToken());
+ }
+
+ // get the module this code object belongs to
+ CordbModule * GetModule()
+ {
+ _ASSERTE(m_pFunction != NULL);
+ return (m_pFunction->GetModule());
+ }
+
+ // get the function signature for this code blob or throw on failure
+ void GetSig(SigParser *pMethodSigParser,
+ ULONG *pFunctionArgCount,
+ BOOL *pFunctionIsStatic)
+ {
+ _ASSERTE(m_pFunction != NULL);
+ IfFailThrow(m_pFunction->GetSig(pMethodSigParser, pFunctionArgCount, pFunctionIsStatic));
+ }
+
+ // get the class to which this code blob belongs
+ CordbClass * GetClass()
+ {
+ _ASSERTE(m_pFunction != NULL);
+ return (m_pFunction->GetClass());
+ }
+
+ // Quick helper to get the AppDomain that this code object lives in.
+ CordbAppDomain *GetAppDomain()
+ {
+ _ASSERTE(m_pFunction != NULL);
+ return (m_pFunction->GetAppDomain());
+ }
+
+ // Get the EnC version of this blob
+ SIZE_T GetVersion() { return m_nVersion; };
+
+ // Return true if this is an IL code blob. Else return false.
+ BOOL IsIL() { return m_fIsIL; }
+
+ // convert to CordbNativeCode as long as m_fIsIl is false.
+ CordbNativeCode * AsNativeCode()
+ {
+ _ASSERTE(m_fIsIL == FALSE);
+ return reinterpret_cast<CordbNativeCode *>(this);
+ }
+
+ // convert to CordbILCode as long as m_fIsIl is true.
+ CordbILCode * AsILCode()
+ {
+ _ASSERTE(m_fIsIL == TRUE);
+ return reinterpret_cast<CordbILCode *>(this);
+ }
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+private:
+ UINT m_fIsIL : 1;
+
+ // EnC version number.
+ SIZE_T m_nVersion;
+
+protected:
+ // Our local copy of the code. It will be GetSize() bytes long.
+ BYTE * m_rgbCode; // will be NULL if we can't fit it into memory
+
+ UINT m_continueCounterLastSync;
+
+ // Owning Function associated with this code.
+ CordbFunction * m_pFunction;
+}; //class CordbCode
+
+
+
+
+
+/* ------------------------------------------------------------------------- *
+* CordbILCode class
+* This class represents an IL code blob for a particular EnC version. Thus it is
+* 1:1 with a given instantiation of CordbFunction. Provided functionality includes
+* methods to get the starting address and size of an IL code blob and to read
+* the actual bytes of IL into a buffer.
+ * ------------------------------------------------------------------------- */
+
+class CordbILCode : public CordbCode
+{
+public:
+ // Initialize a new CordbILCode instance
+ CordbILCode(CordbFunction *pFunction, TargetBuffer codeRegionInfo, SIZE_T nVersion, mdSignature localVarSigToken, UINT_PTR id = 0);
+
+#ifdef _DEBUG
+ const char * DbgGetName() { return "CordbILCode"; };
+#endif // _DEBUG
+
+ COM_METHOD GetAddress(CORDB_ADDRESS * pStart);
+ COM_METHOD GetILToNativeMapping(ULONG32 cMap,
+ ULONG32 * pcMap,
+ COR_DEBUG_IL_TO_NATIVE_MAP map[]);
+ // Quick helper for internal access to: GetAddress(CORDB_ADDRESS *pStart);
+ CORDB_ADDRESS GetAddress() { return m_codeRegionInfo.pAddress; }
+
+ // get total size of the IL code
+ ULONG32 GetSize() { return m_codeRegionInfo.cbSize; }
+
+#ifdef EnC_SUPPORTED
+ void MakeOld();
+#endif // EnC_SUPPORTED
+
+ HRESULT GetLocalVarSig(SigParser *pLocalsSigParser, ULONG *pLocalVarCount);
+ HRESULT GetLocalVariableType(DWORD dwIndex, const Instantiation * pInst, CordbType ** ppResultType);
+ mdSignature GetLocalVarSigToken();
+
+private:
+ // Read the actual bytes of IL code into the data member m_rgbCode.
+ // Helper routine for GetCode
+ HRESULT ReadCodeBytes();
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+private:
+#ifdef EnC_SUPPORTED
+ UINT m_fIsOld : 1; // marks this instance as an old EnC version
+ bool m_encBreakpointsApplied;
+#endif
+
+ // derived types can init this
+protected:
+ TargetBuffer m_codeRegionInfo; // stores the starting address and size of the
+ // IL code blob
+
+ // Metadata token for local's signature.
+ mdSignature m_localVarSigToken;
+
+}; // class CordbILCode
+
+/* ------------------------------------------------------------------------- *
+* CordbReJitILCode class
+* This class represents an IL code blob for a particular EnC version and
+* rejitID. Thus it is 1:N with a given instantiation of CordbFunction.
+* ------------------------------------------------------------------------- */
+
+class CordbReJitILCode : public CordbILCode, public ICorDebugILCode, public ICorDebugILCode2
+{
+public:
+ // Initialize a new CordbILCode instance
+ CordbReJitILCode(CordbFunction *pFunction, SIZE_T encVersion, VMPTR_SharedReJitInfo vmSharedReJitInfo);
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+ COM_METHOD QueryInterface(REFIID riid, void** ppInterface);
+
+
+ //-----------------------------------------------------------
+ // ICorDebugILCode
+ //-----------------------------------------------------------
+ COM_METHOD GetEHClauses(ULONG32 cClauses, ULONG32 * pcClauses, CorDebugEHClause clauses[]);
+
+
+ //-----------------------------------------------------------
+ // ICorDebugILCode2
+ //-----------------------------------------------------------
+ COM_METHOD GetLocalVarSigToken(mdSignature *pmdSig);
+ COM_METHOD GetInstrumentedILMap(ULONG32 cMap, ULONG32 *pcMap, COR_IL_MAP map[]);
+
+private:
+ HRESULT Init(DacSharedReJitInfo* pSharedReJitInfo);
+
+private:
+ ULONG32 m_cClauses;
+ NewArrayHolder<CorDebugEHClause> m_pClauses;
+ ULONG32 m_cbLocalIL;
+ NewArrayHolder<BYTE> m_pLocalIL;
+ ULONG32 m_cILMap;
+ NewArrayHolder<COR_IL_MAP> m_pILMap;
+};
+
+/* ------------------------------------------------------------------------- *
+ * CordbNativeCode class. These correspond to MethodDesc's on the left-side.
+ * There may or may not be a DebuggerJitInfo associated with the MethodDesc.
+ * At most one CordbNativeCode is created for each native code compilation of each method
+ * that is seen by the right-side. Note that if each method were JITted only once
+ * then this information could go in CordbFunction, however generics allow
+ * methods to be compiled more than once.
+ *
+ * The purpose of this class is to encapsulate details about a blob of jitted/ngen'ed
+ * code, including an optional set of mappings from IL to offsets in the native Code.
+ * ------------------------------------------------------------------------- */
+
+class CordbNativeCode : public CordbCode, public ICorDebugCode2, public ICorDebugCode3
+{
+public:
+ CordbNativeCode(CordbFunction * pFunction,
+ const NativeCodeFunctionData * pJitData,
+ BOOL fIsInstantiatedGeneric);
+#ifdef _DEBUG
+ const char * DbgGetName() { return "CordbNativeCode"; };
+#endif // _DEBUG
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugCode
+ //-----------------------------------------------------------
+ COM_METHOD GetAddress(CORDB_ADDRESS * pStart);
+ COM_METHOD GetILToNativeMapping(ULONG32 cMap,
+ ULONG32 * pcMap,
+ COR_DEBUG_IL_TO_NATIVE_MAP map[]);
+ //-----------------------------------------------------------
+ // ICorDebugCode2
+ //-----------------------------------------------------------
+ COM_METHOD GetCodeChunks(ULONG32 cbufSize, ULONG32 * pcnumChunks, CodeChunkInfo chunks[]);
+
+ COM_METHOD GetCompilerFlags(DWORD * pdwFlags);
+
+ //-----------------------------------------------------------
+ // ICorDebugCode3
+ //-----------------------------------------------------------
+ COM_METHOD GetReturnValueLiveOffset(ULONG32 ILoffset, ULONG32 bufferSize, ULONG32 *pFetched, ULONG32 *pOffsets);
+
+
+ //-----------------------------------------------------------
+ // Internal members
+ //-----------------------------------------------------------
+
+ HRESULT ILVariableToNative(DWORD dwIndex,
+ SIZE_T ip,
+ const ICorDebugInfo::NativeVarInfo ** ppNativeInfo);
+ void LoadNativeInfo();
+
+ //-----------------------------------------------------------
+ // Accessors and convenience routines
+ //-----------------------------------------------------------
+
+ // get the argument type for a generic
+ void GetArgumentType(DWORD dwIndex,
+ const Instantiation * pInst,
+ CordbType ** ppResultType)
+ {
+ CordbFunction * pFunction = GetFunction();
+ _ASSERTE(pFunction != NULL);
+ IfFailThrow(pFunction->GetArgumentType(dwIndex, pInst, ppResultType));
+ }
+
+ // Quick helper for internall access to: GetAddress(CORDB_ADDRESS *pStart);
+ CORDB_ADDRESS GetAddress() { return m_rgCodeRegions[kHot].pAddress; };
+
+ VMPTR_MethodDesc GetVMNativeCodeMethodDescToken() { return m_vmNativeCodeMethodDescToken; };
+
+ // Worker function for GetReturnValueLiveOffset.
+ HRESULT GetReturnValueLiveOffsetImpl(Instantiation *currentInstantiation, ULONG32 ILoffset, ULONG32 bufferSize, ULONG32 *pFetched, ULONG32 *pOffsets);
+
+ // get total size of the code including both hot and cold regions
+ ULONG32 GetSize();
+
+ // get the size of the cold region(s) only
+ ULONG32 GetColdSize();
+
+ // Return true if the Code is split into hot + cold regions.
+ bool HasColdRegion() { return m_rgCodeRegions[kCold].pAddress != NULL; }
+
+ // Get the number of fixed arguments for this function (the "this"
+ // but not varargs)
+ unsigned int GetFixedArgCount()
+ {
+ return m_nativeVarData.GetFixedArgCount();
+ }
+
+ // Get the number of all arguments for this function
+ // ("this" pointer, fixed args and varargs)
+ ULONG32 GetAllArgsCount()
+ {
+ return m_nativeVarData.GetAllArgsCount();
+ }
+
+ void SetAllArgsCount(ULONG32 count)
+ {
+ m_nativeVarData.SetAllArgsCount(count);
+ }
+
+ // Determine whether this is an instantiation of a generic function
+ BOOL IsInstantiatedGeneric()
+ {
+ return m_fIsInstantiatedGeneric != 0;
+ }
+
+ // Determine whether we have initialized the native variable and
+ // sequence point offsets
+ BOOL IsNativeCodeValid ()
+ {
+ return ((m_nativeVarData.IsInitialized() != 0) &&
+ (m_sequencePoints.IsInitialized() != 0));
+ }
+
+ SequencePoints * GetSequencePoints()
+ {
+ return &m_sequencePoints;
+ }
+
+
+ // Given an ILOffset in the current function, return the class token and function token of the IL call target at that
+ // location. Also fill "methodSig" with the method's signature and "genericSig" with the method's generic signature.
+ HRESULT GetCallSignature(ULONG32 ILOffset, mdToken *pClass, mdToken *pMDFunction, SigParser &methodSig, SigParser &genericSig);
+
+ // Moves a method signature from the start of the signature to the location of the return value (passing out the
+ // number of generic parameters in the method).
+ static HRESULT SkipToReturn(SigParser &parser, ULONG *genArgCount = 0);
+
+private:
+ // Read the actual bytes of native code into the data member m_rgbCode.
+ // Helper routine for GetCode
+ HRESULT ReadCodeBytes();
+
+ // Returns a failure HRESULT if we cannot handle the return value of the given
+ // methodref, methoddef, or methodspec token, otherwise S_OK. Does NOT return S_FALSE;
+ HRESULT EnsureReturnValueAllowed(Instantiation *currentInstantiation, mdToken targetClass, SigParser &parser, SigParser &methodGenerics);
+ HRESULT EnsureReturnValueAllowedWorker(Instantiation *currentInstantiation, mdToken targetClass, SigParser &parser, SigParser &methodGenerics, ULONG genCount);
+
+ // Grabs the appropriate signature parser for a methodref, methoddef, methodspec.
+ HRESULT GetSigParserFromFunction(mdToken mdFunction, mdToken *pClass, SigParser &methodSig, SigParser &genericSig);
+
+ int GetCallInstructionLength(BYTE *buffer, ULONG32 len);
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+private:
+ // offset of the beginning of the last sequence point in the sequence point map
+ SIZE_T m_lastIL;
+
+ // start address(es) and size(s) of hot and cold regions
+ TargetBuffer m_rgCodeRegions[MAX_REGIONS];
+
+ // LS data structure--method desc for this instantiation.
+ VMPTR_MethodDesc m_vmNativeCodeMethodDescToken;
+
+ bool m_fCodeAvailable; // true iff the code has been jitted but not pitched
+
+ bool m_fIsInstantiatedGeneric; // true iff this is an instantiated generic
+
+ // information in the following two classes tracks native offsets and is initialized on demand.
+
+ // location and ID information for local variables. See code:NativeVarData for details.
+ NativeVarData m_nativeVarData;
+
+ // mapping between IL and native code sequence points.
+ SequencePoints m_sequencePoints;
+
+}; //class CordbNativeCode
+
+//---------------------------------------------------------------------------------------
+//
+// GetActiveInternalFramesData is used to enumerate internal frames on a specific thread.
+// It is used in conjunction with code:CordbThread::GetActiveInternalFramesCallback.
+// We store each internal frame in ppInternalFrames as we enumerate them.
+//
+
+struct GetActiveInternalFramesData
+{
+public:
+ // the thread we are walking
+ CordbThread * pThis;
+
+ // an array to store the internal frames
+ RSPtrArray<CordbInternalFrame> pInternalFrames;
+
+ // next element in the array to be filled
+ ULONG32 uIndex;
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Thread classes
+ * ------------------------------------------------------------------------- */
+
+class CordbThread : public CordbBase, public ICorDebugThread,
+ public ICorDebugThread2,
+ public ICorDebugThread3,
+ public ICorDebugThread4
+{
+public:
+ CordbThread(CordbProcess * pProcess, VMPTR_Thread);
+
+ virtual ~CordbThread();
+ virtual void Neuter();
+
+ using CordbBase::GetProcess;
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbThread"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ // there's an external add ref from within RS in CordbEnumFilter
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugThread
+ //-----------------------------------------------------------
+
+ COM_METHOD GetProcess(ICorDebugProcess **ppProcess);
+ COM_METHOD GetID(DWORD *pdwThreadId);
+ COM_METHOD GetHandle(HANDLE * phThreadHandle);
+ COM_METHOD GetAppDomain(ICorDebugAppDomain **ppAppDomain);
+ COM_METHOD SetDebugState(CorDebugThreadState state);
+ COM_METHOD GetDebugState(CorDebugThreadState *pState);
+ COM_METHOD GetUserState(CorDebugUserState *pState);
+ COM_METHOD GetCurrentException(ICorDebugValue ** ppExceptionObject);
+ COM_METHOD ClearCurrentException();
+ COM_METHOD CreateStepper(ICorDebugStepper **ppStepper);
+ COM_METHOD EnumerateChains(ICorDebugChainEnum **ppChains);
+ COM_METHOD GetActiveChain(ICorDebugChain **ppChain);
+ COM_METHOD GetActiveFrame(ICorDebugFrame **ppFrame);
+ COM_METHOD GetRegisterSet(ICorDebugRegisterSet **ppRegisters);
+ COM_METHOD CreateEval(ICorDebugEval **ppEval);
+ COM_METHOD GetObject(ICorDebugValue ** ppObject);
+
+ // ICorDebugThread2
+ COM_METHOD GetConnectionID(CONNID * pConnectionID);
+ COM_METHOD GetTaskID(TASKID * pTaskID);
+ COM_METHOD GetVolatileOSThreadID(DWORD * pdwTID);
+ COM_METHOD GetActiveFunctions(ULONG32 cFunctions, ULONG32 * pcFunctions, COR_ACTIVE_FUNCTION pFunctions[]);
+ // Intercept the current exception at the specified frame. pFrame must be a valid ICDFrame, possibly from
+ // a previous stackwalk.
+ COM_METHOD InterceptCurrentException(ICorDebugFrame * pFrame);
+
+
+
+ // ICorDebugThread3
+ COM_METHOD CreateStackWalk(ICorDebugStackWalk **ppStackWalk);
+
+ COM_METHOD GetActiveInternalFrames(ULONG32 cInternalFrames,
+ ULONG32 * pcInternalFrames,
+ ICorDebugInternalFrame2 * ppInternalFrames[]);
+
+ // ICorDebugThread4
+ COM_METHOD HasUnhandledException();
+
+ COM_METHOD GetBlockingObjects(ICorDebugBlockingObjectEnum **ppBlockingObjectEnum);
+
+ // Gets the current CustomNotification object from the thread or NULL if no such object exists
+ COM_METHOD GetCurrentCustomDebuggerNotification(ICorDebugValue ** ppNotificationObject);
+ //-----------------------------------------------------------
+ // Internal members
+ //-----------------------------------------------------------
+
+ // callback used to enumerate the internal frames on a thread
+ static void GetActiveInternalFramesCallback(const DebuggerIPCE_STRData * pFrameData,
+ void * pUserData);
+
+ CorDebugUserState GetUserState();
+
+ // Given a FramePointer, find the matching CordbFrame.
+ HRESULT FindFrame(ICorDebugFrame ** ppFrame, FramePointer fp);
+
+ // Get the task ID for this thread.
+ TASKID GetTaskID();
+
+ void RefreshStack();
+ void CleanupStack();
+ void MarkStackFramesDirty();
+
+#if !defined(DBG_TARGET_ARM) // @ARMTODO
+
+#if defined(DBG_TARGET_X86)
+ // Converts the values in the floating point register area of the context to real number values.
+ void Get32bitFPRegisters(CONTEXT * pContext);
+
+#elif defined(DBG_TARGET_AMD64) || defined(DBG_TARGET_ARM64)
+ // Converts the values in the floating point register area of the context to real number values.
+ void Get64bitFPRegisters(FPRegister64 * rgContextFPRegisters, int start, int nRegisters);
+#endif // DBG_TARGET_X86
+
+ // Initializes the float state members of this instance of CordbThread. This function gets the context and
+ // converts the floating point values from their context representation to real number values.
+ void LoadFloatState();
+
+#endif //!DBG_TARGET_ARM @ARMTODO
+
+ HRESULT SetIP( bool fCanSetIPOnly,
+ CordbNativeCode * pNativeCode,
+ SIZE_T offset,
+ bool fIsIL );
+
+ // Tells the LS to remap to the latest version of the function
+ HRESULT SetRemapIP(SIZE_T offset);
+
+ // Ask the left-side for the current (up-to-date) AppDomain of this thread's IP.
+ // This should be preferred over using the cached value from GetAppDomain.
+ HRESULT GetCurrentAppDomain(CordbAppDomain ** ppAppDomain);
+
+ //-----------------------------------------------------------
+ // Convenience routines
+ //-----------------------------------------------------------
+
+ // The last domain from which a debug event for this thread was sent.
+ // This usually (but not always) the domain the thread is currently executing in.
+ // Since this is a cache, it may sometimes be out-of-date. I believe all current
+ // usage of this is OK (we pass AppDomains around a lot without really using them),
+ // but no new code should rely on this value.
+ // TODO: eliminate this and the m_pAppDomain field entirely
+ CordbAppDomain *GetAppDomain()
+ {
+ return (m_pAppDomain);
+ }
+
+ DWORD GetVolatileOSThreadID();
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // Get Context
+ //
+ // <TODO>TODO: Since Thread will share the memory with RegisterSets, how
+ // do we know that the RegisterSets have relinquished all pointers
+ // to the m_pContext structure?</TODO>
+ //
+ // Returns: NULL if the thread's CONTEXT structure couldn't be obtained
+ // A pointer to the CONTEXT otherwise.
+ //
+ //
+ //////////////////////////////////////////////////////////////////////////
+ HRESULT GetManagedContext( DT_CONTEXT ** ppContext );
+ HRESULT SetManagedContext( DT_CONTEXT * pContext );
+
+ // API to retrieve the thread handle from the LS.
+ void InternalGetHandle(HANDLE * phThread);
+ void RefreshHandle(HANDLE * phThread);
+
+ // NeuterList that's executed when this Thread's stack is refreshed.
+ // Chain + Frame + some Value enums can be held on this.
+ NeuterList * GetRefreshStackNeuterList()
+ {
+ return &m_RefreshStackNeuterList;
+ }
+
+ DWORD GetUniqueId();
+
+
+ // Hijack a thread at a 2nd-chance exception so that it can execute the CLR's UEF
+ void HijackForUnhandledException();
+
+ // check whether the specified frame lives on the stack of the current thread
+ bool OwnsFrame(CordbFrame *pFrame);
+
+ // Specify that there's an outstanding exception on this thread.
+ void SetExInfo(VMPTR_OBJECTHANDLE vmExcepObjHandle);
+
+ VMPTR_OBJECTHANDLE GetThreadExceptionRawObjectHandle() { return m_vmExcepObjHandle; }
+ bool HasException() { return m_fException; }
+
+ void SetUnhandledNativeException(const EXCEPTION_RECORD * pExceptionRecord);
+ bool HasUnhandledNativeException();
+
+#ifdef _DEBUG
+ // Helper to assert that this thread no longer appears in dac-dbi enumerations
+ void DbgAssertThreadDeleted();
+
+ // Callback for DbgAssertThreadDeleted
+ static void DbgAssertThreadDeletedCallback(VMPTR_Thread vmThread, void * pUserData);
+#endif // _DEBUG
+
+ // Determine if the thread's current exception is managed or unmanaged.
+ BOOL IsThreadExceptionManaged();
+
+ // This is a private hook for the shim to create a CordbRegisterSet for a ShimChain.
+ void CreateCordbRegisterSet(DT_CONTEXT * pContext,
+ BOOL fActive,
+ CorDebugChainReason reason,
+ ICorDebugRegisterSet ** ppRegSet);
+
+ // This is a private hook for the shim to convert an ICDFrame into an ICDInternalFrame for a dynamic
+ // method. Refer to the function header for more information.
+ BOOL ConvertFrameForILMethodWithoutMetadata(ICorDebugFrame * pFrame,
+ ICorDebugInternalFrame2 ** ppInternalFrame2);
+
+ // Gets/sets m_fCreationEventQueued
+ bool CreateEventWasQueued();
+ void SetCreateEventQueued();
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+public:
+ // RS Cache for LS context.
+ // NULL if we haven't allocated memory for a Right side context
+ DT_CONTEXT * m_pContext;
+
+ // Set to the CONTEXT pointer in the LS if this LS thread is
+ // stopped in managed code. This may be either stopped for execution control
+ // (breakpoint / single-step exception) or hijacked w/ a redirected frame because
+ // another thread synced the LS.
+ // This context is used by the RS to set enregistered vars.
+ VMPTR_CONTEXT m_vmLeftSideContext;
+
+ // indicates whether m_pContext is up-to-date
+ bool m_fContextFresh;
+
+ // last domain we've seen this thread.
+ // If the appdomain exits, it will clear out this value.
+ CordbAppDomain *m_pAppDomain;
+
+ // Handle to VM's Thread* object. This is the primary key for a CordbThread object
+ // @dbgtodo ICDThread - merge with m_id;
+ VMPTR_Thread m_vmThreadToken;
+
+ // Unique ID for this thread. See code:CordbThread::GetID for semantics of this field.
+ DWORD m_dwUniqueID;
+
+ CorDebugThreadState m_debugState; // Note that this is for resume
+ // purposes, NOT the current state of
+ // the thread.
+
+ // The frames are all protected under the Stop-Go lock.
+ // This field indicates whether the stack is valid (i.e. no update is necessary).
+ bool m_fFramesFresh;
+
+ // This is a cache of V3 ICDFrames. The cache is only used by two functions:
+ // - code:CordbThread::GetActiveFunctions
+ // - code:CordbThread::InterceptCurrentException.
+ //
+ // We don't clear the cache in CleanupStack() because we don't refresh the cache every time we stop.
+ // Instead, we mark m_fFramesFresh in CleanupStack() and clear the cache only when it is used next time.
+ CDynArray<CordbFrame *> m_stackFrames;
+
+#if !defined(DBG_TARGET_ARM) // @ARMTODO
+ bool m_fFloatStateValid;
+ unsigned int m_floatStackTop;
+ double m_floatValues[DebuggerIPCE_FloatCount];
+#endif // !DBG_TARGET_ARM @ARMTODO
+
+private:
+ // True for the window after an Exception callback, but before it's been continued.
+ // We dispatch two exception events in a row (ICDManagedCallback::Exception and ICDManagedCallback2::Exception),
+ // and a debugger may normally just skip the first one knowing it can stop on the 2nd once.
+ // Both events will set this bit high. Be careful not to reset this bit inbetween them.
+ bool m_fException;
+
+ // True if a creation event has been queued for this thread
+ // The event may or may not have been dispatched yet
+ // Bugfix DevDiv2\DevDiv 77523 - this is only being set from ShimProcess::QueueFakeThreadAttachEventsNativeOrder
+ bool m_fCreationEventQueued;
+
+ // Object handle for Exception object in debuggee.
+ VMPTR_OBJECTHANDLE m_vmExcepObjHandle;
+
+public:
+
+ //Returns true if current user state of a thread is USER_WAIT_SLEEP_JOIN
+ bool IsThreadWaitingOrSleeping();
+
+ // Returns true if the thread is dead. See function header for definition.
+ bool IsThreadDead();
+
+ // Return CORDBG_E_BAD_THREAD_STATE if the thread is dead.
+ HRESULT EnsureThreadIsAlive();
+
+ // On a RemapBreakpoint, the debugger will eventually call RemapFunction and
+ // we need to communicate the IP back to LS. So we stash the address of where
+ // to store the IP here and stuff it in on RemapFunction.
+ // If we're not at an outstanding RemapOpportunity, this will be NULL
+ REMOTE_PTR m_EnCRemapFunctionIP;
+
+private:
+ void ClearStackFrameCache();
+
+ // True iff this thread has an unhandled exception on it.
+ // Set high when Filter() gets noitifed of an unhandled exception.
+ // Set Low if the thread is hijacked.
+ bool m_fHasUnhandledException;
+
+ // Exception record for last unhandled exception on this thread.
+ // Lazily initialized.
+ EXCEPTION_RECORD * m_pExceptionRecord;
+
+ static const CorDebugUserState kInvalidUserState = CorDebugUserState(-1);
+ CorDebugUserState m_userState; // This is the current state of the
+ // thread, at the time that the
+ // left side synchronized
+
+ // NeuterList that's executed when this Thread's stack is refreshed.
+ // This list is for everything related to stackwalking, i.e. everything which is invalidated
+ // if the stack changes in any way. This list is cleared when any of the following is called:
+ // 1) Continue()
+ // 2) SetIP()
+ // 3) RemapFunction()
+ // 4) ICDProcess::SetThreadContext()
+ NeuterList m_RefreshStackNeuterList;
+
+ // The following two data members are used for caching thread handles.
+ // @dbgtodo - Remove in V3 (can't have local handles with data-target abstraction);
+ // offload to the shim to support V2 scenarios.
+ HANDLE m_hCachedThread;
+ HANDLE m_hCachedOutOfProcThread;
+};
+
+/* ------------------------------------------------------------------------- *
+ * StackWalk class
+ * ------------------------------------------------------------------------- */
+
+class CordbStackWalk : public CordbBase, public ICorDebugStackWalk
+{
+public:
+ CordbStackWalk(CordbThread * pCordbThread);
+ virtual ~CordbStackWalk();
+ virtual void Neuter();
+
+ // helper function for Neuter
+ virtual void DeleteAll();
+
+ using CordbBase::GetProcess;
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbStackWalk"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugStackWalk
+ //-----------------------------------------------------------
+
+ COM_METHOD GetContext(ULONG32 contextFlags,
+ ULONG32 contextBufSize,
+ ULONG32 * pContextSize,
+ BYTE pbContextBuf[]);
+ COM_METHOD SetContext(CorDebugSetContextFlag flag, ULONG32 contextSize, BYTE context[]);
+ COM_METHOD Next();
+ COM_METHOD GetFrame(ICorDebugFrame **ppFrame);
+
+ //-----------------------------------------------------------
+ // Internal members
+ //-----------------------------------------------------------
+
+ void SetContextWorker(CorDebugSetContextFlag flag, ULONG32 contextSize, BYTE context[]);
+ HRESULT GetFrameWorker(ICorDebugFrame **ppFrame);
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+public:
+ void Init();
+
+private:
+ // handle legacy V2 hijacking for unhandled hardware exceptions
+ void CheckForLegacyHijackCase();
+
+ // refresh the data for this instance of CordbStackWalk if we have had an IPC event followed by a
+ // continue since we got the information.
+ void RefreshIfNeeded();
+
+ // unwind the frame and update m_context with the new context
+ BOOL UnwindStackFrame();
+
+ // the thread on which this CordbStackWalk is created
+ CordbThread * m_pCordbThread;
+
+ // This is the same iterator used by the runtime itself.
+ IDacDbiInterface::StackWalkHandle m_pSFIHandle;
+
+ // buffers used for stackwalking
+ DT_CONTEXT m_context;
+
+ // Used to figure out if we have to refresh any reference objects
+ // on the left side. We set it to CordbProcess::m_flushCounter on
+ // creation and will check it against that value when we call GetFrame or Next.
+ // If it doesn't match, an IPC event has occurred and the values will need to be
+ // refreshed via the DAC.
+ UINT m_lastSyncFlushCounter;
+
+ // cached flag used for refreshing a CordbStackWalk
+ CorDebugSetContextFlag m_cachedSetContextFlag;
+
+ // We unwind one frame ahead of time to get the FramePointer on x86.
+ // These fields are used for the bookkeeping.
+ RSSmartPtr<CordbFrame> m_pCachedFrame;
+ HRESULT m_cachedHR;
+ bool m_fIsOneFrameAhead;
+};
+
+
+class CordbContext : public CordbBase, public ICorDebugContext
+{
+public:
+
+ CordbContext() : CordbBase(NULL, 0, enumCordbContext) {}
+
+
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbContext"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugContext
+ //-----------------------------------------------------------
+private:
+
+} ;
+
+
+/* ------------------------------------------------------------------------- *
+ * Frame class
+ * ------------------------------------------------------------------------- */
+
+class CordbFrame : public CordbBase, public ICorDebugFrame
+{
+protected:
+ // Ctor to provide dummy frame that just wraps a frame-pointer
+ CordbFrame(CordbProcess * pProcess, FramePointer fp);
+
+public:
+ CordbFrame(CordbThread * pThread,
+ FramePointer fp,
+ SIZE_T ip,
+ CordbAppDomain * pCurrentAppDomain);
+
+ virtual ~CordbFrame();
+ virtual void Neuter();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbFrame"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugFrame
+ //-----------------------------------------------------------
+
+ COM_METHOD GetChain(ICorDebugChain **ppChain);
+
+ // Derived versions of Frame will implement GetCode.
+ COM_METHOD GetCode(ICorDebugCode **ppCode) = 0;
+
+ COM_METHOD GetFunction(ICorDebugFunction **ppFunction);
+ COM_METHOD GetFunctionToken(mdMethodDef *pToken);
+
+ COM_METHOD GetStackRange(CORDB_ADDRESS *pStart, CORDB_ADDRESS *pEnd);
+ COM_METHOD GetCaller(ICorDebugFrame **ppFrame);
+ COM_METHOD GetCallee(ICorDebugFrame **ppFrame);
+ COM_METHOD CreateStepper(ICorDebugStepper **ppStepper);
+
+ //-----------------------------------------------------------
+ // Convenience routines
+ //-----------------------------------------------------------
+
+ CordbAppDomain *GetCurrentAppDomain()
+ {
+ return m_currentAppDomain;
+ }
+
+ // Internal helper to get a CordbFunction for this frame.
+ virtual CordbFunction *GetFunction() = 0;
+
+ FramePointer GetFramePointer()
+ {
+ return m_fp;
+ }
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+ // Accessors to return NULL or typesafe cast to derived frame
+ virtual CordbInternalFrame * GetAsInternalFrame() { return NULL; }
+ virtual CordbNativeFrame * GetAsNativeFrame() { return NULL; }
+
+ // determine if the frame pointer is in the stack range owned by the frame
+ bool IsContainedInFrame(FramePointer fp);
+
+ // This is basically a complicated cast function. We are casting from an ICorDebugFrame to a CordbFrame.
+ static CordbFrame* GetCordbFrameFromInterface(ICorDebugFrame *pFrame);
+
+ virtual const DT_CONTEXT * GetContext() const { return NULL; }
+
+public:
+ // this represents the IL offset for a CordbJITILFrame, the native offset for a CordbNativeFrame,
+ // and 0 for a CordbInternalFrame
+ SIZE_T m_ip;
+
+ CordbThread * m_pThread;
+
+ CordbAppDomain *m_currentAppDomain;
+ FramePointer m_fp;
+
+protected:
+ // indicates whether this frame is the leaf frame; lazily initialized
+ mutable Optional<bool> m_optfIsLeafFrame;
+
+private:
+#ifdef _DEBUG
+ // For tracking down neutering bugs;
+ UINT m_DbgContinueCounter;
+#endif
+};
+
+// Dummy frame that just wraps a frame pointer.
+// This is used to pass a FramePointer back in the Exception2 callback.
+// Currently, the callback passes back an ICorDebugFrame as a way of exposing a cross-platform
+// frame pointer. However passing back an ICDFrame means we need to do a stackwalk, and
+// that may not be possible in V3:
+// - the stackwalk is very chatty, and may be too much work just to give an exception notification.
+// - in 64-bit, we may not even be able to do the stackwalk ourselves.
+//
+// The shim can take the framePointer and do the stackwalk and resolve it to a real frame,
+// so V2 emulation scenarios will continue to work.
+// @dbgtodo exception - resolve this when we iron out exceptions in V3.
+class CordbPlaceholderFrame : public CordbFrame
+{
+public:
+ // Ctor to provide dummy frame that just wraps a frame-pointer
+ CordbPlaceholderFrame(CordbProcess * pProcess, FramePointer fp)
+ : CordbFrame(pProcess, fp)
+ {
+ }
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbFrame"; }
+#endif
+
+ // Provide dummy implementation for some methods. These should never be called.
+ COM_METHOD GetCode(ICorDebugCode **ppCode)
+ {
+ _ASSERTE(!"Don't call this");
+ return E_NOTIMPL;
+ }
+ virtual CordbFunction *GetFunction()
+ {
+ _ASSERTE(!"Don't call this");
+ return NULL;
+ }
+};
+
+class CordbInternalFrame : public CordbFrame, public ICorDebugInternalFrame, public ICorDebugInternalFrame2
+{
+public:
+ CordbInternalFrame(CordbThread * pThread,
+ FramePointer fp,
+ CordbAppDomain * pCurrentAppDomain,
+ const DebuggerIPCE_STRData * pData);
+
+ CordbInternalFrame(CordbThread * pThread,
+ FramePointer fp,
+ CordbAppDomain * pCurrentAppDomain,
+ CorDebugInternalFrameType frameType,
+ mdMethodDef funcMetadataToken,
+ CordbFunction * pFunction,
+ VMPTR_MethodDesc vmMethodDesc);
+
+ virtual void Neuter();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbInternalFrame"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugFrame
+ //-----------------------------------------------------------
+
+ COM_METHOD GetChain(ICorDebugChain **ppChain)
+ {
+ return (CordbFrame::GetChain(ppChain));
+ }
+
+ // We don't expose a code-object for stubs.
+ COM_METHOD GetCode(ICorDebugCode **ppCode)
+ {
+ return CORDBG_E_CODE_NOT_AVAILABLE;
+ }
+
+ COM_METHOD GetFunction(ICorDebugFunction **ppFunction)
+ {
+ return (CordbFrame::GetFunction(ppFunction));
+ }
+ COM_METHOD GetFunctionToken(mdMethodDef *pToken)
+ {
+ return (CordbFrame::GetFunctionToken(pToken));
+ }
+
+ COM_METHOD GetCaller(ICorDebugFrame **ppFrame)
+ {
+ return (CordbFrame::GetCaller(ppFrame));
+ }
+ COM_METHOD GetCallee(ICorDebugFrame **ppFrame)
+ {
+ return (CordbFrame::GetCallee(ppFrame));
+ }
+ COM_METHOD CreateStepper(ICorDebugStepper **ppStepper)
+ {
+ return E_NOTIMPL;
+ }
+
+ COM_METHOD GetStackRange(CORDB_ADDRESS *pStart, CORDB_ADDRESS *pEnd);
+
+ //-----------------------------------------------------------
+ // ICorDebugInternalFrame
+ //-----------------------------------------------------------
+
+ // Get the type of internal frame. This will never be STUBFRAME_NONE.
+ COM_METHOD GetFrameType(CorDebugInternalFrameType * pType)
+ {
+ VALIDATE_POINTER_TO_OBJECT(pType, CorDebugInternalFrameType)
+ *pType = m_eFrameType;
+ return S_OK;
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugInternalFrame2
+ //-----------------------------------------------------------
+
+ COM_METHOD GetAddress(CORDB_ADDRESS * pAddress);
+ COM_METHOD IsCloserToLeaf(ICorDebugFrame * pFrameToCompare,
+ BOOL * pIsCloser);
+
+ BOOL IsCloserToLeafWorker(ICorDebugFrame * pFrameToCompare);
+
+ //-----------------------------------------------------------
+ // Non COM methods
+ //-----------------------------------------------------------
+
+ virtual CordbFunction *GetFunction();
+
+
+ // Accessors to return NULL or typesafe cast to derived frame
+ virtual CordbInternalFrame * GetAsInternalFrame() { return this; }
+
+ // accessor for the shim private hook code:CordbThread::ConvertFrameForILMethodWithoutMetadata
+ BOOL ConvertInternalFrameForILMethodWithoutMetadata(ICorDebugInternalFrame2 ** ppInternalFrame2);
+
+protected:
+ // the frame type
+ CorDebugInternalFrameType m_eFrameType;
+
+ // the method token of the method (if any) associated with the internal frame
+ mdMethodDef m_funcMetadataToken;
+
+ // the method (if any) associated with the internal frame
+ RSSmartPtr<CordbFunction> m_function;
+
+ VMPTR_MethodDesc m_vmMethodDesc;
+};
+
+//---------------------------------------------------------------------------------------
+//
+// This class implements ICorDebugRuntimeUnwindableFrame. It is used to mark a native stack frame
+// which requires special unwinding and which doesn't correspond to any IL code. It is really
+// just a marker to tell the debugger to use the managed unwinder. The debugger is still responsible
+// to do all the inspection and symbol lookup. An example is the hijack stub.
+//
+
+class CordbRuntimeUnwindableFrame : public CordbFrame, public ICorDebugRuntimeUnwindableFrame
+{
+public:
+ CordbRuntimeUnwindableFrame(CordbThread * pThread,
+ FramePointer fp,
+ CordbAppDomain * pCurrentAppDomain,
+ DT_CONTEXT * pContext);
+
+ virtual void Neuter();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbRuntimeUnwindableFrame"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+
+ COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugFrame
+ //-----------------------------------------------------------
+
+ //
+ // Just return E_NOTIMPL for everything.
+ // See the class comment.
+ //
+
+ COM_METHOD GetChain(ICorDebugChain ** ppChain)
+ {
+ return E_NOTIMPL;
+ }
+
+ COM_METHOD GetCode(ICorDebugCode ** ppCode)
+ {
+ return E_NOTIMPL;
+ }
+
+ COM_METHOD GetFunction(ICorDebugFunction ** ppFunction)
+ {
+ return E_NOTIMPL;
+ }
+
+ COM_METHOD GetFunctionToken(mdMethodDef * pToken)
+ {
+ return E_NOTIMPL;
+ }
+
+ COM_METHOD GetCaller(ICorDebugFrame ** ppFrame)
+ {
+ return E_NOTIMPL;
+ }
+
+ COM_METHOD GetCallee(ICorDebugFrame ** ppFrame)
+ {
+ return E_NOTIMPL;
+ }
+
+ COM_METHOD CreateStepper(ICorDebugStepper ** ppStepper)
+ {
+ return E_NOTIMPL;
+ }
+
+ COM_METHOD GetStackRange(CORDB_ADDRESS * pStart, CORDB_ADDRESS * pEnd)
+ {
+ return E_NOTIMPL;
+ }
+
+ //-----------------------------------------------------------
+ // Non COM methods
+ //-----------------------------------------------------------
+
+ virtual CordbFunction * GetFunction()
+ {
+ return NULL;
+ }
+
+ virtual const DT_CONTEXT * GetContext() const;
+
+private:
+ DT_CONTEXT m_context;
+};
+
+
+class CordbValueEnum : public CordbBase, public ICorDebugValueEnum
+{
+public:
+ enum ValueEnumMode {
+ LOCAL_VARS_ORIGINAL_IL,
+ LOCAL_VARS_REJIT_IL,
+ ARGS,
+ } ;
+
+ CordbValueEnum(CordbNativeFrame *frame, ValueEnumMode mode);
+ HRESULT Init();
+ ~CordbValueEnum();
+ virtual void Neuter();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbValueEnum"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Skip(ULONG celt);
+ COM_METHOD Reset();
+ COM_METHOD Clone(ICorDebugEnum **ppEnum);
+ COM_METHOD GetCount(ULONG *pcelt);
+
+ //-----------------------------------------------------------
+ // ICorDebugValueEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Next(ULONG celt, ICorDebugValue *values[], ULONG *pceltFetched);
+
+private:
+ CordbNativeFrame* m_frame;
+ ValueEnumMode m_mode;
+ UINT m_iCurrent;
+ UINT m_iMax;
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Misc Info for the Native Frame class
+ * ------------------------------------------------------------------------- */
+
+struct CordbMiscFrame
+{
+public:
+ CordbMiscFrame();
+
+ // new-style constructor
+ CordbMiscFrame(DebuggerIPCE_JITFuncData * pJITFuncData);
+
+#if defined(DBG_TARGET_WIN64) || defined(DBG_TARGET_ARM)
+ SIZE_T parentIP;
+ FramePointer fpParentOrSelf;
+ bool fIsFilterFunclet;
+#endif // DBG_TARGET_WIN64 || DBG_TARGET_ARM
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Native Frame class
+ * ------------------------------------------------------------------------- */
+
+class CordbNativeFrame : public CordbFrame, public ICorDebugNativeFrame, public ICorDebugNativeFrame2
+{
+public:
+ CordbNativeFrame(CordbThread * pThread,
+ FramePointer fp,
+ CordbNativeCode * pNativeCode,
+ SIZE_T ip,
+ DebuggerREGDISPLAY * pDRD,
+ TADDR addrAmbientESP,
+ bool fQuicklyUnwound,
+ CordbAppDomain * pCurrentAppDomain,
+ CordbMiscFrame * pMisc = NULL,
+ DT_CONTEXT * pContext = NULL);
+ virtual ~CordbNativeFrame();
+ virtual void Neuter();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbNativeFrame"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugFrame
+ //-----------------------------------------------------------
+
+ COM_METHOD GetChain(ICorDebugChain **ppChain)
+ {
+ return (CordbFrame::GetChain(ppChain));
+ }
+ COM_METHOD GetCode(ICorDebugCode **ppCode);
+ COM_METHOD GetFunction(ICorDebugFunction **ppFunction)
+ {
+ return (CordbFrame::GetFunction(ppFunction));
+ }
+ COM_METHOD GetFunctionToken(mdMethodDef *pToken)
+ {
+ return (CordbFrame::GetFunctionToken(pToken));
+ }
+ COM_METHOD GetCaller(ICorDebugFrame **ppFrame)
+ {
+ return (CordbFrame::GetCaller(ppFrame));
+ }
+ COM_METHOD GetCallee(ICorDebugFrame **ppFrame)
+ {
+ return (CordbFrame::GetCallee(ppFrame));
+ }
+ COM_METHOD CreateStepper(ICorDebugStepper **ppStepper)
+ {
+ return (CordbFrame::CreateStepper(ppStepper));
+ }
+
+ COM_METHOD GetStackRange(CORDB_ADDRESS *pStart, CORDB_ADDRESS *pEnd);
+
+ //-----------------------------------------------------------
+ // ICorDebugNativeFrame
+ //-----------------------------------------------------------
+
+ COM_METHOD GetIP(ULONG32* pnOffset);
+ COM_METHOD SetIP(ULONG32 nOffset);
+ COM_METHOD GetRegisterSet(ICorDebugRegisterSet **ppRegisters);
+ COM_METHOD GetLocalRegisterValue(CorDebugRegister reg,
+ ULONG cbSigBlob,
+ PCCOR_SIGNATURE pvSigBlob,
+ ICorDebugValue ** ppValue);
+
+ COM_METHOD GetLocalDoubleRegisterValue(CorDebugRegister highWordReg,
+ CorDebugRegister lowWordReg,
+ ULONG cbSigBlob,
+ PCCOR_SIGNATURE pvSigBlob,
+ ICorDebugValue ** ppValue);
+
+ COM_METHOD GetLocalMemoryValue(CORDB_ADDRESS address,
+ ULONG cbSigBlob,
+ PCCOR_SIGNATURE pvSigBlob,
+ ICorDebugValue ** ppValue);
+
+ COM_METHOD GetLocalRegisterMemoryValue(CorDebugRegister highWordReg,
+ CORDB_ADDRESS lowWordAddress,
+ ULONG cbSigBlob,
+ PCCOR_SIGNATURE pvSigBlob,
+ ICorDebugValue ** ppValue);
+
+ COM_METHOD GetLocalMemoryRegisterValue(CORDB_ADDRESS highWordAddress,
+ CorDebugRegister lowWordRegister,
+ ULONG cbSigBlob,
+ PCCOR_SIGNATURE pvSigBlob,
+ ICorDebugValue ** ppValue);
+
+ COM_METHOD CanSetIP(ULONG32 nOffset);
+
+ //-----------------------------------------------------------
+ // ICorDebugNativeFrame2
+ //-----------------------------------------------------------
+
+ COM_METHOD IsChild(BOOL * pIsChild);
+
+ COM_METHOD IsMatchingParentFrame(ICorDebugNativeFrame2 *pPotentialParentFrame,
+ BOOL * pIsParent);
+
+ COM_METHOD GetStackParameterSize(ULONG32 * pSize);
+
+ //-----------------------------------------------------------
+ // Non-COM members
+ //-----------------------------------------------------------
+
+ // Accessors to return NULL or typesafe cast to derived frame
+ virtual CordbNativeFrame * GetAsNativeFrame() { return this; }
+
+ CordbFunction * GetFunction();
+ CordbNativeCode * GetNativeCode();
+ virtual const DT_CONTEXT * GetContext() const;
+
+ // Given the native variable information of a variable, return its value.
+ // This function assumes that the value is either in a register or on the stack
+ // (i.e. VLT_REG or VLT_STK).
+ SIZE_T GetRegisterOrStackValue(const ICorDebugInfo::NativeVarInfo * pNativeVarInfo);
+
+ HRESULT GetLocalRegisterValue(CorDebugRegister reg,
+ CordbType * pType,
+ ICorDebugValue **ppValue);
+ HRESULT GetLocalDoubleRegisterValue(CorDebugRegister highWordReg,
+ CorDebugRegister lowWordReg,
+ CordbType * pType,
+ ICorDebugValue **ppValue);
+ HRESULT GetLocalMemoryValue(CORDB_ADDRESS address,
+ CordbType * pType,
+ ICorDebugValue **ppValue);
+ HRESULT GetLocalByRefMemoryValue(CORDB_ADDRESS address,
+ CordbType * pType,
+ ICorDebugValue **ppValue);
+ HRESULT GetLocalRegisterMemoryValue(CorDebugRegister highWordReg,
+ CORDB_ADDRESS lowWordAddress,
+ CordbType * pType,
+ ICorDebugValue **ppValue);
+ HRESULT GetLocalMemoryRegisterValue(CORDB_ADDRESS highWordAddress,
+ CorDebugRegister lowWordRegister,
+ CordbType * pType,
+ ICorDebugValue **ppValue);
+ UINT_PTR * GetAddressOfRegister(CorDebugRegister regNum) const;
+ CORDB_ADDRESS GetLeftSideAddressOfRegister(CorDebugRegister regNum) const;
+#if !defined(DBG_TARGET_ARM) // @ARMTODO
+ HRESULT GetLocalFloatingPointValue(DWORD index,
+ CordbType * pType,
+ ICorDebugValue **ppValue);
+#endif // !DBG_TARGET_ARM @ARMTODO
+
+
+ CORDB_ADDRESS GetLSStackAddress(ICorDebugInfo::RegNum regNum, signed offset);
+
+ bool IsLeafFrame() const;
+
+ // Return the offset used for inspection purposes.
+ // Refer to the comment at the beginning of the function definition in RsThread.cpp for more information.
+ SIZE_T GetInspectionIP();
+
+ ULONG32 GetIPOffset();
+
+ // whether this is a funclet frame
+ bool IsFunclet();
+ bool IsFilterFunclet();
+
+#if defined(DBG_TARGET_WIN64) || defined(DBG_TARGET_ARM)
+ // return the offset of the parent method frame at which an exception occurs
+ SIZE_T GetParentIP();
+#endif // DBG_TARGET_WIN64 || DBG_TARGET_ARM
+
+ TADDR GetAmbientESP() { return m_taAmbientESP; }
+ TADDR GetReturnRegisterValue();
+
+ // accessor for the shim private hook code:CordbThread::ConvertFrameForILMethodWithoutMetadata
+ BOOL ConvertNativeFrameForILMethodWithoutMetadata(ICorDebugInternalFrame2 ** ppInternalFrame2);
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+public:
+ // the register set
+ DebuggerREGDISPLAY m_rd;
+
+ // This field is only true for Enter-Managed chain. It means that the register set is invalid.
+ bool m_quicklyUnwound;
+
+ // each CordbNativeFrame corresponds to exactly one CordbJITILFrame and one CordbNativeCode
+ RSSmartPtr<CordbJITILFrame> m_JITILFrame;
+ RSSmartPtr<CordbNativeCode> m_nativeCode;
+
+ // auxiliary information only used on 64-bit to find the parent stack pointer and offset for funclets
+ CordbMiscFrame m_misc;
+
+private:
+ // the ambient SP value only used on x86 to retrieve sp-relative local variables
+ // (most likely in a frameless method)
+ TADDR m_taAmbientESP;
+
+ // @dbgtodo inspection - When we DACize the various Cordb*Value classes, we should consider getting rid of the
+ // DebuggerREGDISPLAY and just use the CONTEXT. A lot of simplification can be done here.
+ DT_CONTEXT m_context;
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * CordbRegisterSet class
+ *
+ * This can be obtained via GetRegisterSet from
+ * CordbNativeFrame
+ * CordbThread
+ *
+ * ------------------------------------------------------------------------- */
+
+#define SETBITULONG64( x ) ( (ULONG64)1 << (x) )
+#define SET_BIT_MASK(_mask, _reg) (_mask[(_reg) >> 3] |= (1 << ((_reg) & 7)))
+#define RESET_BIT_MASK(_mask, _reg) (_mask[(_reg) >> 3] &= ~(1 << ((_reg) & 7)))
+#define IS_SET_BIT_MASK(_mask, _reg) (_mask[(_reg) >> 3] & (1 << ((_reg) & 7)))
+
+
+class CordbRegisterSet : public CordbBase, public ICorDebugRegisterSet, public ICorDebugRegisterSet2
+{
+public:
+ CordbRegisterSet(DebuggerREGDISPLAY * pRegDisplay,
+ CordbThread * pThread,
+ bool fActive,
+ bool fQuickUnwind,
+ bool fTakeOwnershipOfDRD = false);
+
+
+ ~CordbRegisterSet();
+
+
+
+ virtual void Neuter();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbRegisterSet"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRefEnforceExternal());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseReleaseEnforceExternal());
+ }
+
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+
+
+ //-----------------------------------------------------------
+ // ICorDebugRegisterSet
+ // More extensive explanation are in Src/inc/CorDebug.idl
+ //-----------------------------------------------------------
+ COM_METHOD GetRegistersAvailable(ULONG64 *pAvailable);
+
+ COM_METHOD GetRegisters(ULONG64 mask,
+ ULONG32 regCount,
+ CORDB_REGISTER regBuffer[]);
+ COM_METHOD SetRegisters( ULONG64 mask,
+ ULONG32 regCount,
+ CORDB_REGISTER regBuffer[])
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER,
+ regCount, true, true);
+
+ return E_NOTIMPL;
+ }
+
+ COM_METHOD GetThreadContext(ULONG32 contextSize, BYTE context[]);
+
+ // SetThreadContexthad a very problematic implementation in v1.1.
+ // We've ripped it out in V2.0 and E_NOTIMPL it. See V1.1 sources for what it used to look like
+ // in case we ever want to re-add it.
+ // If we ever re-implement it consider the following:
+ // - must fail on non-leaf frames (just check m_active).
+ // - must make sure that GetThreadContext() is fully accurate. If we don't have SetThCtx, then
+ // GetThreadCtx bugs are much more benign.
+ // - be sure to update any shared reg displays (what if a frame + chain have the same rd) and
+ // also update any cached contexts (such as CordbThread::m_context).
+ // - be sure to honor the context flags and only setting what we can set.
+ //
+ // Friday, July 16, 2004. (This date will be useful for Source control history)
+ COM_METHOD SetThreadContext(ULONG32 contextSize, BYTE context[])
+ {
+ return E_NOTIMPL;
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugRegisterSet2
+ // More extensive explanation are in Src/inc/CorDebug.idl
+ //-----------------------------------------------------------
+ COM_METHOD GetRegistersAvailable(ULONG32 regCount,
+ BYTE pAvailable[]);
+
+ COM_METHOD GetRegisters(ULONG32 maskCount,
+ BYTE mask[],
+ ULONG32 regCount,
+ CORDB_REGISTER regBuffer[]);
+
+ COM_METHOD SetRegisters(ULONG32 maskCount,
+ BYTE mask[],
+ ULONG32 regCount,
+ CORDB_REGISTER regBuffer[])
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER,
+ regCount, true, true);
+
+ return E_NOTIMPL;
+ }
+
+protected:
+ // Platform specific helper for GetThreadContext.
+ void InternalCopyRDToContext(DT_CONTEXT * pContext);
+
+ // Adapters to impl v2.0 interfaces on top of v1.0 interfaces.
+ HRESULT GetRegistersAvailableAdapter(ULONG32 regCount, BYTE pAvailable[]);
+ HRESULT GetRegistersAdapter(ULONG32 maskCount, BYTE mask[], ULONG32 regCount, CORDB_REGISTER regBuffer[]);
+
+
+ // This CordbRegisterSet is responsible to free this memory if m_fTakeOwnershipOfDRD is true. Otherwise,
+ // this memory is freed by the CordbNativeFrame or CordbThread which creates this CordbRegisterSet.
+ DebuggerREGDISPLAY *m_rd;
+ CordbThread *m_thread;
+ bool m_active; // true if we're the leafmost register set.
+ bool m_quickUnwind;
+
+ // true if the CordbRegisterSet owns the DebuggerREGDISPLAY pointer and needs to free the memory
+ bool m_fTakeOwnershipOfDRD;
+} ;
+
+
+
+
+/* ------------------------------------------------------------------------- *
+ * JIT-IL Frame class
+ * ------------------------------------------------------------------------- */
+
+class CordbJITILFrame : public CordbBase, public ICorDebugILFrame, public ICorDebugILFrame2, public ICorDebugILFrame3, public ICorDebugILFrame4
+{
+public:
+ CordbJITILFrame(CordbNativeFrame * pNativeFrame,
+ CordbILCode * pCode,
+ UINT_PTR ip,
+ CorDebugMappingResult mapping,
+ GENERICS_TYPE_TOKEN exactGenericArgsToken,
+ DWORD dwExactGenericArgsTokenIndex,
+ bool fVarArgFnx,
+ CordbReJitILCode * pReJitCode);
+ HRESULT Init();
+ virtual ~CordbJITILFrame();
+ virtual void Neuter();
+
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbJITILFrame"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugFrame
+ //-----------------------------------------------------------
+
+ COM_METHOD GetChain(ICorDebugChain **ppChain);
+ COM_METHOD GetCode(ICorDebugCode **ppCode);
+ COM_METHOD GetFunction(ICorDebugFunction **ppFunction);
+ COM_METHOD GetFunctionToken(mdMethodDef *pToken);
+ COM_METHOD GetStackRange(CORDB_ADDRESS *pStart, CORDB_ADDRESS *pEnd);
+ COM_METHOD CreateStepper(ICorDebugStepper **ppStepper);
+ COM_METHOD GetCaller(ICorDebugFrame **ppFrame);
+ COM_METHOD GetCallee(ICorDebugFrame **ppFrame);
+
+ //-----------------------------------------------------------
+ // ICorDebugILFrame
+ //-----------------------------------------------------------
+
+ COM_METHOD GetIP(ULONG32* pnOffset, CorDebugMappingResult *pMappingResult);
+ COM_METHOD SetIP(ULONG32 nOffset);
+ COM_METHOD EnumerateLocalVariables(ICorDebugValueEnum **ppValueEnum);
+ COM_METHOD GetLocalVariable(DWORD dwIndex, ICorDebugValue **ppValue);
+ COM_METHOD EnumerateArguments(ICorDebugValueEnum **ppValueEnum);
+ COM_METHOD GetArgument(DWORD dwIndex, ICorDebugValue ** ppValue);
+ COM_METHOD GetStackDepth(ULONG32 *pDepth);
+ COM_METHOD GetStackValue(DWORD dwIndex, ICorDebugValue **ppValue);
+ COM_METHOD CanSetIP(ULONG32 nOffset);
+
+ //-----------------------------------------------------------
+ // ICorDebugILFrame2
+ //-----------------------------------------------------------
+
+ // Called at an EnC remap opportunity to remap to the latest version of a function
+ COM_METHOD RemapFunction(ULONG32 nOffset);
+
+ COM_METHOD EnumerateTypeParameters(ICorDebugTypeEnum **ppTyParEnum);
+
+ //-----------------------------------------------------------
+ // ICorDebugILFrame3
+ //-----------------------------------------------------------
+
+ COM_METHOD GetReturnValueForILOffset(ULONG32 ILoffset, ICorDebugValue** ppReturnValue);
+
+ //-----------------------------------------------------------
+ // ICorDebugILFrame4
+ //-----------------------------------------------------------
+
+ COM_METHOD EnumerateLocalVariablesEx(ILCodeKind flags, ICorDebugValueEnum **ppValueEnum);
+ COM_METHOD GetLocalVariableEx(ILCodeKind flags, DWORD dwIndex, ICorDebugValue **ppValue);
+ COM_METHOD GetCodeEx(ILCodeKind flags, ICorDebugCode **ppCode);
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+
+ CordbModule *GetModule();
+
+ HRESULT GetNativeVariable(CordbType *type,
+ const ICorDebugInfo::NativeVarInfo *pNativeVarInfo,
+ ICorDebugValue **ppValue);
+
+ CordbAppDomain *GetCurrentAppDomain();
+
+ CordbFunction *GetFunction();
+
+ // ILVariableToNative serves to let the frame intercept accesses
+ // to var args variables.
+ HRESULT ILVariableToNative(DWORD dwIndex,
+ const ICorDebugInfo::NativeVarInfo ** ppNativeInfo);
+
+ // Fills in our array of var args variables
+ HRESULT FabricateNativeInfo(DWORD dwIndex,
+ const ICorDebugInfo::NativeVarInfo ** ppNativeInfo);
+
+ HRESULT GetArgumentType(DWORD dwIndex,
+ CordbType ** ppResultType);
+
+ // load the generics type and method arguments into a cache
+ void LoadGenericArgs();
+
+ HRESULT QueryInterfaceInternal(REFIID id, void** pInterface);
+
+ // Builds an generic Instaniation object from the mdClass and generic signature
+ // for what we are calling into.
+ static HRESULT BuildInstantiationForCallsite(CordbModule *pModule, NewArrayHolder<CordbType*> &types, Instantiation &inst, Instantiation *currentInstantiation, mdToken targetClass, SigParser funcGenerics);
+
+ CordbILCode* GetOriginalILCode();
+ CordbReJitILCode* GetReJitILCode();
+
+private:
+ void RefreshCachedVarArgSigParserIfNeeded();
+
+ // Worker function for GetReturnValueForILOffset.
+ HRESULT GetReturnValueForILOffsetImpl(ULONG32 ILoffset, ICorDebugValue** ppReturnValue);
+
+ // Given pType, fills ppReturnValue with the correct value.
+ HRESULT GetReturnValueForType(CordbType *pType, ICorDebugValue **ppReturnValue);
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+public:
+ // each CordbJITILFrame corresponds to exactly one CordbNativeFrame and one CordbILCode
+ CordbNativeFrame * m_nativeFrame;
+ CordbILCode * m_ilCode;
+
+ // the IL offset and the mapping result for the offset
+ UINT_PTR m_ip;
+ CorDebugMappingResult m_mapping;
+
+ // <vararg-specific fields>
+
+ // whether this is a vararg function
+ bool m_fVarArgFnx;
+
+ // the number of arguments, including the var args
+ ULONG m_allArgsCount;
+
+ // This byte array is used to store the signature for vararg methods.
+ // It points to the underlying memory used by m_sigParserCached, and it enables us to easily delete
+ // the underlying memory when the CordbJITILFrame is neutered.
+ BYTE * m_rgbSigParserBuf;
+
+ // Do not mutate this, instead make copies of it and use the copies, that way we are guaranteed to
+ // start at the correct position in the signature each time.
+ // The underlying memory used for the signature in the SigParser must not be in the DAC cache.
+ // Otherwise it may be flushed underneath us, and we would AV when we try to access it.
+ SigParser m_sigParserCached;
+
+ // the address of the first arg; only used for vararg functions
+ CORDB_ADDRESS m_FirstArgAddr;
+
+ // This is an array of variable information for the arguments.
+ // The variable information is fabricated by the RS.
+ ICorDebugInfo::NativeVarInfo * m_rgNVI;
+
+ // </vararg-specific fields>
+
+ Instantiation m_genericArgs; // the generics type arguments
+ BOOL m_genericArgsLoaded; // whether we have loaded and cached the generics type arguments
+
+ // An extra token to help fetch information about any generic
+ // parameters passed to the method, perhaps dynamically.
+ // This is the so-called generics type context/token.
+ //
+ // This token comes from the stackwalker and it may be NULL, in which case we need to retrieve the token
+ // ourselves using m_dwFrameParamsTokenIndex and the variable lifetime information.
+ GENERICS_TYPE_TOKEN m_frameParamsToken;
+
+ // IL Variable index of the Generics Arg Token.
+ DWORD m_dwFrameParamsTokenIndex;
+
+ // if this frame is instrumented with rejit, this will point to the instrumented IL code
+ RSSmartPtr<CordbReJitILCode> m_pReJitCode;
+};
+
+/* ------------------------------------------------------------------------- *
+ * Breakpoint class
+ * ------------------------------------------------------------------------- */
+
+enum CordbBreakpointType
+{
+ CBT_FUNCTION,
+ CBT_MODULE,
+ CBT_VALUE
+};
+
+class CordbBreakpoint : public CordbBase, public ICorDebugBreakpoint
+{
+public:
+ CordbBreakpoint(CordbProcess * pProcess, CordbBreakpointType bpType);
+ virtual void Neuter();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbBreakpoint"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugBreakpoint
+ //-----------------------------------------------------------
+
+ COM_METHOD BaseIsActive(BOOL *pbActive);
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+ CordbBreakpointType GetBPType()
+ {
+ return m_type;
+ }
+
+ virtual void Disconnect() {}
+
+ CordbAppDomain *GetAppDomain()
+ {
+ return m_pAppDomain;
+ }
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+public:
+ bool m_active;
+ CordbAppDomain *m_pAppDomain;
+ CordbBreakpointType m_type;
+};
+
+/* ------------------------------------------------------------------------- *
+ * Function Breakpoint class
+ * ------------------------------------------------------------------------- */
+
+class CordbFunctionBreakpoint : public CordbBreakpoint,
+ public ICorDebugFunctionBreakpoint
+{
+public:
+ CordbFunctionBreakpoint(CordbCode *code, SIZE_T offset);
+ ~CordbFunctionBreakpoint();
+
+ virtual void Neuter();
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbFunctionBreakpoint"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugBreakpoint
+ //-----------------------------------------------------------
+
+ COM_METHOD GetFunction(ICorDebugFunction **ppFunction);
+ COM_METHOD GetOffset(ULONG32 *pnOffset);
+ COM_METHOD Activate(BOOL bActive);
+ COM_METHOD IsActive(BOOL *pbActive)
+ {
+ VALIDATE_POINTER_TO_OBJECT(pbActive, BOOL *);
+
+ return BaseIsActive(pbActive);
+ }
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+
+ void Disconnect();
+
+ //-----------------------------------------------------------
+ // Convenience routines
+ //-----------------------------------------------------------
+
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+ // Get a point to the LS BP object.
+ LSPTR_BREAKPOINT GetLsPtrBP();
+public:
+
+ // We need to have a strong pointer because we may access the m_code object after we're neutered.
+ // @todo - use external pointer b/c Breakpoints aren't yet rooted, and so this reference could be
+ // leaked.
+ RSExtSmartPtr<CordbCode> m_code;
+ SIZE_T m_offset;
+};
+
+/* ------------------------------------------------------------------------- *
+ * Module Breakpoint class
+ * ------------------------------------------------------------------------- */
+
+class CordbModuleBreakpoint : public CordbBreakpoint,
+ public ICorDebugModuleBreakpoint
+{
+public:
+ CordbModuleBreakpoint(CordbModule *pModule);
+
+
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbModuleBreakpoint"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugModuleBreakpoint
+ //-----------------------------------------------------------
+
+ COM_METHOD GetModule(ICorDebugModule **ppModule);
+ COM_METHOD Activate(BOOL bActive);
+ COM_METHOD IsActive(BOOL *pbActive)
+ {
+ VALIDATE_POINTER_TO_OBJECT(pbActive, BOOL *);
+
+ return BaseIsActive(pbActive);
+ }
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+
+ void Disconnect();
+
+public:
+ CordbModule *m_module;
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Stepper class
+ * ------------------------------------------------------------------------- */
+
+class CordbStepper : public CordbBase, public ICorDebugStepper, public ICorDebugStepper2
+{
+public:
+ CordbStepper(CordbThread *thread, CordbFrame *frame = NULL);
+
+
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbStepper"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugStepper
+ //-----------------------------------------------------------
+
+ COM_METHOD IsActive(BOOL *pbActive);
+ COM_METHOD Deactivate();
+ COM_METHOD SetInterceptMask(CorDebugIntercept mask);
+ COM_METHOD SetUnmappedStopMask(CorDebugUnmappedStop mask);
+ COM_METHOD Step(BOOL bStepIn);
+ COM_METHOD StepRange(BOOL bStepIn,
+ COR_DEBUG_STEP_RANGE ranges[],
+ ULONG32 cRangeCount);
+ COM_METHOD StepOut();
+ COM_METHOD SetRangeIL(BOOL bIL);
+
+ //-----------------------------------------------------------
+ // ICorDebugStepper2
+ //-----------------------------------------------------------
+ COM_METHOD SetJMC(BOOL fIsJMCStepper);
+
+ //-----------------------------------------------------------
+ // Convenience routines
+ //-----------------------------------------------------------
+
+ CordbAppDomain *GetAppDomain()
+ {
+ return (m_thread->GetAppDomain());
+ }
+
+ LSPTR_STEPPER GetLsPtrStepper();
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+ CordbThread *m_thread;
+ CordbFrame *m_frame;
+ REMOTE_PTR m_stepperToken;
+ bool m_active;
+ bool m_rangeIL;
+ bool m_fIsJMCStepper;
+ CorDebugUnmappedStop m_rgfMappingStop;
+ CorDebugIntercept m_rgfInterceptStop;
+};
+
+#define REG_SIZE sizeof(SIZE_T)
+
+// class RegisterInfo: encapsulates information necessary to identify and access a specific register in a
+// register display
+class RegisterInfo
+{
+public:
+ // constructor for an instance of RegisterInfo
+ // Arguments:
+ // input: kNumber - value from CorDebugRegister to identify the register
+ // addr - address in remote register display that holds the value
+ // output: no out parameters, but this instance of RegisterInfo has been initialized
+ RegisterInfo(const CorDebugRegister kNumber, CORDB_ADDRESS addr, SIZE_T value):
+ m_kRegNumber((CorDebugRegister)kNumber),
+ m_regAddr(addr),
+ m_regValue(value)
+ {};
+
+
+ // copy constructor
+ // Arguments:
+ // input: regInfo - register info from which the values for this instance will come
+ // output: no out parameters, but this instance of RegisterInfo has been initialized
+ RegisterInfo(const RegisterInfo * pRegInfo):
+ m_kRegNumber(pRegInfo->m_kRegNumber),
+ m_regAddr(pRegInfo->m_regAddr),
+ m_regValue(pRegInfo->m_regValue)
+ {};
+
+
+ //-------------------------------------
+ // data members
+ //-------------------------------------
+
+ // enumeration value to identify the register, e.g., REGISTER_X86_EAX, or REGISTER_AMD64_XMM0
+ CorDebugRegister m_kRegNumber;
+
+ // address in a context or frame register display of the register value
+ CORDB_ADDRESS m_regAddr;
+
+ // the actual value of the register
+ SIZE_T m_regValue;
+}; // class RegisterInfo
+
+// class EnregisteredValueHome: abstract class to encapsulate basic information for a register value, and
+// serve as a base class for values residing in register-based locations, such as a single register, a
+// register pair, or a register and memory location.
+class EnregisteredValueHome
+{
+public:
+
+ // constructor to initialize an instance of EnregisteredValueHome
+ EnregisteredValueHome(const CordbNativeFrame * pFrame);
+
+ virtual ~EnregisteredValueHome() {}
+
+ // virtual "copy constructor" to make a copy of "this" to be owned by a different instance of
+ // Cordb*Value. If an instance of CordbVCObjectValue represents an enregistered value class, it means
+ // there is a single field. This implies that the register for the CordbVCObject instance is the same as
+ // the register for its field. When we create a Cordb*Value to represent this field, we need to make a
+ // copy of the EnregisteredValueHome belonging to the CordbVCObject instance to become the
+ // EnregisteredValueHome of the Cord*Value representing the field.
+ // returns:
+ // a new cloned copy of this object, allocated on the heap.
+ // Caller is responsible for deleting the memory (using the standard delete operator).
+ // note:
+ // C++ allows derived implementations to differ on return type, thus allowing
+ // derived impls to return the cloned copy as its actual derived type, and not just as a base type.
+
+
+ virtual
+ EnregisteredValueHome * Clone() const = 0;
+
+ // set a remote enregistered location to a new value
+ // Arguments:
+ // input: pNewValue - buffer containing the new value along with its size
+ // pContext - context from which the value comes
+ // fIsSigned - indicates whether the value is signed or not. The value provided may be smaller than
+ // a register, in which case we'll need to extend it to a full register width. To do this
+ // correctly, we need to know whether to sign extend or zero extend. Currently, only
+ // the RegValueHome virtual function uses this, but we may need it if we introduce
+ // types that don't completely occupy the size of two registers.
+ // output: updates the remote enregistered value on success
+ // Note: Throws E_FAIL for invalid input or various HRESULTs from an
+ // unsuccessful call to WriteProcessMemory
+ virtual
+ void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned) = 0;
+
+ // Gets an enregistered value and returns it to the caller
+ // Arguments:
+ // input: pValueOutBuffer - buffer in which to return the value, along with its size
+ // output: pValueOutBuffer - filled with the value
+ // Note: Throws E_NOTIMPL for attempts to get an enregistered value for a float register
+ // (implementation for derived class FloatRegValueHome)
+ virtual
+ void GetEnregisteredValue(MemoryRange valueOutBuffer) = 0;
+
+ // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this
+ // instance of a derived class of EnregisteredValueHome
+ // Arguments: input: none--uses fields of "this"
+ // output: pRegAddr - address of an instance of RemoteAddress with field values set to corresponding
+ // field values of "this"
+ virtual
+ void CopyToIPCEType(RemoteAddress * pRegAddr) = 0;
+
+ // accessor
+ const CordbNativeFrame * GetFrame() const { return m_pFrame; };
+
+ //-------------------------------------
+ // data members
+ //-------------------------------------
+protected:
+ // The frame on which the value resides
+ const CordbNativeFrame * m_pFrame;
+
+}; // class EnregisteredValueHome
+
+typedef NewHolder<EnregisteredValueHome> EnregisteredValueHomeHolder;
+
+// class RegValueHome: encapsulates basic information for a value that resides in a single register
+// and serves as a base class for values residing in a register pair.
+class RegValueHome: public EnregisteredValueHome
+{
+public:
+
+ // initializing constructor
+ // Arguments:
+ // input: pFrame - frame to which the value belongs
+ // regNum - enumeration value corresponding to the particular hardware register in
+ // which the value resides
+ // regAddr - remote address within a register display (in a context or frame) of the
+ // register value
+ // output: no out parameters, but the instance has been initialized
+ RegValueHome(const CordbNativeFrame * pFrame,
+ CorDebugRegister regNum):
+ EnregisteredValueHome(pFrame),
+ m_reg1Info(regNum,
+ pFrame->GetLeftSideAddressOfRegister(regNum),
+ *(pFrame->GetAddressOfRegister(regNum)))
+ {};
+
+ // copy constructor
+ // Arguments:
+ // input: pRemoteRegAddr - instance of a remote register address from which the values for this
+ // instance will come
+ // output: no out parameters, but the instance has been initialized
+ RegValueHome(const RegValueHome * pRemoteRegAddr):
+ EnregisteredValueHome(pRemoteRegAddr->m_pFrame),
+ m_reg1Info(pRemoteRegAddr->m_reg1Info)
+ {};
+
+ // make a copy of this instance of RegValueHome
+ virtual
+ RegValueHome * Clone() const { return new RegValueHome(*this); };
+
+ // updates a register in a given context, and in the regdisplay of a given frame.
+ void SetContextRegister(DT_CONTEXT * pContext,
+ CorDebugRegister regNum,
+ SIZE_T newVal);
+
+ // set the value of a remote enregistered value
+ virtual
+ void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned);
+
+ // Gets an enregistered value and returns it to the caller
+ virtual
+ void GetEnregisteredValue(MemoryRange valueOutBuffer);
+ // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this
+ // instance of a derived class of RegValueHome
+ virtual
+ void CopyToIPCEType(RemoteAddress * pRegAddr);
+
+ //-------------------------------------
+ // data members
+ //-------------------------------------
+protected:
+ // The information for the register in which the value resides.
+ const RegisterInfo m_reg1Info;
+}; // class RegValueHome
+
+// class RegRegValueHome
+// derived class to add a second register for values that live in a pair of registers
+class RegRegValueHome: public RegValueHome
+{
+public:
+ // initializing constructor
+ // Arguments:
+ // input: pFrame - frame to which the value belongs
+ // reg1Num - enumeration value corresponding to the first particular hardware register in
+ // which the value resides
+ // reg1Addr - remote address within a register display (in a context or frame) of the
+ // first register
+ // reg2Num - enumeration value corresponding to the second particular hardware register in
+ // which the value resides
+ // reg2Addr - remote address within a register display (in a context or frame) of the
+ // second register
+ // output: no out parameters, but the instance has been initialized
+ RegRegValueHome(const CordbNativeFrame * pFrame,
+ CorDebugRegister reg1Num,
+ CorDebugRegister reg2Num):
+ RegValueHome(pFrame, reg1Num),
+ m_reg2Info(reg2Num,
+ pFrame->GetLeftSideAddressOfRegister(reg2Num),
+ *(pFrame->GetAddressOfRegister(reg2Num)))
+ {};
+
+ // copy constructor
+ // Arguments:
+ // input: pRemoteRegAddr - instance of a remote register address from which the values for this
+ // instance will come
+ // output: no out parameters, but the instance has been initialized
+ RegRegValueHome(const RegRegValueHome * pRemoteRegAddr):
+ RegValueHome(pRemoteRegAddr),
+ m_reg2Info(pRemoteRegAddr->m_reg2Info)
+ {};
+
+ // make a copy of this instance of RegRegValueHome
+ virtual
+ RegRegValueHome * Clone() const { return new RegRegValueHome(*this); };
+
+ // set the value of a remote enregistered value
+ virtual
+ void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned);
+
+ // Gets an enregistered value and returns it to the caller
+ virtual
+ void GetEnregisteredValue(MemoryRange valueOutBuffer);
+
+ // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this
+ // instance of a derived class of EnregisteredValueHome
+ void CopyToIPCEType(RemoteAddress * pRegAddr);
+
+ //-------------------------------------
+ // data members
+ //-------------------------------------
+
+protected:
+ // The information for the second of two registers in which the value resides.
+ const RegisterInfo m_reg2Info;
+}; // class RegRegValueHome
+
+// class RegAndMemBaseValueHome
+// derived from RegValueHome, this class is also a base class for RegMemValueHome
+// and MemRegValueHome, which add a memory location for reg-mem or mem-reg values
+class RegAndMemBaseValueHome: public RegValueHome
+{
+public:
+ // initializing constructor
+ // Arguments:
+ // input: pFrame - frame to which the value belongs
+ // reg1Num - enumeration value corresponding to the first particular hardware register in
+ // which the value resides
+ // reg1Addr - remote address within a register display (in a context or frame) of the
+ // register component of the value
+ // memAddr - remote address for the memory component of the value
+ // output: no out parameters, but the instance has been initialized
+ RegAndMemBaseValueHome(const CordbNativeFrame * pFrame,
+ CorDebugRegister reg1Num,
+ CORDB_ADDRESS memAddr):
+ RegValueHome(pFrame, reg1Num),
+ m_memAddr(memAddr)
+ {};
+
+ // copy constructor
+ // Arguments:
+ // input: pRemoteRegAddr - instance of a remote register address from which the values for this
+ // instance will come
+ // output: no out parameters, but the instance has been initialized
+ RegAndMemBaseValueHome(const RegAndMemBaseValueHome * pRemoteRegAddr):
+ RegValueHome(pRemoteRegAddr),
+ m_memAddr()
+ {};
+
+ // make a copy of this instance of RegRegValueHome
+ virtual
+ RegAndMemBaseValueHome * Clone() const = 0;
+
+ // set the value of a remote enregistered value
+ virtual
+ void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * DT_pContext, bool fIsSigned) = 0;
+
+ // Gets an enregistered value and returns it to the caller
+ virtual
+ void GetEnregisteredValue(MemoryRange valueOutBuffer) = 0;
+
+ // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this
+ // instance of a derived class of EnregisteredValueHome
+ virtual
+ void CopyToIPCEType(RemoteAddress * pRegAddr) = 0;
+
+ //-------------------------------------
+ // data members
+ //-------------------------------------
+
+protected:
+ // remote address for the memory component of the value
+ CORDB_ADDRESS m_memAddr;
+
+}; // class RegAndMemBaseValueHome;
+
+// class RegMemValueHome
+// type derived from abstract class RegAndMemBaseValueHome to represent a Register/Memory location where the
+// high order part of the value is kept in a register, and the low order part is kept in memory
+class RegMemValueHome: public RegAndMemBaseValueHome
+{
+public:
+
+ // initializing constructor
+ // Arguments:
+ // input: pFrame - frame to which the value belongs
+ // reg1Num - enumeration value corresponding to the first particular hardware register in
+ // which the value resides
+ // reg1Addr - remote address within a register display (in a context or frame) of the
+ // register component of the value
+ // memAddr - remote address for the memory component of the value
+ // output: no out parameters, but the instance has been initialized
+ RegMemValueHome(const CordbNativeFrame * pFrame,
+ CorDebugRegister reg1Num,
+ CORDB_ADDRESS memAddr):
+ RegAndMemBaseValueHome(pFrame, reg1Num, memAddr)
+ {};
+
+ // copy constructor
+ // Arguments:
+ // input: pRemoteRegAddr - instance of a remote register address from which the values for this
+ // instance will come
+ // output: no out parameters, but the instance has been initialized
+ RegMemValueHome(const RegMemValueHome * pRemoteRegAddr):
+ RegAndMemBaseValueHome(pRemoteRegAddr)
+ {};
+
+ // make a copy of this instance of RegMemValueHome
+ virtual
+ RegMemValueHome * Clone() const { return new RegMemValueHome(*this); };
+
+ // set the value of a remote enregistered value
+ virtual
+ void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned);
+
+ // Gets an enregistered value and returns it to the caller
+ virtual
+ void GetEnregisteredValue(MemoryRange valueOutBuffer);
+
+ // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this
+ // instance of a derived class of EnregisteredValueHome
+ virtual
+ void CopyToIPCEType(RemoteAddress * pRegAddr);
+
+}; // class RegMemValueHome;
+
+// class MemRegValueHome
+// type derived from abstract class RegAndMemBaseValueHome to represent a Register/Memory location where the
+// low order part of the value is kept in a register, and the high order part is kept in memory
+class MemRegValueHome: public RegAndMemBaseValueHome
+{
+public:
+
+ // initializing constructor
+ // Arguments:
+ // input: pFrame - frame to which the value belongs
+ // reg1Num - enumeration value corresponding to the first particular hardware register in
+ // which the value resides
+ // reg1Addr - remote address within a register display (in a context or frame) of the
+ // register component of the value
+ // memAddr - remote address for the memory component of the value
+ // output: no out parameters, but the instance has been initialized
+ MemRegValueHome(const CordbNativeFrame * pFrame,
+ CorDebugRegister reg1Num,
+ CORDB_ADDRESS memAddr):
+ RegAndMemBaseValueHome(pFrame, reg1Num, memAddr)
+ {};
+
+ // copy constructor
+ // Arguments:
+ // input: pRemoteRegAddr - instance of a remote register address from which the values for this
+ // instance will come
+ // output: no out parameters, but the instance has been initialized
+ MemRegValueHome(const MemRegValueHome * pRemoteRegAddr):
+ RegAndMemBaseValueHome(pRemoteRegAddr)
+ {};
+
+ // make a copy of this instance of MemRegValueHome
+ virtual
+ MemRegValueHome * Clone() const { return new MemRegValueHome(*this); };
+
+ // set the value of a remote enregistered value
+ virtual
+ void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned);
+
+ // Gets an enregistered value and returns it to the caller
+ virtual
+ void GetEnregisteredValue(MemoryRange valueOutBuffer);
+
+ // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this
+ // instance of a derived class of EnregisteredValueHome
+ virtual
+ void CopyToIPCEType(RemoteAddress * pRegAddr);
+
+}; // class MemRegValueHome;
+
+// class FloatRegValueHome
+// derived class to add an index into the FP register stack for a floating point value
+class FloatRegValueHome: public EnregisteredValueHome
+{
+public:
+ // initializing constructor
+ // Arguments:
+ // input: pFrame - frame to which the value belongs
+ // index - index into the floating point stack where the value resides
+ // output: no out parameters, but the instance has been initialized
+ FloatRegValueHome(const CordbNativeFrame * pFrame,
+ DWORD index):
+ EnregisteredValueHome(pFrame),
+ m_floatIndex(index)
+ {};
+
+ // copy constructor
+ // Arguments:
+ // input: pRemoteRegAddr - instance of a remote register address from which the values for this
+ // instance will come
+ // output: no out parameters, but the instance has been initialized
+ FloatRegValueHome(const FloatRegValueHome * pRemoteRegAddr):
+ EnregisteredValueHome(pRemoteRegAddr->m_pFrame),
+ m_floatIndex(pRemoteRegAddr->m_floatIndex)
+ {};
+
+ // make a copy of this instance of FloatRegValueHome
+ virtual
+ FloatRegValueHome * Clone() const { return new FloatRegValueHome(*this); };
+
+ // set the value of a remote enregistered value
+ virtual
+ void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned);
+
+ // Gets an enregistered value and returns it to the caller
+ virtual
+ void GetEnregisteredValue(MemoryRange valueOutBuffer);
+
+ // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this
+ // instance of a derived class of EnregisteredValueHome
+ virtual
+ void CopyToIPCEType(RemoteAddress * pRegAddr);
+
+ //-------------------------------------
+ // data members
+ //-------------------------------------
+
+protected:
+ // index into the FP registers for the register in which the floating point value resides
+ const DWORD m_floatIndex;
+ }; // class FloatRegValueHome
+
+// ----------------------------------------------------------------------------
+// Type hierarchy for value locations
+// ValueHome
+// | | |
+// ------------------ | -------------------
+// | | |
+// RemoteValueHome RegisterValueHome HandleValueHome
+// | |
+// -------- -------
+// | |
+// VCRemoteValueHome RefRemoteValueHome
+//
+// ValueHome: abstract base class, provides remote read and write utilities
+// RemoteValueHome: used for CordbObjectValue, CordbArrayValue, and CordbBoxValue instances,
+// which have only remote locations, and for other ICDValues with a remote address
+// RegisterValueHome: used for CordbGenericValue and CordbReferenceValue instances with
+// only a register location
+// HandleValueHome: used for CordbReferenceValue instances with only an object handle
+// VCRemoteValueHome: used for CordbVCObjectValue instances to supply special operation CreateInternalValue for
+// value class objects with only a remote location
+// RefRemoteValueHome: used for CordbReferenceValue instances with only a remote location
+//
+// In addition, we have a special type for the ValueHome field for CordbReferenceValue instances:
+// RefValueHome. This will have a field of type ValueHome and will implement extra operations only relevant
+// for object references.
+//
+// ----------------------------------------------------------------------------
+//
+class ValueHome
+{
+public:
+ ValueHome(CordbProcess * pProcess):
+ m_pProcess(pProcess) { _ASSERTE(pProcess != NULL); };
+
+ virtual
+ ~ValueHome() {}
+
+ // releases resources as necessary
+ virtual
+ void Clear() = 0;
+
+ // gets the remote address for the value or returns NULL if none exists
+ virtual
+ CORDB_ADDRESS GetAddress() = 0;
+
+ // Gets a value and returns it in dest
+ // Argument:
+ // input: none (uses fields of the instance)
+ // output: dest - buffer containing the value retrieved as long as the returned HRESULT doesn't
+ // indicate an error.
+ // Note: Throws errors from read process memory operation or GetThreadContext operation
+ virtual
+ void GetValue(MemoryRange dest) = 0;
+
+ // Sets a location to the value provided in src
+ // Arguments:
+ // input: src - buffer containing the new value to be set--memory for this buffer is owned by the caller
+ // pType - type information about the value
+ // output: none, but on success, changes m_remoteValue to hold the new value
+ // Note: Throws errors from SafeWriteBuffer
+ virtual
+ void SetValue(MemoryRange src, CordbType * pType) = 0;
+
+ // creates an ICDValue for a field or array element or for the value type of a boxed object
+ // Arguments:
+ // input: pType - type of the internal value
+ // offset - offset to the internal value
+ // localAddress - address of thelogical buffer within the parent class' local cached
+ // copy that holds the internal element
+ // size - size of the internal value
+ // output: ppValue - the newly created ICDValue instance
+ // Note: Throws for a variety of possible failures: OOM, E_FAIL, errors from
+ // ReadProcessMemory.
+ virtual
+ void CreateInternalValue(CordbType * pType,
+ SIZE_T offset,
+ void * localAddress,
+ ULONG32 size,
+ ICorDebugValue ** ppValue) = 0;
+
+ // Gets the value of a field or element of an existing ICDValue instance and returns it in dest
+ // Arguments
+ // input: offset - offset within the value to the internal field or element
+ // output: dest - buffer to hold the value--memory for this buffer is owned by the caller
+ // Note: Throws process memory write errors
+ virtual
+ void GetInternalValue(MemoryRange dest, SIZE_T offset) = 0;
+
+ // copies register information from this to a RemoteAddress instance for FuncEval
+ // Arguments:
+ // output: pRegAddr - copy of information in m_pRemoteRegAddr, converted to
+ // an instance of RemoteAddress
+ virtual
+ void CopyToIPCEType(RemoteAddress * pRegAddr) = 0;
+
+private:
+ // unimplemented copy constructor to prevent passing by value
+ ValueHome(ValueHome * pValHome);
+
+protected:
+ // --------------
+ // data member
+ // --------------
+ CordbProcess * m_pProcess;
+}; // class ValueHome
+
+// ============================================================================
+// RemoteValueHome class
+// ============================================================================
+// to be used for CordbObjectValue, CordbArrayValue, and CordbBoxValue, none of which ever have anything but
+// a remote address
+class RemoteValueHome: public ValueHome
+{
+public:
+ // constructor
+ // Note: It's possible that remoteValue.pAddress may be NULL--FuncEval makes
+ // empty GenericValues for literals in which case we would have neither a remote address nor a
+ // register address
+ RemoteValueHome(CordbProcess * pProcess, TargetBuffer remoteValue);
+
+ // gets the remote address for the value
+ virtual
+ CORDB_ADDRESS GetAddress() { return m_remoteValue.pAddress; };
+
+ // releases resources as necessary
+ virtual
+ void Clear() {};
+
+ // Gets a value and returns it in dest
+ virtual
+ void GetValue(MemoryRange dest);
+
+ // Sets a location to the value provided in src
+ virtual
+ void SetValue(MemoryRange src, CordbType * pType);
+
+ // creates an ICDValue for a field or array element or for the value type of a boxed object
+ virtual
+ void CreateInternalValue(CordbType * pType,
+ SIZE_T offset,
+ void * localAddress,
+ ULONG32 size,
+ ICorDebugValue ** ppValue);
+
+ // Gets the value of a field or element of an existing ICDValue instance and returns it in dest
+ virtual
+ void GetInternalValue(MemoryRange dest, SIZE_T offset);
+
+ // copies register information from this to a RemoteAddress instance for FuncEval
+ virtual
+ void CopyToIPCEType(RemoteAddress * pRegAddr);
+
+
+ // ----------------
+ // data member
+ // ----------------
+
+protected:
+ TargetBuffer m_remoteValue;
+}; // class RemoteValueHome
+
+// ============================================================================
+// RegisterValueHome class
+// ============================================================================
+// for values that may either have a remote location or be enregistered--
+// to be used for CordbGenericValue, and as base for CordbVCObjectValue and CordbReferenceValue
+class RegisterValueHome: public ValueHome
+{
+public:
+ // constructor
+ RegisterValueHome(CordbProcess * pProcess,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr);
+
+ // clean up resources
+ virtual
+ void Clear();
+
+ // gets the remote address for the value or returns NULL if none exists
+ virtual
+ CORDB_ADDRESS GetAddress() { return NULL; };
+
+ // Gets a value and returns it in dest
+ virtual
+ void GetValue(MemoryRange dest);
+
+ // Sets a location to the value provided in src
+ virtual
+ void SetValue(MemoryRange src, CordbType * pType);
+
+ // creates an ICDValue for a field or array element or for the value type of a boxed object
+ virtual
+ void CreateInternalValue(CordbType * pType,
+ SIZE_T offset,
+ void * localAddress,
+ ULONG32 size,
+ ICorDebugValue ** ppValue);
+
+ // Gets the value of a field or element of an existing ICDValue instance and returns it in dest
+ virtual
+ void GetInternalValue(MemoryRange dest, SIZE_T offset);
+
+ // copies the register information from this to a RemoteAddress instance
+ virtual
+ void CopyToIPCEType(RemoteAddress * pRegAddr);
+
+protected:
+
+ // sets a remote enregistered location to a new value
+ void SetEnregisteredValue(MemoryRange src, bool fIsSigned);
+
+ // gets a value from an enregistered location
+ void GetEnregisteredValue(MemoryRange dest);
+
+ bool IsSigned(CorElementType elementType);
+
+ // ----------------
+ // data member
+ // ----------------
+
+protected:
+ // Left Side register location info for various kinds of (partly) enregistered values.
+ EnregisteredValueHome * m_pRemoteRegAddr;
+
+}; // class RegisterValueHome
+
+// ============================================================================
+// HandleValueHome class
+// ============================================================================
+
+class HandleValueHome: public ValueHome
+{
+public:
+ // constructor
+ // Arguments:
+ // input: pProcess - process to which the value belongs
+ // vmObjHandle - objectHandle holding the object address
+ HandleValueHome(CordbProcess * pProcess, VMPTR_OBJECTHANDLE vmObjHandle):
+ ValueHome(pProcess),
+ m_vmObjectHandle(vmObjHandle) {};
+
+ // releases resources as necessary
+ virtual
+ void Clear() {};
+
+ // gets the remote address for the value or returns NULL if none exists
+ virtual
+ CORDB_ADDRESS GetAddress();
+
+ // Gets a value and returns it in dest
+ virtual
+ void GetValue(MemoryRange dest);
+
+ // Sets a location to the value provided in src
+ virtual
+ void SetValue(MemoryRange src, CordbType * pType);
+
+ // creates an ICDValue for a field or array element or for the value type of a boxed object
+ virtual
+ void CreateInternalValue(CordbType * pType,
+ SIZE_T offset,
+ void * localAddress,
+ ULONG32 size,
+ ICorDebugValue ** ppValue);
+
+ // Gets the value of a field or element of an existing ICDValue instance and returns it in dest
+ virtual
+ void GetInternalValue(MemoryRange dest, SIZE_T offset);
+
+ // copies the register information from this to a RemoteAddress instance
+ virtual
+ void CopyToIPCEType(RemoteAddress * pRegAddr);
+
+ // ----------------
+ // data member
+ // ----------------
+private:
+ VMPTR_OBJECTHANDLE m_vmObjectHandle;
+}; // class HandleValueHome;
+
+// ============================================================================
+// VCRemoteValueHome class
+// ============================================================================
+// used only for CordbVCObjectValue
+class VCRemoteValueHome: public RemoteValueHome
+{
+public:
+ // constructor
+ VCRemoteValueHome(CordbProcess * pProcess,
+ TargetBuffer remoteValue):
+ RemoteValueHome(pProcess, remoteValue) {};
+
+ // Sets a location to the value provided in src
+ virtual
+ void SetValue(MemoryRange src, CordbType * pType);
+
+}; // class VCRemoteValueHome
+
+// ============================================================================
+// RefRemoteValueHome class
+// ============================================================================
+
+// used only for CordbReferenceValue
+class RefRemoteValueHome: public RemoteValueHome
+{
+public:
+ // constructor
+ // Arguments
+ RefRemoteValueHome(CordbProcess * pProcess,
+ TargetBuffer remoteValue);
+
+ // Sets a location to the value provided in src
+ virtual
+ void SetValue(MemoryRange src, CordbType * pType);
+
+}; // class RefRemoteValueHome
+
+// ============================================================================
+// RefValueHome class
+// ============================================================================
+
+// abstract superclass for derivations RefRemoteValueHome and RefRegValueHome
+class RefValueHome
+{
+public:
+ // constructor
+ RefValueHome() { m_pHome = NULL; m_fNullObjHandle = true; };
+
+ // constructor
+ RefValueHome(CordbProcess * pProcess,
+ TargetBuffer remoteValue,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr,
+ VMPTR_OBJECTHANDLE vmObjHandle);
+
+ // indicates whether the object handle is null
+ bool ObjHandleIsNull() { return m_fNullObjHandle; };
+ void SetObjHandleFlag(bool isNull) { m_fNullObjHandle = isNull; };
+
+ // ----------------
+ // data members
+ // ----------------
+ // appropriate instantiation of ValueHome
+ ValueHome * m_pHome;
+
+private:
+ // true iff m_pHome is an instantiation of RemoteValueHome or RegisterValueHome
+ bool m_fNullObjHandle;
+}; // class RefValueHome
+
+typedef enum {kUnboxed, kBoxed} BoxedValue;
+#define EMPTY_BUFFER TargetBuffer(PTR_TO_CORDB_ADDRESS((void *)NULL), 0)
+
+// for an inheritance graph of the ICDValue types, // See file:./ICorDebugValueTypes.vsd for a diagram of the types.
+/* ------------------------------------------------------------------------- *
+ * Value class
+ * ------------------------------------------------------------------------- */
+
+class CordbValue : public CordbBase
+{
+public:
+ //-----------------------------------------------------------
+ // Constructor/destructor
+ //-----------------------------------------------------------
+ CordbValue(CordbAppDomain * appdomain,
+ CordbType * type,
+ CORDB_ADDRESS id,
+ bool isLiteral,
+ NeuterList * pList = NULL);
+
+ virtual ~CordbValue();
+ virtual void Neuter();
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetType(CorElementType *pType)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(pType, CorElementType *);
+
+ *pType = m_type->m_elementType;
+ return (S_OK);
+ }
+
+ COM_METHOD GetSize(ULONG32 *pSize)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(pSize, ULONG32 *);
+
+ if (m_size > ULONG_MAX)
+ {
+ *pSize = ULONG_MAX;
+ return (COR_E_OVERFLOW);
+ }
+
+ *pSize = (ULONG)m_size;
+ return (S_OK);
+ }
+
+ COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint);
+
+ //-----------------------------------------------------------
+ // ICorDebugValue2
+ //-----------------------------------------------------------
+
+ COM_METHOD GetExactType(ICorDebugType **ppType);
+
+ //-----------------------------------------------------------
+ // ICorDebugValue3
+ //-----------------------------------------------------------
+
+ COM_METHOD GetSize64(ULONG64 *pSize)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(pSize, ULONG64 *);
+
+ *pSize = m_size;
+ return (S_OK);
+ }
+
+ //-----------------------------------------------------------
+ // Methods not exported through COM
+ //-----------------------------------------------------------
+
+ // Helper for code:CordbValue::CreateValueByType. Create a new instance of CordbGenericValue
+ static
+ void CreateGenericValue(CordbAppDomain * pAppdomain,
+ CordbType * pType,
+ TargetBuffer remoteValue,
+ MemoryRange localValue,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr,
+ ICorDebugValue** ppValue);
+
+ // Helper for code:CordbValue::CreateValueByType. Create a new instance of CordbVCObjectValue or
+ // CordbReferenceValue
+ static
+ void CreateVCObjOrRefValue(CordbAppDomain * pAppdomain,
+ CordbType * pType,
+ bool boxed,
+ TargetBuffer remoteValue,
+ MemoryRange localValue,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr,
+ ICorDebugValue** ppValue);
+
+ // Create the proper ICDValue instance based on the given element type.
+ static void CreateValueByType(CordbAppDomain * appdomain,
+ CordbType * type,
+ bool boxed,
+ TargetBuffer remoteValue,
+ MemoryRange localValue,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr,
+ ICorDebugValue** ppValue);
+
+ // Create the proper ICDValue instance based on the given remote heap object
+ static ICorDebugValue* CreateHeapValue(CordbAppDomain* pAppDomain,
+ VMPTR_Object vmObj);
+
+
+ // Returns a pointer to the ValueHome field of this instance of CordbValue if one exists or NULL
+ // otherwise. Therefore, this also tells us indirectly whether this instance of CordbValue is also an
+ // instance of one of its derived types and thus has a ValueHome field.
+ virtual
+ ValueHome * GetValueHome() { return NULL; };
+
+ static ULONG32 GetSizeForType(CordbType * pType, BoxedValue boxing);
+
+ virtual CordbAppDomain *GetAppDomain()
+ {
+ return m_appdomain;
+ }
+
+ HRESULT InternalCreateHandle(
+ CorDebugHandleType handleType,
+ ICorDebugHandleValue ** ppHandle);
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+public:
+ CordbAppDomain * m_appdomain;
+ RSSmartPtr<CordbType> m_type;
+
+ // size of the value
+ SIZE_T m_size;
+
+ // true if the value is a RS fabrication.
+ bool m_isLiteral;
+
+};
+
+/* ------------------------------------------------------------------------- *
+ * Value Breakpoint class
+ * ------------------------------------------------------------------------- */
+
+class CordbValueBreakpoint : public CordbBreakpoint,
+ public ICorDebugValueBreakpoint
+{
+public:
+ CordbValueBreakpoint(CordbValue *pValue);
+
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbValueBreakpoint"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugValueBreakpoint
+ //-----------------------------------------------------------
+
+ COM_METHOD GetValue(ICorDebugValue **ppValue);
+ COM_METHOD Activate(BOOL bActive);
+ COM_METHOD IsActive(BOOL *pbActive)
+ {
+ VALIDATE_POINTER_TO_OBJECT(pbActive, BOOL *);
+
+ return BaseIsActive(pbActive);
+ }
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+
+ void Disconnect();
+
+public:
+ CordbValue *m_value;
+};
+
+/* ------------------------------------------------------------------------- *
+ * Generic Value class
+ * ------------------------------------------------------------------------- */
+
+class CordbGenericValue : public CordbValue, public ICorDebugGenericValue, public ICorDebugValue2, public ICorDebugValue3
+{
+public:
+ CordbGenericValue(CordbAppDomain * appdomain,
+ CordbType * type,
+ TargetBuffer remoteValue,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr);
+
+ CordbGenericValue(CordbType * pType);
+ // destructor
+ ~CordbGenericValue();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbGenericValue"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugValue
+ //-----------------------------------------------------------
+
+ // gets the type of the value
+ // Arguments:
+ // output: pType - the type of the value. The caller must guarantee that pType is non-null.
+ // Return Value: S_OK on success, E_INVALIDARG on failure
+ COM_METHOD GetType(CorElementType *pType)
+ {
+ return (CordbValue::GetType(pType));
+ }
+
+ // gets the size of the value
+ // Arguments:
+ // output: pSize - the size of the value. The caller must guarantee that pSize is non-null.
+ // Return Value: S_OK on success, E_INVALIDARG on failure
+ COM_METHOD GetSize(ULONG32 *pSize)
+ {
+ return (CordbValue::GetSize(pSize));
+ }
+ COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint)
+ {
+ return (CordbValue::CreateBreakpoint(ppBreakpoint));
+ }
+
+ // gets the remote (LS) address of the value. This may return NULL if the
+ // value is a literal or resides in a register.
+ // Arguments:
+ // output: pAddress - the address of the value. The caller must guarantee is
+ // non-Null
+ // Return Value: S_OK on success or E_INVALIDARG if pAddress is null
+ COM_METHOD GetAddress(CORDB_ADDRESS *pAddress)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT_OR_NULL(pAddress, CORDB_ADDRESS *);
+
+ *pAddress = m_pValueHome ? m_pValueHome->GetAddress() : NULL;
+ return (S_OK);
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue2
+ //-----------------------------------------------------------
+
+ COM_METHOD GetExactType(ICorDebugType **ppType)
+ {
+ return (CordbValue::GetExactType(ppType));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue3
+ //-----------------------------------------------------------
+
+ COM_METHOD GetSize64(ULONG64 *pSize)
+ {
+ return (CordbValue::GetSize64(pSize));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugGenericValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetValue(void *pTo);
+ COM_METHOD SetValue(void *pFrom);
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+
+ // initialize a generic value by copying the necessary data, either
+ // from the remote process or from another value in this process.
+ void Init(MemoryRange localValue);
+ bool CopyLiteralData(BYTE *pBuffer);
+
+ // Returns a pointer to the ValueHome field
+ virtual
+ ValueHome * GetValueHome() { return m_pValueHome; };
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+private:
+ // hold copies of up to 64-bit values.
+ BYTE m_pCopyOfData[8];
+
+ // location information--remote or register address
+ ValueHome * m_pValueHome;
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Reference Value class
+ * ------------------------------------------------------------------------- */
+
+class CordbReferenceValue : public CordbValue, public ICorDebugReferenceValue, public ICorDebugValue2, public ICorDebugValue3
+{
+public:
+ CordbReferenceValue(CordbAppDomain * pAppdomain,
+ CordbType * pType,
+ MemoryRange localValue,
+ TargetBuffer remoteValue,
+ EnregisteredValueHomeHolder * ppRegAddr,
+ VMPTR_OBJECTHANDLE vmObjectHandle);
+ CordbReferenceValue(CordbType * pType);
+ virtual ~CordbReferenceValue();
+ virtual void Neuter();
+
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbReferenceValue"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetType(CorElementType *pType);
+
+ // get the size of the reference
+ // Arguments:
+ // output: pSize - the size of the value--this must be non-NULL
+ // Return Value: S_OK on success or E_INVALIDARG
+ COM_METHOD GetSize(ULONG32 *pSize)
+ {
+ return (CordbValue::GetSize(pSize));
+ }
+
+ COM_METHOD GetAddress(CORDB_ADDRESS *pAddress);
+ COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint)
+ {
+ return (CordbValue::CreateBreakpoint(ppBreakpoint));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue2
+ //-----------------------------------------------------------
+
+ COM_METHOD GetExactType(ICorDebugType **ppType)
+ {
+ return (CordbValue::GetExactType(ppType));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue3
+ //-----------------------------------------------------------
+
+ COM_METHOD GetSize64(ULONG64 *pSize)
+ {
+ return (CordbValue::GetSize64(pSize));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugReferenceValue
+ //-----------------------------------------------------------
+
+ COM_METHOD IsNull(BOOL * pfIsNull);
+ COM_METHOD GetValue(CORDB_ADDRESS *pAddress);
+ COM_METHOD SetValue(CORDB_ADDRESS address);
+ COM_METHOD Dereference(ICorDebugValue **ppValue);
+ COM_METHOD DereferenceStrong(ICorDebugValue **ppValue);
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+
+ // Helper function for SanityCheckPointer. Make an attempt to read memory at the address which is the
+ // value of the reference.
+ void TryDereferencingTarget();
+
+ // Do a sanity check on the pointer which is the value of the object reference. We can't efficiently
+ // ensure that the pointer is really good, so we settle for a quick check just to make sure the memory at
+ // the address is readable. We're actually just checking that we can dereference the pointer.
+ // If the address is invalid, this will throw.
+ void SanityCheckPointer (CorElementType type);
+
+ // get information about the reference when it's not an object address but another kind of pointer type:
+ // ELEMENT_TYPE_BYREF, ELEMENT_TYPE_PTR or ELEMENT_TYPE_FNPTR
+ void GetPointerData(CorElementType type, MemoryRange localValue);
+
+ // get basic object specific data when a reference points to an object, plus extra data if the object is
+ // an array or string
+ static
+ void GetObjectData(CordbProcess * pProcess,
+ void * objectAddress,
+ CorElementType type,
+ VMPTR_AppDomain vmAppdomain,
+ DebuggerIPCE_ObjectData * pInfo);
+
+ // get information about a TypedByRef object when the reference is the address of a TypedByRef structure.
+ static
+ void GetTypedByRefData(CordbProcess * pProcess,
+ CORDB_ADDRESS pTypedByRef,
+ CorElementType type,
+ VMPTR_AppDomain vmAppDomain,
+ DebuggerIPCE_ObjectData * pInfo);
+
+ // get the address of the object referenced
+ void * GetObjectAddress(MemoryRange localValue);
+
+ // update type information after initializing -- when we initialize, we may get more exact type
+ // information than we previously had
+ void UpdateTypeInfo();
+
+ // Initialize this CordbReferenceValue. This may involve inspecting the LS to get information about the
+ // referent.
+ HRESULT InitRef(MemoryRange localValue);
+
+ bool CopyLiteralData(BYTE *pBuffer);
+
+ static HRESULT Build(CordbAppDomain * appdomain,
+ CordbType * type,
+ TargetBuffer remoteValue,
+ MemoryRange localValue,
+ VMPTR_OBJECTHANDLE vmObjectHandle,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr,
+ CordbReferenceValue** ppValue);
+
+ static HRESULT BuildFromGCHandle(CordbAppDomain *pAppDomain, VMPTR_OBJECTHANDLE gcHandle, ICorDebugReferenceValue ** pOutRef);
+
+ // Common dereference routine shared by both CordbReferenceValue + CordbHandleValue
+ static HRESULT DereferenceCommon(CordbAppDomain * pAppDomain,
+ CordbType * pType,
+ CordbType * pRealTypeOfTypedByref,
+ DebuggerIPCE_ObjectData * m_pInfo,
+ ICorDebugValue ** ppValue);
+
+ // Returns a pointer to the ValueHome field
+ virtual
+ ValueHome * GetValueHome() { return m_valueHome.m_pHome; };
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+public:
+ DebuggerIPCE_ObjectData m_info;
+ CordbType * m_realTypeOfTypedByref; // weak ref
+
+ RefValueHome m_valueHome;
+
+ // Indicates when we last syncronized our stored data (m_info) from the left side
+ UINT m_continueCounterLastSync;
+};
+
+/* ------------------------------------------------------------------------- *
+ * Object Value class
+ *
+ * Because of the oddness of string objects in the Runtime we have one
+ * object that implements both ObjectValue and StringValue. There is a
+ * definite string type, but its really just an object of the string
+ * class. Furthermore, you can have a variable whose type is listed as
+ * "class", but its an instance of the string class and therefore needs
+ * to be treated like a string.
+ * ------------------------------------------------------------------------- */
+
+class CordbObjectValue : public CordbValue,
+ public ICorDebugObjectValue,
+ public ICorDebugObjectValue2,
+ public ICorDebugGenericValue,
+ public ICorDebugStringValue,
+ public ICorDebugValue2,
+ public ICorDebugValue3,
+ public ICorDebugHeapValue2,
+ public ICorDebugHeapValue3,
+ public ICorDebugExceptionObjectValue,
+ public ICorDebugComObjectValue
+{
+public:
+
+ CordbObjectValue(CordbAppDomain * appdomain,
+ CordbType * type,
+ TargetBuffer remoteValue,
+ DebuggerIPCE_ObjectData * pObjectData );
+
+ virtual ~CordbObjectValue();
+
+
+ virtual void Neuter();
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbObjectValue"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetType(CorElementType * pType);
+ COM_METHOD GetSize(ULONG32 * pSize);
+ COM_METHOD GetAddress(CORDB_ADDRESS * pAddress);
+ COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint ** ppBreakpoint);
+
+ //-----------------------------------------------------------
+ // ICorDebugValue2
+ //-----------------------------------------------------------
+
+ COM_METHOD GetExactType(ICorDebugType ** ppType)
+ {
+ return (CordbValue::GetExactType(ppType));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue3
+ //-----------------------------------------------------------
+
+ COM_METHOD GetSize64(ULONG64 *pSize);
+
+ //-----------------------------------------------------------
+ // ICorDebugHeapValue
+ //-----------------------------------------------------------
+
+ COM_METHOD IsValid(BOOL * pfIsValid);
+ COM_METHOD CreateRelocBreakpoint(ICorDebugValueBreakpoint ** ppBreakpoint);
+
+ //-----------------------------------------------------------
+ // ICorDebugHeapValue2
+ //-----------------------------------------------------------
+ COM_METHOD CreateHandle(CorDebugHandleType type, ICorDebugHandleValue ** ppHandle);
+
+ //-----------------------------------------------------------
+ // ICorDebugHeapValue3
+ //-----------------------------------------------------------
+ COM_METHOD GetThreadOwningMonitorLock(ICorDebugThread **ppThread, DWORD *pAcquisitionCount);
+ COM_METHOD GetMonitorEventWaitList(ICorDebugThreadEnum **ppThreadEnum);
+
+ //-----------------------------------------------------------
+ // ICorDebugObjectValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetClass(ICorDebugClass ** ppClass);
+ COM_METHOD GetFieldValue(ICorDebugClass * pClass,
+ mdFieldDef fieldDef,
+ ICorDebugValue ** ppValue);
+ COM_METHOD GetVirtualMethod(mdMemberRef memberRef,
+ ICorDebugFunction **ppFunction);
+ COM_METHOD GetContext(ICorDebugContext ** ppContext);
+ COM_METHOD IsValueClass(BOOL * pfIsValueClass);
+ COM_METHOD GetManagedCopy(IUnknown ** ppObject);
+ COM_METHOD SetFromManagedCopy(IUnknown * pObject);
+
+ COM_METHOD GetFieldValueForType(ICorDebugType * pType,
+ mdFieldDef fieldDef,
+ ICorDebugValue ** ppValue);
+
+ COM_METHOD GetVirtualMethodAndType(mdMemberRef memberRef,
+ ICorDebugFunction ** ppFunction,
+ ICorDebugType ** ppType);
+
+ //-----------------------------------------------------------
+ // ICorDebugGenericValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetValue(void * pTo);
+ COM_METHOD SetValue(void * pFrom);
+
+ //-----------------------------------------------------------
+ // ICorDebugStringValue
+ //-----------------------------------------------------------
+ COM_METHOD GetLength(ULONG32 * pcchString);
+ COM_METHOD GetString(ULONG32 cchString,
+ ULONG32 * ppcchStrin,
+ __out_ecount_opt(cchString) WCHAR szString[]);
+
+ //-----------------------------------------------------------
+ // ICorDebugExceptionObjectValue
+ //-----------------------------------------------------------
+ COM_METHOD EnumerateExceptionCallStack(ICorDebugExceptionObjectCallStackEnum** ppCallStackEnum);
+
+ //-----------------------------------------------------------
+ // ICorDebugComObjectValue
+ //-----------------------------------------------------------
+ COM_METHOD GetCachedInterfaceTypes(BOOL bIInspectableOnly,
+ ICorDebugTypeEnum** ppInterfacesEnum);
+
+ COM_METHOD GetCachedInterfacePointers(BOOL bIInspectableOnly,
+ ULONG32 celt,
+ ULONG32 *pcEltFetched,
+ CORDB_ADDRESS * ptrs);
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+
+ HRESULT Init();
+
+ DebuggerIPCE_ObjectData GetInfo() { return m_info; }
+ CordbHangingFieldTable * GetHangingFieldTable() { return &m_hangingFieldsInstance; }
+
+ // Returns a pointer to the ValueHome field
+ virtual
+ RemoteValueHome * GetValueHome() { return &m_valueHome; };
+
+protected:
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+ DebuggerIPCE_ObjectData m_info;
+ BYTE * m_pObjectCopy; // local cached copy of the object
+ BYTE * m_objectLocalVars; // var base in _this_ process
+ // points _into_ m_pObjectCopy
+ BYTE * m_stringBuffer; // points _into_ m_pObjectCopy
+
+ // remote location information
+ RemoteValueHome m_valueHome;
+
+ // If instances fields are added by EnC, their storage will be off the objects
+ // syncblock. Cache per-object information about such fields here.
+ CordbHangingFieldTable m_hangingFieldsInstance;
+
+private:
+ HRESULT IsExceptionObject();
+
+ BOOL m_fIsExceptionObject;
+
+ HRESULT IsRcw();
+
+ BOOL m_fIsRcw;
+};
+
+/* ------------------------------------------------------------------------- *
+ * Value Class Object Value class
+ * ------------------------------------------------------------------------- */
+
+class CordbVCObjectValue : public CordbValue,
+ public ICorDebugObjectValue, public ICorDebugObjectValue2,
+ public ICorDebugGenericValue, public ICorDebugValue2,
+ public ICorDebugValue3
+{
+public:
+ CordbVCObjectValue(CordbAppDomain * pAppdomain,
+ CordbType * pType,
+ TargetBuffer remoteValue,
+ EnregisteredValueHomeHolder * ppRemoteRegAddr);
+ virtual ~CordbVCObjectValue();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbVCObjectValue"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetType(CorElementType *pType);
+
+ COM_METHOD GetSize(ULONG32 *pSize)
+ {
+ return (CordbValue::GetSize(pSize));
+ }
+ COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint)
+ {
+ return (CordbValue::CreateBreakpoint(ppBreakpoint));
+ }
+
+ COM_METHOD GetAddress(CORDB_ADDRESS *pAddress)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *);
+
+ *pAddress = m_pValueHome->GetAddress();
+ return (S_OK);
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue2
+ //-----------------------------------------------------------
+
+ COM_METHOD GetExactType(ICorDebugType **ppType)
+ {
+ return (CordbValue::GetExactType(ppType));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue3
+ //-----------------------------------------------------------
+
+ COM_METHOD GetSize64(ULONG64 *pSize)
+ {
+ return (CordbValue::GetSize64(pSize));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugObjectValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetClass(ICorDebugClass **ppClass);
+ COM_METHOD GetFieldValue(ICorDebugClass *pClass,
+ mdFieldDef fieldDef,
+ ICorDebugValue **ppValue);
+ COM_METHOD GetVirtualMethod(mdMemberRef memberRef,
+ ICorDebugFunction **ppFunction);
+ COM_METHOD GetContext(ICorDebugContext **ppContext);
+ COM_METHOD IsValueClass(BOOL *pbIsValueClass);
+ COM_METHOD GetManagedCopy(IUnknown **ppObject);
+ COM_METHOD SetFromManagedCopy(IUnknown *pObject);
+ COM_METHOD GetFieldValueForType(ICorDebugType * pType,
+ mdFieldDef fieldDef,
+ ICorDebugValue ** ppValue);
+ COM_METHOD GetVirtualMethodAndType(mdMemberRef memberRef,
+ ICorDebugFunction **ppFunction,
+ ICorDebugType **ppType);
+
+ //-----------------------------------------------------------
+ // ICorDebugGenericValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetValue(void *pTo);
+ COM_METHOD SetValue(void *pFrom);
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+
+ // Initializes the Right-Side's representation of a Value Class object.
+ HRESULT Init(MemoryRange localValue);
+ //HRESULT ResolveValueClass();
+ CordbClass *GetClass();
+
+ // Returns a pointer to the ValueHome field
+ virtual
+ ValueHome * GetValueHome() { return m_pValueHome; };
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+private:
+
+ // local cached copy of the value class
+ BYTE * m_pObjectCopy;
+
+ // location information
+ ValueHome * m_pValueHome;
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Box Value class
+ * ------------------------------------------------------------------------- */
+
+class CordbBoxValue : public CordbValue,
+ public ICorDebugBoxValue,
+ public ICorDebugGenericValue,
+ public ICorDebugValue2,
+ public ICorDebugValue3,
+ public ICorDebugHeapValue2,
+ public ICorDebugHeapValue3
+{
+public:
+ CordbBoxValue(CordbAppDomain * appdomain,
+ CordbType * type,
+ TargetBuffer remoteValue,
+ ULONG32 size,
+ SIZE_T offsetToVars);
+ virtual ~CordbBoxValue();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbBoxValue"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetType(CorElementType *pType);
+
+ COM_METHOD GetSize(ULONG32 *pSize)
+ {
+ return (CordbValue::GetSize(pSize));
+ }
+ COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint)
+ {
+ return (CordbValue::CreateBreakpoint(ppBreakpoint));
+ }
+
+ COM_METHOD GetAddress(CORDB_ADDRESS *pAddress)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ FAIL_IF_NEUTERED(this);
+ VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *);
+
+ *pAddress = m_valueHome.GetAddress();
+ return (S_OK);
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue2
+ //-----------------------------------------------------------
+
+ COM_METHOD GetExactType(ICorDebugType **ppType)
+ {
+ return (CordbValue::GetExactType(ppType));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue3
+ //-----------------------------------------------------------
+
+ COM_METHOD GetSize64(ULONG64 *pSize)
+ {
+ return (CordbValue::GetSize64(pSize));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugHeapValue
+ //-----------------------------------------------------------
+
+ COM_METHOD IsValid(BOOL *pbValid);
+ COM_METHOD CreateRelocBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint);
+
+ //-----------------------------------------------------------
+ // ICorDebugHeapValue2
+ //-----------------------------------------------------------
+ COM_METHOD CreateHandle(CorDebugHandleType type, ICorDebugHandleValue ** ppHandle);
+
+ //-----------------------------------------------------------
+ // ICorDebugHeapValue3
+ //-----------------------------------------------------------
+ COM_METHOD GetThreadOwningMonitorLock(ICorDebugThread **ppThread, DWORD *pAcquisitionCount);
+ COM_METHOD GetMonitorEventWaitList(ICorDebugThreadEnum **ppThreadEnum);
+
+ //-----------------------------------------------------------
+ // ICorDebugGenericValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetValue(void *pTo);
+ COM_METHOD SetValue(void *pFrom);
+
+ //-----------------------------------------------------------
+ // ICorDebugBoxValue
+ //-----------------------------------------------------------
+ COM_METHOD GetObject(ICorDebugObjectValue **ppObject);
+
+ // Returns a pointer to the ValueHome field
+ virtual
+ RemoteValueHome * GetValueHome() { return &m_valueHome; };
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+private:
+ SIZE_T m_offsetToVars;
+
+ // remote location information
+ RemoteValueHome m_valueHome;
+
+};
+
+/* ------------------------------------------------------------------------- *
+ * Array Value class
+ * ------------------------------------------------------------------------- */
+
+class CordbArrayValue : public CordbValue,
+ public ICorDebugArrayValue,
+ public ICorDebugGenericValue,
+ public ICorDebugValue2,
+ public ICorDebugValue3,
+ public ICorDebugHeapValue2,
+ public ICorDebugHeapValue3
+{
+public:
+ CordbArrayValue(CordbAppDomain * appdomain,
+ CordbType * type,
+ DebuggerIPCE_ObjectData * pObjectInfo,
+ TargetBuffer remoteValue);
+ virtual ~CordbArrayValue();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbArrayValue"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetType(CorElementType *pType)
+ {
+ return (CordbValue::GetType(pType));
+ }
+ COM_METHOD GetSize(ULONG32 *pSize)
+ {
+ return (CordbValue::GetSize(pSize));
+ }
+ COM_METHOD GetAddress(CORDB_ADDRESS *pAddress)
+ {
+ VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *);
+ *pAddress = m_valueHome.GetAddress();
+ return (S_OK);
+ }
+ COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint)
+ {
+ return (CordbValue::CreateBreakpoint(ppBreakpoint));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue2
+ //-----------------------------------------------------------
+
+ COM_METHOD GetExactType(ICorDebugType **ppType)
+ {
+ return (CordbValue::GetExactType(ppType));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue3
+ //-----------------------------------------------------------
+
+ COM_METHOD GetSize64(ULONG64 *pSize)
+ {
+ return (CordbValue::GetSize64(pSize));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugHeapValue
+ //-----------------------------------------------------------
+
+ COM_METHOD IsValid(BOOL *pbValid);
+ COM_METHOD CreateRelocBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint);
+
+ //-----------------------------------------------------------
+ // ICorDebugHeapValue2
+ //-----------------------------------------------------------
+ COM_METHOD CreateHandle(CorDebugHandleType type, ICorDebugHandleValue ** ppHandle);
+
+ //-----------------------------------------------------------
+ // ICorDebugHeapValue3
+ //-----------------------------------------------------------
+ COM_METHOD GetThreadOwningMonitorLock(ICorDebugThread **ppThread, DWORD *pAcquisitionCount);
+ COM_METHOD GetMonitorEventWaitList(ICorDebugThreadEnum **ppThreadEnum);
+
+ //-----------------------------------------------------------
+ // ICorDebugArrayValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetElementType(CorElementType * pType);
+ COM_METHOD GetRank(ULONG32 * pnRank);
+ COM_METHOD GetCount(ULONG32 * pnCount);
+ COM_METHOD GetDimensions(ULONG32 cdim, ULONG32 dims[]);
+ COM_METHOD HasBaseIndicies(BOOL * pbHasBaseIndices);
+ COM_METHOD GetBaseIndicies(ULONG32 cdim, ULONG32 indices[]);
+ COM_METHOD GetElement(ULONG32 cdim, ULONG32 indices[], ICorDebugValue ** ppValue);
+ COM_METHOD GetElementAtPosition(ULONG32 nIndex, ICorDebugValue ** ppValue);
+
+ //-----------------------------------------------------------
+ // ICorDebugGenericValue
+ //-----------------------------------------------------------
+
+ COM_METHOD GetValue(void *pTo);
+ COM_METHOD SetValue(void *pFrom);
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+
+ HRESULT Init();
+
+ // Returns a pointer to the ValueHome field
+ virtual
+ RemoteValueHome * GetValueHome() { return &m_valueHome; };
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+private:
+ // contains information about the array, such as rank, number of elements, element size, etc.
+ DebuggerIPCE_ObjectData m_info;
+
+ // type of the elements
+ CordbType *m_elemtype;
+
+ // consists of three parts: a vector containing the lower bounds for each dimension,
+ // a vector containing the upper bounds for each dimension,
+ // a local cached copy of (part of) the array--initialized lazily when we
+ // request a particular element. If the array is large, we will store only
+ // part of it, swapping out the cached segment as necessary to retrieve
+ // requested elements.
+ BYTE * m_pObjectCopy;
+
+ // points to the beginning of the vector containing the lower bounds for each dimension in m_pObjectCopy
+ DWORD * m_arrayLowerBase;
+
+ // points to the beginning of the vector containing the lower bounds for each dimension in m_pObjectCopy
+ DWORD * m_arrayUpperBase;
+ // index of lower bound of data currently stored in m_pObjectCopy
+ SIZE_T m_idxLower;
+
+ // index of upper bound of data currently stored in m_pObjectCopy
+ SIZE_T m_idxUpper;
+
+ // remote location information
+ RemoteValueHome m_valueHome;
+
+};
+
+class CordbHandleValue : public CordbValue, public ICorDebugHandleValue, public ICorDebugValue2, public ICorDebugValue3
+{
+public:
+ CordbHandleValue(CordbAppDomain *appdomain,
+ CordbType *type,
+ CorDebugHandleType handleType);
+ HRESULT Init(VMPTR_OBJECTHANDLE pHandle);
+
+ virtual ~CordbHandleValue();
+
+ virtual void Neuter();
+ virtual void NeuterLeftSideResources();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbHandleValue"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugHandleValue interface
+ //-----------------------------------------------------------
+ COM_METHOD GetHandleType(CorDebugHandleType *pType);
+
+
+ /*
+ * The final release of the interface will also dispose of the handle. This
+ * API provides the ability for client to early dispose the handle.
+ *
+ */
+ COM_METHOD Dispose();
+
+ //-----------------------------------------------------------
+ // ICorDebugValue interface
+ //-----------------------------------------------------------
+ COM_METHOD GetType(CorElementType *pType);
+ COM_METHOD GetSize(ULONG32 *pSize);
+ COM_METHOD GetAddress(CORDB_ADDRESS *pAddress);
+ COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint);
+
+ //-----------------------------------------------------------
+ // ICorDebugValue2
+ //-----------------------------------------------------------
+
+ COM_METHOD GetExactType(ICorDebugType **ppType)
+ {
+ FAIL_IF_NEUTERED(this);
+
+ // If AppDomain is already unloaded, return error
+ if (m_appdomain->IsNeutered() == TRUE)
+ {
+ return COR_E_APPDOMAINUNLOADED;
+ }
+ if (m_vmHandle.IsNull())
+ {
+ return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED;
+ }
+
+ return (CordbValue::GetExactType(ppType));
+ }
+
+ //-----------------------------------------------------------
+ // ICorDebugValue3
+ //-----------------------------------------------------------
+
+ COM_METHOD GetSize64(ULONG64 *pSize);
+
+ //-----------------------------------------------------------
+ // ICorDebugReferenceValue interface
+ //-----------------------------------------------------------
+
+ COM_METHOD IsNull(BOOL *pbNull);
+ COM_METHOD GetValue(CORDB_ADDRESS *pValue);
+ COM_METHOD SetValue(CORDB_ADDRESS value);
+ COM_METHOD Dereference(ICorDebugValue **ppValue);
+ COM_METHOD DereferenceStrong(ICorDebugValue **ppValue);
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+
+ // Returns a pointer to the ValueHome field
+ virtual
+ RemoteValueHome * GetValueHome() { return NULL; };
+
+private:
+ //BOOL RefreshHandleValue(void **pObjectToken);
+ HRESULT RefreshHandleValue();
+
+ // EE object handle pointer. Can be casted to OBJECTHANDLE when go to LS
+ // This instance owns the handle object and must call into the VM to release
+ // it.
+ // If this is non-null, then we increment code:CordbProces::IncrementOutstandingHandles.
+ // Once it goes null, we should decrement the count.
+ // Use AssignHandle, ClearHandle to keep this in sync.
+ VMPTR_OBJECTHANDLE m_vmHandle;
+
+
+ void AssignHandle(VMPTR_OBJECTHANDLE handle);
+ void ClearHandle();
+
+ BOOL m_fCanBeValid; // true if object "can" be valid. False when object is no longer valid.
+ CorDebugHandleType m_handleType; // handle type can be strong or weak
+ DebuggerIPCE_ObjectData m_info;
+; // ICORDebugClass of this object when we create the handle
+};
+
+// This class actually has the implementation for ICorDebugHeap3 interfaces. Any value which implements
+// the interface just delegates to these static calls.
+class CordbHeapValue3Impl
+{
+public:
+ static HRESULT GetThreadOwningMonitorLock(CordbProcess* pProcess,
+ CORDB_ADDRESS remoteObjAddress,
+ ICorDebugThread **ppThread,
+ DWORD *pAcquistionCount);
+ static HRESULT GetMonitorEventWaitList(CordbProcess* pProcess,
+ CORDB_ADDRESS remoteObjAddress,
+ ICorDebugThreadEnum **ppThreadEnum);
+};
+
+/* ------------------------------------------------------------------------- *
+ * Eval class
+ * ------------------------------------------------------------------------- */
+
+class CordbEval : public CordbBase, public ICorDebugEval, public ICorDebugEval2
+{
+public:
+ CordbEval(CordbThread* pThread);
+ virtual ~CordbEval();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbEval"; }
+#endif
+
+ virtual void Neuter();
+ virtual void NeuterLeftSideResources();
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorDebugEval
+ //-----------------------------------------------------------
+
+ COM_METHOD CallFunction(ICorDebugFunction *pFunction,
+ ULONG32 nArgs,
+ ICorDebugValue *ppArgs[]);
+ COM_METHOD NewObject(ICorDebugFunction *pConstructor,
+ ULONG32 nArgs,
+ ICorDebugValue *ppArgs[]);
+ COM_METHOD NewObjectNoConstructor(ICorDebugClass *pClass);
+ COM_METHOD NewString(LPCWSTR string);
+ COM_METHOD NewArray(CorElementType elementType,
+ ICorDebugClass *pElementClass,
+ ULONG32 rank,
+ ULONG32 dims[],
+ ULONG32 lowBounds[]);
+ COM_METHOD IsActive(BOOL *pbActive);
+ COM_METHOD Abort();
+ COM_METHOD GetResult(ICorDebugValue **ppResult);
+ COM_METHOD GetThread(ICorDebugThread **ppThread);
+ COM_METHOD CreateValue(CorElementType elementType,
+ ICorDebugClass *pElementClass,
+ ICorDebugValue **ppValue);
+ COM_METHOD NewStringWithLength(LPCWSTR wszString, UINT iLength);
+
+ COM_METHOD CallParameterizedFunction(ICorDebugFunction * pFunction,
+ ULONG32 nTypeArgs,
+ ICorDebugType * rgpTypeArgs[],
+ ULONG32 nArgs,
+ ICorDebugValue * rgpArgs[]);
+
+ COM_METHOD CreateValueForType(ICorDebugType *pType,
+ ICorDebugValue **ppValue);
+
+ COM_METHOD NewParameterizedObject(ICorDebugFunction * pConstructor,
+ ULONG32 nTypeArgs,
+ ICorDebugType * rgpTypeArgs[],
+ ULONG32 nArgs,
+ ICorDebugValue * rgpArgs[]);
+
+ COM_METHOD NewParameterizedObjectNoConstructor(ICorDebugClass * pClass,
+ ULONG32 nTypeArgs,
+ ICorDebugType * rgpTypeArgs[]);
+
+ COM_METHOD NewParameterizedArray(ICorDebugType * pElementType,
+ ULONG32 rank,
+ ULONG32 dims[],
+ ULONG32 lowBounds[]);
+
+ //-----------------------------------------------------------
+ // ICorDebugEval2
+ //-----------------------------------------------------------
+
+ COM_METHOD RudeAbort();
+
+ //-----------------------------------------------------------
+ // Non-COM methods
+ //-----------------------------------------------------------
+ HRESULT GatherArgInfo(ICorDebugValue *pValue,
+ DebuggerIPCE_FuncEvalArgData *argData);
+ HRESULT SendCleanup();
+
+ // Create a RS literal for primitive type funceval result. In case the result is used as an argument for
+ // another funceval, we need to make sure that we're not relying on the LS value, which will be freed and
+ // thus unavailable.
+ HRESULT CreatePrimitiveLiteral(CordbType * pType,
+ ICorDebugValue ** ppValue);
+
+ //-----------------------------------------------------------
+ // Data members
+ //-----------------------------------------------------------
+
+ bool IsEvalDuringException() { return m_evalDuringException; }
+private:
+ // We must keep a strong reference to the thread so we can properly fail out of SendCleanup if someone releases an
+ // ICorDebugEval after the process has completely gone away.
+ RSSmartPtr<CordbThread> m_thread;
+
+ CordbFunction *m_function;
+ CordbClass *m_class;
+ DebuggerIPCE_FuncEvalType m_evalType;
+
+ HRESULT SendFuncEval(unsigned int genericArgsCount, ICorDebugType *genericArgs[], void *argData1, unsigned int argData1Size, void *argData2, unsigned int argData2Size, DebuggerIPCEvent * event);
+ HRESULT FilterHR(HRESULT hr);
+ BOOL DoAppDomainsMatch( CordbAppDomain* pAppDomain, ULONG32 nTypes, ICorDebugType *pTypes[], ULONG32 nValues, ICorDebugValue *pValues[] );
+
+public:
+ bool m_complete;
+ bool m_successful;
+ bool m_aborted;
+ void *m_resultAddr;
+
+ // This is an OBJECTHANDLE on the LS if func-eval creates a strong handle.
+ // This is a resource in the left-side and must be cleaned up in the left-side.
+ // This gets handled off to a CordbHandleValue (m_pHandleValue) once code:CordbEval::GetResult
+ // and then the CordbHandle is responsible for releasing it in the left-side.
+ // Issue!! This will be leaked if nobody calls GetResult().
+ VMPTR_OBJECTHANDLE m_vmObjectHandle;
+
+ // This is the corresponding cached CordbHandleValue for GetResult.
+ // This takes ownership of the strong handle, m_objectHandle.
+ // This is an External reference, which keeps the Value from being neutered
+ // on a NeuterAtWill sweep.
+ RSExtSmartPtr<CordbHandleValue> m_pHandleValue;
+
+ DebuggerIPCE_ExpandedTypeData m_resultType;
+ VMPTR_AppDomain m_resultAppDomainToken;
+
+ // Left-side memory that needs to be freed.
+ LSPTR_DEBUGGEREVAL m_debuggerEvalKey;
+
+
+ // If we're evalling during a thread's exception, remember the info so that we can restore it when we're done.
+ bool m_evalDuringException; // flag whether we're during the thread's exception.
+ VMPTR_OBJECTHANDLE m_vmThreadOldExceptionHandle; // object handle for thread's managed exception object.
+
+#ifdef _DEBUG
+ // Func-eval should perturb the the thread's current appdomain. So we remember it at start
+ // and then ensure that the func-eval complete restores it.
+ CordbAppDomain * m_DbgAppDomainStarted;
+#endif
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Win32 Event Thread class
+ * ------------------------------------------------------------------------- */
+const unsigned int CW32ET_UNKNOWN_PROCESS_SLOT = 0xFFffFFff; // it's a managed process,
+ //but we don't know which slot it's in - for Detach.
+
+//---------------------------------------------------------------------------------------
+//
+// Dedicated thread for win32 debugging operations.
+//
+// Notes:
+// This is owned by the ShimProcess object. That will both create this and destroy it.
+// OS restriction is that all win32 debugging APIs (CreateProcess, DebugActiveProcess,
+// DebugActiveProcessStop, WaitForDebugEvent, ContinueDebugEvent, etc) are on the same thread.
+//
+class CordbWin32EventThread
+{
+ friend class CordbProcess; //so that Detach can call ExitProcess
+public:
+ CordbWin32EventThread(Cordb * pCordb, ShimProcess * pShim);
+ virtual ~CordbWin32EventThread();
+
+ //
+ // You create a new instance of this class, call Init() to set it up,
+ // then call Start() start processing events. Stop() terminates the
+ // thread and deleting the instance cleans all the handles and such
+ // up.
+ //
+ HRESULT Init();
+ HRESULT Start();
+ HRESULT Stop();
+
+ HRESULT SendCreateProcessEvent(MachineInfo machineInfo,
+ LPCWSTR programName,
+ __in_z LPWSTR programArgs,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ PVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation,
+ CorDebugCreateProcessFlags corDebugFlags);
+
+ HRESULT SendDebugActiveProcessEvent(MachineInfo machineInfo,
+ DWORD pid,
+ bool fWin32Attach,
+ CordbProcess *pProcess);
+
+ HRESULT SendDetachProcessEvent(CordbProcess *pProcess);
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+ HRESULT SendUnmanagedContinue(CordbProcess *pProcess,
+ EUMContinueType eContType);
+ HRESULT UnmanagedContinue(CordbProcess *pProcess,
+ EUMContinueType eContType);
+ void DoDbgContinue(CordbProcess * pProcess,
+ CordbUnmanagedEvent * pUnmanagedEvent);
+ void ForceDbgContinue(CordbProcess *pProcess,
+ CordbUnmanagedThread *ut,
+ DWORD contType,
+ bool contProcess);
+
+#endif //FEATURE_INTEROP_DEBUGGING
+
+ void LockSendToWin32EventThreadMutex()
+ {
+ LOG((LF_CORDB, LL_INFO10000, "W32ET::LockSendToWin32EventThreadMutex\n"));
+ m_sendToWin32EventThreadMutex.Lock();
+ }
+
+ void UnlockSendToWin32EventThreadMutex()
+ {
+ m_sendToWin32EventThreadMutex.Unlock();
+ LOG((LF_CORDB, LL_INFO10000, "W32ET::UnlockSendToWin32EventThreadMutex\n"));
+ }
+
+ bool IsWin32EventThread()
+ {
+ return (m_threadId == GetCurrentThreadId());
+ }
+
+ void Win32EventLoop();
+
+
+ INativeEventPipeline * GetNativePipeline();
+private:
+ void ThreadProc();
+ static DWORD WINAPI ThreadProc(LPVOID parameter);
+
+ void CreateProcess();
+
+
+ INativeEventPipeline * m_pNativePipeline;
+
+
+ void AttachProcess();
+
+ void HandleUnmanagedContinue();
+
+ void ExitProcess(bool fDetach);
+
+private:
+ RSSmartPtr<Cordb> m_cordb;
+
+ HANDLE m_thread;
+ DWORD m_threadId;
+ HANDLE m_threadControlEvent;
+ HANDLE m_actionTakenEvent;
+ BOOL m_run;
+
+ // The process that we're 1:1 with.
+ // This is set when we get a Create / Attach event.
+ // This is only used on the W32ET, which guarantees it will free of races.
+ RSSmartPtr<CordbProcess> m_pProcess;
+
+
+ ShimProcess * m_pShim;
+
+ // @todo - convert this into Stop-Go lock?
+ RSLock m_sendToWin32EventThreadMutex;
+
+ unsigned int m_action;
+ HRESULT m_actionResult;
+ union
+ {
+ struct
+ {
+ MachineInfo machineInfo;
+ LPCWSTR programName;
+ LPWSTR programArgs;
+ LPSECURITY_ATTRIBUTES lpProcessAttributes;
+ LPSECURITY_ATTRIBUTES lpThreadAttributes;
+ BOOL bInheritHandles;
+ DWORD dwCreationFlags;
+ PVOID lpEnvironment;
+ LPCWSTR lpCurrentDirectory;
+ LPSTARTUPINFOW lpStartupInfo;
+ LPPROCESS_INFORMATION lpProcessInformation;
+ CorDebugCreateProcessFlags corDebugFlags;
+ } createData;
+
+ struct
+ {
+ MachineInfo machineInfo;
+ DWORD processId;
+#if !defined(FEATURE_DBGIPC_TRANSPORT_DI)
+ bool fWin32Attach;
+#endif
+ CordbProcess *pProcess;
+
+ // Wrapper to determine if we're interop-debugging.
+ bool IsInteropDebugging()
+ {
+#if !defined(FEATURE_DBGIPC_TRANSPORT_DI)
+ return fWin32Attach;
+#else
+ return false;
+#endif
+ }
+ } attachData;
+
+ struct
+ {
+ CordbProcess *pProcess;
+ } detachData;
+
+ struct
+ {
+ CordbProcess *process;
+ EUMContinueType eContType;
+ } continueData;
+ } m_actionData;
+};
+
+
+// Thread-safe stack which.
+template <typename T>
+class InterlockedStack
+{
+public:
+ InterlockedStack();
+ ~InterlockedStack();
+
+ // Thread safe pushes + pops.
+ // Many threads can push simultaneously.
+ // Only 1 thread can pop.
+ void Push(T * pItem);
+ T * Pop();
+
+protected:
+ T * m_pHead;
+};
+
+//-----------------------------------------------------------------------------
+// Workitem to be placed on RCET worker queue.
+// There's 1 RCET for to be shared by all processes.
+//-----------------------------------------------------------------------------
+class RCETWorkItem
+{
+public:
+
+ virtual ~RCETWorkItem() {}
+
+ // Item is executed and then removed from the list and deleted.
+ virtual void Do() = 0;
+
+ CordbProcess * GetProcess() { return m_pProcess; }
+
+protected:
+ RCETWorkItem(CordbProcess * pProcess)
+ {
+ m_pProcess.Assign(pProcess);
+ m_next = NULL;
+ }
+
+ RSSmartPtr<CordbProcess> m_pProcess;
+
+ // This field is accessed by the InterlockedStack.
+ friend class InterlockedStack<RCETWorkItem>;
+ RCETWorkItem * m_next;
+};
+
+
+// Item to do Neutering work on ExitProcess.
+class ExitProcessWorkItem : public RCETWorkItem
+{
+public:
+ ExitProcessWorkItem(CordbProcess * pProc) : RCETWorkItem(pProc)
+ {
+ }
+
+ virtual void Do();
+};
+
+// Item to do send Attach event.
+class SendAttachProcessWorkItem : public RCETWorkItem
+{
+public:
+ SendAttachProcessWorkItem(CordbProcess * pProc) : RCETWorkItem(pProc)
+ {
+ }
+
+ virtual void Do();
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Runtime Controller Event Thread class
+ * ------------------------------------------------------------------------- */
+
+class CordbRCEventThread
+{
+public:
+ CordbRCEventThread(Cordb* cordb);
+ virtual ~CordbRCEventThread();
+
+ //
+ // You create a new instance of this class, call Init() to set it up,
+ // then call Start() start processing events. Stop() terminates the
+ // thread and deleting the instance cleans all the handles and such
+ // up.
+ //
+ HRESULT Init();
+ HRESULT Start();
+ HRESULT Stop();
+
+ // RCET will take ownership of this item and delete it.
+ void QueueAsyncWorkItem(RCETWorkItem * pItem);
+
+ HRESULT SendIPCEvent(CordbProcess* process,
+ DebuggerIPCEvent* event,
+ SIZE_T eventSize);
+
+ void ProcessStateChanged();
+ void FlushQueuedEvents(CordbProcess* process);
+
+ HRESULT WaitForIPCEventFromProcess(CordbProcess* process,
+ CordbAppDomain *pAppDomain,
+ DebuggerIPCEvent* event);
+
+ bool IsRCEventThread();
+
+private:
+ void DrainWorkerQueue();
+
+ void ThreadProc();
+ static DWORD WINAPI ThreadProc(LPVOID parameter);
+
+
+private:
+ InterlockedStack<class RCETWorkItem> m_WorkerStack;
+
+ RSSmartPtr<Cordb> m_cordb;
+ HANDLE m_thread;
+ DWORD m_threadId;
+ BOOL m_run;
+ HANDLE m_threadControlEvent;
+ BOOL m_processStateChanged;
+};
+
+#ifdef FEATURE_INTEROP_DEBUGGING
+/* ------------------------------------------------------------------------- *
+ * Unmanaged Event struct
+ * ------------------------------------------------------------------------- */
+
+enum CordbUnmanagedEventState
+{
+
+ // The continued flags get set in one of a few patterns.
+ // 1) The event is continued having never been hijacked =>
+ // EventContinuedUnhijacked is set
+ // 2) The event is continued having been hijacked and then the process terminates or
+ // an error occurs before the hijack finishes =>
+ // EventContinuedHijacked is set
+ // 3) The event is continued having been hijacked, then the hijack completes and
+ // execution resumes in the debuggee
+ // EventContinuedHijacked is set
+ // EventContinuedUnhijacked is set
+
+ CUES_None = 0x00,
+ CUES_ExceptionCleared = 0x01,
+ CUES_EventContinuedHijacked = 0x02,
+ CUES_EventContinuedUnhijacked = 0x04,
+ CUES_Dispatched = 0x08,
+ CUES_ExceptionUnclearable = 0x10,
+
+ // This is set when a user continues the event by calling
+ // Continue()
+ CUES_UserContinued = 0x20,
+ // This is true if the event is an IB event
+ CUES_IsIBEvent = 0x40,
+};
+
+struct CordbUnmanagedEvent
+{
+public:
+ BOOL IsExceptionCleared() { return m_state & CUES_ExceptionCleared; }
+ BOOL IsEventContinuedHijacked() { return m_state & CUES_EventContinuedHijacked; }
+ BOOL IsEventContinuedUnhijacked() { return m_state & CUES_EventContinuedUnhijacked; }
+ BOOL IsEventUserContinued() { return m_state & CUES_UserContinued; }
+ BOOL IsEventWaitingForContinue()
+ {
+ return (!IsEventContinuedHijacked() && !IsEventContinuedUnhijacked());
+ }
+ BOOL IsDispatched() { return m_state & CUES_Dispatched; }
+ BOOL IsExceptionUnclearable() { return m_state & CUES_ExceptionUnclearable; }
+ BOOL IsIBEvent() { return m_state & CUES_IsIBEvent; }
+
+ void SetState(CordbUnmanagedEventState state) { m_state = (CordbUnmanagedEventState)(m_state | state); }
+ void ClearState(CordbUnmanagedEventState state) { m_state = (CordbUnmanagedEventState)(m_state & ~state); }
+
+ CordbUnmanagedThread *m_owner;
+ CordbUnmanagedEventState m_state;
+ DEBUG_EVENT m_currentDebugEvent;
+ CordbUnmanagedEvent *m_next;
+};
+
+
+/* ------------------------------------------------------------------------- *
+ * Unmanaged Thread class
+ * ------------------------------------------------------------------------- */
+
+enum CordbUnmanagedThreadState
+{
+ CUTS_None = 0x0000,
+ CUTS_Deleted = 0x0001,
+ CUTS_FirstChanceHijacked = 0x0002,
+ // Set when interop debugging needs the SS flag to be enabled
+ // regardless of what the user wants it to be
+ CUTS_IsSSFlagNeeded = 0x0004,
+ CUTS_GenericHijacked = 0x0008,
+ // when the m_raiseExceptionEntryContext is valid
+ CUTS_HasRaiseExceptionEntryCtx = 0x0010,
+ CUTS_BlockingForSync = 0x0020,
+ CUTS_Suspended = 0x0040,
+ CUTS_IsSpecialDebuggerThread = 0x0080,
+ // when the thread is re-executing RaiseException to retrigger an exception
+ CUTS_IsRaiseExceptionHijacked = 0x0100,
+ CUTS_HasIBEvent = 0x0200,
+ CUTS_HasOOBEvent = 0x0400,
+ CUTS_HasSpecialStackOverflowCase = 0x0800,
+#ifdef _DEBUG
+ CUTS_DEBUG_SingleStep = 0x1000,
+#endif
+ CUTS_SkippingNativePatch = 0x2000,
+ CUTS_HasContextSet = 0x4000,
+ // Set when interop debugging is making use of the single step flag
+ // but the user has not set it
+ CUTS_IsSSFlagHidden = 0x8000
+
+};
+
+class CordbUnmanagedThread : public CordbBase
+{
+public:
+ CordbUnmanagedThread(CordbProcess *pProcess, DWORD dwThreadId, HANDLE hThread, void *lpThreadLocalBase);
+ ~CordbUnmanagedThread();
+
+ using CordbBase::GetProcess;
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbUnmanagedThread"; }
+#endif
+
+ // CordbUnmanagedThread is a purely internal object. It's not exposed via ICorDebug APIs and so
+ // we should never use External AddRef.
+ ULONG STDMETHODCALLTYPE AddRef() { _ASSERTE(!"Don't use external addref on a CordbUnmanagedThread"); return (BaseAddRef());}
+ ULONG STDMETHODCALLTYPE Release() { _ASSERTE(!"Don't use external release on a CordbUnmanagedThread"); return (BaseRelease());}
+
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface)
+ {
+ _ASSERTE(!"Don't use QI on a CordbUnmanagedThread");
+ // Not really used since we never expose this class. If we ever do expose this class via the ICorDebug API then
+ // we should, of course, implement this.
+ return E_NOINTERFACE;
+ }
+
+ HRESULT LoadTLSArrayPtr();
+
+ // Hijacks this thread to a hijack worker function which recieves the current
+ // context and the provided exception record. The reason determines what code
+ // the hijack worker executes
+ HRESULT SetupFirstChanceHijack(EHijackReason::EHijackReason reason, const EXCEPTION_RECORD * pExceptionRecord);
+ HRESULT SetupFirstChanceHijackForSync();
+
+ HRESULT SetupGenericHijack(DWORD eventCode, const EXCEPTION_RECORD * pRecord);
+ HRESULT FixupFromGenericHijack();
+
+ HRESULT FixupAfterOOBException(CordbUnmanagedEvent * ue);
+
+ void SetupForSkipBreakpoint(NativePatch * pNativePatch);
+ void FixupForSkipBreakpoint();
+ bool IsCantStop();
+
+ // These are wrappers for the OS calls which hide
+ // the effects of hijacking and internal SS flag usage
+ HRESULT GetThreadContext(DT_CONTEXT * pContext);
+ HRESULT SetThreadContext(DT_CONTEXT * pContext);
+
+ // Turns on and off the internal usage of the SS flag
+ VOID BeginStepping();
+ VOID EndStepping();
+
+ // An accessor for &m_context, this value generally stores
+ // a context we may need to restore after a hijack completes
+ DT_CONTEXT * GetHijackCtx();
+
+private:
+ CORDB_ADDRESS m_stackBase;
+ CORDB_ADDRESS m_stackLimit;
+
+public:
+ BOOL GetStackRange(CORDB_ADDRESS *pBase, CORDB_ADDRESS *pLimit);
+
+ BOOL IsDeleted() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_Deleted; }
+ BOOL IsFirstChanceHijacked() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_FirstChanceHijacked; }
+ BOOL IsGenericHijacked() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_GenericHijacked; }
+ BOOL IsBlockingForSync() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_BlockingForSync; }
+ BOOL IsSuspended() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_Suspended; }
+ BOOL IsSpecialDebuggerThread() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_IsSpecialDebuggerThread; }
+ BOOL HasIBEvent() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_HasIBEvent; }
+ BOOL HasOOBEvent() { return m_state & CUTS_HasOOBEvent; }
+ BOOL HasSpecialStackOverflowCase() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_HasSpecialStackOverflowCase; }
+#ifdef _DEBUG
+ BOOL IsDEBUGTrace() { return m_state & CUTS_DEBUG_SingleStep; }
+#endif
+ BOOL IsSkippingNativePatch() { LIMITED_METHOD_CONTRACT; return m_state & CUTS_SkippingNativePatch; }
+ BOOL IsContextSet() { LIMITED_METHOD_CONTRACT; return m_state & CUTS_HasContextSet; }
+ BOOL IsSSFlagNeeded() { LIMITED_METHOD_CONTRACT; return m_state & CUTS_IsSSFlagNeeded; }
+ BOOL IsSSFlagHidden() { LIMITED_METHOD_CONTRACT; return m_state & CUTS_IsSSFlagHidden; }
+ BOOL HasRaiseExceptionEntryCtx() { LIMITED_METHOD_CONTRACT; return m_state & CUTS_HasRaiseExceptionEntryCtx; }
+ BOOL IsRaiseExceptionHijacked() { LIMITED_METHOD_CONTRACT; return m_state & CUTS_IsRaiseExceptionHijacked; }
+
+ void SetState(CordbUnmanagedThreadState state)
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_state = (CordbUnmanagedThreadState)(m_state | state);
+ _ASSERTE(!IsSuspended() || !IsBlockingForSync());
+ _ASSERTE(!IsSuspended() || !IsFirstChanceHijacked());
+ }
+ void ClearState(CordbUnmanagedThreadState state) {LIMITED_METHOD_CONTRACT; m_state = (CordbUnmanagedThreadState)(m_state & ~state); }
+
+ void HijackToRaiseException();
+ void RestoreFromRaiseExceptionHijack();
+ void SaveRaiseExceptionEntryContext();
+ void ClearRaiseExceptionEntryContext();
+ BOOL IsExceptionFromLastRaiseException(const EXCEPTION_RECORD* pExceptionRecord);
+
+ CordbUnmanagedEvent *IBEvent() {LIMITED_METHOD_CONTRACT; return &m_IBEvent; }
+ CordbUnmanagedEvent *IBEvent2() {LIMITED_METHOD_CONTRACT; return &m_IBEvent2; }
+ CordbUnmanagedEvent *OOBEvent() { return &m_OOBEvent; }
+
+ DWORD GetOSTid()
+ {
+ return (DWORD) this->m_id;
+ }
+
+#ifdef DBG_TARGET_X86
+ // Stores the thread's current leaf SEH handler
+ HRESULT SaveCurrentLeafSeh();
+ // Restores the thread's leaf SEH handler from the previously saved value
+ HRESULT RestoreLeafSeh();
+#endif
+
+ // Logs basic data about a context to the debugging log
+ static VOID LogContext(DT_CONTEXT* pContext);
+
+public:
+ HANDLE m_handle;
+
+ // @dbgtodo - the TLS reading is only used for interop hijacks; which goes away in Arrowhead.
+ // Target address of the Thread Information Block (TIB).
+ void *m_threadLocalBase;
+
+ // Target address of the Thread Local Storage (TLS) array. This is for slots 0 -63.
+ void *m_pTLSArray;
+
+ // Target Address of extended Thread local Storage array. These are for slots about 63.
+ // This may be NULL if extended storage is not yet allocated.
+ void *m_pTLSExtendedArray;
+
+
+ CordbUnmanagedThreadState m_state;
+
+ CordbUnmanagedEvent m_IBEvent;
+ CordbUnmanagedEvent m_IBEvent2;
+ CordbUnmanagedEvent m_OOBEvent;
+
+ LSPTR_CONTEXT m_pLeftSideContext;
+ void *m_originalHandler;
+
+private:
+ // Spare context used for various purposes.
+ // See CordbUnmanagedThread::GetThreadContext for details
+ DT_CONTEXT m_context;
+
+ // The context of the thread the last time it called into kernel32!RaiseException
+ DT_CONTEXT m_raiseExceptionEntryContext;
+
+ DWORD m_raiseExceptionExceptionCode;
+ DWORD m_raiseExceptionExceptionFlags;
+ DWORD m_raiseExceptionNumberParameters;
+ ULONG_PTR m_raiseExceptionExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
+
+
+#ifdef DBG_TARGET_X86
+ // the SEH handler which was the leaf when SaveCurrentSeh was called (prior to hijack)
+ REMOTE_PTR m_pSavedLeafSeh;
+#endif
+
+ HRESULT EnableSSAfterBP();
+ bool GetEEThreadCantStopHelper();
+
+ DWORD_PTR GetTlsSlot(SIZE_T slot);
+ REMOTE_PTR GetPreDefTlsSlot(SIZE_T slot, bool * pRead);
+
+ void * m_pPatchSkipAddress;
+
+
+
+ /*
+ * This abstracts away an overload of the OS thread's TLS slot. In
+ * particular the runtime may or may not have created a thread object for
+ * a particular OS thread at any point.
+ *
+ * If the runtime has created a thread object, then it stores a pointer to
+ * that thread object in the thread's TLS slot.
+ *
+ * If not, then interop-debugging uses that TLS slot to store temporary
+ * information.
+ *
+ * To determine this, interop-debugging will set the low bit. Thus when
+ * we read the TLS slot, if it is non-NULL, anything w/o the low bit set
+ * is an EE thread object ptr. Anything with the low bit set is an
+ * interop-debugging value. Any NULL is null, and an indicator that
+ * there does not exist a runtime thread object for this thread yet.
+ *
+ */
+ REMOTE_PTR m_pEEThread;
+ REMOTE_PTR m_pdwTlsValue;
+ BOOL m_fValidTlsData;
+
+ UINT m_continueCountCached;
+
+ void CacheEEDebuggerWord();
+ HRESULT SetEEThreadValue(REMOTE_PTR EETlsValue);
+#ifdef FEATURE_IMPLICIT_TLS
+ DWORD_PTR GetEEThreadValue();
+ REMOTE_PTR GetClrModuleTlsDataAddress();
+ REMOTE_PTR GetEETlsDataBlock();
+#endif
+
+public:
+ HRESULT GetEEDebuggerWord(REMOTE_PTR *pValue);
+ HRESULT SetEEDebuggerWord(REMOTE_PTR value);
+ HRESULT GetEEThreadPtr(REMOTE_PTR *ppEEThread);
+
+ bool GetEEPGCDisabled();
+ void GetEEState(bool *threadStepping, bool *specialManagedException);
+ bool GetEEFrame();
+};
+#endif // FEATURE_INTEROP_DEBUGGING
+
+
+//********************************************************************************
+//**************** App Domain Publishing Service API *****************************
+//********************************************************************************
+
+
+class EnumElement
+{
+public:
+ EnumElement()
+ {
+ m_pData = NULL;
+ m_pNext = NULL;
+ }
+
+ void SetData (void *pData) { m_pData = pData;}
+ void *GetData () { return m_pData;}
+ void SetNext (EnumElement *pNext) { m_pNext = pNext;}
+ EnumElement *GetNext () { return m_pNext;}
+
+private:
+ void *m_pData;
+ EnumElement *m_pNext;
+};
+
+#if defined(FEATURE_DBG_PUBLISH)
+
+// Prototype of psapi!GetModuleFileNameEx.
+typedef DWORD FPGetModuleFileNameEx(HANDLE, HMODULE, LPTSTR, DWORD);
+
+
+class CorpubPublish : public CordbCommonBase, public ICorPublish
+{
+public:
+ CorpubPublish();
+ virtual ~CorpubPublish();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbPublish"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorPublish
+ //-----------------------------------------------------------
+
+ COM_METHOD EnumProcesses(
+ COR_PUB_ENUMPROCESS Type,
+ ICorPublishProcessEnum **ppIEnum);
+
+ COM_METHOD GetProcess(
+ unsigned pid,
+ ICorPublishProcess **ppProcess);
+
+ //-----------------------------------------------------------
+ // CreateObject
+ //-----------------------------------------------------------
+ static COM_METHOD CreateObject(REFIID id, void **object)
+ {
+ *object = NULL;
+
+ if (id != IID_IUnknown && id != IID_ICorPublish)
+ return (E_NOINTERFACE);
+
+ CorpubPublish *pCorPub = new (nothrow) CorpubPublish();
+
+ if (pCorPub == NULL)
+ return (E_OUTOFMEMORY);
+
+ *object = (ICorPublish*)pCorPub;
+ pCorPub->AddRef();
+
+ return (S_OK);
+ }
+
+private:
+ HRESULT GetProcessInternal( unsigned pid, CorpubProcess **ppProcess );
+
+ // Cached information to get the process name. Not available on all platforms, so may be null.
+ HModuleHolder m_hPSAPIdll;
+ FPGetModuleFileNameEx * m_fpGetModuleFileNameEx;
+};
+
+class CorpubProcess : public CordbCommonBase, public ICorPublishProcess
+{
+public:
+ CorpubProcess(DWORD dwProcessId,
+ bool fManaged,
+ HANDLE hProcess,
+ HANDLE hMutex,
+ AppDomainEnumerationIPCBlock *pAD,
+#if !defined(FEATURE_DBGIPC_TRANSPORT_DI)
+ IPCReaderInterface *pIPCReader,
+#endif // !FEATURE_DBGIPC_TRANSPORT_DI
+ FPGetModuleFileNameEx * fpGetModuleFileNameEx);
+ virtual ~CorpubProcess();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CorpubProcess"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorPublishProcess
+ //-----------------------------------------------------------
+ COM_METHOD IsManaged(BOOL *pbManaged);
+
+ /*
+ * Enumerate the list of known application domains in the target process.
+ */
+ COM_METHOD EnumAppDomains(ICorPublishAppDomainEnum **ppEnum);
+
+ /*
+ * Returns the OS ID for the process in question.
+ */
+ COM_METHOD GetProcessID(unsigned *pid);
+
+ /*
+ * Get the display name for a process.
+ */
+ COM_METHOD GetDisplayName(ULONG32 cchName,
+ ULONG32 *pcchName,
+ __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]);
+
+ CorpubProcess *GetNextProcess () { return m_pNext;}
+ void SetNext (CorpubProcess *pNext) { m_pNext = pNext;}
+
+ // Helper to tell if this process has exited
+ bool IsExited();
+
+public:
+ DWORD m_dwProcessId;
+
+private:
+ bool m_fIsManaged;
+ HANDLE m_hProcess;
+ HANDLE m_hMutex;
+ AppDomainEnumerationIPCBlock *m_AppDomainCB;
+#if !defined(FEATURE_DBGIPC_TRANSPORT_DI)
+ IPCReaderInterface *m_pIPCReader; // controls the lifetime of the AppDomainEnumerationIPCBlock
+#endif // !FEATURE_DBGIPC_TRANSPORT_DI
+ CorpubProcess *m_pNext; // pointer to the next process in the process list
+ WCHAR *m_szProcessName;
+
+};
+
+class CorpubAppDomain : public CordbCommonBase, public ICorPublishAppDomain
+{
+public:
+ CorpubAppDomain (__in LPWSTR szAppDomainName, ULONG Id);
+ virtual ~CorpubAppDomain();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CorpubAppDomain"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface (REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorPublishAppDomain
+ //-----------------------------------------------------------
+
+ /*
+ * Get the name and ID for an application domain.
+ */
+ COM_METHOD GetID (ULONG32 *pId);
+
+ /*
+ * Get the name for an application domain.
+ */
+ COM_METHOD GetName (ULONG32 cchName,
+ ULONG32 *pcchName,
+ __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]);
+
+ CorpubAppDomain *GetNextAppDomain () { return m_pNext;}
+ void SetNext (CorpubAppDomain *pNext) { m_pNext = pNext;}
+
+private:
+ CorpubAppDomain *m_pNext;
+ WCHAR *m_szAppDomainName;
+ ULONG m_id;
+
+};
+
+class CorpubProcessEnum : public CordbCommonBase, public ICorPublishProcessEnum
+{
+public:
+ CorpubProcessEnum(CorpubProcess *pFirst);
+ virtual ~CorpubProcessEnum();
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CorpubProcessEnum"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorPublishProcessEnum
+ //-----------------------------------------------------------
+
+ COM_METHOD Skip(ULONG celt);
+ COM_METHOD Reset();
+ COM_METHOD Clone(ICorPublishEnum **ppEnum);
+ COM_METHOD GetCount(ULONG *pcelt);
+ COM_METHOD Next(ULONG celt,
+ ICorPublishProcess *objects[],
+ ULONG *pceltFetched);
+
+private:
+ CorpubProcess *m_pFirst;
+ CorpubProcess *m_pCurrent;
+
+};
+
+class CorpubAppDomainEnum : public CordbCommonBase, public ICorPublishAppDomainEnum
+{
+public:
+ CorpubAppDomainEnum(CorpubAppDomain *pFirst);
+ virtual ~CorpubAppDomainEnum();
+
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbAppDomainEnum"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // ICorPublishAppDomainEnum
+ //-----------------------------------------------------------
+ COM_METHOD Skip(ULONG celt);
+ COM_METHOD Reset();
+ COM_METHOD Clone(ICorPublishEnum **ppEnum);
+ COM_METHOD GetCount(ULONG *pcelt);
+
+ COM_METHOD Next(ULONG celt,
+ ICorPublishAppDomain *objects[],
+ ULONG *pceltFetched);
+
+private:
+ CorpubAppDomain *m_pFirst;
+ CorpubAppDomain *m_pCurrent;
+
+};
+
+#endif // defined(FEATURE_DBG_PUBLISH)
+
+class CordbHeapEnum : public CordbBase, public ICorDebugHeapEnum
+{
+public:
+ CordbHeapEnum(CordbProcess *proc);
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbHeapEnum"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ COM_METHOD Skip(ULONG celt);
+ COM_METHOD Reset();
+ COM_METHOD Clone(ICorDebugEnum **ppEnum);
+ COM_METHOD GetCount(ULONG *pcelt);
+
+ COM_METHOD Next(ULONG celt,
+ COR_HEAPOBJECT objects[],
+ ULONG *pceltFetched);
+
+ virtual void Neuter()
+ {
+ Clear();
+ CordbBase::Neuter();
+ }
+private:
+ void Clear();
+
+private:
+ IDacDbiInterface::HeapWalkHandle mHeapHandle;
+};
+
+
+class CordbRefEnum : public CordbBase, public ICorDebugGCReferenceEnum
+{
+public:
+ CordbRefEnum(CordbProcess *proc, BOOL walkWeakRefs);
+ CordbRefEnum(CordbProcess *proc, CorGCReferenceType types);
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbHeapEnum"; }
+#endif
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ COM_METHOD Skip(ULONG celt);
+ COM_METHOD Reset();
+ COM_METHOD Clone(ICorDebugEnum **ppEnum);
+ COM_METHOD GetCount(ULONG *pcelt);
+
+ COM_METHOD Next(ULONG celt,
+ COR_GC_REFERENCE refs[],
+ ULONG *pceltFetched);
+
+ virtual void Neuter();
+
+private:
+ RefWalkHandle mRefHandle;
+ BOOL mEnumStacksFQ;
+ UINT32 mHandleMask;
+};
+
+// Since the hash table of modules is per app domain (and
+// threads is per process) (for fast lookup from the appdomain/process),
+// we need this wrapper
+// here which allows us to iterate through an assembly's
+// modules. Is basically filters out modules/threads that aren't
+// in the assembly/appdomain. This slow & awkward for assemblies, but fast
+// for the common case - appdomain lookup.
+class CordbEnumFilter : public CordbBase,
+ public ICorDebugThreadEnum,
+ public ICorDebugModuleEnum
+{
+public:
+ CordbEnumFilter(CordbBase * pOwnerObj, NeuterList * pOwnerList);
+ CordbEnumFilter(CordbEnumFilter*src);
+ virtual ~CordbEnumFilter();
+
+ virtual void Neuter();
+
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { return "CordbEnumFilter"; }
+#endif
+
+
+ //-----------------------------------------------------------
+ // IUnknown
+ //-----------------------------------------------------------
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return (BaseAddRef());
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return (BaseRelease());
+ }
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //-----------------------------------------------------------
+ // Common methods
+ //-----------------------------------------------------------
+ COM_METHOD Skip(ULONG celt);
+ COM_METHOD Reset();
+ COM_METHOD Clone(ICorDebugEnum **ppEnum);
+ COM_METHOD GetCount(ULONG *pcelt);
+ //-----------------------------------------------------------
+ // ICorDebugModuleEnum
+ //-----------------------------------------------------------
+ COM_METHOD Next(ULONG celt,
+ ICorDebugModule *objects[],
+ ULONG *pceltFetched);
+
+ //-----------------------------------------------------------
+ // ICorDebugThreadEnum
+ //-----------------------------------------------------------
+ COM_METHOD Next(ULONG celt,
+ ICorDebugThread *objects[],
+ ULONG *pceltFetched);
+
+ HRESULT Init (ICorDebugModuleEnum *pModEnum, CordbAssembly *pAssembly);
+ HRESULT Init (ICorDebugThreadEnum *pThreadEnum, CordbAppDomain *pAppDomain);
+
+
+private:
+ HRESULT NextWorker(ULONG celt, ICorDebugModule *objects[], ULONG *pceltFetched);
+ HRESULT NextWorker(ULONG celt,ICorDebugThread *objects[], ULONG *pceltFetched);
+
+ // Owning object is our link to the CordbProcess* tree. Never null until we're neutered.
+ // NeuterList is related to the owning object. Need to cache it so that we can pass it on
+ // to our clones.
+ CordbBase * m_pOwnerObj; // provides us w/ a CordbProcess*
+ NeuterList * m_pOwnerNeuterList;
+
+
+ EnumElement *m_pFirst;
+ EnumElement *m_pCurrent;
+ int m_iCount;
+};
+
+// Helpers to double-check the RS results against DAC.
+#if defined(_DEBUG)
+void CheckAgainstDAC(CordbFunction * pFunc, void * pIP, mdMethodDef mdExpected);
+#endif
+
+HRESULT CopyOutString(const WCHAR * pInputString, ULONG32 cchName, ULONG32 * pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]);
+
+
+
+inline UINT AllocCookieCordbEval(CordbProcess *pProc, CordbEval* p)
+{
+ _ASSERTE(pProc->GetProcessLock()->HasLock());
+ return pProc->m_EvalTable.Add(p);
+}
+inline CordbEval * UnwrapCookieCordbEval(CordbProcess *pProc, UINT cookie)
+{
+ _ASSERTE(pProc->GetProcessLock()->HasLock());
+ return pProc->m_EvalTable.LookupAndRemove(cookie);
+}
+
+
+// We defined this at the top of the file - undef it now so that we don't pollute other files.
+#undef CRITICAL_SECTION
+
+
+#ifdef RSCONTRACTS
+
+//-----------------------------------------------------------------------------
+// For debug builds, we maintain some thread-state to track debug bits
+// to help us do some more aggressive asserts.
+//-----------------------------------------------------------------------------
+
+class PublicAPIHolder;
+class PublicReentrantAPIHolder;
+class PublicCallbackHolder;
+class PublicDebuggerErrorCallbackHolder;
+
+class DbgRSThread
+{
+public:
+ friend class PublicAPIHolder;
+ friend class PublicReentrantAPIHolder;
+ friend class PublicCallbackHolder;
+ friend class PublicDebuggerErrorCallbackHolder;
+ friend class PrivateShimCallbackHolder;
+
+ DbgRSThread();
+
+ // The TLS slot that we'll put this thread object in.
+ static DWORD s_TlsSlot;
+
+ static LONG s_Total; // Total count of thread objects
+
+ // Get a thread object for the current thread via a TLS lookup.
+ static DbgRSThread * GetThread();
+
+ // Call during DllMain to release this.
+ static DbgRSThread * Create()
+ {
+ InterlockedIncrement(&s_Total);
+
+ DbgRSThread * p = new (nothrow) DbgRSThread();
+ BOOL f = TlsSetValue(s_TlsSlot, p);
+ _ASSERT(f);
+ return p;
+ }
+
+ void Destroy()
+ {
+ InterlockedDecrement(&s_Total);
+
+ BOOL f = TlsSetValue(s_TlsSlot, NULL);
+ _ASSERT(f);
+
+ delete this;
+ }
+
+ // Return true if this thread is inside the RS.
+ bool IsInRS() { return m_cInsideRS > 0; }
+
+ // Locking API..
+ // These will assert if the operation is unsafe.
+ void NotifyTakeLock(RSLock * pLock);
+ void NotifyReleaseLock(RSLock * pLock);
+
+ // Used to map other resources (like thread access) into the lock hierachy.
+ // Note this only effects lock leveling checks and doesn't effect HoldsAnyLock().
+ void TakeVirtualLock(RSLock::ERSLockLevel level);
+ void ReleaseVirtualLock(RSLock::ERSLockLevel level);
+
+ // return true if this thread is holding any RS locks. Useful to check on Public API transition boundaries.
+ bool HoldsAnyDbgApiLocks() { return m_cTotalDbgApiLocks > 0; }
+
+ enum EThreadType
+ {
+ cOther,
+ cW32ET
+ };
+ void SetThreadType(EThreadType e) { m_eThreadType = e; }
+
+ bool IsWin32EventThread() { return m_eThreadType == cW32ET; }
+
+ void SetUnrecoverableCallback(bool fIsUnrecoverableErrorCallback)
+ {
+ // Not reentrant.
+ _ASSERTE(m_fIsUnrecoverableErrorCallback != fIsUnrecoverableErrorCallback);
+
+ m_fIsUnrecoverableErrorCallback = fIsUnrecoverableErrorCallback;
+ }
+
+ inline void AssertThreadIsLockFree()
+ {
+ // If we're in an unrecoverable callback, we may hold locks.
+ _ASSERTE(m_fIsUnrecoverableErrorCallback
+ || !HoldsAnyDbgApiLocks() ||
+ !"Thread should not have locks on public/internal transition");
+ }
+
+protected:
+ EThreadType m_eThreadType;
+
+ // More debugging tidbits - tid that we're on, and a sanity checking cookie.
+ DWORD m_tid;
+ DWORD m_Cookie;
+
+ enum ECookie
+ {
+ COOKIE_VALUE = 0x12345678
+ };
+
+
+ // This tells us if the thread is currently in the scope of a PublicAPIHolder.
+ int m_cInsideRS;
+
+ // This tells us if a thread is currently being dispatched via a callback.
+ bool m_fIsInCallback;
+
+ // We explicitly track if this thread is in an unrecoverable error callback
+ // b/c that will weaken some other asserts.
+ // It would be nice to clean up the unrecoverable error callback and have it
+ // behave like all the other callbacks. Then we can remove this.
+ bool m_fIsUnrecoverableErrorCallback;
+
+ // Locking context. Used to tell what levels of locks we hold so we can determine if a lock is safe to take.
+ int m_cLocks[RSLock::LL_MAX];
+ int m_cTotalDbgApiLocks;
+};
+
+//-----------------------------------------------------------------------------
+// Mark when we enter / exit public APIs
+//-----------------------------------------------------------------------------
+
+// Holder for Non-reentrant Public API (this is the vast majority)
+class PublicAPIHolder
+{
+public:
+ PublicAPIHolder()
+ {
+ // on entry
+ DbgRSThread * pThread = DbgRSThread::GetThread();
+ pThread->m_cInsideRS++;
+ _ASSERTE(pThread->m_cInsideRS == 1 || !"Non-reentrant API being called re-entrantly");
+
+ // Should never be in public w/ these locks
+ pThread->AssertThreadIsLockFree();
+ }
+ ~PublicAPIHolder() {
+ // On exit.
+ DbgRSThread * pThread = DbgRSThread::GetThread();
+ pThread->m_cInsideRS--;
+ _ASSERTE(!pThread->IsInRS());
+
+ // Should never be in public w/ these locks. If we assert here,
+ // then we're leaking locks.
+ pThread->AssertThreadIsLockFree();
+ }
+};
+
+// Holder for reentrant public API
+class PublicReentrantAPIHolder
+{
+public:
+ PublicReentrantAPIHolder()
+ {
+ // on entry
+ DbgRSThread * pThread = DbgRSThread::GetThread();
+ pThread->m_cInsideRS++;
+
+ // Cache count now so that we can calidate it in the dtor.
+ m_oldCount = pThread->m_cInsideRS;
+ // Since a we may have been called from within the RS, we may hold locks
+ }
+ ~PublicReentrantAPIHolder()
+ {
+
+ // On exit.
+ DbgRSThread * pThread = DbgRSThread::GetThread();
+
+ // Ensure that our children were balanced
+ _ASSERTE(pThread->m_cInsideRS == m_oldCount);
+
+ pThread->m_cInsideRS--;
+ _ASSERTE(pThread->m_cInsideRS >= 0);
+
+ // Since a we may have been called from within the RS, we may hold locks
+ }
+private:
+ int m_oldCount;
+};
+
+// Special holder for DebuggerError callback. This adjusts InsideRS count w/o
+// verifying locks. This is very dangerous. We allow this b/c the Debugger Error callback can come at any time.
+class PublicDebuggerErrorCallbackHolder
+{
+public:
+ PublicDebuggerErrorCallbackHolder()
+ {
+ // Exiting from RS; entering Cordbg via a callback
+ DbgRSThread * pThread = DbgRSThread::GetThread();
+
+ // This callback is called from within the RS
+ _ASSERTE(pThread->IsInRS());
+
+ // Debugger error callback may be called from deep within the RS (after many nestings).
+ // So immediately jump to outside. We'll restore this in dtor.
+ m_oldCount = pThread->m_cInsideRS;
+ pThread->m_cInsideRS = 0;
+
+ _ASSERTE(!pThread->IsInRS());
+
+ // We may be leaking locks for the unrecoverable callback. We mark that so that
+ // the asserts about locking can be relaxed.
+ pThread->SetUnrecoverableCallback(true);
+ }
+
+ ~PublicDebuggerErrorCallbackHolder()
+ {
+ // Re-entering RS from after a callback.
+ DbgRSThread * pThread = DbgRSThread::GetThread();
+
+ pThread->SetUnrecoverableCallback(false);
+ pThread->m_cInsideRS = m_oldCount;
+
+ // Our status of being "Inside the RS" is now restored.
+ _ASSERTE(pThread->IsInRS());
+ }
+private:
+ int m_oldCount;
+};
+
+//---------------------------------------------------------------------------------------
+//
+// This is the same as the PublicCallbackHolder, except that this class doesn't assert that we are not holding
+// any locks when we call out to the shim.
+//
+// Notes:
+// @dbgtodo shim, synchronization - We need to settle on one consistent relationshipo between the RS
+// and the shim. Then we can clean up the sychronization story. Right now some code considers the shim
+// to be outside of the RS, and so we cannot hold any locks when we call out to the shim. However, there
+// are cases where we must hold a lock when we call out to the shim. For example, when we call out to the
+// shim to do a V2-style stackwalk, we need to be holding the stop-go lock so that another thread can't
+// come in and call Continue(). Finally, when we fix this, we should fix
+// PUBLIC_REENTRANT_API_ENTRY_FOR_SHIM() as well.
+//
+
+class PrivateShimCallbackHolder
+{
+public:
+ PrivateShimCallbackHolder()
+ {
+ // Exiting from RS; entering Cordbg via a callback
+ DbgRSThread * pThread = DbgRSThread::GetThread();
+
+ // This callback is called from within the RS
+ _ASSERTE(pThread->IsInRS());
+
+ // Debugger error callback may be called from deep within the RS (after many nestings).
+ // So immediately jump to outside. We'll restore this in dtor.
+ m_oldCount = pThread->m_cInsideRS;
+ pThread->m_cInsideRS = 0;
+
+ _ASSERTE(!pThread->IsInRS());
+ }
+
+ ~PrivateShimCallbackHolder()
+ {
+ // Re-entering RS from after a callback.
+ DbgRSThread * pThread = DbgRSThread::GetThread();
+
+ pThread->m_cInsideRS = m_oldCount;
+
+ // Our status of being "Inside the RS" is now restored.
+ _ASSERTE(pThread->IsInRS());
+ }
+private:
+ int m_oldCount;
+};
+
+class InternalAPIHolder
+{
+public:
+ InternalAPIHolder()
+ {
+ DbgRSThread * pThread = DbgRSThread::GetThread();
+
+ // Internal APIs should already be inside the RS.
+ _ASSERTE(pThread->IsInRS() ||!"Internal API being called directly from outside (there should be a public API on the stack)");
+ }
+ void dummy() {}
+};
+
+//---------------------------------------------------------------------------------------
+//
+// This is a simple holder to assert that the current thread is holding the process lock. The purpose of
+// having this holder is to enforce a lock ordering between the process lock in the RS and the DD lock in DAC.
+// If a thread needs to take the process lock, it must do so BEFORE taking the DD lock. Otherwise we could have
+// a deadlock between the process lock and the DD lock.
+//
+// Normally we take the process lock before calling out to DAC, and every DAC API takes the DD lock on entry.
+// Moreover, normally DAC doesn't call back into the RS. The exceptions we currently have are:
+// 1) enumeration callbacks (e.g. code:CordbProcess::AppDomainEnumerationCallback)
+// 2) code:IDacDbiInterface::IMetaDataLookup
+// 3) code:IDacDbiInterface::IAllocator
+// 4) code:IStringHolder
+//
+// Note that the last two are fine because they don't need to take the process lock. The first two categories
+// need to take the process lock before calling into DAC to avoid potential deadlocks.
+//
+
+class InternalDacCallbackHolder
+{
+public:
+ InternalDacCallbackHolder(CordbProcess * pProcess)
+ {
+ _ASSERTE(pProcess->ThreadHoldsProcessLock());
+ }
+};
+
+// cotract that occurs at public builds.
+#define PUBLIC_CONTRACT \
+ CONTRACTL { NOTHROW; } CONTRACTL_END;
+
+
+// Private hook for Shim to call into DBI.
+// Since Shim is considered outside DBI, we need to mark that we've re-entered.
+// Big difference is that we can throw across this boundary.
+// @dbgtodo private shim hook - Eventually, these will all go away since the shim will be fully public.
+#define PUBLIC_API_ENTRY_FOR_SHIM(_pThis) \
+ PublicAPIHolder __pah;
+
+
+#define PUBLIC_API_UNSAFE_ENTRY_FOR_SHIM(_pThis) \
+ PublicDebuggerErrorCallbackHolder __pahCallback;
+
+// @dbgtodo shim, synchronization - Because of the problem mentioned in the comments for
+// PrivateShimCallbackHolder, we need this macro so that we don't hit an assertion when we come back into
+// the RS from the shim.
+#define PUBLIC_REENTRANT_API_ENTRY_FOR_SHIM(_pThis) \
+ PublicReentrantAPIHolder __pah;
+
+//-----------------------------------------------------------------------------
+// Declare whether an API is public or internal
+// Public APIs have the following:
+// - We may be called concurrently from multiple threads (ie, not thread safe)
+// - This thread does not hold any RS Locks while entering or leaving this function.
+// - May or May-not be reentrant.
+// Internal APIs:
+// - let us specifically mark that we're not a public API, and
+// - we're only being called through a public API.
+//-----------------------------------------------------------------------------
+#define PUBLIC_API_ENTRY(_pThis) \
+ STRESS_LOG2(LF_CORDB, LL_INFO1000, "[Public API '%s', this=0x%p]\n", __FUNCTION__, _pThis); \
+ PUBLIC_CONTRACT; \
+ PublicAPIHolder __pah;
+
+// Mark public APIs that are re-entrant.
+// Very few of our APIs should be re-entrant. Even for field access APIs (like GetXXX), the
+// public version is heavier (eg, checking the HRESULT) so we benefit from having a fast
+// internal version and calling that directly.
+#define PUBLIC_REENTRANT_API_ENTRY(_pThis) \
+ STRESS_LOG2(LF_CORDB, LL_INFO1000, "[Public API (re) '%s', this=0x%p]\n", __FUNCTION__, _pThis); \
+ PUBLIC_CONTRACT; \
+ PublicReentrantAPIHolder __pah;
+
+
+
+// Mark internal APIs.
+// All internal APIs are reentrant (duh)
+#define INTERNAL_API_ENTRY(_pThis) InternalAPIHolder __pah; __pah.dummy();
+
+// Mark an internal API from ATT_REQUIRE_STOP / ATT_ALLOW_LIVE_DO_STOP_GO.
+// This can assert that we're safe to send IPC events (that we're stopped and hold the SG lock)
+// @dbgtodo synchronization - in V2, this would assert that we were synced.
+// In V3, our definition of Sync is in flux. Need to resolve this with the synchronization feature crew.
+#define INTERNAL_SYNC_API_ENTRY(pProc) \
+ CordbProcess * __pProc = (pProc); \
+ _ASSERTE(__pProc->GetStopGoLock()->HasLock() || !"Must have stop go lock for internal-sync-api"); \
+ InternalAPIHolder __pah; __pah.dummy();
+
+
+
+// Mark that a thread is owned by us. Thus the thread's "Inside RS" count > 0.
+#define INTERNAL_THREAD_ENTRY(_pThis) \
+ STRESS_LOG1(LF_CORDB, LL_INFO1000, "[Internal thread started, this=0x%p]\n", _pThis); \
+ PUBLIC_CONTRACT; \
+ PublicAPIHolder __pah;
+
+// @dbgtodo unrecoverable error - This sould be deprecated once we deprecate UnrecoverableError.
+#define PUBLIC_CALLBACK_IN_THIS_SCOPE_DEBUGGERERROR(_pThis) \
+ PublicDebuggerErrorCallbackHolder __pahCallback;
+
+#define PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(_pThis) \
+ PrivateShimCallbackHolder __pahCallback;
+
+// Mark places where DAC may call back into DBI. We need to assert that we are holding the process lock in
+// these places, since otherwise we could deadlock between the DD lock and the process lock.
+#define INTERNAL_DAC_CALLBACK(__pProcess) \
+ InternalDacCallbackHolder __idch(__pProcess);
+
+
+// Helper to log debug events.
+inline void StressLogNativeDebugEvent(const DEBUG_EVENT * pDebugEvent, bool fOOB)
+{
+ if ((pDebugEvent)->dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
+ {
+ STRESS_LOG4(LF_CORDB, LL_EVERYTHING, "[Dispatching Win32 code=1 (EXCEPTION_DEBUG_EVENT, tid=%x, oob=%d, code=0x%x, 1st=%d]\n",
+ pDebugEvent->dwThreadId,
+ fOOB,
+ pDebugEvent->u.Exception.ExceptionRecord.ExceptionCode,
+ pDebugEvent->u.Exception.dwFirstChance);
+ }
+ else
+ {
+ STRESS_LOG3(LF_CORDB, LL_EVERYTHING, "[Dispatching Win32 code=%d, tid=%x, oob=%d.]\n",
+ pDebugEvent->dwDebugEventCode, pDebugEvent->dwThreadId, fOOB);
+ }
+
+}
+
+#define PUBLIC_WIN32_CALLBACK_IN_THIS_SCOPE(_pThis, _pDebugEvent, _fOOB) \
+ StressLogNativeDebugEvent(_pDebugEvent, _fOOB); \
+ PublicCallbackHolder __pahCallback(DB_IPCE_INVALID_EVENT);
+
+// Visisbility spec for dtors.
+// Currently, dtors are like public methods b/c they can be called from Release.
+// But they're also reentrant since they may be called from an internal-release.
+// @todo - we'd like to get all "useful" work out of the dtor; in which case we may
+// be able to change this to something more aggressive.
+#define DTOR_ENTRY(_pThis) PUBLIC_REENTRANT_API_ENTRY(_pThis)
+
+
+//-----------------------------------------------------------------------------
+// Typesafe bool for thread safety. This typesafety forces us to use
+// an specific reason for thread-safety, taken from a well-known list.
+// This is mostly concerned w/ being serialized.
+// Note that this assertion must be done on a per function basis and we
+// can't have any sort of 'ThreadSafetyReason CallerIsSafe()' b/c we can't
+// enforce that all of our callers are thread safe (only that our current caller is safe).
+//-----------------------------------------------------------------------------
+struct ThreadSafetyReason
+{
+public:
+ ThreadSafetyReason(bool f) { fIsSafe = f; }
+
+ bool fIsSafe;
+};
+
+// Different valid reasons that we may be threads safe.
+inline ThreadSafetyReason HoldsLock(RSLock * pLock)
+{
+ _ASSERTE(pLock != NULL);
+ return ThreadSafetyReason(pLock->HasLock());
+}
+inline ThreadSafetyReason OnW32ET(CordbProcess * pProc)
+{
+ return ThreadSafetyReason(IsWin32EventThread(pProc));
+}
+
+inline ThreadSafetyReason OnRCET(Cordb *pCordb)
+{
+ return ThreadSafetyReason (IsRCEventThread(pCordb));
+}
+
+// We use this when we assume that a function is thread-safe (b/c it's serialized).
+// The reason also lets us assert that our assumption is true.
+// By using a function, we enforce typesafety and thus require a valid reason
+// (as opposed to an arbitrary bool)
+inline void AssertThreadSafeHelper(ThreadSafetyReason r) {
+ _ASSERTE(r.fIsSafe);
+}
+
+//-----------------------------------------------------------------------------
+// Assert that the given scope is always called on a single thread b/c of
+// xReason. Common reasons may be b/c we hold a lock or we're always
+// called on a specific thread (Eg w32et).
+// The only valid reasons are of type ThreadSafetyReason (thus forcing us to
+// choose from a well-known list of valid reasons).
+//-----------------------------------------------------------------------------
+#define ASSERT_SINGLE_THREAD_ONLY(xReason) \
+ AssertThreadSafeHelper(xReason);
+
+#else
+
+//-----------------------------------------------------------------------------
+// Retail versions just nop. See the debug implementation for these
+// for their semantics.
+//-----------------------------------------------------------------------------
+
+#define PUBLIC_CONTRACT
+#define PUBLIC_API_ENTRY_FOR_SHIM(_pThis)
+#define PUBLIC_API_UNSAFE_ENTRY_FOR_SHIM(_pThis)
+#define PUBLIC_REENTRANT_API_ENTRY_FOR_SHIM(_pThis)
+#define PUBLIC_API_ENTRY(_pThis)
+#define PUBLIC_REENTRANT_API_ENTRY(_pThis)
+#define INTERNAL_API_ENTRY(_pThis)
+#define INTERNAL_SYNC_API_ENTRY(pProc)
+#define INTERNAL_THREAD_ENTRY(_pThis)
+#define PUBLIC_CALLBACK_IN_THIS_SCOPE_DEBUGGERERROR(_pThis)
+#define PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(_pThis)
+#define INTERNAL_DAC_CALLBACK(__pProcess)
+#define PUBLIC_WIN32_CALLBACK_IN_THIS_SCOPE(_pThis, _pDebugEvent, _fOOB)
+#define DTOR_ENTRY(_pThis)
+
+
+#define ASSERT_SINGLE_THREAD_ONLY(x)
+
+#endif // #if RSCONTRACTS
+
+
+class PublicCallbackHolder
+{
+public:
+ PublicCallbackHolder(RSLockHolder * pHolder, DebuggerIPCEventType type)
+ {
+ m_pHolder = pHolder;
+ _ASSERTE(!pHolder->IsNull()); // acquired
+
+ // Release the lock. We'll reacquire it at the dtor.
+ m_pHolder->Release();
+
+ Init(type);
+ }
+
+ PublicCallbackHolder(DebuggerIPCEventType type)
+ {
+ m_pHolder = NULL;
+ Init(type);
+ }
+
+ void Init(DebuggerIPCEventType type)
+ {
+ m_type = type;
+
+#if defined(RSCONTRACTS)
+ // Exiting from RS; entering Cordbg via a callback
+ DbgRSThread * pThread = DbgRSThread::GetThread();
+
+ // m_cInsideRS may be arbitrarily large if we're called from a PUBLIC_REENTRANT_API,
+ // so just remember the current count and blast it back to 0.
+ m_oldCount = pThread->m_cInsideRS;
+ pThread->m_cInsideRS = 0;
+
+ _ASSERTE(!pThread->IsInRS());
+
+ // Should never be in public w/ these locks. (Even if we're re-entrant.)
+ pThread->AssertThreadIsLockFree();
+#endif // RSCONTRACTS
+ }
+
+ ~PublicCallbackHolder()
+ {
+#if defined(RSCONTRACTS)
+ // Re-entering RS from after a callback.
+ DbgRSThread * pThread = DbgRSThread::GetThread();
+ _ASSERTE(!pThread->IsInRS());
+
+ pThread->m_cInsideRS = m_oldCount;
+
+ // Should never be in public w/ these locks. (Even if we're re-entrant.)
+ pThread->AssertThreadIsLockFree();
+#endif // RSCONTRACTS
+
+ // Reacquire the lock
+ if (m_pHolder != NULL)
+ {
+ m_pHolder->Acquire();
+ }
+ }
+protected:
+ int m_oldCount;
+ DebuggerIPCEventType m_type;
+ RSLockHolder * m_pHolder;
+};
+
+
+// Mark that a thread is calling out via a callback. This will adjust the "Inside RS" counter.
+#define PUBLIC_CALLBACK_IN_THIS_SCOPE(_pThis, pLockHolder, event) \
+ STRESS_LOG1(LF_CORDB, LL_EVERYTHING, "[Dispatching '%s']\n", IPCENames::GetName((event)->type)); \
+ PublicCallbackHolder __pahCallback(pLockHolder, (event)->type);
+
+#define PUBLIC_CALLBACK_IN_THIS_SCOPE1(_pThis, pLockHolder, event, formatLiteralString, arg0) \
+ STRESS_LOG2(LF_CORDB, LL_EVERYTHING, "[Dispatching '%s' " formatLiteralString "]\n", IPCENames::GetName((event)->type), arg0); \
+ PublicCallbackHolder __pahCallback(pLockHolder, (event)->type);
+
+#define PUBLIC_CALLBACK_IN_THIS_SCOPE2(_pThis, pLockHolder, event, formatLiteralString, arg0, arg1) \
+ STRESS_LOG3(LF_CORDB, LL_EVERYTHING, "[Dispatching '%s' " formatLiteralString "]\n", IPCENames::GetName((event)->type), arg0, arg1); \
+ PublicCallbackHolder __pahCallback(pLockHolder, (event)->type);
+
+#define PUBLIC_CALLBACK_IN_THIS_SCOPE3(_pThis, pLockHolder, event, formatLiteralString, arg0, arg1, arg2) \
+ STRESS_LOG4(LF_CORDB, LL_EVERYTHING, "[Dispatching '%s' " formatLiteralString "]\n", IPCENames::GetName((event)->type), arg0, arg1, arg2); \
+ PublicCallbackHolder __pahCallback(pLockHolder, (event)->type);
+
+
+#define PUBLIC_CALLBACK_IN_THIS_SCOPE0_NO_LOCK(_pThis) \
+ PublicCallbackHolder __pahCallback(DB_IPCE_INVALID_EVENT);
+
+#define PUBLIC_CALLBACK_IN_THIS_SCOPE0(_pThis, pLockHolder) \
+ PublicCallbackHolder __pahCallback(pLockHolder, DB_IPCE_INVALID_EVENT);
+
+
+//-----------------------------------------------------------------------------
+// Helpers
+inline void ValidateOrThrow(const void * p)
+{
+ if (p == NULL)
+ {
+ ThrowHR(E_INVALIDARG);
+ }
+}
+
+// aligns argBase on platforms that require it else it's a no-op
+inline void AlignAddressForType(CordbType* pArgType, CORDB_ADDRESS& argBase)
+{
+#ifdef DBG_TARGET_ARM
+// TODO: review the following
+#ifdef FEATURE_64BIT_ALIGNMENT
+ BOOL align = FALSE;
+ HRESULT hr = pArgType->RequiresAlign8(&align);
+ _ASSERTE(SUCCEEDED(hr));
+
+ if (align)
+ argBase = ALIGN_ADDRESS(argBase, 8);
+#endif // FEATURE_64BIT_ALIGNMENT
+#endif // DBG_TARGET_ARM
+}
+
+//-----------------------------------------------------------------------------
+// Macros to mark public ICorDebug functions
+// Usage:
+//
+// HRESULT CordbXYZ:Function(...)
+// {
+// HRESULT hr = S_OK;
+// PUBLIC_API_BEGIN(this);
+// // body, may throw
+// PUBLIC_API_END(hr);
+// return hr;
+// }
+#define PUBLIC_API_BEGIN(__this) \
+ CordbBase * __pThis = (__this); \
+ PUBLIC_API_ENTRY(__pThis); \
+ EX_TRY { \
+ RSLockHolder __lockHolder(__pThis->GetProcess()->GetProcessLock()); \
+ THROW_IF_NEUTERED(__pThis); \
+
+// You should not use this in general. We're adding it as a temporary workaround for a
+// particular scenario until we do the synchronization feature crew
+#define PUBLIC_API_NO_LOCK_BEGIN(__this) \
+ CordbBase * __pThis = (__this); \
+ PUBLIC_API_ENTRY(__pThis); \
+ EX_TRY { \
+ THROW_IF_NEUTERED(__pThis); \
+
+// Some APIs (that invoke callbacks), need to toggle the lock.
+#define GET_PUBLIC_LOCK_HOLDER() (&__lockHolder)
+
+#define PUBLIC_API_END(__hr) \
+ } EX_CATCH_HRESULT(__hr); \
+
+// @todo: clean up API constracts. Should we really be taking the Process lock for
+// reentrant APIS??
+#define PUBLIC_REENTRANT_API_BEGIN(__this) \
+ CordbBase * __pThis = (__this); \
+ PUBLIC_REENTRANT_API_ENTRY(__pThis); \
+ EX_TRY { \
+ RSLockHolder __lockHolder(__pThis->GetProcess()->GetProcessLock()); \
+ THROW_IF_NEUTERED(__pThis); \
+
+#define PUBLIC_REENTRANT_API_END(__hr) \
+ } EX_CATCH_HRESULT(__hr); \
+
+// If an API needs to take the stop/go lock as well as the process lock, the
+// stop/go lock has to be taken first. This is an alternative to PUBLIC_REENTRANT_API_BEGIN
+// that allows this, since it doesn't take the process lock. It should be closed with
+// PUBLIC_REENTRANT_API_END
+#define PUBLIC_REENTRANT_API_NO_LOCK_BEGIN(__this) \
+ CordbBase * __pThis = (__this); \
+ PUBLIC_REENTRANT_API_ENTRY(__pThis); \
+ EX_TRY { \
+ THROW_IF_NEUTERED(__pThis); \
+
+
+//-----------------------------------------------------------------------------
+// For debugging ease, cache some global values.
+// Include these in retail & free because that's where we need them the most!!
+// Optimized builds may not let us view locals & parameters. So Having these
+// cached as global values should let us inspect almost all of
+// the interesting parts of the RS even in a Retail build!
+//-----------------------------------------------------------------------------
+struct RSDebuggingInfo
+{
+ // There should only be 1 global Cordb object. Store it here.
+ Cordb * m_Cordb;
+
+ // We have lots of processes. Keep a pointer to the most recently touched
+ // (subjective) process, as a hint about what our "current" process is.
+ // If we're only debugging 1 process, this will be sufficient.
+ CordbProcess * m_MRUprocess;
+
+ CordbRCEventThread * m_RCET;
+};
+
+#include "rspriv.inl"
+
+#endif // #if RSPRIV_H
+
+