1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 //*****************************************************************************
9 // ClrDataAccess implementation.
11 //*****************************************************************************
15 #include "typestring.h"
17 #include "debuginfostore.h"
18 #include "peimagelayout.inl"
19 #include "datatargetadapter.h"
20 #include "readonlydatatargetfacade.h"
21 #include "metadataexports.h"
25 #include "primitives.h"
28 #include <dactablerva.h>
31 #include "dwbucketmanager.hpp"
32 #include "gcinterface.dac.h"
34 // To include definiton of IsThrowableThreadAbortException
35 // #include <exstatecommon.h>
37 CRITICAL_SECTION g_dacCritSec;
38 ClrDataAccess* g_dacImpl;
39 HINSTANCE g_thisModule;
41 extern VOID STDMETHODCALLTYPE TLS_FreeMasterSlotIndex();
45 DLLEXPORT // For Win32 PAL LoadLibrary emulation
47 BOOL WINAPI DllMain(HANDLE instance, DWORD reason, LPVOID reserved)
49 static bool g_procInitialized = false;
53 case DLL_PROCESS_ATTACH:
55 if (g_procInitialized)
58 // Double initialization can happen on Unix
59 // in case of manual load of DAC shared lib and calling DllMain
60 // not a big deal, we just ignore it.
68 int err = PAL_InitializeDLL();
74 InitializeCriticalSection(&g_dacCritSec);
76 // Save the module handle.
77 g_thisModule = (HINSTANCE)instance;
79 g_procInitialized = true;
83 case DLL_PROCESS_DETACH:
84 // It's possible for this to be called without ATTACH completing (eg. if it failed)
85 if (g_procInitialized)
87 DeleteCriticalSection(&g_dacCritSec);
90 TLS_FreeMasterSlotIndex();
92 g_procInitialized = false;
106 ConvertUtf8(__in LPCUTF8 utf8,
109 __out_ecount_part_opt(bufLen, *nameLen) PWSTR buffer)
113 *nameLen = WszMultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
116 return HRESULT_FROM_GetLastError();
120 if (buffer && bufLen)
122 if (!WszMultiByteToWideChar(CP_UTF8, 0, utf8, -1, buffer, bufLen))
124 return HRESULT_FROM_GetLastError();
132 AllocUtf8(__in_opt LPCWSTR wstr,
134 __deref_out LPUTF8* utf8)
136 ULONG32 chars = WszWideCharToMultiByte(CP_UTF8, 0, wstr, srcChars,
137 NULL, 0, NULL, NULL);
140 return HRESULT_FROM_GetLastError();
143 // Make sure the converted string is always terminated.
144 if (srcChars != (ULONG32)-1)
146 if (!ClrSafeInt<ULONG32>::addition(chars, 1, chars))
148 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
152 char* mem = new (nothrow) char[chars];
155 return E_OUTOFMEMORY;
158 if (!WszWideCharToMultiByte(CP_UTF8, 0, wstr, srcChars,
159 mem, chars, NULL, NULL))
161 HRESULT hr = HRESULT_FROM_GetLastError();
166 if (srcChars != (ULONG32)-1)
176 GetFullClassNameFromMetadata(IMDInternalImport* mdImport,
177 mdTypeDef classToken,
179 __inout_ecount(bufferChars) LPUTF8 buffer)
182 LPCUTF8 baseName, namespaceName;
184 IfFailRet(mdImport->GetNameOfTypeDef(classToken, &baseName, &namespaceName));
185 return ns::MakePath(buffer, bufferChars, namespaceName, baseName) ?
186 S_OK : E_OUTOFMEMORY;
190 GetFullMethodNameFromMetadata(IMDInternalImport* mdImport,
191 mdMethodDef methodToken,
193 __inout_ecount(bufferChars) LPUTF8 buffer)
197 mdTypeDef classToken;
200 if (mdImport->GetParentToken(methodToken, &classToken) == S_OK)
203 GetFullClassNameFromMetadata(mdImport, classToken,
204 bufferChars, buffer)) != S_OK)
209 len = strlen(buffer);
211 bufferChars -= static_cast<ULONG32>(len) + 1;
215 return E_OUTOFMEMORY;
218 *buffer++ = NAMESPACE_SEPARATOR_CHAR;
222 IfFailRet(mdImport->GetNameOfMethodDef(methodToken, &methodName));
223 // Review conversion of size_t to ULONG32.
225 #pragma warning(push)
226 #pragma warning(disable:4267)
228 len = strlen(methodName);
232 if (len >= bufferChars)
234 return E_OUTOFMEMORY;
237 strcpy_s(buffer, bufferChars, methodName);
242 SplitFullName(__in_z __in PCWSTR fullName,
245 __deref_out_opt LPUTF8* namespaceName,
246 __deref_out_opt LPUTF8* typeName,
247 __deref_out_opt LPUTF8* memberName,
248 __deref_out_opt LPUTF8* params)
251 PCWSTR paramsStart, memberStart, memberEnd, typeStart;
259 // Split off parameters.
262 paramsStart = wcschr(fullName, W('('));
265 if (syntax != SPLIT_METHOD ||
266 paramsStart == fullName)
271 if ((status = AllocUtf8(paramsStart, (ULONG32)-1, params)) != S_OK)
276 memberEnd = paramsStart - 1;
281 memberEnd = fullName + (wcslen(fullName) - 1);
284 if (syntax != SPLIT_TYPE)
287 // Split off member name.
290 memberStart = memberEnd;
294 while (memberStart >= fullName &&
295 *memberStart != W('.'))
300 // Some member names (e.g. .ctor and .dtor) have
301 // dots, so go back to the first dot.
302 while (memberStart > fullName &&
303 memberStart[-1] == W('.'))
308 if (memberStart <= fullName)
312 // Caller expected dots in the
313 // member name and they weren't found.
314 status = E_INVALIDARG;
320 else if (memberDots == 0)
330 if (memberStart > memberEnd)
332 status = E_INVALIDARG;
336 if ((status = AllocUtf8(memberStart, (ULONG32)
337 (memberEnd - memberStart) + 1,
338 memberName)) != S_OK)
346 memberStart = memberEnd + 2;
350 // Split off type name.
353 if (memberStart > fullName)
355 // Must have at least one character for the type
356 // name. If there was a member name, there must
357 // also be a separator.
358 if (memberStart < fullName + 2)
360 status = E_INVALIDARG;
364 typeStart = memberStart - 2;
365 while (typeStart >= fullName &&
366 *typeStart != W('.'))
372 if ((status = AllocUtf8(typeStart, (ULONG32)
373 (memberStart - typeStart) - 1,
382 typeStart = fullName;
386 // Namespace must be the rest.
389 if (typeStart > fullName)
391 if ((status = AllocUtf8(fullName, (ULONG32)
392 (typeStart - fullName) - 1,
393 namespaceName)) != S_OK)
400 *namespaceName = NULL;
406 delete [] (*typeName);
408 delete [] (*memberName);
415 CompareUtf8(__in LPCUTF8 str1, __in LPCUTF8 str2, __in ULONG32 nameFlags)
417 if (nameFlags & CLRDATA_BYNAME_CASE_INSENSITIVE)
419 // XXX Microsoft - Convert to Unicode?
420 return SString::_stricmp(str1, str2);
423 return strcmp(str1, str2);
426 //----------------------------------------------------------------------------
430 //----------------------------------------------------------------------------
433 MetaEnum::Start(IMDInternalImport* mdImport, ULONG32 kind,
441 status = mdImport->EnumTypeDefInit(&m_enum);
445 status = mdImport->EnumInit(kind, container, &m_enum);
455 m_mdImport = mdImport;
472 m_mdImport->EnumTypeDefClose(&m_enum);
476 m_mdImport->EnumClose(&m_enum);
484 MetaEnum::NextToken(mdToken* token,
485 __deref_opt_out_opt LPCUTF8* namespaceName,
486 __deref_opt_out_opt LPCUTF8* name)
497 if (!m_mdImport->EnumTypeDefNext(&m_enum, token))
501 m_lastToken = *token;
502 if (namespaceName || name)
504 LPCSTR _name, _namespaceName;
506 IfFailRet(m_mdImport->GetNameOfTypeDef(*token, &_name, &_namespaceName));
509 *namespaceName = _namespaceName;
519 if (!m_mdImport->EnumNext(&m_enum, token))
523 m_lastToken = *token;
526 *namespaceName = NULL;
530 IfFailRet(m_mdImport->GetNameOfMethodDef(*token, name));
535 if (!m_mdImport->EnumNext(&m_enum, token))
539 m_lastToken = *token;
542 *namespaceName = NULL;
546 IfFailRet(m_mdImport->GetNameOfFieldDef(*token, name));
556 MetaEnum::NextDomainToken(AppDomain** appDomain,
563 // Use only the caller-provided app domain.
564 *appDomain = m_appDomain;
565 return NextToken(token, NULL, NULL);
569 // Splay tokens across all app domains.
574 if (m_lastToken == mdTokenNil)
576 // Need to fetch a token.
577 if ((status = NextToken(token, NULL, NULL)) != S_OK)
585 if (m_domainIter.Next())
590 m_lastToken = mdTokenNil;
593 *appDomain = m_domainIter.GetDomain();
594 *token = m_lastToken;
600 MetaEnum::NextTokenByName(__in_opt LPCUTF8 namespaceName,
601 __in_opt LPCUTF8 name,
606 LPCUTF8 tokNamespace, tokName;
610 if ((status = NextToken(token, &tokNamespace, &tokName)) != S_OK)
617 CompareUtf8(namespaceName, tokNamespace, nameFlags) != 0))
623 CompareUtf8(name, tokName, nameFlags) != 0))
633 MetaEnum::NextDomainTokenByName(__in_opt LPCUTF8 namespaceName,
634 __in_opt LPCUTF8 name,
636 AppDomain** appDomain, mdToken* token)
642 // Use only the caller-provided app domain.
643 *appDomain = m_appDomain;
644 return NextTokenByName(namespaceName, name, nameFlags, token);
648 // Splay tokens across all app domains.
653 if (m_lastToken == mdTokenNil)
655 // Need to fetch a token.
656 if ((status = NextTokenByName(namespaceName, name, nameFlags,
665 if (m_domainIter.Next())
670 m_lastToken = mdTokenNil;
673 *appDomain = m_domainIter.GetDomain();
674 *token = m_lastToken;
680 MetaEnum::New(Module* mod,
683 IXCLRDataAppDomain* pubAppDomain,
684 MetaEnum** metaEnumRet,
685 CLRDATA_ENUM* handle)
692 *handle = TO_CDENUM(NULL);
695 if (!mod->GetFile()->HasMetadata())
700 metaEnum = new (nothrow) MetaEnum;
703 return E_OUTOFMEMORY;
706 if ((status = metaEnum->
707 Start(mod->GetMDImport(), kind, container)) != S_OK)
715 metaEnum->m_appDomain =
716 ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain();
721 *metaEnumRet = metaEnum;
725 *handle = TO_CDENUM(metaEnum);
730 //----------------------------------------------------------------------------
734 //----------------------------------------------------------------------------
736 SplitName::SplitName(SplitSyntax syntax, ULONG32 nameFlags,
740 m_nameFlags = nameFlags;
741 m_memberDots = memberDots;
747 SplitName::Delete(void)
749 delete [] m_namespaceName;
750 m_namespaceName = NULL;
751 delete [] m_typeName;
753 delete [] m_memberName;
760 SplitName::Clear(void)
762 m_namespaceName = NULL;
764 m_typeToken = mdTypeDefNil;
766 m_memberToken = mdTokenNil;
770 m_metaEnum.m_appDomain = NULL;
776 SplitName::SplitString(__in_opt PCWSTR fullName)
778 if (m_syntax == SPLIT_NO_NAME)
792 return SplitFullName(fullName,
802 WCHAR* wcrscan(LPCWSTR beg, LPCWSTR end, WCHAR ch)
804 //_ASSERTE(beg <= end);
806 for (p = (WCHAR*)end; p >= beg; --p)
814 // This functions allocates a new UTF8 string that contains the classname
815 // lying between the current sepName and the previous sepName. E.g. for a
816 // class name of "Outer+middler+inner" when sepName points to the NULL
817 // terminator this function will return "inner" in pResult and will update
818 // sepName to point to the second '+' character in the string. When sepName
819 // points to the first '+' character this function will return "Outer" in
820 // pResult and sepName will point one WCHAR before fullName.
821 HRESULT NextEnclosingClasName(LPCWSTR fullName, __deref_inout LPWSTR& sepName, __deref_out LPUTF8 *pResult)
823 if (sepName < fullName)
827 //_ASSERTE(*sepName == W('\0') || *sepName == W('+') || *sepName == W('/'));
829 LPWSTR origInnerName = sepName-1;
830 if ((sepName = wcrscan(fullName, origInnerName, W('+'))) < fullName)
832 sepName = wcrscan(fullName, origInnerName, W('/'));
835 return AllocUtf8(sepName+1, static_cast<ULONG32>(origInnerName-sepName), pResult);
839 SplitName::FindType(IMDInternalImport* mdInternal)
841 if (m_typeToken != mdTypeDefNil)
851 if ((m_namespaceName == NULL || m_namespaceName[0] == '\0')
852 && (CompareUtf8(COR_MODULE_CLASS, m_typeName, m_nameFlags)==0))
854 m_typeToken = TokenFromRid(1, mdtTypeDef); // <Module> class always has a RID of 1.
860 if (metaEnum.Start(mdInternal, mdtTypeDef, mdTypeDefNil) != S_OK)
868 WCHAR wszName[MAX_CLASS_NAME];
869 ConvertUtf8(m_typeName, MAX_CLASS_NAME, &length, wszName);
875 pHead = wszName + length;
877 if (FAILED(NextEnclosingClasName(wszName, pHead, &curClassName)))
882 // an inner class has an empty namespace associated with it
883 HRESULT hr = metaEnum.NextTokenByName((pHead < wszName) ? m_namespaceName : "",
887 delete[] curClassName;
891 // if we didn't find a token with the given name
894 else if (pHead < wszName)
896 // if we did find a token, *and* the class name given
897 // does not specify any enclosing class, that's it
902 // restart with innermost class
903 pHead = wszName + length;
904 mdTypeDef tkInner = m_typeToken;
911 && SUCCEEDED(NextEnclosingClasName(wszName, pHead, &utf8Name))
914 if (mdInternal->GetNestedClassProps(tkInner, &tkOuter) != S_OK)
915 tkOuter = mdTypeDefNil;
918 if (FAILED(mdInternal->GetNameOfTypeDef(tkInner, &szName, &szNS)))
922 bRetry = (CompareUtf8(utf8Name, szName, m_nameFlags) != 0);
925 // if this is outermost class we need to compare namespaces too
926 if (tkOuter == mdTypeDefNil)
928 // is this the outermost in the class name, too?
930 && CompareUtf8(m_namespaceName ? m_namespaceName : "", szNS, m_nameFlags) == 0)
951 SplitName::FindMethod(IMDInternalImport* mdInternal)
953 if (m_memberToken != mdTokenNil)
958 if (m_typeToken == mdTypeDefNil ||
964 ULONG32 EmptySig = 0;
966 // XXX Microsoft - Compare using signature when available.
967 if (mdInternal->FindMethodDefUsingCompare(m_typeToken,
969 (PCCOR_SIGNATURE)&EmptySig,
973 &m_memberToken) != S_OK)
975 m_memberToken = mdTokenNil;
983 SplitName::FindField(IMDInternalImport* mdInternal)
985 if (m_memberToken != mdTokenNil)
990 if (m_typeToken == mdTypeDefNil ||
994 // Can't have params with a field.
1000 if (metaEnum.Start(mdInternal, mdtFieldDef, m_typeToken) != S_OK)
1005 return metaEnum.NextTokenByName(NULL,
1008 &m_memberToken) == S_OK;
1012 SplitName::AllocAndSplitString(__in_opt PCWSTR fullName,
1020 if (nameFlags & ~(CLRDATA_BYNAME_CASE_SENSITIVE |
1021 CLRDATA_BYNAME_CASE_INSENSITIVE))
1023 return E_INVALIDARG;
1026 *split = new (nothrow) SplitName(syntax, nameFlags, memberDots);
1029 return E_OUTOFMEMORY;
1032 if ((status = (*split)->SplitString(fullName)) != S_OK)
1042 SplitName::CdStartMethod(__in_opt PCWSTR fullName,
1045 mdTypeDef typeToken,
1046 AppDomain* appDomain,
1047 IXCLRDataAppDomain* pubAppDomain,
1048 SplitName** splitRet,
1049 CLRDATA_ENUM* handle)
1055 *handle = TO_CDENUM(NULL);
1058 if ((status = SplitName::
1059 AllocAndSplitString(fullName, SPLIT_METHOD, nameFlags,
1060 methDots, &split)) != S_OK)
1065 if (typeToken == mdTypeDefNil)
1067 if (!split->FindType(mod->GetMDImport()))
1069 bool hasNamespace = split->m_namespaceName != NULL;
1074 // We may have a case where there's an
1075 // explicitly implemented method which
1076 // has dots in the name. If it's possible
1077 // to move the method name dot split
1078 // back, go ahead and retry that way.
1087 return E_INVALIDARG;
1090 typeToken = split->m_typeToken;
1094 if (split->m_namespaceName || split->m_typeName)
1097 return E_INVALIDARG;
1101 if ((status = split->m_metaEnum.
1102 Start(mod->GetMDImport(), mdtMethodDef, typeToken)) != S_OK)
1108 split->m_metaEnum.m_appDomain = appDomain;
1111 split->m_metaEnum.m_appDomain =
1112 ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain();
1114 split->m_module = mod;
1116 *handle = TO_CDENUM(split);
1125 SplitName::CdNextMethod(CLRDATA_ENUM* handle,
1128 SplitName* split = FROM_CDENUM(SplitName, *handle);
1131 return E_INVALIDARG;
1134 return split->m_metaEnum.
1135 NextTokenByName(NULL, split->m_memberName, split->m_nameFlags,
1140 SplitName::CdNextDomainMethod(CLRDATA_ENUM* handle,
1141 AppDomain** appDomain,
1144 SplitName* split = FROM_CDENUM(SplitName, *handle);
1147 return E_INVALIDARG;
1150 return split->m_metaEnum.
1151 NextDomainTokenByName(NULL, split->m_memberName, split->m_nameFlags,
1156 SplitName::CdStartField(__in_opt PCWSTR fullName,
1159 IXCLRDataTypeInstance* fromTypeInst,
1160 TypeHandle typeHandle,
1162 mdTypeDef typeToken,
1165 IXCLRDataTask* pubTlsThread,
1166 AppDomain* appDomain,
1167 IXCLRDataAppDomain* pubAppDomain,
1168 SplitName** splitRet,
1169 CLRDATA_ENUM* handle)
1174 *handle = TO_CDENUM(NULL);
1176 if ((status = SplitName::
1177 AllocAndSplitString(fullName,
1178 fullName ? SPLIT_FIELD : SPLIT_NO_NAME,
1185 if (typeHandle.IsNull())
1187 if (typeToken == mdTypeDefNil)
1189 if (!split->FindType(mod->GetMDImport()))
1191 status = E_INVALIDARG;
1195 typeToken = split->m_typeToken;
1199 if (split->m_namespaceName || split->m_typeName)
1201 status = E_INVALIDARG;
1206 // With phased class loading, this may return a partially-loaded type
1207 // @todo : does this matter?
1208 typeHandle = mod->LookupTypeDef(split->m_typeToken);
1209 if (typeHandle.IsNull())
1211 status = E_UNEXPECTED;
1216 if ((status = InitFieldIter(&split->m_fieldEnum,
1220 fromTypeInst)) != S_OK)
1225 split->m_objBase = objBase;
1226 split->m_tlsThread = tlsThread;
1229 split->m_tlsThread = ((ClrDataTask*)pubTlsThread)->GetThread();
1231 split->m_metaEnum.m_appDomain = appDomain;
1234 split->m_metaEnum.m_appDomain =
1235 ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain();
1237 split->m_module = mod;
1239 *handle = TO_CDENUM(split);
1252 SplitName::CdNextField(ClrDataAccess* dac,
1253 CLRDATA_ENUM* handle,
1254 IXCLRDataTypeDefinition** fieldType,
1255 ULONG32* fieldFlags,
1256 IXCLRDataValue** value,
1257 ULONG32 nameBufRetLen,
1258 ULONG32* nameLenRet,
1259 __out_ecount_part_opt(nameBufRetLen, *nameLenRet) WCHAR nameBufRet[ ],
1260 IXCLRDataModule** tokenScopeRet,
1261 mdFieldDef* tokenRet)
1265 SplitName* split = FROM_CDENUM(SplitName, *handle);
1268 return E_INVALIDARG;
1271 FieldDesc* fieldDesc;
1273 while ((fieldDesc = split->m_fieldEnum.Next()))
1275 if (split->m_syntax != SPLIT_NO_NAME)
1278 if (FAILED(fieldDesc->GetName_NoThrow(&fieldName)) ||
1279 (split->Compare(split->m_memberName, fieldName) != 0))
1285 split->m_lastField = fieldDesc;
1287 if (fieldFlags != NULL)
1290 GetTypeFieldValueFlags(fieldDesc->GetFieldTypeHandleThrowing(),
1293 IsFieldFromParentClass() ?
1294 CLRDATA_FIELD_IS_INHERITED : 0,
1298 if ((nameBufRetLen != 0) || (nameLenRet != NULL))
1300 LPCUTF8 szFieldName;
1301 status = fieldDesc->GetName_NoThrow(&szFieldName);
1307 status = ConvertUtf8(
1318 if (tokenScopeRet && !value)
1320 *tokenScopeRet = new (nothrow)
1321 ClrDataModule(dac, fieldDesc->GetModule());
1322 if (!*tokenScopeRet)
1324 return E_OUTOFMEMORY;
1330 *tokenRet = fieldDesc->GetMemberDef();
1335 TypeHandle fieldTypeHandle = fieldDesc->GetFieldTypeHandleThrowing();
1336 *fieldType = new (nothrow)
1337 ClrDataTypeDefinition(dac,
1338 fieldTypeHandle.GetModule(),
1339 fieldTypeHandle.GetMethodTable()->GetCl(),
1341 if (!*fieldType && tokenScopeRet)
1343 delete (ClrDataModule*)*tokenScopeRet;
1345 return *fieldType ? S_OK : E_OUTOFMEMORY;
1350 return ClrDataValue::
1351 NewFromFieldDesc(dac,
1352 split->m_metaEnum.m_appDomain,
1353 split->m_fieldEnum.IsFieldFromParentClass() ?
1354 CLRDATA_VALUE_IS_INHERITED : 0,
1374 SplitName::CdNextDomainField(ClrDataAccess* dac,
1375 CLRDATA_ENUM* handle,
1376 IXCLRDataValue** value)
1380 SplitName* split = FROM_CDENUM(SplitName, *handle);
1383 return E_INVALIDARG;
1386 if (split->m_metaEnum.m_appDomain)
1388 // Use only the caller-provided app domain.
1389 return CdNextField(dac, handle, NULL, NULL, value,
1390 0, NULL, NULL, NULL, NULL);
1394 // Splay fields across all app domains.
1399 if (!split->m_lastField)
1401 // Need to fetch a field.
1402 if ((status = CdNextField(dac, handle, NULL, NULL, NULL,
1403 0, NULL, NULL, NULL, NULL)) != S_OK)
1408 split->m_metaEnum.m_domainIter.Init();
1411 if (split->m_metaEnum.m_domainIter.Next())
1416 split->m_lastField = NULL;
1419 return ClrDataValue::
1420 NewFromFieldDesc(dac,
1421 split->m_metaEnum.m_domainIter.GetDomain(),
1422 split->m_fieldEnum.IsFieldFromParentClass() ?
1423 CLRDATA_VALUE_IS_INHERITED : 0,
1437 SplitName::CdStartType(__in_opt PCWSTR fullName,
1440 AppDomain* appDomain,
1441 IXCLRDataAppDomain* pubAppDomain,
1442 SplitName** splitRet,
1443 CLRDATA_ENUM* handle)
1448 *handle = TO_CDENUM(NULL);
1450 if ((status = SplitName::
1451 AllocAndSplitString(fullName, SPLIT_TYPE, nameFlags, 0,
1457 if ((status = split->m_metaEnum.
1458 Start(mod->GetMDImport(), mdtTypeDef, mdTokenNil)) != S_OK)
1464 split->m_metaEnum.m_appDomain = appDomain;
1467 split->m_metaEnum.m_appDomain =
1468 ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain();
1470 split->m_module = mod;
1472 *handle = TO_CDENUM(split);
1481 SplitName::CdNextType(CLRDATA_ENUM* handle,
1484 SplitName* split = FROM_CDENUM(SplitName, *handle);
1487 return E_INVALIDARG;
1490 return split->m_metaEnum.
1491 NextTokenByName(split->m_namespaceName, split->m_typeName,
1492 split->m_nameFlags, token);
1496 SplitName::CdNextDomainType(CLRDATA_ENUM* handle,
1497 AppDomain** appDomain,
1500 SplitName* split = FROM_CDENUM(SplitName, *handle);
1503 return E_INVALIDARG;
1506 return split->m_metaEnum.
1507 NextDomainTokenByName(split->m_namespaceName, split->m_typeName,
1508 split->m_nameFlags, appDomain, token);
1511 //----------------------------------------------------------------------------
1513 // DacInstanceManager.
1515 // Data retrieved from the target process is cached for two reasons:
1517 // 1. It may be necessary to map from the host address back to the target
1518 // address. For example, if any code uses a 'this' pointer or
1519 // takes the address of a field the address has to be translated from
1520 // host to target. This requires instances to be held as long as
1521 // they may be referenced.
1523 // 2. Data is often referenced multiple times so caching is an important
1524 // performance advantage.
1526 // Ideally we'd like to implement a simple page cache but this is
1527 // complicated by the fact that user minidump memory can have
1528 // arbitrary granularity and also that the member operator (->)
1529 // needs to return a pointer to an object. That means that all of
1530 // the data for an object must be sequential and cannot be split
1531 // at page boundaries.
1533 // Data can also be accessed with different sizes. For example,
1534 // a base struct can be accessed, then cast to a derived struct and
1535 // accessed again with the larger derived size. The cache must
1536 // be able to replace data to maintain the largest amount of data
1539 // We keep track of each access and the recovered memory for it.
1540 // A hash on target address allows quick access to instance data
1541 // by target address. The data for each access has a header on it
1542 // for bookkeeping purposes, so host address to target address translation
1543 // is just a matter of backing up to the header and pulling the target
1544 // address from it. Keeping each access separately allows easy
1545 // replacement by larger accesses.
1547 //----------------------------------------------------------------------------
1549 DacInstanceManager::DacInstanceManager(void)
1550 : m_unusedBlock(NULL)
1555 DacInstanceManager::~DacInstanceManager(void)
1557 // We are stopping debugging in this case, so don't save any block of memory.
1558 // Otherwise, there will be a memory leak.
1562 #if defined(DAC_HASHTABLE)
1564 DacInstanceManager::Add(DAC_INSTANCE* inst)
1566 // Assert that we don't add NULL instances. This allows us to assert that found instances
1567 // are not NULL in DacInstanceManager::Find
1568 _ASSERTE(inst != NULL);
1570 DWORD nHash = DAC_INSTANCE_HASH(inst->addr);
1571 HashInstanceKeyBlock* block = m_hash[nHash];
1573 if (!block || block->firstElement == 0)
1576 HashInstanceKeyBlock* newBlock;
1579 newBlock = (HashInstanceKeyBlock*) new (nothrow) BYTE[HASH_INSTANCE_BLOCK_ALLOC_SIZE];
1583 // We allocate one big memory chunk that has a block for every index of the hash table to
1584 // improve data locality and reduce the number of allocs. In most cases, a hash bucket will
1585 // use only one block, so improving data locality across blocks (i.e. keeping the buckets of the
1586 // hash table together) should help.
1587 newBlock = (HashInstanceKeyBlock*)
1588 ClrVirtualAlloc(NULL, HASH_INSTANCE_BLOCK_ALLOC_SIZE*NumItems(m_hash), MEM_COMMIT, PAGE_READWRITE);
1596 // We add the newest block to the start of the list assuming that most accesses are for
1597 // recently added elements.
1598 newBlock->next = block;
1599 m_hash[nHash] = newBlock; // The previously allocated block
1600 newBlock->firstElement = HASH_INSTANCE_BLOCK_NUM_ELEMENTS;
1605 for (DWORD j = 0; j < NumItems(m_hash); j++)
1607 m_hash[j] = newBlock;
1608 newBlock->next = NULL; // The previously allocated block
1609 newBlock->firstElement = HASH_INSTANCE_BLOCK_NUM_ELEMENTS;
1610 newBlock = (HashInstanceKeyBlock*) (((BYTE*) newBlock) + HASH_INSTANCE_BLOCK_ALLOC_SIZE);
1612 block = m_hash[nHash];
1615 _ASSERTE(block->firstElement > 0);
1616 block->firstElement--;
1617 block->instanceKeys[block->firstElement].addr = inst->addr;
1618 block->instanceKeys[block->firstElement].instance = inst;
1623 #else //DAC_HASHTABLE
1625 DacInstanceManager::Add(DAC_INSTANCE* inst)
1627 _ASSERTE(inst != NULL);
1629 bool isInserted = (m_hash.find(inst->addr) == m_hash.end());
1631 DAC_INSTANCE *(&target) = m_hash[inst->addr];
1632 _ASSERTE(!isInserted || target == NULL);
1633 if( target != NULL )
1635 //This is necessary to preserve the semantics of Supersede, however, it
1636 //is more or less dead code.
1637 inst->next = target;
1640 //verify descending order
1641 _ASSERTE(inst->size >= target->size);
1651 #endif // #if defined(DAC_HASHTABLE)
1655 DacInstanceManager::Alloc(TADDR addr, ULONG32 size, DAC_USAGE_TYPE usage)
1657 SUPPORTS_DAC_HOST_ONLY;
1658 DAC_INSTANCE_BLOCK* block;
1662 static_assert_no_msg(sizeof(DAC_INSTANCE_BLOCK) <= DAC_INSTANCE_ALIGN);
1663 static_assert_no_msg((sizeof(DAC_INSTANCE) & (DAC_INSTANCE_ALIGN - 1)) == 0);
1666 // All allocated instances must be kept alive as long
1667 // as anybody may have a host pointer for one of them.
1668 // This means that we cannot delete an arbitrary instance
1669 // unless we are sure no pointers exist, which currently
1670 // is not possible to determine, thus we just hold everything
1671 // until a Flush. This greatly simplifies instance allocation
1672 // as we can then just sweep through large blocks rather
1673 // than having to use a real allocator. The only
1674 // complication is that we need to keep all instance
1675 // data aligned. We have guaranteed that the header will
1676 // preserve alignment of the data following if the header
1677 // is aligned, so as long as we round up all allocations
1678 // to a multiple of the alignment size everything just works.
1681 fullSize = (size + DAC_INSTANCE_ALIGN - 1) & ~(DAC_INSTANCE_ALIGN - 1);
1682 _ASSERTE(fullSize && fullSize <= 0xffffffff - 2 * sizeof(*inst));
1683 fullSize += sizeof(*inst);
1686 // Check for an existing block with space.
1689 for (block = m_blocks; block; block = block->next)
1691 if (fullSize <= block->bytesFree)
1700 // No existing block has enough space, so allocate a new
1701 // one if necessary and link it in. We know we're allocating large
1702 // blocks so directly VirtualAlloc. We save one block through a
1703 // flush so that we spend less time allocating/deallocating.
1706 ULONG32 blockSize = fullSize + DAC_INSTANCE_ALIGN;
1707 if (blockSize < DAC_INSTANCE_BLOCK_ALLOCATION)
1709 blockSize = DAC_INSTANCE_BLOCK_ALLOCATION;
1712 // If we have a saved block and it's large enough, use it.
1713 block = m_unusedBlock;
1714 if ((block != NULL) &&
1715 ((block->bytesUsed + block->bytesFree) >= blockSize))
1717 m_unusedBlock = NULL;
1719 // Right now, we're locked to DAC_INSTANCE_BLOCK_ALLOCATION but
1720 // that might change in the future if we decide to do something
1721 // else with the size guarantee in code:DacInstanceManager::FreeAllBlocks
1722 blockSize = block->bytesUsed + block->bytesFree;
1726 block = (DAC_INSTANCE_BLOCK*)
1727 ClrVirtualAlloc(NULL, blockSize, MEM_COMMIT, PAGE_READWRITE);
1735 // Keep the first aligned unit for the block header.
1736 block->bytesUsed = DAC_INSTANCE_ALIGN;
1737 block->bytesFree = blockSize - DAC_INSTANCE_ALIGN;
1739 block->next = m_blocks;
1742 m_blockMemUsage += blockSize;
1745 inst = (DAC_INSTANCE*)((PBYTE)block + block->bytesUsed);
1746 block->bytesUsed += fullSize;
1747 _ASSERTE(block->bytesFree >= fullSize);
1748 block->bytesFree -= fullSize;
1753 inst->sig = DAC_INSTANCE_SIG;
1754 inst->usage = usage;
1759 m_instMemUsage += fullSize;
1764 DacInstanceManager::ReturnAlloc(DAC_INSTANCE* inst)
1766 SUPPORTS_DAC_HOST_ONLY;
1767 DAC_INSTANCE_BLOCK* block;
1768 DAC_INSTANCE_BLOCK * pPrevBlock;
1772 // This special routine handles cleanup in
1773 // cases where an instances has been allocated
1774 // but must be returned due to a following error.
1775 // The given instance must be the last instance
1776 // in an existing block.
1780 ((inst->size + DAC_INSTANCE_ALIGN - 1) & ~(DAC_INSTANCE_ALIGN - 1)) +
1784 for (block = m_blocks; block; pPrevBlock = block, block = block->next)
1786 if ((PBYTE)inst == (PBYTE)block + (block->bytesUsed - fullSize))
1797 block->bytesUsed -= fullSize;
1798 block->bytesFree += fullSize;
1800 m_instMemUsage -= fullSize;
1802 // If the block is empty after returning the specified instance, that means this block was newly created
1803 // when this instance was allocated. We have seen cases where we are asked to allocate a
1804 // large chunk of memory only to fail to read the memory from a dump later on, i.e. when both the target
1805 // address and the size are invalid. If we keep the allocation, we'll grow the VM size unnecessarily.
1806 // Thus, release a block if it's empty and if it's not the default size (to avoid thrashing memory).
1807 // See Dev10 Dbug 812112 for more information.
1808 if ((block->bytesUsed == DAC_INSTANCE_ALIGN) &&
1809 ((block->bytesFree + block->bytesUsed) != DAC_INSTANCE_BLOCK_ALLOCATION))
1811 // The empty block is at the beginning of the list.
1812 if (pPrevBlock == NULL)
1814 m_blocks = block->next;
1818 _ASSERTE(pPrevBlock->next == block);
1819 pPrevBlock->next = block->next;
1821 ClrVirtualFree(block, 0, MEM_RELEASE);
1826 #if defined(DAC_HASHTABLE)
1828 DacInstanceManager::Find(TADDR addr)
1831 #if defined(DAC_MEASURE_PERF)
1832 unsigned _int64 nStart, nEnd;
1834 nStart = GetCycleCount();
1835 #endif // #if defined(DAC_MEASURE_PERF)
1837 HashInstanceKeyBlock* block = m_hash[DAC_INSTANCE_HASH(addr)];
1839 #if defined(DAC_MEASURE_PERF)
1840 nEnd = GetCycleCount();
1841 g_nFindHashTotalTime += nEnd - nStart;
1842 #endif // #if defined(DAC_MEASURE_PERF)
1846 DWORD nIndex = block->firstElement;
1847 for (; nIndex < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; nIndex++)
1849 if (block->instanceKeys[nIndex].addr == addr)
1851 #if defined(DAC_MEASURE_PERF)
1852 nEnd = GetCycleCount();
1854 g_nFindTotalTime += nEnd - nStart;
1855 if (g_nStackWalk) g_nFindStackTotalTime += nEnd - nStart;
1856 #endif // #if defined(DAC_MEASURE_PERF)
1858 DAC_INSTANCE* inst = block->instanceKeys[nIndex].instance;
1860 // inst should not be NULL even if the address was superseded. We search
1861 // the entries in the reverse order they were added. So we should have
1862 // found the superseding entry before this one. (Of course, if a NULL instance
1863 // has been added, this assert is meaningless. DacInstanceManager::Add
1864 // asserts that NULL instances aren't added.)
1866 _ASSERTE(inst != NULL);
1871 block = block->next;
1874 #if defined(DAC_MEASURE_PERF)
1875 nEnd = GetCycleCount();
1877 g_nFindTotalTime += nEnd - nStart;
1878 if (g_nStackWalk) g_nFindStackTotalTime += nEnd - nStart;
1879 #endif // #if defined(DAC_MEASURE_PERF)
1883 #else //DAC_HASHTABLE
1885 DacInstanceManager::Find(TADDR addr)
1887 DacInstanceHashIterator iter = m_hash.find(addr);
1888 if( iter == m_hash.end() )
1894 return iter->second;
1897 #endif // if defined(DAC_HASHTABLE)
1900 DacInstanceManager::Write(DAC_INSTANCE* inst, bool throwEx)
1904 if (inst->usage == DAC_VPTR)
1906 // Skip over the host-side vtable pointer when
1908 status = DacWriteAll(inst->addr + sizeof(TADDR),
1909 (PBYTE)(inst + 1) + sizeof(PVOID),
1910 inst->size - sizeof(TADDR),
1915 // Write the whole instance back.
1916 status = DacWriteAll(inst->addr, inst + 1, inst->size, throwEx);
1922 #if defined(DAC_HASHTABLE)
1924 DacInstanceManager::Supersede(DAC_INSTANCE* inst)
1926 _ASSERTE(inst != NULL);
1929 // This instance has been superseded by a larger
1930 // one and so must be removed from the hash. However,
1931 // code may be holding the instance pointer so it
1932 // can't just be deleted. Put it on a list for
1936 HashInstanceKeyBlock* block = m_hash[DAC_INSTANCE_HASH(inst->addr)];
1939 DWORD nIndex = block->firstElement;
1940 for (; nIndex < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; nIndex++)
1942 if (block->instanceKeys[nIndex].instance == inst)
1944 block->instanceKeys[nIndex].instance = NULL;
1948 if (nIndex < HASH_INSTANCE_BLOCK_NUM_ELEMENTS)
1952 block = block->next;
1955 AddSuperseded(inst);
1957 #else //DAC_HASHTABLE
1959 DacInstanceManager::Supersede(DAC_INSTANCE* inst)
1961 _ASSERTE(inst != NULL);
1964 // This instance has been superseded by a larger
1965 // one and so must be removed from the hash. However,
1966 // code may be holding the instance pointer so it
1967 // can't just be deleted. Put it on a list for
1971 DacInstanceHashIterator iter = m_hash.find(inst->addr);
1972 if( iter == m_hash.end() )
1975 DAC_INSTANCE** bucket = &(iter->second);
1976 DAC_INSTANCE* cur = *bucket;
1977 DAC_INSTANCE* prev = NULL;
1978 //walk through the chain looking for this particular instance
1985 *bucket = inst->next;
1989 prev->next = inst->next;
1998 AddSuperseded(inst);
2000 #endif // if defined(DAC_HASHTABLE)
2002 // This is the default Flush() called when the DAC cache is invalidated,
2003 // e.g. when we continue the debuggee process. In this case, we want to
2004 // save one block of memory to avoid thrashing. See the usage of m_unusedBlock
2005 // for more information.
2006 void DacInstanceManager::Flush(void)
2011 void DacInstanceManager::Flush(bool fSaveBlock)
2013 SUPPORTS_DAC_HOST_ONLY;
2016 // All allocated memory is in the block
2017 // list, so just free the blocks and
2018 // forget all the internal pointers.
2023 FreeAllBlocks(fSaveBlock);
2025 DAC_INSTANCE_PUSH* push = m_instPushed;
2031 m_instPushed = push->next;
2032 m_blocks = push->blocks;
2036 // If we are not saving any memory blocks, then clear the saved buffer block (if any) as well.
2039 if (m_unusedBlock != NULL)
2041 ClrVirtualFree(m_unusedBlock, 0, MEM_RELEASE);
2042 m_unusedBlock = NULL;
2046 #if defined(DAC_HASHTABLE)
2047 for (int i = NumItems(m_hash) - 1; i >= 0; i--)
2049 HashInstanceKeyBlock* block = m_hash[i];
2050 HashInstanceKeyBlock* next;
2060 ClrVirtualFree(block, 0, MEM_RELEASE);
2065 #else //DAC_HASHTABLE
2067 #endif //DAC_HASHTABLE
2072 #if defined(DAC_HASHTABLE)
2074 DacInstanceManager::ClearEnumMemMarker(void)
2079 for (i = 0; i < NumItems(m_hash); i++)
2081 HashInstanceKeyBlock* block = m_hash[i];
2085 for (j = block->firstElement; j < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; j++)
2087 inst = block->instanceKeys[j].instance;
2093 block = block->next;
2096 for (inst = m_superseded; inst; inst = inst->next)
2101 #else //DAC_HASHTABLE
2103 DacInstanceManager::ClearEnumMemMarker(void)
2108 DacInstanceHashIterator end = m_hash.end();
2109 /* REVISIT_TODO Fri 10/20/2006
2110 * This might have an issue, since it might miss chained entries off of
2111 * ->next. However, ->next is going away, and for all intents and
2112 * purposes, this never happens.
2114 for( DacInstanceHashIterator cur = m_hash.begin(); cur != end; ++cur )
2116 cur->second->enumMem = 0;
2119 for (inst = m_superseded; inst; inst = inst->next)
2124 #endif // if defined(DAC_HASHTABLE)
2127 #if defined(DAC_HASHTABLE)
2130 // Iterating through all of the hash entry and report the memory
2131 // instance to minidump
2133 // This function returns the total number of bytes that it reported.
2137 DacInstanceManager::DumpAllInstances(
2138 ICLRDataEnumMemoryRegionsCallback *pCallBack) // memory report call back
2144 #if defined(DAC_MEASURE_PERF)
2145 FILE* fp = fopen("c:\\dumpLog.txt", "a");
2147 #endif // #if defined(DAC_MEASURE_PERF)
2149 for (i = 0; i < NumItems(m_hash); i++)
2152 #if defined(DAC_MEASURE_PERF)
2153 int numInBucket = 0;
2154 #endif // #if defined(DAC_MEASURE_PERF)
2156 HashInstanceKeyBlock* block = m_hash[i];
2160 for (j = block->firstElement; j < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; j++)
2162 inst = block->instanceKeys[j].instance;
2164 // Only report those we intended to.
2165 // So far, only metadata is excluded!
2167 if (inst && inst->noReport == 0)
2169 cbTotal += inst->size;
2170 HRESULT hr = pCallBack->EnumMemoryRegion(TO_CDADDR(inst->addr), inst->size);
2171 if (hr == COR_E_OPERATIONCANCELED)
2177 #if defined(DAC_MEASURE_PERF)
2182 #endif // #if defined(DAC_MEASURE_PERF)
2184 block = block->next;
2187 #if defined(DAC_MEASURE_PERF)
2188 fprintf(fp, "%4d: %4d%s", i, numInBucket, (i+1)%5? "; " : "\n");
2189 total += numInBucket;
2190 #endif // #if defined(DAC_MEASURE_PERF)
2194 #if defined(DAC_MEASURE_PERF)
2195 fprintf(fp, "\n\nTotal entries: %d\n\n", total);
2197 #endif // #if defined(DAC_MEASURE_PERF)
2202 #else //DAC_HASHTABLE
2205 // Iterating through all of the hash entry and report the memory
2206 // instance to minidump
2208 // This function returns the total number of bytes that it reported.
2212 DacInstanceManager::DumpAllInstances(
2213 ICLRDataEnumMemoryRegionsCallback *pCallBack) // memory report call back
2215 SUPPORTS_DAC_HOST_ONLY;
2220 #if defined(DAC_MEASURE_PERF)
2221 FILE* fp = fopen("c:\\dumpLog.txt", "a");
2222 #endif // #if defined(DAC_MEASURE_PERF)
2224 #if defined(DAC_MEASURE_PERF)
2225 int numInBucket = 0;
2226 #endif // #if defined(DAC_MEASURE_PERF)
2228 DacInstanceHashIterator end = m_hash.end();
2229 for (DacInstanceHashIterator cur = m_hash.begin(); end != cur; ++cur)
2233 // Only report those we intended to.
2234 // So far, only metadata is excluded!
2236 if (inst->noReport == 0)
2238 cbTotal += inst->size;
2239 HRESULT hr = pCallBack->EnumMemoryRegion(TO_CDADDR(inst->addr), inst->size);
2240 if (hr == COR_E_OPERATIONCANCELED)
2246 #if defined(DAC_MEASURE_PERF)
2248 #endif // #if defined(DAC_MEASURE_PERF)
2251 #if defined(DAC_MEASURE_PERF)
2252 fprintf(fp, "\n\nTotal entries: %d\n\n", numInBucket);
2254 #endif // #if defined(DAC_MEASURE_PERF)
2259 #endif // if defined(DAC_HASHTABLE)
2262 DacInstanceManager::FindInstanceBlock(DAC_INSTANCE* inst)
2264 for (DAC_INSTANCE_BLOCK* block = m_blocks; block; block = block->next)
2266 if ((PBYTE)inst >= (PBYTE)block &&
2267 (PBYTE)inst < (PBYTE)block + block->bytesUsed)
2276 // If fSaveBlock is false, free all blocks of allocated memory. Otherwise,
2277 // free all blocks except the one we save to avoid thrashing memory.
2278 // Callers very frequently flush repeatedly with little memory needed in DAC
2279 // so this avoids wasteful repeated allocations/deallocations.
2280 // There is a very unlikely case that we'll have allocated an extremely large
2281 // block; if this is the only block we will save none since this block will
2282 // remain allocated.
2284 DacInstanceManager::FreeAllBlocks(bool fSaveBlock)
2286 DAC_INSTANCE_BLOCK* block;
2288 while ((block = m_blocks))
2290 m_blocks = block->next;
2292 // If we haven't saved our single block yet and this block is the default size
2293 // then we will save it instead of freeing it. This avoids saving an unnecessarily large
2295 // Do *NOT* trash the byte counts. code:DacInstanceManager::Alloc
2296 // depends on them being correct when checking to see if a block is large enough.
2298 (m_unusedBlock == NULL) &&
2299 ((block->bytesFree + block->bytesUsed) == DAC_INSTANCE_BLOCK_ALLOCATION))
2301 // Just to avoid confusion, since we're keeping it around.
2303 m_unusedBlock = block;
2307 ClrVirtualFree(block, 0, MEM_RELEASE);
2312 //----------------------------------------------------------------------------
2314 // DacStreamManager.
2316 //----------------------------------------------------------------------------
2318 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
2320 namespace serialization { namespace bin {
2322 //========================================================================
2323 // Support functions for binary serialization of simple types to a buffer:
2324 // - raw_size() returns the size in bytes of the binary representation
2326 // - raw_serialize() copies the binary representation of a value into a
2328 // - raw_deserialize() generates a value from its binary representation
2330 // Beyond simple types the APIs below support SString instances. SStrings
2331 // are stored as UTF8 strings.
2332 //========================================================================
2334 static const size_t ErrOverflow = (size_t)(-1);
2338 // Template class is_blittable
2339 template <typename _Ty, typename Enable = void>
2342 { // determines whether _Ty is blittable
2345 template <typename _Ty>
2346 struct is_blittable<_Ty, typename std::enable_if<std::is_arithmetic<_Ty>::value>::type>
2348 { // determines whether _Ty is blittable
2351 // allow types to declare themselves blittable by including a static bool
2352 // member "is_blittable".
2353 template <typename _Ty>
2354 struct is_blittable<_Ty, typename std::enable_if<_Ty::is_blittable>::type>
2356 { // determines whether _Ty is blittable
2360 //========================================================================
2361 // serialization::bin::Traits<T> enables binary serialization and
2362 // deserialization of instances of T.
2363 //========================================================================
2366 // General specialization for non-blittable types - must be overridden
2367 // for each specific non-blittable type.
2369 template <typename T, typename Enable = void>
2373 static FORCEINLINE size_t
2374 raw_size(const T & val)
2376 static_assert(false, "Non-blittable types need explicit specializations");
2381 // General type trait supporting serialization/deserialization of blittable
2382 // type arguments (as defined by the is_blittable<> type traits above).
2384 template <typename T>
2385 class Traits<T, typename std::enable_if<is_blittable<T>::value>::type>
2387 #else // FEATURE_PAL
2388 template <typename T>
2391 #endif // !FEATURE_PAL
2394 // raw_size() returns the size in bytes of the binary representation of a
2397 static FORCEINLINE size_t
2398 raw_size(const T & val)
2404 // raw_serialize() copies the binary representation of a value into a
2405 // "dest" buffer that has "destSize" bytes available.
2406 // Returns raw_size(val), or ErrOverflow if the buffer does not have
2407 // enough space to accommodate "val".
2409 static FORCEINLINE size_t
2410 raw_serialize(BYTE* dest, size_t destSize, const T & val)
2412 size_t cnt = raw_size(val);
2419 memcpy_s(dest, destSize, &val, cnt);
2425 // raw_deserialize() generates a value "val" from its binary
2426 // representation in a buffer "src".
2427 // Returns raw_size(val), or ErrOverflow if the buffer does not have
2428 // enough space to accommodate "val".
2430 static FORCEINLINE size_t
2431 raw_deserialize(T & val, const BYTE* src, size_t srcSize)
2433 size_t cnt = raw_size(*(T*)src);
2440 memcpy_s(&val, cnt, src, cnt);
2448 // Specialization for UTF8 strings
2451 class Traits<LPCUTF8>
2454 static FORCEINLINE size_t
2455 raw_size(const LPCUTF8 & val)
2457 return strlen(val) + 1;
2460 static FORCEINLINE size_t
2461 raw_serialize(BYTE* dest, size_t destSize, const LPCUTF8 & val)
2463 size_t cnt = raw_size(val);
2470 memcpy_s(dest, destSize, &val, cnt);
2475 static FORCEINLINE size_t
2476 raw_deserialize(LPCUTF8 & val, const BYTE* src, size_t srcSize)
2478 size_t cnt = strnlen((LPCUTF8)src, srcSize) + 1;
2480 // assert we found a NULL terminated string at "src"
2486 // we won't allocate another buffer for this string
2495 // Specialization for SString.
2496 // SString serialization/deserialization is performed to/from a UTF8
2500 class Traits<SString>
2503 static FORCEINLINE size_t
2504 raw_size(const SString & val)
2507 val.ConvertToUTF8(s);
2508 // make sure to include the NULL terminator
2509 return s.GetCount() + 1;
2512 static FORCEINLINE size_t
2513 raw_serialize(BYTE* dest, size_t destSize, const SString & val)
2515 // instead of calling raw_size() we inline it here, so we can reuse
2516 // the UTF8 string obtained below as an argument to memcpy.
2519 val.ConvertToUTF8(s);
2520 // make sure to include the NULL terminator
2521 size_t cnt = s.GetCount() + 1;
2528 memcpy_s(dest, destSize, s.GetUTF8NoConvert(), cnt);
2533 static FORCEINLINE size_t
2534 raw_deserialize(SString & val, const BYTE* src, size_t srcSize)
2536 size_t cnt = strnlen((LPCUTF8)src, srcSize) + 1;
2538 // assert we found a NULL terminated string at "src"
2544 // a literal SString avoids a new allocation + copy
2545 SString sUtf8(SString::Utf8Literal, (LPCUTF8) src);
2546 sUtf8.ConvertToUnicode(val);
2555 // Specialization for SString-derived classes (like SStrings)
2557 template<typename T>
2558 class Traits<T, typename std::enable_if<std::is_base_of<SString, T>::value>::type>
2559 : public Traits<SString>
2562 #endif // !FEATURE_PAL
2565 // Convenience functions to allow argument type deduction
2567 template <typename T> FORCEINLINE
2568 size_t raw_size(const T & val)
2569 { return Traits<T>::raw_size(val); }
2571 template <typename T> FORCEINLINE
2572 size_t raw_serialize(BYTE* dest, size_t destSize, const T & val)
2573 { return Traits<T>::raw_serialize(dest, destSize, val); }
2575 template <typename T> FORCEINLINE
2576 size_t raw_deserialize(T & val, const BYTE* src, size_t srcSize)
2577 { return Traits<T>::raw_deserialize(val, src, srcSize); }
2580 enum StreamBuffState
2584 sbsOOM = sbsUnrecoverable,
2588 // OStreamBuff - Manages writing to an output buffer
2593 OStreamBuff(BYTE * _buff, size_t _buffsize)
2594 : buffsize(_buffsize)
2600 template <typename T>
2601 OStreamBuff& operator << (const T & val)
2603 if (sbs >= sbsUnrecoverable)
2606 size_t cnt = raw_serialize(buff+crt, buffsize-crt, val);
2607 if (cnt == ErrOverflow)
2619 inline size_t GetPos() const
2624 inline BOOL operator!() const
2626 return sbs >= sbsUnrecoverable;
2629 inline StreamBuffState State() const
2635 size_t buffsize; // size of buffer
2636 BYTE* buff; // buffer to stream to
2637 size_t crt; // current offset in buffer
2638 StreamBuffState sbs; // current state
2643 // OStreamBuff - Manages reading from an input buffer
2648 IStreamBuff(const BYTE* _buff, size_t _buffsize)
2649 : buffsize(_buffsize)
2655 template <typename T>
2656 IStreamBuff& operator >> (T & val)
2658 if (sbs >= sbsUnrecoverable)
2661 size_t cnt = raw_deserialize(val, buff+crt, buffsize-crt);
2662 if (cnt == ErrOverflow)
2674 inline size_t GetPos() const
2679 inline BOOL operator!() const
2681 return sbs >= sbsUnrecoverable;
2684 inline StreamBuffState State() const
2690 size_t buffsize; // size of buffer
2691 const BYTE * buff; // buffer to read from
2692 size_t crt; // current offset in buffer
2693 StreamBuffState sbs; // current state
2698 using serialization::bin::StreamBuffState;
2699 using serialization::bin::IStreamBuff;
2700 using serialization::bin::OStreamBuff;
2703 // Callback function type used by DacStreamManager to coordinate
2704 // amount of available memory between multiple streamable data
2705 // structures (e.g. DacEENamesStreamable)
2706 typedef bool (*Reserve_Fnptr)(DWORD size, void * writeState);
2710 // DacEENamesStreamable
2711 // Stores EE struct* -> Name mappings and streams them to a
2712 // streambuf when asked
2714 class DacEENamesStreamable
2717 // the hash map storing the interesting mappings of EE* -> Names
2718 MapSHash< TADDR, SString,
2719 NoRemoveSHashTraits <
2720 NonDacAwareSHashTraits< MapSHashTraits <TADDR, SString> >
2723 Reserve_Fnptr m_reserveFn;
2727 // signature value in the header in stream
2728 static const DWORD sig = 0x614e4545; // "EENa" - EE Name
2733 DWORD sig; // 0x614e4545 == "EENa"
2734 DWORD cnt; // count of entries
2736 static const bool is_blittable = true;
2740 DacEENamesStreamable()
2742 , m_writeState(NULL)
2745 // Ensures the instance is ready for caching data and later writing
2746 // its map entries to an OStreamBuff.
2747 bool PrepareStreamForWriting(Reserve_Fnptr pfn, void * writeState)
2749 _ASSERTE(pfn != NULL && writeState != NULL);
2751 m_writeState = writeState;
2753 DWORD size = (DWORD) sizeof(StreamHeader);
2755 // notify owner to reserve space for a StreamHeader
2756 return m_reserveFn(size, m_writeState);
2759 // Adds a new mapping from an EE struct pointer (e.g. MethodDesc*) to
2761 bool AddEEName(TADDR taEE, const SString & eeName)
2763 _ASSERTE(m_reserveFn != NULL && m_writeState != NULL);
2765 // as a micro-optimization convert to Utf8 here as both raw_size and
2766 // raw_serialize are optimized for Utf8...
2767 StackSString seeName;
2768 eeName.ConvertToUTF8(seeName);
2770 DWORD size = (DWORD)(serialization::bin::raw_size(taEE) +
2771 serialization::bin::raw_size(seeName));
2773 // notify owner of the amount of space needed in the buffer
2774 if (m_reserveFn(size, m_writeState))
2776 // if there's still space cache the entry in m_hash
2777 m_hash.AddOrReplace(KeyValuePair<TADDR, SString>(taEE, seeName));
2786 // Finds an EE name from a target address of an EE struct (e.g.
2788 bool FindEEName(TADDR taEE, SString & eeName) const
2790 return m_hash.Lookup(taEE, &eeName) == TRUE;
2798 // Writes a header and the hash entries to an OStreamBuff
2799 HRESULT StreamTo(OStreamBuff &out) const
2803 hdr.cnt = (DWORD) m_hash.GetCount();
2807 auto end = m_hash.End();
2808 for (auto cur = m_hash.Begin(); end != cur; ++cur)
2810 out << cur->Key() << cur->Value();
2818 // Reads a header and the hash entries from an IStreamBuff
2819 HRESULT StreamFrom(IStreamBuff &in)
2823 in >> hdr; // in >> hdr.sig >> hdr.cnt;
2828 for (size_t i = 0; i < hdr.cnt; ++i)
2832 in >> taEE >> eeName;
2837 m_hash.AddOrReplace(KeyValuePair<TADDR, SString>(taEE, eeName));
2845 //================================================================================
2846 // This class enables two scenarios:
2847 // 1. When debugging a triage/mini-dump the class is initialized with a valid
2848 // buffer in taMiniMetaDataBuff. Afterwards one can call MdCacheGetEEName to
2849 // retrieve the name associated with a MethodDesc*.
2850 // 2. When generating a dump one must follow this sequence:
2851 // a. Initialize the DacStreamManager passing a valid (if the current
2852 // debugging target is a triage/mini-dump) or empty buffer (if the
2853 // current target is a live processa full or a heap dump)
2854 // b. Call PrepareStreamsForWriting() before starting enumerating any memory
2855 // c. Call MdCacheAddEEName() anytime we enumerate an EE structure of interest
2856 // d. Call EnumStreams() as the last action in the memory enumeration method.
2858 class DacStreamManager
2863 eNone, // the stream doesn't exist (target is a live process/full/heap dump)
2864 eRO, // the stream exists and we've read it (target is triage/mini-dump)
2865 eWO, // the stream doesn't exist but we're creating it
2866 // (e.g. to save a minidump from the current debugging session)
2867 eRW // the stream exists but we're generating another triage/mini-dump
2870 static const DWORD sig = 0x6d727473; // 'strm'
2872 struct StreamsHeader
2874 DWORD dwSig; // 0x6d727473 == "strm"
2875 DWORD dwTotalSize; // total size in bytes
2876 DWORD dwCntStreams; // number of streams (currently 1)
2878 static const bool is_blittable = true;
2881 DacStreamManager(TADDR miniMetaDataBuffAddress, DWORD miniMetaDataBuffSizeMax)
2882 : m_MiniMetaDataBuffAddress(miniMetaDataBuffAddress)
2883 , m_MiniMetaDataBuffSizeMax(miniMetaDataBuffSizeMax)
2887 , m_bStreamsRead(FALSE)
2895 if (m_rawBuffer != NULL)
2897 delete [] m_rawBuffer;
2901 bool PrepareStreamsForWriting()
2905 else if (m_rw == eRO)
2907 else if (m_rw == eRW)
2911 // this is a second invocation from a possibly live process
2912 // clean up the map since the callstacks/exceptions may be different
2916 // update available count based on the header and footer sizes
2917 if (m_MiniMetaDataBuffSizeMax < sizeof(StreamsHeader))
2920 m_cbAvailBuff = m_MiniMetaDataBuffSizeMax - sizeof(StreamsHeader);
2922 // update available count based on each stream's initial needs
2923 if (!m_EENames.PrepareStreamForWriting(&ReserveInBuffer, this))
2929 bool MdCacheAddEEName(TADDR taEEStruct, const SString& name)
2931 // don't cache unless we enabled "W"riting from a target that does not
2932 // already have a stream yet
2936 m_EENames.AddEEName(taEEStruct, name);
2940 HRESULT EnumStreams(IN CLRDataEnumMemoryFlags flags)
2942 _ASSERTE(flags == CLRDATA_ENUM_MEM_MINI || flags == CLRDATA_ENUM_MEM_TRIAGE);
2943 _ASSERTE(m_rw == eWO || m_rw == eRW);
2945 DWORD cbWritten = 0;
2949 // only dump the stream is it wasn't already present in the target
2950 DumpAllStreams(&cbWritten);
2954 cbWritten = m_MiniMetaDataBuffSizeMax;
2957 DacEnumMemoryRegion(m_MiniMetaDataBuffAddress, cbWritten, false);
2958 DacUpdateMemoryRegion(m_MiniMetaDataBuffAddress, cbWritten, m_rawBuffer);
2963 bool MdCacheGetEEName(TADDR taEEStruct, SString & eeName)
2965 if (!m_bStreamsRead)
2970 if (m_rw == eNone || m_rw == eWO)
2975 return m_EENames.FindEEName(taEEStruct, eeName);
2979 HRESULT Initialize()
2981 _ASSERTE(m_rw == eNone);
2982 _ASSERTE(m_rawBuffer == NULL);
2987 DacReadAll(dac_cast<TADDR>(m_MiniMetaDataBuffAddress),
2988 &hdr, sizeof(hdr), true);
2990 // when the DAC looks at a triage dump or minidump generated using
2991 // a "minimetadata" enabled DAC, buff will point to a serialized
2992 // representation of a methoddesc->method name hashmap.
2993 if (hdr.dwSig == sig)
2996 m_MiniMetaDataBuffSizeMax = hdr.dwTotalSize;
3000 // when the DAC initializes this for the case where the target is
3001 // (a) a live process, or (b) a full dump, buff will point to a
3002 // zero initialized memory region (allocated w/ VirtualAlloc)
3003 if (hdr.dwSig == 0 && hdr.dwTotalSize == 0 && hdr.dwCntStreams == 0)
3007 // otherwise we may have some memory corruption. treat this as
3008 // a liveprocess/full dump
3014 BYTE * buff = new BYTE[m_MiniMetaDataBuffSizeMax];
3015 DacReadAll(dac_cast<TADDR>(m_MiniMetaDataBuffAddress),
3016 buff, m_MiniMetaDataBuffSizeMax, true);
3023 HRESULT DumpAllStreams(DWORD * pcbWritten)
3025 _ASSERTE(m_rw == eWO);
3029 OStreamBuff out(m_rawBuffer, m_MiniMetaDataBuffSizeMax);
3034 hdr.dwTotalSize = m_MiniMetaDataBuffSizeMax-m_cbAvailBuff; // will update
3035 hdr.dwCntStreams = 1;
3039 // write MethodDesc->Method name map
3040 hr = m_EENames.StreamTo(out);
3042 // wrap up the buffer whether we ecountered an error or not
3043 size_t cbWritten = out.GetPos();
3044 cbWritten = ALIGN_UP(cbWritten, sizeof(size_t));
3046 // patch the dwTotalSize field blitted at the beginning of the buffer
3047 ((StreamsHeader*)m_rawBuffer)->dwTotalSize = (DWORD) cbWritten;
3050 *pcbWritten = (DWORD) cbWritten;
3055 HRESULT ReadAllStreams()
3057 _ASSERTE(!m_bStreamsRead);
3059 if (m_rw == eNone || m_rw == eWO)
3061 // no streams to read...
3062 m_bStreamsRead = TRUE;
3068 IStreamBuff in(m_rawBuffer, m_MiniMetaDataBuffSizeMax);
3073 _ASSERTE(hdr.dwSig == sig);
3074 _ASSERTE(hdr.dwCntStreams == 1);
3076 // read EE struct pointer -> EE name map
3078 hr = m_EENames.StreamFrom(in);
3080 m_bStreamsRead = TRUE;
3085 static bool ReserveInBuffer(DWORD size, void * writeState)
3087 DacStreamManager * pThis = reinterpret_cast<DacStreamManager*>(writeState);
3088 if (size > pThis->m_cbAvailBuff)
3094 pThis->m_cbAvailBuff -= size;
3100 TADDR m_MiniMetaDataBuffAddress; // TADDR of the buffer
3101 DWORD m_MiniMetaDataBuffSizeMax; // max size of buffer
3102 BYTE * m_rawBuffer; // inproc copy of buffer
3103 DWORD m_cbAvailBuff; // available bytes in buffer
3105 BOOL m_bStreamsRead;
3106 DacEENamesStreamable m_EENames;
3109 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3111 //----------------------------------------------------------------------------
3115 //----------------------------------------------------------------------------
3117 LONG ClrDataAccess::s_procInit;
3119 ClrDataAccess::ClrDataAccess(ICorDebugDataTarget * pTarget, ICLRDataTarget * pLegacyTarget/*=0*/)
3121 SUPPORTS_DAC_HOST_ONLY; // ctor does no marshalling - don't check with DacCop
3124 * Stash the various forms of the new ICorDebugDataTarget interface
3126 m_pTarget = pTarget;
3127 m_pTarget->AddRef();
3131 hr = m_pTarget->QueryInterface(__uuidof(ICorDebugMutableDataTarget),
3132 (void**)&m_pMutableTarget);
3136 // Create a target which always fails the write requests with CORDBG_E_TARGET_READONLY
3137 m_pMutableTarget = new ReadOnlyDataTargetFacade();
3138 m_pMutableTarget->AddRef();
3142 * If we have a legacy target, it means we're providing compatibility for code that used
3143 * the old ICLRDataTarget interfaces. There are still a few things (like metadata location,
3144 * GetImageBase, and VirtualAlloc) that the implementation may use which we haven't superseded
3145 * in ICorDebugDataTarget, so we still need access to the old target interfaces.
3146 * Any functionality that does exist in ICorDebugDataTarget is accessed from that interface
3147 * using the DataTargetAdapter on top of the legacy interface (to unify the calling code).
3148 * Eventually we may expose all functionality we need using ICorDebug (possibly a private
3149 * interface for things like VirtualAlloc), at which point we can stop using the legacy interfaces
3150 * completely (except in the DataTargetAdapter).
3152 m_pLegacyTarget = NULL;
3153 m_pLegacyTarget2 = NULL;
3154 m_pLegacyTarget3 = NULL;
3155 m_legacyMetaDataLocator = NULL;
3157 if (pLegacyTarget != NULL)
3159 m_pLegacyTarget = pLegacyTarget;
3161 m_pLegacyTarget->AddRef();
3163 m_pLegacyTarget->QueryInterface(__uuidof(ICLRDataTarget2), (void**)&m_pLegacyTarget2);
3165 m_pLegacyTarget->QueryInterface(__uuidof(ICLRDataTarget3), (void**)&m_pLegacyTarget3);
3167 if (pLegacyTarget->QueryInterface(__uuidof(ICLRMetadataLocator),
3168 (void**)&m_legacyMetaDataLocator) != S_OK)
3170 // The debugger doesn't implement IMetadataLocator. Use
3171 // IXCLRDataTarget3 if that exists. Otherwise we don't need it.
3172 pLegacyTarget->QueryInterface(__uuidof(IXCLRDataTarget3),
3173 (void**)&m_target3);
3180 m_debugMode = GetEnvironmentVariableA("MSCORDACWKS_DEBUG", NULL, 0) != 0;
3183 m_updateMemCb = NULL;
3184 m_enumMemFlags = (CLRDataEnumMemoryFlags)-1; // invalid
3185 m_jitNotificationTable = NULL;
3186 m_gcNotificationTable = NULL;
3188 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3190 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3192 // Target consistency checks are disabled by default.
3193 // See code:ClrDataAccess::SetTargetConsistencyChecks for details.
3194 m_fEnableTargetConsistencyAsserts = false;
3197 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgDACEnableAssert))
3199 m_fEnableTargetConsistencyAsserts = true;
3202 // Verification asserts are disabled by default because some debuggers (cdb/windbg) probe likely locations
3203 // for DAC and having this assert pop up all the time can be annoying. We let derived classes enable
3204 // this if they want. It can also be overridden at run-time with COMPlus_DbgDACAssertOnMismatch,
3205 // see ClrDataAccess::VerifyDlls for details.
3206 m_fEnableDllVerificationAsserts = false;
3211 ClrDataAccess::~ClrDataAccess(void)
3213 SUPPORTS_DAC_HOST_ONLY;
3215 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3220 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3222 delete [] m_jitNotificationTable;
3223 if (m_pLegacyTarget)
3225 m_pLegacyTarget->Release();
3227 if (m_pLegacyTarget2)
3229 m_pLegacyTarget2->Release();
3231 if (m_pLegacyTarget3)
3233 m_pLegacyTarget3->Release();
3235 if (m_legacyMetaDataLocator)
3237 m_legacyMetaDataLocator->Release();
3241 m_target3->Release();
3243 m_pTarget->Release();
3244 m_pMutableTarget->Release();
3248 ClrDataAccess::QueryInterface(THIS_
3249 IN REFIID interfaceId,
3254 if (IsEqualIID(interfaceId, IID_IUnknown) ||
3255 IsEqualIID(interfaceId, __uuidof(IXCLRDataProcess)) ||
3256 IsEqualIID(interfaceId, __uuidof(IXCLRDataProcess2)))
3258 ifaceRet = static_cast<IXCLRDataProcess2*>(this);
3260 else if (IsEqualIID(interfaceId, __uuidof(ICLRDataEnumMemoryRegions)))
3262 ifaceRet = static_cast<ICLRDataEnumMemoryRegions*>(this);
3264 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface)))
3266 ifaceRet = static_cast<ISOSDacInterface*>(this);
3268 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface2)))
3270 ifaceRet = static_cast<ISOSDacInterface2*>(this);
3272 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface3)))
3274 ifaceRet = static_cast<ISOSDacInterface3*>(this);
3276 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface4)))
3278 ifaceRet = static_cast<ISOSDacInterface4*>(this);
3280 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface5)))
3282 ifaceRet = static_cast<ISOSDacInterface5*>(this);
3284 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface6)))
3286 ifaceRet = static_cast<ISOSDacInterface6*>(this);
3291 return E_NOINTERFACE;
3299 STDMETHODIMP_(ULONG)
3300 ClrDataAccess::AddRef(THIS)
3302 return InterlockedIncrement(&m_refs);
3305 STDMETHODIMP_(ULONG)
3306 ClrDataAccess::Release(THIS)
3308 SUPPORTS_DAC_HOST_ONLY;
3309 LONG newRefs = InterlockedDecrement(&m_refs);
3317 HRESULT STDMETHODCALLTYPE
3318 ClrDataAccess::Flush(void)
3320 SUPPORTS_DAC_HOST_ONLY;
3323 // Free MD import objects.
3325 m_mdImports.Flush();
3327 // Free instance memory.
3328 m_instances.Flush();
3330 // When the host instance cache is flushed we
3331 // update the instance age count so that
3332 // all child objects automatically become
3333 // invalid. This prevents them from using
3334 // any pointers they've kept to host instances
3335 // which are now gone.
3341 HRESULT STDMETHODCALLTYPE
3342 ClrDataAccess::StartEnumTasks(
3343 /* [out] */ CLRDATA_ENUM* handle)
3351 if (ThreadStore::s_pThreadStore)
3353 Thread* thread = ThreadStore::GetAllThreadList(NULL, 0, 0);
3354 *handle = TO_CDENUM(thread);
3355 status = *handle ? S_OK : S_FALSE;
3364 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3369 EX_END_CATCH(SwallowAllExceptions)
3375 HRESULT STDMETHODCALLTYPE
3376 ClrDataAccess::EnumTask(
3377 /* [in, out] */ CLRDATA_ENUM* handle,
3378 /* [out] */ IXCLRDataTask **task)
3388 Thread* thread = FROM_CDENUM(Thread, *handle);
3389 *task = new (nothrow) ClrDataTask(this, thread);
3392 thread = ThreadStore::GetAllThreadList(thread, 0, 0);
3393 *handle = TO_CDENUM(thread);
3398 status = E_OUTOFMEMORY;
3408 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3413 EX_END_CATCH(SwallowAllExceptions)
3419 HRESULT STDMETHODCALLTYPE
3420 ClrDataAccess::EndEnumTasks(
3421 /* [in] */ CLRDATA_ENUM handle)
3429 // Enumerator holds no resources.
3434 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3439 EX_END_CATCH(SwallowAllExceptions)
3445 HRESULT STDMETHODCALLTYPE
3446 ClrDataAccess::GetTaskByOSThreadID(
3447 /* [in] */ ULONG32 osThreadID,
3448 /* [out] */ IXCLRDataTask **task)
3456 status = E_INVALIDARG;
3457 Thread* thread = DacGetThread(osThreadID);
3460 *task = new (nothrow) ClrDataTask(this, thread);
3461 status = *task ? S_OK : E_OUTOFMEMORY;
3466 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3471 EX_END_CATCH(SwallowAllExceptions)
3477 HRESULT STDMETHODCALLTYPE
3478 ClrDataAccess::GetTaskByUniqueID(
3479 /* [in] */ ULONG64 uniqueID,
3480 /* [out] */ IXCLRDataTask **task)
3488 Thread* thread = FindClrThreadByTaskId(uniqueID);
3491 *task = new (nothrow) ClrDataTask(this, thread);
3492 status = *task ? S_OK : E_OUTOFMEMORY;
3496 status = E_INVALIDARG;
3501 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3506 EX_END_CATCH(SwallowAllExceptions)
3512 HRESULT STDMETHODCALLTYPE
3513 ClrDataAccess::GetFlags(
3514 /* [out] */ ULONG32 *flags)
3522 // XXX Microsoft - GC check.
3523 *flags = CLRDATA_PROCESS_DEFAULT;
3528 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3533 EX_END_CATCH(SwallowAllExceptions)
3539 HRESULT STDMETHODCALLTYPE
3540 ClrDataAccess::IsSameObject(
3541 /* [in] */ IXCLRDataProcess* process)
3549 status = m_pTarget == ((ClrDataAccess*)process)->m_pTarget ?
3554 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3559 EX_END_CATCH(SwallowAllExceptions)
3565 HRESULT STDMETHODCALLTYPE
3566 ClrDataAccess::GetManagedObject(
3567 /* [out] */ IXCLRDataValue **value)
3580 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3585 EX_END_CATCH(SwallowAllExceptions)
3591 HRESULT STDMETHODCALLTYPE
3592 ClrDataAccess::GetDesiredExecutionState(
3593 /* [out] */ ULONG32 *state)
3606 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3611 EX_END_CATCH(SwallowAllExceptions)
3617 HRESULT STDMETHODCALLTYPE
3618 ClrDataAccess::SetDesiredExecutionState(
3619 /* [in] */ ULONG32 state)
3632 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3637 EX_END_CATCH(SwallowAllExceptions)
3643 HRESULT STDMETHODCALLTYPE
3644 ClrDataAccess::GetAddressType(
3645 /* [in] */ CLRDATA_ADDRESS address,
3646 /* [out] */ CLRDataAddressType* type)
3654 // The only thing that constitutes a failure is some
3655 // dac failure while checking things.
3657 TADDR taAddr = CLRDATA_ADDRESS_TO_TADDR(address);
3658 if (IsPossibleCodeAddress(taAddr) == S_OK)
3660 if (ExecutionManager::IsManagedCode(taAddr))
3662 *type = CLRDATA_ADDRESS_MANAGED_METHOD;
3666 if (StubManager::IsStub(taAddr))
3668 *type = CLRDATA_ADDRESS_RUNTIME_UNMANAGED_STUB;
3673 *type = CLRDATA_ADDRESS_UNRECOGNIZED;
3679 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3684 EX_END_CATCH(SwallowAllExceptions)
3690 HRESULT STDMETHODCALLTYPE
3691 ClrDataAccess::GetRuntimeNameByAddress(
3692 /* [in] */ CLRDATA_ADDRESS address,
3693 /* [in] */ ULONG32 flags,
3694 /* [in] */ ULONG32 bufLen,
3695 /* [out] */ ULONG32 *symbolLen,
3696 /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR symbolBuf[ ],
3697 /* [out] */ CLRDATA_ADDRESS* displacement)
3706 address &= ~THUMB_CODE; //workaround for windbg passing in addresses with the THUMB mode bit set
3708 status = RawGetMethodName(address, flags, bufLen, symbolLen, symbolBuf,
3713 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3718 EX_END_CATCH(SwallowAllExceptions)
3724 HRESULT STDMETHODCALLTYPE
3725 ClrDataAccess::StartEnumAppDomains(
3726 /* [out] */ CLRDATA_ENUM* handle)
3734 AppDomainIterator* iter = new (nothrow) AppDomainIterator(FALSE);
3737 *handle = TO_CDENUM(iter);
3742 status = E_OUTOFMEMORY;
3747 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3752 EX_END_CATCH(SwallowAllExceptions)
3758 HRESULT STDMETHODCALLTYPE
3759 ClrDataAccess::EnumAppDomain(
3760 /* [in, out] */ CLRDATA_ENUM* handle,
3761 /* [out] */ IXCLRDataAppDomain **appDomain)
3769 AppDomainIterator* iter = FROM_CDENUM(AppDomainIterator, *handle);
3772 *appDomain = new (nothrow)
3773 ClrDataAppDomain(this, iter->GetDomain());
3774 status = *appDomain ? S_OK : E_OUTOFMEMORY;
3783 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3788 EX_END_CATCH(SwallowAllExceptions)
3794 HRESULT STDMETHODCALLTYPE
3795 ClrDataAccess::EndEnumAppDomains(
3796 /* [in] */ CLRDATA_ENUM handle)
3804 AppDomainIterator* iter = FROM_CDENUM(AppDomainIterator, handle);
3810 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3815 EX_END_CATCH(SwallowAllExceptions)
3821 HRESULT STDMETHODCALLTYPE
3822 ClrDataAccess::GetAppDomainByUniqueID(
3823 /* [in] */ ULONG64 uniqueID,
3824 /* [out] */ IXCLRDataAppDomain **appDomain)
3832 if (uniqueID != DefaultADID)
3834 status = E_INVALIDARG;
3838 *appDomain = new (nothrow)
3839 ClrDataAppDomain(this, AppDomain::GetCurrentDomain());
3840 status = *appDomain ? S_OK : E_OUTOFMEMORY;
3845 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3850 EX_END_CATCH(SwallowAllExceptions)
3856 HRESULT STDMETHODCALLTYPE
3857 ClrDataAccess::StartEnumAssemblies(
3858 /* [out] */ CLRDATA_ENUM* handle)
3866 ProcessModIter* iter = new (nothrow) ProcessModIter;
3869 *handle = TO_CDENUM(iter);
3874 status = E_OUTOFMEMORY;
3879 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3884 EX_END_CATCH(SwallowAllExceptions)
3890 HRESULT STDMETHODCALLTYPE
3891 ClrDataAccess::EnumAssembly(
3892 /* [in, out] */ CLRDATA_ENUM* handle,
3893 /* [out] */ IXCLRDataAssembly **assembly)
3901 ProcessModIter* iter = FROM_CDENUM(ProcessModIter, *handle);
3904 if ((assem = iter->NextAssem()))
3906 *assembly = new (nothrow)
3907 ClrDataAssembly(this, assem);
3908 status = *assembly ? S_OK : E_OUTOFMEMORY;
3917 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3922 EX_END_CATCH(SwallowAllExceptions)
3928 HRESULT STDMETHODCALLTYPE
3929 ClrDataAccess::EndEnumAssemblies(
3930 /* [in] */ CLRDATA_ENUM handle)
3938 ProcessModIter* iter = FROM_CDENUM(ProcessModIter, handle);
3944 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3949 EX_END_CATCH(SwallowAllExceptions)
3955 HRESULT STDMETHODCALLTYPE
3956 ClrDataAccess::StartEnumModules(
3957 /* [out] */ CLRDATA_ENUM* handle)
3965 ProcessModIter* iter = new (nothrow) ProcessModIter;
3968 *handle = TO_CDENUM(iter);
3973 status = E_OUTOFMEMORY;
3978 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3983 EX_END_CATCH(SwallowAllExceptions)
3989 HRESULT STDMETHODCALLTYPE
3990 ClrDataAccess::EnumModule(
3991 /* [in, out] */ CLRDATA_ENUM* handle,
3992 /* [out] */ IXCLRDataModule **mod)
4000 ProcessModIter* iter = FROM_CDENUM(ProcessModIter, *handle);
4003 if ((curMod = iter->NextModule()))
4005 *mod = new (nothrow)
4006 ClrDataModule(this, curMod);
4007 status = *mod ? S_OK : E_OUTOFMEMORY;
4016 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4021 EX_END_CATCH(SwallowAllExceptions)
4027 HRESULT STDMETHODCALLTYPE
4028 ClrDataAccess::EndEnumModules(
4029 /* [in] */ CLRDATA_ENUM handle)
4037 ProcessModIter* iter = FROM_CDENUM(ProcessModIter, handle);
4043 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4048 EX_END_CATCH(SwallowAllExceptions)
4054 HRESULT STDMETHODCALLTYPE
4055 ClrDataAccess::GetModuleByAddress(
4056 /* [in] */ CLRDATA_ADDRESS address,
4057 /* [out] */ IXCLRDataModule** mod)
4065 ProcessModIter modIter;
4068 while ((modDef = modIter.NextModule()))
4072 PEFile* file = modDef->GetFile();
4074 if ((base = PTR_TO_TADDR(file->GetLoadedImageContents(&length))))
4076 if (TO_CDADDR(base) <= address &&
4077 TO_CDADDR(base + length) > address)
4082 if (file->HasNativeImage())
4084 base = PTR_TO_TADDR(file->GetLoadedNative()->GetBase());
4085 length = file->GetLoadedNative()->GetVirtualSize();
4086 if (TO_CDADDR(base) <= address &&
4087 TO_CDADDR(base + length) > address)
4096 *mod = new (nothrow)
4097 ClrDataModule(this, modDef);
4098 status = *mod ? S_OK : E_OUTOFMEMORY;
4107 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4112 EX_END_CATCH(SwallowAllExceptions)
4118 HRESULT STDMETHODCALLTYPE
4119 ClrDataAccess::StartEnumMethodDefinitionsByAddress(
4120 /* [in] */ CLRDATA_ADDRESS address,
4121 /* [out] */ CLRDATA_ENUM *handle)
4129 ProcessModIter modIter;
4132 while ((modDef = modIter.NextModule()))
4136 PEFile* file = modDef->GetFile();
4138 if ((base = PTR_TO_TADDR(file->GetLoadedImageContents(&length))))
4140 if (TO_CDADDR(base) <= address &&
4141 TO_CDADDR(base + length) > address)
4148 status = EnumMethodDefinitions::
4149 CdStart(modDef, true, address, handle);
4153 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4158 EX_END_CATCH(SwallowAllExceptions)
4164 HRESULT STDMETHODCALLTYPE
4165 ClrDataAccess::EnumMethodDefinitionByAddress(
4166 /* [out][in] */ CLRDATA_ENUM* handle,
4167 /* [out] */ IXCLRDataMethodDefinition **method)
4175 status = EnumMethodDefinitions::CdNext(this, handle, method);
4179 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4184 EX_END_CATCH(SwallowAllExceptions)
4190 HRESULT STDMETHODCALLTYPE
4191 ClrDataAccess::EndEnumMethodDefinitionsByAddress(
4192 /* [in] */ CLRDATA_ENUM handle)
4200 status = EnumMethodDefinitions::CdEnd(handle);
4204 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4209 EX_END_CATCH(SwallowAllExceptions)
4215 HRESULT STDMETHODCALLTYPE
4216 ClrDataAccess::StartEnumMethodInstancesByAddress(
4217 /* [in] */ CLRDATA_ADDRESS address,
4218 /* [in] */ IXCLRDataAppDomain* appDomain,
4219 /* [out] */ CLRDATA_ENUM *handle)
4227 MethodDesc* methodDesc;
4232 if( (status = TRY_CLRDATA_ADDRESS_TO_TADDR(address, &taddr)) != S_OK )
4237 if (IsPossibleCodeAddress(taddr) != S_OK)
4242 methodDesc = ExecutionManager::GetCodeMethodDesc(taddr);
4248 status = EnumMethodInstances::CdStart(methodDesc, appDomain,
4255 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4260 EX_END_CATCH(SwallowAllExceptions)
4266 HRESULT STDMETHODCALLTYPE
4267 ClrDataAccess::EnumMethodInstanceByAddress(
4268 /* [out][in] */ CLRDATA_ENUM* handle,
4269 /* [out] */ IXCLRDataMethodInstance **method)
4277 status = EnumMethodInstances::CdNext(this, handle, method);
4281 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4286 EX_END_CATCH(SwallowAllExceptions)
4292 HRESULT STDMETHODCALLTYPE
4293 ClrDataAccess::EndEnumMethodInstancesByAddress(
4294 /* [in] */ CLRDATA_ENUM handle)
4302 status = EnumMethodInstances::CdEnd(handle);
4306 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4311 EX_END_CATCH(SwallowAllExceptions)
4317 HRESULT STDMETHODCALLTYPE
4318 ClrDataAccess::GetDataByAddress(
4319 /* [in] */ CLRDATA_ADDRESS address,
4320 /* [in] */ ULONG32 flags,
4321 /* [in] */ IXCLRDataAppDomain* appDomain,
4322 /* [in] */ IXCLRDataTask* tlsTask,
4323 /* [in] */ ULONG32 bufLen,
4324 /* [out] */ ULONG32 *nameLen,
4325 /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ],
4326 /* [out] */ IXCLRDataValue **value,
4327 /* [out] */ CLRDATA_ADDRESS *displacement)
4333 return E_INVALIDARG;
4345 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4350 EX_END_CATCH(SwallowAllExceptions)
4356 HRESULT STDMETHODCALLTYPE
4357 ClrDataAccess::GetExceptionStateByExceptionRecord(
4358 /* [in] */ EXCEPTION_RECORD64 *record,
4359 /* [out] */ IXCLRDataExceptionState **exception)
4372 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4377 EX_END_CATCH(SwallowAllExceptions)
4383 HRESULT STDMETHODCALLTYPE
4384 ClrDataAccess::TranslateExceptionRecordToNotification(
4385 /* [in] */ EXCEPTION_RECORD64 *record,
4386 /* [in] */ IXCLRDataExceptionNotification *notify)
4388 HRESULT status = E_FAIL;
4389 ClrDataModule* pubModule = NULL;
4390 ClrDataMethodInstance* pubMethodInst = NULL;
4391 ClrDataExceptionState* pubExState = NULL;
4392 GcEvtArgs pubGcEvtArgs;
4393 ULONG32 notifyType = 0;
4394 DWORD catcherNativeOffset = 0;
4395 TADDR nativeCodeLocation = NULL;
4402 // We cannot hold the dac lock while calling
4403 // out as the external code can do arbitrary things.
4404 // Instead we make a pass over the exception
4405 // information and create all necessary objects.
4406 // We then leave the lock and make the callbac.
4409 TADDR exInfo[EXCEPTION_MAXIMUM_PARAMETERS];
4410 for (UINT i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
4412 exInfo[i] = TO_TADDR(record->ExceptionInformation[i]);
4415 notifyType = DACNotify::GetType(exInfo);
4418 case DACNotify::MODULE_LOAD_NOTIFICATION:
4422 if (DACNotify::ParseModuleLoadNotification(exInfo, modulePtr))
4424 Module* clrModule = PTR_Module(modulePtr);
4425 pubModule = new (nothrow) ClrDataModule(this, clrModule);
4426 if (pubModule == NULL)
4428 status = E_OUTOFMEMORY;
4438 case DACNotify::MODULE_UNLOAD_NOTIFICATION:
4442 if (DACNotify::ParseModuleUnloadNotification(exInfo, modulePtr))
4444 Module* clrModule = PTR_Module(modulePtr);
4445 pubModule = new (nothrow) ClrDataModule(this, clrModule);
4446 if (pubModule == NULL)
4448 status = E_OUTOFMEMORY;
4458 case DACNotify::JIT_NOTIFICATION2:
4460 TADDR methodDescPtr;
4462 if(DACNotify::ParseJITNotification(exInfo, methodDescPtr, nativeCodeLocation))
4464 // Try and find the right appdomain
4465 MethodDesc* methodDesc = PTR_MethodDesc(methodDescPtr);
4466 BaseDomain* baseDomain = methodDesc->GetDomain();
4467 AppDomain* appDomain = NULL;
4469 if (baseDomain->IsAppDomain())
4471 appDomain = PTR_AppDomain(PTR_HOST_TO_TADDR(baseDomain));
4475 // Find a likely domain, because it's the shared domain.
4476 AppDomainIterator adi(FALSE);
4477 appDomain = adi.GetDomain();
4481 new (nothrow) ClrDataMethodInstance(this,
4484 if (pubMethodInst == NULL)
4486 status = E_OUTOFMEMORY;
4496 case DACNotify::EXCEPTION_NOTIFICATION:
4500 if (DACNotify::ParseExceptionNotification(exInfo, threadPtr))
4502 // Translation can only occur at the time of
4503 // receipt of the notify exception, so we assume
4504 // that the Thread's current exception state
4505 // is the state we want.
4506 status = ClrDataExceptionState::
4508 PTR_Thread(threadPtr),
4515 case DACNotify::GC_NOTIFICATION:
4517 if (DACNotify::ParseGCNotification(exInfo, pubGcEvtArgs))
4524 case DACNotify::CATCH_ENTER_NOTIFICATION:
4526 TADDR methodDescPtr;
4527 if (DACNotify::ParseExceptionCatcherEnterNotification(exInfo, methodDescPtr, catcherNativeOffset))
4529 // Try and find the right appdomain
4530 MethodDesc* methodDesc = PTR_MethodDesc(methodDescPtr);
4531 BaseDomain* baseDomain = methodDesc->GetDomain();
4532 AppDomain* appDomain = NULL;
4534 if (baseDomain->IsAppDomain())
4536 appDomain = PTR_AppDomain(PTR_HOST_TO_TADDR(baseDomain));
4540 // Find a likely domain, because it's the shared domain.
4541 AppDomainIterator adi(FALSE);
4542 appDomain = adi.GetDomain();
4546 new (nothrow) ClrDataMethodInstance(this,
4549 if (pubMethodInst == NULL)
4551 status = E_OUTOFMEMORY;
4562 status = E_INVALIDARG;
4568 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4573 EX_END_CATCH(SwallowAllExceptions)
4579 IXCLRDataExceptionNotification2* notify2;
4581 if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification2),
4582 (void**)¬ify2) != S_OK)
4587 IXCLRDataExceptionNotification3* notify3;
4588 if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification3),
4589 (void**)¬ify3) != S_OK)
4594 IXCLRDataExceptionNotification4* notify4;
4595 if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification4),
4596 (void**)¬ify4) != S_OK)
4601 IXCLRDataExceptionNotification5* notify5;
4602 if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification5),
4603 (void**)¬ify5) != S_OK)
4610 case DACNotify::MODULE_LOAD_NOTIFICATION:
4611 notify->OnModuleLoaded(pubModule);
4614 case DACNotify::MODULE_UNLOAD_NOTIFICATION:
4615 notify->OnModuleUnloaded(pubModule);
4618 case DACNotify::JIT_NOTIFICATION2:
4619 notify->OnCodeGenerated(pubMethodInst);
4623 notify5->OnCodeGenerated2(pubMethodInst, TO_CDADDR(nativeCodeLocation));
4627 case DACNotify::EXCEPTION_NOTIFICATION:
4630 notify2->OnException(pubExState);
4634 status = E_INVALIDARG;
4638 case DACNotify::GC_NOTIFICATION:
4641 notify3->OnGcEvent(pubGcEvtArgs);
4645 case DACNotify::CATCH_ENTER_NOTIFICATION:
4648 notify4->ExceptionCatcherEnter(pubMethodInst, catcherNativeOffset);
4653 // notifyType has already been validated.
4678 pubModule->Release();
4682 pubMethodInst->Release();
4686 pubExState->Release();
4692 HRESULT STDMETHODCALLTYPE
4693 ClrDataAccess::CreateMemoryValue(
4694 /* [in] */ IXCLRDataAppDomain* appDomain,
4695 /* [in] */ IXCLRDataTask* tlsTask,
4696 /* [in] */ IXCLRDataTypeInstance* type,
4697 /* [in] */ CLRDATA_ADDRESS addr,
4698 /* [out] */ IXCLRDataValue** value)
4706 AppDomain* dacDomain;
4710 NativeVarLocation loc;
4712 dacDomain = ((ClrDataAppDomain*)appDomain)->GetAppDomain();
4715 dacThread = ((ClrDataTask*)tlsTask)->GetThread();
4721 dacType = ((ClrDataTypeInstance*)type)->GetTypeHandle();
4723 flags = GetTypeFieldValueFlags(dacType, NULL, 0, false);
4726 loc.size = dacType.GetSize();
4727 loc.contextReg = false;
4729 *value = new (nothrow)
4730 ClrDataValue(this, dacDomain, dacThread, flags,
4731 dacType, addr, 1, &loc);
4732 status = *value ? S_OK : E_OUTOFMEMORY;
4736 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4741 EX_END_CATCH(SwallowAllExceptions)
4747 HRESULT STDMETHODCALLTYPE
4748 ClrDataAccess::SetAllTypeNotifications(
4749 /* [in] */ IXCLRDataModule* mod,
4750 /* [in] */ ULONG32 flags)
4763 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4768 EX_END_CATCH(SwallowAllExceptions)
4774 HRESULT STDMETHODCALLTYPE
4775 ClrDataAccess::SetAllCodeNotifications(
4776 /* [in] */ IXCLRDataModule* mod,
4777 /* [in] */ ULONG32 flags)
4787 if (!IsValidMethodCodeNotification(flags))
4789 status = E_INVALIDARG;
4793 JITNotifications jn(GetHostJitNotificationTable());
4796 status = E_OUTOFMEMORY;
4801 TADDR modulePtr = mod ?
4802 PTR_HOST_TO_TADDR(((ClrDataModule*)mod)->GetModule()) :
4805 if (jn.SetAllNotifications(modulePtr, flags, &changedTable))
4807 if (!changedTable ||
4808 (changedTable && jn.UpdateOutOfProcTable()))
4818 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4823 EX_END_CATCH(SwallowAllExceptions)
4829 HRESULT STDMETHODCALLTYPE
4830 ClrDataAccess::GetTypeNotifications(
4831 /* [in] */ ULONG32 numTokens,
4832 /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[],
4833 /* [in] */ IXCLRDataModule* singleMod,
4834 /* [in, size_is(numTokens)] */ mdTypeDef tokens[],
4835 /* [out, size_is(numTokens)] */ ULONG32 flags[])
4848 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4853 EX_END_CATCH(SwallowAllExceptions)
4859 HRESULT STDMETHODCALLTYPE
4860 ClrDataAccess::SetTypeNotifications(
4861 /* [in] */ ULONG32 numTokens,
4862 /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[],
4863 /* [in] */ IXCLRDataModule* singleMod,
4864 /* [in, size_is(numTokens)] */ mdTypeDef tokens[],
4865 /* [in, size_is(numTokens)] */ ULONG32 flags[],
4866 /* [in] */ ULONG32 singleFlags)
4879 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4884 EX_END_CATCH(SwallowAllExceptions)
4890 HRESULT STDMETHODCALLTYPE
4891 ClrDataAccess::GetCodeNotifications(
4892 /* [in] */ ULONG32 numTokens,
4893 /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[],
4894 /* [in] */ IXCLRDataModule* singleMod,
4895 /* [in, size_is(numTokens)] */ mdMethodDef tokens[],
4896 /* [out, size_is(numTokens)] */ ULONG32 flags[])
4904 if ((flags == NULL || tokens == NULL) ||
4905 (mods == NULL && singleMod == NULL) ||
4906 (mods != NULL && singleMod != NULL))
4908 status = E_INVALIDARG;
4912 JITNotifications jn(GetHostJitNotificationTable());
4915 status = E_OUTOFMEMORY;
4919 TADDR modulePtr = NULL;
4922 modulePtr = PTR_HOST_TO_TADDR(((ClrDataModule*)singleMod)->
4926 for (ULONG32 i = 0; i < numTokens; i++)
4928 if (singleMod == NULL)
4931 PTR_HOST_TO_TADDR(((ClrDataModule*)mods[i])->
4934 USHORT jt = jn.Requested(modulePtr, tokens[i]);
4944 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4949 EX_END_CATCH(SwallowAllExceptions)
4955 HRESULT STDMETHODCALLTYPE
4956 ClrDataAccess::SetCodeNotifications(
4957 /* [in] */ ULONG32 numTokens,
4958 /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[],
4959 /* [in] */ IXCLRDataModule* singleMod,
4960 /* [in, size_is(numTokens)] */ mdMethodDef tokens[],
4961 /* [in, size_is(numTokens)] */ ULONG32 flags[],
4962 /* [in] */ ULONG32 singleFlags)
4964 HRESULT status = E_UNEXPECTED;
4970 if ((tokens == NULL) ||
4971 (mods == NULL && singleMod == NULL) ||
4972 (mods != NULL && singleMod != NULL))
4974 status = E_INVALIDARG;
4978 JITNotifications jn(GetHostJitNotificationTable());
4979 if (!jn.IsActive() || numTokens > jn.GetTableSize())
4981 status = E_OUTOFMEMORY;
4985 BOOL changedTable = FALSE;
4990 for (ULONG32 check = 0; check < numTokens; check++)
4992 if (!IsValidMethodCodeNotification(flags[check]))
4994 status = E_INVALIDARG;
4999 else if (!IsValidMethodCodeNotification(singleFlags))
5001 status = E_INVALIDARG;
5005 TADDR modulePtr = NULL;
5009 PTR_HOST_TO_TADDR(((ClrDataModule*)singleMod)->
5013 for (ULONG32 i = 0; i < numTokens; i++)
5015 if (singleMod == NULL)
5018 PTR_HOST_TO_TADDR(((ClrDataModule*)mods[i])->
5022 USHORT curFlags = jn.Requested(modulePtr, tokens[i]);
5023 USHORT setFlags = (USHORT)(flags ? flags[i] : singleFlags);
5025 if (curFlags != setFlags)
5027 if (!jn.SetNotification(modulePtr, tokens[i],
5034 changedTable = TRUE;
5038 if (!changedTable ||
5039 (changedTable && jn.UpdateOutOfProcTable()))
5050 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5055 EX_END_CATCH(SwallowAllExceptions)
5062 ClrDataAccess::GetOtherNotificationFlags(
5063 /* [out] */ ULONG32* flags)
5071 *flags = g_dacNotificationFlags;
5076 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5081 EX_END_CATCH(SwallowAllExceptions)
5088 ClrDataAccess::SetOtherNotificationFlags(
5089 /* [in] */ ULONG32 flags)
5093 if ((flags & ~(CLRDATA_NOTIFY_ON_MODULE_LOAD |
5094 CLRDATA_NOTIFY_ON_MODULE_UNLOAD |
5095 CLRDATA_NOTIFY_ON_EXCEPTION |
5096 CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER)) != 0)
5098 return E_INVALIDARG;
5105 g_dacNotificationFlags = flags;
5110 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5115 EX_END_CATCH(SwallowAllExceptions)
5123 STUB_BUF_FLAGS_START,
5125 STUB_BUF_METHOD_JITTED,
5126 STUB_BUF_FRAME_PUSHED,
5127 STUB_BUF_STUB_MANAGER_PUSHED,
5134 CLRDATA_FOLLOW_STUB_BUFFER apiBuf;
5144 ClrDataAccess::FollowStubStep(
5145 /* [in] */ Thread* thread,
5146 /* [in] */ ULONG32 inFlags,
5147 /* [in] */ TADDR inAddr,
5148 /* [in] */ union STUB_BUF* inBuffer,
5149 /* [out] */ TADDR* outAddr,
5150 /* [out] */ union STUB_BUF* outBuffer,
5151 /* [out] */ ULONG32* outFlags)
5153 TraceDestination trace;
5154 bool traceDone = false;
5156 T_CONTEXT localContext;
5158 MethodDesc* methodDesc;
5160 ZeroMemory(outBuffer, sizeof(*outBuffer));
5164 switch(inBuffer->u.flags)
5166 case STUB_BUF_METHOD_JITTED:
5167 if (inAddr != GFN_TADDR(DACNotifyCompilationFinished))
5169 return E_INVALIDARG;
5172 // It's possible that this notification is
5173 // for a different method, so double-check
5174 // and recycle the notification if necessary.
5175 methodDesc = PTR_MethodDesc(CORDB_ADDRESS_TO_TADDR(inBuffer->u.addr));
5176 if (methodDesc->HasNativeCode())
5178 *outAddr = methodDesc->GetNativeCode();
5179 *outFlags = CLRDATA_FOLLOW_STUB_EXIT;
5183 // We didn't end up with native code so try again.
5184 trace.InitForUnjittedMethod(methodDesc);
5188 case STUB_BUF_FRAME_PUSHED:
5190 inAddr != inBuffer->u.addr)
5192 return E_INVALIDARG;
5195 trace.InitForFramePush(CORDB_ADDRESS_TO_TADDR(inBuffer->u.addr));
5196 DacGetThreadContext(thread, &localContext);
5197 thread->FillRegDisplay(®Disp, &localContext);
5198 if (!thread->GetFrame()->
5210 case STUB_BUF_STUB_MANAGER_PUSHED:
5212 inAddr != inBuffer->u.addr ||
5215 return E_INVALIDARG;
5218 trace.InitForManagerPush(CORDB_ADDRESS_TO_TADDR(inBuffer->u.addr),
5219 PTR_StubManager(CORDB_ADDRESS_TO_TADDR(inBuffer->u.arg1)));
5220 DacGetThreadContext(thread, &localContext);
5221 if (!trace.GetStubManager()->
5222 TraceManager(thread,
5234 return E_INVALIDARG;
5239 !StubManager::TraceStub(inAddr, &trace)) ||
5240 !StubManager::FollowTrace(&trace))
5242 return E_NOINTERFACE;
5245 switch(trace.GetTraceType())
5247 case TRACE_UNMANAGED:
5249 // We've hit non-stub code so we're done.
5250 *outAddr = trace.GetAddress();
5251 *outFlags = CLRDATA_FOLLOW_STUB_EXIT;
5254 case TRACE_UNJITTED_METHOD:
5255 // The stub causes jitting, so return
5256 // the address of the jit-complete routine
5257 // so that the real native address can
5258 // be picked up once the JIT is done.
5260 // One special case is ngen'ed code that
5261 // needs the prestub run. This results in
5262 // an unjitted trace but no jitting will actually
5263 // occur since the code is ngen'ed. Detect
5264 // this and redirect to the actual code.
5265 methodDesc = trace.GetMethodDesc();
5266 if (methodDesc->IsPreImplemented() &&
5267 !methodDesc->IsPointingToStableNativeCode() &&
5268 !methodDesc->IsGenericMethodDefinition() &&
5269 methodDesc->HasNativeCode())
5271 *outAddr = methodDesc->GetNativeCode();
5272 *outFlags = CLRDATA_FOLLOW_STUB_EXIT;
5276 *outAddr = GFN_TADDR(DACNotifyCompilationFinished);
5277 outBuffer->u.flags = STUB_BUF_METHOD_JITTED;
5278 outBuffer->u.addr = PTR_HOST_TO_TADDR(methodDesc);
5279 *outFlags = CLRDATA_FOLLOW_STUB_INTERMEDIATE;
5282 case TRACE_FRAME_PUSH:
5285 return E_INVALIDARG;
5288 *outAddr = trace.GetAddress();
5289 outBuffer->u.flags = STUB_BUF_FRAME_PUSHED;
5290 outBuffer->u.addr = trace.GetAddress();
5291 *outFlags = CLRDATA_FOLLOW_STUB_INTERMEDIATE;
5294 case TRACE_MGR_PUSH:
5297 return E_INVALIDARG;
5300 *outAddr = trace.GetAddress();
5301 outBuffer->u.flags = STUB_BUF_STUB_MANAGER_PUSHED;
5302 outBuffer->u.addr = trace.GetAddress();
5303 outBuffer->u.arg1 = PTR_HOST_TO_TADDR(trace.GetStubManager());
5304 *outFlags = CLRDATA_FOLLOW_STUB_INTERMEDIATE;
5308 return E_INVALIDARG;
5314 HRESULT STDMETHODCALLTYPE
5315 ClrDataAccess::FollowStub(
5316 /* [in] */ ULONG32 inFlags,
5317 /* [in] */ CLRDATA_ADDRESS inAddr,
5318 /* [in] */ CLRDATA_FOLLOW_STUB_BUFFER* _inBuffer,
5319 /* [out] */ CLRDATA_ADDRESS* outAddr,
5320 /* [out] */ CLRDATA_FOLLOW_STUB_BUFFER* _outBuffer,
5321 /* [out] */ ULONG32* outFlags)
5323 return FollowStub2(NULL, inFlags, inAddr, _inBuffer,
5324 outAddr, _outBuffer, outFlags);
5327 HRESULT STDMETHODCALLTYPE
5328 ClrDataAccess::FollowStub2(
5329 /* [in] */ IXCLRDataTask* task,
5330 /* [in] */ ULONG32 inFlags,
5331 /* [in] */ CLRDATA_ADDRESS _inAddr,
5332 /* [in] */ CLRDATA_FOLLOW_STUB_BUFFER* _inBuffer,
5333 /* [out] */ CLRDATA_ADDRESS* _outAddr,
5334 /* [out] */ CLRDATA_FOLLOW_STUB_BUFFER* _outBuffer,
5335 /* [out] */ ULONG32* outFlags)
5339 if ((inFlags & ~(CLRDATA_FOLLOW_STUB_DEFAULT)) != 0)
5341 return E_INVALIDARG;
5344 STUB_BUF* inBuffer = (STUB_BUF*)_inBuffer;
5345 STUB_BUF* outBuffer = (STUB_BUF*)_outBuffer;
5348 (inBuffer->u.flags <= STUB_BUF_FLAGS_START ||
5349 inBuffer->u.flags >= STUB_BUF_FLAGS_END))
5351 return E_INVALIDARG;
5359 TADDR inAddr = TO_TADDR(_inAddr);
5361 Thread* thread = task ? ((ClrDataTask*)task)->GetThread() : NULL;
5366 if ((status = FollowStubStep(thread,
5377 // Some stub tracing just requests further iterations
5378 // of processing, so detect that case and loop.
5379 if (outAddr != inAddr)
5381 // We can make forward progress, we're done.
5382 *_outAddr = TO_CDADDR(outAddr);
5386 // We need more processing. As a protection
5387 // against infinite loops in corrupted or buggy
5388 // situations, we only allow this to happen a
5389 // small number of times.
5392 ZeroMemory(outBuffer, sizeof(*outBuffer));
5397 cycleBuf = *outBuffer;
5398 inBuffer = &cycleBuf;
5403 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5408 EX_END_CATCH(SwallowAllExceptions)
5415 #pragma warning(push)
5416 #pragma warning(disable:4297)
5419 ClrDataAccess::GetGcNotification(GcEvtArgs* gcEvtArgs)
5427 if (gcEvtArgs->typ >= GC_EVENT_TYPE_MAX)
5429 status = E_INVALIDARG;
5433 GcNotifications gn(GetHostGcNotificationTable());
5436 status = E_OUTOFMEMORY;
5440 GcEvtArgs *res = gn.GetNotification(*gcEvtArgs);
5455 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5460 EX_END_CATCH(SwallowAllExceptions)
5467 ClrDataAccess::SetGcNotification(IN GcEvtArgs gcEvtArgs)
5475 if (gcEvtArgs.typ >= GC_EVENT_TYPE_MAX)
5477 status = E_INVALIDARG;
5481 GcNotifications gn(GetHostGcNotificationTable());
5484 status = E_OUTOFMEMORY;
5488 if (gn.SetNotification(gcEvtArgs) && gn.UpdateOutOfProcTable())
5501 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5506 EX_END_CATCH(SwallowAllExceptions)
5513 #pragma warning(pop)
5517 ClrDataAccess::Initialize(void)
5520 CLRDATA_ADDRESS base;
5523 // We do not currently support cross-platform
5524 // debugging. Verify that cross-platform is not
5528 // Determine our platform based on the pre-processor macros set when we were built
5531 #if defined(DBG_TARGET_X86)
5532 CorDebugPlatform hostPlatform = CORDB_PLATFORM_POSIX_X86;
5533 #elif defined(DBG_TARGET_AMD64)
5534 CorDebugPlatform hostPlatform = CORDB_PLATFORM_POSIX_AMD64;
5535 #elif defined(DBG_TARGET_ARM)
5536 CorDebugPlatform hostPlatform = CORDB_PLATFORM_POSIX_ARM;
5537 #elif defined(DBG_TARGET_ARM64)
5538 CorDebugPlatform hostPlatform = CORDB_PLATFORM_POSIX_ARM64;
5540 #error Unknown Processor.
5543 #if defined(DBG_TARGET_X86)
5544 CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_X86;
5545 #elif defined(DBG_TARGET_AMD64)
5546 CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_AMD64;
5547 #elif defined(DBG_TARGET_ARM)
5548 CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_ARM;
5549 #elif defined(DBG_TARGET_ARM64)
5550 CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_ARM64;
5552 #error Unknown Processor.
5556 CorDebugPlatform targetPlatform;
5557 IfFailRet(m_pTarget->GetPlatform(&targetPlatform));
5559 if (targetPlatform != hostPlatform)
5561 // DAC fatal error: Platform mismatch - the platform reported by the data target
5562 // is not what this version of mscordacwks.dll was built for.
5563 return CORDBG_E_UNCOMPATIBLE_PLATFORMS;
5567 // Get the current DLL base for mscorwks globals.
5568 // In case of multiple-CLRs, there may be multiple dlls named "mscorwks".
5569 // code:OpenVirtualProcess can take the base address (clrInstanceId) to select exactly
5570 // which CLR to is being target. If so, m_globalBase will already be set.
5573 if (m_globalBase == 0)
5575 // Caller didn't specify which CLR to debug. This supports Whidbey SOS cases, so we should
5576 // be using a legacy data target.
5577 if (m_pLegacyTarget == NULL)
5579 DacError(E_INVALIDARG);
5583 // Since this is Whidbey, assume there's only 1 CLR named "mscorwks.dll" and pick that.
5584 IfFailRet(m_pLegacyTarget->GetImageBase(MAIN_CLR_DLL_NAME_W, &base));
5586 m_globalBase = TO_TADDR(base);
5589 // We don't need to try too hard to prevent
5590 // multiple initializations as each one will
5591 // copy the same data into the globals and so
5592 // cannot interfere with each other.
5595 IfFailRet(GetDacGlobals());
5596 IfFailRet(DacGetHostVtPtrs());
5601 // DAC is now setup and ready to use
5604 // Do some validation
5605 IfFailRet(VerifyDlls());
5607 // To support EH SxS, utilcode requires the base address of the runtime
5608 // as part of its initialization so that functions like "WasThrownByUs" work correctly since
5609 // they use the CLR base address to check if an exception was raised by a given instance of the runtime
5612 // Thus, when DAC is initialized, initialize utilcode with the base address of the runtime loaded in the
5613 // target process. This is similar to work done in CorDB::SetTargetCLR for mscordbi.
5615 // Initialize UtilCode for SxS scenarios
5616 CoreClrCallbacks cccallbacks;
5617 cccallbacks.m_hmodCoreCLR = (HINSTANCE)m_globalBase; // Base address of the runtime in the target process
5618 cccallbacks.m_pfnIEE = NULL;
5619 cccallbacks.m_pfnGetCORSystemDirectory = NULL;
5620 InitUtilcode(cccallbacks);
5626 ClrDataAccess::FindClrThreadByTaskId(ULONG64 taskId)
5628 Thread* thread = NULL;
5630 if (!ThreadStore::s_pThreadStore)
5635 while ((thread = ThreadStore::GetAllThreadList(thread, 0, 0)))
5637 if (thread->GetThreadId() == (DWORD)taskId)
5647 ClrDataAccess::IsPossibleCodeAddress(IN TADDR address)
5653 // First do a trivial check on the readability of the
5654 // address. This makes for quick rejection of bogus
5655 // addresses that the debugger sends in when searching
5656 // stacks for return addresses.
5657 // XXX Microsoft - Will this cause problems in minidumps
5658 // where it's possible the stub is identifiable but
5659 // the stub code isn't present? Yes, but the lack
5660 // of that code could confuse the walker on its own
5661 // if it does code analysis.
5662 if ((m_pTarget->ReadVirtual(address, &testRead, sizeof(testRead),
5663 &testDone) != S_OK) ||
5666 return E_INVALIDARG;
5673 ClrDataAccess::GetFullMethodName(
5674 IN MethodDesc* methodDesc,
5675 IN ULONG32 symbolChars,
5676 OUT ULONG32* symbolLen,
5677 __out_ecount_part_opt(symbolChars, *symbolLen) LPWSTR symbol
5681 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
5684 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
5686 TypeString::AppendMethodInternal(s, methodDesc, TypeString::FormatSignature|TypeString::FormatNamespace|TypeString::FormatFullInst);
5688 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
5692 if (!MdCacheGetEEName(dac_cast<TADDR>(methodDesc), s))
5698 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
5702 // Copy as much as we can and truncate the rest.
5703 wcsncpy_s(symbol, symbolChars, s.GetUnicode(), _TRUNCATE);
5707 *symbolLen = s.GetCount() + 1;
5709 if (symbol != NULL && symbolChars < (s.GetCount() + 1))
5716 ClrDataAccess::GetJitHelperName(
5718 IN bool dynamicHelpersOnly /*=false*/
5721 const static PCSTR s_rgHelperNames[] = {
5722 #define JITHELPER(code,fn,sig) #code,
5723 #include <jithelpers.h>
5725 static_assert_no_msg(COUNTOF(s_rgHelperNames) == CORINFO_HELP_COUNT);
5728 if (!dynamicHelpersOnly)
5730 if (!dynamicHelpersOnly && g_runtimeLoadedBaseAddress <= address &&
5731 address < g_runtimeLoadedBaseAddress + g_runtimeVirtualSize)
5732 #endif // FEATURE_PAL
5734 // Read the whole table from the target in one shot for better performance
5735 VMHELPDEF * pTable = static_cast<VMHELPDEF *>(
5736 PTR_READ(dac_cast<TADDR>(&hlpFuncTable), CORINFO_HELP_COUNT * sizeof(VMHELPDEF)));
5738 for (int i = 0; i < CORINFO_HELP_COUNT; i++)
5740 if (address == (TADDR)(pTable[i].pfnHelper))
5741 return s_rgHelperNames[i];
5745 // Check if its a dynamically generated JIT helper
5746 const static CorInfoHelpFunc s_rgDynamicHCallIds[] = {
5747 #define DYNAMICJITHELPER(code, fn, sig) code,
5748 #define JITHELPER(code, fn,sig)
5749 #include <jithelpers.h>
5752 // Read the whole table from the target in one shot for better performance
5753 VMHELPDEF * pDynamicTable = static_cast<VMHELPDEF *>(
5754 PTR_READ(dac_cast<TADDR>(&hlpDynamicFuncTable), DYNAMIC_CORINFO_HELP_COUNT * sizeof(VMHELPDEF)));
5755 for (unsigned d = 0; d < DYNAMIC_CORINFO_HELP_COUNT; d++)
5757 if (address == (TADDR)(pDynamicTable[d].pfnHelper))
5759 return s_rgHelperNames[s_rgDynamicHCallIds[d]];
5767 ClrDataAccess::RawGetMethodName(
5768 /* [in] */ CLRDATA_ADDRESS address,
5769 /* [in] */ ULONG32 flags,
5770 /* [in] */ ULONG32 bufLen,
5771 /* [out] */ ULONG32 *symbolLen,
5772 /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR symbolBuf[ ],
5773 /* [out] */ CLRDATA_ADDRESS* displacement)
5776 _ASSERTE((address & THUMB_CODE) == 0);
5777 address &= ~THUMB_CODE;
5780 const UINT k_cch64BitHexFormat = COUNTOF("1234567812345678");
5785 return E_INVALIDARG;
5789 if( (status = TRY_CLRDATA_ADDRESS_TO_TADDR(address, &taddr)) != S_OK )
5794 if ((status = IsPossibleCodeAddress(taddr)) != S_OK)
5799 PTR_StubManager pStubManager;
5800 MethodDesc* methodDesc = NULL;
5803 EECodeInfo codeInfo(TO_TADDR(address));
5804 if (codeInfo.IsValid())
5808 *displacement = codeInfo.GetRelOffset();
5811 methodDesc = codeInfo.GetMethodDesc();
5812 goto NameFromMethodDesc;
5816 pStubManager = StubManager::FindStubManager(TO_TADDR(address));
5817 if (pStubManager != NULL)
5825 // Special-cased stub managers
5827 #ifdef FEATURE_PREJIT
5828 if (pStubManager == RangeSectionStubManager::g_pManager)
5830 switch (RangeSectionStubManager::GetStubKind(TO_TADDR(address)))
5832 case STUB_CODE_BLOCK_PRECODE:
5835 case STUB_CODE_BLOCK_JUMPSTUB:
5844 if (pStubManager == PrecodeStubManager::g_pManager)
5846 #ifdef FEATURE_PREJIT
5849 PCODE alignedAddress = AlignDown(TO_TADDR(address), PRECODE_ALIGNMENT);
5852 alignedAddress += THUMB_CODE;
5855 SIZE_T maxPrecodeSize = sizeof(StubPrecode);
5857 #ifdef HAS_RELATIVE_STUB_PRECODE
5858 maxPrecodeSize = max(maxPrecodeSize, sizeof(RelativeStubPrecode));
5861 #ifdef HAS_RELATIVE_FIXUP_PRECODE
5862 maxPrecodeSize = max(maxPrecodeSize, sizeof(RelativeFixupPrecode));
5865 #ifdef HAS_THISPTR_RETBUF_PRECODE
5866 maxPrecodeSize = max(maxPrecodeSize, sizeof(ThisPtrRetBufPrecode));
5869 for (SIZE_T i = 0; i < maxPrecodeSize / PRECODE_ALIGNMENT; i++)
5873 // Try to find matching precode entrypoint
5874 Precode* pPrecode = Precode::GetPrecodeFromEntryPoint(alignedAddress, TRUE);
5875 if (pPrecode != NULL)
5877 methodDesc = pPrecode->GetMethodDesc();
5878 if (methodDesc != NULL)
5880 if (DacValidateMD(methodDesc))
5884 *displacement = TO_TADDR(address) - PCODEToPINSTR(alignedAddress);
5886 goto NameFromMethodDesc;
5890 alignedAddress -= PRECODE_ALIGNMENT;
5895 EX_END_CATCH(SwallowAllExceptions)
5899 if (pStubManager == JumpStubStubManager::g_pManager)
5901 #ifdef FEATURE_PREJIT
5904 PCODE pTarget = decodeBackToBackJump(TO_TADDR(address));
5906 HRESULT hr = GetRuntimeNameByAddress(pTarget, flags, bufLen, symbolLen, symbolBuf, NULL);
5912 PCSTR pHelperName = GetJitHelperName(pTarget);
5913 if (pHelperName != NULL)
5915 hr = ConvertUtf8(pHelperName, bufLen, symbolLen, symbolBuf);
5923 static WCHAR s_wszFormatNameWithStubManager[] = W("CLRStub[%s]@%I64x");
5925 LPCWSTR wszStubManagerName = pStubManager->GetStubManagerName(TO_TADDR(address));
5926 _ASSERTE(wszStubManagerName != NULL);
5928 int result = _snwprintf_s(
5932 s_wszFormatNameWithStubManager,
5933 wszStubManagerName, // Arg 1 = stub name
5934 TO_TADDR(address)); // Arg 2 = stub hex address
5938 // Printf succeeded, so we have an exact char count to return
5941 size_t cchSymbol = wcslen(symbolBuf) + 1;
5942 if (!FitsIn<ULONG32>(cchSymbol))
5943 return COR_E_OVERFLOW;
5945 *symbolLen = (ULONG32) cchSymbol;
5950 // Printf failed. Estimate a size that will be at least big enough to hold the name
5953 size_t cchSymbol = COUNTOF(s_wszFormatNameWithStubManager) +
5954 wcslen(wszStubManagerName) +
5955 k_cch64BitHexFormat +
5958 if (!FitsIn<ULONG32>(cchSymbol))
5959 return COR_E_OVERFLOW;
5961 *symbolLen = (ULONG32) cchSymbol;
5966 // Do not waste time looking up name for static helper. Debugger can get the actual name from .pdb.
5968 pHelperName = GetJitHelperName(TO_TADDR(address), true /* dynamicHelpersOnly */);
5969 if (pHelperName != NULL)
5976 HRESULT hr = ConvertUtf8(pHelperName, bufLen, symbolLen, symbolBuf);
5983 return E_NOINTERFACE;
5986 if (methodDesc->GetClassification() == mcDynamic &&
5987 !methodDesc->GetSig())
5989 // XXX Microsoft - Should this case have a more specific name?
5990 static WCHAR s_wszFormatNameAddressOnly[] = W("CLRStub@%I64x");
5992 int result = _snwprintf_s(
5996 s_wszFormatNameAddressOnly,
6001 // Printf succeeded, so we have an exact char count to return
6004 size_t cchSymbol = wcslen(symbolBuf) + 1;
6005 if (!FitsIn<ULONG32>(cchSymbol))
6006 return COR_E_OVERFLOW;
6008 *symbolLen = (ULONG32) cchSymbol;
6013 // Printf failed. Estimate a size that will be at least big enough to hold the name
6016 size_t cchSymbol = COUNTOF(s_wszFormatNameAddressOnly) +
6017 k_cch64BitHexFormat +
6020 if (!FitsIn<ULONG32>(cchSymbol))
6021 return COR_E_OVERFLOW;
6023 *symbolLen = (ULONG32) cchSymbol;
6029 return GetFullMethodName(methodDesc, bufLen, symbolLen, symbolBuf);
6033 ClrDataAccess::GetMethodExtents(MethodDesc* methodDesc,
6034 METH_EXTENTS** extents)
6036 CLRDATA_ADDRESS_RANGE* curExtent;
6040 // Get the information from the methoddesc.
6041 // We'll go through the CodeManager + JitManagers, so this should work
6042 // for all types of managed code.
6045 PCODE methodStart = methodDesc->GetNativeCode();
6048 return E_NOINTERFACE;
6051 EECodeInfo codeInfo(methodStart);
6052 _ASSERTE(codeInfo.IsValid());
6054 TADDR codeSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfoToken());
6056 *extents = new (nothrow) METH_EXTENTS;
6059 return E_OUTOFMEMORY;
6062 (*extents)->numExtents = 1;
6063 curExtent = (*extents)->extents;
6064 curExtent->startAddress = TO_CDADDR(methodStart);
6065 curExtent->endAddress =
6066 curExtent->startAddress + codeSize;
6070 (*extents)->curExtent = 0;
6075 // Allocator to pass to the debug-info-stores...
6076 BYTE* DebugInfoStoreNew(void * pData, size_t cBytes)
6078 return new (nothrow) BYTE[cBytes];
6082 ClrDataAccess::GetMethodVarInfo(MethodDesc* methodDesc,
6084 ULONG32* numVarInfo,
6085 ICorDebugInfo::NativeVarInfo** varInfo,
6086 ULONG32* codeOffset)
6089 COUNT_T countNativeVarInfo;
6090 NewHolder<ICorDebugInfo::NativeVarInfo> nativeVars(NULL);
6092 DebugInfoRequest request;
6093 TADDR nativeCodeStartAddr = PCODEToPINSTR(methodDesc->GetNativeCode());
6094 request.InitFromStartingAddr(methodDesc, nativeCodeStartAddr);
6096 BOOL success = DebugInfoManager::GetBoundariesAndVars(
6098 DebugInfoStoreNew, NULL, // allocator
6100 &countNativeVarInfo, &nativeVars);
6108 if (!nativeVars || !countNativeVarInfo)
6110 return E_NOINTERFACE;
6113 *numVarInfo = countNativeVarInfo;
6114 *varInfo = nativeVars;
6115 nativeVars.SuppressRelease(); // To prevent NewHolder from releasing the memory
6119 *codeOffset = (ULONG32)
6120 (address - nativeCodeStartAddr);
6126 ClrDataAccess::GetMethodNativeMap(MethodDesc* methodDesc,
6129 DebuggerILToNativeMap** map,
6131 CLRDATA_ADDRESS* codeStart,
6132 ULONG32* codeOffset)
6134 _ASSERTE((codeOffset == NULL) || (address != NULL));
6136 // Use the DebugInfoStore to get IL->Native maps.
6137 // It doesn't matter whether we're jitted, ngenned etc.
6139 DebugInfoRequest request;
6140 TADDR nativeCodeStartAddr = PCODEToPINSTR(methodDesc->GetNativeCode());
6141 request.InitFromStartingAddr(methodDesc, nativeCodeStartAddr);
6145 ULONG32 countMapCopy;
6146 NewHolder<ICorDebugInfo::OffsetMapping> mapCopy(NULL);
6148 BOOL success = DebugInfoManager::GetBoundariesAndVars(
6150 DebugInfoStoreNew, NULL, // allocator
6151 &countMapCopy, &mapCopy,
6160 // Need to convert map formats.
6161 *numMap = countMapCopy;
6163 *map = new (nothrow) DebuggerILToNativeMap[countMapCopy];
6166 return E_OUTOFMEMORY;
6170 for (i = 0; i < *numMap; i++)
6172 (*map)[i].ilOffset = mapCopy[i].ilOffset;
6173 (*map)[i].nativeStartOffset = mapCopy[i].nativeOffset;
6176 (*map)[i - 1].nativeEndOffset = (*map)[i].nativeStartOffset;
6178 (*map)[i].source = mapCopy[i].source;
6182 (*map)[i - 1].nativeEndOffset = 0;
6186 // Update varion out params.
6189 *codeStart = TO_CDADDR(nativeCodeStartAddr);
6193 *codeOffset = (ULONG32)
6194 (address - nativeCodeStartAddr);
6197 *mapAllocated = true;
6201 // Get the MethodDesc for a function
6204 // pModule - pointer to the module for the function
6205 // memberRef - metadata token for the function
6207 // MethodDesc for the function
6208 MethodDesc * ClrDataAccess::FindLoadedMethodRefOrDef(Module* pModule,
6211 CONTRACT(MethodDesc *)
6214 PRECONDITION(CheckPointer(pModule));
6215 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
6219 // Must have a MemberRef or a MethodDef
6220 mdToken tkType = TypeFromToken(memberRef);
6221 _ASSERTE((tkType == mdtMemberRef) || (tkType == mdtMethodDef));
6223 if (tkType == mdtMemberRef)
6225 RETURN pModule->LookupMemberRefAsMethod(memberRef);
6228 RETURN pModule->LookupMethodDef(memberRef);
6229 } // FindLoadedMethodRefOrDef
6232 // ReportMem - report a region of memory for dump gathering
6234 // If you specify that you expect success, any failure will cause ReportMem to
6235 // return false. If you do not expect success, true is always returned.
6236 // This function only throws when all dump collection should be cancelled.
6239 // addr - the starting target address for the memory to report
6240 // size - the length (in bytes) to report
6241 // fExpectSuccess - if true (the default), then we expect that this region of memory
6242 // should be fully readable. Any read errors indicate a corrupt target.
6244 bool ClrDataAccess::ReportMem(TADDR addr, TSIZE_T size, bool fExpectSuccess /*= true*/)
6246 SUPPORTS_DAC_HOST_ONLY;
6248 // This block of code is to help debugging blocks that we report
6249 // to minidump/heapdump. You can set break point here to view the static
6250 // variable to figure out the size of blocks that we are reporting.
6251 // Most useful is set conditional break point to catch large chuck of
6252 // memory. We will leave it here for all builds.
6254 static TADDR debugAddr;
6255 static TSIZE_T debugSize;
6260 if (!addr || addr == (TADDR)-1 || !size)
6269 // Try and sanity-check the reported region of memory
6272 // in debug builds, sanity-check all reports
6273 const TSIZE_T k_minSizeToCheck = 1;
6275 // in retail builds, only sanity-check larger chunks which have the potential to waste a
6276 // lot of time and/or space. This avoids the overhead of checking for the majority of
6277 // memory regions (which are small).
6278 const TSIZE_T k_minSizeToCheck = 1024;
6280 if (size >= k_minSizeToCheck)
6282 if (!IsFullyReadable(addr, size))
6284 if (!fExpectSuccess)
6286 // We know the read might fail (eg. we're trying to find mapped pages in
6287 // a module image), so just skip this block silently.
6288 // Note that the EnumMemoryRegion callback won't necessarily do anything if any part of
6289 // the region is unreadable, and so there is no point in calling it. For cases where we expect
6290 // the read might fail, but we want to report any partial blocks, we have to break up the region
6291 // into pages and try reporting each page anyway
6295 // We're reporting bogus memory, so the target must be corrupt (or there is a issue). We should abort
6296 // reporting and continue with the next data structure (where the exception is caught),
6297 // just like we would for a DAC read error (otherwise we might do something stupid
6298 // like get into an infinite loop, or otherwise waste time with corrupt data).
6300 TARGET_CONSISTENCY_CHECK(false, "Found unreadable memory while reporting memory regions for dump gathering");
6305 // Minidumps should never contain data structures that are anywhere near 4MB. If we see this, it's
6306 // probably due to memory corruption. To keep the dump small, we'll truncate the block. Note that
6307 // the size to which the block is truncated is pretty unique, so should be good evidence in a dump
6308 // that this has happened.
6309 // Note that it's hard to say what a good value would be here, or whether we should dump any of the
6310 // data structure at all. Hopefully experience will help guide this going forward.
6311 // @dbgtodo : Extend dump-gathering API to allow a dump-log to be included.
6312 const TSIZE_T kMaxMiniDumpRegion = 4*1024*1024 - 3; // 4MB-3
6313 if( size > kMaxMiniDumpRegion
6314 && (m_enumMemFlags == CLRDATA_ENUM_MEM_MINI
6315 || m_enumMemFlags == CLRDATA_ENUM_MEM_TRIAGE))
6317 TARGET_CONSISTENCY_CHECK( false, "Dump target consistency failure - truncating minidump data structure");
6318 size = kMaxMiniDumpRegion;
6321 // track the total memory reported.
6322 m_cbMemoryReported += size;
6324 // ICLRData APIs take only 32-bit sizes. In practice this will almost always be sufficient, but
6325 // in theory we might have some >4GB ranges on large 64-bit processes doing a heap dump
6326 // (for example, the code:LoaderHeap). If necessary, break up the reporting into maximum 4GB
6327 // chunks so we can use the existing API.
6328 // @dbgtodo : ICorDebugDataTarget should probably use 64-bit sizes
6332 if (size > ULONG_MAX)
6334 enumSize = ULONG_MAX;
6338 enumSize = (ULONG32)size;
6341 // Actually perform the memory reporting callback
6342 status = m_enumMemCb->EnumMemoryRegion(TO_CDADDR(addr), enumSize);
6345 // If dump generation was cancelled, allow us to throw upstack so we'll actually quit.
6346 if ((fExpectSuccess) && (status != COR_E_OPERATIONCANCELED))
6350 // If the return value of EnumMemoryRegion is COR_E_OPERATIONCANCELED,
6351 // it means that user has requested that the minidump gathering be canceled.
6352 // To do this we throw an exception which is caught in EnumMemoryRegionsWrapper.
6353 if (status == COR_E_OPERATIONCANCELED)
6358 // Move onto the next chunk (if any)
6368 // DacUpdateMemoryRegion - updates/poisons a region of memory of generated dump
6371 // addr - target address of the beginning of the memory region
6372 // bufferSize - number of bytes to update/poison
6373 // buffer - data to be written at given target address
6375 bool ClrDataAccess::DacUpdateMemoryRegion(TADDR addr, TSIZE_T bufferSize, BYTE* buffer)
6377 SUPPORTS_DAC_HOST_ONLY;
6380 if (!addr || addr == (TADDR)-1 || !bufferSize)
6385 // track the total memory reported.
6386 m_cbMemoryReported += bufferSize;
6388 if (m_updateMemCb == NULL)
6393 // Actually perform the memory updating callback
6394 status = m_updateMemCb->UpdateMemoryRegion(TO_CDADDR(addr), (ULONG32)bufferSize, buffer);
6404 // Check whether a region of target memory is fully readable.
6407 // addr The base target address of the region
6408 // size The size of the region to analyze
6411 // True if the entire regions appears to be readable, false otherwise.
6414 // The motivation here is that reporting large regions of unmapped address space to dbgeng can result in
6415 // it taking a long time trying to identify a valid subrange. This can happen when the target
6416 // memory is corrupt, and we enumerate a data structure with a dynamic size. Ideally we would just spec
6417 // the ICLRDataEnumMemoryRegionsCallback API to require the client to fail if it detects an unmapped
6418 // memory address in the region. However, we can't change the existing dbgeng code, so for now we'll
6419 // rely on this heuristic here.
6420 // @dbgtodo : Try and get the dbg team to change their EnumMemoryRegion behavior. See DevDiv Bugs 6265
6422 bool ClrDataAccess::IsFullyReadable(TADDR taBase, TSIZE_T dwSize)
6424 // The only way we have to verify that a memory region is readable is to try reading it in it's
6425 // entirety. This is potentially expensive, so we'll rely on a heuristic that spot-checks various
6426 // points in the region.
6428 // Ensure we've got something to check
6432 // Check for overflow
6433 TADDR taEnd = DacTAddrOffset(taBase, dwSize, 1);
6435 // Loop through using expontential growth, being sure to check both the first and last byte
6436 TADDR taCurr = taBase;
6437 TSIZE_T dwInc = 4096;
6441 // Try and read a byte from the target. Note that we don't use PTR_BYTE here because we don't want
6442 // the overhead of inserting entries into the DAC instance cache.
6444 ULONG32 dwBytesRead;
6445 HRESULT hr = m_pTarget->ReadVirtual(taCurr, &b, 1, &dwBytesRead);
6446 if( hr != S_OK || dwBytesRead < 1 )
6451 if (taEnd - taCurr <= 1)
6453 // We just read the last byte so we're done
6454 _ASSERTE( taCurr = taEnd - 1 );
6457 else if (dwInc == 0 || dwInc >= taEnd - taCurr)
6459 // we've reached the end of the exponential series, check the last byte
6464 // advance current pointer (subtraction above ensures this won't overflow)
6467 // double the increment for next time (or set to 0 if it's already the max)
6475 ClrDataAccess::GetHostJitNotificationTable()
6477 if (m_jitNotificationTable == NULL)
6479 m_jitNotificationTable =
6480 JITNotifications::InitializeNotificationTable(1000);
6483 return m_jitNotificationTable;
6487 ClrDataAccess::GetHostGcNotificationTable()
6489 if (m_gcNotificationTable == NULL)
6491 m_gcNotificationTable =
6492 GcNotifications::InitializeNotificationTable(128);
6495 return m_gcNotificationTable;
6499 ClrDataAccess::GetMetaDataFileInfoFromPEFile(PEFile *pPEFile,
6505 __out_ecount(cchFilePath) LPWSTR wszFilePath,
6506 const DWORD cchFilePath)
6508 SUPPORTS_DAC_HOST_ONLY;
6509 PEImage *mdImage = NULL;
6510 PEImageLayout *layout;
6511 IMAGE_DATA_DIRECTORY *pDir = NULL;
6512 COUNT_T uniPathChars = 0;
6516 if (pPEFile->HasNativeImage())
6518 mdImage = pPEFile->GetNativeImage();
6519 _ASSERTE(mdImage != NULL);
6520 layout = mdImage->GetLoadedLayout();
6521 pDir = &(layout->GetCorHeader()->MetaData);
6522 // For ngen image, the IL metadata is stored for private use. So we need to pass
6523 // the RVA hint to find it to debuggers.
6525 if (pDir->Size != 0)
6528 dwRvaHint = pDir->VirtualAddress;
6529 dwDataSize = pDir->Size;
6533 if (pDir == NULL || pDir->Size == 0)
6535 mdImage = pPEFile->GetILimage();
6536 if (mdImage != NULL)
6538 layout = mdImage->GetLoadedLayout();
6539 pDir = &layout->GetCorHeader()->MetaData;
6541 // In IL image case, we do not have any hint to IL metadata since it is stored
6542 // in the corheader.
6545 dwDataSize = pDir->Size;
6553 // Do not fail if path can not be read. Triage dumps don't have paths and we want to fallback
6554 // on searching metadata from IL image.
6555 mdImage->GetPath().DacGetUnicode(cchFilePath, wszFilePath, &uniPathChars);
6557 if (!mdImage->HasNTHeaders() ||
6558 !mdImage->HasCorHeader() ||
6559 !mdImage->HasLoadedLayout() ||
6560 (uniPathChars > cchFilePath))
6565 // It is possible that the module is in-memory. That is the wszFilePath here is empty.
6566 // We will try to use the module name instead in this case for hosting debugger
6568 if (wcslen(wszFilePath) == 0)
6570 mdImage->GetModuleFileNameHintForDAC().DacGetUnicode(cchFilePath, wszFilePath, &uniPathChars);
6571 if (uniPathChars > cchFilePath)
6577 dwTimeStamp = layout->GetTimeDateStamp();
6578 dwSize = (ULONG32)layout->GetVirtualSize();
6584 bool ClrDataAccess::GetILImageInfoFromNgenPEFile(PEFile *peFile,
6587 __out_ecount(cchFilePath) LPWSTR wszFilePath,
6588 const DWORD cchFilePath)
6590 SUPPORTS_DAC_HOST_ONLY;
6591 DWORD dwWritten = 0;
6593 // use the IL File name
6594 if (!peFile->GetPath().DacGetUnicode(cchFilePath, wszFilePath, (COUNT_T *)(&dwWritten)))
6596 // Use DAC hint to retrieve the IL name.
6597 peFile->GetModuleFileNameHint().DacGetUnicode(cchFilePath, wszFilePath, (COUNT_T *)(&dwWritten));
6599 #ifdef FEATURE_PREJIT
6600 // Need to get IL image information from cached info in the ngen image.
6601 dwTimeStamp = peFile->GetLoaded()->GetNativeVersionInfo()->sourceAssembly.timeStamp;
6602 dwSize = peFile->GetLoaded()->GetNativeVersionInfo()->sourceAssembly.ilImageSize;
6606 #endif // FEATURE_PREJIT
6611 #if defined(FEATURE_CORESYSTEM)
6613 // We extract "ni.dll or .ni.winmd" from the NGEM image name to obtain the IL image name.
6614 // In the end we add given ilExtension.
6615 // This dependecy is based on Apollo installer behavior.
6616 bool ClrDataAccess::GetILImageNameFromNgenImage( LPCWSTR ilExtension,
6617 __out_ecount(cchFilePath) LPWSTR wszFilePath,
6618 const DWORD cchFilePath)
6620 if (wszFilePath == NULL || cchFilePath == 0)
6625 _wcslwr_s(wszFilePath, cchFilePath);
6626 // Find the "ni.dll" or "ni.winmd" extension (check for PEFile isWinRT something to know when is winmd or not.
6627 // If none exists use NGEN image name.
6629 const WCHAR* ngenExtension[] = {W("ni.dll"), W("ni.winmd")};
6631 for (unsigned i = 0; i < COUNTOF(ngenExtension); ++i)
6633 if (wcslen(ilExtension) > wcslen(ngenExtension[i]))
6635 // We should not have IL image name bigger than NGEN image.
6636 // It will not fit inside wszFilePath.
6639 LPWSTR wszFileExtension = wcsstr(wszFilePath, ngenExtension[i]);
6640 if (wszFileExtension != 0)
6642 LPWSTR wszNextFileExtension = wszFileExtension;
6643 // Find last occurence
6646 wszFileExtension = wszNextFileExtension;
6647 wszNextFileExtension = wcsstr(wszFileExtension + 1, ngenExtension[i]);
6648 } while (wszNextFileExtension != 0);
6650 // Overwrite ni.dll or ni.winmd with ilExtension(.dll, .winmd)
6651 if (!memcpy_s(wszFileExtension,
6652 wcslen(ngenExtension[i])*sizeof(WCHAR),
6654 wcslen(ilExtension)*sizeof(WCHAR)))
6656 wszFileExtension[wcslen(ilExtension)] = '\0';
6662 //Use ngen filename if there is no ".ni"
6663 if (wcsstr(wszFilePath, W(".ni")) == 0)
6670 #endif // FEATURE_CORESYSTEM
6673 ClrDataAccess::GetMetaDataFromHost(PEFile* peFile,
6676 DWORD imageTimestamp, imageSize, dataSize;
6677 void* buffer = NULL;
6678 WCHAR uniPath[MAX_LONGPATH] = {0};
6680 bool isNGEN = false;
6681 DAC_INSTANCE* inst = NULL;
6685 // We always ask for the IL image metadata,
6686 // as we expect that to be more
6687 // available than others. The drawback is that
6688 // there may be differences between the IL image
6689 // metadata and native image metadata, so we
6690 // have to mark such alternate metadata so that
6691 // we can fail unsupported usage of it.
6694 // Microsoft - above comment seems to be an unimplemented thing.
6695 // The DAC_MD_IMPORT.isAlternate field gets ultimately set, but
6696 // on the searching I did, I cannot find any usage of it
6697 // other than in the ctor. Should we be doing something, or should
6698 // we remove this comment and the isAlternate field?
6699 // It's possible that test will want us to track whether we have
6700 // an IL image's metadata loaded against an NGEN'ed image
6701 // so the field remains for now.
6703 if (!ClrDataAccess::GetMetaDataFileInfoFromPEFile(
6716 // try direct match for the image that is loaded into the managed process
6717 peFile->GetLoadedMetadata((COUNT_T *)(&dataSize));
6719 DWORD allocSize = 0;
6720 if (!ClrSafeInt<DWORD>::addition(dataSize, sizeof(DAC_INSTANCE), allocSize))
6722 DacError(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW));
6725 inst = m_instances.Alloc(0, allocSize, DAC_DPTR);
6728 DacError(E_OUTOFMEMORY);
6732 buffer = (void*)(inst + 1);
6734 // APIs implemented by hosting debugger. It can use the path/filename, timestamp, and
6735 // file size to find an exact match for the image. If that fails for an ngen'ed image,
6736 // we can request the IL image which it came from.
6737 if (m_legacyMetaDataLocator)
6739 // Legacy API implemented by hosting debugger.
6740 hr = m_legacyMetaDataLocator->GetMetadata(
6744 NULL, // MVID - not used yet
6746 0, // flags - reserved for future.
6753 hr = m_target3->GetMetaData(
6757 NULL, // MVID - not used yet
6759 0, // flags - reserved for future.
6764 if (FAILED(hr) && isNGEN)
6766 // We failed to locate the ngen'ed image. We should try to
6767 // find the matching IL image
6770 if (!ClrDataAccess::GetILImageInfoFromNgenPEFile(
6780 #if defined(FEATURE_CORESYSTEM)
6781 const WCHAR* ilExtension[] = {W("dll"), W("winmd")};
6782 WCHAR ngenImageName[MAX_LONGPATH] = {0};
6783 if (wcscpy_s(ngenImageName, NumItems(ngenImageName), uniPath) != 0)
6787 for (unsigned i = 0; i < COUNTOF(ilExtension); i++)
6789 if (wcscpy_s(uniPath, NumItems(uniPath), ngenImageName) != 0)
6793 // Transform NGEN image name into IL Image name
6794 if (!GetILImageNameFromNgenImage(ilExtension[i], uniPath, NumItems(uniPath)))
6798 #endif//FEATURE_CORESYSTEM
6800 // RVA size in ngen image and IL image is the same. Because the only
6801 // different is in RVA. That is 4 bytes column fixed.
6805 if (m_legacyMetaDataLocator)
6807 hr = m_legacyMetaDataLocator->GetMetadata(
6811 NULL, // MVID - not used yet
6812 0, // pass zero hint here... important
6813 0, // flags - reserved for future.
6820 hr = m_target3->GetMetaData(
6824 NULL, // MVID - not used yet
6825 0, // pass zero hint here... important
6826 0, // flags - reserved for future.
6831 #if defined(FEATURE_CORESYSTEM)
6837 #endif // FEATURE_CORESYSTEM
6845 *isAlternate = isAlt;
6846 m_instances.AddSuperseded(inst);
6852 m_instances.ReturnAlloc(inst);
6858 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6860 // Given a PEFile or a ReflectionModule try to find the corresponding metadata
6861 // We will first ask debugger to locate it. If fail, we will try
6862 // to get it from the target process
6864 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6866 ClrDataAccess::GetMDImport(const PEFile* peFile, const ReflectionModule* reflectionModule, bool throwEx)
6869 PTR_CVOID mdBaseTarget = NULL;
6871 IMDInternalImport* mdImport = NULL;
6872 PVOID mdBaseHost = NULL;
6873 bool isAlternate = false;
6875 _ASSERTE((peFile == NULL && reflectionModule != NULL) || (peFile != NULL && reflectionModule == NULL));
6876 TADDR peFileAddr = (peFile != NULL) ? dac_cast<TADDR>(peFile) : dac_cast<TADDR>(reflectionModule);
6879 // Look for one we've already created.
6881 mdImport = m_mdImports.Get(peFileAddr);
6882 if (mdImport != NULL)
6889 // Get the metadata size
6890 mdBaseTarget = ((PEFile*)peFile)->GetLoadedMetadata(&mdSize);
6892 else if (reflectionModule != NULL)
6895 PTR_SBuffer metadataBuffer = reflectionModule->GetDynamicMetadataBuffer();
6896 if (metadataBuffer != PTR_NULL)
6898 mdBaseTarget = dac_cast<PTR_CVOID>((metadataBuffer->DacGetRawBuffer()).StartAddress());
6899 mdSize = metadataBuffer->GetSize();
6919 if (mdBaseTarget == PTR_NULL)
6927 // Maybe the target process has the metadata
6928 // Find out where the metadata for the image is
6929 // in the target's memory.
6932 // Read the metadata into the host process. Make sure pass in false in the last
6933 // parameter. This is only matters when producing skinny mini-dump. This will
6934 // prevent metadata gets reported into mini-dump.
6936 mdBaseHost = DacInstantiateTypeByAddressNoReport(dac_cast<TADDR>(mdBaseTarget), mdSize,
6940 // Try to see if debugger can locate it
6941 if (peFile != NULL && mdBaseHost == NULL && (m_target3 || m_legacyMetaDataLocator))
6943 // We couldn't read the metadata from memory. Ask
6944 // the target for metadata as it may be able to
6945 // provide it from some alternate means.
6946 mdBaseHost = GetMetaDataFromHost(const_cast<PEFile *>(peFile), &isAlternate);
6949 if (mdBaseHost == NULL)
6951 // cannot locate metadata anywhere
6954 DacError(E_INVALIDARG);
6960 // Open the MD interface on the host copy of the metadata.
6963 status = GetMDInternalInterface(mdBaseHost, mdSize, ofRead,
6964 IID_IMDInternalImport,
6976 // Remember the object for this module for
6977 // possible later use.
6978 // The m_mdImports list does get cleaned up by calls to ClrDataAccess::Flush,
6979 // i.e. every time the process changes state.
6981 if (m_mdImports.Add(peFileAddr, mdImport, isAlternate) == NULL)
6983 mdImport->Release();
6984 DacError(E_OUTOFMEMORY);
6992 // Set whether inconsistencies in the target should raise asserts.
6993 // This overrides the default initial setting.
6996 // fEnableAsserts - whether ASSERTs in dacized code should be enabled
6999 void ClrDataAccess::SetTargetConsistencyChecks(bool fEnableAsserts)
7001 LIMITED_METHOD_DAC_CONTRACT;
7002 m_fEnableTargetConsistencyAsserts = fEnableAsserts;
7006 // Get whether inconsistencies in the target should raise asserts.
7009 // whether ASSERTs in dacized code should be enabled
7012 // The implementation of ASSERT accesses this via code:DacTargetConsistencyAssertsEnabled
7014 // By default, this is disabled, unless COMPlus_DbgDACEnableAssert is set (see code:ClrDataAccess::ClrDataAccess).
7015 // This is necessary for compatibility. For example, SOS expects to be able to scan for
7016 // valid MethodTables etc. (which may cause ASSERTs), and also doesn't want ASSERTs when working
7017 // with targets with corrupted memory.
7019 // Calling code:ClrDataAccess::SetTargetConsistencyChecks overrides the default setting.
7021 bool ClrDataAccess::TargetConsistencyAssertsEnabled()
7023 LIMITED_METHOD_DAC_CONTRACT;
7024 return m_fEnableTargetConsistencyAsserts;
7027 #ifdef FEATURE_CORESYSTEM
7028 #define ctime_s _ctime32_s
7029 #define time_t __time32_t
7033 // VerifyDlls - Validate that the mscorwks in the target matches this version of mscordacwks
7034 // Only done on Windows and Mac builds at the moment.
7035 // See code:CordbProcess::CordbProcess#DBIVersionChecking for more information regarding version checking.
7037 HRESULT ClrDataAccess::VerifyDlls()
7040 // Provide a knob for disabling this check if we really want to try and proceed anyway with a
7041 // DAC mismatch. DAC behavior may be arbitrarily bad - globals probably won't be at the same
7042 // address, data structures may be laid out differently, etc.
7043 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgDACSkipVerifyDlls))
7048 // Read the debug directory timestamp from the target mscorwks image using DAC
7049 // Note that we don't use the PE timestamp because the PE file might be changed in ways
7050 // that don't effect the PDB (and therefore don't effect DAC). Specifically, we rebase
7051 // our DLLs at the end of a build, that changes the PE file, but not the PDB.
7052 // Note that if we wanted to be extra careful, we could read the CV contents (which includes
7053 // the GUID signature) and verify it matches. Using the timestamp is useful for helpful error
7054 // messages, and should be sufficient in any real scenario.
7055 DWORD timestamp = 0;
7060 // Note that we don't need to worry about ensuring the image memory read by this code
7061 // is saved in a minidump. Managed minidump debugging already requires that you have
7062 // the full mscorwks.dll available at debug time (eg. windbg won't even load DAC without it).
7063 PEDecoder pedecoder(dac_cast<PTR_VOID>(m_globalBase));
7065 // We use the first codeview debug directory entry since this should always refer to the single
7066 // PDB for mscorwks.dll.
7067 const UINT k_maxDebugEntries = 32; // a reasonable upper limit in case of corruption
7068 for( UINT i = 0; i < k_maxDebugEntries; i++)
7070 PTR_IMAGE_DEBUG_DIRECTORY pDebugEntry = pedecoder.GetDebugDirectoryEntry(i);
7072 // If there are no more entries, then stop
7073 if (pDebugEntry == NULL)
7076 // Ignore non-codeview entries. Some scenarios (eg. optimized builds), there may be extra
7077 // debug directory entries at the end of some other type.
7078 if (pDebugEntry->Type == IMAGE_DEBUG_TYPE_CODEVIEW)
7080 // Found a codeview entry - use it's timestamp for comparison
7081 timestamp = pDebugEntry->TimeDateStamp;
7085 char szMsgBuf[1024];
7086 _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE,
7087 "Failed to find any valid codeview debug directory entry in %s image",
7088 MAIN_CLR_MODULE_NAME_A);
7089 _ASSERTE_MSG(timestamp != 0, szMsgBuf);
7093 if (!DacExceptionFilter(GET_EXCEPTION(), this, &hr))
7098 EX_END_CATCH(SwallowAllExceptions)
7105 // Validate that we got a timestamp and it matches what the DAC table told us to expect
7106 if (timestamp == 0 || timestamp != g_dacTableInfo.dwID0)
7108 // Timestamp mismatch. This means mscordacwks is being used with a version of
7109 // mscorwks other than the one it was built for. This will not work reliably.
7112 // Check if verbose asserts are enabled. The default is up to the specific instantiation of
7113 // ClrDataAccess, but can be overridden (in either direction) by a COMPlus_ knob.
7114 // Note that we check this knob every time because it may be handy to turn it on in
7115 // the environment mid-flight.
7116 DWORD dwAssertDefault = m_fEnableDllVerificationAsserts ? 1 : 0;
7117 if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgDACAssertOnMismatch, dwAssertDefault))
7119 // Output a nice error message that contains the timestamps in string format.
7120 time_t actualTime = timestamp;
7121 char szActualTime[30];
7122 ctime_s(szActualTime, sizeof(szActualTime), &actualTime);
7124 time_t expectedTime = g_dacTableInfo.dwID0;
7125 char szExpectedTime[30];
7126 ctime_s(szExpectedTime, sizeof(szExpectedTime), &expectedTime);
7128 // Create a nice detailed message for the assert dialog.
7129 // Note that the strings returned by ctime_s have terminating newline characters.
7130 // This is technically a TARGET_CONSISTENCY_CHECK because a corrupt target could,
7131 // in-theory, have a corrupt mscrowks PE header and cause this check to fail
7132 // unnecessarily. However, this check occurs during startup, before we know
7133 // whether target consistency checks should be enabled, so it's always enabled
7136 char szMsgBuf[1024];
7137 _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE,
7138 "DAC fatal error: %s/mscordacwks.dll version mismatch\n\n"\
7139 "The debug directory timestamp of the loaded %s does not match the\n"\
7140 "version mscordacwks.dll was built for.\n"\
7141 "Expected %s timestamp: %s"\
7142 "Actual %s timestamp: %s\n"\
7143 "DAC will now fail to initialize with a CORDBG_E_MISMATCHED_CORWKS_AND_DACWKS_DLLS\n"\
7144 "error. If you really want to try and use the mimatched DLLs, you can disable this\n"\
7145 "check by setting COMPlus_DbgDACSkipVerifyDlls=1. However, using a mismatched DAC\n"\
7146 "DLL will usually result in arbitrary debugger failures.\n",
7147 MAIN_CLR_DLL_NAME_A,
7148 MAIN_CLR_DLL_NAME_A,
7149 MAIN_CLR_DLL_NAME_A,
7151 MAIN_CLR_DLL_NAME_A,
7153 _ASSERTE_MSG(false, szMsgBuf);
7157 // Return a specific hresult indicating this problem
7158 return CORDBG_E_MISMATCHED_CORWKS_AND_DACWKS_DLLS;
7160 #endif // FEATURE_PAL
7165 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
7167 void ClrDataAccess::InitStreamsForWriting(IN CLRDataEnumMemoryFlags flags)
7169 // enforce this should only be called when generating triage and mini-dumps
7170 if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE)
7175 if (m_streams == NULL)
7176 m_streams = new DacStreamManager(g_MiniMetaDataBuffAddress, g_MiniMetaDataBuffMaxSize);
7178 if (!m_streams->PrepareStreamsForWriting())
7186 if (m_streams != NULL)
7192 EX_END_CATCH(SwallowAllExceptions)
7195 bool ClrDataAccess::MdCacheAddEEName(TADDR taEEStruct, const SString& name)
7197 bool result = false;
7200 if (m_streams != NULL)
7201 result = m_streams->MdCacheAddEEName(taEEStruct, name);
7207 EX_END_CATCH(SwallowAllExceptions)
7212 void ClrDataAccess::EnumStreams(IN CLRDataEnumMemoryFlags flags)
7214 // enforce this should only be called when generating triage and mini-dumps
7215 if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE)
7220 if (m_streams != NULL)
7221 m_streams->EnumStreams(flags);
7226 EX_END_CATCH(SwallowAllExceptions)
7229 bool ClrDataAccess::MdCacheGetEEName(TADDR taEEStruct, SString & eeName)
7231 bool result = false;
7234 if (m_streams == NULL)
7235 m_streams = new DacStreamManager(g_MiniMetaDataBuffAddress, g_MiniMetaDataBuffMaxSize);
7237 result = m_streams->MdCacheGetEEName(taEEStruct, eeName);
7243 EX_END_CATCH(SwallowAllExceptions)
7248 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
7250 // Needed for RT_RCDATA.
7251 #define MAKEINTRESOURCE(v) MAKEINTRESOURCEW(v)
7253 // this funny looking double macro forces x to be macro expanded before L is prepended
7254 #define _WIDE(x) _WIDE2(x)
7255 #define _WIDE2(x) W(x)
7258 ClrDataAccess::GetDacGlobals()
7261 #ifdef DAC_TABLE_SIZE
7262 if (DAC_TABLE_SIZE != sizeof(g_dacGlobals))
7264 return E_INVALIDARG;
7267 ULONG64 dacTableAddress = m_globalBase + DAC_TABLE_RVA;
7268 if (FAILED(ReadFromDataTarget(m_pTarget, dacTableAddress, (BYTE*)&g_dacGlobals, sizeof(g_dacGlobals))))
7270 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7272 if (g_dacGlobals.ThreadStore__s_pThreadStore == NULL)
7274 return CORDBG_E_UNSUPPORTED;
7278 HRESULT status = E_FAIL;
7280 LPVOID rsrcData = NULL;
7283 DWORD resourceSectionRVA = 0;
7285 if (FAILED(status = GetMachineAndResourceSectionRVA(m_pTarget, m_globalBase, NULL, &resourceSectionRVA)))
7287 _ASSERTE_MSG(false, "DAC fatal error: can't locate resource section in " MAIN_CLR_DLL_NAME_A);
7288 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7291 if (FAILED(status = GetResourceRvaFromResourceSectionRvaByName(m_pTarget, m_globalBase,
7292 resourceSectionRVA, (DWORD)RT_RCDATA, _WIDE(DACCESS_TABLE_RESOURCE), 0,
7293 &rsrcRVA, &rsrcSize)))
7295 _ASSERTE_MSG(false, "DAC fatal error: can't locate DAC table resource in " MAIN_CLR_DLL_NAME_A);
7296 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7299 rsrcData = new (nothrow) BYTE[rsrcSize];
7300 if (rsrcData == NULL)
7301 return E_OUTOFMEMORY;
7303 if (FAILED(status = ReadFromDataTarget(m_pTarget, m_globalBase + rsrcRVA, (BYTE*)rsrcData, rsrcSize)))
7305 _ASSERTE_MSG(false, "DAC fatal error: can't load DAC table resource from " MAIN_CLR_DLL_NAME_A);
7306 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7310 PBYTE rawData = (PBYTE)rsrcData;
7311 DWORD bytesLeft = rsrcSize;
7314 struct DacTableHeader header;
7316 // We currently expect the header to be 2 32-bit values and 1 16-byte value,
7317 // make sure there is no packing going on or anything.
7318 static_assert_no_msg(sizeof(DacTableHeader) == 2 * 4 + 16);
7320 if (bytesLeft < sizeof(DacTableHeader))
7322 _ASSERTE_MSG(false, "DAC fatal error: DAC table too small for header.");
7325 memcpy(&header, rawData, sizeof(DacTableHeader));
7326 rawData += sizeof(DacTableHeader);
7327 bytesLeft -= sizeof(DacTableHeader);
7329 // Save the table info for later use
7330 g_dacTableInfo = header.info;
7332 // Sanity check that the DAC table is the size we expect.
7333 // This could fail if a different version of dacvars.h or vptr_list.h was used when building
7334 // mscordacwks.dll than when running DacTableGen.
7336 if (offsetof(DacGlobals, Thread__vtAddr) != header.numGlobals * sizeof(ULONG))
7339 char szMsgBuf[1024];
7340 _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE,
7341 "DAC fatal error: mismatch in number of globals in DAC table. Read from file: %d, expected: %d.",
7343 offsetof(DacGlobals, Thread__vtAddr) / sizeof(ULONG));
7344 _ASSERTE_MSG(false, szMsgBuf);
7347 status = E_INVALIDARG;
7351 if (sizeof(DacGlobals) != (header.numGlobals + header.numVptrs) * sizeof(ULONG))
7354 char szMsgBuf[1024];
7355 _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE,
7356 "DAC fatal error: mismatch in number of vptrs in DAC table. Read from file: %d, expected: %d.",
7358 (sizeof(DacGlobals) - offsetof(DacGlobals, Thread__vtAddr)) / sizeof(ULONG));
7359 _ASSERTE_MSG(false, szMsgBuf);
7362 status = E_INVALIDARG;
7366 // Copy the DAC table into g_dacGlobals
7367 if (bytesLeft < sizeof(DacGlobals))
7369 _ASSERTE_MSG(false, "DAC fatal error: DAC table resource too small for DacGlobals.");
7370 status = E_UNEXPECTED;
7373 memcpy(&g_dacGlobals, rawData, sizeof(DacGlobals));
7374 rawData += sizeof(DacGlobals);
7375 bytesLeft -= sizeof(DacGlobals);
7385 #undef MAKEINTRESOURCE
7387 //----------------------------------------------------------------------------
7389 // IsExceptionFromManagedCode - report if pExceptionRecord points to an exception belonging to the current runtime
7392 // pExceptionRecord - the exception record
7398 //----------------------------------------------------------------------------
7399 BOOL ClrDataAccess::IsExceptionFromManagedCode(EXCEPTION_RECORD* pExceptionRecord)
7405 if (::IsExceptionFromManagedCode(pExceptionRecord))
7417 //----------------------------------------------------------------------------
7419 // GetWatsonBuckets - retrieve Watson buckets from the specified thread
7422 // dwThreadId - the thread ID
7423 // pGM - pointer to the space to store retrieved Watson buckets
7426 // S_OK if the operation is successful.
7427 // or S_FALSE if Watson buckets cannot be found
7428 // else detailed error code.
7430 //----------------------------------------------------------------------------
7431 HRESULT ClrDataAccess::GetWatsonBuckets(DWORD dwThreadId, GenericModeBlock * pGM)
7433 _ASSERTE((dwThreadId != 0) && (pGM != NULL));
7434 if ((dwThreadId == 0) || (pGM == NULL))
7436 return E_INVALIDARG;
7441 Thread * pThread = DacGetThread(dwThreadId);
7442 _ASSERTE(pThread != NULL);
7444 HRESULT hr = E_UNEXPECTED;
7446 if (pThread != NULL)
7448 hr = GetClrWatsonBucketsWorker(pThread, pGM);
7455 #endif // FEATURE_PAL
7457 //----------------------------------------------------------------------------
7459 // CLRDataAccessCreateInstance - create and initialize a ClrDataAccess object
7462 // pLegacyTarget - data target object
7463 // pClrDataAccess - ClrDataAccess object
7466 // S_OK on success, else detailed error code.
7468 //----------------------------------------------------------------------------
7469 STDAPI CLRDataAccessCreateInstance(ICLRDataTarget * pLegacyTarget,
7470 ClrDataAccess ** pClrDataAccess)
7472 if ((pLegacyTarget == NULL) || (pClrDataAccess == NULL))
7474 return E_INVALIDARG;
7477 *pClrDataAccess = NULL;
7479 // Create an adapter which implements the new ICorDebugDataTarget interfaces using
7480 // a legacy implementation of ICLRDataTarget
7481 // ClrDataAccess will take a take a ref on this and delete it when it's released.
7482 DataTargetAdapter * pDtAdapter = new (nothrow) DataTargetAdapter(pLegacyTarget);
7485 return E_OUTOFMEMORY;
7488 ClrDataAccess* dacClass = new (nothrow) ClrDataAccess(pDtAdapter, pLegacyTarget);
7492 return E_OUTOFMEMORY;
7495 HRESULT hr = dacClass->Initialize();
7498 dacClass->Release();
7502 *pClrDataAccess = dacClass;
7507 //----------------------------------------------------------------------------
7509 // CLRDataCreateInstance.
7510 // Creates the IXClrData object
7511 // This is the legacy entrypoint to DAC, used by dbgeng/dbghelp (windbg, SOS, watson, etc).
7513 //----------------------------------------------------------------------------
7516 CLRDataCreateInstance(REFIID iid,
7517 ICLRDataTarget * pLegacyTarget,
7520 if ((pLegacyTarget == NULL) || (iface == NULL))
7522 return E_INVALIDARG;
7526 ClrDataAccess * pClrDataAccess;
7527 HRESULT hr = CLRDataAccessCreateInstance(pLegacyTarget, &pClrDataAccess);
7533 hr = pClrDataAccess->QueryInterface(iid, iface);
7535 pClrDataAccess->Release();
7540 //----------------------------------------------------------------------------
7542 // OutOfProcessExceptionEventGetProcessIdAndThreadId - get ProcessID and ThreadID
7545 // hProcess - process handle
7546 // hThread - thread handle
7547 // pPId - pointer to DWORD to store ProcessID
7548 // pThreadId - pointer to DWORD to store ThreadID
7551 // TRUE if the operation is successful.
7552 // FALSE if it fails
7554 //----------------------------------------------------------------------------
7555 BOOL OutOfProcessExceptionEventGetProcessIdAndThreadId(HANDLE hProcess, HANDLE hThread, DWORD * pPId, DWORD * pThreadId)
7557 _ASSERTE((pPId != NULL) && (pThreadId != NULL));
7560 // UNIXTODO: mikem 1/13/15 Need appropriate PAL functions for getting ids
7561 *pPId = (DWORD)(SIZE_T)hProcess;
7562 *pThreadId = (DWORD)(SIZE_T)hThread;
7564 #if !defined(FEATURE_CORESYSTEM)
7565 HMODULE hKernel32 = WszGetModuleHandle(W("kernel32.dll"));
7567 HMODULE hKernel32 = WszGetModuleHandle(W("api-ms-win-core-processthreads-l1-1-1.dll"));
7569 if (hKernel32 == NULL)
7574 typedef WINBASEAPI DWORD (WINAPI GET_PROCESSID_OF_THREAD)(HANDLE);
7575 GET_PROCESSID_OF_THREAD * pGetProcessIdOfThread;
7577 typedef WINBASEAPI DWORD (WINAPI GET_THREADID)(HANDLE);
7578 GET_THREADID * pGetThreadId;
7580 pGetProcessIdOfThread = (GET_PROCESSID_OF_THREAD *)GetProcAddress(hKernel32, "GetProcessIdOfThread");
7581 pGetThreadId = (GET_THREADID *)GetProcAddress(hKernel32, "GetThreadId");
7583 // OOP callbacks are used on Win7 or later. We should have having below two APIs available.
7584 _ASSERTE((pGetProcessIdOfThread != NULL) && (pGetThreadId != NULL));
7585 if ((pGetProcessIdOfThread == NULL) || (pGetThreadId == NULL))
7590 *pPId = (*pGetProcessIdOfThread)(hThread);
7591 *pThreadId = (*pGetThreadId)(hThread);
7592 #endif // FEATURE_PAL
7596 // WER_RUNTIME_EXCEPTION_INFORMATION will be available from Win7 SDK once Win7 SDK is released.
7597 #if !defined(WER_RUNTIME_EXCEPTION_INFORMATION)
7598 typedef struct _WER_RUNTIME_EXCEPTION_INFORMATION
7603 EXCEPTION_RECORD exceptionRecord;
7605 } WER_RUNTIME_EXCEPTION_INFORMATION, * PWER_RUNTIME_EXCEPTION_INFORMATION;
7606 #endif // !defined(WER_RUNTIME_EXCEPTION_INFORMATION)
7611 //----------------------------------------------------------------------------
7613 // OutOfProcessExceptionEventGetWatsonBucket - retrieve Watson buckets if it is a managed exception
7616 // pContext - the context passed at helper module registration
7617 // pExceptionInformation - structure that contains information about the crash
7618 // pGM - pointer to the space to store retrieved Watson buckets
7621 // S_OK if the operation is successful.
7622 // or S_FALSE if it is not a managed exception or Watson buckets cannot be found
7623 // else detailed error code.
7625 //----------------------------------------------------------------------------
7626 STDAPI OutOfProcessExceptionEventGetWatsonBucket(__in PDWORD pContext,
7627 __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,
7628 __out GenericModeBlock * pGMB)
7630 HANDLE hProcess = pExceptionInformation->hProcess;
7631 HANDLE hThread = pExceptionInformation->hThread;
7632 DWORD PId, ThreadId;
7634 if (!OutOfProcessExceptionEventGetProcessIdAndThreadId(hProcess, hThread, &PId, &ThreadId))
7639 CLRDATA_ADDRESS baseAddressOfRuntime = (CLRDATA_ADDRESS)pContext;
7640 NewHolder<LiveProcDataTarget> dataTarget(NULL);
7642 dataTarget = new (nothrow) LiveProcDataTarget(hProcess, PId, baseAddressOfRuntime);
7643 if (dataTarget == NULL)
7645 return E_OUTOFMEMORY;
7648 NewHolder<ClrDataAccess> pClrDataAccess(NULL);
7650 HRESULT hr = CLRDataAccessCreateInstance(dataTarget, &pClrDataAccess);
7663 if (!pClrDataAccess->IsExceptionFromManagedCode(&pExceptionInformation->exceptionRecord))
7668 return pClrDataAccess->GetWatsonBuckets(ThreadId, pGMB);
7671 //----------------------------------------------------------------------------
7673 // OutOfProcessExceptionEventCallback - claim the ownership of this event if current
7674 // runtime threw the unhandled exception
7677 // pContext - the context passed at helper module registration
7678 // pExceptionInformation - structure that contains information about the crash
7679 // pbOwnershipClaimed - output parameter for claiming the ownership of this event
7680 // pwszEventName - name of the event. If this is NULL, pchSize cannot be NULL.
7681 // This parameter is valid only if * pbOwnershipClaimed is TRUE.
7682 // pchSize - the size of the buffer pointed by pwszEventName
7683 // pdwSignatureCount - the count of signature parameters. Valid values range from
7684 // 0 to 10. If the value returned is greater than 10, only the
7685 // 1st 10 parameters are used for bucketing parameters. This
7686 // parameter is valid only if * pbOwnershipClaimed is TRUE.
7689 // S_OK on success, else detailed error code.
7692 // This is the 1st function that is called into by WER. This API through its out
7693 // parameters, tells WER as to whether or not it is claiming the crash. If it does
7694 // claim the crash, WER uses the event name specified in the string pointed to by
7695 // pwszEventName for error reporting. WER then proceed to call the
7696 // OutOfProcessExceptionEventSignatureCallback to get the bucketing parameters from
7699 // This function follows the multiple call paradigms. WER may call into this function
7700 // with *pwszEventName pointer set to NULL. This is to indicate to the function, that
7701 // WER wants to know the buffer size needed by the function to populate the string
7702 // into the buffer. The function should return E_INSUFFICIENTBUFFER with the needed
7703 // buffer size in *pchSize. WER shall then allocate a buffer of size *pchSize for
7704 // pwszEventName and then call this function again at which point the function should
7705 // populate the string and return S_OK.
7707 // Note that *pdOwnershipClaimed should be set to TRUE everytime this function is called
7708 // for the helper dll to claim ownership of bucketing.
7710 // The Win7 WER spec is at
7711 // http://windows/windows7/docs/COSD%20Documents/Fundamentals/Feedback%20Services%20and%20Platforms/WER-CLR%20Integration%20Dev%20Spec.docx
7714 // Since this is called by external modules it's important that we don't let any exceptions leak out (see Win8 95224).
7716 //----------------------------------------------------------------------------
7717 STDAPI OutOfProcessExceptionEventCallback(__in PDWORD pContext,
7718 __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,
7719 __out BOOL * pbOwnershipClaimed,
7720 __out_ecount(*pchSize) PWSTR pwszEventName,
7721 __inout PDWORD pchSize,
7722 __out PDWORD pdwSignatureCount)
7724 SUPPORTS_DAC_HOST_ONLY;
7726 if ((pContext == NULL) ||
7727 (pExceptionInformation == NULL) ||
7728 (pExceptionInformation->dwSize < sizeof(WER_RUNTIME_EXCEPTION_INFORMATION)) ||
7729 (pbOwnershipClaimed == NULL) ||
7730 (pchSize == NULL) ||
7731 (pdwSignatureCount == NULL))
7733 return E_INVALIDARG;
7736 *pbOwnershipClaimed = FALSE;
7738 GenericModeBlock gmb;
7739 HRESULT hr = E_FAIL;
7743 // get Watson buckets if it is a managed exception
7744 hr = OutOfProcessExceptionEventGetWatsonBucket(pContext, pExceptionInformation, &gmb);
7746 EX_CATCH_HRESULT(hr);
7750 // S_FALSE means either it is not a managed exception or we do not have Watson buckets.
7751 // Since we have set pbOwnershipClaimed to FALSE, we return S_OK to WER.
7760 if ((pwszEventName == NULL) || (*pchSize <= wcslen(gmb.wzEventTypeName)))
7762 *pchSize = static_cast<DWORD>(wcslen(gmb.wzEventTypeName)) + 1;
7763 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
7766 // copy custom event name
7767 wcscpy_s(pwszEventName, *pchSize, gmb.wzEventTypeName);
7768 *pdwSignatureCount = GetCountBucketParamsForEvent(gmb.wzEventTypeName);
7769 *pbOwnershipClaimed = TRUE;
7775 //----------------------------------------------------------------------------
7777 // OutOfProcessExceptionEventCallback - provide custom Watson buckets
7780 // pContext - the context passed at helper module registration
7781 // pExceptionInformation - structure that contains information about the crash
7782 // dwIndex - the index of the bucketing parameter being requested. Valid values are
7784 // pwszName - pointer to the name of the bucketing parameter
7785 // pchName - pointer to character count of the pwszName buffer. If pwszName points to
7786 // null, *pchName represents the buffer size (represented in number of characters)
7787 // needed to populate the name in pwszName.
7788 // pwszValue - pointer to the value of the pwszName bucketing parameter
7789 // pchValue - pointer to the character count of the pwszValue buffer. If pwszValue points
7790 // to null, *pchValue represents the buffer size (represented in number of
7791 // characters) needed to populate the value in pwszValue.
7794 // S_OK on success, else detailed error code.
7797 // This function is called by WER only if the call to OutOfProcessExceptionEventCallback()
7798 // was successful and the value of *pbOwnershipClaimed was TRUE. This function is called
7799 // pdwSignatureCount times to collect the bucketing parameters from the helper dll.
7801 // This function also follows the multiple call paradigm as described for the
7802 // OutOfProcessExceptionEventCallback() function. The buffer sizes needed for
7803 // this function are of the pwszName and pwszValue buffers.
7806 // Since this is called by external modules it's important that we don't let any exceptions leak out (see Win8 95224).
7808 //----------------------------------------------------------------------------
7809 STDAPI OutOfProcessExceptionEventSignatureCallback(__in PDWORD pContext,
7810 __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,
7812 __out_ecount(*pchName) PWSTR pwszName,
7813 __inout PDWORD pchName,
7814 __out_ecount(*pchValue) PWSTR pwszValue,
7815 __inout PDWORD pchValue)
7817 SUPPORTS_DAC_HOST_ONLY;
7819 if ((pContext == NULL) ||
7820 (pExceptionInformation == NULL) ||
7821 (pExceptionInformation->dwSize < sizeof(WER_RUNTIME_EXCEPTION_INFORMATION)) ||
7822 (pchName == NULL) ||
7825 return E_INVALIDARG;
7828 if ((pwszName == NULL) || (*pchName == 0))
7831 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
7834 GenericModeBlock gmb;
7835 const PWSTR pwszBucketValues[] = {gmb.wzP1,
7846 HRESULT hr = E_FAIL;
7850 // get Watson buckets if it is a managed exception
7851 hr = OutOfProcessExceptionEventGetWatsonBucket(pContext, pExceptionInformation, &gmb);
7853 EX_CATCH_HRESULT(hr);
7855 // it's possible for the OS to kill
7856 // the faulting process before WER crash reporting has completed.
7857 _ASSERTE(hr == S_OK || hr == CORDBG_E_READVIRTUAL_FAILURE);
7861 // S_FALSE means either it is not a managed exception or we do not have Watson buckets.
7862 // Either case is a logic error becuase this function is called by WER only if the call
7863 // to OutOfProcessExceptionEventCallback() was successful and the value of
7864 // *pbOwnershipClaimed was TRUE.
7873 DWORD paramCount = GetCountBucketParamsForEvent(gmb.wzEventTypeName);
7875 if (dwIndex >= paramCount)
7877 _ASSERTE(!"dwIndex is out of range");
7878 return E_INVALIDARG;
7881 // Return pwszName as an emptry string to let WER use localized version of "Parameter n"
7882 *pwszName = W('\0');
7884 if ((pwszValue == NULL) || (*pchValue <= wcslen(pwszBucketValues[dwIndex])))
7886 *pchValue = static_cast<DWORD>(wcslen(pwszBucketValues[dwIndex]))+ 1;
7887 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
7890 // copy custom Watson bucket value
7891 wcscpy_s(pwszValue, *pchValue, pwszBucketValues[dwIndex]);
7896 #endif // FEATURE_PAL
7898 //----------------------------------------------------------------------------
7900 // OutOfProcessExceptionEventCallback - provide custom debugger launch string
7903 // pContext - the context passed at helper module registration
7904 // pExceptionInformation - structure that contains information about the crash
7905 // pbCustomDebuggerNeeded - pointer to a BOOL. If this BOOL is set to TRUE, then
7906 // a custom debugger launch option is needed by the
7907 // process. In that case, the subsequent parameters will
7908 // be meaningfully used. If this is FALSE, the subsequent
7909 // parameters will be ignored.
7910 // pwszDebuggerLaunch - pointer to a string that will be used to launch the debugger,
7911 // if the debugger is launched. The value of this string overrides
7912 // the default debugger launch string used by WER.
7913 // pchSize - pointer to the character count of the pwszDebuggerLaunch buffer. If
7914 // pwszDebuggerLaunch points to null, *pchSize represents the buffer size
7915 // (represented in number of characters) needed to populate the debugger
7916 // launch string in pwszDebuggerLaunch.
7917 // pbAutoLaunchDebugger - pointer to a BOOL. If this BOOL is set to TRUE, WER will
7918 // directly launch the debugger. If set to FALSE, WER will show
7919 // the debug option to the user in the WER UI.
7922 // S_OK on success, else detailed error code.
7925 // This function is called into by WER only if the call to OutOfProcessExceptionEventCallback()
7926 // was successful and the value of *pbOwnershipClaimed was TRUE. This function allows the helper
7927 // dll to customize the debugger launch options including the launch string.
7929 // This function also follows the multiple call paradigm as described for the
7930 // OutOfProcessExceptionEventCallback() function. The buffer sizes needed for
7931 // this function are of the pwszName and pwszValue buffers.
7933 //----------------------------------------------------------------------------
7934 STDAPI OutOfProcessExceptionEventDebuggerLaunchCallback(__in PDWORD pContext,
7935 __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,
7936 __out BOOL * pbCustomDebuggerNeeded,
7937 __out_ecount_opt(*pchSize) PWSTR pwszDebuggerLaunch,
7938 __inout PDWORD pchSize,
7939 __out BOOL * pbAutoLaunchDebugger)
7941 SUPPORTS_DAC_HOST_ONLY;
7943 if ((pContext == NULL) ||
7944 (pExceptionInformation == NULL) ||
7945 (pExceptionInformation->dwSize < sizeof(WER_RUNTIME_EXCEPTION_INFORMATION)) ||
7946 (pbCustomDebuggerNeeded == NULL) ||
7947 (pwszDebuggerLaunch == NULL) ||
7948 (pchSize == NULL) ||
7949 (pbAutoLaunchDebugger == NULL))
7951 return E_INVALIDARG;
7954 // Starting from CLRv4 managed debugger string and setting are unified with native debuggers.
7955 // There is no need to provide custom debugger string for WER.
7956 *pbCustomDebuggerNeeded = FALSE;
7963 #include "comcallablewrapper.h"
7965 DacHandleWalker::DacHandleWalker()
7966 : mDac(0), m_instanceAge(0), mMap(0), mIndex(0),
7967 mTypeMask(0), mGenerationFilter(-1), mChunkIndex(0), mCurr(0),
7973 DacHandleWalker::~DacHandleWalker()
7977 HandleChunkHead *curr = mHead.Next;
7981 HandleChunkHead *tmp = curr;
7987 HRESULT DacHandleWalker::Init(ClrDataAccess *dac, UINT types[], UINT typeCount)
7991 if (dac == NULL || types == NULL)
7995 m_instanceAge = dac->m_instanceAge;
7997 return Init(BuildTypemask(types, typeCount));
8000 HRESULT DacHandleWalker::Init(ClrDataAccess *dac, UINT types[], UINT typeCount, int gen)
8004 if (gen < 0 || gen > (int)*g_gcDacGlobals->max_gen)
8005 return E_INVALIDARG;
8007 mGenerationFilter = gen;
8009 return Init(dac, types, typeCount);
8012 HRESULT DacHandleWalker::Init(UINT32 typemask)
8016 mMap = g_gcDacGlobals->handle_table_map;
8017 mTypeMask = typemask;
8022 UINT32 DacHandleWalker::BuildTypemask(UINT types[], UINT typeCount)
8028 for (UINT i = 0; i < typeCount; ++i)
8030 _ASSERTE(types[i] < 32);
8031 mask |= (1 << types[i]);
8037 HRESULT DacHandleWalker::Next(unsigned int celt,
8038 SOSHandleData handles[],
8039 unsigned int *pceltFetched)
8043 if (handles == NULL || pceltFetched == NULL)
8048 hr = DoHandleWalk<SOSHandleData, unsigned int, DacHandleWalker::EnumCallbackSOS>(celt, handles, pceltFetched);
8055 bool DacHandleWalker::FetchMoreHandles(HANDLESCANPROC callback)
8059 // The table slots are based on the number of GC heaps in the process.
8062 #ifdef FEATURE_SVR_GC
8063 if (GCHeapUtilities::IsServerHeap())
8064 max_slots = GCHeapCount();
8065 #endif // FEATURE_SVR_GC
8067 // Reset the Count on all cached chunks. We reuse chunks after allocating
8068 // them, and the count is the only thing which needs resetting.
8069 for (HandleChunkHead *curr = &mHead; curr; curr = curr->Next)
8072 DacHandleWalkerParam param(&mHead);
8076 // Have we advanced past the end of the current bucket?
8077 if (mMap && mIndex >= INITIAL_HANDLE_TABLE_ARRAY_SIZE)
8083 // Have we walked the entire handle table map?
8090 if (mMap->pBuckets[mIndex] != NULL)
8092 for (int i = 0; i < max_slots; ++i)
8094 DPTR(dac_handle_table) hTable = mMap->pBuckets[mIndex]->pTable[i];
8097 // Yikes! The handle table callbacks don't produce the handle type or
8098 // the AppDomain that we need, and it's too difficult to propogate out
8099 // these things (especially the type) without worrying about performance
8100 // implications for the GC. Instead we'll have the callback walk each
8101 // type individually. There are only a few handle types, and the handle
8102 // table has a fast-path for only walking a single type anyway.
8103 UINT32 handleType = 0;
8104 for (UINT32 mask = mTypeMask; mask; mask >>= 1, handleType++)
8108 dac_handle_table *pTable = hTable;
8109 PTR_AppDomain pDomain = AppDomain::GetCurrentDomain();
8110 param.AppDomain = TO_CDADDR(pDomain.GetAddr());
8111 param.Type = handleType;
8113 // Either enumerate the handles regularly, or walk the handle
8114 // table as the GC does if a generation filter was requested.
8115 if (mGenerationFilter != -1)
8116 HndScanHandlesForGC(hTable, callback,
8119 mGenerationFilter, *g_gcDacGlobals->max_gen, 0);
8121 HndEnumHandles(hTable, &handleType, 1, callback, (LPARAM)¶m, 0, FALSE);
8128 // Stop looping as soon as we have found data. We also stop if we have a failed HRESULT during
8129 // the callback (this should indicate OOM).
8131 } while (mHead.Count == 0 && SUCCEEDED(param.Result));
8138 HRESULT DacHandleWalker::Skip(unsigned int celt)
8143 HRESULT DacHandleWalker::Reset()
8148 HRESULT DacHandleWalker::GetCount(unsigned int *pcelt)
8154 void DacHandleWalker::GetRefCountedHandleInfo(
8155 OBJECTREF oref, unsigned int uType,
8156 unsigned int *pRefCount, unsigned int *pJupiterRefCount, BOOL *pIsPegged, BOOL *pIsStrong)
8160 #ifdef FEATURE_COMINTEROP
8161 if (uType == HNDTYPE_REFCOUNTED)
8163 // get refcount from the CCW
8164 PTR_ComCallWrapper pWrap = ComCallWrapper::GetWrapperForObject(oref);
8168 *pRefCount = (unsigned int)pWrap->GetRefCount();
8170 if (pJupiterRefCount)
8171 *pJupiterRefCount = (unsigned int)pWrap->GetJupiterRefCount();
8174 *pIsPegged = pWrap->IsConsideredPegged();
8177 *pIsStrong = pWrap->IsWrapperActive();
8182 #endif // FEATURE_COMINTEROP
8187 if (pJupiterRefCount)
8188 *pJupiterRefCount = 0;
8197 void CALLBACK DacHandleWalker::EnumCallbackSOS(PTR_UNCHECKED_OBJECTREF handle, uintptr_t *pExtraInfo, uintptr_t param1, uintptr_t param2)
8201 DacHandleWalkerParam *param = (DacHandleWalkerParam *)param1;
8202 HandleChunkHead *curr = param->Curr;
8204 // If we failed on a previous call (OOM) don't keep trying to allocate, it's not going to work.
8205 if (FAILED(param->Result))
8208 // We've moved past the size of the current chunk. We'll allocate a new chunk
8209 // and stuff the handles there. These are cleaned up by the destructor
8210 if (curr->Count >= (curr->Size/sizeof(SOSHandleData)))
8212 if (curr->Next == NULL)
8214 HandleChunk *next = new (nothrow) HandleChunk;
8221 param->Result = E_OUTOFMEMORY;
8226 curr = param->Curr = param->Curr->Next;
8229 // Fill the current handle.
8230 SOSHandleData *dataArray = (SOSHandleData*)curr->pData;
8231 SOSHandleData &data = dataArray[curr->Count++];
8233 data.Handle = TO_CDADDR(handle.GetAddr());
8234 data.Type = param->Type;
8235 if (param->Type == HNDTYPE_DEPENDENT)
8236 data.Secondary = GetDependentHandleSecondary(handle.GetAddr()).GetAddr();
8237 #ifdef FEATURE_COMINTEROP
8238 else if (param->Type == HNDTYPE_WEAK_WINRT)
8239 data.Secondary = HndGetHandleExtraInfo(handle.GetAddr());
8240 #endif // FEATURE_COMINTEROP
8243 data.AppDomain = param->AppDomain;
8244 GetRefCountedHandleInfo((OBJECTREF)*handle, param->Type, &data.RefCount, &data.JupiterRefCount, &data.IsPegged, &data.StrongReference);
8245 data.StrongReference |= (BOOL)IsAlwaysStrongReference(param->Type);
8248 DacStackReferenceWalker::DacStackReferenceWalker(ClrDataAccess *dac, DWORD osThreadID)
8249 : mDac(dac), m_instanceAge(dac ? dac->m_instanceAge : 0), mThread(0), mErrors(0), mEnumerated(false),
8250 mChunkIndex(0), mCurr(0), mIteratorIndex(0)
8252 Thread *curr = NULL;
8254 for (curr = ThreadStore::GetThreadList(curr);
8256 curr = ThreadStore::GetThreadList(curr))
8258 if (curr->GetOSThreadId() == osThreadID)
8266 DacStackReferenceWalker::~DacStackReferenceWalker()
8268 StackRefChunkHead *curr = mHead.next;
8272 StackRefChunkHead *tmp = curr;
8278 HRESULT DacStackReferenceWalker::Init()
8281 return E_INVALIDARG;
8282 return mHeap.Init();
8285 HRESULT STDMETHODCALLTYPE DacStackReferenceWalker::Skip(unsigned int count)
8290 HRESULT STDMETHODCALLTYPE DacStackReferenceWalker::Reset()
8295 HRESULT DacStackReferenceWalker::GetCount(unsigned int *pCount)
8304 // Fill out our data structures.
8305 WalkStack<unsigned int, SOSStackRefData>(0, NULL, DacStackReferenceWalker::GCReportCallbackSOS, DacStackReferenceWalker::GCEnumCallbackSOS);
8308 unsigned int count = 0;
8309 for(StackRefChunkHead *curr = &mHead; curr; curr = curr->next)
8310 count += curr->count;
8318 HRESULT DacStackReferenceWalker::Next(unsigned int count,
8319 SOSStackRefData stackRefs[],
8320 unsigned int *pFetched)
8322 if (stackRefs == NULL || pFetched == NULL)
8327 hr = DoStackWalk<unsigned int, SOSStackRefData,
8328 DacStackReferenceWalker::GCReportCallbackSOS,
8329 DacStackReferenceWalker::GCEnumCallbackSOS>
8330 (count, stackRefs, pFetched);
8337 HRESULT DacStackReferenceWalker::EnumerateErrors(ISOSStackRefErrorEnum **ppEnum)
8346 // Fill out our data structures.
8347 WalkStack<unsigned int, SOSStackRefData>(0, NULL, DacStackReferenceWalker::GCReportCallbackSOS, DacStackReferenceWalker::GCEnumCallbackSOS);
8350 DacStackReferenceErrorEnum *pEnum = new DacStackReferenceErrorEnum(this, mErrors);
8351 hr = pEnum->QueryInterface(__uuidof(ISOSStackRefErrorEnum), (void**)ppEnum);
8357 CLRDATA_ADDRESS DacStackReferenceWalker::ReadPointer(TADDR addr)
8359 ULONG32 bytesRead = 0;
8361 HRESULT hr = mDac->m_pTarget->ReadVirtual(addr, (BYTE*)&result, sizeof(TADDR), &bytesRead);
8363 if (FAILED(hr) || (bytesRead != sizeof(TADDR)))
8364 return (CLRDATA_ADDRESS)~0;
8366 return TO_CDADDR(result);
8370 void DacStackReferenceWalker::GCEnumCallbackSOS(LPVOID hCallback, OBJECTREF *pObject, uint32_t flags, DacSlotLocation loc)
8372 GCCONTEXT *gcctx = (GCCONTEXT *)hCallback;
8373 DacScanContext *dsc = (DacScanContext*)gcctx->sc;
8375 // Yuck. The GcInfoDecoder reports a local pointer for registers (as it's reading out of the REGDISPLAY
8376 // in the stack walk), and it reports a TADDR for stack locations. This is architecturally difficulty
8377 // to fix, so we are leaving it for now.
8383 addr = (TADDR)pObject;
8384 obj = TO_TADDR(dsc->pWalker->ReadPointer((CORDB_ADDRESS)addr));
8388 obj = pObject->GetAddr();
8391 if (flags & GC_CALL_INTERIOR)
8393 CORDB_ADDRESS fixed_obj = 0;
8394 HRESULT hr = dsc->pWalker->mHeap.ListNearObjects((CORDB_ADDRESS)obj, NULL, &fixed_obj, NULL);
8396 // If we failed...oh well, SOS won't mind. We'll just report the interior pointer as is.
8398 obj = TO_TADDR(fixed_obj);
8401 SOSStackRefData *data = dsc->pWalker->GetNextObject<SOSStackRefData>(dsc);
8404 // Report where the object and where it was found.
8405 data->HasRegisterInformation = true;
8406 data->Register = loc.reg;
8407 data->Offset = loc.regOffset;
8408 data->Address = TO_CDADDR(addr);
8409 data->Object = TO_CDADDR(obj);
8410 data->Flags = flags;
8412 // Report the frame that the data came from.
8413 data->StackPointer = TO_CDADDR(dsc->sp);
8417 data->SourceType = SOS_StackSourceFrame;
8418 data->Source = dac_cast<PTR_Frame>(dsc->pFrame).GetAddr();
8422 data->SourceType = SOS_StackSourceIP;
8423 data->Source = TO_CDADDR(dsc->pc);
8429 void DacStackReferenceWalker::GCReportCallbackSOS(PTR_PTR_Object ppObj, ScanContext *sc, uint32_t flags)
8431 DacScanContext *dsc = (DacScanContext*)sc;
8432 CLRDATA_ADDRESS obj = dsc->pWalker->ReadPointer(ppObj.GetAddr());
8434 if (flags & GC_CALL_INTERIOR)
8436 CORDB_ADDRESS fixed_addr = 0;
8437 HRESULT hr = dsc->pWalker->mHeap.ListNearObjects((CORDB_ADDRESS)obj, NULL, &fixed_addr, NULL);
8439 // If we failed...oh well, SOS won't mind. We'll just report the interior pointer as is.
8441 obj = TO_CDADDR(fixed_addr);
8444 SOSStackRefData *data = dsc->pWalker->GetNextObject<SOSStackRefData>(dsc);
8447 data->HasRegisterInformation = false;
8450 data->Address = ppObj.GetAddr();
8452 data->Flags = flags;
8453 data->StackPointer = TO_CDADDR(dsc->sp);
8457 data->SourceType = SOS_StackSourceFrame;
8458 data->Source = dac_cast<PTR_Frame>(dsc->pFrame).GetAddr();
8462 data->SourceType = SOS_StackSourceIP;
8463 data->Source = TO_CDADDR(dsc->pc);
8468 StackWalkAction DacStackReferenceWalker::Callback(CrawlFrame *pCF, VOID *pData)
8471 // KEEP IN SYNC WITH GcStackCrawlCallBack in vm\gcscan.cpp
8474 GCCONTEXT *gcctx = (GCCONTEXT*)pData;
8475 DacScanContext *dsc = (DacScanContext*)gcctx->sc;
8477 MethodDesc *pMD = pCF->GetFunction();
8478 gcctx->sc->pMD = pMD;
8480 PREGDISPLAY pRD = pCF->GetRegisterSet();
8481 dsc->sp = (TADDR)GetRegdisplaySP(pRD);;
8482 dsc->pc = PCODEToPINSTR(GetControlPC(pRD));
8484 ResetPointerHolder<CrawlFrame*> rph(&gcctx->cf);
8487 bool fReportGCReferences = true;
8488 #if defined(WIN64EXCEPTIONS)
8489 // On Win64 and ARM, we may have unwound this crawlFrame and thus, shouldn't report the invalid
8490 // references it may contain.
8492 fReportGCReferences = pCF->ShouldCrawlframeReportGCReferences();
8493 #endif // defined(WIN64EXCEPTIONS)
8495 Frame *pFrame = ((DacScanContext*)gcctx->sc)->pFrame = pCF->GetFrame();
8499 if (fReportGCReferences)
8501 if (pCF->IsFrameless())
8503 ICodeManager * pCM = pCF->GetCodeManager();
8504 _ASSERTE(pCM != NULL);
8506 unsigned flags = pCF->GetCodeManagerFlags();
8508 pCM->EnumGcRefs(pCF->GetRegisterSet(),
8516 pFrame->GcScanRoots(gcctx->f, gcctx->sc);
8522 SOSStackErrorList *err = new SOSStackErrorList;
8527 err->error.SourceType = SOS_StackSourceFrame;
8528 err->error.Source = dac_cast<PTR_Frame>(pFrame).GetAddr();
8532 err->error.SourceType = SOS_StackSourceIP;
8533 err->error.Source = TO_CDADDR(dsc->pc);
8536 if (dsc->pWalker->mErrors == NULL)
8538 dsc->pWalker->mErrors = err;
8542 // This exception case should be non-existent. It only happens when there is either
8543 // a clr!Frame on the callstack which is not properly dac-ized, or when a call down
8544 // EnumGcRefs causes a data read exception. Since this is so rare, we don't worry
8545 // about making this code very efficient.
8546 SOSStackErrorList *curr = dsc->pWalker->mErrors;
8553 EX_END_CATCH(SwallowAllExceptions)
8558 // If we're executing a LCG dynamic method then we must promote the associated resolver to ensure it
8559 // doesn't get collected and yank the method code out from under us).
8561 // Be careful to only promote the reference -- we can also be called to relocate the reference and
8562 // that can lead to all sorts of problems since we could be racing for the relocation with the long
8563 // weak handle we recover the reference from. Promoting the reference is enough, the handle in the
8564 // reference will be relocated properly as long as we keep it alive till the end of the collection
8565 // as long as the reference is actually maintained by the long weak handle.
8568 BOOL fMaybeCollectibleMethod = TRUE;
8570 // If this is a frameless method then the jitmanager can answer the question of whether
8571 // or not this is LCG simply by looking at the heap where the code lives, however there
8572 // is also the prestub case where we need to explicitly look at the MD for stuff that isn't
8574 if (pCF->IsFrameless() && pMD->IsLCGMethod())
8576 fMaybeCollectibleMethod = ExecutionManager::IsCollectibleMethod(pCF->GetMethodToken());
8579 if (fMaybeCollectibleMethod && pMD->IsLCGMethod())
8581 PTR_Object obj = OBJECTREFToObject(pMD->AsDynamicMethodDesc()->GetLCGMethodResolver()->GetManagedResolver());
8582 dsc->pWalker->ReportObject(obj);
8586 if (fMaybeCollectibleMethod)
8588 PTR_Object obj = pMD->GetLoaderAllocator()->GetExposedObject();
8589 dsc->pWalker->ReportObject(obj);
8592 if (fReportGCReferences)
8594 GenericParamContextType paramContextType = GENERIC_PARAM_CONTEXT_NONE;
8596 if (pCF->IsFrameless())
8598 // We need to grab the Context Type here because there are cases where the MethodDesc
8599 // is shared, and thus indicates there should be an instantion argument, but the JIT
8600 // was still allowed to optimize it away and we won't grab it below because we're not
8601 // reporting any references from this frame.
8602 paramContextType = pCF->GetCodeManager()->GetParamContextType(pCF->GetRegisterSet(), pCF->GetCodeInfo());
8606 if (pMD->RequiresInstMethodDescArg())
8607 paramContextType = GENERIC_PARAM_CONTEXT_METHODDESC;
8608 else if (pMD->RequiresInstMethodTableArg())
8609 paramContextType = GENERIC_PARAM_CONTEXT_METHODTABLE;
8612 // Handle the case where the method is a static shared generic method and we need to keep the type of the generic parameters alive
8613 if (paramContextType == GENERIC_PARAM_CONTEXT_METHODDESC)
8615 MethodDesc *pMDReal = dac_cast<PTR_MethodDesc>(pCF->GetParamTypeArg());
8616 _ASSERTE((pMDReal != NULL) || !pCF->IsFrameless());
8617 if (pMDReal != NULL)
8619 PTR_Object obj = pMDReal->GetLoaderAllocator()->GetExposedObject();
8620 dsc->pWalker->ReportObject(obj);
8623 else if (paramContextType == GENERIC_PARAM_CONTEXT_METHODTABLE)
8625 MethodTable *pMTReal = dac_cast<PTR_MethodTable>(pCF->GetParamTypeArg());
8626 _ASSERTE((pMTReal != NULL) || !pCF->IsFrameless());
8627 if (pMTReal != NULL)
8629 PTR_Object obj = pMTReal->GetLoaderAllocator()->GetExposedObject();
8630 dsc->pWalker->ReportObject(obj);
8638 return SWA_CONTINUE;
8642 DacStackReferenceErrorEnum::DacStackReferenceErrorEnum(DacStackReferenceWalker *pEnum, SOSStackErrorList *pErrors)
8643 : mEnum(pEnum), mHead(pErrors), mCurr(pErrors)
8651 DacStackReferenceErrorEnum::~DacStackReferenceErrorEnum()
8657 HRESULT DacStackReferenceErrorEnum::Skip(unsigned int count)
8660 for (i = 0; i < count && mCurr; ++i)
8661 mCurr = mCurr->pNext;
8663 return i < count ? S_FALSE : S_OK;
8666 HRESULT DacStackReferenceErrorEnum::Reset()
8673 HRESULT DacStackReferenceErrorEnum::GetCount(unsigned int *pCount)
8675 SOSStackErrorList *curr = mHead;
8676 unsigned int count = 0;
8688 HRESULT DacStackReferenceErrorEnum::Next(unsigned int count, SOSStackRefError ref[], unsigned int *pFetched)
8690 if (pFetched == NULL || ref == NULL)
8694 for (i = 0; i < count && mCurr; ++i, mCurr = mCurr->pNext)
8695 ref[i] = mCurr->error;
8698 return i < count ? S_FALSE : S_OK;