// 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. /* * * COM+99 EE to Debugger Interface Implementation * */ #include "common.h" #include "dbginterface.h" #include "eedbginterfaceimpl.h" #include "virtualcallstub.h" #include "contractimpl.h" #ifdef DEBUGGING_SUPPORTED #ifndef DACCESS_COMPILE // // Cleanup any global data used by this interface. // void EEDbgInterfaceImpl::Terminate(void) { CONTRACTL { SO_INTOLERANT; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; if (g_pEEDbgInterfaceImpl) { delete g_pEEDbgInterfaceImpl; g_pEEDbgInterfaceImpl = NULL; } } #endif // #ifndef DACCESS_COMPILE Thread* EEDbgInterfaceImpl::GetThread(void) { LIMITED_METHOD_CONTRACT; // Since this may be called from a Debugger Interop Hijack, the EEThread may be bogus. // Thus we can't use contracts. If we do fix that, then the contract below would be nice... #if 0 CONTRACT(Thread *) { NOTHROW; GC_NOTRIGGER; POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); } CONTRACT_END; #endif return ::GetThread(); } #ifndef DACCESS_COMPILE StackWalkAction EEDbgInterfaceImpl::StackWalkFramesEx(Thread* pThread, PREGDISPLAY pRD, PSTACKWALKFRAMESCALLBACK pCallback, VOID* pData, unsigned int flags) { CONTRACTL { DISABLED(NOTHROW); // FIX THIS when StackWalkFramesEx gets fixed. DISABLED(GC_TRIGGERS); // We cannot predict if pCallback will trigger or not. // Disabled is not a bug in this case. PRECONDITION(CheckPointer(pThread)); } CONTRACTL_END; return pThread->StackWalkFramesEx(pRD, pCallback, pData, flags); } Frame *EEDbgInterfaceImpl::GetFrame(CrawlFrame *pCF) { CONTRACT(Frame *) { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pCF)); POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); } CONTRACT_END; RETURN pCF->GetFrame(); } bool EEDbgInterfaceImpl::InitRegDisplay(Thread* pThread, const PREGDISPLAY pRD, const PCONTEXT pctx, bool validContext) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pThread)); PRECONDITION(CheckPointer(pRD)); if (validContext) { PRECONDITION(CheckPointer(pctx)); } } CONTRACTL_END; return pThread->InitRegDisplay(pRD, pctx, validContext); } BOOL EEDbgInterfaceImpl::IsStringObject(Object* o) { CONTRACTL { SO_NOT_MAINLINE; THROWS; GC_NOTRIGGER; PRECONDITION(CheckPointer(o)); } CONTRACTL_END; return o->GetMethodTable() == g_pStringClass; } BOOL EEDbgInterfaceImpl::IsTypedReference(MethodTable* pMT) { CONTRACTL { SO_NOT_MAINLINE; THROWS; GC_NOTRIGGER; PRECONDITION(CheckPointer(pMT)); } CONTRACTL_END; return pMT == g_TypedReferenceMT; } WCHAR* EEDbgInterfaceImpl::StringObjectGetBuffer(StringObject* so) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(so)); } CONTRACTL_END; return so->GetBuffer(); } DWORD EEDbgInterfaceImpl::StringObjectGetStringLength(StringObject* so) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(so)); } CONTRACTL_END; return so->GetStringLength(); } void* EEDbgInterfaceImpl::GetObjectFromHandle(OBJECTHANDLE handle) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; void *v; *((OBJECTREF *)&v) = *(OBJECTREF *)handle; return v; } OBJECTHANDLE EEDbgInterfaceImpl::GetHandleFromObject(void *obj, bool fStrongNewRef, AppDomain *pAppDomain) { CONTRACTL { SO_NOT_MAINLINE; THROWS; // From CreateHandle GC_NOTRIGGER; PRECONDITION(CheckPointer(pAppDomain)); } CONTRACTL_END; OBJECTHANDLE oh; if (fStrongNewRef) { oh = pAppDomain->CreateStrongHandle(ObjectToOBJECTREF((Object *)obj)); LOG((LF_CORDB, LL_INFO1000, "EEI::GHFO: Given objectref 0x%x," "created strong handle 0x%x!\n", obj, oh)); } else { oh = pAppDomain->CreateLongWeakHandle( ObjectToOBJECTREF((Object *)obj)); LOG((LF_CORDB, LL_INFO1000, "EEI::GHFO: Given objectref 0x%x," "created long weak handle 0x%x!\n", obj, oh)); } return oh; } void EEDbgInterfaceImpl::DbgDestroyHandle(OBJECTHANDLE oh, bool fStrongNewRef) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; LOG((LF_CORDB, LL_INFO1000, "EEI::GHFO: Destroyed given handle 0x%x," "fStrong: 0x%x!\n", oh, fStrongNewRef)); if (fStrongNewRef) { DestroyStrongHandle(oh); } else { DestroyLongWeakHandle(oh); } } OBJECTHANDLE EEDbgInterfaceImpl::GetThreadException(Thread *pThread) { CONTRACTL { NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pThread)); } CONTRACTL_END; OBJECTHANDLE oh = pThread->GetThrowableAsHandle(); if (oh != NULL) { return oh; } // Return the last thrown object if there's no current throwable. // This logic is similar to UpdateCurrentThrowable(). return pThread->m_LastThrownObjectHandle; } bool EEDbgInterfaceImpl::IsThreadExceptionNull(Thread *pThread) { CONTRACTL { SO_TOLERANT; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pThread)); } CONTRACTL_END; // // We're assuming that the handle on the // thread is a strong handle and we're goona check it for // NULL. We're also assuming something about the // implementation of the handle here, too. // OBJECTHANDLE h = pThread->GetThrowableAsHandle(); if (h == NULL) { return true; } void *pThrowable = *((void**)h); return (pThrowable == NULL); } void EEDbgInterfaceImpl::ClearThreadException(Thread *pThread) { // // If one day there is a continuable exception, then this will have to be // implemented properly. // // LIMITED_METHOD_CONTRACT; } bool EEDbgInterfaceImpl::StartSuspendForDebug(AppDomain *pAppDomain, BOOL fHoldingThreadStoreLock) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; LOG((LF_CORDB,LL_INFO1000, "EEDbgII:SSFD: start suspend on AD:0x%x\n", pAppDomain)); bool result = Thread::SysStartSuspendForDebug(pAppDomain); return result; } bool EEDbgInterfaceImpl::SweepThreadsForDebug(bool forceSync) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; DISABLED(GC_TRIGGERS); // Called by unmanaged threads. } CONTRACTL_END; return Thread::SysSweepThreadsForDebug(forceSync); } void EEDbgInterfaceImpl::ResumeFromDebug(AppDomain *pAppDomain) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; Thread::SysResumeFromDebug(pAppDomain); } void EEDbgInterfaceImpl::MarkThreadForDebugSuspend(Thread* pRuntimeThread) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pRuntimeThread)); } CONTRACTL_END; pRuntimeThread->MarkForDebugSuspend(); } void EEDbgInterfaceImpl::MarkThreadForDebugStepping(Thread* pRuntimeThread, bool onOff) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pRuntimeThread)); } CONTRACTL_END; pRuntimeThread->MarkDebuggerIsStepping(onOff); } void EEDbgInterfaceImpl::SetThreadFilterContext(Thread *thread, CONTEXT *context) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(thread)); } CONTRACTL_END; thread->SetFilterContext(context); } CONTEXT *EEDbgInterfaceImpl::GetThreadFilterContext(Thread *thread) { CONTRACT(CONTEXT *) { NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(thread)); POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); } CONTRACT_END; RETURN thread->GetFilterContext(); } #ifdef FEATURE_INTEROP_DEBUGGING VOID * EEDbgInterfaceImpl::GetThreadDebuggerWord() { return UnsafeTlsGetValue(g_debuggerWordTLSIndex); } void EEDbgInterfaceImpl::SetThreadDebuggerWord(VOID *dw) { UnsafeTlsSetValue(g_debuggerWordTLSIndex, dw); } #endif BOOL EEDbgInterfaceImpl::IsManagedNativeCode(const BYTE *address) { WRAPPER_NO_CONTRACT; return ExecutionManager::IsManagedCode((PCODE)address); } PCODE EEDbgInterfaceImpl::GetNativeCodeStartAddress(PCODE address) { WRAPPER_NO_CONTRACT; _ASSERTE(address != NULL); return ExecutionManager::GetCodeStartAddress(address); } MethodDesc *EEDbgInterfaceImpl::GetNativeCodeMethodDesc(const PCODE address) { CONTRACT(MethodDesc *) { NOTHROW; GC_NOTRIGGER; PRECONDITION(address != NULL); POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); } CONTRACT_END; RETURN ExecutionManager::GetCodeMethodDesc(address); } #ifndef USE_GC_INFO_DECODER // IsInPrologOrEpilog doesn't seem to be used for code that uses GC_INFO_DECODER BOOL EEDbgInterfaceImpl::IsInPrologOrEpilog(const BYTE *address, size_t* prologSize) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; *prologSize = 0; EECodeInfo codeInfo((PCODE)address); if (codeInfo.IsValid()) { GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken(); if (codeInfo.GetCodeManager()->IsInPrologOrEpilog(codeInfo.GetRelOffset(), gcInfoToken, prologSize)) { return TRUE; } } return FALSE; } #endif // USE_GC_INFO_DECODER // // Given a collection of native offsets of a certain function, determine if each falls // within an exception filter or handler. // void EEDbgInterfaceImpl::DetermineIfOffsetsInFilterOrHandler(const BYTE *functionAddress, DebugOffsetToHandlerInfo *pOffsetToHandlerInfo, unsigned offsetToHandlerInfoLength) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; EECodeInfo codeInfo((PCODE)functionAddress); if (!codeInfo.IsValid()) { return; } // Loop through all the exception handling clause information for the method EH_CLAUSE_ENUMERATOR pEnumState; unsigned EHCount = codeInfo.GetJitManager()->InitializeEHEnumeration(codeInfo.GetMethodToken(), &pEnumState); if (EHCount == 0) { return; } for (ULONG i=0; i < EHCount; i++) { EE_ILEXCEPTION_CLAUSE EHClause; codeInfo.GetJitManager()->GetNextEHClause(&pEnumState, &EHClause); // Check each EH clause against each offset of interest. // Note that this could be time consuming for very long methods ( O(n^2) ). // We could make this linear if we could guarentee that the two lists are sorted. for (ULONG j=0; j < offsetToHandlerInfoLength; j++) { SIZE_T offs = pOffsetToHandlerInfo[j].offset; // those with -1 indicate slots to skip if (offs == (SIZE_T) -1) { continue; } // For a filter, the handler comes directly after it so check from start of filter // to end of handler if (IsFilterHandler(&EHClause)) { if (offs >= EHClause.FilterOffset && offs < EHClause.HandlerEndPC) { pOffsetToHandlerInfo[j].isInFilterOrHandler = TRUE; } } // For anything else, only care about handler range else if (offs >= EHClause.HandlerStartPC && offs < EHClause.HandlerEndPC) { pOffsetToHandlerInfo[j].isInFilterOrHandler = TRUE; } } } } #endif // #ifndef DACCESS_COMPILE void EEDbgInterfaceImpl::GetMethodRegionInfo(const PCODE pStart, PCODE * pCold, size_t *hotSize, size_t *coldSize) { CONTRACTL { SO_INTOLERANT; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pCold)); PRECONDITION(CheckPointer(hotSize)); PRECONDITION(CheckPointer(coldSize)); SUPPORTS_DAC; } CONTRACTL_END; IJitManager::MethodRegionInfo methodRegionInfo = {NULL, 0, NULL, 0}; EECodeInfo codeInfo(pStart); if (codeInfo.IsValid() != NULL) { codeInfo.GetMethodRegionInfo(&methodRegionInfo); } *pCold = methodRegionInfo.coldStartAddress; *hotSize = methodRegionInfo.hotSize; *coldSize = methodRegionInfo.coldSize; } #if defined(WIN64EXCEPTIONS) DWORD EEDbgInterfaceImpl::GetFuncletStartOffsets(const BYTE *pStart, DWORD* pStartOffsets, DWORD dwLength) { CONTRACTL { NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pStart)); } CONTRACTL_END; EECodeInfo codeInfo((PCODE)pStart); _ASSERTE(codeInfo.IsValid()); return codeInfo.GetJitManager()->GetFuncletStartOffsets(codeInfo.GetMethodToken(), pStartOffsets, dwLength); } StackFrame EEDbgInterfaceImpl::FindParentStackFrame(CrawlFrame* pCF) { CONTRACTL { NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pCF)); } CONTRACTL_END; #if defined(DACCESS_COMPILE) DacNotImpl(); return StackFrame(); #else // !DACCESS_COMPILE return ExceptionTracker::FindParentStackFrameForStackWalk(pCF); #endif // !DACCESS_COMPILE } #endif // WIN64EXCEPTIONS #ifndef DACCESS_COMPILE size_t EEDbgInterfaceImpl::GetFunctionSize(MethodDesc *pFD) { CONTRACTL { NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pFD)); } CONTRACTL_END; PCODE methodStart = pFD->GetNativeCode(); if (methodStart == NULL) return 0; EECodeInfo codeInfo(methodStart); GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken(); return codeInfo.GetCodeManager()->GetFunctionSize(gcInfoToken); } #endif //!DACCESS_COMPILE const PCODE EEDbgInterfaceImpl::GetFunctionAddress(MethodDesc *pFD) { CONTRACTL { NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pFD)); SUPPORTS_DAC; } CONTRACTL_END; return pFD->GetNativeCode(); } #ifndef DACCESS_COMPILE void EEDbgInterfaceImpl::DisablePreemptiveGC(void) { CONTRACTL { SO_INTOLERANT; NOTHROW; DISABLED(GC_TRIGGERS); // Disabled because disabled in RareDisablePreemptiveGC() } CONTRACTL_END; ::GetThread()->DisablePreemptiveGC(); } void EEDbgInterfaceImpl::EnablePreemptiveGC(void) { CONTRACTL { SO_INTOLERANT; NOTHROW; DISABLED(GC_TRIGGERS); // Disabled because disabled in RareEnablePreemptiveGC() } CONTRACTL_END; ::GetThread()->EnablePreemptiveGC(); } bool EEDbgInterfaceImpl::IsPreemptiveGCDisabled(void) { CONTRACTL { SO_TOLERANT; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; return ::GetThread()->PreemptiveGCDisabled() != 0; } DWORD EEDbgInterfaceImpl::MethodDescIsStatic(MethodDesc *pFD) { CONTRACTL { NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pFD)); } CONTRACTL_END; return pFD->IsStatic(); } #endif // #ifndef DACCESS_COMPILE Module *EEDbgInterfaceImpl::MethodDescGetModule(MethodDesc *pFD) { CONTRACT(Module *) { NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pFD)); POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); } CONTRACT_END; RETURN pFD->GetModule(); } #ifndef DACCESS_COMPILE COR_ILMETHOD* EEDbgInterfaceImpl::MethodDescGetILHeader(MethodDesc *pFD) { CONTRACT(COR_ILMETHOD *) { THROWS; GC_NOTRIGGER; PRECONDITION(CheckPointer(pFD)); POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); } CONTRACT_END; if (pFD->IsIL()) { RETURN pFD->GetILHeader(); } RETURN NULL; } ULONG EEDbgInterfaceImpl::MethodDescGetRVA(MethodDesc *pFD) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pFD)); } CONTRACTL_END; return pFD->GetRVA(); } MethodDesc *EEDbgInterfaceImpl::FindLoadedMethodRefOrDef(Module* pModule, mdToken memberRef) { CONTRACT(MethodDesc *) { NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pModule)); POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); } CONTRACT_END; // Must have a MemberRef or a MethodDef mdToken tkType = TypeFromToken(memberRef); _ASSERTE((tkType == mdtMemberRef) || (tkType == mdtMethodDef)); if (tkType == mdtMemberRef) { RETURN pModule->LookupMemberRefAsMethod(memberRef); } RETURN pModule->LookupMethodDef(memberRef); } MethodDesc *EEDbgInterfaceImpl::LoadMethodDef(Module* pModule, mdMethodDef methodDef, DWORD numGenericArgs, TypeHandle *pGenericArgs, TypeHandle *pOwnerType) { CONTRACT(MethodDesc *) { THROWS; GC_TRIGGERS; PRECONDITION(CheckPointer(pModule)); POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; _ASSERTE(TypeFromToken(methodDef) == mdtMethodDef); // The generic class and method args are sent as one array // by the debugger. We now split this into two by finding out how // many generic args are for the class and how many for the // method. The actual final checks are done in MemberLoader::GetMethodDescFromMethodDef. DWORD numGenericClassArgs = 0; TypeHandle *pGenericClassArgs = NULL; DWORD nGenericMethodArgs = 0; TypeHandle *pGenericMethodArgs = NULL; mdTypeDef typeDef = 0; TypeHandle thOwner; BOOL forceRemotable = FALSE; if (numGenericArgs != 0) { HRESULT hr = pModule->GetMDImport()->GetParentToken(methodDef, &typeDef); if (FAILED(hr)) COMPlusThrowHR(E_INVALIDARG); TypeHandle thClass = LoadClass(pModule, typeDef); _ASSERTE(!thClass.IsNull()); numGenericClassArgs = thClass.GetNumGenericArgs(); if (numGenericArgs < numGenericClassArgs) { COMPlusThrowHR(COR_E_TARGETPARAMCOUNT); } pGenericClassArgs = (numGenericClassArgs > 0) ? pGenericArgs : NULL; nGenericMethodArgs = (numGenericArgs >= numGenericClassArgs) ? (numGenericArgs - numGenericClassArgs) : 0; pGenericMethodArgs = (nGenericMethodArgs > 0) ? (pGenericArgs + numGenericClassArgs) : NULL; #ifdef FEATURE_COMINTEROP if (numGenericClassArgs > 0) { thOwner = ClassLoader::LoadGenericInstantiationThrowing(pModule, typeDef, Instantiation(pGenericClassArgs, numGenericClassArgs)); // for classes supporting generic interop force remotable method descs forceRemotable = thOwner.GetMethodTable()->SupportsGenericInterop(TypeHandle::Interop_ManagedToNative); } #endif // FEATURE_COMINTEROP } MethodDesc *pRes = MemberLoader::GetMethodDescFromMethodDef(pModule, methodDef, Instantiation(pGenericClassArgs, numGenericClassArgs), Instantiation(pGenericMethodArgs, nGenericMethodArgs), forceRemotable); // The ownerType is extra information that augments the specification of an interface MD. // It is only needed if generics code sharing is supported, because otherwise MDs are // fully self-describing. if (pOwnerType != NULL) { if (numGenericClassArgs != 0) { if (thOwner.IsNull()) *pOwnerType = ClassLoader::LoadGenericInstantiationThrowing(pModule, typeDef, Instantiation(pGenericClassArgs, numGenericClassArgs)); else *pOwnerType = thOwner; } else { *pOwnerType = TypeHandle(pRes->GetMethodTable()); } } RETURN (pRes); } TypeHandle EEDbgInterfaceImpl::FindLoadedClass(Module *pModule, mdTypeDef classToken) { CONTRACT(TypeHandle) { NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pModule)); } CONTRACT_END; RETURN ClassLoader::LookupTypeDefOrRefInModule(pModule, classToken); } TypeHandle EEDbgInterfaceImpl::FindLoadedInstantiation(Module *pModule, mdTypeDef typeDef, DWORD ntypars, TypeHandle *inst) { // Lookup operations run the class loader in non-load mode. ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); // scan violation: asserts that this can be suppressed since there is currently // work on dac-izing all this code and as a result the issue will become moot. CONTRACT_VIOLATION(FaultViolation); return ClassLoader::LoadGenericInstantiationThrowing(pModule, typeDef, Instantiation(inst, ntypars), ClassLoader::DontLoadTypes); } TypeHandle EEDbgInterfaceImpl::FindLoadedFnptrType(TypeHandle *inst, DWORD ntypars) { // Lookup operations run the class loader in non-load mode. ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); // : CALLCONV? return ClassLoader::LoadFnptrTypeThrowing(0, ntypars, inst, // should this be FailIfNotLoaded? - NO - although we may // want to debug unrestored VCs, we can't do it because the debug API // is not set up to handle them // == FailIfNotLoadedOrNotRestored ClassLoader::DontLoadTypes); } TypeHandle EEDbgInterfaceImpl::FindLoadedPointerOrByrefType(CorElementType et, TypeHandle elemtype) { // Lookup operations run the class loader in non-load mode. ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); return ClassLoader::LoadPointerOrByrefTypeThrowing(et, elemtype, // should this be FailIfNotLoaded? - NO - although we may // want to debug unrestored VCs, we can't do it because the debug API // is not set up to handle them // == FailIfNotLoadedOrNotRestored ClassLoader::DontLoadTypes); } TypeHandle EEDbgInterfaceImpl::FindLoadedArrayType(CorElementType et, TypeHandle elemtype, unsigned rank) { // Lookup operations run the class loader in non-load mode. ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); if (elemtype.IsNull()) return TypeHandle(); else return ClassLoader::LoadArrayTypeThrowing(elemtype, et, rank, // should this be FailIfNotLoaded? - NO - although we may // want to debug unrestored VCs, we can't do it because the debug API // is not set up to handle them // == FailIfNotLoadedOrNotRestored ClassLoader::DontLoadTypes ); } TypeHandle EEDbgInterfaceImpl::FindLoadedElementType(CorElementType et) { // Lookup operations run the class loader in non-load mode. ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); MethodTable *m = MscorlibBinder::GetElementType(et); return TypeHandle(m); } TypeHandle EEDbgInterfaceImpl::LoadClass(Module *pModule, mdTypeDef classToken) { CONTRACT(TypeHandle) { THROWS; GC_TRIGGERS; PRECONDITION(CheckPointer(pModule)); } CONTRACT_END; RETURN ClassLoader::LoadTypeDefOrRefThrowing(pModule, classToken, ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef); } TypeHandle EEDbgInterfaceImpl::LoadInstantiation(Module *pModule, mdTypeDef typeDef, DWORD ntypars, TypeHandle *inst) { CONTRACT(TypeHandle) { THROWS; GC_TRIGGERS; PRECONDITION(CheckPointer(pModule)); } CONTRACT_END; RETURN ClassLoader::LoadGenericInstantiationThrowing(pModule, typeDef, Instantiation(inst, ntypars)); } TypeHandle EEDbgInterfaceImpl::LoadArrayType(CorElementType et, TypeHandle elemtype, unsigned rank) { CONTRACT(TypeHandle) { THROWS; GC_TRIGGERS; } CONTRACT_END; if (elemtype.IsNull()) RETURN TypeHandle(); else RETURN ClassLoader::LoadArrayTypeThrowing(elemtype, et, rank); } TypeHandle EEDbgInterfaceImpl::LoadPointerOrByrefType(CorElementType et, TypeHandle elemtype) { CONTRACTL { SO_NOT_MAINLINE; THROWS; GC_TRIGGERS; } CONTRACTL_END; return ClassLoader::LoadPointerOrByrefTypeThrowing(et, elemtype); } TypeHandle EEDbgInterfaceImpl::LoadFnptrType(TypeHandle *inst, DWORD ntypars) { CONTRACTL { SO_NOT_MAINLINE; THROWS; GC_TRIGGERS; } CONTRACTL_END; /* @TODO : CALLCONV? */ return ClassLoader::LoadFnptrTypeThrowing(0, ntypars, inst); } TypeHandle EEDbgInterfaceImpl::LoadElementType(CorElementType et) { CONTRACTL { SO_NOT_MAINLINE; THROWS; GC_TRIGGERS; } CONTRACTL_END; MethodTable *m = MscorlibBinder::GetElementType(et); if (m == NULL) { return TypeHandle(); } return TypeHandle(m); } HRESULT EEDbgInterfaceImpl::GetMethodImplProps(Module *pModule, mdToken tk, DWORD *pRVA, DWORD *pImplFlags) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pModule)); } CONTRACTL_END; return pModule->GetMDImport()->GetMethodImplProps(tk, pRVA, pImplFlags); } HRESULT EEDbgInterfaceImpl::GetParentToken(Module *pModule, mdToken tk, mdToken *pParentToken) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pModule)); } CONTRACTL_END; return pModule->GetMDImport()->GetParentToken(tk, pParentToken); } void EEDbgInterfaceImpl::MarkDebuggerAttached(void) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; g_CORDebuggerControlFlags |= DBCF_ATTACHED; g_CORDebuggerControlFlags &= ~DBCF_PENDING_ATTACH; } void EEDbgInterfaceImpl::MarkDebuggerUnattached(void) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; g_CORDebuggerControlFlags &= ~DBCF_ATTACHED; } #ifdef EnC_SUPPORTED // Apply an EnC edit to the specified module HRESULT EEDbgInterfaceImpl::EnCApplyChanges(EditAndContinueModule *pModule, DWORD cbMetadata, BYTE *pMetadata, DWORD cbIL, BYTE *pIL) { LOG((LF_ENC, LL_INFO100, "EncApplyChanges\n")); CONTRACTL { SO_NOT_MAINLINE; DISABLED(THROWS); DISABLED(GC_TRIGGERS); PRECONDITION(CheckPointer(pModule)); } CONTRACTL_END; return pModule->ApplyEditAndContinue(cbMetadata, pMetadata, cbIL, pIL); } // Remap execution to the latest version of an edited method // This function should never return. void EEDbgInterfaceImpl::ResumeInUpdatedFunction(EditAndContinueModule *pModule, MethodDesc *pFD, void *debuggerFuncHandle, SIZE_T resumeIP, CONTEXT *pContext) { CONTRACTL { SO_NOT_MAINLINE; DISABLED(THROWS); DISABLED(GC_TRIGGERS); PRECONDITION(CheckPointer(pModule)); } CONTRACTL_END; pModule->ResumeInUpdatedFunction(pFD, debuggerFuncHandle, resumeIP, pContext); } #endif // EnC_SUPPORTED bool EEDbgInterfaceImpl::CrawlFrameIsGcSafe(CrawlFrame *pCF) { CONTRACTL { SO_NOT_MAINLINE; SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pCF)); } CONTRACTL_END; return pCF->IsGcSafe(); } bool EEDbgInterfaceImpl::IsStub(const BYTE *ip) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; // IsStub will catch any exceptions and return false. return StubManager::IsStub((PCODE) ip) != FALSE; } #endif // #ifndef DACCESS_COMPILE // static bool EEDbgInterfaceImpl::DetectHandleILStubs(Thread *thread) { CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; return thread->DetectHandleILStubsForDebugger(); } bool EEDbgInterfaceImpl::TraceStub(const BYTE *ip, TraceDestination *trace) { #ifndef DACCESS_COMPILE CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; return StubManager::TraceStub((PCODE) ip, trace) != FALSE; #else DacNotImpl(); return false; #endif // #ifndef DACCESS_COMPILE } #ifndef DACCESS_COMPILE bool EEDbgInterfaceImpl::FollowTrace(TraceDestination *trace) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; return StubManager::FollowTrace(trace) != FALSE; } bool EEDbgInterfaceImpl::TraceFrame(Thread *thread, Frame *frame, BOOL fromPatch, TraceDestination *trace, REGDISPLAY *regs) { CONTRACTL { SO_NOT_MAINLINE; THROWS; DISABLED(GC_TRIGGERS); // This is not a bug - the debugger can call this on an un-managed thread. PRECONDITION(CheckPointer(frame)); } CONTRACTL_END; bool fResult = frame->TraceFrame(thread, fromPatch, trace, regs) != FALSE; #ifdef _DEBUG StubManager::DbgWriteLog("Doing TraceFrame on frame=0x%p (fromPatch=%d), yeilds:\n", frame, fromPatch); if (fResult) { SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; FAULT_NOT_FATAL(); SString buffer; StubManager::DbgWriteLog(" td=%S\n", trace->DbgToString(buffer)); } else { StubManager::DbgWriteLog(" false (this frame does not expect to call managed code).\n"); } #endif return fResult; } bool EEDbgInterfaceImpl::TraceManager(Thread *thread, StubManager *stubManager, TraceDestination *trace, CONTEXT *context, BYTE **pRetAddr) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_TRIGGERS; PRECONDITION(CheckPointer(stubManager)); } CONTRACTL_END; bool fResult = false; EX_TRY { fResult = stubManager->TraceManager(thread, trace, context, pRetAddr) != FALSE; } EX_CATCH { // We never expect TraceManager() to fail and throw an exception, // so we should never hit this assertion. _ASSERTE(!"Fail to trace a stub through TraceManager()"); fResult = false; } EX_END_CATCH(SwallowAllExceptions); #ifdef _DEBUG StubManager::DbgWriteLog("Doing TraceManager on %s (0x%p) for IP=0x%p, yields:\n", stubManager->DbgGetName(), stubManager, GetIP(context)); if (fResult) { // Should never be on helper thread FAULT_NOT_FATAL(); SString buffer; StubManager::DbgWriteLog(" td=%S\n", trace->DbgToString(buffer)); } else { StubManager::DbgWriteLog(" false (this stub does not expect to call managed code).\n"); } #endif return fResult; } void EEDbgInterfaceImpl::EnableTraceCall(Thread *thread) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(thread)); } CONTRACTL_END; thread->IncrementTraceCallCount(); } void EEDbgInterfaceImpl::DisableTraceCall(Thread *thread) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(thread)); } CONTRACTL_END; thread->DecrementTraceCallCount(); } EXTERN_C UINT32 _tls_index; void EEDbgInterfaceImpl::GetRuntimeOffsets(SIZE_T *pTLSIndex, SIZE_T *pTLSIsSpecialIndex, SIZE_T *pTLSCantStopIndex, SIZE_T *pEEThreadStateOffset, SIZE_T *pEEThreadStateNCOffset, SIZE_T *pEEThreadPGCDisabledOffset, DWORD *pEEThreadPGCDisabledValue, SIZE_T *pEEThreadFrameOffset, SIZE_T *pEEThreadMaxNeededSize, DWORD *pEEThreadSteppingStateMask, DWORD *pEEMaxFrameValue, SIZE_T *pEEThreadDebuggerFilterContextOffset, SIZE_T *pEEThreadCantStopOffset, SIZE_T *pEEFrameNextOffset, DWORD *pEEIsManagedExceptionStateMask) { CONTRACTL { SO_INTOLERANT; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pTLSIndex)); PRECONDITION(CheckPointer(pTLSIsSpecialIndex)); PRECONDITION(CheckPointer(pEEThreadStateOffset)); PRECONDITION(CheckPointer(pEEThreadStateNCOffset)); PRECONDITION(CheckPointer(pEEThreadPGCDisabledOffset)); PRECONDITION(CheckPointer(pEEThreadPGCDisabledValue)); PRECONDITION(CheckPointer(pEEThreadFrameOffset)); PRECONDITION(CheckPointer(pEEThreadMaxNeededSize)); PRECONDITION(CheckPointer(pEEThreadSteppingStateMask)); PRECONDITION(CheckPointer(pEEMaxFrameValue)); PRECONDITION(CheckPointer(pEEThreadDebuggerFilterContextOffset)); PRECONDITION(CheckPointer(pEEThreadCantStopOffset)); PRECONDITION(CheckPointer(pEEFrameNextOffset)); PRECONDITION(CheckPointer(pEEIsManagedExceptionStateMask)); } CONTRACTL_END; *pTLSIndex = g_TlsIndex; *pTLSIsSpecialIndex = TlsIdx_ThreadType; *pTLSCantStopIndex = TlsIdx_CantStopCount; *pEEThreadStateOffset = Thread::GetOffsetOfState(); *pEEThreadStateNCOffset = Thread::GetOffsetOfStateNC(); *pEEThreadPGCDisabledOffset = Thread::GetOffsetOfGCFlag(); *pEEThreadPGCDisabledValue = 1; // A little obvious, but just in case... *pEEThreadFrameOffset = Thread::GetOffsetOfCurrentFrame(); *pEEThreadMaxNeededSize = sizeof(Thread); *pEEThreadDebuggerFilterContextOffset = Thread::GetOffsetOfDebuggerFilterContext(); *pEEThreadCantStopOffset = Thread::GetOffsetOfCantStop(); *pEEThreadSteppingStateMask = Thread::TSNC_DebuggerIsStepping; *pEEMaxFrameValue = (DWORD)(size_t)FRAME_TOP; // should this be size_t for 64bit? *pEEFrameNextOffset = Frame::GetOffsetOfNextLink(); *pEEIsManagedExceptionStateMask = Thread::TSNC_DebuggerIsManagedException; } void EEDbgInterfaceImpl::DebuggerModifyingLogSwitch (int iNewLevel, const WCHAR *pLogSwitchName) { CONTRACTL { SO_NOT_MAINLINE; THROWS; GC_NOTRIGGER; } CONTRACTL_END; } HRESULT EEDbgInterfaceImpl::SetIPFromSrcToDst(Thread *pThread, SLOT addrStart, DWORD offFrom, DWORD offTo, bool fCanSetIPOnly, PREGDISPLAY pReg, PCONTEXT pCtx, void *pDji, EHRangeTree *pEHRT) { CONTRACTL { SO_NOT_MAINLINE; THROWS; GC_TRIGGERS; } CONTRACTL_END; return ::SetIPFromSrcToDst(pThread, addrStart, offFrom, offTo, fCanSetIPOnly, pReg, pCtx, pDji, pEHRT); } void EEDbgInterfaceImpl::SetDebugState(Thread *pThread, CorDebugThreadState state) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pThread)); } CONTRACTL_END; _ASSERTE(state == THREAD_SUSPEND || state == THREAD_RUN); LOG((LF_CORDB,LL_INFO10000,"EEDbg:Setting thread 0x%x (ID:0x%x) to 0x%x\n", pThread, pThread->GetThreadId(), state)); if (state == THREAD_SUSPEND) { pThread->SetThreadStateNC(Thread::TSNC_DebuggerUserSuspend); } else { pThread->ResetThreadStateNC(Thread::TSNC_DebuggerUserSuspend); } } void EEDbgInterfaceImpl::SetAllDebugState(Thread *et, CorDebugThreadState state) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; Thread *pThread = NULL; while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL) { if (pThread != et) { SetDebugState(pThread, state); } } } // This is pretty much copied from VM\COMSynchronizable's // INT32 __stdcall ThreadNative::GetThreadState, so propogate changes // to both functions // This just gets the user state from the EE's perspective (hence "partial"). CorDebugUserState EEDbgInterfaceImpl::GetPartialUserState(Thread *pThread) { CONTRACTL { SO_NOT_MAINLINE; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pThread)); } CONTRACTL_END; Thread::ThreadState ts = pThread->GetSnapshotState(); unsigned ret = 0; if (ts & Thread::TS_Background) { ret |= (unsigned)USER_BACKGROUND; } if (ts & Thread::TS_Unstarted) { ret |= (unsigned)USER_UNSTARTED; } // Don't report a StopRequested if the thread has actually stopped. if (ts & Thread::TS_Dead) { ret |= (unsigned)USER_STOPPED; } if (ts & Thread::TS_Interruptible) { ret |= (unsigned)USER_WAIT_SLEEP_JOIN; } // CoreCLR does not support user-requested thread suspension _ASSERTE(!(ts & Thread::TS_UserSuspendPending)); LOG((LF_CORDB,LL_INFO1000, "EEDbgII::GUS: thread 0x%x (id:0x%x)" " userThreadState is 0x%x\n", pThread, pThread->GetThreadId(), ret)); return (CorDebugUserState)ret; } #endif // #ifndef DACCESS_COMPILE #ifdef DACCESS_COMPILE void EEDbgInterfaceImpl::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) { DAC_ENUM_VTHIS(); } #endif unsigned EEDbgInterfaceImpl::GetSizeForCorElementType(CorElementType etyp) { WRAPPER_NO_CONTRACT; return (::GetSizeForCorElementType(etyp)); } #ifndef DACCESS_COMPILE /* * ObjIsInstanceOf * * This method supplies the internal VM implementation of this method to the * debugger left-side. * */ BOOL EEDbgInterfaceImpl::ObjIsInstanceOf(Object *pElement, TypeHandle toTypeHnd) { WRAPPER_NO_CONTRACT; return (::ObjIsInstanceOf(pElement, toTypeHnd)); } #endif /* * ClearAllDebugInterfaceReferences * * This method is called by the debugging part of the runtime to notify * that the debugger resources are no longer valid and any internal references * to it must be null'ed out. * * Parameters: * None. * * Returns: * None. * */ void EEDbgInterfaceImpl::ClearAllDebugInterfaceReferences() { LIMITED_METHOD_CONTRACT; } #ifndef DACCESS_COMPILE #ifdef _DEBUG /* * ObjectRefFlush * * Flushes all debug tracking information for object referencing. * * Parameters: * pThread - The target thread to flush object references of. * * Returns: * None. * */ void EEDbgInterfaceImpl::ObjectRefFlush(Thread *pThread) { WRAPPER_NO_CONTRACT; Thread::ObjectRefFlush(pThread); } #endif #endif #endif // DEBUGGING_SUPPORTED