diff options
Diffstat (limited to 'src/debug/daccess/dacdbiimpl.cpp')
-rw-r--r-- | src/debug/daccess/dacdbiimpl.cpp | 7639 |
1 files changed, 7639 insertions, 0 deletions
diff --git a/src/debug/daccess/dacdbiimpl.cpp b/src/debug/daccess/dacdbiimpl.cpp new file mode 100644 index 0000000000..9b17f4cd46 --- /dev/null +++ b/src/debug/daccess/dacdbiimpl.cpp @@ -0,0 +1,7639 @@ +// 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: DacDbiImpl.cpp +// + +// +// Implement DAC/DBI interface +// +//***************************************************************************** + + +#include "stdafx.h" + +#include "dacdbiinterface.h" + +#include "typestring.h" +#include "holder.h" +#include "debuginfostore.h" +#include "peimagelayout.inl" +#include "encee.h" +#include "switches.h" +#include "generics.h" +#include "stackwalk.h" + +#include "dacdbiimpl.h" +#ifndef FEATURE_CORECLR +#include "assemblyusagelogmanager.h" +#endif + +#ifdef FEATURE_COMINTEROP +#include "runtimecallablewrapper.h" +#include "comcallablewrapper.h" +#endif // FEATURE_COMINTEROP + +//----------------------------------------------------------------------------- +// Have standard enter and leave macros at the DacDbi boundary to enforce +// standard behavior. +// 1. catch exceptions and convert them at the boundary. +// 2. provide a space to hook logging and transitions. +// 3. provide a hook to verify return values. +// +// Usage notes: +// - use this at the DacDbi boundary; but not at internal functions +// - it's ok to Return from the middle. +// +// Expected usage is: +// Foo() +// { +// DD_ENTER_MAY_THROW +// ... +// if (...) { ThrowHr(E_SOME_FAILURE); } +// ... +// if (...) { return; } // early success case +// ... +// } +//----------------------------------------------------------------------------- + + + + +// Global allocator for DD. Access is protected under the g_dacCritSec lock. +IDacDbiInterface::IAllocator * g_pAllocator = NULL; + +//--------------------------------------------------------------------------------------- +// +// Extra sugar for wrapping IAllocator under friendly New/Delete operators. +// +// Sample usage: +// void Foo(TestClass ** ppOut) +// { +// *ppOut = NULL; +// TestClass * p = new (forDbi) TestClass(); +// ... +// if (ok) +// { +// *ppOut = p; +// return; // DBI will then free this memory. +// } +// ... +// DeleteDbiMemory(p); +// } +// +// Be very careful when using this on classes since Dbi and DAC may be in +// separate dlls. This is best used when operating on blittable data-structures. +// (no ctor/dtor, plain data fields) to guarantee the proper DLL isolation. +// You don't want to call the ctor in DAC's context and the dtor in DBI's context +// unless you really know what you're doing and that it's safe. +// + +// Need a class to serve as a tag that we can use to overload New/Delete. +#define forDbi (*(forDbiWorker *)NULL) + +void * operator new(size_t lenBytes, const forDbiWorker &) +{ + _ASSERTE(g_pAllocator != NULL); + void *result = g_pAllocator->Alloc(lenBytes); + if (result == NULL) + { + ThrowOutOfMemory(); + } + return result; +} + +void * operator new[](size_t lenBytes, const forDbiWorker &) +{ + _ASSERTE(g_pAllocator != NULL); + void *result = g_pAllocator->Alloc(lenBytes); + if (result == NULL) + { + ThrowOutOfMemory(); + } + return result; +} + +// Note: there is no C++ syntax for manually invoking this, but if a constructor throws an exception I understand that +// this delete operator will be invoked automatically to destroy the object. +void operator delete(void *p, const forDbiWorker &) +{ + if (p == NULL) + { + return; + } + + _ASSERTE(g_pAllocator != NULL); + g_pAllocator->Free((BYTE*) p); + +} + +// Note: there is no C++ syntax for manually invoking this, but if a constructor throws an exception I understand that +// this delete operator will be invoked automatically to destroy the object. +void operator delete[](void *p, const forDbiWorker &) +{ + if (p == NULL) + { + return; + } + + _ASSERTE(g_pAllocator != NULL); + g_pAllocator->Free((BYTE*) p); +} + +// @dbgtodo dac support: determine how to handle an array of class instances to ensure the dtors get +// called correctly or document that they won't +// Delete memory and invoke dtor for memory allocated with 'operator (forDbi) new' +template<class T> void DeleteDbiMemory(T *p) +{ + if (p == NULL) + { + return; + } + p->~T(); + + _ASSERTE(g_pAllocator != NULL); + g_pAllocator->Free((BYTE*) p); +} + + +//--------------------------------------------------------------------------------------- +// Creates the DacDbiInterface object, used by Dbi. +// +// Arguments: +// pTarget - pointer to a Data-Target +// baseAddress - non-zero base address of mscorwks in target to debug. +// pAllocator - pointer to client allocator object. This lets DD allocate objects and +// pass them out back to the client, which can then delete them. +// DD takes a weak ref to this, so client must keep it alive until it +// calls Destroy. +// pMetadataLookup - callback interface to do internal metadata lookup. This is because +// metadata is not dac-ized. +// ppInterface - mandatory out-parameter +// +// Return Value: +// S_OK on success. +// +// +// Notes: +// On Windows, this is public function that can be retrieved by GetProcAddress. + +// On Mac, this is used internally by DacDbiMarshalStubInstance below +// This will yield an IDacDbiInterface to provide structured access to the +// data-target. +// +// Must call Destroy to on interface to free its resources. +// +//--------------------------------------------------------------------------------------- +STDAPI +DacDbiInterfaceInstance( + ICorDebugDataTarget * pTarget, + CORDB_ADDRESS baseAddress, + IDacDbiInterface::IAllocator * pAllocator, + IDacDbiInterface::IMetaDataLookup * pMetaDataLookup, + IDacDbiInterface ** ppInterface) +{ + // No marshalling is done by the instantiationf function - we just need to setup the infrastructure. + // We don't want to warn if this involves creating and accessing undacized data structures, + // because it's for the infrastructure, not DACized code itself. + SUPPORTS_DAC_HOST_ONLY; + + // Since this is public, verify it. + if ((ppInterface == NULL) || (pTarget == NULL) || (baseAddress == 0)) + { + return E_INVALIDARG; + } + + *ppInterface = NULL; + + // + // Actually allocate the real object and initialize it. + // + DacDbiInterfaceImpl * pDac = new (nothrow) DacDbiInterfaceImpl(pTarget, baseAddress, pAllocator, pMetaDataLookup); + if (!pDac) + { + return E_OUTOFMEMORY; + } + + HRESULT hrStatus = pDac->Initialize(); + + if (SUCCEEDED(hrStatus)) + { + *ppInterface = pDac; + } + else + { + delete pDac; + } + return hrStatus; +} + + +//--------------------------------------------------------------------------------------- +// Constructor. Instantiates a DAC/DBI interface around a DataTarget. +// +// Arguments: +// pTarget - pointer to a Data-Target +// baseAddress - non-zero base address of mscorwks in target to debug. +// pAllocator - pointer to client allocator object. This lets DD allocate objects and +// pass them out back to the client, which can then delete them. +// DD takes a weak ref to this, so client must keep it alive until it +// calls Destroy. +// pMetadataLookup - callback interface to do internal metadata lookup. This is because +// metadata is not dac-ized. +// +// Notes: +// pAllocator is a weak reference. +//--------------------------------------------------------------------------------------- +DacDbiInterfaceImpl::DacDbiInterfaceImpl( + ICorDebugDataTarget* pTarget, + CORDB_ADDRESS baseAddress, + IAllocator * pAllocator, + IMetaDataLookup * pMetaDataLookup +) : ClrDataAccess(pTarget), + m_pAllocator(pAllocator), + m_pMetaDataLookup(pMetaDataLookup), + m_pCachedPEFile(VMPTR_PEFile::NullPtr()), + m_pCachedImporter(NULL), + m_isCachedHijackFunctionValid(FALSE) +{ + _ASSERTE(baseAddress != NULL); + m_globalBase = CORDB_ADDRESS_TO_TADDR(baseAddress); + + _ASSERTE(pMetaDataLookup != NULL); + _ASSERTE(pAllocator != NULL); + _ASSERTE(pTarget != NULL); + +#ifdef _DEBUG + // Enable verification asserts in ICorDebug scenarios. ICorDebug never guesses at the DAC path, so any + // mismatch should be fatal, and so always of interest to the user. + // This overrides the assignment in the base class ctor (which runs first). + m_fEnableDllVerificationAsserts = true; +#endif +} + +//----------------------------------------------------------------------------- +// Destructor. +// +// Notes: +// This gets invoked after Destroy(). +//----------------------------------------------------------------------------- +DacDbiInterfaceImpl::~DacDbiInterfaceImpl() +{ + SUPPORTS_DAC_HOST_ONLY; + // This will automatically chain to the base class dtor +} + +//----------------------------------------------------------------------------- +// Called from DAC-ized code to get a IMDInternalImport +// +// Arguments: +// pPEFile - PE file for which to get importer for +// fThrowEx - if true, throw instead of returning NULL. +// +// Returns: +// an Internal importer object for this file. +// May return NULL or throw (depending on fThrowEx). +// May throw in exceptional circumstances (eg, corrupt debuggee). +// +// Assumptions: +// This is called from DAC-ized code within the VM, which +// was in turn called from some DD primitive. The returned importer will +// be used by the DAC-ized code in the callstack, but it won't be cached. +// +// Notes: +// This is an Internal importer, not a public Metadata importer. +// +interface IMDInternalImport* DacDbiInterfaceImpl::GetMDImport( + const PEFile* pPEFile, + const ReflectionModule * pReflectionModule, + bool fThrowEx) +{ + // Since this is called from an existing DAC-primitive, we already hold the g_dacCritSec lock. + // The lock conveniently protects our cache. + SUPPORTS_DAC; + + IDacDbiInterface::IMetaDataLookup * pLookup = m_pMetaDataLookup; + _ASSERTE(pLookup != NULL); + + VMPTR_PEFile vmPEFile = VMPTR_PEFile::NullPtr(); + + if (pPEFile != NULL) + { + vmPEFile.SetHostPtr(pPEFile); + } + else if (pReflectionModule != NULL) + { + // SOS and ClrDataAccess rely on special logic to find the metadata for methods in dynamic modules. + // We don't need to. The RS has already taken care of the special logic for us. + // So here we just grab the PEFile off of the ReflectionModule and continue down the normal + // code path. See code:ClrDataAccess::GetMDImport for comparison. + vmPEFile.SetHostPtr(pReflectionModule->GetFile()); + } + + // Optimize for the case where the VM queries the same Importer many times in a row. + if (m_pCachedPEFile == vmPEFile) + { + return m_pCachedImporter; + } + + // Go to DBI to find the metadata. + IMDInternalImport * pInternal = NULL; + bool isILMetaDataForNI = false; + EX_TRY + { + // If test needs it in the future, prop isILMetaDataForNI back up to + // ClrDataAccess.m_mdImports.Add() call. + // example in code:ClrDataAccess::GetMDImport + // CordbModule::GetMetaDataInterface also looks up MetaData and would need attention. + + // This is the new codepath that uses ICorDebugMetaDataLookup. + // To get the old codepath that uses the v2 metadata lookup methods, + // you'd have to load DAC only and then you'll get ClrDataAccess's implementation + // of this function. + pInternal = pLookup->LookupMetaData(vmPEFile, isILMetaDataForNI); + } + EX_CATCH + { + // Any expected error we should ignore. + if ((GET_EXCEPTION()->GetHR() != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) && + (GET_EXCEPTION()->GetHR() != CORDBG_E_READVIRTUAL_FAILURE) && + (GET_EXCEPTION()->GetHR() != CORDBG_E_SYMBOLS_NOT_AVAILABLE) && + (GET_EXCEPTION()->GetHR() != CORDBG_E_MODULE_LOADED_FROM_DISK)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + if (pInternal == NULL) + { + SIMPLIFYING_ASSUMPTION(!"MD lookup failed"); + if (fThrowEx) + { + ThrowHR(E_FAIL); + } + return NULL; + } + else + { + // Cache it such that it we look for the exact same Importer again, we'll return it. + m_pCachedPEFile = vmPEFile; + m_pCachedImporter = pInternal; + } + + return pInternal; +} + +//----------------------------------------------------------------------------- +// Implementation of IDacDbiInterface +// See DacDbiInterface.h for full descriptions of all of these functions +//----------------------------------------------------------------------------- + +// Destroy the connection, freeing up any resources. +void DacDbiInterfaceImpl::Destroy() +{ + m_pAllocator = NULL; + + this->Release(); + // Memory is deleted, don't access this object any more +} + +// Check whether the version of the DBI matches the version of the runtime. +// See code:CordbProcess::CordbProcess#DBIVersionChecking for more information regarding version checking. +HRESULT DacDbiInterfaceImpl::CheckDbiVersion(const DbiVersion * pVersion) +{ + DD_ENTER_MAY_THROW; + + if (pVersion->m_dwFormat != kCurrentDbiVersionFormat) + { + return CORDBG_E_INCOMPATIBLE_PROTOCOL; + } + + if ((pVersion->m_dwProtocolBreakingChangeCounter != kCurrentDacDbiProtocolBreakingChangeCounter) || + (pVersion->m_dwReservedMustBeZero1 != 0)) + { + return CORDBG_E_INCOMPATIBLE_PROTOCOL; + } + + return S_OK; +} + +// Flush the DAC cache. This should be called when target memory changes. +HRESULT DacDbiInterfaceImpl::FlushCache() +{ + // Non-reentrant. We don't want to flush cached instances from a callback. + // That would remove host DAC instances while they're being used. + DD_NON_REENTRANT_MAY_THROW; + + m_pCachedPEFile = VMPTR_PEFile::NullPtr(); + m_pCachedImporter = NULL; + m_isCachedHijackFunctionValid = FALSE; + + HRESULT hr = ClrDataAccess::Flush(); + + // Current impl of Flush() should always succeed. If it ever fails, we want to know. + _ASSERTE(SUCCEEDED(hr)); + return hr; +} + +// enable or disable DAC target consistency checks +void DacDbiInterfaceImpl::DacSetTargetConsistencyChecks(bool fEnableAsserts) +{ + // forward on to our ClrDataAccess base class + ClrDataAccess::SetTargetConsistencyChecks(fEnableAsserts); +} + +// Query if Left-side is started up? +BOOL DacDbiInterfaceImpl::IsLeftSideInitialized() +{ + DD_ENTER_MAY_THROW; + + if (g_pDebugger != NULL) + { + // This check is "safe". + // The initialize order in the left-side is: + // 1) g_pDebugger is an RVA based global initialized to NULL when the module is loaded. + // 2) Allocate a "Debugger" object. + // 3) run the ctor, which will set m_fLeftSideInitialized = FALSE. + // 4) assign the object to g_pDebugger. + // 5) later, LS initialization code will assign g_pDebugger->m_fLeftSideInitialized = TRUE. + // + // The memory write in #5 is atomic. There is no window where we're reading unitialized data. + + return (g_pDebugger->m_fLeftSideInitialized != 0); + } + + return FALSE; +} + + +// Determines if a given adddress is a CLR stub. +BOOL DacDbiInterfaceImpl::IsTransitionStub(CORDB_ADDRESS address) +{ + DD_ENTER_MAY_THROW; + + BOOL fIsStub = FALSE; + +#if defined(FEATURE_PAL) + // Currently IsIPInModule() is not implemented in the PAL. Rather than skipping the check, we should + // either E_NOTIMPL this API or implement IsIPInModule() in the PAL. Since ICDProcess::IsTransitionStub() + // is only called by VS in mixed-mode debugging scenarios, and mixed-mode debugging is not supported on + // POSIX systems, there is really no incentive to implement this API at this point. + ThrowHR(E_NOTIMPL); + +#else // !FEATURE_PAL + + TADDR ip = (TADDR)address; + + if (ip == NULL) + { + fIsStub = FALSE; + } + else + { + fIsStub = StubManager::IsStub(ip); + } + + // If it's in Mscorwks, count that as a stub too. + if (fIsStub == FALSE) + { + fIsStub = IsIPInModule(m_globalBase, ip); + } + +#endif // FEATURE_PAL + + return fIsStub; +} + +// Gets the type of 'address'. +IDacDbiInterface::AddressType DacDbiInterfaceImpl::GetAddressType(CORDB_ADDRESS address) +{ + DD_ENTER_MAY_THROW; + TADDR taAddr = CORDB_ADDRESS_TO_TADDR(address); + + if (IsPossibleCodeAddress(taAddr) == S_OK) + { + if (ExecutionManager::IsManagedCode(taAddr)) + { + return kAddressManagedMethod; + } + + if (StubManager::IsStub(taAddr)) + { + return kAddressRuntimeUnmanagedStub; + } + } + + return kAddressUnrecognized; +} + + +// Get a VM appdomain pointer that matches the appdomain ID +VMPTR_AppDomain DacDbiInterfaceImpl::GetAppDomainFromId(ULONG appdomainId) +{ + DD_ENTER_MAY_THROW; + + VMPTR_AppDomain vmAppDomain; + + // @dbgtodo dac support - We would like to wean ourselves off the IXClrData interfaces. + IXCLRDataProcess * pDAC = this; + ReleaseHolder<IXCLRDataAppDomain> pDacAppDomain; + + HRESULT hrStatus = pDAC->GetAppDomainByUniqueID(appdomainId, &pDacAppDomain); + IfFailThrow(hrStatus); + + IXCLRDataAppDomain * pIAppDomain = pDacAppDomain; + AppDomain * pAppDomain = (static_cast<ClrDataAppDomain *> (pIAppDomain))->GetAppDomain(); + SIMPLIFYING_ASSUMPTION(pAppDomain != NULL); + if (pAppDomain == NULL) + { + ThrowHR(E_FAIL); // corrupted left-side? + } + + TADDR addrAppDomain = PTR_HOST_TO_TADDR(pAppDomain); + vmAppDomain.SetDacTargetPtr(addrAppDomain); + + return vmAppDomain; +} + + +// Get the AppDomain ID for an AppDomain. +ULONG DacDbiInterfaceImpl::GetAppDomainId(VMPTR_AppDomain vmAppDomain) +{ + DD_ENTER_MAY_THROW; + + if (vmAppDomain.IsNull()) + { + return 0; + } + else + { + AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); + return pAppDomain->GetId().m_dwId; + } +} + +// Get the managed AppDomain object for an AppDomain. +VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetAppDomainObject(VMPTR_AppDomain vmAppDomain) +{ + DD_ENTER_MAY_THROW; + + AppDomain* pAppDomain = vmAppDomain.GetDacPtr(); + OBJECTHANDLE hAppDomainManagedObject = pAppDomain->GetRawExposedObjectHandleForDebugger(); + VMPTR_OBJECTHANDLE vmObj = VMPTR_OBJECTHANDLE::NullPtr(); + vmObj.SetDacTargetPtr(hAppDomainManagedObject); + return vmObj; + +} + +// Determine if the specified AppDomain is the default domain +BOOL DacDbiInterfaceImpl::IsDefaultDomain(VMPTR_AppDomain vmAppDomain) +{ + DD_ENTER_MAY_THROW; + + AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); + BOOL fDefaultDomain = pAppDomain->IsDefaultDomain(); + + return fDefaultDomain; +} + + +// Get the full AD friendly name for the given EE AppDomain. +void DacDbiInterfaceImpl::GetAppDomainFullName( + VMPTR_AppDomain vmAppDomain, + IStringHolder * pStrName ) +{ + DD_ENTER_MAY_THROW; + AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); + + // Get the AppDomain name from the VM without changing anything + // We might be able to simplify this, eg. by returning an SString. + bool fIsUtf8; + PVOID pRawName = pAppDomain->GetFriendlyNameNoSet(&fIsUtf8); + + if (!pRawName) + { + ThrowHR(E_NOINTERFACE); + } + + HRESULT hrStatus = S_OK; + if (fIsUtf8) + { + // we have to allocate a temporary string + // we could avoid this by adding a version of IStringHolder::AssignCopy that takes a UTF8 string + // We should also probably check to see when fIsUtf8 is ever true (it looks like it should normally be false). + ULONG32 dwNameLen = 0; + hrStatus = ConvertUtf8((LPCUTF8)pRawName, 0, &dwNameLen, NULL); + if (SUCCEEDED( hrStatus )) + { + NewArrayHolder<WCHAR> pwszName(new WCHAR[dwNameLen]); + hrStatus = ConvertUtf8((LPCUTF8)pRawName, dwNameLen, &dwNameLen, pwszName ); + IfFailThrow(hrStatus); + + hrStatus = pStrName->AssignCopy(pwszName); + } + } + else + { + hrStatus = pStrName->AssignCopy(static_cast<PCWSTR>(pRawName)); + } + + // Very important that this either sets pStrName or Throws. + // Don't set it and then then throw. + IfFailThrow(hrStatus); +} + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// JIT Compiler Flags +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +// Get the values of the JIT Optimization and EnC flags. +void DacDbiInterfaceImpl::GetCompilerFlags ( + VMPTR_DomainFile vmDomainFile, + BOOL *pfAllowJITOpts, + BOOL *pfEnableEnC) +{ + DD_ENTER_MAY_THROW; + + DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); + + if (pDomainFile == NULL) + { + ThrowHR(E_FAIL); + } + + // Get the underlying module - none of this is AppDomain specific + Module * pModule = pDomainFile->GetModule(); + DWORD dwBits = pModule->GetDebuggerInfoBits(); + *pfAllowJITOpts = !CORDisableJITOptimizations(dwBits); + *pfEnableEnC = pModule->IsEditAndContinueEnabled(); + + +} //GetCompilerFlags + +//----------------------------------------------------------------------------- +// Helper function for SetCompilerFlags to set EnC status. +// Arguments: +// Input: +// pModule - The runtime module for which flags are being set. +// +// Return value: +// true if the Enc bits can be set on this module +//----------------------------------------------------------------------------- + +bool DacDbiInterfaceImpl::CanSetEnCBits(Module * pModule) +{ + _ASSERTE(pModule != NULL); +#ifdef EnC_SUPPORTED + // If we're using explicit sequence points (from the PDB), then we can't do EnC + // because EnC won't get updated pdbs and so the sequence points will be wrong. + bool fIgnorePdbs = ((pModule->GetDebuggerInfoBits() & DACF_IGNORE_PDBS) != 0); + + bool fAllowEnc = pModule->IsEditAndContinueCapable() && + +#ifdef PROFILING_SUPPORTED_DATA + !CORProfilerPresent() && // this queries target +#endif + fIgnorePdbs; +#else // ! EnC_SUPPORTED + // Enc not supported on any other platforms. + bool fAllowEnc = false; +#endif + + return fAllowEnc; +} // DacDbiInterfaceImpl::SetEnCBits + +// Set the values of the JIT optimization and EnC flags. +HRESULT DacDbiInterfaceImpl::SetCompilerFlags(VMPTR_DomainFile vmDomainFile, + BOOL fAllowJitOpts, + BOOL fEnableEnC) +{ + DD_ENTER_MAY_THROW; + + DWORD dwBits = 0; + DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); + Module * pModule = pDomainFile->GetCurrentModule(); + HRESULT hr = S_OK; + + +#ifdef FEATURE_PREJIT + if (pModule->HasNativeImage()) + { + ThrowHR(CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE); + } +#endif + _ASSERTE(pModule != NULL); + + // Initialize dwBits. + dwBits = (pModule->GetDebuggerInfoBits() & ~(DACF_ALLOW_JIT_OPTS | DACF_ENC_ENABLED)); + dwBits &= DACF_CONTROL_FLAGS_MASK; + + if (fAllowJitOpts) + { + dwBits |= DACF_ALLOW_JIT_OPTS; + } + if (fEnableEnC) + { + if (CanSetEnCBits(pModule)) + { + dwBits |= DACF_ENC_ENABLED; + } + else + { + hr = CORDBG_S_NOT_ALL_BITS_SET; + } + } + // Settings from the debugger take precedence over all other settings. + dwBits |= DACF_USER_OVERRIDE; + + // set flags. This will write back to the target + pModule->SetDebuggerInfoBits((DebuggerAssemblyControlFlags)dwBits); + + + LOG((LF_CORDB, LL_INFO100, "D::HIPCE, Changed Jit-Debug-Info: fOpt=%d, fEnableEnC=%d, new bits=0x%08x\n", + (dwBits & DACF_ALLOW_JIT_OPTS) != 0, + (dwBits & DACF_ENC_ENABLED) != 0, + dwBits)); + + _ASSERTE(SUCCEEDED(hr)); + return hr; + +} // DacDbiInterfaceImpl::SetCompilerFlags + + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// sequence points and var info +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +// Initialize the native/IL sequence points and native var info for a function. +void DacDbiInterfaceImpl::GetNativeCodeSequencePointsAndVarInfo(VMPTR_MethodDesc vmMethodDesc, + CORDB_ADDRESS startAddr, + BOOL fCodeAvailable, + NativeVarData * pNativeVarData, + SequencePoints * pSequencePoints) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(!vmMethodDesc.IsNull()); + + MethodDesc * pMD = vmMethodDesc.GetDacPtr(); + + _ASSERTE(fCodeAvailable != 0); + + // get information about the locations of arguments and local variables + GetNativeVarData(pMD, startAddr, GetArgCount(pMD), pNativeVarData); + + // get the sequence points + GetSequencePoints(pMD, startAddr, pSequencePoints); + +} // GetNativeCodeSequencePointsAndVarInfo + +//----------------------------------------------------------------------------- +// Get the number of fixed arguments to a function, i.e., the explicit args and the "this" pointer. +// This does not include other implicit arguments or varargs. This is used to compute a variable ID +// (see comment in CordbJITILFrame::ILVariableToNative for more detail) +// Arguments: +// input: pMD pointer to the method desc for the function +// output: none +// Return value: +// the number of fixed arguments to the function +//----------------------------------------------------------------------------- +SIZE_T DacDbiInterfaceImpl::GetArgCount(MethodDesc * pMD) +{ + + // Create a MetaSig for the given method's sig. (Easier than + // picking the sig apart ourselves.) + PCCOR_SIGNATURE pCallSig; + DWORD cbCallSigSize; + + pMD->GetSig(&pCallSig, &cbCallSigSize); + + if (pCallSig == NULL) + { + // Sig should only be null if the image is corrupted. (Even for lightweight-codegen) + // We expect the jit+verifier to catch this, so that we never land here. + // But just in case ... + CONSISTENCY_CHECK_MSGF(false, ("Corrupted image, null sig.(%s::%s)", + pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName)); + return 0; + } + + MetaSig msig(pCallSig, cbCallSigSize, pMD->GetModule(), NULL, MetaSig::sigMember); + + // Get the arg count. + UINT32 NumArguments = msig.NumFixedArgs(); + + // Account for the 'this' argument. + if (!pMD->IsStatic()) + { + NumArguments++; + } +/* + SigParser sigParser(pCallSig, cbCallSigSize); + sigParser.SkipMethodHeaderSignature(&m_allArgsCount); +*/ + return NumArguments; +} //GetArgCount + +// Allocator to pass to DebugInfoStores, allocating forDBI +BYTE* InfoStoreForDbiNew(void * pData, size_t cBytes) +{ + return new(forDbi) BYTE[cBytes]; +} + +// Allocator to pass to the debug-info-stores... +BYTE* InfoStoreNew(void * pData, size_t cBytes) +{ + return new BYTE[cBytes]; +} + +//----------------------------------------------------------------------------- +// Get locations and code offsets for local variables and arguments in a function +// This information is used to find the location of a value at a given IP. +// Arguments: +// input: +// pMethodDesc pointer to the method desc for the function +// startAddr starting address of the function--used to differentiate +// EnC versions +// fixedArgCount number of fixed arguments to the function +// output: +// pVarInfo data structure containing a list of variable and +// argument locations by range of IP offsets +// Note: this function may throw +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::GetNativeVarData(MethodDesc * pMethodDesc, + CORDB_ADDRESS startAddr, + SIZE_T fixedArgCount, + NativeVarData * pVarInfo) +{ + // make sure we haven't done this already + if (pVarInfo->IsInitialized()) + { + return; + } + + NewHolder<ICorDebugInfo::NativeVarInfo> nativeVars(NULL); + + DebugInfoRequest request; + request.InitFromStartingAddr(pMethodDesc, CORDB_ADDRESS_TO_TADDR(startAddr)); + + ULONG32 entryCount; + + BOOL success = DebugInfoManager::GetBoundariesAndVars(request, + InfoStoreNew, NULL, // allocator + NULL, NULL, + &entryCount, &nativeVars); + + if (!success) + ThrowHR(E_FAIL); + + // set key fields of pVarInfo + pVarInfo->InitVarDataList(nativeVars, (int)fixedArgCount, (int)entryCount); +} // GetNativeVarData + + +//----------------------------------------------------------------------------- +// Given a instrumented IL map from the profiler that maps: +// Original offset IL_A -> Instrumentend offset IL_B +// And a native mapping from the JIT that maps: +// Instrumented offset IL_B -> native offset Native_C +// This function merges the two maps and stores the result back into the nativeMap. +// The nativeMap now maps: +// Original offset IL_A -> native offset Native_C +// pEntryCount is the number of valid entries in nativeMap, and it may be adjusted downwards +// as part of the composition. +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::ComposeMapping(InstrumentedILOffsetMapping profilerILMap, ICorDebugInfo::OffsetMapping nativeMap[], ULONG32* pEntryCount) +{ + // Translate the IL offset if the profiler has provided us with a mapping. + // The ICD public API should always expose the original IL offsets, but GetBoundaries() + // directly accesses the debug info, which stores the instrumented IL offsets. + + ULONG32 entryCount = *pEntryCount; + if (!profilerILMap.IsNull()) + { + // If we did instrument, then we can't have any sequence points that + // are "in-between" the old-->new map that the profiler gave us. + // Ex, if map is: + // (6 old -> 36 new) + // (8 old -> 50 new) + // And the jit gives us an entry for 44 new, that will map back to 6 old. + // Since the map can only have one entry for 6 old, we remove 44 new. + + // First Pass: invalidate all the duplicate entries by setting their IL offset to MAX_ILNUM + ULONG32 cDuplicate = 0; + ULONG32 prevILOffset = (ULONG32)(ICorDebugInfo::MAX_ILNUM); + for (ULONG32 i = 0; i < entryCount; i++) + { + ULONG32 origILOffset = TranslateInstrumentedILOffsetToOriginal(nativeMap[i].ilOffset, &profilerILMap); + + if (origILOffset == prevILOffset) + { + // mark this sequence point as invalid; refer to the comment above + nativeMap[i].ilOffset = (ULONG32)(ICorDebugInfo::MAX_ILNUM); + cDuplicate += 1; + } + else + { + // overwrite the instrumented IL offset with the original IL offset + nativeMap[i].ilOffset = origILOffset; + prevILOffset = origILOffset; + } + } + + // Second Pass: move all the valid entries up front + ULONG32 realIndex = 0; + for (ULONG32 curIndex = 0; curIndex < entryCount; curIndex++) + { + if (nativeMap[curIndex].ilOffset != (ULONG32)(ICorDebugInfo::MAX_ILNUM)) + { + // This is a valid entry. Move it up front. + nativeMap[realIndex] = nativeMap[curIndex]; + realIndex += 1; + } + } + + // make sure we have done the bookkeeping correctly + _ASSERTE((realIndex + cDuplicate) == entryCount); + + // Final Pass: derecement entryCount + entryCount -= cDuplicate; + *pEntryCount = entryCount; + } +} + + +//----------------------------------------------------------------------------- +// Get the native/IL sequence points for a function +// Arguments: +// input: +// pMethodDesc pointer to the method desc for the function +// startAddr starting address of the function--used to differentiate +// output: +// pNativeMap data structure containing a list of sequence points +// Note: this function may throw +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::GetSequencePoints(MethodDesc * pMethodDesc, + CORDB_ADDRESS startAddr, + SequencePoints * pSeqPoints) +{ + + // make sure we haven't done this already + if (pSeqPoints->IsInitialized()) + { + return; + } + + // Use the DebugInfoStore to get IL->Native maps. + // It doesn't matter whether we're jitted, ngenned etc. + DebugInfoRequest request; + request.InitFromStartingAddr(pMethodDesc, CORDB_ADDRESS_TO_TADDR(startAddr)); + + + // Bounds info. + NewArrayHolder<ICorDebugInfo::OffsetMapping> mapCopy(NULL); + + ULONG32 entryCount; + BOOL success = DebugInfoManager::GetBoundariesAndVars(request, + InfoStoreNew, NULL, // allocator + &entryCount, &mapCopy, + NULL, NULL); + if (!success) + ThrowHR(E_FAIL); + + // if there is a rejit IL map for this function, apply that in preference to load-time mapping +#ifdef FEATURE_REJIT + ReJitManager * pReJitMgr = pMethodDesc->GetReJitManager(); + ReJitInfo* pReJitInfo = pReJitMgr->FindReJitInfo(dac_cast<PTR_MethodDesc>(pMethodDesc), (PCODE)startAddr, 0); + if (pReJitInfo != NULL) + { + InstrumentedILOffsetMapping rejitMapping = pReJitInfo->m_pShared->m_instrumentedILMap; + ComposeMapping(rejitMapping, mapCopy, &entryCount); + } + else + { +#endif + // if there is a profiler load-time mapping and not a rejit mapping, apply that instead + InstrumentedILOffsetMapping loadTimeMapping = + pMethodDesc->GetModule()->GetInstrumentedILOffsetMapping(pMethodDesc->GetMemberDef()); + ComposeMapping(loadTimeMapping, mapCopy, &entryCount); +#ifdef FEATURE_REJIT + } +#endif + + pSeqPoints->InitSequencePoints(entryCount); + + // mapCopy and pSeqPoints have elements of different types. Thus, we + // need to copy the individual members from the elements of mapCopy to the + // elements of pSeqPoints. Once we're done, we can release mapCopy + pSeqPoints->CopyAndSortSequencePoints(mapCopy); + +} // GetSequencePoints + +// ---------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TranslateInstrumentedILOffsetToOriginal +// +// Description: +// Helper function to convert an instrumented IL offset to the corresponding original IL offset. +// +// Arguments: +// * ilOffset - offset to be translated +// * pMapping - the profiler-provided mapping between original IL offsets and instrumented IL offsets +// +// Return Value: +// Return the translated offset. +// + +ULONG DacDbiInterfaceImpl::TranslateInstrumentedILOffsetToOriginal(ULONG ilOffset, + const InstrumentedILOffsetMapping * pMapping) +{ + SIZE_T cMap = pMapping->GetCount(); + ARRAY_PTR_COR_IL_MAP rgMap = pMapping->GetOffsets(); + + _ASSERTE((cMap == 0) == (rgMap == NULL)); + + // Early out if there is no mapping, or if we are dealing with a special IL offset such as + // prolog, epilog, etc. + if ((cMap == 0) || ((int)ilOffset < 0)) + { + return ilOffset; + } + + SIZE_T i = 0; + for (i = 1; i < cMap; i++) + { + if (ilOffset < rgMap[i].newOffset) + { + return rgMap[i - 1].oldOffset; + } + } + return rgMap[i - 1].oldOffset; +} + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Function Data +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +// GetILCodeAndSig returns the function's ILCode and SigToken given +// a module and a token. The info will come from a MethodDesc, if +// one exists or from metadata. +// +void DacDbiInterfaceImpl::GetILCodeAndSig(VMPTR_DomainFile vmDomainFile, + mdToken functionToken, + TargetBuffer * pCodeInfo, + mdToken * pLocalSigToken) +{ + DD_ENTER_MAY_THROW; + + DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); + Module * pModule = pDomainFile->GetCurrentModule(); + RVA methodRVA = 0; + DWORD implFlags; + + // preinitialize out params + pCodeInfo->Clear(); + *pLocalSigToken = mdSignatureNil; + + // Get the RVA and impl flags for this method. + IfFailThrow(pModule->GetMDImport()->GetMethodImplProps(functionToken, + &methodRVA, + &implFlags)); + + MethodDesc* pMethodDesc = + FindLoadedMethodRefOrDef(pModule, functionToken); + + // If the RVA is 0 or it's native, then the method is not IL + if (methodRVA == 0) + { + LOG((LF_CORDB,LL_INFO100000, "DDI::GICAS: Function is not IL - methodRVA == NULL!\n")); + // return (CORDBG_E_FUNCTION_NOT_IL); + // Sanity check this.... + + if(!pMethodDesc || !pMethodDesc->IsIL()) + { + LOG((LF_CORDB,LL_INFO100000, "DDI::GICAS: And the MD agrees..\n")); + ThrowHR(CORDBG_E_FUNCTION_NOT_IL); + } + else + { + LOG((LF_CORDB,LL_INFO100000, "DDI::GICAS: But the MD says it's IL..\n")); + } + + if (pMethodDesc != NULL && pMethodDesc->GetRVA() == 0) + { + LOG((LF_CORDB,LL_INFO100000, "DDI::GICAS: Actually, MD says RVA is 0 too - keep going...!\n")); + } + } + if (IsMiNative(implFlags)) + { + LOG((LF_CORDB,LL_INFO100000, "DDI::GICAS: Function is not IL - IsMiNative!\n")); + ThrowHR(CORDBG_E_FUNCTION_NOT_IL); + } + + *pLocalSigToken = GetILCodeAndSigHelper(pModule, pMethodDesc, functionToken, methodRVA, pCodeInfo); + +#ifdef LOGGING + else + { + LOG((LF_CORDB,LL_INFO100000, "DDI::GICAS: GetMethodImplProps failed!\n")); + } +#endif +} // GetILCodeAndSig + +//--------------------------------------------------------------------------------------- +// +// This is just a worker function for GetILCodeAndSig. It returns the function's ILCode and SigToken +// given a module, a token, and the RVA. If a MethodDesc is provided, it has to be consistent with +// the token and the RVA. +// +// Arguments: +// pModule - the Module containing the specified method +// pMD - the specified method; can be NULL +// mdMethodToken - the MethodDef token of the specified method +// methodRVA - the RVA of the IL for the specified method +// pIL - out parameter; return the target address and size of the IL of the specified method +// +// Return Value: +// Return the local variable signature token of the specified method. Can be mdSignatureNil. +// + +mdSignature DacDbiInterfaceImpl::GetILCodeAndSigHelper(Module * pModule, + MethodDesc * pMD, + mdMethodDef mdMethodToken, + RVA methodRVA, + TargetBuffer * pIL) +{ + _ASSERTE(pModule != NULL); + + // If a MethodDesc is provided, it has to be consistent with the MethodDef token and the RVA. + _ASSERTE((pMD == NULL) || ((pMD->GetMemberDef() == mdMethodToken) && (pMD->GetRVA() == methodRVA))); + + TADDR pTargetIL; // target address of start of IL blob + + // This works for methods in dynamic modules, and methods overriden by a profiler. + pTargetIL = pModule->GetDynamicIL(mdMethodToken, TRUE); + + // Method not overriden - get the original copy of the IL by going to the PE file/RVA + // If this is in a dynamic module then don't even attempt this since ReflectionModule::GetIL isn't + // implemend for DAC. + if (pTargetIL == 0 && !pModule->IsReflection()) + { + pTargetIL = (TADDR)pModule->GetIL(methodRVA); + } + + mdSignature mdSig = mdSignatureNil; + if (pTargetIL == 0) + { + // Currently this should only happen for LCG methods (including IL stubs). + // LCG methods have a 0 RVA, and so we don't currently have any way to get the IL here. + _ASSERTE(pMD->IsDynamicMethod()); + _ASSERTE(pMD->AsDynamicMethodDesc()->IsLCGMethod()|| + pMD->AsDynamicMethodDesc()->IsILStub()); + + // Clear the buffer. + pIL->Clear(); + } + else + { + // Now we have the target address of the IL blob, we need to bring it over to the host. + // DacGetILMethod will copy the COR_ILMETHOD information that we need + COR_ILMETHOD * pHostIL = DacGetIlMethod(pTargetIL); // host address of start of IL blob + COR_ILMETHOD_DECODER header(pHostIL); // host address of header + + + // Get the IL code info. We need the address of the IL itself, which will be beyond the header + // at the beginning of the blob. We ultimately need the target address. To get this, we take + // target address of the target IL blob and add the offset from the beginning of the host IL blob + // (the header) to the beginning of the IL itself (we get this information from the header). + pIL->pAddress = pTargetIL + ((SIZE_T)(header.Code) - (SIZE_T)pHostIL); + pIL->cbSize = header.GetCodeSize(); + + // Now we get the signature token + if (header.LocalVarSigTok != NULL) + { + mdSig = header.GetLocalVarSigTok(); + } + else + { + mdSig = mdSignatureNil; + } + } + + return mdSig; +} + + +bool DacDbiInterfaceImpl::GetMetaDataFileInfoFromPEFile(VMPTR_PEFile vmPEFile, + DWORD &dwTimeStamp, + DWORD &dwSize, + bool &isNGEN, + IStringHolder* pStrFilename) +{ +#if !defined(FEATURE_PREJIT) + + return false; + +#else // defined(FEATURE_PREJIT) + + DD_ENTER_MAY_THROW; + + DWORD dwDataSize; + DWORD dwRvaHint; + PEFile * pPEFile = vmPEFile.GetDacPtr(); + _ASSERTE(pPEFile != NULL); + if (pPEFile == NULL) + return false; + + WCHAR wszFilePath[MAX_LONGPATH] = {0}; + DWORD cchFilePath = MAX_LONGPATH; + bool ret = ClrDataAccess::GetMetaDataFileInfoFromPEFile(pPEFile, + dwTimeStamp, + dwSize, + dwDataSize, + dwRvaHint, + isNGEN, + wszFilePath, + cchFilePath); + + pStrFilename->AssignCopy(wszFilePath); + return ret; +#endif // !defined(FEATURE_PREJIT) +} + + +bool DacDbiInterfaceImpl::GetILImageInfoFromNgenPEFile(VMPTR_PEFile vmPEFile, + DWORD &dwTimeStamp, + DWORD &dwSize, + IStringHolder* pStrFilename) +{ +#if !defined(FEATURE_PREJIT) + + return false; + +#else // defined(FEATURE_PREJIT) + + DD_ENTER_MAY_THROW; + + PEFile * pPEFile = vmPEFile.GetDacPtr(); + _ASSERTE(pPEFile != NULL); + if (pPEFile == NULL) + { + return false; + } + + WCHAR wszFilePath[MAX_LONGPATH] = {0}; + DWORD cchFilePath = MAX_LONGPATH; + bool ret = ClrDataAccess::GetILImageInfoFromNgenPEFile(pPEFile, + dwTimeStamp, + dwSize, + wszFilePath, + cchFilePath); + + pStrFilename->AssignCopy(wszFilePath); + return ret; +#endif // !defined(FEATURE_PREJIT) +} + +// Get start addresses and sizes for hot and cold regions for a native code blob. +// Arguments: +// Input: +// pMethodDesc - method desc for the function we are inspecting +// Output (required): +// pCodeInfo - initializes the m_rgCodeRegions field of this structure +// if the native code is available. Otherwise, +// pCodeInfo->IsValid() is false. + +void DacDbiInterfaceImpl::GetMethodRegionInfo(MethodDesc * pMethodDesc, + NativeCodeFunctionData * pCodeInfo) +{ + CONTRACTL + { + SO_INTOLERANT; + GC_NOTRIGGER; + PRECONDITION(CheckPointer(pCodeInfo)); + } + CONTRACTL_END; + + IJitManager::MethodRegionInfo methodRegionInfo = {NULL, 0, NULL, 0}; + PCODE functionAddress = pMethodDesc->GetNativeCode(); + + // get the start address of the hot region and initialize the jit manager + pCodeInfo->m_rgCodeRegions[kHot].pAddress = CORDB_ADDRESS(PCODEToPINSTR(functionAddress)); + + // if the start address is NULL, the code isn't available yet, so just return + if (functionAddress != NULL) + { + EECodeInfo codeInfo(functionAddress); + _ASSERTE(codeInfo.IsValid()); + + codeInfo.GetMethodRegionInfo(&methodRegionInfo); + + // now get the rest of the region information + pCodeInfo->m_rgCodeRegions[kHot].cbSize = (ULONG)methodRegionInfo.hotSize; + pCodeInfo->m_rgCodeRegions[kCold].Init(PCODEToPINSTR(methodRegionInfo.coldStartAddress), + (ULONG)methodRegionInfo.coldSize); + _ASSERTE(pCodeInfo->IsValid()); + } + else + { + _ASSERTE(!pCodeInfo->IsValid()); + } +} // GetMethodRegionInfo + + +// Gets the following information about a native code blob: +// - its method desc +// - whether it's an instantiated generic +// - its EnC version number +// - hot and cold region information. +// If the hot region start address is NULL at the end, it means the native code +// isn't currently available. In this case, all values in pCodeInfo will be +// cleared. + +void DacDbiInterfaceImpl::GetNativeCodeInfo(VMPTR_DomainFile vmDomainFile, + mdToken functionToken, + NativeCodeFunctionData * pCodeInfo) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(pCodeInfo != NULL); + + // pre-initialize: + pCodeInfo->Clear(); + + DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); + Module * pModule = pDomainFile->GetCurrentModule(); + + MethodDesc* pMethodDesc = FindLoadedMethodRefOrDef(pModule, functionToken); + pCodeInfo->vmNativeCodeMethodDescToken.SetHostPtr(pMethodDesc); + + // if we are loading a module and trying to bind a previously set breakpoint, we may not have + // a method desc yet, so check for that situation + if(pMethodDesc != NULL) + { + GetMethodRegionInfo(pMethodDesc, pCodeInfo); + if (pCodeInfo->m_rgCodeRegions[kHot].pAddress != NULL) + { + pCodeInfo->isInstantiatedGeneric = pMethodDesc->HasClassOrMethodInstantiation(); + LookupEnCVersions(pModule, + pCodeInfo->vmNativeCodeMethodDescToken, + functionToken, + pCodeInfo->m_rgCodeRegions[kHot].pAddress, + &(pCodeInfo->encVersion)); + } + } +} // GetNativeCodeInfo + +// Gets the following information about a native code blob: +// - its method desc +// - whether it's an instantiated generic +// - its EnC version number +// - hot and cold region information. +void DacDbiInterfaceImpl::GetNativeCodeInfoForAddr(VMPTR_MethodDesc vmMethodDesc, + CORDB_ADDRESS hotCodeStartAddr, + NativeCodeFunctionData * pCodeInfo) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(pCodeInfo != NULL); + + if (hotCodeStartAddr == NULL) + { + // if the start address is NULL, the code isn't available yet, so just return + _ASSERTE(!pCodeInfo->IsValid()); + return; + } + + IJitManager::MethodRegionInfo methodRegionInfo = {NULL, 0, NULL, 0}; + TADDR codeAddr = CORDB_ADDRESS_TO_TADDR(hotCodeStartAddr); + +#ifdef _TARGET_ARM_ + // TADDR should not have the thumb code bit set. + _ASSERTE((codeAddr & THUMB_CODE) == 0); + codeAddr &= ~THUMB_CODE; +#endif + + EECodeInfo codeInfo(codeAddr); + _ASSERTE(codeInfo.IsValid()); + + // We may not have the memory for the cold code region in a minidump. + // Do not fail stackwalking because of this. + EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY + { + codeInfo.GetMethodRegionInfo(&methodRegionInfo); + } + EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY; + + // Even if GetMethodRegionInfo() fails to retrieve the cold code region info, + // we should still be able to get the hot code region info. We are counting on this for + // stackwalking to work in dump debugging scenarios. + _ASSERTE(methodRegionInfo.hotStartAddress == codeAddr); + + // now get the rest of the region information + pCodeInfo->m_rgCodeRegions[kHot].Init(PCODEToPINSTR(methodRegionInfo.hotStartAddress), + (ULONG)methodRegionInfo.hotSize); + pCodeInfo->m_rgCodeRegions[kCold].Init(PCODEToPINSTR(methodRegionInfo.coldStartAddress), + (ULONG)methodRegionInfo.coldSize); + _ASSERTE(pCodeInfo->IsValid()); + + MethodDesc* pMethodDesc = vmMethodDesc.GetDacPtr(); + pCodeInfo->isInstantiatedGeneric = pMethodDesc->HasClassOrMethodInstantiation(); + pCodeInfo->vmNativeCodeMethodDescToken = vmMethodDesc; + + SIZE_T unusedLatestEncVersion; + Module * pModule = pMethodDesc->GetModule(); + _ASSERTE(pModule != NULL); + LookupEnCVersions(pModule, + vmMethodDesc, + pMethodDesc->GetMemberDef(), + codeAddr, + &unusedLatestEncVersion, //unused by caller + &(pCodeInfo->encVersion)); + +} // GetNativeCodeInfo + + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// +// Functions to get Type and Class information +// +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//----------------------------------------------------------------------------- +//DacDbiInterfaceImpl::GetTypeHandles +// Get the approximate and exact type handles for a type +// Arguments: +// input: +// vmThExact - VMPTR of the exact type handle. If this method is called +// to get information for a new generic instantiation, this will already +// be initialized. If it's called to get type information for an arbitrary +// type (i.e., called to initialize an instance of CordbClass), it will be NULL +// vmThApprox - VMPTR of the approximate type handle. If this method is called +// to get information for a new generic instantiation, this will already +// be initialized. If it's called to get type information for an arbitrary +// type (i.e., called to initialize an instance of CordbClass), it will be NULL +// output: +// pThExact - handle for exact type information for a generic instantiation +// pThApprox - handle for type information +// Notes: +// pThExact and pTHApprox must be pointers to existing memory. +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::GetTypeHandles(VMPTR_TypeHandle vmThExact, + VMPTR_TypeHandle vmThApprox, + TypeHandle * pThExact, + TypeHandle * pThApprox) + { + _ASSERTE((pThExact != NULL) && (pThApprox != NULL)); + + *pThExact = TypeHandle::FromPtr(vmThExact.GetDacPtr()); + *pThApprox = TypeHandle::FromPtr(vmThApprox.GetDacPtr()); + + // If we can't find the class, return the proper HR to the right side. Note: if the class is not a value class and + // the class is also not restored, then we must pretend that the class is still not loaded. We are gonna let + // unrestored value classes slide, though, and special case access to the class's parent below. + if ((pThApprox->IsNull()) || ((!pThApprox->IsValueType()) && (!pThApprox->IsRestored()))) + { + LOG((LF_CORDB, LL_INFO10000, "D::GASCI: class isn't loaded.\n")); + ThrowHR(CORDBG_E_CLASS_NOT_LOADED); + } + // If the exact type handle is not restored ignore it. + if (!pThExact->IsNull() && !pThExact->IsRestored()) + { + *pThExact = TypeHandle(); + } + } // DacDbiInterfaceImpl::GetTypeHandles + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetTotalFieldCount +// Gets the total number of fields for a type. +// Input Argument: thApprox - type handle used to determine the number of fields +// Return Value: count of the total fields of the type. +//----------------------------------------------------------------------------- +unsigned int DacDbiInterfaceImpl::GetTotalFieldCount(TypeHandle thApprox) +{ + MethodTable *pMT = thApprox.GetMethodTable(); + + // Count the instance and static fields for this class (not including parent). + // This will not include any newly added EnC fields. + unsigned int IFCount = pMT->GetNumIntroducedInstanceFields(); + unsigned int SFCount = pMT->GetNumStaticFields(); + +#ifdef EnC_SUPPORTED + PTR_Module pModule = pMT->GetModule(); + + // Stats above don't include EnC fields. So add them now. + if (pModule->IsEditAndContinueEnabled()) + { + PTR_EnCEEClassData pEncData = + (dac_cast<PTR_EditAndContinueModule>(pModule))->GetEnCEEClassData(pMT, TRUE); + + if (pEncData != NULL) + { + _ASSERTE(pEncData->GetMethodTable() == pMT); + + // EnC only adds fields, never removes them. + IFCount += pEncData->GetAddedInstanceFields(); + SFCount += pEncData->GetAddedStaticFields(); + } + } +#endif + return IFCount + SFCount; +} // DacDbiInterfaceImpl::GetTotalFieldCount + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::InitClassData +// initializes various values of the ClassInfo data structure, including the +// field count, generic args count, size and value class flag +// Arguments: +// input: thApprox - used to get access to all the necessary values +// fIsInstantiatedType - used to determine how to compute the size +// output: pData - contains fields to be initialized +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::InitClassData(TypeHandle thApprox, + BOOL fIsInstantiatedType, + ClassInfo * pData) +{ + pData->m_fieldList.Alloc(GetTotalFieldCount(thApprox)); + + // For Generic classes you must get the object size via the type handle, which + // will get you to the right information for the particular instantiation + // you're working with... + pData->m_objectSize = 0; + if ((!thApprox.GetNumGenericArgs()) || fIsInstantiatedType) + { + pData->m_objectSize = thApprox.GetMethodTable()->GetNumInstanceFieldBytes(); + } + +} // DacDbiInterfaceImpl::InitClassData + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetStaticsBases +// Gets the base table addresses for both GC and non-GC statics +// Arguments: +// input: thExact - exact type handle for the class +// pAppDomain - AppDomain in which the class is loaded +// output: ppGCStaticsBase - base pointer for GC statics +// ppNonGCStaticsBase - base pointer for non GC statics +// Notes: +// If this is a non-generic type, or an instantiated type, then we'll be able to get the static var bases +// If the typeHandle represents a generic type constructor (i.e. an uninstantiated generic class), then +// the static bases will be null (since statics are per-instantiation). +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::GetStaticsBases(TypeHandle thExact, + AppDomain * pAppDomain, + PTR_BYTE * ppGCStaticsBase, + PTR_BYTE * ppNonGCStaticsBase) + { + MethodTable * pMT = thExact.GetMethodTable(); + Module * pModuleForStatics = pMT->GetModuleForStatics(); + if (pModuleForStatics != NULL) + { + PTR_DomainLocalModule pLocalModule = pModuleForStatics->GetDomainLocalModule(pAppDomain); + if (pLocalModule != NULL) + { + *ppGCStaticsBase = pLocalModule->GetGCStaticsBasePointer(pMT); + *ppNonGCStaticsBase = pLocalModule->GetNonGCStaticsBasePointer(pMT); + } + } +} // DacDbiInterfaceImpl::GetStaticsBases + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::ComputeFieldData +// Computes the field info for pFD and stores it in pcurrentFieldData +// Arguments: +// input: pFD - FieldDesc used to get necessary information +// pGCStaticsBase - base table address for GC statics +// pNonGCStaticsBase - base table address for non-GC statics +// output: pCurrentFieldData - contains fields to be initialized +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::ComputeFieldData(PTR_FieldDesc pFD, + PTR_BYTE pGCStaticsBase, + PTR_BYTE pNonGCStaticsBase, + FieldData * pCurrentFieldData) +{ + pCurrentFieldData->Initialize(pFD->IsStatic(), pFD->IsPrimitive(), pFD->GetMemberDef()); + +#ifdef EnC_SUPPORTED + // If the field was newly introduced via EnC, and hasn't yet + // been fixed up, then we'll send back a marker indicating + // that it isn't yet available. + if (pFD->IsEnCNew()) + { + // @dbgtodo Microsoft inspection: eliminate the debugger token when ICDClass and ICDType are + // completely DACized + pCurrentFieldData->m_vmFieldDesc.SetHostPtr(pFD); + pCurrentFieldData->m_fFldStorageAvailable = FALSE; + pCurrentFieldData->m_fFldIsTLS = FALSE; + pCurrentFieldData->m_fFldIsContextStatic = FALSE; + pCurrentFieldData->m_fFldIsRVA = FALSE; + pCurrentFieldData->m_fFldIsCollectibleStatic = FALSE; + } + else +#endif // EnC_SUPPORTED + { + // Otherwise, we'll compute the info & send it back. + pCurrentFieldData->m_fFldStorageAvailable = TRUE; + // @dbgtodo Microsoft inspection: eliminate the debugger token when ICDClass and ICDType are + // completely DACized + pCurrentFieldData->m_vmFieldDesc.SetHostPtr(pFD); + pCurrentFieldData->m_fFldIsTLS = (pFD->IsThreadStatic() == TRUE); + pCurrentFieldData->m_fFldIsContextStatic = (pFD->IsContextStatic() == TRUE); + pCurrentFieldData->m_fFldIsRVA = (pFD->IsRVA() == TRUE); + pCurrentFieldData->m_fFldIsCollectibleStatic = (pFD->IsStatic() == TRUE && + pFD->GetEnclosingMethodTable()->Collectible()); + + // Compute the address of the field + if (pFD->IsStatic()) + { + // statics are addressed using an absolute address. + if (pFD->IsRVA()) + { + // RVA statics are relative to a base module address + DWORD offset = pFD->GetOffset(); + PTR_VOID addr = pFD->GetModule()->GetRvaField(offset, pFD->IsZapped()); + if (pCurrentFieldData->OkToGetOrSetStaticAddress()) + { + pCurrentFieldData->SetStaticAddress(PTR_TO_TADDR(addr)); + } + } + else if (pFD->IsThreadStatic() || pFD->IsContextStatic() || + pCurrentFieldData->m_fFldIsCollectibleStatic) + { + // this is a special type of static that must be queried using DB_IPCE_GET_SPECIAL_STATIC + } + else + { + // This is a normal static variable in the GC or Non-GC static base table + PTR_BYTE base = pFD->IsPrimitive() ? pNonGCStaticsBase : pGCStaticsBase; + if (base == NULL) + { + // static var not available. This may be an open generic class (not an instantiated type), + // or we might only have approximate type information because the type hasn't been + // initialized yet. + + if (pCurrentFieldData->OkToGetOrSetStaticAddress()) + { + pCurrentFieldData->SetStaticAddress(NULL); + } + } + else + { + if (pCurrentFieldData->OkToGetOrSetStaticAddress()) + { + // calculate the absolute address using the base and the offset from the base + pCurrentFieldData->SetStaticAddress(PTR_TO_TADDR(base) + pFD->GetOffset()); + } + } + } + } + else + { + // instance variables are addressed using an offset within the instance + if (pCurrentFieldData->OkToGetOrSetInstanceOffset()) + { + pCurrentFieldData->SetInstanceOffset(pFD->GetOffset()); + } + } + } + +} // DacDbiInterfaceImpl::ComputeFieldData + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::CollectFields +// Gets information for all the fields for a given type +// Arguments: +// input: thExact - used to determine whether we need to get statics base tables +// thApprox - used to get the field desc iterator +// pAppDomain - used to get statics base tables +// output: +// pFieldList - contains fields to be initialized +// Note: the caller must ensure that *ppFields is NULL (i.e., any previously allocated memory +// must have been deallocated. +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::CollectFields(TypeHandle thExact, + TypeHandle thApprox, + AppDomain * pAppDomain, + DacDbiArrayList<FieldData> * pFieldList) +{ + PTR_BYTE pGCStaticsBase = NULL; + PTR_BYTE pNonGCStaticsBase = NULL; + if (!thExact.IsNull() && !thExact.GetMethodTable()->Collectible()) + { + // get base tables for static fields + GetStaticsBases(thExact, pAppDomain, &pGCStaticsBase, &pNonGCStaticsBase); + } + + unsigned int fieldCount = 0; + + // <TODO> we are losing exact type information for static fields in generic types. We have + // field desc iterators only for approximate types, but statics are per instantiation, so we + // need an exact type to be able to handle these correctly. We need to use + // FieldDesc::GetExactDeclaringType to get at the correct field. This requires the exact + // TypeHandle. </TODO> + EncApproxFieldDescIterator fdIterator(thApprox.GetMethodTable(), + ApproxFieldDescIterator::ALL_FIELDS, + FALSE); // don't fixup EnC (we can't, we're stopped) + + PTR_FieldDesc pCurrentFD; + int index = 0; + while (((pCurrentFD = fdIterator.Next()) != NULL) && (index < pFieldList->Count())) + { + // fill in the pCurrentEntry structure + ComputeFieldData(pCurrentFD, pGCStaticsBase, pNonGCStaticsBase, &((*pFieldList)[index])); + + // Bump our counts and pointers. + fieldCount++; + index++; + } + _ASSERTE(fieldCount == (unsigned int)pFieldList->Count()); + +} // DacDbiInterfaceImpl::CollectFields + + +// Determine if a type is a ValueType +BOOL DacDbiInterfaceImpl::IsValueType (VMPTR_TypeHandle vmTypeHandle) +{ + DD_ENTER_MAY_THROW; + + TypeHandle th = TypeHandle::FromPtr(vmTypeHandle.GetDacPtr()); + return th.IsValueType(); +} + +// Determine if a type has generic parameters +BOOL DacDbiInterfaceImpl::HasTypeParams (VMPTR_TypeHandle vmTypeHandle) +{ + DD_ENTER_MAY_THROW; + + TypeHandle th = TypeHandle::FromPtr(vmTypeHandle.GetDacPtr()); + return th.ContainsGenericVariables(); +} + +// DacDbi API: Get type information for a class +void DacDbiInterfaceImpl::GetClassInfo(VMPTR_AppDomain vmAppDomain, + VMPTR_TypeHandle vmThExact, + ClassInfo * pData) +{ + DD_ENTER_MAY_THROW; + + AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); + + TypeHandle thExact; + TypeHandle thApprox; + + GetTypeHandles(vmThExact, vmThExact, &thExact, &thApprox); + + // initialize field count, generic args count, size and value class flag + InitClassData(thApprox, false, pData); + + if (pAppDomain != NULL) + CollectFields(thExact, thApprox, pAppDomain, &(pData->m_fieldList)); +} // DacDbiInterfaceImpl::GetClassInfo + +// DacDbi API: Get field information and object size for an instantiated generic type +void DacDbiInterfaceImpl::GetInstantiationFieldInfo (VMPTR_DomainFile vmDomainFile, + VMPTR_TypeHandle vmThExact, + VMPTR_TypeHandle vmThApprox, + DacDbiArrayList<FieldData> * pFieldList, + SIZE_T * pObjectSize) +{ + DD_ENTER_MAY_THROW; + + DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); + _ASSERTE(pDomainFile != NULL); + AppDomain * pAppDomain = pDomainFile->GetAppDomain(); + TypeHandle thExact; + TypeHandle thApprox; + + GetTypeHandles(vmThExact, vmThApprox, &thExact, &thApprox); + + *pObjectSize = thApprox.GetMethodTable()->GetNumInstanceFieldBytes(); + + pFieldList->Alloc(GetTotalFieldCount(thApprox)); + + CollectFields(thExact, thApprox, pAppDomain, pFieldList); + +} // DacDbiInterfaceImpl::GetInstantiationFieldInfo + +//----------------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeDataWalk member functions +//----------------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// TypeDataWalk constructor--initialize the buffer and number of remaining items from input data +// Arguments: pData - pointer to a list of records containing information about type parameters for an +// instantiated type +// nData - number of entries in pData +//----------------------------------------------------------------------------- +DacDbiInterfaceImpl::TypeDataWalk::TypeDataWalk(DebuggerIPCE_TypeArgData * pData, unsigned int nData) +{ + m_pCurrentData = pData; + m_nRemaining = nData; +} // DacDbiInterfaceImpl::TypeDataWalk::TypeDataWalk + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeDataWalk::ReadOne +// read and return a single node from the list of type parameters +// Arguments: none (uses internal state) +// Return value: information about the next type parameter in m_pCurrentData +//----------------------------------------------------------------------------- +DebuggerIPCE_TypeArgData * DacDbiInterfaceImpl::TypeDataWalk::ReadOne() +{ + LIMITED_METHOD_CONTRACT; + if (m_nRemaining) + { + m_nRemaining--; + return m_pCurrentData++; + } + else + { + return NULL; + } +} // DacDbiInterfaceImpl::TypeDataWalk::ReadOne + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeDataWalk::Skip +// Skip a single node from the list of type handles along with any children it might have +// Arguments: none (uses internal state) +// Return value: none (updates internal state) +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::TypeDataWalk::Skip() +{ + LIMITED_METHOD_CONTRACT; + + DebuggerIPCE_TypeArgData * pData = ReadOne(); + if (pData) + { + for (unsigned int i = 0; i < pData->numTypeArgs; i++) + { + Skip(); + } + } +} // DacDbiInterfaceImpl::TypeDataWalk::Skip + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeArg +// Read a type handle when it is used in the position of a generic argument or +// argument of an array or address type. Take into account generic code sharing if we +// have been requested to find the canonical representation amongst a set of shared- +// code generic types. That is, if generics code sharing is enabled then return "Object" +// for all reference types, and canonicalize underneath value types, e.g. V<string> --> V<object>. +// Return TypeHandle() if any of the type handles are not loaded. +// +// Arguments: retrieveWhich - indicates whether to retrieve a canonical representation or +// an exact representation +// Return value: the type handle for the type parameter +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeArg(TypeHandleReadType retrieveWhich) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#if !defined(FEATURE_SHARE_GENERIC_CODE) + return ReadLoadedTypeHandle(kGetExact); +#else + + if (retrieveWhich == kGetExact) + return ReadLoadedTypeHandle(kGetExact); + + // This nasty bit of code works out what the "canonicalization" of a + // parameter to a generic is once we take into account generics code sharing. + // + // This logic is somewhat a duplication of logic in vm\typehandle.cpp, though + // that logic operates on a TypeHandle format, i.e. assumes we're finding the + // canonical form of a type that has already been loaded. Here we are finding + // the canonical form of a type that may not have been loaded (but where we expect + // its canonical form to have been loaded). + // + // Ideally this logic would not be duplicated in this way, but it is difficult + // to arrange for that. + DebuggerIPCE_TypeArgData * pData = ReadOne(); + if (!pData) + return TypeHandle(); + + // If we have code sharing then the process of canonicalizing is trickier. + // unfortunately we have to include the exact specification of compatibility at + // this point. + CorElementType elementType = pData->data.elementType; + + switch (elementType) + { + case ELEMENT_TYPE_PTR: + _ASSERTE(pData->numTypeArgs == 1); + return PtrOrByRefTypeArg(pData, retrieveWhich); + break; + + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_VALUETYPE: + return ClassTypeArg(pData, retrieveWhich); + break; + + case ELEMENT_TYPE_FNPTR: + return FnPtrTypeArg(pData, retrieveWhich); + break; + + default: + return ObjRefOrPrimitiveTypeArg(pData, elementType); + break; + } + +#endif // FEATURE_SHARE_GENERIC_CODE +} // DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeArg + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeHandles +// Iterate through the type argument data, creating type handles as we go. +// +// Arguments: +// input: retrieveWhich - indicates whether we can return a canonical type handle +// or we must return an exact type handle +// nTypeArgs - number of type arguments to be read +// output: ppResults - pointer to a list of TypeHandles that will hold the type handles +// for each type parameter +// +// Return Value: FALSE iff any of the type handles are not loaded. +//----------------------------------------------------------------------------- +BOOL DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeHandles(TypeHandleReadType retrieveWhich, + unsigned int nTypeArgs, + TypeHandle * ppResults) +{ + WRAPPER_NO_CONTRACT; + + BOOL allOK = true; + for (unsigned int i = 0; i < nTypeArgs; i++) + { + ppResults[i] = ReadLoadedTypeArg(retrieveWhich); + allOK &= !ppResults[i].IsNull(); + } + return allOK; +} // DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeHandles + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedInstantiation +// Read an instantiation of a generic type if it has already been created. +// +// Arguments: +// input: retrieveWhich - indicates whether we can return a canonical type handle +// or we must return an exact type handle +// pModule - module in which the instantiated type is loaded +// mdToken - metadata token for the type +// nTypeArgs - number of type arguments to be read +// Return value: the type handle for the instantiated type +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedInstantiation(TypeHandleReadType retrieveWhich, + Module * pModule, + mdTypeDef mdToken, + unsigned int nTypeArgs) +{ + WRAPPER_NO_CONTRACT; + + NewHolder<TypeHandle> pInst(new TypeHandle[nTypeArgs]); + + // get the type handle for each of the type parameters + if (!ReadLoadedTypeHandles(retrieveWhich, nTypeArgs, pInst)) + { + return TypeHandle(); + } + + // get the type handle for the particular instantiation that corresponds to + // the given type parameters + return FindLoadedInstantiation(pModule, mdToken, nTypeArgs, pInst); + +} // DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedInstantiation + + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeHandle +// +// Compute the type handle for a given type. +// This is the top-level function that will return the type handle for an +// arbitrary type. It uses mutual recursion with ReadLoadedTypeArg to get +// the type handle for a (possibly parameterized) type. Note that the referent of +// address types or the element type of an array type are viewed as type parameters. +// +// For example, assume that we are retrieving only exact types, and we have as our +// top level type an array defined as int [][]. +// We start by noting that the type is an array type, so we call ReadLoadedTypeArg to +// get the element type. We find that the element type is also an array:int []. +// ReadLoadedTypeArg will call ReadLoadedTypeHandle with this type information. +// Again, we determine that the top-level type is an array, so we call ReadLoadedTypeArg +// to get the element type, int. ReadLoadedTypeArg will again call ReadLoadedTypeHandle +// which will find that this time, the top-level type is a primitive type. It will request +// the loaded type handle from the loader and return it. On return, we get the type handle +// for an array of int from the loader. We return again and request the type handle for an +// array of arrays of int. This is the type handle we will return. +// +// Arguments: +// input: retrieveWhich - determines whether we can return the type handle for +// a canonical type or only for an exact type +// we use the list of type data stored in the TypeDataWalk data members +// for other input information +// Return value: type handle for the current type. +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeHandle(TypeHandleReadType retrieveWhich) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // get the type information at the head of the list m_pCurrentData + DebuggerIPCE_TypeArgData * pData = ReadOne(); + if (!pData) + return TypeHandle(); + + // get the type handle that corresponds to its elementType + TypeHandle typeHandle; + switch (pData->data.elementType) + { + case ELEMENT_TYPE_ARRAY: + case ELEMENT_TYPE_SZARRAY: + typeHandle = ArrayTypeArg(pData, retrieveWhich); + break; + + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_BYREF: + typeHandle = PtrOrByRefTypeArg(pData, retrieveWhich); + break; + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_VALUETYPE: + { + Module * pModule = pData->data.ClassTypeData.vmModule.GetDacPtr(); + typeHandle = ReadLoadedInstantiation(retrieveWhich, + pModule, + pData->data.ClassTypeData.metadataToken, + pData->numTypeArgs); + } + break; + + case ELEMENT_TYPE_FNPTR: + { + typeHandle = FnPtrTypeArg(pData, retrieveWhich); + } + break; + + default: + typeHandle = FindLoadedElementType(pData->data.elementType); + break; + } + return typeHandle; +} // DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeHandle + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeDataWalk::ArrayTypeArg +// get a loaded type handle for an array type (E_T_ARRAY or E_T_SZARRAY) +// +// Arguments: +// input: pArrayTypeInfo - type information for an array type +// Although this is in fact a pointer (in)to a list, we treat it here +// simply as a pointer to a single instance of DebuggerIPCE_TypeArgData +// which holds type information for an array. +// This is the most recent type node (for an array type) retrieved +// by TypeDataWalk::ReadOne(). The call to ReadLoadedTypeArg will +// result in call(s) to ReadOne to retrieve one or more type nodes +// that are needed to compute the type handle for the +// element type of the array. When we return from that call, we pass +// pArrayTypeInfo along with arrayElementTypeArg to FindLoadedArrayType +// to get the type handle for this particular array type. +// Note: +// On entry, we know that pArrayTypeInfo is the same as m_pCurrentData - 1, +// but by the time we need to use it, this is no longer true. Because +// we can't predict how many nodes will be consumed by the call to +// ReadLoadedTypeArg, we can't compute this value from the member fields +// of TypeDataWalk and therefore pass it as a parameter. +// retrieveWhich - determines whether we can return the type handle for +// a canonical type or only for an exact type +// Return value: the type handle corresponding to the array type +//----------------------------------------------------------------------------- + +TypeHandle DacDbiInterfaceImpl::TypeDataWalk::ArrayTypeArg(DebuggerIPCE_TypeArgData * pArrayTypeInfo, + TypeHandleReadType retrieveWhich) +{ + TypeHandle arrayElementTypeArg = ReadLoadedTypeArg(retrieveWhich); + if (!arrayElementTypeArg.IsNull()) + { + return FindLoadedArrayType(pArrayTypeInfo->data.elementType, + arrayElementTypeArg, + pArrayTypeInfo->data.ArrayTypeData.arrayRank); + } + return TypeHandle(); +} // DacDbiInterfaceImpl::TypeDataWalk::ArrayTypeArg + + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeDataWalk::PtrOrByRefTypeArg +// get a loaded type handle for an address type (E_T_PTR or E_T_BYREF) +// +// Arguments: +// input: pPtrOrByRefTypeInfo - type information for a pointer or byref type +// Although this is in fact a pointer (in)to a list, we treat it here +// simply as a pointer to a single instance of DebuggerIPCE_TypeArgData +// which holds type information for a pointer or byref type. +// This is the most recent type node (for a pointer or byref type) retrieved +// by TypeDataWalk::ReadOne(). The call to ReadLoadedTypeArg will +// result in call(s) to ReadOne to retrieve one or more type nodes +// that are needed to compute the type handle for the +// referent type of the pointer. When we return from that call, we pass +// pPtrOrByRefTypeInfo along with referentTypeArg to FindLoadedPointerOrByrefType +// to get the type handle for this particular pointer or byref type. +// Note: +// On entry, we know that pPtrOrByRefTypeInfo is the same as m_pCurrentData - 1, +// but by the time we need to use it, this is no longer true. Because +// we can't predict how many nodes will be consumed by the call to +// ReadLoadedTypeArg, we can't compute this value from the member fields +// of TypeDataWalk and therefore pass it as a parameter. +// retrieveWhich - determines whether we can return the type handle for +// a canonical type or only for an exact type +// Return value: the type handle corresponding to the address type +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::TypeDataWalk::PtrOrByRefTypeArg(DebuggerIPCE_TypeArgData * pPtrOrByRefTypeInfo, + TypeHandleReadType retrieveWhich) +{ + TypeHandle referentTypeArg = ReadLoadedTypeArg(retrieveWhich); + if (!referentTypeArg.IsNull()) + { + return FindLoadedPointerOrByrefType(pPtrOrByRefTypeInfo->data.elementType, referentTypeArg); + } + + return TypeHandle(); + +} // DacDbiInterfaceImpl::TypeDataWalk::PtrOrByRefTypeArg + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeDataWalk::ClassTypeArg +// get a loaded type handle for a class type (E_T_CLASS or E_T_VALUETYPE) +// +// Arguments: +// input: pClassTypeInfo - type information for a class type +// Although this is in fact a pointer (in)to a list, we treat it here +// simply as a pointer to a single instance of DebuggerIPCE_TypeArgData +// which holds type information for a pointer or byref type. +// This is the most recent type node (for a pointer or byref type) retrieved +// by TypeDataWalk::ReadOne(). The call to ReadLoadedInstantiation will +// result in call(s) to ReadOne to retrieve one or more type nodes +// that are needed to compute the type handle for the type parameters +// for the class. If we can't find an exact loaded type for the class, we will +// instead return a canonical method table. In this case, we need to skip +// the type parameter information for each actual parameter to the class. +// This is necessary because we may be getting a type handle for a class which is +// in turn an argument to a parent type. If the parent type has more arguments, we +// need to be at the right place in the list when we return. We use +// pClassTypeInfo to get the number of type arguments that we need to skip. +// retrieveWhich - determines whether we can return the type handle for +// a canonical type or only for an exact type +// Return value: the type handle corresponding to the class type +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::TypeDataWalk::ClassTypeArg(DebuggerIPCE_TypeArgData * pClassTypeInfo, + TypeHandleReadType retrieveWhich) +{ + Module * pModule = pClassTypeInfo->data.ClassTypeData.vmModule.GetDacPtr(); + TypeHandle typeDef = ClassLoader::LookupTypeDefOrRefInModule(pModule, + pClassTypeInfo->data.ClassTypeData.metadataToken); + + if ((!typeDef.IsNull() && typeDef.IsValueType()) || (pClassTypeInfo->data.elementType == ELEMENT_TYPE_VALUETYPE)) + { + return ReadLoadedInstantiation(retrieveWhich, + pModule, + pClassTypeInfo->data.ClassTypeData.metadataToken, + pClassTypeInfo->numTypeArgs); + } + else + { + _ASSERTE(retrieveWhich == kGetCanonical); + // skip the instantiation - no need to look at it since the type canonicalizes to "Object" + for (unsigned int i = 0; i < pClassTypeInfo->numTypeArgs; i++) + { + Skip(); + } + return TypeHandle(g_pCanonMethodTableClass); + } +}// DacDbiInterfaceImpl::TypeDataWalk::ClassTypeArg + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeDataWalk::FnPtrTypeArg +// get a loaded type handle for a function pointer type (E_T_FNPTR) +// +// Arguments: +// input: pFnPtrTypeInfo - type information for a pointer or byref type +// Although this is in fact a pointer (in)to a list, we treat it here +// simply as a pointer to a single instance of DebuggerIPCE_TypeArgData +// which holds type information for a function pointer type. +// This is the most recent type node (for a function pointer type) retrieved +// by TypeDataWalk::ReadOne(). The call to ReadLoadedTypeHandles will +// result in call(s) to ReadOne to retrieve one or more type nodes +// that are needed to compute the type handle for the return type and +// parameter types of the function. When we return from that call, we pass +// pFnPtrTypeInfo along with pInst to FindLoadedFnptrType +// to get the type handle for this particular function pointer type. +// retrieveWhich - determines whether we can return the type handle for +// a canonical type or only for an exact type +// Return value: the type handle corresponding to the function pointer type +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::TypeDataWalk::FnPtrTypeArg(DebuggerIPCE_TypeArgData * pFnPtrTypeInfo, + TypeHandleReadType retrieveWhich) +{ + // allocate space to store a list of type handles, one for the return type and one for each + // of the parameter types of the function to which the FnPtr type refers. + NewHolder<TypeHandle> pInst(new TypeHandle[sizeof(TypeHandle) * pFnPtrTypeInfo->numTypeArgs]); + + if (ReadLoadedTypeHandles(retrieveWhich, pFnPtrTypeInfo->numTypeArgs, pInst)) + { + return FindLoadedFnptrType(pFnPtrTypeInfo->numTypeArgs, pInst); + } + + return TypeHandle(); + +} // DacDbiInterfaceImpl::TypeDataWalk::FnPtrTypeArg + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeDataWalk::ObjRefOrPrimitiveTypeArg +// get a loaded type handle for a primitive type or ObjRef +// +// Arguments: +// input: pArgInfo - type information for an objref or primitive type. +// This is called only when the objref or primitive type +// is a type argument for a parent type. In this case, +// we treat all objrefs the same, that is, we don't care +// about type parameters for the referent. Instead, we will +// simply return the canonical object type handle as the type +// of the referent. <@dbgtodo Microsoft: why is this?> +// If this is a primitive type, we'll simply get the +// type handle for that type. +// elementType - type of the argument +// Return value: the type handle corresponding to the elementType +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::TypeDataWalk::ObjRefOrPrimitiveTypeArg(DebuggerIPCE_TypeArgData * pArgInfo, + CorElementType elementType) +{ + // If there are any type args (e.g. for arrays) they can be skipped. The thing + // is a reference type anyway. + for (unsigned int i = 0; i < pArgInfo->numTypeArgs; i++) + { + Skip(); + } + + // for an ObjRef, just return the CLASS____CANON type handle + if (CorTypeInfo::IsObjRef_NoThrow(elementType)) + { + return TypeHandle(g_pCanonMethodTableClass); + } + else + { + return FindLoadedElementType(elementType); + } +} // DacDbiInterfaceImpl::TypeDataWalk::ObjRefOrPrimitiveTypeArg + + +//------------------------------------------------------------------------- +// end of TypeDataWalk implementations +//------------------------------------------------------------------------- +//------------------------------------------------------------------------- +// functions to use loader to get type handles +// ------------------------------------------------------------------------ + +// Note, in these functions, the use of ClassLoader::DontLoadTypes was chosen +// instead of FailIfNotLoaded because, although we may want to debug unrestored +// VCs, we can't do it because the debug API is not set up to handle them. +// +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::FindLoadedArrayType +// Use ClassLoader to find a loaded type handle for an array type (E_T_ARRAY or E_T_SZARRAY) +// Arguments: +// input: arrayType - type of the array +// TypeArg - type handle for the base type +// rank - array rank +// Return Value: type handle for the array type +//----------------------------------------------------------------------------- +// static +TypeHandle DacDbiInterfaceImpl::FindLoadedArrayType(CorElementType arrayType, + TypeHandle typeArg, + unsigned rank) +{ + // Lookup operations run the class loader in non-load mode. + ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); + + if (typeArg.IsNull()) + { + return TypeHandle(); + } + else + { + return ClassLoader::LoadArrayTypeThrowing(typeArg, + arrayType, + rank, + ClassLoader::DontLoadTypes ); + } +} // DacDbiInterfaceImpl::FindLoadedArrayType; + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::FindLoadedPointerOrByrefType +// Use ClassLoader to find a loaded type handle for an address type (E_T_PTR or E_T_BYREF) +// Arguments: +// input: addressType - type of the address type +// TypeArg - type handle for the base type +// Return Value: type handle for the address type +//----------------------------------------------------------------------------- +// static +TypeHandle DacDbiInterfaceImpl::FindLoadedPointerOrByrefType(CorElementType addressType, TypeHandle typeArg) +{ + // Lookup operations run the class loader in non-load mode. + ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); + + return ClassLoader::LoadPointerOrByrefTypeThrowing(addressType, + typeArg, + ClassLoader::DontLoadTypes); +} // DacDbiInterfaceImpl::FindLoadedPointerOrByrefType + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::FindLoadedFnptrType +// Use ClassLoader to find a loaded type handle for a function pointer type (E_T_FNPTR) +// Arguments: +// input: pInst - type handles of the function's return value and arguments +// numTypeArgs - number of type handles in pInst +// Return Value: type handle for the function pointer type +//----------------------------------------------------------------------------- +// static +TypeHandle DacDbiInterfaceImpl::FindLoadedFnptrType(DWORD numTypeArgs, TypeHandle * pInst) +{ + // Lookup operations run the class loader in non-load mode. + ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); + + // @dbgtodo : Do we need to worry about calling convention here? + return ClassLoader::LoadFnptrTypeThrowing(0, + numTypeArgs, + pInst, + ClassLoader::DontLoadTypes); +} // DacDbiInterfaceImpl::FindLoadedFnptrType + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::FindLoadedInstantiation +// Use ClassLoader to find a loaded type handle for a particular instantiation of a +// class type (E_T_CLASS or E_T_VALUECLASS) +// +// Arguments: +// input: pModule - module in which the type is loaded +// mdToken - metadata token for the type +// nTypeArgs - number of type arguments in pInst +// pInst - list of type handles for the type parameters +// Return value: type handle for the instantiated class type +//----------------------------------------------------------------------------- +// static +TypeHandle DacDbiInterfaceImpl::FindLoadedInstantiation(Module * pModule, + mdTypeDef mdToken, + DWORD nTypeArgs, + TypeHandle * pInst) +{ + // Lookup operations run the class loader in non-load mode. + ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); + + return ClassLoader::LoadGenericInstantiationThrowing(pModule, + mdToken, + Instantiation(pInst,nTypeArgs), + ClassLoader::DontLoadTypes); + +} // DacDbiInterfaceImpl::FindLoadedInstantiation + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::FindLoadedElementType +// Get the type handle for a primitive type +// Arguments: +// input: elementType - type of the primitive type +// Return Value: Type handle for the primitive type +//----------------------------------------------------------------------------- +// static +TypeHandle DacDbiInterfaceImpl::FindLoadedElementType(CorElementType elementType) +{ + // Lookup operations run the class loader in non-load mode. + ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); + + MethodTable * pMethodTable = (&g_Mscorlib)->GetElementType(elementType); + + return TypeHandle(pMethodTable); +} // DacDbiInterfaceImpl::FindLoadedElementType + + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetArrayTypeInfo +// Gets additional information to convert a type handle to an instance of CordbType if the type is E_T_ARRAY. +// Specifically, we get the rank and the type of the array elements +// +// Arguments: +// input: typeHandle - type handle for the array type +// pAppDomain - AppDomain into which the type is loaded +// output: pTypeInfo - information for the array rank and element type +// +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::GetArrayTypeInfo(TypeHandle typeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo, + AppDomain * pAppDomain) +{ + _ASSERTE(typeHandle.IsArray()); + pTypeInfo->ArrayTypeData.arrayRank = typeHandle.AsArray()->GetRank(); + TypeHandleToBasicTypeInfo(typeHandle.AsArray()->GetArrayElementTypeHandle(), + &(pTypeInfo->ArrayTypeData.arrayTypeArg), + pAppDomain); +} // DacDbiInterfaceImpl::GetArrayTypeInfo + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetPtrTypeInfo +// Gets additional information to convert a type handle to an instance of CordbType if the type is +// E_T_PTR or E_T_BYREF. Specifically, we get the type for the referent of the address type +// +// Arguments: +// input: boxed - indicates what, if anything, is boxed (see code:AreValueTypesBoxed for +// more specific information) +// typeHandle - type handle for the address type +// pAppDomain - AppDomain into which the type is loaded +// output: pTypeInfo - information for the referent type +// +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::GetPtrTypeInfo(AreValueTypesBoxed boxed, + TypeHandle typeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo, + AppDomain * pAppDomain) +{ + if (boxed == AllBoxed) + { + GetClassTypeInfo(typeHandle, pTypeInfo, pAppDomain); + } + else + { + _ASSERTE(typeHandle.IsTypeDesc()); + TypeHandleToBasicTypeInfo(typeHandle.AsTypeDesc()->GetTypeParam(), + &(pTypeInfo->UnaryTypeData.unaryTypeArg), + pAppDomain); + } +} // DacDbiInterfaceImpl::GetPtrTypeInfo + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetFnPtrTypeInfo +// Gets additional information to convert a type handle to an instance of CordbType if the type is +// E_T_FNPTR, specifically the typehandle for the referent. +// +// Arguments +// input: boxed - indicates what, if anything, is boxed (see code:AreValueTypesBoxed for +// more specific information) +// typeHandle - type handle for the address type +// pAppDomain - AppDomain into which the type is loaded +// output: pTypeInfo - information for the referent type +// +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::GetFnPtrTypeInfo(AreValueTypesBoxed boxed, + TypeHandle typeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo, + AppDomain * pAppDomain) +{ + if (boxed == AllBoxed) + { + GetClassTypeInfo(typeHandle, pTypeInfo, pAppDomain); + } + else + { + pTypeInfo->NaryTypeData.typeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); + } +} // DacDbiInterfaceImpl::GetFnPtrTypeInfo + + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetClassTypeInfo +// Gets additional information to convert a type handle to an instance of CordbType if the type is +// E_T_CLASS or E_T_VALUETYPE +// +// Arguments +// input: typeHandle - type handle for the address type +// pAppDomain - AppDomain into which the type is loaded +// output: pTypeInfo - information for the referent type +// +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::GetClassTypeInfo(TypeHandle typeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo, + AppDomain * pAppDomain) +{ + Module * pModule = typeHandle.GetModule(); + + if (typeHandle.HasInstantiation()) // the type handle represents a generic instantiation + { + pTypeInfo->ClassTypeData.typeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); + } + else // non-generic + { + pTypeInfo->ClassTypeData.typeHandle = VMPTR_TypeHandle::NullPtr(); + } + + pTypeInfo->ClassTypeData.metadataToken = typeHandle.GetCl(); + + _ASSERTE(pModule); + pTypeInfo->ClassTypeData.vmModule.SetDacTargetPtr(PTR_HOST_TO_TADDR(pModule)); + if (pAppDomain) + { + pTypeInfo->ClassTypeData.vmDomainFile.SetDacTargetPtr(PTR_HOST_TO_TADDR(pModule->GetDomainFile(pAppDomain))); + } + else + { + pTypeInfo->ClassTypeData.vmDomainFile = VMPTR_DomainFile::NullPtr(); + } +} // DacDbiInterfaceImpl::GetClassTypeInfo + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetElementType +// Gets the correct CorElementType value from a type handle +// +// Arguments +// input: typeHandle - type handle for the address type +// Return Value: the CorElementType enum value for the type handle +//----------------------------------------------------------------------------- +CorElementType DacDbiInterfaceImpl::GetElementType (TypeHandle typeHandle) +{ + if (typeHandle.IsNull()) + { + return ELEMENT_TYPE_VOID; + } + else if (typeHandle.GetMethodTable() == g_pObjectClass) + { + return ELEMENT_TYPE_OBJECT; + } + else if (typeHandle.GetMethodTable() == g_pStringClass) + { + return ELEMENT_TYPE_STRING; + } + else + { + // GetSignatureCorElementType returns E_T_CLASS for E_T_STRING... :-( + return typeHandle.GetSignatureCorElementType(); + } + +} // DacDbiInterfaceImpl::GetElementType + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo +// Gets additional information to convert a type handle to an instance of CordbType for the referent of an +// E_T_BYREF or E_T_PTR or for the element type of an E_T_ARRAY or E_T_SZARRAY +// +// Arguments: +// input: typeHandle - type handle for the address type +// pAppDomain - AppDomain into which the type is loaded +// output: pTypeInfo - information for the referent type +// +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo(TypeHandle typeHandle, + DebuggerIPCE_BasicTypeData * pTypeInfo, + AppDomain * pAppDomain) +{ + pTypeInfo->elementType = GetElementType(typeHandle); + + switch (pTypeInfo->elementType) + { + case ELEMENT_TYPE_ARRAY: + case ELEMENT_TYPE_SZARRAY: + case ELEMENT_TYPE_FNPTR: + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_BYREF: + pTypeInfo->vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); + pTypeInfo->metadataToken = mdTokenNil; + pTypeInfo->vmDomainFile = VMPTR_DomainFile::NullPtr(); + break; + + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_VALUETYPE: + { + Module * pModule = typeHandle.GetModule(); + + if (typeHandle.HasInstantiation()) // only set if instantiated + { + pTypeInfo->vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); + } + else + { + pTypeInfo->vmTypeHandle = VMPTR_TypeHandle::NullPtr(); + } + + pTypeInfo->metadataToken = typeHandle.GetCl(); + _ASSERTE(pModule); + + pTypeInfo->vmModule.SetDacTargetPtr(PTR_HOST_TO_TADDR(pModule)); + if (pAppDomain) + { + pTypeInfo->vmDomainFile.SetDacTargetPtr(PTR_HOST_TO_TADDR(pModule->GetDomainFile(pAppDomain))); + } + else + { + pTypeInfo->vmDomainFile = VMPTR_DomainFile::NullPtr(); + } + break; + } + + default: + pTypeInfo->vmTypeHandle = VMPTR_TypeHandle::NullPtr(); + pTypeInfo->metadataToken = mdTokenNil; + pTypeInfo->vmDomainFile = VMPTR_DomainFile::NullPtr(); + break; + } + return; +} // DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo + + +void DacDbiInterfaceImpl::GetObjectExpandedTypeInfoFromID(AreValueTypesBoxed boxed, + VMPTR_AppDomain vmAppDomain, + COR_TYPEID id, + DebuggerIPCE_ExpandedTypeData *pTypeInfo) +{ + DD_ENTER_MAY_THROW; + + PTR_MethodTable pMT(TO_TADDR(id.token1)); + + if (pMT->IsArray()) + { + // ArrayBase::GetTypeHandle() may return a NULL handle in corner case scenarios. This check prevents + // us from an AV but doesn't actually fix the problem. See DevDiv 653441 for more info. + TypeHandle arrayHandle = ArrayBase::GetTypeHandle(pMT); + if (arrayHandle.IsNull()) + { + ThrowHR(CORDBG_E_CLASS_NOT_LOADED); + } + + TypeHandleToExpandedTypeInfoImpl(boxed, vmAppDomain, arrayHandle, pTypeInfo); + } + else + { + TypeHandleToExpandedTypeInfoImpl(boxed, vmAppDomain, TypeHandle::FromPtr(TO_TADDR(id.token1)), pTypeInfo); + } +} + +void DacDbiInterfaceImpl::GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, + VMPTR_AppDomain vmAppDomain, + CORDB_ADDRESS addr, + DebuggerIPCE_ExpandedTypeData *pTypeInfo) +{ + DD_ENTER_MAY_THROW; + + PTR_Object obj(TO_TADDR(addr)); + TypeHandleToExpandedTypeInfoImpl(boxed, vmAppDomain, obj->GetGCSafeTypeHandle(), pTypeInfo); +} + +// DacDbi API: use a type handle to get the information needed to create the corresponding RS CordbType instance +void DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, + VMPTR_AppDomain vmAppDomain, + VMPTR_TypeHandle vmTypeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo) +{ + DD_ENTER_MAY_THROW; + + + TypeHandle typeHandle = TypeHandle::FromPtr(vmTypeHandle.GetDacPtr()); + TypeHandleToExpandedTypeInfoImpl(boxed, vmAppDomain, typeHandle, pTypeInfo); +} + + +void DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfoImpl(AreValueTypesBoxed boxed, + VMPTR_AppDomain vmAppDomain, + TypeHandle typeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo) +{ + AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); + pTypeInfo->elementType = GetElementType(typeHandle); + + switch (pTypeInfo->elementType) + { + case ELEMENT_TYPE_ARRAY: + case ELEMENT_TYPE_SZARRAY: + GetArrayTypeInfo(typeHandle, pTypeInfo, pAppDomain); + break; + + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_BYREF: + GetPtrTypeInfo(boxed, typeHandle, pTypeInfo, pAppDomain); + break; + + case ELEMENT_TYPE_VALUETYPE: + if (boxed == OnlyPrimitivesUnboxed || boxed == AllBoxed) + { + pTypeInfo->elementType = ELEMENT_TYPE_CLASS; + } + GetClassTypeInfo(typeHandle, pTypeInfo, pAppDomain); + break; + + case ELEMENT_TYPE_CLASS: + GetClassTypeInfo(typeHandle, pTypeInfo, pAppDomain); + break; + + case ELEMENT_TYPE_FNPTR: + GetFnPtrTypeInfo(boxed, typeHandle, pTypeInfo, pAppDomain); + break; + default: + if (boxed == AllBoxed) + { + pTypeInfo->elementType = ELEMENT_TYPE_CLASS; + GetClassTypeInfo(typeHandle, pTypeInfo, pAppDomain); + } + // else the element type is sufficient + break; + } + LOG((LF_CORDB, LL_INFO10000, "D::THTETI: converted left-side type handle to expanded right-side type info, pTypeInfo->ClassTypeData.typeHandle = 0x%08x.\n", pTypeInfo->ClassTypeData.typeHandle.GetRawPtr())); + return; +} // DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfo + +// Get type handle for a TypeDef token, if one exists. For generics this returns the open type. +VMPTR_TypeHandle DacDbiInterfaceImpl::GetTypeHandle(VMPTR_Module vmModule, + mdTypeDef metadataToken) +{ + DD_ENTER_MAY_THROW; + Module* pModule = vmModule.GetDacPtr(); + VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); + + TypeHandle th = ClassLoader::LookupTypeDefOrRefInModule(pModule, metadataToken); + if (th.IsNull()) + { + LOG((LF_CORDB, LL_INFO10000, "D::GTH: class isn't loaded.\n")); + ThrowHR(CORDBG_E_CLASS_NOT_LOADED); + } + + vmTypeHandle.SetDacTargetPtr(th.AsTAddr()); + return vmTypeHandle; +} + +// DacDbi API: GetAndSendApproxTypeHandle finds the type handle for the layout of the instance fields of an +// instantiated type if it is available. +VMPTR_TypeHandle DacDbiInterfaceImpl::GetApproxTypeHandle(TypeInfoList * pTypeData) +{ + DD_ENTER_MAY_THROW; + + LOG((LF_CORDB, LL_INFO10000, "D::GATH: getting info.\n")); + + + TypeDataWalk walk(&((*pTypeData)[0]), pTypeData->Count()); + TypeHandle typeHandle = walk.ReadLoadedTypeHandle(TypeDataWalk::kGetCanonical); + VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); + + vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); + if (!typeHandle.IsNull()) + { + vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); + } + else + { + ThrowHR(CORDBG_E_CLASS_NOT_LOADED); + } + + LOG((LF_CORDB, LL_INFO10000, + "D::GATH: sending result, result = 0x%0x8\n", + typeHandle)); + return vmTypeHandle; +} // DacDbiInterfaceImpl::GetApproxTypeHandle + +// DacDbiInterface API: Get the exact type handle from type data +HRESULT DacDbiInterfaceImpl::GetExactTypeHandle(DebuggerIPCE_ExpandedTypeData * pTypeData, + ArgInfoList * pArgInfo, + VMPTR_TypeHandle& vmTypeHandle) +{ + DD_ENTER_MAY_THROW; + + LOG((LF_CORDB, LL_INFO10000, "D::GETH: getting info.\n")); + + HRESULT hr = S_OK; + + EX_TRY + { + vmTypeHandle = vmTypeHandle.NullPtr(); + + // convert the type information to a type handle + TypeHandle typeHandle = ExpandedTypeInfoToTypeHandle(pTypeData, pArgInfo); + _ASSERTE(!typeHandle.IsNull()); + vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); + } + EX_CATCH_HRESULT(hr); + + return hr; +} // DacDbiInterfaceImpl::GetExactTypeHandle + +// Retrieve the generic type params for a given MethodDesc. This function is specifically +// for stackwalking because it requires the generic type token on the stack. +void DacDbiInterfaceImpl::GetMethodDescParams( + VMPTR_AppDomain vmAppDomain, + VMPTR_MethodDesc vmMethodDesc, + GENERICS_TYPE_TOKEN genericsToken, + UINT32 * pcGenericClassTypeParams, + TypeParamsList * pGenericTypeParams) +{ + DD_ENTER_MAY_THROW; + + if (vmAppDomain.IsNull() || vmMethodDesc.IsNull()) + { + ThrowHR(E_INVALIDARG); + } + + _ASSERTE((pcGenericClassTypeParams != NULL) && (pGenericTypeParams != NULL)); + + MethodDesc * pMD = vmMethodDesc.GetDacPtr(); + + // Retrieve the number of type parameters for the class and + // the number of type parameters for the method itself. + // For example, the method Foo<T, U>::Bar<V>() has 2 class type parameters and 1 method type parameters. + UINT32 cGenericClassTypeParams = pMD->GetNumGenericClassArgs(); + UINT32 cGenericMethodTypeParams = pMD->GetNumGenericMethodArgs(); + UINT32 cTotalGenericTypeParams = cGenericClassTypeParams + cGenericMethodTypeParams; + + // Set the out parameter. + *pcGenericClassTypeParams = cGenericClassTypeParams; + + TypeHandle thSpecificClass; + MethodDesc * pSpecificMethod; + + // Try to retrieve a more specific MethodDesc and TypeHandle via the generics type token. + // The generics token is not always guaranteed to be available. + // For example, it may be unavailable in prologs and epilogs. + // In dumps, not available can also mean a thrown exception for missing memory. + BOOL fExact = FALSE; + ALLOW_DATATARGET_MISSING_MEMORY( + fExact = Generics::GetExactInstantiationsOfMethodAndItsClassFromCallInformation( + pMD, + PTR_VOID((TADDR)genericsToken), + &thSpecificClass, + &pSpecificMethod); + ); + if (!fExact || + !thSpecificClass.GetMethodTable()->SanityCheck() || + !pSpecificMethod->GetMethodTable()->SanityCheck()) + { + // Use the canonical MethodTable and MethodDesc if the exact generics token is not available. + thSpecificClass = TypeHandle(pMD->GetMethodTable()); + pSpecificMethod = pMD; + } + + // Retrieve the array of class type parameters and the array of method type parameters. + Instantiation classInst = pSpecificMethod->GetExactClassInstantiation(thSpecificClass); + Instantiation methodInst = pSpecificMethod->GetMethodInstantiation(); + + _ASSERTE((classInst.IsEmpty()) == (cGenericClassTypeParams == 0)); + _ASSERTE((methodInst.IsEmpty()) == (cGenericMethodTypeParams == 0)); + + // allocate memory for the return array + pGenericTypeParams->Alloc(cTotalGenericTypeParams); + + for (UINT32 i = 0; i < cTotalGenericTypeParams; i++) + { + // Retrieve the current type parameter depending on the index. + TypeHandle thCurrent; + if (i < cGenericClassTypeParams) + { + thCurrent = classInst[i]; + } + else + { + thCurrent = methodInst[i - cGenericClassTypeParams]; + } + + // There is the possiblity that we'll get this far with a dump and not fail, but still + // not be able to get full info for a particular param. + EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER + { + // Fill in the struct using the TypeHandle of the current type parameter if we can. + VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); + vmTypeHandle.SetDacTargetPtr(thCurrent.AsTAddr()); + TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, + vmAppDomain, + vmTypeHandle, + &((*pGenericTypeParams)[i])); + } + EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER + { + // On failure for a particular type, default it back to System.__Canon. + VMPTR_TypeHandle vmTHCanon = VMPTR_TypeHandle::NullPtr(); + TypeHandle thCanon = TypeHandle(g_pCanonMethodTableClass); + vmTHCanon.SetDacTargetPtr(thCanon.AsTAddr()); + TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, + vmAppDomain, + vmTHCanon, + &((*pGenericTypeParams)[i])); + } + EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER + } +} + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetClassOrValueTypeHandle +// get a typehandle for a class or valuetype from basic type data (metadata token +// and domain file). +// Arguments: +// input: pData - contains the metadata token and domain file +// Return value: the type handle for the corresponding type +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::GetClassOrValueTypeHandle(DebuggerIPCE_BasicTypeData * pData) +{ + TypeHandle typeHandle; + + // if we already have a type handle, just return it + if (!pData->vmTypeHandle.IsNull()) + { + typeHandle = TypeHandle::FromPtr(pData->vmTypeHandle.GetDacPtr()); + } + // otherwise, have the loader look it up using the metadata token and domain file + else + { + DomainFile * pDomainFile = pData->vmDomainFile.GetDacPtr(); + Module * pModule = pDomainFile->GetModule(); + + typeHandle = ClassLoader::LookupTypeDefOrRefInModule(pModule, pData->metadataToken); + if (typeHandle.IsNull()) + { + LOG((LF_CORDB, LL_INFO10000, "D::BTITTH: class isn't loaded.\n")); + ThrowHR(CORDBG_E_CLASS_NOT_LOADED); + } + + _ASSERTE(typeHandle.GetNumGenericArgs() == 0); + } + + return typeHandle; + +} // DacDbiInterfaceImpl::GetClassOrValueTypeHandle + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetExactArrayTypeHandle +// get an exact type handle for an array type +// Arguments: +// input: pTopLevelTypeData - type information for a top-level array type +// pArgInfo - contains the following information: +// m_genericArgsCount - number of generic parameters for the element type--this should be 1 +// m_pGenericArgs - pointer to the generic parameter for the element type--this is +// effectively a one-element list. These are the actual parameters +// Return Value: the exact type handle for the type +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::GetExactArrayTypeHandle(DebuggerIPCE_ExpandedTypeData * pTopLevelTypeData, + ArgInfoList * pArgInfo) +{ + TypeHandle typeArg; + + _ASSERTE(pArgInfo->Count() == 1); + + // get the type handle for the element type + typeArg = BasicTypeInfoToTypeHandle(&((*pArgInfo)[0])); + + // get the exact type handle for the array type + return FindLoadedArrayType(pTopLevelTypeData->elementType, + typeArg, + pTopLevelTypeData->ArrayTypeData.arrayRank); + +} // DacDbiInterfaceImpl::GetExactArrayTypeHandle + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetExactPtrOrByRefTypeHandle +// get an exact type handle for a PTR or BYREF type +// Arguments: +// input: pTopLevelTypeData - type information for the PTR or BYREF type +// pArgInfo - contains the following information: +// m_genericArgsCount - number of generic parameters for the element type--this should be 1 +// m_pGenericArgs - pointer to the generic parameter for the element type--this is +// effectively a one-element list. These are the actual parameters +// Return Value: the exact type handle for the type +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::GetExactPtrOrByRefTypeHandle(DebuggerIPCE_ExpandedTypeData * pTopLevelTypeData, + ArgInfoList * pArgInfo) +{ + TypeHandle typeArg; + _ASSERTE(pArgInfo->Count() == 1); + + // get the type handle for the referent + typeArg = BasicTypeInfoToTypeHandle(&((*pArgInfo)[0])); + + // get the exact type handle for the PTR or BYREF type + return FindLoadedPointerOrByrefType(pTopLevelTypeData->elementType, typeArg); + +} // DacDbiInterfaceImpl::GetExactPtrOrByRefTypeHandle + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetExactClassTypeHandle +// get an exact type handle for a CLASS or VALUETYPE type +// Arguments: +// input: pTopLevelTypeData - type information for the CLASS or VALUETYPE type +// pArgInfo - contains the following information: +// m_genericArgsCount - number of generic parameters for the class +// m_pGenericArgs - list of generic parameters for the class--these +// are the actual parameters +// Return Value: the exact type handle for the type +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::GetExactClassTypeHandle(DebuggerIPCE_ExpandedTypeData * pTopLevelTypeData, + ArgInfoList * pArgInfo) +{ + Module * pModule = pTopLevelTypeData->ClassTypeData.vmModule.GetDacPtr(); + int argCount = pArgInfo->Count(); + + TypeHandle typeConstructor = + ClassLoader::LookupTypeDefOrRefInModule(pModule, pTopLevelTypeData->ClassTypeData.metadataToken); + + // If we can't find the class, throw the appropriate HR. Note: if the class is not a value class and + // the class is also not restored, then we must pretend that the class is still not loaded. We are gonna let + // unrestored value classes slide, though, and special case access to the class's parent below. + if (typeConstructor.IsNull()) + { + LOG((LF_CORDB, LL_INFO10000, "D::ETITTH: class isn't loaded.\n")); + ThrowHR(CORDBG_E_CLASS_NOT_LOADED); + } + + // if there are no generic parameters, we already have the correct type handle + if (argCount == 0) + { + return typeConstructor; + } + + // we have generic parameters--first validate we have a number consistent with the list + // of parameters we received + if ((unsigned int)argCount != typeConstructor.GetNumGenericArgs()) + { + LOG((LF_CORDB, LL_INFO10000, + "D::ETITTH: wrong number of type parameters, %d given, %d expected\n", + argCount, typeConstructor.GetNumGenericArgs())); + _ASSERTE((unsigned int)argCount == typeConstructor.GetNumGenericArgs()); + ThrowHR(E_FAIL); + } + + // now we allocate a list to store the type handles for each parameter + S_UINT32 allocSize = S_UINT32(argCount) * S_UINT32(sizeof(TypeHandle)); + if (allocSize.IsOverflow()) + { + ThrowHR(E_OUTOFMEMORY); + } + + NewHolder<TypeHandle> pInst(new TypeHandle[allocSize.Value()]); + + // convert the type information for each parameter to its corresponding type handle + // and store it in the list + for (unsigned int i = 0; i < (unsigned int)argCount; i++) + { + pInst[i] = BasicTypeInfoToTypeHandle(&((*pArgInfo)[i])); + } + + // Finally, we find the type handle corresponding to this particular instantiation + return FindLoadedInstantiation(typeConstructor.GetModule(), + typeConstructor.GetCl(), + argCount, + pInst); + +} // DacDbiInterfaceImpl::GetExactClassTypeHandle + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetExactFnPtrTypeHandle +// get an exact type handle for a FNPTR type +// Arguments: +// input: pArgInfo - Contains the following information: +// m_genericArgsCount - number of generic parameters for the referent +// m_pGenericArgs - list of generic parameters for the referent--these +// are the actual parameters for the function signature +// Return Value: the exact type handle for the type +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::GetExactFnPtrTypeHandle(ArgInfoList * pArgInfo) +{ + // allocate a list to store the type handles for each parameter + S_UINT32 allocSize = S_UINT32(pArgInfo->Count()) * S_UINT32(sizeof(TypeHandle)); + if( allocSize.IsOverflow() ) + { + ThrowHR(E_OUTOFMEMORY); + } + NewHolder<TypeHandle> pInst(new TypeHandle[allocSize.Value()]); + + // convert the type information for each parameter to its corresponding type handle + // and store it in the list + for (int i = 0; i < pArgInfo->Count(); i++) + { + pInst[i] = BasicTypeInfoToTypeHandle(&((*pArgInfo)[i])); + } + + // find the type handle corresponding to this particular FNPTR + return FindLoadedFnptrType(pArgInfo->Count(), pInst); +} // DacDbiInterfaceImpl::GetExactFnPtrTypeHandle + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::BasicTypeInfoToTypeHandle +// Convert basic type info for a type parameter that came from a top-level type to +// the corresponding type handle. If the type parameter is an array or pointer +// type, we simply extract the LS type handle from the VMPTR_TypeHandle that is +// part of the type information. If the type parameter is a class or value type, +// we use the metadata token and domain file in the type info to look up the +// appropriate type handle. If the type parameter is any other types, we get the +// type handle by having the loader look up the type handle for the element type. +// Arguments: +// input: pArgTypeData - basic type information for the type. +// Return Value: the type handle for the type. +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::BasicTypeInfoToTypeHandle(DebuggerIPCE_BasicTypeData * pArgTypeData) +{ + LOG((LF_CORDB, LL_INFO10000, + "D::BTITTH: expanding basic right-side type to left-side type, ELEMENT_TYPE: %d.\n", + pArgTypeData->elementType)); + TypeHandle typeHandle = TypeHandle(); + + switch (pArgTypeData->elementType) + { + case ELEMENT_TYPE_ARRAY: + case ELEMENT_TYPE_SZARRAY: + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_BYREF: + case ELEMENT_TYPE_FNPTR: + _ASSERTE(!pArgTypeData->vmTypeHandle.IsNull()); + typeHandle = TypeHandle::FromPtr(pArgTypeData->vmTypeHandle.GetDacPtr()); + break; + + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_VALUETYPE: + typeHandle = GetClassOrValueTypeHandle(pArgTypeData); + break; + + default: + typeHandle = FindLoadedElementType(pArgTypeData->elementType); + break; + } + if (typeHandle.IsNull()) + { + ThrowHR(CORDBG_E_CLASS_NOT_LOADED); + } + return typeHandle; +} // DacDbiInterfaceImpl::BasicTypeInfoToTypeHandle + + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::ExpandedTypeInfoToTypeHandle +// Convert type information for a top-level type to an exact type handle. This +// information includes information about the element type if the top-level type is +// an array type, the referent if the top-level type is a pointer type, or actual +// parameters if the top-level type is a generic class or value type. +// Arguments: +// input: pTopLevelTypeData - type information for the top-level type +// pArgInfo - contains the following information: +// m_genericArtsCount - number of parameters +// m_pGenericArgs - list of actual parameters +// Return Value: the exact type handle corresponding to the type represented by +// pTopLevelTypeData +//----------------------------------------------------------------------------- +TypeHandle DacDbiInterfaceImpl::ExpandedTypeInfoToTypeHandle(DebuggerIPCE_ExpandedTypeData * pTopLevelTypeData, + ArgInfoList * pArgInfo) +{ + WRAPPER_NO_CONTRACT; + + LOG((LF_CORDB, LL_INFO10000, + "D::ETITTH: expanding right-side type to left-side type, ELEMENT_TYPE: %d.\n", + pData->elementType)); + + TypeHandle typeHandle = TypeHandle(); + // depending on the top-level type, get the type handle incorporating information about any type arguments + switch (pTopLevelTypeData->elementType) + { + case ELEMENT_TYPE_ARRAY: + case ELEMENT_TYPE_SZARRAY: + typeHandle = GetExactArrayTypeHandle(pTopLevelTypeData, pArgInfo); + break; + + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_BYREF: + typeHandle = GetExactPtrOrByRefTypeHandle(pTopLevelTypeData, pArgInfo); + break; + + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_VALUETYPE: + typeHandle = GetExactClassTypeHandle(pTopLevelTypeData, pArgInfo); + break; + case ELEMENT_TYPE_FNPTR: + typeHandle = GetExactFnPtrTypeHandle(pArgInfo); + break; + default: + typeHandle = FindLoadedElementType(pTopLevelTypeData->elementType); + break; + } // end switch (pData->elementType) + + if (typeHandle.IsNull()) + { + // This may fail because there are cases when a type can be used (and so visible to the + // debugger), but not yet loaded to the point of being available in the EETypeHashTable. + // For example, generic value types (without explicit constructors) may not need their + // exact instantiation type to be loaded in order to be used as a field of an object + // created on the heap + LOG((LF_CORDB, LL_INFO10000, "D::ETITTH: type isn't loaded.\n")); + ThrowHR(CORDBG_E_CLASS_NOT_LOADED); + } + return typeHandle; +} // DacDbiInterfaceImpl::ExpandedTypeInfoToTypeHandle + +// ---------------------------------------------------------------------------- +// DacDbi API: GetThreadOrContextStaticAddress +// Get the target field address of a context or thread local static. +// +// Notes: +// The address is constant and could be cached. +// +// If this is a context-static, the function uses the thread's current context. +// [This is important because it means that you can't lookup a context static +// unless you have a thread in that context. If anybody actually cared about contexts, +// we might have to revise this in the future] +// +// This can commonly fail, in which case, it will return NULL. +// ---------------------------------------------------------------------------- +CORDB_ADDRESS DacDbiInterfaceImpl::GetThreadOrContextStaticAddress(VMPTR_FieldDesc vmField, + VMPTR_Thread vmRuntimeThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pRuntimeThread = vmRuntimeThread.GetDacPtr(); + PTR_FieldDesc pFieldDesc = vmField.GetDacPtr(); + TADDR fieldAddress = NULL; + + _ASSERTE(pRuntimeThread != NULL); + + // Find out whether the field is thread local or context local and get its + // address. + if (pFieldDesc->IsThreadStatic()) + { + fieldAddress = pRuntimeThread->GetStaticFieldAddrNoCreate(pFieldDesc, NULL); + } +#ifdef FEATURE_REMOTING + else if (pFieldDesc->IsContextStatic()) + { + fieldAddress = PTR_TO_TADDR(pRuntimeThread->GetContext()->GetStaticFieldAddrNoCreate(pFieldDesc)); + } +#endif + else + { + // In case we have more special cases added later, this will allow us to notice the need to + // update this function. + ThrowHR(E_NOTIMPL); + } + return fieldAddress; + +} // DacDbiInterfaceImpl::GetThreadOrContextStaticAddress + + // Get the target field address of a collectible types static. +CORDB_ADDRESS DacDbiInterfaceImpl::GetCollectibleTypeStaticAddress(VMPTR_FieldDesc vmField, + VMPTR_AppDomain vmAppDomain) +{ + DD_ENTER_MAY_THROW; + + AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); + PTR_FieldDesc pFieldDesc = vmField.GetDacPtr(); + _ASSERTE(pAppDomain != NULL); + + // + // Verify this field is of the right type + // + if(!pFieldDesc->IsStatic() || + pFieldDesc->IsSpecialStatic()) + { + _ASSERTE(!"BUG: Unsupported static field type for collectible types"); + } + + // + // Check that the data is available + // + /* TODO: Ideally we should be checking if the class is allocated first, however + we don't appear to be doing this even for non-collectible statics and + we have never seen an issue. + */ + + // + // Get the address + // + PTR_VOID base = pFieldDesc->GetBaseInDomain(pAppDomain); + if (base == PTR_NULL) + { + return PTR_HOST_TO_TADDR(NULL); + } + + // + // Store the result and return + // + PTR_VOID addr = pFieldDesc->GetStaticAddressHandle(base); + return PTR_TO_TADDR(addr); + +} // DacDbiInterfaceImpl::GetCollectibleTypeStaticAddress + +// DacDbi API: GetTypeHandleParams +// - gets the necessary data for a type handle, i.e. its type parameters, e.g. "String" and "List<int>" from the type handle +// for "Dict<String,List<int>>", and sends it back to the right side. +// - pParams is allocated and initialized by this function +// - This should not fail except for OOM +void DacDbiInterfaceImpl::GetTypeHandleParams(VMPTR_AppDomain vmAppDomain, + VMPTR_TypeHandle vmTypeHandle, + TypeParamsList * pParams) +{ + DD_ENTER_MAY_THROW + + TypeHandle typeHandle = TypeHandle::FromPtr(vmTypeHandle.GetDacPtr()); + LOG((LF_CORDB, LL_INFO10000, "D::GTHP: getting type parameters for 0x%08x 0x%0x8.\n", + vmAppDomain.GetDacPtr(), typeHandle.AsPtr())); + + + // Find the class given its type handle. + _ASSERTE(pParams->IsEmpty()); + pParams->Alloc(typeHandle.GetNumGenericArgs()); + + // collect type information for each type parameter + for (int i = 0; i < pParams->Count(); ++i) + { + VMPTR_TypeHandle thInst = VMPTR_TypeHandle::NullPtr(); + thInst.SetDacTargetPtr(typeHandle.GetInstantiation()[i].AsTAddr()); + + TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, + vmAppDomain, + thInst, + &((*pParams)[i])); + } + + LOG((LF_CORDB, LL_INFO10000, "D::GTHP: sending result")); +} // DacDbiInterfaceImpl::GetTypeHandleParams + +//----------------------------------------------------------------------------- +// DacDbi API: GetSimpleType +// gets the metadata token and domain file corresponding to a simple type +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::GetSimpleType(VMPTR_AppDomain vmAppDomain, + CorElementType simpleType, + mdTypeDef *pMetadataToken, + VMPTR_Module *pVmModule, + VMPTR_DomainFile *pVmDomainFile) +{ + DD_ENTER_MAY_THROW; + + AppDomain *pAppDomain = vmAppDomain.GetDacPtr(); + + // if we fail to get either a valid type handle or module, we will want to send back + // a NULL domain file too, so we'll to preinitialize this here. + _ASSERTE(pVmDomainFile != NULL); + *pVmDomainFile = VMPTR_DomainFile::NullPtr(); + // FindLoadedElementType will return NULL if the type hasn't been loaded yet. + TypeHandle typeHandle = FindLoadedElementType(simpleType); + + if (typeHandle.IsNull()) + { + ThrowHR(CORDBG_E_CLASS_NOT_LOADED); + } + else + { + _ASSERTE(pMetadataToken != NULL); + *pMetadataToken = typeHandle.GetCl(); + + Module * pModule = typeHandle.GetModule(); + if (pModule == NULL) + ThrowHR(CORDBG_E_TARGET_INCONSISTENT); + + pVmModule->SetHostPtr(pModule); + + if (pAppDomain) + { + pVmDomainFile->SetHostPtr(pModule->GetDomainFile(pAppDomain)); + if (pVmDomainFile->IsNull()) + ThrowHR(CORDBG_E_TARGET_INCONSISTENT); + } + } + + LOG((LF_CORDB, LL_INFO10000, "D::STI: sending result.\n")); +} // DacDbiInterfaceImpl::GetSimpleType + +BOOL DacDbiInterfaceImpl::IsExceptionObject(VMPTR_Object vmObject) +{ + DD_ENTER_MAY_THROW; + + Object* objPtr = vmObject.GetDacPtr(); + MethodTable* pMT = objPtr->GetMethodTable(); + + return IsExceptionObject(pMT); +} + +BOOL DacDbiInterfaceImpl::IsExceptionObject(MethodTable* pMT) +{ + PTR_MethodTable pExMT = g_pExceptionClass; + + TADDR targetMT = dac_cast<TADDR>(pMT); + TADDR exceptionMT = dac_cast<TADDR>(pExMT); + + do + { + if (targetMT == exceptionMT) + return TRUE; + + pMT = pMT->GetParentMethodTable(); + targetMT = dac_cast<TADDR>(pMT); + } while (pMT); + + return FALSE; +} + +void DacDbiInterfaceImpl::GetStackFramesFromException(VMPTR_Object vmObject, DacDbiArrayList<DacExceptionCallStackData>& dacStackFrames) +{ + DD_ENTER_MAY_THROW; + + PTR_Object objPtr = vmObject.GetDacPtr(); + +#ifdef _DEBUG + // ensure we have an Exception object + MethodTable* pMT = objPtr->GetMethodTable(); + _ASSERTE(IsExceptionObject(pMT)); +#endif + + OBJECTREF objRef = ObjectToOBJECTREF(objPtr); + + DebugStackTrace::GetStackFramesData stackFramesData; + + stackFramesData.pDomain = NULL; + stackFramesData.skip = 0; + stackFramesData.NumFramesRequested = 0; + + DebugStackTrace::GetStackFramesFromException(&objRef, &stackFramesData); + + INT32 dacStackFramesLength = stackFramesData.cElements; + + if (dacStackFramesLength > 0) + { + dacStackFrames.Alloc(dacStackFramesLength); + + for (INT32 index = 0; index < dacStackFramesLength; ++index) + { + DebugStackTrace::DebugStackTraceElement const& currentElement = stackFramesData.pElements[index]; + DacExceptionCallStackData& currentFrame = dacStackFrames[index]; + + Module* pModule = currentElement.pFunc->GetModule(); + BaseDomain* pBaseDomain = currentElement.pFunc->GetAssembly()->GetDomain(); + + AppDomain* pDomain = NULL; + DomainFile* pDomainFile = NULL; + + if (pBaseDomain->IsSharedDomain()) + pDomain = SystemDomain::System()->DefaultDomain(); + else + pDomain = pBaseDomain->AsAppDomain(); + + _ASSERTE(pDomain != NULL); + + pDomainFile = pModule->FindDomainFile(pDomain); + _ASSERTE(pDomainFile != NULL); + + currentFrame.vmAppDomain.SetHostPtr(pDomain); + currentFrame.vmDomainFile.SetHostPtr(pDomainFile); + currentFrame.ip = currentElement.ip; + currentFrame.methodDef = currentElement.pFunc->GetMemberDef(); +#if defined(FEATURE_EXCEPTIONDISPATCHINFO) + currentFrame.isLastForeignExceptionFrame = currentElement.fIsLastFrameFromForeignStackTrace; +#else + // for CLRs lacking exception dispatch info just set it to 0 + currentFrame.isLastForeignExceptionFrame = 0; +#endif + } + } +} + +#ifdef FEATURE_COMINTEROP + +PTR_RCW GetRcwFromVmptrObject(VMPTR_Object vmObject) +{ + PTR_RCW pRCW = NULL; + + Object* objPtr = vmObject.GetDacPtr(); + + PTR_SyncBlock pSyncBlock = NULL; + pSyncBlock = objPtr->PassiveGetSyncBlock(); + if (pSyncBlock == NULL) + return pRCW; + + PTR_InteropSyncBlockInfo pInfo = NULL; + pInfo = pSyncBlock->GetInteropInfoNoCreate(); + if (pInfo == NULL) + return pRCW; + + pRCW = dac_cast<PTR_RCW>(pInfo->DacGetRawRCW()); + + return pRCW; +} + +#endif + +BOOL DacDbiInterfaceImpl::IsRcw(VMPTR_Object vmObject) +{ +#ifdef FEATURE_COMINTEROP + DD_ENTER_MAY_THROW; + return GetRcwFromVmptrObject(vmObject) != NULL; +#else + return FALSE; +#endif // FEATURE_COMINTEROP + +} + +void DacDbiInterfaceImpl::GetRcwCachedInterfaceTypes( + VMPTR_Object vmObject, + VMPTR_AppDomain vmAppDomain, + BOOL bIInspectableOnly, + DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pDacInterfaces) +{ +#ifdef FEATURE_COMINTEROP + + DD_ENTER_MAY_THROW; + + Object* objPtr = vmObject.GetDacPtr(); + + InlineSArray<PTR_MethodTable, INTERFACE_ENTRY_CACHE_SIZE> rgMT; + + PTR_RCW pRCW = GetRcwFromVmptrObject(vmObject); + if (pRCW != NULL) + { + pRCW->GetCachedInterfaceTypes(bIInspectableOnly, &rgMT); + + pDacInterfaces->Alloc(rgMT.GetCount()); + + for (COUNT_T i = 0; i < rgMT.GetCount(); ++i) + { + // There is the possiblity that we'll get this far with a dump and not fail, but still + // not be able to get full info for a particular param. + EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER + { + // Fill in the struct using the current TypeHandle + VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); + TypeHandle th = TypeHandle::FromTAddr(dac_cast<TADDR>(rgMT[i])); + vmTypeHandle.SetDacTargetPtr(th.AsTAddr()); + TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, + vmAppDomain, + vmTypeHandle, + &((*pDacInterfaces)[i])); + } + EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER + { + // On failure for a particular type, default it to NULL. + (*pDacInterfaces)[i].elementType = ELEMENT_TYPE_END; + } + EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER + + } + + } + else +#endif // FEATURE_COMINTEROP + { + pDacInterfaces->Alloc(0); + } +} + +void DacDbiInterfaceImpl::GetRcwCachedInterfacePointers( + VMPTR_Object vmObject, + BOOL bIInspectableOnly, + DacDbiArrayList<CORDB_ADDRESS> * pDacItfPtrs) +{ +#ifdef FEATURE_COMINTEROP + + DD_ENTER_MAY_THROW; + + Object* objPtr = vmObject.GetDacPtr(); + + InlineSArray<TADDR, INTERFACE_ENTRY_CACHE_SIZE> rgUnks; + + PTR_RCW pRCW = GetRcwFromVmptrObject(vmObject); + if (pRCW != NULL) + { + pRCW->GetCachedInterfacePointers(bIInspectableOnly, &rgUnks); + + pDacItfPtrs->Alloc(rgUnks.GetCount()); + + for (COUNT_T i = 0; i < rgUnks.GetCount(); ++i) + { + (*pDacItfPtrs)[i] = (CORDB_ADDRESS)(rgUnks[i]); + } + + } + else +#endif // FEATURE_COMINTEROP + { + pDacItfPtrs->Alloc(0); + } +} + +void DacDbiInterfaceImpl::GetCachedWinRTTypesForIIDs( + VMPTR_AppDomain vmAppDomain, + DacDbiArrayList<GUID> & iids, + OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pTypes) +{ +#ifdef FEATURE_COMINTEROP + + DD_ENTER_MAY_THROW; + + AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); + if (pAppDomain->IsUnloading()) + { + return; + } + + { + pTypes->Alloc(iids.Count()); + + for (int i = 0; i < iids.Count(); ++i) + { + // There is the possiblity that we'll get this far with a dump and not fail, but still + // not be able to get full info for a particular param. + EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER + { + PTR_MethodTable pMT = pAppDomain->LookupTypeByGuid(iids[i]); + + // Fill in the struct using the current TypeHandle + VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); + TypeHandle th = TypeHandle::FromTAddr(dac_cast<TADDR>(pMT)); + vmTypeHandle.SetDacTargetPtr(th.AsTAddr()); + TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, + vmAppDomain, + vmTypeHandle, + &((*pTypes)[i])); + } + EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER + { + // On failure for a particular type, default it to NULL. + (*pTypes)[i].elementType = ELEMENT_TYPE_END; + } + EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER + } + } +#else // FEATURE_COMINTEROP + { + pTypes->Alloc(0); + } +#endif // FEATURE_COMINTEROP +} + +void DacDbiInterfaceImpl::GetCachedWinRTTypes( + VMPTR_AppDomain vmAppDomain, + OUT DacDbiArrayList<GUID> * pGuids, + OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pTypes) +{ +#ifdef FEATURE_COMINTEROP + + DD_ENTER_MAY_THROW; + + AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); + if (pAppDomain->IsUnloading()) + { + return; + } + + InlineSArray<PTR_MethodTable, 32> rgMT; + InlineSArray<GUID, 32> rgGuid; + + { + pAppDomain->GetCachedWinRTTypes(&rgMT, &rgGuid, 0, NULL); + + pTypes->Alloc(rgMT.GetCount()); + pGuids->Alloc(rgGuid.GetCount()); + + for (COUNT_T i = 0; i < rgMT.GetCount(); ++i) + { + // There is the possiblity that we'll get this far with a dump and not fail, but still + // not be able to get full info for a particular param. + EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER + { + // Fill in the struct using the current TypeHandle + VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); + TypeHandle th = TypeHandle::FromTAddr(dac_cast<TADDR>(rgMT[i])); + vmTypeHandle.SetDacTargetPtr(th.AsTAddr()); + TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, + vmAppDomain, + vmTypeHandle, + &((*pTypes)[i])); + } + EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER + { + // On failure for a particular type, default it to NULL. + (*pTypes)[i].elementType = ELEMENT_TYPE_END; + } + EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER + (*pGuids)[i] = rgGuid[i]; + + } + + } +#else // FEATURE_COMINTEROP + { + pTypes->Alloc(0); + } +#endif // FEATURE_COMINTEROP +} + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::FindField +// Finds information for a particular class field +// Arguments: +// input: thApprox - type handle for the type to which the field belongs +// fldToken - metadata token for the field +// Return Value: FieldDesc containing information for the field if found or NULL otherwise +//----------------------------------------------------------------------------- +PTR_FieldDesc DacDbiInterfaceImpl::FindField(TypeHandle thApprox, mdFieldDef fldToken) +{ + EncApproxFieldDescIterator fdIterator(thApprox.GetMethodTable(), + ApproxFieldDescIterator::ALL_FIELDS, + FALSE); // don't fixup EnC (we can't, we're stopped) + + PTR_FieldDesc pCurrentFD; + + while ((pCurrentFD = fdIterator.Next()) != NULL) + { + // We're looking for a specific fieldDesc, see if we got it. + if (pCurrentFD->GetMemberDef() == fldToken) + { + return pCurrentFD; + } + } + + // we never found it... + return NULL; +} // DacDbiInterfaceImpl::FindField + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetEnCFieldDesc +// Get the FieldDesc corresponding to a particular EnC field token +// Arguments: +// input: pEnCFieldInfo +// Return Value: pointer to the FieldDesc that corresponds to the EnC field +// Note: this function may throw +//----------------------------------------------------------------------------- +FieldDesc * DacDbiInterfaceImpl::GetEnCFieldDesc(const EnCHangingFieldInfo * pEnCFieldInfo) +{ + FieldDesc * pFD = NULL; + + DomainFile * pDomainFile = pEnCFieldInfo->GetObjectTypeData().vmDomainFile.GetDacPtr(); + Module * pModule = pDomainFile->GetModule(); + + // get the type handle for the object + TypeHandle typeHandle = ClassLoader::LookupTypeDefOrRefInModule(pModule, + pEnCFieldInfo->GetObjectTypeData().metadataToken); + if (typeHandle == NULL) + { + ThrowHR(CORDBG_E_CLASS_NOT_LOADED); + } + // and find the field desc + pFD = FindField(typeHandle, pEnCFieldInfo->GetFieldToken()); + if (pFD == NULL) + { + // FieldDesc is not yet available, so can't get EnC field info + ThrowHR(CORDBG_E_ENC_HANGING_FIELD); + } + return pFD; + +} // DacDbiInterfaceImpl::GetEnCFieldDesc + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::GetPtrToEnCField +// Get the address of a field added with EnC. +// Arguments: +// input: pFD - field desc for the added field +// pEnCFieldInfo - information about the new field +// Return Value: The field address if the field is available (i.e., it has been accessed) +// or NULL otherwise +// Note: this function may throw +//----------------------------------------------------------------------------- +PTR_CBYTE DacDbiInterfaceImpl::GetPtrToEnCField(FieldDesc * pFD, const EnCHangingFieldInfo * pEnCFieldInfo) +{ +#ifndef EnC_SUPPORTED + _ASSERTE(!"Trying to get the address of an EnC field where EnC is not supported! "); + return NULL; +#else + + PTR_EditAndContinueModule pEnCModule; + DomainFile * pDomainFile = pEnCFieldInfo->GetObjectTypeData().vmDomainFile.GetDacPtr(); + Module * pModule = pDomainFile->GetModule(); + + // make sure we actually have an EditAndContinueModule + _ASSERTE(pModule->IsEditAndContinueCapable()); + pEnCModule = dac_cast<PTR_EditAndContinueModule>(pModule); + + // we should also have an EnCFieldDesc + _ASSERTE(pFD->IsEnCNew()); + EnCFieldDesc * pEnCFieldDesc; + pEnCFieldDesc = dac_cast<PTR_EnCFieldDesc>(pFD); + + // If it hasn't been fixed up yet, then we can't return the pointer. + if (pEnCFieldDesc->NeedsFixup()) + { + ThrowHR(CORDBG_E_ENC_HANGING_FIELD); + } + // Get a pointer to the field + PTR_CBYTE pORField = NULL; + + PTR_Object pObject = pEnCFieldInfo->GetVmObject().GetDacPtr(); + pORField = pEnCModule->ResolveField(ObjectToOBJECTREF(pObject), + pEnCFieldDesc); + + // The field could be absent because the code hasn't accessed it yet. If so, we're not going to add it + // since we can't allocate anyway. + if (pORField == NULL) + { + ThrowHR(CORDBG_E_ENC_HANGING_FIELD); + } + return pORField; +#endif // EnC_SUPPORTED +} // DacDbiInterfaceImpl::GetPtrToEnCField + +//----------------------------------------------------------------------------- +// DacDbiInterfaceImpl::InitFieldData +// Initialize information about a field added with EnC +// Arguments : +// input: +// pFD - provides information about whether the field is static, +// the metadata token, etc. +// pORField - provides the field address or offset +// pEnCFieldData - provides the offset to the fields of the object +// output: pFieldData - initialized in accordance with the input information +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::InitFieldData(const FieldDesc * pFD, + const PTR_CBYTE pORField, + const EnCHangingFieldInfo * pEnCFieldData, + FieldData * pFieldData) +{ + + pFieldData->ClearFields(); + + pFieldData->m_fFldIsStatic = (pFD->IsStatic() != 0); + pFieldData->m_vmFieldDesc.SetHostPtr(pFD); + pFieldData->m_fFldIsTLS = (pFD->IsThreadStatic() == TRUE); + pFieldData->m_fldMetadataToken = pFD->GetMemberDef(); + pFieldData->m_fFldIsRVA = (pFD->IsRVA() == TRUE); + pFieldData->m_fFldIsContextStatic = (pFD->IsContextStatic() == TRUE); + pFieldData->m_fFldIsCollectibleStatic = FALSE; + pFieldData->m_fFldStorageAvailable = true; + + if (pFieldData->m_fFldIsStatic) + { + //EnC is only supported on regular static fields + _ASSERTE(!pFieldData->m_fFldIsContextStatic); + _ASSERTE(!pFieldData->m_fFldIsTLS); + _ASSERTE(!pFieldData->m_fFldIsRVA); + + // pORField contains the absolute address + pFieldData->SetStaticAddress(PTR_TO_TADDR(pORField)); + } + else + { + // fldInstanceOffset is computed to work correctly with GetFieldValue + // which computes: + // addr of pORField = object + pEnCFieldInfo->m_offsetToVars + offsetToFld + pFieldData->SetInstanceOffset(PTR_TO_TADDR(pORField) - + (PTR_TO_TADDR(pEnCFieldData->GetVmObject().GetDacPtr()) + + pEnCFieldData->GetOffsetToVars())); + } +} // DacDbiInterfaceImpl::InitFieldData + + +// ---------------------------------------------------------------------------- +// DacDbi API: GetEnCHangingFieldInfo +// After a class has been loaded, if a field has been added via EnC we'll have to jump through +// some hoops to get at it (it hangs off the sync block or FieldDesc). +// +// GENERICS: TODO: this method will need to be modified if we ever support EnC on +// generic classes. +//----------------------------------------------------------------------------- +void DacDbiInterfaceImpl::GetEnCHangingFieldInfo(const EnCHangingFieldInfo * pEnCFieldInfo, + FieldData * pFieldData, + BOOL * pfStatic) +{ + DD_ENTER_MAY_THROW; + + LOG((LF_CORDB, LL_INFO100000, "DDI::IEnCHFI: Obj:0x%x, objType" + ":0x%x, offset:0x%x\n", pEnCFieldInfo->m_pObject, pEnCFieldInfo->m_objectTypeData.elementType, + pEnCFieldInfo->m_offsetToVars)); + + FieldDesc * pFD = NULL; + PTR_CBYTE pORField = NULL; + + pFD = GetEnCFieldDesc(pEnCFieldInfo); + _ASSERTE(pFD->IsEnCNew()); // We shouldn't be here if it wasn't added to an + // already loaded class. + +#ifdef EnC_SUPPORTED + pORField = GetPtrToEnCField(pFD, pEnCFieldInfo); +#else + _ASSERTE(!"We shouldn't be here: EnC not supported"); +#endif // EnC_SUPPORTED + + InitFieldData(pFD, pORField, pEnCFieldInfo, pFieldData); + *pfStatic = (pFD->IsStatic() != 0); + +} // DacDbiInterfaceImpl::GetEnCHangingFieldInfo + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +void DacDbiInterfaceImpl::GetAssemblyFromDomainAssembly(VMPTR_DomainAssembly vmDomainAssembly, VMPTR_Assembly *vmAssembly) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(vmAssembly != NULL); + + DomainAssembly * pDomainAssembly = vmDomainAssembly.GetDacPtr(); + vmAssembly->SetHostPtr(pDomainAssembly->GetAssembly()); +} + +// Determines whether the runtime security system has assigned full-trust to this assembly. +BOOL DacDbiInterfaceImpl::IsAssemblyFullyTrusted(VMPTR_DomainAssembly vmDomainAssembly) +{ + DD_ENTER_MAY_THROW; + + DomainAssembly * pAssembly = vmDomainAssembly.GetDacPtr(); + IAssemblySecurityDescriptor * pSecDisc = pAssembly->GetSecurityDescriptor(); + return pSecDisc->IsFullyTrusted(); +} + +// Get the full path and file name to the assembly's manifest module. +BOOL DacDbiInterfaceImpl::GetAssemblyPath( + VMPTR_Assembly vmAssembly, + IStringHolder * pStrFilename) +{ + DD_ENTER_MAY_THROW; + + // Get the manifest module for this assembly + Assembly * pAssembly = vmAssembly.GetDacPtr(); + Module * pManifestModule = pAssembly->GetManifestModule(); + + // Get the path for the manifest module. + // since we no longer support Win9x, we assume all paths will be in unicode format already + const WCHAR * szPath = pManifestModule->GetPath().DacGetRawUnicode(); + HRESULT hrStatus = pStrFilename->AssignCopy(szPath); + IfFailThrow(hrStatus); + + if(szPath == NULL || *szPath=='\0') + { + // The asembly has no (and will never have a) file name, but we didn't really fail + return FALSE; + } + + return TRUE; +} + +// DAC/DBI API +// Get a resolved type def from a type ref. The type ref may come from a module other than the +// referencing module. +void DacDbiInterfaceImpl::ResolveTypeReference(const TypeRefData * pTypeRefInfo, + TypeRefData * pTargetRefInfo) +{ + DD_ENTER_MAY_THROW; + DomainFile * pDomainFile = pTypeRefInfo->vmDomainFile.GetDacPtr(); + Module * pReferencingModule = pDomainFile->GetCurrentModule(); + BOOL fSuccess = FALSE; + + // Resolve the type ref + // g_pEEInterface->FindLoadedClass is almost what we want, but it isn't guaranteed to work if + // the typeRef was originally loaded from a different assembly. Also, we need to ensure that + // we can resolve even unloaded types in fully loaded assemblies, so APIs such as + // LoadTypeDefOrRefThrowing aren't acceptable. + + Module * pTargetModule = NULL; + mdTypeDef targetTypeDef = mdTokenNil; + + // The loader won't need to trigger a GC or throw because we've told it not to load anything + ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); + + fSuccess = ClassLoader::ResolveTokenToTypeDefThrowing(pReferencingModule, + pTypeRefInfo->typeToken, + &pTargetModule, + &targetTypeDef, + Loader::SafeLookup //don't load, no locks/allocations + ); + if (fSuccess) + { + _ASSERTE(pTargetModule != NULL); + _ASSERTE( TypeFromToken(targetTypeDef) == mdtTypeDef ); + + AppDomain * pAppDomain = pDomainFile->GetAppDomain(); + + pTargetRefInfo->vmDomainFile.SetDacTargetPtr(PTR_HOST_TO_TADDR(pTargetModule->GetDomainFile(pAppDomain))); + pTargetRefInfo->typeToken = targetTypeDef; + } + else + { + // failed - presumably because the target assembly isn't loaded + ThrowHR(CORDBG_E_CLASS_NOT_LOADED); + } +} // DacDbiInterfaceImpl::ResolveTypeReference + + +// Get the full path and file name to the module (if any). +BOOL DacDbiInterfaceImpl::GetModulePath(VMPTR_Module vmModule, + IStringHolder * pStrFilename) +{ + DD_ENTER_MAY_THROW; + + Module * pModule = vmModule.GetDacPtr(); + PEFile * pFile = pModule->GetFile(); + if (pFile != NULL) + { + if( !pFile->GetPath().IsEmpty() ) + { + // Module has an on-disk path + const WCHAR * szPath = pFile->GetPath().DacGetRawUnicode(); + if (szPath == NULL) + { + szPath = pFile->GetModuleFileNameHint().DacGetRawUnicode(); + if (szPath == NULL) + { + goto NoFileName; + } + } + IfFailThrow(pStrFilename->AssignCopy(szPath)); + return TRUE; + } + } + +NoFileName: + // no filename + IfFailThrow(pStrFilename->AssignCopy(W(""))); + return FALSE; +} + +// Get the full path and file name to the ngen image for the module (if any). +BOOL DacDbiInterfaceImpl::GetModuleNGenPath(VMPTR_Module vmModule, + IStringHolder * pStrFilename) +{ + DD_ENTER_MAY_THROW; +#ifdef FEATURE_PREJIT + Module * pModule = vmModule.GetDacPtr(); + PEFile * pFile = pModule->GetFile(); + if (pFile != NULL && pFile->HasNativeImage()) + { + PEImage * pImage = pFile->GetPersistentNativeImage(); + if (pImage != NULL && pImage->IsFile()) + { + // We have an on-disk ngen image. Return the path. + // since we no longer support Win9x, we assume all paths will be in unicode format already + const WCHAR * szPath = pImage->GetPath().DacGetRawUnicode(); + if (szPath == NULL) + { + szPath = pFile->GetModuleFileNameHint().DacGetRawUnicode(); + if (szPath == NULL) + { + goto NoFileName; + } + } + IfFailThrow(pStrFilename->AssignCopy(szPath)); + return TRUE; + } + } +#endif // FEATURE_PREJIT + +NoFileName: + // no ngen filename + IfFailThrow(pStrFilename->AssignCopy(W(""))); + return FALSE; +} + +// Implementation of IDacDbiInterface::GetModuleSimpleName +void DacDbiInterfaceImpl::GetModuleSimpleName(VMPTR_Module vmModule, IStringHolder * pStrFilename) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(pStrFilename != NULL); + + Module * pModule = vmModule.GetDacPtr(); + LPCUTF8 szNameUtf8 = pModule->GetSimpleName(); + + SString convert(SString::Utf8, szNameUtf8); + IfFailThrow(pStrFilename->AssignCopy(convert.GetUnicode())); +} + +// Helper to intialize a TargetBuffer from a MemoryRange +// +// Arguments: +// memoryRange - memory range. +// pTargetBuffer - required out parameter to be initialized to value of memory range. +// +// Notes: +// MemoryRange and TargetBuffer both conceptually describe a single contiguous buffer of memory in the +// target. MemoryRange is a VM structure, which can't bleed across the DacDbi boundary. TargetBuffer is +// a DacDbi structure, which can cross the DacDbi boundary. +void InitTargetBufferFromMemoryRange(const MemoryRange memoryRange, TargetBuffer * pTargetBuffer) +{ + SUPPORTS_DAC; + + _ASSERTE(pTargetBuffer != NULL); + PTR_CVOID p = memoryRange.StartAddress(); + CORDB_ADDRESS addr = PTR_TO_CORDB_ADDRESS(PTR_TO_TADDR(p)); + + _ASSERTE(memoryRange.Size() <= 0xffffffff); + pTargetBuffer->Init(addr, (ULONG)memoryRange.Size()); +} + +// Helper to intialize a TargetBuffer (host representation of target) from an SBuffer (target) +// +// Arguments: +// pBuffer - target pointer to a SBuffer structure. If pBuffer is NULL, then target buffer will be empty. +// pTargetBuffer - required out pointer to hold buffer description. +// +// Notes: +// PTR_SBuffer and TargetBuffer are both semantically equivalent structures. They both are a pointer and length +// describing a buffer in the target address space. (SBufer also has ownership semantics, but for DAC's +// read-only nature, that doesn't matter). +// Neither of these will actually copy the target buffer into the host without explicit action. +// The important difference is that TargetBuffer is a host datastructure and so easier to manipulate. +// +void InitTargetBufferFromTargetSBuffer(PTR_SBuffer pBuffer, TargetBuffer * pTargetBuffer) +{ + SUPPORTS_DAC; + + _ASSERTE(pTargetBuffer != NULL); + + SBuffer * pBufferHost = pBuffer; + if (pBufferHost == NULL) + { + pTargetBuffer->Clear(); + return; + } + + MemoryRange m = pBufferHost->DacGetRawBuffer(); + InitTargetBufferFromMemoryRange(m, pTargetBuffer); +} + + +// Implementation of IDacDbiInterface::GetMetadata +void DacDbiInterfaceImpl::GetMetadata(VMPTR_Module vmModule, TargetBuffer * pTargetBuffer) +{ + DD_ENTER_MAY_THROW; + + pTargetBuffer->Clear(); + + Module * pModule = vmModule.GetDacPtr(); + + // Target should only be asking about modules that are visible to debugger. + _ASSERTE(pModule->IsVisibleToDebugger()); + + // For dynamic modules, metadata is stored as an eagerly-serialized buffer hanging off the Reflection Module. + if (pModule->IsReflection()) + { + // Here is the fetch. + ReflectionModule * pReflectionModule = pModule->GetReflectionModule(); + InitTargetBufferFromTargetSBuffer(pReflectionModule->GetDynamicMetadataBuffer(), pTargetBuffer); + } + else + { + PEFile * pFile = pModule->GetFile(); + + // For non-dynamic modules, metadata is in the pe-image. + COUNT_T size; + CORDB_ADDRESS address = PTR_TO_CORDB_ADDRESS(dac_cast<TADDR>(pFile->GetLoadedMetadata(&size))); + + pTargetBuffer->Init(address, (ULONG) size); + } + + if (pTargetBuffer->IsEmpty()) + { + // We never expect this to happen in a well-behaved scenario. But just in case. + ThrowHR(CORDBG_E_MISSING_METADATA); + } + +} + +// Implementation of IDacDbiInterface::GetSymbolsBuffer +void DacDbiInterfaceImpl::GetSymbolsBuffer(VMPTR_Module vmModule, TargetBuffer * pTargetBuffer, SymbolFormat * pSymbolFormat) +{ + DD_ENTER_MAY_THROW; + + pTargetBuffer->Clear(); + *pSymbolFormat = kSymbolFormatNone; + + Module * pModule = vmModule.GetDacPtr(); + + // Target should only be asking about modules that are visible to debugger. + _ASSERTE(pModule->IsVisibleToDebugger()); + + PTR_CGrowableStream pStream = pModule->GetInMemorySymbolStream(); + if (pStream == NULL) + { + // Common case is to not have PDBs in-memory. + return; + } + + const MemoryRange m = pStream->GetRawBuffer(); + if (m.Size() == 0) + { + // We may be prepared to store symbols (in some particular format) but none are there yet. + // We treat this the same as not having any symbols above. + return; + } + InitTargetBufferFromMemoryRange(m, pTargetBuffer); + + // Set the symbol format appropriately + ESymbolFormat symFormat = pModule->GetInMemorySymbolStreamFormat(); + switch (symFormat) + { + case eSymbolFormatPDB: + *pSymbolFormat = kSymbolFormatPDB; + break; + + case eSymbolFormatILDB: + *pSymbolFormat = kSymbolFormatILDB; + break; + + default: + CONSISTENCY_CHECK_MSGF(false, "Unexpected symbol format"); + pTargetBuffer->Clear(); + ThrowHR(E_UNEXPECTED); + } +} + + + +void DacDbiInterfaceImpl::GetModuleForDomainFile(VMPTR_DomainFile vmDomainFile, OUT VMPTR_Module * pModule) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(pModule != NULL); + + DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); + pModule->SetHostPtr(pDomainFile->GetModule()); +} + + +// Implement IDacDbiInterface::GetDomainFileData +void DacDbiInterfaceImpl::GetDomainFileData(VMPTR_DomainFile vmDomainFile, DomainFileInfo * pData) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(pData != NULL); + + ZeroMemory(pData, sizeof(*pData)); + + DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); + AppDomain * pAppDomain = pDomainFile->GetAppDomain(); + + // @dbgtodo - is this efficient DAC usage (perhaps a dac-cop rule)? Are we round-tripping the pointer? + // Should we have a GetDomainAssembly() that returns a PTR_DomainAssembly? + pData->vmDomainAssembly.SetHostPtr(pDomainFile->GetDomainAssembly()); + pData->vmAppDomain.SetHostPtr(pAppDomain); +} + +// Implement IDacDbiInterface::GetModuleData +void DacDbiInterfaceImpl::GetModuleData(VMPTR_Module vmModule, ModuleInfo * pData) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(pData != NULL); + + ZeroMemory(pData, sizeof(*pData)); + + Module * pModule = vmModule.GetDacPtr(); + PEFile * pFile = pModule->GetFile(); + + pData->vmPEFile.SetHostPtr(pFile); + pData->vmAssembly.SetHostPtr(pModule->GetAssembly()); + + // Is it dynamic? + BOOL fIsDynamic = pModule->IsReflection(); + pData->fIsDynamic = fIsDynamic; + + // Get PE BaseAddress and Size + // For dynamic modules, these are 0. Else, + pData->pPEBaseAddress = NULL; + pData->nPESize = 0; + + if (!fIsDynamic) + { + COUNT_T size = 0; + pData->pPEBaseAddress = PTR_TO_TADDR(pFile->GetDebuggerContents(&size)); + pData->nPESize = (ULONG) size; + } + + // In-memory is determined by whether the module has a filename. + pData->fInMemory = FALSE; + if (pFile != NULL) + { + pData->fInMemory = pFile->GetPath().IsEmpty(); + } +} + + +// Enumerate all AppDomains in the process. +void DacDbiInterfaceImpl::EnumerateAppDomains( + FP_APPDOMAIN_ENUMERATION_CALLBACK fpCallback, + void * pUserData) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(fpCallback != NULL); + + // Only include active appdomains in the enumeration. + // This includes appdomains sent before the AD load event, + // and does not include appdomains that are in shutdown after the AD exit event. + const BOOL bOnlyActive = TRUE; + AppDomainIterator iterator(bOnlyActive); + + while(iterator.Next()) + { + // It's critical that we don't yield appdomains after the unload event has been sent. + // See code:IDacDbiInterface#Enumeration for details. + AppDomain * pAppDomain = iterator.GetDomain(); + if (pAppDomain->IsUnloading()) + { + continue; + } + + VMPTR_AppDomain vmAppDomain = VMPTR_AppDomain::NullPtr(); + vmAppDomain.SetHostPtr(pAppDomain); + + fpCallback(vmAppDomain, pUserData); + } +} + +// Enumerate all Assemblies in an appdomain. +void DacDbiInterfaceImpl::EnumerateAssembliesInAppDomain( + VMPTR_AppDomain vmAppDomain, + FP_ASSEMBLY_ENUMERATION_CALLBACK fpCallback, + void * pUserData +) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(fpCallback != NULL); + + // Iterate through all Assemblies (including shared) in the appdomain. + AppDomain::AssemblyIterator iterator; + + // If the containing appdomain is unloading, then don't enumerate any assemblies + // in the domain. This is to enforce rules at code:IDacDbiInterface#Enumeration. + // See comment in code:DacDbiInterfaceImpl::EnumerateModulesInAssembly code for details. + AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); + if (pAppDomain->IsUnloading()) + { + return; + } + + // Pass the magical flags to the loader enumerator to get all Execution-only assemblies. + iterator = pAppDomain->IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoading | kIncludeLoaded | kIncludeExecution)); + CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; + + while (iterator.Next(pDomainAssembly.This())) + { + if (!pDomainAssembly->IsVisibleToDebugger()) + { + continue; + } + + VMPTR_DomainAssembly vmDomainAssembly = VMPTR_DomainAssembly::NullPtr(); + vmDomainAssembly.SetHostPtr(pDomainAssembly); + + fpCallback(vmDomainAssembly, pUserData); + } +} + +// Implementation of IDacDbiInterface::EnumerateModulesInAssembly, +// Enumerate all the modules (non-resource) in an assembly. +void DacDbiInterfaceImpl::EnumerateModulesInAssembly( + VMPTR_DomainAssembly vmAssembly, + FP_MODULE_ENUMERATION_CALLBACK fpCallback, + void * pUserData) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(fpCallback != NULL); + + DomainAssembly * pDomainAssembly = vmAssembly.GetDacPtr(); + + // If the appdomain or assembly containing this module is unloading, then don't enumerate any modules. + // in the domain. This is to enforce rules at code:IDacDbiInterface#Enumeration, specifically + // that new objects are not available after the unload event is sent. + // This is a very large hammer, but since modules only unload with appdomains or assemblies, we're + // erring on the side of safety. If the debugger happens to have VMPTR_DomainFiles (CordbModules) already + // cached, it can still use those until the unload event. + if (pDomainAssembly->IsUnloading()) + { + return; + } + + + // If the domain is not yet fully-loaded, don't advertise it yet. + // It's not ready to be inspected. + DomainModuleIterator iterator = pDomainAssembly->IterateModules(kModIterIncludeLoaded); + + while (iterator.Next()) + { + DomainFile * pDomainFile = iterator.GetDomainFile(); + + // Debugger isn't notified of Resource / Inspection-only modules. + if (!pDomainFile->GetModule()->IsVisibleToDebugger()) + { + continue; + } + + _ASSERTE(pDomainFile->IsLoaded()); + + VMPTR_DomainFile vmDomainFile = VMPTR_DomainFile::NullPtr(); + vmDomainFile.SetHostPtr(pDomainFile); + + fpCallback(vmDomainFile, pUserData); + } +} + +// Implementation of IDacDbiInterface::ResolveAssembly +// Returns NULL if not found. +VMPTR_DomainAssembly DacDbiInterfaceImpl::ResolveAssembly( + VMPTR_DomainFile vmScope, + mdToken tkAssemblyRef) +{ + DD_ENTER_MAY_THROW; + + + DomainFile * pDomainFile = vmScope.GetDacPtr(); + AppDomain * pAppDomain = pDomainFile->GetAppDomain(); + Module * pModule = pDomainFile->GetCurrentModule(); + + VMPTR_DomainAssembly vmDomainAssembly = VMPTR_DomainAssembly::NullPtr(); + + Assembly * pAssembly = pModule->LookupAssemblyRef(tkAssemblyRef); + if (pAssembly != NULL) + { + DomainAssembly * pDomainAssembly = pAssembly->FindDomainAssembly(pAppDomain); + vmDomainAssembly.SetHostPtr(pDomainAssembly); + } + return vmDomainAssembly; +} + +// When stopped at an event, request a synchronization. +// See DacDbiInterface.h for full comments +void DacDbiInterfaceImpl::RequestSyncAtEvent() +{ + DD_ENTER_MAY_THROW; + + // To request a sync, we just need to set g_pDebugger->m_RSRequestedSync high. + if (g_pDebugger != NULL) + { + TADDR addr = PTR_HOST_MEMBER_TADDR(Debugger, g_pDebugger, m_RSRequestedSync); + + BOOL fTrue = TRUE; + SafeWriteStructOrThrow<BOOL>(addr, &fTrue); + + } +} + +HRESULT DacDbiInterfaceImpl::SetSendExceptionsOutsideOfJMC(BOOL sendExceptionsOutsideOfJMC) +{ + DD_ENTER_MAY_THROW + + HRESULT hr = S_OK; + EX_TRY + { + if (g_pDebugger != NULL) + { + TADDR addr = PTR_HOST_MEMBER_TADDR(Debugger, g_pDebugger, m_sendExceptionsOutsideOfJMC); + SafeWriteStructOrThrow<BOOL>(addr, &sendExceptionsOutsideOfJMC); + } + } + EX_CATCH_HRESULT(hr); + return hr; +} + +// Notify the debuggee that a debugger attach is pending. +// See DacDbiInterface.h for full comments +void DacDbiInterfaceImpl::MarkDebuggerAttachPending() +{ + DD_ENTER_MAY_THROW; + + if (g_pDebugger != NULL) + { + DWORD flags = g_CORDebuggerControlFlags; + flags |= DBCF_PENDING_ATTACH; + + // Uses special DAC writing. PTR_TO_TADDR doesn't fetch for globals. + // @dbgtodo dac support - the exact mechanism of writing to the target needs to be flushed out, + // especially as it relates to DAC cop and enforcing undac-ized writes. + g_CORDebuggerControlFlags = flags; + } + else + { + // Caller should have gauranteed that the LS is loaded. + // If we're detaching, then don't throw because we don't care. + ThrowHR(CORDBG_E_NOTREADY); + } +} + + +// Notify the debuggee that a debugger is attached. +// See DacDbiInterface.h for full comments +void DacDbiInterfaceImpl::MarkDebuggerAttached(BOOL fAttached) +{ + DD_ENTER_MAY_THROW; + + if (g_pDebugger != NULL) + { + // To be attached, we need to set the following + // g_CORDebuggerControlFlags |= DBCF_ATTACHED; + // To detach (if !fAttached), we need to do the opposite. + + DWORD flags = g_CORDebuggerControlFlags; + if (fAttached) + { + flags |= DBCF_ATTACHED; + } + else + { + flags &= ~ (DBCF_ATTACHED | DBCF_PENDING_ATTACH); + } + + // Uses special DAC writing. PTR_TO_TADDR doesn't fetch for globals. + // @dbgtodo dac support - the exact mechanism of writing to the target needs to be flushed out, + // especially as it relates to DAC cop and enforcing undac-ized writes. + g_CORDebuggerControlFlags = flags; + } + else if (fAttached) + { + // Caller should have gauranteed that the LS is loaded. + // If we're detaching, then don't throw because we don't care. + ThrowHR(CORDBG_E_NOTREADY); + } + +} + +#ifdef FEATURE_INCLUDE_ALL_INTERFACES +// Enumerate all the Connections in the process. +void DacDbiInterfaceImpl::EnumerateConnections(FP_CONNECTION_CALLBACK fpCallback, void * pUserData) +{ + DD_ENTER_MAY_THROW; + + ConnectionNameHashEntry * pConnection; + + HASHFIND hashfind; + + pConnection = CCLRDebugManager::FindFirst(&hashfind); + while (pConnection) + { + DWORD id = pConnection->m_dwConnectionId; + LPCWSTR pName = pConnection->m_pwzName; + + fpCallback(id, pName, pUserData); + + // now get the next connection record + pConnection = CCLRDebugManager::FindNext(&hashfind); + } +} +#endif + + +// Enumerate all threads in the process. +void DacDbiInterfaceImpl::EnumerateThreads(FP_THREAD_ENUMERATION_CALLBACK fpCallback, void * pUserData) +{ + DD_ENTER_MAY_THROW; + + if (ThreadStore::s_pThreadStore == NULL) + { + return; + } + + Thread *pThread = ThreadStore::GetThreadList(NULL); + + while (pThread != NULL) + { + + // Don't want to publish threads via enumeration before they're ready to be inspected. + // Use the same window that we used in whidbey. + Thread::ThreadState threadState = pThread->GetSnapshotState(); + if (!((IsThreadMarkedDeadWorker(pThread)) || (threadState & Thread::TS_Unstarted))) + { + VMPTR_Thread vmThread = VMPTR_Thread::NullPtr(); + vmThread.SetHostPtr(pThread); + fpCallback(vmThread, pUserData); + } + + pThread = ThreadStore::GetThreadList(pThread); + } +} + +// public implementation of IsThreadMarkedDead +bool DacDbiInterfaceImpl::IsThreadMarkedDead(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + Thread * pThread = vmThread.GetDacPtr(); + return IsThreadMarkedDeadWorker(pThread); +} + +// Private worker for IsThreadMarkedDead +// +// Arguments: +// pThread - valid thread to check if dead +// +// Returns: +// true iff thread is marked as dead. +// +// Notes: +// This is an internal method that skips public validation. +// See code:IDacDbiInterface::#IsThreadMarkedDead for purpose. +bool DacDbiInterfaceImpl::IsThreadMarkedDeadWorker(Thread * pThread) +{ + _ASSERTE(pThread != NULL); + + Thread::ThreadState threadState = pThread->GetSnapshotState(); + + bool fIsDead = (threadState & Thread::TS_Dead) != 0; + + return fIsDead; +} + + +// Return the handle of the specified thread. +HANDLE DacDbiInterfaceImpl::GetThreadHandle(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + return pThread->GetThreadHandle(); +} + +// Return the object handle for the managed Thread object corresponding to the specified thread. +VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetThreadObject(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + Thread::ThreadState threadState = pThread->GetSnapshotState(); + + if ( (threadState & Thread::TS_Dead) || + (threadState & Thread::TS_Unstarted) || + (threadState & Thread::TS_Detached) || + g_fProcessDetach ) + { + ThrowHR(CORDBG_E_BAD_THREAD_STATE); + } + else + { + VMPTR_OBJECTHANDLE vmObjHandle = VMPTR_OBJECTHANDLE::NullPtr(); + vmObjHandle.SetDacTargetPtr(pThread->GetExposedObjectHandleForDebugger()); + return vmObjHandle; + } +} + +// Set and reset the TSNC_DebuggerUserSuspend bit on the state of the specified thread +// according to the CorDebugThreadState. +void DacDbiInterfaceImpl::SetDebugState(VMPTR_Thread vmThread, + CorDebugThreadState debugState) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + + // update the field on the host copy + if (debugState == THREAD_SUSPEND) + { + pThread->SetThreadStateNC(Thread::TSNC_DebuggerUserSuspend); + } + else if (debugState == THREAD_RUN) + { + pThread->ResetThreadStateNC(Thread::TSNC_DebuggerUserSuspend); + } + else + { + ThrowHR(E_INVALIDARG); + } + + // update the field on the target copy + TADDR taThreadState = PTR_HOST_MEMBER_TADDR(Thread, pThread, m_StateNC); + SafeWriteStructOrThrow<Thread::ThreadStateNoConcurrency>(taThreadState, &(pThread->m_StateNC)); +} + +// Gets the debugger unhandled exception threadstate flag +BOOL DacDbiInterfaceImpl::HasUnhandledException(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + + // some managed exceptions don't have any underlying + // native exception processing going on. They just consist + // of a managed throwable that we have stashed away followed + // by a debugger notification and some form of failfast. + // Everything that comes through EEFatalError is in this category + if(pThread->IsLastThrownObjectUnhandled()) + { + return TRUE; + } + + // most managed exceptions are just a throwable bound to a + // native exception. In that case this handle will be non-null + OBJECTHANDLE ohException = pThread->GetThrowableAsHandle(); + if (ohException != NULL) + { + // during the UEF we set the unhandled bit, if it is set the exception + // was unhandled + // however if the exception has intercept info then we consider it handled + // again + return pThread->GetExceptionState()->GetFlags()->IsUnhandled() && + !(pThread->GetExceptionState()->GetFlags()->DebuggerInterceptInfo()); + } + + return FALSE; +} + +// Return the user state of the specified thread. +CorDebugUserState DacDbiInterfaceImpl::GetUserState(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + UINT result = 0; + result = GetPartialUserState(vmThread); + + if (!IsThreadAtGCSafePlace(vmThread)) + { + result |= USER_UNSAFE_POINT; + } + + return (CorDebugUserState)result; +} + + +// Return the connection ID of the specified thread. +CONNID DacDbiInterfaceImpl::GetConnectionID(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + return pThread->GetConnectionId(); +} + +// Return the task ID of the specified thread. +TASKID DacDbiInterfaceImpl::GetTaskID(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + return pThread->GetTaskId(); +} + +// Return the OS thread ID of the specified thread +DWORD DacDbiInterfaceImpl::TryGetVolatileOSThreadID(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + _ASSERTE(pThread != NULL); + + DWORD dwThreadId = pThread->GetOSThreadIdForDebugger(); + + // If the thread ID is a the magical cookie value, then this is really + // a switched out thread and doesn't have an OS tid. In that case, the + // DD contract is to return 0 (a much more sane value) + const DWORD dwSwitchedOutThreadId = SWITCHED_OUT_FIBER_OSID; + if (dwThreadId == dwSwitchedOutThreadId) + { + return 0; + } + return dwThreadId; +} + +// Return the unique thread ID of the specified thread. +DWORD DacDbiInterfaceImpl::GetUniqueThreadID(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + _ASSERTE(pThread != NULL); + + if (CLRTaskHosted()) + { + return pThread->GetThreadId(); + } + else + { + return pThread->GetOSThreadId(); + } +} + +// Return the object handle to the managed Exception object of the current exception +// on the specified thread. The return value could be NULL if there is no current exception. +VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetCurrentException(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + + // OBJECTHANDLEs are really just TADDRs. + OBJECTHANDLE ohException = pThread->GetThrowableAsHandle(); // ohException can be NULL + + if (ohException == NULL) + { + if (pThread->IsLastThrownObjectUnhandled()) + { + ohException = pThread->LastThrownObjectHandle(); + } + } + + VMPTR_OBJECTHANDLE vmObjHandle; + vmObjHandle.SetDacTargetPtr(ohException); + return vmObjHandle; +} + +// Return the object handle to the managed object for a given CCW pointer. +VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetObjectForCCW(CORDB_ADDRESS ccwPtr) +{ + DD_ENTER_MAY_THROW; + + OBJECTHANDLE ohCCW = NULL; + +#ifdef FEATURE_COMINTEROP + ComCallWrapper *pCCW = DACGetCCWFromAddress(ccwPtr); + if (pCCW) + { + ohCCW = pCCW->GetObjectHandle(); + } +#endif + + VMPTR_OBJECTHANDLE vmObjHandle; + vmObjHandle.SetDacTargetPtr(ohCCW); + return vmObjHandle; +} + +// Return the object handle to the managed CustomNotification object of the current notification +// on the specified thread. The return value could be NULL if there is no current notification. +// Arguments: +// input: vmThread - the thread on which the notification occurred +// Return value: object handle for the current notification (if any) on the thread. This will return non-null +// if and only if we are currently inside a CustomNotification Callback (or a dump was generated while in this +// callback) +// +VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetCurrentCustomDebuggerNotification(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + + // OBJECTHANDLEs are really just TADDRs. + OBJECTHANDLE ohNotification = pThread->GetThreadCurrNotification(); // ohNotification can be NULL + + VMPTR_OBJECTHANDLE vmObjHandle; + vmObjHandle.SetDacTargetPtr(ohNotification); + return vmObjHandle; +} + +// Return the current appdomain the specified thread is in. +VMPTR_AppDomain DacDbiInterfaceImpl::GetCurrentAppDomain(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + AppDomain * pAppDomain = pThread->GetDomain(); + + if (pAppDomain == NULL) + { + ThrowHR(E_FAIL); + } + + VMPTR_AppDomain vmAppDomain = VMPTR_AppDomain::NullPtr(); + vmAppDomain.SetDacTargetPtr(PTR_HOST_TO_TADDR(pAppDomain)); + return vmAppDomain; +} + + +// Returns a bitfield reflecting the managed debugging state at the time of +// the jit attach. +CLR_DEBUGGING_PROCESS_FLAGS DacDbiInterfaceImpl::GetAttachStateFlags() +{ + DD_ENTER_MAY_THROW; + + CLR_DEBUGGING_PROCESS_FLAGS res = (CLR_DEBUGGING_PROCESS_FLAGS)0; + if (g_pDebugger != NULL) + { + res = g_pDebugger->GetAttachStateFlags(); + } + else + { + // When launching the process under a managed debugger we + // request these flags when CLR is loaded (before g_pDebugger + // had a chance to be initialized). In these cases simply + // return 0 + } + return res; +} + +//--------------------------------------------------------------------------------------- +// Helper to get the address of the 2nd-chance hijack function Or throw +// +// Returns: +// Non-null Target Address of hijack function. +TADDR DacDbiInterfaceImpl::GetHijackAddress() +{ + TADDR addr = NULL; + if (g_pDebugger != NULL) + { + // Get the start address of the redirect function for unhandled exceptions. + addr = dac_cast<TADDR>(g_pDebugger->m_rgHijackFunction[Debugger::kUnhandledException].StartAddress()); + } + if (addr == NULL) + { + ThrowHR(CORDBG_E_NOTREADY); + } + return addr; +} + +//--------------------------------------------------------------------------------------- +// Helper to determine whether a control PC is in any native stub which the runtime knows how to unwind. +// +// Arguments: +// taControlPC - control PC to be checked +// +// Returns: +// Returns true if the control PC is in a runtime unwindable stub. +// +// Notes: +// Currently this function only recognizes the ExceptionHijack() stub, +// which is used for unhandled exceptions. +// + +bool DacDbiInterfaceImpl::IsRuntimeUnwindableStub(PCODE targetControlPC) +{ + + TADDR controlPC = PCODEToPINSTR(targetControlPC); + // we call this function a lot while walking the stack and the values here will never change + // Getting the g_pDebugger and each entry in the m_rgHijackFunction is potentially ~7 DAC + // accesses per frame. Caching the data into a single local array is much faster. This optimization + // recovered a few % of DAC stackwalking time + if(!m_isCachedHijackFunctionValid) + { + Debugger* pDebugger = g_pDebugger; + if ((pDebugger == NULL) || (pDebugger->m_rgHijackFunction == NULL)) + { + // The in-process debugging infrastructure hasn't been fully initialized, which means that we could + // NOT have hijacked anything yet. + return false; + } + + // PERF NOTE: if needed this array copy could probably be made more efficient + // hitting the DAC only once for a single memory block, or even better + // put the array inline in the Debugger object so that we only do 1 DAC + // access for this entire thing + for (int i = 0; i < Debugger::kMaxHijackFunctions; i++) + { + InitTargetBufferFromMemoryRange(pDebugger->m_rgHijackFunction[i], &m_pCachedHijackFunction[i] ); + } + m_isCachedHijackFunctionValid = TRUE; + } + + // Check whether the control PC is in any of the thread redirection functions. + for (int i = 0; i < Debugger::kMaxHijackFunctions; i++) + { + CORDB_ADDRESS start = m_pCachedHijackFunction[i].pAddress; + CORDB_ADDRESS end = start + m_pCachedHijackFunction[i].cbSize; + if ((start <= controlPC) && (controlPC < end)) + { + return true; + } + } + return false; +} + +//--------------------------------------------------------------------------------------- +// Align a stack pointer for the given architecture +// +// Arguments: +// pEsp - in/out: pointer to stack pointer. +// +void DacDbiInterfaceImpl::AlignStackPointer(CORDB_ADDRESS * pEsp) +{ + SUPPORTS_DAC; + + // Nop on x86. +#if defined(_WIN64) + // on 64-bit, stack pointer must be 16-byte aligned. + // Stacks grown down, so round down to nearest 0xF bits. + *pEsp &= ~((CORDB_ADDRESS) 0xF); +#endif +} + +//--------------------------------------------------------------------------------------- +// Emulate pushing something on a thread's stack. +// +// Arguments: +// pEsp - in/out: pointer to stack pointer to push object at. On output, +// updated stack pointer. +// pData - object to push on the stack. +// fAlignStack - whether to align the stack pointer before and after the push. +// Callers which specify FALSE must be very careful and know exactly +// what they are doing. +// +// Return: +// address of pushed object. Throws on error. +template <class T> +CORDB_ADDRESS DacDbiInterfaceImpl::PushHelper(CORDB_ADDRESS * pEsp, + const T * pData, + BOOL fAlignStack) +{ + SUPPORTS_DAC; + + if (fAlignStack == TRUE) + { + AlignStackPointer(pEsp); + } + *pEsp -= sizeof(T); + if (fAlignStack == TRUE) + { + AlignStackPointer(pEsp); + } + SafeWriteStructOrThrow(*pEsp, pData); + return *pEsp; +} + +//--------------------------------------------------------------------------------------- +// Write an EXCEPTION_RECORD structure to the remote target at the specified address while taking +// into account the number of exception parameters. On 64-bit OS and on the WOW64, the OS always +// pushes the entire EXCEPTION_RECORD onto the stack. However, on native x86 OS, the OS only pushes +// enough of the EXCEPTION_RECORD to cover the specified number of exception parameters. Thus we +// need to be extra careful when we overwrite an EXCEPTION_RECORD on the stack. +// +// Arguments: +// pRemotePtr - address of the EXCEPTION_RECORD in the remote target +// pExcepRecord - EXCEPTION_RECORD to be written +// +// Notes: +// This function is only used by the code which hijacks a therad when there's an unhandled exception. +// It only works when we are actually debugging a live process, not a dump. +// + +void DacDbiInterfaceImpl::WriteExceptionRecordHelper(CORDB_ADDRESS pRemotePtr, + const EXCEPTION_RECORD * pExcepRecord) +{ + // Calculate the correct size to push onto the stack. + ULONG32 cbSize = offsetof(EXCEPTION_RECORD, ExceptionInformation); + cbSize += pExcepRecord->NumberParameters * sizeof(pExcepRecord->ExceptionInformation[0]); + + // Use the data target to write to the remote target. Here we are assuming that we are debugging a + // live process, since this function is only called by the hijacking code for unhandled exceptions. + HRESULT hr = m_pMutableTarget->WriteVirtual(pRemotePtr, + reinterpret_cast<const BYTE *>(pExcepRecord), + cbSize); + + if (FAILED(hr)) + { + ThrowHR(hr); + } +} + +// Implement IDacDbiInterface::Hijack +void DacDbiInterfaceImpl::Hijack( + VMPTR_Thread vmThread, + ULONG32 dwThreadId, + const EXCEPTION_RECORD * pRecord, + T_CONTEXT * pOriginalContext, + ULONG32 cbSizeContext, + EHijackReason::EHijackReason reason, + void * pUserData, + CORDB_ADDRESS * pRemoteContextAddr) +{ + DD_ENTER_MAY_THROW; + + // + // Validate parameters + // + + // pRecord may be NULL if we're not hijacking at an exception + // pOriginalContext may be NULL if caller doesn't want a copy of the context. + // (The hijack function already has the context) + _ASSERTE((pOriginalContext == NULL) == (cbSizeContext == 0)); + _ASSERTE(EHijackReason::IsValid(reason)); +#ifdef PLATFORM_UNIX + _ASSERTE(!"Not supported on this platform"); +#endif + + // + // If we hijack a thread which might not be managed we can set vmThread = NULL + // The only side-effect in this case is that we can't reuse CONTEXT and + // EXCEPTION_RECORD space on the stack by an already underway in-process exception + // filter. If you depend on those being used and updated you must provide the vmThread + // + Thread* pThread = NULL; + if(!vmThread.IsNull()) + { + pThread = vmThread.GetDacPtr(); + _ASSERTE(pThread->GetOSThreadIdForDebugger() == dwThreadId); + } + + TADDR pfnHijackFunction = GetHijackAddress(); + + // + // Setup context for hijack + // + T_CONTEXT ctx; + HRESULT hr = m_pTarget->GetThreadContext( + dwThreadId, + CONTEXT_FULL, + sizeof(ctx), + (BYTE*) &ctx); + IfFailThrow(hr); + + // If caller requested, copy back the original context that we're hijacking from. + if (pOriginalContext != NULL) + { + // Since Dac + DBI are tightly coupled, context sizes should be the same. + if (cbSizeContext != sizeof(T_CONTEXT)) + { + ThrowHR(E_INVALIDARG); + } + + memcpy(pOriginalContext, &ctx, cbSizeContext); + } + + // Make sure the trace flag isn't on. This can happen if we were single stepping the thread when we faulted. This + // will ensure that we don't try to single step through the OS's exception logic, which greatly confuses our second + // chance hijack logic. This also mimics what the OS does for us automaically when single stepping in process, i.e., + // when you turn the trace flag on in-process and go, if there is a fault, the fault is reported and the trace flag + // is automatically turned off. + // + // The debugger could always re-enable the single-step flag if it wants to. +#ifndef _TARGET_ARM_ + UnsetSSFlag(reinterpret_cast<DT_CONTEXT *>(&ctx)); +#endif + + // Push pointers + void* espContext = NULL; + void* espRecord = NULL; + const void* pData = pUserData; + + // @dbgtodo cross-plat - this is not cross plat + CORDB_ADDRESS esp = GetSP(&ctx); + + // + // Find out where the OS exception dispatcher has pushed the EXCEPTION_RECORD and CONTEXT. The ExInfo and + // ExceptionTracker have pointers to these data structures, but when we get the unhandled exception + // notification, the OS exception dispatcher is no longer on the stack, so these pointers are no longer + // valid. We need to either update these pointers in the ExInfo/ExcepionTracker, or reuse the stack + // space used by the OS exception dispatcher. We are using the latter approach here. + // + + CORDB_ADDRESS espOSContext = NULL; + CORDB_ADDRESS espOSRecord = NULL; + if (pThread != NULL && pThread->IsExceptionInProgress()) + { + espOSContext = (CORDB_ADDRESS)PTR_TO_TADDR(pThread->GetExceptionState()->GetContextRecord()); + espOSRecord = (CORDB_ADDRESS)PTR_TO_TADDR(pThread->GetExceptionState()->GetExceptionRecord()); + + // The managed exception may not be related to the unhandled exception for which we are trying to + // hijack. An example would be when a thread hits a managed exception, VS tries to do func eval on + // the thread, but the func eval causes an unhandled exception (e.g. AV in mscorwks.dll). In this + // case, the pointers stored on the ExInfo/ExceptionTracker are closer to the root than the current + // SP of the thread. The check below makes sure we don't reuse the pointers in this case. + if (espOSContext < esp) + { + SafeWriteStructOrThrow(espOSContext, &ctx); + espContext = CORDB_ADDRESS_TO_PTR(espOSContext); + + // We should have an EXCEPTION_RECORD if we are hijacked at an exception. + // We need to be careful when we overwrite the exception record. On x86, the OS doesn't + // always push the full record onto the stack, and so we can't blindly use sizeof(EXCEPTION_RECORD). + // Instead, we have to look at the number of exception parameters and calculate the size. + _ASSERTE(pRecord != NULL); + WriteExceptionRecordHelper(espOSRecord, pRecord); + espRecord = CORDB_ADDRESS_TO_PTR(espOSRecord); + + esp = min(espOSContext, espOSRecord); + } + } + + // If we haven't reused the pointers, then push everything at the leaf of the stack. + if (espContext == NULL) + { + _ASSERTE(espRecord == NULL); + + // Push on full Context and ExceptionRecord structures. We'll then push pointers to these, + // and those pointers will serve as the actual args to the function. + espContext = CORDB_ADDRESS_TO_PTR(PushHelper(&esp, &ctx, TRUE)); + + // If caller didn't pass an exception-record, then we're not being hijacked at an exception. + // We'll just pass NULL for the exception-record to the Hijack function. + if (pRecord != NULL) + { + espRecord = CORDB_ADDRESS_TO_PTR(PushHelper(&esp, pRecord, TRUE)); + } + } + + if(pRemoteContextAddr != NULL) + { + *pRemoteContextAddr = PTR_TO_CORDB_ADDRESS(espContext); + } + + // + // Push args onto the stack to be able to call the hijack function + // + + // Prototype of hijack is: + // void __stdcall ExceptionHijackWorker(CONTEXT * pContext, EXCEPTION_RECORD * pRecord, EHijackReason, void * pData) + // Set up everything so that the hijack stub can just do a "call" instruction. + // + // Regarding stack overflow: We could do an explicit check against the thread's stack base limit. + // However, we don't need an explicit overflow check because if the stack does overflow, + // the hijack will just hit a regular stack-overflow exception. +#if defined(_TARGET_X86_) // TARGET + // X86 calling convention is to push args on the stack in reverse order. + // If we fail here, the stack is written, but esp hasn't been committed yet so it shouldn't matter. + PushHelper(&esp, &pData, TRUE); + PushHelper(&esp, &reason, TRUE); + PushHelper(&esp, &espRecord, TRUE); + PushHelper(&esp, &espContext, TRUE); +#elif defined (_TARGET_AMD64_) // TARGET + // AMD64 calling convention is to place first 4 parameters in: rcx, rdx, r8 and r9 + ctx.Rcx = (DWORD64) espContext; + ctx.Rdx = (DWORD64) espRecord; + ctx.R8 = (DWORD64) reason; + ctx.R9 = (DWORD64) pData; + + // Caller must allocate stack space to spill for args. + // Push the arguments onto the outgoing argument homes. + // Make sure we push pointer-sized values to keep the stack aligned. + PushHelper(&esp, reinterpret_cast<SIZE_T *>(&(ctx.R9)), FALSE); + PushHelper(&esp, reinterpret_cast<SIZE_T *>(&(ctx.R8)), FALSE); + PushHelper(&esp, reinterpret_cast<SIZE_T *>(&(ctx.Rdx)), FALSE); + PushHelper(&esp, reinterpret_cast<SIZE_T *>(&(ctx.Rcx)), FALSE); +#elif defined(_TARGET_ARM_) + ctx.R0 = (DWORD)espContext; + ctx.R1 = (DWORD)espRecord; + ctx.R2 = (DWORD)reason; + ctx.R3 = (DWORD)pData; +#elif defined(_TARGET_ARM64_) + ctx.X0 = (DWORD64)espContext; + ctx.X1 = (DWORD64)espRecord; + ctx.X2 = (DWORD64)reason; + ctx.X3 = (DWORD64)pData; +#else + PORTABILITY_ASSERT("CordbThread::HijackForUnhandledException is not implemented on this platform."); +#endif + SetSP(&ctx, CORDB_ADDRESS_TO_TADDR(esp)); + + // @dbgtodo cross-plat - not cross-platform safe + SetIP(&ctx, pfnHijackFunction); + + // + // Commit the context. + // + hr = m_pMutableTarget->SetThreadContext(dwThreadId, sizeof(ctx), reinterpret_cast<BYTE*> (&ctx)); + IfFailThrow(hr); +} + +// Return the filter CONTEXT on the LS. +VMPTR_CONTEXT DacDbiInterfaceImpl::GetManagedStoppedContext(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + VMPTR_CONTEXT vmContext = VMPTR_CONTEXT::NullPtr(); + + Thread * pThread = vmThread.GetDacPtr(); + if (pThread->GetInteropDebuggingHijacked()) + { + _ASSERTE(!ISREDIRECTEDTHREAD(pThread)); + vmContext = VMPTR_CONTEXT::NullPtr(); + } + else + { + DT_CONTEXT * pLSContext = reinterpret_cast<DT_CONTEXT *>(pThread->GetFilterContext()); + if (pLSContext != NULL) + { + _ASSERTE(!ISREDIRECTEDTHREAD(pThread)); + vmContext.SetHostPtr(pLSContext); + } + else if (ISREDIRECTEDTHREAD(pThread)) + { + pLSContext = reinterpret_cast<DT_CONTEXT *>(GETREDIRECTEDCONTEXT(pThread)); + _ASSERTE(pLSContext != NULL); + + if (pLSContext != NULL) + { + vmContext.SetHostPtr(pLSContext); + } + } + } + + return vmContext; +} + +// Return a TargetBuffer for the raw vararg signature. +TargetBuffer DacDbiInterfaceImpl::GetVarArgSig(CORDB_ADDRESS VASigCookieAddr, + CORDB_ADDRESS * pArgBase) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(pArgBase != NULL); + *pArgBase = NULL; + + // First, read the VASigCookie pointer. + TADDR taVASigCookie = NULL; + SafeReadStructOrThrow(VASigCookieAddr, &taVASigCookie); + + // Now create a DAC copy of VASigCookie. + VASigCookie * pVACookie = PTR_VASigCookie(taVASigCookie); + + // Figure out where the first argument is. +#if defined(_TARGET_X86_) // (STACK_GROWS_DOWN_ON_ARGS_WALK) + *pArgBase = VASigCookieAddr + pVACookie->sizeOfArgs; +#else // !_TARGET_X86_ (STACK_GROWS_UP_ON_ARGS_WALK) + *pArgBase = VASigCookieAddr + sizeof(VASigCookie *); +#endif // !_TARGET_X86_ (STACK_GROWS_UP_ON_ARGS_WALK) + + return TargetBuffer(PTR_TO_CORDB_ADDRESS(pVACookie->signature.GetRawSig()), + pVACookie->signature.GetRawSigLen()); +} + +// returns TRUE if the type requires 8-byte alignment +BOOL DacDbiInterfaceImpl::RequiresAlign8(VMPTR_TypeHandle thExact) +{ + DD_ENTER_MAY_THROW; + +#ifdef FEATURE_64BIT_ALIGNMENT + TypeHandle th = TypeHandle::FromPtr(thExact.GetDacPtr()); + PTR_MethodTable mt = th.AsMethodTable(); + + return mt->RequiresAlign8(); +#else + ThrowHR(E_NOTIMPL); +#endif +} + +// Resolve the raw generics token to the real generics type token. The resolution is based on the +// given index. +GENERICS_TYPE_TOKEN DacDbiInterfaceImpl::ResolveExactGenericArgsToken(DWORD dwExactGenericArgsTokenIndex, + GENERICS_TYPE_TOKEN rawToken) +{ + DD_ENTER_MAY_THROW; + + if (dwExactGenericArgsTokenIndex == 0) + { + // In this case the real generics type token is the MethodTable of the "this" object. + // Note that we want the target address here. + + // Incoming rawToken is actually a PTR_Object for the 'this' pointer. + // Need to do some casting to convert GENERICS_TYPE_TOKEN --> PTR_Object + TADDR addrObjThis = CORDB_ADDRESS_TO_TADDR(rawToken); + PTR_Object pObjThis = dac_cast<PTR_Object>(addrObjThis); + + + PTR_MethodTable pMT = pObjThis->GetMethodTable(); + + // Now package up the PTR_MethodTable back into a GENERICS_TYPE_TOKEN + TADDR addrMT = dac_cast<TADDR>(pMT); + GENERICS_TYPE_TOKEN realToken = (GENERICS_TYPE_TOKEN) addrMT; + return realToken; + } + else if (dwExactGenericArgsTokenIndex == (DWORD)ICorDebugInfo::TYPECTXT_ILNUM) + { + // rawToken is already initialized correctly. Nothing to do here. + return rawToken; + } + + // The index of the generics type token should not be anything else. + // This is indeed an error condition, and so we throw here. + _ASSERTE(!"DDII::REGAT - Unexpected generics type token index."); + ThrowHR(CORDBG_E_TARGET_INCONSISTENT); +} + +// Check if the given method is an IL stub or an LCD method. +IDacDbiInterface::DynamicMethodType DacDbiInterfaceImpl::IsILStubOrLCGMethod(VMPTR_MethodDesc vmMethodDesc) +{ + DD_ENTER_MAY_THROW; + + MethodDesc * pMD = vmMethodDesc.GetDacPtr(); + + if (pMD->IsILStub()) + { + return kILStub; + } + else if (pMD->IsLCGMethod()) + { + return kLCGMethod; + } + else + { + return kNone; + } +} + +//--------------------------------------------------------------------------------------- +// +// Determine whether the specified thread is at a GC safe place. +// +// Arguments: +// vmThread - the thread to be examined +// +// Return Value: +// Return TRUE if the thread is at a GC safe place. +// and under what conditions +// +// Notes: +// This function basically does a one-frame stackwalk. +// The logic is adopted from Debugger::IsThreadAtSafePlace(). +// + +BOOL DacDbiInterfaceImpl::IsThreadAtGCSafePlace(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + BOOL fIsGCSafe = FALSE; + Thread * pThread = vmThread.GetDacPtr(); + + // Check if the runtime has entered "Shutdown for Finalizer" mode. + if ((g_fEEShutDown & ShutDown_Finalize2) != 0) + { + fIsGCSafe = TRUE; + } + else + { + T_CONTEXT ctx; + REGDISPLAY rd; + SetUpRegdisplayForStackWalk(pThread, &ctx, &rd); + + ULONG32 flags = (QUICKUNWIND | HANDLESKIPPEDFRAMES | DISABLE_MISSING_FRAME_DETECTION); + + StackFrameIterator iter; + iter.Init(pThread, pThread->GetFrame(), &rd, flags); + + CrawlFrame * pCF = &(iter.m_crawl); + if (pCF->IsFrameless() && pCF->IsActiveFunc()) + { + if (pCF->IsGcSafe()) + { + fIsGCSafe = TRUE; + } + } + } + + return fIsGCSafe; +} + +//--------------------------------------------------------------------------------------- +// +// Return a partial user state of the specified thread. The returned user state doesn't contain +// information about USER_UNSAFE_POINT. The caller needs to call IsThreadAtGCSafePlace() to get +// the full user state. +// +// Arguments: +// vmThread - the specified thread +// +// Return Value: +// Return the partial user state except for USER_UNSAFE_POINT +// + +CorDebugUserState DacDbiInterfaceImpl::GetPartialUserState(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + Thread::ThreadState ts = pThread->GetSnapshotState(); + + UINT result = 0; + if (ts & Thread::TS_Background) + { + result |= USER_BACKGROUND; + } + + if (ts & Thread::TS_Unstarted) + { + result |= USER_UNSTARTED; + } + + // Don't report a StopRequested if the thread has actually stopped. + if (ts & Thread::TS_Dead) + { + result |= USER_STOPPED; + } + + // The interruptible flag is unreliable (see issue 699245) + // The Debugger_SleepWaitJoin is always accurate when it is present, but it is still + // just a band-aid fix to cover some of the race conditions interruptible has. + + if (ts & Thread::TS_Interruptible || pThread->HasThreadStateNC(Thread::TSNC_DebuggerSleepWaitJoin)) + { + result |= USER_WAIT_SLEEP_JOIN; + } + + // Don't report a SuspendRequested if the thread has actually Suspended. + if ((ts & Thread::TS_UserSuspendPending) && (ts & Thread::TS_SyncSuspended)) + { + result |= USER_SUSPENDED; + } + else if (ts & Thread::TS_UserSuspendPending) + { + result |= USER_SUSPEND_REQUESTED; + } + + if (pThread->IsThreadPoolThread()) + { + result |= USER_THREADPOOL; + } + + return (CorDebugUserState)result; +} + +//--------------------------------------------------------------------------------------- +// +// Look up the EnC version number of a particular jitted instance of a managed method. +// +// Arguments: +// pModule - the module containing the managed method +// vmMethodDesc - the MethodDesc of the managed method +// mdMethod - the MethodDef metadata token of the managed method +// pNativeStartAddress - the native start address of the jitted code +// pJittedInstanceEnCVersion - out parameter; the version number of the version +// corresponding to the specified native start address +// pLatestEnCVersion - out parameter; the version number of the latest version +// +// Assumptions: +// vmMethodDesc and mdMethod must match (see below). +// +// Notes: +// mdMethod is not strictly necessary, since we can always get that from vmMethodDesc. +// It is just a perf optimization since the caller has the metadata token around already. +// +// Today, there is no way to retrieve the EnC version number from the RS data structures. +// This primitive uses DAC to retrieve it from the LS data structures. This function may +// very well be ripped out in the future if we DACize this information, but the current +// thinking is that some of the RS data structures will remain, most likely in a reduced form. +// + +void DacDbiInterfaceImpl::LookupEnCVersions(Module* pModule, + VMPTR_MethodDesc vmMethodDesc, + mdMethodDef mdMethod, + CORDB_ADDRESS pNativeStartAddress, + SIZE_T * pLatestEnCVersion, + SIZE_T * pJittedInstanceEnCVersion /* = NULL */) +{ + MethodDesc * pMD = vmMethodDesc.GetDacPtr(); + + // make sure the vmMethodDesc and mdMethod match + _ASSERTE(pMD->GetMemberDef() == mdMethod); + + _ASSERTE(pLatestEnCVersion != NULL); + + // @dbgtodo inspection - once we do EnC, stop using DMIs. + // If the method wasn't EnCed, DMIs may not exist. And since this is DAC, we can't create them. + + // We may not have the memory for the DebuggerMethodInfos in a minidump. + // When dump debugging EnC information isn't very useful so just fallback + // to default version. + DebuggerMethodInfo * pDMI = NULL; + DebuggerJitInfo * pDJI = NULL; + EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY + { + pDMI = g_pDebugger->GetOrCreateMethodInfo(pModule, mdMethod); + if (pDMI != NULL) + { + pDJI = pDMI->FindJitInfo(pMD, CORDB_ADDRESS_TO_TADDR(pNativeStartAddress)); + } + } + EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY; + if (pDJI != NULL) + { + if (pJittedInstanceEnCVersion != NULL) + { + *pJittedInstanceEnCVersion = pDJI->m_encVersion; + } + *pLatestEnCVersion = pDMI->GetCurrentEnCVersion(); + } + else + { + // If we have no DMI/DJI, then we must never have EnCed. So we can use default EnC info + // Several cases where we don't have a DMI/DJI: + // - LCG methods + // - method was never "touched" by debugger. (DJIs are created lazily). + if (pJittedInstanceEnCVersion != NULL) + { + *pJittedInstanceEnCVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION; + } + *pLatestEnCVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION; + } +} + +// Get the address of the Debugger control block on the helper thread +// Arguments: none +// Return Value: The remote address of the Debugger control block allocated on the helper thread +// if it has been successfully allocated or NULL otherwise. +CORDB_ADDRESS DacDbiInterfaceImpl::GetDebuggerControlBlockAddress() +{ + DD_ENTER_MAY_THROW; + + if ((g_pDebugger != NULL) && + (g_pDebugger->m_pRCThread != NULL)) + { + return CORDB_ADDRESS(dac_cast<TADDR>(g_pDebugger->m_pRCThread->GetDCB())); + } + + return NULL; +} + +// DacDbi API: Get the context for a particular thread of the target process +void DacDbiInterfaceImpl::GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pContextBuffer) +{ + DD_ENTER_MAY_THROW + + _ASSERTE(pContextBuffer != NULL); + + Thread * pThread = vmThread.GetDacPtr(); + + // @dbgtodo Once the filter context is removed, then we should always + // start with the leaf CONTEXT. + DT_CONTEXT * pFilterContext = reinterpret_cast<DT_CONTEXT *>(pThread->GetFilterContext()); + + if (pFilterContext == NULL) + { + // If the filter context is NULL, then we use the true context of the thread. + pContextBuffer->ContextFlags = CONTEXT_ALL; + HRESULT hr = m_pTarget->GetThreadContext(pThread->GetOSThreadId(), + pContextBuffer->ContextFlags, + sizeof(*pContextBuffer), + reinterpret_cast<BYTE *>(pContextBuffer)); + if (hr == E_NOTIMPL) + { + // GetThreadContext is not implemented on this data target. + // That's why we have to make do with context we can obtain from Frames explicitly stored in Thread object. + // It suffices for managed debugging stackwalk. + REGDISPLAY tmpRd = {}; + T_CONTEXT tmpContext = {}; + FillRegDisplay(&tmpRd, &tmpContext); + + // Going through thread Frames and looking for first (deepest one) one that + // that has context available for stackwalking (SP and PC) + // For example: RedirectedThreadFrame, InlinedCallFrame, HelperMethodFrame, ComPlusMethodFrame + Frame *frame = pThread->GetFrame(); + while (frame != NULL && frame != FRAME_TOP) + { + frame->UpdateRegDisplay(&tmpRd); + if (GetRegdisplaySP(&tmpRd) != 0 && GetControlPC(&tmpRd) != 0) + { + UpdateContextFromRegDisp(&tmpRd, &tmpContext); + CopyMemory(pContextBuffer, &tmpContext, sizeof(*pContextBuffer)); + pContextBuffer->ContextFlags = DT_CONTEXT_CONTROL; + return; + } + frame = frame->Next(); + } + + // It looks like this thread is not running managed code. + ZeroMemory(pContextBuffer, sizeof(*pContextBuffer)); + } + else + { + IfFailThrow(hr); + } + } + else + { + *pContextBuffer = *pFilterContext; + } + +} // DacDbiInterfaceImpl::GetContext + +// Create a VMPTR_Object from a target object address +// @dbgtodo validate the VMPTR_Object is in fact a object, possibly by DACizing +// Object::Validate +VMPTR_Object DacDbiInterfaceImpl::GetObject(CORDB_ADDRESS ptr) +{ + DD_ENTER_MAY_THROW; + + VMPTR_Object vmObj = VMPTR_Object::NullPtr(); + vmObj.SetDacTargetPtr(CORDB_ADDRESS_TO_TADDR(ptr)); + return vmObj; +} + +HRESULT DacDbiInterfaceImpl::EnableNGENPolicy(CorDebugNGENPolicy ePolicy) +{ +#ifndef FEATURE_CORECLR + DD_ENTER_MAY_THROW; + + // translate from our publicly exposed enum to the appropriate internal value + AssemblyUsageLogManager::ASSEMBLY_USAGE_LOG_FLAGS asmFlag = AssemblyUsageLogManager::ASSEMBLY_USAGE_LOG_FLAGS_NONE; + + switch (ePolicy) + { + case DISABLE_LOCAL_NIC: + asmFlag = AssemblyUsageLogManager::ASSEMBLY_USAGE_LOG_FLAGS_APPLOCALNGENDISABLED; + break; + default: + return E_INVALIDARG; + } + + // we should have made some selection + _ASSERTE(asmFlag != AssemblyUsageLogManager::ASSEMBLY_USAGE_LOG_FLAGS_NONE); + + return AssemblyUsageLogManager::SetUsageLogFlag(asmFlag, TRUE); +#else + return E_NOTIMPL; +#endif // FEATURE_CORECLR +} + +HRESULT DacDbiInterfaceImpl::SetNGENCompilerFlags(DWORD dwFlags) +{ + DD_ENTER_MAY_THROW; + +#ifndef FEATURE_PREJIT + return CORDBG_E_NGEN_NOT_SUPPORTED; +#else + // verify that we are still early enough in runtime lifecycle to mutate these + // flags. Typically this is done in the CreateProcess event though it is possible + // to do it even earlier + if(!Debugger::s_fCanChangeNgenFlags) + return CORDBG_E_MUST_BE_IN_CREATE_PROCESS; + + BOOL fAllowOpt = + ((dwFlags & CORDEBUG_JIT_DISABLE_OPTIMIZATION) != CORDEBUG_JIT_DISABLE_OPTIMIZATION); + PEFile::SetNGENDebugFlags(fAllowOpt); + return S_OK; +#endif +} + +HRESULT DacDbiInterfaceImpl::GetNGENCompilerFlags(DWORD *pdwFlags) +{ + DD_ENTER_MAY_THROW; + +#ifndef FEATURE_PREJIT + return CORDBG_E_NGEN_NOT_SUPPORTED; +#else + BOOL fAllowOpt = TRUE; + PEFile::GetNGENDebugFlags(&fAllowOpt); + if(!fAllowOpt) + { + *pdwFlags = CORDEBUG_JIT_DISABLE_OPTIMIZATION; + } + else + { + *pdwFlags = CORDEBUG_JIT_DEFAULT; + } + + return S_OK; +#endif +} + +typedef DPTR(OBJECTREF) PTR_ObjectRef; + +// Create a VMPTR_Object from an address which points to a reference to an object +// @dbgtodo validate the VMPTR_Object is in fact a object, possibly by DACizing +// Object::Validate +VMPTR_Object DacDbiInterfaceImpl::GetObjectFromRefPtr(CORDB_ADDRESS ptr) +{ + DD_ENTER_MAY_THROW; + + VMPTR_Object vmObj = VMPTR_Object::NullPtr(); + PTR_ObjectRef objRef = PTR_ObjectRef(CORDB_ADDRESS_TO_TADDR(ptr)); + vmObj.SetDacTargetPtr(PTR_TO_TADDR(*objRef)); + + return vmObj; +} + +// Create a VMPTR_OBJECTHANDLE from a handle +VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetVmObjectHandle(CORDB_ADDRESS handleAddress) +{ + DD_ENTER_MAY_THROW; + + VMPTR_OBJECTHANDLE vmObjHandle = VMPTR_OBJECTHANDLE::NullPtr(); + vmObjHandle.SetDacTargetPtr(CORDB_ADDRESS_TO_TADDR(handleAddress)); + + return vmObjHandle; +} + + +// Validate that the VMPTR_OBJECTHANDLE refers to a legitimate managed object +BOOL DacDbiInterfaceImpl::IsVmObjectHandleValid(VMPTR_OBJECTHANDLE vmHandle) +{ + DD_ENTER_MAY_THROW; + + BOOL ret = FALSE; + // this may cause unallocated debuggee memory to be read + // SEH exceptions will be caught + EX_TRY + { + OBJECTREF objRef = ObjectFromHandle((OBJECTHANDLE)vmHandle.GetDacPtr()); + + // NULL is certinally valid... + if (objRef != NULL) + { + if (objRef->ValidateObjectWithPossibleAV()) + { + ret = TRUE; + } + } + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); + + return ret; +} + +// determines if the specified module is a WinRT module +HRESULT DacDbiInterfaceImpl::IsWinRTModule(VMPTR_Module vmModule, BOOL& isWinRT) +{ + DD_ENTER_MAY_THROW; + + HRESULT hr = S_OK; + isWinRT = FALSE; + + EX_TRY + { + Module* pModule = vmModule.GetDacPtr(); + isWinRT = pModule->GetFile()->GetAssembly()->IsWindowsRuntime(); + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +// Determines the app domain id for the object refered to by a given VMPTR_OBJECTHANDLE +ULONG DacDbiInterfaceImpl::GetAppDomainIdFromVmObjectHandle(VMPTR_OBJECTHANDLE vmHandle) +{ + DD_ENTER_MAY_THROW; + + OBJECTHANDLE handle = (OBJECTHANDLE) vmHandle.GetDacPtr(); + return HndGetHandleADIndex(handle).m_dwIndex; +} + +// Get the target address from a VMPTR_OBJECTHANDLE, i.e., the handle address +CORDB_ADDRESS DacDbiInterfaceImpl::GetHandleAddressFromVmHandle(VMPTR_OBJECTHANDLE vmHandle) +{ + DD_ENTER_MAY_THROW; + + CORDB_ADDRESS handle = vmHandle.GetDacPtr(); + + return handle; +} + +// Create a TargetBuffer which describes the location of the object +TargetBuffer DacDbiInterfaceImpl::GetObjectContents(VMPTR_Object vmObj) +{ + DD_ENTER_MAY_THROW; + PTR_Object objPtr = vmObj.GetDacPtr(); + + _ASSERTE(objPtr->GetSize() <= 0xffffffff); + return TargetBuffer(PTR_TO_TADDR(objPtr), (ULONG)objPtr->GetSize()); +} + +// ============================================================================ +// functions to get information about objects referenced via an instance of CordbReferenceValue or +// CordbHandleValue +// ============================================================================ + +// DacDbiInterfaceImpl::FastSanityCheckObject +// Helper function for CheckRef. Sanity check an object. +// We use a fast and easy check to improve confidence that objPtr points to a valid object. +// We can't tell cheaply if this is really a valid object (that would require walking the GC heap), but at +// least we can check if we get an EEClass from the supposed method table and then get the method table from +// the class. If we can, we have improved the probability that the object is valid. +// Arguments: +// input: objPtr - address of the object we are checking +// Return Value: E_INVALIDARG or S_OK. +HRESULT DacDbiInterfaceImpl::FastSanityCheckObject(PTR_Object objPtr) +{ + CONTRACTL + { + SO_NOT_MAINLINE; + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + EX_TRY + { + // NULL is certainly valid... + if (objPtr != NULL) + { + if (!objPtr->ValidateObjectWithPossibleAV()) + { + LOG((LF_CORDB, LL_INFO10000, "GOI: object methodtable-class invariant doesn't hold.\n")); + hr = E_INVALIDARG; + } + } + } + EX_CATCH + { + LOG((LF_CORDB, LL_INFO10000, "GOI: exception indicated ref is bad.\n")); + hr = E_INVALIDARG; + } + EX_END_CATCH(SwallowAllExceptions); + + return hr; +} // DacDbiInterfaceImpl::FastSanityCheckObject + +// Perform a sanity check on an object address to determine if this _could be_ a valid object. +// We can't tell this for certain without walking the GC heap, but we do some fast tests to rule +// out clearly invalid object addresses. See code:DacDbiInterfaceImpl::FastSanityCheckObject for more +// details. +// Arguments: +// input: objPtr - address of the object we are checking +// Return Value: +// objRefBad - true iff we have determined the address cannot be pointing to a valid object. +// Note that a value of false doesn't necessarily guarantee the object is really +// valid +bool DacDbiInterfaceImpl::CheckRef(PTR_Object objPtr) +{ + bool objRefBad = false; + + // Shortcut null references now... + if (objPtr == NULL) + { + LOG((LF_CORDB, LL_INFO10000, "D::GOI: ref is NULL.\n")); + + objRefBad = true; + } + else + { + // Try to verify the integrity of the object. This is not fool proof. + // @todo - this whole idea of expecting AVs is broken, but it does rule + // out a fair bit of rubbish. Find another + // way to test if the object is valid? + if (FAILED(FastSanityCheckObject(objPtr))) + { + LOG((LF_CORDB, LL_INFO10000, "D::GOI: address is not a valid object.\n")); + + objRefBad = true; + } + } + + return objRefBad; +} // DacDbiInterfaceImpl::CheckRef + +// DacDbiInterfaceImpl::InitObjectData +// Initialize basic object information: type handle, object size, offset to fields and expanded type +// information. +// Arguments: +// input: objPtr - address of object of interest +// vmAppDomain - AppDomain for the type f the object +// output: pObjectData - object information +// Note: It is assumed that pObjectData is non-null. +void DacDbiInterfaceImpl::InitObjectData(PTR_Object objPtr, + VMPTR_AppDomain vmAppDomain, + DebuggerIPCE_ObjectData * pObjectData) +{ + _ASSERTE(pObjectData != NULL); + // @todo - this is still dangerous because the object may still be invalid. + VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); + vmTypeHandle.SetDacTargetPtr(objPtr->GetGCSafeTypeHandle().AsTAddr()); + + // Save basic object info. + pObjectData->objSize = objPtr->GetSize(); + pObjectData->objOffsetToVars = dac_cast<TADDR>((objPtr)->GetData()) - dac_cast<TADDR>(objPtr); + + TypeHandleToExpandedTypeInfo(AllBoxed, vmAppDomain, vmTypeHandle, &(pObjectData->objTypeData)); + + // If this is a string object, set the type to ELEMENT_TYPE_STRING. + if (objPtr->GetGCSafeMethodTable() == g_pStringClass) + { + pObjectData->objTypeData.elementType = ELEMENT_TYPE_STRING; + if(pObjectData->objSize < MIN_OBJECT_SIZE) + { + pObjectData->objSize = PtrAlign(pObjectData->objSize); + } + } +} // DacDbiInterfaceImpl::InitObjectData + +// DAC/DBI API + +// Get object information for a TypedByRef object (System.TypedReference). + +// These are objects that contain a managed pointer to a location and the type of the value at that location. +// They are most commonly used for varargs but also may be used for parameters and locals. They are +// stack-allocated. They provide a means for adding dynamic type information to a value type, whereas boxing +// provides only static type information. This means they can be passed as reference parameters to +// polymorphic methods that don't statically restrict the type of arguments they can receive. + +// Although they are represented simply as an address, unlike other object references, they don't point +// directly to the object. Instead, there is an extra level of indirection. The reference points to a struct +// that contains the address of the object, so we need to treat them differently. They have their own +// CorElementType (ELEMENT_TYPE_TYPEDBYREF) which makes it possible to identify this special case. + +// Example: +// static int AddABunchOfInts (__arglist) +// { +// int result = 0; +// +// System.ArgIterator iter = new System.ArgIterator (__arglist); +// int argCount = iter.GetRemainingCount(); +// +// for (int i = 0; i < argCount; i++) +// { +// System.TypedReference typedRef = iter.GetNextArg(); +// result += (int)TypedReference.ToObject(typedRef); +// } +// +// return result; +// } +// +// static int Main (string[] args) +// { +// int result = AddABunchOfInts (__arglist (2, 3, 4)); +// Console.WriteLine ("Answer: {0}", result); +// +// if (result != 9) +// return 1; +// +// return 0; +// } + +// Initializes the objRef and typedByRefType fields of pObjectData (type info for the referent). +void DacDbiInterfaceImpl::GetTypedByRefInfo(CORDB_ADDRESS pTypedByRef, + VMPTR_AppDomain vmAppDomain, + DebuggerIPCE_ObjectData * pObjectData) +{ + DD_ENTER_MAY_THROW; + + // pTypedByRef is really the address of a TypedByRef struct rather than of a normal object. + // The data field of the TypedByRef struct is the actual object ref. + PTR_TypedByRef refAddr = PTR_TypedByRef(TADDR(pTypedByRef)); + + _ASSERTE(refAddr != NULL); + _ASSERTE(pObjectData != NULL); + + // The type of the referent is in the type field of the TypedByRef. We need to initialize the object + // data type information. + TypeHandleToBasicTypeInfo(refAddr->type, + &(pObjectData->typedByrefInfo.typedByrefType), + vmAppDomain.GetDacPtr()); + + // The reference to the object is in the data field of the TypedByRef. + CORDB_ADDRESS tempRef = dac_cast<TADDR>(refAddr->data); + pObjectData->objRef = CORDB_ADDRESS_TO_PTR(tempRef); + + LOG((LF_CORDB, LL_INFO10000, "D::GASOI: sending REFANY result: " + "ref=0x%08x, cls=0x%08x, mod=0x%p\n", + pObjectData->objRef, + pObjectData->typedByrefType.metadataToken, + pObjectData->typedByrefType.vmDomainFile.GetDacPtr())); +} // DacDbiInterfaceImpl::GetTypedByRefInfo + +// Get the string data associated withn obj and put it into the pointers +// DAC/DBI API +// Get the string length and offset to string base for a string object +void DacDbiInterfaceImpl::GetStringData(CORDB_ADDRESS objectAddress, DebuggerIPCE_ObjectData * pObjectData) +{ + DD_ENTER_MAY_THROW; + + PTR_Object objPtr = PTR_Object(TADDR(objectAddress)); + LOG((LF_CORDB, LL_INFO10000, "D::GOI: The referent is a string.\n")); + + if (objPtr->GetGCSafeMethodTable() != g_pStringClass) + { + ThrowHR(CORDBG_E_TARGET_INCONSISTENT); + } + + PTR_StringObject pStrObj = dac_cast<PTR_StringObject>(objPtr); + + _ASSERTE(pStrObj != NULL); + pObjectData->stringInfo.length = pStrObj->GetStringLength(); + pObjectData->stringInfo.offsetToStringBase = (UINT_PTR) pStrObj->GetBufferOffset(); + +} // DacDbiInterfaceImpl::GetStringData + + +// DAC/DBI API +// Get information for an array type referent of an objRef, including rank, upper and lower +// bounds, element size and type, and the number of elements. +void DacDbiInterfaceImpl::GetArrayData(CORDB_ADDRESS objectAddress, DebuggerIPCE_ObjectData * pObjectData) +{ + DD_ENTER_MAY_THROW; + + PTR_Object objPtr = PTR_Object(TADDR(objectAddress)); + PTR_MethodTable pMT = objPtr->GetGCSafeMethodTable(); + + if (!objPtr->GetGCSafeTypeHandle().IsArray()) + { + LOG((LF_CORDB, LL_INFO10000, + "D::GASOI: object should be an array.\n")); + + pObjectData->objRefBad = true; + } + else + { + PTR_ArrayBase arrPtr = dac_cast<PTR_ArrayBase>(objPtr); + + // this is also returned in the type information for the array - we return both for sanity checking... + pObjectData->arrayInfo.rank = arrPtr->GetRank(); + pObjectData->arrayInfo.componentCount = arrPtr->GetNumComponents(); + pObjectData->arrayInfo.offsetToArrayBase = arrPtr->GetDataPtrOffset(pMT); + + if (arrPtr->IsMultiDimArray()) + { + pObjectData->arrayInfo.offsetToUpperBounds = SIZE_T(arrPtr->GetBoundsOffset(pMT)); + + pObjectData->arrayInfo.offsetToLowerBounds = SIZE_T(arrPtr->GetLowerBoundsOffset(pMT)); + } + else + { + pObjectData->arrayInfo.offsetToUpperBounds = 0; + pObjectData->arrayInfo.offsetToLowerBounds = 0; + } + + pObjectData->arrayInfo.elementSize = arrPtr->GetComponentSize(); + + LOG((LF_CORDB, LL_INFO10000, "D::GOI: array info: " + "baseOff=%d, lowerOff=%d, upperOff=%d, cnt=%d, rank=%d, rank (2) = %d," + "eleSize=%d, eleType=0x%02x\n", + pObjectData->arrayInfo.offsetToArrayBase, + pObjectData->arrayInfo.offsetToLowerBounds, + pObjectData->arrayInfo.offsetToUpperBounds, + pObjectData->arrayInfo.componentCount, + pObjectData->arrayInfo.rank, + pObjectData->objTypeData.ArrayTypeData.arrayRank, + pObjectData->arrayInfo.elementSize, + pObjectData->objTypeData.ArrayTypeData.arrayTypeArg.elementType)); + } +} // DacDbiInterfaceImpl::GetArrayData + +// DAC/DBI API: Get information about an object for which we have a reference, including the object size and +// type information. +void DacDbiInterfaceImpl::GetBasicObjectInfo(CORDB_ADDRESS objectAddress, + CorElementType type, + VMPTR_AppDomain vmAppDomain, + DebuggerIPCE_ObjectData * pObjectData) +{ + DD_ENTER_MAY_THROW; + + PTR_Object objPtr = PTR_Object(TADDR(objectAddress)); + pObjectData->objRefBad = CheckRef(objPtr); + if (pObjectData->objRefBad != true) + { + // initialize object type, size, offset information. Note: We may have a different element type + // after this. For example, we may start with E_T_CLASS but return with something more specific. + InitObjectData (objPtr, vmAppDomain, pObjectData); + } +} // DacDbiInterfaceImpl::GetBasicObjectInfo + +// This is the data passed to EnumerateBlockingObjectsCallback below +struct BlockingObjectUserDataWrapper +{ + CALLBACK_DATA pUserData; + IDacDbiInterface::FP_BLOCKINGOBJECT_ENUMERATION_CALLBACK fpCallback; +}; + +// The callback helper used by EnumerateBlockingObjects below, this +// callback in turn invokes the user's callback with the right arguments +void EnumerateBlockingObjectsCallback(PTR_DebugBlockingItem obj, VOID* pUserData) +{ + BlockingObjectUserDataWrapper* wrapper = (BlockingObjectUserDataWrapper*)pUserData; + DacBlockingObject dacObj; + + // init to an arbitrary value to avoid mac compiler error about unintialized use + // it will be correctly set in the switch and is never used with only this init here + dacObj.blockingReason = DacBlockReason_MonitorCriticalSection; + + dacObj.vmBlockingObject.SetDacTargetPtr(dac_cast<TADDR>(OBJECTREFToObject(obj->pMonitor->GetOwningObject()))); + dacObj.dwTimeout = obj->dwTimeout; + dacObj.vmAppDomain.SetDacTargetPtr(dac_cast<TADDR>(obj->pAppDomain)); + switch(obj->type) + { + case DebugBlock_MonitorCriticalSection: + dacObj.blockingReason = DacBlockReason_MonitorCriticalSection; + break; + case DebugBlock_MonitorEvent: + dacObj.blockingReason = DacBlockReason_MonitorEvent; + break; + default: + _ASSERTE(!"obj->type has an invalid value"); + return; + } + + wrapper->fpCallback(dacObj, wrapper->pUserData); +} + +// DAC/DBI API: +// Enumerate all monitors blocking a thread +void DacDbiInterfaceImpl::EnumerateBlockingObjects(VMPTR_Thread vmThread, + FP_BLOCKINGOBJECT_ENUMERATION_CALLBACK fpCallback, + CALLBACK_DATA pUserData) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + _ASSERTE(pThread != NULL); + + BlockingObjectUserDataWrapper wrapper; + wrapper.fpCallback = fpCallback; + wrapper.pUserData = pUserData; + + pThread->DebugBlockingInfo.VisitBlockingItems((DebugBlockingItemVisitor)EnumerateBlockingObjectsCallback, + (VOID*)&wrapper); +} + +// DAC/DBI API: +// Returns the thread which owns the monitor lock on an object and the acquisition count +MonitorLockInfo DacDbiInterfaceImpl::GetThreadOwningMonitorLock(VMPTR_Object vmObject) +{ + DD_ENTER_MAY_THROW; + MonitorLockInfo info; + info.lockOwner = VMPTR_Thread::NullPtr(); + info.acquisitionCount = 0; + + Object* pObj = vmObject.GetDacPtr(); + DWORD threadId; + DWORD acquisitionCount; + if(!pObj->GetHeader()->GetThreadOwningMonitorLock(&threadId, &acquisitionCount)) + { + return info; + } + + Thread *pThread = ThreadStore::GetThreadList(NULL); + while (pThread != NULL) + { + if(pThread->GetThreadId() == threadId) + { + info.lockOwner.SetDacTargetPtr(PTR_HOST_TO_TADDR(pThread)); + info.acquisitionCount = acquisitionCount; + return info; + } + pThread = ThreadStore::GetThreadList(pThread); + } + _ASSERTE(!"A thread should have been found"); + return info; +} + +// The data passed to EnumerateThreadsCallback below +struct ThreadUserDataWrapper +{ + CALLBACK_DATA pUserData; + IDacDbiInterface::FP_THREAD_ENUMERATION_CALLBACK fpCallback; +}; + +// The callback helper used for EnumerateMonitorEventWaitList below. This callback +// invokes the user's callback with the correct arguments. +void EnumerateThreadsCallback(PTR_Thread pThread, VOID* pUserData) +{ + ThreadUserDataWrapper* wrapper = (ThreadUserDataWrapper*)pUserData; + VMPTR_Thread vmThread = VMPTR_Thread::NullPtr(); + vmThread.SetDacTargetPtr(dac_cast<TADDR>(pThread)); + wrapper->fpCallback(vmThread, wrapper->pUserData); +} + +// DAC/DBI API: +// Enumerate all threads waiting on the monitor event for an object +void DacDbiInterfaceImpl::EnumerateMonitorEventWaitList(VMPTR_Object vmObject, + FP_THREAD_ENUMERATION_CALLBACK fpCallback, + CALLBACK_DATA pUserData) +{ + DD_ENTER_MAY_THROW; + + Object* pObj = vmObject.GetDacPtr(); + SyncBlock* psb = pObj->PassiveGetSyncBlock(); + + // no sync block means no wait list + if(psb == NULL) + return; + + ThreadUserDataWrapper wrapper; + wrapper.fpCallback = fpCallback; + wrapper.pUserData = pUserData; + ThreadQueue::EnumerateThreads(psb, (FP_TQ_THREAD_ENUMERATION_CALLBACK)EnumerateThreadsCallback, (VOID*) &wrapper); +} + + +bool DacDbiInterfaceImpl::AreGCStructuresValid() +{ + return true; +} + +HeapData::HeapData() + : YoungestGenPtr(0), YoungestGenLimit(0), Gen0Start(0), Gen0End(0), SegmentCount(0), Segments(0) +{ +} + +HeapData::~HeapData() +{ + if (Segments) + delete [] Segments; +} + +LinearReadCache::LinearReadCache() + : mCurrPageStart(0), mPageSize(0), mCurrPageSize(0), mPage(0) +{ + SYSTEM_INFO si; + GetSystemInfo(&si); + + mPageSize = si.dwPageSize; + mPage = new (nothrow) BYTE[mPageSize]; +} + +LinearReadCache::~LinearReadCache() +{ + if (mPage) + delete [] mPage; +} + +bool LinearReadCache::MoveToPage(CORDB_ADDRESS addr) +{ + mCurrPageStart = addr - (addr % mPageSize); + HRESULT hr = g_dacImpl->m_pTarget->ReadVirtual(mCurrPageStart, mPage, mPageSize, &mCurrPageSize); + + if (hr != S_OK) + { + mCurrPageStart = 0; + mCurrPageSize = 0; + return false; + } + + return true; +} + + +CORDB_ADDRESS DacHeapWalker::HeapStart = 0; +CORDB_ADDRESS DacHeapWalker::HeapEnd = ~0; + +DacHeapWalker::DacHeapWalker() + : mThreadCount(0), mAllocInfo(0), mHeapCount(0), mHeaps(0), + mCurrObj(0), mCurrSize(0), mCurrMT(0), + mCurrHeap(0), mCurrSeg(0), mStart((TADDR)HeapStart), mEnd((TADDR)HeapEnd) +{ +} + +DacHeapWalker::~DacHeapWalker() +{ + if (mAllocInfo) + delete [] mAllocInfo; + + if (mHeaps) + delete [] mHeaps; +} + +SegmentData *DacHeapWalker::FindSegment(CORDB_ADDRESS obj) +{ + for (size_t i = 0; i < mHeapCount; ++i) + for (size_t j = 0; j < mHeaps[i].SegmentCount; ++j) + if (mHeaps[i].Segments[j].Start <= obj && obj <= mHeaps[i].Segments[j].End) + return &mHeaps[i].Segments[j]; + + return NULL; +} + +HRESULT DacHeapWalker::Next(CORDB_ADDRESS *pValue, CORDB_ADDRESS *pMT, ULONG64 *pSize) +{ + if (!HasMoreObjects()) + return E_FAIL; + + if (pValue) + *pValue = mCurrObj; + + if (pMT) + *pMT = (CORDB_ADDRESS)mCurrMT; + + if (pSize) + *pSize = (ULONG64)mCurrSize; + + HRESULT hr = MoveToNextObject(); + return FAILED(hr) ? hr : S_OK; +} + + + +HRESULT DacHeapWalker::MoveToNextObject() +{ + do + { + // Move to the next object + mCurrObj += mCurrSize; + + // Check to see if we are in the correct bounds. + if (mHeaps[mCurrHeap].Gen0Start <= mCurrObj && mHeaps[mCurrHeap].Gen0End > mCurrObj) + CheckAllocAndSegmentRange(); + + // Check to see if we've moved off the end of a segment + if (mCurrObj >= mHeaps[mCurrHeap].Segments[mCurrSeg].End || mCurrObj > mEnd) + { + HRESULT hr = NextSegment(); + if (FAILED(hr) || hr == S_FALSE) + return hr; + } + + // Get the method table pointer + if (!mCache.ReadMT(mCurrObj, &mCurrMT)) + return E_FAIL; + + if (!GetSize(mCurrMT, mCurrSize)) + return E_FAIL; + } while (mCurrObj < mStart); + + _ASSERTE(mStart <= mCurrObj && mCurrObj <= mEnd); + return S_OK; +} + +bool DacHeapWalker::GetSize(TADDR tMT, size_t &size) +{ + // With heap corruption, it's entierly possible that the MethodTable + // we get is bad. This could cause exceptions, which we will catch + // and return false. This causes the heapwalker to move to the next + // segment. + bool ret = true; + EX_TRY + { + MethodTable *mt = PTR_MethodTable(tMT); + size_t cs = mt->GetComponentSize(); + + if (cs) + { + DWORD tmp = 0; + if (mCache.Read(mCurrObj+sizeof(TADDR), &tmp)) + cs *= tmp; + else + ret = false; + } + + size = mt->GetBaseSize() + cs; + + // The size is not guaranteed to be aligned, we have to + // do that ourself. + if (mHeaps[mCurrHeap].Segments[mCurrSeg].Generation == 3) + size = AlignLarge(size); + else + size = Align(size); + } + EX_CATCH + { + ret = false; + } + EX_END_CATCH(SwallowAllExceptions) + + return ret; +} + + +HRESULT DacHeapWalker::NextSegment() +{ + mCurrObj = 0; + mCurrMT = 0; + mCurrSize = 0; + + do + { + mCurrSeg++; + while (mCurrSeg >= mHeaps[mCurrHeap].SegmentCount) + { + mCurrSeg = 0; + mCurrHeap++; + + if (mCurrHeap >= mHeapCount) + { + return S_FALSE; + } + } + + mCurrObj = mHeaps[mCurrHeap].Segments[mCurrSeg].Start; + + if (mHeaps[mCurrHeap].Gen0Start <= mCurrObj && mHeaps[mCurrHeap].Gen0End > mCurrObj) + CheckAllocAndSegmentRange(); + + if (!mCache.ReadMT(mCurrObj, &mCurrMT)) + { + return E_FAIL; + } + + if (!GetSize(mCurrMT, mCurrSize)) + { + return E_FAIL; + } + } while((mHeaps[mCurrHeap].Segments[mCurrSeg].Start > mEnd) || (mHeaps[mCurrHeap].Segments[mCurrSeg].End < mStart)); + + return S_OK; +} + +void DacHeapWalker::CheckAllocAndSegmentRange() +{ + const size_t MinObjSize = sizeof(TADDR)*3; + + for (int i = 0; i < mThreadCount; ++i) + if (mCurrObj == mAllocInfo[i].Ptr) + { + mCurrObj = mAllocInfo[i].Limit + Align(MinObjSize); + break; + } + + if (mCurrObj == mHeaps[mCurrHeap].YoungestGenPtr) + { + mCurrObj = mHeaps[mCurrHeap].YoungestGenLimit + Align(MinObjSize); + } +} + +HRESULT DacHeapWalker::Init(CORDB_ADDRESS start, CORDB_ADDRESS end) +{ + // Collect information about the allocation contexts in the process. + ThreadStore* threadStore = ThreadStore::s_pThreadStore; + if (threadStore != NULL) + { + int count = (int)threadStore->ThreadCountInEE(); + mAllocInfo = new (nothrow) AllocInfo[count]; + if (mAllocInfo == NULL) + return E_OUTOFMEMORY; + + Thread *thread = NULL; + int j = 0; + for (int i = 0; i < count; ++i) + { + // The thread or allocation context being null is troubling, but not fatal. + // We may have stopped the process where the thread list or thread's alloc + // context was in an inconsistent state. We will simply skip over affected + // segments during the heap walk if we encounter problems due to this. + thread = ThreadStore::GetThreadList(thread); + if (thread == NULL) + continue; + + alloc_context *ctx = thread->GetAllocContext(); + if (ctx == NULL) + continue; + + if ((CORDB_ADDRESS)ctx->alloc_ptr != NULL) + { + mAllocInfo[j].Ptr = (CORDB_ADDRESS)ctx->alloc_ptr; + mAllocInfo[j].Limit = (CORDB_ADDRESS)ctx->alloc_limit; + j++; + } + } + + mThreadCount = j; + } + +#ifdef FEATURE_SVR_GC + HRESULT hr = GCHeap::IsServerHeap() ? InitHeapDataSvr(mHeaps, mHeapCount) : InitHeapDataWks(mHeaps, mHeapCount); +#else + HRESULT hr = InitHeapDataWks(mHeaps, mHeapCount); +#endif + + // Set up mCurrObj/mCurrMT. + if (SUCCEEDED(hr)) + hr = Reset(start, end); + + // Collect information about GC heaps + return hr; +} + +HRESULT DacHeapWalker::Reset(CORDB_ADDRESS start, CORDB_ADDRESS end) +{ + _ASSERTE(mHeaps); + _ASSERTE(mHeapCount > 0); + _ASSERTE(mHeaps[0].Segments); + _ASSERTE(mHeaps[0].SegmentCount > 0); + + mStart = start; + mEnd = end; + + // Set up first object + mCurrObj = mHeaps[0].Segments[0].Start; + mCurrMT = 0; + mCurrSize = 0; + mCurrHeap = 0; + mCurrSeg = 0; + + if (!mCache.ReadMT(mCurrObj, &mCurrMT)) + return E_FAIL; + + if (!GetSize(mCurrMT, mCurrSize)) + return E_FAIL; + + if (mCurrObj < mStart || mCurrObj > mEnd) + MoveToNextObject(); + + return S_OK; +} + +HRESULT DacHeapWalker::ListNearObjects(CORDB_ADDRESS obj, CORDB_ADDRESS *pPrev, CORDB_ADDRESS *pContaining, CORDB_ADDRESS *pNext) +{ + SegmentData *seg = FindSegment(obj); + + if (seg == NULL) + return E_FAIL; + + HRESULT hr = Reset(seg->Start, seg->End); + if (SUCCEEDED(hr)) + { + CORDB_ADDRESS prev = 0; + CORDB_ADDRESS curr = 0; + ULONG64 size = 0; + bool found = false; + + while (!found && HasMoreObjects()) + { + prev = curr; + hr = Next(&curr, NULL, &size); + if (FAILED(hr)) + break; + + if (obj >= curr && obj < curr + size) + found = true; + } + + if (found) + { + if (pPrev) + *pPrev = prev; + + if (pContaining) + *pContaining = curr; + + if (pNext) + { + if (HasMoreObjects()) + { + hr = Next(&curr, NULL, NULL); + if (SUCCEEDED(hr)) + *pNext = curr; + } + else + { + *pNext = 0; + } + } + + hr = S_OK; + } + else if (SUCCEEDED(hr)) + { + hr = E_FAIL; + } + } + + return hr; +} + +#include "gceewks.cpp" +HRESULT DacHeapWalker::InitHeapDataWks(HeapData *&pHeaps, size_t &pCount) +{ + // Scrape basic heap details + pCount = 1; + pHeaps = new (nothrow) HeapData[1]; + if (pHeaps == NULL) + return E_OUTOFMEMORY; + + pHeaps[0].YoungestGenPtr = (CORDB_ADDRESS)WKS::generation_table[0].allocation_context.alloc_ptr; + pHeaps[0].YoungestGenLimit = (CORDB_ADDRESS)WKS::generation_table[0].allocation_context.alloc_limit; + + pHeaps[0].Gen0Start = (CORDB_ADDRESS)WKS::generation_table[0].allocation_start; + pHeaps[0].Gen0End = (CORDB_ADDRESS)WKS::gc_heap::alloc_allocated.GetAddr(); + pHeaps[0].Gen1Start = (CORDB_ADDRESS)WKS::generation_table[1].allocation_start; + + // Segments + int count = GetSegmentCount(WKS::generation_table[NUMBERGENERATIONS-1].start_segment); + count += GetSegmentCount(WKS::generation_table[NUMBERGENERATIONS-2].start_segment); + + pHeaps[0].SegmentCount = count; + pHeaps[0].Segments = new (nothrow) SegmentData[count]; + if (pHeaps[0].Segments == NULL) + return E_OUTOFMEMORY; + + // Small object heap segments + WKS::heap_segment *seg = WKS::generation_table[NUMBERGENERATIONS-2].start_segment; + int i = 0; + for (; seg && (i < count); ++i) + { + pHeaps[0].Segments[i].Start = (CORDB_ADDRESS)seg->mem; + if (seg == WKS::gc_heap::ephemeral_heap_segment) + { + pHeaps[0].Segments[i].End = (CORDB_ADDRESS)WKS::gc_heap::alloc_allocated.GetAddr(); + pHeaps[0].Segments[i].Generation = 1; + pHeaps[0].EphemeralSegment = i; + } + else + { + pHeaps[0].Segments[i].End = (CORDB_ADDRESS)seg->allocated; + pHeaps[0].Segments[i].Generation = 2; + } + + seg = seg->next; + } + + // Large object heap segments + seg = WKS::generation_table[NUMBERGENERATIONS-1].start_segment; + for (; seg && (i < count); ++i) + { + pHeaps[0].Segments[i].Generation = 3; + pHeaps[0].Segments[i].Start = (CORDB_ADDRESS)seg->mem; + pHeaps[0].Segments[i].End = (CORDB_ADDRESS)seg->allocated; + + seg = seg->next; + } + + return S_OK; +} + + HRESULT DacDbiInterfaceImpl::CreateHeapWalk(IDacDbiInterface::HeapWalkHandle *pHandle) +{ + DD_ENTER_MAY_THROW; + + DacHeapWalker *data = new (nothrow) DacHeapWalker; + if (data == NULL) + return E_OUTOFMEMORY; + + HRESULT hr = data->Init(); + if (SUCCEEDED(hr)) + *pHandle = reinterpret_cast<HeapWalkHandle>(data); + else + delete data; + + return hr; +} + +void DacDbiInterfaceImpl::DeleteHeapWalk(HeapWalkHandle handle) +{ + DD_ENTER_MAY_THROW; + + DacHeapWalker *data = reinterpret_cast<DacHeapWalker*>(handle); + if (data) + delete data; +} + +HRESULT DacDbiInterfaceImpl::WalkHeap(HeapWalkHandle handle, + ULONG count, + OUT COR_HEAPOBJECT * objects, + OUT ULONG *fetched) +{ + DD_ENTER_MAY_THROW; + if (fetched == NULL) + return E_INVALIDARG; + + DacHeapWalker *walk = reinterpret_cast<DacHeapWalker*>(handle); + *fetched = 0; + + if (!walk->HasMoreObjects()) + return S_FALSE; + + CORDB_ADDRESS freeMT = (CORDB_ADDRESS)g_pFreeObjectMethodTable.GetAddr(); + + HRESULT hr = S_OK; + CORDB_ADDRESS addr, mt; + ULONG64 size; + + ULONG i = 0; + while (i < count && walk->HasMoreObjects()) + { + hr = walk->Next(&addr, &mt, &size); + + if (FAILED(hr)) + break; + + if (mt != freeMT) + { + objects[i].address = addr; + objects[i].type.token1 = mt; + objects[i].type.token2 = NULL; + objects[i].size = size; + i++; + } + } + + if (SUCCEEDED(hr)) + hr = (i < count) ? S_FALSE : S_OK; + + *fetched = i; + return hr; +} + + + +HRESULT DacDbiInterfaceImpl::GetHeapSegments(OUT DacDbiArrayList<COR_SEGMENT> *pSegments) +{ + DD_ENTER_MAY_THROW; + + + size_t heapCount = 0; + HeapData *heaps = 0; + +#ifdef FEATURE_SVR_GC + HRESULT hr = GCHeap::IsServerHeap() ? DacHeapWalker::InitHeapDataSvr(heaps, heapCount) : DacHeapWalker::InitHeapDataWks(heaps, heapCount); +#else + HRESULT hr = DacHeapWalker::InitHeapDataWks(heaps, heapCount); +#endif + + NewArrayHolder<HeapData> _heapHolder = heaps; + + // Count the number of segments to know how much to allocate. + int total = 0; + for (size_t i = 0; i < heapCount; ++i) + { + // SegmentCount is +1 due to the ephemeral segment containing more than one + // generation (Gen1 + Gen0, and sometimes part of Gen2). + total += (int)heaps[i].SegmentCount + 1; + + // It's possible that part of Gen2 lives on the ephemeral segment. If so, + // we need to add one more to the output. + const size_t eph = heaps[i].EphemeralSegment; + _ASSERTE(eph < heaps[i].SegmentCount); + if (heaps[i].Segments[eph].Start != heaps[i].Gen1Start) + total++; + } + + pSegments->Alloc(total); + + // Now walk all segments and write them to the array. + int curr = 0; + for (size_t i = 0; i < heapCount; ++i) + { + // Generation 0 is not in the segment list. + _ASSERTE(curr < total); + { + COR_SEGMENT &seg = (*pSegments)[curr++]; + seg.start = heaps[i].Gen0Start; + seg.end = heaps[i].Gen0End; + seg.type = CorDebug_Gen0; + seg.heap = (ULONG)i; + } + + for (size_t j = 0; j < heaps[i].SegmentCount; ++j) + { + if (heaps[i].Segments[j].Generation == 1) + { + // This is the ephemeral segment. We have already written Gen0, + // now write Gen1. + _ASSERTE(heaps[i].Segments[j].Start <= heaps[i].Gen1Start); + _ASSERTE(heaps[i].Segments[j].End > heaps[i].Gen1Start); + + { + _ASSERTE(curr < total); + COR_SEGMENT &seg = (*pSegments)[curr++]; + seg.start = heaps[i].Gen1Start; + seg.end = heaps[i].Gen0Start; + seg.type = CorDebug_Gen1; + seg.heap = (ULONG)i; + } + + // It's possible for Gen2 to take up a portion of the ephemeral segment. + // We test for that here. + if (heaps[i].Segments[j].Start != heaps[i].Gen1Start) + { + _ASSERTE(curr < total); + COR_SEGMENT &seg = (*pSegments)[curr++]; + seg.start = heaps[i].Segments[j].Start; + seg.end = heaps[i].Gen1Start; + seg.type = CorDebug_Gen2; + seg.heap = (ULONG)i; + } + } + else + { + // Otherwise, we have a gen2 or gen3 (LOH) segment + _ASSERTE(curr < total); + COR_SEGMENT &seg = (*pSegments)[curr++]; + seg.start = heaps[i].Segments[j].Start; + seg.end = heaps[i].Segments[j].End; + + _ASSERTE(heaps[i].Segments[j].Generation <= CorDebug_LOH); + seg.type = (CorDebugGenerationTypes)heaps[i].Segments[j].Generation; + seg.heap = (ULONG)i; + } + } + } + + _ASSERTE(total == curr); + return hr; +} + +bool DacDbiInterfaceImpl::IsValidObject(CORDB_ADDRESS addr) +{ + DD_ENTER_MAY_THROW; + + bool isValid = false; + EX_TRY + { + PTR_Object obj(TO_TADDR(addr)); + + PTR_MethodTable mt = obj->GetMethodTable(); + PTR_EEClass cls = mt->GetClass(); + + if (mt == cls->GetMethodTable()) + isValid = true; + else if (!mt->IsCanonicalMethodTable()) + isValid = cls->GetMethodTable()->GetClass() == cls; + } + EX_CATCH + { + isValid = false; + } + EX_END_CATCH(SwallowAllExceptions) + + return isValid; +} + +bool DacDbiInterfaceImpl::GetAppDomainForObject(CORDB_ADDRESS addr, OUT VMPTR_AppDomain * pAppDomain, + OUT VMPTR_Module *pModule, OUT VMPTR_DomainFile *pDomainFile) +{ + DD_ENTER_MAY_THROW; + + PTR_Object obj(TO_TADDR(addr)); + MethodTable *mt = obj->GetMethodTable(); + + PTR_Module module = mt->GetModule(); + PTR_Assembly assembly = module->GetAssembly(); + BaseDomain *baseDomain = assembly->GetDomain(); + + if (baseDomain->IsSharedDomain()) + { + pModule->SetDacTargetPtr(PTR_HOST_TO_TADDR(module)); + *pAppDomain = VMPTR_AppDomain::NullPtr(); + *pDomainFile = VMPTR_DomainFile::NullPtr(); + } + else if (baseDomain->IsAppDomain()) + { + pAppDomain->SetDacTargetPtr(PTR_HOST_TO_TADDR(baseDomain->AsAppDomain())); + pModule->SetDacTargetPtr(PTR_HOST_TO_TADDR(module)); + pDomainFile->SetDacTargetPtr(PTR_HOST_TO_TADDR(module->GetDomainFile(baseDomain->AsAppDomain()))); + } + else + { + return false; + } + + return true; +} + +HRESULT DacDbiInterfaceImpl::CreateRefWalk(OUT RefWalkHandle * pHandle, BOOL walkStacks, BOOL walkFQ, UINT32 handleWalkMask) +{ + DD_ENTER_MAY_THROW; + + DacRefWalker *walker = new (nothrow) DacRefWalker(this, walkStacks, walkFQ, handleWalkMask); + + if (walker == NULL) + return E_OUTOFMEMORY; + + HRESULT hr = walker->Init(); + if (FAILED(hr)) + { + delete walker; + } + else + { + *pHandle = reinterpret_cast<RefWalkHandle>(walker); + } + + return hr; +} + + +void DacDbiInterfaceImpl::DeleteRefWalk(IN RefWalkHandle handle) +{ + DD_ENTER_MAY_THROW; + + DacRefWalker *walker = reinterpret_cast<DacRefWalker*>(handle); + + if (walker) + delete walker; +} + + +HRESULT DacDbiInterfaceImpl::WalkRefs(RefWalkHandle handle, ULONG count, OUT DacGcReference * objects, OUT ULONG *pFetched) +{ + if (objects == NULL || pFetched == NULL) + return E_POINTER; + + DD_ENTER_MAY_THROW; + + DacRefWalker *walker = reinterpret_cast<DacRefWalker*>(handle); + if (!walker) + return E_INVALIDARG; + + return walker->Next(count, objects, pFetched); +} + +HRESULT DacDbiInterfaceImpl::GetTypeID(CORDB_ADDRESS dbgObj, COR_TYPEID *pID) +{ + DD_ENTER_MAY_THROW; + + TADDR obj[3]; + ULONG32 read = 0; + HRESULT hr = g_dacImpl->m_pTarget->ReadVirtual(dbgObj, (BYTE*)obj, sizeof(obj), &read); + if (FAILED(hr)) + return hr; + + pID->token1 = (UINT64)(obj[0] & ~1); + pID->token2 = 0; + + return hr; +} + +HRESULT DacDbiInterfaceImpl::GetObjectFields(COR_TYPEID id, ULONG32 celt, COR_FIELD *layout, ULONG32 *pceltFetched) +{ + if (layout == NULL || pceltFetched == NULL) + return E_POINTER; + + if (id.token1 == 0) + return CORDBG_E_CLASS_NOT_LOADED; + + DD_ENTER_MAY_THROW; + + HRESULT hr = S_OK; + + TypeHandle typeHandle = TypeHandle::FromPtr(TO_TADDR(id.token1)); + + if (typeHandle.IsTypeDesc()) + return E_INVALIDARG; + + ApproxFieldDescIterator fieldDescIterator(typeHandle.AsMethodTable(), ApproxFieldDescIterator::INSTANCE_FIELDS); + + ULONG32 cFields = fieldDescIterator.Count(); + + // Handle case where user only wanted to know the number of fields. + if (layout == NULL) + { + *pceltFetched = cFields; + return S_FALSE; + } + + if (celt < cFields) + { + cFields = celt; + + // we are returning less than the total + hr = S_FALSE; + } + + // This must be non-null due to check at beginning of function. + *pceltFetched = celt; + + CorElementType componentType = typeHandle.AsMethodTable()->GetInternalCorElementType(); + BOOL fReferenceType = CorTypeInfo::IsObjRef_NoThrow(componentType); + for (ULONG32 i = 0; i < cFields; ++i) + { + FieldDesc *pField = fieldDescIterator.Next(); + layout[i].token = pField->GetMemberDef(); + layout[i].offset = (ULONG32)pField->GetOffset() + (fReferenceType ? Object::GetOffsetOfFirstField() : 0); + + TypeHandle fieldHandle = pField->LookupFieldTypeHandle(); + + if (fieldHandle.IsNull()) + { + layout[i].id.token1 = 0; + layout[i].id.token2 = 0; + layout[i].fieldType = (CorElementType)0; + } + else + { + PTR_MethodTable mt = fieldHandle.GetMethodTable(); + layout[i].fieldType = mt->GetInternalCorElementType(); + layout[i].id.token1 = (ULONG64)mt.GetAddr(); + + if (!mt->IsArray()) + { + layout[i].id.token2 = 0; + } + else + { + TypeHandle hnd = mt->GetApproxArrayElementTypeHandle(); + PTR_MethodTable cmt = hnd.GetMethodTable(); + layout[i].id.token2 = (ULONG64)cmt.GetAddr(); + } + } + } + + return hr; +} + + +HRESULT DacDbiInterfaceImpl::GetTypeLayout(COR_TYPEID id, COR_TYPE_LAYOUT *pLayout) +{ + if (pLayout == NULL) + return E_POINTER; + + if (id.token1 == 0) + return CORDBG_E_CLASS_NOT_LOADED; + + DD_ENTER_MAY_THROW; + + PTR_MethodTable mt = PTR_MethodTable(TO_TADDR(id.token1)); + PTR_MethodTable parentMT = mt->GetParentMethodTable(); + + COR_TYPEID parent = {parentMT.GetAddr(), 0}; + pLayout->parentID = parent; + + DWORD size = mt->GetBaseSize(); + ApproxFieldDescIterator fieldDescIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS); + + pLayout->objectSize = size; + pLayout->numFields = fieldDescIterator.Count(); + + // Get type + CorElementType componentType = mt->IsString() ? ELEMENT_TYPE_STRING : mt->GetInternalCorElementType(); + pLayout->type = componentType; + pLayout->boxOffset = CorTypeInfo::IsObjRef_NoThrow(componentType) ? 0 : sizeof(TADDR); + + return S_OK; +} + +HRESULT DacDbiInterfaceImpl::GetArrayLayout(COR_TYPEID id, COR_ARRAY_LAYOUT *pLayout) +{ + if (pLayout == NULL) + return E_POINTER; + + if (id.token1 == 0) + return CORDBG_E_CLASS_NOT_LOADED; + + DD_ENTER_MAY_THROW; + + PTR_MethodTable mt = PTR_MethodTable(TO_TADDR(id.token1)); + + if (!mt->IsStringOrArray()) + return E_INVALIDARG; + + if (mt->IsString()) + { + COR_TYPEID token; + token.token1 = MscorlibBinder::GetElementType(ELEMENT_TYPE_CHAR).GetAddr(); + token.token2 = 0; + + pLayout->componentID = token; + + pLayout->rankSize = 4; + pLayout->numRanks = 1; + pLayout->rankOffset = sizeof(TADDR); + pLayout->firstElementOffset = sizeof(TADDR) + 4; + pLayout->countOffset = sizeof(TADDR); + pLayout->componentType = ELEMENT_TYPE_CHAR; + pLayout->elementSize = 2; + } + else + { + DWORD ranks = mt->GetRank(); + pLayout->rankSize = 4; + pLayout->numRanks = ranks; + bool multiDim = (ranks > 1); + + pLayout->rankOffset = multiDim ? sizeof(TADDR)*2 : sizeof(TADDR); + pLayout->countOffset = sizeof(TADDR); + pLayout->firstElementOffset = ArrayBase::GetDataPtrOffset(mt); + + + TypeHandle hnd = mt->GetApproxArrayElementTypeHandle(); + PTR_MethodTable cmt = hnd.GetMethodTable(); + + CorElementType componentType = cmt->GetInternalCorElementType(); + if ((UINT64)cmt.GetAddr() == (UINT64)g_pStringClass.GetAddr()) + componentType = ELEMENT_TYPE_STRING; + + COR_TYPEID token; + token.token1 = cmt.GetAddr(); // This could be type handle + token.token2 = 0; + pLayout->componentID = token; + pLayout->componentType = componentType; + + if (CorTypeInfo::IsObjRef_NoThrow(componentType)) + pLayout->elementSize = sizeof(TADDR); + else if (CorIsPrimitiveType(componentType)) + pLayout->elementSize = gElementTypeInfo[componentType].m_cbSize; + else + pLayout->elementSize = cmt->GetNumInstanceFieldBytes(); + } + + return S_OK; +} + + +void DacDbiInterfaceImpl::GetGCHeapInformation(COR_HEAPINFO * pHeapInfo) +{ + DD_ENTER_MAY_THROW; + + size_t heapCount = 0; + pHeapInfo->areGCStructuresValid = GCScan::GetGcRuntimeStructuresValid(); + +#ifdef FEATURE_SVR_GC + if (GCHeap::IsServerHeap()) + { + pHeapInfo->gcType = CorDebugServerGC; + pHeapInfo->numHeaps = DacGetNumHeaps(); + } + else +#endif + { + pHeapInfo->gcType = CorDebugWorkstationGC; + pHeapInfo->numHeaps = 1; + } + + pHeapInfo->pointerSize = sizeof(TADDR); + pHeapInfo->concurrent = g_pConfig->GetGCconcurrent() ? TRUE : FALSE; +} + + +HRESULT DacDbiInterfaceImpl::GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TADDR* pAddrMDInternalRW) +{ + DD_ENTER_MAY_THROW; + if (pAddrMDInternalRW == NULL) + return E_INVALIDARG; + PEFile * pPEFile = vmPEFile.GetDacPtr(); + *pAddrMDInternalRW = pPEFile->GetMDInternalRWAddress(); + return S_OK; +} + +HRESULT DacDbiInterfaceImpl::GetReJitInfo(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ReJitInfo* pvmReJitInfo) +{ + DD_ENTER_MAY_THROW; + if (pvmReJitInfo == NULL) + return E_INVALIDARG; +#ifdef FEATURE_REJIT + PTR_Module pModule = vmModule.GetDacPtr(); + ReJitManager * pReJitMgr = pModule->GetReJitManager(); + PTR_ReJitInfo pReJitInfoCurrent = pReJitMgr->FindNonRevertedReJitInfo(pModule, methodTk); + // if the token lookup failed, we need to search again by method desc + // The rejit manager will index by token if the method isn't loaded when RequestReJIT runs + // and by methoddesc if it was loaded + if (pReJitInfoCurrent == NULL) + { + MethodDesc* pMD = pModule->LookupMethodDef(methodTk); + if (pMD != NULL) + { + pReJitInfoCurrent = pReJitMgr->FindNonRevertedReJitInfo(dac_cast<PTR_MethodDesc>(pMD)); + } + } + pvmReJitInfo->SetDacTargetPtr(PTR_TO_TADDR(pReJitInfoCurrent)); +#else + pvmReJitInfo->SetDacTargetPtr(0); +#endif + return S_OK; +} + +HRESULT DacDbiInterfaceImpl::GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_ReJitInfo* pvmReJitInfo) +{ + DD_ENTER_MAY_THROW; + if (pvmReJitInfo == NULL) + return E_INVALIDARG; +#ifdef FEATURE_REJIT + PTR_MethodDesc pMD = vmMethod.GetDacPtr(); + ReJitManager * pReJitMgr = pMD->GetReJitManager(); + PTR_ReJitInfo pReJitInfoCurrent = pReJitMgr->FindReJitInfo(pMD, (PCODE)codeStartAddress, 0); + pvmReJitInfo->SetDacTargetPtr(PTR_TO_TADDR(pReJitInfoCurrent)); +#else + pvmReJitInfo->SetDacTargetPtr(0); +#endif + return S_OK; +} + +HRESULT DacDbiInterfaceImpl::GetSharedReJitInfo(VMPTR_ReJitInfo vmReJitInfo, OUT VMPTR_SharedReJitInfo* pvmSharedReJitInfo) +{ + DD_ENTER_MAY_THROW; + if (pvmSharedReJitInfo == NULL) + return E_INVALIDARG; +#ifdef FEATURE_REJIT + ReJitInfo* pReJitInfo = vmReJitInfo.GetDacPtr(); + pvmSharedReJitInfo->SetDacTargetPtr(PTR_TO_TADDR(pReJitInfo->m_pShared)); +#else + _ASSERTE(!"You shouldn't be calling this - how did you get a ReJitInfo?"); + pvmSharedReJitInfo->SetDacTargetPtr(0); +#endif + return S_OK; +} + +HRESULT DacDbiInterfaceImpl::GetSharedReJitInfoData(VMPTR_SharedReJitInfo vmSharedReJitInfo, DacSharedReJitInfo* pData) +{ + DD_ENTER_MAY_THROW; +#ifdef FEATURE_REJIT + SharedReJitInfo* pSharedReJitInfo = vmSharedReJitInfo.GetDacPtr(); + pData->m_state = pSharedReJitInfo->GetState(); + pData->m_pbIL = PTR_TO_CORDB_ADDRESS(pSharedReJitInfo->m_pbIL); + pData->m_dwCodegenFlags = pSharedReJitInfo->m_dwCodegenFlags; + pData->m_cInstrumentedMapEntries = (ULONG)pSharedReJitInfo->m_instrumentedILMap.GetCount(); + pData->m_rgInstrumentedMapEntries = PTR_TO_CORDB_ADDRESS(dac_cast<ULONG_PTR>(pSharedReJitInfo->m_instrumentedILMap.GetOffsets())); +#else + _ASSERTE(!"You shouldn't be calling this - how did you get a SharedReJitInfo?"); +#endif + return S_OK; +} + +HRESULT DacDbiInterfaceImpl::GetDefinesBitField(ULONG32 *pDefines) +{ + DD_ENTER_MAY_THROW; + if (pDefines == NULL) + return E_INVALIDARG; + *pDefines = g_pDebugger->m_defines; + return S_OK; +} + +HRESULT DacDbiInterfaceImpl::GetMDStructuresVersion(ULONG32* pMDStructuresVersion) +{ + DD_ENTER_MAY_THROW; + if (pMDStructuresVersion == NULL) + return E_INVALIDARG; + *pMDStructuresVersion = g_pDebugger->m_mdDataStructureVersion; + return S_OK; +} + + +DacRefWalker::DacRefWalker(ClrDataAccess *dac, BOOL walkStacks, BOOL walkFQ, UINT32 handleMask) + : mDac(dac), mWalkStacks(walkStacks), mWalkFQ(walkFQ), mHandleMask(handleMask), mStackWalker(NULL), + mHandleWalker(NULL), mFQStart(PTR_NULL), mFQEnd(PTR_NULL), mFQCurr(PTR_NULL) +{ +} + +DacRefWalker::~DacRefWalker() +{ + Clear(); +} + +HRESULT DacRefWalker::Init() +{ + HRESULT hr = S_OK; + if (mHandleMask) + { + // Will throw on OOM, which is fine. + mHandleWalker = new DacHandleWalker(); + + hr = mHandleWalker->Init(GetHandleWalkerMask()); + } + + if (mWalkStacks && SUCCEEDED(hr)) + { + hr = NextThread(); + } + + return hr; +} + +void DacRefWalker::Clear() +{ + if (mHandleWalker) + { + delete mHandleWalker; + mHandleWalker = NULL; + } + + if (mStackWalker) + { + delete mStackWalker; + mStackWalker = NULL; + } +} + + + +UINT32 DacRefWalker::GetHandleWalkerMask() +{ + UINT32 result = 0; + if (mHandleMask & CorHandleStrong) + result |= (1 << HNDTYPE_STRONG); + + if (mHandleMask & CorHandleStrongPinning) + result |= (1 << HNDTYPE_PINNED); + + if (mHandleMask & CorHandleWeakShort) + result |= (1 << HNDTYPE_WEAK_SHORT); + + if (mHandleMask & CorHandleWeakLong) + result |= (1 << HNDTYPE_WEAK_LONG); + +#ifdef FEATURE_COMINTEROP + if ((mHandleMask & CorHandleWeakRefCount) || (mHandleMask & CorHandleStrongRefCount)) + result |= (1 << HNDTYPE_REFCOUNTED); + + if (mHandleMask & CorHandleWeakWinRT) + result |= (1 << HNDTYPE_WEAK_WINRT); +#endif // FEATURE_COMINTEROP + + if (mHandleMask & CorHandleStrongDependent) + result |= (1 << HNDTYPE_DEPENDENT); + + if (mHandleMask & CorHandleStrongAsyncPinned) + result |= (1 << HNDTYPE_ASYNCPINNED); + + if (mHandleMask & CorHandleStrongSizedByref) + result |= (1 << HNDTYPE_SIZEDREF); + + return result; +} + + + +HRESULT DacRefWalker::Next(ULONG celt, DacGcReference roots[], ULONG *pceltFetched) +{ + if (roots == NULL || pceltFetched == NULL) + return E_POINTER; + + ULONG total = 0; + HRESULT hr = S_OK; + + if (mHandleWalker) + { + hr = mHandleWalker->Next(celt, roots, &total); + + if (hr == S_FALSE || FAILED(hr)) + { + delete mHandleWalker; + mHandleWalker = NULL; + + if (FAILED(hr)) + return hr; + } + } + + if (total < celt) + { + while (total < celt && mFQCurr < mFQEnd) + { + DacGcReference &ref = roots[total++]; + + ref.vmDomain = VMPTR_AppDomain::NullPtr(); + ref.objHnd.SetDacTargetPtr(mFQCurr.GetAddr()); + ref.dwType = (DWORD)CorReferenceFinalizer; + ref.i64ExtraData = 0; + + mFQCurr++; + } + } + + while (total < celt && mStackWalker) + { + ULONG fetched = 0; + hr = mStackWalker->Next(celt-total, roots+total, &fetched); + + if (FAILED(hr)) + return hr; + + if (hr == S_FALSE) + { + hr = NextThread(); + + if (FAILED(hr)) + return hr; + } + + total += fetched; + } + + *pceltFetched = total; + + return total < celt ? S_FALSE : S_OK; +} + +HRESULT DacRefWalker::NextThread() +{ + Thread *pThread = NULL; + if (mStackWalker) + { + pThread = mStackWalker->GetThread(); + delete mStackWalker; + mStackWalker = NULL; + } + + pThread = ThreadStore::GetThreadList(pThread); + + if (!pThread) + return S_FALSE; + + mStackWalker = new DacStackReferenceWalker(mDac, pThread->GetOSThreadId()); + return mStackWalker->Init(); +} + +HRESULT DacHandleWalker::Next(ULONG celt, DacGcReference roots[], ULONG *pceltFetched) +{ + SUPPORTS_DAC; + + if (roots == NULL || pceltFetched == NULL) + return E_POINTER; + + return DoHandleWalk<DacGcReference, ULONG, DacHandleWalker::EnumCallbackDac>(celt, roots, pceltFetched); +} + + +void CALLBACK DacHandleWalker::EnumCallbackDac(PTR_UNCHECKED_OBJECTREF handle, uintptr_t *pExtraInfo, uintptr_t param1, uintptr_t param2) +{ + SUPPORTS_DAC; + + DacHandleWalkerParam *param = (DacHandleWalkerParam *)param1; + HandleChunkHead *curr = param->Curr; + + // If we failed on a previous call (OOM) don't keep trying to allocate, it's not going to work. + if (FAILED(param->Result)) + return; + + // We've moved past the size of the current chunk. We'll allocate a new chunk + // and stuff the handles there. These are cleaned up by the destructor + if (curr->Count >= (curr->Size/sizeof(DacGcReference))) + { + if (curr->Next == NULL) + { + HandleChunk *next = new (nothrow) HandleChunk; + if (next != NULL) + { + curr->Next = next; + } + else + { + param->Result = E_OUTOFMEMORY; + return; + } + } + + curr = param->Curr = param->Curr->Next; + } + + // Fill the current handle. + DacGcReference *dataArray = (DacGcReference*)curr->pData; + DacGcReference &data = dataArray[curr->Count++]; + + data.objHnd.SetDacTargetPtr(handle.GetAddr()); + data.vmDomain.SetDacTargetPtr(TO_TADDR(param->AppDomain)); + + data.i64ExtraData = 0; + unsigned int refCnt = 0; + + switch (param->Type) + { + case HNDTYPE_STRONG: + data.dwType = (DWORD)CorHandleStrong; + break; + + case HNDTYPE_PINNED: + data.dwType = (DWORD)CorHandleStrongPinning; + break; + + case HNDTYPE_WEAK_SHORT: + data.dwType = (DWORD)CorHandleWeakShort; + break; + + case HNDTYPE_WEAK_LONG: + data.dwType = (DWORD)CorHandleWeakLong; + break; + +#ifdef FEATURE_COMINTEROP + case HNDTYPE_REFCOUNTED: + data.dwType = (DWORD)(data.i64ExtraData ? CorHandleStrongRefCount : CorHandleWeakRefCount); + GetRefCountedHandleInfo((OBJECTREF)*handle, param->Type, &refCnt, NULL, NULL, NULL); + data.i64ExtraData = refCnt; + break; + + case HNDTYPE_WEAK_WINRT: + data.dwType = (DWORD)CorHandleWeakWinRT; + break; +#endif + + case HNDTYPE_DEPENDENT: + data.dwType = (DWORD)CorHandleStrongDependent; + data.i64ExtraData = GetDependentHandleSecondary(handle.GetAddr()).GetAddr(); + break; + + case HNDTYPE_ASYNCPINNED: + data.dwType = (DWORD)CorHandleStrongAsyncPinned; + break; + + case HNDTYPE_SIZEDREF: + data.dwType = (DWORD)CorHandleStrongSizedByref; + break; + } +} + + +void DacStackReferenceWalker::GCEnumCallbackDac(LPVOID hCallback, OBJECTREF *pObject, uint32_t flags, DacSlotLocation loc) +{ + GCCONTEXT *gcctx = (GCCONTEXT *)hCallback; + DacScanContext *dsc = (DacScanContext*)gcctx->sc; + + CORDB_ADDRESS obj = 0; + + if (flags & GC_CALL_INTERIOR) + { + if (loc.targetPtr) + obj = (CORDB_ADDRESS)(*PTR_PTR_Object((TADDR)pObject)).GetAddr(); + else + obj = (CORDB_ADDRESS)TO_TADDR(pObject); + + HRESULT hr = dsc->pWalker->mHeap.ListNearObjects(obj, NULL, &obj, NULL); + + // If we failed don't add this instance to the list. ICorDebug doesn't handle invalid pointers + // very well, and the only way the heap walker's ListNearObjects will fail is if we have heap + // corruption...which ICorDebug doesn't deal with anyway. + if (FAILED(hr)) + return; + } + + DacGcReference *data = dsc->pWalker->GetNextObject<DacGcReference>(dsc); + if (data != NULL) + { + data->vmDomain.SetDacTargetPtr(dac_cast<PTR_AppDomain>(dsc->pCurrentDomain).GetAddr()); + if (obj) + data->pObject = obj | 1; + else if (loc.targetPtr) + data->objHnd.SetDacTargetPtr(TO_TADDR(pObject)); + else + data->pObject = pObject->GetAddr() | 1; + + data->dwType = CorReferenceStack; + data->i64ExtraData = 0; + } +} + + +void DacStackReferenceWalker::GCReportCallbackDac(PTR_PTR_Object ppObj, ScanContext *sc, uint32_t flags) +{ + DacScanContext *dsc = (DacScanContext*)sc; + + TADDR obj = ppObj.GetAddr(); + if (flags & GC_CALL_INTERIOR) + { + CORDB_ADDRESS fixed_addr = 0; + HRESULT hr = dsc->pWalker->mHeap.ListNearObjects((CORDB_ADDRESS)obj, NULL, &fixed_addr, NULL); + + // If we failed don't add this instance to the list. ICorDebug doesn't handle invalid pointers + // very well, and the only way the heap walker's ListNearObjects will fail is if we have heap + // corruption...which ICorDebug doesn't deal with anyway. + if (FAILED(hr)) + return; + + obj = TO_TADDR(fixed_addr); + } + + DacGcReference *data = dsc->pWalker->GetNextObject<DacGcReference>(dsc); + if (data != NULL) + { + data->vmDomain.SetDacTargetPtr(dac_cast<PTR_AppDomain>(dsc->pCurrentDomain).GetAddr()); + data->objHnd.SetDacTargetPtr(obj); + data->dwType = CorReferenceStack; + data->i64ExtraData = 0; + } +} + + + +HRESULT DacStackReferenceWalker::Next(ULONG count, DacGcReference stackRefs[], ULONG *pFetched) +{ + if (stackRefs == NULL || pFetched == NULL) + return E_POINTER; + + HRESULT hr = DoStackWalk<ULONG, DacGcReference, + DacStackReferenceWalker::GCReportCallbackDac, + DacStackReferenceWalker::GCEnumCallbackDac> + (count, stackRefs, pFetched); + + return hr; +} |