1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 //*****************************************************************************
9 // private header for RS shim which bridges from V2 to V3.
10 //*****************************************************************************
17 #include "shimdatatarget.h"
21 // Forward declarations
22 class CordbWin32EventThread;
30 // This struct specifies that it's a hash table of ShimStackWalk * using ICorDebugThread as the key.
31 struct ShimStackWalkHashTableTraits : public PtrSHashTraits<ShimStackWalk, ICorDebugThread *> {};
32 typedef SHash<ShimStackWalkHashTableTraits> ShimStackWalkHashTable;
35 //---------------------------------------------------------------------------------------
37 // Simple struct for storing a void *. This is to be used with a SHash hash table.
40 struct DuplicateCreationEventEntry
43 DuplicateCreationEventEntry(void * pKey) : m_pKey(pKey) {};
45 // These functions must be defined for DuplicateCreationEventsHashTableTraits.
46 void * GetKey() {return m_pKey;};
47 static UINT32 Hash(void * pKey) {return (UINT32)(size_t)pKey;};
53 // This struct specifies that it's a hash table of DuplicateCreationEventEntry * using a void * as the key.
54 // The void * is expected to be an ICDProcess/ICDAppDomain/ICDThread/ICDAssembly/ICDThread interface pointer.
55 struct DuplicateCreationEventsHashTableTraits : public PtrSHashTraits<DuplicateCreationEventEntry, void *> {};
56 typedef SHash<DuplicateCreationEventsHashTableTraits> DuplicateCreationEventsHashTable;
59 // Callback that shim provides, which then queues up the events.
61 class ShimProxyCallback :
62 public ICorDebugManagedCallback,
63 public ICorDebugManagedCallback2,
64 public ICorDebugManagedCallback3
66 ShimProcess * m_pShim; // weak reference
70 ShimProxyCallback(ShimProcess * pShim);
71 virtual ~ShimProxyCallback() {}
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 // Retrieve the IP address and the port number of the debugger proxy.
484 MachineInfo GetMachineInfo();
486 // Add an entry in the duplicate creation event hash table for the specified key.
487 void AddDuplicateCreationEvent(void * pKey);
489 // Check if a duplicate creation event entry exists for the specified key. If so, remove it.
490 bool RemoveDuplicateCreationEventIfPresent(void * pKey);
492 void SetMarkAttachPendingEvent();
494 void SetTerminatingEvent();
496 RSLock * GetShimLock();
506 HRESULT CreateAndStartWin32ET(Cordb * pCordb);
509 // Synchronization events to ensure that AttachPending bit is marked before DebugActiveProcess
510 // returns or debugger is detaching
512 HANDLE m_markAttachPendingEvent;
513 HANDLE m_terminatingEvent;
515 // Finds the base address of [core]clr.dll
516 CORDB_ADDRESS GetCLRInstanceBaseAddress();
522 // Shim maintains event queue to emulate V2 semantics.
523 // In V2, IcorDebug internally queued debug events and dispatched them
524 // once the debuggee was synchronized. In V3, ICorDebug dispatches events immediately.
525 // The event queue is moved into the shim to build V2 semantics of V3 behavior.
526 ManagedEventQueue m_eventQueue;
528 // Lock to protect Shim data structures. This is currently a small lock that
529 // protects leaf-level structures, but it may grow to protect larger things.
532 // Serializes ShimProcess:Dispose() with other ShimProcess functions. For now, this
533 // cannot be the same as m_ShimLock. See LL_SHIM_PROCESS_DISPOSE_LOCK for more
535 RSLock m_ShimProcessDisposeLock;
537 // Sticky bit to do lazy-initialization on the first managed event.
538 bool m_fFirstManagedEvent;
540 RSExtSmartPtr<ShimProxyCallback> m_pShimCallback;
543 // This is for emulating V2 Attach. Initialized to false, and then set to true if we ened to send fake attach events.
544 // Reset to false once the events are sent. See code:ShimProcess::QueueFakeAttachEventsIfNeeded
545 bool m_fNeedFakeAttachEvents;
547 // True if the process was created from an attach (DebugActiveProcess); False if it was launched (CreateProcess)
548 // This is used to send an Attach IPC event, and also used to provide more specific error codes.
551 // True iff we are in the shim's CreateProcess callback. This is used to determine which hresult to
552 // return from code:CordbProcess::SetDesiredNGENCompilerFlags so we correctly emulate the behavior of v2.0.
553 // This is set at the beginning of the callback and cleared in code:CordbProcess::ContinueInternal.
554 bool m_fInCreateProcess;
556 // True iff we are in the shim's FakeLoadModule callback. This is used to determine which hresult to
557 // return from code:CordbModule::SetJITCompilerFlags so we correctly emulate the behavior of v2.0.
558 // This is set at the beginning of the callback and cleared in code:CordbProcess::ContinueInternal.
559 bool m_fInLoadModule;
564 // Pointer to CordbProcess.
565 // @dbgtodo shim: We'd like this to eventually go through public interfaces (ICorDebugProcess)
566 IProcessShimHooks * m_pProcess; // Reference is kept by m_pIProcess;
567 RSExtSmartPtr<ICorDebugProcess> m_pIProcess;
569 // Win32EvenThread, which is the thread that uses the native debug API.
570 CordbWin32EventThread * m_pWin32EventThread;
572 // Actual data-target. Since we're shimming V2 scenarios, and V3 is always
573 // live-debugging, this is always a live data-target.
574 RSExtSmartPtr<ShimDataTarget> m_pLiveDataTarget;
577 // If true, the shim is emulating interop-debugging
578 // If false, the shim is emulating managed-only debugging.
579 // Both managed and native debugging have the same underlying pipeline (built
580 // on native-debug events). So the only difference is how they handle those events.
581 bool m_fIsInteropDebugging;
583 // true iff Dispose() was called. Consult this and do your work under m_ShimProcessDisposeLock
584 // to serialize yourself against a call to Dispose(). This protects your work
585 // from the user doing a Debugger Detach in the middle.
588 //.............................................................................
590 // Members used for handling native events when managed-only debugging.
592 //.............................................................................
594 // Default handler for native events when managed-only debugging.
595 void DefaultEventHandler(const DEBUG_EVENT * pEvent, DWORD * pdwContinueStatus);
597 // Given a debug event, track the file handles.
598 void TrackFileHandleForDebugEvent(const DEBUG_EVENT * pEvent);
600 // Have we gotten the loader breakpoint yet?
601 // A Debugger needs to do special work to skip the loader breakpoint,
602 // and that's also when it should dispatch the faked managed attach events.
603 bool m_loaderBPReceived;
605 // Raw callback for ContinueStatusChanged from Data-target.
606 static HRESULT ContinueStatusChanged(void * pUserData, DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus);
608 // Real worker to update ContinueStatusChangedData
609 HRESULT ContinueStatusChangedWorker(DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus);
611 struct ContinueStatusChangedData
615 // Tid of Thread changed
618 // New continue status.
619 CORDB_CONTINUE_STATUS m_status;
620 } m_ContinueStatusChangedData;
622 // the hash table of ShimStackWalks
623 ShimStackWalkHashTable * m_pShimStackWalkHashTable;
625 // the hash table of duplicate creation events
626 DuplicateCreationEventsHashTable * m_pDupeEventsHashTable;
628 MachineInfo m_machineInfo;
632 //---------------------------------------------------------------------------------------
634 // This is the container class of ShimChains, ICorDebugFrames, ShimChainEnums, and ShimFrameEnums.
635 // It has a 1:1 relationship with ICorDebugThreads. Upon creation, this class walks the entire stack and
636 // caches all the stack frames and chains. The enumerators are created on demand.
642 ShimStackWalk(ShimProcess * pProcess, ICorDebugThread * pThread);
645 // These functions do not adjust the reference count.
646 ICorDebugThread * GetThread();
647 ShimChain * GetChain(UINT32 index);
648 ICorDebugFrame * GetFrame(UINT32 index);
650 // Get the number of frames and chains.
651 ULONG GetChainCount();
652 ULONG GetFrameCount();
654 RSLock * GetShimLock();
656 // Add ICDChainEnum and ICDFrameEnum.
657 void AddChainEnum(ShimChainEnum * pChainEnum);
658 void AddFrameEnum(ShimFrameEnum * pFrameEnum);
660 // The next two functions are for ShimStackWalkHashTableTraits.
661 ICorDebugThread * GetKey();
662 static UINT32 Hash(ICorDebugThread * pThread);
664 // Check if the specified frame is the leaf frame according to the V2 definition.
665 BOOL IsLeafFrame(ICorDebugFrame * pFrame);
667 // Check if the two specified frames are the same. This function checks the SPs, frame address, etc.
668 // instead of just checking for pointer equality.
669 BOOL IsSameFrame(ICorDebugFrame * pLeft, ICorDebugFrame * pRight);
671 // The following functions are entry point into the ShimStackWalk. They are called by the RS.
672 void EnumerateChains(ICorDebugChainEnum ** ppChainEnum);
674 void GetActiveChain(ICorDebugChain ** ppChain);
675 void GetActiveFrame(ICorDebugFrame ** ppFrame);
676 void GetActiveRegisterSet(ICorDebugRegisterSet ** ppRegisterSet);
678 void GetChainForFrame(ICorDebugFrame * pFrame, ICorDebugChain ** ppChain);
679 void GetCallerForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCallerFrame);
680 void GetCalleeForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCalleeFrame);
683 //---------------------------------------------------------------------------------------
685 // This is a helper class used to store the information of a chain during a stackwalk. A chain is marked
686 // by the CONTEXT on the leaf boundary and a FramePointer on the root boundary. Also, notice that we
687 // are keeping two CONTEXTs. This is because some chain types may cancel a previous unmanaged chain.
688 // For example, a CHAIN_FUNC_EVAL chain cancels any CHAIN_ENTER_UNMANAGED chain immediately preceding
689 // it. In this case, the leaf boundary of the CHAIN_FUNC_EVAL chain is marked by the CONTEXT of the
690 // previous CHAIN_ENTER_MANAGED, not the previous CHAIN_ENTER_UNMANAGED.
696 ChainInfo() : m_rootFP(LEAF_MOST_FRAME), m_reason(CHAIN_NONE), m_fNeedEnterManagedChain(FALSE), m_fLeafNativeContextIsValid(FALSE) {}
698 void CancelUMChain() { m_reason = CHAIN_NONE; }
699 BOOL IsTrackingUMChain() { return (m_reason == CHAIN_ENTER_UNMANAGED); }
701 DT_CONTEXT m_leafNativeContext;
702 DT_CONTEXT m_leafManagedContext;
703 FramePointer m_rootFP;
704 CorDebugChainReason m_reason;
705 bool m_fNeedEnterManagedChain;
706 bool m_fLeafNativeContextIsValid;
709 //---------------------------------------------------------------------------------------
711 // This is a helper class used to store information during a stackwalk. Conceptually it is a simplified
712 // version of FrameInfo used on the LS in V2.
721 // Reset all the per-frame information.
722 void ResetForNextFrame();
724 // During the stackwalk, we need to find out whether we should process the next stack frame or the
725 // next internal frame. These functions help us determine whether we have exhausted one or both
726 // types of frames. The stackwalk is finished when both types are exhausted.
727 bool ExhaustedAllFrames();
728 bool ExhaustedAllStackFrames();
729 bool ExhaustedAllInternalFrames();
731 // Simple helper function to get the current internal frame.
732 ICorDebugInternalFrame2 * GetCurrentInternalFrame();
734 // Check whether we are processing the first frame.
737 // Check whether we are skipping frames because of a child frame.
738 BOOL IsSkippingFrame();
740 // Indicates whether we are dealing with a converted frame.
741 // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
742 BOOL HasConvertedFrame();
744 // Store the child frame we are currently trying to find the parent frame for.
745 // If this is NULL, then we are not skipping frames.
746 RSExtSmartPtr<ICorDebugNativeFrame2> m_pChildFrame;
748 // Store the converted frame, if any.
749 RSExtSmartPtr<ICorDebugInternalFrame2> m_pConvertedInternalFrame2;
751 // Store the array of internal frames. This is an array of RSExtSmartPtrs, and so each element
752 // is protected, and we only need to call Clear() to release each element and free all the memory.
753 RSExtPtrArray<ICorDebugInternalFrame2> m_ppInternalFrame2;
755 UINT32 m_cChain; // number of chains
756 UINT32 m_cFrame; // number of frames
757 UINT32 m_firstFrameInChain; // the index of the first frame in the current chain
758 UINT32 m_cInternalFrames; // number of internal frames
759 UINT32 m_curInternalFrame; // the index of the current internal frame being processed
761 CorDebugInternalFrameType m_internalFrameType;
763 bool m_fExhaustedAllStackFrames;
765 // Indicate whether we are processing an internal frame or a stack frame.
766 bool m_fProcessingInternalFrame;
768 // Indicate whether we should skip the current chain because it's a chain derived from a leaf frame
769 // of type TYPE_INTERNAL. This is the behaviour in V2.
770 // See code:DebuggerWalkStackProc.
773 // Indicate whether the current frame is the first frame we process.
776 // Indicate whether we are processing a converted frame.
777 bool m_fHasConvertedFrame;
780 // A ShimStackWalk is deleted when a process is continued, or when the stack is changed in any way
781 // (e.g. SetIP, EnC, etc.).
785 // Get a FramePointer to mark the root boundary of a chain.
786 FramePointer GetFramePointerForChain(DT_CONTEXT * pContext);
787 FramePointer GetFramePointerForChain(ICorDebugInternalFrame2 * pInternalFrame2);
789 CorDebugInternalFrameType GetInternalFrameType(ICorDebugInternalFrame2 * pFrame2);
791 // Append a frame to the array.
792 void AppendFrame(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo);
793 void AppendFrame(ICorDebugInternalFrame2 * pInternalFrame2, StackWalkInfo * pStackWalkInfo);
795 // Append a chain to the array.
796 void AppendChainWorker(StackWalkInfo * pStackWalkInfo,
797 DT_CONTEXT * pLeafContext,
799 CorDebugChainReason chainReason,
800 BOOL fIsManagedChain);
801 void AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo);
803 // Save information on the ChainInfo regarding the current chain.
804 void SaveChainContext(ICorDebugStackWalk * pSW, ChainInfo * pChainInfo, DT_CONTEXT * pContext);
806 // Check what we are process next, a internal frame or a stack frame.
807 BOOL CheckInternalFrame(ICorDebugFrame * pNextStackFrame,
808 StackWalkInfo * pStackWalkInfo,
809 ICorDebugThread3 * pThread3,
810 ICorDebugStackWalk * pSW);
812 // Convert an ICDInternalFrame to another ICDInternalFrame due to IL methods without metadata.
813 // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
814 BOOL ConvertInternalFrameToDynamicMethod(StackWalkInfo * pStackWalkInfo);
816 // Convert an ICDNativeFrame to an ICDInternalFrame due to IL methods without metadata.
817 // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
818 BOOL ConvertStackFrameToDynamicMethod(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo);
820 // Process an unmanaged chain.
821 BOOL ShouldTrackUMChain(StackWalkInfo * pswInfo);
822 void TrackUMChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo);
824 // Check whether the internal frame is a newly exposed type in Arrowhead. If so, then the shim should
826 BOOL IsV3FrameType(CorDebugInternalFrameType type);
828 // Check whether the specified frame represents a dynamic method.
829 BOOL IsILFrameWithoutMetadata(ICorDebugFrame * pFrame);
831 CDynArray<ShimChain *> m_stackChains; // growable ordered array of chains and frames
832 CDynArray<ICorDebugFrame *> m_stackFrames;
834 ShimChainEnum * m_pChainEnumList; // linked list of ShimChainEnum and ShimFrameEnum
835 ShimFrameEnum * m_pFrameEnumList;
837 // the thread on which we are doing a stackwalk, i.e. the "owning" thread
838 RSExtSmartPtr<ShimProcess> m_pProcess;
839 RSExtSmartPtr<ICorDebugThread> m_pThread;
843 //---------------------------------------------------------------------------------------
845 // This class implements the deprecated ICDChain interface.
848 class ShimChain : public ICorDebugChain
851 ShimChain(ShimStackWalk * pSW,
852 DT_CONTEXT * pContext,
855 UINT32 frameStartIndex,
856 UINT32 frameEndIndex,
857 CorDebugChainReason chainReason,
860 virtual ~ShimChain();
869 ULONG STDMETHODCALLTYPE AddRef();
870 ULONG STDMETHODCALLTYPE Release();
871 COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
877 COM_METHOD GetThread(ICorDebugThread ** ppThread);
878 COM_METHOD GetStackRange(CORDB_ADDRESS * pStart, CORDB_ADDRESS * pEnd);
879 COM_METHOD GetContext(ICorDebugContext ** ppContext);
880 COM_METHOD GetCaller(ICorDebugChain ** ppChain);
881 COM_METHOD GetCallee(ICorDebugChain ** ppChain);
882 COM_METHOD GetPrevious(ICorDebugChain ** ppChain);
883 COM_METHOD GetNext(ICorDebugChain ** ppChain);
884 COM_METHOD IsManaged(BOOL * pManaged);
885 COM_METHOD EnumerateFrames(ICorDebugFrameEnum ** ppFrames);
886 COM_METHOD GetActiveFrame(ICorDebugFrame ** ppFrame);
887 COM_METHOD GetRegisterSet(ICorDebugRegisterSet ** ppRegisters);
888 COM_METHOD GetReason(CorDebugChainReason * pReason);
894 // Get the owning ShimStackWalk.
895 ShimStackWalk * GetShimStackWalk();
897 // Get the first and last index of the frame owned by this chain. This class itself doesn't store the
898 // frames. Rather, the frames are stored on the ShimStackWalk. This class just stores the indices.
899 // Note that the indices are [firstIndex, lastIndex), i.e. the last index is exclusive.
900 UINT32 GetFirstFrameIndex();
901 UINT32 GetLastFrameIndex();
904 // A chain describes a stack range within the stack. This includes a CONTEXT at the start (leafmost)
905 // end of the chain, and a frame pointer where the chain ends (rootmost). This stack range is exposed
906 // publicly via ICDChain::GetStackRange(), and can be used to stitch managed and native stack frames
907 // together into a unified stack.
908 DT_CONTEXT m_context; // the leaf end of the chain
909 FramePointer m_fpRoot; // the root end of the chain
911 ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
912 Volatile<ULONG> m_refCount;
914 // The 0-based index of this chain in the ShimStackWalk's chain array (m_pStackWalk->m_stackChains).
917 // The 0-based index of the first frame owned by this chain in the ShimStackWalk's frame array
918 // (m_pStackWalk->m_stackFrames). See code::ShimChain::GetFirstFrameIndex().
919 UINT32 m_frameStartIndex;
921 // The 0-based index of the last frame owned by this chain in the ShimStackWalk's frame array
922 // (m_pStackWalk->m_stackFrames). This index is exlusive. See code::ShimChain::GetLastFrameIndex().
923 UINT32 m_frameEndIndex;
925 CorDebugChainReason m_chainReason;
926 BOOL m_fIsManaged; // indicates whether this chain contains managed frames
929 RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
933 //---------------------------------------------------------------------------------------
935 // This class implements the deprecated ICDChainEnum interface.
938 class ShimChainEnum : public ICorDebugChainEnum
941 ShimChainEnum(ShimStackWalk * pSW, RSLock * pShimLock);
942 virtual ~ShimChainEnum();
951 ULONG STDMETHODCALLTYPE AddRef();
952 ULONG STDMETHODCALLTYPE Release();
953 COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
959 COM_METHOD Skip(ULONG celt);
961 COM_METHOD Clone(ICorDebugEnum ** ppEnum);
962 COM_METHOD GetCount(ULONG * pcChains);
965 // ICorDebugChainEnum
968 COM_METHOD Next(ULONG cChains, ICorDebugChain * rgpChains[], ULONG * pcChainsFetched);
974 // used to link ShimChainEnums in a list
975 ShimChainEnum * GetNext();
976 void SetNext(ShimChainEnum * pNext);
979 ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
981 // This points to the next ShimChainEnum in the linked list of ShimChainEnums to be cleaned up.
982 // The head of the list is on the ShimStackWalk (m_pStackWalk->m_pChainEnumList).
983 ShimChainEnum * m_pNext;
985 UINT32 m_currentChainIndex; // the index of the current ShimChain being enumerated
986 Volatile<ULONG> m_refCount;
989 RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
993 //---------------------------------------------------------------------------------------
995 // This class implements the deprecated ICDFrameEnum interface.
998 class ShimFrameEnum : public ICorDebugFrameEnum
1001 ShimFrameEnum(ShimStackWalk * pSW, ShimChain * pChain, UINT32 frameStartIndex, UINT32 frameEndIndex, RSLock * pShimLock);
1002 virtual ~ShimFrameEnum();
1011 ULONG STDMETHODCALLTYPE AddRef();
1012 ULONG STDMETHODCALLTYPE Release();
1013 COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
1019 COM_METHOD Skip(ULONG celt);
1021 COM_METHOD Clone(ICorDebugEnum ** ppEnum);
1022 COM_METHOD GetCount(ULONG * pcFrames);
1025 // ICorDebugFrameEnum
1028 COM_METHOD Next(ULONG cFrames, ICorDebugFrame * rgpFrames[], ULONG * pcFramesFetched);
1034 // used to link ShimChainEnums in a list
1035 ShimFrameEnum * GetNext();
1036 void SetNext(ShimFrameEnum * pNext);
1039 ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
1040 ShimChain * m_pChain; // the owning ShimChain
1041 RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
1043 // This points to the next ShimFrameEnum in the linked list of ShimFrameEnums to be cleaned up.
1044 // The head of the list is on the ShimStackWalk (m_pStackWalk->m_pFrameEnumList).
1045 ShimFrameEnum * m_pNext;
1047 UINT32 m_currentFrameIndex; // the current ICDFrame being enumerated
1048 UINT32 m_endFrameIndex; // the last index (exclusive) of the frame owned by the chain;
1049 // see code:ShimChain::GetLastFrameIndex
1050 Volatile<ULONG> m_refCount;
1055 #endif // SHIMPRIV_H