summaryrefslogtreecommitdiff
path: root/src/debug/daccess
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/daccess')
-rw-r--r--src/debug/daccess/.gitmirror1
-rw-r--r--src/debug/daccess/CMakeLists.txt49
-rw-r--r--src/debug/daccess/amd64/.gitmirror1
-rw-r--r--src/debug/daccess/amd64/primitives.cpp13
-rw-r--r--src/debug/daccess/arm/.gitmirror1
-rw-r--r--src/debug/daccess/arm/primitives.cpp9
-rw-r--r--src/debug/daccess/arm64/.gitmirror1
-rw-r--r--src/debug/daccess/arm64/primitives.cpp9
-rw-r--r--src/debug/daccess/daccess.cpp8673
-rw-r--r--src/debug/daccess/daccess.targets67
-rw-r--r--src/debug/daccess/dacdbiimpl.cpp7639
-rw-r--r--src/debug/daccess/dacdbiimpl.h1152
-rw-r--r--src/debug/daccess/dacdbiimpl.inl79
-rw-r--r--src/debug/daccess/dacdbiimpllocks.cpp43
-rw-r--r--src/debug/daccess/dacdbiimplstackwalk.cpp1313
-rw-r--r--src/debug/daccess/dacfn.cpp1505
-rw-r--r--src/debug/daccess/dacimpl.h4015
-rw-r--r--src/debug/daccess/datatargetadapter.cpp255
-rw-r--r--src/debug/daccess/datatargetadapter.h84
-rw-r--r--src/debug/daccess/dirs.proj19
-rw-r--r--src/debug/daccess/enummem.cpp2057
-rw-r--r--src/debug/daccess/fntableaccess.cpp461
-rw-r--r--src/debug/daccess/fntableaccess.h216
-rw-r--r--src/debug/daccess/i386/.gitmirror1
-rw-r--r--src/debug/daccess/i386/primitives.cpp11
-rw-r--r--src/debug/daccess/inspect.cpp3840
-rw-r--r--src/debug/daccess/nidump.cpp9579
-rw-r--r--src/debug/daccess/nidump.h624
-rw-r--r--src/debug/daccess/nidump.inl169
-rw-r--r--src/debug/daccess/reimpl.cpp115
-rw-r--r--src/debug/daccess/request.cpp4377
-rw-r--r--src/debug/daccess/request_svr.cpp348
-rw-r--r--src/debug/daccess/stack.cpp1434
-rw-r--r--src/debug/daccess/stdafx.cpp12
-rw-r--r--src/debug/daccess/stdafx.h111
-rw-r--r--src/debug/daccess/task.cpp5335
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**)&notify2) != S_OK)
+ {
+ notify2 = NULL;
+ }
+
+ IXCLRDataExceptionNotification3* notify3;
+ if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification3),
+ (void**)&notify3) != S_OK)
+ {
+ notify3 = NULL;
+ }
+
+ IXCLRDataExceptionNotification4* notify4;
+ if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification4),
+ (void**)&notify4) != 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(&regDisp, &localContext);
+ if (!thread->GetFrame()->
+ TraceFrame(thread,
+ TRUE,
+ &trace,
+ &regDisp))
+ {
+ 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)&param, 0,
+ &handleType, 1,
+ mGenerationFilter, GCHeap::GetMaxGeneration(), 0);
+ else
+ HndEnumHandles(hTable, &handleType, 1, callback, (LPARAM)&param, 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(&regDisp, &context);
+ addr = GetControlPC(&regDisp);
+ 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(&regDisp);
+
+ 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, &currentRef, 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,
+ &paramToken);
+ if (status == S_OK)
+ {
+ status = mdImport->GetParamDefProps(
+ paramToken,
+ &seq,
+ &attr,
+ &paramName);
+ 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;
+ }
+}