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 #if !defined(FEATURE_CORESYSTEM)
522 // Finds the base address of mscorwks.dll
523 CORDB_ADDRESS GetCLRInstanceBaseAddress();
524 #endif // !defined(FEATURE_CORESYSTEM)
530 // Shim maintains event queue to emulate V2 semantics.
531 // In V2, IcorDebug internally queued debug events and dispatched them
532 // once the debuggee was synchronized. In V3, ICorDebug dispatches events immediately.
533 // The event queue is moved into the shim to build V2 semantics of V3 behavior.
534 ManagedEventQueue m_eventQueue;
536 // Lock to protect Shim data structures. This is currently a small lock that
537 // protects leaf-level structures, but it may grow to protect larger things.
540 // Serializes ShimProcess:Dispose() with other ShimProcess functions. For now, this
541 // cannot be the same as m_ShimLock. See LL_SHIM_PROCESS_DISPOSE_LOCK for more
543 RSLock m_ShimProcessDisposeLock;
545 // Sticky bit to do lazy-initialization on the first managed event.
546 bool m_fFirstManagedEvent;
548 RSExtSmartPtr<ShimProxyCallback> m_pShimCallback;
551 // This is for emulating V2 Attach. Initialized to false, and then set to true if we ened to send fake attach events.
552 // Reset to false once the events are sent. See code:ShimProcess::QueueFakeAttachEventsIfNeeded
553 bool m_fNeedFakeAttachEvents;
555 // True if the process was created from an attach (DebugActiveProcess); False if it was launched (CreateProcess)
556 // This is used to send an Attach IPC event, and also used to provide more specific error codes.
559 // True iff we are in the shim's CreateProcess callback. This is used to determine which hresult to
560 // return from code:CordbProcess::SetDesiredNGENCompilerFlags so we correctly emulate the behavior of v2.0.
561 // This is set at the beginning of the callback and cleared in code:CordbProcess::ContinueInternal.
562 bool m_fInCreateProcess;
564 // True iff we are in the shim's FakeLoadModule callback. This is used to determine which hresult to
565 // return from code:CordbModule::SetJITCompilerFlags so we correctly emulate the behavior of v2.0.
566 // This is set at the beginning of the callback and cleared in code:CordbProcess::ContinueInternal.
567 bool m_fInLoadModule;
572 // Pointer to CordbProcess.
573 // @dbgtodo shim: We'd like this to eventually go through public interfaces (ICorDebugProcess)
574 IProcessShimHooks * m_pProcess; // Reference is kept by m_pIProcess;
575 RSExtSmartPtr<ICorDebugProcess> m_pIProcess;
577 // Win32EvenThread, which is the thread that uses the native debug API.
578 CordbWin32EventThread * m_pWin32EventThread;
580 // Actual data-target. Since we're shimming V2 scenarios, and V3 is always
581 // live-debugging, this is always a live data-target.
582 RSExtSmartPtr<ShimDataTarget> m_pLiveDataTarget;
585 // If true, the shim is emulating interop-debugging
586 // If false, the shim is emulating managed-only debugging.
587 // Both managed and native debugging have the same underlying pipeline (built
588 // on native-debug events). So the only difference is how they handle those events.
589 bool m_fIsInteropDebugging;
591 // true iff Dispose() was called. Consult this and do your work under m_ShimProcessDisposeLock
592 // to serialize yourself against a call to Dispose(). This protects your work
593 // from the user doing a Debugger Detach in the middle.
596 //.............................................................................
598 // Members used for handling native events when managed-only debugging.
600 //.............................................................................
602 // Default handler for native events when managed-only debugging.
603 void DefaultEventHandler(const DEBUG_EVENT * pEvent, DWORD * pdwContinueStatus);
605 // Given a debug event, track the file handles.
606 void TrackFileHandleForDebugEvent(const DEBUG_EVENT * pEvent);
608 // Have we gotten the loader breakpoint yet?
609 // A Debugger needs to do special work to skip the loader breakpoint,
610 // and that's also when it should dispatch the faked managed attach events.
611 bool m_loaderBPReceived;
613 // Raw callback for ContinueStatusChanged from Data-target.
614 static HRESULT ContinueStatusChanged(void * pUserData, DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus);
616 // Real worker to update ContinueStatusChangedData
617 HRESULT ContinueStatusChangedWorker(DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus);
619 struct ContinueStatusChangedData
623 // Tid of Thread changed
626 // New continue status.
627 CORDB_CONTINUE_STATUS m_status;
628 } m_ContinueStatusChangedData;
630 // the hash table of ShimStackWalks
631 ShimStackWalkHashTable * m_pShimStackWalkHashTable;
633 // the hash table of duplicate creation events
634 DuplicateCreationEventsHashTable * m_pDupeEventsHashTable;
636 MachineInfo m_machineInfo;
640 //---------------------------------------------------------------------------------------
642 // This is the container class of ShimChains, ICorDebugFrames, ShimChainEnums, and ShimFrameEnums.
643 // It has a 1:1 relationship with ICorDebugThreads. Upon creation, this class walks the entire stack and
644 // caches all the stack frames and chains. The enumerators are created on demand.
650 ShimStackWalk(ShimProcess * pProcess, ICorDebugThread * pThread);
653 // These functions do not adjust the reference count.
654 ICorDebugThread * GetThread();
655 ShimChain * GetChain(UINT32 index);
656 ICorDebugFrame * GetFrame(UINT32 index);
658 // Get the number of frames and chains.
659 ULONG GetChainCount();
660 ULONG GetFrameCount();
662 RSLock * GetShimLock();
664 // Add ICDChainEnum and ICDFrameEnum.
665 void AddChainEnum(ShimChainEnum * pChainEnum);
666 void AddFrameEnum(ShimFrameEnum * pFrameEnum);
668 // The next two functions are for ShimStackWalkHashTableTraits.
669 ICorDebugThread * GetKey();
670 static UINT32 Hash(ICorDebugThread * pThread);
672 // Check if the specified frame is the leaf frame according to the V2 definition.
673 BOOL IsLeafFrame(ICorDebugFrame * pFrame);
675 // Check if the two specified frames are the same. This function checks the SPs, frame address, etc.
676 // instead of just checking for pointer equality.
677 BOOL IsSameFrame(ICorDebugFrame * pLeft, ICorDebugFrame * pRight);
679 // The following functions are entry point into the ShimStackWalk. They are called by the RS.
680 void EnumerateChains(ICorDebugChainEnum ** ppChainEnum);
682 void GetActiveChain(ICorDebugChain ** ppChain);
683 void GetActiveFrame(ICorDebugFrame ** ppFrame);
684 void GetActiveRegisterSet(ICorDebugRegisterSet ** ppRegisterSet);
686 void GetChainForFrame(ICorDebugFrame * pFrame, ICorDebugChain ** ppChain);
687 void GetCallerForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCallerFrame);
688 void GetCalleeForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCalleeFrame);
691 //---------------------------------------------------------------------------------------
693 // This is a helper class used to store the information of a chain during a stackwalk. A chain is marked
694 // by the CONTEXT on the leaf boundary and a FramePointer on the root boundary. Also, notice that we
695 // are keeping two CONTEXTs. This is because some chain types may cancel a previous unmanaged chain.
696 // For example, a CHAIN_FUNC_EVAL chain cancels any CHAIN_ENTER_UNMANAGED chain immediately preceding
697 // it. In this case, the leaf boundary of the CHAIN_FUNC_EVAL chain is marked by the CONTEXT of the
698 // previous CHAIN_ENTER_MANAGED, not the previous CHAIN_ENTER_UNMANAGED.
704 ChainInfo() : m_rootFP(LEAF_MOST_FRAME), m_reason(CHAIN_NONE), m_fNeedEnterManagedChain(FALSE), m_fLeafNativeContextIsValid(FALSE) {}
706 void CancelUMChain() { m_reason = CHAIN_NONE; }
707 BOOL IsTrackingUMChain() { return (m_reason == CHAIN_ENTER_UNMANAGED); }
709 DT_CONTEXT m_leafNativeContext;
710 DT_CONTEXT m_leafManagedContext;
711 FramePointer m_rootFP;
712 CorDebugChainReason m_reason;
713 bool m_fNeedEnterManagedChain;
714 bool m_fLeafNativeContextIsValid;
717 //---------------------------------------------------------------------------------------
719 // This is a helper class used to store information during a stackwalk. Conceptually it is a simplified
720 // version of FrameInfo used on the LS in V2.
729 // Reset all the per-frame information.
730 void ResetForNextFrame();
732 // During the stackwalk, we need to find out whether we should process the next stack frame or the
733 // next internal frame. These functions help us determine whether we have exhausted one or both
734 // types of frames. The stackwalk is finished when both types are exhausted.
735 bool ExhaustedAllFrames();
736 bool ExhaustedAllStackFrames();
737 bool ExhaustedAllInternalFrames();
739 // Simple helper function to get the current internal frame.
740 ICorDebugInternalFrame2 * GetCurrentInternalFrame();
742 // Check whether we are processing the first frame.
745 // Check whether we are skipping frames because of a child frame.
746 BOOL IsSkippingFrame();
748 // Indicates whether we are dealing with a converted frame.
749 // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
750 BOOL HasConvertedFrame();
752 // Store the child frame we are currently trying to find the parent frame for.
753 // If this is NULL, then we are not skipping frames.
754 RSExtSmartPtr<ICorDebugNativeFrame2> m_pChildFrame;
756 // Store the converted frame, if any.
757 RSExtSmartPtr<ICorDebugInternalFrame2> m_pConvertedInternalFrame2;
759 // Store the array of internal frames. This is an array of RSExtSmartPtrs, and so each element
760 // is protected, and we only need to call Clear() to release each element and free all the memory.
761 RSExtPtrArray<ICorDebugInternalFrame2> m_ppInternalFrame2;
763 UINT32 m_cChain; // number of chains
764 UINT32 m_cFrame; // number of frames
765 UINT32 m_firstFrameInChain; // the index of the first frame in the current chain
766 UINT32 m_cInternalFrames; // number of internal frames
767 UINT32 m_curInternalFrame; // the index of the current internal frame being processed
769 CorDebugInternalFrameType m_internalFrameType;
771 bool m_fExhaustedAllStackFrames;
773 // Indicate whether we are processing an internal frame or a stack frame.
774 bool m_fProcessingInternalFrame;
776 // Indicate whether we should skip the current chain because it's a chain derived from a leaf frame
777 // of type TYPE_INTERNAL. This is the behaviour in V2.
778 // See code:DebuggerWalkStackProc.
781 // Indicate whether the current frame is the first frame we process.
784 // Indicate whether we are processing a converted frame.
785 bool m_fHasConvertedFrame;
788 // A ShimStackWalk is deleted when a process is continued, or when the stack is changed in any way
789 // (e.g. SetIP, EnC, etc.).
793 // Get a FramePointer to mark the root boundary of a chain.
794 FramePointer GetFramePointerForChain(DT_CONTEXT * pContext);
795 FramePointer GetFramePointerForChain(ICorDebugInternalFrame2 * pInternalFrame2);
797 CorDebugInternalFrameType GetInternalFrameType(ICorDebugInternalFrame2 * pFrame2);
799 // Append a frame to the array.
800 void AppendFrame(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo);
801 void AppendFrame(ICorDebugInternalFrame2 * pInternalFrame2, StackWalkInfo * pStackWalkInfo);
803 // Append a chain to the array.
804 void AppendChainWorker(StackWalkInfo * pStackWalkInfo,
805 DT_CONTEXT * pLeafContext,
807 CorDebugChainReason chainReason,
808 BOOL fIsManagedChain);
809 void AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo);
811 // Save information on the ChainInfo regarding the current chain.
812 void SaveChainContext(ICorDebugStackWalk * pSW, ChainInfo * pChainInfo, DT_CONTEXT * pContext);
814 // Check what we are process next, a internal frame or a stack frame.
815 BOOL CheckInternalFrame(ICorDebugFrame * pNextStackFrame,
816 StackWalkInfo * pStackWalkInfo,
817 ICorDebugThread3 * pThread3,
818 ICorDebugStackWalk * pSW);
820 // Convert an ICDInternalFrame to another ICDInternalFrame due to IL methods without metadata.
821 // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
822 BOOL ConvertInternalFrameToDynamicMethod(StackWalkInfo * pStackWalkInfo);
824 // Convert an ICDNativeFrame to an ICDInternalFrame due to IL methods without metadata.
825 // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
826 BOOL ConvertStackFrameToDynamicMethod(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo);
828 // Process an unmanaged chain.
829 BOOL ShouldTrackUMChain(StackWalkInfo * pswInfo);
830 void TrackUMChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo);
832 // Check whether the internal frame is a newly exposed type in Arrowhead. If so, then the shim should
834 BOOL IsV3FrameType(CorDebugInternalFrameType type);
836 // Check whether the specified frame represents a dynamic method.
837 BOOL IsILFrameWithoutMetadata(ICorDebugFrame * pFrame);
839 CDynArray<ShimChain *> m_stackChains; // growable ordered array of chains and frames
840 CDynArray<ICorDebugFrame *> m_stackFrames;
842 ShimChainEnum * m_pChainEnumList; // linked list of ShimChainEnum and ShimFrameEnum
843 ShimFrameEnum * m_pFrameEnumList;
845 // the thread on which we are doing a stackwalk, i.e. the "owning" thread
846 RSExtSmartPtr<ShimProcess> m_pProcess;
847 RSExtSmartPtr<ICorDebugThread> m_pThread;
851 //---------------------------------------------------------------------------------------
853 // This class implements the deprecated ICDChain interface.
856 class ShimChain : public ICorDebugChain
859 ShimChain(ShimStackWalk * pSW,
860 DT_CONTEXT * pContext,
863 UINT32 frameStartIndex,
864 UINT32 frameEndIndex,
865 CorDebugChainReason chainReason,
877 ULONG STDMETHODCALLTYPE AddRef();
878 ULONG STDMETHODCALLTYPE Release();
879 COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
885 COM_METHOD GetThread(ICorDebugThread ** ppThread);
886 COM_METHOD GetStackRange(CORDB_ADDRESS * pStart, CORDB_ADDRESS * pEnd);
887 COM_METHOD GetContext(ICorDebugContext ** ppContext);
888 COM_METHOD GetCaller(ICorDebugChain ** ppChain);
889 COM_METHOD GetCallee(ICorDebugChain ** ppChain);
890 COM_METHOD GetPrevious(ICorDebugChain ** ppChain);
891 COM_METHOD GetNext(ICorDebugChain ** ppChain);
892 COM_METHOD IsManaged(BOOL * pManaged);
893 COM_METHOD EnumerateFrames(ICorDebugFrameEnum ** ppFrames);
894 COM_METHOD GetActiveFrame(ICorDebugFrame ** ppFrame);
895 COM_METHOD GetRegisterSet(ICorDebugRegisterSet ** ppRegisters);
896 COM_METHOD GetReason(CorDebugChainReason * pReason);
902 // Get the owning ShimStackWalk.
903 ShimStackWalk * GetShimStackWalk();
905 // Get the first and last index of the frame owned by this chain. This class itself doesn't store the
906 // frames. Rather, the frames are stored on the ShimStackWalk. This class just stores the indices.
907 // Note that the indices are [firstIndex, lastIndex), i.e. the last index is exclusive.
908 UINT32 GetFirstFrameIndex();
909 UINT32 GetLastFrameIndex();
912 // A chain describes a stack range within the stack. This includes a CONTEXT at the start (leafmost)
913 // end of the chain, and a frame pointer where the chain ends (rootmost). This stack range is exposed
914 // publicly via ICDChain::GetStackRange(), and can be used to stitch managed and native stack frames
915 // together into a unified stack.
916 DT_CONTEXT m_context; // the leaf end of the chain
917 FramePointer m_fpRoot; // the root end of the chain
919 ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
920 Volatile<ULONG> m_refCount;
922 // The 0-based index of this chain in the ShimStackWalk's chain array (m_pStackWalk->m_stackChains).
925 // The 0-based index of the first frame owned by this chain in the ShimStackWalk's frame array
926 // (m_pStackWalk->m_stackFrames). See code::ShimChain::GetFirstFrameIndex().
927 UINT32 m_frameStartIndex;
929 // The 0-based index of the last frame owned by this chain in the ShimStackWalk's frame array
930 // (m_pStackWalk->m_stackFrames). This index is exlusive. See code::ShimChain::GetLastFrameIndex().
931 UINT32 m_frameEndIndex;
933 CorDebugChainReason m_chainReason;
934 BOOL m_fIsManaged; // indicates whether this chain contains managed frames
937 RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
941 //---------------------------------------------------------------------------------------
943 // This class implements the deprecated ICDChainEnum interface.
946 class ShimChainEnum : public ICorDebugChainEnum
949 ShimChainEnum(ShimStackWalk * pSW, RSLock * pShimLock);
959 ULONG STDMETHODCALLTYPE AddRef();
960 ULONG STDMETHODCALLTYPE Release();
961 COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
967 COM_METHOD Skip(ULONG celt);
969 COM_METHOD Clone(ICorDebugEnum ** ppEnum);
970 COM_METHOD GetCount(ULONG * pcChains);
973 // ICorDebugChainEnum
976 COM_METHOD Next(ULONG cChains, ICorDebugChain * rgpChains[], ULONG * pcChainsFetched);
982 // used to link ShimChainEnums in a list
983 ShimChainEnum * GetNext();
984 void SetNext(ShimChainEnum * pNext);
987 ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
989 // This points to the next ShimChainEnum in the linked list of ShimChainEnums to be cleaned up.
990 // The head of the list is on the ShimStackWalk (m_pStackWalk->m_pChainEnumList).
991 ShimChainEnum * m_pNext;
993 UINT32 m_currentChainIndex; // the index of the current ShimChain being enumerated
994 Volatile<ULONG> m_refCount;
997 RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
1001 //---------------------------------------------------------------------------------------
1003 // This class implements the deprecated ICDFrameEnum interface.
1006 class ShimFrameEnum : public ICorDebugFrameEnum
1009 ShimFrameEnum(ShimStackWalk * pSW, ShimChain * pChain, UINT32 frameStartIndex, UINT32 frameEndIndex, RSLock * pShimLock);
1019 ULONG STDMETHODCALLTYPE AddRef();
1020 ULONG STDMETHODCALLTYPE Release();
1021 COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
1027 COM_METHOD Skip(ULONG celt);
1029 COM_METHOD Clone(ICorDebugEnum ** ppEnum);
1030 COM_METHOD GetCount(ULONG * pcFrames);
1033 // ICorDebugFrameEnum
1036 COM_METHOD Next(ULONG cFrames, ICorDebugFrame * rgpFrames[], ULONG * pcFramesFetched);
1042 // used to link ShimChainEnums in a list
1043 ShimFrameEnum * GetNext();
1044 void SetNext(ShimFrameEnum * pNext);
1047 ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
1048 ShimChain * m_pChain; // the owning ShimChain
1049 RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
1051 // This points to the next ShimFrameEnum in the linked list of ShimFrameEnums to be cleaned up.
1052 // The head of the list is on the ShimStackWalk (m_pStackWalk->m_pFrameEnumList).
1053 ShimFrameEnum * m_pNext;
1055 UINT32 m_currentFrameIndex; // the current ICDFrame being enumerated
1056 UINT32 m_endFrameIndex; // the last index (exclusive) of the frame owned by the chain;
1057 // see code:ShimChain::GetLastFrameIndex
1058 Volatile<ULONG> m_refCount;
1063 #endif // SHIMPRIV_H