2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
5 //*****************************************************************************
10 // ClrDataAccess implementation.
12 //*****************************************************************************
16 #include "typestring.h"
18 #include "debuginfostore.h"
19 #include "peimagelayout.inl"
20 #include "datatargetadapter.h"
21 #include "readonlydatatargetfacade.h"
22 #include "metadataexports.h"
26 #include "primitives.h"
29 #include "dwbucketmanager.hpp"
31 // To include definiton of IsThrowableThreadAbortException
32 // #include <exstatecommon.h>
34 CRITICAL_SECTION g_dacCritSec;
35 ClrDataAccess* g_dacImpl;
36 HINSTANCE g_thisModule;
38 extern VOID STDMETHODCALLTYPE TLS_FreeMasterSlotIndex();
41 DllMain(HANDLE instance, DWORD reason, LPVOID reserved)
43 static bool g_procInitialized = false;
47 case DLL_PROCESS_ATTACH:
49 if (g_procInitialized)
51 return FALSE; // should only get called once
55 int err = PAL_Initialize(0, NULL);
61 InitializeCriticalSection(&g_dacCritSec);
63 // Save the module handle.
64 g_thisModule = (HINSTANCE)instance;
66 g_procInitialized = true;
70 case DLL_PROCESS_DETACH:
71 // It's possible for this to be called without ATTACH completing (eg. if it failed)
72 if (g_procInitialized)
74 DeleteCriticalSection(&g_dacCritSec);
77 TLS_FreeMasterSlotIndex();
79 g_procInitialized = false;
93 ConvertUtf8(__in LPCUTF8 utf8,
96 __out_ecount_part_opt(bufLen, *nameLen) PWSTR buffer)
100 *nameLen = WszMultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
103 return HRESULT_FROM_GetLastError();
107 if (buffer && bufLen)
109 if (!WszMultiByteToWideChar(CP_UTF8, 0, utf8, -1, buffer, bufLen))
111 return HRESULT_FROM_GetLastError();
119 AllocUtf8(__in_opt LPCWSTR wstr,
121 __deref_out LPUTF8* utf8)
123 ULONG32 chars = WszWideCharToMultiByte(CP_UTF8, 0, wstr, srcChars,
124 NULL, 0, NULL, NULL);
127 return HRESULT_FROM_GetLastError();
130 // Make sure the converted string is always terminated.
131 if (srcChars != (ULONG32)-1)
133 if (!ClrSafeInt<ULONG32>::addition(chars, 1, chars))
135 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
139 char* mem = new (nothrow) char[chars];
142 return E_OUTOFMEMORY;
145 if (!WszWideCharToMultiByte(CP_UTF8, 0, wstr, srcChars,
146 mem, chars, NULL, NULL))
148 HRESULT hr = HRESULT_FROM_GetLastError();
153 if (srcChars != (ULONG32)-1)
163 GetFullClassNameFromMetadata(IMDInternalImport* mdImport,
164 mdTypeDef classToken,
166 __inout_ecount(bufferChars) LPUTF8 buffer)
169 LPCUTF8 baseName, namespaceName;
171 IfFailRet(mdImport->GetNameOfTypeDef(classToken, &baseName, &namespaceName));
172 return ns::MakePath(buffer, bufferChars, namespaceName, baseName) ?
173 S_OK : E_OUTOFMEMORY;
177 GetFullMethodNameFromMetadata(IMDInternalImport* mdImport,
178 mdMethodDef methodToken,
180 __inout_ecount(bufferChars) LPUTF8 buffer)
184 mdTypeDef classToken;
187 if (mdImport->GetParentToken(methodToken, &classToken) == S_OK)
190 GetFullClassNameFromMetadata(mdImport, classToken,
191 bufferChars, buffer)) != S_OK)
196 len = strlen(buffer);
198 bufferChars -= static_cast<ULONG32>(len) + 1;
202 return E_OUTOFMEMORY;
205 *buffer++ = NAMESPACE_SEPARATOR_CHAR;
209 IfFailRet(mdImport->GetNameOfMethodDef(methodToken, &methodName));
210 // Review conversion of size_t to ULONG32.
212 #pragma warning(push)
213 #pragma warning(disable:4267)
215 len = strlen(methodName);
219 if (len >= bufferChars)
221 return E_OUTOFMEMORY;
224 strcpy_s(buffer, bufferChars, methodName);
229 SplitFullName(__in_z __in PCWSTR fullName,
232 __deref_out_opt LPUTF8* namespaceName,
233 __deref_out_opt LPUTF8* typeName,
234 __deref_out_opt LPUTF8* memberName,
235 __deref_out_opt LPUTF8* params)
238 PCWSTR paramsStart, memberStart, memberEnd, typeStart;
246 // Split off parameters.
249 paramsStart = wcschr(fullName, W('('));
252 if (syntax != SPLIT_METHOD ||
253 paramsStart == fullName)
258 if ((status = AllocUtf8(paramsStart, (ULONG32)-1, params)) != S_OK)
263 memberEnd = paramsStart - 1;
268 memberEnd = fullName + (wcslen(fullName) - 1);
271 if (syntax != SPLIT_TYPE)
274 // Split off member name.
277 memberStart = memberEnd;
281 while (memberStart >= fullName &&
282 *memberStart != W('.'))
287 // Some member names (e.g. .ctor and .dtor) have
288 // dots, so go back to the first dot.
289 while (memberStart > fullName &&
290 memberStart[-1] == W('.'))
295 if (memberStart <= fullName)
299 // Caller expected dots in the
300 // member name and they weren't found.
301 status = E_INVALIDARG;
307 else if (memberDots == 0)
317 if (memberStart > memberEnd)
319 status = E_INVALIDARG;
323 if ((status = AllocUtf8(memberStart, (ULONG32)
324 (memberEnd - memberStart) + 1,
325 memberName)) != S_OK)
333 memberStart = memberEnd + 2;
337 // Split off type name.
340 if (memberStart > fullName)
342 // Must have at least one character for the type
343 // name. If there was a member name, there must
344 // also be a separator.
345 if (memberStart < fullName + 2)
347 status = E_INVALIDARG;
351 typeStart = memberStart - 2;
352 while (typeStart >= fullName &&
353 *typeStart != W('.'))
359 if ((status = AllocUtf8(typeStart, (ULONG32)
360 (memberStart - typeStart) - 1,
369 typeStart = fullName;
373 // Namespace must be the rest.
376 if (typeStart > fullName)
378 if ((status = AllocUtf8(fullName, (ULONG32)
379 (typeStart - fullName) - 1,
380 namespaceName)) != S_OK)
387 *namespaceName = NULL;
393 delete [] (*typeName);
395 delete [] (*memberName);
402 CompareUtf8(__in LPCUTF8 str1, __in LPCUTF8 str2, __in ULONG32 nameFlags)
404 if (nameFlags & CLRDATA_BYNAME_CASE_INSENSITIVE)
406 // XXX Microsoft - Convert to Unicode?
407 return SString::_stricmp(str1, str2);
410 return strcmp(str1, str2);
413 //----------------------------------------------------------------------------
417 //----------------------------------------------------------------------------
420 MetaEnum::Start(IMDInternalImport* mdImport, ULONG32 kind,
428 status = mdImport->EnumTypeDefInit(&m_enum);
432 status = mdImport->EnumInit(kind, container, &m_enum);
442 m_mdImport = mdImport;
459 m_mdImport->EnumTypeDefClose(&m_enum);
463 m_mdImport->EnumClose(&m_enum);
471 MetaEnum::NextToken(mdToken* token,
472 __deref_opt_out_opt LPCUTF8* namespaceName,
473 __deref_opt_out_opt LPCUTF8* name)
484 if (!m_mdImport->EnumTypeDefNext(&m_enum, token))
488 m_lastToken = *token;
489 if (namespaceName || name)
491 LPCSTR _name, _namespaceName;
493 IfFailRet(m_mdImport->GetNameOfTypeDef(*token, &_name, &_namespaceName));
496 *namespaceName = _namespaceName;
506 if (!m_mdImport->EnumNext(&m_enum, token))
510 m_lastToken = *token;
513 *namespaceName = NULL;
517 IfFailRet(m_mdImport->GetNameOfMethodDef(*token, name));
522 if (!m_mdImport->EnumNext(&m_enum, token))
526 m_lastToken = *token;
529 *namespaceName = NULL;
533 IfFailRet(m_mdImport->GetNameOfFieldDef(*token, name));
543 MetaEnum::NextDomainToken(AppDomain** appDomain,
550 // Use only the caller-provided app domain.
551 *appDomain = m_appDomain;
552 return NextToken(token, NULL, NULL);
556 // Splay tokens across all app domains.
561 if (m_lastToken == mdTokenNil)
563 // Need to fetch a token.
564 if ((status = NextToken(token, NULL, NULL)) != S_OK)
572 if (m_domainIter.Next())
577 m_lastToken = mdTokenNil;
580 *appDomain = m_domainIter.GetDomain();
581 *token = m_lastToken;
587 MetaEnum::NextTokenByName(__in_opt LPCUTF8 namespaceName,
588 __in_opt LPCUTF8 name,
593 LPCUTF8 tokNamespace, tokName;
597 if ((status = NextToken(token, &tokNamespace, &tokName)) != S_OK)
604 CompareUtf8(namespaceName, tokNamespace, nameFlags) != 0))
610 CompareUtf8(name, tokName, nameFlags) != 0))
620 MetaEnum::NextDomainTokenByName(__in_opt LPCUTF8 namespaceName,
621 __in_opt LPCUTF8 name,
623 AppDomain** appDomain, mdToken* token)
629 // Use only the caller-provided app domain.
630 *appDomain = m_appDomain;
631 return NextTokenByName(namespaceName, name, nameFlags, token);
635 // Splay tokens across all app domains.
640 if (m_lastToken == mdTokenNil)
642 // Need to fetch a token.
643 if ((status = NextTokenByName(namespaceName, name, nameFlags,
652 if (m_domainIter.Next())
657 m_lastToken = mdTokenNil;
660 *appDomain = m_domainIter.GetDomain();
661 *token = m_lastToken;
667 MetaEnum::New(Module* mod,
670 IXCLRDataAppDomain* pubAppDomain,
671 MetaEnum** metaEnumRet,
672 CLRDATA_ENUM* handle)
679 *handle = TO_CDENUM(NULL);
682 if (!mod->GetFile()->HasMetadata())
687 metaEnum = new (nothrow) MetaEnum;
690 return E_OUTOFMEMORY;
693 if ((status = metaEnum->
694 Start(mod->GetMDImport(), kind, container)) != S_OK)
702 metaEnum->m_appDomain =
703 ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain();
708 *metaEnumRet = metaEnum;
712 *handle = TO_CDENUM(metaEnum);
717 //----------------------------------------------------------------------------
721 //----------------------------------------------------------------------------
723 SplitName::SplitName(SplitSyntax syntax, ULONG32 nameFlags,
727 m_nameFlags = nameFlags;
728 m_memberDots = memberDots;
734 SplitName::Delete(void)
736 delete [] m_namespaceName;
737 m_namespaceName = NULL;
738 delete [] m_typeName;
740 delete [] m_memberName;
747 SplitName::Clear(void)
749 m_namespaceName = NULL;
751 m_typeToken = mdTypeDefNil;
753 m_memberToken = mdTokenNil;
757 m_metaEnum.m_appDomain = NULL;
763 SplitName::SplitString(__in_opt PCWSTR fullName)
765 if (m_syntax == SPLIT_NO_NAME)
779 return SplitFullName(fullName,
789 WCHAR* wcrscan(LPCWSTR beg, LPCWSTR end, WCHAR ch)
791 //_ASSERTE(beg <= end);
793 for (p = (WCHAR*)end; p >= beg; --p)
801 // This functions allocates a new UTF8 string that contains the classname
802 // lying between the current sepName and the previous sepName. E.g. for a
803 // class name of "Outer+middler+inner" when sepName points to the NULL
804 // terminator this function will return "inner" in pResult and will update
805 // sepName to point to the second '+' character in the string. When sepName
806 // points to the first '+' character this function will return "Outer" in
807 // pResult and sepName will point one WCHAR before fullName.
808 HRESULT NextEnclosingClasName(LPCWSTR fullName, __deref_inout LPWSTR& sepName, __deref_out LPUTF8 *pResult)
810 if (sepName < fullName)
814 //_ASSERTE(*sepName == W('\0') || *sepName == W('+') || *sepName == W('/'));
816 LPWSTR origInnerName = sepName-1;
817 if ((sepName = wcrscan(fullName, origInnerName, W('+'))) < fullName)
819 sepName = wcrscan(fullName, origInnerName, W('/'));
822 return AllocUtf8(sepName+1, static_cast<ULONG32>(origInnerName-sepName), pResult);
826 SplitName::FindType(IMDInternalImport* mdInternal)
828 if (m_typeToken != mdTypeDefNil)
838 if ((m_namespaceName == NULL || m_namespaceName[0] == '\0')
839 && (CompareUtf8(COR_MODULE_CLASS, m_typeName, m_nameFlags)==0))
841 m_typeToken = TokenFromRid(1, mdtTypeDef); // <Module> class always has a RID of 1.
847 if (metaEnum.Start(mdInternal, mdtTypeDef, mdTypeDefNil) != S_OK)
855 WCHAR wszName[MAX_CLASS_NAME];
856 ConvertUtf8(m_typeName, MAX_CLASS_NAME, &length, wszName);
862 pHead = wszName + length;
864 if (FAILED(NextEnclosingClasName(wszName, pHead, &curClassName)))
869 // an inner class has an empty namespace associated with it
870 HRESULT hr = metaEnum.NextTokenByName((pHead < wszName) ? m_namespaceName : "",
874 delete[] curClassName;
878 // if we didn't find a token with the given name
881 else if (pHead < wszName)
883 // if we did find a token, *and* the class name given
884 // does not specify any enclosing class, that's it
889 // restart with innermost class
890 pHead = wszName + length;
891 mdTypeDef tkInner = m_typeToken;
898 && SUCCEEDED(NextEnclosingClasName(wszName, pHead, &utf8Name))
901 if (mdInternal->GetNestedClassProps(tkInner, &tkOuter) != S_OK)
902 tkOuter = mdTypeDefNil;
905 if (FAILED(mdInternal->GetNameOfTypeDef(tkInner, &szName, &szNS)))
909 bRetry = (CompareUtf8(utf8Name, szName, m_nameFlags) != 0);
912 // if this is outermost class we need to compare namespaces too
913 if (tkOuter == mdTypeDefNil)
915 // is this the outermost in the class name, too?
917 && CompareUtf8(m_namespaceName ? m_namespaceName : "", szNS, m_nameFlags) == 0)
938 SplitName::FindMethod(IMDInternalImport* mdInternal)
940 if (m_memberToken != mdTokenNil)
945 if (m_typeToken == mdTypeDefNil ||
951 ULONG32 EmptySig = 0;
953 // XXX Microsoft - Compare using signature when available.
954 if (mdInternal->FindMethodDefUsingCompare(m_typeToken,
956 (PCCOR_SIGNATURE)&EmptySig,
960 &m_memberToken) != S_OK)
962 m_memberToken = mdTokenNil;
970 SplitName::FindField(IMDInternalImport* mdInternal)
972 if (m_memberToken != mdTokenNil)
977 if (m_typeToken == mdTypeDefNil ||
981 // Can't have params with a field.
987 if (metaEnum.Start(mdInternal, mdtFieldDef, m_typeToken) != S_OK)
992 return metaEnum.NextTokenByName(NULL,
995 &m_memberToken) == S_OK;
999 SplitName::AllocAndSplitString(__in_opt PCWSTR fullName,
1007 if (nameFlags & ~(CLRDATA_BYNAME_CASE_SENSITIVE |
1008 CLRDATA_BYNAME_CASE_INSENSITIVE))
1010 return E_INVALIDARG;
1013 *split = new (nothrow) SplitName(syntax, nameFlags, memberDots);
1016 return E_OUTOFMEMORY;
1019 if ((status = (*split)->SplitString(fullName)) != S_OK)
1029 SplitName::CdStartMethod(__in_opt PCWSTR fullName,
1032 mdTypeDef typeToken,
1033 AppDomain* appDomain,
1034 IXCLRDataAppDomain* pubAppDomain,
1035 SplitName** splitRet,
1036 CLRDATA_ENUM* handle)
1042 *handle = TO_CDENUM(NULL);
1045 if ((status = SplitName::
1046 AllocAndSplitString(fullName, SPLIT_METHOD, nameFlags,
1047 methDots, &split)) != S_OK)
1052 if (typeToken == mdTypeDefNil)
1054 if (!split->FindType(mod->GetMDImport()))
1056 bool hasNamespace = split->m_namespaceName != NULL;
1061 // We may have a case where there's an
1062 // explicitly implemented method which
1063 // has dots in the name. If it's possible
1064 // to move the method name dot split
1065 // back, go ahead and retry that way.
1074 return E_INVALIDARG;
1077 typeToken = split->m_typeToken;
1081 if (split->m_namespaceName || split->m_typeName)
1084 return E_INVALIDARG;
1088 if ((status = split->m_metaEnum.
1089 Start(mod->GetMDImport(), mdtMethodDef, typeToken)) != S_OK)
1095 split->m_metaEnum.m_appDomain = appDomain;
1098 split->m_metaEnum.m_appDomain =
1099 ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain();
1101 split->m_module = mod;
1103 *handle = TO_CDENUM(split);
1112 SplitName::CdNextMethod(CLRDATA_ENUM* handle,
1115 SplitName* split = FROM_CDENUM(SplitName, *handle);
1118 return E_INVALIDARG;
1121 return split->m_metaEnum.
1122 NextTokenByName(NULL, split->m_memberName, split->m_nameFlags,
1127 SplitName::CdNextDomainMethod(CLRDATA_ENUM* handle,
1128 AppDomain** appDomain,
1131 SplitName* split = FROM_CDENUM(SplitName, *handle);
1134 return E_INVALIDARG;
1137 return split->m_metaEnum.
1138 NextDomainTokenByName(NULL, split->m_memberName, split->m_nameFlags,
1143 SplitName::CdStartField(__in_opt PCWSTR fullName,
1146 IXCLRDataTypeInstance* fromTypeInst,
1147 TypeHandle typeHandle,
1149 mdTypeDef typeToken,
1152 IXCLRDataTask* pubTlsThread,
1153 AppDomain* appDomain,
1154 IXCLRDataAppDomain* pubAppDomain,
1155 SplitName** splitRet,
1156 CLRDATA_ENUM* handle)
1161 *handle = TO_CDENUM(NULL);
1163 if ((status = SplitName::
1164 AllocAndSplitString(fullName,
1165 fullName ? SPLIT_FIELD : SPLIT_NO_NAME,
1172 if (typeHandle.IsNull())
1174 if (typeToken == mdTypeDefNil)
1176 if (!split->FindType(mod->GetMDImport()))
1178 status = E_INVALIDARG;
1182 typeToken = split->m_typeToken;
1186 if (split->m_namespaceName || split->m_typeName)
1188 status = E_INVALIDARG;
1193 // With phased class loading, this may return a partially-loaded type
1194 // @todo : does this matter?
1195 typeHandle = mod->LookupTypeDef(split->m_typeToken);
1196 if (typeHandle.IsNull())
1198 status = E_UNEXPECTED;
1203 if ((status = InitFieldIter(&split->m_fieldEnum,
1207 fromTypeInst)) != S_OK)
1212 split->m_objBase = objBase;
1213 split->m_tlsThread = tlsThread;
1216 split->m_tlsThread = ((ClrDataTask*)pubTlsThread)->GetThread();
1218 split->m_metaEnum.m_appDomain = appDomain;
1221 split->m_metaEnum.m_appDomain =
1222 ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain();
1224 split->m_module = mod;
1226 *handle = TO_CDENUM(split);
1239 SplitName::CdNextField(ClrDataAccess* dac,
1240 CLRDATA_ENUM* handle,
1241 IXCLRDataTypeDefinition** fieldType,
1242 ULONG32* fieldFlags,
1243 IXCLRDataValue** value,
1244 ULONG32 nameBufRetLen,
1245 ULONG32* nameLenRet,
1246 __out_ecount_part_opt(nameBufRetLen, *nameLenRet) WCHAR nameBufRet[ ],
1247 IXCLRDataModule** tokenScopeRet,
1248 mdFieldDef* tokenRet)
1252 SplitName* split = FROM_CDENUM(SplitName, *handle);
1255 return E_INVALIDARG;
1258 FieldDesc* fieldDesc;
1260 while ((fieldDesc = split->m_fieldEnum.Next()))
1262 if (split->m_syntax != SPLIT_NO_NAME)
1265 if (FAILED(fieldDesc->GetName_NoThrow(&fieldName)) ||
1266 (split->Compare(split->m_memberName, fieldName) != 0))
1272 split->m_lastField = fieldDesc;
1274 if (fieldFlags != NULL)
1277 GetTypeFieldValueFlags(fieldDesc->GetFieldTypeHandleThrowing(),
1280 IsFieldFromParentClass() ?
1281 CLRDATA_FIELD_IS_INHERITED : 0,
1285 if ((nameBufRetLen != 0) || (nameLenRet != NULL))
1287 LPCUTF8 szFieldName;
1288 status = fieldDesc->GetName_NoThrow(&szFieldName);
1294 status = ConvertUtf8(
1305 if (tokenScopeRet && !value)
1307 *tokenScopeRet = new (nothrow)
1308 ClrDataModule(dac, fieldDesc->GetModule());
1309 if (!*tokenScopeRet)
1311 return E_OUTOFMEMORY;
1317 *tokenRet = fieldDesc->GetMemberDef();
1322 TypeHandle fieldTypeHandle = fieldDesc->GetFieldTypeHandleThrowing();
1323 *fieldType = new (nothrow)
1324 ClrDataTypeDefinition(dac,
1325 fieldTypeHandle.GetModule(),
1326 fieldTypeHandle.GetMethodTable()->GetCl(),
1328 if (!*fieldType && tokenScopeRet)
1330 delete *tokenScopeRet;
1332 return *fieldType ? S_OK : E_OUTOFMEMORY;
1337 return ClrDataValue::
1338 NewFromFieldDesc(dac,
1339 split->m_metaEnum.m_appDomain,
1340 split->m_fieldEnum.IsFieldFromParentClass() ?
1341 CLRDATA_VALUE_IS_INHERITED : 0,
1361 SplitName::CdNextDomainField(ClrDataAccess* dac,
1362 CLRDATA_ENUM* handle,
1363 IXCLRDataValue** value)
1367 SplitName* split = FROM_CDENUM(SplitName, *handle);
1370 return E_INVALIDARG;
1373 if (split->m_metaEnum.m_appDomain)
1375 // Use only the caller-provided app domain.
1376 return CdNextField(dac, handle, NULL, NULL, value,
1377 0, NULL, NULL, NULL, NULL);
1381 // Splay fields across all app domains.
1386 if (!split->m_lastField)
1388 // Need to fetch a field.
1389 if ((status = CdNextField(dac, handle, NULL, NULL, NULL,
1390 0, NULL, NULL, NULL, NULL)) != S_OK)
1395 split->m_metaEnum.m_domainIter.Init();
1398 if (split->m_metaEnum.m_domainIter.Next())
1403 split->m_lastField = NULL;
1406 return ClrDataValue::
1407 NewFromFieldDesc(dac,
1408 split->m_metaEnum.m_domainIter.GetDomain(),
1409 split->m_fieldEnum.IsFieldFromParentClass() ?
1410 CLRDATA_VALUE_IS_INHERITED : 0,
1424 SplitName::CdStartType(__in_opt PCWSTR fullName,
1427 AppDomain* appDomain,
1428 IXCLRDataAppDomain* pubAppDomain,
1429 SplitName** splitRet,
1430 CLRDATA_ENUM* handle)
1435 *handle = TO_CDENUM(NULL);
1437 if ((status = SplitName::
1438 AllocAndSplitString(fullName, SPLIT_TYPE, nameFlags, 0,
1444 if ((status = split->m_metaEnum.
1445 Start(mod->GetMDImport(), mdtTypeDef, mdTokenNil)) != S_OK)
1451 split->m_metaEnum.m_appDomain = appDomain;
1454 split->m_metaEnum.m_appDomain =
1455 ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain();
1457 split->m_module = mod;
1459 *handle = TO_CDENUM(split);
1468 SplitName::CdNextType(CLRDATA_ENUM* handle,
1471 SplitName* split = FROM_CDENUM(SplitName, *handle);
1474 return E_INVALIDARG;
1477 return split->m_metaEnum.
1478 NextTokenByName(split->m_namespaceName, split->m_typeName,
1479 split->m_nameFlags, token);
1483 SplitName::CdNextDomainType(CLRDATA_ENUM* handle,
1484 AppDomain** appDomain,
1487 SplitName* split = FROM_CDENUM(SplitName, *handle);
1490 return E_INVALIDARG;
1493 return split->m_metaEnum.
1494 NextDomainTokenByName(split->m_namespaceName, split->m_typeName,
1495 split->m_nameFlags, appDomain, token);
1498 //----------------------------------------------------------------------------
1500 // DacInstanceManager.
1502 // Data retrieved from the target process is cached for two reasons:
1504 // 1. It may be necessary to map from the host address back to the target
1505 // address. For example, if any code uses a 'this' pointer or
1506 // takes the address of a field the address has to be translated from
1507 // host to target. This requires instances to be held as long as
1508 // they may be referenced.
1510 // 2. Data is often referenced multiple times so caching is an important
1511 // performance advantage.
1513 // Ideally we'd like to implement a simple page cache but this is
1514 // complicated by the fact that user minidump memory can have
1515 // arbitrary granularity and also that the member operator (->)
1516 // needs to return a pointer to an object. That means that all of
1517 // the data for an object must be sequential and cannot be split
1518 // at page boundaries.
1520 // Data can also be accessed with different sizes. For example,
1521 // a base struct can be accessed, then cast to a derived struct and
1522 // accessed again with the larger derived size. The cache must
1523 // be able to replace data to maintain the largest amount of data
1526 // We keep track of each access and the recovered memory for it.
1527 // A hash on target address allows quick access to instance data
1528 // by target address. The data for each access has a header on it
1529 // for bookkeeping purposes, so host address to target address translation
1530 // is just a matter of backing up to the header and pulling the target
1531 // address from it. Keeping each access separately allows easy
1532 // replacement by larger accesses.
1534 //----------------------------------------------------------------------------
1536 DacInstanceManager::DacInstanceManager(void)
1537 : m_unusedBlock(NULL)
1542 DacInstanceManager::~DacInstanceManager(void)
1544 // We are stopping debugging in this case, so don't save any block of memory.
1545 // Otherwise, there will be a memory leak.
1549 #if defined(DAC_HASHTABLE)
1551 DacInstanceManager::Add(DAC_INSTANCE* inst)
1553 // Assert that we don't add NULL instances. This allows us to assert that found instances
1554 // are not NULL in DacInstanceManager::Find
1555 _ASSERTE(inst != NULL);
1557 DWORD nHash = DAC_INSTANCE_HASH(inst->addr);
1558 HashInstanceKeyBlock* block = m_hash[nHash];
1560 if (!block || block->firstElement == 0)
1563 HashInstanceKeyBlock* newBlock;
1566 newBlock = (HashInstanceKeyBlock*) new (nothrow) BYTE[HASH_INSTANCE_BLOCK_ALLOC_SIZE];
1570 // We allocate one big memory chunk that has a block for every index of the hash table to
1571 // improve data locality and reduce the number of allocs. In most cases, a hash bucket will
1572 // use only one block, so improving data locality across blocks (i.e. keeping the buckets of the
1573 // hash table together) should help.
1574 newBlock = (HashInstanceKeyBlock*)
1575 ClrVirtualAlloc(NULL, HASH_INSTANCE_BLOCK_ALLOC_SIZE*NumItems(m_hash), MEM_COMMIT, PAGE_READWRITE);
1583 // We add the newest block to the start of the list assuming that most accesses are for
1584 // recently added elements.
1585 newBlock->next = block;
1586 m_hash[nHash] = newBlock; // The previously allocated block
1587 newBlock->firstElement = HASH_INSTANCE_BLOCK_NUM_ELEMENTS;
1592 for (DWORD j = 0; j < NumItems(m_hash); j++)
1594 m_hash[j] = newBlock;
1595 newBlock->next = NULL; // The previously allocated block
1596 newBlock->firstElement = HASH_INSTANCE_BLOCK_NUM_ELEMENTS;
1597 newBlock = (HashInstanceKeyBlock*) (((BYTE*) newBlock) + HASH_INSTANCE_BLOCK_ALLOC_SIZE);
1599 block = m_hash[nHash];
1602 _ASSERTE(block->firstElement > 0);
1603 block->firstElement--;
1604 block->instanceKeys[block->firstElement].addr = inst->addr;
1605 block->instanceKeys[block->firstElement].instance = inst;
1610 #else //DAC_HASHTABLE
1612 DacInstanceManager::Add(DAC_INSTANCE* inst)
1614 _ASSERTE(inst != NULL);
1616 bool isInserted = (m_hash.find(inst->addr) == m_hash.end());
1618 DAC_INSTANCE *(&target) = m_hash[inst->addr];
1619 _ASSERTE(!isInserted || target == NULL);
1620 if( target != NULL )
1622 //This is necessary to preserve the semantics of Supersede, however, it
1623 //is more or less dead code.
1624 inst->next = target;
1627 //verify descending order
1628 _ASSERTE(inst->size >= target->size);
1638 #endif // #if defined(DAC_HASHTABLE)
1642 DacInstanceManager::Alloc(TADDR addr, ULONG32 size, DAC_USAGE_TYPE usage)
1644 SUPPORTS_DAC_HOST_ONLY;
1645 DAC_INSTANCE_BLOCK* block;
1649 static_assert_no_msg(sizeof(DAC_INSTANCE_BLOCK) <= DAC_INSTANCE_ALIGN);
1650 static_assert_no_msg((sizeof(DAC_INSTANCE) & (DAC_INSTANCE_ALIGN - 1)) == 0);
1653 // All allocated instances must be kept alive as long
1654 // as anybody may have a host pointer for one of them.
1655 // This means that we cannot delete an arbitrary instance
1656 // unless we are sure no pointers exist, which currently
1657 // is not possible to determine, thus we just hold everything
1658 // until a Flush. This greatly simplifies instance allocation
1659 // as we can then just sweep through large blocks rather
1660 // than having to use a real allocator. The only
1661 // complication is that we need to keep all instance
1662 // data aligned. We have guaranteed that the header will
1663 // preserve alignment of the data following if the header
1664 // is aligned, so as long as we round up all allocations
1665 // to a multiple of the alignment size everything just works.
1668 fullSize = (size + DAC_INSTANCE_ALIGN - 1) & ~(DAC_INSTANCE_ALIGN - 1);
1669 _ASSERTE(fullSize && fullSize <= 0xffffffff - 2 * sizeof(*inst));
1670 fullSize += sizeof(*inst);
1673 // Check for an existing block with space.
1676 for (block = m_blocks; block; block = block->next)
1678 if (fullSize <= block->bytesFree)
1687 // No existing block has enough space, so allocate a new
1688 // one if necessary and link it in. We know we're allocating large
1689 // blocks so directly VirtualAlloc. We save one block through a
1690 // flush so that we spend less time allocating/deallocating.
1693 ULONG32 blockSize = fullSize + DAC_INSTANCE_ALIGN;
1694 if (blockSize < DAC_INSTANCE_BLOCK_ALLOCATION)
1696 blockSize = DAC_INSTANCE_BLOCK_ALLOCATION;
1699 // If we have a saved block and it's large enough, use it.
1700 block = m_unusedBlock;
1701 if ((block != NULL) &&
1702 ((block->bytesUsed + block->bytesFree) >= blockSize))
1704 m_unusedBlock = NULL;
1706 // Right now, we're locked to DAC_INSTANCE_BLOCK_ALLOCATION but
1707 // that might change in the future if we decide to do something
1708 // else with the size guarantee in code:DacInstanceManager::FreeAllBlocks
1709 blockSize = block->bytesUsed + block->bytesFree;
1713 block = (DAC_INSTANCE_BLOCK*)
1714 ClrVirtualAlloc(NULL, blockSize, MEM_COMMIT, PAGE_READWRITE);
1722 // Keep the first aligned unit for the block header.
1723 block->bytesUsed = DAC_INSTANCE_ALIGN;
1724 block->bytesFree = blockSize - DAC_INSTANCE_ALIGN;
1726 block->next = m_blocks;
1729 m_blockMemUsage += blockSize;
1732 inst = (DAC_INSTANCE*)((PBYTE)block + block->bytesUsed);
1733 block->bytesUsed += fullSize;
1734 _ASSERTE(block->bytesFree >= fullSize);
1735 block->bytesFree -= fullSize;
1740 inst->sig = DAC_INSTANCE_SIG;
1741 inst->usage = usage;
1746 m_instMemUsage += fullSize;
1751 DacInstanceManager::ReturnAlloc(DAC_INSTANCE* inst)
1753 SUPPORTS_DAC_HOST_ONLY;
1754 DAC_INSTANCE_BLOCK* block;
1755 DAC_INSTANCE_BLOCK * pPrevBlock;
1759 // This special routine handles cleanup in
1760 // cases where an instances has been allocated
1761 // but must be returned due to a following error.
1762 // The given instance must be the last instance
1763 // in an existing block.
1767 ((inst->size + DAC_INSTANCE_ALIGN - 1) & ~(DAC_INSTANCE_ALIGN - 1)) +
1771 for (block = m_blocks; block; pPrevBlock = block, block = block->next)
1773 if ((PBYTE)inst == (PBYTE)block + (block->bytesUsed - fullSize))
1784 block->bytesUsed -= fullSize;
1785 block->bytesFree += fullSize;
1787 m_instMemUsage -= fullSize;
1789 // If the block is empty after returning the specified instance, that means this block was newly created
1790 // when this instance was allocated. We have seen cases where we are asked to allocate a
1791 // large chunk of memory only to fail to read the memory from a dump later on, i.e. when both the target
1792 // address and the size are invalid. If we keep the allocation, we'll grow the VM size unnecessarily.
1793 // Thus, release a block if it's empty and if it's not the default size (to avoid thrashing memory).
1794 // See Dev10 Dbug 812112 for more information.
1795 if ((block->bytesUsed == DAC_INSTANCE_ALIGN) &&
1796 ((block->bytesFree + block->bytesUsed) != DAC_INSTANCE_BLOCK_ALLOCATION))
1798 // The empty block is at the beginning of the list.
1799 if (pPrevBlock == NULL)
1801 m_blocks = block->next;
1805 _ASSERTE(pPrevBlock->next == block);
1806 pPrevBlock->next = block->next;
1808 ClrVirtualFree(block, 0, MEM_RELEASE);
1813 #if defined(DAC_HASHTABLE)
1815 DacInstanceManager::Find(TADDR addr)
1818 #if defined(DAC_MEASURE_PERF)
1819 unsigned _int64 nStart, nEnd;
1821 nStart = GetCycleCount();
1822 #endif // #if defined(DAC_MEASURE_PERF)
1824 HashInstanceKeyBlock* block = m_hash[DAC_INSTANCE_HASH(addr)];
1826 #if defined(DAC_MEASURE_PERF)
1827 nEnd = GetCycleCount();
1828 g_nFindHashTotalTime += nEnd - nStart;
1829 #endif // #if defined(DAC_MEASURE_PERF)
1833 DWORD nIndex = block->firstElement;
1834 for (; nIndex < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; nIndex++)
1836 if (block->instanceKeys[nIndex].addr == addr)
1838 #if defined(DAC_MEASURE_PERF)
1839 nEnd = GetCycleCount();
1841 g_nFindTotalTime += nEnd - nStart;
1842 if (g_nStackWalk) g_nFindStackTotalTime += nEnd - nStart;
1843 #endif // #if defined(DAC_MEASURE_PERF)
1845 DAC_INSTANCE* inst = block->instanceKeys[nIndex].instance;
1847 // inst should not be NULL even if the address was superseded. We search
1848 // the entries in the reverse order they were added. So we should have
1849 // found the superseding entry before this one. (Of course, if a NULL instance
1850 // has been added, this assert is meaningless. DacInstanceManager::Add
1851 // asserts that NULL instances aren't added.)
1853 _ASSERTE(inst != NULL);
1858 block = block->next;
1861 #if defined(DAC_MEASURE_PERF)
1862 nEnd = GetCycleCount();
1864 g_nFindTotalTime += nEnd - nStart;
1865 if (g_nStackWalk) g_nFindStackTotalTime += nEnd - nStart;
1866 #endif // #if defined(DAC_MEASURE_PERF)
1870 #else //DAC_HASHTABLE
1872 DacInstanceManager::Find(TADDR addr)
1874 DacInstanceHashIterator iter = m_hash.find(addr);
1875 if( iter == m_hash.end() )
1881 return iter->second;
1884 #endif // if defined(DAC_HASHTABLE)
1887 DacInstanceManager::Write(DAC_INSTANCE* inst, bool throwEx)
1891 if (inst->usage == DAC_VPTR)
1893 // Skip over the host-side vtable pointer when
1895 status = DacWriteAll(inst->addr + sizeof(TADDR),
1896 (PBYTE)(inst + 1) + sizeof(PVOID),
1897 inst->size - sizeof(TADDR),
1902 // Write the whole instance back.
1903 status = DacWriteAll(inst->addr, inst + 1, inst->size, throwEx);
1909 #if defined(DAC_HASHTABLE)
1911 DacInstanceManager::Supersede(DAC_INSTANCE* inst)
1913 _ASSERTE(inst != NULL);
1916 // This instance has been superseded by a larger
1917 // one and so must be removed from the hash. However,
1918 // code may be holding the instance pointer so it
1919 // can't just be deleted. Put it on a list for
1923 HashInstanceKeyBlock* block = m_hash[DAC_INSTANCE_HASH(inst->addr)];
1926 DWORD nIndex = block->firstElement;
1927 for (; nIndex < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; nIndex++)
1929 if (block->instanceKeys[nIndex].instance == inst)
1931 block->instanceKeys[nIndex].instance = NULL;
1935 if (nIndex < HASH_INSTANCE_BLOCK_NUM_ELEMENTS)
1939 block = block->next;
1942 AddSuperseded(inst);
1944 #else //DAC_HASHTABLE
1946 DacInstanceManager::Supersede(DAC_INSTANCE* inst)
1948 _ASSERTE(inst != NULL);
1951 // This instance has been superseded by a larger
1952 // one and so must be removed from the hash. However,
1953 // code may be holding the instance pointer so it
1954 // can't just be deleted. Put it on a list for
1958 DacInstanceHashIterator iter = m_hash.find(inst->addr);
1959 if( iter == m_hash.end() )
1962 DAC_INSTANCE** bucket = &(iter->second);
1963 DAC_INSTANCE* cur = *bucket;
1964 DAC_INSTANCE* prev = NULL;
1965 //walk through the chain looking for this particular instance
1972 *bucket = inst->next;
1976 prev->next = inst->next;
1985 AddSuperseded(inst);
1987 #endif // if defined(DAC_HASHTABLE)
1989 // This is the default Flush() called when the DAC cache is invalidated,
1990 // e.g. when we continue the debuggee process. In this case, we want to
1991 // save one block of memory to avoid thrashing. See the usage of m_unusedBlock
1992 // for more information.
1993 void DacInstanceManager::Flush(void)
1998 void DacInstanceManager::Flush(bool fSaveBlock)
2000 SUPPORTS_DAC_HOST_ONLY;
2003 // All allocated memory is in the block
2004 // list, so just free the blocks and
2005 // forget all the internal pointers.
2010 FreeAllBlocks(fSaveBlock);
2012 DAC_INSTANCE_PUSH* push = m_instPushed;
2018 m_instPushed = push->next;
2019 m_blocks = push->blocks;
2023 // If we are not saving any memory blocks, then clear the saved buffer block (if any) as well.
2026 if (m_unusedBlock != NULL)
2028 ClrVirtualFree(m_unusedBlock, 0, MEM_RELEASE);
2029 m_unusedBlock = NULL;
2033 #if defined(DAC_HASHTABLE)
2034 for (int i = NumItems(m_hash) - 1; i >= 0; i--)
2036 HashInstanceKeyBlock* block = m_hash[i];
2037 HashInstanceKeyBlock* next;
2047 ClrVirtualFree(block, 0, MEM_RELEASE);
2052 #else //DAC_HASHTABLE
2054 #endif //DAC_HASHTABLE
2059 #if defined(DAC_HASHTABLE)
2061 DacInstanceManager::ClearEnumMemMarker(void)
2066 for (i = 0; i < NumItems(m_hash); i++)
2068 HashInstanceKeyBlock* block = m_hash[i];
2072 for (j = block->firstElement; j < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; j++)
2074 inst = block->instanceKeys[j].instance;
2080 block = block->next;
2083 for (inst = m_superseded; inst; inst = inst->next)
2088 #else //DAC_HASHTABLE
2090 DacInstanceManager::ClearEnumMemMarker(void)
2095 DacInstanceHashIterator end = m_hash.end();
2096 /* REVISIT_TODO Fri 10/20/2006
2097 * This might have an issue, since it might miss chained entries off of
2098 * ->next. However, ->next is going away, and for all intents and
2099 * purposes, this never happens.
2101 for( DacInstanceHashIterator cur = m_hash.begin(); cur != end; ++cur )
2103 cur->second->enumMem = 0;
2106 for (inst = m_superseded; inst; inst = inst->next)
2111 #endif // if defined(DAC_HASHTABLE)
2114 #if defined(DAC_HASHTABLE)
2117 // Iterating through all of the hash entry and report the memory
2118 // instance to minidump
2120 // This function returns the total number of bytes that it reported.
2124 DacInstanceManager::DumpAllInstances(
2125 ICLRDataEnumMemoryRegionsCallback *pCallBack) // memory report call back
2131 #if defined(DAC_MEASURE_PERF)
2132 FILE* fp = fopen("c:\\dumpLog.txt", "a");
2134 #endif // #if defined(DAC_MEASURE_PERF)
2136 for (i = 0; i < NumItems(m_hash); i++)
2139 #if defined(DAC_MEASURE_PERF)
2140 int numInBucket = 0;
2141 #endif // #if defined(DAC_MEASURE_PERF)
2143 HashInstanceKeyBlock* block = m_hash[i];
2147 for (j = block->firstElement; j < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; j++)
2149 inst = block->instanceKeys[j].instance;
2151 // Only report those we intended to.
2152 // So far, only metadata is excluded!
2154 if (inst && inst->noReport == 0)
2156 cbTotal += inst->size;
2157 HRESULT hr = pCallBack->EnumMemoryRegion(TO_CDADDR(inst->addr), inst->size);
2158 if (hr == COR_E_OPERATIONCANCELED)
2164 #if defined(DAC_MEASURE_PERF)
2169 #endif // #if defined(DAC_MEASURE_PERF)
2171 block = block->next;
2174 #if defined(DAC_MEASURE_PERF)
2175 fprintf(fp, "%4d: %4d%s", i, numInBucket, (i+1)%5? "; " : "\n");
2176 total += numInBucket;
2177 #endif // #if defined(DAC_MEASURE_PERF)
2181 #if defined(DAC_MEASURE_PERF)
2182 fprintf(fp, "\n\nTotal entries: %d\n\n", total);
2184 #endif // #if defined(DAC_MEASURE_PERF)
2189 #else //DAC_HASHTABLE
2192 // Iterating through all of the hash entry and report the memory
2193 // instance to minidump
2195 // This function returns the total number of bytes that it reported.
2199 DacInstanceManager::DumpAllInstances(
2200 ICLRDataEnumMemoryRegionsCallback *pCallBack) // memory report call back
2202 SUPPORTS_DAC_HOST_ONLY;
2207 #if defined(DAC_MEASURE_PERF)
2208 FILE* fp = fopen("c:\\dumpLog.txt", "a");
2209 #endif // #if defined(DAC_MEASURE_PERF)
2211 #if defined(DAC_MEASURE_PERF)
2212 int numInBucket = 0;
2213 #endif // #if defined(DAC_MEASURE_PERF)
2215 DacInstanceHashIterator end = m_hash.end();
2216 for (DacInstanceHashIterator cur = m_hash.begin(); end != cur; ++cur)
2220 // Only report those we intended to.
2221 // So far, only metadata is excluded!
2223 if (inst->noReport == 0)
2225 cbTotal += inst->size;
2226 HRESULT hr = pCallBack->EnumMemoryRegion(TO_CDADDR(inst->addr), inst->size);
2227 if (hr == COR_E_OPERATIONCANCELED)
2233 #if defined(DAC_MEASURE_PERF)
2235 #endif // #if defined(DAC_MEASURE_PERF)
2238 #if defined(DAC_MEASURE_PERF)
2239 fprintf(fp, "\n\nTotal entries: %d\n\n", numInBucket);
2241 #endif // #if defined(DAC_MEASURE_PERF)
2246 #endif // if defined(DAC_HASHTABLE)
2249 DacInstanceManager::FindInstanceBlock(DAC_INSTANCE* inst)
2251 for (DAC_INSTANCE_BLOCK* block = m_blocks; block; block = block->next)
2253 if ((PBYTE)inst >= (PBYTE)block &&
2254 (PBYTE)inst < (PBYTE)block + block->bytesUsed)
2263 // If fSaveBlock is false, free all blocks of allocated memory. Otherwise,
2264 // free all blocks except the one we save to avoid thrashing memory.
2265 // Callers very frequently flush repeatedly with little memory needed in DAC
2266 // so this avoids wasteful repeated allocations/deallocations.
2267 // There is a very unlikely case that we'll have allocated an extremely large
2268 // block; if this is the only block we will save none since this block will
2269 // remain allocated.
2271 DacInstanceManager::FreeAllBlocks(bool fSaveBlock)
2273 DAC_INSTANCE_BLOCK* block;
2275 while ((block = m_blocks))
2277 m_blocks = block->next;
2279 // If we haven't saved our single block yet and this block is the default size
2280 // then we will save it instead of freeing it. This avoids saving an unnecessarily large
2282 // Do *NOT* trash the byte counts. code:DacInstanceManager::Alloc
2283 // depends on them being correct when checking to see if a block is large enough.
2285 (m_unusedBlock == NULL) &&
2286 ((block->bytesFree + block->bytesUsed) == DAC_INSTANCE_BLOCK_ALLOCATION))
2288 // Just to avoid confusion, since we're keeping it around.
2290 m_unusedBlock = block;
2294 ClrVirtualFree(block, 0, MEM_RELEASE);
2299 //----------------------------------------------------------------------------
2301 // DacStreamManager.
2303 //----------------------------------------------------------------------------
2305 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
2307 namespace serialization { namespace bin {
2309 //========================================================================
2310 // Support functions for binary serialization of simple types to a buffer:
2311 // - raw_size() returns the size in bytes of the binary representation
2313 // - raw_serialize() copies the binary representation of a value into a
2315 // - raw_deserialize() generates a value from its binary representation
2317 // Beyond simple types the APIs below support SString instances. SStrings
2318 // are stored as UTF8 strings.
2319 //========================================================================
2321 static const size_t ErrOverflow = (size_t)(-1);
2325 // Template class is_blittable
2326 template <typename _Ty, typename Enable = void>
2329 { // determines whether _Ty is blittable
2332 template <typename _Ty>
2333 class is_blittable<_Ty, typename std::enable_if<std::is_arithmetic<_Ty>::value>::type>
2335 { // determines whether _Ty is blittable
2338 // allow types to declare themselves blittable by including a static bool
2339 // member "is_blittable".
2340 template <typename _Ty>
2341 class is_blittable<_Ty, typename std::enable_if<_Ty::is_blittable>::type>
2343 { // determines whether _Ty is blittable
2347 //========================================================================
2348 // serialization::bin::Traits<T> enables binary serialization and
2349 // deserialization of instances of T.
2350 //========================================================================
2353 // General specialization for non-blittable types - must be overridden
2354 // for each specific non-blittable type.
2356 template <typename T, typename Enable = void>
2360 static FORCEINLINE size_t
2361 raw_size(const T & val)
2363 static_assert(false, "Non-blittable types need explicit specializations");
2368 // General type trait supporting serialization/deserialization of blittable
2369 // type arguments (as defined by the is_blittable<> type traits above).
2371 template <typename T>
2372 class Traits<T, typename std::enable_if<is_blittable<T>::value>::type>
2374 #else // FEATURE_PAL
2375 template <typename T>
2378 #endif // !FEATURE_PAL
2381 // raw_size() returns the size in bytes of the binary representation of a
2384 static FORCEINLINE size_t
2385 raw_size(const T & val)
2391 // raw_serialize() copies the binary representation of a value into a
2392 // "dest" buffer that has "destSize" bytes available.
2393 // Returns raw_size(val), or ErrOverflow if the buffer does not have
2394 // enough space to accommodate "val".
2396 static FORCEINLINE size_t
2397 raw_serialize(BYTE* dest, size_t destSize, const T & val)
2399 size_t cnt = raw_size(val);
2406 memcpy_s(dest, destSize, &val, cnt);
2412 // raw_deserialize() generates a value "val" from its binary
2413 // representation in a buffer "src".
2414 // Returns raw_size(val), or ErrOverflow if the buffer does not have
2415 // enough space to accommodate "val".
2417 static FORCEINLINE size_t
2418 raw_deserialize(T & val, const BYTE* src, size_t srcSize)
2420 size_t cnt = raw_size(*(T*)src);
2427 memcpy_s(&val, cnt, src, cnt);
2435 // Specialization for UTF8 strings
2438 class Traits<LPCUTF8>
2441 static FORCEINLINE size_t
2442 raw_size(const LPCUTF8 & val)
2444 return strlen(val) + 1;
2447 static FORCEINLINE size_t
2448 raw_serialize(BYTE* dest, size_t destSize, const LPCUTF8 & val)
2450 size_t cnt = raw_size(val);
2457 memcpy_s(dest, destSize, &val, cnt);
2462 static FORCEINLINE size_t
2463 raw_deserialize(LPCUTF8 & val, const BYTE* src, size_t srcSize)
2465 size_t cnt = strnlen((LPCUTF8)src, srcSize) + 1;
2467 // assert we found a NULL terminated string at "src"
2473 // we won't allocate another buffer for this string
2482 // Specialization for SString.
2483 // SString serialization/deserialization is performed to/from a UTF8
2487 class Traits<SString>
2490 static FORCEINLINE size_t
2491 raw_size(const SString & val)
2494 val.ConvertToUTF8(s);
2495 // make sure to include the NULL terminator
2496 return s.GetCount() + 1;
2499 static FORCEINLINE size_t
2500 raw_serialize(BYTE* dest, size_t destSize, const SString & val)
2502 // instead of calling raw_size() we inline it here, so we can reuse
2503 // the UTF8 string obtained below as an argument to memcpy.
2506 val.ConvertToUTF8(s);
2507 // make sure to include the NULL terminator
2508 size_t cnt = s.GetCount() + 1;
2515 memcpy_s(dest, destSize, s.GetUTF8NoConvert(), cnt);
2520 static FORCEINLINE size_t
2521 raw_deserialize(SString & val, const BYTE* src, size_t srcSize)
2523 size_t cnt = strnlen((LPCUTF8)src, srcSize) + 1;
2525 // assert we found a NULL terminated string at "src"
2531 // a literal SString avoids a new allocation + copy
2532 SString sUtf8(SString::Utf8Literal, (LPCUTF8) src);
2533 sUtf8.ConvertToUnicode(val);
2542 // Specialization for SString-derived classes (like SStrings)
2544 template<typename T>
2545 class Traits<T, typename std::enable_if<std::is_base_of<SString, T>::value>::type>
2546 : public Traits<SString>
2549 #endif // !FEATURE_PAL
2552 // Convenience functions to allow argument type deduction
2554 template <typename T> FORCEINLINE
2555 size_t raw_size(const T & val)
2556 { return Traits<T>::raw_size(val); }
2558 template <typename T> FORCEINLINE
2559 size_t raw_serialize(BYTE* dest, size_t destSize, const T & val)
2560 { return Traits<T>::raw_serialize(dest, destSize, val); }
2562 template <typename T> FORCEINLINE
2563 size_t raw_deserialize(T & val, const BYTE* src, size_t srcSize)
2564 { return Traits<T>::raw_deserialize(val, src, srcSize); }
2567 enum StreamBuffState
2571 sbsOOM = sbsUnrecoverable,
2575 // OStreamBuff - Manages writing to an output buffer
2580 OStreamBuff(BYTE * _buff, size_t _buffsize)
2581 : buffsize(_buffsize)
2587 template <typename T>
2588 OStreamBuff& operator << (const T & val)
2590 if (sbs >= sbsUnrecoverable)
2593 size_t cnt = raw_serialize(buff+crt, buffsize-crt, val);
2594 if (cnt == ErrOverflow)
2606 inline size_t GetPos() const
2611 inline BOOL operator!() const
2613 return sbs >= sbsUnrecoverable;
2616 inline StreamBuffState State() const
2622 size_t buffsize; // size of buffer
2623 BYTE* buff; // buffer to stream to
2624 size_t crt; // current offset in buffer
2625 StreamBuffState sbs; // current state
2630 // OStreamBuff - Manages reading from an input buffer
2635 IStreamBuff(const BYTE* _buff, size_t _buffsize)
2636 : buffsize(_buffsize)
2642 template <typename T>
2643 IStreamBuff& operator >> (T & val)
2645 if (sbs >= sbsUnrecoverable)
2648 size_t cnt = raw_deserialize(val, buff+crt, buffsize-crt);
2649 if (cnt == ErrOverflow)
2661 inline size_t GetPos() const
2666 inline BOOL operator!() const
2668 return sbs >= sbsUnrecoverable;
2671 inline StreamBuffState State() const
2677 size_t buffsize; // size of buffer
2678 const BYTE * buff; // buffer to read from
2679 size_t crt; // current offset in buffer
2680 StreamBuffState sbs; // current state
2685 using serialization::bin::StreamBuffState;
2686 using serialization::bin::IStreamBuff;
2687 using serialization::bin::OStreamBuff;
2690 // Callback function type used by DacStreamManager to coordinate
2691 // amount of available memory between multiple streamable data
2692 // structures (e.g. DacEENamesStreamable)
2693 typedef bool (*Reserve_Fnptr)(DWORD size, void * writeState);
2697 // DacEENamesStreamable
2698 // Stores EE struct* -> Name mappings and streams them to a
2699 // streambuf when asked
2701 class DacEENamesStreamable
2704 // the hash map storing the interesting mappings of EE* -> Names
2705 MapSHash< TADDR, SString,
2706 NoRemoveSHashTraits <
2707 NonDacAwareSHashTraits< MapSHashTraits <TADDR, SString> >
2710 Reserve_Fnptr m_reserveFn;
2714 // signature value in the header in stream
2715 static const DWORD sig = 0x614e4545; // "EENa" - EE Name
2720 DWORD sig; // 0x614e4545 == "EENa"
2721 DWORD cnt; // count of entries
2723 static const bool is_blittable = true;
2727 DacEENamesStreamable()
2729 , m_writeState(NULL)
2732 // Ensures the instance is ready for caching data and later writing
2733 // its map entries to an OStreamBuff.
2734 bool PrepareStreamForWriting(Reserve_Fnptr pfn, void * writeState)
2736 _ASSERTE(pfn != NULL && writeState != NULL);
2738 m_writeState = writeState;
2740 DWORD size = (DWORD) sizeof(StreamHeader);
2742 // notify owner to reserve space for a StreamHeader
2743 return m_reserveFn(size, m_writeState);
2746 // Adds a new mapping from an EE struct pointer (e.g. MethodDesc*) to
2748 bool AddEEName(TADDR taEE, const SString & eeName)
2750 _ASSERTE(m_reserveFn != NULL && m_writeState != NULL);
2752 // as a micro-optimization convert to Utf8 here as both raw_size and
2753 // raw_serialize are optimized for Utf8...
2754 StackSString seeName;
2755 eeName.ConvertToUTF8(seeName);
2757 DWORD size = (DWORD)(serialization::bin::raw_size(taEE) +
2758 serialization::bin::raw_size(seeName));
2760 // notify owner of the amount of space needed in the buffer
2761 if (m_reserveFn(size, m_writeState))
2763 // if there's still space cache the entry in m_hash
2764 m_hash.AddOrReplace(KeyValuePair<TADDR, SString>(taEE, seeName));
2773 // Finds an EE name from a target address of an EE struct (e.g.
2775 bool FindEEName(TADDR taEE, SString & eeName) const
2777 return m_hash.Lookup(taEE, &eeName) == TRUE;
2785 // Writes a header and the hash entries to an OStreamBuff
2786 HRESULT StreamTo(OStreamBuff &out) const
2790 hdr.cnt = (DWORD) m_hash.GetCount();
2794 auto end = m_hash.End();
2795 for (auto cur = m_hash.Begin(); end != cur; ++cur)
2797 out << cur->Key() << cur->Value();
2805 // Reads a header and the hash entries from an IStreamBuff
2806 HRESULT StreamFrom(IStreamBuff &in)
2811 in >> hdr; // in >> hdr.sig >> hdr.cnt;
2816 for (size_t i = 0; i < hdr.cnt; ++i)
2820 in >> taEE >> eeName;
2825 m_hash.AddOrReplace(KeyValuePair<TADDR, SString>(taEE, eeName));
2833 //================================================================================
2834 // This class enables two scenarios:
2835 // 1. When debugging a triage/mini-dump the class is initialized with a valid
2836 // buffer in taMiniMetaDataBuff. Afterwards one can call MdCacheGetEEName to
2837 // retrieve the name associated with a MethodDesc*.
2838 // 2. When generating a dump one must follow this sequence:
2839 // a. Initialize the DacStreamManager passing a valid (if the current
2840 // debugging target is a triage/mini-dump) or empty buffer (if the
2841 // current target is a live processa full or a heap dump)
2842 // b. Call PrepareStreamsForWriting() before starting enumerating any memory
2843 // c. Call MdCacheAddEEName() anytime we enumerate an EE structure of interest
2844 // d. Call EnumStreams() as the last action in the memory enumeration method.
2846 class DacStreamManager
2851 eNone, // the stream doesn't exist (target is a live process/full/heap dump)
2852 eRO, // the stream exists and we've read it (target is triage/mini-dump)
2853 eWO, // the stream doesn't exist but we're creating it
2854 // (e.g. to save a minidump from the current debugging session)
2855 eRW // the stream exists but we're generating another triage/mini-dump
2858 static const DWORD sig = 0x6d727473; // 'strm'
2860 struct StreamsHeader
2862 DWORD dwSig; // 0x6d727473 == "strm"
2863 DWORD dwTotalSize; // total size in bytes
2864 DWORD dwCntStreams; // number of streams (currently 1)
2866 static const bool is_blittable = true;
2869 DacStreamManager(TADDR miniMetaDataBuffAddress, DWORD miniMetaDataBuffSizeMax)
2870 : m_MiniMetaDataBuffAddress(miniMetaDataBuffAddress)
2871 , m_MiniMetaDataBuffSizeMax(miniMetaDataBuffSizeMax)
2875 , m_bStreamsRead(FALSE)
2883 if (m_rawBuffer != NULL)
2885 delete [] m_rawBuffer;
2889 bool PrepareStreamsForWriting()
2893 else if (m_rw == eRO)
2895 else if (m_rw == eRW)
2899 // this is a second invocation from a possibly live process
2900 // clean up the map since the callstacks/exceptions may be different
2904 // update available count based on the header and footer sizes
2905 if (m_MiniMetaDataBuffSizeMax < sizeof(StreamsHeader))
2908 m_cbAvailBuff = m_MiniMetaDataBuffSizeMax - sizeof(StreamsHeader);
2910 // update available count based on each stream's initial needs
2911 if (!m_EENames.PrepareStreamForWriting(&ReserveInBuffer, this))
2917 bool MdCacheAddEEName(TADDR taEEStruct, const SString& name)
2919 // don't cache unless we enabled "W"riting from a target that does not
2920 // already have a stream yet
2924 m_EENames.AddEEName(taEEStruct, name);
2928 HRESULT EnumStreams(IN CLRDataEnumMemoryFlags flags)
2930 _ASSERTE(flags == CLRDATA_ENUM_MEM_MINI || flags == CLRDATA_ENUM_MEM_TRIAGE);
2931 _ASSERTE(m_rw == eWO || m_rw == eRW);
2933 DWORD cbWritten = 0;
2937 // only dump the stream is it wasn't already present in the target
2938 DumpAllStreams(&cbWritten);
2942 cbWritten = m_MiniMetaDataBuffSizeMax;
2945 DacEnumMemoryRegion(m_MiniMetaDataBuffAddress, cbWritten, false);
2946 DacUpdateMemoryRegion(m_MiniMetaDataBuffAddress, cbWritten, m_rawBuffer);
2951 bool MdCacheGetEEName(TADDR taEEStruct, SString & eeName)
2953 if (!m_bStreamsRead)
2958 if (m_rw == eNone || m_rw == eWO)
2963 return m_EENames.FindEEName(taEEStruct, eeName);
2967 HRESULT Initialize()
2969 _ASSERTE(m_rw == eNone);
2970 _ASSERTE(m_rawBuffer == NULL);
2975 DacReadAll(dac_cast<TADDR>(m_MiniMetaDataBuffAddress),
2976 &hdr, sizeof(hdr), true);
2978 // when the DAC looks at a triage dump or minidump generated using
2979 // a "minimetadata" enabled DAC, buff will point to a serialized
2980 // representation of a methoddesc->method name hashmap.
2981 if (hdr.dwSig == sig)
2984 m_MiniMetaDataBuffSizeMax = hdr.dwTotalSize;
2988 // when the DAC initializes this for the case where the target is
2989 // (a) a live process, or (b) a full dump, buff will point to a
2990 // zero initialized memory region (allocated w/ VirtualAlloc)
2991 if (hdr.dwSig == 0 && hdr.dwTotalSize == 0 && hdr.dwCntStreams == 0)
2995 // otherwise we may have some memory corruption. treat this as
2996 // a liveprocess/full dump
3002 BYTE * buff = new BYTE[m_MiniMetaDataBuffSizeMax];
3003 DacReadAll(dac_cast<TADDR>(m_MiniMetaDataBuffAddress),
3004 buff, m_MiniMetaDataBuffSizeMax, true);
3011 HRESULT DumpAllStreams(DWORD * pcbWritten)
3013 _ASSERTE(m_rw == eWO);
3017 OStreamBuff out(m_rawBuffer, m_MiniMetaDataBuffSizeMax);
3022 hdr.dwTotalSize = m_MiniMetaDataBuffSizeMax-m_cbAvailBuff; // will update
3023 hdr.dwCntStreams = 1;
3027 // write MethodDesc->Method name map
3028 hr = m_EENames.StreamTo(out);
3030 // wrap up the buffer whether we ecountered an error or not
3031 size_t cbWritten = out.GetPos();
3032 cbWritten = ALIGN_UP(cbWritten, sizeof(size_t));
3034 // patch the dwTotalSize field blitted at the beginning of the buffer
3035 ((StreamsHeader*)m_rawBuffer)->dwTotalSize = (DWORD) cbWritten;
3038 *pcbWritten = (DWORD) cbWritten;
3043 HRESULT ReadAllStreams()
3045 _ASSERTE(!m_bStreamsRead);
3047 if (m_rw == eNone || m_rw == eWO)
3049 // no streams to read...
3050 m_bStreamsRead = TRUE;
3056 IStreamBuff in(m_rawBuffer, m_MiniMetaDataBuffSizeMax);
3061 _ASSERTE(hdr.dwSig == sig);
3062 _ASSERTE(hdr.dwCntStreams == 1);
3064 // read EE struct pointer -> EE name map
3066 hr = m_EENames.StreamFrom(in);
3068 m_bStreamsRead = TRUE;
3073 static bool ReserveInBuffer(DWORD size, void * writeState)
3075 DacStreamManager * pThis = reinterpret_cast<DacStreamManager*>(writeState);
3076 if (size > pThis->m_cbAvailBuff)
3082 pThis->m_cbAvailBuff -= size;
3088 TADDR m_MiniMetaDataBuffAddress; // TADDR of the buffer
3089 DWORD m_MiniMetaDataBuffSizeMax; // max size of buffer
3090 BYTE * m_rawBuffer; // inproc copy of buffer
3091 DWORD m_cbAvailBuff; // available bytes in buffer
3093 BOOL m_bStreamsRead;
3094 DacEENamesStreamable m_EENames;
3097 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3099 //----------------------------------------------------------------------------
3103 //----------------------------------------------------------------------------
3105 LONG ClrDataAccess::s_procInit;
3107 ClrDataAccess::ClrDataAccess(ICorDebugDataTarget * pTarget, ICLRDataTarget * pLegacyTarget/*=0*/)
3109 SUPPORTS_DAC_HOST_ONLY; // ctor does no marshalling - don't check with DacCop
3112 * Stash the various forms of the new ICorDebugDataTarget interface
3114 m_pTarget = pTarget;
3115 m_pTarget->AddRef();
3119 hr = m_pTarget->QueryInterface(__uuidof(ICorDebugMutableDataTarget),
3120 (void**)&m_pMutableTarget);
3124 // Create a target which always fails the write requests with CORDBG_E_TARGET_READONLY
3125 m_pMutableTarget = new ReadOnlyDataTargetFacade();
3126 m_pMutableTarget->AddRef();
3130 * If we have a legacy target, it means we're providing compatibility for code that used
3131 * the old ICLRDataTarget interfaces. There are still a few things (like metadata location,
3132 * GetImageBase, and VirtualAlloc) that the implementation may use which we haven't superseded
3133 * in ICorDebugDataTarget, so we still need access to the old target interfaces.
3134 * Any functionality that does exist in ICorDebugDataTarget is accessed from that interface
3135 * using the DataTargetAdapter on top of the legacy interface (to unify the calling code).
3136 * Eventually we may expose all functionality we need using ICorDebug (possibly a private
3137 * interface for things like VirtualAlloc), at which point we can stop using the legacy interfaces
3138 * completely (except in the DataTargetAdapter).
3140 m_pLegacyTarget = NULL;
3141 m_pLegacyTarget2 = NULL;
3142 m_pLegacyTarget3 = NULL;
3143 m_legacyMetaDataLocator = NULL;
3145 if (pLegacyTarget != NULL)
3147 m_pLegacyTarget = pLegacyTarget;
3149 m_pLegacyTarget->AddRef();
3151 m_pLegacyTarget->QueryInterface(__uuidof(ICLRDataTarget2), (void**)&m_pLegacyTarget2);
3153 m_pLegacyTarget->QueryInterface(__uuidof(ICLRDataTarget3), (void**)&m_pLegacyTarget3);
3155 if (pLegacyTarget->QueryInterface(__uuidof(ICLRMetadataLocator),
3156 (void**)&m_legacyMetaDataLocator) != S_OK)
3158 // The debugger doesn't implement IMetadataLocator. Use
3159 // IXCLRDataTarget3 if that exists. Otherwise we don't need it.
3160 pLegacyTarget->QueryInterface(__uuidof(IXCLRDataTarget3),
3161 (void**)&m_target3);
3168 m_debugMode = GetEnvironmentVariableA("MSCORDACWKS_DEBUG", NULL, 0) != 0;
3171 m_updateMemCb = NULL;
3172 m_enumMemFlags = (CLRDataEnumMemoryFlags)-1; // invalid
3173 m_jitNotificationTable = NULL;
3174 m_gcNotificationTable = NULL;
3176 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3178 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3180 // Target consistency checks are disabled by default.
3181 // See code:ClrDataAccess::SetTargetConsistencyChecks for details.
3182 m_fEnableTargetConsistencyAsserts = false;
3185 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgDACEnableAssert))
3187 m_fEnableTargetConsistencyAsserts = true;
3190 // Verification asserts are disabled by default because some debuggers (cdb/windbg) probe likely locations
3191 // for DAC and having this assert pop up all the time can be annoying. We let derived classes enable
3192 // this if they want. It can also be overridden at run-time with COMPLUS_DbgDACAssertOnMismatch,
3193 // see ClrDataAccess::VerifyDlls for details.
3194 m_fEnableDllVerificationAsserts = false;
3199 ClrDataAccess::~ClrDataAccess(void)
3201 SUPPORTS_DAC_HOST_ONLY;
3203 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3208 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3210 delete [] m_jitNotificationTable;
3211 if (m_pLegacyTarget)
3213 m_pLegacyTarget->Release();
3215 if (m_pLegacyTarget2)
3217 m_pLegacyTarget2->Release();
3219 if (m_pLegacyTarget3)
3221 m_pLegacyTarget3->Release();
3223 if (m_legacyMetaDataLocator)
3225 m_legacyMetaDataLocator->Release();
3229 m_target3->Release();
3231 m_pTarget->Release();
3232 m_pMutableTarget->Release();
3236 ClrDataAccess::QueryInterface(THIS_
3237 IN REFIID interfaceId,
3242 if (IsEqualIID(interfaceId, IID_IUnknown) ||
3243 IsEqualIID(interfaceId, __uuidof(IXCLRDataProcess)) ||
3244 IsEqualIID(interfaceId, __uuidof(IXCLRDataProcess2)))
3246 ifaceRet = static_cast<IXCLRDataProcess2*>(this);
3248 else if (IsEqualIID(interfaceId, __uuidof(ICLRDataEnumMemoryRegions)))
3250 ifaceRet = static_cast<ICLRDataEnumMemoryRegions*>(this);
3252 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface)))
3254 ifaceRet = static_cast<ISOSDacInterface*>(this);
3256 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface2)))
3258 ifaceRet = static_cast<ISOSDacInterface2*>(this);
3263 return E_NOINTERFACE;
3271 STDMETHODIMP_(ULONG)
3272 ClrDataAccess::AddRef(THIS)
3274 return InterlockedIncrement(&m_refs);
3277 STDMETHODIMP_(ULONG)
3278 ClrDataAccess::Release(THIS)
3280 SUPPORTS_DAC_HOST_ONLY;
3281 LONG newRefs = InterlockedDecrement(&m_refs);
3289 HRESULT STDMETHODCALLTYPE
3290 ClrDataAccess::Flush(void)
3292 SUPPORTS_DAC_HOST_ONLY;
3295 // Free MD import objects.
3297 m_mdImports.Flush();
3299 // Free instance memory.
3300 m_instances.Flush();
3302 // When the host instance cache is flushed we
3303 // update the instance age count so that
3304 // all child objects automatically become
3305 // invalid. This prevents them from using
3306 // any pointers they've kept to host instances
3307 // which are now gone.
3313 HRESULT STDMETHODCALLTYPE
3314 ClrDataAccess::StartEnumTasks(
3315 /* [out] */ CLRDATA_ENUM* handle)
3323 if (ThreadStore::s_pThreadStore)
3325 Thread* thread = ThreadStore::GetAllThreadList(NULL, 0, 0);
3326 *handle = TO_CDENUM(thread);
3327 status = *handle ? S_OK : S_FALSE;
3336 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3341 EX_END_CATCH(SwallowAllExceptions)
3347 HRESULT STDMETHODCALLTYPE
3348 ClrDataAccess::EnumTask(
3349 /* [in, out] */ CLRDATA_ENUM* handle,
3350 /* [out] */ IXCLRDataTask **task)
3360 Thread* thread = FROM_CDENUM(Thread, *handle);
3361 *task = new (nothrow) ClrDataTask(this, thread);
3364 thread = ThreadStore::GetAllThreadList(thread, 0, 0);
3365 *handle = TO_CDENUM(thread);
3370 status = E_OUTOFMEMORY;
3380 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3385 EX_END_CATCH(SwallowAllExceptions)
3391 HRESULT STDMETHODCALLTYPE
3392 ClrDataAccess::EndEnumTasks(
3393 /* [in] */ CLRDATA_ENUM handle)
3401 // Enumerator holds no resources.
3406 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3411 EX_END_CATCH(SwallowAllExceptions)
3417 HRESULT STDMETHODCALLTYPE
3418 ClrDataAccess::GetTaskByOSThreadID(
3419 /* [in] */ ULONG32 osThreadID,
3420 /* [out] */ IXCLRDataTask **task)
3428 status = E_INVALIDARG;
3429 Thread* thread = DacGetThread(osThreadID);
3432 *task = new (nothrow) ClrDataTask(this, thread);
3433 status = *task ? S_OK : E_OUTOFMEMORY;
3438 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3443 EX_END_CATCH(SwallowAllExceptions)
3449 HRESULT STDMETHODCALLTYPE
3450 ClrDataAccess::GetTaskByUniqueID(
3451 /* [in] */ ULONG64 uniqueID,
3452 /* [out] */ IXCLRDataTask **task)
3460 Thread* thread = FindClrThreadByTaskId(uniqueID);
3463 *task = new (nothrow) ClrDataTask(this, thread);
3464 status = *task ? S_OK : E_OUTOFMEMORY;
3468 status = E_INVALIDARG;
3473 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3478 EX_END_CATCH(SwallowAllExceptions)
3484 HRESULT STDMETHODCALLTYPE
3485 ClrDataAccess::GetFlags(
3486 /* [out] */ ULONG32 *flags)
3494 // XXX Microsoft - GC check.
3495 *flags = CLRDATA_PROCESS_DEFAULT;
3500 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3505 EX_END_CATCH(SwallowAllExceptions)
3511 HRESULT STDMETHODCALLTYPE
3512 ClrDataAccess::IsSameObject(
3513 /* [in] */ IXCLRDataProcess* process)
3521 status = m_pTarget == ((ClrDataAccess*)process)->m_pTarget ?
3526 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3531 EX_END_CATCH(SwallowAllExceptions)
3537 HRESULT STDMETHODCALLTYPE
3538 ClrDataAccess::GetManagedObject(
3539 /* [out] */ IXCLRDataValue **value)
3552 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3557 EX_END_CATCH(SwallowAllExceptions)
3563 HRESULT STDMETHODCALLTYPE
3564 ClrDataAccess::GetDesiredExecutionState(
3565 /* [out] */ ULONG32 *state)
3578 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3583 EX_END_CATCH(SwallowAllExceptions)
3589 HRESULT STDMETHODCALLTYPE
3590 ClrDataAccess::SetDesiredExecutionState(
3591 /* [in] */ ULONG32 state)
3604 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3609 EX_END_CATCH(SwallowAllExceptions)
3615 HRESULT STDMETHODCALLTYPE
3616 ClrDataAccess::GetAddressType(
3617 /* [in] */ CLRDATA_ADDRESS address,
3618 /* [out] */ CLRDataAddressType* type)
3626 // The only thing that constitutes a failure is some
3627 // dac failure while checking things.
3629 TADDR taAddr = CLRDATA_ADDRESS_TO_TADDR(address);
3630 if (IsPossibleCodeAddress(taAddr) == S_OK)
3632 if (ExecutionManager::IsManagedCode(taAddr))
3634 *type = CLRDATA_ADDRESS_MANAGED_METHOD;
3638 if (StubManager::IsStub(taAddr))
3640 *type = CLRDATA_ADDRESS_RUNTIME_UNMANAGED_STUB;
3645 *type = CLRDATA_ADDRESS_UNRECOGNIZED;
3651 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3656 EX_END_CATCH(SwallowAllExceptions)
3662 HRESULT STDMETHODCALLTYPE
3663 ClrDataAccess::GetRuntimeNameByAddress(
3664 /* [in] */ CLRDATA_ADDRESS address,
3665 /* [in] */ ULONG32 flags,
3666 /* [in] */ ULONG32 bufLen,
3667 /* [out] */ ULONG32 *symbolLen,
3668 /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR symbolBuf[ ],
3669 /* [out] */ CLRDATA_ADDRESS* displacement)
3678 address &= ~THUMB_CODE; //workaround for windbg passing in addresses with the THUMB mode bit set
3680 status = RawGetMethodName(address, flags, bufLen, symbolLen, symbolBuf,
3685 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3690 EX_END_CATCH(SwallowAllExceptions)
3696 HRESULT STDMETHODCALLTYPE
3697 ClrDataAccess::StartEnumAppDomains(
3698 /* [out] */ CLRDATA_ENUM* handle)
3706 AppDomainIterator* iter = new (nothrow) AppDomainIterator(FALSE);
3709 *handle = TO_CDENUM(iter);
3714 status = E_OUTOFMEMORY;
3719 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3724 EX_END_CATCH(SwallowAllExceptions)
3730 HRESULT STDMETHODCALLTYPE
3731 ClrDataAccess::EnumAppDomain(
3732 /* [in, out] */ CLRDATA_ENUM* handle,
3733 /* [out] */ IXCLRDataAppDomain **appDomain)
3741 AppDomainIterator* iter = FROM_CDENUM(AppDomainIterator, *handle);
3744 *appDomain = new (nothrow)
3745 ClrDataAppDomain(this, iter->GetDomain());
3746 status = *appDomain ? S_OK : E_OUTOFMEMORY;
3755 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3760 EX_END_CATCH(SwallowAllExceptions)
3766 HRESULT STDMETHODCALLTYPE
3767 ClrDataAccess::EndEnumAppDomains(
3768 /* [in] */ CLRDATA_ENUM handle)
3776 AppDomainIterator* iter = FROM_CDENUM(AppDomainIterator, handle);
3782 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3787 EX_END_CATCH(SwallowAllExceptions)
3793 HRESULT STDMETHODCALLTYPE
3794 ClrDataAccess::GetAppDomainByUniqueID(
3795 /* [in] */ ULONG64 uniqueID,
3796 /* [out] */ IXCLRDataAppDomain **appDomain)
3804 AppDomainIterator iter(FALSE);
3806 status = E_INVALIDARG;
3809 if (iter.GetDomain()->GetId().m_dwId == uniqueID)
3811 *appDomain = new (nothrow)
3812 ClrDataAppDomain(this, iter.GetDomain());
3813 status = *appDomain ? S_OK : E_OUTOFMEMORY;
3820 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3825 EX_END_CATCH(SwallowAllExceptions)
3831 HRESULT STDMETHODCALLTYPE
3832 ClrDataAccess::StartEnumAssemblies(
3833 /* [out] */ CLRDATA_ENUM* handle)
3841 ProcessModIter* iter = new (nothrow) ProcessModIter;
3844 *handle = TO_CDENUM(iter);
3849 status = E_OUTOFMEMORY;
3854 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3859 EX_END_CATCH(SwallowAllExceptions)
3865 HRESULT STDMETHODCALLTYPE
3866 ClrDataAccess::EnumAssembly(
3867 /* [in, out] */ CLRDATA_ENUM* handle,
3868 /* [out] */ IXCLRDataAssembly **assembly)
3876 ProcessModIter* iter = FROM_CDENUM(ProcessModIter, *handle);
3879 if ((assem = iter->NextAssem()))
3881 *assembly = new (nothrow)
3882 ClrDataAssembly(this, assem);
3883 status = *assembly ? S_OK : E_OUTOFMEMORY;
3892 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3897 EX_END_CATCH(SwallowAllExceptions)
3903 HRESULT STDMETHODCALLTYPE
3904 ClrDataAccess::EndEnumAssemblies(
3905 /* [in] */ CLRDATA_ENUM handle)
3913 ProcessModIter* iter = FROM_CDENUM(ProcessModIter, handle);
3919 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3924 EX_END_CATCH(SwallowAllExceptions)
3930 HRESULT STDMETHODCALLTYPE
3931 ClrDataAccess::StartEnumModules(
3932 /* [out] */ CLRDATA_ENUM* handle)
3940 ProcessModIter* iter = new (nothrow) ProcessModIter;
3943 *handle = TO_CDENUM(iter);
3948 status = E_OUTOFMEMORY;
3953 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3958 EX_END_CATCH(SwallowAllExceptions)
3964 HRESULT STDMETHODCALLTYPE
3965 ClrDataAccess::EnumModule(
3966 /* [in, out] */ CLRDATA_ENUM* handle,
3967 /* [out] */ IXCLRDataModule **mod)
3975 ProcessModIter* iter = FROM_CDENUM(ProcessModIter, *handle);
3978 if ((curMod = iter->NextModule()))
3980 *mod = new (nothrow)
3981 ClrDataModule(this, curMod);
3982 status = *mod ? S_OK : E_OUTOFMEMORY;
3991 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3996 EX_END_CATCH(SwallowAllExceptions)
4002 HRESULT STDMETHODCALLTYPE
4003 ClrDataAccess::EndEnumModules(
4004 /* [in] */ CLRDATA_ENUM handle)
4012 ProcessModIter* iter = FROM_CDENUM(ProcessModIter, handle);
4018 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4023 EX_END_CATCH(SwallowAllExceptions)
4029 HRESULT STDMETHODCALLTYPE
4030 ClrDataAccess::GetModuleByAddress(
4031 /* [in] */ CLRDATA_ADDRESS address,
4032 /* [out] */ IXCLRDataModule** mod)
4040 ProcessModIter modIter;
4043 while ((modDef = modIter.NextModule()))
4047 PEFile* file = modDef->GetFile();
4049 if ((base = PTR_TO_TADDR(file->GetLoadedImageContents(&length))))
4051 if (TO_CDADDR(base) <= address &&
4052 TO_CDADDR(base + length) > address)
4057 if (file->HasNativeImage())
4059 base = PTR_TO_TADDR(file->GetLoadedNative()->GetBase());
4060 length = file->GetLoadedNative()->GetVirtualSize();
4061 if (TO_CDADDR(base) <= address &&
4062 TO_CDADDR(base + length) > address)
4071 *mod = new (nothrow)
4072 ClrDataModule(this, modDef);
4073 status = *mod ? S_OK : E_OUTOFMEMORY;
4082 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4087 EX_END_CATCH(SwallowAllExceptions)
4093 HRESULT STDMETHODCALLTYPE
4094 ClrDataAccess::StartEnumMethodDefinitionsByAddress(
4095 /* [in] */ CLRDATA_ADDRESS address,
4096 /* [out] */ CLRDATA_ENUM *handle)
4104 ProcessModIter modIter;
4107 while ((modDef = modIter.NextModule()))
4111 PEFile* file = modDef->GetFile();
4113 if ((base = PTR_TO_TADDR(file->GetLoadedImageContents(&length))))
4115 if (TO_CDADDR(base) <= address &&
4116 TO_CDADDR(base + length) > address)
4123 status = EnumMethodDefinitions::
4124 CdStart(modDef, true, address, handle);
4128 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4133 EX_END_CATCH(SwallowAllExceptions)
4139 HRESULT STDMETHODCALLTYPE
4140 ClrDataAccess::EnumMethodDefinitionByAddress(
4141 /* [out][in] */ CLRDATA_ENUM* handle,
4142 /* [out] */ IXCLRDataMethodDefinition **method)
4150 status = EnumMethodDefinitions::CdNext(this, handle, method);
4154 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4159 EX_END_CATCH(SwallowAllExceptions)
4165 HRESULT STDMETHODCALLTYPE
4166 ClrDataAccess::EndEnumMethodDefinitionsByAddress(
4167 /* [in] */ CLRDATA_ENUM handle)
4175 status = EnumMethodDefinitions::CdEnd(handle);
4179 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4184 EX_END_CATCH(SwallowAllExceptions)
4190 HRESULT STDMETHODCALLTYPE
4191 ClrDataAccess::StartEnumMethodInstancesByAddress(
4192 /* [in] */ CLRDATA_ADDRESS address,
4193 /* [in] */ IXCLRDataAppDomain* appDomain,
4194 /* [out] */ CLRDATA_ENUM *handle)
4202 MethodDesc* methodDesc;
4207 if( (status = TRY_CLRDATA_ADDRESS_TO_TADDR(address, &taddr)) != S_OK )
4212 if (IsPossibleCodeAddress(taddr) != S_OK)
4217 methodDesc = ExecutionManager::GetCodeMethodDesc(taddr);
4223 status = EnumMethodInstances::CdStart(methodDesc, appDomain,
4230 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4235 EX_END_CATCH(SwallowAllExceptions)
4241 HRESULT STDMETHODCALLTYPE
4242 ClrDataAccess::EnumMethodInstanceByAddress(
4243 /* [out][in] */ CLRDATA_ENUM* handle,
4244 /* [out] */ IXCLRDataMethodInstance **method)
4252 status = EnumMethodInstances::CdNext(this, handle, method);
4256 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4261 EX_END_CATCH(SwallowAllExceptions)
4267 HRESULT STDMETHODCALLTYPE
4268 ClrDataAccess::EndEnumMethodInstancesByAddress(
4269 /* [in] */ CLRDATA_ENUM handle)
4277 status = EnumMethodInstances::CdEnd(handle);
4281 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4286 EX_END_CATCH(SwallowAllExceptions)
4292 HRESULT STDMETHODCALLTYPE
4293 ClrDataAccess::GetDataByAddress(
4294 /* [in] */ CLRDATA_ADDRESS address,
4295 /* [in] */ ULONG32 flags,
4296 /* [in] */ IXCLRDataAppDomain* appDomain,
4297 /* [in] */ IXCLRDataTask* tlsTask,
4298 /* [in] */ ULONG32 bufLen,
4299 /* [out] */ ULONG32 *nameLen,
4300 /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ],
4301 /* [out] */ IXCLRDataValue **value,
4302 /* [out] */ CLRDATA_ADDRESS *displacement)
4308 return E_INVALIDARG;
4320 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4325 EX_END_CATCH(SwallowAllExceptions)
4331 HRESULT STDMETHODCALLTYPE
4332 ClrDataAccess::GetExceptionStateByExceptionRecord(
4333 /* [in] */ EXCEPTION_RECORD64 *record,
4334 /* [out] */ IXCLRDataExceptionState **exception)
4347 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4352 EX_END_CATCH(SwallowAllExceptions)
4358 HRESULT STDMETHODCALLTYPE
4359 ClrDataAccess::TranslateExceptionRecordToNotification(
4360 /* [in] */ EXCEPTION_RECORD64 *record,
4361 /* [in] */ IXCLRDataExceptionNotification *notify)
4363 HRESULT status = E_FAIL;
4364 ClrDataModule* pubModule = NULL;
4365 ClrDataMethodInstance* pubMethodInst = NULL;
4366 ClrDataExceptionState* pubExState = NULL;
4367 GcEvtArgs pubGcEvtArgs;
4368 ULONG32 notifyType = 0;
4369 DWORD catcherNativeOffset = 0;
4376 // We cannot hold the dac lock while calling
4377 // out as the external code can do arbitrary things.
4378 // Instead we make a pass over the exception
4379 // information and create all necessary objects.
4380 // We then leave the lock and make the callbac.
4383 TADDR exInfo[EXCEPTION_MAXIMUM_PARAMETERS];
4384 for (UINT i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
4386 exInfo[i] = TO_TADDR(record->ExceptionInformation[i]);
4389 notifyType = DACNotify::GetType(exInfo);
4392 case DACNotify::MODULE_LOAD_NOTIFICATION:
4396 if (DACNotify::ParseModuleLoadNotification(exInfo, modulePtr))
4398 Module* clrModule = PTR_Module(modulePtr);
4399 pubModule = new (nothrow) ClrDataModule(this, clrModule);
4400 if (pubModule == NULL)
4402 status = E_OUTOFMEMORY;
4412 case DACNotify::MODULE_UNLOAD_NOTIFICATION:
4416 if (DACNotify::ParseModuleUnloadNotification(exInfo, modulePtr))
4418 Module* clrModule = PTR_Module(modulePtr);
4419 pubModule = new (nothrow) ClrDataModule(this, clrModule);
4420 if (pubModule == NULL)
4422 status = E_OUTOFMEMORY;
4432 case DACNotify::JIT_NOTIFICATION:
4434 TADDR methodDescPtr;
4436 if (DACNotify::ParseJITNotification(exInfo, methodDescPtr))
4438 // Try and find the right appdomain
4439 MethodDesc* methodDesc = PTR_MethodDesc(methodDescPtr);
4440 BaseDomain* baseDomain = methodDesc->GetDomain();
4441 AppDomain* appDomain = NULL;
4443 if (baseDomain->IsAppDomain())
4445 appDomain = PTR_AppDomain(PTR_HOST_TO_TADDR(baseDomain));
4449 // Find a likely domain, because it's the shared domain.
4450 AppDomainIterator adi(FALSE);
4451 appDomain = adi.GetDomain();
4455 new (nothrow) ClrDataMethodInstance(this,
4458 if (pubMethodInst == NULL)
4460 status = E_OUTOFMEMORY;
4470 case DACNotify::EXCEPTION_NOTIFICATION:
4474 if (DACNotify::ParseExceptionNotification(exInfo, threadPtr))
4476 // Translation can only occur at the time of
4477 // receipt of the notify exception, so we assume
4478 // that the Thread's current exception state
4479 // is the state we want.
4480 status = ClrDataExceptionState::
4482 PTR_Thread(threadPtr),
4489 case DACNotify::GC_NOTIFICATION:
4491 if (DACNotify::ParseGCNotification(exInfo, pubGcEvtArgs))
4498 case DACNotify::CATCH_ENTER_NOTIFICATION:
4500 TADDR methodDescPtr;
4501 if (DACNotify::ParseExceptionCatcherEnterNotification(exInfo, methodDescPtr, catcherNativeOffset))
4503 // Try and find the right appdomain
4504 MethodDesc* methodDesc = PTR_MethodDesc(methodDescPtr);
4505 BaseDomain* baseDomain = methodDesc->GetDomain();
4506 AppDomain* appDomain = NULL;
4508 if (baseDomain->IsAppDomain())
4510 appDomain = PTR_AppDomain(PTR_HOST_TO_TADDR(baseDomain));
4514 // Find a likely domain, because it's the shared domain.
4515 AppDomainIterator adi(FALSE);
4516 appDomain = adi.GetDomain();
4520 new (nothrow) ClrDataMethodInstance(this,
4523 if (pubMethodInst == NULL)
4525 status = E_OUTOFMEMORY;
4536 status = E_INVALIDARG;
4542 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4547 EX_END_CATCH(SwallowAllExceptions)
4553 IXCLRDataExceptionNotification2* notify2;
4555 if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification2),
4556 (void**)¬ify2) != S_OK)
4561 IXCLRDataExceptionNotification3* notify3;
4562 if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification3),
4563 (void**)¬ify3) != S_OK)
4568 IXCLRDataExceptionNotification4* notify4;
4569 if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification4),
4570 (void**)¬ify4) != S_OK)
4577 case DACNotify::MODULE_LOAD_NOTIFICATION:
4578 notify->OnModuleLoaded(pubModule);
4581 case DACNotify::MODULE_UNLOAD_NOTIFICATION:
4582 notify->OnModuleUnloaded(pubModule);
4585 case DACNotify::JIT_NOTIFICATION:
4586 notify->OnCodeGenerated(pubMethodInst);
4589 case DACNotify::EXCEPTION_NOTIFICATION:
4592 notify2->OnException(pubExState);
4596 status = E_INVALIDARG;
4600 case DACNotify::GC_NOTIFICATION:
4603 notify3->OnGcEvent(pubGcEvtArgs);
4607 case DACNotify::CATCH_ENTER_NOTIFICATION:
4610 notify4->ExceptionCatcherEnter(pubMethodInst, catcherNativeOffset);
4615 // notifyType has already been validated.
4632 pubModule->Release();
4636 pubMethodInst->Release();
4640 pubExState->Release();
4646 HRESULT STDMETHODCALLTYPE
4647 ClrDataAccess::CreateMemoryValue(
4648 /* [in] */ IXCLRDataAppDomain* appDomain,
4649 /* [in] */ IXCLRDataTask* tlsTask,
4650 /* [in] */ IXCLRDataTypeInstance* type,
4651 /* [in] */ CLRDATA_ADDRESS addr,
4652 /* [out] */ IXCLRDataValue** value)
4660 AppDomain* dacDomain;
4664 NativeVarLocation loc;
4666 dacDomain = ((ClrDataAppDomain*)appDomain)->GetAppDomain();
4669 dacThread = ((ClrDataTask*)tlsTask)->GetThread();
4675 dacType = ((ClrDataTypeInstance*)type)->GetTypeHandle();
4677 flags = GetTypeFieldValueFlags(dacType, NULL, 0, false);
4680 loc.size = dacType.GetSize();
4681 loc.contextReg = false;
4683 *value = new (nothrow)
4684 ClrDataValue(this, dacDomain, dacThread, flags,
4685 dacType, addr, 1, &loc);
4686 status = *value ? S_OK : E_OUTOFMEMORY;
4690 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4695 EX_END_CATCH(SwallowAllExceptions)
4701 HRESULT STDMETHODCALLTYPE
4702 ClrDataAccess::SetAllTypeNotifications(
4703 /* [in] */ IXCLRDataModule* mod,
4704 /* [in] */ ULONG32 flags)
4717 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4722 EX_END_CATCH(SwallowAllExceptions)
4728 HRESULT STDMETHODCALLTYPE
4729 ClrDataAccess::SetAllCodeNotifications(
4730 /* [in] */ IXCLRDataModule* mod,
4731 /* [in] */ ULONG32 flags)
4741 if (!IsValidMethodCodeNotification(flags))
4743 status = E_INVALIDARG;
4747 JITNotifications jn(GetHostJitNotificationTable());
4750 status = E_OUTOFMEMORY;
4755 TADDR modulePtr = mod ?
4756 PTR_HOST_TO_TADDR(((ClrDataModule*)mod)->GetModule()) :
4759 if (jn.SetAllNotifications(modulePtr, flags, &changedTable))
4761 if (!changedTable ||
4762 (changedTable && jn.UpdateOutOfProcTable()))
4772 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4777 EX_END_CATCH(SwallowAllExceptions)
4783 HRESULT STDMETHODCALLTYPE
4784 ClrDataAccess::GetTypeNotifications(
4785 /* [in] */ ULONG32 numTokens,
4786 /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[],
4787 /* [in] */ IXCLRDataModule* singleMod,
4788 /* [in, size_is(numTokens)] */ mdTypeDef tokens[],
4789 /* [out, size_is(numTokens)] */ ULONG32 flags[])
4802 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4807 EX_END_CATCH(SwallowAllExceptions)
4813 HRESULT STDMETHODCALLTYPE
4814 ClrDataAccess::SetTypeNotifications(
4815 /* [in] */ ULONG32 numTokens,
4816 /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[],
4817 /* [in] */ IXCLRDataModule* singleMod,
4818 /* [in, size_is(numTokens)] */ mdTypeDef tokens[],
4819 /* [in, size_is(numTokens)] */ ULONG32 flags[],
4820 /* [in] */ ULONG32 singleFlags)
4833 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4838 EX_END_CATCH(SwallowAllExceptions)
4844 HRESULT STDMETHODCALLTYPE
4845 ClrDataAccess::GetCodeNotifications(
4846 /* [in] */ ULONG32 numTokens,
4847 /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[],
4848 /* [in] */ IXCLRDataModule* singleMod,
4849 /* [in, size_is(numTokens)] */ mdMethodDef tokens[],
4850 /* [out, size_is(numTokens)] */ ULONG32 flags[])
4858 if ((flags == NULL || tokens == NULL) ||
4859 (mods == NULL && singleMod == NULL) ||
4860 (mods != NULL && singleMod != NULL))
4862 status = E_INVALIDARG;
4866 JITNotifications jn(GetHostJitNotificationTable());
4869 status = E_OUTOFMEMORY;
4873 TADDR modulePtr = NULL;
4876 modulePtr = PTR_HOST_TO_TADDR(((ClrDataModule*)singleMod)->
4880 for (ULONG32 i = 0; i < numTokens; i++)
4882 if (singleMod == NULL)
4885 PTR_HOST_TO_TADDR(((ClrDataModule*)mods[i])->
4888 USHORT jt = jn.Requested(modulePtr, tokens[i]);
4898 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4903 EX_END_CATCH(SwallowAllExceptions)
4909 HRESULT STDMETHODCALLTYPE
4910 ClrDataAccess::SetCodeNotifications(
4911 /* [in] */ ULONG32 numTokens,
4912 /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[],
4913 /* [in] */ IXCLRDataModule* singleMod,
4914 /* [in, size_is(numTokens)] */ mdMethodDef tokens[],
4915 /* [in, size_is(numTokens)] */ ULONG32 flags[],
4916 /* [in] */ ULONG32 singleFlags)
4918 HRESULT status = E_UNEXPECTED;
4924 if ((tokens == NULL) ||
4925 (mods == NULL && singleMod == NULL) ||
4926 (mods != NULL && singleMod != NULL))
4928 status = E_INVALIDARG;
4932 JITNotifications jn(GetHostJitNotificationTable());
4933 if (!jn.IsActive() || numTokens > jn.GetTableSize())
4935 status = E_OUTOFMEMORY;
4939 BOOL changedTable = FALSE;
4944 for (ULONG32 check = 0; check < numTokens; check++)
4946 if (!IsValidMethodCodeNotification(flags[check]))
4948 status = E_INVALIDARG;
4953 else if (!IsValidMethodCodeNotification(singleFlags))
4955 status = E_INVALIDARG;
4959 TADDR modulePtr = NULL;
4963 PTR_HOST_TO_TADDR(((ClrDataModule*)singleMod)->
4967 for (ULONG32 i = 0; i < numTokens; i++)
4969 if (singleMod == NULL)
4972 PTR_HOST_TO_TADDR(((ClrDataModule*)mods[i])->
4976 USHORT curFlags = jn.Requested(modulePtr, tokens[i]);
4977 USHORT setFlags = (USHORT)(flags ? flags[i] : singleFlags);
4979 if (curFlags != setFlags)
4981 if (!jn.SetNotification(modulePtr, tokens[i],
4988 changedTable = TRUE;
4992 if (!changedTable ||
4993 (changedTable && jn.UpdateOutOfProcTable()))
5004 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5009 EX_END_CATCH(SwallowAllExceptions)
5016 ClrDataAccess::GetOtherNotificationFlags(
5017 /* [out] */ ULONG32* flags)
5025 *flags = g_dacNotificationFlags;
5030 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5035 EX_END_CATCH(SwallowAllExceptions)
5042 ClrDataAccess::SetOtherNotificationFlags(
5043 /* [in] */ ULONG32 flags)
5047 if ((flags & ~(CLRDATA_NOTIFY_ON_MODULE_LOAD |
5048 CLRDATA_NOTIFY_ON_MODULE_UNLOAD |
5049 CLRDATA_NOTIFY_ON_EXCEPTION |
5050 CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER)) != 0)
5052 return E_INVALIDARG;
5059 g_dacNotificationFlags = flags;
5064 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5069 EX_END_CATCH(SwallowAllExceptions)
5077 STUB_BUF_FLAGS_START,
5079 STUB_BUF_METHOD_JITTED,
5080 STUB_BUF_FRAME_PUSHED,
5081 STUB_BUF_STUB_MANAGER_PUSHED,
5088 CLRDATA_FOLLOW_STUB_BUFFER apiBuf;
5098 ClrDataAccess::FollowStubStep(
5099 /* [in] */ Thread* thread,
5100 /* [in] */ ULONG32 inFlags,
5101 /* [in] */ TADDR inAddr,
5102 /* [in] */ union STUB_BUF* inBuffer,
5103 /* [out] */ TADDR* outAddr,
5104 /* [out] */ union STUB_BUF* outBuffer,
5105 /* [out] */ ULONG32* outFlags)
5107 TraceDestination trace;
5108 bool traceDone = false;
5110 T_CONTEXT localContext;
5112 MethodDesc* methodDesc;
5114 ZeroMemory(outBuffer, sizeof(*outBuffer));
5118 switch(inBuffer->u.flags)
5120 case STUB_BUF_METHOD_JITTED:
5121 if (inAddr != GFN_TADDR(DACNotifyCompilationFinished))
5123 return E_INVALIDARG;
5126 // It's possible that this notification is
5127 // for a different method, so double-check
5128 // and recycle the notification if necessary.
5129 methodDesc = PTR_MethodDesc(CORDB_ADDRESS_TO_TADDR(inBuffer->u.addr));
5130 if (methodDesc->HasNativeCode())
5132 *outAddr = methodDesc->GetNativeCode();
5133 *outFlags = CLRDATA_FOLLOW_STUB_EXIT;
5137 // We didn't end up with native code so try again.
5138 trace.InitForUnjittedMethod(methodDesc);
5142 case STUB_BUF_FRAME_PUSHED:
5144 inAddr != inBuffer->u.addr)
5146 return E_INVALIDARG;
5149 trace.InitForFramePush(CORDB_ADDRESS_TO_TADDR(inBuffer->u.addr));
5150 DacGetThreadContext(thread, &localContext);
5151 thread->FillRegDisplay(®Disp, &localContext);
5152 if (!thread->GetFrame()->
5164 case STUB_BUF_STUB_MANAGER_PUSHED:
5166 inAddr != inBuffer->u.addr ||
5169 return E_INVALIDARG;
5172 trace.InitForManagerPush(CORDB_ADDRESS_TO_TADDR(inBuffer->u.addr),
5173 PTR_StubManager(CORDB_ADDRESS_TO_TADDR(inBuffer->u.arg1)));
5174 DacGetThreadContext(thread, &localContext);
5175 if (!trace.GetStubManager()->
5176 TraceManager(thread,
5188 return E_INVALIDARG;
5193 !StubManager::TraceStub(inAddr, &trace)) ||
5194 !StubManager::FollowTrace(&trace))
5196 return E_NOINTERFACE;
5199 switch(trace.GetTraceType())
5201 case TRACE_UNMANAGED:
5203 // We've hit non-stub code so we're done.
5204 *outAddr = trace.GetAddress();
5205 *outFlags = CLRDATA_FOLLOW_STUB_EXIT;
5208 case TRACE_UNJITTED_METHOD:
5209 // The stub causes jitting, so return
5210 // the address of the jit-complete routine
5211 // so that the real native address can
5212 // be picked up once the JIT is done.
5214 // One special case is ngen'ed code that
5215 // needs the prestub run. This results in
5216 // an unjitted trace but no jitting will actually
5217 // occur since the code is ngen'ed. Detect
5218 // this and redirect to the actual code.
5219 methodDesc = trace.GetMethodDesc();
5220 if (methodDesc->IsPreImplemented() &&
5221 !methodDesc->IsPointingToNativeCode() &&
5222 !methodDesc->IsGenericMethodDefinition() &&
5223 methodDesc->HasNativeCode())
5225 *outAddr = methodDesc->GetNativeCode();
5226 *outFlags = CLRDATA_FOLLOW_STUB_EXIT;
5230 *outAddr = GFN_TADDR(DACNotifyCompilationFinished);
5231 outBuffer->u.flags = STUB_BUF_METHOD_JITTED;
5232 outBuffer->u.addr = PTR_HOST_TO_TADDR(methodDesc);
5233 *outFlags = CLRDATA_FOLLOW_STUB_INTERMEDIATE;
5236 case TRACE_FRAME_PUSH:
5239 return E_INVALIDARG;
5242 *outAddr = trace.GetAddress();
5243 outBuffer->u.flags = STUB_BUF_FRAME_PUSHED;
5244 outBuffer->u.addr = trace.GetAddress();
5245 *outFlags = CLRDATA_FOLLOW_STUB_INTERMEDIATE;
5248 case TRACE_MGR_PUSH:
5251 return E_INVALIDARG;
5254 *outAddr = trace.GetAddress();
5255 outBuffer->u.flags = STUB_BUF_STUB_MANAGER_PUSHED;
5256 outBuffer->u.addr = trace.GetAddress();
5257 outBuffer->u.arg1 = PTR_HOST_TO_TADDR(trace.GetStubManager());
5258 *outFlags = CLRDATA_FOLLOW_STUB_INTERMEDIATE;
5262 return E_INVALIDARG;
5268 HRESULT STDMETHODCALLTYPE
5269 ClrDataAccess::FollowStub(
5270 /* [in] */ ULONG32 inFlags,
5271 /* [in] */ CLRDATA_ADDRESS inAddr,
5272 /* [in] */ CLRDATA_FOLLOW_STUB_BUFFER* _inBuffer,
5273 /* [out] */ CLRDATA_ADDRESS* outAddr,
5274 /* [out] */ CLRDATA_FOLLOW_STUB_BUFFER* _outBuffer,
5275 /* [out] */ ULONG32* outFlags)
5277 return FollowStub2(NULL, inFlags, inAddr, _inBuffer,
5278 outAddr, _outBuffer, outFlags);
5281 HRESULT STDMETHODCALLTYPE
5282 ClrDataAccess::FollowStub2(
5283 /* [in] */ IXCLRDataTask* task,
5284 /* [in] */ ULONG32 inFlags,
5285 /* [in] */ CLRDATA_ADDRESS _inAddr,
5286 /* [in] */ CLRDATA_FOLLOW_STUB_BUFFER* _inBuffer,
5287 /* [out] */ CLRDATA_ADDRESS* _outAddr,
5288 /* [out] */ CLRDATA_FOLLOW_STUB_BUFFER* _outBuffer,
5289 /* [out] */ ULONG32* outFlags)
5293 if ((inFlags & ~(CLRDATA_FOLLOW_STUB_DEFAULT)) != 0)
5295 return E_INVALIDARG;
5298 STUB_BUF* inBuffer = (STUB_BUF*)_inBuffer;
5299 STUB_BUF* outBuffer = (STUB_BUF*)_outBuffer;
5302 (inBuffer->u.flags <= STUB_BUF_FLAGS_START ||
5303 inBuffer->u.flags >= STUB_BUF_FLAGS_END))
5305 return E_INVALIDARG;
5313 TADDR inAddr = TO_TADDR(_inAddr);
5315 Thread* thread = task ? ((ClrDataTask*)task)->GetThread() : NULL;
5320 if ((status = FollowStubStep(thread,
5331 // Some stub tracing just requests further iterations
5332 // of processing, so detect that case and loop.
5333 if (outAddr != inAddr)
5335 // We can make forward progress, we're done.
5336 *_outAddr = TO_CDADDR(outAddr);
5340 // We need more processing. As a protection
5341 // against infinite loops in corrupted or buggy
5342 // situations, we only allow this to happen a
5343 // small number of times.
5346 ZeroMemory(outBuffer, sizeof(*outBuffer));
5351 cycleBuf = *outBuffer;
5352 inBuffer = &cycleBuf;
5357 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5362 EX_END_CATCH(SwallowAllExceptions)
5369 #pragma warning(push)
5370 #pragma warning(disable:4297)
5373 ClrDataAccess::GetGcNotification(GcEvtArgs* gcEvtArgs)
5381 if (gcEvtArgs->typ >= GC_EVENT_TYPE_MAX)
5383 status = E_INVALIDARG;
5387 GcNotifications gn(GetHostGcNotificationTable());
5390 status = E_OUTOFMEMORY;
5394 GcEvtArgs *res = gn.GetNotification(*gcEvtArgs);
5409 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5414 EX_END_CATCH(SwallowAllExceptions)
5421 ClrDataAccess::SetGcNotification(IN GcEvtArgs gcEvtArgs)
5429 if (gcEvtArgs.typ >= GC_EVENT_TYPE_MAX)
5431 status = E_INVALIDARG;
5435 GcNotifications gn(GetHostGcNotificationTable());
5438 status = E_OUTOFMEMORY;
5442 if (gn.SetNotification(gcEvtArgs) && gn.UpdateOutOfProcTable())
5455 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5460 EX_END_CATCH(SwallowAllExceptions)
5467 #pragma warning(pop)
5471 ClrDataAccess::Initialize(void)
5474 CLRDATA_ADDRESS base;
5477 // We do not currently support cross-platform
5478 // debugging. Verify that cross-platform is not
5482 // Determine our platform based on the pre-processor macros set when we were built
5484 #if defined(_TARGET_X86_)
5485 CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_X86;
5486 #elif defined(_TARGET_AMD64_)
5487 CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_AMD64;
5488 #elif defined(_TARGET_ARM_)
5489 CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_ARM;
5490 #elif defined(_TARGET_ARM64_)
5491 CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_ARM64;
5493 #error Unknown processor.
5496 CorDebugPlatform targetPlatform;
5497 IfFailRet(m_pTarget->GetPlatform(&targetPlatform));
5499 if (targetPlatform != hostPlatform)
5501 // DAC fatal error: Platform mismatch - the platform reported by the data target
5502 // is not what this version of mscordacwks.dll was built for.
5503 return CORDBG_E_UNCOMPATIBLE_PLATFORMS;
5507 // Get the current DLL base for mscorwks globals.
5508 // In case of multiple-CLRs, there may be multiple dlls named "mscorwks".
5509 // code:OpenVirtualProcess can take the base address (clrInstanceId) to select exactly
5510 // which CLR to is being target. If so, m_globalBase will already be set.
5513 if (m_globalBase == 0)
5515 // Caller didn't specify which CLR to debug. This supports Whidbey SOS cases, so we should
5516 // be using a legacy data target.
5517 if (m_pLegacyTarget == NULL)
5519 DacError(E_INVALIDARG);
5523 // Since this is Whidbey, assume there's only 1 CLR named "mscorwks.dll" and pick that.
5524 IfFailRet(m_pLegacyTarget->GetImageBase(MAIN_CLR_DLL_NAME_W, &base));
5526 m_globalBase = TO_TADDR(base);
5529 // We don't need to try too hard to prevent
5530 // multiple initializations as each one will
5531 // copy the same data into the globals and so
5532 // cannot interfere with each other.
5535 IfFailRet(GetDacGlobals());
5536 IfFailRet(DacGetHostVtPtrs());
5541 // DAC is now setup and ready to use
5544 // Do some validation
5545 IfFailRet(VerifyDlls());
5547 // To support EH SxS, utilcode requires the base address of the runtime
5548 // as part of its initialization so that functions like "WasThrownByUs" work correctly since
5549 // they use the CLR base address to check if an exception was raised by a given instance of the runtime
5552 // Thus, when DAC is initialized, initialize utilcode with the base address of the runtime loaded in the
5553 // target process. This is similar to work done in CorDB::SetTargetCLR for mscordbi.
5555 // Initialize UtilCode for SxS scenarios
5556 CoreClrCallbacks cccallbacks;
5557 cccallbacks.m_hmodCoreCLR = (HINSTANCE)m_globalBase; // Base address of the runtime in the target process
5558 cccallbacks.m_pfnIEE = NULL;
5559 cccallbacks.m_pfnGetCORSystemDirectory = NULL;
5560 cccallbacks.m_pfnGetCLRFunction = NULL;
5561 InitUtilcode(cccallbacks);
5567 ClrDataAccess::FindClrThreadByTaskId(ULONG64 taskId)
5569 Thread* thread = NULL;
5571 if (!ThreadStore::s_pThreadStore)
5576 while ((thread = ThreadStore::GetAllThreadList(thread, 0, 0)))
5578 if (thread->GetThreadId() == (DWORD)taskId)
5588 ClrDataAccess::IsPossibleCodeAddress(IN TADDR address)
5594 // First do a trivial check on the readability of the
5595 // address. This makes for quick rejection of bogus
5596 // addresses that the debugger sends in when searching
5597 // stacks for return addresses.
5598 // XXX Microsoft - Will this cause problems in minidumps
5599 // where it's possible the stub is identifiable but
5600 // the stub code isn't present? Yes, but the lack
5601 // of that code could confuse the walker on its own
5602 // if it does code analysis.
5603 if ((m_pTarget->ReadVirtual(address, &testRead, sizeof(testRead),
5604 &testDone) != S_OK) ||
5607 return E_INVALIDARG;
5614 ClrDataAccess::GetFullMethodName(
5615 IN MethodDesc* methodDesc,
5616 IN ULONG32 symbolChars,
5617 OUT ULONG32* symbolLen,
5618 __out_ecount_part_opt(symbolChars, *symbolLen) LPWSTR symbol
5622 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
5625 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
5627 TypeString::AppendMethodInternal(s, methodDesc, TypeString::FormatSignature|TypeString::FormatNamespace|TypeString::FormatFullInst);
5629 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
5633 if (!MdCacheGetEEName(dac_cast<TADDR>(methodDesc), s))
5639 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
5643 // Copy as much as we can and truncate the rest.
5644 wcsncpy_s(symbol, symbolChars, s.GetUnicode(), _TRUNCATE);
5648 *symbolLen = s.GetCount() + 1;
5650 if (symbol != NULL && symbolChars < (s.GetCount() + 1))
5657 ClrDataAccess::GetJitHelperName(
5659 IN bool dynamicHelpersOnly /*=false*/
5662 const static PCSTR s_rgHelperNames[] = {
5663 #define JITHELPER(code,fn,sig) #code,
5664 #include <jithelpers.h>
5666 static_assert_no_msg(COUNTOF(s_rgHelperNames) == CORINFO_HELP_COUNT);
5669 if (!dynamicHelpersOnly)
5671 if (!dynamicHelpersOnly && g_runtimeLoadedBaseAddress <= address &&
5672 address < g_runtimeLoadedBaseAddress + g_runtimeVirtualSize)
5673 #endif // FEATURE_PAL
5675 // Read the whole table from the target in one shot for better performance
5676 VMHELPDEF * pTable = static_cast<VMHELPDEF *>(
5677 PTR_READ(dac_cast<TADDR>(&hlpFuncTable), CORINFO_HELP_COUNT * sizeof(VMHELPDEF)));
5679 for (int i = 0; i < CORINFO_HELP_COUNT; i++)
5681 if (address == (TADDR)(pTable[i].pfnHelper))
5682 return s_rgHelperNames[i];
5686 // Check if its a dynamically generated JIT helper
5687 const static CorInfoHelpFunc s_rgDynamicHCallIds[] = {
5688 #define DYNAMICJITHELPER(code, fn, sig) code,
5689 #define JITHELPER(code, fn,sig)
5690 #include <jithelpers.h>
5693 // Read the whole table from the target in one shot for better performance
5694 VMHELPDEF * pDynamicTable = static_cast<VMHELPDEF *>(
5695 PTR_READ(dac_cast<TADDR>(&hlpDynamicFuncTable), DYNAMIC_CORINFO_HELP_COUNT * sizeof(VMHELPDEF)));
5696 for (unsigned d = 0; d < DYNAMIC_CORINFO_HELP_COUNT; d++)
5698 if (address == (TADDR)(pDynamicTable[d].pfnHelper))
5700 return s_rgHelperNames[s_rgDynamicHCallIds[d]];
5708 ClrDataAccess::RawGetMethodName(
5709 /* [in] */ CLRDATA_ADDRESS address,
5710 /* [in] */ ULONG32 flags,
5711 /* [in] */ ULONG32 bufLen,
5712 /* [out] */ ULONG32 *symbolLen,
5713 /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR symbolBuf[ ],
5714 /* [out] */ CLRDATA_ADDRESS* displacement)
5717 _ASSERTE((address & THUMB_CODE) == 0);
5718 address &= ~THUMB_CODE;
5721 const UINT k_cch64BitHexFormat = COUNTOF("1234567812345678");
5726 return E_INVALIDARG;
5730 if( (status = TRY_CLRDATA_ADDRESS_TO_TADDR(address, &taddr)) != S_OK )
5735 if ((status = IsPossibleCodeAddress(taddr)) != S_OK)
5740 PTR_StubManager pStubManager;
5741 MethodDesc* methodDesc = NULL;
5744 EECodeInfo codeInfo(TO_TADDR(address));
5745 if (codeInfo.IsValid())
5749 *displacement = codeInfo.GetRelOffset();
5752 methodDesc = codeInfo.GetMethodDesc();
5753 goto NameFromMethodDesc;
5757 pStubManager = StubManager::FindStubManager(TO_TADDR(address));
5758 if (pStubManager != NULL)
5766 // Special-cased stub managers
5768 #ifdef FEATURE_PREJIT
5769 if (pStubManager == RangeSectionStubManager::g_pManager)
5771 switch (RangeSectionStubManager::GetStubKind(TO_TADDR(address)))
5773 case STUB_CODE_BLOCK_PRECODE:
5776 case STUB_CODE_BLOCK_JUMPSTUB:
5785 if (pStubManager == PrecodeStubManager::g_pManager)
5788 PCODE alignedAddress = AlignDown(TO_TADDR(address), PRECODE_ALIGNMENT);
5791 alignedAddress += THUMB_CODE;
5794 SIZE_T maxPrecodeSize = sizeof(StubPrecode);
5796 #ifdef HAS_THISPTR_RETBUF_PRECODE
5797 maxPrecodeSize = max(maxPrecodeSize, sizeof(ThisPtrRetBufPrecode));
5799 #ifdef HAS_REMOTING_PRECODE
5800 maxPrecodeSize = max(maxPrecodeSize, sizeof(RemotingPrecode));
5805 for (SIZE_T i = 0; i < maxPrecodeSize / PRECODE_ALIGNMENT; i++)
5807 // Try to find matching precode entrypoint
5808 if (PrecodeStubManager::IsPrecodeByAsm(alignedAddress))
5810 Precode* pPrecode = Precode::GetPrecodeFromEntryPoint(alignedAddress, TRUE);
5811 if (pPrecode != NULL)
5813 methodDesc = pPrecode->GetMethodDesc();
5814 if (methodDesc != NULL)
5818 *displacement = TO_TADDR(address) - PCODEToPINSTR(alignedAddress);
5821 goto NameFromMethodDesc;
5825 alignedAddress -= PRECODE_ALIGNMENT;
5831 EX_END_CATCH(SwallowAllExceptions)
5834 if (pStubManager == JumpStubStubManager::g_pManager)
5837 PCODE pTarget = decodeBackToBackJump(TO_TADDR(address));
5839 HRESULT hr = GetRuntimeNameByAddress(pTarget, flags, bufLen, symbolLen, symbolBuf, NULL);
5845 PCSTR pHelperName = GetJitHelperName(pTarget);
5846 if (pHelperName != NULL)
5848 hr = ConvertUtf8(pHelperName, bufLen, symbolLen, symbolBuf);
5856 static WCHAR s_wszFormatNameWithStubManager[] = W("CLRStub[%s]@%I64x");
5858 LPCWSTR wszStubManagerName = pStubManager->GetStubManagerName(TO_TADDR(address));
5859 _ASSERTE(wszStubManagerName != NULL);
5861 HRESULT hr = StringCchPrintfW(
5864 s_wszFormatNameWithStubManager,
5865 wszStubManagerName, // Arg 1 = stub name
5866 TO_TADDR(address)); // Arg 2 = stub hex address
5870 // Printf succeeded, so we have an exact char count to return
5873 size_t cchSymbol = wcslen(symbolBuf) + 1;
5874 if (!FitsIn<ULONG32>(cchSymbol))
5875 return COR_E_OVERFLOW;
5877 *symbolLen = (ULONG32) cchSymbol;
5882 // Printf failed. Estimate a size that will be at least big enough to hold the name
5885 size_t cchSymbol = COUNTOF(s_wszFormatNameWithStubManager) +
5886 wcslen(wszStubManagerName) +
5887 k_cch64BitHexFormat +
5890 if (!FitsIn<ULONG32>(cchSymbol))
5891 return COR_E_OVERFLOW;
5893 *symbolLen = (ULONG32) cchSymbol;
5898 // Do not waste time looking up name for static helper. Debugger can get the actual name from .pdb.
5900 pHelperName = GetJitHelperName(TO_TADDR(address), true /* dynamicHelpersOnly */);
5901 if (pHelperName != NULL)
5908 HRESULT hr = ConvertUtf8(pHelperName, bufLen, symbolLen, symbolBuf);
5915 return E_NOINTERFACE;
5918 if (methodDesc->GetClassification() == mcDynamic &&
5919 !methodDesc->GetSig())
5921 // XXX Microsoft - Should this case have a more specific name?
5922 static WCHAR s_wszFormatNameAddressOnly[] = W("CLRStub@%I64x");
5924 HRESULT hr = StringCchPrintfW(
5927 s_wszFormatNameAddressOnly,
5932 // Printf succeeded, so we have an exact char count to return
5935 size_t cchSymbol = wcslen(symbolBuf) + 1;
5936 if (!FitsIn<ULONG32>(cchSymbol))
5937 return COR_E_OVERFLOW;
5939 *symbolLen = (ULONG32) cchSymbol;
5944 // Printf failed. Estimate a size that will be at least big enough to hold the name
5947 size_t cchSymbol = COUNTOF(s_wszFormatNameAddressOnly) +
5948 k_cch64BitHexFormat +
5951 if (!FitsIn<ULONG32>(cchSymbol))
5952 return COR_E_OVERFLOW;
5954 *symbolLen = (ULONG32) cchSymbol;
5960 return GetFullMethodName(methodDesc, bufLen, symbolLen, symbolBuf);
5964 ClrDataAccess::GetMethodExtents(MethodDesc* methodDesc,
5965 METH_EXTENTS** extents)
5967 CLRDATA_ADDRESS_RANGE* curExtent;
5971 // Get the information from the methoddesc.
5972 // We'll go through the CodeManager + JitManagers, so this should work
5973 // for all types of managed code.
5976 PCODE methodStart = methodDesc->GetNativeCode();
5979 return E_NOINTERFACE;
5982 EECodeInfo codeInfo(methodStart);
5983 _ASSERTE(codeInfo.IsValid());
5985 TADDR codeSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfo());
5987 *extents = new (nothrow) METH_EXTENTS;
5990 return E_OUTOFMEMORY;
5993 (*extents)->numExtents = 1;
5994 curExtent = (*extents)->extents;
5995 curExtent->startAddress = TO_CDADDR(methodStart);
5996 curExtent->endAddress =
5997 curExtent->startAddress + codeSize;
6001 (*extents)->curExtent = 0;
6006 // Allocator to pass to the debug-info-stores...
6007 BYTE* DebugInfoStoreNew(void * pData, size_t cBytes)
6009 return new (nothrow) BYTE[cBytes];
6013 ClrDataAccess::GetMethodVarInfo(MethodDesc* methodDesc,
6015 ULONG32* numVarInfo,
6016 ICorDebugInfo::NativeVarInfo** varInfo,
6017 ULONG32* codeOffset)
6020 COUNT_T countNativeVarInfo;
6021 NewHolder<ICorDebugInfo::NativeVarInfo> nativeVars(NULL);
6023 DebugInfoRequest request;
6024 TADDR nativeCodeStartAddr = PCODEToPINSTR(methodDesc->GetNativeCode());
6025 request.InitFromStartingAddr(methodDesc, nativeCodeStartAddr);
6027 BOOL success = DebugInfoManager::GetBoundariesAndVars(
6029 DebugInfoStoreNew, NULL, // allocator
6031 &countNativeVarInfo, &nativeVars);
6039 if (!nativeVars || !countNativeVarInfo)
6041 return E_NOINTERFACE;
6044 *numVarInfo = countNativeVarInfo;
6045 *varInfo = nativeVars;
6046 nativeVars.SuppressRelease(); // To prevent NewHolder from releasing the memory
6050 *codeOffset = (ULONG32)
6051 (address - nativeCodeStartAddr);
6057 ClrDataAccess::GetMethodNativeMap(MethodDesc* methodDesc,
6060 DebuggerILToNativeMap** map,
6062 CLRDATA_ADDRESS* codeStart,
6063 ULONG32* codeOffset)
6065 _ASSERTE((codeOffset == NULL) || (address != NULL));
6067 // Use the DebugInfoStore to get IL->Native maps.
6068 // It doesn't matter whether we're jitted, ngenned etc.
6070 DebugInfoRequest request;
6071 TADDR nativeCodeStartAddr = PCODEToPINSTR(methodDesc->GetNativeCode());
6072 request.InitFromStartingAddr(methodDesc, nativeCodeStartAddr);
6076 ULONG32 countMapCopy;
6077 NewHolder<ICorDebugInfo::OffsetMapping> mapCopy(NULL);
6079 BOOL success = DebugInfoManager::GetBoundariesAndVars(
6081 DebugInfoStoreNew, NULL, // allocator
6082 &countMapCopy, &mapCopy,
6091 // Need to convert map formats.
6092 *numMap = countMapCopy;
6094 *map = new (nothrow) DebuggerILToNativeMap[countMapCopy];
6097 return E_OUTOFMEMORY;
6101 for (i = 0; i < *numMap; i++)
6103 (*map)[i].ilOffset = mapCopy[i].ilOffset;
6104 (*map)[i].nativeStartOffset = mapCopy[i].nativeOffset;
6107 (*map)[i - 1].nativeEndOffset = (*map)[i].nativeStartOffset;
6109 (*map)[i].source = mapCopy[i].source;
6113 (*map)[i - 1].nativeEndOffset = 0;
6117 // Update varion out params.
6120 *codeStart = TO_CDADDR(nativeCodeStartAddr);
6124 *codeOffset = (ULONG32)
6125 (address - nativeCodeStartAddr);
6128 *mapAllocated = true;
6132 // Get the MethodDesc for a function
6135 // pModule - pointer to the module for the function
6136 // memberRef - metadata token for the function
6138 // MethodDesc for the function
6139 MethodDesc * ClrDataAccess::FindLoadedMethodRefOrDef(Module* pModule,
6142 CONTRACT(MethodDesc *)
6145 PRECONDITION(CheckPointer(pModule));
6146 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
6150 // Must have a MemberRef or a MethodDef
6151 mdToken tkType = TypeFromToken(memberRef);
6152 _ASSERTE((tkType == mdtMemberRef) || (tkType == mdtMethodDef));
6154 if (tkType == mdtMemberRef)
6156 RETURN pModule->LookupMemberRefAsMethod(memberRef);
6159 RETURN pModule->LookupMethodDef(memberRef);
6160 } // FindLoadedMethodRefOrDef
6163 // ReportMem - report a region of memory for dump gathering
6165 // If you specify that you expect success, any failure will cause ReportMem to
6166 // return false. If you do not expect success, true is always returned.
6167 // This function only throws when all dump collection should be cancelled.
6170 // addr - the starting target address for the memory to report
6171 // size - the length (in bytes) to report
6172 // fExpectSuccess - if true (the default), then we expect that this region of memory
6173 // should be fully readable. Any read errors indicate a corrupt target.
6175 bool ClrDataAccess::ReportMem(TADDR addr, TSIZE_T size, bool fExpectSuccess /*= true*/)
6177 SUPPORTS_DAC_HOST_ONLY;
6179 // This block of code is to help debugging blocks that we report
6180 // to minidump/heapdump. You can set break point here to view the static
6181 // variable to figure out the size of blocks that we are reporting.
6182 // Most useful is set conditional break point to catch large chuck of
6183 // memory. We will leave it here for all builds.
6185 static TADDR debugAddr;
6186 static TSIZE_T debugSize;
6191 if (!addr || addr == (TADDR)-1 || !size)
6200 // Try and sanity-check the reported region of memory
6203 // in debug builds, sanity-check all reports
6204 const TSIZE_T k_minSizeToCheck = 1;
6206 // in retail builds, only sanity-check larger chunks which have the potential to waste a
6207 // lot of time and/or space. This avoids the overhead of checking for the majority of
6208 // memory regions (which are small).
6209 const TSIZE_T k_minSizeToCheck = 1024;
6211 if (size >= k_minSizeToCheck)
6213 if (!IsFullyReadable(addr, size))
6215 if (!fExpectSuccess)
6217 // We know the read might fail (eg. we're trying to find mapped pages in
6218 // a module image), so just skip this block silently.
6219 // Note that the EnumMemoryRegion callback won't necessarily do anything if any part of
6220 // the region is unreadable, and so there is no point in calling it. For cases where we expect
6221 // the read might fail, but we want to report any partial blocks, we have to break up the region
6222 // into pages and try reporting each page anyway
6226 // We're reporting bogus memory, so the target must be corrupt (or there is a issue). We should abort
6227 // reporting and continue with the next data structure (where the exception is caught),
6228 // just like we would for a DAC read error (otherwise we might do something stupid
6229 // like get into an infinite loop, or otherwise waste time with corrupt data).
6231 TARGET_CONSISTENCY_CHECK(false, "Found unreadable memory while reporting memory regions for dump gathering");
6236 // Minidumps should never contain data structures that are anywhere near 4MB. If we see this, it's
6237 // probably due to memory corruption. To keep the dump small, we'll truncate the block. Note that
6238 // the size to which the block is truncated is pretty unique, so should be good evidence in a dump
6239 // that this has happened.
6240 // Note that it's hard to say what a good value would be here, or whether we should dump any of the
6241 // data structure at all. Hopefully experience will help guide this going forward.
6242 // @dbgtodo : Extend dump-gathering API to allow a dump-log to be included.
6243 const TSIZE_T kMaxMiniDumpRegion = 4*1024*1024 - 3; // 4MB-3
6244 if( size > kMaxMiniDumpRegion
6245 && (m_enumMemFlags == CLRDATA_ENUM_MEM_MINI
6246 || m_enumMemFlags == CLRDATA_ENUM_MEM_TRIAGE))
6248 TARGET_CONSISTENCY_CHECK( false, "Dump target consistency failure - truncating minidump data structure");
6249 size = kMaxMiniDumpRegion;
6252 // track the total memory reported.
6253 m_cbMemoryReported += size;
6255 // ICLRData APIs take only 32-bit sizes. In practice this will almost always be sufficient, but
6256 // in theory we might have some >4GB ranges on large 64-bit processes doing a heap dump
6257 // (for example, the code:LoaderHeap). If necessary, break up the reporting into maximum 4GB
6258 // chunks so we can use the existing API.
6259 // @dbgtodo : ICorDebugDataTarget should probably use 64-bit sizes
6263 if (size > ULONG_MAX)
6265 enumSize = ULONG_MAX;
6269 enumSize = (ULONG32)size;
6272 // Actually perform the memory reporting callback
6273 status = m_enumMemCb->EnumMemoryRegion(TO_CDADDR(addr), enumSize);
6276 m_memStatus = status;
6278 // If dump generation was cancelled, allow us to throw upstack so we'll actually quit.
6279 if ((fExpectSuccess) && (status != COR_E_OPERATIONCANCELED))
6283 // If the return value of EnumMemoryRegion is COR_E_OPERATIONCANCELED,
6284 // it means that user has requested that the minidump gathering be canceled.
6285 // To do this we throw an exception which is caught in EnumMemoryRegionsWrapper.
6286 if (status == COR_E_OPERATIONCANCELED)
6291 // Move onto the next chunk (if any)
6301 // DacUpdateMemoryRegion - updates/poisons a region of memory of generated dump
6304 // addr - target address of the beginning of the memory region
6305 // bufferSize - number of bytes to update/poison
6306 // buffer - data to be written at given target address
6308 bool ClrDataAccess::DacUpdateMemoryRegion(TADDR addr, TSIZE_T bufferSize, BYTE* buffer)
6310 SUPPORTS_DAC_HOST_ONLY;
6313 if (!addr || addr == (TADDR)-1 || !bufferSize)
6318 // track the total memory reported.
6319 m_cbMemoryReported += bufferSize;
6321 if (m_updateMemCb == NULL)
6326 // Actually perform the memory updating callback
6327 status = m_updateMemCb->UpdateMemoryRegion(TO_CDADDR(addr), (ULONG32)bufferSize, buffer);
6337 // Check whether a region of target memory is fully readable.
6340 // addr The base target address of the region
6341 // size The size of the region to analyze
6344 // True if the entire regions appears to be readable, false otherwise.
6347 // The motivation here is that reporting large regions of unmapped address space to dbgeng can result in
6348 // it taking a long time trying to identify a valid subrange. This can happen when the target
6349 // memory is corrupt, and we enumerate a data structure with a dynamic size. Ideally we would just spec
6350 // the ICLRDataEnumMemoryRegionsCallback API to require the client to fail if it detects an unmapped
6351 // memory address in the region. However, we can't change the existing dbgeng code, so for now we'll
6352 // rely on this heuristic here.
6353 // @dbgtodo : Try and get the dbg team to change their EnumMemoryRegion behavior. See DevDiv Bugs 6265
6355 bool ClrDataAccess::IsFullyReadable(TADDR taBase, TSIZE_T dwSize)
6357 // The only way we have to verify that a memory region is readable is to try reading it in it's
6358 // entirety. This is potentially expensive, so we'll rely on a heuristic that spot-checks various
6359 // points in the region.
6361 // Ensure we've got something to check
6365 // Check for overflow
6366 TADDR taEnd = DacTAddrOffset(taBase, dwSize, 1);
6368 // Loop through using expontential growth, being sure to check both the first and last byte
6369 TADDR taCurr = taBase;
6370 TSIZE_T dwInc = 4096;
6374 // Try and read a byte from the target. Note that we don't use PTR_BYTE here because we don't want
6375 // the overhead of inserting entries into the DAC instance cache.
6377 ULONG32 dwBytesRead;
6378 HRESULT hr = m_pTarget->ReadVirtual(taCurr, &b, 1, &dwBytesRead);
6379 if( hr != S_OK || dwBytesRead < 1 )
6384 if (taEnd - taCurr <= 1)
6386 // We just read the last byte so we're done
6387 _ASSERTE( taCurr = taEnd - 1 );
6390 else if (dwInc == 0 || dwInc >= taEnd - taCurr)
6392 // we've reached the end of the exponential series, check the last byte
6397 // advance current pointer (subtraction above ensures this won't overflow)
6400 // double the increment for next time (or set to 0 if it's already the max)
6408 ClrDataAccess::GetHostJitNotificationTable()
6410 if (m_jitNotificationTable == NULL)
6412 m_jitNotificationTable =
6413 JITNotifications::InitializeNotificationTable(1000);
6416 return m_jitNotificationTable;
6420 ClrDataAccess::GetHostGcNotificationTable()
6422 if (m_gcNotificationTable == NULL)
6424 m_gcNotificationTable =
6425 GcNotifications::InitializeNotificationTable(128);
6428 return m_gcNotificationTable;
6432 ClrDataAccess::GetMetaDataFileInfoFromPEFile(PEFile *pPEFile,
6438 __out_ecount(cchFilePath) LPWSTR wszFilePath,
6439 const DWORD cchFilePath)
6441 SUPPORTS_DAC_HOST_ONLY;
6442 PEImage *mdImage = NULL;
6443 PEImageLayout *layout;
6444 IMAGE_DATA_DIRECTORY *pDir = NULL;
6445 COUNT_T uniPathChars = 0;
6449 if (pPEFile->HasNativeImage())
6451 mdImage = pPEFile->GetNativeImage();
6452 _ASSERTE(mdImage != NULL);
6453 layout = mdImage->GetLoadedLayout();
6454 pDir = &(layout->GetCorHeader()->MetaData);
6455 // For ngen image, the IL metadata is stored for private use. So we need to pass
6456 // the RVA hint to find it to debuggers.
6458 if (pDir->Size != 0)
6461 dwRvaHint = pDir->VirtualAddress;
6462 dwDataSize = pDir->Size;
6466 if (pDir == NULL || pDir->Size == 0)
6468 mdImage = pPEFile->GetILimage();
6469 if (mdImage != NULL)
6471 layout = mdImage->GetLoadedLayout();
6472 pDir = &layout->GetCorHeader()->MetaData;
6474 // In IL image case, we do not have any hint to IL metadata since it is stored
6475 // in the corheader.
6478 dwDataSize = pDir->Size;
6486 // Do not fail if path can not be read. Triage dumps don't have paths and we want to fallback
6487 // on searching metadata from IL image.
6488 mdImage->GetPath().DacGetUnicode(cchFilePath, wszFilePath, &uniPathChars);
6490 if (!mdImage->HasNTHeaders() ||
6491 !mdImage->HasCorHeader() ||
6492 !mdImage->HasLoadedLayout() ||
6493 (uniPathChars > cchFilePath))
6498 // It is possible that the module is in-memory. That is the wszFilePath here is empty.
6499 // We will try to use the module name instead in this case for hosting debugger
6501 if (wcslen(wszFilePath) == 0)
6503 mdImage->GetModuleFileNameHintForDAC().DacGetUnicode(cchFilePath, wszFilePath, &uniPathChars);
6504 if (uniPathChars > cchFilePath)
6510 dwTimeStamp = layout->GetTimeDateStamp();
6511 dwSize = (ULONG32)layout->GetVirtualSize();
6517 bool ClrDataAccess::GetILImageInfoFromNgenPEFile(PEFile *peFile,
6520 __out_ecount(cchFilePath) LPWSTR wszFilePath,
6521 const DWORD cchFilePath)
6523 SUPPORTS_DAC_HOST_ONLY;
6524 DWORD dwWritten = 0;
6526 // use the IL File name
6527 if (!peFile->GetPath().DacGetUnicode(cchFilePath, wszFilePath, (COUNT_T *)(&dwWritten)))
6529 // Use DAC hint to retrieve the IL name.
6530 peFile->GetModuleFileNameHint().DacGetUnicode(cchFilePath, wszFilePath, (COUNT_T *)(&dwWritten));
6532 #ifdef FEATURE_PREJIT
6533 // Need to get IL image information from cached info in the ngen image.
6534 dwTimeStamp = peFile->GetLoaded()->GetNativeVersionInfo()->sourceAssembly.timeStamp;
6535 dwSize = peFile->GetLoaded()->GetNativeVersionInfo()->sourceAssembly.ilImageSize;
6539 #endif // FEATURE_PREJIT
6544 #if defined(FEATURE_CORESYSTEM)
6546 // We extract "ni.dll or .ni.winmd" from the NGEM image name to obtain the IL image name.
6547 // In the end we add given ilExtension.
6548 // This dependecy is based on Apollo installer behavior.
6549 bool ClrDataAccess::GetILImageNameFromNgenImage( LPCWSTR ilExtension,
6550 __out_ecount(cchFilePath) LPWSTR wszFilePath,
6551 const DWORD cchFilePath)
6553 if (wszFilePath == NULL || cchFilePath == 0)
6558 _wcslwr_s(wszFilePath, cchFilePath);
6559 // Find the "ni.dll" or "ni.winmd" extension (check for PEFile isWinRT something to know when is winmd or not.
6560 // If none exists use NGEN image name.
6562 const WCHAR* ngenExtension[] = {W("ni.dll"), W("ni.winmd")};
6564 for (unsigned i = 0; i < COUNTOF(ngenExtension); ++i)
6566 if (wcslen(ilExtension) > wcslen(ngenExtension[i]))
6568 // We should not have IL image name bigger than NGEN image.
6569 // It will not fit inside wszFilePath.
6572 LPWSTR wszFileExtension = wcsstr(wszFilePath, ngenExtension[i]);
6573 if (wszFileExtension != 0)
6575 LPWSTR wszNextFileExtension = wszFileExtension;
6576 // Find last occurence
6579 wszFileExtension = wszNextFileExtension;
6580 wszNextFileExtension = wcsstr(wszFileExtension + 1, ngenExtension[i]);
6581 } while (wszNextFileExtension != 0);
6583 // Overwrite ni.dll or ni.winmd with ilExtension(.dll, .winmd)
6584 if (!memcpy_s(wszFileExtension,
6585 wcslen(ngenExtension[i])*sizeof(WCHAR),
6587 wcslen(ilExtension)*sizeof(WCHAR)))
6589 wszFileExtension[wcslen(ilExtension)] = '\0';
6595 //Use ngen filename if there is no ".ni"
6596 if (wcsstr(wszFilePath, W(".ni")) == 0)
6603 #endif // FEATURE_CORESYSTEM
6606 ClrDataAccess::GetMetaDataFromHost(PEFile* peFile,
6609 DWORD imageTimestamp, imageSize, dataSize;
6610 void* buffer = NULL;
6611 WCHAR uniPath[MAX_PATH] = {0};
6613 bool isNGEN = false;
6614 DAC_INSTANCE* inst = NULL;
6618 // We always ask for the IL image metadata,
6619 // as we expect that to be more
6620 // available than others. The drawback is that
6621 // there may be differences between the IL image
6622 // metadata and native image metadata, so we
6623 // have to mark such alternate metadata so that
6624 // we can fail unsupported usage of it.
6627 // Microsoft - above comment seems to be an unimplemented thing.
6628 // The DAC_MD_IMPORT.isAlternate field gets ultimately set, but
6629 // on the searching I did, I cannot find any usage of it
6630 // other than in the ctor. Should we be doing something, or should
6631 // we remove this comment and the isAlternate field?
6632 // It's possible that test will want us to track whether we have
6633 // an IL image's metadata loaded against an NGEN'ed image
6634 // so the field remains for now.
6636 if (!ClrDataAccess::GetMetaDataFileInfoFromPEFile(
6649 // try direct match for the image that is loaded into the managed process
6650 peFile->GetLoadedMetadata((COUNT_T *)(&dataSize));
6652 DWORD allocSize = 0;
6653 if (!ClrSafeInt<DWORD>::addition(dataSize, sizeof(DAC_INSTANCE), allocSize))
6655 DacError(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW));
6658 inst = m_instances.Alloc(0, allocSize, DAC_DPTR);
6661 DacError(E_OUTOFMEMORY);
6665 buffer = (void*)(inst + 1);
6667 // APIs implemented by hosting debugger. It can use the path/filename, timestamp, and
6668 // file size to find an exact match for the image. If that fails for an ngen'ed image,
6669 // we can request the IL image which it came from.
6670 if (m_legacyMetaDataLocator)
6672 // Legacy API implemented by hosting debugger.
6673 hr = m_legacyMetaDataLocator->GetMetadata(
6677 NULL, // MVID - not used yet
6679 0, // flags - reserved for future.
6686 hr = m_target3->GetMetaData(
6690 NULL, // MVID - not used yet
6692 0, // flags - reserved for future.
6697 if (FAILED(hr) && isNGEN)
6699 // We failed to locate the ngen'ed image. We should try to
6700 // find the matching IL image
6703 if (!ClrDataAccess::GetILImageInfoFromNgenPEFile(
6713 #if defined(FEATURE_CORESYSTEM)
6714 const WCHAR* ilExtension[] = {W("dll"), W("winmd")};
6715 WCHAR ngenImageName[MAX_PATH] = {0};
6716 if (wcscpy_s(ngenImageName, NumItems(ngenImageName), uniPath) != 0)
6720 for (unsigned i = 0; i < COUNTOF(ilExtension); i++)
6722 if (wcscpy_s(uniPath, NumItems(uniPath), ngenImageName) != 0)
6726 // Transform NGEN image name into IL Image name
6727 if (!GetILImageNameFromNgenImage(ilExtension[i], uniPath, NumItems(uniPath)))
6731 #endif//FEATURE_CORESYSTEM
6733 // RVA size in ngen image and IL image is the same. Because the only
6734 // different is in RVA. That is 4 bytes column fixed.
6738 if (m_legacyMetaDataLocator)
6740 hr = m_legacyMetaDataLocator->GetMetadata(
6744 NULL, // MVID - not used yet
6745 0, // pass zero hint here... important
6746 0, // flags - reserved for future.
6753 hr = m_target3->GetMetaData(
6757 NULL, // MVID - not used yet
6758 0, // pass zero hint here... important
6759 0, // flags - reserved for future.
6764 #if defined(FEATURE_CORESYSTEM)
6770 #endif // FEATURE_CORESYSTEM
6778 *isAlternate = isAlt;
6779 m_instances.AddSuperseded(inst);
6785 m_instances.ReturnAlloc(inst);
6791 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6793 // Given a PEFile or a ReflectionModule try to find the corresponding metadata
6794 // We will first ask debugger to locate it. If fail, we will try
6795 // to get it from the target process
6797 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6799 ClrDataAccess::GetMDImport(const PEFile* peFile, const ReflectionModule* reflectionModule, bool throwEx)
6802 PTR_CVOID mdBaseTarget = NULL;
6804 IMDInternalImport* mdImport = NULL;
6805 PVOID mdBaseHost = NULL;
6806 bool isAlternate = false;
6808 _ASSERTE(peFile == NULL && reflectionModule != NULL || peFile != NULL && reflectionModule == NULL);
6809 TADDR peFileAddr = (peFile != NULL) ? dac_cast<TADDR>(peFile) : dac_cast<TADDR>(reflectionModule);
6812 // Look for one we've already created.
6814 mdImport = m_mdImports.Get(peFileAddr);
6815 if (mdImport != NULL)
6822 // Get the metadata size
6823 mdBaseTarget = ((PEFile*)peFile)->GetLoadedMetadata(&mdSize);
6825 else if (reflectionModule != NULL)
6828 PTR_SBuffer metadataBuffer = reflectionModule->GetDynamicMetadataBuffer();
6829 mdBaseTarget = dac_cast<PTR_CVOID>((metadataBuffer->DacGetRawBuffer()).StartAddress());
6830 mdSize = metadataBuffer->GetSize();
6841 if (mdBaseTarget == PTR_NULL)
6849 // Maybe the target process has the metadata
6850 // Find out where the metadata for the image is
6851 // in the target's memory.
6854 // Read the metadata into the host process. Make sure pass in false in the last
6855 // parameter. This is only matters when producing skinny mini-dump. This will
6856 // prevent metadata gets reported into mini-dump.
6858 mdBaseHost = DacInstantiateTypeByAddressNoReport(dac_cast<TADDR>(mdBaseTarget), mdSize,
6862 // Try to see if debugger can locate it
6863 if (peFile != NULL && mdBaseHost == NULL && (m_target3 || m_legacyMetaDataLocator))
6865 // We couldn't read the metadata from memory. Ask
6866 // the target for metadata as it may be able to
6867 // provide it from some alternate means.
6868 mdBaseHost = GetMetaDataFromHost(const_cast<PEFile *>(peFile), &isAlternate);
6871 if (mdBaseHost == NULL)
6873 // cannot locate metadata anywhere
6876 DacError(E_INVALIDARG);
6882 // Open the MD interface on the host copy of the metadata.
6885 status = GetMDInternalInterface(mdBaseHost, mdSize, ofRead,
6886 IID_IMDInternalImport,
6898 // Remember the object for this module for
6899 // possible later use.
6900 // The m_mdImports list does get cleaned up by calls to ClrDataAccess::Flush,
6901 // i.e. every time the process changes state.
6903 if (m_mdImports.Add(peFileAddr, mdImport, isAlternate) == NULL)
6905 mdImport->Release();
6906 DacError(E_OUTOFMEMORY);
6914 // Set whether inconsistencies in the target should raise asserts.
6915 // This overrides the default initial setting.
6918 // fEnableAsserts - whether ASSERTs in dacized code should be enabled
6921 void ClrDataAccess::SetTargetConsistencyChecks(bool fEnableAsserts)
6923 LIMITED_METHOD_DAC_CONTRACT;
6924 m_fEnableTargetConsistencyAsserts = fEnableAsserts;
6928 // Get whether inconsistencies in the target should raise asserts.
6931 // whether ASSERTs in dacized code should be enabled
6934 // The implementation of ASSERT accesses this via code:DacTargetConsistencyAssertsEnabled
6936 // By default, this is disabled, unless COMPLUS_DbgDACEnableAssert is set (see code:ClrDataAccess::ClrDataAccess).
6937 // This is necessary for compatibility. For example, SOS expects to be able to scan for
6938 // valid MethodTables etc. (which may cause ASSERTs), and also doesn't want ASSERTs when working
6939 // with targets with corrupted memory.
6941 // Calling code:ClrDataAccess::SetTargetConsistencyChecks overrides the default setting.
6943 bool ClrDataAccess::TargetConsistencyAssertsEnabled()
6945 LIMITED_METHOD_DAC_CONTRACT;
6946 return m_fEnableTargetConsistencyAsserts;
6949 #ifdef FEATURE_CORESYSTEM
6950 #define ctime_s _ctime32_s
6951 #define time_t __time32_t
6955 // VerifyDlls - Validate that the mscorwks in the target matches this version of mscordacwks
6956 // Only done on Windows and Mac builds at the moment.
6957 // See code:CordbProcess::CordbProcess#DBIVersionChecking for more information regarding version checking.
6959 HRESULT ClrDataAccess::VerifyDlls()
6962 // Provide a knob for disabling this check if we really want to try and proceed anyway with a
6963 // DAC mismatch. DAC behavior may be arbitrarily bad - globals probably won't be at the same
6964 // address, data structures may be laid out differently, etc.
6965 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgDACSkipVerifyDlls))
6970 // Read the debug directory timestamp from the target mscorwks image using DAC
6971 // Note that we don't use the PE timestamp because the PE file might be changed in ways
6972 // that don't effect the PDB (and therefore don't effect DAC). Specifically, we rebase
6973 // our DLLs at the end of a build, that changes the PE file, but not the PDB.
6974 // Note that if we wanted to be extra careful, we could read the CV contents (which includes
6975 // the GUID signature) and verify it matches. Using the timestamp is useful for helpful error
6976 // messages, and should be sufficient in any real scenario.
6977 DWORD timestamp = 0;
6982 // Note that we don't need to worry about ensuring the image memory read by this code
6983 // is saved in a minidump. Managed minidump debugging already requires that you have
6984 // the full mscorwks.dll available at debug time (eg. windbg won't even load DAC without it).
6985 PEDecoder pedecoder(dac_cast<PTR_VOID>(m_globalBase));
6987 // We use the first codeview debug directory entry since this should always refer to the single
6988 // PDB for mscorwks.dll.
6989 const UINT k_maxDebugEntries = 32; // a reasonable upper limit in case of corruption
6990 for( UINT i = 0; i < k_maxDebugEntries; i++)
6992 PTR_IMAGE_DEBUG_DIRECTORY pDebugEntry = pedecoder.GetDebugDirectoryEntry(i);
6994 // If there are no more entries, then stop
6995 if (pDebugEntry == NULL)
6998 // Ignore non-codeview entries. Some scenarios (eg. optimized builds), there may be extra
6999 // debug directory entries at the end of some other type.
7000 if (pDebugEntry->Type == IMAGE_DEBUG_TYPE_CODEVIEW)
7002 // Found a codeview entry - use it's timestamp for comparison
7003 timestamp = pDebugEntry->TimeDateStamp;
7007 char szMsgBuf[1024];
7008 _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE,
7009 "Failed to find any valid codeview debug directory entry in %s image",
7010 MAIN_CLR_MODULE_NAME_A);
7011 _ASSERTE_MSG(timestamp != 0, szMsgBuf);
7015 if (!DacExceptionFilter(GET_EXCEPTION(), this, &hr))
7020 EX_END_CATCH(SwallowAllExceptions)
7027 // Validate that we got a timestamp and it matches what the DAC table told us to expect
7028 if (timestamp == 0 || timestamp != g_dacTableInfo.dwID0)
7030 // Timestamp mismatch. This means mscordacwks is being used with a version of
7031 // mscorwks other than the one it was built for. This will not work reliably.
7034 // Check if verbose asserts are enabled. The default is up to the specific instantiation of
7035 // ClrDataAccess, but can be overridden (in either direction) by a COMPLUS knob.
7036 // Note that we check this knob every time because it may be handy to turn it on in
7037 // the environment mid-flight.
7038 DWORD dwAssertDefault = m_fEnableDllVerificationAsserts ? 1 : 0;
7039 if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgDACAssertOnMismatch, dwAssertDefault))
7041 // Output a nice error message that contains the timestamps in string format.
7042 time_t actualTime = timestamp;
7043 char szActualTime[30];
7044 ctime_s(szActualTime, sizeof(szActualTime), &actualTime);
7046 time_t expectedTime = g_dacTableInfo.dwID0;
7047 char szExpectedTime[30];
7048 ctime_s(szExpectedTime, sizeof(szExpectedTime), &expectedTime);
7050 // Create a nice detailed message for the assert dialog.
7051 // Note that the strings returned by ctime_s have terminating newline characters.
7052 // This is technically a TARGET_CONSISTENCY_CHECK because a corrupt target could,
7053 // in-theory, have a corrupt mscrowks PE header and cause this check to fail
7054 // unnecessarily. However, this check occurs during startup, before we know
7055 // whether target consistency checks should be enabled, so it's always enabled
7058 char szMsgBuf[1024];
7059 _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE,
7060 "DAC fatal error: %s/mscordacwks.dll version mismatch\n\n"\
7061 "The debug directory timestamp of the loaded %s does not match the\n"\
7062 "version mscordacwks.dll was built for.\n"\
7063 "Expected %s timestamp: %s"\
7064 "Actual %s timestamp: %s\n"\
7065 "DAC will now fail to initialize with a CORDBG_E_MISMATCHED_CORWKS_AND_DACWKS_DLLS\n"\
7066 "error. If you really want to try and use the mimatched DLLs, you can disable this\n"\
7067 "check by setting COMPLUS_DbgDACSkipVerifyDlls=1. However, using a mismatched DAC\n"\
7068 "DLL will usually result in arbitrary debugger failures.\n",
7069 MAIN_CLR_DLL_NAME_A,
7070 MAIN_CLR_DLL_NAME_A,
7071 MAIN_CLR_DLL_NAME_A,
7073 MAIN_CLR_DLL_NAME_A,
7075 _ASSERTE_MSG(false, szMsgBuf);
7079 // Return a specific hresult indicating this problem
7080 return CORDBG_E_MISMATCHED_CORWKS_AND_DACWKS_DLLS;
7082 #endif // FEATURE_PAL
7087 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
7089 void ClrDataAccess::InitStreamsForWriting(IN CLRDataEnumMemoryFlags flags)
7091 // enforce this should only be called when generating triage and mini-dumps
7092 if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE)
7097 if (m_streams == NULL)
7098 m_streams = new DacStreamManager(g_MiniMetaDataBuffAddress, g_MiniMetaDataBuffMaxSize);
7100 if (!m_streams->PrepareStreamsForWriting())
7108 if (m_streams != NULL)
7114 EX_END_CATCH(SwallowAllExceptions)
7117 bool ClrDataAccess::MdCacheAddEEName(TADDR taEEStruct, const SString& name)
7119 bool result = false;
7122 if (m_streams != NULL)
7123 result = m_streams->MdCacheAddEEName(taEEStruct, name);
7129 EX_END_CATCH(SwallowAllExceptions)
7134 void ClrDataAccess::EnumStreams(IN CLRDataEnumMemoryFlags flags)
7136 // enforce this should only be called when generating triage and mini-dumps
7137 if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE)
7142 if (m_streams != NULL)
7143 m_streams->EnumStreams(flags);
7148 EX_END_CATCH(SwallowAllExceptions)
7151 bool ClrDataAccess::MdCacheGetEEName(TADDR taEEStruct, SString & eeName)
7153 bool result = false;
7156 if (m_streams == NULL)
7157 m_streams = new DacStreamManager(g_MiniMetaDataBuffAddress, g_MiniMetaDataBuffMaxSize);
7159 result = m_streams->MdCacheGetEEName(taEEStruct, eeName);
7165 EX_END_CATCH(SwallowAllExceptions)
7170 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
7172 // Needed for RT_RCDATA.
7173 #define MAKEINTRESOURCE(v) MAKEINTRESOURCEW(v)
7175 // this funny looking double macro forces x to be macro expanded before L is prepended
7176 #define _WIDE(x) _WIDE2(x)
7177 #define _WIDE2(x) W(x)
7180 ClrDataAccess::GetDacGlobals()
7183 // TODO - 3/5/15 - the DAC side needs the debuggee pid
7185 PVOID dacTableAddress = nullptr;
7186 ULONG dacTableSize = 0;
7187 DWORD err = PAL_GetDacTableAddress(pid, &dacTableAddress, &dacTableSize);
7188 if (err != ERROR_SUCCESS)
7190 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7193 if (dacTableSize != sizeof(g_dacGlobals))
7195 return E_INVALIDARG;
7198 if (FAILED(ReadFromDataTarget(m_pTarget, (ULONG64)dacTableAddress, (BYTE*)&g_dacGlobals, dacTableSize)))
7200 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7204 HRESULT status = E_FAIL;
7206 LPVOID rsrcData = NULL;
7212 DWORD resourceSectionRVA = 0;
7214 if (FAILED(status = GetMachineAndResourceSectionRVA(m_pTarget, m_globalBase, NULL, &resourceSectionRVA)))
7216 _ASSERTE_MSG(false, "DAC fatal error: can't locate resource section in " MAIN_CLR_DLL_NAME_A);
7217 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7220 if (FAILED(status = GetResourceRvaFromResourceSectionRvaByName(m_pTarget, m_globalBase,
7221 resourceSectionRVA, (DWORD)RT_RCDATA, _WIDE(DACCESS_TABLE_RESOURCE), 0,
7222 &rsrcRVA, &rsrcSize)))
7224 _ASSERTE_MSG(false, "DAC fatal error: can't locate DAC table resource in " MAIN_CLR_DLL_NAME_A);
7225 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7228 rsrcData = new (nothrow) BYTE[rsrcSize];
7229 if (rsrcData == NULL)
7230 return E_OUTOFMEMORY;
7232 if (FAILED(status = ReadFromDataTarget(m_pTarget, m_globalBase + rsrcRVA, (BYTE*)rsrcData, rsrcSize)))
7234 _ASSERTE_MSG(false, "DAC fatal error: can't load DAC table resource from " MAIN_CLR_DLL_NAME_A);
7235 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7239 PBYTE rawData = (PBYTE)rsrcData;
7240 DWORD bytesLeft = rsrcSize;
7243 struct DacTableHeader header;
7245 // We currently expect the header to be 2 32-bit values and 1 16-byte value,
7246 // make sure there is no packing going on or anything.
7247 static_assert_no_msg(sizeof(DacTableHeader) == 2 * 4 + 16);
7249 if (bytesLeft < sizeof(DacTableHeader))
7251 _ASSERTE_MSG(false, "DAC fatal error: DAC table too small for header.");
7254 memcpy(&header, rawData, sizeof(DacTableHeader));
7255 rawData += sizeof(DacTableHeader);
7256 bytesLeft -= sizeof(DacTableHeader);
7258 // Save the table info for later use
7259 g_dacTableInfo = header.info;
7261 // Sanity check that the DAC table is the size we expect.
7262 // This could fail if a different version of dacvars.h or vptr_list.h was used when building
7263 // mscordacwks.dll than when running DacTableGen.
7265 if (offsetof(DacGlobals, Thread__vtAddr) != header.numGlobals * sizeof(ULONG))
7268 char szMsgBuf[1024];
7269 _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE,
7270 "DAC fatal error: mismatch in number of globals in DAC table. Read from file: %d, expected: %d.",
7272 offsetof(DacGlobals, Thread__vtAddr) / sizeof(ULONG));
7273 _ASSERTE_MSG(false, szMsgBuf);
7276 status = E_INVALIDARG;
7280 if (sizeof(DacGlobals) != (header.numGlobals + header.numVptrs) * sizeof(ULONG))
7283 char szMsgBuf[1024];
7284 _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE,
7285 "DAC fatal error: mismatch in number of vptrs in DAC table. Read from file: %d, expected: %d.",
7287 (sizeof(DacGlobals) - offsetof(DacGlobals, Thread__vtAddr)) / sizeof(ULONG));
7288 _ASSERTE_MSG(false, szMsgBuf);
7291 status = E_INVALIDARG;
7295 // Copy the DAC table into g_dacGlobals
7296 if (bytesLeft < sizeof(DacGlobals))
7298 _ASSERTE_MSG(false, "DAC fatal error: DAC table resource too small for DacGlobals.");
7299 status = E_UNEXPECTED;
7302 memcpy(&g_dacGlobals, rawData, sizeof(DacGlobals));
7303 rawData += sizeof(DacGlobals);
7304 bytesLeft -= sizeof(DacGlobals);
7314 #undef MAKEINTRESOURCE
7316 //----------------------------------------------------------------------------
7318 // IsExceptionFromManagedCode - report if pExceptionRecord points to a exception belonging to the current runtime
7321 // pExceptionRecord - the exception record
7327 //----------------------------------------------------------------------------
7328 BOOL ClrDataAccess::IsExceptionFromManagedCode(EXCEPTION_RECORD* pExceptionRecord)
7335 if (::IsExceptionFromManagedCode(pExceptionRecord))
7347 //----------------------------------------------------------------------------
7349 // GetWatsonBuckets - retrieve Watson buckets from the specified thread
7352 // dwThreadId - the thread ID
7353 // pGM - pointer to the space to store retrieved Watson buckets
7356 // S_OK if the operation is successful.
7357 // or S_FALSE if Watson buckets cannot be found
7358 // else detailed error code.
7360 //----------------------------------------------------------------------------
7361 HRESULT ClrDataAccess::GetWatsonBuckets(DWORD dwThreadId, GenericModeBlock * pGM)
7363 _ASSERTE((dwThreadId != 0) && (pGM != NULL));
7364 if ((dwThreadId == 0) || (pGM == NULL))
7366 return E_INVALIDARG;
7371 Thread * pThread = DacGetThread(dwThreadId);
7372 _ASSERTE(pThread != NULL);
7374 HRESULT hr = E_UNEXPECTED;
7376 if (pThread != NULL)
7378 hr = GetClrWatsonBucketsWorker(pThread, pGM);
7385 #endif // FEATURE_PAL
7387 //----------------------------------------------------------------------------
7389 // CLRDataAccessCreateInstance - create and initialize a ClrDataAccess object
7392 // pLegacyTarget - data target object
7393 // pClrDataAccess - ClrDataAccess object
7396 // S_OK on success, else detailed error code.
7398 //----------------------------------------------------------------------------
7399 STDAPI CLRDataAccessCreateInstance(ICLRDataTarget * pLegacyTarget,
7400 ClrDataAccess ** pClrDataAccess)
7402 if ((pLegacyTarget == NULL) || (pClrDataAccess == NULL))
7404 return E_INVALIDARG;
7407 *pClrDataAccess = NULL;
7409 // Create an adapter which implements the new ICorDebugDataTarget interfaces using
7410 // a legacy implementation of ICLRDataTarget
7411 // ClrDataAccess will take a take a ref on this and delete it when it's released.
7412 DataTargetAdapter * pDtAdapter = new (nothrow) DataTargetAdapter(pLegacyTarget);
7415 return E_OUTOFMEMORY;
7418 ClrDataAccess* dacClass = new (nothrow) ClrDataAccess(pDtAdapter, pLegacyTarget);
7422 return E_OUTOFMEMORY;
7425 HRESULT hr = dacClass->Initialize();
7428 dacClass->Release();
7432 *pClrDataAccess = dacClass;
7437 //----------------------------------------------------------------------------
7439 // CLRDataCreateInstance.
7440 // Creates the IXClrData object
7441 // This is the legacy entrypoint to DAC, used by dbgeng/dbghelp (windbg, SOS, watson, etc).
7443 //----------------------------------------------------------------------------
7445 CLRDataCreateInstance(REFIID iid,
7446 ICLRDataTarget * pLegacyTarget,
7449 __attribute__((used))
7452 if ((pLegacyTarget == NULL) || (iface == NULL))
7454 return E_INVALIDARG;
7458 ClrDataAccess * pClrDataAccess;
7459 HRESULT hr = CLRDataAccessCreateInstance(pLegacyTarget, &pClrDataAccess);
7465 hr = pClrDataAccess->QueryInterface(iid, iface);
7467 pClrDataAccess->Release();
7472 //----------------------------------------------------------------------------
7474 // OutOfProcessExceptionEventGetProcessIdAndThreadId - get ProcessID and ThreadID
7477 // hProcess - process handle
7478 // hThread - thread handle
7479 // pPId - pointer to DWORD to store ProcessID
7480 // pThreadId - pointer to DWORD to store ThreadID
7483 // TRUE if the operation is successful.
7484 // FALSE if it fails
7486 //----------------------------------------------------------------------------
7487 BOOL OutOfProcessExceptionEventGetProcessIdAndThreadId(HANDLE hProcess, HANDLE hThread, DWORD * pPId, DWORD * pThreadId)
7489 _ASSERTE((pPId != NULL) && (pThreadId != NULL));
7492 // UNIXTODO: mikem 1/13/15 Need appropriate PAL functions for getting ids
7493 *pPId = (DWORD)hProcess;
7494 *pThreadId = (DWORD)hThread;
7496 #if !defined(FEATURE_CORESYSTEM)
7497 HMODULE hKernel32 = WszGetModuleHandle(W("kernel32.dll"));
7499 HMODULE hKernel32 = WszGetModuleHandle(W("api-ms-win-core-processthreads-l1-1-1.dll"));
7501 if (hKernel32 == NULL)
7506 typedef WINBASEAPI DWORD (WINAPI GET_PROCESSID_OF_THREAD)(HANDLE);
7507 GET_PROCESSID_OF_THREAD * pGetProcessIdOfThread;
7509 typedef WINBASEAPI DWORD (WINAPI GET_THREADID)(HANDLE);
7510 GET_THREADID * pGetThreadId;
7512 pGetProcessIdOfThread = (GET_PROCESSID_OF_THREAD *)GetProcAddress(hKernel32, "GetProcessIdOfThread");
7513 pGetThreadId = (GET_THREADID *)GetProcAddress(hKernel32, "GetThreadId");
7515 // OOP callbacks are used on Win7 or later. We should have having below two APIs available.
7516 _ASSERTE((pGetProcessIdOfThread != NULL) && (pGetThreadId != NULL));
7517 if ((pGetProcessIdOfThread == NULL) || (pGetThreadId == NULL))
7522 *pPId = (*pGetProcessIdOfThread)(hThread);
7523 *pThreadId = (*pGetThreadId)(hThread);
7524 #endif // FEATURE_PAL
7528 // WER_RUNTIME_EXCEPTION_INFORMATION will be available from Win7 SDK once Win7 SDK is released.
7529 #if !defined(WER_RUNTIME_EXCEPTION_INFORMATION)
7530 typedef struct _WER_RUNTIME_EXCEPTION_INFORMATION
7535 EXCEPTION_RECORD exceptionRecord;
7537 } WER_RUNTIME_EXCEPTION_INFORMATION, * PWER_RUNTIME_EXCEPTION_INFORMATION;
7538 #endif // !defined(WER_RUNTIME_EXCEPTION_INFORMATION)
7543 //----------------------------------------------------------------------------
7545 // OutOfProcessExceptionEventGetWatsonBucket - retrieve Watson buckets if it is a managed exception
7548 // pContext - the context passed at helper module registration
7549 // pExceptionInformation - structure that contains information about the crash
7550 // pGM - pointer to the space to store retrieved Watson buckets
7553 // S_OK if the operation is successful.
7554 // or S_FALSE if it is not a managed exception or Watson buckets cannot be found
7555 // else detailed error code.
7557 //----------------------------------------------------------------------------
7558 STDAPI OutOfProcessExceptionEventGetWatsonBucket(__in PDWORD pContext,
7559 __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,
7560 __out GenericModeBlock * pGMB)
7562 HANDLE hProcess = pExceptionInformation->hProcess;
7563 HANDLE hThread = pExceptionInformation->hThread;
7564 DWORD PId, ThreadId;
7566 if (!OutOfProcessExceptionEventGetProcessIdAndThreadId(hProcess, hThread, &PId, &ThreadId))
7571 CLRDATA_ADDRESS baseAddressOfRuntime = (CLRDATA_ADDRESS)pContext;
7572 NewHolder<LiveProcDataTarget> dataTarget(NULL);
7574 dataTarget = new (nothrow) LiveProcDataTarget(hProcess, PId, baseAddressOfRuntime);
7575 if (dataTarget == NULL)
7577 return E_OUTOFMEMORY;
7580 NewHolder<ClrDataAccess> pClrDataAccess(NULL);
7582 HRESULT hr = CLRDataAccessCreateInstance(dataTarget, &pClrDataAccess);
7595 if (!pClrDataAccess->IsExceptionFromManagedCode(&pExceptionInformation->exceptionRecord))
7600 return pClrDataAccess->GetWatsonBuckets(ThreadId, pGMB);
7603 //----------------------------------------------------------------------------
7605 // OutOfProcessExceptionEventCallback - claim the ownership of this event if current
7606 // runtime threw the unhandled exception
7609 // pContext - the context passed at helper module registration
7610 // pExceptionInformation - structure that contains information about the crash
7611 // pbOwnershipClaimed - output parameter for claiming the ownership of this event
7612 // pwszEventName - name of the event. If this is NULL, pchSize cannot be NULL.
7613 // This parameter is valid only if * pbOwnershipClaimed is TRUE.
7614 // pchSize - the size of the buffer pointed by pwszEventName
7615 // pdwSignatureCount - the count of signature parameters. Valid values range from
7616 // 0 to 10. If the value returned is greater than 10, only the
7617 // 1st 10 parameters are used for bucketing parameters. This
7618 // parameter is valid only if * pbOwnershipClaimed is TRUE.
7621 // S_OK on success, else detailed error code.
7624 // This is the 1st function that is called into by WER. This API through its out
7625 // parameters, tells WER as to whether or not it is claiming the crash. If it does
7626 // claim the crash, WER uses the event name specified in the string pointed to by
7627 // pwszEventName for error reporting. WER then proceed to call the
7628 // OutOfProcessExceptionEventSignatureCallback to get the bucketing parameters from
7631 // This function follows the multiple call paradigms. WER may call into this function
7632 // with *pwszEventName pointer set to NULL. This is to indicate to the function, that
7633 // WER wants to know the buffer size needed by the function to populate the string
7634 // into the buffer. The function should return E_INSUFFICIENTBUFFER with the needed
7635 // buffer size in *pchSize. WER shall then allocate a buffer of size *pchSize for
7636 // pwszEventName and then call this function again at which point the function should
7637 // populate the string and return S_OK.
7639 // Note that *pdOwnershipClaimed should be set to TRUE everytime this function is called
7640 // for the helper dll to claim ownership of bucketing.
7642 // The Win7 WER spec is at
7643 // http://windows/windows7/docs/COSD%20Documents/Fundamentals/Feedback%20Services%20and%20Platforms/WER-CLR%20Integration%20Dev%20Spec.docx
7646 // Since this is called by external modules it's important that we don't let any exceptions leak out (see Win8 95224).
7648 //----------------------------------------------------------------------------
7649 STDAPI OutOfProcessExceptionEventCallback(__in PDWORD pContext,
7650 __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,
7651 __out BOOL * pbOwnershipClaimed,
7652 __out_ecount(*pchSize) PWSTR pwszEventName,
7653 __inout PDWORD pchSize,
7654 __out PDWORD pdwSignatureCount)
7656 SUPPORTS_DAC_HOST_ONLY;
7658 if ((pContext == NULL) ||
7659 (pExceptionInformation == NULL) ||
7660 (pExceptionInformation->dwSize < sizeof(WER_RUNTIME_EXCEPTION_INFORMATION)) ||
7661 (pbOwnershipClaimed == NULL) ||
7662 (pchSize == NULL) ||
7663 (pdwSignatureCount == NULL))
7665 return E_INVALIDARG;
7668 *pbOwnershipClaimed = FALSE;
7670 GenericModeBlock gmb;
7671 HRESULT hr = E_FAIL;
7675 // get Watson buckets if it is a managed exception
7676 hr = OutOfProcessExceptionEventGetWatsonBucket(pContext, pExceptionInformation, &gmb);
7678 EX_CATCH_HRESULT(hr);
7682 // S_FALSE means either it is not a managed exception or we do not have Watson buckets.
7683 // Since we have set pbOwnershipClaimed to FALSE, we return S_OK to WER.
7692 if ((pwszEventName == NULL) || (*pchSize <= wcslen(gmb.wzEventTypeName)))
7694 *pchSize = static_cast<DWORD>(wcslen(gmb.wzEventTypeName)) + 1;
7695 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
7698 // copy custom event name
7699 wcscpy_s(pwszEventName, *pchSize, gmb.wzEventTypeName);
7700 *pdwSignatureCount = GetCountBucketParamsForEvent(gmb.wzEventTypeName);
7701 *pbOwnershipClaimed = TRUE;
7707 //----------------------------------------------------------------------------
7709 // OutOfProcessExceptionEventCallback - provide custom Watson buckets
7712 // pContext - the context passed at helper module registration
7713 // pExceptionInformation - structure that contains information about the crash
7714 // dwIndex - the index of the bucketing parameter being requested. Valid values are
7716 // pwszName - pointer to the name of the bucketing parameter
7717 // pchName - pointer to character count of the pwszName buffer. If pwszName points to
7718 // null, *pchName represents the buffer size (represented in number of characters)
7719 // needed to populate the name in pwszName.
7720 // pwszValue - pointer to the value of the pwszName bucketing parameter
7721 // pchValue - pointer to the character count of the pwszValue buffer. If pwszValue points
7722 // to null, *pchValue represents the buffer size (represented in number of
7723 // characters) needed to populate the value in pwszValue.
7726 // S_OK on success, else detailed error code.
7729 // This function is called by WER only if the call to OutOfProcessExceptionEventCallback()
7730 // was successful and the value of *pbOwnershipClaimed was TRUE. This function is called
7731 // pdwSignatureCount times to collect the bucketing parameters from the helper dll.
7733 // This function also follows the multiple call paradigm as described for the
7734 // OutOfProcessExceptionEventCallback() function. The buffer sizes needed for
7735 // this function are of the pwszName and pwszValue buffers.
7738 // Since this is called by external modules it's important that we don't let any exceptions leak out (see Win8 95224).
7740 //----------------------------------------------------------------------------
7741 STDAPI OutOfProcessExceptionEventSignatureCallback(__in PDWORD pContext,
7742 __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,
7744 __out_ecount(*pchName) PWSTR pwszName,
7745 __inout PDWORD pchName,
7746 __out_ecount(*pchValue) PWSTR pwszValue,
7747 __inout PDWORD pchValue)
7749 SUPPORTS_DAC_HOST_ONLY;
7751 if ((pContext == NULL) ||
7752 (pExceptionInformation == NULL) ||
7753 (pExceptionInformation->dwSize < sizeof(WER_RUNTIME_EXCEPTION_INFORMATION)) ||
7754 (pchName == NULL) ||
7757 return E_INVALIDARG;
7760 if ((pwszName == NULL) || (*pchName == 0))
7763 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
7766 GenericModeBlock gmb;
7767 const PWSTR pwszBucketValues[] = {gmb.wzP1,
7778 HRESULT hr = E_FAIL;
7782 // get Watson buckets if it is a managed exception
7783 hr = OutOfProcessExceptionEventGetWatsonBucket(pContext, pExceptionInformation, &gmb);
7785 EX_CATCH_HRESULT(hr);
7787 #ifndef FEATURE_WINDOWSPHONE
7788 // we can't assert this on phone as it's possible for the OS to kill
7789 // the faulting process before WER crash reporting has completed.
7790 _ASSERTE(hr == S_OK);
7792 _ASSERTE(hr == S_OK || hr == CORDBG_E_READVIRTUAL_FAILURE);
7796 // S_FALSE means either it is not a managed exception or we do not have Watson buckets.
7797 // Either case is a logic error becuase this function is called by WER only if the call
7798 // to OutOfProcessExceptionEventCallback() was successful and the value of
7799 // *pbOwnershipClaimed was TRUE.
7808 DWORD paramCount = GetCountBucketParamsForEvent(gmb.wzEventTypeName);
7810 if (dwIndex >= paramCount)
7812 _ASSERTE(!"dwIndex is out of range");
7813 return E_INVALIDARG;
7816 // Return pwszName as an emptry string to let WER use localized version of "Parameter n"
7817 *pwszName = W('\0');
7819 if ((pwszValue == NULL) || (*pchValue <= wcslen(pwszBucketValues[dwIndex])))
7821 *pchValue = static_cast<DWORD>(wcslen(pwszBucketValues[dwIndex]))+ 1;
7822 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
7825 // copy custom Watson bucket value
7826 wcscpy_s(pwszValue, *pchValue, pwszBucketValues[dwIndex]);
7831 #endif // FEATURE_PAL
7833 //----------------------------------------------------------------------------
7835 // OutOfProcessExceptionEventCallback - provide custom debugger launch string
7838 // pContext - the context passed at helper module registration
7839 // pExceptionInformation - structure that contains information about the crash
7840 // pbCustomDebuggerNeeded - pointer to a BOOL. If this BOOL is set to TRUE, then
7841 // a custom debugger launch option is needed by the
7842 // process. In that case, the subsequent parameters will
7843 // be meaningfully used. If this is FALSE, the subsequent
7844 // parameters will be ignored.
7845 // pwszDebuggerLaunch - pointer to a string that will be used to launch the debugger,
7846 // if the debugger is launched. The value of this string overrides
7847 // the default debugger launch string used by WER.
7848 // pchSize - pointer to the character count of the pwszDebuggerLaunch buffer. If
7849 // pwszDebuggerLaunch points to null, *pchSize represents the buffer size
7850 // (represented in number of characters) needed to populate the debugger
7851 // launch string in pwszDebuggerLaunch.
7852 // pbAutoLaunchDebugger - pointer to a BOOL. If this BOOL is set to TRUE, WER will
7853 // directly launch the debugger. If set to FALSE, WER will show
7854 // the debug option to the user in the WER UI.
7857 // S_OK on success, else detailed error code.
7860 // This function is called into by WER only if the call to OutOfProcessExceptionEventCallback()
7861 // was successful and the value of *pbOwnershipClaimed was TRUE. This function allows the helper
7862 // dll to customize the debugger launch options including the launch string.
7864 // This function also follows the multiple call paradigm as described for the
7865 // OutOfProcessExceptionEventCallback() function. The buffer sizes needed for
7866 // this function are of the pwszName and pwszValue buffers.
7868 //----------------------------------------------------------------------------
7869 STDAPI OutOfProcessExceptionEventDebuggerLaunchCallback(__in PDWORD pContext,
7870 __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,
7871 __out BOOL * pbCustomDebuggerNeeded,
7872 __out_ecount_opt(*pchSize) PWSTR pwszDebuggerLaunch,
7873 __inout PDWORD pchSize,
7874 __out BOOL * pbAutoLaunchDebugger)
7876 SUPPORTS_DAC_HOST_ONLY;
7878 if ((pContext == NULL) ||
7879 (pExceptionInformation == NULL) ||
7880 (pExceptionInformation->dwSize < sizeof(WER_RUNTIME_EXCEPTION_INFORMATION)) ||
7881 (pbCustomDebuggerNeeded == NULL) ||
7882 (pwszDebuggerLaunch == NULL) ||
7883 (pchSize == NULL) ||
7884 (pbAutoLaunchDebugger == NULL))
7886 return E_INVALIDARG;
7889 // Starting from CLRv4 managed debugger string and setting are unified with native debuggers.
7890 // There is no need to provide custom debugger string for WER.
7891 *pbCustomDebuggerNeeded = FALSE;
7898 #include "handletablepriv.h"
7899 #include "comcallablewrapper.h"
7901 DacHandleWalker::DacHandleWalker()
7902 : mDac(0), m_instanceAge(0), mMap(0), mIndex(0),
7903 mTypeMask(0), mGenerationFilter(-1), mChunkIndex(0), mCurr(0),
7909 DacHandleWalker::~DacHandleWalker()
7913 HandleChunkHead *curr = mHead.Next;
7917 HandleChunkHead *tmp = curr;
7923 HRESULT DacHandleWalker::Init(ClrDataAccess *dac, UINT types[], UINT typeCount)
7927 if (dac == NULL || types == NULL)
7931 m_instanceAge = dac->m_instanceAge;
7933 return Init(BuildTypemask(types, typeCount));
7936 HRESULT DacHandleWalker::Init(ClrDataAccess *dac, UINT types[], UINT typeCount, int gen)
7940 if (gen < 0 || gen > (int)GCHeap::GetMaxGeneration())
7941 return E_INVALIDARG;
7943 mGenerationFilter = gen;
7945 return Init(dac, types, typeCount);
7948 HRESULT DacHandleWalker::Init(UINT32 typemask)
7952 mMap = &g_HandleTableMap;
7953 mTypeMask = typemask;
7958 UINT32 DacHandleWalker::BuildTypemask(UINT types[], UINT typeCount)
7964 for (UINT i = 0; i < typeCount; ++i)
7966 _ASSERTE(types[i] < 32);
7967 mask |= (1 << types[i]);
7973 HRESULT DacHandleWalker::Next(unsigned int celt,
7974 SOSHandleData handles[],
7975 unsigned int *pceltFetched)
7979 if (handles == NULL || pceltFetched == NULL)
7984 hr = DoHandleWalk<SOSHandleData, unsigned int, DacHandleWalker::EnumCallbackSOS>(celt, handles, pceltFetched);
7991 bool DacHandleWalker::FetchMoreHandles(HANDLESCANPROC callback)
7995 // The table slots are based on the number of GC heaps in the process.
7998 #ifdef FEATURE_SVR_GC
7999 if (GCHeap::IsServerHeap())
8000 max_slots = GCHeapCount();
8001 #endif // FEATURE_SVR_GC
8003 // Reset the Count on all cached chunks. We reuse chunks after allocating
8004 // them, and the count is the only thing which needs resetting.
8005 for (HandleChunkHead *curr = &mHead; curr; curr = curr->Next)
8008 DacHandleWalkerParam param(&mHead);
8012 // Have we advanced past the end of the current bucket?
8013 if (mMap && mIndex >= INITIAL_HANDLE_TABLE_ARRAY_SIZE)
8019 // Have we walked the entire handle table map?
8026 if (mMap->pBuckets[mIndex] != NULL)
8028 for (int i = 0; i < max_slots; ++i)
8030 HHANDLETABLE hTable = mMap->pBuckets[mIndex]->pTable[i];
8033 // Yikes! The handle table callbacks don't produce the handle type or
8034 // the AppDomain that we need, and it's too difficult to propogate out
8035 // these things (especially the type) without worrying about performance
8036 // implications for the GC. Instead we'll have the callback walk each
8037 // type individually. There are only a few handle types, and the handle
8038 // table has a fast-path for only walking a single type anyway.
8039 UINT32 handleType = 0;
8040 for (UINT32 mask = mTypeMask; mask; mask >>= 1, handleType++)
8044 HandleTable *pTable = (HandleTable *)hTable;
8045 PTR_AppDomain pDomain = SystemDomain::GetAppDomainAtIndex(pTable->uADIndex);
8046 param.AppDomain = TO_CDADDR(pDomain.GetAddr());
8047 param.Type = handleType;
8049 // Either enumerate the handles regularly, or walk the handle
8050 // table as the GC does if a generation filter was requested.
8051 if (mGenerationFilter != -1)
8052 HndScanHandlesForGC(hTable, callback,
8055 mGenerationFilter, GCHeap::GetMaxGeneration(), 0);
8057 HndEnumHandles(hTable, &handleType, 1, callback, (LPARAM)¶m, 0, FALSE);
8064 // Stop looping as soon as we have found data. We also stop if we have a failed HRESULT during
8065 // the callback (this should indicate OOM).
8067 } while (mHead.Count == 0 && SUCCEEDED(param.Result));
8074 HRESULT DacHandleWalker::Skip(unsigned int celt)
8079 HRESULT DacHandleWalker::Reset()
8084 HRESULT DacHandleWalker::GetCount(unsigned int *pcelt)
8090 void DacHandleWalker::GetRefCountedHandleInfo(
8091 OBJECTREF oref, unsigned int uType,
8092 unsigned int *pRefCount, unsigned int *pJupiterRefCount, BOOL *pIsPegged, BOOL *pIsStrong)
8096 #ifdef FEATURE_COMINTEROP
8097 if (uType == HNDTYPE_REFCOUNTED)
8099 // get refcount from the CCW
8100 PTR_ComCallWrapper pWrap = ComCallWrapper::GetWrapperForObject(oref);
8104 *pRefCount = (unsigned int)pWrap->GetRefCount();
8106 if (pJupiterRefCount)
8107 *pJupiterRefCount = (unsigned int)pWrap->GetJupiterRefCount();
8110 *pIsPegged = pWrap->IsConsideredPegged();
8113 *pIsStrong = pWrap->IsWrapperActive();
8118 #endif // FEATURE_COMINTEROP
8123 if (pJupiterRefCount)
8124 *pJupiterRefCount = 0;
8133 void CALLBACK DacHandleWalker::EnumCallbackSOS(PTR_UNCHECKED_OBJECTREF handle, LPARAM *pExtraInfo, LPARAM param1, LPARAM param2)
8137 DacHandleWalkerParam *param = (DacHandleWalkerParam *)param1;
8138 HandleChunkHead *curr = param->Curr;
8140 // If we failed on a previous call (OOM) don't keep trying to allocate, it's not going to work.
8141 if (FAILED(param->Result))
8144 // We've moved past the size of the current chunk. We'll allocate a new chunk
8145 // and stuff the handles there. These are cleaned up by the destructor
8146 if (curr->Count >= (curr->Size/sizeof(SOSHandleData)))
8148 if (curr->Next == NULL)
8150 HandleChunk *next = new (nothrow) HandleChunk;
8157 param->Result = E_OUTOFMEMORY;
8162 curr = param->Curr = param->Curr->Next;
8165 // Fill the current handle.
8166 SOSHandleData *dataArray = (SOSHandleData*)curr->pData;
8167 SOSHandleData &data = dataArray[curr->Count++];
8169 data.Handle = TO_CDADDR(handle.GetAddr());
8170 data.Type = param->Type;
8171 if (param->Type == HNDTYPE_DEPENDENT)
8172 data.Secondary = GetDependentHandleSecondary(handle.GetAddr()).GetAddr();
8173 #ifdef FEATURE_COMINTEROP
8174 else if (param->Type == HNDTYPE_WEAK_WINRT)
8175 data.Secondary = HndGetHandleExtraInfo(handle.GetAddr());
8176 #endif // FEATURE_COMINTEROP
8179 data.AppDomain = param->AppDomain;
8180 GetRefCountedHandleInfo((OBJECTREF)*handle, param->Type, &data.RefCount, &data.JupiterRefCount, &data.IsPegged, &data.StrongReference);
8181 data.StrongReference |= (BOOL)IsAlwaysStrongReference(param->Type);
8184 DacStackReferenceWalker::DacStackReferenceWalker(ClrDataAccess *dac, DWORD osThreadID)
8185 : mDac(dac), m_instanceAge(dac ? dac->m_instanceAge : 0), mThread(0), mErrors(0), mEnumerated(false),
8186 mChunkIndex(0), mCurr(0), mIteratorIndex(0)
8188 Thread *curr = NULL;
8190 for (curr = ThreadStore::GetThreadList(curr);
8192 curr = ThreadStore::GetThreadList(curr))
8194 if (curr->GetOSThreadId() == osThreadID)
8202 DacStackReferenceWalker::~DacStackReferenceWalker()
8204 StackRefChunkHead *curr = mHead.next;
8208 StackRefChunkHead *tmp = curr;
8214 HRESULT DacStackReferenceWalker::Init()
8217 return E_INVALIDARG;
8218 return mHeap.Init();
8221 HRESULT STDMETHODCALLTYPE DacStackReferenceWalker::Skip(unsigned int count)
8226 HRESULT STDMETHODCALLTYPE DacStackReferenceWalker::Reset()
8231 HRESULT DacStackReferenceWalker::GetCount(unsigned int *pCount)
8240 // Fill out our data structures.
8241 WalkStack<unsigned int, SOSStackRefData>(0, NULL, DacStackReferenceWalker::GCReportCallbackSOS, DacStackReferenceWalker::GCEnumCallbackSOS);
8244 unsigned int count = 0;
8245 for(StackRefChunkHead *curr = &mHead; curr; curr = curr->next)
8246 count += curr->count;
8254 HRESULT DacStackReferenceWalker::Next(unsigned int count,
8255 SOSStackRefData stackRefs[],
8256 unsigned int *pFetched)
8258 if (stackRefs == NULL || pFetched == NULL)
8263 hr = DoStackWalk<unsigned int, SOSStackRefData,
8264 DacStackReferenceWalker::GCReportCallbackSOS,
8265 DacStackReferenceWalker::GCEnumCallbackSOS>
8266 (count, stackRefs, pFetched);
8273 HRESULT DacStackReferenceWalker::EnumerateErrors(ISOSStackRefErrorEnum **ppEnum)
8282 // Fill out our data structures.
8283 WalkStack<unsigned int, SOSStackRefData>(0, NULL, DacStackReferenceWalker::GCReportCallbackSOS, DacStackReferenceWalker::GCEnumCallbackSOS);
8286 DacStackReferenceErrorEnum *pEnum = new DacStackReferenceErrorEnum(this, mErrors);
8287 hr = pEnum->QueryInterface(__uuidof(ISOSStackRefErrorEnum), (void**)ppEnum);
8293 CLRDATA_ADDRESS DacStackReferenceWalker::ReadPointer(TADDR addr)
8295 ULONG32 bytesRead = 0;
8297 HRESULT hr = mDac->m_pTarget->ReadVirtual(addr, (BYTE*)&result, sizeof(TADDR), &bytesRead);
8299 if (FAILED(hr) || (bytesRead != sizeof(TADDR)))
8300 return (CLRDATA_ADDRESS)~0;
8302 return TO_CDADDR(result);
8306 void DacStackReferenceWalker::GCEnumCallbackSOS(LPVOID hCallback, OBJECTREF *pObject, DWORD flags, DacSlotLocation loc)
8308 GCCONTEXT *gcctx = (GCCONTEXT *)hCallback;
8309 DacScanContext *dsc = (DacScanContext*)gcctx->sc;
8311 // Yuck. The GcInfoDecoder reports a local pointer for registers (as it's reading out of the REGDISPLAY
8312 // in the stack walk), and it reports a TADDR for stack locations. This is architecturally difficulty
8313 // to fix, so we are leaving it for now.
8319 addr = (TADDR)pObject;
8320 obj = TO_TADDR(dsc->pWalker->ReadPointer((CORDB_ADDRESS)addr));
8324 obj = pObject->GetAddr();
8327 if (flags & GC_CALL_INTERIOR)
8329 CORDB_ADDRESS fixed_obj = 0;
8330 HRESULT hr = dsc->pWalker->mHeap.ListNearObjects((CORDB_ADDRESS)obj, NULL, &fixed_obj, NULL);
8332 // If we failed...oh well, SOS won't mind. We'll just report the interior pointer as is.
8334 obj = TO_TADDR(fixed_obj);
8337 SOSStackRefData *data = dsc->pWalker->GetNextObject<SOSStackRefData>(dsc);
8340 // Report where the object and where it was found.
8341 data->HasRegisterInformation = true;
8342 data->Register = loc.reg;
8343 data->Offset = loc.regOffset;
8344 data->Address = TO_CDADDR(addr);
8345 data->Object = TO_CDADDR(obj);
8346 data->Flags = flags;
8348 // Report the frame that the data came from.
8349 data->StackPointer = TO_CDADDR(dsc->sp);
8353 data->SourceType = SOS_StackSourceFrame;
8354 data->Source = dac_cast<PTR_Frame>(dsc->pFrame).GetAddr();
8358 data->SourceType = SOS_StackSourceIP;
8359 data->Source = TO_CDADDR(dsc->pc);
8365 void DacStackReferenceWalker::GCReportCallbackSOS(PTR_PTR_Object ppObj, ScanContext *sc, DWORD flags)
8367 DacScanContext *dsc = (DacScanContext*)sc;
8368 CLRDATA_ADDRESS obj = dsc->pWalker->ReadPointer(ppObj.GetAddr());
8370 if (flags & GC_CALL_INTERIOR)
8372 CORDB_ADDRESS fixed_addr = 0;
8373 HRESULT hr = dsc->pWalker->mHeap.ListNearObjects((CORDB_ADDRESS)obj, NULL, &fixed_addr, NULL);
8375 // If we failed...oh well, SOS won't mind. We'll just report the interior pointer as is.
8377 obj = TO_CDADDR(fixed_addr);
8380 SOSStackRefData *data = dsc->pWalker->GetNextObject<SOSStackRefData>(dsc);
8383 data->HasRegisterInformation = false;
8386 data->Address = ppObj.GetAddr();
8388 data->Flags = flags;
8389 data->StackPointer = TO_CDADDR(dsc->sp);
8393 data->SourceType = SOS_StackSourceFrame;
8394 data->Source = dac_cast<PTR_Frame>(dsc->pFrame).GetAddr();
8398 data->SourceType = SOS_StackSourceIP;
8399 data->Source = TO_CDADDR(dsc->pc);
8404 StackWalkAction DacStackReferenceWalker::Callback(CrawlFrame *pCF, VOID *pData)
8407 // KEEP IN SYNC WITH GcStackCrawlCallBack in vm\gcscan.cpp
8410 GCCONTEXT *gcctx = (GCCONTEXT*)pData;
8411 DacScanContext *dsc = (DacScanContext*)gcctx->sc;
8413 MethodDesc *pMD = pCF->GetFunction();
8414 gcctx->sc->pMD = pMD;
8415 gcctx->sc->pCurrentDomain = pCF->GetAppDomain();
8417 PREGDISPLAY pRD = pCF->GetRegisterSet();
8418 dsc->sp = (TADDR)GetRegdisplaySP(pRD);;
8419 dsc->pc = PCODEToPINSTR(GetControlPC(pRD));
8421 ResetPointerHolder<CrawlFrame*> rph(&gcctx->cf);
8424 bool fReportGCReferences = true;
8425 #if defined(WIN64EXCEPTIONS)
8426 // On Win64 and ARM, we may have unwound this crawlFrame and thus, shouldn't report the invalid
8427 // references it may contain.
8429 fReportGCReferences = pCF->ShouldCrawlframeReportGCReferences();
8430 #endif // defined(WIN64EXCEPTIONS)
8432 Frame *pFrame = ((DacScanContext*)gcctx->sc)->pFrame = pCF->GetFrame();
8436 if (fReportGCReferences)
8438 if (pCF->IsFrameless())
8440 ICodeManager * pCM = pCF->GetCodeManager();
8441 _ASSERTE(pCM != NULL);
8443 unsigned flags = pCF->GetCodeManagerFlags();
8445 pCM->EnumGcRefs(pCF->GetRegisterSet(),
8453 pFrame->GcScanRoots(gcctx->f, gcctx->sc);
8459 SOSStackErrorList *err = new SOSStackErrorList;
8464 err->error.SourceType = SOS_StackSourceFrame;
8465 err->error.Source = dac_cast<PTR_Frame>(pFrame).GetAddr();
8469 err->error.SourceType = SOS_StackSourceIP;
8470 err->error.Source = TO_CDADDR(dsc->pc);
8473 if (dsc->pWalker->mErrors == NULL)
8475 dsc->pWalker->mErrors = err;
8479 // This exception case should be non-existent. It only happens when there is either
8480 // a clr!Frame on the callstack which is not properly dac-ized, or when a call down
8481 // EnumGcRefs causes a data read exception. Since this is so rare, we don't worry
8482 // about making this code very efficient.
8483 SOSStackErrorList *curr = dsc->pWalker->mErrors;
8490 EX_END_CATCH(SwallowAllExceptions)
8495 // If we're executing a LCG dynamic method then we must promote the associated resolver to ensure it
8496 // doesn't get collected and yank the method code out from under us).
8498 // Be careful to only promote the reference -- we can also be called to relocate the reference and
8499 // that can lead to all sorts of problems since we could be racing for the relocation with the long
8500 // weak handle we recover the reference from. Promoting the reference is enough, the handle in the
8501 // reference will be relocated properly as long as we keep it alive till the end of the collection
8502 // as long as the reference is actually maintained by the long weak handle.
8505 BOOL fMaybeCollectibleMethod = TRUE;
8507 // If this is a frameless method then the jitmanager can answer the question of whether
8508 // or not this is LCG simply by looking at the heap where the code lives, however there
8509 // is also the prestub case where we need to explicitly look at the MD for stuff that isn't
8511 if (pCF->IsFrameless() && pMD->IsLCGMethod())
8513 fMaybeCollectibleMethod = ExecutionManager::IsCollectibleMethod(pCF->GetMethodToken());
8516 if (fMaybeCollectibleMethod && pMD->IsLCGMethod())
8518 PTR_Object obj = OBJECTREFToObject(pMD->AsDynamicMethodDesc()->GetLCGMethodResolver()->GetManagedResolver());
8519 dsc->pWalker->ReportObject(obj);
8523 if (fMaybeCollectibleMethod)
8525 PTR_Object obj = pMD->GetLoaderAllocator()->GetExposedObject();
8526 dsc->pWalker->ReportObject(obj);
8529 if (fReportGCReferences)
8531 GenericParamContextType paramContextType = GENERIC_PARAM_CONTEXT_NONE;
8533 if (pCF->IsFrameless())
8535 // We need to grab the Context Type here because there are cases where the MethodDesc
8536 // is shared, and thus indicates there should be an instantion argument, but the JIT
8537 // was still allowed to optimize it away and we won't grab it below because we're not
8538 // reporting any references from this frame.
8539 paramContextType = pCF->GetCodeManager()->GetParamContextType(pCF->GetRegisterSet(), pCF->GetCodeInfo());
8543 if (pMD->RequiresInstMethodDescArg())
8544 paramContextType = GENERIC_PARAM_CONTEXT_METHODDESC;
8545 else if (pMD->RequiresInstMethodTableArg())
8546 paramContextType = GENERIC_PARAM_CONTEXT_METHODTABLE;
8549 // Handle the case where the method is a static shared generic method and we need to keep the type of the generic parameters alive
8550 if (paramContextType == GENERIC_PARAM_CONTEXT_METHODDESC)
8552 MethodDesc *pMDReal = dac_cast<PTR_MethodDesc>(pCF->GetParamTypeArg());
8553 _ASSERTE((pMDReal != NULL) || !pCF->IsFrameless());
8554 if (pMDReal != NULL)
8556 PTR_Object obj = pMDReal->GetLoaderAllocator()->GetExposedObject();
8557 dsc->pWalker->ReportObject(obj);
8560 else if (paramContextType == GENERIC_PARAM_CONTEXT_METHODTABLE)
8562 MethodTable *pMTReal = dac_cast<PTR_MethodTable>(pCF->GetParamTypeArg());
8563 _ASSERTE((pMTReal != NULL) || !pCF->IsFrameless());
8564 if (pMTReal != NULL)
8566 PTR_Object obj = pMTReal->GetLoaderAllocator()->GetExposedObject();
8567 dsc->pWalker->ReportObject(obj);
8575 return SWA_CONTINUE;
8579 DacStackReferenceErrorEnum::DacStackReferenceErrorEnum(DacStackReferenceWalker *pEnum, SOSStackErrorList *pErrors)
8580 : mEnum(pEnum), mHead(pErrors), mCurr(pErrors)
8588 DacStackReferenceErrorEnum::~DacStackReferenceErrorEnum()
8594 HRESULT DacStackReferenceErrorEnum::Skip(unsigned int count)
8597 for (i = 0; i < count && mCurr; ++i)
8598 mCurr = mCurr->pNext;
8600 return i < count ? S_FALSE : S_OK;
8603 HRESULT DacStackReferenceErrorEnum::Reset()
8610 HRESULT DacStackReferenceErrorEnum::GetCount(unsigned int *pCount)
8612 SOSStackErrorList *curr = mHead;
8613 unsigned int count = 0;
8625 HRESULT DacStackReferenceErrorEnum::Next(unsigned int count, SOSStackRefError ref[], unsigned int *pFetched)
8627 if (pFetched == NULL || ref == NULL)
8631 for (i = 0; i < count && mCurr; ++i, mCurr = mCurr->pNext)
8632 ref[i] = mCurr->error;
8635 return i < count ? S_FALSE : S_OK;