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: RCThread.cpp
6//
7
8//
9// Runtime Controller Thread
10//
11//*****************************************************************************
12
13#include "stdafx.h"
14#include "threadsuspend.h"
15#ifndef FEATURE_PAL
16
17#include "securitywrapper.h"
18#endif
19#include <aclapi.h>
20#include <hosting.h>
21
22#include "eemessagebox.h"
23#include "genericstackprobe.h"
24
25#ifndef SM_REMOTESESSION
26#define SM_REMOTESESSION 0x1000
27#endif
28
29#include <limits.h>
30
31#ifdef _DEBUG
32// Declare statics
33EEThreadId DebuggerRCThread::s_DbgHelperThreadId;
34#endif
35
36//
37// Constructor
38//
39DebuggerRCThread::DebuggerRCThread(Debugger * pDebugger)
40 : m_debugger(pDebugger),
41 m_pDCB(NULL),
42 m_thread(NULL),
43 m_run(true),
44 m_threadControlEvent(NULL),
45 m_helperThreadCanGoEvent(NULL),
46 m_fDetachRightSide(false)
47{
48 CONTRACTL
49 {
50 SO_INTOLERANT;
51 WRAPPER(THROWS);
52 GC_NOTRIGGER;
53 CONSTRUCTOR_CHECK;
54 }
55 CONTRACTL_END;
56
57 _ASSERTE(pDebugger != NULL);
58
59 for( int i = 0; i < IPC_TARGET_COUNT;i++)
60 {
61 m_rgfInitRuntimeOffsets[i] = true;
62 }
63
64 // Initialize this here because we Destroy it in the DTOR.
65 // Note that this function can't fail.
66}
67
68
69//
70// Destructor. Cleans up all of the open handles the RC thread uses.
71// This expects that the RC thread has been stopped and has terminated
72// before being called.
73//
74DebuggerRCThread::~DebuggerRCThread()
75{
76 CONTRACTL
77 {
78 SO_INTOLERANT;
79 NOTHROW;
80 GC_NOTRIGGER;
81 DESTRUCTOR_CHECK;
82 }
83 CONTRACTL_END;
84
85 LOG((LF_CORDB,LL_INFO1000, "DebuggerRCThread::~DebuggerRCThread\n"));
86
87 // We explicitly leak the debugger object on shutdown. See Debugger::StopDebugger for details.
88 _ASSERTE(!"RCThread dtor should not be called.");
89}
90
91
92
93//---------------------------------------------------------------------------------------
94//
95// Close the IPC events associated with a debugger connection
96//
97// Notes:
98// The only IPC connection supported is OOP.
99//
100//---------------------------------------------------------------------------------------
101void DebuggerRCThread::CloseIPCHandles()
102{
103 CONTRACTL
104 {
105 SO_NOT_MAINLINE;
106 NOTHROW;
107 GC_NOTRIGGER;
108 }
109 CONTRACTL_END;
110
111 if( m_pDCB != NULL)
112 {
113 m_pDCB->m_rightSideProcessHandle.Close();
114 }
115}
116
117//-----------------------------------------------------------------------------
118// Helper to get the proper decorated name
119// Caller ensures that pBufSize is large enough. We'll assert just to check,
120// but no runtime failure.
121// pBuf - the output buffer to write the decorated name in
122// cBufSizeInChars - the size of the buffer in characters, including the null.
123// pPrefx - The undecorated name of the event.
124//-----------------------------------------------------------------------------
125void GetPidDecoratedName(__out_ecount(cBufSizeInChars) WCHAR * pBuf,
126 int cBufSizeInChars,
127 const WCHAR * pPrefix)
128{
129 LIMITED_METHOD_CONTRACT;
130
131 DWORD pid = GetCurrentProcessId();
132
133 GetPidDecoratedName(pBuf, cBufSizeInChars, pPrefix, pid);
134}
135
136
137
138
139//-----------------------------------------------------------------------------
140// Simple wrapper to create win32 events.
141// This helps make DebuggerRCThread::Init pretty, beccause we
142// create lots of events there.
143// These will either:
144// 1) Create/Open and return an event
145// 2) or throw an exception.
146// @todo - should these be CLREvents? ClrCreateManualEvent / ClrCreateAutoEvent
147//-----------------------------------------------------------------------------
148HANDLE CreateWin32EventOrThrow(
149 LPSECURITY_ATTRIBUTES lpEventAttributes,
150 EEventResetType eType,
151 BOOL bInitialState
152)
153{
154 CONTRACT(HANDLE)
155 {
156 THROWS;
157 GC_NOTRIGGER;
158 PRECONDITION(CheckPointer(lpEventAttributes, NULL_OK));
159 POSTCONDITION(RETVAL != NULL);
160 }
161 CONTRACT_END;
162
163 HANDLE h = NULL;
164 h = WszCreateEvent(lpEventAttributes, (BOOL) eType, bInitialState, NULL);
165
166 if (h == NULL)
167 ThrowLastError();
168
169 RETURN h;
170}
171
172//-----------------------------------------------------------------------------
173// Open an event. Another helper for DebuggerRCThread::Init
174//-----------------------------------------------------------------------------
175HANDLE OpenWin32EventOrThrow(
176 DWORD dwDesiredAccess,
177 BOOL bInheritHandle,
178 LPCWSTR lpName
179)
180{
181 CONTRACT(HANDLE)
182 {
183 THROWS;
184 GC_NOTRIGGER;
185 POSTCONDITION(RETVAL != NULL);
186 }
187 CONTRACT_END;
188
189 HANDLE h = WszOpenEvent(
190 dwDesiredAccess,
191 bInheritHandle,
192 lpName
193 );
194 if (h == NULL)
195 ThrowLastError();
196
197 RETURN h;
198}
199
200//---------------------------------------------------------------------------------------
201//
202// Init
203//
204// Initialize the IPC block.
205//
206// Arguments:
207// hRsea - Handle to Right-Side Event Available event.
208// hRser - Handle to Right-Side Event Read event.
209// hLsea - Handle to Left-Side Event Available event.
210// hLser - Handle to Left-Side Event Read event.
211// hLsuwe - Handle to Left-Side unmanaged wait event.
212//
213// Notes:
214// The Init method works since there are no virtual functions - don't add any virtual functions without
215// changing this!
216// We assume ownership of the handles as soon as we're called; regardless of our success.
217// On failure, we throw.
218// Initialization of the debugger control block occurs partly on the left side and partly on
219// the right side. This initialization occurs in parallel, so it's unsafe to make assumptions about
220// the order in which the fields will be initialized.
221//
222//
223//---------------------------------------------------------------------------------------
224HRESULT DebuggerIPCControlBlock::Init(
225 HANDLE hRsea,
226 HANDLE hRser,
227 HANDLE hLsea,
228 HANDLE hLser,
229 HANDLE hLsuwe
230)
231{
232 CONTRACTL
233 {
234 SO_INTOLERANT;
235 THROWS;
236 GC_NOTRIGGER;
237 }
238 CONTRACTL_END;
239
240 // NOTE this works since there are no virtual functions - don't add any without changing this!
241 // Although we assume the IPC block is zero-initialized by the OS upon creation, we still need to clear
242 // the memory here to protect ourselves from DOS attack. One scenario is when a malicious debugger
243 // pre-creates a bogus IPC block. This means that our synchronization scheme won't work in DOS
244 // attack scenarios, but we will be messed up anyway.
245 // WARNING!!! m_DCBSize is used as a semaphore and is set to non-zero to signal that initialization of the
246 // WARNING!!! DCB is complete. if you remove the below memset be sure to initialize m_DCBSize to zero in the ctor!
247 memset( this, 0, sizeof( DebuggerIPCControlBlock) );
248
249 // Setup version checking info.
250 m_verMajor = CLR_BUILD_VERSION;
251 m_verMinor = CLR_BUILD_VERSION_QFE;
252
253#ifdef _DEBUG
254 m_checkedBuild = true;
255#else
256 m_checkedBuild = false;
257#endif
258 m_bHostingInFiber = false;
259
260 // Are we in fiber mode? In Whidbey, we do not support launch a fiber mode process
261 // nor do we support attach to a fiber mode process.
262 //
263 if (g_CORDebuggerControlFlags & DBCF_FIBERMODE)
264 {
265 m_bHostingInFiber = true;
266 }
267
268#if !defined(FEATURE_DBGIPC_TRANSPORT_VM)
269 // Copy RSEA and RSER into the control block.
270 if (!m_rightSideEventAvailable.SetLocal(hRsea))
271 {
272 ThrowLastError();
273 }
274
275 if (!m_rightSideEventRead.SetLocal(hRser))
276 {
277 ThrowLastError();
278 }
279
280 if (!m_leftSideUnmanagedWaitEvent.SetLocal(hLsuwe))
281 {
282 ThrowLastError();
283 }
284#endif // !FEATURE_DBGIPC_TRANSPORT_VM
285
286
287 // Mark the debugger special thread list as not dirty, empty and null.
288 m_specialThreadListDirty = false;
289 m_specialThreadListLength = 0;
290 m_specialThreadList = NULL;
291
292 m_shutdownBegun = false;
293
294 return S_OK;
295}
296
297void DebuggerRCThread::WatchForStragglers(void)
298{
299 WRAPPER_NO_CONTRACT;
300
301 _ASSERTE(m_threadControlEvent != NULL);
302 LOG((LF_CORDB,LL_INFO100000, "DRCT::WFS:setting event to watch "
303 "for stragglers\n"));
304
305 SetEvent(m_threadControlEvent);
306}
307
308//---------------------------------------------------------------------------------------
309//
310// Init sets up all the objects that the RC thread will need to run.
311//
312//
313// Return Value:
314// S_OK on success. May also throw.
315//
316// Assumptions:
317// Called during startup, even if we're not debugging.
318//
319//
320//---------------------------------------------------------------------------------------
321HRESULT DebuggerRCThread::Init(void)
322{
323 CONTRACTL
324 {
325 SO_INTOLERANT;
326 THROWS;
327 GC_NOTRIGGER;
328 PRECONDITION(!ThisIsHelperThreadWorker()); // initialized by main thread
329 }
330 CONTRACTL_END;
331
332
333 LOG((LF_CORDB, LL_EVERYTHING, "DebuggerRCThreadInit called\n"));
334
335 DWORD dwStatus;
336 if (m_debugger == NULL)
337 {
338 ThrowHR(E_INVALIDARG);
339 }
340
341 // Init should only be called once.
342 if (g_pRCThread != NULL)
343 {
344 ThrowHR(E_FAIL);
345 }
346
347 g_pRCThread = this;
348
349 m_favorData.Init(); // throws
350
351
352 // Create the thread control event.
353 m_threadControlEvent = CreateWin32EventOrThrow(NULL, kAutoResetEvent, FALSE);
354
355 // Create the helper thread can go event.
356 m_helperThreadCanGoEvent = CreateWin32EventOrThrow(NULL, kManualResetEvent, TRUE);
357
358 m_pDCB = new(nothrow) DebuggerIPCControlBlock;
359
360 // Don't fail out because the shared memory failed to create
361#if _DEBUG
362 if (m_pDCB == NULL)
363 {
364 LOG((LF_CORDB, LL_INFO10000,
365 "DRCT::I: Failed to get Debug IPC block.\n"));
366 }
367#endif // _DEBUG
368
369 HRESULT hr;
370
371#if defined(FEATURE_DBGIPC_TRANSPORT_VM)
372
373 if (m_pDCB)
374 {
375 hr = m_pDCB->Init(NULL, NULL, NULL, NULL, NULL);
376 _ASSERTE(SUCCEEDED(hr)); // throws on error.
377 }
378#else //FEATURE_DBGIPC_TRANSPORT_VM
379
380 // Create the events that the thread will need to receive events
381 // from the out of process piece on the right side.
382 // We will not fail out if CreateEvent fails for RSEA or RSER. Because
383 // the worst case is that debugger cannot attach to debuggee.
384 //
385 HandleHolder rightSideEventAvailable(WszCreateEvent(NULL, (BOOL) kAutoResetEvent, FALSE, NULL));
386
387 // Security fix:
388 // We need to check the last error to see if the event was precreated or not
389 // If so, we need to release the handle right now.
390 //
391 dwStatus = GetLastError();
392 if (dwStatus == ERROR_ALREADY_EXISTS)
393 {
394 // clean up the handle now
395 rightSideEventAvailable.Clear();
396 }
397
398 HandleHolder rightSideEventRead(WszCreateEvent(NULL, (BOOL) kAutoResetEvent, FALSE, NULL));
399
400 // Security fix:
401 // We need to check the last error to see if the event was precreated or not
402 // If so, we need to release the handle right now.
403 //
404 dwStatus = GetLastError();
405 if (dwStatus == ERROR_ALREADY_EXISTS)
406 {
407 // clean up the handle now
408 rightSideEventRead.Clear();
409 }
410
411
412 HandleHolder leftSideUnmanagedWaitEvent(CreateWin32EventOrThrow(NULL, kManualResetEvent, FALSE));
413
414 // Copy RSEA and RSER into the control block only if shared memory is created without error.
415 if (m_pDCB)
416 {
417 // Since Init() gets ownership of handles as soon as it's called, we can
418 // release our ownership now.
419 rightSideEventAvailable.SuppressRelease();
420 rightSideEventRead.SuppressRelease();
421 leftSideUnmanagedWaitEvent.SuppressRelease();
422
423 // NOTE: initialization of the debugger control block occurs partly on the left side and partly on
424 // the right side. This initialization occurs in parallel, so it's unsafe to make assumptions about
425 // the order in which the fields will be initialized.
426 hr = m_pDCB->Init(rightSideEventAvailable,
427 rightSideEventRead,
428 NULL,
429 NULL,
430 leftSideUnmanagedWaitEvent);
431
432 _ASSERTE(SUCCEEDED(hr)); // throws on error.
433 }
434#endif //FEATURE_DBGIPC_TRANSPORT_VM
435
436 if(m_pDCB)
437 {
438 // We have to ensure that most of the runtime offsets for the out-of-proc DCB are initialized right away. This is
439 // needed to support certian races during an interop attach. Since we can't know whether an interop attach will ever
440 // happen or not, we are forced to do this now. Note: this is really too early, as some data structures haven't been
441 // initialized yet!
442 hr = EnsureRuntimeOffsetsInit(IPC_TARGET_OUTOFPROC);
443 _ASSERTE(SUCCEEDED(hr)); // throw on error
444
445 // Note: we have to mark that we need the runtime offsets re-initialized for the out-of-proc DCB. This is because
446 // things like the patch table aren't initialized yet. Calling NeedRuntimeOffsetsReInit() ensures that this happens
447 // before we really need the patch table.
448 NeedRuntimeOffsetsReInit(IPC_TARGET_OUTOFPROC);
449
450 m_pDCB->m_helperThreadStartAddr = (void *) DebuggerRCThread::ThreadProcStatic;
451 m_pDCB->m_helperRemoteStartAddr = (void *) DebuggerRCThread::ThreadProcRemote;
452 m_pDCB->m_leftSideProtocolCurrent = CorDB_LeftSideProtocolCurrent;
453 m_pDCB->m_leftSideProtocolMinSupported = CorDB_LeftSideProtocolMinSupported;
454
455 LOG((LF_CORDB, LL_INFO10,
456 "DRCT::I: version info: %d.%d.%d current protocol=%d, min protocol=%d\n",
457 m_pDCB->m_verMajor,
458 m_pDCB->m_verMinor,
459 m_pDCB->m_checkedBuild,
460 m_pDCB->m_leftSideProtocolCurrent,
461 m_pDCB->m_leftSideProtocolMinSupported));
462
463 // Left-side always creates helper-thread.
464 // @dbgtodo inspection - by end of V3, LS will never create helper-thread :)
465 m_pDCB->m_rightSideShouldCreateHelperThread = false;
466
467 // m_DCBSize is used as a semaphore to indicate that the DCB is fully initialized.
468 // let's ensure that it's updated after all the other fields.
469 MemoryBarrier();
470 m_pDCB->m_DCBSize = sizeof(DebuggerIPCControlBlock);
471 }
472
473 return S_OK;
474}
475
476#ifndef FEATURE_PAL
477
478// This function is used to verify the security descriptor on an event
479// matches our expectation to prevent attack. This should be called when
480// we opened an event by name and assumed that the RS creates the event.
481// That means the event's dacl should match our default policy - current user
482// and admin. It can be narrower. By default, the DACL looks like the debugger
483// process user, debuggee user, and admin.
484//
485HRESULT DebuggerRCThread::VerifySecurityOnRSCreatedEvents(
486 HANDLE sse,
487 HANDLE lsea,
488 HANDLE lser)
489{
490
491 CONTRACTL
492 {
493 SO_NOT_MAINLINE;
494 NOTHROW;
495 GC_NOTRIGGER;
496 }
497 CONTRACTL_END;
498
499 STRESS_LOG0(LF_CORDB,LL_INFO1000,"DRCT::VerifySecurityOnRSCreatedEvents\n");
500
501 if (lsea == NULL || lser == NULL)
502 {
503 // no valid handle, does not need to verify.
504 // The caller will close the handles
505 return E_FAIL;
506 }
507
508 HRESULT hr = S_OK;
509
510 SIZE_T i;
511 ACCESS_ALLOWED_ACE *pAllowAceSSE = NULL;
512 ACCESS_ALLOWED_ACE *pAllowAceLSEA = NULL;
513 ACCESS_ALLOWED_ACE *pAllowAceLSER = NULL;
514
515
516 EX_TRY
517 {
518 // Get security descriptors for the handles.
519 Win32SecurityDescriptor sdSSE;
520 sdSSE.InitFromHandle(sse);
521
522 Win32SecurityDescriptor sdLSEA;
523 sdLSEA.InitFromHandle(lsea);
524
525 Win32SecurityDescriptor sdLSER;
526 sdLSER.InitFromHandle(lser);
527
528
529
530
531 // Make sure all 3 have the same creator
532 // We've already verifed in CreateSetupSyncEvent that the SSE's owner is in the DACL.
533 if (!Sid::Equals(sdSSE.GetOwner(), sdLSEA.GetOwner()) ||
534 !Sid::Equals(sdSSE.GetOwner(), sdLSER.GetOwner()))
535 {
536 // Not equal! return now with failure code.
537 STRESS_LOG1(LF_CORDB,LL_INFO1000,"DRCT::VSORSCE failed on EqualSid - 0x%08x\n", hr);
538 ThrowHR(E_FAIL);
539 }
540
541 // DACL_SECURITY_INFORMATION
542 // Now verify the DACL. It should be only two of them at most. One of them is the
543 // target process SID.
544 Dacl daclSSE = sdSSE.GetDacl();
545 Dacl daclLSEA = sdLSEA.GetDacl();
546 Dacl daclLSER = sdLSER.GetDacl();
547
548
549 // Now all of these three ACL should be alike. There should be at most two of entries
550 // there. One if the debugger process's SID and one if debuggee sid.
551 if ((daclSSE.GetAceCount() != 1) && (daclSSE.GetAceCount() != 2))
552 {
553 ThrowHR(E_FAIL);
554 }
555
556
557 // All of the ace count should equal for all events.
558 if ((daclSSE.GetAceCount() != daclLSEA.GetAceCount()) ||
559 (daclSSE.GetAceCount() != daclLSER.GetAceCount()))
560 {
561 ThrowHR(E_FAIL);
562 }
563
564 // Now check the ACE inside.These should be all equal
565 for (i = 0; i < daclSSE.GetAceCount(); i++)
566 {
567 ACE_HEADER *pAce;
568
569 // Get the ace from the SSE
570 pAce = daclSSE.GetAce(i);
571 if (pAce->AceType != ACCESS_ALLOWED_ACE_TYPE)
572 {
573 ThrowHR(E_FAIL);
574 }
575 pAllowAceSSE = (ACCESS_ALLOWED_ACE*)pAce;
576
577 // Get the ace from LSEA
578 pAce = daclLSEA.GetAce(i);
579 if (pAce->AceType != ACCESS_ALLOWED_ACE_TYPE)
580 {
581 ThrowHR(E_FAIL);
582 }
583 pAllowAceLSEA = (ACCESS_ALLOWED_ACE*)pAce;
584
585 // This is the SID
586 // We can call EqualSid on this pAllowAce->SidStart
587 if (EqualSid((PSID)&(pAllowAceSSE->SidStart), (PSID)&(pAllowAceLSEA->SidStart)) == FALSE)
588 {
589 // ACE not equal. Fail out.
590 ThrowHR(E_FAIL);
591 }
592
593 // Get the ace from LSER
594 pAce = daclLSER.GetAce(i);
595 if (pAce->AceType != ACCESS_ALLOWED_ACE_TYPE)
596 {
597 ThrowHR(E_FAIL);
598 }
599 pAllowAceLSER = (ACCESS_ALLOWED_ACE*)pAce;
600
601 if (EqualSid((PSID)&(pAllowAceSSE->SidStart), (PSID)&(pAllowAceLSER->SidStart)) == FALSE)
602 {
603 // ACE not equal. Fail out.
604 ThrowHR(E_FAIL);
605 }
606 } // end for loop.
607
608
609 // The last ACE should be target process. That is it should be
610 // our process's sid!
611 //
612 if (pAllowAceLSER == NULL)
613 {
614 ThrowHR(E_FAIL);; // fail if we don't have the ACE.
615 }
616 {
617 SidBuffer sbCurrentProcess;
618 sbCurrentProcess.InitFromProcess(GetCurrentProcessId());
619 if (!Sid::Equals(sbCurrentProcess.GetSid(), (PSID)&(pAllowAceLSER->SidStart)))
620 {
621 ThrowHR(E_FAIL);
622 }
623 }
624 }
625 EX_CATCH
626 {
627 // If we threw an exception, then the verification failed.
628 hr = E_FAIL;
629 }
630 EX_END_CATCH(RethrowTerminalExceptions);
631
632 if (FAILED(hr))
633 {
634 STRESS_LOG1(LF_CORDB,LL_INFO1000,"DRCT::VSORSCE failed with - 0x%08x\n", hr);
635 }
636
637 return hr;
638}
639
640#endif // FEATURE_PAL
641
642//---------------------------------------------------------------------------------------
643//
644// Setup the Runtime Offsets struct.
645//
646// Arguments:
647// pDebuggerIPCControlBlock - Pointer to the debugger's portion of the IPC
648// block, which this routine will write into the offsets of various parts of
649// the runtime.
650//
651// Return Value:
652// S_OK on success.
653//
654//---------------------------------------------------------------------------------------
655HRESULT DebuggerRCThread::SetupRuntimeOffsets(DebuggerIPCControlBlock * pDebuggerIPCControlBlock)
656{
657 CONTRACTL
658 {
659 SO_INTOLERANT;
660 NOTHROW;
661 GC_NOTRIGGER;
662
663 PRECONDITION(ThisMaybeHelperThread());
664 }
665 CONTRACTL_END;
666
667 // Allocate the struct if needed. We just fill in any existing one.
668 DebuggerIPCRuntimeOffsets * pDebuggerRuntimeOffsets = pDebuggerIPCControlBlock->m_pRuntimeOffsets;
669
670 if (pDebuggerRuntimeOffsets == NULL)
671 {
672 // Perhaps we should preallocate this. This is the only allocation
673 // that would force SendIPCEvent to throw an exception. It'd be very
674 // nice to have
675 CONTRACT_VIOLATION(ThrowsViolation);
676 pDebuggerRuntimeOffsets = new DebuggerIPCRuntimeOffsets();
677 _ASSERTE(pDebuggerRuntimeOffsets != NULL); // throws on oom
678 }
679
680 // Fill out the struct.
681#ifdef FEATURE_INTEROP_DEBUGGING
682 pDebuggerRuntimeOffsets->m_genericHijackFuncAddr = Debugger::GenericHijackFunc;
683 // Set flares - these only exist for interop debugging.
684 pDebuggerRuntimeOffsets->m_signalHijackStartedBPAddr = (void*) SignalHijackStartedFlare;
685 pDebuggerRuntimeOffsets->m_excepForRuntimeHandoffStartBPAddr = (void*) ExceptionForRuntimeHandoffStartFlare;
686 pDebuggerRuntimeOffsets->m_excepForRuntimeHandoffCompleteBPAddr = (void*) ExceptionForRuntimeHandoffCompleteFlare;
687 pDebuggerRuntimeOffsets->m_signalHijackCompleteBPAddr = (void*) SignalHijackCompleteFlare;
688 pDebuggerRuntimeOffsets->m_excepNotForRuntimeBPAddr = (void*) ExceptionNotForRuntimeFlare;
689 pDebuggerRuntimeOffsets->m_notifyRSOfSyncCompleteBPAddr = (void*) NotifyRightSideOfSyncCompleteFlare;
690 pDebuggerRuntimeOffsets->m_debuggerWordTLSIndex = g_debuggerWordTLSIndex;
691
692#if !defined(FEATURE_CORESYSTEM)
693 // Grab the address of RaiseException in kernel32 because we have to play some games with exceptions
694 // that are generated there (just another reason why mixed mode debugging is shady). See bug 476768.
695 HMODULE hModule = WszGetModuleHandle(W("kernel32.dll"));
696 _ASSERTE(hModule != NULL);
697 PREFAST_ASSUME(hModule != NULL);
698 pDebuggerRuntimeOffsets->m_raiseExceptionAddr = GetProcAddress(hModule, "RaiseException");
699 _ASSERTE(pDebuggerRuntimeOffsets->m_raiseExceptionAddr != NULL);
700 hModule = NULL;
701#else
702 pDebuggerRuntimeOffsets->m_raiseExceptionAddr = NULL;
703#endif
704#endif // FEATURE_INTEROP_DEBUGGING
705
706 pDebuggerRuntimeOffsets->m_pPatches = DebuggerController::GetPatchTable();
707 pDebuggerRuntimeOffsets->m_pPatchTableValid = (BOOL*)DebuggerController::GetPatchTableValidAddr();
708 pDebuggerRuntimeOffsets->m_offRgData = DebuggerPatchTable::GetOffsetOfEntries();
709 pDebuggerRuntimeOffsets->m_offCData = DebuggerPatchTable::GetOffsetOfCount();
710 pDebuggerRuntimeOffsets->m_cbPatch = sizeof(DebuggerControllerPatch);
711 pDebuggerRuntimeOffsets->m_offAddr = offsetof(DebuggerControllerPatch, address);
712 pDebuggerRuntimeOffsets->m_offOpcode = offsetof(DebuggerControllerPatch, opcode);
713 pDebuggerRuntimeOffsets->m_cbOpcode = sizeof(PRD_TYPE);
714 pDebuggerRuntimeOffsets->m_offTraceType = offsetof(DebuggerControllerPatch, trace.type);
715 pDebuggerRuntimeOffsets->m_traceTypeUnmanaged = TRACE_UNMANAGED;
716
717 // @dbgtodo inspection - this should all go away or be obtained from DacDbi Primitives.
718 g_pEEInterface->GetRuntimeOffsets(&pDebuggerRuntimeOffsets->m_TLSIndex,
719 &pDebuggerRuntimeOffsets->m_TLSIsSpecialIndex,
720 &pDebuggerRuntimeOffsets->m_TLSCantStopIndex,
721 &pDebuggerRuntimeOffsets->m_EEThreadStateOffset,
722 &pDebuggerRuntimeOffsets->m_EEThreadStateNCOffset,
723 &pDebuggerRuntimeOffsets->m_EEThreadPGCDisabledOffset,
724 &pDebuggerRuntimeOffsets->m_EEThreadPGCDisabledValue,
725 &pDebuggerRuntimeOffsets->m_EEThreadFrameOffset,
726 &pDebuggerRuntimeOffsets->m_EEThreadMaxNeededSize,
727 &pDebuggerRuntimeOffsets->m_EEThreadSteppingStateMask,
728 &pDebuggerRuntimeOffsets->m_EEMaxFrameValue,
729 &pDebuggerRuntimeOffsets->m_EEThreadDebuggerFilterContextOffset,
730 &pDebuggerRuntimeOffsets->m_EEThreadCantStopOffset,
731 &pDebuggerRuntimeOffsets->m_EEFrameNextOffset,
732 &pDebuggerRuntimeOffsets->m_EEIsManagedExceptionStateMask);
733
734 // Remember the struct in the control block.
735 pDebuggerIPCControlBlock->m_pRuntimeOffsets = pDebuggerRuntimeOffsets;
736
737 return S_OK;
738}
739
740struct DebugFilterParam
741{
742 DebuggerIPCEvent *event;
743};
744
745// Filter called when we throw an exception while Handling events.
746static LONG _debugFilter(LPEXCEPTION_POINTERS ep, PVOID pv)
747{
748 LOG((LF_CORDB, LL_INFO10,
749 "Unhandled exception in Debugger::HandleIPCEvent\n"));
750
751 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
752
753#if defined(_DEBUG) || !defined(FEATURE_CORESYSTEM)
754 DebuggerIPCEvent *event = ((DebugFilterParam *)pv)->event;
755
756 DWORD pid = GetCurrentProcessId();
757 DWORD tid = GetCurrentThreadId();
758
759 DebuggerIPCEventType type = (DebuggerIPCEventType) (event->type & DB_IPCE_TYPE_MASK);
760#endif // _DEBUG || !FEATURE_CORESYSTEM
761
762 // We should never AV here. In a debug build, throw up an assert w/ lots of useful (private) info.
763#ifdef _DEBUG
764 {
765 // We can't really use SStrings on the helper thread; though if we're at this point, we've already died.
766 // So go ahead and risk it and use them anyways.
767 SString sStack;
768 StackScratchBuffer buffer;
769 GetStackTraceAtContext(sStack, ep->ContextRecord);
770 const CHAR *string = NULL;
771
772 EX_TRY
773 {
774 string = sStack.GetANSI(buffer);
775 }
776 EX_CATCH
777 {
778 string = "*Could not retrieve stack*";
779 }
780 EX_END_CATCH(RethrowTerminalExceptions);
781
782 CONSISTENCY_CHECK_MSGF(false,
783 ("Unhandled exception on the helper thread.\nEvent=%s(0x%x)\nCode=0x%0x, Ip=0x%p, .cxr=%p, .exr=%p.\n pid=0x%x (%d), tid=0x%x (%d).\n-----\nStack of exception:\n%s\n----\n",
784 IPCENames::GetName(type), type,
785 ep->ExceptionRecord->ExceptionCode, GetIP(ep->ContextRecord), ep->ContextRecord, ep->ExceptionRecord,
786 pid, pid, tid, tid,
787 string));
788 }
789#endif
790
791// this message box doesn't work well on coresystem... we actually get in a recursive exception handling loop
792#ifndef FEATURE_CORESYSTEM
793 // We took an AV on the helper thread. This is a catastrophic situation so we can
794 // simply call the EE's catastrophic message box to display the error.
795 EEMessageBoxCatastrophic(
796 IDS_DEBUG_UNHANDLEDEXCEPTION_IPC, IDS_DEBUG_SERVICE_CAPTION,
797 type,
798 ep->ExceptionRecord->ExceptionCode,
799 GetIP(ep->ContextRecord),
800 pid, pid, tid, tid);
801#endif
802
803 // For debugging, we can change the behavior by manually setting eax.
804 // EXCEPTION_EXECUTE_HANDLER=1, EXCEPTION_CONTINUE_SEARCH=0, EXCEPTION_CONTINUE_EXECUTION=-1
805 return EXCEPTION_CONTINUE_SEARCH;
806}
807
808#ifdef _DEBUG
809// Tracking to ensure that we don't call New() for the normal (non interop-safe heap)
810// on the helper thread. We also can't do a normal allocation when we have hard
811// suspended any other thread (since it could hold the OS heap lock).
812
813// TODO: this probably belongs in the EE itself, not here in the debugger stuff.
814
815void AssertAllocationAllowed()
816{
817#ifdef USE_INTEROPSAFE_HEAP
818 // Don't forget to preserve error status!
819 DWORD err = GetLastError();
820
821 // We can mark certain
822 if (g_DbgSuppressAllocationAsserts == 0)
823 {
824
825 // if we have hard suspended any threads. We want to assert as it could cause deadlock
826 // since those suspended threads may hold the OS heap lock
827 if (g_fEEStarted) {
828 _ASSERTE (!EEAllocationDisallowed());
829 }
830
831 // Can't call IsDbgHelperSpecialThread() here b/c that changes program state.
832 // So we use our
833 if (DebuggerRCThread::s_DbgHelperThreadId.IsCurrentThread())
834 {
835 // In case assert allocates, bump up the 'OK' counter to avoid an infinite recursion.
836 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
837
838 _ASSERTE(false || !"New called on Helper Thread");
839
840 }
841 }
842 SetLastError(err);
843#endif
844}
845#endif
846
847
848//---------------------------------------------------------------------------------------
849//
850// Primary function of the Runtime Controller thread. First, we let
851// the Debugger Interface know that we're up and running. Then, we run
852// the main loop.
853//
854//---------------------------------------------------------------------------------------
855void DebuggerRCThread::ThreadProc(void)
856{
857 CONTRACTL
858 {
859 SO_INTOLERANT;
860 NOTHROW;
861 GC_TRIGGERS; // Debugger::SuspendComplete can trigger GC
862
863 // Although we're the helper thread, we haven't set it yet.
864 DISABLED(PRECONDITION(ThisIsHelperThreadWorker()));
865
866 INSTANCE_CHECK;
867 }
868 CONTRACTL_END;
869
870 STRESS_LOG_RESERVE_MEM (0);
871 // This message actually serves a purpose (which is why it is always run)
872 // The Stress log is run during hijacking, when other threads can be suspended
873 // at arbitrary locations (including when holding a lock that NT uses to serialize
874 // all memory allocations). By sending a message now, we insure that the stress
875 // log will not allocate memory at these critical times an avoid deadlock.
876 {
877 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
878 STRESS_LOG0(LF_CORDB|LF_ALWAYS, LL_ALWAYS, "Debugger Thread spinning up\n");
879
880 // Call this to force creation of the TLS slots on helper-thread.
881 IsDbgHelperSpecialThread();
882 }
883
884#ifdef _DEBUG
885 // Track the helper thread.
886 s_DbgHelperThreadId.SetToCurrentThread();
887#endif
888 CantAllocHolder caHolder;
889
890
891#ifdef _DEBUG
892 // Cause wait in the helper thread startup. This lets us test against certain races.
893 // 1 = 6 sec. (shorter than Poll)
894 // 2 = 12 sec (longer than Poll).
895 // 3 = infinite - never comes up.
896 static int fDelayHelper = -1;
897
898 if (fDelayHelper == -1)
899 {
900 fDelayHelper = UnsafeGetConfigDWORD(CLRConfig::INTERNAL_DbgDelayHelper);
901 }
902
903 if (fDelayHelper)
904 {
905 DWORD dwSleep = 6000;
906
907 switch(fDelayHelper)
908 {
909 case 1: dwSleep = 6000; break;
910 case 2: dwSleep = 12000; break;
911 case 3: dwSleep = INFINITE; break;
912 }
913
914 ClrSleepEx(dwSleep, FALSE);
915 }
916#endif
917
918 LOG((LF_CORDB, LL_INFO1000, "DRCT::TP: helper thread spinning up...\n"));
919
920 // In case the shared memory is not initialized properly, it will be noop
921 if (m_pDCB == NULL)
922 {
923 return;
924 }
925
926 // Lock the debugger before spinning up.
927 Debugger::DebuggerLockHolder debugLockHolder(m_debugger);
928
929 if (m_pDCB->m_helperThreadId != 0)
930 {
931 // someone else has created a helper thread, we're outta here
932 // the most likely scenario here is that there was some kind of
933 // race between remotethread creation and localthread creation
934
935 LOG((LF_CORDB, LL_EVERYTHING, "Second debug helper thread creation detected, thread will safely suicide\n"));
936 // dbgLockHolder goes out of scope - implicit Release
937 return;
938 }
939
940 // this thread took the lock and there is no existing m_helperThreadID therefore
941 // this *IS* the helper thread and nobody else can be the helper thread
942
943 // the handle was created by the Start method
944 _ASSERTE(m_thread != NULL);
945
946#ifdef _DEBUG
947 // Make sure that we have the proper permissions.
948 {
949 DWORD dwWaitResult = WaitForSingleObject(m_thread, 0);
950 _ASSERTE(dwWaitResult == WAIT_TIMEOUT);
951 }
952#endif
953
954 // Mark that we're the true helper thread. Now that we've marked
955 // this, no other threads will ever become the temporary helper
956 // thread.
957 m_pDCB->m_helperThreadId = GetCurrentThreadId();
958
959 LOG((LF_CORDB, LL_INFO1000, "DRCT::TP: helper thread id is 0x%x helperThreadId\n",
960 m_pDCB->m_helperThreadId));
961
962 // If there is a temporary helper thread, then we need to wait for
963 // it to finish being the helper thread before we can become the
964 // helper thread.
965 if (m_pDCB->m_temporaryHelperThreadId != 0)
966 {
967 LOG((LF_CORDB, LL_INFO1000,
968 "DRCT::TP: temporary helper thread 0x%x is in the way, "
969 "waiting...\n",
970 m_pDCB->m_temporaryHelperThreadId));
971
972 debugLockHolder.Release();
973
974 // Wait for the temporary helper thread to finish up.
975 DWORD dwWaitResult = WaitForSingleObject(m_helperThreadCanGoEvent, INFINITE);
976 (void)dwWaitResult; //prevent "unused variable" error from GCC
977
978 LOG((LF_CORDB, LL_INFO1000, "DRCT::TP: done waiting for temp help to finish up.\n"));
979
980 _ASSERTE(dwWaitResult == WAIT_OBJECT_0);
981 _ASSERTE(m_pDCB->m_temporaryHelperThreadId==0);
982 }
983 else
984 {
985 LOG((LF_CORDB, LL_INFO1000, "DRCT::TP: no temp help in the way...\n"));
986
987 debugLockHolder.Release();
988 }
989
990 // Run the main loop as the true helper thread.
991 MainLoop();
992}
993
994void DebuggerRCThread::RightSideDetach(void)
995{
996 _ASSERTE( m_fDetachRightSide == false );
997 m_fDetachRightSide = true;
998#if !defined(FEATURE_DBGIPC_TRANSPORT_VM)
999 CloseIPCHandles();
1000#endif // !FEATURE_DBGIPC_TRANSPORT_VM
1001}
1002
1003//
1004// These defines control how many times we spin while waiting for threads to sync and how often. Note its higher in
1005// debug builds to allow extra time for threads to sync.
1006//
1007#define CorDB_SYNC_WAIT_TIMEOUT 20 // 20ms
1008
1009#ifdef _DEBUG
1010#define CorDB_MAX_SYNC_SPIN_COUNT (10000 / CorDB_SYNC_WAIT_TIMEOUT) // (10 seconds)
1011#else
1012#define CorDB_MAX_SYNC_SPIN_COUNT (3000 / CorDB_SYNC_WAIT_TIMEOUT) // (3 seconds)
1013#endif
1014
1015//
1016// NDPWhidbey issue 10749 - Due to a compiler change for vc7.1,
1017// Don't inline this function!
1018// PAL_TRY allocates space on the stack and so can not be used within a loop,
1019// else we'll slowly leak stack space w/ each interation and get an overflow.
1020// So make this its own function to enforce that we free the stack space between
1021// iterations.
1022//
1023bool HandleIPCEventWrapper(Debugger* pDebugger, DebuggerIPCEvent *e)
1024{
1025 struct Param : DebugFilterParam
1026 {
1027 Debugger* pDebugger;
1028 bool wasContinue;
1029 } param;
1030 param.event = e;
1031 param.pDebugger = pDebugger;
1032 param.wasContinue = false;
1033 PAL_TRY(Param *, pParam, &param)
1034 {
1035 pParam->wasContinue = pParam->pDebugger->HandleIPCEvent(pParam->event);
1036 }
1037 PAL_EXCEPT_FILTER(_debugFilter)
1038 {
1039 LOG((LF_CORDB, LL_INFO10, "Unhandled exception caught in Debugger::HandleIPCEvent\n"));
1040 }
1041 PAL_ENDTRY
1042
1043 return param.wasContinue;
1044}
1045
1046bool DebuggerRCThread::HandleRSEA()
1047{
1048 CONTRACTL
1049 {
1050 SO_NOT_MAINLINE;
1051 NOTHROW;
1052 if (g_pEEInterface->GetThread() != NULL) { GC_TRIGGERS; } else { GC_NOTRIGGER; }
1053 PRECONDITION(ThisIsHelperThreadWorker());
1054 }
1055 CONTRACTL_END;
1056
1057 LOG((LF_CORDB,LL_INFO10000, "RSEA from out of process (right side)\n"));
1058 DebuggerIPCEvent * e;
1059#if !defined(FEATURE_DBGIPC_TRANSPORT_VM)
1060 // Make room for any Right Side event on the stack.
1061 BYTE buffer[CorDBIPC_BUFFER_SIZE];
1062 e = (DebuggerIPCEvent *) buffer;
1063
1064 // If the RSEA is signaled, then handle the event from the Right Side.
1065 memcpy(e, GetIPCEventReceiveBuffer(), CorDBIPC_BUFFER_SIZE);
1066#else
1067 // Be sure to fetch the event into the official receive buffer since some event handlers assume it's there
1068 // regardless of the the event buffer pointer passed to them.
1069 e = GetIPCEventReceiveBuffer();
1070 g_pDbgTransport->GetNextEvent(e, CorDBIPC_BUFFER_SIZE);
1071#endif // !FEATURE_DBGIPC_TRANSPOPRT
1072
1073#if !defined(FEATURE_DBGIPC_TRANSPORT_VM)
1074 // If no reply is required, then let the Right Side go since we've got a copy of the event now.
1075 _ASSERTE(!e->asyncSend || !e->replyRequired);
1076
1077 if (!e->replyRequired && !e->asyncSend)
1078 {
1079 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML: no reply required, letting Right Side go.\n"));
1080
1081 BOOL succ = SetEvent(m_pDCB->m_rightSideEventRead);
1082
1083 if (!succ)
1084 CORDBDebuggerSetUnrecoverableWin32Error(m_debugger, 0, true);
1085 }
1086#ifdef LOGGING
1087 else if (e->asyncSend)
1088 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML: async send.\n"));
1089 else
1090 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML: reply required, holding Right Side...\n"));
1091#endif
1092#endif // !FEATURE_DBGIPC_TRANSPORT_VM
1093
1094 // Pass the event to the debugger for handling. Returns true if the event was a Continue event and we can
1095 // stop looking for stragglers. We wrap this whole thing in an exception handler to help us debug faults.
1096 bool wasContinue = false;
1097
1098 wasContinue = HandleIPCEventWrapper(m_debugger, e);
1099
1100 return wasContinue;
1101}
1102
1103//---------------------------------------------------------------------------------------
1104//
1105// Main loop of the Runtime Controller thread. It waits for IPC events
1106// and dishes them out to the Debugger object for processing.
1107//
1108// Some of this logic is copied in Debugger::VrpcToVls
1109//
1110//---------------------------------------------------------------------------------------
1111void DebuggerRCThread::MainLoop()
1112{
1113 // This function can only be called on native Debugger helper thread.
1114 //
1115
1116 CONTRACTL
1117 {
1118 SO_INTOLERANT;
1119 NOTHROW;
1120
1121 PRECONDITION(m_thread != NULL);
1122 PRECONDITION(ThisIsHelperThreadWorker());
1123 PRECONDITION(IsDbgHelperSpecialThread()); // Can only be called on native debugger helper thread
1124 PRECONDITION((!ThreadStore::HoldingThreadStore()) || g_fProcessDetach);
1125 }
1126 CONTRACTL_END;
1127
1128 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML:: running main loop\n"));
1129
1130 // Anybody doing helper duty is in a can't-stop range, period.
1131 // Our helper thread is already in a can't-stop range, so this is particularly useful for
1132 // threads doing helper duty.
1133 CantStopHolder cantStopHolder;
1134
1135 HANDLE rghWaitSet[DRCT_COUNT_FINAL];
1136
1137#ifdef _DEBUG
1138 DWORD dwSyncSpinCount = 0;
1139#endif
1140
1141 // We start out just listening on RSEA and the thread control event...
1142 unsigned int cWaitCount = DRCT_COUNT_INITIAL;
1143 DWORD dwWaitTimeout = INFINITE;
1144 rghWaitSet[DRCT_CONTROL_EVENT] = m_threadControlEvent;
1145 rghWaitSet[DRCT_FAVORAVAIL] = GetFavorAvailableEvent();
1146#if !defined(FEATURE_DBGIPC_TRANSPORT_VM)
1147 rghWaitSet[DRCT_RSEA] = m_pDCB->m_rightSideEventAvailable;
1148#else
1149 rghWaitSet[DRCT_RSEA] = g_pDbgTransport->GetIPCEventReadyEvent();
1150#endif // !FEATURE_DBGIPC_TRANSPORT_VM
1151
1152 CONTRACT_VIOLATION(ThrowsViolation);// HndCreateHandle throws, and this loop is not backstopped by any EH
1153
1154 // Lock holder. Don't take it yet. We take lock on this when we succeeded suspended runtime.
1155 // We will release the lock later when continue happens and runtime resumes
1156 Debugger::DebuggerLockHolder debugLockHolderSuspended(m_debugger, false);
1157
1158 while (m_run)
1159 {
1160 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML: waiting for event.\n"));
1161
1162#if !defined(FEATURE_DBGIPC_TRANSPORT_VM)
1163 // If there is a debugger attached, wait on its handle, too...
1164 if ((cWaitCount == DRCT_COUNT_INITIAL) &&
1165 m_pDCB->m_rightSideProcessHandle.ImportToLocalProcess() != NULL)
1166 {
1167 _ASSERTE((cWaitCount + 1) == DRCT_COUNT_FINAL);
1168 rghWaitSet[DRCT_DEBUGGER_EVENT] = m_pDCB->m_rightSideProcessHandle;
1169 cWaitCount = DRCT_COUNT_FINAL;
1170 }
1171#endif // !FEATURE_DBGIPC_TRANSPORT_VM
1172
1173
1174 if (m_fDetachRightSide)
1175 {
1176 m_fDetachRightSide = false;
1177
1178#if !defined(FEATURE_DBGIPC_TRANSPORT_VM)
1179 _ASSERTE(cWaitCount == DRCT_COUNT_FINAL);
1180 _ASSERTE((cWaitCount - 1) == DRCT_COUNT_INITIAL);
1181
1182 rghWaitSet[DRCT_DEBUGGER_EVENT] = NULL;
1183 cWaitCount = DRCT_COUNT_INITIAL;
1184#endif // !FEATURE_DBGIPC_TRANSPORT_VM
1185 }
1186
1187 // Wait for an event from the Right Side.
1188 DWORD dwWaitResult = WaitForMultipleObjectsEx(cWaitCount, rghWaitSet, FALSE, dwWaitTimeout, FALSE);
1189
1190 if (!m_run)
1191 {
1192 continue;
1193 }
1194
1195
1196 if (dwWaitResult == WAIT_OBJECT_0 + DRCT_DEBUGGER_EVENT)
1197 {
1198 // If the handle of the right side process is signaled, then we've lost our controlling debugger. We
1199 // terminate this process immediatley in such a case.
1200 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML: terminating this process. Right Side has exited.\n"));
1201 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
1202 EEPOLICY_HANDLE_FATAL_ERROR(0);
1203 _ASSERTE(!"Should never reach this point.");
1204 }
1205 else if (dwWaitResult == WAIT_OBJECT_0 + DRCT_FAVORAVAIL)
1206 {
1207 // execute the callback set by DoFavor()
1208 FAVORCALLBACK fpCallback = GetFavorFnPtr();
1209 // We never expect the callback to be null unless some other component
1210 // wrongly signals our event (see DD 463807).
1211 // In case we messed up, we will not set the FavorReadEvent and will hang favor requesting thread.
1212 if (fpCallback)
1213 {
1214 (*fpCallback)(GetFavorData());
1215 SetEvent(GetFavorReadEvent());
1216 }
1217 }
1218 else if (dwWaitResult == WAIT_OBJECT_0 + DRCT_RSEA)
1219 {
1220 bool fWasContinue = HandleRSEA();
1221
1222 if (fWasContinue)
1223 {
1224
1225 // If they called continue, then we must have released the TSL.
1226 _ASSERTE(!ThreadStore::HoldingThreadStore() || g_fProcessDetach);
1227
1228 // Let's release the lock here since runtime is resumed.
1229 debugLockHolderSuspended.Release();
1230
1231 // This debugger thread shoud not be holding debugger locks anymore
1232 _ASSERTE(!g_pDebugger->ThreadHoldsLock());
1233#ifdef _DEBUG
1234 // Always reset the syncSpinCount to 0 on a continue so that we have the maximum number of possible
1235 // spins the next time we need to sync.
1236 dwSyncSpinCount = 0;
1237#endif
1238
1239 if (dwWaitTimeout != INFINITE)
1240 {
1241 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML:: don't check for stragglers due to continue.\n"));
1242
1243 dwWaitTimeout = INFINITE;
1244 }
1245
1246 }
1247 }
1248 else if (dwWaitResult == WAIT_OBJECT_0 + DRCT_CONTROL_EVENT)
1249 {
1250 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML:: straggler event set.\n"));
1251
1252 ThreadStoreLockHolder tsl;
1253 Debugger::DebuggerLockHolder debugLockHolder(m_debugger);
1254 // Make sure that we're still synchronizing...
1255 if (m_debugger->IsSynchronizing())
1256 {
1257 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML:: dropping the timeout.\n"));
1258
1259 dwWaitTimeout = CorDB_SYNC_WAIT_TIMEOUT;
1260
1261 //
1262 // Skip waiting the first time and just give it a go. Note: Implicit
1263 // release of the debugger and thread store lock, because we are leaving its scope.
1264 //
1265 goto LWaitTimedOut;
1266 }
1267#ifdef LOGGING
1268 else
1269 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML:: told to wait, but not syncing anymore.\n"));
1270#endif
1271 // dbgLockHolder goes out of scope - implicit Release
1272 // tsl goes out of scope - implicit Release
1273 }
1274 else if (dwWaitResult == WAIT_TIMEOUT)
1275 {
1276
1277LWaitTimedOut:
1278
1279 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML:: wait timed out.\n"));
1280
1281 ThreadStore::LockThreadStore();
1282 // Debugger::DebuggerLockHolder debugLockHolder(m_debugger);
1283 // Explicitly get the lock here since we try to check to see if
1284 // have suspended. We will release the lock if we are not suspended yet.
1285 //
1286 debugLockHolderSuspended.Acquire();
1287
1288 // We should still be synchronizing, otherwise we would not have timed out.
1289 _ASSERTE(m_debugger->IsSynchronizing());
1290
1291 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML:: sweeping the thread list.\n"));
1292
1293#ifdef _DEBUG
1294 // If we fail to suspend the CLR, don't bother waiting for a BVT to timeout,
1295 // fire up an assert up now.
1296 // Threads::m_DebugWillSyncCount+1 is the number of outstanding threads.
1297 // We're trying to suspend any thread w/ TS_DebugWillSync set.
1298 if (dwSyncSpinCount++ > CorDB_MAX_SYNC_SPIN_COUNT)
1299 {
1300 _ASSERTE_MSG(false, "Timeout trying to suspend CLR for debugging. Possibly a deadlock.\n"\
1301 "You can ignore this assert to continue waiting\n");
1302 dwSyncSpinCount = 0;
1303 }
1304#endif
1305
1306 // Don't call Sweep if we're doing helper thread duty.
1307 // If we're doing helper thread duty, then we already Suspended the CLR, and we already hold the TSL.
1308 bool fSuspended;
1309 {
1310 // SweepThreadsForDebug() may call new!!! ARGG!!!
1311 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
1312 fSuspended = g_pEEInterface->SweepThreadsForDebug(false);
1313 }
1314
1315 if (fSuspended)
1316 {
1317 STRESS_LOG0(LF_CORDB, LL_INFO1000, "DRCT::ML:: wait set empty after sweep.\n");
1318
1319 // There are no more threads to wait for, so go ahead and send the sync complete event.
1320 m_debugger->SuspendComplete();
1321 dwWaitTimeout = INFINITE;
1322
1323 // Note: we hold the thread store lock now and debugger lock...
1324
1325 // We also hold debugger lock the whole time that Runtime is stopped. We will release the debugger lock
1326 // when we receive the Continue event that resumes the runtime.
1327
1328 _ASSERTE(ThreadStore::HoldingThreadStore() || g_fProcessDetach);
1329 }
1330 else
1331 {
1332 // If we're doing helper thread duty, then we expect to have been suspended already.
1333 // And so the sweep should always succeed.
1334 STRESS_LOG0(LF_CORDB, LL_INFO1000, "DRCT::ML:: threads still syncing after sweep.\n");
1335 debugLockHolderSuspended.Release();
1336 ThreadStore::UnlockThreadStore();
1337 }
1338 // debugLockHolderSuspended does not go out of scope. It has to be either released explicitly on the line above or
1339 // we intend to hold the lock till we hit continue event.
1340
1341 }
1342 }
1343
1344 STRESS_LOG0(LF_CORDB, LL_INFO1000, "DRCT::ML:: Exiting.\n");
1345}
1346
1347//---------------------------------------------------------------------------------------
1348//
1349// Main loop of the temporary Helper thread. It waits for IPC events
1350// and dishes them out to the Debugger object for processing.
1351//
1352// Notes:
1353// When we enter here, we are holding debugger lock and thread store lock.
1354// The debugger lock was SuppressRelease in DoHelperThreadDuty. The continue event
1355// that we are waiting for will trigger the corresponding release.
1356//
1357// IMPORTANT!!! READ ME!!!!
1358// This MainLoop is similiar to MainLoop function above but simplified to deal with only
1359// some scenario. So if you change here, you should look at MainLoop to see if same change is
1360// required.
1361//---------------------------------------------------------------------------------------
1362void DebuggerRCThread::TemporaryHelperThreadMainLoop()
1363{
1364 CONTRACTL
1365 {
1366 SO_NOT_MAINLINE;
1367 NOTHROW;
1368
1369
1370 // If we come in here, this managed thread is trying to do helper thread duty.
1371 // It should be holding the debugger lock!!!
1372 //
1373 PRECONDITION(m_debugger->ThreadHoldsLock());
1374 PRECONDITION((ThreadStore::HoldingThreadStore()) || g_fProcessDetach);
1375 PRECONDITION(ThisIsTempHelperThread());
1376 }
1377 CONTRACTL_END;
1378
1379 STRESS_LOG0(LF_CORDB, LL_INFO1000, "DRCT::THTML:: Doing helper thread duty, running main loop.\n");
1380 // Anybody doing helper duty is in a can't-stop range, period.
1381 // Our helper thread is already in a can't-stop range, so this is particularly useful for
1382 // threads doing helper duty.
1383 CantStopHolder cantStopHolder;
1384
1385 HANDLE rghWaitSet[DRCT_COUNT_FINAL];
1386
1387#ifdef _DEBUG
1388 DWORD dwSyncSpinCount = 0;
1389#endif
1390
1391 // We start out just listening on RSEA and the thread control event...
1392 unsigned int cWaitCount = DRCT_COUNT_INITIAL;
1393 DWORD dwWaitTimeout = INFINITE;
1394 rghWaitSet[DRCT_CONTROL_EVENT] = m_threadControlEvent;
1395 rghWaitSet[DRCT_FAVORAVAIL] = GetFavorAvailableEvent();
1396#if !defined(FEATURE_DBGIPC_TRANSPORT_VM)
1397 rghWaitSet[DRCT_RSEA] = m_pDCB->m_rightSideEventAvailable;
1398#else //FEATURE_DBGIPC_TRANSPORT_VM
1399 rghWaitSet[DRCT_RSEA] = g_pDbgTransport->GetIPCEventReadyEvent();
1400#endif // !FEATURE_DBGIPC_TRANSPORT_VM
1401
1402 CONTRACT_VIOLATION(ThrowsViolation);// HndCreateHandle throws, and this loop is not backstopped by any EH
1403
1404 while (m_run)
1405 {
1406 LOG((LF_CORDB, LL_INFO1000, "DRCT::ML: waiting for event.\n"));
1407
1408 // Wait for an event from the Right Side.
1409 DWORD dwWaitResult = WaitForMultipleObjectsEx(cWaitCount, rghWaitSet, FALSE, dwWaitTimeout, FALSE);
1410
1411 if (!m_run)
1412 {
1413 continue;
1414 }
1415
1416
1417 if (dwWaitResult == WAIT_OBJECT_0 + DRCT_DEBUGGER_EVENT)
1418 {
1419 // If the handle of the right side process is signaled, then we've lost our controlling debugger. We
1420 // terminate this process immediatley in such a case.
1421 LOG((LF_CORDB, LL_INFO1000, "DRCT::THTML: terminating this process. Right Side has exited.\n"));
1422
1423 TerminateProcess(GetCurrentProcess(), 0);
1424 _ASSERTE(!"Should never reach this point.");
1425 }
1426 else if (dwWaitResult == WAIT_OBJECT_0 + DRCT_FAVORAVAIL)
1427 {
1428 // execute the callback set by DoFavor()
1429 (*GetFavorFnPtr())(GetFavorData());
1430
1431 SetEvent(GetFavorReadEvent());
1432 }
1433 else if (dwWaitResult == WAIT_OBJECT_0 + DRCT_RSEA)
1434 {
1435 // @todo:
1436 // We are only interested in dealing with Continue event here...
1437 // Once we remove the HelperThread duty, this will just go away.
1438 //
1439 bool fWasContinue = HandleRSEA();
1440
1441 if (fWasContinue)
1442 {
1443 // If they called continue, then we must have released the TSL.
1444 _ASSERTE(!ThreadStore::HoldingThreadStore() || g_fProcessDetach);
1445
1446#ifdef _DEBUG
1447 // Always reset the syncSpinCount to 0 on a continue so that we have the maximum number of possible
1448 // spins the next time we need to sync.
1449 dwSyncSpinCount = 0;
1450#endif
1451
1452 // HelperThread duty is finished. We have got a Continue message
1453 goto LExit;
1454 }
1455 }
1456 else if (dwWaitResult == WAIT_OBJECT_0 + DRCT_CONTROL_EVENT)
1457 {
1458 LOG((LF_CORDB, LL_INFO1000, "DRCT::THTML:: straggler event set.\n"));
1459
1460 // Make sure that we're still synchronizing...
1461 _ASSERTE(m_debugger->IsSynchronizing());
1462 LOG((LF_CORDB, LL_INFO1000, "DRCT::THTML:: dropping the timeout.\n"));
1463
1464 dwWaitTimeout = CorDB_SYNC_WAIT_TIMEOUT;
1465
1466 //
1467 // Skip waiting the first time and just give it a go. Note: Implicit
1468 // release of the lock, because we are leaving its scope.
1469 //
1470 goto LWaitTimedOut;
1471 }
1472 else if (dwWaitResult == WAIT_TIMEOUT)
1473 {
1474
1475LWaitTimedOut:
1476
1477 LOG((LF_CORDB, LL_INFO1000, "DRCT::THTML:: wait timed out.\n"));
1478
1479 // We should still be synchronizing, otherwise we would not have timed out.
1480 _ASSERTE(m_debugger->IsSynchronizing());
1481
1482 LOG((LF_CORDB, LL_INFO1000, "DRCT::THTML:: sweeping the thread list.\n"));
1483
1484#ifdef _DEBUG
1485 // If we fail to suspend the CLR, don't bother waiting for a BVT to timeout,
1486 // fire up an assert up now.
1487 // Threads::m_DebugWillSyncCount+1 is the number of outstanding threads.
1488 // We're trying to suspend any thread w/ TS_DebugWillSync set.
1489 if (dwSyncSpinCount++ > CorDB_MAX_SYNC_SPIN_COUNT)
1490 {
1491 _ASSERTE(false || !"Timeout trying to suspend CLR for debugging. Possibly a deadlock. "
1492 "You can ignore this assert to continue waiting\n");
1493 dwSyncSpinCount = 0;
1494 }
1495#endif
1496
1497 STRESS_LOG0(LF_CORDB, LL_INFO1000, "DRCT::THTML:: wait set empty after sweep.\n");
1498
1499 // We are holding Debugger lock (Look at the SuppressRelease on the DoHelperThreadDuty)
1500 // The debugger lock will be released on the Continue event which we will then
1501 // exit the loop.
1502
1503 // There are no more threads to wait for, so go ahead and send the sync complete event.
1504 m_debugger->SuspendComplete();
1505 dwWaitTimeout = INFINITE;
1506
1507 // Note: we hold the thread store lock now and debugger lock...
1508 _ASSERTE(ThreadStore::HoldingThreadStore() || g_fProcessDetach);
1509
1510 }
1511 }
1512
1513LExit:
1514
1515 STRESS_LOG0(LF_CORDB, LL_INFO1000, "DRCT::THTML:: Exiting.\n");
1516}
1517
1518
1519
1520//
1521// This is the thread's real thread proc. It simply calls to the
1522// thread proc on the RCThread object.
1523//
1524/*static*/ DWORD WINAPI DebuggerRCThread::ThreadProcRemote(LPVOID)
1525{
1526 // We just wrap create a local thread and we're outta here
1527 WRAPPER_NO_CONTRACT;
1528
1529 ClrFlsSetThreadType(ThreadType_DbgHelper);
1530
1531 LOG((LF_CORDB, LL_EVERYTHING, "ThreadProcRemote called\n"));
1532#ifdef _DEBUG
1533 dbgOnly_IdentifySpecialEEThread();
1534#endif
1535
1536 // this method can be called both by a local createthread or a remote create thread
1537 // so we must use the g_RCThread global to find the (unique!) this pointer
1538 // we cannot count on the parameter.
1539
1540 DebuggerRCThread* t = (DebuggerRCThread*)g_pRCThread;
1541
1542 // This remote thread is created by the debugger process
1543 // and so its ACLs will reflect permissions for the user running
1544 // the debugger. If this process is running in the context of a
1545 // different user then this (the now running) process will not be
1546 // able to do operations on that (remote) thread.
1547 //
1548 // To avoid this problem, if we are the remote thread, then
1549 // we simply launch a new, local, thread right here and let
1550 // the remote thread die. This new thread is created the same
1551 // way as always, and since it is created by this process
1552 // this process will be able to synchronize with it and so forth
1553
1554 t->Start(); // this thread is remote, we must start a new thread
1555
1556 return 0;
1557}
1558
1559//
1560// This is the thread's real thread proc. It simply calls to the
1561// thread proc on the RCThread object.
1562//
1563/*static*/ DWORD WINAPI DebuggerRCThread::ThreadProcStatic(LPVOID)
1564{
1565 // We just wrap the instance method DebuggerRCThread::ThreadProc
1566 WRAPPER_NO_CONTRACT;
1567
1568 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO();
1569
1570 ClrFlsSetThreadType(ThreadType_DbgHelper);
1571
1572 LOG((LF_CORDB, LL_EVERYTHING, "ThreadProcStatic called\n"));
1573
1574#ifdef _DEBUG
1575 dbgOnly_IdentifySpecialEEThread();
1576#endif
1577
1578 DebuggerRCThread* t = (DebuggerRCThread*)g_pRCThread;
1579
1580 t->ThreadProc(); // this thread is local, go and become the helper
1581
1582 END_SO_INTOLERANT_CODE;
1583
1584 return 0;
1585}
1586
1587RCThreadLazyInit * DebuggerRCThread::GetLazyData()
1588{
1589 return g_pDebugger->GetRCThreadLazyData();
1590}
1591
1592
1593//
1594// Start actually creates and starts the RC thread. It waits for the thread
1595// to come up and perform initial synchronization with the Debugger
1596// Interface before returning.
1597//
1598HRESULT DebuggerRCThread::Start(void)
1599{
1600 CONTRACTL
1601 {
1602 SO_INTOLERANT;
1603 NOTHROW;
1604 GC_NOTRIGGER;
1605 }
1606 CONTRACTL_END;
1607
1608 HRESULT hr = S_OK;
1609
1610 LOG((LF_CORDB, LL_EVERYTHING, "DebuggerRCThread::Start called...\n"));
1611
1612 DWORD helperThreadId;
1613
1614 if (m_thread != NULL)
1615 {
1616 LOG((LF_CORDB, LL_EVERYTHING, "DebuggerRCThread::Start declined to start another helper thread...\n"));
1617 return S_OK;
1618 }
1619
1620 Debugger::DebuggerLockHolder debugLockHolder(m_debugger);
1621
1622 if (m_thread == NULL)
1623 {
1624 // Create suspended so that we can sniff the tid before the thread actually runs.
1625 // This may not be before the native thread-create event, but should be before everything else.
1626 // Note: strange as it may seem, the Right Side depends on us
1627 // using CreateThread to create the helper thread here. If you
1628 // ever change this to some other thread creation routine, you
1629 // need to update the logic in process.cpp where we discover the
1630 // helper thread on CREATE_THREAD_DEBUG_EVENTs...
1631 m_thread = CreateThread(NULL, 0, DebuggerRCThread::ThreadProcStatic,
1632 NULL, CREATE_SUSPENDED, &helperThreadId );
1633
1634 if (m_thread == NULL)
1635 {
1636 LOG((LF_CORDB, LL_EVERYTHING, "DebuggerRCThread failed, err=%d\n", GetLastError()));
1637 hr = HRESULT_FROM_GetLastError();
1638
1639 }
1640 else
1641 {
1642 LOG((LF_CORDB, LL_EVERYTHING, "DebuggerRCThread start was successful, id=%d\n", helperThreadId));
1643 }
1644
1645 // This gets published immediately.
1646 DebuggerIPCControlBlock* dcb = GetDCB();
1647 PREFIX_ASSUME(dcb != NULL);
1648 dcb->m_realHelperThreadId = helperThreadId;
1649
1650#ifdef _DEBUG
1651 // Record the OS Thread ID for debugging purposes.
1652 m_DbgHelperThreadOSTid = helperThreadId ;
1653#endif
1654
1655 if (m_thread != NULL)
1656 {
1657 ResumeThread(m_thread);
1658 }
1659
1660 }
1661
1662 // unlock debugger lock is implied.
1663
1664 return hr;
1665}
1666
1667
1668//---------------------------------------------------------------------------------------
1669//
1670// Stop causes the RC thread to stop receiving events and exit.
1671// It does not wait for it to exit before returning (hence "AsyncStop" instead of "Stop").
1672//
1673// Return Value:
1674// Always S_OK at the moment.
1675//
1676//---------------------------------------------------------------------------------------
1677HRESULT DebuggerRCThread::AsyncStop(void)
1678{
1679 CONTRACTL
1680 {
1681 SO_INTOLERANT;
1682 NOTHROW;
1683 GC_NOTRIGGER;
1684
1685#ifdef _TARGET_X86_
1686 PRECONDITION(!ThisIsHelperThreadWorker());
1687#else
1688 PRECONDITION(!ThisIsHelperThreadWorker());
1689#endif
1690 }
1691 CONTRACTL_END;
1692
1693 HRESULT hr = S_OK;
1694
1695 m_run = FALSE;
1696
1697 // We need to get the helper thread out of its wait loop. So ping the thread-control event.
1698 // (Don't ping RSEA since that event should be used only for IPC communication).
1699 // Don't bother waiting for it to exit.
1700 SetEvent(this->m_threadControlEvent);
1701
1702 return hr;
1703}
1704
1705//---------------------------------------------------------------------------------------
1706//
1707// This method checks that the runtime offset has been loaded, and if not, loads it.
1708//
1709//---------------------------------------------------------------------------------------
1710HRESULT inline DebuggerRCThread::EnsureRuntimeOffsetsInit(IpcTarget ipcTarget)
1711{
1712 CONTRACTL
1713 {
1714 SO_INTOLERANT;
1715 NOTHROW;
1716 GC_NOTRIGGER;
1717
1718 PRECONDITION(ThisMaybeHelperThread());
1719 }
1720 CONTRACTL_END;
1721
1722 HRESULT hr = S_OK;
1723
1724 if (m_rgfInitRuntimeOffsets[ipcTarget] == true)
1725 {
1726 hr = SetupRuntimeOffsets(m_pDCB);
1727 _ASSERTE(SUCCEEDED(hr)); // throws on failure
1728
1729 // RuntimeOffsets structure is setup.
1730 m_rgfInitRuntimeOffsets[ipcTarget] = false;
1731 }
1732
1733 return hr;
1734}
1735
1736//
1737// Call this function to tell the rc thread that we need the runtime offsets re-initialized at the next avaliable time.
1738//
1739void DebuggerRCThread::NeedRuntimeOffsetsReInit(IpcTarget i)
1740{
1741 LIMITED_METHOD_CONTRACT;
1742
1743 m_rgfInitRuntimeOffsets[i] = true;
1744}
1745
1746//---------------------------------------------------------------------------------------
1747//
1748// Send an debug event to the Debugger. This may be either a notification
1749// or a reply to a debugger query.
1750//
1751// Arguments:
1752// iTarget - which connection. This must be IPC_TARGET_OUTOFPROC.
1753//
1754// Return Value:
1755// S_OK on success
1756//
1757// Notes:
1758// SendIPCEvent is used by the Debugger object to send IPC events to
1759// the Debugger Interface. It waits for acknowledgement from the DI
1760// before returning.
1761//
1762// This assumes that the event send buffer has been properly
1763// filled in. All it does it wake up the DI and let it know that its
1764// safe to copy the event out of this process.
1765//
1766// This function may block indefinitely if the controlling debugger
1767// suddenly went away.
1768//
1769// @dbgtodo inspection - this is all a nop around SendRawEvent!
1770//
1771//---------------------------------------------------------------------------------------
1772HRESULT DebuggerRCThread::SendIPCEvent()
1773{
1774 CONTRACTL
1775 {
1776 SO_NOT_MAINLINE;
1777 NOTHROW;
1778 GC_NOTRIGGER; // duh, we're in preemptive..
1779
1780 if (m_debugger->m_isBlockedOnGarbageCollectionEvent)
1781 {
1782 //
1783 // If m_debugger->m_isBlockedOnGarbageCollectionEvent is true, then it must be reporting
1784 // either the BeforeGarbageCollection event or the AfterGarbageCollection event
1785 // The thread is in preemptive mode during BeforeGarbageCollection
1786 // The thread is in cooperative mode during AfterGarbageCollection
1787 // In either case, the thread mode doesn't really matter because GC has already taken control
1788 // of execution.
1789 //
1790 // Despite the fact that we are actually in preemptive mode during BeforeGarbageCollection,
1791 // because IsGCThread() is true, the EEContract::DoCheck() will happily accept the fact we are
1792 // testing for MODE_COOPERATIVE.
1793 //
1794 MODE_COOPERATIVE;
1795 }
1796 else
1797 {
1798 if (ThisIsHelperThreadWorker())
1799 {
1800 // When we're stopped, the helper could actually be contracted as either mode-cooperative
1801 // or mode-preemptive!
1802 // If we're the helper thread, we're only sending events while we're stopped.
1803 // Our callers will be mode-cooperative, so call this mode_cooperative to avoid a bunch
1804 // of unncessary contract violations.
1805 MODE_COOPERATIVE;
1806 }
1807 else
1808 {
1809 // Managed threads sending debug events should always be in preemptive mode.
1810 MODE_PREEMPTIVE;
1811 }
1812 }
1813 PRECONDITION(ThisMaybeHelperThread());
1814 }
1815 CONTRACTL_END;
1816
1817
1818 // one right side
1819 _ASSERTE(m_debugger->ThreadHoldsLock());
1820
1821 HRESULT hr = S_OK;
1822
1823 // All the initialization is already done in code:DebuggerRCThread.Init,
1824 // so we can just go ahead and send the event.
1825
1826 DebuggerIPCEvent* pManagedEvent = GetIPCEventSendBuffer();
1827
1828 STRESS_LOG2(LF_CORDB, LL_INFO1000, "D::SendIPCEvent %s to outofproc appD 0x%x,\n",
1829 IPCENames::GetName(pManagedEvent->type),
1830 VmPtrToCookie(pManagedEvent->vmAppDomain));
1831
1832 // increase the debug counter
1833 DbgLog((DebuggerIPCEventType)(pManagedEvent->type & DB_IPCE_TYPE_MASK));
1834
1835 g_pDebugger->SendRawEvent(pManagedEvent);
1836
1837 return hr;
1838}
1839
1840//
1841// Return true if the helper thread is up & running
1842//
1843bool DebuggerRCThread::IsRCThreadReady()
1844{
1845 LIMITED_METHOD_CONTRACT;
1846
1847 if (GetDCB() == NULL)
1848 {
1849 return false;
1850 }
1851
1852 int idHelper = GetDCB()->m_helperThreadId;
1853
1854 // The simplest check. If the threadid isn't set, we're not ready.
1855 if (idHelper == 0)
1856 {
1857 LOG((LF_CORDB, LL_EVERYTHING, "DRCT::IsReady - Helper not ready since DCB says id = 0.\n"));
1858 return false;
1859 }
1860
1861 // a more subtle check. It's possible the thread was up, but then
1862 // an bad call to ExitProcess suddenly terminated the helper thread,
1863 // leaving the threadid still non-0. So check the actual thread object
1864 // and make sure it's still around.
1865 int ret = WaitForSingleObject(m_thread, 0);
1866 LOG((LF_CORDB, LL_EVERYTHING, "DRCT::IsReady - wait(0x%x)=%d, GetLastError() = %d\n", m_thread, ret, GetLastError()));
1867
1868 if (ret != WAIT_TIMEOUT)
1869 {
1870 return false;
1871 }
1872
1873 return true;
1874}
1875
1876
1877HRESULT DebuggerRCThread::ReDaclEvents(PSECURITY_DESCRIPTOR pSecurityDescriptor)
1878{
1879 LIMITED_METHOD_CONTRACT;
1880
1881#ifndef FEATURE_PAL
1882 if (m_pDCB != NULL)
1883 {
1884 if (m_pDCB->m_rightSideEventAvailable)
1885 {
1886 if (SetKernelObjectSecurity(m_pDCB->m_rightSideEventAvailable,
1887 DACL_SECURITY_INFORMATION,
1888 pSecurityDescriptor) == 0)
1889 {
1890 // failed!
1891 return HRESULT_FROM_GetLastError();
1892 }
1893 }
1894 if (m_pDCB->m_rightSideEventRead)
1895 {
1896 if (SetKernelObjectSecurity(m_pDCB->m_rightSideEventRead,
1897 DACL_SECURITY_INFORMATION,
1898 pSecurityDescriptor) == 0)
1899 {
1900 // failed!
1901 return HRESULT_FROM_GetLastError();
1902 }
1903 }
1904 }
1905#endif // FEATURE_PAL
1906
1907 return S_OK;
1908}
1909
1910
1911//
1912// A normal thread may hit a stack overflow and so we want to do
1913// any stack-intensive work on the Helper thread so that we don't
1914// use up the grace memory.
1915// Note that DoFavor will block until the fp is executed
1916//
1917void DebuggerRCThread::DoFavor(FAVORCALLBACK fp, void * pData)
1918{
1919 CONTRACTL
1920 {
1921 SO_INTOLERANT;
1922 NOTHROW;
1923 GC_TRIGGERS;
1924
1925 PRECONDITION(!ThisIsHelperThreadWorker());
1926
1927#ifdef PREFAST
1928 // Prefast issue
1929 // error C2664: 'CHECK CheckPointer(TypeHandle,IsNullOK)' : cannot convert parameter 1 from
1930 // 'DebuggerRCThread::FAVORCALLBACK' to 'TypeHandle'
1931#else
1932 PRECONDITION(CheckPointer(fp));
1933 PRECONDITION(CheckPointer(pData, NULL_OK));
1934#endif
1935 }
1936 CONTRACTL_END;
1937
1938 // We are being called on managed thread only.
1939 //
1940
1941 // We'll have problems if another thread comes in and
1942 // deletes the RCThread object on us while we're in this call.
1943 if (IsRCThreadReady())
1944 {
1945 // If the helper thread calls this, we deadlock.
1946 // (Since we wait on an event that only the helper thread sets)
1947 _ASSERTE(GetRCThreadId() != GetCurrentThreadId());
1948
1949 // Only lock if we're waiting on the helper thread.
1950 // This should be the only place the FavorLock is used.
1951 // Note this is never called on the helper thread.
1952 CrstHolder ch(GetFavorLock());
1953
1954 SetFavorFnPtr(fp, pData);
1955
1956 // Our main message loop operating on the Helper thread will
1957 // pickup that event, call the fp, and set the Read event
1958 SetEvent(GetFavorAvailableEvent());
1959
1960 LOG((LF_CORDB, LL_INFO10000, "DRCT::DF - Waiting on FavorReadEvent for favor 0x%08x\n", fp));
1961
1962 // Wait for either the FavorEventRead to be set (which means that the favor
1963 // was executed by the helper thread) or the helper thread's handle (which means
1964 // that the helper thread exited without doing the favor, so we should do it)
1965 //
1966 // Note we are assuming that there's only 2 ways the helper thread can exit:
1967 // 1) Someone calls ::ExitProcess, killing all threads. That will kill us too, so we're "ok".
1968 // 2) Someone calls Stop(), causing the helper to exit gracefully. That's ok too. The helper
1969 // didn't execute the Favor (else the FREvent would have been set first) and so we can.
1970 //
1971 // Beware of problems:
1972 // 1) If the helper can block, we may deadlock.
1973 // 2) If the helper can exit magically (or if we change the Wait to include a timeout) ,
1974 // the helper thread may have not executed the favor, partially executed the favor,
1975 // or totally executed the favor but not yet signaled the FavorReadEvent. We don't
1976 // know what it did, so we don't know what we can do; so we're in an unstable state.
1977
1978 const HANDLE waitset [] = { GetFavorReadEvent(), m_thread };
1979
1980 // the favor worker thread will require a transition to cooperative mode in order to complete its work and we will
1981 // wait for the favor to complete before terminating the process. if there is a GC in progress the favor thread
1982 // will be blocked and if the thread requesting the favor is in cooperative mode we'll deadlock, so we switch to
1983 // preemptive mode before waiting for the favor to complete (see Dev11 72349).
1984 GCX_PREEMP();
1985
1986 DWORD ret = WaitForMultipleObjectsEx(
1987 NumItems(waitset),
1988 waitset,
1989 FALSE,
1990 INFINITE,
1991 FALSE
1992 );
1993
1994 DWORD wn = (ret - WAIT_OBJECT_0);
1995 if (wn == 0) // m_FavorEventRead
1996 {
1997 // Favor was executed, nothing to do here.
1998 LOG((LF_CORDB, LL_INFO10000, "DRCT::DF - favor 0x%08x finished, ret = %d\n", fp, ret));
1999 }
2000 else
2001 {
2002 LOG((LF_CORDB, LL_INFO10000, "DRCT::DF - lost helper thread during wait, "
2003 "doing favor 0x%08x on current thread\n", fp));
2004
2005 // Since we have no timeout, we shouldn't be able to get an error on the wait,
2006 // but just in case ...
2007 _ASSERTE(ret != WAIT_FAILED);
2008 _ASSERTE((wn == 1) && !"DoFavor - unexpected return from WFMO");
2009
2010 // Thread exited without doing favor, so execute it on our thread.
2011 // If we're here because of a stack overflow, this may push us over the edge,
2012 // but there's nothing else we can really do
2013 (*fp)(pData);
2014
2015 ResetEvent(GetFavorAvailableEvent());
2016 }
2017
2018 // m_fpFavor & m_pFavorData are meaningless now. We could set them
2019 // to NULL, but we may as well leave them as is to leave a trail.
2020
2021 }
2022 else
2023 {
2024 LOG((LF_CORDB, LL_INFO10000, "DRCT::DF - helper thread not ready, "
2025 "doing favor 0x%08x on current thread\n", fp));
2026 // If helper isn't ready yet, go ahead and execute the favor
2027 // on the callee's space
2028 (*fp)(pData);
2029 }
2030
2031 // Drop a log message so that we know if we survived a stack overflow or not
2032 LOG((LF_CORDB, LL_INFO10000, "DRCT::DF - Favor 0x%08x completed successfully\n", fp));
2033}
2034
2035
2036//
2037// SendIPCReply simply indicates to the Right Side that a reply to a
2038// two-way event is ready to be read and that the last event sent from
2039// the Right Side has been fully processed.
2040//
2041// NOTE: this assumes that the event receive buffer has been properly
2042// filled in. All it does it wake up the DI and let it know that its
2043// safe to copy the event out of this process.
2044//
2045HRESULT DebuggerRCThread::SendIPCReply()
2046{
2047 HRESULT hr = S_OK;
2048
2049#ifdef LOGGING
2050 DebuggerIPCEvent* event = GetIPCEventReceiveBuffer();
2051
2052 LOG((LF_CORDB, LL_INFO10000, "D::SIPCR: replying with %s.\n",
2053 IPCENames::GetName(event->type)));
2054#endif
2055
2056#if !defined(FEATURE_DBGIPC_TRANSPORT_VM)
2057 BOOL succ = SetEvent(m_pDCB->m_rightSideEventRead);
2058 if (!succ)
2059 {
2060 hr = CORDBDebuggerSetUnrecoverableWin32Error(m_debugger, 0, false);
2061 }
2062#else // !FEATURE_DBGIPC_TRANSPORT_VM
2063 hr = g_pDbgTransport->SendEvent(GetIPCEventReceiveBuffer());
2064 if (FAILED(hr))
2065 {
2066 m_debugger->UnrecoverableError(hr,
2067 0,
2068 __FILE__,
2069 __LINE__,
2070 false);
2071 }
2072#endif // !FEATURE_DBGIPC_TRANSPORT_VM
2073
2074 return hr;
2075}
2076
2077//
2078// EarlyHelperThreadDeath handles the case where the helper
2079// thread has been ripped out from underneath of us by
2080// ExitProcess or TerminateProcess. These calls are bad, whacking
2081// all threads except the caller in the process. This can happen, for
2082// instance, when an app calls ExitProcess. All threads are wacked,
2083// the main thread calls all DLL main's, and the EE starts shutting
2084// down in its DLL main with the helper thread terminated.
2085//
2086void DebuggerRCThread::EarlyHelperThreadDeath(void)
2087{
2088 LOG((LF_CORDB, LL_INFO10000, "DRCT::EHTD\n"));
2089
2090 // If we ever spun up a thread...
2091 if (m_thread != NULL && m_pDCB)
2092 {
2093 Debugger::DebuggerLockHolder debugLockHolder(m_debugger);
2094
2095 m_pDCB->m_helperThreadId = 0;
2096
2097 LOG((LF_CORDB, LL_INFO10000, "DRCT::EHTD helperThreadId\n"));
2098 // dbgLockHolder goes out of scope - implicit Release
2099 }
2100}
2101
2102