diff options
Diffstat (limited to 'src/debug/daccess')
36 files changed, 53618 insertions, 0 deletions
diff --git a/src/debug/daccess/.gitmirror b/src/debug/daccess/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/debug/daccess/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/debug/daccess/CMakeLists.txt b/src/debug/daccess/CMakeLists.txt new file mode 100644 index 0000000000..30068450b4 --- /dev/null +++ b/src/debug/daccess/CMakeLists.txt @@ -0,0 +1,49 @@ + +include(${CLR_DIR}/dac.cmake) + +add_definitions(-DFEATURE_NO_HOST) + +include_directories(BEFORE ${VM_DIR}) +include_directories(BEFORE ${VM_DIR}/${ARCH_SOURCES_DIR}) +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CLR_DIR}/src/debug/ee) +include_directories(${CLR_DIR}/src/gc) +include_directories(${CLR_DIR}/src/gcdump) + +if(CLR_CMAKE_PLATFORM_UNIX) + include_directories(${GENERATED_INCLUDE_DIR}) + add_compile_options(-fPIC) +endif(CLR_CMAKE_PLATFORM_UNIX) + +set(DACCESS_SOURCES + dacdbiimpl.cpp + dacdbiimpllocks.cpp + dacdbiimplstackwalk.cpp + daccess.cpp + dacfn.cpp + enummem.cpp + fntableaccess.cpp + inspect.cpp + reimpl.cpp + request.cpp + request_svr.cpp + stack.cpp + task.cpp + nidump.cpp + datatargetadapter.cpp +) + +include_directories(${ARCH_SOURCES_DIR}) + list(APPEND DACCESS_SOURCES + ${ARCH_SOURCES_DIR}/primitives.cpp + ) + +convert_to_absolute_path(DACCESS_SOURCES ${DACCESS_SOURCES}) + +add_precompiled_header(stdafx.h stdafx.cpp DACCESS_SOURCES) + +add_library_clr(daccess ${DACCESS_SOURCES}) + +if(CLR_CMAKE_PLATFORM_UNIX) + add_dependencies(daccess coreclr) +endif(CLR_CMAKE_PLATFORM_UNIX) diff --git a/src/debug/daccess/amd64/.gitmirror b/src/debug/daccess/amd64/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/debug/daccess/amd64/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/debug/daccess/amd64/primitives.cpp b/src/debug/daccess/amd64/primitives.cpp new file mode 100644 index 0000000000..055b75d120 --- /dev/null +++ b/src/debug/daccess/amd64/primitives.cpp @@ -0,0 +1,13 @@ +// 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. + + +// + + +#include "stdafx.h" + +#include "../../shared/amd64/primitives.cpp" + + diff --git a/src/debug/daccess/arm/.gitmirror b/src/debug/daccess/arm/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/debug/daccess/arm/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/debug/daccess/arm/primitives.cpp b/src/debug/daccess/arm/primitives.cpp new file mode 100644 index 0000000000..ce8263ad49 --- /dev/null +++ b/src/debug/daccess/arm/primitives.cpp @@ -0,0 +1,9 @@ +// 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. + +// + +#include "stdafx.h" + +#include "../../shared/arm/primitives.cpp" diff --git a/src/debug/daccess/arm64/.gitmirror b/src/debug/daccess/arm64/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/debug/daccess/arm64/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/debug/daccess/arm64/primitives.cpp b/src/debug/daccess/arm64/primitives.cpp new file mode 100644 index 0000000000..a2e52138ac --- /dev/null +++ b/src/debug/daccess/arm64/primitives.cpp @@ -0,0 +1,9 @@ +// 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. + +// + +#include "stdafx.h" + +#include "../../shared/arm64/primitives.cpp" diff --git a/src/debug/daccess/daccess.cpp b/src/debug/daccess/daccess.cpp new file mode 100644 index 0000000000..ba3995b1f7 --- /dev/null +++ b/src/debug/daccess/daccess.cpp @@ -0,0 +1,8673 @@ +// 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: daccess.cpp +// + +// +// ClrDataAccess implementation. +// +//***************************************************************************** + +#include "stdafx.h" +#include <clrdata.h> +#include "typestring.h" +#include "holder.h" +#include "debuginfostore.h" +#include "peimagelayout.inl" +#include "datatargetadapter.h" +#include "readonlydatatargetfacade.h" +#include "metadataexports.h" +#include "excep.h" +#include "debugger.h" +#include "dwreport.h" +#include "primitives.h" +#include "dbgutil.h" +#ifdef FEATURE_PAL +#include <dactablerva.h> +#endif + +#include "dwbucketmanager.hpp" + +// To include definiton of IsThrowableThreadAbortException +// #include <exstatecommon.h> + +CRITICAL_SECTION g_dacCritSec; +ClrDataAccess* g_dacImpl; +HINSTANCE g_thisModule; + +extern VOID STDMETHODCALLTYPE TLS_FreeMasterSlotIndex(); + +EXTERN_C BOOL WINAPI +DllMain(HANDLE instance, DWORD reason, LPVOID reserved) +{ + static bool g_procInitialized = false; + + switch(reason) + { + case DLL_PROCESS_ATTACH: + { + if (g_procInitialized) + { +#ifdef FEATURE_PAL + // Double initialization can happen on Unix + // in case of manual load of DAC shared lib and calling DllMain + // not a big deal, we just ignore it. + return TRUE; +#else + return FALSE; +#endif + } + +#ifdef FEATURE_PAL + int err = PAL_InitializeDLL(); + if(err != 0) + { + return FALSE; + } +#endif + InitializeCriticalSection(&g_dacCritSec); + + // Save the module handle. + g_thisModule = (HINSTANCE)instance; + + g_procInitialized = true; + break; + } + + case DLL_PROCESS_DETACH: + // It's possible for this to be called without ATTACH completing (eg. if it failed) + if (g_procInitialized) + { + DeleteCriticalSection(&g_dacCritSec); + } +#ifndef FEATURE_PAL + TLS_FreeMasterSlotIndex(); +#endif + g_procInitialized = false; + break; + } + + return TRUE; +} + +HINSTANCE +GetModuleInst(void) +{ + return g_thisModule; +} + +HRESULT +ConvertUtf8(__in LPCUTF8 utf8, + ULONG32 bufLen, + ULONG32* nameLen, + __out_ecount_part_opt(bufLen, *nameLen) PWSTR buffer) +{ + if (nameLen) + { + *nameLen = WszMultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); + if (!*nameLen) + { + return HRESULT_FROM_GetLastError(); + } + } + + if (buffer && bufLen) + { + if (!WszMultiByteToWideChar(CP_UTF8, 0, utf8, -1, buffer, bufLen)) + { + return HRESULT_FROM_GetLastError(); + } + } + + return S_OK; +} + +HRESULT +AllocUtf8(__in_opt LPCWSTR wstr, + ULONG32 srcChars, + __deref_out LPUTF8* utf8) +{ + ULONG32 chars = WszWideCharToMultiByte(CP_UTF8, 0, wstr, srcChars, + NULL, 0, NULL, NULL); + if (!chars) + { + return HRESULT_FROM_GetLastError(); + } + + // Make sure the converted string is always terminated. + if (srcChars != (ULONG32)-1) + { + if (!ClrSafeInt<ULONG32>::addition(chars, 1, chars)) + { + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + } + } + + char* mem = new (nothrow) char[chars]; + if (!mem) + { + return E_OUTOFMEMORY; + } + + if (!WszWideCharToMultiByte(CP_UTF8, 0, wstr, srcChars, + mem, chars, NULL, NULL)) + { + HRESULT hr = HRESULT_FROM_GetLastError(); + delete [] mem; + return hr; + } + + if (srcChars != (ULONG32)-1) + { + mem[chars - 1] = 0; + } + + *utf8 = mem; + return S_OK; +} + +HRESULT +GetFullClassNameFromMetadata(IMDInternalImport* mdImport, + mdTypeDef classToken, + ULONG32 bufferChars, + __inout_ecount(bufferChars) LPUTF8 buffer) +{ + HRESULT hr; + LPCUTF8 baseName, namespaceName; + + IfFailRet(mdImport->GetNameOfTypeDef(classToken, &baseName, &namespaceName)); + return ns::MakePath(buffer, bufferChars, namespaceName, baseName) ? + S_OK : E_OUTOFMEMORY; +} + +HRESULT +GetFullMethodNameFromMetadata(IMDInternalImport* mdImport, + mdMethodDef methodToken, + ULONG32 bufferChars, + __inout_ecount(bufferChars) LPUTF8 buffer) +{ + HRESULT status; + HRESULT hr; + mdTypeDef classToken; + size_t len; + + if (mdImport->GetParentToken(methodToken, &classToken) == S_OK) + { + if ((status = + GetFullClassNameFromMetadata(mdImport, classToken, + bufferChars, buffer)) != S_OK) + { + return status; + } + + len = strlen(buffer); + buffer += len; + bufferChars -= static_cast<ULONG32>(len) + 1; + + if (!bufferChars) + { + return E_OUTOFMEMORY; + } + + *buffer++ = NAMESPACE_SEPARATOR_CHAR; + } + + LPCUTF8 methodName; + IfFailRet(mdImport->GetNameOfMethodDef(methodToken, &methodName)); +// Review conversion of size_t to ULONG32. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4267) +#endif + len = strlen(methodName); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + if (len >= bufferChars) + { + return E_OUTOFMEMORY; + } + + strcpy_s(buffer, bufferChars, methodName); + return S_OK; +} + +HRESULT +SplitFullName(__in_z __in PCWSTR fullName, + SplitSyntax syntax, + ULONG32 memberDots, + __deref_out_opt LPUTF8* namespaceName, + __deref_out_opt LPUTF8* typeName, + __deref_out_opt LPUTF8* memberName, + __deref_out_opt LPUTF8* params) +{ + HRESULT status; + PCWSTR paramsStart, memberStart, memberEnd, typeStart; + + if (!*fullName) + { + return E_INVALIDARG; + } + + // + // Split off parameters. + // + + paramsStart = wcschr(fullName, W('(')); + if (paramsStart) + { + if (syntax != SPLIT_METHOD || + paramsStart == fullName) + { + return E_INVALIDARG; + } + + if ((status = AllocUtf8(paramsStart, (ULONG32)-1, params)) != S_OK) + { + return status; + } + + memberEnd = paramsStart - 1; + } + else + { + *params = NULL; + memberEnd = fullName + (wcslen(fullName) - 1); + } + + if (syntax != SPLIT_TYPE) + { + // + // Split off member name. + // + + memberStart = memberEnd; + + for (;;) + { + while (memberStart >= fullName && + *memberStart != W('.')) + { + memberStart--; + } + + // Some member names (e.g. .ctor and .dtor) have + // dots, so go back to the first dot. + while (memberStart > fullName && + memberStart[-1] == W('.')) + { + memberStart--; + } + + if (memberStart <= fullName) + { + if (memberDots > 0) + { + // Caller expected dots in the + // member name and they weren't found. + status = E_INVALIDARG; + goto DelParams; + } + + break; + } + else if (memberDots == 0) + { + break; + } + + memberStart--; + memberDots--; + } + + memberStart++; + if (memberStart > memberEnd) + { + status = E_INVALIDARG; + goto DelParams; + } + + if ((status = AllocUtf8(memberStart, (ULONG32) + (memberEnd - memberStart) + 1, + memberName)) != S_OK) + { + goto DelParams; + } + } + else + { + *memberName = NULL; + memberStart = memberEnd + 2; + } + + // + // Split off type name. + // + + if (memberStart > fullName) + { + // Must have at least one character for the type + // name. If there was a member name, there must + // also be a separator. + if (memberStart < fullName + 2) + { + status = E_INVALIDARG; + goto DelMember; + } + + typeStart = memberStart - 2; + while (typeStart >= fullName && + *typeStart != W('.')) + { + typeStart--; + } + typeStart++; + + if ((status = AllocUtf8(typeStart, (ULONG32) + (memberStart - typeStart) - 1, + typeName)) != S_OK) + { + goto DelMember; + } + } + else + { + *typeName = NULL; + typeStart = fullName; + } + + // + // Namespace must be the rest. + // + + if (typeStart > fullName) + { + if ((status = AllocUtf8(fullName, (ULONG32) + (typeStart - fullName) - 1, + namespaceName)) != S_OK) + { + goto DelType; + } + } + else + { + *namespaceName = NULL; + } + + return S_OK; + + DelType: + delete [] (*typeName); + DelMember: + delete [] (*memberName); + DelParams: + delete [] (*params); + return status; +} + +int +CompareUtf8(__in LPCUTF8 str1, __in LPCUTF8 str2, __in ULONG32 nameFlags) +{ + if (nameFlags & CLRDATA_BYNAME_CASE_INSENSITIVE) + { + // XXX Microsoft - Convert to Unicode? + return SString::_stricmp(str1, str2); + } + + return strcmp(str1, str2); +} + +//---------------------------------------------------------------------------- +// +// MetaEnum. +// +//---------------------------------------------------------------------------- + +HRESULT +MetaEnum::Start(IMDInternalImport* mdImport, ULONG32 kind, + mdToken container) +{ + HRESULT status; + + switch(kind) + { + case mdtTypeDef: + status = mdImport->EnumTypeDefInit(&m_enum); + break; + case mdtMethodDef: + case mdtFieldDef: + status = mdImport->EnumInit(kind, container, &m_enum); + break; + default: + return E_INVALIDARG; + } + if (status != S_OK) + { + return status; + } + + m_mdImport = mdImport; + m_kind = kind; + + return S_OK; +} + +void +MetaEnum::End(void) +{ + if (!m_mdImport) + { + return; + } + + switch(m_kind) + { + case mdtTypeDef: + m_mdImport->EnumTypeDefClose(&m_enum); + break; + case mdtMethodDef: + case mdtFieldDef: + m_mdImport->EnumClose(&m_enum); + break; + } + + Clear(); +} + +HRESULT +MetaEnum::NextToken(mdToken* token, + __deref_opt_out_opt LPCUTF8* namespaceName, + __deref_opt_out_opt LPCUTF8* name) +{ + HRESULT hr; + if (!m_mdImport) + { + return E_INVALIDARG; + } + + switch(m_kind) + { + case mdtTypeDef: + if (!m_mdImport->EnumTypeDefNext(&m_enum, token)) + { + return S_FALSE; + } + m_lastToken = *token; + if (namespaceName || name) + { + LPCSTR _name, _namespaceName; + + IfFailRet(m_mdImport->GetNameOfTypeDef(*token, &_name, &_namespaceName)); + if (namespaceName) + { + *namespaceName = _namespaceName; + } + if (name) + { + *name = _name; + } + } + return S_OK; + + case mdtMethodDef: + if (!m_mdImport->EnumNext(&m_enum, token)) + { + return S_FALSE; + } + m_lastToken = *token; + if (namespaceName) + { + *namespaceName = NULL; + } + if (name != NULL) + { + IfFailRet(m_mdImport->GetNameOfMethodDef(*token, name)); + } + return S_OK; + + case mdtFieldDef: + if (!m_mdImport->EnumNext(&m_enum, token)) + { + return S_FALSE; + } + m_lastToken = *token; + if (namespaceName) + { + *namespaceName = NULL; + } + if (name != NULL) + { + IfFailRet(m_mdImport->GetNameOfFieldDef(*token, name)); + } + return S_OK; + + default: + return E_INVALIDARG; + } +} + +HRESULT +MetaEnum::NextDomainToken(AppDomain** appDomain, + mdToken* token) +{ + HRESULT status; + + if (m_appDomain) + { + // Use only the caller-provided app domain. + *appDomain = m_appDomain; + return NextToken(token, NULL, NULL); + } + + // + // Splay tokens across all app domains. + // + + for (;;) + { + if (m_lastToken == mdTokenNil) + { + // Need to fetch a token. + if ((status = NextToken(token, NULL, NULL)) != S_OK) + { + return status; + } + + m_domainIter.Init(); + } + + if (m_domainIter.Next()) + { + break; + } + + m_lastToken = mdTokenNil; + } + + *appDomain = m_domainIter.GetDomain(); + *token = m_lastToken; + + return S_OK; +} + +HRESULT +MetaEnum::NextTokenByName(__in_opt LPCUTF8 namespaceName, + __in_opt LPCUTF8 name, + ULONG32 nameFlags, + mdToken* token) +{ + HRESULT status; + LPCUTF8 tokNamespace, tokName; + + for (;;) + { + if ((status = NextToken(token, &tokNamespace, &tokName)) != S_OK) + { + return status; + } + + if (namespaceName && + (!tokNamespace || + CompareUtf8(namespaceName, tokNamespace, nameFlags) != 0)) + { + continue; + } + if (name && + (!tokName || + CompareUtf8(name, tokName, nameFlags) != 0)) + { + continue; + } + + return S_OK; + } +} + +HRESULT +MetaEnum::NextDomainTokenByName(__in_opt LPCUTF8 namespaceName, + __in_opt LPCUTF8 name, + ULONG32 nameFlags, + AppDomain** appDomain, mdToken* token) +{ + HRESULT status; + + if (m_appDomain) + { + // Use only the caller-provided app domain. + *appDomain = m_appDomain; + return NextTokenByName(namespaceName, name, nameFlags, token); + } + + // + // Splay tokens across all app domains. + // + + for (;;) + { + if (m_lastToken == mdTokenNil) + { + // Need to fetch a token. + if ((status = NextTokenByName(namespaceName, name, nameFlags, + token)) != S_OK) + { + return status; + } + + m_domainIter.Init(); + } + + if (m_domainIter.Next()) + { + break; + } + + m_lastToken = mdTokenNil; + } + + *appDomain = m_domainIter.GetDomain(); + *token = m_lastToken; + + return S_OK; +} + +HRESULT +MetaEnum::New(Module* mod, + ULONG32 kind, + mdToken container, + IXCLRDataAppDomain* pubAppDomain, + MetaEnum** metaEnumRet, + CLRDATA_ENUM* handle) +{ + HRESULT status; + MetaEnum* metaEnum; + + if (handle) + { + *handle = TO_CDENUM(NULL); + } + + if (!mod->GetFile()->HasMetadata()) + { + return S_FALSE; + } + + metaEnum = new (nothrow) MetaEnum; + if (!metaEnum) + { + return E_OUTOFMEMORY; + } + + if ((status = metaEnum-> + Start(mod->GetMDImport(), kind, container)) != S_OK) + { + delete metaEnum; + return status; + } + + if (pubAppDomain) + { + metaEnum->m_appDomain = + ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain(); + } + + if (metaEnumRet) + { + *metaEnumRet = metaEnum; + } + if (handle) + { + *handle = TO_CDENUM(metaEnum); + } + return S_OK; +} + +//---------------------------------------------------------------------------- +// +// SplitName +// +//---------------------------------------------------------------------------- + +SplitName::SplitName(SplitSyntax syntax, ULONG32 nameFlags, + ULONG32 memberDots) +{ + m_syntax = syntax; + m_nameFlags = nameFlags; + m_memberDots = memberDots; + + Clear(); +} + +void +SplitName::Delete(void) +{ + delete [] m_namespaceName; + m_namespaceName = NULL; + delete [] m_typeName; + m_typeName = NULL; + delete [] m_memberName; + m_memberName = NULL; + delete [] m_params; + m_params = NULL; +} + +void +SplitName::Clear(void) +{ + m_namespaceName = NULL; + m_typeName = NULL; + m_typeToken = mdTypeDefNil; + m_memberName = NULL; + m_memberToken = mdTokenNil; + m_params = NULL; + + m_tlsThread = NULL; + m_metaEnum.m_appDomain = NULL; + m_module = NULL; + m_lastField = NULL; +} + +HRESULT +SplitName::SplitString(__in_opt PCWSTR fullName) +{ + if (m_syntax == SPLIT_NO_NAME) + { + if (fullName) + { + return E_INVALIDARG; + } + + return S_OK; + } + else if (!fullName) + { + return E_INVALIDARG; + } + + return SplitFullName(fullName, + m_syntax, + m_memberDots, + &m_namespaceName, + &m_typeName, + &m_memberName, + &m_params); +} + +FORCEINLINE +WCHAR* wcrscan(LPCWSTR beg, LPCWSTR end, WCHAR ch) +{ + //_ASSERTE(beg <= end); + WCHAR *p; + for (p = (WCHAR*)end; p >= beg; --p) + { + if (*p == ch) + break; + } + return p; +} + +// This functions allocates a new UTF8 string that contains the classname +// lying between the current sepName and the previous sepName. E.g. for a +// class name of "Outer+middler+inner" when sepName points to the NULL +// terminator this function will return "inner" in pResult and will update +// sepName to point to the second '+' character in the string. When sepName +// points to the first '+' character this function will return "Outer" in +// pResult and sepName will point one WCHAR before fullName. +HRESULT NextEnclosingClasName(LPCWSTR fullName, __deref_inout LPWSTR& sepName, __deref_out LPUTF8 *pResult) +{ + if (sepName < fullName) + { + return E_FAIL; + } + //_ASSERTE(*sepName == W('\0') || *sepName == W('+') || *sepName == W('/')); + + LPWSTR origInnerName = sepName-1; + if ((sepName = wcrscan(fullName, origInnerName, W('+'))) < fullName) + { + sepName = wcrscan(fullName, origInnerName, W('/')); + } + + return AllocUtf8(sepName+1, static_cast<ULONG32>(origInnerName-sepName), pResult); +} + +bool +SplitName::FindType(IMDInternalImport* mdInternal) +{ + if (m_typeToken != mdTypeDefNil) + { + return true; + } + + if (!m_typeName) + { + return false; + } + + if ((m_namespaceName == NULL || m_namespaceName[0] == '\0') + && (CompareUtf8(COR_MODULE_CLASS, m_typeName, m_nameFlags)==0)) + { + m_typeToken = TokenFromRid(1, mdtTypeDef); // <Module> class always has a RID of 1. + return true; + } + + MetaEnum metaEnum; + + if (metaEnum.Start(mdInternal, mdtTypeDef, mdTypeDefNil) != S_OK) + { + return false; + } + + LPUTF8 curClassName; + + ULONG32 length; + WCHAR wszName[MAX_CLASS_NAME]; + ConvertUtf8(m_typeName, MAX_CLASS_NAME, &length, wszName); + + WCHAR *pHead; + +Retry: + + pHead = wszName + length; + + if (FAILED(NextEnclosingClasName(wszName, pHead, &curClassName))) + { + return false; + } + + // an inner class has an empty namespace associated with it + HRESULT hr = metaEnum.NextTokenByName((pHead < wszName) ? m_namespaceName : "", + curClassName, + m_nameFlags, + &m_typeToken); + delete[] curClassName; + + if (hr != S_OK) + { + // if we didn't find a token with the given name + return false; + } + else if (pHead < wszName) + { + // if we did find a token, *and* the class name given + // does not specify any enclosing class, that's it + return true; + } + else + { + // restart with innermost class + pHead = wszName + length; + mdTypeDef tkInner = m_typeToken; + mdTypeDef tkOuter; + BOOL bRetry = FALSE; + LPUTF8 utf8Name; + + while ( + !bRetry + && SUCCEEDED(NextEnclosingClasName(wszName, pHead, &utf8Name)) + ) + { + if (mdInternal->GetNestedClassProps(tkInner, &tkOuter) != S_OK) + tkOuter = mdTypeDefNil; + + LPCSTR szName, szNS; + if (FAILED(mdInternal->GetNameOfTypeDef(tkInner, &szName, &szNS))) + { + return false; + } + bRetry = (CompareUtf8(utf8Name, szName, m_nameFlags) != 0); + if (!bRetry) + { + // if this is outermost class we need to compare namespaces too + if (tkOuter == mdTypeDefNil) + { + // is this the outermost in the class name, too? + if (pHead < wszName + && CompareUtf8(m_namespaceName ? m_namespaceName : "", szNS, m_nameFlags) == 0) + { + delete[] utf8Name; + return true; + } + else + { + bRetry = TRUE; + } + } + } + delete[] utf8Name; + tkInner = tkOuter; + } + + goto Retry; + } + +} + +bool +SplitName::FindMethod(IMDInternalImport* mdInternal) +{ + if (m_memberToken != mdTokenNil) + { + return true; + } + + if (m_typeToken == mdTypeDefNil || + !m_memberName) + { + return false; + } + + ULONG32 EmptySig = 0; + + // XXX Microsoft - Compare using signature when available. + if (mdInternal->FindMethodDefUsingCompare(m_typeToken, + m_memberName, + (PCCOR_SIGNATURE)&EmptySig, + sizeof(EmptySig), + NULL, + NULL, + &m_memberToken) != S_OK) + { + m_memberToken = mdTokenNil; + return false; + } + + return true; +} + +bool +SplitName::FindField(IMDInternalImport* mdInternal) +{ + if (m_memberToken != mdTokenNil) + { + return true; + } + + if (m_typeToken == mdTypeDefNil || + !m_memberName || + m_params) + { + // Can't have params with a field. + return false; + } + + MetaEnum metaEnum; + + if (metaEnum.Start(mdInternal, mdtFieldDef, m_typeToken) != S_OK) + { + return false; + } + + return metaEnum.NextTokenByName(NULL, + m_memberName, + m_nameFlags, + &m_memberToken) == S_OK; +} + +HRESULT +SplitName::AllocAndSplitString(__in_opt PCWSTR fullName, + SplitSyntax syntax, + ULONG32 nameFlags, + ULONG32 memberDots, + SplitName** split) +{ + HRESULT status; + + if (nameFlags & ~(CLRDATA_BYNAME_CASE_SENSITIVE | + CLRDATA_BYNAME_CASE_INSENSITIVE)) + { + return E_INVALIDARG; + } + + *split = new (nothrow) SplitName(syntax, nameFlags, memberDots); + if (!*split) + { + return E_OUTOFMEMORY; + } + + if ((status = (*split)->SplitString(fullName)) != S_OK) + { + delete (*split); + return status; + } + + return S_OK; +} + +HRESULT +SplitName::CdStartMethod(__in_opt PCWSTR fullName, + ULONG32 nameFlags, + Module* mod, + mdTypeDef typeToken, + AppDomain* appDomain, + IXCLRDataAppDomain* pubAppDomain, + SplitName** splitRet, + CLRDATA_ENUM* handle) +{ + HRESULT status; + SplitName* split; + ULONG methDots = 0; + + *handle = TO_CDENUM(NULL); + + Retry: + if ((status = SplitName:: + AllocAndSplitString(fullName, SPLIT_METHOD, nameFlags, + methDots, &split)) != S_OK) + { + return status; + } + + if (typeToken == mdTypeDefNil) + { + if (!split->FindType(mod->GetMDImport())) + { + bool hasNamespace = split->m_namespaceName != NULL; + + delete split; + + // + // We may have a case where there's an + // explicitly implemented method which + // has dots in the name. If it's possible + // to move the method name dot split + // back, go ahead and retry that way. + // + + if (hasNamespace) + { + methDots++; + goto Retry; + } + + return E_INVALIDARG; + } + + typeToken = split->m_typeToken; + } + else + { + if (split->m_namespaceName || split->m_typeName) + { + delete split; + return E_INVALIDARG; + } + } + + if ((status = split->m_metaEnum. + Start(mod->GetMDImport(), mdtMethodDef, typeToken)) != S_OK) + { + delete split; + return status; + } + + split->m_metaEnum.m_appDomain = appDomain; + if (pubAppDomain) + { + split->m_metaEnum.m_appDomain = + ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain(); + } + split->m_module = mod; + + *handle = TO_CDENUM(split); + if (splitRet) + { + *splitRet = split; + } + return S_OK; +} + +HRESULT +SplitName::CdNextMethod(CLRDATA_ENUM* handle, + mdMethodDef* token) +{ + SplitName* split = FROM_CDENUM(SplitName, *handle); + if (!split) + { + return E_INVALIDARG; + } + + return split->m_metaEnum. + NextTokenByName(NULL, split->m_memberName, split->m_nameFlags, + token); +} + +HRESULT +SplitName::CdNextDomainMethod(CLRDATA_ENUM* handle, + AppDomain** appDomain, + mdMethodDef* token) +{ + SplitName* split = FROM_CDENUM(SplitName, *handle); + if (!split) + { + return E_INVALIDARG; + } + + return split->m_metaEnum. + NextDomainTokenByName(NULL, split->m_memberName, split->m_nameFlags, + appDomain, token); +} + +HRESULT +SplitName::CdStartField(__in_opt PCWSTR fullName, + ULONG32 nameFlags, + ULONG32 fieldFlags, + IXCLRDataTypeInstance* fromTypeInst, + TypeHandle typeHandle, + Module* mod, + mdTypeDef typeToken, + ULONG64 objBase, + Thread* tlsThread, + IXCLRDataTask* pubTlsThread, + AppDomain* appDomain, + IXCLRDataAppDomain* pubAppDomain, + SplitName** splitRet, + CLRDATA_ENUM* handle) +{ + HRESULT status; + SplitName* split; + + *handle = TO_CDENUM(NULL); + + if ((status = SplitName:: + AllocAndSplitString(fullName, + fullName ? SPLIT_FIELD : SPLIT_NO_NAME, + nameFlags, 0, + &split)) != S_OK) + { + return status; + } + + if (typeHandle.IsNull()) + { + if (typeToken == mdTypeDefNil) + { + if (!split->FindType(mod->GetMDImport())) + { + status = E_INVALIDARG; + goto Fail; + } + + typeToken = split->m_typeToken; + } + else + { + if (split->m_namespaceName || split->m_typeName) + { + status = E_INVALIDARG; + goto Fail; + } + } + + // With phased class loading, this may return a partially-loaded type + // @todo : does this matter? + typeHandle = mod->LookupTypeDef(split->m_typeToken); + if (typeHandle.IsNull()) + { + status = E_UNEXPECTED; + goto Fail; + } + } + + if ((status = InitFieldIter(&split->m_fieldEnum, + typeHandle, + true, + fieldFlags, + fromTypeInst)) != S_OK) + { + goto Fail; + } + + split->m_objBase = objBase; + split->m_tlsThread = tlsThread; + if (pubTlsThread) + { + split->m_tlsThread = ((ClrDataTask*)pubTlsThread)->GetThread(); + } + split->m_metaEnum.m_appDomain = appDomain; + if (pubAppDomain) + { + split->m_metaEnum.m_appDomain = + ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain(); + } + split->m_module = mod; + + *handle = TO_CDENUM(split); + if (splitRet) + { + *splitRet = split; + } + return S_OK; + + Fail: + delete split; + return status; +} + +HRESULT +SplitName::CdNextField(ClrDataAccess* dac, + CLRDATA_ENUM* handle, + IXCLRDataTypeDefinition** fieldType, + ULONG32* fieldFlags, + IXCLRDataValue** value, + ULONG32 nameBufRetLen, + ULONG32* nameLenRet, + __out_ecount_part_opt(nameBufRetLen, *nameLenRet) WCHAR nameBufRet[ ], + IXCLRDataModule** tokenScopeRet, + mdFieldDef* tokenRet) +{ + HRESULT status; + + SplitName* split = FROM_CDENUM(SplitName, *handle); + if (!split) + { + return E_INVALIDARG; + } + + FieldDesc* fieldDesc; + + while ((fieldDesc = split->m_fieldEnum.Next())) + { + if (split->m_syntax != SPLIT_NO_NAME) + { + LPCUTF8 fieldName; + if (FAILED(fieldDesc->GetName_NoThrow(&fieldName)) || + (split->Compare(split->m_memberName, fieldName) != 0)) + { + continue; + } + } + + split->m_lastField = fieldDesc; + + if (fieldFlags != NULL) + { + *fieldFlags = + GetTypeFieldValueFlags(fieldDesc->GetFieldTypeHandleThrowing(), + fieldDesc, + split->m_fieldEnum. + IsFieldFromParentClass() ? + CLRDATA_FIELD_IS_INHERITED : 0, + false); + } + + if ((nameBufRetLen != 0) || (nameLenRet != NULL)) + { + LPCUTF8 szFieldName; + status = fieldDesc->GetName_NoThrow(&szFieldName); + if (status != S_OK) + { + return status; + } + + status = ConvertUtf8( + szFieldName, + nameBufRetLen, + nameLenRet, + nameBufRet); + if (status != S_OK) + { + return status; + } + } + + if (tokenScopeRet && !value) + { + *tokenScopeRet = new (nothrow) + ClrDataModule(dac, fieldDesc->GetModule()); + if (!*tokenScopeRet) + { + return E_OUTOFMEMORY; + } + } + + if (tokenRet) + { + *tokenRet = fieldDesc->GetMemberDef(); + } + + if (fieldType) + { + TypeHandle fieldTypeHandle = fieldDesc->GetFieldTypeHandleThrowing(); + *fieldType = new (nothrow) + ClrDataTypeDefinition(dac, + fieldTypeHandle.GetModule(), + fieldTypeHandle.GetMethodTable()->GetCl(), + fieldTypeHandle); + if (!*fieldType && tokenScopeRet) + { + delete (ClrDataModule*)*tokenScopeRet; + } + return *fieldType ? S_OK : E_OUTOFMEMORY; + } + + if (value) + { + return ClrDataValue:: + NewFromFieldDesc(dac, + split->m_metaEnum.m_appDomain, + split->m_fieldEnum.IsFieldFromParentClass() ? + CLRDATA_VALUE_IS_INHERITED : 0, + fieldDesc, + split->m_objBase, + split->m_tlsThread, + NULL, + value, + nameBufRetLen, + nameLenRet, + nameBufRet, + tokenScopeRet, + tokenRet); + } + + return S_OK; + } + + return S_FALSE; +} + +HRESULT +SplitName::CdNextDomainField(ClrDataAccess* dac, + CLRDATA_ENUM* handle, + IXCLRDataValue** value) +{ + HRESULT status; + + SplitName* split = FROM_CDENUM(SplitName, *handle); + if (!split) + { + return E_INVALIDARG; + } + + if (split->m_metaEnum.m_appDomain) + { + // Use only the caller-provided app domain. + return CdNextField(dac, handle, NULL, NULL, value, + 0, NULL, NULL, NULL, NULL); + } + + // + // Splay fields across all app domains. + // + + for (;;) + { + if (!split->m_lastField) + { + // Need to fetch a field. + if ((status = CdNextField(dac, handle, NULL, NULL, NULL, + 0, NULL, NULL, NULL, NULL)) != S_OK) + { + return status; + } + + split->m_metaEnum.m_domainIter.Init(); + } + + if (split->m_metaEnum.m_domainIter.Next()) + { + break; + } + + split->m_lastField = NULL; + } + + return ClrDataValue:: + NewFromFieldDesc(dac, + split->m_metaEnum.m_domainIter.GetDomain(), + split->m_fieldEnum.IsFieldFromParentClass() ? + CLRDATA_VALUE_IS_INHERITED : 0, + split->m_lastField, + split->m_objBase, + split->m_tlsThread, + NULL, + value, + 0, + NULL, + NULL, + NULL, + NULL); +} + +HRESULT +SplitName::CdStartType(__in_opt PCWSTR fullName, + ULONG32 nameFlags, + Module* mod, + AppDomain* appDomain, + IXCLRDataAppDomain* pubAppDomain, + SplitName** splitRet, + CLRDATA_ENUM* handle) +{ + HRESULT status; + SplitName* split; + + *handle = TO_CDENUM(NULL); + + if ((status = SplitName:: + AllocAndSplitString(fullName, SPLIT_TYPE, nameFlags, 0, + &split)) != S_OK) + { + return status; + } + + if ((status = split->m_metaEnum. + Start(mod->GetMDImport(), mdtTypeDef, mdTokenNil)) != S_OK) + { + delete split; + return status; + } + + split->m_metaEnum.m_appDomain = appDomain; + if (pubAppDomain) + { + split->m_metaEnum.m_appDomain = + ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain(); + } + split->m_module = mod; + + *handle = TO_CDENUM(split); + if (splitRet) + { + *splitRet = split; + } + return S_OK; +} + +HRESULT +SplitName::CdNextType(CLRDATA_ENUM* handle, + mdTypeDef* token) +{ + SplitName* split = FROM_CDENUM(SplitName, *handle); + if (!split) + { + return E_INVALIDARG; + } + + return split->m_metaEnum. + NextTokenByName(split->m_namespaceName, split->m_typeName, + split->m_nameFlags, token); +} + +HRESULT +SplitName::CdNextDomainType(CLRDATA_ENUM* handle, + AppDomain** appDomain, + mdTypeDef* token) +{ + SplitName* split = FROM_CDENUM(SplitName, *handle); + if (!split) + { + return E_INVALIDARG; + } + + return split->m_metaEnum. + NextDomainTokenByName(split->m_namespaceName, split->m_typeName, + split->m_nameFlags, appDomain, token); +} + +//---------------------------------------------------------------------------- +// +// DacInstanceManager. +// +// Data retrieved from the target process is cached for two reasons: +// +// 1. It may be necessary to map from the host address back to the target +// address. For example, if any code uses a 'this' pointer or +// takes the address of a field the address has to be translated from +// host to target. This requires instances to be held as long as +// they may be referenced. +// +// 2. Data is often referenced multiple times so caching is an important +// performance advantage. +// +// Ideally we'd like to implement a simple page cache but this is +// complicated by the fact that user minidump memory can have +// arbitrary granularity and also that the member operator (->) +// needs to return a pointer to an object. That means that all of +// the data for an object must be sequential and cannot be split +// at page boundaries. +// +// Data can also be accessed with different sizes. For example, +// a base struct can be accessed, then cast to a derived struct and +// accessed again with the larger derived size. The cache must +// be able to replace data to maintain the largest amount of data +// touched. +// +// We keep track of each access and the recovered memory for it. +// A hash on target address allows quick access to instance data +// by target address. The data for each access has a header on it +// for bookkeeping purposes, so host address to target address translation +// is just a matter of backing up to the header and pulling the target +// address from it. Keeping each access separately allows easy +// replacement by larger accesses. +// +//---------------------------------------------------------------------------- + +DacInstanceManager::DacInstanceManager(void) + : m_unusedBlock(NULL) +{ + InitEmpty(); +} + +DacInstanceManager::~DacInstanceManager(void) +{ + // We are stopping debugging in this case, so don't save any block of memory. + // Otherwise, there will be a memory leak. + Flush(false); +} + +#if defined(DAC_HASHTABLE) +DAC_INSTANCE* +DacInstanceManager::Add(DAC_INSTANCE* inst) +{ + // Assert that we don't add NULL instances. This allows us to assert that found instances + // are not NULL in DacInstanceManager::Find + _ASSERTE(inst != NULL); + + DWORD nHash = DAC_INSTANCE_HASH(inst->addr); + HashInstanceKeyBlock* block = m_hash[nHash]; + + if (!block || block->firstElement == 0) + { + + HashInstanceKeyBlock* newBlock; + if (block) + { + newBlock = (HashInstanceKeyBlock*) new (nothrow) BYTE[HASH_INSTANCE_BLOCK_ALLOC_SIZE]; + } + else + { + // We allocate one big memory chunk that has a block for every index of the hash table to + // improve data locality and reduce the number of allocs. In most cases, a hash bucket will + // use only one block, so improving data locality across blocks (i.e. keeping the buckets of the + // hash table together) should help. + newBlock = (HashInstanceKeyBlock*) + ClrVirtualAlloc(NULL, HASH_INSTANCE_BLOCK_ALLOC_SIZE*NumItems(m_hash), MEM_COMMIT, PAGE_READWRITE); + } + if (!newBlock) + { + return NULL; + } + if (block) + { + // We add the newest block to the start of the list assuming that most accesses are for + // recently added elements. + newBlock->next = block; + m_hash[nHash] = newBlock; // The previously allocated block + newBlock->firstElement = HASH_INSTANCE_BLOCK_NUM_ELEMENTS; + block = newBlock; + } + else + { + for (DWORD j = 0; j < NumItems(m_hash); j++) + { + m_hash[j] = newBlock; + newBlock->next = NULL; // The previously allocated block + newBlock->firstElement = HASH_INSTANCE_BLOCK_NUM_ELEMENTS; + newBlock = (HashInstanceKeyBlock*) (((BYTE*) newBlock) + HASH_INSTANCE_BLOCK_ALLOC_SIZE); + } + block = m_hash[nHash]; + } + } + _ASSERTE(block->firstElement > 0); + block->firstElement--; + block->instanceKeys[block->firstElement].addr = inst->addr; + block->instanceKeys[block->firstElement].instance = inst; + + inst->next = NULL; + return inst; +} +#else //DAC_HASHTABLE +DAC_INSTANCE* +DacInstanceManager::Add(DAC_INSTANCE* inst) +{ + _ASSERTE(inst != NULL); +#ifdef _DEBUG + bool isInserted = (m_hash.find(inst->addr) == m_hash.end()); +#endif //_DEBUG + DAC_INSTANCE *(&target) = m_hash[inst->addr]; + _ASSERTE(!isInserted || target == NULL); + if( target != NULL ) + { + //This is necessary to preserve the semantics of Supersede, however, it + //is more or less dead code. + inst->next = target; + target = inst; + + //verify descending order + _ASSERTE(inst->size >= target->size); + } + else + { + target = inst; + } + + return inst; +} + +#endif // #if defined(DAC_HASHTABLE) + + +DAC_INSTANCE* +DacInstanceManager::Alloc(TADDR addr, ULONG32 size, DAC_USAGE_TYPE usage) +{ + SUPPORTS_DAC_HOST_ONLY; + DAC_INSTANCE_BLOCK* block; + DAC_INSTANCE* inst; + ULONG32 fullSize; + + static_assert_no_msg(sizeof(DAC_INSTANCE_BLOCK) <= DAC_INSTANCE_ALIGN); + static_assert_no_msg((sizeof(DAC_INSTANCE) & (DAC_INSTANCE_ALIGN - 1)) == 0); + + // + // All allocated instances must be kept alive as long + // as anybody may have a host pointer for one of them. + // This means that we cannot delete an arbitrary instance + // unless we are sure no pointers exist, which currently + // is not possible to determine, thus we just hold everything + // until a Flush. This greatly simplifies instance allocation + // as we can then just sweep through large blocks rather + // than having to use a real allocator. The only + // complication is that we need to keep all instance + // data aligned. We have guaranteed that the header will + // preserve alignment of the data following if the header + // is aligned, so as long as we round up all allocations + // to a multiple of the alignment size everything just works. + // + + fullSize = (size + DAC_INSTANCE_ALIGN - 1) & ~(DAC_INSTANCE_ALIGN - 1); + _ASSERTE(fullSize && fullSize <= 0xffffffff - 2 * sizeof(*inst)); + fullSize += sizeof(*inst); + + // + // Check for an existing block with space. + // + + for (block = m_blocks; block; block = block->next) + { + if (fullSize <= block->bytesFree) + { + break; + } + } + + if (!block) + { + // + // No existing block has enough space, so allocate a new + // one if necessary and link it in. We know we're allocating large + // blocks so directly VirtualAlloc. We save one block through a + // flush so that we spend less time allocating/deallocating. + // + + ULONG32 blockSize = fullSize + DAC_INSTANCE_ALIGN; + if (blockSize < DAC_INSTANCE_BLOCK_ALLOCATION) + { + blockSize = DAC_INSTANCE_BLOCK_ALLOCATION; + } + + // If we have a saved block and it's large enough, use it. + block = m_unusedBlock; + if ((block != NULL) && + ((block->bytesUsed + block->bytesFree) >= blockSize)) + { + m_unusedBlock = NULL; + + // Right now, we're locked to DAC_INSTANCE_BLOCK_ALLOCATION but + // that might change in the future if we decide to do something + // else with the size guarantee in code:DacInstanceManager::FreeAllBlocks + blockSize = block->bytesUsed + block->bytesFree; + } + else + { + block = (DAC_INSTANCE_BLOCK*) + ClrVirtualAlloc(NULL, blockSize, MEM_COMMIT, PAGE_READWRITE); + } + + if (!block) + { + return NULL; + } + + // Keep the first aligned unit for the block header. + block->bytesUsed = DAC_INSTANCE_ALIGN; + block->bytesFree = blockSize - DAC_INSTANCE_ALIGN; + + block->next = m_blocks; + m_blocks = block; + + m_blockMemUsage += blockSize; + } + + inst = (DAC_INSTANCE*)((PBYTE)block + block->bytesUsed); + block->bytesUsed += fullSize; + _ASSERTE(block->bytesFree >= fullSize); + block->bytesFree -= fullSize; + + inst->next = NULL; + inst->addr = addr; + inst->size = size; + inst->sig = DAC_INSTANCE_SIG; + inst->usage = usage; + inst->enumMem = 0; + inst->MDEnumed = 0; + + m_numInst++; + m_instMemUsage += fullSize; + return inst; +} + +void +DacInstanceManager::ReturnAlloc(DAC_INSTANCE* inst) +{ + SUPPORTS_DAC_HOST_ONLY; + DAC_INSTANCE_BLOCK* block; + DAC_INSTANCE_BLOCK * pPrevBlock; + ULONG32 fullSize; + + // + // This special routine handles cleanup in + // cases where an instances has been allocated + // but must be returned due to a following error. + // The given instance must be the last instance + // in an existing block. + // + + fullSize = + ((inst->size + DAC_INSTANCE_ALIGN - 1) & ~(DAC_INSTANCE_ALIGN - 1)) + + sizeof(*inst); + + pPrevBlock = NULL; + for (block = m_blocks; block; pPrevBlock = block, block = block->next) + { + if ((PBYTE)inst == (PBYTE)block + (block->bytesUsed - fullSize)) + { + break; + } + } + + if (!block) + { + return; + } + + block->bytesUsed -= fullSize; + block->bytesFree += fullSize; + m_numInst--; + m_instMemUsage -= fullSize; + + // If the block is empty after returning the specified instance, that means this block was newly created + // when this instance was allocated. We have seen cases where we are asked to allocate a + // large chunk of memory only to fail to read the memory from a dump later on, i.e. when both the target + // address and the size are invalid. If we keep the allocation, we'll grow the VM size unnecessarily. + // Thus, release a block if it's empty and if it's not the default size (to avoid thrashing memory). + // See Dev10 Dbug 812112 for more information. + if ((block->bytesUsed == DAC_INSTANCE_ALIGN) && + ((block->bytesFree + block->bytesUsed) != DAC_INSTANCE_BLOCK_ALLOCATION)) + { + // The empty block is at the beginning of the list. + if (pPrevBlock == NULL) + { + m_blocks = block->next; + } + else + { + _ASSERTE(pPrevBlock->next == block); + pPrevBlock->next = block->next; + } + ClrVirtualFree(block, 0, MEM_RELEASE); + } +} + + +#if defined(DAC_HASHTABLE) +DAC_INSTANCE* +DacInstanceManager::Find(TADDR addr) +{ + +#if defined(DAC_MEASURE_PERF) + unsigned _int64 nStart, nEnd; + g_nFindCalls++; + nStart = GetCycleCount(); +#endif // #if defined(DAC_MEASURE_PERF) + + HashInstanceKeyBlock* block = m_hash[DAC_INSTANCE_HASH(addr)]; + +#if defined(DAC_MEASURE_PERF) + nEnd = GetCycleCount(); + g_nFindHashTotalTime += nEnd - nStart; +#endif // #if defined(DAC_MEASURE_PERF) + + while (block) + { + DWORD nIndex = block->firstElement; + for (; nIndex < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; nIndex++) + { + if (block->instanceKeys[nIndex].addr == addr) + { + #if defined(DAC_MEASURE_PERF) + nEnd = GetCycleCount(); + g_nFindHits++; + g_nFindTotalTime += nEnd - nStart; + if (g_nStackWalk) g_nFindStackTotalTime += nEnd - nStart; +#endif // #if defined(DAC_MEASURE_PERF) + + DAC_INSTANCE* inst = block->instanceKeys[nIndex].instance; + + // inst should not be NULL even if the address was superseded. We search + // the entries in the reverse order they were added. So we should have + // found the superseding entry before this one. (Of course, if a NULL instance + // has been added, this assert is meaningless. DacInstanceManager::Add + // asserts that NULL instances aren't added.) + + _ASSERTE(inst != NULL); + + return inst; + } + } + block = block->next; + } + +#if defined(DAC_MEASURE_PERF) + nEnd = GetCycleCount(); + g_nFindFails++; + g_nFindTotalTime += nEnd - nStart; + if (g_nStackWalk) g_nFindStackTotalTime += nEnd - nStart; +#endif // #if defined(DAC_MEASURE_PERF) + + return NULL; +} +#else //DAC_HASHTABLE +DAC_INSTANCE* +DacInstanceManager::Find(TADDR addr) +{ + DacInstanceHashIterator iter = m_hash.find(addr); + if( iter == m_hash.end() ) + { + return NULL; + } + else + { + return iter->second; + } +} +#endif // if defined(DAC_HASHTABLE) + +HRESULT +DacInstanceManager::Write(DAC_INSTANCE* inst, bool throwEx) +{ + HRESULT status; + + if (inst->usage == DAC_VPTR) + { + // Skip over the host-side vtable pointer when + // writing back. + status = DacWriteAll(inst->addr + sizeof(TADDR), + (PBYTE)(inst + 1) + sizeof(PVOID), + inst->size - sizeof(TADDR), + throwEx); + } + else + { + // Write the whole instance back. + status = DacWriteAll(inst->addr, inst + 1, inst->size, throwEx); + } + + return status; +} + +#if defined(DAC_HASHTABLE) +void +DacInstanceManager::Supersede(DAC_INSTANCE* inst) +{ + _ASSERTE(inst != NULL); + + // + // This instance has been superseded by a larger + // one and so must be removed from the hash. However, + // code may be holding the instance pointer so it + // can't just be deleted. Put it on a list for + // later cleanup. + // + + HashInstanceKeyBlock* block = m_hash[DAC_INSTANCE_HASH(inst->addr)]; + while (block) + { + DWORD nIndex = block->firstElement; + for (; nIndex < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; nIndex++) + { + if (block->instanceKeys[nIndex].instance == inst) + { + block->instanceKeys[nIndex].instance = NULL; + break; + } + } + if (nIndex < HASH_INSTANCE_BLOCK_NUM_ELEMENTS) + { + break; + } + block = block->next; + } + + AddSuperseded(inst); +} +#else //DAC_HASHTABLE +void +DacInstanceManager::Supersede(DAC_INSTANCE* inst) +{ + _ASSERTE(inst != NULL); + + // + // This instance has been superseded by a larger + // one and so must be removed from the hash. However, + // code may be holding the instance pointer so it + // can't just be deleted. Put it on a list for + // later cleanup. + // + + DacInstanceHashIterator iter = m_hash.find(inst->addr); + if( iter == m_hash.end() ) + return; + + DAC_INSTANCE** bucket = &(iter->second); + DAC_INSTANCE* cur = *bucket; + DAC_INSTANCE* prev = NULL; + //walk through the chain looking for this particular instance + while (cur) + { + if (cur == inst) + { + if (!prev) + { + *bucket = inst->next; + } + else + { + prev->next = inst->next; + } + break; + } + + prev = cur; + cur = cur->next; + } + + AddSuperseded(inst); +} +#endif // if defined(DAC_HASHTABLE) + +// This is the default Flush() called when the DAC cache is invalidated, +// e.g. when we continue the debuggee process. In this case, we want to +// save one block of memory to avoid thrashing. See the usage of m_unusedBlock +// for more information. +void DacInstanceManager::Flush(void) +{ + Flush(true); +} + +void DacInstanceManager::Flush(bool fSaveBlock) +{ + SUPPORTS_DAC_HOST_ONLY; + + // + // All allocated memory is in the block + // list, so just free the blocks and + // forget all the internal pointers. + // + + for (;;) + { + FreeAllBlocks(fSaveBlock); + + DAC_INSTANCE_PUSH* push = m_instPushed; + if (!push) + { + break; + } + + m_instPushed = push->next; + m_blocks = push->blocks; + delete push; + } + + // If we are not saving any memory blocks, then clear the saved buffer block (if any) as well. + if (!fSaveBlock) + { + if (m_unusedBlock != NULL) + { + ClrVirtualFree(m_unusedBlock, 0, MEM_RELEASE); + m_unusedBlock = NULL; + } + } + +#if defined(DAC_HASHTABLE) + for (int i = NumItems(m_hash) - 1; i >= 0; i--) + { + HashInstanceKeyBlock* block = m_hash[i]; + HashInstanceKeyBlock* next; + while (block) + { + next = block->next; + if (next) + { + delete [] block; + } + else if (i == 0) + { + ClrVirtualFree(block, 0, MEM_RELEASE); + } + block = next; + } + } +#else //DAC_HASHTABLE + m_hash.clear(); +#endif //DAC_HASHTABLE + + InitEmpty(); +} + +#if defined(DAC_HASHTABLE) +void +DacInstanceManager::ClearEnumMemMarker(void) +{ + ULONG i; + DAC_INSTANCE* inst; + + for (i = 0; i < NumItems(m_hash); i++) + { + HashInstanceKeyBlock* block = m_hash[i]; + while (block) + { + DWORD j; + for (j = block->firstElement; j < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; j++) + { + inst = block->instanceKeys[j].instance; + if (inst != NULL) + { + inst->enumMem = 0; + } + } + block = block->next; + } + } + for (inst = m_superseded; inst; inst = inst->next) + { + inst->enumMem = 0; + } +} +#else //DAC_HASHTABLE +void +DacInstanceManager::ClearEnumMemMarker(void) +{ + ULONG i; + DAC_INSTANCE* inst; + + DacInstanceHashIterator end = m_hash.end(); + /* REVISIT_TODO Fri 10/20/2006 + * This might have an issue, since it might miss chained entries off of + * ->next. However, ->next is going away, and for all intents and + * purposes, this never happens. +*/ + for( DacInstanceHashIterator cur = m_hash.begin(); cur != end; ++cur ) + { + cur->second->enumMem = 0; + } + + for (inst = m_superseded; inst; inst = inst->next) + { + inst->enumMem = 0; + } +} +#endif // if defined(DAC_HASHTABLE) + + +#if defined(DAC_HASHTABLE) +// +// +// Iterating through all of the hash entry and report the memory +// instance to minidump +// +// This function returns the total number of bytes that it reported. +// +// +UINT +DacInstanceManager::DumpAllInstances( + ICLRDataEnumMemoryRegionsCallback *pCallBack) // memory report call back +{ + ULONG i; + DAC_INSTANCE* inst; + UINT cbTotal = 0; + +#if defined(DAC_MEASURE_PERF) + FILE* fp = fopen("c:\\dumpLog.txt", "a"); + int total = 0; +#endif // #if defined(DAC_MEASURE_PERF) + + for (i = 0; i < NumItems(m_hash); i++) + { + +#if defined(DAC_MEASURE_PERF) + int numInBucket = 0; +#endif // #if defined(DAC_MEASURE_PERF) + + HashInstanceKeyBlock* block = m_hash[i]; + while (block) + { + DWORD j; + for (j = block->firstElement; j < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; j++) + { + inst = block->instanceKeys[j].instance; + + // Only report those we intended to. + // So far, only metadata is excluded! + // + if (inst && inst->noReport == 0) + { + cbTotal += inst->size; + HRESULT hr = pCallBack->EnumMemoryRegion(TO_CDADDR(inst->addr), inst->size); + if (hr == COR_E_OPERATIONCANCELED) + { + ThrowHR(hr); + } + } + +#if defined(DAC_MEASURE_PERF) + if (inst) + { + numInBucket++; + } +#endif // #if defined(DAC_MEASURE_PERF) + } + block = block->next; + } + + #if defined(DAC_MEASURE_PERF) + fprintf(fp, "%4d: %4d%s", i, numInBucket, (i+1)%5? "; " : "\n"); + total += numInBucket; +#endif // #if defined(DAC_MEASURE_PERF) + + } + +#if defined(DAC_MEASURE_PERF) + fprintf(fp, "\n\nTotal entries: %d\n\n", total); + fclose(fp); +#endif // #if defined(DAC_MEASURE_PERF) + + return cbTotal; + +} +#else //DAC_HASHTABLE +// +// +// Iterating through all of the hash entry and report the memory +// instance to minidump +// +// This function returns the total number of bytes that it reported. +// +// +UINT +DacInstanceManager::DumpAllInstances( + ICLRDataEnumMemoryRegionsCallback *pCallBack) // memory report call back +{ + SUPPORTS_DAC_HOST_ONLY; + + DAC_INSTANCE* inst; + UINT cbTotal = 0; + +#if defined(DAC_MEASURE_PERF) + FILE* fp = fopen("c:\\dumpLog.txt", "a"); +#endif // #if defined(DAC_MEASURE_PERF) + +#if defined(DAC_MEASURE_PERF) + int numInBucket = 0; +#endif // #if defined(DAC_MEASURE_PERF) + + DacInstanceHashIterator end = m_hash.end(); + for (DacInstanceHashIterator cur = m_hash.begin(); end != cur; ++cur) + { + inst = cur->second; + + // Only report those we intended to. + // So far, only metadata is excluded! + // + if (inst->noReport == 0) + { + cbTotal += inst->size; + HRESULT hr = pCallBack->EnumMemoryRegion(TO_CDADDR(inst->addr), inst->size); + if (hr == COR_E_OPERATIONCANCELED) + { + ThrowHR(hr); + } + } + +#if defined(DAC_MEASURE_PERF) + numInBucket++; +#endif // #if defined(DAC_MEASURE_PERF) + } + +#if defined(DAC_MEASURE_PERF) + fprintf(fp, "\n\nTotal entries: %d\n\n", numInBucket); + fclose(fp); +#endif // #if defined(DAC_MEASURE_PERF) + + return cbTotal; + +} +#endif // if defined(DAC_HASHTABLE) + +DAC_INSTANCE_BLOCK* +DacInstanceManager::FindInstanceBlock(DAC_INSTANCE* inst) +{ + for (DAC_INSTANCE_BLOCK* block = m_blocks; block; block = block->next) + { + if ((PBYTE)inst >= (PBYTE)block && + (PBYTE)inst < (PBYTE)block + block->bytesUsed) + { + return block; + } + } + + return NULL; +} + +// If fSaveBlock is false, free all blocks of allocated memory. Otherwise, +// free all blocks except the one we save to avoid thrashing memory. +// Callers very frequently flush repeatedly with little memory needed in DAC +// so this avoids wasteful repeated allocations/deallocations. +// There is a very unlikely case that we'll have allocated an extremely large +// block; if this is the only block we will save none since this block will +// remain allocated. +void +DacInstanceManager::FreeAllBlocks(bool fSaveBlock) +{ + DAC_INSTANCE_BLOCK* block; + + while ((block = m_blocks)) + { + m_blocks = block->next; + + // If we haven't saved our single block yet and this block is the default size + // then we will save it instead of freeing it. This avoids saving an unnecessarily large + // memory block. + // Do *NOT* trash the byte counts. code:DacInstanceManager::Alloc + // depends on them being correct when checking to see if a block is large enough. + if (fSaveBlock && + (m_unusedBlock == NULL) && + ((block->bytesFree + block->bytesUsed) == DAC_INSTANCE_BLOCK_ALLOCATION)) + { + // Just to avoid confusion, since we're keeping it around. + block->next = NULL; + m_unusedBlock = block; + } + else + { + ClrVirtualFree(block, 0, MEM_RELEASE); + } + } +} + +//---------------------------------------------------------------------------- +// +// DacStreamManager. +// +//---------------------------------------------------------------------------- + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + +namespace serialization { namespace bin { + + //======================================================================== + // Support functions for binary serialization of simple types to a buffer: + // - raw_size() returns the size in bytes of the binary representation + // of a value. + // - raw_serialize() copies the binary representation of a value into a + // buffer. + // - raw_deserialize() generates a value from its binary representation + // in a buffer. + // Beyond simple types the APIs below support SString instances. SStrings + // are stored as UTF8 strings. + //======================================================================== + + static const size_t ErrOverflow = (size_t)(-1); + +#ifndef FEATURE_PAL + + // Template class is_blittable + template <typename _Ty, typename Enable = void> + struct is_blittable + : std::false_type + { // determines whether _Ty is blittable + }; + + template <typename _Ty> + struct is_blittable<_Ty, typename std::enable_if<std::is_arithmetic<_Ty>::value>::type> + : std::true_type + { // determines whether _Ty is blittable + }; + + // allow types to declare themselves blittable by including a static bool + // member "is_blittable". + template <typename _Ty> + struct is_blittable<_Ty, typename std::enable_if<_Ty::is_blittable>::type> + : std::true_type + { // determines whether _Ty is blittable + }; + + + //======================================================================== + // serialization::bin::Traits<T> enables binary serialization and + // deserialization of instances of T. + //======================================================================== + + // + // General specialization for non-blittable types - must be overridden + // for each specific non-blittable type. + // + template <typename T, typename Enable = void> + class Traits + { + public: + static FORCEINLINE size_t + raw_size(const T & val) + { + static_assert(false, "Non-blittable types need explicit specializations"); + } + }; + + // + // General type trait supporting serialization/deserialization of blittable + // type arguments (as defined by the is_blittable<> type traits above). + // + template <typename T> + class Traits<T, typename std::enable_if<is_blittable<T>::value>::type> + { +#else // FEATURE_PAL + template <typename T> + class Traits + { +#endif // !FEATURE_PAL + public: + // + // raw_size() returns the size in bytes of the binary representation of a + // value. + // + static FORCEINLINE size_t + raw_size(const T & val) + { + return sizeof(T); + } + + // + // raw_serialize() copies the binary representation of a value into a + // "dest" buffer that has "destSize" bytes available. + // Returns raw_size(val), or ErrOverflow if the buffer does not have + // enough space to accommodate "val". + // + static FORCEINLINE size_t + raw_serialize(BYTE* dest, size_t destSize, const T & val) + { + size_t cnt = raw_size(val); + + if (destSize < cnt) + { + return ErrOverflow; + } + + memcpy_s(dest, destSize, &val, cnt); + + return cnt; + } + + // + // raw_deserialize() generates a value "val" from its binary + // representation in a buffer "src". + // Returns raw_size(val), or ErrOverflow if the buffer does not have + // enough space to accommodate "val". + // + static FORCEINLINE size_t + raw_deserialize(T & val, const BYTE* src, size_t srcSize) + { + size_t cnt = raw_size(*(T*)src); + + if (srcSize < cnt) + { + return ErrOverflow; + } + + memcpy_s(&val, cnt, src, cnt); + + return cnt; + } + + }; + + // + // Specialization for UTF8 strings + // + template<> + class Traits<LPCUTF8> + { + public: + static FORCEINLINE size_t + raw_size(const LPCUTF8 & val) + { + return strlen(val) + 1; + } + + static FORCEINLINE size_t + raw_serialize(BYTE* dest, size_t destSize, const LPCUTF8 & val) + { + size_t cnt = raw_size(val); + + if (destSize < cnt) + { + return ErrOverflow; + } + + memcpy_s(dest, destSize, &val, cnt); + + return cnt; + } + + static FORCEINLINE size_t + raw_deserialize(LPCUTF8 & val, const BYTE* src, size_t srcSize) + { + size_t cnt = strnlen((LPCUTF8)src, srcSize) + 1; + + // assert we found a NULL terminated string at "src" + if (srcSize < cnt) + { + return ErrOverflow; + } + + // we won't allocate another buffer for this string + val = (LPCUTF8)src; + + return cnt; + } + + }; + + // + // Specialization for SString. + // SString serialization/deserialization is performed to/from a UTF8 + // string. + // + template<> + class Traits<SString> + { + public: + static FORCEINLINE size_t + raw_size(const SString & val) + { + StackSString s; + val.ConvertToUTF8(s); + // make sure to include the NULL terminator + return s.GetCount() + 1; + } + + static FORCEINLINE size_t + raw_serialize(BYTE* dest, size_t destSize, const SString & val) + { + // instead of calling raw_size() we inline it here, so we can reuse + // the UTF8 string obtained below as an argument to memcpy. + + StackSString s; + val.ConvertToUTF8(s); + // make sure to include the NULL terminator + size_t cnt = s.GetCount() + 1; + + if (destSize < cnt) + { + return ErrOverflow; + } + + memcpy_s(dest, destSize, s.GetUTF8NoConvert(), cnt); + + return cnt; + } + + static FORCEINLINE size_t + raw_deserialize(SString & val, const BYTE* src, size_t srcSize) + { + size_t cnt = strnlen((LPCUTF8)src, srcSize) + 1; + + // assert we found a NULL terminated string at "src" + if (srcSize < cnt) + { + return ErrOverflow; + } + + // a literal SString avoids a new allocation + copy + SString sUtf8(SString::Utf8Literal, (LPCUTF8) src); + sUtf8.ConvertToUnicode(val); + + return cnt; + } + + }; + +#ifndef FEATURE_PAL + // + // Specialization for SString-derived classes (like SStrings) + // + template<typename T> + class Traits<T, typename std::enable_if<std::is_base_of<SString, T>::value>::type> + : public Traits<SString> + { + }; +#endif // !FEATURE_PAL + + // + // Convenience functions to allow argument type deduction + // + template <typename T> FORCEINLINE + size_t raw_size(const T & val) + { return Traits<T>::raw_size(val); } + + template <typename T> FORCEINLINE + size_t raw_serialize(BYTE* dest, size_t destSize, const T & val) + { return Traits<T>::raw_serialize(dest, destSize, val); } + + template <typename T> FORCEINLINE + size_t raw_deserialize(T & val, const BYTE* src, size_t srcSize) + { return Traits<T>::raw_deserialize(val, src, srcSize); } + + + enum StreamBuffState + { + sbsOK, + sbsUnrecoverable, + sbsOOM = sbsUnrecoverable, + }; + + // + // OStreamBuff - Manages writing to an output buffer + // + class OStreamBuff + { + public: + OStreamBuff(BYTE * _buff, size_t _buffsize) + : buffsize(_buffsize) + , buff(_buff) + , crt(0) + , sbs(sbsOK) + { } + + template <typename T> + OStreamBuff& operator << (const T & val) + { + if (sbs >= sbsUnrecoverable) + return *this; + + size_t cnt = raw_serialize(buff+crt, buffsize-crt, val); + if (cnt == ErrOverflow) + { + sbs = sbsOOM; + } + else + { + crt += cnt; + } + + return *this; + } + + inline size_t GetPos() const + { + return crt; + } + + inline BOOL operator!() const + { + return sbs >= sbsUnrecoverable; + } + + inline StreamBuffState State() const + { + return sbs; + } + + private: + size_t buffsize; // size of buffer + BYTE* buff; // buffer to stream to + size_t crt; // current offset in buffer + StreamBuffState sbs; // current state + }; + + + // + // OStreamBuff - Manages reading from an input buffer + // + class IStreamBuff + { + public: + IStreamBuff(const BYTE* _buff, size_t _buffsize) + : buffsize(_buffsize) + , buff(_buff) + , crt(0) + , sbs(sbsOK) + { } + + template <typename T> + IStreamBuff& operator >> (T & val) + { + if (sbs >= sbsUnrecoverable) + return *this; + + size_t cnt = raw_deserialize(val, buff+crt, buffsize-crt); + if (cnt == ErrOverflow) + { + sbs = sbsOOM; + } + else + { + crt += cnt; + } + + return *this; + } + + inline size_t GetPos() const + { + return crt; + } + + inline BOOL operator!() const + { + return sbs >= sbsUnrecoverable; + } + + inline StreamBuffState State() const + { + return sbs; + } + + private: + size_t buffsize; // size of buffer + const BYTE * buff; // buffer to read from + size_t crt; // current offset in buffer + StreamBuffState sbs; // current state + }; + +} } + +using serialization::bin::StreamBuffState; +using serialization::bin::IStreamBuff; +using serialization::bin::OStreamBuff; + + +// Callback function type used by DacStreamManager to coordinate +// amount of available memory between multiple streamable data +// structures (e.g. DacEENamesStreamable) +typedef bool (*Reserve_Fnptr)(DWORD size, void * writeState); + + +// +// DacEENamesStreamable +// Stores EE struct* -> Name mappings and streams them to a +// streambuf when asked +// +class DacEENamesStreamable +{ +private: + // the hash map storing the interesting mappings of EE* -> Names + MapSHash< TADDR, SString, + NoRemoveSHashTraits < + NonDacAwareSHashTraits< MapSHashTraits <TADDR, SString> > + > > m_hash; + + Reserve_Fnptr m_reserveFn; + void *m_writeState; + +private: + // signature value in the header in stream + static const DWORD sig = 0x614e4545; // "EENa" - EE Name + + // header in stream + struct StreamHeader + { + DWORD sig; // 0x614e4545 == "EENa" + DWORD cnt; // count of entries + + static const bool is_blittable = true; + }; + +public: + DacEENamesStreamable() + : m_reserveFn(NULL) + , m_writeState(NULL) + {} + + // Ensures the instance is ready for caching data and later writing + // its map entries to an OStreamBuff. + bool PrepareStreamForWriting(Reserve_Fnptr pfn, void * writeState) + { + _ASSERTE(pfn != NULL && writeState != NULL); + m_reserveFn = pfn; + m_writeState = writeState; + + DWORD size = (DWORD) sizeof(StreamHeader); + + // notify owner to reserve space for a StreamHeader + return m_reserveFn(size, m_writeState); + } + + // Adds a new mapping from an EE struct pointer (e.g. MethodDesc*) to + // its name + bool AddEEName(TADDR taEE, const SString & eeName) + { + _ASSERTE(m_reserveFn != NULL && m_writeState != NULL); + + // as a micro-optimization convert to Utf8 here as both raw_size and + // raw_serialize are optimized for Utf8... + StackSString seeName; + eeName.ConvertToUTF8(seeName); + + DWORD size = (DWORD)(serialization::bin::raw_size(taEE) + + serialization::bin::raw_size(seeName)); + + // notify owner of the amount of space needed in the buffer + if (m_reserveFn(size, m_writeState)) + { + // if there's still space cache the entry in m_hash + m_hash.AddOrReplace(KeyValuePair<TADDR, SString>(taEE, seeName)); + return true; + } + else + { + return false; + } + } + + // Finds an EE name from a target address of an EE struct (e.g. + // MethodDesc*) + bool FindEEName(TADDR taEE, SString & eeName) const + { + return m_hash.Lookup(taEE, &eeName) == TRUE; + } + + void Clear() + { + m_hash.RemoveAll(); + } + + // Writes a header and the hash entries to an OStreamBuff + HRESULT StreamTo(OStreamBuff &out) const + { + StreamHeader hdr; + hdr.sig = sig; + hdr.cnt = (DWORD) m_hash.GetCount(); + + out << hdr; + + auto end = m_hash.End(); + for (auto cur = m_hash.Begin(); end != cur; ++cur) + { + out << cur->Key() << cur->Value(); + if (!out) + return E_FAIL; + } + + return S_OK; + } + + // Reads a header and the hash entries from an IStreamBuff + HRESULT StreamFrom(IStreamBuff &in) + { + StreamHeader hdr; + + DWORD _sig; + in >> hdr; // in >> hdr.sig >> hdr.cnt; + + if (hdr.sig != sig) + return E_FAIL; + + for (size_t i = 0; i < hdr.cnt; ++i) + { + TADDR taEE; + SString eeName; + in >> taEE >> eeName; + + if (!in) + return E_FAIL; + + m_hash.AddOrReplace(KeyValuePair<TADDR, SString>(taEE, eeName)); + } + + return S_OK; + } + +}; + +//================================================================================ +// This class enables two scenarios: +// 1. When debugging a triage/mini-dump the class is initialized with a valid +// buffer in taMiniMetaDataBuff. Afterwards one can call MdCacheGetEEName to +// retrieve the name associated with a MethodDesc*. +// 2. When generating a dump one must follow this sequence: +// a. Initialize the DacStreamManager passing a valid (if the current +// debugging target is a triage/mini-dump) or empty buffer (if the +// current target is a live processa full or a heap dump) +// b. Call PrepareStreamsForWriting() before starting enumerating any memory +// c. Call MdCacheAddEEName() anytime we enumerate an EE structure of interest +// d. Call EnumStreams() as the last action in the memory enumeration method. +// +class DacStreamManager +{ +public: + enum eReadOrWrite + { + eNone, // the stream doesn't exist (target is a live process/full/heap dump) + eRO, // the stream exists and we've read it (target is triage/mini-dump) + eWO, // the stream doesn't exist but we're creating it + // (e.g. to save a minidump from the current debugging session) + eRW // the stream exists but we're generating another triage/mini-dump + }; + + static const DWORD sig = 0x6d727473; // 'strm' + + struct StreamsHeader + { + DWORD dwSig; // 0x6d727473 == "strm" + DWORD dwTotalSize; // total size in bytes + DWORD dwCntStreams; // number of streams (currently 1) + + static const bool is_blittable = true; + }; + + DacStreamManager(TADDR miniMetaDataBuffAddress, DWORD miniMetaDataBuffSizeMax) + : m_MiniMetaDataBuffAddress(miniMetaDataBuffAddress) + , m_MiniMetaDataBuffSizeMax(miniMetaDataBuffSizeMax) + , m_rawBuffer(NULL) + , m_cbAvailBuff(0) + , m_rw(eNone) + , m_bStreamsRead(FALSE) + , m_EENames() + { + Initialize(); + } + + ~DacStreamManager() + { + if (m_rawBuffer != NULL) + { + delete [] m_rawBuffer; + } + } + + bool PrepareStreamsForWriting() + { + if (m_rw == eNone) + m_rw = eWO; + else if (m_rw == eRO) + m_rw = eRW; + else if (m_rw == eRW) + /* nothing */; + else // m_rw == eWO + { + // this is a second invocation from a possibly live process + // clean up the map since the callstacks/exceptions may be different + m_EENames.Clear(); + } + + // update available count based on the header and footer sizes + if (m_MiniMetaDataBuffSizeMax < sizeof(StreamsHeader)) + return false; + + m_cbAvailBuff = m_MiniMetaDataBuffSizeMax - sizeof(StreamsHeader); + + // update available count based on each stream's initial needs + if (!m_EENames.PrepareStreamForWriting(&ReserveInBuffer, this)) + return false; + + return true; + } + + bool MdCacheAddEEName(TADDR taEEStruct, const SString& name) + { + // don't cache unless we enabled "W"riting from a target that does not + // already have a stream yet + if (m_rw != eWO) + return false; + + m_EENames.AddEEName(taEEStruct, name); + return true; + } + + HRESULT EnumStreams(IN CLRDataEnumMemoryFlags flags) + { + _ASSERTE(flags == CLRDATA_ENUM_MEM_MINI || flags == CLRDATA_ENUM_MEM_TRIAGE); + _ASSERTE(m_rw == eWO || m_rw == eRW); + + DWORD cbWritten = 0; + + if (m_rw == eWO) + { + // only dump the stream is it wasn't already present in the target + DumpAllStreams(&cbWritten); + } + else + { + cbWritten = m_MiniMetaDataBuffSizeMax; + } + + DacEnumMemoryRegion(m_MiniMetaDataBuffAddress, cbWritten, false); + DacUpdateMemoryRegion(m_MiniMetaDataBuffAddress, cbWritten, m_rawBuffer); + + return S_OK; + } + + bool MdCacheGetEEName(TADDR taEEStruct, SString & eeName) + { + if (!m_bStreamsRead) + { + ReadAllStreams(); + } + + if (m_rw == eNone || m_rw == eWO) + { + return false; + } + + return m_EENames.FindEEName(taEEStruct, eeName); + } + +private: + HRESULT Initialize() + { + _ASSERTE(m_rw == eNone); + _ASSERTE(m_rawBuffer == NULL); + + HRESULT hr = S_OK; + + StreamsHeader hdr; + DacReadAll(dac_cast<TADDR>(m_MiniMetaDataBuffAddress), + &hdr, sizeof(hdr), true); + + // when the DAC looks at a triage dump or minidump generated using + // a "minimetadata" enabled DAC, buff will point to a serialized + // representation of a methoddesc->method name hashmap. + if (hdr.dwSig == sig) + { + m_rw = eRO; + m_MiniMetaDataBuffSizeMax = hdr.dwTotalSize; + hr = S_OK; + } + else + // when the DAC initializes this for the case where the target is + // (a) a live process, or (b) a full dump, buff will point to a + // zero initialized memory region (allocated w/ VirtualAlloc) + if (hdr.dwSig == 0 && hdr.dwTotalSize == 0 && hdr.dwCntStreams == 0) + { + hr = S_OK; + } + // otherwise we may have some memory corruption. treat this as + // a liveprocess/full dump + else + { + hr = S_FALSE; + } + + BYTE * buff = new BYTE[m_MiniMetaDataBuffSizeMax]; + DacReadAll(dac_cast<TADDR>(m_MiniMetaDataBuffAddress), + buff, m_MiniMetaDataBuffSizeMax, true); + + m_rawBuffer = buff; + + return hr; + } + + HRESULT DumpAllStreams(DWORD * pcbWritten) + { + _ASSERTE(m_rw == eWO); + + HRESULT hr = S_OK; + + OStreamBuff out(m_rawBuffer, m_MiniMetaDataBuffSizeMax); + + // write header + StreamsHeader hdr; + hdr.dwSig = sig; + hdr.dwTotalSize = m_MiniMetaDataBuffSizeMax-m_cbAvailBuff; // will update + hdr.dwCntStreams = 1; + + out << hdr; + + // write MethodDesc->Method name map + hr = m_EENames.StreamTo(out); + + // wrap up the buffer whether we ecountered an error or not + size_t cbWritten = out.GetPos(); + cbWritten = ALIGN_UP(cbWritten, sizeof(size_t)); + + // patch the dwTotalSize field blitted at the beginning of the buffer + ((StreamsHeader*)m_rawBuffer)->dwTotalSize = (DWORD) cbWritten; + + if (pcbWritten) + *pcbWritten = (DWORD) cbWritten; + + return hr; + } + + HRESULT ReadAllStreams() + { + _ASSERTE(!m_bStreamsRead); + + if (m_rw == eNone || m_rw == eWO) + { + // no streams to read... + m_bStreamsRead = TRUE; + return S_FALSE; + } + + HRESULT hr = S_OK; + + IStreamBuff in(m_rawBuffer, m_MiniMetaDataBuffSizeMax); + + // read header + StreamsHeader hdr; + in >> hdr; + _ASSERTE(hdr.dwSig == sig); + _ASSERTE(hdr.dwCntStreams == 1); + + // read EE struct pointer -> EE name map + m_EENames.Clear(); + hr = m_EENames.StreamFrom(in); + + m_bStreamsRead = TRUE; + + return hr; + } + + static bool ReserveInBuffer(DWORD size, void * writeState) + { + DacStreamManager * pThis = reinterpret_cast<DacStreamManager*>(writeState); + if (size > pThis->m_cbAvailBuff) + { + return false; + } + else + { + pThis->m_cbAvailBuff -= size; + return true; + } + } + +private: + TADDR m_MiniMetaDataBuffAddress; // TADDR of the buffer + DWORD m_MiniMetaDataBuffSizeMax; // max size of buffer + BYTE * m_rawBuffer; // inproc copy of buffer + DWORD m_cbAvailBuff; // available bytes in buffer + eReadOrWrite m_rw; + BOOL m_bStreamsRead; + DacEENamesStreamable m_EENames; +}; + +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + +//---------------------------------------------------------------------------- +// +// ClrDataAccess. +// +//---------------------------------------------------------------------------- + +LONG ClrDataAccess::s_procInit; + +ClrDataAccess::ClrDataAccess(ICorDebugDataTarget * pTarget, ICLRDataTarget * pLegacyTarget/*=0*/) +{ + SUPPORTS_DAC_HOST_ONLY; // ctor does no marshalling - don't check with DacCop + + /* + * Stash the various forms of the new ICorDebugDataTarget interface + */ + m_pTarget = pTarget; + m_pTarget->AddRef(); + + HRESULT hr; + + hr = m_pTarget->QueryInterface(__uuidof(ICorDebugMutableDataTarget), + (void**)&m_pMutableTarget); + + if (hr != S_OK) + { + // Create a target which always fails the write requests with CORDBG_E_TARGET_READONLY + m_pMutableTarget = new ReadOnlyDataTargetFacade(); + m_pMutableTarget->AddRef(); + } + + /* + * If we have a legacy target, it means we're providing compatibility for code that used + * the old ICLRDataTarget interfaces. There are still a few things (like metadata location, + * GetImageBase, and VirtualAlloc) that the implementation may use which we haven't superseded + * in ICorDebugDataTarget, so we still need access to the old target interfaces. + * Any functionality that does exist in ICorDebugDataTarget is accessed from that interface + * using the DataTargetAdapter on top of the legacy interface (to unify the calling code). + * Eventually we may expose all functionality we need using ICorDebug (possibly a private + * interface for things like VirtualAlloc), at which point we can stop using the legacy interfaces + * completely (except in the DataTargetAdapter). + */ + m_pLegacyTarget = NULL; + m_pLegacyTarget2 = NULL; + m_pLegacyTarget3 = NULL; + m_legacyMetaDataLocator = NULL; + m_target3 = NULL; + if (pLegacyTarget != NULL) + { + m_pLegacyTarget = pLegacyTarget; + + m_pLegacyTarget->AddRef(); + + m_pLegacyTarget->QueryInterface(__uuidof(ICLRDataTarget2), (void**)&m_pLegacyTarget2); + + m_pLegacyTarget->QueryInterface(__uuidof(ICLRDataTarget3), (void**)&m_pLegacyTarget3); + + if (pLegacyTarget->QueryInterface(__uuidof(ICLRMetadataLocator), + (void**)&m_legacyMetaDataLocator) != S_OK) + { + // The debugger doesn't implement IMetadataLocator. Use + // IXCLRDataTarget3 if that exists. Otherwise we don't need it. + pLegacyTarget->QueryInterface(__uuidof(IXCLRDataTarget3), + (void**)&m_target3); + } + } + + m_globalBase = 0; + m_refs = 1; + m_instanceAge = 0; + m_debugMode = GetEnvironmentVariableA("MSCORDACWKS_DEBUG", NULL, 0) != 0; + + m_enumMemCb = NULL; + m_updateMemCb = NULL; + m_enumMemFlags = (CLRDataEnumMemoryFlags)-1; // invalid + m_jitNotificationTable = NULL; + m_gcNotificationTable = NULL; + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + m_streams = NULL; +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + // Target consistency checks are disabled by default. + // See code:ClrDataAccess::SetTargetConsistencyChecks for details. + m_fEnableTargetConsistencyAsserts = false; + +#ifdef _DEBUG + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgDACEnableAssert)) + { + m_fEnableTargetConsistencyAsserts = true; + } + + // Verification asserts are disabled by default because some debuggers (cdb/windbg) probe likely locations + // for DAC and having this assert pop up all the time can be annoying. We let derived classes enable + // this if they want. It can also be overridden at run-time with COMPlus_DbgDACAssertOnMismatch, + // see ClrDataAccess::VerifyDlls for details. + m_fEnableDllVerificationAsserts = false; +#endif + +} + +ClrDataAccess::~ClrDataAccess(void) +{ + SUPPORTS_DAC_HOST_ONLY; + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + if (m_streams) + { + delete m_streams; + } +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + delete [] m_jitNotificationTable; + if (m_pLegacyTarget) + { + m_pLegacyTarget->Release(); + } + if (m_pLegacyTarget2) + { + m_pLegacyTarget2->Release(); + } + if (m_pLegacyTarget3) + { + m_pLegacyTarget3->Release(); + } + if (m_legacyMetaDataLocator) + { + m_legacyMetaDataLocator->Release(); + } + if (m_target3) + { + m_target3->Release(); + } + m_pTarget->Release(); + m_pMutableTarget->Release(); +} + +STDMETHODIMP +ClrDataAccess::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + void* ifaceRet; + + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataProcess)) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataProcess2))) + { + ifaceRet = static_cast<IXCLRDataProcess2*>(this); + } + else if (IsEqualIID(interfaceId, __uuidof(ICLRDataEnumMemoryRegions))) + { + ifaceRet = static_cast<ICLRDataEnumMemoryRegions*>(this); + } + else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface))) + { + ifaceRet = static_cast<ISOSDacInterface*>(this); + } + else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface2))) + { + ifaceRet = static_cast<ISOSDacInterface2*>(this); + } + else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface3))) + { + ifaceRet = static_cast<ISOSDacInterface3*>(this); + } + else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface4))) + { + ifaceRet = static_cast<ISOSDacInterface4*>(this); + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + *iface = ifaceRet; + return S_OK; +} + +STDMETHODIMP_(ULONG) +ClrDataAccess::AddRef(THIS) +{ + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataAccess::Release(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::Flush(void) +{ + SUPPORTS_DAC_HOST_ONLY; + + // + // Free MD import objects. + // + m_mdImports.Flush(); + + // Free instance memory. + m_instances.Flush(); + + // When the host instance cache is flushed we + // update the instance age count so that + // all child objects automatically become + // invalid. This prevents them from using + // any pointers they've kept to host instances + // which are now gone. + m_instanceAge++; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::StartEnumTasks( + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + if (ThreadStore::s_pThreadStore) + { + Thread* thread = ThreadStore::GetAllThreadList(NULL, 0, 0); + *handle = TO_CDENUM(thread); + status = *handle ? S_OK : S_FALSE; + } + else + { + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::EnumTask( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataTask **task) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + if (*handle) + { + Thread* thread = FROM_CDENUM(Thread, *handle); + *task = new (nothrow) ClrDataTask(this, thread); + if (*task) + { + thread = ThreadStore::GetAllThreadList(thread, 0, 0); + *handle = TO_CDENUM(thread); + status = S_OK; + } + else + { + status = E_OUTOFMEMORY; + } + } + else + { + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::EndEnumTasks( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + // Enumerator holds no resources. + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetTaskByOSThreadID( + /* [in] */ ULONG32 osThreadID, + /* [out] */ IXCLRDataTask **task) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + status = E_INVALIDARG; + Thread* thread = DacGetThread(osThreadID); + if (thread != NULL) + { + *task = new (nothrow) ClrDataTask(this, thread); + status = *task ? S_OK : E_OUTOFMEMORY; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetTaskByUniqueID( + /* [in] */ ULONG64 uniqueID, + /* [out] */ IXCLRDataTask **task) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + Thread* thread = FindClrThreadByTaskId(uniqueID); + if (thread) + { + *task = new (nothrow) ClrDataTask(this, thread); + status = *task ? S_OK : E_OUTOFMEMORY; + } + else + { + status = E_INVALIDARG; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetFlags( + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + // XXX Microsoft - GC check. + *flags = CLRDATA_PROCESS_DEFAULT; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::IsSameObject( + /* [in] */ IXCLRDataProcess* process) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + status = m_pTarget == ((ClrDataAccess*)process)->m_pTarget ? + S_OK : S_FALSE; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetManagedObject( + /* [out] */ IXCLRDataValue **value) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetDesiredExecutionState( + /* [out] */ ULONG32 *state) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::SetDesiredExecutionState( + /* [in] */ ULONG32 state) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetAddressType( + /* [in] */ CLRDATA_ADDRESS address, + /* [out] */ CLRDataAddressType* type) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + // The only thing that constitutes a failure is some + // dac failure while checking things. + status = S_OK; + TADDR taAddr = CLRDATA_ADDRESS_TO_TADDR(address); + if (IsPossibleCodeAddress(taAddr) == S_OK) + { + if (ExecutionManager::IsManagedCode(taAddr)) + { + *type = CLRDATA_ADDRESS_MANAGED_METHOD; + goto Exit; + } + + if (StubManager::IsStub(taAddr)) + { + *type = CLRDATA_ADDRESS_RUNTIME_UNMANAGED_STUB; + goto Exit; + } + } + + *type = CLRDATA_ADDRESS_UNRECOGNIZED; + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetRuntimeNameByAddress( + /* [in] */ CLRDATA_ADDRESS address, + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *symbolLen, + /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR symbolBuf[ ], + /* [out] */ CLRDATA_ADDRESS* displacement) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { +#ifdef _TARGET_ARM_ + address &= ~THUMB_CODE; //workaround for windbg passing in addresses with the THUMB mode bit set +#endif + status = RawGetMethodName(address, flags, bufLen, symbolLen, symbolBuf, + displacement); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::StartEnumAppDomains( + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + AppDomainIterator* iter = new (nothrow) AppDomainIterator(FALSE); + if (iter) + { + *handle = TO_CDENUM(iter); + status = S_OK; + } + else + { + status = E_OUTOFMEMORY; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::EnumAppDomain( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataAppDomain **appDomain) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + AppDomainIterator* iter = FROM_CDENUM(AppDomainIterator, *handle); + if (iter->Next()) + { + *appDomain = new (nothrow) + ClrDataAppDomain(this, iter->GetDomain()); + status = *appDomain ? S_OK : E_OUTOFMEMORY; + } + else + { + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::EndEnumAppDomains( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + AppDomainIterator* iter = FROM_CDENUM(AppDomainIterator, handle); + delete iter; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetAppDomainByUniqueID( + /* [in] */ ULONG64 uniqueID, + /* [out] */ IXCLRDataAppDomain **appDomain) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + AppDomainIterator iter(FALSE); + + status = E_INVALIDARG; + while (iter.Next()) + { + if (iter.GetDomain()->GetId().m_dwId == uniqueID) + { + *appDomain = new (nothrow) + ClrDataAppDomain(this, iter.GetDomain()); + status = *appDomain ? S_OK : E_OUTOFMEMORY; + break; + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::StartEnumAssemblies( + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + ProcessModIter* iter = new (nothrow) ProcessModIter; + if (iter) + { + *handle = TO_CDENUM(iter); + status = S_OK; + } + else + { + status = E_OUTOFMEMORY; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::EnumAssembly( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataAssembly **assembly) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + ProcessModIter* iter = FROM_CDENUM(ProcessModIter, *handle); + Assembly* assem; + + if ((assem = iter->NextAssem())) + { + *assembly = new (nothrow) + ClrDataAssembly(this, assem); + status = *assembly ? S_OK : E_OUTOFMEMORY; + } + else + { + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::EndEnumAssemblies( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + ProcessModIter* iter = FROM_CDENUM(ProcessModIter, handle); + delete iter; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::StartEnumModules( + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + ProcessModIter* iter = new (nothrow) ProcessModIter; + if (iter) + { + *handle = TO_CDENUM(iter); + status = S_OK; + } + else + { + status = E_OUTOFMEMORY; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::EnumModule( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataModule **mod) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + ProcessModIter* iter = FROM_CDENUM(ProcessModIter, *handle); + Module* curMod; + + if ((curMod = iter->NextModule())) + { + *mod = new (nothrow) + ClrDataModule(this, curMod); + status = *mod ? S_OK : E_OUTOFMEMORY; + } + else + { + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::EndEnumModules( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + ProcessModIter* iter = FROM_CDENUM(ProcessModIter, handle); + delete iter; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetModuleByAddress( + /* [in] */ CLRDATA_ADDRESS address, + /* [out] */ IXCLRDataModule** mod) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + ProcessModIter modIter; + Module* modDef; + + while ((modDef = modIter.NextModule())) + { + TADDR base; + ULONG32 length; + PEFile* file = modDef->GetFile(); + + if ((base = PTR_TO_TADDR(file->GetLoadedImageContents(&length)))) + { + if (TO_CDADDR(base) <= address && + TO_CDADDR(base + length) > address) + { + break; + } + } + if (file->HasNativeImage()) + { + base = PTR_TO_TADDR(file->GetLoadedNative()->GetBase()); + length = file->GetLoadedNative()->GetVirtualSize(); + if (TO_CDADDR(base) <= address && + TO_CDADDR(base + length) > address) + { + break; + } + } + } + + if (modDef) + { + *mod = new (nothrow) + ClrDataModule(this, modDef); + status = *mod ? S_OK : E_OUTOFMEMORY; + } + else + { + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::StartEnumMethodDefinitionsByAddress( + /* [in] */ CLRDATA_ADDRESS address, + /* [out] */ CLRDATA_ENUM *handle) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + ProcessModIter modIter; + Module* modDef; + + while ((modDef = modIter.NextModule())) + { + TADDR base; + ULONG32 length; + PEFile* file = modDef->GetFile(); + + if ((base = PTR_TO_TADDR(file->GetLoadedImageContents(&length)))) + { + if (TO_CDADDR(base) <= address && + TO_CDADDR(base + length) > address) + { + break; + } + } + } + + status = EnumMethodDefinitions:: + CdStart(modDef, true, address, handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::EnumMethodDefinitionByAddress( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodDefinition **method) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + status = EnumMethodDefinitions::CdNext(this, handle, method); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::EndEnumMethodDefinitionsByAddress( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + status = EnumMethodDefinitions::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::StartEnumMethodInstancesByAddress( + /* [in] */ CLRDATA_ADDRESS address, + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [out] */ CLRDATA_ENUM *handle) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + MethodDesc* methodDesc; + + *handle = 0; + status = S_FALSE; + TADDR taddr; + if( (status = TRY_CLRDATA_ADDRESS_TO_TADDR(address, &taddr)) != S_OK ) + { + goto Exit; + } + + if (IsPossibleCodeAddress(taddr) != S_OK) + { + goto Exit; + } + + methodDesc = ExecutionManager::GetCodeMethodDesc(taddr); + if (!methodDesc) + { + goto Exit; + } + + status = EnumMethodInstances::CdStart(methodDesc, appDomain, + handle); + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::EnumMethodInstanceByAddress( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodInstance **method) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + status = EnumMethodInstances::CdNext(this, handle, method); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::EndEnumMethodInstancesByAddress( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + status = EnumMethodInstances::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetDataByAddress( + /* [in] */ CLRDATA_ADDRESS address, + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [in] */ IXCLRDataTask* tlsTask, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataValue **value, + /* [out] */ CLRDATA_ADDRESS *displacement) +{ + HRESULT status; + + if (flags != 0) + { + return E_INVALIDARG; + } + + DAC_ENTER(); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetExceptionStateByExceptionRecord( + /* [in] */ EXCEPTION_RECORD64 *record, + /* [out] */ IXCLRDataExceptionState **exception) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::TranslateExceptionRecordToNotification( + /* [in] */ EXCEPTION_RECORD64 *record, + /* [in] */ IXCLRDataExceptionNotification *notify) +{ + HRESULT status = E_FAIL; + ClrDataModule* pubModule = NULL; + ClrDataMethodInstance* pubMethodInst = NULL; + ClrDataExceptionState* pubExState = NULL; + GcEvtArgs pubGcEvtArgs; + ULONG32 notifyType = 0; + DWORD catcherNativeOffset = 0; + + DAC_ENTER(); + + EX_TRY + { + // + // We cannot hold the dac lock while calling + // out as the external code can do arbitrary things. + // Instead we make a pass over the exception + // information and create all necessary objects. + // We then leave the lock and make the callbac. + // + + TADDR exInfo[EXCEPTION_MAXIMUM_PARAMETERS]; + for (UINT i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++) + { + exInfo[i] = TO_TADDR(record->ExceptionInformation[i]); + } + + notifyType = DACNotify::GetType(exInfo); + switch(notifyType) + { + case DACNotify::MODULE_LOAD_NOTIFICATION: + { + TADDR modulePtr; + + if (DACNotify::ParseModuleLoadNotification(exInfo, modulePtr)) + { + Module* clrModule = PTR_Module(modulePtr); + pubModule = new (nothrow) ClrDataModule(this, clrModule); + if (pubModule == NULL) + { + status = E_OUTOFMEMORY; + } + else + { + status = S_OK; + } + } + break; + } + + case DACNotify::MODULE_UNLOAD_NOTIFICATION: + { + TADDR modulePtr; + + if (DACNotify::ParseModuleUnloadNotification(exInfo, modulePtr)) + { + Module* clrModule = PTR_Module(modulePtr); + pubModule = new (nothrow) ClrDataModule(this, clrModule); + if (pubModule == NULL) + { + status = E_OUTOFMEMORY; + } + else + { + status = S_OK; + } + } + break; + } + + case DACNotify::JIT_NOTIFICATION: + { + TADDR methodDescPtr; + + if (DACNotify::ParseJITNotification(exInfo, methodDescPtr)) + { + // Try and find the right appdomain + MethodDesc* methodDesc = PTR_MethodDesc(methodDescPtr); + BaseDomain* baseDomain = methodDesc->GetDomain(); + AppDomain* appDomain = NULL; + + if (baseDomain->IsAppDomain()) + { + appDomain = PTR_AppDomain(PTR_HOST_TO_TADDR(baseDomain)); + } + else + { + // Find a likely domain, because it's the shared domain. + AppDomainIterator adi(FALSE); + appDomain = adi.GetDomain(); + } + + pubMethodInst = + new (nothrow) ClrDataMethodInstance(this, + appDomain, + methodDesc); + if (pubMethodInst == NULL) + { + status = E_OUTOFMEMORY; + } + else + { + status = S_OK; + } + } + break; + } + + case DACNotify::EXCEPTION_NOTIFICATION: + { + TADDR threadPtr; + + if (DACNotify::ParseExceptionNotification(exInfo, threadPtr)) + { + // Translation can only occur at the time of + // receipt of the notify exception, so we assume + // that the Thread's current exception state + // is the state we want. + status = ClrDataExceptionState:: + NewFromThread(this, + PTR_Thread(threadPtr), + &pubExState, + NULL); + } + break; + } + + case DACNotify::GC_NOTIFICATION: + { + if (DACNotify::ParseGCNotification(exInfo, pubGcEvtArgs)) + { + status = S_OK; + } + break; + } + + case DACNotify::CATCH_ENTER_NOTIFICATION: + { + TADDR methodDescPtr; + if (DACNotify::ParseExceptionCatcherEnterNotification(exInfo, methodDescPtr, catcherNativeOffset)) + { + // Try and find the right appdomain + MethodDesc* methodDesc = PTR_MethodDesc(methodDescPtr); + BaseDomain* baseDomain = methodDesc->GetDomain(); + AppDomain* appDomain = NULL; + + if (baseDomain->IsAppDomain()) + { + appDomain = PTR_AppDomain(PTR_HOST_TO_TADDR(baseDomain)); + } + else + { + // Find a likely domain, because it's the shared domain. + AppDomainIterator adi(FALSE); + appDomain = adi.GetDomain(); + } + + pubMethodInst = + new (nothrow) ClrDataMethodInstance(this, + appDomain, + methodDesc); + if (pubMethodInst == NULL) + { + status = E_OUTOFMEMORY; + } + else + { + status = S_OK; + } + } + break; + } + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + + if (status == S_OK) + { + IXCLRDataExceptionNotification2* notify2; + + if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification2), + (void**)¬ify2) != S_OK) + { + notify2 = NULL; + } + + IXCLRDataExceptionNotification3* notify3; + if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification3), + (void**)¬ify3) != S_OK) + { + notify3 = NULL; + } + + IXCLRDataExceptionNotification4* notify4; + if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification4), + (void**)¬ify4) != S_OK) + { + notify4 = NULL; + } + + switch(notifyType) + { + case DACNotify::MODULE_LOAD_NOTIFICATION: + notify->OnModuleLoaded(pubModule); + break; + + case DACNotify::MODULE_UNLOAD_NOTIFICATION: + notify->OnModuleUnloaded(pubModule); + break; + + case DACNotify::JIT_NOTIFICATION: + notify->OnCodeGenerated(pubMethodInst); + break; + + case DACNotify::EXCEPTION_NOTIFICATION: + if (notify2) + { + notify2->OnException(pubExState); + } + else + { + status = E_INVALIDARG; + } + break; + + case DACNotify::GC_NOTIFICATION: + if (notify3) + { + notify3->OnGcEvent(pubGcEvtArgs); + } + break; + + case DACNotify::CATCH_ENTER_NOTIFICATION: + if (notify4) + { + notify4->ExceptionCatcherEnter(pubMethodInst, catcherNativeOffset); + } + break; + + default: + // notifyType has already been validated. + _ASSERTE(FALSE); + break; + } + + if (notify2) + { + notify2->Release(); + } + if (notify3) + { + notify3->Release(); + } + } + + if (pubModule) + { + pubModule->Release(); + } + if (pubMethodInst) + { + pubMethodInst->Release(); + } + if (pubExState) + { + pubExState->Release(); + } + + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::CreateMemoryValue( + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [in] */ IXCLRDataTask* tlsTask, + /* [in] */ IXCLRDataTypeInstance* type, + /* [in] */ CLRDATA_ADDRESS addr, + /* [out] */ IXCLRDataValue** value) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + AppDomain* dacDomain; + Thread* dacThread; + TypeHandle dacType; + ULONG32 flags; + NativeVarLocation loc; + + dacDomain = ((ClrDataAppDomain*)appDomain)->GetAppDomain(); + if (tlsTask) + { + dacThread = ((ClrDataTask*)tlsTask)->GetThread(); + } + else + { + dacThread = NULL; + } + dacType = ((ClrDataTypeInstance*)type)->GetTypeHandle(); + + flags = GetTypeFieldValueFlags(dacType, NULL, 0, false); + + loc.addr = addr; + loc.size = dacType.GetSize(); + loc.contextReg = false; + + *value = new (nothrow) + ClrDataValue(this, dacDomain, dacThread, flags, + dacType, addr, 1, &loc); + status = *value ? S_OK : E_OUTOFMEMORY; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::SetAllTypeNotifications( + /* [in] */ IXCLRDataModule* mod, + /* [in] */ ULONG32 flags) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::SetAllCodeNotifications( + /* [in] */ IXCLRDataModule* mod, + /* [in] */ ULONG32 flags) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + status = E_FAIL; + + if (!IsValidMethodCodeNotification(flags)) + { + status = E_INVALIDARG; + } + else + { + JITNotifications jn(GetHostJitNotificationTable()); + if (!jn.IsActive()) + { + status = E_OUTOFMEMORY; + } + else + { + BOOL changedTable; + TADDR modulePtr = mod ? + PTR_HOST_TO_TADDR(((ClrDataModule*)mod)->GetModule()) : + NULL; + + if (jn.SetAllNotifications(modulePtr, flags, &changedTable)) + { + if (!changedTable || + (changedTable && jn.UpdateOutOfProcTable())) + { + status = S_OK; + } + } + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetTypeNotifications( + /* [in] */ ULONG32 numTokens, + /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[], + /* [in] */ IXCLRDataModule* singleMod, + /* [in, size_is(numTokens)] */ mdTypeDef tokens[], + /* [out, size_is(numTokens)] */ ULONG32 flags[]) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::SetTypeNotifications( + /* [in] */ ULONG32 numTokens, + /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[], + /* [in] */ IXCLRDataModule* singleMod, + /* [in, size_is(numTokens)] */ mdTypeDef tokens[], + /* [in, size_is(numTokens)] */ ULONG32 flags[], + /* [in] */ ULONG32 singleFlags) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::GetCodeNotifications( + /* [in] */ ULONG32 numTokens, + /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[], + /* [in] */ IXCLRDataModule* singleMod, + /* [in, size_is(numTokens)] */ mdMethodDef tokens[], + /* [out, size_is(numTokens)] */ ULONG32 flags[]) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + if ((flags == NULL || tokens == NULL) || + (mods == NULL && singleMod == NULL) || + (mods != NULL && singleMod != NULL)) + { + status = E_INVALIDARG; + } + else + { + JITNotifications jn(GetHostJitNotificationTable()); + if (!jn.IsActive()) + { + status = E_OUTOFMEMORY; + } + else + { + TADDR modulePtr = NULL; + if (singleMod) + { + modulePtr = PTR_HOST_TO_TADDR(((ClrDataModule*)singleMod)-> + GetModule()); + } + + for (ULONG32 i = 0; i < numTokens; i++) + { + if (singleMod == NULL) + { + modulePtr = + PTR_HOST_TO_TADDR(((ClrDataModule*)mods[i])-> + GetModule()); + } + USHORT jt = jn.Requested(modulePtr, tokens[i]); + flags[i] = jt; + } + + status = S_OK; + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::SetCodeNotifications( + /* [in] */ ULONG32 numTokens, + /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[], + /* [in] */ IXCLRDataModule* singleMod, + /* [in, size_is(numTokens)] */ mdMethodDef tokens[], + /* [in, size_is(numTokens)] */ ULONG32 flags[], + /* [in] */ ULONG32 singleFlags) +{ + HRESULT status = E_UNEXPECTED; + + DAC_ENTER(); + + EX_TRY + { + if ((tokens == NULL) || + (mods == NULL && singleMod == NULL) || + (mods != NULL && singleMod != NULL)) + { + status = E_INVALIDARG; + } + else + { + JITNotifications jn(GetHostJitNotificationTable()); + if (!jn.IsActive() || numTokens > jn.GetTableSize()) + { + status = E_OUTOFMEMORY; + } + else + { + BOOL changedTable = FALSE; + + // Are flags valid? + if (flags) + { + for (ULONG32 check = 0; check < numTokens; check++) + { + if (!IsValidMethodCodeNotification(flags[check])) + { + status = E_INVALIDARG; + goto Exit; + } + } + } + else if (!IsValidMethodCodeNotification(singleFlags)) + { + status = E_INVALIDARG; + goto Exit; + } + + TADDR modulePtr = NULL; + if (singleMod) + { + modulePtr = + PTR_HOST_TO_TADDR(((ClrDataModule*)singleMod)-> + GetModule()); + } + + for (ULONG32 i = 0; i < numTokens; i++) + { + if (singleMod == NULL) + { + modulePtr = + PTR_HOST_TO_TADDR(((ClrDataModule*)mods[i])-> + GetModule()); + } + + USHORT curFlags = jn.Requested(modulePtr, tokens[i]); + USHORT setFlags = (USHORT)(flags ? flags[i] : singleFlags); + + if (curFlags != setFlags) + { + if (!jn.SetNotification(modulePtr, tokens[i], + setFlags)) + { + status = E_FAIL; + goto Exit; + } + + changedTable = TRUE; + } + } + + if (!changedTable || + (changedTable && jn.UpdateOutOfProcTable())) + { + status = S_OK; + } + } + } + +Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT +ClrDataAccess::GetOtherNotificationFlags( + /* [out] */ ULONG32* flags) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + *flags = g_dacNotificationFlags; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT +ClrDataAccess::SetOtherNotificationFlags( + /* [in] */ ULONG32 flags) +{ + HRESULT status; + + if ((flags & ~(CLRDATA_NOTIFY_ON_MODULE_LOAD | + CLRDATA_NOTIFY_ON_MODULE_UNLOAD | + CLRDATA_NOTIFY_ON_EXCEPTION | + CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER)) != 0) + { + return E_INVALIDARG; + } + + DAC_ENTER(); + + EX_TRY + { + g_dacNotificationFlags = flags; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +enum +{ + STUB_BUF_FLAGS_START, + + STUB_BUF_METHOD_JITTED, + STUB_BUF_FRAME_PUSHED, + STUB_BUF_STUB_MANAGER_PUSHED, + + STUB_BUF_FLAGS_END, +}; + +union STUB_BUF +{ + CLRDATA_FOLLOW_STUB_BUFFER apiBuf; + struct + { + ULONG64 flags; + ULONG64 addr; + ULONG64 arg1; + } u; +}; + +HRESULT +ClrDataAccess::FollowStubStep( + /* [in] */ Thread* thread, + /* [in] */ ULONG32 inFlags, + /* [in] */ TADDR inAddr, + /* [in] */ union STUB_BUF* inBuffer, + /* [out] */ TADDR* outAddr, + /* [out] */ union STUB_BUF* outBuffer, + /* [out] */ ULONG32* outFlags) +{ + TraceDestination trace; + bool traceDone = false; + BYTE* retAddr; + T_CONTEXT localContext; + REGDISPLAY regDisp; + MethodDesc* methodDesc; + + ZeroMemory(outBuffer, sizeof(*outBuffer)); + + if (inBuffer) + { + switch(inBuffer->u.flags) + { + case STUB_BUF_METHOD_JITTED: + if (inAddr != GFN_TADDR(DACNotifyCompilationFinished)) + { + return E_INVALIDARG; + } + + // It's possible that this notification is + // for a different method, so double-check + // and recycle the notification if necessary. + methodDesc = PTR_MethodDesc(CORDB_ADDRESS_TO_TADDR(inBuffer->u.addr)); + if (methodDesc->HasNativeCode()) + { + *outAddr = methodDesc->GetNativeCode(); + *outFlags = CLRDATA_FOLLOW_STUB_EXIT; + return S_OK; + } + + // We didn't end up with native code so try again. + trace.InitForUnjittedMethod(methodDesc); + traceDone = true; + break; + + case STUB_BUF_FRAME_PUSHED: + if (!thread || + inAddr != inBuffer->u.addr) + { + return E_INVALIDARG; + } + + trace.InitForFramePush(CORDB_ADDRESS_TO_TADDR(inBuffer->u.addr)); + DacGetThreadContext(thread, &localContext); + thread->FillRegDisplay(®Disp, &localContext); + if (!thread->GetFrame()-> + TraceFrame(thread, + TRUE, + &trace, + ®Disp)) + { + return E_FAIL; + } + + traceDone = true; + break; + + case STUB_BUF_STUB_MANAGER_PUSHED: + if (!thread || + inAddr != inBuffer->u.addr || + !inBuffer->u.arg1) + { + return E_INVALIDARG; + } + + trace.InitForManagerPush(CORDB_ADDRESS_TO_TADDR(inBuffer->u.addr), + PTR_StubManager(CORDB_ADDRESS_TO_TADDR(inBuffer->u.arg1))); + DacGetThreadContext(thread, &localContext); + if (!trace.GetStubManager()-> + TraceManager(thread, + &trace, + &localContext, + &retAddr)) + { + return E_FAIL; + } + + traceDone = true; + break; + + default: + return E_INVALIDARG; + } + } + + if ((!traceDone && + !StubManager::TraceStub(inAddr, &trace)) || + !StubManager::FollowTrace(&trace)) + { + return E_NOINTERFACE; + } + + switch(trace.GetTraceType()) + { + case TRACE_UNMANAGED: + case TRACE_MANAGED: + // We've hit non-stub code so we're done. + *outAddr = trace.GetAddress(); + *outFlags = CLRDATA_FOLLOW_STUB_EXIT; + break; + + case TRACE_UNJITTED_METHOD: + // The stub causes jitting, so return + // the address of the jit-complete routine + // so that the real native address can + // be picked up once the JIT is done. + + // One special case is ngen'ed code that + // needs the prestub run. This results in + // an unjitted trace but no jitting will actually + // occur since the code is ngen'ed. Detect + // this and redirect to the actual code. + methodDesc = trace.GetMethodDesc(); + if (methodDesc->IsPreImplemented() && + !methodDesc->IsPointingToNativeCode() && + !methodDesc->IsGenericMethodDefinition() && + methodDesc->HasNativeCode()) + { + *outAddr = methodDesc->GetNativeCode(); + *outFlags = CLRDATA_FOLLOW_STUB_EXIT; + break; + } + + *outAddr = GFN_TADDR(DACNotifyCompilationFinished); + outBuffer->u.flags = STUB_BUF_METHOD_JITTED; + outBuffer->u.addr = PTR_HOST_TO_TADDR(methodDesc); + *outFlags = CLRDATA_FOLLOW_STUB_INTERMEDIATE; + break; + + case TRACE_FRAME_PUSH: + if (!thread) + { + return E_INVALIDARG; + } + + *outAddr = trace.GetAddress(); + outBuffer->u.flags = STUB_BUF_FRAME_PUSHED; + outBuffer->u.addr = trace.GetAddress(); + *outFlags = CLRDATA_FOLLOW_STUB_INTERMEDIATE; + break; + + case TRACE_MGR_PUSH: + if (!thread) + { + return E_INVALIDARG; + } + + *outAddr = trace.GetAddress(); + outBuffer->u.flags = STUB_BUF_STUB_MANAGER_PUSHED; + outBuffer->u.addr = trace.GetAddress(); + outBuffer->u.arg1 = PTR_HOST_TO_TADDR(trace.GetStubManager()); + *outFlags = CLRDATA_FOLLOW_STUB_INTERMEDIATE; + break; + + default: + return E_INVALIDARG; + } + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::FollowStub( + /* [in] */ ULONG32 inFlags, + /* [in] */ CLRDATA_ADDRESS inAddr, + /* [in] */ CLRDATA_FOLLOW_STUB_BUFFER* _inBuffer, + /* [out] */ CLRDATA_ADDRESS* outAddr, + /* [out] */ CLRDATA_FOLLOW_STUB_BUFFER* _outBuffer, + /* [out] */ ULONG32* outFlags) +{ + return FollowStub2(NULL, inFlags, inAddr, _inBuffer, + outAddr, _outBuffer, outFlags); +} + +HRESULT STDMETHODCALLTYPE +ClrDataAccess::FollowStub2( + /* [in] */ IXCLRDataTask* task, + /* [in] */ ULONG32 inFlags, + /* [in] */ CLRDATA_ADDRESS _inAddr, + /* [in] */ CLRDATA_FOLLOW_STUB_BUFFER* _inBuffer, + /* [out] */ CLRDATA_ADDRESS* _outAddr, + /* [out] */ CLRDATA_FOLLOW_STUB_BUFFER* _outBuffer, + /* [out] */ ULONG32* outFlags) +{ + HRESULT status; + + if ((inFlags & ~(CLRDATA_FOLLOW_STUB_DEFAULT)) != 0) + { + return E_INVALIDARG; + } + + STUB_BUF* inBuffer = (STUB_BUF*)_inBuffer; + STUB_BUF* outBuffer = (STUB_BUF*)_outBuffer; + + if (inBuffer && + (inBuffer->u.flags <= STUB_BUF_FLAGS_START || + inBuffer->u.flags >= STUB_BUF_FLAGS_END)) + { + return E_INVALIDARG; + } + + DAC_ENTER(); + + EX_TRY + { + STUB_BUF cycleBuf; + TADDR inAddr = TO_TADDR(_inAddr); + TADDR outAddr; + Thread* thread = task ? ((ClrDataTask*)task)->GetThread() : NULL; + ULONG32 loops = 4; + + for (;;) + { + if ((status = FollowStubStep(thread, + inFlags, + inAddr, + inBuffer, + &outAddr, + outBuffer, + outFlags)) != S_OK) + { + break; + } + + // Some stub tracing just requests further iterations + // of processing, so detect that case and loop. + if (outAddr != inAddr) + { + // We can make forward progress, we're done. + *_outAddr = TO_CDADDR(outAddr); + break; + } + + // We need more processing. As a protection + // against infinite loops in corrupted or buggy + // situations, we only allow this to happen a + // small number of times. + if (--loops == 0) + { + ZeroMemory(outBuffer, sizeof(*outBuffer)); + status = E_FAIL; + break; + } + + cycleBuf = *outBuffer; + inBuffer = &cycleBuf; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4297) +#endif // _MSC_VER +STDMETHODIMP +ClrDataAccess::GetGcNotification(GcEvtArgs* gcEvtArgs) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + if (gcEvtArgs->typ >= GC_EVENT_TYPE_MAX) + { + status = E_INVALIDARG; + } + else + { + GcNotifications gn(GetHostGcNotificationTable()); + if (!gn.IsActive()) + { + status = E_OUTOFMEMORY; + } + else + { + GcEvtArgs *res = gn.GetNotification(*gcEvtArgs); + if (res != NULL) + { + *gcEvtArgs = *res; + status = S_OK; + } + else + { + status = E_FAIL; + } + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +STDMETHODIMP +ClrDataAccess::SetGcNotification(IN GcEvtArgs gcEvtArgs) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + if (gcEvtArgs.typ >= GC_EVENT_TYPE_MAX) + { + status = E_INVALIDARG; + } + else + { + GcNotifications gn(GetHostGcNotificationTable()); + if (!gn.IsActive()) + { + status = E_OUTOFMEMORY; + } + else + { + if (gn.SetNotification(gcEvtArgs) && gn.UpdateOutOfProcTable()) + { + status = S_OK; + } + else + { + status = E_FAIL; + } + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +HRESULT +ClrDataAccess::Initialize(void) +{ + HRESULT hr; + CLRDATA_ADDRESS base; + + // + // We do not currently support cross-platform + // debugging. Verify that cross-platform is not + // being attempted. + // + + // Determine our platform based on the pre-processor macros set when we were built + +#ifdef FEATURE_PAL + #if defined(DBG_TARGET_X86) + CorDebugPlatform hostPlatform = CORDB_PLATFORM_POSIX_X86; + #elif defined(DBG_TARGET_AMD64) + CorDebugPlatform hostPlatform = CORDB_PLATFORM_POSIX_AMD64; + #elif defined(DBG_TARGET_ARM) + CorDebugPlatform hostPlatform = CORDB_PLATFORM_POSIX_ARM; + #elif defined(DBG_TARGET_ARM64) + CorDebugPlatform hostPlatform = CORDB_PLATFORM_POSIX_ARM64; + #else + #error Unknown Processor. + #endif +#else + #if defined(DBG_TARGET_X86) + CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_X86; + #elif defined(DBG_TARGET_AMD64) + CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_AMD64; + #elif defined(DBG_TARGET_ARM) + CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_ARM; + #elif defined(DBG_TARGET_ARM64) + CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_ARM64; + #else + #error Unknown Processor. + #endif +#endif + + CorDebugPlatform targetPlatform; + IfFailRet(m_pTarget->GetPlatform(&targetPlatform)); + + if (targetPlatform != hostPlatform) + { + // DAC fatal error: Platform mismatch - the platform reported by the data target + // is not what this version of mscordacwks.dll was built for. + return CORDBG_E_UNCOMPATIBLE_PLATFORMS; + } + + // + // Get the current DLL base for mscorwks globals. + // In case of multiple-CLRs, there may be multiple dlls named "mscorwks". + // code:OpenVirtualProcess can take the base address (clrInstanceId) to select exactly + // which CLR to is being target. If so, m_globalBase will already be set. + // + + if (m_globalBase == 0) + { + // Caller didn't specify which CLR to debug. This supports Whidbey SOS cases, so we should + // be using a legacy data target. + if (m_pLegacyTarget == NULL) + { + DacError(E_INVALIDARG); + UNREACHABLE(); + } + + // Since this is Whidbey, assume there's only 1 CLR named "mscorwks.dll" and pick that. + IfFailRet(m_pLegacyTarget->GetImageBase(MAIN_CLR_DLL_NAME_W, &base)); + + m_globalBase = TO_TADDR(base); + } + + // We don't need to try too hard to prevent + // multiple initializations as each one will + // copy the same data into the globals and so + // cannot interfere with each other. + if (!s_procInit) + { + IfFailRet(GetDacGlobals()); + IfFailRet(DacGetHostVtPtrs()); + s_procInit = true; + } + + // + // DAC is now setup and ready to use + // + + // Do some validation + IfFailRet(VerifyDlls()); + + // To support EH SxS, utilcode requires the base address of the runtime + // as part of its initialization so that functions like "WasThrownByUs" work correctly since + // they use the CLR base address to check if an exception was raised by a given instance of the runtime + // or not. + // + // Thus, when DAC is initialized, initialize utilcode with the base address of the runtime loaded in the + // target process. This is similar to work done in CorDB::SetTargetCLR for mscordbi. + + // Initialize UtilCode for SxS scenarios + CoreClrCallbacks cccallbacks; + cccallbacks.m_hmodCoreCLR = (HINSTANCE)m_globalBase; // Base address of the runtime in the target process + cccallbacks.m_pfnIEE = NULL; + cccallbacks.m_pfnGetCORSystemDirectory = NULL; + cccallbacks.m_pfnGetCLRFunction = NULL; + InitUtilcode(cccallbacks); + + return S_OK; +} + +Thread* +ClrDataAccess::FindClrThreadByTaskId(ULONG64 taskId) +{ + Thread* thread = NULL; + + if (!ThreadStore::s_pThreadStore) + { + return NULL; + } + + while ((thread = ThreadStore::GetAllThreadList(thread, 0, 0))) + { + if (thread->GetThreadId() == (DWORD)taskId) + { + return thread; + } + } + + return NULL; +} + +HRESULT +ClrDataAccess::IsPossibleCodeAddress(IN TADDR address) +{ + SUPPORTS_DAC; + BYTE testRead; + ULONG32 testDone; + + // First do a trivial check on the readability of the + // address. This makes for quick rejection of bogus + // addresses that the debugger sends in when searching + // stacks for return addresses. + // XXX Microsoft - Will this cause problems in minidumps + // where it's possible the stub is identifiable but + // the stub code isn't present? Yes, but the lack + // of that code could confuse the walker on its own + // if it does code analysis. + if ((m_pTarget->ReadVirtual(address, &testRead, sizeof(testRead), + &testDone) != S_OK) || + !testDone) + { + return E_INVALIDARG; + } + + return S_OK; +} + +HRESULT +ClrDataAccess::GetFullMethodName( + IN MethodDesc* methodDesc, + IN ULONG32 symbolChars, + OUT ULONG32* symbolLen, + __out_ecount_part_opt(symbolChars, *symbolLen) LPWSTR symbol + ) +{ + StackSString s; +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + PAL_CPP_TRY + { +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + TypeString::AppendMethodInternal(s, methodDesc, TypeString::FormatSignature|TypeString::FormatNamespace|TypeString::FormatFullInst); + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + } + PAL_CPP_CATCH_ALL + { + if (!MdCacheGetEEName(dac_cast<TADDR>(methodDesc), s)) + { + PAL_CPP_RETHROW; + } + } + PAL_CPP_ENDTRY +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + if (symbol) + { + // Copy as much as we can and truncate the rest. + wcsncpy_s(symbol, symbolChars, s.GetUnicode(), _TRUNCATE); + } + + if (symbolLen) + *symbolLen = s.GetCount() + 1; + + if (symbol != NULL && symbolChars < (s.GetCount() + 1)) + return S_FALSE; + else + return S_OK; +} + +PCSTR +ClrDataAccess::GetJitHelperName( + IN TADDR address, + IN bool dynamicHelpersOnly /*=false*/ + ) +{ + const static PCSTR s_rgHelperNames[] = { +#define JITHELPER(code,fn,sig) #code, +#include <jithelpers.h> + }; + static_assert_no_msg(COUNTOF(s_rgHelperNames) == CORINFO_HELP_COUNT); + +#ifdef FEATURE_PAL + if (!dynamicHelpersOnly) +#else + if (!dynamicHelpersOnly && g_runtimeLoadedBaseAddress <= address && + address < g_runtimeLoadedBaseAddress + g_runtimeVirtualSize) +#endif // FEATURE_PAL + { + // Read the whole table from the target in one shot for better performance + VMHELPDEF * pTable = static_cast<VMHELPDEF *>( + PTR_READ(dac_cast<TADDR>(&hlpFuncTable), CORINFO_HELP_COUNT * sizeof(VMHELPDEF))); + + for (int i = 0; i < CORINFO_HELP_COUNT; i++) + { + if (address == (TADDR)(pTable[i].pfnHelper)) + return s_rgHelperNames[i]; + } + } + + // Check if its a dynamically generated JIT helper + const static CorInfoHelpFunc s_rgDynamicHCallIds[] = { +#define DYNAMICJITHELPER(code, fn, sig) code, +#define JITHELPER(code, fn,sig) +#include <jithelpers.h> + }; + + // Read the whole table from the target in one shot for better performance + VMHELPDEF * pDynamicTable = static_cast<VMHELPDEF *>( + PTR_READ(dac_cast<TADDR>(&hlpDynamicFuncTable), DYNAMIC_CORINFO_HELP_COUNT * sizeof(VMHELPDEF))); + for (unsigned d = 0; d < DYNAMIC_CORINFO_HELP_COUNT; d++) + { + if (address == (TADDR)(pDynamicTable[d].pfnHelper)) + { + return s_rgHelperNames[s_rgDynamicHCallIds[d]]; + } + } + + return NULL; +} + +HRESULT +ClrDataAccess::RawGetMethodName( + /* [in] */ CLRDATA_ADDRESS address, + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *symbolLen, + /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR symbolBuf[ ], + /* [out] */ CLRDATA_ADDRESS* displacement) +{ +#ifdef _TARGET_ARM_ + _ASSERTE((address & THUMB_CODE) == 0); + address &= ~THUMB_CODE; +#endif + + const UINT k_cch64BitHexFormat = COUNTOF("1234567812345678"); + HRESULT status; + + if (flags != 0) + { + return E_INVALIDARG; + } + + TADDR taddr; + if( (status = TRY_CLRDATA_ADDRESS_TO_TADDR(address, &taddr)) != S_OK ) + { + return status; + } + + if ((status = IsPossibleCodeAddress(taddr)) != S_OK) + { + return status; + } + + PTR_StubManager pStubManager; + MethodDesc* methodDesc = NULL; + + { + EECodeInfo codeInfo(TO_TADDR(address)); + if (codeInfo.IsValid()) + { + if (displacement) + { + *displacement = codeInfo.GetRelOffset(); + } + + methodDesc = codeInfo.GetMethodDesc(); + goto NameFromMethodDesc; + } + } + + pStubManager = StubManager::FindStubManager(TO_TADDR(address)); + if (pStubManager != NULL) + { + if (displacement) + { + *displacement = 0; + } + + // + // Special-cased stub managers + // +#ifdef FEATURE_PREJIT + if (pStubManager == RangeSectionStubManager::g_pManager) + { + switch (RangeSectionStubManager::GetStubKind(TO_TADDR(address))) + { + case STUB_CODE_BLOCK_PRECODE: + goto PrecodeStub; + + case STUB_CODE_BLOCK_JUMPSTUB: + goto JumpStub; + + default: + break; + } + } + else +#endif + if (pStubManager == PrecodeStubManager::g_pManager) + { + PrecodeStub: + PCODE alignedAddress = AlignDown(TO_TADDR(address), PRECODE_ALIGNMENT); + +#ifdef _TARGET_ARM_ + alignedAddress += THUMB_CODE; +#endif + + SIZE_T maxPrecodeSize = sizeof(StubPrecode); + +#ifdef HAS_THISPTR_RETBUF_PRECODE + maxPrecodeSize = max(maxPrecodeSize, sizeof(ThisPtrRetBufPrecode)); +#endif +#ifdef HAS_REMOTING_PRECODE + maxPrecodeSize = max(maxPrecodeSize, sizeof(RemotingPrecode)); +#endif + + for (SIZE_T i = 0; i < maxPrecodeSize / PRECODE_ALIGNMENT; i++) + { + EX_TRY + { + // Try to find matching precode entrypoint + Precode* pPrecode = Precode::GetPrecodeFromEntryPoint(alignedAddress, TRUE); + if (pPrecode != NULL) + { + methodDesc = pPrecode->GetMethodDesc(); + if (methodDesc != NULL) + { + if (DacValidateMD(methodDesc)) + { + if (displacement) + { + *displacement = TO_TADDR(address) - PCODEToPINSTR(alignedAddress); + } + goto NameFromMethodDesc; + } + } + } + alignedAddress -= PRECODE_ALIGNMENT; + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions) + } + } + else + if (pStubManager == JumpStubStubManager::g_pManager) + { + JumpStub: + PCODE pTarget = decodeBackToBackJump(TO_TADDR(address)); + + HRESULT hr = GetRuntimeNameByAddress(pTarget, flags, bufLen, symbolLen, symbolBuf, NULL); + if (SUCCEEDED(hr)) + { + return hr; + } + + PCSTR pHelperName = GetJitHelperName(pTarget); + if (pHelperName != NULL) + { + hr = ConvertUtf8(pHelperName, bufLen, symbolLen, symbolBuf); + if (FAILED(hr)) + return S_FALSE; + + return hr; + } + } + + static WCHAR s_wszFormatNameWithStubManager[] = W("CLRStub[%s]@%I64x"); + + LPCWSTR wszStubManagerName = pStubManager->GetStubManagerName(TO_TADDR(address)); + _ASSERTE(wszStubManagerName != NULL); + + HRESULT hr = StringCchPrintfW( + symbolBuf, + bufLen, + s_wszFormatNameWithStubManager, + wszStubManagerName, // Arg 1 = stub name + TO_TADDR(address)); // Arg 2 = stub hex address + + if (hr == S_OK) + { + // Printf succeeded, so we have an exact char count to return + if (symbolLen) + { + size_t cchSymbol = wcslen(symbolBuf) + 1; + if (!FitsIn<ULONG32>(cchSymbol)) + return COR_E_OVERFLOW; + + *symbolLen = (ULONG32) cchSymbol; + } + return S_OK; + } + + // Printf failed. Estimate a size that will be at least big enough to hold the name + if (symbolLen) + { + size_t cchSymbol = COUNTOF(s_wszFormatNameWithStubManager) + + wcslen(wszStubManagerName) + + k_cch64BitHexFormat + + 1; + + if (!FitsIn<ULONG32>(cchSymbol)) + return COR_E_OVERFLOW; + + *symbolLen = (ULONG32) cchSymbol; + } + return S_FALSE; + } + + // Do not waste time looking up name for static helper. Debugger can get the actual name from .pdb. + PCSTR pHelperName; + pHelperName = GetJitHelperName(TO_TADDR(address), true /* dynamicHelpersOnly */); + if (pHelperName != NULL) + { + if (displacement) + { + *displacement = 0; + } + + HRESULT hr = ConvertUtf8(pHelperName, bufLen, symbolLen, symbolBuf); + if (FAILED(hr)) + return S_FALSE; + + return S_OK; + } + + return E_NOINTERFACE; + +NameFromMethodDesc: + if (methodDesc->GetClassification() == mcDynamic && + !methodDesc->GetSig()) + { + // XXX Microsoft - Should this case have a more specific name? + static WCHAR s_wszFormatNameAddressOnly[] = W("CLRStub@%I64x"); + + HRESULT hr = StringCchPrintfW( + symbolBuf, + bufLen, + s_wszFormatNameAddressOnly, + TO_TADDR(address)); + + if (hr == S_OK) + { + // Printf succeeded, so we have an exact char count to return + if (symbolLen) + { + size_t cchSymbol = wcslen(symbolBuf) + 1; + if (!FitsIn<ULONG32>(cchSymbol)) + return COR_E_OVERFLOW; + + *symbolLen = (ULONG32) cchSymbol; + } + return S_OK; + } + + // Printf failed. Estimate a size that will be at least big enough to hold the name + if (symbolLen) + { + size_t cchSymbol = COUNTOF(s_wszFormatNameAddressOnly) + + k_cch64BitHexFormat + + 1; + + if (!FitsIn<ULONG32>(cchSymbol)) + return COR_E_OVERFLOW; + + *symbolLen = (ULONG32) cchSymbol; + } + + return S_FALSE; + } + + return GetFullMethodName(methodDesc, bufLen, symbolLen, symbolBuf); +} + +HRESULT +ClrDataAccess::GetMethodExtents(MethodDesc* methodDesc, + METH_EXTENTS** extents) +{ + CLRDATA_ADDRESS_RANGE* curExtent; + + { + // + // Get the information from the methoddesc. + // We'll go through the CodeManager + JitManagers, so this should work + // for all types of managed code. + // + + PCODE methodStart = methodDesc->GetNativeCode(); + if (!methodStart) + { + return E_NOINTERFACE; + } + + EECodeInfo codeInfo(methodStart); + _ASSERTE(codeInfo.IsValid()); + + TADDR codeSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfoToken()); + + *extents = new (nothrow) METH_EXTENTS; + if (!*extents) + { + return E_OUTOFMEMORY; + } + + (*extents)->numExtents = 1; + curExtent = (*extents)->extents; + curExtent->startAddress = TO_CDADDR(methodStart); + curExtent->endAddress = + curExtent->startAddress + codeSize; + curExtent++; + } + + (*extents)->curExtent = 0; + + return S_OK; +} + +// Allocator to pass to the debug-info-stores... +BYTE* DebugInfoStoreNew(void * pData, size_t cBytes) +{ + return new (nothrow) BYTE[cBytes]; +} + +HRESULT +ClrDataAccess::GetMethodVarInfo(MethodDesc* methodDesc, + TADDR address, + ULONG32* numVarInfo, + ICorDebugInfo::NativeVarInfo** varInfo, + ULONG32* codeOffset) +{ + SUPPORTS_DAC; + COUNT_T countNativeVarInfo; + NewHolder<ICorDebugInfo::NativeVarInfo> nativeVars(NULL); + + DebugInfoRequest request; + TADDR nativeCodeStartAddr = PCODEToPINSTR(methodDesc->GetNativeCode()); + request.InitFromStartingAddr(methodDesc, nativeCodeStartAddr); + + BOOL success = DebugInfoManager::GetBoundariesAndVars( + request, + DebugInfoStoreNew, NULL, // allocator + NULL, NULL, + &countNativeVarInfo, &nativeVars); + + + if (!success) + { + return E_FAIL; + } + + if (!nativeVars || !countNativeVarInfo) + { + return E_NOINTERFACE; + } + + *numVarInfo = countNativeVarInfo; + *varInfo = nativeVars; + nativeVars.SuppressRelease(); // To prevent NewHolder from releasing the memory + + if (codeOffset) + { + *codeOffset = (ULONG32) + (address - nativeCodeStartAddr); + } + return S_OK; +} + +HRESULT +ClrDataAccess::GetMethodNativeMap(MethodDesc* methodDesc, + TADDR address, + ULONG32* numMap, + DebuggerILToNativeMap** map, + bool* mapAllocated, + CLRDATA_ADDRESS* codeStart, + ULONG32* codeOffset) +{ + _ASSERTE((codeOffset == NULL) || (address != NULL)); + + // Use the DebugInfoStore to get IL->Native maps. + // It doesn't matter whether we're jitted, ngenned etc. + + DebugInfoRequest request; + TADDR nativeCodeStartAddr = PCODEToPINSTR(methodDesc->GetNativeCode()); + request.InitFromStartingAddr(methodDesc, nativeCodeStartAddr); + + + // Bounds info. + ULONG32 countMapCopy; + NewHolder<ICorDebugInfo::OffsetMapping> mapCopy(NULL); + + BOOL success = DebugInfoManager::GetBoundariesAndVars( + request, + DebugInfoStoreNew, NULL, // allocator + &countMapCopy, &mapCopy, + NULL, NULL); + + if (!success) + { + return E_FAIL; + } + + + // Need to convert map formats. + *numMap = countMapCopy; + + *map = new (nothrow) DebuggerILToNativeMap[countMapCopy]; + if (!*map) + { + return E_OUTOFMEMORY; + } + + ULONG32 i; + for (i = 0; i < *numMap; i++) + { + (*map)[i].ilOffset = mapCopy[i].ilOffset; + (*map)[i].nativeStartOffset = mapCopy[i].nativeOffset; + if (i > 0) + { + (*map)[i - 1].nativeEndOffset = (*map)[i].nativeStartOffset; + } + (*map)[i].source = mapCopy[i].source; + } + if (*numMap >= 1) + { + (*map)[i - 1].nativeEndOffset = 0; + } + + + // Update varion out params. + if (codeStart) + { + *codeStart = TO_CDADDR(nativeCodeStartAddr); + } + if (codeOffset) + { + *codeOffset = (ULONG32) + (address - nativeCodeStartAddr); + } + + *mapAllocated = true; + return S_OK; +} + +// Get the MethodDesc for a function +// Arguments: +// Input: +// pModule - pointer to the module for the function +// memberRef - metadata token for the function +// Return Value: +// MethodDesc for the function +MethodDesc * ClrDataAccess::FindLoadedMethodRefOrDef(Module* pModule, + mdToken memberRef) +{ + CONTRACT(MethodDesc *) + { + 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); +} // FindLoadedMethodRefOrDef + +// +// ReportMem - report a region of memory for dump gathering +// +// If you specify that you expect success, any failure will cause ReportMem to +// return false. If you do not expect success, true is always returned. +// This function only throws when all dump collection should be cancelled. +// +// Arguments: +// addr - the starting target address for the memory to report +// size - the length (in bytes) to report +// fExpectSuccess - if true (the default), then we expect that this region of memory +// should be fully readable. Any read errors indicate a corrupt target. +// +bool ClrDataAccess::ReportMem(TADDR addr, TSIZE_T size, bool fExpectSuccess /*= true*/) +{ + SUPPORTS_DAC_HOST_ONLY; + + // This block of code is to help debugging blocks that we report + // to minidump/heapdump. You can set break point here to view the static + // variable to figure out the size of blocks that we are reporting. + // Most useful is set conditional break point to catch large chuck of + // memory. We will leave it here for all builds. + // + static TADDR debugAddr; + static TSIZE_T debugSize; + debugAddr = addr; + debugSize = size; + + HRESULT status; + if (!addr || addr == (TADDR)-1 || !size) + { + if (fExpectSuccess) + return false; + else + return true; + } + + // + // Try and sanity-check the reported region of memory + // +#ifdef _DEBUG + // in debug builds, sanity-check all reports + const TSIZE_T k_minSizeToCheck = 1; +#else + // in retail builds, only sanity-check larger chunks which have the potential to waste a + // lot of time and/or space. This avoids the overhead of checking for the majority of + // memory regions (which are small). + const TSIZE_T k_minSizeToCheck = 1024; +#endif + if (size >= k_minSizeToCheck) + { + if (!IsFullyReadable(addr, size)) + { + if (!fExpectSuccess) + { + // We know the read might fail (eg. we're trying to find mapped pages in + // a module image), so just skip this block silently. + // Note that the EnumMemoryRegion callback won't necessarily do anything if any part of + // the region is unreadable, and so there is no point in calling it. For cases where we expect + // the read might fail, but we want to report any partial blocks, we have to break up the region + // into pages and try reporting each page anyway + return true; + } + + // We're reporting bogus memory, so the target must be corrupt (or there is a issue). We should abort + // reporting and continue with the next data structure (where the exception is caught), + // just like we would for a DAC read error (otherwise we might do something stupid + // like get into an infinite loop, or otherwise waste time with corrupt data). + + TARGET_CONSISTENCY_CHECK(false, "Found unreadable memory while reporting memory regions for dump gathering"); + return false; + } + } + + // Minidumps should never contain data structures that are anywhere near 4MB. If we see this, it's + // probably due to memory corruption. To keep the dump small, we'll truncate the block. Note that + // the size to which the block is truncated is pretty unique, so should be good evidence in a dump + // that this has happened. + // Note that it's hard to say what a good value would be here, or whether we should dump any of the + // data structure at all. Hopefully experience will help guide this going forward. + // @dbgtodo : Extend dump-gathering API to allow a dump-log to be included. + const TSIZE_T kMaxMiniDumpRegion = 4*1024*1024 - 3; // 4MB-3 + if( size > kMaxMiniDumpRegion + && (m_enumMemFlags == CLRDATA_ENUM_MEM_MINI + || m_enumMemFlags == CLRDATA_ENUM_MEM_TRIAGE)) + { + TARGET_CONSISTENCY_CHECK( false, "Dump target consistency failure - truncating minidump data structure"); + size = kMaxMiniDumpRegion; + } + + // track the total memory reported. + m_cbMemoryReported += size; + + // ICLRData APIs take only 32-bit sizes. In practice this will almost always be sufficient, but + // in theory we might have some >4GB ranges on large 64-bit processes doing a heap dump + // (for example, the code:LoaderHeap). If necessary, break up the reporting into maximum 4GB + // chunks so we can use the existing API. + // @dbgtodo : ICorDebugDataTarget should probably use 64-bit sizes + while (size) + { + ULONG32 enumSize; + if (size > ULONG_MAX) + { + enumSize = ULONG_MAX; + } + else + { + enumSize = (ULONG32)size; + } + + // Actually perform the memory reporting callback + status = m_enumMemCb->EnumMemoryRegion(TO_CDADDR(addr), enumSize); + if (status != S_OK) + { + m_memStatus = status; + + // If dump generation was cancelled, allow us to throw upstack so we'll actually quit. + if ((fExpectSuccess) && (status != COR_E_OPERATIONCANCELED)) + return false; + } + + // If the return value of EnumMemoryRegion is COR_E_OPERATIONCANCELED, + // it means that user has requested that the minidump gathering be canceled. + // To do this we throw an exception which is caught in EnumMemoryRegionsWrapper. + if (status == COR_E_OPERATIONCANCELED) + { + ThrowHR(status); + } + + // Move onto the next chunk (if any) + size -= enumSize; + addr += enumSize; + } + + return true; +} + + +// +// DacUpdateMemoryRegion - updates/poisons a region of memory of generated dump +// +// Parameters: +// addr - target address of the beginning of the memory region +// bufferSize - number of bytes to update/poison +// buffer - data to be written at given target address +// +bool ClrDataAccess::DacUpdateMemoryRegion(TADDR addr, TSIZE_T bufferSize, BYTE* buffer) +{ + SUPPORTS_DAC_HOST_ONLY; + + HRESULT status; + if (!addr || addr == (TADDR)-1 || !bufferSize) + { + return false; + } + + // track the total memory reported. + m_cbMemoryReported += bufferSize; + + if (m_updateMemCb == NULL) + { + return false; + } + + // Actually perform the memory updating callback + status = m_updateMemCb->UpdateMemoryRegion(TO_CDADDR(addr), (ULONG32)bufferSize, buffer); + if (status != S_OK) + { + return false; + } + + return true; +} + +// +// Check whether a region of target memory is fully readable. +// +// Arguments: +// addr The base target address of the region +// size The size of the region to analyze +// +// Return value: +// True if the entire regions appears to be readable, false otherwise. +// +// Notes: +// The motivation here is that reporting large regions of unmapped address space to dbgeng can result in +// it taking a long time trying to identify a valid subrange. This can happen when the target +// memory is corrupt, and we enumerate a data structure with a dynamic size. Ideally we would just spec +// the ICLRDataEnumMemoryRegionsCallback API to require the client to fail if it detects an unmapped +// memory address in the region. However, we can't change the existing dbgeng code, so for now we'll +// rely on this heuristic here. +// @dbgtodo : Try and get the dbg team to change their EnumMemoryRegion behavior. See DevDiv Bugs 6265 +// +bool ClrDataAccess::IsFullyReadable(TADDR taBase, TSIZE_T dwSize) +{ + // The only way we have to verify that a memory region is readable is to try reading it in it's + // entirety. This is potentially expensive, so we'll rely on a heuristic that spot-checks various + // points in the region. + + // Ensure we've got something to check + if( dwSize == 0 ) + return true; + + // Check for overflow + TADDR taEnd = DacTAddrOffset(taBase, dwSize, 1); + + // Loop through using expontential growth, being sure to check both the first and last byte + TADDR taCurr = taBase; + TSIZE_T dwInc = 4096; + bool bDone = false; + while (!bDone) + { + // Try and read a byte from the target. Note that we don't use PTR_BYTE here because we don't want + // the overhead of inserting entries into the DAC instance cache. + BYTE b; + ULONG32 dwBytesRead; + HRESULT hr = m_pTarget->ReadVirtual(taCurr, &b, 1, &dwBytesRead); + if( hr != S_OK || dwBytesRead < 1 ) + { + return false; + } + + if (taEnd - taCurr <= 1) + { + // We just read the last byte so we're done + _ASSERTE( taCurr = taEnd - 1 ); + bDone = true; + } + else if (dwInc == 0 || dwInc >= taEnd - taCurr) + { + // we've reached the end of the exponential series, check the last byte + taCurr = taEnd - 1; + } + else + { + // advance current pointer (subtraction above ensures this won't overflow) + taCurr += dwInc; + + // double the increment for next time (or set to 0 if it's already the max) + dwInc <<= 1; + } + } + return true; +} + +JITNotification* +ClrDataAccess::GetHostJitNotificationTable() +{ + if (m_jitNotificationTable == NULL) + { + m_jitNotificationTable = + JITNotifications::InitializeNotificationTable(1000); + } + + return m_jitNotificationTable; +} + +GcNotification* +ClrDataAccess::GetHostGcNotificationTable() +{ + if (m_gcNotificationTable == NULL) + { + m_gcNotificationTable = + GcNotifications::InitializeNotificationTable(128); + } + + return m_gcNotificationTable; +} + +/* static */ bool +ClrDataAccess::GetMetaDataFileInfoFromPEFile(PEFile *pPEFile, + DWORD &dwTimeStamp, + DWORD &dwSize, + DWORD &dwDataSize, + DWORD &dwRvaHint, + bool &isNGEN, + __out_ecount(cchFilePath) LPWSTR wszFilePath, + const DWORD cchFilePath) +{ + SUPPORTS_DAC_HOST_ONLY; + PEImage *mdImage = NULL; + PEImageLayout *layout; + IMAGE_DATA_DIRECTORY *pDir = NULL; + COUNT_T uniPathChars = 0; + + isNGEN = false; + + if (pPEFile->HasNativeImage()) + { + mdImage = pPEFile->GetNativeImage(); + _ASSERTE(mdImage != NULL); + layout = mdImage->GetLoadedLayout(); + pDir = &(layout->GetCorHeader()->MetaData); + // For ngen image, the IL metadata is stored for private use. So we need to pass + // the RVA hint to find it to debuggers. + // + if (pDir->Size != 0) + { + isNGEN = true; + dwRvaHint = pDir->VirtualAddress; + dwDataSize = pDir->Size; + } + + } + if (pDir == NULL || pDir->Size == 0) + { + mdImage = pPEFile->GetILimage(); + if (mdImage != NULL) + { + layout = mdImage->GetLoadedLayout(); + pDir = &layout->GetCorHeader()->MetaData; + + // In IL image case, we do not have any hint to IL metadata since it is stored + // in the corheader. + // + dwRvaHint = 0; + dwDataSize = pDir->Size; + } + else + { + return false; + } + } + + // Do not fail if path can not be read. Triage dumps don't have paths and we want to fallback + // on searching metadata from IL image. + mdImage->GetPath().DacGetUnicode(cchFilePath, wszFilePath, &uniPathChars); + + if (!mdImage->HasNTHeaders() || + !mdImage->HasCorHeader() || + !mdImage->HasLoadedLayout() || + (uniPathChars > cchFilePath)) + { + return false; + } + + // It is possible that the module is in-memory. That is the wszFilePath here is empty. + // We will try to use the module name instead in this case for hosting debugger + // to find match. + if (wcslen(wszFilePath) == 0) + { + mdImage->GetModuleFileNameHintForDAC().DacGetUnicode(cchFilePath, wszFilePath, &uniPathChars); + if (uniPathChars > cchFilePath) + { + return false; + } + } + + dwTimeStamp = layout->GetTimeDateStamp(); + dwSize = (ULONG32)layout->GetVirtualSize(); + + return true; +} + +/* static */ +bool ClrDataAccess::GetILImageInfoFromNgenPEFile(PEFile *peFile, + DWORD &dwTimeStamp, + DWORD &dwSize, + __out_ecount(cchFilePath) LPWSTR wszFilePath, + const DWORD cchFilePath) +{ + SUPPORTS_DAC_HOST_ONLY; + DWORD dwWritten = 0; + + // use the IL File name + if (!peFile->GetPath().DacGetUnicode(cchFilePath, wszFilePath, (COUNT_T *)(&dwWritten))) + { + // Use DAC hint to retrieve the IL name. + peFile->GetModuleFileNameHint().DacGetUnicode(cchFilePath, wszFilePath, (COUNT_T *)(&dwWritten)); + } +#ifdef FEATURE_PREJIT + // Need to get IL image information from cached info in the ngen image. + dwTimeStamp = peFile->GetLoaded()->GetNativeVersionInfo()->sourceAssembly.timeStamp; + dwSize = peFile->GetLoaded()->GetNativeVersionInfo()->sourceAssembly.ilImageSize; +#else + dwTimeStamp = 0; + dwSize = 0; +#endif // FEATURE_PREJIT + + return true; +} + +#if defined(FEATURE_CORESYSTEM) +/* static */ +// We extract "ni.dll or .ni.winmd" from the NGEM image name to obtain the IL image name. +// In the end we add given ilExtension. +// This dependecy is based on Apollo installer behavior. +bool ClrDataAccess::GetILImageNameFromNgenImage( LPCWSTR ilExtension, + __out_ecount(cchFilePath) LPWSTR wszFilePath, + const DWORD cchFilePath) +{ + if (wszFilePath == NULL || cchFilePath == 0) + { + return false; + } + + _wcslwr_s(wszFilePath, cchFilePath); + // Find the "ni.dll" or "ni.winmd" extension (check for PEFile isWinRT something to know when is winmd or not. + // If none exists use NGEN image name. + // + const WCHAR* ngenExtension[] = {W("ni.dll"), W("ni.winmd")}; + + for (unsigned i = 0; i < COUNTOF(ngenExtension); ++i) + { + if (wcslen(ilExtension) > wcslen(ngenExtension[i])) + { + // We should not have IL image name bigger than NGEN image. + // It will not fit inside wszFilePath. + continue; + } + LPWSTR wszFileExtension = wcsstr(wszFilePath, ngenExtension[i]); + if (wszFileExtension != 0) + { + LPWSTR wszNextFileExtension = wszFileExtension; + // Find last occurence + do + { + wszFileExtension = wszNextFileExtension; + wszNextFileExtension = wcsstr(wszFileExtension + 1, ngenExtension[i]); + } while (wszNextFileExtension != 0); + + // Overwrite ni.dll or ni.winmd with ilExtension(.dll, .winmd) + if (!memcpy_s(wszFileExtension, + wcslen(ngenExtension[i])*sizeof(WCHAR), + ilExtension, + wcslen(ilExtension)*sizeof(WCHAR))) + { + wszFileExtension[wcslen(ilExtension)] = '\0'; + return true; + } + } + } + + //Use ngen filename if there is no ".ni" + if (wcsstr(wszFilePath, W(".ni")) == 0) + { + return true; + } + + return false; +} +#endif // FEATURE_CORESYSTEM + +void * +ClrDataAccess::GetMetaDataFromHost(PEFile* peFile, + bool* isAlternate) +{ + DWORD imageTimestamp, imageSize, dataSize; + void* buffer = NULL; + WCHAR uniPath[MAX_LONGPATH] = {0}; + bool isAlt = false; + bool isNGEN = false; + DAC_INSTANCE* inst = NULL; + HRESULT hr = S_OK; + DWORD ulRvaHint; + // + // We always ask for the IL image metadata, + // as we expect that to be more + // available than others. The drawback is that + // there may be differences between the IL image + // metadata and native image metadata, so we + // have to mark such alternate metadata so that + // we can fail unsupported usage of it. + // + + // Microsoft - above comment seems to be an unimplemented thing. + // The DAC_MD_IMPORT.isAlternate field gets ultimately set, but + // on the searching I did, I cannot find any usage of it + // other than in the ctor. Should we be doing something, or should + // we remove this comment and the isAlternate field? + // It's possible that test will want us to track whether we have + // an IL image's metadata loaded against an NGEN'ed image + // so the field remains for now. + + if (!ClrDataAccess::GetMetaDataFileInfoFromPEFile( + peFile, + imageTimestamp, + imageSize, + dataSize, + ulRvaHint, + isNGEN, + uniPath, + NumItems(uniPath))) + { + return NULL; + } + + // try direct match for the image that is loaded into the managed process + peFile->GetLoadedMetadata((COUNT_T *)(&dataSize)); + + DWORD allocSize = 0; + if (!ClrSafeInt<DWORD>::addition(dataSize, sizeof(DAC_INSTANCE), allocSize)) + { + DacError(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)); + } + + inst = m_instances.Alloc(0, allocSize, DAC_DPTR); + if (!inst) + { + DacError(E_OUTOFMEMORY); + return NULL; + } + + buffer = (void*)(inst + 1); + + // APIs implemented by hosting debugger. It can use the path/filename, timestamp, and + // file size to find an exact match for the image. If that fails for an ngen'ed image, + // we can request the IL image which it came from. + if (m_legacyMetaDataLocator) + { + // Legacy API implemented by hosting debugger. + hr = m_legacyMetaDataLocator->GetMetadata( + uniPath, + imageTimestamp, + imageSize, + NULL, // MVID - not used yet + ulRvaHint, + 0, // flags - reserved for future. + dataSize, + (BYTE*)buffer, + NULL); + } + else + { + hr = m_target3->GetMetaData( + uniPath, + imageTimestamp, + imageSize, + NULL, // MVID - not used yet + ulRvaHint, + 0, // flags - reserved for future. + dataSize, + (BYTE*)buffer, + NULL); + } + if (FAILED(hr) && isNGEN) + { + // We failed to locate the ngen'ed image. We should try to + // find the matching IL image + // + isAlt = true; + if (!ClrDataAccess::GetILImageInfoFromNgenPEFile( + peFile, + imageTimestamp, + imageSize, + uniPath, + NumItems(uniPath))) + { + goto ErrExit; + } + +#if defined(FEATURE_CORESYSTEM) + const WCHAR* ilExtension[] = {W("dll"), W("winmd")}; + WCHAR ngenImageName[MAX_LONGPATH] = {0}; + if (wcscpy_s(ngenImageName, NumItems(ngenImageName), uniPath) != 0) + { + goto ErrExit; + } + for (unsigned i = 0; i < COUNTOF(ilExtension); i++) + { + if (wcscpy_s(uniPath, NumItems(uniPath), ngenImageName) != 0) + { + goto ErrExit; + } + // Transform NGEN image name into IL Image name + if (!GetILImageNameFromNgenImage(ilExtension[i], uniPath, NumItems(uniPath))) + { + goto ErrExit; + } +#endif//FEATURE_CORESYSTEM + + // RVA size in ngen image and IL image is the same. Because the only + // different is in RVA. That is 4 bytes column fixed. + // + + // try again + if (m_legacyMetaDataLocator) + { + hr = m_legacyMetaDataLocator->GetMetadata( + uniPath, + imageTimestamp, + imageSize, + NULL, // MVID - not used yet + 0, // pass zero hint here... important + 0, // flags - reserved for future. + dataSize, + (BYTE*)buffer, + NULL); + } + else + { + hr = m_target3->GetMetaData( + uniPath, + imageTimestamp, + imageSize, + NULL, // MVID - not used yet + 0, // pass zero hint here... important + 0, // flags - reserved for future. + dataSize, + (BYTE*)buffer, + NULL); + } +#if defined(FEATURE_CORESYSTEM) + if (SUCCEEDED(hr)) + { + break; + } + } +#endif // FEATURE_CORESYSTEM + } + + if (FAILED(hr)) + { + goto ErrExit; + } + + *isAlternate = isAlt; + m_instances.AddSuperseded(inst); + return buffer; + +ErrExit: + if (inst != NULL) + { + m_instances.ReturnAlloc(inst); + } + return NULL; +} + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Given a PEFile or a ReflectionModule try to find the corresponding metadata +// We will first ask debugger to locate it. If fail, we will try +// to get it from the target process +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +IMDInternalImport* +ClrDataAccess::GetMDImport(const PEFile* peFile, const ReflectionModule* reflectionModule, bool throwEx) +{ + HRESULT status; + PTR_CVOID mdBaseTarget = NULL; + COUNT_T mdSize; + IMDInternalImport* mdImport = NULL; + PVOID mdBaseHost = NULL; + bool isAlternate = false; + + _ASSERTE(peFile == NULL && reflectionModule != NULL || peFile != NULL && reflectionModule == NULL); + TADDR peFileAddr = (peFile != NULL) ? dac_cast<TADDR>(peFile) : dac_cast<TADDR>(reflectionModule); + + // + // Look for one we've already created. + // + mdImport = m_mdImports.Get(peFileAddr); + if (mdImport != NULL) + { + return mdImport; + } + + if (peFile != NULL) + { + // Get the metadata size + mdBaseTarget = ((PEFile*)peFile)->GetLoadedMetadata(&mdSize); + } + else if (reflectionModule != NULL) + { + // Get the metadata + PTR_SBuffer metadataBuffer = reflectionModule->GetDynamicMetadataBuffer(); + if (metadataBuffer != PTR_NULL) + { + mdBaseTarget = dac_cast<PTR_CVOID>((metadataBuffer->DacGetRawBuffer()).StartAddress()); + mdSize = metadataBuffer->GetSize(); + } + else + { + if (throwEx) + { + DacError(E_FAIL); + } + return NULL; + } + } + else + { + if (throwEx) + { + DacError(E_FAIL); + } + return NULL; + } + + if (mdBaseTarget == PTR_NULL) + { + mdBaseHost = NULL; + } + else + { + + // + // Maybe the target process has the metadata + // Find out where the metadata for the image is + // in the target's memory. + // + // + // Read the metadata into the host process. Make sure pass in false in the last + // parameter. This is only matters when producing skinny mini-dump. This will + // prevent metadata gets reported into mini-dump. + // + mdBaseHost = DacInstantiateTypeByAddressNoReport(dac_cast<TADDR>(mdBaseTarget), mdSize, + false); + } + + // Try to see if debugger can locate it + if (peFile != NULL && mdBaseHost == NULL && (m_target3 || m_legacyMetaDataLocator)) + { + // We couldn't read the metadata from memory. Ask + // the target for metadata as it may be able to + // provide it from some alternate means. + mdBaseHost = GetMetaDataFromHost(const_cast<PEFile *>(peFile), &isAlternate); + } + + if (mdBaseHost == NULL) + { + // cannot locate metadata anywhere + if (throwEx) + { + DacError(E_INVALIDARG); + } + return NULL; + } + + // + // Open the MD interface on the host copy of the metadata. + // + + status = GetMDInternalInterface(mdBaseHost, mdSize, ofRead, + IID_IMDInternalImport, + (void**)&mdImport); + if (status != S_OK) + { + if (throwEx) + { + DacError(status); + } + return NULL; + } + + // + // Remember the object for this module for + // possible later use. + // The m_mdImports list does get cleaned up by calls to ClrDataAccess::Flush, + // i.e. every time the process changes state. + + if (m_mdImports.Add(peFileAddr, mdImport, isAlternate) == NULL) + { + mdImport->Release(); + DacError(E_OUTOFMEMORY); + } + + return mdImport; +} + + +// +// Set whether inconsistencies in the target should raise asserts. +// This overrides the default initial setting. +// +// Arguments: +// fEnableAsserts - whether ASSERTs in dacized code should be enabled +// + +void ClrDataAccess::SetTargetConsistencyChecks(bool fEnableAsserts) +{ + LIMITED_METHOD_DAC_CONTRACT; + m_fEnableTargetConsistencyAsserts = fEnableAsserts; +} + +// +// Get whether inconsistencies in the target should raise asserts. +// +// Return value: +// whether ASSERTs in dacized code should be enabled +// +// Notes: +// The implementation of ASSERT accesses this via code:DacTargetConsistencyAssertsEnabled +// +// By default, this is disabled, unless COMPlus_DbgDACEnableAssert is set (see code:ClrDataAccess::ClrDataAccess). +// This is necessary for compatibility. For example, SOS expects to be able to scan for +// valid MethodTables etc. (which may cause ASSERTs), and also doesn't want ASSERTs when working +// with targets with corrupted memory. +// +// Calling code:ClrDataAccess::SetTargetConsistencyChecks overrides the default setting. +// +bool ClrDataAccess::TargetConsistencyAssertsEnabled() +{ + LIMITED_METHOD_DAC_CONTRACT; + return m_fEnableTargetConsistencyAsserts; +} + +#ifdef FEATURE_CORESYSTEM +#define ctime_s _ctime32_s +#define time_t __time32_t +#endif + +// +// VerifyDlls - Validate that the mscorwks in the target matches this version of mscordacwks +// Only done on Windows and Mac builds at the moment. +// See code:CordbProcess::CordbProcess#DBIVersionChecking for more information regarding version checking. +// +HRESULT ClrDataAccess::VerifyDlls() +{ +#ifndef FEATURE_PAL + // Provide a knob for disabling this check if we really want to try and proceed anyway with a + // DAC mismatch. DAC behavior may be arbitrarily bad - globals probably won't be at the same + // address, data structures may be laid out differently, etc. + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgDACSkipVerifyDlls)) + { + return S_OK; + } + + // Read the debug directory timestamp from the target mscorwks image using DAC + // Note that we don't use the PE timestamp because the PE file might be changed in ways + // that don't effect the PDB (and therefore don't effect DAC). Specifically, we rebase + // our DLLs at the end of a build, that changes the PE file, but not the PDB. + // Note that if we wanted to be extra careful, we could read the CV contents (which includes + // the GUID signature) and verify it matches. Using the timestamp is useful for helpful error + // messages, and should be sufficient in any real scenario. + DWORD timestamp = 0; + HRESULT hr = S_OK; + DAC_ENTER(); + EX_TRY + { + // Note that we don't need to worry about ensuring the image memory read by this code + // is saved in a minidump. Managed minidump debugging already requires that you have + // the full mscorwks.dll available at debug time (eg. windbg won't even load DAC without it). + PEDecoder pedecoder(dac_cast<PTR_VOID>(m_globalBase)); + + // We use the first codeview debug directory entry since this should always refer to the single + // PDB for mscorwks.dll. + const UINT k_maxDebugEntries = 32; // a reasonable upper limit in case of corruption + for( UINT i = 0; i < k_maxDebugEntries; i++) + { + PTR_IMAGE_DEBUG_DIRECTORY pDebugEntry = pedecoder.GetDebugDirectoryEntry(i); + + // If there are no more entries, then stop + if (pDebugEntry == NULL) + break; + + // Ignore non-codeview entries. Some scenarios (eg. optimized builds), there may be extra + // debug directory entries at the end of some other type. + if (pDebugEntry->Type == IMAGE_DEBUG_TYPE_CODEVIEW) + { + // Found a codeview entry - use it's timestamp for comparison + timestamp = pDebugEntry->TimeDateStamp; + break; + } + } + char szMsgBuf[1024]; + _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE, + "Failed to find any valid codeview debug directory entry in %s image", + MAIN_CLR_MODULE_NAME_A); + _ASSERTE_MSG(timestamp != 0, szMsgBuf); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &hr)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + DAC_LEAVE(); + if (FAILED(hr)) + { + return hr; + } + + // Validate that we got a timestamp and it matches what the DAC table told us to expect + if (timestamp == 0 || timestamp != g_dacTableInfo.dwID0) + { + // Timestamp mismatch. This means mscordacwks is being used with a version of + // mscorwks other than the one it was built for. This will not work reliably. + +#ifdef _DEBUG + // Check if verbose asserts are enabled. The default is up to the specific instantiation of + // ClrDataAccess, but can be overridden (in either direction) by a COMPlus_ knob. + // Note that we check this knob every time because it may be handy to turn it on in + // the environment mid-flight. + DWORD dwAssertDefault = m_fEnableDllVerificationAsserts ? 1 : 0; + if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgDACAssertOnMismatch, dwAssertDefault)) + { + // Output a nice error message that contains the timestamps in string format. + time_t actualTime = timestamp; + char szActualTime[30]; + ctime_s(szActualTime, sizeof(szActualTime), &actualTime); + + time_t expectedTime = g_dacTableInfo.dwID0; + char szExpectedTime[30]; + ctime_s(szExpectedTime, sizeof(szExpectedTime), &expectedTime); + + // Create a nice detailed message for the assert dialog. + // Note that the strings returned by ctime_s have terminating newline characters. + // This is technically a TARGET_CONSISTENCY_CHECK because a corrupt target could, + // in-theory, have a corrupt mscrowks PE header and cause this check to fail + // unnecessarily. However, this check occurs during startup, before we know + // whether target consistency checks should be enabled, so it's always enabled + // at the moment. + + char szMsgBuf[1024]; + _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE, + "DAC fatal error: %s/mscordacwks.dll version mismatch\n\n"\ + "The debug directory timestamp of the loaded %s does not match the\n"\ + "version mscordacwks.dll was built for.\n"\ + "Expected %s timestamp: %s"\ + "Actual %s timestamp: %s\n"\ + "DAC will now fail to initialize with a CORDBG_E_MISMATCHED_CORWKS_AND_DACWKS_DLLS\n"\ + "error. If you really want to try and use the mimatched DLLs, you can disable this\n"\ + "check by setting COMPlus_DbgDACSkipVerifyDlls=1. However, using a mismatched DAC\n"\ + "DLL will usually result in arbitrary debugger failures.\n", + MAIN_CLR_DLL_NAME_A, + MAIN_CLR_DLL_NAME_A, + MAIN_CLR_DLL_NAME_A, + szExpectedTime, + MAIN_CLR_DLL_NAME_A, + szActualTime); + _ASSERTE_MSG(false, szMsgBuf); + } +#endif + + // Return a specific hresult indicating this problem + return CORDBG_E_MISMATCHED_CORWKS_AND_DACWKS_DLLS; + } +#endif // FEATURE_PAL + + return S_OK; +} + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + +void ClrDataAccess::InitStreamsForWriting(IN CLRDataEnumMemoryFlags flags) +{ + // enforce this should only be called when generating triage and mini-dumps + if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE) + return; + + EX_TRY + { + if (m_streams == NULL) + m_streams = new DacStreamManager(g_MiniMetaDataBuffAddress, g_MiniMetaDataBuffMaxSize); + + if (!m_streams->PrepareStreamsForWriting()) + { + delete m_streams; + m_streams = NULL; + } + } + EX_CATCH + { + if (m_streams != NULL) + { + delete m_streams; + m_streams = NULL; + } + } + EX_END_CATCH(SwallowAllExceptions) +} + +bool ClrDataAccess::MdCacheAddEEName(TADDR taEEStruct, const SString& name) +{ + bool result = false; + EX_TRY + { + if (m_streams != NULL) + result = m_streams->MdCacheAddEEName(taEEStruct, name); + } + EX_CATCH + { + result = false; + } + EX_END_CATCH(SwallowAllExceptions) + + return result; +} + +void ClrDataAccess::EnumStreams(IN CLRDataEnumMemoryFlags flags) +{ + // enforce this should only be called when generating triage and mini-dumps + if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE) + return; + + EX_TRY + { + if (m_streams != NULL) + m_streams->EnumStreams(flags); + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions) +} + +bool ClrDataAccess::MdCacheGetEEName(TADDR taEEStruct, SString & eeName) +{ + bool result = false; + EX_TRY + { + if (m_streams == NULL) + m_streams = new DacStreamManager(g_MiniMetaDataBuffAddress, g_MiniMetaDataBuffMaxSize); + + result = m_streams->MdCacheGetEEName(taEEStruct, eeName); + } + EX_CATCH + { + result = false; + } + EX_END_CATCH(SwallowAllExceptions) + + return result; +} + +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + +// Needed for RT_RCDATA. +#define MAKEINTRESOURCE(v) MAKEINTRESOURCEW(v) + +// this funny looking double macro forces x to be macro expanded before L is prepended +#define _WIDE(x) _WIDE2(x) +#define _WIDE2(x) W(x) + +HRESULT +ClrDataAccess::GetDacGlobals() +{ +#ifdef FEATURE_PAL +#ifdef DAC_TABLE_SIZE + if (DAC_TABLE_SIZE != sizeof(g_dacGlobals)) + { + return E_INVALIDARG; + } +#endif + ULONG64 dacTableAddress = m_globalBase + DAC_TABLE_RVA; + if (FAILED(ReadFromDataTarget(m_pTarget, dacTableAddress, (BYTE*)&g_dacGlobals, sizeof(g_dacGlobals)))) + { + return CORDBG_E_MISSING_DEBUGGER_EXPORTS; + } + if (g_dacGlobals.ThreadStore__s_pThreadStore == NULL) + { + return CORDBG_E_UNSUPPORTED; + } + return S_OK; +#else + HRESULT status = E_FAIL; + DWORD rsrcRVA = 0; + LPVOID rsrcData = NULL; + DWORD rsrcSize = 0; + + HRSRC rsrcFound; + HGLOBAL rsrc; + + DWORD resourceSectionRVA = 0; + + if (FAILED(status = GetMachineAndResourceSectionRVA(m_pTarget, m_globalBase, NULL, &resourceSectionRVA))) + { + _ASSERTE_MSG(false, "DAC fatal error: can't locate resource section in " MAIN_CLR_DLL_NAME_A); + return CORDBG_E_MISSING_DEBUGGER_EXPORTS; + } + + if (FAILED(status = GetResourceRvaFromResourceSectionRvaByName(m_pTarget, m_globalBase, + resourceSectionRVA, (DWORD)RT_RCDATA, _WIDE(DACCESS_TABLE_RESOURCE), 0, + &rsrcRVA, &rsrcSize))) + { + _ASSERTE_MSG(false, "DAC fatal error: can't locate DAC table resource in " MAIN_CLR_DLL_NAME_A); + return CORDBG_E_MISSING_DEBUGGER_EXPORTS; + } + + rsrcData = new (nothrow) BYTE[rsrcSize]; + if (rsrcData == NULL) + return E_OUTOFMEMORY; + + if (FAILED(status = ReadFromDataTarget(m_pTarget, m_globalBase + rsrcRVA, (BYTE*)rsrcData, rsrcSize))) + { + _ASSERTE_MSG(false, "DAC fatal error: can't load DAC table resource from " MAIN_CLR_DLL_NAME_A); + return CORDBG_E_MISSING_DEBUGGER_EXPORTS; + } + + + PBYTE rawData = (PBYTE)rsrcData; + DWORD bytesLeft = rsrcSize; + + // Read the header + struct DacTableHeader header; + + // We currently expect the header to be 2 32-bit values and 1 16-byte value, + // make sure there is no packing going on or anything. + static_assert_no_msg(sizeof(DacTableHeader) == 2 * 4 + 16); + + if (bytesLeft < sizeof(DacTableHeader)) + { + _ASSERTE_MSG(false, "DAC fatal error: DAC table too small for header."); + goto Exit; + } + memcpy(&header, rawData, sizeof(DacTableHeader)); + rawData += sizeof(DacTableHeader); + bytesLeft -= sizeof(DacTableHeader); + + // Save the table info for later use + g_dacTableInfo = header.info; + + // Sanity check that the DAC table is the size we expect. + // This could fail if a different version of dacvars.h or vptr_list.h was used when building + // mscordacwks.dll than when running DacTableGen. + + if (offsetof(DacGlobals, Thread__vtAddr) != header.numGlobals * sizeof(ULONG)) + { +#ifdef _DEBUG + char szMsgBuf[1024]; + _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE, + "DAC fatal error: mismatch in number of globals in DAC table. Read from file: %d, expected: %d.", + header.numGlobals, + offsetof(DacGlobals, Thread__vtAddr) / sizeof(ULONG)); + _ASSERTE_MSG(false, szMsgBuf); +#endif // _DEBUG + + status = E_INVALIDARG; + goto Exit; + } + + if (sizeof(DacGlobals) != (header.numGlobals + header.numVptrs) * sizeof(ULONG)) + { +#ifdef _DEBUG + char szMsgBuf[1024]; + _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE, + "DAC fatal error: mismatch in number of vptrs in DAC table. Read from file: %d, expected: %d.", + header.numVptrs, + (sizeof(DacGlobals) - offsetof(DacGlobals, Thread__vtAddr)) / sizeof(ULONG)); + _ASSERTE_MSG(false, szMsgBuf); +#endif // _DEBUG + + status = E_INVALIDARG; + goto Exit; + } + + // Copy the DAC table into g_dacGlobals + if (bytesLeft < sizeof(DacGlobals)) + { + _ASSERTE_MSG(false, "DAC fatal error: DAC table resource too small for DacGlobals."); + status = E_UNEXPECTED; + goto Exit; + } + memcpy(&g_dacGlobals, rawData, sizeof(DacGlobals)); + rawData += sizeof(DacGlobals); + bytesLeft -= sizeof(DacGlobals); + + status = S_OK; + +Exit: + + return status; +#endif +} + +#undef MAKEINTRESOURCE + +//---------------------------------------------------------------------------- +// +// IsExceptionFromManagedCode - report if pExceptionRecord points to a exception belonging to the current runtime +// +// Arguments: +// pExceptionRecord - the exception record +// +// Return Value: +// TRUE if it is +// Otherwise, FALSE +// +//---------------------------------------------------------------------------- +BOOL ClrDataAccess::IsExceptionFromManagedCode(EXCEPTION_RECORD* pExceptionRecord) +{ + DAC_ENTER(); + + HRESULT status; + BOOL flag = FALSE; + + if (::IsExceptionFromManagedCode(pExceptionRecord)) + { + flag = TRUE; + } + + DAC_LEAVE(); + + return flag; +} + +#ifndef FEATURE_PAL + +//---------------------------------------------------------------------------- +// +// GetWatsonBuckets - retrieve Watson buckets from the specified thread +// +// Arguments: +// dwThreadId - the thread ID +// pGM - pointer to the space to store retrieved Watson buckets +// +// Return Value: +// S_OK if the operation is successful. +// or S_FALSE if Watson buckets cannot be found +// else detailed error code. +// +//---------------------------------------------------------------------------- +HRESULT ClrDataAccess::GetWatsonBuckets(DWORD dwThreadId, GenericModeBlock * pGM) +{ + _ASSERTE((dwThreadId != 0) && (pGM != NULL)); + if ((dwThreadId == 0) || (pGM == NULL)) + { + return E_INVALIDARG; + } + + DAC_ENTER(); + + Thread * pThread = DacGetThread(dwThreadId); + _ASSERTE(pThread != NULL); + + HRESULT hr = E_UNEXPECTED; + + if (pThread != NULL) + { + hr = GetClrWatsonBucketsWorker(pThread, pGM); + } + + DAC_LEAVE(); + return hr; +} + +#endif // FEATURE_PAL + +//---------------------------------------------------------------------------- +// +// CLRDataAccessCreateInstance - create and initialize a ClrDataAccess object +// +// Arguments: +// pLegacyTarget - data target object +// pClrDataAccess - ClrDataAccess object +// +// Return Value: +// S_OK on success, else detailed error code. +// +//---------------------------------------------------------------------------- +STDAPI CLRDataAccessCreateInstance(ICLRDataTarget * pLegacyTarget, + ClrDataAccess ** pClrDataAccess) +{ + if ((pLegacyTarget == NULL) || (pClrDataAccess == NULL)) + { + return E_INVALIDARG; + } + + *pClrDataAccess = NULL; + + // Create an adapter which implements the new ICorDebugDataTarget interfaces using + // a legacy implementation of ICLRDataTarget + // ClrDataAccess will take a take a ref on this and delete it when it's released. + DataTargetAdapter * pDtAdapter = new (nothrow) DataTargetAdapter(pLegacyTarget); + if (!pDtAdapter) + { + return E_OUTOFMEMORY; + } + + ClrDataAccess* dacClass = new (nothrow) ClrDataAccess(pDtAdapter, pLegacyTarget); + if (!dacClass) + { + delete pDtAdapter; + return E_OUTOFMEMORY; + } + + HRESULT hr = dacClass->Initialize(); + if (FAILED(hr)) + { + dacClass->Release(); + return hr; + } + + *pClrDataAccess = dacClass; + return S_OK; +} + + +//---------------------------------------------------------------------------- +// +// CLRDataCreateInstance. +// Creates the IXClrData object +// This is the legacy entrypoint to DAC, used by dbgeng/dbghelp (windbg, SOS, watson, etc). +// +//---------------------------------------------------------------------------- +#ifdef __llvm__ +__attribute__((used)) +#endif // __llvm__ +STDAPI +CLRDataCreateInstance(REFIID iid, + ICLRDataTarget * pLegacyTarget, + void ** iface) +{ + if ((pLegacyTarget == NULL) || (iface == NULL)) + { + return E_INVALIDARG; + } + + *iface = NULL; + ClrDataAccess * pClrDataAccess; + HRESULT hr = CLRDataAccessCreateInstance(pLegacyTarget, &pClrDataAccess); + if (hr != S_OK) + { + return hr; + } + + hr = pClrDataAccess->QueryInterface(iid, iface); + + pClrDataAccess->Release(); + return hr; +} + + +//---------------------------------------------------------------------------- +// +// OutOfProcessExceptionEventGetProcessIdAndThreadId - get ProcessID and ThreadID +// +// Arguments: +// hProcess - process handle +// hThread - thread handle +// pPId - pointer to DWORD to store ProcessID +// pThreadId - pointer to DWORD to store ThreadID +// +// Return Value: +// TRUE if the operation is successful. +// FALSE if it fails +// +//---------------------------------------------------------------------------- +BOOL OutOfProcessExceptionEventGetProcessIdAndThreadId(HANDLE hProcess, HANDLE hThread, DWORD * pPId, DWORD * pThreadId) +{ + _ASSERTE((pPId != NULL) && (pThreadId != NULL)); + +#ifdef FEATURE_PAL + // UNIXTODO: mikem 1/13/15 Need appropriate PAL functions for getting ids + *pPId = (DWORD)hProcess; + *pThreadId = (DWORD)hThread; +#else +#if !defined(FEATURE_CORESYSTEM) + HMODULE hKernel32 = WszGetModuleHandle(W("kernel32.dll")); +#else + HMODULE hKernel32 = WszGetModuleHandle(W("api-ms-win-core-processthreads-l1-1-1.dll")); +#endif + if (hKernel32 == NULL) + { + return FALSE; + } + + typedef WINBASEAPI DWORD (WINAPI GET_PROCESSID_OF_THREAD)(HANDLE); + GET_PROCESSID_OF_THREAD * pGetProcessIdOfThread; + + typedef WINBASEAPI DWORD (WINAPI GET_THREADID)(HANDLE); + GET_THREADID * pGetThreadId; + + pGetProcessIdOfThread = (GET_PROCESSID_OF_THREAD *)GetProcAddress(hKernel32, "GetProcessIdOfThread"); + pGetThreadId = (GET_THREADID *)GetProcAddress(hKernel32, "GetThreadId"); + + // OOP callbacks are used on Win7 or later. We should have having below two APIs available. + _ASSERTE((pGetProcessIdOfThread != NULL) && (pGetThreadId != NULL)); + if ((pGetProcessIdOfThread == NULL) || (pGetThreadId == NULL)) + { + return FALSE; + } + + *pPId = (*pGetProcessIdOfThread)(hThread); + *pThreadId = (*pGetThreadId)(hThread); +#endif // FEATURE_PAL + return TRUE; +} + +// WER_RUNTIME_EXCEPTION_INFORMATION will be available from Win7 SDK once Win7 SDK is released. +#if !defined(WER_RUNTIME_EXCEPTION_INFORMATION) +typedef struct _WER_RUNTIME_EXCEPTION_INFORMATION +{ + DWORD dwSize; + HANDLE hProcess; + HANDLE hThread; + EXCEPTION_RECORD exceptionRecord; + CONTEXT context; +} WER_RUNTIME_EXCEPTION_INFORMATION, * PWER_RUNTIME_EXCEPTION_INFORMATION; +#endif // !defined(WER_RUNTIME_EXCEPTION_INFORMATION) + + +#ifndef FEATURE_PAL + +//---------------------------------------------------------------------------- +// +// OutOfProcessExceptionEventGetWatsonBucket - retrieve Watson buckets if it is a managed exception +// +// Arguments: +// pContext - the context passed at helper module registration +// pExceptionInformation - structure that contains information about the crash +// pGM - pointer to the space to store retrieved Watson buckets +// +// Return Value: +// S_OK if the operation is successful. +// or S_FALSE if it is not a managed exception or Watson buckets cannot be found +// else detailed error code. +// +//---------------------------------------------------------------------------- +STDAPI OutOfProcessExceptionEventGetWatsonBucket(__in PDWORD pContext, + __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation, + __out GenericModeBlock * pGMB) +{ + HANDLE hProcess = pExceptionInformation->hProcess; + HANDLE hThread = pExceptionInformation->hThread; + DWORD PId, ThreadId; + + if (!OutOfProcessExceptionEventGetProcessIdAndThreadId(hProcess, hThread, &PId, &ThreadId)) + { + return E_FAIL; + } + + CLRDATA_ADDRESS baseAddressOfRuntime = (CLRDATA_ADDRESS)pContext; + NewHolder<LiveProcDataTarget> dataTarget(NULL); + + dataTarget = new (nothrow) LiveProcDataTarget(hProcess, PId, baseAddressOfRuntime); + if (dataTarget == NULL) + { + return E_OUTOFMEMORY; + } + + NewHolder<ClrDataAccess> pClrDataAccess(NULL); + + HRESULT hr = CLRDataAccessCreateInstance(dataTarget, &pClrDataAccess); + if (hr != S_OK) + { + if (hr == S_FALSE) + { + return E_FAIL; + } + else + { + return hr; + } + } + + if (!pClrDataAccess->IsExceptionFromManagedCode(&pExceptionInformation->exceptionRecord)) + { + return S_FALSE; + } + + return pClrDataAccess->GetWatsonBuckets(ThreadId, pGMB); +} + +//---------------------------------------------------------------------------- +// +// OutOfProcessExceptionEventCallback - claim the ownership of this event if current +// runtime threw the unhandled exception +// +// Arguments: +// pContext - the context passed at helper module registration +// pExceptionInformation - structure that contains information about the crash +// pbOwnershipClaimed - output parameter for claiming the ownership of this event +// pwszEventName - name of the event. If this is NULL, pchSize cannot be NULL. +// This parameter is valid only if * pbOwnershipClaimed is TRUE. +// pchSize - the size of the buffer pointed by pwszEventName +// pdwSignatureCount - the count of signature parameters. Valid values range from +// 0 to 10. If the value returned is greater than 10, only the +// 1st 10 parameters are used for bucketing parameters. This +// parameter is valid only if * pbOwnershipClaimed is TRUE. +// +// Return Value: +// S_OK on success, else detailed error code. +// +// Note: +// This is the 1st function that is called into by WER. This API through its out +// parameters, tells WER as to whether or not it is claiming the crash. If it does +// claim the crash, WER uses the event name specified in the string pointed to by +// pwszEventName for error reporting. WER then proceed to call the +// OutOfProcessExceptionEventSignatureCallback to get the bucketing parameters from +// the helper dll. +// +// This function follows the multiple call paradigms. WER may call into this function +// with *pwszEventName pointer set to NULL. This is to indicate to the function, that +// WER wants to know the buffer size needed by the function to populate the string +// into the buffer. The function should return E_INSUFFICIENTBUFFER with the needed +// buffer size in *pchSize. WER shall then allocate a buffer of size *pchSize for +// pwszEventName and then call this function again at which point the function should +// populate the string and return S_OK. +// +// Note that *pdOwnershipClaimed should be set to TRUE everytime this function is called +// for the helper dll to claim ownership of bucketing. +// +// The Win7 WER spec is at +// http://windows/windows7/docs/COSD%20Documents/Fundamentals/Feedback%20Services%20and%20Platforms/WER-CLR%20Integration%20Dev%20Spec.docx +// +// !!!READ THIS!!! +// Since this is called by external modules it's important that we don't let any exceptions leak out (see Win8 95224). +// +//---------------------------------------------------------------------------- +STDAPI OutOfProcessExceptionEventCallback(__in PDWORD pContext, + __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation, + __out BOOL * pbOwnershipClaimed, + __out_ecount(*pchSize) PWSTR pwszEventName, + __inout PDWORD pchSize, + __out PDWORD pdwSignatureCount) +{ + SUPPORTS_DAC_HOST_ONLY; + + if ((pContext == NULL) || + (pExceptionInformation == NULL) || + (pExceptionInformation->dwSize < sizeof(WER_RUNTIME_EXCEPTION_INFORMATION)) || + (pbOwnershipClaimed == NULL) || + (pchSize == NULL) || + (pdwSignatureCount == NULL)) + { + return E_INVALIDARG; + } + + *pbOwnershipClaimed = FALSE; + + GenericModeBlock gmb; + HRESULT hr = E_FAIL; + + EX_TRY + { + // get Watson buckets if it is a managed exception + hr = OutOfProcessExceptionEventGetWatsonBucket(pContext, pExceptionInformation, &gmb); + } + EX_CATCH_HRESULT(hr); + + if (hr != S_OK) + { + // S_FALSE means either it is not a managed exception or we do not have Watson buckets. + // Since we have set pbOwnershipClaimed to FALSE, we return S_OK to WER. + if (hr == S_FALSE) + { + hr = S_OK; + } + + return hr; + } + + if ((pwszEventName == NULL) || (*pchSize <= wcslen(gmb.wzEventTypeName))) + { + *pchSize = static_cast<DWORD>(wcslen(gmb.wzEventTypeName)) + 1; + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + // copy custom event name + wcscpy_s(pwszEventName, *pchSize, gmb.wzEventTypeName); + *pdwSignatureCount = GetCountBucketParamsForEvent(gmb.wzEventTypeName); + *pbOwnershipClaimed = TRUE; + + return S_OK; +} + + +//---------------------------------------------------------------------------- +// +// OutOfProcessExceptionEventCallback - provide custom Watson buckets +// +// Arguments: +// pContext - the context passed at helper module registration +// pExceptionInformation - structure that contains information about the crash +// dwIndex - the index of the bucketing parameter being requested. Valid values are +// from 0 to 9 +// pwszName - pointer to the name of the bucketing parameter +// pchName - pointer to character count of the pwszName buffer. If pwszName points to +// null, *pchName represents the buffer size (represented in number of characters) +// needed to populate the name in pwszName. +// pwszValue - pointer to the value of the pwszName bucketing parameter +// pchValue - pointer to the character count of the pwszValue buffer. If pwszValue points +// to null, *pchValue represents the buffer size (represented in number of +// characters) needed to populate the value in pwszValue. +// +// Return Value: +// S_OK on success, else detailed error code. +// +// Note: +// This function is called by WER only if the call to OutOfProcessExceptionEventCallback() +// was successful and the value of *pbOwnershipClaimed was TRUE. This function is called +// pdwSignatureCount times to collect the bucketing parameters from the helper dll. +// +// This function also follows the multiple call paradigm as described for the +// OutOfProcessExceptionEventCallback() function. The buffer sizes needed for +// this function are of the pwszName and pwszValue buffers. +// +// !!!READ THIS!!! +// Since this is called by external modules it's important that we don't let any exceptions leak out (see Win8 95224). +// +//---------------------------------------------------------------------------- +STDAPI OutOfProcessExceptionEventSignatureCallback(__in PDWORD pContext, + __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation, + __in DWORD dwIndex, + __out_ecount(*pchName) PWSTR pwszName, + __inout PDWORD pchName, + __out_ecount(*pchValue) PWSTR pwszValue, + __inout PDWORD pchValue) +{ + SUPPORTS_DAC_HOST_ONLY; + + if ((pContext == NULL) || + (pExceptionInformation == NULL) || + (pExceptionInformation->dwSize < sizeof(WER_RUNTIME_EXCEPTION_INFORMATION)) || + (pchName == NULL) || + (pchValue == NULL)) + { + return E_INVALIDARG; + } + + if ((pwszName == NULL) || (*pchName == 0)) + { + *pchName = 1; + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + GenericModeBlock gmb; + const PWSTR pwszBucketValues[] = {gmb.wzP1, + gmb.wzP2, + gmb.wzP3, + gmb.wzP4, + gmb.wzP5, + gmb.wzP6, + gmb.wzP7, + gmb.wzP8, + gmb.wzP9, + gmb.wzP10}; + + HRESULT hr = E_FAIL; + + EX_TRY + { + // get Watson buckets if it is a managed exception + hr = OutOfProcessExceptionEventGetWatsonBucket(pContext, pExceptionInformation, &gmb); + } + EX_CATCH_HRESULT(hr); + +#ifndef FEATURE_WINDOWSPHONE + // we can't assert this on phone as it's possible for the OS to kill + // the faulting process before WER crash reporting has completed. + _ASSERTE(hr == S_OK); +#else + _ASSERTE(hr == S_OK || hr == CORDBG_E_READVIRTUAL_FAILURE); +#endif + if (hr != S_OK) + { + // S_FALSE means either it is not a managed exception or we do not have Watson buckets. + // Either case is a logic error becuase this function is called by WER only if the call + // to OutOfProcessExceptionEventCallback() was successful and the value of + // *pbOwnershipClaimed was TRUE. + if (hr == S_FALSE) + { + hr = E_FAIL; + } + + return hr; + } + + DWORD paramCount = GetCountBucketParamsForEvent(gmb.wzEventTypeName); + + if (dwIndex >= paramCount) + { + _ASSERTE(!"dwIndex is out of range"); + return E_INVALIDARG; + } + + // Return pwszName as an emptry string to let WER use localized version of "Parameter n" + *pwszName = W('\0'); + + if ((pwszValue == NULL) || (*pchValue <= wcslen(pwszBucketValues[dwIndex]))) + { + *pchValue = static_cast<DWORD>(wcslen(pwszBucketValues[dwIndex]))+ 1; + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + // copy custom Watson bucket value + wcscpy_s(pwszValue, *pchValue, pwszBucketValues[dwIndex]); + + return S_OK; +} + +#endif // FEATURE_PAL + +//---------------------------------------------------------------------------- +// +// OutOfProcessExceptionEventCallback - provide custom debugger launch string +// +// Arguments: +// pContext - the context passed at helper module registration +// pExceptionInformation - structure that contains information about the crash +// pbCustomDebuggerNeeded - pointer to a BOOL. If this BOOL is set to TRUE, then +// a custom debugger launch option is needed by the +// process. In that case, the subsequent parameters will +// be meaningfully used. If this is FALSE, the subsequent +// parameters will be ignored. +// pwszDebuggerLaunch - pointer to a string that will be used to launch the debugger, +// if the debugger is launched. The value of this string overrides +// the default debugger launch string used by WER. +// pchSize - pointer to the character count of the pwszDebuggerLaunch buffer. If +// pwszDebuggerLaunch points to null, *pchSize represents the buffer size +// (represented in number of characters) needed to populate the debugger +// launch string in pwszDebuggerLaunch. +// pbAutoLaunchDebugger - pointer to a BOOL. If this BOOL is set to TRUE, WER will +// directly launch the debugger. If set to FALSE, WER will show +// the debug option to the user in the WER UI. +// +// Return Value: +// S_OK on success, else detailed error code. +// +// Note: +// This function is called into by WER only if the call to OutOfProcessExceptionEventCallback() +// was successful and the value of *pbOwnershipClaimed was TRUE. This function allows the helper +// dll to customize the debugger launch options including the launch string. +// +// This function also follows the multiple call paradigm as described for the +// OutOfProcessExceptionEventCallback() function. The buffer sizes needed for +// this function are of the pwszName and pwszValue buffers. +// +//---------------------------------------------------------------------------- +STDAPI OutOfProcessExceptionEventDebuggerLaunchCallback(__in PDWORD pContext, + __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation, + __out BOOL * pbCustomDebuggerNeeded, + __out_ecount_opt(*pchSize) PWSTR pwszDebuggerLaunch, + __inout PDWORD pchSize, + __out BOOL * pbAutoLaunchDebugger) +{ + SUPPORTS_DAC_HOST_ONLY; + + if ((pContext == NULL) || + (pExceptionInformation == NULL) || + (pExceptionInformation->dwSize < sizeof(WER_RUNTIME_EXCEPTION_INFORMATION)) || + (pbCustomDebuggerNeeded == NULL) || + (pwszDebuggerLaunch == NULL) || + (pchSize == NULL) || + (pbAutoLaunchDebugger == NULL)) + { + return E_INVALIDARG; + } + + // Starting from CLRv4 managed debugger string and setting are unified with native debuggers. + // There is no need to provide custom debugger string for WER. + *pbCustomDebuggerNeeded = FALSE; + + return S_OK; +} + +// DacHandleEnum + +#include "handletablepriv.h" +#include "comcallablewrapper.h" + +DacHandleWalker::DacHandleWalker() + : mDac(0), m_instanceAge(0), mMap(0), mIndex(0), + mTypeMask(0), mGenerationFilter(-1), mChunkIndex(0), mCurr(0), + mIteratorIndex(0) +{ + SUPPORTS_DAC; +} + +DacHandleWalker::~DacHandleWalker() +{ + SUPPORTS_DAC; + + HandleChunkHead *curr = mHead.Next; + + while (curr) + { + HandleChunkHead *tmp = curr; + curr = curr->Next; + delete tmp; + } +} + +HRESULT DacHandleWalker::Init(ClrDataAccess *dac, UINT types[], UINT typeCount) +{ + SUPPORTS_DAC; + + if (dac == NULL || types == NULL) + return E_POINTER; + + mDac = dac; + m_instanceAge = dac->m_instanceAge; + + return Init(BuildTypemask(types, typeCount)); +} + +HRESULT DacHandleWalker::Init(ClrDataAccess *dac, UINT types[], UINT typeCount, int gen) +{ + SUPPORTS_DAC; + + if (gen < 0 || gen > (int)GCHeap::GetMaxGeneration()) + return E_INVALIDARG; + + mGenerationFilter = gen; + + return Init(dac, types, typeCount); +} + +HRESULT DacHandleWalker::Init(UINT32 typemask) +{ + SUPPORTS_DAC; + + mMap = &g_HandleTableMap; + mTypeMask = typemask; + + return S_OK; +} + +UINT32 DacHandleWalker::BuildTypemask(UINT types[], UINT typeCount) +{ + SUPPORTS_DAC; + + UINT32 mask = 0; + + for (UINT i = 0; i < typeCount; ++i) + { + _ASSERTE(types[i] < 32); + mask |= (1 << types[i]); + } + + return mask; +} + +HRESULT DacHandleWalker::Next(unsigned int celt, + SOSHandleData handles[], + unsigned int *pceltFetched) +{ + SUPPORTS_DAC; + + if (handles == NULL || pceltFetched == NULL) + return E_POINTER; + + SOSHelperEnter(); + + hr = DoHandleWalk<SOSHandleData, unsigned int, DacHandleWalker::EnumCallbackSOS>(celt, handles, pceltFetched); + + SOSHelperLeave(); + + return hr; +} + +bool DacHandleWalker::FetchMoreHandles(HANDLESCANPROC callback) +{ + SUPPORTS_DAC; + + // The table slots are based on the number of GC heaps in the process. + int max_slots = 1; + +#ifdef FEATURE_SVR_GC + if (GCHeap::IsServerHeap()) + max_slots = GCHeapCount(); +#endif // FEATURE_SVR_GC + + // Reset the Count on all cached chunks. We reuse chunks after allocating + // them, and the count is the only thing which needs resetting. + for (HandleChunkHead *curr = &mHead; curr; curr = curr->Next) + curr->Count = 0; + + DacHandleWalkerParam param(&mHead); + + do + { + // Have we advanced past the end of the current bucket? + if (mMap && mIndex >= INITIAL_HANDLE_TABLE_ARRAY_SIZE) + { + mIndex = 0; + mMap = mMap->pNext; + } + + // Have we walked the entire handle table map? + if (mMap == NULL) + { + mCurr = NULL; + return false; + } + + if (mMap->pBuckets[mIndex] != NULL) + { + for (int i = 0; i < max_slots; ++i) + { + HHANDLETABLE hTable = mMap->pBuckets[mIndex]->pTable[i]; + if (hTable) + { + // Yikes! The handle table callbacks don't produce the handle type or + // the AppDomain that we need, and it's too difficult to propogate out + // these things (especially the type) without worrying about performance + // implications for the GC. Instead we'll have the callback walk each + // type individually. There are only a few handle types, and the handle + // table has a fast-path for only walking a single type anyway. + UINT32 handleType = 0; + for (UINT32 mask = mTypeMask; mask; mask >>= 1, handleType++) + { + if (mask & 1) + { + HandleTable *pTable = (HandleTable *)hTable; + PTR_AppDomain pDomain = SystemDomain::GetAppDomainAtIndex(pTable->uADIndex); + param.AppDomain = TO_CDADDR(pDomain.GetAddr()); + param.Type = handleType; + + // Either enumerate the handles regularly, or walk the handle + // table as the GC does if a generation filter was requested. + if (mGenerationFilter != -1) + HndScanHandlesForGC(hTable, callback, + (LPARAM)¶m, 0, + &handleType, 1, + mGenerationFilter, GCHeap::GetMaxGeneration(), 0); + else + HndEnumHandles(hTable, &handleType, 1, callback, (LPARAM)¶m, 0, FALSE); + } + } + } + } + } + + // Stop looping as soon as we have found data. We also stop if we have a failed HRESULT during + // the callback (this should indicate OOM). + mIndex++; + } while (mHead.Count == 0 && SUCCEEDED(param.Result)); + + mCurr = mHead.Next; + return true; +} + + +HRESULT DacHandleWalker::Skip(unsigned int celt) +{ + return E_NOTIMPL; +} + +HRESULT DacHandleWalker::Reset() +{ + return E_NOTIMPL; +} + +HRESULT DacHandleWalker::GetCount(unsigned int *pcelt) +{ + return E_NOTIMPL; +} + + +void DacHandleWalker::GetRefCountedHandleInfo( + OBJECTREF oref, unsigned int uType, + unsigned int *pRefCount, unsigned int *pJupiterRefCount, BOOL *pIsPegged, BOOL *pIsStrong) +{ + SUPPORTS_DAC; + +#ifdef FEATURE_COMINTEROP + if (uType == HNDTYPE_REFCOUNTED) + { + // get refcount from the CCW + PTR_ComCallWrapper pWrap = ComCallWrapper::GetWrapperForObject(oref); + if (pWrap != NULL) + { + if (pRefCount) + *pRefCount = (unsigned int)pWrap->GetRefCount(); + + if (pJupiterRefCount) + *pJupiterRefCount = (unsigned int)pWrap->GetJupiterRefCount(); + + if (pIsPegged) + *pIsPegged = pWrap->IsConsideredPegged(); + + if (pIsStrong) + *pIsStrong = pWrap->IsWrapperActive(); + + return; + } + } +#endif // FEATURE_COMINTEROP + + if (pRefCount) + *pRefCount = 0; + + if (pJupiterRefCount) + *pJupiterRefCount = 0; + + if (pIsPegged) + *pIsPegged = FALSE; + + if (pIsStrong) + *pIsStrong = FALSE; +} + +void CALLBACK DacHandleWalker::EnumCallbackSOS(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(SOSHandleData))) + { + 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. + SOSHandleData *dataArray = (SOSHandleData*)curr->pData; + SOSHandleData &data = dataArray[curr->Count++]; + + data.Handle = TO_CDADDR(handle.GetAddr()); + data.Type = param->Type; + if (param->Type == HNDTYPE_DEPENDENT) + data.Secondary = GetDependentHandleSecondary(handle.GetAddr()).GetAddr(); +#ifdef FEATURE_COMINTEROP + else if (param->Type == HNDTYPE_WEAK_WINRT) + data.Secondary = HndGetHandleExtraInfo(handle.GetAddr()); +#endif // FEATURE_COMINTEROP + else + data.Secondary = 0; + data.AppDomain = param->AppDomain; + GetRefCountedHandleInfo((OBJECTREF)*handle, param->Type, &data.RefCount, &data.JupiterRefCount, &data.IsPegged, &data.StrongReference); + data.StrongReference |= (BOOL)IsAlwaysStrongReference(param->Type); +} + +DacStackReferenceWalker::DacStackReferenceWalker(ClrDataAccess *dac, DWORD osThreadID) + : mDac(dac), m_instanceAge(dac ? dac->m_instanceAge : 0), mThread(0), mErrors(0), mEnumerated(false), + mChunkIndex(0), mCurr(0), mIteratorIndex(0) +{ + Thread *curr = NULL; + + for (curr = ThreadStore::GetThreadList(curr); + curr; + curr = ThreadStore::GetThreadList(curr)) + { + if (curr->GetOSThreadId() == osThreadID) + { + mThread = curr; + break; + } + } +} + +DacStackReferenceWalker::~DacStackReferenceWalker() +{ + StackRefChunkHead *curr = mHead.next; + + while (curr) + { + StackRefChunkHead *tmp = curr; + curr = curr->next; + delete tmp; + } +} + +HRESULT DacStackReferenceWalker::Init() +{ + if (!mThread) + return E_INVALIDARG; + return mHeap.Init(); +} + +HRESULT STDMETHODCALLTYPE DacStackReferenceWalker::Skip(unsigned int count) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE DacStackReferenceWalker::Reset() +{ + return E_NOTIMPL; +} + +HRESULT DacStackReferenceWalker::GetCount(unsigned int *pCount) +{ + if (!pCount) + return E_POINTER; + + SOSHelperEnter(); + + if (!mEnumerated) + { + // Fill out our data structures. + WalkStack<unsigned int, SOSStackRefData>(0, NULL, DacStackReferenceWalker::GCReportCallbackSOS, DacStackReferenceWalker::GCEnumCallbackSOS); + } + + unsigned int count = 0; + for(StackRefChunkHead *curr = &mHead; curr; curr = curr->next) + count += curr->count; + + *pCount = count; + + SOSHelperLeave(); + return hr; +} + +HRESULT DacStackReferenceWalker::Next(unsigned int count, + SOSStackRefData stackRefs[], + unsigned int *pFetched) +{ + if (stackRefs == NULL || pFetched == NULL) + return E_POINTER; + + SOSHelperEnter(); + + hr = DoStackWalk<unsigned int, SOSStackRefData, + DacStackReferenceWalker::GCReportCallbackSOS, + DacStackReferenceWalker::GCEnumCallbackSOS> + (count, stackRefs, pFetched); + + SOSHelperLeave(); + + return hr; +} + +HRESULT DacStackReferenceWalker::EnumerateErrors(ISOSStackRefErrorEnum **ppEnum) +{ + if (!ppEnum) + return E_POINTER; + + SOSHelperEnter(); + + if (mThread) + { + // Fill out our data structures. + WalkStack<unsigned int, SOSStackRefData>(0, NULL, DacStackReferenceWalker::GCReportCallbackSOS, DacStackReferenceWalker::GCEnumCallbackSOS); + } + + DacStackReferenceErrorEnum *pEnum = new DacStackReferenceErrorEnum(this, mErrors); + hr = pEnum->QueryInterface(__uuidof(ISOSStackRefErrorEnum), (void**)ppEnum); + + SOSHelperLeave(); + return hr; +} + +CLRDATA_ADDRESS DacStackReferenceWalker::ReadPointer(TADDR addr) +{ + ULONG32 bytesRead = 0; + TADDR result = 0; + HRESULT hr = mDac->m_pTarget->ReadVirtual(addr, (BYTE*)&result, sizeof(TADDR), &bytesRead); + + if (FAILED(hr) || (bytesRead != sizeof(TADDR))) + return (CLRDATA_ADDRESS)~0; + + return TO_CDADDR(result); +} + + +void DacStackReferenceWalker::GCEnumCallbackSOS(LPVOID hCallback, OBJECTREF *pObject, uint32_t flags, DacSlotLocation loc) +{ + GCCONTEXT *gcctx = (GCCONTEXT *)hCallback; + DacScanContext *dsc = (DacScanContext*)gcctx->sc; + + // Yuck. The GcInfoDecoder reports a local pointer for registers (as it's reading out of the REGDISPLAY + // in the stack walk), and it reports a TADDR for stack locations. This is architecturally difficulty + // to fix, so we are leaving it for now. + TADDR addr = 0; + TADDR obj = 0; + + if (loc.targetPtr) + { + addr = (TADDR)pObject; + obj = TO_TADDR(dsc->pWalker->ReadPointer((CORDB_ADDRESS)addr)); + } + else + { + obj = pObject->GetAddr(); + } + + if (flags & GC_CALL_INTERIOR) + { + CORDB_ADDRESS fixed_obj = 0; + HRESULT hr = dsc->pWalker->mHeap.ListNearObjects((CORDB_ADDRESS)obj, NULL, &fixed_obj, NULL); + + // If we failed...oh well, SOS won't mind. We'll just report the interior pointer as is. + if (SUCCEEDED(hr)) + obj = TO_TADDR(fixed_obj); + } + + SOSStackRefData *data = dsc->pWalker->GetNextObject<SOSStackRefData>(dsc); + if (data != NULL) + { + // Report where the object and where it was found. + data->HasRegisterInformation = true; + data->Register = loc.reg; + data->Offset = loc.regOffset; + data->Address = TO_CDADDR(addr); + data->Object = TO_CDADDR(obj); + data->Flags = flags; + + // Report the frame that the data came from. + data->StackPointer = TO_CDADDR(dsc->sp); + + if (dsc->pFrame) + { + data->SourceType = SOS_StackSourceFrame; + data->Source = dac_cast<PTR_Frame>(dsc->pFrame).GetAddr(); + } + else + { + data->SourceType = SOS_StackSourceIP; + data->Source = TO_CDADDR(dsc->pc); + } + } +} + + +void DacStackReferenceWalker::GCReportCallbackSOS(PTR_PTR_Object ppObj, ScanContext *sc, uint32_t flags) +{ + DacScanContext *dsc = (DacScanContext*)sc; + CLRDATA_ADDRESS obj = dsc->pWalker->ReadPointer(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...oh well, SOS won't mind. We'll just report the interior pointer as is. + if (SUCCEEDED(hr)) + obj = TO_CDADDR(fixed_addr); + } + + SOSStackRefData *data = dsc->pWalker->GetNextObject<SOSStackRefData>(dsc); + if (data != NULL) + { + data->HasRegisterInformation = false; + data->Register = 0; + data->Offset = 0; + data->Address = ppObj.GetAddr(); + data->Object = obj; + data->Flags = flags; + data->StackPointer = TO_CDADDR(dsc->sp); + + if (dsc->pFrame) + { + data->SourceType = SOS_StackSourceFrame; + data->Source = dac_cast<PTR_Frame>(dsc->pFrame).GetAddr(); + } + else + { + data->SourceType = SOS_StackSourceIP; + data->Source = TO_CDADDR(dsc->pc); + } + } +} + +StackWalkAction DacStackReferenceWalker::Callback(CrawlFrame *pCF, VOID *pData) +{ + // + // KEEP IN SYNC WITH GcStackCrawlCallBack in vm\gcscan.cpp + // + + GCCONTEXT *gcctx = (GCCONTEXT*)pData; + DacScanContext *dsc = (DacScanContext*)gcctx->sc; + + MethodDesc *pMD = pCF->GetFunction(); + gcctx->sc->pMD = pMD; + gcctx->sc->pCurrentDomain = pCF->GetAppDomain(); + + PREGDISPLAY pRD = pCF->GetRegisterSet(); + dsc->sp = (TADDR)GetRegdisplaySP(pRD);; + dsc->pc = PCODEToPINSTR(GetControlPC(pRD)); + + ResetPointerHolder<CrawlFrame*> rph(&gcctx->cf); + gcctx->cf = pCF; + + bool fReportGCReferences = true; +#if defined(WIN64EXCEPTIONS) + // On Win64 and ARM, we may have unwound this crawlFrame and thus, shouldn't report the invalid + // references it may contain. + // todo. + fReportGCReferences = pCF->ShouldCrawlframeReportGCReferences(); +#endif // defined(WIN64EXCEPTIONS) + + Frame *pFrame = ((DacScanContext*)gcctx->sc)->pFrame = pCF->GetFrame(); + + EX_TRY + { + if (fReportGCReferences) + { + if (pCF->IsFrameless()) + { + ICodeManager * pCM = pCF->GetCodeManager(); + _ASSERTE(pCM != NULL); + + unsigned flags = pCF->GetCodeManagerFlags(); + + pCM->EnumGcRefs(pCF->GetRegisterSet(), + pCF->GetCodeInfo(), + flags, + dsc->pEnumFunc, + pData); + } + else + { + pFrame->GcScanRoots(gcctx->f, gcctx->sc); + } + } + } + EX_CATCH + { + SOSStackErrorList *err = new SOSStackErrorList; + err->pNext = NULL; + + if (pFrame) + { + err->error.SourceType = SOS_StackSourceFrame; + err->error.Source = dac_cast<PTR_Frame>(pFrame).GetAddr(); + } + else + { + err->error.SourceType = SOS_StackSourceIP; + err->error.Source = TO_CDADDR(dsc->pc); + } + + if (dsc->pWalker->mErrors == NULL) + { + dsc->pWalker->mErrors = err; + } + else + { + // This exception case should be non-existent. It only happens when there is either + // a clr!Frame on the callstack which is not properly dac-ized, or when a call down + // EnumGcRefs causes a data read exception. Since this is so rare, we don't worry + // about making this code very efficient. + SOSStackErrorList *curr = dsc->pWalker->mErrors; + while (curr->pNext) + curr = curr->pNext; + + curr->pNext = err; + } + } + EX_END_CATCH(SwallowAllExceptions) + +#if 0 + // todo + + // If we're executing a LCG dynamic method then we must promote the associated resolver to ensure it + // doesn't get collected and yank the method code out from under us). + + // Be careful to only promote the reference -- we can also be called to relocate the reference and + // that can lead to all sorts of problems since we could be racing for the relocation with the long + // weak handle we recover the reference from. Promoting the reference is enough, the handle in the + // reference will be relocated properly as long as we keep it alive till the end of the collection + // as long as the reference is actually maintained by the long weak handle. + if (pMD) + { + BOOL fMaybeCollectibleMethod = TRUE; + + // If this is a frameless method then the jitmanager can answer the question of whether + // or not this is LCG simply by looking at the heap where the code lives, however there + // is also the prestub case where we need to explicitly look at the MD for stuff that isn't + // ngen'd + if (pCF->IsFrameless() && pMD->IsLCGMethod()) + { + fMaybeCollectibleMethod = ExecutionManager::IsCollectibleMethod(pCF->GetMethodToken()); + } + + if (fMaybeCollectibleMethod && pMD->IsLCGMethod()) + { + PTR_Object obj = OBJECTREFToObject(pMD->AsDynamicMethodDesc()->GetLCGMethodResolver()->GetManagedResolver()); + dsc->pWalker->ReportObject(obj); + } + else + { + if (fMaybeCollectibleMethod) + { + PTR_Object obj = pMD->GetLoaderAllocator()->GetExposedObject(); + dsc->pWalker->ReportObject(obj); + } + + if (fReportGCReferences) + { + GenericParamContextType paramContextType = GENERIC_PARAM_CONTEXT_NONE; + + if (pCF->IsFrameless()) + { + // We need to grab the Context Type here because there are cases where the MethodDesc + // is shared, and thus indicates there should be an instantion argument, but the JIT + // was still allowed to optimize it away and we won't grab it below because we're not + // reporting any references from this frame. + paramContextType = pCF->GetCodeManager()->GetParamContextType(pCF->GetRegisterSet(), pCF->GetCodeInfo()); + } + else + { + if (pMD->RequiresInstMethodDescArg()) + paramContextType = GENERIC_PARAM_CONTEXT_METHODDESC; + else if (pMD->RequiresInstMethodTableArg()) + paramContextType = GENERIC_PARAM_CONTEXT_METHODTABLE; + } + + // Handle the case where the method is a static shared generic method and we need to keep the type of the generic parameters alive + if (paramContextType == GENERIC_PARAM_CONTEXT_METHODDESC) + { + MethodDesc *pMDReal = dac_cast<PTR_MethodDesc>(pCF->GetParamTypeArg()); + _ASSERTE((pMDReal != NULL) || !pCF->IsFrameless()); + if (pMDReal != NULL) + { + PTR_Object obj = pMDReal->GetLoaderAllocator()->GetExposedObject(); + dsc->pWalker->ReportObject(obj); + } + } + else if (paramContextType == GENERIC_PARAM_CONTEXT_METHODTABLE) + { + MethodTable *pMTReal = dac_cast<PTR_MethodTable>(pCF->GetParamTypeArg()); + _ASSERTE((pMTReal != NULL) || !pCF->IsFrameless()); + if (pMTReal != NULL) + { + PTR_Object obj = pMTReal->GetLoaderAllocator()->GetExposedObject(); + dsc->pWalker->ReportObject(obj); + } + } + } + } + } +#endif + + return SWA_CONTINUE; +} + + +DacStackReferenceErrorEnum::DacStackReferenceErrorEnum(DacStackReferenceWalker *pEnum, SOSStackErrorList *pErrors) + : mEnum(pEnum), mHead(pErrors), mCurr(pErrors) +{ + _ASSERTE(mEnum); + + if (mHead != NULL) + mEnum->AddRef(); +} + +DacStackReferenceErrorEnum::~DacStackReferenceErrorEnum() +{ + if (mHead) + mEnum->Release(); +} + +HRESULT DacStackReferenceErrorEnum::Skip(unsigned int count) +{ + unsigned int i = 0; + for (i = 0; i < count && mCurr; ++i) + mCurr = mCurr->pNext; + + return i < count ? S_FALSE : S_OK; +} + +HRESULT DacStackReferenceErrorEnum::Reset() +{ + mCurr = mHead; + + return S_OK; +} + +HRESULT DacStackReferenceErrorEnum::GetCount(unsigned int *pCount) +{ + SOSStackErrorList *curr = mHead; + unsigned int count = 0; + + while (curr) + { + curr = curr->pNext; + count++; + } + + *pCount = count; + return S_OK; +} + +HRESULT DacStackReferenceErrorEnum::Next(unsigned int count, SOSStackRefError ref[], unsigned int *pFetched) +{ + if (pFetched == NULL || ref == NULL) + return E_POINTER; + + unsigned int i; + for (i = 0; i < count && mCurr; ++i, mCurr = mCurr->pNext) + ref[i] = mCurr->error; + + *pFetched = i; + return i < count ? S_FALSE : S_OK; +} diff --git a/src/debug/daccess/daccess.targets b/src/debug/daccess/daccess.targets new file mode 100644 index 0000000000..a7d9e41554 --- /dev/null +++ b/src/debug/daccess/daccess.targets @@ -0,0 +1,67 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <!--*****************************************************--> + <!--This MSBuild project file was automatically generated--> + <!--from the original SOURCES/DIRS file by the KBC tool.--> + <!--*****************************************************--> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\dac.props" /> + <Import Project="..\SetDebugTargetLocal.props" /> + <!--Leaf project Properties--> + <PropertyGroup> + <UseStl Condition="'$(BuildForCoreSystem)' != 'true'">true</UseStl> + <DaccessSrcDirectory>$(ClrSrcDirectory)\debug\daccess</DaccessSrcDirectory> + <UserIncludes>$(UserIncludes); + $(DaccessSrcDirectory); + $(ClrSrcDirectory)\vm; + $(ClrSrcDirectory)\vm\$(TargetCpu); + $(ClrSrcDirectory)\debug\inc; + $(ClrSrcDirectory)\debug\inc\$(TargetCpu); + $(ClrSrcDirectory)\debug\inc\dump; + $(ClrSrcDirectory)\debug\ee; + $(ClrSrcDirectory)\inc; + $(ClrSrcDirectory)\inc\$(IntermediateOutputDirectory); + $(VCToolsIncPath); + $(ClrSrcDirectory)\gcdump; + $(ClrSrcDirectory)\md\inc; + $(ClrSrcDirectory)\gc; + $(ClrSrcDirectory)\strongname\inc</UserIncludes> + <CDefines>$(CDefines);UNICODE;_UNICODE;$(USER_SPECIFIC_C_DEFINES);FEATURE_NO_HOST</CDefines> + <OutputName Condition="'$(OutputName)' == ''">dac_wks</OutputName> + <OutputPath>$(ClrLibDest)</OutputPath> + <TargetType>LIBRARY</TargetType> + <PCHHeader>stdafx.h</PCHHeader> + <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders> + <PCHCompile>$(DaccessSrcDirectory)\stdafx.cpp</PCHCompile> + </PropertyGroup> + <!--Leaf Project Items--> + <ItemGroup> + <ProjectReference Include="$(ClrSrcDirectory)inc\corguids.nativeproj" /> + </ItemGroup> + + <ItemGroup> + <CppCompile Include="$(DaccessSrcDirectory)\dacdbiimpl.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\dacdbiimpllocks.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\dacdbiimplstackwalk.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\daccess.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\dacfn.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\enummem.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\fntableaccess.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\inspect.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\reimpl.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\request.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\request_svr.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\stack.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\task.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\nidump.cpp" /> + <CppCompile Include="$(DaccessSrcDirectory)\datatargetadapter.cpp" /> + </ItemGroup> + <ItemGroup> + <CppCompile Condition="'$(TargetArch)' == 'i386'" Include="$(DaccessSrcDirectory)\i386\primitives.cpp" /> + <CppCompile Condition="'$(TargetArch)' == 'amd64'" Include="$(DaccessSrcDirectory)\amd64\primitives.cpp" /> + <CppCompile Condition="'$(TargetArch)' == 'arm'" Include="$(DaccessSrcDirectory)\arm\primitives.cpp" /> + <CppCompile Condition="'$(TargetArch)' == 'arm64'" Include="$(DaccessSrcDirectory)\arm64\primitives.cpp" /> + </ItemGroup> + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> 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; +} diff --git a/src/debug/daccess/dacdbiimpl.h b/src/debug/daccess/dacdbiimpl.h new file mode 100644 index 0000000000..56d7b0d1d7 --- /dev/null +++ b/src/debug/daccess/dacdbiimpl.h @@ -0,0 +1,1152 @@ +// 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. +//***************************************************************************** +// DacDbiImpl.h +// + +// +// Implement the interface between the DAC and DBI. +//***************************************************************************** + +#ifndef _DACDBI_IMPL_H_ +#define _DACDBI_IMPL_H_ + +// Prototype for creation function +STDAPI +DacDbiInterfaceInstance( + ICorDebugDataTarget * pTarget, + CORDB_ADDRESS baseAddress, + IDacDbiInterface::IAllocator * pAllocator, + IDacDbiInterface::IMetaDataLookup * pMetaDataLookup, + IDacDbiInterface ** ppInterface); + +//--------------------------------------------------------------------------------------- +// +// This implements the DAC/DBI interface. See that interface declaration for +// full documentation on these methods. +// +// Assumptions: +// This class is free-threaded and provides its own synchronization. +// +// Notes: +// It inherits from ClrDataAccess to get the DAC-management implementation, and to +// override GetMDImport. +// +class DacDbiInterfaceImpl : + public ClrDataAccess, + public IDacDbiInterface +{ +public: + // Ctor to instantiate a DAC reader around a given data-target. + DacDbiInterfaceImpl(ICorDebugDataTarget * pTarget, CORDB_ADDRESS baseAddress, IAllocator * pAllocator, IMetaDataLookup * pLookup); + + // Destructor. + virtual ~DacDbiInterfaceImpl(void); + + // Overridden from ClrDataAccess. Gets an internal metadata importer for the file. + virtual IMDInternalImport* GetMDImport( + const PEFile* pPEFile, + const ReflectionModule * pReflectionModule, + bool fThrowEx); + + + // Check whether the version of the DBI matches the version of the runtime. + HRESULT CheckDbiVersion(const DbiVersion * pVersion); + + // Flush the DAC cache. This should be called when target memory changes. + HRESULT FlushCache(); + + // enable or disable DAC target consistency checks + void DacSetTargetConsistencyChecks(bool fEnableAsserts); + + // Destroy the interface object. The client should call this when it's done + // with the IDacDbiInterface to free up any resources. + void Destroy(); + + IAllocator * GetAllocator() + { + return m_pAllocator; + } + + + // Is Left-side started up? + BOOL IsLeftSideInitialized(); + + // Get an LS Appdomain via an AppDomain unique ID. + // Fails if the AD is not found or if the ID is invalid. + VMPTR_AppDomain GetAppDomainFromId(ULONG appdomainId); + + // Get the AppDomain ID for an AppDomain. + ULONG GetAppDomainId(VMPTR_AppDomain vmAppDomain); + + // Get the managed AppDomain object for an AppDomain. + VMPTR_OBJECTHANDLE GetAppDomainObject(VMPTR_AppDomain vmAppDomain); + + // Determine if the specified AppDomain is the default domain + BOOL IsDefaultDomain(VMPTR_AppDomain vmAppDomain); + + // Get the full AD friendly name for the appdomain. + void GetAppDomainFullName( + VMPTR_AppDomain vmAppDomain, + IStringHolder * pStrName); + + // Get the values of the JIT Optimization and EnC flags. + void GetCompilerFlags (VMPTR_DomainFile vmDomainFile, + BOOL * pfAllowJITOpts, + BOOL * pfEnableEnC); + + // Helper function for SetCompilerFlags to set EnC status + bool CanSetEnCBits(Module * pModule); + + // Set the values of the JIT optimization and EnC flags. + HRESULT SetCompilerFlags(VMPTR_DomainFile vmDomainFile, + BOOL fAllowJitOpts, + BOOL fEnableEnC); + + + // Initialize the native/IL sequence points and native var info for a function. + void GetNativeCodeSequencePointsAndVarInfo(VMPTR_MethodDesc vmMethodDesc, + CORDB_ADDRESS startAddr, + BOOL fCodeAvailable, + NativeVarData * pNativeVarData, + SequencePoints * pSequencePoints); + + bool IsThreadSuspendedOrHijacked(VMPTR_Thread vmThread); + + + bool AreGCStructuresValid(); + HRESULT CreateHeapWalk(HeapWalkHandle *pHandle); + void DeleteHeapWalk(HeapWalkHandle handle); + + HRESULT WalkHeap(HeapWalkHandle handle, + ULONG count, + OUT COR_HEAPOBJECT * objects, + OUT ULONG *fetched); + + HRESULT GetHeapSegments(OUT DacDbiArrayList<COR_SEGMENT> *pSegments); + + + bool IsValidObject(CORDB_ADDRESS obj); + + bool GetAppDomainForObject(CORDB_ADDRESS obj, OUT VMPTR_AppDomain * pApp, OUT VMPTR_Module *pModule, OUT VMPTR_DomainFile *mod); + + + + HRESULT CreateRefWalk(RefWalkHandle * pHandle, BOOL walkStacks, BOOL walkFQ, UINT32 handleWalkMask); + void DeleteRefWalk(RefWalkHandle handle); + HRESULT WalkRefs(RefWalkHandle handle, ULONG count, OUT DacGcReference * objects, OUT ULONG *pFetched); + + HRESULT GetTypeID(CORDB_ADDRESS obj, COR_TYPEID *pID); + + HRESULT GetObjectFields(COR_TYPEID id, ULONG32 celt, COR_FIELD *layout, ULONG32 *pceltFetched); + HRESULT GetTypeLayout(COR_TYPEID id, COR_TYPE_LAYOUT *pLayout); + HRESULT GetArrayLayout(COR_TYPEID id, COR_ARRAY_LAYOUT *pLayout); + void GetGCHeapInformation(COR_HEAPINFO * pHeapInfo); + HRESULT GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TADDR* pAddrMDInternalRW); + HRESULT GetReJitInfo(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ReJitInfo* pReJitInfo); + HRESULT GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_ReJitInfo* pReJitInfo); + HRESULT GetSharedReJitInfo(VMPTR_ReJitInfo vmReJitInfo, VMPTR_SharedReJitInfo* pSharedReJitInfo); + HRESULT GetSharedReJitInfoData(VMPTR_SharedReJitInfo sharedReJitInfo, DacSharedReJitInfo* pData); + HRESULT GetDefinesBitField(ULONG32 *pDefines); + HRESULT GetMDStructuresVersion(ULONG32* pMDStructuresVersion); + +private: + void TypeHandleToExpandedTypeInfoImpl(AreValueTypesBoxed boxed, + VMPTR_AppDomain vmAppDomain, + TypeHandle typeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo); + + // Get the number of fixed arguments to a function, i.e., the explicit args and the "this" pointer. + SIZE_T GetArgCount(MethodDesc * pMD); + + // Get locations and code offsets for local variables and arguments in a function + void GetNativeVarData(MethodDesc * pMethodDesc, + CORDB_ADDRESS startAddr, + SIZE_T fixedArgCount, + NativeVarData * pVarInfo); + + // Get the native/IL sequence points for a function + void GetSequencePoints(MethodDesc * pMethodDesc, + CORDB_ADDRESS startAddr, + SequencePoints * pNativeMap); + + // Helper to compose a IL->IL and IL->Native mapping + void ComposeMapping(InstrumentedILOffsetMapping profilerILMap, ICorDebugInfo::OffsetMapping nativeMap[], ULONG32* pEntryCount); + + // Helper function to convert an instrumented IL offset to the corresponding original IL offset. + ULONG TranslateInstrumentedILOffsetToOriginal(ULONG ilOffset, + const InstrumentedILOffsetMapping * pMapping); + +public: +//---------------------------------------------------------------------------------- + // class MapSortILMap: A template class that will sort an array of DebuggerILToNativeMap. + // This class is intended to be instantiated on the stack / in temporary storage, and used + // to reorder the sequence map. + //---------------------------------------------------------------------------------- + class MapSortILMap : public CQuickSort<DebuggerILToNativeMap> + { + public: + //Constructor + MapSortILMap(DebuggerILToNativeMap * map, + int count) + : CQuickSort<DebuggerILToNativeMap>(map, count) {} + + // secondary key comparison--if two IL offsets are the same, + // we determine order based on native offset + int CompareInternal(DebuggerILToNativeMap * first, + DebuggerILToNativeMap * second); + + //Comparison operator + int Compare(DebuggerILToNativeMap * first, + DebuggerILToNativeMap * second); + }; + + + // 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 GetILCodeAndSig(VMPTR_DomainFile vmDomainFile, + mdToken functionToken, + TargetBuffer * pCodeInfo, + mdToken * pLocalSigToken); + + // Gets the following information about the native code blob for a function, if the native + // code is available: + // its method desc + // whether it's an instantiated generic + // its EnC version number + // hot and cold region information. + void GetNativeCodeInfo(VMPTR_DomainFile vmDomainFile, + mdToken functionToken, + NativeCodeFunctionData * pCodeInfo); + + // Gets the following information about the native code blob for a function + // its method desc + // whether it's an instantiated generic + // its EnC version number + // hot and cold region information. + void GetNativeCodeInfoForAddr(VMPTR_MethodDesc vmMethodDesc, + CORDB_ADDRESS hotCodeStartAddr, + NativeCodeFunctionData * pCodeInfo); + +private: + // Get start addresses and sizes for hot and cold regions for a native code blob + void GetMethodRegionInfo(MethodDesc * pMethodDesc, + NativeCodeFunctionData * pCodeInfo); + +public: + // Determine if a type is a ValueType + BOOL IsValueType (VMPTR_TypeHandle th); + + // Determine if a type has generic parameters + BOOL HasTypeParams (VMPTR_TypeHandle th); + + // Get type information for a class + void GetClassInfo (VMPTR_AppDomain vmAppDomain, + VMPTR_TypeHandle thExact, + ClassInfo * pData); + + // get field information and object size for an instantiated generic type + void GetInstantiationFieldInfo (VMPTR_DomainFile vmDomainFile, + VMPTR_TypeHandle vmThExact, + VMPTR_TypeHandle vmThApprox, + DacDbiArrayList<FieldData> * pFieldList, + SIZE_T * pObjectSize); + + + void GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, + VMPTR_AppDomain vmAppDomain, + CORDB_ADDRESS addr, + DebuggerIPCE_ExpandedTypeData *pTypeInfo); + + + void GetObjectExpandedTypeInfoFromID(AreValueTypesBoxed boxed, + VMPTR_AppDomain vmAppDomain, + COR_TYPEID id, + DebuggerIPCE_ExpandedTypeData *pTypeInfo); + + + // @dbgtodo Microsoft inspection: change DebuggerIPCE_ExpandedTypeData to DacDbiStructures type hierarchy + // once ICorDebugType and ICorDebugClass are DACized + // use a type handle to get the information needed to create the corresponding RS CordbType instance + void TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, + VMPTR_AppDomain vmAppDomain, + VMPTR_TypeHandle vmTypeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo); + + // Get type handle for a TypeDef token, if one exists. For generics this returns the open type. + VMPTR_TypeHandle GetTypeHandle(VMPTR_Module vmModule, + mdTypeDef metadataToken); + + // Get the approximate type handle for an instantiated type. This may be identical to the exact type handle, + // but if we have code sharing for generics,it may differ in that it may have canonical type parameters. + VMPTR_TypeHandle GetApproxTypeHandle(TypeInfoList * pTypeData); + + // Get the exact type handle from type data + HRESULT GetExactTypeHandle(DebuggerIPCE_ExpandedTypeData * pTypeData, + ArgInfoList * pArgInfo, + VMPTR_TypeHandle& vmTypeHandle); + + // 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 GetMethodDescParams(VMPTR_AppDomain vmAppDomain, + VMPTR_MethodDesc vmMethodDesc, + GENERICS_TYPE_TOKEN genericsToken, + UINT32 * pcGenericClassTypeParams, + TypeParamsList * pGenericTypeParams); + + // Get the target field address of a context or thread local static. + CORDB_ADDRESS GetThreadOrContextStaticAddress(VMPTR_FieldDesc vmField, + VMPTR_Thread vmRuntimeThread); + + // Get the target field address of a collectible types static. + CORDB_ADDRESS GetCollectibleTypeStaticAddress(VMPTR_FieldDesc vmField, + VMPTR_AppDomain vmAppDomain); + + // Get information about a field added with Edit And Continue. + void GetEnCHangingFieldInfo(const EnCHangingFieldInfo * pEnCFieldInfo, + FieldData * pFieldData, + BOOL * pfStatic); + + // 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. + // This should not fail except for OOM + + void GetTypeHandleParams(VMPTR_AppDomain vmAppDomain, + VMPTR_TypeHandle vmTypeHandle, + TypeParamsList * pParams); + + // DacDbi API: GetSimpleType + // gets the metadata token and domain file corresponding to a simple type + void GetSimpleType(VMPTR_AppDomain vmAppDomain, + CorElementType simpleType, + mdTypeDef * pMetadataToken, + VMPTR_Module * pVmModule, + VMPTR_DomainFile * pVmDomainFile); + + BOOL IsExceptionObject(VMPTR_Object vmObject); + + void GetStackFramesFromException(VMPTR_Object vmObject, DacDbiArrayList<DacExceptionCallStackData>& dacStackFrames); + + // Returns true if the argument is a runtime callable wrapper + BOOL IsRcw(VMPTR_Object vmObject); + + // retrieves the list of COM interfaces implemented by vmObject, as it is known at + // the time of the call (the list may change as new interface types become available + // in the runtime) + void GetRcwCachedInterfaceTypes( + VMPTR_Object vmObject, + VMPTR_AppDomain vmAppDomain, + BOOL bIInspectableOnly, + OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pDacInterfaces); + + // retrieves the list of interfaces pointers implemented by vmObject, as it is known at + // the time of the call (the list may change as new interface types become available + // in the runtime) + void GetRcwCachedInterfacePointers( + VMPTR_Object vmObject, + BOOL bIInspectableOnly, + OUT DacDbiArrayList<CORDB_ADDRESS> * pDacItfPtrs); + + // retrieves a list of interface types corresponding to the passed in + // list of IIDs. the interface types are retrieved from an app domain + // IID / Type cache, that is updated as new types are loaded. will + // have NULL entries corresponding to unknown IIDs in "iids" + void GetCachedWinRTTypesForIIDs( + VMPTR_AppDomain vmAppDomain, + DacDbiArrayList<GUID> & iids, + OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pTypes); + + // retrieves the whole app domain cache of IID / Type mappings. + void GetCachedWinRTTypes( + VMPTR_AppDomain vmAppDomain, + OUT DacDbiArrayList<GUID> * pGuids, + OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pTypes); + +private: + BOOL IsExceptionObject(MethodTable* pMT); + + // Get the approximate and exact type handles for a type + void GetTypeHandles(VMPTR_TypeHandle vmThExact, + VMPTR_TypeHandle vmThApprox, + TypeHandle * pThExact, + TypeHandle * pThApprox); + + // Gets the total number of fields for a type. + unsigned int GetTotalFieldCount(TypeHandle thApprox); + + // initializes various values of the ClassInfo data structure, including the + // field count, generic args count, size and value class flag + void InitClassData(TypeHandle thApprox, + BOOL fIsInstantiatedType, + ClassInfo * pData); + + // Gets the base table addresses for both GC and non-GC statics + void GetStaticsBases(TypeHandle thExact, + AppDomain * pAppDomain, + PTR_BYTE * ppGCStaticsBase, + PTR_BYTE * ppNonGCStaticsBase); + + // Computes the field info for pFD and stores it in pcurrentFieldData + void ComputeFieldData(PTR_FieldDesc pFD, + PTR_BYTE pGCStaticsBase, + PTR_BYTE pNonGCStaticsBase, + FieldData * pCurrentFieldData); + + // Gets information for all the fields for a given type + void CollectFields(TypeHandle thExact, + TypeHandle thApprox, + AppDomain * pAppDomain, + DacDbiArrayList<FieldData> * pFieldList); + + // Gets additional information to convert a type handle to an instance of CordbType if the type is E_T_ARRAY + void GetArrayTypeInfo(TypeHandle typeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo, + AppDomain * pAppDomain); + + // Gets additional information to convert a type handle to an instance of CordbType if the type is + // E_T_PTR or E_T_BYREF + void GetPtrTypeInfo(AreValueTypesBoxed boxed, + TypeHandle typeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo, + AppDomain * pAppDomain); + + // Gets additional information to convert a type handle to an instance of CordbType if the type is E_T_FNPTR + void GetFnPtrTypeInfo(AreValueTypesBoxed boxed, + TypeHandle typeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo, + AppDomain * pAppDomain); + + // Gets additional information to convert a type handle to an instance of CordbType if the type is + // E_T_CLASS or E_T_VALUETYPE + void GetClassTypeInfo(TypeHandle typeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo, + AppDomain * pAppDomain); + + // Gets the correct CorElementType value from a type handle + CorElementType GetElementType (TypeHandle typeHandle); + + // 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 + void TypeHandleToBasicTypeInfo(TypeHandle typeHandle, + DebuggerIPCE_BasicTypeData * pTypeInfo, + AppDomain * pAppDomain); + + // wrapper routines to set up for a call to ClassLoader functions to retrieve a type handle for a + // particular kind of type + + // find a loaded type handle for a primitive type + static TypeHandle FindLoadedElementType(CorElementType elementType); + + // find a loaded type handle for an array type (E_T_ARRAY or E_T_SZARRAY) + static TypeHandle FindLoadedArrayType(CorElementType elementType, TypeHandle typeArg, unsigned rank); + + // find a loaded type handle for an address type (E_T_PTR or E_T_BYREF) + static TypeHandle FindLoadedPointerOrByrefType(CorElementType elementType, TypeHandle typeArg); + + // find a loaded type handle for a function pointer type (E_T_FNPTR) + static TypeHandle FindLoadedFnptrType(DWORD numTypeArgs, TypeHandle * pInst); + + // find a loaded type handle for a particular instantiation of a class type (E_T_CLASS or E_T_VALUETYPE) + static TypeHandle FindLoadedInstantiation(Module * pModule, + mdTypeDef mdToken, + DWORD nTypeArgs, + TypeHandle * pInst); + + + // TypeDataWalk + // This class provides functionality to allow us to read type handles for generic type parameters or the + // argument of an array or address type. It takes code sharing into account and allows us to get the canonical + // form where necessary. It operates on a list of type arguments gathered on the RS and passed to the constructor. + // See code:CordbType::GatherTypeData for more information. + // + class TypeDataWalk + { + private: + // list of type arguments + DebuggerIPCE_TypeArgData * m_pCurrentData; + + // number of type arguments still to be processed + unsigned int m_nRemaining; + + public: + typedef enum {kGetExact, kGetCanonical} TypeHandleReadType; + // constructor + TypeDataWalk(DebuggerIPCE_TypeArgData *pData, unsigned int nData); + + // 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. + TypeHandle ReadLoadedTypeHandle(TypeHandleReadType retrieveWhich); + + private: + // skip a single node from the list of type handles + void Skip(); + + // read and return a single node from the list of type parameters + DebuggerIPCE_TypeArgData * ReadOne(); + + // + // These are for type arguments. They return null if the item could not be found. + // They also optionally find the canonical form for the specified type + // (used if generic code sharing is enabled) even if the exact form has not + // yet been loaded for some reason + // + + // Read a type handle when it is used in the position of a generic argument or + // argument of an array 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() (null) if any of the type handles are not loaded. + TypeHandle ReadLoadedTypeArg(TypeHandleReadType retrieveWhich); + + // Iterate through the type argument data, creating type handles as we go. + // Return FALSE if any of the type handles are not loaded. + BOOL ReadLoadedTypeHandles(TypeHandleReadType retrieveWhich, unsigned int nTypeArgs, TypeHandle *ppResults); + + // Read an instantiation of a generic type if it has already been created. + TypeHandle ReadLoadedInstantiation(TypeHandleReadType retrieveWhich, + Module * pModule, + mdTypeDef mdToken, + unsigned int nTypeArgs); + + // These are helper functions to get the type handle for specific classes of types + TypeHandle ArrayTypeArg(DebuggerIPCE_TypeArgData * pData, TypeHandleReadType retrieveWhich); + TypeHandle PtrOrByRefTypeArg(DebuggerIPCE_TypeArgData * pData, TypeHandleReadType retrieveWhich); + TypeHandle FnPtrTypeArg(DebuggerIPCE_TypeArgData * pData, TypeHandleReadType retrieveWhich); + TypeHandle ClassTypeArg(DebuggerIPCE_TypeArgData * pData, TypeHandleReadType retrieveWhich); + TypeHandle ObjRefOrPrimitiveTypeArg(DebuggerIPCE_TypeArgData * pData, CorElementType elementType); + + }; // class TypeDataWalk + + // get a typehandle for a class or valuetype from basic type data (metadata token + // and domain file + TypeHandle GetClassOrValueTypeHandle(DebuggerIPCE_BasicTypeData * pData); + + // get an exact type handle for an array type + TypeHandle GetExactArrayTypeHandle(DebuggerIPCE_ExpandedTypeData * pTopLevelTypeData, + ArgInfoList * pArgInfo); + + // get an exact type handle for a PTR or BYREF type + TypeHandle GetExactPtrOrByRefTypeHandle(DebuggerIPCE_ExpandedTypeData * pTopLevelTypeData, + ArgInfoList * pArgInfo); + + // get an exact type handle for a CLASS or VALUETYPE type + TypeHandle GetExactClassTypeHandle(DebuggerIPCE_ExpandedTypeData * pTopLevelTypeData, + ArgInfoList * pArgInfo); + + // get an exact type handle for a FNPTR type + TypeHandle GetExactFnPtrTypeHandle(ArgInfoList * pArgInfo); + + // 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. + TypeHandle BasicTypeInfoToTypeHandle(DebuggerIPCE_BasicTypeData * pArgTypeData); + + // 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. + TypeHandle ExpandedTypeInfoToTypeHandle(DebuggerIPCE_ExpandedTypeData * pTopLevelTypeData, + ArgInfoList * pArgInfo); + + // Initialize information about a field added with EnC + void InitFieldData(const FieldDesc * pFD, + const PTR_CBYTE pORField, + const EnCHangingFieldInfo * pEncFieldData, + FieldData * pFieldData); + + // Get the address of a field added with EnC. + PTR_CBYTE GetPtrToEnCField(FieldDesc * pFD, const EnCHangingFieldInfo * pEnCFieldInfo); + + // Get the FieldDesc corresponding to a particular EnC field token + FieldDesc * GetEnCFieldDesc(const EnCHangingFieldInfo * pEnCFieldInfo); + + // Finds information for a particular class field + PTR_FieldDesc FindField(TypeHandle thApprox, mdFieldDef fldToken); + +// ============================================================================ +// functions to get information about instances of ICDValue implementations +// ============================================================================ + +public: + // Get object information for a TypedByRef object. Initializes the objRef and typedByRefType fields of + // pObjectData (type info for the referent). + void GetTypedByRefInfo(CORDB_ADDRESS pTypedByRef, + VMPTR_AppDomain vmAppDomain, + DebuggerIPCE_ObjectData * pObjectData); + + // Get the string length and offset to string base for a string object + void GetStringData(CORDB_ADDRESS objectAddress, DebuggerIPCE_ObjectData * pObjectData); + + // 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 GetArrayData(CORDB_ADDRESS objectAddress, DebuggerIPCE_ObjectData * pObjectData); + + // Get information about an object for which we have a reference, including the object size and + // type information. + void GetBasicObjectInfo(CORDB_ADDRESS objectAddress, + CorElementType type, + VMPTR_AppDomain vmAppDomain, + DebuggerIPCE_ObjectData * pObjectData); + + // Returns the thread which owns the monitor lock on an object and the acquisition count + MonitorLockInfo GetThreadOwningMonitorLock(VMPTR_Object vmObject); + + + // Enumerate all threads waiting on the monitor event for an object + void EnumerateMonitorEventWaitList(VMPTR_Object vmObject, + FP_THREAD_ENUMERATION_CALLBACK fpCallback, + CALLBACK_DATA pUserData); + +private: + // Helper function for CheckRef. Sanity check an object. + HRESULT FastSanityCheckObject(PTR_Object objPtr); + + // 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. + bool CheckRef(PTR_Object objPtr); + + // Initialize basic object information: type handle, object size, offset to fields and expanded type + // information. + void InitObjectData(PTR_Object objPtr, + VMPTR_AppDomain vmAppDomain, + DebuggerIPCE_ObjectData * pObjectData); + +// ============================================================================ +// Functions to test data safety. In these functions we determine whether a lock +// is held in a code path we need to execute for inspection. If so, we throw an +// exception. +// ============================================================================ + +#ifdef TEST_DATA_CONSISTENCY +public: + void TestCrst(VMPTR_Crst vmCrst); + void TestRWLock(VMPTR_SimpleRWLock vmRWLock); +#endif + +// ============================================================================ +// CordbAssembly, CordbModule +// ============================================================================ + + using ClrDataAccess::GetModuleData; + using ClrDataAccess::GetAddressType; + +public: + // Get the full path and file name to the assembly's manifest module. + BOOL GetAssemblyPath(VMPTR_Assembly vmAssembly, + IStringHolder * pStrFilename); + + void GetAssemblyFromDomainAssembly(VMPTR_DomainAssembly vmDomainAssembly, VMPTR_Assembly *vmAssembly); + + // Determines whether the runtime security system has assigned full-trust to this assembly. + BOOL IsAssemblyFullyTrusted(VMPTR_DomainAssembly vmDomainAssembly); + + // get a type def resolved across modules + void ResolveTypeReference(const TypeRefData * pTypeRefInfo, + TypeRefData * pTargetRefInfo); + + // Get the full path and file name to the module (if any). + BOOL GetModulePath(VMPTR_Module vmModule, + IStringHolder * pStrFilename); + + // Get the full path and file name to the ngen image for the module (if any). + BOOL GetModuleNGenPath(VMPTR_Module vmModule, + IStringHolder * pStrFilename); + + // Implementation of IDacDbiInterface::GetModuleSimpleName + void GetModuleSimpleName(VMPTR_Module vmModule, IStringHolder * pStrFilename); + + // Implementation of IDacDbiInterface::GetMetadata + void GetMetadata(VMPTR_Module vmModule, TargetBuffer * pTargetBuffer); + + // Implementation of IDacDbiInterface::GetSymbolsBuffer + void GetSymbolsBuffer(VMPTR_Module vmModule, TargetBuffer * pTargetBuffer, SymbolFormat * pSymbolFormat); + + // Gets properties for a module + void GetModuleData(VMPTR_Module vmModule, ModuleInfo * pData); + + // Gets properties for a domainfile + void GetDomainFileData(VMPTR_DomainFile vmDomainFile, DomainFileInfo * pData); + + void GetModuleForDomainFile(VMPTR_DomainFile vmDomainFile, OUT VMPTR_Module * pModule); + + // Yields true if the adddress is a CLR stub. + BOOL IsTransitionStub(CORDB_ADDRESS address); + + // Get the "type" of address. + AddressType GetAddressType(CORDB_ADDRESS address); + + + // Enumerate the appdomains + void EnumerateAppDomains(FP_APPDOMAIN_ENUMERATION_CALLBACK fpCallback, + void * pUserData); + + // Enumerate the assemblies in the appdomain. + void EnumerateAssembliesInAppDomain(VMPTR_AppDomain vmAppDomain, + FP_ASSEMBLY_ENUMERATION_CALLBACK fpCallback, + void * pUserData); + + // Enumerate the moduels in the given assembly. + void EnumerateModulesInAssembly( + VMPTR_DomainAssembly vmAssembly, + FP_MODULE_ENUMERATION_CALLBACK fpCallback, + void * pUserData + ); + + // When stopped at an event, request a synchronization. + void RequestSyncAtEvent(); + + //sets flag Debugger::m_sendExceptionsOutsideOfJMC on the LS + HRESULT SetSendExceptionsOutsideOfJMC(BOOL sendExceptionsOutsideOfJMC); + + // Notify the debuggee that a debugger attach is pending. + void MarkDebuggerAttachPending(); + + // Notify the debuggee that a debugger is attached. + void MarkDebuggerAttached(BOOL fAttached); + + // Enumerate connections in the process. + void EnumerateConnections(FP_CONNECTION_CALLBACK fpCallback, void * pUserData); + + void EnumerateThreads(FP_THREAD_ENUMERATION_CALLBACK fpCallback, void * pUserData); + + bool IsThreadMarkedDead(VMPTR_Thread vmThread); + + // Return the handle of the specified thread. + HANDLE GetThreadHandle(VMPTR_Thread vmThread); + + // Return the object handle for the managed Thread object corresponding to the specified thread. + VMPTR_OBJECTHANDLE GetThreadObject(VMPTR_Thread vmThread); + + // Set and reset the TSNC_DebuggerUserSuspend bit on the state of the specified thread + // according to the CorDebugThreadState. + void SetDebugState(VMPTR_Thread vmThread, + CorDebugThreadState debugState); + + // Returns TRUE if there is a current exception which is unhandled + BOOL HasUnhandledException(VMPTR_Thread vmThread); + + // Return the user state of the specified thread. + CorDebugUserState GetUserState(VMPTR_Thread vmThread); + + // Returns the user state of the specified thread except for USER_UNSAFE_POINT. + CorDebugUserState GetPartialUserState(VMPTR_Thread vmThread); + + // Return the connection ID of the specified thread. + CONNID GetConnectionID(VMPTR_Thread vmThread); + + // Return the task ID of the specified thread. + TASKID GetTaskID(VMPTR_Thread vmThread); + + // Return the OS thread ID of the specified thread + DWORD TryGetVolatileOSThreadID(VMPTR_Thread vmThread); + + // Return the unique thread ID of the specified thread. + DWORD GetUniqueThreadID(VMPTR_Thread vmThread); + + // 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 GetCurrentException(VMPTR_Thread vmThread); + + // Return the object handle to the managed object for a given CCW pointer. + VMPTR_OBJECTHANDLE GetObjectForCCW(CORDB_ADDRESS ccwPtr); + + // 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. + // 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 GetCurrentCustomDebuggerNotification(VMPTR_Thread vmThread); + + + // Return the current appdomain the specified thread is in. + VMPTR_AppDomain GetCurrentAppDomain(VMPTR_Thread vmThread); + + // Given an assembly ref token and metadata scope (via the DomainFile), resolve the assembly. + VMPTR_DomainAssembly ResolveAssembly(VMPTR_DomainFile vmScope, mdToken tkAssemblyRef); + + + // Hijack the thread + void Hijack( + VMPTR_Thread vmThread, + ULONG32 dwThreadId, + const EXCEPTION_RECORD * pRecord, + T_CONTEXT * pOriginalContext, + ULONG32 cbSizeContext, + EHijackReason::EHijackReason reason, + void * pUserData, + CORDB_ADDRESS * pRemoteContextAddr); + + // Return the filter CONTEXT on the LS. + VMPTR_CONTEXT GetManagedStoppedContext(VMPTR_Thread vmThread); + + // Create and return a stackwalker on the specified thread. + void CreateStackWalk(VMPTR_Thread vmThread, + DT_CONTEXT * pInternalContextBuffer, + StackWalkHandle * ppSFIHandle); + + // Delete the stackwalk object + void DeleteStackWalk(StackWalkHandle ppSFIHandle); + + // Get the CONTEXT of the current frame at which the stackwalker is stopped. + void GetStackWalkCurrentContext(StackWalkHandle pSFIHandle, + DT_CONTEXT * pContext); + + void GetStackWalkCurrentContext(StackFrameIterator * pIter, DT_CONTEXT * pContext); + + // Set the stackwalker to the specified CONTEXT. + void SetStackWalkCurrentContext(VMPTR_Thread vmThread, + StackWalkHandle pSFIHandle, + CorDebugSetContextFlag flag, + DT_CONTEXT * pContext); + + // Unwind the stackwalker to the next frame. + BOOL UnwindStackWalkFrame(StackWalkHandle pSFIHandle); + + HRESULT CheckContext(VMPTR_Thread vmThread, + const DT_CONTEXT * pContext); + + // Retrieve information about the current frame from the stackwalker. + FrameType GetStackWalkCurrentFrameInfo(StackWalkHandle pSFIHandle, + DebuggerIPCE_STRData * pFrameData); + + // Return the number of internal frames on the specified thread. + ULONG32 GetCountOfInternalFrames(VMPTR_Thread vmThread); + + // Enumerate the internal frames on the specified thread and invoke the provided callback on each of them. + void EnumerateInternalFrames(VMPTR_Thread vmThread, + FP_INTERNAL_FRAME_ENUMERATION_CALLBACK fpCallback, + void * pUserData); + + // Given the FramePointer of the parent frame and the FramePointer of the current frame, + // check if the current frame is the parent frame. + BOOL IsMatchingParentFrame(FramePointer fpToCheck, FramePointer fpParent); + + // Return the stack parameter size of the given method. + ULONG32 GetStackParameterSize(CORDB_ADDRESS controlPC); + + // Return the FramePointer of the current frame at which the stackwalker is stopped. + FramePointer GetFramePointer(StackWalkHandle pSFIHandle); + + FramePointer GetFramePointerWorker(StackFrameIterator * pIter); + + // Return TRUE if the specified CONTEXT is the CONTEXT of the leaf frame. + // @dbgtodo filter CONTEXT - Currently we check for the filter CONTEXT first. + BOOL IsLeafFrame(VMPTR_Thread vmThread, + const DT_CONTEXT * pContext); + + // DacDbi API: Get the context for a particular thread of the target process + void GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pContextBuffer); + + // This is a simple helper function to convert a CONTEXT to a DebuggerREGDISPLAY. We need to do this + // inside DDI because the RS has no notion of REGDISPLAY. + void ConvertContextToDebuggerRegDisplay(const DT_CONTEXT * pInContext, + DebuggerREGDISPLAY * pOutDRD, + BOOL fActive); + + // Check if the given method is an IL stub or an LCD method. + DynamicMethodType IsILStubOrLCGMethod(VMPTR_MethodDesc vmMethodDesc); + + // Return a TargetBuffer for the raw vararg signature. + TargetBuffer GetVarArgSig(CORDB_ADDRESS VASigCookieAddr, + CORDB_ADDRESS * pArgBase); + + // returns TRUE if the type requires 8-byte alignment + BOOL RequiresAlign8(VMPTR_TypeHandle thExact); + + // Resolve the raw generics token to the real generics type token. The resolution is based on the + // given index. + GENERICS_TYPE_TOKEN ResolveExactGenericArgsToken(DWORD dwExactGenericArgsTokenIndex, + GENERICS_TYPE_TOKEN rawToken); + + // Enumerate all monitors blocking a thread + void EnumerateBlockingObjects(VMPTR_Thread vmThread, + FP_BLOCKINGOBJECT_ENUMERATION_CALLBACK fpCallback, + CALLBACK_DATA pUserData); + + // Returns a bitfield reflecting the managed debugging state at the time of + // the jit attach. + CLR_DEBUGGING_PROCESS_FLAGS GetAttachStateFlags(); + +protected: + // This class used to be stateless, but we are relaxing the requirements + // slightly to gain perf. We should still be stateless in the sense that an API call + // should always return the same result regardless of the internal state. Hence + // a caller can not distinguish that we have any state. Internally however we are + // allowed to cache pieces of frequently used data to improve the perf of various + // operations. All of this cached data should be flushed when the DAC is flushed. + + // But it can have helper methods. + + // The allocator object is conceptually stateless. It lets us allocate data buffers to hand back. + IAllocator * m_pAllocator; + + // Callback to DBI to get internal metadata. + IMetaDataLookup * m_pMetaDataLookup; + + + // Metadata lookups is just a property on the PEFile in the normal builds, + // and so VM code tends to access the same metadata importer many times in a row. + // Cache the most-recently used to avoid excessive redundant lookups. + + // PEFile of Cached Importer. Invalidated between Flush calls. If this is Non-null, + // then the importer is m_pCachedImporter, and we can avoid using IMetaDataLookup + VMPTR_PEFile m_pCachedPEFile; + + // Value of cached importer, corresponds with m_pCachedPEFile. + IMDInternalImport * m_pCachedImporter; + + // Value of cached hijack function list, corresponds to g_pDebugger->m_rgHijackFunction + BOOL m_isCachedHijackFunctionValid; + TargetBuffer m_pCachedHijackFunction[Debugger::kMaxHijackFunctions]; + + // Helper to write structured data to target. + template<typename T> + void SafeWriteStructOrThrow(CORDB_ADDRESS pRemotePtr, const T * pLocalBuffer); + + // Helper to read structured data from the target process. + template<typename T> + void SafeReadStructOrThrow(CORDB_ADDRESS pRemotePtr, T * pLocalBuffer); + + TADDR GetHijackAddress(); + + void AlignStackPointer(CORDB_ADDRESS * pEsp); + + template <class T> + CORDB_ADDRESS PushHelper(CORDB_ADDRESS * pEsp, const T * pData, BOOL fAlignStack); + + // Write an EXCEPTION_RECORD structure to the remote target at the specified address while taking + // into account the number of exception parameters. + void WriteExceptionRecordHelper(CORDB_ADDRESS pRemotePtr, const EXCEPTION_RECORD * pExcepRecord); + + typedef DPTR(struct DebuggerIPCControlBlock) PTR_DebuggerIPCControlBlock; + + // Get the address of the Debugger control block on the helper thread. Returns + // NULL if the control block has not been successfully allocated + CORDB_ADDRESS GetDebuggerControlBlockAddress(); + + // Creates a VMPTR of an Object from a target address + VMPTR_Object GetObject(CORDB_ADDRESS ptr); + + // sets state in the native binder + HRESULT EnableNGENPolicy(CorDebugNGENPolicy ePolicy); + + // Sets the NGEN compiler flags. This restricts NGEN to only use images with certain + // types of pregenerated code. + HRESULT SetNGENCompilerFlags(DWORD dwFlags); + + // Gets the NGEN compiler flags currently in effect. + HRESULT GetNGENCompilerFlags(DWORD *pdwFlags); + + // Creates a VMPTR of an Object from a target address pointing to an OBJECTREF + VMPTR_Object GetObjectFromRefPtr(CORDB_ADDRESS ptr); + + // Get the target address from a VMPTR_OBJECTHANDLE, i.e., the handle address + CORDB_ADDRESS GetHandleAddressFromVmHandle(VMPTR_OBJECTHANDLE vmHandle); + + // Gets the target address of an VMPTR of an Object + TargetBuffer GetObjectContents(VMPTR_Object vmObj); + + // Create a VMPTR_OBJECTHANDLE from a CORDB_ADDRESS pointing to an object handle + VMPTR_OBJECTHANDLE GetVmObjectHandle(CORDB_ADDRESS handleAddress); + + // Validate that the VMPTR_OBJECTHANDLE refers to a legitimate managed object + BOOL IsVmObjectHandleValid(VMPTR_OBJECTHANDLE vmHandle); + + // if the specified module is a WinRT module then isWinRT will equal TRUE + HRESULT IsWinRTModule(VMPTR_Module vmModule, BOOL& isWinRT); + + // Determines the app domain id for the object refered to by a given VMPTR_OBJECTHANDLE + ULONG GetAppDomainIdFromVmObjectHandle(VMPTR_OBJECTHANDLE vmHandle); + +private: + bool IsThreadMarkedDeadWorker(Thread * pThread); + + // Check whether the specified thread is at a GC-safe place, i.e. in an interruptible region. + BOOL IsThreadAtGCSafePlace(VMPTR_Thread vmThread); + + // Fill in the structure with information about the current frame at which the stackwalker is stopped + void InitFrameData(StackFrameIterator * pIter, + FrameType ft, + DebuggerIPCE_STRData * pFrameData); + + // Helper method to fill in the address and the size of the hot and cold regions. + void InitNativeCodeAddrAndSize(TADDR taStartAddr, + DebuggerIPCE_JITFuncData * pJITFuncData); + + // Fill in the information about the parent frame. + void InitParentFrameInfo(CrawlFrame * pCF, + DebuggerIPCE_JITFuncData * pJITFuncData); + + // Return the stack parameter size of the given method. + ULONG32 GetStackParameterSize(EECodeInfo * pCodeInfo); + + typedef enum + { + kFromManagedToUnmanaged, + kFromUnmanagedToManaged, + } StackAdjustmentDirection; + + // Adjust the stack pointer in the CONTEXT for the stack parameter. + void AdjustRegDisplayForStackParameter(REGDISPLAY * pRD, + DWORD cbStackParameterSize, + BOOL fIsActiveFrame, + StackAdjustmentDirection direction); + + // Given an explicit frame, return the corresponding type in terms of CorDebugInternalFrameType. + CorDebugInternalFrameType GetInternalFrameType(Frame * pFrame); + + // Helper method to convert a REGDISPLAY to a CONTEXT. + void UpdateContextFromRegDisp(REGDISPLAY * pRegDisp, + T_CONTEXT * pContext); + + // Check if a control PC is in one of the native functions which require special unwinding. + bool IsRuntimeUnwindableStub(PCODE taControlPC); + + // Given the REGDISPLAY of a stack frame for one of the redirect functions, retrieve the original CONTEXT + // before the thread redirection. + PTR_CONTEXT RetrieveHijackedContext(REGDISPLAY * pRD); + + // Unwind special native stack frame which the runtime knows how to unwind. + BOOL UnwindRuntimeStackFrame(StackFrameIterator * pIter); + + // Look up the EnC version number of a particular jitted instance of a managed method. + void LookupEnCVersions(Module* pModule, + VMPTR_MethodDesc vmMethodDesc, + mdMethodDef mdMethod, + CORDB_ADDRESS pNativeStartAddress, + SIZE_T * pLatestEnCVersion, + SIZE_T * pJittedInstanceEnCVersion = NULL); + + // @dbgtodo - This method should be removed once CordbFunctionBreakpoint and SetIP are moved OOP and + // no longer use nativeCodeJITInfoToken. + void SetDJIPointer(Module * pModule, + MethodDesc * pMD, + mdMethodDef mdMethod, + DebuggerIPCE_JITFuncData * pJITFuncData); + + // 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. + mdSignature GetILCodeAndSigHelper(Module * pModule, + MethodDesc * pMD, + mdMethodDef mdMethodToken, + RVA methodRVA, + TargetBuffer * pIL); + +public: + // APIs for picking up the info needed for a debugger to look up an ngen image or IL image + // from it's search path. + bool GetMetaDataFileInfoFromPEFile(VMPTR_PEFile vmPEFile, + DWORD &dwTimeStamp, + DWORD &dwSize, + bool &isNGEN, + IStringHolder* pStrFilename); + + bool GetILImageInfoFromNgenPEFile(VMPTR_PEFile vmPEFile, + DWORD &dwTimeStamp, + DWORD &dwSize, + IStringHolder* pStrFilename); + +}; + + +// Global allocator for DD. Access is protected under the g_dacCritSec lock. +extern "C" IDacDbiInterface::IAllocator * g_pAllocator; + + +class DDHolder +{ +public: + DDHolder(DacDbiInterfaceImpl* pContainer, bool fAllowReentrant) + { + EnterCriticalSection(&g_dacCritSec); + + // If we're not re-entrant, then assert. + if (!fAllowReentrant) + { + _ASSERTE(g_dacImpl == NULL); + } + + // This cast is safe because ClrDataAccess can't call the DacDbi layer. + m_pOldContainer = static_cast<DacDbiInterfaceImpl *> (g_dacImpl); + m_pOldAllocator = g_pAllocator; + + g_dacImpl = pContainer; + g_pAllocator = pContainer->GetAllocator(); + + } + ~DDHolder() + { + // If an exception is being thrown, we won't be in the PAL (but in normal return paths it will). + + g_dacImpl = m_pOldContainer; + g_pAllocator = m_pOldAllocator; + + LeaveCriticalSection(&g_dacCritSec); + } + +protected: + DacDbiInterfaceImpl * m_pOldContainer; + IDacDbiInterface::IAllocator * m_pOldAllocator; +}; + + +// Use this macro at the start of each DD function. +// This may nest if a DD primitive takes in a callback that then calls another DD primitive. +#define DD_ENTER_MAY_THROW \ + DDHolder __dacHolder(this, true); \ + + +// Non-reentrant version of DD_ENTER_MAY_THROW. Asserts non-reentrancy. +// Use this macro at the start of each DD function. +// This may nest if a DD primitive takes in a callback that then calls another DD primitive. +#define DD_NON_REENTRANT_MAY_THROW \ + DDHolder __dacHolder(this, false); \ + +#include "dacdbiimpl.inl" + +class DacRefWalker +{ +public: + DacRefWalker(ClrDataAccess *dac, BOOL walkStacks, BOOL walkFQ, UINT32 handleMask); + ~DacRefWalker(); + + HRESULT Init(); + HRESULT Next(ULONG celt, DacGcReference roots[], ULONG *pceltFetched); + +private: + UINT32 GetHandleWalkerMask(); + void Clear(); + HRESULT NextThread(); + +private: + ClrDataAccess *mDac; + BOOL mWalkStacks, mWalkFQ; + UINT32 mHandleMask; + + // Stacks + DacStackReferenceWalker *mStackWalker; + + // Handles + DacHandleWalker *mHandleWalker; + + // FQ + PTR_PTR_Object mFQStart; + PTR_PTR_Object mFQEnd; + PTR_PTR_Object mFQCurr; +}; + +#endif // _DACDBI_IMPL_H_ diff --git a/src/debug/daccess/dacdbiimpl.inl b/src/debug/daccess/dacdbiimpl.inl new file mode 100644 index 0000000000..c9deae7fee --- /dev/null +++ b/src/debug/daccess/dacdbiimpl.inl @@ -0,0 +1,79 @@ +// 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. +//***************************************************************************** +// DacDbiImpl.inl +// + +// +// Inline functions for DacDbiImpl.h +// +//***************************************************************************** + +#ifndef _DACDBI_IMPL_INL_ +#define _DACDBI_IMPL_INL_ + +#include "dacdbiimpl.h" + +//--------------------------------------------------------------------------------------- +// Helper to write a structure to the target +// +// Arguments: +// T - type of structure to read. +// pRemotePtr - remote pointer into target (dest). +// pLocalBuffer - local buffer to write (Src). +// +// Return Value: +// Throws on error. +// +// Notes: +// This just does a raw Byte copy into the Target, but does not do any Marshalling. +// This fails if any part of the buffer can't be written. +// +// +//--------------------------------------------------------------------------------------- +template<typename T> +void DacDbiInterfaceImpl::SafeWriteStructOrThrow(CORDB_ADDRESS pRemotePtr, const T * pLocalBuffer) +{ + HRESULT hr = m_pMutableTarget->WriteVirtual(pRemotePtr, + (BYTE *)(pLocalBuffer), sizeof(T)); + + if (FAILED(hr)) + { + ThrowHR(hr); + } +} + +//--------------------------------------------------------------------------------------- +// Helper to read a structure from the target process +// +// Arguments: +// T - type of structure to read +// pRemotePtr - remote pointer into the target process (src) +// pLocalBuffer - local buffer to store the structure (dest) +// +// Notes: +// This just does a raw Byte copy into the Target, but does not do any Marshalling. +// This fails if any part of the buffer can't be written. +// + +template<typename T> +void DacDbiInterfaceImpl::SafeReadStructOrThrow(CORDB_ADDRESS pRemotePtr, T * pLocalBuffer) +{ + ULONG32 cbRead = 0; + + HRESULT hr = m_pTarget->ReadVirtual(pRemotePtr, + reinterpret_cast<BYTE *>(pLocalBuffer), sizeof(T), &cbRead); + + if (FAILED(hr)) + { + ThrowHR(CORDBG_E_READVIRTUAL_FAILURE); + } + + if (cbRead != sizeof(T)) + { + ThrowWin32(ERROR_PARTIAL_COPY); + } +} + +#endif // _DACDBI_IMPL_INL_ diff --git a/src/debug/daccess/dacdbiimpllocks.cpp b/src/debug/daccess/dacdbiimpllocks.cpp new file mode 100644 index 0000000000..d3fb589e13 --- /dev/null +++ b/src/debug/daccess/dacdbiimpllocks.cpp @@ -0,0 +1,43 @@ +// 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: DacDbiImplLocks.cpp +// + +// +// Implement DAC/DBI interface for testing our ability to detect when the LS +// holds a lock that we encounter while executing in the DAC. +// +//***************************************************************************** + +#include "stdafx.h" +#include "dacdbiinterface.h" +#include "holder.h" +#include "switches.h" +#include "dacdbiimpl.h" + +// ============================================================================ +// Functions to test data safety. In these functions we determine whether a lock +// is held in a code path we need to execute for inspection. If so, we throw an +// exception. +// ============================================================================ + +#ifdef TEST_DATA_CONSISTENCY +#include "crst.h" + +void DacDbiInterfaceImpl::TestCrst(VMPTR_Crst vmCrst) +{ + DD_ENTER_MAY_THROW; + + DebugTryCrst(vmCrst.GetDacPtr()); +} + +void DacDbiInterfaceImpl::TestRWLock(VMPTR_SimpleRWLock vmRWLock) +{ + DD_ENTER_MAY_THROW; + + DebugTryRWLock(vmRWLock.GetDacPtr()); +} +#endif + diff --git a/src/debug/daccess/dacdbiimplstackwalk.cpp b/src/debug/daccess/dacdbiimplstackwalk.cpp new file mode 100644 index 0000000000..d3a2a63bbc --- /dev/null +++ b/src/debug/daccess/dacdbiimplstackwalk.cpp @@ -0,0 +1,1313 @@ +// 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. +// +// DacDbiImplStackWalk.cpp +// + +// +// This file contains the implementation of the stackwalking-related functions on the DacDbiInterface. +// +// ====================================================================================== + +#include "stdafx.h" +#include "dacdbiinterface.h" +#include "dacdbiimpl.h" +#include "excepcpu.h" + +#if defined(FEATURE_COMINTEROP) +#include "comtoclrcall.h" +#include "comcallablewrapper.h" +#endif // FEATURE_COMINTEROP + +typedef IDacDbiInterface::StackWalkHandle StackWalkHandle; + + +// Persistent data needed to do a stackwalk. This is allocated on the forDbi heap. +// It can survive across multiple DD calls. +// However, it has data structures that have raw pointers into the DAC cache, and so it must +// be re-iniatialized after each time the Dac cache is flushed. +struct StackWalkData +{ +public: + StackWalkData(Thread * pThread, Frame * pFrame, ULONG32 flags) : + m_iterator(pThread, NULL, flags) + { SUPPORTS_DAC; + } + + // Unwrap a handle to get StackWalkData instance. + static StackWalkData * FromHandle(StackWalkHandle handle) + { + SUPPORTS_DAC; + _ASSERTE(handle != NULL); + return reinterpret_cast<StackWalkData *>(handle); + } + + // The stackwalk iterator. This has lots of pointers into the DAC cache. + StackFrameIterator m_iterator; + + // The context buffer, which can be pointed to by the RegDisplay. + T_CONTEXT m_context; + + // A regdisplay used by the stackwalker. + REGDISPLAY m_regdisplay; +}; + +// Helper to allocate stackwalk datastructures for given parameters. +// This is allocated on the local heap (and not via the forDbi allocatoror on the dac-cache), and then +// freed via code:DacDbiInterfaceImpl::DeleteStackWalk +// +// Throws on error (mainly OOM). +void AllocateStackwalk(StackWalkHandle * pHandle, Thread * pThread, Frame * pFrame, ULONG32 flags) +{ + SUPPORTS_DAC; + + StackWalkData * p = new StackWalkData(pThread, NULL, flags); // throews + + StackWalkHandle h = reinterpret_cast<StackWalkHandle>(p); + *pHandle = h; +} +void DeleteStackwalk(StackWalkHandle pHandle) +{ + SUPPORTS_DAC; + + StackWalkData * pBuffer = (StackWalkData *) pHandle; + _ASSERTE(pBuffer != NULL); + delete pBuffer; +} + + +// Helper to get the StackFrameIterator from a Stackwalker handle +StackFrameIterator * GetIteratorFromHandle(StackWalkHandle pSFIHandle) +{ + SUPPORTS_DAC; + + StackWalkData * pBuffer = StackWalkData::FromHandle(pSFIHandle); + return &(pBuffer->m_iterator); +} + +// Helper to get a RegDisplay from a Stackwalker handle +REGDISPLAY * GetRegDisplayFromHandle(StackWalkHandle pSFIHandle) +{ + SUPPORTS_DAC; + StackWalkData * pBuffer = StackWalkData::FromHandle(pSFIHandle); + return &(pBuffer->m_regdisplay); +} + +// Helper to get a Context buffer from a Stackwalker handle +T_CONTEXT * GetContextBufferFromHandle(StackWalkHandle pSFIHandle) +{ + SUPPORTS_DAC; + StackWalkData * pBuffer = StackWalkData::FromHandle(pSFIHandle); + return &(pBuffer->m_context); +} + + +// Create and return a stackwalker on the specified thread. +void DacDbiInterfaceImpl::CreateStackWalk(VMPTR_Thread vmThread, + DT_CONTEXT * pInternalContextBuffer, + StackWalkHandle * ppSFIHandle) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(ppSFIHandle != NULL); + + Thread * pThread = vmThread.GetDacPtr(); + + // Set the stackwalk flags. We pretty much want to stop at everything. + DWORD dwFlags = (NOTIFY_ON_U2M_TRANSITIONS | + NOTIFY_ON_NO_FRAME_TRANSITIONS | + NOTIFY_ON_INITIAL_NATIVE_CONTEXT); + + // allocate memory for various stackwalker buffers (StackFrameIterator, RegDisplay, Context) + AllocateStackwalk(ppSFIHandle, pThread, NULL, dwFlags); + + // initialize the the CONTEXT. + // SetStackWalk will initial the RegDisplay from this context. + GetContext(vmThread, pInternalContextBuffer); + + // initialize the stackwalker + SetStackWalkCurrentContext(vmThread, + *ppSFIHandle, + SET_CONTEXT_FLAG_ACTIVE_FRAME, + pInternalContextBuffer); +} + +// Delete the stackwalk object allocated by code:AllocateStackwalk +void DacDbiInterfaceImpl::DeleteStackWalk(StackWalkHandle ppSFIHandle) +{ + DeleteStackwalk(ppSFIHandle); +} + +// Get the CONTEXT of the current frame at which the stackwalker is stopped. +void DacDbiInterfaceImpl::GetStackWalkCurrentContext(StackWalkHandle pSFIHandle, + DT_CONTEXT * pContext) +{ + DD_ENTER_MAY_THROW; + + StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle); + + GetStackWalkCurrentContext(pIter, pContext); +} + +// Internal Worker for GetStackWalkCurrentContext(). +void DacDbiInterfaceImpl::GetStackWalkCurrentContext(StackFrameIterator * pIter, + DT_CONTEXT * pContext) +{ + // convert the current REGDISPLAY to a CONTEXT + CrawlFrame * pCF = &(pIter->m_crawl); + UpdateContextFromRegDisp(pCF->GetRegisterSet(), reinterpret_cast<T_CONTEXT *>(pContext)); +} + + + +// Set the stackwalker to the specified CONTEXT. +void DacDbiInterfaceImpl::SetStackWalkCurrentContext(VMPTR_Thread vmThread, + StackWalkHandle pSFIHandle, + CorDebugSetContextFlag flag, + DT_CONTEXT * pContext) +{ + DD_ENTER_MAY_THROW; + + StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle); + REGDISPLAY * pRD = GetRegDisplayFromHandle(pSFIHandle); + +#if defined(_DEBUG) + // The caller should have checked this already. + _ASSERTE(CheckContext(vmThread, pContext) == S_OK); +#endif // _DEBUG + + // DD can't keep pointers back into the RS address space. + // Allocate a context in DDImpl's memory space. DDImpl can't contain raw pointers back into + // the client space since that may not marshal. + T_CONTEXT * pContext2 = GetContextBufferFromHandle(pSFIHandle); + *pContext2 = *reinterpret_cast<T_CONTEXT *>(pContext); // memcpy + + // update the REGDISPLAY with the given CONTEXT. + // Be sure that the context is in DDImpl's memory space and not the Right-sides. + FillRegDisplay(pRD, pContext2); + BOOL fSuccess = pIter->ResetRegDisp(pRD, (flag == SET_CONTEXT_FLAG_ACTIVE_FRAME)); + if (!fSuccess) + { + // ResetRegDisp() may fail for the same reason Init() may fail, i.e. + // because the stackwalker tries to unwind one frame ahead of time, + // or because the stackwalker needs to filter out some frames based on the stackwalk flags. + ThrowHR(E_FAIL); + } +} + + +// Unwind the stackwalker to the next frame. +BOOL DacDbiInterfaceImpl::UnwindStackWalkFrame(StackWalkHandle pSFIHandle) +{ + DD_ENTER_MAY_THROW; + + StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle); + + CrawlFrame * pCF = &(pIter->m_crawl); + + if ((pIter->GetFrameState() == StackFrameIterator::SFITER_INITIAL_NATIVE_CONTEXT) || + (pIter->GetFrameState() == StackFrameIterator::SFITER_NATIVE_MARKER_FRAME)) + { + if (IsRuntimeUnwindableStub(GetControlPC(pCF->GetRegisterSet()))) + { + // This is a native stack frame which the StackFrameIterator doesn't know how to unwind. + // Use our special unwind logic. + return UnwindRuntimeStackFrame(pIter); + } + } + + // On x86, we need to adjust the stack pointer for the callee parameter adjustment. + // This requires us to save the number of bytes used for the stack parameters of the callee. + // Thus, let's save it here before we unwind. + DWORD cbStackParameterSize = 0; + if (pIter->GetFrameState() == StackFrameIterator::SFITER_FRAMELESS_METHOD) + { + cbStackParameterSize = GetStackParameterSize(pCF->GetCodeInfo()); + } + + // If the stackwalker is invalid to begin with, we'll just say that it is at the end of the stack. + BOOL fIsAtEndOfStack = TRUE; + while (pIter->IsValid()) + { + StackWalkAction swa = pIter->Next(); + + if (swa == SWA_FAILED) + { + // The stackwalker is valid to begin with, so this must be a failure case. + ThrowHR(E_FAIL); + } + else if (swa == SWA_CONTINUE) + { + if (pIter->GetFrameState() == StackFrameIterator::SFITER_DONE) + { + // We are at the end of the stack. We will break at the end of the loop and fIsAtEndOfStack + // will be TRUE. + } + else if ((pIter->GetFrameState() == StackFrameIterator::SFITER_FRAME_FUNCTION) || + (pIter->GetFrameState() == StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION)) + { + // If the stackwalker is stopped at an explicit frame, unwind directly to the next frame. + // The V3 stackwalker doesn't stop on explicit frames. + continue; + } + else if (pIter->GetFrameState() == StackFrameIterator::SFITER_NO_FRAME_TRANSITION) + { + // No frame transitions are not exposed in V2. + // Just continue onto the next managed stack frame. + continue; + } + else + { + fIsAtEndOfStack = FALSE; + } + } + else + { + UNREACHABLE(); + } + + // If we get here, then we want to stop at this current frame. + break; + } + + if (fIsAtEndOfStack == FALSE) + { + // Currently the only case where we adjust the stack pointer is at M2U transitions. + if (pIter->GetFrameState() == StackFrameIterator::SFITER_NATIVE_MARKER_FRAME) + { + _ASSERTE(!pCF->IsActiveFrame()); + AdjustRegDisplayForStackParameter(pCF->GetRegisterSet(), + cbStackParameterSize, + pCF->IsActiveFrame(), + kFromManagedToUnmanaged); + } + } + + return (fIsAtEndOfStack == FALSE); +} + +bool g_fSkipStackCheck = false; +bool g_fSkipStackCheckInit = false; + +// Check whether the specified CONTEXT is valid. The only check we perform right now is whether the +// SP in the specified CONTEXT is in the stack range of the thread. +HRESULT DacDbiInterfaceImpl::CheckContext(VMPTR_Thread vmThread, + const DT_CONTEXT * pContext) +{ + DD_ENTER_MAY_THROW; + + // If the SP in the CONTEXT isn't valid, then there's no point in checking. + if ((pContext->ContextFlags & CONTEXT_CONTROL) == 0) + { + return S_OK; + } + + if (!g_fSkipStackCheckInit) + { + g_fSkipStackCheck = (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_DbgSkipStackCheck) != 0); + g_fSkipStackCheckInit = true; + } + + // Skip this check if the customer has set the reg key/env var. This is necessary for AutoCad. They + // enable fiber mode by calling the Win32 API ConvertThreadToFiber(), but when a managed debugger is + // attached, they don't actually call into our hosting APIs such as SwitchInLogicalThreadState(). This + // leads to the cached stack range on the Thread object being stale. + if (!g_fSkipStackCheck) + { + // We don't have the backing store boundaries stored on the thread, but this is just + // a sanity check anyway. + Thread * pThread = vmThread.GetDacPtr(); + PTR_VOID sp = GetSP(reinterpret_cast<const T_CONTEXT *>(pContext)); + + if ((sp < pThread->GetCachedStackLimit()) || (pThread->GetCachedStackBase() <= sp)) + { + return CORDBG_E_NON_MATCHING_CONTEXT; + } + } + + return S_OK; +} + +// Retrieve information about the current frame from the stackwalker. +IDacDbiInterface::FrameType DacDbiInterfaceImpl::GetStackWalkCurrentFrameInfo(StackWalkHandle pSFIHandle, + DebuggerIPCE_STRData * pFrameData) +{ + DD_ENTER_MAY_THROW; + + _ASSERTE(pSFIHandle != NULL); + + StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle); + + FrameType ftResult = kInvalid; + if (pIter->GetFrameState() == StackFrameIterator::SFITER_DONE) + { + _ASSERTE(!pIter->IsValid()); + ftResult = kAtEndOfStack; + } + else + { + BOOL fInitFrameData = FALSE; + switch (pIter->GetFrameState()) + { + case StackFrameIterator::SFITER_UNINITIALIZED: + ftResult = kInvalid; + break; + + case StackFrameIterator::SFITER_FRAMELESS_METHOD: + ftResult = kManagedStackFrame; + fInitFrameData = TRUE; + break; + + case StackFrameIterator::SFITER_FRAME_FUNCTION: + // + // fall through + // + case StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION: + ftResult = kExplicitFrame; + fInitFrameData = TRUE; + break; + + case StackFrameIterator::SFITER_NO_FRAME_TRANSITION: + // no-frame transition represents an ExInfo for a native exception on x86. + // For all intents and purposes this should be treated just like another explicit frame. + ftResult = kExplicitFrame; + fInitFrameData = TRUE; + break; + + case StackFrameIterator::SFITER_NATIVE_MARKER_FRAME: + // + // fall through + // + case StackFrameIterator::SFITER_INITIAL_NATIVE_CONTEXT: + if (IsRuntimeUnwindableStub(GetControlPC(pIter->m_crawl.GetRegisterSet()))) + { + ftResult = kNativeRuntimeUnwindableStackFrame; + fInitFrameData = TRUE; + } + else + { + ftResult = kNativeStackFrame; + } + break; + + default: + UNREACHABLE(); + } + + if ((fInitFrameData == TRUE) && (pFrameData != NULL)) + { + InitFrameData(pIter, ftResult, pFrameData); + } + } + + return ftResult; +} + +//--------------------------------------------------------------------------------------- +// +// Return the number of internal frames on the specified thread. +// +// Arguments: +// vmThread - the thread to be walked +// +// Return Value: +// Return the number of interesting internal frames on the thread. +// +// Notes: +// Internal frames are interesting if they are not of type STUBFRAME_NONE. +// + +ULONG32 DacDbiInterfaceImpl::GetCountOfInternalFrames(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + Frame * pFrame = pThread->GetFrame(); + + // We could call EnumerateInternalFrames() here, but it would be a lot of overhead for what we need. + ULONG32 uCount = 0; + while (pFrame != FRAME_TOP) + { + CorDebugInternalFrameType ift = GetInternalFrameType(pFrame); + if (ift != STUBFRAME_NONE) + { + uCount++; + } + pFrame = pFrame->Next(); + } + return uCount; +} + +//--------------------------------------------------------------------------------------- +// +// Enumerate the internal frames on the specified thread and invoke the provided callback on each of them. +// +// Arguments: +// vmThread - the thread to be walked +// fpCallback - callback function to be invoked for each interesting internal frame +// pUserData - user-defined custom data to be passed to the callback +// + +void DacDbiInterfaceImpl::EnumerateInternalFrames(VMPTR_Thread vmThread, + FP_INTERNAL_FRAME_ENUMERATION_CALLBACK fpCallback, + void * pUserData) +{ + DD_ENTER_MAY_THROW; + + DebuggerIPCE_STRData frameData; + + Thread * pThread = vmThread.GetDacPtr(); + Frame * pFrame = pThread->GetFrame(); + AppDomain * pAppDomain = pThread->GetDomain(INDEBUG(TRUE)); + + // This used to be only true for Enter-Managed chains. + // Since we don't have chains anymore, this can always be false. + frameData.quicklyUnwound = false; + frameData.eType = DebuggerIPCE_STRData::cStubFrame; + + while (pFrame != FRAME_TOP) + { + // check if the internal frame is interesting + frameData.stubFrame.frameType = GetInternalFrameType(pFrame); + if (frameData.stubFrame.frameType != STUBFRAME_NONE) + { + frameData.fp = FramePointer::MakeFramePointer(PTR_HOST_TO_TADDR(pFrame)); + + frameData.vmCurrentAppDomainToken.SetHostPtr(pAppDomain); + + MethodDesc * pMD = pFrame->GetFunction(); +#if defined(FEATURE_COMINTEROP) + if (frameData.stubFrame.frameType == STUBFRAME_U2M) + { + _ASSERTE(pMD == NULL); + + // U2M transition frame generally don't store the target MD because we know what the target + // is by looking at the callee stack frame. However, for reverse COM interop, we can try + // to get the MD for the interface. + // + // Note that some reverse COM interop cases don't have an intermediate interface MD, so + // pMD may still be NULL. + // + // Even if there is an MD on the ComMethodFrame, it could be in a different appdomain than + // the ComMethodFrame itself. The only known scenario is a cross-appdomain reverse COM + // interop call. We need to check for this case. The end result is that GetFunction() and + // GetFunctionToken() on ICDInternalFrame will return NULL. + + // Minidumps without full memory don't guarantee to capture the CCW since we can do without + // it. In this case, pMD will remain NULL. + EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY + { + if (pFrame->GetVTablePtr() == ComMethodFrame::GetMethodFrameVPtr()) + { + ComMethodFrame * pCOMFrame = dac_cast<PTR_ComMethodFrame>(pFrame); + PTR_VOID pUnkStackSlot = pCOMFrame->GetPointerToArguments(); + PTR_IUnknown pUnk = dac_cast<PTR_IUnknown>(*dac_cast<PTR_TADDR>(pUnkStackSlot)); + ComCallWrapper * pCCW = ComCallWrapper::GetWrapperFromIP(pUnk); + + if (!pCCW->NeedToSwitchDomains(pAppDomain->GetId())) + { + ComCallMethodDesc * pCMD = NULL; + pCMD = dac_cast<PTR_ComCallMethodDesc>(pCOMFrame->ComMethodFrame::GetDatum()); + pMD = pCMD->GetInterfaceMethodDesc(); + } + } + } + EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY + } +#endif // FEATURE_COMINTEROP + + Module * pModule = (pMD ? pMD->GetModule() : NULL); + DomainFile * pDomainFile = (pModule ? pModule->GetDomainFile(pAppDomain) : NULL); + + if (frameData.stubFrame.frameType == STUBFRAME_FUNC_EVAL) + { + FuncEvalFrame * pFEF = dac_cast<PTR_FuncEvalFrame>(pFrame); + DebuggerEval * pDE = pFEF->GetDebuggerEval(); + + frameData.stubFrame.funcMetadataToken = pDE->m_methodToken; + frameData.stubFrame.vmDomainFile.SetHostPtr( + pDE->m_debuggerModule ? pDE->m_debuggerModule->GetDomainFile() : NULL); + frameData.stubFrame.vmMethodDesc = VMPTR_MethodDesc::NullPtr(); + } + else + { + frameData.stubFrame.funcMetadataToken = (pMD == NULL ? NULL : pMD->GetMemberDef()); + frameData.stubFrame.vmDomainFile.SetHostPtr(pDomainFile); + frameData.stubFrame.vmMethodDesc.SetHostPtr(pMD); + } + + // invoke the callback + fpCallback(&frameData, pUserData); + } + + // update the current appdomain if necessary + AppDomain * pRetDomain = pFrame->GetReturnDomain(); + if (pRetDomain != NULL) + { + pAppDomain = pRetDomain; + } + + // move on to the next internal frame + pFrame = pFrame->Next(); + } +} + +// Given the FramePointer of the parent frame and the FramePointer of the current frame, +// check if the current frame is the parent frame. +BOOL DacDbiInterfaceImpl::IsMatchingParentFrame(FramePointer fpToCheck, FramePointer fpParent) +{ + DD_ENTER_MAY_THROW; + +#ifdef WIN64EXCEPTIONS + StackFrame sfToCheck = StackFrame((UINT_PTR)fpToCheck.GetSPValue()); + + StackFrame sfParent = StackFrame((UINT_PTR)fpParent.GetSPValue()); + + // Ask the ExceptionTracker to figure out the answer. + // Don't try to compare the StackFrames/FramePointers ourselves. + return ExceptionTracker::IsUnwoundToTargetParentFrame(sfToCheck, sfParent); + +#else // !WIN64EXCEPTIONS + return FALSE; + +#endif // WIN64EXCEPTIONS +} + +// Return the stack parameter size of the given method. +ULONG32 DacDbiInterfaceImpl::GetStackParameterSize(CORDB_ADDRESS controlPC) +{ + DD_ENTER_MAY_THROW; + + PCODE currentPC = PCODE(controlPC); + + EECodeInfo codeInfo(currentPC); + return GetStackParameterSize(&codeInfo); +} + +// Return the FramePointer of the current frame at which the stackwalker is stopped. +FramePointer DacDbiInterfaceImpl::GetFramePointer(StackWalkHandle pSFIHandle) +{ + DD_ENTER_MAY_THROW; + + StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle); + return GetFramePointerWorker(pIter); +} + +// Internal helper for GetFramePointer. +FramePointer DacDbiInterfaceImpl::GetFramePointerWorker(StackFrameIterator * pIter) +{ + CrawlFrame * pCF = &(pIter->m_crawl); + REGDISPLAY * pRD = pCF->GetRegisterSet(); + + FramePointer fp; + switch (pIter->GetFrameState()) + { + // For managed methods, we have the full CONTEXT. Additionally, we also have the caller CONTEXT + // on WIN64. + case StackFrameIterator::SFITER_FRAMELESS_METHOD: + fp = FramePointer::MakeFramePointer(GetRegdisplayStackMark(pRD)); + break; + + // In these cases, we only have the full CONTEXT, not the caller CONTEXT. + case StackFrameIterator::SFITER_NATIVE_MARKER_FRAME: + // + // fall through + // + case StackFrameIterator::SFITER_INITIAL_NATIVE_CONTEXT: + fp = FramePointer::MakeFramePointer(GetRegdisplayStackMark(pRD)); + break; + + // In these cases, we use the address of the explicit frame as the frame marker. + case StackFrameIterator::SFITER_FRAME_FUNCTION: + // + // fall through + // + case StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION: + fp = FramePointer::MakeFramePointer(PTR_HOST_TO_TADDR(pCF->GetFrame())); + break; + + // No-frame transition represents an ExInfo for a native exception on x86. + // For all intents and purposes this should be treated just like another explicit frame. + case StackFrameIterator::SFITER_NO_FRAME_TRANSITION: + fp = FramePointer::MakeFramePointer(pCF->GetNoFrameTransitionMarker()); + break; + + case StackFrameIterator::SFITER_UNINITIALIZED: + // + // fall through + // + default: + UNREACHABLE(); + } + + return fp; +} + +// Return TRUE if the specified CONTEXT is the CONTEXT of the leaf frame. +// @dbgtodo filter CONTEXT - Currently we check for the filter CONTEXT first. +BOOL DacDbiInterfaceImpl::IsLeafFrame(VMPTR_Thread vmThread, + const DT_CONTEXT * pContext) +{ + DD_ENTER_MAY_THROW; + + DT_CONTEXT ctxLeaf; + GetContext(vmThread, &ctxLeaf); + + // Call a platform-specific helper to compare the two contexts. + return CompareControlRegisters(pContext, &ctxLeaf); +} + +// This is a simple helper function to convert a CONTEXT to a DebuggerREGDISPLAY. We need to do this +// inside DDI because the RS has no notion of REGDISPLAY. +void DacDbiInterfaceImpl::ConvertContextToDebuggerRegDisplay(const DT_CONTEXT * pInContext, + DebuggerREGDISPLAY * pOutDRD, + BOOL fActive) +{ + DD_ENTER_MAY_THROW; + + // This is a bit cumbersome. First we need to convert the CONTEXT into a REGDISPLAY. Then we need + // to convert the REGDISPLAY to a DebuggerREGDISPLAY. + REGDISPLAY rd; + FillRegDisplay(&rd, reinterpret_cast<T_CONTEXT *>(const_cast<DT_CONTEXT *>(pInContext))); + SetDebuggerREGDISPLAYFromREGDISPLAY(pOutDRD, &rd); +} + +//--------------------------------------------------------------------------------------- +// +// Fill in the structure with information about the current frame at which the stackwalker is stopped. +// +// Arguments: +// pIter - the stackwalker +// pFrameData - the structure to be filled out +// + +void DacDbiInterfaceImpl::InitFrameData(StackFrameIterator * pIter, + FrameType ft, + DebuggerIPCE_STRData * pFrameData) +{ + CrawlFrame * pCF = &(pIter->m_crawl); + + // + // do common initialization of DebuggerIPCE_STRData for both managed stack frames and explicit frames + // + + pFrameData->fp = GetFramePointerWorker(pIter); + + // This used to be only true for Enter-Managed chains. + // Since we don't have chains anymore, this can always be false. + pFrameData->quicklyUnwound = false; + + AppDomain * pAppDomain = pCF->GetAppDomain(); + pFrameData->vmCurrentAppDomainToken.SetHostPtr(pAppDomain); + + if (ft == kNativeRuntimeUnwindableStackFrame) + { + pFrameData->eType = DebuggerIPCE_STRData::cRuntimeNativeFrame; + + GetStackWalkCurrentContext(pIter, &(pFrameData->ctx)); + } + else if (ft == kManagedStackFrame) + { + MethodDesc * pMD = pCF->GetFunction(); + Module * pModule = (pMD ? pMD->GetModule() : NULL); + // Although MiniDumpNormal tries to dump all AppDomains, it's possible + // target corruption will keep one from being present. This should mean + // we'll just fail later, but struggle on for now. + DomainFile *pDomainFile = NULL; + EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY + { + pDomainFile = (pModule ? pModule->GetDomainFile(pAppDomain) : NULL); + _ASSERTE(pDomainFile != NULL); + } + EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY + + // + // This is a managed stack frame. + // + + _ASSERTE(pMD != NULL); + _ASSERTE(pModule != NULL); + + // + // initialize the rest of the DebuggerIPCE_STRData + // + + pFrameData->eType = DebuggerIPCE_STRData::cMethodFrame; + + SetDebuggerREGDISPLAYFromREGDISPLAY(&(pFrameData->rd), pCF->GetRegisterSet()); + + GetStackWalkCurrentContext(pIter, &(pFrameData->ctx)); + + // + // initialize the fields in DebuggerIPCE_STRData::v + // + + // These fields will be filled in later. We don't have the sequence point mapping information here. + pFrameData->v.ILOffset = (SIZE_T)(-1); + pFrameData->v.mapping = MAPPING_NO_INFO; + + // Check if this is a vararg method by getting the managed calling convention from the signature. + // Strictly speaking, we can do this in CordbJITILFrame::Init(), but it's just easier and more + // efficiently to do it here. CordbJITILFrame::Init() will initialize the other vararg-related + // fields. We don't have the native var info here to fully initialize everything. + pFrameData->v.fVarArgs = (pMD->IsVarArg() == TRUE); + + pFrameData->v.fNoMetadata = (pMD->IsNoMetadata() == TRUE); + + pFrameData->v.taAmbientESP = pCF->GetAmbientSPFromCrawlFrame(); + if (pMD->IsSharedByGenericInstantiations()) + { + // This method has a generic type token which is required to figure out the exact instantiation + // of the method. CrawlFrame::GetExactGenericArgsToken() can't always successfully retrieve + // the token because the JIT doesn't generate the required information all the time. As such, + // we need to save the variable index of the generic type token in order to do the look up later. + ALLOW_DATATARGET_MISSING_MEMORY( + pFrameData->v.exactGenericArgsToken = (GENERICS_TYPE_TOKEN)(dac_cast<TADDR>(pCF->GetExactGenericArgsToken())); + ); + + if (pMD->AcquiresInstMethodTableFromThis()) + { + // The generic type token is the "this" object. + pFrameData->v.dwExactGenericArgsTokenIndex = 0; + } + else + { + // The generic type token is one of the secret arguments. + pFrameData->v.dwExactGenericArgsTokenIndex = (DWORD)ICorDebugInfo::TYPECTXT_ILNUM; + } + } + else + { + pFrameData->v.exactGenericArgsToken = NULL; + pFrameData->v.dwExactGenericArgsTokenIndex = (DWORD)ICorDebugInfo::MAX_ILNUM; + } + + // + // initialize the DebuggerIPCE_FuncData and DebuggerIPCE_JITFuncData + // + + DebuggerIPCE_FuncData * pFuncData = &(pFrameData->v.funcData); + DebuggerIPCE_JITFuncData * pJITFuncData = &(pFrameData->v.jitFuncData); + + // + // initialize the "easy" fields of DebuggerIPCE_FuncData + // + + pFuncData->funcMetadataToken = pMD->GetMemberDef(); + pFuncData->vmDomainFile.SetHostPtr(pDomainFile); + + // PERF: this is expensive to get so I stopped fetching it eagerly + // It is only needed if we haven't already got a cached copy + pFuncData->classMetadataToken = mdTokenNil; + + // + // initialize the remaining fields of DebuggerIPCE_FuncData to the default values + // + + pFuncData->ilStartAddress = NULL; + pFuncData->ilSize = 0; + pFuncData->currentEnCVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION; + pFuncData->localVarSigToken = mdSignatureNil; + + // + // inititalize the fields of DebuggerIPCE_JITFuncData + // + + // For MiniDumpNormal, we do not guarantee method region info for all JIT tokens + // is present in the dump. + ALLOW_DATATARGET_MISSING_MEMORY( + pJITFuncData->nativeStartAddressPtr = PCODEToPINSTR(pCF->GetCodeInfo()->GetStartAddress()); + ); + + // PERF: this is expensive to get so I stopped fetching it eagerly + // It is only needed if we haven't already got a cached copy + pJITFuncData->nativeHotSize = 0; + pJITFuncData->nativeStartAddressColdPtr = 0; + pJITFuncData->nativeColdSize = 0; + + pJITFuncData->nativeOffset = pCF->GetRelOffset(); + + // Here we detect (and set the appropriate flag) if the nativeOffset in the current frame points to the return address of IL_Throw() + // (or other exception related JIT helpers like IL_Throw, IL_Rethrow, JIT_RngChkFail, IL_VerificationError, JIT_Overflow etc). + // Since return addres point to the next(!) instruction after [call IL_Throw] this sometimes can lead to incorrect exception stacktraces + // where a next source line is spotted as an exception origin. This happends when the next instruction after [call IL_Throw] belongs to + // a sequence point and a source line different from a sequence point and a source line of [call IL_Throw]. + // Later on this flag is used in order to adjust nativeOffset and make ICorDebugILFrame::GetIP return IL offset withing + // the same sequence point as an actuall IL throw instruction. + + // Here is how we detect it: + // We can assume that nativeOffset points to an the instruction after [call IL_Throw] when these conditioins are met: + // 1. pCF->IsInterrupted() - Exception has been thrown by this managed frame (frame attr FRAME_ATTR_EXCEPTION) + // 2. !pCF->HasFaulted() - It wasn't a "hardware" exception (Access violation, dev by 0, etc.) + // 3. !pCF->IsIPadjusted() - It hasn't been previously adjusted to point to [call IL_Throw] + // 4. pJITFuncData->nativeOffset != 0 - nativeOffset contains something that looks like a real return address. + pJITFuncData->jsutAfterILThrow = pCF->IsInterrupted() + && !pCF->HasFaulted() + && !pCF->IsIPadjusted() + && pJITFuncData->nativeOffset != 0; + + pJITFuncData->nativeCodeJITInfoToken.Set(NULL); + pJITFuncData->vmNativeCodeMethodDescToken.SetHostPtr(pMD); + + InitParentFrameInfo(pCF, pJITFuncData); + + ALLOW_DATATARGET_MISSING_MEMORY( + pJITFuncData->isInstantiatedGeneric = pMD->HasClassOrMethodInstantiation(); + ); + pJITFuncData->enCVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION; + + // PERF: this is expensive to get so I stopped fetching it eagerly + // It is only needed if we haven't already got a cached copy + pFuncData->localVarSigToken = 0; + pFuncData->ilStartAddress = 0; + pFuncData->ilSize = 0; + + + // See the comment for LookupEnCVersions(). + // PERF: this is expensive to get so I stopped fetching it eagerly + pFuncData->currentEnCVersion = 0; + pJITFuncData->enCVersion = 0; + } + else + { + _ASSERTE(!"DDII::InitFrameData() - We should never stop at internal frames."); + ThrowHR(CORDBG_E_TARGET_INCONSISTENT); + } +} + +//--------------------------------------------------------------------------------------- +// +// Initialize the address and the size of the jitted code, including both hot and cold regions. +// +// Arguments: +// methodToken - METHODTOKEN of the method in question; this should actually be the CodeHeader address +// pJITFuncData - structure to be filled out +// + +void DacDbiInterfaceImpl::InitNativeCodeAddrAndSize(TADDR taStartAddr, + DebuggerIPCE_JITFuncData * pJITFuncData) +{ + PTR_CORDB_ADDRESS_TYPE pAddr = dac_cast<PTR_CORDB_ADDRESS_TYPE>(taStartAddr); + CodeRegionInfo crInfo = CodeRegionInfo::GetCodeRegionInfo(NULL, NULL, pAddr); + + pJITFuncData->nativeStartAddressPtr = PCODEToPINSTR(crInfo.getAddrOfHotCode()); + pJITFuncData->nativeHotSize = crInfo.getSizeOfHotCode(); + + pJITFuncData->nativeStartAddressColdPtr = PCODEToPINSTR(crInfo.getAddrOfColdCode()); + pJITFuncData->nativeColdSize = crInfo.getSizeOfColdCode(); +} + +//--------------------------------------------------------------------------------------- +// +// Initialize the funclet-related fields of DebuggerIPCE_JITFuncData. This is an nop on non-WIN64 platforms. +// +// Arguments: +// pCF - the CrawlFrame for the current frame +// pJITFuncData - the structure to be filled out +// + +void DacDbiInterfaceImpl::InitParentFrameInfo(CrawlFrame * pCF, + DebuggerIPCE_JITFuncData * pJITFuncData) +{ +#ifdef WIN64EXCEPTIONS + pJITFuncData->fIsFilterFrame = pCF->IsFilterFunclet(); + + if (pCF->IsFunclet()) + { + DWORD dwParentOffset; + StackFrame sfParent = ExceptionTracker::FindParentStackFrameEx(pCF, &dwParentOffset, NULL); + + // + // For funclets, fpParentOrSelf is the FramePointer of the parent. + // Don't mess around with this FramePointer. The only thing we can do with it is to pass it back + // to the ExceptionTracker when we are checking if a particular frame is the parent frame. + // + + pJITFuncData->fpParentOrSelf = FramePointer::MakeFramePointer(sfParent.SP); + pJITFuncData->parentNativeOffset = dwParentOffset; + } + else + { + StackFrame sfSelf = ExceptionTracker::GetStackFrameForParentCheck(pCF); + + // + // For non-funclets, fpParentOrSelf is the FramePointer of the current frame itself. + // Don't mess around with this FramePointer. The only thing we can do with it is to pass it back + // to the ExceptionTracker when we are checking if a particular frame is the parent frame. + // + + pJITFuncData->fpParentOrSelf = FramePointer::MakeFramePointer(sfSelf.SP); + pJITFuncData->parentNativeOffset = 0; + } +#endif // WIN64EXCEPTIONS +} + +// Return the stack parameter size of the given method. +// Refer to the full comment for the overloaded version. +ULONG32 DacDbiInterfaceImpl::GetStackParameterSize(EECodeInfo * pCodeInfo) +{ + return pCodeInfo->GetCodeManager()->GetStackParameterSize(pCodeInfo); +} + + +//--------------------------------------------------------------------------------------- +// +// Adjust the stack pointer in the CONTEXT for the stack parameters. +// This is a nop on non-x86 platforms. +// +// Arguments: +// pRD - the REGDISPLAY to be adjusted +// cbStackParameterSize - the number of bytes for the stack parameters +// fIsActiveFrame - whether the CONTEXT is for an active frame +// StackAdjustmentDirection - whether we are changing a CONTEXT from the managed convention +// to the unmanaged convention +// +// Notes: +// Consider this code: +// +// push 1 +// push 2 +// call Foo +// -> inc eax +// +// Here we are assuming that the return instruction in Foo() pops the stack arguments. +// +// Suppose the IP in the CONTEXT is at the arrow. The question is, where should the stack pointer be? +// +// 0x0 ret addr for Foo +// 0x4 2 +// 0x8 1 +// 0xc ..... +// +// If the CONTEXT is the active frame, i.e. the IP is the active instruction, +// not the instruction at the return address, then the SP should be at 0xc. +// However, if the CONTEXT is not active, then the SP can be at either 0x4 or 0xc, depending on +// the convention used by the stackwalker. The managed stackwalker reports 0xc, but dbghelp reports +// 0x4. To bridge the gap we have to shim it in the DDI. +// +// Currently, we have no way to reliably shim the CONTEXT in all cases. Consider this stack, +// where U* are native stack frames and M* are managed stack frames: +// +// [leaf] +// U2 +// U1 +// ------- (M2U transition) +// M2 +// M1 +// M0 +// ------- (U2M transition) +// U0 +// [root] +// +// There are only two transition cases where we can reliably adjust for the callee stack parameter size: +// 1) when the debugger calls SetContext() with the CONTEXT of the first managed stack frame in a +// managed stack chain (i.e. SetContext() with M2's CONTEXT) +// - the M2U transition is protected by an explicit frame (aka Frame-chain frame) +// 2) when the debugger calls GetContext() on the first native stack frame in a native stack chain +// (i.e. GetContext() at U0) +// - we unwind from M0 to U0, so we know the stack parameter size of M0 +// +// If we want to do the adjustment in all cases, we need to ask the JIT to store the callee stack +// parameter size in either the unwind info. +// + +void DacDbiInterfaceImpl::AdjustRegDisplayForStackParameter(REGDISPLAY * pRD, + DWORD cbStackParameterSize, + BOOL fIsActiveFrame, + StackAdjustmentDirection direction) +{ +#if defined(_TARGET_X86_) + // If the CONTEXT is active then no adjustment is needed. + if (!fIsActiveFrame) + { + UINT_PTR sp = GetRegdisplaySP(pRD); + if (direction == kFromManagedToUnmanaged) + { + // The CONTEXT comes from the managed world. + sp -= cbStackParameterSize; + } + else + { + _ASSERTE(!"Currently, we should not hit this case.\n"); + + // The CONTEXT comes from the unmanaged world. + sp += cbStackParameterSize; + } + SetRegdisplaySP(pRD, reinterpret_cast<LPVOID>(sp)); + } +#endif // _TARGET_X86_ +} + +//--------------------------------------------------------------------------------------- +// +// Given an explicit frame, return its frame type in terms of CorDebugInternalFrameType. +// +// Arguments: +// pFrame - the explicit frame in question +// +// Return Value: +// Return the CorDebugInternalFrameType of the explicit frame +// +// Notes: +// I wish this function were simpler, but it's not. The logic in this function is adopted +// from the logic in the old in-proc debugger stackwalker. +// + +CorDebugInternalFrameType DacDbiInterfaceImpl::GetInternalFrameType(Frame * pFrame) +{ + CorDebugInternalFrameType resultType = STUBFRAME_NONE; + + Frame::ETransitionType tt = pFrame->GetTransitionType(); + Frame::Interception it = pFrame->GetInterception(); + int ft = pFrame->GetFrameType(); + + switch (tt) + { + case Frame::TT_NONE: + if (it == Frame::INTERCEPTION_CLASS_INIT) + { + resultType = STUBFRAME_CLASS_INIT; + } + else if (it == Frame::INTERCEPTION_EXCEPTION) + { + resultType = STUBFRAME_EXCEPTION; + } + else if (it == Frame::INTERCEPTION_SECURITY) + { + resultType = STUBFRAME_SECURITY; + } + else if (it == Frame::INTERCEPTION_PRESTUB) + { + resultType = STUBFRAME_JIT_COMPILATION; + } + else + { + if (ft == Frame::TYPE_FUNC_EVAL) + { + resultType = STUBFRAME_FUNC_EVAL; + } + else if (ft == Frame::TYPE_EXIT) + { + if ((pFrame->GetVTablePtr() != InlinedCallFrame::GetMethodFrameVPtr()) || + InlinedCallFrame::FrameHasActiveCall(pFrame)) + { + resultType = STUBFRAME_M2U; + } + } + } + break; + + case Frame::TT_M2U: + // Refer to the comment in DebuggerWalkStackProc() for StubDispatchFrame. + if (pFrame->GetVTablePtr() != StubDispatchFrame::GetMethodFrameVPtr()) + { + if (it == Frame::INTERCEPTION_SECURITY) + { + resultType = STUBFRAME_SECURITY; + } + else + { + resultType = STUBFRAME_M2U; + } + } + break; + + case Frame::TT_U2M: + resultType = STUBFRAME_U2M; + break; + + case Frame::TT_AppDomain: + resultType = STUBFRAME_APPDOMAIN_TRANSITION; + break; + + case Frame::TT_InternalCall: + if (it == Frame::INTERCEPTION_EXCEPTION) + { + resultType = STUBFRAME_EXCEPTION; + } + else + { + resultType = STUBFRAME_INTERNALCALL; + } + break; + + default: + UNREACHABLE(); + break; + } + + return resultType; +} + +//--------------------------------------------------------------------------------------- +// +// This is just a simpler helper function to convert a REGDISPLAY to a CONTEXT. +// +// Arguments: +// pRegDisp - the REGDISPLAY to be converted +// pContext - the buffer for storing the converted CONTEXT +// + +void DacDbiInterfaceImpl::UpdateContextFromRegDisp(REGDISPLAY * pRegDisp, + T_CONTEXT * pContext) +{ +#if defined(_TARGET_X86_) + // Do a partial copy first. + pContext->ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL); + + pContext->Edi = *pRegDisp->pEdi; + pContext->Esi = *pRegDisp->pEsi; + pContext->Ebx = *pRegDisp->pEbx; + pContext->Ebp = *pRegDisp->pEbp; + pContext->Eax = *pRegDisp->pEax; + pContext->Ecx = *pRegDisp->pEcx; + pContext->Edx = *pRegDisp->pEdx; + pContext->Esp = pRegDisp->Esp; + pContext->Eip = pRegDisp->ControlPC; + + // If we still have the pointer to the leaf CONTEXT, and the leaf CONTEXT is the same as the CONTEXT for + // the current frame (i.e. the stackwalker is at the leaf frame), then we do a full copy. + if ((pRegDisp->pContext != NULL) && + (CompareControlRegisters(const_cast<const DT_CONTEXT *>(reinterpret_cast<DT_CONTEXT *>(pContext)), + const_cast<const DT_CONTEXT *>(reinterpret_cast<DT_CONTEXT *>(pRegDisp->pContext))))) + { + *pContext = *pRegDisp->pContext; + } +#else + *pContext = *pRegDisp->pCurrentContext; +#endif +} + +//--------------------------------------------------------------------------------------- +// +// Given the REGDISPLAY of a stack frame for one of the redirect functions, retrieve the original CONTEXT +// before the thread redirection. +// +// Arguments: +// pRD - the REGDISPLAY of the stack frame in question +// +// Return Value: +// Return the original CONTEXT before the thread got redirected. +// +// Assumptions: +// The caller has checked that the REGDISPLAY is indeed for one of the redirect functions. +// + +PTR_CONTEXT DacDbiInterfaceImpl::RetrieveHijackedContext(REGDISPLAY * pRD) +{ + CORDB_ADDRESS ContextPointerAddr = NULL; + + TADDR controlPC = PCODEToPINSTR(GetControlPC(pRD)); + + // Check which thread redirection mechanism is used. + if (g_pDebugger->m_rgHijackFunction[Debugger::kUnhandledException].IsInRange(controlPC)) + { + // The thread is redirected because of an unhandled exception. + + // The CONTEXT pointer is the last thing pushed onto the stack. + // So just read the stack slot at ESP. That will be the TADDR to the CONTEXT. + ContextPointerAddr = PTR_TO_CORDB_ADDRESS(GetRegdisplaySP(pRD)); + + // Read the CONTEXT from OOP. + return *dac_cast<PTR_PTR_CONTEXT>((TADDR)ContextPointerAddr); + } + else + { + // The thread is redirected by the EE via code:Thread::RedirectThreadAtHandledJITCase. + + // Convert the REGDISPLAY to a CONTEXT; + T_CONTEXT * pContext = NULL; + +#if defined(_TARGET_X86_) + T_CONTEXT ctx; + pContext = &ctx; + UpdateContextFromRegDisp(pRD, pContext); +#else + pContext = pRD->pCurrentContext; +#endif + + // Retrieve the original CONTEXT. + return GetCONTEXTFromRedirectedStubStackFrame(pContext); + } +} + +//--------------------------------------------------------------------------------------- +// +// Unwind special native stack frame which the runtime knows how to unwind. +// +// Arguments: +// pIter - the StackFrameIterator we are currently using to walk the stack +// +// Return Value: +// Return TRUE if there are more frames to walk, i.e. if we are NOT at the end of the stack. +// +// Assumptions: +// pIter is currently stopped at a special stub which the runtime knows how to unwind. +// +// Notes: +// * Refer to code:DacDbiInterfaceImpl::IsRuntimeUnwindableStub to see how we determine whether a control +// PC is in a runtime-unwindable stub +// + +BOOL DacDbiInterfaceImpl::UnwindRuntimeStackFrame(StackFrameIterator * pIter) +{ + _ASSERTE(IsRuntimeUnwindableStub(GetControlPC(pIter->m_crawl.GetRegisterSet()))); + + T_CONTEXT * pContext = NULL; + REGDISPLAY * pRD = pIter->m_crawl.GetRegisterSet(); + + // + // Retrieve the CONTEXT to unwind to and unwind the REGDISPLAY. + // + pContext = RetrieveHijackedContext(pRD); + + FillRegDisplay(pRD, pContext); + + // Update the StackFrameIterator. + BOOL fSuccess = pIter->ResetRegDisp(pRD, true); + if (!fSuccess) + { + // ResetRegDisp() may fail for the same reason Init() may fail, i.e. + // because the stackwalker tries to unwind one frame ahead of time, + // or because the stackwalker needs to filter out some frames based on the stackwalk flags. + ThrowHR(E_FAIL); + } + + // Currently we only unwind the hijack function, which will never be the last stack frame. + // So return TRUE to indicate that this is not the end of stack. + return TRUE; +} + +//--------------------------------------------------------------------------------------- +// +// To aid in doing the stack walk, the shim needs to know if either TS_SyncSuspended or +// TS_Hijacked is set on a given thread. This DAC helper provides that access. +// +// Arguments: +// vmThread - Thread on which to check the TS_SyncSuspended & TS_Hijacked states +// +// Return Value: +// Return true iff TS_SyncSuspended or TS_Hijacked is set on the specified thread. +// + +bool DacDbiInterfaceImpl::IsThreadSuspendedOrHijacked(VMPTR_Thread vmThread) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + Thread::ThreadState ts = pThread->GetSnapshotState(); + if ((ts & Thread::TS_SyncSuspended) != 0) + { + return true; + } + +#ifdef FEATURE_HIJACK + if ((ts & Thread::TS_Hijacked) != 0) + { + return true; + } +#endif + + return false; +} diff --git a/src/debug/daccess/dacfn.cpp b/src/debug/daccess/dacfn.cpp new file mode 100644 index 0000000000..88d45993b3 --- /dev/null +++ b/src/debug/daccess/dacfn.cpp @@ -0,0 +1,1505 @@ +// 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: dacfn.cpp +// + +// +// Dac function implementations. +// +//***************************************************************************** + +#include "stdafx.h" + +#include <encee.h> +#ifdef FEATURE_PREJIT +#include "compile.h" +#endif // FEATURE_PREJIT +#ifdef FEATURE_REMOTING +#include <remoting.h> +#include "objectclone.h" +#endif +#include <virtualcallstub.h> +#include "peimagelayout.inl" + +DacTableInfo g_dacTableInfo; +DacGlobals g_dacGlobals; + +struct DacHostVtPtrs +{ +#define VPTR_CLASS(name) PVOID name; +#define VPTR_MULTI_CLASS(name, keyBase) PVOID name##__##keyBase; +#include <vptr_list.h> +#undef VPTR_CLASS +#undef VPTR_MULTI_CLASS +}; + + +const WCHAR *g_dacVtStrings[] = +{ +#define VPTR_CLASS(name) W(#name), +#define VPTR_MULTI_CLASS(name, keyBase) W(#name), +#include <vptr_list.h> +#undef VPTR_CLASS +#undef VPTR_MULTI_CLASS +}; + +DacHostVtPtrs g_dacHostVtPtrs; + +HRESULT +DacGetHostVtPtrs(void) +{ +#define VPTR_CLASS(name) \ + g_dacHostVtPtrs.name = name::VPtrHostVTable(); +#define VPTR_MULTI_CLASS(name, keyBase) \ + g_dacHostVtPtrs.name##__##keyBase = name::VPtrHostVTable(); +#include <vptr_list.h> +#undef VPTR_CLASS +#undef VPTR_MULTI_CLASS + + return S_OK; +} + +bool +DacExceptionFilter(Exception* ex, ClrDataAccess* access, + HRESULT* status) +{ + SUPPORTS_DAC_HOST_ONLY; + + // The DAC support functions throw HRExceptions and + // the underlying code can throw the normal set of + // CLR exceptions. Handle any exception + // other than an unexpected SEH exception. + // If we're not debugging, handle SEH exceptions also + // so that dac absorbs all exceptions by default. + if ((access && access->m_debugMode) && + ex->IsType(SEHException::GetType())) + { + // Indicate this exception should be rethrown. + return FALSE; + } + + // Indicate this exception is handled. + // XXX Microsoft - The C++-based EH has broken the ability + // to get proper SEH results. Make sure that the + // error returned is actually an error code as + // often it's just zero. + *status = ex->GetHR(); + if (!FAILED(*status)) + { + *status = E_FAIL; + } + return TRUE; +} + +void __cdecl +DacWarning(__in char* format, ...) +{ + char text[256]; + va_list args; + + va_start(args, format); + _vsnprintf_s(text, sizeof(text), _TRUNCATE, format, args); + text[sizeof(text) - 1] = 0; + va_end(args); + OutputDebugStringA(text); +} + +void +DacNotImpl(void) +{ + EX_THROW(HRException, (E_NOTIMPL)); +} + +void +DacError(HRESULT err) +{ + EX_THROW(HRException, (err)); +} + +// Ideally DacNoImpl and DacError would be marked no-return, but that will require changing a bunch of existing +// code to avoid "unreachable code" warnings. +void DECLSPEC_NORETURN +DacError_NoRet(HRESULT err) +{ + EX_THROW(HRException, (err)); +} + +TADDR +DacGlobalBase(void) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + return g_dacImpl->m_globalBase; +} + +HRESULT +DacReadAll(TADDR addr, PVOID buffer, ULONG32 size, bool throwEx) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + ClrSafeInt<TADDR> end = ClrSafeInt<TADDR>(addr) + ClrSafeInt<TADDR>(size); + if( end.IsOverflow() ) + { + // Overflow - corrupt data + DacError(CORDBG_E_TARGET_INCONSISTENT); + } + + HRESULT status; + ULONG32 returned; + +#if defined(DAC_MEASURE_PERF) + unsigned __int64 nStart, nEnd; + nStart = GetCycleCount(); +#endif // #if defined(DAC_MEASURE_PERF) + + status = g_dacImpl->m_pTarget-> + ReadVirtual(addr, (PBYTE)buffer, size, &returned); + +#if defined(DAC_MEASURE_PERF) + nEnd = GetCycleCount(); + g_nReadVirtualTotalTime += nEnd - nStart; +#endif // #if defined(DAC_MEASURE_PERF) + + if (status != S_OK) + { + // Regardless of what status is, it's very important for dump debugging to + // always return CORDBG_E_READVIRTUAL_FAILURE. + if (throwEx) + { + DacError(CORDBG_E_READVIRTUAL_FAILURE); + } + return CORDBG_E_READVIRTUAL_FAILURE; + } + if (returned != size) + { + if (throwEx) + { + DacError(HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)); + } + return HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY); + } + + return S_OK; +} + +HRESULT +DacWriteAll(TADDR addr, PVOID buffer, ULONG32 size, bool throwEx) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + HRESULT status; + + status = g_dacImpl->m_pMutableTarget-> + WriteVirtual(addr, (PBYTE)buffer, size); + if (status != S_OK) + { + if (throwEx) + { + DacError(status); + } + return status; + } + + return S_OK; +} + +#if defined(WIN64EXCEPTIONS) && defined(FEATURE_PAL) +HRESULT +DacVirtualUnwind(DWORD threadId, PCONTEXT context, PT_KNONVOLATILE_CONTEXT_POINTERS contextPointers) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + // The DAC code doesn't use these context pointers but zero them out to be safe. + if (contextPointers != NULL) + { + memset(contextPointers, 0, sizeof(T_KNONVOLATILE_CONTEXT_POINTERS)); + } + + ReleaseHolder<ICorDebugDataTarget4> dt; + HRESULT hr = g_dacImpl->m_pTarget->QueryInterface(IID_ICorDebugDataTarget4, (void **)&dt); + if (SUCCEEDED(hr)) + { + hr = dt->VirtualUnwind(threadId, sizeof(CONTEXT), (BYTE*)context); + } + + return hr; +} +#endif // defined(WIN64EXCEPTIONS) && defined(FEATURE_PAL) + +// DacAllocVirtual - Allocate memory from the target process +// Note: this is only available to clients supporting the legacy +// ICLRDataTarget2 interface. It's currently used by SOS for notification tables. +HRESULT +DacAllocVirtual(TADDR addr, ULONG32 size, + ULONG32 typeFlags, ULONG32 protectFlags, + bool throwEx, TADDR* mem) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + ICLRDataTarget2 * pTarget2 = g_dacImpl->GetLegacyTarget2(); + if (pTarget2 == NULL) + { + DacError(E_NOTIMPL); + UNREACHABLE(); + } + + CLRDATA_ADDRESS cdaMem; + HRESULT status = pTarget2->AllocVirtual( + TO_CDADDR(addr), size, typeFlags, protectFlags, &cdaMem); + if (status != S_OK) + { + if (throwEx) + { + DacError(status); + UNREACHABLE(); + } + + return status; + } + + *mem = CLRDATA_ADDRESS_TO_TADDR(cdaMem); + return S_OK; +} + +// DacFreeVirtual - Free memory from the target process +// Note: this is only available to clients supporting the legacy +// ICLRDataTarget2 interface. This is not currently used. +HRESULT +DacFreeVirtual(TADDR mem, ULONG32 size, ULONG32 typeFlags, + bool throwEx) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + ICLRDataTarget2 * pTarget2 = g_dacImpl->GetLegacyTarget2(); + if (pTarget2 == NULL) + { + DacError(E_NOTIMPL); + UNREACHABLE(); + } + + HRESULT status = pTarget2->FreeVirtual( + TO_CDADDR(mem), size, typeFlags); + + if (status != S_OK && throwEx) + { + DacError(status); + UNREACHABLE(); + } + + return status; +} + +PVOID +DacInstantiateTypeByAddressHelper(TADDR addr, ULONG32 size, bool throwEx, bool fReport) +{ +#ifdef _PREFIX_ + + // Dac accesses are not interesting for PREfix and cause alot of PREfix noise + // so we just return the unmodified pointer for our PREFIX builds + return (PVOID)addr; + +#else // !_PREFIX_ + + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + // Preserve special pointer values. + if (!addr || addr == (TADDR)-1) + { + return (PVOID)addr; + } + + // DacInstanceManager::Alloc will assert (with a non-obvious message) on 0-size instances. + // Fail sooner and more obviously here. + _ASSERTE_MSG( size > 0, "DAC coding error: instance size cannot be 0" ); + + // Do not attempt to allocate more than 64megs for one object instance. While we should + // never even come close to this size, in cases of heap corruption or bogus data passed + // into the dac, we can allocate huge amounts of data if we are unlucky. This santiy + // checks the size to ensure we don't allocate gigs of data. + if (size > 0x4000000) + { + if (throwEx) + { + DacError(E_OUTOFMEMORY); + } + return NULL; + } + + // + // Check the cache for an existing DPTR instance. + // It's possible that a previous access may have been + // smaller than the current access, so we have to + // allow an existing instance to be superseded. + // + + DAC_INSTANCE* inst = g_dacImpl->m_instances.Find(addr); + DAC_INSTANCE* oldInst = NULL; + if (inst) + { + // If the existing instance is large enough we + // can reuse it, otherwise we need to promote. + // We cannot promote a VPTR as the VPTR data + // has been updated with a host vtable and we + // don't want to lose that. This shouldn't + // happen anyway. + if (inst->size >= size) + { + return inst + 1; + } + else + { + // Existing instance is too small and must + // be superseded. + if (inst->usage == DAC_VPTR) + { + // The same address has already been marshalled as a VPTR, now we're trying to marshal as a + // DPTR. This is not allowed. + _ASSERTE_MSG(false, "DAC coding error: DPTR/VPTR usage conflict"); + DacError(E_INVALIDARG); + UNREACHABLE(); + } + + // Promote the larger instance into the hash + // in place of the smaller, but keep the + // smaller instance around in case code still + // has a pointer to it. But ensure that we can + // create the larger instance and add it to the + // hash table before removing the old one. + oldInst = inst; + } + } + + inst = g_dacImpl->m_instances.Alloc(addr, size, DAC_DPTR); + if (!inst) + { + DacError(E_OUTOFMEMORY); + UNREACHABLE(); + } + + if (fReport == false) + { + // mark the bit if necessary + inst->noReport = 1; + } + else + { + // clear the bit + inst->noReport = 0; + } + HRESULT status = DacReadAll(addr, inst + 1, size, false); + if (status != S_OK) + { + g_dacImpl->m_instances.ReturnAlloc(inst); + if (throwEx) + { + DacError(status); + } + return NULL; + } + + if (!g_dacImpl->m_instances.Add(inst)) + { + g_dacImpl->m_instances.ReturnAlloc(inst); + DacError(E_OUTOFMEMORY); + UNREACHABLE(); + } + + if (oldInst) + { + g_dacImpl->m_instances.Supersede(oldInst); + } + + return inst + 1; + +#endif // !_PREFIX_ +} + +PVOID DacInstantiateTypeByAddress(TADDR addr, ULONG32 size, bool throwEx) +{ + return DacInstantiateTypeByAddressHelper(addr, size, throwEx, true); +} + +PVOID DacInstantiateTypeByAddressNoReport(TADDR addr, ULONG32 size, bool throwEx) +{ + return DacInstantiateTypeByAddressHelper(addr, size, throwEx, false); +} + + +PVOID +DacInstantiateClassByVTable(TADDR addr, ULONG32 minSize, bool throwEx) +{ +#ifdef _PREFIX_ + + // Dac accesses are not interesting for PREfix and cause alot of PREfix noise + // so we just return the unmodified pointer for our PREFIX builds + return (PVOID)addr; + +#else // !_PREFIX_ + + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + // Preserve special pointer values. + if (!addr || addr == (TADDR)-1) + { + return (PVOID)addr; + } + + // Do not attempt to allocate more than 64megs for one object instance. While we should + // never even come close to this size, in cases of heap corruption or bogus data passed + // into the dac, we can allocate huge amounts of data if we are unlucky. This santiy + // checks the size to ensure we don't allocate gigs of data. + if (minSize > 0x4000000) + { + if (throwEx) + { + DacError(E_OUTOFMEMORY); + } + return NULL; + } + + // + // Check the cache for an existing VPTR instance. + // If there is an instance we assume that it's + // the right object. + // + + DAC_INSTANCE* inst = g_dacImpl->m_instances.Find(addr); + DAC_INSTANCE* oldInst = NULL; + if (inst) + { + // If the existing instance is a VPTR we can + // reuse it, otherwise we need to promote. + if (inst->usage == DAC_VPTR) + { + // Sanity check that the object we're returning is big enough to fill the PTR type it's being + // accessed with. For more information, see the similar check below for the case when the + // object isn't already cached + _ASSERTE_MSG(inst->size >= minSize, "DAC coding error: Attempt to instantiate a VPTR from an object that is too small"); + + return inst + 1; + } + else + { + // Existing instance is not a match and must + // be superseded. + // Promote the new instance into the hash + // in place of the old, but keep the + // old instance around in case code still + // has a pointer to it. But ensure that we can + // create the larger instance and add it to the + // hash table before removing the old one. + oldInst = inst; + } + } + + HRESULT status; + TADDR vtAddr; + ULONG32 size; + PVOID hostVtPtr; + + // Read the vtable pointer to get the actual + // implementation class identity. + if ((status = DacReadAll(addr, &vtAddr, sizeof(vtAddr), throwEx)) != S_OK) + { + return NULL; + } + + // + // Instantiate the right class, using the vtable as + // class identity. + // + +#define VPTR_CLASS(name) \ + if (vtAddr == g_dacImpl->m_globalBase + \ + g_dacGlobals.name##__vtAddr) \ + { \ + size = sizeof(name); \ + hostVtPtr = g_dacHostVtPtrs.name; \ + } \ + else +#define VPTR_MULTI_CLASS(name, keyBase) \ + if (vtAddr == g_dacImpl->m_globalBase + \ + g_dacGlobals.name##__##keyBase##__mvtAddr) \ + { \ + size = sizeof(name); \ + hostVtPtr = g_dacHostVtPtrs.name##__##keyBase; \ + } \ + else +#include <vptr_list.h> +#undef VPTR_CLASS +#undef VPTR_MULTI_CLASS + + { + // Can't identify the vtable pointer. + if (throwEx) + { + _ASSERTE_MSG(false,"DAC coding error: Unrecognized vtable pointer in VPTR marshalling code"); + DacError(E_INVALIDARG); + } + return NULL; + } + + // Sanity check that the object we're returning is big enough to fill the PTR type it's being + // accessed with. + // If this is not true, it means the type being marshalled isn't a sub-type (or the same type) + // as the PTR type it's being used as. For example, trying to marshal an instance of a SystemDomain + // object into a PTR_AppDomain will cause this ASSERT to fire (because both SystemDomain and AppDomain + // derived from BaseDomain, and SystemDomain is smaller than AppDomain). + _ASSERTE_MSG(size >= minSize, "DAC coding error: Attempt to instantiate a VPTR from an object that is too small"); + + inst = g_dacImpl->m_instances.Alloc(addr, size, DAC_VPTR); + if (!inst) + { + DacError(E_OUTOFMEMORY); + UNREACHABLE(); + } + + // Copy the object contents into the host instance. Note that this assumes the host and target + // have the same exact layout. Specifically, it assumes the host and target vtable pointers are + // the same size. + if ((status = DacReadAll(addr, inst + 1, size, false)) != S_OK) + { + g_dacImpl->m_instances.ReturnAlloc(inst); + if (throwEx) + { + DacError(status); + } + return NULL; + } + + // We now have a proper target object with a target + // vtable. We need to patch the vtable to the appropriate + // host vtable so that the virtual functions can be + // called in the host process. + *(PVOID*)(inst + 1) = hostVtPtr; + + if (!g_dacImpl->m_instances.Add(inst)) + { + g_dacImpl->m_instances.ReturnAlloc(inst); + DacError(E_OUTOFMEMORY); + UNREACHABLE(); + } + + if (oldInst) + { + g_dacImpl->m_instances.Supersede(oldInst); + } + return inst + 1; + +#endif // !_PREFIX_ +} + +#define LOCAL_STR_BUF 256 + +PSTR +DacInstantiateStringA(TADDR addr, ULONG32 maxChars, bool throwEx) +{ +#ifdef _PREFIX_ + + // Dac accesses are not interesting for PREfix and cause alot of PREfix noise + // so we just return the unmodified pointer for our PREFIX builds + return (PSTR)addr; + +#else // !_PREFIX_ + + HRESULT status; + + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + // Preserve special pointer values. + if (!addr || addr == (TADDR)-1) + { + return (PSTR)addr; + } + + + // Do not attempt to allocate more than 64megs for a string. While we should + // never even come close to this size, in cases of heap corruption or bogus data passed + // into the dac, we can allocate huge amounts of data if we are unlucky. This santiy + // checks the size to ensure we don't allocate gigs of data. + if (maxChars > 0x4000000) + { + if (throwEx) + { + DacError(E_OUTOFMEMORY); + } + return NULL; + } + + // + // Look for an existing string instance. + // + + DAC_INSTANCE* inst = g_dacImpl->m_instances.Find(addr); + if (inst && inst->usage == DAC_STRA) + { + return (PSTR)(inst + 1); + } + + // + // Determine the length of the string + // by iteratively reading blocks and scanning them + // for a terminator. + // + + char buf[LOCAL_STR_BUF]; + TADDR scanAddr = addr; + ULONG32 curBytes = 0; + ULONG32 returned; + + for (;;) + { + status = g_dacImpl->m_pTarget-> + ReadVirtual(scanAddr, (PBYTE)buf, sizeof(buf), + &returned); + if (status != S_OK) + { + // We hit invalid memory before finding a terminator. + if (throwEx) + { + DacError(CORDBG_E_READVIRTUAL_FAILURE); + } + return NULL; + } + + PSTR scan = (PSTR)buf; + PSTR scanEnd = scan + (returned / sizeof(*scan)); + while (scan < scanEnd) + { + if (!*scan) + { + break; + } + + scan++; + } + + if (!*scan) + { + // Found a terminator. + scanAddr += ((scan + 1) - buf) * sizeof(*scan); + break; + } + + // Ignore any partial character reads. The character + // will be reread on the next loop if necessary. + returned &= ~(sizeof(buf[0]) - 1); + + // The assumption is that a memory read cannot wrap + // around the address space, thus if we have read to + // the top of memory scanAddr cannot wrap farther + // than to zero. + curBytes += returned; + scanAddr += returned; + + if (!scanAddr || + (curBytes + sizeof(buf[0]) - 1) / sizeof(buf[0]) >= maxChars) + { + // Wrapped around the top of memory or + // we didn't find a terminator within the given bound. + if (throwEx) + { + DacError(E_INVALIDARG); + } + return NULL; + } + } + + // Now that we know the length we can create a + // host copy of the string. + PSTR retVal = (PSTR) + DacInstantiateTypeByAddress(addr, (ULONG32)(scanAddr - addr), throwEx); + if (retVal && + (inst = g_dacImpl->m_instances.Find(addr))) + { + inst->usage = DAC_STRA; + } + return retVal; + +#endif // !_PREFIX_ +} + +PWSTR +DacInstantiateStringW(TADDR addr, ULONG32 maxChars, bool throwEx) +{ +#ifdef _PREFIX_ + + // Dac accesses are not interesting for PREfix and cause alot of PREfix noise + // so we just return the unmodified pointer for our PREFIX builds + return (PWSTR)addr; + +#else // !_PREFIX_ + + HRESULT status; + + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + // Preserve special pointer values. + if (!addr || addr == (TADDR)-1) + { + return (PWSTR)addr; + } + + // Do not attempt to allocate more than 64megs for a string. While we should + // never even come close to this size, in cases of heap corruption or bogus data passed + // into the dac, we can allocate huge amounts of data if we are unlucky. This santiy + // checks the size to ensure we don't allocate gigs of data. + if (maxChars > 0x4000000) + { + if (throwEx) + { + DacError(E_OUTOFMEMORY); + } + return NULL; + } + + + // + // Look for an existing string instance. + // + + DAC_INSTANCE* inst = g_dacImpl->m_instances.Find(addr); + if (inst && inst->usage == DAC_STRW) + { + return (PWSTR)(inst + 1); + } + + // + // Determine the length of the string + // by iteratively reading blocks and scanning them + // for a terminator. + // + + WCHAR buf[LOCAL_STR_BUF]; + TADDR scanAddr = addr; + ULONG32 curBytes = 0; + ULONG32 returned; + + for (;;) + { + status = g_dacImpl->m_pTarget-> + ReadVirtual(scanAddr, (PBYTE)buf, sizeof(buf), + &returned); + if (status != S_OK) + { + // We hit invalid memory before finding a terminator. + if (throwEx) + { + DacError(CORDBG_E_READVIRTUAL_FAILURE); + } + return NULL; + } + + PWSTR scan = (PWSTR)buf; + PWSTR scanEnd = scan + (returned / sizeof(*scan)); + while (scan < scanEnd) + { + if (!*scan) + { + break; + } + + scan++; + } + + if (!*scan) + { + // Found a terminator. + scanAddr += ((scan + 1) - buf) * sizeof(*scan); + break; + } + + // Ignore any partial character reads. The character + // will be reread on the next loop if necessary. + returned &= ~(sizeof(buf[0]) - 1); + + // The assumption is that a memory read cannot wrap + // around the address space, thus if we have read to + // the top of memory scanAddr cannot wrap farther + // than to zero. + curBytes += returned; + scanAddr += returned; + + if (!scanAddr || + (curBytes + sizeof(buf[0]) - 1) / sizeof(buf[0]) >= maxChars) + { + // Wrapped around the top of memory or + // we didn't find a terminator within the given bound. + if (throwEx) + { + DacError(E_INVALIDARG); + } + return NULL; + } + } + + // Now that we know the length we can create a + // host copy of the string. + PWSTR retVal = (PWSTR) + DacInstantiateTypeByAddress(addr, (ULONG32)(scanAddr - addr), throwEx); + if (retVal && + (inst = g_dacImpl->m_instances.Find(addr))) + { + inst->usage = DAC_STRW; + } + return retVal; + +#endif // !_PREFIX_ +} + +TADDR +DacGetTargetAddrForHostAddr(LPCVOID ptr, bool throwEx) +{ +#ifdef _PREFIX_ + + // Dac accesses are not interesting for PREfix and cause alot of PREfix noise + // so we just return the unmodified pointer for our PREFIX builds + return (TADDR) ptr; + +#else // !_PREFIX_ + + // Preserve special pointer values. + if (ptr == NULL || ((TADDR) ptr == (TADDR)-1)) + { + return 0; + } + else + { + TADDR addr = 0; + HRESULT status = E_FAIL; + + EX_TRY + { + DAC_INSTANCE* inst = (DAC_INSTANCE*)ptr - 1; + if (inst->sig == DAC_INSTANCE_SIG) + { + addr = inst->addr; + status = S_OK; + } + else + { + status = E_INVALIDARG; + } + } + EX_CATCH + { + status = E_INVALIDARG; + } + EX_END_CATCH(SwallowAllExceptions) + + if (status != S_OK) + { + if (g_dacImpl && g_dacImpl->m_debugMode) + { + DebugBreak(); + } + + if (throwEx) + { + // This means a pointer was supplied which doesn't actually point to the beginning of + // a marshalled DAC instance. + _ASSERTE_MSG(false, "DAC coding error: Attempt to get target address from a host pointer " + "which is not an instance marshalled by DAC!"); + DacError(status); + } + } + + return addr; + } + +#endif // !_PREFIX_ +} + +// Similar to DacGetTargetAddrForHostAddr above except that ptr can represent any pointer within a host data +// structure marshalled from the target (rather than just a pointer to the first field). +TADDR +DacGetTargetAddrForHostInteriorAddr(LPCVOID ptr, bool throwEx) +{ + // Our algorithm for locating the containing DAC instance will search backwards through memory in + // DAC_INSTANCE_ALIGN increments looking for a valid header. The following constant determines how many of + // these iterations we'll perform before deciding the caller made a mistake and didn't marshal the + // containing instance from the target to the host properly. Lower values will determine the maximum + // offset from the start of a marshalled structure at which an interior pointer can appear. Higher values + // will bound the amount of time it takes to report an error in the case where code has been incorrectly + // DAC-ized. + const DWORD kMaxSearchIterations = 100; + +#ifdef _PREFIX_ + + // Dac accesses are not interesting for PREfix and cause alot of PREfix noise + // so we just return the unmodified pointer for our PREFIX builds + return (TADDR) ptr; + +#else // !_PREFIX_ + + // Preserve special pointer values. + if (ptr == NULL || ((TADDR) ptr == (TADDR)-1)) + { + return 0; + } + else + { + TADDR addr = 0; + HRESULT status = E_FAIL; + + EX_TRY + { + // We're going to search backwards through memory from the pointer looking for a valid DAC + // instance header. Initialize this search pointer to the first legal value it could hold. + // Intuitively this would be ptr - sizeof(DAC_INSTANCE), but DAC_INSTANCE headers are further + // constrained to lie on DAC_INSTANCE_ALIGN boundaries. DAC_INSTANCE_ALIGN is large (16 bytes) due + // to the need to keep the marshalled structure also aligned for any possible need, so we gain + // considerable performance from only needing to test for DAC_INSTANCE headers at + // DAC_INSTANCE_ALIGN aligned addresses. + DAC_INSTANCE * inst = (DAC_INSTANCE*)(((ULONG_PTR)ptr - sizeof(DAC_INSTANCE)) & ~(DAC_INSTANCE_ALIGN - 1)); + + // When code is DAC'ized correctly then our search algorithm is guaranteed to terminate safely + // before reading memory that doesn't belong to the containing DAC instance. Since people do make + // mistakes we want to limit how long and far we search however. The counter below will let us + // assert if we've likely tried to locate an interior host pointer in a non-marshalled structure. + DWORD cIterations = 0; + + bool tryAgain = false; + + // Scan backwards in memory looking for a DAC_INSTANCE header. + while (true) + { + // Step back DAC_INSTANCE_ALIGN bytes at a time (the initialization of inst above guarantees + // we start with an aligned pointer value. Stop every time our potential DAC_INSTANCE header + // has a correct signature value. + while (tryAgain || inst->sig != DAC_INSTANCE_SIG) + { + tryAgain = false; + inst = (DAC_INSTANCE*)((BYTE*)inst - DAC_INSTANCE_ALIGN); + + // If we've searched a lot of memory (currently 100 * 16 == 1600 bytes) without success, + // then assume this is due to an issue DAC-izing code (if you really do have a field within a + // DAC marshalled structure whose offset is >1600 bytes then feel free to update the + // constant at the start of this method). + if (++cIterations > kMaxSearchIterations) + { + status = E_INVALIDARG; + break; + } + } + + // Fall through to a DAC error if we searched too long without finding a header candidate. + if (status == E_INVALIDARG) + break; + + // Validate our candidate header by looking up the target address it claims to map in the + // instance hash. The entry should both exist and correspond exactly to our candidate instance + // pointer. + // TODO: but what if the same memory was marshalled more than once (eg. once as a DPTR, once as a VPTR)? + if (inst == g_dacImpl->m_instances.Find(inst->addr)) + { + // We've found a valid DAC instance. Now validate that the marshalled structure it + // represents really does enclose the pointer we're asking about. If not, someone hasn't + // marshalled a containing structure before trying to map a pointer within that structure + // (we've just gone and found the previous, unrelated marshalled structure in host memory). + BYTE * parent = (BYTE*)(inst + 1); + if (((BYTE*)ptr + sizeof(LPCVOID)) <= (parent + inst->size)) + { + // Everything checks out: we've found a DAC instance header and its address range + // encompasses the pointer we're interested in. Compute the corresponding target + // address by taking into account the offset of the interior pointer into its + // enclosing structure. + addr = inst->addr + ((BYTE*)ptr - parent); + status = S_OK; + } + else + { + // We found a valid DAC instance but it doesn't cover the address range containing our + // input pointer. Fall though to report an erroring DAC-izing code. + status = E_INVALIDARG; + } + break; + } + else + { + // This must not really be a match, perhaps a coincidence? + // Keep searching + tryAgain = true; + } + } + } + EX_CATCH + { + status = E_INVALIDARG; + } + EX_END_CATCH(SwallowAllExceptions) + + if (status != S_OK) + { + if (g_dacImpl && g_dacImpl->m_debugMode) + { + DebugBreak(); + } + + if (throwEx) + { + // This means a pointer was supplied which doesn't actually point to somewhere in a marshalled + // DAC instance. + _ASSERTE_MSG(false, "DAC coding error: Attempt to get target address from a host interior " + "pointer which is not an instance marshalled by DAC!"); + DacError(status); + } + } + + return addr; + } +#endif // !_PREFIX_ +} + +PWSTR DacGetVtNameW(TADDR targetVtable) +{ + PWSTR pszRet = NULL; + + ULONG *targ = &g_dacGlobals.Thread__vtAddr; + ULONG *targStart = targ; + for (ULONG i = 0; i < sizeof(g_dacHostVtPtrs) / sizeof(PVOID); i++) + { + if (targetVtable == (*targ + DacGlobalBase())) + { + pszRet = (PWSTR) *(g_dacVtStrings + (targ - targStart)); + break; + } + + targ++; + } + return pszRet; +} + +TADDR +DacGetTargetVtForHostVt(LPCVOID vtHost, bool throwEx) +{ + PVOID* host; + ULONG* targ; + ULONG i; + + // The host vtable table exactly parallels the + // target vtable table, so just iterate to a match + // return the matching entry. + host = &g_dacHostVtPtrs.Thread; + targ = &g_dacGlobals.Thread__vtAddr; + for (i = 0; i < sizeof(g_dacHostVtPtrs) / sizeof(PVOID); i++) + { + if (*host == vtHost) + { + return *targ + DacGlobalBase(); + } + + host++; + targ++; + } + + if (throwEx) + { + DacError(E_INVALIDARG); + } + return 0; +} + +// +// DacEnumMemoryRegion - report a region of memory to the dump generation code +// +// Parameters: +// addr - target address of the beginning of the memory region +// size - number of bytes to report +// fExpectSuccess - whether or not ASSERTs should be raised if some memory in this region +// is found to be unreadable. Generally we should only report readable +// memory (unless the target is corrupt, in which case we expect asserts +// if target consistency checking is enabled). Reporting memory that +// isn't fully readable often indicates an issue that could cause much worse +// problems (loss of dump data, long/infinite loops in dump generation), +// so we want to try and catch any such usage. Ocassionally we can't say +// for sure how much of the reported region will be readable (eg. for the +// LoaderHeap, we only know the length of the allocated address space, not +// the size of the commit region for every block). In these special cases, +// we pass false to indicate that we're happy reporting up to the first +// unreadable byte. This should be avoided if at all possible. +// +bool DacEnumMemoryRegion(TADDR addr, TSIZE_T size, bool fExpectSuccess /*=true*/) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + return g_dacImpl->ReportMem(addr, size, fExpectSuccess); +} + +// +// DacUpdateMemoryRegion - updates/poisons a region of memory of generated dump +// +// Parameters: +// addr - target address of the beginning of the memory region +// bufferSize - number of bytes to update/poison +// buffer - data to be written at given target address +// +bool DacUpdateMemoryRegion(TADDR addr, TSIZE_T bufferSize, BYTE* buffer) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + return g_dacImpl->DacUpdateMemoryRegion(addr, bufferSize, buffer); +} + +HRESULT +DacWriteHostInstance(PVOID host, bool throwEx) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + TADDR addr = DacGetTargetAddrForHostAddr(host, throwEx); + if (!addr) + { + return S_OK; + } + + DAC_INSTANCE* inst = (DAC_INSTANCE*)host - 1; + return g_dacImpl->m_instances.Write(inst, throwEx); +} + +bool +DacHostPtrHasEnumMark(LPCVOID host) +{ + if (!DacGetTargetAddrForHostAddr(host, false)) + { + // Make it easy to ignore invalid pointers when enumerating. + return true; + } + + DAC_INSTANCE* inst = ((DAC_INSTANCE*)host) - 1; + bool marked = inst->enumMem ? true : false; + inst->enumMem = true; + return marked; +} + +bool +DacHasMethodDescBeenEnumerated(LPCVOID pMD) +{ + if (!DacGetTargetAddrForHostAddr(pMD, false)) + { + // Make it easy to ignore invalid pointers when enumerating. + return true; + } + + DAC_INSTANCE* inst = ((DAC_INSTANCE*) pMD) - 1; + bool MDEnumed = inst->MDEnumed ? true : false; + return MDEnumed; +} + +bool +DacSetMethodDescEnumerated(LPCVOID pMD) +{ + if (!DacGetTargetAddrForHostAddr(pMD, false)) + { + // Make it easy to ignore invalid pointers when enumerating. + return true; + } + + DAC_INSTANCE* inst = ((DAC_INSTANCE*) pMD) - 1; + bool MDEnumed = inst->MDEnumed ? true : false; + inst->MDEnumed = true; + return MDEnumed; +} + +// This gets called from DAC-ized code in the VM. +IMDInternalImport* +DacGetMDImport(const PEFile* peFile, bool throwEx) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + return g_dacImpl->GetMDImport(peFile, throwEx); +} + +IMDInternalImport* +DacGetMDImport(const ReflectionModule* reflectionModule, bool throwEx) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + return g_dacImpl->GetMDImport(reflectionModule, throwEx); +} + +COR_ILMETHOD* +DacGetIlMethod(TADDR methAddr) +{ + ULONG32 methodSize = static_cast<ULONG32>(PEDecoder::ComputeILMethodSize(methAddr)); + + // Sometimes when reading from dumps and inspect NGEN images, but we end up reading metadata from IL image + // the method RVA could not match and we could read from a random address that will translate in inconsistent + // IL code header. If we see the size of the code bigger than 64 Megs we are probably reading a bad IL code header. + // For details see issue DevDiv 273199. + if (methodSize > 0x4000000) + { + DacError(CORDBG_E_TARGET_INCONSISTENT); + UNREACHABLE(); + } + return (COR_ILMETHOD*) + DacInstantiateTypeByAddress(methAddr, methodSize, + true); +} + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS +void +DacMdCacheAddEEName(TADDR taEE, const SString& ssEEName) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + g_dacImpl->MdCacheAddEEName(taEE, ssEEName); +} +bool +DacMdCacheGetEEName(TADDR taEE, SString & eeName) +{ + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + return g_dacImpl->MdCacheGetEEName(taEE, eeName); +} + +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + +PVOID +DacAllocHostOnlyInstance(ULONG32 size, bool throwEx) +{ + SUPPORTS_DAC_HOST_ONLY; + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + DAC_INSTANCE* inst = g_dacImpl->m_instances.Alloc(0, size, DAC_DPTR); + if (!inst) + { + DacError(E_OUTOFMEMORY); + UNREACHABLE(); + } + + g_dacImpl->m_instances.AddSuperseded(inst); + + return inst + 1; +} + +// +// Queries whether ASSERTs should be raised when inconsistencies in the target are detected +// +// Return Value: +// true if ASSERTs should be raised in DACized code. +// false if ASSERTs should be ignored. +// +// Notes: +// See code:ClrDataAccess::TargetConsistencyAssertsEnabled for details. +bool DacTargetConsistencyAssertsEnabled() +{ + if (!g_dacImpl) + { + // No ClrDataAccess instance available (maybe we're still initializing). Any asserts when this is + // the case should only be host-asserts (i.e. always bugs), and so we should just return true. + return true; + } + + return g_dacImpl->TargetConsistencyAssertsEnabled(); +} + +// +// DacEnumCodeForStackwalk +// This is a helper function to enumerate the instructions around a call site to aid heuristics +// used by debugger stack walkers. +// +// Arguments: +// taCallEnd - target address of the instruction just after the call instruction for the stack +// frame we want to examine(i.e. the return address for the next frame). +// +// Note that this is shared by our two stackwalks during minidump generation, +// code:Thread::EnumMemoryRegionsWorker and code:ClrDataAccess::EnumMemWalkStackHelper. Ideally +// we'd only have one stackwalk, but we currently have two different APIs for stackwalking +// (CLR StackFrameIterator and IXCLRDataStackWalk), and we must ensure that the memory needed +// for either is captured in a minidump. Eventually, all clients should get moved over to the +// arrowhead debugging architecture, at which time we can rip out all the IXCLRData APIs, and +// so this logic could just be private to the EnumMem code for Thread. +// +void DacEnumCodeForStackwalk(TADDR taCallEnd) +{ + // + // x86 stack walkers often end up having to guess + // about what's a return address on the stack. + // Doing so involves looking at the code at the + // possible call site and seeing if it could + // reach the callee. Save enough code and around + // the call site to allow this with a dump. + // + // For whatever reason 64-bit platforms require us to save + // the instructions around the call sites on the stack as well. + // Otherwise we cannnot show the stack in a minidump. + // + // Note that everything we do here is a heuristic that won't always work in general. + // Eg., part of the 2xMAX_INSTRUCTION_LENGTH range might not be mapped (we could be + // right on a page boundary). More seriously, X86 is not necessarily parsable in reverse + // (eg. there could be a segment-override prefix in front of the call instruction that + // we miss). So we'll dump what we can and ignore any failures. Ideally we'd better + // quantify exactly what debuggers need and why, and try and avoid these ugly heuristics. + // It seems like these heuristics are too tightly coupled to the implementation details + // of some specific debugger stackwalking algorithm. + // + DacEnumMemoryRegion(taCallEnd - MAX_INSTRUCTION_LENGTH, MAX_INSTRUCTION_LENGTH * 2, false); + +#if defined(_TARGET_X86_) + // If it was an indirect call we also need to save the data indirected through. + // Note that this only handles absolute indirect calls (ModR/M byte of 0x15), all the other forms of + // indirect calls are register-relative, and so we'd have to do a much more complicated decoding based + // on the register context. Regardless, it seems like this is fundamentally error-prone because it's + // aways possible that the call instruction was not 6 bytes long, and we could have some other instructions + // that happen to match the pattern we're looking for. + PTR_BYTE callCode = PTR_BYTE(taCallEnd - 6); + PTR_BYTE callMrm = PTR_BYTE(taCallEnd - 5); + PTR_TADDR callInd = PTR_TADDR(taCallEnd - 4); + if (callCode.IsValid() && + (*callCode == 0xff) && + callMrm.IsValid() && + (*callMrm == 0x15) && + callInd.IsValid()) + { + DacEnumMemoryRegion(*callInd, sizeof(TADDR), false); + } +#endif // #ifdef _TARGET_X86_ +} + +// ---------------------------------------------------------------------------- +// DacReplacePatches +// +// Description: +// Given the address and the size of a memory range which is stored in the buffer, replace all the patches +// in the buffer with the real opcodes. This is especially important on X64 where the unwinder needs to +// disassemble the native instructions. +// +// Arguments: +// * range - the address and the size of the memory range +// * pBuffer - the buffer containting the memory range +// +// Return Value: +// Return S_OK if everything succeeds. +// +// Assumptions: +// * The debuggee has to be stopped. +// +// Notes: +// * @dbgtodo ICDProcess - When we DACize code:CordbProcess::ReadMemory, +// we should change it to use this function. +// + +HRESULT DacReplacePatchesInHostMemory(MemoryRange range, PVOID pBuffer) +{ + SUPPORTS_DAC; + + // If the patch table is invalid, then there is no patch to replace. + if (!DebuggerController::GetPatchTableValid()) + { + return S_OK; + } + + HASHFIND info; + + DebuggerPatchTable * pTable = DebuggerController::GetPatchTable(); + DebuggerControllerPatch * pPatch = pTable->GetFirstPatch(&info); + + // <PERF> + // The unwinder needs to read the stack very often to restore pushed registers, retrieve the + // return addres, etc. However, stack addresses should never be patched. + // One way to optimize this code is to pass the stack base and the stack limit of the thread to this + // function and use those two values to filter out stack addresses. + // + // Another thing we can do is instead of enumerating the patches, we could enumerate the address. + // This is more efficient when we have a large number of patches and a small memory range. Perhaps + // we could do a hybrid approach, i.e. use the size of the range and the number of patches to dynamically + // determine which enumeration is more efficient. + // </PERF> + while (pPatch != NULL) + { + CORDB_ADDRESS patchAddress = (CORDB_ADDRESS)dac_cast<TADDR>(pPatch->address); + + if (patchAddress != NULL) + { + PRD_TYPE opcode = pPatch->opcode; + + CORDB_ADDRESS address = (CORDB_ADDRESS)(dac_cast<TADDR>(range.StartAddress())); + SIZE_T cbSize = range.Size(); + + // Check if the address of the patch is in the specified memory range. + if (IsPatchInRequestedRange(address, cbSize, patchAddress)) + { + // Replace the patch in the buffer with the original opcode. + CORDbgSetInstructionEx(reinterpret_cast<PBYTE>(pBuffer), address, patchAddress, opcode, cbSize); + } + } + + pPatch = pTable->GetNextPatch(&info); + } + + return S_OK; +} diff --git a/src/debug/daccess/dacimpl.h b/src/debug/daccess/dacimpl.h new file mode 100644 index 0000000000..635be80232 --- /dev/null +++ b/src/debug/daccess/dacimpl.h @@ -0,0 +1,4015 @@ +// 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: dacimpl.h +// + +// +// Central header file for external data access implementation. +// +//***************************************************************************** + + +#ifndef __DACIMPL_H__ +#define __DACIMPL_H__ + +#if defined(_TARGET_ARM_) || defined(FEATURE_CORESYSTEM) // @ARMTODO: STL breaks the build with current VC headers +//--------------------------------------------------------------------------------------- +// Setting DAC_HASHTABLE tells the DAC to use the hand rolled hashtable for +// storing code:DAC_INSTANCE . Otherwise, the DAC uses the STL unordered_map to. + +#define DAC_HASHTABLE +#endif // _TARGET_ARM_|| FEATURE_CORESYSTEM + +#ifndef DAC_HASHTABLE +#pragma push_macro("return") +#undef return +#include <unordered_map> +#pragma pop_macro("return") +#endif //DAC_HASHTABLE +extern CRITICAL_SECTION g_dacCritSec; + +// Convert between CLRDATA_ADDRESS and TADDR. +// Note that CLRDATA_ADDRESS is sign-extended (for compat with Windbg and OS conventions). Converting +// from pointer-size values to CLRDATA_ADDRESS should ALWAYS use this TO_CDADDR macro to avoid bugs when +// dealing with 3/4GB 32-bit address spaces. You must not rely on the compiler's implicit conversion +// from ULONG32 to ULONG64 - it is incorrect. Ideally we'd use some compiler tricks or static analysis +// to help detect such errors (they are nefarious since 3/4GB addresses aren't well tested) . +// +// Note: We're in the process of switching the implementation over to CORDB_ADDRESS instead, which is also +// 64 bits, but 0-extended. This means that conversions between TADDR and CORDB_ADDRESS are simple and natural, +// but as long as we have some legacy code, conversions involving CLRDATA_ADDRESS are a pain. Eventually we +// should eliminate CLRDATA_ADDRESS entirely from the implementation, but that will require moving SOS off of +// the old DAC stuff etc. +// +// Here are the possible conversions: +// TADDR -> CLRDATA_ADDRESS: TO_CDADDR +// CORDB_ADDRESS -> CLRDATA_ADDRESS: TO_CDADDR +// CLRDATA_ADDRESS -> TADDR: CLRDATA_ADDRESS_TO_TADDR +// CORDB_ADDRESS -> TADDR: CORDB_ADDRESS_TO_TADDR +// TADDR -> CORDB_ADDRESS: implicit +// CLRDATA_ADDRESS -> CORDB_ADDRESS: CLRDATA_ADDRESS_TO_TADDR +// +#define TO_CDADDR(taddr) ((CLRDATA_ADDRESS)(LONG_PTR)(taddr)) + +// Convert a CLRDATA_ADDRESS (64-bit unsigned sign-extended target address) to a TADDR +inline TADDR CLRDATA_ADDRESS_TO_TADDR(CLRDATA_ADDRESS cdAddr) +{ + SUPPORTS_DAC; +#ifndef _WIN64 + static_assert_no_msg(sizeof(TADDR)==sizeof(UINT)); + INT64 iSignedAddr = (INT64)cdAddr; + if (iSignedAddr > INT_MAX || iSignedAddr < INT_MIN) + { + _ASSERTE_MSG(false, "CLRDATA_ADDRESS out of range for this platform"); + DacError(E_INVALIDARG); + } +#endif + return (TADDR)cdAddr; +} + +// No throw, Convert a CLRDATA_ADDRESS (64-bit unsigned sign-extended target address) to a TADDR +// Use this in places where we know windbg may pass in bad addresses +inline HRESULT TRY_CLRDATA_ADDRESS_TO_TADDR(CLRDATA_ADDRESS cdAddr, TADDR* pOutTaddr) +{ + SUPPORTS_DAC; +#ifndef _WIN64 + static_assert_no_msg(sizeof(TADDR)==sizeof(UINT)); + INT64 iSignedAddr = (INT64)cdAddr; + if (iSignedAddr > INT_MAX || iSignedAddr < INT_MIN) + { + *pOutTaddr = 0; + return E_INVALIDARG; + } +#endif + *pOutTaddr = (TADDR)cdAddr; + return S_OK; +} + +// Convert a CORDB_ADDRESS (64-bit unsigned 0-extended target address) to a TADDR +inline TADDR CORDB_ADDRESS_TO_TADDR(CORDB_ADDRESS cdbAddr) +{ + SUPPORTS_DAC; +#ifndef _WIN64 + static_assert_no_msg(sizeof(TADDR)==sizeof(UINT)); + if (cdbAddr > UINT_MAX) + { + _ASSERTE_MSG(false, "CORDB_ADDRESS out of range for this platform"); + DacError(E_INVALIDARG); + } +#endif + return (TADDR)cdbAddr; +} + +// TO_TADDR is the old way of converting CLRDATA_ADDRESSes to TADDRs. Unfortunately, +// this has been used in many places to also cast pointers (void* etc.) to TADDR, and +// so we can't actually require the argument to be a valid CLRDATA_ADDRESS. New code +// should use CLRDATA_ADDRESS_TO_TADDR instead. +#define TO_TADDR(cdaddr) ((TADDR)(cdaddr)) + +#define TO_CDENUM(ptr) ((CLRDATA_ENUM)(ULONG_PTR)(ptr)) +#define FROM_CDENUM(type, cdenum) ((type*)(ULONG_PTR)(cdenum)) + +#define SIMPFRAME_ALL \ + (CLRDATA_SIMPFRAME_UNRECOGNIZED | \ + CLRDATA_SIMPFRAME_MANAGED_METHOD | \ + CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE | \ + CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE) + +enum DAC_USAGE_TYPE +{ + DAC_DPTR, + DAC_VPTR, + DAC_STRA, + DAC_STRW, +}; + +// mscordacwks's module handle +extern HINSTANCE g_thisModule; + +class ReflectionModule; + +struct DAC_MD_IMPORT +{ + DAC_MD_IMPORT* next; // list link field + TADDR peFile; // a TADDR for a PEFile* or a ReflectionModule* + IMDInternalImport* impl; // Associated metadata interface + bool isAlternate; // for NGEN images set to true if the metadata corresponds to the IL image + + DAC_MD_IMPORT(TADDR peFile_, + IMDInternalImport* impl_, + bool isAlt_ = false, + DAC_MD_IMPORT* next_ = NULL) + : next(next_) + , peFile(peFile_) + , impl(impl_) + , isAlternate(isAlt_) + { + SUPPORTS_DAC_HOST_ONLY; + } +}; + + +// This class maintains a cache of IMDInternalImport* and their corresponding +// source (a PEFile* or a ReflectionModule*), as a singly-linked list of +// DAC_MD_IMPORT nodes. The cache is flushed whenever the process state changes +// by calling its Flush() member function. +class MDImportsCache +{ +public: + + MDImportsCache() + : m_head(NULL) + {} + + ~MDImportsCache() + { + Flush(); + } + + FORCEINLINE + IMDInternalImport* Get(TADDR key) const + { + SUPPORTS_DAC; + for (DAC_MD_IMPORT* importList = m_head; importList; importList = importList->next) + { + if (importList->peFile == key) + { + return importList->impl; + } + } + return NULL; + } + + FORCEINLINE + DAC_MD_IMPORT* Add(TADDR peFile, IMDInternalImport* impl, bool isAlt) + { + SUPPORTS_DAC; + DAC_MD_IMPORT* importList = new (nothrow) DAC_MD_IMPORT(peFile, impl, isAlt, m_head); + if (!importList) + { + return NULL; + } + + m_head = importList; + return importList; + } + + void Flush() + { + DAC_MD_IMPORT* importList; + + while (m_head) + { + importList = m_head; + m_head = importList->next; + importList->impl->Release(); + delete importList; + } + } + +private: + + DAC_MD_IMPORT* m_head; // the beginning of the list of cached MD imports + +}; + +struct METH_EXTENTS +{ + ULONG32 numExtents; + ULONG32 curExtent; + // Currently only one is needed. + CLRDATA_ADDRESS_RANGE extents[1]; +}; + +HRESULT ConvertUtf8(__in LPCUTF8 utf8, + ULONG32 bufLen, + ULONG32* nameLen, + __out_ecount_part_opt(bufLen, *nameLen) PWSTR buffer); +HRESULT AllocUtf8(__in_opt LPCWSTR wstr, + ULONG32 srcChars, + __deref_out LPUTF8* utf8); + +HRESULT GetFullClassNameFromMetadata(IMDInternalImport* mdImport, + mdTypeDef classToken, + ULONG32 bufferChars, + __inout_ecount(bufferChars) LPUTF8 buffer); +HRESULT GetFullMethodNameFromMetadata(IMDInternalImport* mdImport, + mdMethodDef methodToken, + ULONG32 bufferChars, + __inout_ecount(bufferChars) LPUTF8 buffer); + +enum SplitSyntax +{ + SPLIT_METHOD, + SPLIT_TYPE, + SPLIT_FIELD, + SPLIT_NO_NAME, +}; + +HRESULT SplitFullName(__in_z __in PCWSTR fullName, + SplitSyntax syntax, + ULONG32 memberDots, + __deref_out_opt LPUTF8* namespaceName, + __deref_out_opt LPUTF8* typeName, + __deref_out_opt LPUTF8* memberName, + __deref_out_opt LPUTF8* params); + +int CompareUtf8(__in LPCUTF8 str1, __in LPCUTF8 str2, __in ULONG32 nameFlags); + +#define INH_STATIC \ + (CLRDATA_VALUE_ALL_KINDS | \ + CLRDATA_VALUE_IS_INHERITED | CLRDATA_VALUE_FROM_STATIC) + +HRESULT InitFieldIter(DeepFieldDescIterator* fieldIter, + TypeHandle typeHandle, + bool canHaveFields, + ULONG32 flags, + IXCLRDataTypeInstance* fromType); + +ULONG32 GetTypeFieldValueFlags(TypeHandle typeHandle, + FieldDesc* fieldDesc, + ULONG32 otherFlags, + bool isDeref); + +//---------------------------------------------------------------------------- +// +// MetaEnum. +// +//---------------------------------------------------------------------------- + +class MetaEnum +{ +public: + MetaEnum(void) + : m_domainIter(FALSE) + { + Clear(); + m_appDomain = NULL; + } + ~MetaEnum(void) + { + End(); + } + + void Clear(void) + { + m_mdImport = NULL; + m_kind = 0; + m_lastToken = mdTokenNil; + } + + HRESULT Start(IMDInternalImport* mdImport, ULONG32 kind, + mdToken container); + void End(void); + + HRESULT NextToken(mdToken* token, + __deref_opt_out_opt LPCUTF8* namespaceName, + __deref_opt_out_opt LPCUTF8* name); + HRESULT NextDomainToken(AppDomain** appDomain, + mdToken* token); + HRESULT NextTokenByName(__in_opt LPCUTF8 namespaceName, + __in_opt LPCUTF8 name, + ULONG32 nameFlags, + mdToken* token); + HRESULT NextDomainTokenByName(__in_opt LPCUTF8 namespaceName, + __in_opt LPCUTF8 name, + ULONG32 nameFlags, + AppDomain** appDomain, mdToken* token); + + static HRESULT CdNextToken(CLRDATA_ENUM* handle, + mdToken* token) + { + MetaEnum* iter = FROM_CDENUM(MetaEnum, *handle); + if (!iter) + { + return S_FALSE; + } + + return iter->NextToken(token, NULL, NULL); + } + static HRESULT CdNextDomainToken(CLRDATA_ENUM* handle, + AppDomain** appDomain, + mdToken* token) + { + MetaEnum* iter = FROM_CDENUM(MetaEnum, *handle); + if (!iter) + { + return S_FALSE; + } + + return iter->NextDomainToken(appDomain, token); + } + static HRESULT CdEnd(CLRDATA_ENUM handle) + { + MetaEnum* iter = FROM_CDENUM(MetaEnum, handle); + if (iter) + { + delete iter; + return S_OK; + } + else + { + return E_INVALIDARG; + } + } + + IMDInternalImport* m_mdImport; + ULONG32 m_kind; + HENUMInternal m_enum; + AppDomain* m_appDomain; + AppDomainIterator m_domainIter; + mdToken m_lastToken; + + static HRESULT New(Module* mod, + ULONG32 kind, + mdToken container, + IXCLRDataAppDomain* pubAppDomain, + MetaEnum** metaEnum, + CLRDATA_ENUM* handle); +}; + +//---------------------------------------------------------------------------- +// +// SplitName. +// +//---------------------------------------------------------------------------- + +class SplitName +{ +public: + // Type of name and splitting being done in this instance. + SplitSyntax m_syntax; + ULONG32 m_nameFlags; + ULONG32 m_memberDots; + + // Split fields. + LPUTF8 m_namespaceName; + LPUTF8 m_typeName; + mdTypeDef m_typeToken; + LPUTF8 m_memberName; + mdMethodDef m_memberToken; + LPUTF8 m_params; + // XXX Microsoft - Translated signature. + + // Arbitrary extra data. + Thread* m_tlsThread; + Module* m_module; + MetaEnum m_metaEnum; + DeepFieldDescIterator m_fieldEnum; + ULONG64 m_objBase; + FieldDesc* m_lastField; + + SplitName(SplitSyntax syntax, ULONG32 nameFlags, + ULONG32 memberDots); + ~SplitName(void) + { + Delete(); + } + + void Delete(void); + void Clear(void); + + HRESULT SplitString(__in_opt PCWSTR fullName); + + bool FindType(IMDInternalImport* mdInternal); + bool FindMethod(IMDInternalImport* mdInternal); + bool FindField(IMDInternalImport* mdInternal); + + int Compare(LPCUTF8 str1, LPCUTF8 str2) + { + return CompareUtf8(str1, str2, m_nameFlags); + } + + static HRESULT AllocAndSplitString(__in_opt PCWSTR fullName, + SplitSyntax syntax, + ULONG32 nameFlags, + ULONG32 memberDots, + SplitName** split); + + static HRESULT CdStartMethod(__in_opt PCWSTR fullName, + ULONG32 nameFlags, + Module* mod, + mdTypeDef typeToken, + AppDomain* appDomain, + IXCLRDataAppDomain* pubAppDomain, + SplitName** split, + CLRDATA_ENUM* handle); + static HRESULT CdNextMethod(CLRDATA_ENUM* handle, + mdMethodDef* token); + static HRESULT CdNextDomainMethod(CLRDATA_ENUM* handle, + AppDomain** appDomain, + mdMethodDef* token); + + static HRESULT CdStartField(__in_opt PCWSTR fullName, + ULONG32 nameFlags, + ULONG32 fieldFlags, + IXCLRDataTypeInstance* fromTypeInst, + TypeHandle typeHandle, + Module* mod, + mdTypeDef typeToken, + ULONG64 objBase, + Thread* tlsThread, + IXCLRDataTask* pubTlsThread, + AppDomain* appDomain, + IXCLRDataAppDomain* pubAppDomain, + SplitName** split, + CLRDATA_ENUM* handle); + static HRESULT CdNextField(ClrDataAccess* dac, + CLRDATA_ENUM* handle, + IXCLRDataTypeDefinition** fieldType, + ULONG32* fieldFlags, + IXCLRDataValue** value, + ULONG32 nameBufRetLen, + ULONG32* nameLenRet, + __out_ecount_part_opt(nameBufRetLen, *nameLenRet) WCHAR nameBufRet[ ], + IXCLRDataModule** tokenScopeRet, + mdFieldDef* tokenRet); + static HRESULT CdNextDomainField(ClrDataAccess* dac, + CLRDATA_ENUM* handle, + IXCLRDataValue** value); + + static HRESULT CdStartType(__in_opt PCWSTR fullName, + ULONG32 nameFlags, + Module* mod, + AppDomain* appDomain, + IXCLRDataAppDomain* pubAppDomain, + SplitName** split, + CLRDATA_ENUM* handle); + static HRESULT CdNextType(CLRDATA_ENUM* handle, + mdTypeDef* token); + static HRESULT CdNextDomainType(CLRDATA_ENUM* handle, + AppDomain** appDomain, + mdTypeDef* token); + + static HRESULT CdEnd(CLRDATA_ENUM handle) + { + SplitName* split = FROM_CDENUM(SplitName, handle); + if (split) + { + delete split; + return S_OK; + } + else + { + return E_INVALIDARG; + } + } +}; + +//---------------------------------------------------------------------------- +// +// ProcessModIter. +// +//---------------------------------------------------------------------------- + +struct ProcessModIter +{ + AppDomainIterator m_domainIter; + bool m_nextDomain; + AppDomain::AssemblyIterator m_assemIter; + bool m_iterShared; +#ifdef FEATURE_LOADER_OPTIMIZATION + SharedDomain::SharedAssemblyIterator m_sharedIter; +#endif + Assembly* m_curAssem; + Assembly::ModuleIterator m_modIter; + + ProcessModIter(void) + : m_domainIter(FALSE) + { + SUPPORTS_DAC; + m_nextDomain = true; + m_iterShared = false; + m_curAssem = NULL; + } + + Assembly * NextAssem() + { + SUPPORTS_DAC; + while (!m_iterShared) + { + if (m_nextDomain) + { + if (!m_domainIter.Next()) + { + m_iterShared = true; + break; + } + + m_nextDomain = false; + + m_assemIter = m_domainIter.GetDomain()->IterateAssembliesEx((AssemblyIterationFlags)( + kIncludeLoaded | kIncludeExecution)); + } + + CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; + if (!m_assemIter.Next(pDomainAssembly.This())) + { + m_nextDomain = true; + continue; + } + + // Note: DAC doesn't need to keep the assembly alive - see code:CollectibleAssemblyHolder#CAH_DAC + CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly(); + if (!pAssembly->IsDomainNeutral()) + { + // We've found a domain-specific assembly, so this is a unique element in the Assembly + // iteration. + return pAssembly; + } + + // Found a shared assembly, which may be duplicated + // across app domains. Ignore it now and let + // it get picked up in the shared iteration where + // it'll only occur once. + } +#ifdef FEATURE_LOADER_OPTIMIZATION + if (!m_sharedIter.Next()) + { + return NULL; + } + return m_sharedIter.GetAssembly(); +#else + return NULL; +#endif + } + + Module* NextModule(void) + { + SUPPORTS_DAC; + for (;;) + { + if (!m_curAssem) + { + m_curAssem = NextAssem(); + if (!m_curAssem) + { + return NULL; + } + + m_modIter = m_curAssem->IterateModules(); + } + + if (!m_modIter.Next()) + { + m_curAssem = NULL; + continue; + } + + return m_modIter.GetModule(); + } + } +}; + +//---------------------------------------------------------------------------- +// +// DacInstanceManager. +// +//---------------------------------------------------------------------------- + +// The data for an access may have special alignment needs and +// the cache must provide similar semantics. +#define DAC_INSTANCE_ALIGN 16 + +#define DAC_INSTANCE_SIG 0xdac1 + +// The instance manager allocates large blocks and then +// suballocates those for particular instances. +struct DAC_INSTANCE_BLOCK +{ + DAC_INSTANCE_BLOCK* next; + ULONG32 bytesUsed; + ULONG32 bytesFree; +}; + +#define DAC_INSTANCE_BLOCK_ALLOCATION 0x40000 + +// Sufficient memory is allocated to guarantee storage of the +// instance header plus room for alignment padding. +// Once the aligned pointer is found, this structure is prepended to +// the aligned pointer and therefore doesn't affect the alignment +// of the actual instance data. +struct DAC_INSTANCE +{ + DAC_INSTANCE* next; + TADDR addr; + ULONG32 size; + // Identifying marker to give a simple + // check for host->taddr validity. + ULONG32 sig:16; + // DPTR or VPTR. See code:DAC_USAGE_TYPE + ULONG32 usage:2; + + // Marker that can be used to prevent reporting this memory to the callback + // object (via ICLRDataEnumMemoryRegionsCallback:EnumMemoryRegion) + // more than once. This bit is checked only by the DacEnumHost?PtrMem + // macros, so consistent use of those macros ensures that the memory is + // reported at most once + ULONG32 enumMem:1; + + // Marker to prevent metadata gets reported to mini-dump + ULONG32 noReport:1; + + // Marker to determine if EnumMemoryRegions has been called on + // a method descriptor + ULONG32 MDEnumed:1; + +#ifdef _WIN64 + // Keep DAC_INSTANCE a multiple of DAC_INSTANCE_ALIGN + // bytes in size. + ULONG32 pad[2]; +#endif +}; + +struct DAC_INSTANCE_PUSH +{ + DAC_INSTANCE_PUSH* next; + DAC_INSTANCE_BLOCK* blocks; + ULONG64 blockMemUsage; + ULONG32 numInst; + ULONG64 instMemUsage; +}; + +// The runtime will want the best access locality possible, +// so it's likely that many instances will be clustered. +// The hash function needs to spread near addresses across +// hash entries, so hash on the low bits of the target address. +// Not all the way down to the LSB, though, as there generally +// won't be individual accesses at the byte level. Assume that +// most accesses will be natural-word aligned. +#define DAC_INSTANCE_HASH_BITS 10 +#define DAC_INSTANCE_HASH_SHIFT 2 + +#define DAC_INSTANCE_HASH(addr) \ + (((ULONG32)(ULONG_PTR)(addr) >> DAC_INSTANCE_HASH_SHIFT) & \ + ((1 << DAC_INSTANCE_HASH_BITS) - 1)) +#define DAC_INSTANCE_HASH_SIZE (1 << DAC_INSTANCE_HASH_BITS) + + +struct DumpMemoryReportStatics +{ + TSIZE_T m_cbStack; // number of bytes that we report directly for stack walk + TSIZE_T m_cbNgen; // number of bytes that we report directly for ngen images + TSIZE_T m_cbModuleList; // number of bytes that we report for module list directly + TSIZE_T m_cbClrStatics; // number of bytes that we report for CLR statics + TSIZE_T m_cbClrHeapStatics; // number of bytes that we report for CLR heap statics + TSIZE_T m_cbImplicity; // number of bytes that we report implicitly +}; + + +class DacInstanceManager +{ +public: + DacInstanceManager(void); + ~DacInstanceManager(void); + + DAC_INSTANCE* Add(DAC_INSTANCE* inst); + + DAC_INSTANCE* Alloc(TADDR addr, ULONG32 size, DAC_USAGE_TYPE usage); + void ReturnAlloc(DAC_INSTANCE* inst); + DAC_INSTANCE* Find(TADDR addr); + HRESULT Write(DAC_INSTANCE* inst, bool throwEx); + void Supersede(DAC_INSTANCE* inst); + void Flush(void); + void Flush(bool fSaveBlock); + void ClearEnumMemMarker(void); + + void AddSuperseded(DAC_INSTANCE* inst) + { + SUPPORTS_DAC; + inst->next = m_superseded; + m_superseded = inst; + } + + UINT DumpAllInstances(ICLRDataEnumMemoryRegionsCallback *pCallBack); + +private: + + DAC_INSTANCE_BLOCK* FindInstanceBlock(DAC_INSTANCE* inst); + void FreeAllBlocks(bool fSaveBlock); + + void InitEmpty(void) + { + m_blocks = NULL; + // m_unusedBlock is not NULLed here; it can contain one block we will use after + // a flush is complete. + m_blockMemUsage = 0; + m_numInst = 0; + m_instMemUsage = 0; +#ifdef DAC_HASHTABLE + ZeroMemory(m_hash, sizeof(m_hash)); +#endif + m_superseded = NULL; + m_instPushed = NULL; + } + +#if defined(DAC_HASHTABLE) + + typedef struct _HashInstanceKey { + TADDR addr; + DAC_INSTANCE* instance; + } HashInstanceKey; + + typedef struct _HashInstanceKeyBlock { + // Blocks are chained in reverse order of allocation so that the most recently allocated + // block is searched first. + _HashInstanceKeyBlock* next; + + // Entries to a block are added from the max index on down so that recently added + // entries are at the start of the block. + DWORD firstElement; + HashInstanceKey instanceKeys[] ; + } HashInstanceKeyBlock; + +// The hashing function does a good job of distributing the entries across buckets. To handle a +// SO on x86, we have under 250 entries in a bucket. A 4K block size allows 511 entries on x86 and +// about half that on x64. On x64, the number of entries added to the hash table is significantly +// smaller than on x86 (and the max recursion depth for default stack sizes is also far less), so +// 4K is generally adequate. + +#define HASH_INSTANCE_BLOCK_ALLOC_SIZE (4 * 1024) +#define HASH_INSTANCE_BLOCK_NUM_ELEMENTS ((HASH_INSTANCE_BLOCK_ALLOC_SIZE - offsetof(_HashInstanceKeyBlock, instanceKeys))/sizeof(HashInstanceKey)) +#endif // #if defined(DAC_HASHTABLE) + + DAC_INSTANCE_BLOCK* m_blocks; + DAC_INSTANCE_BLOCK* m_unusedBlock; + ULONG64 m_blockMemUsage; + ULONG32 m_numInst; + ULONG64 m_instMemUsage; + +#if defined(DAC_HASHTABLE) + HashInstanceKeyBlock* m_hash[DAC_INSTANCE_HASH_SIZE]; +#else //DAC_HASHTABLE + + // We're going to use the STL unordered_map for our instance hash. + // This has the benefit of scaling to different workloads appropriately (as opposed to having a + // fixed number of buckets). + + class DacHashCompare : public std::hash_compare<TADDR> + { + public: + // Custom hash function + // The default hash function uses a pseudo-randomizing function to get a random + // distribution. In our case, we'd actually like a more even distribution to get + // better access locality (see comments for DAC_INSTANCE_HASH_BITS). + // + // Also, when enumerating the hash during dump generation, clustering nearby addresses + // together can have a significant positive impact on the performance of the minidump + // library (at least the un-optimized version 6.5 linked into our dw20.exe - on Vista+ + // we use the OS's version 6.7+ with radically improved perf characteristics). Having + // a random distribution is actually the worst-case because it means most blocks won't + // be merged until near the end, and a large number of intermediate blocks will have to + // be searched with each call. + // + // The default pseudo-randomizing function also requires a call to ldiv which shows up as + // a 3%-5% perf hit in most perf-sensitive scenarios, so this should also always be + // faster. + inline size_t operator()(const TADDR& keyval) const + { + return (unsigned)(keyval >>DAC_INSTANCE_HASH_SHIFT); + } + + // Explicitly bring in the two-argument comparison function from the base class (just less-than) + // This is necessary because once we override one form of operator() above, we don't automatically + // get the others by C++ inheritance rules. + using std::hash_compare<TADDR>::operator(); + +#ifdef NIDUMP_CUSTOMIZED_DAC_HASH // not set + //this particular number is supposed to be roughly the same amount of + //memory as the old code (buckets * number of entries in the old + //blocks.) + //disabled for now. May tweak implementation later. It turns out that + //having a large number of initial buckets is excellent for nidump, but it + // is terrible for most other scenarios due to the cost of clearing them at + // every Flush. Once there is a better perf suite, we can tweak these values more. + static const size_t min_buckets = DAC_INSTANCE_HASH_SIZE * 256; +#endif + + }; + typedef std::unordered_map<TADDR, DAC_INSTANCE*, DacHashCompare > DacInstanceHash; + typedef DacInstanceHash::value_type DacInstanceHashValue; + typedef DacInstanceHash::iterator DacInstanceHashIterator; + DacInstanceHash m_hash; +#endif //DAC_HASHTABLE + + DAC_INSTANCE* m_superseded; + DAC_INSTANCE_PUSH* m_instPushed; +}; + + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + +class DacStreamManager; + +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + +//---------------------------------------------------------------------------- +// +// ClrDataAccess. +// +//---------------------------------------------------------------------------- + +class ClrDataAccess + : public IXCLRDataProcess2, + public ICLRDataEnumMemoryRegions, + public ISOSDacInterface, + public ISOSDacInterface2, + public ISOSDacInterface3, + public ISOSDacInterface4 +{ +public: + ClrDataAccess(ICorDebugDataTarget * pTarget, ICLRDataTarget * pLegacyTarget=0); + virtual ~ClrDataAccess(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataProcess. + // + + virtual HRESULT STDMETHODCALLTYPE Flush( void); + + virtual HRESULT STDMETHODCALLTYPE StartEnumTasks( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumTask( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataTask **task); + + virtual HRESULT STDMETHODCALLTYPE EndEnumTasks( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetTaskByOSThreadID( + /* [in] */ ULONG32 OSThreadID, + /* [out] */ IXCLRDataTask **task); + + virtual HRESULT STDMETHODCALLTYPE GetTaskByUniqueID( + /* [in] */ ULONG64 uniqueID, + /* [out] */ IXCLRDataTask **task); + + virtual HRESULT STDMETHODCALLTYPE GetFlags( + /* [out] */ ULONG32 *flags); + + virtual HRESULT STDMETHODCALLTYPE IsSameObject( + /* [in] */ IXCLRDataProcess *process); + + virtual HRESULT STDMETHODCALLTYPE GetManagedObject( + /* [out] */ IXCLRDataValue **value); + + virtual HRESULT STDMETHODCALLTYPE GetDesiredExecutionState( + /* [out] */ ULONG32 *state); + + virtual HRESULT STDMETHODCALLTYPE SetDesiredExecutionState( + /* [in] */ ULONG32 state); + + virtual HRESULT STDMETHODCALLTYPE GetAddressType( + /* [in] */ CLRDATA_ADDRESS address, + /* [out] */ CLRDataAddressType* type); + + virtual HRESULT STDMETHODCALLTYPE GetRuntimeNameByAddress( + /* [in] */ CLRDATA_ADDRESS address, + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR nameBuf[ ], + /* [out] */ CLRDATA_ADDRESS* displacement); + + virtual HRESULT STDMETHODCALLTYPE StartEnumAppDomains( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumAppDomain( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataAppDomain **appDomain); + + virtual HRESULT STDMETHODCALLTYPE EndEnumAppDomains( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetAppDomainByUniqueID( + /* [in] */ ULONG64 uniqueID, + /* [out] */ IXCLRDataAppDomain **appDomain); + + virtual HRESULT STDMETHODCALLTYPE StartEnumAssemblies( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumAssembly( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataAssembly **assembly); + + virtual HRESULT STDMETHODCALLTYPE EndEnumAssemblies( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumModules( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumModule( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataModule **mod); + + virtual HRESULT STDMETHODCALLTYPE EndEnumModules( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetModuleByAddress( + /* [in] */ CLRDATA_ADDRESS address, + /* [out] */ IXCLRDataModule** mod); + + virtual HRESULT STDMETHODCALLTYPE StartEnumMethodDefinitionsByAddress( + /* [in] */ CLRDATA_ADDRESS address, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumMethodDefinitionByAddress( + /* [in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodDefinition **method); + + virtual HRESULT STDMETHODCALLTYPE EndEnumMethodDefinitionsByAddress( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumMethodInstancesByAddress( + /* [in] */ CLRDATA_ADDRESS address, + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumMethodInstanceByAddress( + /* [in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodInstance **method); + + virtual HRESULT STDMETHODCALLTYPE EndEnumMethodInstancesByAddress( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetDataByAddress( + /* [in] */ CLRDATA_ADDRESS address, + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [in] */ IXCLRDataTask* tlsTask, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataValue **value, + /* [out] */ CLRDATA_ADDRESS *displacement); + + virtual HRESULT STDMETHODCALLTYPE GetExceptionStateByExceptionRecord( + /* [in] */ EXCEPTION_RECORD64 *record, + /* [out] */ IXCLRDataExceptionState **exception); + + virtual HRESULT STDMETHODCALLTYPE TranslateExceptionRecordToNotification( + /* [in] */ EXCEPTION_RECORD64 *record, + /* [in] */ IXCLRDataExceptionNotification *notify); + + virtual HRESULT STDMETHODCALLTYPE CreateMemoryValue( + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [in] */ IXCLRDataTask* tlsTask, + /* [in] */ IXCLRDataTypeInstance* type, + /* [in] */ CLRDATA_ADDRESS addr, + /* [out] */ IXCLRDataValue** value); + + virtual HRESULT STDMETHODCALLTYPE SetAllTypeNotifications( + /* [in] */ IXCLRDataModule* mod, + /* [in] */ ULONG32 flags); + + virtual HRESULT STDMETHODCALLTYPE SetAllCodeNotifications( + /* [in] */ IXCLRDataModule* mod, + /* [in] */ ULONG32 flags); + + virtual HRESULT STDMETHODCALLTYPE GetTypeNotifications( + /* [in] */ ULONG32 numTokens, + /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[], + /* [in] */ IXCLRDataModule* singleMod, + /* [in, size_is(numTokens)] */ mdTypeDef tokens[], + /* [out, size_is(numTokens)] */ ULONG32 flags[]); + + virtual HRESULT STDMETHODCALLTYPE SetTypeNotifications( + /* [in] */ ULONG32 numTokens, + /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[], + /* [in] */ IXCLRDataModule* singleMod, + /* [in, size_is(numTokens)] */ mdTypeDef tokens[], + /* [in, size_is(numTokens)] */ ULONG32 flags[], + /* [in] */ ULONG32 singleFlags); + + virtual HRESULT STDMETHODCALLTYPE GetCodeNotifications( + /* [in] */ ULONG32 numTokens, + /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[], + /* [in] */ IXCLRDataModule* singleMod, + /* [in, size_is(numTokens)] */ mdMethodDef tokens[], + /* [out, size_is(numTokens)] */ ULONG32 flags[]); + + virtual HRESULT STDMETHODCALLTYPE SetCodeNotifications( + /* [in] */ ULONG32 numTokens, + /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[], + /* [in] */ IXCLRDataModule* singleMod, + /* [in, size_is(numTokens)] */ mdMethodDef tokens[], + /* [in, size_is(numTokens)] */ ULONG32 flags[], + /* [in] */ ULONG32 singleFlags); + + virtual HRESULT STDMETHODCALLTYPE GetOtherNotificationFlags( + /* [out] */ ULONG32* flags); + + virtual HRESULT STDMETHODCALLTYPE SetOtherNotificationFlags( + /* [in] */ ULONG32 flags); + + virtual HRESULT STDMETHODCALLTYPE FollowStub( + /* [in] */ ULONG32 inFlags, + /* [in] */ CLRDATA_ADDRESS inAddr, + /* [in] */ CLRDATA_FOLLOW_STUB_BUFFER* inBuffer, + /* [out] */ CLRDATA_ADDRESS* outAddr, + /* [out] */ CLRDATA_FOLLOW_STUB_BUFFER* outBuffer, + /* [out] */ ULONG32* outFlags); + + virtual HRESULT STDMETHODCALLTYPE FollowStub2( + /* [in] */ IXCLRDataTask* task, + /* [in] */ ULONG32 inFlags, + /* [in] */ CLRDATA_ADDRESS inAddr, + /* [in] */ CLRDATA_FOLLOW_STUB_BUFFER* inBuffer, + /* [out] */ CLRDATA_ADDRESS* outAddr, + /* [out] */ CLRDATA_FOLLOW_STUB_BUFFER* outBuffer, + /* [out] */ ULONG32* outFlags); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + + + // + // IXCLRDataProcess2. + // + STDMETHOD(GetGcNotification)(/* [out] */ GcEvtArgs* gcEvtArgs); + STDMETHOD(SetGcNotification)(/* [in] */ GcEvtArgs gcEvtArgs); + + // + // ICLRDataEnumMemoryRegions. + // + virtual HRESULT STDMETHODCALLTYPE EnumMemoryRegions( + /* [in] */ ICLRDataEnumMemoryRegionsCallback *callback, + /* [in] */ ULONG32 miniDumpFlags, + /* [in] */ CLRDataEnumMemoryFlags clrFlags); + + + // ISOSDacInterface + virtual HRESULT STDMETHODCALLTYPE GetThreadStoreData(struct DacpThreadStoreData *data); + virtual HRESULT STDMETHODCALLTYPE GetAppDomainStoreData(struct DacpAppDomainStoreData *data); + virtual HRESULT STDMETHODCALLTYPE GetAppDomainList(unsigned int count, CLRDATA_ADDRESS values[], unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetAppDomainData(CLRDATA_ADDRESS addr, struct DacpAppDomainData *data); + virtual HRESULT STDMETHODCALLTYPE GetAppDomainName(CLRDATA_ADDRESS addr, unsigned int count, __out_z __inout_ecount(count) wchar_t *name, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetAssemblyList(CLRDATA_ADDRESS appDomain, int count, CLRDATA_ADDRESS values[], int *fetched); + virtual HRESULT STDMETHODCALLTYPE GetAssemblyData(CLRDATA_ADDRESS baseDomainPtr, CLRDATA_ADDRESS assembly, struct DacpAssemblyData *data); + virtual HRESULT STDMETHODCALLTYPE GetAssemblyName(CLRDATA_ADDRESS assembly, unsigned int count, __out_z __inout_ecount(count) wchar_t *name, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetThreadData(CLRDATA_ADDRESS thread, struct DacpThreadData *data); + virtual HRESULT STDMETHODCALLTYPE GetThreadFromThinlockID(UINT thinLockId, CLRDATA_ADDRESS *pThread); + virtual HRESULT STDMETHODCALLTYPE GetStackLimits(CLRDATA_ADDRESS threadPtr, CLRDATA_ADDRESS *lower, CLRDATA_ADDRESS *upper, CLRDATA_ADDRESS *fp); + virtual HRESULT STDMETHODCALLTYPE GetDomainFromContext(CLRDATA_ADDRESS context, CLRDATA_ADDRESS *domain); + + virtual HRESULT STDMETHODCALLTYPE GetMethodDescData(CLRDATA_ADDRESS methodDesc, CLRDATA_ADDRESS ip, struct DacpMethodDescData *data, ULONG cRevertedRejitVersions, DacpReJitData * rgRevertedRejitData, ULONG * pcNeededRevertedRejitData); + virtual HRESULT STDMETHODCALLTYPE GetMethodDescPtrFromIP(CLRDATA_ADDRESS ip, CLRDATA_ADDRESS * ppMD); + virtual HRESULT STDMETHODCALLTYPE GetMethodDescName(CLRDATA_ADDRESS methodDesc, unsigned int count, __out_z __inout_ecount(count) wchar_t *name, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetMethodDescPtrFromFrame(CLRDATA_ADDRESS frameAddr, CLRDATA_ADDRESS * ppMD); + virtual HRESULT STDMETHODCALLTYPE GetCodeHeaderData(CLRDATA_ADDRESS ip, struct DacpCodeHeaderData *data); + virtual HRESULT STDMETHODCALLTYPE GetThreadpoolData(struct DacpThreadpoolData *data); + virtual HRESULT STDMETHODCALLTYPE GetWorkRequestData(CLRDATA_ADDRESS addrWorkRequest, struct DacpWorkRequestData *data); + virtual HRESULT STDMETHODCALLTYPE GetObjectData(CLRDATA_ADDRESS objAddr, struct DacpObjectData *data); + virtual HRESULT STDMETHODCALLTYPE GetObjectStringData(CLRDATA_ADDRESS obj, unsigned int count, __out_z __inout_ecount(count) wchar_t *stringData, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetObjectClassName(CLRDATA_ADDRESS obj, unsigned int count, __out_z __inout_ecount(count) wchar_t *className, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetMethodTableName(CLRDATA_ADDRESS mt, unsigned int count, __out_z __inout_ecount(count) wchar_t *mtName, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetMethodTableData(CLRDATA_ADDRESS mt, struct DacpMethodTableData *data); + virtual HRESULT STDMETHODCALLTYPE GetMethodTableFieldData(CLRDATA_ADDRESS mt, struct DacpMethodTableFieldData *data); + virtual HRESULT STDMETHODCALLTYPE GetMethodTableTransparencyData(CLRDATA_ADDRESS mt, struct DacpMethodTableTransparencyData *data); + virtual HRESULT STDMETHODCALLTYPE GetMethodTableForEEClass(CLRDATA_ADDRESS eeClass, CLRDATA_ADDRESS *value); + virtual HRESULT STDMETHODCALLTYPE GetFieldDescData(CLRDATA_ADDRESS fieldDesc, struct DacpFieldDescData *data); + virtual HRESULT STDMETHODCALLTYPE GetFrameName(CLRDATA_ADDRESS vtable, unsigned int count, __out_z __inout_ecount(count) wchar_t *frameName, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetModule(CLRDATA_ADDRESS addr, IXCLRDataModule **mod); + virtual HRESULT STDMETHODCALLTYPE GetModuleData(CLRDATA_ADDRESS moduleAddr, struct DacpModuleData *data); + virtual HRESULT STDMETHODCALLTYPE TraverseModuleMap(ModuleMapType mmt, CLRDATA_ADDRESS moduleAddr, MODULEMAPTRAVERSE pCallback, LPVOID token); + virtual HRESULT STDMETHODCALLTYPE GetMethodDescFromToken(CLRDATA_ADDRESS moduleAddr, mdToken token, CLRDATA_ADDRESS *methodDesc); + virtual HRESULT STDMETHODCALLTYPE GetPEFileBase(CLRDATA_ADDRESS addr, CLRDATA_ADDRESS *base); + virtual HRESULT STDMETHODCALLTYPE GetPEFileName(CLRDATA_ADDRESS addr, unsigned int count, __out_z __inout_ecount(count) wchar_t *fileName, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetAssemblyModuleList(CLRDATA_ADDRESS assembly, unsigned int count, CLRDATA_ADDRESS modules[], unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetGCHeapData(struct DacpGcHeapData *data); + virtual HRESULT STDMETHODCALLTYPE GetGCHeapList(unsigned int count, CLRDATA_ADDRESS heaps[], unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetGCHeapDetails(CLRDATA_ADDRESS heap, struct DacpGcHeapDetails *details); + virtual HRESULT STDMETHODCALLTYPE GetGCHeapStaticData(struct DacpGcHeapDetails *data); + virtual HRESULT STDMETHODCALLTYPE GetHeapSegmentData(CLRDATA_ADDRESS seg, struct DacpHeapSegmentData *data); + virtual HRESULT STDMETHODCALLTYPE GetDomainLocalModuleData(CLRDATA_ADDRESS addr, struct DacpDomainLocalModuleData *data); + virtual HRESULT STDMETHODCALLTYPE GetDomainLocalModuleDataFromAppDomain(CLRDATA_ADDRESS appDomainAddr, int moduleID, struct DacpDomainLocalModuleData *data); + virtual HRESULT STDMETHODCALLTYPE GetDomainLocalModuleDataFromModule(CLRDATA_ADDRESS moduleAddr, struct DacpDomainLocalModuleData *data); + virtual HRESULT STDMETHODCALLTYPE GetSyncBlockData(unsigned int number, struct DacpSyncBlockData *data); + virtual HRESULT STDMETHODCALLTYPE GetSyncBlockCleanupData(CLRDATA_ADDRESS addr, struct DacpSyncBlockCleanupData *data); + virtual HRESULT STDMETHODCALLTYPE TraverseRCWCleanupList(CLRDATA_ADDRESS cleanupListPtr, VISITRCWFORCLEANUP pCallback, LPVOID token); + virtual HRESULT STDMETHODCALLTYPE TraverseEHInfo(CLRDATA_ADDRESS ip, DUMPEHINFO pCallback, LPVOID token); + virtual HRESULT STDMETHODCALLTYPE GetStressLogAddress(CLRDATA_ADDRESS *stressLog); + virtual HRESULT STDMETHODCALLTYPE GetJitManagerList(unsigned int count, struct DacpJitManagerInfo managers[], unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetJitHelperFunctionName(CLRDATA_ADDRESS ip, unsigned int count, __out_z __inout_ecount(count) char *name, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetJumpThunkTarget(T_CONTEXT *ctx, CLRDATA_ADDRESS *targetIP, CLRDATA_ADDRESS *targetMD); + virtual HRESULT STDMETHODCALLTYPE TraverseLoaderHeap(CLRDATA_ADDRESS loaderHeapAddr, VISITHEAP pCallback); + virtual HRESULT STDMETHODCALLTYPE GetCodeHeapList(CLRDATA_ADDRESS jitManager, unsigned int count, struct DacpJitCodeHeapInfo codeHeaps[], unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetMethodTableSlot(CLRDATA_ADDRESS mt, unsigned int slot, CLRDATA_ADDRESS *value); + virtual HRESULT STDMETHODCALLTYPE TraverseVirtCallStubHeap(CLRDATA_ADDRESS pAppDomain, VCSHeapType heaptype, VISITHEAP pCallback); + virtual HRESULT STDMETHODCALLTYPE GetNestedExceptionData(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS *exceptionObject, CLRDATA_ADDRESS *nextNestedException); + virtual HRESULT STDMETHODCALLTYPE GetUsefulGlobals(struct DacpUsefulGlobalsData *data); + virtual HRESULT STDMETHODCALLTYPE GetILForModule(CLRDATA_ADDRESS moduleAddr, DWORD rva, CLRDATA_ADDRESS *il); + virtual HRESULT STDMETHODCALLTYPE GetClrWatsonBuckets(CLRDATA_ADDRESS thread, void *pGenericModeBlock); + virtual HRESULT STDMETHODCALLTYPE GetOOMData(CLRDATA_ADDRESS oomAddr, struct DacpOomData *data); + virtual HRESULT STDMETHODCALLTYPE GetOOMStaticData(struct DacpOomData *data); + virtual HRESULT STDMETHODCALLTYPE GetHeapAnalyzeData(CLRDATA_ADDRESS addr,struct DacpGcHeapAnalyzeData *data); + virtual HRESULT STDMETHODCALLTYPE GetHeapAnalyzeStaticData(struct DacpGcHeapAnalyzeData *data); + virtual HRESULT STDMETHODCALLTYPE GetMethodDescTransparencyData(CLRDATA_ADDRESS methodDesc, struct DacpMethodDescTransparencyData *data); + virtual HRESULT STDMETHODCALLTYPE GetHillClimbingLogEntry(CLRDATA_ADDRESS addr, struct DacpHillClimbingLogEntry *data); + virtual HRESULT STDMETHODCALLTYPE GetThreadLocalModuleData(CLRDATA_ADDRESS thread, unsigned int index, struct DacpThreadLocalModuleData *data); + virtual HRESULT STDMETHODCALLTYPE GetRCWData(CLRDATA_ADDRESS addr, struct DacpRCWData *data); + virtual HRESULT STDMETHODCALLTYPE GetRCWInterfaces(CLRDATA_ADDRESS rcw, unsigned int count, struct DacpCOMInterfacePointerData interfaces[], unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetCCWData(CLRDATA_ADDRESS ccw, struct DacpCCWData *data); + virtual HRESULT STDMETHODCALLTYPE GetCCWInterfaces(CLRDATA_ADDRESS ccw, unsigned int count, struct DacpCOMInterfacePointerData interfaces[], unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetTLSIndex(ULONG *pIndex); + virtual HRESULT STDMETHODCALLTYPE GetDacModuleHandle(HMODULE *phModule); + + virtual HRESULT STDMETHODCALLTYPE GetFailedAssemblyList(CLRDATA_ADDRESS appDomain, int count, CLRDATA_ADDRESS values[], unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetPrivateBinPaths(CLRDATA_ADDRESS appDomain, int count, __out_z __inout_ecount(count) wchar_t *paths, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetAssemblyLocation(CLRDATA_ADDRESS assembly, int count, __out_z __inout_ecount(count) wchar_t *location, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetAppDomainConfigFile(CLRDATA_ADDRESS appDomain, int count, __out_z __inout_ecount(count) wchar_t *configFile, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetApplicationBase(CLRDATA_ADDRESS appDomain, int count, __out_z __inout_ecount(count) wchar_t *base, unsigned int *pNeeded); + + virtual HRESULT STDMETHODCALLTYPE GetFailedAssemblyData(CLRDATA_ADDRESS assembly, unsigned int *pContext, HRESULT *pResult); + virtual HRESULT STDMETHODCALLTYPE GetFailedAssemblyLocation(CLRDATA_ADDRESS assembly, unsigned int count, __out_z __inout_ecount(count) wchar_t *location, unsigned int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetFailedAssemblyDisplayName(CLRDATA_ADDRESS assembly, unsigned int count, __out_z __inout_ecount(count) wchar_t *name, unsigned int *pNeeded); + + virtual HRESULT STDMETHODCALLTYPE GetStackReferences(DWORD osThreadID, ISOSStackRefEnum **ppEnum); + virtual HRESULT STDMETHODCALLTYPE GetRegisterName(int regNum, unsigned int count, __out_z __inout_ecount(count) wchar_t *buffer, unsigned int *pNeeded); + + virtual HRESULT STDMETHODCALLTYPE GetHandleEnum(ISOSHandleEnum **ppHandleEnum); + virtual HRESULT STDMETHODCALLTYPE GetHandleEnumForTypes(unsigned int types[], unsigned int count, ISOSHandleEnum **ppHandleEnum); + virtual HRESULT STDMETHODCALLTYPE GetHandleEnumForGC(unsigned int gen, ISOSHandleEnum **ppHandleEnum); + + virtual HRESULT STDMETHODCALLTYPE GetThreadAllocData(CLRDATA_ADDRESS thread, struct DacpAllocData *data); + virtual HRESULT STDMETHODCALLTYPE GetHeapAllocData(unsigned int count, struct DacpGenerationAllocData *data, unsigned int *pNeeded); + + // ISOSDacInterface2 + virtual HRESULT STDMETHODCALLTYPE GetObjectExceptionData(CLRDATA_ADDRESS objAddr, struct DacpExceptionObjectData *data); + virtual HRESULT STDMETHODCALLTYPE IsRCWDCOMProxy(CLRDATA_ADDRESS rcwAddr, BOOL* isDCOMProxy); + + // ISOSDacInterface3 + virtual HRESULT STDMETHODCALLTYPE GetGCInterestingInfoData(CLRDATA_ADDRESS interestingInfoAddr, struct DacpGCInterestingInfoData *data); + virtual HRESULT STDMETHODCALLTYPE GetGCInterestingInfoStaticData(struct DacpGCInterestingInfoData *data); + virtual HRESULT STDMETHODCALLTYPE GetGCGlobalMechanisms(size_t* globalMechanisms); + + // ISOSDacInterface4 + virtual HRESULT STDMETHODCALLTYPE GetClrNotification(CLRDATA_ADDRESS arguments[], int count, int *pNeeded); + + // + // ClrDataAccess. + // + + HRESULT Initialize(void); + + BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord); +#ifndef FEATURE_PAL + HRESULT GetWatsonBuckets(DWORD dwThreadId, GenericModeBlock * pGM); +#endif // FEATURE_PAL + + + Thread* FindClrThreadByTaskId(ULONG64 taskId); + HRESULT IsPossibleCodeAddress(IN TADDR address); + + PCSTR GetJitHelperName(IN TADDR address, + IN bool dynamicHelpersOnly = false); + HRESULT GetFullMethodName(IN MethodDesc* methodDesc, + IN ULONG32 symbolChars, + IN ULONG32* symbolLen, + __out_ecount_part_opt(symbolChars, *symbolLen) LPWSTR symbol); + HRESULT RawGetMethodName(/* [in] */ CLRDATA_ADDRESS address, + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR nameBuf[ ], + /* [out] */ CLRDATA_ADDRESS* displacement); + + HRESULT FollowStubStep( + /* [in] */ Thread* thread, + /* [in] */ ULONG32 inFlags, + /* [in] */ TADDR inAddr, + /* [in] */ union STUB_BUF* inBuffer, + /* [out] */ TADDR* outAddr, + /* [out] */ union STUB_BUF* outBuffer, + /* [out] */ ULONG32* outFlags); + + DebuggerJitInfo* GetDebuggerJitInfo(MethodDesc* methodDesc, + TADDR addr) + { + if (g_pDebugger) + { + return g_pDebugger->GetJitInfo(methodDesc, (PBYTE)addr, NULL); + } + + return NULL; + } + + HRESULT GetMethodExtents(MethodDesc* methodDesc, + METH_EXTENTS** extents); + HRESULT GetMethodVarInfo(MethodDesc* methodDesc, + TADDR address, + ULONG32* numVarInfo, + ICorDebugInfo::NativeVarInfo** varInfo, + ULONG32* codeOffset); + + // If the method has multiple copies of code (because of EnC or code-pitching), + // this returns the info corresponding to address. + // If 'address' and 'codeOffset' are both non-NULL, *codeOffset gets set to + // the offset of 'address' from the start of the method. + HRESULT GetMethodNativeMap(MethodDesc* methodDesc, + TADDR address, + ULONG32* numMap, + DebuggerILToNativeMap** map, + bool* mapAllocated, + CLRDATA_ADDRESS* codeStart, + ULONG32* codeOffset); + + // Get the MethodDesc for a function + MethodDesc * FindLoadedMethodRefOrDef(Module* pModule, mdToken memberRef); + +#ifndef FEATURE_PAL + HRESULT GetClrWatsonBucketsWorker(Thread * pThread, GenericModeBlock * pGM); +#endif // FEATURE_PAL + + HRESULT ServerGCHeapDetails(CLRDATA_ADDRESS heapAddr, + DacpGcHeapDetails *detailsData); + HRESULT GetServerAllocData(unsigned int count, struct DacpGenerationAllocData *data, unsigned int *pNeeded); + HRESULT ServerOomData(CLRDATA_ADDRESS addr, DacpOomData *oomData); + HRESULT ServerGCInterestingInfoData(CLRDATA_ADDRESS addr, DacpGCInterestingInfoData *interestingInfoData); + HRESULT ServerGCHeapAnalyzeData(CLRDATA_ADDRESS heapAddr, + DacpGcHeapAnalyzeData *analyzeData); + + // + // Memory enumeration. + // + + HRESULT EnumMemoryRegionsWrapper(CLRDataEnumMemoryFlags flags); + + // skinny minidump functions + HRESULT EnumMemoryRegionsWorkerSkinny(CLRDataEnumMemoryFlags flags); + // triage minidump functions + HRESULT EnumMemoryRegionsWorkerMicroTriage(CLRDataEnumMemoryFlags flags); + HRESULT EnumMemoryRegionsWorkerHeap(CLRDataEnumMemoryFlags flags); + + HRESULT EnumMemWalkStackHelper(CLRDataEnumMemoryFlags flags, IXCLRDataStackWalk *pStackWalk, Thread * pThread); + HRESULT DumpManagedObject(CLRDataEnumMemoryFlags flags, OBJECTREF objRef); + HRESULT DumpManagedExcepObject(CLRDataEnumMemoryFlags flags, OBJECTREF objRef); + HRESULT DumpManagedStackTraceStringObject(CLRDataEnumMemoryFlags flags, STRINGREF orefStackTrace); +#ifdef FEATURE_COMINTEROP + HRESULT DumpStowedExceptionObject(CLRDataEnumMemoryFlags flags, CLRDATA_ADDRESS ccwPtr); + HRESULT EnumMemStowedException(CLRDataEnumMemoryFlags flags); +#endif + + HRESULT EnumMemWriteDataSegment(); + + // Custom Dump + HRESULT EnumMemoryRegionsWorkerCustom(); + + // helper function for dump code + void EnumWksGlobalMemoryRegions(CLRDataEnumMemoryFlags flags); + void EnumSvrGlobalMemoryRegions(CLRDataEnumMemoryFlags flags); + + HRESULT EnumMemCollectImages(); + HRESULT EnumMemCLRStatic(CLRDataEnumMemoryFlags flags); + HRESULT EnumMemCLRHeapCrticalStatic(CLRDataEnumMemoryFlags flags); + HRESULT EnumMemDumpModuleList(CLRDataEnumMemoryFlags flags); + HRESULT EnumMemDumpAppDomainInfo(CLRDataEnumMemoryFlags flags); + HRESULT EnumMemDumpAllThreadsStack(CLRDataEnumMemoryFlags flags); + HRESULT EnumMemCLRMainModuleInfo(); + + bool ReportMem(TADDR addr, TSIZE_T size, bool fExpectSuccess = true); + bool DacUpdateMemoryRegion(TADDR addr, TSIZE_T bufferSize, BYTE* buffer); + + void ClearDumpStats(); + JITNotification* GetHostJitNotificationTable(); + GcNotification* GetHostGcNotificationTable(); + + void* GetMetaDataFromHost(PEFile* peFile, + bool* isAlternate); + + virtual + interface IMDInternalImport* GetMDImport(const PEFile* peFile, + const ReflectionModule* reflectionModule, + bool throwEx); + + interface IMDInternalImport* GetMDImport(const PEFile* peFile, + bool throwEx) + { + return GetMDImport(peFile, NULL, throwEx); + } + + interface IMDInternalImport* GetMDImport(const ReflectionModule* reflectionModule, + bool throwEx) + { + return GetMDImport(NULL, reflectionModule, throwEx); + } + + //ClrDump support + HRESULT STDMETHODCALLTYPE DumpNativeImage(CLRDATA_ADDRESS loadedBase, + LPCWSTR name, + IXCLRDataDisplay *display, + IXCLRLibrarySupport *support, + IXCLRDisassemblySupport *dis); + + // Set whether inconsistencies in the target should raise asserts. + void SetTargetConsistencyChecks(bool fEnableAsserts); + + // Get whether inconsistencies in the target should raise asserts. + bool TargetConsistencyAssertsEnabled(); + + // Get the ICLRDataTarget2 instance, if any + ICLRDataTarget2 * GetLegacyTarget2() { return m_pLegacyTarget2; } + + // Get the ICLRDataTarget3 instance, if any + ICLRDataTarget3 * GetLegacyTarget3() { return m_pLegacyTarget3; } + + // + // Public Fields + // Note that it would be nice if all of these were made private. However, the visibility + // model of the DAC implementation is that the public surface area is declared in daccess.h + // (implemented in dacfn.cpp), and the private surface area (like ClrDataAccess) is declared + // in dacimpl.h which is only included by the DAC infrastructure. Therefore the DAC + // infrastructure agressively uses these fields, and we don't get a huge amount of benefit from + // reworking this model (since there is some form of encapsulation in place already). + // + + // The underlying data target - always present (strong reference) + ICorDebugDataTarget * m_pTarget; + + // Mutable version of the data target if any - optional (strong reference) + ICorDebugMutableDataTarget * m_pMutableTarget; + + TADDR m_globalBase; + DacInstanceManager m_instances; + ULONG32 m_instanceAge; + bool m_debugMode; + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + +protected: + DacStreamManager * m_streams; + +public: + // Used to mark the point after which enumerated EE structs of interest + // will get their names cached in the triage/mini-dump + void InitStreamsForWriting(IN CLRDataEnumMemoryFlags flags); + + // Used during triage/mini-dump collection to populate the map of + // pointers to EE struct (MethodDesc* for now) to their corresponding + // name. + bool MdCacheAddEEName(TADDR taEEStruct, const SString& name); + + // Used to mark the end point for the name caching. Will update streams + // based on built caches + void EnumStreams(IN CLRDataEnumMemoryFlags flags); + + // Used during triage/mini-dump analysis to retrieve the name associated + // with an EE struct pointer (MethodDesc* for now). + bool MdCacheGetEEName(TADDR taEEStruct, SString & eeName); + +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + +private: + // Read the DAC table and initialize g_dacGlobals + HRESULT GetDacGlobals(); + + // Verify the target mscorwks.dll matches the version expected + HRESULT VerifyDlls(); + + // Check whether a region of memory is fully readable. + bool IsFullyReadable(TADDR addr, TSIZE_T size); + + // Legacy target interfaces - optional + ICLRDataTarget * m_pLegacyTarget; + ICLRDataTarget2 * m_pLegacyTarget2; + ICLRDataTarget3 * m_pLegacyTarget3; + IXCLRDataTarget3 * m_target3; + ICLRMetadataLocator * m_legacyMetaDataLocator; + + LONG m_refs; + HRESULT m_memStatus; + MDImportsCache m_mdImports; + ICLRDataEnumMemoryRegionsCallback* m_enumMemCb; + ICLRDataEnumMemoryRegionsCallback2* m_updateMemCb; + CLRDataEnumMemoryFlags m_enumMemFlags; + JITNotification* m_jitNotificationTable; + GcNotification* m_gcNotificationTable; + TSIZE_T m_cbMemoryReported; + DumpMemoryReportStatics m_dumpStats; + + // If true, inconsistencies in the target will cause ASSERTs to be raised in DEBUG builds + bool m_fEnableTargetConsistencyAsserts; + +#ifdef _DEBUG +protected: + // If true, a mscorwks/mscordacwks mismatch will trigger a nice assert dialog + bool m_fEnableDllVerificationAsserts; +private: +#endif + +#ifdef FEATURE_COMINTEROP +protected: + // Returns CCW pointer based on a target address. + PTR_ComCallWrapper DACGetCCWFromAddress(CLRDATA_ADDRESS addr); + +private: + // Returns COM interface pointer corresponding to a given CCW and internal vtable + // index. Returns NULL if the vtable is unused or not fully laid out. + PTR_IUnknown DACGetCOMIPFromCCW(PTR_ComCallWrapper pCCW, int vtableIndex); +#endif + + static LONG s_procInit; + +public: + // APIs for picking up the info needed for a debugger to look up an ngen image or IL image + // from it's search path. + static bool GetMetaDataFileInfoFromPEFile(PEFile *pPEFile, + DWORD &dwImageTimestamp, + DWORD &dwImageSize, + DWORD &dwDataSize, + DWORD &dwRvaHint, + bool &isNGEN, + __out_ecount(cchFilePath) LPWSTR wszFilePath, + DWORD cchFilePath); + + static bool GetILImageInfoFromNgenPEFile(PEFile *peFile, + DWORD &dwTimeStamp, + DWORD &dwSize, + __out_ecount(cchPath) LPWSTR wszPath, + const DWORD cchPath); +#if defined(FEATURE_CORESYSTEM) + static bool GetILImageNameFromNgenImage(LPCWSTR ilExtension, + __out_ecount(cchFilePath) LPWSTR wszFilePath, + const DWORD cchFilePath); +#endif // FEATURE_CORESYSTEM +}; + +extern ClrDataAccess* g_dacImpl; + +/* DacHandleWalker. + * + * Iterates over the handle table, enumerating all handles of the requested type on the + * handle table. This also will report the handle type, whether the handle is a strong + * reference, the AppDomain the handle comes from, as well as the reference count (in + * the case of a RefCount handle). Optionally this class can also be used to filter + * based on GC generation that would be collected (that is, to emulate a GC scan of the + * handle table). + * + * General implementation details: + * We have four sets of variables: + * 1. Overhead variables needed to operate in the Dac. + * 2. Variables needed to walk the handle table. We walk the handle table one bucket + * at a time, filling the array the user gave us until we have either enumerated + * all handles, or filled the array. + * 3. Storage variables to hold the overflow. That is, we were walking the handle + * table, filled the array that the user gave us, then needed to store the extra + * handles the handle table continued to enumerate to us. This is implmeneted + * as a linked list of arrays (mHead, mHead.Next, etc). + * 4. Variables which store the location of where we are in the overflow data. + * + * Note that "mHead" is a HandleChunkHead where we stuff the user's array. Everything + * which follows mHead (mHead.Next, etc) is a HandleChunk containing overflow data. + * + * Lastly, note this does not do robust error handling. If we fail to allocate a + * HandleChunk while walking the handle table, we will miss handles and not report + * this to the user. Unfortunately this will have to be fixed in the next iteration + * when we add more robust error handling to SOS's interface. + */ + template <class T> +class DefaultCOMImpl : public T +{ +public: + DefaultCOMImpl() + : mRef(0) + { + } + + virtual ~DefaultCOMImpl() {} + + ULONG STDMETHODCALLTYPE AddRef() + { + return ++mRef; + } + + ULONG STDMETHODCALLTYPE Release() + { + ULONG res = mRef--; + if (res == 0) + delete this; + return res; + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppObj) + { + if (ppObj == NULL) + return E_INVALIDARG; + + if (IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppObj = static_cast<IUnknown*>(this); + return S_OK; + } + else if (IsEqualIID(riid, __uuidof(T))) + { + AddRef(); + *ppObj = static_cast<T*>(this); + return S_OK; + } + + *ppObj = NULL; + return E_NOINTERFACE; + } + +private: + ULONG mRef; +}; + + +// A stuct representing a thread's allocation context. +struct AllocInfo +{ + CORDB_ADDRESS Ptr; + CORDB_ADDRESS Limit; + + AllocInfo() + : Ptr(0), Limit(0) + { + } +}; + +// A struct representing a segment in the heap. +struct SegmentData +{ + CORDB_ADDRESS Start; + CORDB_ADDRESS End; + + // Whether this segment is part of the large object heap. + int Generation; + + SegmentData() + : Start(0), End(0), Generation(0) + { + } +}; + +// A struct representing a gc heap in the process. +struct HeapData +{ + CORDB_ADDRESS YoungestGenPtr; + CORDB_ADDRESS YoungestGenLimit; + + CORDB_ADDRESS Gen0Start; + CORDB_ADDRESS Gen0End; + + CORDB_ADDRESS Gen1Start; + size_t EphemeralSegment; + + size_t SegmentCount; + SegmentData *Segments; + + HeapData(); + ~HeapData(); +}; + +/* This cache is used to read data from the target process if the reads are known + * to be sequential. This will object will read one page of memory out of the + * process at a time, aligned to the page boundary, to + */ +class LinearReadCache +{ +public: + LinearReadCache(); + ~LinearReadCache(); + + /* Reads an address out of the target process, caching the page of memory read. + * Params: + * addr - The address to read out of the target process. + * t - A pointer to the data to stuff it in. We will read sizeof(T) data + * from the process and write it into the location t points to. This + * parameter must be non-null. + * Returns: + * True if the read succeeded. False if it did not, usually as a result + * of the memory simply not being present in the target process. + * Note: + * The state of *t is undefined if this function returns false. We may + * have written partial data to it if we return false, so you must + * absolutely NOT use it if Read returns false. + */ + template <class T> + bool Read(CORDB_ADDRESS addr, T *t) + { + _ASSERTE(t); + + // Unfortunately the ctor can fail the alloc for the byte array. In this case + // we'll just fall back to non-cached reads. + if (mPage == NULL) + return MisalignedRead(addr, t); + + // Is addr on the current page? If not read the page of memory addr is on. + // If this fails, we will fall back to a raw read out of the process (which + // is what MisalignedRead does). + if ((addr < mCurrPageStart) || (addr - mCurrPageStart > mCurrPageSize)) + if (!MoveToPage(addr)) + return MisalignedRead(addr, t); + + // If MoveToPage succeeds, we MUST be on the right page. + _ASSERTE(addr >= mCurrPageStart); + + // However, the amount of data requested may fall off of the page. In that case, + // fall back to MisalignedRead. + CORDB_ADDRESS offset = addr - mCurrPageStart; + if (offset + sizeof(T) > mCurrPageSize) + return MisalignedRead(addr, t); + + // If we reach here we know we are on the right page of memory in the cache, and + // that the read won't fall off of the end of the page. + *t = *reinterpret_cast<T*>(mPage+offset); + return true; + } + + // helper used to read the MethodTable + bool ReadMT(CORDB_ADDRESS addr, TADDR *mt) + { + if (!Read(addr, mt)) + return false; + + // clear the GC flag bits off the MethodTable + // equivalent to Object::GetGCSafeMethodTable() + *mt &= ~3; + return true; + } + +private: + /* Sets the cache to the page specified by addr, or false if we could not move to + * that page. + */ + bool MoveToPage(CORDB_ADDRESS addr); + + /* Attempts to read from the target process if the data is possibly hanging off + * the end of a page. + */ + template<class T> + inline bool MisalignedRead(CORDB_ADDRESS addr, T *t) + { + return SUCCEEDED(DacReadAll(TO_TADDR(addr), t, sizeof(T), false)); + } + +private: + CORDB_ADDRESS mCurrPageStart; + ULONG32 mPageSize, mCurrPageSize; + BYTE *mPage; +}; + +DWORD DacGetNumHeaps(); + +/* The implementation of the dac heap walker. This class will enumerate all objects on + * the heap with three important caveats: + * - This class will skip all Free objects in the heap. Free objects are an + * implementation detail of the GC, and ICorDebug does not have a mechanism + * to expose them. + * - This class does NOT guarantee that all objects will be enumerated. In + * the event that we find heap corruption on a segment, or if the background + * GC is modifying a segment, the remainder of that segment will be skipped + * by design. + * - The GC heap must be in a walkable state before you attempt to use this + * class on it. The IDacDbiInterface::AreGCStructuresValid function will + * tell you whether it is safe to walk the heap or not. + */ +class DacHeapWalker +{ + static CORDB_ADDRESS HeapStart; + static CORDB_ADDRESS HeapEnd; + +public: + DacHeapWalker(); + ~DacHeapWalker(); + + /* Initializes the heap walker. This must be called before Next or + * HasMoreObjects. Returns false if the initialization was not successful. + * (In practice this should only return false if we hit an OOM trying + * to allocate space for data structures.) Limits the heap walk to be in the range + * [start, end] (inclusive). Use DacHeapWalker::HeapStart, DacHeapWalker::HeapEnd + * as start or end to start from the beginning or end. + */ + HRESULT Init(CORDB_ADDRESS start=HeapStart, CORDB_ADDRESS end=HeapEnd); + + /* Returns a CORDB_ADDRESS which points to the next value on the heap. + * You must call HasMoreObjects on this class, and it must return true + * before calling Next. + */ + HRESULT Next(CORDB_ADDRESS *pValue, CORDB_ADDRESS *pMT, ULONG64 *size); + + /* Returns true if there are more objects on the heap, false otherwise. + */ + inline bool HasMoreObjects() const + { + return mCurrHeap < mHeapCount; + } + + HRESULT Reset(CORDB_ADDRESS start, CORDB_ADDRESS end); + + static HRESULT InitHeapDataWks(HeapData *&pHeaps, size_t &count); + static HRESULT InitHeapDataSvr(HeapData *&pHeaps, size_t &count); + + HRESULT GetHeapData(HeapData **ppHeapData, size_t *pNumHeaps); + + SegmentData *FindSegment(CORDB_ADDRESS obj); + + HRESULT ListNearObjects(CORDB_ADDRESS obj, CORDB_ADDRESS *pPrev, CORDB_ADDRESS *pContaining, CORDB_ADDRESS *pNext); + +private: + HRESULT MoveToNextObject(); + + bool GetSize(TADDR tMT, size_t &size); + + inline static size_t Align(size_t size) + { + if (sizeof(TADDR) == 4) + return (size+3) & ~3; + else + return (size+7) & ~7; + } + + inline static size_t AlignLarge(size_t size) + { + return (size + 7) & ~7; + } + + template <class T> + static int GetSegmentCount(T seg_start) + { + int count = 0; + while (seg_start) + { + // If we find this many segments, something is seriously wrong. + if (count++ > 4096) + break; + + seg_start = seg_start->next; + } + + return count; + } + + HRESULT NextSegment(); + void CheckAllocAndSegmentRange(); + +private: + int mThreadCount; + AllocInfo *mAllocInfo; + + size_t mHeapCount; + HeapData *mHeaps; + + CORDB_ADDRESS mCurrObj; + size_t mCurrSize; + TADDR mCurrMT; + + size_t mCurrHeap; + size_t mCurrSeg; + + CORDB_ADDRESS mStart; + CORDB_ADDRESS mEnd; + + LinearReadCache mCache; + static CORDB_ADDRESS sFreeMT; +}; + +struct DacGcReference; +struct SOSStackErrorList +{ + SOSStackRefError error; + SOSStackErrorList *pNext; + + SOSStackErrorList() + : pNext(0) + { + } +}; + +class DacStackReferenceWalker; +class DacStackReferenceErrorEnum : public DefaultCOMImpl<ISOSStackRefErrorEnum> +{ +public: + DacStackReferenceErrorEnum(DacStackReferenceWalker *pEnum, SOSStackErrorList *pErrors); + ~DacStackReferenceErrorEnum(); + + HRESULT STDMETHODCALLTYPE Skip(unsigned int count); + HRESULT STDMETHODCALLTYPE Reset(); + HRESULT STDMETHODCALLTYPE GetCount(unsigned int *pCount); + HRESULT STDMETHODCALLTYPE Next(unsigned int count, SOSStackRefError ref[], unsigned int *pFetched); + +private: + // The lifetime of the error list is tied to the enum, so we must addref/release it. + DacStackReferenceWalker *mEnum; + SOSStackErrorList *mHead; + SOSStackErrorList *mCurr; +}; + +// For GCCONTEXT +#include "gcenv.h" + + /* DacStackReferenceWalker. + */ +class DacStackReferenceWalker : public DefaultCOMImpl<ISOSStackRefEnum> +{ + struct DacScanContext : public ScanContext + { + DacStackReferenceWalker *pWalker; + Frame *pFrame; + TADDR sp, pc; + bool stop; + GCEnumCallback pEnumFunc; + + DacScanContext() + : pWalker(NULL), pFrame(0), sp(0), pc(0), stop(false), pEnumFunc(0) + { + } + }; + + typedef struct _StackRefChunkHead + { + struct _StackRefChunkHead *next; // Next chunk + unsigned int count; // The count of how many StackRefs were written to pData + unsigned int size; // The capacity of pData (in bytes) + void *pData; // The overflow data + + _StackRefChunkHead() + : next(0), count(0), size(0), pData(0) + { + } + } StackRefChunkHead; + + // The actual struct used for storing overflow StackRefs + typedef struct _StackRefChunk : public StackRefChunkHead + { + SOSStackRefData data[64]; + + _StackRefChunk() + { + pData = data; + size = sizeof(data); + } + } StackRefChunk; +public: + DacStackReferenceWalker(ClrDataAccess *dac, DWORD osThreadID); + virtual ~DacStackReferenceWalker(); + + HRESULT Init(); + + HRESULT STDMETHODCALLTYPE Skip(unsigned int count); + HRESULT STDMETHODCALLTYPE Reset(); + HRESULT STDMETHODCALLTYPE GetCount(unsigned int *pCount); + HRESULT STDMETHODCALLTYPE Next(unsigned int count, + SOSStackRefData refs[], + unsigned int *pFetched); + + // Dac-Dbi Functions + HRESULT Next(ULONG celt, DacGcReference roots[], ULONG *pceltFetched); + Thread *GetThread() const + { + return mThread; + } + + HRESULT STDMETHODCALLTYPE EnumerateErrors(ISOSStackRefErrorEnum **ppEnum); + +private: + static StackWalkAction Callback(CrawlFrame *pCF, VOID *pData); + static void GCEnumCallbackSOS(LPVOID hCallback, OBJECTREF *pObject, uint32_t flags, DacSlotLocation loc); + static void GCReportCallbackSOS(PTR_PTR_Object ppObj, ScanContext *sc, uint32_t flags); + static void GCEnumCallbackDac(LPVOID hCallback, OBJECTREF *pObject, uint32_t flags, DacSlotLocation loc); + static void GCReportCallbackDac(PTR_PTR_Object ppObj, ScanContext *sc, uint32_t flags); + + CLRDATA_ADDRESS ReadPointer(TADDR addr); + + template <class StructType> + StructType *GetNextObject(DacScanContext *ctx) + { + SUPPORTS_DAC; + + // If we failed on a previous call (OOM) don't keep trying to allocate, it's not going to work. + if (ctx->stop || !mCurr) + return NULL; + + // We've moved past the size of the current chunk. We'll allocate a new chunk + // and stuff the references there. These are cleaned up by the destructor. + if (mCurr->count >= mCurr->size/sizeof(StructType)) + { + if (mCurr->next == NULL) + { + StackRefChunk *next = new (nothrow) StackRefChunk; + if (next != NULL) + { + mCurr->next = next; + } + else + { + ctx->stop = true; + return NULL; + } + } + + mCurr = mCurr->next; + } + + // Fill the current ref. + StructType *pData = (StructType*)mCurr->pData; + return &pData[mCurr->count++]; + } + + + template <class IntType, class StructType> + IntType WalkStack(IntType count, StructType refs[], promote_func promote, GCEnumCallback enumFunc) + { + _ASSERTE(mThread); + _ASSERTE(!mEnumerated); + + // If this is the first time we were called, fill local data structures. + // This will fill out the user's handles as well. + _ASSERTE(mCurr == NULL); + _ASSERTE(mHead.next == NULL); + + // Get the current thread's context and set that as the filter context + if (mThread->GetFilterContext() == NULL && mThread->GetProfilerFilterContext() == NULL) + { + T_CONTEXT ctx; + mDac->m_pTarget->GetThreadContext(mThread->GetOSThreadId(), CONTEXT_FULL, sizeof(ctx), (BYTE*)&ctx); + mThread->SetProfilerFilterContext(&ctx); + } + + // Setup GCCONTEXT structs for the stackwalk. + GCCONTEXT gcctx; + DacScanContext dsc; + dsc.pWalker = this; + dsc.pEnumFunc = enumFunc; + gcctx.f = promote; + gcctx.sc = &dsc; + + // Put the user's array/count in the + mHead.size = count*sizeof(StructType); + mHead.pData = refs; + mHead.count = 0; + + mCurr = &mHead; + + // Walk the stack, set mEnumerated to true to ensure we don't do it again. + unsigned int flagsStackWalk = ALLOW_INVALID_OBJECTS|ALLOW_ASYNC_STACK_WALK; +#if defined(WIN64EXCEPTIONS) + flagsStackWalk |= GC_FUNCLET_REFERENCE_REPORTING; +#endif // defined(WIN64EXCEPTIONS) + + mEnumerated = true; + mThread->StackWalkFrames(DacStackReferenceWalker::Callback, &gcctx, flagsStackWalk); + + // We have filled the user's array as much as we could. If there's more data than + // could fit, mHead.Next will contain a linked list of refs to enumerate. + mCurr = mHead.next; + + // Return how many we put in the user's array. + return mHead.count; + } + + template <class IntType, class StructType, promote_func PromoteFunc, GCEnumCallback EnumFunc> + HRESULT DoStackWalk(IntType count, StructType stackRefs[], IntType *pFetched) + { + HRESULT hr = S_OK; + IntType fetched = 0; + if (!mEnumerated) + { + // If this is the first time we were called, fill local data structures. + // This will fill out the user's handles as well. + fetched = (IntType)WalkStack((unsigned int)count, stackRefs, PromoteFunc, EnumFunc); + } + + while (fetched < count) + { + if (mCurr == NULL) + { + // Case 1: We have no more refs to walk. + hr = S_FALSE; + break; + } + else if (mChunkIndex >= mCurr->count) + { + // Case 2: We have exhausted the current chunk. + mCurr = mCurr->next; + mChunkIndex = 0; + } + else + { + // Case 3: The last call to "Next" filled the user's array and had some ref + // data leftover. Walk the linked-list of arrays copying them into the user's + // buffer until we have either exhausted the user's array or the leftover data. + IntType toCopy = count - fetched; // Fill the user's buffer... + + // ...unless that would go past the bounds of the current chunk. + if (toCopy + mChunkIndex > mCurr->count) + toCopy = mCurr->count - mChunkIndex; + + memcpy(stackRefs+fetched, (StructType*)mCurr->pData+mChunkIndex, toCopy*sizeof(StructType)); + mChunkIndex += toCopy; + fetched += toCopy; + } + } + + *pFetched = fetched; + + return hr; + } + +private: + // Dac variables required for entering/leaving the dac. + ClrDataAccess *mDac; + ULONG32 m_instanceAge; + + // Operational variables + Thread *mThread; + SOSStackErrorList *mErrors; + bool mEnumerated; + + // Storage variables + StackRefChunkHead mHead; + unsigned int mChunkIndex; + + // Iterator variables + StackRefChunkHead *mCurr; + int mIteratorIndex; + + // Heap. Used to resolve interior pointers. + DacHeapWalker mHeap; +}; + + + +struct DacGcReference; +class DacHandleWalker : public DefaultCOMImpl<ISOSHandleEnum> +{ + typedef struct _HandleChunkHead + { + struct _HandleChunkHead *Next; // Next chunk + unsigned int Count; // The count of how many handles were written to pData + unsigned int Size; // The capacity of pData + void *pData; // The overflow data + + _HandleChunkHead() + : Next(0), Count(0), Size(0), pData(0) + { + } + } HandleChunkHead; + + // The actual struct used for storing overflow handles + typedef struct _HandleChunk : public HandleChunkHead + { + SOSHandleData Data[128]; + + _HandleChunk() + { + pData = Data; + Size = sizeof(Data); + } + } HandleChunk; + + // Parameter used in HndEnumHandles callback. + struct DacHandleWalkerParam + { + HandleChunkHead *Curr; // The current chunk to write to + HRESULT Result; // HRESULT of the current enumeration + CLRDATA_ADDRESS AppDomain; // The AppDomain for the current bucket we are walking + unsigned int Type; // The type of handle we are currently walking + + DacHandleWalkerParam(HandleChunk *curr) + : Curr(curr), Result(S_OK), AppDomain(0), Type(0) + { + } + }; + +public: + DacHandleWalker(); + virtual ~DacHandleWalker(); + + HRESULT Init(ClrDataAccess *dac, UINT types[], UINT typeCount); + HRESULT Init(ClrDataAccess *dac, UINT types[], UINT typeCount, int gen); + HRESULT Init(UINT32 typemask); + + // SOS functions + HRESULT STDMETHODCALLTYPE Skip(unsigned int count); + HRESULT STDMETHODCALLTYPE Reset(); + HRESULT STDMETHODCALLTYPE GetCount(unsigned int *pCount); + HRESULT STDMETHODCALLTYPE Next(unsigned int count, + SOSHandleData handles[], + unsigned int *pNeeded); + + // Dac-Dbi Functions + HRESULT Next(ULONG celt, DacGcReference roots[], ULONG *pceltFetched); +private: + static void CALLBACK EnumCallback(PTR_UNCHECKED_OBJECTREF pref, LPARAM *pExtraInfo, LPARAM userParam, LPARAM type); + static void GetRefCountedHandleInfo( + OBJECTREF oref, unsigned int uType, + unsigned int *pRefCount, unsigned int *pJupiterRefCount, BOOL *pIsPegged, BOOL *pIsStrong); + static UINT32 BuildTypemask(UINT types[], UINT typeCount); + +private: + static void CALLBACK EnumCallbackSOS(PTR_UNCHECKED_OBJECTREF pref, uintptr_t *pExtraInfo, uintptr_t userParam, uintptr_t type); + static void CALLBACK EnumCallbackDac(PTR_UNCHECKED_OBJECTREF pref, uintptr_t *pExtraInfo, uintptr_t userParam, uintptr_t type); + + bool FetchMoreHandles(HANDLESCANPROC proc); + static inline bool IsAlwaysStrongReference(unsigned int type) + { + return type == HNDTYPE_STRONG || type == HNDTYPE_PINNED || type == HNDTYPE_ASYNCPINNED || type == HNDTYPE_SIZEDREF; + } + + template <class StructType, class IntType, HANDLESCANPROC EnumFunc> + HRESULT DoHandleWalk(IntType celt, StructType handles[], IntType *pceltFetched) + { + SUPPORTS_DAC; + + if (handles == NULL || pceltFetched == NULL) + return E_POINTER; + + HRESULT hr = S_OK; + IntType fetched = 0; + bool done = false; + + // On each iteration of the loop, either fetch more handles (filling in + // the user's data structure), or copy handles from previous calls to + // FetchMoreHandles which we could not store in the user's data (or simply + // advance the current chunk to the next chunk). + while (fetched < celt) + { + if (mCurr == NULL) + { + // Case 1: We have no overflow data. Stuff the user's array/size into + // mHead, fetch more handles. Additionally, if the previous call to + // FetchMoreHandles returned false (mMap == NULL), break. + if (mMap == NULL) + break; + + mHead.pData = handles+fetched; + mHead.Size = (celt - fetched)*sizeof(StructType); + + done = !FetchMoreHandles(EnumFunc); + fetched += mHead.Count; + + // Sanity check to make sure we haven't overflowed. This should not happen. + _ASSERTE(fetched <= celt); + } + else if (mChunkIndex >= mCurr->Count) + { + // Case 2: We have overflow data, but the current index into the current + // chunk is past the bounds. Move to the next. This could set mCurr to + // null, which we'll catch on the next iteration. + mCurr = mCurr->Next; + mChunkIndex = 0; + } + else + { + // Case 3: The last call to "Next" filled the user's array and had some handle + // data leftover. Walk the linked-list of arrays copying them into the user's + // buffer until we have either exhausted the user's array or the leftover data. + unsigned int toCopy = celt - fetched; // Fill the user's buffer... + + // ...unless that would go past the bounds of the current chunk. + if (toCopy + mChunkIndex > mCurr->Count) + toCopy = mCurr->Count - mChunkIndex; + + memcpy(handles+fetched, ((StructType*)(mCurr->pData))+mChunkIndex, toCopy*sizeof(StructType)); + mChunkIndex += toCopy; + fetched += toCopy; + } + } + + if (fetched < celt) + hr = S_FALSE; + + *pceltFetched = fetched; + + return hr; + } + +private: + // Dac variables required for entering/leaving the dac. + ClrDataAccess *mDac; + ULONG32 m_instanceAge; + + // Handle table walking variables. + HandleTableMap *mMap; + int mIndex; + UINT32 mTypeMask; + int mGenerationFilter; + + // Storage variables + HandleChunk mHead; + unsigned int mChunkIndex; + + // Iterator variables + HandleChunkHead *mCurr; + int mIteratorIndex; +}; + + +//---------------------------------------------------------------------------- +// +// ClrDataAppDomain. +// +//---------------------------------------------------------------------------- + +class ClrDataAppDomain : public IXCLRDataAppDomain +{ +public: + ClrDataAppDomain(ClrDataAccess* dac, + AppDomain* appDomain); + virtual ~ClrDataAppDomain(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataAppDomain. + // + + virtual HRESULT STDMETHODCALLTYPE GetProcess( + /* [out] */ IXCLRDataProcess **process); + + virtual HRESULT STDMETHODCALLTYPE GetName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetUniqueID( + /* [out] */ ULONG64 *id); + + virtual HRESULT STDMETHODCALLTYPE GetFlags( + /* [out] */ ULONG32 *flags); + + virtual HRESULT STDMETHODCALLTYPE IsSameObject( + /* [in] */ IXCLRDataAppDomain *appDomain); + + virtual HRESULT STDMETHODCALLTYPE GetManagedObject( + /* [out] */ IXCLRDataValue **value); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + + AppDomain* GetAppDomain(void) + { + SUPPORTS_DAC; + return m_appDomain; + } + +private: + LONG m_refs; + ClrDataAccess* m_dac; + ULONG32 m_instanceAge; + AppDomain* m_appDomain; +}; + +//---------------------------------------------------------------------------- +// +// ClrDataAssembly. +// +//---------------------------------------------------------------------------- + +class ClrDataAssembly : public IXCLRDataAssembly +{ +public: + ClrDataAssembly(ClrDataAccess* dac, + Assembly* assembly); + virtual ~ClrDataAssembly(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataAssembly. + // + + virtual HRESULT STDMETHODCALLTYPE StartEnumModules( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumModule( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataModule **mod); + + virtual HRESULT STDMETHODCALLTYPE EndEnumModules( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumAppDomains( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumAppDomain( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataAppDomain **appDomain); + + virtual HRESULT STDMETHODCALLTYPE EndEnumAppDomains( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetFileName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetDisplayName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetFlags( + /* [out] */ ULONG32 *flags); + + virtual HRESULT STDMETHODCALLTYPE IsSameObject( + /* [in] */ IXCLRDataAssembly *assembly); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + +private: + LONG m_refs; + ClrDataAccess* m_dac; + ULONG32 m_instanceAge; + Assembly* m_assembly; +}; + +//---------------------------------------------------------------------------- +// +// ClrDataModule. +// +//---------------------------------------------------------------------------- + +class ClrDataModule : public IXCLRDataModule, IXCLRDataModule2 +{ +public: + ClrDataModule(ClrDataAccess* dac, + Module* module); + virtual ~ClrDataModule(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataModule. + // + + virtual HRESULT STDMETHODCALLTYPE StartEnumAssemblies( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumAssembly( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataAssembly **assembly); + + virtual HRESULT STDMETHODCALLTYPE EndEnumAssemblies( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumAppDomains( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumAppDomain( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataAppDomain **appDomain); + + virtual HRESULT STDMETHODCALLTYPE EndEnumAppDomains( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumTypeDefinitions( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumTypeDefinition( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataTypeDefinition **typeDefinition); + + virtual HRESULT STDMETHODCALLTYPE EndEnumTypeDefinitions( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumTypeInstances( + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumTypeInstance( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataTypeInstance **typeInstance); + + virtual HRESULT STDMETHODCALLTYPE EndEnumTypeInstances( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumTypeDefinitionsByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumTypeDefinitionByName( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataTypeDefinition **type); + + virtual HRESULT STDMETHODCALLTYPE EndEnumTypeDefinitionsByName( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumTypeInstancesByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataAppDomain *appDomain, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumTypeInstanceByName( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataTypeInstance **type); + + virtual HRESULT STDMETHODCALLTYPE EndEnumTypeInstancesByName( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetTypeDefinitionByToken( + /* [in] */ mdTypeDef token, + /* [out] */ IXCLRDataTypeDefinition **typeDefinition); + + virtual HRESULT STDMETHODCALLTYPE StartEnumMethodDefinitionsByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumMethodDefinitionByName( + /* [in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodDefinition **method); + + virtual HRESULT STDMETHODCALLTYPE EndEnumMethodDefinitionsByName( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumMethodInstancesByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumMethodInstanceByName( + /* [in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodInstance **method); + + virtual HRESULT STDMETHODCALLTYPE EndEnumMethodInstancesByName( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetMethodDefinitionByToken( + /* [in] */ mdMethodDef token, + /* [out] */ IXCLRDataMethodDefinition **methodDefinition); + + virtual HRESULT STDMETHODCALLTYPE StartEnumDataByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [in] */ IXCLRDataTask* tlsTask, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumDataByName( + /* [in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataValue **value); + + virtual HRESULT STDMETHODCALLTYPE EndEnumDataByName( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetFileName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part(bufLen, *nameLen) WCHAR name[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetVersionId( + /* [out] */ GUID* vid); + + virtual HRESULT STDMETHODCALLTYPE GetFlags( + /* [out] */ ULONG32 *flags); + + virtual HRESULT STDMETHODCALLTYPE IsSameObject( + /* [in] */ IXCLRDataModule *mod); + + virtual HRESULT STDMETHODCALLTYPE StartEnumExtents( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumExtent( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ CLRDATA_MODULE_EXTENT *extent); + + virtual HRESULT STDMETHODCALLTYPE EndEnumExtents( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + + HRESULT RequestGetModulePtr(IN ULONG32 inBufferSize, + IN BYTE* inBuffer, + IN ULONG32 outBufferSize, + OUT BYTE* outBuffer); + + HRESULT RequestGetModuleData(IN ULONG32 inBufferSize, + IN BYTE* inBuffer, + IN ULONG32 outBufferSize, + OUT BYTE* outBuffer); + + Module* GetModule(void) + { + return m_module; + } + + // + // IXCLRDataModule2 + // + virtual HRESULT STDMETHODCALLTYPE SetJITCompilerFlags( + /* [in] */ DWORD dwFlags ); + +private: + // Returns an instance of IID_IMetaDataImport. + HRESULT GetMdInterface(PVOID* retIface); + + LONG m_refs; + ClrDataAccess* m_dac; + ULONG32 m_instanceAge; + Module* m_module; + IMetaDataImport* m_mdImport; + bool m_setExtents; + CLRDATA_MODULE_EXTENT m_extents[2]; + CLRDATA_MODULE_EXTENT* m_extentsEnd; +}; + +//---------------------------------------------------------------------------- +// +// ClrDataTypeDefinition. +// +//---------------------------------------------------------------------------- + +class ClrDataTypeDefinition : public IXCLRDataTypeDefinition +{ +public: + ClrDataTypeDefinition(ClrDataAccess* dac, + Module* module, + mdTypeDef token, + TypeHandle typeHandle); + virtual ~ClrDataTypeDefinition(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataTypeDefinition. + // + + virtual HRESULT STDMETHODCALLTYPE GetModule( + /* [out] */ IXCLRDataModule **mod); + + virtual HRESULT STDMETHODCALLTYPE StartEnumMethodDefinitions( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumMethodDefinition( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodDefinition **methodDefinition); + + virtual HRESULT STDMETHODCALLTYPE EndEnumMethodDefinitions( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumMethodDefinitionsByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumMethodDefinitionByName( + /* [in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodDefinition **method); + + virtual HRESULT STDMETHODCALLTYPE EndEnumMethodDefinitionsByName( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetMethodDefinitionByToken( + /* [in] */ mdMethodDef token, + /* [out] */ IXCLRDataMethodDefinition **methodDefinition); + + virtual HRESULT STDMETHODCALLTYPE StartEnumInstances( + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumInstance( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataTypeInstance **instance); + + virtual HRESULT STDMETHODCALLTYPE EndEnumInstances( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetNumFields( + /* [in] */ ULONG32 flags, + /* [out] */ ULONG32 *numFields); + + virtual HRESULT STDMETHODCALLTYPE StartEnumFields( + /* [in] */ ULONG32 flags, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumField( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [in] */ ULONG32 nameBufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(nameBufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataTypeDefinition **type, + /* [out] */ ULONG32 *flags, + /* [out] */ mdFieldDef *token); + + virtual HRESULT STDMETHODCALLTYPE EnumField2( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [in] */ ULONG32 nameBufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(nameBufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataTypeDefinition **type, + /* [out] */ ULONG32 *flags, + /* [out] */ IXCLRDataModule** tokenScope, + /* [out] */ mdFieldDef *token); + + virtual HRESULT STDMETHODCALLTYPE EndEnumFields( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumFieldsByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 nameFlags, + /* [in] */ ULONG32 fieldFlags, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumFieldByName( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataTypeDefinition **type, + /* [out] */ ULONG32 *flags, + /* [out] */ mdFieldDef *token); + + virtual HRESULT STDMETHODCALLTYPE EnumFieldByName2( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataTypeDefinition **type, + /* [out] */ ULONG32 *flags, + /* [out] */ IXCLRDataModule** tokenScope, + /* [out] */ mdFieldDef *token); + + virtual HRESULT STDMETHODCALLTYPE EndEnumFieldsByName( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetFieldByToken( + /* [in] */ mdFieldDef token, + /* [in] */ ULONG32 nameBufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(nameBufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataTypeDefinition **type, + /* [out] */ ULONG32* flags); + + virtual HRESULT STDMETHODCALLTYPE GetFieldByToken2( + /* [in] */ IXCLRDataModule* tokenScope, + /* [in] */ mdFieldDef token, + /* [in] */ ULONG32 nameBufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(nameBufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataTypeDefinition **type, + /* [out] */ ULONG32* flags); + + virtual HRESULT STDMETHODCALLTYPE GetName( + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetTokenAndScope( + /* [out] */ mdTypeDef *token, + /* [out] */ IXCLRDataModule **mod); + + virtual HRESULT STDMETHODCALLTYPE GetCorElementType( + /* [out] */ CorElementType *type); + + virtual HRESULT STDMETHODCALLTYPE GetFlags( + /* [out] */ ULONG32 *flags); + + virtual HRESULT STDMETHODCALLTYPE GetBase( + /* [out] */ IXCLRDataTypeDefinition **base); + + virtual HRESULT STDMETHODCALLTYPE IsSameObject( + /* [in] */ IXCLRDataTypeDefinition *type); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + + virtual HRESULT STDMETHODCALLTYPE GetArrayRank( + /* [out] */ ULONG32* rank); + + virtual HRESULT STDMETHODCALLTYPE GetTypeNotification( + /* [out] */ ULONG32* flags); + + virtual HRESULT STDMETHODCALLTYPE SetTypeNotification( + /* [in] */ ULONG32 flags); + + static HRESULT NewFromModule(ClrDataAccess* dac, + Module* module, + mdTypeDef token, + ClrDataTypeDefinition** typeDef, + IXCLRDataTypeDefinition** pubTypeDef); + + TypeHandle GetTypeHandle(void) + { + return m_typeHandle; + } + +private: + LONG m_refs; + ClrDataAccess* m_dac; + ULONG32 m_instanceAge; + Module* m_module; + mdTypeDef m_token; + TypeHandle m_typeHandle; +}; + +//---------------------------------------------------------------------------- +// +// ClrDataTypeInstance. +// +//---------------------------------------------------------------------------- + +class ClrDataTypeInstance : public IXCLRDataTypeInstance +{ +public: + ClrDataTypeInstance(ClrDataAccess* dac, + AppDomain* appDomain, + TypeHandle typeHandle); + virtual ~ClrDataTypeInstance(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataTypeInstance. + // + + virtual HRESULT STDMETHODCALLTYPE StartEnumMethodInstances( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumMethodInstance( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodInstance **methodInstance); + + virtual HRESULT STDMETHODCALLTYPE EndEnumMethodInstances( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumMethodInstancesByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumMethodInstanceByName( + /* [in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodInstance **method); + + virtual HRESULT STDMETHODCALLTYPE EndEnumMethodInstancesByName( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetNumStaticFields( + /* [out] */ ULONG32 *numFields); + + virtual HRESULT STDMETHODCALLTYPE GetStaticFieldByIndex( + /* [in] */ ULONG32 index, + /* [in] */ IXCLRDataTask *tlsTask, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ mdFieldDef *token); + + virtual HRESULT STDMETHODCALLTYPE StartEnumStaticFieldsByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataTask *tlsTask, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumStaticFieldByName( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **value); + + virtual HRESULT STDMETHODCALLTYPE EndEnumStaticFieldsByName( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetNumStaticFields2( + /* [in] */ ULONG32 flags, + /* [out] */ ULONG32 *numFields); + + virtual HRESULT STDMETHODCALLTYPE StartEnumStaticFields( + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataTask *tlsTask, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumStaticField( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **value); + + virtual HRESULT STDMETHODCALLTYPE EnumStaticField2( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **value, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataModule** tokenScope, + /* [out] */ mdFieldDef *token); + + virtual HRESULT STDMETHODCALLTYPE EndEnumStaticFields( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumStaticFieldsByName2( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 nameFlags, + /* [in] */ ULONG32 fieldFlags, + /* [in] */ IXCLRDataTask *tlsTask, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumStaticFieldByName3( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **value, + /* [out] */ IXCLRDataModule** tokenScope, + /* [out] */ mdFieldDef *token); + + virtual HRESULT STDMETHODCALLTYPE EnumStaticFieldByName2( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **value); + + virtual HRESULT STDMETHODCALLTYPE EndEnumStaticFieldsByName2( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetStaticFieldByToken( + /* [in] */ mdFieldDef token, + /* [in] */ IXCLRDataTask *tlsTask, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetStaticFieldByToken2( + /* [in] */ IXCLRDataModule* tokenScope, + /* [in] */ mdFieldDef token, + /* [in] */ IXCLRDataTask *tlsTask, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetName( + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetModule( + /* [out] */ IXCLRDataModule **mod); + + virtual HRESULT STDMETHODCALLTYPE GetDefinition( + /* [out] */ IXCLRDataTypeDefinition **typeDefinition); + + virtual HRESULT STDMETHODCALLTYPE GetFlags( + /* [out] */ ULONG32 *flags); + + virtual HRESULT STDMETHODCALLTYPE GetBase( + /* [out] */ IXCLRDataTypeInstance **base); + + virtual HRESULT STDMETHODCALLTYPE IsSameObject( + /* [in] */ IXCLRDataTypeInstance *type); + + virtual HRESULT STDMETHODCALLTYPE GetNumTypeArguments( + /* [out] */ ULONG32 *numTypeArgs); + + virtual HRESULT STDMETHODCALLTYPE GetTypeArgumentByIndex( + /* [in] */ ULONG32 index, + /* [out] */ IXCLRDataTypeInstance **typeArg); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + + static HRESULT NewFromModule(ClrDataAccess* dac, + AppDomain* appDomain, + Module* module, + mdTypeDef token, + ClrDataTypeInstance** typeInst, + IXCLRDataTypeInstance** pubTypeInst); + + TypeHandle GetTypeHandle(void) + { + return m_typeHandle; + } + +private: + LONG m_refs; + ClrDataAccess* m_dac; + ULONG32 m_instanceAge; + AppDomain* m_appDomain; + TypeHandle m_typeHandle; +}; + +//---------------------------------------------------------------------------- +// +// ClrDataMethodDefinition. +// +//---------------------------------------------------------------------------- + +class ClrDataMethodDefinition : public IXCLRDataMethodDefinition +{ +public: + ClrDataMethodDefinition(ClrDataAccess* dac, + Module* module, + mdMethodDef token, + MethodDesc* methodDesc); + virtual ~ClrDataMethodDefinition(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataMethodDefinition. + // + + virtual HRESULT STDMETHODCALLTYPE GetTypeDefinition( + /* [out] */ IXCLRDataTypeDefinition **typeDefinition); + + virtual HRESULT STDMETHODCALLTYPE StartEnumInstances( + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumInstance( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataMethodInstance **instance); + + virtual HRESULT STDMETHODCALLTYPE EndEnumInstances( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetName( + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetTokenAndScope( + /* [out] */ mdMethodDef *token, + /* [out] */ IXCLRDataModule **mod); + + virtual HRESULT STDMETHODCALLTYPE GetFlags( + /* [out] */ ULONG32 *flags); + + virtual HRESULT STDMETHODCALLTYPE IsSameObject( + /* [in] */ IXCLRDataMethodDefinition *method); + + virtual HRESULT STDMETHODCALLTYPE GetLatestEnCVersion( + /* [out] */ ULONG32* version); + + virtual HRESULT STDMETHODCALLTYPE StartEnumExtents( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumExtent( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ CLRDATA_METHDEF_EXTENT *extent); + + virtual HRESULT STDMETHODCALLTYPE EndEnumExtents( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetCodeNotification( + /* [out] */ ULONG32 *flags); + + virtual HRESULT STDMETHODCALLTYPE SetCodeNotification( + /* [in] */ ULONG32 flags); + + virtual HRESULT STDMETHODCALLTYPE GetRepresentativeEntryAddress( + /* [out] */ CLRDATA_ADDRESS* addr); + + virtual HRESULT STDMETHODCALLTYPE HasClassOrMethodInstantiation( + /*[out]*/ BOOL* bGeneric); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + + COR_ILMETHOD* GetIlMethod(void); + + static HRESULT NewFromModule(ClrDataAccess* dac, + Module* module, + mdMethodDef token, + ClrDataMethodDefinition** methDef, + IXCLRDataMethodDefinition** pubMethDef); + + static HRESULT GetSharedMethodFlags(MethodDesc* methodDesc, + ULONG32* flags); + + // We don't need this if we are able to form name using token + MethodDesc *GetMethodDesc() { return m_methodDesc;} +private: + LONG m_refs; + ClrDataAccess* m_dac; + ULONG32 m_instanceAge; + Module* m_module; + mdMethodDef m_token; + MethodDesc* m_methodDesc; +}; + +//---------------------------------------------------------------------------- +// +// ClrDataMethodInstance. +// +//---------------------------------------------------------------------------- + +class ClrDataMethodInstance : public IXCLRDataMethodInstance +{ +public: + ClrDataMethodInstance(ClrDataAccess* dac, + AppDomain* appDomain, + MethodDesc* methodDesc); + virtual ~ClrDataMethodInstance(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataMethodInstance. + // + + virtual HRESULT STDMETHODCALLTYPE GetTypeInstance( + /* [out] */ IXCLRDataTypeInstance **typeInstance); + + virtual HRESULT STDMETHODCALLTYPE GetDefinition( + /* [out] */ IXCLRDataMethodDefinition **methodDefinition); + + virtual HRESULT STDMETHODCALLTYPE GetTokenAndScope( + /* [out] */ mdMethodDef *token, + /* [out] */ IXCLRDataModule **mod); + + virtual HRESULT STDMETHODCALLTYPE GetName( + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part(bufLen, *nameLen) WCHAR name[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetFlags( + /* [out] */ ULONG32 *flags); + + virtual HRESULT STDMETHODCALLTYPE IsSameObject( + /* [in] */ IXCLRDataMethodInstance *method); + + virtual HRESULT STDMETHODCALLTYPE GetEnCVersion( + /* [out] */ ULONG32* version); + + virtual HRESULT STDMETHODCALLTYPE GetNumTypeArguments( + /* [out] */ ULONG32 *numTypeArgs); + + virtual HRESULT STDMETHODCALLTYPE GetTypeArgumentByIndex( + /* [in] */ ULONG32 index, + /* [out] */ IXCLRDataTypeInstance **typeArg); + + virtual HRESULT STDMETHODCALLTYPE GetILOffsetsByAddress( + /* [in] */ CLRDATA_ADDRESS address, + /* [in] */ ULONG32 offsetsLen, + /* [out] */ ULONG32 *offsetsNeeded, + /* [size_is][out] */ ULONG32 ilOffsets[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetAddressRangesByILOffset( + /* [in] */ ULONG32 ilOffset, + /* [in] */ ULONG32 rangesLen, + /* [out] */ ULONG32 *rangesNeeded, + /* [size_is][out] */ CLRDATA_ADDRESS_RANGE addressRanges[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetILAddressMap( + /* [in] */ ULONG32 mapLen, + /* [out] */ ULONG32 *mapNeeded, + /* [size_is][out] */ CLRDATA_IL_ADDRESS_MAP maps[ ]); + + virtual HRESULT STDMETHODCALLTYPE StartEnumExtents( + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumExtent( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ CLRDATA_ADDRESS_RANGE *extent); + + virtual HRESULT STDMETHODCALLTYPE EndEnumExtents( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetRepresentativeEntryAddress( + /* [out] */ CLRDATA_ADDRESS* addr); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + + static HRESULT NewFromModule(ClrDataAccess* dac, + AppDomain* appDomain, + Module* module, + mdMethodDef token, + ClrDataMethodInstance** methInst, + IXCLRDataMethodInstance** pubMethInst); + +private: + friend class ClrDataAccess; + LONG m_refs; + ClrDataAccess* m_dac; + ULONG32 m_instanceAge; + AppDomain* m_appDomain; + MethodDesc* m_methodDesc; +}; + +//---------------------------------------------------------------------------- +// +// ClrDataTask. +// +//---------------------------------------------------------------------------- + +class ClrDataTask : public IXCLRDataTask +{ +public: + ClrDataTask(ClrDataAccess* dac, + Thread* Thread); + virtual ~ClrDataTask(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataTask. + // + + virtual HRESULT STDMETHODCALLTYPE GetProcess( + /* [out] */ IXCLRDataProcess **process); + + virtual HRESULT STDMETHODCALLTYPE GetCurrentAppDomain( + /* [out] */ IXCLRDataAppDomain **appDomain); + + virtual HRESULT STDMETHODCALLTYPE GetName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetUniqueID( + /* [out] */ ULONG64 *id); + + virtual HRESULT STDMETHODCALLTYPE GetFlags( + /* [out] */ ULONG32 *flags); + + virtual HRESULT STDMETHODCALLTYPE IsSameObject( + /* [in] */ IXCLRDataTask *task); + + virtual HRESULT STDMETHODCALLTYPE GetManagedObject( + /* [out] */ IXCLRDataValue **value); + + virtual HRESULT STDMETHODCALLTYPE GetDesiredExecutionState( + /* [out] */ ULONG32 *state); + + virtual HRESULT STDMETHODCALLTYPE SetDesiredExecutionState( + /* [in] */ ULONG32 state); + + virtual HRESULT STDMETHODCALLTYPE CreateStackWalk( + /* [in] */ ULONG32 flags, + /* [out] */ IXCLRDataStackWalk **stackWalk); + + virtual HRESULT STDMETHODCALLTYPE GetOSThreadID( + /* [out] */ ULONG32 *id); + + virtual HRESULT STDMETHODCALLTYPE GetContext( + /* [in] */ ULONG32 contextFlags, + /* [in] */ ULONG32 contextBufSize, + /* [out] */ ULONG32 *contextSize, + /* [size_is][out] */ BYTE contextBuf[ ]); + + virtual HRESULT STDMETHODCALLTYPE SetContext( + /* [in] */ ULONG32 contextSize, + /* [size_is][in] */ BYTE context[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetCurrentExceptionState( + /* [out] */ IXCLRDataExceptionState **exception); + + virtual HRESULT STDMETHODCALLTYPE GetLastExceptionState( + /* [out] */ IXCLRDataExceptionState **exception); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + + Thread* GetThread(void) + { + return m_thread; + } + +private: + LONG m_refs; + ClrDataAccess* m_dac; + ULONG32 m_instanceAge; + Thread* m_thread; +}; + +//---------------------------------------------------------------------------- +// +// ClrDataStackWalk. +// +//---------------------------------------------------------------------------- + +class ClrDataStackWalk : public IXCLRDataStackWalk +{ +public: + ClrDataStackWalk(ClrDataAccess* dac, + Thread* Thread, + ULONG32 flags); + virtual ~ClrDataStackWalk(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataStackWalk. + // + + virtual HRESULT STDMETHODCALLTYPE GetContext( + /* [in] */ ULONG32 contextFlags, + /* [in] */ ULONG32 contextBufSize, + /* [out] */ ULONG32 *contextSize, + /* [size_is][out] */ BYTE contextBuf[ ]); + + virtual HRESULT STDMETHODCALLTYPE SetContext( + /* [in] */ ULONG32 contextSize, + /* [size_is][in] */ BYTE context[ ]); + + virtual HRESULT STDMETHODCALLTYPE Next( void); + + virtual HRESULT STDMETHODCALLTYPE GetStackSizeSkipped( + /* [out] */ ULONG64 *stackSizeSkipped); + + virtual HRESULT STDMETHODCALLTYPE GetFrameType( + /* [out] */ CLRDataSimpleFrameType *simpleType, + /* [out] */ CLRDataDetailedFrameType *detailedType); + + virtual HRESULT STDMETHODCALLTYPE GetFrame( + /* [out] */ IXCLRDataFrame **frame); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + + virtual HRESULT STDMETHODCALLTYPE SetContext2( + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 contextSize, + /* [size_is][in] */ BYTE context[ ]); + + HRESULT Init(void); + +private: + void FilterFrames(void); + void RawGetFrameType( + /* [out] */ CLRDataSimpleFrameType* simpleType, + /* [out] */ CLRDataDetailedFrameType* detailedType); + + LONG m_refs; + ClrDataAccess* m_dac; + ULONG32 m_instanceAge; + Thread* m_thread; + ULONG32 m_walkFlags; + StackFrameIterator m_frameIter; + REGDISPLAY m_regDisp; + T_CONTEXT m_context; + TADDR m_stackPrev; + + // This is part of a test hook for debugging. Unless you're code:ClrDataStackWalk::Next + // you should never do anything with this member. + INDEBUG( int m_framesUnwound; ) + +}; + +//---------------------------------------------------------------------------- +// +// ClrDataFrame. +// +//---------------------------------------------------------------------------- + +class ClrDataFrame : public IXCLRDataFrame, + IXCLRDataFrame2 +{ + friend class ClrDataStackWalk; + +public: + ClrDataFrame(ClrDataAccess* dac, + CLRDataSimpleFrameType simpleType, + CLRDataDetailedFrameType detailedType, + AppDomain* appDomain, + MethodDesc* methodDesc); + virtual ~ClrDataFrame(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataFrame. + // + + virtual HRESULT STDMETHODCALLTYPE GetContext( + /* [in] */ ULONG32 contextFlags, + /* [in] */ ULONG32 contextBufSize, + /* [out] */ ULONG32 *contextSize, + /* [size_is][out] */ BYTE contextBuf[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetFrameType( + /* [out] */ CLRDataSimpleFrameType *simpleType, + /* [out] */ CLRDataDetailedFrameType *detailedType); + + virtual HRESULT STDMETHODCALLTYPE GetAppDomain( + /* [out] */ IXCLRDataAppDomain **appDomain); + + virtual HRESULT STDMETHODCALLTYPE GetNumArguments( + /* [out] */ ULONG32 *numParams); + + virtual HRESULT STDMETHODCALLTYPE GetArgumentByIndex( + /* [in] */ ULONG32 index, + /* [out] */ IXCLRDataValue **arg, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part(bufLen, *nameLen) WCHAR name[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetNumLocalVariables( + /* [out] */ ULONG32 *numLocals); + + virtual HRESULT STDMETHODCALLTYPE GetLocalVariableByIndex( + /* [in] */ ULONG32 index, + /* [out] */ IXCLRDataValue **localVariable, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part(bufLen, *nameLen) WCHAR name[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetNumTypeArguments( + /* [out] */ ULONG32 *numTypeArgs); + + virtual HRESULT STDMETHODCALLTYPE GetTypeArgumentByIndex( + /* [in] */ ULONG32 index, + /* [out] */ IXCLRDataTypeInstance **typeArg); + + virtual HRESULT STDMETHODCALLTYPE GetCodeName( + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR nameBuf[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetMethodInstance( + /* [out] */ IXCLRDataMethodInstance **method); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + + // + // IXCLRDataFrame2. + // + + virtual HRESULT STDMETHODCALLTYPE GetExactGenericArgsToken( + /* [out] */ IXCLRDataValue ** genericToken); + +private: + HRESULT GetMethodSig(MetaSig** sig, + ULONG32* count); + HRESULT GetLocalSig(MetaSig** sig, + ULONG32* count); + HRESULT ValueFromDebugInfo(MetaSig* sig, + bool isArg, + DWORD sigIndex, + DWORD varInfoSlot, + IXCLRDataValue** value); + + LONG m_refs; + ClrDataAccess* m_dac; + ULONG32 m_instanceAge; + CLRDataSimpleFrameType m_simpleType; + CLRDataDetailedFrameType m_detailedType; + AppDomain* m_appDomain; + MethodDesc* m_methodDesc; + REGDISPLAY m_regDisp; + T_CONTEXT m_context; + MetaSig* m_methodSig; + MetaSig* m_localSig; +}; + +//---------------------------------------------------------------------------- +// +// ClrDataExceptionState. +// +//---------------------------------------------------------------------------- + +#ifdef WIN64EXCEPTIONS +typedef ExceptionTracker ClrDataExStateType; +#else // WIN64EXCEPTIONS +typedef ExInfo ClrDataExStateType; +#endif // WIN64EXCEPTIONS + + +class ClrDataExceptionState : public IXCLRDataExceptionState +{ +public: + ClrDataExceptionState(ClrDataAccess* dac, + AppDomain* appDomain, + Thread* thread, + ULONG32 flags, + ClrDataExStateType* exInfo, + OBJECTHANDLE throwable, + ClrDataExStateType* prevExInfo); + virtual ~ClrDataExceptionState(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataExceptionState. + // + + virtual HRESULT STDMETHODCALLTYPE GetFlags( + /* [out] */ ULONG32 *flags); + + virtual HRESULT STDMETHODCALLTYPE GetPrevious( + /* [out] */ IXCLRDataExceptionState **exState); + + virtual HRESULT STDMETHODCALLTYPE GetManagedObject( + /* [out] */ IXCLRDataValue **value); + + virtual HRESULT STDMETHODCALLTYPE GetBaseType( + /* [out] */ CLRDataBaseExceptionType *type); + + virtual HRESULT STDMETHODCALLTYPE GetCode( + /* [out] */ ULONG32 *code); + + virtual HRESULT STDMETHODCALLTYPE GetString( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *strLen, + /* [size_is][out] */ __out_ecount_part(bufLen, *strLen) WCHAR str[ ]); + + virtual HRESULT STDMETHODCALLTYPE IsSameState( + /* [in] */ EXCEPTION_RECORD64 *exRecord, + /* [in] */ ULONG32 contextSize, + /* [size_is][in] */ BYTE cxRecord[ ]); + + virtual HRESULT STDMETHODCALLTYPE IsSameState2( + /* [in] */ ULONG32 flags, + /* [in] */ EXCEPTION_RECORD64 *exRecord, + /* [in] */ ULONG32 contextSize, + /* [size_is][in] */ BYTE cxRecord[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetTask( + /* [out] */ IXCLRDataTask** task); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + + static HRESULT NewFromThread(ClrDataAccess* dac, + Thread* thread, + ClrDataExceptionState** exception, + IXCLRDataExceptionState** pubException); + + PTR_CONTEXT GetCurrentContextRecord(); + PTR_EXCEPTION_RECORD GetCurrentExceptionRecord(); + +friend class ClrDataAccess; +private: + LONG m_refs; + ClrDataAccess* m_dac; + ULONG32 m_instanceAge; + AppDomain* m_appDomain; + Thread* m_thread; + ULONG32 m_flags; + ClrDataExStateType* m_exInfo; + OBJECTHANDLE m_throwable; + ClrDataExStateType* m_prevExInfo; +}; + +//---------------------------------------------------------------------------- +// +// ClrDataValue. +// +//---------------------------------------------------------------------------- + +class ClrDataValue : public IXCLRDataValue +{ +public: + ClrDataValue(ClrDataAccess* dac, + AppDomain* appDomain, + Thread* thread, + ULONG32 flags, + TypeHandle typeHandle, + ULONG64 baseAddr, + ULONG32 numLocs, + NativeVarLocation* locs); + virtual ~ClrDataValue(void); + + // IUnknown. + STDMETHOD(QueryInterface)(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + + // + // IXCLRDataValue. + // + + virtual HRESULT STDMETHODCALLTYPE GetFlags( + /* [out] */ ULONG32 *flags); + + virtual HRESULT STDMETHODCALLTYPE GetAddress( + /* [out] */ CLRDATA_ADDRESS *address); + + virtual HRESULT STDMETHODCALLTYPE GetSize( + /* [out] */ ULONG64 *size); + + virtual HRESULT STDMETHODCALLTYPE GetBytes( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *dataSize, + /* [size_is][out] */ BYTE buffer[ ]); + + virtual HRESULT STDMETHODCALLTYPE SetBytes( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *dataSize, + /* [size_is][in] */ BYTE buffer[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetType( + /* [out] */ IXCLRDataTypeInstance **typeInstance); + + virtual HRESULT STDMETHODCALLTYPE GetNumFields( + /* [out] */ ULONG32 *numFields); + + virtual HRESULT STDMETHODCALLTYPE GetFieldByIndex( + /* [in] */ ULONG32 index, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ mdFieldDef *token); + + virtual HRESULT STDMETHODCALLTYPE GetNumFields2( + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataTypeInstance *fromType, + /* [out] */ ULONG32 *numFields); + + virtual HRESULT STDMETHODCALLTYPE StartEnumFields( + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataTypeInstance *fromType, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumField( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 nameBufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(nameBufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ mdFieldDef *token); + + virtual HRESULT STDMETHODCALLTYPE EnumField2( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 nameBufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(nameBufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataModule** tokenScope, + /* [out] */ mdFieldDef *token); + + virtual HRESULT STDMETHODCALLTYPE EndEnumFields( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE StartEnumFieldsByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 nameFlags, + /* [in] */ ULONG32 fieldFlags, + /* [in] */ IXCLRDataTypeInstance *fromType, + /* [out] */ CLRDATA_ENUM *handle); + + virtual HRESULT STDMETHODCALLTYPE EnumFieldByName( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **field, + /* [out] */ mdFieldDef *token); + + virtual HRESULT STDMETHODCALLTYPE EnumFieldByName2( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **field, + /* [out] */ IXCLRDataModule** tokenScope, + /* [out] */ mdFieldDef *token); + + virtual HRESULT STDMETHODCALLTYPE EndEnumFieldsByName( + /* [in] */ CLRDATA_ENUM handle); + + virtual HRESULT STDMETHODCALLTYPE GetFieldByToken( + /* [in] */ mdFieldDef token, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetFieldByToken2( + /* [in] */ IXCLRDataModule* tokenScope, + /* [in] */ mdFieldDef token, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetAssociatedValue( + /* [out] */ IXCLRDataValue **assocValue); + + virtual HRESULT STDMETHODCALLTYPE GetAssociatedType( + /* [out] */ IXCLRDataTypeInstance **assocType); + + virtual HRESULT STDMETHODCALLTYPE GetString( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *strLen, + /* [size_is][out] */ __out_ecount_part(bufLen, *strLen) WCHAR str[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetArrayProperties( + /* [out] */ ULONG32 *rank, + /* [out] */ ULONG32 *totalElements, + /* [in] */ ULONG32 numDim, + /* [size_is][out] */ ULONG32 dims[ ], + /* [in] */ ULONG32 numBases, + /* [size_is][out] */ LONG32 bases[ ]); + + virtual HRESULT STDMETHODCALLTYPE GetArrayElement( + /* [in] */ ULONG32 numInd, + /* [size_is][in] */ LONG32 indices[ ], + /* [out] */ IXCLRDataValue **value); + + virtual HRESULT STDMETHODCALLTYPE GetNumLocations( + /* [out] */ ULONG32* numLocs); + + virtual HRESULT STDMETHODCALLTYPE GetLocationByIndex( + /* [in] */ ULONG32 loc, + /* [out] */ ULONG32* flags, + /* [out] */ CLRDATA_ADDRESS* arg); + + virtual HRESULT STDMETHODCALLTYPE Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer); + + HRESULT GetRefAssociatedValue(IXCLRDataValue** assocValue); + + static HRESULT NewFromFieldDesc(ClrDataAccess* dac, + AppDomain* appDomain, + ULONG32 flags, + FieldDesc* fieldDesc, + ULONG64 objBase, + Thread* tlsThread, + ClrDataValue** value, + IXCLRDataValue** pubValue, + ULONG32 nameBufRetLen, + ULONG32* nameLenRet, + __out_ecount_part_opt(nameBufRetLen, *nameLenRet) WCHAR nameBufRet[ ], + IXCLRDataModule** tokenScopeRet, + mdFieldDef* tokenRet); + + HRESULT NewFromSubField(FieldDesc* fieldDesc, + ULONG32 flags, + ClrDataValue** value, + IXCLRDataValue** pubValue, + ULONG32 nameBufRetLen, + ULONG32* nameLenRet, + __out_ecount_part_opt(nameBufRetLen, *nameLenRet) WCHAR nameBufRet[ ], + IXCLRDataModule** tokenScopeRet, + mdFieldDef* tokenRet) + { + return ClrDataValue::NewFromFieldDesc(m_dac, + m_appDomain, + flags, + fieldDesc, + m_baseAddr, + m_thread, + value, + pubValue, + nameBufRetLen, + nameLenRet, + nameBufRet, + tokenScopeRet, + tokenRet); + } + + bool CanHaveFields(void) + { + return (m_flags & CLRDATA_VALUE_IS_REFERENCE) == 0; + } + + HRESULT IntGetBytes( + /* [in] */ ULONG32 bufLen, + /* [size_is][out] */ BYTE buffer[ ]); + +private: + LONG m_refs; + ClrDataAccess* m_dac; + ULONG32 m_instanceAge; + AppDomain* m_appDomain; + Thread* m_thread; + ULONG32 m_flags; + TypeHandle m_typeHandle; + ULONG64 m_totalSize; + ULONG64 m_baseAddr; + ULONG32 m_numLocs; + NativeVarLocation m_locs[MAX_NATIVE_VAR_LOCS]; +}; + +//---------------------------------------------------------------------------- +// +// EnumMethodDefinitions. +// +//---------------------------------------------------------------------------- + +class EnumMethodDefinitions +{ +public: + HRESULT Start(Module* mod, + bool useAddrFilter, + CLRDATA_ADDRESS addrFilter); + HRESULT Next(ClrDataAccess* dac, + IXCLRDataMethodDefinition **method); + + static HRESULT CdStart(Module* mod, + bool useAddrFilter, + CLRDATA_ADDRESS addrFilter, + CLRDATA_ENUM* handle); + static HRESULT CdNext(ClrDataAccess* dac, + CLRDATA_ENUM* handle, + IXCLRDataMethodDefinition** method); + static HRESULT CdEnd(CLRDATA_ENUM handle); + + Module* m_module; + bool m_useAddrFilter; + CLRDATA_ADDRESS m_addrFilter; + MetaEnum m_typeEnum; + mdToken m_typeToken; + bool m_needMethodStart; + MetaEnum m_methodEnum; +}; + +//---------------------------------------------------------------------------- +// +// EnumMethodInstances. +// +//---------------------------------------------------------------------------- + +class EnumMethodInstances +{ +public: + EnumMethodInstances(MethodDesc* methodDesc, + IXCLRDataAppDomain* givenAppDomain); + + HRESULT Next(ClrDataAccess* dac, + IXCLRDataMethodInstance **instance); + + static HRESULT CdStart(MethodDesc* methodDesc, + IXCLRDataAppDomain* appDomain, + CLRDATA_ENUM* handle); + static HRESULT CdNext(ClrDataAccess* dac, + CLRDATA_ENUM* handle, + IXCLRDataMethodInstance** method); + static HRESULT CdEnd(CLRDATA_ENUM handle); + + MethodDesc* m_methodDesc; + AppDomain* m_givenAppDomain; + bool m_givenAppDomainUsed; + AppDomainIterator m_domainIter; + AppDomain* m_appDomain; + LoadedMethodDescIterator m_methodIter; +}; + +//---------------------------------------------------------------------------- +// +// Internal functions. +// +//---------------------------------------------------------------------------- + +#define DAC_ENTER() \ + EnterCriticalSection(&g_dacCritSec); \ + ClrDataAccess* __prevDacImpl = g_dacImpl; \ + g_dacImpl = this; + +// When entering a child object we validate that +// the process's host instance cache hasn't been flushed +// since the child was created. +#define DAC_ENTER_SUB(dac) \ + EnterCriticalSection(&g_dacCritSec); \ + if (dac->m_instanceAge != m_instanceAge) \ + { \ + LeaveCriticalSection(&g_dacCritSec); \ + return E_INVALIDARG; \ + } \ + ClrDataAccess* __prevDacImpl = g_dacImpl; \ + g_dacImpl = (dac) + +#define DAC_LEAVE() \ + g_dacImpl = __prevDacImpl; \ + LeaveCriticalSection(&g_dacCritSec) + + +#define SOSHelperEnter() \ + DAC_ENTER_SUB(mDac); \ + HRESULT hr = S_OK; \ + EX_TRY \ + { + +#define SOSHelperLeave() \ + } \ + EX_CATCH \ + { \ + if (!DacExceptionFilter(GET_EXCEPTION(), mDac, &hr)) \ + { \ + EX_RETHROW; \ + } \ + } \ + EX_END_CATCH(SwallowAllExceptions) \ + DAC_LEAVE(); + +HRESULT DacGetHostVtPtrs(void); +bool DacExceptionFilter(Exception* ex, ClrDataAccess* process, + HRESULT* status); +Thread* __stdcall DacGetThread(ULONG32 osThread); +BOOL DacGetThreadContext(Thread* thread, T_CONTEXT* context); + +// Imports from request_svr.cpp, to provide data we need from the SVR namespace +int GCHeapCount(); +HRESULT GetServerHeapData(CLRDATA_ADDRESS addr, DacpHeapSegmentData *pSegment); +HRESULT GetServerHeaps(CLRDATA_ADDRESS pGCHeaps[], ICorDebugDataTarget* pTarget); + +#if defined(DAC_MEASURE_PERF) + +#if defined(_TARGET_X86_) + +// Assume Pentium CPU + +#define CCNT_OVERHEAD_32 8 +#define CCNT_OVERHEAD 13 + +#pragma warning( disable: 4035 ) /* Don't complain about lack of return value */ + +__inline unsigned __int64 GetCycleCount () +{ +__asm _emit 0x0F +__asm _emit 0x31 /* rdtsc */ + // return EDX:EAX causes annoying warning +}; + +__inline unsigned GetCycleCount32 () // enough for about 40 seconds +{ + LIMITED_METHOD_CONTRACT; + +__asm push EDX +__asm _emit 0x0F +__asm _emit 0x31 /* rdtsc */ +__asm pop EDX + // return EAX causes annoying warning +}; + +#pragma warning( default: 4035 ) + +#else // #if defined(_TARGET_X86_) + +#define CCNT_OVERHEAD 0 // Don't know + +__inline unsigned __int64 GetCycleCount() +{ + LIMITED_METHOD_CONTRACT; + + LARGE_INTEGER qwTmp; + QueryPerformanceCounter(&qwTmp); + return qwTmp.QuadPart; +} + +#endif // #if defined(_TARGET_X86_) + +extern unsigned __int64 g_nTotalTime; +extern unsigned __int64 g_nStackTotalTime; +extern unsigned __int64 g_nReadVirtualTotalTime; +extern unsigned __int64 g_nFindTotalTime; +extern unsigned __int64 g_nFindHashTotalTime; +extern unsigned __int64 g_nFindHits; +extern unsigned __int64 g_nFindCalls; +extern unsigned __int64 g_nFindFails; +extern unsigned __int64 g_nStackWalk; +extern unsigned __int64 g_nFindStackTotalTime; + +#endif // #if defined(DAC_MEASURE_PERF) + +#endif // #ifndef __DACIMPL_H__ diff --git a/src/debug/daccess/datatargetadapter.cpp b/src/debug/daccess/datatargetadapter.cpp new file mode 100644 index 0000000000..f2a1cc652a --- /dev/null +++ b/src/debug/daccess/datatargetadapter.cpp @@ -0,0 +1,255 @@ +// 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. +//***************************************************************************** +// DataTargetAdapter.cpp +// + +// +// implementation of compatibility adapter for ICLRDataTarget +//***************************************************************************** + +#include "stdafx.h" +#include "datatargetadapter.h" +#include <clrdata.h> +#include "dacimpl.h" + +#ifndef IMAGE_FILE_MACHINE_ARM64 +#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian +#endif + +// +// DataTargetAdaptor ctor +// +// Instantiate a DataTargetAdapter over the supplied legacy DataTarget interface. +// Takes a ref on the supplied interface and releases it in our dtor. +// +DataTargetAdapter::DataTargetAdapter(ICLRDataTarget * pLegacyTarget) : + m_ref(0), + m_pLegacyTarget(pLegacyTarget) +{ + m_pLegacyTarget->AddRef(); +} + +// +// DataTargetAdapter dtor +// +// Releases the underlying DataTarget interface +// +DataTargetAdapter::~DataTargetAdapter() +{ + m_pLegacyTarget->Release(); +} + +// Standard impl of IUnknown::QueryInterface +HRESULT STDMETHODCALLTYPE +DataTargetAdapter::QueryInterface( + REFIID interfaceId, + PVOID* pInterface) +{ + if (interfaceId == IID_IUnknown) + { + *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugDataTarget *>(this)); + } + else if (interfaceId == IID_ICorDebugDataTarget) + { + *pInterface = static_cast<ICorDebugDataTarget *>(this); + } + else if (interfaceId == IID_ICorDebugMutableDataTarget) + { + // Note that we always implement the mutable interface, even though our underlying target + // may return E_NOTIMPL for all the functions on this interface. There is no reliable way + // to tell apriori whether an ICLRDataTarget instance supports writing or not. + *pInterface = static_cast<ICorDebugMutableDataTarget *>(this); + } + else + { + // For ICorDebugDataTarget4 and other interfaces directly implemented by the legacy data target. + return m_pLegacyTarget->QueryInterface(interfaceId, pInterface); + } + + AddRef(); + return S_OK; +} + +// Standard impl of IUnknown::AddRef +ULONG STDMETHODCALLTYPE +DataTargetAdapter::AddRef() +{ + LONG ref = InterlockedIncrement(&m_ref); + return ref; +} + +// Standard impl of IUnknown::Release +ULONG STDMETHODCALLTYPE +DataTargetAdapter::Release() +{ + SUPPORTS_DAC_HOST_ONLY; + LONG ref = InterlockedDecrement(&m_ref); + if (ref == 0) + { + delete this; + } + return ref; +} + +// impl of interface method ICorDebugDataTarget::GetPlatform +HRESULT STDMETHODCALLTYPE +DataTargetAdapter::GetPlatform( + CorDebugPlatform * pPlatform) +{ + SUPPORTS_DAC_HOST_ONLY; + + // Get the target machine type, and assume it's Windows + HRESULT hr; + + ULONG32 ulMachineType; + IfFailRet(m_pLegacyTarget->GetMachineType(&ulMachineType)); + + ULONG32 ulExpectedPointerSize; + CorDebugPlatform platform; + + switch(ulMachineType) + { +#ifdef FEATURE_PAL + case IMAGE_FILE_MACHINE_I386: + ulExpectedPointerSize = 4; + platform = CORDB_PLATFORM_POSIX_X86; + break; + + case IMAGE_FILE_MACHINE_AMD64: + ulExpectedPointerSize = 8; + platform = CORDB_PLATFORM_POSIX_AMD64; + break; + + case IMAGE_FILE_MACHINE_ARMNT: + ulExpectedPointerSize = 4; + platform = CORDB_PLATFORM_POSIX_ARM; + break; + + case IMAGE_FILE_MACHINE_IA64: + case IMAGE_FILE_MACHINE_ARM64: + _ASSERTE_MSG(false, "Not supported platform."); + return E_NOTIMPL; + +#else // FEATURE_PAL + case IMAGE_FILE_MACHINE_I386: + ulExpectedPointerSize = 4; + platform = CORDB_PLATFORM_WINDOWS_X86; + break; + + case IMAGE_FILE_MACHINE_AMD64: + ulExpectedPointerSize = 8; + platform = CORDB_PLATFORM_WINDOWS_AMD64; + break; + + case IMAGE_FILE_MACHINE_IA64: + ulExpectedPointerSize = 8; + platform = CORDB_PLATFORM_WINDOWS_IA64; + break; + + case IMAGE_FILE_MACHINE_ARMNT: + ulExpectedPointerSize = 4; + platform = CORDB_PLATFORM_WINDOWS_ARM; + break; + + case IMAGE_FILE_MACHINE_ARM64: + ulExpectedPointerSize = 8; + platform = CORDB_PLATFORM_WINDOWS_ARM64; + break; +#endif // FEATURE_PAL + + default: + // No other platforms are current supported + return E_NOTIMPL; + } + + // Validate that the target pointer size matches + ULONG32 ulPointerSize; + IfFailRet(m_pLegacyTarget->GetPointerSize(&ulPointerSize)); + + if (ulPointerSize != ulExpectedPointerSize) + { + return E_UNEXPECTED; + } + + // Found a match + *pPlatform = platform; + return S_OK; +} + +// impl of interface method ICorDebugDataTarget::ReadVirtual +HRESULT STDMETHODCALLTYPE +DataTargetAdapter::ReadVirtual( + CORDB_ADDRESS address, + PBYTE pBuffer, + ULONG32 cbRequestSize, + ULONG32 * pcbRead) +{ + SUPPORTS_DAC_HOST_ONLY; + CLRDATA_ADDRESS cdAddr = TO_CDADDR(address); + return m_pLegacyTarget->ReadVirtual(cdAddr, pBuffer, cbRequestSize, pcbRead); +} + +// impl of interface method ICorDebugMutableDataTarget::WriteVirtual +HRESULT STDMETHODCALLTYPE +DataTargetAdapter::WriteVirtual( + CORDB_ADDRESS address, + const BYTE * pBuffer, + ULONG32 cbRequestSize) +{ + SUPPORTS_DAC_HOST_ONLY; + CLRDATA_ADDRESS cdAddr = TO_CDADDR(address); + ULONG32 cbWritten = 0; + HRESULT hr = S_OK; + + hr = m_pLegacyTarget->WriteVirtual(cdAddr, const_cast<BYTE *>(pBuffer), cbRequestSize, &cbWritten); + + if (SUCCEEDED(hr) && cbWritten != cbRequestSize) + { + // This shouldn't happen - existing data target implementations make writes atomic (eg. + // WriteProcessMemory), even though that isn't strictly required by the old interface. + // If this does happen, we technically leave the process in an inconsistent state, and we make no + // attempt to recover from that here. + _ASSERTE_MSG(false, "Legacy data target WriteVirtual partial write - target left in inconsistent state"); + return HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY); + } + return hr; +} + + +// impl of interface method ICorDebugDataTarget::GetThreadContext +HRESULT STDMETHODCALLTYPE +DataTargetAdapter::GetThreadContext( + DWORD dwThreadID, + ULONG32 contextFlags, + ULONG32 contextSize, + PBYTE pContext) +{ + SUPPORTS_DAC_HOST_ONLY; + return m_pLegacyTarget->GetThreadContext(dwThreadID, contextFlags, contextSize, pContext); +} + +// impl of interface method ICorDebugMutableDataTarget::SetThreadContext +HRESULT STDMETHODCALLTYPE +DataTargetAdapter::SetThreadContext( + DWORD dwThreadID, + ULONG32 contextSize, + const BYTE * pContext) +{ + SUPPORTS_DAC_HOST_ONLY; + return m_pLegacyTarget->SetThreadContext(dwThreadID, contextSize, const_cast<BYTE *>(pContext)); +} + +// implementation of ICorDebugMutableDataTarget::ContinueStatusChanged +HRESULT STDMETHODCALLTYPE +DataTargetAdapter::ContinueStatusChanged( + DWORD dwThreadId, + CORDB_CONTINUE_STATUS continueStatus) +{ + SUPPORTS_DAC_HOST_ONLY; + // No corresponding API in pre-arrowhead ICLRDataTarget* interfaces. + // Note that we briefly had a ICLRDataTarget4 with this API, but this was never released outside the CLR so + // all existing implementations should now be gone. + return E_NOTIMPL; +} diff --git a/src/debug/daccess/datatargetadapter.h b/src/debug/daccess/datatargetadapter.h new file mode 100644 index 0000000000..d2620c88e1 --- /dev/null +++ b/src/debug/daccess/datatargetadapter.h @@ -0,0 +1,84 @@ +// 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. +//***************************************************************************** +// DataTargetAdapter.h +// + +// +// header for compatibility adapter for ICLRDataTarget +//***************************************************************************** + +#ifndef DATATARGETADAPTER_H_ +#define DATATARGETADAPTER_H_ + +#include <cordebug.h> + +// Forward decl to avoid including clrdata.h here (it's use is being deprecated) +interface ICLRDataTarget; + +/* + * DataTargetAdapter - implements the new ICorDebugDataTarget interfaces + * by wrapping legacy ICLRDataTarget implementations. New code should use + * ICorDebugDataTarget, but we must continue to support ICLRDataTarget + * for dbgeng (watson, windbg, etc.) and for any other 3rd parties since + * it is a documented API for dump generation. + */ +class DataTargetAdapter : public ICorDebugMutableDataTarget +{ +public: + // Create an adapter over the supplied legacy data target interface + DataTargetAdapter(ICLRDataTarget * pLegacyTarget); + virtual ~DataTargetAdapter(); + + // + // IUnknown. + // + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void** ppInterface); + + virtual ULONG STDMETHODCALLTYPE AddRef(); + + virtual ULONG STDMETHODCALLTYPE Release(); + + // + // ICorDebugMutableDataTarget. + // + + virtual HRESULT STDMETHODCALLTYPE GetPlatform( + CorDebugPlatform *pPlatform); + + virtual HRESULT STDMETHODCALLTYPE ReadVirtual( + CORDB_ADDRESS address, + PBYTE pBuffer, + ULONG32 request, + ULONG32 *pcbRead); + + virtual HRESULT STDMETHODCALLTYPE WriteVirtual( + CORDB_ADDRESS address, + const BYTE * pBuffer, + ULONG32 request); + + virtual HRESULT STDMETHODCALLTYPE GetThreadContext( + DWORD dwThreadID, + ULONG32 contextFlags, + ULONG32 contextSize, + PBYTE context); + + virtual HRESULT STDMETHODCALLTYPE SetThreadContext( + DWORD dwThreadID, + ULONG32 contextSize, + const BYTE * context); + + virtual HRESULT STDMETHODCALLTYPE ContinueStatusChanged( + DWORD dwThreadId, + CORDB_CONTINUE_STATUS continueStatus); + +private: + LONG m_ref; // Reference count. + ICLRDataTarget * m_pLegacyTarget; // underlying data target +}; + + +#endif //DATATARGETADAPTER_H_ diff --git a/src/debug/daccess/dirs.proj b/src/debug/daccess/dirs.proj new file mode 100644 index 0000000000..795d462778 --- /dev/null +++ b/src/debug/daccess/dirs.proj @@ -0,0 +1,19 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <PropertyGroup> + <BuildInPhase1>true</BuildInPhase1> + <BuildInPhaseDefault>false</BuildInPhaseDefault> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + </PropertyGroup> + + <!--The following projects will build during PHASE 1--> + <ItemGroup Condition="'$(BuildExePhase)' == '1'"> + <ProjectFile Include="HostLocal\daccess.nativeproj" /> + </ItemGroup> + + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/debug/daccess/enummem.cpp b/src/debug/daccess/enummem.cpp new file mode 100644 index 0000000000..068c2f2b13 --- /dev/null +++ b/src/debug/daccess/enummem.cpp @@ -0,0 +1,2057 @@ +// 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: enummem.cpp +// + +// +// ICLRDataEnumMemoryRegions implementation. +// +//***************************************************************************** + +#include "stdafx.h" + +#include <eeconfig.h> +#include <ecall.h> +#include <gcinfodecoder.h> + +#include "typestring.h" +#include "daccess.h" +#include "ipcmanagerinterface.h" +#include "binder.h" +#include "win32threadpool.h" +#include "gcscan.h" + +#ifdef FEATURE_APPX +#include "appxutil.h" +#endif // FEATURE_APPX + +#if defined(DAC_MEASURE_PERF) + +unsigned __int64 g_nTotalTime; +unsigned __int64 g_nStackTotalTime; +unsigned __int64 g_nReadVirtualTotalTime; +unsigned __int64 g_nFindTotalTime; +unsigned __int64 g_nFindHashTotalTime; +unsigned __int64 g_nFindHits; +unsigned __int64 g_nFindCalls; +unsigned __int64 g_nFindFails; +unsigned __int64 g_nStackWalk; +unsigned __int64 g_nFindStackTotalTime; + +#endif // #if defined(DAC_MEASURE_PERF) + + +// +// EnumMemCollectImages - collect all images of interest for heap dumps +// +// This is used primarily to save ngen images. +// This is necessary so that heap dumps contain the full native code for the +// process. Normally mini/heap dump debugging requires that the images be +// available at debug-time, (in fact, watson explicitly does not want to +// be downloading 3rd party images). Not including images is the main size +// advantage of heap dumps over full dumps. However, since ngen images are +// produced on the client, we can't always ensure that the debugger will +// have access to the exact ngen image used in the dump. Therefore, managed +// heap dumps also include full copies of all NGen images in the process. +// +// We also currently include in-memory modules (provided by a host, or loaded +// from a Byte[]). +// +HRESULT ClrDataAccess::EnumMemCollectImages() +{ + SUPPORTS_DAC; + + ProcessModIter modIter; + Module* modDef = NULL; + HRESULT status = S_OK; + PEFile *file; + TADDR pStartAddr = 0; + ULONG32 ulSize = 0; + ULONG32 ulSizeBlock; + + TSIZE_T cbMemoryReported = m_cbMemoryReported; + + // + // Collect the ngen images - Iterating through module list + // + EX_TRY + { + while ((modDef = modIter.NextModule())) + { + EX_TRY + { + ulSize = 0; + file = modDef->GetFile(); + + // We want to save all native images + if (file->HasNativeImage()) + { + // We should only skip if signed by Microsoft! + pStartAddr = PTR_TO_TADDR(file->GetLoadedNative()->GetBase()); + ulSize = file->GetLoadedNative()->GetSize(); + } + // We also want to save any in-memory images. These show up like mapped files + // and so would not be in a heap dump by default. Technically it's not clear we + // should include them in the dump - you can often have the files available + // after-the-fact. But in-memory modules may be harder to track down at debug time + // and people may have come to rely on this - so we'll include them for now. + else + if ( + // With Copy On Write feature enabled + // IL images would not be dumped as part of the private pages. + // We need to explicitly dump them here. +#ifndef FEATURE_LAZY_COW_PAGES + file->GetPath().IsEmpty() && // is in-memory +#endif // FEATURE_LAZY_COW_PAGES + file->HasMetadata() && // skip resource assemblies + file->IsLoaded(FALSE) && // skip files not yet loaded + !file->IsDynamic()) // skip dynamic (GetLoadedIL asserts anyway) + { + pStartAddr = PTR_TO_TADDR(file->GetLoadedIL()->GetBase()); + ulSize = file->GetLoadedIL()->GetSize(); + } + + // memory are mapped in in OS_PAGE_SIZE size. + // Some memory are mapped in but some are not. You cannot + // write all in one block. So iterating through page size + // + while (ulSize > 0) + { + // + // Note that we have talked about not writing IL and Metadata to save size. + // It turns out IL was rarely mapped in. + // Metadata is needed. The RVA field is needed for it is pointed to a + // MethodHeader MethodDesc::GetILHeader. Without this RVA, + // all locals are broken. In case, you are asked about this question again. + // + ulSizeBlock = ulSize > OS_PAGE_SIZE ? OS_PAGE_SIZE : ulSize; + ReportMem(pStartAddr, ulSizeBlock, false); + pStartAddr += ulSizeBlock; + ulSize -= ulSizeBlock; + } + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + } + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + m_dumpStats.m_cbNgen = m_cbMemoryReported - cbMemoryReported; + return status; +} + + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// collecting memory for mscorwks's heap dump critical statics +// This include the stress log, config structure, and IPC block +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemCLRHeapCrticalStatic(IN CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + TSIZE_T cbMemoryReported = m_cbMemoryReported; + + // Write out the stress log structure itself + DacEnumHostDPtrMem(g_pStressLog); + + // This is pointing to a static buffer + DacEnumHostDPtrMem(g_pConfig); + + // dump GC heap structures. Note that the managed heap is not dumped out. + // We are just dump the GC heap structures. + // + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( EnumWksGlobalMemoryRegions(flags); ); +#ifdef FEATURE_SVR_GC + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( EnumSvrGlobalMemoryRegions(flags); ); +#endif + +#ifdef FEATURE_IPCMAN + // + // Write Out IPC Blocks + // + EX_TRY + { + g_pIPCManagerInterface.EnumMem(); + if (g_pIPCManagerInterface.IsValid()) + { + // write out the instance + DacEnumHostDPtrMem(g_pIPCManagerInterface); + + // Then write out the public and private block + ReportMem(PTR_TO_TADDR(g_pIPCManagerInterface->GetBlockStart()), g_pIPCManagerInterface->GetBlockSize()); + ReportMem(PTR_TO_TADDR(g_pIPCManagerInterface->GetBlockTableStart()), g_pIPCManagerInterface->GetBlockTableSize()); + } + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED +#endif // FEATURE_IPCMAN + + m_dumpStats.m_cbClrHeapStatics = m_cbMemoryReported - cbMemoryReported; + + return S_OK; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// collecting memory for mscorwks's statics. This is the minimal +// set of global and statics that we need to have !threads, !pe, !ClrStack +// to work. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemCLRStatic(IN CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + TSIZE_T cbMemoryReported = m_cbMemoryReported; + + // + // write out the static and global content that we care. + // + + // The followig macro will report memory all of the dac related mscorwks static and + // global variables. But it won't report the structures that are pointed by + // global pointers. + // +#define DEFINE_DACVAR(id_type, size_type, id, var) \ + ReportMem(m_globalBase + g_dacGlobals.id, sizeof(size_type)); + +#define DEFINE_DACVAR_SVR(id_type, size_type, id, var) \ + ReportMem(m_globalBase + g_dacGlobals.id, sizeof(size_type)); + + // Cannot use CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED + // around conditional preprocessor directives in a sane fashion. + EX_TRY + { +#include "dacvars.h" + } + EX_CATCH + { + // Catch the exception and keep going unless COR_E_OPERATIONCANCELED + // was thrown. Used generating dumps, where rethrow will cancel dump. + } + EX_END_CATCH(RethrowCancelExceptions) + + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED + ( + // StressLog is not defined on Rotor build for DAC + ReportMem(m_globalBase + g_dacGlobals.dac__g_pStressLog, sizeof(StressLog *)); + ); + + EX_TRY + { + // These two static pointers are pointed to static data of byte[] + // then run constructor in place + // + ReportMem(m_globalBase + g_dacGlobals.SystemDomain__m_pSystemDomain, + sizeof(SystemDomain)); + ReportMem(m_globalBase + g_dacGlobals.SharedDomain__m_pSharedDomain, + sizeof(SharedDomain)); + + // We need GCHeap pointer to make EEVersion work + ReportMem(m_globalBase + g_dacGlobals.dac__g_pGCHeap, + sizeof(GCHeap *)); + + // see synblk.cpp, the pointer is pointed to a static byte[] + SyncBlockCache::s_pSyncBlockCache.EnumMem(); + +#ifndef FEATURE_IMPLICIT_TLS + ReportMem(m_globalBase + g_dacGlobals.dac__gThreadTLSIndex, + sizeof(DWORD)); + ReportMem(m_globalBase + g_dacGlobals.dac__gAppDomainTLSIndex, + sizeof(DWORD)); +#endif + + ReportMem( m_globalBase + g_dacGlobals.dac__g_FCDynamicallyAssignedImplementations, + sizeof(TADDR)*ECall::NUM_DYNAMICALLY_ASSIGNED_FCALL_IMPLEMENTATIONS); + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + +#ifndef FEATURE_PAL + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_runtimeLoadedBaseAddress.EnumMem(); ) +#endif // !FEATURE_PAL + + // These are the structures that are pointed by global pointers and we care. + // Some may reside in heap and some may reside as a static byte array in mscorwks.dll + // That is ok. We will report them explicitly. + // + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pConfig.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pPredefinedArrayTypes.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pObjectClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pStringClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pArrayClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pExceptionClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pThreadAbortExceptionClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pOutOfMemoryExceptionClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pStackOverflowExceptionClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pExecutionEngineExceptionClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pDelegateClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pMulticastDelegateClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pValueTypeClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pEnumClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pThreadClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pCriticalFinalizerObjectClass.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pFreeObjectMethodTable.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pObjectCtorMD.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_fHostConfig.EnumMem(); ) + + // These two static pointers are pointed to static data of byte[] + // then run constructor in place + // + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( SystemDomain::m_pSystemDomain.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( SharedDomain::m_pSharedDomain.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pDebugger.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pEEInterface.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pDebugInterface.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pEEDbgInterfaceImpl.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_CORDebuggerControlFlags.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_Mscorlib.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT]->EnumMemoryRegions(flags); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( StubManager::EnumMemoryRegions(flags); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pFinalizerThread.EnumMem(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pSuspensionThread.EnumMem(); ) + +#ifdef FEATURE_SVR_GC + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED + ( + GCHeap::gcHeapType.EnumMem(); + ); +#endif // FEATURE_SVR_GC + + m_dumpStats.m_cbClrStatics = m_cbMemoryReported - cbMemoryReported; + + return S_OK; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// This function reports memory that a heap dump need to debug CLR +// and managed code efficiently. +// +// We will write out - +// 1. mscorwks.dll's image read/write pages +// 2. IPC blocks - shared memory (needed for debugging service and perf counter) +// 3. ngen images excluding Metadata and IL for size perf +// 4. We may want to touch the code pages on the stack - to be safe.... +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemoryRegionsWorkerHeap(IN CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + HRESULT status = S_OK; + + m_instances.ClearEnumMemMarker(); + + // clear all of the previous cached memory + Flush(); + + // collect ngen image + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCollectImages(); ); + + // collect CLR static + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(flags); ); + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRHeapCrticalStatic(flags); ); + + // Note that we do not need to flush out all of the dac instance manager's instance. + // This is because it is a heap dump here. Assembly and AppDomain objects will be reported + // by the default heap collection mechanism by dbghelp.lib + // + // Microsoft: I suspect if we have all private read-write pages the preceding statement + // would be true, but I don't think we have that guarantee here. + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(flags); ); + +#ifdef FEATURE_LAZY_COW_PAGES + // Iterating to all threads' stacks, as we have to collect data stored inside (core)clr.dll + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(flags); ) + + // Dump AppDomain-specific info + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAppDomainInfo(flags); ) + + // Dump the Debugger object data needed + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pDebugger->EnumMemoryRegions(flags); ) + + // now dump the memory get dragged in by using DAC API implicitly. + m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb); +#endif // FEATURE_LAZY_COW_PAGES + + // end of code + status = m_memStatus; + + return S_OK; +} // EnumMemoryRegionsWorkerHeap + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Helper function for skinny mini-dump +// Pass in an managed object, this function will dump the EEClass hierachy +// and field desc of object so SOS's !DumpObj will work +// +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::DumpManagedObject(CLRDataEnumMemoryFlags flags, OBJECTREF objRef) +{ + SUPPORTS_DAC; + + HRESULT status = S_OK; + + if (objRef == NULL) + { + return status; + } + + if (!GCScan::GetGcRuntimeStructuresValid ()) + { + // GC is in progress, don't dump this object + return S_OK; + } + + EX_TRY + { + // write out the current EE class and the direct/indirect inherited EE Classes + MethodTable *pMethodTable = objRef->GetGCSafeMethodTable(); + + while (pMethodTable) + { + EX_TRY + { + pMethodTable->EnumMemoryRegions(flags); + + StackSString s; + + // This might look odd. We are not using variable s after forming it. + // That is because our DAC inspecting API is using TypeString to form + // full type name. Form the full name here is a implicit reference to needed + // memory. + // + TypeString::AppendType(s, TypeHandle(pMethodTable), TypeString::FormatNamespace|TypeString::FormatFullInst); + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + // Walk up to parent MethodTable + pMethodTable = pMethodTable->GetParentMethodTable(); + } + + // now dump the content for the managed object + objRef->EnumMemoryRegions(); + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + return status; +} + + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Helper function for skinny mini-dump +// Pass in an managed excption object, this function will dump +// the managed exception object and some of its fields, such as message, stack trace string, +// inner exception. +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::DumpManagedExcepObject(CLRDataEnumMemoryFlags flags, OBJECTREF objRef) +{ + SUPPORTS_DAC; + + if (objRef == NULL) + { + return S_OK; + } + + if (!GCScan::GetGcRuntimeStructuresValid ()) + { + // GC is in progress, don't dump this object + return S_OK; + } + + // write out the managed object for exception. This will only write out the + // direct field value. After this, we need to visit some selected fields, such as + // exception message and stack trace field, and dump out the object referenced via + // the fields. + // + DumpManagedObject(flags, objRef); + + // If this is not a pre-allocated exception type, then we'll need to dump out enough memory to ensure + // that the lookup codepath from the Module to information for the type of this Exception will + // be present. Simply dumping the managed object itself isn't enough. Sos doesn't need this. + EX_TRY + { + MethodTable * pMethodTable = objRef->GetGCSafeMethodTable(); + PTR_Module pModule = pMethodTable->GetModule(); + mdTypeDef exceptionTypeDef = pMethodTable->GetCl(); + + if (TypeFromToken(exceptionTypeDef) != mdtTypeDef) + { + _ASSERTE(!"Module should have contained a TypeDef, dump will likely be missing exception type lookup!"); + } + + // The lookup from the Module that contains this TypeDef: + pModule->LookupTypeDef(RidFromToken(exceptionTypeDef)); + + // If it's a generic class, we need to implicitly enumerate the memory needed to look it up + // and enable the calls that ICD will want to make against the TypeHandle when retrieving the + // Exception info. + TypeHandle th; + th = ClassLoader::LookupTypeDefOrRefInModule(pModule, exceptionTypeDef); + th.EnumMemoryRegions(flags); + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + // store the exception type name + EX_TRY + { + MethodTable * pMethodTable = objRef->GetGCSafeMethodTable(); + StackSString s; + TypeString::AppendType(s, TypeHandle(pMethodTable), TypeString::FormatNamespace|TypeString::FormatFullInst); + DacMdCacheAddEEName(dac_cast<TADDR>(pMethodTable), s); + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + EXCEPTIONREF exceptRef = (EXCEPTIONREF)objRef; + + if (flags != CLRDATA_ENUM_MEM_TRIAGE) + { + // dump the exception message field + DumpManagedObject(flags, (OBJECTREF)exceptRef->GetMessage()); + } + + // dump the exception's stack trace field + DumpManagedStackTraceStringObject(flags, exceptRef->GetStackTraceString()); + + // dump the exception's remote stack trace field only if we are not generating a triage dump, or + // if we are generating a triage dump of an AppX process, or the exception type does not override + // the StackTrace getter (see Exception.InternalPreserveStackTrace to understand why) + if (flags != CLRDATA_ENUM_MEM_TRIAGE || +#ifdef FEATURE_APPX + AppX::DacIsAppXProcess() || +#endif // FEATURE_APPX + !ExceptionTypeOverridesStackTraceGetter(exceptRef->GetGCSafeMethodTable())) + { + DumpManagedStackTraceStringObject(flags, exceptRef->GetRemoteStackTraceString()); + } + + // Dump inner exception + DumpManagedExcepObject(flags, exceptRef->GetInnerException()); + + // Dump the stack trace array object and its underlying type + I1ARRAYREF stackTraceArrayObj = exceptRef->GetStackTraceArrayObject(); + + // There are cases where a managed exception does not have a stack trace. + // These cases are: + // * exception was thrown by VM and no managed frames are on the thread. + // * exception thrown is a preallocated exception. + if (stackTraceArrayObj != NULL) + { + // first dump the array's element type + TypeHandle arrayTypeHandle = stackTraceArrayObj->GetTypeHandle(); + ArrayTypeDesc* pArrayTypeDesc = arrayTypeHandle.AsArray(); + TypeHandle elementTypeHandle = pArrayTypeDesc->GetArrayElementTypeHandle(); + elementTypeHandle.AsMethodTable()->EnumMemoryRegions(flags); + elementTypeHandle.AsMethodTable()->GetClass()->EnumMemoryRegions(flags, elementTypeHandle.AsMethodTable()); + + // now dump the actual stack trace array object + DumpManagedObject(flags, (OBJECTREF)stackTraceArrayObj); + } + + // Dump the stack trace native structure. Unfortunately, we need to write out the + // native structure and also dump the MethodDesc that we care about! + // We need to ensure the entire _stackTrace from the Exception is enumerated and + // included in the dump. When we touch the header and each element looking for the + // MD this happens. + StackTraceArray stackTrace; + exceptRef->GetStackTrace(stackTrace); + for(size_t i = 0; i < stackTrace.Size(); i++) + { + MethodDesc* pMD = stackTrace[i].pFunc; + if (!DacHasMethodDescBeenEnumerated(pMD) && DacValidateMD(pMD)) + { + pMD->EnumMemoryRegions(flags); + + // The following calls are to ensure that mscordacwks!DacDbiInterfaceImpl::GetNativeCodeInfo + // will succeed for all dumps. + + // Pulls in data to translate from token to MethodDesc + FindLoadedMethodRefOrDef(pMD->GetMethodTable()->GetModule(), pMD->GetMemberDef()); + + // Pulls in sequence points. + DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, pMD); + PCODE addr = pMD->GetNativeCode(); + if (addr != NULL) + { + IJitManager::MethodRegionInfo methodRegionInfo = { NULL, 0, NULL, 0 }; + EECodeInfo codeInfo(addr); + codeInfo.GetMethodRegionInfo(&methodRegionInfo); + } + } + + // Enumerate the code around call site to help SOS resolve the source lines + TADDR callEnd = PCODEToPINSTR(stackTrace[i].ip); + DacEnumCodeForStackwalk(callEnd); + } + + return S_OK; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Helper function for skinny mini-dump +// Pass in a string object representing a managed stack trace, this function will +// dump it and "poison" the contents with a PII-free version of the stack trace. +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::DumpManagedStackTraceStringObject(CLRDataEnumMemoryFlags flags, STRINGREF orefStackTrace) +{ + SUPPORTS_DAC; + + if (orefStackTrace == NULL) + { + return S_OK; + } + + // dump the stack trace string object + DumpManagedObject(flags, (OBJECTREF)orefStackTrace); + + if (flags == CLRDATA_ENUM_MEM_TRIAGE) + { + // StringObject::GetSString does not support DAC, use GetBuffer/GetStringLength + SString stackTrace(dac_cast<PTR_WSTR>((TADDR)orefStackTrace->GetBuffer()), orefStackTrace->GetStringLength()); + + StripFileInfoFromStackTrace(stackTrace); + + COUNT_T traceCharCount = stackTrace.GetCount(); + _ASSERTE(traceCharCount <= orefStackTrace->GetStringLength()); + + // fill the rest of the string with \0 + WCHAR *buffer = stackTrace.OpenUnicodeBuffer(orefStackTrace->GetStringLength()); + memset(buffer + traceCharCount, 0, sizeof(WCHAR) * (orefStackTrace->GetStringLength() - traceCharCount)); + + // replace the string + DacUpdateMemoryRegion(dac_cast<TADDR>(orefStackTrace) + StringObject::GetBufferOffset(), sizeof(WCHAR) * orefStackTrace->GetStringLength(), (BYTE *)buffer); + } + + return S_OK; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Iterating through module list and report the memory. +// Remember to call +// m_instances.DumpAllInstances(m_enumMemCb); +// when all memory enumeration are done if you call this function! +// This is because using ProcessModIter will drag in some memory implicitly. +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemDumpModuleList(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + ProcessModIter modIter; + Module* modDef; + TADDR base; + ULONG32 length; + PEFile *file; + TSIZE_T cbMemoryReported = m_cbMemoryReported; +#ifdef FEATURE_PREJIT + COUNT_T count; +#endif // FEATURE_PREJIT + + // + // Iterating through module list + // + + // Cannot use CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED around + // conditional pre-processor directives in a sane fashion + EX_TRY + { + while ((modDef = modIter.NextModule())) + { + // We also want to dump the link from the Module back to the AppDomain, + // since the stackwalker uses it to find the AD. + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED + ( + // Pass false to ensure we force enumeration of this module's references. + modDef->EnumMemoryRegions(flags, false); + ); + + EX_TRY + { + // To enable a debugger to check on whether a module is an NI or IL image, they need + // the DOS header, PE headers, and IMAGE_COR20_HEADER for the Flags member. + // We expose no API today to find this out. + PTR_PEFile pPEFile = modDef->GetFile(); + PEImage * pILImage = pPEFile->GetILimage(); + PEImage * pNIImage = pPEFile->GetNativeImage(); + + // Implicitly gets the COR header. + if ((pILImage) && (pILImage->HasLoadedLayout())) + { + pILImage->GetCorHeaderFlags(); + } + if ((pNIImage) && (pNIImage->HasLoadedLayout())) + { + pNIImage->GetCorHeaderFlags(); + } + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + + EX_TRY + { + file = modDef->GetFile(); + base = PTR_TO_TADDR(file->GetLoadedImageContents(&length)); + file->EnumMemoryRegions(flags); +#ifdef FEATURE_PREJIT + + // If module has native image and it has debug map, we need to get the debug map. + // + if (modDef->HasNativeImage() && modDef->GetNativeImage()->HasNativeDebugMap()) + { + modDef->GetNativeImage()->GetNativeDebugMap(&count); + } +#endif // FEATURE_PREJIT + } + EX_CATCH + { + // Catch the exception and keep going unless COR_E_OPERATIONCANCELED + // was thrown. Used generating dumps, where rethrow will cancel dump. + } + EX_END_CATCH(RethrowCancelExceptions) + } + } + EX_CATCH + { + // Catch the exception and keep going unless COR_E_OPERATIONCANCELED + // was thrown. Used generating dumps, where rethrow will cancel dump. + } + EX_END_CATCH(RethrowCancelExceptions) + + m_dumpStats.m_cbModuleList = m_cbMemoryReported - cbMemoryReported; + + return S_OK; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Iterate through AppDomains and report specific memory needed +// for all dumps, such as the Module lookup path. +// This is intended for MiniDumpNormal and should be kept small. +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemDumpAppDomainInfo(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + AppDomainIterator adIter(FALSE); + EX_TRY + { + while (adIter.Next()) + { + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED + ( + // Note that the flags being CLRDATA_ENUM_MEM_MINI prevents + // you from pulling entire files loaded into memory into the dump. + adIter.GetDomain()->EnumMemoryRegions(flags, true); + ); + } + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + return S_OK; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Iterating through each frame to make sure +// we dump out MethodDesc, DJI etc related info +// This is a generic helper for walking stack. However, if you call +// this function, make sure to flush instance in the DAC Instance manager. +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemWalkStackHelper(CLRDataEnumMemoryFlags flags, + IXCLRDataStackWalk *pStackWalk, + Thread * pThread) +{ + SUPPORTS_DAC; + +#if defined(DAC_MEASURE_PERF) + g_nStackWalk = 1; + unsigned __int64 nStart= GetCycleCount(); +#endif + + HRESULT status = S_OK; + ReleaseHolder<IXCLRDataFrame> pFrame(NULL); + ReleaseHolder<IXCLRDataMethodInstance> pMethod(NULL); + ReleaseHolder<IXCLRDataMethodDefinition> pMethodDefinition(NULL); + ReleaseHolder<IXCLRDataTypeInstance> pTypeInstance(NULL); + + MethodDesc * pMethodDesc = NULL; + EX_TRY + { + TADDR previousSP = 0; //start at zero; this allows first check to always succeed. + TADDR currentSP; + currentSP = dac_cast<TADDR>(pThread->GetCachedStackLimit()) + sizeof(TADDR); + + // exhaust the frames using DAC api + bool frameHadContext; + for (; status == S_OK; ) + { + frameHadContext = false; + status = pStackWalk->GetFrame(&pFrame); + PCODE addr = NULL; + if (status == S_OK && pFrame != NULL) + { + // write out the code that ip pointed to + T_CONTEXT context; + REGDISPLAY regDisp; + if ((status=pFrame->GetContext(CONTEXT_ALL, sizeof(T_CONTEXT), + NULL, (BYTE *)&context))==S_OK) + { + // Enumerate the code around the call site to help debugger stack walking heuristics + ::FillRegDisplay(®Disp, &context); + addr = GetControlPC(®Disp); + TADDR callEnd = PCODEToPINSTR(addr); + DacEnumCodeForStackwalk(callEnd); + frameHadContext = true; + } + + // + // There are identical stack pointer checking semantics in code:Thread::EnumMemoryRegionsWorker + // See that code for comments. + // You ***MUST*** maintain identical semantics for both checks! + // + CLRDataSimpleFrameType simpleFrameType; + CLRDataDetailedFrameType detailedFrameType; + if (SUCCEEDED(pFrame->GetFrameType(&simpleFrameType, &detailedFrameType))) + { + if (!frameHadContext) + { + _ASSERTE(!"Stack frame should always have an associated context!"); + break; + } + + // This is StackFrameIterator::SFITER_FRAMELESS_METHOD, initialized by Code:ClrDataStackWalk::GetFrame + // from code:ClrDataStackWalk::RawGetFrameType + if (simpleFrameType == CLRDATA_SIMPFRAME_MANAGED_METHOD) + { + currentSP = (TADDR)GetRegdisplaySP(®Disp); + + if (currentSP <= previousSP) + { + _ASSERTE(!"Target stack has been corrupted, SP for current frame must be larger than previous frame."); + break; + } + + if (currentSP % sizeof(TADDR) != 0) + { + _ASSERTE(!"Target stack has been corrupted, SP must be aligned."); + break; + } + + if (!pThread->IsAddressInStack(currentSP)) + { + _ASSERTE(!"Target stack has been corrupted, SP must in in the stack range."); + break; + } + } + } + else + { + _ASSERTE(!"The stack frame should always know what type it is!"); + break; + } + + status = pFrame->GetMethodInstance(&pMethod); + if (status == S_OK && pMethod != NULL) + { + // managed frame + if (SUCCEEDED(pMethod->GetTypeInstance(&pTypeInstance)) && + (pTypeInstance != NULL)) + { + pTypeInstance.Clear(); + } + + if(SUCCEEDED(pMethod->GetDefinition(&pMethodDefinition)) && + (pMethodDefinition != NULL)) + { + pMethodDesc = ((ClrDataMethodDefinition *)pMethodDefinition.GetValue())->GetMethodDesc(); + if (pMethodDesc) + { + + // If this is a generic, we'll need to pull in enough extra info that + // we get decent results later when stackwalking. Note that we do not guarantee + // we'll always get an exact type for any reference type; most of the time the + // stack walk will just show System.__Canon, which is the level of support we + // guarantee for minidumps without full memory. + EX_TRY + { + if ((pMethodDesc->AcquiresInstMethodTableFromThis()) || + (pMethodDesc->RequiresInstMethodTableArg())) + { + // MethodTable + ReleaseHolder<IXCLRDataValue> pDV(NULL); + ReleaseHolder<IXCLRDataValue> pAssociatedValue(NULL); + CLRDATA_ADDRESS address; + PTR_Object pObjThis = NULL; + + if (SUCCEEDED(pFrame->GetArgumentByIndex(0, &pDV, 0, NULL, NULL)) && + SUCCEEDED(pDV->GetAssociatedValue(&pAssociatedValue)) && + SUCCEEDED(pAssociatedValue->GetAddress(&address))) + { + // Implicitly enumerate the object itself. + TADDR addrObjThis = CLRDATA_ADDRESS_TO_TADDR(address); + pObjThis = dac_cast<PTR_Object>(addrObjThis); + } + + // And now get the extra info we need for the AcquiresInstMethodTableFromThis case. + if (pMethodDesc->AcquiresInstMethodTableFromThis()) + { + // When working with the 'this' case, we need to pick up the MethodTable from + // object lookup. + PTR_MethodTable pMT = NULL; + if (pObjThis != NULL) + { + pMT = pObjThis->GetMethodTable(); + } + + TypeHandle th; + if (pMT != NULL) + { + th = TypeHandle(pMT); + } + + Instantiation classInst = pMethodDesc->GetExactClassInstantiation(th); + Instantiation methodInst = pMethodDesc->GetMethodInstantiation(); + } + + } + else if (pMethodDesc->RequiresInstMethodDescArg()) + { + // This method has a generic type token which is required to figure out the exact instantiation + // of the method. + // We need to to use the variable index of the generic type token in order to do the look up. + CLRDATA_ADDRESS address = NULL; + DWORD dwExactGenericArgsTokenIndex = 0; + ReleaseHolder<IXCLRDataValue> pDV(NULL); + ReleaseHolder<IXCLRDataValue> pAssociatedValue(NULL); + ReleaseHolder<IXCLRDataFrame2> pFrame2(NULL); + + if (SUCCEEDED(pFrame->QueryInterface(__uuidof(IXCLRDataFrame2), (void**)&pFrame2)) && + SUCCEEDED(pFrame2->GetExactGenericArgsToken(&pDV)) && + SUCCEEDED(pDV->GetAssociatedValue(&pAssociatedValue)) && + SUCCEEDED(pAssociatedValue->GetAddress(&address))) + { + TADDR addrMD = CLRDATA_ADDRESS_TO_TADDR(address); + PTR_MethodDesc pMD = dac_cast<PTR_MethodDesc>(addrMD); + pMD->EnumMemoryRegions(flags); + } + + pMethodDesc->EnumMemoryRegions(flags); + MethodTable * pCanonicalMT = pMethodDesc->GetCanonicalMethodTable(); + MethodTable * pNormalMT = pMethodDesc->GetMethodTable(); + pCanonicalMT->EnumMemoryRegions(flags); + pNormalMT->EnumMemoryRegions(flags); + } + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + pMethodDesc->EnumMemoryRegions(flags); + + // The following calls are to ensure that mscordacwks!DacDbiInterfaceImpl::GetNativeCodeSequencePointsAndVarInfo + // will succeed for all dumps. Local variable info usefulness is somewhat questionable + // since most dumps will be for optimized targets. However, being able to map + // back to source lines for functions on stacks is very useful and we don't + // want to allow the function to fail for all targets. + + // Pulls in sequence points and local variable info + DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, pMethodDesc); + +#ifdef WIN64EXCEPTIONS + + if (addr != NULL) + { + EECodeInfo codeInfo(addr); + + // We want IsFilterFunclet to work for anything on the stack + codeInfo.GetJitManager()->IsFilterFunclet(&codeInfo); + + // The stackwalker needs GC info to find the parent 'stack pointer' or PSP + GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken(); + PTR_BYTE pGCInfo = dac_cast<PTR_BYTE>(gcInfoToken.Info); + if (pGCInfo != NULL) + { + GcInfoDecoder gcDecoder(gcInfoToken, DECODE_PSP_SYM, 0); + DacEnumMemoryRegion(dac_cast<TADDR>(pGCInfo), gcDecoder.GetNumBytesRead(), true); + } + } +#endif // WIN64EXCEPTIONS + } + pMethodDefinition.Clear(); + } + pMethod.Clear(); + } + pFrame.Clear(); + } + + previousSP = currentSP; + status = pStackWalk->Next(); + } + + } + EX_CATCH + { + status = E_FAIL; + // Catch the exception and keep going unless a COR_E_OPERATIONCANCELED + // was thrown. In which case, rethrow to cancel the dump gathering + } + EX_END_CATCH(RethrowCancelExceptions) + +#if defined(DAC_MEASURE_PERF) + unsigned __int64 nEnd = GetCycleCount(); + g_nStackTotalTime += nEnd - nStart; + g_nStackWalk = 0; +#endif // #if defined(DAC_MEASURE_PERF) + + return status; +} + +// code: ClrDataAccess::EnumMemDumpAllThreadsStack needs a trivial implementation of +// an un-DACized container class to track what exceptions have happened so far. +// It shouldn't get used anywhere else. +class DebuggingExceptionTrackerList +{ +private: + + struct TrivialTADDRNode + { + TADDR m_exceptionAddress; + TrivialTADDRNode * m_pNext; + + TrivialTADDRNode(TrivialTADDRNode *pNext, TADDR address) + : m_exceptionAddress(address), m_pNext(pNext) + { + SUPPORTS_DAC_HOST_ONLY; + } + + private: + TrivialTADDRNode() { _ASSERTE(!"You should never call this ctor."); } + }; + + TrivialTADDRNode *m_pHead; + + bool Find(TADDR address) + { + SUPPORTS_DAC_HOST_ONLY; + for (TrivialTADDRNode *pFind = m_pHead; pFind != NULL; pFind = pFind->m_pNext) + if (pFind->m_exceptionAddress == address) + return true; + + return false; + } + +public: + DebuggingExceptionTrackerList() + : m_pHead(NULL) + { + SUPPORTS_DAC_HOST_ONLY; + } + + bool AddNewAddressOnly(TADDR address) + { + SUPPORTS_DAC_HOST_ONLY; + if (Find(address)) + { + return false; + } + else + { + TrivialTADDRNode *pNew = new TrivialTADDRNode(m_pHead, address); + m_pHead = pNew; + return true; + } + } + + ~DebuggingExceptionTrackerList() + { + SUPPORTS_DAC_HOST_ONLY; + for (TrivialTADDRNode *pTemp = m_pHead; m_pHead != NULL; pTemp = m_pHead) + { + m_pHead = m_pHead->m_pNext; + delete pTemp; + } + } +}; + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// This function will walk all threads, all the context in the +// exception state to report memory. This can also drag in memory implicitly. +// So do call +// m_instances.DumpAllInstances(m_enumMemCb); +// when function is done. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemDumpAllThreadsStack(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + +#ifdef FEATURE_COMINTEROP + // Dump the exception object stored in the WinRT stowed exception + EnumMemStowedException(flags); +#endif + + HRESULT status = S_OK; + TSIZE_T cbMemoryReported = m_cbMemoryReported; + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + // Duplicate the enumeration code below, to allow Exception stacks to be enumerated first. + // These exception stacks will get MethodDesc names cached to the DacStreamManager before + // MethodDescs residing on the "regular" callstacks + EX_TRY + { + DebuggingExceptionTrackerList exceptionTrackingInner; + + CLRDATA_ENUM handle; + ReleaseHolder<IXCLRDataTask> pIXCLRDataTask(NULL); + ReleaseHolder<IXCLRDataExceptionState> pExcepState(NULL); + Thread *pThread = NULL; + + // enumerating through each thread + StartEnumTasks(&handle); + status = EnumTask(&handle, &pIXCLRDataTask); + for (unsigned nbThreads = 0; status == S_OK && pIXCLRDataTask != NULL; nbThreads++) + { + // Avoid infinite loop if target process is corrupted. + if (nbThreads > 100000) + { + break; + } + EX_TRY + { + // get Thread * + pThread = ((ClrDataTask *)pIXCLRDataTask.GetValue())->GetThread(); + + // dump the exception object + DumpManagedExcepObject(flags, pThread->LastThrownObject()); + + // Now probe into the exception info + status = pIXCLRDataTask->GetCurrentExceptionState(&pExcepState); + while (status == S_OK && pExcepState != NULL) + { + EX_TRY + { + // touch the throwable in exception state + PTR_UNCHECKED_OBJECTREF throwRef(((ClrDataExceptionState *)pExcepState.GetValue())->m_throwable); + + // If we've already attempted enumeration for this exception, it's time to quit. + if (!exceptionTrackingInner.AddNewAddressOnly(throwRef.GetAddr())) + { + break; + } + + DumpManagedExcepObject(flags, *throwRef); + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + // get the previous exception + IXCLRDataExceptionState * pExcepStatePrev = NULL; + status = pExcepState->GetPrevious(&pExcepStatePrev); + + // Release our current exception object, and transfer ref ownership of the previous + // exception object into the holder. + pExcepState = pExcepStatePrev; + } + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + // get next thread + pIXCLRDataTask.Clear(); + status = EnumTask(&handle, &pIXCLRDataTask); + } + EndEnumTasks(handle); + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + // exceptionTracking is used for exactly that; it is a per-dump list of the + // addresses of all exceptions enumerated for this dump. If an exception is + // enumerated more than once it indicates that we have multiple threads pointing to + // the same object, or the same thread has an InnerException chain with a cycle. + // In either case, we need to terminate exception reporting. + DebuggingExceptionTrackerList exceptionTracking; + + EX_TRY + { + CLRDATA_ENUM handle; + ReleaseHolder<IXCLRDataTask> pIXCLRDataTask(NULL); + ReleaseHolder<IXCLRDataExceptionState> pExcepState(NULL); + ReleaseHolder<IXCLRDataStackWalk> pStackWalk(NULL); + Thread *pThread = NULL; + + // enumerating through each thread's each frame, dump out some interesting + // code memory needed to debugger to recognize frame + // + ThreadStore::EnumMemoryRegions(flags); + + // enumerating through each thread + StartEnumTasks(&handle); + status = EnumTask(&handle, &pIXCLRDataTask); + for (unsigned nbThreads = 0; status == S_OK && pIXCLRDataTask != NULL; nbThreads++) + { + // Avoid infinite loop if target process is corrupted. + if (nbThreads > 100000) + { + break; + } + EX_TRY + { + // get Thread * + pThread = ((ClrDataTask *)pIXCLRDataTask.GetValue())->GetThread(); + + // Write out the Thread instance + DacEnumHostDPtrMem(pThread); + + // Write out the context pointed by the thread + DacEnumHostDPtrMem(pThread->GetContext()); + + // @TODO + // write TEB pointed by the thread + // DacEnumHostDPtrMem(pThread->GetTEB()); + + // @TODO + // If CLR is hosted, we want to write out fiber data + + // Dump the managed thread object + DumpManagedObject(flags, pThread->GetExposedObjectRaw()); + +#ifndef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + // dump the exception object + DumpManagedExcepObject(flags, pThread->LastThrownObject()); +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + // Stack Walking + // We need for the ClrDataTask::CreateStackWalk from IXCLRDataTask to work, which is the + // following walk. However, the CordbStackWalk code requires some different (extra) data + // to walk the stack, such as info being present for + // mscordacwks!DacDbiInterfaceImpl::GetNativeCodeSequencePointsAndVarInfo. + status = pIXCLRDataTask->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED | CLRDATA_SIMPFRAME_MANAGED_METHOD | CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE | CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE, + &pStackWalk); + if (status == S_OK && pStackWalk != NULL) + { + status = EnumMemWalkStackHelper(flags, pStackWalk, pThread); + pStackWalk.Clear(); + } + + // Now probe into the exception info + status = pIXCLRDataTask->GetCurrentExceptionState(&pExcepState); + while (status == S_OK && pExcepState != NULL) + { + EX_TRY + { + // touch the throwable in exception state + PTR_UNCHECKED_OBJECTREF throwRef(((ClrDataExceptionState *)pExcepState.GetValue())->m_throwable); + + // If we've already attempted enumeration for this exception, it's time to quit. + if (!exceptionTracking.AddNewAddressOnly(throwRef.GetAddr())) + { + break; + } + +#ifndef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + DumpManagedExcepObject(flags, *throwRef); +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + // get the type of the exception + ReleaseHolder<IXCLRDataValue> pValue(NULL); + status = pExcepState->GetManagedObject(&pValue); + if (status == S_OK && pValue != NULL) + { + ReleaseHolder<IXCLRDataTypeInstance> pTypeInstance(NULL); + // Make sure that we can get back a TypeInstance during inspection + status = pValue->GetType(&pTypeInstance); + pValue.Clear(); + } + + // If Exception state has a new context, we will walk with the stashed context as well. + // Note that in stack overflow exception's case, m_pContext is null. + // + // It is possible that we are in exception's catch clause when we + // try to walk the stack below. This is a very weird situation where + // stack is logically unwind and not physically unwind. We may not be able + // to walk the stack correctly here. Anyway, we try to catch exception thrown + // by bad stack walk in EnumMemWalkStackHelper. + // + PTR_CONTEXT pContext = ((ClrDataExceptionState*)pExcepState.GetValue())->GetCurrentContextRecord(); + if (pContext != NULL) + { + T_CONTEXT newContext; + newContext = *pContext; + + // We need to trigger stack walk again using the exception's context! + status = pIXCLRDataTask->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED | CLRDATA_SIMPFRAME_MANAGED_METHOD | CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE | CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE, + &pStackWalk); + if (status == S_OK && pStackWalk != NULL) + { + status = pStackWalk->SetContext2(CLRDATA_STACK_SET_CURRENT_CONTEXT, sizeof(T_CONTEXT), (BYTE *) &newContext); + if (status == S_OK) + { + status = EnumMemWalkStackHelper(flags, pStackWalk, pThread); + } + pStackWalk.Clear(); + } + } + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + // get the previous exception + IXCLRDataExceptionState * pExcepStatePrev = NULL; + status = pExcepState->GetPrevious(&pExcepStatePrev); + + // Release our current exception object, and transfer ref ownership of the previous + // exception object into the holder. + pExcepState = pExcepStatePrev; + } + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + // get next thread + pIXCLRDataTask.Clear(); + status = EnumTask(&handle, &pIXCLRDataTask); + } + EndEnumTasks(handle); + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + // updating the statistics + m_dumpStats.m_cbStack = m_cbMemoryReported - cbMemoryReported; + + return status; +} + + +#ifdef FEATURE_COMINTEROP +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// WinRT stowed exception holds the (CCW)pointer to a managed exception object. +// We should check for the presence of a such an exception object and dump it if available. +// This can also drag in memory implicitly. +// So do call +// m_instances.DumpAllInstances(m_enumMemCb); +// when function is done. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemStowedException(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + ICLRDataTarget3 *pTarget3 = GetLegacyTarget3(); + if (pTarget3 == NULL) + return S_OK; + + // get the thread that raised the exception + ULONG32 exThreadID = 0; + if (FAILED(pTarget3->GetExceptionThreadID(&exThreadID)) || exThreadID == 0) + return S_OK; + + // + // check that the thread is one of the known managed threads + // + BOOL foundThread = FALSE; + CLRDATA_ENUM handle; + ReleaseHolder<IXCLRDataTask> pIXCLRDataTask(NULL); + + // enumerate through each thread + StartEnumTasks(&handle); + HRESULT status = EnumTask(&handle, &pIXCLRDataTask); + for (unsigned nbThreads = 0; status == S_OK && pIXCLRDataTask != NULL; ++nbThreads) + { + // Avoid infinite loop if target process is corrupted. + if (nbThreads > 100000) + { + break; + } + EX_TRY + { + if (((ClrDataTask *)pIXCLRDataTask.GetValue())->GetThread()->GetOSThreadId() == exThreadID) + { + // found the thread + foundThread = TRUE; + break; + } + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + // get next thread + pIXCLRDataTask.Clear(); + status = EnumTask(&handle, &pIXCLRDataTask); + } + EndEnumTasks(handle); + + if (!foundThread) + return S_OK; + + + // + // Read the remote stowed exceptions. + // + // EXCEPTION_RECORD.ExceptionCode: STATUS_STOWED_EXCEPTION. + // EXCEPTION_RECORD.NumberParameters: 2. + // EXCEPTION_RECORD.ExceptionInformation[0]: pointer to an array of pointers + // to STOWED_EXCEPTION_INFORMATION structures. + // EXCEPTION_RECORD.ExceptionInformation[1]: count of elements in the array. + // + ULONG32 bytesRead = 0; + MINIDUMP_EXCEPTION minidumpException = { 0 }; + if (FAILED(pTarget3->GetExceptionRecord(sizeof(MINIDUMP_EXCEPTION), &bytesRead, (PBYTE)&minidumpException))) + return S_OK; + + TADDR remoteStowedExceptionArray = (TADDR)minidumpException.ExceptionInformation[0]; + ULONG stowedExceptionCount = (ULONG)minidumpException.ExceptionInformation[1]; + if (bytesRead != sizeof(MINIDUMP_EXCEPTION) + || minidumpException.ExceptionCode != STATUS_STOWED_EXCEPTION + || minidumpException.NumberParameters != 2 + || stowedExceptionCount < 1 // there must atleast be 1 stowed exception + || stowedExceptionCount > 256 // upper bound: 256 + || remoteStowedExceptionArray == NULL) + { + return S_OK; + } + + for (ULONG i = 0; i < stowedExceptionCount; ++i) + { + // Read the i-th stowed exception + TADDR remoteStowedException = NULL; + if (FAILED(m_pTarget->ReadVirtual(TO_CDADDR(remoteStowedExceptionArray + (i * sizeof(TADDR))), + (PBYTE)&remoteStowedException, sizeof(TADDR), &bytesRead)) + || bytesRead != sizeof(TADDR) + || remoteStowedException == NULL) + { + continue; + } + + // check if this is a v2 stowed exception + STOWED_EXCEPTION_INFORMATION_V2 stowedException = { 0 }; + if (FAILED(m_pTarget->ReadVirtual(TO_CDADDR(remoteStowedException), + (PBYTE)&stowedException, sizeof(STOWED_EXCEPTION_INFORMATION_HEADER), &bytesRead)) + || bytesRead != sizeof(STOWED_EXCEPTION_INFORMATION_HEADER) + || stowedException.Header.Signature != STOWED_EXCEPTION_INFORMATION_V2_SIGNATURE) + { + continue; + } + + // Read the full v2 stowed exception and get the CCW pointer out of it + if (FAILED(m_pTarget->ReadVirtual(TO_CDADDR(remoteStowedException), + (PBYTE)&stowedException, sizeof(STOWED_EXCEPTION_INFORMATION_V2), &bytesRead)) + || bytesRead != sizeof(STOWED_EXCEPTION_INFORMATION_V2) + || stowedException.NestedExceptionType != STOWED_EXCEPTION_NESTED_TYPE_LEO + || stowedException.NestedException == NULL) + { + continue; + } + + // Find out if NestedException is a pointer to CCW and then dump the exception object in it + DumpStowedExceptionObject(flags, TO_CDADDR(stowedException.NestedException)); + } + + return S_OK; +} + +HRESULT ClrDataAccess::DumpStowedExceptionObject(CLRDataEnumMemoryFlags flags, CLRDATA_ADDRESS ccwPtr) +{ + SUPPORTS_DAC; + if (ccwPtr == NULL) + return S_OK; + + // dump the managed exception object wrapped in CCW + // memory of the CCW object itself is dumped later by DacInstanceManager::DumpAllInstances + DacpCCWData ccwData; + GetCCWData(ccwPtr, &ccwData); // this call collects some memory implicitly + DumpManagedExcepObject(flags, OBJECTREF(TO_TADDR(ccwData.managedObject))); + + // dump memory of the 2nd slot in the CCW's vtable + // this is used in DACGetCCWFromAddress to identify if the passed in pointer is a valid CCW. + ULONG32 bytesRead = 0; + TADDR vTableAddress = NULL; + if (FAILED(m_pTarget->ReadVirtual(ccwPtr, (PBYTE)&vTableAddress, sizeof(TADDR), &bytesRead)) + || bytesRead != sizeof (TADDR) + || vTableAddress == NULL) + { + return S_OK; + } + + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED + ( + ReportMem(vTableAddress + sizeof(PBYTE)* TEAR_OFF_SLOT, sizeof(TADDR)); + ); + + return S_OK; +} +#endif + +#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory +#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Reports critical data from the CLR main module +// that needs to be present in all minidumps. +// Implicitly reports memory, so remember to call +// m_instances.DumpAllInstances(m_enumMemCb); +// after this function completes. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemCLRMainModuleInfo() +{ + SUPPORTS_DAC; + + HRESULT status = S_OK; + + // PEDecoder is DACized, so we just need to touch what we want to + // make subsequent lookup work. + PEDecoder pe(m_globalBase); + + // We currently only actually have one debug directory entry. + // Post-processing, such as optimization, may add an extra directory. + // These directories are of type IMAGE_DEBUG_TYPE_RESERVED10, while our + // standard CodeView directory with pdb info is IMAGE_DEBUG_TYPE_CODEVIEW. + UINT i; + for (i = 0; pe.GetDebugDirectoryEntry(i); i++) + { + } + + if (i < 1) + { + status = E_UNEXPECTED; + _ASSERTE(!"Collecting dump of target with no debug directory entries!"); + } + + // For CLRv4+, the resource directory contains the necessary info + // to retrieve the DBI/DAC from a symbol server. + // Specifically, in v4 it contains a mscoree!PE_FIXEDFILEINFO. + // This is also required since OpenVirtualProcess will check against + // this content to determine if a target module is indeed a CLR + // main module. + + // Retrieve all resources in clr.dll. Right now, the entire resource + // content is very small (~0x600 bytes of raw data), so getting all is + // the easy thing to do. If resources become larger in later + // releases, we'll have to specifically get just the debugging-related resources. + _ASSERTE(pe.HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE)); + if (pe.HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE)) + { + COUNT_T size = 0; + TADDR pResourceDirData = pe.GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_RESOURCE, &size); + + _ASSERTE(size < 0x2000); + ReportMem((TADDR)pResourceDirData, size, true); + } + else + { + // In later releases, we should log the ERROR_RESOURCE_DATA_NOT_FOUND. + status = E_UNEXPECTED; + } + + return status; +} + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Generating skinny mini-dump. Skinny mini-dump will only support stack trace, module list, +// and Exception list viewing. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemoryRegionsWorkerSkinny(IN CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + HRESULT status = S_OK; + + // clear all of the previous cached memory + Flush(); + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + // Enable caching enumerated metadata of interest + InitStreamsForWriting(flags); +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + //TODO: actually *do* something with potential failures. It would be relatively easy to + // hook up an official dump stream to put info on our failures and other 'metadata' + // about dumping into in a generic sort of way. Our code doesn't have access to + // MDWD's callbacks, so we can't just do it ourselves. Thus we could have useful info + // baked into the dump, like we failed to enumerate mem for certain threads, etc. + + // Each enumeration function below should be wrapped in a try/catch + // so that we have a chance to create a debuggable dump in the face of target problems. + + // Iterating to all threads' stacks + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(flags); ) + + // Iterating to module list. + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(flags); ) + + // + // iterating through static that we care + // + // collect CLR static + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(flags); ) + + // now dump the memory get dragged in by using DAC API implicitly. + m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb); + status = m_memStatus; + + // Dump AppDomain-specific info needed for MiniDumpNormal. + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAppDomainInfo(flags); ) + + // Dump the Debugger object data needed + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pDebugger->EnumMemoryRegions(flags); ) + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + // Dump the extra data needed for metadata-free debugging + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( EnumStreams(flags); ) +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + // Do not let any remaining implicitly enumerated memory leak out. + Flush(); + + return S_OK; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Generating triage micro-dump. Triage dumps will only support stack trace +// and Exception viewing.More than that triage dumps have to be PII free, +// so all exception messages have to be poisoned with 0xcc mask. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemoryRegionsWorkerMicroTriage(IN CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + HRESULT status = S_OK; + + // clear all of the previous cached memory + Flush(); + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + // Enable caching enumerated metadata of interest + InitStreamsForWriting(flags); +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + // Iterating to all threads' stacks + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(flags); ) + + // Iterating to module list. + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(flags); ) + + // collect CLR static + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(flags); ) + + // now dump the memory get dragged in by using DAC API implicitly. + m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb); + status = m_memStatus; + + // Dump AppDomain-specific info needed for triage dumps methods enumeration (k command). + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAppDomainInfo(flags); ) + + // Dump the Debugger object data needed + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pDebugger->EnumMemoryRegions(flags); ) + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + // Dump the extra data needed for metadata-free debugging + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( EnumStreams(flags); ) +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + // Do not let any remaining implicitly enumerated memory leak out. + Flush(); + + return S_OK; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Write out mscorwks's data segment. This will write out the whole +// data segment for mscorwks. It is about 200 or 300K. Most of it (90%) are +// vtable definition that we don't really care. But we don't have a +// good walk to just write out all globals and statics. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemWriteDataSegment() +{ + SUPPORTS_DAC; + + NewHolder<PEDecoder> pedecoder(NULL); + + EX_TRY + { + // Collecting mscorwks's data segment + { + // m_globalBase is the base address of target process's mscorwks module + pedecoder = new PEDecoder(dac_cast<PTR_VOID>(m_globalBase)); + + PTR_IMAGE_SECTION_HEADER pSection = (PTR_IMAGE_SECTION_HEADER) pedecoder->FindFirstSection(); + PTR_IMAGE_SECTION_HEADER pSectionEnd = pSection + VAL16(pedecoder->GetNumberOfSections()); + + while (pSection < pSectionEnd) + { + if (pSection->Name[0] == '.' && + pSection->Name[1] == 'd' && + pSection->Name[2] == 'a' && + pSection->Name[3] == 't' && + pSection->Name[4] == 'a') + { + // This is the .data section of mscorwks + ReportMem(m_globalBase + pSection->VirtualAddress, pSection->Misc.VirtualSize); + } + pSection++; + } + } + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + return S_OK; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Custom Dump. Depending on the value of g_ECustomDumpFlavor, different dump +// will be taken. You can set this global variable using hosting API +// ICLRErrorReportingManager::BeginCustomDump. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemoryRegionsWorkerCustom() +{ + SUPPORTS_DAC; + + HRESULT status = S_OK; + + ECustomDumpFlavor eFlavor; + +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + eFlavor = CCLRErrorReportingManager::g_ECustomDumpFlavor; +#else + eFlavor = DUMP_FLAVOR_Default; +#endif + + m_enumMemFlags = CLRDATA_ENUM_MEM_MINI; + + // clear all of the previous cached memory + Flush(); + + if (eFlavor == DUMP_FLAVOR_Mini) + { + // Iterating to all threads' stacks + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(m_enumMemFlags); ) + + // Iterating to module list. + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(m_enumMemFlags); ) + + // + // iterating through static that we care + // + // collect CLR static + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(m_enumMemFlags); ) + + // we are done... + + // now dump the memory get dragged in implicitly + m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb); + + } + else if (eFlavor == DUMP_FLAVOR_CriticalCLRState) + { + // We need to walk Threads stack to view managed frames. + // Iterating through module list + + // Iterating to all threads' stacks + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(m_enumMemFlags); ) + + // Iterating to module list. + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(m_enumMemFlags); ) + + // + // iterating through static that we care + // + // collect CLR static + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(m_enumMemFlags); ) + + // Collecting some CLR secondary critical data + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRHeapCrticalStatic(m_enumMemFlags); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemWriteDataSegment(); ) + + // we are done... + + // now dump the memory get dragged in implicitly + m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb); + + } + else if (eFlavor == DUMP_FLAVOR_NonHeapCLRState) + { + // since all CLR hosted heap will be dump by the host, + // the EE structures that are not loaded using LoadLibrary will + // be included by the host. + // + // Thus we only need to include mscorwks's critical data and ngen images + + m_enumMemFlags = CLRDATA_ENUM_MEM_HEAP; + + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(m_enumMemFlags); ) + + // Collecting some CLR secondary critical data + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRHeapCrticalStatic(m_enumMemFlags); ) + + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemWriteDataSegment(); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCollectImages(); ) + } + else + { + status = E_INVALIDARG; + } + + status = m_memStatus; + + return S_OK; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Minidumps traverse a giant static calltree. We already try to catch +// exceptions at various lower level places and continue to report memory. +// +// However, if we'll jump to the top-level catcher and skip the rest of the tree, +// that may mean some key data may not get emitted to the minidump. +// In the case that a user requests a dump is canceled, we should skip the rest +// of the tree. When a COR_E_OPERATIONCANCELED exception is thrown, is allowed to +// escape all the way to this function. If any exception makes it here and is not +// COR_E_OPERATIONCANCELED that indicates an issue, and the assert is meant to catch that. +// Unfortunately the stack unwind will already have happened. +// +// Internal API to support minidump and heap dump. It just delegate +// to proper function but with a top level catch. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemoryRegionsWrapper(IN CLRDataEnumMemoryFlags flags) +{ + // This is infrastructure code - we don't want DacCop complaining about the calls as a result + // of the use of EX_CATCH_HRESULT here. We're careful to mark EnumMemoryRegionsWorkerSkinny + // and EnumMemoryRegionsWorkerHeap as just SUPPORTS_DAC so that we still get analysis. + SUPPORTS_DAC_HOST_ONLY; + + HRESULT status = S_OK; + m_enumMemFlags = flags; + EX_TRY + { + // The various EnumMemoryRegions() implementations should understand + // CLRDATA_ENUM_MEM_MINI to mean that the bare minimimum memory + // to make a MiniDumpNormal work should be included. + if ( flags == CLRDATA_ENUM_MEM_MINI) + { + // skinny mini-dump + status = EnumMemoryRegionsWorkerSkinny(flags); + } + else if ( flags == CLRDATA_ENUM_MEM_TRIAGE) + { + // triage micro-dump + status = EnumMemoryRegionsWorkerMicroTriage(flags); + } + else if ( flags == CLRDATA_ENUM_MEM_HEAP) + { + status = EnumMemoryRegionsWorkerHeap(flags); + } + else + { + _ASSERTE(!"Bad flags passing to EnumMemoryRegionsWrapper!"); + } + } + EX_CATCH_HRESULT(status); + + // The only exception that should reach here is the cancel exception + _ASSERTE(SUCCEEDED(status) || status == COR_E_OPERATIONCANCELED); + + return status; +} + +#define MiniDumpWithPrivateReadWriteMemory 0x00000200 +#define MiniDumpWithFullAuxiliaryState 0x00008000 +#define MiniDumpFilterTriage 0x00100000 + + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Entry function for generating CLR aware dump. This function is called +// for minidump, heap dump, and custom dumps. CLR specific memory will +// be reported to outer level dumper (usually dbghelp's MiniDumpWriteDump api) +// through the callback. We do not write out to file directly. +// +// N.B.: The CLR may report duplicate memory chunks and it's up to +// the debugger to coalesce memory. *However* the debugger's current +// implementation coalesces memory we enumerate and memory that +// they enumerate; the two sets of memory are not guaranteed to be +// coalesced. The dump produced may thus have memory blocks in the +// MemoryListStream that overlap or are totally contained in other blocks. +// This issue was resolved by-design by dbgteam. Win7 #407019. +// Note also that Memory64ListStream (when passing MiniDumpWithFullMemory) +// will have no duplicates, be sorted, etc. In that case, none of +// our code is called. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +STDMETHODIMP +ClrDataAccess::EnumMemoryRegions(IN ICLRDataEnumMemoryRegionsCallback* callback, + IN ULONG32 miniDumpFlags, + IN CLRDataEnumMemoryFlags flags) // reserved not used +{ + SUPPORTS_DAC; + HRESULT status; + +#if defined(DAC_MEASURE_PERF) + + g_nTotalTime = 0; + g_nStackTotalTime = 0; + g_nReadVirtualTotalTime = 0; + g_nFindTotalTime = 0; + g_nFindHashTotalTime = 0; + g_nFindHits = 0; + g_nFindCalls = 0; + g_nFindFails = 0; + g_nStackWalk = 0; + g_nFindStackTotalTime = 0; + + LARGE_INTEGER nClockFrequency; + unsigned __int64 nStart = 0; + unsigned __int64 nEnd = 0; + + QueryPerformanceFrequency(&nClockFrequency); + + FILE* fp = fopen("c:\\dumpLog.txt", "a"); + if (fp) + { + fprintf(fp, "\nMinidumpFlags = %d\n", miniDumpFlags); + fclose(fp); + } + + nStart = GetCycleCount(); + +#endif // #if defined(DAC_MEASURE_PERF) + + DAC_ENTER(); + + // We should not be trying to enumerate while we have an enumeration outstanding + _ASSERTE(m_enumMemCb==NULL); + m_memStatus = S_OK; + m_enumMemCb = callback; + + // QI for ICLRDataEnumMemoryRegionsCallback2 will succeed only for Win8+. + // It is expected to fail on pre Win8 OSes. + callback->QueryInterface(IID_ICLRDataEnumMemoryRegionsCallback2, (void **)&m_updateMemCb); + + EX_TRY + { + ClearDumpStats(); + if (miniDumpFlags & MiniDumpWithPrivateReadWriteMemory) + { + // heap dump + status = EnumMemoryRegionsWrapper(CLRDATA_ENUM_MEM_HEAP); + } + else if (miniDumpFlags & MiniDumpWithFullAuxiliaryState) + { + // This is the host custom dump. + status = EnumMemoryRegionsWorkerCustom(); + } + else if (miniDumpFlags & MiniDumpFilterTriage) + { + // triage micro-dump + status = EnumMemoryRegionsWrapper(CLRDATA_ENUM_MEM_TRIAGE); + } + else + { + // minidump + status = EnumMemoryRegionsWrapper(CLRDATA_ENUM_MEM_MINI); + } + + // For all dump types, we need to capture the chain to the IMAGE_DIRECTORY_ENTRY_DEBUG + // contents, so that DAC can validate against the TimeDateStamp even if the + // debugger can't find the main CLR module on disk. + // If we already failed, don't bother. + if (SUCCEEDED(status)) + { + // In case there's implicitly enumerated memory hanging around + // let's not accidentally pick it up. + Flush(); + if (SUCCEEDED(status = EnumMemCLRMainModuleInfo())) + { + m_instances.DumpAllInstances(m_enumMemCb); + } + } + + Flush(); + } + EX_CATCH + { + m_enumMemCb = NULL; + + // We should never actually be here b/c none of the EMR functions should throw. + // They should all either be written robustly w/ ptr.IsValid() and catching their + // own exceptions. + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + _ASSERTE_MSG(false, "Got unexpected exception in EnumMemoryRegions"); + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + // fix for issue 866100: DAC is too late in releasing ICLRDataEnumMemoryRegionsCallback2* + if (m_updateMemCb) + { + m_updateMemCb->Release(); + m_updateMemCb = NULL; + } + m_enumMemCb = NULL; + + DAC_LEAVE(); + +#if defined(DAC_MEASURE_PERF) + + nEnd = GetCycleCount(); + g_nTotalTime= nEnd - nStart; + fp = fopen("c:\\dumpLog.txt", "a"); + fprintf(fp, "Total = %g msec\n" + "ReadVirtual = %g msec\n" + "StackWalk = %g msec; Find: %g msec\n" + "Find = %g msec; Hash = %g msec; Calls = %I64u; Hits = %I64u; Not found = %I64u\n\n=====\n", + (float) (1000*g_nTotalTime/nClockFrequency.QuadPart), + (float) (1000*g_nReadVirtualTotalTime/nClockFrequency.QuadPart), + (float) (1000*g_nStackTotalTime/nClockFrequency.QuadPart), (float) (1000*g_nFindStackTotalTime/nClockFrequency.QuadPart), + (float) (1000*g_nFindTotalTime/nClockFrequency.QuadPart), (float) (1000*g_nFindHashTotalTime/nClockFrequency.QuadPart), + g_nFindCalls, g_nFindHits, g_nFindFails + ); + fclose(fp); + +#endif // #if defined(DAC_MEASURE_PERF) + + return status; +} + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Clear the statistics for the dump. For each dump generation, we +// clear the dump statistics. At the end of the dump generation, you can +// view the statics data member m_dumpStats and see how many bytes that +// we have reported to our debugger host. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void ClrDataAccess::ClearDumpStats() +{ + SUPPORTS_DAC; + + m_cbMemoryReported = 0; + memset(&m_dumpStats, 0, sizeof(DumpMemoryReportStatics)); +} diff --git a/src/debug/daccess/fntableaccess.cpp b/src/debug/daccess/fntableaccess.cpp new file mode 100644 index 0000000000..e7b96ec3e0 --- /dev/null +++ b/src/debug/daccess/fntableaccess.cpp @@ -0,0 +1,461 @@ +// 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: DebugSupport.cpp +// +// Support routines for debugging the CLR +// =========================================================================== + +#include "stdafx.h" + +#ifndef _TARGET_X86_ + +// +// +// @TODO: This is old code that should be easy to implement on top of the existing DAC support. +// This code was originally written prior to DAC. +// +// + +#include <winwrap.h> +#include <windows.h> +#include <winnt.h> +#include <clrnt.h> +#include <stddef.h> // offsetof +#include "nibblemapmacros.h" +#include "stdmacros.h" + +#include "fntableaccess.h" + +#define move(dst, src) \ +{ \ + if (!fpReadMemory(pUserContext, (LPCVOID)(src), &(dst), sizeof(dst), NULL)) \ + { \ + _ASSERTE(!"MSCORDBG ERROR: ReadProcessMemory failed!!"); \ + return STATUS_UNSUCCESSFUL; \ + } \ +} + +#define move_field(dst, src, cls, fld) \ + move(dst, (SIZE_T)(src) + FIELD_OFFSET(cls, fld)) + +static NTSTATUS OutOfProcessFindHeader(ReadMemoryFunction fpReadMemory,PVOID pUserContext, DWORD_PTR pMapIn, DWORD_PTR addr, DWORD_PTR &codeHead) +{ + codeHead = 0; + + DWORD tmp; // must be a DWORD, not a DWORD_PTR + DWORD_PTR startPos = ADDR2POS(addr); // align to 128 byte buckets ( == index into the array of nibbles) + DWORD_PTR offset = ADDR2OFFS(addr); // this is the offset inside the bucket + 1 + DWORD * pMap = (DWORD *) pMapIn; // make this a pointer type so our pointer math is correct w/o adding sizeof(DWORD) everywhere + + _ASSERTE(offset == (offset & NIBBLE_MASK)); // the offset must fit in a nibble + + pMap += (startPos >> LOG2_NIBBLES_PER_DWORD); // points to the proper DWORD of the map + + // + // get DWORD and shift down our nibble + // + move(tmp, pMap); + tmp = tmp >> POS2SHIFTCOUNT(startPos); + + // don't allow equality in the next check (tmp & NIBBLE_MASK == offset) + // there are code blocks that terminate with a call instruction + // (like call throwobject), i.e. their return address is + // right behind the code block. If the memory manager allocates + // heap blocks w/o gaps, we could find the next header in such + // cases. Therefore we exclude the first DWORD of the header + // from our search, but since we call this function for code + // anyway (which starts at the end of the header) this is not + // a problem. + if ((tmp & NIBBLE_MASK) && ((tmp & NIBBLE_MASK) < offset) ) + { + codeHead = POSOFF2ADDR(startPos, tmp & NIBBLE_MASK) - sizeof(CodeHeader); + return STATUS_SUCCESS; + } + + // is there a header in the remainder of the DWORD ? + tmp = tmp >> NIBBLE_SIZE; + + if (tmp) + { + startPos--; + while (!(tmp & NIBBLE_MASK)) + { + tmp = tmp >> NIBBLE_SIZE; + startPos--; + } + + codeHead = POSOFF2ADDR(startPos, tmp & NIBBLE_MASK) - sizeof(CodeHeader); + return STATUS_SUCCESS; + } + + // we skipped the remainder of the DWORD, + // so we must set startPos to the highest position of + // previous DWORD + + startPos = ((startPos >> LOG2_NIBBLES_PER_DWORD) << LOG2_NIBBLES_PER_DWORD) - 1; + + if ((INT_PTR)startPos < 0) + { + return STATUS_SUCCESS; + } + + // skip "headerless" DWORDS + + pMap--; + move(tmp, pMap); + while (!tmp) + { + startPos -= NIBBLES_PER_DWORD; + if ((INT_PTR)startPos < 0) + { + return STATUS_SUCCESS; + } + pMap--; + move (tmp, pMap); + } + + + while (!(tmp & NIBBLE_MASK)) + { + tmp = tmp >> NIBBLE_SIZE; + startPos--; + } + + codeHead = POSOFF2ADDR(startPos, tmp & NIBBLE_MASK) - sizeof(CodeHeader); + return STATUS_SUCCESS; +} + +#define CODE_HEADER FakeRealCodeHeader +#define ResolveCodeHeader(pHeader) \ + if (pHeader) \ + { \ + DWORD_PTR tmp = pHeader; \ + tmp += offsetof (FakeCodeHeader, pRealCodeHeader); \ + move (tmp, tmp); \ + pHeader = tmp; \ + } + +static NTSTATUS OutOfProcessFunctionTableCallback_JIT(IN ReadMemoryFunction fpReadMemory, + IN PVOID pUserContext, + IN PVOID TableAddress, + OUT PULONG pnEntries, + OUT PT_RUNTIME_FUNCTION* ppFunctions) +{ + if (NULL == pnEntries) { return STATUS_INVALID_PARAMETER_3; } + if (NULL == ppFunctions) { return STATUS_INVALID_PARAMETER_4; } + + DYNAMIC_FUNCTION_TABLE * pTable = (DYNAMIC_FUNCTION_TABLE *) TableAddress; + + PVOID pvContext; + move(pvContext, &pTable->Context); + + DWORD_PTR JitMan = (((DWORD_PTR)pvContext) & ~3); + + DWORD_PTR MinAddress = (DWORD_PTR) &(pTable->MinimumAddress); + move(MinAddress, MinAddress); + + *ppFunctions = 0; + *pnEntries = 0; + + DWORD_PTR pHp = JitMan + (DWORD_PTR)offsetof(FakeEEJitManager, m_pCodeHeap); + + move(pHp, pHp); + + while (pHp) + { + FakeHeapList Hp; + + move(Hp, pHp); + + if (pHp == MinAddress) + { + DWORD_PTR pThisHeader; + DWORD_PTR hdrOffset; + DWORD_PTR hdrOffsetInitial; + DWORD nEntries; + DWORD index; + DWORD_PTR pUnwindInfo; + PT_RUNTIME_FUNCTION pFunctions; + LONG64 lSmallestOffset; + + // + // walk the header map and count functions with unwind info + // + nEntries = 0; + hdrOffset = Hp.endAddress - Hp.mapBase; + lSmallestOffset = (LONG64)(Hp.startAddress - Hp.mapBase); + + // Save the initial offset at which we start our enumeration (from the end to the beginning). + // The target process could be running when this function is called. New methods could be + // added after we have started our enumeration, but their code headers would be added after + // this initial offset. Methods could also be deleted, but the memory would still be there. + // It just wouldn't be marked as the beginning of a method, and we would collect fewer entries + // than we have anticipated. + hdrOffsetInitial = hdrOffset; + + _ASSERTE(((LONG64)hdrOffset) >= lSmallestOffset); + OutOfProcessFindHeader(fpReadMemory, pUserContext, Hp.pHdrMap, hdrOffset, hdrOffset); + + while (((LONG64)hdrOffset) >= lSmallestOffset) // MUST BE A SIGNED COMPARISON + { + pThisHeader = Hp.mapBase + hdrOffset; + ResolveCodeHeader(pThisHeader); + + if (pThisHeader > FAKE_STUB_CODE_BLOCK_LAST) + { + DWORD nUnwindInfos; + move_field(nUnwindInfos, pThisHeader, CODE_HEADER, nUnwindInfos); + + nEntries += nUnwindInfos; + } + + _ASSERTE(((LONG64)hdrOffset) >= lSmallestOffset); + OutOfProcessFindHeader(fpReadMemory, pUserContext, Hp.pHdrMap, hdrOffset, hdrOffset); + } + + pFunctions = (PT_RUNTIME_FUNCTION)ClrHeapAlloc(ClrGetProcessHeap(), HEAP_ZERO_MEMORY, S_SIZE_T(nEntries) * S_SIZE_T(sizeof(T_RUNTIME_FUNCTION))); + *ppFunctions = pFunctions; + *pnEntries = nEntries; + + // + // walk the header map and copy the function tables + // + + index = 0; + hdrOffset = hdrOffsetInitial; + + _ASSERTE(((LONG64)hdrOffset) >= lSmallestOffset); + OutOfProcessFindHeader(fpReadMemory, pUserContext, Hp.pHdrMap, hdrOffset, hdrOffset); + + while (((LONG64)hdrOffset) >= lSmallestOffset) // MUST BE A SIGNED COMPARISON + { + pThisHeader = Hp.mapBase + hdrOffset; + ResolveCodeHeader(pThisHeader); + + if (pThisHeader > FAKE_STUB_CODE_BLOCK_LAST) + { + DWORD nUnwindInfos; + move_field(nUnwindInfos, pThisHeader, CODE_HEADER, nUnwindInfos); + + if ((index + nUnwindInfos) > nEntries) + { + break; + } + for (DWORD iUnwindInfo = 0; iUnwindInfo < nUnwindInfos; iUnwindInfo++) + { + move(pFunctions[index], pThisHeader + offsetof(CODE_HEADER, unwindInfos[iUnwindInfo])); + index++; + } + } + + _ASSERTE(((LONG64)hdrOffset) >= lSmallestOffset); + OutOfProcessFindHeader(fpReadMemory, pUserContext, Hp.pHdrMap, hdrOffset, hdrOffset); + } + + // Return the final count. + *pnEntries = index; + break; + } + + pHp = (DWORD_PTR)Hp.hpNext; + } + + return STATUS_SUCCESS; +} + + +#ifdef DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO + +static NTSTATUS OutOfProcessFunctionTableCallback_Stub(IN ReadMemoryFunction fpReadMemory, + IN PVOID pUserContext, + IN PVOID TableAddress, + OUT PULONG pnEntries, + OUT PT_RUNTIME_FUNCTION* ppFunctions) +{ + if (NULL == pnEntries) { return STATUS_INVALID_PARAMETER_3; } + if (NULL == ppFunctions) { return STATUS_INVALID_PARAMETER_4; } + + *ppFunctions = 0; + *pnEntries = 0; + + PVOID pvContext; + move_field(pvContext, TableAddress, DYNAMIC_FUNCTION_TABLE, Context); + + SIZE_T pStubHeapSegment = ((SIZE_T)pvContext & ~3); + + FakeStubUnwindInfoHeapSegment stubHeapSegment; + move(stubHeapSegment, pStubHeapSegment); + + UINT nEntries = 0; + UINT nEntriesAllocated = 0; + PT_RUNTIME_FUNCTION rgFunctions = NULL; + + for (int pass = 1; pass <= 2; pass++) + { + // Use the same initial header for both passes. The process may still be running, + // and so new entries could be added at the beginning of the list. Using the initial header + // makes sure new entries are not picked up in the second pass. Entries could also be deleted, + // and there is a small time window here where we could read invalid memory. This just means + // that ReadProcessMemory() may fail. As long as we don't crash the host process (e.g. WER) + // we are fine. + SIZE_T pHeader = (SIZE_T)stubHeapSegment.pUnwindHeaderList; + + while (pHeader) + { + FakeStubUnwindInfoHeader unwindInfoHeader; + move(unwindInfoHeader, pHeader); +#if defined(_TARGET_AMD64_) + // Consistency checks to detect corrupted process state + if (unwindInfoHeader.FunctionEntry.BeginAddress > unwindInfoHeader.FunctionEntry.EndAddress || + unwindInfoHeader.FunctionEntry.EndAddress > stubHeapSegment.cbSegment) + { + _ASSERTE(1 == pass); + return STATUS_UNSUCCESSFUL; + } + + if ((SIZE_T)stubHeapSegment.pbBaseAddress + unwindInfoHeader.FunctionEntry.UnwindData != + pHeader + FIELD_OFFSET(FakeStubUnwindInfoHeader, UnwindInfo)) + { + _ASSERTE(1 == pass); + return STATUS_UNSUCCESSFUL; + } +#elif defined(_TARGET_ARM_) + + // Skip checking the corrupted process stateon ARM + +#elif defined(_TARGET_ARM64_) + // Compute the function length + ULONG64 functionLength = 0; + ULONG64 unwindData = unwindInfoHeader.FunctionEntry.UnwindData; + if (( unwindData & 3) != 0) { + // the unwindData contains the function length, retrieve it directly from unwindData + functionLength = (unwindInfoHeader.FunctionEntry.UnwindData >> 2) & 0x7ff; + } else { + // the unwindData is an RVA to the .xdata record which contains the function length + DWORD xdataHeader=0; + if ((SIZE_T)stubHeapSegment.pbBaseAddress + unwindData != pHeader + FIELD_OFFSET(FakeStubUnwindInfoHeader, UnwindInfo)) + { + _ASSERTE(1 == pass); + return STATUS_UNSUCCESSFUL; + } + move(xdataHeader, stubHeapSegment.pbBaseAddress + unwindData); + functionLength = (xdataHeader & 0x3ffff) << 2; + } + if (unwindInfoHeader.FunctionEntry.BeginAddress + functionLength > stubHeapSegment.cbSegment) + { + _ASSERTE(1 == pass); + return STATUS_UNSUCCESSFUL; + } +#else + PORTABILITY_ASSERT("OutOfProcessFunctionTableCallback_Stub"); +#endif + if (nEntriesAllocated) + { + if (nEntries >= nEntriesAllocated) + break; + rgFunctions[nEntries] = unwindInfoHeader.FunctionEntry; + } + nEntries++; + + pHeader = (SIZE_T)unwindInfoHeader.pNext; + } + + if (1 == pass) + { + if (!nEntries) + break; + + _ASSERTE(!nEntriesAllocated); + nEntriesAllocated = nEntries; + rgFunctions = (PT_RUNTIME_FUNCTION)ClrHeapAlloc(ClrGetProcessHeap(), HEAP_ZERO_MEMORY, S_SIZE_T(nEntries) * S_SIZE_T(sizeof(T_RUNTIME_FUNCTION))); + nEntries = 0; + } + else + { + _ASSERTE(nEntriesAllocated >= nEntries); + } + } + + *ppFunctions = rgFunctions; + *pnEntries = nEntries; // return the final count + + return STATUS_SUCCESS; +} + +#endif // DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO + +BOOL ReadMemory(PVOID pUserContext, LPCVOID lpBaseAddress, PVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead) +{ + HANDLE hProcess = (HANDLE)pUserContext; + return ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead); +} + +extern "C" NTSTATUS OutOfProcessFunctionTableCallback(IN HANDLE hProcess, + IN PVOID TableAddress, + OUT PULONG pnEntries, + OUT PT_RUNTIME_FUNCTION* ppFunctions) +{ + return OutOfProcessFunctionTableCallbackEx(&ReadMemory, hProcess, TableAddress, pnEntries, ppFunctions); +} + +extern "C" NTSTATUS OutOfProcessFunctionTableCallbackEx(IN ReadMemoryFunction fpReadMemory, + IN PVOID pUserContext, + IN PVOID TableAddress, + OUT PULONG pnEntries, + OUT PT_RUNTIME_FUNCTION* ppFunctions) +{ + if (NULL == pnEntries) { return STATUS_INVALID_PARAMETER_3; } + if (NULL == ppFunctions) { return STATUS_INVALID_PARAMETER_4; } + + DYNAMIC_FUNCTION_TABLE * pTable = (DYNAMIC_FUNCTION_TABLE *) TableAddress; + PVOID pvContext; + + move(pvContext, &pTable->Context); + + FakeEEDynamicFunctionTableType type = (FakeEEDynamicFunctionTableType)((SIZE_T)pvContext & 3); + + switch (type) + { + case FAKEDYNFNTABLE_JIT: + return OutOfProcessFunctionTableCallback_JIT( + fpReadMemory, + pUserContext, + TableAddress, + pnEntries, + ppFunctions); + +#ifdef DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO + case FAKEDYNFNTABLE_STUB: + return OutOfProcessFunctionTableCallback_Stub( + fpReadMemory, + pUserContext, + TableAddress, + pnEntries, + ppFunctions); +#endif // DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO + default: + break; + } + + return STATUS_UNSUCCESSFUL; +} + +#else + +extern "C" NTSTATUS OutOfProcessFunctionTableCallback() +{ + return STATUS_UNSUCCESSFUL; +} + +extern "C" NTSTATUS OutOfProcessFunctionTableCallbackEx() +{ + return STATUS_UNSUCCESSFUL; +} + + + +#endif // !_TARGET_X86_ diff --git a/src/debug/daccess/fntableaccess.h b/src/debug/daccess/fntableaccess.h new file mode 100644 index 0000000000..0dbabdf8c9 --- /dev/null +++ b/src/debug/daccess/fntableaccess.h @@ -0,0 +1,216 @@ +// 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. + +// + +#ifdef _MSC_VER +#pragma once +#endif + +#ifndef _FN_TABLE_ACCESS_H +#define _FN_TABLE_ACCESS_H + + +#if !defined(_TARGET_X86_) + +#ifndef FEATURE_PAL +#define DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO +#endif // !FEATURE_PAL + +#ifndef USE_INDIRECT_CODEHEADER +#define USE_INDIRECT_CODEHEADER +#endif // USE_INDIRECT_CODEHEADER +#endif + + +struct FakeEEJitManager +{ + LPVOID __VFN_table; + LPVOID m_runtimeSupport; + LPVOID m_pCodeHeap; + // Nothing after this point matters: we only need the correct offset of m_pCodeHeap. +}; + +struct FakeHeapList +{ + FakeHeapList* hpNext; + LPVOID pHeap; // changed type from LoaderHeap* + DWORD_PTR startAddress; // changed from PBYTE + DWORD_PTR endAddress; // changed from PBYTE + DWORD_PTR mapBase; // changed from PBYTE + DWORD_PTR pHdrMap; // changed from DWORD* + size_t maxCodeHeapSize; + DWORD cBlocks; + bool bFull; // Heap is considered full do not use for new allocations + bool bFullForJumpStubs; // Heap is considered full do not use for new allocations of jump stubs +}; + +typedef struct _FakeHpRealCodeHdr +{ + LPVOID phdrDebugInfo; + LPVOID phdrJitEHInfo; // changed from EE_ILEXCEPTION* + LPVOID phdrJitGCInfo; // changed from BYTE* + LPVOID hdrMDesc; // changed from MethodDesc* + DWORD nUnwindInfos; + T_RUNTIME_FUNCTION unwindInfos[0]; +} FakeRealCodeHeader; + +typedef struct _FakeHpCodeHdr +{ + LPVOID pRealCodeHeader; +} FakeCodeHeader; + +#define FAKE_STUB_CODE_BLOCK_LAST 0xF + +#ifdef DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO + +struct FakeStubUnwindInfoHeaderSuffix +{ + UCHAR nUnwindInfoSize; +}; + +// Variable-sized struct that preceeds a Stub when the stub requires unwind +// information. Followed by a StubUnwindInfoHeaderSuffix. +struct FakeStubUnwindInfoHeader +{ + FakeStubUnwindInfoHeader *pNext; + T_RUNTIME_FUNCTION FunctionEntry; + UNWIND_INFO UnwindInfo; // variable length +}; + +// List of stub address ranges, in increasing address order. +struct FakeStubUnwindInfoHeapSegment +{ + PBYTE pbBaseAddress; + SIZE_T cbSegment; + FakeStubUnwindInfoHeader *pUnwindHeaderList; + FakeStubUnwindInfoHeapSegment *pNext; +}; + +#define FAKE_STUB_EXTERNAL_ENTRY_BIT 0x40000000 +#define FAKE_STUB_INTERCEPT_BIT 0x10000000 +#define FAKE_STUB_UNWIND_INFO_BIT 0x08000000 + +#ifdef _DEBUG +#define FAKE_STUB_SIGNATURE 0x42555453 +#endif + +struct FakeStub +{ + ULONG m_refcount; + ULONG m_patchOffset; + + UINT m_numCodeBytes; +#ifdef _DEBUG + UINT32 m_signature; +#else +#ifdef _WIN64 + //README ALIGNEMENT: in retail mode UINT m_numCodeBytes does not align to 16byte for the code + // after the Stub struct. This is to pad properly + UINT m_pad_code_bytes; +#endif // _WIN64 +#endif // _DEBUG +}; + +#endif // DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO + + +enum FakeEEDynamicFunctionTableType +{ + FAKEDYNFNTABLE_JIT = 0, + FAKEDYNFNTABLE_STUB = 1, +}; + + +#ifdef CHECK_DUPLICATED_STRUCT_LAYOUTS + +// +// These are the fields of the above structs that we use. +// We need to assert that their layout matches the layout +// in the EE. +// +class CheckDuplicatedStructLayouts +{ +#define CHECK_OFFSET(cls, fld) CPP_ASSERT(cls##fld, offsetof(Fake##cls, fld) == offsetof(cls, fld)) + + CHECK_OFFSET(EEJitManager, m_pCodeHeap); + + CHECK_OFFSET(HeapList, hpNext); + CHECK_OFFSET(HeapList, startAddress); + CHECK_OFFSET(HeapList, endAddress); + CHECK_OFFSET(HeapList, mapBase); + CHECK_OFFSET(HeapList, pHdrMap); + +#if !defined(_TARGET_X86_) + CHECK_OFFSET(RealCodeHeader, nUnwindInfos); + CHECK_OFFSET(RealCodeHeader, unwindInfos); +#endif // !_TARGET_X86_ + +#ifdef DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO + CHECK_OFFSET(StubUnwindInfoHeader, pNext); + + CHECK_OFFSET(StubUnwindInfoHeapSegment, pbBaseAddress); + CHECK_OFFSET(StubUnwindInfoHeapSegment, cbSegment); + CHECK_OFFSET(StubUnwindInfoHeapSegment, pUnwindHeaderList); + CHECK_OFFSET(StubUnwindInfoHeapSegment, pNext); + + + CHECK_OFFSET(Stub, m_refcount); + CHECK_OFFSET(Stub, m_patchOffset); + CHECK_OFFSET(Stub, m_numCodeBytes); +#ifdef _DEBUG + CHECK_OFFSET(Stub, m_signature); +#endif // _DEBUG + +#endif // DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO + +#undef CHECK_OFFSET + +#ifdef DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO + + static_assert_no_msg( Stub::EXTERNAL_ENTRY_BIT + == FAKE_STUB_EXTERNAL_ENTRY_BIT); + + static_assert_no_msg( Stub::INTERCEPT_BIT + == FAKE_STUB_INTERCEPT_BIT); + + static_assert_no_msg( Stub::UNWIND_INFO_BIT + == FAKE_STUB_UNWIND_INFO_BIT); + +#ifdef _DEBUG + static_assert_no_msg( FAKE_STUB_SIGNATURE + == Stub::kUsedStub); +#endif + +#endif // DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO +}; + +#ifdef DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO + +static_assert_no_msg( FAKEDYNFNTABLE_JIT + == DYNFNTABLE_JIT); + +static_assert_no_msg( FAKEDYNFNTABLE_STUB + == DYNFNTABLE_STUB); + +#endif // DEBUGSUPPORT_STUBS_HAVE_UNWIND_INFO + +#else // CHECK_DUPLICATED_STRUCT_LAYOUTS + +BOOL WINAPI DllMain(HINSTANCE hDLL, DWORD dwReason, LPVOID pReserved); +//NTSTATUS OutOfProcessFindHeader(HANDLE hProcess, DWORD_PTR pMapIn, DWORD_PTR addr, DWORD_PTR &codeHead); +extern "C" NTSTATUS OutOfProcessFunctionTableCallback(IN HANDLE hProcess, IN PVOID TableAddress, OUT PULONG pnEntries, OUT PT_RUNTIME_FUNCTION* ppFunctions); + + +// OutOfProcessFunctionTableCallbackEx is like the standard OS-defined OutOfProcessFunctionTableCallback, but rather +// than take a handle to a process, it takes a callback function which can read from the target. This allows the API to work on +// targets other than live processes (such as TTT trace files). +// pUserContext is passed directly to fpReadMemory, and the semantics of all other ReadMemoryFunction arguments (and return value) are +// the same as those for kernel32!ReadProcessMemory. +typedef BOOL (ReadMemoryFunction)(PVOID pUserContext, LPCVOID lpBaseAddress, PVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead); +extern "C" NTSTATUS OutOfProcessFunctionTableCallbackEx(IN ReadMemoryFunction fpReadMemory, IN PVOID pUserContext, IN PVOID TableAddress, OUT PULONG pnEntries, OUT PT_RUNTIME_FUNCTION* ppFunctions); + +#endif // CHECK_DUPLICATED_STRUCT_LAYOUTS + +#endif //_FN_TABLE_ACCESS_H diff --git a/src/debug/daccess/i386/.gitmirror b/src/debug/daccess/i386/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/debug/daccess/i386/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/debug/daccess/i386/primitives.cpp b/src/debug/daccess/i386/primitives.cpp new file mode 100644 index 0000000000..e4d3c6cb76 --- /dev/null +++ b/src/debug/daccess/i386/primitives.cpp @@ -0,0 +1,11 @@ +// 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. + +// + +#include "stdafx.h" + +#include "../../shared/i386/primitives.cpp" + + diff --git a/src/debug/daccess/inspect.cpp b/src/debug/daccess/inspect.cpp new file mode 100644 index 0000000000..5276626dcb --- /dev/null +++ b/src/debug/daccess/inspect.cpp @@ -0,0 +1,3840 @@ +// 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: inspect.cpp +// + +// +// ClrData object inspection. +// +//***************************************************************************** + +#include "stdafx.h" + + +HRESULT +InitFieldIter(DeepFieldDescIterator* fieldIter, + TypeHandle typeHandle, + bool canHaveFields, + ULONG32 flags, + IXCLRDataTypeInstance* fromType) +{ + // Currently we can't filter on kinds so + // require them all. + if ((flags & ~CLRDATA_FIELD_ALL_FIELDS) != 0 || + (flags & CLRDATA_TYPE_ALL_KINDS) != CLRDATA_TYPE_ALL_KINDS || + (flags & CLRDATA_FIELD_ALL_LOCATIONS) == 0) + { + return E_INVALIDARG; + } + + if (!canHaveFields) + { + // Leave default empty initialization. + return S_OK; + } + + int fieldIterFlags = ApproxFieldDescIterator::ALL_FIELDS; + + if ((flags & CLRDATA_FIELD_FROM_INSTANCE) == 0) + { + fieldIterFlags &= ~ApproxFieldDescIterator::INSTANCE_FIELDS; + } + if ((flags & CLRDATA_FIELD_FROM_STATIC) == 0) + { + fieldIterFlags &= ~ApproxFieldDescIterator::STATIC_FIELDS; + } + + bool includeParents; + + if ((flags & CLRDATA_FIELD_IS_INHERITED) == 0) + { + if (fromType) + { + typeHandle = ((ClrDataTypeInstance*)fromType)->GetTypeHandle(); + } + includeParents = false; + } + else if (fromType) + { + return E_INVALIDARG; + } + else + { + includeParents = true; + } + + if (typeHandle.IsNull() || + !typeHandle.GetMethodTable() || + !typeHandle.IsRestored()) + { + return E_INVALIDARG; + } + + fieldIter->Init(typeHandle.GetMethodTable(), fieldIterFlags, includeParents); + + return S_OK; +} + +ULONG32 +GetTypeFieldValueFlags(TypeHandle typeHandle, + FieldDesc* fieldDesc, + ULONG32 otherFlags, + bool isDeref) +{ + otherFlags &= ~(CLRDATA_VALUE_IS_PRIMITIVE | + CLRDATA_VALUE_IS_VALUE_TYPE | + CLRDATA_VALUE_IS_STRING | + CLRDATA_VALUE_IS_ARRAY | + CLRDATA_VALUE_IS_REFERENCE | + CLRDATA_VALUE_IS_POINTER | + CLRDATA_VALUE_IS_ENUM); + + CorElementType eltType; + + if (fieldDesc) + { + eltType = fieldDesc->GetFieldType(); + } + else + { + _ASSERTE(!typeHandle.IsNull()); + eltType = typeHandle.GetInternalCorElementType(); + } + + if (!isDeref && CorTypeInfo::IsObjRef_NoThrow(eltType)) + { + otherFlags |= CLRDATA_VALUE_IS_REFERENCE; + } + else if (typeHandle.IsEnum()) + { + otherFlags |= CLRDATA_VALUE_IS_ENUM; + } + else if (eltType == ELEMENT_TYPE_STRING) + { + otherFlags |= CLRDATA_VALUE_IS_STRING; + } + else if (eltType == ELEMENT_TYPE_PTR) + { + otherFlags |= CLRDATA_VALUE_IS_POINTER; + } + else if (CorTypeInfo::IsPrimitiveType_NoThrow(eltType)) + { + otherFlags |= CLRDATA_VALUE_IS_PRIMITIVE; + } + else if (typeHandle.IsArray()) + { + otherFlags |= CLRDATA_VALUE_IS_ARRAY; + } + else if (typeHandle.IsValueType()) + { + otherFlags |= CLRDATA_VALUE_IS_VALUE_TYPE; + } + else if (eltType == ELEMENT_TYPE_CLASS) + { + // + // Perform extra checks to identify well-known classes. + // + + if ((&g_Mscorlib)->IsClass(typeHandle.GetMethodTable(), CLASS__STRING)) + { + otherFlags |= CLRDATA_VALUE_IS_STRING; + } + } + + if (fieldDesc) + { + otherFlags &= ~(CLRDATA_VALUE_IS_LITERAL | + CLRDATA_VALUE_FROM_INSTANCE | + CLRDATA_VALUE_FROM_TASK_LOCAL | + CLRDATA_VALUE_FROM_STATIC); + + if ((isDeref || + (otherFlags & CLRDATA_VALUE_IS_REFERENCE) == 0) && + IsFdLiteral(fieldDesc->GetAttributes())) + { + otherFlags |= CLRDATA_VALUE_IS_LITERAL; + } + + if (fieldDesc->IsStatic()) + { + otherFlags |= CLRDATA_VALUE_FROM_STATIC; + } + else if (fieldDesc->IsThreadStatic()) + { + otherFlags |= CLRDATA_VALUE_FROM_TASK_LOCAL; + } + else +#ifdef FEATURE_REMOTING + if (!fieldDesc->IsContextStatic()) +#endif + { + otherFlags |= CLRDATA_VALUE_FROM_INSTANCE; + } + } + + return otherFlags; +} + +//---------------------------------------------------------------------------- +// +// ClrDataValue. +// +//---------------------------------------------------------------------------- + +ClrDataValue::ClrDataValue(ClrDataAccess* dac, + AppDomain* appDomain, + Thread* thread, + ULONG32 flags, + TypeHandle typeHandle, + ULONG64 baseAddr, + ULONG32 numLocs, + NativeVarLocation* locs) +{ + m_dac = dac; + m_dac->AddRef(); + m_instanceAge = m_dac->m_instanceAge; + m_refs = 1; + m_appDomain = appDomain; + m_thread = thread; + m_flags = flags; + m_typeHandle = typeHandle; + m_baseAddr = baseAddr; + m_numLocs = numLocs; + if (numLocs) + { + memcpy(m_locs, locs, numLocs * sizeof(m_locs[0])); + } + + if (numLocs && (m_flags & CLRDATA_VALUE_IS_REFERENCE) != 0) + { + m_totalSize = sizeof(TADDR); + } + else + { + m_totalSize = 0; + for (ULONG32 i = 0; i < m_numLocs; i++) + { + m_totalSize += m_locs[i].size; + } + } +} + +ClrDataValue::~ClrDataValue(void) +{ + m_dac->Release(); +} + +STDMETHODIMP +ClrDataValue::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataValue))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataValue*>(this)); + return S_OK; + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +ClrDataValue::AddRef(THIS) +{ + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataValue::Release(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetFlags( + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *flags = m_flags; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetAddress( + /* [out] */ CLRDATA_ADDRESS *address) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // This query can only be answered if there's a + // single non-register address. + if (m_numLocs == 1 && + !m_locs[0].contextReg) + { + *address = TO_CDADDR(m_locs[0].addr); + status = S_OK; + } + else + { + status = E_NOINTERFACE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetSize( + /* [out] */ ULONG64 *size) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_totalSize) + { + *size = m_totalSize; + status = S_OK; + } + else + { + status = E_NOINTERFACE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT +ClrDataValue::IntGetBytes( + /* [in] */ ULONG32 bufLen, + /* [size_is][out] */ BYTE buffer[ ]) +{ + HRESULT status; + + NativeVarLocation* loc = m_locs; + for (ULONG32 i = 0; i < m_numLocs; i++) + { + if (loc->contextReg) + { + memcpy(buffer, (void*)(ULONG_PTR)loc->addr, loc->size); + buffer += loc->size; + } + else + { + ULONG32 done; + + _ASSERTE(FitsIn<ULONG32>(loc->size)); + status = m_dac->m_pTarget-> + ReadVirtual(loc->addr, buffer, static_cast<ULONG32>(loc->size), + &done); + if (status != S_OK) + { + return CORDBG_E_READVIRTUAL_FAILURE; + } + if (done != loc->size) + { + return HRESULT_FROM_WIN32(ERROR_READ_FAULT); + } + + buffer += loc->size; + } + + loc++; + } + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetBytes( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *dataSize, + /* [size_is][out] */ BYTE buffer[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_totalSize) + { + status = E_NOINTERFACE; + goto Exit; + } + + if (dataSize) + { + _ASSERTE(FitsIn<ULONG32>(m_totalSize)); + *dataSize = static_cast<ULONG32>(m_totalSize); + } + + if (bufLen < m_totalSize) + { + status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); + goto Exit; + } + + status = IntGetBytes(bufLen, buffer); + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::SetBytes( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *dataSize, + /* [size_is][in] */ BYTE buffer[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + NativeVarLocation* loc = NULL; + + if (!m_totalSize) + { + status = E_NOINTERFACE; + goto Exit; + } + + if (dataSize) + { + _ASSERTE(FitsIn<ULONG32>(m_totalSize)); + *dataSize = static_cast<ULONG32>(m_totalSize); + } + + if (bufLen < m_totalSize) + { + status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); + goto Exit; + } + + loc = m_locs; + for (ULONG32 i = 0; i < m_numLocs; i++) + { + if (loc->contextReg) + { + // XXX Microsoft - Context update? + // memcpy(buffer, (void*)(ULONG_PTR)loc->addr, loc->size); + // buffer += loc->size; + // until drew decides, return notimpl + status = E_NOTIMPL; + goto Exit; + } + else + { + _ASSERT(FitsIn<ULONG32>(loc->size)); + status = m_dac->m_pMutableTarget-> + WriteVirtual(loc->addr, buffer, static_cast<ULONG32>(loc->size)); + if (status != S_OK) + { + goto Exit; + } + + buffer += loc->size; + } + + loc++; + } + + status = S_OK; + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetType( + /* [out] */ IXCLRDataTypeInstance **typeInstance) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if ((m_flags & CLRDATA_VALUE_IS_REFERENCE) != 0) + { + *typeInstance = NULL; + status = S_FALSE; + } + else if (!m_appDomain || + m_typeHandle.IsNull()) + { + status = E_NOTIMPL; + } + else + { + *typeInstance = new (nothrow) + ClrDataTypeInstance(m_dac, m_appDomain, m_typeHandle); + status = *typeInstance ? S_OK : E_OUTOFMEMORY; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetNumFields( + /* [out] */ ULONG32 *numFields) +{ + // XXX Microsoft - Obsolete method, never implemented. + return E_UNEXPECTED; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetFieldByIndex( + /* [in] */ ULONG32 index, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ mdFieldDef *token) +{ + // XXX Microsoft - Obsolete method, never implemented. + return E_UNEXPECTED; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetNumFields2( + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataTypeInstance *fromType, + /* [out] */ ULONG32 *numFields) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + DeepFieldDescIterator fieldIter; + + if ((status = InitFieldIter(&fieldIter, m_typeHandle, CanHaveFields(), + flags, fromType)) == S_OK) + { + *numFields = fieldIter.Count(); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::StartEnumFields( + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataTypeInstance *fromType, + /* [out] */ CLRDATA_ENUM *handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName:: + CdStartField(NULL, + 0, + flags, + fromType, + m_typeHandle, + NULL, + mdTypeDefNil, + m_baseAddr, + m_thread, + NULL, + m_appDomain, + NULL, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::EnumField( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 nameBufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(nameBufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ mdFieldDef *token) +{ + return EnumField2(handle, field, nameBufLen, nameLen, nameBuf, + NULL, token); +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::EnumField2( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 nameBufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(nameBufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataModule** tokenScope, + /* [out] */ mdFieldDef *token) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdNextField(m_dac, handle, NULL, NULL, field, + nameBufLen, nameLen, nameBuf, + tokenScope, token); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::EndEnumFields( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::StartEnumFieldsByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 nameFlags, + /* [in] */ ULONG32 fieldFlags, + /* [in] */ IXCLRDataTypeInstance *fromType, + /* [out] */ CLRDATA_ENUM *handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName:: + CdStartField(name, + nameFlags, + fieldFlags, + fromType, + m_typeHandle, + NULL, + mdTypeDefNil, + m_baseAddr, + m_thread, + NULL, + m_appDomain, + NULL, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::EnumFieldByName( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **field, + /* [out] */ mdFieldDef *token) +{ + return EnumFieldByName2(handle, field, NULL, token); +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::EnumFieldByName2( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataValue **field, + /* [out] */ IXCLRDataModule** tokenScope, + /* [out] */ mdFieldDef *token) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdNextField(m_dac, handle, NULL, NULL, field, + 0, NULL, NULL, + tokenScope, token); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::EndEnumFieldsByName( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetFieldByToken( + /* [in] */ mdFieldDef token, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ]) +{ + return GetFieldByToken2(NULL, token, field, bufLen, nameLen, nameBuf); +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetFieldByToken2( + /* [in] */ IXCLRDataModule* tokenScope, + /* [in] */ mdFieldDef token, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + DeepFieldDescIterator fieldIter; + + if ((status = InitFieldIter(&fieldIter, m_typeHandle, CanHaveFields(), + CLRDATA_VALUE_ALL_FIELDS, NULL)) == S_OK) + { + FieldDesc* fieldDesc; + + status = E_INVALIDARG; + while ((fieldDesc = fieldIter.Next())) + { + if ((!tokenScope || + PTR_HOST_TO_TADDR(((ClrDataModule*)tokenScope)-> + GetModule()) == + PTR_HOST_TO_TADDR(fieldDesc->GetModule())) && + fieldDesc->GetMemberDef() == token) + { + status = NewFromSubField(fieldDesc, + fieldIter. + IsFieldFromParentClass() ? + CLRDATA_VALUE_IS_INHERITED : 0, + NULL, field, + bufLen, nameLen, nameBuf, + NULL, NULL); + break; + } + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT +ClrDataValue::GetRefAssociatedValue(IXCLRDataValue** assocValue) +{ + HRESULT status; + + if (m_typeHandle.IsNull()) + { + return E_NOINTERFACE; + } + + TADDR refAddr; + + _ASSERTE(m_totalSize == sizeof(refAddr)); + + if ((status = IntGetBytes(sizeof(refAddr), + (PBYTE)&refAddr)) != S_OK) + { + return status; + } + + // We assume that objrefs always refer + // to objects so there is no ref chain. + ULONG32 valueFlags = + GetTypeFieldValueFlags(m_typeHandle, NULL, + m_flags & CLRDATA_VALUE_ALL_LOCATIONS, true); + + NativeVarLocation loc; + + loc.addr = TO_CDADDR(refAddr); + // XXX Microsoft - Good way to get the right size for everything? + loc.size = (m_typeHandle.GetMethodTable())->GetBaseSize(); + loc.contextReg = false; + + *assocValue = new (nothrow) + ClrDataValue(m_dac, + m_appDomain, + m_thread, + valueFlags, + m_typeHandle, + loc.addr, + 1, + &loc); + return *assocValue ? S_OK : E_OUTOFMEMORY; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetAssociatedValue( + /* [out] */ IXCLRDataValue **assocValue) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_numLocs && (m_flags & CLRDATA_VALUE_IS_REFERENCE) != 0) + { + status = GetRefAssociatedValue(assocValue); + } + else + { + status = E_NOINTERFACE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetAssociatedType( + /* [out] */ IXCLRDataTypeInstance **assocType) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + TypeHandle dacType; + + if ((m_flags & CLRDATA_VALUE_IS_REFERENCE) != 0) + { + dacType = m_typeHandle; + } + else if ((m_flags & CLRDATA_VALUE_IS_ARRAY) != 0) + { + ArrayBase* arrayBase = PTR_ArrayBase(CLRDATA_ADDRESS_TO_TADDR(m_baseAddr)); + dacType = arrayBase->GetArrayElementTypeHandle(); + } + + if (dacType.IsNull()) + { + status = E_NOINTERFACE; + } + else + { + *assocType = new (nothrow) + ClrDataTypeInstance(m_dac, + m_appDomain, + dacType); + status = *assocType ? S_OK : E_OUTOFMEMORY; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetString( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *strLen, + /* [size_is][out] */ __out_ecount_part(bufLen, *strLen) WCHAR str[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if ((m_flags & CLRDATA_VALUE_IS_STRING) != 0) + { + STRINGREF message = STRINGREF(TO_TADDR(m_baseAddr)); + + PWSTR msgStr = DacInstantiateStringW((TADDR)message->GetBuffer(), + message->GetStringLength(), + true); + + if (strLen) + { + *strLen = static_cast<ULONG32>(wcslen(msgStr) + 1); + } + status = StringCchCopy(str, bufLen, msgStr) == S_OK ? + S_OK : S_FALSE; + } + else + { + status = E_INVALIDARG; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetArrayProperties( + /* [out] */ ULONG32 *rank, + /* [out] */ ULONG32 *totalElements, + /* [in] */ ULONG32 numDim, + /* [size_is][out] */ ULONG32 dims[ ], + /* [in] */ ULONG32 numBases, + /* [size_is][out] */ LONG32 bases[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if ((m_flags & CLRDATA_VALUE_IS_ARRAY) != 0) + { + ArrayBase* arrayBase = PTR_ArrayBase(CLRDATA_ADDRESS_TO_TADDR(m_baseAddr)); + unsigned baseRank = arrayBase->GetRank(); + unsigned i; + + if (rank) + { + *rank = baseRank; + } + + if (totalElements) + { + *totalElements = arrayBase->GetNumComponents(); + } + + if (numDim) + { + PTR_INT32 bounds = arrayBase->GetBoundsPtr(); + + for (i = 0; i < baseRank; i++) + { + if (i < numDim) + { + dims[i] = bounds[i]; + } + else + { + break; + } + } + } + + if (numBases) + { + PTR_INT32 lowBounds = arrayBase->GetLowerBoundsPtr(); + + for (i = 0; i < baseRank; i++) + { + if (i < numBases) + { + bases[i] = lowBounds[i]; + } + else + { + break; + } + } + } + + status = S_OK; + } + else + { + status = E_INVALIDARG; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetArrayElement( + /* [in] */ ULONG32 numInd, + /* [size_is][in] */ LONG32 indices[ ], + /* [out] */ IXCLRDataValue **value) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + PTR_INT32 bounds, lowBounds; + TypeHandle eltType; + + if ((m_flags & CLRDATA_VALUE_IS_ARRAY) == 0) + { + status = E_INVALIDARG; + goto Exit; + } + + ArrayBase* arrayBase; + unsigned baseRank; + + arrayBase = PTR_ArrayBase(CLRDATA_ADDRESS_TO_TADDR(m_baseAddr)); + baseRank = arrayBase->GetRank(); + if (numInd != baseRank) + { + status = E_INVALIDARG; + goto Exit; + } + + eltType = arrayBase->GetArrayElementTypeHandle(); + if (eltType.IsNull()) + { + status = E_INVALIDARG; + goto Exit; + } + + unsigned dim; + ULONG64 offs; + SIZE_T dimSize; + + dim = baseRank; + offs = TO_CDADDR(PTR_TO_TADDR(arrayBase->GetDataPtr())); + dimSize = arrayBase->GetComponentSize(); + bounds = arrayBase->GetBoundsPtr(); + lowBounds = arrayBase->GetLowerBoundsPtr(); + + while (dim-- > 0) + { + if (indices[dim] < lowBounds[dim]) + { + status = E_INVALIDARG; + goto Exit; + } + + UINT32 uindex = (UINT32)(indices[dim] - lowBounds[dim]); + if (uindex >= (UINT32)bounds[dim]) + { + status = E_INVALIDARG; + goto Exit; + } + + offs += dimSize * uindex; + + dimSize *= (UINT64)bounds[dim]; + } + + NativeVarLocation loc; + + loc.addr = offs; + loc.size = eltType.GetSize(); + loc.contextReg = false; + + *value = new (nothrow) + ClrDataValue(m_dac, m_appDomain, m_thread, + GetTypeFieldValueFlags(eltType, NULL, 0, false), + eltType, offs, 1, &loc); + status = *value ? S_OK : E_OUTOFMEMORY; + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetNumLocations( + /* [out] */ ULONG32* numLocs) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *numLocs = m_numLocs; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::GetLocationByIndex( + /* [in] */ ULONG32 loc, + /* [out] */ ULONG32* flags, + /* [out] */ CLRDATA_ADDRESS* arg) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (loc < m_numLocs) + { + if (m_locs[loc].contextReg) + { + *flags = CLRDATA_VLOC_REGISTER; + *arg = 0; + } + else + { + *flags = CLRDATA_VLOC_MEMORY; + *arg = TO_CDADDR(m_locs[loc].addr); + } + + status = S_OK; + } + else + { + status = E_INVALIDARG; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataValue::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + switch(reqCode) + { + case CLRDATA_REQUEST_REVISION: + if (inBufferSize != 0 || + inBuffer || + outBufferSize != sizeof(ULONG32)) + { + status = E_INVALIDARG; + } + else + { + *(ULONG32*)outBuffer = 3; + status = S_OK; + } + break; + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT +ClrDataValue::NewFromFieldDesc(ClrDataAccess* dac, + AppDomain* appDomain, + ULONG32 flags, + FieldDesc* fieldDesc, + ULONG64 objBase, + Thread* tlsThread, + ClrDataValue** value, + IXCLRDataValue** pubValue, + ULONG32 nameBufRetLen, + ULONG32 *nameLenRet, + __out_ecount_part_opt(nameBufRetLen, *nameLenRet) WCHAR nameBufRet[ ], + IXCLRDataModule** tokenScopeRet, + mdFieldDef *tokenRet) +{ + HRESULT status; + ClrDataValue* field; + ULONG numLocs = 1; + NativeVarLocation varLoc, *locs = &varLoc; + ULONG64 baseAddr; + LPCUTF8 szFieldName; + + status = fieldDesc->GetName_NoThrow(&szFieldName); + if (status != S_OK) + { + return status; + } + + status = ConvertUtf8( + szFieldName, + nameBufRetLen, + nameLenRet, + nameBufRet); + if (status != S_OK) + { + return status; + } + + if (tokenRet != NULL) + { + *tokenRet = fieldDesc->GetMemberDef(); + } + + if (fieldDesc->GetEnclosingMethodTable()->ContainsGenericVariables()) + { + // This field is for a generic type definition and + // so doesn't have a real location. Produce + // a placeholder no-data value. + numLocs = 0; + locs = NULL; + baseAddr = 0; + } + else if (fieldDesc->IsThreadStatic()) + { + if (!tlsThread) + { + return E_INVALIDARG; + } + + baseAddr = + TO_CDADDR(tlsThread->GetStaticFieldAddrNoCreate(fieldDesc, NULL)); + } +#ifdef FEATURE_REMOTING + else if (fieldDesc->IsContextStatic()) + { + // XXX Microsoft - Microsoft says Context is going away. + return E_NOTIMPL; + } +#endif + else if (fieldDesc->IsStatic()) + { + baseAddr = TO_CDADDR + (PTR_TO_TADDR(fieldDesc->GetStaticAddressHandle + (fieldDesc->GetBaseInDomain(appDomain)))); + } + else + { + // objBase is basically a CLRDATA_ADDRESS, which is a pointer-sized target address sign-extened to + // 64-bit. We need to get a TADDR here, which is a pointer-size unsigned value. + baseAddr = TO_CDADDR(PTR_TO_TADDR(fieldDesc->GetAddress(PTR_VOID(CLRDATA_ADDRESS_TO_TADDR(objBase))))); + } + + if (locs) + { + locs->addr = baseAddr; + locs->size = fieldDesc->GetSize(); + locs->contextReg = false; + } + + TypeHandle typeHandle = fieldDesc->LookupFieldTypeHandle(); + + // We allow no-type situations for reference fields + // as they can still be useful even though they cannot + // be expanded. This is also a common case where the + // referred-to type may not be loaded if the field + // is holding null. + if (typeHandle.IsNull() && !fieldDesc->IsObjRef()) + { + return E_INVALIDARG; + } + + flags = GetTypeFieldValueFlags(typeHandle, fieldDesc, flags, false); + + if (tokenScopeRet) + { + *tokenScopeRet = new (nothrow) + ClrDataModule(dac, fieldDesc->GetModule()); + if (!*tokenScopeRet) + { + return E_OUTOFMEMORY; + } + } + + field = new (nothrow) ClrDataValue(dac, + appDomain, + tlsThread, + flags, + typeHandle, + baseAddr, + numLocs, + locs); + if (value) + { + *value = field; + } + if (pubValue) + { + *pubValue = field; + } + + if (!field) + { + if (tokenScopeRet) + { + delete (ClrDataModule*)*tokenScopeRet; + } + return E_OUTOFMEMORY; + } + + return S_OK; +} + +//---------------------------------------------------------------------------- +// +// ClrDataTypeDefinition. +// +//---------------------------------------------------------------------------- + +ClrDataTypeDefinition::ClrDataTypeDefinition(ClrDataAccess* dac, + Module* module, + mdTypeDef token, + TypeHandle typeHandle) +{ + m_dac = dac; + m_dac->AddRef(); + m_instanceAge = m_dac->m_instanceAge; + m_refs = 1; + m_module = module; + m_token = token; + m_typeHandle = typeHandle; +} + +ClrDataTypeDefinition::~ClrDataTypeDefinition(void) +{ + m_dac->Release(); +} + +STDMETHODIMP +ClrDataTypeDefinition::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataTypeDefinition))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataTypeDefinition*>(this)); + return S_OK; + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +ClrDataTypeDefinition::AddRef(THIS) +{ + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataTypeDefinition::Release(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::GetModule( + /* [out] */ IXCLRDataModule **mod) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *mod = new (nothrow) + ClrDataModule(m_dac, m_module); + status = *mod ? S_OK : E_OUTOFMEMORY; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::StartEnumMethodDefinitions( + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = MetaEnum::New(m_module, + mdtMethodDef, + m_token, + NULL, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::EnumMethodDefinition( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodDefinition **methodDefinition) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + mdMethodDef token; + + if ((status = MetaEnum::CdNextToken(handle, &token)) == S_OK) + { + status = ClrDataMethodDefinition:: + NewFromModule(m_dac, + m_module, + token, + NULL, + methodDefinition); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::EndEnumMethodDefinitions( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = MetaEnum::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::StartEnumMethodDefinitionsByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdStartMethod(name, + flags, + m_module, + m_token, + NULL, + NULL, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::EnumMethodDefinitionByName( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodDefinition **method) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + mdMethodDef token; + + if ((status = SplitName::CdNextMethod(handle, &token)) == S_OK) + { + status = ClrDataMethodDefinition:: + NewFromModule(m_dac, + m_module, + token, + NULL, + method); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::EndEnumMethodDefinitionsByName( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::GetMethodDefinitionByToken( + /* [in] */ mdMethodDef token, + /* [out] */ IXCLRDataMethodDefinition **methodDefinition) +{ + HRESULT status; + + // This isn't critically necessary but it prevents + // an assert in the metadata code. + if (TypeFromToken(token) != mdtMethodDef) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = ClrDataMethodDefinition:: + NewFromModule(m_dac, + m_module, + token, + NULL, + methodDefinition); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::StartEnumInstances( + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [out] */ CLRDATA_ENUM *handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::EnumInstance( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataTypeInstance **instance) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::EndEnumInstances( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::GetNumFields( + /* [in] */ ULONG32 flags, + /* [out] */ ULONG32 *numFields) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_typeHandle.IsNull()) + { + status = E_NOTIMPL; + } + else + { + DeepFieldDescIterator fieldIter; + + if ((status = InitFieldIter(&fieldIter, m_typeHandle, true, + flags, NULL)) == S_OK) + { + *numFields = fieldIter.Count(); + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::StartEnumFields( + /* [in] */ ULONG32 flags, + /* [out] */ CLRDATA_ENUM *handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_typeHandle.IsNull()) + { + *handle = 0; + status = E_NOTIMPL; + } + else + { + status = SplitName:: + CdStartField(NULL, + 0, + flags, + NULL, + m_typeHandle, + NULL, + mdTypeDefNil, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + handle); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::EnumField( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [in] */ ULONG32 nameBufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(nameBufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataTypeDefinition **type, + /* [out] */ ULONG32 *flags, + /* [out] */ mdFieldDef *token) +{ + return EnumField2(handle, nameBufLen, nameLen, nameBuf, + type, flags, NULL, token); +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::EnumField2( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [in] */ ULONG32 nameBufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(nameBufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataTypeDefinition **type, + /* [out] */ ULONG32 *flags, + /* [out] */ IXCLRDataModule** tokenScope, + /* [out] */ mdFieldDef *token) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdNextField(m_dac, handle, type, flags, NULL, + nameBufLen, nameLen, nameBuf, + tokenScope, token); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::EndEnumFields( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::StartEnumFieldsByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 nameFlags, + /* [in] */ ULONG32 fieldFlags, + /* [out] */ CLRDATA_ENUM *handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_typeHandle.IsNull()) + { + *handle = 0; + status = E_NOTIMPL; + } + else + { + status = SplitName:: + CdStartField(name, + nameFlags, + fieldFlags, + NULL, + m_typeHandle, + NULL, + mdTypeDefNil, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + handle); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::EnumFieldByName( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataTypeDefinition **type, + /* [out] */ ULONG32 *flags, + /* [out] */ mdFieldDef *token) +{ + return EnumFieldByName2(handle, type, flags, NULL, token); +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::EnumFieldByName2( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataTypeDefinition **type, + /* [out] */ ULONG32 *flags, + /* [out] */ IXCLRDataModule** tokenScope, + /* [out] */ mdFieldDef *token) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdNextField(m_dac, handle, type, flags, NULL, + 0, NULL, NULL, + tokenScope, token); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::EndEnumFieldsByName( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::GetFieldByToken( + /* [in] */ mdFieldDef token, + /* [in] */ ULONG32 nameBufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(nameBufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataTypeDefinition **type, + /* [out] */ ULONG32 *flags) +{ + return GetFieldByToken2(NULL, token, nameBufLen, nameLen, nameBuf, + type, flags); +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::GetFieldByToken2( + /* [in] */ IXCLRDataModule* tokenScope, + /* [in] */ mdFieldDef token, + /* [in] */ ULONG32 nameBufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(nameBufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataTypeDefinition **type, + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + DeepFieldDescIterator fieldIter; + + if (m_typeHandle.IsNull()) + { + status = E_NOTIMPL; + goto Exit; + } + + if ((status = InitFieldIter(&fieldIter, m_typeHandle, true, + CLRDATA_VALUE_ALL_FIELDS, NULL)) == S_OK) + { + FieldDesc* fieldDesc; + + status = E_INVALIDARG; + while ((fieldDesc = fieldIter.Next())) + { + if ((!tokenScope || + PTR_HOST_TO_TADDR(((ClrDataModule*)tokenScope)-> + GetModule()) == + PTR_HOST_TO_TADDR(fieldDesc->GetModule())) && + fieldDesc->GetMemberDef() == token) + { + if (flags) + { + *flags = GetTypeFieldValueFlags(m_typeHandle, + fieldDesc, + fieldIter.IsFieldFromParentClass() ? + CLRDATA_VALUE_IS_INHERITED : 0, + false); + } + + status = ConvertUtf8(fieldDesc->GetName(), + nameBufLen, nameLen, nameBuf); + + if (SUCCEEDED(status) && type) + { + TypeHandle fieldTypeHandle = + fieldDesc->LookupFieldTypeHandle(); + *type = new (nothrow) + ClrDataTypeDefinition(m_dac, + fieldTypeHandle.GetModule(), + fieldTypeHandle. + GetMethodTable()->GetCl(), + fieldTypeHandle); + status = *type ? S_OK : E_OUTOFMEMORY; + } + + break; + } + } + } + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::GetName( + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ]) +{ + HRESULT status = S_OK; + + if (flags != 0) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + char classNameBuf[MAX_CLASSNAME_LENGTH]; + + if (m_typeHandle.IsNull()) + { + if ((status = + GetFullClassNameFromMetadata(m_module->GetMDImport(), + m_token, + NumItems(classNameBuf), + classNameBuf)) == S_OK) + { + status = ConvertUtf8(classNameBuf, bufLen, nameLen, nameBuf); + } + } + else + { + StackSString ssClassNameBuf; + m_typeHandle.GetName(ssClassNameBuf); + if (wcsncpy_s(nameBuf, bufLen, ssClassNameBuf.GetUnicode(), _TRUNCATE) == STRUNCATE) + { + status = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + if (nameLen != NULL) + { + size_t cchName = ssClassNameBuf.GetCount() + 1; + if (FitsIn<ULONG32>(cchName)) + { + *nameLen = (ULONG32) cchName; + } + else + { + status = COR_E_OVERFLOW; + } + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::GetTokenAndScope( + /* [out] */ mdTypeDef *token, + /* [out] */ IXCLRDataModule **mod) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = S_OK; + + if (token) + { + *token = m_token; + } + + if (mod) + { + *mod = new (nothrow) + ClrDataModule(m_dac, m_module); + status = *mod ? S_OK : E_OUTOFMEMORY; + } + + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::GetCorElementType( + /* [out] */ CorElementType *type) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_typeHandle.IsNull()) + { + status = E_NOTIMPL; + } + else + { + *type = m_typeHandle.GetInternalCorElementType(); + status = S_OK; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::GetFlags( + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *flags = CLRDATA_TYPE_DEFAULT; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::GetBase( + /* [out] */ IXCLRDataTypeDefinition **base) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + mdTypeDef token; + TypeHandle typeHandle; + + if (m_typeHandle.IsNull()) + { + ULONG attr; + + status = m_module->GetMDImport()->GetTypeDefProps(m_token, &attr, &token); + if (FAILED(status)) + { + goto Exit; + } + } + else + { + typeHandle = m_typeHandle.GetParent(); + if (typeHandle.IsNull() || + !typeHandle.GetMethodTable()) + { + status = E_NOINTERFACE; + goto Exit; + } + + token = typeHandle.GetMethodTable()->GetCl(); + } + + *base = new (nothrow) + ClrDataTypeDefinition(m_dac, m_module, token, typeHandle); + status = *base ? S_OK : E_OUTOFMEMORY; + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::GetArrayRank( + /* [out] */ ULONG32* rank) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_typeHandle.IsNull()) + { + status = E_NOTIMPL; + } + else + { + MethodTable* pMT = m_typeHandle.GetMethodTable(); + + if (!m_typeHandle.IsArray() || + (pMT == NULL)) + { + status = E_NOINTERFACE; + } + else + { + *rank = pMT->GetRank(); + status = S_OK; + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::IsSameObject( + /* [in] */ IXCLRDataTypeDefinition* type) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_typeHandle.IsNull()) + { + status = (PTR_HOST_TO_TADDR(m_module) == + PTR_HOST_TO_TADDR(((ClrDataTypeDefinition*)type)-> + m_module) && + m_token == ((ClrDataTypeDefinition*)type)->m_token) ? + S_OK : S_FALSE; + } + else + { + status = (m_typeHandle.AsTAddr() == + ((ClrDataTypeDefinition*)type)->m_typeHandle.AsTAddr()) ? + S_OK : S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::GetTypeNotification( + /* [out] */ ULONG32* flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::SetTypeNotification( + /* [in] */ ULONG32 flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeDefinition::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + switch(reqCode) + { + case CLRDATA_REQUEST_REVISION: + if (inBufferSize != 0 || + inBuffer || + outBufferSize != sizeof(ULONG32)) + { + status = E_INVALIDARG; + } + else + { + *(ULONG32*)outBuffer = 2; + status = S_OK; + } + break; + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT +ClrDataTypeDefinition::NewFromModule(ClrDataAccess* dac, + Module* module, + mdTypeDef token, + ClrDataTypeDefinition** typeDef, + IXCLRDataTypeDefinition** pubTypeDef) +{ + // The type may not be loaded yet so the + // absence of a TypeHandle is not fatal. + // If the type isn't loaded a metadata-query + // TypeDefinition is produced. + TypeHandle typeHandle = module->LookupTypeDef(token); + if (!typeHandle.IsNull() && + !typeHandle.IsRestored()) + { + // The type isn't fully usable so just go with metadata. + typeHandle = TypeHandle(); + } + + ClrDataTypeDefinition* def = new (nothrow) + ClrDataTypeDefinition(dac, module, token, typeHandle); + if (!def) + { + return E_OUTOFMEMORY; + } + + PREFIX_ASSUME(typeDef || pubTypeDef); + + if (typeDef) + { + *typeDef = def; + } + if (pubTypeDef) + { + *pubTypeDef = def; + } + + return S_OK; +} + +//---------------------------------------------------------------------------- +// +// ClrDataTypeInstance. +// +//---------------------------------------------------------------------------- + +ClrDataTypeInstance::ClrDataTypeInstance(ClrDataAccess* dac, + AppDomain* appDomain, + TypeHandle typeHandle) +{ + m_dac = dac; + m_dac->AddRef(); + m_instanceAge = m_dac->m_instanceAge; + m_refs = 1; + m_appDomain = appDomain; + m_typeHandle = typeHandle; +} + +ClrDataTypeInstance::~ClrDataTypeInstance(void) +{ + m_dac->Release(); +} + +STDMETHODIMP +ClrDataTypeInstance::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataTypeInstance))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataTypeInstance*>(this)); + return S_OK; + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +ClrDataTypeInstance::AddRef(THIS) +{ + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataTypeInstance::Release(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::StartEnumMethodInstances( + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_typeHandle.GetMethodTable()) + { + *handle = 0; + status = S_FALSE; + goto Exit; + } + + status = MetaEnum::New(m_typeHandle.GetModule(), + mdtMethodDef, + m_typeHandle.GetCl(), + NULL, + NULL, + handle); + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::EnumMethodInstance( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodInstance **method) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + for (;;) + { + mdMethodDef token; + + if ((status = MetaEnum::CdNextToken(handle, &token)) != S_OK) + { + break; + } + + // If the method doesn't have a MethodDesc or hasn't + // been JIT'ed yet it's not an instance and should + // just be skipped. + if ((status = ClrDataMethodInstance:: + NewFromModule(m_dac, + m_appDomain, + m_typeHandle.GetModule(), + token, + NULL, + method)) != E_INVALIDARG) + { + break; + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::EndEnumMethodInstances( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = MetaEnum::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::StartEnumMethodInstancesByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_typeHandle.GetMethodTable()) + { + *handle = 0; + status = S_FALSE; + goto Exit; + } + + status = SplitName::CdStartMethod(name, + flags, + m_typeHandle.GetModule(), + m_typeHandle.GetCl(), + m_appDomain, + NULL, + NULL, + handle); + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::EnumMethodInstanceByName( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodInstance **method) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + for (;;) + { + mdMethodDef token; + + if ((status = SplitName::CdNextMethod(handle, &token)) != S_OK) + { + break; + } + + // If the method doesn't have a MethodDesc or hasn't + // been JIT'ed yet it's not an instance and should + // just be skipped. + if ((status = ClrDataMethodInstance:: + NewFromModule(m_dac, + m_appDomain, + m_typeHandle.GetModule(), + token, + NULL, + method)) != E_INVALIDARG) + { + break; + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::EndEnumMethodInstancesByName( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::GetNumStaticFields( + /* [out] */ ULONG32 *numFields) +{ + return GetNumStaticFields2(INH_STATIC, numFields); +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::GetStaticFieldByIndex( + /* [in] */ ULONG32 index, + /* [in] */ IXCLRDataTask *tlsTask, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ mdFieldDef *token) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + DeepFieldDescIterator fieldIter; + + if ((status = InitFieldIter(&fieldIter, m_typeHandle, true, + INH_STATIC, NULL)) == S_OK) + { + ULONG32 count = 0; + FieldDesc* fieldDesc; + + status = E_INVALIDARG; + while ((fieldDesc = fieldIter.Next())) + { + if (count++ == index) + { + Thread* tlsThread = tlsTask ? + ((ClrDataTask*)tlsTask)->GetThread() : NULL; + + status = ClrDataValue:: + NewFromFieldDesc(m_dac, + m_appDomain, + fieldIter.IsFieldFromParentClass() ? + CLRDATA_VALUE_IS_INHERITED : 0, + fieldDesc, + 0, + tlsThread, + NULL, + field, + bufLen, + nameLen, + nameBuf, + NULL, + token); + break; + } + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::StartEnumStaticFieldsByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataTask* tlsTask, + /* [out] */ CLRDATA_ENUM* handle) +{ + return StartEnumStaticFieldsByName2(name, flags, INH_STATIC, tlsTask, + handle); +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::EnumStaticFieldByName( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataValue **value) +{ + return EnumStaticFieldByName2(handle, value); +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::EndEnumStaticFieldsByName( + /* [in] */ CLRDATA_ENUM handle) +{ + return EndEnumStaticFieldsByName2(handle); +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::GetNumStaticFields2( + /* [in] */ ULONG32 flags, + /* [out] */ ULONG32 *numFields) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + DeepFieldDescIterator fieldIter; + + if ((status = InitFieldIter(&fieldIter, m_typeHandle, true, + flags, NULL)) == S_OK) + { + *numFields = fieldIter.Count(); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::StartEnumStaticFields( + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataTask* tlsTask, + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName:: + CdStartField(NULL, + 0, + flags, + NULL, + m_typeHandle, + NULL, + mdTypeDefNil, + 0, + NULL, + tlsTask, + m_appDomain, + NULL, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::EnumStaticField( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataValue **value) +{ + return EnumStaticField2(handle, value, 0, NULL, NULL, + NULL, NULL); +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::EnumStaticField2( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataValue **value, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ], + /* [out] */ IXCLRDataModule** tokenScope, + /* [out] */ mdFieldDef *token) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdNextField(m_dac, handle, NULL, NULL, value, + bufLen, nameLen, nameBuf, + tokenScope, token); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::EndEnumStaticFields( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::StartEnumStaticFieldsByName2( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 nameFlags, + /* [in] */ ULONG32 fieldFlags, + /* [in] */ IXCLRDataTask* tlsTask, + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName:: + CdStartField(name, + nameFlags, + fieldFlags, + NULL, + m_typeHandle, + NULL, + mdTypeDefNil, + 0, + NULL, + tlsTask, + m_appDomain, + NULL, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::EnumStaticFieldByName2( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataValue **value) +{ + return EnumStaticFieldByName3(handle, value, NULL, NULL); +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::EnumStaticFieldByName3( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataValue **value, + /* [out] */ IXCLRDataModule** tokenScope, + /* [out] */ mdFieldDef *token) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdNextField(m_dac, handle, NULL, NULL, value, + 0, NULL, NULL, + tokenScope, token); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::EndEnumStaticFieldsByName2( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::GetStaticFieldByToken( + /* [in] */ mdFieldDef token, + /* [in] */ IXCLRDataTask *tlsTask, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ]) +{ + return GetStaticFieldByToken2(NULL, token, tlsTask, field, + bufLen, nameLen, nameBuf); +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::GetStaticFieldByToken2( + /* [in] */ IXCLRDataModule* tokenScope, + /* [in] */ mdFieldDef token, + /* [in] */ IXCLRDataTask *tlsTask, + /* [out] */ IXCLRDataValue **field, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + DeepFieldDescIterator fieldIter; + + if ((status = InitFieldIter(&fieldIter, m_typeHandle, true, + INH_STATIC, NULL)) == S_OK) + { + FieldDesc* fieldDesc; + + status = E_INVALIDARG; + while ((fieldDesc = fieldIter.Next())) + { + if ((!tokenScope || + PTR_HOST_TO_TADDR(((ClrDataModule*)tokenScope)-> + GetModule()) == + PTR_HOST_TO_TADDR(fieldDesc->GetModule())) && + fieldDesc->GetMemberDef() == token) + { + Thread* tlsThread = tlsTask ? + ((ClrDataTask*)tlsTask)->GetThread() : NULL; + + status = ClrDataValue:: + NewFromFieldDesc(m_dac, + m_appDomain, + fieldIter.IsFieldFromParentClass() ? + CLRDATA_VALUE_IS_INHERITED : 0, + fieldDesc, + 0, + tlsThread, + NULL, + field, + bufLen, + nameLen, + nameBuf, + NULL, + NULL); + break; + } + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::GetName( + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ]) +{ + HRESULT status = S_OK; + + if (flags != 0) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + StackSString ssClassNameBuf; + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + PAL_CPP_TRY + { +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + m_typeHandle.GetName(ssClassNameBuf); + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + } + PAL_CPP_CATCH_ALL + { + // if metadata is unavailable try the DAC's MdCache + ssClassNameBuf.Clear(); + PTR_MethodTable pMT = m_typeHandle.AsMethodTable(); + if (pMT != NULL) + { + if (!DacMdCacheGetEEName(dac_cast<TADDR>(pMT), ssClassNameBuf)) + { + ssClassNameBuf.Clear(); + } + } + if (ssClassNameBuf.IsEmpty()) + { + PAL_CPP_RETHROW; + } + } + PAL_CPP_ENDTRY +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + if (wcsncpy_s(nameBuf, bufLen, ssClassNameBuf.GetUnicode(), _TRUNCATE) == STRUNCATE) + { + status = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + if (nameLen != NULL) + { + size_t cchName = ssClassNameBuf.GetCount() + 1; + if (FitsIn<ULONG32>(cchName)) + { + *nameLen = (ULONG32) cchName; + } + else + { + status = COR_E_OVERFLOW; + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::GetModule( + /* [out] */ IXCLRDataModule **mod) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *mod = new (nothrow) + ClrDataModule(m_dac, m_typeHandle.GetModule()); + status = *mod ? S_OK : E_OUTOFMEMORY; + + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::GetDefinition( + /* [out] */ IXCLRDataTypeDefinition **typeDefinition) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + TypeHandle defType; + + if (m_typeHandle.IsArray() || m_typeHandle.IsFnPtrType()) + { + // Arrays don't necessarily have metadata so + // we can't rely on being able to look up + // a definition that way. + + // Also ByRef and FUNC_PTR does not have typedef token backing it. + + // Instead, just use the same type handle. + // XXX Microsoft - Generics issues? + + // Question - what does the GetCl return return here? The underlying element type? + // If so, we are lossing informaiton. + // + defType = m_typeHandle; + *typeDefinition = new (nothrow) + ClrDataTypeDefinition(m_dac, + defType.GetModule(), + defType.GetCl(), + defType); + } + + else if (m_typeHandle.IsTypeDesc() && m_typeHandle.AsTypeDesc()->HasTypeParam()) + { + // HasTypeParam is true for - ParamTypeDesc (ARRAY, SZARRAY, BYREF, PTR) + defType = m_typeHandle.AsTypeDesc()->GetTypeParam(); + + // The DefinitionType won't contain ByRef, PTR. + *typeDefinition = new (nothrow) + ClrDataTypeDefinition(m_dac, + defType.GetModule(), + defType.GetCl(), + defType); + } + else + { + // @TODO:: Should this be only for generic? + // + defType = m_typeHandle.GetModule()-> + LookupTypeDef(m_typeHandle.GetCl()); + *typeDefinition = new (nothrow) + ClrDataTypeDefinition(m_dac, + m_typeHandle.GetModule(), + m_typeHandle.GetCl(), + defType); + } + + status = *typeDefinition ? S_OK : E_OUTOFMEMORY; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::GetFlags( + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *flags = CLRDATA_TYPE_DEFAULT; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::GetBase( + /* [out] */ IXCLRDataTypeInstance **base) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *base = new (nothrow) + ClrDataTypeInstance(m_dac, m_appDomain, m_typeHandle.GetParent()); + status = *base ? S_OK : E_OUTOFMEMORY; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::IsSameObject( + /* [in] */ IXCLRDataTypeInstance* type) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = (PTR_HOST_TO_TADDR(m_appDomain) == + PTR_HOST_TO_TADDR(((ClrDataTypeInstance*)type)-> + m_appDomain) && + m_typeHandle == ((ClrDataTypeInstance*)type)-> + m_typeHandle) ? + S_OK : S_FALSE; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::GetNumTypeArguments( + /* [out] */ ULONG32 *numTypeArgs) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::GetTypeArgumentByIndex( + /* [in] */ ULONG32 index, + /* [out] */ IXCLRDataTypeInstance **typeArg) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTypeInstance::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + switch(reqCode) + { + case CLRDATA_REQUEST_REVISION: + if (inBufferSize != 0 || + inBuffer || + outBufferSize != sizeof(ULONG32)) + { + status = E_INVALIDARG; + } + else + { + *(ULONG32*)outBuffer = 2; + status = S_OK; + } + break; + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT +ClrDataTypeInstance::NewFromModule(ClrDataAccess* dac, + AppDomain* appDomain, + Module* module, + mdTypeDef token, + ClrDataTypeInstance** typeInst, + IXCLRDataTypeInstance** pubTypeInst) +{ + TypeHandle typeHandle = module->LookupTypeDef(token); + if (typeHandle.IsNull() || + !typeHandle.IsRestored()) + { + return E_INVALIDARG; + } + + ClrDataTypeInstance* inst = new (nothrow) + ClrDataTypeInstance(dac, appDomain, typeHandle); + if (!inst) + { + return E_OUTOFMEMORY; + } + + PREFIX_ASSUME(typeInst || pubTypeInst); + + if (typeInst) + { + *typeInst = inst; + } + if (pubTypeInst) + { + *pubTypeInst = inst; + } + + return S_OK; +} + + diff --git a/src/debug/daccess/nidump.cpp b/src/debug/daccess/nidump.cpp new file mode 100644 index 0000000000..32eab498d3 --- /dev/null +++ b/src/debug/daccess/nidump.cpp @@ -0,0 +1,9579 @@ +// 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. + + +// + /*vim: set foldmethod=marker: */ +#include <stdafx.h> + +#if defined(FEATURE_PREJIT) +#include "nidump.h" + +#include <metadataexports.h> + +#include <comcallablewrapper.h> +#include <gcdump.h> + +#if !defined(FEATURE_CORESYSTEM) +#include <algorithm> +#endif + +#include <constrainedexecutionregion.h> + +#include <formattype.h> + +#include <pedecoder.h> + +#ifdef FEATURE_REMOTING +#include <crossdomaincalls.h> +#endif + +#include <mdfileformat.h> + +#if !defined(FEATURE_CORESYSTEM) +#include <cassert> +#undef _ASSERTE +#define _ASSERTE(x) assert(x) +#endif + +#include <compile.h> + +#ifdef USE_GC_INFO_DECODER +#include <gcinfodecoder.h> +#endif + +#include <ngenhash.inl> + +#define FEATURE_MSDIS + +//---------------------------------------------------------------------------- +// +// ClrDump functionality +// +//---------------------------------------------------------------------------- + +///////////////////////////////////////////////////////////////////////////////////////////// +// +// Given a compressed integer(*pData), expand the compressed int to *pDataOut. +// Return value is the number of bytes that the integer occupies in the compressed format +// It is caller's responsibility to ensure pDataOut has at least 4 bytes to be written to. +// +// This function returns -1 if pass in with an incorrectly compressed data, such as +// (*pBytes & 0xE0) == 0XE0. +///////////////////////////////////////////////////////////////////////////////////////////// +/* XXX Wed 09/14/2005 + * copied from cor.h. Modified to operate on PTR_PCCOR_SIGNATUREs + */ +inline ULONG DacSigUncompressBigData( + PTR_CCOR_SIGNATURE &pData) // [IN,OUT] compressed data +{ + ULONG res; + + // 1 byte data is handled in DacSigUncompressData + // _ASSERTE(*pData & 0x80); + + // Medium. + if ((*pData & 0xC0) == 0x80) // 10?? ???? + { + res = (ULONG)((*pData++ & 0x3f) << 8); + res |= *pData++; + } + else // 110? ???? + { + res = (*pData++ & 0x1f) << 24; + res |= *pData++ << 16; + res |= *pData++ << 8; + res |= *pData++; + } + return res; +} +FORCEINLINE ULONG DacSigUncompressData( + PTR_CCOR_SIGNATURE &pData) // [IN,OUT] compressed data +{ + // Handle smallest data inline. + if ((*pData & 0x80) == 0x00) // 0??? ???? + return *pData++; + return DacSigUncompressBigData(pData); +} +//const static mdToken g_tkCorEncodeToken[4] ={mdtTypeDef, mdtTypeRef, mdtTypeSpec, mdtBaseType}; + +// uncompress a token +inline mdToken DacSigUncompressToken( // return the token. + PTR_CCOR_SIGNATURE &pData) // [IN,OUT] compressed data +{ + mdToken tk; + mdToken tkType; + + tk = DacSigUncompressData(pData); + tkType = g_tkCorEncodeToken[tk & 0x3]; + tk = TokenFromRid(tk >> 2, tkType); + return tk; +} +// uncompress encoded element type +FORCEINLINE CorElementType DacSigUncompressElementType(//Element type + PTR_CCOR_SIGNATURE &pData) // [IN,OUT] compressed data +{ + return (CorElementType)*pData++; +} + + + +const char * g_helperNames[] = +{ +#define JITHELPER(val, fn, sig) # val, +#include <jithelpers.h> +#undef JITHELPER +}; + + + +#define dim(x) (sizeof(x)/sizeof((x)[0])) + +void EnumFlagsToString( DWORD value, + const NativeImageDumper::EnumMnemonics * table, + int count, const WCHAR * sep, SString& output ) +{ + bool firstValue = true; + for( int i = 0; i < count; ++i ) + { + bool match = false; + const NativeImageDumper::EnumMnemonics& entry = table[i]; + if( entry.mask != 0 ) + match = ((entry.mask & value) == entry.value); + else + match = (entry.value == value); + + if( match ) + { + if( !firstValue ) + output.Append(sep); + firstValue = false; + + output.Append( table[i].mnemonic ); + + value &= ~entry.value; + } + } +} + +const NativeImageDumper::EnumMnemonics s_ImageSections[] = +{ +#define IS_ENTRY(v, s) NativeImageDumper::EnumMnemonics(v, s) + IS_ENTRY(IMAGE_SCN_MEM_READ, W("read")), + IS_ENTRY(IMAGE_SCN_MEM_WRITE, W("write")), + IS_ENTRY(IMAGE_SCN_MEM_EXECUTE, W("execute")), + IS_ENTRY(IMAGE_SCN_CNT_CODE, W("code")), + IS_ENTRY(IMAGE_SCN_CNT_INITIALIZED_DATA, W("init data")), + IS_ENTRY(IMAGE_SCN_CNT_UNINITIALIZED_DATA, W("uninit data")), +#undef IS_ENTRY +}; +inline int CheckFlags( DWORD source, DWORD flags ) +{ + return (source & flags) == flags; +} + +HRESULT ClrDataAccess::DumpNativeImage(CLRDATA_ADDRESS loadedBase, + LPCWSTR name, + IXCLRDataDisplay * display, + IXCLRLibrarySupport * support, + IXCLRDisassemblySupport *dis) +{ + DAC_ENTER(); + /* REVISIT_TODO Fri 09/09/2005 + * catch exceptions + */ + NativeImageDumper dump(dac_cast<PTR_VOID>(CLRDATA_ADDRESS_TO_TADDR(loadedBase)), name, display, + support, dis); + dump.DumpNativeImage(); + DAC_LEAVE(); + return S_OK; +} + + + +static ULONG bigBufferSize = 8192; +static WCHAR bigBuffer[8192]; +static BYTE bigByteBuffer[1024]; + +//---------------------------------------------------------------------------- +// +// NativeImageDumper +// +//---------------------------------------------------------------------------- +template<typename T> +inline T combine(T a, T b) +{ + return (T)(((DWORD)a) | ((DWORD)b)); +} +template<typename T> +inline T combine(T a, T b, T c) +{ + return (T)(((DWORD)a) | ((DWORD)b) | ((DWORD)c)); +} +#define CLRNATIVEIMAGE_ALWAYS ((CLRNativeImageDumpOptions)~0) +#define CHECK_OPT(opt) CheckOptions(CLRNATIVEIMAGE_ ## opt) +#define IF_OPT(opt) if( CHECK_OPT(opt) ) +#define IF_OPT_AND(opt1, opt2) if( CHECK_OPT(opt1) && CHECK_OPT(opt2) ) +#define IF_OPT_OR(opt1, opt2) if( CHECK_OPT(opt1) || CHECK_OPT(opt2) ) +#define IF_OPT_OR3(opt1, opt2, opt3) if( CHECK_OPT(opt1) || CHECK_OPT(opt2) || CHECK_OPT(opt3) ) +#define IF_OPT_OR4(opt1, opt2, opt3, opt4) if( CHECK_OPT(opt1) || CHECK_OPT(opt2) || CHECK_OPT(opt3) || CHECK_OPT(opt4) ) +#define IF_OPT_OR5(opt1, opt2, opt3, opt4, opt5) if( CHECK_OPT(opt1) || CHECK_OPT(opt2) || CHECK_OPT(opt3) || CHECK_OPT(opt4) || CHECK_OPT(opt5) ) + +#define fieldsize(type, field) (sizeof(((type*)NULL)->field)) + + +/*{{{Display helpers*/ +#define DisplayStartCategory(name, filter)\ + do { IF_OPT(filter) m_display->StartCategory(name); } while(0) +#define DisplayEndCategory(filter)\ + do { IF_OPT(filter) m_display->EndCategory(); } while(0) +#define DisplayStartArray(name, fmt, filter) \ + do { IF_OPT(filter) m_display->StartArray(name, fmt); } while(0) +#define DisplayStartArrayWithOffset(field, fmt, type, filter) \ + do { IF_OPT(filter) m_display->StartArrayWithOffset( # field, offsetof(type, field), fieldsize(type, field), fmt); } while(0) + +#define DisplayStartElement( name, filter ) \ + do { IF_OPT(filter) m_display->StartElement( name ); } while(0) +#define DisplayStartStructure( name, ptr, size, filter ) \ + do { IF_OPT(filter) m_display->StartStructure( name, ptr, size ); } while(0) +#define DisplayStartList(fmt, filter) \ + do { IF_OPT(filter) m_display->StartList(fmt); } while(0) + +#define DisplayStartStructureWithOffset( field, ptr, size, type, filter ) \ + do { IF_OPT(filter) m_display->StartStructureWithOffset( # field, offsetof(type, field), fieldsize(type, field), ptr, size ); } while(0) +#define DisplayStartVStructure( name, filter ) \ + do { IF_OPT(filter) m_display->StartVStructure( name ); } while(0) +#define DisplayEndVStructure( filter ) \ + do { IF_OPT(filter) m_display->EndVStructure(); } while(0) + +#define DisplayEndList(filter) \ + do { IF_OPT(filter) m_display->EndList(); } while(0) +#define DisplayEndArray(footer, filter) \ + do { IF_OPT(filter) m_display->EndArray(footer); } while(0) +#define DisplayEndStructure(filter) \ + do { IF_OPT(filter) m_display->EndStructure(); } while(0) + +#define DisplayEndElement(filter) \ + do { IF_OPT(filter) m_display->EndElement(); } while(0) + +#define DisplayWriteElementString(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementString(name, value); } while(0) +#define DisplayWriteElementStringW(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementStringW(name, value); } while(0) + +#define DisplayWriteElementStringW(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementStringW(name, value); } while(0) + +#define DisplayWriteElementInt(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementInt(name, value); } while(0) +#define DisplayWriteElementIntWithSuppress(name, value, defVal, filter) \ + do { IF_OPT(filter) m_display->WriteElementIntWithSuppress(name, value, defVal); } while(0) +#define DisplayWriteElementUInt(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementUInt(name, value); } while(0) +#define DisplayWriteElementFlag(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementFlag(name, value); } while(0) +#define DisplayWriteElementPointer(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementPointer(name, value); } while(0) +#define DisplayWriteElementPointerAnnotated(name, value, annotation, filter) \ + do { IF_OPT(filter) m_display->WriteElementPointerAnnotated(name, value, annotation ); } while(0) +#define DisplayWriteEmptyElement(name, filter) \ + do { IF_OPT(filter) m_display->WriteEmptyElement(name); } while(0) + +#define DisplayWriteElementEnumerated(name, value, mnemonics, sep, filter) \ + do { \ + IF_OPT(filter) { \ + TempBuffer buf; \ + EnumFlagsToString(value, mnemonics, _countof(mnemonics), sep, buf);\ + m_display->WriteElementEnumerated( name, value, (const WCHAR*)buf ); \ + }\ + }while(0) +#define DisplayWriteFieldEnumerated(field, value, type, mnemonics, sep, filter)\ + do { \ + IF_OPT(filter) { \ + TempBuffer buf; \ + EnumFlagsToString(value, mnemonics, _countof(mnemonics), sep, buf);\ + m_display->WriteFieldEnumerated( # field, offsetof(type, field), \ + fieldsize(type, field), value,\ + (const WCHAR*)buf ); \ + }\ + }while(0) +#define DisplayWriteElementAddress(name, ptr, size, filter) \ + do { IF_OPT(filter) m_display->WriteElementAddress( name, ptr, size ); } while(0) +#define DisplayWriteElementAddressNamed(eltName, name, ptr, size, filter) \ + do { IF_OPT(filter) m_display->WriteElementAddressNamed( eltName, name, ptr, size ); } while(0) +#define DisplayWriteElementAddressNamedW(eltName, name, ptr, size, filter) \ + do { IF_OPT(filter) m_display->WriteElementAddressNamedW( eltName, name, ptr, size ); } while(0) + +#define DisplayWriteFieldString(field, value, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldString( # field, offsetof(type, field), fieldsize(type, field), value ); } while(0) +#define DisplayWriteFieldStringW(field, value, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldStringW( # field, offsetof(type, field), fieldsize(type, field), value ); } while(0) +#define DisplayWriteFieldInt(field, value, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldInt( # field, offsetof(type, field), fieldsize(type, field), value ); } while(0) +#define DisplayWriteFieldUInt(field, value, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldUInt( # field, offsetof(type, field), fieldsize(type, field), value ); } while(0) +#define DisplayWriteFieldPointer(field, ptr, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldPointer( # field, offsetof(type, field), fieldsize(type, field), ptr ); } while(0) +#define DisplayWriteFieldPointerWithSize(field, ptr, size, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldPointerWithSize( # field, offsetof(type, field), fieldsize(type, field), ptr, size ); } while(0) +#define DisplayWriteFieldEmpty(field, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldEmpty( # field, offsetof(type, field), fieldsize(type, field) ); } while(0) +#define DisplayWriteFieldFlag(field, value, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldFlag(# field, offsetof(type, field), fieldsize(type, field), value); } while(0) +#define WriteFieldFieldDesc(field, ptr, type, filter) \ + do { IF_OPT(filter) DoWriteFieldFieldDesc( # field, offsetof(type, field), fieldsize(type, field), ptr ); } while(0) +#define WriteFieldMethodDesc(field, ptr, type, filter) \ + do { IF_OPT(filter) DoWriteFieldMethodDesc( # field, offsetof(type, field), fieldsize(type, field), ptr ); } while(0) +#define WriteFieldStr(field, ptr, type, filter) \ + do { IF_OPT(filter) DoWriteFieldStr( ptr, # field, offsetof(type, field), fieldsize(type, field) ); } while(0) +#define WriteFieldMethodTable(field, ptr, type, filter) \ + do { IF_OPT(filter) DoWriteFieldMethodTable( # field, offsetof(type, field), fieldsize(type, field), ptr ); } while(0) +#define WriteFieldMDToken(field, token, type, filter) \ + do { IF_OPT(filter) DoWriteFieldMDToken( # field, offsetof(type, field), fieldsize(type, field), token ); } while(0) +#define WriteFieldAsHex(field, ptr, type, filter) \ + do { IF_OPT(filter) DoWriteFieldAsHex( # field, offsetof(type, field), fieldsize(type, field), ptr, fieldsize(type, field)); } while(0) +#define WriteFieldMDTokenImport(field, token, type, filter, import) \ + do { IF_OPT(filter) DoWriteFieldMDToken( # field, offsetof(type, field), fieldsize(type, field), token, import); } while(0) +#define WriteFieldTypeHandle(field, ptr, type, filter) \ + do { IF_OPT(filter) DoWriteFieldTypeHandle( # field, offsetof(type, field), fieldsize(type, field), ptr ); } while(0) +#define WriteFieldCorElementType(field, et, type, filter) \ + do { IF_OPT(filter) DoWriteFieldCorElementType( # field, offsetof(type, field), fieldsize(type, field), et ); } while(0) +#define DumpFieldStub(field, ptr, type, filter) \ + do { IF_OPT(filter) DoDumpFieldStub( ptr, offsetof(type, field), fieldsize(type, field), # field ); } while(0) +#define DumpComPlusCallInfo(compluscall, filter) \ + do { IF_OPT(filter) DoDumpComPlusCallInfo( compluscall ); } while(0) +#define DisplayWriteFieldPointerAnnotated(field, ptr, annotation, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldPointer( # field, offsetof(type, field), fieldsize(type, field), ptr ); } while(0) +#define DisplayWriteFieldAddress(field, ptr, size, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldAddress( # field, offsetof(type, field), fieldsize(type, field), ptr, size ); } while(0) +#define DisplayStartTextElement(name, filter) \ + do { IF_OPT(filter) m_display->StartTextElement( name ); } while(0) +#define DisplayEndTextElement(filter) \ + do { IF_OPT(filter) m_display->EndTextElement(); } while(0) +#define DisplayWriteXmlText( args, filter ) \ + do { IF_OPT(filter) m_display->WriteXmlText args; } while(0) +#define DisplayWriteXmlTextBlock( args, filter ) \ + do { IF_OPT(filter) m_display->WriteXmlTextBlock args; } while(0) + +#define CoverageRead( ptr, size ) \ + do { IF_OPT(DEBUG_COVERAGE) PTR_READ(TO_TADDR(ptr), size); } while(0) +#define CoverageReadString( taddr ) \ + do { PTR_BYTE ptr(TO_TADDR(taddr)); while( *ptr++ ); }while(0) + +void AppendNilToken( mdToken token, SString& buf ) +{ + _ASSERTE(RidFromToken(token) == mdTokenNil); + + const WCHAR * id = NULL; + switch(token) + { +#define mdNilEnt(x) case x: \ + id = W(#x); \ + break + mdNilEnt(mdModuleNil); + mdNilEnt(mdTypeRefNil); + mdNilEnt(mdTypeDefNil); + mdNilEnt(mdFieldDefNil); + mdNilEnt(mdMethodDefNil); + mdNilEnt(mdParamDefNil); + mdNilEnt(mdInterfaceImplNil); + mdNilEnt(mdMemberRefNil); + mdNilEnt(mdCustomAttributeNil); + mdNilEnt(mdPermissionNil); + mdNilEnt(mdSignatureNil); + mdNilEnt(mdEventNil); + mdNilEnt(mdPropertyNil); + mdNilEnt(mdModuleRefNil); + mdNilEnt(mdTypeSpecNil); + mdNilEnt(mdAssemblyNil); + mdNilEnt(mdAssemblyRefNil); + mdNilEnt(mdFileNil); + mdNilEnt(mdExportedTypeNil); + mdNilEnt(mdManifestResourceNil); + + mdNilEnt(mdGenericParamNil); + mdNilEnt(mdGenericParamConstraintNil); + mdNilEnt(mdMethodSpecNil); + + mdNilEnt(mdStringNil); +#undef mdNilEnt + } + buf.Append( id ); +} +void appendByteArray(SString& buf, const BYTE * bytes, ULONG cbBytes) +{ + for( COUNT_T i = 0; i < cbBytes; ++i ) + { + buf.AppendPrintf(W("%02x"), bytes[i]); + } +} +/*}}}*/ + + + + +struct OptionDependencies +{ + OptionDependencies(CLRNativeImageDumpOptions value, + CLRNativeImageDumpOptions dep) : m_value(value), + m_dep(dep) + { + + } + CLRNativeImageDumpOptions m_value; + CLRNativeImageDumpOptions m_dep; +}; + +static OptionDependencies g_dependencies[] = +{ +#define OPT_DEP(value, dep) OptionDependencies(CLRNATIVEIMAGE_ ## value,\ + CLRNATIVEIMAGE_ ## dep) + OPT_DEP(RESOURCES, COR_INFO), + OPT_DEP(METADATA, COR_INFO), + OPT_DEP(PRECODES, MODULE), + //Does methoddescs require ModuleTables? + OPT_DEP(VERBOSE_TYPES, METHODDESCS), + OPT_DEP(GC_INFO, METHODS), + OPT_DEP(FROZEN_SEGMENT, MODULE), + OPT_DEP(SLIM_MODULE_TBLS, MODULE), + OPT_DEP(MODULE_TABLES, SLIM_MODULE_TBLS), + OPT_DEP(DISASSEMBLE_CODE, METHODS), + + OPT_DEP(FIXUP_HISTOGRAM, FIXUP_TABLES), + OPT_DEP(FIXUP_THUNKS, FIXUP_TABLES), + +#undef OPT_DEP +}; + +// Metadata helpers for DAC +// This is mostly copied from mscoree.cpp which isn't available in mscordacwks.dll. +// + +// This function gets the Dispenser interface given the CLSID and REFIID. +STDAPI MetaDataGetDispenser( + REFCLSID rclsid, // The class to desired. + REFIID riid, // Interface wanted on class factory. + LPVOID FAR * ppv) // Return interface pointer here. +{ + _ASSERTE(rclsid == CLSID_CorMetaDataDispenser); + + return InternalCreateMetaDataDispenser(riid, ppv); +} + + +NativeImageDumper::NativeImageDumper(PTR_VOID loadedBase, + const WCHAR * const name, + IXCLRDataDisplay * display, + IXCLRLibrarySupport * support, + IXCLRDisassemblySupport *dis) + : + m_decoder(loadedBase), + m_name(name), + m_baseAddress(loadedBase), + m_display(display), + m_librarySupport(support), + m_import(NULL), + m_assemblyImport(NULL), + m_manifestAssemblyImport(NULL), + m_dependencies(NULL), + m_imports(NULL), + m_dis(dis), + m_MetadataSize(0), + m_ILHostCopy(NULL), + m_isMscorlibHardBound(false), + m_sectionAlignment(0) +{ + IfFailThrow(m_display->GetDumpOptions(&m_dumpOptions)); + + //set up mscorwks stuff. + m_mscorwksBase = DacGlobalBase(); + _ASSERTE(m_mscorwksBase); + PEDecoder mscorwksDecoder(dac_cast<PTR_VOID>(m_mscorwksBase)); + m_mscorwksSize = mscorwksDecoder.GetSize(); + m_mscorwksPreferred = TO_TADDR(mscorwksDecoder.GetPreferredBase()); + //add implied options (i.e. if you want to dump the module, you also have + //to dump the native info. + CLRNativeImageDumpOptions current; + do + { + current = m_dumpOptions; + for( unsigned i = 0; i < _countof(g_dependencies); ++i ) + { + if( m_dumpOptions & g_dependencies[i].m_value ) + m_dumpOptions |= g_dependencies[i].m_dep; + } + }while( current != m_dumpOptions ); + IF_OPT(DISASSEMBLE_CODE) + { + //configure the disassembler + m_dis->SetTranslateAddrCallback(TranslateAddressCallback); + m_dis->SetTranslateFixupCallback(TranslateFixupCallback); + m_dis->PvClientSet(this); + } +} + +void GuidToString( GUID& guid, SString& s ) +{ + WCHAR guidString[64]; + GuidToLPWSTR(guid, guidString, sizeof(guidString) / sizeof(WCHAR)); + //prune the { and } + _ASSERTE(guidString[0] == W('{') + && guidString[wcslen(guidString) - 1] == W('}')); + guidString[wcslen(guidString) - 1] = W('\0'); + s.Append( guidString + 1 ); +} + +NativeImageDumper::~NativeImageDumper() +{ +} + +inline const void * ptr_add(const void * ptr, COUNT_T size) +{ + return reinterpret_cast<const BYTE *>(ptr) + size; +} + +//This does pointer arithmetic on a DPtr. +template<typename T> +inline const DPTR(T) dptr_add(T* ptr, COUNT_T offset) +{ + return DPTR(T)(PTR_HOST_TO_TADDR(ptr) + (offset * sizeof(T))); +} + +template<typename T> +inline const DPTR(T) dptr_sub(T* ptr, COUNT_T offset) +{ + return DPTR(T)(PTR_HOST_TO_TADDR(ptr) - (offset * sizeof(T))); +} +template<typename T> +inline const DPTR(T) dptr_sub(DPTR(T)* ptr, COUNT_T offset) +{ + return DPTR(T)(PTR_HOST_TO_TADDR(ptr) - (offset * sizeof(T))); +} + +struct MDTableType +{ + MDTableType(unsigned t, const char * n) : m_token(t), m_name(n) { } + unsigned m_token; + const char * m_name; +}; + +static unsigned s_tableTypes[] = +{ + /* +#ifdef MiniMdTable +#undef MiniMdTable +#endif +#define MiniMdTable(x) TBL_##x << 24, + MiniMdTables() +#undef MiniMdTable + mdtName + */ + mdtModule, + mdtTypeRef, + mdtTypeDef, + mdtFieldDef, + mdtMethodDef, + mdtParamDef, + mdtInterfaceImpl, + mdtMemberRef, + mdtCustomAttribute, + mdtPermission, + mdtSignature, + mdtEvent, + mdtProperty, + mdtModuleRef, + mdtTypeSpec, + mdtAssembly, + mdtAssemblyRef, + mdtFile, + mdtExportedType, + mdtManifestResource, + mdtGenericParam, + mdtMethodSpec, + mdtGenericParamConstraint, +}; + +const NativeImageDumper::EnumMnemonics s_CorHdrFlags[] = +{ +#define CHF_ENTRY(f,v) NativeImageDumper::EnumMnemonics(f, v) + CHF_ENTRY(COMIMAGE_FLAGS_ILONLY, W("IL Only")), + CHF_ENTRY(COMIMAGE_FLAGS_32BITREQUIRED, W("32-bit Required")), + CHF_ENTRY(COMIMAGE_FLAGS_IL_LIBRARY, W("IL Library")), + CHF_ENTRY(COMIMAGE_FLAGS_STRONGNAMESIGNED, W("Strong Name Signed")), + CHF_ENTRY(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT, W("Has Native Entrypoint")), + CHF_ENTRY(COMIMAGE_FLAGS_TRACKDEBUGDATA, W("Track Debug Data")), + CHF_ENTRY(COMIMAGE_FLAGS_32BITPREFERRED, W("32-bit Preferred")) +#undef CHF_ENTRY +}; + +void NativeImageDumper::DumpAssemblySignature(CORCOMPILE_ASSEMBLY_SIGNATURE & assemblySignature) +{ + { + TempBuffer buf; + GuidToString(assemblySignature.mvid, buf); + DisplayWriteFieldStringW( mvid, (const WCHAR*)buf, + CORCOMPILE_ASSEMBLY_SIGNATURE, + COR_INFO ); + } + DisplayWriteFieldInt( timeStamp, assemblySignature.timeStamp, + CORCOMPILE_ASSEMBLY_SIGNATURE, COR_INFO ); + DisplayWriteFieldInt( ilImageSize, + assemblySignature.ilImageSize, + CORCOMPILE_ASSEMBLY_SIGNATURE, COR_INFO ); +} + +#ifndef FEATURE_CORECLR + +const NativeImageDumper::EnumMnemonics s_CorCompileDependencyInfoFlags[] = +{ +#define CMDI_ENTRY(f) NativeImageDumper::EnumMnemonics(CORCOMPILE_DEPENDENCY_ ## f, W(#f)) + +#ifdef FEATURE_APTCA + CMDI_ENTRY(IS_APTCA), + CMDI_ENTRY(IS_CAPTCA), +#endif //FEATURE_APTCA +#undef CMDI_ENTRY +}; + +#endif //!FEATURE_CORECLR + +//error code return? +void +NativeImageDumper::DumpNativeImage() +{ + COUNT_T size; + const void *data; + + m_display->StartDocument(); + + DisplayStartCategory( "File", PE_INFO ); + DisplayWriteElementStringW( "path", m_name, PE_INFO ); + + DisplayWriteElementInt( "diskSize", m_decoder.GetSize(), PE_INFO ); + _ASSERTE(sizeof(IMAGE_DOS_HEADER) < m_decoder.GetSize()); + + PTR_IMAGE_DOS_HEADER dosHeader = + PTR_IMAGE_DOS_HEADER(dac_cast<TADDR>(m_baseAddress)); + DisplayWriteElementAddress( "IMAGE_DOS_HEADER", + DPtrToPreferredAddr(dosHeader), + sizeof(*dosHeader), PE_INFO ); + + // NT headers + + if (!m_decoder.HasNTHeaders()) + { + IF_OPT(PE_INFO) + { + DisplayWriteElementString("isPEFile", "false", PE_INFO); + DisplayEndCategory(PE_INFO); + } + else + m_display->ErrorPrintF("Non-PE file"); + + m_display->EndDocument(); + return; + } + + CONSISTENCY_CHECK(m_decoder.CheckNTHeaders()); + if (!m_decoder.CheckNTHeaders()) + { + m_display->ErrorPrintF("*** NT headers are not valid ***"); + return; + } + + DisplayWriteElementString("imageType", m_decoder.Has32BitNTHeaders() + ? "32 bit image" : "64 bit image", PE_INFO); + DisplayWriteElementAddress("address", (SIZE_T)m_decoder.GetNativePreferredBase(), + m_decoder.GetVirtualSize(), PE_INFO); + DisplayWriteElementInt( "TimeDateStamp", m_decoder.GetTimeDateStamp(), + PE_INFO ); + + if( m_decoder.Has32BitNTHeaders() ) + { + PTR_IMAGE_NT_HEADERS32 ntHeaders(m_decoder.GetNTHeaders32()); + //base, size, sectionAlign + _ASSERTE(ntHeaders->OptionalHeader.SectionAlignment >= + ntHeaders->OptionalHeader.FileAlignment); + m_imageSize = ntHeaders->OptionalHeader.SizeOfImage; + m_display->NativeImageDimensions(PTR_TO_TADDR(m_decoder.GetBase()), + ntHeaders->OptionalHeader.SizeOfImage, + ntHeaders->OptionalHeader.SectionAlignment); + /* REVISIT_TODO Mon 11/21/2005 + * I don't understand this. Sections start on a two page boundary, but + * data ends on a one page boundary. What's up with that? + */ + m_sectionAlignment = PAGE_SIZE; //ntHeaders->OptionalHeader.SectionAlignment; + unsigned ntHeaderSize = sizeof(*ntHeaders) + - sizeof(ntHeaders->OptionalHeader) + + ntHeaders->FileHeader.SizeOfOptionalHeader; + DisplayWriteElementAddress( "IMAGE_NT_HEADERS32", + DPtrToPreferredAddr(ntHeaders), + ntHeaderSize, PE_INFO ); + + } + else + { + PTR_IMAGE_NT_HEADERS64 ntHeaders(m_decoder.GetNTHeaders64()); + //base, size, sectionAlign + _ASSERTE(ntHeaders->OptionalHeader.SectionAlignment >= + ntHeaders->OptionalHeader.FileAlignment); + m_imageSize = ntHeaders->OptionalHeader.SizeOfImage; + m_display->NativeImageDimensions((SIZE_T)ntHeaders->OptionalHeader.ImageBase, + ntHeaders->OptionalHeader.SizeOfImage, + ntHeaders->OptionalHeader.SectionAlignment); + m_sectionAlignment = ntHeaders->OptionalHeader.SectionAlignment; + unsigned ntHeaderSize = sizeof(*ntHeaders) + - sizeof(ntHeaders->OptionalHeader) + + ntHeaders->FileHeader.SizeOfOptionalHeader; + DisplayWriteElementAddress( "IMAGE_NT_HEADERS64", + DPtrToPreferredAddr(ntHeaders), + ntHeaderSize, PE_INFO ); + } + DisplayEndCategory(PE_INFO); + + // PE Section info + + DisplayStartArray("Sections", W("%-8s%s\t(disk %s) %s"), PE_INFO); + + for (COUNT_T i = 0; i < m_decoder.GetNumberOfSections(); i++) + { + PTR_IMAGE_SECTION_HEADER section = dptr_add(m_decoder.FindFirstSection(), i); + m_display->Section(reinterpret_cast<char *>(section->Name), + section->VirtualAddress, + section->SizeOfRawData); + DisplayStartStructure( "Section", DPtrToPreferredAddr(section), + sizeof(*section), PE_INFO ); + DisplayWriteElementString("name", (const char *)section->Name, PE_INFO); + DisplayWriteElementAddress( "address", RvaToDisplay(section->VirtualAddress), + section->Misc.VirtualSize, PE_INFO ); + DisplayWriteElementAddress( "disk", section->PointerToRawData, + section->SizeOfRawData, PE_INFO ); + + DisplayWriteElementEnumerated( "access", section->Characteristics, + s_ImageSections, W(", "), PE_INFO ); + DisplayEndStructure( PE_INFO ); //Section + } + DisplayEndArray("Total Sections", PE_INFO); + + // Image directory info + + DisplayStartArray( "Directories", W("%-40s%s"), PE_INFO ); + + for ( COUNT_T i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++) + { + static const char *directoryNames[] = + { + /* 0*/"IMAGE_DIRECTORY_ENTRY_EXPORT", + /* 1*/"IMAGE_DIRECTORY_ENTRY_IMPORT", + /* 2*/"IMAGE_DIRECTORY_ENTRY_RESOURCE", + /* 3*/"IMAGE_DIRECTORY_ENTRY_EXCEPTION", + /* 4*/"IMAGE_DIRECTORY_ENTRY_SECURITY", + /* 5*/"IMAGE_DIRECTORY_ENTRY_BASERELOC", + /* 6*/"IMAGE_DIRECTORY_ENTRY_DEBUG", + /* 7*/"IMAGE_DIRECTORY_ENTRY_ARCHITECTURE", + /* 8*/"IMAGE_DIRECTORY_ENTRY_GLOBALPTR", + /* 9*/"IMAGE_DIRECTORY_ENTRY_TLS", + /*10*/"IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG", + /*11*/"IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT", + /*12*/"IMAGE_DIRECTORY_ENTRY_IAT", + /*13*/"IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT", + /*14*/"IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR", + /* 2*/"", + }; + + IMAGE_DATA_DIRECTORY *entry = m_decoder.GetDirectoryEntry(i); + + if (entry->VirtualAddress != 0) + { + DisplayStartElement("Directory", PE_INFO); + DisplayWriteElementString("name", directoryNames[i], PE_INFO); + DisplayWriteElementAddress("address", + RvaToDisplay(entry->VirtualAddress), + entry->Size, PE_INFO); + + DisplayEndElement( PE_INFO ); //Directory + } + } + DisplayEndArray("Total Directories", PE_INFO); //Directories + + // COM+ info + + if (!m_decoder.HasCorHeader()) + { + IF_OPT(COR_INFO) + DisplayWriteElementString("CLRInfo", "<none>", COR_INFO); + else + m_display->ErrorPrintF("Non-CLR image\n"); + + m_display->EndDocument(); + return; + } + + CONSISTENCY_CHECK(m_decoder.CheckCorHeader()); + if (!m_decoder.CheckCorHeader()) + { + m_display->ErrorPrintF("*** INVALID CLR Header ***"); + m_display->EndDocument(); + return; + } + + DisplayStartCategory("CLRInfo", COR_INFO); + PTR_IMAGE_COR20_HEADER pCor(m_decoder.GetCorHeader()); + { +#define WRITE_COR20_FIELD( name ) m_display->WriteFieldAddress( \ + # name, offsetof(IMAGE_COR20_HEADER, name), \ + fieldsize(IMAGE_COR20_HEADER, name), \ + RvaToDisplay( pCor-> name . VirtualAddress ), \ + pCor-> name . Size ) + + m_display->StartStructure( "IMAGE_COR20_HEADER", + DPtrToPreferredAddr(pCor), + sizeof(*pCor) ); + + DisplayWriteFieldUInt( MajorRuntimeVersion, pCor->MajorRuntimeVersion, IMAGE_COR20_HEADER, COR_INFO ); + DisplayWriteFieldUInt( MinorRuntimeVersion, pCor->MinorRuntimeVersion, IMAGE_COR20_HEADER, COR_INFO ); + + // Symbol table and startup information + WRITE_COR20_FIELD(MetaData); + DisplayWriteFieldEnumerated( Flags, pCor->Flags, IMAGE_COR20_HEADER, s_CorHdrFlags, W(", "), COR_INFO ); + DisplayWriteFieldUInt( EntryPointToken, pCor->EntryPointToken, IMAGE_COR20_HEADER, COR_INFO ); + + // Binding information + WRITE_COR20_FIELD(Resources); + WRITE_COR20_FIELD(StrongNameSignature); + + // Regular fixup and binding information + WRITE_COR20_FIELD(CodeManagerTable); + WRITE_COR20_FIELD(VTableFixups); + WRITE_COR20_FIELD(ExportAddressTableJumps); + + // Precompiled image info + WRITE_COR20_FIELD(ManagedNativeHeader); + + m_display->EndStructure(); //IMAGE_COR20_HEADER +#undef WRITE_COR20_FIELD + } + + //make sure to touch the strong name signature even if we won't print it. + if (m_decoder.HasStrongNameSignature()) + { + if (m_decoder.IsStrongNameSigned()) + { + DACCOP_IGNORE(CastBetweenAddressSpaces,"nidump is in-proc and doesn't maintain a clean separation of address spaces (target and host are the same."); + data = reinterpret_cast<void*>(dac_cast<TADDR>(m_decoder.GetStrongNameSignature(&size))); + + IF_OPT(COR_INFO) + { + TempBuffer sig; + + appendByteArray(sig, (BYTE*)data, size); + + DisplayWriteElementStringW( "StrongName", (const WCHAR *)sig, + COR_INFO ); + } + } + else + { + DisplayWriteEmptyElement("DelaySigned", COR_INFO); + } + } + +#ifdef FEATURE_READYTORUN + if (m_decoder.HasReadyToRunHeader()) + DisplayWriteElementString( "imageType", "ReadyToRun image", COR_INFO); + else +#endif + if (m_decoder.IsILOnly()) + DisplayWriteElementString( "imageType", "IL only image", COR_INFO); + else + if (m_decoder.HasNativeHeader()) + DisplayWriteElementString( "imageType", "Native image", COR_INFO); + else + DisplayWriteElementString( "imageType", "Mixed image", COR_INFO); + + DACCOP_IGNORE(CastBetweenAddressSpaces,"nidump is in-proc and doesn't maintain a clean separation of address spaces (target and host are the same."); + data = reinterpret_cast<void*>(dac_cast<TADDR>(m_decoder.GetMetadata(&size))); + OpenMetadata(); + IF_OPT(METADATA) + { + DWORD dwAssemblyFlags = 0; + IfFailThrow(m_manifestAssemblyImport->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, &dwAssemblyFlags)); + if ((afContentType_WindowsRuntime & dwAssemblyFlags) == afContentType_WindowsRuntime) + { + // The WinMD adapter doesn't implement the IID_IMetaDataTables interface so we can't dump + // the raw metadata. + DisplayWriteElementString ("Metadata", "Not supported by WinRT", COR_INFO); + } + else + { + WriteElementsMetadata( "Metadata", TO_TADDR(data), size ); + } + } + + CoverageRead(TO_TADDR(data), size); + + if (m_decoder.HasNativeHeader()) + { + DACCOP_IGNORE(CastBetweenAddressSpaces,"nidump is in-proc and doesn't maintain a clean separation of address spaces (target and host are the same."); + data = reinterpret_cast<void*>(dac_cast<TADDR>(m_decoder.GetNativeManifestMetadata(&size))); + + IF_OPT(METADATA) + { + WriteElementsMetadata( "NativeManifestMetadata", TO_TADDR(data), size ); + } + else + { + DisplayWriteElementAddress( "NativeManifestMetadata", + DataPtrToDisplay((TADDR)data), size, + COR_INFO ); + } + + + /* REVISIT_TODO Tue 09/20/2005 + * Anything to display in the native metadata? + */ + CoverageRead(TO_TADDR(data), size); + + /* REVISIT_TODO Tue 09/20/2005 + * Dump the debug map? Indexed by method RID... probably a good idea + */ + data = reinterpret_cast<void*>(m_decoder.GetNativeDebugMap(&size)); + + DisplayWriteElementAddress( "debugMap", DataPtrToDisplay((TADDR)data), size, + COR_INFO); + CoverageRead(TO_TADDR(data), size); + + //also read the entire debug map section + IMAGE_SECTION_HEADER * dbgmap = FindSection( ".dbgmap" ); + if (dbgmap != NULL) + { + CoverageRead(TO_TADDR(dbgmap->VirtualAddress) + + PTR_TO_TADDR(m_decoder.GetBase()), + (ULONG32)ALIGN_UP(dbgmap->Misc.VirtualSize, GetSectionAlignment())); + } + + //read the .il and .rsrc sections in their entirety + IF_OPT(DEBUG_COVERAGE) + { + IMAGE_SECTION_HEADER *hdr; + hdr = FindSection( ".rsrc" ); + if( hdr != NULL ) + { + CoverageRead( m_decoder.GetRvaData(hdr->VirtualAddress), + (ULONG32)hdr->Misc.VirtualSize ); + } + } + IF_OPT_OR(DEBUG_COVERAGE, IL) + { + IMAGE_SECTION_HEADER *hdr = FindSection( ".text" ); + + if( hdr != NULL ) + { + m_ILSectionStart = hdr->VirtualAddress; + m_ILHostCopy = (BYTE*)PTR_READ(m_decoder.GetRvaData(hdr->VirtualAddress), hdr->Misc.VirtualSize); +#ifdef _DEBUG + m_ILSectionSize = hdr->Misc.VirtualSize; +#endif + } + else + { + m_ILSectionStart = 0; + m_ILHostCopy = NULL; +#ifdef _DEBUG + m_ILSectionSize = 0; +#endif + } + _ASSERTE( (((TADDR)m_ILHostCopy) & 3) == 0 ); + _ASSERTE((m_ILSectionStart & 3) == 0); + + } + } + + data = m_decoder.GetResources(&size); + IF_OPT(RESOURCES) + { + DisplayStartStructure( "resource", DataPtrToDisplay((TADDR)data), size, + COR_INFO ); + DisplayStartArray( "Resources", NULL, COR_INFO ); + HCORENUM hEnum = NULL; + for(;;) + { + mdManifestResource resTokens[1]; + ULONG numTokens = 0; + IfFailThrow(m_assemblyImport->EnumManifestResources(&hEnum, + resTokens, + 1, + &numTokens)); + if( numTokens == 0 ) + break; + + WCHAR resourceName[256]; + ULONG nameLen; + mdToken impl; + DWORD offset, flags; + IfFailThrow(m_assemblyImport->GetManifestResourceProps(resTokens[0], + resourceName, + _countof(resourceName), + &nameLen, + &impl, + &offset, + &flags)); + if( RidFromToken(impl) != 0 ) + continue; //skip all non-zero providers + resourceName[nameLen] = W('\0'); + DPTR(DWORD UNALIGNED) res(TO_TADDR(data) + offset); + DWORD resSize = *res; + DisplayWriteElementAddressNamedW( "Resource", resourceName, + DPtrToPreferredAddr(res), + resSize + sizeof(DWORD), + RESOURCES ); + } + DisplayEndArray( "Total Resources", COR_INFO ); //Resources + DisplayEndStructure( COR_INFO ); //resource + } + else + { + DisplayWriteElementAddress( "resource", DataPtrToDisplay((TADDR)data), size, + COR_INFO ); + } + + ULONG resultSize; + GUID mvid; + m_manifestImport->GetScopeProps(bigBuffer, bigBufferSize, &resultSize, &mvid); + /* REVISIT_TODO Wed 09/07/2005 + * The name is the .module entry. Why isn't it present in the ngen image? + */ + TempBuffer guidString; + GuidToString( mvid, guidString ); + if( wcslen(bigBuffer) ) + DisplayWriteElementStringW( "scopeName", bigBuffer, COR_INFO ); + DisplayWriteElementStringW( "mvid", (const WCHAR *)guidString, COR_INFO ); + + if (m_decoder.HasManagedEntryPoint()) + { + DisplayStartVStructure( "ManagedEntryPoint", COR_INFO ); + unsigned token = m_decoder.GetEntryPointToken(); + DisplayWriteElementUInt( "Token", token, COR_INFO ); + TempBuffer buf; + AppendTokenName( token, buf ); + DisplayWriteElementStringW( "TokenName", (const WCHAR *)buf, COR_INFO ); + DisplayEndVStructure( COR_INFO ); + } + else if (m_decoder.HasNativeEntryPoint()) + { + DisplayWriteElementPointer( "NativeEntryPoint", (SIZE_T)m_decoder.GetNativeEntryPoint(), + COR_INFO ); + } + + /* REVISIT_TODO Mon 11/21/2005 + * Dump the version info completely + */ + if( m_decoder.HasNativeHeader() ) + { + PTR_CORCOMPILE_VERSION_INFO versionInfo( m_decoder.GetNativeVersionInfo() ); + + DisplayStartStructure("CORCOMPILE_VERSION_INFO", + DPtrToPreferredAddr(versionInfo), + sizeof(*versionInfo), COR_INFO); + + DisplayStartStructureWithOffset( sourceAssembly, + DPtrToPreferredAddr(versionInfo) + offsetof(CORCOMPILE_VERSION_INFO, sourceAssembly), + sizeof(versionInfo->sourceAssembly), + CORCOMPILE_VERSION_INFO, COR_INFO ); + DumpAssemblySignature(versionInfo->sourceAssembly); + DisplayEndStructure(COR_INFO); //sourceAssembly + + COUNT_T numDeps; + PTR_CORCOMPILE_DEPENDENCY deps(TO_TADDR(m_decoder.GetNativeDependencies(&numDeps))); + + DisplayStartArray( "Dependencies", NULL, COR_INFO ); + + for( COUNT_T i = 0; i < numDeps; ++i ) + { + DisplayStartStructure("CORCOMPILE_DEPENDENCY", DPtrToPreferredAddr(deps + i), + sizeof(deps[i]), COR_INFO ); + WriteFieldMDTokenImport( dwAssemblyRef, deps[i].dwAssemblyRef, + CORCOMPILE_DEPENDENCY, COR_INFO, + m_manifestImport ); + WriteFieldMDTokenImport( dwAssemblyDef, deps[i].dwAssemblyDef, + CORCOMPILE_DEPENDENCY, COR_INFO, + m_manifestImport ); +#ifndef FEATURE_CORECLR + DisplayWriteFieldEnumerated( dependencyInfo, deps[i].dependencyInfo, + CORCOMPILE_DEPENDENCY, + s_CorCompileDependencyInfoFlags, W(", "), + COR_INFO ); +#endif // !FEATURE_CORECLR + DisplayStartStructureWithOffset( signAssemblyDef, + DPtrToPreferredAddr(deps + i) + offsetof(CORCOMPILE_DEPENDENCY, signAssemblyDef), + sizeof(deps[i]).signAssemblyDef, + CORCOMPILE_DEPENDENCY, COR_INFO ); + DumpAssemblySignature(deps[i].signAssemblyDef); + DisplayEndStructure(COR_INFO); //signAssemblyDef + + { + TempBuffer buf; + if( deps[i].signNativeImage == INVALID_NGEN_SIGNATURE ) + { + buf.Append( W("INVALID_NGEN_SIGNATURE") ); + } + else + { + GuidToString(deps[i].signNativeImage, buf); + } + DisplayWriteFieldStringW( signNativeImage, (const WCHAR*)buf, + CORCOMPILE_DEPENDENCY, COR_INFO ); +#if 0 + if( m_librarySupport + && deps[i].signNativeImage != INVALID_NGEN_SIGNATURE ) + { + buf.Clear(); + AppendTokenName(deps[i].dwAssemblyRef, buf, m_import ); + IfFailThrow(m_librarySupport->LoadDependency( (const WCHAR*)buf, + deps[i].signNativeImage )); + } +#endif + + } + + + DisplayEndStructure(COR_INFO); //CORCOMPILE_DEPENDENCY + } + DisplayEndArray( "Total Dependencies", COR_INFO ); + DisplayEndStructure(COR_INFO); //CORCOMPILE_VERSION_INFO + + //Now load all dependencies and imports. There may be more + //dependencies than imports, so make sure to get them all. + for( COUNT_T i = 0; i < m_decoder.GetNativeImportTableCount(); ++i ) + { + NativeImageDumper::Import * import = OpenImport(i); + TraceDumpImport( i, import ); + } + NativeImageDumper::Dependency * traceDependency = OpenDependency(0); + TraceDumpDependency( 0, traceDependency ); + + for( COUNT_T i = 0; i < numDeps; ++i ) + { + traceDependency = OpenDependency( i + 1 ); + TraceDumpDependency( i + 1, traceDependency ); + } + _ASSERTE(m_dependencies[0].pModule != NULL); + + /* XXX Wed 12/14/2005 + * Now for the real insanity. I need to initialize static classes in + * the DAC. First I need to find mscorlib's dependency entry. Search + * through all of the dependencies to find the one marked as + * fIsMscorlib. If I don't find anything marked that way, then "self" + * is mscorlib. + */ + Dependency * mscorlib = NULL; + for( COUNT_T i = 0; i < m_numDependencies; ++i ) + { + if( m_dependencies[i].fIsMscorlib ) + { + mscorlib = &m_dependencies[i]; + break; + } + } + + //If we're actually dumping mscorlib, remap the mscorlib dependency to our own native image. + if( (mscorlib == NULL) || !wcscmp(m_name, CoreLibName_W)) + { + mscorlib = GetDependency(0); + mscorlib->fIsMscorlib = TRUE; + _ASSERTE(mscorlib->fIsHardbound); + } + if( mscorlib->fIsHardbound ) + { + m_isMscorlibHardBound = true; + } + + _ASSERTE(mscorlib != NULL); + if( m_isMscorlibHardBound ) + { + //go through the module to the binder. + PTR_Module mscorlibModule = mscorlib->pModule; + + PTR_MscorlibBinder binder = mscorlibModule->m_pBinder; + g_Mscorlib = *binder; + + PTR_MethodTable mt = MscorlibBinder::GetExistingClass(CLASS__OBJECT); + g_pObjectClass = mt; + } + + + if (g_pObjectClass == NULL) + { + //if mscorlib is not hard bound, then warn the user (many features of nidump are shut off) + m_display->ErrorPrintF( "Assembly %S is soft bound to mscorlib. nidump cannot dump MethodTables completely.\n", m_name ); + // TritonTODO: reason? + // reset "hard bound state" + m_isMscorlibHardBound = false; + + } + } + + + // @todo: VTable Fixups + + // @todo: EAT Jumps + + DisplayEndCategory(COR_INFO); //CLRInfo + +#ifdef FEATURE_READYTORUN + if (m_decoder.HasReadyToRunHeader()) + { + DumpReadyToRun(); + } + else +#endif + if (m_decoder.HasNativeHeader()) + { + DumpNative(); + } + + m_display->EndDocument(); +} + +void NativeImageDumper::DumpNative() +{ + DisplayStartCategory("NativeInfo", NATIVE_INFO); + + CONSISTENCY_CHECK(m_decoder.CheckNativeHeader()); + if (!m_decoder.CheckNativeHeader()) + { + m_display->ErrorPrintF("*** INVALID NATIVE HEADER ***\n"); + return; + } + + IF_OPT(NATIVE_INFO) + DumpNativeHeader(); + + //host pointer + CORCOMPILE_EE_INFO_TABLE * infoTable = m_decoder.GetNativeEEInfoTable(); + + DisplayStartStructure( "CORCOMPILE_EE_INFO_TABLE", + DataPtrToDisplay(PTR_HOST_TO_TADDR(infoTable)), + sizeof(*infoTable), NATIVE_INFO ); + + /* REVISIT_TODO Mon 09/26/2005 + * Move this further down to include the dumping of the module, and + * other things. + */ + DisplayEndStructure(NATIVE_INFO); //NativeInfoTable + DisplayEndCategory(NATIVE_INFO); //NativeInfo +#if LATER + //come back here and dump all the fields of the CORCOMPILE_EE_INFO_TABLE +#endif + + IF_OPT(RELOCATIONS) + DumpBaseRelocs(); + + IF_OPT(NATIVE_TABLES) + DumpHelperTable(); + + PTR_Module module = (TADDR)m_decoder.GetPersistedModuleImage(); + + //this needs to run for precodes to load the tables that identify precode ranges + IF_OPT_OR5(MODULE, METHODTABLES, EECLASSES, TYPEDESCS, PRECODES) + DumpModule(module); + + IF_OPT_OR3(FIXUP_TABLES, FIXUP_HISTOGRAM, FIXUP_THUNKS) + DumpFixupTables( module ); + IF_OPT_OR3(METHODS, GC_INFO, DISASSEMBLE_CODE ) + DumpMethods( module ); + IF_OPT_OR3(METHODTABLES, EECLASSES, TYPEDESCS) + DumpTypes( module ); +} + +void NativeImageDumper::TraceDumpImport(int idx, NativeImageDumper::Import * import) +{ + IF_OPT(DEBUG_TRACE) + { + m_display->ErrorPrintF("Import: %d\n", idx); + m_display->ErrorPrintF("\tDependency: %p\n", import->dependency); + m_display->ErrorPrintF("\twAssemblyRid: %d\n", import->entry->wAssemblyRid); + m_display->ErrorPrintF("\twModuleRid %d\n", import->entry->wModuleRid); + } +} +void NativeImageDumper::TraceDumpDependency(int idx, NativeImageDumper::Dependency * dependency) +{ + IF_OPT(DEBUG_TRACE) + { + m_display->ErrorPrintF("Dependency: %d (%p)\n", idx, dependency); + m_display->ErrorPrintF("\tPreferred: %p\n", dependency->pPreferredBase); + m_display->ErrorPrintF("\tLoaded: %p\n", dependency->pLoadedAddress); + m_display->ErrorPrintF("\tSize: %x (%d)\n", dependency->size, dependency->size); + m_display->ErrorPrintF("\tModule: P=%p, L=%p\n", DataPtrToDisplay(dac_cast<TADDR>(dependency->pModule)), + PTR_TO_TADDR(dependency->pModule)); + m_display->ErrorPrintF("Mscorlib=%s, Hardbound=%s\n", + (dependency->fIsMscorlib ? "true" : "false"), + (dependency->fIsHardbound ? "true" : "false")); + m_display->ErrorPrintF("Name: %S\n", dependency->name); + } +} + +void NativeImageDumper::WriteElementsMetadata( const char * elementName, + TADDR data, SIZE_T size ) +{ + DisplayStartStructure( elementName, + DataPtrToDisplay(data), size, ALWAYS ); + + /* XXX Mon 03/13/2006 + * Create new metatadata dispenser. When I define the Emit for defining + * assemblyrefs for dependencies, I copy the memory and totally hork any + * mapping back to base addresses. + */ + ReleaseHolder<IMetaDataTables> tables; + ReleaseHolder<IMetaDataDispenserEx> pDispenser; + IfFailThrow(MetaDataGetDispenser(CLSID_CorMetaDataDispenser, + IID_IMetaDataDispenserEx, (void **) &pDispenser)); + + VARIANT opt; + + TADDR hostCopyStart = TO_TADDR(PTR_READ(data, (ULONG32)size)); + TADDR rebasedPointer; + + IfFailThrow(pDispenser->GetOption(MetaDataCheckDuplicatesFor, &opt)); + V_UI4(&opt) |= MDDupAssemblyRef | MDDupFile; + IfFailThrow(pDispenser->SetOption(MetaDataCheckDuplicatesFor, &opt)); + + IfFailThrow(pDispenser->OpenScopeOnMemory((const void *)hostCopyStart, (DWORD)size, + ofRead, IID_IMetaDataTables, + (IUnknown **) &tables)); + DisplayStartArray( "Tables", W("%s"), ALWAYS ); + + for( unsigned i = 0; i < _countof(s_tableTypes); ++i ) + { + HRESULT hr = S_OK; + ULONG idx = 0; + hr = tables->GetTableIndex(s_tableTypes[i], &idx); + _ASSERTE(SUCCEEDED(hr)); + ULONG cbRow = 0, cRows = 0, cCols = 0, iKey = 0; + const char * name = NULL; + BYTE * ptr = NULL; + hr = tables->GetTableInfo(idx, &cbRow, &cRows, &cCols, + &iKey, &name); + _ASSERTE(SUCCEEDED(hr) || hr == E_INVALIDARG); + if( hr == E_INVALIDARG || cRows == 0 ) + { + continue; //no such table. + } + + hr = tables->GetRow(idx, 1, (void**)&ptr); + IfFailThrow(hr); + _ASSERTE(SUCCEEDED(hr)); + //compute address + rebasedPointer = data + (TO_TADDR(ptr) - hostCopyStart); + _ASSERTE( rebasedPointer >= data && rebasedPointer < (data + size) ); + DisplayWriteElementAddressNamed( "table", name, + DataPtrToDisplay(rebasedPointer), + cbRow * cRows , ALWAYS ); +#if 0 + DisplayStartElement( "table", ALWAYS ); + DisplayWriteElementString( "name", name, ALWAYS ); + //compute address + rebasedPointer = data + (TO_TADDR(ptr) - hostCopyStart); + _ASSERTE( rebasedPointer >= data && rebasedPointer < (data + size) ); + DisplayWriteElementAddress( "address", DataPtrToDisplay(rebasedPointer), + cbRow * cRows, ALWAYS ); + DisplayEndElement( ALWAYS ); //Table +#endif + } + DisplayEndArray( "Total Tables", ALWAYS ); + + PTR_STORAGESIGNATURE root(data); + _ASSERTE(root->lSignature == STORAGE_MAGIC_SIG); + //the root is followed by the version string who's length is + //root->iVersionString. After that is a storage header that counts the + //number of streams. + PTR_STORAGEHEADER sHdr(data + sizeof(*root) + root->iVersionString); + DisplayStartArray( "Pools", NULL, ALWAYS ); + + //now check the pools + + //start of stream headers + PTR_STORAGESTREAM streamHeader( PTR_TO_TADDR(sHdr) + sizeof(*sHdr) ); + for( unsigned i = 0; i < sHdr->iStreams; ++i ) + { + if( streamHeader->iSize > 0 ) + { + DisplayWriteElementAddressNamed( "heap", streamHeader->rcName, + DataPtrToDisplay( data + streamHeader->iOffset ), + streamHeader->iSize, ALWAYS ); + } + //Stream headers aren't fixed size. the size is aligned up based on a + //variable length string at the end. + streamHeader = PTR_STORAGESTREAM(PTR_TO_TADDR(streamHeader) + + ALIGN_UP(offsetof(STORAGESTREAM, rcName) + strlen(streamHeader->rcName) + 1, 4)); + } + + DisplayEndArray( "Total Pools", ALWAYS ); //Pools + DisplayEndStructure( ALWAYS ); //nativeMetadata +} +void NativeImageDumper::OpenMetadata() +{ + COUNT_T size; + + DACCOP_IGNORE(CastBetweenAddressSpaces,"nidump is in-proc and doesn't maintain a clean separation of address spaces (target and host are the same."); + const void *data = reinterpret_cast<void*>(dac_cast<TADDR>(m_decoder.GetMetadata(&size))); + + ReleaseHolder<IMetaDataDispenserEx> pDispenser; + IfFailThrow(MetaDataGetDispenser(CLSID_CorMetaDataDispenser, + IID_IMetaDataDispenserEx, (void **) &pDispenser)); + + VARIANT opt; + IfFailThrow(pDispenser->GetOption(MetaDataCheckDuplicatesFor, &opt)); + V_UI4(&opt) |= MDDupAssemblyRef | MDDupFile; + IfFailThrow(pDispenser->SetOption(MetaDataCheckDuplicatesFor, &opt)); + + data = PTR_READ(TO_TADDR(data), size); + IfFailThrow(pDispenser->OpenScopeOnMemory(data, size, ofRead, + IID_IMetaDataImport2, (IUnknown **) &m_import)); + + IfFailThrow(m_import->QueryInterface(IID_IMetaDataAssemblyImport, + (void **)&m_assemblyImport)); + + m_MetadataStartTarget = TO_TADDR(data); + m_MetadataSize = size; + data = PTR_READ(TO_TADDR(data), size); + m_MetadataStartHost = TO_TADDR(data); + + if (m_decoder.HasNativeHeader()) + { + DACCOP_IGNORE(CastBetweenAddressSpaces,"nidump is in-proc and doesn't maintain a clean separation of address spaces (target and host are the same."); + data = reinterpret_cast<void*>(dac_cast<TADDR>(m_decoder.GetNativeManifestMetadata(&size))); + + IfFailThrow(pDispenser->OpenScopeOnMemory(data, size, ofRead, + IID_IMetaDataImport2, (IUnknown **) &m_manifestImport)); + + IfFailThrow(m_manifestImport->QueryInterface(IID_IMetaDataAssemblyImport, + (void **)&m_manifestAssemblyImport)); + } + else + { + m_manifestImport = m_import; + m_manifestImport->AddRef(); + + m_manifestAssemblyImport = m_assemblyImport; + m_manifestAssemblyImport->AddRef(); + } +} +void +NativeImageDumper::AppendTokenName(mdToken token, SString& buf) +{ + AppendTokenName(token, buf, NULL); +} +void +NativeImageDumper::AppendTokenName(mdToken token, SString& buf, + IMetaDataImport2 *pImport, + bool force) +{ + mdToken parent; + ULONG size; + DWORD attr; + PCCOR_SIGNATURE pSig; + PTR_CCOR_SIGNATURE dacSig; + ULONG cSig; + DWORD flags; + ULONG rva; + CQuickBytes bytes; + + if( CHECK_OPT(DISABLE_NAMES) && !force ) + { + buf.Append( W("Disabled") ); + return; + } + + if (pImport == NULL) + pImport = m_import; + if( RidFromToken(token) == mdTokenNil ) + { + AppendNilToken( token, buf ); + } + else + { + switch (TypeFromToken(token)) + { + case mdtTypeDef: + IfFailThrow(pImport->GetTypeDefProps(token, bigBuffer, bigBufferSize, &size, &flags, &parent)); + buf.Append(bigBuffer); + break; + + case mdtTypeRef: + // TritonTODO: consolidate with desktop + // IfFailThrow(pImport->GetTypeRefProps(token, &parent, bigBuffer, bigBufferSize, &size)); + if (FAILED(pImport->GetTypeRefProps(token, &parent, bigBuffer, bigBufferSize, &size))) + buf.Append(W("ADDED TYPEREF (?)")); + else + buf.Append(bigBuffer); + break; + + case mdtTypeSpec: + IfFailThrow(pImport->GetTypeSpecFromToken(token, &pSig, &cSig)); + dacSig = metadataToHostDAC(pSig, pImport); + TypeToString(dacSig, buf, pImport); + break; + + case mdtFieldDef: + IfFailThrow(pImport->GetFieldProps(token, &parent, bigBuffer, bigBufferSize, &size, &attr, + &pSig, &cSig, &flags, NULL, NULL)); + AppendTokenName(parent, buf, pImport); + IfFailThrow(pImport->GetFieldProps(token, &parent, bigBuffer, bigBufferSize, &size, &attr, + &pSig, &cSig, &flags, NULL, NULL)); + buf.AppendPrintf( W("::%s"), bigBuffer ); + break; + + case mdtMethodDef: + IfFailThrow(pImport->GetMethodProps(token, &parent, bigBuffer, bigBufferSize, &size, &attr, + &pSig, &cSig, &rva, &flags)); + AppendTokenName(parent, buf, pImport); + IfFailThrow(pImport->GetMethodProps(token, &parent, bigBuffer, bigBufferSize, &size, &attr, + &pSig, &cSig, &rva, &flags)); + buf.AppendPrintf( W("::%s"), bigBuffer ); + break; + + case mdtMemberRef: + IfFailThrow(pImport->GetMemberRefProps(token, &parent, bigBuffer, bigBufferSize, &size, + &pSig, &cSig)); + AppendTokenName(parent, buf, pImport); + IfFailThrow(pImport->GetMemberRefProps(token, &parent, bigBuffer, bigBufferSize, &size, + &pSig, &cSig)); + buf.AppendPrintf( W("::%s"), bigBuffer ); + break; + + case mdtSignature: + IfFailThrow(pImport->GetSigFromToken(token, &pSig, &cSig)); +#if LATER + PrettyPrintSig(pSig, cSig, W(""), &bytes, pImport); + m_display->ErrorPrintF("%S", bytes.Ptr()); +#else + _ASSERTE(!"Unimplemented"); + m_display->ErrorPrintF( "unimplemented" ); +#endif + break; + + case mdtString: + IfFailThrow(pImport->GetUserString(token, bigBuffer, bigBufferSize, &size)); + bigBuffer[min(size, bigBufferSize-1)] = 0; + buf.Append( bigBuffer ); + break; + + case mdtAssembly: + case mdtAssemblyRef: + case mdtFile: + case mdtExportedType: + { + ReleaseHolder<IMetaDataAssemblyImport> pAssemblyImport; + IfFailThrow(pImport->QueryInterface(IID_IMetaDataAssemblyImport, + (void **)&pAssemblyImport)); + PrintManifestTokenName(token, buf, pAssemblyImport, force); + } + break; + + case mdtGenericParam: + { + ULONG nameLen; + IfFailThrow(pImport->GetGenericParamProps(token, NULL, NULL, NULL, NULL, bigBuffer, + _countof(bigBuffer), &nameLen)); + bigBuffer[min(nameLen, _countof(bigBuffer) - 1)] = 0; + buf.Append( bigBuffer ); + } + break; + + default: + _ASSERTE( !"Unknown token type in AppendToken" ); + buf.AppendPrintf( W("token 0x%x"), token ); + } + } +} +void NativeImageDumper::PrintManifestTokenName(mdToken token, SString& str) +{ + PrintManifestTokenName(token, str, NULL); +} +void +NativeImageDumper::PrintManifestTokenName(mdToken token, + SString& buf, + IMetaDataAssemblyImport *pAssemblyImport, + bool force) +{ + ULONG size; + const void *pSig; + ULONG cSig; + DWORD flags; + CQuickBytes bytes; + ULONG hash; + + if( CHECK_OPT(DISABLE_NAMES) && !force ) + { + buf.Append( W("Disabled") ); + return; + } + + if (pAssemblyImport == NULL) + pAssemblyImport = m_manifestAssemblyImport; + + if( RidFromToken(token) == mdTokenNil ) + { + AppendNilToken( token, buf ); + } + else + { + switch (TypeFromToken(token)) + { + case mdtAssembly: + IfFailThrow(pAssemblyImport->GetAssemblyProps(token, &pSig, &cSig, + &hash, bigBuffer, + bigBufferSize, &size, + NULL, &flags)); + + buf.Append(bigBuffer); + break; + + case mdtAssemblyRef: + IfFailThrow(pAssemblyImport->GetAssemblyRefProps(token, &pSig, + &cSig, bigBuffer, + bigBufferSize, + &size, NULL, NULL, + NULL, &flags)); + buf.Append(bigBuffer); + break; + + case mdtFile: + IfFailThrow(pAssemblyImport->GetFileProps(token, bigBuffer, + bigBufferSize, &size, + NULL, NULL, &flags)); + + buf.Append(bigBuffer); + break; + + case mdtExportedType: + IfFailThrow(pAssemblyImport->GetExportedTypeProps(token, bigBuffer, + bigBufferSize, &size, + NULL, NULL, &flags)); + + buf.Append(bigBuffer); + break; + + default: + buf.AppendPrintf(W("token %x"), token); + } + } +} + +BOOL NativeImageDumper::HandleFixupForHistogram(PTR_CORCOMPILE_IMPORT_SECTION pSection, + SIZE_T fixupIndex, + SIZE_T *fixupCell) +{ + COUNT_T nImportSections; + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_decoder.GetNativeImportSections(&nImportSections); + + COUNT_T tableSize; + TADDR tableBase = m_decoder.GetDirectoryData(&pSection->Section, &tableSize); + + COUNT_T table = (COUNT_T)(pSection - pImportSections); + _ASSERTE(table < nImportSections); + + SIZE_T offset = dac_cast<TADDR>(fixupCell) - tableBase; + _ASSERTE( offset < tableSize ); + + COUNT_T entry = (COUNT_T)(offset / sizeof(TADDR)); + m_fixupHistogram[table][entry]++; + + return TRUE; +} + +void NativeImageDumper::ComputeMethodFixupHistogram( PTR_Module module ) +{ + COUNT_T nImportSections; + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_decoder.GetNativeImportSections(&nImportSections); + + m_fixupHistogram = new COUNT_T * [nImportSections]; + + for (COUNT_T i=0; i < nImportSections; i++) + { + PTR_CORCOMPILE_IMPORT_SECTION pSection = m_decoder.GetNativeImportSectionFromIndex(i); + + COUNT_T count = pSection->Section.Size / sizeof(TADDR); + + m_fixupHistogram[i] = new COUNT_T [count]; + ZeroMemory(m_fixupHistogram[i], count * sizeof(COUNT_T)); + } + + ZeroMemory(&m_fixupCountHistogram, sizeof(m_fixupCountHistogram)); + // profiled hot code + + MethodIterator mi(module, &m_decoder, MethodIterator::Hot); + while (mi.Next()) + { + m_fixupCount = 0; + + TADDR pFixupList = mi.GetMethodDesc()->GetFixupList(); + + if (pFixupList != NULL) + { + COUNT_T nImportSections; + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_decoder.GetNativeImportSections(&nImportSections); + + module->FixupDelayListAux(pFixupList, this, + &NativeImageDumper::HandleFixupForHistogram, + pImportSections, nImportSections, + &m_decoder); + } + + if (m_fixupCount < COUNT_HISTOGRAM_SIZE) + m_fixupCountHistogram[m_fixupCount]++; + else + m_fixupCountHistogram[COUNT_HISTOGRAM_SIZE-1]++; + } + + // unprofiled code + MethodIterator miUnprofiled(module, &m_decoder, MethodIterator::Unprofiled); + + while(miUnprofiled.Next()) + { + m_fixupCount = 0; + + TADDR pFixupList = miUnprofiled.GetMethodDesc()->GetFixupList(); + + if (pFixupList != NULL) + { + COUNT_T nImportSections; + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_decoder.GetNativeImportSections(&nImportSections); + + module->FixupDelayListAux(pFixupList, this, + &NativeImageDumper::HandleFixupForHistogram, + pImportSections, nImportSections, + &m_decoder); + } + + if (m_fixupCount < COUNT_HISTOGRAM_SIZE) + m_fixupCountHistogram[m_fixupCount]++; + else + m_fixupCountHistogram[COUNT_HISTOGRAM_SIZE-1]++; + } +} + +void NativeImageDumper::DumpFixupTables( PTR_Module module ) +{ + IF_OPT(FIXUP_HISTOGRAM) + ComputeMethodFixupHistogram( module ); + + DisplayStartCategory( "Imports", FIXUP_TABLES ); + + COUNT_T nImportSections; + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_decoder.GetNativeImportSections(&nImportSections); + + for (COUNT_T iImportSections = 0; iImportSections < nImportSections; iImportSections++) + { + PTR_CORCOMPILE_IMPORT_SECTION pImportSection = pImportSections + iImportSections; + + COUNT_T size; + TADDR pTable(m_decoder.GetDirectoryData(&pImportSection->Section, &size)); + TADDR pTableEnd = pTable + size; + + TADDR pDataTable(NULL); + + if (pImportSection->Signatures != 0) + pDataTable = m_decoder.GetRvaData(pImportSection->Signatures); + + switch (pImportSection->Type) + { + case CORCOMPILE_IMPORT_TYPE_VIRTUAL_METHOD: + { + COUNT_T entrySize = pImportSection->EntrySize; + COUNT_T count = size / entrySize; + _ASSERTE(entrySize == sizeof(CORCOMPILE_VIRTUAL_IMPORT_THUNK)); + + for (TADDR pEntry = pTable; pEntry < pTableEnd; pEntry += entrySize) + { + PTR_CORCOMPILE_VIRTUAL_IMPORT_THUNK pThunk = pEntry; + + DisplayStartStructure("VirtualImportThunk", DPtrToPreferredAddr(pThunk), + entrySize, FIXUP_THUNKS ); + + DisplayWriteElementInt( "Slot", pThunk->slotNum, FIXUP_THUNKS); + + DisplayEndStructure( FIXUP_THUNKS ); + } + } + break; + + case CORCOMPILE_IMPORT_TYPE_EXTERNAL_METHOD: + { + COUNT_T entrySize = pImportSection->EntrySize; + COUNT_T count = size / entrySize; + _ASSERTE(entrySize == sizeof(CORCOMPILE_EXTERNAL_METHOD_THUNK)); + + for (TADDR pEntry = pTable; pEntry < pTableEnd; pEntry += entrySize) + { + PTR_CORCOMPILE_EXTERNAL_METHOD_THUNK pThunk = pEntry; + + DisplayStartStructure("ExternalImportThunk", DPtrToPreferredAddr(pThunk), + entrySize, FIXUP_THUNKS ); + + TADDR pDataAddr = pDataTable + ((pEntry - pTable) / entrySize) * sizeof(DWORD); + PTR_DWORD pData = pDataAddr; + + DisplayWriteElementPointer( "DataAddress ", pDataAddr, FIXUP_THUNKS ); + + TADDR blobSigAddr = RvaToDisplay(*pData); + DisplayWriteElementPointer( "TargetSigAddress", blobSigAddr, FIXUP_THUNKS ); + TempBuffer buf; + FixupBlobToString(*pData, buf); + DisplayWriteElementStringW( "TargetName", (const WCHAR*)buf, FIXUP_THUNKS ); + + DisplayEndStructure( FIXUP_THUNKS ); + } + } + break; + + default: + { + COUNT_T count = size / sizeof(TADDR); + + for (COUNT_T j = 0; j < count; j++) + { + if (dac_cast<PTR_TADDR>(pTable)[j] == 0) + continue; + + SIZE_T nNextEntry = j + 1; + while (nNextEntry < count && dac_cast<PTR_TADDR>(pTable)[nNextEntry] == 0) + nNextEntry++; + + DisplayStartStructure("ImportEntry", DPtrToPreferredAddr(dac_cast<PTR_TADDR>(pTable) + j), + (nNextEntry - j) * sizeof(TADDR), FIXUP_TABLES ); + + if (pDataTable != NULL) + { + DWORD rva = dac_cast<PTR_DWORD>(pDataTable)[j]; + WriteElementsFixupTargetAndName(rva); + } + else + { + SIZE_T token = dac_cast<PTR_TADDR>(pTable)[j]; + DisplayWriteElementPointer( "TaggedValue", token, FIXUP_TABLES ); + WriteElementsFixupBlob(pImportSection, token); + } + + DisplayWriteElementInt( "index", j, FIXUP_HISTOGRAM); + DisplayWriteElementInt( "ReferenceCount", m_fixupHistogram[iImportSections][j], FIXUP_HISTOGRAM ); + + DisplayEndStructure( FIXUP_TABLES ); + } + } + } + } + DisplayEndCategory( FIXUP_TABLES ); +} + +void NativeImageDumper::FixupThunkToString(PTR_CORCOMPILE_IMPORT_SECTION pImportSection, TADDR addr, SString& buf) +{ + switch (pImportSection->Type) + { + case CORCOMPILE_IMPORT_TYPE_VIRTUAL_METHOD: + { + PTR_CORCOMPILE_VIRTUAL_IMPORT_THUNK pThunk = addr; + buf.AppendPrintf( W("slot %d"), pThunk->slotNum ); + } + break; + + case CORCOMPILE_IMPORT_TYPE_EXTERNAL_METHOD: + case CORCOMPILE_IMPORT_TYPE_STUB_DISPATCH: + { + TADDR pTable(m_decoder.GetDirectoryData(&pImportSection->Section)); + COUNT_T index = (COUNT_T)(addr - pTable) / pImportSection->EntrySize; + TADDR pDataTable(m_decoder.GetRvaData(pImportSection->Signatures)); + TADDR pDataAddr = pDataTable + (index * sizeof(DWORD)); + PTR_DWORD pData = pDataAddr; + FixupBlobToString(*pData, buf); + } + break; + + default: + _ASSERTE(!"Unknown import type"); + } +} + +void NativeImageDumper::WriteElementsFixupBlob(PTR_CORCOMPILE_IMPORT_SECTION pSection, SIZE_T fixup) +{ + if (pSection != NULL && !CORCOMPILE_IS_FIXUP_TAGGED(fixup, pSection)) + { + TempBuffer buf; + if (pSection->Type == CORCOMPILE_IMPORT_TYPE_TYPE_HANDLE) + { + TypeHandleToString(TypeHandle::FromTAddr((TADDR)fixup), buf); + } + else + if (pSection->Type == CORCOMPILE_IMPORT_TYPE_METHOD_HANDLE) + { + MethodDescToString(PTR_MethodDesc((TADDR)fixup), buf); + } + else + { + _ASSERTE(!"Unknown Type"); + IfFailThrow(E_FAIL); + } + m_display->WriteElementStringW( "FixupTargetName", (const WCHAR*)buf ); + return; + } + + RVA rva = CORCOMPILE_UNTAG_TOKEN(fixup); + + WriteElementsFixupTargetAndName(rva); +} + +const NativeImageDumper::EnumMnemonics s_EncodeMethodSigFlags[] = +{ +#define EMS_ENTRY(f) NativeImageDumper::EnumMnemonics(ENCODE_METHOD_SIG_ ## f, W(#f)) + EMS_ENTRY(UnboxingStub), + EMS_ENTRY(InstantiatingStub), + EMS_ENTRY(MethodInstantiation), + EMS_ENTRY(SlotInsteadOfToken), + EMS_ENTRY(MemberRefToken), + EMS_ENTRY(Constrained), + EMS_ENTRY(OwnerType), +#undef EMS_ENTRY +}; + +void NativeImageDumper::FixupBlobToString(RVA rva, SString& buf) +{ + PTR_CCOR_SIGNATURE sig = (TADDR) m_decoder.GetRvaData(rva); + BYTE kind = *sig++; + + CorTokenType tkType = (CorTokenType)0; + + IMetaDataImport2 * pImport = m_import; + + if (kind & ENCODE_MODULE_OVERRIDE) + { + Import *import = OpenImport(DacSigUncompressData(sig)); + kind &= ~ENCODE_MODULE_OVERRIDE; + + Dependency *pDep = import->dependency; + if (pDep == NULL) + { + return; + } + + pImport = pDep->pImport; + + _ASSERTE(pImport != NULL); + + // print assembly/module info + + PTR_CORCOMPILE_IMPORT_TABLE_ENTRY entry = import->entry; + if (entry->wAssemblyRid != 0) + { + mdToken realRef = + MapAssemblyRefToManifest(TokenFromRid(entry->wAssemblyRid, + mdtAssemblyRef), + m_assemblyImport); + AppendToken(realRef, buf, m_manifestImport); + buf.Append( W(" ") ); + } + if (entry->wModuleRid != 0) + { + AppendToken(TokenFromRid(entry->wModuleRid, mdtFile), buf, pImport); + buf.Append( W(" ") ); + } + } + + // print further info + + mdToken token; + + switch (kind) + { + case ENCODE_MODULE_HANDLE: + // No further info + break; + + case ENCODE_TYPE_HANDLE: + EncodeType: + if (pImport != NULL) + TypeToString(sig, buf, pImport); + else + buf.Append( W("<unresolved type> ") ); + + break; + + case ENCODE_METHOD_HANDLE: + EncodeMethod: + { + //Flags are first + DWORD methodFlags = DacSigUncompressData(sig); + + // If the type portion for this generic method signature + // is from a different module then both the generic type and the + // generic method tokens are interpreted in the context of that module, + // and not the current import. This is returned by TypeToString. + // + IMetaDataImport2 * pMethodImport = pImport; + if (pImport != NULL) + { + if (methodFlags & ENCODE_METHOD_SIG_OwnerType) + { + pMethodImport = TypeToString(sig, buf, pImport); + } + } + else + { + buf.Append( W("<unresolved method signature>") ); + break; + } + + //If we have SlotInsteadOfToken set then this is a slot number (i.e. for an array) + if( methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken ) + { + buf.AppendPrintf( W(" method slot %d"), DacSigUncompressData(sig) ); + } + else + { + // decode the methodToken (a rid is encoded) + RID rid = DacSigUncompressData(sig); + + mdMethodDef methodToken = ((methodFlags & ENCODE_METHOD_SIG_MemberRefToken) ? mdtMemberRef : mdtMethodDef) | rid; + + buf.Append( W(" ") ); + + // Get the full signature of method from external module + // Need temporary buffer because method name will be inserted + // in between the signature + + TempBuffer tempName; + + AppendTokenName( methodToken, tempName, pMethodImport ); + + if( methodFlags & ENCODE_METHOD_SIG_MethodInstantiation ) + { + //for each generic arg, there is a type handle. + ULONG numParams = DacSigUncompressData(sig); + + tempName.Append( W("<") ); + for( unsigned i = 0;i < numParams; ++i ) + { + if( i != 0 ) + tempName.Append( W(", ") ); + + // switch back to using pImport to resolve tokens + TypeToString(sig, tempName, pImport); + } + tempName.Append( W(">") ); + } + + PCCOR_SIGNATURE pvSigBlob; + ULONG cbSigBlob; + + if (methodFlags & ENCODE_METHOD_SIG_MemberRefToken) + { + IfFailThrow(pMethodImport->GetMemberRefProps(methodToken, + NULL, + NULL, + 0, + NULL, + &pvSigBlob, + &cbSigBlob)); + } + else + { + IfFailThrow(pMethodImport->GetMethodProps(methodToken, + NULL, + NULL, + 0, + NULL, + NULL, + &pvSigBlob, + &cbSigBlob, + NULL, + NULL)); + } + + CQuickBytes prettySig; + ReleaseHolder<IMDInternalImport> pInternal; + IfFailThrow(GetMDInternalInterfaceFromPublic(pMethodImport, IID_IMDInternalImport, + (void**)&pInternal)); + StackScratchBuffer buffer; + const ANSI * ansi = tempName.GetANSI(buffer); + ansi = PrettyPrintSig(pvSigBlob, cbSigBlob, ansi, &prettySig, pInternal, NULL); + tempName.SetANSI( ansi ); + buf.Append(tempName); + } + + buf.Append( W(" flags=(") ); + EnumFlagsToString( methodFlags, s_EncodeMethodSigFlags, _countof(s_EncodeMethodSigFlags), + W(", "), buf ); + buf.Append( W(")") ); + } + break; + + case ENCODE_FIELD_HANDLE: + EncodeField: + { + //Flags are first + DWORD fieldFlags = DacSigUncompressData(sig); + + IMetaDataImport2 * pFieldImport = pImport; + if (pImport != NULL) + { + if (fieldFlags & ENCODE_FIELD_SIG_OwnerType) + { + pFieldImport = TypeToString(sig, buf, pImport); + } + } + else + buf.Append( W("<unresolved type>") ); + + if (fieldFlags & ENCODE_FIELD_SIG_IndexInsteadOfToken) + { + buf.AppendPrintf( W(" field index %d"), DacSigUncompressData(sig) ); + } + else + { + // decode the methodToken (a rid is encoded) + RID rid = DacSigUncompressData(sig); + + mdMethodDef fieldToken = ((fieldFlags & ENCODE_FIELD_SIG_MemberRefToken) ? mdtMemberRef : mdtFieldDef) | rid; + + buf.Append( W(" ") ); + + AppendTokenName( fieldToken, buf, pFieldImport ); + } + } + break; + + case ENCODE_STRING_HANDLE: + token = TokenFromRid(DacSigUncompressData(sig), mdtString); + if (pImport != NULL) + AppendToken(token, buf, pImport); + else + buf.AppendPrintf( W("<unresolved token %d>"), token ); + break; + + case ENCODE_VARARGS_SIG: + tkType = mdtFieldDef; + goto DataToTokenCore; + case ENCODE_VARARGS_METHODREF: + tkType = mdtMemberRef; + goto DataToTokenCore; + case ENCODE_VARARGS_METHODDEF: + tkType = mdtMemberRef; + goto DataToTokenCore; +DataToTokenCore: + token = TokenFromRid(DacSigUncompressData(sig), tkType); + if (pImport != NULL) + AppendToken(token, buf, pImport); + else + buf.AppendPrintf( "<unresolved token %d>", token ); + break; + + case ENCODE_METHOD_ENTRY: + buf.Append( W("Entrypoint for ") ); + goto EncodeMethod; + + case ENCODE_METHOD_ENTRY_DEF_TOKEN: + { + buf.Append( W("Entrypoint for ") ); + token = TokenFromRid(DacSigUncompressData(sig), mdtMethodDef); + AppendTokenName(token, buf, pImport); + } + break; + + case ENCODE_METHOD_ENTRY_REF_TOKEN: + { + buf.Append( W("Entrypoint for ref ") ); + token = TokenFromRid(DacSigUncompressData(sig), mdtMemberRef); + AppendTokenName(token, buf, pImport); + } + break; + + case ENCODE_VIRTUAL_ENTRY: + buf.Append( W("Entrypoint for ") ); + goto EncodeMethod; + + case ENCODE_VIRTUAL_ENTRY_DEF_TOKEN: + { + buf.Append( W("Virtual call for ") ); + token = TokenFromRid(DacSigUncompressData(sig), mdtMethodDef); + AppendTokenName(token, buf, pImport); + } + break; + + case ENCODE_VIRTUAL_ENTRY_REF_TOKEN: + { + buf.Append( W("Virtual call for ref ") ); + token = TokenFromRid(DacSigUncompressData(sig), mdtMemberRef); + AppendTokenName(token, buf, pImport); + } + break; + + case ENCODE_VIRTUAL_ENTRY_SLOT: + { + buf.Append( W("Virtual call for ") ); + int slot = DacSigUncompressData(sig); + buf.AppendPrintf( W("slot %d "), slot ); + goto EncodeType; + } + + case ENCODE_MODULE_ID_FOR_STATICS: + buf.Append( W("Module For Statics") ); + // No further info + break; + + case ENCODE_MODULE_ID_FOR_GENERIC_STATICS: + buf.Append( W("Module For Statics for ") ); + goto EncodeType; + + case ENCODE_CLASS_ID_FOR_STATICS: + buf.Append( W("Statics ID for ") ); + goto EncodeType; + + case ENCODE_STATIC_FIELD_ADDRESS: + buf.Append( W("Static field address for ") ); + goto EncodeField; + + case ENCODE_SYNC_LOCK: + buf.Append( W("Synchronization handle for ") ); + break; + + case ENCODE_INDIRECT_PINVOKE_TARGET: + buf.Append( W("Indirect P/Invoke target for ") ); + break; + + case ENCODE_PROFILING_HANDLE: + buf.Append( W("Profiling handle for ") ); + goto EncodeMethod; + + case ENCODE_ACTIVE_DEPENDENCY: + { + buf.Append( W("Active dependency for ") ); + + int targetModuleIndex = DacSigUncompressData(sig); + Import *targetImport = OpenImport(targetModuleIndex); + + CORCOMPILE_IMPORT_TABLE_ENTRY *entry = targetImport->entry; + if (entry->wAssemblyRid != 0) + { + mdToken realRef = + MapAssemblyRefToManifest(TokenFromRid(entry->wAssemblyRid, + mdtAssemblyRef), + m_assemblyImport); + AppendToken(realRef, buf, m_manifestImport); + buf.Append( W(" ") ); + } + if (entry->wModuleRid != 0) + { + AppendToken(TokenFromRid(entry->wModuleRid, mdtFile), buf, + targetImport->dependency->pImport); + } + } + break; + + default: + buf.Append( W("Unknown fixup kind") ); + _ASSERTE(!"Unknown fixup kind"); + } +} + +void NativeImageDumper::WriteElementsFixupTargetAndName(RVA rva) +{ + if( rva == NULL ) + { + /* XXX Tue 04/11/2006 + * This should only happen for static fields. If the field is + * unaligned, we need an extra cell for an indirection. + */ + m_display->WriteElementPointer( "FixupTargetValue", NULL ); + m_display->WriteElementStringW( "FixupTargetName", W("NULL") ); + return; + } + + m_display->WriteElementPointer( "FixupTargetValue", RvaToDisplay(rva) ); + + TempBuffer buf; + FixupBlobToString(rva, buf); + + m_display->WriteElementStringW( "FixupTargetName", (const WCHAR*)buf ); +} + +NativeImageDumper::Dependency * NativeImageDumper::GetDependency(mdAssemblyRef token, IMetaDataAssemblyImport *pImport) +{ + if (RidFromToken(token) == 0) + return OpenDependency(0); + + if (pImport == NULL) + pImport = m_assemblyImport; + + // Need to map from IL token to manifest token + mdAssemblyRef manifestToken = MapAssemblyRefToManifest(token, pImport); + + if( manifestToken == mdAssemblyNil ) + { + //this is "self" + return OpenDependency(0); + } + + COUNT_T count; + PTR_CORCOMPILE_DEPENDENCY deps(TO_TADDR(m_decoder.GetNativeDependencies(&count))); + + for (COUNT_T i = 0; i < count; i++) + { + if (deps[i].dwAssemblyRef == manifestToken) + return OpenDependency(i+1); + } + + TempBuffer buf; + AppendTokenName(manifestToken, buf, m_manifestImport); + m_display->ErrorPrintF("Error: unlisted assembly dependency %S\n", (const WCHAR*)buf); + + return NULL; +} + +mdAssemblyRef NativeImageDumper::MapAssemblyRefToManifest(mdAssemblyRef token, IMetaDataAssemblyImport *pAssemblyImport) +{ + // Reference may be to self + if (TypeFromToken(token) == mdtAssembly) + return token; + + // Additional tokens not originally present overflow to manifest automatically during emit + /* REVISIT_TODO Tue 01/31/2006 + * Factor this code out so that it is shared with the module index code in the CLR that looks + * exactly thes same + */ + //count the assembly refs. + ULONG count = 0; + + HCORENUM iter = NULL; + for (;;) + { + ULONG tokens = 0; + mdAssemblyRef tmp; + IfFailThrow(pAssemblyImport->EnumAssemblyRefs(&iter, &tmp, 1, + &tokens)); + if (tokens == 0) + break; + count ++; + } + pAssemblyImport->CloseEnum(iter); + + if( RidFromToken(token) > count ) + { + //out of range import. This means that it has spilled over. Subtract + //off the max number of assembly refs and return it as a manifest + //token. + return token - (count + 1); + } + + ULONG cchName; + ASSEMBLYMETADATA metadata; + + ZeroMemory(&metadata, sizeof(metadata)); + + IfFailThrow(pAssemblyImport->GetAssemblyRefProps(token, NULL, NULL, + NULL, 0, &cchName, + &metadata, NULL, NULL, + NULL)); + + LPWSTR szAssemblyName = NULL; + + if (cchName > 0) + szAssemblyName = (LPWSTR) _alloca(cchName * sizeof(WCHAR)); + + if (metadata.cbLocale > 0) + metadata.szLocale = (LPWSTR) _alloca(metadata.cbLocale * sizeof(WCHAR)); + if (metadata.ulProcessor > 0) + metadata.rProcessor = (DWORD*) _alloca(metadata.ulProcessor * sizeof(DWORD)); + if (metadata.ulOS > 0) + metadata.rOS = (OSINFO*) _alloca(metadata.ulOS * sizeof(OSINFO)); + + const void *pbPublicKey; + ULONG cbPublicKey; + DWORD flags; + const void *pbHashValue; + ULONG cbHashValue; + + + IfFailThrow(pAssemblyImport->GetAssemblyRefProps(token, &pbPublicKey, &cbPublicKey, + szAssemblyName, cchName, NULL, + &metadata, &pbHashValue, &cbHashValue, + &flags)); + + //Notice that we're searching for the provided metadata for the dependency info and then looking in the + //image we're dumping for the dependency. + // + //Also, sometimes we find "self" in these searches. If so, return mdAssemblyDefNil as a canary value. + + if( !wcscmp(szAssemblyName, m_name) ) + { + //we need "self". + return mdAssemblyNil; + } + + mdAssemblyRef ret = mdAssemblyRefNil; + /*HCORENUM*/ iter = NULL; + for(;;) + { + //Walk through all the assemblyRefs and search for a match. I would use DefineAssemblyRef here, but + //if I do it will create an assemblyRef is one is not found. Then I fail in a bad place. This + //way I can fail in a less bad place. + mdAssemblyRef currentRef; + //ULONG count; + IfFailThrow(m_manifestAssemblyImport->EnumAssemblyRefs(&iter, ¤tRef, 1, &count)); + if( 0 == count ) + break; + + //get the information about the assembly ref and compare. + const void * publicKeyToken; + ULONG pktSize = 0; + WCHAR name[128]; + /*ULONG*/ cchName = _countof(name); + ASSEMBLYMETADATA curMD = {0}; + + IfFailThrow(m_manifestAssemblyImport->GetAssemblyRefProps(currentRef, &publicKeyToken, &pktSize, name, + cchName, &cchName, &curMD, + NULL /*ppbHashValue*/, NULL/*pcbHashValue*/, + NULL/*pdwAssemblyRefFlags*/)); + if( !wcscmp(name, szAssemblyName) ) + { + if( cbPublicKey == pktSize && !memcmp(pbPublicKey, publicKeyToken, pktSize) + && curMD.usMajorVersion == metadata.usMajorVersion + && curMD.usMinorVersion == metadata.usMinorVersion) + { + ret = currentRef; + break; + } + else if (wcscmp(szAssemblyName, CoreLibName_W) == 0) + { + // Mscorlib is special - version number and public key token are ignored. + ret = currentRef; + break; + } + else if (metadata.usMajorVersion == 255 && + metadata.usMinorVersion == 255 && + metadata.usBuildNumber == 255 && + metadata.usRevisionNumber == 255) + { + // WinMDs encode all assemblyrefs with version 255.255.255.255 including CLR assembly dependencies (mscorlib, System). + ret = currentRef; + } + else + { + //there was an assembly with the correct name, but with the wrong version number. Let the + //user know. + m_display->ErrorPrintF("MapAssemblyRefToManifest: found %S with version %d.%d in manifest. Wanted version %d.%d.\n", szAssemblyName, curMD.usMajorVersion, curMD.usMinorVersion, metadata.usMajorVersion, metadata.usMinorVersion); + // TritonTODO: why? + ret = currentRef; + break; + } + + } + } + pAssemblyImport->CloseEnum(iter); + if( ret == mdAssemblyRefNil ) + { + TempBuffer pkt; + appendByteArray(pkt, (const BYTE*)pbPublicKey, cbPublicKey); + m_display->ErrorPrintF("MapAssemblyRefToManifest could not find token for %S, Version=%d.%d, PublicKeyToken=%S\n", szAssemblyName, metadata.usMajorVersion, metadata.usMinorVersion, (const WCHAR *)pkt); + _ASSERTE(!"MapAssemblyRefToManifest failed to find a match"); + } + + return ret; +} + +NativeImageDumper::Import * NativeImageDumper::OpenImport(int i) +{ + if (m_imports == NULL) + { + COUNT_T count = m_decoder.GetNativeImportTableCount(); + m_numImports = count; + m_imports = new Import [count]; + ZeroMemory(m_imports, count * sizeof(m_imports[0])); + } + + if (m_imports[i].entry == NULL) + { + //GetNativeImportFromIndex returns a host pointer. + CORCOMPILE_IMPORT_TABLE_ENTRY * entry = m_decoder.GetNativeImportFromIndex(i); + m_imports[i].entry = (PTR_CORCOMPILE_IMPORT_TABLE_ENTRY)(TADDR)entry; + + /* + mdToken tok = TokenFromRid(entry->wAssemblyRid, mdtAssemblyRef); + Dependency * dependency = GetDependency( MapAssemblyRefToManifest(tok, + */ + Dependency *dependency = GetDependency(TokenFromRid(entry->wAssemblyRid, mdtAssemblyRef)); + m_imports[i].dependency = dependency; + _ASSERTE(dependency); //Why can this be null? + + } + + return &m_imports[i]; +} + + +const NativeImageDumper::Dependency *NativeImageDumper::GetDependencyForFixup(RVA rva) +{ + PTR_CCOR_SIGNATURE sig = (TADDR) m_decoder.GetRvaData(rva); + if (*sig++ & ENCODE_MODULE_OVERRIDE) + { + unsigned idx = DacSigUncompressData(sig); + + _ASSERTE(idx >= 0 && idx < (int)m_decoder.GetNativeImportTableCount()); + return OpenImport(idx)->dependency; + } + + return &m_dependencies[0]; +} + + +void NativeImageDumper::AppendToken(mdToken token, SString& buf) +{ + return NativeImageDumper::AppendToken(token, buf, NULL); +} +void NativeImageDumper::AppendToken(mdToken token, SString& buf, + IMetaDataImport2 *pImport) +{ + IF_OPT(DISABLE_NAMES) + { + buf.Append( W("Disabled") ); + return; + } + switch (TypeFromToken(token)) + { + case mdtTypeDef: + buf.Append( W("TypeDef ") ); + break; + + case mdtTypeRef: + buf.Append( W("TypeRef ") ); + break; + + case mdtTypeSpec: + buf.Append( W("TypeRef ") ); + break; + + case mdtFieldDef: + buf.Append( W("FieldDef ")); + break; + + case mdtMethodDef: + buf.Append( W("MethodDef ") ); + break; + + case mdtMemberRef: + buf.Append( W("MemberRef ") ); + break; + + case mdtAssemblyRef: + buf.Append( W("AssemblyRef ") ); + break; + + case mdtFile: + buf.Append( W("File ") ); + break; + + case mdtString: + buf.Append( W("String ") ); + break; + + case mdtSignature: + buf.Append( W("Signature ") ); + break; + + } + if( RidFromToken(token) == mdTokenNil ) + buf.Append( W("Nil") ); + else + AppendTokenName(token, buf, pImport); +} + +NativeImageDumper::Dependency *NativeImageDumper::OpenDependency(int index) +{ + CORCOMPILE_VERSION_INFO *info = m_decoder.GetNativeVersionInfo(); + + if (m_dependencies == NULL) + { + COUNT_T count; + m_decoder.GetNativeDependencies(&count); + + // Add one for self + count++; + + m_numDependencies = count; + m_dependencies = new Dependency [count]; + ZeroMemory(m_dependencies, count * sizeof (Dependency)); + } + + if (m_dependencies[index].entry == NULL) + { + CORCOMPILE_DEPENDENCY *entry; + + if (index == 0) + { + // Make dummy entry for self + entry = &m_self; + m_self.dwAssemblyRef = TokenFromRid(1, mdtAssembly); + m_self.dwAssemblyDef = TokenFromRid(1, mdtAssembly); + m_self.signAssemblyDef = info->sourceAssembly; + m_manifestImport->GetScopeProps(NULL, NULL, 0, &m_self.signNativeImage); + m_dependencies[index].pLoadedAddress = dac_cast<TADDR>(m_baseAddress); + m_dependencies[index].pPreferredBase = + TO_TADDR(m_decoder.GetNativePreferredBase()); + m_dependencies[index].size = m_imageSize; + m_dependencies[index].pImport = m_import; + m_dependencies[index].pMetadataStartTarget = + m_MetadataStartTarget; + m_dependencies[index].pMetadataStartHost = + m_MetadataStartHost; + m_dependencies[index].MetadataSize = m_MetadataSize; + m_dependencies[index].pModule = + (TADDR)m_decoder.GetPersistedModuleImage(); + m_dependencies[index].fIsHardbound = TRUE; + _ASSERTE( (m_dependencies[index].pModule + > m_dependencies[index].pLoadedAddress) + && (m_dependencies[index].pModule + < m_dependencies[index].pLoadedAddress + + m_dependencies[index].size) ); + // patch the Module vtable so that the DAC is able to instantiate it + TADDR vtbl = DacGetTargetVtForHostVt(Module::VPtrHostVTable(), true); + DacWriteAll( m_dependencies[index].pModule.GetAddr(), &vtbl, sizeof(vtbl), false ); + } + else + { + COUNT_T numDeps; + PTR_CORCOMPILE_DEPENDENCY deps(TO_TADDR(m_decoder.GetNativeDependencies(&numDeps))); + + entry = deps + (index-1); + + //load the dependency, get the pointer, and use the PEDecoder + //to open the metadata. + + TempBuffer buf; + TADDR loadedBase; + /* REVISIT_TODO Tue 11/22/2005 + * Is this the right name? + */ + Dependency& dependency = m_dependencies[index]; + AppendTokenName(entry->dwAssemblyRef, buf, m_manifestImport, true); + bool isHardBound = !!(entry->signNativeImage != INVALID_NGEN_SIGNATURE); + SString mscorlibStr(SString::Literal, CoreLibName_W); + bool isMscorlib = (0 == buf.Compare( mscorlibStr )); + dependency.fIsHardbound = isHardBound; + wcscpy_s(dependency.name, _countof(dependency.name), + (const WCHAR*)buf); + if( isHardBound ) + { + IfFailThrow(m_librarySupport->LoadHardboundDependency((const WCHAR*)buf, + entry->signNativeImage, &loadedBase)); + + dependency.pLoadedAddress = loadedBase; + } + else + { + ASSEMBLYMETADATA asmData = {0}; + const void * hashValue; + ULONG hashLength, size, flags; + IfFailThrow(m_manifestAssemblyImport->GetAssemblyRefProps(entry->dwAssemblyRef, &hashValue, &hashLength, bigBuffer, bigBufferSize, &size, &asmData, NULL, NULL, &flags)); + + + HRESULT hr = + m_librarySupport->LoadSoftboundDependency((const WCHAR*)buf, + (const BYTE*)&asmData, (const BYTE*)hashValue, hashLength, + &loadedBase); + if( FAILED(hr) ) + { + TempBuffer pkt; + if( hashLength > 0 ) + { + appendByteArray(pkt, (BYTE*)hashValue, hashLength); + } + else + { + pkt.Set( W("<No Hash>") ); + } + //try to continue without loading this softbound + //dependency. + m_display->ErrorPrintF( "WARNING Failed to load softbound dependency:\n\t%S,Version=%d.%d.0.0,PublicKeyToken=%S.\n\tAttempting to continue. May crash later in due to missing metadata\n", + (const WCHAR *)buf, asmData.usMajorVersion, + asmData.usMinorVersion, (const WCHAR *)pkt ); + m_dependencies[index].entry = entry; + return &m_dependencies[index]; + + } + //save this off to the side so OpenImport can find the metadata. + m_dependencies[index].pLoadedAddress = loadedBase; + } + /* REVISIT_TODO Wed 11/23/2005 + * Refactor this with OpenMetadata from above. + */ + //now load the metadata from the new image. + PEDecoder decoder(dac_cast<PTR_VOID>(loadedBase)); + if( isHardBound ) + { + dependency.pPreferredBase = + TO_TADDR(decoder.GetNativePreferredBase()); + dependency.size = decoder.Has32BitNTHeaders() ? + decoder.GetNTHeaders32()->OptionalHeader.SizeOfImage : + decoder.GetNTHeaders64()->OptionalHeader.SizeOfImage; + } + ReleaseHolder<IMetaDataDispenserEx> pDispenser; + IfFailThrow(MetaDataGetDispenser(CLSID_CorMetaDataDispenser, + IID_IMetaDataDispenserEx, + (void **) &pDispenser)); + + VARIANT opt; + IfFailThrow(pDispenser->GetOption(MetaDataCheckDuplicatesFor, + &opt)); + V_UI4(&opt) |= MDDupAssemblyRef | MDDupFile; + IfFailThrow(pDispenser->SetOption(MetaDataCheckDuplicatesFor, + &opt)); + if( decoder.HasNativeHeader() ) + { + dependency.pModule = + TO_TADDR(decoder.GetPersistedModuleImage()); + _ASSERTE( (PTR_TO_TADDR(dependency.pModule) > loadedBase) + && (PTR_TO_TADDR(dependency.pModule) < loadedBase + + decoder.GetSize()) ); + // patch the Module vtable so that the DAC is able to instantiate it + TADDR vtbl = DacGetTargetVtForHostVt(Module::VPtrHostVTable(), true); + DacWriteAll( m_dependencies[index].pModule.GetAddr(), &vtbl, sizeof(vtbl), false ); + } + else + { + dependency.pModule = NULL; + } + + const void * data; + COUNT_T size; + + DACCOP_IGNORE(CastBetweenAddressSpaces,"nidump is in-proc and doesn't maintain a clean separation of address spaces (target and host are the same."); + data = reinterpret_cast<void*>(dac_cast<TADDR>(decoder.GetMetadata(&size))); + + dependency.pMetadataStartTarget = TO_TADDR(data); + dependency.MetadataSize = size; + data = PTR_READ(TO_TADDR(data), size); + dependency.pMetadataStartHost = TO_TADDR(data); + IfFailThrow(pDispenser->OpenScopeOnMemory(data, size, + ofRead, + IID_IMetaDataImport2, + (IUnknown **) &dependency.pImport)); + dependency.fIsMscorlib = isMscorlib; + } + + m_dependencies[index].entry = entry; + + } + + return &m_dependencies[index]; +} + +IMetaDataImport2* NativeImageDumper::TypeToString(PTR_CCOR_SIGNATURE &sig, SString& buf) +{ + return TypeToString(sig, buf, NULL); +} +#if 0 +void NativeImageDumper::TypeToString(PTR_CCOR_SIGNATURE &sig, + IMetaDataImport2 *pImport) +{ + CQuickBytes tmp; + + if (pImport == NULL) + pImport = m_import; + + LPCWSTR type = PrettyPrintSig( sig, INT_MAX, W(""), &tmp, pImport ); + _ASSERTE(type); + m_display->ErrorPrintF( "%S", type ); +} +#endif + +IMetaDataImport2 * NativeImageDumper::TypeToString(PTR_CCOR_SIGNATURE &sig, + SString& buf, + IMetaDataImport2 *pImport, + IMetaDataImport2 *pOrigImport /* =NULL */) + +{ + IF_OPT(DISABLE_NAMES) + { + buf.Append( W("Disabled") ); + return pImport; + } + + if (pImport == NULL) + pImport = m_import; + if (pOrigImport == NULL) + pOrigImport = pImport; + + IMetaDataImport2 * pRet = pImport; +#define TYPEINFO(enumName, classSpace, className, size, gcType, isArray, isPrim, isFloat, isModifier, isGenVar) \ + className, + static const char *elementNames[] = { +#include "cortypeinfo.h" + }; +#undef TYPEINFO + + CorElementType type = DacSigUncompressElementType(sig); + + if (type == (CorElementType) ELEMENT_TYPE_MODULE_ZAPSIG) + { + unsigned idx = DacSigUncompressData(sig); + buf.AppendPrintf( W("module %d "), idx ); + //switch module + const Import * import = OpenImport(idx); + pImport = import->dependency->pImport; + + //if there was a module switch, return the import for the new module. + //This is useful for singatures, where the module index applies to + //subsequent tokens. + pRet = pImport; + + type = DacSigUncompressElementType(sig); + } + if (type >= 0 && (size_t)type < _countof(elementNames) + && elementNames[type] != NULL) + { + buf.AppendPrintf( "%s", elementNames[type] ); + } + else switch ((DWORD)type) + { + case ELEMENT_TYPE_CANON_ZAPSIG: + buf.Append( W("System.__Canon") ); + break; + + case ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG: + case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG: + { + buf.Append( W("native ") ); + TypeToString(sig, buf, pImport); + } + break; + + case ELEMENT_TYPE_VALUETYPE: + case ELEMENT_TYPE_CLASS: + { + if (type == ELEMENT_TYPE_VALUETYPE) + buf.Append( W("struct ") ); + + mdToken token = DacSigUncompressToken(sig); + AppendTokenName(token, buf, pImport); + } + break; + + case ELEMENT_TYPE_SZARRAY: + TypeToString(sig, buf, pImport); + buf.Append( W("[]") ); + break; + + case ELEMENT_TYPE_ARRAY: + { + TypeToString(sig, buf, pImport, pOrigImport); + unsigned rank = DacSigUncompressData(sig); + if (rank == 0) + buf.Append( W("[??]") ); + else + { + size_t cbLowerBounds; + if (!ClrSafeInt<size_t>::multiply(rank, 2*sizeof(int), cbLowerBounds/* passed by ref */)) + ThrowHR(COR_E_OVERFLOW); + int* lowerBounds = (int*) _alloca(cbLowerBounds); + int* sizes = &lowerBounds[rank]; + memset(lowerBounds, 0, sizeof(int)*2*rank); + + unsigned numSizes = DacSigUncompressData(sig); + _ASSERTE(numSizes <= rank); + unsigned int i; + for(i =0; i < numSizes; i++) + sizes[i] = DacSigUncompressData(sig); + + unsigned numLowBounds = DacSigUncompressData(sig); + _ASSERTE(numLowBounds <= rank); + for(i = 0; i < numLowBounds; i++) + lowerBounds[i] = DacSigUncompressData(sig); + + buf.Append(W("[")); + for(i = 0; i < rank; i++) + { + if (sizes[i] != 0 && lowerBounds[i] != 0) + { + if (lowerBounds[i] == 0) + buf.AppendPrintf( W("%s"), sizes[i] ); + else + { + buf.AppendPrintf( W("%d ..."), lowerBounds[i] ); + if (sizes[i] != 0) + buf.AppendPrintf( W("%d"), + lowerBounds[i] + sizes[i] + + 1 ); + } + } + if (i < rank-1) + buf.Append( W(",") ); + } + buf.Append( W("]") ); + } + } + break; + + case ELEMENT_TYPE_MVAR: + buf.Append( W("!") ); + // fall through + case ELEMENT_TYPE_VAR: + buf.AppendPrintf( W("!%d"), DacSigUncompressData(sig)); + break; + + case ELEMENT_TYPE_VAR_ZAPSIG: + { + buf.Append( W("var ") ); + + mdToken token = TokenFromRid(DacSigUncompressData(sig), mdtGenericParam); + AppendTokenName(token, buf, pImport); + } + break; + + case ELEMENT_TYPE_GENERICINST: + { + TypeToString(sig, buf, pImport, pOrigImport); + unsigned ntypars = DacSigUncompressData(sig); + buf.Append( W("<") ); + for (unsigned i = 0; i < ntypars; i++) + { + if (i > 0) + buf.Append( W(",") ); + // switch pImport back to our original Metadata importer + TypeToString(sig, buf, pOrigImport, pOrigImport); + } + buf.Append( W(">") ); + } + break; + + case ELEMENT_TYPE_FNPTR: + buf.Append( W("(fnptr)") ); + break; + + // Modifiers or depedant types + case ELEMENT_TYPE_PINNED: + TypeToString(sig, buf, pImport, pOrigImport); + buf.Append( W(" pinned") ); + break; + + case ELEMENT_TYPE_PTR: + TypeToString(sig, buf, pImport, pOrigImport); + buf.Append( W("*") ); + break; + + case ELEMENT_TYPE_BYREF: + TypeToString(sig, buf, pImport, pOrigImport); + buf.Append( W("&") ); + break; + + case ELEMENT_TYPE_SENTINEL: + case ELEMENT_TYPE_END: + default: + _ASSERTE(!"Unknown Type"); + IfFailThrow(E_FAIL); + break; + } + return pRet; +} + +void NativeImageDumper::DumpMethods(PTR_Module module) +{ + COUNT_T hotCodeSize; + PCODE hotCode = m_decoder.GetNativeHotCode(&hotCodeSize); + + + COUNT_T codeSize; + PCODE code = m_decoder.GetNativeCode(&codeSize); + + COUNT_T coldCodeSize; + PCODE coldCode = m_decoder.GetNativeColdCode(&coldCodeSize); + + DisplayStartCategory( "Code", METHODS ); + DisplayWriteElementAddress( "HotCode", DataPtrToDisplay(hotCode), + hotCodeSize, METHODS ); + + DisplayWriteElementAddress( "UnprofiledCode", + DataPtrToDisplay(code), + codeSize, METHODS ); + DisplayWriteElementAddress( "ColdCode", + DataPtrToDisplay(coldCode), + coldCodeSize, METHODS ); + + PTR_CORCOMPILE_CODE_MANAGER_ENTRY codeEntry(m_decoder.GetNativeCodeManagerTable()); + + DisplayWriteElementAddress( "ROData", + RvaToDisplay(codeEntry->ROData.VirtualAddress), + codeEntry->ROData.Size, METHODS ); + + DisplayWriteElementAddress( "HotCommonCode", + DataPtrToDisplay(hotCode), + codeEntry->HotIBCMethodOffset, METHODS ); + + DisplayWriteElementAddress( "HotIBCMethodCode", + DataPtrToDisplay(hotCode + + codeEntry->HotIBCMethodOffset), + codeEntry->HotGenericsMethodOffset + - codeEntry->HotIBCMethodOffset, + METHODS ); + + DisplayWriteElementAddress( "HotGenericsMethodCode", + DataPtrToDisplay(hotCode + + codeEntry->HotGenericsMethodOffset), + hotCodeSize - codeEntry->HotGenericsMethodOffset, + METHODS ); + + DisplayWriteElementAddress( "ColdIBCMethodCode", + DataPtrToDisplay(coldCode), + codeEntry->ColdUntrainedMethodOffset, + METHODS ); + + MethodIterator mi(module, &m_decoder); + + DisplayStartArray( "Methods", NULL, METHODS ); + + while( mi.Next() ) + { + DumpCompleteMethod( module, mi ); + } + + DisplayEndArray( "Total Methods", METHODS ); //Methods + + /* REVISIT_TODO Wed 12/14/2005 + * I have this coverage read in here because there is some other data between the + * methods in debug builds. For now just whack the whole text section. Go + * back later and check out that I really got everything. + */ + CoverageRead( hotCode, hotCodeSize ); + CoverageRead( coldCode, coldCodeSize ); +#ifdef USE_CORCOMPILE_HEADER + CoverageRead( hotCodeTable, hotCodeTableSize ); + CoverageRead( coldCodeTable, coldCodeTableSize ); +#endif + + DisplayEndCategory( METHODS ); //Code + + //m_display->StartCategory( "Methods" ); +} + +static SString g_holdStringOutData; + +static void stringOut( const char* fmt, ... ) +{ + va_list args; + va_start(args, fmt); + g_holdStringOutData.AppendVPrintf(fmt, args); + va_end(args); +} + +static void nullStringOut( const char * fmt, ... ) { } + +const NativeImageDumper::EnumMnemonics s_CorExceptionFlags[] = +{ +#define CEF_ENTRY(f,v) NativeImageDumper::EnumMnemonics(f, v) + CEF_ENTRY(COR_ILEXCEPTION_CLAUSE_NONE, W("none")), + CEF_ENTRY(COR_ILEXCEPTION_CLAUSE_FILTER, W("filter")), + CEF_ENTRY(COR_ILEXCEPTION_CLAUSE_FINALLY, W("finally")), + CEF_ENTRY(COR_ILEXCEPTION_CLAUSE_FAULT, W("fault")), + CEF_ENTRY(COR_ILEXCEPTION_CLAUSE_DUPLICATED, W("duplicated")), +#undef CEF_ENTRY +}; + +void NativeImageDumper::DumpCompleteMethod(PTR_Module module, MethodIterator& mi) +{ + PTR_MethodDesc md = mi.GetMethodDesc(); + +#ifdef WIN64EXCEPTIONS + PTR_RUNTIME_FUNCTION pRuntimeFunction = mi.GetRuntimeFunction(); +#endif + + //Read the GCInfo to get the total method size. + unsigned methodSize = 0; + unsigned gcInfoSize = UINT_MAX; + + //parse GCInfo for size information. + GCInfoToken gcInfoToken = mi.GetGCInfoToken(); + PTR_CBYTE gcInfo = dac_cast<PTR_CBYTE>(gcInfoToken.Info); + + void (* stringOutFn)(const char *, ...); + IF_OPT(GC_INFO) + { + stringOutFn = stringOut; + } + else + { + stringOutFn = nullStringOut; + } + if (gcInfo != NULL) + { + PTR_CBYTE curGCInfoPtr = gcInfo; + g_holdStringOutData.Clear(); + GCDump gcDump(gcInfoToken.Version); + gcDump.gcPrintf = stringOutFn; +#if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER) + GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_CODE_LENGTH); + methodSize = gcInfoDecoder.GetCodeLength(); +#endif + + //dump the data to a string first so we can get the gcinfo size. +#ifdef _TARGET_X86_ + InfoHdr hdr; + stringOutFn( "method info Block:\n" ); + curGCInfoPtr += gcDump.DumpInfoHdr(PTR_CBYTE(gcInfoToken.Info), &hdr, &methodSize, 0); + stringOutFn( "\n" ); +#endif + + IF_OPT(METHODS) + { +#ifdef _TARGET_X86_ + stringOutFn( "PointerTable:\n" ); + curGCInfoPtr += gcDump.DumpGCTable( curGCInfoPtr, + hdr, + methodSize, 0); + gcInfoSize = curGCInfoPtr - gcInfo; +#elif defined(USE_GC_INFO_DECODER) + stringOutFn( "PointerTable:\n" ); + curGCInfoPtr += gcDump.DumpGCTable( curGCInfoPtr, + methodSize, 0); + gcInfoSize = (unsigned)(curGCInfoPtr - gcInfo); +#endif + } + + //data is output below. + } + + TADDR hotCodePtr = mi.GetMethodStartAddress(); + TADDR coldCodePtr = mi.GetMethodColdStartAddress(); + + size_t hotCodeSize = methodSize; + size_t coldCodeSize = 0; + + if (coldCodePtr != NULL) + { + hotCodeSize = mi.GetHotCodeSize(); + coldCodeSize = methodSize - hotCodeSize; + } + + _ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(md))); + const Dependency* mdDep = GetDependencyFromMD(md); + TempBuffer buffer; + _ASSERTE(mdDep->pImport); + MethodDescToString(md, buffer); + + DisplayStartElement( "Method", METHODS ); + DisplayWriteElementStringW( "Name", (const WCHAR *)buffer, METHODS ); + + /* REVISIT_TODO Mon 10/24/2005 + * Do I have to annotate this? + */ + DisplayWriteElementPointer("m_methodDesc", + DPtrToPreferredAddr(md), + METHODS); + + DisplayStartStructure( "m_gcInfo", + DPtrToPreferredAddr(gcInfo), + gcInfoSize, + METHODS ); + + DisplayStartTextElement( "Contents", GC_INFO ); + DisplayWriteXmlTextBlock( ("%S", (const WCHAR *)g_holdStringOutData), GC_INFO ); + DisplayEndTextElement( GC_INFO ); //Contents + + DisplayEndStructure( METHODS ); //GCInfo + + PTR_CORCOMPILE_EXCEPTION_LOOKUP_TABLE pExceptionInfoTable (PTR_TO_TADDR(module->GetNGenLayoutInfo()->m_ExceptionInfoLookupTable.StartAddress())); + if (pExceptionInfoTable) + { + COUNT_T numLookupEntries = (COUNT_T) (module->GetNGenLayoutInfo()->m_ExceptionInfoLookupTable.Size() / sizeof(CORCOMPILE_EXCEPTION_LOOKUP_TABLE_ENTRY)); + DWORD methodStartRVA = m_decoder.GetDataRva(TO_TADDR(hotCodePtr)); + + COUNT_T ehInfoSize = 0; + DWORD exceptionInfoRVA = NativeExceptionInfoLookupTable::LookupExceptionInfoRVAForMethod(pExceptionInfoTable, + numLookupEntries, + methodStartRVA, + &ehInfoSize); + + if( exceptionInfoRVA != 0 ) + { + PTR_CORCOMPILE_EXCEPTION_CLAUSE pExceptionInfoArray = dac_cast<PTR_CORCOMPILE_EXCEPTION_CLAUSE>(PTR_TO_TADDR(m_decoder.GetBase()) + exceptionInfoRVA); + COUNT_T ehCount = ehInfoSize / sizeof(CORCOMPILE_EXCEPTION_CLAUSE); + _ASSERTE(ehCount > 0); + DisplayStartArray("EHClauses", NULL, METHODS ); + for( unsigned i = 0; i < ehCount; ++i ) + { + PTR_CORCOMPILE_EXCEPTION_CLAUSE host = pExceptionInfoArray + i; + + DisplayStartStructure( "Clause", DPtrToPreferredAddr(host), sizeof(PTR_CORCOMPILE_EXCEPTION_CLAUSE), METHODS); + DisplayWriteFieldEnumerated( Flags, host->Flags, + EE_ILEXCEPTION_CLAUSE, + s_CorExceptionFlags, W(", "), + METHODS ); + DisplayWriteFieldUInt( TryStartPC, host->TryStartPC, + EE_ILEXCEPTION_CLAUSE, METHODS ); + DisplayWriteFieldUInt( TryEndPC, host->TryEndPC, + EE_ILEXCEPTION_CLAUSE, METHODS ); + DisplayWriteFieldUInt( HandlerStartPC, + host->HandlerStartPC, + EE_ILEXCEPTION_CLAUSE, METHODS ); + DisplayWriteFieldUInt( HandlerEndPC, + host->HandlerEndPC, + EE_ILEXCEPTION_CLAUSE, METHODS ); + if( host->Flags & COR_ILEXCEPTION_CLAUSE_FILTER ) + { + DisplayWriteFieldUInt( FilterOffset, host->FilterOffset, + EE_ILEXCEPTION_CLAUSE, METHODS ); + } + else if( !(host->Flags & (COR_ILEXCEPTION_CLAUSE_FAULT | COR_ILEXCEPTION_CLAUSE_FINALLY)) ) + { + WriteFieldMDTokenImport( ClassToken, host->ClassToken, + EE_ILEXCEPTION_CLAUSE, METHODS, + mdDep->pImport ); + } + DisplayEndStructure( METHODS ); //Clause + } + DisplayEndArray("Total EHClauses", METHODS ); // Clauses + } + } + + TADDR fixupList = md->GetFixupList(); + if (fixupList != NULL) + { + DisplayStartArray( "Fixups", NULL, METHODS ); + DumpMethodFixups(module, fixupList); + DisplayEndArray(NULL, METHODS); //Fixups + } + + DisplayStartStructure( "Code", DataPtrToDisplay(hotCodePtr), hotCodeSize, + METHODS ); + + IF_OPT(DISASSEMBLE_CODE) + { + // Disassemble hot code. Read the code into the host process. + /* REVISIT_TODO Mon 10/24/2005 + * Is this align up right? + */ + BYTE * codeStartHost = + reinterpret_cast<BYTE*>(PTR_READ(hotCodePtr, + (ULONG32)ALIGN_UP(hotCodeSize, + CODE_SIZE_ALIGN))); + DisassembleMethod( codeStartHost, hotCodeSize ); + } + else + { + CoverageRead(hotCodePtr, + (ULONG32)ALIGN_UP(hotCodeSize, CODE_SIZE_ALIGN)); + } + + DisplayEndStructure(METHODS); //HotCode + + if( coldCodePtr != NULL ) + { + DisplayStartStructure( "ColdCode", DataPtrToDisplay(coldCodePtr), + coldCodeSize, METHODS ); + IF_OPT(DISASSEMBLE_CODE) + { + // Disassemble cold code. Read the code into the host process. + BYTE * codeStartHost = + reinterpret_cast<BYTE*>(PTR_READ(coldCodePtr, + (ULONG32)ALIGN_UP(coldCodeSize, + CODE_SIZE_ALIGN))); + DisassembleMethod( codeStartHost, coldCodeSize ); + } + else + { + CoverageRead(coldCodePtr, + (ULONG32)ALIGN_UP(coldCodeSize, CODE_SIZE_ALIGN)); + + } + DisplayEndStructure( METHODS ); //ColdCode + } + DisplayEndElement( METHODS ); //Method +} +#undef IDC_SWITCH + + + +void NativeImageDumper::DisassembleMethod(BYTE *code, SIZE_T size) +{ + _ASSERTE(CHECK_OPT(DISASSEMBLE_CODE)); + + m_display->StartTextElement( "NativeCode" ); + +#ifdef FEATURE_MSDIS + + BYTE *codeStart = code; + + /* XXX Wed 8/22/2007 + * The way I compute code size includes the switch tables at the end of the hot and/or cold section. + * When the disassembler gets there, it has a tendency to crash as it runs off the end of mapped + * memory. In order to properly compute this I need to look at the UnwindData (which is a + * kernel32!RUNTIME_FUNCTION structure that gives the address range for the code. However, I also need + * to chase through the list of funclets to make sure I disassemble everything. Instead of doing that, + * I'll just trap the AV. + */ + EX_TRY + { + while (code < (codeStart + size)) + { + const size_t count = m_dis->CbDisassemble(0, code, size); + + if (count == 0) + { + m_display->WriteXmlText( "%04x\tUnknown instruction (%02x)\n", code-codeStart, *code); + code++; + continue; + } + + /* XXX Fri 09/16/2005 + * PTR_HOST_TO_TADDR doesn't work on interior pointers. + */ + m_currentAddress = m_decoder.GetDataRva(PTR_HOST_TO_TADDR(codeStart) + + (code - codeStart)) + + PTR_TO_TADDR(m_decoder.GetBase()); + + const size_t cinstr = m_dis->Cinstruction(); + size_t inum = 0; + while (true) + { + WCHAR szOpcode[4096]; + size_t len = m_dis->CchFormatInstr(szOpcode, _countof(szOpcode)); + _ASSERTE(szOpcode[len-1] == 0); + m_display->WriteXmlText( "%04x\t%S\n", (code-codeStart) + (inum * 4), szOpcode ); + +NEXT_INSTR: + if (++inum >= cinstr) + break; + + _ASSERTE((inum * 4) < count); // IA64 has 3 instructions per bundle commonly + // referenced as offset 0, 4, and 8 + if (!m_dis->FSelectInstruction(inum)) + { + m_display->WriteXmlText( "%04x\tUnknown instruction within bundle\n", (code-codeStart) + (inum * 4)); + goto NEXT_INSTR; + } + } + + code += count; + } + } + EX_CATCH + { + + } + EX_END_CATCH(SwallowAllExceptions); + +#else // FEATURE_MSDIS + + m_display->WriteXmlText( "Disassembly not supported\n" ); + +#endif // FEATURE_MSDIS + + m_display->EndTextElement(); //NativeCode +} + +SIZE_T NativeImageDumper::TranslateAddressCallback(IXCLRDisassemblySupport *dis, + CLRDATA_ADDRESS addr, + __out_ecount(nameSize) WCHAR *name, SIZE_T nameSize, + DWORDLONG *offset) +{ + NativeImageDumper *pThis = (NativeImageDumper *) dis->PvClient(); + + SIZE_T ret = pThis->TranslateSymbol(dis, + addr+(SIZE_T)pThis->m_currentAddress, + name, nameSize, offset); +#ifdef _DEBUG + if( ret == 0 ) + { + _snwprintf_s(name, nameSize, _TRUNCATE, W("@TRANSLATED ADDRESS@ %p"), + (TADDR)(addr + (SIZE_T)pThis->m_currentAddress) ); + ret = wcslen(name); + *offset = -1; + } +#endif + return ret; +} +SIZE_T NativeImageDumper::TranslateFixupCallback(IXCLRDisassemblySupport *dis, + CLRDATA_ADDRESS addr, + SIZE_T size, __out_ecount(nameSize) WCHAR *name, + SIZE_T nameSize, + DWORDLONG *offset) +{ + NativeImageDumper *pThis = (NativeImageDumper *) dis->PvClient(); + if( !dis->TargetIsAddress() ) + return 0; + + TADDR taddr = TO_TADDR(pThis->m_currentAddress) + (TADDR)addr; + SSIZE_T targetOffset; + switch (size) + { + case sizeof(void*): + targetOffset = *PTR_SIZE_T(taddr); + break; +#ifdef _WIN64 + case sizeof(INT32): + targetOffset = *PTR_INT32(taddr); + break; +#endif + case sizeof(short): + targetOffset = *(short*)(WORD*)PTR_WORD(taddr); + break; + case sizeof(signed char): + targetOffset = *PTR_SBYTE(taddr); + break; + default: + return 0; + } + + CLRDATA_ADDRESS address = targetOffset + TO_TADDR(pThis->m_currentAddress) + addr + size; + + SIZE_T ret = pThis->TranslateSymbol(dis, address, name, nameSize, offset); + if( ret == 0 ) + { + _snwprintf_s(name, nameSize, _TRUNCATE, W("@TRANSLATED FIXUP@ %p"), (TADDR)address); + ret = wcslen(name); + *offset = -1; + } + return ret; +} + +size_t NativeImageDumper::TranslateSymbol(IXCLRDisassemblySupport *dis, + CLRDATA_ADDRESS addr, __out_ecount(nameSize) WCHAR *name, + SIZE_T nameSize, DWORDLONG *offset) +{ +#ifdef FEATURE_READYTORUN + if (m_pReadyToRunHeader != NULL) + return 0; +#endif + + if (isInRange((TADDR)addr)) + { + COUNT_T rva = (COUNT_T)(addr - PTR_TO_TADDR(m_decoder.GetBase())); + + COUNT_T helperTableSize; + void *helperTable = m_decoder.GetNativeHelperTable(&helperTableSize); + + if (rva >= m_decoder.GetDataRva(TO_TADDR(helperTable)) + && rva < (m_decoder.GetDataRva(TO_TADDR(helperTable)) + +helperTableSize)) + { + int helperIndex = (USHORT)*PTR_DWORD(TO_TADDR(addr)); +// _ASSERTE(helperIndex < CORINFO_HELP_COUNT); + // because of literal blocks we might have bogus values + if (helperIndex < CORINFO_HELP_COUNT) + _snwprintf_s(name, nameSize, _TRUNCATE, W("<%S>"), g_helperNames[helperIndex]); + else + _snwprintf_s(name, nameSize, _TRUNCATE, W("Illegal HelperIndex<%04X>"), helperIndex); + *offset = 0; + return wcslen(name); + } + + PTR_Module module = (TADDR)m_decoder.GetPersistedModuleImage(); + PTR_NGenLayoutInfo pNgenLayout = module->GetNGenLayoutInfo(); + + for (int iRange = 0; iRange < 2; iRange++) + { + if (pNgenLayout->m_CodeSections[iRange].IsInRange((TADDR)addr)) + { + int MethodIndex = NativeUnwindInfoLookupTable::LookupUnwindInfoForMethod(rva, pNgenLayout->m_pRuntimeFunctions[iRange], 0, pNgenLayout->m_nRuntimeFunctions[iRange] - 1); + if (MethodIndex >= 0) + { +#ifdef WIN64EXCEPTIONS + while (pNgenLayout->m_MethodDescs[iRange][MethodIndex] == 0) + MethodIndex--; +#endif + + PTR_RUNTIME_FUNCTION pRuntimeFunction = pNgenLayout->m_pRuntimeFunctions[iRange] + MethodIndex; + + PTR_MethodDesc pMD = NativeUnwindInfoLookupTable::GetMethodDesc(pNgenLayout, pRuntimeFunction, PTR_TO_TADDR(m_decoder.GetBase())); + TempBuffer buf; + MethodDescToString( pMD, buf ); + _snwprintf_s(name, nameSize, _TRUNCATE, W("%s "), (const WCHAR *)buf ); + *offset = rva - RUNTIME_FUNCTION__BeginAddress(pRuntimeFunction); + return wcslen(name); + } + } + } + + if (pNgenLayout->m_CodeSections[2].IsInRange((TADDR)addr)) + { + int ColdMethodIndex = NativeUnwindInfoLookupTable::LookupUnwindInfoForMethod(rva, pNgenLayout->m_pRuntimeFunctions[2], 0, pNgenLayout->m_nRuntimeFunctions[2] - 1); + if (ColdMethodIndex >= 0) + { + PTR_RUNTIME_FUNCTION pRuntimeFunction; + + PTR_CORCOMPILE_COLD_METHOD_ENTRY pColdCodeMap = pNgenLayout->m_ColdCodeMap; + +#ifdef WIN64EXCEPTIONS + while (pColdCodeMap[ColdMethodIndex].mainFunctionEntryRVA == 0) + ColdMethodIndex--; + + pRuntimeFunction = dac_cast<PTR_RUNTIME_FUNCTION>(PTR_TO_TADDR(m_decoder.GetBase()) + pColdCodeMap[ColdMethodIndex].mainFunctionEntryRVA); +#else + DWORD ColdUnwindData = pNgenLayout->m_pRuntimeFunctions[2][ColdMethodIndex].UnwindData; + _ASSERTE((ColdUnwindData & RUNTIME_FUNCTION_INDIRECT) != 0); + pRuntimeFunction = dac_cast<PTR_RUNTIME_FUNCTION>(PTR_TO_TADDR(m_decoder.GetBase()) + (ColdUnwindData & ~RUNTIME_FUNCTION_INDIRECT)); +#endif + + PTR_MethodDesc pMD = NativeUnwindInfoLookupTable::GetMethodDesc(pNgenLayout, pRuntimeFunction, PTR_TO_TADDR(m_decoder.GetBase())); + TempBuffer buf; + MethodDescToString( pMD, buf ); + _snwprintf_s(name, nameSize, _TRUNCATE, W("%s (cold region)"), (const WCHAR *)buf ); + *offset = rva - RUNTIME_FUNCTION__BeginAddress(&pNgenLayout->m_pRuntimeFunctions[2][ColdMethodIndex]); + return wcslen(name); + } + } + + //Dumping precodes by name requires some information from the module (the precode ranges). + IF_OPT_OR(PRECODES, MODULE) + { + TempBuffer precodeBuf; + //maybe it is a precode + PTR_Precode maybePrecode((TADDR)addr); + const char * precodeName = NULL; + if (isPrecode((TADDR)addr)) + { + switch(maybePrecode->GetType()) + { + case PRECODE_INVALID: + precodeName = "InvalidPrecode"; break; + case PRECODE_STUB: + precodeName = "StubPrecode"; break; +#ifdef HAS_NDIRECT_IMPORT_PRECODE + case PRECODE_NDIRECT_IMPORT: + precodeName = "NDirectImportPrecode"; break; +#endif // HAS_NDIRECT_IMPORT_PRECODE +#ifdef HAS_REMOTING_PRECODE + case PRECODE_REMOTING: + precodeName = "RemotingPrecode"; break; +#endif // HAS_REMOTING_PRECODE +#ifdef HAS_FIXUP_PRECODE + case PRECODE_FIXUP: + precodeName = "FixupPrecode"; break; +#endif // HAS_FIXUP_PRECODE +#ifdef HAS_THISPTR_RETBUF_PRECODE + case PRECODE_THISPTR_RETBUF: + precodeName = "ThisPtrRetBufPrecode"; break; +#endif // HAS_THISPTR_RETBUF_PRECODE + } + + if( precodeName ) + { + //hot or cold? + precodeBuf.AppendPrintf( W("%S (0x%p)"), precodeName, addr ); + } + //get MethodDesc from precode and dump the target + PTR_MethodDesc precodeMD(maybePrecode->GetMethodDesc()); + precodeBuf.Append( W(" for ") ); + MethodDescToString(precodeMD, precodeBuf); + + _snwprintf_s(name, nameSize, _TRUNCATE, W("%s"), (const WCHAR *)precodeBuf); + + *offset = 0; + return wcslen(name); + } + } + + PTR_CORCOMPILE_IMPORT_SECTION pImportSection = m_decoder.GetNativeImportSectionForRVA(rva); + if (pImportSection != NULL) + { + const char * wbRangeName = NULL; + switch (pImportSection->Type) + { + case CORCOMPILE_IMPORT_TYPE_EXTERNAL_METHOD: + wbRangeName = "ExternalMethod"; + break; + +#if 0 + case CORCOMPILE_IMPORT_TYPE_VIRTUAL_METHOD: + wbRangeName = "VirtualMethod"; + break; + + case CORCOMPILE_IMPORT_TYPE_STUB_DISPATCH: + wbRangeName = "StubDispatch"; + break; +#endif + + // This method is only ever called for targets of direct calls right now and so the only + // import that can meaninfully show up here is external method thunk. + default: + return 0; + } + + TempBuffer fixupThunkBuf; + fixupThunkBuf.AppendPrintf( W("%S (0x%p) for "), wbRangeName, addr ); + FixupThunkToString(pImportSection, (TADDR)addr, fixupThunkBuf); + + _snwprintf_s(name, nameSize, _TRUNCATE, W("%s"), (const WCHAR *)fixupThunkBuf); + + *offset = 0; + return wcslen(name); + } + } + else if( g_dacImpl->GetJitHelperFunctionName(addr, + _countof(bigByteBuffer), + (char*)bigByteBuffer, + NULL ) == S_OK ) + { + *offset = 0; + _snwprintf_s( name, nameSize, _TRUNCATE, W("%S"), bigByteBuffer ); + return wcslen(name); + } + else + { + //check mscorwks + if( m_mscorwksBase <= addr && + addr < (m_mscorwksBase + m_mscorwksSize) ) + { + *offset = addr - m_mscorwksBase; + _snwprintf_s( name, nameSize, _TRUNCATE, W("clr") ); + return wcslen(name); + } + for( COUNT_T i = 0; i < m_numDependencies; ++i ) + { + const Dependency& dep = m_dependencies[i]; + if( dep.pLoadedAddress <= addr && + addr < (dep.pLoadedAddress + dep.size) ) + { + *offset = addr - dep.pLoadedAddress; + _snwprintf_s( name, nameSize, _TRUNCATE, W("%s.ni"), dep.name ); + return wcslen(name); + } + } + } + + return 0; +} + +BOOL NativeImageDumper::HandleFixupForMethodDump(PTR_CORCOMPILE_IMPORT_SECTION pSection, SIZE_T fixupIndex, SIZE_T *fixupCell) +{ + PTR_SIZE_T fixupPtr(TO_TADDR(fixupCell)); + m_display->StartElement( "Fixup" ); + m_display->WriteElementPointer( "Address", + DataPtrToDisplay( TO_TADDR(fixupCell) ) ); + m_display->WriteElementUInt( "TaggedValue", (DWORD)*fixupPtr ); + WriteElementsFixupBlob(pSection, *fixupPtr); + m_display->EndElement(); + + return TRUE; +} + +void NativeImageDumper::DumpMethodFixups(PTR_Module module, + TADDR fixupList) +{ + _ASSERTE( CHECK_OPT(METHODS) ); + + COUNT_T nImportSections; + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_decoder.GetNativeImportSections(&nImportSections); + + //create the first element outside of the callback. The callback creates + //subsequent elements. + module->FixupDelayListAux( fixupList, this, + &NativeImageDumper::HandleFixupForMethodDump, + pImportSections, nImportSections, + &m_decoder ); +} + +IMAGE_SECTION_HEADER * NativeImageDumper::FindSection( char const * name ) +{ + COUNT_T numberOfSections = m_decoder.GetNumberOfSections(); + PTR_IMAGE_SECTION_HEADER curSection( m_decoder.FindFirstSection() ); + + for ( ; numberOfSections > 0; --numberOfSections, ++curSection ) + { + if ( ! strncmp( reinterpret_cast< char * >( curSection->Name ), name, 8 ) ) + break; + } + + if ( ! numberOfSections ) + return NULL; + + return curSection; +} + +NativeImageDumper::EnumMnemonics NativeImageDumper::s_ModulePersistedFlags[] = +{ +#define MPF_ENTRY(f) NativeImageDumper::EnumMnemonics(Module::f, W(#f)) + MPF_ENTRY(COMPUTED_GLOBAL_CLASS), + + MPF_ENTRY(COMPUTED_STRING_INTERNING), + MPF_ENTRY(NO_STRING_INTERNING), + + MPF_ENTRY(COMPUTED_WRAP_EXCEPTIONS), + MPF_ENTRY(WRAP_EXCEPTIONS), + + MPF_ENTRY(COMPUTED_RELIABILITY_CONTRACT), + + MPF_ENTRY(COLLECTIBLE_MODULE), + MPF_ENTRY(COMPUTED_IS_PRE_V4_ASSEMBLY), + MPF_ENTRY(IS_PRE_V4_ASSEMBLY), + MPF_ENTRY(DEFAULT_DLL_IMPORT_SEARCH_PATHS_IS_CACHED), + MPF_ENTRY(DEFAULT_DLL_IMPORT_SEARCH_PATHS_STATUS), + + MPF_ENTRY(NEUTRAL_RESOURCES_LANGUAGE_IS_CACHED), + MPF_ENTRY(COMPUTED_METHODDEF_TO_PROPERTYINFO_MAP), + MPF_ENTRY(LOW_LEVEL_SYSTEM_ASSEMBLY_BY_NAME), +#undef MPF_ENTRY +}; + +//VirtualSectionTypes. +#define TEXTIFY(x) W(#x) +static const NativeImageDumper::EnumMnemonics s_virtualSectionFlags [] = +{ + +#define CORCOMPILE_SECTION_IBCTYPE(ibcType, _value) NativeImageDumper::EnumMnemonics(_value, TEXTIFY(ibcType)), + CORCOMPILE_SECTION_IBCTYPES() +#undef CORCOMPILE_SECTION_IBCTYPE + +#define CORCOMPILE_SECTION_RANGE_TYPE(rangeType, _value) NativeImageDumper::EnumMnemonics(_value, TEXTIFY(rangeType) W("Range")), + CORCOMPILE_SECTION_RANGE_TYPES() +#undef CORCOMPILE_SECTION_RANGE_TYPE +}; +const WCHAR * g_sectionNames[] = +{ + W("SECTION_DUMMY"), // the first section start at 0x1. Make the array 1 based. +#define CORCOMPILE_SECTION_TYPE(section) W("SECTION_") TEXTIFY(section), + CORCOMPILE_SECTION_TYPES() +#undef CORCOMPILE_SECTION + +}; +#undef TEXTIFY + + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif + +const NativeImageDumper::EnumMnemonics s_MSDFlags[] = +{ +#define MSD_ENTRY(f) NativeImageDumper::EnumMnemonics(ModuleSecurityDescriptorFlags_ ## f, W(#f)) + MSD_ENTRY(IsComputed), +#ifdef FEATURE_APTCA + MSD_ENTRY(IsAPTCA), +#endif // FEATURE_APTCA + MSD_ENTRY(IsAllCritical), + MSD_ENTRY(IsAllTransparent), + MSD_ENTRY(IsTreatAsSafe), + MSD_ENTRY(IsOpportunisticallyCritical), + MSD_ENTRY(SkipFullTrustVerification) +#undef MSD_ENTRY +}; + +void NativeImageDumper::DumpModule( PTR_Module module ) +{ + + //the module is the fisrt thing in the .data section. We use this fact for + //the sectionBases down below. +// _ASSERTE(m_decoder.GetDataRva(PTR_TO_TADDR(module)) +// == FindSection(".data")->VirtualAddress ); + + DisplayStartStructure( "module", DPtrToPreferredAddr(module), + sizeof(*module), MODULE ); + PTR_PEFile file = module->m_file; + _ASSERTE(file == NULL); + DisplayWriteFieldPointer( m_file, DPtrToPreferredAddr(file), Module, + MODULE ); + + PTR_MethodDesc dllMain( TO_TADDR(module->m_pDllMain) ); + WriteFieldMethodDesc( m_pDllMain, dllMain, Module, + MODULE ); + + _ASSERTE(module->m_dwTransientFlags == 0U); + DisplayWriteFieldUInt(m_dwTransientFlags, module->m_dwTransientFlags, + Module, MODULE ); + + + + DisplayWriteFieldEnumerated( m_dwPersistedFlags, module->m_dwPersistedFlags, + Module, s_ModulePersistedFlags, W("|"), MODULE ); + + DisplayWriteFieldPointer( m_pAssembly, + DPtrToPreferredAddr(module->m_pAssembly), + Module, MODULE ); + _ASSERTE(module->m_pAssembly == NULL); //never appears in the image + + DisplayWriteFieldUInt( m_moduleRef, module->m_moduleRef, Module, MODULE ); + DisplayWriteFieldInt( m_dwDebuggerJMCProbeCount, + module->m_dwDebuggerJMCProbeCount, Module, MODULE ); + /* REVISIT_TODO Fri 10/14/2005 + * Dump the binder + */ + PTR_MscorlibBinder binder = module->m_pBinder; + if( NULL != binder ) + { + DisplayStartStructureWithOffset( m_pBinder, DPtrToPreferredAddr(binder), + sizeof(*binder), Module, + MODULE ); + + //these four fields don't have anything useful in ngen images. + DisplayWriteFieldPointer( m_classDescriptions, + DPtrToPreferredAddr(binder->m_classDescriptions), + MscorlibBinder, MODULE ); + DisplayWriteFieldPointer( m_methodDescriptions, + DPtrToPreferredAddr(binder->m_methodDescriptions), + MscorlibBinder, MODULE ); + DisplayWriteFieldPointer( m_fieldDescriptions, + DPtrToPreferredAddr(binder->m_fieldDescriptions), + MscorlibBinder, MODULE ); + DisplayWriteFieldPointer( m_pModule, + DPtrToPreferredAddr(binder->m_pModule), + MscorlibBinder, MODULE ); + + DisplayWriteFieldInt( m_cClasses, binder->m_cClasses, MscorlibBinder, + MODULE ); + DisplayWriteFieldAddress( m_pClasses, + DPtrToPreferredAddr(binder->m_pClasses), + sizeof(*binder->m_pClasses) + * binder->m_cClasses, + MscorlibBinder, MODULE ); + DisplayWriteFieldInt( m_cFields, binder->m_cFields, MscorlibBinder, + MODULE ); + DisplayWriteFieldAddress( m_pFields, + DPtrToPreferredAddr(binder->m_pFields), + sizeof(*binder->m_pFields) + * binder->m_cFields, + MscorlibBinder, MODULE ); + DisplayWriteFieldInt( m_cMethods, binder->m_cMethods, MscorlibBinder, + MODULE ); + DisplayWriteFieldAddress( m_pMethods, + DPtrToPreferredAddr(binder->m_pMethods), + sizeof(*binder->m_pMethods) + * binder->m_cMethods, + MscorlibBinder, MODULE ); + + DisplayEndStructure( MODULE ); //m_pBinder + } + else + { + DisplayWriteFieldPointer( m_pBinder, NULL, Module, MODULE ); + } + _ASSERTE(module->m_activeDependencies.GetCount() == 0); + + + /* REVISIT_TODO Tue 10/25/2005 + * unconditional dependencies, activations, class dependencies, thunktable + */ + + + //round trip the LookupMap back through the DAC so that we don't have an + //interior host pointer. + PTR_LookupMapBase lookupMap( PTR_TO_TADDR(module) + + offsetof(Module, m_TypeDefToMethodTableMap) ); + TraverseMap( lookupMap, "m_TypeDefToMethodTableMap", + offsetof(Module, m_TypeDefToMethodTableMap), + fieldsize(Module, m_TypeDefToMethodTableMap), + &NativeImageDumper::IterateTypeDefToMTCallback ); + + lookupMap = PTR_LookupMapBase( PTR_TO_TADDR(module) + + offsetof(Module, m_TypeRefToMethodTableMap) ); + + TraverseMap( lookupMap, "m_TypeRefToMethodTableMap", + offsetof(Module, m_TypeRefToMethodTableMap), + fieldsize(Module, m_TypeRefToMethodTableMap), + &NativeImageDumper::IterateTypeRefToMTCallback ); + + lookupMap = PTR_LookupMapBase( PTR_TO_TADDR(module) + + offsetof(Module, m_MethodDefToDescMap) ); + TraverseMap( lookupMap, "m_MethodDefToDescMap", + offsetof(Module, m_MethodDefToDescMap), + fieldsize(Module, m_MethodDefToDescMap), + &NativeImageDumper::IterateMethodDefToMDCallback); + + lookupMap = PTR_LookupMapBase( PTR_TO_TADDR(module) + + offsetof(Module, m_FieldDefToDescMap) ); + TraverseMap( lookupMap, "m_FieldDefToDescMap", + offsetof(Module, m_FieldDefToDescMap), + fieldsize(Module, m_FieldDefToDescMap), + &NativeImageDumper::IterateFieldDefToFDCallback); + + TraverseMemberRefToDescHash(module->m_pMemberRefToDescHashTable, "m_pMemberRefToDescHashTable", + offsetof(Module, m_pMemberRefToDescHashTable), + fieldsize(Module, m_pMemberRefToDescHashTable), + FALSE); + + lookupMap = PTR_LookupMapBase( PTR_TO_TADDR(module) + + offsetof(Module, m_GenericParamToDescMap) ); + + TraverseMap( lookupMap, "m_GenericParamToDescMap", + offsetof(Module, m_GenericParamToDescMap), + fieldsize(Module, m_GenericParamToDescMap), + &NativeImageDumper::IterateGenericParamToDescCallback); + + lookupMap = PTR_LookupMapBase( PTR_TO_TADDR(module) + + offsetof(Module, m_GenericTypeDefToCanonMethodTableMap) ); + + TraverseMap( lookupMap, "m_GenericTypeDefToCanonMethodTableMap", + offsetof(Module, m_GenericTypeDefToCanonMethodTableMap), + fieldsize(Module, m_GenericTypeDefToCanonMethodTableMap), + &NativeImageDumper::IterateTypeDefToMTCallback ); + + lookupMap = PTR_LookupMapBase( PTR_TO_TADDR(module) + + offsetof(Module, m_FileReferencesMap) ); + TraverseMap( lookupMap, "m_FileReferencesMap", + offsetof(Module, m_FileReferencesMap), + fieldsize(Module, m_FileReferencesMap), + &NativeImageDumper::IterateMemberRefToDescCallback); + + lookupMap = PTR_LookupMapBase(PTR_TO_TADDR(module) + + offsetof(Module,m_ManifestModuleReferencesMap)); + + TraverseMap( lookupMap, "m_ManifestModuleReferencesMap", + offsetof(Module, m_ManifestModuleReferencesMap), + fieldsize(Module, m_ManifestModuleReferencesMap), + &NativeImageDumper::IterateManifestModules); + + TraverseClassHash( module->m_pAvailableClasses, "m_pAvailableClasses", + offsetof(Module, m_pAvailableClasses), + fieldsize(Module, m_pAvailableClasses), true ); + + TraverseTypeHash( module->m_pAvailableParamTypes, "m_pAvailableParamTypes", + offsetof(Module, m_pAvailableParamTypes), + fieldsize(Module, m_pAvailableParamTypes) ); + TraverseInstMethodHash( module->m_pInstMethodHashTable, + "m_pInstMethodHashTable", + offsetof(Module, m_pInstMethodHashTable), + fieldsize(Module, m_pInstMethodHashTable), + module ); + TraverseStubMethodHash( module->m_pStubMethodHashTable, + "m_pStubMethodHashTable", + offsetof(Module, m_pStubMethodHashTable), + fieldsize(Module, m_pStubMethodHashTable), + module ); + + IF_OPT(MODULE) + { + TraverseClassHash( module->m_pAvailableClassesCaseIns, + "m_pAvailableClassesCaseIns", + offsetof(Module, m_pAvailableClassesCaseIns), + fieldsize(Module, m_pAvailableClassesCaseIns), + false ); + } + +#ifdef FEATURE_COMINTEROP + TraverseGuidToMethodTableHash( module->m_pGuidToTypeHash, + "m_pGuidToTypeHash", + offsetof(Module, m_pGuidToTypeHash), + fieldsize(Module, m_pGuidToTypeHash), + true); + +#endif // FEATURE_COMINTEROP + + _ASSERTE(module->m_pProfilingBlobTable == NULL); + + DisplayWriteFieldFlag( m_nativeImageProfiling, + module->m_nativeImageProfiling, Module, MODULE ); + + DisplayWriteFieldPointer( m_methodProfileList, + DataPtrToDisplay((TADDR)module->m_methodProfileList), + Module, MODULE ); + _ASSERTE(module->m_methodProfileList == NULL); + + /* REVISIT_TODO Tue 10/04/2005 + * Dump module->m_moduleCtorInfo + */ + PTR_ModuleCtorInfo ctorInfo( PTR_HOST_MEMBER_TADDR(Module, module, + m_ModuleCtorInfo) ); + + DisplayStartStructureWithOffset( m_ModuleCtorInfo, + DPtrToPreferredAddr(ctorInfo), + sizeof(*ctorInfo), + Module, SLIM_MODULE_TBLS ); + DisplayWriteFieldInt( numElements, ctorInfo->numElements, ModuleCtorInfo, + SLIM_MODULE_TBLS ); + DisplayWriteFieldInt( numLastAllocated, ctorInfo->numLastAllocated, + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldInt( numElementsHot, ctorInfo->numElementsHot, + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldAddress( ppMT, DPtrToPreferredAddr(ctorInfo->ppMT), + ctorInfo->numElements * sizeof(MethodTable*), + ModuleCtorInfo, SLIM_MODULE_TBLS ); + /* REVISIT_TODO Tue 03/21/2006 + * is cctorInfoHot and cctorInfoCold actually have anything interesting + * inside of them? + */ + DisplayWriteFieldAddress( cctorInfoHot, + DPtrToPreferredAddr(ctorInfo->cctorInfoHot), + sizeof(*ctorInfo->cctorInfoHot) + * ctorInfo->numElementsHot, + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldAddress( cctorInfoCold, + DPtrToPreferredAddr(ctorInfo->cctorInfoCold), + sizeof(*ctorInfo->cctorInfoCold) + * (ctorInfo->numElements + - ctorInfo->numElementsHot), + ModuleCtorInfo, SLIM_MODULE_TBLS ); + /* XXX Thu 03/23/2006 + * See ModuleCtorInfo::Save for why these are +1. + */ + DisplayWriteFieldAddress( hotHashOffsets, + DPtrToPreferredAddr(ctorInfo->hotHashOffsets), + (ctorInfo->numHotHashes + 1) + * sizeof(*ctorInfo->hotHashOffsets), + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldAddress( coldHashOffsets, + DPtrToPreferredAddr(ctorInfo->coldHashOffsets), + (ctorInfo->numColdHashes + 1) + * sizeof(*ctorInfo->coldHashOffsets), + ModuleCtorInfo, SLIM_MODULE_TBLS ); + + DisplayWriteFieldInt( numHotHashes, ctorInfo->numHotHashes, ModuleCtorInfo, + SLIM_MODULE_TBLS ); + DisplayWriteFieldInt( numColdHashes, ctorInfo->numColdHashes, + ModuleCtorInfo, SLIM_MODULE_TBLS ); + + DisplayWriteFieldAddress( ppHotGCStaticsMTs, + DPtrToPreferredAddr(ctorInfo->ppHotGCStaticsMTs), + ctorInfo->numHotGCStaticsMTs + * sizeof(*ctorInfo->ppHotGCStaticsMTs), + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldAddress( ppColdGCStaticsMTs, + DPtrToPreferredAddr(ctorInfo->ppColdGCStaticsMTs), + ctorInfo->numColdGCStaticsMTs + * sizeof(*ctorInfo->ppColdGCStaticsMTs), + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldInt( numHotGCStaticsMTs, ctorInfo->numHotGCStaticsMTs, + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldInt( numColdGCStaticsMTs, ctorInfo->numColdGCStaticsMTs, + ModuleCtorInfo, SLIM_MODULE_TBLS ); + + DisplayEndStructure( SLIM_MODULE_TBLS ); //m_ModuleCtorInfo + + _ASSERTE(module->m_pNgenStats == NULL); + + DisplayWriteFieldPointer( m_pNgenStats, + DataPtrToDisplay((TADDR)module->m_pNgenStats), + Module, MODULE ); +#if defined(FEATURE_MIXEDMODE) + DisplayWriteFieldPointer( m_pThunkHeap, + DataPtrToDisplay(dac_cast<TADDR>(module->m_pThunkHeap)), + Module, MODULE ); + _ASSERTE(module->m_pThunkHeap == NULL); +#endif + + DisplayWriteFieldAddress(m_propertyNameSet, + DPtrToPreferredAddr(module->m_propertyNameSet), + sizeof(module->m_propertyNameSet[0]) * + module->m_nPropertyNameSet, + Module, MODULE); + + DisplayWriteFieldPointer( m_ModuleID, + DataPtrToDisplay(dac_cast<TADDR>(module->m_ModuleID)), + Module, MODULE ); + _ASSERTE(module->m_ModuleID == NULL); + + /* XXX Tue 04/11/2006 + * Value is either -1 or 0, so no need to rebase. + */ + DisplayWriteFieldPointer( m_pRegularStaticOffsets, + PTR_TO_TADDR(module->m_pRegularStaticOffsets), + Module, MODULE ); + _ASSERTE(module->m_pRegularStaticOffsets == (void*)-1 + || module->m_pRegularStaticOffsets == 0 ); + + DisplayWriteFieldInt( m_dwMaxGCRegularStaticHandles, + module->m_dwMaxGCRegularStaticHandles, Module, MODULE ); + DisplayWriteFieldInt( m_dwRegularStaticsBlockSize, module->m_dwRegularStaticsBlockSize, + Module, MODULE ); + DisplayWriteFieldAddress( m_pDynamicStaticsInfo, + DataPtrToDisplay((TADDR)module->m_pDynamicStaticsInfo), + module->m_maxDynamicEntries + * sizeof(*(module->m_pDynamicStaticsInfo)), + Module, MODULE ); + + DisplayWriteFieldInt( m_cDynamicEntries, + (int)module->m_cDynamicEntries, Module, MODULE ); + + CoverageRead(TO_TADDR(module->m_pDynamicStaticsInfo), + (int)(module->m_maxDynamicEntries + * sizeof(*(module->m_pDynamicStaticsInfo)))); + + DisplayWriteFieldInt( m_dwReliabilityContract, + module->m_dwReliabilityContract, Module, MODULE ); + + DisplayWriteFieldPointer( m_pCerPrepInfo, + DataPtrToDisplay((TADDR)module->m_pCerPrepInfo), + Module, MODULE ); + DisplayWriteFieldPointer( m_pCerCrst, DataPtrToDisplay((TADDR)module->m_pCerCrst), + Module, MODULE ); + _ASSERTE(module->m_pCerPrepInfo == NULL && module->m_pCerCrst == NULL); + + IF_OPT_OR(MODULE, SLIM_MODULE_TBLS) + { + PTR_CerNgenRootTable table( TO_TADDR(module->m_pCerNgenRootTable) ); + DumpNgenRootTable( table, "m_pCerNgenRootTable", + offsetof(Module, m_pCerNgenRootTable), + fieldsize(Module, m_pCerNgenRootTable) ); + } + + + _ASSERTE(module->m_debuggerSpecificData.m_pDynamicILCrst == NULL); + DisplayWriteFieldPointer( m_debuggerSpecificData.m_pDynamicILCrst, + DataPtrToDisplay(dac_cast<TADDR>(module->m_debuggerSpecificData.m_pDynamicILCrst)), + Module, MODULE ); + + + _ASSERTE(module->m_pModuleSecurityDescriptor); + PTR_ModuleSecurityDescriptor msd(TO_TADDR(module->m_pModuleSecurityDescriptor)); + DisplayStartStructureWithOffset( m_pModuleSecurityDescriptor, + DPtrToPreferredAddr(msd), sizeof(*msd), + Module, MODULE ); + DisplayWriteElementEnumerated("Flags", msd->GetRawFlags(), s_MSDFlags, W(", "), MODULE ); + + _ASSERTE(msd->GetModule() == module); + DisplayEndStructure(MODULE); //ModuleSecurityDescriptor + + /* REVISIT_TODO Wed 09/21/2005 + * Get me in the debugger and look at the activations and module/class + * dependencies. + * As well as the thunks. + */ + + /* REVISIT_TODO Wed 09/21/2005 + * Dump the following + */ + //file + //assembly + + DisplayWriteFieldInt( m_DefaultDllImportSearchPathsAttributeValue, + module->m_DefaultDllImportSearchPathsAttributeValue, Module, MODULE ); + + + DisplayEndStructure(MODULE); //Module +} +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +bool NativeImageDumper::isPrecode(TADDR maybePrecode) +{ + PTR_Module module = (TADDR)m_decoder.GetPersistedModuleImage(); + + return !!module->IsZappedPrecode(maybePrecode); +} + +void NativeImageDumper::DumpNgenRootTable( PTR_CerNgenRootTable table, + const char * name, unsigned offset, + unsigned fieldSize ) +{ + if( table == NULL ) + { + IF_OPT(MODULE) + { + m_display->WriteFieldPointer( name, (unsigned)DPtrToPreferredAddr(table), + offset, fieldSize ); + } + return; + } + IF_OPT(MODULE) + { + m_display->StartStructureWithOffset( name, offset, fieldSize, + DPtrToPreferredAddr(table), + sizeof(*table) ); + } + + DisplayWriteFieldPointer( m_cRoots, table->GetRootCount(), + CerNgenRootTable, MODULE ); + DisplayWriteFieldAddress( m_pRestoreBitmap, + DataPtrToDisplay((TADDR)table->GetRestoreBitmap()), + table->SizeOfRestoreBitmap(), + CerNgenRootTable, MODULE ); + DisplayWriteFieldInt( m_cSlots, table->m_cSlots, CerNgenRootTable, + MODULE ); + DisplayStartArray( "Roots", NULL, SLIM_MODULE_TBLS ); + + PTR_CerRoot roots(TO_TADDR(table->GetRoots())); + for( unsigned i = 0; i < table->GetRootCount(); ++i ) + { + PTR_CerRoot root = roots + i; + DisplayStartStructure( "CerRoot", DPtrToPreferredAddr(root), + sizeof(*root), SLIM_MODULE_TBLS ); + WriteFieldMethodDesc( m_pRootMD, + PTR_MethodDesc(TO_TADDR(root->m_pRootMD)), + CerRoot, SLIM_MODULE_TBLS ); + + DisplayStartArray( "MethodContexts", NULL, SLIM_MODULE_TBLS ); + + PTR_MethodContextElement ctx(TO_TADDR(root->m_pList)); + bool dumpedSentinel = false; + while( !dumpedSentinel ) + { + DisplayStartStructure( "MethodContext", + DPtrToPreferredAddr(ctx), + sizeof(*ctx), SLIM_MODULE_TBLS ); + if( ctx->m_pMethodDesc.IsNull() ) + dumpedSentinel = true; + WriteFieldMethodDesc( m_pMethodDesc, + ctx->m_pMethodDesc.GetValue(), + MethodContextElement, SLIM_MODULE_TBLS ); + + if (!ctx->m_pExactMT.IsNull()) + { + WriteFieldMethodTable( m_pExactMT, + ctx->m_pExactMT.GetValue(), + MethodContextElement, SLIM_MODULE_TBLS ); + } + + DisplayEndStructure( SLIM_MODULE_TBLS ); //MethodContext + ++ctx; + } + + DisplayEndArray( "Total Contexts", SLIM_MODULE_TBLS); //MethodContexts + DisplayEndStructure(SLIM_MODULE_TBLS); //CerRoot + } + + DisplayEndArray( "Total Roots", SLIM_MODULE_TBLS ); //Roots + + /* REVISIT_TODO Wed 10/05/2005 + * m_cSlots seems to be set to something, but the number seems + * completely useless. What is up with that? + */ + + DisplayEndStructure( MODULE ); //CERNgenRootTable +} +void NativeImageDumper::IterateTypeDefToMTCallback( TADDR mtTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + DisplayStartElement( "Entry", MODULE_TABLES ); + + PTR_MethodTable mt(mtTarget); + + DisplayWriteElementUInt( "Token", rid | mdtTypeDef, MODULE_TABLES ); + /* REVISIT_TODO Fri 10/21/2005 + * Can I use WriteElementMethodTable here? + */ + DisplayWriteElementPointer( "MethodTable", DPtrToPreferredAddr(mt), + MODULE_TABLES ); + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + /* REVISIT_TODO Fri 09/30/2005 + * This handles the extra entries in the type table that shouldn't be there. + */ + if( rid == 0 || ((rid != 1) && (mtTarget == NULL)) ) + { + DisplayWriteElementString( "Name", "mdTypeDefNil", MODULE_TABLES ); + } + else + { + TempBuffer buf; + MethodTableToString( mt, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); + + if( isInRange(PTR_TO_TADDR(mt)) ) + { + m_discoveredMTs.AppendEx(mt); + PTR_EEClass clazz = GetClassFromMT(mt); + if( isInRange(PTR_TO_TADDR(clazz)) ) + m_discoveredClasses.AppendEx(mt); + } +} + +void NativeImageDumper::IterateTypeRefToMTCallback( TADDR mtTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + DisplayStartElement( "Entry", MODULE_TABLES ); + + mtTarget = ((FixupPointer<TADDR>&)mtTarget).GetValue(); + + PTR_MethodTable mt(mtTarget); + +#if 0 + RecordTypeRef(rid | mdtTypeRef, mt); +#endif + + DisplayWriteElementUInt( "Token", rid | mdtTypeRef, MODULE_TABLES ); + + DisplayWriteElementPointer( "MethodTable", DPtrToPreferredAddr(mt), + MODULE_TABLES ); + + if( rid == 0 ) + { + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + DisplayWriteElementString( "Name", "mdtTypeRefNil", MODULE_TABLES ); + } + else if( mt == NULL ) + { + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + IF_OPT(MODULE_TABLES) + WriteElementMDToken( "Name", mdtTypeRef | rid ); + } + else if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ) + { + RVA rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(mt)); + // + // This guy writes two things FixupTargetValue and FixupTargetName + // + WriteElementsFixupBlob( NULL,PTR_TO_TADDR(mt)); + } + else + { + TempBuffer buf; + MethodTableToString( mt, buf ); + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); + if( isInRange(mtTarget) ) + { + m_discoveredMTs.AppendEx(mt); + PTR_EEClass clazz = GetClassFromMT(mt); + if( isInRange(PTR_TO_TADDR(clazz)) ) + m_discoveredClasses.AppendEx(mt); + } +} + +void NativeImageDumper::IterateMethodDefToMDCallback( TADDR mdTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + DisplayStartElement( "Entry", MODULE_TABLES ); + + PTR_MethodDesc md(mdTarget); + + DisplayWriteElementUInt( "Token", rid | mdtMethodDef, MODULE_TABLES ); + + DisplayWriteElementPointer( "MethodDesc", DPtrToPreferredAddr(md), + MODULE_TABLES ); + + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + if( rid == 0 ) + { + DisplayWriteElementString( "Name", "mdtMethodDefNil", MODULE_TABLES ); + } + else + { + TempBuffer buf; + MethodDescToString( md, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); + //m_discoveredMDs.AppendEx(md); +} + +void NativeImageDumper::IterateFieldDefToFDCallback( TADDR fdTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + PTR_FieldDesc fd(fdTarget); + DisplayStartElement( "Entry", MODULE_TABLES ); + + + DisplayWriteElementUInt( "Token", rid | mdtFieldDef, MODULE_TABLES ); + + DisplayWriteElementPointer( "FieldDef", DPtrToPreferredAddr(fd), + MODULE_TABLES ); + + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + if( rid == 0 ) + { + DisplayWriteElementString( "Name", "mdtFieldDefNil", MODULE_TABLES ); + } + else + { + TempBuffer buf; + FieldDescToString( fd, mdtFieldDef | rid, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); + /* XXX Mon 10/17/2005 + * All FieldDescs are reachable from the EEClasses + */ + //m_discoveredFDs.AppendEx(PTR_FieldDesc(fdTarget)); +} + +void NativeImageDumper::IterateMemberRefToDescCallback( TADDR mrTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + DisplayStartElement( "Entry", MODULE_TABLES ); + + + bool isFieldRef = (flags & IS_FIELD_MEMBER_REF) != 0; + mdToken targetToken = mdtMemberRef | rid; + mrTarget = ((FixupPointer<TADDR>&)mrTarget).GetValue(); + DisplayWriteElementUInt( "Token", targetToken, MODULE_TABLES ); + DisplayWriteElementPointer( isFieldRef ? "FieldDesc" : "MethodDesc", + DataPtrToDisplay(mrTarget), MODULE_TABLES ); + + TempBuffer buf; + if( rid == 0 ) + { + buf.Append( W("mdtMemberDefNil") ); + } + else if( CORCOMPILE_IS_POINTER_TAGGED(mrTarget) ) + { + WriteElementsFixupBlob( NULL, mrTarget ); + } + else if( isFieldRef ) + { + FieldDescToString( PTR_FieldDesc(mrTarget), buf ); + } + else + { + MethodDescToString( PTR_MethodDesc(mrTarget), buf ); + } + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement(MODULE_TABLES); + //m_discoveredMTs.AppendEx(mt); +} + +void NativeImageDumper::IterateGenericParamToDescCallback( TADDR tdTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + PTR_TypeDesc td(tdTarget); + DisplayStartElement( "Entry", MODULE_TABLES ); + + + DisplayWriteElementUInt( "Token", rid | mdtGenericParam, MODULE_TABLES ); + + DisplayWriteElementPointer( "GenericParam", DPtrToPreferredAddr(td), + MODULE_TABLES ); + + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + if( rid == 0 || td == NULL ) + { + DisplayWriteElementString( "Name", "mdtGenericParamNil", MODULE_TABLES ); + } + else + { + TempBuffer buf; + TypeDescToString( td, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); +} + +#if 0 +void NativeImageDumper::IterateFileReferencesCallback(TADDR moduleTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid) +{ + DisplayStartElement( "Entry", MODULE_TABLES ); + + PTR_Module module(moduleTarget); + + DisplayWriteElementUInt( "Token", rid | mdtFile, MODULE_TABLES ); + + DisplayWriteElementPointer( "Module", DPtrToPreferredAddr(module), + MODULE_TABLES ); + + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + if( rid == 0 || (module == NULL) ) + { + DisplayWriteElementString( "Name", "mdtFileNil", MODULE_TABLES ); + } + else + { + TempBuffer buf; + AppendTokenName(mdtFile | rid, buf); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); + //m_discoveredFDs.AppendEx(mt); +} +#endif + +void NativeImageDumper::IterateManifestModules( TADDR moduleTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + DisplayStartElement( "Entry", MODULE_TABLES ); + + moduleTarget = ((FixupPointer<TADDR>&)moduleTarget).GetValue(); + + PTR_Module module(moduleTarget); + + DisplayWriteElementUInt( "Token", rid | mdtAssemblyRef, MODULE_TABLES ); + + DisplayWriteElementPointer( "Module", DPtrToPreferredAddr(module), + MODULE_TABLES ); + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + if( rid == 0 || (module == NULL) ) + { + DisplayWriteElementString( "Name", "mdtAssemblyRefNil", MODULE_TABLES ); + } + else + { + TempBuffer buf; + AppendTokenName(mdtAssemblyRef | rid, buf, m_import); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); + //m_discoveredFDs.AppendEx(mt); +} + +void NativeImageDumper::TraverseMap(PTR_LookupMapBase map, const char * name, + unsigned offset, unsigned fieldSize, + void(NativeImageDumper::*cb)(TADDR, + TADDR, + PTR_LookupMapBase, + DWORD)) +{ + if( map == NULL ) + { + IF_OPT(MODULE) + m_display->WriteFieldPointer( name, offset, fieldSize, NULL ); + return; + } + DisplayStartVStructure(name, MODULE); + + DisplayStartArray( "Tables", W("%s"), MODULE ); + PTR_LookupMapBase current = map; + do + { + DWORD cbTable = map->MapIsCompressed() ? map->cbTable : map->dwCount * sizeof(*map->pTable); + + IF_OPT(MODULE) + { + DisplayWriteElementAddress( "Table", + DPtrToPreferredAddr(map->pTable), + cbTable, + MODULE); + } + + CoverageRead( PTR_TO_TADDR(map->pTable), cbTable ); + _ASSERTE(current == map || current->hotItemList == NULL); + current = current->pNext; + }while( current != NULL ); + + DisplayEndArray( "Total Tables", MODULE ); //Tables + + DisplayWriteFieldAddress( hotItemList, + DPtrToPreferredAddr(map->hotItemList), + map->dwNumHotItems * sizeof(*map->hotItemList), + LookupMapBase, MODULE ); + + DisplayStartArray( "Map", W("[%s]: %s %s%s %s %s %s"), MODULE_TABLES ); + + IF_OPT_OR3(MODULE_TABLES, EECLASSES, METHODTABLES) + { + LookupMap<TADDR>::Iterator iter(dac_cast<DPTR(LookupMap<TADDR>)>(map)); + DWORD rid = 0; + while(iter.Next()) + { + TADDR flags = 0; + TADDR element = iter.GetElementAndFlags(&flags); + (this->*cb)( element, flags, map, rid ); + rid++; + } + + } + CoverageRead( PTR_TO_TADDR(map->hotItemList), + map->dwNumHotItems * sizeof(*map->hotItemList) ); + DisplayEndArray( "Total" , MODULE_TABLES );//Map + + DisplayEndVStructure(MODULE); //name +} + +// Templated method containing the core code necessary to traverse hash tables based on NgenHash (see +// vm\NgenHash.h). +template<typename HASH_CLASS, typename HASH_ENTRY_CLASS> +void NativeImageDumper::TraverseNgenHash(DPTR(HASH_CLASS) pTable, + const char * name, + unsigned offset, + unsigned fieldSize, + bool saveClasses, + void (NativeImageDumper::*DisplayEntryFunction)(void *, DPTR(HASH_ENTRY_CLASS), bool), + void *pContext) +{ + if (pTable == NULL) + { + IF_OPT(MODULE) + m_display->WriteFieldPointer(name, offset, fieldSize, NULL); + return; + } + IF_OPT(MODULE) + { + m_display->StartStructureWithOffset(name, offset, fieldSize, + DPtrToPreferredAddr(pTable), + sizeof(HASH_CLASS)); + } + + DisplayWriteFieldPointer(m_pModule, + DPtrToPreferredAddr(pTable->m_pModule), + HASH_CLASS, MODULE); + + // Dump warm (volatile) entries. + DisplayWriteFieldUInt(m_cWarmEntries, pTable->m_cWarmEntries, HASH_CLASS, MODULE); + DisplayWriteFieldUInt(m_cWarmBuckets, pTable->m_cWarmBuckets, HASH_CLASS, MODULE); + DisplayWriteFieldAddress(m_pWarmBuckets, + DPtrToPreferredAddr(pTable->m_pWarmBuckets), + sizeof(HASH_ENTRY_CLASS*) * pTable->m_cWarmBuckets, + HASH_CLASS, MODULE); + + // Dump hot (persisted) entries. + DPTR(typename HASH_CLASS::PersistedEntries) pHotEntries(PTR_HOST_MEMBER_TADDR(HASH_CLASS, pTable, m_sHotEntries)); + DisplayStartStructureWithOffset(m_sHotEntries, DPtrToPreferredAddr(pHotEntries), + sizeof(typename HASH_CLASS::PersistedEntries), + HASH_CLASS, MODULE); + TraverseNgenPersistedEntries<HASH_CLASS, HASH_ENTRY_CLASS>(pTable, pHotEntries, saveClasses, DisplayEntryFunction, pContext); + DisplayEndStructure(MODULE); // Hot entries + + // Dump cold (persisted) entries. + DPTR(typename HASH_CLASS::PersistedEntries) pColdEntries(PTR_HOST_MEMBER_TADDR(HASH_CLASS, pTable, m_sColdEntries)); + DisplayStartStructureWithOffset(m_sColdEntries, DPtrToPreferredAddr(pColdEntries), + sizeof(typename HASH_CLASS::PersistedEntries), + HASH_CLASS, MODULE); + TraverseNgenPersistedEntries<HASH_CLASS, HASH_ENTRY_CLASS>(pTable, pColdEntries, saveClasses, DisplayEntryFunction, pContext); + DisplayEndStructure(MODULE); // Cold entries + + DisplayEndStructure(MODULE); // pTable +} + +// Helper used by TraverseNgenHash above to traverse an ngen persisted section of a table (separated out here +// because NgenHash-based tables can have two such sections, one for hot and one for cold entries). +template<typename HASH_CLASS, typename HASH_ENTRY_CLASS> +void NativeImageDumper::TraverseNgenPersistedEntries(DPTR(HASH_CLASS) pTable, + DPTR(typename HASH_CLASS::PersistedEntries) pEntries, + bool saveClasses, + void (NativeImageDumper::*DisplayEntryFunction)(void *, DPTR(HASH_ENTRY_CLASS), bool), + void *pContext) +{ + // Display top-level fields. + DisplayWriteFieldUInt(m_cEntries, pEntries->m_cEntries, typename HASH_CLASS::PersistedEntries, MODULE); + DisplayWriteFieldUInt(m_cBuckets, pEntries->m_cBuckets, typename HASH_CLASS::PersistedEntries, MODULE); + DisplayWriteFieldAddress(m_pBuckets, + DPtrToPreferredAddr(pEntries->m_pBuckets), + pEntries->m_cBuckets ? pEntries->m_pBuckets->GetSize(pEntries->m_cBuckets) : 0, + typename HASH_CLASS::PersistedEntries, MODULE); + DisplayWriteFieldAddress(m_pEntries, + DPtrToPreferredAddr(pEntries->m_pEntries), + sizeof(typename HASH_CLASS::PersistedEntry) * pEntries->m_cEntries, + typename HASH_CLASS::PersistedEntries, MODULE); + + // Display entries (or maybe just the classes referenced by those entries). + DisplayStartArray("Entries", NULL, SLIM_MODULE_TBLS); + + // Enumerate bucket list. + for (DWORD i = 0; i < pEntries->m_cBuckets; ++i) + { + // Get index of the first entry and the count of entries in the bucket. + DWORD dwEntryId, cEntries; + pEntries->m_pBuckets->GetBucket(i, &dwEntryId, &cEntries); + + // Loop over entries. + while (cEntries && (CHECK_OPT(SLIM_MODULE_TBLS) + || CHECK_OPT(EECLASSES) + || CHECK_OPT(METHODTABLES))) + { + // Lookup entry in the array via the index we have. + typename HASH_CLASS::PTR_PersistedEntry pEntry(PTR_TO_TADDR(pEntries->m_pEntries) + + (dwEntryId * sizeof(typename HASH_CLASS::PersistedEntry))); + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("PersistedEntry", + DPtrToPreferredAddr(pEntry), + sizeof(typename HASH_CLASS::PersistedEntry), SLIM_MODULE_TBLS); + } + + // Display entry via a member function specific to the type of hash table we're traversing. Each + // sub-class of NgenHash hash its own entry structure that is embedded NgenHash's entry. The + // helper function expects a pointer to this inner entry. + DPTR(HASH_ENTRY_CLASS) pInnerEntry(PTR_TO_MEMBER_TADDR(typename HASH_CLASS::PersistedEntry, pEntry, m_sValue)); + (this->*DisplayEntryFunction)(pContext, pInnerEntry, saveClasses); + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayWriteFieldUInt(m_iHashValue, pEntry->m_iHashValue, + typename HASH_CLASS::PersistedEntry, SLIM_MODULE_TBLS); + + DisplayEndStructure(SLIM_MODULE_TBLS); // Entry + } + + dwEntryId++; + cEntries--; + } + } + + DisplayEndArray("Total Entries", SLIM_MODULE_TBLS); // Entry array +} + +void NativeImageDumper::TraverseClassHashEntry(void *pContext, PTR_EEClassHashEntry pEntry, bool saveClasses) +{ + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("EEClassHashEntry", + DPtrToPreferredAddr(pEntry), + sizeof(EEClassHashEntry), SLIM_MODULE_TBLS); + } + + size_t datum = size_t(PTR_TO_TADDR(pEntry->GetData())); + + if (datum & EECLASSHASH_TYPEHANDLE_DISCR) + { + IF_OPT(SLIM_MODULE_TBLS) + { + /* REVISIT_TODO Tue 10/25/2005 + * Raw data with annotation? + */ + mdTypeDef tk; + tk = EEClassHashTable::UncompressModuleAndClassDef(pEntry->GetData()); + DoWriteFieldMDToken("Token", + offsetof(EEClassHashEntry, m_Data), + fieldsize(EEClassHashEntry, m_Data), + tk); + } + } + else + { + PTR_MethodTable pMT(TO_TADDR(datum)); + IF_OPT(SLIM_MODULE_TBLS) + { + DoWriteFieldMethodTable("MethodTable", + offsetof(EEClassHashEntry, m_Data), + fieldsize(EEClassHashEntry, m_Data), + pMT); + } + + if (saveClasses) + { + // These are MethodTables. Get back to the EEClass from there. + if (isInRange(PTR_TO_TADDR(pMT))) + m_discoveredMTs.AppendEx(pMT); + if (pMT != NULL) + { + PTR_EEClass pClass = GetClassFromMT(pMT); + if (isInRange(PTR_TO_TADDR(pClass))) + m_discoveredClasses.AppendEx(pMT); + } + } + } + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayWriteFieldPointer(m_pEncloser, + DPtrToPreferredAddr(pEntry->GetEncloser()), + EEClassHashEntry, SLIM_MODULE_TBLS); + DisplayEndStructure(SLIM_MODULE_TBLS); + } +} + +void NativeImageDumper::TraverseClassHash(PTR_EEClassHashTable pTable, + const char * name, + unsigned offset, + unsigned fieldSize, + bool saveClasses) +{ + TraverseNgenHash<EEClassHashTable, EEClassHashEntry>(pTable, + name, + offset, + fieldSize, + saveClasses, + &NativeImageDumper::TraverseClassHashEntry, + NULL); +} + +#ifdef FEATURE_COMINTEROP + +void NativeImageDumper::TraverseGuidToMethodTableEntry(void *pContext, PTR_GuidToMethodTableEntry pEntry, bool saveClasses) +{ + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("GuidToMethodTableEntry", + DPtrToPreferredAddr(pEntry), + sizeof(GuidToMethodTableEntry), SLIM_MODULE_TBLS); + } + + WriteFieldMethodTable(m_pMT, pEntry->m_pMT, GuidToMethodTableEntry, ALWAYS); + + TempBuffer buf; + GuidToString( *(pEntry->m_Guid), buf ); + DisplayWriteFieldStringW( m_Guid, (const WCHAR *)buf, GuidToMethodTableEntry, ALWAYS ); + + DisplayEndStructure( SLIM_MODULE_TBLS ); +} + +void NativeImageDumper::TraverseGuidToMethodTableHash(PTR_GuidToMethodTableHashTable pTable, + const char * name, + unsigned offset, + unsigned fieldSize, + bool saveClasses) +{ + TraverseNgenHash<GuidToMethodTableHashTable, GuidToMethodTableEntry>(pTable, + name, + offset, + fieldSize, + saveClasses, + &NativeImageDumper::TraverseGuidToMethodTableEntry, + NULL); +} + +#endif // FEATURE_COMINTEROP + +void NativeImageDumper::TraverseMemberRefToDescHashEntry(void *pContext, PTR_MemberRefToDescHashEntry pEntry, bool saveClasses) +{ + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("MemberRefToDescHashEntry", + DPtrToPreferredAddr(pEntry), + sizeof(MemberRefToDescHashEntry), SLIM_MODULE_TBLS); + } + + if(pEntry->m_value & IS_FIELD_MEMBER_REF) + WriteFieldFieldDesc(m_value, dac_cast<PTR_FieldDesc>(pEntry->m_value & (~MEMBER_REF_MAP_ALL_FLAGS)), MemberRefToDescHashEntry, MODULE_TABLES); + else + WriteFieldMethodDesc(m_value, dac_cast<PTR_MethodDesc>(pEntry->m_value), MemberRefToDescHashEntry, MODULE_TABLES); + + DisplayEndStructure( SLIM_MODULE_TBLS ); +} + +void NativeImageDumper::TraverseMemberRefToDescHash(PTR_MemberRefToDescHashTable pTable, + const char * name, + unsigned offset, + unsigned fieldSize, + bool saveClasses) +{ + TraverseNgenHash<MemberRefToDescHashTable, MemberRefToDescHashEntry>(pTable, + name, + offset, + fieldSize, + saveClasses, + &NativeImageDumper::TraverseMemberRefToDescHashEntry, + NULL); +} + + +void NativeImageDumper::TraverseTypeHashEntry(void *pContext, PTR_EETypeHashEntry pEntry, bool saveClasses) +{ + TypeHandle th = pEntry->GetTypeHandle(); + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("EETypeHashEntry", + DPtrToPreferredAddr(pEntry), + sizeof(EETypeHashEntry), SLIM_MODULE_TBLS); + + DoWriteFieldTypeHandle("TypeHandle", + offsetof(EETypeHashEntry, m_data), + fieldsize(EETypeHashEntry, m_data), + th); + } + + if (!CORCOMPILE_IS_POINTER_TAGGED(th.AsTAddr()) && th.IsTypeDesc()) + { + PTR_TypeDesc td(th.AsTypeDesc()); + if (isInRange(PTR_TO_TADDR(td))) + m_discoveredTypeDescs.AppendEx(td); + if (td->HasTypeParam()) + { + PTR_ParamTypeDesc ptd(td); + + /* REVISIT_TODO Thu 12/15/2005 + * Check OwnsTemplateMethodTable. However, this asserts in + * this special completely unrestored and messed up state + * (also, it chases through MT->GetClass()). There isn't + * all that much harm here (bloats m_discoveredMTs though, + * but not by a huge amount. + */ + PTR_MethodTable mt(ptd->m_TemplateMT.GetValue()); + if (isInRange(PTR_TO_TADDR(mt))) + { + m_discoveredMTs.AppendEx(mt); + if (mt->IsClassPointerValid()) + { + PTR_EEClass pClass = mt->GetClass(); + if (isInRange(PTR_TO_TADDR(pClass))) + m_discoveredClasses.AppendEx(mt); + } + } + } + } + else + { + PTR_MethodTable mt(th.AsTAddr()); + + if (isInRange( PTR_TO_TADDR(mt))) + m_discoveredMTs.AppendEx(mt); + //don't use GetClassFromMT here. mt->m_pEEClass might be a + //fixup. In that case, just skip it. + if (mt->IsClassPointerValid()) + { + PTR_EEClass pClass = mt->GetClass(); + if (isInRange(PTR_TO_TADDR(pClass))) + m_discoveredClasses.AppendEx(mt); + } + } + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayEndStructure(SLIM_MODULE_TBLS); + } +} + +void NativeImageDumper::TraverseTypeHash(PTR_EETypeHashTable pTable, + const char * name, + unsigned offset, + unsigned fieldSize) +{ + TraverseNgenHash<EETypeHashTable, EETypeHashEntry>(pTable, + name, + offset, + fieldSize, + true, + &NativeImageDumper::TraverseTypeHashEntry, + NULL); +} + +void NativeImageDumper::TraverseInstMethodHashEntry(void *pContext, PTR_InstMethodHashEntry pEntry, bool saveClasses) +{ + PTR_Module pModule((TADDR)pContext); + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("InstMethodHashEntry", + DPtrToPreferredAddr(pEntry), + sizeof(InstMethodHashEntry), SLIM_MODULE_TBLS); + } + + IF_OPT_OR(SLIM_MODULE_TBLS, METHODDESCS) + { + IF_OPT(METHODDESCS) + { + PTR_MethodDesc md = pEntry->GetMethod(); + _ASSERTE(md != NULL); + + //if we want methoddescs, write the data field as a + //structure with the whole contents of the method desc. + m_display->StartVStructureWithOffset("data", offsetof(InstMethodHashEntry, data), + sizeof(pEntry->data)); + DumpMethodDesc(md, pModule); + DisplayEndVStructure(ALWAYS); //data + } + else + { + PTR_MethodDesc md = pEntry->GetMethod(); + WriteFieldMethodDesc(data, md, + InstMethodHashEntry, ALWAYS); + } + } + else + CoverageRead(PTR_TO_TADDR(pEntry), sizeof(*pEntry)); + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayEndStructure(SLIM_MODULE_TBLS); + } +} + +void NativeImageDumper::TraverseStubMethodHashEntry(void *pContext, PTR_StubMethodHashEntry pEntry, bool saveClasses) +{ + PTR_Module pModule((TADDR)pContext); + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("StubMethodHashEntry", + DPtrToPreferredAddr(pEntry), + sizeof(StubMethodHashEntry), SLIM_MODULE_TBLS); + } + + IF_OPT_OR(SLIM_MODULE_TBLS, METHODDESCS) + { + PTR_MethodDesc md = pEntry->GetMethod(); + _ASSERTE(md != NULL); + + PTR_MethodDesc stub = pEntry->GetStubMethod(); + _ASSERTE(stub != NULL); + + IF_OPT(METHODDESCS) + { + //if we want methoddescs, write the data fields as a + //structure with the whole contents of the method desc. + m_display->StartVStructureWithOffset("pMD", offsetof(StubMethodHashEntry, pMD), + sizeof(pEntry->pMD)); + DumpMethodDesc(md, pModule); + DisplayEndVStructure(ALWAYS); //pMD + + m_display->StartVStructureWithOffset("pStubMD", offsetof(StubMethodHashEntry, pStubMD), + sizeof(pEntry->pStubMD)); + DumpMethodDesc(stub, pModule); + DisplayEndVStructure(ALWAYS); //pStubMD + } + else + { + WriteFieldMethodDesc(pMD, md, + StubMethodHashEntry, ALWAYS); + WriteFieldMethodDesc(pStubMD, stub, + StubMethodHashEntry, ALWAYS); + } + } + else + CoverageRead(PTR_TO_TADDR(pEntry), sizeof(*pEntry)); + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayEndStructure(SLIM_MODULE_TBLS); + } +} + +void NativeImageDumper::TraverseInstMethodHash(PTR_InstMethodHashTable pTable, + const char * name, + unsigned fieldOffset, + unsigned fieldSize, + PTR_Module module) +{ + TraverseNgenHash<InstMethodHashTable, InstMethodHashEntry>(pTable, + name, + fieldOffset, + fieldSize, + true, + &NativeImageDumper::TraverseInstMethodHashEntry, + (void*)dac_cast<TADDR>(module)); +} + +void NativeImageDumper::TraverseStubMethodHash(PTR_StubMethodHashTable pTable, + const char * name, + unsigned fieldOffset, + unsigned fieldSize, + PTR_Module module) +{ + TraverseNgenHash<StubMethodHashTable, StubMethodHashEntry>(pTable, + name, + fieldOffset, + fieldSize, + true, + &NativeImageDumper::TraverseStubMethodHashEntry, + (void*)dac_cast<TADDR>(module)); +} + +const NativeImageDumper::Dependency * +NativeImageDumper::GetDependencyForModule( PTR_Module module ) +{ + for( COUNT_T i = 0; i < m_numDependencies; ++i ) + { + if( m_dependencies[i].pModule == module ) + return &m_dependencies[i]; + } + return NULL; +} + +#if 0 +const NativeImageDumper::Import * +NativeImageDumper::GetImportForPointer( TADDR ptr ) +{ + for( int i = 0; i < m_numImports; ++i ) + { + const Import * import = &m_imports[i]; + if( import->dependency->pPreferredBase == NULL ) + continue; + if( import->dependency->pPreferredBase <= ptr + && ((import->dependency->pPreferredBase + + import->dependency->size) > ptr) ) + { + //found the right target + return import; + } + } + return NULL; +} +#endif +const NativeImageDumper::Dependency * +NativeImageDumper::GetDependencyForPointer( TADDR ptr ) +{ + for( COUNT_T i = 0; i < m_numDependencies; ++i ) + { + const Dependency * dependency = &m_dependencies[i]; + if( dependency->pLoadedAddress == NULL ) + continue; + if( dependency->pLoadedAddress <= ptr + && ((dependency->pLoadedAddress + dependency->size) > ptr) ) + { + //found the right target + return dependency; + } + } + return NULL; +} + +void NativeImageDumper::DictionaryToArgString( PTR_Dictionary dictionary, unsigned numArgs, SString& buf ) +{ + //this can be called with numArgs == 0 for value type instantiations. + buf.Append( W("<") ); + + for( unsigned i = 0; i < numArgs; ++i ) + { + if( i > 0 ) + buf.Append( W(",") ); + + TypeHandle th = dictionary->GetInstantiation()[i].GetValue(); + if( CORCOMPILE_IS_POINTER_TAGGED(th.AsTAddr()) ) + { + if (!isSelf(GetDependencyForPointer(PTR_TO_TADDR(dictionary)))) + { + //this is an RVA from another hardbound dependency. We cannot decode it + buf.Append(W("OUT_OF_MODULE_FIXUP")); + } + else + { + RVA rva = CORCOMPILE_UNTAG_TOKEN(th.AsTAddr()); + FixupBlobToString(rva, buf); + } + } + else + { + TypeHandleToString( th, buf ); + } + } + buf.Append( W(">") ); +} + +void NativeImageDumper::MethodTableToString( PTR_MethodTable mt, SString& buf ) +{ + bool hasCompleteExtents = true; + IF_OPT(DISABLE_NAMES) + { + buf.Append( W("Disabled") ); + return; + } + mdToken token = mdTokenNil; + if( mt == NULL ) + buf.Append( W("mdTypeDefNil") ); + else + { + _ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt))); + const Dependency * dependency; + if( !mt->IsClassPointerValid() ) + { + if( isSelf(GetDependencyForPointer(PTR_TO_TADDR(mt))) ) + { + + hasCompleteExtents = false; + RVA rva = CORCOMPILE_UNTAG_TOKEN(mt->GetCanonicalMethodTableFixup()); + PTR_CCOR_SIGNATURE sig = (TADDR) m_decoder.GetRvaData(rva); + + BYTE kind = *sig++; + + if (kind & ENCODE_MODULE_OVERRIDE) + { + /* int moduleIndex = */ DacSigUncompressData(sig); + kind &= ~ENCODE_MODULE_OVERRIDE; + } + + _ASSERTE(kind == ENCODE_TYPE_HANDLE); + CorElementType et = DacSigUncompressElementType(sig); + if( et == ELEMENT_TYPE_GENERICINST ) + { + //generic instances have another element type + et = DacSigUncompressElementType(sig); + } + if (et == ELEMENT_TYPE_VALUETYPE || et == ELEMENT_TYPE_CLASS) + { + token = DacSigUncompressToken(sig); + } + else + { + // Arrays, etc. + token = mdtTypeDef; + } + dependency = GetDependencyForFixup(rva); + } + else + { + //this is an RVA from another hardbound dependency. We cannot decode it + buf.Append(W("OUT_OF_MODULE_FIXUP")); + return; + } + } + else + { + token = mt->GetCl(); + dependency = GetDependencyFromMT(mt); + } + + if( !isSelf(dependency) ) + { + AppendTokenName( dependency->entry->dwAssemblyRef, buf, + m_manifestImport ); + buf.Append(W("!")); + } + + _ASSERTE(dependency->pImport); + if( token == mdtTypeDef ) + buf.Append( W("No Token") ); + else + AppendTokenName( token, buf, dependency->pImport ); + + if( mt->HasPerInstInfo() ) + { + unsigned numDicts; + if( hasCompleteExtents ) + { + numDicts = mt->GetNumDicts(); + _ASSERTE(numDicts == CountDictionariesInClass(token, dependency->pImport)); + } + else + { + numDicts = (DWORD)CountDictionariesInClass(token, dependency->pImport); + } + PTR_Dictionary dictionary( mt->GetPerInstInfo()[numDicts-1] ); + unsigned numArgs = mt->GetNumGenericArgs(); + + DictionaryToArgString( dictionary, numArgs, buf ); + } + } +} + +mdToken NativeImageDumper::ConvertToTypeDef( mdToken typeToken, IMetaDataImport2* (&pImport) ) +{ + _ASSERTE( (TypeFromToken(typeToken) == mdtTypeDef) || (TypeFromToken(typeToken) == mdtTypeRef) + || (TypeFromToken(typeToken) == mdtTypeSpec) ); + if( mdtTypeDef == TypeFromToken(typeToken) ) + return typeToken; + if( mdtTypeRef == TypeFromToken(typeToken) ) + { + //convert the ref to a def. + mdToken scope; + WCHAR trName[MAX_CLASS_NAME]; + ULONG trNameLen; + IfFailThrow(pImport->GetTypeRefProps(typeToken, &scope, trName, _countof(trName), &trNameLen)); + _ASSERTE(trName[trNameLen-1] == 0); + + //scope is now a moduleRef or assemblyRef. Find the IMetaData import for that Ref + /* REVISIT_TODO Fri 10/6/2006 + * How do I handle moduleRefs? + */ + _ASSERTE(TypeFromToken(scope) == mdtAssemblyRef); + ReleaseHolder<IMetaDataAssemblyImport> pAssemblyImport; + IfFailThrow(pImport->QueryInterface(IID_IMetaDataAssemblyImport, + (void **)&pAssemblyImport)); + NativeImageDumper::Dependency * dep = GetDependency(scope, pAssemblyImport); + + pImport = dep->pImport; + + /* REVISIT_TODO Fri 10/6/2006 + * Does this work for inner types? + */ + //now I have the correct MetaData. Find the typeDef + HRESULT hr = pImport->FindTypeDefByName(trName, mdTypeDefNil, &typeToken); + while (hr == CLDB_E_RECORD_NOTFOUND) + { + // No matching TypeDef, try ExportedType + pAssemblyImport = NULL; + IfFailThrow(pImport->QueryInterface(IID_IMetaDataAssemblyImport, + (void **)&pAssemblyImport)); + mdExportedType tkExportedType = mdExportedTypeNil; + IfFailThrow(pAssemblyImport->FindExportedTypeByName(trName, mdExportedTypeNil, &tkExportedType)); + mdToken tkImplementation; + IfFailThrow(pAssemblyImport->GetExportedTypeProps(tkExportedType, NULL, 0, NULL, &tkImplementation, NULL, NULL)); + dep = GetDependency(tkImplementation, pAssemblyImport); + + pImport = dep->pImport; + hr = pImport->FindTypeDefByName(trName, mdTypeDefNil, &typeToken); + } + IfFailThrow(hr); + } + else + { + PCCOR_SIGNATURE pSig; + ULONG cbSig; + IfFailThrow(pImport->GetTypeSpecFromToken(typeToken, &pSig, &cbSig)); + //GENERICINST (CLASS|VALUETYPE) typeDefOrRef + CorElementType et = CorSigUncompressElementType(pSig); + _ASSERTE(et == ELEMENT_TYPE_GENERICINST); + et = CorSigUncompressElementType(pSig); + _ASSERTE((et == ELEMENT_TYPE_CLASS) || (et == ELEMENT_TYPE_VALUETYPE)); + typeToken = CorSigUncompressToken(pSig); + } + + //we just removed one level of indirection. We still might have a ref or spec. + typeToken = ConvertToTypeDef(typeToken, pImport); + _ASSERTE(TypeFromToken(typeToken) == mdtTypeDef); + return typeToken; +} + +SIZE_T NativeImageDumper::CountDictionariesInClass( mdToken typeToken, IMetaDataImport2 * pImport ) +{ + SIZE_T myDicts; //either 0 or 1 + + _ASSERTE((TypeFromToken(typeToken) == mdtTypeDef) || (TypeFromToken(typeToken) == mdtTypeRef) + || (TypeFromToken(typeToken) == mdtTypeSpec)); + + + //for refs and specs, convert to a def. This is a nop for defs. + typeToken = ConvertToTypeDef(typeToken, pImport); + + _ASSERTE(TypeFromToken(typeToken) == mdtTypeDef); + + + //count the number of generic arguments. If there are any, then we have a dictionary. + HCORENUM hEnum = NULL; + mdGenericParam params[2]; + ULONG numParams = 0; + IfFailThrow(pImport->EnumGenericParams(&hEnum, typeToken, params, _countof(params), &numParams)); + myDicts = (numParams > 0) ? 1 : 0; + + pImport->CloseEnum(hEnum); + + //get my parent for the recursive call. + mdToken parent; + IfFailThrow(pImport->GetTypeDefProps(typeToken, NULL, 0, NULL, NULL, &parent)); + return myDicts + (IsNilToken(parent) ? 0 : CountDictionariesInClass(parent, pImport)); +} + +const NativeImageDumper::EnumMnemonics s_Subsystems[] = +{ +#define S_ENTRY(f,v) NativeImageDumper::EnumMnemonics(f, 0, v) + S_ENTRY(IMAGE_SUBSYSTEM_UNKNOWN, W("Unknown")), + S_ENTRY(IMAGE_SUBSYSTEM_NATIVE, W("Native")), + S_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_CUI, W("Windows CUI")), + S_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_GUI, W("Windows GUI")), + S_ENTRY(IMAGE_SUBSYSTEM_OS2_CUI, W("OS/2 CUI")), + S_ENTRY(IMAGE_SUBSYSTEM_POSIX_CUI, W("POSIX CUI")), + S_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_CE_GUI, W("WinCE GUI")), + S_ENTRY(IMAGE_SUBSYSTEM_XBOX, W("XBox")) +#undef S_ENTRY +}; + +const NativeImageDumper::EnumMnemonics s_CorCompileHdrFlags[] = +{ +#define CCHF_ENTRY(f) NativeImageDumper::EnumMnemonics(f, W(#f)) + CCHF_ENTRY(CORCOMPILE_HEADER_HAS_SECURITY_DIRECTORY), + CCHF_ENTRY(CORCOMPILE_HEADER_IS_IBC_OPTIMIZED), + CCHF_ENTRY(CORCOMPILE_HEADER_IS_READY_TO_RUN), +#undef CCHF_ENTRY +}; + +const NativeImageDumper::EnumMnemonics s_CorPEKind[] = +{ +#define CPEK_ENTRY(f) NativeImageDumper::EnumMnemonics(f, W(#f)) + CPEK_ENTRY(peNot), + CPEK_ENTRY(peILonly), + CPEK_ENTRY(pe32BitRequired), + CPEK_ENTRY(pe32Plus), + CPEK_ENTRY(pe32Unmanaged), + CPEK_ENTRY(pe32BitPreferred) +#undef CPEK_ENTRY +}; +const NativeImageDumper::EnumMnemonics s_IFH_Machine[] = +{ +#define IFH_ENTRY(f) NativeImageDumper::EnumMnemonics(f, 0, W(#f)) + IFH_ENTRY(IMAGE_FILE_MACHINE_UNKNOWN), + IFH_ENTRY(IMAGE_FILE_MACHINE_I386), + IFH_ENTRY(IMAGE_FILE_MACHINE_AMD64), + IFH_ENTRY(IMAGE_FILE_MACHINE_ARMNT), +#undef IFH_ENTRY +}; + +const NativeImageDumper::EnumMnemonics s_IFH_Characteristics[] = +{ +#define IFH_ENTRY(f) NativeImageDumper::EnumMnemonics(f, W(#f)) + IFH_ENTRY(IMAGE_FILE_RELOCS_STRIPPED), + IFH_ENTRY(IMAGE_FILE_EXECUTABLE_IMAGE), + IFH_ENTRY(IMAGE_FILE_LINE_NUMS_STRIPPED), + IFH_ENTRY(IMAGE_FILE_LOCAL_SYMS_STRIPPED), + IFH_ENTRY(IMAGE_FILE_AGGRESIVE_WS_TRIM), + IFH_ENTRY(IMAGE_FILE_LARGE_ADDRESS_AWARE), + IFH_ENTRY(IMAGE_FILE_BYTES_REVERSED_LO), + IFH_ENTRY(IMAGE_FILE_32BIT_MACHINE), + IFH_ENTRY(IMAGE_FILE_DEBUG_STRIPPED), + IFH_ENTRY(IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP), + IFH_ENTRY(IMAGE_FILE_NET_RUN_FROM_SWAP), + IFH_ENTRY(IMAGE_FILE_SYSTEM), + IFH_ENTRY(IMAGE_FILE_DLL), + IFH_ENTRY(IMAGE_FILE_UP_SYSTEM_ONLY), + IFH_ENTRY(IMAGE_FILE_BYTES_REVERSED_HI), +#undef IFH_ENTRY +}; + +const NativeImageDumper::EnumMnemonics s_ImportSectionType[] = +{ +#define IST_ENTRY(f) NativeImageDumper::EnumMnemonics(f, 0, W(#f)) + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_UNKNOWN), + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_EXTERNAL_METHOD), + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_STUB_DISPATCH), + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_STRING_HANDLE), + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_TYPE_HANDLE), + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_METHOD_HANDLE), + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_VIRTUAL_METHOD), +#undef IST_ENTRY +}; + +const NativeImageDumper::EnumMnemonics s_ImportSectionFlags[] = +{ +#define IST_FLAGS(f) NativeImageDumper::EnumMnemonics(f, W(#f)) + IST_FLAGS(CORCOMPILE_IMPORT_FLAGS_EAGER), + IST_FLAGS(CORCOMPILE_IMPORT_FLAGS_CODE), + IST_FLAGS(CORCOMPILE_IMPORT_FLAGS_PCODE), +#undef IST_FLAGS +}; + +void NativeImageDumper::DumpNativeHeader() +{ + PTR_CORCOMPILE_HEADER nativeHeader(m_decoder.GetNativeHeader()); + + IF_OPT(NATIVE_INFO) + { + +#define WRITE_NATIVE_FIELD( name ) m_display->WriteFieldAddress(\ + # name, offsetof(CORCOMPILE_HEADER, name), \ + fieldsize(CORCOMPILE_HEADER, name), \ + RvaToDisplay( nativeHeader-> name . VirtualAddress ), \ + nativeHeader-> name . Size ) + + m_display->StartStructure( "CORCOMPILE_HEADER", + DPtrToPreferredAddr(nativeHeader), + sizeof(*nativeHeader) ); + + DisplayWriteFieldUInt( Signature, nativeHeader->Signature, CORCOMPILE_HEADER, ALWAYS ); + DisplayWriteFieldUInt( MajorVersion, nativeHeader->MajorVersion, CORCOMPILE_HEADER, ALWAYS ); + DisplayWriteFieldUInt( MinorVersion, nativeHeader->MinorVersion, CORCOMPILE_HEADER, ALWAYS ); + + WRITE_NATIVE_FIELD(HelperTable); + + WRITE_NATIVE_FIELD(ImportSections); + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = + nativeHeader->ImportSections.VirtualAddress + + PTR_TO_TADDR(m_decoder.GetBase()); + DisplayStartArray( "ImportSections", NULL, ALWAYS ); + for( COUNT_T i = 0; i < nativeHeader->ImportSections.Size + / sizeof(*pImportSections); ++i ) + { + DisplayStartStructure( "CORCOMPILE_IMPORT_SECTION", + DPtrToPreferredAddr(pImportSections + i), + sizeof(pImportSections[i]), ALWAYS ); + DisplayWriteElementAddress( "Section", + RvaToDisplay(pImportSections[i].Section.VirtualAddress), + pImportSections[i].Section.Size, ALWAYS ); + + DisplayWriteFieldEnumerated( Flags, pImportSections[i].Flags, + CORCOMPILE_IMPORT_SECTION, s_ImportSectionFlags, W(", "), ALWAYS ); + DisplayWriteFieldEnumerated( Type, pImportSections[i].Type, + CORCOMPILE_IMPORT_SECTION, s_ImportSectionType, W(""), ALWAYS ); + + DisplayWriteFieldUInt( EntrySize, pImportSections[i].EntrySize, + CORCOMPILE_IMPORT_SECTION, ALWAYS ); + DisplayWriteFieldUInt( Signatures, pImportSections[i].Signatures, + CORCOMPILE_IMPORT_SECTION, ALWAYS ); + DisplayWriteFieldUInt( AuxiliaryData, pImportSections[i].AuxiliaryData, + CORCOMPILE_IMPORT_SECTION, ALWAYS ); + DisplayEndStructure( ALWAYS ); //PTR_CORCOMPILE_IMPORT_SECTION + + } + DisplayEndArray( NULL, ALWAYS ); //delayLoads + + WRITE_NATIVE_FIELD(ImportTable); + DisplayStartArray( "imports", NULL, ALWAYS ); + PTR_CORCOMPILE_IMPORT_TABLE_ENTRY ent( nativeHeader->ImportTable.VirtualAddress + PTR_TO_TADDR(m_decoder.GetBase()) ); + for( COUNT_T i = 0; i < nativeHeader->ImportTable.Size / sizeof(*ent); ++i ) + { + DisplayStartStructure( "CORCOMPILE_IMPORT_TABLE_ENTRY", + DPtrToPreferredAddr(ent + i), + sizeof(ent[i]), ALWAYS ); + DisplayWriteFieldUInt( wAssemblyRid, ent[i].wAssemblyRid, + CORCOMPILE_IMPORT_TABLE_ENTRY, ALWAYS ); + DisplayWriteFieldUInt( wModuleRid, ent[i].wModuleRid, + CORCOMPILE_IMPORT_TABLE_ENTRY, ALWAYS ); + DisplayEndStructure( ALWAYS ); //CORCOMPILE_IMPORT_TABLE_ENTRY + } + DisplayEndArray( NULL, ALWAYS ); //imports + + WRITE_NATIVE_FIELD(VersionInfo); + WRITE_NATIVE_FIELD(DebugMap); + WRITE_NATIVE_FIELD(ModuleImage); + WRITE_NATIVE_FIELD(CodeManagerTable); + WRITE_NATIVE_FIELD(ProfileDataList); + WRITE_NATIVE_FIELD(ManifestMetaData); + + WRITE_NATIVE_FIELD(VirtualSectionsTable); + DisplayStartArray( "VirtualSections", W("%-48s%s"), SLIM_MODULE_TBLS ); + PTR_CORCOMPILE_VIRTUAL_SECTION_INFO sects( nativeHeader->VirtualSectionsTable.VirtualAddress + PTR_TO_TADDR(m_decoder.GetBase()) ); + COUNT_T numVirtualSections = nativeHeader->VirtualSectionsTable.Size / sizeof (CORCOMPILE_VIRTUAL_SECTION_INFO); + + for( COUNT_T i = 0; i < numVirtualSections; ++i ) + { + TempBuffer sectionNameBuf; + TempBuffer sectionFlags; + StackScratchBuffer scratch; + + sectionNameBuf.Append(g_sectionNames[VirtualSectionData::VirtualSectionType(sects[i].SectionType)]); + + EnumFlagsToString( sects[i].SectionType, s_virtualSectionFlags, dim(s_virtualSectionFlags), + W(" | "), sectionFlags); + + sectionNameBuf.Append(W(" [")); + sectionNameBuf.Append(sectionFlags); + sectionNameBuf.Append(W("]")); + + DisplayStartElement( "Section", SLIM_MODULE_TBLS ); + DisplayWriteElementString("Name", sectionNameBuf.GetANSI(scratch), SLIM_MODULE_TBLS); + + DisplayWriteElementAddress( "Address", + RvaToDisplay(sects[i].VirtualAddress), + sects[i].Size, + SLIM_MODULE_TBLS ); + DisplayEndElement( SLIM_MODULE_TBLS ); //Section + } + DisplayEndArray( "Total VirtualSections", SLIM_MODULE_TBLS ); + + WRITE_NATIVE_FIELD(EEInfoTable); + +#undef WRITE_NATIVE_FIELD + DisplayWriteFieldEnumerated( Flags, nativeHeader->Flags, + CORCOMPILE_HEADER, s_CorCompileHdrFlags, W(", "), + NATIVE_INFO ); + + DisplayWriteFieldEnumerated( PEKind, nativeHeader->PEKind, + CORCOMPILE_HEADER, s_CorPEKind, W(", "), + NATIVE_INFO ); + + DisplayWriteFieldEnumerated( COR20Flags, nativeHeader->COR20Flags, + CORCOMPILE_HEADER, s_CorHdrFlags, W(", "), + NATIVE_INFO ); + + DisplayWriteFieldEnumerated( Machine, nativeHeader->Machine, + CORCOMPILE_HEADER, s_IFH_Machine, + W(""), NATIVE_INFO ); + DisplayWriteFieldEnumerated( Characteristics, + nativeHeader->Characteristics, + CORCOMPILE_HEADER, s_IFH_Characteristics, + W(", "), NATIVE_INFO ); + + m_display->EndStructure(); //CORCOMPILE_HEADER + } +} + +const NativeImageDumper::EnumMnemonics s_RelocType[] = +{ +#define REL_ENTRY(x) NativeImageDumper::EnumMnemonics( x, 0, W(#x)) + REL_ENTRY(IMAGE_REL_BASED_ABSOLUTE), + REL_ENTRY(IMAGE_REL_BASED_HIGHLOW), + REL_ENTRY(IMAGE_REL_BASED_DIR64), + REL_ENTRY(IMAGE_REL_BASED_THUMB_MOV32), +#undef REL_ENTRY +}; + +void NativeImageDumper::DumpBaseRelocs() +{ + COUNT_T size; + TADDR data; + + data = m_decoder.GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_BASERELOC, &size); + + if (size != 0) + { + DisplayStartStructure( "Relocations", DataPtrToDisplay(data), size, + ALWAYS ); + + while (size != 0) + { + IMAGE_BASE_RELOCATION * pBaseRelocation = dac_cast<DPTR(IMAGE_BASE_RELOCATION)>(data); + _ASSERTE(size >= pBaseRelocation->SizeOfBlock); + + SIZE_T rel = sizeof(IMAGE_BASE_RELOCATION); + while (rel < pBaseRelocation->SizeOfBlock) + { + USHORT typeOffset = *PTR_USHORT(data + rel); + + DisplayStartElement( "Entry", ALWAYS ); + + DisplayWriteElementPointer( "Address", RvaToDisplay(pBaseRelocation->VirtualAddress + (typeOffset & 0xFFF)), ALWAYS ); + + DisplayWriteElementEnumerated( "Type", (typeOffset >> 12), + s_RelocType, W(", "), ALWAYS ); + + DisplayEndElement( ALWAYS ); //Entry + + rel += sizeof(USHORT); + } + + data += pBaseRelocation->SizeOfBlock; + size -= pBaseRelocation->SizeOfBlock; + } + + DisplayEndStructure( ALWAYS ); //Relocations + } +} + +void NativeImageDumper::DumpHelperTable() +{ + COUNT_T size; + TADDR data; + + data = TO_TADDR(m_decoder.GetNativeHelperTable(&size)); + if( size != 0 ) + { + DisplayStartStructure( "HelperTable", DataPtrToDisplay(data), size, + ALWAYS ); + + TADDR curEntry = data; + TADDR tableEnd = data + size; + + while (curEntry < tableEnd) + { + DWORD dwHelper = *PTR_DWORD(curEntry); + + int iHelper = (USHORT)dwHelper; + _ASSERTE(iHelper < CORINFO_HELP_COUNT); + + DisplayStartStructure( "Helper", + DataPtrToDisplay(curEntry), (dwHelper & CORCOMPILE_HELPER_PTR) ? sizeof(TADDR) : HELPER_TABLE_ENTRY_LEN, + ALWAYS ); + + DisplayWriteElementUInt( "dwHelper", dwHelper, ALWAYS ); + DisplayWriteElementString( "Name", g_helperNames[iHelper], ALWAYS ); + + DisplayEndStructure( ALWAYS ); //Helper + + curEntry += (dwHelper & CORCOMPILE_HELPER_PTR) ? sizeof(TADDR) : HELPER_TABLE_ENTRY_LEN; + } + + DisplayEndStructure( ALWAYS ); //HelperTable + } +} + +// TODO: fix these to work with the updated flags in MethodTable, AND to understand +// the new overloading of component size... + +NativeImageDumper::EnumMnemonics s_MTFlagsLow[] = +{ +#define MTFLAG_ENTRY(x) \ + NativeImageDumper::EnumMnemonics(MethodTable::enum_flag_ ## x, W(#x)) + + MTFLAG_ENTRY(UNUSED_ComponentSize_1), + MTFLAG_ENTRY(StaticsMask), + MTFLAG_ENTRY(StaticsMask_NonDynamic), + MTFLAG_ENTRY(StaticsMask_Dynamic), + MTFLAG_ENTRY(StaticsMask_Generics), + MTFLAG_ENTRY(StaticsMask_CrossModuleGenerics), + MTFLAG_ENTRY(NotInPZM), + MTFLAG_ENTRY(GenericsMask), + MTFLAG_ENTRY(GenericsMask_NonGeneric), + MTFLAG_ENTRY(GenericsMask_GenericInst), + MTFLAG_ENTRY(GenericsMask_SharedInst), + MTFLAG_ENTRY(GenericsMask_TypicalInst), +#if defined(FEATURE_REMOTING) + MTFLAG_ENTRY(ContextStatic), +#endif + MTFLAG_ENTRY(HasRemotingVtsInfo), + MTFLAG_ENTRY(HasVariance), + MTFLAG_ENTRY(HasDefaultCtor), + MTFLAG_ENTRY(HasPreciseInitCctors), +#if defined(FEATURE_HFA) + MTFLAG_ENTRY(IsHFA), +#endif // FEATURE_HFA +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING_ITF) + MTFLAG_ENTRY(IsRegStructPassed), +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING_ITF + MTFLAG_ENTRY(IsByRefLike), + MTFLAG_ENTRY(UNUSED_ComponentSize_5), + MTFLAG_ENTRY(UNUSED_ComponentSize_6), + MTFLAG_ENTRY(UNUSED_ComponentSize_7), +#undef MTFLAG_ENTRY +}; + +NativeImageDumper::EnumMnemonics s_MTFlagsHigh[] = +{ +#define MTFLAG_ENTRY(x) \ + NativeImageDumper::EnumMnemonics(MethodTable::enum_flag_ ## x, W(#x)) + +#define MTFLAG_CATEGORY_ENTRY(x) \ + NativeImageDumper::EnumMnemonics(MethodTable::enum_flag_Category_ ## x, MethodTable::enum_flag_Category_Mask, W("Category_") W(#x)) + +#define MTFLAG_CATEGORY_ENTRY_WITH_MASK(x, m) \ + NativeImageDumper::EnumMnemonics(MethodTable::enum_flag_Category_ ## x, MethodTable::enum_flag_Category_ ## m, W("Category_") W(#x)) + + MTFLAG_CATEGORY_ENTRY(Class), + MTFLAG_CATEGORY_ENTRY(Unused_1), + MTFLAG_CATEGORY_ENTRY(MarshalByRef), + MTFLAG_CATEGORY_ENTRY(Contextful), + MTFLAG_CATEGORY_ENTRY(ValueType), + MTFLAG_CATEGORY_ENTRY(Nullable), + MTFLAG_CATEGORY_ENTRY(PrimitiveValueType), + MTFLAG_CATEGORY_ENTRY(TruePrimitive), + + MTFLAG_CATEGORY_ENTRY(Interface), + MTFLAG_CATEGORY_ENTRY(Unused_2), + MTFLAG_CATEGORY_ENTRY(TransparentProxy), + MTFLAG_CATEGORY_ENTRY(AsyncPin), + + MTFLAG_CATEGORY_ENTRY_WITH_MASK(Array, Array_Mask), + MTFLAG_CATEGORY_ENTRY_WITH_MASK(IfArrayThenSzArray, IfArrayThenSzArray), + +#undef MTFLAG_CATEGORY_ENTRY_WITH_MASK +#undef MTFLAG_CATEGORY_ENTRY + + MTFLAG_ENTRY(HasFinalizer), + MTFLAG_ENTRY(IfNotInterfaceThenMarshalable), +#if defined(FEATURE_COMINTEROP) + MTFLAG_ENTRY(IfInterfaceThenHasGuidInfo), +#endif +#if defined(FEATURE_ICASTABLE) + MTFLAG_ENTRY(ICastable), +#endif + MTFLAG_ENTRY(HasIndirectParent), + MTFLAG_ENTRY(ContainsPointers), + MTFLAG_ENTRY(HasTypeEquivalence), +#if defined(FEATURE_COMINTEROP) + MTFLAG_ENTRY(HasRCWPerTypeData), +#endif + MTFLAG_ENTRY(HasCriticalFinalizer), + MTFLAG_ENTRY(Collectible), + MTFLAG_ENTRY(ContainsGenericVariables), +#if defined(FEATURE_COMINTEROP) + MTFLAG_ENTRY(ComObject), +#endif + MTFLAG_ENTRY(HasComponentSize), +#undef MTFLAG_ENTRY +}; + + +NativeImageDumper::EnumMnemonics s_MTFlags2[] = +{ +#define MTFLAG2_ENTRY(x) \ + NativeImageDumper::EnumMnemonics(MethodTable::enum_flag_ ## x, W(#x)) + MTFLAG2_ENTRY(HasPerInstInfo), + MTFLAG2_ENTRY(HasInterfaceMap), + MTFLAG2_ENTRY(HasDispatchMapSlot), + MTFLAG2_ENTRY(HasNonVirtualSlots), + MTFLAG2_ENTRY(HasModuleOverride), + MTFLAG2_ENTRY(IsZapped), + MTFLAG2_ENTRY(IsPreRestored), + MTFLAG2_ENTRY(HasModuleDependencies), + MTFLAG2_ENTRY(NoSecurityProperties), + MTFLAG2_ENTRY(RequiresDispatchTokenFat), + MTFLAG2_ENTRY(HasCctor), + MTFLAG2_ENTRY(HasCCWTemplate), +#ifdef FEATURE_64BIT_ALIGNMENT + MTFLAG2_ENTRY(RequiresAlign8), +#endif + MTFLAG2_ENTRY(HasBoxedRegularStatics), + MTFLAG2_ENTRY(HasSingleNonVirtualSlot), + MTFLAG2_ENTRY(DependsOnEquivalentOrForwardedStructs), +#undef MTFLAG2_ENTRY +}; + +NativeImageDumper::EnumMnemonics s_WriteableMTFlags[] = +{ +#define WMTFLAG_ENTRY(x) \ + NativeImageDumper::EnumMnemonics(MethodTableWriteableData::enum_flag_ ## x,\ + W(#x)) + + WMTFLAG_ENTRY(RemotingConfigChecked), + WMTFLAG_ENTRY(RequiresManagedActivation), + WMTFLAG_ENTRY(Unrestored), + WMTFLAG_ENTRY(CriticalTypePrepared), + WMTFLAG_ENTRY(HasApproxParent), + WMTFLAG_ENTRY(UnrestoredTypeKey), + WMTFLAG_ENTRY(IsNotFullyLoaded), + WMTFLAG_ENTRY(DependenciesLoaded), + +#ifdef _DEBUG + WMTFLAG_ENTRY(ParentMethodTablePointerValid), +#endif + + WMTFLAG_ENTRY(NGEN_IsFixedUp), + WMTFLAG_ENTRY(NGEN_IsNeedsRestoreCached), + WMTFLAG_ENTRY(NGEN_CachedNeedsRestore), +#undef WMTFLAG_ENTRY +}; + +static NativeImageDumper::EnumMnemonics s_CorElementType[] = +{ +#define CET_ENTRY(x) NativeImageDumper::EnumMnemonics(ELEMENT_TYPE_ ## x, 0, W("ELEMENT_TYPE_") W(#x)) + CET_ENTRY(END), + CET_ENTRY(VOID), + CET_ENTRY(BOOLEAN), + CET_ENTRY(CHAR), + CET_ENTRY(I1), + CET_ENTRY(U1), + CET_ENTRY(I2), + CET_ENTRY(U2), + CET_ENTRY(I4), + CET_ENTRY(U4), + CET_ENTRY(I8), + CET_ENTRY(U8), + CET_ENTRY(R4), + CET_ENTRY(R8), + CET_ENTRY(STRING), + CET_ENTRY(PTR), + CET_ENTRY(BYREF), + CET_ENTRY(VALUETYPE), + CET_ENTRY(CLASS), + CET_ENTRY(VAR), + CET_ENTRY(ARRAY), + CET_ENTRY(GENERICINST), + CET_ENTRY(TYPEDBYREF), + CET_ENTRY(VALUEARRAY_UNSUPPORTED), + CET_ENTRY(I), + CET_ENTRY(U), + CET_ENTRY(R_UNSUPPORTED), + CET_ENTRY(FNPTR), + CET_ENTRY(OBJECT), + CET_ENTRY(SZARRAY), + CET_ENTRY(MVAR), + CET_ENTRY(CMOD_REQD), + CET_ENTRY(CMOD_OPT), + CET_ENTRY(INTERNAL), + + CET_ENTRY(SENTINEL), + CET_ENTRY(PINNED), +#undef CET_ENTRY +}; + +void NativeImageDumper::DoWriteFieldCorElementType( const char * name, + unsigned offset, + unsigned fieldSize, + CorElementType type ) +{ + TempBuffer buf; + EnumFlagsToString( (int)type, s_CorElementType, dim(s_CorElementType), + W(""), buf ); + m_display->WriteFieldEnumerated( name, offset, fieldSize, (unsigned)type, + (const WCHAR *) buf ); + +} + +static NativeImageDumper::EnumMnemonics s_CorTypeAttr[] = +{ +#define CTA_ENTRY(x) NativeImageDumper::EnumMnemonics( x, W(#x) ) + +#define CTA_VISIBILITY_ENTRY(x) NativeImageDumper::EnumMnemonics( x, tdVisibilityMask, W(#x) ) + CTA_VISIBILITY_ENTRY(tdNotPublic), + CTA_VISIBILITY_ENTRY(tdPublic), + CTA_VISIBILITY_ENTRY(tdNestedPublic), + CTA_VISIBILITY_ENTRY(tdNestedPrivate), + CTA_VISIBILITY_ENTRY(tdNestedFamily), + CTA_VISIBILITY_ENTRY(tdNestedAssembly), + CTA_VISIBILITY_ENTRY(tdNestedFamANDAssem), + CTA_VISIBILITY_ENTRY(tdNestedFamORAssem), +#undef CTA_VISIBILITY_ENTRY + + CTA_ENTRY(tdSequentialLayout), + CTA_ENTRY(tdExplicitLayout), + + CTA_ENTRY(tdInterface), + + CTA_ENTRY(tdAbstract), + CTA_ENTRY(tdSealed), + CTA_ENTRY(tdSpecialName), + + CTA_ENTRY(tdImport), + CTA_ENTRY(tdSerializable), + + CTA_ENTRY(tdUnicodeClass), + CTA_ENTRY(tdAutoClass), + CTA_ENTRY(tdCustomFormatClass), + CTA_ENTRY(tdCustomFormatMask), + + CTA_ENTRY(tdBeforeFieldInit), + CTA_ENTRY(tdForwarder), + + CTA_ENTRY(tdRTSpecialName), + CTA_ENTRY(tdHasSecurity) +#undef CTA_ENTRY +}; +static NativeImageDumper::EnumMnemonics s_VMFlags[] = +{ +#define VMF_ENTRY_TRANSPARENCY(x) NativeImageDumper::EnumMnemonics( EEClass::VMFLAG_ ## x, EEClass::VMFLAG_TRANSPARENCY_MASK, W(#x) ) + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_UNKNOWN), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_TRANSPARENT), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_ALL_TRANSPARENT), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_CRITICAL), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_CRITICAL_TAS), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_ALLCRITICAL), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_ALLCRITICAL_TAS), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_TAS_NOTCRITICAL), +#undef VMF_ENTRY_TRANSPARENCY + +#define VMF_ENTRY(x) NativeImageDumper::EnumMnemonics( EEClass::VMFLAG_ ## x, W(#x) ) + +#ifdef FEATURE_READYTORUN + VMF_ENTRY(LAYOUT_DEPENDS_ON_OTHER_MODULES), +#endif + VMF_ENTRY(DELEGATE), + VMF_ENTRY(FIXED_ADDRESS_VT_STATICS), + VMF_ENTRY(HASLAYOUT), + VMF_ENTRY(ISNESTED), +#ifdef FEATURE_REMOTING + VMF_ENTRY(CANNOT_BE_BLITTED_BY_OBJECT_CLONER), +#endif + VMF_ENTRY(IS_EQUIVALENT_TYPE), + + VMF_ENTRY(HASOVERLAYEDFIELDS), + VMF_ENTRY(HAS_FIELDS_WHICH_MUST_BE_INITED), + VMF_ENTRY(UNSAFEVALUETYPE), + + VMF_ENTRY(BESTFITMAPPING_INITED), + VMF_ENTRY(BESTFITMAPPING), + VMF_ENTRY(THROWONUNMAPPABLECHAR), + + VMF_ENTRY(NOSUPPRESSUNMGDCODEACCESS), + VMF_ENTRY(NO_GUID), + VMF_ENTRY(HASNONPUBLICFIELDS), + VMF_ENTRY(REMOTING_PROXY_ATTRIBUTE), + VMF_ENTRY(PREFER_ALIGN8), + VMF_ENTRY(METHODS_REQUIRE_INHERITANCE_CHECKS), + +#ifdef FEATURE_COMINTEROP + VMF_ENTRY(SPARSE_FOR_COMINTEROP), + VMF_ENTRY(HASCOCLASSATTRIB), + VMF_ENTRY(COMEVENTITFMASK), + VMF_ENTRY(PROJECTED_FROM_WINRT), + VMF_ENTRY(EXPORTED_TO_WINRT), +#endif // FEATURE_COMINTEROP + + VMF_ENTRY(NOT_TIGHTLY_PACKED), + VMF_ENTRY(CONTAINS_METHODIMPLS), +#ifdef FEATURE_COMINTEROP + VMF_ENTRY(MARSHALINGTYPE_MASK), + VMF_ENTRY(MARSHALINGTYPE_INHIBIT), + VMF_ENTRY(MARSHALINGTYPE_FREETHREADED), + VMF_ENTRY(MARSHALINGTYPE_STANDARD), +#endif +#undef VMF_ENTRY +}; +static NativeImageDumper::EnumMnemonics s_SecurityProperties[] = +{ +#define SP_ENTRY(x) NativeImageDumper::EnumMnemonics(DECLSEC_ ## x, W(#x)) + SP_ENTRY(DEMANDS), + SP_ENTRY(ASSERTIONS), + SP_ENTRY(DENIALS), + SP_ENTRY(INHERIT_CHECKS), + SP_ENTRY(LINK_CHECKS), + SP_ENTRY(PERMITONLY), + SP_ENTRY(REQUESTS), + SP_ENTRY(UNMNGD_ACCESS_DEMAND), + SP_ENTRY(NONCAS_DEMANDS), + SP_ENTRY(NONCAS_LINK_DEMANDS), + SP_ENTRY(NONCAS_INHERITANCE), + + SP_ENTRY(NULL_INHERIT_CHECKS), + SP_ENTRY(NULL_LINK_CHECKS), +#undef SP_ENTRY +}; +static NativeImageDumper::EnumMnemonics s_CorFieldAttr[] = +{ +#define CFA_ENTRY(x) NativeImageDumper::EnumMnemonics( x, W(#x) ) + +#define CFA_ACCESS_ENTRY(x) NativeImageDumper::EnumMnemonics( x, fdFieldAccessMask, W(#x) ) + CFA_ENTRY(fdPrivateScope), + CFA_ENTRY(fdPrivate), + CFA_ENTRY(fdFamANDAssem), + CFA_ENTRY(fdAssembly), + CFA_ENTRY(fdFamily), + CFA_ENTRY(fdFamORAssem), + CFA_ENTRY(fdPublic), +#undef CFA_ACCESS_ENTRY + + CFA_ENTRY(fdStatic), + CFA_ENTRY(fdInitOnly), + CFA_ENTRY(fdLiteral), + CFA_ENTRY(fdNotSerialized), + + CFA_ENTRY(fdSpecialName), + + CFA_ENTRY(fdPinvokeImpl), + + CFA_ENTRY(fdRTSpecialName), + CFA_ENTRY(fdHasFieldMarshal), + CFA_ENTRY(fdHasDefault), + CFA_ENTRY(fdHasFieldRVA), +#undef CFA_ENTRY +}; + +NativeImageDumper::EnumMnemonics NativeImageDumper::s_MDFlag2[] = +{ +#define MDF2_ENTRY(x) NativeImageDumper::EnumMnemonics( MethodDesc::enum_flag2_ ## x, W("enum_flag2_") W(#x) ) + MDF2_ENTRY(HasStableEntryPoint), + MDF2_ENTRY(HasPrecode), + MDF2_ENTRY(IsUnboxingStub), + MDF2_ENTRY(HasNativeCodeSlot), + MDF2_ENTRY(Transparency_TreatAsSafe), + MDF2_ENTRY(Transparency_Transparent), + MDF2_ENTRY(Transparency_Critical), + MDF2_ENTRY(HostProtectionLinkCheckOnly), + MDF2_ENTRY(CASDemandsOnly), +#undef MDF2_ENTRY +}; + +NativeImageDumper::EnumMnemonics NativeImageDumper::s_MDC[] = +{ +#define MDC_ENTRY(x) NativeImageDumper::EnumMnemonics( x, W(#x) ) + +#define MDC_ENTRY_CLASSIFICATION(x) NativeImageDumper::EnumMnemonics( x, mdcClassification, W(#x) ) + MDC_ENTRY_CLASSIFICATION(mcIL), + MDC_ENTRY_CLASSIFICATION(mcFCall), + MDC_ENTRY_CLASSIFICATION(mcNDirect), + MDC_ENTRY_CLASSIFICATION(mcEEImpl), + MDC_ENTRY_CLASSIFICATION(mcArray), + MDC_ENTRY_CLASSIFICATION(mcInstantiated), +#ifdef FEATURE_COMINTEROP + MDC_ENTRY_CLASSIFICATION(mcComInterop), +#endif // FEATURE_COMINTEROP + MDC_ENTRY_CLASSIFICATION(mcDynamic), +#undef MDC_ENTRY_CLASSIFICATION + + MDC_ENTRY(mdcHasNonVtableSlot), + MDC_ENTRY(mdcMethodImpl), + + // Method is static + MDC_ENTRY(mdcStatic), + MDC_ENTRY(mdcIntercepted), + + MDC_ENTRY(mdcRequiresLinktimeCheck), + + MDC_ENTRY(mdcRequiresInheritanceCheck), + + MDC_ENTRY(mdcParentRequiresInheritanceCheck), + + MDC_ENTRY(mdcDuplicate), + MDC_ENTRY(mdcVerifiedState), + MDC_ENTRY(mdcVerifiable), + MDC_ENTRY(mdcNotInline), + MDC_ENTRY(mdcSynchronized), + MDC_ENTRY(mdcRequiresFullSlotNumber), +#undef MDC_ENTRY +}; + + + +void NativeImageDumper::DumpTypes(PTR_Module module) +{ + _ASSERTE(CHECK_OPT(EECLASSES) || CHECK_OPT(METHODTABLES) + || CHECK_OPT(TYPEDESCS)); + + IF_OPT_OR3(METHODTABLES, EECLASSES, TYPEDESCS) + m_display->StartCategory( "Types" ); + IF_OPT(METHODTABLES) + { + //there may be duplicates in the list. Remove them before moving on. + COUNT_T mtCount = m_discoveredMTs.GetCount(); + +#if !defined(FEATURE_CORESYSTEM) // no STL right now + std::sort(&*m_discoveredMTs.Begin(), + (&*m_discoveredMTs.Begin()) + + (m_discoveredMTs.End() - m_discoveredMTs.Begin())); + PTR_MethodTable* newEnd = std::unique(&*m_discoveredMTs.Begin(), + (&*m_discoveredMTs.Begin()) + + (m_discoveredMTs.End() + - m_discoveredMTs.Begin())); + mtCount = (COUNT_T)(newEnd - &*m_discoveredMTs.Begin()); +#endif + + DisplayStartArray( "MethodTables", NULL, METHODTABLES ); + for(COUNT_T i = 0; i < mtCount; ++i ) + { + PTR_MethodTable mt = m_discoveredMTs[i]; + if( mt == NULL ) + continue; + DumpMethodTable( mt, "MethodTable", module ); + } + + DisplayEndArray( "Total MethodTables", METHODTABLES ); + + DisplayStartArray( "MethodTableSlotChunks", NULL, METHODTABLES ); + { + COUNT_T slotChunkCount = m_discoveredSlotChunks.GetCount(); +#if !defined(FEATURE_CORESYSTEM) // no STL right now + std::sort(&*m_discoveredSlotChunks.Begin(), + (&*m_discoveredSlotChunks.Begin()) + + (m_discoveredSlotChunks.End() - m_discoveredSlotChunks.Begin())); + SlotChunk *newEndChunks = std::unique(&*m_discoveredSlotChunks.Begin(), + (&*m_discoveredSlotChunks.Begin()) + + (m_discoveredSlotChunks.End() - m_discoveredSlotChunks.Begin())); + slotChunkCount = (COUNT_T)(newEndChunks - &*m_discoveredSlotChunks.Begin()); +#endif + + for (COUNT_T i = 0; i < slotChunkCount; ++i) + { + DumpMethodTableSlotChunk(m_discoveredSlotChunks[i].addr, m_discoveredSlotChunks[i].nSlots); + } + } + DisplayEndArray( "Total MethodTableSlotChunks", METHODTABLES ); + } + IF_OPT(EECLASSES) + { + DisplayStartArray( "EEClasses", NULL, EECLASSES ); + + //there may be duplicates in the list. Remove them before moving on. + COUNT_T clazzCount = m_discoveredClasses.GetCount(); +#if !defined(FEATURE_CORESYSTEM) // no STL right now + std::sort(&*m_discoveredClasses.Begin(), + (&*m_discoveredClasses.Begin()) + + (m_discoveredClasses.End() - m_discoveredClasses.Begin())); + PTR_MethodTable * newEndClazz = std::unique(&*m_discoveredClasses.Begin(), + (&*m_discoveredClasses.Begin()) + +(m_discoveredClasses.End() + -m_discoveredClasses.Begin())); + clazzCount = (COUNT_T)(newEndClazz - &*m_discoveredClasses.Begin()); +#endif + + for(COUNT_T i = 0; i < clazzCount; ++i ) + { + PTR_MethodTable mt = m_discoveredClasses[i]; + if( mt == NULL ) + continue; + DumpEEClassForMethodTable( mt ); + } + + DisplayEndArray( "Total EEClasses", EECLASSES ); //EEClasses + + } + IF_OPT(TYPEDESCS) + { + DisplayStartArray( "TypeDescs", NULL, TYPEDESCS ); + + //there may be duplicates in the list. Remove them before moving on. + COUNT_T tdCount = m_discoveredTypeDescs.GetCount(); +#if !defined(FEATURE_CORESYSTEM) // no STL right now + std::sort(&*m_discoveredTypeDescs.Begin(), + (&*m_discoveredTypeDescs.Begin()) + + (m_discoveredTypeDescs.End() + - m_discoveredTypeDescs.Begin())); + PTR_TypeDesc* newEndTD = std::unique(&*m_discoveredTypeDescs.Begin(), + (&*m_discoveredTypeDescs.Begin()) + +(m_discoveredTypeDescs.End() + -m_discoveredTypeDescs.Begin())); + tdCount = (COUNT_T)(newEndTD - &*m_discoveredTypeDescs.Begin()); +#endif + + for(COUNT_T i = 0; i < tdCount; ++i ) + { + PTR_TypeDesc td = m_discoveredTypeDescs[i]; + if( td == NULL ) + continue; + DumpTypeDesc( td ); + } + + DisplayEndArray( "Total TypeDescs", TYPEDESCS ); //EEClasses + + } + IF_OPT_OR3(EECLASSES, METHODTABLES, TYPEDESCS) + m_display->EndCategory(); //Types +} + +PTR_EEClass NativeImageDumper::GetClassFromMT( PTR_MethodTable mt ) +{ + /* REVISIT_TODO Tue 10/11/2005 + * Handle fixups + */ + _ASSERTE( mt->IsClassPointerValid() ); + PTR_EEClass clazz( mt->GetClass() ); + return clazz; +} +PTR_MethodTable NativeImageDumper::GetParent( PTR_MethodTable mt ) +{ + /* REVISIT_TODO Thu 12/01/2005 + * Handle fixups + */ + PTR_MethodTable parent( mt->m_pParentMethodTable ); + _ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(parent))); + return parent; +} + +//Counts the FieldDescs in a class. This is all of the non-static and static +//non-literal fields. +SIZE_T NativeImageDumper::CountFields( PTR_MethodTable mt ) +{ + SIZE_T fieldCount = 0; + + HCORENUM hEnum = NULL; + + const Dependency * dep = GetDependencyFromMT(mt); + mdToken classToken = mt->GetCl(); + + _ASSERTE(dep); + _ASSERTE(dep->pImport); + + //Arrays have no token. + if( RidFromToken(classToken) == 0 ) + return 0; + + for (;;) + { + mdToken fields[1]; + ULONG numFields; + + IfFailThrow(dep->pImport->EnumFields( &hEnum, classToken, fields, + 1, &numFields)); + + if (numFields == 0) + break; + + DWORD dwAttr; + IfFailThrow(dep->pImport->GetFieldProps( fields[0], NULL, NULL, 0, + NULL, & dwAttr, NULL, NULL, + NULL, NULL, NULL ) ); + if( !IsFdStatic(dwAttr) || !IsFdLiteral(dwAttr) ) + ++fieldCount; + } + dep->pImport->CloseEnum(hEnum); + return fieldCount; +} +const NativeImageDumper::Dependency* +NativeImageDumper::GetDependencyFromMT( PTR_MethodTable mt ) +{ + if( !mt->IsClassPointerValid() ) + { + //This code will not work for out of module dependencies. + _ASSERTE(isSelf(GetDependencyForPointer(PTR_TO_TADDR(mt)))); + + //the EEClass is a fixup. The home for that fixup tells us the + //home for the metadata. + unsigned rva = CORCOMPILE_UNTAG_TOKEN(mt->GetCanonicalMethodTableFixup()); + return GetDependencyForFixup(rva); + } + PTR_Module module = mt->GetModule(); + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(module)) ) + { + unsigned rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(module)); + return GetDependencyForFixup(rva); + } + return GetDependencyForModule(module); +} +const NativeImageDumper::Dependency* +NativeImageDumper::GetDependencyFromFD( PTR_FieldDesc fd ) +{ + PTR_MethodTable mt = fd->GetApproxEnclosingMethodTable(); + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ) + { + //This code will not work for out of module dependencies. + _ASSERTE(isSelf(GetDependencyForPointer(PTR_TO_TADDR(fd)))); + + //the MethodTable has a fixup. The home for that fixup tells us the + //home for the metadata. + unsigned rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(mt)); + return GetDependencyForFixup(rva); + } + return GetDependencyFromMT(mt); +} +const NativeImageDumper::Dependency* +NativeImageDumper::GetDependencyFromMD( PTR_MethodDesc md ) +{ + PTR_MethodDescChunk chunk( md->GetMethodDescChunk() ); + PTR_MethodTable mt = chunk->GetMethodTable(); + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ) + { + //This code will not work for out of module dependencies. + _ASSERTE(isSelf(GetDependencyForPointer(PTR_TO_TADDR(md)))); + + //the MethodTable has a fixup. The home for that fixup tells us the + //home for the metadata. + unsigned rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(mt)); + return GetDependencyForFixup(rva); + } + return GetDependencyFromMT(mt); +} + +BOOL NativeImageDumper::DoWriteFieldAsFixup( const char * name, + unsigned offset, + unsigned fieldSize, TADDR fixup) +{ + if( !CORCOMPILE_IS_POINTER_TAGGED(fixup) ) + return FALSE; + if( UINT_MAX == offset ) + m_display->StartVStructure( name ); + else + m_display->StartVStructureWithOffset( name, offset, fieldSize ); + + WriteElementsFixupBlob( NULL, fixup ); + m_display->EndVStructure(); //name + return TRUE; +} + +void AppendTypeQualifier( CorElementType kind, DWORD rank, SString& buf ) +{ + switch( kind ) + { + case ELEMENT_TYPE_BYREF : + buf.Append( W("&") ); + break; + case ELEMENT_TYPE_PTR : + buf.Append( W("*") ); + break; + case ELEMENT_TYPE_SZARRAY : + buf.Append( W("[]") ); + break; + case ELEMENT_TYPE_ARRAY : + if( rank == 1 ) + { + buf.Append( W("[*]") ); + } + else + { + buf.Append( W("[") ); + for( COUNT_T i = 0; i < rank; ++i ) + buf.Append( W(",")); + buf.Append( W("]") ); + } + break; + default : + break; + } +} +void NativeImageDumper::TypeDescToString( PTR_TypeDesc td, SString& buf ) +{ + _ASSERTE(!(PTR_TO_TADDR(td) & 0x2)); + if( td->IsGenericVariable() ) + { + PTR_TypeVarTypeDesc tvtd( PTR_TO_TADDR(td) ); + //From code:TypeString::AppendType + mdGenericParam token = tvtd->GetToken(); + PTR_Module module(tvtd->GetModule()); + IMetaDataImport2 * pImport; + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(module)) ) + { + if (!isSelf(GetDependencyForPointer(PTR_TO_TADDR(td)))) + { + //this is an RVA from another hardbound dependency. We cannot decode it + buf.Append(W("OUT_OF_MODULE_FIXUP")); + return; + } + else + { + RVA rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(module)); + pImport = GetDependencyForFixup(rva)->pImport; + } + } + else + { + pImport = GetDependencyForModule(module)->pImport; + } + AppendTokenName(token, buf, pImport); + } + else if( ELEMENT_TYPE_FNPTR == td->GetInternalCorElementType() ) + { + PTR_FnPtrTypeDesc fptd( PTR_TO_TADDR(td) ); + buf.Append( W("(fnptr)") ); + } + else if( td->HasTypeParam() || td->IsArray() ) + { + //either a Parameter or an Array. + PTR_ParamTypeDesc ptd(PTR_TO_TADDR(td)); + TypeHandle elemType; + /* REVISIT_TODO Thu 10/5/2006 + * Do I need to find a rank somewhere in the TypeDesc? + */ + unsigned rank; + if( td->IsArray() ) + { + //td->HasTypeParam() may also be true. + PTR_MethodTable mt = ptd->m_TemplateMT.GetValue(); + _ASSERTE( PTR_TO_TADDR(mt) ); + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ) + { + if (!isSelf(GetDependencyForPointer(PTR_TO_TADDR(ptd)))) + { + //this is an RVA from another hardbound dependency. We cannot decode it + buf.Append(W("OUT_OF_MODULE_FIXUP")); + } + else + { + RVA rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(mt)); + FixupBlobToString(rva, buf); + } + return; + } + else + { + _ASSERTE( !CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ); + MethodTableToString( mt, buf ); + rank = PTR_ArrayTypeDesc(PTR_TO_TADDR(ptd))->GetRank(); + } + } + else + { + _ASSERTE(td->HasTypeParam()); + TypeHandle th(ptd->GetTypeParam()); + _ASSERTE( !CORCOMPILE_IS_POINTER_TAGGED(th.AsTAddr()) ); + _ASSERTE( th.AsTAddr() ); + TypeHandleToString(th, buf); + rank = 0; + } + AppendTypeQualifier( td->GetInternalCorElementType(), rank, buf ); + } + else + { + //generic typedesc? + EnumFlagsToString( (int)td->GetInternalCorElementType(), s_CorElementType, dim(s_CorElementType), + W(""), buf ); + } +} +void NativeImageDumper::TypeHandleToString( TypeHandle th, SString& buf ) +{ + TADDR arg = th.AsTAddr(); + /* REVISIT_TODO Thu 10/5/2006 + * Is this constant somewhere? + */ + //0x2 is the subtle hint that this is a typedesc. code:TypeHandle::AsTypeDesc + if( arg & 0x2 ) + { + PTR_TypeDesc argTD( arg & ~0x2 ); + TypeDescToString( argTD, buf ); + } + else + { + PTR_MethodTable argMT( th.AsTAddr() ); + MethodTableToString( argMT, buf ); + } +} + +void NativeImageDumper::DoWriteFieldTypeHandle( const char * name, + unsigned offset, + unsigned fieldSize, + TypeHandle th ) +{ + TempBuffer buf; + TADDR ptr = th.AsTAddr(); + if( DoWriteFieldAsFixup(name, offset, fieldSize, th.AsTAddr() ) ) + return; + else + { + TypeHandleToString(th, buf); + + buf.Append( W(" (from TypeHandle)") ); + /* REVISIT_TODO Fri 10/14/2005 + * Do a better job of this + */ + if( offset == UINT_MAX ) + { + m_display->WriteElementPointerAnnotated( name, + DataPtrToDisplay(ptr), + (const WCHAR*) buf ); + } + else + { + m_display->WriteFieldPointerAnnotated( name, offset, fieldSize, + DataPtrToDisplay(ptr), + (const WCHAR*) buf ); + } + } +} +void NativeImageDumper::WriteElementTypeHandle( const char * name, + TypeHandle th ) +{ + DoWriteFieldTypeHandle( name, UINT_MAX, UINT_MAX, th ); +} + +void NativeImageDumper::DoDumpFieldStub( PTR_Stub stub, unsigned offset, + unsigned fieldSize, const char * name ) +{ + _ASSERTE(CHECK_OPT(EECLASSES)); + if( stub == NULL ) + { + m_display->WriteFieldPointer( name, offset, fieldSize, NULL ); + } + else + { + m_display->StartStructureWithOffset( name, offset, fieldSize, + DPtrToPreferredAddr(stub), + sizeof(*stub) ); + /* REVISIT_TODO Fri 10/14/2005 + * Dump stub + */ + m_display->EndStructure(); + } +} + +#ifdef FEATURE_COMINTEROP +void NativeImageDumper::DoDumpComPlusCallInfo( PTR_ComPlusCallInfo compluscall ) +{ + m_display->StartStructure( "ComPlusCallInfo", + DPtrToPreferredAddr(compluscall), + sizeof(*compluscall) ); + + DisplayWriteFieldPointer( m_pILStub, compluscall->m_pILStub, + ComPlusCallInfo, ALWAYS); + /* REVISIT_TODO Fri 12/16/2005 + * Coverage read stub? + */ + WriteFieldMethodTable(m_pInterfaceMT, + compluscall->m_pInterfaceMT, + ComPlusCallInfo, ALWAYS); + + PTR_MethodDesc pEventProviderMD = PTR_MethodDesc((TADDR)compluscall->m_pEventProviderMD); + WriteFieldMethodDesc(m_pEventProviderMD, + pEventProviderMD, + ComPlusCallInfo, ALWAYS); + DisplayWriteFieldInt( m_cachedComSlot, compluscall->m_cachedComSlot, + ComPlusCallInfo, ALWAYS ); + + /* REVISIT_TODO Fri 12/16/2005 + * Dump these as mnemonics + */ + DisplayWriteFieldInt( m_flags, compluscall->m_flags, + ComPlusCallInfo, ALWAYS ); + WriteFieldMethodDesc( m_pStubMD, + compluscall->m_pStubMD.GetValueMaybeNull(PTR_HOST_MEMBER_TADDR(ComPlusCallInfo, compluscall, m_pStubMD)), + ComPlusCallInfo, ALWAYS ); + +#ifdef _TARGET_X86_ + DisplayWriteFieldInt( m_cbStackArgumentSize, compluscall->m_cbStackArgumentSize, + ComPlusCallInfo, ALWAYS ); + + DisplayWriteFieldPointer( m_pRetThunk, + DataPtrToDisplay((TADDR)compluscall->m_pRetThunk), + ComPlusCallInfo, ALWAYS ); +#endif + m_display->EndStructure(); //ComPlusCallInfo +} +#endif // FEATURE_COMINTEROP + +void NativeImageDumper::DoWriteFieldStr( PTR_BYTE ptr, const char * name, + unsigned offset, unsigned fieldSize ) +{ + if( ptr == NULL ) + { + if( UINT_MAX == offset ) + m_display->WriteElementPointer( name, NULL ); + else + m_display->WriteFieldPointer( name, offset, fieldSize, NULL ); + } + else + { + /* REVISIT_TODO Wed 03/22/2006 + * Obviously this does the wrong thing for UTF-8. + */ + TempBuffer buf; + BYTE b; + TADDR taddr = DPtrToPreferredAddr(ptr); + PTR_BYTE current = ptr; + /* REVISIT_TODO Mon 03/27/2006 + * Actually handle UTF-8 properly + */ + while( (b = *current++) != 0 ) + buf.Append( (WCHAR)b ); + /* REVISIT_TODO Wed 03/22/2006 + * This seems way way way more verbose than it needs to be. + */ + if( UINT_MAX == offset ) + { + m_display->StartStructure( name, DataPtrToDisplay(taddr), + current - ptr ); + } + else + { + m_display->StartStructureWithOffset( name, offset, fieldSize, + DataPtrToDisplay(taddr), + current - ptr ); + } + DisplayWriteElementStringW( "Value", (const WCHAR *)buf, ALWAYS ); + m_display->EndStructure(); + /* + m_display->WriteFieldPointerAnnotated( name, offset, fieldSize, + taddr, (const WCHAR *)buf ); + */ + } +} +void NativeImageDumper::WriteFieldDictionaryLayout(const char * name, + unsigned offset, + unsigned fieldSize, + PTR_DictionaryLayout layout, + IMetaDataImport2 * import) +{ + if( layout == NULL ) + { + m_display->WriteFieldPointer(name, NULL, offset, fieldSize); + return; + } + m_display->StartVStructureWithOffset( name, offset, fieldSize ); + DisplayStartArray( "DictionaryLayouts", NULL, ALWAYS ); + do + { + DisplayStartStructure( "DictionaryLayout", DPtrToPreferredAddr(layout), + sizeof(DictionaryLayout) + + sizeof(DictionaryEntryLayout) + * (layout->m_numSlots - 1), ALWAYS ); + + + DisplayWriteFieldPointer( m_pNext, DataPtrToDisplay((TADDR)layout->m_pNext), + DictionaryLayout, ALWAYS ); + DisplayWriteFieldInt( m_numSlots, layout->m_numSlots, + DictionaryLayout, ALWAYS ); + DisplayStartArrayWithOffset( m_slots, NULL, DictionaryLayout, ALWAYS ); + for( unsigned i = 0; i < layout->m_numSlots; ++i ) + { + PTR_DictionaryEntryLayout entry( PTR_HOST_MEMBER_TADDR(DictionaryLayout, layout, m_slots) + (i * sizeof(DictionaryEntryLayout)) ); + DisplayStartStructure( "DictionaryEntryLayout", + DPtrToPreferredAddr(entry), sizeof(*entry), + ALWAYS ); + const char * kind = NULL; + switch( entry->GetKind() ) + { +#define KIND_ENTRY(x) case x : kind = # x ; break + KIND_ENTRY(EmptySlot); + KIND_ENTRY(TypeHandleSlot); + KIND_ENTRY(MethodDescSlot); + KIND_ENTRY(MethodEntrySlot); + KIND_ENTRY(ConstrainedMethodEntrySlot); + KIND_ENTRY(DispatchStubAddrSlot); + KIND_ENTRY(FieldDescSlot); +#undef KIND_ENTRY + default: + _ASSERTE( !"unreachable" ); + } + DisplayWriteElementString( "Kind", kind, ALWAYS ); + DisplayWriteElementPointer( "Signature", DPtrToPreferredAddr(entry->m_signature), ALWAYS ); + DisplayEndStructure( ALWAYS ); //DictionaryEntryLayout + } + DisplayEndArray( "Total Dictionary Entries", ALWAYS ); //m_slots + DisplayEndStructure( ALWAYS ); //Layout + layout = PTR_DictionaryLayout(TO_TADDR(layout->m_pNext)); + }while( layout != NULL ); + DisplayEndArray( "Total Dictionary Layouts", ALWAYS ); //DictionaryLayouts + + + DisplayEndVStructure( ALWAYS ); // name +} +void NativeImageDumper::DoWriteFieldFieldDesc( const char * name, + unsigned offset, + unsigned fieldSize, + PTR_FieldDesc fd ) +{ + if( fd == NULL ) + { + m_display->WriteFieldPointer( name, offset, fieldSize, NULL ); + } + else + { + TempBuffer buf; + FieldDescToString( fd, buf ); + m_display->WriteFieldPointerAnnotated( name, offset, fieldSize, + DPtrToPreferredAddr(fd), + (const WCHAR*) buf ); + } + +} +void NativeImageDumper::DoWriteFieldMethodDesc( const char * name, + unsigned offset, + unsigned fieldSize, + PTR_MethodDesc md ) +{ + if( md == NULL ) + { + m_display->WriteFieldPointer( name, offset, fieldSize, NULL ); + } + else if( DoWriteFieldAsFixup(name, offset, fieldSize, PTR_TO_TADDR(md)) ) + { + return; + } + else + { + TempBuffer buf; + MethodDescToString( md, buf ); + m_display->WriteFieldPointerAnnotated( name, offset, fieldSize, + DPtrToPreferredAddr(md), + (const WCHAR*) buf ); + } +} + +void NativeImageDumper::EntryPointToString( TADDR pEntryPoint, + SString& buf ) +{ + const Dependency * dependency = GetDependencyForPointer(pEntryPoint); + + PTR_MethodDesc md; + if (dependency->pModule->IsZappedPrecode(pEntryPoint)) + { + md = dac_cast<PTR_MethodDesc>(Precode::GetPrecodeFromEntryPoint(pEntryPoint)->GetMethodDesc()); + } + else + { + PTR_Module module = (TADDR)m_decoder.GetPersistedModuleImage(); + PTR_NGenLayoutInfo pNgenLayout = module->GetNGenLayoutInfo(); + DWORD rva = (DWORD)(pEntryPoint - PTR_TO_TADDR(m_decoder.GetBase())); + + for (int iRange = 0; iRange < 2; iRange++) + { + if (pNgenLayout->m_CodeSections[iRange].IsInRange(pEntryPoint)) + { + int MethodIndex = NativeUnwindInfoLookupTable::LookupUnwindInfoForMethod(rva, pNgenLayout->m_pRuntimeFunctions[iRange], 0, pNgenLayout->m_nRuntimeFunctions[iRange] - 1); + if (MethodIndex >= 0) + { +#ifdef WIN64EXCEPTIONS + while (pNgenLayout->m_MethodDescs[iRange][MethodIndex] == 0) + MethodIndex--; +#endif + + PTR_RUNTIME_FUNCTION pRuntimeFunction = pNgenLayout->m_pRuntimeFunctions[iRange] + MethodIndex; + + md = NativeUnwindInfoLookupTable::GetMethodDesc(pNgenLayout, pRuntimeFunction, PTR_TO_TADDR(m_decoder.GetBase())); + break; + } + } + } + } + + MethodDescToString(md, buf); +} + +void NativeImageDumper::MethodDescToString( PTR_MethodDesc md, + SString& buf ) +{ + if( md == NULL ) + buf.Append( W("mdMethodDefNil") ); + else if( md->IsILStub() ) + buf.AppendUTF8(md->AsDynamicMethodDesc()->GetName()); + else + { + //write the name to a temporary location, since I'm going to insert it + //into the middle of a signature. + TempBuffer tempName; + + _ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(md))); + //work back to the EEClass. That gives us the context for the token. + PTR_MethodDescChunk chunk(md->GetMethodDescChunk()); + //chunk is implicitly remapped because it's calculated from the pointer + //to MD. + PTR_MethodTable mt = chunk->GetMethodTable(); + const Dependency * dependency; + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ) + { + //This code will not work for out of module dependencies. + _ASSERTE(isSelf(GetDependencyForPointer(PTR_TO_TADDR(md)))); + + RVA rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(mt)); + dependency = GetDependencyForFixup(rva); + mt = NULL; //make sure we don't use this for anything. + } + else + dependency = GetDependencyFromMT(mt); + + _ASSERTE(dependency); + + + /* REVISIT_TODO Fri 10/13/2006 + * Don't I need the array type name here? + */ + _ASSERTE(dependency->pImport); + if( md->GetClassification() == mcArray ) + { + + //We don't need to append the dependency all the time. + //MethodTableToString() already appends it to the MethodTable. + //Only do it in cases where we don't call MethodTableToString. + if( !isSelf(dependency) ) + { + AppendTokenName( dependency->entry->dwAssemblyRef, tempName, + m_manifestImport ); + tempName.Append(W("!")); + } + + _ASSERTE(PTR_TO_TADDR(mt)); + MethodTableToString( mt, tempName ); + tempName.Append( W("::") ); + + //there are four hard coded names for array method descs, use these + //instead of the token. + PTR_ArrayMethodDesc amd(PTR_TO_TADDR(md)); + tempName.AppendUTF8( amd->GetMethodName() ); + } + else + { + //if we have a MethodTable, use that and compose the name + //ourselves. That way we can get generic arguments. + if( mt ) + { + ULONG size; + MethodTableToString( mt, tempName ); + tempName.Append( W("::") ); + IfFailThrow(dependency->pImport->GetMethodProps(md->GetMemberDef(), NULL, bigBuffer, + bigBufferSize, &size, NULL, NULL, NULL, NULL, + NULL)); + tempName.Append(bigBuffer); + } + else + { + //We don't need to append the dependency all the time. + //MethodTableToString() already appends it to the MethodTable. + //Only do it in cases where we don't call MethodTableToString. + if( !isSelf(dependency) ) + { + AppendTokenName( dependency->entry->dwAssemblyRef, tempName, + m_manifestImport ); + tempName.Append(W("!")); + } + AppendTokenName( md->GetMemberDef(), tempName, dependency->pImport ); + } + + if( mcInstantiated == md->GetClassification() ) + { + PTR_InstantiatedMethodDesc imd(PTR_TO_TADDR(md)); + unsigned numArgs = imd->m_wNumGenericArgs; + PTR_Dictionary dict(imd->IMD_GetMethodDictionary()); + if( dict != NULL ) + { + DictionaryToArgString( dict, numArgs, tempName ); + } + } + + PCCOR_SIGNATURE pvSigBlob; + ULONG cbSigBlob; + IfFailThrow(dependency->pImport->GetMethodProps(md->GetMemberDef(), + NULL, + NULL, + 0, + NULL, + NULL, + &pvSigBlob, + &cbSigBlob, + NULL, + NULL)); + + + CQuickBytes prettySig; + ReleaseHolder<IMDInternalImport> pInternal; + IfFailThrow(GetMDInternalInterfaceFromPublic(dependency->pImport, IID_IMDInternalImport, + (void**)&pInternal)); + StackScratchBuffer buffer; + const ANSI * ansi = tempName.GetANSI(buffer); + ansi = PrettyPrintSig(pvSigBlob, cbSigBlob, ansi, &prettySig, pInternal, NULL); + tempName.SetANSI( ansi ); + } + buf.Append(tempName); + } +} +void NativeImageDumper::WriteElementMethodDesc( const char * name, + PTR_MethodDesc md ) +{ + if( md == NULL ) + { + m_display->WriteElementPointer( name, NULL ); + } + else + { + TempBuffer buf; + MethodDescToString( md, buf ); + m_display->WriteElementPointerAnnotated( name, DPtrToPreferredAddr(md), + (const WCHAR*) buf ); + } +} +void NativeImageDumper::FieldDescToString( PTR_FieldDesc fd, SString& buf ) +{ + FieldDescToString( fd, mdFieldDefNil, buf ); +} +void NativeImageDumper::FieldDescToString( PTR_FieldDesc fd, mdFieldDef tok, + SString& buf ) +{ + IF_OPT(DISABLE_NAMES) + { + buf.Append( W("Disabled") ); + return; + } + if( fd == NULL ) + { + if( tok == mdFieldDefNil ) + buf.Append( W("mdFieldDefNil") ); + else + AppendTokenName( tok, buf ); + } + else + { + _ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(fd))); + IMetaDataImport2 * importMD = NULL; + if( !isInRange(PTR_TO_TADDR(fd)) ) + { + const Dependency * dependency = GetDependencyFromFD(fd); + _ASSERTE(dependency); + AppendTokenName( dependency->entry->dwAssemblyRef, buf, + m_manifestImport ); + buf.Append(W("!")); + importMD = dependency->pImport; + _ASSERTE(importMD); + + } + else + { + importMD = m_import; + } + AppendTokenName( fd->GetMemberDef(), buf, importMD ); + } +} + +void NativeImageDumper::DoWriteFieldAsHex( const char * name, unsigned offset, + unsigned fieldSize, PTR_BYTE ptr, + unsigned dataLen ) +{ + TempBuffer buffer; + for( unsigned i = 0; i < dataLen; ++i ) + { + unsigned char b = ptr[i]; + buffer.AppendPrintf( W("%02x%02x"), (b & 0xf0) >> 4, b & 0xf ); + } + if( offset == UINT_MAX ) + { + m_display->WriteElementStringW( name, (const WCHAR *)buffer ); + } + else + { + m_display->WriteFieldStringW( name, offset, fieldSize, + (const WCHAR *)buffer ); + } +} +void NativeImageDumper::WriteElementMDToken( const char * name, mdToken token ) +{ + DoWriteFieldMDToken( name, UINT_MAX, UINT_MAX, token ); +} +void NativeImageDumper::DoWriteFieldMDToken( const char * name, unsigned offset, + unsigned fieldSize, mdToken token, + IMetaDataImport2 * import ) +{ + TempBuffer buf; + if( RidFromToken(token) == mdTokenNil ) + { + AppendNilToken( token, buf ); + } + else + { + AppendToken( token, buf, import ); + } + if( UINT_MAX == offset ) + m_display->WriteElementEnumerated( name, token, (const WCHAR *)buf ); + else + { + m_display->WriteFieldEnumerated(name, offset, fieldSize, token, + (const WCHAR*)buf); + } +} + +void NativeImageDumper::WriteElementMethodTable( const char * name, + PTR_MethodTable mt ) +{ + DoWriteFieldMethodTable( name, UINT_MAX, UINT_MAX, mt ); +} +void NativeImageDumper::DoWriteFieldMethodTable( const char * name, + unsigned offset, + unsigned fieldSize, + PTR_MethodTable mt ) +{ + if( mt == NULL ) + { + if( UINT_MAX == offset ) + m_display->WriteElementPointer( name, NULL ); + else + m_display->WriteFieldPointer( name, offset, fieldSize, NULL ); + } + else if( DoWriteFieldAsFixup( name, offset, fieldSize, PTR_TO_TADDR(mt) ) ) + { + return; + } + else + { + TempBuffer buf; + MethodTableToString( mt, buf ); + if( UINT_MAX == offset ) + { + + m_display->WriteElementPointerAnnotated( name, + DPtrToPreferredAddr(mt), + (const WCHAR*) buf ); + } + else + { + m_display->WriteFieldPointerAnnotated( name, offset, fieldSize, + DPtrToPreferredAddr(mt), + (const WCHAR*) buf ); + } + } +} + +const char * s_VTSCallbackNames[] = +{ +#define VTSCB_ENTRY(x) # x + VTSCB_ENTRY(VTS_CALLBACK_ON_SERIALIZING), + VTSCB_ENTRY(VTS_CALLBACK_ON_SERIALIZED), + VTSCB_ENTRY(VTS_CALLBACK_ON_DESERIALIZING), + VTSCB_ENTRY(VTS_CALLBACK_ON_DESERIALIZED), +#undef VTSCB_ENTRY +}; +void NativeImageDumper::DumpFieldDesc( PTR_FieldDesc fd, const char * name ) +{ + DisplayStartStructure( name, DPtrToPreferredAddr(fd), sizeof(*fd), + ALWAYS ); + WriteFieldMethodTable( m_pMTOfEnclosingClass, + fd->GetApproxEnclosingMethodTable(), FieldDesc, ALWAYS ); + m_display->WriteFieldUInt( "m_mb", offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->GetMemberDef() ); + m_display->WriteFieldFlag( "m_isStatic", + offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->m_isStatic ); + m_display->WriteFieldFlag( "m_isThreadLocal", + offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->m_isThreadLocal ); +#if defined(FEATURE_REMOTING) + m_display->WriteFieldFlag( "m_isContextLocal", + offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->m_isContextLocal ); +#endif + m_display->WriteFieldFlag( "m_isRVA", offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->m_isRVA ); + + { + TempBuffer buf; + EnumFlagsToString( fd->m_prot, s_CorFieldAttr, + _countof(s_CorFieldAttr), W(" "), buf ); + m_display->WriteFieldEnumerated( "m_prot", + offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->m_prot, (const WCHAR *)buf ); + } + m_display->WriteFieldFlag( "m_requiresFullMbValue", + offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->m_requiresFullMbValue ); + m_display->WriteFieldInt( "m_dwOffset", + offsetof(FieldDesc, m_dword2), + fieldsize(FieldDesc, m_dword2), + fd->m_dwOffset ); + DoWriteFieldCorElementType( "m_type", + offsetof(FieldDesc, m_dword2), + fieldsize(FieldDesc, m_dword2), + (CorElementType)fd->m_type ); +#ifdef _DEBUG + WriteFieldStr( m_debugName, PTR_BYTE(TO_TADDR(fd->m_debugName)), + FieldDesc, ALWAYS ); +#endif + DisplayEndStructure( ALWAYS ); //name +} +#if defined(FEATURE_REMOTING) +NativeImageDumper::EnumMnemonics s_XADOptFlags[] = +{ +#define XAD_ENTRY(x) NativeImageDumper::EnumMnemonics( RemotableMethodInfo:: x, W(#x) ) + XAD_ENTRY(XAD_FLAGS_INITIALIZED), + XAD_ENTRY(XAD_NOT_OPTIMIZABLE), + XAD_ENTRY(XAD_BLITTABLE_ARGS), + XAD_ENTRY(XAD_BLITTABLE_RET), + + //this is actually the OR of the three below this. It is a "flag" so + //it gets cleared out. + XAD_ENTRY(XAD_RET_GC_REF), + XAD_ENTRY(XAD_RET_FLOAT), + XAD_ENTRY(XAD_RET_DOUBLE), +#ifdef FEATURE_HFA + XAD_ENTRY(XAD_RET_HFA_TYPE), +#endif + + XAD_ENTRY(XAD_METHOD_IS_VIRTUAL), + XAD_ENTRY(XAD_ARGS_HAVE_A_FLOAT), + +#undef XAD_ENTRY +}; +#endif + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +void +NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name, + PTR_Module module ) +{ + _ASSERTE(NULL != mt); + TADDR start, end; + bool haveCompleteExtents = true; + PTR_EEClass clazz = NULL; + if( !mt->IsCanonicalMethodTable() && CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt->GetCanonicalMethodTable())) ) + { + /* REVISIT_TODO Wed 02/01/2006 + * GetExtent requires the class in order to compute GetInstAndDictSize. + * If the EEClass isn't present, I cannot compute the size. If we are + * in this case, skip all of the generic dictionaries. + */ + haveCompleteExtents = false; + TempBuffer buf; + MethodTableToString( mt, buf ); + m_display->ErrorPrintF( "WARNING! MethodTable %S is generic but is not hard bound to its EEClass. Cannot compute generic dictionary sizes.\n", (const WCHAR *)buf ); + } + else if( !m_isMscorlibHardBound ) + { + /* REVISIT_TODO Mon 8/20/2007 + * If we're not hard bound to mscorlib, most things don't work. They depend on knowing what + * g_pObjectClass is. Without the hard binding to mscorlib, I can't figure that out. + */ + haveCompleteExtents = false; + } + if( haveCompleteExtents ) + { + mt->GetSavedExtent(&start, &end); + clazz = mt->GetClass(); + } + else + { + start = PTR_TO_TADDR(mt); + end = start + sizeof(*mt); + } + IF_OPT(METHODTABLES) + { + m_display->StartStructureWithNegSpace( name, DPtrToPreferredAddr(mt), + DataPtrToDisplay(start), end - start ); + } + + IF_OPT(METHODTABLES) + { + { + TempBuffer buf; + MethodTableToString( mt, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR *)buf, ALWAYS ); + } + if( mt->ContainsPointers() ) + { + PTR_CGCDesc cgc = CGCDesc::GetCGCDescFromMT(mt); + unsigned size = (unsigned)cgc->GetSize(); + /* REVISIT_TODO Tue 12/13/2005 + * Does anyone actually care about what's inside here? + */ + m_display->WriteFieldEmpty( "CGCDesc", ~size + 1, size ); + } + } + + /* XXX Mon 10/24/2005 + * The MT might have a component size as the low WORD of the m_dwFlags + * field, if it doesn't then that field instead represents a number of + * flags, which we know as the "low flags" + */ + if (mt->HasComponentSize()) + { + DisplayWriteElementInt( "ComponentSize", mt->RawGetComponentSize(), + METHODTABLES ); + } + else + { + DisplayWriteFieldEnumerated( m_dwFlags, mt->m_dwFlags & 0xFFFF, MethodTable, + s_MTFlagsLow, W(", "), METHODTABLES ); + } + + /* XXX Fri 10/07/2005 + * The low WORD of the flags is used for either a component size or flags + * (see above), the high WORD is always flags. If this changes then this + * might be busted. + */ + DisplayWriteFieldEnumerated( m_dwFlags, mt->m_dwFlags & ~0xFFFF, MethodTable, + s_MTFlagsHigh, W(", "), METHODTABLES ); + + DisplayWriteFieldInt( m_BaseSize, mt->m_BaseSize, MethodTable, + METHODTABLES ); + + DisplayWriteFieldEnumerated( m_wFlags2, mt->m_wFlags2, MethodTable, + s_MTFlags2, W(", "), METHODTABLES ); + + DisplayWriteFieldInt( m_wToken, mt->m_wToken, MethodTable, + METHODTABLES ); + DisplayWriteFieldInt( m_wNumVirtuals, mt->m_wNumVirtuals, MethodTable, + METHODTABLES ); + DisplayWriteFieldInt( m_wNumInterfaces, mt->m_wNumInterfaces, MethodTable, + METHODTABLES ); + + + + PTR_MethodTable parent = mt->m_pParentMethodTable; + if( parent == NULL ) + { + DisplayWriteFieldPointer( m_pParentMethodTable, NULL, MethodTable, + METHODTABLES ); + } + else + { + IF_OPT(METHODTABLES) + { + DoWriteFieldMethodTable( "m_pParentMethodTable", + offsetof(MethodTable, m_pParentMethodTable), + fieldsize(MethodTable, m_pParentMethodTable), + mt->GetParentMethodTable() ); + } + } + DisplayWriteFieldPointer( m_pLoaderModule, + DPtrToPreferredAddr(mt->GetLoaderModule()), + MethodTable, METHODTABLES ); + + PTR_MethodTableWriteableData wd = mt->m_pWriteableData; + _ASSERTE(wd != NULL); + DisplayStartStructureWithOffset( m_pWriteableData, DPtrToPreferredAddr(wd), + sizeof(*wd), MethodTable, METHODTABLES ); + DisplayWriteFieldEnumerated( m_dwFlags, wd->m_dwFlags, + MethodTableWriteableData, s_WriteableMTFlags, + W(", "), METHODTABLES ); + DisplayWriteFieldPointer( m_hExposedClassObject, + DataPtrToDisplay(wd->m_hExposedClassObject), + MethodTableWriteableData, METHODTABLES ); + _ASSERTE(wd->m_hExposedClassObject == 0); + DisplayEndStructure( METHODTABLES ); //m_pWriteableData + + if( !mt->IsCanonicalMethodTable() ) + { + WriteFieldMethodTable( m_pCanonMT, mt->GetCanonicalMethodTable(), + MethodTable, METHODTABLES ); + } + else + { + DisplayWriteFieldPointer( m_pEEClass, DPtrToPreferredAddr(mt->GetClass()), + MethodTable, METHODTABLES ); + } + + if( mt->IsArray() ) + { + WriteFieldTypeHandle( m_ElementTypeHnd, + mt->GetApproxArrayElementTypeHandle(), + MethodTable, METHODTABLES ); + } + + if( mt->HasPerInstInfo() && haveCompleteExtents ) + { + //print out the generics dictionary info, and then print out + //the contents of those dictionaries. + PTR_GenericsDictInfo di = mt->GetGenericsDictInfo(); + _ASSERTE(NULL != di); + + DisplayStartStructure("GenericsDictInfo", DPtrToPreferredAddr(di), sizeof(*di), METHODTABLES); + + DisplayWriteFieldInt( m_wNumDicts, di->m_wNumDicts, GenericsDictInfo, + METHODTABLES ); + DisplayWriteFieldInt( m_wNumTyPars, di->m_wNumTyPars, + GenericsDictInfo, METHODTABLES); + DisplayEndStructure( METHODTABLES ); //GenericsDictInfo + + + DPTR(PTR_Dictionary) perInstInfo = mt->GetPerInstInfo(); + + DisplayStartStructure( "PerInstInfo", + DPtrToPreferredAddr(perInstInfo), + mt->GetPerInstInfoSize(), + METHODTABLES ); + /* XXX Tue 10/11/2005 + * Only dump this type's dictionary, rather than the inherited + * dictionaries. (there are multiple entries in m_pPerInstInfo, but + * only print the last one, which is the one for this class). + * cloned from Genericdict.cpp + */ + PTR_Dictionary currentDictionary(mt->GetDictionary()); + if( currentDictionary != NULL ) + { + PTR_DictionaryEntry entry(currentDictionary->EntryAddr(0)); + + PTR_DictionaryLayout layout( clazz->GetDictionaryLayout() ); + + DisplayStartStructure( "Dictionary", + DPtrToPreferredAddr(currentDictionary), + //if there is a layout, use it to compute + //the size, otherwise there is just the one + //entry. + DictionaryLayout::GetFirstDictionaryBucketSize(mt->GetNumGenericArgs(), layout), + METHODTABLES ); + + DisplayStartArrayWithOffset( m_pEntries, NULL, Dictionary, + METHODTABLES ); + + /* REVISIT_TODO Thu 12/15/2005 + * use VERBOSE_TYPES here. + */ + _ASSERTE(CHECK_OPT(METHODTABLES)); + + //for each generic arg, there is a type handle slot + for( unsigned i = 0; i < mt->GetNumGenericArgs(); ++i ) + DumpDictionaryEntry("Entry", TypeHandleSlot, entry + i); + + //now check for a layout. If it is present, then there are more + //entries. + if( layout != NULL && (layout->GetNumUsedSlots() > 0) ) + { + unsigned numUsedSlots = layout->GetNumUsedSlots(); + for( unsigned i = 0; i < numUsedSlots; ++i ) + { + //DictionaryLayout::GetEntryLayout + PTR_DictionaryEntryLayout entryLayout(layout->GetEntryLayout(i)); + + //Dictionary::GetSlotAddr + PTR_DictionaryEntry ent(currentDictionary->EntryAddr(mt->GetNumGenericArgs() + i)); + + DumpDictionaryEntry( "Entry", entryLayout->GetKind(), ent ); + } + } + if( layout != NULL ) + { + /* REVISIT_TODO Thu 12/15/2005 + * Where is this data? + */ + } + DisplayEndArray( "Total Per instance Info", + METHODTABLES ); //m_pEntries + DisplayEndStructure( METHODTABLES ); //Dictionary + } + DisplayEndStructure( METHODTABLES ); //m_pPerInstInfo + } + +#ifdef _DEBUG + WriteFieldStr( debug_m_szClassName, + PTR_BYTE(TO_TADDR(mt->debug_m_szClassName)), MethodTable, + METHODTABLES ); +#if 0 //already dumping the optional member + PTR_InterfaceInfo imap( TO_TADDR(mt->m_pIMapDEBUG) ); + /* REVISIT_TODO Mon 10/24/2005 + * Dump interface map + */ + DisplayStartArrayWithOffset( m_pIMapDEBUG, NULL, MethodTable, + METHODTABLES ); + DisplayEndArray( "Total Interfaces", METHODTABLES ); +#endif +#endif + + if( mt->HasDispatchMapSlot() ) + { + PTR_DispatchMap dispatchMap(mt->GetDispatchMap()); + + DisplayStartStructure( "DispatchMap", + DPtrToPreferredAddr(dispatchMap), + DispatchMap::GetObjectSize(dispatchMap->GetMapSize()), + METHODTABLES ); + + IF_OPT(VERBOSE_TYPES ) + { + DispatchMap::Iterator iter(mt); + DisplayStartArray( "DispatchMap", NULL, VERBOSE_TYPES ); + while( iter.Next() ) + { + DispatchMapEntry * ent = iter.Entry(); + + DisplayStartElement( "Entry", METHODTABLES ); + DisplayStartVStructure( "TypeID", METHODTABLES ); + DispatchMapTypeID typeID = ent->GetTypeID(); + if( typeID.IsThisClass() ) + DisplayWriteElementFlag("IsThisClass", true, METHODTABLES ); + else if( typeID.IsImplementedInterface() ) + { + DisplayWriteElementFlag( "IsImplementedInterface", + true, METHODTABLES ); + DisplayWriteElementInt( "GetInterfaceNum", + typeID.GetInterfaceNum(), METHODTABLES ); + } + DisplayEndStructure( METHODTABLES ); //TypeID + m_display->WriteElementInt( "SlotNumber", + ent->GetSlotNumber() ); + DisplayWriteElementInt( "TargetSlotNumber", + ent->GetSlotNumber(), METHODTABLES ); + + m_display->EndElement(); //Entry + } + //DispatchMap + DisplayEndArray("Total Dispatch Map Entries", METHODTABLES ); + } + else + { + CoverageRead(PTR_TO_TADDR(dispatchMap), + DispatchMap::GetObjectSize(dispatchMap->GetMapSize())); + } + + DisplayEndStructure( METHODTABLES ); //DispatchMap + } + + IF_OPT( METHODTABLES ) + { + m_display->StartStructureWithOffset("Vtable", + mt->GetVtableOffset(), + mt->GetNumVtableIndirections() * sizeof(PTR_PCODE), + DataPtrToDisplay(PTR_TO_TADDR(mt) + mt->GetVtableOffset()), + mt->GetNumVtableIndirections() * sizeof(PTR_PCODE)); + + + MethodTable::VtableIndirectionSlotIterator itIndirect = mt->IterateVtableIndirectionSlots(); + while (itIndirect.Next()) + { + SlotChunk sc; + sc.addr = itIndirect.GetIndirectionSlot(); + sc.nSlots = (WORD)itIndirect.GetNumSlots(); + m_discoveredSlotChunks.AppendEx(sc); + } + + IF_OPT(VERBOSE_TYPES) + { + DisplayStartList( W("[%-4s]: %s (%s)"), ALWAYS ); + for( unsigned i = 0; i < mt->GetNumVtableIndirections(); ++i ) + { + DisplayStartElement( "Slot", ALWAYS ); + DisplayWriteElementInt( "Index", i, ALWAYS ); + PTR_PCODE tgt = mt->GetVtableIndirections()[i]; + DisplayWriteElementPointer( "Pointer", + DataPtrToDisplay(dac_cast<TADDR>(tgt)), + ALWAYS ); + DisplayWriteElementString( "Type", "chunk indirection", + ALWAYS ); + DisplayEndElement( ALWAYS ); //Slot + } + + if (mt->HasNonVirtualSlotsArray()) + { + DisplayStartElement( "Slot", ALWAYS ); + DisplayWriteElementInt( "Index", -1, ALWAYS ); + PTR_PCODE tgt = mt->GetNonVirtualSlotsArray(); + DisplayWriteElementPointer( "Pointer", + DataPtrToDisplay(dac_cast<TADDR>(tgt)), + ALWAYS ); + DisplayWriteElementString( "Type", "non-virtual chunk indirection", + ALWAYS ); + DisplayEndElement( ALWAYS ); //Slot + + SlotChunk sc; + sc.addr = tgt; + sc.nSlots = (mt->GetNumVtableSlots() - mt->GetNumVirtuals()); + m_discoveredSlotChunks.AppendEx(sc); + } + else if (mt->HasSingleNonVirtualSlot()) + { + DumpSlot((unsigned)-1, mt->GetSlot(mt->GetNumVirtuals())); + } + + DisplayEndList( ALWAYS ); //vtable + } + else + { + CoverageRead( PTR_TO_TADDR(mt) + mt->GetVtableOffset(), + mt->GetNumVtableIndirections() * sizeof(PTR_PCODE) ); + + if (mt->HasNonVirtualSlotsArray()) + { + CoverageRead( PTR_TO_TADDR(mt->GetNonVirtualSlotsArray()), + mt->GetNonVirtualSlotsArraySize() ); + } + + } + DisplayEndStructure(ALWAYS); //Vtable + } + + if( mt->HasInterfaceMap() && CHECK_OPT(METHODTABLES) ) + { + PTR_InterfaceInfo ifMap = mt->GetInterfaceMap(); + m_display->StartArrayWithOffset( "InterfaceMap", + offsetof(MethodTable, m_pMultipurposeSlot2), + sizeof(void*), + NULL ); + for( unsigned i = 0; i < mt->GetNumInterfaces(); ++i ) + { + PTR_InterfaceInfo info = ifMap + i; + DisplayStartStructure( "InterfaceInfo_t", DPtrToPreferredAddr(info), + sizeof(*info), METHODTABLES ); + WriteFieldMethodTable( m_pMethodTable, + info->GetMethodTable(), + InterfaceInfo_t, METHODTABLES ); + DisplayEndStructure( METHODTABLES ); //InterfaceInfo_t + } + DisplayEndArray( "Total InterfaceInfos", + METHODTABLES ); //InterfaceMap + } + + //rest of the optional members + + //GenericStatics comes after the generic dictionaries. So if I + //don't have extents, I can't print them. + if( haveCompleteExtents && + mt->HasGenericsStaticsInfo() && + CHECK_OPT(METHODTABLES) + ) + { + PTR_GenericsStaticsInfo genStatics = mt->GetGenericsStaticsInfo(); + m_display->StartStructureWithOffset( "OptionalMember_" + "GenericsStaticsInfo", + mt->GetOffsetOfOptionalMember(MethodTable::OptionalMember_GenericsStaticsInfo), + sizeof(*genStatics), + DPtrToPreferredAddr(genStatics), + sizeof(*genStatics) ); + + PTR_FieldDesc fieldDescs = genStatics->m_pFieldDescs; + if( fieldDescs == NULL ) + { + DisplayWriteFieldPointer( m_pFieldDescs, NULL, GenericsStaticsInfo, + ALWAYS ); + } + else + { + DisplayStartArrayWithOffset( m_pFieldDescs, NULL, + GenericsStaticsInfo, ALWAYS ); + _ASSERTE(clazz == GetClassFromMT(mt)); + for( int i = 0; i < clazz->GetNumStaticFields(); ++i ) + { + PTR_FieldDesc fd = fieldDescs + i; + DumpFieldDesc( fd, "FieldDesc" ); + } + DisplayEndArray( "Total Static Fields", ALWAYS ); // m_pFieldDescs + } + DisplayWriteFieldUInt( m_DynamicTypeID, (DWORD)genStatics->m_DynamicTypeID, + GenericsStaticsInfo, METHODTABLES ); + + DisplayEndStructure( METHODTABLES );//OptionalMember_GenericsStaticsInfo + + } + +#ifdef FEATURE_COMINTEROP + if (haveCompleteExtents && + mt->HasGuidInfo() && + CHECK_OPT(METHODTABLES) + ) + { + PTR_GuidInfo guidInfo(*mt->GetGuidInfoPtr()); + + if (guidInfo != NULL) + { + m_display->StartStructureWithOffset( "OptionalMember_GuidInfo", + mt->GetOffsetOfOptionalMember(MethodTable::OptionalMember_GuidInfo), + sizeof(void*), + DPtrToPreferredAddr(guidInfo), + sizeof(GuidInfo) + ); + TempBuffer buf; + GuidToString( guidInfo->m_Guid, buf ); + DisplayWriteFieldStringW( m_Guid, (const WCHAR *)buf, GuidInfo, + ALWAYS ); + DisplayWriteFieldFlag( m_bGeneratedFromName, + guidInfo->m_bGeneratedFromName, + GuidInfo, ALWAYS ); + DisplayEndStructure( ALWAYS ); // OptionalMember_GuidInfo + } + } + + if (haveCompleteExtents && + mt->HasCCWTemplate() + && CHECK_OPT(METHODTABLES) + ) + { + PTR_ComCallWrapperTemplate ccwTemplate(TO_TADDR(*mt->GetCCWTemplatePtr())); + m_display->WriteFieldPointer( "OptionalMember_CCWTemplate", + mt->GetOffsetOfOptionalMember(MethodTable::OptionalMember_CCWTemplate), + sizeof(void *), + DPtrToPreferredAddr(ccwTemplate) + ); + } +#endif // FEATURE_COMINTEROP + +#if defined(FEATURE_REMOTING) + //RemotingVtsInfo comes after the generic dictionaries. So if I + //don't have extents, I can't print them. + if(haveCompleteExtents && + mt->HasRemotingVtsInfo() + && CHECK_OPT(METHODTABLES) + ) + { + _ASSERTE(clazz == GetClassFromMT(mt)); + /* REVISIT_TODO Tue 12/13/2005 + * Is this right? is m_wNumStaticFields actually the number of static + * fields? + */ + unsigned numFields = (unsigned)(CountFields(mt) + - clazz->GetNumStaticFields()); + PTR_RemotingVtsInfo vts = *mt->GetRemotingVtsInfoPtr(); + m_display->StartStructureWithOffset( "OptionalMember_RemotingVtsInfo", + mt->GetOffsetOfOptionalMember(MethodTable::OptionalMember_RemotingVtsInfo), + sizeof(void*), + DPtrToPreferredAddr(vts), + RemotingVtsInfo::GetSize(numFields) + ); + + DisplayStartArrayWithOffset( m_pCallbacks, W("%-30s: %s"), + RemotingVtsInfo, ALWAYS ); + for( int i = 0; i < _countof(vts->m_pCallbacks); ++i ) + { + PTR_MethodDesc md( vts->m_pCallbacks[i].GetValue() ); + DisplayStartElement( "Callback", ALWAYS ); + DisplayWriteElementString( "CallbackType", s_VTSCallbackNames[i], + ALWAYS ); + if (CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(md))) + { + DoWriteFieldMethodDesc( "MethodDesc", UINT_MAX, UINT_MAX, md ); + } + else + { + WriteElementMethodDesc( "MethodDesc", md ); + } + DisplayEndElement( ALWAYS ); //Callback + } + DisplayEndArray( NULL, ALWAYS ); //m_pCallbacks +#ifdef _DEBUG + DisplayWriteFieldInt( m_dwNumFields, vts->m_dwNumFields, + RemotingVtsInfo, ALWAYS ); +#endif + /* REVISIT_TODO Tue 12/13/2005 + * Is there any reason to dump this map? + */ + m_display->WriteFieldEmpty("m_rFieldTypes", + offsetof(RemotingVtsInfo, m_rFieldTypes), + RemotingVtsInfo::GetSize(numFields) + - offsetof(RemotingVtsInfo, m_rFieldTypes) ); + DisplayEndStructure( ALWAYS ); //OptionalMember_RemotingVtsInfo + } + + //RemotableMethodInfo comes after the generic dictionaries. So if I + //don't have extents, I can't print them. + if(haveCompleteExtents && + mt->HasRemotableMethodInfo() && + CHECK_OPT(METHODTABLES) + ) + { + PTR_CrossDomainOptimizationInfo cdOpt = *mt->GetRemotableMethodInfoPtr(); + if( cdOpt == NULL ) + { + _ASSERTE(mt->GetNumVtableSlots() == 0 ); + m_display->WriteElementPointer("OptionalMember_RemotableMethodInfo", + NULL); + } + else + { + _ASSERTE(mt->GetNumVtableSlots() > 0 ); + /* REVISIT_TODO Tue 12/13/2005 + * One liner copied from CrossDomainCalls.cpp + */ + unsigned size = sizeof(RemotableMethodInfo) * mt->GetNumVtableSlots(); + //one remotable method info for each method. + m_display->StartStructureWithOffset( "OptionalMember_RemotableMethodInfo", + mt->GetOffsetOfOptionalMember(MethodTable::OptionalMember_RemotableMethodInfo), + sizeof(void*), + DPtrToPreferredAddr(cdOpt), + size ); + m_display->StartArrayWithOffset( "m_rmi", + offsetof(CrossDomainOptimizationInfo, + m_rmi), + mt->GetNumVtableSlots() + * sizeof(RemotableMethodInfo), NULL ); + PTR_RemotableMethodInfo rmi( PTR_HOST_MEMBER_TADDR(CrossDomainOptimizationInfo, cdOpt, m_rmi) ); + for( unsigned i = 0; i < mt->GetNumVtableSlots(); ++i ) + { + PTR_RemotableMethodInfo current = rmi + i; + DisplayStartStructure( "RemotableMethodInfo", + DPtrToPreferredAddr(current), + sizeof(*current), ALWAYS ); + DisplayWriteFieldEnumerated( m_OptFlags, current->m_OptFlags, + RemotableMethodInfo, s_XADOptFlags, + W(", "), ALWAYS ); + DisplayEndStructure( ALWAYS ); //RemotableMethodInfo + } + DisplayEndArray( "Total RemotableMethodInfos", ALWAYS ); //m_rmi + DisplayEndStructure( ALWAYS ); // OptionalMember_RemotableMethodInfo + } + } + + //ContextStatics comes after the generic dictionaries. So if I + //don't have extents, I can't print them. + if(haveCompleteExtents && + mt->HasContextStatics() && + CHECK_OPT(METHODTABLES) + ) + { + PTR_ContextStaticsBucket statics = + *(mt->GetContextStaticsBucketPtr()); + m_display->StartStructureWithOffset( "OptionalMember_ContextStatics", + mt->GetOffsetOfOptionalMember(MethodTable::OptionalMember_ContextStatics), + sizeof(void*), + DPtrToPreferredAddr(statics), + sizeof(*statics) ); + +#define HANDLE_FIELD(field) \ + DisplayWriteFieldInt( field, statics->field, \ + ContextStaticsBucket, ALWAYS ) + HANDLE_FIELD(m_dwContextStaticsOffset); + HANDLE_FIELD(m_wContextStaticsSize); +#undef HANDLE_FIELD + DisplayEndStructure( ALWAYS ); //OptionalMember_ContextStatics + } +#endif + DisplayEndStructure( METHODTABLES ); //MethodTable +} // NativeImageDumper::DumpMethodTable +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +void +NativeImageDumper::DumpMethodTableSlotChunk( PTR_PCODE slotChunk, COUNT_T numSlots ) +{ + IF_OPT( METHODTABLES ) + { + DisplayStartStructure( "MethodTableSlotChunk", DPtrToPreferredAddr(slotChunk), numSlots * sizeof(PCODE), + METHODTABLES ); + + IF_OPT(VERBOSE_TYPES) + { + DisplayStartList( W("[%-4s]: %s (%s)"), ALWAYS ); + for( unsigned i = 0; i < numSlots; ++i ) + { + DumpSlot(i, slotChunk[i]); + } + DisplayEndList( ALWAYS ); //Slot list + } + else + CoverageRead( PTR_TO_TADDR(slotChunk), + numSlots * sizeof(PCODE) ); + DisplayEndStructure(ALWAYS); //Slot chunk + } +} + + +void +NativeImageDumper::DumpSlot( unsigned index, PCODE tgt ) +{ + IF_OPT(VERBOSE_TYPES) + { + DisplayStartElement( "Slot", ALWAYS ); + DisplayWriteElementInt( "Index", index, ALWAYS ); + DisplayWriteElementPointer( "Pointer", + DataPtrToDisplay(tgt), + ALWAYS ); + if( !isInRange(TO_TADDR(tgt)) ) + { + DisplayWriteElementString( "Type", "external", + ALWAYS ); + } + else if( isPrecode(TO_TADDR(tgt)) + && Precode::IsValidType(PTR_Precode(TO_TADDR(tgt))->GetType()) ) + { + PTR_Precode precode(TO_TADDR(tgt)); + DisplayWriteElementString( "Type", "precode", + ALWAYS ); + //DumpPrecode( precode, module ); + } + else + { + DisplayWriteElementString( "Type", "code pointer", + ALWAYS ); + } + DisplayEndElement( ALWAYS ); //Slot + } +} + +NativeImageDumper::EnumMnemonics NativeImageDumper::s_SSMDExtendedFlags[] = +{ +#define SSMD_ENTRY(x) NativeImageDumper::EnumMnemonics( x, W(#x) ) + +#define SSMD_ACCESS_ENTRY(x) NativeImageDumper::EnumMnemonics( x, mdMemberAccessMask, W(#x) ) + SSMD_ACCESS_ENTRY(mdPrivateScope), + SSMD_ACCESS_ENTRY(mdPrivate), + SSMD_ACCESS_ENTRY(mdFamANDAssem), + SSMD_ACCESS_ENTRY(mdAssem), + SSMD_ACCESS_ENTRY(mdFamily), + SSMD_ACCESS_ENTRY(mdFamORAssem), + SSMD_ACCESS_ENTRY(mdPublic), +#undef SSMD_ACCESS_ENTRY + + SSMD_ENTRY(mdStatic), + SSMD_ENTRY(mdFinal), + SSMD_ENTRY(mdVirtual), + SSMD_ENTRY(mdHideBySig), + + SSMD_ENTRY(mdVtableLayoutMask), + SSMD_ENTRY(mdNewSlot), + + SSMD_ENTRY(mdCheckAccessOnOverride), + SSMD_ENTRY(mdAbstract), + SSMD_ENTRY(mdSpecialName), + + SSMD_ENTRY(mdPinvokeImpl), + SSMD_ENTRY(mdUnmanagedExport), + + SSMD_ENTRY(mdRTSpecialName), + SSMD_ENTRY(mdHasSecurity), + SSMD_ENTRY(mdRequireSecObject), + + NativeImageDumper::EnumMnemonics( DynamicMethodDesc::nomdILStub, + W("nomdILStub") ), + NativeImageDumper::EnumMnemonics( DynamicMethodDesc::nomdLCGMethod, + W("nomdLCGMethod") ), +#undef SSMD_ENTRY +}; + +//maps MethodClassification to a name for a MethodDesc +const char * const s_MDTypeName[] = +{ + "MethodDesc", //mcIL + "FCallMethodDesc", //mcFCall + "NDirectMethodDesc", //mcNDirect + "EEImplMethodDesc", //mcEEImpl - //public StoredSigMethodDesc + "ArrayMethodDesc", //mcArray - //public StoredSigMethodDesc + "InstantiatedMethodDesc", //mcInstantiated +#if defined(FEATURE_COMINTEROP) + "ComPlusCallMethodDesc", //mcComInterop +#else + "", +#endif + "DynamicMethodDesc", //mcDynamic -- //public StoredSigMethodDesc +}; + +unsigned s_MDSizes[] = +{ + sizeof(MethodDesc), //mcIL + sizeof(FCallMethodDesc), //mcFCall + sizeof(NDirectMethodDesc), //mcNDirect + sizeof(EEImplMethodDesc), //mcEEImpl + sizeof(ArrayMethodDesc), //mcArray + sizeof(InstantiatedMethodDesc), //mcInstantiated +#if defined(FEATURE_COMINTEROP) + sizeof(ComPlusCallMethodDesc), //mcComInterop +#else + 0, +#endif + sizeof(DynamicMethodDesc), //mcDynamic +}; + +static NativeImageDumper::EnumMnemonics g_NDirectFlags[] = +{ +#define NDF_ENTRY(x) NativeImageDumper::EnumMnemonics( NDirectMethodDesc:: x, W(#x) ) + NDF_ENTRY(kEarlyBound), + NDF_ENTRY(kHasSuppressUnmanagedCodeAccess), + NDF_ENTRY(kIsMarshalingRequiredCached), + NDF_ENTRY(kCachedMarshalingRequired), + NDF_ENTRY(kNativeAnsi), + NDF_ENTRY(kLastError), + NDF_ENTRY(kNativeNoMangle), + NDF_ENTRY(kVarArgs), + NDF_ENTRY(kStdCall), + NDF_ENTRY(kThisCall), + NDF_ENTRY(kIsQCall), + NDF_ENTRY(kHasCopyCtorArgs), + NDF_ENTRY(kStdCallWithRetBuf), +#undef NDF_ENTRY +}; +NativeImageDumper::EnumMnemonics NativeImageDumper::s_IMDFlags[] = +{ +#define IMD_ENTRY(x) NativeImageDumper::EnumMnemonics( InstantiatedMethodDesc:: x, W(#x) ) + +#define IMD_KIND_ENTRY(x) NativeImageDumper::EnumMnemonics( InstantiatedMethodDesc:: x, InstantiatedMethodDesc::KindMask, W(#x) ) + IMD_KIND_ENTRY(GenericMethodDefinition), + IMD_KIND_ENTRY(UnsharedMethodInstantiation), + IMD_KIND_ENTRY(SharedMethodInstantiation), + IMD_KIND_ENTRY(WrapperStubWithInstantiations), +#undef IMD_KIND_ENTRY + +#ifdef EnC_SUPPORTED + // Method is a new virtual function added through EditAndContinue. + IMD_ENTRY(EnCAddedMethod), +#endif // EnC_SUPPORTED + + IMD_ENTRY(Unrestored), + +#ifdef FEATURE_COMINTEROP + IMD_ENTRY(HasComPlusCallInfo), +#endif // FEATURE_COMINTEROP + +#undef IMD_ENTRY +}; + +void NativeImageDumper::DumpPrecode( PTR_Precode precode, PTR_Module module ) +{ + _ASSERTE(isPrecode(PTR_TO_TADDR(precode))); + + PrecodeType pType = precode->GetType(); + switch(pType) + { +#define DISPLAY_PRECODE(type) \ + IF_OPT_AND(PRECODES, METHODDESCS) \ + { \ + PTR_ ## type p( precode->As ## type () ); \ + DisplayStartStructure( # type, \ + DPtrToPreferredAddr(p), \ + sizeof(*p), ALWAYS ); \ + WriteFieldMethodDesc( m_pMethodDesc, \ + p->m_pMethodDesc, \ + type, ALWAYS ); \ + TADDR target = p->GetTarget(); \ + DisplayWriteElementPointer("Target",\ + DataPtrToDisplay(target),\ + ALWAYS );\ + DisplayEndStructure( ALWAYS ); \ + } + + case PRECODE_STUB: + DISPLAY_PRECODE(StubPrecode); break; +#ifdef HAS_NDIRECT_IMPORT_PRECODE + case PRECODE_NDIRECT_IMPORT: + DISPLAY_PRECODE(NDirectImportPrecode); break; +#endif +#ifdef HAS_REMOTING_PRECODE + case PRECODE_REMOTING: + DISPLAY_PRECODE(RemotingPrecode); break; +#endif +#ifdef HAS_FIXUP_PRECODE + case PRECODE_FIXUP: + IF_OPT_AND(PRECODES, METHODDESCS) + { + PTR_FixupPrecode p( precode->AsFixupPrecode() ); + DisplayStartStructure( "FixupPrecode", + DPtrToPreferredAddr(p), + sizeof(*p), + ALWAYS ); + PTR_MethodDesc precodeMD(p->GetMethodDesc()); +#ifdef HAS_FIXUP_PRECODE_CHUNKS + { + DisplayWriteFieldInt( m_MethodDescChunkIndex, + p->m_MethodDescChunkIndex, FixupPrecode, + ALWAYS ); + DisplayWriteFieldInt( m_PrecodeChunkIndex, + p->m_PrecodeChunkIndex, FixupPrecode, + ALWAYS ); + if( p->m_PrecodeChunkIndex == 0 ) + { + //dump the location of the Base + DisplayWriteElementAddress( "PrecodeChunkBase", + DataPtrToDisplay(p->GetBase()), + sizeof(void*), ALWAYS ); + } + //Make sure I align up if there is no code slot to make + //sure that I get the padding + TADDR mdPtrStart = p->GetBase() + + (p->m_MethodDescChunkIndex * MethodDesc::ALIGNMENT); + TADDR mdPtrEnd = ALIGN_UP( mdPtrStart + sizeof(MethodDesc*), + 8 ); + CoverageRead( mdPtrStart, (ULONG32)(mdPtrEnd - mdPtrStart) ); + TADDR precodeMDSlot = p->GetBase() + + p->m_MethodDescChunkIndex * MethodDesc::ALIGNMENT; + DoWriteFieldMethodDesc( "MethodDesc", + (DWORD)(precodeMDSlot - PTR_TO_TADDR(p)), + sizeof(TADDR), precodeMD ); + } +#else //HAS_FIXUP_PRECODE_CHUNKS + WriteFieldMethodDesc( m_pMethodDesc, + p->m_pMethodDesc, + FixupPrecode, ALWAYS ); +#endif //HAS_FIXUP_PRECODE_CHUNKS + TADDR target = p->GetTarget(); + DisplayWriteElementPointer("Target", + DataPtrToDisplay(target), + ALWAYS ); + /* REVISIT_TODO Thu 01/05/2006 + * dump slot with offset if it is here + */ + DisplayEndStructure( ALWAYS ); //FixupPrecode + } + break; +#endif +#ifdef HAS_THISPTR_RETBUF_PRECODE + case PRECODE_THISPTR_RETBUF: + DISPLAY_PRECODE(ThisPtrRetBufPrecode); break; +#endif + default: + _ASSERTE( !"Unsupported precode type" ); +#undef DISPLAY_PRECODE +#undef PrecodeMDWrite + } +} + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +void NativeImageDumper::DumpMethodDesc( PTR_MethodDesc md, PTR_Module module ) +{ + //StoredSigMethodDesc + + MethodClassification mc = + (MethodClassification)md->GetClassification(); + _ASSERTE(mc >= 0 && mc < mcCount); + const char * mdTypeName = s_MDTypeName[mc]; + unsigned mdSize = (unsigned)md->SizeOf(); + + DisplayStartStructure( mdTypeName, DPtrToPreferredAddr(md), + mdSize, METHODDESCS ); + IF_OPT(METHODDESCS) + { + TempBuffer buf; + MethodDescToString( md, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR *)buf, METHODDESCS ); + } +#ifdef _DEBUG + IF_OPT(METHODDESCS) + { + WriteFieldStr(m_pszDebugMethodName, + PTR_BYTE(TO_TADDR(md->m_pszDebugMethodName)), + MethodDesc, METHODDESCS); + WriteFieldStr(m_pszDebugClassName, + PTR_BYTE(TO_TADDR(md->m_pszDebugClassName)), + MethodDesc, METHODDESCS); + WriteFieldStr(m_pszDebugMethodSignature, + PTR_BYTE(TO_TADDR(md->m_pszDebugMethodSignature)), + MethodDesc, METHODDESCS); + } + else + { + CoverageReadString(TO_TADDR(md->m_pszDebugMethodName)); + CoverageReadString(TO_TADDR(md->m_pszDebugClassName)); + CoverageReadString(TO_TADDR(md->m_pszDebugMethodSignature)); + } +#endif + + DisplayWriteFieldInt( m_wFlags3AndTokenRemainder, md->m_wFlags3AndTokenRemainder, + MethodDesc, METHODDESCS ); + + DisplayWriteFieldInt( m_chunkIndex, md->m_chunkIndex, + MethodDesc, METHODDESCS ); + + /* XXX Fri 03/24/2006 + * This is a workaround. The InstantiatedMethodDescs are in chunks, but there's + * no obvious place to display the chunk, so display the bounds here. + */ + if( mc == mcInstantiated && md->m_chunkIndex == 0 ) + { + PTR_MethodDescChunk chunk( md->GetMethodDescChunk() ); + DisplayWriteElementAddress( "MethodDescChunk", DPtrToPreferredAddr(chunk), + chunk->SizeOf(), METHODDESCS ); + } + + DisplayWriteFieldEnumerated( m_bFlags2, md->m_bFlags2, MethodDesc, + s_MDFlag2, W(", "), METHODDESCS ); + + DisplayWriteFieldInt( m_wSlotNumber, md->GetSlot(), MethodDesc, + METHODDESCS ); + DisplayWriteFieldEnumerated( m_wFlags, md->m_wFlags, MethodDesc, + s_MDC, W(", "), METHODDESCS ); + + IF_OPT(IL) + { + if( md->IsIL() ) + { + PTR_MethodDescChunk chunk(md->GetMethodDescChunk()); + //chunk is implicitly remapped because it's calculated from the pointer + //to MD. + PTR_MethodTable mt = chunk->GetMethodTable(); + if( !CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ) + { + if ( md->IsTypicalMethodDefinition() ) + { + DWORD dwRVA = 0; + m_import->GetMethodProps(md->GetMemberDef(), NULL, NULL, NULL, 0, + NULL, NULL, NULL, &dwRVA, NULL); + + if (dwRVA != 0) + { + _ASSERTE(m_ILHostCopy); + _ASSERTE(m_ILSectionStart); + _ASSERTE(dwRVA >= m_ILSectionStart); + _ASSERTE(dwRVA < (m_ILSectionStart + m_ILSectionSize)); + //The RVA is from the start of the file, so convert it + //to an RVA to the start of the .text section. + TADDR pILTarget = (TADDR)m_decoder.GetRvaData(dwRVA); + COR_ILMETHOD * pILHeader = (COR_ILMETHOD*)(m_ILHostCopy + dwRVA - m_ILSectionStart); + + COR_ILMETHOD_DECODER decoder(pILHeader); + + DisplayStartStructure( "IL", + DataPtrToDisplay(pILTarget), + PEDecoder::ComputeILMethodSize(pILTarget), + ALWAYS ); + + DisplayWriteElementInt( "CodeSize", decoder.GetCodeSize(), ALWAYS ); + + // Dump the disassembled IL code? + + DisplayEndStructure( ALWAYS ); + } + } + } + } + } + if( md->HasPrecode() ) + { + PTR_Precode precode( md->GetPrecode() ); + + DumpPrecode( precode, module ); + } + if ( md->HasNonVtableSlot() ) + { + DisplayWriteElementInt( "Slot", (DWORD)(PTR_TO_TADDR(md->GetAddrOfSlot()) - PTR_TO_TADDR(md)), ALWAYS); + } + if (md->HasNativeCodeSlot()) + { + DisplayWriteElementInt( "NativeCode", DWORD(md->GetAddrOfNativeCodeSlot() - PTR_TO_TADDR(md)), ALWAYS); + //m_display->WriteFieldPointer( "NativeCode", + // DWORD(md->GetAddrOfNativeCodeSlot() - PTR_TO_TADDR(md)), + // sizeof(TADDR), + // md->GetNativeCode() ); + } + if (md->HasMethodImplSlot()) + { + DisplayStartVStructure( "MethodImpl", METHODDESCS ); + PTR_MethodImpl impl(md->GetMethodImpl()); + PTR_DWORD slots = impl->GetSlots() - 1; // GetSlots returns the address of the first real slot (past the size) + unsigned numSlots = impl->GetSize(); + _ASSERTE(!numSlots || numSlots == slots[0]); + _ASSERTE(slots == NULL || isInRange(PTR_TO_TADDR(slots))); + if ((slots != NULL) && isInRange(PTR_TO_TADDR(slots))) + { + DisplayWriteFieldAddress(pdwSlots, DataPtrToDisplay(dac_cast<TADDR>(slots)), + (numSlots + 1) * sizeof(*slots), + MethodImpl, METHODDESCS); + } + else + { + DisplayWriteFieldPointer(pdwSlots, DataPtrToDisplay(dac_cast<TADDR>(slots)), + MethodImpl, METHODDESCS); + + } + _ASSERTE(impl->pImplementedMD == NULL + || isInRange(PTR_TO_TADDR(impl->pImplementedMD))); + if ((impl->pImplementedMD != NULL) && + isInRange(PTR_TO_TADDR(impl->pImplementedMD))) + { + DisplayWriteFieldAddress( pImplementedMD, + DataPtrToDisplay(dac_cast<TADDR>(impl->pImplementedMD)), + numSlots * sizeof(MethodDesc*), + MethodImpl, METHODDESCS ); + } + else + { + DisplayWriteFieldPointer( pImplementedMD, + DataPtrToDisplay(dac_cast<TADDR>(impl->pImplementedMD)), + MethodImpl, METHODDESCS ); + } + DisplayEndVStructure( METHODDESCS ); + } + if (md->HasStoredSig()) + { + DisplayStartVStructure( "StoredSigMethodDesc", METHODDESCS ); + PTR_StoredSigMethodDesc ssmd(md); + //display signature information. + if( isInRange(ssmd->m_pSig) ) + { + DisplayWriteFieldAddress(m_pSig, DataPtrToDisplay(ssmd->m_pSig), + ssmd->m_cSig, StoredSigMethodDesc, + METHODDESCS); + } + else + { + DisplayWriteFieldPointer(m_pSig, DataPtrToDisplay(ssmd->m_pSig), + StoredSigMethodDesc, METHODDESCS); + + } + CoverageRead(TO_TADDR(ssmd->m_pSig), ssmd->m_cSig); + DisplayWriteFieldInt( m_cSig, ssmd->m_cSig, + StoredSigMethodDesc, METHODDESCS ); +#ifdef _WIN64 + DisplayWriteFieldEnumerated( m_dwExtendedFlags, + ssmd->m_dwExtendedFlags, + StoredSigMethodDesc, + s_SSMDExtendedFlags, W(", "), + METHODDESCS ); +#endif + DisplayEndVStructure( METHODDESCS ); //StoredSigMethodDesc + } + if( mc == mcDynamic ) + { + PTR_DynamicMethodDesc dmd(md); + DisplayStartVStructure( "DynamicMethodDesc", METHODDESCS ); + WriteFieldStr( m_pszMethodName, PTR_BYTE(dmd->m_pszMethodName), + DynamicMethodDesc, METHODDESCS ); + if( !CHECK_OPT(METHODDESCS) ) + CoverageReadString( PTR_TO_TADDR(dmd->m_pszMethodName) ); + DisplayWriteFieldPointer( m_pResolver, + DPtrToPreferredAddr(dmd->m_pResolver), + DynamicMethodDesc, METHODDESCS ); +#ifndef _WIN64 + DisplayWriteFieldEnumerated( m_dwExtendedFlags, + dmd->m_dwExtendedFlags, + DynamicMethodDesc, + s_SSMDExtendedFlags, W(", "), + METHODDESCS ); +#endif + DisplayEndVStructure( METHODDESCS ); + } + if (mc == mcFCall ) + { + PTR_FCallMethodDesc fcmd(md); + DisplayStartVStructure( "FCallMethodDesc", METHODDESCS ); + + DisplayWriteFieldInt( m_dwECallID, + fcmd->m_dwECallID, + FCallMethodDesc, + METHODDESCS ); + + DisplayEndVStructure( METHODDESCS ); //NDirectMethodDesc + } + if( mc == mcNDirect ) + { + PTR_NDirectMethodDesc ndmd(md); + DisplayStartVStructure( "NDirectMethodDesc", METHODDESCS ); + DPTR(NDirectMethodDesc::temp1) nd( PTR_HOST_MEMBER_TADDR(NDirectMethodDesc, ndmd, ndirect) ); + DisplayStartStructureWithOffset( ndirect, + DPtrToPreferredAddr(nd), + sizeof(*nd), NDirectMethodDesc, + METHODDESCS ); + DisplayWriteFieldPointer( m_pNativeNDirectTarget, + DataPtrToDisplay((TADDR)nd->m_pNativeNDirectTarget), + NDirectMethodDesc::temp1, + METHODDESCS ); + DisplayWriteFieldEnumerated( m_wFlags, nd->m_wFlags, + NDirectMethodDesc::temp1, + g_NDirectFlags, W(", "), + METHODDESCS ); + + WriteFieldStr( m_pszEntrypointName, + PTR_BYTE(TO_TADDR(nd->m_pszEntrypointName)), + NDirectMethodDesc::temp1, METHODDESCS ); + if( !CHECK_OPT(METHODDESCS) ) + CoverageReadString(TO_TADDR(nd->m_pszEntrypointName)); + if (md->IsQCall()) + { + DisplayWriteFieldInt( m_dwECallID, + nd->m_dwECallID, + NDirectMethodDesc::temp1, + METHODDESCS ); + } + else + { + WriteFieldStr( m_pszLibName, + PTR_BYTE(TO_TADDR(nd->m_pszLibName)), + NDirectMethodDesc::temp1, METHODDESCS ); + } + if( !CHECK_OPT(METHODDESCS) ) + CoverageReadString( TO_TADDR(nd->m_pszLibName) ); + + PTR_NDirectWriteableData wnd( nd->m_pWriteableData ); + DisplayStartStructureWithOffset( m_pWriteableData, + DPtrToPreferredAddr(wnd), + sizeof(*wnd), + NDirectMethodDesc::temp1, + METHODDESCS ); + DisplayWriteFieldPointer( m_pNDirectTarget, + DataPtrToDisplay((TADDR)wnd->m_pNDirectTarget), NDirectWriteableData, METHODDESCS ); + if( !CHECK_OPT(METHODDESCS) ) + CoverageRead( PTR_TO_TADDR(wnd), sizeof(*wnd) ); + DisplayEndStructure( METHODDESCS ); //m_pWriteableData + + +#ifdef HAS_NDIRECT_IMPORT_PRECODE + PTR_NDirectImportThunkGlue glue(nd->m_pImportThunkGlue); +#else + PTR_NDirectImportThunkGlue glue(PTR_HOST_MEMBER_TADDR(NDirectMethodDesc::temp1, nd, m_ImportThunkGlue)); +#endif + +#ifdef HAS_NDIRECT_IMPORT_PRECODE + if (glue == NULL) + { + // import thunk glue is not needed for P/Invoke that is not inlinable + DisplayWriteFieldPointer( m_pImportThunkGlue, + NULL, + NDirectMethodDesc::temp1, + METHODDESCS ); + } + else + { + DisplayStartStructureWithOffset( m_pImportThunkGlue, + DPtrToPreferredAddr(glue), + sizeof(*glue), + NDirectMethodDesc::temp1, + METHODDESCS); +#else + DisplayStartStructureWithOffset( m_ImportThunkGlue, + DPtrToPreferredAddr(glue), + sizeof(*glue), + NDirectMethodDesc::temp1, + METHODDESCS); +#endif +#ifdef HAS_NDIRECT_IMPORT_PRECODE + /* REVISIT_TODO Thu 01/05/2006 + * Dump this properly as a precode + */ + WriteFieldMethodDesc( m_pMethodDesc, glue->m_pMethodDesc, + NDirectImportThunkGlue, METHODDESCS ); + { + PTR_Precode p(glue); + DumpPrecode( p, module ); + } + if( !CHECK_OPT(METHODDESCS) ) + CoverageRead(PTR_TO_TADDR(glue), sizeof(*glue)); + /* REVISIT_TODO Fri 12/16/2005 + * Factor out this code into some shared precode dumping code + */ +#else //!HAS_NDIRECT_IMPORT_PRECODE + /* REVISIT_TODO Fri 10/27/2006 + * For Whidbey AMD64 (!HAS_NDIRECT_IMPORT_PRECODE), I don't have this data structure in the output. + */ +#endif //HAS_NDIRECT_IMPORT_PRECODE + + DisplayEndStructure( METHODDESCS ); //m_pImportThunkGlue +#ifdef HAS_NDIRECT_IMPORT_PRECODE + } +#endif + +#ifdef _TARGET_X86_ + DisplayWriteFieldInt( m_cbStackArgumentSize, + nd->m_cbStackArgumentSize, + NDirectMethodDesc::temp1, METHODDESCS ); +#endif + + WriteFieldMethodDesc( m_pStubMD, + nd->m_pStubMD.GetValueMaybeNull(PTR_HOST_MEMBER_TADDR(NDirectMethodDesc::temp1, nd, m_pStubMD)), + NDirectMethodDesc::temp1, METHODDESCS ); + + DisplayEndStructure( METHODDESCS ); //ndirect + + + DisplayEndVStructure( METHODDESCS ); //NDirectMethodDesc + } + if( mc == mcEEImpl ) + { + DisplayStartVStructure( "EEImplMethodDesc", METHODDESCS ); + DisplayEndVStructure( METHODDESCS ); + } +#if defined(FEATURE_COMINTEROP) + if( mc == mcComInterop ) + { + PTR_ComPlusCallMethodDesc cpmd(md); + DisplayStartVStructure( "ComPlusCallMethodDesc", METHODDESCS ); + PTR_ComPlusCallInfo compluscall((TADDR)cpmd->m_pComPlusCallInfo); + + if (compluscall == NULL) + { + DisplayWriteFieldPointer( m_pComPlusCallInfo, + NULL, + ComPlusCallMethodDesc, + METHODDESCS ); + } + else + { + DumpComPlusCallInfo( compluscall, METHODDESCS ); + } + + DisplayEndVStructure( METHODDESCS ); //ComPlusCallMethodDesc + } +#endif + if( mc == mcInstantiated ) + { + PTR_InstantiatedMethodDesc imd(md); + DisplayStartVStructure( "InstantiatedMethodDesc", METHODDESCS ); + unsigned kind = imd->m_wFlags2 + & InstantiatedMethodDesc::KindMask; + if( kind == InstantiatedMethodDesc::SharedMethodInstantiation ) + { + PTR_DictionaryLayout layout(TO_TADDR(imd->m_pDictLayout)); + IF_OPT(METHODDESCS) + { + WriteFieldDictionaryLayout( "m_pDictLayout", + offsetof(InstantiatedMethodDesc, m_pDictLayout ), + fieldsize(InstantiatedMethodDesc, m_pDictLayout), + layout, + GetDependencyFromMD(md)->pImport ); + } + else + { + while( layout != NULL ) + { + CoverageRead( PTR_TO_TADDR(layout), + sizeof(DictionaryLayout) + + sizeof(DictionaryEntryLayout) + * (layout->m_numSlots - 1) ); + layout = PTR_DictionaryLayout(TO_TADDR(layout->m_pNext)); + } + } + } + else if( kind == + InstantiatedMethodDesc::WrapperStubWithInstantiations ) + { + PTR_MethodDesc wimd(imd->m_pWrappedMethodDesc.GetValue()); + if( wimd == NULL || !DoWriteFieldAsFixup( "m_pWrappedMethodDesc", + offsetof(InstantiatedMethodDesc, m_pWrappedMethodDesc), + fieldsize(InstantiatedMethodDesc, m_pWrappedMethodDesc), + PTR_TO_TADDR(wimd) ) ) + { + WriteFieldMethodDesc( m_pWrappedMethodDesc, wimd, + InstantiatedMethodDesc, METHODDESCS ); + } + } + else + { + _ASSERTE(imd->m_pDictLayout == NULL); + DisplayWriteFieldPointer( m_pDictLayout, NULL, + InstantiatedMethodDesc, + METHODDESCS ); + } + //now handle the contents of the m_pMethInst/m_pPerInstInfo union. + unsigned numSlots = imd->m_wNumGenericArgs; + PTR_Dictionary inst(imd->m_pPerInstInfo); + unsigned dictSize; + if( kind == InstantiatedMethodDesc::SharedMethodInstantiation ) + { + dictSize = sizeof(TypeHandle); + } + else if( kind == InstantiatedMethodDesc::WrapperStubWithInstantiations ) + { + PTR_InstantiatedMethodDesc wrapped = + PTR_InstantiatedMethodDesc(imd->m_pWrappedMethodDesc.GetValue()); + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(wrapped)) ) + { + /* XXX Mon 03/27/2006 + * Note that 4 is the correct answer for all IMDs at this time. + */ + TempBuffer buf; + MethodDescToString( md, buf ); + //m_display->ErrorPrintF( "WARNING! InstantiatedMethodDesc %S wraps a MethodDesc that is a fixup. I cannot accurately determine the size of the associated generic dictionary. Assuming 4.\n", (const WCHAR *)buf ); + dictSize = (imd->GetNumGenericMethodArgs() + 4) * sizeof(void*); + } + else + { + PTR_DictionaryLayout layout(wrapped->IsSharedByGenericMethodInstantiations() + ? TO_TADDR(wrapped->m_pDictLayout) : NULL ); + dictSize = DictionaryLayout::GetFirstDictionaryBucketSize(imd->GetNumGenericMethodArgs(), + layout); + } + } + else + { + dictSize = sizeof(TypeHandle); + } + //instantiations has the number of slots of + //GetNumGenericMethodArgs. + if( inst == NULL ) + { + m_display->WriteFieldPointer( "m_pPerInstInfo", + offsetof(InstantiatedMethodDesc, m_pPerInstInfo), + fieldsize(InstantiatedMethodDesc, m_pPerInstInfo), + NULL ); + } + else + { + IF_OPT(METHODDESCS) + { + + m_display->StartStructureWithOffset( "m_pPerInstInfo", + offsetof(InstantiatedMethodDesc, m_pPerInstInfo), + fieldsize(InstantiatedMethodDesc, m_pPerInstInfo), + DPtrToPreferredAddr(inst), + dictSize ); + } + DisplayStartArray( "InstantiationInfo", W("[%-2s]: %s"), + METHODDESCS ); + /* REVISIT_TODO Thu 03/23/2006 + * This doesn't dump the contents of the dictionary which are + * hanging around after the real slots. Get around to doing that. + */ + for( unsigned i = 0; i < numSlots + && CHECK_OPT(METHODDESCS); ++i ) + { + DisplayStartElement( "Handle", METHODDESCS ); + DisplayWriteElementInt( "Index", i, METHODDESCS ); + + TypeHandle thArg = inst->GetInstantiation()[i].GetValue(); + IF_OPT(METHODDESCS) + WriteElementTypeHandle( "TypeHandle", thArg); + + /* XXX Fri 03/24/2006 + * There is no really good home for TypeDescs, so I gotta check + * lots of places for them. + */ + if( !CORCOMPILE_IS_POINTER_TAGGED(thArg.AsTAddr()) && + thArg.IsTypeDesc() ) + { + PTR_TypeDesc td(thArg.AsTypeDesc()); + if( isInRange(PTR_TO_TADDR(td)) ) + { + m_discoveredTypeDescs.AppendEx(td); + } + } + DisplayEndElement( METHODDESCS ); //Handle + } + //Instantiation Info + DisplayEndArray( "Total TypeHandles", METHODDESCS ); + + DisplayEndVStructure(METHODDESCS); //m_pPerInstInfo; + if( !CHECK_OPT(METHODDESCS) ) + CoverageRead(PTR_TO_TADDR(inst), numSlots * sizeof(*inst)); + } + + DisplayWriteFieldEnumerated( m_wFlags2, imd->m_wFlags2, + InstantiatedMethodDesc, s_IMDFlags, + W(", "), METHODDESCS ); + DisplayWriteFieldInt( m_wNumGenericArgs, imd->m_wNumGenericArgs, + InstantiatedMethodDesc, METHODDESCS ); + +#ifdef FEATURE_COMINTEROP + if (imd->IMD_HasComPlusCallInfo()) + { + PTR_ComPlusCallInfo compluscall = imd->IMD_GetComPlusCallInfo(); + DumpComPlusCallInfo( compluscall, METHODDESCS ); + } +#endif // FEATURE_COMINTEROP + + DisplayEndStructure( METHODDESCS ); + } + + DisplayEndStructure( METHODDESCS ); //MethodDesc (mdTypeName) + if( !CHECK_OPT(METHODDESCS) ) + CoverageRead( PTR_TO_TADDR(md), mdSize ); + +} +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +NativeImageDumper::EnumMnemonics NativeImageDumper::s_EECLIFlags[] = +{ +#define EECLI_FLAGS_ENTRY(x) NativeImageDumper::EnumMnemonics( EEClassLayoutInfo:: x, W(#x) ) + EECLI_FLAGS_ENTRY(e_BLITTABLE), + EECLI_FLAGS_ENTRY(e_MANAGED_SEQUENTIAL), + EECLI_FLAGS_ENTRY(e_ZERO_SIZED), + EECLI_FLAGS_ENTRY(e_HAS_EXPLICIT_SIZE), +#undef EECLI_FLAGS_ENTRY +}; + + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +void +NativeImageDumper::DumpEEClassForMethodTable( PTR_MethodTable mt ) +{ + PTR_EEClass clazz = mt->GetClass(); + + _ASSERTE(CHECK_OPT(EECLASSES)); + _ASSERTE(clazz != NULL); + _ASSERTE(isInRange(PTR_TO_TADDR(clazz))); + + const char * eeClassType; + + if( clazz->HasLayout() ) + eeClassType = "LayoutEEClass"; + else if( mt->IsArray() ) + eeClassType = "ArrayClass"; + else if( clazz->IsDelegate() ) + eeClassType = "DelegateEEClass"; + else + eeClassType = "EEClass"; + + DisplayStartStructure( eeClassType, DPtrToPreferredAddr(clazz), clazz->GetSize(), + EECLASSES ); + { + TempBuffer buf; + MethodTableToString( mt, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR *)buf, EECLASSES ); + } + + PTR_GuidInfo guidInfo = clazz->GetGuidInfo(); + if(guidInfo != NULL) + { + DisplayStartStructureWithOffset( m_pGuidInfo, + DPtrToPreferredAddr(guidInfo), + sizeof(*guidInfo), EEClass, + EECLASSES ); + TempBuffer buf; + GuidToString( guidInfo->m_Guid, buf ); + DisplayWriteFieldStringW( m_Guid, (const WCHAR *)buf, GuidInfo, + EECLASSES ); + DisplayWriteFieldFlag( m_bGeneratedFromName, + guidInfo->m_bGeneratedFromName, + GuidInfo, EECLASSES ); + DisplayEndStructure( EECLASSES ); //guidinfo + } + else + { + /* XXX Fri 10/14/2005 + * if Clazz isn't an interface, m_pGuidInfo is undefined. + */ + DisplayWriteFieldPointerAnnotated( m_pGuidInfo, PTR_TO_TADDR(guidInfo), + W("Invalid"), EEClass, EECLASSES ); + } + + +#ifdef _DEBUG + WriteFieldStr( m_szDebugClassName, + PTR_BYTE(TO_TADDR(clazz->m_szDebugClassName)), + EEClass, EECLASSES ); + DisplayWriteFieldFlag( m_fDebuggingClass, clazz->m_fDebuggingClass, + EEClass, EECLASSES ); +#endif + + WriteFieldMethodTable( m_pMethodTable, clazz->m_pMethodTable, EEClass, + EECLASSES ); + + WriteFieldCorElementType( m_NormType, (CorElementType)clazz->m_NormType, + EEClass, EECLASSES ); + + PTR_FieldDesc fdList = clazz->GetFieldDescList(); + + ULONG fieldCount = (ULONG)CountFields(mt); + _ASSERTE((fdList == NULL) == (fieldCount == 0)); + + IF_OPT(EECLASSES) + { + m_display->StartStructureWithOffset( "m_pFieldDescList", + offsetof(EEClass, m_pFieldDescList), + fieldsize(EEClass, m_pFieldDescList), + DPtrToPreferredAddr(fdList), + fdList != NULL ? + sizeof(*fdList) * fieldCount : + 0 ); + } + IF_OPT(VERBOSE_TYPES) + { + if( fdList != NULL ) + { + DisplayStartArray( "FieldDescs", NULL, EECLASSES ); + for( SIZE_T i = 0; i < fieldCount; ++i ) + { + PTR_FieldDesc fd = fdList + i; + IF_OPT(EECLASSES) + DumpFieldDesc( fd, "FieldDesc" ); + } + DisplayEndArray( "Total FieldDescs", EECLASSES ); //FieldDescs + } + } + else if( (fdList != NULL) && CHECK_OPT(DEBUG_COVERAGE) ) + { + for( SIZE_T i = 0; i < fieldCount; ++i ) + { + PTR_FieldDesc fd = fdList + i; +#ifdef _DEBUG + if( fd != NULL && fd->m_debugName != NULL ) + CoverageReadString( fd->m_debugName ); +#endif + } + CoverageRead( PTR_TO_TADDR(fdList), sizeof(*fdList) * fieldCount ); + } + + DisplayEndStructure( EECLASSES ); //FieldDescList + + DisplayWriteFieldEnumerated( m_dwAttrClass, clazz->GetAttrClass(), + EEClass, s_CorTypeAttr, W(" "), EECLASSES ); + DisplayWriteFieldEnumerated( m_VMFlags, clazz->m_VMFlags, EEClass, + s_VMFlags, W(", "), EECLASSES ); + + PTR_MethodDescChunk chunk = clazz->GetChunks(); + + DisplayStartArrayWithOffset( m_pChunks, NULL, EEClass, EECLASSES ); + while( chunk != NULL ) + { + DisplayStartStructure( "MethodDescChunk", + DPtrToPreferredAddr(chunk), + chunk->SizeOf(), EECLASSES ); + _ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(chunk->GetMethodTable()))); + PTR_MethodTable chunkMT = chunk->GetMethodTable(); + DisplayWriteFieldPointer( m_methodTable, + DPtrToPreferredAddr(chunkMT), + MethodDescChunk, EECLASSES ); + PTR_MethodDescChunk chunkNext = chunk->GetNextChunk(); + DisplayWriteFieldPointer( m_next, + DPtrToPreferredAddr(chunkNext), + MethodDescChunk, EECLASSES ); + DisplayWriteFieldInt( m_size, chunk->m_size, MethodDescChunk, + EECLASSES ); + DisplayWriteFieldInt( m_count, chunk->m_count, MethodDescChunk, + EECLASSES ); + DisplayWriteFieldInt( m_flagsAndTokenRange, chunk->m_flagsAndTokenRange, MethodDescChunk, + EECLASSES ); + /* XXX Wed 12/14/2005 + * Don't skip walking this array. I need to make sure I touch the + * precodes. + */ + DisplayStartArray( "MethodDescs", NULL, METHODDESCS ); + PTR_MethodDesc md(chunk->GetFirstMethodDesc()); + while (md != NULL) + { + IF_OPT_OR(METHODDESCS, DEBUG_COVERAGE) + { + PTR_Module module = mt->GetModule(); + if(CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(module) )) + DumpMethodDesc( md, PTR_Module((TADDR)0) ); + else + DumpMethodDesc( md, module ); + } + + // Check whether the next MethodDesc is within the bounds of the current chunks + TADDR pNext = PTR_HOST_TO_TADDR(md) + md->SizeOf(); + TADDR pEnd = PTR_HOST_TO_TADDR(chunk) + chunk->SizeOf(); + + md = (pNext < pEnd) ? PTR_MethodDesc(pNext) : NULL; + } + + DisplayEndArray( "Total MethodDescs", METHODDESCS); //MethodDescs + + chunk = chunk->GetNextChunk(); + + DisplayEndStructure( EECLASSES ); //MethodDescChunk + } + + DisplayEndArray( "Total MethodDescChunks", EECLASSES ); + /* REVISIT_TODO Fri 10/14/2005 + * Dump the class dependencies + */ + //_ASSERTE(!clazz->m_classDependencies.TestAnyBit()); + + /* REVISIT_TODO Mon 10/24/2005 + * Create vstructure for union? + */ + //decode union here +#ifdef FEATURE_COMINTEROP + if( clazz->IsBlittable() || clazz->HasLayout() ) + { + DisplayWriteFieldInt(m_cbNativeSize, clazz->m_cbNativeSize, EEClass, + EECLASSES ); + } + else if( clazz->IsInterface() ) + { + DisplayWriteFieldPointer( m_ohDelegate, + DataPtrToDisplay(clazz->m_ohDelegate), + EEClass, EECLASSES ); + } + else + { + static const WCHAR * ifnames[] ={W("Dual"),W("Vtable"),W("Dispatch")}; + m_display->WriteFieldEnumerated( "ComInterfaceType", + offsetof(EEClass, + m_ComInterfaceType), + fieldsize(EEClass, + m_ComInterfaceType), + (int)clazz->m_ComInterfaceType, + ifnames[(int)clazz->m_ComInterfaceType] ); + } +#else + DisplayWriteFieldInt( m_cbNativeSize, clazz->m_cbNativeSize, + EEClass, EECLASSES ); +#endif + +#if defined(FEATURE_COMINTEROP) + PTR_ComCallWrapperTemplate ccwTemplate(TO_TADDR(clazz->m_pccwTemplate)); + if( ccwTemplate != NULL ) + { + DisplayWriteFieldPointer( m_pccwTemplate, NULL, EEClass, + EECLASSES ); + } + else + { + /* REVISIT_TODO Fri 10/14/2005 + * Dump CcwTemplate + */ + DisplayWriteFieldPointer( m_pccwTemplate, + DPtrToPreferredAddr(ccwTemplate), EEClass, + EECLASSES ); + } +#endif // defined(FEATURE_COMINTEROP) + + //fields for classes that aren't just EEClasses. + if( clazz->HasLayout() ) + { + PTR_LayoutEEClass layoutClass(PTR_TO_TADDR(clazz)); + DisplayStartVStructure("LayoutEEClass", EECLASSES ); + + PTR_EEClassLayoutInfo eecli( PTR_HOST_MEMBER_TADDR( LayoutEEClass, + layoutClass, + m_LayoutInfo ) ); + DisplayStartStructureWithOffset( m_LayoutInfo, + DPtrToPreferredAddr(eecli), + sizeof(EEClassLayoutInfo), + LayoutEEClass, EECLASSES ); + /* REVISIT_TODO Fri 10/14/2005 + * Dump EEClassLayoutInfo + */ + DisplayWriteFieldInt( m_cbNativeSize, eecli->m_cbNativeSize, + EEClassLayoutInfo, VERBOSE_TYPES ); + DisplayWriteFieldInt( m_cbManagedSize, eecli->m_cbManagedSize, + EEClassLayoutInfo, VERBOSE_TYPES ); + DisplayWriteFieldInt( m_LargestAlignmentRequirementOfAllMembers, + eecli->m_LargestAlignmentRequirementOfAllMembers, + EEClassLayoutInfo, VERBOSE_TYPES ); + DisplayWriteFieldInt( m_ManagedLargestAlignmentRequirementOfAllMembers, + eecli->m_ManagedLargestAlignmentRequirementOfAllMembers, + EEClassLayoutInfo, VERBOSE_TYPES ); + DisplayWriteFieldEnumerated( m_bFlags, eecli->m_bFlags, + EEClassLayoutInfo, s_EECLIFlags, W(", "), + VERBOSE_TYPES ); + DisplayWriteFieldInt( m_numCTMFields, eecli->m_numCTMFields, + EEClassLayoutInfo, VERBOSE_TYPES ); + PTR_FieldMarshaler fmArray( TO_TADDR(eecli->m_pFieldMarshalers) ); + DisplayWriteFieldAddress( m_pFieldMarshalers, + DPtrToPreferredAddr(fmArray), + eecli->m_numCTMFields + * MAXFIELDMARSHALERSIZE, + EEClassLayoutInfo, VERBOSE_TYPES ); + /* REVISIT_TODO Wed 03/22/2006 + * Dump the various types of FieldMarshalers. + */ +#if 0 + DisplayStartArrayWithOffset( m_pFieldMarshalers, NULL, + EEClassLayoutInfo, VERBOSE_TYPES ); + for( unsigned i = 0; i < eecli->m_numCTMFields; ++i ) + { + /* REVISIT_TODO Wed 03/22/2006 + * Try to display the type of the field marshaler in the future. + */ + PTR_FieldMarshaler current = fmArray + i; + DisplayStartStructure( "FieldMarshaler", + DPtrToPreferredAddr(current), + sizeof(*current), VERBOSE_TYPES ); + WriteFieldFieldDesc( m_pFD, PTR_FieldDesc(TO_TADDR(current->m_pFD)), + FieldMarshaler, VERBOSE_TYPES ); + DisplayWriteFieldInt( m_dwExternalOffset, + current->m_dwExternalOffset, FieldMarshaler, + VERBOSE_TYPES ); + DisplayEndStructure( VERBOSE_TYPES ); //FieldMarshaler + } + + DisplayEndArray( "Number of FieldMarshalers", VERBOSE_TYPES ); //m_pFieldMarshalers +#endif + + DisplayEndStructure( EECLASSES ); //LayoutInfo + + DisplayEndVStructure( EECLASSES ); //LayoutEEClass + } + else if( mt->IsArray() ) + { + PTR_ArrayClass arrayClass(PTR_TO_TADDR(clazz)); + DisplayStartVStructure( "ArrayClass", EECLASSES); + IF_OPT(EECLASSES) + { + m_display->WriteFieldInt( "m_rank", offsetof(ArrayClass, m_rank), + fieldsize(ArrayClass, m_rank), + arrayClass->GetRank() ); + } + DoWriteFieldCorElementType( "m_ElementType", + offsetof(ArrayClass, m_ElementType), + fieldsize(ArrayClass, m_ElementType), + arrayClass->GetArrayElementType() ); + + DisplayEndVStructure( EECLASSES ); //ArrayClass + } + else if( clazz->IsDelegate() ) + { + PTR_DelegateEEClass delegateClass(PTR_TO_TADDR(clazz)); + DisplayStartVStructure( "DelegateEEClass", EECLASSES ); + + DumpFieldStub( m_pStaticCallStub, delegateClass->m_pStaticCallStub, + DelegateEEClass, EECLASSES ); + DumpFieldStub( m_pInstRetBuffCallStub, + delegateClass->m_pInstRetBuffCallStub, + DelegateEEClass, EECLASSES ); + + WriteFieldMethodDesc( m_pInvokeMethod, + delegateClass->m_pInvokeMethod, + DelegateEEClass, EECLASSES ); + DumpFieldStub( m_pMultiCastInvokeStub, + delegateClass->m_pMultiCastInvokeStub, + DelegateEEClass, EECLASSES ); + + DPTR(UMThunkMarshInfo) + umInfo(TO_TADDR(delegateClass->m_pUMThunkMarshInfo)); + + if( umInfo == NULL ) + { + DisplayWriteFieldPointer( m_pUMThunkMarshInfo, NULL, + DelegateEEClass, EECLASSES ); + } + else + { + DisplayStartStructureWithOffset( m_pUMThunkMarshInfo, + DPtrToPreferredAddr(umInfo), + sizeof(*umInfo), + DelegateEEClass, EECLASSES ); + /* REVISIT_TODO Fri 10/14/2005 + * DumpUMThunkMarshInfo + */ + DisplayEndStructure( EECLASSES ); //UMThunkMarshInfo + } + + WriteFieldMethodDesc( m_pBeginInvokeMethod, + delegateClass->m_pBeginInvokeMethod, + DelegateEEClass, EECLASSES ); + WriteFieldMethodDesc( m_pEndInvokeMethod, + delegateClass->m_pEndInvokeMethod, + DelegateEEClass, EECLASSES ); + DisplayWriteFieldPointer( m_pMarshalStub, delegateClass->m_pMarshalStub, + DelegateEEClass, EECLASSES ); + + WriteFieldMethodDesc( m_pForwardStubMD, + PTR_MethodDesc(TO_TADDR(delegateClass->m_pForwardStubMD)), + DelegateEEClass, EECLASSES ); + WriteFieldMethodDesc( m_pReverseStubMD, + PTR_MethodDesc(TO_TADDR(delegateClass->m_pReverseStubMD)), + DelegateEEClass, EECLASSES ); + +#ifdef FEATURE_COMINTEROP + DPTR(ComPlusCallInfo) compluscall((TADDR)delegateClass->m_pComPlusCallInfo); + if (compluscall == NULL) + { + DisplayWriteFieldPointer( m_pComPlusCallInfo, + NULL, + DelegateEEClass, + EECLASSES ); + } + else + { + DumpComPlusCallInfo( compluscall, EECLASSES ); + } +#endif // FEATURE_COMINTEROP + + DisplayEndVStructure( EECLASSES ); //DelegateEEClass + } + + DisplayEndStructure( EECLASSES ); //eeClassType + + PTR_EEClassOptionalFields pClassOptional = clazz->GetOptionalFields(); + if (pClassOptional) + { + DisplayStartStructure( "EEClassOptionalFields", DPtrToPreferredAddr(pClassOptional), sizeof(EEClassOptionalFields), + EECLASSES ); + +#ifdef FEATURE_COMINTEROP + PTR_SparseVTableMap sparseVTMap(TO_TADDR(pClassOptional->m_pSparseVTableMap)); + if( sparseVTMap == NULL ) + { + DisplayWriteFieldPointer( m_pSparseVTableMap, NULL, EEClassOptionalFields, + EECLASSES ); + } + else + { + _ASSERTE( !"Untested code" ); + IF_OPT(EECLASSES) + { + m_display->StartStructure( "m_SparseVTableMap", + DPtrToPreferredAddr(sparseVTMap), + sizeof(*sparseVTMap) ); + } + _ASSERTE(sparseVTMap->m_MapList != NULL); + PTR_SparseVTableMap_Entry mapList(TO_TADDR(sparseVTMap->m_MapList)); + DisplayStartArray( "m_MapList", NULL, EECLASSES ); + for( WORD i = 0; i < sparseVTMap->m_MapEntries; ++i ) + { + DisplayWriteFieldInt( m_Start, mapList[i].m_Start, + SparseVTableMap::Entry, EECLASSES ); + DisplayWriteFieldInt( m_Span, mapList[i].m_Span, + SparseVTableMap::Entry, EECLASSES ); + DisplayWriteFieldInt( m_Span, mapList[i].m_MapTo, + SparseVTableMap::Entry, EECLASSES ); + } + + DisplayEndArray( "Total Entries", EECLASSES ); //m_MapList + + DisplayWriteFieldInt( m_MapEntries, sparseVTMap->m_MapEntries, + SparseVTableMap, EECLASSES ); + DisplayWriteFieldInt( m_Allocated, sparseVTMap->m_Allocated, + SparseVTableMap, EECLASSES ); + DisplayWriteFieldInt( m_LastUsed, sparseVTMap->m_LastUsed, + SparseVTableMap, EECLASSES ); + DisplayWriteFieldInt( m_VTSlot, sparseVTMap->m_VTSlot, + SparseVTableMap, EECLASSES ); + DisplayWriteFieldInt( m_MTSlot, sparseVTMap->m_MTSlot, + SparseVTableMap, EECLASSES ); + + DisplayEndStructure( EECLASSES ); //SparseVTableMap + } + + WriteFieldTypeHandle( m_pCoClassForIntf, pClassOptional->m_pCoClassForIntf, + EEClassOptionalFields, EECLASSES ); + + PTR_ClassFactoryBase classFactory(TO_TADDR(pClassOptional->m_pClassFactory)); + if( classFactory != NULL ) + { + DisplayWriteFieldPointer( m_pClassFactory, NULL, EEClassOptionalFields, + EECLASSES ); + } + else + { + /* REVISIT_TODO Fri 10/14/2005 + * Dump ComClassFactory + */ + DisplayWriteFieldPointer( m_pClassFactory, + DPtrToPreferredAddr(classFactory), + EEClassOptionalFields, EECLASSES ); + } +#endif // FEATURE_COMINTEROP + + PTR_DictionaryLayout layout = pClassOptional->m_pDictLayout; + if( layout == NULL ) + { + DisplayWriteFieldPointer( m_pDictLayout, NULL, EEClassOptionalFields, EECLASSES ); + } + else + { + IF_OPT(VERBOSE_TYPES) + { + WriteFieldDictionaryLayout( "m_pDictLayout", + offsetof(EEClassOptionalFields, m_pDictLayout), + fieldsize(EEClassOptionalFields, m_pDictLayout), + layout, GetDependencyFromMT(mt)->pImport ); + } + else + { + while( layout != NULL ) + { + CoverageRead( PTR_TO_TADDR(layout), + sizeof(DictionaryLayout) + + sizeof(DictionaryEntryLayout) + * (layout->m_numSlots - 1) ); + layout = PTR_DictionaryLayout(TO_TADDR(layout->m_pNext)); + } + } + } + PTR_BYTE varianceInfo = TO_TADDR(pClassOptional->m_pVarianceInfo); + if( varianceInfo == NULL ) + { + DisplayWriteFieldPointer( m_pVarianceInfo, NULL, + EEClassOptionalFields, EECLASSES ); + } + else + { + /* REVISIT_TODO Fri 10/14/2005 + * Dump variance info + */ + DisplayWriteFieldPointer( m_pVarianceInfo, + DPtrToPreferredAddr(varianceInfo), EEClassOptionalFields, + EECLASSES ); + } + + DisplayWriteFieldInt( m_cbModuleDynamicID, pClassOptional->m_cbModuleDynamicID, + EEClassOptionalFields, EECLASSES ); + + /* REVISIT_TODO Fri 10/14/2005 + * Use the macros from ConstrainedExecutionRegion.cpp on this? + */ + DisplayWriteFieldUInt( m_dwReliabilityContract, + clazz->GetReliabilityContract(), + EEClassOptionalFields, EECLASSES ); + + DisplayWriteFieldEnumerated( m_SecProps, clazz->GetSecurityProperties()->dwFlags, + EEClassOptionalFields, s_SecurityProperties, W("|"), + EECLASSES ); + + DisplayEndStructure( EECLASSES ); // EEClassOptionalFields + } +} // NativeImageDumper::DumpEEClassForMethodTable +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +enum TypeDescType +{ + TDT_IsTypeDesc, + TDT_IsParamTypeDesc, + TDT_IsArrayTypeDesc, + TDT_IsTypeVarTypeDesc, + TDT_IsFnPtrTypeDesc +}; +const char * const g_typeDescTypeNames[] = +{ + "TypeDesc", + "ParamTypeDesc", + "ArrayTypeDesc", + "TypeVarTypeDesc", + "FnPtrTypeDesc" +}; +int g_typeDescSizes[] = +{ + sizeof(TypeDesc), + sizeof(ParamTypeDesc), + sizeof(ArrayTypeDesc), + sizeof(TypeVarTypeDesc), + -1//sizeof(FnPtrTypeDesc) -- variable size +}; +TypeDescType getTypeDescType( PTR_TypeDesc td ) +{ + _ASSERTE(td != NULL); + if( td->IsArray() ) + return TDT_IsArrayTypeDesc; + if( td->HasTypeParam() ) + return TDT_IsParamTypeDesc; + if( td->IsGenericVariable() ) + return TDT_IsTypeVarTypeDesc; + if( td->GetInternalCorElementType() == ELEMENT_TYPE_FNPTR ) + return TDT_IsFnPtrTypeDesc; + return TDT_IsTypeDesc; +} +NativeImageDumper::EnumMnemonics NativeImageDumper::s_TDFlags[] = +{ + +#define TDF_ENTRY(x) NativeImageDumper::EnumMnemonics(TypeDesc:: x, W(#x) ) + TDF_ENTRY(enum_flag_NeedsRestore), + TDF_ENTRY(enum_flag_PreRestored), + TDF_ENTRY(enum_flag_Unrestored), + TDF_ENTRY(enum_flag_UnrestoredTypeKey), + TDF_ENTRY(enum_flag_IsNotFullyLoaded), + TDF_ENTRY(enum_flag_DependenciesLoaded), +#undef TDF_ENTRY +}; + +NativeImageDumper::EnumMnemonics s_CConv[] = +{ +#define CC_ENTRY(x) NativeImageDumper::EnumMnemonics( x, W(#x) ) + +#define CC_CALLCONV_ENTRY(x) NativeImageDumper::EnumMnemonics( x, IMAGE_CEE_CS_CALLCONV_MASK, W(#x) ) + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_VARARG), + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_FIELD), + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_LOCAL_SIG), + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_PROPERTY), + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_UNMGD), + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_GENERICINST), + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_NATIVEVARARG), +#undef CC_CALLCONV_ENTRY + + CC_ENTRY(IMAGE_CEE_CS_CALLCONV_HASTHIS), + CC_ENTRY(IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS), + CC_ENTRY(IMAGE_CEE_CS_CALLCONV_GENERIC) +}; + + +void NativeImageDumper::DumpTypeDesc( PTR_TypeDesc td ) +{ + _ASSERTE(CHECK_OPT(TYPEDESCS)); + TypeDescType tdt = getTypeDescType(td); + int size = g_typeDescSizes[(int)tdt]; + if( size == -1 ) + { + _ASSERTE(tdt == TDT_IsFnPtrTypeDesc); + size = FnPtrTypeDesc::DacSize(PTR_TO_TADDR(td)); + } + DisplayStartStructure( g_typeDescTypeNames[(int)tdt], + DPtrToPreferredAddr(td), size, TYPEDESCS ); + + //first handle the fields of typedesc + WriteFieldCorElementType( m_typeAndFlags, td->GetInternalCorElementType(), + TypeDesc, TYPEDESCS ); + DisplayWriteFieldEnumerated( m_typeAndFlags, td->m_typeAndFlags, TypeDesc, + s_TDFlags, W(", "), TYPEDESCS ); + if( tdt == TDT_IsParamTypeDesc || tdt == TDT_IsArrayTypeDesc ) + { + PTR_ParamTypeDesc ptd(td); + DisplayStartVStructure( "ParamTypeDesc", TYPEDESCS ); + WriteFieldMethodTable( m_TemplateMT, ptd->m_TemplateMT.GetValue(), + ParamTypeDesc, TYPEDESCS ); + WriteFieldTypeHandle( m_Arg, ptd->m_Arg, + ParamTypeDesc, TYPEDESCS ); + DisplayWriteFieldPointer( m_hExposedClassObject, + DataPtrToDisplay(ptd->m_hExposedClassObject), + ParamTypeDesc, TYPEDESCS ); + + DisplayEndVStructure( TYPEDESCS ); //ParamTypeDesc + } + else if( tdt == TDT_IsFnPtrTypeDesc ) + { + PTR_FnPtrTypeDesc ftd(td); + DisplayStartVStructure( "FnPtrTypeDesc", TYPEDESCS ); + DisplayWriteFieldInt( m_NumArgs, ftd->m_NumArgs, FnPtrTypeDesc, + TYPEDESCS ); + DisplayWriteFieldEnumerated( m_CallConv, ftd->m_CallConv, + FnPtrTypeDesc, s_CConv, W(", "), + TYPEDESCS ); + DisplayStartArrayWithOffset( m_RetAndArgTypes, W("[%-4s]: %s"), + FnPtrTypeDesc, TYPEDESCS ); + PTR_TypeHandle args( PTR_HOST_MEMBER_TADDR(FnPtrTypeDesc, ftd, + m_RetAndArgTypes) ); + for( unsigned i = 0; i < ftd->m_NumArgs; ++i ) + { + DisplayStartElement( "Argument", TYPEDESCS ); + DisplayWriteElementInt( "Index", i, TYPEDESCS ); + IF_OPT( TYPEDESCS ) + WriteElementTypeHandle( "TypeHandle", args[i] ); + DisplayEndElement( TYPEDESCS ); + } + DisplayEndArray( "Total Arguments", TYPEDESCS ); + DisplayEndVStructure( TYPEDESCS ); + } + else if( tdt == TDT_IsTypeVarTypeDesc ) + { + PTR_TypeVarTypeDesc tvtd(td); + DisplayStartVStructure( "TypeVarTypeDesc", TYPEDESCS ); + DisplayWriteFieldPointer( m_pModule, + DPtrToPreferredAddr(tvtd->m_pModule), + TypeVarTypeDesc, TYPEDESCS ); + DisplayWriteFieldUInt( m_typeOrMethodDef, + tvtd->m_typeOrMethodDef, + TypeVarTypeDesc, TYPEDESCS ); + DisplayWriteFieldInt( m_numConstraints, tvtd->m_numConstraints, + TypeVarTypeDesc, TYPEDESCS ); + if( tvtd->m_constraints == NULL ) + { + DisplayWriteFieldPointer( m_constraints, NULL, TypeVarTypeDesc, + TYPEDESCS ); + } + else + { + DisplayStartStructureWithOffset( m_constraints, + DPtrToPreferredAddr(tvtd->m_constraints), + sizeof(*tvtd->m_constraints) * + tvtd->m_numConstraints, + TypeVarTypeDesc, TYPEDESCS ); + DisplayStartArray( "Constraints", NULL, TYPEDESCS ); + for( unsigned i = 0; i < tvtd->m_numConstraints; ++i ) + { + WriteElementTypeHandle( "TypeHandle", tvtd->m_constraints[i] ); + } + DisplayEndArray( "Total Constraints", TYPEDESCS ); //Constraints + DisplayEndStructure( TYPEDESCS ); //m_constraints + } + DisplayWriteFieldPointer( m_hExposedClassObject, + DataPtrToDisplay(tvtd->m_hExposedClassObject), + TypeVarTypeDesc, TYPEDESCS ); + DisplayWriteFieldUInt( m_token, tvtd->m_token, TypeVarTypeDesc, + TYPEDESCS ); + DisplayWriteFieldInt( m_index, tvtd->m_index, TypeVarTypeDesc, + TYPEDESCS ); + + DisplayEndVStructure( TYPEDESCS ); //TypeVarTypeDesc + } + + + DisplayEndStructure( TYPEDESCS ); // g_typeDescTypeNames + +} + +void NativeImageDumper::DumpDictionaryEntry( const char * elementName, + DictionaryEntryKind kind, + PTR_DictionaryEntry entry ) +{ + m_display->StartElement( elementName ); + const char * name = NULL; + switch(kind) + { + case EmptySlot: + m_display->WriteEmptyElement("EmptySlot"); + break; + case TypeHandleSlot: + { + TypeHandle th = dac_cast<DPTR(FixupPointer<TypeHandle>)>(entry)->GetValue(); + WriteElementTypeHandle( "TypeHandle", th ); + /* XXX Fri 03/24/2006 + * There is no straightforward home for these, so make sure to + * record them + */ + if( !CORCOMPILE_IS_POINTER_TAGGED(th.AsTAddr()) && th.IsTypeDesc() ) + { + PTR_TypeDesc td(th.AsTypeDesc()); + if( isInRange(PTR_TO_TADDR(td)) ) + { + m_discoveredTypeDescs.AppendEx(td); + } + } + } + break; + case MethodDescSlot: + { + TempBuffer buf; + PTR_MethodDesc md(TO_TADDR(*entry)); + WriteElementMethodDesc( "MethodDesc", md ); + } + break; + case MethodEntrySlot: + name = "MethodEntry"; + goto StandardEntryDisplay; + case ConstrainedMethodEntrySlot: + name = "ConstrainedMethodEntry"; + goto StandardEntryDisplay; + case DispatchStubAddrSlot: + name = "DispatchStubAddr"; + goto StandardEntryDisplay; + /* REVISIT_TODO Tue 10/11/2005 + * Print out name information here + */ + case FieldDescSlot: + name = "FieldDescSlot"; +StandardEntryDisplay: + m_display->WriteElementPointer(name, DataPtrToDisplay((TADDR)*entry)); + break; + default: + _ASSERTE( !"unreachable" ); + } + m_display->EndElement(); //elementName +} + +#ifdef FEATURE_READYTORUN +IMAGE_DATA_DIRECTORY * NativeImageDumper::FindReadyToRunSection(DWORD type) +{ + PTR_READYTORUN_SECTION pSections = dac_cast<PTR_READYTORUN_SECTION>(dac_cast<TADDR>(m_pReadyToRunHeader) + sizeof(READYTORUN_HEADER)); + for (DWORD i = 0; i < m_pReadyToRunHeader->NumberOfSections; i++) + { + // Verify that section types are sorted + _ASSERTE(i == 0 || (pSections[i - 1].Type < pSections[i].Type)); + + READYTORUN_SECTION * pSection = pSections + i; + if (pSection->Type == type) + return &pSection->Section; + } + return NULL; +} + +// +// Ready to Run specific dumping methods +// +void NativeImageDumper::DumpReadyToRun() +{ + m_pReadyToRunHeader = m_decoder.GetReadyToRunHeader(); + + m_nativeReader = NativeFormat::NativeReader(dac_cast<PTR_BYTE>(m_decoder.GetBase()), m_decoder.GetVirtualSize()); + + IMAGE_DATA_DIRECTORY * pRuntimeFunctionsDir = FindReadyToRunSection(READYTORUN_SECTION_RUNTIME_FUNCTIONS); + if (pRuntimeFunctionsDir != NULL) + { + m_pRuntimeFunctions = dac_cast<PTR_RUNTIME_FUNCTION>(m_decoder.GetDirectoryData(pRuntimeFunctionsDir)); + m_nRuntimeFunctions = pRuntimeFunctionsDir->Size / sizeof(T_RUNTIME_FUNCTION); + } + else + { + m_nRuntimeFunctions = 0; + } + + IMAGE_DATA_DIRECTORY * pEntryPointsDir = FindReadyToRunSection(READYTORUN_SECTION_METHODDEF_ENTRYPOINTS); + if (pEntryPointsDir != NULL) + m_methodDefEntryPoints = NativeFormat::NativeArray((TADDR)&m_nativeReader, pEntryPointsDir->VirtualAddress); + + DisplayStartCategory("NativeInfo", NATIVE_INFO); + + IF_OPT(NATIVE_INFO) + DumpReadyToRunHeader(); + + DisplayEndCategory(NATIVE_INFO); //NativeInfo + + IF_OPT_OR3(METHODS, GC_INFO, DISASSEMBLE_CODE) + DumpReadyToRunMethods(); + + IF_OPT(RELOCATIONS) + DumpBaseRelocs(); +} + +const NativeImageDumper::EnumMnemonics s_ReadyToRunFlags[] = +{ +#define RTR_FLAGS(f) NativeImageDumper::EnumMnemonics(f, W(#f)) + RTR_FLAGS(READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE), +#undef RTR_FLAGS +}; + +void NativeImageDumper::DumpReadyToRunHeader() +{ + IF_OPT(NATIVE_INFO) + { + m_display->StartStructure( "READYTORUN_HEADER", + DPtrToPreferredAddr(dac_cast<PTR_READYTORUN_HEADER>(m_pReadyToRunHeader)), + sizeof(*m_pReadyToRunHeader) ); + + DisplayWriteFieldUInt( Signature, m_pReadyToRunHeader->Signature, READYTORUN_HEADER, ALWAYS ); + DisplayWriteFieldUInt( MajorVersion, m_pReadyToRunHeader->MajorVersion, READYTORUN_HEADER, ALWAYS ); + DisplayWriteFieldUInt( MinorVersion, m_pReadyToRunHeader->MinorVersion, READYTORUN_HEADER, ALWAYS ); + + DisplayWriteFieldEnumerated( Flags, m_pReadyToRunHeader->Flags, + READYTORUN_HEADER, s_ReadyToRunFlags, W(", "), + NATIVE_INFO ); + + m_display->EndStructure(); //READYTORUN_HEADER + } +} + +void NativeImageDumper::DumpReadyToRunMethods() +{ + DisplayStartArray("Methods", NULL, METHODS); + + for (uint rid = 1; rid <= m_methodDefEntryPoints.GetCount(); rid++) + { + uint offset; + if (!m_methodDefEntryPoints.TryGetAt(rid - 1, &offset)) + continue; + + uint id; + offset = m_nativeReader.DecodeUnsigned(offset, &id); + + if (id & 1) + { + if (id & 2) + { + uint val; + m_nativeReader.DecodeUnsigned(offset, &val); + offset -= val; + } + + // TODO: Dump fixups from dac_cast<TADDR>(m_pLayout->GetBase()) + offset + + id >>= 2; + } + else + { + id >>= 1; + } + + _ASSERTE(id < m_nRuntimeFunctions); + PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctions + id; + PCODE pEntryPoint = dac_cast<TADDR>(m_decoder.GetBase()) + pRuntimeFunction->BeginAddress; + + SString buf; + AppendTokenName(TokenFromRid(rid, mdtMethodDef), buf, m_import); + + DumpReadyToRunMethod(pEntryPoint, pRuntimeFunction, buf); + } + + DisplayEndArray("Total Methods", METHODS); //Methods +} + +extern PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFunction, /* out */ SIZE_T * pSize); + +void NativeImageDumper::DumpReadyToRunMethod(PCODE pEntryPoint, PTR_RUNTIME_FUNCTION pRuntimeFunction, SString& name) +{ + //Read the GCInfo to get the total method size. + unsigned methodSize = 0; + unsigned gcInfoSize = UINT_MAX; + + SIZE_T nUnwindDataSize; + PTR_VOID pUnwindData = GetUnwindDataBlob(dac_cast<TADDR>(m_decoder.GetBase()), pRuntimeFunction, &nUnwindDataSize); + + // GCInfo immediatelly follows unwind data + PTR_CBYTE gcInfo = dac_cast<PTR_CBYTE>(pUnwindData) + nUnwindDataSize; + + void(*stringOutFn)(const char *, ...); + IF_OPT(GC_INFO) + { + stringOutFn = stringOut; + } + else + { + stringOutFn = nullStringOut; + } + if (gcInfo != NULL) + { + PTR_CBYTE curGCInfoPtr = gcInfo; + g_holdStringOutData.Clear(); + GCDump gcDump(GCINFO_VERSION); + gcDump.gcPrintf = stringOutFn; +#if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER) + UINT32 r2rversion = m_pReadyToRunHeader->MajorVersion; + UINT32 gcInfoVersion = GCInfoToken::ReadyToRunVersionToGcInfoVersion(r2rversion); + GcInfoDecoder gcInfoDecoder({ curGCInfoPtr, gcInfoVersion }, DECODE_CODE_LENGTH); + methodSize = gcInfoDecoder.GetCodeLength(); +#endif + + //dump the data to a string first so we can get the gcinfo size. +#ifdef _TARGET_X86_ + InfoHdr hdr; + stringOutFn("method info Block:\n"); + curGCInfoPtr += gcDump.DumpInfoHdr(curGCInfoPtr, &hdr, &methodSize, 0); + stringOutFn("\n"); +#endif + + IF_OPT(METHODS) + { +#ifdef _TARGET_X86_ + stringOutFn("PointerTable:\n"); + curGCInfoPtr += gcDump.DumpGCTable(curGCInfoPtr, + hdr, + methodSize, 0); + gcInfoSize = curGCInfoPtr - gcInfo; +#elif defined(USE_GC_INFO_DECODER) + stringOutFn("PointerTable:\n"); + curGCInfoPtr += gcDump.DumpGCTable(curGCInfoPtr, + methodSize, 0); + gcInfoSize = (unsigned)(curGCInfoPtr - gcInfo); +#endif + } + + //data is output below. + } + + DisplayStartElement("Method", METHODS); + DisplayWriteElementStringW("Name", (const WCHAR *)name, METHODS); + + DisplayStartStructure("GCInfo", + DPtrToPreferredAddr(gcInfo), + gcInfoSize, + METHODS); + + DisplayStartTextElement("Contents", GC_INFO); + DisplayWriteXmlTextBlock(("%S", (const WCHAR *)g_holdStringOutData), GC_INFO); + DisplayEndTextElement(GC_INFO); //Contents + + DisplayEndStructure(METHODS); //GCInfo + + DisplayStartStructure("Code", DataPtrToDisplay(pEntryPoint), methodSize, + METHODS); + + IF_OPT(DISASSEMBLE_CODE) + { + // Disassemble hot code. Read the code into the host process. + /* REVISIT_TODO Mon 10/24/2005 + * Is this align up right? + */ + BYTE * codeStartHost = + reinterpret_cast<BYTE*>(PTR_READ(pEntryPoint, + (ULONG32)ALIGN_UP(methodSize, + CODE_SIZE_ALIGN))); + DisassembleMethod(codeStartHost, methodSize); + } + + DisplayEndStructure(METHODS); //Code + + DisplayEndElement(METHODS); //Method +} +#endif // FEATURE_READYTORUN + +#if 0 +void NativeImageDumper::RecordTypeRef( mdTypeRef token, PTR_MethodTable mt ) +{ + if( mt != NULL ) + m_mtToTypeRefMap.Add( mt, token ); +} +mdTypeRef NativeImageDumper::FindTypeRefForMT( PTR_MethodTable mt ) +{ + return m_mtToTypeRefMap.Find(mt); +} +#endif + + +/* REVISIT_TODO Mon 10/10/2005 + * Here is where it gets bad. There is no DAC build of gcdump, so instead + * build it directly into the the dac. That's what all these ugly defines + * are all about. + */ +#ifdef __MSC_VER +#pragma warning(disable:4244) // conversion from 'unsigned int' to 'unsigned short', possible loss of data +#pragma warning(disable:4189) // local variable is initialized but not referenced +#endif // __MSC_VER + +#undef assert +#define assert(a) +#define NOTHROW +#define GC_NOTRIGGER +#include <gcdecoder.cpp> +#undef NOTHROW +#undef GC_NOTRIGGER + +#if defined _DEBUG && defined _TARGET_X86_ +// disable FPO for checked build +#pragma optimize("y", off) +#endif + +#undef _ASSERTE +#define _ASSERTE(a) do {} while (0) +#ifdef _TARGET_X86_ +#include <gcdump.cpp> +#endif + +#undef LIMITED_METHOD_CONTRACT +#undef WRAPPER_NO_CONTRACT +#ifdef _TARGET_X86_ +#include <i386/gcdumpx86.cpp> +#else // !_TARGET_X86_ +#undef PREGDISPLAY +#include <gcdumpnonx86.cpp> +#endif // !_TARGET_X86_ + +#ifdef __MSC_VER +#pragma warning(default:4244) +#pragma warning(default:4189) +#endif // __MSC_VER + + +#else //!FEATURE_PREJIT +//dummy implementation for dac +HRESULT ClrDataAccess::DumpNativeImage(CLRDATA_ADDRESS loadedBase, + LPCWSTR name, + IXCLRDataDisplay * display, + IXCLRLibrarySupport * support, + IXCLRDisassemblySupport * dis) +{ + return E_FAIL; +} +#endif //FEATURE_PREJIT diff --git a/src/debug/daccess/nidump.h b/src/debug/daccess/nidump.h new file mode 100644 index 0000000000..d14eb89f24 --- /dev/null +++ b/src/debug/daccess/nidump.h @@ -0,0 +1,624 @@ +// 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. + + +#ifndef _NIDUMP_H_ +#define _NIDUMP_H_ + + +#ifdef FEATURE_PREJIT +#include <daccess.h> + +//some DPTR definitions that aren't elsewhere in the source +typedef DPTR(const COR_SIGNATURE) PTR_CCOR_SIGNATURE; +typedef DPTR(IMAGE_SECTION_HEADER) PTR_IMAGE_SECTION_HEADER; +typedef DPTR(CerNgenRootTable) PTR_CerNgenRootTable; +typedef DPTR(struct CerRoot) PTR_CerRoot; +typedef DPTR(MethodContextElement) PTR_MethodContextElement; +typedef DPTR(ModuleSecurityDescriptor) PTR_ModuleSecurityDescriptor; +typedef DPTR(DictionaryEntry) PTR_DictionaryEntry; +typedef DPTR(GuidInfo) PTR_GuidInfo; +#if defined(FEATURE_COMINTEROP) +typedef DPTR(SparseVTableMap) PTR_SparseVTableMap; +#endif +#if defined(FEATURE_COMINTEROP) +typedef DPTR(ClassFactoryBase) PTR_ClassFactoryBase; +#endif +typedef DPTR(LayoutEEClass) PTR_LayoutEEClass; +typedef DPTR(ArrayClass) PTR_ArrayClass; +typedef DPTR(DelegateEEClass) PTR_DelegateEEClass; +typedef DPTR(UMThunkMarshInfo) PTR_UMThunkMarshInfo; +typedef DPTR(CORCOMPILE_DEPENDENCY) PTR_CORCOMPILE_DEPENDENCY; +typedef DPTR(struct RemotableMethodInfo) PTR_RemotableMethodInfo; +typedef DPTR(struct ModuleCtorInfo) PTR_ModuleCtorInfo; +typedef DPTR(class EEImplMethodDesc) PTR_EEImplMethodDesc; +typedef DPTR(class EEClassLayoutInfo) PTR_EEClassLayoutInfo; +typedef DPTR(class FieldMarshaler) PTR_FieldMarshaler; +typedef DPTR(LPCUTF8) PTR_LPCUTF8; +typedef DPTR(struct STORAGESIGNATURE UNALIGNED) PTR_STORAGESIGNATURE; +typedef DPTR(struct STORAGEHEADER UNALIGNED) PTR_STORAGEHEADER; +typedef DPTR(struct STORAGESTREAM UNALIGNED) PTR_STORAGESTREAM; +typedef DPTR(ArrayMethodDesc) PTR_ArrayMethodDesc; + + +#if 0 +template<typename PtrType> +class TokenHashMap : CClosedHash< Pair<DPTR(PtrType), mdToken> > +{ +public: + typedef DPTR(PtrType) Key; + typedef mdTypeRef Data; + typedef Pair<Key, Data> Entry; + typedef CClosedHash< Entry > Parent; + TokenHashMap(int buckets = 23) : Parent(buckets) + { + + } + ~TokenHashMap() { } + + void Add(const Key key, const Data data) + { + Entry * newEntry = Parent::Add((void*)PTR_HOST_TO_TADDR(key)); + newEntry->First() = key; + newEntry->Second() = data; + } + + Data Find(const Key key) + { + Entry * found = Parent::Find((void*)PTR_HOST_TO_TADDR(key)); + if( !found ) + return mdTokenNil; + else + return found->Second(); + } + inline Key GetKey(Entry * entry) { return entry->First(); } + Parent::ELEMENTSTATUS Status(Entry * entry) + { + if( entry->First() == 0xffffffff && entry->Second() == 0xffffffff ) + return Parent::DELETED; + else if( entry->First() == 0x00000000 && entry->Second() == 0x00000000 ) + return Parent::FREE; + else + return Parent::USED; + } + void SetStatus(Entry * entry, Parent::ELEMENTSTATUS status) + { + switch(status) + { + case Parent::FREE: + entry->First() = Key((TADDR)0x00000000); + entry->Second() = 0x00000000; + break; + case Parent::DELETED: + entry->First() = Key((TADDR)0xffffffff); + entry->Second() = 0xffffffff; + break; + } + } + + unsigned int Compare(const Entry * lhs, Entry * rhs) + { + return lhs->First() == rhs->First() && lhs->Second() == rhs->Second(); + } + + //parent methods + unsigned int Hash(const void *pData) + { + return (int)(INT_PTR)pData; + } + unsigned int Compare(const void * p1, BYTE * p2) + { + return Compare((const Entry *) p1, (Entry*) p2); + } + Parent::ELEMENTSTATUS Status(BYTE * p){ + return Status((Entry*)p); + } + void SetStatus(BYTE * p, Parent::ELEMENTSTATUS status) { + SetStatus((Entry*)p, status); + } + void * GetKey(BYTE *p) { return (void*)GetKey((Entry*)p); } +}; +typedef TokenHashMap<EEClass> EEClassToTypeRefMap; +typedef TokenHashMap<MethodTable> MTToTypeRefMap; +#endif + +class NativeImageDumper +{ +public: + //DPTR to private field needs to be a member of NativeImageDumper +#if defined(FEATURE_COMINTEROP) + typedef DPTR(SparseVTableMap::Entry) PTR_SparseVTableMap_Entry; +#endif + + NativeImageDumper(PTR_VOID loadedBase, const WCHAR * const name, + IXCLRDataDisplay * display, IXCLRLibrarySupport *support, + IXCLRDisassemblySupport * dis); + ~NativeImageDumper(); + + //type dumping methods + void DumpNativeImage(); + + void ComputeMethodFixupHistogram( PTR_Module module ); + void DumpFixupTables( PTR_Module module); + + void WriteElementTypeHandle( const char * name, TypeHandle th ); + void DoWriteFieldFieldDesc( const char * name, unsigned offset, + unsigned fieldSize, PTR_FieldDesc fd ); + void DoWriteFieldMethodDesc( const char * name, unsigned offset, + unsigned fieldSize, PTR_MethodDesc md ); + void DoWriteFieldTypeHandle( const char * name, unsigned offset, + unsigned fieldSize, TypeHandle th ); + void DoWriteFieldMDToken( const char * name, unsigned offset, + unsigned fieldsize, mdToken token, + IMetaDataImport2 *pAssemblyImport = NULL); + void DoWriteFieldMethodTable( const char * name, unsigned offset, + unsigned fieldSize, PTR_MethodTable mt ); + //if fixup is a fixup, it writes the field as if it were a fixup (including + //subelements) and returns true. Otherwise, it returns false. + BOOL DoWriteFieldAsFixup( const char * name, unsigned offset, + unsigned fieldSize, TADDR fixup ); + + void WriteElementMethodTable( const char * name, PTR_MethodTable mt ); + void WriteElementMethodDesc( const char * name, PTR_MethodDesc md ); + + void DoWriteFieldCorElementType( const char * name, unsigned offset, + unsigned fieldSize, CorElementType type ); + void WriteElementMDToken( const char * name, mdToken token ); + + void DoWriteFieldAsHex( const char * name, unsigned offset, + unsigned fieldSize, PTR_BYTE data, + unsigned dataLen ); + + void DumpMethods(PTR_Module module); + + void DumpCompleteMethod(PTR_Module module, MethodIterator& mi); + + void DisassembleMethod(BYTE *method, SIZE_T size); + + void DumpModule( PTR_Module module ); + void DumpNative(); + void DumpNativeHeader(); + + void DumpBaseRelocs(); + void DumpHelperTable(); + + void DumpMethodFixups(PTR_Module module, + TADDR fixupList); + + void DumpTypes( PTR_Module module ); + + void DumpNgenRootTable( PTR_CerNgenRootTable table, const char * name, + unsigned offset, unsigned fieldSize ); + + void DumpMethodTable( PTR_MethodTable mt, const char * name, + PTR_Module module ); + +#ifndef STUB_DISPATCH_ALL + void DumpMethodTableSlotChunk( PTR_PCODE slotChunk, COUNT_T size ); +#endif + + void DumpSlot( unsigned index, PCODE tgt ); + void DumpFieldDesc( PTR_FieldDesc fd, const char * name ); + void DumpEEClassForMethodTable( PTR_MethodTable mt ); + void DumpTypeDesc( PTR_TypeDesc td ); + + void DumpMethodDesc( PTR_MethodDesc md, PTR_Module module ); + void DumpPrecode( PTR_Precode precode, PTR_Module module ); + + + + + + + + //utility routines + void AppendTokenName(mdToken token, SString& str); + void AppendTokenName(mdToken token, SString& str, IMetaDataImport2 *pImport, + bool force = false); + void PrintManifestTokenName(mdToken token, SString& str); + void PrintManifestTokenName(mdToken token, SString& str, + IMetaDataAssemblyImport *pAssemblyImport, + bool force = false); + void WriteElementsFixupBlob(PTR_CORCOMPILE_IMPORT_SECTION pSection, SIZE_T fixup); + void WriteElementsFixupTargetAndName(RVA rva); + void FixupBlobToString(RVA rva, SString& buf); + + void AppendToken(mdToken token, SString& buf); + void AppendToken(mdToken token, SString& buf, IMetaDataImport2 *pImport); + IMetaDataImport2* TypeToString(PTR_CCOR_SIGNATURE &sig, SString& buf); // assumes pImport is m_import + IMetaDataImport2* TypeToString(PTR_CCOR_SIGNATURE &sig, SString& buf, + IMetaDataImport2 *pImport, + IMetaDataImport2 *pOrigImport =NULL); + void MethodTableToString( PTR_MethodTable mt, SString& buf ); + void TypeHandleToString( TypeHandle td, SString& buf ); + void TypeDescToString( PTR_TypeDesc td, SString& buf ); + void DictionaryToArgString( PTR_Dictionary dictionary, unsigned numArgs, SString& buf ); + + void EntryPointToString( PCODE pEntryPoint, SString& buf ); + void MethodDescToString( PTR_MethodDesc md, SString& buf ); + void FieldDescToString( PTR_FieldDesc fd, SString& buf ); + //uses tok to generate a name if fd == NULL + void FieldDescToString( PTR_FieldDesc fd, mdFieldDef tok, SString& buf ); + +#ifdef FEATURE_READYTORUN +private: + READYTORUN_HEADER * m_pReadyToRunHeader; + + PTR_RUNTIME_FUNCTION m_pRuntimeFunctions; + DWORD m_nRuntimeFunctions; + + NativeFormat::NativeReader m_nativeReader; + NativeFormat::NativeArray m_methodDefEntryPoints; + + IMAGE_DATA_DIRECTORY * FindReadyToRunSection(DWORD type); + +public: + void DumpReadyToRun(); + void DumpReadyToRunHeader(); + void DumpReadyToRunMethods(); + void DumpReadyToRunMethod(PCODE pEntryPoint, PTR_RUNTIME_FUNCTION pRuntimeFunction, SString& name); +#endif // FEATURE_READYTORUN + +private: + PEDecoder m_decoder; + const WCHAR * const m_name; + PTR_VOID m_baseAddress; + SIZE_T m_imageSize; + IXCLRDataDisplay * m_display; + IXCLRLibrarySupport * m_librarySupport; + + bool isInRange(TADDR ptr) + { + return dac_cast<TADDR>(m_baseAddress) <= ptr + && ptr < (dac_cast<TADDR>(m_baseAddress) + m_imageSize); + } + + + COUNT_T ** m_fixupHistogram; + + #define COUNT_HISTOGRAM_SIZE 16 + COUNT_T m_fixupCountHistogram[COUNT_HISTOGRAM_SIZE]; + COUNT_T m_fixupCount; //used to track above counts + + // Primary image metadata + IMetaDataImport2 *m_import; + IMetaDataAssemblyImport *m_assemblyImport; + + // Installation manifest metadata. For native images this is metadata + // copied from the IL image. + IMetaDataImport2 *m_manifestImport; + IMetaDataAssemblyImport *m_manifestAssemblyImport; + + //helper for ComputeMethodFixupHistogram + BOOL HandleFixupForHistogram(PTR_CORCOMPILE_IMPORT_SECTION pSection, SIZE_T fixupIndex, SIZE_T *fixupCell); + + //helper for DumpMethodFixups + BOOL HandleFixupForMethodDump(PTR_CORCOMPILE_IMPORT_SECTION pSection, SIZE_T fixupIndex, SIZE_T *fixupCell); + + // Dependencies + +public: + struct Dependency + { + CORCOMPILE_DEPENDENCY * entry; + //CORINFO_ASSEMBLY_HANDLE assembly; + + TADDR pPreferredBase; + TADDR pLoadedAddress; + SIZE_T size; + + PTR_Module pModule; + IMetaDataImport2 *pImport; + TADDR pMetadataStartTarget; + TADDR pMetadataStartHost; + SIZE_T MetadataSize; + bool fIsMscorlib; + bool fIsHardbound; + WCHAR name[128]; + }; + + /* REVISIT_TODO Fri 12/09/2005 + * Perhaps the module and import should be in the dependency. In order to + * properly name tokens in modules w/o import entries. + */ + struct Import + { + PTR_CORCOMPILE_IMPORT_TABLE_ENTRY entry; + Dependency *dependency; + }; +private: + + Dependency *m_dependencies; + COUNT_T m_numDependencies; + Import *m_imports; + COUNT_T m_numImports; + CORCOMPILE_DEPENDENCY m_self; + + bool inline isSelf(const Dependency* dep) { + return &m_dependencies[0] == dep; + } + + + void OpenMetadata(); + void WriteElementsMetadata( const char * elementName, + TADDR data, SIZE_T size ); + NativeImageDumper::Dependency* + GetDependency(mdAssemblyRef token, IMetaDataAssemblyImport *pImport = NULL); + NativeImageDumper::Import *OpenImport(int i); + NativeImageDumper::Dependency * OpenDependency(int index); + void TraceDumpImport(int idx, NativeImageDumper::Import * import); + void TraceDumpDependency(int idx, NativeImageDumper::Dependency * dependency); + mdAssemblyRef MapAssemblyRefToManifest(mdAssemblyRef token, IMetaDataAssemblyImport *pAssemblyImport); + + const Dependency * GetDependencyForFixup(RVA rva); + const Dependency * GetDependencyForModule( PTR_Module module ); +#if 0 + const Import * GetImportForPointer( TADDR ptr ); +#endif + const Dependency * GetDependencyForPointer( TADDR ptr ); +#ifdef MANUAL_RELOCS + template< typename T > + inline T RemapPointerForReloc( T ptr ); + + inline TADDR RemapTAddrForReloc( TADDR ptr ); + + inline TADDR RemapTAddrForReloc( const NativeImageDumper::Dependency * d, + TADDR ptr ); + + template< typename T > + inline T RemapPointerForReloc( const NativeImageDumper::Dependency * d, + T ptr ); +#endif + + + // msdis support + +#if 0 + static size_t TranslateFixupCallback(const DIS *, DIS::ADDR, size_t, WCHAR *, size_t, DWORDLONG *); + static size_t TranslateRegrelCallback(const DIS *, DIS::REGA, DWORD, WCHAR *, size_t, DWORD *); + static size_t TranslateConstCallback(const DIS *, DWORD, WCHAR *, size_t); +#endif + IXCLRDisassemblySupport * m_dis; + static SIZE_T __stdcall TranslateFixupCallback(IXCLRDisassemblySupport *dis, + CLRDATA_ADDRESS addr, + SIZE_T size, __out_ecount(nameSize) WCHAR *name, + SIZE_T nameSize, + DWORDLONG *offset); + static SIZE_T __stdcall TranslateAddressCallback(IXCLRDisassemblySupport *dis, + CLRDATA_ADDRESS addr, + __out_ecount(nameSize) WCHAR *name, SIZE_T nameSize, + DWORDLONG *offset); + size_t TranslateSymbol(IXCLRDisassemblySupport *dis, + CLRDATA_ADDRESS addr, __out_ecount(nameSize) WCHAR *name, + SIZE_T nameSize, DWORDLONG *offset); + + CLRDATA_ADDRESS m_currentAddress; + bool m_currentIsAddress; + + //mscorwks sizes + TADDR m_mscorwksBase; + TADDR m_mscorwksPreferred; + SIZE_T m_mscorwksSize; + + + //internal type dumpers + void DumpDictionaryEntry( const char * name, DictionaryEntryKind kind, + PTR_DictionaryEntry entry ); + void WriteFieldDictionaryLayout( const char * name, unsigned offset, + unsigned fieldSize, + PTR_DictionaryLayout layout, + IMetaDataImport2 * import ); + + + IMAGE_SECTION_HEADER * FindSection( char const * name ); + + + //map traversal methods and helpers + void IterateTypeDefToMTCallback(TADDR taddrTarget, TADDR flags, PTR_LookupMapBase map, DWORD rid); + void IterateTypeRefToMTCallback(TADDR taddrTarget, TADDR flags, PTR_LookupMapBase map, DWORD rid); + void IterateMethodDefToMDCallback(TADDR taddrTarget, TADDR flags, PTR_LookupMapBase map, DWORD rid); + void IterateFieldDefToFDCallback(TADDR taddrTarget, TADDR flags, PTR_LookupMapBase map, DWORD rid); + void IterateMemberRefToDescCallback(TADDR taddrTarget, TADDR flags, PTR_LookupMapBase map, DWORD rid); + void IterateGenericParamToDescCallback(TADDR fdTarget, TADDR flags, PTR_LookupMapBase map, DWORD rid); + void IterateFileReferencesCallback(TADDR moduleTarget, TADDR flags, PTR_LookupMapBase map, DWORD rid); + void IterateManifestModules(TADDR moduleTarget, TADDR flags, PTR_LookupMapBase map, DWORD rid); + + void TraverseMap(PTR_LookupMapBase map, const char * name, unsigned offset, + unsigned fieldSize, + void(NativeImageDumper::*cb)(TADDR, TADDR, PTR_LookupMapBase, DWORD)); + + template<typename HASH_CLASS, typename HASH_ENTRY_CLASS> + void TraverseNgenHash(DPTR(HASH_CLASS) pTable, const char * name, + unsigned offset, unsigned fieldSize, + bool saveClasses, + void (NativeImageDumper::*DisplayEntryFunction)(void *, DPTR(HASH_ENTRY_CLASS), bool), + void *pContext); + template<typename HASH_CLASS, typename HASH_ENTRY_CLASS> + void TraverseNgenPersistedEntries(DPTR(HASH_CLASS) pTable, + DPTR(typename HASH_CLASS::PersistedEntries) pEntries, + bool saveClasses, + void (NativeImageDumper::*DisplayEntryFunction)(void *, DPTR(HASH_ENTRY_CLASS), bool), + void *pContext); + + void TraverseClassHashEntry(void *pContext, PTR_EEClassHashEntry pEntry, bool saveClasses); + void TraverseClassHash(PTR_EEClassHashTable pTable, const char * name, + unsigned offset, unsigned fieldSize, + bool saveClasses); + +#ifdef FEATURE_COMINTEROP + void TraverseGuidToMethodTableEntry(void *pContext, PTR_GuidToMethodTableEntry pEntry, bool saveClasses); + void TraverseGuidToMethodTableHash(PTR_GuidToMethodTableHashTable pTable, const char * name, + unsigned offset, unsigned fieldSize, bool saveClasses); +#endif // FEATURE_COMINTEROP + + void TraverseMemberRefToDescHashEntry(void *pContext, PTR_MemberRefToDescHashEntry pEntry, bool saveClasses); + + void TraverseMemberRefToDescHash(PTR_MemberRefToDescHashTable pTable, const char * name, + unsigned offset, unsigned fieldSize, bool saveClasses); + + + void TraverseTypeHashEntry(void *pContext, PTR_EETypeHashEntry pEntry, bool saveClasses); + void TraverseTypeHash(PTR_EETypeHashTable pTable, const char * name, + unsigned offset, unsigned fieldSize ); + + void TraverseInstMethodHashEntry(void *pContext, PTR_InstMethodHashEntry pEntry, bool saveClasses); + void TraverseInstMethodHash(PTR_InstMethodHashTable pTable, + const char * name, unsigned offset, + unsigned fieldSize, PTR_Module module); + + void TraverseStubMethodHashEntry(void *pContext, PTR_StubMethodHashEntry pEntry, bool saveClasses); + void TraverseStubMethodHash(PTR_StubMethodHashTable pTable, + const char * name, unsigned offset, + unsigned fieldSize, PTR_Module module); + + void DoWriteFieldStr( PTR_BYTE ptr, const char * name, unsigned offset, + unsigned fieldSize ); + + + template<typename T> + TADDR DPtrToPreferredAddr( T ptr ); + + void DumpAssemblySignature(CORCOMPILE_ASSEMBLY_SIGNATURE & assemblySignature); + + SIZE_T CountFields( PTR_MethodTable mt ); + mdToken ConvertToTypeDef( mdToken typeSpecOrRef, IMetaDataImport2* (&pImport) ); + SIZE_T CountDictionariesInClass( mdToken typeDefOrRef, IMetaDataImport2 * pImport ); + PTR_EEClass GetClassFromMT( PTR_MethodTable mt ); + PTR_MethodTable GetParent( PTR_MethodTable mt ); + + const Dependency* GetDependencyFromFD( PTR_FieldDesc fd ); + const Dependency* GetDependencyFromMD( PTR_MethodDesc md ); + const Dependency* GetDependencyFromMT( PTR_MethodTable mt ); + + CLRNativeImageDumpOptions m_dumpOptions; + inline TADDR RvaToDisplay( SIZE_T rva ); + inline TADDR DataPtrToDisplay(TADDR ptr); + inline int CheckOptions( CLRNativeImageDumpOptions opt ); + + //support various LookupMap Iterators + SArray<PTR_MethodTable> m_discoveredMTs; + + struct SlotChunk + { + PTR_PCODE addr; + WORD nSlots; + + inline bool operator==(const SlotChunk& sc) const + { + return (addr == sc.addr) && (nSlots == sc.nSlots); + } + + inline bool operator<(const SlotChunk& sc) const + { + if (addr < sc.addr) + { + return TRUE; + } + else if (addr > sc.addr) + { + return FALSE; + } + else + { + return nSlots < sc.nSlots; + } + } + }; + + SArray<SlotChunk> m_discoveredSlotChunks; + //SArray<PTR_MethodDesc> m_discoveredMDs; + //SArray<PTR_FieldDesc> m_discoveredFDs; + SArray<PTR_MethodTable> m_discoveredClasses; + SArray<PTR_TypeDesc> m_discoveredTypeDescs; + + typedef InlineSString<128> TempBuffer; + + /* XXX Mon 10/03/2005 + * When we encounter pointers from metadata they are already in the host + * process because we read all of metadata in as one big block (since the + * metadata api isn't dac-ized. Map the metadata pointers back to good DAC + * pointers for compatibility with certain sig parsing code. + */ + TADDR m_MetadataStartHost; + TADDR m_MetadataStartTarget; + COUNT_T m_MetadataSize; + + //Support dumping IL. The COR_ILMETHOD_DECODER is not DACized, so read the + //whole IL section in, and translate RVAs into host pointers into the IL + //section copy + RVA m_ILSectionStart; + BYTE * m_ILHostCopy; +#ifdef _DEBUG + COUNT_T m_ILSectionSize; +#endif + + //This is true if we are hard bound to mscorlib. This enables various forms of generics dumping and MT + //dumping that require g_pObjectClass to be set. + bool m_isMscorlibHardBound; + +#if 0 + PTR_CCOR_SIGNATURE metadataToHostDAC( PCCOR_SIGNATURE pSig, + IMetaDataImport2 * import ); +#endif + template<typename T> + DPTR(T) metadataToHostDAC( T * pSig, IMetaDataImport2 * import); + + void DoDumpFieldStub( PTR_Stub stub, unsigned offset, unsigned fieldSize, + const char * name ); +#ifdef FEATURE_COMINTEROP + void DoDumpComPlusCallInfo( PTR_ComPlusCallInfo compluscall ); +#endif // FEATURE_COMINTEROP + + SIZE_T m_sectionAlignment; + inline SIZE_T GetSectionAlignment() const; + +public: + //this is the list of valid precode addresses for the current module. + struct PrecodeRange + { + PrecodeRange( CorCompileSection section, TADDR start, SIZE_T size ) + : m_sectionType(section), m_rangeStart(start), + m_rangeSize(size) { } + CorCompileSection m_sectionType; + TADDR m_rangeStart; + SIZE_T m_rangeSize; + }; +private: + bool isPrecode(TADDR maybePrecode); + void FixupThunkToString(PTR_CORCOMPILE_IMPORT_SECTION pImportSection, TADDR thunkAddr, SString& buf); + +#if 0 + MTToTypeRefMap m_mtToTypeRefMap; + EEClassToTypeRefMap m_eeClassToTypeRefMap; + void RecordTypeRef( mdTypeRef token, PTR_MethodTable mt ); + void RecordTypeRef( mdTypeRef token, PTR_EEClass clazz ); + mdTypeRef FindTypeRefForMT( PTR_MethodTable mt ); + mdTypeRef FindTypeRefForEEClass( PTR_EEClass clazz ); +#endif + + +public: + struct EnumMnemonics + { + EnumMnemonics( DWORD val, const WCHAR * m ) + : value(val), mask(val), mnemonic(m){ } + EnumMnemonics( DWORD val, DWORD msk, const WCHAR * m ) + : value(val), mask(msk), mnemonic(m) { } + DWORD value; + DWORD mask; + const WCHAR * mnemonic; + }; + + static EnumMnemonics s_ModulePersistedFlags[]; + static EnumMnemonics s_MDC[]; + static EnumMnemonics s_MDFlag2[]; + static EnumMnemonics s_TDFlags[]; + static EnumMnemonics s_SSMDExtendedFlags[]; + static EnumMnemonics s_IMDFlags[]; + static EnumMnemonics s_EECLIFlags[]; +}; +#include "nidump.inl" + +#endif //FEATURE_PREJIT +#endif diff --git a/src/debug/daccess/nidump.inl b/src/debug/daccess/nidump.inl new file mode 100644 index 0000000000..541f4194e9 --- /dev/null +++ b/src/debug/daccess/nidump.inl @@ -0,0 +1,169 @@ +// 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. + + +#ifndef _NIDUMP_INL_ +#define _NIDUMP_INL_ +template<typename T> +TADDR NativeImageDumper::DPtrToPreferredAddr( T ptr ) +{ + TADDR tptr = PTR_TO_TADDR(ptr); + return DataPtrToDisplay(tptr); +} + +inline TADDR NativeImageDumper::RvaToDisplay( SIZE_T rva ) +{ + return DataPtrToDisplay(m_decoder.GetRvaData((RVA)rva)); +} +inline TADDR NativeImageDumper::DataPtrToDisplay(TADDR ptr) +{ + if( ptr == NULL || ptr == (TADDR)-1 + || CheckOptions(CLRNATIVEIMAGE_DISABLE_REBASING) ) + return TO_TADDR(ptr); + + if( isInRange(ptr) || m_dependencies == NULL ) + { + //fast path in case the dependencies aren't loaded. + RVA rva = m_decoder.GetDataRva(ptr); + if (CheckOptions(CLRNATIVEIMAGE_FILE_OFFSET)) + return (TADDR) m_decoder.RvaToOffset(rva); + else + return rva + (INT_PTR)m_decoder.GetNativePreferredBase(); + } + if( m_mscorwksBase <= ptr && ptr < (m_mscorwksBase + m_mscorwksSize) ) + { + return ptr - m_mscorwksBase + m_mscorwksPreferred; + } + for( COUNT_T i = 0; i < m_numDependencies; ++i ) + { + const Dependency * dependency = &m_dependencies[i]; + if( dependency->pPreferredBase == NULL ) + continue; + if( dependency->pLoadedAddress <= ptr + && ((dependency->pLoadedAddress + dependency->size) > ptr) ) + { + //found the right target + return ptr - (INT_PTR)dependency->pLoadedAddress + + (INT_PTR)dependency->pPreferredBase; + } + } + return ptr; +} +inline int NativeImageDumper::CheckOptions( CLRNativeImageDumpOptions opt ) +{ + //if( opt == ((CLRNativeImageDumpOptions)~0) ) + //return 1; + return (m_dumpOptions & opt) != 0; +} + + +#if 0 +PTR_CCOR_SIGNATURE +NativeImageDumper::metadataToHostDAC( PCCOR_SIGNATURE pSig, + IMetaDataImport2 * import) +{ + TADDR tsig = TO_TADDR(pSig); + if( m_MetadataSize == 0 ) //assume target + return PTR_CCOR_SIGNATURE(tsig); + + //find the dependency for this import + const Dependency * dependency = NULL; + for( COUNT_T i = 0; i < m_numDependencies; ++i ) + { + if( m_dependencies[i].pImport == import ) + { + dependency = &m_dependencies[i]; + break; + } + } + if( dependency != NULL && dependency->pMetadataStartHost <= tsig + && tsig < (dependency->pMetadataStartHost + + dependency->MetadataSize) ) + { + //host metadata pointer + return PTR_CCOR_SIGNATURE((tsig + - dependency->pMetadataStartHost) + + dependency->pMetadataStartTarget ); + } + return PTR_CCOR_SIGNATURE(tsig); +} +#endif +template<typename T> +DPTR(T) +NativeImageDumper::metadataToHostDAC( T * pSig, + IMetaDataImport2 * import) +{ + TADDR tsig = TO_TADDR(pSig); + if( m_MetadataSize == 0 ) //assume target + return DPTR(T)(tsig); + + //find the dependency for this import + const Dependency * dependency = NULL; + for( COUNT_T i = 0; i < m_numDependencies; ++i ) + { + if( m_dependencies[i].pImport == import ) + { + dependency = &m_dependencies[i]; + break; + } + } + if( dependency != NULL && dependency->pMetadataStartHost <= tsig + && tsig < (dependency->pMetadataStartHost + + dependency->MetadataSize) ) + { + //host metadata pointer + return DPTR(T)((tsig - dependency->pMetadataStartHost) + + dependency->pMetadataStartTarget ); + } + return DPTR(T)(tsig); +} + +inline SIZE_T NativeImageDumper::GetSectionAlignment() const { + _ASSERTE( m_sectionAlignment > 0 ); + return m_sectionAlignment; +} +#ifdef MANUAL_RELOCS +template< typename T > +inline T NativeImageDumper::RemapPointerForReloc( T ptr ) +{ + return T(RemapTAddrForReloc(PTR_TO_TADDR(ptr))); +} +inline TADDR NativeImageDumper::RemapTAddrForReloc( TADDR ptr ) +{ +#if 0 + if( NULL == ptr ) + return ptr; + if( isInRange(ptr) ) + return ptr; + const NativeImageDumper::Dependency * dependency = + GetDependencyForPointer(ptr); + _ASSERTE(dependency); + return RemapTAddrForReloc( dependency, ptr ); +#else + return ptr; +#endif + +} +template< typename T > +inline T +NativeImageDumper::RemapPointerForReloc(const NativeImageDumper::Dependency* dependency, + T ptr ) +{ + return T(RemapTAddrForReloc( dependency, PTR_TO_TADDR(ptr) )); +} +inline TADDR +NativeImageDumper::RemapTAddrForReloc( const NativeImageDumper::Dependency * d, + TADDR ptr ) +{ +#if 0 + if( d->pPreferredBase == d->pLoadedAddress ) + return ptr; + else + return (ptr - d->pPreferredBase) + d->pLoadedAddress; +#else + return ptr; +#endif +} +#endif //MANUAL_RELOCS +#endif //!_NIDUMP_INL_ diff --git a/src/debug/daccess/reimpl.cpp b/src/debug/daccess/reimpl.cpp new file mode 100644 index 0000000000..d1198eea7b --- /dev/null +++ b/src/debug/daccess/reimpl.cpp @@ -0,0 +1,115 @@ +// 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: reimpl.cpp +// + +// +// Data-access-specific reimplementations of standard code. +// +//***************************************************************************** + +#include "stdafx.h" + +// +// Get the Thread instance for a specific OS thread ID +// +// Arguments: +// osThread - the OS thread ID of interest. +// +// Return value: +// A Thread object marshalled from the target corresponding to the specified OS thread +// ID, or NULL if there is no such Thread. +// +// Notes: +// We used to accept a thread ID of '0' to mean "use the current thread", which was based on +// ICLRDataTarget::GetCurrentThreadID. But this is error-prone and not well-defined (many data targets +// don't implement that API). It's better to require explicit thread IDs to be passed down when they +// are needed. +// +Thread* __stdcall +DacGetThread(ULONG32 osThread) +{ + _ASSERTE(osThread > 0); + + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + // Note that if we had access to TLS, we could get this at index gThreadTLSIndex for the specified + // thread. However, this is the only place we might want to use TLS, and it's not performance critical, + // so we haven't added TLS support to ICorDebugDataTarget (the legacy ICLRDataTarget interface has it though) + + // Scan the whole thread store to see if there's a matching thread. + + if (!ThreadStore::s_pThreadStore) + { + return NULL; + } + + Thread* thread = ThreadStore::s_pThreadStore->m_ThreadList.GetHead(); + while (thread) + { + if (thread->GetOSThreadId() == osThread) + { + return thread; + } + + thread = ThreadStore::s_pThreadStore->m_ThreadList.GetNext(thread); + } + + return NULL; +} + +EXTERN_C Thread* GetThread() +{ + // In dac mode it's unlikely that the thread calling dac + // is actually the same "current thread" that the runtime cares + // about. Fail all queries of the current thread by + // the runtime code to catch any inadvertent usage. + // Enumerating the ThreadStore is the proper way to get access + // to specific Thread objects. + DacError(E_UNEXPECTED); + return NULL; +} + +BOOL +DacGetThreadContext(Thread* thread, T_CONTEXT* context) +{ + SUPPORTS_DAC; + + if (!g_dacImpl) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + // XXX Microsoft - How do you retrieve the context for + // a Thread that's not running? + if (!thread->GetOSThreadId() || + thread->GetOSThreadId() == 0xbaadf00d) + { + DacError(E_UNEXPECTED); + UNREACHABLE(); + } + + ULONG32 contextFlags; + + contextFlags = CONTEXT_ALL; + + HRESULT status = + g_dacImpl->m_pTarget-> + GetThreadContext(thread->GetOSThreadId(), contextFlags, + sizeof(*context), (PBYTE)context); + if (status != S_OK) + { + DacError(status); + UNREACHABLE(); + } + + return TRUE; +} + diff --git a/src/debug/daccess/request.cpp b/src/debug/daccess/request.cpp new file mode 100644 index 0000000000..a30ec37eac --- /dev/null +++ b/src/debug/daccess/request.cpp @@ -0,0 +1,4377 @@ +// 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: request.cpp +// + +// +// CorDataAccess::Request implementation. +// +//***************************************************************************** + +#include "stdafx.h" +#include <win32threadpool.h> + +#include <gceewks.cpp> +#include <handletablepriv.h> +#include "typestring.h" +#include <gccover.h> +#include <virtualcallstub.h> +#ifdef FEATURE_COMINTEROP +#include <comcallablewrapper.h> +#endif // FEATURE_COMINTEROP + +#ifndef FEATURE_PAL +// It is unfortunate having to include this header just to get the definition of GenericModeBlock +#include <msodw.h> +#endif // FEATURE_PAL + +// To include definiton of IsThrowableThreadAbortException +#include <exstatecommon.h> + +#include "rejit.h" + + +// GC headers define these to EE-specific stuff that we don't want. +#undef EnterCriticalSection +#undef LeaveCriticalSection + +#define PTR_CDADDR(ptr) TO_CDADDR(PTR_TO_TADDR(ptr)) +#define HOST_CDADDR(host) TO_CDADDR(PTR_HOST_TO_TADDR(host)) + +#define SOSDacEnter() \ + DAC_ENTER(); \ + HRESULT hr = S_OK; \ + EX_TRY \ + { + +#define SOSDacLeave() \ + } \ + EX_CATCH \ + { \ + if (!DacExceptionFilter(GET_EXCEPTION(), this, &hr)) \ + { \ + EX_RETHROW; \ + } \ + } \ + EX_END_CATCH(SwallowAllExceptions) \ + DAC_LEAVE(); + +// Use this when you don't want to instantiate an Object * in the host. +TADDR DACGetMethodTableFromObjectPointer(TADDR objAddr, ICorDebugDataTarget * target) +{ + ULONG32 returned = 0; + TADDR Value = NULL; + + HRESULT hr = target->ReadVirtual(objAddr, (PBYTE)&Value, sizeof(TADDR), &returned); + + if ((hr != S_OK) || (returned != sizeof(TADDR))) + { + return NULL; + } + + Value = Value & ~3; // equivalent to Object::GetGCSafeMethodTable() + return Value; +} + +// Use this when you don't want to instantiate an Object * in the host. +PTR_SyncBlock DACGetSyncBlockFromObjectPointer(TADDR objAddr, ICorDebugDataTarget * target) +{ + ULONG32 returned = 0; + DWORD Value = NULL; + + HRESULT hr = target->ReadVirtual(objAddr - sizeof(DWORD), (PBYTE)&Value, sizeof(DWORD), &returned); + + if ((hr != S_OK) || (returned != sizeof(DWORD))) + { + return NULL; + } + + if ((Value & (BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_IS_HASHCODE)) != BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) + return NULL; + Value &= MASK_SYNCBLOCKINDEX; + + PTR_SyncTableEntry ste = PTR_SyncTableEntry(dac_cast<TADDR>(g_pSyncTable) + (sizeof(SyncTableEntry) * Value)); + return ste->m_SyncBlock; +} + +BOOL DacValidateEEClass(EEClass *pEEClass) +{ + // Verify things are right. + // The EEClass method table pointer should match the method table. + // TODO: Microsoft, need another test for validity, this one isn't always true anymore. + BOOL retval = TRUE; + EX_TRY + { + MethodTable *pMethodTable = pEEClass->GetMethodTable(); + if (!pMethodTable) + { + // PREfix. + retval = FALSE; + } + else if (pEEClass != pMethodTable->GetClass()) + { + retval = FALSE; + } + } + EX_CATCH + { + retval = FALSE; // Something is wrong + } + EX_END_CATCH(SwallowAllExceptions) + return retval; + +} + +BOOL DacValidateMethodTable(MethodTable *pMT, BOOL &bIsFree) +{ + // Verify things are right. + BOOL retval = FALSE; + EX_TRY + { + bIsFree = FALSE; + EEClass *pEEClass = pMT->GetClass(); + if (pEEClass==NULL) + { + // Okay to have a NULL EEClass if this is a free methodtable + CLRDATA_ADDRESS MethTableAddr = HOST_CDADDR(pMT); + CLRDATA_ADDRESS FreeObjMethTableAddr = HOST_CDADDR(g_pFreeObjectMethodTable); + if (MethTableAddr != FreeObjMethTableAddr) + goto BadMethodTable; + + bIsFree = TRUE; + } + else + { + // Standard fast check + if (!pMT->ValidateWithPossibleAV()) + goto BadMethodTable; + + // In rare cases, we've seen the standard check above pass when it shouldn't. + // Insert additional/ad-hoc tests below. + + // Metadata token should look valid for a class + mdTypeDef td = pMT->GetCl(); + if (td != mdTokenNil && TypeFromToken(td) != mdtTypeDef) + goto BadMethodTable; + + // BaseSize should always be greater than 0 for valid objects (unless it's an interface) + // For strings, baseSize is not ptr-aligned + if (!pMT->IsInterface() && !pMT->IsString()) + { + if (pMT->GetBaseSize() == 0 || !IS_ALIGNED(pMT->GetBaseSize(), sizeof(void *))) + goto BadMethodTable; + } + } + + retval = TRUE; + +BadMethodTable: ; + } + EX_CATCH + { + retval = FALSE; // Something is wrong + } + EX_END_CATCH(SwallowAllExceptions) + return retval; + +} + +BOOL DacValidateMD(MethodDesc * pMD) +{ + if (pMD == NULL) + { + return FALSE; + } + + // Verify things are right. + BOOL retval = TRUE; + EX_TRY + { + MethodTable *pMethodTable = pMD->GetMethodTable(); + + // Standard fast check + if (!pMethodTable->ValidateWithPossibleAV()) + { + retval = FALSE; + } + + if (retval && (pMD->GetSlot() >= pMethodTable->GetNumVtableSlots() && !pMD->HasNonVtableSlot())) + { + retval = FALSE; + } + + if (retval && pMD->HasTemporaryEntryPoint()) + { + MethodDesc *pMDCheck = MethodDesc::GetMethodDescFromStubAddr(pMD->GetTemporaryEntryPoint(), TRUE); + + if (PTR_HOST_TO_TADDR(pMD) != PTR_HOST_TO_TADDR(pMDCheck)) + { + retval = FALSE; + } + } + + if (retval && pMD->HasNativeCode()) + { + PCODE jitCodeAddr = pMD->GetNativeCode(); + + MethodDesc *pMDCheck = ExecutionManager::GetCodeMethodDesc(jitCodeAddr); + if (pMDCheck) + { + // Check that the given MethodDesc matches the MethodDesc from + // the CodeHeader + if (PTR_HOST_TO_TADDR(pMD) != PTR_HOST_TO_TADDR(pMDCheck)) + { + retval = FALSE; + } + } + else + { + retval = FALSE; + } + } + } + EX_CATCH + { + retval = FALSE; // Something is wrong + } + EX_END_CATCH(SwallowAllExceptions) + return retval; +} + +BOOL DacValidateMD(LPCVOID pMD) +{ + return DacValidateMD((MethodDesc *)pMD); +} + +VOID GetJITMethodInfo (EECodeInfo * pCodeInfo, JITTypes *pJITType, CLRDATA_ADDRESS *pGCInfo) +{ + DWORD dwType = pCodeInfo->GetJitManager()->GetCodeType(); + if (IsMiIL(dwType)) + { + *pJITType = TYPE_JIT; + } + else if (IsMiNative(dwType)) + { + *pJITType = TYPE_PJIT; + } + else + { + *pJITType = TYPE_UNKNOWN; + } + + *pGCInfo = (CLRDATA_ADDRESS)PTR_TO_TADDR(pCodeInfo->GetGCInfo()); +} + + +HRESULT +ClrDataAccess::GetWorkRequestData(CLRDATA_ADDRESS addr, struct DacpWorkRequestData *workRequestData) +{ + if (addr == 0 || workRequestData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + WorkRequest *pRequest = PTR_WorkRequest(TO_TADDR(addr)); + workRequestData->Function = (TADDR)(pRequest->Function); + workRequestData->Context = (TADDR)(pRequest->Context); + workRequestData->NextWorkRequest = (TADDR)(pRequest->next); + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetHillClimbingLogEntry(CLRDATA_ADDRESS addr, struct DacpHillClimbingLogEntry *entry) +{ + if (addr == 0 || entry == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + HillClimbingLogEntry *pLogEntry = PTR_HillClimbingLogEntry(TO_TADDR(addr)); + entry->TickCount = pLogEntry->TickCount; + entry->NewControlSetting = pLogEntry->NewControlSetting; + entry->LastHistoryCount = pLogEntry->LastHistoryCount; + entry->LastHistoryMean = pLogEntry->LastHistoryMean; + entry->Transition = pLogEntry->Transition; + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetThreadpoolData(struct DacpThreadpoolData *threadpoolData) +{ + if (threadpoolData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + threadpoolData->cpuUtilization = ThreadpoolMgr::cpuUtilization; + threadpoolData->MinLimitTotalWorkerThreads = ThreadpoolMgr::MinLimitTotalWorkerThreads; + threadpoolData->MaxLimitTotalWorkerThreads = ThreadpoolMgr::MaxLimitTotalWorkerThreads; + + // + // Read ThreadpoolMgr::WorkerCounter + // + TADDR pCounter = DacGetTargetAddrForHostAddr(&ThreadpoolMgr::WorkerCounter,true); + ThreadpoolMgr::ThreadCounter counter; + DacReadAll(pCounter,&counter,sizeof(ThreadpoolMgr::ThreadCounter),true); + ThreadpoolMgr::ThreadCounter::Counts counts = counter.counts; + + threadpoolData->NumWorkingWorkerThreads = counts.NumWorking; + threadpoolData->NumIdleWorkerThreads = counts.NumActive - counts.NumWorking; + threadpoolData->NumRetiredWorkerThreads = counts.NumRetired; + + threadpoolData->FirstUnmanagedWorkRequest = HOST_CDADDR(ThreadpoolMgr::WorkRequestHead); + + threadpoolData->HillClimbingLog = dac_cast<TADDR>(&HillClimbingLog); + threadpoolData->HillClimbingLogFirstIndex = HillClimbingLogFirstIndex; + threadpoolData->HillClimbingLogSize = HillClimbingLogSize; + + + // + // Read ThreadpoolMgr::CPThreadCounter + // + pCounter = DacGetTargetAddrForHostAddr(&ThreadpoolMgr::CPThreadCounter,true); + DacReadAll(pCounter,&counter,sizeof(ThreadpoolMgr::ThreadCounter),true); + counts = counter.counts; + + threadpoolData->NumCPThreads = (LONG)(counts.NumActive + counts.NumRetired); + threadpoolData->NumFreeCPThreads = (LONG)(counts.NumActive - counts.NumWorking); + threadpoolData->MaxFreeCPThreads = ThreadpoolMgr::MaxFreeCPThreads; + threadpoolData->NumRetiredCPThreads = (LONG)(counts.NumRetired); + threadpoolData->MaxLimitTotalCPThreads = ThreadpoolMgr::MaxLimitTotalCPThreads; + threadpoolData->CurrentLimitTotalCPThreads = (LONG)(counts.NumActive); //legacy: currently has no meaning + threadpoolData->MinLimitTotalCPThreads = ThreadpoolMgr::MinLimitTotalCPThreads; + + TADDR pEntry = DacGetTargetAddrForHostAddr(&ThreadpoolMgr::TimerQueue,true); + ThreadpoolMgr::LIST_ENTRY entry; + DacReadAll(pEntry,&entry,sizeof(ThreadpoolMgr::LIST_ENTRY),true); + TADDR node = (TADDR) entry.Flink; + threadpoolData->NumTimers = 0; + while (node && node != pEntry) + { + threadpoolData->NumTimers++; + DacReadAll(node,&entry,sizeof(ThreadpoolMgr::LIST_ENTRY),true); + node = (TADDR) entry.Flink; + } + + threadpoolData->AsyncTimerCallbackCompletionFPtr = (CLRDATA_ADDRESS) GFN_TADDR(ThreadpoolMgr__AsyncTimerCallbackCompletion); + SOSDacLeave(); + return hr; +} + +HRESULT ClrDataAccess::GetThreadStoreData(struct DacpThreadStoreData *threadStoreData) +{ + SOSDacEnter(); + + ThreadStore* threadStore = ThreadStore::s_pThreadStore; + if (!threadStore) + { + hr = E_UNEXPECTED; + } + else + { + // initialize the fields of our local structure + threadStoreData->threadCount = threadStore->m_ThreadCount; + threadStoreData->unstartedThreadCount = threadStore->m_UnstartedThreadCount; + threadStoreData->backgroundThreadCount = threadStore->m_BackgroundThreadCount; + threadStoreData->pendingThreadCount = threadStore->m_PendingThreadCount; + threadStoreData->deadThreadCount = threadStore->m_DeadThreadCount; + threadStoreData->fHostConfig = g_fHostConfig; + + // identify the "important" threads + threadStoreData->firstThread = HOST_CDADDR(threadStore->m_ThreadList.GetHead()); + threadStoreData->finalizerThread = HOST_CDADDR(g_pFinalizerThread); + threadStoreData->gcThread = HOST_CDADDR(g_pSuspensionThread); + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetStressLogAddress(CLRDATA_ADDRESS *stressLog) +{ + if (stressLog == NULL) + return E_INVALIDARG; + +#ifdef STRESS_LOG + SOSDacEnter(); + if (g_pStressLog.IsValid()) + *stressLog = HOST_CDADDR(g_pStressLog); + else + hr = E_FAIL; + + SOSDacLeave(); + return hr; +#else + return E_NOTIMPL; +#endif // STRESS_LOG +} + +HRESULT +ClrDataAccess::GetJitManagerList(unsigned int count, struct DacpJitManagerInfo managers[], unsigned int *pNeeded) +{ + SOSDacEnter(); + + if (managers) + { + if (count >= 1) + { + EEJitManager * managerPtr = ExecutionManager::GetEEJitManager(); + + DacpJitManagerInfo *currentPtr = &managers[0]; + currentPtr->managerAddr = HOST_CDADDR(managerPtr); + currentPtr->codeType = managerPtr->GetCodeType(); + + EEJitManager *eeJitManager = PTR_EEJitManager(PTR_HOST_TO_TADDR(managerPtr)); + currentPtr->ptrHeapList = HOST_CDADDR(eeJitManager->m_pCodeHeap); + } +#ifdef FEATURE_PREJIT + if (count >= 2) + { + NativeImageJitManager * managerPtr = ExecutionManager::GetNativeImageJitManager(); + DacpJitManagerInfo *currentPtr = &managers[1]; + currentPtr->managerAddr = HOST_CDADDR(managerPtr); + currentPtr->codeType = managerPtr->GetCodeType(); + } +#endif + } + else if (pNeeded) + { + *pNeeded = 1; +#ifdef FEATURE_PREJIT + (*pNeeded)++; +#endif + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetMethodTableSlot(CLRDATA_ADDRESS mt, unsigned int slot, CLRDATA_ADDRESS *value) +{ + if (mt == 0 || value == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + MethodTable* mTable = PTR_MethodTable(TO_TADDR(mt)); + BOOL bIsFree = FALSE; + if (!DacValidateMethodTable(mTable, bIsFree)) + { + hr = E_INVALIDARG; + } + else if (slot < mTable->GetNumVtableSlots()) + { + // Now get the slot: + *value = mTable->GetRestoredSlot(slot); + } + else + { + hr = E_INVALIDARG; + MethodTable::IntroducedMethodIterator it(mTable); + for (; it.IsValid() && FAILED(hr); it.Next()) + { + MethodDesc * pMD = it.GetMethodDesc(); + if (pMD->GetSlot() == slot) + { + *value = pMD->GetMethodEntryPoint(); + hr = S_OK; + } + } + } + + SOSDacLeave(); + return hr; +} + + +HRESULT +ClrDataAccess::GetCodeHeapList(CLRDATA_ADDRESS jitManager, unsigned int count, struct DacpJitCodeHeapInfo codeHeaps[], unsigned int *pNeeded) +{ + if (jitManager == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + EEJitManager *pJitManager = PTR_EEJitManager(TO_TADDR(jitManager)); + HeapList *heapList = pJitManager->m_pCodeHeap; + + if (codeHeaps) + { + unsigned int i = 0; + while ((heapList != NULL) && (i < count)) + { + // What type of CodeHeap pointer do we have? + CodeHeap *codeHeap = heapList->pHeap; + TADDR ourVTablePtr = VPTR_HOST_VTABLE_TO_TADDR(*(LPVOID*)codeHeap); + if (ourVTablePtr == LoaderCodeHeap::VPtrTargetVTable()) + { + LoaderCodeHeap *loaderCodeHeap = PTR_LoaderCodeHeap(PTR_HOST_TO_TADDR(codeHeap)); + codeHeaps[i].codeHeapType = CODEHEAP_LOADER; + codeHeaps[i].LoaderHeap = + TO_CDADDR(PTR_HOST_MEMBER_TADDR(LoaderCodeHeap, loaderCodeHeap, m_LoaderHeap)); + } + else if (ourVTablePtr == HostCodeHeap::VPtrTargetVTable()) + { + HostCodeHeap *hostCodeHeap = PTR_HostCodeHeap(PTR_HOST_TO_TADDR(codeHeap)); + codeHeaps[i].codeHeapType = CODEHEAP_HOST; + codeHeaps[i].HostData.baseAddr = PTR_CDADDR(hostCodeHeap->m_pBaseAddr); + codeHeaps[i].HostData.currentAddr = PTR_CDADDR(hostCodeHeap->m_pLastAvailableCommittedAddr); + } + else + { + codeHeaps[i].codeHeapType = CODEHEAP_UNKNOWN; + } + heapList = heapList->hpNext; + i++; + } + + if (pNeeded) + *pNeeded = i; + } + else if (pNeeded) + { + int i = 0; + while (heapList != NULL) + { + heapList = heapList->hpNext; + i++; + } + + *pNeeded = i; + } + else + { + hr = E_INVALIDARG; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetStackLimits(CLRDATA_ADDRESS threadPtr, CLRDATA_ADDRESS *lower, + CLRDATA_ADDRESS *upper, CLRDATA_ADDRESS *fp) +{ + if (threadPtr == 0 || (lower == NULL && upper == NULL && fp == NULL)) + return E_INVALIDARG; + + SOSDacEnter(); + + Thread * thread = PTR_Thread(TO_TADDR(threadPtr)); + + if (lower) + *lower = TO_CDADDR(thread->GetCachedStackBase().GetAddr()); + + if (upper) + *upper = TO_CDADDR(thread->GetCachedStackLimit().GetAddr()); + + if (fp) + *fp = PTR_HOST_MEMBER_TADDR(Thread, thread, m_pFrame); + + SOSDacLeave(); + + return hr; +} + +HRESULT +ClrDataAccess::GetRegisterName(int regNum, unsigned int count, __out_z __inout_ecount(count) wchar_t *buffer, unsigned int *pNeeded) +{ + if (!buffer && !pNeeded) + return E_POINTER; + +#ifdef _TARGET_AMD64_ + static const wchar_t *regs[] = + { + W("rax"), W("rcx"), W("rdx"), W("rbx"), W("rsp"), W("rbp"), W("rsi"), W("rdi"), + W("r8"), W("r9"), W("r10"), W("r11"), W("r12"), W("r13"), W("r14"), W("r15"), + }; +#elif defined(_TARGET_ARM_) + static const wchar_t *regs[] = + { + W("r0"), + W("r1"), + W("r2"), + W("r3"), + W("r4"), + W("r5"), + W("r6"), + W("r7"), + W("r8"), W("r9"), W("r10"), W("r11"), W("r12"), W("sp"), W("lr") + }; +#elif defined(_TARGET_ARM64_) + static const wchar_t *regs[] = + { + W("X0"), + W("X1"), + W("X2"), + W("X3"), + W("X4"), + W("X5"), + W("X6"), + W("X7"), + W("X8"), W("X9"), W("X10"), W("X11"), W("X12"), W("X13"), W("X14"), W("X15"), W("X16"), W("X17"), + W("X18"), W("X19"), W("X20"), W("X21"), W("X22"), W("X23"), W("X24"), W("X25"), W("X26"), W("X27"), + W("X28"), W("Fp"), W("Sp"), W("Lr") + }; +#elif defined(_TARGET_X86_) + static const wchar_t *regs[] = + { + W("eax"), W("ecx"), W("edx"), W("ebx"), W("esp"), W("ebp"), W("esi"), W("edi"), + }; +#endif + + // Caller frame registers are encoded as "-(reg+1)". + bool callerFrame = regNum < 0; + if (callerFrame) + regNum = -regNum-1; + + if ((unsigned int)regNum >= _countof(regs)) + return E_UNEXPECTED; + + + const wchar_t caller[] = W("caller."); + unsigned int needed = (callerFrame?(unsigned int)wcslen(caller):0) + (unsigned int)wcslen(regs[regNum]) + 1; + if (pNeeded) + *pNeeded = needed; + + if (buffer) + { + _snwprintf_s(buffer, count, _TRUNCATE, W("%s%s"), callerFrame ? caller : W(""), regs[regNum]); + if (count < needed) + return S_FALSE; + } + + return S_OK; +} + +HRESULT +ClrDataAccess::GetStackReferences(DWORD osThreadID, ISOSStackRefEnum **ppEnum) +{ + if (ppEnum == NULL) + return E_POINTER; + + SOSDacEnter(); + + DacStackReferenceWalker *walker = new (nothrow) DacStackReferenceWalker(this, osThreadID); + + if (walker == NULL) + { + hr = E_OUTOFMEMORY; + } + else + { + hr = walker->Init(); + + if (SUCCEEDED(hr)) + hr = walker->QueryInterface(__uuidof(ISOSStackRefEnum), (void**)ppEnum); + + if (FAILED(hr)) + { + delete walker; + *ppEnum = NULL; + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetThreadFromThinlockID(UINT thinLockId, CLRDATA_ADDRESS *pThread) +{ + if (pThread == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + Thread *thread = g_pThinLockThreadIdDispenser->IdToThread(thinLockId); + *pThread = PTR_HOST_TO_TADDR(thread); + + SOSDacLeave(); + return hr; +} + + +HRESULT +ClrDataAccess::GetThreadAllocData(CLRDATA_ADDRESS addr, struct DacpAllocData *data) +{ + if (data == NULL) + return E_POINTER; + + SOSDacEnter(); + + Thread* thread = PTR_Thread(TO_TADDR(addr)); + + data->allocBytes = TO_CDADDR(thread->m_alloc_context.alloc_bytes); + data->allocBytesLoh = TO_CDADDR(thread->m_alloc_context.alloc_bytes_loh); + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetHeapAllocData(unsigned int count, struct DacpGenerationAllocData *data, unsigned int *pNeeded) +{ + if (data == 0 && pNeeded == NULL) + return E_INVALIDARG; + + SOSDacEnter(); +#if defined(FEATURE_SVR_GC) + if (GCHeap::IsServerHeap()) + { + hr = GetServerAllocData(count, data, pNeeded); + } + else +#endif //FEATURE_SVR_GC + { + if (pNeeded) + *pNeeded = 1; + + if (data && count >= 1) + { + for (int i=0;i<NUMBERGENERATIONS;i++) + { + data[0].allocData[i].allocBytes = (CLRDATA_ADDRESS)(ULONG_PTR) WKS::generation_table[i].allocation_context.alloc_bytes; + data[0].allocData[i].allocBytesLoh = (CLRDATA_ADDRESS)(ULONG_PTR) WKS::generation_table[i].allocation_context.alloc_bytes_loh; + } + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetThreadData(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *threadData) +{ + SOSDacEnter(); + + // marshal the Thread object from the target + Thread* thread = PTR_Thread(TO_TADDR(threadAddr)); + + // initialize our local copy from the marshaled target Thread instance + ZeroMemory (threadData, sizeof(DacpThreadData)); + threadData->corThreadId = thread->m_ThreadId; + threadData->osThreadId = thread->m_OSThreadId; + threadData->state = thread->m_State; + threadData->preemptiveGCDisabled = thread->m_fPreemptiveGCDisabled; + threadData->allocContextPtr = TO_CDADDR(thread->m_alloc_context.alloc_ptr); + threadData->allocContextLimit = TO_CDADDR(thread->m_alloc_context.alloc_limit); + + // @todo Microsoft: the following assignment is pointless--we're just getting the + // target address of the m_pFiberData field of the Thread instance. Then we're going to + // compute it again as the argument to ReadVirtual. Ultimately, we want the value of + // that field, not its address. We already have that value as part of thread (the + // marshaled Thread instance).This should just go away and we should simply have: + // threadData->fiberData = TO_CDADDR(thread->m_pFiberData ); + // instead of the next 11 lines. + threadData->fiberData = (CLRDATA_ADDRESS)PTR_HOST_MEMBER_TADDR(Thread, thread, m_pFiberData); + + ULONG32 returned = 0; + TADDR Value = NULL; + HRESULT hr = m_pTarget->ReadVirtual(PTR_HOST_MEMBER_TADDR(Thread, thread, m_pFiberData), + (PBYTE)&Value, + sizeof(TADDR), + &returned); + + if ((hr == S_OK) && (returned == sizeof(TADDR))) + { + threadData->fiberData = (CLRDATA_ADDRESS) Value; + } + + threadData->pFrame = PTR_CDADDR(thread->m_pFrame); + threadData->context = PTR_CDADDR(thread->m_Context); + threadData->domain = PTR_CDADDR(thread->m_pDomain); + threadData->lockCount = thread->m_dwLockCount; +#ifndef FEATURE_PAL + threadData->teb = TO_CDADDR(thread->m_pTEB); +#else + threadData->teb = NULL; +#endif + threadData->lastThrownObjectHandle = + TO_CDADDR(thread->m_LastThrownObjectHandle); + threadData->nextThread = + HOST_CDADDR(ThreadStore::s_pThreadStore->m_ThreadList.GetNext(thread)); +#ifdef WIN64EXCEPTIONS + if (thread->m_ExceptionState.m_pCurrentTracker) + { + threadData->firstNestedException = PTR_HOST_TO_TADDR( + thread->m_ExceptionState.m_pCurrentTracker->m_pPrevNestedInfo); + } +#else + threadData->firstNestedException = PTR_HOST_TO_TADDR( + thread->m_ExceptionState.m_currentExInfo.m_pPrevNestedInfo); +#endif // _WIN64 + + SOSDacLeave(); + return hr; +} + +#ifdef FEATURE_REJIT +void CopyReJitInfoToReJitData(ReJitInfo * pReJitInfo, DacpReJitData * pReJitData) +{ + pReJitData->rejitID = pReJitInfo->m_pShared->GetId(); + pReJitData->NativeCodeAddr = pReJitInfo->m_pCode; + + switch (pReJitInfo->m_pShared->GetState()) + { + default: + _ASSERTE(!"Unknown SharedRejitInfo state. DAC should be updated to understand this new state."); + pReJitData->flags = DacpReJitData::kUnknown; + break; + + case SharedReJitInfo::kStateRequested: + pReJitData->flags = DacpReJitData::kRequested; + break; + + case SharedReJitInfo::kStateActive: + pReJitData->flags = DacpReJitData::kActive; + break; + + case SharedReJitInfo::kStateReverted: + pReJitData->flags = DacpReJitData::kReverted; + break; + } +} +#endif // FEATURE_REJIT + + + +//--------------------------------------------------------------------------------------- +// +// Given a method desc addr, this loads up DacpMethodDescData and multiple DacpReJitDatas +// with data on that method +// +// Arguments: +// * methodDesc - MD to look up +// * ip - IP address of interest (e.g., from an !ip2md call). This is used to ensure +// the rejitted version corresponding to this IP is returned. May be NULL if you +// don't care. +// * methodDescData - [out] DacpMethodDescData to populate +// * cRevertedRejitVersions - Number of entries allocated in rgRevertedRejitData +// array +// * rgRevertedRejitData - [out] Array of DacpReJitDatas to populate with rejitted +// rejit version data +// * pcNeededRevertedRejitData - [out] If cRevertedRejitVersions==0, the total +// number of available rejit versions (including the current version) is +// returned here. Else, the number of reverted rejit data actually fetched is +// returned here. +// +// Return Value: +// HRESULT indicating success or failure. +// + +HRESULT ClrDataAccess::GetMethodDescData( + CLRDATA_ADDRESS methodDesc, + CLRDATA_ADDRESS ip, + struct DacpMethodDescData *methodDescData, + ULONG cRevertedRejitVersions, + DacpReJitData * rgRevertedRejitData, + ULONG * pcNeededRevertedRejitData) +{ + if (methodDesc == 0) + return E_INVALIDARG; + + if ((cRevertedRejitVersions != 0) && (rgRevertedRejitData == NULL)) + { + return E_INVALIDARG; + } + + if ((rgRevertedRejitData != NULL) && (pcNeededRevertedRejitData == NULL)) + { + // If you're asking for reverted rejit data, you'd better ask for the number of + // elements we return + return E_INVALIDARG; + } + + SOSDacEnter(); + + PTR_MethodDesc pMD = PTR_MethodDesc(TO_TADDR(methodDesc)); + + if (!DacValidateMD(pMD)) + { + hr = E_INVALIDARG; + } + else + { + ZeroMemory(methodDescData,sizeof(DacpMethodDescData)); + if (rgRevertedRejitData != NULL) + ZeroMemory(rgRevertedRejitData, sizeof(*rgRevertedRejitData)*cRevertedRejitVersions); + if (pcNeededRevertedRejitData != NULL) + *pcNeededRevertedRejitData = 0; + + methodDescData->requestedIP = ip; + methodDescData->bHasNativeCode = pMD->HasNativeCode(); + methodDescData->bIsDynamic = (pMD->IsLCGMethod()) ? TRUE : FALSE; + methodDescData->wSlotNumber = pMD->GetSlot(); + if (pMD->HasNativeCode()) + { + methodDescData->NativeCodeAddr = TO_CDADDR(pMD->GetNativeCode()); +#ifdef DBG_TARGET_ARM + methodDescData->NativeCodeAddr &= ~THUMB_CODE; +#endif + } + else + { + methodDescData->NativeCodeAddr = (CLRDATA_ADDRESS)-1; + } + methodDescData->AddressOfNativeCodeSlot = pMD->HasNativeCodeSlot() ? + TO_CDADDR(pMD->GetAddrOfNativeCodeSlot()) : NULL; + methodDescData->MDToken = pMD->GetMemberDef(); + methodDescData->MethodDescPtr = methodDesc; + methodDescData->MethodTablePtr = HOST_CDADDR(pMD->GetMethodTable()); + methodDescData->ModulePtr = HOST_CDADDR(pMD->GetModule()); + +#ifdef FEATURE_REJIT + + // If rejit info is appropriate, get the following: + // * ReJitInfo for the current, active version of the method + // * ReJitInfo for the requested IP (for !ip2md and !u) + // * ReJitInfos for all reverted versions of the method (up to + // cRevertedRejitVersions) + // + // Minidumps will not have all this rejit info, and failure to get rejit info + // should not be fatal. So enclose all rejit stuff in a try. + + EX_TRY + { + ReJitManager * pReJitMgr = pMD->GetReJitManager(); + + // Current ReJitInfo + ReJitInfo * pReJitInfoCurrent = pReJitMgr->FindNonRevertedReJitInfo(pMD); + if (pReJitInfoCurrent != NULL) + { + CopyReJitInfoToReJitData(pReJitInfoCurrent, &methodDescData->rejitDataCurrent); + } + + // Requested ReJitInfo + _ASSERTE(methodDescData->rejitDataRequested.rejitID == 0); + if (methodDescData->requestedIP != NULL) + { + ReJitInfo * pReJitInfoRequested = pReJitMgr->FindReJitInfo( + pMD, + CLRDATA_ADDRESS_TO_TADDR(methodDescData->requestedIP), + NULL /* reJitId */); + + if (pReJitInfoRequested != NULL) + { + CopyReJitInfoToReJitData(pReJitInfoRequested, &methodDescData->rejitDataRequested); + } + } + + // Total number of jitted rejit versions + ULONG cJittedRejitVersions; + if (SUCCEEDED(pReJitMgr->GetReJITIDs(pMD, 0 /* cReJitIds */, &cJittedRejitVersions, NULL /* reJitIds */))) + { + methodDescData->cJittedRejitVersions = cJittedRejitVersions; + } + + // Reverted ReJitInfos + if (rgRevertedRejitData == NULL) + { + // No reverted rejit versions will be returned, but maybe caller wants a + // count of all versions + if (pcNeededRevertedRejitData != NULL) + { + *pcNeededRevertedRejitData = methodDescData->cJittedRejitVersions; + } + } + else + { + // Caller wants some reverted rejit versions. Gather reverted rejit version data to return + ULONG cReJitIds; + StackSArray<ReJITID> reJitIds; + + // Prepare array to populate with rejitids. "+ 1" because GetReJITIDs + // returns all available rejitids, including the rejitid for the one non-reverted + // current version. + ReJITID * rgReJitIds = reJitIds.OpenRawBuffer(cRevertedRejitVersions + 1); + if (rgReJitIds != NULL) + { + hr = pReJitMgr->GetReJITIDs(pMD, cRevertedRejitVersions + 1, &cReJitIds, rgReJitIds); + if (SUCCEEDED(hr)) + { + // Go through rejitids. For each reverted one, populate a entry in rgRevertedRejitData + reJitIds.CloseRawBuffer(cReJitIds); + ULONG iRejitDataReverted = 0; + for (COUNT_T i=0; + (i < cReJitIds) && (iRejitDataReverted < cRevertedRejitVersions); + i++) + { + ReJitInfo * pRejitInfo = pReJitMgr->FindReJitInfo( + pMD, + NULL /* pCodeStart */, + reJitIds[i]); + + if ((pRejitInfo == NULL) || + (pRejitInfo->m_pShared->GetState() != SharedReJitInfo::kStateReverted)) + { + continue; + } + + CopyReJitInfoToReJitData(pRejitInfo, &rgRevertedRejitData[iRejitDataReverted]); + iRejitDataReverted++; + } + // pcNeededRevertedRejitData != NULL as per condition at top of function (cuz rgRevertedRejitData != + // NULL). + *pcNeededRevertedRejitData = iRejitDataReverted; + } + } + } + } + EX_CATCH + { + if (pcNeededRevertedRejitData != NULL) + *pcNeededRevertedRejitData = 0; + } + EX_END_CATCH(SwallowAllExceptions) + hr = S_OK; // Failure to get rejitids is not fatal +#endif // FEATURE_REJIT + +#if defined(HAVE_GCCOVER) + if (pMD->m_GcCover) + { + EX_TRY + { + // In certain minidumps, we won't save the gccover information. + // (it would be unwise to do so, it is heavy and not a customer scenario). + methodDescData->GCStressCodeCopy = HOST_CDADDR(pMD->m_GcCover) + offsetof(GCCoverageInfo, savedCode); + } + EX_CATCH + { + methodDescData->GCStressCodeCopy = 0; + } + EX_END_CATCH(SwallowAllExceptions) + } + else +#endif // HAVE_GCCOVER + + // Set this above Dario since you know how to tell if dynamic + if (methodDescData->bIsDynamic) + { + DynamicMethodDesc *pDynamicMethod = PTR_DynamicMethodDesc(TO_TADDR(methodDesc)); + if (pDynamicMethod) + { + LCGMethodResolver *pResolver = pDynamicMethod->GetLCGMethodResolver(); + if (pResolver) + { + OBJECTREF value = pResolver->GetManagedResolver(); + if (value) + { + FieldDesc *pField = (&g_Mscorlib)->GetField(FIELD__DYNAMICRESOLVER__DYNAMIC_METHOD); + _ASSERTE(pField); + value = pField->GetRefValue(value); + if (value) + { + methodDescData->managedDynamicMethodObject = PTR_HOST_TO_TADDR(value); + } + } + } + } + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetMethodDescTransparencyData(CLRDATA_ADDRESS methodDesc, struct DacpMethodDescTransparencyData *data) +{ + if (methodDesc == 0 || data == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + MethodDesc *pMD = PTR_MethodDesc(TO_TADDR(methodDesc)); + if (!DacValidateMD(pMD)) + { + hr = E_INVALIDARG; + } + else + { + ZeroMemory(data, sizeof(DacpMethodDescTransparencyData)); + + if (pMD->HasCriticalTransparentInfo()) + { + data->bHasCriticalTransparentInfo = pMD->HasCriticalTransparentInfo(); + data->bIsCritical = pMD->IsCritical(); + data->bIsTreatAsSafe = pMD->IsTreatAsSafe(); + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetCodeHeaderData(CLRDATA_ADDRESS ip, struct DacpCodeHeaderData *codeHeaderData) +{ + if (ip == 0 || codeHeaderData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + EECodeInfo codeInfo(TO_TADDR(ip)); + + if (!codeInfo.IsValid()) + { + // We may be able to walk stubs to find a method desc if it's not a jitted method. + MethodDesc *methodDescI = MethodTable::GetMethodDescForSlotAddress(TO_TADDR(ip)); + if (methodDescI == NULL) + { + hr = E_INVALIDARG; + } + else + { + codeHeaderData->MethodDescPtr = HOST_CDADDR(methodDescI); + codeHeaderData->JITType = TYPE_UNKNOWN; + codeHeaderData->GCInfo = NULL; + codeHeaderData->MethodStart = NULL; + codeHeaderData->MethodSize = 0; + codeHeaderData->ColdRegionStart = NULL; + } + } + else + { + codeHeaderData->MethodDescPtr = HOST_CDADDR(codeInfo.GetMethodDesc()); + + GetJITMethodInfo(&codeInfo, &codeHeaderData->JITType, &codeHeaderData->GCInfo); + + codeHeaderData->MethodStart = + (CLRDATA_ADDRESS) codeInfo.GetStartAddress(); + size_t methodSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfoToken()); + _ASSERTE(FitsIn<DWORD>(methodSize)); + codeHeaderData->MethodSize = static_cast<DWORD>(methodSize); + + IJitManager::MethodRegionInfo methodRegionInfo = {NULL, 0, NULL, 0}; + codeInfo.GetMethodRegionInfo(&methodRegionInfo); + + codeHeaderData->HotRegionSize = (DWORD) methodRegionInfo.hotSize; + codeHeaderData->ColdRegionSize = (DWORD) methodRegionInfo.coldSize; + codeHeaderData->ColdRegionStart = (CLRDATA_ADDRESS) methodRegionInfo.coldStartAddress; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetMethodDescPtrFromFrame(CLRDATA_ADDRESS frameAddr, CLRDATA_ADDRESS * ppMD) +{ + if (frameAddr == 0 || ppMD == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + Frame *pFrame = PTR_Frame(TO_TADDR(frameAddr)); + CLRDATA_ADDRESS methodDescAddr = HOST_CDADDR(pFrame->GetFunction()); + if ((methodDescAddr == NULL) || !DacValidateMD(PTR_MethodDesc(TO_TADDR(methodDescAddr)))) + { + hr = E_INVALIDARG; + } + else + { + *ppMD = methodDescAddr; + hr = S_OK; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetMethodDescPtrFromIP(CLRDATA_ADDRESS ip, CLRDATA_ADDRESS * ppMD) +{ + if (ip == 0 || ppMD == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + EECodeInfo codeInfo(TO_TADDR(ip)); + + if (!codeInfo.IsValid()) + { + hr = E_FAIL; + } + else + { + CLRDATA_ADDRESS pMD = HOST_CDADDR(codeInfo.GetMethodDesc()); + if ((pMD == NULL) || !DacValidateMD(PTR_MethodDesc(TO_TADDR(pMD)))) + { + hr = E_INVALIDARG; + } + else + { + *ppMD = pMD; + hr = S_OK; + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetMethodDescName(CLRDATA_ADDRESS methodDesc, unsigned int count, __out_z __inout_ecount(count) wchar_t *name, unsigned int *pNeeded) +{ + if (methodDesc == 0) + return E_INVALIDARG; + + SOSDacEnter(); + + MethodDesc* pMD = PTR_MethodDesc(TO_TADDR(methodDesc)); + StackSString str; + + EX_TRY + { + TypeString::AppendMethodInternal(str, pMD, TypeString::FormatSignature|TypeString::FormatNamespace|TypeString::FormatFullInst); + } + EX_CATCH + { + hr = E_FAIL; + if (pMD->IsDynamicMethod()) + { + if (pMD->IsLCGMethod() || pMD->IsILStub()) + { + // In heap dumps, trying to format the signature can fail + // in certain cases because StoredSigMethodDesc::m_pSig points + // to the IMAGE_MAPPED layout (in the PEImage::m_pLayouts array). + // We save only the IMAGE_LOADED layout to the heap dump. Rather + // than bloat the dump, we just drop the signature in these + // cases. + + str.Clear(); + TypeString::AppendMethodInternal(str, pMD, TypeString::FormatNamespace|TypeString::FormatFullInst); + hr = S_OK; + } + } + else + { +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + if (MdCacheGetEEName(TO_TADDR(methodDesc), str)) + { + hr = S_OK; + } + else + { +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + str.Clear(); + Module* pModule = pMD->GetModule(); + if (pModule) + { + WCHAR path[MAX_LONGPATH]; + COUNT_T nChars = 0; + if (pModule->GetPath().DacGetUnicode(NumItems(path), path, &nChars) && + nChars > 0 && nChars <= NumItems(path)) + { + WCHAR* pFile = path + nChars - 1; + while ((pFile >= path) && (*pFile != W('\\'))) + { + pFile--; + } + pFile++; + if (*pFile) + { + str.Append(pFile); + str.Append(W("!Unknown")); + hr = S_OK; + } + } + } +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + } +#endif + } + } + EX_END_CATCH(SwallowAllExceptions) + + if (SUCCEEDED(hr)) + { + + const wchar_t *val = str.GetUnicode(); + + if (pNeeded) + *pNeeded = str.GetCount() + 1; + + if (name && count) + { + wcsncpy_s(name, count, val, _TRUNCATE); + name[count-1] = 0; + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetDomainFromContext(CLRDATA_ADDRESS contextAddr, CLRDATA_ADDRESS *domain) +{ + if (contextAddr == 0 || domain == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + Context* context = PTR_Context(TO_TADDR(contextAddr)); + *domain = HOST_CDADDR(context->GetDomain()); + + SOSDacLeave(); + return hr; +} + + +HRESULT +ClrDataAccess::GetObjectStringData(CLRDATA_ADDRESS obj, unsigned int count, __out_z __inout_ecount(count) wchar_t *stringData, unsigned int *pNeeded) +{ + if (obj == 0) + return E_INVALIDARG; + + if ((stringData == 0 || count <= 0) && (pNeeded == NULL)) + return E_INVALIDARG; + + SOSDacEnter(); + + TADDR mtTADDR = DACGetMethodTableFromObjectPointer(TO_TADDR(obj), m_pTarget); + MethodTable *mt = PTR_MethodTable(mtTADDR); + + // Object must be a string + BOOL bFree = FALSE; + if (!DacValidateMethodTable(mt, bFree)) + hr = E_INVALIDARG; + else if (HOST_CDADDR(mt) != HOST_CDADDR(g_pStringClass)) + hr = E_INVALIDARG; + + if (SUCCEEDED(hr)) + { + PTR_StringObject str(TO_TADDR(obj)); + ULONG32 needed = (ULONG32)str->GetStringLength() + 1; + + if (stringData && count > 0) + { + if (count > needed) + count = needed; + + TADDR pszStr = TO_TADDR(obj)+offsetof(StringObject, m_Characters); + hr = m_pTarget->ReadVirtual(pszStr, (PBYTE)stringData, count * sizeof(wchar_t), &needed); + + if (SUCCEEDED(hr)) + stringData[count - 1] = W('\0'); + else + stringData[0] = W('\0'); + } + else + { + hr = E_INVALIDARG; + } + + if (pNeeded) + *pNeeded = needed; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetObjectClassName(CLRDATA_ADDRESS obj, unsigned int count, __out_z __inout_ecount(count) wchar_t *className, unsigned int *pNeeded) +{ + if (obj == 0) + return E_INVALIDARG; + + SOSDacEnter(); + + // Don't turn the Object into a pointer, it is too costly on + // scans of the gc heap. + MethodTable *mt = NULL; + TADDR mtTADDR = DACGetMethodTableFromObjectPointer(CLRDATA_ADDRESS_TO_TADDR(obj), m_pTarget); + if (mtTADDR != NULL) + mt = PTR_MethodTable(mtTADDR); + else + hr = E_INVALIDARG; + + BOOL bFree = FALSE; + if (SUCCEEDED(hr) && !DacValidateMethodTable(mt, bFree)) + hr = E_INVALIDARG; + + if (SUCCEEDED(hr)) + { + // There is a case where metadata was unloaded and the AppendType call will fail. + // This is when an AppDomain has been unloaded but not yet collected. + PEFile *pPEFile = mt->GetModule()->GetFile(); + if (pPEFile->GetNativeImage() == NULL && pPEFile->GetILimage() == NULL) + { + if (pNeeded) + *pNeeded = 16; + + if (className) + wcsncpy_s(className, count, W("<Unloaded Type>"), _TRUNCATE); + } + else + { + StackSString s; + TypeString::AppendType(s, TypeHandle(mt), TypeString::FormatNamespace|TypeString::FormatFullInst); + const wchar_t *val = s.GetUnicode(); + + if (pNeeded) + *pNeeded = s.GetCount() + 1; + + if (className && count) + { + wcsncpy_s(className, count, val, _TRUNCATE); + className[count-1] = 0; + } + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetMethodDescFromToken(CLRDATA_ADDRESS moduleAddr, mdToken token, CLRDATA_ADDRESS *methodDesc) +{ + if (moduleAddr == 0 || methodDesc == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + Module* pModule = PTR_Module(TO_TADDR(moduleAddr)); + TypeHandle th; + switch (TypeFromToken(token)) + { + case mdtFieldDef: + *methodDesc = HOST_CDADDR(pModule->LookupFieldDef(token)); + break; + case mdtMethodDef: + *methodDesc = HOST_CDADDR(pModule->LookupMethodDef(token)); + break; + case mdtTypeDef: + th = pModule->LookupTypeDef(token); + *methodDesc = th.AsTAddr(); + break; + case mdtTypeRef: + th = pModule->LookupTypeRef(token); + *methodDesc = th.AsTAddr(); + break; + default: + hr = E_INVALIDARG; + break; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::TraverseModuleMap(ModuleMapType mmt, CLRDATA_ADDRESS moduleAddr, MODULEMAPTRAVERSE pCallback, LPVOID token) +{ + if (moduleAddr == 0) + return E_INVALIDARG; + + SOSDacEnter(); + + Module* pModule = PTR_Module(TO_TADDR(moduleAddr)); + + // We want to traverse these two tables, passing callback information + switch (mmt) + { + case TYPEDEFTOMETHODTABLE: + { + LookupMap<PTR_MethodTable>::Iterator typeIter(&pModule->m_TypeDefToMethodTableMap); + for (int i = 0; typeIter.Next(); i++) + { + if (typeIter.GetElement()) + { + MethodTable* pMT = typeIter.GetElement(); + (pCallback)(i,PTR_HOST_TO_TADDR(pMT), token); + } + } + } + break; + case TYPEREFTOMETHODTABLE: + { + LookupMap<PTR_TypeRef>::Iterator typeIter(&pModule->m_TypeRefToMethodTableMap); + for (int i = 0; typeIter.Next(); i++) + { + if (typeIter.GetElement()) + { + MethodTable* pMT = TypeHandle::FromTAddr(dac_cast<TADDR>(typeIter.GetElement())).GetMethodTable(); + (pCallback)(i,PTR_HOST_TO_TADDR(pMT), token); + } + } + } + break; + default: + hr = E_INVALIDARG; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetModule(CLRDATA_ADDRESS addr, IXCLRDataModule **mod) +{ + if (addr == 0 || mod == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + Module* pModule = PTR_Module(TO_TADDR(addr)); + *mod = new ClrDataModule(this, pModule); + SOSDacLeave(); + + return hr; +} + +HRESULT +ClrDataAccess::GetModuleData(CLRDATA_ADDRESS addr, struct DacpModuleData *ModuleData) +{ + if (addr == 0 || ModuleData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + Module* pModule = PTR_Module(TO_TADDR(addr)); + + ZeroMemory(ModuleData,sizeof(DacpModuleData)); + ModuleData->Address = addr; + ModuleData->File = HOST_CDADDR(pModule->GetFile()); + COUNT_T metadataSize = 0; + if (pModule->GetFile()->HasNativeImage()) + { + ModuleData->ilBase = (CLRDATA_ADDRESS)PTR_TO_TADDR(pModule->GetFile()->GetLoadedNative()->GetBase()); + } + else + if (!pModule->GetFile()->IsDynamic()) + { + ModuleData->ilBase = (CLRDATA_ADDRESS)(ULONG_PTR) pModule->GetFile()->GetIJWBase(); + } + + ModuleData->metadataStart = (CLRDATA_ADDRESS)dac_cast<TADDR>(pModule->GetFile()->GetLoadedMetadata(&metadataSize)); + ModuleData->metadataSize = (SIZE_T) metadataSize; + + ModuleData->bIsReflection = pModule->IsReflection(); + ModuleData->bIsPEFile = pModule->IsPEFile(); + ModuleData->Assembly = HOST_CDADDR(pModule->GetAssembly()); + ModuleData->dwModuleID = pModule->GetModuleID(); + ModuleData->dwModuleIndex = pModule->GetModuleIndex().m_dwIndex; + ModuleData->dwTransientFlags = pModule->m_dwTransientFlags; + + EX_TRY + { + // + // In minidump's case, these data structure is not avaiable. + // + ModuleData->TypeDefToMethodTableMap = PTR_CDADDR(pModule->m_TypeDefToMethodTableMap.pTable); + ModuleData->TypeRefToMethodTableMap = PTR_CDADDR(pModule->m_TypeRefToMethodTableMap.pTable); + ModuleData->MethodDefToDescMap = PTR_CDADDR(pModule->m_MethodDefToDescMap.pTable); + ModuleData->FieldDefToDescMap = PTR_CDADDR(pModule->m_FieldDefToDescMap.pTable); + ModuleData->MemberRefToDescMap = NULL; + ModuleData->FileReferencesMap = PTR_CDADDR(pModule->m_FileReferencesMap.pTable); + ModuleData->ManifestModuleReferencesMap = PTR_CDADDR(pModule->m_ManifestModuleReferencesMap.pTable); + +#ifdef FEATURE_MIXEDMODE // IJW + ModuleData->pThunkHeap = HOST_CDADDR(pModule->m_pThunkHeap); +#endif // FEATURE_MIXEDMODE // IJW + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions) + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetILForModule(CLRDATA_ADDRESS moduleAddr, DWORD rva, CLRDATA_ADDRESS *il) +{ + if (moduleAddr == 0 || il == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + Module* pModule = PTR_Module(TO_TADDR(moduleAddr)); + *il = (TADDR)(CLRDATA_ADDRESS)pModule->GetIL(rva); + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetMethodTableData(CLRDATA_ADDRESS mt, struct DacpMethodTableData *MTData) +{ + if (mt == 0 || MTData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + MethodTable* pMT = PTR_MethodTable(TO_TADDR(mt)); + BOOL bIsFree = FALSE; + if (!DacValidateMethodTable(pMT, bIsFree)) + { + hr = E_INVALIDARG; + } + else + { + ZeroMemory(MTData,sizeof(DacpMethodTableData)); + MTData->BaseSize = pMT->GetBaseSize(); + if(pMT->IsString()) + MTData->BaseSize -= sizeof(WCHAR); + MTData->ComponentSize = (DWORD)pMT->GetComponentSize(); + MTData->bIsFree = bIsFree; + if(!bIsFree) + { + MTData->Module = HOST_CDADDR(pMT->GetModule()); + MTData->Class = HOST_CDADDR(pMT->GetClass()); + MTData->ParentMethodTable = HOST_CDADDR(pMT->GetParentMethodTable());; + MTData->wNumInterfaces = pMT->GetNumInterfaces(); + MTData->wNumMethods = pMT->GetNumMethods(); + MTData->wNumVtableSlots = pMT->GetNumVtableSlots(); + MTData->wNumVirtuals = pMT->GetNumVirtuals(); + MTData->cl = pMT->GetCl(); + MTData->dwAttrClass = pMT->GetAttrClass(); + MTData->bContainsPointers = pMT->ContainsPointers(); + MTData->bIsShared = (pMT->IsDomainNeutral() ? TRUE : FALSE); // flags & enum_flag_DomainNeutral + MTData->bIsDynamic = pMT->IsDynamicStatics(); + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetMethodTableName(CLRDATA_ADDRESS mt, unsigned int count, __out_z __inout_ecount(count) wchar_t *mtName, unsigned int *pNeeded) +{ + if (mt == 0) + return E_INVALIDARG; + + SOSDacEnter(); + + MethodTable *pMT = PTR_MethodTable(TO_TADDR(mt)); + BOOL free = FALSE; + + if (mt == HOST_CDADDR(g_pFreeObjectMethodTable)) + { + if (pNeeded) + *pNeeded = 5; + + if (mtName && count) + wcsncpy_s(mtName, count, W("Free"), _TRUNCATE); + } + else if (!DacValidateMethodTable(pMT, free)) + { + hr = E_INVALIDARG; + } + else + { + // There is a case where metadata was unloaded and the AppendType call will fail. + // This is when an AppDomain has been unloaded but not yet collected. + PEFile *pPEFile = pMT->GetModule()->GetFile(); + if (pPEFile->GetNativeImage() == NULL && pPEFile->GetILimage() == NULL) + { + if (pNeeded) + *pNeeded = 16; + + if (mtName) + wcsncpy_s(mtName, count, W("<Unloaded Type>"), _TRUNCATE); + } + else + { + StackSString s; +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + EX_TRY + { +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + TypeString::AppendType(s, TypeHandle(pMT), TypeString::FormatNamespace|TypeString::FormatFullInst); + +#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + } + EX_CATCH + { + if (!MdCacheGetEEName(dac_cast<TADDR>(pMT), s)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) +#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS + + if (s.IsEmpty()) + { + hr = E_OUTOFMEMORY; + } + else + { + const wchar_t *val = s.GetUnicode(); + + if (pNeeded) + *pNeeded = s.GetCount() + 1; + + if (mtName && count) + { + wcsncpy_s(mtName, count, val, _TRUNCATE); + mtName[count-1] = 0; + } + } + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetFieldDescData(CLRDATA_ADDRESS addr, struct DacpFieldDescData *FieldDescData) +{ + if (addr == 0 || FieldDescData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + FieldDesc* pFieldDesc = PTR_FieldDesc(TO_TADDR(addr)); + FieldDescData->Type = pFieldDesc->GetFieldType(); + FieldDescData->sigType = FieldDescData->Type; + + EX_TRY + { + // minidump case, we do not have the field's type's type handle! + // Strike should be able to form name based on the metadata token in + // the field desc. Find type is using look up map which is huge. We cannot + // drag in this data structure in minidump's case. + // + TypeHandle th = pFieldDesc->LookupFieldTypeHandle(); + MethodTable *pMt = th.GetMethodTable(); + if (pMt) + { + FieldDescData->MTOfType = HOST_CDADDR(th.GetMethodTable()); + } + else + { + FieldDescData->MTOfType = NULL; + } + } + EX_CATCH + { + FieldDescData->MTOfType = NULL; + } + EX_END_CATCH(SwallowAllExceptions) + + // TODO: This is not currently useful, I need to get the module of the + // type definition not that of the field description. + + // TODO: Is there an easier way to get this information? + // I'm getting the typeDef of a (possibly unloaded) type. + MetaSig tSig(pFieldDesc); + tSig.NextArg(); + SigPointer sp1 = tSig.GetArgProps(); + CorElementType et; + hr = sp1.GetElemType(&et); // throw away the value, we just need to walk past. + + if (SUCCEEDED(hr)) + { + if (et == ELEMENT_TYPE_CLASS || et == ELEMENT_TYPE_VALUETYPE) // any other follows token? + { + hr = sp1.GetToken(&(FieldDescData->TokenOfType)); + } + else + { + // There is no encoded token of field type + FieldDescData->TokenOfType = mdTypeDefNil; + if (FieldDescData->MTOfType == NULL) + { + // If there is no encoded token (that is, it is primitive type) and no MethodTable for it, remember the + // element_type from signature + // + FieldDescData->sigType = et; + } + } + } + + FieldDescData->ModuleOfType = HOST_CDADDR(pFieldDesc->GetModule()); + FieldDescData->mb = pFieldDesc->GetMemberDef(); + FieldDescData->MTOfEnclosingClass = HOST_CDADDR(pFieldDesc->GetApproxEnclosingMethodTable()); + FieldDescData->dwOffset = pFieldDesc->GetOffset(); + FieldDescData->bIsThreadLocal = pFieldDesc->IsThreadStatic(); +#ifdef FEATURE_REMOTING + FieldDescData->bIsContextLocal = pFieldDesc->IsContextStatic();; +#else + FieldDescData->bIsContextLocal = FALSE; +#endif + FieldDescData->bIsStatic = pFieldDesc->IsStatic(); + FieldDescData->NextField = HOST_CDADDR(PTR_FieldDesc(PTR_HOST_TO_TADDR(pFieldDesc) + sizeof(FieldDesc))); + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetMethodTableFieldData(CLRDATA_ADDRESS mt, struct DacpMethodTableFieldData *data) +{ + if (mt == 0 || data == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + MethodTable* pMT = PTR_MethodTable(TO_TADDR(mt)); + BOOL bIsFree = FALSE; + if (!pMT || !DacValidateMethodTable(pMT, bIsFree)) + { + hr = E_INVALIDARG; + } + else + { + data->wNumInstanceFields = pMT->GetNumInstanceFields(); + data->wNumStaticFields = pMT->GetNumStaticFields(); + data->wNumThreadStaticFields = pMT->GetNumThreadStaticFields(); + + data->FirstField = PTR_TO_TADDR(pMT->GetClass()->GetFieldDescList()); + +#ifdef FEATURE_REMOTING + BOOL hasContextStatics = pMT->HasContextStatics(); + + data->wContextStaticsSize = (hasContextStatics) ? pMT->GetContextStaticsSize() : 0; + _ASSERTE(!hasContextStatics || FitsIn<WORD>(pMT->GetContextStaticsOffset())); + data->wContextStaticOffset = (hasContextStatics) ? static_cast<WORD>(pMT->GetContextStaticsOffset()) : 0; +#else + data->wContextStaticsSize = 0; + data->wContextStaticOffset = 0; +#endif + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetMethodTableTransparencyData(CLRDATA_ADDRESS mt, struct DacpMethodTableTransparencyData *pTransparencyData) +{ + if (mt == 0 || pTransparencyData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + MethodTable *pMT = PTR_MethodTable(TO_TADDR(mt)); + BOOL bIsFree = FALSE; + if (!DacValidateMethodTable(pMT, bIsFree)) + { + hr = E_INVALIDARG; + } + else + { + ZeroMemory(pTransparencyData, sizeof(DacpMethodTableTransparencyData)); + + EEClass * pClass = pMT->GetClass(); + if (pClass->HasCriticalTransparentInfo()) + { + pTransparencyData->bHasCriticalTransparentInfo = pClass->HasCriticalTransparentInfo(); + pTransparencyData->bIsCritical = pClass->IsCritical() || pClass->IsAllCritical(); + pTransparencyData->bIsTreatAsSafe = pClass->IsTreatAsSafe(); + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetMethodTableForEEClass(CLRDATA_ADDRESS eeClass, CLRDATA_ADDRESS *value) +{ + if (eeClass == 0 || value == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + EEClass * pClass = PTR_EEClass(TO_TADDR(eeClass)); + if (!DacValidateEEClass(pClass)) + { + hr = E_INVALIDARG; + } + else + { + *value = HOST_CDADDR(pClass->GetMethodTable()); + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetFrameName(CLRDATA_ADDRESS vtable, unsigned int count, __out_z __inout_ecount(count) wchar_t *frameName, unsigned int *pNeeded) +{ + if (vtable == 0) + return E_INVALIDARG; + + SOSDacEnter(); + + PWSTR pszName = DacGetVtNameW(CLRDATA_ADDRESS_TO_TADDR(vtable)); + if (pszName == NULL) + { + hr = E_INVALIDARG; + } + else + { + // Turn from bytes to wide characters + unsigned int len = (unsigned int)wcslen(pszName); + + if (frameName) + { + wcsncpy_s(frameName, count, pszName, _TRUNCATE); + + if (pNeeded) + { + if (count < len) + *pNeeded = count - 1; + else + *pNeeded = len; + } + } + else if (pNeeded) + { + *pNeeded = len + 1; + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetPEFileName(CLRDATA_ADDRESS addr, unsigned int count, __out_z __inout_ecount(count) wchar_t *fileName, unsigned int *pNeeded) +{ + if (addr == 0 || (fileName == NULL && pNeeded == NULL) || (fileName != NULL && count == 0)) + return E_INVALIDARG; + + SOSDacEnter(); + PEFile* pPEFile = PTR_PEFile(TO_TADDR(addr)); + + // Turn from bytes to wide characters + if (!pPEFile->GetPath().IsEmpty()) + { + if (!pPEFile->GetPath().DacGetUnicode(count, fileName, pNeeded)) + hr = E_FAIL; + } + else if (!pPEFile->IsDynamic()) + { + PEAssembly *pAssembly = pPEFile->GetAssembly(); + StackSString displayName; + pAssembly->GetDisplayName(displayName, 0); + + if (displayName.IsEmpty()) + { + if (fileName) + fileName[0] = 0; + + if (pNeeded) + *pNeeded = 1; + } + else + { + unsigned int len = displayName.GetCount()+1; + + if (fileName) + { + wcsncpy_s(fileName, count, displayName.GetUnicode(), _TRUNCATE); + + if (count < len) + len = count; + } + + if (pNeeded) + *pNeeded = len; + } + } + else + { + if (fileName && count) + fileName[0] = 0; + + if (pNeeded) + *pNeeded = 1; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetPEFileBase(CLRDATA_ADDRESS addr, CLRDATA_ADDRESS *base) +{ + if (addr == 0 || base == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + PEFile* pPEFile = PTR_PEFile(TO_TADDR(addr)); + + // More fields later? + if (pPEFile->HasNativeImage()) + *base = TO_CDADDR(PTR_TO_TADDR(pPEFile->GetLoadedNative()->GetBase())); + else if (!pPEFile->IsDynamic()) + *base = TO_CDADDR(pPEFile->GetIJWBase()); + else + *base = NULL; + + SOSDacLeave(); + return hr; +} + +DWORD DACGetNumComponents(TADDR addr, ICorDebugDataTarget* target) +{ + // For an object pointer, this attempts to read the number of + // array components. + addr+=sizeof(size_t); + ULONG32 returned = 0; + DWORD Value = NULL; + HRESULT hr = target->ReadVirtual(addr, (PBYTE)&Value, sizeof(DWORD), &returned); + + if ((hr != S_OK) || (returned != sizeof(DWORD))) + { + return 0; + } + return Value; +} + +HRESULT +ClrDataAccess::GetObjectData(CLRDATA_ADDRESS addr, struct DacpObjectData *objectData) +{ + if (addr == 0 || objectData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + ZeroMemory (objectData, sizeof(DacpObjectData)); + TADDR mtTADDR = DACGetMethodTableFromObjectPointer(CLRDATA_ADDRESS_TO_TADDR(addr),m_pTarget); + if (mtTADDR==NULL) + hr = E_INVALIDARG; + + BOOL bFree = FALSE; + MethodTable *mt = NULL; + if (SUCCEEDED(hr)) + { + mt = PTR_MethodTable(mtTADDR); + if (!DacValidateMethodTable(mt, bFree)) + hr = E_INVALIDARG; + } + + if (SUCCEEDED(hr)) + { + objectData->MethodTable = HOST_CDADDR(mt); + objectData->Size = mt->GetBaseSize(); + if (mt->GetComponentSize()) + { + objectData->Size += (DACGetNumComponents(CLRDATA_ADDRESS_TO_TADDR(addr),m_pTarget) * mt->GetComponentSize()); + objectData->dwComponentSize = mt->GetComponentSize(); + } + + if (bFree) + { + objectData->ObjectType = OBJ_FREE; + } + else + { + if (objectData->MethodTable == HOST_CDADDR(g_pStringClass)) + { + objectData->ObjectType = OBJ_STRING; + } + else if (objectData->MethodTable == HOST_CDADDR(g_pObjectClass)) + { + objectData->ObjectType = OBJ_OBJECT; + } + else if (mt->IsArray()) + { + objectData->ObjectType = OBJ_ARRAY; + + // For now, go ahead and instantiate array classes. + // TODO: avoid instantiating even object Arrays in the host. + // NOTE: This code is carefully written to deal with MethodTable fields + // in the array object having the mark bit set (because we may + // be in mark phase when this function is called). + ArrayBase *pArrayObj = PTR_ArrayBase(TO_TADDR(addr)); + objectData->ElementType = mt->GetArrayElementType(); + + TypeHandle thElem = mt->GetApproxArrayElementTypeHandle(); + + TypeHandle thCur = thElem; + while (thCur.IsTypeDesc()) + thCur = thCur.AsArray()->GetArrayElementTypeHandle(); + + TADDR mtCurTADDR = thCur.AsTAddr(); + if (!DacValidateMethodTable(PTR_MethodTable(mtCurTADDR), bFree)) + { + hr = E_INVALIDARG; + } + else + { + objectData->ElementTypeHandle = (CLRDATA_ADDRESS)(thElem.AsTAddr()); + objectData->dwRank = mt->GetRank(); + objectData->dwNumComponents = pArrayObj->GetNumComponents (); + objectData->ArrayDataPtr = PTR_CDADDR(pArrayObj->GetDataPtr (TRUE)); + objectData->ArrayBoundsPtr = HOST_CDADDR(pArrayObj->GetBoundsPtr()); + objectData->ArrayLowerBoundsPtr = HOST_CDADDR(pArrayObj->GetLowerBoundsPtr()); + } + } + else + { + objectData->ObjectType = OBJ_OTHER; + } + } + } + +#ifdef FEATURE_COMINTEROP + if (SUCCEEDED(hr)) + { + EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY + { + PTR_SyncBlock pSyncBlk = DACGetSyncBlockFromObjectPointer(CLRDATA_ADDRESS_TO_TADDR(addr), m_pTarget); + if (pSyncBlk != NULL) + { + // see if we have an RCW and/or CCW associated with this object + PTR_InteropSyncBlockInfo pInfo = pSyncBlk->GetInteropInfoNoCreate(); + if (pInfo != NULL) + { + objectData->RCW = TO_CDADDR(pInfo->DacGetRawRCW()); + objectData->CCW = HOST_CDADDR(pInfo->GetCCW()); + } + } + } + EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY; + } +#endif // FEATURE_COMINTEROP + + SOSDacLeave(); + + return hr; +} + +HRESULT ClrDataAccess::GetAppDomainList(unsigned int count, CLRDATA_ADDRESS values[], unsigned int *fetched) +{ + SOSDacEnter(); + + AppDomainIterator ai(FALSE); + unsigned int i = 0; + while (ai.Next() && (i < count)) + { + if (values) + values[i] = HOST_CDADDR(ai.GetDomain()); + i++; + } + + if (fetched) + *fetched = i; + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetAppDomainStoreData(struct DacpAppDomainStoreData *adsData) +{ + SOSDacEnter(); + + adsData->systemDomain = HOST_CDADDR(SystemDomain::System()); + adsData->sharedDomain = HOST_CDADDR(SharedDomain::GetDomain()); + + // Get an accurate count of appdomains. + adsData->DomainCount = 0; + AppDomainIterator ai(FALSE); + while (ai.Next()) + adsData->DomainCount++; + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetAppDomainData(CLRDATA_ADDRESS addr, struct DacpAppDomainData *appdomainData) +{ + SOSDacEnter(); + + if (addr == 0) + { + hr = E_INVALIDARG; + } + else + { + PTR_BaseDomain pBaseDomain = PTR_BaseDomain(TO_TADDR(addr)); + + ZeroMemory(appdomainData, sizeof(DacpAppDomainData)); + appdomainData->AppDomainPtr = PTR_CDADDR(pBaseDomain); + PTR_LoaderAllocator pLoaderAllocator = pBaseDomain->GetLoaderAllocator(); + appdomainData->pHighFrequencyHeap = HOST_CDADDR(pLoaderAllocator->GetHighFrequencyHeap()); + appdomainData->pLowFrequencyHeap = HOST_CDADDR(pLoaderAllocator->GetLowFrequencyHeap()); + appdomainData->pStubHeap = HOST_CDADDR(pLoaderAllocator->GetStubHeap()); + appdomainData->appDomainStage = STAGE_OPEN; + + if (pBaseDomain->IsSharedDomain()) + { + #ifdef FEATURE_LOADER_OPTIMIZATION + SharedDomain::SharedAssemblyIterator i; + while (i.Next()) + { + appdomainData->AssemblyCount++; + } + #endif // FEATURE_LOADER_OPTIMIZATION + } + else if (pBaseDomain->IsAppDomain()) + { + AppDomain * pAppDomain = pBaseDomain->AsAppDomain(); + appdomainData->DomainLocalBlock = appdomainData->AppDomainPtr + + offsetof(AppDomain, m_sDomainLocalBlock); + appdomainData->pDomainLocalModules = PTR_CDADDR(pAppDomain->m_sDomainLocalBlock.m_pModuleSlots); + + appdomainData->dwId = pAppDomain->GetId().m_dwId; + appdomainData->appDomainStage = (DacpAppDomainDataStage)pAppDomain->m_Stage.Load(); + if (pAppDomain->IsActive()) + { + // The assembly list is not valid in a closed appdomain. + AppDomain::AssemblyIterator i = pAppDomain->IterateAssembliesEx((AssemblyIterationFlags)( + kIncludeLoading | kIncludeLoaded | kIncludeExecution)); + CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; + + while (i.Next(pDomainAssembly.This())) + { + if (pDomainAssembly->IsLoaded()) + { + appdomainData->AssemblyCount++; + } + } + + AppDomain::FailedAssemblyIterator j = pAppDomain->IterateFailedAssembliesEx(); + while (j.Next()) + { + appdomainData->FailedAssemblyCount++; + } + } +#ifndef FEATURE_PAL + // MiniDumpNormal doesn't guarantee to dump the SecurityDescriptor, let it fail. + EX_TRY + { + appdomainData->AppSecDesc = HOST_CDADDR(pAppDomain->GetSecurityDescriptor()); + } + EX_CATCH + { + HRESULT hrExc = GET_EXCEPTION()->GetHR(); + if (hrExc != HRESULT_FROM_WIN32(ERROR_READ_FAULT) + && hrExc != CORDBG_E_READVIRTUAL_FAILURE) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) +#endif // FEATURE_PAL + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetFailedAssemblyData(CLRDATA_ADDRESS assembly, unsigned int *pContext, HRESULT *pResult) +{ + if (assembly == NULL || (pContext == NULL && pResult == NULL)) + { + return E_INVALIDARG; + } + + SOSDacEnter(); + + FailedAssembly* pAssembly = PTR_FailedAssembly(TO_TADDR(assembly)); + if (!pAssembly) + { + hr = E_INVALIDARG; + } + else + { +#ifdef FEATURE_FUSION + if (pContext) + *pContext = pAssembly->context; +#endif + if (pResult) + *pResult = pAssembly->error; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetFailedAssemblyLocation(CLRDATA_ADDRESS assembly, unsigned int count, + __out_z __inout_ecount(count) wchar_t *location, unsigned int *pNeeded) +{ + if (assembly == NULL || (location == NULL && pNeeded == NULL) || (location != NULL && count == 0)) + return E_INVALIDARG; + + SOSDacEnter(); + FailedAssembly* pAssembly = PTR_FailedAssembly(TO_TADDR(assembly)); + + // Turn from bytes to wide characters + if (!pAssembly->location.IsEmpty()) + { + if (!pAssembly->location.DacGetUnicode(count, location, pNeeded)) + { + hr = E_FAIL; + } + } + else + { + if (pNeeded) + *pNeeded = 1; + + if (location) + location[0] = 0; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetFailedAssemblyDisplayName(CLRDATA_ADDRESS assembly, unsigned int count, __out_z __inout_ecount(count) wchar_t *name, unsigned int *pNeeded) +{ + if (assembly == NULL || (name == NULL && pNeeded == NULL) || (name != NULL && count == 0)) + return E_INVALIDARG; + + SOSDacEnter(); + FailedAssembly* pAssembly = PTR_FailedAssembly(TO_TADDR(assembly)); + + if (!pAssembly->displayName.IsEmpty()) + { + if (!pAssembly->displayName.DacGetUnicode(count, name, pNeeded)) + { + hr = E_FAIL; + } + } + else + { + if (pNeeded) + *pNeeded = 1; + + if (name) + name[0] = 0; + } + + SOSDacLeave(); + return hr; +} + + +HRESULT +ClrDataAccess::GetAssemblyList(CLRDATA_ADDRESS addr, int count, CLRDATA_ADDRESS values[], int *pNeeded) +{ + if (addr == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + BaseDomain* pBaseDomain = PTR_BaseDomain(TO_TADDR(addr)); + + int n=0; + if (pBaseDomain->IsSharedDomain()) + { +#ifdef FEATURE_LOADER_OPTIMIZATION + SharedDomain::SharedAssemblyIterator i; + if (values) + { + while (i.Next() && n < count) + values[n++] = HOST_CDADDR(i.GetAssembly()); + } + else + { + while (i.Next()) + n++; + } + + if (pNeeded) + *pNeeded = n; +#else + hr = E_UNEXPECTED; +#endif + } + else if (pBaseDomain->IsAppDomain()) + { + AppDomain::AssemblyIterator i = pBaseDomain->AsAppDomain()->IterateAssembliesEx( + (AssemblyIterationFlags)(kIncludeLoading | kIncludeLoaded | kIncludeExecution)); + CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; + + if (values) + { + while (i.Next(pDomainAssembly.This()) && (n < count)) + { + if (pDomainAssembly->IsLoaded()) + { + CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetAssembly(); + // Note: DAC doesn't need to keep the assembly alive - see code:CollectibleAssemblyHolder#CAH_DAC + values[n++] = HOST_CDADDR(pAssembly.Extract()); + } + } + } + else + { + while (i.Next(pDomainAssembly.This())) + if (pDomainAssembly->IsLoaded()) + n++; + } + + if (pNeeded) + *pNeeded = n; + } + else + { + // The only other type of BaseDomain is the SystemDomain, and we shouldn't be asking + // for the assemblies in it. + _ASSERTE(false); + hr = E_INVALIDARG; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetFailedAssemblyList(CLRDATA_ADDRESS appDomain, int count, + CLRDATA_ADDRESS values[], unsigned int *pNeeded) +{ + if ((appDomain == NULL) || (values == NULL && pNeeded == NULL)) + { + return E_INVALIDARG; + } + + SOSDacEnter(); + AppDomain* pAppDomain = PTR_AppDomain(TO_TADDR(appDomain)); + + int n=0; + AppDomain::FailedAssemblyIterator i = pAppDomain->IterateFailedAssembliesEx(); + while (i.Next() && n<=count) + { + if (values) + values[n] = HOST_CDADDR(i.GetFailedAssembly()); + + n++; + } + + if (pNeeded) + *pNeeded = n; + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetAppDomainName(CLRDATA_ADDRESS addr, unsigned int count, __out_z __inout_ecount(count) wchar_t *name, unsigned int *pNeeded) +{ + SOSDacEnter(); + + PTR_BaseDomain pBaseDomain = PTR_BaseDomain(TO_TADDR(addr)); + if (!pBaseDomain->IsAppDomain()) + { + // Shared domain and SystemDomain don't have this field. + if (pNeeded) + *pNeeded = 1; + if (name) + name[0] = 0; + } + else + { + AppDomain* pAppDomain = pBaseDomain->AsAppDomain(); + + if (!pAppDomain->m_friendlyName.IsEmpty()) + { + if (!pAppDomain->m_friendlyName.DacGetUnicode(count, name, pNeeded)) + { + hr = E_FAIL; + } + } + else + { + if (pNeeded) + *pNeeded = 1; + if (name) + name[0] = 0; + + hr = S_OK; + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetApplicationBase(CLRDATA_ADDRESS appDomain, int count, + __out_z __inout_ecount(count) wchar_t *base, unsigned int *pNeeded) +{ + if (appDomain == NULL || (base == NULL && pNeeded == NULL) || (base != NULL && count == 0)) + { + return E_INVALIDARG; + } + + SOSDacEnter(); + AppDomain* pAppDomain = PTR_AppDomain(TO_TADDR(appDomain)); + + // Turn from bytes to wide characters + if ((PTR_BaseDomain(pAppDomain) == PTR_BaseDomain(SharedDomain::GetDomain())) || + (PTR_BaseDomain(pAppDomain) == PTR_BaseDomain(SystemDomain::System()))) + { + // Shared domain and SystemDomain don't have this field. + if (base) + base[0] = 0; + + if (pNeeded) + *pNeeded = 1; + } + + if (!pAppDomain->m_applicationBase.IsEmpty()) + { + if (!pAppDomain->m_applicationBase. + DacGetUnicode(count, base, pNeeded)) + { + hr = E_FAIL; + } + } + else + { + if (base) + base[0] = 0; + + if (pNeeded) + *pNeeded = 1; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetPrivateBinPaths(CLRDATA_ADDRESS appDomain, int count, + __out_z __inout_ecount(count) wchar_t *paths, unsigned int *pNeeded) +{ + if (appDomain == NULL || (paths == NULL && pNeeded == NULL) || (paths != NULL && count == 0)) + return E_INVALIDARG; + + SOSDacEnter(); + AppDomain* pAppDomain = PTR_AppDomain(TO_TADDR(appDomain)); + + // Turn from bytes to wide characters + if ((PTR_BaseDomain(pAppDomain) == PTR_BaseDomain(SharedDomain::GetDomain())) || + (PTR_BaseDomain(pAppDomain) == PTR_BaseDomain(SystemDomain::System()))) + { + // Shared domain and SystemDomain don't have this field. + if (pNeeded) + *pNeeded = 1; + + if (paths) + paths[0] = 0; + + hr = S_OK; + } + + if (!pAppDomain->m_privateBinPaths.IsEmpty()) + { + if (!pAppDomain->m_privateBinPaths.DacGetUnicode(count, paths, pNeeded)) + { + hr = E_FAIL; + } + } + else + { + if (paths) + paths[0] = 0; + + if (pNeeded) + *pNeeded = 1; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetAppDomainConfigFile(CLRDATA_ADDRESS appDomain, int count, + __out_z __inout_ecount(count) wchar_t *configFile, unsigned int *pNeeded) +{ + if (appDomain == NULL || (configFile == NULL && pNeeded == NULL) || (configFile != NULL && count == 0)) + { + return E_INVALIDARG; + } + + SOSDacEnter(); + AppDomain* pAppDomain = PTR_AppDomain(TO_TADDR(appDomain)); + + // Turn from bytes to wide characters + + if ((PTR_BaseDomain(pAppDomain) == PTR_BaseDomain(SharedDomain::GetDomain())) || + (PTR_BaseDomain(pAppDomain) == PTR_BaseDomain(SystemDomain::System()))) + { + // Shared domain and SystemDomain don't have this field. + if (configFile) + configFile[0] = 0; + + if (pNeeded) + *pNeeded = 1; + } + + if (!pAppDomain->m_configFile.IsEmpty()) + { + if (!pAppDomain->m_configFile.DacGetUnicode(count, configFile, pNeeded)) + { + hr = E_FAIL; + } + } + else + { + if (configFile) + configFile[0] = 0; + + if (pNeeded) + *pNeeded = 1; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetAssemblyData(CLRDATA_ADDRESS cdBaseDomainPtr, CLRDATA_ADDRESS assembly, struct DacpAssemblyData *assemblyData) +{ + if (assembly == NULL && cdBaseDomainPtr == NULL) + { + return E_INVALIDARG; + } + + SOSDacEnter(); + + Assembly* pAssembly = PTR_Assembly(TO_TADDR(assembly)); + + // Make sure conditionally-assigned fields like AssemblySecDesc, LoadContext, etc. are zeroed + ZeroMemory(assemblyData, sizeof(DacpAssemblyData)); + + // If the specified BaseDomain is an AppDomain, get a pointer to it + AppDomain * pDomain = NULL; + if (cdBaseDomainPtr != NULL) + { + assemblyData->BaseDomainPtr = cdBaseDomainPtr; + PTR_BaseDomain baseDomain = PTR_BaseDomain(TO_TADDR(cdBaseDomainPtr)); + if( baseDomain->IsAppDomain() ) + pDomain = baseDomain->AsAppDomain(); + } + + assemblyData->AssemblyPtr = HOST_CDADDR(pAssembly); + assemblyData->ClassLoader = HOST_CDADDR(pAssembly->GetLoader()); + assemblyData->ParentDomain = HOST_CDADDR(pAssembly->GetDomain()); + if (pDomain != NULL) + assemblyData->AssemblySecDesc = HOST_CDADDR(pAssembly->GetSecurityDescriptor(pDomain)); + assemblyData->isDynamic = pAssembly->IsDynamic(); + assemblyData->ModuleCount = 0; + assemblyData->isDomainNeutral = pAssembly->IsDomainNeutral(); + + if (pAssembly->GetManifestFile()) + { +#ifdef FEATURE_FUSION + assemblyData->LoadContext = pAssembly->GetManifestFile()->GetLoadContext(); + assemblyData->dwLocationFlags = pAssembly->GetManifestFile()->GetLocationFlags(); +#endif + + } + + ModuleIterator mi = pAssembly->IterateModules(); + while (mi.Next()) + { + assemblyData->ModuleCount++; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetAssemblyName(CLRDATA_ADDRESS assembly, unsigned int count, __out_z __inout_ecount(count) wchar_t *name, unsigned int *pNeeded) +{ + SOSDacEnter(); + Assembly* pAssembly = PTR_Assembly(TO_TADDR(assembly)); + + if (name) + name[0] = 0; + + if (!pAssembly->GetManifestFile()->GetPath().IsEmpty()) + { + if (!pAssembly->GetManifestFile()->GetPath().DacGetUnicode(count, name, pNeeded)) + hr = E_FAIL; + else if (name) + name[count-1] = 0; + } + else if (!pAssembly->GetManifestFile()->IsDynamic()) + { + StackSString displayName; + pAssembly->GetManifestFile()->GetDisplayName(displayName, 0); + + const wchar_t *val = displayName.GetUnicode(); + + if (pNeeded) + *pNeeded = displayName.GetCount() + 1; + + if (name && count) + { + wcsncpy_s(name, count, val, _TRUNCATE); + name[count-1] = 0; + } + } + else + { + hr = E_FAIL; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetAssemblyLocation(CLRDATA_ADDRESS assembly, int count, __out_z __inout_ecount(count) wchar_t *location, unsigned int *pNeeded) +{ + if ((assembly == NULL) || (location == NULL && pNeeded == NULL) || (location != NULL && count == 0)) + { + return E_INVALIDARG; + } + + SOSDacEnter(); + + Assembly* pAssembly = PTR_Assembly(TO_TADDR(assembly)); + + // Turn from bytes to wide characters + if (!pAssembly->GetManifestFile()->GetPath().IsEmpty()) + { + if (!pAssembly->GetManifestFile()->GetPath(). + DacGetUnicode(count, location, pNeeded)) + { + hr = E_FAIL; + } + } + else + { + if (location) + location[0] = 0; + + if (pNeeded) + *pNeeded = 1; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetAssemblyModuleList(CLRDATA_ADDRESS assembly, unsigned int count, CLRDATA_ADDRESS modules[], unsigned int *pNeeded) +{ + if (assembly == 0) + return E_INVALIDARG; + + SOSDacEnter(); + + Assembly* pAssembly = PTR_Assembly(TO_TADDR(assembly)); + ModuleIterator mi = pAssembly->IterateModules(); + unsigned int n = 0; + if (modules) + { + while (mi.Next() && n < count) + modules[n++] = HOST_CDADDR(mi.GetModule()); + } + else + { + while (mi.Next()) + n++; + } + + if (pNeeded) + *pNeeded = n; + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetGCHeapDetails(CLRDATA_ADDRESS heap, struct DacpGcHeapDetails *details) +{ + if (heap == 0 || details == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + // doesn't make sense to call this on WKS mode + if (!GCHeap::IsServerHeap()) + hr = E_INVALIDARG; + else +#ifdef FEATURE_SVR_GC + hr = ServerGCHeapDetails(heap, details); +#else + hr = E_NOTIMPL; +#endif + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetGCHeapStaticData(struct DacpGcHeapDetails *detailsData) +{ + if (detailsData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + detailsData->lowest_address = PTR_CDADDR(g_lowest_address); + detailsData->highest_address = PTR_CDADDR(g_highest_address); + detailsData->card_table = PTR_CDADDR(g_card_table); + + detailsData->heapAddr = NULL; + + detailsData->alloc_allocated = PTR_CDADDR(WKS::gc_heap::alloc_allocated); + detailsData->ephemeral_heap_segment = PTR_CDADDR(WKS::gc_heap::ephemeral_heap_segment); + +#ifdef BACKGROUND_GC + detailsData->mark_array = PTR_CDADDR(WKS::gc_heap::mark_array); + detailsData->current_c_gc_state = (CLRDATA_ADDRESS)(ULONG_PTR)WKS::gc_heap::current_c_gc_state; + detailsData->next_sweep_obj = PTR_CDADDR(WKS::gc_heap::next_sweep_obj); + detailsData->saved_sweep_ephemeral_seg = PTR_CDADDR(WKS::gc_heap::saved_sweep_ephemeral_seg); + detailsData->saved_sweep_ephemeral_start = PTR_CDADDR(WKS::gc_heap::saved_sweep_ephemeral_start); + detailsData->background_saved_lowest_address = PTR_CDADDR(WKS::gc_heap::background_saved_lowest_address); + detailsData->background_saved_highest_address = PTR_CDADDR(WKS::gc_heap::background_saved_highest_address); +#endif //BACKGROUND_GC + + for (int i=0;i<NUMBERGENERATIONS;i++) + { + detailsData->generation_table[i].start_segment = (CLRDATA_ADDRESS)dac_cast<TADDR>(WKS::generation_table[i].start_segment); + detailsData->generation_table[i].allocation_start = (CLRDATA_ADDRESS)(ULONG_PTR) WKS::generation_table[i].allocation_start; + detailsData->generation_table[i].allocContextPtr = (CLRDATA_ADDRESS)(ULONG_PTR) WKS::generation_table[i].allocation_context.alloc_ptr; + detailsData->generation_table[i].allocContextLimit = (CLRDATA_ADDRESS)(ULONG_PTR) WKS::generation_table[i].allocation_context.alloc_limit; + } + + TADDR pFillPointerArray = TO_TADDR(WKS::gc_heap::finalize_queue.GetAddr()) + offsetof(WKS::CFinalize,m_FillPointers); + for(int i=0;i<(NUMBERGENERATIONS+WKS::CFinalize::ExtraSegCount);i++) + { + ULONG32 returned = 0; + size_t pValue; + hr = m_pTarget->ReadVirtual(pFillPointerArray+(i*sizeof(size_t)), (PBYTE)&pValue, sizeof(size_t), &returned); + if (SUCCEEDED(hr)) + { + if (returned == sizeof(size_t)) + detailsData->finalization_fill_pointers[i] = (CLRDATA_ADDRESS) pValue; + else + hr = E_FAIL; + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetHeapSegmentData(CLRDATA_ADDRESS seg, struct DacpHeapSegmentData *heapSegment) +{ + if (seg == 0 || heapSegment == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + if (GCHeap::IsServerHeap()) + { +#if !defined(FEATURE_SVR_GC) + _ASSERTE(0); +#else // !defined(FEATURE_SVR_GC) + hr = GetServerHeapData(seg, heapSegment); +#endif //!defined(FEATURE_SVR_GC) + } + else + { + WKS::heap_segment *pSegment = __DPtr<WKS::heap_segment>(TO_TADDR(seg)); + if (!pSegment) + { + hr = E_INVALIDARG; + } + else + { + heapSegment->segmentAddr = seg; + heapSegment->allocated = (CLRDATA_ADDRESS)(ULONG_PTR) pSegment->allocated; + heapSegment->committed = (CLRDATA_ADDRESS)(ULONG_PTR) pSegment->committed; + heapSegment->reserved = (CLRDATA_ADDRESS)(ULONG_PTR) pSegment->reserved; + heapSegment->used = (CLRDATA_ADDRESS)(ULONG_PTR) pSegment->used; + heapSegment->mem = (CLRDATA_ADDRESS)(ULONG_PTR) pSegment->mem; + heapSegment->next = (CLRDATA_ADDRESS)dac_cast<TADDR>(pSegment->next); + heapSegment->flags = pSegment->flags; + heapSegment->gc_heap = NULL; + heapSegment->background_allocated = (CLRDATA_ADDRESS)(ULONG_PTR)pSegment->background_allocated; + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetGCHeapList(unsigned int count, CLRDATA_ADDRESS heaps[], unsigned int *pNeeded) +{ + SOSDacEnter(); + + // make sure we called this in appropriate circumstances (i.e., we have multiple heaps) + if (GCHeap::IsServerHeap()) + { +#if !defined(FEATURE_SVR_GC) + _ASSERTE(0); +#else // !defined(FEATURE_SVR_GC) + int heapCount = GCHeapCount(); + if (pNeeded) + *pNeeded = heapCount; + + if (heaps) + { + // get the heap locations + if (count == heapCount) + hr = GetServerHeaps(heaps, m_pTarget); + else + hr = E_INVALIDARG; + } +#endif // !defined(FEATURE_SVR_GC) + } + else + { + hr = E_FAIL; // doesn't make sense to call this on WKS mode + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetGCHeapData(struct DacpGcHeapData *gcheapData) +{ + if (gcheapData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + // Now get the heap type. The first data member of the GCHeap class is the GC_HEAP_TYPE, which has + // three possible values: + // GC_HEAP_INVALID = 0, + // GC_HEAP_WKS = 1, + // GC_HEAP_SVR = 2 + + TADDR gcHeapLocation = g_pGCHeap.GetAddrRaw (); // get the starting address of the global GCHeap instance + size_t gcHeapValue = 0; // this will hold the heap type + ULONG32 returned = 0; + + // @todo Microsoft: we should probably be capturing the HRESULT from ReadVirtual. We could + // provide a more informative error message. E_FAIL is a wretchedly vague thing to return. + hr = m_pTarget->ReadVirtual(gcHeapLocation, (PBYTE)&gcHeapValue, sizeof(gcHeapValue), &returned); + + //@todo Microsoft: We have an enumerated type, we probably should use the symbolic name + // we have GC_HEAP_INVALID if gcHeapValue == 0, so we're done + if (SUCCEEDED(hr) && ((returned != sizeof(gcHeapValue)) || (gcHeapValue == 0))) + hr = E_FAIL; + + if (SUCCEEDED(hr)) + { + // Now we can get other important information about the heap + gcheapData->g_max_generation = GCHeap::GetMaxGeneration(); + gcheapData->bServerMode = GCHeap::IsServerHeap(); + gcheapData->bGcStructuresValid = GCScan::GetGcRuntimeStructuresValid(); + if (GCHeap::IsServerHeap()) + { +#if !defined (FEATURE_SVR_GC) + _ASSERTE(0); + gcheapData->HeapCount = 1; +#else // !defined (FEATURE_SVR_GC) + gcheapData->HeapCount = GCHeapCount(); +#endif // !defined (FEATURE_SVR_GC) + } + else + { + gcheapData->HeapCount = 1; + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetOOMStaticData(struct DacpOomData *oomData) +{ + if (oomData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + memset(oomData, 0, sizeof(DacpOomData)); + + if (!GCHeap::IsServerHeap()) + { + oom_history* pOOMInfo = &(WKS::gc_heap::oom_info); + oomData->reason = pOOMInfo->reason; + oomData->alloc_size = pOOMInfo->alloc_size; + oomData->available_pagefile_mb = pOOMInfo->available_pagefile_mb; + oomData->gc_index = pOOMInfo->gc_index; + oomData->fgm = pOOMInfo->fgm; + oomData->size = pOOMInfo->size; + oomData->loh_p = pOOMInfo->loh_p; + } + else + { + hr = E_FAIL; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetOOMData(CLRDATA_ADDRESS oomAddr, struct DacpOomData *data) +{ + if (oomAddr == 0 || data == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + memset(data, 0, sizeof(DacpOomData)); + + if (!GCHeap::IsServerHeap()) + hr = E_FAIL; // doesn't make sense to call this on WKS mode + +#ifdef FEATURE_SVR_GC + else + hr = ServerOomData(oomAddr, data); +#else + _ASSERTE_MSG(false, "IsServerHeap returned true but FEATURE_SVR_GC not defined"); + hr = E_NOTIMPL; +#endif //FEATURE_SVR_GC + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetGCGlobalMechanisms(size_t* globalMechanisms) +{ +#ifdef GC_CONFIG_DRIVEN + if (globalMechanisms == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + memset(globalMechanisms, 0, (sizeof(size_t) * MAX_GLOBAL_GC_MECHANISMS_COUNT)); + + for (int i = 0; i < MAX_GLOBAL_GC_MECHANISMS_COUNT; i++) + { + globalMechanisms[i] = gc_global_mechanisms[i]; + } + + SOSDacLeave(); + return hr; +#else + return E_NOTIMPL; +#endif //GC_CONFIG_DRIVEN +} + +HRESULT +ClrDataAccess::GetGCInterestingInfoStaticData(struct DacpGCInterestingInfoData *data) +{ +#ifdef GC_CONFIG_DRIVEN + if (data == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + memset(data, 0, sizeof(DacpGCInterestingInfoData)); + + if (!GCHeap::IsServerHeap()) + { + for (int i = 0; i < NUM_GC_DATA_POINTS; i++) + data->interestingDataPoints[i] = WKS::interesting_data_per_heap[i]; + for (int i = 0; i < MAX_COMPACT_REASONS_COUNT; i++) + data->compactReasons[i] = WKS::compact_reasons_per_heap[i]; + for (int i = 0; i < MAX_EXPAND_MECHANISMS_COUNT; i++) + data->expandMechanisms[i] = WKS::expand_mechanisms_per_heap[i]; + for (int i = 0; i < MAX_GC_MECHANISM_BITS_COUNT; i++) + data->bitMechanisms[i] = WKS::interesting_mechanism_bits_per_heap[i]; + } + else + { + hr = E_FAIL; + } + + SOSDacLeave(); + return hr; +#else + return E_NOTIMPL; +#endif //GC_CONFIG_DRIVEN +} + +HRESULT +ClrDataAccess::GetGCInterestingInfoData(CLRDATA_ADDRESS interestingInfoAddr, struct DacpGCInterestingInfoData *data) +{ +#ifdef GC_CONFIG_DRIVEN + if (interestingInfoAddr == 0 || data == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + memset(data, 0, sizeof(DacpGCInterestingInfoData)); + + if (!GCHeap::IsServerHeap()) + hr = E_FAIL; // doesn't make sense to call this on WKS mode + +#ifdef FEATURE_SVR_GC + else + hr = ServerGCInterestingInfoData(interestingInfoAddr, data); +#else + _ASSERTE_MSG(false, "IsServerHeap returned true but FEATURE_SVR_GC not defined"); + hr = E_NOTIMPL; +#endif //FEATURE_SVR_GC + + SOSDacLeave(); + return hr; +#else + return E_NOTIMPL; +#endif //GC_CONFIG_DRIVEN +} + +HRESULT +ClrDataAccess::GetHeapAnalyzeData(CLRDATA_ADDRESS addr, struct DacpGcHeapAnalyzeData *data) +{ + if (addr == 0 || data == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + if (!GCHeap::IsServerHeap()) + hr = E_FAIL; // doesn't make sense to call this on WKS mode + +#ifdef FEATURE_SVR_GC + else + hr = ServerGCHeapAnalyzeData(addr, data); +#else + _ASSERTE_MSG(false, "IsServerHeap returned true but FEATURE_SVR_GC not defined"); + hr = E_NOTIMPL; +#endif //FEATURE_SVR_GC + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetHeapAnalyzeStaticData(struct DacpGcHeapAnalyzeData *analyzeData) +{ + if (analyzeData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + analyzeData->internal_root_array = PTR_CDADDR(WKS::gc_heap::internal_root_array); + analyzeData->internal_root_array_index = (size_t) WKS::gc_heap::internal_root_array_index; + analyzeData->heap_analyze_success = (BOOL) WKS::gc_heap::heap_analyze_success; + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetUsefulGlobals(struct DacpUsefulGlobalsData *globalsData) +{ + if (globalsData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + PTR_ArrayTypeDesc objArray = g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT]; + if (objArray) + globalsData->ArrayMethodTable = HOST_CDADDR(objArray->GetMethodTable()); + else + globalsData->ArrayMethodTable = 0; + + globalsData->StringMethodTable = HOST_CDADDR(g_pStringClass); + globalsData->ObjectMethodTable = HOST_CDADDR(g_pObjectClass); + globalsData->ExceptionMethodTable = HOST_CDADDR(g_pExceptionClass); + globalsData->FreeMethodTable = HOST_CDADDR(g_pFreeObjectMethodTable); + + SOSDacLeave(); + return hr; +} + + +HRESULT +ClrDataAccess::GetNestedExceptionData(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS *exceptionObject, CLRDATA_ADDRESS *nextNestedException) +{ + if (exception == 0 || exceptionObject == NULL || nextNestedException == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + +#ifdef WIN64EXCEPTIONS + ExceptionTracker *pExData = PTR_ExceptionTracker(TO_TADDR(exception)); +#else + ExInfo *pExData = PTR_ExInfo(TO_TADDR(exception)); +#endif // _WIN64 + + if (!pExData) + { + hr = E_INVALIDARG; + } + else + { + *exceptionObject = TO_CDADDR(*PTR_TADDR(pExData->m_hThrowable)); + *nextNestedException = PTR_HOST_TO_TADDR(pExData->m_pPrevNestedInfo); + } + + SOSDacLeave(); + return hr; +} + + +HRESULT +ClrDataAccess::GetDomainLocalModuleData(CLRDATA_ADDRESS addr, struct DacpDomainLocalModuleData *pLocalModuleData) +{ + if (addr == 0 || pLocalModuleData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + DomainLocalModule* pLocalModule = PTR_DomainLocalModule(TO_TADDR(addr)); + + pLocalModuleData->pGCStaticDataStart = TO_CDADDR(PTR_TO_TADDR(pLocalModule->GetPrecomputedGCStaticsBasePointer())); + pLocalModuleData->pNonGCStaticDataStart = TO_CDADDR(pLocalModule->GetPrecomputedNonGCStaticsBasePointer()); + pLocalModuleData->pDynamicClassTable = PTR_CDADDR(pLocalModule->m_pDynamicClassTable.Load()); + pLocalModuleData->pClassData = (TADDR) (PTR_HOST_MEMBER_TADDR(DomainLocalModule, pLocalModule, m_pDataBlob)); + + SOSDacLeave(); + return hr; +} + + +HRESULT +ClrDataAccess::GetDomainLocalModuleDataFromModule(CLRDATA_ADDRESS addr, struct DacpDomainLocalModuleData *pLocalModuleData) +{ + if (addr == 0 || pLocalModuleData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + Module* pModule = PTR_Module(TO_TADDR(addr)); + if( pModule->GetAssembly()->IsDomainNeutral() ) + { + // The module is loaded domain-neutral, then we need to know the specific AppDomain in order to + // choose a DomainLocalModule instance. Rather than try and guess an AppDomain (eg. based on + // whatever the current debugger thread is in), we'll fail and force the debugger to explicitly use + // a specific AppDomain. + hr = E_INVALIDARG; + } + else + { + DomainLocalModule* pLocalModule = PTR_DomainLocalModule(pModule->GetDomainLocalModule(NULL)); + if (!pLocalModule) + { + hr = E_INVALIDARG; + } + else + { + pLocalModuleData->pGCStaticDataStart = TO_CDADDR(PTR_TO_TADDR(pLocalModule->GetPrecomputedGCStaticsBasePointer())); + pLocalModuleData->pNonGCStaticDataStart = TO_CDADDR(pLocalModule->GetPrecomputedNonGCStaticsBasePointer()); + pLocalModuleData->pDynamicClassTable = PTR_CDADDR(pLocalModule->m_pDynamicClassTable.Load()); + pLocalModuleData->pClassData = (TADDR) (PTR_HOST_MEMBER_TADDR(DomainLocalModule, pLocalModule, m_pDataBlob)); + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetDomainLocalModuleDataFromAppDomain(CLRDATA_ADDRESS appDomainAddr, int moduleID, struct DacpDomainLocalModuleData *pLocalModuleData) +{ + if (appDomainAddr == 0 || moduleID < 0 || pLocalModuleData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + pLocalModuleData->appDomainAddr = appDomainAddr; + pLocalModuleData->ModuleID = moduleID; + + AppDomain *pAppDomain = PTR_AppDomain(TO_TADDR(appDomainAddr)); + ModuleIndex index = Module::IDToIndex(moduleID); + DomainLocalModule* pLocalModule = pAppDomain->GetDomainLocalBlock()->GetModuleSlot(index); + if (!pLocalModule) + { + hr = E_INVALIDARG; + } + else + { + pLocalModuleData->pGCStaticDataStart = TO_CDADDR(PTR_TO_TADDR(pLocalModule->GetPrecomputedGCStaticsBasePointer())); + pLocalModuleData->pNonGCStaticDataStart = TO_CDADDR(pLocalModule->GetPrecomputedNonGCStaticsBasePointer()); + pLocalModuleData->pDynamicClassTable = PTR_CDADDR(pLocalModule->m_pDynamicClassTable.Load()); + pLocalModuleData->pClassData = (TADDR) (PTR_HOST_MEMBER_TADDR(DomainLocalModule, pLocalModule, m_pDataBlob)); + } + + SOSDacLeave(); + return hr; +} + + + + +HRESULT +ClrDataAccess::GetThreadLocalModuleData(CLRDATA_ADDRESS thread, unsigned int index, struct DacpThreadLocalModuleData *pLocalModuleData) +{ + if (pLocalModuleData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + pLocalModuleData->threadAddr = thread; + pLocalModuleData->ModuleIndex = index; + + PTR_Thread pThread = PTR_Thread(TO_TADDR(thread)); + PTR_ThreadLocalBlock pLocalBlock = ThreadStatics::GetCurrentTLBIfExists(pThread, NULL); + if (!pLocalBlock) + { + hr = E_INVALIDARG; + } + else + { + PTR_ThreadLocalModule pLocalModule = pLocalBlock->GetTLMIfExists(ModuleIndex(index)); + if (!pLocalModule) + { + hr = E_INVALIDARG; + } + else + { + pLocalModuleData->pGCStaticDataStart = TO_CDADDR(PTR_TO_TADDR(pLocalModule->GetPrecomputedGCStaticsBasePointer())); + pLocalModuleData->pNonGCStaticDataStart = TO_CDADDR(pLocalModule->GetPrecomputedNonGCStaticsBasePointer()); + pLocalModuleData->pDynamicClassTable = PTR_CDADDR(pLocalModule->m_pDynamicClassTable); + pLocalModuleData->pClassData = (TADDR) (PTR_HOST_MEMBER_TADDR(ThreadLocalModule, pLocalModule, m_pDataBlob)); + } + } + + SOSDacLeave(); + return hr; +} + + +HRESULT ClrDataAccess::GetHandleEnum(ISOSHandleEnum **ppHandleEnum) +{ + unsigned int types[] = {HNDTYPE_WEAK_SHORT, HNDTYPE_WEAK_LONG, HNDTYPE_STRONG, HNDTYPE_PINNED, HNDTYPE_VARIABLE, HNDTYPE_DEPENDENT, + HNDTYPE_ASYNCPINNED, HNDTYPE_SIZEDREF, +#ifdef FEATURE_COMINTEROP + HNDTYPE_REFCOUNTED, HNDTYPE_WEAK_WINRT +#endif + }; + + return GetHandleEnumForTypes(types, _countof(types), ppHandleEnum); +} + +HRESULT ClrDataAccess::GetHandleEnumForTypes(unsigned int types[], unsigned int count, ISOSHandleEnum **ppHandleEnum) +{ + if (ppHandleEnum == 0) + return E_POINTER; + + SOSDacEnter(); + + DacHandleWalker *walker = new DacHandleWalker(); + + HRESULT hr = walker->Init(this, types, count); + + if (SUCCEEDED(hr)) + hr = walker->QueryInterface(__uuidof(ISOSHandleEnum), (void**)ppHandleEnum); + + if (FAILED(hr)) + delete walker; + + SOSDacLeave(); + return hr; +} + +HRESULT ClrDataAccess::GetHandleEnumForGC(unsigned int gen, ISOSHandleEnum **ppHandleEnum) +{ + if (ppHandleEnum == 0) + return E_POINTER; + + SOSDacEnter(); + + unsigned int types[] = {HNDTYPE_WEAK_SHORT, HNDTYPE_WEAK_LONG, HNDTYPE_STRONG, HNDTYPE_PINNED, HNDTYPE_VARIABLE, HNDTYPE_DEPENDENT, + HNDTYPE_ASYNCPINNED, HNDTYPE_SIZEDREF, +#ifdef FEATURE_COMINTEROP + HNDTYPE_REFCOUNTED, HNDTYPE_WEAK_WINRT +#endif + }; + + DacHandleWalker *walker = new DacHandleWalker(); + + HRESULT hr = walker->Init(this, types, _countof(types), gen); + if (SUCCEEDED(hr)) + hr = walker->QueryInterface(__uuidof(ISOSHandleEnum), (void**)ppHandleEnum); + + if (FAILED(hr)) + delete walker; + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::TraverseEHInfo(CLRDATA_ADDRESS ip, DUMPEHINFO pFunc, LPVOID token) +{ + if (ip == 0 || pFunc == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + EECodeInfo codeInfo(TO_TADDR(ip)); + if (!codeInfo.IsValid()) + { + hr = E_INVALIDARG; + } + + if (SUCCEEDED(hr)) + { + EH_CLAUSE_ENUMERATOR EnumState; + EE_ILEXCEPTION_CLAUSE EHClause; + unsigned EHCount; + + EHCount = codeInfo.GetJitManager()->InitializeEHEnumeration(codeInfo.GetMethodToken(), &EnumState); + for (unsigned i = 0; i < EHCount; i++) + { + codeInfo.GetJitManager()->GetNextEHClause(&EnumState, &EHClause); + + DACEHInfo deh; + ZeroMemory(&deh,sizeof(deh)); + + if (IsFault(&EHClause)) + { + deh.clauseType = EHFault; + } + else if (IsFinally(&EHClause)) + { + deh.clauseType = EHFinally; + } + else if (IsFilterHandler(&EHClause)) + { + deh.clauseType = EHFilter; + deh.filterOffset = EHClause.FilterOffset; + } + else if (IsTypedHandler(&EHClause)) + { + deh.clauseType = EHTyped; + deh.isCatchAllHandler = (&EHClause.TypeHandle == (void*)(size_t)mdTypeRefNil); + } + else + { + deh.clauseType = EHUnknown; + } + + if (HasCachedTypeHandle(&EHClause)) + { + deh.mtCatch = TO_CDADDR(&EHClause.TypeHandle); + } + else if(!IsFaultOrFinally(&EHClause)) + { + // the module of the token (whether a ref or def token) is the same as the module of the method containing the EH clause + deh.moduleAddr = HOST_CDADDR(codeInfo.GetMethodDesc()->GetModule()); + deh.tokCatch = EHClause.ClassToken; + } + + deh.tryStartOffset = EHClause.TryStartPC; + deh.tryEndOffset = EHClause.TryEndPC; + deh.handlerStartOffset = EHClause.HandlerStartPC; + deh.handlerEndOffset = EHClause.HandlerEndPC; + deh.isDuplicateClause = IsDuplicateClause(&EHClause); + + if (!(pFunc)(i, EHCount, &deh, token)) + { + // User wants to stop the enumeration + hr = E_ABORT; + break; + } + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::TraverseRCWCleanupList(CLRDATA_ADDRESS cleanupListPtr, VISITRCWFORCLEANUP pFunc, LPVOID token) +{ +#ifdef FEATURE_COMINTEROP + if (pFunc == 0) + return E_INVALIDARG; + + SOSDacEnter(); + RCWCleanupList *pList = g_pRCWCleanupList; + + if (cleanupListPtr) + { + pList = PTR_RCWCleanupList(TO_TADDR(cleanupListPtr)); + } + + if (pList) + { + PTR_RCW pBucket = dac_cast<PTR_RCW>(TO_TADDR(pList->m_pFirstBucket)); + while (pBucket != NULL) + { + PTR_RCW pRCW = pBucket; + Thread *pSTAThread = pRCW->GetSTAThread(); + LPVOID pCtxCookie = pRCW->GetWrapperCtxCookie(); + BOOL bIsFreeThreaded = pRCW->IsFreeThreaded(); + + while (pRCW) + { + (pFunc)(HOST_CDADDR(pRCW),(CLRDATA_ADDRESS)pCtxCookie, (CLRDATA_ADDRESS)(TADDR)pSTAThread, bIsFreeThreaded, token); + pRCW = pRCW->m_pNextRCW; + } + pBucket = pBucket->m_pNextCleanupBucket; + } + } + + SOSDacLeave(); + return hr; +#else + return E_NOTIMPL; +#endif // FEATURE_COMINTEROP +} + +HRESULT +ClrDataAccess::TraverseLoaderHeap(CLRDATA_ADDRESS loaderHeapAddr, VISITHEAP pFunc) +{ + if (loaderHeapAddr == 0 || pFunc == 0) + return E_INVALIDARG; + + SOSDacEnter(); + + LoaderHeap *pLoaderHeap = PTR_LoaderHeap(TO_TADDR(loaderHeapAddr)); + PTR_LoaderHeapBlock block = pLoaderHeap->m_pFirstBlock; + while (block.IsValid()) + { + TADDR addr = PTR_TO_TADDR(block->pVirtualAddress); + size_t size = block->dwVirtualSize; + + BOOL bCurrentBlock = (block == pLoaderHeap->m_pCurBlock); + + pFunc(addr,size,bCurrentBlock); + + block = block->pNext; + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::TraverseVirtCallStubHeap(CLRDATA_ADDRESS pAppDomain, VCSHeapType heaptype, VISITHEAP pFunc) +{ + if (pAppDomain == 0) + return E_INVALIDARG; + + SOSDacEnter(); + + BaseDomain* pBaseDomain = PTR_BaseDomain(TO_TADDR(pAppDomain)); + VirtualCallStubManager *pVcsMgr = PTR_VirtualCallStubManager((TADDR)pBaseDomain->GetLoaderAllocator()->GetVirtualCallStubManager()); + if (!pVcsMgr) + { + hr = E_POINTER; + } + else + { + LoaderHeap *pLoaderHeap = NULL; + switch(heaptype) + { + case IndcellHeap: + pLoaderHeap = pVcsMgr->indcell_heap; + break; + case LookupHeap: + pLoaderHeap = pVcsMgr->lookup_heap; + break; + case ResolveHeap: + pLoaderHeap = pVcsMgr->resolve_heap; + break; + case DispatchHeap: + pLoaderHeap = pVcsMgr->dispatch_heap; + break; + case CacheEntryHeap: + pLoaderHeap = pVcsMgr->cache_entry_heap; + break; + default: + hr = E_INVALIDARG; + } + + if (SUCCEEDED(hr)) + { + PTR_LoaderHeapBlock block = pLoaderHeap->m_pFirstBlock; + while (block.IsValid()) + { + TADDR addr = PTR_TO_TADDR(block->pVirtualAddress); + size_t size = block->dwVirtualSize; + + BOOL bCurrentBlock = (block == pLoaderHeap->m_pCurBlock); + pFunc(addr, size, bCurrentBlock); + + block = block->pNext; + } + } + } + + SOSDacLeave(); + return hr; +} + + +HRESULT +ClrDataAccess::GetSyncBlockData(unsigned int SBNumber, struct DacpSyncBlockData *pSyncBlockData) +{ + if (pSyncBlockData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + ZeroMemory(pSyncBlockData,sizeof(DacpSyncBlockData)); + pSyncBlockData->SyncBlockCount = (SyncBlockCache::s_pSyncBlockCache->m_FreeSyncTableIndex) - 1; + PTR_SyncTableEntry ste = PTR_SyncTableEntry(dac_cast<TADDR>(g_pSyncTable)+(sizeof(SyncTableEntry) * SBNumber)); + pSyncBlockData->bFree = ((dac_cast<TADDR>(ste->m_Object.Load())) & 1); + + if (pSyncBlockData->bFree == FALSE) + { + pSyncBlockData->Object = (CLRDATA_ADDRESS)dac_cast<TADDR>(ste->m_Object.Load()); + + if (ste->m_SyncBlock != NULL) + { + SyncBlock *pBlock = PTR_SyncBlock(ste->m_SyncBlock); + pSyncBlockData->SyncBlockPointer = HOST_CDADDR(pBlock); +#ifdef FEATURE_COMINTEROP + if (pBlock->m_pInteropInfo) + { + pSyncBlockData->COMFlags |= (pBlock->m_pInteropInfo->DacGetRawRCW() != 0) ? SYNCBLOCKDATA_COMFLAGS_RCW : 0; + pSyncBlockData->COMFlags |= (pBlock->m_pInteropInfo->GetCCW() != NULL) ? SYNCBLOCKDATA_COMFLAGS_CCW : 0; +#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION + pSyncBlockData->COMFlags |= (pBlock->m_pInteropInfo->GetComClassFactory() != NULL) ? SYNCBLOCKDATA_COMFLAGS_CF : 0; +#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION + } +#endif // FEATURE_COMINTEROP + + pSyncBlockData->MonitorHeld = pBlock->m_Monitor.m_MonitorHeld; + pSyncBlockData->Recursion = pBlock->m_Monitor.m_Recursion; + pSyncBlockData->HoldingThread = HOST_CDADDR(pBlock->m_Monitor.m_HoldingThread); + + if (pBlock->GetAppDomainIndex().m_dwIndex) + { + pSyncBlockData->appDomainPtr = PTR_HOST_TO_TADDR( + SystemDomain::TestGetAppDomainAtIndex(pBlock->GetAppDomainIndex())); + } + + // TODO: Microsoft, implement the wait list + pSyncBlockData->AdditionalThreadCount = 0; + + if (pBlock->m_Link.m_pNext != NULL) + { + PTR_SLink pLink = pBlock->m_Link.m_pNext; + do + { + pSyncBlockData->AdditionalThreadCount++; + pLink = pBlock->m_Link.m_pNext; + } + while ((pLink != NULL) && + (pSyncBlockData->AdditionalThreadCount < 1000)); + } + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetSyncBlockCleanupData(CLRDATA_ADDRESS syncBlock, struct DacpSyncBlockCleanupData *syncBlockCData) +{ + if (syncBlock == 0 || syncBlockCData == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + ZeroMemory (syncBlockCData, sizeof(DacpSyncBlockCleanupData)); + SyncBlock *pBlock = NULL; + + if (syncBlock == NULL && SyncBlockCache::s_pSyncBlockCache->m_pCleanupBlockList) + { + pBlock = (SyncBlock *) PTR_SyncBlock( + PTR_HOST_TO_TADDR(SyncBlockCache::s_pSyncBlockCache->m_pCleanupBlockList) - offsetof(SyncBlock, m_Link)); + } + else + { + pBlock = PTR_SyncBlock(TO_TADDR(syncBlock)); + } + + if (pBlock) + { + syncBlockCData->SyncBlockPointer = HOST_CDADDR(pBlock); + if (pBlock->m_Link.m_pNext) + { + syncBlockCData->nextSyncBlock = (CLRDATA_ADDRESS) + (PTR_HOST_TO_TADDR(pBlock->m_Link.m_pNext) - offsetof(SyncBlock, m_Link)); + } + +#ifdef FEATURE_COMINTEROP + if (pBlock->m_pInteropInfo->DacGetRawRCW()) + syncBlockCData->blockRCW = (CLRDATA_ADDRESS) pBlock->m_pInteropInfo->DacGetRawRCW(); +#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION + if (pBlock->m_pInteropInfo->GetComClassFactory()) + syncBlockCData->blockClassFactory = (CLRDATA_ADDRESS) (TADDR) pBlock->m_pInteropInfo->GetComClassFactory(); +#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION + if (pBlock->m_pInteropInfo->GetCCW()) + syncBlockCData->blockCCW = (CLRDATA_ADDRESS) dac_cast<TADDR>(pBlock->m_pInteropInfo->GetCCW()); +#endif // FEATURE_COMINTEROP + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetJitHelperFunctionName(CLRDATA_ADDRESS ip, unsigned int count, __out_z __inout_ecount(count) char *name, unsigned int *pNeeded) +{ + SOSDacEnter(); + + PCSTR pszHelperName = GetJitHelperName(TO_TADDR(ip)); + if (pszHelperName == NULL) + { + hr = E_INVALIDARG; + } + else + { + unsigned int len = (unsigned int)strlen(pszHelperName) + 1; + + if (pNeeded) + *pNeeded = len; + + if (name) + { + if (count < len) + hr = E_FAIL; + else + strcpy_s(name, count, pszHelperName); + } + } + + SOSDacLeave(); + return hr; +}; + +HRESULT +ClrDataAccess::GetJumpThunkTarget(T_CONTEXT *ctx, CLRDATA_ADDRESS *targetIP, CLRDATA_ADDRESS *targetMD) +{ + if (ctx == NULL || targetIP == NULL || targetMD == NULL) + return E_INVALIDARG; + +#ifdef _TARGET_AMD64_ + SOSDacEnter(); + + if (!GetAnyThunkTarget(ctx, targetIP, targetMD)) + hr = E_FAIL; + + SOSDacLeave(); + return hr; +#else + return E_FAIL; +#endif // _WIN64 +} + + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +STDMETHODIMP +ClrDataAccess::Request(IN ULONG32 reqCode, + IN ULONG32 inBufferSize, + IN BYTE* inBuffer, + IN ULONG32 outBufferSize, + OUT BYTE* outBuffer) +{ + HRESULT status; + + DAC_ENTER(); + + EX_TRY + { + switch(reqCode) + { + case CLRDATA_REQUEST_REVISION: + if (inBufferSize != 0 || + inBuffer || + outBufferSize != sizeof(ULONG32)) + { + status = E_INVALIDARG; + } + else + { + *(ULONG32*)outBuffer = 9; + status = S_OK; + } + break; + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +void +ClrDataAccess::EnumWksGlobalMemoryRegions(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + WKS::gc_heap::ephemeral_heap_segment.EnumMem(); + WKS::gc_heap::alloc_allocated.EnumMem(); + WKS::gc_heap::finalize_queue.EnumMem(); + WKS::generation_table.EnumMem(); + WKS::gc_heap::oom_info.EnumMem(); + + if (WKS::generation_table.IsValid()) + { + // enumerating the generations from max (which is normally gen2) to max+1 gives you + // the segment list for all the normal segements plus the large heap segment (max+1) + // this is the convention in the GC so it is repeated here + for (ULONG i = GCHeap::GetMaxGeneration(); i <= GCHeap::GetMaxGeneration()+1; i++) + { + __DPtr<WKS::heap_segment> seg = dac_cast<TADDR>(WKS::generation_table[i].start_segment); + while (seg) + { + DacEnumMemoryRegion(dac_cast<TADDR>(seg), sizeof(WKS::heap_segment)); + + seg = __DPtr<WKS::heap_segment>(dac_cast<TADDR>(seg->next)); + } + } + } +} + +HRESULT +ClrDataAccess::GetClrWatsonBuckets(CLRDATA_ADDRESS thread, void *pGenericModeBlock) +{ +#ifdef FEATURE_PAL + // This API is not available under FEATURE_PAL + return E_FAIL; +#else // FEATURE_PAL + if (thread == 0 || pGenericModeBlock == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + + Thread * pThread = PTR_Thread(TO_TADDR(thread)); + hr = GetClrWatsonBucketsWorker(pThread, reinterpret_cast<GenericModeBlock *>(pGenericModeBlock)); + + SOSDacLeave(); + return hr; +#endif // FEATURE_PAL +} + +#ifndef FEATURE_PAL + +HRESULT ClrDataAccess::GetClrWatsonBucketsWorker(Thread * pThread, GenericModeBlock * pGM) +{ + if ((pThread == NULL) || (pGM == NULL)) + { + return E_INVALIDARG; + } + + // By default, there are no buckets + PTR_VOID pBuckets = NULL; + + // Get the handle to the throwble + OBJECTHANDLE ohThrowable = pThread->GetThrowableAsHandle(); + if (ohThrowable != NULL) + { + // Get the object from handle and check if the throwable is preallocated or not + OBJECTREF oThrowable = ObjectFromHandle(ohThrowable); + if (oThrowable != NULL) + { + // Does the throwable have buckets? + if (((EXCEPTIONREF)oThrowable)->AreWatsonBucketsPresent()) + { + // Get the watson buckets from the throwable for non-preallocated + // exceptions + U1ARRAYREF refWatsonBucketArray = ((EXCEPTIONREF)oThrowable)->GetWatsonBucketReference(); + pBuckets = dac_cast<PTR_VOID>(refWatsonBucketArray->GetDataPtr()); + } + else + { + // This is a preallocated exception object - check if the UE Watson bucket tracker + // has any bucket details + pBuckets = pThread->GetExceptionState()->GetUEWatsonBucketTracker()->RetrieveWatsonBuckets(); + if (pBuckets == NULL) + { + // Since the UE watson bucket tracker does not have them, look up the current + // exception tracker + if (pThread->GetExceptionState()->GetCurrentExceptionTracker() != NULL) + { + pBuckets = pThread->GetExceptionState()->GetCurrentExceptionTracker()->GetWatsonBucketTracker()->RetrieveWatsonBuckets(); + } + } + } + } + } + else + { + // Debuger.Break doesn't have a throwable, but saves Watson buckets in EHWatsonBucketTracker. + pBuckets = pThread->GetExceptionState()->GetUEWatsonBucketTracker()->RetrieveWatsonBuckets(); + } + + // If pBuckets is non-null, it is the address of a Watson GenericModeBlock in the target process. + if (pBuckets != NULL) + { + ULONG32 returned = 0; + HRESULT hr = m_pTarget->ReadVirtual(dac_cast<TADDR>(pBuckets), reinterpret_cast<BYTE *>(pGM), sizeof(*pGM), &returned); + if (FAILED(hr)) + { + hr = CORDBG_E_READVIRTUAL_FAILURE; + } + if (SUCCEEDED(hr) && (returned != sizeof(*pGM))) + { + hr = HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY); + } + return hr; + } + else + { + // Buckets are not available + return S_FALSE; + } +} + +#endif // FEATURE_PAL + +HRESULT ClrDataAccess::GetTLSIndex(ULONG *pIndex) +{ + if (pIndex == NULL) + return E_INVALIDARG; + + SOSDacEnter(); + if (CExecutionEngine::GetTlsIndex() == TLS_OUT_OF_INDEXES) + { + *pIndex = 0; + hr = S_FALSE; + } + else + { + *pIndex = CExecutionEngine::GetTlsIndex(); + } + + SOSDacLeave(); + return hr; +} + +HRESULT ClrDataAccess::GetDacModuleHandle(HMODULE *phModule) +{ + if(phModule == NULL) + return E_INVALIDARG; + *phModule = GetModuleInst(); + return S_OK; +} + +HRESULT ClrDataAccess::GetRCWData(CLRDATA_ADDRESS addr, struct DacpRCWData *rcwData) +{ + if (addr == 0 || rcwData == NULL) + return E_INVALIDARG; + +#ifdef FEATURE_COMINTEROP + SOSDacEnter(); + + ZeroMemory (rcwData, sizeof(DacpRCWData)); + + PTR_RCW pRCW = dac_cast<PTR_RCW>(CLRDATA_ADDRESS_TO_TADDR(addr)); + + rcwData->identityPointer = TO_CDADDR(pRCW->m_pIdentity); + rcwData->unknownPointer = TO_CDADDR(pRCW->GetRawIUnknown_NoAddRef()); + rcwData->vtablePtr = TO_CDADDR(pRCW->m_vtablePtr); + rcwData->creatorThread = TO_CDADDR(pRCW->m_pCreatorThread); + rcwData->ctxCookie = TO_CDADDR(pRCW->GetWrapperCtxCookie()); + rcwData->refCount = pRCW->m_cbRefCount; + + rcwData->isJupiterObject = pRCW->IsJupiterObject(); + rcwData->supportsIInspectable = pRCW->SupportsIInspectable(); + rcwData->isAggregated = pRCW->IsURTAggregated(); + rcwData->isContained = pRCW->IsURTContained(); + rcwData->jupiterObject = TO_CDADDR(pRCW->GetJupiterObject()); + rcwData->isFreeThreaded = pRCW->IsFreeThreaded(); + rcwData->isDisconnected = pRCW->IsDisconnected(); + + if (pRCW->m_SyncBlockIndex != 0) + { + PTR_SyncTableEntry ste = PTR_SyncTableEntry(dac_cast<TADDR>(g_pSyncTable) + (sizeof(SyncTableEntry) * pRCW->m_SyncBlockIndex)); + rcwData->managedObject = PTR_CDADDR(ste->m_Object.Load()); + } + + // count the number of cached interface pointers + rcwData->interfaceCount = 0; + RCW::CachedInterfaceEntryIterator it = pRCW->IterateCachedInterfacePointers(); + while (it.Next()) + { + if (it.GetEntry()->m_pUnknown.Load() != NULL) + rcwData->interfaceCount++; + } + + SOSDacLeave(); + return hr; +#else + return E_NOTIMPL; +#endif +} + +HRESULT ClrDataAccess::GetRCWInterfaces(CLRDATA_ADDRESS rcw, unsigned int count, struct DacpCOMInterfacePointerData interfaces[], unsigned int *pNeeded) +{ + if (rcw == 0) + return E_INVALIDARG; + +#ifdef FEATURE_COMINTEROP + + SOSDacEnter(); + PTR_RCW pRCW = dac_cast<PTR_RCW>(CLRDATA_ADDRESS_TO_TADDR(rcw)); + if (interfaces == NULL) + { + if (pNeeded) + { + unsigned int c = 0; + RCW::CachedInterfaceEntryIterator it = pRCW->IterateCachedInterfacePointers(); + while (it.Next()) + { + if (it.GetEntry()->m_pUnknown.Load() != NULL) + c++; + } + + *pNeeded = c; + } + else + { + hr = E_INVALIDARG; + } + } + else + { + ZeroMemory(interfaces, sizeof(DacpCOMInterfacePointerData) * count); + + unsigned int itemIndex = 0; + RCW::CachedInterfaceEntryIterator it = pRCW->IterateCachedInterfacePointers(); + while (it.Next()) + { + InterfaceEntry *pEntry = it.GetEntry(); + if (pEntry->m_pUnknown.Load() != NULL) + { + if (itemIndex >= count) + { + // the outBuffer is too small + hr = E_INVALIDARG; + break; + } + else + { + interfaces[itemIndex].interfacePtr = TO_CDADDR(pEntry->m_pUnknown.Load()); + interfaces[itemIndex].methodTable = TO_CDADDR(pEntry->m_pMT.Load()); + interfaces[itemIndex].comContext = TO_CDADDR(it.GetCtxCookie()); + itemIndex++; + } + } + } + + if (SUCCEEDED(hr) && pNeeded) + *pNeeded = itemIndex; + } + + SOSDacLeave(); + return hr; +#else + return E_NOTIMPL; +#endif +} + +#ifdef FEATURE_COMINTEROP +PTR_ComCallWrapper ClrDataAccess::DACGetCCWFromAddress(CLRDATA_ADDRESS addr) +{ + PTR_ComCallWrapper pCCW = NULL; + + // first check whether the address is our COM IP + TADDR pPtr = CLRDATA_ADDRESS_TO_TADDR(addr); + + ULONG32 returned = 0; + if (m_pTarget->ReadVirtual(pPtr, (PBYTE)&pPtr, sizeof(TADDR), &returned) == S_OK && + returned == sizeof(TADDR)) + { + // this should be the vtable pointer - dereference the 2nd slot + if (m_pTarget->ReadVirtual(pPtr + sizeof(PBYTE) * TEAR_OFF_SLOT, (PBYTE)&pPtr, sizeof(TADDR), &returned) == S_OK && + returned == sizeof(TADDR)) + { + +#ifdef DBG_TARGET_ARM + // clear the THUMB bit on pPtr before comparing with known vtable entry + pPtr &= ~THUMB_CODE; +#endif + + if (pPtr == GetEEFuncEntryPoint(TEAR_OFF_STANDARD)) + { + // Points to ComCallWrapper + PTR_IUnknown pUnk(CLRDATA_ADDRESS_TO_TADDR(addr)); + pCCW = ComCallWrapper::GetWrapperFromIP(pUnk); + } + else if (pPtr == GetEEFuncEntryPoint(TEAR_OFF_SIMPLE) || pPtr == GetEEFuncEntryPoint(TEAR_OFF_SIMPLE_INNER)) + { + // Points to SimpleComCallWrapper + PTR_IUnknown pUnk(CLRDATA_ADDRESS_TO_TADDR(addr)); + pCCW = SimpleComCallWrapper::GetWrapperFromIP(pUnk)->GetMainWrapper(); + } + } + } + + if (pCCW == NULL) + { + // no luck interpreting the address as a COM interface pointer - it must be a CCW address + pCCW = dac_cast<PTR_ComCallWrapper>(CLRDATA_ADDRESS_TO_TADDR(addr)); + } + + if (pCCW->IsLinked()) + pCCW = ComCallWrapper::GetStartWrapper(pCCW); + + return pCCW; +} + +PTR_IUnknown ClrDataAccess::DACGetCOMIPFromCCW(PTR_ComCallWrapper pCCW, int vtableIndex) +{ + if (pCCW->m_rgpIPtr[vtableIndex] != NULL) + { + PTR_IUnknown pUnk = dac_cast<PTR_IUnknown>(dac_cast<TADDR>(pCCW) + offsetof(ComCallWrapper, m_rgpIPtr[vtableIndex])); + + PTR_ComMethodTable pCMT = ComMethodTable::ComMethodTableFromIP(pUnk); + if (pCMT->IsLayoutComplete()) + { + // return only fully laid out vtables + return pUnk; + } + } + return NULL; +} +#endif + + +HRESULT ClrDataAccess::GetCCWData(CLRDATA_ADDRESS ccw, struct DacpCCWData *ccwData) +{ + if (ccw == 0 || ccwData == NULL) + return E_INVALIDARG; + +#ifdef FEATURE_COMINTEROP + SOSDacEnter(); + ZeroMemory (ccwData, sizeof(DacpCCWData)); + + PTR_ComCallWrapper pCCW = DACGetCCWFromAddress(ccw); + PTR_SimpleComCallWrapper pSimpleCCW = pCCW->GetSimpleWrapper(); + + ccwData->outerIUnknown = TO_CDADDR(pSimpleCCW->m_pOuter); + ccwData->refCount = pSimpleCCW->GetRefCount(); + ccwData->isNeutered = pSimpleCCW->IsNeutered(); + ccwData->ccwAddress = TO_CDADDR(dac_cast<TADDR>(pCCW)); + + ccwData->jupiterRefCount = pSimpleCCW->GetJupiterRefCount(); + ccwData->isPegged = pSimpleCCW->IsPegged(); + ccwData->isGlobalPegged = RCWWalker::IsGlobalPeggingOn(); + ccwData->hasStrongRef = pCCW->IsWrapperActive(); + ccwData->handle = pCCW->GetObjectHandle(); + ccwData->isExtendsCOMObject = pCCW->GetSimpleWrapper()->IsExtendsCOMObject(); + ccwData->isAggregated = pCCW->GetSimpleWrapper()->IsAggregated(); + + if (pCCW->GetObjectHandle() != NULL) + ccwData->managedObject = PTR_CDADDR(ObjectFromHandle(pCCW->GetObjectHandle())); + + // count the number of COM vtables + ccwData->interfaceCount = 0; + while (pCCW != NULL) + { + for (int i = 0; i < ComCallWrapper::NumVtablePtrs; i++) + { + if (DACGetCOMIPFromCCW(pCCW, i) != NULL) + ccwData->interfaceCount++; + } + pCCW = ComCallWrapper::GetNext(pCCW); + } + + SOSDacLeave(); + return hr; +#else + return E_NOTIMPL; +#endif +} + +HRESULT ClrDataAccess::GetCCWInterfaces(CLRDATA_ADDRESS ccw, unsigned int count, struct DacpCOMInterfacePointerData interfaces[], unsigned int *pNeeded) +{ + if (ccw == 0) + return E_INVALIDARG; + +#ifdef FEATURE_COMINTEROP + SOSDacEnter(); + PTR_ComCallWrapper pCCW = DACGetCCWFromAddress(ccw); + + if (interfaces == NULL) + { + if (pNeeded) + { + unsigned int c = 0; + while (pCCW != NULL) + { + for (int i = 0; i < ComCallWrapper::NumVtablePtrs; i++) + if (DACGetCOMIPFromCCW(pCCW, i) != NULL) + c++; + pCCW = ComCallWrapper::GetNext(pCCW); + } + + *pNeeded = c; + } + else + { + hr = E_INVALIDARG; + } + } + else + { + ZeroMemory(interfaces, sizeof(DacpCOMInterfacePointerData) * count); + + PTR_ComCallWrapperTemplate pCCWTemplate = pCCW->GetSimpleWrapper()->GetComCallWrapperTemplate(); + unsigned int itemIndex = 0; + unsigned int wrapperOffset = 0; + while (pCCW != NULL && SUCCEEDED(hr)) + { + for (int i = 0; i < ComCallWrapper::NumVtablePtrs && SUCCEEDED(hr); i++) + { + PTR_IUnknown pUnk = DACGetCOMIPFromCCW(pCCW, i); + if (pUnk != NULL) + { + if (itemIndex >= count) + { + // the outBuffer is too small + hr = E_INVALIDARG; + break; + } + + interfaces[itemIndex].interfacePtr = PTR_CDADDR(pUnk); + + // if this is the first ComCallWrapper, the 0th vtable slots is special + if (wrapperOffset == 0 && i == ComCallWrapper::Slot_Basic) + { + // this is IDispatch/IUnknown + interfaces[itemIndex].methodTable = NULL; + } + else + { + // this slot represents the class interface or an interface implemented by the class + DWORD ifaceMapIndex = wrapperOffset + i - ComCallWrapper::Slot_FirstInterface; + + PTR_ComMethodTable pCMT = ComMethodTable::ComMethodTableFromIP(pUnk); + interfaces[itemIndex].methodTable = PTR_CDADDR(pCMT->GetMethodTable()); + } + + itemIndex++; + } + } + + pCCW = ComCallWrapper::GetNext(pCCW); + wrapperOffset += ComCallWrapper::NumVtablePtrs; + } + + if (SUCCEEDED(hr) && pNeeded) + *pNeeded = itemIndex; + } + + SOSDacLeave(); + return hr; +#else + return E_NOTIMPL; +#endif +} + +HRESULT ClrDataAccess::GetObjectExceptionData(CLRDATA_ADDRESS objAddr, struct DacpExceptionObjectData *data) +{ + if (data == NULL) + return E_POINTER; + + SOSDacEnter(); + + PTR_ExceptionObject pObj = dac_cast<PTR_ExceptionObject>(TO_TADDR(objAddr)); + + data->Message = TO_CDADDR(dac_cast<TADDR>(pObj->GetMessage())); + data->InnerException = TO_CDADDR(dac_cast<TADDR>(pObj->GetInnerException())); + data->StackTrace = TO_CDADDR(dac_cast<TADDR>(pObj->GetStackTraceArrayObject())); + data->WatsonBuckets = TO_CDADDR(dac_cast<TADDR>(pObj->GetWatsonBucketReference())); + data->StackTraceString = TO_CDADDR(dac_cast<TADDR>(pObj->GetStackTraceString())); + data->RemoteStackTraceString = TO_CDADDR(dac_cast<TADDR>(pObj->GetRemoteStackTraceString())); + data->HResult = pObj->GetHResult(); + data->XCode = pObj->GetXCode(); + + SOSDacLeave(); + + return hr; +} + +HRESULT ClrDataAccess::IsRCWDCOMProxy(CLRDATA_ADDRESS rcwAddr, BOOL* isDCOMProxy) +{ + if (isDCOMProxy == nullptr) + { + return E_POINTER; + } + + *isDCOMProxy = FALSE; + +#ifdef FEATURE_COMINTEROP + SOSDacEnter(); + + PTR_RCW pRCW = dac_cast<PTR_RCW>(CLRDATA_ADDRESS_TO_TADDR(rcwAddr)); + *isDCOMProxy = pRCW->IsDCOMProxy(); + + SOSDacLeave(); + + return S_OK; +#else + return E_NOTIMPL; +#endif // FEATURE_COMINTEROP +} + +HRESULT ClrDataAccess::GetClrNotification(CLRDATA_ADDRESS arguments[], int count, int *pNeeded) +{ + SOSDacEnter(); + + *pNeeded = MAX_CLR_NOTIFICATION_ARGS; + + if (g_clrNotificationArguments[0] == NULL) + { + hr = E_FAIL; + } + else + { + for (int i = 0; i < count && i < MAX_CLR_NOTIFICATION_ARGS; i++) + { + arguments[i] = g_clrNotificationArguments[i]; + } + } + + SOSDacLeave(); + + return hr;; +}
\ No newline at end of file diff --git a/src/debug/daccess/request_svr.cpp b/src/debug/daccess/request_svr.cpp new file mode 100644 index 0000000000..429f30020f --- /dev/null +++ b/src/debug/daccess/request_svr.cpp @@ -0,0 +1,348 @@ +// 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: request.cpp +// +// CorDataAccess::Request implementation. +// +//***************************************************************************** + +#include "stdafx.h" +#include "dacdbiinterface.h" +#include "dacdbiimpl.h" + +#if defined(FEATURE_SVR_GC) + +#include <sigformat.h> +#include <win32threadpool.h> + +#include <gceesvr.cpp> + + +int GCHeapCount() +{ + return SVR::gc_heap::n_heaps; +} + +HRESULT GetServerHeapData(CLRDATA_ADDRESS addr, DacpHeapSegmentData *pSegment) +{ + // get field values (target addresses) for the heap segment at addr + if (!addr) + { + // PREfix. + return E_INVALIDARG; + } + + // marshal the segment from target to host + SVR::heap_segment *pHeapSegment = + __DPtr<SVR::heap_segment>(TO_TADDR(addr)); + + // initialize fields by copying from the marshaled segment (note that these are all target addresses) + pSegment->segmentAddr = addr; + pSegment->allocated = (CLRDATA_ADDRESS)(ULONG_PTR) pHeapSegment->allocated; + pSegment->committed = (CLRDATA_ADDRESS)(ULONG_PTR) pHeapSegment->committed; + pSegment->reserved = (CLRDATA_ADDRESS)(ULONG_PTR) pHeapSegment->reserved; + pSegment->used = (CLRDATA_ADDRESS)(ULONG_PTR) pHeapSegment->used; + pSegment->mem = (CLRDATA_ADDRESS)(ULONG_PTR) (pHeapSegment->mem); + pSegment->next = (CLRDATA_ADDRESS)dac_cast<TADDR>(pHeapSegment->next); + pSegment->gc_heap = (CLRDATA_ADDRESS)(ULONG_PTR) pHeapSegment->heap; + + return S_OK; +} + +HRESULT GetServerHeaps(CLRDATA_ADDRESS pGCHeaps[], ICorDebugDataTarget * pTarget) +{ + // @todo Microsoft: It would be good to have an assert here to ensure pGCHeaps is large enough to + // hold all the addresses. Currently we check that in the only caller, but if we were to call this from + // somewhere else in the future, we could have a buffer overrun. + + // The runtime declares its own global array of gc heap addresses for multiple heap scenarios. We need to get + // its starting address. This expression is a little tricky to parse, but in DAC builds, g_heaps is + // a DAC global (__GlobalPtr). The __GlobalPtr<...>::GetAddr() function gets the starting address of that global, but + // be sure to note this is a target address. We'll use this as our source for getting our local list of + // heap addresses. + TADDR ptr = SVR::gc_heap::g_heaps.GetAddr(); + ULONG32 bytesRead = 0; + + for (int i=0;i<GCHeapCount();i++) + { + + LPVOID pGCHeapAddr; + + // read the i-th element of g_heaps into pGCHeapAddr + // @todo Microsoft: Again, if we capture the HRESULT from ReadVirtual, we can print a more explanatory + // failure message. + if (pTarget->ReadVirtual(ptr + i*sizeof(TADDR), + (PBYTE) &pGCHeapAddr, sizeof(TADDR), + &bytesRead) != S_OK) + { + return E_FAIL; + } + if (bytesRead != sizeof(LPVOID)) + { + return E_FAIL; + } + + // store the heap's starting address in our array. + pGCHeaps[i] = (CLRDATA_ADDRESS)(ULONG_PTR) pGCHeapAddr; + } + return S_OK; +} + +#define PTR_CDADDR(ptr) TO_CDADDR(PTR_TO_TADDR(ptr)) +#define HOST_CDADDR(host) TO_CDADDR(PTR_HOST_TO_TADDR(host)) + +typedef DPTR(class SVR::gc_heap) PTR_SVR_gc_heap; + +HRESULT ClrDataAccess::GetServerAllocData(unsigned int count, struct DacpGenerationAllocData *data, unsigned int *pNeeded) +{ + unsigned int heaps = (unsigned int)SVR::gc_heap::n_heaps; + if (pNeeded) + *pNeeded = heaps; + + if (data) + { + if (count > heaps) + count = heaps; + + for (int n=0;n<SVR::gc_heap::n_heaps;n++) + { + PTR_SVR_gc_heap pHeap = PTR_SVR_gc_heap(SVR::gc_heap::g_heaps[n]); + for (int i=0;i<NUMBERGENERATIONS;i++) + { + data[n].allocData[i].allocBytes = (CLRDATA_ADDRESS)(ULONG_PTR) pHeap->generation_table[i].allocation_context.alloc_bytes; + data[n].allocData[i].allocBytesLoh = (CLRDATA_ADDRESS)(ULONG_PTR) pHeap->generation_table[i].allocation_context.alloc_bytes_loh; + } + } + } + + return S_OK; +} + +HRESULT ClrDataAccess::ServerGCHeapDetails(CLRDATA_ADDRESS heapAddr, DacpGcHeapDetails *detailsData) +{ + if (!heapAddr) + { + // PREfix. + return E_INVALIDARG; + } + + SVR::gc_heap *pHeap = PTR_SVR_gc_heap(TO_TADDR(heapAddr)); + int i; + + //get global information first + detailsData->heapAddr = heapAddr; + + detailsData->lowest_address = PTR_CDADDR(g_lowest_address); + detailsData->highest_address = PTR_CDADDR(g_highest_address); + detailsData->card_table = PTR_CDADDR(g_card_table); + + // now get information specific to this heap (server mode gives us several heaps; we're getting + // information about only one of them. + detailsData->alloc_allocated = (CLRDATA_ADDRESS)(ULONG_PTR) pHeap->alloc_allocated; + detailsData->ephemeral_heap_segment = (CLRDATA_ADDRESS)(ULONG_PTR) pHeap->ephemeral_heap_segment; + + // get bounds for the different generations + for (i=0; i<NUMBERGENERATIONS; i++) + { + detailsData->generation_table[i].start_segment = (CLRDATA_ADDRESS)dac_cast<TADDR>(pHeap->generation_table[i].start_segment); + detailsData->generation_table[i].allocation_start = (CLRDATA_ADDRESS)(ULONG_PTR) pHeap->generation_table[i].allocation_start; + detailsData->generation_table[i].allocContextPtr = (CLRDATA_ADDRESS)(ULONG_PTR) pHeap->generation_table[i].allocation_context.alloc_ptr; + detailsData->generation_table[i].allocContextLimit = (CLRDATA_ADDRESS)(ULONG_PTR) pHeap->generation_table[i].allocation_context.alloc_limit; + } + + // since these are all TADDRS, we have to compute the address of the m_FillPointers field explicitly + TADDR pFillPointerArray = dac_cast<TADDR>(pHeap->finalize_queue) + offsetof(SVR::CFinalize,m_FillPointers); + + for(i=0; i<(NUMBERGENERATIONS+SVR::CFinalize::ExtraSegCount); i++) + { + ULONG32 returned = 0; + size_t pValue; + HRESULT hr = m_pTarget->ReadVirtual(pFillPointerArray+(i*sizeof(TADDR)), + (PBYTE)&pValue, + sizeof(TADDR), + &returned); + if (FAILED(hr) || (returned != sizeof(TADDR))) + { + return E_FAIL; + } + + detailsData->finalization_fill_pointers[i] = (CLRDATA_ADDRESS) pValue; + } + + return S_OK; +} + +HRESULT +ClrDataAccess::ServerOomData(CLRDATA_ADDRESS addr, DacpOomData *oomData) +{ + SVR::gc_heap *pHeap = PTR_SVR_gc_heap(TO_TADDR(addr)); + + oom_history* pOOMInfo = (oom_history*)((TADDR)pHeap + offsetof(SVR::gc_heap,oom_info)); + oomData->reason = pOOMInfo->reason; + oomData->alloc_size = pOOMInfo->alloc_size; + oomData->available_pagefile_mb = pOOMInfo->available_pagefile_mb; + oomData->gc_index = pOOMInfo->gc_index; + oomData->fgm = pOOMInfo->fgm; + oomData->size = pOOMInfo->size; + oomData->loh_p = pOOMInfo->loh_p; + + return S_OK; +} + +HRESULT +ClrDataAccess::ServerGCInterestingInfoData(CLRDATA_ADDRESS addr, DacpGCInterestingInfoData *interestingInfoData) +{ +#ifdef GC_CONFIG_DRIVEN + SVR::gc_heap *pHeap = PTR_SVR_gc_heap(TO_TADDR(addr)); + + size_t* dataPoints = (size_t*)&(pHeap->interesting_data_per_heap); + for (int i = 0; i < NUM_GC_DATA_POINTS; i++) + interestingInfoData->interestingDataPoints[i] = dataPoints[i]; + size_t* mechanisms = (size_t*)&(pHeap->compact_reasons_per_heap); + for (int i = 0; i < MAX_COMPACT_REASONS_COUNT; i++) + interestingInfoData->compactReasons[i] = mechanisms[i]; + mechanisms = (size_t*)&(pHeap->expand_mechanisms_per_heap); + for (int i = 0; i < MAX_EXPAND_MECHANISMS_COUNT; i++) + interestingInfoData->expandMechanisms[i] = mechanisms[i]; + mechanisms = (size_t*)&(pHeap->interesting_mechanism_bits_per_heap); + for (int i = 0; i < MAX_GC_MECHANISM_BITS_COUNT; i++) + interestingInfoData->bitMechanisms[i] = mechanisms[i]; + + return S_OK; +#else + return E_NOTIMPL; +#endif //GC_CONFIG_DRIVEN +} + +HRESULT ClrDataAccess::ServerGCHeapAnalyzeData(CLRDATA_ADDRESS heapAddr, DacpGcHeapAnalyzeData *analyzeData) +{ + if (!heapAddr) + { + // PREfix. + return E_INVALIDARG; + } + + SVR::gc_heap *pHeap = PTR_SVR_gc_heap(TO_TADDR(heapAddr)); + + analyzeData->heapAddr = heapAddr; + analyzeData->internal_root_array = (CLRDATA_ADDRESS)(ULONG_PTR) pHeap->internal_root_array; + analyzeData->internal_root_array_index = (size_t) pHeap->internal_root_array_index; + analyzeData->heap_analyze_success = (BOOL)pHeap->heap_analyze_success; + + return S_OK; +} + +void +ClrDataAccess::EnumSvrGlobalMemoryRegions(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + SVR::gc_heap::n_heaps.EnumMem(); + DacEnumMemoryRegion(SVR::gc_heap::g_heaps.GetAddr(), + sizeof(TADDR) * SVR::gc_heap::n_heaps); + + SVR::gc_heap::g_heaps.EnumMem(); + + for (int i=0;i<SVR::gc_heap::n_heaps;i++) + { + PTR_SVR_gc_heap pHeap = PTR_SVR_gc_heap(SVR::gc_heap::g_heaps[i]); + + DacEnumMemoryRegion(dac_cast<TADDR>(pHeap), sizeof(SVR::gc_heap)); + DacEnumMemoryRegion(dac_cast<TADDR>(pHeap->finalize_queue), sizeof(SVR::CFinalize)); + + // enumerating the generations from max (which is normally gen2) to max+1 gives you + // the segment list for all the normal segements plus the large heap segment (max+1) + // this is the convention in the GC so it is repeated here + for (ULONG i = GCHeap::GetMaxGeneration(); i <= GCHeap::GetMaxGeneration()+1; i++) + { + __DPtr<SVR::heap_segment> seg = dac_cast<TADDR>(pHeap->generation_table[i].start_segment); + while (seg) + { + DacEnumMemoryRegion(PTR_HOST_TO_TADDR(seg), sizeof(SVR::heap_segment)); + + seg = __DPtr<SVR::heap_segment>(dac_cast<TADDR>(seg->next)); + } + } + } +} + +DWORD DacGetNumHeaps() +{ + if (GCHeap::IsServerHeap()) + return (DWORD)SVR::gc_heap::n_heaps; + + // workstation gc + return 1; +} + +HRESULT DacHeapWalker::InitHeapDataSvr(HeapData *&pHeaps, size_t &pCount) +{ + // Scrape basic heap details + int heaps = SVR::gc_heap::n_heaps; + pCount = heaps; + pHeaps = new (nothrow) HeapData[heaps]; + if (pHeaps == NULL) + return E_OUTOFMEMORY; + + for (int i = 0; i < heaps; ++i) + { + // Basic heap info. + PTR_SVR_gc_heap heap = PTR_SVR_gc_heap(SVR::gc_heap::g_heaps[i]); + + pHeaps[i].YoungestGenPtr = (CORDB_ADDRESS)heap->generation_table[0].allocation_context.alloc_ptr; + pHeaps[i].YoungestGenLimit = (CORDB_ADDRESS)heap->generation_table[0].allocation_context.alloc_limit; + + pHeaps[i].Gen0Start = (CORDB_ADDRESS)heap->generation_table[0].allocation_start; + pHeaps[i].Gen0End = (CORDB_ADDRESS)heap->alloc_allocated; + pHeaps[i].Gen1Start = (CORDB_ADDRESS)heap->generation_table[1].allocation_start; + + // Segments + int count = GetSegmentCount(heap->generation_table[NUMBERGENERATIONS-1].start_segment); + count += GetSegmentCount(heap->generation_table[NUMBERGENERATIONS-2].start_segment); + + pHeaps[i].SegmentCount = count; + pHeaps[i].Segments = new (nothrow) SegmentData[count]; + if (pHeaps[i].Segments == NULL) + return E_OUTOFMEMORY; + + // Small object heap segments + SVR::PTR_heap_segment seg = heap->generation_table[NUMBERGENERATIONS-2].start_segment; + int j = 0; + for (; seg && (j < count); ++j) + { + pHeaps[i].Segments[j].Start = (CORDB_ADDRESS)seg->mem; + if (seg.GetAddr() == TO_TADDR(heap->ephemeral_heap_segment)) + { + pHeaps[i].Segments[j].End = (CORDB_ADDRESS)heap->alloc_allocated; + pHeaps[i].EphemeralSegment = j; + pHeaps[i].Segments[j].Generation = 1; + } + else + { + pHeaps[i].Segments[j].End = (CORDB_ADDRESS)seg->allocated; + pHeaps[i].Segments[j].Generation = 2; + } + + seg = seg->next; + } + + + // Large object heap segments + seg = heap->generation_table[NUMBERGENERATIONS-1].start_segment; + for (; seg && (j < count); ++j) + { + pHeaps[i].Segments[j].Generation = 3; + pHeaps[i].Segments[j].Start = (CORDB_ADDRESS)seg->mem; + pHeaps[i].Segments[j].End = (CORDB_ADDRESS)seg->allocated; + + seg = seg->next; + } + } + + return S_OK; +} + +#endif // defined(FEATURE_SVR_GC) diff --git a/src/debug/daccess/stack.cpp b/src/debug/daccess/stack.cpp new file mode 100644 index 0000000000..b235366bb5 --- /dev/null +++ b/src/debug/daccess/stack.cpp @@ -0,0 +1,1434 @@ +// 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: stack.cpp +// + +// +// CLRData stack walking. +// +//***************************************************************************** + +#include "stdafx.h" + +//---------------------------------------------------------------------------- +// +// ClrDataStackWalk. +// +//---------------------------------------------------------------------------- + +ClrDataStackWalk::ClrDataStackWalk(ClrDataAccess* dac, + Thread* thread, + ULONG32 flags) +{ + m_dac = dac; + m_dac->AddRef(); + m_instanceAge = m_dac->m_instanceAge; + m_thread = thread; + m_walkFlags = flags; + m_refs = 1; + m_stackPrev = 0; + + INDEBUG( m_framesUnwound = 0; ) +} + +ClrDataStackWalk::~ClrDataStackWalk(void) +{ + m_dac->Release(); +} + +STDMETHODIMP +ClrDataStackWalk::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataStackWalk))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataStackWalk*>(this)); + return S_OK; + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +ClrDataStackWalk::AddRef(THIS) +{ + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataStackWalk::Release(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataStackWalk::GetContext( + /* [in] */ ULONG32 contextFlags, + /* [in] */ ULONG32 contextBufSize, + /* [out] */ ULONG32 *contextSize, + /* [size_is][out] */ BYTE contextBuf[ ]) +{ + HRESULT status; + + if (contextSize) + { + *contextSize = ContextSizeForFlags(contextFlags); + } + + if (!CheckContextSizeForFlags(contextBufSize, contextFlags)) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_frameIter.IsValid()) + { + status = S_FALSE; + } + else + { + *(PT_CONTEXT)contextBuf = m_context; + UpdateContextFromRegDisp(&m_regDisp, (PT_CONTEXT)contextBuf); + status = S_OK; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataStackWalk::SetContext( + /* [in] */ ULONG32 contextSize, + /* [size_is][in] */ BYTE context[ ]) +{ + return SetContext2(m_frameIter.m_crawl.IsActiveFrame() ? + CLRDATA_STACK_SET_CURRENT_CONTEXT : + CLRDATA_STACK_SET_UNWIND_CONTEXT, + contextSize, context); +} + +HRESULT STDMETHODCALLTYPE +ClrDataStackWalk::SetContext2( + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 contextSize, + /* [size_is][in] */ BYTE context[ ]) +{ + HRESULT status; + + if ((flags & ~(CLRDATA_STACK_SET_CURRENT_CONTEXT | + CLRDATA_STACK_SET_UNWIND_CONTEXT)) != 0 || + !CheckContextSizeForBuffer(contextSize, context)) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // Copy the context to local state so + // that its lifetime extends beyond this call. + m_context = *(PT_CONTEXT)context; + m_thread->FillRegDisplay(&m_regDisp, &m_context); + m_frameIter.ResetRegDisp(&m_regDisp, (flags & CLRDATA_STACK_SET_CURRENT_CONTEXT) != 0); + m_stackPrev = (TADDR)GetRegdisplaySP(&m_regDisp); + FilterFrames(); + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataStackWalk::Next(void) +{ + HRESULT status = E_FAIL; + + INDEBUG( static const int kFrameToReturnForever = 56; ) + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_frameIter.IsValid()) + { + status = S_FALSE; + } + else +#if defined(_DEBUG) + // m_framesUnwound is not incremented unless the special config value is set below in this function. + if (m_framesUnwound < kFrameToReturnForever) +#endif // defined(_DEBUG) + { + // Default the previous stack value. + m_stackPrev = (TADDR)GetRegdisplaySP(&m_regDisp); + StackWalkAction action = m_frameIter.Next(); + switch(action) + { + case SWA_CONTINUE: + // We sucessfully unwound a frame so update + // the previous stack pointer before going into + // filtering to get the amount of stack skipped + // by the filtering. + m_stackPrev = (TADDR)GetRegdisplaySP(&m_regDisp); + FilterFrames(); + status = m_frameIter.IsValid() ? S_OK : S_FALSE; + break; + case SWA_ABORT: + status = S_FALSE; + break; + default: + status = E_FAIL; + break; + } + } + +#if defined(_DEBUG) + // Test hook: when testing on debug builds, we want an easy way to test that the target + // stack behaves as if it's smashed in a particular way. It would be very difficult to create + // a test that carefully broke the stack in a way that would force the stackwalker to report + // success on the same frame forever, and have that corruption be reliable over time. However, it's + // pretty easy for us to control the number of frames on the stack for tests that use this specific + // internal flag. + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DumpGeneration_IntentionallyCorruptDataFromTarget)) + { + if (m_framesUnwound >= kFrameToReturnForever) + { + status = S_OK; + } + else + { + m_framesUnwound++; + } + } +#endif // defined(_DEBUG) + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataStackWalk::GetStackSizeSkipped( + /* [out] */ ULONG64 *stackSizeSkipped) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_stackPrev) + { + *stackSizeSkipped = + (TADDR)GetRegdisplaySP(&m_regDisp) - m_stackPrev; + status = S_OK; + } + else + { + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataStackWalk::GetFrameType( + /* [out] */ CLRDataSimpleFrameType *simpleType, + /* [out] */ CLRDataDetailedFrameType *detailedType) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_frameIter.IsValid()) + { + RawGetFrameType(simpleType, detailedType); + status = S_OK; + } + else + { + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataStackWalk::GetFrame( + /* [out] */ IXCLRDataFrame **frame) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + ClrDataFrame* dataFrame = NULL; + if (!m_frameIter.IsValid()) + { + status = E_INVALIDARG; + goto Exit; + } + + CLRDataSimpleFrameType simpleType; + CLRDataDetailedFrameType detailedType; + + RawGetFrameType(&simpleType, &detailedType); + dataFrame = + new (nothrow) ClrDataFrame(m_dac, simpleType, detailedType, + m_frameIter.m_crawl.GetAppDomain(), + m_frameIter.m_crawl.GetFunction()); + if (!dataFrame) + { + status = E_OUTOFMEMORY; + goto Exit; + } + + dataFrame->m_context = m_context; + UpdateContextFromRegDisp(&m_regDisp, &dataFrame->m_context); + m_thread->FillRegDisplay(&dataFrame->m_regDisp, + &dataFrame->m_context); + + *frame = static_cast<IXCLRDataFrame*>(dataFrame); + status = S_OK; + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataStackWalk::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + switch(reqCode) + { + case CLRDATA_REQUEST_REVISION: + if (inBufferSize != 0 || + inBuffer || + outBufferSize != sizeof(ULONG32)) + { + status = E_INVALIDARG; + } + else + { + *(ULONG32*)outBuffer = 1; + status = S_OK; + } + break; + + case CLRDATA_STACK_WALK_REQUEST_SET_FIRST_FRAME: + // This code should be removed once the Windows debuggers stop using the old DAC API. + if ((inBufferSize != sizeof(ULONG32)) || + (outBufferSize != 0)) + { + status = E_INVALIDARG; + break; + } + + m_frameIter.SetIsFirstFrame(*(ULONG32 UNALIGNED *)inBuffer != 0); + status = S_OK; + break; + + case DACSTACKPRIV_REQUEST_FRAME_DATA: + if ((inBufferSize != 0) || + (inBuffer != NULL) || + (outBufferSize != sizeof(DacpFrameData))) + { + status = E_INVALIDARG; + break; + } + if (!m_frameIter.IsValid()) + { + status = E_INVALIDARG; + break; + } + + DacpFrameData* frameData; + + frameData = (DacpFrameData*)outBuffer; + frameData->frameAddr = + TO_CDADDR(PTR_HOST_TO_TADDR(m_frameIter.m_crawl.GetFrame())); + status = S_OK; + break; + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT +ClrDataStackWalk::Init(void) +{ + if (m_thread->IsUnstarted()) + { + return E_FAIL; + } + + if (m_thread->GetFilterContext()) + { + m_context = *m_thread->GetFilterContext(); + } + else + { + DacGetThreadContext(m_thread, &m_context); + } + m_thread->FillRegDisplay(&m_regDisp, &m_context); + + m_stackPrev = (TADDR)GetRegdisplaySP(&m_regDisp); + + ULONG32 iterFlags = NOTIFY_ON_NO_FRAME_TRANSITIONS; + + // If the filter is only allowing method frames + // turn on the appropriate iterator flag. + if ((m_walkFlags & SIMPFRAME_ALL) == + CLRDATA_SIMPFRAME_MANAGED_METHOD) + { + iterFlags |= FUNCTIONSONLY; + } + + m_frameIter.Init(m_thread, NULL, &m_regDisp, iterFlags); + if (m_frameIter.GetFrameState() == StackFrameIterator::SFITER_UNINITIALIZED) + { + return E_FAIL; + } + FilterFrames(); + + return S_OK; +} + +void +ClrDataStackWalk::FilterFrames(void) +{ + // + // Advance to a state compatible with the + // current filtering flags. + // + + while (m_frameIter.IsValid()) + { + switch(m_frameIter.GetFrameState()) + { + case StackFrameIterator::SFITER_FRAMELESS_METHOD: + if (m_walkFlags & CLRDATA_SIMPFRAME_MANAGED_METHOD) + { + return; + } + break; + case StackFrameIterator::SFITER_FRAME_FUNCTION: + case StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION: + case StackFrameIterator::SFITER_NO_FRAME_TRANSITION: + if (m_walkFlags & CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE) + { + return; + } + break; + default: + break; + } + + m_frameIter.Next(); + } +} + +void +ClrDataStackWalk::RawGetFrameType( + /* [out] */ CLRDataSimpleFrameType* simpleType, + /* [out] */ CLRDataDetailedFrameType* detailedType) +{ + if (simpleType) + { + switch(m_frameIter.GetFrameState()) + { + case StackFrameIterator::SFITER_FRAMELESS_METHOD: + *simpleType = CLRDATA_SIMPFRAME_MANAGED_METHOD; + break; + case StackFrameIterator::SFITER_FRAME_FUNCTION: + case StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION: + *simpleType = CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE; + break; + default: + *simpleType = CLRDATA_SIMPFRAME_UNRECOGNIZED; + break; + } + } + + if (detailedType) + { + if (m_frameIter.m_crawl.GetFrame() && m_frameIter.m_crawl.GetFrame()->GetFrameAttribs() & Frame::FRAME_ATTR_EXCEPTION) + *detailedType = CLRDATA_DETFRAME_EXCEPTION_FILTER; + else + *detailedType = CLRDATA_DETFRAME_UNRECOGNIZED; + } +} + +//---------------------------------------------------------------------------- +// +// ClrDataFrame. +// +//---------------------------------------------------------------------------- + +ClrDataFrame::ClrDataFrame(ClrDataAccess* dac, + CLRDataSimpleFrameType simpleType, + CLRDataDetailedFrameType detailedType, + AppDomain* appDomain, + MethodDesc* methodDesc) +{ + m_dac = dac; + m_dac->AddRef(); + m_instanceAge = m_dac->m_instanceAge; + m_simpleType = simpleType; + m_detailedType = detailedType; + m_appDomain = appDomain; + m_methodDesc = methodDesc; + m_refs = 1; + m_methodSig = NULL; + m_localSig = NULL; +} + +ClrDataFrame::~ClrDataFrame(void) +{ + delete m_methodSig; + delete m_localSig; + m_dac->Release(); +} + +STDMETHODIMP +ClrDataFrame::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataFrame))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataFrame*>(this)); + return S_OK; + } + else if (IsEqualIID(interfaceId, __uuidof(IXCLRDataFrame2))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataFrame2*>(this)); + return S_OK; + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +ClrDataFrame::AddRef(THIS) +{ + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataFrame::Release(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::GetContext( + /* [in] */ ULONG32 contextFlags, + /* [in] */ ULONG32 contextBufSize, + /* [out] */ ULONG32 *contextSize, + /* [size_is][out] */ BYTE contextBuf[ ]) +{ + HRESULT status; + + if (contextSize) + { + *contextSize = ContextSizeForFlags(contextFlags); + } + + if (!CheckContextSizeForFlags(contextBufSize, contextFlags)) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *(PT_CONTEXT)contextBuf = m_context; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::GetFrameType( + /* [out] */ CLRDataSimpleFrameType *simpleType, + /* [out] */ CLRDataDetailedFrameType *detailedType) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *simpleType = m_simpleType; + *detailedType = m_detailedType; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::GetAppDomain( + /* [out] */ IXCLRDataAppDomain **appDomain) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_appDomain) + { + ClrDataAppDomain* dataAppDomain = + new (nothrow) ClrDataAppDomain(m_dac, m_appDomain); + if (!dataAppDomain) + { + status = E_OUTOFMEMORY; + } + else + { + *appDomain = static_cast<IXCLRDataAppDomain*>(dataAppDomain); + status = S_OK; + } + } + else + { + *appDomain = NULL; + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::GetNumArguments( + /* [out] */ ULONG32 *numArgs) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_methodDesc) + { + status = E_NOINTERFACE; + } + else + { + MetaSig* sig; + + status = GetMethodSig(&sig, numArgs); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::GetArgumentByIndex( + /* [in] */ ULONG32 index, + /* [out] */ IXCLRDataValue **arg, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part(bufLen, *nameLen) WCHAR name[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (nameLen) + { + *nameLen = 0; + } + + if (!m_methodDesc) + { + status = E_NOINTERFACE; + goto Exit; + } + + MetaSig* sig; + ULONG32 numArgs; + + if (FAILED(status = GetMethodSig(&sig, &numArgs))) + { + goto Exit; + } + + if (index >= numArgs) + { + status = E_INVALIDARG; + goto Exit; + } + + if ((bufLen && name) || nameLen) + { + if (index == 0 && sig->HasThis()) + { + if (nameLen) + { + *nameLen = 5; + } + + StringCchCopy(name, bufLen, W("this")); + } + else + { + if (!m_methodDesc->IsNoMetadata()) + { + IMDInternalImport* mdImport = m_methodDesc->GetMDImport(); + mdParamDef paramToken; + LPCSTR paramName; + USHORT seq; + DWORD attr; + + // Param indexing is 1-based. + ULONG32 mdIndex = index + 1; + + // 'this' doesn't show up in the signature but + // is present in the dac API indexing so adjust the + // index down for methods with 'this'. + if (sig->HasThis()) + { + mdIndex--; + } + + status = mdImport->FindParamOfMethod( + m_methodDesc->GetMemberDef(), + mdIndex, + ¶mToken); + if (status == S_OK) + { + status = mdImport->GetParamDefProps( + paramToken, + &seq, + &attr, + ¶mName); + if ((status == S_OK) && (paramName != NULL)) + { + if ((status = ConvertUtf8(paramName, + bufLen, nameLen, name)) != S_OK) + { + goto Exit; + } + } + } + } + else + { + if (nameLen) + { + *nameLen = 1; + } + + name[0] = 0; + } + } + } + + status = ValueFromDebugInfo(sig, true, index, index, arg); + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::GetNumLocalVariables( + /* [out] */ ULONG32 *numLocals) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_methodDesc) + { + status = E_NOINTERFACE; + } + else + { + MetaSig* sig; + + status = GetLocalSig(&sig, numLocals); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::GetLocalVariableByIndex( + /* [in] */ ULONG32 index, + /* [out] */ IXCLRDataValue **localVariable, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part(bufLen, *nameLen) WCHAR name[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_methodDesc) + { + status = E_NOINTERFACE; + goto Exit; + } + + MetaSig* sig; + ULONG32 numLocals; + + if (FAILED(status = GetLocalSig(&sig, &numLocals))) + { + goto Exit; + } + + if (index >= numLocals) + { + status = E_INVALIDARG; + goto Exit; + } + + MetaSig* argSig; + ULONG32 numArgs; + + if (FAILED(status = GetMethodSig(&argSig, &numArgs))) + { + goto Exit; + } + + // Can't get names for locals in the Whidbey runtime. + if (bufLen && name) + { + if (nameLen) + { + *nameLen = 1; + } + + name[0] = 0; + } + + // The locals are indexed immediately following the arguments + // in the NativeVarInfos. + status = ValueFromDebugInfo(sig, false, index, index + numArgs, + localVariable); + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::GetNumTypeArguments( + /* [out] */ ULONG32 *numTypeArgs) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::GetTypeArgumentByIndex( + /* [in] */ ULONG32 index, + /* [out] */ IXCLRDataTypeInstance **typeArg) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::GetExactGenericArgsToken( + /* [out] */ IXCLRDataValue ** genericToken) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_methodDesc) + { + status = E_NOINTERFACE; + goto Exit; + } + + MetaSig* sig; + ULONG32 numLocals; + + if (FAILED(status = GetLocalSig(&sig, &numLocals))) + { + goto Exit; + } + + // The locals are indexed immediately following the arguments + // in the NativeVarInfos. + status = ValueFromDebugInfo(sig, false, 1, (DWORD)ICorDebugInfo::TYPECTXT_ILNUM, + genericToken); + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::GetCodeName( + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *symbolLen, + /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR symbolBuf[ ]) +{ + HRESULT status = E_FAIL; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + TADDR pcAddr = PCODEToPINSTR(GetControlPC(&m_regDisp)); + status = m_dac-> + RawGetMethodName(TO_CDADDR(pcAddr), flags, + bufLen, symbolLen, symbolBuf, + NULL); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::GetMethodInstance( + /* [out] */ IXCLRDataMethodInstance **method) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_methodDesc) + { + status = E_NOINTERFACE; + } + else + { + ClrDataMethodInstance* dataMethod = + new (nothrow) ClrDataMethodInstance(m_dac, + m_appDomain, + m_methodDesc); + *method = static_cast<IXCLRDataMethodInstance*>(dataMethod); + status = dataMethod ? S_OK : E_OUTOFMEMORY; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataFrame::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + switch(reqCode) + { + case CLRDATA_REQUEST_REVISION: + if (inBufferSize != 0 || + inBuffer || + outBufferSize != sizeof(ULONG32)) + { + status = E_INVALIDARG; + } + else + { + *(ULONG32*)outBuffer = 1; + status = S_OK; + } + break; + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT +ClrDataFrame::GetMethodSig(MetaSig** sig, + ULONG32* count) +{ + if (!m_methodSig) + { + m_methodSig = new (nothrow) MetaSig(m_methodDesc); + if (!m_methodSig) + { + return E_OUTOFMEMORY; + } + } + + *sig = m_methodSig; + *count = m_methodSig->NumFixedArgs() + + (m_methodSig->HasThis() ? 1 : 0); + return *count ? S_OK : S_FALSE; +} + +HRESULT +ClrDataFrame::GetLocalSig(MetaSig** sig, + ULONG32* count) +{ + HRESULT hr; + if (!m_localSig) + { + // It turns out we cannot really get rid of this check. Dynamic methods + // (including IL stubs) do not have their local sig's available after JIT time. + if (!m_methodDesc->IsIL()) + { + *sig = NULL; + *count = 0; + return S_FALSE; + } + + COR_ILMETHOD_DECODER methodDecoder(m_methodDesc->GetILHeader()); + mdSignature localSig = methodDecoder.GetLocalVarSigTok() ? + methodDecoder.GetLocalVarSigTok() : mdSignatureNil; + if (localSig == mdSignatureNil) + { + *sig = NULL; + *count = 0; + return S_FALSE; + } + + ULONG tokenSigLen; + PCCOR_SIGNATURE tokenSig; + IfFailRet(m_methodDesc->GetModule()->GetMDImport()->GetSigFromToken( + localSig, + &tokenSigLen, + &tokenSig)); + + SigTypeContext typeContext(m_methodDesc, TypeHandle()); + m_localSig = new (nothrow) + MetaSig(tokenSig, + tokenSigLen, + m_methodDesc->GetModule(), + &typeContext, + MetaSig::sigLocalVars); + if (!m_localSig) + { + return E_OUTOFMEMORY; + } + } + + *sig = m_localSig; + *count = m_localSig->NumFixedArgs(); + return S_OK; +} + +HRESULT +ClrDataFrame::ValueFromDebugInfo(MetaSig* sig, + bool isArg, + DWORD sigIndex, + DWORD varInfoSlot, + IXCLRDataValue** _value) +{ + HRESULT status; + ULONG32 numVarInfo; + NewHolder<ICorDebugInfo::NativeVarInfo> varInfo(NULL); + ULONG32 codeOffset; + ULONG32 valueFlags; + ULONG32 i; + + TADDR ip = PCODEToPINSTR(GetControlPC(&m_regDisp)); + if ((status = m_dac->GetMethodVarInfo(m_methodDesc, + ip, + &numVarInfo, + &varInfo, + &codeOffset)) != S_OK) + { + // We have signature info indicating that there + // are values, but couldn't find any location info. + // Optimized routines may have eliminated all + // traditional variable locations, so just treat + // this as a no-location case just like not being + // able to find a matching lifetime. + numVarInfo = 0; + } + + for (i = 0; i < numVarInfo; i++) + { + if (varInfo[i].startOffset <= codeOffset && + varInfo[i].endOffset >= codeOffset && + varInfo[i].varNumber == varInfoSlot && + varInfo[i].loc.vlType != ICorDebugInfo::VLT_INVALID) + { + break; + } + } + + ULONG64 baseAddr; + NativeVarLocation locs[MAX_NATIVE_VAR_LOCS]; + ULONG32 numLocs; + + if (i >= numVarInfo) + { + numLocs = 0; + } + else + { + numLocs = NativeVarLocations(varInfo[i].loc, &m_context, + NumItems(locs), locs); + } + + if (numLocs == 1 && !locs[0].contextReg) + { + baseAddr = TO_CDADDR(locs[0].addr); + } + else + { + baseAddr = 0; + } + + TypeHandle argType; + + sig->Reset(); + if (isArg && sigIndex == 0 && sig->HasThis()) + { + argType = TypeHandle(m_methodDesc->GetMethodTable()); + valueFlags = CLRDATA_VALUE_IS_REFERENCE; + } + else + { + // 'this' doesn't show up in the signature but + // is present in the indexing so adjust the + // index down for methods with 'this'. + if (isArg && sig->HasThis()) + { + + sigIndex--; + } + + do + { + sig->NextArg(); + } + while (sigIndex-- > 0); + + // == FailIfNotLoaded + // Will also return null if type is not restored + argType = sig->GetLastTypeHandleThrowing(ClassLoader::DontLoadTypes); + if (argType.IsNull()) + { + // XXX Microsoft - Sometimes types can't be looked + // up and this at least allows the value to be used, + // but is it the right behavior? + argType = TypeHandle(MscorlibBinder::GetElementType(ELEMENT_TYPE_U8)); + valueFlags = 0; + } + else + { + valueFlags = GetTypeFieldValueFlags(argType, NULL, 0, false); + + // If this is a primitive variable and the actual size is smaller than what we have been told, + // then lower the size so that we won't read in trash memory (e.g. reading 4 bytes for a short). + if ((valueFlags & CLRDATA_VALUE_IS_PRIMITIVE) != 0) + { + if (numLocs == 1) + { + UINT actualSize = argType.GetSize(); + if (actualSize < locs[0].size) + { + locs[0].size = actualSize; + } + } + } + } + } + + ClrDataValue* value = new (nothrow) + ClrDataValue(m_dac, + m_appDomain, + NULL, + valueFlags, + argType, + baseAddr, + numLocs, + locs); + if (!value) + { + return E_OUTOFMEMORY; + } + + *_value = value; + return S_OK; +} diff --git a/src/debug/daccess/stdafx.cpp b/src/debug/daccess/stdafx.cpp new file mode 100644 index 0000000000..f508973779 --- /dev/null +++ b/src/debug/daccess/stdafx.cpp @@ -0,0 +1,12 @@ +// 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: stdafx.cpp +// + +// +// Host for precompiled headers. +// +//***************************************************************************** +#include "stdafx.h" // Precompiled header key. diff --git a/src/debug/daccess/stdafx.h b/src/debug/daccess/stdafx.h new file mode 100644 index 0000000000..5c2d37688b --- /dev/null +++ b/src/debug/daccess/stdafx.h @@ -0,0 +1,111 @@ +// 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: stdafx.h +// + +// +//***************************************************************************** +/* XXX Fri 10/14/2005 + * prevent winioctl from defining something called "Unknown" + */ +#define _WINIOCTL_ + +// Define ALLOW_VMPTR_ACCESS to grant DAC access to VMPTR +#define ALLOW_VMPTR_ACCESS + +// Prevent the inclusion of Random.h from disabling rand(). rand() is used by some other headers we include +// and there's no reason why DAC should be forbidden from using it. +#define DO_NOT_DISABLE_RAND + +#define USE_COM_CONTEXT_DEF + +#include <stdint.h> +#include <windows.h> + +#include <winwrap.h> + +#include <dbghelp.h> + +#include <wchar.h> +#include <stdio.h> + +#include <dbgtargetcontext.h> + +#include <cor.h> +#include <dacprivate.h> +#include <sospriv.h> + +#include <common.h> +#include <codeman.h> +#include <debugger.h> +#include <controller.h> +#include <eedbginterfaceimpl.h> +#include <methoditer.h> + +#include <xcordebug.h> +#include "dacimpl.h" + +#if defined(FEATURE_APPX_BINDER) +#include <clrprivbinderappx.h> +#endif // defined(FEATURE_APPX) + +#define STRSAFE_NO_DEPRECATE +#include <strsafe.h> +#undef _ftcscat +#undef _ftcscpy + +// from ntstatus.h +#define STATUS_STOWED_EXCEPTION ((NTSTATUS)0xC000027BL) + +// unpublished Windows structures. these will be published soon in a new header. +// copying here for now and we'll use the windows header when it's available. +// this is tracked with issue 824225 +#ifndef _STOWED_EXCEPTION_TEMP_DEFINITION +#define _STOWED_EXCEPTION_TEMP_DEFINITION + +typedef struct _STOWED_EXCEPTION_INFORMATION_HEADER { + ULONG Size; + ULONG Signature; +} STOWED_EXCEPTION_INFORMATION_HEADER, *PSTOWED_EXCEPTION_INFORMATION_HEADER; + +typedef struct _STOWED_EXCEPTION_INFORMATION_V2 { + STOWED_EXCEPTION_INFORMATION_HEADER Header; + + HRESULT ResultCode; + + struct { + DWORD ExceptionForm : 2; + DWORD ThreadId : 30; + }; + + union { + struct { + PVOID ExceptionAddress; + + ULONG StackTraceWordSize; // sizeof (PVOID) + ULONG StackTraceWords; // number of words pointed to by StackTrace + PVOID StackTrace; // StackTrace buffer + }; + struct { + PWSTR ErrorText; + }; + }; + + ULONG NestedExceptionType; + PVOID NestedException; // opaque exception addendum +} STOWED_EXCEPTION_INFORMATION_V2, *PSTOWED_EXCEPTION_INFORMATION_V2; + +// +// Nested exception: type definition macro (byte swap). Assumes little-endian. +// +#define STOWED_EXCEPTION_NESTED_TYPE(t) ((((((ULONG)(t)) >> 24) & 0xFF) << 0) | \ + (((((ULONG)(t)) >> 16) & 0xFF) << 8) | \ + (((((ULONG)(t)) >> 8) & 0xFF) << 16) | \ + (((((ULONG)(t)) >> 0) & 0xFF) << 24)) + +#define STOWED_EXCEPTION_INFORMATION_V2_SIGNATURE 'SE02' +#define STOWED_EXCEPTION_NESTED_TYPE_LEO STOWED_EXCEPTION_NESTED_TYPE('LEO1') // language exception object + +#endif // _STOWED_EXCEPTION_TEMP_DEFINITION diff --git a/src/debug/daccess/task.cpp b/src/debug/daccess/task.cpp new file mode 100644 index 0000000000..601ad401af --- /dev/null +++ b/src/debug/daccess/task.cpp @@ -0,0 +1,5335 @@ +// 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: task.cpp +// + +// +// ClrDataTask. +// +//***************************************************************************** + +#include "stdafx.h" + +// XXX Microsoft - Why aren't these extra MD APIs in a header? +STDAPI GetMDPublicInterfaceFromInternal( + void *pIUnkPublic, // [IN] Given scope. + REFIID riid, // [in] The interface desired. + void **ppIUnkInternal); // [out] Return interface on success. + +STDAPI GetMetaDataPublicInterfaceFromInternal( + void *pv, // [IN] Given interface. + REFIID riid, // [IN] desired interface. + void **ppv) // [OUT] returned interface +{ + return GetMDPublicInterfaceFromInternal(pv, riid, ppv); +} + +//---------------------------------------------------------------------------- +// +// ClrDataTask. +// +//---------------------------------------------------------------------------- + +ClrDataTask::ClrDataTask(ClrDataAccess* dac, + Thread* thread) +{ + m_dac = dac; + m_dac->AddRef(); + m_instanceAge = m_dac->m_instanceAge; + m_thread = thread; + m_refs = 1; +} + +ClrDataTask::~ClrDataTask(void) +{ + m_dac->Release(); +} + +STDMETHODIMP +ClrDataTask::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataTask))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataTask*>(this)); + return S_OK; + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +ClrDataTask::AddRef(THIS) +{ + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataTask::Release(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::GetProcess( + /* [out] */ IXCLRDataProcess **process) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *process = static_cast<IXCLRDataProcess*>(m_dac); + m_dac->AddRef(); + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::GetCurrentAppDomain( + /* [out] */ IXCLRDataAppDomain **appDomain) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_thread->GetDomain()) + { + *appDomain = new (nothrow) + ClrDataAppDomain(m_dac, m_thread->GetDomain()); + status = *appDomain ? S_OK : E_OUTOFMEMORY; + } + else + { + status = E_INVALIDARG; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::GetName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX - Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::GetUniqueID( + /* [out] */ ULONG64 *id) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *id = m_thread->GetThreadId(); + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::GetFlags( + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft - GC check. + *flags = CLRDATA_TASK_DEFAULT; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::IsSameObject( + /* [in] */ IXCLRDataTask* task) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = PTR_HOST_TO_TADDR(m_thread) == + PTR_HOST_TO_TADDR(((ClrDataTask*)task)->m_thread) ? + S_OK : S_FALSE; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::GetManagedObject( + /* [out] */ IXCLRDataValue **value) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::GetDesiredExecutionState( + /* [out] */ ULONG32 *state) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::SetDesiredExecutionState( + /* [in] */ ULONG32 state) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::CreateStackWalk( + /* [in] */ ULONG32 flags, + /* [out] */ IXCLRDataStackWalk **stackWalk) +{ + HRESULT status; + + if (flags & ~SIMPFRAME_ALL) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + ClrDataStackWalk* walkClass = NULL; + + EX_TRY + { + walkClass = new (nothrow) ClrDataStackWalk(m_dac, m_thread, flags); + + if (!walkClass) + { + status = E_OUTOFMEMORY; + } + else if ((status = walkClass->Init()) != S_OK) + { + delete walkClass; + } + else + { + *stackWalk = static_cast<IXCLRDataStackWalk*>(walkClass); + } + } + EX_CATCH + { + if (walkClass) + { + delete walkClass; + } + + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::GetOSThreadID( + /* [out] */ ULONG32 *id) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_thread->GetOSThreadId() && + m_thread->GetOSThreadId() != 0xbaadf00d) + { + *id = m_thread->GetOSThreadId(); + status = S_OK; + } + else + { + *id = 0; + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::GetContext( + /* [in] */ ULONG32 contextFlags, + /* [in] */ ULONG32 contextBufSize, + /* [out] */ ULONG32 *contextSize, + /* [size_is][out] */ BYTE contextBuf[ ]) +{ + HRESULT status; + + if (contextSize) + { + *contextSize = ContextSizeForFlags(contextFlags); + } + + if (!CheckContextSizeForFlags(contextBufSize, contextFlags)) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_thread->GetOSThreadId()) + { + status = m_dac->m_pTarget-> + GetThreadContext(m_thread->GetOSThreadId(), + contextFlags, + contextBufSize, + contextBuf); + } + else + { + status = E_INVALIDARG; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::SetContext( + /* [in] */ ULONG32 contextSize, + /* [size_is][in] */ BYTE context[ ]) +{ + HRESULT status; + + if (!CheckContextSizeForBuffer(contextSize, context)) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_thread->GetOSThreadId()) + { + status = m_dac->m_pMutableTarget-> + SetThreadContext(m_thread->GetOSThreadId(), + contextSize, + context); + } + else + { + status = E_INVALIDARG; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::GetCurrentExceptionState( + /* [out] */ IXCLRDataExceptionState **exception) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = ClrDataExceptionState::NewFromThread(m_dac, + m_thread, + NULL, + exception); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::GetLastExceptionState( + /* [out] */ IXCLRDataExceptionState **exception) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_thread->m_LastThrownObjectHandle) + { + *exception = new (nothrow) + ClrDataExceptionState(m_dac, + m_thread->GetDomain(), + m_thread, + CLRDATA_EXCEPTION_PARTIAL, + NULL, + m_thread->m_LastThrownObjectHandle, + NULL); + status = *exception ? S_OK : E_OUTOFMEMORY; + } + else + { + status = E_NOINTERFACE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataTask::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + switch(reqCode) + { + case CLRDATA_REQUEST_REVISION: + if (inBufferSize != 0 || + inBuffer || + outBufferSize != sizeof(ULONG32)) + { + status = E_INVALIDARG; + } + else + { + *(ULONG32*)outBuffer = 3; + status = S_OK; + } + break; + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +//---------------------------------------------------------------------------- +// +// ClrDataAppDomain. +// +//---------------------------------------------------------------------------- + +ClrDataAppDomain::ClrDataAppDomain(ClrDataAccess* dac, + AppDomain* appDomain) +{ + m_dac = dac; + m_dac->AddRef(); + m_instanceAge = m_dac->m_instanceAge; + m_appDomain = appDomain; + m_refs = 1; +} + +ClrDataAppDomain::~ClrDataAppDomain(void) +{ + m_dac->Release(); +} + +STDMETHODIMP +ClrDataAppDomain::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataAppDomain))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataAppDomain*>(this)); + return S_OK; + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +ClrDataAppDomain::AddRef(THIS) +{ + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataAppDomain::Release(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAppDomain::GetProcess( + /* [out] */ IXCLRDataProcess **process) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *process = static_cast<IXCLRDataProcess*>(m_dac); + m_dac->AddRef(); + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAppDomain::GetName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]) +{ + HRESULT status = S_OK; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + bool isUtf8; + PVOID rawName = m_appDomain->GetFriendlyNameNoSet(&isUtf8); + if (rawName) + { + if (isUtf8) + { + status = ConvertUtf8((LPCUTF8)rawName, + bufLen, nameLen, name); + } + else + { + status = StringCchCopy(name, bufLen, (PCWSTR)rawName) == S_OK ? + S_OK : S_FALSE; + if (nameLen) + { + size_t cchName = wcslen((PCWSTR)rawName) + 1; + if (FitsIn<ULONG32>(cchName)) + { + *nameLen = (ULONG32) cchName; + } + else + { + status = COR_E_OVERFLOW; + } + } + } + } + else + { + status = E_NOINTERFACE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAppDomain::GetFlags( + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *flags = CLRDATA_DOMAIN_DEFAULT; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAppDomain::IsSameObject( + /* [in] */ IXCLRDataAppDomain* appDomain) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = PTR_HOST_TO_TADDR(m_appDomain) == + PTR_HOST_TO_TADDR(((ClrDataAppDomain*)appDomain)->m_appDomain) ? + S_OK : S_FALSE; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAppDomain::GetManagedObject( + /* [out] */ IXCLRDataValue **value) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAppDomain::GetUniqueID( + /* [out] */ ULONG64 *id) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *id = m_appDomain->GetId().m_dwId; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAppDomain::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = E_INVALIDARG; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +//---------------------------------------------------------------------------- +// +// ClrDataAssembly. +// +//---------------------------------------------------------------------------- + +ClrDataAssembly::ClrDataAssembly(ClrDataAccess* dac, + Assembly* assembly) +{ + m_dac = dac; + m_dac->AddRef(); + m_instanceAge = m_dac->m_instanceAge; + m_refs = 1; + m_assembly = assembly; +} + +ClrDataAssembly::~ClrDataAssembly(void) +{ + m_dac->Release(); +} + +STDMETHODIMP +ClrDataAssembly::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataAssembly))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataAssembly*>(this)); + return S_OK; + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +ClrDataAssembly::AddRef(THIS) +{ + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataAssembly::Release(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAssembly::StartEnumModules( + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + Assembly::ModuleIterator* iter = new (nothrow) + Assembly::ModuleIterator; + if (iter) + { + *iter = m_assembly->IterateModules(); + *handle = TO_CDENUM(iter); + status = S_OK; + } + else + { + status = E_OUTOFMEMORY; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAssembly::EnumModule( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataModule **mod) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + Assembly::ModuleIterator* iter = + FROM_CDENUM(Assembly::ModuleIterator, *handle); + if (iter->Next()) + { + *mod = new (nothrow) + ClrDataModule(m_dac, iter->GetModule()); + status = *mod ? S_OK : E_OUTOFMEMORY; + } + else + { + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAssembly::EndEnumModules( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + Assembly::ModuleIterator* iter = + FROM_CDENUM(Assembly::ModuleIterator, handle); + delete iter; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAssembly::StartEnumAppDomains( + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAssembly::EnumAppDomain( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataAppDomain **appDomain) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAssembly::EndEnumAppDomains( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAssembly::GetName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = ConvertUtf8(m_assembly->GetSimpleName(), + bufLen, nameLen, name); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAssembly::GetFileName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + COUNT_T _nameLen; + + if (m_assembly->GetManifestFile()->GetPath(). + DacGetUnicode(bufLen, name, &_nameLen)) + { + if (nameLen) + { + *nameLen = _nameLen; + } + status = S_OK; + } + else + { + status = E_FAIL; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAssembly::GetDisplayName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAssembly::GetFlags( + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *flags = CLRDATA_ASSEMBLY_DEFAULT; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAssembly::IsSameObject( + /* [in] */ IXCLRDataAssembly* assembly) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = (PTR_HOST_TO_TADDR(m_assembly) == + PTR_HOST_TO_TADDR(((ClrDataAssembly*)assembly)-> + m_assembly)) ? + S_OK : S_FALSE; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataAssembly::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + switch(reqCode) + { + case CLRDATA_REQUEST_REVISION: + if (inBufferSize != 0 || + inBuffer || + outBufferSize != sizeof(ULONG32)) + { + status = E_INVALIDARG; + } + else + { + *(ULONG32*)outBuffer = 2; + status = S_OK; + } + break; + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +//---------------------------------------------------------------------------- +// +// ClrDataModule. +// +//---------------------------------------------------------------------------- + +ClrDataModule::ClrDataModule(ClrDataAccess* dac, + Module* module) +{ + m_dac = dac; + m_dac->AddRef(); + m_instanceAge = m_dac->m_instanceAge; + m_refs = 1; + m_module = module; + m_mdImport = NULL; + m_setExtents = false; +} + +ClrDataModule::~ClrDataModule(void) +{ + m_dac->Release(); + if (m_mdImport) + { + m_mdImport->Release(); + } +} + +STDMETHODIMP +ClrDataModule::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + _ASSERTE(iface != NULL); + + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataModule))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataModule*>(this)); + return S_OK; + } + else if (IsEqualIID(interfaceId, __uuidof(IXCLRDataModule2))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataModule2*>(this)); + return S_OK; + } + else if (IsEqualIID(interfaceId, IID_IMetaDataImport)) + { + return GetMdInterface(iface); + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +ClrDataModule::AddRef(THIS) +{ + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataModule::Release(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::StartEnumAssemblies( + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + ProcessModIter* iter = new (nothrow) ProcessModIter; + if (iter) + { + *handle = TO_CDENUM(iter); + status = S_OK; + } + else + { + status = E_OUTOFMEMORY; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EnumAssembly( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataAssembly **assembly) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + ProcessModIter* iter = FROM_CDENUM(ProcessModIter, *handle); + Module* module; + + // + // Iterate over all of the modules in the process. + // When this module is found, return the containing + // assembly. + // Is there a more direct way? + // + + for (;;) + { + if (!(module = iter->NextModule())) + { + status = S_FALSE; + break; + } + + if (PTR_HOST_TO_TADDR(module) == PTR_HOST_TO_TADDR(m_module)) + { + *assembly = new (nothrow) + ClrDataAssembly(m_dac, iter->m_curAssem); + status = *assembly ? S_OK : E_OUTOFMEMORY; + break; + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EndEnumAssemblies( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + ProcessModIter* iter = FROM_CDENUM(ProcessModIter, handle); + delete iter; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::StartEnumAppDomains( + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EnumAppDomain( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataAppDomain **appDomain) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EndEnumAppDomains( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::StartEnumTypeDefinitions( + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = MetaEnum::New(m_module, + mdtTypeDef, + 0, + NULL, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EnumTypeDefinition( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataTypeDefinition **typeDefinition) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + mdTypeDef token; + + if ((status = MetaEnum::CdNextToken(handle, &token)) == S_OK) + { + status = ClrDataTypeDefinition:: + NewFromModule(m_dac, + m_module, + token, + NULL, + typeDefinition); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EndEnumTypeDefinitions( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = MetaEnum::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::StartEnumTypeInstances( + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = MetaEnum::New(m_module, + mdtTypeDef, + 0, + appDomain, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EnumTypeInstance( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataTypeInstance **typeInstance) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + for (;;) + { + AppDomain* appDomain; + mdTypeDef token; + + if ((status = MetaEnum:: + CdNextDomainToken(handle, &appDomain, &token)) != S_OK) + { + break; + } + + // If the type hasn't been used there won't be anything + // loaded. It's not an instance, then, just keep going. + if ((status = ClrDataTypeInstance:: + NewFromModule(m_dac, + appDomain, + m_module, + token, + NULL, + typeInstance)) != E_INVALIDARG) + { + break; + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EndEnumTypeInstances( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = MetaEnum::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::StartEnumTypeDefinitionsByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdStartType(name, + flags, + m_module, + NULL, + NULL, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EnumTypeDefinitionByName( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataTypeDefinition **type) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + mdTypeDef token; + + if ((status = SplitName::CdNextType(handle, &token)) == S_OK) + { + status = ClrDataTypeDefinition:: + NewFromModule(m_dac, + m_module, + token, + NULL, + type); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EndEnumTypeDefinitionsByName( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::StartEnumTypeInstancesByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataAppDomain *appDomain, + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdStartType(name, + flags, + m_module, + NULL, + appDomain, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EnumTypeInstanceByName( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataTypeInstance **type) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + SplitName* split; split = FROM_CDENUM(SplitName, *handle); + + for (;;) + { + AppDomain* appDomain; + mdTypeDef token; + + if ((status = SplitName:: + CdNextDomainType(handle, &appDomain, &token)) != S_OK) + { + break; + } + + // If the type hasn't been used there won't be anything + // loaded. It's not an instance, then, just keep going. + if ((status = ClrDataTypeInstance:: + NewFromModule(m_dac, + appDomain, + m_module, + token, + NULL, + type)) != E_INVALIDARG) + { + break; + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EndEnumTypeInstancesByName( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::GetTypeDefinitionByToken( + /* [in] */ mdTypeDef token, + /* [out] */ IXCLRDataTypeDefinition **typeDefinition) +{ + HRESULT status; + + // This isn't critically necessary but it prevents + // an assert in the metadata code. + if (TypeFromToken(token) != mdtTypeDef) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = ClrDataTypeDefinition:: + NewFromModule(m_dac, + m_module, + token, + NULL, + typeDefinition); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::StartEnumMethodDefinitionsByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdStartMethod(name, + flags, + m_module, + mdTypeDefNil, + NULL, + NULL, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EnumMethodDefinitionByName( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodDefinition **method) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + mdMethodDef token; + + if ((status = SplitName::CdNextMethod(handle, &token)) == S_OK) + { + status = ClrDataMethodDefinition:: + NewFromModule(m_dac, + m_module, + token, + NULL, + method); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EndEnumMethodDefinitionsByName( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::StartEnumMethodInstancesByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdStartMethod(name, + flags, + m_module, + mdTypeDefNil, + NULL, + appDomain, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EnumMethodInstanceByName( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataMethodInstance **method) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + SplitName* split; split = FROM_CDENUM(SplitName, *handle); + + for (;;) + { + AppDomain* appDomain; + mdMethodDef token; + + if ((status = SplitName:: + CdNextDomainMethod(handle, &appDomain, &token)) != S_OK) + { + break; + } + + // If the method doesn't have a MethodDesc or hasn't + // been JIT'ed yet it's not an instance and should + // just be skipped. + if ((status = ClrDataMethodInstance:: + NewFromModule(m_dac, + appDomain, + m_module, + token, + NULL, + method)) != E_INVALIDARG) + { + break; + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EndEnumMethodInstancesByName( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::GetMethodDefinitionByToken( + /* [in] */ mdMethodDef token, + /* [out] */ IXCLRDataMethodDefinition **methodDefinition) +{ + HRESULT status; + + // This isn't critically necessary but it prevents + // an assert in the metadata code. + if (TypeFromToken(token) != mdtMethodDef) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = ClrDataMethodDefinition:: + NewFromModule(m_dac, + m_module, + token, + NULL, + methodDefinition); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::StartEnumDataByName( + /* [in] */ LPCWSTR name, + /* [in] */ ULONG32 flags, + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [in] */ IXCLRDataTask* tlsTask, + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdStartField(name, + flags, + INH_STATIC, + NULL, + TypeHandle(), + m_module, + mdTypeDefNil, + 0, + NULL, + tlsTask, + NULL, + appDomain, + NULL, + handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EnumDataByName( + /* [out][in] */ CLRDATA_ENUM* handle, + /* [out] */ IXCLRDataValue **value) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdNextDomainField(m_dac, handle, value); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EndEnumDataByName( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = SplitName::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::GetName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = ConvertUtf8(m_module->GetSimpleName(), + bufLen, nameLen, name); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::GetFileName( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part(bufLen, *nameLen) WCHAR name[ ]) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + COUNT_T _nameLen; + + // Try to get the file name through GetPath. + // If the returned name is empty, then try to get the guessed module file name. + // The guessed file name is propogated from metadata's module name. + // + if ((m_module->GetFile()->GetPath().DacGetUnicode(bufLen, name, &_nameLen) && name[0])|| + (m_module->GetFile()->GetModuleFileNameHint().DacGetUnicode(bufLen, name, &_nameLen) && name[0])) + { + if (nameLen) + { + *nameLen = _nameLen; + } + status = S_OK; + } + else + { + status = E_FAIL; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::GetVersionId( + /* [out] */ GUID* vid) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_module->GetFile()->HasMetadata()) + { + status = E_NOINTERFACE; + } + else + { + GUID mdVid; + + status = m_module->GetMDImport()->GetScopeProps(NULL, &mdVid); + if (SUCCEEDED(status)) + { + *vid = mdVid; + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::GetFlags( + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *flags = 0; + + if (m_module->IsReflection()) + { + (*flags) |= CLRDATA_MODULE_IS_DYNAMIC; + } + if (m_module->IsIStream()) + { + (*flags) |= CLRDATA_MODULE_IS_MEMORY_STREAM; + } + + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::IsSameObject( + /* [in] */ IXCLRDataModule* mod) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = (PTR_HOST_TO_TADDR(m_module) == + PTR_HOST_TO_TADDR(((ClrDataModule*)mod)-> + m_module)) ? + S_OK : S_FALSE; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::StartEnumExtents( + /* [out] */ CLRDATA_ENUM* handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_setExtents) + { + PEFile* file = m_module->GetFile(); + if (!file) + { + *handle = 0; + status = E_INVALIDARG; + goto Exit; + } + + CLRDATA_MODULE_EXTENT* extent = m_extents; + + if (file->GetLoadedImageContents() != NULL) + { + extent->base = + TO_CDADDR( PTR_TO_TADDR(file->GetLoadedImageContents(&extent->length)) ); + extent->type = CLRDATA_MODULE_PE_FILE; + extent++; + } + if (file->HasNativeImage()) + { + extent->base = TO_CDADDR(PTR_TO_TADDR(file->GetLoadedNative()->GetBase())); + extent->length = file->GetLoadedNative()->GetVirtualSize(); + extent->type = CLRDATA_MODULE_PREJIT_FILE; + extent++; + } + + m_setExtents = true; + m_extentsEnd = extent; + } + + *handle = TO_CDENUM(m_extents); + status = m_extents != m_extentsEnd ? S_OK : S_FALSE; + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EnumExtent( + /* [in, out] */ CLRDATA_ENUM* handle, + /* [out] */ CLRDATA_MODULE_EXTENT *extent) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + CLRDATA_MODULE_EXTENT* curExtent = + FROM_CDENUM(CLRDATA_MODULE_EXTENT, *handle); + if (!m_setExtents || + curExtent < m_extents || + curExtent > m_extentsEnd) + { + status = E_INVALIDARG; + } + else if (curExtent < m_extentsEnd) + { + *extent = *curExtent++; + *handle = TO_CDENUM(curExtent); + status = S_OK; + } + else + { + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::EndEnumExtents( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // Enumerator holds no resources. + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT +ClrDataModule::RequestGetModulePtr( + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + // Validate params. + // Input: Nothing. + // Output: a DacpGetModuleAddress structure. + if ((inBufferSize != 0) || + (inBuffer != NULL) || + (outBufferSize != sizeof(DacpGetModuleAddress)) || + (outBuffer == NULL)) + { + return E_INVALIDARG; + } + + DacpGetModuleAddress * outGMA = reinterpret_cast<DacpGetModuleAddress *> (outBuffer); + + outGMA->ModulePtr = TO_CDADDR(PTR_HOST_TO_TADDR(m_module)); + return S_OK; +} + +HRESULT +ClrDataModule::RequestGetModuleData( + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + // Validate params. + // Input: Nothing. + // Output: a DacpGetModuleData structure. + if ((inBufferSize != 0) || + (inBuffer != NULL) || + (outBufferSize != sizeof(DacpGetModuleData)) || + (outBuffer == NULL)) + { + return E_INVALIDARG; + } + + DacpGetModuleData * outGMD = reinterpret_cast<DacpGetModuleData *>(outBuffer); + ZeroMemory(outGMD, sizeof(DacpGetModuleData)); + + Module* pModule = GetModule(); + PEFile *pPEFile = pModule->GetFile(); + + outGMD->PEFile = TO_CDADDR(PTR_HOST_TO_TADDR(pPEFile)); + outGMD->IsDynamic = pModule->IsReflection(); + + if (pPEFile != NULL) + { + outGMD->IsInMemory = pPEFile->GetPath().IsEmpty(); + + COUNT_T peSize; + outGMD->LoadedPEAddress = TO_CDADDR(PTR_TO_TADDR(pPEFile->GetLoadedImageContents(&peSize))); + outGMD->LoadedPESize = (ULONG64)peSize; + outGMD->IsFileLayout = pPEFile->GetLoaded()->IsFlat(); + } + + // If there is a in memory symbol stream + CGrowableStream* stream = pModule->GetInMemorySymbolStream(); + if (stream != NULL) + { + // Save the in-memory PDB address and size + MemoryRange range = stream->GetRawBuffer(); + outGMD->InMemoryPdbAddress = TO_CDADDR(PTR_TO_TADDR(range.StartAddress())); + outGMD->InMemoryPdbSize = range.Size(); + } + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + switch(reqCode) + { + case CLRDATA_REQUEST_REVISION: + if (inBufferSize != 0 || + inBuffer || + outBufferSize != sizeof(ULONG32)) + { + status = E_INVALIDARG; + } + else + { + *(ULONG32*)outBuffer = 3; + status = S_OK; + } + break; + + case DACDATAMODULEPRIV_REQUEST_GET_MODULEPTR: + status = RequestGetModulePtr(inBufferSize, inBuffer, outBufferSize, outBuffer); + break; + + case DACDATAMODULEPRIV_REQUEST_GET_MODULEDATA: + status = RequestGetModuleData(inBufferSize, inBuffer, outBufferSize, outBuffer); + break; + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataModule::SetJITCompilerFlags( + /* [in] */ DWORD dwFlags) +{ + // Note: this is similar but not equivalent to the DacDbi version of this function + HRESULT hr = S_OK; + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // can't have a subset of these, eg 0x101, so make sure we have an exact match + if ((dwFlags != CORDEBUG_JIT_DEFAULT) && + (dwFlags != CORDEBUG_JIT_DISABLE_OPTIMIZATION)) + { + hr = E_INVALIDARG; + } +#ifdef FEATURE_PREJIT + else if (m_module->HasNativeImage()) + { + hr = CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE; + } +#endif + else + { + _ASSERTE(m_module != NULL); + + BOOL fAllowJitOpts = ((dwFlags & CORDEBUG_JIT_DISABLE_OPTIMIZATION) != CORDEBUG_JIT_DISABLE_OPTIMIZATION); + + // Initialize dwBits. + DWORD dwBits = (m_module->GetDebuggerInfoBits() & ~(DACF_ALLOW_JIT_OPTS | DACF_ENC_ENABLED)); + dwBits &= DACF_CONTROL_FLAGS_MASK; + + if (fAllowJitOpts) + { + dwBits |= DACF_ALLOW_JIT_OPTS; + } + + // Settings from the debugger take precedence over all other settings. + dwBits |= DACF_USER_OVERRIDE; + + // set flags. This will write back to the target + m_module->SetDebuggerInfoBits((DebuggerAssemblyControlFlags)dwBits); + + _ASSERTE(SUCCEEDED(hr)); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &hr)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return hr; +} + +HRESULT +ClrDataModule::GetMdInterface(PVOID* retIface) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_mdImport == NULL) + { + if (!m_module->GetFile()->HasMetadata()) + { + status = E_NOINTERFACE; + goto Exit; + } + + // + // Make sure internal MD is in RW format. + // + IMDInternalImport* rwMd; + + status = ConvertMDInternalImport(m_module->GetMDImport(), &rwMd); + if (FAILED(status)) + { + goto Exit; + } + + // If no conversion took place the same interface was + // was returned without an AddRef. AddRef now so + // that rwMd has a reference either way. + if (status == S_FALSE) + { + rwMd->AddRef(); + } + + status = GetMDPublicInterfaceFromInternal((PVOID)rwMd, + IID_IMetaDataImport, + (PVOID*)&m_mdImport); + + rwMd->Release(); + + if (status != S_OK) + { + goto Exit; + } + } + + _ASSERTE(m_mdImport != NULL); + m_mdImport->AddRef(); + *retIface = m_mdImport; + status = S_OK; + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +//---------------------------------------------------------------------------- +// +// ClrDataMethodDefinition. +// +//---------------------------------------------------------------------------- + +ClrDataMethodDefinition::ClrDataMethodDefinition(ClrDataAccess* dac, + Module* module, + mdMethodDef token, + MethodDesc* methodDesc) +{ + m_dac = dac; + m_dac->AddRef(); + m_instanceAge = m_dac->m_instanceAge; + m_refs = 1; + m_module = module; + m_token = token; + m_methodDesc = methodDesc; +} + +ClrDataMethodDefinition::~ClrDataMethodDefinition(void) +{ + m_dac->Release(); +} + +STDMETHODIMP +ClrDataMethodDefinition::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataMethodDefinition))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataMethodDefinition*>(this)); + return S_OK; + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +ClrDataMethodDefinition::AddRef(THIS) +{ + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataMethodDefinition::Release(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::GetTypeDefinition( + /* [out] */ IXCLRDataTypeDefinition **typeDefinition) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + TypeHandle typeHandle; + mdTypeDef token; + + if (m_methodDesc) + { + typeHandle = TypeHandle(m_methodDesc->GetMethodTable()); + token = typeHandle.GetMethodTable()->GetCl(); + } + else + { + if ((status = m_module->GetMDImport()-> + GetParentToken(m_token, &token)) != S_OK) + { + goto Exit; + } + } + + *typeDefinition = new (nothrow) + ClrDataTypeDefinition(m_dac, m_module, token, typeHandle); + status = *typeDefinition ? S_OK : E_OUTOFMEMORY; + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::StartEnumInstances( + /* [in] */ IXCLRDataAppDomain* appDomain, + /* [out] */ CLRDATA_ENUM *handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_methodDesc) + { + status = EnumMethodInstances::CdStart(m_methodDesc, appDomain, + handle); + } + else + { + status = S_FALSE; + *handle = 0; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::EnumInstance( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ IXCLRDataMethodInstance **instance) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = EnumMethodInstances::CdNext(m_dac, handle, instance); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::EndEnumInstances( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = EnumMethodInstances::CdEnd(handle); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::GetName( + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR name[ ]) +{ + HRESULT status; + + if (flags != 0) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_methodDesc) + { + status = m_dac->GetFullMethodName(m_methodDesc, + bufLen, nameLen, name); + } + else + { + char methName[MAX_CLASSNAME_LENGTH]; + + status = GetFullMethodNameFromMetadata(m_module->GetMDImport(), + m_token, + NumItems(methName), + methName); + if (status == S_OK) + { + status = ConvertUtf8(methName, bufLen, nameLen, name); + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::GetTokenAndScope( + /* [out] */ mdToken *token, + /* [out] */ IXCLRDataModule **Module) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = S_OK; + + if (token) + { + *token = m_token; + } + + if (Module) + { + *Module = new (nothrow) + ClrDataModule(m_dac, m_module); + status = *Module ? S_OK : E_OUTOFMEMORY; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::GetFlags( + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = GetSharedMethodFlags(m_methodDesc, flags); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::IsSameObject( + /* [in] */ IXCLRDataMethodDefinition* method) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_methodDesc) + { + status = (PTR_HOST_TO_TADDR(m_methodDesc) == + PTR_HOST_TO_TADDR(((ClrDataMethodDefinition*)method)-> + m_methodDesc)) ? + S_OK : S_FALSE; + } + else + { + status = (PTR_HOST_TO_TADDR(m_module) == + PTR_HOST_TO_TADDR(((ClrDataMethodDefinition*)method)-> + m_module) && + m_token == ((ClrDataMethodDefinition*)method)->m_token) ? + S_OK : S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::GetLatestEnCVersion( + /* [out] */ ULONG32* version) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + *version = 0; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::StartEnumExtents( + /* [out] */ CLRDATA_ENUM *handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + COR_ILMETHOD* ilMeth = GetIlMethod(); + status = ilMeth ? S_OK : S_FALSE; + *handle = TO_CDENUM(ilMeth); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::EnumExtent( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ CLRDATA_METHDEF_EXTENT *extent) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (*handle) + { + COR_ILMETHOD* ilMeth = FROM_CDENUM(COR_ILMETHOD, *handle); + COR_ILMETHOD_DECODER ilDec(ilMeth); + *handle = 0; + + extent->startAddress = TO_CDADDR(PTR_HOST_TO_TADDR(ilMeth) + + 4 * ilDec.GetSize()); + extent->endAddress = extent->startAddress + + ilDec.GetCodeSize() - 1; + extent->type = CLRDATA_METHDEF_IL; + // XXX Microsoft - EnC version. + extent->enCVersion = 0; + + status = S_OK; + } + else + { + status = E_INVALIDARG; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::EndEnumExtents( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // Nothing to do. + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::GetCodeNotification( + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + JITNotifications jn(m_dac->GetHostJitNotificationTable()); + if (!jn.IsActive()) + { + status = E_OUTOFMEMORY; + } + else + { + TADDR modulePtr = PTR_HOST_TO_TADDR(m_module); + *flags = jn.Requested(modulePtr, m_token); + status = S_OK; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::SetCodeNotification( + /* [in] */ ULONG32 flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!IsValidMethodCodeNotification(flags)) + { + status = E_INVALIDARG; + } + else + { + JITNotifications jn(m_dac->GetHostJitNotificationTable()); + if (!jn.IsActive()) + { + status = E_OUTOFMEMORY; + } + else + { + TADDR modulePtr = PTR_HOST_TO_TADDR(m_module); + USHORT NType = jn.Requested(modulePtr, m_token); + + if (NType == flags) + { + // notification already set + status = S_OK; + } + else + { + if (jn.SetNotification(modulePtr, m_token, flags) && + jn.UpdateOutOfProcTable()) + { + // new notification added + status = S_OK; + } + else + { + // error setting notification + status = E_FAIL; + } + } + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::GetRepresentativeEntryAddress( + /* [out] */ CLRDATA_ADDRESS* addr) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + COR_ILMETHOD* ilMeth = GetIlMethod(); + if (ilMeth) + { + COR_ILMETHOD_DECODER ilDec(ilMeth); + *addr = TO_CDADDR(PTR_HOST_TO_TADDR(ilMeth) + + 4 * ilDec.GetSize()); + status = S_OK; + } + else + { + status = E_UNEXPECTED; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::HasClassOrMethodInstantiation( + /* [out] */ BOOL* bGeneric) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_methodDesc) + { + *bGeneric = m_methodDesc->HasClassOrMethodInstantiation(); + status = S_OK; + } + else + { + status = E_UNEXPECTED; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodDefinition::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + switch(reqCode) + { + case CLRDATA_REQUEST_REVISION: + if (inBufferSize != 0 || + inBuffer || + outBufferSize != sizeof(ULONG32)) + { + status = E_INVALIDARG; + } + else + { + *(ULONG32*)outBuffer = 1; + status = S_OK; + } + break; + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +COR_ILMETHOD* +ClrDataMethodDefinition::GetIlMethod(void) +{ + if (m_methodDesc) + { + if (!m_methodDesc->HasILHeader()) + { + return NULL; + } + else + { + return m_methodDesc->GetILHeader(); + } + } + else + { + ULONG ilRva; + ULONG implFlags; + + if (FAILED(m_module->GetMDImport()-> + GetMethodImplProps(m_token, &ilRva, &implFlags))) + { + return NULL; + } + if (!ilRva) + { + return NULL; + } + else + { + return DacGetIlMethod(m_module->GetIL((RVA)ilRva)); + } + } +} + +HRESULT +ClrDataMethodDefinition::NewFromModule(ClrDataAccess* dac, + Module* module, + mdMethodDef token, + ClrDataMethodDefinition** methDef, + IXCLRDataMethodDefinition** pubMethDef) +{ + // The method may not have internal runtime data yet, + // so the absence of a MethodDesc is not a failure. + // It'll just produce a metadata-query MethoDefinition. + MethodDesc* methodDesc = module->LookupMethodDef(token); + + ClrDataMethodDefinition* def = new (nothrow) + ClrDataMethodDefinition(dac, module, token, methodDesc); + if (!def) + { + return E_OUTOFMEMORY; + } + + PREFIX_ASSUME(methDef || pubMethDef); + + if (methDef) + { + *methDef = def; + } + if (pubMethDef) + { + *pubMethDef = def; + } + + return S_OK; +} + +HRESULT +ClrDataMethodDefinition::GetSharedMethodFlags(MethodDesc* methodDesc, + ULONG32* flags) +{ + *flags = CLRDATA_METHOD_DEFAULT; + + if (methodDesc) + { + MetaSig sig(methodDesc); + + if (sig.HasThis()) + { + (*flags) |= CLRDATA_METHOD_HAS_THIS; + } + } + + return S_OK; +} + +//---------------------------------------------------------------------------- +// +// ClrDataMethodInstance. +// +//---------------------------------------------------------------------------- + +ClrDataMethodInstance::ClrDataMethodInstance(ClrDataAccess* dac, + AppDomain* appDomain, + MethodDesc* methodDesc) +{ + m_dac = dac; + m_dac->AddRef(); + m_instanceAge = m_dac->m_instanceAge; + m_refs = 1; + m_appDomain = appDomain; + m_methodDesc = methodDesc; +} + +ClrDataMethodInstance::~ClrDataMethodInstance(void) +{ + m_dac->Release(); +} + +STDMETHODIMP +ClrDataMethodInstance::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataMethodInstance))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataMethodInstance*>(this)); + return S_OK; + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +ClrDataMethodInstance::AddRef(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataMethodInstance::Release(THIS) +{ + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::GetTypeInstance( + /* [out] */ IXCLRDataTypeInstance **typeInstance) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (!m_appDomain) + { + status = E_UNEXPECTED; + } + else + { + *typeInstance = new (nothrow) + ClrDataTypeInstance(m_dac, + m_appDomain, + TypeHandle(m_methodDesc-> + GetMethodTable())); + status = *typeInstance ? S_OK : E_OUTOFMEMORY; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::GetDefinition( + /* [out] */ IXCLRDataMethodDefinition **methodDefinition) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *methodDefinition = new (nothrow) + ClrDataMethodDefinition(m_dac, + m_methodDesc->GetModule(), + m_methodDesc->GetMemberDef(), + m_methodDesc); + status = *methodDefinition ? S_OK : E_OUTOFMEMORY; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::GetTokenAndScope( + /* [out] */ mdToken *token, + /* [out] */ IXCLRDataModule **mod) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = S_OK; + + if (token) + { + *token = m_methodDesc->GetMemberDef(); + } + + if (mod) + { + *mod = new (nothrow) + ClrDataModule(m_dac, m_methodDesc->GetModule()); + status = *mod ? S_OK : E_OUTOFMEMORY; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::GetName( + /* [in] */ ULONG32 flags, + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *nameLen, + /* [size_is][out] */ __out_ecount_part(bufLen, *nameLen) WCHAR name[ ]) +{ + HRESULT status; + + if (flags != 0) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = m_dac->GetFullMethodName(m_methodDesc, bufLen, nameLen, name); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + else + { + static WCHAR nameUnk[] = W("Unknown"); + wcscpy_s(name, bufLen, nameUnk); + if (nameLen != NULL) + { + *nameLen = _countof(nameUnk); + } + status = S_OK; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::GetFlags( + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = ClrDataMethodDefinition:: + GetSharedMethodFlags(m_methodDesc, flags); + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::IsSameObject( + /* [in] */ IXCLRDataMethodInstance* method) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + status = (PTR_HOST_TO_TADDR(m_appDomain) == + PTR_HOST_TO_TADDR(((ClrDataMethodInstance*)method)-> + m_appDomain) && + PTR_HOST_TO_TADDR(m_methodDesc) == + PTR_HOST_TO_TADDR(((ClrDataMethodInstance*)method)-> + m_methodDesc)) ? + S_OK : S_FALSE; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::GetEnCVersion( + /* [out] */ ULONG32* version) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + *version = 0; + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::GetNumTypeArguments( + /* [out] */ ULONG32 *numTypeArgs) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::GetTypeArgumentByIndex( + /* [in] */ ULONG32 index, + /* [out] */ IXCLRDataTypeInstance **typeArg) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::GetILOffsetsByAddress( + /* [in] */ CLRDATA_ADDRESS address, + /* [in] */ ULONG32 offsetsLen, + /* [out] */ ULONG32 *offsetsNeeded, + /* [size_is][out] */ ULONG32 ilOffsets[ ]) +{ + HRESULT status; + DebuggerILToNativeMap* map = NULL; + bool mapAllocated = false; + + DAC_ENTER_SUB(m_dac); + EX_TRY + { + ULONG32 numMap; + ULONG32 codeOffset; + ULONG32 hits = 0; + +#ifdef _TARGET_ARM_ + address &= ~THUMB_CODE; // on ARM windbg passes in an address with the mode flag set... need to workaround +#endif + if ((status = m_dac->GetMethodNativeMap(m_methodDesc, + CLRDATA_ADDRESS_TO_TADDR(address), + &numMap, + &map, + &mapAllocated, + NULL, + &codeOffset)) != S_OK) + { + goto Exit; + } + + for (ULONG32 i = 0; i < numMap; i++) + { + if (codeOffset >= map[i].nativeStartOffset && + (((LONG)map[i].ilOffset == ICorDebugInfo::EPILOG && + !map[i].nativeEndOffset) || + codeOffset < map[i].nativeEndOffset)) + { + hits++; + + if (offsetsLen && ilOffsets) + { + *ilOffsets = map[i].ilOffset; + ilOffsets++; + offsetsLen--; + } + } + } + + if (offsetsNeeded) + { + *offsetsNeeded = hits; + } + status = hits ? S_OK : E_NOINTERFACE; + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + if (mapAllocated) + { + delete [] map; + } + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::GetAddressRangesByILOffset( + /* [in] */ ULONG32 ilOffset, + /* [in] */ ULONG32 rangesLen, + /* [out] */ ULONG32 *rangesNeeded, + /* [size_is][out] */ CLRDATA_ADDRESS_RANGE addressRanges[ ]) +{ + HRESULT status; + DebuggerILToNativeMap* map = NULL; + bool mapAllocated = false; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + ULONG32 numMap; + CLRDATA_ADDRESS codeStart; + ULONG32 hits = 0; + + if ((status = m_dac->GetMethodNativeMap(m_methodDesc, + 0, + &numMap, + &map, + &mapAllocated, + &codeStart, + NULL)) != S_OK) + { + goto Exit; + } + + for (ULONG32 i = 0; i < numMap; i++) + { + if (map[i].ilOffset == ilOffset) + { + hits++; + + if (rangesLen && addressRanges) + { + addressRanges->startAddress = + TO_CDADDR(codeStart + map[i].nativeStartOffset); + if ((LONG)map[i].ilOffset == ICorDebugInfo::EPILOG && + !map[i].nativeEndOffset) + { + addressRanges->endAddress = 0; + } + else + { + addressRanges->endAddress = + TO_CDADDR(codeStart + map[i].nativeEndOffset); + } + addressRanges++; + rangesLen--; + } + } + } + + if (rangesNeeded) + { + *rangesNeeded = hits; + } + status = hits ? S_OK : E_NOINTERFACE; + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + if (mapAllocated) + { + delete [] map; + } + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::GetILAddressMap( + /* [in] */ ULONG32 mapLen, + /* [out] */ ULONG32 *mapNeeded, + /* [size_is][out] */ CLRDATA_IL_ADDRESS_MAP maps[ ]) +{ + HRESULT status; + DebuggerILToNativeMap* map = NULL; + bool mapAllocated = false; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + ULONG32 numMap; + CLRDATA_ADDRESS codeStart; + + if ((status = m_dac->GetMethodNativeMap(m_methodDesc, + 0, + &numMap, + &map, + &mapAllocated, + &codeStart, + NULL)) != S_OK) + { + goto Exit; + } + + for (ULONG32 i = 0; i < numMap; i++) + { + if (mapLen && maps) + { + maps->ilOffset = map[i].ilOffset; + maps->startAddress = + TO_CDADDR(codeStart + map[i].nativeStartOffset); + maps->endAddress = + TO_CDADDR(codeStart + map[i].nativeEndOffset); + // XXX Microsoft - Define types as mapping of + // ICorDebugInfo::SourceTypes. + maps->type = CLRDATA_SOURCE_TYPE_INVALID; + maps++; + mapLen--; + } + else + { + break; + } + } + + if (mapNeeded) + { + *mapNeeded = numMap; + } + status = numMap ? S_OK : E_NOINTERFACE; + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + if (mapAllocated) + { + delete [] map; + } + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::StartEnumExtents( + /* [out] */ CLRDATA_ENUM *handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + METH_EXTENTS* extents; + + if ((status = m_dac-> + GetMethodExtents(m_methodDesc, &extents)) == S_OK) + { + *handle = TO_CDENUM(extents); + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::EnumExtent( + /* [out][in] */ CLRDATA_ENUM *handle, + /* [out] */ CLRDATA_ADDRESS_RANGE *extent) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + METH_EXTENTS* extents = + FROM_CDENUM(METH_EXTENTS, *handle); + if (extents->curExtent >= extents->numExtents) + { + status = S_FALSE; + } + else + { + CLRDATA_ADDRESS_RANGE* curExtent = + extents->extents + extents->curExtent++; + *extent = *curExtent; + status = S_OK; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::EndEnumExtents( + /* [in] */ CLRDATA_ENUM handle) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + delete FROM_CDENUM(METH_EXTENTS, handle); + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::GetRepresentativeEntryAddress( + /* [out] */ CLRDATA_ADDRESS* addr) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_methodDesc->HasNativeCode()) + { + *addr = TO_CDADDR(m_methodDesc->GetNativeCode()); + status = S_OK; + } + else + { + status = E_UNEXPECTED; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataMethodInstance::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + switch(reqCode) + { + case CLRDATA_REQUEST_REVISION: + if (inBufferSize != 0 || + inBuffer || + outBufferSize != sizeof(ULONG32)) + { + status = E_INVALIDARG; + } + else + { + *(ULONG32*)outBuffer = 1; + status = S_OK; + } + break; + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT +ClrDataMethodInstance::NewFromModule(ClrDataAccess* dac, + AppDomain* appDomain, + Module* module, + mdMethodDef token, + ClrDataMethodInstance** methInst, + IXCLRDataMethodInstance** pubMethInst) +{ + MethodDesc* methodDesc = module->LookupMethodDef(token); + if (!methodDesc || + !methodDesc->HasNativeCode()) + { + return E_INVALIDARG; + } + + ClrDataMethodInstance* inst = new (nothrow) + ClrDataMethodInstance(dac, appDomain, methodDesc); + if (!inst) + { + return E_OUTOFMEMORY; + } + + PREFIX_ASSUME(methInst || pubMethInst); + + if (methInst) + { + *methInst = inst; + } + if (pubMethInst) + { + *pubMethInst = inst; + } + + return S_OK; +} + +//---------------------------------------------------------------------------- +// +// ClrDataExceptionState. +// +//---------------------------------------------------------------------------- + +ClrDataExceptionState::ClrDataExceptionState(ClrDataAccess* dac, + AppDomain* appDomain, + Thread* thread, + ULONG32 flags, + ClrDataExStateType* exInfo, + OBJECTHANDLE throwable, + ClrDataExStateType* prevExInfo) +{ + m_dac = dac; + m_dac->AddRef(); + m_instanceAge = m_dac->m_instanceAge; + m_appDomain = appDomain; + m_thread = thread; + m_flags = flags; + m_exInfo = exInfo; + m_throwable = throwable; + m_prevExInfo = prevExInfo; + m_refs = 1; +} + +ClrDataExceptionState::~ClrDataExceptionState(void) +{ + m_dac->Release(); +} + +STDMETHODIMP +ClrDataExceptionState::QueryInterface(THIS_ + IN REFIID interfaceId, + OUT PVOID* iface) +{ + if (IsEqualIID(interfaceId, IID_IUnknown) || + IsEqualIID(interfaceId, __uuidof(IXCLRDataExceptionState))) + { + AddRef(); + *iface = static_cast<IUnknown*> + (static_cast<IXCLRDataExceptionState*>(this)); + return S_OK; + } + else + { + *iface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +ClrDataExceptionState::AddRef(THIS) +{ + return InterlockedIncrement(&m_refs); +} + +STDMETHODIMP_(ULONG) +ClrDataExceptionState::Release(THIS) +{ + SUPPORTS_DAC_HOST_ONLY; + LONG newRefs = InterlockedDecrement(&m_refs); + if (newRefs == 0) + { + delete this; + } + return newRefs; +} + +HRESULT STDMETHODCALLTYPE +ClrDataExceptionState::GetFlags( + /* [out] */ ULONG32 *flags) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *flags = m_flags; + + if (m_prevExInfo) + { + (*flags) |= CLRDATA_EXCEPTION_NESTED; + } + + status = S_OK; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataExceptionState::GetPrevious( + /* [out] */ IXCLRDataExceptionState **exState) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + if (m_prevExInfo) + { + *exState = new (nothrow) + ClrDataExceptionState(m_dac, + m_appDomain, + m_thread, + CLRDATA_EXCEPTION_DEFAULT, + m_prevExInfo, + m_prevExInfo->m_hThrowable, + m_prevExInfo->m_pPrevNestedInfo); + status = *exState ? S_OK : E_OUTOFMEMORY; + } + else + { + *exState = NULL; + status = S_FALSE; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataExceptionState::GetManagedObject( + /* [out] */ IXCLRDataValue **value) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + PTR_UNCHECKED_OBJECTREF throwRef(m_throwable); + if (!throwRef.IsValid()) + { + status = E_INVALIDARG; + goto Exit; + } + + NativeVarLocation varLoc; + ClrDataValue* RefVal; + + varLoc.addr = TO_CDADDR(m_throwable); + varLoc.size = sizeof(TADDR); + varLoc.contextReg = false; + + RefVal = new (nothrow) + ClrDataValue(m_dac, + m_appDomain, + m_thread, + CLRDATA_VALUE_IS_REFERENCE, + TypeHandle((*throwRef)->GetMethodTable()), + varLoc.addr, + 1, &varLoc); + if (!RefVal) + { + status = E_OUTOFMEMORY; + goto Exit; + } + + status = RefVal->GetAssociatedValue(value); + + delete RefVal; + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataExceptionState::GetBaseType( + /* [out] */ CLRDataBaseExceptionType *type) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataExceptionState::GetCode( + /* [out] */ ULONG32 *code) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + // XXX Microsoft. + status = E_NOTIMPL; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataExceptionState::GetString( + /* [in] */ ULONG32 bufLen, + /* [out] */ ULONG32 *strLen, + /* [size_is][out] */ __out_ecount_part(bufLen, *strLen) WCHAR str[ ]) +{ + HRESULT status = E_FAIL; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + PTR_UNCHECKED_OBJECTREF throwRef(m_throwable); + STRINGREF message = EXCEPTIONREF(*throwRef)->GetMessage(); + + if (message == NULL) + { + if (strLen) + { + *strLen = 0; + } + if (bufLen >= 1) + { + str[0] = 0; + } + status = S_OK; + } + else + { + PWSTR msgStr = DacInstantiateStringW((TADDR)message->GetBuffer(), + message->GetStringLength(), + true); + + status = StringCchCopy(str, bufLen, msgStr) == S_OK ? S_OK : S_FALSE; + if (strLen != NULL) + { + size_t cchName = wcslen(msgStr) + 1; + if (FitsIn<ULONG32>(cchName)) + { + *strLen = (ULONG32) cchName; + } + else + { + status = COR_E_OVERFLOW; + } + } + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataExceptionState::IsSameState( + /* [in] */ EXCEPTION_RECORD64 *exRecord, + /* [in] */ ULONG32 contextSize, + /* [size_is][in] */ BYTE cxRecord[ ]) +{ + return IsSameState2(CLRDATA_EXSAME_SECOND_CHANCE, + exRecord, contextSize, cxRecord); +} + +HRESULT STDMETHODCALLTYPE +ClrDataExceptionState::IsSameState2( + /* [in] */ ULONG32 flags, + /* [in] */ EXCEPTION_RECORD64 *exRecord, + /* [in] */ ULONG32 contextSize, + /* [size_is][in] */ BYTE cxRecord[ ]) +{ + HRESULT status; + + if ((flags & ~(CLRDATA_EXSAME_SECOND_CHANCE | + CLRDATA_EXSAME_FIRST_CHANCE)) != 0) + { + return E_INVALIDARG; + } + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + PTR_EXCEPTION_RECORD infoExRecord; + + // XXX Microsoft - This should also check that the + // context matches the context at the time + // of the exception, but it's not clear + // how to do that in all cases. + + status = S_FALSE; + + if (!m_exInfo) + { + // We don't have full state, but that's expected + // on a first chance exception so allow that. + if ((flags & CLRDATA_EXSAME_FIRST_CHANCE) != 0) + { + status = S_OK; + } + + goto Exit; + } + + infoExRecord = GetCurrentExceptionRecord(); + + if ((TADDR)infoExRecord->ExceptionAddress == + (TADDR)exRecord->ExceptionAddress) + { + status = S_OK; + } + + Exit: ; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataExceptionState::GetTask( + /* [out] */ IXCLRDataTask** task) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + *task = new (nothrow) + ClrDataTask(m_dac, + m_thread); + status = *task ? S_OK : E_OUTOFMEMORY; + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT STDMETHODCALLTYPE +ClrDataExceptionState::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + HRESULT status; + + DAC_ENTER_SUB(m_dac); + + EX_TRY + { + switch(reqCode) + { + case CLRDATA_REQUEST_REVISION: + if (inBufferSize != 0 || + inBuffer || + outBufferSize != sizeof(ULONG32)) + { + status = E_INVALIDARG; + } + else + { + *(ULONG32*)outBuffer = 2; + status = S_OK; + } + break; + + default: + status = E_INVALIDARG; + break; + } + } + EX_CATCH + { + if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) + { + EX_RETHROW; + } + } + EX_END_CATCH(SwallowAllExceptions) + + DAC_LEAVE(); + return status; +} + +HRESULT +ClrDataExceptionState::NewFromThread(ClrDataAccess* dac, + Thread* thread, + ClrDataExceptionState** exception, + IXCLRDataExceptionState** pubException) +{ + if (!thread->HasException()) + { + return E_NOINTERFACE; + } + + ClrDataExStateType* exState; + ClrDataExceptionState* exIf; + +#ifdef WIN64EXCEPTIONS + exState = thread->GetExceptionState()->m_pCurrentTracker; +#else + exState = &(thread->GetExceptionState()->m_currentExInfo); +#endif // WIN64EXCEPTIONS + + exIf = new (nothrow) + ClrDataExceptionState(dac, + thread->GetDomain(), + thread, + CLRDATA_EXCEPTION_DEFAULT, + exState, + exState->m_hThrowable, + exState->m_pPrevNestedInfo); + if (!exIf) + { + return E_OUTOFMEMORY; + } + + PREFIX_ASSUME(exception || pubException); + + if (exception) + { + *exception = exIf; + } + if (pubException) + { + *pubException = exIf; + } + + return S_OK; +} + +PTR_EXCEPTION_RECORD +ClrDataExceptionState::GetCurrentExceptionRecord() +{ + PTR_EXCEPTION_RECORD pExRecord = NULL; + +#ifdef WIN64EXCEPTIONS + pExRecord = m_exInfo->m_ptrs.ExceptionRecord; +#else // WIN64EXCEPTIONS + pExRecord = m_exInfo->m_pExceptionRecord; +#endif // WIN64EXCEPTIONS + + return pExRecord; +} + +PTR_CONTEXT +ClrDataExceptionState::GetCurrentContextRecord() +{ + PTR_CONTEXT pContext = NULL; + +#ifdef WIN64EXCEPTIONS + pContext = m_exInfo->m_ptrs.ContextRecord; +#else // WIN64EXCEPTIONS + pContext = m_exInfo->m_pContext; +#endif // WIN64EXCEPTIONS + + return pContext; +} + +//---------------------------------------------------------------------------- +// +// EnumMethodDefinitions. +// +//---------------------------------------------------------------------------- + +HRESULT +EnumMethodDefinitions::Start(Module* mod, + bool useAddrFilter, + CLRDATA_ADDRESS addrFilter) +{ + m_module = mod; + m_useAddrFilter = useAddrFilter; + m_addrFilter = addrFilter; + m_typeToken = mdTokenNil; + m_needMethodStart = true; + return m_typeEnum.Start(m_module->GetMDImport(), mdtTypeDef, mdTokenNil); +} + +HRESULT +EnumMethodDefinitions::Next(ClrDataAccess* dac, + IXCLRDataMethodDefinition **method) +{ + HRESULT status; + + NextType: + if (m_typeToken == mdTokenNil) + { + if ((status = m_typeEnum.NextToken(&m_typeToken, NULL, NULL)) != S_OK) + { + return status; + } + + m_needMethodStart = true; + } + + if (m_needMethodStart) + { + if ((status = m_methodEnum. + Start(m_module->GetMDImport(), + mdtMethodDef, m_typeToken)) != S_OK) + { + return status; + } + + m_needMethodStart = false; + } + + NextMethod: + mdToken methodToken; + + if ((status = m_methodEnum.NextToken(&methodToken, NULL, NULL)) != S_OK) + { + if (status == S_FALSE) + { + m_typeToken = mdTokenNil; + goto NextType; + } + + return status; + } + + if (m_useAddrFilter) + { + ULONG ilRva; + ULONG implFlags; + + status = m_module->GetMDImport()-> + GetMethodImplProps(methodToken, &ilRva, &implFlags); + if (FAILED(status)) + { + return status; + } + if (!ilRva) + { + goto NextMethod; + } + + COR_ILMETHOD* ilMeth = + DacGetIlMethod(m_module->GetIL((RVA)ilRva)); + COR_ILMETHOD_DECODER ilDec(ilMeth); + + CLRDATA_ADDRESS start = + TO_CDADDR(PTR_HOST_TO_TADDR(ilMeth) + 4 * ilDec.GetSize()); + if (m_addrFilter < start || + m_addrFilter > start + ilDec.GetCodeSize() - 1) + { + goto NextMethod; + } + } + + return ClrDataMethodDefinition:: + NewFromModule(dac, + m_module, + methodToken, + NULL, + method); +} + +HRESULT +EnumMethodDefinitions::CdStart(Module* mod, + bool useAddrFilter, + CLRDATA_ADDRESS addrFilter, + CLRDATA_ENUM* handle) +{ + HRESULT status; + + *handle = NULL; + + if (!mod) + { + return S_FALSE; + } + + EnumMethodDefinitions* iter = new (nothrow) + EnumMethodDefinitions; + if (!iter) + { + return E_OUTOFMEMORY; + } + + if ((status = iter->Start(mod, useAddrFilter, addrFilter)) != S_OK) + { + delete iter; + return status; + } + + *handle = TO_CDENUM(iter); + return S_OK; +} + +HRESULT +EnumMethodDefinitions::CdNext(ClrDataAccess* dac, + CLRDATA_ENUM* handle, + IXCLRDataMethodDefinition** method) +{ + EnumMethodDefinitions* iter = FROM_CDENUM(EnumMethodDefinitions, *handle); + if (!iter) + { + return S_FALSE; + } + + return iter->Next(dac, method); +} + +HRESULT +EnumMethodDefinitions::CdEnd(CLRDATA_ENUM handle) +{ + EnumMethodDefinitions* iter = FROM_CDENUM(EnumMethodDefinitions, handle); + if (iter) + { + delete iter; + return S_OK; + } + else + { + return E_INVALIDARG; + } +} + +//---------------------------------------------------------------------------- +// +// EnumMethodInstances. +// +//---------------------------------------------------------------------------- + +EnumMethodInstances::EnumMethodInstances(MethodDesc* methodDesc, + IXCLRDataAppDomain* givenAppDomain) + : m_domainIter(FALSE) +{ + m_methodDesc = methodDesc; + if (givenAppDomain) + { + m_givenAppDomain = + ((ClrDataAppDomain*)givenAppDomain)->GetAppDomain(); + } + else + { + m_givenAppDomain = NULL; + } + m_givenAppDomainUsed = false; + m_appDomain = NULL; +} + +HRESULT +EnumMethodInstances::Next(ClrDataAccess* dac, + IXCLRDataMethodInstance **instance) +{ + NextDomain: + if (!m_appDomain) + { + if (m_givenAppDomainUsed || + !m_domainIter.Next()) + { + return S_FALSE; + } + + if (m_givenAppDomain) + { + m_appDomain = m_givenAppDomain; + m_givenAppDomainUsed = true; + } + else + { + m_appDomain = m_domainIter.GetDomain(); + } + + m_methodIter.Start(m_appDomain, + m_methodDesc->GetModule(), // module + m_methodDesc->GetMemberDef(), // token + m_methodDesc); // intial method desc + } + + NextMethod: + { + // Note: DAC doesn't need to keep the assembly alive - see code:CollectibleAssemblyHolder#CAH_DAC + CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; + if (!m_methodIter.Next(pDomainAssembly.This())) + { + m_appDomain = NULL; + goto NextDomain; + } + } + + if (!m_methodIter.Current()->HasNativeCode()) + { + goto NextMethod; + } + + *instance = new (nothrow) + ClrDataMethodInstance(dac, + m_appDomain, + m_methodIter.Current()); + return *instance ? S_OK : E_OUTOFMEMORY; +} + +HRESULT +EnumMethodInstances::CdStart(MethodDesc* methodDesc, + IXCLRDataAppDomain* appDomain, + CLRDATA_ENUM* handle) +{ + if (!methodDesc->HasClassOrMethodInstantiation() && + !methodDesc->HasNativeCode()) + { + *handle = 0; + return S_FALSE; + } + + EnumMethodInstances* iter = new (nothrow) + EnumMethodInstances(methodDesc, appDomain); + if (iter) + { + *handle = TO_CDENUM(iter); + return S_OK; + } + else + { + *handle = 0; + return E_OUTOFMEMORY; + } +} + +HRESULT +EnumMethodInstances::CdNext(ClrDataAccess* dac, + CLRDATA_ENUM* handle, + IXCLRDataMethodInstance** method) +{ + EnumMethodInstances* iter = FROM_CDENUM(EnumMethodInstances, *handle); + if (!iter) + { + return S_FALSE; + } + + return iter->Next(dac, method); +} + +HRESULT +EnumMethodInstances::CdEnd(CLRDATA_ENUM handle) +{ + EnumMethodInstances* iter = FROM_CDENUM(EnumMethodInstances, handle); + if (iter) + { + delete iter; + return S_OK; + } + else + { + return E_INVALIDARG; + } +} |