summaryrefslogtreecommitdiff
path: root/src/debug/di/shimpriv.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/di/shimpriv.h')
-rw-r--r--src/debug/di/shimpriv.h1056
1 files changed, 1056 insertions, 0 deletions
diff --git a/src/debug/di/shimpriv.h b/src/debug/di/shimpriv.h
new file mode 100644
index 0000000000..9c83301009
--- /dev/null
+++ b/src/debug/di/shimpriv.h
@@ -0,0 +1,1056 @@
+// 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.
+//*****************************************************************************
+// shimprivate.h
+//
+
+//
+// private header for RS shim which bridges from V2 to V3.
+//*****************************************************************************
+
+#ifndef SHIMPRIV_H
+#define SHIMPRIV_H
+
+#include "helpers.h"
+
+#include "shimdatatarget.h"
+
+#include <shash.h>
+
+// Forward declarations
+class CordbWin32EventThread;
+class Cordb;
+
+class ShimStackWalk;
+class ShimChain;
+class ShimChainEnum;
+class ShimFrameEnum;
+
+// This struct specifies that it's a hash table of ShimStackWalk * using ICorDebugThread as the key.
+struct ShimStackWalkHashTableTraits : public PtrSHashTraits<ShimStackWalk, ICorDebugThread *> {};
+typedef SHash<ShimStackWalkHashTableTraits> ShimStackWalkHashTable;
+
+
+//---------------------------------------------------------------------------------------
+//
+// Simple struct for storing a void *. This is to be used with a SHash hash table.
+//
+
+struct DuplicateCreationEventEntry
+{
+public:
+ DuplicateCreationEventEntry(void * pKey) : m_pKey(pKey) {};
+
+ // These functions must be defined for DuplicateCreationEventsHashTableTraits.
+ void * GetKey() {return m_pKey;};
+ static UINT32 Hash(void * pKey) {return (UINT32)(size_t)pKey;};
+
+private:
+ void * m_pKey;
+};
+
+// This struct specifies that it's a hash table of DuplicateCreationEventEntry * using a void * as the key.
+// The void * is expected to be an ICDProcess/ICDAppDomain/ICDThread/ICDAssembly/ICDThread interface pointer.
+struct DuplicateCreationEventsHashTableTraits : public PtrSHashTraits<DuplicateCreationEventEntry, void *> {};
+typedef SHash<DuplicateCreationEventsHashTableTraits> DuplicateCreationEventsHashTable;
+
+//
+// Callback that shim provides, which then queues up the events.
+//
+class ShimProxyCallback :
+ public ICorDebugManagedCallback,
+ public ICorDebugManagedCallback2,
+ public ICorDebugManagedCallback3
+{
+ ShimProcess * m_pShim; // weak reference
+ LONG m_cRef;
+
+public:
+ ShimProxyCallback(ShimProcess * pShim);
+ virtual ~ShimProxyCallback() {}
+
+ // Implement IUnknown
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+ COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
+
+ //
+ // Implementation of ICorDebugManagedCallback
+ //
+
+ COM_METHOD Breakpoint( ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ ICorDebugBreakpoint *pBreakpoint);
+
+ COM_METHOD StepComplete( ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ ICorDebugStepper *pStepper,
+ CorDebugStepReason reason);
+
+ COM_METHOD Break( ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *thread);
+
+ COM_METHOD Exception( ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ BOOL unhandled);
+
+ COM_METHOD EvalComplete( ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ ICorDebugEval *pEval);
+
+ COM_METHOD EvalException( ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ ICorDebugEval *pEval);
+
+ COM_METHOD CreateProcess( ICorDebugProcess *pProcess);
+ void QueueCreateProcess( ICorDebugProcess *pProcess);
+
+ COM_METHOD ExitProcess( ICorDebugProcess *pProcess);
+
+ COM_METHOD CreateThread( ICorDebugAppDomain *pAppDomain, ICorDebugThread *thread);
+
+
+ COM_METHOD ExitThread( ICorDebugAppDomain *pAppDomain, ICorDebugThread *thread);
+
+ COM_METHOD LoadModule( ICorDebugAppDomain *pAppDomain, ICorDebugModule *pModule);
+
+ void FakeLoadModule(ICorDebugAppDomain *pAppDomain, ICorDebugModule *pModule);
+
+ COM_METHOD UnloadModule( ICorDebugAppDomain *pAppDomain, ICorDebugModule *pModule);
+
+ COM_METHOD LoadClass( ICorDebugAppDomain *pAppDomain, ICorDebugClass *c);
+
+ COM_METHOD UnloadClass( ICorDebugAppDomain *pAppDomain, ICorDebugClass *c);
+
+ COM_METHOD DebuggerError( ICorDebugProcess *pProcess, HRESULT errorHR, DWORD errorCode);
+
+ COM_METHOD LogMessage( ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ LONG lLevel,
+ __in LPWSTR pLogSwitchName,
+ __in LPWSTR pMessage);
+
+ COM_METHOD LogSwitch( ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ LONG lLevel,
+ ULONG ulReason,
+ __in LPWSTR pLogSwitchName,
+ __in LPWSTR pParentName);
+
+ COM_METHOD CreateAppDomain(ICorDebugProcess *pProcess,
+ ICorDebugAppDomain *pAppDomain);
+
+ COM_METHOD ExitAppDomain(ICorDebugProcess *pProcess,
+ ICorDebugAppDomain *pAppDomain);
+
+ COM_METHOD LoadAssembly(ICorDebugAppDomain *pAppDomain,
+ ICorDebugAssembly *pAssembly);
+
+ COM_METHOD UnloadAssembly(ICorDebugAppDomain *pAppDomain,
+ ICorDebugAssembly *pAssembly);
+
+ COM_METHOD ControlCTrap(ICorDebugProcess *pProcess);
+
+ COM_METHOD NameChange(ICorDebugAppDomain *pAppDomain, ICorDebugThread *pThread);
+
+
+ COM_METHOD UpdateModuleSymbols( ICorDebugAppDomain *pAppDomain,
+ ICorDebugModule *pModule,
+ IStream *pSymbolStream);
+
+ COM_METHOD EditAndContinueRemap( ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ ICorDebugFunction *pFunction,
+ BOOL fAccurate);
+
+ COM_METHOD BreakpointSetError( ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ ICorDebugBreakpoint *pBreakpoint,
+ DWORD dwError);
+
+ ///
+ /// Implementation of ICorDebugManagedCallback2
+ ///
+ COM_METHOD FunctionRemapOpportunity( ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ ICorDebugFunction *pOldFunction,
+ ICorDebugFunction *pNewFunction,
+ ULONG32 oldILOffset);
+
+ COM_METHOD CreateConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId, __in LPWSTR pConnName);
+
+ COM_METHOD ChangeConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId );
+
+
+ COM_METHOD DestroyConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId);
+
+ COM_METHOD Exception(ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ ICorDebugFrame *pFrame,
+ ULONG32 nOffset,
+ CorDebugExceptionCallbackType dwEventType,
+ DWORD dwFlags );
+
+ COM_METHOD ExceptionUnwind(ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ CorDebugExceptionUnwindCallbackType dwEventType,
+ DWORD dwFlags);
+
+ COM_METHOD FunctionRemapComplete( ICorDebugAppDomain *pAppDomain,
+ ICorDebugThread *pThread,
+ ICorDebugFunction *pFunction);
+
+ COM_METHOD MDANotification(ICorDebugController * pController, ICorDebugThread *pThread, ICorDebugMDA * pMDA);
+
+ ///
+ /// Implementation of ICorDebugManagedCallback3
+ ///
+
+ // Implementation of ICorDebugManagedCallback3::CustomNotification
+ COM_METHOD CustomNotification(ICorDebugThread * pThread, ICorDebugAppDomain * pAppDomain);
+
+};
+
+
+//
+// Base class for event queue. These are nested into a singly linked list.
+// Shim maintains event queue
+//
+class ManagedEvent
+{
+public:
+ // Need virtual dtor since this is a base class.
+ virtual ~ManagedEvent();
+
+#ifdef _DEBUG
+ // For debugging, get a pointer value that can identify the type of this event.
+ void * GetDebugCookie();
+#endif
+
+ // We'll have a lot of derived classes of ManagedEvent, and so encapsulating the arguments
+ // for the Dispatch() function lets us juggle them around easily without hitting every signature.
+ class DispatchArgs
+ {
+ public:
+ DispatchArgs(ICorDebugManagedCallback * pCallback1, ICorDebugManagedCallback2 * pCallback2, ICorDebugManagedCallback3 * pCallback3);
+
+ ICorDebugManagedCallback * GetCallback1();
+ ICorDebugManagedCallback2 * GetCallback2();
+ ICorDebugManagedCallback3 * GetCallback3();
+
+
+ protected:
+ ICorDebugManagedCallback * m_pCallback1;
+ ICorDebugManagedCallback2 * m_pCallback2;
+ ICorDebugManagedCallback3 * m_pCallback3;
+ };
+
+ // Returns: value of callback from end-user
+ virtual HRESULT Dispatch(DispatchArgs args) = 0;
+
+
+ // Returns 0 if none.
+ DWORD GetOSTid();
+
+protected:
+ // Ctor for events with thread-affinity
+ ManagedEvent(ICorDebugThread * pThread);
+
+ // Ctor for events without thread affinity.
+ ManagedEvent();
+
+ friend class ManagedEventQueue;
+ ManagedEvent * m_pNext;
+
+ DWORD m_dwThreadId;
+};
+
+//
+// Queue of managed events.
+// Shim can use this to collect managed debug events, queue them, and then drain the event
+// queue when a sync-complete occurs.
+// Event queue gets initialized with a lock and will lock internally.
+class ManagedEventQueue
+{
+public:
+ ManagedEventQueue();
+
+
+ void Init(RSLock * pLock);
+
+ // Remove event from the top. Caller then takes ownership of Event and will call Delete on it.
+ // Caller checks IsEmpty() first.
+ ManagedEvent * Dequeue();
+
+ // Queue owns the event and will delete it (unless it's dequeued first).
+ void QueueEvent(ManagedEvent * pEvent);
+
+ // Test if event queue is empty
+ bool IsEmpty();
+
+ // Empty event queue and delete all objects
+ void DeleteAll();
+
+ // Nothrows
+ BOOL HasQueuedCallbacks(ICorDebugThread * pThread);
+
+ // Save the current queue and start with a new empty queue
+ void SuspendQueue();
+
+ // Restore the saved queue onto the end of the current queue
+ void RestoreSuspendedQueue();
+
+protected:
+ // The lock to be used for synchronizing all access to the queue
+ RSLock * m_pLock;
+
+ // If empty, First + Last are both NULL.
+ // Else first points to the head of the queue; and Last points to the end of the queue.
+ ManagedEvent * m_pFirstEvent;
+ ManagedEvent * m_pLastEvent;
+
+};
+
+
+//---------------------------------------------------------------------------------------
+//
+// Shim's layer on top of a process.
+//
+// Notes:
+// This contains a V3 ICorDebugProcess, and provides V2 ICDProcess functionality.
+//
+class ShimProcess
+{
+ // Delete via Ref count semantics.
+ ~ShimProcess();
+public:
+ // Initialize ref count is 0.
+ ShimProcess();
+
+ // Lifetime semantics handled by reference counting.
+ void AddRef();
+ void Release();
+
+ // Release all resources. Can be called multiple times.
+ void Dispose();
+
+ // Initialization phases.
+ // 1. allocate new ShimProcess(). This lets us spin up a Win32 EventThread, which can then
+ // be used to
+ // 2. Call ShimProcess::CreateProcess/DebugActiveProcess. This will call CreateAndStartWin32ET to
+ // craete the w32et.
+ // 3. Create OS-debugging pipeline. This establishes the physical OS process and gets us a pid/handle
+ // 4. pShim->InitializeDataTarget - this creates a reader/writer abstraction around the OS process.
+ // 5. pShim->SetProcess() - this connects the Shim to the ICDProcess object.
+ HRESULT InitializeDataTarget(DWORD processId);
+ void SetProcess(ICorDebugProcess * pProcess);
+
+ //-----------------------------------------------------------
+ // Creation
+ //-----------------------------------------------------------
+
+ static HRESULT CreateProcess(
+ Cordb * pCordb,
+ ICorDebugRemoteTarget * pRemoteTarget,
+ 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
+ );
+
+ static HRESULT DebugActiveProcess(
+ Cordb * pCordb,
+ ICorDebugRemoteTarget * pRemoteTarget,
+ DWORD pid,
+ BOOL win32Attach
+
+ );
+
+ // Locates the DAC module adjacent to DBI
+ static HMODULE GetDacModule();
+
+ //
+ // Functions used by CordbProcess
+ //
+
+ // Determine if the calling thread is the win32 event thread.
+ bool IsWin32EventThread();
+
+
+ // Expose the W32ET thread to the CordbProcess so that it can emulate V2 behavior
+ CordbWin32EventThread * GetWin32EventThread();
+
+ // Accessor wrapper to mark whether we're interop-debugging.
+ void SetIsInteropDebugging(bool fIsInteropDebugging);
+
+ // Handle a debug event.
+ HRESULT HandleWin32DebugEvent(const DEBUG_EVENT * pEvent);
+
+ ManagedEventQueue * GetManagedEventQueue();
+
+ ManagedEvent * DequeueManagedEvent();
+
+ ShimProxyCallback * GetShimCallback();
+
+ // Begin Queing the fake attach events.
+ void BeginQueueFakeAttachEvents();
+
+ // Queue fake attach events if needed
+ void QueueFakeAttachEventsIfNeeded(bool fRealCreateProcessEvent);
+
+ // Actually do the work to queue the fake attach events.
+ void QueueFakeAttachEvents();
+
+ // Helper to queue fake assembly and mdule events
+ void QueueFakeAssemblyAndModuleEvent(ICorDebugAssembly * pAssembly);
+
+ // Queue fake thread-create events on attach. Order via native threads.
+ HRESULT QueueFakeThreadAttachEventsNativeOrder();
+
+ // Queue fake thread-create events on attach. No ordering.
+ HRESULT QueueFakeThreadAttachEventsNoOrder();
+
+ bool IsThreadSuspendedOrHijacked(ICorDebugThread * pThread);
+
+ // Expose m_attached to CordbProcess.
+ bool GetAttached();
+
+ // We need to know whether we are in the CreateProcess callback to be able to
+ // return the v2.0 hresults from code:CordbProcess::SetDesiredNGENCompilerFlags
+ // when we are using the shim.
+ //
+ // Expose m_fInCreateProcess
+ bool GetInCreateProcess();
+ void SetInCreateProcess(bool value);
+
+ // We need to know whether we are in the FakeLoadModule callback to be able to
+ // return the v2.0 hresults from code:CordbModule::SetJITCompilerFlags when
+ // we are using the shim.
+ //
+ // Expose m_fInLoadModule
+ bool GetInLoadModule();
+ void SetInLoadModule(bool value);
+
+ // When we get a continue, we need to clear the flags indicating we're still in a callback
+ void NotifyOnContinue ();
+
+ // The RS calls this function when the stack is about to be changed in any way, e.g. continue, SetIP,
+ // etc.
+ void NotifyOnStackInvalidate();
+
+ // Helpers to filter HRs to emulate V2 error codes.
+ HRESULT FilterSetNgenHresult(HRESULT hr);
+ HRESULT FilterSetJitFlagsHresult(HRESULT hr);
+
+ //.............................................................
+
+
+ // Lookup or create a ShimStackWalk for the specified thread. ShimStackWalk and ICorDebugThread has
+ // a 1:1 relationship.
+ ShimStackWalk * LookupOrCreateShimStackWalk(ICorDebugThread * pThread);
+
+ // Clear all ShimStackWalks and flush all the caches.
+ void ClearAllShimStackWalk();
+
+ // Get the corresponding ICDProcess object.
+ ICorDebugProcess * GetProcess();
+
+ // Get the data target to access the debuggee.
+ ICorDebugMutableDataTarget * GetDataTarget();
+
+ // Get the native event pipeline
+ INativeEventPipeline * GetNativePipeline();
+
+ // Are we interop-debugging?
+ bool IsInteropDebugging();
+
+
+ // Finish all the necessary initialization work and queue up any necessary fake attach events before
+ // dispatching an event.
+ void PreDispatchEvent(bool fRealCreateProcessEvent = false);
+
+ // Look for a CLR in the process and if found, return it's instance ID
+ HRESULT FindLoadedCLR(CORDB_ADDRESS * pClrInstanceId);
+
+ // Retrieve the IP address and the port number of the debugger proxy.
+ MachineInfo GetMachineInfo();
+
+ // Add an entry in the duplicate creation event hash table for the specified key.
+ void AddDuplicateCreationEvent(void * pKey);
+
+ // Check if a duplicate creation event entry exists for the specified key. If so, remove it.
+ bool RemoveDuplicateCreationEventIfPresent(void * pKey);
+
+ void SetMarkAttachPendingEvent();
+
+ void SetTerminatingEvent();
+
+ RSLock * GetShimLock();
+
+protected:
+
+ // Reference count.
+ LONG m_ref;
+
+ //
+ // Helper functions
+ //
+ HRESULT CreateAndStartWin32ET(Cordb * pCordb);
+
+ //
+ // Synchronization events to ensure that AttachPending bit is marked before DebugActiveProcess
+ // returns or debugger is detaching
+ //
+ HANDLE m_markAttachPendingEvent;
+ HANDLE m_terminatingEvent;
+
+ // Finds the base address of [core]clr.dll
+ CORDB_ADDRESS GetCLRInstanceBaseAddress();
+
+ //
+ // Event Queues
+ //
+
+ // Shim maintains event queue to emulate V2 semantics.
+ // In V2, IcorDebug internally queued debug events and dispatched them
+ // once the debuggee was synchronized. In V3, ICorDebug dispatches events immediately.
+ // The event queue is moved into the shim to build V2 semantics of V3 behavior.
+ ManagedEventQueue m_eventQueue;
+
+ // Lock to protect Shim data structures. This is currently a small lock that
+ // protects leaf-level structures, but it may grow to protect larger things.
+ RSLock m_ShimLock;
+
+ // Serializes ShimProcess:Dispose() with other ShimProcess functions. For now, this
+ // cannot be the same as m_ShimLock. See LL_SHIM_PROCESS_DISPOSE_LOCK for more
+ // information
+ RSLock m_ShimProcessDisposeLock;
+
+ // Sticky bit to do lazy-initialization on the first managed event.
+ bool m_fFirstManagedEvent;
+
+ RSExtSmartPtr<ShimProxyCallback> m_pShimCallback;
+
+
+ // This is for emulating V2 Attach. Initialized to false, and then set to true if we ened to send fake attach events.
+ // Reset to false once the events are sent. See code:ShimProcess::QueueFakeAttachEventsIfNeeded
+ bool m_fNeedFakeAttachEvents;
+
+ // True if the process was created from an attach (DebugActiveProcess); False if it was launched (CreateProcess)
+ // This is used to send an Attach IPC event, and also used to provide more specific error codes.
+ bool m_attached;
+
+ // True iff we are in the shim's CreateProcess callback. This is used to determine which hresult to
+ // return from code:CordbProcess::SetDesiredNGENCompilerFlags so we correctly emulate the behavior of v2.0.
+ // This is set at the beginning of the callback and cleared in code:CordbProcess::ContinueInternal.
+ bool m_fInCreateProcess;
+
+ // True iff we are in the shim's FakeLoadModule callback. This is used to determine which hresult to
+ // return from code:CordbModule::SetJITCompilerFlags so we correctly emulate the behavior of v2.0.
+ // This is set at the beginning of the callback and cleared in code:CordbProcess::ContinueInternal.
+ bool m_fInLoadModule;
+ //
+ // Data
+ //
+
+ // Pointer to CordbProcess.
+ // @dbgtodo shim: We'd like this to eventually go through public interfaces (ICorDebugProcess)
+ IProcessShimHooks * m_pProcess; // Reference is kept by m_pIProcess;
+ RSExtSmartPtr<ICorDebugProcess> m_pIProcess;
+
+ // Win32EvenThread, which is the thread that uses the native debug API.
+ CordbWin32EventThread * m_pWin32EventThread;
+
+ // Actual data-target. Since we're shimming V2 scenarios, and V3 is always
+ // live-debugging, this is always a live data-target.
+ RSExtSmartPtr<ShimDataTarget> m_pLiveDataTarget;
+
+
+ // If true, the shim is emulating interop-debugging
+ // If false, the shim is emulating managed-only debugging.
+ // Both managed and native debugging have the same underlying pipeline (built
+ // on native-debug events). So the only difference is how they handle those events.
+ bool m_fIsInteropDebugging;
+
+ // true iff Dispose() was called. Consult this and do your work under m_ShimProcessDisposeLock
+ // to serialize yourself against a call to Dispose(). This protects your work
+ // from the user doing a Debugger Detach in the middle.
+ bool m_fIsDisposed;
+
+ //.............................................................................
+ //
+ // Members used for handling native events when managed-only debugging.
+ //
+ //.............................................................................
+
+ // Default handler for native events when managed-only debugging.
+ void DefaultEventHandler(const DEBUG_EVENT * pEvent, DWORD * pdwContinueStatus);
+
+ // Given a debug event, track the file handles.
+ void TrackFileHandleForDebugEvent(const DEBUG_EVENT * pEvent);
+
+ // Have we gotten the loader breakpoint yet?
+ // A Debugger needs to do special work to skip the loader breakpoint,
+ // and that's also when it should dispatch the faked managed attach events.
+ bool m_loaderBPReceived;
+
+ // Raw callback for ContinueStatusChanged from Data-target.
+ static HRESULT ContinueStatusChanged(void * pUserData, DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus);
+
+ // Real worker to update ContinueStatusChangedData
+ HRESULT ContinueStatusChangedWorker(DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus);
+
+ struct ContinueStatusChangedData
+ {
+ void Clear();
+ bool IsSet();
+ // Tid of Thread changed
+ DWORD m_dwThreadId;
+
+ // New continue status.
+ CORDB_CONTINUE_STATUS m_status;
+ } m_ContinueStatusChangedData;
+
+ // the hash table of ShimStackWalks
+ ShimStackWalkHashTable * m_pShimStackWalkHashTable;
+
+ // the hash table of duplicate creation events
+ DuplicateCreationEventsHashTable * m_pDupeEventsHashTable;
+
+ MachineInfo m_machineInfo;
+};
+
+
+//---------------------------------------------------------------------------------------
+//
+// This is the container class of ShimChains, ICorDebugFrames, ShimChainEnums, and ShimFrameEnums.
+// It has a 1:1 relationship with ICorDebugThreads. Upon creation, this class walks the entire stack and
+// caches all the stack frames and chains. The enumerators are created on demand.
+//
+
+class ShimStackWalk
+{
+public:
+ ShimStackWalk(ShimProcess * pProcess, ICorDebugThread * pThread);
+ ~ShimStackWalk();
+
+ // These functions do not adjust the reference count.
+ ICorDebugThread * GetThread();
+ ShimChain * GetChain(UINT32 index);
+ ICorDebugFrame * GetFrame(UINT32 index);
+
+ // Get the number of frames and chains.
+ ULONG GetChainCount();
+ ULONG GetFrameCount();
+
+ RSLock * GetShimLock();
+
+ // Add ICDChainEnum and ICDFrameEnum.
+ void AddChainEnum(ShimChainEnum * pChainEnum);
+ void AddFrameEnum(ShimFrameEnum * pFrameEnum);
+
+ // The next two functions are for ShimStackWalkHashTableTraits.
+ ICorDebugThread * GetKey();
+ static UINT32 Hash(ICorDebugThread * pThread);
+
+ // Check if the specified frame is the leaf frame according to the V2 definition.
+ BOOL IsLeafFrame(ICorDebugFrame * pFrame);
+
+ // Check if the two specified frames are the same. This function checks the SPs, frame address, etc.
+ // instead of just checking for pointer equality.
+ BOOL IsSameFrame(ICorDebugFrame * pLeft, ICorDebugFrame * pRight);
+
+ // The following functions are entry point into the ShimStackWalk. They are called by the RS.
+ void EnumerateChains(ICorDebugChainEnum ** ppChainEnum);
+
+ void GetActiveChain(ICorDebugChain ** ppChain);
+ void GetActiveFrame(ICorDebugFrame ** ppFrame);
+ void GetActiveRegisterSet(ICorDebugRegisterSet ** ppRegisterSet);
+
+ void GetChainForFrame(ICorDebugFrame * pFrame, ICorDebugChain ** ppChain);
+ void GetCallerForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCallerFrame);
+ void GetCalleeForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCalleeFrame);
+
+private:
+ //---------------------------------------------------------------------------------------
+ //
+ // This is a helper class used to store the information of a chain during a stackwalk. A chain is marked
+ // by the CONTEXT on the leaf boundary and a FramePointer on the root boundary. Also, notice that we
+ // are keeping two CONTEXTs. This is because some chain types may cancel a previous unmanaged chain.
+ // For example, a CHAIN_FUNC_EVAL chain cancels any CHAIN_ENTER_UNMANAGED chain immediately preceding
+ // it. In this case, the leaf boundary of the CHAIN_FUNC_EVAL chain is marked by the CONTEXT of the
+ // previous CHAIN_ENTER_MANAGED, not the previous CHAIN_ENTER_UNMANAGED.
+ //
+
+ struct ChainInfo
+ {
+ public:
+ ChainInfo() : m_rootFP(LEAF_MOST_FRAME), m_reason(CHAIN_NONE), m_fNeedEnterManagedChain(FALSE), m_fLeafNativeContextIsValid(FALSE) {}
+
+ void CancelUMChain() { m_reason = CHAIN_NONE; }
+ BOOL IsTrackingUMChain() { return (m_reason == CHAIN_ENTER_UNMANAGED); }
+
+ DT_CONTEXT m_leafNativeContext;
+ DT_CONTEXT m_leafManagedContext;
+ FramePointer m_rootFP;
+ CorDebugChainReason m_reason;
+ bool m_fNeedEnterManagedChain;
+ bool m_fLeafNativeContextIsValid;
+ };
+
+ //---------------------------------------------------------------------------------------
+ //
+ // This is a helper class used to store information during a stackwalk. Conceptually it is a simplified
+ // version of FrameInfo used on the LS in V2.
+ //
+
+ struct StackWalkInfo
+ {
+ public:
+ StackWalkInfo();
+ ~StackWalkInfo();
+
+ // Reset all the per-frame information.
+ void ResetForNextFrame();
+
+ // During the stackwalk, we need to find out whether we should process the next stack frame or the
+ // next internal frame. These functions help us determine whether we have exhausted one or both
+ // types of frames. The stackwalk is finished when both types are exhausted.
+ bool ExhaustedAllFrames();
+ bool ExhaustedAllStackFrames();
+ bool ExhaustedAllInternalFrames();
+
+ // Simple helper function to get the current internal frame.
+ ICorDebugInternalFrame2 * GetCurrentInternalFrame();
+
+ // Check whether we are processing the first frame.
+ BOOL IsLeafFrame();
+
+ // Check whether we are skipping frames because of a child frame.
+ BOOL IsSkippingFrame();
+
+ // Indicates whether we are dealing with a converted frame.
+ // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
+ BOOL HasConvertedFrame();
+
+ // Store the child frame we are currently trying to find the parent frame for.
+ // If this is NULL, then we are not skipping frames.
+ RSExtSmartPtr<ICorDebugNativeFrame2> m_pChildFrame;
+
+ // Store the converted frame, if any.
+ RSExtSmartPtr<ICorDebugInternalFrame2> m_pConvertedInternalFrame2;
+
+ // Store the array of internal frames. This is an array of RSExtSmartPtrs, and so each element
+ // is protected, and we only need to call Clear() to release each element and free all the memory.
+ RSExtPtrArray<ICorDebugInternalFrame2> m_ppInternalFrame2;
+
+ UINT32 m_cChain; // number of chains
+ UINT32 m_cFrame; // number of frames
+ UINT32 m_firstFrameInChain; // the index of the first frame in the current chain
+ UINT32 m_cInternalFrames; // number of internal frames
+ UINT32 m_curInternalFrame; // the index of the current internal frame being processed
+
+ CorDebugInternalFrameType m_internalFrameType;
+
+ bool m_fExhaustedAllStackFrames;
+
+ // Indicate whether we are processing an internal frame or a stack frame.
+ bool m_fProcessingInternalFrame;
+
+ // Indicate whether we should skip the current chain because it's a chain derived from a leaf frame
+ // of type TYPE_INTERNAL. This is the behaviour in V2.
+ // See code:DebuggerWalkStackProc.
+ bool m_fSkipChain;
+
+ // Indicate whether the current frame is the first frame we process.
+ bool m_fLeafFrame;
+
+ // Indicate whether we are processing a converted frame.
+ bool m_fHasConvertedFrame;
+ };
+
+ // A ShimStackWalk is deleted when a process is continued, or when the stack is changed in any way
+ // (e.g. SetIP, EnC, etc.).
+ void Populate();
+ void Clear();
+
+ // Get a FramePointer to mark the root boundary of a chain.
+ FramePointer GetFramePointerForChain(DT_CONTEXT * pContext);
+ FramePointer GetFramePointerForChain(ICorDebugInternalFrame2 * pInternalFrame2);
+
+ CorDebugInternalFrameType GetInternalFrameType(ICorDebugInternalFrame2 * pFrame2);
+
+ // Append a frame to the array.
+ void AppendFrame(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo);
+ void AppendFrame(ICorDebugInternalFrame2 * pInternalFrame2, StackWalkInfo * pStackWalkInfo);
+
+ // Append a chain to the array.
+ void AppendChainWorker(StackWalkInfo * pStackWalkInfo,
+ DT_CONTEXT * pLeafContext,
+ FramePointer fpRoot,
+ CorDebugChainReason chainReason,
+ BOOL fIsManagedChain);
+ void AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo);
+
+ // Save information on the ChainInfo regarding the current chain.
+ void SaveChainContext(ICorDebugStackWalk * pSW, ChainInfo * pChainInfo, DT_CONTEXT * pContext);
+
+ // Check what we are process next, a internal frame or a stack frame.
+ BOOL CheckInternalFrame(ICorDebugFrame * pNextStackFrame,
+ StackWalkInfo * pStackWalkInfo,
+ ICorDebugThread3 * pThread3,
+ ICorDebugStackWalk * pSW);
+
+ // Convert an ICDInternalFrame to another ICDInternalFrame due to IL methods without metadata.
+ // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
+ BOOL ConvertInternalFrameToDynamicMethod(StackWalkInfo * pStackWalkInfo);
+
+ // Convert an ICDNativeFrame to an ICDInternalFrame due to IL methods without metadata.
+ // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
+ BOOL ConvertStackFrameToDynamicMethod(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo);
+
+ // Process an unmanaged chain.
+ BOOL ShouldTrackUMChain(StackWalkInfo * pswInfo);
+ void TrackUMChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo);
+
+ // Check whether the internal frame is a newly exposed type in Arrowhead. If so, then the shim should
+ // not expose it.
+ BOOL IsV3FrameType(CorDebugInternalFrameType type);
+
+ // Check whether the specified frame represents a dynamic method.
+ BOOL IsILFrameWithoutMetadata(ICorDebugFrame * pFrame);
+
+ CDynArray<ShimChain *> m_stackChains; // growable ordered array of chains and frames
+ CDynArray<ICorDebugFrame *> m_stackFrames;
+
+ ShimChainEnum * m_pChainEnumList; // linked list of ShimChainEnum and ShimFrameEnum
+ ShimFrameEnum * m_pFrameEnumList;
+
+ // the thread on which we are doing a stackwalk, i.e. the "owning" thread
+ RSExtSmartPtr<ShimProcess> m_pProcess;
+ RSExtSmartPtr<ICorDebugThread> m_pThread;
+};
+
+
+//---------------------------------------------------------------------------------------
+//
+// This class implements the deprecated ICDChain interface.
+//
+
+class ShimChain : public ICorDebugChain
+{
+public:
+ ShimChain(ShimStackWalk * pSW,
+ DT_CONTEXT * pContext,
+ FramePointer fpRoot,
+ UINT32 chainIndex,
+ UINT32 frameStartIndex,
+ UINT32 frameEndIndex,
+ CorDebugChainReason chainReason,
+ BOOL fIsManaged,
+ RSLock * pShimLock);
+ virtual ~ShimChain();
+
+ void Neuter();
+ BOOL IsNeutered();
+
+ //
+ // IUnknown
+ //
+
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+ COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
+
+ //
+ // ICorDebugChain
+ //
+
+ COM_METHOD GetThread(ICorDebugThread ** ppThread);
+ COM_METHOD GetStackRange(CORDB_ADDRESS * pStart, CORDB_ADDRESS * pEnd);
+ COM_METHOD GetContext(ICorDebugContext ** ppContext);
+ COM_METHOD GetCaller(ICorDebugChain ** ppChain);
+ COM_METHOD GetCallee(ICorDebugChain ** ppChain);
+ COM_METHOD GetPrevious(ICorDebugChain ** ppChain);
+ COM_METHOD GetNext(ICorDebugChain ** ppChain);
+ COM_METHOD IsManaged(BOOL * pManaged);
+ COM_METHOD EnumerateFrames(ICorDebugFrameEnum ** ppFrames);
+ COM_METHOD GetActiveFrame(ICorDebugFrame ** ppFrame);
+ COM_METHOD GetRegisterSet(ICorDebugRegisterSet ** ppRegisters);
+ COM_METHOD GetReason(CorDebugChainReason * pReason);
+
+ //
+ // accessors
+ //
+
+ // Get the owning ShimStackWalk.
+ ShimStackWalk * GetShimStackWalk();
+
+ // Get the first and last index of the frame owned by this chain. This class itself doesn't store the
+ // frames. Rather, the frames are stored on the ShimStackWalk. This class just stores the indices.
+ // Note that the indices are [firstIndex, lastIndex), i.e. the last index is exclusive.
+ UINT32 GetFirstFrameIndex();
+ UINT32 GetLastFrameIndex();
+
+private:
+ // A chain describes a stack range within the stack. This includes a CONTEXT at the start (leafmost)
+ // end of the chain, and a frame pointer where the chain ends (rootmost). This stack range is exposed
+ // publicly via ICDChain::GetStackRange(), and can be used to stitch managed and native stack frames
+ // together into a unified stack.
+ DT_CONTEXT m_context; // the leaf end of the chain
+ FramePointer m_fpRoot; // the root end of the chain
+
+ ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
+ Volatile<ULONG> m_refCount;
+
+ // The 0-based index of this chain in the ShimStackWalk's chain array (m_pStackWalk->m_stackChains).
+ UINT32 m_chainIndex;
+
+ // The 0-based index of the first frame owned by this chain in the ShimStackWalk's frame array
+ // (m_pStackWalk->m_stackFrames). See code::ShimChain::GetFirstFrameIndex().
+ UINT32 m_frameStartIndex;
+
+ // The 0-based index of the last frame owned by this chain in the ShimStackWalk's frame array
+ // (m_pStackWalk->m_stackFrames). This index is exlusive. See code::ShimChain::GetLastFrameIndex().
+ UINT32 m_frameEndIndex;
+
+ CorDebugChainReason m_chainReason;
+ BOOL m_fIsManaged; // indicates whether this chain contains managed frames
+ BOOL m_fIsNeutered;
+
+ RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
+};
+
+
+//---------------------------------------------------------------------------------------
+//
+// This class implements the deprecated ICDChainEnum interface.
+//
+
+class ShimChainEnum : public ICorDebugChainEnum
+{
+public:
+ ShimChainEnum(ShimStackWalk * pSW, RSLock * pShimLock);
+ virtual ~ShimChainEnum();
+
+ void Neuter();
+ BOOL IsNeutered();
+
+ //
+ // IUnknown
+ //
+
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+ 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 * pcChains);
+
+ //
+ // ICorDebugChainEnum
+ //
+
+ COM_METHOD Next(ULONG cChains, ICorDebugChain * rgpChains[], ULONG * pcChainsFetched);
+
+ //
+ // accessors
+ //
+
+ // used to link ShimChainEnums in a list
+ ShimChainEnum * GetNext();
+ void SetNext(ShimChainEnum * pNext);
+
+private:
+ ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
+
+ // This points to the next ShimChainEnum in the linked list of ShimChainEnums to be cleaned up.
+ // The head of the list is on the ShimStackWalk (m_pStackWalk->m_pChainEnumList).
+ ShimChainEnum * m_pNext;
+
+ UINT32 m_currentChainIndex; // the index of the current ShimChain being enumerated
+ Volatile<ULONG> m_refCount;
+ BOOL m_fIsNeutered;
+
+ RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
+};
+
+
+//---------------------------------------------------------------------------------------
+//
+// This class implements the deprecated ICDFrameEnum interface.
+//
+
+class ShimFrameEnum : public ICorDebugFrameEnum
+{
+public:
+ ShimFrameEnum(ShimStackWalk * pSW, ShimChain * pChain, UINT32 frameStartIndex, UINT32 frameEndIndex, RSLock * pShimLock);
+ virtual ~ShimFrameEnum();
+
+ void Neuter();
+ BOOL IsNeutered();
+
+ //
+ // IUnknown
+ //
+
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+ 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 * pcFrames);
+
+ //
+ // ICorDebugFrameEnum
+ //
+
+ COM_METHOD Next(ULONG cFrames, ICorDebugFrame * rgpFrames[], ULONG * pcFramesFetched);
+
+ //
+ // accessors
+ //
+
+ // used to link ShimChainEnums in a list
+ ShimFrameEnum * GetNext();
+ void SetNext(ShimFrameEnum * pNext);
+
+private:
+ ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
+ ShimChain * m_pChain; // the owning ShimChain
+ RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
+
+ // This points to the next ShimFrameEnum in the linked list of ShimFrameEnums to be cleaned up.
+ // The head of the list is on the ShimStackWalk (m_pStackWalk->m_pFrameEnumList).
+ ShimFrameEnum * m_pNext;
+
+ UINT32 m_currentFrameIndex; // the current ICDFrame being enumerated
+ UINT32 m_endFrameIndex; // the last index (exclusive) of the frame owned by the chain;
+ // see code:ShimChain::GetLastFrameIndex
+ Volatile<ULONG> m_refCount;
+ BOOL m_fIsNeutered;
+};
+
+
+#endif // SHIMPRIV_H
+