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//*****************************************************************************
5// shimprivate.h
6//
7
8//
9// private header for RS shim which bridges from V2 to V3.
10//*****************************************************************************
11
12#ifndef SHIMPRIV_H
13#define SHIMPRIV_H
14
15#include "helpers.h"
16
17#include "shimdatatarget.h"
18
19#include <shash.h>
20
21// Forward declarations
22class CordbWin32EventThread;
23class Cordb;
24
25class ShimStackWalk;
26class ShimChain;
27class ShimChainEnum;
28class ShimFrameEnum;
29
30// This struct specifies that it's a hash table of ShimStackWalk * using ICorDebugThread as the key.
31struct ShimStackWalkHashTableTraits : public PtrSHashTraits<ShimStackWalk, ICorDebugThread *> {};
32typedef SHash<ShimStackWalkHashTableTraits> ShimStackWalkHashTable;
33
34
35//---------------------------------------------------------------------------------------
36//
37// Simple struct for storing a void *. This is to be used with a SHash hash table.
38//
39
40struct DuplicateCreationEventEntry
41{
42public:
43 DuplicateCreationEventEntry(void * pKey) : m_pKey(pKey) {};
44
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;};
48
49private:
50 void * m_pKey;
51};
52
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.
55struct DuplicateCreationEventsHashTableTraits : public PtrSHashTraits<DuplicateCreationEventEntry, void *> {};
56typedef SHash<DuplicateCreationEventsHashTableTraits> DuplicateCreationEventsHashTable;
57
58//
59// Callback that shim provides, which then queues up the events.
60//
61class ShimProxyCallback :
62 public ICorDebugManagedCallback,
63 public ICorDebugManagedCallback2,
64 public ICorDebugManagedCallback3,
65 public ICorDebugManagedCallback4
66{
67 ShimProcess * m_pShim; // weak reference
68 LONG m_cRef;
69
70public:
71 ShimProxyCallback(ShimProcess * pShim);
72 virtual ~ShimProxyCallback() {}
73
74 // Implement IUnknown
75 ULONG STDMETHODCALLTYPE AddRef();
76 ULONG STDMETHODCALLTYPE Release();
77 COM_METHOD QueryInterface(REFIID riid, void **ppInterface);
78
79 //
80 // Implementation of ICorDebugManagedCallback
81 //
82
83 COM_METHOD Breakpoint( ICorDebugAppDomain *pAppDomain,
84 ICorDebugThread *pThread,
85 ICorDebugBreakpoint *pBreakpoint);
86
87 COM_METHOD StepComplete( ICorDebugAppDomain *pAppDomain,
88 ICorDebugThread *pThread,
89 ICorDebugStepper *pStepper,
90 CorDebugStepReason reason);
91
92 COM_METHOD Break( ICorDebugAppDomain *pAppDomain,
93 ICorDebugThread *thread);
94
95 COM_METHOD Exception( ICorDebugAppDomain *pAppDomain,
96 ICorDebugThread *pThread,
97 BOOL unhandled);
98
99 COM_METHOD EvalComplete( ICorDebugAppDomain *pAppDomain,
100 ICorDebugThread *pThread,
101 ICorDebugEval *pEval);
102
103 COM_METHOD EvalException( ICorDebugAppDomain *pAppDomain,
104 ICorDebugThread *pThread,
105 ICorDebugEval *pEval);
106
107 COM_METHOD CreateProcess( ICorDebugProcess *pProcess);
108 void QueueCreateProcess( ICorDebugProcess *pProcess);
109
110 COM_METHOD ExitProcess( ICorDebugProcess *pProcess);
111
112 COM_METHOD CreateThread( ICorDebugAppDomain *pAppDomain, ICorDebugThread *thread);
113
114
115 COM_METHOD ExitThread( ICorDebugAppDomain *pAppDomain, ICorDebugThread *thread);
116
117 COM_METHOD LoadModule( ICorDebugAppDomain *pAppDomain, ICorDebugModule *pModule);
118
119 void FakeLoadModule(ICorDebugAppDomain *pAppDomain, ICorDebugModule *pModule);
120
121 COM_METHOD UnloadModule( ICorDebugAppDomain *pAppDomain, ICorDebugModule *pModule);
122
123 COM_METHOD LoadClass( ICorDebugAppDomain *pAppDomain, ICorDebugClass *c);
124
125 COM_METHOD UnloadClass( ICorDebugAppDomain *pAppDomain, ICorDebugClass *c);
126
127 COM_METHOD DebuggerError( ICorDebugProcess *pProcess, HRESULT errorHR, DWORD errorCode);
128
129 COM_METHOD LogMessage( ICorDebugAppDomain *pAppDomain,
130 ICorDebugThread *pThread,
131 LONG lLevel,
132 __in LPWSTR pLogSwitchName,
133 __in LPWSTR pMessage);
134
135 COM_METHOD LogSwitch( ICorDebugAppDomain *pAppDomain,
136 ICorDebugThread *pThread,
137 LONG lLevel,
138 ULONG ulReason,
139 __in LPWSTR pLogSwitchName,
140 __in LPWSTR pParentName);
141
142 COM_METHOD CreateAppDomain(ICorDebugProcess *pProcess,
143 ICorDebugAppDomain *pAppDomain);
144
145 COM_METHOD ExitAppDomain(ICorDebugProcess *pProcess,
146 ICorDebugAppDomain *pAppDomain);
147
148 COM_METHOD LoadAssembly(ICorDebugAppDomain *pAppDomain,
149 ICorDebugAssembly *pAssembly);
150
151 COM_METHOD UnloadAssembly(ICorDebugAppDomain *pAppDomain,
152 ICorDebugAssembly *pAssembly);
153
154 COM_METHOD ControlCTrap(ICorDebugProcess *pProcess);
155
156 COM_METHOD NameChange(ICorDebugAppDomain *pAppDomain, ICorDebugThread *pThread);
157
158
159 COM_METHOD UpdateModuleSymbols( ICorDebugAppDomain *pAppDomain,
160 ICorDebugModule *pModule,
161 IStream *pSymbolStream);
162
163 COM_METHOD EditAndContinueRemap( ICorDebugAppDomain *pAppDomain,
164 ICorDebugThread *pThread,
165 ICorDebugFunction *pFunction,
166 BOOL fAccurate);
167
168 COM_METHOD BreakpointSetError( ICorDebugAppDomain *pAppDomain,
169 ICorDebugThread *pThread,
170 ICorDebugBreakpoint *pBreakpoint,
171 DWORD dwError);
172
173 ///
174 /// Implementation of ICorDebugManagedCallback2
175 ///
176 COM_METHOD FunctionRemapOpportunity( ICorDebugAppDomain *pAppDomain,
177 ICorDebugThread *pThread,
178 ICorDebugFunction *pOldFunction,
179 ICorDebugFunction *pNewFunction,
180 ULONG32 oldILOffset);
181
182 COM_METHOD CreateConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId, __in LPWSTR pConnName);
183
184 COM_METHOD ChangeConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId );
185
186
187 COM_METHOD DestroyConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId);
188
189 COM_METHOD Exception(ICorDebugAppDomain *pAppDomain,
190 ICorDebugThread *pThread,
191 ICorDebugFrame *pFrame,
192 ULONG32 nOffset,
193 CorDebugExceptionCallbackType dwEventType,
194 DWORD dwFlags );
195
196 COM_METHOD ExceptionUnwind(ICorDebugAppDomain *pAppDomain,
197 ICorDebugThread *pThread,
198 CorDebugExceptionUnwindCallbackType dwEventType,
199 DWORD dwFlags);
200
201 COM_METHOD FunctionRemapComplete( ICorDebugAppDomain *pAppDomain,
202 ICorDebugThread *pThread,
203 ICorDebugFunction *pFunction);
204
205 COM_METHOD MDANotification(ICorDebugController * pController, ICorDebugThread *pThread, ICorDebugMDA * pMDA);
206
207 ///
208 /// Implementation of ICorDebugManagedCallback3
209 ///
210
211 // Implementation of ICorDebugManagedCallback3::CustomNotification
212 COM_METHOD CustomNotification(ICorDebugThread * pThread, ICorDebugAppDomain * pAppDomain);
213
214 ///
215 /// Implementation of ICorDebugManagedCallback4
216 ///
217
218 // Implementation of ICorDebugManagedCallback4::BeforeGarbageCollection
219 COM_METHOD ShimProxyCallback::BeforeGarbageCollection(ICorDebugProcess* pProcess);
220
221 // Implementation of ICorDebugManagedCallback4::AfterGarbageCollection
222 COM_METHOD ShimProxyCallback::AfterGarbageCollection(ICorDebugProcess* pProcess);
223
224 // Implementation of ICorDebugManagedCallback4::DataBreakpoint
225 COM_METHOD ShimProxyCallback::DataBreakpoint(ICorDebugProcess* pProcess, ICorDebugThread* pThread, BYTE* pContext, ULONG32 contextSize);
226};
227
228
229//
230// Base class for event queue. These are nested into a singly linked list.
231// Shim maintains event queue
232//
233class ManagedEvent
234{
235public:
236 // Need virtual dtor since this is a base class.
237 virtual ~ManagedEvent();
238
239#ifdef _DEBUG
240 // For debugging, get a pointer value that can identify the type of this event.
241 void * GetDebugCookie();
242#endif
243
244 // We'll have a lot of derived classes of ManagedEvent, and so encapsulating the arguments
245 // for the Dispatch() function lets us juggle them around easily without hitting every signature.
246 class DispatchArgs
247 {
248 public:
249 DispatchArgs(ICorDebugManagedCallback * pCallback1, ICorDebugManagedCallback2 * pCallback2, ICorDebugManagedCallback3 * pCallback3, ICorDebugManagedCallback4 * pCallback4);
250
251 ICorDebugManagedCallback * GetCallback1();
252 ICorDebugManagedCallback2 * GetCallback2();
253 ICorDebugManagedCallback3 * GetCallback3();
254 ICorDebugManagedCallback4 * GetCallback4();
255
256
257 protected:
258 ICorDebugManagedCallback * m_pCallback1;
259 ICorDebugManagedCallback2 * m_pCallback2;
260 ICorDebugManagedCallback3 * m_pCallback3;
261 ICorDebugManagedCallback4 * m_pCallback4;
262 };
263
264 // Returns: value of callback from end-user
265 virtual HRESULT Dispatch(DispatchArgs args) = 0;
266
267
268 // Returns 0 if none.
269 DWORD GetOSTid();
270
271protected:
272 // Ctor for events with thread-affinity
273 ManagedEvent(ICorDebugThread * pThread);
274
275 // Ctor for events without thread affinity.
276 ManagedEvent();
277
278 friend class ManagedEventQueue;
279 ManagedEvent * m_pNext;
280
281 DWORD m_dwThreadId;
282};
283
284//
285// Queue of managed events.
286// Shim can use this to collect managed debug events, queue them, and then drain the event
287// queue when a sync-complete occurs.
288// Event queue gets initialized with a lock and will lock internally.
289class ManagedEventQueue
290{
291public:
292 ManagedEventQueue();
293
294
295 void Init(RSLock * pLock);
296
297 // Remove event from the top. Caller then takes ownership of Event and will call Delete on it.
298 // Caller checks IsEmpty() first.
299 ManagedEvent * Dequeue();
300
301 // Queue owns the event and will delete it (unless it's dequeued first).
302 void QueueEvent(ManagedEvent * pEvent);
303
304 // Test if event queue is empty
305 bool IsEmpty();
306
307 // Empty event queue and delete all objects
308 void DeleteAll();
309
310 // Nothrows
311 BOOL HasQueuedCallbacks(ICorDebugThread * pThread);
312
313 // Save the current queue and start with a new empty queue
314 void SuspendQueue();
315
316 // Restore the saved queue onto the end of the current queue
317 void RestoreSuspendedQueue();
318
319protected:
320 // The lock to be used for synchronizing all access to the queue
321 RSLock * m_pLock;
322
323 // If empty, First + Last are both NULL.
324 // Else first points to the head of the queue; and Last points to the end of the queue.
325 ManagedEvent * m_pFirstEvent;
326 ManagedEvent * m_pLastEvent;
327
328};
329
330
331//---------------------------------------------------------------------------------------
332//
333// Shim's layer on top of a process.
334//
335// Notes:
336// This contains a V3 ICorDebugProcess, and provides V2 ICDProcess functionality.
337//
338class ShimProcess
339{
340 // Delete via Ref count semantics.
341 ~ShimProcess();
342public:
343 // Initialize ref count is 0.
344 ShimProcess();
345
346 // Lifetime semantics handled by reference counting.
347 void AddRef();
348 void Release();
349
350 // Release all resources. Can be called multiple times.
351 void Dispose();
352
353 // Initialization phases.
354 // 1. allocate new ShimProcess(). This lets us spin up a Win32 EventThread, which can then
355 // be used to
356 // 2. Call ShimProcess::CreateProcess/DebugActiveProcess. This will call CreateAndStartWin32ET to
357 // craete the w32et.
358 // 3. Create OS-debugging pipeline. This establishes the physical OS process and gets us a pid/handle
359 // 4. pShim->InitializeDataTarget - this creates a reader/writer abstraction around the OS process.
360 // 5. pShim->SetProcess() - this connects the Shim to the ICDProcess object.
361 HRESULT InitializeDataTarget(const ProcessDescriptor * pProcessDescriptor);
362 void SetProcess(ICorDebugProcess * pProcess);
363
364 //-----------------------------------------------------------
365 // Creation
366 //-----------------------------------------------------------
367
368 static HRESULT CreateProcess(
369 Cordb * pCordb,
370 ICorDebugRemoteTarget * pRemoteTarget,
371 LPCWSTR programName,
372 __in_z LPWSTR programArgs,
373 LPSECURITY_ATTRIBUTES lpProcessAttributes,
374 LPSECURITY_ATTRIBUTES lpThreadAttributes,
375 BOOL bInheritHandles,
376 DWORD dwCreationFlags,
377 PVOID lpEnvironment,
378 LPCWSTR lpCurrentDirectory,
379 LPSTARTUPINFOW lpStartupInfo,
380 LPPROCESS_INFORMATION lpProcessInformation,
381 CorDebugCreateProcessFlags corDebugFlags
382 );
383
384 static HRESULT DebugActiveProcess(
385 Cordb * pCordb,
386 ICorDebugRemoteTarget * pRemoteTarget,
387 const ProcessDescriptor * pProcessDescriptor,
388 BOOL win32Attach
389
390 );
391
392 // Locates the DAC module adjacent to DBI
393 static HMODULE GetDacModule();
394
395 //
396 // Functions used by CordbProcess
397 //
398
399 // Determine if the calling thread is the win32 event thread.
400 bool IsWin32EventThread();
401
402
403 // Expose the W32ET thread to the CordbProcess so that it can emulate V2 behavior
404 CordbWin32EventThread * GetWin32EventThread();
405
406 // Accessor wrapper to mark whether we're interop-debugging.
407 void SetIsInteropDebugging(bool fIsInteropDebugging);
408
409 // Handle a debug event.
410 HRESULT HandleWin32DebugEvent(const DEBUG_EVENT * pEvent);
411
412 ManagedEventQueue * GetManagedEventQueue();
413
414 ManagedEvent * DequeueManagedEvent();
415
416 ShimProxyCallback * GetShimCallback();
417
418 // Begin Queing the fake attach events.
419 void BeginQueueFakeAttachEvents();
420
421 // Queue fake attach events if needed
422 void QueueFakeAttachEventsIfNeeded(bool fRealCreateProcessEvent);
423
424 // Actually do the work to queue the fake attach events.
425 void QueueFakeAttachEvents();
426
427 // Helper to queue fake assembly and mdule events
428 void QueueFakeAssemblyAndModuleEvent(ICorDebugAssembly * pAssembly);
429
430 // Queue fake thread-create events on attach. Order via native threads.
431 HRESULT QueueFakeThreadAttachEventsNativeOrder();
432
433 // Queue fake thread-create events on attach. No ordering.
434 HRESULT QueueFakeThreadAttachEventsNoOrder();
435
436 bool IsThreadSuspendedOrHijacked(ICorDebugThread * pThread);
437
438 // Expose m_attached to CordbProcess.
439 bool GetAttached();
440
441 // We need to know whether we are in the CreateProcess callback to be able to
442 // return the v2.0 hresults from code:CordbProcess::SetDesiredNGENCompilerFlags
443 // when we are using the shim.
444 //
445 // Expose m_fInCreateProcess
446 bool GetInCreateProcess();
447 void SetInCreateProcess(bool value);
448
449 // We need to know whether we are in the FakeLoadModule callback to be able to
450 // return the v2.0 hresults from code:CordbModule::SetJITCompilerFlags when
451 // we are using the shim.
452 //
453 // Expose m_fInLoadModule
454 bool GetInLoadModule();
455 void SetInLoadModule(bool value);
456
457 // When we get a continue, we need to clear the flags indicating we're still in a callback
458 void NotifyOnContinue ();
459
460 // The RS calls this function when the stack is about to be changed in any way, e.g. continue, SetIP,
461 // etc.
462 void NotifyOnStackInvalidate();
463
464 // Helpers to filter HRs to emulate V2 error codes.
465 HRESULT FilterSetNgenHresult(HRESULT hr);
466 HRESULT FilterSetJitFlagsHresult(HRESULT hr);
467
468 //.............................................................
469
470
471 // Lookup or create a ShimStackWalk for the specified thread. ShimStackWalk and ICorDebugThread has
472 // a 1:1 relationship.
473 ShimStackWalk * LookupOrCreateShimStackWalk(ICorDebugThread * pThread);
474
475 // Clear all ShimStackWalks and flush all the caches.
476 void ClearAllShimStackWalk();
477
478 // Get the corresponding ICDProcess object.
479 ICorDebugProcess * GetProcess();
480
481 // Get the data target to access the debuggee.
482 ICorDebugMutableDataTarget * GetDataTarget();
483
484 // Get the native event pipeline
485 INativeEventPipeline * GetNativePipeline();
486
487 // Are we interop-debugging?
488 bool IsInteropDebugging();
489
490
491 // Finish all the necessary initialization work and queue up any necessary fake attach events before
492 // dispatching an event.
493 void PreDispatchEvent(bool fRealCreateProcessEvent = false);
494
495 // Look for a CLR in the process and if found, return it's instance ID
496 HRESULT FindLoadedCLR(CORDB_ADDRESS * pClrInstanceId);
497
498 // Retrieve the IP address and the port number of the debugger proxy.
499 MachineInfo GetMachineInfo();
500
501 // Add an entry in the duplicate creation event hash table for the specified key.
502 void AddDuplicateCreationEvent(void * pKey);
503
504 // Check if a duplicate creation event entry exists for the specified key. If so, remove it.
505 bool RemoveDuplicateCreationEventIfPresent(void * pKey);
506
507 void SetMarkAttachPendingEvent();
508
509 void SetTerminatingEvent();
510
511 RSLock * GetShimLock();
512
513protected:
514
515 // Reference count.
516 LONG m_ref;
517
518 //
519 // Helper functions
520 //
521 HRESULT CreateAndStartWin32ET(Cordb * pCordb);
522
523 //
524 // Synchronization events to ensure that AttachPending bit is marked before DebugActiveProcess
525 // returns or debugger is detaching
526 //
527 HANDLE m_markAttachPendingEvent;
528 HANDLE m_terminatingEvent;
529
530 // Finds the base address of [core]clr.dll
531 CORDB_ADDRESS GetCLRInstanceBaseAddress();
532
533 //
534 // Event Queues
535 //
536
537 // Shim maintains event queue to emulate V2 semantics.
538 // In V2, IcorDebug internally queued debug events and dispatched them
539 // once the debuggee was synchronized. In V3, ICorDebug dispatches events immediately.
540 // The event queue is moved into the shim to build V2 semantics of V3 behavior.
541 ManagedEventQueue m_eventQueue;
542
543 // Lock to protect Shim data structures. This is currently a small lock that
544 // protects leaf-level structures, but it may grow to protect larger things.
545 RSLock m_ShimLock;
546
547 // Serializes ShimProcess:Dispose() with other ShimProcess functions. For now, this
548 // cannot be the same as m_ShimLock. See LL_SHIM_PROCESS_DISPOSE_LOCK for more
549 // information
550 RSLock m_ShimProcessDisposeLock;
551
552 // Sticky bit to do lazy-initialization on the first managed event.
553 bool m_fFirstManagedEvent;
554
555 RSExtSmartPtr<ShimProxyCallback> m_pShimCallback;
556
557
558 // This is for emulating V2 Attach. Initialized to false, and then set to true if we ened to send fake attach events.
559 // Reset to false once the events are sent. See code:ShimProcess::QueueFakeAttachEventsIfNeeded
560 bool m_fNeedFakeAttachEvents;
561
562 // True if the process was created from an attach (DebugActiveProcess); False if it was launched (CreateProcess)
563 // This is used to send an Attach IPC event, and also used to provide more specific error codes.
564 bool m_attached;
565
566 // True iff we are in the shim's CreateProcess callback. This is used to determine which hresult to
567 // return from code:CordbProcess::SetDesiredNGENCompilerFlags so we correctly emulate the behavior of v2.0.
568 // This is set at the beginning of the callback and cleared in code:CordbProcess::ContinueInternal.
569 bool m_fInCreateProcess;
570
571 // True iff we are in the shim's FakeLoadModule callback. This is used to determine which hresult to
572 // return from code:CordbModule::SetJITCompilerFlags so we correctly emulate the behavior of v2.0.
573 // This is set at the beginning of the callback and cleared in code:CordbProcess::ContinueInternal.
574 bool m_fInLoadModule;
575 //
576 // Data
577 //
578
579 // Pointer to CordbProcess.
580 // @dbgtodo shim: We'd like this to eventually go through public interfaces (ICorDebugProcess)
581 IProcessShimHooks * m_pProcess; // Reference is kept by m_pIProcess;
582 RSExtSmartPtr<ICorDebugProcess> m_pIProcess;
583
584 // Win32EvenThread, which is the thread that uses the native debug API.
585 CordbWin32EventThread * m_pWin32EventThread;
586
587 // Actual data-target. Since we're shimming V2 scenarios, and V3 is always
588 // live-debugging, this is always a live data-target.
589 RSExtSmartPtr<ShimDataTarget> m_pLiveDataTarget;
590
591
592 // If true, the shim is emulating interop-debugging
593 // If false, the shim is emulating managed-only debugging.
594 // Both managed and native debugging have the same underlying pipeline (built
595 // on native-debug events). So the only difference is how they handle those events.
596 bool m_fIsInteropDebugging;
597
598 // true iff Dispose() was called. Consult this and do your work under m_ShimProcessDisposeLock
599 // to serialize yourself against a call to Dispose(). This protects your work
600 // from the user doing a Debugger Detach in the middle.
601 bool m_fIsDisposed;
602
603 //.............................................................................
604 //
605 // Members used for handling native events when managed-only debugging.
606 //
607 //.............................................................................
608
609 // Default handler for native events when managed-only debugging.
610 void DefaultEventHandler(const DEBUG_EVENT * pEvent, DWORD * pdwContinueStatus);
611
612 // Given a debug event, track the file handles.
613 void TrackFileHandleForDebugEvent(const DEBUG_EVENT * pEvent);
614
615 // Have we gotten the loader breakpoint yet?
616 // A Debugger needs to do special work to skip the loader breakpoint,
617 // and that's also when it should dispatch the faked managed attach events.
618 bool m_loaderBPReceived;
619
620 // Raw callback for ContinueStatusChanged from Data-target.
621 static HRESULT ContinueStatusChanged(void * pUserData, DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus);
622
623 // Real worker to update ContinueStatusChangedData
624 HRESULT ContinueStatusChangedWorker(DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus);
625
626 struct ContinueStatusChangedData
627 {
628 void Clear();
629 bool IsSet();
630 // Tid of Thread changed
631 DWORD m_dwThreadId;
632
633 // New continue status.
634 CORDB_CONTINUE_STATUS m_status;
635 } m_ContinueStatusChangedData;
636
637 // the hash table of ShimStackWalks
638 ShimStackWalkHashTable * m_pShimStackWalkHashTable;
639
640 // the hash table of duplicate creation events
641 DuplicateCreationEventsHashTable * m_pDupeEventsHashTable;
642
643 MachineInfo m_machineInfo;
644};
645
646
647//---------------------------------------------------------------------------------------
648//
649// This is the container class of ShimChains, ICorDebugFrames, ShimChainEnums, and ShimFrameEnums.
650// It has a 1:1 relationship with ICorDebugThreads. Upon creation, this class walks the entire stack and
651// caches all the stack frames and chains. The enumerators are created on demand.
652//
653
654class ShimStackWalk
655{
656public:
657 ShimStackWalk(ShimProcess * pProcess, ICorDebugThread * pThread);
658 ~ShimStackWalk();
659
660 // These functions do not adjust the reference count.
661 ICorDebugThread * GetThread();
662 ShimChain * GetChain(UINT32 index);
663 ICorDebugFrame * GetFrame(UINT32 index);
664
665 // Get the number of frames and chains.
666 ULONG GetChainCount();
667 ULONG GetFrameCount();
668
669 RSLock * GetShimLock();
670
671 // Add ICDChainEnum and ICDFrameEnum.
672 void AddChainEnum(ShimChainEnum * pChainEnum);
673 void AddFrameEnum(ShimFrameEnum * pFrameEnum);
674
675 // The next two functions are for ShimStackWalkHashTableTraits.
676 ICorDebugThread * GetKey();
677 static UINT32 Hash(ICorDebugThread * pThread);
678
679 // Check if the specified frame is the leaf frame according to the V2 definition.
680 BOOL IsLeafFrame(ICorDebugFrame * pFrame);
681
682 // Check if the two specified frames are the same. This function checks the SPs, frame address, etc.
683 // instead of just checking for pointer equality.
684 BOOL IsSameFrame(ICorDebugFrame * pLeft, ICorDebugFrame * pRight);
685
686 // The following functions are entry point into the ShimStackWalk. They are called by the RS.
687 void EnumerateChains(ICorDebugChainEnum ** ppChainEnum);
688
689 void GetActiveChain(ICorDebugChain ** ppChain);
690 void GetActiveFrame(ICorDebugFrame ** ppFrame);
691 void GetActiveRegisterSet(ICorDebugRegisterSet ** ppRegisterSet);
692
693 void GetChainForFrame(ICorDebugFrame * pFrame, ICorDebugChain ** ppChain);
694 void GetCallerForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCallerFrame);
695 void GetCalleeForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCalleeFrame);
696
697private:
698 //---------------------------------------------------------------------------------------
699 //
700 // This is a helper class used to store the information of a chain during a stackwalk. A chain is marked
701 // by the CONTEXT on the leaf boundary and a FramePointer on the root boundary. Also, notice that we
702 // are keeping two CONTEXTs. This is because some chain types may cancel a previous unmanaged chain.
703 // For example, a CHAIN_FUNC_EVAL chain cancels any CHAIN_ENTER_UNMANAGED chain immediately preceding
704 // it. In this case, the leaf boundary of the CHAIN_FUNC_EVAL chain is marked by the CONTEXT of the
705 // previous CHAIN_ENTER_MANAGED, not the previous CHAIN_ENTER_UNMANAGED.
706 //
707
708 struct ChainInfo
709 {
710 public:
711 ChainInfo() : m_rootFP(LEAF_MOST_FRAME), m_reason(CHAIN_NONE), m_fNeedEnterManagedChain(FALSE), m_fLeafNativeContextIsValid(FALSE) {}
712
713 void CancelUMChain() { m_reason = CHAIN_NONE; }
714 BOOL IsTrackingUMChain() { return (m_reason == CHAIN_ENTER_UNMANAGED); }
715
716 DT_CONTEXT m_leafNativeContext;
717 DT_CONTEXT m_leafManagedContext;
718 FramePointer m_rootFP;
719 CorDebugChainReason m_reason;
720 bool m_fNeedEnterManagedChain;
721 bool m_fLeafNativeContextIsValid;
722 };
723
724 //---------------------------------------------------------------------------------------
725 //
726 // This is a helper class used to store information during a stackwalk. Conceptually it is a simplified
727 // version of FrameInfo used on the LS in V2.
728 //
729
730 struct StackWalkInfo
731 {
732 public:
733 StackWalkInfo();
734 ~StackWalkInfo();
735
736 // Reset all the per-frame information.
737 void ResetForNextFrame();
738
739 // During the stackwalk, we need to find out whether we should process the next stack frame or the
740 // next internal frame. These functions help us determine whether we have exhausted one or both
741 // types of frames. The stackwalk is finished when both types are exhausted.
742 bool ExhaustedAllFrames();
743 bool ExhaustedAllStackFrames();
744 bool ExhaustedAllInternalFrames();
745
746 // Simple helper function to get the current internal frame.
747 ICorDebugInternalFrame2 * GetCurrentInternalFrame();
748
749 // Check whether we are processing the first frame.
750 BOOL IsLeafFrame();
751
752 // Check whether we are skipping frames because of a child frame.
753 BOOL IsSkippingFrame();
754
755 // Indicates whether we are dealing with a converted frame.
756 // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
757 BOOL HasConvertedFrame();
758
759 // Store the child frame we are currently trying to find the parent frame for.
760 // If this is NULL, then we are not skipping frames.
761 RSExtSmartPtr<ICorDebugNativeFrame2> m_pChildFrame;
762
763 // Store the converted frame, if any.
764 RSExtSmartPtr<ICorDebugInternalFrame2> m_pConvertedInternalFrame2;
765
766 // Store the array of internal frames. This is an array of RSExtSmartPtrs, and so each element
767 // is protected, and we only need to call Clear() to release each element and free all the memory.
768 RSExtPtrArray<ICorDebugInternalFrame2> m_ppInternalFrame2;
769
770 UINT32 m_cChain; // number of chains
771 UINT32 m_cFrame; // number of frames
772 UINT32 m_firstFrameInChain; // the index of the first frame in the current chain
773 UINT32 m_cInternalFrames; // number of internal frames
774 UINT32 m_curInternalFrame; // the index of the current internal frame being processed
775
776 CorDebugInternalFrameType m_internalFrameType;
777
778 bool m_fExhaustedAllStackFrames;
779
780 // Indicate whether we are processing an internal frame or a stack frame.
781 bool m_fProcessingInternalFrame;
782
783 // Indicate whether we should skip the current chain because it's a chain derived from a leaf frame
784 // of type TYPE_INTERNAL. This is the behaviour in V2.
785 // See code:DebuggerWalkStackProc.
786 bool m_fSkipChain;
787
788 // Indicate whether the current frame is the first frame we process.
789 bool m_fLeafFrame;
790
791 // Indicate whether we are processing a converted frame.
792 bool m_fHasConvertedFrame;
793 };
794
795 // A ShimStackWalk is deleted when a process is continued, or when the stack is changed in any way
796 // (e.g. SetIP, EnC, etc.).
797 void Populate();
798 void Clear();
799
800 // Get a FramePointer to mark the root boundary of a chain.
801 FramePointer GetFramePointerForChain(DT_CONTEXT * pContext);
802 FramePointer GetFramePointerForChain(ICorDebugInternalFrame2 * pInternalFrame2);
803
804 CorDebugInternalFrameType GetInternalFrameType(ICorDebugInternalFrame2 * pFrame2);
805
806 // Append a frame to the array.
807 void AppendFrame(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo);
808 void AppendFrame(ICorDebugInternalFrame2 * pInternalFrame2, StackWalkInfo * pStackWalkInfo);
809
810 // Append a chain to the array.
811 void AppendChainWorker(StackWalkInfo * pStackWalkInfo,
812 DT_CONTEXT * pLeafContext,
813 FramePointer fpRoot,
814 CorDebugChainReason chainReason,
815 BOOL fIsManagedChain);
816 void AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo);
817
818 // Save information on the ChainInfo regarding the current chain.
819 void SaveChainContext(ICorDebugStackWalk * pSW, ChainInfo * pChainInfo, DT_CONTEXT * pContext);
820
821 // Check what we are process next, a internal frame or a stack frame.
822 BOOL CheckInternalFrame(ICorDebugFrame * pNextStackFrame,
823 StackWalkInfo * pStackWalkInfo,
824 ICorDebugThread3 * pThread3,
825 ICorDebugStackWalk * pSW);
826
827 // Convert an ICDInternalFrame to another ICDInternalFrame due to IL methods without metadata.
828 // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
829 BOOL ConvertInternalFrameToDynamicMethod(StackWalkInfo * pStackWalkInfo);
830
831 // Convert an ICDNativeFrame to an ICDInternalFrame due to IL methods without metadata.
832 // See code:CordbThread::ConvertFrameForILMethodWithoutMetadata.
833 BOOL ConvertStackFrameToDynamicMethod(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo);
834
835 // Process an unmanaged chain.
836 BOOL ShouldTrackUMChain(StackWalkInfo * pswInfo);
837 void TrackUMChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo);
838
839 // Check whether the internal frame is a newly exposed type in Arrowhead. If so, then the shim should
840 // not expose it.
841 BOOL IsV3FrameType(CorDebugInternalFrameType type);
842
843 // Check whether the specified frame represents a dynamic method.
844 BOOL IsILFrameWithoutMetadata(ICorDebugFrame * pFrame);
845
846 CDynArray<ShimChain *> m_stackChains; // growable ordered array of chains and frames
847 CDynArray<ICorDebugFrame *> m_stackFrames;
848
849 ShimChainEnum * m_pChainEnumList; // linked list of ShimChainEnum and ShimFrameEnum
850 ShimFrameEnum * m_pFrameEnumList;
851
852 // the thread on which we are doing a stackwalk, i.e. the "owning" thread
853 RSExtSmartPtr<ShimProcess> m_pProcess;
854 RSExtSmartPtr<ICorDebugThread> m_pThread;
855};
856
857
858//---------------------------------------------------------------------------------------
859//
860// This class implements the deprecated ICDChain interface.
861//
862
863class ShimChain : public ICorDebugChain
864{
865public:
866 ShimChain(ShimStackWalk * pSW,
867 DT_CONTEXT * pContext,
868 FramePointer fpRoot,
869 UINT32 chainIndex,
870 UINT32 frameStartIndex,
871 UINT32 frameEndIndex,
872 CorDebugChainReason chainReason,
873 BOOL fIsManaged,
874 RSLock * pShimLock);
875 virtual ~ShimChain();
876
877 void Neuter();
878 BOOL IsNeutered();
879
880 //
881 // IUnknown
882 //
883
884 ULONG STDMETHODCALLTYPE AddRef();
885 ULONG STDMETHODCALLTYPE Release();
886 COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
887
888 //
889 // ICorDebugChain
890 //
891
892 COM_METHOD GetThread(ICorDebugThread ** ppThread);
893 COM_METHOD GetStackRange(CORDB_ADDRESS * pStart, CORDB_ADDRESS * pEnd);
894 COM_METHOD GetContext(ICorDebugContext ** ppContext);
895 COM_METHOD GetCaller(ICorDebugChain ** ppChain);
896 COM_METHOD GetCallee(ICorDebugChain ** ppChain);
897 COM_METHOD GetPrevious(ICorDebugChain ** ppChain);
898 COM_METHOD GetNext(ICorDebugChain ** ppChain);
899 COM_METHOD IsManaged(BOOL * pManaged);
900 COM_METHOD EnumerateFrames(ICorDebugFrameEnum ** ppFrames);
901 COM_METHOD GetActiveFrame(ICorDebugFrame ** ppFrame);
902 COM_METHOD GetRegisterSet(ICorDebugRegisterSet ** ppRegisters);
903 COM_METHOD GetReason(CorDebugChainReason * pReason);
904
905 //
906 // accessors
907 //
908
909 // Get the owning ShimStackWalk.
910 ShimStackWalk * GetShimStackWalk();
911
912 // Get the first and last index of the frame owned by this chain. This class itself doesn't store the
913 // frames. Rather, the frames are stored on the ShimStackWalk. This class just stores the indices.
914 // Note that the indices are [firstIndex, lastIndex), i.e. the last index is exclusive.
915 UINT32 GetFirstFrameIndex();
916 UINT32 GetLastFrameIndex();
917
918private:
919 // A chain describes a stack range within the stack. This includes a CONTEXT at the start (leafmost)
920 // end of the chain, and a frame pointer where the chain ends (rootmost). This stack range is exposed
921 // publicly via ICDChain::GetStackRange(), and can be used to stitch managed and native stack frames
922 // together into a unified stack.
923 DT_CONTEXT m_context; // the leaf end of the chain
924 FramePointer m_fpRoot; // the root end of the chain
925
926 ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
927 Volatile<ULONG> m_refCount;
928
929 // The 0-based index of this chain in the ShimStackWalk's chain array (m_pStackWalk->m_stackChains).
930 UINT32 m_chainIndex;
931
932 // The 0-based index of the first frame owned by this chain in the ShimStackWalk's frame array
933 // (m_pStackWalk->m_stackFrames). See code::ShimChain::GetFirstFrameIndex().
934 UINT32 m_frameStartIndex;
935
936 // The 0-based index of the last frame owned by this chain in the ShimStackWalk's frame array
937 // (m_pStackWalk->m_stackFrames). This index is exlusive. See code::ShimChain::GetLastFrameIndex().
938 UINT32 m_frameEndIndex;
939
940 CorDebugChainReason m_chainReason;
941 BOOL m_fIsManaged; // indicates whether this chain contains managed frames
942 BOOL m_fIsNeutered;
943
944 RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
945};
946
947
948//---------------------------------------------------------------------------------------
949//
950// This class implements the deprecated ICDChainEnum interface.
951//
952
953class ShimChainEnum : public ICorDebugChainEnum
954{
955public:
956 ShimChainEnum(ShimStackWalk * pSW, RSLock * pShimLock);
957 virtual ~ShimChainEnum();
958
959 void Neuter();
960 BOOL IsNeutered();
961
962 //
963 // IUnknown
964 //
965
966 ULONG STDMETHODCALLTYPE AddRef();
967 ULONG STDMETHODCALLTYPE Release();
968 COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
969
970 //
971 // ICorDebugEnum
972 //
973
974 COM_METHOD Skip(ULONG celt);
975 COM_METHOD Reset();
976 COM_METHOD Clone(ICorDebugEnum ** ppEnum);
977 COM_METHOD GetCount(ULONG * pcChains);
978
979 //
980 // ICorDebugChainEnum
981 //
982
983 COM_METHOD Next(ULONG cChains, ICorDebugChain * rgpChains[], ULONG * pcChainsFetched);
984
985 //
986 // accessors
987 //
988
989 // used to link ShimChainEnums in a list
990 ShimChainEnum * GetNext();
991 void SetNext(ShimChainEnum * pNext);
992
993private:
994 ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
995
996 // This points to the next ShimChainEnum in the linked list of ShimChainEnums to be cleaned up.
997 // The head of the list is on the ShimStackWalk (m_pStackWalk->m_pChainEnumList).
998 ShimChainEnum * m_pNext;
999
1000 UINT32 m_currentChainIndex; // the index of the current ShimChain being enumerated
1001 Volatile<ULONG> m_refCount;
1002 BOOL m_fIsNeutered;
1003
1004 RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
1005};
1006
1007
1008//---------------------------------------------------------------------------------------
1009//
1010// This class implements the deprecated ICDFrameEnum interface.
1011//
1012
1013class ShimFrameEnum : public ICorDebugFrameEnum
1014{
1015public:
1016 ShimFrameEnum(ShimStackWalk * pSW, ShimChain * pChain, UINT32 frameStartIndex, UINT32 frameEndIndex, RSLock * pShimLock);
1017 virtual ~ShimFrameEnum();
1018
1019 void Neuter();
1020 BOOL IsNeutered();
1021
1022 //
1023 // IUnknown
1024 //
1025
1026 ULONG STDMETHODCALLTYPE AddRef();
1027 ULONG STDMETHODCALLTYPE Release();
1028 COM_METHOD QueryInterface(REFIID riid, void ** ppInterface);
1029
1030 //
1031 // ICorDebugEnum
1032 //
1033
1034 COM_METHOD Skip(ULONG celt);
1035 COM_METHOD Reset();
1036 COM_METHOD Clone(ICorDebugEnum ** ppEnum);
1037 COM_METHOD GetCount(ULONG * pcFrames);
1038
1039 //
1040 // ICorDebugFrameEnum
1041 //
1042
1043 COM_METHOD Next(ULONG cFrames, ICorDebugFrame * rgpFrames[], ULONG * pcFramesFetched);
1044
1045 //
1046 // accessors
1047 //
1048
1049 // used to link ShimChainEnums in a list
1050 ShimFrameEnum * GetNext();
1051 void SetNext(ShimFrameEnum * pNext);
1052
1053private:
1054 ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk
1055 ShimChain * m_pChain; // the owning ShimChain
1056 RSLock * m_pShimLock; // shim lock from ShimProcess to protect neuteredness checks
1057
1058 // This points to the next ShimFrameEnum in the linked list of ShimFrameEnums to be cleaned up.
1059 // The head of the list is on the ShimStackWalk (m_pStackWalk->m_pFrameEnumList).
1060 ShimFrameEnum * m_pNext;
1061
1062 UINT32 m_currentFrameIndex; // the current ICDFrame being enumerated
1063 UINT32 m_endFrameIndex; // the last index (exclusive) of the frame owned by the chain;
1064 // see code:ShimChain::GetLastFrameIndex
1065 Volatile<ULONG> m_refCount;
1066 BOOL m_fIsNeutered;
1067};
1068
1069
1070#endif // SHIMPRIV_H
1071
1072