diff options
Diffstat (limited to 'src/debug/di/rsfunction.cpp')
-rw-r--r-- | src/debug/di/rsfunction.cpp | 1192 |
1 files changed, 1192 insertions, 0 deletions
diff --git a/src/debug/di/rsfunction.cpp b/src/debug/di/rsfunction.cpp new file mode 100644 index 0000000000..6d3abbf107 --- /dev/null +++ b/src/debug/di/rsfunction.cpp @@ -0,0 +1,1192 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// File: rsfunction.cpp +// + +// +//***************************************************************************** +#include "stdafx.h" + +// We have an assert in ceemain.cpp that validates this assumption +#define FIELD_OFFSET_NEW_ENC_DB 0x07FFFFFB + +#include "winbase.h" +#include "corpriv.h" + +/* ------------------------------------------------------------------------- * + * Function class + * ------------------------------------------------------------------------- */ + +//----------------------------------------------------------------------------- +// Constructor for CordbFunction class. +// This represents an IL Function in the debuggee. +// CordbFunction is 1:1 with IL method bodies. +// +// Parameters: +// m - module containing this function. All functions live in a single module. +// funcMetadataToken - the metadata token for this function (scoped to the module). +// enCVersion - Enc Version number of this function (in sync with the module's +// EnC version). Each edit to a function means a whole new IL method body, +// and since CordbFunction is 1:1 with IL, that means a new CordbFunction instance. +//----------------------------------------------------------------------------- +CordbFunction::CordbFunction(CordbModule * m, + mdMethodDef funcMetadataToken, + SIZE_T enCVersion) + : CordbBase(m->GetProcess(), funcMetadataToken, enumCordbFunction), m_pModule(m), m_pClass(NULL), + m_pILCode(NULL), + m_nativeCode(NULL), + m_MDToken(funcMetadataToken), + m_dwEnCVersionNumber(enCVersion), + m_pPrevVersion(NULL), + m_fIsNativeImpl(kUnknownImpl), + m_fCachedMethodValuesValid(FALSE), + m_argCountCached(0), + m_fIsStaticCached(FALSE), + m_reJitILCodes(1) +{ + m_methodSigParserCached = SigParser(NULL, 0); + + _ASSERTE(enCVersion >= CorDB_DEFAULT_ENC_FUNCTION_VERSION); +} + + + +/* + A list of which resources owned by this object are accounted for. + + UNKNOWN: + ICorDebugInfo::NativeVarInfo *m_nativeInfo; + + HANDLED: + CordbModule *m_module; // Assigned w/o AddRef() + CordbClass *m_class; // Assigned w/o AddRef() +*/ + +//----------------------------------------------------------------------------- +// CordbFunction destructor +// All external resources, including references counts, should have been +// released in Neuter(), so this should literally just delete memory or +// or check that the object is already dead. +//----------------------------------------------------------------------------- +CordbFunction::~CordbFunction() +{ + // We should have been explicitly neutered before our internal ref went to 0. + _ASSERTE(IsNeutered()); + + // Since we've been neutered, we shouldn't have any References to release and + // our hash of JitInfos should be empty. + _ASSERTE(m_pILCode == NULL); + _ASSERTE(m_pPrevVersion == NULL); +} + +//----------------------------------------------------------------------------- +// CordbFunction::Neuter +// Neuter releases all of the resources this object holds. CordbFunction +// lives in a CordbModule, so Module neuter will neuter this. +// See CordbBase::Neuter for further semantics. +// +//----------------------------------------------------------------------------- +void CordbFunction::Neuter() +{ + // Neuter any/all CordbNativeCode & CordbILCode objects + if (m_pILCode != NULL) + { + m_pILCode->Neuter(); + m_pILCode.Clear(); // this will internal release. + } + + // Neuter & Release the Prev-Function list. + if (m_pPrevVersion != NULL) + { + m_pPrevVersion->Neuter(); + m_pPrevVersion.Clear(); // this will internal release. + } + + m_pModule = NULL; + m_pClass = NULL; + + m_nativeCode.Clear(); + m_reJitILCodes.NeuterAndClear(GetProcess()->GetProcessLock()); + + CordbBase::Neuter(); +} + +//----------------------------------------------------------------------------- +// CordbFunction::QueryInterface +// Public method to implement IUnknown::QueryInterface. +// Has standard QI semantics. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::QueryInterface(REFIID id, void **pInterface) +{ + if (id == IID_ICorDebugFunction) + { + *pInterface = static_cast<ICorDebugFunction*>(this); + } + else if (id == IID_ICorDebugFunction2) + { + *pInterface = static_cast<ICorDebugFunction2*>(this); + } + else if (id == IID_ICorDebugFunction3) + { + *pInterface = static_cast<ICorDebugFunction3*>(this); + } + else if (id == IID_IUnknown) + { + *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugFunction*>(this)); + } + else + { + *pInterface = NULL; + return E_NOINTERFACE; + } + + ExternalAddRef(); + return S_OK; +} + +//----------------------------------------------------------------------------- +// CordbFunction::GetModule +// Public method (implements ICorDebugFunction::GetModule). +// Get the ICorDebugModule (external representation of a module) that this +// Function is contained in. All functions live in exactly 1 module. +// This is related to the 'CordbModule* GetModule()' method which returns the +// internal module representation for the containing module. +// +// Parameters: +// ppModule - out parameter to hold module. +// +// Return values: +// S_OK iff *ppModule is set. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::GetModule(ICorDebugModule **ppModule) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(ppModule, ICorDebugModule **); + + HRESULT hr = S_OK; + + // Module is set on creation, so just return it. + *ppModule = static_cast<ICorDebugModule*> (m_pModule); + m_pModule->ExternalAddRef(); + + return hr; +} + +//----------------------------------------------------------------------------- +// CordbFunction::GetClass +// Public function to get ICorDebugClass that this function is in. +// +// Parameters: +// ppClass - out parameter holding which class this function lives in. +// +// Return value: +// S_OK iff *ppClass is set. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::GetClass(ICorDebugClass **ppClass) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(ppClass, ICorDebugClass **); + ATT_ALLOW_LIVE_DO_STOPGO(GetProcess()); + *ppClass = NULL; + + HRESULT hr = S_OK; + + if (m_pClass == NULL) + { + // We're not looking for any particular version, just + // the class info. This seems like the best version to request + hr = InitParentClassOfFunction(); + + if (FAILED(hr)) + goto LExit; + } + + *ppClass = static_cast<ICorDebugClass*> (m_pClass); + +LExit: + if (FAILED(hr)) + return hr; + + if (*ppClass) + { + m_pClass->ExternalAddRef(); + return S_OK; + } + else + return S_FALSE; +} + +//----------------------------------------------------------------------------- +// CordbFunction::GetToken +// Public function to get the metadata token for this function. +// This is a MethodDef, which is scoped to a module. +// +// Parameters: +// pMemberDef - out parameter to hold token. +// +// Return values: +// S_OK if pMemberDef is set. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::GetToken(mdMethodDef *pMemberDef) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(pMemberDef, mdMethodDef *); + + + // Token is set on creation, so no updating needed. + CONSISTENCY_CHECK_MSGF((TypeFromToken(m_MDToken) == mdtMethodDef), + ("CordbFunction token (%08x) is not a mdtMethodDef. This=%p", m_MDToken, this)); + + *pMemberDef = m_MDToken; + return S_OK; +} + +//----------------------------------------------------------------------------- +// CordbFunction::GetILCode +// Public function to get an ICorDebugCode object for the IL code in +// this function. +// If we EnC, we get a new ICorDebugFunction, so the IL code & function +// should be 1:1. +// +// Parameters: +// ppCode - out parameter to hold the code object. +// +// Return value: +// S_OK iff *ppCode != NULL. Else error. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::GetILCode(ICorDebugCode ** ppCode) +{ + PUBLIC_REENTRANT_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(ppCode, ICorDebugCode **); + ATT_ALLOW_LIVE_DO_STOPGO(GetProcess()); + + *ppCode = NULL; + HRESULT hr = S_OK; + + // Get the code object. + CordbILCode * pCode = NULL; + hr = GetILCode(&pCode); + _ASSERTE((pCode == NULL) == FAILED(hr)); + + if (FAILED(hr)) + return hr; + + *ppCode = (ICorDebugCode *)pCode; + + return hr; +} + +//----------------------------------------------------------------------------- +// CordbFunction::GetNativeCode +// Public API (ICorDebugFunction::GetNativeCode) to get the native code for +// this function. +// Note that this gets a pretty much random version of the native code when the +// function is a generic method that gets JITted more than once, e.g. for generics. +// Use EnumerateNativeCode instead in that case. +// +// Parameters: +// ppCode - out parameter yeilding the native code object. +// +// Returns: +// S_OK iff *ppCode is set. +// CORDBG_E_CODE_NOT_AVAILABLE if there is no native code. This is common +// if the function is not yet jitted. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::GetNativeCode(ICorDebugCode **ppCode) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(ppCode, ICorDebugCode **); + ATT_ALLOW_LIVE_DO_STOPGO(GetProcess()); + + HRESULT hr = S_OK; + + // Make sure native code is updated before we go searching it. + hr = InitNativeCodeInfo(); + if (FAILED(hr)) + return hr; + + // Generic methods may be jitted multiple times for different native instantiations, + // and so have 1:n relationship between IL:Native. CordbFunction is 1:1 with IL, + // CordbNativeCode is 1:1 with native. + // The interface here only lets us return 1 CordbNativeCode object, so we are + // returning an arbitrary one + RSLockHolder lockHolder(GetProcess()->GetProcessLock()); + _ASSERTE(m_nativeCode == NULL || m_nativeCode->GetVersion() == m_dwEnCVersionNumber); + + if (m_nativeCode == NULL) + { + hr = CORDBG_E_CODE_NOT_AVAILABLE; // This is the case for an unjitted function, + // and so it will be very common. + } + else + { + m_nativeCode->ExternalAddRef(); + *ppCode = m_nativeCode; + hr = S_OK; + } + + return hr; +} + + +//----------------------------------------------------------------------------- +// CordbFunction::GetCode +// Internal method to get the IL code for this function. Each CordbFunction is +// 1:1 with IL, so there is a unique IL Code object to hand out. +// +// Parameters: +// ppCode - out parameter, the IL code object for this function. This should +// be set to NULL on entry. +// Return value: +// S_OK iff *ppCode is set. Else error. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::GetILCode(CordbILCode ** ppCode) +{ + FAIL_IF_NEUTERED(this); + INTERNAL_SYNC_API_ENTRY(GetProcess()); // + VALIDATE_POINTER_TO_OBJECT(ppCode, ICorDebugCode **); + + _ASSERTE(*ppCode == NULL && "Common source of errors is getting addref'd copy here and never Release()ing it"); + *ppCode = NULL; + + // Its okay to do this if the process is not sync'd. + CORDBRequireProcessStateOK(GetProcess()); + + // Fetch all information about this function. + HRESULT hr = S_OK; + CordbILCode * pCode = NULL; + + hr = GetILCodeAndSigToken(); + if (FAILED(hr)) + { + return hr; + } + + // It's possible that m_ILCode will still be NULL. + pCode = m_pILCode; + + if (pCode != NULL) + { + pCode->ExternalAddRef(); + *ppCode = pCode; + + return hr; + } + else + { + return CORDBG_E_CODE_NOT_AVAILABLE; + } +} // CordbFunction::GetCode + +//----------------------------------------------------------------------------- +// CordbFunction::CreateBreakpoint +// Implements ICorDebugFunction::CreateBreakpoint +// Creates a breakpoint at IL offset 0 (which is after the prolog) of the function. +// The function does not need to be jitted yet. +// +// Parameters: +// ppBreakpoint - out parameter for newly created breakpoint object. +// +// Return: +// S_OK - on success. Else error. +//---------------------------------------------------------------------------- +HRESULT CordbFunction::CreateBreakpoint(ICorDebugFunctionBreakpoint **ppBreakpoint) +{ + HRESULT hr = S_OK; + + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(ppBreakpoint, ICorDebugFunctionBreakpoint **); + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + + RSExtSmartPtr<ICorDebugCode> pCode; + + // Use the IL code so that we stop after the prolog + hr = GetILCode(&pCode); + + if (SUCCEEDED(hr)) + { + hr = pCode->CreateBreakpoint(0, ppBreakpoint); + } + + return hr; +} + +#ifdef EnC_SUPPORTED +//----------------------------------------------------------------------------- +// CordbFunction::MakeOld +// Internal method to do any cleanup necessary when a Function is no longer +// the most current. +//----------------------------------------------------------------------------- +void CordbFunction::MakeOld() +{ + if (m_pILCode != NULL) + { + m_pILCode->MakeOld(); + } +} +#endif + +//----------------------------------------------------------------------------- +// CordbFunction::GetLocalVarSigToken +// Public function (implements ICorDebugFunction::GetLocalVarSigToken) to +// get signature token. +// +// Parameters: +// pmdSig - out parameter to hold signature token, which is scoped to the +// function's module. +// +// Return value: +// S_OK if pmdSig is set. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::GetLocalVarSigToken(mdSignature *pmdSig) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(pmdSig, mdSignature *); + + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + + // This will initialize the token. + HRESULT hr = GetILCodeAndSigToken(); + if (FAILED(hr)) + return hr; + + *pmdSig = GetILCode()->GetLocalVarSigToken(); + + return S_OK; +} + +//----------------------------------------------------------------------------- +// CordbFunction::GetCurrentVersionNumber +// Public method for ICorDebugFunction::GetCurrentVersionNumber. +// Gets the most recent (highest) EnC version number of this Function. +// See CordbModule for EnC version number semantics. +// +// Parameters +// pnCurrentVersion - out parameter to hold the version number. +// +// Returns: +// S_OK on success. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::GetCurrentVersionNumber(ULONG32 *pnCurrentVersion) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(pnCurrentVersion, ULONG32 *); + + HRESULT hr = S_OK; + RSLockHolder lockHolder(GetProcess()->GetProcessLock()); + + // the most current version will always be the one found. + CordbFunction* curFunc = m_pModule->LookupFunctionLatestVersion(m_MDToken); + + // will always find at least ourself + PREFIX_ASSUME(curFunc != NULL); + + *pnCurrentVersion = (ULONG32)(curFunc->m_dwEnCVersionNumber); + +#ifdef EnC_SUPPORTED + _ASSERTE( *pnCurrentVersion >= this->m_dwEnCVersionNumber ); +#else + _ASSERTE(*pnCurrentVersion == CorDB_DEFAULT_ENC_FUNCTION_VERSION); +#endif + + return hr; +} + +//----------------------------------------------------------------------------- +// CordbFunction::GetVersionNumber +// Public method for ICorDebugFunction2::GetVersionNumber. +// Gets the EnC version number of this specific Function instance. +// See CordbModule for EnC version number semantics. +// +// Parameters +// pnVersion - out parameter to hold the version number. +// +// Returns: +// S_OK on success. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::GetVersionNumber(ULONG32 *pnVersion) +{ + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(pnVersion, ULONG32 *); + + // This API existed in V1.0 but wasn't implemented. It needs V2 support to work. + if (! this->GetProcess()->SupportsVersion(ver_ICorDebugFunction2)) + { + return E_NOTIMPL; + } + + *pnVersion = (ULONG32)m_dwEnCVersionNumber; + +#ifdef EnC_SUPPORTED + _ASSERTE(*pnVersion >= CorDB_DEFAULT_ENC_FUNCTION_VERSION); +#else + _ASSERTE(*pnVersion == CorDB_DEFAULT_ENC_FUNCTION_VERSION); +#endif + + return S_OK; +} + +//----------------------------------------------------------------------------- +// CordbFunction::GetVersionNumber +// Public method for ICorDebugFunction2::GetVersionNumber. +// Gets the EnC version number of this specific Function instance. +// See CordbModule for EnC version number semantics. +// +// Parameters +// pnVersion - out parameter to hold the version number. +// +// Returns: +// S_OK on success. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::GetActiveReJitRequestILCode(ICorDebugILCode **ppReJitedILCode) +{ + HRESULT hr = S_OK; + VALIDATE_POINTER_TO_OBJECT(ppReJitedILCode, ICorDebugILCode **); + PUBLIC_API_BEGIN(this); + { + *ppReJitedILCode = NULL; + + VMPTR_ReJitInfo vmReJitInfo = VMPTR_ReJitInfo::NullPtr(); + GetProcess()->GetDAC()->GetReJitInfo(GetModule()->m_vmModule, m_MDToken, &vmReJitInfo); + if (!vmReJitInfo.IsNull()) + { + VMPTR_SharedReJitInfo vmSharedReJitInfo = VMPTR_SharedReJitInfo::NullPtr(); + GetProcess()->GetDAC()->GetSharedReJitInfo(vmReJitInfo, &vmSharedReJitInfo); + RSSmartPtr<CordbReJitILCode> pILCode; + IfFailThrow(LookupOrCreateReJitILCode(vmSharedReJitInfo, &pILCode)); + IfFailThrow(pILCode->QueryInterface(IID_ICorDebugILCode, (void**)ppReJitedILCode)); + } + } + PUBLIC_API_END(hr); + return hr; +} + +// determine whether we have a native-only implementation +// Arguments: +// Input: none (we use information in various data members of this instance of CordbFunction: m_isNativeImpl, +// m_pIMImport, m_EnCCount) +// Output none, although we will set m_isNativeImpl to true iff the function has a native-only implementation + +void CordbFunction::InitNativeImpl() +{ + INTERNAL_SYNC_API_ENTRY(GetProcess()); + + // Bail now if we've already discovered that this function is implemented natively as part of the Runtime. + if (m_fIsNativeImpl != kUnknownImpl) + { + return; + } + + // If we don't have a methodToken then we can't figure out what kind of function this is. This includes functions + // such as LCG and ILStubs. In the past we created codepaths that avoided ever calling in here in the common case + // and there would have been asserts and exceptions in the uncommon cases. Now I have just officially let the + // function handle staying as an 'unknown' impl. In such a state it provides no IL, no sigtoken, no native code, and + // no parent class. + if (m_MDToken == mdMethodDefNil) + { + return; + } + + // Figure out if this function is implemented as a native part of the Runtime. If it is, then this ICorDebugFunction + // is just a container for certain Right Side bits of info, i.e., module, class, token, etc. + DWORD attrs; + DWORD implAttrs; + ULONG ulRVA; + BOOL isDynamic; + + IfFailThrow(GetModule()->GetMetaDataImporter()->GetMethodProps(m_MDToken, NULL, NULL, 0, NULL, + &attrs, NULL, NULL, &ulRVA, &implAttrs)); + isDynamic = GetModule()->IsDynamic(); + + // A method has associated IL if its RVA is non-zero, unless it is a dynamic module + // @todo : if RVA is 0 and function has been EnC'd then it isn't native. Remove isEnC + // condition when the compilers stop generating 0 for an RVA. + BOOL isEnC = (GetModule()->m_EnCCount != 0); + if (IsMiNative(implAttrs) || ((isDynamic == FALSE) && (isEnC == FALSE) && (ulRVA == 0))) + { + + m_fIsNativeImpl = kNativeOnly; + } + else + { + m_fIsNativeImpl = kHasIL; + } + +} // CordbFunction::GetProcessAndCheckForNativeImpl + +// Returns the function's ILCode and SigToken +// Arguments: +// Input: none (required info comes from various data members of this instance of CordbFunction +// Output (required): +// none explicit, but this will: +// construct a new instance of CordbILCode and assign it to m_pILCode + +HRESULT CordbFunction::GetILCodeAndSigToken() +{ + INTERNAL_SYNC_API_ENTRY(GetProcess()); + + CordbProcess * pProcess = m_pModule->GetProcess(); + HRESULT hr = S_OK; + + EX_TRY + { + + // ensure that we're not trying to get information about a native-only function + InitNativeImpl(); + if (m_fIsNativeImpl == kNativeOnly || m_fIsNativeImpl == kUnknownImpl) + { + ThrowHR(CORDBG_E_FUNCTION_NOT_IL); + } + + if (m_pILCode == NULL) + { + // we haven't gotten the information previously + + _ASSERTE(pProcess != NULL); + + // This target buffer and mdSignature might never have their values changed from the + // initial ones if the dump target is missing memory. TargetBuffer has a default + // constructor to zero its data and localVarSigToken is explicitly inited. + TargetBuffer codeInfo; + mdSignature localVarSigToken = mdSignatureNil; + SIZE_T currentEnCVersion; + + { + RSLockHolder lockHolder(GetProcess()->GetProcessLock()); + + // In the dump case we may not have the backing memory for this. In such a case + // we construct an empty ILCode object and leave the signatureToken as mdSignatureNil. + // It may also be the case that the memory we read from the dump be inconsistend (huge method size) + // and we also fallback on creating an empty ILCode object. + // See issue DD 273199 for cases where IL and NGEN metadata mismatch (different RVAs). + ALLOW_DATATARGET_MISSING_OR_INCONSISTENT_MEMORY( + pProcess->GetDAC()->GetILCodeAndSig(m_pModule->GetRuntimeDomainFile(), + m_MDToken, + &codeInfo, + &localVarSigToken); + ); + + currentEnCVersion = m_pModule->LookupFunctionLatestVersion(m_MDToken)->m_dwEnCVersionNumber; + } + + LOG((LF_CORDB,LL_INFO10000,"R:CF::GICAST: looking for IL code, version 0x%x\n", currentEnCVersion)); + + if (m_pILCode == NULL) + { + LOG((LF_CORDB,LL_INFO10000,"R:CF::GICAST: not found, creating...\n")); + if(codeInfo.pAddress == 0) + { + LOG((LF_CORDB,LL_INFO10000,"R:CF::GICAST: memory was missing - empty ILCode being created\n")); + } + + // If everything succeeded, we set the IL code object (it's an outparam here). + _ASSERTE(m_pILCode == NULL); + m_pILCode.Assign(new(nothrow)CordbILCode(this, + codeInfo, + currentEnCVersion, + localVarSigToken)); + + if (m_pILCode == NULL) + { + ThrowHR(E_OUTOFMEMORY); + } + } + } + } + EX_CATCH_HRESULT(hr); + return hr; +} // CordbFunction::GetILCodeAndSigToken + + +// Get the metadata token for the class to which a function belongs. +// Arguments: +// Input: +// funcMetadataToken - the metadata token for the method +// Output (required): +// classMetadataToken - the metadata token for the class to which the method belongs +mdTypeDef CordbFunction::InitParentClassOfFunctionHelper(mdToken funcMetadataToken) +{ + // Get the class this method is in. + mdToken tkParent = mdTypeDefNil; + IfFailThrow(GetModule()->GetInternalMD()->GetParentToken(funcMetadataToken, &tkParent)); + _ASSERTE(TypeFromToken(tkParent) == mdtTypeDef); + + return tkParent; +} // CordbFunction::InitParentClassOfFunctionHelper + +// Get the class to which a given function belongs +// Arguments: +// Input: none (required information comes from data members of this instance of CordbFunction) +// Output (required): none, but sets m_pClass +HRESULT CordbFunction::InitParentClassOfFunction() +{ + INTERNAL_SYNC_API_ENTRY(GetProcess()); + + CordbProcess * pProcess = m_pModule->GetProcess(); + (void)pProcess; //prevent "unused variable" error from GCC + HRESULT hr = S_OK; + + EX_TRY + { + + // ensure that we're not trying to get information about a native-only function + InitNativeImpl(); + if (m_fIsNativeImpl == kNativeOnly || m_fIsNativeImpl == kUnknownImpl) + { + ThrowHR(CORDBG_E_FUNCTION_NOT_IL); + } + + mdTypeDef classMetadataToken; + VMPTR_DomainFile vmDomainFile = m_pModule->GetRuntimeDomainFile(); + + classMetadataToken = InitParentClassOfFunctionHelper(m_MDToken); + + if ((m_pClass == NULL) && (classMetadataToken != mdTypeDefNil)) + { + // we haven't gotten the information previously but we have it now + + _ASSERTE(pProcess != NULL); + + CordbAssembly *pAssembly = m_pModule->GetCordbAssembly(); + PREFIX_ASSUME(pAssembly != NULL); + + CordbModule* pClassModule = pAssembly->GetAppDomain()->LookupOrCreateModule(vmDomainFile); + PREFIX_ASSUME(pClassModule != NULL); + + CordbClass *pClass; + hr = pClassModule->LookupOrCreateClass(classMetadataToken, &pClass); + + IfFailThrow(hr); + + _ASSERTE(pClass != NULL); + m_pClass = pClass; + } + } + EX_CATCH_HRESULT(hr); + return hr; + +} // CordbFunction::InitParentClassOfFunction + +// Get information about the native code blob for a function and add it to m_nativeCodeTable +// Arguments: +// Input: none, but we use some data members of this instance of CordbFunction +// Output: standard HRESULT value +// Notes: Apart from the HRESULT, this function will build a new instance of CordbNativeCode and +// add it to the hash table of CordbNativeCodes for this function, unless we have done that +// previously + +HRESULT CordbFunction::InitNativeCodeInfo() +{ + INTERNAL_SYNC_API_ENTRY(GetProcess()); + + CordbProcess * pProcess = m_pModule->GetProcess(); + HRESULT hr = S_OK; + + EX_TRY + { + + // ensure that we're not trying to get information about a native-only function + InitNativeImpl(); + if (m_fIsNativeImpl == kNativeOnly || m_fIsNativeImpl == kUnknownImpl) + { + ThrowHR(CORDBG_E_FUNCTION_NOT_IL); + } + + _ASSERTE(pProcess != NULL); + + // storage for information retrieved from the DAC. This is cleared in the constructor, so it + // won't contain garbage if we don't use the DAC to retrieve information we already got before. + NativeCodeFunctionData codeInfo; + + if (m_nativeCode == NULL) + { + // Get the native code information from the DAC + // PERF: this call is potentially more costly than it needs to be + // All we actually need is the start address and method desc which are cheap to get relative + // to some of the other members. So far this doesn't appear to be a perf hotspot, but if it + // shows up in some scenario it wouldn't be too hard to improve it + pProcess->GetDAC()->GetNativeCodeInfo(m_pModule->GetRuntimeDomainFile(), m_MDToken, &codeInfo); + } + + // populate the m_nativeCode pointer with the code info we found + if (codeInfo.IsValid()) + { + m_nativeCode.Assign(m_pModule->LookupOrCreateNativeCode(m_MDToken, codeInfo.vmNativeCodeMethodDescToken, + codeInfo.m_rgCodeRegions[kHot].pAddress)); + } + + } + EX_CATCH_HRESULT(hr); + return hr; +} // CordbFunction::InitNativeCodeInfo + +//----------------------------------------------------------------------------- +// CordbFunction::SetJMCStatus +// Public method (implements ICorDebugFunction2::SetJMCStatus). +// Set the JMC (eg, "User code" vs. "Non-user code") status of this function. +// +// Parameters: +// fIsUserCode - true to set this Function to JMC, else False. +// +// Returns: +// S_OK if successfully updated JMC status. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::SetJMCStatus(BOOL fIsUserCode) +{ + PUBLIC_REENTRANT_API_ENTRY(this); + HRESULT hr = S_OK; + + LOG((LF_CORDB,LL_INFO10000,"CordbFunction::SetJMCStatus to %d, (token=0x%08x, module=%p)\n", + fIsUserCode, m_MDToken, m_pModule)); + + // Make sure the Left-Side is in a good state. + FAIL_IF_NEUTERED(this); + CordbProcess* pProcess = m_pModule->GetProcess(); + ATT_REQUIRE_STOPPED_MAY_FAIL(pProcess); + + + + // Send an event to the LS to keep it updated. + + // Validation - JMC Steppers don't have defined behavior if + // JMC method status gets toggled underneath them. However, we don't have + // a good way of verifying which methods are of interest to a JMC stepper. + // Having outstanding JMC steppers is dangerous here, but still can be + // done safely. + // Furthermore, debuggers may want to lazily set JMC status (such as when + // code is loaded), which may happen while we have outstanding steppers. + + + DebuggerIPCEvent event; + pProcess->InitIPCEvent(&event, DB_IPCE_SET_METHOD_JMC_STATUS, true, m_pModule->GetAppDomain()->GetADToken()); + event.SetJMCFunctionStatus.vmDomainFile = m_pModule->GetRuntimeDomainFile(); + event.SetJMCFunctionStatus.funcMetadataToken = m_MDToken; + event.SetJMCFunctionStatus.dwStatus = fIsUserCode; + + + // Note: two-way event here... + hr = pProcess->m_cordb->SendIPCEvent(pProcess, &event, sizeof(DebuggerIPCEvent)); + + // Stop now if we can't even send the event. + if (!SUCCEEDED(hr)) + return hr; + + _ASSERTE(event.type == DB_IPCE_SET_METHOD_JMC_STATUS_RESULT); + + return event.hr; +} + +//----------------------------------------------------------------------------- +// CordbFunction::GetJMCStatus +// Public function (implements ICorDebugFunction2::GetJMCStatus) +// Get the JMC status of this function. +// +// Parameters: +// pfIsUserCode - out parameter describing whether this method is user code. +// true iff this function is user code, else false. +// +// Return: +// returns S_OK if *pfIsUserCode is set. +//----------------------------------------------------------------------------- +HRESULT CordbFunction::GetJMCStatus(BOOL * pfIsUserCode) +{ + PUBLIC_REENTRANT_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + VALIDATE_POINTER_TO_OBJECT(pfIsUserCode, BOOL*); + + + _ASSERTE(pfIsUserCode != NULL); + if (pfIsUserCode == NULL) + return E_INVALIDARG; + + // <TODO> @perf - If we know that we haven't updated the JMC status on anything + // in this module (we could keep a dirty flag), then we can just cache + // the jmc status and not send an event to query each time. </TODO> + + // Make sure the process is in a sane state. + CordbProcess* pProcess = m_pModule->GetProcess(); + _ASSERTE(pProcess != NULL); + + // Ask the left-side if a method is user code or not. + DebuggerIPCEvent event; + pProcess->InitIPCEvent(&event, DB_IPCE_GET_METHOD_JMC_STATUS, true, m_pModule->GetAppDomain()->GetADToken()); + event.SetJMCFunctionStatus.vmDomainFile = m_pModule->GetRuntimeDomainFile(); + event.SetJMCFunctionStatus.funcMetadataToken = m_MDToken; + + + // Note: two-way event here... + HRESULT hr = pProcess->m_cordb->SendIPCEvent(pProcess, &event, sizeof(DebuggerIPCEvent)); + + // Stop now if we can't even send the event. + if (!SUCCEEDED(hr)) + return hr; + + _ASSERTE(event.type == DB_IPCE_GET_METHOD_JMC_STATUS_RESULT); + + // update our internal copy of the status. + BOOL fIsUserCode = event.SetJMCFunctionStatus.dwStatus; + + *pfIsUserCode = fIsUserCode; + + return event.hr; +} + + +/* + * CordbFunction::GetSig + * + * Get the method's full metadata signature. This may be cached, but for dynamic modules we'll always read it from + * the metadata. This function also returns the argument count and whether or not the method is static. + * + * Parameters: + * pMethodSigParser - OUT: the signature parser class to use for all signature parsing. + * pFunctionArgCount - OUT: the number of arguments the method takes. + * pFunctionIsStatic - OUT: TRUE if the method is static, FALSE if it is not.. + * + * Returns: + * HRESULT for success or failure. + * + */ +HRESULT CordbFunction::GetSig(SigParser *pMethodSigParser, + ULONG *pFunctionArgCount, + BOOL *pFunctionIsStatic) +{ + INTERNAL_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + + HRESULT hr = S_OK; + + // If the module is dynamic, there had better not be a cached locals signature. + _ASSERTE(!GetModule()->IsDynamic() || !m_fCachedMethodValuesValid); + + // If the method signature cache is null, then go read the signature from the + // matadata. For dynamic methods we never cache the parser because the method + // may change and the cached value will not match. + if (!m_fCachedMethodValuesValid) + { + PCCOR_SIGNATURE functionSignature; + ULONG size; + DWORD methodAttr = 0; + ULONG argCount; + + EX_TRY // @dbgtotod - push this up + { + hr = GetModule()->GetMetaDataImporter()->GetMethodProps(m_MDToken, NULL, NULL, 0, NULL, + &methodAttr, &functionSignature, &size, NULL, NULL); + } + EX_CATCH_HRESULT(hr); + IfFailRet(hr); + + SigParser sigParser = SigParser(functionSignature, size); + + IfFailRet(sigParser.SkipMethodHeaderSignature(&argCount)); + + // If this function is not static, then we've got one extra arg. + BOOL isStatic = (methodAttr & mdStatic) != 0; + + if (!isStatic) + { + argCount++; + } + + // Cache the value for non-dynamic modules, so this is faster later. + if (!GetModule()->IsDynamic()) + { + m_methodSigParserCached = sigParser; + m_argCountCached = argCount; + m_fIsStaticCached = isStatic; + m_fCachedMethodValuesValid = TRUE; + } + else + { + // This is the Dynamic method case, so we can't cache. Just leave fields blank + // and set out-parameters based off locals. + if (pMethodSigParser != NULL) + { + *pMethodSigParser = sigParser; + } + + if (pFunctionArgCount != NULL) + { + *pFunctionArgCount = argCount; + } + + if (pFunctionIsStatic != NULL) + { + *pFunctionIsStatic = isStatic; + } + } + } + + if (m_fCachedMethodValuesValid) + { + // + // Retrieve values from cache + // + + if (pMethodSigParser != NULL) + { + // + // Give them a new instance of the cached value + // + *pMethodSigParser = m_methodSigParserCached; + } + + if (pFunctionArgCount != NULL) + { + *pFunctionArgCount = m_argCountCached; + } + + if (pFunctionIsStatic != NULL) + { + *pFunctionIsStatic = m_fIsStaticCached; + } + + } + + // + // We should never have a cached value for in a dynamic module. + // + CONSISTENCY_CHECK_MSGF(((GetModule()->IsDynamic() && !m_fCachedMethodValuesValid) || + (!GetModule()->IsDynamic() && m_fCachedMethodValuesValid)), + ("No dynamic modules should be cached! Module=%p This=%p", GetModule(), this)); + + return hr; +} + + +//----------------------------------------------------------------------------- +// CordbFunction::GetArgumentType +// Internal method. Given an 0-based IL argument number, return its type. +// This can't access hidden parameters. +// +// Parameters: +// dwIndex - 0-based index for IL argument number. For instance types, +// 'this' argument is #0. For static types, first argument is #0. +// pInst - instantiation information if this is a generic function. Eg, +// if function is List<T>, inst describes T. +// ppResultType - out parameter, yields to CordbType of the argument. +// +// Return: +// S_OK on success. +// +HRESULT CordbFunction::GetArgumentType(DWORD dwIndex, + const Instantiation * pInst, + CordbType ** ppResultType) +{ + FAIL_IF_NEUTERED(this); + INTERNAL_SYNC_API_ENTRY(GetProcess()); + + HRESULT hr = S_OK; + + // Get the method's signature, which contains the types for all the arguments. + SigParser sigParser; + ULONG cMethodArgs; + BOOL fMethodIsStatic; + + IfFailRet(GetSig(&sigParser, &cMethodArgs, &fMethodIsStatic)); + + // Check the index + if (dwIndex >= cMethodArgs) + { + return E_INVALIDARG; + } + + if (!fMethodIsStatic) + { + if (dwIndex == 0) + { + // Return the signature for the 'this' pointer for the + // class this method is in. + return m_pClass->GetThisType(pInst, ppResultType); + } + else + { + dwIndex--; + } + } + + // Run the signature and find the required argument. + for (unsigned int i = 0; i < dwIndex; i++) + { + IfFailRet(sigParser.SkipExactlyOne()); + } + + hr = CordbType::SigToType(m_pModule, &sigParser, pInst, ppResultType); + + return hr; +} + +//----------------------------------------------------------------------------- +// CordbFunction::NotifyCodeCreated +// Internal method. Allows CordbFunctions to get access to a canonical native code entry +// that they will return when asked for native code. The 1:1 mapping between +// function and code was invalidated by generics but debuggers continue to use +// the old API. When they do we need to have some code to hand them back even +// though it is an arbitrary instantiation. Note that that the cannonical code +// here is merely the first one that a user inspects... it is not guaranteed to +// be the same in each debugging session but once set it will never change. It is +// also definately NOT guaranteed to be the instantation over the runtime type +// __Canon. +// +// Parameters: +// nativeCode - the code which corresponds to this function +// +VOID CordbFunction::NotifyCodeCreated(CordbNativeCode* nativeCode) +{ + INTERNAL_SYNC_API_ENTRY(GetProcess()); + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + // Grab this native code as the canonical one if we don't already + // have a canonical entry + if(m_nativeCode == NULL) + m_nativeCode.Assign(nativeCode); +} + + +//----------------------------------------------------------------------------- +// LookupOrCreateReJitILCode finds an existing version of CordbReJitILCode in the given function. +// If the CordbReJitILCode doesn't exist, it creates it. +// +// +HRESULT CordbFunction::LookupOrCreateReJitILCode(VMPTR_SharedReJitInfo vmSharedReJitInfo, CordbReJitILCode** ppILCode) +{ + INTERNAL_API_ENTRY(this); + + HRESULT hr = S_OK; + _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); + + CordbReJitILCode * pILCode = m_reJitILCodes.GetBase(VmPtrToCookie(vmSharedReJitInfo)); + + // special case non-existance as need to add to the hash table too + if (pILCode == NULL) + { + // we don't yet support ENC and ReJIT together, so the version should be 1 + _ASSERTE(m_dwEnCVersionNumber == 1); + RSInitHolder<CordbReJitILCode> pILCodeHolder(new CordbReJitILCode(this, 1, vmSharedReJitInfo)); + IfFailRet(m_reJitILCodes.AddBase(pILCodeHolder)); + pILCode = pILCodeHolder; + pILCodeHolder.ClearAndMarkDontNeuter(); + } + + pILCode->InternalAddRef(); + *ppILCode = pILCode; + return S_OK; +} |