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: RsAppDomain.cpp
6//
7
8//
9//*****************************************************************************
10#include "stdafx.h"
11#include "primitives.h"
12#include "safewrap.h"
13
14#include "check.h"
15
16#include <tlhelp32.h>
17#include "wtsapi32.h"
18
19#ifndef SM_REMOTESESSION
20#define SM_REMOTESESSION 0x1000
21#endif
22
23#include "corpriv.h"
24#include "../../dlls/mscorrc/resource.h"
25#include <limits.h>
26
27
28/* ------------------------------------------------------------------------- *
29 * AppDomain class methods
30 * ------------------------------------------------------------------------- */
31
32//
33// Create a CordbAppDomain object based on a pointer to the AppDomain instance
34// in the CLR. Pre-populates some cached information about the AppDomain
35// from the CLR using DAC.
36//
37// Arguments:
38// pProcess - the CordbProcess object that this AppDomain is part of
39// vmAppDomain - the address in the CLR of the AppDomain object this corresponds to.
40// This will be used to read any additional information about the AppDomain.
41//
42// Assumptions:
43// The IMetaSig object should have been allocated by
44// IMDInternal on a valid metadata blob
45//
46//
47CordbAppDomain::CordbAppDomain(CordbProcess * pProcess, VMPTR_AppDomain vmAppDomain)
48 : CordbBase(pProcess, LsPtrToCookie(vmAppDomain.ToLsPtr()), enumCordbAppDomain),
49 m_AppDomainId(0),
50 m_breakpoints(17),
51 m_sharedtypes(3),
52 m_modules(17),
53 m_assemblies(9),
54 m_vmAppDomain(vmAppDomain)
55{
56 // This may throw out of the Ctor on error.
57
58 // @dbgtodo reliability: we should probably tolerate failures here and keep track
59 // of whether our ADID is valid or not, and requery if necessary.
60 m_AppDomainId = m_pProcess->GetDAC()->GetAppDomainId(m_vmAppDomain);
61
62 LOG((LF_CORDB,LL_INFO10000, "CAD::CAD: this:0x%x (void*)this:0x%x<%d>\n", this, (void *)this, m_AppDomainId));
63
64#ifdef _DEBUG
65 m_assemblies.DebugSetRSLock(pProcess->GetProcessLock());
66 m_modules.DebugSetRSLock(pProcess->GetProcessLock());
67 m_breakpoints.DebugSetRSLock(pProcess->GetProcessLock());
68 m_sharedtypes.DebugSetRSLock(pProcess->GetProcessLock());
69#endif
70
71}
72
73/*
74 A list of which resources owened by this object are accounted for.
75
76 RESOLVED:
77 // AddRef() in CordbHashTable::GetBase for a special InProc case
78 // AddRef() on the DB_IPCE_CREATE_APP_DOMAIN event from the LS
79 // Release()ed in Neuter
80 CordbProcess *m_pProcess;
81
82 WCHAR *m_szAppDomainName; // Deleted in ~CordbAppDomain
83
84 // Cleaned up in Neuter
85 CordbHashTable m_assemblies;
86 CordbHashTable m_sharedtypes;
87 CordbHashTable m_modules;
88 CordbHashTable m_breakpoints; // Disconnect()ed in ~CordbAppDomain
89
90 private:
91*/
92
93CordbAppDomain::~CordbAppDomain()
94{
95
96 // We expect to be Neutered before being released. Neutering will release our process ref
97 _ASSERTE(IsNeutered());
98}
99
100
101// Neutered by process. Once we're neutered, we lose our backpointer to the CordbProcess object, and
102// thus can't do things like call GetProcess() or Continue().
103void CordbAppDomain::Neuter()
104{
105 // This check prevents us from calling this twice and underflowing the internal ref count!
106 if (IsNeutered())
107 {
108 return;
109 }
110 _ASSERTE(GetProcess()->ThreadHoldsProcessLock());
111
112 //
113 // Disconnect any active breakpoints
114 //
115 {
116 CordbBreakpoint* entry;
117 HASHFIND find;
118
119 for (entry = m_breakpoints.FindFirst(&find);
120 entry != NULL;
121 entry = m_breakpoints.FindNext(&find))
122 {
123 entry->Disconnect();
124 }
125 }
126
127 // Mark as neutered so that our children can tell the appdomain has now
128 // exited.
129 CordbBase::Neuter();
130
131 //
132 // Purge neuter lists.
133 //
134 m_TypeNeuterList.NeuterAndClear(GetProcess());
135 m_SweepableNeuterList.NeuterAndClear(GetProcess());
136
137
138 m_assemblies.NeuterAndClear(GetProcess()->GetProcessLock());
139 m_modules.NeuterAndClear(GetProcess()->GetProcessLock());
140 m_sharedtypes.NeuterAndClear(GetProcess()->GetProcessLock());
141 m_breakpoints.NeuterAndClear(GetProcess()->GetProcessLock());
142
143}
144
145
146HRESULT CordbAppDomain::QueryInterface(REFIID id, void **ppInterface)
147{
148 if (id == IID_ICorDebugAppDomain)
149 {
150 *ppInterface = (ICorDebugAppDomain*)this;
151 }
152 else if (id == IID_ICorDebugAppDomain2)
153 {
154 *ppInterface = (ICorDebugAppDomain2*)this;
155 }
156 else if (id == IID_ICorDebugAppDomain3)
157 {
158 *ppInterface = (ICorDebugAppDomain3*)this;
159 }
160 else if (id == IID_ICorDebugAppDomain4)
161 {
162 *ppInterface = (ICorDebugAppDomain4*)this;
163 }
164 else if (id == IID_ICorDebugController)
165 *ppInterface = (ICorDebugController*)(ICorDebugAppDomain*)this;
166 else if (id == IID_IUnknown)
167 *ppInterface = (IUnknown*)(ICorDebugAppDomain*)this;
168 else
169 {
170 *ppInterface = NULL;
171 return E_NOINTERFACE;
172 }
173
174 ExternalAddRef();
175 return S_OK;
176}
177
178
179//---------------------------------------------------------------------------------------
180//
181// Ensure the AppDomain friendly name has been set.
182//
183// Return value:
184// S_OK on success, or a failure code if we couldn't read the name for some reason.
185// There shouldn't be any reason in practice for this to fail other than a corrupt
186// process image.
187//
188// Assumptions:
189// The AppDomain object has already been initialized to know about
190// it's corresponding VM appdomain.
191// InvalidateName is called whenever the name may have changed to prompt us to re-fetch.
192//
193//---------------------------------------------------------------------------------------
194HRESULT CordbAppDomain::RefreshName()
195{
196 if (m_strAppDomainName.IsSet())
197 {
198 // If we already have a valid name, we're done.
199 return S_OK;
200 }
201
202 // Use DAC to get the name.
203
204 _ASSERTE(!m_vmAppDomain.IsNull());
205 IDacDbiInterface * pDac = NULL;
206 HRESULT hr = S_OK;
207 EX_TRY
208 {
209 pDac = m_pProcess->GetDAC();
210
211 #ifdef _DEBUG
212 // For debug, double-check the cached value against getting the AD via an AppDomainId.
213 VMPTR_AppDomain pAppDomain = pDac->GetAppDomainFromId(m_AppDomainId);
214 _ASSERTE(m_vmAppDomain == pAppDomain);
215 #endif
216
217 // Get the actual string contents.
218 pDac->GetAppDomainFullName(m_vmAppDomain, &m_strAppDomainName);
219
220 // Now that m_strAppDomainName is set, don't fail without clearing it.
221 }
222 EX_CATCH_HRESULT(hr);
223
224 _ASSERTE(SUCCEEDED(hr) == m_strAppDomainName.IsSet());
225
226 return hr;
227}
228
229
230HRESULT CordbAppDomain::Stop(DWORD dwTimeout)
231{
232 FAIL_IF_NEUTERED(this);
233 PUBLIC_API_ENTRY(this);
234 return (m_pProcess->StopInternal(dwTimeout, this->GetADToken()));
235}
236
237HRESULT CordbAppDomain::Continue(BOOL fIsOutOfBand)
238{
239 PUBLIC_API_ENTRY(this);
240 FAIL_IF_NEUTERED(this);
241 return m_pProcess->ContinueInternal(fIsOutOfBand);
242}
243
244HRESULT CordbAppDomain::IsRunning(BOOL *pbRunning)
245{
246 PUBLIC_API_ENTRY(this);
247 VALIDATE_POINTER_TO_OBJECT(pbRunning, BOOL *);
248 FAIL_IF_NEUTERED(this);
249
250 *pbRunning = !m_pProcess->GetSynchronized();
251
252 return S_OK;
253}
254
255HRESULT CordbAppDomain::HasQueuedCallbacks(ICorDebugThread *pThread, BOOL *pbQueued)
256{
257 PUBLIC_API_ENTRY(this);
258 FAIL_IF_NEUTERED(this);
259
260 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pThread,ICorDebugThread *);
261 VALIDATE_POINTER_TO_OBJECT(pbQueued,BOOL *);
262
263 return m_pProcess->HasQueuedCallbacks (pThread, pbQueued);
264}
265
266HRESULT CordbAppDomain::EnumerateThreads(ICorDebugThreadEnum **ppThreads)
267{
268 // @TODO E_NOIMPL this
269 //
270 // (use Process::EnumerateThreads and let users filter their own data)
271 HRESULT hr = S_OK;
272 PUBLIC_API_BEGIN(this);
273 {
274 ValidateOrThrow(ppThreads);
275
276 RSInitHolder<CordbEnumFilter> pThreadEnum(
277 new CordbEnumFilter(GetProcess(), GetProcess()->GetContinueNeuterList()));
278
279 GetProcess()->PrepopulateThreadsOrThrow();
280
281 RSInitHolder<CordbHashTableEnum> pEnum;
282 GetProcess()->BuildThreadEnum(this, NULL, pEnum.GetAddr());
283
284 // This builds up auxillary list. don't need pEnum after this.
285 hr = pThreadEnum->Init(pEnum, this);
286 IfFailThrow(hr);
287
288 pThreadEnum.TransferOwnershipExternal(ppThreads);
289 }
290 PUBLIC_API_END(hr);
291 return hr;
292}
293
294
295HRESULT CordbAppDomain::SetAllThreadsDebugState(CorDebugThreadState state,
296 ICorDebugThread *pExceptThisThread)
297{
298 PUBLIC_API_ENTRY(this);
299 FAIL_IF_NEUTERED(this);
300 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
301
302 return m_pProcess->SetAllThreadsDebugState(state, pExceptThisThread);
303}
304
305HRESULT CordbAppDomain::Detach()
306{
307 PUBLIC_REENTRANT_API_ENTRY(this); // may be called from IMDA::Detach
308 FAIL_IF_NEUTERED(this);
309
310 return E_NOTIMPL;
311}
312
313HRESULT CordbAppDomain::Terminate(unsigned int exitCode)
314{
315 PUBLIC_API_ENTRY(this);
316 FAIL_IF_NEUTERED(this);
317 return E_NOTIMPL;
318}
319
320void CordbAppDomain::AddToTypeList(CordbBase *pObject)
321{
322 INTERNAL_API_ENTRY(this);
323 _ASSERTE(pObject != NULL);
324 RSLockHolder lockHolder(GetProcess()->GetProcessLock());
325 this->m_TypeNeuterList.Add(GetProcess(), pObject);
326}
327
328
329HRESULT CordbAppDomain::CanCommitChanges(
330 ULONG cSnapshots,
331 ICorDebugEditAndContinueSnapshot *pSnapshots[],
332 ICorDebugErrorInfoEnum **pError)
333{
334 return E_NOTIMPL;
335}
336
337HRESULT CordbAppDomain::CommitChanges(
338 ULONG cSnapshots,
339 ICorDebugEditAndContinueSnapshot *pSnapshots[],
340 ICorDebugErrorInfoEnum **pError)
341{
342 return E_NOTIMPL;
343}
344
345
346/*
347 * GetProcess returns the process containing the app domain
348 */
349HRESULT CordbAppDomain::GetProcess(ICorDebugProcess **ppProcess)
350{
351 PUBLIC_REENTRANT_API_ENTRY(this);
352 FAIL_IF_NEUTERED(this);
353
354 VALIDATE_POINTER_TO_OBJECT(ppProcess,ICorDebugProcess **);
355
356 _ASSERTE (m_pProcess != NULL);
357
358 *ppProcess = static_cast<ICorDebugProcess *> (m_pProcess);
359 m_pProcess->ExternalAddRef();
360
361 return S_OK;
362}
363
364//---------------------------------------------------------------------------------------
365//
366// Callback for assembly enumeration.
367//
368// Arguments:
369// vmDomainAssembly - new assembly to add
370// pThis - user data for CordbAppDomain to add assembly too
371//
372//
373// Assumptions:
374// Invoked as callback from code:CordbAppDomain::PrepopulateAssemblies
375//
376// Notes:
377//
378
379// static
380void CordbAppDomain::AssemblyEnumerationCallback(VMPTR_DomainAssembly vmDomainAssembly, void * pThis)
381{
382 CordbAppDomain * pAppDomain = static_cast<CordbAppDomain *> (pThis);
383 INTERNAL_DAC_CALLBACK(pAppDomain->GetProcess());
384
385 // This lookup will cause the cache to be populated if we haven't seen this assembly before.
386 pAppDomain->LookupOrCreateAssembly(vmDomainAssembly);
387}
388
389
390//---------------------------------------------------------------------------------------
391//
392// Cache a new assembly
393//
394// Arguments:
395// vmDomainAssembly - new assembly to add to cache
396//
397// Return Value:
398// Pointer to Assembly in cache.
399// NULL on failure, and sets unrecoverable error.
400//
401// Assumptions:
402// Caller guarantees assembly is not already added.
403// Called under the stop-go lock.
404//
405// Notes:
406//
407CordbAssembly * CordbAppDomain::CacheAssembly(VMPTR_DomainAssembly vmDomainAssembly)
408{
409 INTERNAL_API_ENTRY(GetProcess());
410
411 VMPTR_Assembly vmAssembly;
412 GetProcess()->GetDAC()->GetAssemblyFromDomainAssembly(vmDomainAssembly, &vmAssembly);
413
414 RSInitHolder<CordbAssembly> pAssembly(new CordbAssembly(this, vmAssembly, vmDomainAssembly));
415
416 return pAssembly.TransferOwnershipToHash(&m_assemblies);
417}
418
419CordbAssembly * CordbAppDomain::CacheAssembly(VMPTR_Assembly vmAssembly)
420{
421 INTERNAL_API_ENTRY(GetProcess());
422
423 RSInitHolder<CordbAssembly> pAssembly(new CordbAssembly(this, vmAssembly, VMPTR_DomainAssembly()));
424
425 return pAssembly.TransferOwnershipToHash(&m_assemblies);
426}
427
428//---------------------------------------------------------------------------------------
429//
430// Build up cache of assmeblies
431//
432// Arguments:
433//
434// Return Value:
435// Throws on error.
436//
437// Assumptions:
438// This is an non-invasive inspection operation called when the debuggee is stopped.
439//
440// Notes:
441// This can safely be called multiple times.
442//
443
444void CordbAppDomain::PrepopulateAssembliesOrThrow()
445{
446 INTERNAL_API_ENTRY(GetProcess());
447
448 RSLockHolder lockHolder(GetProcess()->GetProcessLock());
449
450 if (!GetProcess()->IsDacInitialized())
451 {
452 return;
453 }
454
455 // DD-primitive that invokes a callback.
456 GetProcess()->GetDAC()->EnumerateAssembliesInAppDomain(
457 this->m_vmAppDomain,
458 CordbAppDomain::AssemblyEnumerationCallback,
459 this); // user data
460}
461
462//---------------------------------------------------------------------------------------
463//
464// Public API tp EnumerateAssemblies enumerates all assemblies in the app domain
465//
466// Arguments:
467// ppAssemblies - OUT: get enumerator
468//
469// Return Value:
470// S_OK on success.
471//
472//
473// Notes:
474// This will prepopulate the list of assemblies (useful for non-invasive case
475// where we don't get debug event).
476//
477
478HRESULT CordbAppDomain::EnumerateAssemblies(ICorDebugAssemblyEnum **ppAssemblies)
479{
480 HRESULT hr = S_OK;
481 PUBLIC_API_BEGIN(this);
482 {
483 ValidateOrThrow(ppAssemblies);
484 *ppAssemblies = NULL;
485
486 PrepopulateAssembliesOrThrow();
487
488 RSInitHolder<CordbHashTableEnum> pEnum;
489 CordbHashTableEnum::BuildOrThrow(
490 this,
491 GetProcess()->GetContinueNeuterList(), // ownership
492 &m_assemblies,
493 IID_ICorDebugAssemblyEnum,
494 pEnum.GetAddr());
495 pEnum.TransferOwnershipExternal(ppAssemblies);
496
497 }
498 PUBLIC_API_END(hr);
499 return hr;
500}
501
502// Implement public interface
503HRESULT CordbAppDomain::GetModuleFromMetaDataInterface(
504 IUnknown *pIMetaData,
505 ICorDebugModule **ppModule)
506{
507 PUBLIC_API_ENTRY(this);
508 FAIL_IF_NEUTERED(this);
509 VALIDATE_POINTER_TO_OBJECT(pIMetaData, IUnknown *);
510 VALIDATE_POINTER_TO_OBJECT(ppModule, ICorDebugModule **);
511
512
513
514 HRESULT hr = S_OK;
515
516 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
517
518 *ppModule = NULL;
519
520 EX_TRY
521 {
522 CordbModule * pModule = GetModuleFromMetaDataInterface(pIMetaData);
523 _ASSERTE(pModule != NULL); // thrown on error
524
525 *ppModule = static_cast<ICorDebugModule*> (pModule);
526 pModule->ExternalAddRef();
527 }
528 EX_CATCH_HRESULT(hr);
529
530
531 return hr;
532}
533
534// Gets a CordbModule that has the given metadata interface
535//
536// Arguments:
537// pIMetaData - metadata interface
538//
539// Returns:
540// CordbModule whose associated metadata matches the metadata interface provided here
541// Throws on error. Returns non-null
542//
543CordbModule * CordbAppDomain::GetModuleFromMetaDataInterface(IUnknown *pIMetaData)
544{
545 HRESULT hr = S_OK;
546
547 RSExtSmartPtr<IMetaDataImport> pImport;
548 RSLockHolder lockHolder(GetProcess()->GetProcessLock()); // need for module enumeration
549
550 // Grab the interface we need...
551 hr = pIMetaData->QueryInterface(IID_IMetaDataImport, (void**)&pImport);
552 if (FAILED(hr))
553 {
554 ThrowHR(E_INVALIDARG);
555 }
556
557 // Get the mvid of the given module.
558 GUID matchMVID;
559 hr = pImport->GetScopeProps(NULL, 0, 0, &matchMVID);
560 IfFailThrow(hr);
561
562 CordbModule* pModule;
563 HASHFIND findmodule;
564
565 PrepopulateModules();
566
567 for (pModule = m_modules.FindFirst(&findmodule);
568 pModule != NULL;
569 pModule = m_modules.FindNext(&findmodule))
570 {
571 IMetaDataImport * pImportCurrent = pModule->GetMetaDataImporter(); // throws
572 _ASSERTE(pImportCurrent != NULL);
573
574 // Get the mvid of this module
575 GUID MVID;
576 hr = pImportCurrent->GetScopeProps(NULL, 0, 0, &MVID);
577 IfFailThrow(hr);
578
579 if (MVID == matchMVID)
580 {
581 return pModule;
582 }
583 }
584
585 ThrowHR(E_INVALIDARG);
586}
587
588HRESULT CordbAppDomain::EnumerateBreakpoints(ICorDebugBreakpointEnum **ppBreakpoints)
589{
590 PUBLIC_API_ENTRY(this);
591 FAIL_IF_NEUTERED(this);
592 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
593 VALIDATE_POINTER_TO_OBJECT(ppBreakpoints, ICorDebugBreakpointEnum **);
594
595 HRESULT hr = S_OK;
596 EX_TRY
597 {
598 RSInitHolder<CordbHashTableEnum> pEnum;
599 CordbHashTableEnum::BuildOrThrow(
600 this,
601 GetProcess()->GetContinueNeuterList(), // ownership
602 &m_breakpoints,
603 IID_ICorDebugBreakpointEnum,
604 pEnum.GetAddr());
605
606 pEnum.TransferOwnershipExternal(ppBreakpoints);
607 }
608 EX_CATCH_HRESULT(hr);
609 return hr;
610}
611
612HRESULT CordbAppDomain::EnumerateSteppers(ICorDebugStepperEnum **ppSteppers)
613{
614 PUBLIC_API_ENTRY(this);
615 FAIL_IF_NEUTERED(this);
616 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
617 VALIDATE_POINTER_TO_OBJECT(ppSteppers,ICorDebugStepperEnum **);
618
619 HRESULT hr = S_OK;
620 EX_TRY
621 {
622 //
623 // !!! m_steppers may be modified while user is enumerating,
624 // if steppers complete (if process is running)
625 //
626
627 RSInitHolder<CordbHashTableEnum> pEnum;
628 CordbHashTableEnum::BuildOrThrow(
629 GetProcess(),
630 GetProcess()->GetContinueNeuterList(), // ownership
631 &(m_pProcess->m_steppers),
632 IID_ICorDebugStepperEnum,
633 pEnum.GetAddr());
634
635 pEnum.TransferOwnershipExternal(ppSteppers);
636 }
637 EX_CATCH_HRESULT(hr);
638 return hr;
639}
640
641
642//---------------------------------------------------------------------------------------
643//
644// CordbAppDomain::IsAttached - always returns true
645//
646// Arguments:
647// pfAttached - out parameter, will be set to TRUE
648//
649// Return Value:
650// CORDB_E_OBJECT_NEUTERED if the AppDomain has been neutered
651// E_INVALIDARG if pbAttached is null
652// Otherwise always returns S_OK.
653//
654// Notes:
655// Prior to V3, we used to keep track of a per-appdomain attached status.
656// Debuggers were required to explicitly attach to every AppDomain, so this
657// did not provide any actual functionality. In V3, there is no longer any
658// concept of per-AppDomain attach/detach. This API is provided for compatibility.
659//
660
661HRESULT CordbAppDomain::IsAttached(BOOL *pfAttached)
662{
663 PUBLIC_API_ENTRY(this);
664 FAIL_IF_NEUTERED(this);
665 VALIDATE_POINTER_TO_OBJECT(pfAttached, BOOL *);
666
667 *pfAttached = TRUE;
668
669 return S_OK;
670}
671
672
673//---------------------------------------------------------------------------------------
674//
675// CordbAppDomain::Attach - does nothing
676//
677// Arguments:
678//
679// Return Value:
680// CORDB_E_OBJECT_NEUTERED if the AppDomain has been neutered
681// Otherwise always returns S_OK.
682//
683// Notes:
684// Prior to V3, we used to keep track of a per-appdomain attached status.
685// Debuggers were required to explicitly attach to every AppDomain, so this
686// did not provide any actual functionality. In V3, there is no longer any
687// concept of per-AppDomain attach/detach. This API is provided for compatibility.
688//
689
690HRESULT CordbAppDomain::Attach()
691{
692 PUBLIC_API_ENTRY(this);
693 FAIL_IF_NEUTERED(this);
694 ATT_REQUIRE_STOPPED_MAY_FAIL(m_pProcess);
695
696 return S_OK;
697}
698
699/*
700 * GetName returns the name of the app domain.
701 */
702HRESULT CordbAppDomain::GetName(ULONG32 cchName,
703 ULONG32 *pcchName,
704 __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[])
705{
706 HRESULT hr = S_OK;
707 PUBLIC_API_BEGIN(this)
708 {
709 // Some reasonable defaults
710 if (szName)
711 *szName = 0;
712
713 if (pcchName)
714 *pcchName = 0;
715
716
717 // Lazily refresh.
718 IfFailThrow(RefreshName());
719
720 const WCHAR * pName = m_strAppDomainName;
721 _ASSERTE(pName != NULL);
722
723 hr = CopyOutString(pName, cchName, pcchName, szName);
724 }
725 PUBLIC_API_END(hr);
726 return hr;
727}
728
729/*
730 * GetObject returns the runtime app domain object.
731 * Note: this is lazily initialized and may be NULL
732 */
733HRESULT CordbAppDomain::GetObject(ICorDebugValue **ppObject)
734{
735 PUBLIC_API_ENTRY(this);
736 FAIL_IF_NEUTERED(this);
737 VALIDATE_POINTER_TO_OBJECT(ppObject,ICorDebugObjectValue **);
738
739 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
740
741 _ASSERTE(!m_vmAppDomain.IsNull());
742 IDacDbiInterface * pDac = NULL;
743 HRESULT hr = S_OK;
744 EX_TRY
745 {
746 pDac = m_pProcess->GetDAC();
747 VMPTR_OBJECTHANDLE vmObjHandle = pDac->GetAppDomainObject(m_vmAppDomain);
748 if (!vmObjHandle.IsNull())
749 {
750 ICorDebugReferenceValue * pRefValue = NULL;
751 hr = CordbReferenceValue::BuildFromGCHandle(this, vmObjHandle, &pRefValue);
752 *ppObject = pRefValue;
753 }
754 else
755 {
756 *ppObject = NULL;
757 hr = S_FALSE;
758 }
759 }
760 EX_CATCH_HRESULT(hr);
761
762 return hr;
763}
764
765/*
766 * Get the ID of the app domain.
767 */
768HRESULT CordbAppDomain::GetID (ULONG32 *pId)
769{
770 PUBLIC_REENTRANT_API_ENTRY(this);
771 OK_IF_NEUTERED(this);
772 VALIDATE_POINTER_TO_OBJECT(pId, ULONG32 *);
773
774 *pId = m_AppDomainId;
775
776 return S_OK;
777}
778
779//---------------------------------------------------------------------------------------
780// Remove an assembly from the ICorDebug cache.
781//
782// Arguments:
783// vmDomainAssembly - token to remove.
784//
785// Notes:
786// This is the opposite of code:CordbAppDomain::LookupOrCreateAssembly.
787// This only need to be called at assembly unload events.
788void CordbAppDomain::RemoveAssemblyFromCache(VMPTR_DomainAssembly vmDomainAssembly)
789{
790 // This will handle if the assembly is not in the hash.
791 // This could happen if we attach right before an assembly-unload event.
792 m_assemblies.RemoveBase(VmPtrToCookie(vmDomainAssembly));
793}
794
795//---------------------------------------------------------------------------------------
796// Lookup (or create) the CordbAssembly for the given VMPTR_DomainAssembly
797//
798// Arguments:
799// vmDomainAssembly - CLR token for the Assembly.
800//
801// Returns:
802// a CordbAssembly object for the given CLR assembly. This may be from the cache,
803// or newly created if not yet in the cache.
804// Never returns NULL. Throws on error (eg, oom).
805//
806CordbAssembly * CordbAppDomain::LookupOrCreateAssembly(VMPTR_DomainAssembly vmDomainAssembly)
807{
808 CordbAssembly * pAssembly = m_assemblies.GetBase(VmPtrToCookie(vmDomainAssembly));
809 if (pAssembly != NULL)
810 {
811 return pAssembly;
812 }
813 return CacheAssembly(vmDomainAssembly);
814}
815
816
817//
818CordbAssembly * CordbAppDomain::LookupOrCreateAssembly(VMPTR_Assembly vmAssembly)
819{
820 CordbAssembly * pAssembly = m_assemblies.GetBase(VmPtrToCookie(vmAssembly));
821 if (pAssembly != NULL)
822 {
823 return pAssembly;
824 }
825 return CacheAssembly(vmAssembly);
826}
827
828
829//---------------------------------------------------------------------------------------
830// Lookup or create a module within the appdomain
831//
832// Arguments:
833// vmDomainFile - non-null module to lookup
834//
835// Returns:
836// a CordbModule object for the given cookie. Object may be from the cache, or created
837// lazily.
838// Never returns null. Throws on error.
839//
840// Notes:
841// If you don't know which appdomain the module is in, use code:CordbProcess::LookupOrCreateModule.
842//
843CordbModule* CordbAppDomain::LookupOrCreateModule(VMPTR_Module vmModule, VMPTR_DomainFile vmDomainFile)
844{
845 INTERNAL_API_ENTRY(this);
846 CordbModule * pModule;
847
848 RSLockHolder lockHolder(GetProcess()->GetProcessLock()); // @dbgtodo locking: push this up.
849
850 _ASSERTE(!vmDomainFile.IsNull() || !vmModule.IsNull());
851
852 // check to see if the module is present in this app domain
853 pModule = m_modules.GetBase(vmDomainFile.IsNull() ? VmPtrToCookie(vmModule) : VmPtrToCookie(vmDomainFile));
854 if (pModule != NULL)
855 {
856 return pModule;
857 }
858
859 if (vmModule.IsNull())
860 GetProcess()->GetDAC()->GetModuleForDomainFile(vmDomainFile, &vmModule);
861
862 RSInitHolder<CordbModule> pModuleInit(new CordbModule(GetProcess(), vmModule, vmDomainFile));
863 pModule = pModuleInit.TransferOwnershipToHash(&m_modules);
864
865 // The appdomains should match.
866 GetProcess()->TargetConsistencyCheck(pModule->GetAppDomain() == this);
867
868 return pModule;
869}
870
871
872CordbModule* CordbAppDomain::LookupOrCreateModule(VMPTR_DomainFile vmDomainFile)
873{
874 INTERNAL_API_ENTRY(this);
875
876 _ASSERTE(!vmDomainFile.IsNull());
877 return LookupOrCreateModule(VMPTR_Module::NullPtr(), vmDomainFile);
878}
879
880
881
882//---------------------------------------------------------------------------------------
883// Callback invoked by DAC for each module in an assembly. Used to populate RS module cache.
884//
885// Arguments:
886// vmModule - module from enumeration
887// pUserData - user data, a 'this' pointer to the CordbAssembly to add to.
888//
889// Notes:
890// This is called from code:CordbAppDomain::PrepopulateModules invoking DAC, which
891// invokes this callback.
892
893// static
894void CordbAppDomain::ModuleEnumerationCallback(VMPTR_DomainFile vmModule, void * pUserData)
895{
896 CONTRACTL
897 {
898 THROWS;
899 }
900 CONTRACTL_END;
901
902 CordbAppDomain * pAppDomain = static_cast<CordbAppDomain *> (pUserData);
903 INTERNAL_DAC_CALLBACK(pAppDomain->GetProcess());
904
905 pAppDomain->LookupOrCreateModule(vmModule);
906}
907
908
909//
910// Use DAC to preopulate the list of modules for this assembly
911//
912// Notes:
913// This may pick up modules for which a load notification has not yet been dispatched.
914void CordbAppDomain::PrepopulateModules()
915{
916 INTERNAL_API_ENTRY(GetProcess());
917
918 if (!GetProcess()->IsDacInitialized())
919 {
920 return;
921 }
922
923 RSLockHolder lockHolder(GetProcess()->GetProcessLock());
924
925 // Want to make sure we don't double-add modules.
926 // Modules for all assemblies are stored in 1 giant hash in the AppDomain, so
927 // we don't have a good way of querying if this specific assembly needs to be prepopulated.
928 // We'll check before adding each module that it's unique.
929
930 PrepopulateAssembliesOrThrow();
931
932 HASHFIND hashfind;
933
934 for (CordbAssembly * pAssembly = m_assemblies.FindFirst(&hashfind);
935 pAssembly != NULL;
936 pAssembly = m_assemblies.FindNext(&hashfind))
937 {
938
939 // DD-primitive that invokes a callback.
940 GetProcess()->GetDAC()->EnumerateModulesInAssembly(
941 pAssembly->GetDomainAssemblyPtr(),
942 CordbAppDomain::ModuleEnumerationCallback,
943 this); // user data
944
945 }
946}
947
948//-----------------------------------------------------------------------------
949//
950// Get a type that represents an array or pointer of the given type.
951//
952// Arguments:
953// elementType - determines if this will be an array or a pointer
954// nRank - Rank of the array to make
955// pTypeArg - The type of array element or pointer.
956// ppResultType - OUT: out parameter to hold outgoing CordbType.
957//
958// Returns:
959// S_OK on success. Else failure.
960//
961HRESULT CordbAppDomain::GetArrayOrPointerType(CorElementType elementType,
962 ULONG32 nRank,
963 ICorDebugType * pTypeArg,
964 ICorDebugType ** ppResultType)
965{
966 PUBLIC_API_ENTRY(this);
967 FAIL_IF_NEUTERED(this);
968 VALIDATE_POINTER_TO_OBJECT(ppResultType, ICorDebugType **);
969 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
970
971 CordbType * pResultType = NULL;
972
973 if (!(elementType == ELEMENT_TYPE_PTR && nRank == 0) &&
974 !(elementType == ELEMENT_TYPE_BYREF && nRank == 0) &&
975 !(elementType == ELEMENT_TYPE_SZARRAY && nRank == 1) &&
976 !(elementType == ELEMENT_TYPE_ARRAY))
977 {
978 return E_INVALIDARG;
979 }
980
981 HRESULT hr = CordbType::MkType(
982 this,
983 elementType,
984 (ULONG) nRank,
985 static_cast<CordbType *>(pTypeArg),
986 &pResultType);
987
988 if (FAILED(hr))
989 {
990 return hr;
991 }
992
993 _ASSERTE(pResultType != NULL);
994
995 pResultType->ExternalAddRef();
996
997 *ppResultType = pResultType;
998 return hr;
999
1000}
1001
1002
1003//-----------------------------------------------------------------------------
1004//
1005// Get a type that represents a function pointer with signature of the types given.
1006//
1007// Arguments:
1008// cTypeArgs - count of the number of entries in rgpTypeArgs
1009// rgpTypeArgs - Array of types
1010// ppResultType - OUT: out parameter to hold outgoing CordbType.
1011//
1012// Returns:
1013// S_OK on success. Else failure.
1014//
1015HRESULT CordbAppDomain::GetFunctionPointerType(ULONG32 cTypeArgs,
1016 ICorDebugType * rgpTypeArgs[],
1017 ICorDebugType ** ppResultType)
1018{
1019 PUBLIC_API_ENTRY(this);
1020 FAIL_IF_NEUTERED(this);
1021 VALIDATE_POINTER_TO_OBJECT(ppResultType, ICorDebugType **);
1022 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
1023
1024 // Prefast overflow check:
1025 S_UINT32 allocSize = S_UINT32(cTypeArgs) * S_UINT32(sizeof(CordbType *));
1026
1027 if (allocSize.IsOverflow())
1028 {
1029 return E_INVALIDARG;
1030 }
1031
1032 CordbType ** ppTypeInstantiations = reinterpret_cast<CordbType **>(_alloca(allocSize.Value()));
1033
1034 for (unsigned int i = 0; i < cTypeArgs; i++)
1035 {
1036 ppTypeInstantiations[i] = (CordbType *) rgpTypeArgs[i];
1037 }
1038
1039
1040 Instantiation typeInstantiation(cTypeArgs, ppTypeInstantiations);
1041
1042 CordbType * pType;
1043
1044 HRESULT hr = CordbType::MkType(this, ELEMENT_TYPE_FNPTR, &typeInstantiation, &pType);
1045
1046 if (FAILED(hr))
1047 {
1048 return hr;
1049 }
1050
1051 _ASSERTE(pType != NULL);
1052
1053 pType->ExternalAddRef();
1054
1055 *ppResultType = static_cast<ICorDebugType *>(pType);
1056
1057 return hr;
1058
1059}
1060
1061//
1062// ICorDebugAppDomain3
1063//
1064
1065HRESULT CordbAppDomain::GetCachedWinRTTypesForIIDs(
1066 ULONG32 cGuids,
1067 GUID * iids,
1068 ICorDebugTypeEnum * * ppTypesEnum)
1069{
1070#if !defined(FEATURE_COMINTEROP)
1071
1072 return E_NOTIMPL;
1073
1074#else
1075
1076 HRESULT hr = S_OK;
1077 PUBLIC_API_ENTRY(this);
1078 FAIL_IF_NEUTERED(this);
1079 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
1080
1081 _ASSERTE(!m_vmAppDomain.IsNull());
1082
1083
1084 EX_TRY
1085 {
1086 *ppTypesEnum = NULL;
1087
1088 DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> dacTypes;
1089 DacDbiArrayList<GUID> dacGuids;
1090
1091 IDacDbiInterface* pDAC = GetProcess()->GetDAC();
1092
1093 dacGuids.Init(iids, cGuids);
1094
1095 // retrieve type info from LS
1096 pDAC->GetCachedWinRTTypesForIIDs(m_vmAppDomain, dacGuids, &dacTypes);
1097
1098 // synthesize CordbType instances
1099 int cItfs = dacTypes.Count();
1100 NewArrayHolder<CordbType*> pTypes(NULL);
1101
1102 if (cItfs > 0)
1103 {
1104 pTypes = new CordbType*[cItfs];
1105 for (int n = 0; n < cItfs; ++n)
1106 {
1107 hr = CordbType::TypeDataToType(this,
1108 &(dacTypes[n]),
1109 &pTypes[n]);
1110 }
1111 }
1112
1113 // build a type enumerator
1114 CordbTypeEnum* pTypeEnum = CordbTypeEnum::Build(this, GetProcess()->GetContinueNeuterList(), cItfs, pTypes);
1115 if ( pTypeEnum == NULL )
1116 {
1117 IfFailThrow(E_OUTOFMEMORY);
1118 }
1119
1120 (*ppTypesEnum) = static_cast<ICorDebugTypeEnum*> (pTypeEnum);
1121 pTypeEnum->ExternalAddRef();
1122
1123 }
1124 EX_CATCH_HRESULT(hr);
1125
1126 return hr;
1127
1128#endif // !defined(FEATURE_COMINTEROP)
1129}
1130
1131HRESULT CordbAppDomain::GetCachedWinRTTypes(
1132 ICorDebugGuidToTypeEnum * * ppTypesEnum)
1133{
1134#if !defined(FEATURE_COMINTEROP)
1135
1136 return E_NOTIMPL;
1137
1138#else
1139
1140 HRESULT hr = S_OK;
1141 PUBLIC_API_ENTRY(this);
1142 FAIL_IF_NEUTERED(this);
1143 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
1144
1145 EX_TRY
1146 {
1147 *ppTypesEnum = NULL;
1148
1149 DacDbiArrayList<GUID> guids;
1150 DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> types;
1151
1152 IDacDbiInterface* pDAC = GetProcess()->GetDAC();
1153
1154 // retrieve type info from LS
1155 pDAC->GetCachedWinRTTypes(m_vmAppDomain, &guids, &types);
1156
1157 _ASSERTE(guids.Count() == types.Count());
1158 if (guids.Count() != types.Count())
1159 IfFailThrow(E_FAIL);
1160
1161 int cnt = types.Count();
1162
1163 RsGuidToTypeMapping* pMap = new RsGuidToTypeMapping[cnt];
1164
1165 for (int i = 0; i < cnt; ++i)
1166 {
1167 pMap[i].iid = guids[i];
1168 CordbType* pType;
1169 hr = CordbType::TypeDataToType(this, &(types[i]), &pType);
1170 if (SUCCEEDED(hr))
1171 {
1172 pMap[i].spType.Assign(pType);
1173 }
1174 else
1175 {
1176 // spType stays NULL
1177 }
1178 }
1179
1180 CordbGuidToTypeEnumerator * enumerator = new CordbGuidToTypeEnumerator(GetProcess(), &pMap, cnt);
1181 _ASSERTE(pMap == NULL);
1182 GetProcess()->GetContinueNeuterList()->Add(GetProcess(), enumerator);
1183
1184 hr = enumerator->QueryInterface(IID_ICorDebugGuidToTypeEnum, reinterpret_cast<void**>(ppTypesEnum));
1185 _ASSERTE(SUCCEEDED(hr));
1186 IfFailThrow(hr);
1187
1188 }
1189 EX_CATCH_HRESULT(hr);
1190
1191 return hr;
1192
1193#endif // !defined(FEATURE_COMINTEROP)
1194}
1195
1196//-----------------------------------------------------------
1197// ICorDebugAppDomain4
1198//-----------------------------------------------------------
1199
1200HRESULT CordbAppDomain::GetObjectForCCW(CORDB_ADDRESS ccwPointer, ICorDebugValue **ppManagedObject)
1201{
1202#if defined(FEATURE_COMINTEROP)
1203
1204 PUBLIC_API_ENTRY(this);
1205 FAIL_IF_NEUTERED(this);
1206
1207 VALIDATE_POINTER_TO_OBJECT(ppManagedObject, ICorDebugValue **);
1208 HRESULT hr = S_OK;
1209
1210 *ppManagedObject = NULL;
1211
1212 EX_TRY
1213 {
1214 VMPTR_OBJECTHANDLE vmObjHandle = GetProcess()->GetDAC()->GetObjectForCCW(ccwPointer);
1215 if (vmObjHandle.IsNull())
1216 {
1217 hr = E_INVALIDARG;
1218 }
1219 else
1220 {
1221 ICorDebugReferenceValue *pRefValue = NULL;
1222 hr = CordbReferenceValue::BuildFromGCHandle(this, vmObjHandle, &pRefValue);
1223 *ppManagedObject = pRefValue;
1224 }
1225 }
1226 EX_CATCH_HRESULT(hr);
1227
1228 return hr;
1229
1230#else
1231
1232 return E_NOTIMPL;
1233
1234#endif // defined(FEATURE_COMINTEROP)
1235}
1236