2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
5 //*****************************************************************************
10 // private header for RS shim which bridges from V2 to V3.
11 //*****************************************************************************
18 #include "shimdatatarget.h"
22 // Forward declarations
23 class CordbWin32EventThread;
31 // This struct specifies that it's a hash table of ShimStackWalk * using ICorDebugThread as the key.
32 struct ShimStackWalkHashTableTraits : public PtrSHashTraits<ShimStackWalk, ICorDebugThread *> {};
33 typedef SHash<ShimStackWalkHashTableTraits> ShimStackWalkHashTable;
36 //---------------------------------------------------------------------------------------
38 // Simple struct for storing a void *. This is to be used with a SHash hash table.
41 struct DuplicateCreationEventEntry
44 DuplicateCreationEventEntry(void * pKey) : m_pKey(pKey) {};
46 // These functions must be defined for DuplicateCreationEventsHashTableTraits.
47 void * GetKey() {return m_pKey;};
48 static UINT32 Hash(void * pKey) {return (UINT32)(size_t)pKey;};
54 // This struct specifies that it's a hash table of DuplicateCreationEventEntry * using a void * as the key.
55 // The void * is expected to be an ICDProcess/ICDAppDomain/ICDThread/ICDAssembly/ICDThread interface pointer.
56 struct DuplicateCreationEventsHashTableTraits : public PtrSHashTraits<DuplicateCreationEventEntry, void *> {};
57 typedef SHash<DuplicateCreationEventsHashTableTraits> DuplicateCreationEventsHashTable;
60 // Callback that shim provides, which then queues up the events.
62 class ShimProxyCallback :
63 public ICorDebugManagedCallback,
64 public ICorDebugManagedCallback2,
65 public ICorDebugManagedCallback3
67 ShimProcess * m_pShim; // weak reference
71 ShimProxyCallback(ShimProcess * pShim);
74 ULONG STDMETHODCALLTYPE AddRef();
75 ULONG STDMETHODCALLTYPE Release();
76 COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
79 // Implementation of ICorDebugManagedCallback
82 COM_METHOD Breakpoint( ICorDebugAppDomain *pAppDomain,
83 ICorDebugThread *pThread,
84 ICorDebugBreakpoint *pBreakpoint);
86 COM_METHOD StepComplete( ICorDebugAppDomain *pAppDomain,
87 ICorDebugThread *pThread,
88 ICorDebugStepper *pStepper,
89 CorDebugStepReason reason);
91 COM_METHOD Break( ICorDebugAppDomain *pAppDomain,
92 ICorDebugThread *thread);
94 COM_METHOD Exception( ICorDebugAppDomain *pAppDomain,
95 ICorDebugThread *pThread,
98 COM_METHOD EvalComplete( ICorDebugAppDomain *pAppDomain,
99 ICorDebugThread *pThread,
100 ICorDebugEval *pEval);
102 COM_METHOD EvalException( ICorDebugAppDomain *pAppDomain,
103 ICorDebugThread *pThread,
104 ICorDebugEval *pEval);
106 COM_METHOD CreateProcess( ICorDebugProcess *pProcess);
107 void QueueCreateProcess( ICorDebugProcess *pProcess);
109 COM_METHOD ExitProcess( ICorDebugProcess *pProcess);
111 COM_METHOD CreateThread( ICorDebugAppDomain *pAppDomain, ICorDebugThread *thread);
114 COM_METHOD ExitThread( ICorDebugAppDomain *pAppDomain, ICorDebugThread *thread);
116 COM_METHOD LoadModule( ICorDebugAppDomain *pAppDomain, ICorDebugModule *pModule);
118 void FakeLoadModule(ICorDebugAppDomain *pAppDomain, ICorDebugModule *pModule);
120 COM_METHOD UnloadModule( ICorDebugAppDomain *pAppDomain, ICorDebugModule *pModule);
122 COM_METHOD LoadClass( ICorDebugAppDomain *pAppDomain, ICorDebugClass *c);
124 COM_METHOD UnloadClass( ICorDebugAppDomain *pAppDomain, ICorDebugClass *c);
126 COM_METHOD DebuggerError( ICorDebugProcess *pProcess, HRESULT errorHR, DWORD errorCode);
128 COM_METHOD LogMessage( ICorDebugAppDomain *pAppDomain,
129 ICorDebugThread *pThread,
131 __in LPWSTR pLogSwitchName,
132 __in LPWSTR pMessage);
134 COM_METHOD LogSwitch( ICorDebugAppDomain *pAppDomain,
135 ICorDebugThread *pThread,
138 __in LPWSTR pLogSwitchName,
139 __in LPWSTR pParentName);
141 COM_METHOD CreateAppDomain(ICorDebugProcess *pProcess,
142 ICorDebugAppDomain *pAppDomain);
144 COM_METHOD ExitAppDomain(ICorDebugProcess *pProcess,
145 ICorDebugAppDomain *pAppDomain);
147 COM_METHOD LoadAssembly(ICorDebugAppDomain *pAppDomain,
148 ICorDebugAssembly *pAssembly);
150 COM_METHOD UnloadAssembly(ICorDebugAppDomain *pAppDomain,
151 ICorDebugAssembly *pAssembly);
153 COM_METHOD ControlCTrap(ICorDebugProcess *pProcess);
155 COM_METHOD NameChange(ICorDebugAppDomain *pAppDomain, ICorDebugThread *pThread);
158 COM_METHOD UpdateModuleSymbols( ICorDebugAppDomain *pAppDomain,
159 ICorDebugModule *pModule,
160 IStream *pSymbolStream);
162 COM_METHOD EditAndContinueRemap( ICorDebugAppDomain *pAppDomain,
163 ICorDebugThread *pThread,
164 ICorDebugFunction *pFunction,
167 COM_METHOD BreakpointSetError( ICorDebugAppDomain *pAppDomain,
168 ICorDebugThread *pThread,
169 ICorDebugBreakpoint *pBreakpoint,
173 /// Implementation of ICorDebugManagedCallback2
175 COM_METHOD FunctionRemapOpportunity( ICorDebugAppDomain *pAppDomain,
176 ICorDebugThread *pThread,
177 ICorDebugFunction *pOldFunction,
178 ICorDebugFunction *pNewFunction,
179 ULONG32 oldILOffset);
181 COM_METHOD CreateConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId, __in LPWSTR pConnName);
183 COM_METHOD ChangeConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId );
186 COM_METHOD DestroyConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId);
188 COM_METHOD Exception(ICorDebugAppDomain *pAppDomain,
189 ICorDebugThread *pThread,
190 ICorDebugFrame *pFrame,
192 CorDebugExceptionCallbackType dwEventType,
195 COM_METHOD ExceptionUnwind(ICorDebugAppDomain *pAppDomain,
196 ICorDebugThread *pThread,
197 CorDebugExceptionUnwindCallbackType dwEventType,
200 COM_METHOD FunctionRemapComplete( ICorDebugAppDomain *pAppDomain,
201 ICorDebugThread *pThread,
202 ICorDebugFunction *pFunction);
204 COM_METHOD MDANotification(ICorDebugController * pController, ICorDebugThread *pThread, ICorDebugMDA * pMDA);
207 /// Implementation of ICorDebugManagedCallback3
210 // Implementation of ICorDebugManagedCallback3::CustomNotification
211 COM_METHOD CustomNotification(ICorDebugThread * pThread, ICorDebugAppDomain * pAppDomain);
217 // Base class for event queue. These are nested into a singly linked list.
218 // Shim maintains event queue
223 // Need virtual dtor since this is a base class.
224 virtual ~ManagedEvent();
227 // For debugging, get a pointer value that can identify the type of this event.
228 void * GetDebugCookie();
231 // We'll have a lot of derived classes of ManagedEvent, and so encapsulating the arguments
232 // for the Dispatch() function lets us juggle them around easily without hitting every signature.
236 DispatchArgs(ICorDebugManagedCallback * pCallback1, ICorDebugManagedCallback2 * pCallback2, ICorDebugManagedCallback3 * pCallback3);
238 ICorDebugManagedCallback * GetCallback1();
239 ICorDebugManagedCallback2 * GetCallback2();
240 ICorDebugManagedCallback3 * GetCallback3();
244 ICorDebugManagedCallback * m_pCallback1;
245 ICorDebugManagedCallback2 * m_pCallback2;
246 ICorDebugManagedCallback3 * m_pCallback3;
249 // Returns: value of callback from end-user
250 virtual HRESULT Dispatch(DispatchArgs args) = 0;
253 // Returns 0 if none.
257 // Ctor for events with thread-affinity
258 ManagedEvent(ICorDebugThread * pThread);
260 // Ctor for events without thread affinity.
263 friend class ManagedEventQueue;
264 ManagedEvent * m_pNext;
270 // Queue of managed events.
271 // Shim can use this to collect managed debug events, queue them, and then drain the event
272 // queue when a sync-complete occurs.
273 // Event queue gets initialized with a lock and will lock internally.
274 class ManagedEventQueue
280 void Init(RSLock * pLock);
282 // Remove event from the top. Caller then takes ownership of Event and will call Delete on it.
283 // Caller checks IsEmpty() first.
284 ManagedEvent * Dequeue();
286 // Queue owns the event and will delete it (unless it's dequeued first).
287 void QueueEvent(ManagedEvent * pEvent);
289 // Test if event queue is empty
292 // Empty event queue and delete all objects
296 BOOL HasQueuedCallbacks(ICorDebugThread * pThread);
298 // Save the current queue and start with a new empty queue
301 // Restore the saved queue onto the end of the current queue
302 void RestoreSuspendedQueue();
305 // The lock to be used for synchronizing all access to the queue
308 // If empty, First + Last are both NULL.
309 // Else first points to the head of the queue; and Last points to the end of the queue.
310 ManagedEvent * m_pFirstEvent;
311 ManagedEvent * m_pLastEvent;
316 //---------------------------------------------------------------------------------------
318 // Shim's layer on top of a process.
321 // This contains a V3 ICorDebugProcess, and provides V2 ICDProcess functionality.
325 // Delete via Ref count semantics.
328 // Initialize ref count is 0.
331 // Lifetime semantics handled by reference counting.
335 // Release all resources. Can be called multiple times.
338 // Initialization phases.
339 // 1. allocate new ShimProcess(). This lets us spin up a Win32 EventThread, which can then
341 // 2. Call ShimProcess::CreateProcess/DebugActiveProcess. This will call CreateAndStartWin32ET to
343 // 3. Create OS-debugging pipeline. This establishes the physical OS process and gets us a pid/handle
344 // 4. pShim->InitializeDataTarget - this creates a reader/writer abstraction around the OS process.
345 // 5. pShim->SetProcess() - this connects the Shim to the ICDProcess object.
346 HRESULT InitializeDataTarget(DWORD processId);
347 void SetProcess(ICorDebugProcess * pProcess);
349 //-----------------------------------------------------------
351 //-----------------------------------------------------------
353 static HRESULT CreateProcess(
355 ICorDebugRemoteTarget * pRemoteTarget,
357 __in_z LPWSTR programArgs,
358 LPSECURITY_ATTRIBUTES lpProcessAttributes,
359 LPSECURITY_ATTRIBUTES lpThreadAttributes,
360 BOOL bInheritHandles,
361 DWORD dwCreationFlags,
363 LPCWSTR lpCurrentDirectory,
364 LPSTARTUPINFOW lpStartupInfo,
365 LPPROCESS_INFORMATION lpProcessInformation,
366 CorDebugCreateProcessFlags corDebugFlags
369 static HRESULT DebugActiveProcess(
371 ICorDebugRemoteTarget * pRemoteTarget,
377 // Locates the DAC module adjacent to DBI
378 static HMODULE GetDacModule();
381 // Functions used by CordbProcess
384 // Determine if the calling thread is the win32 event thread.
385 bool IsWin32EventThread();
388 // Expose the W32ET thread to the CordbProcess so that it can emulate V2 behavior
389 CordbWin32EventThread * GetWin32EventThread();
391 // Accessor wrapper to mark whether we're interop-debugging.
392 void SetIsInteropDebugging(bool fIsInteropDebugging);
394 // Handle a debug event.
395 HRESULT HandleWin32DebugEvent(const DEBUG_EVENT * pEvent);
397 ManagedEventQueue * GetManagedEventQueue();
399 ManagedEvent * DequeueManagedEvent();
401 ShimProxyCallback * GetShimCallback();
403 // Begin Queing the fake attach events.
404 void BeginQueueFakeAttachEvents();
406 // Queue fake attach events if needed
407 void QueueFakeAttachEventsIfNeeded(bool fRealCreateProcessEvent);
409 // Actually do the work to queue the fake attach events.
410 void QueueFakeAttachEvents();
412 // Helper to queue fake assembly and mdule events
413 void QueueFakeAssemblyAndModuleEvent(ICorDebugAssembly * pAssembly);
415 // Queue fake thread-create events on attach. Order via native threads.
416 HRESULT QueueFakeThreadAttachEventsNativeOrder();
418 // Queue fake thread-create events on attach. No ordering.
419 HRESULT QueueFakeThreadAttachEventsNoOrder();
421 bool IsThreadSuspendedOrHijacked(ICorDebugThread * pThread);
423 // Expose m_attached to CordbProcess.
426 // We need to know whether we are in the CreateProcess callback to be able to
427 // return the v2.0 hresults from code:CordbProcess::SetDesiredNGENCompilerFlags
428 // when we are using the shim.
430 // Expose m_fInCreateProcess
431 bool GetInCreateProcess();
432 void SetInCreateProcess(bool value);
434 // We need to know whether we are in the FakeLoadModule callback to be able to
435 // return the v2.0 hresults from code:CordbModule::SetJITCompilerFlags when
436 // we are using the shim.
438 // Expose m_fInLoadModule
439 bool GetInLoadModule();
440 void SetInLoadModule(bool value);
442 // When we get a continue, we need to clear the flags indicating we're still in a callback
443 void NotifyOnContinue ();
445 // The RS calls this function when the stack is about to be changed in any way, e.g. continue, SetIP,
447 void NotifyOnStackInvalidate();
449 // Helpers to filter HRs to emulate V2 error codes.
450 HRESULT FilterSetNgenHresult(HRESULT hr);
451 HRESULT FilterSetJitFlagsHresult(HRESULT hr);
453 //.............................................................
456 // Lookup or create a ShimStackWalk for the specified thread. ShimStackWalk and ICorDebugThread has
457 // a 1:1 relationship.
458 ShimStackWalk * LookupOrCreateShimStackWalk(ICorDebugThread * pThread);
460 // Clear all ShimStackWalks and flush all the caches.
461 void ClearAllShimStackWalk();
463 // Get the corresponding ICDProcess object.
464 ICorDebugProcess * GetProcess();
466 // Get the data target to access the debuggee.
467 ICorDebugMutableDataTarget * GetDataTarget();
469 // Get the native event pipeline
470 INativeEventPipeline * GetNativePipeline();
472 // Are we interop-debugging?
473 bool IsInteropDebugging();
476 // Finish all the necessary initialization work and queue up any necessary fake attach events before
477 // dispatching an event.
478 void PreDispatchEvent(bool fRealCreateProcessEvent = false);
480 // Look for a CLR in the process and if found, return it's instance ID
481 HRESULT FindLoadedCLR(CORDB_ADDRESS * pClrInstanceId);
483 // Check whether the debugger has given us an ICorDebugRemoteTarget to query for the host name of the remote machine.
484 void CheckForPortInfo(ICorDebugRemoteTarget * pRemoteTarget);
486 // Resolve the host name given to us by the debugger to an IP address. Currently we don't support IPv6.
487 DWORD ResolveHostName(ICorDebugRemoteTarget * pRemoteTarget);
489 // Retrieve the IP address and the port number of the debugger proxy.
490 MachineInfo GetMachineInfo();
492 // Add an entry in the duplicate creation event hash table for the specified key.
493 void AddDuplicateCreationEvent(void * pKey);
495 // Check if a duplicate creation event entry exists for the specified key. If so, remove it.
496 bool RemoveDuplicateCreationEventIfPresent(void * pKey);
498 void SetMarkAttachPendingEvent();
500 void SetTerminatingEvent();
502 RSLock * GetShimLock();
512 HRESULT CreateAndStartWin32ET(Cordb * pCordb);
515 // Synchronization events to ensure that AttachPending bit is marked before DebugActiveProcess
516 // returns or debugger is detaching
518 HANDLE m_markAttachPendingEvent;
519 HANDLE m_terminatingEvent;
521 // Finds the base address of [core]clr.dll
522 CORDB_ADDRESS GetCLRInstanceBaseAddress();
528 // Shim maintains event queue to emulate V2 semantics.
529 // In V2, IcorDebug internally queued debug events and dispatched them
530 // once the debuggee was synchronized. In V3, ICorDebug dispatches events immediately.
531 // The event queue is moved into the shim to build V2 semantics of V3 behavior.
532 ManagedEventQueue m_eventQueue;
534 // Lock to protect Shim data structures. This is currently a small lock that
535 // protects leaf-level structures, but it may grow to protect larger things.
538 // Serializes ShimProcess:Dispose() with other ShimProcess functions. For now, this
539 // cannot be the same as m_ShimLock. See LL_SHIM_PROCESS_DISPOSE_LOCK for more
541 RSLock m_ShimProcessDisposeLock;
543 // Sticky bit to do lazy-initialization on the first managed event.
544 bool m_fFirstManagedEvent;
546 RSExtSmartPtr<ShimProxyCallback> m_pShimCallback;
549 // This is for emulating V2 Attach. Initialized to false, and then set to true if we ened to send fake attach events.
550 // Reset to false once the events are sent. See code:ShimProcess::QueueFakeAttachEventsIfNeeded
551 bool m_fNeedFakeAttachEvents;
553 // True if the process was created from an attach (DebugActiveProcess); False if it was launched (CreateProcess)
554 // This is used to send an Attach IPC event, and also used to provide more specific error codes.
557 // True iff we are in the shim's CreateProcess callback. This is used to determine which hresult to
558 // return from code:CordbProcess::SetDesiredNGENCompilerFlags so we correctly emulate the behavior of v2.0.
559 // This is set at the beginning of the callback and cleared in code:CordbProcess::ContinueInternal.
560 bool m_fInCreateProcess;
562 // True iff we are in the shim's FakeLoadModule callback. This is used to determine which hresult to
563 // return from code:CordbModule::SetJITCompilerFlags so we correctly emulate the behavior of v2.0.
564 // This is set at the beginning of the callback and cleared in code:CordbProcess::ContinueInternal.
565 bool m_fInLoadModule;
570 // Pointer to CordbProcess.
571 // @dbgtodo shim: We'd like this to eventually go through public interfaces (ICorDebugProcess)
572 IProcessShimHooks * m_pProcess; // Reference is kept by m_pIProcess;
573 RSExtSmartPtr<ICorDebugProcess> m_pIProcess;
575 // Win32EvenThread, which is the thread that uses the native debug API.
576 CordbWin32EventThread * m_pWin32EventThread;
578 // Actual data-target. Since we're shimming V2 scenarios, and V3 is always
579 // live-debugging, this is always a live data-target.
580 RSExtSmartPtr<ShimDataTarget> m_pLiveDataTarget;
583 // If true, the shim is emulating interop-debugging
584 // If false, the shim is emulating managed-only debugging.
585 // Both managed and native debugging have the same underlying pipeline (built
586 // on native-debug events). So the only difference is how they handle those events.
587 bool m_fIsInteropDebugging;
589 // true iff Dispose() was called. Consult this and do your work under m_ShimProcessDisposeLock
590 // to serialize yourself against a call to Dispose(). This protects your work
591 // from the user doing a Debugger Detach in the middle.
594 //.............................................................................
596 // Members used for handling native events when managed-only debugging.
598 //.............................................................................
600 // Default handler for native events when managed-only debugging.
601 void DefaultEventHandler(const DEBUG_EVENT * pEvent, DWORD * pdwContinueStatus);
603 // Given a debug event, track the file handles.
604 void TrackFileHandleForDebugEvent(const DEBUG_EVENT * pEvent);
606 // Have we gotten the loader breakpoint yet?
607 // A Debugger needs to do special work to skip the loader breakpoint,
608 // and that's also when it should dispatch the faked managed attach events.
609 bool m_loaderBPReceived;
611 // Raw callback for ContinueStatusChanged from Data-target.
612 static HRESULT ContinueStatusChanged(void * pUserData, DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus);
614 // Real worker to update ContinueStatusChangedData
615 HRESULT ContinueStatusChangedWorker(DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus);
617 struct ContinueStatusChangedData
621 // Tid of Thread changed
624 // New continue status.
625 CORDB_CONTINUE_STATUS m_status;
626 } m_ContinueStatusChangedData;
628 // the hash table of ShimStackWalks
629 ShimStackWalkHashTable * m_pShimStackWalkHashTable;
631 // the hash table of duplicate creation events
632 DuplicateCreationEventsHashTable * m_pDupeEventsHashTable;
634 MachineInfo m_machineInfo;
638 //---------------------------------------------------------------------------------------
640 // This is the container class of ShimChains, ICorDebugFrames, ShimChainEnums, and ShimFrameEnums.
641 // It has a 1:1 relationship with ICorDebugThreads. Upon creation, this class walks the entire stack and
642 // caches all the stack frames and chains. The enumerators are created on demand.
648 ShimStackWalk(ShimProcess * pProcess, ICorDebugThread * pThread);
651 // These functions do not adjust the reference count.
652 ICorDebugThread * GetThread();
653 ShimChain * GetChain(UINT32 index);
654 ICorDebugFrame * GetFrame(UINT32 index);
656 // Get the number of frames and chains.
657 ULONG GetChainCount();
658 ULONG GetFrameCount();
660 RSLock * GetShimLock();
662 // Add ICDChainEnum and ICDFrameEnum.
663 void AddChainEnum(ShimChainEnum * pChainEnum);
664 void AddFrameEnum(ShimFrameEnum * pFrameEnum);
666 // The next two functions are for ShimStackWalkHashTableTraits.
667 ICorDebugThread * GetKey();
668 static UINT32 Hash(ICorDebugThread * pThread);
670 // Check if the specified frame is the leaf frame according to the V2 definition.
671 BOOL IsLeafFrame(ICorDebugFrame * pFrame);
673 // Check if the two specified frames are the same. This function checks the SPs, frame address, etc.
674 // instead of just checking for pointer equality.
675 BOOL IsSameFrame(ICorDebugFrame * pLeft, ICorDebugFrame * pRight);
677 // The following functions are entry point into the ShimStackWalk. They are called by the RS.
678 void EnumerateChains(ICorDebugChainEnum ** ppChainEnum);
680 void GetActiveChain(ICorDebugChain ** ppChain);
681 void GetActiveFrame(ICorDebugFrame ** ppFrame);
682 void GetActiveRegisterSet(ICorDebugRegisterSet ** ppRegisterSet);
684 void GetChainForFrame(ICorDebugFrame * pFrame, ICorDebugChain ** ppChain);
685 void GetCallerForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCallerFrame);
686 void GetCalleeForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCalleeFrame);
689 //---------------------------------------------------------------------------------------
691 // This is a helper class used to store the information of a chain during a stackwalk. A chain is marked
692 // by the CONTEXT on the leaf boundary and a FramePointer on the root boundary. Also, notice that we
693 // are keeping two CONTEXTs. This is because some chain types may cancel a previous unmanaged chain.
694 // For example, a CHAIN_FUNC_EVAL chain cancels any CHAIN_ENTER_UNMANAGED chain immediately preceding
695 // it. In this case, the leaf boundary of the CHAIN_FUNC_EVAL chain is marked by the CONTEXT of the
696 // previous CHAIN_ENTER_MANAGED, not the previous CHAIN_ENTER_UNMANAGED.
702 ChainInfo() : m_rootFP(LEAF_MOST_FRAME), m_reason(CHAIN_NONE), m_fNeedEnterManagedChain(FALSE), m_fLeafNativeContextIsValid(FALSE) {}
704 void CancelUMChain() { m_reason = CHAIN_NONE; }
705 BOOL IsTrackingUMChain() { return (m_reason == CHAIN_ENTER_UNMANAGED); }
707 DT_CONTEXT m_leafNativeContext;
708 DT_CONTEXT m_leafManagedContext;
709 FramePointer m_rootFP;
710 CorDebugChainReason m_reason;
711 bool m_fNeedEnterManagedChain;
712 bool m_fLeafNativeContextIsValid;
715 //---------------------------------------------------------------------------------------
717 // This is a helper class used to store information during a stackwalk. Conceptually it is a simplified
718 // version of FrameInfo used on the LS in V2.
727 // Reset all the per-frame information.
728 void ResetForNextFrame();
730 // During the stackwalk, we need to find out whether we should process the next stack frame or the
731 // next internal frame. These functions help us determine whether we have exhausted one or both
732 // types of frames. The stackwalk is finished when both types are exhausted.
733 bool ExhaustedAllFrames();
734 bool ExhaustedAllStackFrames();
735 bool ExhaustedAllInternalFrames();
737 // Simple helper function to get the current internal frame.
738 ICorDebugInternalFrame2 * GetCurrentInternalFrame();
740 // Check whether we are processing the first frame.
743 // Check whether we are skipping frames because of a child frame.
744 BOOL IsSkippingFrame();
746 // Indicates whether we are dealing with a converted frame.
747 // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
748 BOOL HasConvertedFrame();
750 // Store the child frame we are currently trying to find the parent frame for.
751 // If this is NULL, then we are not skipping frames.
752 RSExtSmartPtr<ICorDebugNativeFrame2> m_pChildFrame;
754 // Store the converted frame, if any.
755 RSExtSmartPtr<ICorDebugInternalFrame2> m_pConvertedInternalFrame2;
757 // Store the array of internal frames. This is an array of RSExtSmartPtrs, and so each element
758 // is protected, and we only need to call Clear() to release each element and free all the memory.
759 RSExtPtrArray<ICorDebugInternalFrame2> m_ppInternalFrame2;
761 UINT32 m_cChain; // number of chains
762 UINT32 m_cFrame; // number of frames
763 UINT32 m_firstFrameInChain; // the index of the first frame in the current chain
764 UINT32 m_cInternalFrames; // number of internal frames
765 UINT32 m_curInternalFrame; // the index of the current internal frame being processed
767 CorDebugInternalFrameType m_internalFrameType;
769 bool m_fExhaustedAllStackFrames;
771 // Indicate whether we are processing an internal frame or a stack frame.
772 bool m_fProcessingInternalFrame;
774 // Indicate whether we should skip the current chain because it's a chain derived from a leaf frame
775 // of type TYPE_INTERNAL. This is the behaviour in V2.
776 // See code:DebuggerWalkStackProc.
779 // Indicate whether the current frame is the first frame we process.
782 // Indicate whether we are processing a converted frame.
783 bool m_fHasConvertedFrame;
786 // A ShimStackWalk is deleted when a process is continued, or when the stack is changed in any way
787 // (e.g. SetIP, EnC, etc.).
791 // Get a FramePointer to mark the root boundary of a chain.
792 FramePointer GetFramePointerForChain(DT_CONTEXT * pContext);
793 FramePointer GetFramePointerForChain(ICorDebugInternalFrame2 * pInternalFrame2);
795 CorDebugInternalFrameType GetInternalFrameType(ICorDebugInternalFrame2 * pFrame2);
797 // Append a frame to the array.
798 void AppendFrame(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo);
799 void AppendFrame(ICorDebugInternalFrame2 * pInternalFrame2, StackWalkInfo * pStackWalkInfo);
801 // Append a chain to the array.
802 void AppendChainWorker(StackWalkInfo * pStackWalkInfo,
803 DT_CONTEXT * pLeafContext,
805 CorDebugChainReason chainReason,
806 BOOL fIsManagedChain);
807 void AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo);
809 // Save information on the ChainInfo regarding the current chain.
810 void SaveChainContext(ICorDebugStackWalk * pSW, ChainInfo * pChainInfo, DT_CONTEXT * pContext);
812 // Check what we are process next, a internal frame or a stack frame.
813 BOOL CheckInternalFrame(ICorDebugFrame * pNextStackFrame,
814 StackWalkInfo * pStackWalkInfo,
815 ICorDebugThread3 * pThread3,
816 ICorDebugStackWalk * pSW);
818 // Convert an ICDInternalFrame to another ICDInternalFrame due to IL methods without metadata.
819 // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
820 BOOL ConvertInternalFrameToDynamicMethod(StackWalkInfo * pStackWalkInfo);
822 // Convert an ICDNativeFrame to an ICDInternalFrame due to IL methods without metadata.
823 // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
824 BOOL ConvertStackFrameToDynamicMethod(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo);
826 // Process an unmanaged chain.
827 BOOL ShouldTrackUMChain(StackWalkInfo * pswInfo);
828 void TrackUMChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo);
830 // Check whether the internal frame is a newly exposed type in Arrowhead. If so, then the shim should
832 BOOL IsV3FrameType(CorDebugInternalFrameType type);
834 // Check whether the specified frame represents a dynamic method.
835 BOOL IsILFrameWithoutMetadata(ICorDebugFrame * pFrame);
837 CDynArray<ShimChain *> m_stackChains; // growable ordered array of chains and frames
838 CDynArray<ICorDebugFrame *> m_stackFrames;
840 ShimChainEnum * m_pChainEnumList; // linked list of ShimChainEnum and ShimFrameEnum
841 ShimFrameEnum * m_pFrameEnumList;
843 // the thread on which we are doing a stackwalk, i.e. the "owning" thread
844 RSExtSmartPtr<ShimProcess> m_pProcess;
845 RSExtSmartPtr<ICorDebugThread> m_pThread;
849 //---------------------------------------------------------------------------------------
851 // This class implements the deprecated ICDChain interface.
854 class ShimChain : public ICorDebugChain
857 ShimChain(ShimStackWalk * pSW,
858 DT_CONTEXT * pContext,
861 UINT32 frameStartIndex,
862 UINT32 frameEndIndex,
863 CorDebugChainReason chainReason,
875 ULONG STDMETHODCALLTYPE AddRef();
876 ULONG STDMETHODCALLTYPE Release();
877 COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
883 COM_METHOD GetThread(ICorDebugThread ** ppThread);
884 COM_METHOD GetStackRange(CORDB_ADDRESS * pStart, CORDB_ADDRESS * pEnd);
885 COM_METHOD GetContext(ICorDebugContext ** ppContext);
886 COM_METHOD GetCaller(ICorDebugChain ** ppChain);
887 COM_METHOD GetCallee(ICorDebugChain ** ppChain);
888 COM_METHOD GetPrevious(ICorDebugChain ** ppChain);
889 COM_METHOD GetNext(ICorDebugChain ** ppChain);
890 COM_METHOD IsManaged(BOOL * pManaged);
891 COM_METHOD EnumerateFrames(ICorDebugFrameEnum ** ppFrames);
892 COM_METHOD GetActiveFrame(ICorDebugFrame ** ppFrame);
893 COM_METHOD GetRegisterSet(ICorDebugRegisterSet ** ppRegisters);
894 COM_METHOD GetReason(CorDebugChainReason * pReason);
900 // Get the owning ShimStackWalk.
901 ShimStackWalk * GetShimStackWalk();
903 // Get the first and last index of the frame owned by this chain. This class itself doesn't store the
904 // frames. Rather, the frames are stored on the ShimStackWalk. This class just stores the indices.
905 // Note that the indices are [firstIndex, lastIndex), i.e. the last index is exclusive.
906 UINT32 GetFirstFrameIndex();
907 UINT32 GetLastFrameIndex();
910 // A chain describes a stack range within the stack. This includes a CONTEXT at the start (leafmost)
911 // end of the chain, and a frame pointer where the chain ends (rootmost). This stack range is exposed
912 // publicly via ICDChain::GetStackRange(), and can be used to stitch managed and native stack frames
913 // together into a unified stack.
914 DT_CONTEXT m_context; // the leaf end of the chain
915 FramePointer m_fpRoot; // the root end of the chain
917 ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
918 Volatile<ULONG> m_refCount;
920 // The 0-based index of this chain in the ShimStackWalk's chain array (m_pStackWalk->m_stackChains).
923 // The 0-based index of the first frame owned by this chain in the ShimStackWalk's frame array
924 // (m_pStackWalk->m_stackFrames). See code::ShimChain::GetFirstFrameIndex().
925 UINT32 m_frameStartIndex;
927 // The 0-based index of the last frame owned by this chain in the ShimStackWalk's frame array
928 // (m_pStackWalk->m_stackFrames). This index is exlusive. See code::ShimChain::GetLastFrameIndex().
929 UINT32 m_frameEndIndex;
931 CorDebugChainReason m_chainReason;
932 BOOL m_fIsManaged; // indicates whether this chain contains managed frames
935 RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
939 //---------------------------------------------------------------------------------------
941 // This class implements the deprecated ICDChainEnum interface.
944 class ShimChainEnum : public ICorDebugChainEnum
947 ShimChainEnum(ShimStackWalk * pSW, RSLock * pShimLock);
957 ULONG STDMETHODCALLTYPE AddRef();
958 ULONG STDMETHODCALLTYPE Release();
959 COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
965 COM_METHOD Skip(ULONG celt);
967 COM_METHOD Clone(ICorDebugEnum ** ppEnum);
968 COM_METHOD GetCount(ULONG * pcChains);
971 // ICorDebugChainEnum
974 COM_METHOD Next(ULONG cChains, ICorDebugChain * rgpChains[], ULONG * pcChainsFetched);
980 // used to link ShimChainEnums in a list
981 ShimChainEnum * GetNext();
982 void SetNext(ShimChainEnum * pNext);
985 ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
987 // This points to the next ShimChainEnum in the linked list of ShimChainEnums to be cleaned up.
988 // The head of the list is on the ShimStackWalk (m_pStackWalk->m_pChainEnumList).
989 ShimChainEnum * m_pNext;
991 UINT32 m_currentChainIndex; // the index of the current ShimChain being enumerated
992 Volatile<ULONG> m_refCount;
995 RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
999 //---------------------------------------------------------------------------------------
1001 // This class implements the deprecated ICDFrameEnum interface.
1004 class ShimFrameEnum : public ICorDebugFrameEnum
1007 ShimFrameEnum(ShimStackWalk * pSW, ShimChain * pChain, UINT32 frameStartIndex, UINT32 frameEndIndex, RSLock * pShimLock);
1017 ULONG STDMETHODCALLTYPE AddRef();
1018 ULONG STDMETHODCALLTYPE Release();
1019 COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
1025 COM_METHOD Skip(ULONG celt);
1027 COM_METHOD Clone(ICorDebugEnum ** ppEnum);
1028 COM_METHOD GetCount(ULONG * pcFrames);
1031 // ICorDebugFrameEnum
1034 COM_METHOD Next(ULONG cFrames, ICorDebugFrame * rgpFrames[], ULONG * pcFramesFetched);
1040 // used to link ShimChainEnums in a list
1041 ShimFrameEnum * GetNext();
1042 void SetNext(ShimFrameEnum * pNext);
1045 ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
1046 ShimChain * m_pChain; // the owning ShimChain
1047 RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
1049 // This points to the next ShimFrameEnum in the linked list of ShimFrameEnums to be cleaned up.
1050 // The head of the list is on the ShimStackWalk (m_pStackWalk->m_pFrameEnumList).
1051 ShimFrameEnum * m_pNext;
1053 UINT32 m_currentFrameIndex; // the current ICDFrame being enumerated
1054 UINT32 m_endFrameIndex; // the last index (exclusive) of the frame owned by the chain;
1055 // see code:ShimChain::GetLastFrameIndex
1056 Volatile<ULONG> m_refCount;
1061 #endif // SHIMPRIV_H