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 |
33 | EEThreadId DebuggerRCThread::s_DbgHelperThreadId; |
34 | #endif |
35 | |
36 | // |
37 | // Constructor |
38 | // |
39 | DebuggerRCThread::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 | // |
74 | DebuggerRCThread::~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 | //--------------------------------------------------------------------------------------- |
101 | void 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 | //----------------------------------------------------------------------------- |
125 | void 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 | //----------------------------------------------------------------------------- |
148 | HANDLE 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 | //----------------------------------------------------------------------------- |
175 | HANDLE 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 | //--------------------------------------------------------------------------------------- |
224 | HRESULT 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 | |
297 | void 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 | //--------------------------------------------------------------------------------------- |
321 | HRESULT 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 | // |
485 | HRESULT 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 | //--------------------------------------------------------------------------------------- |
655 | HRESULT 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 | |
740 | struct DebugFilterParam |
741 | { |
742 | DebuggerIPCEvent *event; |
743 | }; |
744 | |
745 | // Filter called when we throw an exception while Handling events. |
746 | static 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 | |
815 | void 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 | //--------------------------------------------------------------------------------------- |
855 | void 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 | |
994 | void 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 | // |
1023 | bool 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, ¶m) |
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 | |
1046 | bool 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 | //--------------------------------------------------------------------------------------- |
1111 | void 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 | |
1277 | LWaitTimedOut: |
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 | //--------------------------------------------------------------------------------------- |
1362 | void 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 | |
1475 | LWaitTimedOut: |
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 | |
1513 | LExit: |
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 | |
1587 | RCThreadLazyInit * 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 | // |
1598 | HRESULT 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 | //--------------------------------------------------------------------------------------- |
1677 | HRESULT 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 | //--------------------------------------------------------------------------------------- |
1710 | HRESULT 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 | // |
1739 | void 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 | //--------------------------------------------------------------------------------------- |
1772 | HRESULT 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 | // |
1843 | bool 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 | |
1877 | HRESULT 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 | // |
1917 | void 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 | // |
2045 | HRESULT 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 | // |
2086 | void 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 | |