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: RsMain.cpp |
6 | // |
7 | |
8 | // Random RS utility stuff, plus root ICorCordbug implementation |
9 | // |
10 | //***************************************************************************** |
11 | #include "stdafx.h" |
12 | #include "primitives.h" |
13 | #include "safewrap.h" |
14 | |
15 | #include "check.h" |
16 | |
17 | #include <tlhelp32.h> |
18 | #include "wtsapi32.h" |
19 | |
20 | #ifndef SM_REMOTESESSION |
21 | #define SM_REMOTESESSION 0x1000 |
22 | #endif |
23 | |
24 | #include "corpriv.h" |
25 | #include "../../dlls/mscorrc/resource.h" |
26 | #include <limits.h> |
27 | |
28 | |
29 | // The top level Cordb object is built around the Shim |
30 | #include "shimpriv.h" |
31 | |
32 | //----------------------------------------------------------------------------- |
33 | // For debugging ease, cache some global values. |
34 | // Include these in retail & free because that's where we need them the most!! |
35 | // Optimized builds may not let us view locals & parameters. So Having these |
36 | // cached as global values should let us inspect almost all of |
37 | // the interesting parts of the RS even in a Retail build! |
38 | //----------------------------------------------------------------------------- |
39 | |
40 | RSDebuggingInfo g_RSDebuggingInfo_OutOfProc = {0 }; // set to NULL |
41 | RSDebuggingInfo * g_pRSDebuggingInfo = &g_RSDebuggingInfo_OutOfProc; |
42 | |
43 | // The following instances are used for invoking overloaded new/delete |
44 | forDbiWorker forDbi; |
45 | |
46 | #ifdef _DEBUG |
47 | // For logs, we can print the string name for the debug codes. |
48 | const char * GetDebugCodeName(DWORD dwCode) |
49 | { |
50 | if (dwCode < 1 || dwCode > 9) |
51 | { |
52 | return "!Invalid Debug Event Code!" ; |
53 | } |
54 | |
55 | static const char * const szNames[] = { |
56 | "(1) EXCEPTION_DEBUG_EVENT" , |
57 | "(2) CREATE_THREAD_DEBUG_EVENT" , |
58 | "(3) CREATE_PROCESS_DEBUG_EVENT" , |
59 | "(4) EXIT_THREAD_DEBUG_EVENT" , |
60 | "(5) EXIT_PROCESS_DEBUG_EVENT" , |
61 | "(6) LOAD_DLL_DEBUG_EVENT" , |
62 | "(7) UNLOAD_DLL_DEBUG_EVENT" , |
63 | "(8) OUTPUT_DEBUG_STRING_EVENT" , |
64 | "(9) RIP_EVENT" ,// <-- only on Win9X |
65 | }; |
66 | |
67 | return szNames[dwCode - 1]; |
68 | } |
69 | |
70 | #endif |
71 | |
72 | |
73 | //----------------------------------------------------------------------------- |
74 | // Per-thread state for Debug builds... |
75 | //----------------------------------------------------------------------------- |
76 | #ifdef RSCONTRACTS |
77 | DWORD DbgRSThread::s_TlsSlot = TLS_OUT_OF_INDEXES; |
78 | LONG DbgRSThread::s_Total = 0; |
79 | |
80 | DbgRSThread::DbgRSThread() |
81 | { |
82 | m_cInsideRS = 0; |
83 | m_fIsInCallback = false; |
84 | m_fIsUnrecoverableErrorCallback = false; |
85 | |
86 | m_cTotalDbgApiLocks = 0; |
87 | for(int i = 0; i < RSLock::LL_MAX; i++) |
88 | { |
89 | m_cLocks[i] = 0; |
90 | } |
91 | |
92 | // Initialize Identity info |
93 | m_Cookie = COOKIE_VALUE; |
94 | m_tid = GetCurrentThreadId(); |
95 | } |
96 | |
97 | // NotifyTakeLock & NotifyReleaseLock are called by RSLock to update the per-thread locking context. |
98 | // This will assert if the operation is unsafe (ie, violates lock order). |
99 | void DbgRSThread::NotifyTakeLock(RSLock * pLock) |
100 | { |
101 | if (pLock->HasLock()) |
102 | { |
103 | return; |
104 | } |
105 | |
106 | int iLevel = pLock->GetLevel(); |
107 | |
108 | // Is it safe to take this lock? |
109 | // Must take "bigger" locks first. We shouldn't hold any locks at our current level either. |
110 | // If this lock is re-entrant and we're double-taking it, we would have returned already. |
111 | // And the locking model on the RS forbids taking multiple locks at the same level. |
112 | for(int i = iLevel; i >= 0; i --) |
113 | { |
114 | bool fHasLowerLock = m_cLocks[i] > 0; |
115 | CONSISTENCY_CHECK_MSGF(!fHasLowerLock, ( |
116 | "RSLock violation. Trying to take lock '%s (%d)', but already have smaller lock at level %d'\n" , |
117 | pLock->Name(), iLevel, |
118 | i)); |
119 | } |
120 | |
121 | // Update the counts |
122 | _ASSERTE(m_cLocks[iLevel] == 0); |
123 | m_cLocks[iLevel]++; |
124 | |
125 | if (pLock->IsDbgApiLock()) |
126 | m_cTotalDbgApiLocks++; |
127 | } |
128 | |
129 | void DbgRSThread::NotifyReleaseLock(RSLock * pLock) |
130 | { |
131 | if (pLock->HasLock()) |
132 | { |
133 | return; |
134 | } |
135 | |
136 | int iLevel = pLock->GetLevel(); |
137 | m_cLocks[iLevel]--; |
138 | _ASSERTE(m_cLocks[iLevel] == 0); |
139 | |
140 | if (pLock->IsDbgApiLock()) |
141 | m_cTotalDbgApiLocks--; |
142 | |
143 | _ASSERTE(m_cTotalDbgApiLocks >= 0); |
144 | } |
145 | |
146 | void DbgRSThread::TakeVirtualLock(RSLock::ERSLockLevel level) |
147 | { |
148 | m_cLocks[level]++; |
149 | } |
150 | |
151 | void DbgRSThread::ReleaseVirtualLock(RSLock::ERSLockLevel level) |
152 | { |
153 | m_cLocks[level]--; |
154 | _ASSERTE(m_cLocks[level] >= 0); |
155 | } |
156 | |
157 | |
158 | // Get a DbgRSThread for the current OS thread id; lazily create if needed. |
159 | DbgRSThread * DbgRSThread::GetThread() |
160 | { |
161 | _ASSERTE(DbgRSThread::s_TlsSlot != TLS_OUT_OF_INDEXES); |
162 | |
163 | void * p2 = TlsGetValue(DbgRSThread::s_TlsSlot); |
164 | if (p2 == NULL) |
165 | { |
166 | // We lazily create for threads that haven't gone through DllMain |
167 | // Since this is per-thread, we don't need to lock. |
168 | p2 = DbgRSThread::Create(); |
169 | } |
170 | DbgRSThread * p = reinterpret_cast<DbgRSThread*> (p2); |
171 | |
172 | _ASSERTE(p->m_Cookie == COOKIE_VALUE); |
173 | |
174 | return p; |
175 | } |
176 | |
177 | |
178 | |
179 | #endif // RSCONTRACTS |
180 | |
181 | |
182 | |
183 | |
184 | |
185 | |
186 | #ifdef _DEBUG |
187 | LONG CordbCommonBase::s_TotalObjectCount = 0; |
188 | LONG CordbCommonBase::s_CordbObjectUID = 0; |
189 | |
190 | |
191 | LONG CordbCommonBase::m_saDwInstance[enumMaxDerived]; |
192 | LONG CordbCommonBase::m_saDwAlive[enumMaxDerived]; |
193 | PVOID CordbCommonBase::m_sdThis[enumMaxDerived][enumMaxThis]; |
194 | |
195 | #endif |
196 | |
197 | #ifdef _DEBUG_IMPL |
198 | // Mem tracking |
199 | LONG Cordb::s_DbgMemTotalOutstandingCordb = 0; |
200 | LONG Cordb::s_DbgMemTotalOutstandingInternalRefs = 0; |
201 | #endif |
202 | |
203 | #ifdef TRACK_OUTSTANDING_OBJECTS |
204 | void *Cordb::s_DbgMemOutstandingObjects[MAX_TRACKED_OUTSTANDING_OBJECTS] = { NULL }; |
205 | LONG Cordb::s_DbgMemOutstandingObjectMax = 0; |
206 | #endif |
207 | |
208 | // Default implementation for neutering left-side resources. |
209 | void CordbBase::NeuterLeftSideResources() |
210 | { |
211 | LIMITED_METHOD_CONTRACT; |
212 | |
213 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
214 | Neuter(); |
215 | } |
216 | |
217 | // Default implementation for neutering. |
218 | // All derived objects should eventually chain to this. |
219 | void CordbBase::Neuter() |
220 | { |
221 | // Neutering occurs under the process lock. Neuter can be called twice |
222 | // and so locking protects against races in double-delete. |
223 | // @dbgtodo - , some CordbBase objects (Cordb, CordbProcessEnum), |
224 | // don't have process affinity these should eventually be hoisted to the shim, |
225 | // and then we can enforce. |
226 | CordbProcess * pProcess = GetProcess(); |
227 | if (pProcess != NULL) |
228 | { |
229 | _ASSERTE(pProcess->ThreadHoldsProcessLock()); |
230 | } |
231 | CordbCommonBase::Neuter(); |
232 | } |
233 | |
234 | //----------------------------------------------------------------------------- |
235 | // NeuterLists |
236 | //----------------------------------------------------------------------------- |
237 | |
238 | NeuterList::NeuterList() |
239 | { |
240 | m_pHead = NULL; |
241 | } |
242 | |
243 | NeuterList::~NeuterList() |
244 | { |
245 | // Our owner should have neutered us before deleting us. |
246 | // Thus we should be empty. |
247 | CONSISTENCY_CHECK_MSGF(m_pHead == NULL, ("NeuterList not empty on shutdown. this=0x%p" , this)); |
248 | } |
249 | |
250 | // Wrapper around code:NeuterList::UnsafeAdd |
251 | void NeuterList::Add(CordbProcess * pProcess, CordbBase * pObject) |
252 | { |
253 | CONTRACTL |
254 | { |
255 | THROWS; |
256 | } |
257 | CONTRACTL_END; |
258 | |
259 | UnsafeAdd(pProcess, pObject); |
260 | } |
261 | |
262 | // |
263 | // Add an object to be neutered. |
264 | // |
265 | // Arguments: |
266 | // pProcess - process that holds lock that will protect the neuter list |
267 | // pObject - object to add |
268 | // |
269 | // Returns: |
270 | // Throws on error. |
271 | // |
272 | // Notes: |
273 | // This will add it to the list and maintain an internal reference to it. |
274 | // This will take the process lock. |
275 | // |
276 | void NeuterList::UnsafeAdd(CordbProcess * pProcess, CordbBase * pObject) |
277 | { |
278 | _ASSERTE(pObject != NULL); |
279 | |
280 | // Lock if needed. |
281 | RSLock * pLock = (pProcess != NULL) ? pProcess->GetProcessLock() : NULL; |
282 | RSLockHolder lockHolder(pLock, FALSE); |
283 | if (pLock != NULL) lockHolder.Acquire(); |
284 | |
285 | |
286 | Node * pNode = new Node(); // throws on error. |
287 | pNode->m_pObject.Assign(pObject); |
288 | pNode->m_pNext = m_pHead; |
289 | |
290 | m_pHead = pNode; |
291 | } |
292 | |
293 | // Neuter everything on the list and clear it |
294 | // |
295 | // Arguments: |
296 | // pProcess - process tree that this neuterlist belongs in |
297 | // ticket - neuter ticket proving caller ensured we're safe to neuter. |
298 | // |
299 | // Assumptions: |
300 | // Caller ensures we're safe to neuter (required to obtain NeuterTicket) |
301 | // |
302 | // Notes: |
303 | // This will release all internal references and empty the list. |
304 | void NeuterList::NeuterAndClear(CordbProcess * pProcess) |
305 | { |
306 | RSLock * pLock = (pProcess != NULL) ? pProcess->GetProcessLock() : NULL; |
307 | (void)pLock; //prevent "unused variable" error from GCC |
308 | _ASSERTE((pLock == NULL) || pLock->HasLock()); |
309 | |
310 | while (m_pHead != NULL) |
311 | { |
312 | Node * pTemp = m_pHead; |
313 | m_pHead = m_pHead->m_pNext; |
314 | |
315 | pTemp->m_pObject->Neuter(); |
316 | delete pTemp; // will implicitly release |
317 | } |
318 | } |
319 | |
320 | // Only neuter objects that are marked. |
321 | // Removes neutered objects from the list. |
322 | void NeuterList::SweepAllNeuterAtWillObjects(CordbProcess * pProcess) |
323 | { |
324 | _ASSERTE(pProcess != NULL); |
325 | RSLock * pLock = pProcess->GetProcessLock(); |
326 | RSLockHolder lockHolder(pLock); |
327 | |
328 | Node ** ppLast = &m_pHead; |
329 | Node * pCur = m_pHead; |
330 | |
331 | while (pCur != NULL) |
332 | { |
333 | CordbBase * pObject = pCur->m_pObject; |
334 | if (pObject->IsNeuterAtWill() || pObject->IsNeutered()) |
335 | { |
336 | // Delete |
337 | pObject->Neuter(); |
338 | |
339 | Node * pNext = pCur->m_pNext; |
340 | delete pCur; // dtor will implicitly release the internal ref to pObject |
341 | pCur = *ppLast = pNext; |
342 | } |
343 | else |
344 | { |
345 | // Move to next. |
346 | ppLast = &pCur->m_pNext; |
347 | pCur = pCur->m_pNext; |
348 | } |
349 | } |
350 | } |
351 | |
352 | //----------------------------------------------------------------------------- |
353 | // Neuters all objects in the list and empties the list. |
354 | // |
355 | // Notes: |
356 | // See also code:LeftSideResourceCleanupList::SweepNeuterLeftSideResources, |
357 | // which only neuters objects that have been marked as NeuterAtWill (external |
358 | // ref count has gone to 0). |
359 | void LeftSideResourceCleanupList::NeuterLeftSideResourcesAndClear(CordbProcess * pProcess) |
360 | { |
361 | // Traversal protected under Process-lock. |
362 | // SG-lock must already be held to do neutering. |
363 | // Stop-Go lock is bigger than Process-lock. |
364 | // Neutering requires the Stop-Go lock (until we get rid of IPC events) |
365 | // But we want to be able to add to the Neuter list under the Process-lock. |
366 | // So we just need to protected m_pHead under process-lock. |
367 | |
368 | // "Privatize" the list under the lock. |
369 | _ASSERTE(pProcess != NULL); |
370 | RSLock * pLock = pProcess->GetProcessLock(); |
371 | |
372 | Node * pCur = NULL; |
373 | { |
374 | RSLockHolder lockHolder(pLock); // only acquire lock if we have one |
375 | pCur = m_pHead; |
376 | m_pHead = NULL; |
377 | } |
378 | |
379 | // @dbgtodo - eventually everything can be under the process lock. |
380 | _ASSERTE(!pLock->HasLock()); // Can't hold Process lock while calling NeuterLeftSideResources |
381 | |
382 | // Now we're operating on local data, so traversing doesn't need to be under the lock. |
383 | while (pCur != NULL) |
384 | { |
385 | Node * pTemp = pCur; |
386 | pCur = pCur->m_pNext; |
387 | |
388 | pTemp->m_pObject->NeuterLeftSideResources(); |
389 | delete pTemp; // will implicitly release |
390 | } |
391 | |
392 | } |
393 | |
394 | //----------------------------------------------------------------------------- |
395 | // Only neuter objects that are marked. Removes neutered objects from the list. |
396 | // |
397 | // Arguments: |
398 | // pProcess - non-null process owning the objects in the list |
399 | // |
400 | // Notes: |
401 | // this cleans up left-side resources held by objects in the list. |
402 | // It may send IPC events to do this. |
403 | void LeftSideResourceCleanupList::SweepNeuterLeftSideResources(CordbProcess * pProcess) |
404 | { |
405 | _ASSERTE(pProcess != NULL); |
406 | |
407 | // Must be safe to send IPC events. |
408 | _ASSERTE(pProcess->GetStopGoLock()->HasLock()); // holds this for neutering |
409 | _ASSERTE(pProcess->GetSynchronized()); |
410 | |
411 | RSLock * pLock = pProcess->GetProcessLock(); |
412 | |
413 | // Lock while we "privatize" the head. |
414 | RSLockHolder lockHolder(pLock); |
415 | Node * pHead = m_pHead; |
416 | m_pHead = NULL; |
417 | lockHolder.Release(); |
418 | |
419 | Node ** ppLast = &pHead; |
420 | Node * pCur = pHead; |
421 | |
422 | // Can't hold the process-lock while calling Neuter. |
423 | while (pCur != NULL) |
424 | { |
425 | CordbBase * pObject = pCur->m_pObject; |
426 | if (pObject->IsNeuterAtWill() || pObject->IsNeutered()) |
427 | { |
428 | // HeavyNueter can not be done under the process-lock because |
429 | // it may take the Stop-Go lock and send events. |
430 | pObject->NeuterLeftSideResources(); |
431 | |
432 | // Delete |
433 | Node * pNext = pCur->m_pNext; |
434 | delete pCur; // dtor will implicitly release the internal ref to pObject |
435 | pCur = *ppLast = pNext; |
436 | } |
437 | else |
438 | { |
439 | // Move to next. |
440 | ppLast = &pCur->m_pNext; |
441 | pCur = pCur->m_pNext; |
442 | } |
443 | } |
444 | |
445 | // Now link back in. m_pHead may have changed while we were unlocked. |
446 | // The list does not need to be ordered. |
447 | |
448 | lockHolder.Acquire(); |
449 | *ppLast = m_pHead; |
450 | m_pHead = pHead; |
451 | } |
452 | |
453 | |
454 | |
455 | /* ------------------------------------------------------------------------- * |
456 | * CordbBase class |
457 | * ------------------------------------------------------------------------- */ |
458 | |
459 | // Do any initialization necessary for both CorPublish and CorDebug |
460 | // This includes enabling logging and adding the SEDebug priv. |
461 | void CordbCommonBase::InitializeCommon() |
462 | { |
463 | static bool IsInitialized = false; |
464 | if( IsInitialized ) |
465 | { |
466 | return; |
467 | } |
468 | |
469 | #ifdef STRESS_LOG |
470 | { |
471 | bool fStressLog = false; |
472 | |
473 | #ifdef _DEBUG |
474 | // default for stress log is on debug build |
475 | fStressLog = true; |
476 | #endif // DEBUG |
477 | |
478 | // StressLog will turn on stress logging for the entire runtime. |
479 | // RSStressLog is only used here and only effects just the RS. |
480 | fStressLog = |
481 | (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLog, fStressLog) != 0) || |
482 | (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_RSStressLog) != 0); |
483 | |
484 | if (fStressLog == true) |
485 | { |
486 | unsigned facilities = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility, LF_ALL); |
487 | unsigned level = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_LogLevel, LL_INFO1000); |
488 | unsigned bytesPerThread = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLogSize, STRESSLOG_CHUNK_SIZE * 2); |
489 | unsigned totalBytes = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_TotalStressLogSize, STRESSLOG_CHUNK_SIZE * 1024); |
490 | #ifndef FEATURE_PAL |
491 | StressLog::Initialize(facilities, level, bytesPerThread, totalBytes, GetModuleInst()); |
492 | #else |
493 | StressLog::Initialize(facilities, level, bytesPerThread, totalBytes, NULL); |
494 | #endif |
495 | } |
496 | } |
497 | |
498 | #endif // STRESS_LOG |
499 | |
500 | #ifdef LOGGING |
501 | InitializeLogging(); |
502 | #endif |
503 | |
504 | // Add debug privilege. This will let us call OpenProcess() on anything, regardless of ACL. |
505 | AddDebugPrivilege(); |
506 | |
507 | IsInitialized = true; |
508 | } |
509 | |
510 | // Adjust the permissions of this process to ensure that we have |
511 | // the debugging priviledge. If we can't make the adjustment, it |
512 | // only means that we won't be able to attach to a service under |
513 | // NT, so we won't treat that as a critical failure. |
514 | // This also will let us call OpenProcess() on anything, regardless of DACL. This allows an |
515 | // Admin debugger to attach to a debuggee in the guest account. |
516 | // Ideally, the debugger would set this (and we wouldn't mess with privileges at all). However, we've been |
517 | // setting this since V1.0 and removing it may be a breaking change. |
518 | void CordbCommonBase::AddDebugPrivilege() |
519 | { |
520 | #ifndef FEATURE_PAL |
521 | HANDLE hToken; |
522 | TOKEN_PRIVILEGES Privileges; |
523 | BOOL fSucc; |
524 | |
525 | LUID SeDebugLuid = {0, 0}; |
526 | |
527 | fSucc = LookupPrivilegeValueW(NULL, SE_DEBUG_NAME, &SeDebugLuid); |
528 | DWORD err = GetLastError(); |
529 | |
530 | if (!fSucc) |
531 | { |
532 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "Unable to adjust permissions of this process to include SE_DEBUG. Lookup failed %d\n" , err); |
533 | return; |
534 | } |
535 | |
536 | |
537 | // Retrieve a handle of the access token |
538 | fSucc = OpenProcessToken(GetCurrentProcess(), |
539 | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, |
540 | &hToken); |
541 | |
542 | if (fSucc) |
543 | { |
544 | Privileges.PrivilegeCount = 1; |
545 | Privileges.Privileges[0].Luid = SeDebugLuid; |
546 | Privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; |
547 | |
548 | AdjustTokenPrivileges(hToken, |
549 | FALSE, |
550 | &Privileges, |
551 | sizeof(TOKEN_PRIVILEGES), |
552 | (PTOKEN_PRIVILEGES) NULL, |
553 | (PDWORD) NULL); |
554 | err = GetLastError(); |
555 | // The return value of AdjustTokenPrivileges cannot be tested. |
556 | if (err != ERROR_SUCCESS) |
557 | { |
558 | STRESS_LOG1(LF_CORDB, LL_INFO1000, |
559 | "Unable to adjust permissions of this process to include SE_DEBUG. Adjust failed %d\n" , err); |
560 | } |
561 | else |
562 | { |
563 | LOG((LF_CORDB, LL_INFO1000, "Adjusted process permissions to include SE_DEBUG.\n" )); |
564 | } |
565 | CloseHandle(hToken); |
566 | } |
567 | #endif |
568 | } |
569 | |
570 | |
571 | namespace |
572 | { |
573 | |
574 | // |
575 | // DefaultManagedCallback2 |
576 | // |
577 | // In the event that the debugger is of an older version than the Right Side & Left Side, the Right Side may issue |
578 | // new callbacks that the debugger is not expecting. In this case, we need to provide a default behavior for those |
579 | // new callbacks, if for nothing else than to force the debugger to Continue(). |
580 | // |
581 | class DefaultManagedCallback2 : public ICorDebugManagedCallback2 |
582 | { |
583 | public: |
584 | DefaultManagedCallback2(ICorDebug* pDebug); |
585 | virtual ~DefaultManagedCallback2() { } |
586 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** pInterface); |
587 | virtual ULONG STDMETHODCALLTYPE AddRef(); |
588 | virtual ULONG STDMETHODCALLTYPE Release(); |
589 | COM_METHOD FunctionRemapOpportunity(ICorDebugAppDomain* pAppDomain, |
590 | ICorDebugThread* pThread, |
591 | ICorDebugFunction* pOldFunction, |
592 | ICorDebugFunction* pNewFunction, |
593 | ULONG32 oldILOffset); |
594 | COM_METHOD FunctionRemapComplete(ICorDebugAppDomain *pAppDomain, |
595 | ICorDebugThread *pThread, |
596 | ICorDebugFunction *pFunction); |
597 | |
598 | COM_METHOD CreateConnection(ICorDebugProcess *pProcess, |
599 | CONNID dwConnectionId, |
600 | __in_z WCHAR* pConnectionName); |
601 | COM_METHOD ChangeConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId); |
602 | COM_METHOD DestroyConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId); |
603 | |
604 | COM_METHOD Exception(ICorDebugAppDomain *pAddDomain, |
605 | ICorDebugThread *pThread, |
606 | ICorDebugFrame *pFrame, |
607 | ULONG32 nOffset, |
608 | CorDebugExceptionCallbackType eventType, |
609 | DWORD dwFlags ); |
610 | |
611 | COM_METHOD ExceptionUnwind(ICorDebugAppDomain *pAddDomain, |
612 | ICorDebugThread *pThread, |
613 | CorDebugExceptionUnwindCallbackType eventType, |
614 | DWORD dwFlags ); |
615 | COM_METHOD MDANotification( |
616 | ICorDebugController * pController, |
617 | ICorDebugThread *pThread, |
618 | ICorDebugMDA * pMDA |
619 | ) { return E_NOTIMPL; } |
620 | |
621 | private: |
622 | // not implemented |
623 | DefaultManagedCallback2(const DefaultManagedCallback2&); |
624 | DefaultManagedCallback2& operator=(const DefaultManagedCallback2&); |
625 | |
626 | ICorDebug* m_pDebug; |
627 | LONG m_refCount; |
628 | }; |
629 | |
630 | |
631 | |
632 | |
633 | DefaultManagedCallback2::DefaultManagedCallback2(ICorDebug* pDebug) : m_pDebug(pDebug), m_refCount(0) |
634 | { |
635 | } |
636 | |
637 | HRESULT |
638 | DefaultManagedCallback2::QueryInterface(REFIID iid, void** pInterface) |
639 | { |
640 | if (IID_ICorDebugManagedCallback2 == iid) |
641 | { |
642 | *pInterface = static_cast<ICorDebugManagedCallback2*>(this); |
643 | } |
644 | else if (IID_IUnknown == iid) |
645 | { |
646 | *pInterface = static_cast<IUnknown*>(this); |
647 | } |
648 | else |
649 | { |
650 | *pInterface = NULL; |
651 | return E_NOINTERFACE; |
652 | } |
653 | |
654 | this->AddRef(); |
655 | return S_OK; |
656 | } |
657 | |
658 | ULONG |
659 | DefaultManagedCallback2::AddRef() |
660 | { |
661 | return InterlockedIncrement(&m_refCount); |
662 | } |
663 | |
664 | ULONG |
665 | DefaultManagedCallback2::Release() |
666 | { |
667 | ULONG ulRef = InterlockedDecrement(&m_refCount); |
668 | if (0 == ulRef) |
669 | { |
670 | delete this; |
671 | } |
672 | |
673 | return ulRef; |
674 | } |
675 | |
676 | HRESULT |
677 | DefaultManagedCallback2::FunctionRemapOpportunity(ICorDebugAppDomain* pAppDomain, |
678 | ICorDebugThread* pThread, |
679 | ICorDebugFunction* pOldFunction, |
680 | ICorDebugFunction* pNewFunction, |
681 | ULONG32 oldILOffset) |
682 | { |
683 | |
684 | // |
685 | // In theory, this function should never be reached. To get here, we'd have to have a debugger which doesn't |
686 | // support edit and continue somehow turn on edit & continue features. |
687 | // |
688 | _ASSERTE(!"Edit & Continue callback reached when debugger doesn't support Edit And Continue" ); |
689 | |
690 | |
691 | // If you ignore this assertion, or you're in a retail build, there are two options as far as how to proceed |
692 | // from this point |
693 | // o We can do nothing, and let the debugee process hang, or |
694 | // o We can silently ignore the FunctionRemapOpportunity, and tell the debugee to Continue running. |
695 | // |
696 | // For now, we'll silently ignore the function remapping. |
697 | pAppDomain->Continue(false); |
698 | pAppDomain->Release(); |
699 | |
700 | return S_OK; |
701 | } |
702 | |
703 | |
704 | HRESULT |
705 | DefaultManagedCallback2::FunctionRemapComplete(ICorDebugAppDomain *pAppDomain, |
706 | ICorDebugThread *pThread, |
707 | ICorDebugFunction *pFunction) |
708 | { |
709 | // |
710 | // In theory, this function should never be reached. To get here, we'd have to have a debugger which doesn't |
711 | // support edit and continue somehow turn on edit & continue features. |
712 | // |
713 | _ASSERTE(!"Edit & Continue callback reached when debugger doesn't support Edit And Continue" ); |
714 | return E_NOTIMPL; |
715 | } |
716 | |
717 | // |
718 | // <TODO> |
719 | // These methods are current left unimplemented. |
720 | // |
721 | // Create/Change/Destroy Connection *should* force the Process/AppDomain/Thread to Continue(). Currently the |
722 | // arguments to these functions don't provide the relevant Process/AppDomain/Thread, so there is no way to figure |
723 | // out which Threads should be forced to Continue(). |
724 | // |
725 | // </TODO> |
726 | // |
727 | HRESULT |
728 | DefaultManagedCallback2::CreateConnection(ICorDebugProcess *pProcess, |
729 | CONNID dwConnectionId, |
730 | __in_z WCHAR* pConnectionName) |
731 | { |
732 | _ASSERTE(!"DefaultManagedCallback2::CreateConnection not implemented" ); |
733 | return E_NOTIMPL; |
734 | } |
735 | |
736 | HRESULT |
737 | DefaultManagedCallback2::ChangeConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId) |
738 | { |
739 | _ASSERTE(!"DefaultManagedCallback2::ChangeConnection not implemented" ); |
740 | return E_NOTIMPL; |
741 | } |
742 | |
743 | HRESULT |
744 | DefaultManagedCallback2::DestroyConnection(ICorDebugProcess *pProcess, CONNID dwConnectionId) |
745 | { |
746 | _ASSERTE(!"DefaultManagedCallback2::DestroyConnection not implemented" ); |
747 | return E_NOTIMPL; |
748 | } |
749 | |
750 | HRESULT |
751 | DefaultManagedCallback2::Exception(ICorDebugAppDomain *pAppDomain, |
752 | ICorDebugThread *pThread, |
753 | ICorDebugFrame *pFrame, |
754 | ULONG32 nOffset, |
755 | CorDebugExceptionCallbackType eventType, |
756 | DWORD dwFlags ) |
757 | { |
758 | // |
759 | // Just ignore and continue the process. |
760 | // |
761 | pAppDomain->Continue(false); |
762 | return S_OK; |
763 | } |
764 | |
765 | HRESULT |
766 | DefaultManagedCallback2::ExceptionUnwind(ICorDebugAppDomain *pAppDomain, |
767 | ICorDebugThread *pThread, |
768 | CorDebugExceptionUnwindCallbackType eventType, |
769 | DWORD dwFlags ) |
770 | { |
771 | // |
772 | // Just ignore and continue the process. |
773 | // |
774 | pAppDomain->Continue(false); |
775 | return S_OK; |
776 | } |
777 | |
778 | // |
779 | // DefaultManagedCallback3 |
780 | // |
781 | // In the event that the debugger is of an older version than the Right Side & Left Side, the Right Side may issue |
782 | // new callbacks that the debugger is not expecting. In this case, we need to provide a default behavior for those |
783 | // new callbacks, if for nothing else than to force the debugger to Continue(). |
784 | // |
785 | class DefaultManagedCallback3 : public ICorDebugManagedCallback3 |
786 | { |
787 | public: |
788 | DefaultManagedCallback3(ICorDebug* pDebug); |
789 | virtual ~DefaultManagedCallback3() { } |
790 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** pInterface); |
791 | virtual ULONG STDMETHODCALLTYPE AddRef(); |
792 | virtual ULONG STDMETHODCALLTYPE Release(); |
793 | COM_METHOD CustomNotification(ICorDebugThread * pThread, ICorDebugAppDomain * pAppDomain); |
794 | private: |
795 | // not implemented |
796 | DefaultManagedCallback3(const DefaultManagedCallback3&); |
797 | DefaultManagedCallback3& operator=(const DefaultManagedCallback3&); |
798 | |
799 | ICorDebug* m_pDebug; |
800 | LONG m_refCount; |
801 | }; |
802 | |
803 | DefaultManagedCallback3::DefaultManagedCallback3(ICorDebug* pDebug) : m_pDebug(pDebug), m_refCount(0) |
804 | { |
805 | } |
806 | |
807 | HRESULT |
808 | DefaultManagedCallback3::QueryInterface(REFIID iid, void** pInterface) |
809 | { |
810 | if (IID_ICorDebugManagedCallback3 == iid) |
811 | { |
812 | *pInterface = static_cast<ICorDebugManagedCallback3*>(this); |
813 | } |
814 | else if (IID_IUnknown == iid) |
815 | { |
816 | *pInterface = static_cast<IUnknown*>(this); |
817 | } |
818 | else |
819 | { |
820 | *pInterface = NULL; |
821 | return E_NOINTERFACE; |
822 | } |
823 | |
824 | this->AddRef(); |
825 | return S_OK; |
826 | } |
827 | |
828 | ULONG |
829 | DefaultManagedCallback3::AddRef() |
830 | { |
831 | return InterlockedIncrement(&m_refCount); |
832 | } |
833 | |
834 | ULONG |
835 | DefaultManagedCallback3::Release() |
836 | { |
837 | ULONG ulRef = InterlockedDecrement(&m_refCount); |
838 | if (0 == ulRef) |
839 | { |
840 | delete this; |
841 | } |
842 | |
843 | return ulRef; |
844 | } |
845 | |
846 | HRESULT |
847 | DefaultManagedCallback3::CustomNotification(ICorDebugThread * pThread, ICorDebugAppDomain * pAppDomain) |
848 | { |
849 | // |
850 | // Just ignore and continue the process. |
851 | // |
852 | pAppDomain->Continue(false); |
853 | return S_OK; |
854 | } |
855 | |
856 | // |
857 | // DefaultManagedCallback4 |
858 | // |
859 | // In the event that the debugger is of an older version than the Right Side & Left Side, the Right Side may issue |
860 | // new callbacks that the debugger is not expecting. In this case, we need to provide a default behavior for those |
861 | // new callbacks, if for nothing else than to force the debugger to Continue(). |
862 | // |
863 | class DefaultManagedCallback4 : public ICorDebugManagedCallback4 |
864 | { |
865 | public: |
866 | DefaultManagedCallback4(ICorDebug* pDebug); |
867 | virtual ~DefaultManagedCallback4() { } |
868 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** pInterface); |
869 | virtual ULONG STDMETHODCALLTYPE AddRef(); |
870 | virtual ULONG STDMETHODCALLTYPE Release(); |
871 | COM_METHOD BeforeGarbageCollection(ICorDebugProcess* pProcess); |
872 | COM_METHOD AfterGarbageCollection(ICorDebugProcess* pProcess); |
873 | COM_METHOD DataBreakpoint(ICorDebugProcess* pProcess, ICorDebugThread* pThread, BYTE* pContext, ULONG32 contextSize); |
874 | private: |
875 | // not implemented |
876 | DefaultManagedCallback4(const DefaultManagedCallback4&); |
877 | DefaultManagedCallback4& operator=(const DefaultManagedCallback4&); |
878 | |
879 | ICorDebug* m_pDebug; |
880 | LONG m_refCount; |
881 | }; |
882 | |
883 | DefaultManagedCallback4::DefaultManagedCallback4(ICorDebug* pDebug) : m_pDebug(pDebug), m_refCount(0) |
884 | { |
885 | } |
886 | |
887 | HRESULT |
888 | DefaultManagedCallback4::QueryInterface(REFIID iid, void** pInterface) |
889 | { |
890 | if (IID_ICorDebugManagedCallback4 == iid) |
891 | { |
892 | *pInterface = static_cast<ICorDebugManagedCallback4*>(this); |
893 | } |
894 | else if (IID_IUnknown == iid) |
895 | { |
896 | *pInterface = static_cast<IUnknown*>(this); |
897 | } |
898 | else |
899 | { |
900 | *pInterface = NULL; |
901 | return E_NOINTERFACE; |
902 | } |
903 | |
904 | this->AddRef(); |
905 | return S_OK; |
906 | } |
907 | |
908 | ULONG |
909 | DefaultManagedCallback4::AddRef() |
910 | { |
911 | return InterlockedIncrement(&m_refCount); |
912 | } |
913 | |
914 | ULONG |
915 | DefaultManagedCallback4::Release() |
916 | { |
917 | ULONG ulRef = InterlockedDecrement(&m_refCount); |
918 | if (0 == ulRef) |
919 | { |
920 | delete this; |
921 | } |
922 | |
923 | return ulRef; |
924 | } |
925 | |
926 | HRESULT |
927 | DefaultManagedCallback4::BeforeGarbageCollection(ICorDebugProcess* pProcess) |
928 | { |
929 | // |
930 | // Just ignore and continue the process. |
931 | // |
932 | pProcess->Continue(false); |
933 | return S_OK; |
934 | } |
935 | |
936 | HRESULT |
937 | DefaultManagedCallback4::AfterGarbageCollection(ICorDebugProcess* pProcess) |
938 | { |
939 | // |
940 | // Just ignore and continue the process. |
941 | // |
942 | pProcess->Continue(false); |
943 | return S_OK; |
944 | } |
945 | |
946 | HRESULT |
947 | DefaultManagedCallback4::DataBreakpoint(ICorDebugProcess* pProcess, ICorDebugThread* pThread, BYTE* pContext, ULONG32 contextSize) |
948 | { |
949 | // |
950 | // Just ignore and continue the process. |
951 | // |
952 | pProcess->Continue(false); |
953 | return S_OK; |
954 | } |
955 | } |
956 | |
957 | /* ------------------------------------------------------------------------- * |
958 | * Cordb class |
959 | * ------------------------------------------------------------------------- */ |
960 | Cordb::Cordb(CorDebugInterfaceVersion iDebuggerVersion) |
961 | : Cordb(iDebuggerVersion, ProcessDescriptor::CreateUninitialized()) |
962 | { |
963 | } |
964 | |
965 | Cordb::Cordb(CorDebugInterfaceVersion iDebuggerVersion, const ProcessDescriptor& pd) |
966 | : CordbBase(NULL, 0, enumCordb), |
967 | m_processes(11), |
968 | m_initialized(false), |
969 | m_debuggerSpecifiedVersion(iDebuggerVersion), |
970 | m_pd(pd) |
971 | #ifdef FEATURE_CORESYSTEM |
972 | , |
973 | m_targetCLR(0) |
974 | #endif |
975 | { |
976 | g_pRSDebuggingInfo->m_Cordb = this; |
977 | |
978 | #ifdef _DEBUG_IMPL |
979 | // Memory leak detection |
980 | InterlockedIncrement(&s_DbgMemTotalOutstandingCordb); |
981 | #endif |
982 | } |
983 | |
984 | Cordb::~Cordb() |
985 | { |
986 | LOG((LF_CORDB, LL_INFO10, "C::~C Terminating Cordb object.\n" )); |
987 | if (m_pd.m_ApplicationGroupId != NULL) |
988 | { |
989 | delete [] m_pd.m_ApplicationGroupId; |
990 | } |
991 | g_pRSDebuggingInfo->m_Cordb = NULL; |
992 | } |
993 | |
994 | void Cordb::Neuter() |
995 | { |
996 | if (this->IsNeutered()) |
997 | { |
998 | return; |
999 | } |
1000 | |
1001 | |
1002 | RSLockHolder lockHolder(&m_processListMutex); |
1003 | m_pProcessEnumList.NeuterAndClear(NULL); |
1004 | |
1005 | |
1006 | HRESULT hr = S_OK; |
1007 | EX_TRY // @dbgtodo push this up. |
1008 | { |
1009 | // Iterating needs to be done under the processList lock (small), while neutering |
1010 | // needs to be able to take the process lock (big). |
1011 | RSPtrArray<CordbProcess> list; |
1012 | m_processes.TransferToArray(&list); // throws |
1013 | |
1014 | // can't hold list lock while calling CordbProcess::Neuter (which |
1015 | // will take the Process-lock). |
1016 | lockHolder.Release(); |
1017 | |
1018 | list.NeuterAndClear(); |
1019 | // List dtor calls release on each element |
1020 | } |
1021 | EX_CATCH_HRESULT(hr); |
1022 | SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr); |
1023 | |
1024 | CordbCommonBase::Neuter(); |
1025 | |
1026 | // Implicit release from smart ptr. |
1027 | } |
1028 | |
1029 | #ifdef _DEBUG_IMPL |
1030 | void CheckMemLeaks() |
1031 | { |
1032 | // Memory leak detection. |
1033 | LONG l = InterlockedDecrement(&Cordb::s_DbgMemTotalOutstandingCordb); |
1034 | if (l == 0) |
1035 | { |
1036 | // If we just released our final Cordb root object, then we expect no internal references at all. |
1037 | // Note that there may still be external references (and thus not all objects may have been |
1038 | // deleted yet). |
1039 | bool fLeakedInternal = (Cordb::s_DbgMemTotalOutstandingInternalRefs > 0); |
1040 | |
1041 | // Some Cordb objects (such as CordbValues) may not be rooted, and thus we can't neuter |
1042 | // them and thus an external ref may keep them alive. Since these objects may have internal refs, |
1043 | // This means that external refs can keep internal refs. |
1044 | // Thus this assert must be tempered if unrooted objects are leaked. (But that means we can always |
1045 | // assert the tempered version; regardless of bugs in Cordbg). |
1046 | CONSISTENCY_CHECK_MSGF(!fLeakedInternal, |
1047 | ("'%d' Outstanding internal references at final Cordb::Terminate\n" , |
1048 | Cordb::s_DbgMemTotalOutstandingInternalRefs)); |
1049 | |
1050 | DWORD dLeakCheck = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgLeakCheck); |
1051 | if (dLeakCheck > 0) |
1052 | { |
1053 | // We have 1 ref for this Cordb root object. All other refs should have been deleted. |
1054 | CONSISTENCY_CHECK_MSGF(Cordb::s_TotalObjectCount == 1, ("'%d' total cordbBase objects are leaked.\n" , |
1055 | Cordb::s_TotalObjectCount-1)); |
1056 | } |
1057 | } |
1058 | } |
1059 | #endif |
1060 | |
1061 | // This shuts down ICorDebug. |
1062 | // All CordbProcess objects owned by this Cordb object must have either: |
1063 | // - returned for a Detach() call |
1064 | // - returned from dispatching the ExitProcess() callback. |
1065 | // In both cases, CordbProcess::NeuterChildren has been called, although the Process object itself |
1066 | // may not yet be neutered. This condition will ensure that the CordbProcess objects don't need |
1067 | // any resources that we're about to release. |
1068 | HRESULT Cordb::Terminate() |
1069 | { |
1070 | LOG((LF_CORDB, LL_INFO10000, "[%x] Terminating Cordb\n" , GetCurrentThreadId())); |
1071 | |
1072 | if (!m_initialized) |
1073 | return E_FAIL; |
1074 | |
1075 | FAIL_IF_NEUTERED(this); |
1076 | |
1077 | // We can't terminate the debugging services from within a callback. |
1078 | // Caller is supposed to be out of all callbacks when they call this. |
1079 | // This also avoids a deadlock because we'll shutdown the RCET, which would block if we're |
1080 | // in the RCET. |
1081 | if (m_rcEventThread->IsRCEventThread()) |
1082 | { |
1083 | STRESS_LOG0(LF_CORDB, LL_INFO10, "C::T: failed on RCET\n" ); |
1084 | _ASSERTE(!"Gross API Misuse: Debugger shouldn't call ICorDebug::Terminate from within a managed callback." ); |
1085 | return CORDBG_E_CANT_CALL_ON_THIS_THREAD; |
1086 | } |
1087 | |
1088 | // @todo - do we need to throw some switch to prevent new processes from being added now? |
1089 | |
1090 | // VS must stop all debugging before terminating. Fail if we have any non-neutered processes |
1091 | // (b/c those processes should have been either shutdown or detached). |
1092 | // We are in an undefined state if this check fails. |
1093 | // Process are removed from this list before Process::Detach() returns and before the ExitProcess callback is dispatched. |
1094 | // Thus any processes in this list should be live or have an unrecoverable error. |
1095 | { |
1096 | RSLockHolder ch(&m_processListMutex); |
1097 | |
1098 | HASHFIND hfDT; |
1099 | CordbProcess * pProcess; |
1100 | |
1101 | for (pProcess= (CordbProcess*) m_processes.FindFirst(&hfDT); |
1102 | pProcess != NULL; |
1103 | pProcess = (CordbProcess*) m_processes.FindNext(&hfDT)) |
1104 | { |
1105 | _ASSERTE(pProcess->IsSafeToSendEvents() || pProcess->m_unrecoverableError); |
1106 | if (pProcess->IsSafeToSendEvents() && !pProcess->m_unrecoverableError) |
1107 | { |
1108 | CONSISTENCY_CHECK_MSGF(false, ("Gross API misuses. Callling terminate with live process:0x%p\n" , pProcess)); |
1109 | STRESS_LOG1(LF_CORDB, LL_INFO10, "Cordb::Terminate b/c of non-neutered process '%p'\n" , pProcess); |
1110 | // This is very bad. |
1111 | // GROSS API MISUSES - Debugger is calling ICorDebug::Terminate while there |
1112 | // are still outstanding (non-neutered) ICorDebugProcess. |
1113 | // ICorDebug is now in an undefined state. |
1114 | // We will also leak memory b/c we're leaving the EventThreads up (which will in turn |
1115 | // keep a reference to this Cordb object). |
1116 | return ErrWrapper(CORDBG_E_ILLEGAL_SHUTDOWN_ORDER); |
1117 | } |
1118 | } |
1119 | } |
1120 | |
1121 | // @todo- ideally, we'd wait for all threads to get outside of ICorDebug before we proceed. |
1122 | // That's tough to implement in practice; but we at least wait for both ET to exit. As these |
1123 | // guys dispatch callbacks, that means at least we'll wait until VS is outside of any callback. |
1124 | // |
1125 | // Stop the event handling threads. |
1126 | // |
1127 | if (m_rcEventThread != NULL) |
1128 | { |
1129 | // Stop may do significant work b/c if it drains the worker queue. |
1130 | m_rcEventThread->Stop(); |
1131 | delete m_rcEventThread; |
1132 | m_rcEventThread = NULL; |
1133 | } |
1134 | |
1135 | |
1136 | #ifdef _DEBUG |
1137 | // @todo - this disables thread-safety asserts on the process-list-hash. We clearly |
1138 | // can't hold the lock while neutering it. (lock violation since who knows what neuter may do) |
1139 | // @todo- we may have races beteen Cordb::Terminate and Cordb::CreateProcess as both |
1140 | // modify the process list. This is mitigated since Terminate is supposed to be the last method called. |
1141 | m_processes.DebugSetRSLock(NULL); |
1142 | #endif |
1143 | |
1144 | // |
1145 | // We expect the debugger to neuter all processes before calling Terminate(), so do not neuter them here. |
1146 | // |
1147 | |
1148 | #ifdef _DEBUG |
1149 | { |
1150 | HASHFIND find; |
1151 | _ASSERTE(m_processes.FindFirst(&find) == NULL); // should be emptied by neuter |
1152 | } |
1153 | #endif //_DEBUG |
1154 | |
1155 | // Officially mark us as neutered. |
1156 | this->Neuter(); |
1157 | |
1158 | m_processListMutex.Destroy(); |
1159 | |
1160 | // |
1161 | // Release the callbacks |
1162 | // |
1163 | m_managedCallback.Clear(); |
1164 | m_managedCallback2.Clear(); |
1165 | m_managedCallback3.Clear(); |
1166 | m_managedCallback4.Clear(); |
1167 | m_unmanagedCallback.Clear(); |
1168 | |
1169 | // The Shell may still have outstanding references, so we don't want to shutdown logging yet. |
1170 | // But everything should be neutered anyways. |
1171 | |
1172 | m_initialized = FALSE; |
1173 | |
1174 | |
1175 | // After this, all outstanding Cordb objects should be neutered. |
1176 | LOG((LF_CORDB, LL_EVERYTHING, "Cordb finished terminating.\n" )); |
1177 | |
1178 | #if defined(_DEBUG) |
1179 | // |
1180 | // Assert that there are no outstanding object references within the debugging |
1181 | // API itself. |
1182 | // |
1183 | CheckMemLeaks(); |
1184 | #endif |
1185 | |
1186 | return S_OK; |
1187 | } |
1188 | |
1189 | HRESULT Cordb::QueryInterface(REFIID id, void **pInterface) |
1190 | { |
1191 | if (id == IID_ICorDebug) |
1192 | *pInterface = static_cast<ICorDebug*>(this); |
1193 | else if (id == IID_IUnknown) |
1194 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebug*>(this)); |
1195 | else |
1196 | { |
1197 | *pInterface = NULL; |
1198 | return E_NOINTERFACE; |
1199 | } |
1200 | |
1201 | ExternalAddRef(); |
1202 | return S_OK; |
1203 | } |
1204 | |
1205 | |
1206 | |
1207 | // |
1208 | // Initialize -- setup the ICorDebug object by creating any objects |
1209 | // that the object needs to operate and starting the two needed IPC |
1210 | // threads. |
1211 | // |
1212 | HRESULT Cordb::Initialize(void) |
1213 | { |
1214 | HRESULT hr = S_OK; |
1215 | |
1216 | FAIL_IF_NEUTERED(this); |
1217 | |
1218 | if (!m_initialized) |
1219 | { |
1220 | CordbCommonBase::InitializeCommon(); |
1221 | |
1222 | // Since logging wasn't active when we called CordbBase, do it now. |
1223 | LOG((LF_CORDB, LL_EVERYTHING, "Memory: CordbBase object allocated: this=%p, count=%d, RootObject\n" , this, s_TotalObjectCount)); |
1224 | LOG((LF_CORDB, LL_INFO10, "Initializing ICorDebug...\n" )); |
1225 | |
1226 | // Ensure someone hasn't messed up the IPC buffer size |
1227 | _ASSERTE(sizeof(DebuggerIPCEvent) <= CorDBIPC_BUFFER_SIZE); |
1228 | |
1229 | // |
1230 | // Init things that the Cordb will need to operate |
1231 | // |
1232 | m_processListMutex.Init("Process-List Lock" , RSLock::cLockReentrant, RSLock::LL_PROCESS_LIST_LOCK); |
1233 | |
1234 | #ifdef _DEBUG |
1235 | m_processes.DebugSetRSLock(&m_processListMutex); |
1236 | #endif |
1237 | |
1238 | // |
1239 | // Create the runtime controller event listening thread |
1240 | // |
1241 | m_rcEventThread = new (nothrow) CordbRCEventThread(this); |
1242 | |
1243 | if (m_rcEventThread == NULL) |
1244 | { |
1245 | hr = E_OUTOFMEMORY; |
1246 | } |
1247 | else |
1248 | { |
1249 | // This stuff only creates events & starts the thread |
1250 | hr = m_rcEventThread->Init(); |
1251 | |
1252 | if (SUCCEEDED(hr)) |
1253 | hr = m_rcEventThread->Start(); |
1254 | |
1255 | if (FAILED(hr)) |
1256 | { |
1257 | delete m_rcEventThread; |
1258 | m_rcEventThread = NULL; |
1259 | } |
1260 | } |
1261 | |
1262 | if (FAILED(hr)) |
1263 | goto exit; |
1264 | |
1265 | m_initialized = TRUE; |
1266 | } |
1267 | |
1268 | exit: |
1269 | return hr; |
1270 | } |
1271 | |
1272 | //--------------------------------------------------------------------------------------- |
1273 | // |
1274 | // Throw if no more process can be debugged with this Cordb object. |
1275 | // |
1276 | // Notes: |
1277 | // This is highly dependent on the wait sets in the Win32 & RCET threads. |
1278 | // @dbgtodo- this will end up in the shim. |
1279 | |
1280 | void Cordb::EnsureAllowAnotherProcess() |
1281 | { |
1282 | CONTRACTL |
1283 | { |
1284 | THROWS; |
1285 | } |
1286 | CONTRACTL_END; |
1287 | |
1288 | RSLockHolder ch(&m_processListMutex); |
1289 | |
1290 | // Cordb, Win32, and RCET all have process sets, but Cordb's is the |
1291 | // best count of total debuggees. The RCET set is volatile (processes |
1292 | // are added / removed when they become synchronized), and Win32's set |
1293 | // doesn't include all processes. |
1294 | int cCurProcess = GetProcessList()->GetCount(); |
1295 | |
1296 | // In order to accept another debuggee, we must have a free slot in all |
1297 | // wait sets. Currently, we don't expose the size of those sets, but |
1298 | // we know they're MAXIMUM_WAIT_OBJECTS. Note that we lose one slot |
1299 | // to the control event. |
1300 | if (cCurProcess >= MAXIMUM_WAIT_OBJECTS - 1) |
1301 | { |
1302 | ThrowHR(CORDBG_E_TOO_MANY_PROCESSES); |
1303 | } |
1304 | } |
1305 | |
1306 | //--------------------------------------------------------------------------------------- |
1307 | // |
1308 | // Add process to the list. |
1309 | // |
1310 | // Notes: |
1311 | // AddProcess -- add a process object to this ICorDebug's hash of processes. |
1312 | // This also tells this ICorDebug's runtime controller thread that the |
1313 | // process set has changed so it can update its list of wait events. |
1314 | // |
1315 | void Cordb::AddProcess(CordbProcess* process) |
1316 | { |
1317 | // At this point, we should have already checked that we |
1318 | // can have another debuggee. |
1319 | STRESS_LOG1(LF_CORDB, LL_INFO10, "Cordb::AddProcess %08x...\n" , process); |
1320 | |
1321 | if ((m_managedCallback == NULL) || (m_managedCallback2 == NULL) || (m_managedCallback3 == NULL) || (m_managedCallback4 == NULL)) |
1322 | { |
1323 | ThrowHR(E_FAIL); |
1324 | } |
1325 | |
1326 | |
1327 | |
1328 | RSLockHolder lockHolder(&m_processListMutex); |
1329 | |
1330 | // Once we add another process, all outstanding process-enumerators become invalid. |
1331 | m_pProcessEnumList.NeuterAndClear(NULL); |
1332 | |
1333 | GetProcessList()->AddBaseOrThrow(process); |
1334 | m_rcEventThread->ProcessStateChanged(); |
1335 | } |
1336 | |
1337 | // |
1338 | // RemoveProcess -- remove a process object from this ICorDebug's hash of |
1339 | // processes. This also tells this ICorDebug's runtime controller thread |
1340 | // that the process set has changed so it can update its list of wait events. |
1341 | // |
1342 | void Cordb::RemoveProcess(CordbProcess* process) |
1343 | { |
1344 | STRESS_LOG1(LF_CORDB, LL_INFO10, "Cordb::RemoveProcess %08x...\n" , process); |
1345 | |
1346 | LockProcessList(); |
1347 | GetProcessList()->RemoveBase((ULONG_PTR)process->m_id); |
1348 | |
1349 | m_rcEventThread->ProcessStateChanged(); |
1350 | |
1351 | UnlockProcessList(); |
1352 | } |
1353 | |
1354 | // |
1355 | // LockProcessList -- Lock the process list. |
1356 | // |
1357 | void Cordb::LockProcessList(void) |
1358 | { |
1359 | m_processListMutex.Lock(); |
1360 | } |
1361 | |
1362 | // |
1363 | // UnlockProcessList -- Unlock the process list. |
1364 | // |
1365 | void Cordb::UnlockProcessList(void) |
1366 | { |
1367 | m_processListMutex.Unlock(); |
1368 | } |
1369 | |
1370 | #ifdef _DEBUG |
1371 | // Return true iff this thread owns the ProcessList lock |
1372 | bool Cordb::ThreadHasProcessListLock() |
1373 | { |
1374 | return m_processListMutex.HasLock(); |
1375 | } |
1376 | #endif |
1377 | |
1378 | |
1379 | // Get the hash that has the process. |
1380 | CordbSafeHashTable<CordbProcess> *Cordb::GetProcessList() |
1381 | { |
1382 | // If we're accessing the hash, we'd better be locked. |
1383 | _ASSERTE(ThreadHasProcessListLock()); |
1384 | |
1385 | return &m_processes; |
1386 | } |
1387 | |
1388 | |
1389 | HRESULT Cordb::SendIPCEvent(CordbProcess * pProcess, |
1390 | DebuggerIPCEvent * pEvent, |
1391 | SIZE_T eventSize) |
1392 | { |
1393 | HRESULT hr = S_OK; |
1394 | |
1395 | LOG((LF_CORDB, LL_EVERYTHING, "SendIPCEvent in Cordb called\n" )); |
1396 | EX_TRY |
1397 | { |
1398 | hr = m_rcEventThread->SendIPCEvent(pProcess, pEvent, eventSize); |
1399 | } |
1400 | EX_CATCH_HRESULT(hr) |
1401 | return hr; |
1402 | } |
1403 | |
1404 | |
1405 | void Cordb::ProcessStateChanged(void) |
1406 | { |
1407 | m_rcEventThread->ProcessStateChanged(); |
1408 | } |
1409 | |
1410 | |
1411 | HRESULT Cordb::WaitForIPCEventFromProcess(CordbProcess* process, |
1412 | CordbAppDomain *pAppDomain, |
1413 | DebuggerIPCEvent* event) |
1414 | { |
1415 | return m_rcEventThread->WaitForIPCEventFromProcess(process, |
1416 | pAppDomain, |
1417 | event); |
1418 | } |
1419 | |
1420 | HRESULT Cordb::SetTargetCLR(HMODULE hmodTargetCLR) |
1421 | { |
1422 | if (m_initialized) |
1423 | return E_FAIL; |
1424 | |
1425 | #ifdef FEATURE_CORESYSTEM |
1426 | m_targetCLR = hmodTargetCLR; |
1427 | #endif |
1428 | |
1429 | // @REVIEW: are we happy with this workaround? It allows us to use the existing |
1430 | // infrastructure for instance name decoration, but it really doesn't fit |
1431 | // the same model because coreclr.dll isn't in this process and hmodTargetCLR |
1432 | // is the debuggee target, not the coreclr.dll to bind utilcode to.. |
1433 | |
1434 | CoreClrCallbacks cccallbacks; |
1435 | cccallbacks.m_hmodCoreCLR = hmodTargetCLR; |
1436 | cccallbacks.m_pfnIEE = NULL; |
1437 | cccallbacks.m_pfnGetCORSystemDirectory = NULL; |
1438 | cccallbacks.m_pfnGetCLRFunction = NULL; |
1439 | InitUtilcode(cccallbacks); |
1440 | |
1441 | return S_OK; |
1442 | } |
1443 | |
1444 | //----------------------------------------------------------- |
1445 | // ICorDebug |
1446 | //----------------------------------------------------------- |
1447 | |
1448 | // Set the handler for callbacks on managed events |
1449 | // This can not be NULL. |
1450 | // If we're debugging V2.0 apps, pCallback must implement ICDManagedCallback2 |
1451 | // @todo- what if somebody calls this after we've already initialized? (eg, changes |
1452 | // the callback underneath us) |
1453 | HRESULT Cordb::SetManagedHandler(ICorDebugManagedCallback *pCallback) |
1454 | { |
1455 | if (!m_initialized) |
1456 | return E_FAIL; |
1457 | |
1458 | FAIL_IF_NEUTERED(this); |
1459 | VALIDATE_POINTER_TO_OBJECT(pCallback, ICorDebugManagedCallback*); |
1460 | |
1461 | m_managedCallback.Clear(); |
1462 | m_managedCallback2.Clear(); |
1463 | m_managedCallback3.Clear(); |
1464 | m_managedCallback4.Clear(); |
1465 | |
1466 | // For SxS, V2.0 debuggers must implement ManagedCallback2 to handle v2.0 debug events. |
1467 | // For Single-CLR, A v1.0 debugger may actually geta V2.0 debuggee. |
1468 | pCallback->QueryInterface(IID_ICorDebugManagedCallback2, (void **)&m_managedCallback2); |
1469 | if (m_managedCallback2 == NULL) |
1470 | { |
1471 | if (GetDebuggerVersion() >= CorDebugVersion_2_0) |
1472 | { |
1473 | // This will leave our internal callbacks null, which future operations (Create/Attach) will |
1474 | // use to know that we're not sufficiently initialized. |
1475 | return E_NOINTERFACE; |
1476 | } |
1477 | else |
1478 | { |
1479 | // This should only be used in a single-CLR shimming scenario. |
1480 | m_managedCallback2.Assign(new (nothrow) DefaultManagedCallback2(this)); |
1481 | |
1482 | if (m_managedCallback2 == NULL) |
1483 | { |
1484 | return E_OUTOFMEMORY; |
1485 | } |
1486 | } |
1487 | } |
1488 | |
1489 | pCallback->QueryInterface(IID_ICorDebugManagedCallback3, (void **)&m_managedCallback3); |
1490 | if (m_managedCallback3 == NULL) |
1491 | { |
1492 | m_managedCallback3.Assign(new (nothrow) DefaultManagedCallback3(this)); |
1493 | } |
1494 | |
1495 | if (m_managedCallback3 == NULL) |
1496 | { |
1497 | return E_OUTOFMEMORY; |
1498 | } |
1499 | |
1500 | pCallback->QueryInterface(IID_ICorDebugManagedCallback4, (void **)&m_managedCallback4); |
1501 | if (m_managedCallback4 == NULL) |
1502 | { |
1503 | m_managedCallback4.Assign(new (nothrow) DefaultManagedCallback4(this)); |
1504 | } |
1505 | |
1506 | if (m_managedCallback4 == NULL) |
1507 | { |
1508 | return E_OUTOFMEMORY; |
1509 | } |
1510 | |
1511 | m_managedCallback.Assign(pCallback); |
1512 | return S_OK; |
1513 | } |
1514 | |
1515 | HRESULT Cordb::SetUnmanagedHandler(ICorDebugUnmanagedCallback *pCallback) |
1516 | { |
1517 | if (!m_initialized) |
1518 | return E_FAIL; |
1519 | |
1520 | FAIL_IF_NEUTERED(this); |
1521 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pCallback, ICorDebugUnmanagedCallback*); |
1522 | |
1523 | m_unmanagedCallback.Assign(pCallback); |
1524 | |
1525 | return S_OK; |
1526 | } |
1527 | |
1528 | // CreateProcess() isn't supported on Windows CoreCLR. |
1529 | // It is currently supported on Mac CoreCLR, but that may change. |
1530 | bool Cordb::IsCreateProcessSupported() |
1531 | { |
1532 | #if !defined(FEATURE_DBGIPC_TRANSPORT_DI) |
1533 | return false; |
1534 | #else |
1535 | return true; |
1536 | #endif |
1537 | } |
1538 | |
1539 | // Given everything we know about our configuration, can we support interop-debugging |
1540 | bool Cordb::IsInteropDebuggingSupported() |
1541 | { |
1542 | // We explicitly refrain from checking the unmanaged callback. See comment in |
1543 | // ICorDebug::SetUnmanagedHandler for details. |
1544 | #ifdef FEATURE_INTEROP_DEBUGGING |
1545 | |
1546 | #if !defined(FEATURE_CORESYSTEM) |
1547 | // Interop debugging is only supported internally on CoreCLR. |
1548 | // Check if the special reg key is set. If not, then we don't allow interop debugging. |
1549 | if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgEnableMixedModeDebugging) == 0) |
1550 | { |
1551 | return false; |
1552 | } |
1553 | #endif // FEATURE_CORESYSTEM |
1554 | |
1555 | return true; |
1556 | #else |
1557 | return false; |
1558 | #endif |
1559 | } |
1560 | |
1561 | |
1562 | //--------------------------------------------------------------------------------------- |
1563 | // |
1564 | // Implementation of ICorDebug::CreateProcess. |
1565 | // Creates a process. |
1566 | // |
1567 | // Arguments: |
1568 | // The following arguments are passed thru unmodified to the OS CreateProcess API and |
1569 | // are defined by that API. |
1570 | // lpApplicationName |
1571 | // lpCommandLine |
1572 | // lpProcessAttributes |
1573 | // lpThreadAttributes |
1574 | // bInheritHandles |
1575 | // dwCreationFlags |
1576 | // lpCurrentDirectory |
1577 | // lpStartupInfo |
1578 | // lpProcessInformation |
1579 | // debuggingFlags |
1580 | // |
1581 | // ppProcess - Space to fill in for the resulting process, returned as a valid pointer |
1582 | // on any success HRESULT. |
1583 | // |
1584 | // Return Value: |
1585 | // Normal HRESULT semantics. |
1586 | // |
1587 | //--------------------------------------------------------------------------------------- |
1588 | HRESULT Cordb::CreateProcess(LPCWSTR lpApplicationName, |
1589 | __in_z LPWSTR lpCommandLine, |
1590 | LPSECURITY_ATTRIBUTES lpProcessAttributes, |
1591 | LPSECURITY_ATTRIBUTES lpThreadAttributes, |
1592 | BOOL bInheritHandles, |
1593 | DWORD dwCreationFlags, |
1594 | PVOID lpEnvironment, |
1595 | LPCWSTR lpCurrentDirectory, |
1596 | LPSTARTUPINFOW lpStartupInfo, |
1597 | LPPROCESS_INFORMATION lpProcessInformation, |
1598 | CorDebugCreateProcessFlags debuggingFlags, |
1599 | ICorDebugProcess **ppProcess) |
1600 | { |
1601 | return CreateProcessCommon(NULL, |
1602 | lpApplicationName, |
1603 | lpCommandLine, |
1604 | lpProcessAttributes, |
1605 | lpThreadAttributes, |
1606 | bInheritHandles, |
1607 | dwCreationFlags, |
1608 | lpEnvironment, |
1609 | lpCurrentDirectory, |
1610 | lpStartupInfo, |
1611 | lpProcessInformation, |
1612 | debuggingFlags, |
1613 | ppProcess); |
1614 | } |
1615 | |
1616 | HRESULT Cordb::CreateProcessCommon(ICorDebugRemoteTarget * pRemoteTarget, |
1617 | LPCWSTR lpApplicationName, |
1618 | __in_z LPWSTR lpCommandLine, |
1619 | LPSECURITY_ATTRIBUTES lpProcessAttributes, |
1620 | LPSECURITY_ATTRIBUTES lpThreadAttributes, |
1621 | BOOL bInheritHandles, |
1622 | DWORD dwCreationFlags, |
1623 | PVOID lpEnvironment, |
1624 | LPCWSTR lpCurrentDirectory, |
1625 | LPSTARTUPINFOW lpStartupInfo, |
1626 | LPPROCESS_INFORMATION lpProcessInformation, |
1627 | CorDebugCreateProcessFlags debuggingFlags, |
1628 | ICorDebugProcess ** ppProcess) |
1629 | { |
1630 | // If you hit this assert, it means that you are attempting to create a process without specifying the version |
1631 | // number. |
1632 | _ASSERTE(CorDebugInvalidVersion != m_debuggerSpecifiedVersion); |
1633 | |
1634 | PUBLIC_API_ENTRY(this); |
1635 | FAIL_IF_NEUTERED(this); |
1636 | VALIDATE_POINTER_TO_OBJECT(ppProcess, ICorDebugProcess**); |
1637 | |
1638 | HRESULT hr = S_OK; |
1639 | |
1640 | EX_TRY |
1641 | { |
1642 | if (!m_initialized) |
1643 | { |
1644 | ThrowHR(E_FAIL); |
1645 | } |
1646 | |
1647 | // Check that we support the debugger version |
1648 | CheckCompatibility(); |
1649 | |
1650 | #ifdef FEATURE_INTEROP_DEBUGGING |
1651 | // DEBUG_PROCESS (=0x1) means debug this process & all future children. |
1652 | // DEBUG_ONLY_THIS_PROCESS =(0x2) means just debug the immediate process. |
1653 | // If we want to support DEBUG_PROCESS, then we need to have the RS sniff for new CREATE_PROCESS |
1654 | // events and spawn new CordbProcess for them. |
1655 | switch(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) |
1656 | { |
1657 | // 1) managed-only debugging |
1658 | case 0: |
1659 | break; |
1660 | |
1661 | // 2) failure - returns E_NOTIMPL. (as this would involve debugging all of our children processes). |
1662 | case DEBUG_PROCESS: |
1663 | ThrowHR(E_NOTIMPL); |
1664 | |
1665 | // 3) Interop-debugging. |
1666 | // Note that MSDN (at least as of Jan 2003) is wrong about this flag. MSDN claims |
1667 | // DEBUG_ONLY_THIS_PROCESS w/o DEBUG_PROCESS should be ignored. |
1668 | // But it really should do launch as a debuggee (but not auto-attach to child processes). |
1669 | case DEBUG_ONLY_THIS_PROCESS: |
1670 | // Emprically, this is the common case for native / interop-debugging. |
1671 | break; |
1672 | |
1673 | // 4) Interop. |
1674 | // The spec for ICorDebug::CreateProcess says this is the one to use for interop-debugging. |
1675 | case DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS: |
1676 | // Win2k does not honor these flags properly. So we just use |
1677 | // It treats (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS) as if it were DEBUG_PROCESS. |
1678 | // We'll just always touch up the flags, even though WinXP and above is fine here. |
1679 | // Per win2k issue, strip off DEBUG_PROCESS, so that we're just left w/ DEBUG_ONLY_THIS_PROCESS. |
1680 | dwCreationFlags &= ~(DEBUG_PROCESS); |
1681 | break; |
1682 | |
1683 | default: |
1684 | __assume(0); |
1685 | } |
1686 | |
1687 | #endif // FEATURE_INTEROP_DEBUGGING |
1688 | |
1689 | // Must have a managed-callback by now. |
1690 | if ((m_managedCallback == NULL) || (m_managedCallback2 == NULL) || (m_managedCallback3 == NULL) || (m_managedCallback4 == NULL)) |
1691 | { |
1692 | ThrowHR(E_FAIL); |
1693 | } |
1694 | |
1695 | if (!IsCreateProcessSupported()) |
1696 | { |
1697 | ThrowHR(E_NOTIMPL); |
1698 | } |
1699 | |
1700 | if (!IsInteropDebuggingSupported() && |
1701 | ((dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) != 0)) |
1702 | { |
1703 | ThrowHR(CORDBG_E_INTEROP_NOT_SUPPORTED); |
1704 | } |
1705 | |
1706 | // Check that we can even accept another debuggee before trying anything. |
1707 | EnsureAllowAnotherProcess(); |
1708 | |
1709 | } EX_CATCH_HRESULT(hr); |
1710 | if (FAILED(hr)) |
1711 | { |
1712 | return hr; |
1713 | } |
1714 | |
1715 | hr = ShimProcess::CreateProcess(this, |
1716 | pRemoteTarget, |
1717 | lpApplicationName, |
1718 | lpCommandLine, |
1719 | lpProcessAttributes, |
1720 | lpThreadAttributes, |
1721 | bInheritHandles, |
1722 | dwCreationFlags, |
1723 | lpEnvironment, |
1724 | lpCurrentDirectory, |
1725 | lpStartupInfo, |
1726 | lpProcessInformation, |
1727 | debuggingFlags |
1728 | ); |
1729 | |
1730 | LOG((LF_CORDB, LL_EVERYTHING, "Handle in Cordb::CreateProcess is: %.I64x\n" , lpProcessInformation->hProcess)); |
1731 | |
1732 | if (SUCCEEDED(hr)) |
1733 | { |
1734 | LockProcessList(); |
1735 | |
1736 | CordbProcess * pProcess = GetProcessList()->GetBase(lpProcessInformation->dwProcessId); |
1737 | |
1738 | UnlockProcessList(); |
1739 | |
1740 | PREFIX_ASSUME(pProcess != NULL); |
1741 | |
1742 | pProcess->ExternalAddRef(); |
1743 | *ppProcess = (ICorDebugProcess *)pProcess; |
1744 | } |
1745 | |
1746 | return hr; |
1747 | } |
1748 | |
1749 | |
1750 | HRESULT Cordb::CreateProcessEx(ICorDebugRemoteTarget * pRemoteTarget, |
1751 | LPCWSTR lpApplicationName, |
1752 | __in_z LPWSTR lpCommandLine, |
1753 | LPSECURITY_ATTRIBUTES lpProcessAttributes, |
1754 | LPSECURITY_ATTRIBUTES lpThreadAttributes, |
1755 | BOOL bInheritHandles, |
1756 | DWORD dwCreationFlags, |
1757 | PVOID lpEnvironment, |
1758 | LPCWSTR lpCurrentDirectory, |
1759 | LPSTARTUPINFOW lpStartupInfo, |
1760 | LPPROCESS_INFORMATION lpProcessInformation, |
1761 | CorDebugCreateProcessFlags debuggingFlags, |
1762 | ICorDebugProcess ** ppProcess) |
1763 | { |
1764 | if (pRemoteTarget == NULL) |
1765 | { |
1766 | return E_INVALIDARG; |
1767 | } |
1768 | |
1769 | return CreateProcessCommon(pRemoteTarget, |
1770 | lpApplicationName, |
1771 | lpCommandLine, |
1772 | lpProcessAttributes, |
1773 | lpThreadAttributes, |
1774 | bInheritHandles, |
1775 | dwCreationFlags, |
1776 | lpEnvironment, |
1777 | lpCurrentDirectory, |
1778 | lpStartupInfo, |
1779 | lpProcessInformation, |
1780 | debuggingFlags, |
1781 | ppProcess); |
1782 | } |
1783 | |
1784 | |
1785 | //--------------------------------------------------------------------------------------- |
1786 | // |
1787 | // Attachs to an existing process. |
1788 | // |
1789 | // Arguments: |
1790 | // dwProcessID - The PID to attach to |
1791 | // fWin32Attach - Flag to tell whether to attach as the Win32 debugger or not. |
1792 | // ppProcess - Space to fill in for the resulting process, returned as a valid pointer |
1793 | // on any success HRESULT. |
1794 | // |
1795 | // Return Value: |
1796 | // Normal HRESULT semantics. |
1797 | // |
1798 | //--------------------------------------------------------------------------------------- |
1799 | HRESULT Cordb::DebugActiveProcess(DWORD dwProcessId, |
1800 | BOOL fWin32Attach, |
1801 | ICorDebugProcess **ppProcess) |
1802 | { |
1803 | return DebugActiveProcessCommon(NULL, dwProcessId, fWin32Attach, ppProcess); |
1804 | } |
1805 | |
1806 | HRESULT Cordb::DebugActiveProcessCommon(ICorDebugRemoteTarget * pRemoteTarget, |
1807 | DWORD dwProcessId, |
1808 | BOOL fWin32Attach, |
1809 | ICorDebugProcess ** ppProcess) |
1810 | { |
1811 | PUBLIC_API_ENTRY(this); |
1812 | FAIL_IF_NEUTERED(this); |
1813 | VALIDATE_POINTER_TO_OBJECT(ppProcess, ICorDebugProcess **); |
1814 | |
1815 | HRESULT hr = S_OK; |
1816 | |
1817 | EX_TRY |
1818 | { |
1819 | if (!m_initialized) |
1820 | { |
1821 | ThrowHR(E_FAIL); |
1822 | } |
1823 | |
1824 | // Must have a managed-callback by now. |
1825 | if ((m_managedCallback == NULL) || (m_managedCallback2 == NULL) || (m_managedCallback3 == NULL) || (m_managedCallback4 == NULL)) |
1826 | { |
1827 | ThrowHR(E_FAIL); |
1828 | } |
1829 | |
1830 | // Verify that given process ID, matches the process ID for which the object was created |
1831 | if (m_pd.IsInitialized() && m_pd.m_Pid != dwProcessId) |
1832 | { |
1833 | ThrowHR(E_INVALIDARG); |
1834 | } |
1835 | |
1836 | // See the comment in Cordb::CreateProcess |
1837 | _ASSERTE(CorDebugInvalidVersion != m_debuggerSpecifiedVersion); |
1838 | |
1839 | // Check that we support the debugger version |
1840 | CheckCompatibility(); |
1841 | |
1842 | // Check that we can even accept another debuggee before trying anything. |
1843 | EnsureAllowAnotherProcess(); |
1844 | |
1845 | // Check if we're allowed to do interop. |
1846 | bool fAllowInterop = IsInteropDebuggingSupported(); |
1847 | |
1848 | if (!fAllowInterop && fWin32Attach) |
1849 | { |
1850 | ThrowHR(CORDBG_E_INTEROP_NOT_SUPPORTED); |
1851 | } |
1852 | |
1853 | } EX_CATCH_HRESULT(hr) |
1854 | if (FAILED(hr)) |
1855 | { |
1856 | return hr; |
1857 | } |
1858 | |
1859 | hr = ShimProcess::DebugActiveProcess( |
1860 | this, |
1861 | pRemoteTarget, |
1862 | &m_pd, |
1863 | fWin32Attach == TRUE); |
1864 | |
1865 | // If that worked, then there will be a process object... |
1866 | if (SUCCEEDED(hr)) |
1867 | { |
1868 | LockProcessList(); |
1869 | CordbProcess * pProcess = GetProcessList()->GetBase(dwProcessId); |
1870 | |
1871 | if (pProcess != NULL) |
1872 | { |
1873 | // Add a reference now so process won't go away |
1874 | pProcess->ExternalAddRef(); |
1875 | } |
1876 | UnlockProcessList(); |
1877 | |
1878 | if (pProcess == NULL) |
1879 | { |
1880 | // This can happen if we add the process into process hash in |
1881 | // SendDebugActiveProcessEvent and then process exit |
1882 | // before we attemp to retrieve it again from GetBase. |
1883 | // |
1884 | *ppProcess = NULL; |
1885 | return S_FALSE; |
1886 | } |
1887 | |
1888 | #if defined(FEATURE_DBGIPC_TRANSPORT_DI) |
1889 | // This is where we queue the managed attach event in Whidbey. In the new architecture, the Windows |
1890 | // pipeline gets a loader breakpoint when native attach is completed, and that's where we queue the |
1891 | // managed attach event. See how we handle the loader breakpoint in code:ShimProcess::DefaultEventHandler. |
1892 | // However, the Mac debugging transport gets no such breakpoint, and so we need to do this here. |
1893 | // |
1894 | // @dbgtodo Mac - Ideally we should hide this in our pipeline implementation, or at least move |
1895 | // this to the shim. |
1896 | _ASSERTE(!fWin32Attach); |
1897 | { |
1898 | pProcess->Lock(); |
1899 | hr = pProcess->QueueManagedAttach(); |
1900 | pProcess->Unlock(); |
1901 | } |
1902 | #endif // FEATURE_DBGIPC_TRANSPORT_DI |
1903 | |
1904 | *ppProcess = (ICorDebugProcess*) pProcess; |
1905 | } |
1906 | |
1907 | return hr; |
1908 | } |
1909 | |
1910 | // Make sure we want to support the debugger that's using us |
1911 | void Cordb::CheckCompatibility() |
1912 | { |
1913 | // Get the debugger version specified by the startup APIs and convert it to a CLR major version number |
1914 | CorDebugInterfaceVersion debuggerVersion = GetDebuggerVersion(); |
1915 | DWORD clrMajor; |
1916 | if (debuggerVersion <= CorDebugVersion_1_0 || debuggerVersion == CorDebugVersion_1_1) |
1917 | clrMajor = 1; |
1918 | else if (debuggerVersion <= CorDebugVersion_2_0) |
1919 | clrMajor = 2; |
1920 | else if (debuggerVersion <= CorDebugVersion_4_0) |
1921 | clrMajor = 4; |
1922 | else |
1923 | clrMajor = 5; // some unrecognized future version |
1924 | |
1925 | if(!CordbProcess::IsCompatibleWith(clrMajor)) |
1926 | { |
1927 | // Carefully choose our error-code to get an appropriate error-message from VS 2008 |
1928 | // If GetDebuggerVersion is >= 4, we could consider using the more-appropriate (but not |
1929 | // added until V4) HRESULT CORDBG_E_UNSUPPORTED_FORWARD_COMPAT that is used by |
1930 | // OpenVirtualProcess, but it's probably simpler to keep ICorDebug APIs returning |
1931 | // consistent error codes. |
1932 | ThrowHR(CORDBG_E_INCOMPATIBLE_PROTOCOL); |
1933 | } |
1934 | } |
1935 | |
1936 | HRESULT Cordb::DebugActiveProcessEx(ICorDebugRemoteTarget * pRemoteTarget, |
1937 | DWORD dwProcessId, |
1938 | BOOL fWin32Attach, |
1939 | ICorDebugProcess ** ppProcess) |
1940 | { |
1941 | if (pRemoteTarget == NULL) |
1942 | { |
1943 | return E_INVALIDARG; |
1944 | } |
1945 | |
1946 | return DebugActiveProcessCommon(pRemoteTarget, dwProcessId, fWin32Attach, ppProcess); |
1947 | } |
1948 | |
1949 | |
1950 | HRESULT Cordb::GetProcess(DWORD dwProcessId, ICorDebugProcess **ppProcess) |
1951 | { |
1952 | PUBLIC_API_ENTRY(this); |
1953 | FAIL_IF_NEUTERED(this); |
1954 | VALIDATE_POINTER_TO_OBJECT(ppProcess, ICorDebugProcess**); |
1955 | |
1956 | if (!m_initialized) |
1957 | { |
1958 | return E_FAIL; |
1959 | } |
1960 | |
1961 | LockProcessList(); |
1962 | CordbProcess *p = GetProcessList()->GetBase(dwProcessId); |
1963 | UnlockProcessList(); |
1964 | |
1965 | if (p == NULL) |
1966 | return E_INVALIDARG; |
1967 | |
1968 | p->ExternalAddRef(); |
1969 | *ppProcess = static_cast<ICorDebugProcess*> (p); |
1970 | |
1971 | return S_OK; |
1972 | } |
1973 | |
1974 | HRESULT Cordb::EnumerateProcesses(ICorDebugProcessEnum **ppProcesses) |
1975 | { |
1976 | PUBLIC_API_ENTRY(this); |
1977 | FAIL_IF_NEUTERED(this); |
1978 | VALIDATE_POINTER_TO_OBJECT(ppProcesses, ICorDebugProcessEnum **); |
1979 | |
1980 | HRESULT hr = S_OK; |
1981 | EX_TRY |
1982 | { |
1983 | if (!m_initialized) |
1984 | { |
1985 | ThrowHR(E_FAIL); |
1986 | } |
1987 | |
1988 | // Locking here just means that the enumerator gets initialized against a consistent |
1989 | // process-list. If we add/remove processes w/ an outstanding enumerator, things |
1990 | // could still get out of sync. |
1991 | RSLockHolder lockHolder(&this->m_processListMutex); |
1992 | |
1993 | RSInitHolder<CordbHashTableEnum> pEnum; |
1994 | CordbHashTableEnum::BuildOrThrow( |
1995 | this, |
1996 | &m_pProcessEnumList, |
1997 | GetProcessList(), |
1998 | IID_ICorDebugProcessEnum, |
1999 | pEnum.GetAddr()); |
2000 | |
2001 | |
2002 | pEnum.TransferOwnershipExternal(ppProcesses); |
2003 | } |
2004 | EX_CATCH_HRESULT(hr); |
2005 | return hr; |
2006 | } |
2007 | |
2008 | |
2009 | // |
2010 | // Note: the following defs and structs are copied from various NT headers. I wasn't able to include those headers (like |
2011 | // ntexapi.h) due to loads of redef problems and other conflicts with headers that we already pull in. |
2012 | // |
2013 | typedef LONG NTSTATUS; |
2014 | |
2015 | #ifndef FEATURE_PAL |
2016 | typedef BOOL (*NTQUERYSYSTEMINFORMATION)(SYSTEM_INFORMATION_CLASS SystemInformationClass, |
2017 | PVOID SystemInformation, |
2018 | ULONG SystemInformationLength, |
2019 | PULONG ReturnLength); |
2020 | #endif |
2021 | |
2022 | // Implementation of ICorDebug::CanLaunchOrAttach |
2023 | // @dbgtodo- this all goes away in V3. |
2024 | // @dbgtodo- this should go away in Dev11. |
2025 | HRESULT Cordb::CanLaunchOrAttach(DWORD dwProcessId, BOOL fWin32DebuggingEnabled) |
2026 | { |
2027 | PUBLIC_API_ENTRY(this); |
2028 | FAIL_IF_NEUTERED(this); |
2029 | |
2030 | HRESULT hr = S_OK; |
2031 | EX_TRY |
2032 | { |
2033 | EnsureCanLaunchOrAttach(fWin32DebuggingEnabled); |
2034 | } |
2035 | EX_CATCH_HRESULT(hr); |
2036 | |
2037 | return hr; |
2038 | } |
2039 | |
2040 | //--------------------------------------------------------------------------------------- |
2041 | // |
2042 | // Throw an expcetion if we can't launch/attach. |
2043 | // |
2044 | // Arguments: |
2045 | // fWin32DebuggingEnabled - true if interop-debugging, else false |
2046 | // |
2047 | // Return Value: |
2048 | // None. If this returns, then it's safe to launch/attach. |
2049 | // Else this throws an exception on failure. |
2050 | // |
2051 | // Assumptions: |
2052 | // |
2053 | // Notes: |
2054 | // It should always be safe to launch/attach except in exceptional cases. |
2055 | // @dbgtodo- this all goes away in V3. |
2056 | // @dbgtodo- this should go away in Dev11. |
2057 | // |
2058 | void Cordb::EnsureCanLaunchOrAttach(BOOL fWin32DebuggingEnabled) |
2059 | { |
2060 | CONTRACTL |
2061 | { |
2062 | THROWS; |
2063 | } |
2064 | CONTRACTL_END; |
2065 | if (!m_initialized) |
2066 | { |
2067 | ThrowHR(E_FAIL); |
2068 | } |
2069 | |
2070 | EnsureAllowAnotherProcess(); |
2071 | |
2072 | if (!IsInteropDebuggingSupported() && fWin32DebuggingEnabled) |
2073 | { |
2074 | ThrowHR(CORDBG_E_INTEROP_NOT_SUPPORTED); |
2075 | } |
2076 | |
2077 | // Made it this far, we succeeded. |
2078 | } |
2079 | |
2080 | HRESULT Cordb::CreateObjectV1(REFIID id, void **object) |
2081 | { |
2082 | return CreateObject(CorDebugVersion_1_0, ProcessDescriptor::UNINITIALIZED_PID, NULL, id, object); |
2083 | } |
2084 | |
2085 | #if defined(FEATURE_DBGIPC_TRANSPORT_DI) |
2086 | // CoreCLR activates debugger objects via direct COM rather than the shim (just like V1). For now we share the |
2087 | // same debug engine version as V2, though this may change in the future. |
2088 | HRESULT Cordb::CreateObjectTelesto(REFIID id, void ** pObject) |
2089 | { |
2090 | return CreateObject(CorDebugVersion_2_0, ProcessDescriptor::UNINITIALIZED_PID, NULL, id, pObject); |
2091 | } |
2092 | #endif // FEATURE_DBGIPC_TRANSPORT_DI |
2093 | |
2094 | // Static |
2095 | // Used to create an instance for a ClassFactory (thus an external ref). |
2096 | HRESULT Cordb::CreateObject(CorDebugInterfaceVersion iDebuggerVersion, DWORD pid, LPCWSTR lpApplicationGroupId, REFIID id, void **object) |
2097 | { |
2098 | if (id != IID_IUnknown && id != IID_ICorDebug) |
2099 | return (E_NOINTERFACE); |
2100 | |
2101 | LPSTR applicationGroupId = NULL; |
2102 | if (lpApplicationGroupId != NULL) |
2103 | { |
2104 | // Get length of target string |
2105 | int cbMultiByte = WideCharToMultiByte(CP_ACP, 0, lpApplicationGroupId, -1, NULL, 0, NULL, NULL); |
2106 | if (cbMultiByte == 0) |
2107 | { |
2108 | return E_FAIL; |
2109 | } |
2110 | |
2111 | applicationGroupId = new (nothrow) CHAR[cbMultiByte]; |
2112 | if (applicationGroupId == NULL) |
2113 | { |
2114 | return (E_OUTOFMEMORY); |
2115 | } |
2116 | |
2117 | /* Convert to ASCII */ |
2118 | cbMultiByte = WideCharToMultiByte(CP_ACP, 0, lpApplicationGroupId, -1, applicationGroupId, cbMultiByte, NULL, NULL); |
2119 | if (cbMultiByte == 0) |
2120 | { |
2121 | return E_FAIL; |
2122 | } |
2123 | } |
2124 | |
2125 | ProcessDescriptor pd = ProcessDescriptor::Create(pid, applicationGroupId); |
2126 | |
2127 | Cordb *db = new (nothrow) Cordb(iDebuggerVersion, pd); |
2128 | |
2129 | if (db == NULL) |
2130 | { |
2131 | if (applicationGroupId != NULL) |
2132 | delete [] applicationGroupId; |
2133 | |
2134 | return (E_OUTOFMEMORY); |
2135 | } |
2136 | |
2137 | *object = static_cast<ICorDebug*> (db); |
2138 | db->ExternalAddRef(); |
2139 | |
2140 | return (S_OK); |
2141 | } |
2142 | |
2143 | |
2144 | // This is the version of the ICorDebug APIs that the debugger believes it's consuming. |
2145 | // If this is a different version than that of the debuggee, we have the option of shimming |
2146 | // behavior. |
2147 | CorDebugInterfaceVersion |
2148 | Cordb::GetDebuggerVersion() const |
2149 | { |
2150 | return m_debuggerSpecifiedVersion; |
2151 | } |
2152 | |
2153 | //*********************************************************************** |
2154 | // ICorDebugTMEnum (Thread and Module enumerator) |
2155 | //*********************************************************************** |
2156 | CordbEnumFilter::CordbEnumFilter(CordbBase * pOwnerObj, NeuterList * pOwnerList) |
2157 | : CordbBase (pOwnerObj->GetProcess(), 0), |
2158 | m_pOwnerObj(pOwnerObj), |
2159 | m_pOwnerNeuterList(pOwnerList), |
2160 | m_pFirst (NULL), |
2161 | m_pCurrent (NULL), |
2162 | m_iCount (0) |
2163 | { |
2164 | _ASSERTE(m_pOwnerNeuterList != NULL); |
2165 | |
2166 | HRESULT hr = S_OK; |
2167 | EX_TRY |
2168 | { |
2169 | m_pOwnerNeuterList->Add(pOwnerObj->GetProcess(), this); |
2170 | } |
2171 | EX_CATCH_HRESULT(hr); |
2172 | SetUnrecoverableIfFailed(GetProcess(), hr); |
2173 | |
2174 | } |
2175 | |
2176 | CordbEnumFilter::CordbEnumFilter(CordbEnumFilter *src) |
2177 | : CordbBase (src->GetProcess(), 0), |
2178 | m_pOwnerObj(src->m_pOwnerObj), |
2179 | m_pOwnerNeuterList(src->m_pOwnerNeuterList), |
2180 | m_pFirst (NULL), |
2181 | m_pCurrent (NULL) |
2182 | { |
2183 | _ASSERTE(m_pOwnerNeuterList != NULL); |
2184 | |
2185 | HRESULT hr = S_OK; |
2186 | EX_TRY |
2187 | { |
2188 | m_pOwnerNeuterList->Add(src->GetProcess(), this); |
2189 | } |
2190 | EX_CATCH_HRESULT(hr); |
2191 | SetUnrecoverableIfFailed(GetProcess(), hr); |
2192 | |
2193 | |
2194 | |
2195 | int iCountSanityCheck = 0; |
2196 | EnumElement *pElementCur = NULL; |
2197 | EnumElement *pElementNew = NULL; |
2198 | EnumElement *pElementNewPrev = NULL; |
2199 | |
2200 | m_iCount = src->m_iCount; |
2201 | |
2202 | pElementCur = src->m_pFirst; |
2203 | |
2204 | while (pElementCur != NULL) |
2205 | { |
2206 | pElementNew = new (nothrow) EnumElement; |
2207 | if (pElementNew == NULL) |
2208 | { |
2209 | // Out of memory. Clean up and bail out. |
2210 | goto Error; |
2211 | } |
2212 | |
2213 | if (pElementNewPrev == NULL) |
2214 | { |
2215 | m_pFirst = pElementNew; |
2216 | } |
2217 | else |
2218 | { |
2219 | pElementNewPrev->SetNext(pElementNew); |
2220 | } |
2221 | |
2222 | pElementNewPrev = pElementNew; |
2223 | |
2224 | // Copy the element, including the AddRef part |
2225 | pElementNew->SetData(pElementCur->GetData()); |
2226 | IUnknown *iu = (IUnknown *)pElementCur->GetData(); |
2227 | iu->AddRef(); |
2228 | |
2229 | if (pElementCur == src->m_pCurrent) |
2230 | m_pCurrent = pElementNew; |
2231 | |
2232 | pElementCur = pElementCur->GetNext(); |
2233 | iCountSanityCheck++; |
2234 | } |
2235 | |
2236 | _ASSERTE(iCountSanityCheck == m_iCount); |
2237 | |
2238 | return; |
2239 | Error: |
2240 | // release all the allocated memory before returning |
2241 | pElementCur = m_pFirst; |
2242 | |
2243 | while (pElementCur != NULL) |
2244 | { |
2245 | pElementNewPrev = pElementCur; |
2246 | pElementCur = pElementCur->GetNext(); |
2247 | |
2248 | ((ICorDebugModule *)pElementNewPrev->GetData())->Release(); |
2249 | delete pElementNewPrev; |
2250 | } |
2251 | } |
2252 | |
2253 | CordbEnumFilter::~CordbEnumFilter() |
2254 | { |
2255 | _ASSERTE(this->IsNeutered()); |
2256 | |
2257 | _ASSERTE(m_pFirst == NULL); |
2258 | } |
2259 | |
2260 | void CordbEnumFilter::Neuter() |
2261 | { |
2262 | EnumElement *pElement = m_pFirst; |
2263 | EnumElement *pPrevious = NULL; |
2264 | |
2265 | while (pElement != NULL) |
2266 | { |
2267 | pPrevious = pElement; |
2268 | pElement = pElement->GetNext(); |
2269 | delete pPrevious; |
2270 | } |
2271 | |
2272 | // Null out the head in case we get neutered again. |
2273 | m_pFirst = NULL; |
2274 | m_pCurrent = NULL; |
2275 | |
2276 | CordbBase::Neuter(); |
2277 | } |
2278 | |
2279 | |
2280 | |
2281 | HRESULT CordbEnumFilter::QueryInterface(REFIID id, void **ppInterface) |
2282 | { |
2283 | // if we QI with the IID of the base type, we can't just return a pointer ICorDebugEnum directly, because |
2284 | // the cast is ambiguous. This happens because CordbEnumFilter implements both ICorDebugModuleEnum and |
2285 | // ICorDebugThreadEnum, both of which derive in turn from ICorDebugEnum. This produces a diamond inheritance |
2286 | // graph. Thus we need a double cast. It doesn't really matter whether we pick ICorDebugThreadEnum or |
2287 | // ICorDebugModuleEnum, because it will be backed by the same object regardless. |
2288 | if (id == IID_ICorDebugEnum) |
2289 | *ppInterface = static_cast<ICorDebugEnum *>(static_cast<ICorDebugThreadEnum *>(this)); |
2290 | else if (id == IID_ICorDebugModuleEnum) |
2291 | *ppInterface = (ICorDebugModuleEnum*)this; |
2292 | else if (id == IID_ICorDebugThreadEnum) |
2293 | *ppInterface = (ICorDebugThreadEnum*)this; |
2294 | else if (id == IID_IUnknown) |
2295 | *ppInterface = this; |
2296 | else |
2297 | { |
2298 | *ppInterface = NULL; |
2299 | return E_NOINTERFACE; |
2300 | } |
2301 | |
2302 | ExternalAddRef(); |
2303 | return S_OK; |
2304 | } |
2305 | |
2306 | HRESULT CordbEnumFilter::Skip(ULONG celt) |
2307 | { |
2308 | HRESULT hr = S_OK; |
2309 | PUBLIC_API_BEGIN(this); |
2310 | { |
2311 | while ((celt-- > 0) && (m_pCurrent != NULL)) |
2312 | { |
2313 | m_pCurrent = m_pCurrent->GetNext(); |
2314 | } |
2315 | } |
2316 | PUBLIC_API_END(hr); |
2317 | return hr; |
2318 | } |
2319 | |
2320 | HRESULT CordbEnumFilter::Reset() |
2321 | { |
2322 | HRESULT hr = S_OK; |
2323 | PUBLIC_API_BEGIN(this); |
2324 | { |
2325 | m_pCurrent = m_pFirst; |
2326 | } |
2327 | PUBLIC_API_END(hr); |
2328 | return hr; |
2329 | } |
2330 | |
2331 | HRESULT CordbEnumFilter::Clone(ICorDebugEnum **ppEnum) |
2332 | { |
2333 | HRESULT hr = S_OK; |
2334 | PUBLIC_API_BEGIN(this); |
2335 | { |
2336 | ValidateOrThrow(ppEnum); |
2337 | |
2338 | CordbEnumFilter * pClone = new CordbEnumFilter(this); |
2339 | |
2340 | // Ambiguous conversion from CordbEnumFilter to ICorDebugEnum, so |
2341 | // we explicitly convert it through ICorDebugThreadEnum. |
2342 | pClone->ExternalAddRef(); |
2343 | (*ppEnum) = static_cast<ICorDebugThreadEnum *> (pClone); |
2344 | } |
2345 | PUBLIC_API_END(hr); |
2346 | return hr; |
2347 | } |
2348 | |
2349 | HRESULT CordbEnumFilter::GetCount(ULONG *pcelt) |
2350 | { |
2351 | HRESULT hr = S_OK; |
2352 | PUBLIC_API_BEGIN(this); |
2353 | { |
2354 | ValidateOrThrow(pcelt); |
2355 | *pcelt = (ULONG)m_iCount; |
2356 | } |
2357 | PUBLIC_API_END(hr); |
2358 | return hr; |
2359 | } |
2360 | |
2361 | HRESULT CordbEnumFilter::Next(ULONG celt, |
2362 | ICorDebugModule *objects[], |
2363 | ULONG *pceltFetched) |
2364 | { |
2365 | HRESULT hr = S_OK; |
2366 | PUBLIC_API_BEGIN(this); |
2367 | { |
2368 | hr = NextWorker(celt, objects, pceltFetched); |
2369 | } |
2370 | PUBLIC_API_END(hr); |
2371 | return hr; |
2372 | } |
2373 | |
2374 | HRESULT CordbEnumFilter::NextWorker(ULONG celt, ICorDebugModule *objects[], ULONG *pceltFetched) |
2375 | { |
2376 | // <TODO> |
2377 | // |
2378 | // nickbe 11/20/2002 10:43:39 |
2379 | // This function allows you to enumerate threads that "belong" to a |
2380 | // particular AppDomain. While this operation makes some sense, it makes |
2381 | // very little sense to |
2382 | // (a) enumerate the list of threads in the enter process |
2383 | // (b) build up a hand-rolled singly linked list (grrr) |
2384 | // </TODO> |
2385 | VALIDATE_POINTER_TO_OBJECT_ARRAY(objects, ICorDebugModule *, |
2386 | celt, true, true); |
2387 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pceltFetched, ULONG *); |
2388 | |
2389 | if ((pceltFetched == NULL) && (celt != 1)) |
2390 | { |
2391 | return E_INVALIDARG; |
2392 | } |
2393 | |
2394 | if (celt == 0) |
2395 | { |
2396 | if (pceltFetched != NULL) |
2397 | { |
2398 | *pceltFetched = 0; |
2399 | } |
2400 | return S_OK; |
2401 | } |
2402 | |
2403 | HRESULT hr = S_OK; |
2404 | |
2405 | ULONG count = 0; |
2406 | |
2407 | while ((m_pCurrent != NULL) && (count < celt)) |
2408 | { |
2409 | objects[count] = (ICorDebugModule *)m_pCurrent->GetData(); |
2410 | m_pCurrent = m_pCurrent->GetNext(); |
2411 | count++; |
2412 | } |
2413 | |
2414 | if (pceltFetched != NULL) |
2415 | { |
2416 | *pceltFetched = count; |
2417 | } |
2418 | |
2419 | // |
2420 | // If we reached the end of the enumeration, but not the end |
2421 | // of the number of requested items, we return S_FALSE. |
2422 | // |
2423 | if (count < celt) |
2424 | { |
2425 | return S_FALSE; |
2426 | } |
2427 | |
2428 | return hr; |
2429 | } |
2430 | |
2431 | |
2432 | HRESULT CordbEnumFilter::Next(ULONG celt, |
2433 | ICorDebugThread *objects[], |
2434 | ULONG *pceltFetched) |
2435 | { |
2436 | HRESULT hr = S_OK; |
2437 | PUBLIC_API_BEGIN(this); |
2438 | { |
2439 | hr = NextWorker(celt, objects, pceltFetched); |
2440 | } |
2441 | PUBLIC_API_END(hr); |
2442 | return hr; |
2443 | } |
2444 | |
2445 | HRESULT CordbEnumFilter::NextWorker(ULONG celt, ICorDebugThread *objects[], ULONG *pceltFetched) |
2446 | { |
2447 | // @TODO remove this class |
2448 | VALIDATE_POINTER_TO_OBJECT_ARRAY(objects, ICorDebugThread *, celt, true, true); |
2449 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pceltFetched, ULONG *); |
2450 | |
2451 | if ((pceltFetched == NULL) && (celt != 1)) |
2452 | { |
2453 | return E_INVALIDARG; |
2454 | } |
2455 | |
2456 | if (celt == 0) |
2457 | { |
2458 | if (pceltFetched != NULL) |
2459 | { |
2460 | *pceltFetched = 0; |
2461 | } |
2462 | return S_OK; |
2463 | } |
2464 | |
2465 | HRESULT hr = S_OK; |
2466 | |
2467 | ULONG count = 0; |
2468 | |
2469 | while ((m_pCurrent != NULL) && (count < celt)) |
2470 | { |
2471 | objects[count] = (ICorDebugThread *)m_pCurrent->GetData(); |
2472 | m_pCurrent = m_pCurrent->GetNext(); |
2473 | count++; |
2474 | } |
2475 | |
2476 | if (pceltFetched != NULL) |
2477 | { |
2478 | *pceltFetched = count; |
2479 | } |
2480 | |
2481 | // |
2482 | // If we reached the end of the enumeration, but not the end |
2483 | // of the number of requested items, we return S_FALSE. |
2484 | // |
2485 | if (count < celt) |
2486 | { |
2487 | return S_FALSE; |
2488 | } |
2489 | |
2490 | return hr; |
2491 | } |
2492 | |
2493 | |
2494 | |
2495 | HRESULT CordbEnumFilter::Init (ICorDebugModuleEnum * pModEnum, CordbAssembly *pAssembly) |
2496 | { |
2497 | INTERNAL_API_ENTRY(GetProcess()); |
2498 | |
2499 | ICorDebugModule *pCorModule = NULL; |
2500 | CordbModule *pModule = NULL; |
2501 | ULONG ulDummy = 0; |
2502 | |
2503 | HRESULT hr = pModEnum->Next(1, &pCorModule, &ulDummy); |
2504 | |
2505 | // |
2506 | // Next returns E_FAIL if there is no next item, along with |
2507 | // the count being 0. Convert that to just being S_OK. |
2508 | // |
2509 | if ((hr == E_FAIL) && (ulDummy == 0)) |
2510 | { |
2511 | hr = S_OK; |
2512 | } |
2513 | |
2514 | if (FAILED (hr)) |
2515 | return hr; |
2516 | |
2517 | EnumElement *pPrevious = NULL; |
2518 | EnumElement *pElement = NULL; |
2519 | |
2520 | while (ulDummy != 0) |
2521 | { |
2522 | pModule = (CordbModule *)(ICorDebugModule *)pCorModule; |
2523 | // Is this module part of the assembly for which we're enumerating? |
2524 | if (pModule->m_pAssembly == pAssembly) |
2525 | { |
2526 | pElement = new (nothrow) EnumElement; |
2527 | if (pElement == NULL) |
2528 | { |
2529 | // Out of memory. Clean up and bail out. |
2530 | hr = E_OUTOFMEMORY; |
2531 | goto Error; |
2532 | } |
2533 | |
2534 | pElement->SetData ((void *)pCorModule); |
2535 | m_iCount++; |
2536 | |
2537 | if (m_pFirst == NULL) |
2538 | { |
2539 | m_pFirst = pElement; |
2540 | } |
2541 | else |
2542 | { |
2543 | PREFIX_ASSUME(pPrevious != NULL); |
2544 | pPrevious->SetNext (pElement); |
2545 | } |
2546 | pPrevious = pElement; |
2547 | } |
2548 | else |
2549 | ((ICorDebugModule *)pModule)->Release(); |
2550 | |
2551 | hr = pModEnum->Next(1, &pCorModule, &ulDummy); |
2552 | |
2553 | // |
2554 | // Next returns E_FAIL if there is no next item, along with |
2555 | // the count being 0. Convert that to just being S_OK. |
2556 | // |
2557 | if ((hr == E_FAIL) && (ulDummy == 0)) |
2558 | { |
2559 | hr = S_OK; |
2560 | } |
2561 | |
2562 | if (FAILED (hr)) |
2563 | goto Error; |
2564 | } |
2565 | |
2566 | m_pCurrent = m_pFirst; |
2567 | |
2568 | return S_OK; |
2569 | |
2570 | Error: |
2571 | // release all the allocated memory before returning |
2572 | pElement = m_pFirst; |
2573 | |
2574 | while (pElement != NULL) |
2575 | { |
2576 | pPrevious = pElement; |
2577 | pElement = pElement->GetNext(); |
2578 | |
2579 | ((ICorDebugModule *)pPrevious->GetData())->Release(); |
2580 | delete pPrevious; |
2581 | } |
2582 | |
2583 | return hr; |
2584 | } |
2585 | |
2586 | HRESULT CordbEnumFilter::Init (ICorDebugThreadEnum *pThreadEnum, CordbAppDomain *pAppDomain) |
2587 | { |
2588 | INTERNAL_API_ENTRY(GetProcess()); |
2589 | |
2590 | ICorDebugThread *pCorThread = NULL; |
2591 | CordbThread *pThread = NULL; |
2592 | ULONG ulDummy = 0; |
2593 | |
2594 | HRESULT hr = pThreadEnum->Next(1, &pCorThread, &ulDummy); |
2595 | |
2596 | // |
2597 | // Next returns E_FAIL if there is no next item, but we want to consider this |
2598 | // ok in this context. |
2599 | // |
2600 | if ((hr == E_FAIL) && (ulDummy == 0)) |
2601 | { |
2602 | hr = S_OK; |
2603 | } |
2604 | |
2605 | if (FAILED(hr)) |
2606 | { |
2607 | return hr; |
2608 | } |
2609 | |
2610 | EnumElement *pPrevious = NULL; |
2611 | EnumElement *pElement = NULL; |
2612 | |
2613 | while (ulDummy > 0) |
2614 | { |
2615 | pThread = (CordbThread *)(ICorDebugThread *) pCorThread; |
2616 | |
2617 | // Is this module part of the appdomain for which we're enumerating? |
2618 | // Note that this is rather inefficient (we call into the left side for every AppDomain), |
2619 | // but the whole idea of enumerating the threads of an AppDomain is pretty bad, |
2620 | // and we don't expect this to be used much if at all. |
2621 | CordbAppDomain* pThreadDomain; |
2622 | hr = pThread->GetCurrentAppDomain( &pThreadDomain ); |
2623 | if( FAILED(hr) ) |
2624 | { |
2625 | goto Error; |
2626 | } |
2627 | |
2628 | if (pThreadDomain == pAppDomain) |
2629 | { |
2630 | pElement = new (nothrow) EnumElement; |
2631 | if (pElement == NULL) |
2632 | { |
2633 | // Out of memory. Clean up and bail out. |
2634 | hr = E_OUTOFMEMORY; |
2635 | goto Error; |
2636 | } |
2637 | |
2638 | pElement->SetData ((void *)pCorThread); |
2639 | m_iCount++; |
2640 | |
2641 | if (m_pFirst == NULL) |
2642 | { |
2643 | m_pFirst = pElement; |
2644 | } |
2645 | else |
2646 | { |
2647 | PREFIX_ASSUME(pPrevious != NULL); |
2648 | pPrevious->SetNext (pElement); |
2649 | } |
2650 | |
2651 | pPrevious = pElement; |
2652 | } |
2653 | else |
2654 | { |
2655 | ((ICorDebugThread *)pThread)->Release(); |
2656 | } |
2657 | |
2658 | // get the next thread in the thread list |
2659 | hr = pThreadEnum->Next(1, &pCorThread, &ulDummy); |
2660 | |
2661 | // |
2662 | // Next returns E_FAIL if there is no next item, along with |
2663 | // the count being 0. Convert that to just being S_OK. |
2664 | // |
2665 | if ((hr == E_FAIL) && (ulDummy == 0)) |
2666 | { |
2667 | hr = S_OK; |
2668 | } |
2669 | |
2670 | if (FAILED (hr)) |
2671 | goto Error; |
2672 | } |
2673 | |
2674 | m_pCurrent = m_pFirst; |
2675 | |
2676 | return S_OK; |
2677 | |
2678 | Error: |
2679 | // release all the allocated memory before returning |
2680 | pElement = m_pFirst; |
2681 | |
2682 | while (pElement != NULL) |
2683 | { |
2684 | pPrevious = pElement; |
2685 | pElement = pElement->GetNext(); |
2686 | |
2687 | ((ICorDebugThread *)pPrevious->GetData())->Release(); |
2688 | delete pPrevious; |
2689 | } |
2690 | |
2691 | return hr; |
2692 | } |
2693 | |
2694 | |