diff options
Diffstat (limited to 'src/debug/di/rsappdomain.cpp')
-rw-r--r-- | src/debug/di/rsappdomain.cpp | 1235 |
1 files changed, 1235 insertions, 0 deletions
diff --git a/src/debug/di/rsappdomain.cpp b/src/debug/di/rsappdomain.cpp new file mode 100644 index 0000000000..fdfe657c57 --- /dev/null +++ b/src/debug/di/rsappdomain.cpp @@ -0,0 +1,1235 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// File: RsAppDomain.cpp +// + +// +//***************************************************************************** +#include "stdafx.h" +#include "primitives.h" +#include "safewrap.h" + +#include "check.h" + +#include <tlhelp32.h> +#include "wtsapi32.h" + +#ifndef SM_REMOTESESSION +#define SM_REMOTESESSION 0x1000 +#endif + +#include "corpriv.h" +#include "../../dlls/mscorrc/resource.h" +#include <limits.h> + + +/* ------------------------------------------------------------------------- * + * AppDomain class methods + * ------------------------------------------------------------------------- */ + +// +// Create a CordbAppDomain object based on a pointer to the AppDomain instance +// in the CLR. Pre-populates some cached information about the AppDomain +// from the CLR using DAC. +// +// Arguments: +// pProcess - the CordbProcess object that this AppDomain is part of +// vmAppDomain - the address in the CLR of the AppDomain object this corresponds to. +// This will be used to read any additional information about the AppDomain. +// +// Assumptions: +// The IMetaSig object should have been allocated by +// IMDInternal on a valid metadata blob +// +// +CordbAppDomain::CordbAppDomain(CordbProcess * pProcess, VMPTR_AppDomain vmAppDomain) + : CordbBase(pProcess, LsPtrToCookie(vmAppDomain.ToLsPtr()), enumCordbAppDomain), + m_AppDomainId(0), + m_breakpoints(17), + m_sharedtypes(3), + m_modules(17), + m_assemblies(9), + m_vmAppDomain(vmAppDomain) +{ + // This may throw out of the Ctor on error. + + // @dbgtodo reliability: we should probably tolerate failures here and keep track + // of whether our ADID is valid or not, and requery if necessary. + m_AppDomainId = m_pProcess->GetDAC()->GetAppDomainId(m_vmAppDomain); + + LOG((LF_CORDB,LL_INFO10000, "CAD::CAD: this:0x%x (void*)this:0x%x<%d>\n", this, (void *)this, m_AppDomainId)); + +#ifdef _DEBUG + m_assemblies.DebugSetRSLock(pProcess->GetProcessLock()); + m_modules.DebugSetRSLock(pProcess->GetProcessLock()); + m_breakpoints.DebugSetRSLock(pProcess->GetProcessLock()); + m_sharedtypes.DebugSetRSLock(pProcess->GetProcessLock()); +#endif + +} + +/* + A list of which resources owened by this object are accounted for. + + RESOLVED: + // AddRef() in CordbHashTable::GetBase for a special InProc case + // AddRef() on the DB_IPCE_CREATE_APP_DOMAIN event from the LS + // Release()ed in Neuter + CordbProcess *m_pProcess; + + WCHAR *m_szAppDomainName; // Deleted in ~CordbAppDomain + + // Cleaned up in Neuter + CordbHashTable m_assemblies; + CordbHashTable m_sharedtypes; + CordbHashTable m_modules; + CordbHashTable m_breakpoints; // Disconnect()ed in ~CordbAppDomain + + private: +*/ + +CordbAppDomain::~CordbAppDomain() +{ + + // We expect to be Neutered before being released. Neutering will release our process ref + _ASSERTE(IsNeutered()); +} + + +// Neutered by process. Once we're neutered, we lose our backpointer to the CordbProcess object, and +// thus can't do things like call GetProcess() or Continue(). +void CordbAppDomain::Neuter() +{ + // This check prevents us from calling this twice and underflowing the internal ref count! + if (IsNeutered()) + { + return; + } + _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); + + // + // Disconnect any active breakpoints + // + { + CordbBreakpoint* entry; + HASHFIND find; + + for (entry = m_breakpoints.FindFirst(&find); + entry != NULL; + entry = m_breakpoints.FindNext(&find)) + { + entry->Disconnect(); + } + } + + // Mark as neutered so that our children can tell the appdomain has now + // exited. + CordbBase::Neuter(); + + // + // Purge neuter lists. + // + m_TypeNeuterList.NeuterAndClear(GetProcess()); + m_SweepableNeuterList.NeuterAndClear(GetProcess()); + + + m_assemblies.NeuterAndClear(GetProcess()->GetProcessLock()); + m_modules.NeuterAndClear(GetProcess()->GetProcessLock()); + m_sharedtypes.NeuterAndClear(GetProcess()->GetProcessLock()); + m_breakpoints.NeuterAndClear(GetProcess()->GetProcessLock()); + +} + + +HRESULT CordbAppDomain::QueryInterface(REFIID id, void **ppInterface) +{ + if (id == IID_ICorDebugAppDomain) + { + *ppInterface = (ICorDebugAppDomain*)this; + } + else if (id == IID_ICorDebugAppDomain2) + { + *ppInterface = (ICorDebugAppDomain2*)this; + } + else if (id == IID_ICorDebugAppDomain3) + { + *ppInterface = (ICorDebugAppDomain3*)this; + } + else if (id == IID_ICorDebugAppDomain4) + { + *ppInterface = (ICorDebugAppDomain4*)this; + } + else if (id == IID_ICorDebugController) + *ppInterface = (ICorDebugController*)(ICorDebugAppDomain*)this; + else if (id == IID_IUnknown) + *ppInterface = (IUnknown*)(ICorDebugAppDomain*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + ExternalAddRef(); + return S_OK; +} + + +//--------------------------------------------------------------------------------------- +// +// Ensure the AppDomain friendly name has been set. +// +// Return value: +// S_OK on success, or a failure code if we couldn't read the name for some reason. +// There shouldn't be any reason in practice for this to fail other than a corrupt +// process image. +// +// Assumptions: +// The AppDomain object has already been initialized to know about +// it's corresponding VM appdomain. +// InvalidateName is called whenever the name may have changed to prompt us to re-fetch. +// +//--------------------------------------------------------------------------------------- +HRESULT CordbAppDomain::RefreshName() +{ + if (m_strAppDomainName.IsSet()) + { + // If we already have a valid name, we're done. + return S_OK; + } + + // Use DAC to get the name. + + _ASSERTE(!m_vmAppDomain.IsNull()); + IDacDbiInterface * pDac = NULL; + HRESULT hr = S_OK; + EX_TRY + { + pDac = m_pProcess->GetDAC(); + + #ifdef _DEBUG + // For debug, double-check the cached value against getting the AD via an AppDomainId. + VMPTR_AppDomain pAppDomain = pDac->GetAppDomainFromId(m_AppDomainId); + _ASSERTE(m_vmAppDomain == pAppDomain); + #endif + + // Get the actual string contents. + pDac->GetAppDomainFullName(m_vmAppDomain, &m_strAppDomainName); + + // Now that m_strAppDomainName is set, don't fail without clearing it. + } + EX_CATCH_HRESULT(hr); + + _ASSERTE(SUCCEEDED(hr) == m_strAppDomainName.IsSet()); + + return hr; +} + + +HRESULT CordbAppDomain::Stop(DWORD dwTimeout) +{ + FAIL_IF_NEUTERED(this); + PUBLIC_API_ENTRY(this); + return (m_pProcess->StopInternal(dwTimeout, this->GetADToken())); +} + +HRESULT CordbAppDomain::Continue(BOOL fIsOutOfBand) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + return m_pProcess->ContinueInternal(fIsOutOfBand); +} + +HRESULT CordbAppDomain::IsRunning(BOOL *pbRunning) +{ + PUBLIC_API_ENTRY(this); + VALIDATE_POINTER_TO_OBJECT(pbRunning, BOOL *); + FAIL_IF_NEUTERED(this); + + *pbRunning = !m_pProcess->GetSynchronized(); + + return S_OK; +} + +HRESULT CordbAppDomain::HasQueuedCallbacks(ICorDebugThread *pThread, BOOL *pbQueued) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + + VALIDATE_POINTER_TO_OBJECT_OR_NULL(pThread,ICorDebugThread *); + VALIDATE_POINTER_TO_OBJECT(pbQueued,BOOL *); + + return m_pProcess->HasQueuedCallbacks (pThread, pbQueued); +} + +HRESULT CordbAppDomain::EnumerateThreads(ICorDebugThreadEnum **ppThreads) +{ + // @TODO E_NOIMPL this + // + // (use Process::EnumerateThreads and let users filter their own data) + HRESULT hr = S_OK; + PUBLIC_API_BEGIN(this); + { + ValidateOrThrow(ppThreads); + + RSInitHolder<CordbEnumFilter> pThreadEnum( + new CordbEnumFilter(GetProcess(), GetProcess()->GetContinueNeuterList())); + + GetProcess()->PrepopulateThreadsOrThrow(); + + RSInitHolder<CordbHashTableEnum> pEnum; + GetProcess()->BuildThreadEnum(this, NULL, pEnum.GetAddr()); + + // This builds up auxillary list. don't need pEnum after this. + hr = pThreadEnum->Init(pEnum, this); + IfFailThrow(hr); + + pThreadEnum.TransferOwnershipExternal(ppThreads); + } + PUBLIC_API_END(hr); + return hr; +} + + +HRESULT CordbAppDomain::SetAllThreadsDebugState(CorDebugThreadState state, + ICorDebugThread *pExceptThisThread) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + + return m_pProcess->SetAllThreadsDebugState(state, pExceptThisThread); +} + +HRESULT CordbAppDomain::Detach() +{ + PUBLIC_REENTRANT_API_ENTRY(this); // may be called from IMDA::Detach + FAIL_IF_NEUTERED(this); + + return E_NOTIMPL; +} + +HRESULT CordbAppDomain::Terminate(unsigned int exitCode) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + return E_NOTIMPL; +} + +void CordbAppDomain::AddToTypeList(CordbBase *pObject) +{ + INTERNAL_API_ENTRY(this); + _ASSERTE(pObject != NULL); + RSLockHolder lockHolder(GetProcess()->GetProcessLock()); + this->m_TypeNeuterList.Add(GetProcess(), pObject); +} + + +HRESULT CordbAppDomain::CanCommitChanges( + ULONG cSnapshots, + ICorDebugEditAndContinueSnapshot *pSnapshots[], + ICorDebugErrorInfoEnum **pError) +{ + return E_NOTIMPL; +} + +HRESULT CordbAppDomain::CommitChanges( + ULONG cSnapshots, + ICorDebugEditAndContinueSnapshot *pSnapshots[], + ICorDebugErrorInfoEnum **pError) +{ + return E_NOTIMPL; +} + + +/* + * GetProcess returns the process containing the app domain + */ +HRESULT CordbAppDomain::GetProcess(ICorDebugProcess **ppProcess) +{ + PUBLIC_REENTRANT_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + + VALIDATE_POINTER_TO_OBJECT(ppProcess,ICorDebugProcess **); + + _ASSERTE (m_pProcess != NULL); + + *ppProcess = static_cast<ICorDebugProcess *> (m_pProcess); + m_pProcess->ExternalAddRef(); + + return S_OK; +} + +//--------------------------------------------------------------------------------------- +// +// Callback for assembly enumeration. +// +// Arguments: +// vmDomainAssembly - new assembly to add +// pThis - user data for CordbAppDomain to add assembly too +// +// +// Assumptions: +// Invoked as callback from code:CordbAppDomain::PrepopulateAssemblies +// +// Notes: +// + +// static +void CordbAppDomain::AssemblyEnumerationCallback(VMPTR_DomainAssembly vmDomainAssembly, void * pThis) +{ + CordbAppDomain * pAppDomain = static_cast<CordbAppDomain *> (pThis); + INTERNAL_DAC_CALLBACK(pAppDomain->GetProcess()); + + // This lookup will cause the cache to be populated if we haven't seen this assembly before. + pAppDomain->LookupOrCreateAssembly(vmDomainAssembly); +} + + +//--------------------------------------------------------------------------------------- +// +// Cache a new assembly +// +// Arguments: +// vmDomainAssembly - new assembly to add to cache +// +// Return Value: +// Pointer to Assembly in cache. +// NULL on failure, and sets unrecoverable error. +// +// Assumptions: +// Caller gaurantees assembly is not already added. +// Called under the stop-go lock. +// +// Notes: +// +CordbAssembly * CordbAppDomain::CacheAssembly(VMPTR_DomainAssembly vmDomainAssembly) +{ + INTERNAL_API_ENTRY(GetProcess()); + + VMPTR_Assembly vmAssembly; + GetProcess()->GetDAC()->GetAssemblyFromDomainAssembly(vmDomainAssembly, &vmAssembly); + + RSInitHolder<CordbAssembly> pAssembly(new CordbAssembly(this, vmAssembly, vmDomainAssembly)); + + return pAssembly.TransferOwnershipToHash(&m_assemblies); +} + +CordbAssembly * CordbAppDomain::CacheAssembly(VMPTR_Assembly vmAssembly) +{ + INTERNAL_API_ENTRY(GetProcess()); + + RSInitHolder<CordbAssembly> pAssembly(new CordbAssembly(this, vmAssembly, VMPTR_DomainAssembly())); + + return pAssembly.TransferOwnershipToHash(&m_assemblies); +} + +//--------------------------------------------------------------------------------------- +// +// Build up cache of assmeblies +// +// Arguments: +// +// Return Value: +// Throws on error. +// +// Assumptions: +// This is an non-invasive inspection operation called when the debuggee is stopped. +// +// Notes: +// This can safely be called multiple times. +// + +void CordbAppDomain::PrepopulateAssembliesOrThrow() +{ + INTERNAL_API_ENTRY(GetProcess()); + + RSLockHolder lockHolder(GetProcess()->GetProcessLock()); + + if (!GetProcess()->IsDacInitialized()) + { + return; + } + + // DD-primitive that invokes a callback. + GetProcess()->GetDAC()->EnumerateAssembliesInAppDomain( + this->m_vmAppDomain, + CordbAppDomain::AssemblyEnumerationCallback, + this); // user data +} + +//--------------------------------------------------------------------------------------- +// +// Public API tp EnumerateAssemblies enumerates all assemblies in the app domain +// +// Arguments: +// ppAssemblies - OUT: get enumerator +// +// Return Value: +// S_OK on success. +// +// +// Notes: +// This will prepopulate the list of assemblies (useful for non-invasive case +// where we don't get debug event). +// + +HRESULT CordbAppDomain::EnumerateAssemblies(ICorDebugAssemblyEnum **ppAssemblies) +{ + HRESULT hr = S_OK; + PUBLIC_API_BEGIN(this); + { + ValidateOrThrow(ppAssemblies); + *ppAssemblies = NULL; + + PrepopulateAssembliesOrThrow(); + + RSInitHolder<CordbHashTableEnum> pEnum; + CordbHashTableEnum::BuildOrThrow( + this, + GetProcess()->GetContinueNeuterList(), // ownership + &m_assemblies, + IID_ICorDebugAssemblyEnum, + pEnum.GetAddr()); + pEnum.TransferOwnershipExternal(ppAssemblies); + + } + PUBLIC_API_END(hr); + return hr; +} + +// Implement public interface +HRESULT CordbAppDomain::GetModuleFromMetaDataInterface( + IUnknown *pIMetaData, + ICorDebugModule **ppModule) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(pIMetaData, IUnknown *); + VALIDATE_POINTER_TO_OBJECT(ppModule, ICorDebugModule **); + + + + HRESULT hr = S_OK; + + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + + *ppModule = NULL; + + EX_TRY + { + CordbModule * pModule = GetModuleFromMetaDataInterface(pIMetaData); + _ASSERTE(pModule != NULL); // thrown on error + + *ppModule = static_cast<ICorDebugModule*> (pModule); + pModule->ExternalAddRef(); + } + EX_CATCH_HRESULT(hr); + + + return hr; +} + +// Gets a CordbModule that has the given metadata interface +// +// Arguments: +// pIMetaData - metadata interface +// +// Returns: +// CordbModule whose associated metadata matches the metadata interface provided here +// Throws on error. Returns non-null +// +CordbModule * CordbAppDomain::GetModuleFromMetaDataInterface(IUnknown *pIMetaData) +{ + HRESULT hr = S_OK; + + RSExtSmartPtr<IMetaDataImport> pImport; + RSLockHolder lockHolder(GetProcess()->GetProcessLock()); // need for module enumeration + + // Grab the interface we need... + hr = pIMetaData->QueryInterface(IID_IMetaDataImport, (void**)&pImport); + if (FAILED(hr)) + { + ThrowHR(E_INVALIDARG); + } + + // Get the mvid of the given module. + GUID matchMVID; + hr = pImport->GetScopeProps(NULL, 0, 0, &matchMVID); + IfFailThrow(hr); + + CordbModule* pModule; + HASHFIND findmodule; + + PrepopulateModules(); + + for (pModule = m_modules.FindFirst(&findmodule); + pModule != NULL; + pModule = m_modules.FindNext(&findmodule)) + { + IMetaDataImport * pImportCurrent = pModule->GetMetaDataImporter(); // throws + _ASSERTE(pImportCurrent != NULL); + + // Get the mvid of this module + GUID MVID; + hr = pImportCurrent->GetScopeProps(NULL, 0, 0, &MVID); + IfFailThrow(hr); + + if (MVID == matchMVID) + { + return pModule; + } + } + + ThrowHR(E_INVALIDARG); +} + +HRESULT CordbAppDomain::EnumerateBreakpoints(ICorDebugBreakpointEnum **ppBreakpoints) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + VALIDATE_POINTER_TO_OBJECT(ppBreakpoints, ICorDebugBreakpointEnum **); + + HRESULT hr = S_OK; + EX_TRY + { + RSInitHolder<CordbHashTableEnum> pEnum; + CordbHashTableEnum::BuildOrThrow( + this, + GetProcess()->GetContinueNeuterList(), // ownership + &m_breakpoints, + IID_ICorDebugBreakpointEnum, + pEnum.GetAddr()); + + pEnum.TransferOwnershipExternal(ppBreakpoints); + } + EX_CATCH_HRESULT(hr); + return hr; +} + +HRESULT CordbAppDomain::EnumerateSteppers(ICorDebugStepperEnum **ppSteppers) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + VALIDATE_POINTER_TO_OBJECT(ppSteppers,ICorDebugStepperEnum **); + + HRESULT hr = S_OK; + EX_TRY + { + // + // !!! m_steppers may be modified while user is enumerating, + // if steppers complete (if process is running) + // + + RSInitHolder<CordbHashTableEnum> pEnum; + CordbHashTableEnum::BuildOrThrow( + GetProcess(), + GetProcess()->GetContinueNeuterList(), // ownership + &(m_pProcess->m_steppers), + IID_ICorDebugStepperEnum, + pEnum.GetAddr()); + + pEnum.TransferOwnershipExternal(ppSteppers); + } + EX_CATCH_HRESULT(hr); + return hr; +} + + +//--------------------------------------------------------------------------------------- +// +// CordbAppDomain::IsAttached - always returns true +// +// Arguments: +// pfAttached - out parameter, will be set to TRUE +// +// Return Value: +// CORDB_E_OBJECT_NEUTERED if the AppDomain has been neutered +// E_INVALIDARG if pbAttached is null +// Otherwise always returns S_OK. +// +// Notes: +// Prior to V3, we used to keep track of a per-appdomain attached status. +// Debuggers were required to explicitly attach to every AppDomain, so this +// did not provide any actual functionality. In V3, there is no longer any +// concept of per-AppDomain attach/detach. This API is provided for compatibility. +// + +HRESULT CordbAppDomain::IsAttached(BOOL *pfAttached) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(pfAttached, BOOL *); + + *pfAttached = TRUE; + + return S_OK; +} + + +//--------------------------------------------------------------------------------------- +// +// CordbAppDomain::Attach - does nothing +// +// Arguments: +// +// Return Value: +// CORDB_E_OBJECT_NEUTERED if the AppDomain has been neutered +// Otherwise always returns S_OK. +// +// Notes: +// Prior to V3, we used to keep track of a per-appdomain attached status. +// Debuggers were required to explicitly attach to every AppDomain, so this +// did not provide any actual functionality. In V3, there is no longer any +// concept of per-AppDomain attach/detach. This API is provided for compatibility. +// + +HRESULT CordbAppDomain::Attach() +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + ATT_REQUIRE_STOPPED_MAY_FAIL(m_pProcess); + + return S_OK; +} + +/* + * GetName returns the name of the app domain. + */ +HRESULT CordbAppDomain::GetName(ULONG32 cchName, + ULONG32 *pcchName, + __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]) +{ + HRESULT hr = S_OK; + PUBLIC_API_BEGIN(this) + { + // Some reasonable defaults + if (szName) + *szName = 0; + + if (pcchName) + *pcchName = 0; + + + // Lazily refresh. + IfFailThrow(RefreshName()); + + const WCHAR * pName = m_strAppDomainName; + _ASSERTE(pName != NULL); + + hr = CopyOutString(pName, cchName, pcchName, szName); + } + PUBLIC_API_END(hr); + return hr; +} + +/* + * GetObject returns the runtime app domain object. + * Note: this is lazily initialized and may be NULL + */ +HRESULT CordbAppDomain::GetObject(ICorDebugValue **ppObject) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(ppObject,ICorDebugObjectValue **); + + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + + _ASSERTE(!m_vmAppDomain.IsNull()); + IDacDbiInterface * pDac = NULL; + HRESULT hr = S_OK; + EX_TRY + { + pDac = m_pProcess->GetDAC(); + VMPTR_OBJECTHANDLE vmObjHandle = pDac->GetAppDomainObject(m_vmAppDomain); + if (!vmObjHandle.IsNull()) + { + ICorDebugReferenceValue * pRefValue = NULL; + hr = CordbReferenceValue::BuildFromGCHandle(this, vmObjHandle, &pRefValue); + *ppObject = pRefValue; + } + else + { + *ppObject = NULL; + hr = S_FALSE; + } + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +/* + * Get the ID of the app domain. + */ +HRESULT CordbAppDomain::GetID (ULONG32 *pId) +{ + PUBLIC_REENTRANT_API_ENTRY(this); + OK_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(pId, ULONG32 *); + + *pId = m_AppDomainId; + + return S_OK; +} + +//--------------------------------------------------------------------------------------- +// Remove an assembly from the ICorDebug cache. +// +// Arguments: +// vmDomainAssembly - token to remove. +// +// Notes: +// This is the opposite of code:CordbAppDomain::LookupOrCreateAssembly. +// This only need to be called at assembly unload events. +void CordbAppDomain::RemoveAssemblyFromCache(VMPTR_DomainAssembly vmDomainAssembly) +{ + // This will handle if the assembly is not in the hash. + // This could happen if we attach right before an assembly-unload event. + m_assemblies.RemoveBase(VmPtrToCookie(vmDomainAssembly)); +} + +//--------------------------------------------------------------------------------------- +// Lookup (or create) the CordbAssembly for the given VMPTR_DomainAssembly +// +// Arguments: +// vmDomainAssembly - CLR token for the Assembly. +// +// Returns: +// a CordbAssembly object for the given CLR assembly. This may be from the cache, +// or newly created if not yet in the cache. +// Never returns NULL. Throws on error (eg, oom). +// +CordbAssembly * CordbAppDomain::LookupOrCreateAssembly(VMPTR_DomainAssembly vmDomainAssembly) +{ + CordbAssembly * pAssembly = m_assemblies.GetBase(VmPtrToCookie(vmDomainAssembly)); + if (pAssembly != NULL) + { + return pAssembly; + } + return CacheAssembly(vmDomainAssembly); +} + + +// +CordbAssembly * CordbAppDomain::LookupOrCreateAssembly(VMPTR_Assembly vmAssembly) +{ + CordbAssembly * pAssembly = m_assemblies.GetBase(VmPtrToCookie(vmAssembly)); + if (pAssembly != NULL) + { + return pAssembly; + } + return CacheAssembly(vmAssembly); +} + + +//--------------------------------------------------------------------------------------- +// Lookup or create a module within the appdomain +// +// Arguments: +// vmDomainFile - non-null module to lookup +// +// Returns: +// a CordbModule object for the given cookie. Object may be from the cache, or created +// lazily. +// Never returns null. Throws on error. +// +// Notes: +// If you don't know which appdomain the module is in, use code:CordbProcess::LookupOrCreateModule. +// +CordbModule* CordbAppDomain::LookupOrCreateModule(VMPTR_Module vmModule, VMPTR_DomainFile vmDomainFile) +{ + INTERNAL_API_ENTRY(this); + CordbModule * pModule; + + RSLockHolder lockHolder(GetProcess()->GetProcessLock()); // @dbgtodo locking: push this up. + + _ASSERTE(!vmDomainFile.IsNull() || !vmModule.IsNull()); + + // check to see if the module is present in this app domain + pModule = m_modules.GetBase(vmDomainFile.IsNull() ? VmPtrToCookie(vmModule) : VmPtrToCookie(vmDomainFile)); + if (pModule != NULL) + { + return pModule; + } + + if (vmModule.IsNull()) + GetProcess()->GetDAC()->GetModuleForDomainFile(vmDomainFile, &vmModule); + + RSInitHolder<CordbModule> pModuleInit(new CordbModule(GetProcess(), vmModule, vmDomainFile)); + pModule = pModuleInit.TransferOwnershipToHash(&m_modules); + + // The appdomains should match. + GetProcess()->TargetConsistencyCheck(pModule->GetAppDomain() == this); + + return pModule; +} + + +CordbModule* CordbAppDomain::LookupOrCreateModule(VMPTR_DomainFile vmDomainFile) +{ + INTERNAL_API_ENTRY(this); + + _ASSERTE(!vmDomainFile.IsNull()); + return LookupOrCreateModule(VMPTR_Module::NullPtr(), vmDomainFile); +} + + + +//--------------------------------------------------------------------------------------- +// Callback invoked by DAC for each module in an assembly. Used to populate RS module cache. +// +// Arguments: +// vmModule - module from enumeration +// pUserData - user data, a 'this' pointer to the CordbAssembly to add to. +// +// Notes: +// This is called from code:CordbAppDomain::PrepopulateModules invoking DAC, which +// invokes this callback. + +// static +void CordbAppDomain::ModuleEnumerationCallback(VMPTR_DomainFile vmModule, void * pUserData) +{ + CONTRACTL + { + THROWS; + } + CONTRACTL_END; + + CordbAppDomain * pAppDomain = static_cast<CordbAppDomain *> (pUserData); + INTERNAL_DAC_CALLBACK(pAppDomain->GetProcess()); + + pAppDomain->LookupOrCreateModule(vmModule); +} + + +// +// Use DAC to preopulate the list of modules for this assembly +// +// Notes: +// This may pick up modules for which a load notification has not yet been dispatched. +void CordbAppDomain::PrepopulateModules() +{ + INTERNAL_API_ENTRY(GetProcess()); + + if (!GetProcess()->IsDacInitialized()) + { + return; + } + + RSLockHolder lockHolder(GetProcess()->GetProcessLock()); + + // Want to make sure we don't double-add modules. + // Modules for all assemblies are stored in 1 giant hash in the AppDomain, so + // we don't have a good way of querying if this specific assembly needs to be prepopulated. + // We'll check before adding each module that it's unique. + + PrepopulateAssembliesOrThrow(); + + HASHFIND hashfind; + + for (CordbAssembly * pAssembly = m_assemblies.FindFirst(&hashfind); + pAssembly != NULL; + pAssembly = m_assemblies.FindNext(&hashfind)) + { + + // DD-primitive that invokes a callback. + GetProcess()->GetDAC()->EnumerateModulesInAssembly( + pAssembly->GetDomainAssemblyPtr(), + CordbAppDomain::ModuleEnumerationCallback, + this); // user data + + } +} + +//----------------------------------------------------------------------------- +// +// Get a type that represents an array or pointer of the given type. +// +// Arguments: +// elementType - determines if this will be an array or a pointer +// nRank - Rank of the array to make +// pTypeArg - The type of array element or pointer. +// ppResultType - OUT: out parameter to hold outgoing CordbType. +// +// Returns: +// S_OK on success. Else failure. +// +HRESULT CordbAppDomain::GetArrayOrPointerType(CorElementType elementType, + ULONG32 nRank, + ICorDebugType * pTypeArg, + ICorDebugType ** ppResultType) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(ppResultType, ICorDebugType **); + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + + CordbType * pResultType = NULL; + + if (!(elementType == ELEMENT_TYPE_PTR && nRank == 0) && + !(elementType == ELEMENT_TYPE_BYREF && nRank == 0) && + !(elementType == ELEMENT_TYPE_SZARRAY && nRank == 1) && + !(elementType == ELEMENT_TYPE_ARRAY)) + { + return E_INVALIDARG; + } + + HRESULT hr = CordbType::MkType( + this, + elementType, + (ULONG) nRank, + static_cast<CordbType *>(pTypeArg), + &pResultType); + + if (FAILED(hr)) + { + return hr; + } + + _ASSERTE(pResultType != NULL); + + pResultType->ExternalAddRef(); + + *ppResultType = pResultType; + return hr; + +} + + +//----------------------------------------------------------------------------- +// +// Get a type that represents a function pointer with signature of the types given. +// +// Arguments: +// cTypeArgs - count of the number of entries in rgpTypeArgs +// rgpTypeArgs - Array of types +// ppResultType - OUT: out parameter to hold outgoing CordbType. +// +// Returns: +// S_OK on success. Else failure. +// +HRESULT CordbAppDomain::GetFunctionPointerType(ULONG32 cTypeArgs, + ICorDebugType * rgpTypeArgs[], + ICorDebugType ** ppResultType) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(ppResultType, ICorDebugType **); + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + + // Prefast overflow check: + S_UINT32 allocSize = S_UINT32(cTypeArgs) * S_UINT32(sizeof(CordbType *)); + + if (allocSize.IsOverflow()) + { + return E_INVALIDARG; + } + + CordbType ** ppTypeInstantiations = reinterpret_cast<CordbType **>(_alloca(allocSize.Value())); + + for (unsigned int i = 0; i < cTypeArgs; i++) + { + ppTypeInstantiations[i] = (CordbType *) rgpTypeArgs[i]; + } + + + Instantiation typeInstantiation(cTypeArgs, ppTypeInstantiations); + + CordbType * pType; + + HRESULT hr = CordbType::MkType(this, ELEMENT_TYPE_FNPTR, &typeInstantiation, &pType); + + if (FAILED(hr)) + { + return hr; + } + + _ASSERTE(pType != NULL); + + pType->ExternalAddRef(); + + *ppResultType = static_cast<ICorDebugType *>(pType); + + return hr; + +} + +// +// ICorDebugAppDomain3 +// + +HRESULT CordbAppDomain::GetCachedWinRTTypesForIIDs( + ULONG32 cGuids, + GUID * iids, + ICorDebugTypeEnum * * ppTypesEnum) +{ +#if !defined(FEATURE_COMINTEROP) + + return E_NOTIMPL; + +#else + + HRESULT hr = S_OK; + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + + _ASSERTE(!m_vmAppDomain.IsNull()); + + + EX_TRY + { + *ppTypesEnum = NULL; + + DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> dacTypes; + DacDbiArrayList<GUID> dacGuids; + + IDacDbiInterface* pDAC = GetProcess()->GetDAC(); + + dacGuids.Init(iids, cGuids); + + // retrieve type info from LS + pDAC->GetCachedWinRTTypesForIIDs(m_vmAppDomain, dacGuids, &dacTypes); + + // synthesize CordbType instances + int cItfs = dacTypes.Count(); + NewArrayHolder<CordbType*> pTypes(NULL); + + if (cItfs > 0) + { + pTypes = new CordbType*[cItfs]; + for (int n = 0; n < cItfs; ++n) + { + hr = CordbType::TypeDataToType(this, + &(dacTypes[n]), + &pTypes[n]); + } + } + + // build a type enumerator + CordbTypeEnum* pTypeEnum = CordbTypeEnum::Build(this, GetProcess()->GetContinueNeuterList(), cItfs, pTypes); + if ( pTypeEnum == NULL ) + { + IfFailThrow(E_OUTOFMEMORY); + } + + (*ppTypesEnum) = static_cast<ICorDebugTypeEnum*> (pTypeEnum); + pTypeEnum->ExternalAddRef(); + + } + EX_CATCH_HRESULT(hr); + + return hr; + +#endif // !defined(FEATURE_COMINTEROP) +} + +HRESULT CordbAppDomain::GetCachedWinRTTypes( + ICorDebugGuidToTypeEnum * * ppTypesEnum) +{ +#if !defined(FEATURE_COMINTEROP) + + return E_NOTIMPL; + +#else + + HRESULT hr = S_OK; + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + + EX_TRY + { + *ppTypesEnum = NULL; + + DacDbiArrayList<GUID> guids; + DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> types; + + IDacDbiInterface* pDAC = GetProcess()->GetDAC(); + + // retrieve type info from LS + pDAC->GetCachedWinRTTypes(m_vmAppDomain, &guids, &types); + + _ASSERTE(guids.Count() == types.Count()); + if (guids.Count() != types.Count()) + IfFailThrow(E_FAIL); + + int cnt = types.Count(); + + RsGuidToTypeMapping* pMap = new RsGuidToTypeMapping[cnt]; + + for (int i = 0; i < cnt; ++i) + { + pMap[i].iid = guids[i]; + CordbType* pType; + hr = CordbType::TypeDataToType(this, &(types[i]), &pType); + if (SUCCEEDED(hr)) + { + pMap[i].spType.Assign(pType); + } + else + { + // spType stays NULL + } + } + + CordbGuidToTypeEnumerator * enumerator = new CordbGuidToTypeEnumerator(GetProcess(), &pMap, cnt); + _ASSERTE(pMap == NULL); + GetProcess()->GetContinueNeuterList()->Add(GetProcess(), enumerator); + + hr = enumerator->QueryInterface(IID_ICorDebugGuidToTypeEnum, reinterpret_cast<void**>(ppTypesEnum)); + _ASSERTE(SUCCEEDED(hr)); + IfFailThrow(hr); + + } + EX_CATCH_HRESULT(hr); + + return hr; + +#endif // !defined(FEATURE_COMINTEROP) +} + +//----------------------------------------------------------- +// ICorDebugAppDomain4 +//----------------------------------------------------------- + +HRESULT CordbAppDomain::GetObjectForCCW(CORDB_ADDRESS ccwPointer, ICorDebugValue **ppManagedObject) +{ +#if defined(FEATURE_COMINTEROP) + + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + + VALIDATE_POINTER_TO_OBJECT(ppManagedObject, ICorDebugValue **); + HRESULT hr = S_OK; + + *ppManagedObject = NULL; + + EX_TRY + { + VMPTR_OBJECTHANDLE vmObjHandle = GetProcess()->GetDAC()->GetObjectForCCW(ccwPointer); + if (vmObjHandle.IsNull()) + { + hr = E_INVALIDARG; + } + else + { + ICorDebugReferenceValue *pRefValue = NULL; + hr = CordbReferenceValue::BuildFromGCHandle(this, vmObjHandle, &pRefValue); + *ppManagedObject = pRefValue; + } + } + EX_CATCH_HRESULT(hr); + + return hr; + +#else + + return E_NOTIMPL; + +#endif // defined(FEATURE_COMINTEROP) +} |