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// File: ShimProcess.cpp
6//
7
8//
9// The V3 ICD debugging APIs have a lower abstraction level than V2.
10// This provides V2 ICD debugging functionality on top of the V3 debugger object.
11//*****************************************************************************
12
13#include "stdafx.h"
14
15#include "safewrap.h"
16#include "check.h"
17
18#include <limits.h>
19#include "shimpriv.h"
20
21#if !defined(FEATURE_CORESYSTEM)
22#include <tlhelp32.h>
23#endif
24
25//---------------------------------------------------------------------------------------
26//
27// Ctor for a ShimProcess
28//
29// Notes:
30// See InitializeDataTarget in header for details of how to instantiate a ShimProcess and hook it up.
31// Initial ref count is 0. This is the convention used int the RS, and it plays well with semantics
32// like immediately assigning to a smart pointer (which will bump the count up to 1).
33
34ShimProcess::ShimProcess() :
35 m_ref(0),
36 m_fFirstManagedEvent(false),
37 m_fInCreateProcess(false),
38 m_fInLoadModule(false),
39 m_fIsInteropDebugging(false),
40 m_fIsDisposed(false),
41 m_loaderBPReceived(false)
42{
43 m_ShimLock.Init("ShimLock", RSLock::cLockReentrant, RSLock::LL_SHIM_LOCK);
44 m_ShimProcessDisposeLock.Init(
45 "ShimProcessDisposeLock",
46 RSLock::cLockReentrant | RSLock::cLockNonDbgApi,
47 RSLock::LL_SHIM_PROCESS_DISPOSE_LOCK);
48 m_eventQueue.Init(&m_ShimLock);
49 m_pShimCallback.Assign(new ShimProxyCallback(this)); // Throws
50
51 m_fNeedFakeAttachEvents = false;
52 m_ContinueStatusChangedData.Clear();
53
54 m_pShimStackWalkHashTable = new ShimStackWalkHashTable();
55
56 m_pDupeEventsHashTable = new DuplicateCreationEventsHashTable();
57
58 m_machineInfo.Clear();
59
60 m_markAttachPendingEvent = WszCreateEvent(NULL, TRUE, FALSE, NULL);
61 if (m_markAttachPendingEvent == NULL)
62 {
63 ThrowLastError();
64 }
65
66 m_terminatingEvent = WszCreateEvent(NULL, TRUE, FALSE, NULL);
67 if (m_terminatingEvent == NULL)
68 {
69 ThrowLastError();
70 }
71}
72
73//---------------------------------------------------------------------------------------
74//
75// ShimProcess dtor. Invoked when reference count goes to 0.
76//
77// Assumptions:
78// Dtors should not do any interesting work. If this object has been initialized,
79// then call Dispose() first.
80//
81//
82ShimProcess::~ShimProcess()
83{
84 // Expected that this was either already disposed first, or not initialized.
85 _ASSERTE(m_pWin32EventThread == NULL);
86
87 _ASSERTE(m_ShimProcessDisposeLock.IsInit());
88 m_ShimProcessDisposeLock.Destroy();
89
90 if (m_markAttachPendingEvent != NULL)
91 {
92 CloseHandle(m_markAttachPendingEvent);
93 m_markAttachPendingEvent = NULL;
94 }
95
96 if (m_terminatingEvent != NULL)
97 {
98 CloseHandle(m_terminatingEvent);
99 m_terminatingEvent = NULL;
100 }
101
102 // Dtor will release m_pLiveDataTarget
103}
104
105//---------------------------------------------------------------------------------------
106//
107// Part of initialization to hook up to process.
108//
109// Arguments:
110// pProcess - debuggee object to connect to. Maybe null if part of shutdown.
111//
112// Notes:
113// This will take a strong reference to the process object.
114// This is part of the initialization phase.
115// This should only be called once.
116//
117//
118void ShimProcess::SetProcess(ICorDebugProcess * pProcess)
119{
120 PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(NULL);
121
122 // Data-target should already be setup before we try to connect to a process.
123 _ASSERTE(m_pLiveDataTarget != NULL);
124
125 // Reference is kept by m_pProcess;
126 m_pIProcess.Assign(pProcess);
127
128 // Get the private shim hooks. This just exists to access private functionality that has not
129 // yet been promoted to the ICorDebug interfaces.
130 m_pProcess = static_cast<CordbProcess *>(pProcess);
131
132 if (pProcess != NULL)
133 {
134 // Verify that DataTarget + new process have the same pid?
135 _ASSERTE(m_pProcess->GetProcessDescriptor()->m_Pid == m_pLiveDataTarget->GetPid());
136 }
137}
138
139//---------------------------------------------------------------------------------------
140//
141// Create a Data-Target around the live process.
142//
143// Arguments:
144// processId - OS process ID to connect to. Must be a local, same platform, process.
145//
146// Return Value:
147// S_OK on success.
148//
149// Assumptions:
150// This is part of the initialization dance.
151//
152// Notes:
153// Only call this once, during the initialization dance.
154//
155HRESULT ShimProcess::InitializeDataTarget(const ProcessDescriptor * pProcessDescriptor)
156{
157 _ASSERTE(m_pLiveDataTarget == NULL);
158
159
160 HRESULT hr = BuildPlatformSpecificDataTarget(GetMachineInfo(), pProcessDescriptor, &m_pLiveDataTarget);
161 if (FAILED(hr))
162 {
163 _ASSERTE(m_pLiveDataTarget == NULL);
164 return hr;
165 }
166 m_pLiveDataTarget->HookContinueStatusChanged(ShimProcess::ContinueStatusChanged, this);
167
168 // Ref on pDataTarget is now 1.
169 _ASSERTE(m_pLiveDataTarget != NULL);
170
171 return S_OK;
172}
173
174//---------------------------------------------------------------------------------------
175//
176// Determines if current thread is the Win32 Event Thread
177//
178// Return Value:
179// True iff current thread is win32 event thread, else false.
180//
181// Notes:
182// The win32 event thread is created by code:ShimProcess::CreateAndStartWin32ET
183//
184bool ShimProcess::IsWin32EventThread()
185{
186 return (m_pWin32EventThread != NULL) && m_pWin32EventThread->IsWin32EventThread();
187}
188
189//---------------------------------------------------------------------------------------
190//
191// Add a reference
192//
193void ShimProcess::AddRef()
194{
195 InterlockedIncrement(&m_ref);
196}
197
198//---------------------------------------------------------------------------------------
199//
200// Release a reference.
201//
202// Notes:
203// When ref goes to 0, object is deleted.
204//
205void ShimProcess::Release()
206{
207 LONG ref = InterlockedDecrement(&m_ref);
208 if (ref == 0)
209 {
210 delete this;
211 }
212}
213
214//---------------------------------------------------------------------------------------
215//
216// Dispose (Neuter) the object.
217//
218//
219// Assumptions:
220// This is called to gracefully shutdown the ShimProcess object.
221// This must be called before destruction if the object was initialized.
222//
223// Notes:
224// This will release all external resources, including getting the win32 event thread to exit.
225// This can safely be called multiple times.
226//
227void ShimProcess::Dispose()
228{
229 // Serialize Dispose with any other locked access to the shim. This helps
230 // protect against the debugger detaching while we're in the middle of
231 // doing stuff on the ShimProcess
232 RSLockHolder lockHolder(&m_ShimProcessDisposeLock);
233
234 m_fIsDisposed = true;
235
236 // Can't shut down the W32ET if we're on it.
237 _ASSERTE(!IsWin32EventThread());
238
239 m_eventQueue.DeleteAll();
240
241 if (m_pWin32EventThread != NULL)
242 {
243 // This will block waiting for the thread to exit gracefully.
244 m_pWin32EventThread->Stop();
245
246 delete m_pWin32EventThread;
247 m_pWin32EventThread = NULL;
248 }
249
250 if (m_pLiveDataTarget != NULL)
251 {
252 m_pLiveDataTarget->Dispose();
253 m_pLiveDataTarget.Clear();
254 }
255
256 m_pIProcess.Clear();
257 m_pProcess = NULL;
258
259 _ASSERTE(m_ShimLock.IsInit());
260 m_ShimLock.Destroy();
261
262 if (m_pShimStackWalkHashTable != NULL)
263 {
264 // The hash table should be empty by now. ClearAllShimStackWalk() should have been called.
265 _ASSERTE(m_pShimStackWalkHashTable->GetCount() == 0);
266
267 delete m_pShimStackWalkHashTable;
268 m_pShimStackWalkHashTable = NULL;
269 }
270
271 if (m_pDupeEventsHashTable != NULL)
272 {
273 if (m_pDupeEventsHashTable->GetCount() > 0)
274 {
275 // loop through all the entries in the hash table, remove them, and delete them
276 for (DuplicateCreationEventsHashTable::Iterator pCurElem = m_pDupeEventsHashTable->Begin(),
277 pEndElem = m_pDupeEventsHashTable->End();
278 pCurElem != pEndElem;
279 pCurElem++)
280 {
281 DuplicateCreationEventEntry * pEntry = *pCurElem;
282 delete pEntry;
283 }
284 m_pDupeEventsHashTable->RemoveAll();
285 }
286
287 delete m_pDupeEventsHashTable;
288 m_pDupeEventsHashTable = NULL;
289 }
290}
291
292
293
294//---------------------------------------------------------------------------------------
295// Track (and close) file handles from debug events.
296//
297// Arguments:
298// pEvent - debug event
299//
300// Notes:
301// Some debug events introduce file handles that the debugger needs to track and
302// close on other debug events. For example, the LoadDll,CreateProcess debug
303// events both give back a file handle that the debugger must close. This is generally
304// done on the corresponding UnloadDll/ExitProcess debug events.
305//
306// Since we won't use the file handles, we'll just close them as soon as we get them.
307// That way, we don't need to remember any state.
308void ShimProcess::TrackFileHandleForDebugEvent(const DEBUG_EVENT * pEvent)
309{
310 CONTRACTL
311 {
312 THROWS;
313 }
314 CONTRACTL_END;
315
316 HANDLE hFile = NULL;
317
318 switch(pEvent->dwDebugEventCode)
319 {
320 //
321 // Events that add a file handle
322 //
323 case CREATE_PROCESS_DEBUG_EVENT:
324 hFile = pEvent->u.CreateProcessInfo.hFile;
325 CloseHandle(hFile);
326 break;
327
328 case LOAD_DLL_DEBUG_EVENT:
329 hFile = pEvent->u.LoadDll.hFile;
330 CloseHandle(hFile);
331 break;
332
333 }
334}
335
336//---------------------------------------------------------------------------------------
337// ThreadProc helper to drain event queue.
338//
339// Arguments:
340// parameter - thread proc parameter, an ICorDebugProcess*
341//
342// Returns
343// 0.
344//
345// Notes:
346// This is useful when the shim queued a fake managed event (such as Control+C)
347// and needs to get the debuggee to synchronize in order to start dispatching events.
348// @dbgtodo sync: this will likely change as we iron out the Synchronization feature crew.
349//
350// We do this in a new thread proc to avoid thread restrictions:
351// Can't call this on win32 event thread because that can't send the IPC event to
352// make the aysnc-break request.
353// Can't call this on the RCET because that can't send an async-break (see SendIPCEvent for details)
354// So we just spin up a new thread to do the work.
355//---------------------------------------------------------------------------------------
356DWORD WINAPI CallStopGoThreadProc(LPVOID parameter)
357{
358 ICorDebugProcess* pProc = reinterpret_cast<ICorDebugProcess *>(parameter);
359
360 // We expect these operations to succeed; but if they do fail, there's nothing we can really do about it.
361 // If it fails on process exit/neuter/detach, then it would be ignorable.
362 HRESULT hr;
363
364
365 // Calling Stop + Continue will synchronize the process and force any queued events to be called.
366 // Stop is synchronous and will block until debuggee is synchronized.
367 hr = pProc->Stop(INFINITE);
368 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
369
370 // Continue will resume the debuggee. If there are queued events (which we expect in this case)
371 // then continue will drain the event queue instead of actually resuming the process.
372 hr = pProc->Continue(FALSE);
373 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
374
375 // This thread just needs to trigger an event dispatch. Now that it's done that, it can exit.
376 return 0;
377}
378
379
380//---------------------------------------------------------------------------------------
381// Does default event handling for native debug events.
382//
383// Arguments:
384// pEvent - IN event ot handle
385// pdwContinueStatus - IN /OUT - continuation status for event.
386//
387// Assumptions:
388// Called when target is stopped. Caller still needs to Continue the debug event.
389// This is called on the win32 event thread.
390//
391// Notes:
392// Some native events require extra work before continuing. Eg, skip loader
393// breakpoint, close certain handles, etc.
394// This is only called in the manage-only case. In the interop-case, the
395// debugger will get and handle these native debug events.
396void ShimProcess::DefaultEventHandler(
397 const DEBUG_EVENT * pEvent,
398 DWORD * pdwContinueStatus)
399{
400 CONTRACTL
401 {
402 THROWS;
403 }
404 CONTRACTL_END;
405
406
407 //
408 // Loader breakpoint
409 //
410
411 BOOL fFirstChance;
412 const EXCEPTION_RECORD * pRecord = NULL;
413
414 if (IsExceptionEvent(pEvent, &fFirstChance, &pRecord))
415 {
416 DWORD dwThreadId = GetThreadId(pEvent);
417
418 switch(pRecord->ExceptionCode)
419 {
420 case STATUS_BREAKPOINT:
421 {
422 if (!m_loaderBPReceived)
423 {
424 m_loaderBPReceived = true;
425
426 // Clear the loader breakpoint
427 *pdwContinueStatus = DBG_CONTINUE;
428
429 // After loader-breakpoint, notify that managed attach can begin.
430 // This is done to trigger a synchronization. The shim
431 // can then send the fake attach events once the target
432 // is synced.
433 // @dbgtodo sync: not needed once shim can
434 // work on sync APIs.
435 m_pProcess->QueueManagedAttachIfNeeded(); // throws
436 }
437 }
438 break;
439
440 /*
441 // If we handle the Ctlr-C event here and send the notification to the debugger, then we may break pre-V4
442 // behaviour because the debugger may handle the event and intercept the handlers registered in the debuggee
443 // process. So don't handle the event here and let the debuggee process handle it instead. See Dev10 issue
444 // 846455 for more info.
445 //
446 // However, when the re-arch is completed, we will need to work with VS to define what the right behaviour
447 // should be. We don't want to rely on in-process code to handle the Ctrl-C event.
448 case DBG_CONTROL_C:
449 {
450 // Queue a fake managed Ctrl+C event.
451 m_pShimCallback->ControlCTrap(GetProcess());
452
453 // Request an Async Break
454 // This is on Win32 Event Thread, so we can't call Stop / Continue.
455 // Instead, spawn a new threead, and have that call Stop/Continue, which
456 // will get the RCET to drain the event queue and dispatch the ControlCTrap we just queued.
457 {
458 DWORD dwDummyId;
459 CreateThread(NULL,
460 0,
461 CallStopGoThreadProc,
462 (LPVOID) GetProcess(),
463 0,
464 &dwDummyId);
465 }
466
467 // We don't worry about suspending the Control-C thread right now. The event is
468 // coming asynchronously, and so it's ok if the debuggee slips forward while
469 // we try to do a managed async break.
470
471
472 // Clear the control-C event.
473 *pdwContinueStatus = DBG_CONTINUE;
474 }
475 break;
476
477*/
478 }
479
480
481 }
482
483
484 // Native debugging APIs have an undocumented expectation that you clear for OutputDebugString.
485 if (pEvent->dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
486 {
487 *pdwContinueStatus = DBG_CONTINUE;
488 }
489
490 //
491 // File handles.
492 //
493 TrackFileHandleForDebugEvent(pEvent);
494}
495
496//---------------------------------------------------------------------------------------
497// Determine if we need to change the continue status
498//
499// Returns:
500// True if the continue status was changed. Else false.
501//
502// Assumptions:
503// This is single-threaded, which is enforced by it only be called on the win32et.
504// The shim guarnatees only 1 outstanding debug-event at a time.
505//
506// Notes:
507// See code:ShimProcess::ContinueStatusChangedWorker for big picture.
508// Continue status is changed from a data-target callback which invokes
509// code:ShimProcess::ContinueStatusChangedWorker.
510// Call code:ShimProcess::ContinueStatusChangedData::Clear to clear the 'IsSet' bit.
511//
512bool ShimProcess::ContinueStatusChangedData::IsSet()
513{
514
515 return m_dwThreadId != 0;
516}
517
518//---------------------------------------------------------------------------------------
519// Clears the bit marking
520//
521// Assumptions:
522// This is single-threaded, which is enforced by it only be called on the win32et.
523// The shim guarantees only 1 outstanding debug-event at a time.
524//
525// Notes:
526// See code:ShimProcess::ContinueStatusChangedWorker for big picture.
527// This makes code:ShimProcess::ContinueStatusChangedData::IsSet return false.
528// This can safely be called multiple times in a row.
529//
530void ShimProcess::ContinueStatusChangedData::Clear()
531{
532 m_dwThreadId = 0;
533}
534
535//---------------------------------------------------------------------------------------
536// Callback invoked from data-target when continue status is changed.
537//
538// Arguments:
539// pUserData - data we supplied to the callback. a 'this' pointer.
540// dwThreadId - the tid whose continue status is changing
541// dwContinueStatus - the new continue status.
542//
543// Notes:
544//
545
546// Static
547HRESULT ShimProcess::ContinueStatusChanged(void * pUserData, DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus)
548{
549 ShimProcess * pThis = reinterpret_cast<ShimProcess *>(pUserData);
550 return pThis->ContinueStatusChangedWorker(dwThreadId, dwContinueStatus);
551}
552
553//---------------------------------------------------------------------------------------
554// Real worker callback invoked from data-target when continue status is changed.
555//
556// Arguments:
557// dwThreadId - the tid whose continue status is changing
558// dwContinueStatus - the new continue status.
559//
560// Notes:
561// ICorDebugProcess4::Filter returns an initial continue status (heavily biased to 'gn').
562// Some ICorDebug operations may need to change the continue status that filter returned.
563// For example, on windows, hijacking a thread at an unhandled exception would need to
564// change the status to 'gh' (since continuing 2nd chance exception 'gn' will tear down the
565// process and the hijack would never execute).
566//
567// Such operations will invoke into the data-target (code:ICorDebugMutableDataTarget::ContinueStatusChanged)
568// to notify the debugger that the continue status was changed.
569//
570// The shim only executes such operations on the win32-event thread in a small window between
571// WaitForDebugEvent and Continue. Therefore, we know:
572// * the callback must come on the Win32EventThread (which means our handling the callback is
573// single-threaded.
574// * We only have 1 outstanding debug event to worry about at a time. This simplifies our tracking.
575//
576// The shim tracks the outstanding change request in m_ContinueStatusChangedData.
577
578HRESULT ShimProcess::ContinueStatusChangedWorker(DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus)
579{
580 // Should only be set once. This is only called on the win32 event thread, which protects against races.
581 _ASSERTE(IsWin32EventThread());
582 _ASSERTE(!m_ContinueStatusChangedData.IsSet());
583
584 m_ContinueStatusChangedData.m_dwThreadId = dwThreadId;
585 m_ContinueStatusChangedData.m_status = dwContinueStatus;
586
587 // Setting dwThreadId to non-zero should now mark this as set.
588 _ASSERTE(m_ContinueStatusChangedData.IsSet());
589 return S_OK;
590}
591
592
593//---------------------------------------------------------------------------------------
594//
595// Add a duplicate creation event entry for the specified key.
596//
597// Arguments:
598// pKey - the key of the entry to be added; this is expected to be an
599// ICDProcess/ICDAppDomain/ICDThread/ICDAssembly/ICDModule
600//
601// Assumptions:
602// pKey is really an interface pointer of one of the types mentioned above
603//
604// Notes:
605// We have to keep track of which creation events we have sent already because some runtime data structures
606// are discoverable through enumeration before they send their creation events. As a result, we may have
607// faked up a creation event for a data structure during attach, and then later on get another creation
608// event for the same data structure. VS is not resilient in the face of multiple creation events for
609// the same data structure.
610//
611// Needless to say this is a problem in attach scenarios only. However, keep in mind that for CoreCLR,
612// launch really is early attach. For early attach, we get three creation events up front: a create
613// process, a create appdomain, and a create thread.
614//
615
616void ShimProcess::AddDuplicateCreationEvent(void * pKey)
617{
618 NewHolder<DuplicateCreationEventEntry> pEntry(new DuplicateCreationEventEntry(pKey));
619 m_pDupeEventsHashTable->Add(pEntry);
620 pEntry.SuppressRelease();
621}
622
623
624//---------------------------------------------------------------------------------------
625//
626// Check whether the specified key exists in the hash table. If so, remove it.
627//
628// Arguments:
629// pKey - the key of the entry to check; this is expected to be an
630// ICDProcess/ICDAppDomain/ICDThread/ICDAssembly/ICDModule
631//
632// Return Value:
633// Returns true if the entry exists. The entry will have been removed because we can't have more than two
634// duplicates for any given event.
635//
636// Assumptions:
637// pKey is really an interface pointer of one of the types mentioned above
638//
639// Notes:
640// See code:ShimProcess::AddDuplicateCreationEvent.
641//
642
643bool ShimProcess::RemoveDuplicateCreationEventIfPresent(void * pKey)
644{
645 // We only worry about duplicate events in attach scenarios.
646 if (GetAttached())
647 {
648 // Only do the check if the hash table actually contains entries.
649 if (m_pDupeEventsHashTable->GetCount() > 0)
650 {
651 // Check if this is a dupe.
652 DuplicateCreationEventEntry * pResult = m_pDupeEventsHashTable->Lookup(pKey);
653 if (pResult != NULL)
654 {
655 // This is a dupe. We can't have a dupe twice, so remove it.
656 // This will help as a bit of optimization, since we will no longer check the hash table if
657 // its count reaches 0.
658 m_pDupeEventsHashTable->Remove(pKey);
659 delete pResult;
660 return true;
661 }
662 }
663 }
664 return false;
665}
666
667
668//---------------------------------------------------------------------------------------
669// Gets the exception record format of the host
670//
671// Returns:
672// The CorDebugRecordFormat for the host architecture.
673//
674// Notes:
675// This corresponds to the definition EXCEPTION_RECORD on the host-architecture.
676// It can be passed into ICorDebugProcess4::Filter.
677CorDebugRecordFormat GetHostExceptionRecordFormat()
678{
679#if defined(_WIN64)
680 return FORMAT_WINDOWS_EXCEPTIONRECORD64;
681#else
682 return FORMAT_WINDOWS_EXCEPTIONRECORD32;
683#endif
684}
685
686//---------------------------------------------------------------------------------------
687// Main event handler for native debug events. Must also ensure Continue is called.
688//
689// Arguments:
690// pEvent - debug event to handle
691//
692// Assumptions:
693// Caller did a Flush() if needed.
694//
695// Notes:
696// The main Handle native debug events.
697// This must call back into ICD to let ICD filter the debug event (in case it's a managed notification).
698//
699// If we're interop-debugging (V2), then the debugger is expecting the debug events. In that case,
700// we go through the V2 interop-debugging logic to queue / dispatch the events.
701// If we're managed-only debugging, then the shim provides a default handler for the native debug.
702// This includes some basic work (skipping the loader breakpoint, close certain handles, etc).
703//---------------------------------------------------------------------------------------
704HRESULT ShimProcess::HandleWin32DebugEvent(const DEBUG_EVENT * pEvent)
705{
706 _ASSERTE(IsWin32EventThread());
707
708 //
709 // If this is an exception event, then we need to feed it into the CLR.
710 //
711 BOOL dwFirstChance = FALSE;
712 const EXCEPTION_RECORD * pRecord = NULL;
713 const DWORD dwThreadId = GetThreadId(pEvent);
714
715 bool fContinueNow = true;
716
717 // If true, we're continuing (unhandled) a 2nd-chance exception
718 bool fExceptionGoingUnhandled = false;
719
720 //
721 const DWORD kDONTCARE = 0;
722 DWORD dwContinueStatus = kDONTCARE;
723
724 if (IsExceptionEvent(pEvent, &dwFirstChance, &pRecord))
725 {
726 // As a diagnostic aid we can configure the debugger to assert when the debuggee does DebugBreak()
727#ifdef DEBUG
728 static ConfigDWORD config;
729 DWORD fAssert = config.val(CLRConfig::INTERNAL_DbgAssertOnDebuggeeDebugBreak);
730 if (fAssert)
731 {
732 // If we got a 2nd-chance breakpoint, then it's extremely likely that it's from an
733 // _ASSERTE in the target and we really want to know about it now before we kill the
734 // target. The debuggee will exit once we continue (unless we are mixed-mode debugging), so alert now.
735 // This assert could be our only warning of various catastrophic failures in the left-side.
736 if (!dwFirstChance && (pRecord->ExceptionCode == STATUS_BREAKPOINT) && !m_fIsInteropDebugging)
737 {
738 DWORD pid = (m_pLiveDataTarget == NULL) ? 0 : m_pLiveDataTarget->GetPid();
739
740 CONSISTENCY_CHECK_MSGF(false,
741 ("Unhandled breakpoint exception in debuggee (pid=%d (0x%x)) on thread %d(0x%x)\n"
742 "This may mean there was an assert in the debuggee on that thread.\n"
743 "\n"
744 "You should attach to that process (non-invasively) and get a callstack of that thread.\n"
745 "(This assert only occurs when CLRConfig::INTERNAL_DebuggerAssertOnDebuggeeDebugBreak is set)\n",
746 pid, pid, dwThreadId,dwThreadId));
747 }
748 }
749#endif
750
751 // We pass the Shim's proxy callback object, which will just take the callbacks and queue them
752 // to an event-queue in the shim. When we get the sync-complete event, the shim
753 // will then drain the event queue and dispatch the events to the user's callback object.
754 const DWORD dwFlags = dwFirstChance ? 1 : 0;
755
756 m_ContinueStatusChangedData.Clear();
757
758 // If ICorDebug doesn't care about this exception, it will leave dwContinueStatus unchanged.
759 RSExtSmartPtr<ICorDebugProcess4> pProcess4;
760 GetProcess()->QueryInterface(IID_ICorDebugProcess4, (void**) &pProcess4);
761
762 HRESULT hrFilter = pProcess4->Filter(
763 (const BYTE*) pRecord,
764 sizeof(EXCEPTION_RECORD),
765 GetHostExceptionRecordFormat(),
766 dwFlags,
767 dwThreadId,
768 m_pShimCallback,
769 &dwContinueStatus);
770 if (FAILED(hrFilter))
771 {
772 // Filter failed (eg. DAC couldn't be loaded), return the
773 // error so it can become an unrecoverable error.
774 return hrFilter;
775 }
776
777 // For unhandled exceptions, hijacking if needed.
778 if (!dwFirstChance)
779 {
780 // May invoke data-target callback (which may call code:ShimProcess::ContinueStatusChanged) to change continue status.
781 if (!m_pProcess->HijackThreadForUnhandledExceptionIfNeeded(dwThreadId))
782 {
783 // We decided not to hijack, so this exception is going to go unhandled
784 fExceptionGoingUnhandled = true;
785 }
786
787 if (m_ContinueStatusChangedData.IsSet())
788 {
789 _ASSERTE(m_ContinueStatusChangedData.m_dwThreadId == dwThreadId);
790
791 // Claiming this now means we won't do any other processing on the exception event.
792 // This means the interop-debugging logic will never see 2nd-chance managed exceptions.
793 dwContinueStatus = m_ContinueStatusChangedData.m_status;
794 }
795 }
796 }
797
798 // Do standard event handling, including Handling loader-breakpoint,
799 // and callback into CordbProcess for Attach if needed.
800 HRESULT hrIgnore = S_OK;
801 EX_TRY
802 {
803 // For NonClr notifications, allow extra processing.
804 // This includes both non-exception events, and exception events that aren't
805 // specific CLR debugging services notifications.
806 if (dwContinueStatus == kDONTCARE)
807 {
808 if (m_fIsInteropDebugging)
809 {
810 // Interop-debugging logic will handle the continue.
811 fContinueNow = false;
812#if defined(FEATURE_INTEROP_DEBUGGING)
813 // @dbgtodo interop: All the interop-debugging logic is still in CordbProcess.
814 // Call back into that. This will handle Continuing the debug event.
815 m_pProcess->HandleDebugEventForInteropDebugging(pEvent);
816#else
817 _ASSERTE(!"Interop debugging not supported");
818#endif
819 }
820 else
821 {
822 dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
823
824 // For managed-only debugging, there's no user handler for native debug events,
825 // and so we still need to do some basic work on certain debug events.
826 DefaultEventHandler(pEvent, &dwContinueStatus);
827
828 // This is the managed-only case. No reason to keep the target win32 frozen, so continue it immediately.
829 _ASSERTE(fContinueNow);
830 }
831 }
832 }
833 EX_CATCH_HRESULT(hrIgnore);
834 // Dont' expect errors here (but could probably return it up to become an
835 // unrecoverable error if necessary). We still want to call Continue thought.
836 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hrIgnore);
837
838 //
839 // Continue the debuggee if needed.
840 //
841 if (fContinueNow)
842 {
843 BOOL fContinueOk = GetNativePipeline()->ContinueDebugEvent(
844 GetProcessId(pEvent),
845 dwThreadId,
846 dwContinueStatus);
847 (void)fContinueOk; //prevent "unused variable" error from GCC
848 SIMPLIFYING_ASSUMPTION(fContinueOk);
849
850 if (fExceptionGoingUnhandled)
851 {
852 _ASSERTE(dwContinueStatus == DBG_EXCEPTION_NOT_HANDLED);
853 // We just passed a 2nd-chance exception back to the OS which may have now invoked
854 // Windows error-reporting logic which suspended all threads in the target. Since we're
855 // still debugging and may want to break, inspect state and even detach (eg. to attach
856 // a different sort of debugger that can handle the exception) we need to let our threads run.
857 // Note that when WER auto-invokes a debugger it doesn't suspend threads, so it doesn't really
858 // make sense for them to be suspended now when a debugger is already attached.
859 // A better solution may be to suspend this faulting thread before continuing the event, do an
860 // async-break and give the debugger a notification of an unhandled exception. But this will require
861 // an ICorDebug API change, and also makes it harder to reliably get the WER dialog box once we're
862 // ready for it.
863 // Unfortunately we have to wait for WerFault.exe to start and actually suspend the threads, and
864 // there doesn't appear to be any better way than to just sleep for a little here. In practice 200ms
865 // seems like more than enough, but this is so uncommon of a scenario that a half-second delay
866 // (just to be safe) isn't a problem.
867 // Provide an undocumented knob to turn this behavior off in the very rare case it's not what we want
868 // (eg. we're trying to debug something that races with crashing / terminating the process on multiple
869 // threads)
870 static ConfigDWORD config;
871 DWORD fSkipResume = config.val(CLRConfig::UNSUPPORTED_DbgDontResumeThreadsOnUnhandledException);
872 if (!fSkipResume)
873 {
874 ::Sleep(500);
875 hrIgnore = GetNativePipeline()->EnsureThreadsRunning();
876 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hrIgnore);
877 }
878 }
879 }
880
881 return S_OK;
882}
883
884// Trivial accessor to get the event queue.
885ManagedEventQueue * ShimProcess::GetManagedEventQueue()
886{
887 return &m_eventQueue;
888}
889
890// Combines GetManagedEventQueue() and Dequeue() into a single function
891// that holds m_ShimProcessDisposeLock for the duration
892ManagedEvent * ShimProcess::DequeueManagedEvent()
893{
894 // Serialize this function with Dispoe()
895 RSLockHolder lockHolder(&m_ShimProcessDisposeLock);
896 if (m_fIsDisposed)
897 return NULL;
898
899 return m_eventQueue.Dequeue();
900}
901
902// Trivial accessor to get Shim's proxy callback object.
903ShimProxyCallback * ShimProcess::GetShimCallback()
904{
905 return m_pShimCallback;
906}
907
908// Trivial accessor to get the ICDProcess for the debuggee.
909// A ShimProcess object can then provide V2 functionality by building it on V3 functionality
910// exposed by the ICDProcess object.
911ICorDebugProcess * ShimProcess::GetProcess()
912{
913 return m_pIProcess;
914}
915
916// Trivial accessor to get the data-target for the debuggee.
917// The data-target lets us access the debuggee, especially reading debuggee memory.
918ICorDebugMutableDataTarget * ShimProcess::GetDataTarget()
919{
920 return m_pLiveDataTarget;
921};
922
923
924// Trivial accessor to get the raw native event pipeline.
925// In V3, ICorDebug no longer owns the event thread and it does not own the event pipeline either.
926INativeEventPipeline * ShimProcess::GetNativePipeline()
927{
928 return m_pWin32EventThread->GetNativePipeline();
929}
930
931// Trivial accessor to expose the W32ET thread to the CordbProcess so that it can emulate V2 behavior.
932// In V3, ICorDebug no longer owns the event thread and it does not own the event pipeline either.
933// The Win32 Event Thread is the only thread that can use the native pipeline
934// see code:ShimProcess::GetNativePipeline.
935CordbWin32EventThread * ShimProcess::GetWin32EventThread()
936{
937 return m_pWin32EventThread;
938}
939
940
941// Trivial accessor to mark whether we're interop-debugging.
942// Retreived via code:ShimProcess::IsInteropDebugging
943void ShimProcess::SetIsInteropDebugging(bool fIsInteropDebugging)
944{
945 m_fIsInteropDebugging = fIsInteropDebugging;
946}
947
948// Trivial accessor to check if we're interop-debugging.
949// This affects how we handle native debug events.
950// The significant usage of this is in code:ShimProcess::HandleWin32DebugEvent
951bool ShimProcess::IsInteropDebugging()
952{
953 return m_fIsInteropDebugging;
954}
955
956
957//---------------------------------------------------------------------------------------
958// Begin queueing the fake attach events.
959//
960// Notes:
961// See code:ShimProcess::QueueFakeAttachEvents for more about "fake attach events".
962//
963// This marks that we need to send fake attach events, and queus a CreateProcess.
964// Caller calls code:ShimProcess::QueueFakeAttachEventsIfNeeded to finish queuing
965// the rest of the fake attach events.
966void ShimProcess::BeginQueueFakeAttachEvents()
967{
968 m_fNeedFakeAttachEvents = true;
969
970 // Put a fake CreateProcess event in the queue.
971 // This will not be drained until we get a Sync-Complete from the Left-side.
972 GetShimCallback()->QueueCreateProcess(GetProcess());
973 AddDuplicateCreationEvent(GetProcess());
974}
975
976//---------------------------------------------------------------------------------------
977// potentially Queue fake attach events like we did in V2.
978//
979// Arguments:
980// fRealCreateProcessEvent - true if the shim is about to dispatch a real create process event (as opposed
981// to one faked up by the shim itself)
982//
983// Notes:
984// See code:ShimProcess::QueueFakeAttachEvents for details.
985void ShimProcess::QueueFakeAttachEventsIfNeeded(bool fRealCreateProcessEvent)
986{
987 // This was set high in code:ShimProcess::BeginQueueFakeAttachEvents
988 if (!m_fNeedFakeAttachEvents)
989 {
990 return;
991 }
992 m_fNeedFakeAttachEvents = false;
993
994 // If the first event we get after attaching is a create process event, then this is an early attach
995 // scenario and we don't need to queue any fake attach events.
996 if (!fRealCreateProcessEvent)
997 {
998 HRESULT hr = S_OK;
999 EX_TRY
1000 {
1001 QueueFakeAttachEvents();
1002 }
1003 EX_CATCH_HRESULT(hr);
1004 }
1005}
1006
1007//---------------------------------------------------------------------------------------
1008// Send fake Thread-create events for attach, using an arbitrary order.
1009//
1010// Returns:
1011// S_OK on success, else error.
1012//
1013// Notes:
1014// This sends fake thread-create events, ala V2 attach.
1015// See code:ShimProcess::QueueFakeAttachEvents for details
1016//
1017// The order of thread creates is random and at the mercy of ICorDebugProcess::EnumerateThreads.
1018// Whidbey would send thread creates in the order of the OS's native thread
1019// list. Since Arrowhead no longer sends fake attach events, the shim simulates
1020// the fake attach events. But ICorDebug doesn't provide a way to get the
1021// same order that V2 used. So without using platform-specific thread-enumeration,
1022// we can't get the V2 ordering.
1023//
1024// Compare to code:ShimProcess::QueueFakeThreadAttachEventsNativeOrder,
1025// which sends threads in the OS native thread create order.
1026//
1027HRESULT ShimProcess::QueueFakeThreadAttachEventsNoOrder()
1028{
1029 ICorDebugProcess * pProcess = GetProcess();
1030
1031 RSExtSmartPtr<ICorDebugThreadEnum> pThreadEnum;
1032 RSExtSmartPtr<ICorDebugThread> pThread;
1033
1034 // V2 would only send create threads after a thread had run managed code.
1035 // V3 has a discovery model where Enumeration can find threads before they've run managed code.
1036 // So the emulation here may send some additional create-thread events that v2 didn't send.
1037 HRESULT hr = pProcess->EnumerateThreads(&pThreadEnum);
1038 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1039 if (FAILED(hr))
1040 {
1041 return hr;
1042 }
1043
1044 ULONG cDummy;
1045
1046 while(SUCCEEDED(pThreadEnum->Next(1, &pThread, &cDummy)) && (pThread != NULL))
1047 {
1048 RSExtSmartPtr<ICorDebugAppDomain> pAppDomain;
1049 hr = pThread->GetAppDomain(&pAppDomain);
1050
1051 // Getting the appdomain shouldn't fail. If it does, we can't dispatch
1052 // this callback, but we can still dispatch the other thread creates.
1053 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1054 if (pAppDomain != NULL)
1055 {
1056 GetShimCallback()->CreateThread(pAppDomain, pThread);
1057 AddDuplicateCreationEvent(pThread);
1058 }
1059 pThread.Clear();
1060 }
1061
1062 return S_OK;
1063}
1064
1065//---------------------------------------------------------------------------------------
1066// Send fake Thread-create events for attach, using the order of the OS native
1067// thread list.
1068//
1069// Returns:
1070// S_OK on success, else error.
1071//
1072// Notes:
1073// This sends fake thread-create events, ala V2 attach.
1074// See code:ShimProcess::QueueFakeAttachEvents for details
1075// The order of the thread creates matches the OS's native thread list.
1076// This is important because the debugger can use the order of thread-create
1077// callbacks to associate logical thread-ids (0,1,2...) with threads. Users
1078// may rely on thread 0 always being the main thread.
1079// In contrast, the order from ICorDebugProcess::EnumerateThreads is random.
1080//
1081// Compare to code:ShimProcess::QueueFakeThreadAttachEventsNoOrder, which
1082// sends the threads in an arbitrary order.
1083HRESULT ShimProcess::QueueFakeThreadAttachEventsNativeOrder()
1084{
1085#ifdef FEATURE_CORESYSTEM
1086 _ASSERTE("NYI");
1087 return E_FAIL;
1088#else
1089 ICorDebugProcess * pProcess = GetProcess();
1090
1091 DWORD dwProcessId;
1092 HRESULT hr = pProcess->GetID(&dwProcessId);
1093 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1094 if (FAILED(hr))
1095 {
1096 return hr;
1097 }
1098
1099 HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
1100 THREADENTRY32 te32;
1101
1102 // Take a snapshot of all running threads
1103 hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
1104 if (hThreadSnap == INVALID_HANDLE_VALUE)
1105 {
1106 hr = HRESULT_FROM_GetLastError();
1107 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1108 return hr;
1109 }
1110 // HandleHolder doesn't deal with INVALID_HANDLE_VALUE, so we only assign if we have a legal value.
1111 HandleHolder hSnapshotHolder(hThreadSnap);
1112
1113 // Fill in the size of the structure before using it.
1114 te32.dwSize = sizeof(THREADENTRY32);
1115
1116 // Retrieve information about the first thread, and exit if unsuccessful
1117 if (!Thread32First(hThreadSnap, &te32))
1118 {
1119 hr = HRESULT_FROM_GetLastError();
1120 return hr;
1121 }
1122
1123 // Now walk the thread list of the system,
1124 // and display information about each thread
1125 // associated with the specified process
1126 do
1127 {
1128 if (te32.th32OwnerProcessID == dwProcessId)
1129 {
1130 RSExtSmartPtr<ICorDebugThread> pThread;
1131 pProcess->GetThread(te32.th32ThreadID, &pThread);
1132 if (pThread != NULL)
1133 {
1134 // If we fail to get the appdomain for some reason, then then
1135 // we can't dispatch this thread callback. But we can still
1136 // finish enumerating.
1137 RSExtSmartPtr<ICorDebugAppDomain> pAppDomain;
1138 HRESULT hrGetAppDomain = pThread->GetAppDomain(&pAppDomain);
1139 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hrGetAppDomain);
1140 if (pAppDomain != NULL)
1141 {
1142 GetShimCallback()->CreateThread(pAppDomain, pThread);
1143 AddDuplicateCreationEvent(pThread);
1144
1145 //fix for issue DevDiv2\DevDiv 77523 - threads are switched out in SQL don't get thread create notifications
1146 // mark that this thread has queued a create event
1147 CordbThread* pThreadInternal = static_cast<CordbThread*>(pThread.GetValue());
1148 pThreadInternal->SetCreateEventQueued();
1149 }
1150 }
1151 }
1152 } while(Thread32Next(hThreadSnap, &te32));
1153
1154
1155 //fix for issue DevDiv2\DevDiv 77523 - threads are switched out in SQL don't get thread create notifications
1156 //
1157
1158
1159 // Threads which were switched out won't be present in the native thread order enumeration above.
1160 // In order to not miss them we will enumerate all the managed thread objects and for any that we haven't
1161 // already queued a notification for, we will queue a notification now.
1162 RSExtSmartPtr<ICorDebugThreadEnum> pThreadEnum;
1163 RSExtSmartPtr<ICorDebugThread> pThread;
1164 hr = pProcess->EnumerateThreads(&pThreadEnum);
1165 if (FAILED(hr))
1166 {
1167 return hr;
1168 }
1169
1170 ULONG cDummy;
1171
1172 while(SUCCEEDED(pThreadEnum->Next(1, &pThread, &cDummy)) && (pThread != NULL))
1173 {
1174 RSExtSmartPtr<ICorDebugAppDomain> pAppDomain;
1175 hr = pThread->GetAppDomain(&pAppDomain);
1176 CordbThread* pThreadInternal = static_cast<CordbThread*>(pThread.GetValue());
1177
1178 // Getting the appdomain shouldn't fail. If it does, we can't dispatch
1179 // this callback, but we can still dispatch the other thread creates.
1180 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1181 if (pAppDomain != NULL && !pThreadInternal->CreateEventWasQueued())
1182 {
1183 GetShimCallback()->CreateThread(pAppDomain, pThread);
1184 AddDuplicateCreationEvent(pThread);
1185 pThreadInternal->SetCreateEventQueued();
1186 }
1187 pThread.Clear();
1188 }
1189
1190
1191 return S_OK;
1192#endif
1193}
1194
1195//---------------------------------------------------------------------------------------
1196// Queues the fake Assembly and Module load events
1197//
1198// Arguments:
1199// pAssembly - non-null, the assembly to queue.
1200//
1201// Notes:
1202// Helper for code:ShimProcess::QueueFakeAttachEvents
1203// Queues create events for the assembly and for all modules within the
1204// assembly. Most assemblies only have 1 module.
1205void ShimProcess::QueueFakeAssemblyAndModuleEvent(ICorDebugAssembly * pAssembly)
1206{
1207 RSExtSmartPtr<ICorDebugAppDomain> pAppDomain;
1208
1209 HRESULT hr = pAssembly->GetAppDomain(&pAppDomain);
1210 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1211
1212 //
1213 // Send the fake Load Assembly event.
1214 //
1215 GetShimCallback()->LoadAssembly(pAppDomain, pAssembly);
1216 AddDuplicateCreationEvent(pAssembly);
1217
1218 //
1219 // Send Modules - must be in load order
1220 //
1221 RSExtSmartPtr<ICorDebugModuleEnum> pModuleEnum;
1222 hr = pAssembly->EnumerateModules(&pModuleEnum);
1223 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1224
1225 ULONG countModules;
1226 hr = pModuleEnum->GetCount(&countModules);
1227 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1228
1229 // ISSUE WORKAROUND 835869
1230 // The CordbEnumFilter used as the implementation of CordbAssembly::EnumerateModules has
1231 // a ref counting bug in it. It adds one ref to each item when it is constructed and never
1232 // removes that ref. Expected behavior would be that it adds a ref at construction, another on
1233 // every call to next, and releases the construction ref when the enumerator is destroyed. The
1234 // user is expected to release the reference they receive from Next. Thus enumerating exactly
1235 // one time and calling Release() does the correct thing regardless of whether this bug is present
1236 // or not. Note that with the bug the enumerator holds 0 references at the end of this loop,
1237 // however the assembly also holds references so the modules will not be prematurely released.
1238 for(ULONG i = 0; i < countModules; i++)
1239 {
1240 ICorDebugModule* pModule = NULL;
1241 ULONG countFetched = 0;
1242 pModuleEnum->Next(1, &pModule, &countFetched);
1243 _ASSERTE(pModule != NULL);
1244 if(pModule != NULL)
1245 {
1246 pModule->Release();
1247 }
1248 }
1249
1250 RSExtSmartPtr<ICorDebugModule> * pModules = new RSExtSmartPtr<ICorDebugModule> [countModules];
1251 m_pProcess->GetModulesInLoadOrder(pAssembly, pModules, countModules);
1252 for(ULONG iModule = 0; iModule < countModules; iModule++)
1253 {
1254 ICorDebugModule * pModule = pModules[iModule];
1255
1256 GetShimCallback()->FakeLoadModule(pAppDomain, pModule);
1257 AddDuplicateCreationEvent(pModule);
1258
1259 // V2 may send UpdatePdbStreams for certain modules (like dynamic or in-memory modules).
1260 // We don't yet have this support for out-of-proc.
1261 // When the LoadModule event that we just queued is actually dispatched, it will
1262 // send an IPC event in-process that will collect the information and queue the event
1263 // at that time.
1264 // @dbgtodo : I don't think the above is true anymore - clean it up?
1265
1266 RSExtSmartPtr<IStream> pSymbolStream;
1267
1268 // ICorDebug has no public way to request raw symbols. This is by-design because we
1269 // don't want people taking a dependency on a specific format (to give us the ability
1270 // to innovate for the RefEmit case). So we must use a private hook here to get the
1271 // symbol data.
1272 CordbModule * pCordbModule = static_cast<CordbModule *>(pModule);
1273 IDacDbiInterface::SymbolFormat symFormat = IDacDbiInterface::kSymbolFormatNone;
1274 EX_TRY
1275 {
1276 symFormat = pCordbModule->GetInMemorySymbolStream(&pSymbolStream);
1277 }
1278 EX_CATCH_HRESULT(hr);
1279 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr); // Shouldn't be any errors trying to read symbols
1280
1281 // Only pass the raw symbols onto the debugger if they're in PDB format (all that was supported
1282 // in V2). Note that we could have avoided creating a stream for the non-PDB case, but we'd have
1283 // to refactor GetInMemorySymbolStream and the perf impact should be negligable.
1284 if (symFormat == IDacDbiInterface::kSymbolFormatPDB)
1285 {
1286 _ASSERTE(pSymbolStream != NULL); // symFormat should have been kSymbolFormatNone if null stream
1287 GetShimCallback()->UpdateModuleSymbols(pAppDomain, pModule, pSymbolStream);
1288 }
1289
1290 }
1291 delete [] pModules;
1292}
1293
1294//---------------------------------------------------------------------------------------
1295// Get an array of appdomains, sorted by increasing AppDomain ID
1296//
1297// Arguments:
1298// pProcess - process containing the appdomains
1299// ppAppDomains - array that this function will allocate to hold appdomains
1300// pCount - size of ppAppDomains array
1301//
1302// Assumptions:
1303// Caller must delete [] ppAppDomains
1304//
1305// Notes
1306// This is used as part of code:ShimProcess::QueueFakeAttachEvents.
1307// The fake attach events want appdomains in creation order. ICorDebug doesn't provide
1308// this ordering in the enumerators.
1309//
1310// This returns the appdomains sorted in order of increasing AppDomain ID, since that's the best
1311// approximation of creation order that we have.
1312// @dbgtodo - determine if ICD will provide
1313// ordered enumerators
1314//
1315HRESULT GetSortedAppDomains(ICorDebugProcess * pProcess, RSExtSmartPtr<ICorDebugAppDomain> **ppAppDomains, ULONG * pCount)
1316{
1317 _ASSERTE(ppAppDomains != NULL);
1318
1319 HRESULT hr = S_OK;
1320 RSExtSmartPtr<ICorDebugAppDomainEnum> pAppEnum;
1321
1322 //
1323 // Find the size of the array to hold all the appdomains
1324 //
1325 hr = pProcess->EnumerateAppDomains(&pAppEnum);
1326 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1327 ULONG countAppDomains = 0;
1328
1329 hr = pAppEnum->GetCount(&countAppDomains);
1330 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1331
1332 //
1333 // Allocate the array
1334 //
1335 RSExtSmartPtr<ICorDebugAppDomain> * pAppDomains = new RSExtSmartPtr<ICorDebugAppDomain>[countAppDomains];
1336 *ppAppDomains = pAppDomains;
1337 *pCount = countAppDomains;
1338
1339 //
1340 // Load all the appdomains into the array
1341 //
1342 ULONG countDummy;
1343 hr = pAppEnum->Next(countAppDomains, (ICorDebugAppDomain**) pAppDomains, &countDummy);
1344 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1345 SIMPLIFYING_ASSUMPTION(countDummy == countAppDomains);
1346
1347 //
1348 // Now sort them based on appdomain ID.
1349 // We generally expect a very low number of appdomains (usually 1). So a n^2 sort shouldn't be a perf
1350 // problem here.
1351 //
1352 for(ULONG i = 0; i < countAppDomains; i++)
1353 {
1354 ULONG32 id1;
1355 hr = pAppDomains[i]->GetID(&id1);
1356 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1357
1358 for(ULONG j = i + 1; j < countAppDomains; j++)
1359 {
1360 ULONG32 id2;
1361 hr = pAppDomains[j]->GetID(&id2);
1362 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
1363
1364 if (id1 > id2)
1365 {
1366 // swap values
1367 ICorDebugAppDomain * pTemp = pAppDomains[i];
1368 pAppDomains[i].Assign(pAppDomains[j]);
1369 pAppDomains[j].Assign(pTemp);
1370
1371 // update id1 key since it's in the outer-loop.
1372 id1 = id2;
1373 }
1374 }
1375 }
1376
1377
1378
1379 return S_OK;
1380
1381}
1382
1383//---------------------------------------------------------------------------------------
1384// To emulate the V2 attach-handshake, give the shim a chance to inject fake attach events.
1385//
1386// Notes:
1387// Do this before the queue is empty so that HasQueuedCallbacks() doesn't toggle from false to true.
1388// This is called once the process is synchronized, which emulates V2 semantics on attach.
1389// This may be called on the Win32Event Thread from inside of Filter, or on another thread.
1390void ShimProcess::QueueFakeAttachEvents()
1391{
1392 // Serialize this function with Dispose()
1393 RSLockHolder lockHolder(&m_ShimProcessDisposeLock);
1394 if (m_fIsDisposed)
1395 return;
1396
1397 // The fake CreateProcess is already queued. Start queuing the rest of the events.
1398 // The target is stopped (synchronized) this whole time.
1399 // This will use the inspection API to look at the process and queue up the fake
1400 // events that V2 would have sent in a similar situation. All of the callbacks to GetShimCallback()
1401 // just queue up the events. The event queue is then drained as the V2 debugger calls continue.
1402
1403 HRESULT hr = S_OK;
1404 ICorDebugProcess * pProcess = GetProcess();
1405
1406 //
1407 // First, Queue all the Fake AppDomains
1408 //
1409 RSExtSmartPtr<ICorDebugAppDomain> * pAppDomains = NULL;
1410 ULONG countAppDomains = 0;
1411
1412 hr = GetSortedAppDomains(pProcess, &pAppDomains, &countAppDomains);
1413 if (FAILED(hr))
1414 return;
1415
1416 for(ULONG i = 0; i < countAppDomains; i++)
1417 {
1418 // V2 expects that the debugger then attaches to each AppDomain during the Create-appdomain callback.
1419 // This was done to allow for potential per-appdomain debugging. However, only-process
1420 // wide debugging support was allowed in V2. The caller had to attach to all Appdomains.
1421
1422 GetShimCallback()->CreateAppDomain(pProcess, pAppDomains[i]);
1423 AddDuplicateCreationEvent(pAppDomains[i]);
1424 }
1425
1426 // V2 had a break in the callback queue at this point.
1427
1428 //
1429 // Second, queue all Assembly and Modules events.
1430 //
1431
1432 for(ULONG iAppDomain = 0; iAppDomain < countAppDomains; iAppDomain++)
1433 {
1434 ICorDebugAppDomain * pAppDomain = pAppDomains[iAppDomain];
1435 //
1436 // Send Assemblies. Must be in load order.
1437 //
1438
1439 RSExtSmartPtr<ICorDebugAssemblyEnum> pAssemblyEnum;
1440 hr = pAppDomain->EnumerateAssemblies(&pAssemblyEnum);
1441 if (FAILED(hr))
1442 break;
1443
1444 ULONG countAssemblies;
1445 hr = pAssemblyEnum->GetCount(&countAssemblies);
1446 if (FAILED(hr))
1447 break;
1448
1449 RSExtSmartPtr<ICorDebugAssembly> * pAssemblies = new RSExtSmartPtr<ICorDebugAssembly> [countAssemblies];
1450 m_pProcess->GetAssembliesInLoadOrder(pAppDomain, pAssemblies, countAssemblies);
1451 for(ULONG iAssembly = 0; iAssembly < countAssemblies; iAssembly++)
1452 {
1453 QueueFakeAssemblyAndModuleEvent(pAssemblies[iAssembly]);
1454 }
1455 delete [] pAssemblies;
1456
1457 }
1458
1459 delete [] pAppDomains;
1460
1461
1462 // V2 would have a break in the callback queue at this point.
1463
1464 // V2 would send all relevant ClassLoad events now.
1465 //
1466 // That includes class loads for all modules that:
1467 // - are dynamic
1468 // - subscribed to class load events via ICorDebugModule::EnableClassLoadCallbacks.
1469 // We don't provide Class-loads in our emulation because:
1470 // 1. "ClassLoad" doesn't actually mean anything here.
1471 // 2. We have no way of enumerating "loaded" classes in the CLR. We could use the metadata to enumerate
1472 // all classes, but that's offers no value.
1473 // 3. ClassLoad is useful for dynamic modules to notify a debugger that the module changed and
1474 // to update symbols; but the LoadModule/UpdateModule syms already do that.
1475
1476
1477 //
1478 // Third, Queue all Threads
1479 //
1480#if !defined(FEATURE_DBGIPC_TRANSPORT_DI) && !defined(FEATURE_CORESYSTEM)
1481 // Use OS thread enumeration facilities to ensure that the managed thread
1482 // thread order is the same as the corresponding native thread order.
1483 QueueFakeThreadAttachEventsNativeOrder();
1484#else
1485 // Use ICorDebug to enumerate threads. The order of managed threads may
1486 // not match the order the threads were created in.
1487 QueueFakeThreadAttachEventsNoOrder();
1488#endif
1489
1490 // Forth, Queue all Connections.
1491 // Enumerate connections is not exposed through ICorDebug, so we need to go use a private hook on CordbProcess.
1492 m_pProcess->QueueFakeConnectionEvents();
1493
1494 // For V2 jit-attach, the callback queue would also include the jit-attach event (Exception, UserBreak, MDA, etc).
1495 // This was explicitly in the same callback queue so that a debugger would drain it as part of draining the attach
1496 // events.
1497
1498 // In V3, on normal attach, the VM just sends a Sync-complete event.
1499 // On jit-attach, the VM sends the jit-attach event and then the sync-complete.
1500 // The shim just queues the fake attach events at the first event it gets from the left-side.
1501 // In jit-attach, the shim will queue the fake events right before it queues the jit-attach event,
1502 // thus keeping them in the same callback queue as V2 did.
1503
1504}
1505
1506// Accessor for m_attached.
1507bool ShimProcess::GetAttached()
1508{
1509 return m_attached;
1510}
1511// We need to know whether we are in the CreateProcess callback to be able to
1512// return the v2.0 hresults from code:CordbProcess::SetDesiredNGENCompilerFlags
1513// when we are using the shim.
1514//
1515// Expose m_fInCreateProcess
1516bool ShimProcess::GetInCreateProcess()
1517{
1518 return m_fInCreateProcess;
1519}
1520
1521void ShimProcess::SetInCreateProcess(bool value)
1522{
1523 m_fInCreateProcess = value;
1524}
1525
1526// We need to know whether we are in the FakeLoadModule callback to be able to
1527// return the v2.0 hresults from code:CordbModule::SetJITCompilerFlags when
1528// we are using the shim.
1529//
1530// Expose m_fInLoadModule
1531bool ShimProcess::GetInLoadModule()
1532{
1533 return m_fInLoadModule;
1534
1535}
1536
1537void ShimProcess::SetInLoadModule(bool value)
1538{
1539 m_fInLoadModule = value;
1540}
1541
1542// When we get a continue, we need to clear the flags indicating we're still in a callback
1543void ShimProcess::NotifyOnContinue ()
1544{
1545 m_fInCreateProcess = false;
1546 m_fInLoadModule = false;
1547}
1548
1549// The RS calls this function when the stack is about to be changed in any way, e.g. continue, SetIP, etc.
1550void ShimProcess::NotifyOnStackInvalidate()
1551{
1552 ClearAllShimStackWalk();
1553}
1554
1555//---------------------------------------------------------------------------------------
1556//
1557// Filter HResults for ICorDebugProcess2::SetDesiredNGENCompilerFlags to emualte V2 error semantics.
1558// Arguments:
1559// hr - V3 hresult
1560//
1561// Returns:
1562// hresult V2 would have returned in same situation.
1563HRESULT ShimProcess::FilterSetNgenHresult(HRESULT hr)
1564{
1565 if ((hr == CORDBG_E_MUST_BE_IN_CREATE_PROCESS) && !m_fInCreateProcess)
1566 {
1567 return hr;
1568 }
1569 if (m_attached)
1570 {
1571 return CORDBG_E_CANNOT_BE_ON_ATTACH;
1572 }
1573 return hr;
1574}
1575
1576//---------------------------------------------------------------------------------------
1577// Filter HRs for ICorDebugModule::EnableJITDebugging, ICorDebugModule2::SetJITCompilerFlags
1578// to emulate V2 error semantics
1579//
1580// Arguments:
1581// hr - V3 hresult
1582//
1583// Returns:
1584// hresult V2 would have returned in same situation.
1585HRESULT ShimProcess::FilterSetJitFlagsHresult(HRESULT hr)
1586{
1587 if ((hr == CORDBG_E_MUST_BE_IN_LOAD_MODULE) && !m_fInLoadModule)
1588 {
1589 return hr;
1590 }
1591 if (m_attached && (hr == CORDBG_E_MUST_BE_IN_LOAD_MODULE))
1592 {
1593 return CORDBG_E_CANNOT_BE_ON_ATTACH;
1594 }
1595 return hr;
1596}
1597
1598// ----------------------------------------------------------------------------
1599// ShimProcess::LookupOrCreateShimStackWalk
1600//
1601// Description:
1602// Find the ShimStackWalk associated with the specified ICDThread. Create one if it's not found.
1603//
1604// Arguments:
1605// * pThread - the specified thread
1606//
1607// Return Value:
1608// Return the ShimStackWalk associated with the specified thread.
1609//
1610// Notes:
1611// The ShimStackWalks handed back by this function is only valid until the next time the stack is changed
1612// in any way. In other words, the ShimStackWalks are valid until the next time
1613// code:CordbThread::CleanupStack or code:CordbThread::MarkStackFramesDirty is called.
1614//
1615// ShimStackWalk and ICDThread have a 1:1 relationship. Only one ShimStackWalk will be created for any
1616// given ICDThread. So if two threads in the debugger are walking the same thread in the debuggee, they
1617// operate on the same ShimStackWalk. This is ok because ShimStackWalks walk the stack at creation time,
1618// cache all the frames, and become read-only after creation.
1619//
1620// Refer to code:ShimProcess::ClearAllShimStackWalk to see how ShimStackWalks are cleared.
1621//
1622
1623ShimStackWalk * ShimProcess::LookupOrCreateShimStackWalk(ICorDebugThread * pThread)
1624{
1625 ShimStackWalk * pSW = NULL;
1626
1627 {
1628 // do the lookup under the Shim lock
1629 RSLockHolder lockHolder(&m_ShimLock);
1630 pSW = m_pShimStackWalkHashTable->Lookup(pThread);
1631 }
1632
1633 if (pSW == NULL)
1634 {
1635 // create one if it's not found and add it to the hash table
1636 NewHolder<ShimStackWalk> pNewSW(new ShimStackWalk(this, pThread));
1637
1638 {
1639 // Do the lookup again under the Shim lock, and only add the new ShimStackWalk if no other thread
1640 // has beaten us to it.
1641 RSLockHolder lockHolder(&m_ShimLock);
1642 pSW = m_pShimStackWalkHashTable->Lookup(pThread);
1643 if (pSW == NULL)
1644 {
1645 m_pShimStackWalkHashTable->Add(pNewSW);
1646 pSW = pNewSW;
1647
1648 // don't release the memory if all goes well
1649 pNewSW.SuppressRelease();
1650 }
1651 else
1652 {
1653 // The NewHolder will automatically delete the ShimStackWalk when it goes out of scope.
1654 }
1655 }
1656 }
1657
1658 return pSW;
1659}
1660
1661// ----------------------------------------------------------------------------
1662// ShimProcess::ClearAllShimStackWalk
1663//
1664// Description:
1665// Remove and delete all the entries in the hash table of ShimStackWalks.
1666//
1667// Notes:
1668// Refer to code:ShimProcess::LookupOrCreateShimStackWalk to see how ShimStackWalks are created.
1669//
1670
1671void ShimProcess::ClearAllShimStackWalk()
1672{
1673 RSLockHolder lockHolder(&m_ShimLock);
1674
1675 // loop through all the entries in the hash table, remove them, and delete them
1676 for (ShimStackWalkHashTable::Iterator pCurElem = m_pShimStackWalkHashTable->Begin(),
1677 pEndElem = m_pShimStackWalkHashTable->End();
1678 pCurElem != pEndElem;
1679 pCurElem++)
1680 {
1681 ShimStackWalk * pSW = *pCurElem;
1682 m_pShimStackWalkHashTable->Remove(pSW->GetThread());
1683 delete pSW;
1684 }
1685}
1686
1687//---------------------------------------------------------------------------------------
1688// Called before shim dispatches an event.
1689//
1690// Arguments:
1691// fRealCreateProcessEvent - true if the shim is about to dispatch a real create process event (as opposed
1692// to one faked up by the shim itself)
1693// Notes:
1694// This may be called from within Filter, which means we may be on the win32-event-thread.
1695// This is called on all callbacks from the VM.
1696// This gives us a chance to queue fake-attach events. So call it before the Jit-attach
1697// event has been queued.
1698void ShimProcess::PreDispatchEvent(bool fRealCreateProcessEvent /*= false*/)
1699{
1700 CONTRACTL
1701 {
1702 THROWS;
1703 }
1704 CONTRACTL_END;
1705
1706 // For emulating the V2 case, we need to do additional initialization before dispatching the callback to the user.
1707 if (!m_fFirstManagedEvent)
1708 {
1709 // Remember that we're processing the first managed event so that we only call HandleFirstRCEvent() once
1710 m_fFirstManagedEvent = true;
1711
1712 // This can fail with the incompatable version HR. The process has already been terminated if this
1713 // is the case. This will dispatch an Error callback
1714 // If this fails, the process is in an undefined state.
1715 // @dbgtodo ipc-block: this will go away once we get rid
1716 // of the IPC block.
1717 m_pProcess->FinishInitializeIPCChannel(); // throws on error
1718 }
1719
1720 {
1721 // In jit-attach cases, the first event the shim gets is the event that triggered the jit-attach.
1722 // Queue up the fake events now, and then once we return, our caller will queue the jit-attach event.
1723 // In the jit-attach case, this is before a sync-complete has been sent (since the sync doesn't get sent
1724 // until after the jit-attach event is sent).
1725 QueueFakeAttachEventsIfNeeded(fRealCreateProcessEvent);
1726 }
1727
1728 // Always request an sync (emulates V2 behavior). If LS is not sync-ready, it will ignore the request.
1729 m_pProcess->RequestSyncAtEvent();
1730
1731
1732}
1733
1734// ----------------------------------------------------------------------------
1735// ShimProcess::GetCLRInstanceBaseAddress
1736// Finds the base address of [core]clr.dll
1737// Arguments: none
1738// Return value: returns the base address of [core]clr.dll if possible or NULL otherwise
1739//
1740CORDB_ADDRESS ShimProcess::GetCLRInstanceBaseAddress()
1741{
1742 CORDB_ADDRESS baseAddress = CORDB_ADDRESS(NULL);
1743 DWORD dwPid = m_pLiveDataTarget->GetPid();
1744
1745#if defined(FEATURE_CORESYSTEM)
1746 // Debugger attaching to CoreCLR via CoreCLRCreateCordbObject should have already specified CLR module address.
1747 // Code that help to find it now lives in dbgshim.
1748#else
1749 // get a "snapshot" of all modules in the target
1750 HandleHolder hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);
1751 MODULEENTRY32 moduleEntry = { 0 };
1752
1753 if (hSnapshot == INVALID_HANDLE_VALUE)
1754 {
1755 // we haven't got a loaded CLR yet
1756 baseAddress = CORDB_ADDRESS(NULL);
1757 }
1758 else
1759 {
1760 // we need to loop through the modules until we find mscorwks.dll
1761 moduleEntry.dwSize = sizeof(MODULEENTRY32);
1762
1763 if (!Module32First(hSnapshot, &moduleEntry))
1764 {
1765 baseAddress = CORDB_ADDRESS(NULL);
1766 }
1767 else
1768 {
1769
1770 do
1771 {
1772 if (!_wcsicmp(moduleEntry.szModule, MAKEDLLNAME_W(MAIN_CLR_MODULE_NAME_W)))
1773 {
1774 // we found it, so save the base address
1775 baseAddress = PTR_TO_CORDB_ADDRESS(moduleEntry.modBaseAddr);
1776 }
1777 } while (Module32Next(hSnapshot, &moduleEntry));
1778 }
1779 }
1780#endif
1781 return baseAddress;
1782} // ShimProcess::GetCLRInstanceBaseAddress
1783
1784// ----------------------------------------------------------------------------
1785// ShimProcess::FindLoadedCLR
1786//
1787// Description:
1788// Look for any CLR loaded into the process. If found, return the instance ID for it.
1789//
1790// Arguments:
1791// * pClrInstanceId - out parameter for the instance ID of the CLR
1792//
1793// Return Value:
1794// Returns S_OK if a CLR was found, and stores its instance ID in pClrInstanceId.
1795// Otherwise returns an error code.
1796//
1797// Notes:
1798// If there are multiple CLRs loaded in the process, the one chosen for the returned
1799// instance ID is unspecified.
1800//
1801HRESULT ShimProcess::FindLoadedCLR(CORDB_ADDRESS * pClrInstanceId)
1802{
1803 *pClrInstanceId = GetCLRInstanceBaseAddress();
1804
1805 if (*pClrInstanceId == 0)
1806 {
1807 return E_UNEXPECTED;
1808 }
1809
1810 return S_OK;
1811}
1812
1813
1814//---------------------------------------------------------------------------------------
1815//
1816// Locates DAC by finding mscordac{wks|core} next to DBI
1817//
1818// Return Value:
1819// Returns the module handle for DAC
1820// Throws on errors.
1821//
1822
1823HMODULE ShimProcess::GetDacModule()
1824{
1825 HModuleHolder hDacDll;
1826 PathString wszAccessDllPath;
1827
1828#ifdef FEATURE_PAL
1829 if (!PAL_GetPALDirectoryWrapper(wszAccessDllPath))
1830 {
1831 ThrowLastError();
1832 }
1833 PCWSTR eeFlavor = MAKEDLLNAME_W(W("mscordaccore"));
1834#else
1835 //
1836 // Load the access DLL from the same directory as the the current CLR Debugging Services DLL.
1837 //
1838
1839 if (!WszGetModuleFileName(GetModuleInst(), wszAccessDllPath))
1840 {
1841 ThrowLastError();
1842 }
1843
1844 if (!SUCCEEDED(CopySystemDirectory(wszAccessDllPath, wszAccessDllPath)))
1845 {
1846 ThrowHR(E_INVALIDARG);
1847 }
1848
1849 // Dac Dll is named:
1850 // mscordaccore.dll <-- coreclr
1851 // mscordacwks.dll <-- desktop
1852 PCWSTR eeFlavor =
1853 W("mscordaccore.dll");
1854
1855#endif // FEATURE_PAL
1856 wszAccessDllPath.Append(eeFlavor);
1857
1858 hDacDll.Assign(WszLoadLibrary(wszAccessDllPath));
1859 if (!hDacDll)
1860 {
1861 DWORD dwLastError = GetLastError();
1862 if (dwLastError == ERROR_MOD_NOT_FOUND)
1863 {
1864 // Give a more specific error in the case where we can't find the DAC dll.
1865 ThrowHR(CORDBG_E_DEBUG_COMPONENT_MISSING);
1866 }
1867 else
1868 {
1869 ThrowWin32(dwLastError);
1870 }
1871 }
1872 hDacDll.SuppressRelease();
1873 return (HMODULE) hDacDll;
1874}
1875
1876MachineInfo ShimProcess::GetMachineInfo()
1877{
1878 return m_machineInfo;
1879}
1880
1881void ShimProcess::SetMarkAttachPendingEvent()
1882{
1883 SetEvent(m_markAttachPendingEvent);
1884}
1885
1886void ShimProcess::SetTerminatingEvent()
1887{
1888 SetEvent(m_terminatingEvent);
1889}
1890
1891RSLock * ShimProcess::GetShimLock()
1892{
1893 return &m_ShimLock;
1894}
1895
1896
1897bool ShimProcess::IsThreadSuspendedOrHijacked(ICorDebugThread * pThread)
1898{
1899 return m_pProcess->IsThreadSuspendedOrHijacked(pThread);
1900}
1901