1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5// File: daccess.cpp
6//
7
8//
9// ClrDataAccess implementation.
10//
11//*****************************************************************************
12
13#include "stdafx.h"
14#include <clrdata.h>
15#include "typestring.h"
16#include "holder.h"
17#include "debuginfostore.h"
18#include "peimagelayout.inl"
19#include "datatargetadapter.h"
20#include "readonlydatatargetfacade.h"
21#include "metadataexports.h"
22#include "excep.h"
23#include "debugger.h"
24#include "dwreport.h"
25#include "primitives.h"
26#include "dbgutil.h"
27#ifdef FEATURE_PAL
28#include <dactablerva.h>
29#endif
30
31#include "dwbucketmanager.hpp"
32#include "gcinterface.dac.h"
33
34// To include definiton of IsThrowableThreadAbortException
35// #include <exstatecommon.h>
36
37CRITICAL_SECTION g_dacCritSec;
38ClrDataAccess* g_dacImpl;
39HINSTANCE g_thisModule;
40
41extern VOID STDMETHODCALLTYPE TLS_FreeMasterSlotIndex();
42
43EXTERN_C BOOL WINAPI
44DllMain(HANDLE instance, DWORD reason, LPVOID reserved)
45{
46 static bool g_procInitialized = false;
47
48 switch(reason)
49 {
50 case DLL_PROCESS_ATTACH:
51 {
52 if (g_procInitialized)
53 {
54#ifdef FEATURE_PAL
55 // Double initialization can happen on Unix
56 // in case of manual load of DAC shared lib and calling DllMain
57 // not a big deal, we just ignore it.
58 return TRUE;
59#else
60 return FALSE;
61#endif
62 }
63
64#ifdef FEATURE_PAL
65 int err = PAL_InitializeDLL();
66 if(err != 0)
67 {
68 return FALSE;
69 }
70#endif
71 InitializeCriticalSection(&g_dacCritSec);
72
73 // Save the module handle.
74 g_thisModule = (HINSTANCE)instance;
75
76 g_procInitialized = true;
77 break;
78 }
79
80 case DLL_PROCESS_DETACH:
81 // It's possible for this to be called without ATTACH completing (eg. if it failed)
82 if (g_procInitialized)
83 {
84 DeleteCriticalSection(&g_dacCritSec);
85 }
86#ifndef FEATURE_PAL
87 TLS_FreeMasterSlotIndex();
88#endif
89 g_procInitialized = false;
90 break;
91 }
92
93 return TRUE;
94}
95
96HINSTANCE
97GetModuleInst(void)
98{
99 return g_thisModule;
100}
101
102HRESULT
103ConvertUtf8(__in LPCUTF8 utf8,
104 ULONG32 bufLen,
105 ULONG32* nameLen,
106 __out_ecount_part_opt(bufLen, *nameLen) PWSTR buffer)
107{
108 if (nameLen)
109 {
110 *nameLen = WszMultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
111 if (!*nameLen)
112 {
113 return HRESULT_FROM_GetLastError();
114 }
115 }
116
117 if (buffer && bufLen)
118 {
119 if (!WszMultiByteToWideChar(CP_UTF8, 0, utf8, -1, buffer, bufLen))
120 {
121 return HRESULT_FROM_GetLastError();
122 }
123 }
124
125 return S_OK;
126}
127
128HRESULT
129AllocUtf8(__in_opt LPCWSTR wstr,
130 ULONG32 srcChars,
131 __deref_out LPUTF8* utf8)
132{
133 ULONG32 chars = WszWideCharToMultiByte(CP_UTF8, 0, wstr, srcChars,
134 NULL, 0, NULL, NULL);
135 if (!chars)
136 {
137 return HRESULT_FROM_GetLastError();
138 }
139
140 // Make sure the converted string is always terminated.
141 if (srcChars != (ULONG32)-1)
142 {
143 if (!ClrSafeInt<ULONG32>::addition(chars, 1, chars))
144 {
145 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
146 }
147 }
148
149 char* mem = new (nothrow) char[chars];
150 if (!mem)
151 {
152 return E_OUTOFMEMORY;
153 }
154
155 if (!WszWideCharToMultiByte(CP_UTF8, 0, wstr, srcChars,
156 mem, chars, NULL, NULL))
157 {
158 HRESULT hr = HRESULT_FROM_GetLastError();
159 delete [] mem;
160 return hr;
161 }
162
163 if (srcChars != (ULONG32)-1)
164 {
165 mem[chars - 1] = 0;
166 }
167
168 *utf8 = mem;
169 return S_OK;
170}
171
172HRESULT
173GetFullClassNameFromMetadata(IMDInternalImport* mdImport,
174 mdTypeDef classToken,
175 ULONG32 bufferChars,
176 __inout_ecount(bufferChars) LPUTF8 buffer)
177{
178 HRESULT hr;
179 LPCUTF8 baseName, namespaceName;
180
181 IfFailRet(mdImport->GetNameOfTypeDef(classToken, &baseName, &namespaceName));
182 return ns::MakePath(buffer, bufferChars, namespaceName, baseName) ?
183 S_OK : E_OUTOFMEMORY;
184}
185
186HRESULT
187GetFullMethodNameFromMetadata(IMDInternalImport* mdImport,
188 mdMethodDef methodToken,
189 ULONG32 bufferChars,
190 __inout_ecount(bufferChars) LPUTF8 buffer)
191{
192 HRESULT status;
193 HRESULT hr;
194 mdTypeDef classToken;
195 size_t len;
196
197 if (mdImport->GetParentToken(methodToken, &classToken) == S_OK)
198 {
199 if ((status =
200 GetFullClassNameFromMetadata(mdImport, classToken,
201 bufferChars, buffer)) != S_OK)
202 {
203 return status;
204 }
205
206 len = strlen(buffer);
207 buffer += len;
208 bufferChars -= static_cast<ULONG32>(len) + 1;
209
210 if (!bufferChars)
211 {
212 return E_OUTOFMEMORY;
213 }
214
215 *buffer++ = NAMESPACE_SEPARATOR_CHAR;
216 }
217
218 LPCUTF8 methodName;
219 IfFailRet(mdImport->GetNameOfMethodDef(methodToken, &methodName));
220// Review conversion of size_t to ULONG32.
221#ifdef _MSC_VER
222#pragma warning(push)
223#pragma warning(disable:4267)
224#endif
225 len = strlen(methodName);
226#ifdef _MSC_VER
227#pragma warning(pop)
228#endif
229 if (len >= bufferChars)
230 {
231 return E_OUTOFMEMORY;
232 }
233
234 strcpy_s(buffer, bufferChars, methodName);
235 return S_OK;
236}
237
238HRESULT
239SplitFullName(__in_z __in PCWSTR fullName,
240 SplitSyntax syntax,
241 ULONG32 memberDots,
242 __deref_out_opt LPUTF8* namespaceName,
243 __deref_out_opt LPUTF8* typeName,
244 __deref_out_opt LPUTF8* memberName,
245 __deref_out_opt LPUTF8* params)
246{
247 HRESULT status;
248 PCWSTR paramsStart, memberStart, memberEnd, typeStart;
249
250 if (!*fullName)
251 {
252 return E_INVALIDARG;
253 }
254
255 //
256 // Split off parameters.
257 //
258
259 paramsStart = wcschr(fullName, W('('));
260 if (paramsStart)
261 {
262 if (syntax != SPLIT_METHOD ||
263 paramsStart == fullName)
264 {
265 return E_INVALIDARG;
266 }
267
268 if ((status = AllocUtf8(paramsStart, (ULONG32)-1, params)) != S_OK)
269 {
270 return status;
271 }
272
273 memberEnd = paramsStart - 1;
274 }
275 else
276 {
277 *params = NULL;
278 memberEnd = fullName + (wcslen(fullName) - 1);
279 }
280
281 if (syntax != SPLIT_TYPE)
282 {
283 //
284 // Split off member name.
285 //
286
287 memberStart = memberEnd;
288
289 for (;;)
290 {
291 while (memberStart >= fullName &&
292 *memberStart != W('.'))
293 {
294 memberStart--;
295 }
296
297 // Some member names (e.g. .ctor and .dtor) have
298 // dots, so go back to the first dot.
299 while (memberStart > fullName &&
300 memberStart[-1] == W('.'))
301 {
302 memberStart--;
303 }
304
305 if (memberStart <= fullName)
306 {
307 if (memberDots > 0)
308 {
309 // Caller expected dots in the
310 // member name and they weren't found.
311 status = E_INVALIDARG;
312 goto DelParams;
313 }
314
315 break;
316 }
317 else if (memberDots == 0)
318 {
319 break;
320 }
321
322 memberStart--;
323 memberDots--;
324 }
325
326 memberStart++;
327 if (memberStart > memberEnd)
328 {
329 status = E_INVALIDARG;
330 goto DelParams;
331 }
332
333 if ((status = AllocUtf8(memberStart, (ULONG32)
334 (memberEnd - memberStart) + 1,
335 memberName)) != S_OK)
336 {
337 goto DelParams;
338 }
339 }
340 else
341 {
342 *memberName = NULL;
343 memberStart = memberEnd + 2;
344 }
345
346 //
347 // Split off type name.
348 //
349
350 if (memberStart > fullName)
351 {
352 // Must have at least one character for the type
353 // name. If there was a member name, there must
354 // also be a separator.
355 if (memberStart < fullName + 2)
356 {
357 status = E_INVALIDARG;
358 goto DelMember;
359 }
360
361 typeStart = memberStart - 2;
362 while (typeStart >= fullName &&
363 *typeStart != W('.'))
364 {
365 typeStart--;
366 }
367 typeStart++;
368
369 if ((status = AllocUtf8(typeStart, (ULONG32)
370 (memberStart - typeStart) - 1,
371 typeName)) != S_OK)
372 {
373 goto DelMember;
374 }
375 }
376 else
377 {
378 *typeName = NULL;
379 typeStart = fullName;
380 }
381
382 //
383 // Namespace must be the rest.
384 //
385
386 if (typeStart > fullName)
387 {
388 if ((status = AllocUtf8(fullName, (ULONG32)
389 (typeStart - fullName) - 1,
390 namespaceName)) != S_OK)
391 {
392 goto DelType;
393 }
394 }
395 else
396 {
397 *namespaceName = NULL;
398 }
399
400 return S_OK;
401
402 DelType:
403 delete [] (*typeName);
404 DelMember:
405 delete [] (*memberName);
406 DelParams:
407 delete [] (*params);
408 return status;
409}
410
411int
412CompareUtf8(__in LPCUTF8 str1, __in LPCUTF8 str2, __in ULONG32 nameFlags)
413{
414 if (nameFlags & CLRDATA_BYNAME_CASE_INSENSITIVE)
415 {
416 // XXX Microsoft - Convert to Unicode?
417 return SString::_stricmp(str1, str2);
418 }
419
420 return strcmp(str1, str2);
421}
422
423//----------------------------------------------------------------------------
424//
425// MetaEnum.
426//
427//----------------------------------------------------------------------------
428
429HRESULT
430MetaEnum::Start(IMDInternalImport* mdImport, ULONG32 kind,
431 mdToken container)
432{
433 HRESULT status;
434
435 switch(kind)
436 {
437 case mdtTypeDef:
438 status = mdImport->EnumTypeDefInit(&m_enum);
439 break;
440 case mdtMethodDef:
441 case mdtFieldDef:
442 status = mdImport->EnumInit(kind, container, &m_enum);
443 break;
444 default:
445 return E_INVALIDARG;
446 }
447 if (status != S_OK)
448 {
449 return status;
450 }
451
452 m_mdImport = mdImport;
453 m_kind = kind;
454
455 return S_OK;
456}
457
458void
459MetaEnum::End(void)
460{
461 if (!m_mdImport)
462 {
463 return;
464 }
465
466 switch(m_kind)
467 {
468 case mdtTypeDef:
469 m_mdImport->EnumTypeDefClose(&m_enum);
470 break;
471 case mdtMethodDef:
472 case mdtFieldDef:
473 m_mdImport->EnumClose(&m_enum);
474 break;
475 }
476
477 Clear();
478}
479
480HRESULT
481MetaEnum::NextToken(mdToken* token,
482 __deref_opt_out_opt LPCUTF8* namespaceName,
483 __deref_opt_out_opt LPCUTF8* name)
484{
485 HRESULT hr;
486 if (!m_mdImport)
487 {
488 return E_INVALIDARG;
489 }
490
491 switch(m_kind)
492 {
493 case mdtTypeDef:
494 if (!m_mdImport->EnumTypeDefNext(&m_enum, token))
495 {
496 return S_FALSE;
497 }
498 m_lastToken = *token;
499 if (namespaceName || name)
500 {
501 LPCSTR _name, _namespaceName;
502
503 IfFailRet(m_mdImport->GetNameOfTypeDef(*token, &_name, &_namespaceName));
504 if (namespaceName)
505 {
506 *namespaceName = _namespaceName;
507 }
508 if (name)
509 {
510 *name = _name;
511 }
512 }
513 return S_OK;
514
515 case mdtMethodDef:
516 if (!m_mdImport->EnumNext(&m_enum, token))
517 {
518 return S_FALSE;
519 }
520 m_lastToken = *token;
521 if (namespaceName)
522 {
523 *namespaceName = NULL;
524 }
525 if (name != NULL)
526 {
527 IfFailRet(m_mdImport->GetNameOfMethodDef(*token, name));
528 }
529 return S_OK;
530
531 case mdtFieldDef:
532 if (!m_mdImport->EnumNext(&m_enum, token))
533 {
534 return S_FALSE;
535 }
536 m_lastToken = *token;
537 if (namespaceName)
538 {
539 *namespaceName = NULL;
540 }
541 if (name != NULL)
542 {
543 IfFailRet(m_mdImport->GetNameOfFieldDef(*token, name));
544 }
545 return S_OK;
546
547 default:
548 return E_INVALIDARG;
549 }
550}
551
552HRESULT
553MetaEnum::NextDomainToken(AppDomain** appDomain,
554 mdToken* token)
555{
556 HRESULT status;
557
558 if (m_appDomain)
559 {
560 // Use only the caller-provided app domain.
561 *appDomain = m_appDomain;
562 return NextToken(token, NULL, NULL);
563 }
564
565 //
566 // Splay tokens across all app domains.
567 //
568
569 for (;;)
570 {
571 if (m_lastToken == mdTokenNil)
572 {
573 // Need to fetch a token.
574 if ((status = NextToken(token, NULL, NULL)) != S_OK)
575 {
576 return status;
577 }
578
579 m_domainIter.Init();
580 }
581
582 if (m_domainIter.Next())
583 {
584 break;
585 }
586
587 m_lastToken = mdTokenNil;
588 }
589
590 *appDomain = m_domainIter.GetDomain();
591 *token = m_lastToken;
592
593 return S_OK;
594}
595
596HRESULT
597MetaEnum::NextTokenByName(__in_opt LPCUTF8 namespaceName,
598 __in_opt LPCUTF8 name,
599 ULONG32 nameFlags,
600 mdToken* token)
601{
602 HRESULT status;
603 LPCUTF8 tokNamespace, tokName;
604
605 for (;;)
606 {
607 if ((status = NextToken(token, &tokNamespace, &tokName)) != S_OK)
608 {
609 return status;
610 }
611
612 if (namespaceName &&
613 (!tokNamespace ||
614 CompareUtf8(namespaceName, tokNamespace, nameFlags) != 0))
615 {
616 continue;
617 }
618 if (name &&
619 (!tokName ||
620 CompareUtf8(name, tokName, nameFlags) != 0))
621 {
622 continue;
623 }
624
625 return S_OK;
626 }
627}
628
629HRESULT
630MetaEnum::NextDomainTokenByName(__in_opt LPCUTF8 namespaceName,
631 __in_opt LPCUTF8 name,
632 ULONG32 nameFlags,
633 AppDomain** appDomain, mdToken* token)
634{
635 HRESULT status;
636
637 if (m_appDomain)
638 {
639 // Use only the caller-provided app domain.
640 *appDomain = m_appDomain;
641 return NextTokenByName(namespaceName, name, nameFlags, token);
642 }
643
644 //
645 // Splay tokens across all app domains.
646 //
647
648 for (;;)
649 {
650 if (m_lastToken == mdTokenNil)
651 {
652 // Need to fetch a token.
653 if ((status = NextTokenByName(namespaceName, name, nameFlags,
654 token)) != S_OK)
655 {
656 return status;
657 }
658
659 m_domainIter.Init();
660 }
661
662 if (m_domainIter.Next())
663 {
664 break;
665 }
666
667 m_lastToken = mdTokenNil;
668 }
669
670 *appDomain = m_domainIter.GetDomain();
671 *token = m_lastToken;
672
673 return S_OK;
674}
675
676HRESULT
677MetaEnum::New(Module* mod,
678 ULONG32 kind,
679 mdToken container,
680 IXCLRDataAppDomain* pubAppDomain,
681 MetaEnum** metaEnumRet,
682 CLRDATA_ENUM* handle)
683{
684 HRESULT status;
685 MetaEnum* metaEnum;
686
687 if (handle)
688 {
689 *handle = TO_CDENUM(NULL);
690 }
691
692 if (!mod->GetFile()->HasMetadata())
693 {
694 return S_FALSE;
695 }
696
697 metaEnum = new (nothrow) MetaEnum;
698 if (!metaEnum)
699 {
700 return E_OUTOFMEMORY;
701 }
702
703 if ((status = metaEnum->
704 Start(mod->GetMDImport(), kind, container)) != S_OK)
705 {
706 delete metaEnum;
707 return status;
708 }
709
710 if (pubAppDomain)
711 {
712 metaEnum->m_appDomain =
713 ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain();
714 }
715
716 if (metaEnumRet)
717 {
718 *metaEnumRet = metaEnum;
719 }
720 if (handle)
721 {
722 *handle = TO_CDENUM(metaEnum);
723 }
724 return S_OK;
725}
726
727//----------------------------------------------------------------------------
728//
729// SplitName
730//
731//----------------------------------------------------------------------------
732
733SplitName::SplitName(SplitSyntax syntax, ULONG32 nameFlags,
734 ULONG32 memberDots)
735{
736 m_syntax = syntax;
737 m_nameFlags = nameFlags;
738 m_memberDots = memberDots;
739
740 Clear();
741}
742
743void
744SplitName::Delete(void)
745{
746 delete [] m_namespaceName;
747 m_namespaceName = NULL;
748 delete [] m_typeName;
749 m_typeName = NULL;
750 delete [] m_memberName;
751 m_memberName = NULL;
752 delete [] m_params;
753 m_params = NULL;
754}
755
756void
757SplitName::Clear(void)
758{
759 m_namespaceName = NULL;
760 m_typeName = NULL;
761 m_typeToken = mdTypeDefNil;
762 m_memberName = NULL;
763 m_memberToken = mdTokenNil;
764 m_params = NULL;
765
766 m_tlsThread = NULL;
767 m_metaEnum.m_appDomain = NULL;
768 m_module = NULL;
769 m_lastField = NULL;
770}
771
772HRESULT
773SplitName::SplitString(__in_opt PCWSTR fullName)
774{
775 if (m_syntax == SPLIT_NO_NAME)
776 {
777 if (fullName)
778 {
779 return E_INVALIDARG;
780 }
781
782 return S_OK;
783 }
784 else if (!fullName)
785 {
786 return E_INVALIDARG;
787 }
788
789 return SplitFullName(fullName,
790 m_syntax,
791 m_memberDots,
792 &m_namespaceName,
793 &m_typeName,
794 &m_memberName,
795 &m_params);
796}
797
798FORCEINLINE
799WCHAR* wcrscan(LPCWSTR beg, LPCWSTR end, WCHAR ch)
800{
801 //_ASSERTE(beg <= end);
802 WCHAR *p;
803 for (p = (WCHAR*)end; p >= beg; --p)
804 {
805 if (*p == ch)
806 break;
807 }
808 return p;
809}
810
811// This functions allocates a new UTF8 string that contains the classname
812// lying between the current sepName and the previous sepName. E.g. for a
813// class name of "Outer+middler+inner" when sepName points to the NULL
814// terminator this function will return "inner" in pResult and will update
815// sepName to point to the second '+' character in the string. When sepName
816// points to the first '+' character this function will return "Outer" in
817// pResult and sepName will point one WCHAR before fullName.
818HRESULT NextEnclosingClasName(LPCWSTR fullName, __deref_inout LPWSTR& sepName, __deref_out LPUTF8 *pResult)
819{
820 if (sepName < fullName)
821 {
822 return E_FAIL;
823 }
824 //_ASSERTE(*sepName == W('\0') || *sepName == W('+') || *sepName == W('/'));
825
826 LPWSTR origInnerName = sepName-1;
827 if ((sepName = wcrscan(fullName, origInnerName, W('+'))) < fullName)
828 {
829 sepName = wcrscan(fullName, origInnerName, W('/'));
830 }
831
832 return AllocUtf8(sepName+1, static_cast<ULONG32>(origInnerName-sepName), pResult);
833}
834
835bool
836SplitName::FindType(IMDInternalImport* mdInternal)
837{
838 if (m_typeToken != mdTypeDefNil)
839 {
840 return true;
841 }
842
843 if (!m_typeName)
844 {
845 return false;
846 }
847
848 if ((m_namespaceName == NULL || m_namespaceName[0] == '\0')
849 && (CompareUtf8(COR_MODULE_CLASS, m_typeName, m_nameFlags)==0))
850 {
851 m_typeToken = TokenFromRid(1, mdtTypeDef); // <Module> class always has a RID of 1.
852 return true;
853 }
854
855 MetaEnum metaEnum;
856
857 if (metaEnum.Start(mdInternal, mdtTypeDef, mdTypeDefNil) != S_OK)
858 {
859 return false;
860 }
861
862 LPUTF8 curClassName;
863
864 ULONG32 length;
865 WCHAR wszName[MAX_CLASS_NAME];
866 ConvertUtf8(m_typeName, MAX_CLASS_NAME, &length, wszName);
867
868 WCHAR *pHead;
869
870Retry:
871
872 pHead = wszName + length;
873
874 if (FAILED(NextEnclosingClasName(wszName, pHead, &curClassName)))
875 {
876 return false;
877 }
878
879 // an inner class has an empty namespace associated with it
880 HRESULT hr = metaEnum.NextTokenByName((pHead < wszName) ? m_namespaceName : "",
881 curClassName,
882 m_nameFlags,
883 &m_typeToken);
884 delete[] curClassName;
885
886 if (hr != S_OK)
887 {
888 // if we didn't find a token with the given name
889 return false;
890 }
891 else if (pHead < wszName)
892 {
893 // if we did find a token, *and* the class name given
894 // does not specify any enclosing class, that's it
895 return true;
896 }
897 else
898 {
899 // restart with innermost class
900 pHead = wszName + length;
901 mdTypeDef tkInner = m_typeToken;
902 mdTypeDef tkOuter;
903 BOOL bRetry = FALSE;
904 LPUTF8 utf8Name;
905
906 while (
907 !bRetry
908 && SUCCEEDED(NextEnclosingClasName(wszName, pHead, &utf8Name))
909 )
910 {
911 if (mdInternal->GetNestedClassProps(tkInner, &tkOuter) != S_OK)
912 tkOuter = mdTypeDefNil;
913
914 LPCSTR szName, szNS;
915 if (FAILED(mdInternal->GetNameOfTypeDef(tkInner, &szName, &szNS)))
916 {
917 return false;
918 }
919 bRetry = (CompareUtf8(utf8Name, szName, m_nameFlags) != 0);
920 if (!bRetry)
921 {
922 // if this is outermost class we need to compare namespaces too
923 if (tkOuter == mdTypeDefNil)
924 {
925 // is this the outermost in the class name, too?
926 if (pHead < wszName
927 && CompareUtf8(m_namespaceName ? m_namespaceName : "", szNS, m_nameFlags) == 0)
928 {
929 delete[] utf8Name;
930 return true;
931 }
932 else
933 {
934 bRetry = TRUE;
935 }
936 }
937 }
938 delete[] utf8Name;
939 tkInner = tkOuter;
940 }
941
942 goto Retry;
943 }
944
945}
946
947bool
948SplitName::FindMethod(IMDInternalImport* mdInternal)
949{
950 if (m_memberToken != mdTokenNil)
951 {
952 return true;
953 }
954
955 if (m_typeToken == mdTypeDefNil ||
956 !m_memberName)
957 {
958 return false;
959 }
960
961 ULONG32 EmptySig = 0;
962
963 // XXX Microsoft - Compare using signature when available.
964 if (mdInternal->FindMethodDefUsingCompare(m_typeToken,
965 m_memberName,
966 (PCCOR_SIGNATURE)&EmptySig,
967 sizeof(EmptySig),
968 NULL,
969 NULL,
970 &m_memberToken) != S_OK)
971 {
972 m_memberToken = mdTokenNil;
973 return false;
974 }
975
976 return true;
977}
978
979bool
980SplitName::FindField(IMDInternalImport* mdInternal)
981{
982 if (m_memberToken != mdTokenNil)
983 {
984 return true;
985 }
986
987 if (m_typeToken == mdTypeDefNil ||
988 !m_memberName ||
989 m_params)
990 {
991 // Can't have params with a field.
992 return false;
993 }
994
995 MetaEnum metaEnum;
996
997 if (metaEnum.Start(mdInternal, mdtFieldDef, m_typeToken) != S_OK)
998 {
999 return false;
1000 }
1001
1002 return metaEnum.NextTokenByName(NULL,
1003 m_memberName,
1004 m_nameFlags,
1005 &m_memberToken) == S_OK;
1006}
1007
1008HRESULT
1009SplitName::AllocAndSplitString(__in_opt PCWSTR fullName,
1010 SplitSyntax syntax,
1011 ULONG32 nameFlags,
1012 ULONG32 memberDots,
1013 SplitName** split)
1014{
1015 HRESULT status;
1016
1017 if (nameFlags & ~(CLRDATA_BYNAME_CASE_SENSITIVE |
1018 CLRDATA_BYNAME_CASE_INSENSITIVE))
1019 {
1020 return E_INVALIDARG;
1021 }
1022
1023 *split = new (nothrow) SplitName(syntax, nameFlags, memberDots);
1024 if (!*split)
1025 {
1026 return E_OUTOFMEMORY;
1027 }
1028
1029 if ((status = (*split)->SplitString(fullName)) != S_OK)
1030 {
1031 delete (*split);
1032 return status;
1033 }
1034
1035 return S_OK;
1036}
1037
1038HRESULT
1039SplitName::CdStartMethod(__in_opt PCWSTR fullName,
1040 ULONG32 nameFlags,
1041 Module* mod,
1042 mdTypeDef typeToken,
1043 AppDomain* appDomain,
1044 IXCLRDataAppDomain* pubAppDomain,
1045 SplitName** splitRet,
1046 CLRDATA_ENUM* handle)
1047{
1048 HRESULT status;
1049 SplitName* split;
1050 ULONG methDots = 0;
1051
1052 *handle = TO_CDENUM(NULL);
1053
1054 Retry:
1055 if ((status = SplitName::
1056 AllocAndSplitString(fullName, SPLIT_METHOD, nameFlags,
1057 methDots, &split)) != S_OK)
1058 {
1059 return status;
1060 }
1061
1062 if (typeToken == mdTypeDefNil)
1063 {
1064 if (!split->FindType(mod->GetMDImport()))
1065 {
1066 bool hasNamespace = split->m_namespaceName != NULL;
1067
1068 delete split;
1069
1070 //
1071 // We may have a case where there's an
1072 // explicitly implemented method which
1073 // has dots in the name. If it's possible
1074 // to move the method name dot split
1075 // back, go ahead and retry that way.
1076 //
1077
1078 if (hasNamespace)
1079 {
1080 methDots++;
1081 goto Retry;
1082 }
1083
1084 return E_INVALIDARG;
1085 }
1086
1087 typeToken = split->m_typeToken;
1088 }
1089 else
1090 {
1091 if (split->m_namespaceName || split->m_typeName)
1092 {
1093 delete split;
1094 return E_INVALIDARG;
1095 }
1096 }
1097
1098 if ((status = split->m_metaEnum.
1099 Start(mod->GetMDImport(), mdtMethodDef, typeToken)) != S_OK)
1100 {
1101 delete split;
1102 return status;
1103 }
1104
1105 split->m_metaEnum.m_appDomain = appDomain;
1106 if (pubAppDomain)
1107 {
1108 split->m_metaEnum.m_appDomain =
1109 ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain();
1110 }
1111 split->m_module = mod;
1112
1113 *handle = TO_CDENUM(split);
1114 if (splitRet)
1115 {
1116 *splitRet = split;
1117 }
1118 return S_OK;
1119}
1120
1121HRESULT
1122SplitName::CdNextMethod(CLRDATA_ENUM* handle,
1123 mdMethodDef* token)
1124{
1125 SplitName* split = FROM_CDENUM(SplitName, *handle);
1126 if (!split)
1127 {
1128 return E_INVALIDARG;
1129 }
1130
1131 return split->m_metaEnum.
1132 NextTokenByName(NULL, split->m_memberName, split->m_nameFlags,
1133 token);
1134}
1135
1136HRESULT
1137SplitName::CdNextDomainMethod(CLRDATA_ENUM* handle,
1138 AppDomain** appDomain,
1139 mdMethodDef* token)
1140{
1141 SplitName* split = FROM_CDENUM(SplitName, *handle);
1142 if (!split)
1143 {
1144 return E_INVALIDARG;
1145 }
1146
1147 return split->m_metaEnum.
1148 NextDomainTokenByName(NULL, split->m_memberName, split->m_nameFlags,
1149 appDomain, token);
1150}
1151
1152HRESULT
1153SplitName::CdStartField(__in_opt PCWSTR fullName,
1154 ULONG32 nameFlags,
1155 ULONG32 fieldFlags,
1156 IXCLRDataTypeInstance* fromTypeInst,
1157 TypeHandle typeHandle,
1158 Module* mod,
1159 mdTypeDef typeToken,
1160 ULONG64 objBase,
1161 Thread* tlsThread,
1162 IXCLRDataTask* pubTlsThread,
1163 AppDomain* appDomain,
1164 IXCLRDataAppDomain* pubAppDomain,
1165 SplitName** splitRet,
1166 CLRDATA_ENUM* handle)
1167{
1168 HRESULT status;
1169 SplitName* split;
1170
1171 *handle = TO_CDENUM(NULL);
1172
1173 if ((status = SplitName::
1174 AllocAndSplitString(fullName,
1175 fullName ? SPLIT_FIELD : SPLIT_NO_NAME,
1176 nameFlags, 0,
1177 &split)) != S_OK)
1178 {
1179 return status;
1180 }
1181
1182 if (typeHandle.IsNull())
1183 {
1184 if (typeToken == mdTypeDefNil)
1185 {
1186 if (!split->FindType(mod->GetMDImport()))
1187 {
1188 status = E_INVALIDARG;
1189 goto Fail;
1190 }
1191
1192 typeToken = split->m_typeToken;
1193 }
1194 else
1195 {
1196 if (split->m_namespaceName || split->m_typeName)
1197 {
1198 status = E_INVALIDARG;
1199 goto Fail;
1200 }
1201 }
1202
1203 // With phased class loading, this may return a partially-loaded type
1204 // @todo : does this matter?
1205 typeHandle = mod->LookupTypeDef(split->m_typeToken);
1206 if (typeHandle.IsNull())
1207 {
1208 status = E_UNEXPECTED;
1209 goto Fail;
1210 }
1211 }
1212
1213 if ((status = InitFieldIter(&split->m_fieldEnum,
1214 typeHandle,
1215 true,
1216 fieldFlags,
1217 fromTypeInst)) != S_OK)
1218 {
1219 goto Fail;
1220 }
1221
1222 split->m_objBase = objBase;
1223 split->m_tlsThread = tlsThread;
1224 if (pubTlsThread)
1225 {
1226 split->m_tlsThread = ((ClrDataTask*)pubTlsThread)->GetThread();
1227 }
1228 split->m_metaEnum.m_appDomain = appDomain;
1229 if (pubAppDomain)
1230 {
1231 split->m_metaEnum.m_appDomain =
1232 ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain();
1233 }
1234 split->m_module = mod;
1235
1236 *handle = TO_CDENUM(split);
1237 if (splitRet)
1238 {
1239 *splitRet = split;
1240 }
1241 return S_OK;
1242
1243 Fail:
1244 delete split;
1245 return status;
1246}
1247
1248HRESULT
1249SplitName::CdNextField(ClrDataAccess* dac,
1250 CLRDATA_ENUM* handle,
1251 IXCLRDataTypeDefinition** fieldType,
1252 ULONG32* fieldFlags,
1253 IXCLRDataValue** value,
1254 ULONG32 nameBufRetLen,
1255 ULONG32* nameLenRet,
1256 __out_ecount_part_opt(nameBufRetLen, *nameLenRet) WCHAR nameBufRet[ ],
1257 IXCLRDataModule** tokenScopeRet,
1258 mdFieldDef* tokenRet)
1259{
1260 HRESULT status;
1261
1262 SplitName* split = FROM_CDENUM(SplitName, *handle);
1263 if (!split)
1264 {
1265 return E_INVALIDARG;
1266 }
1267
1268 FieldDesc* fieldDesc;
1269
1270 while ((fieldDesc = split->m_fieldEnum.Next()))
1271 {
1272 if (split->m_syntax != SPLIT_NO_NAME)
1273 {
1274 LPCUTF8 fieldName;
1275 if (FAILED(fieldDesc->GetName_NoThrow(&fieldName)) ||
1276 (split->Compare(split->m_memberName, fieldName) != 0))
1277 {
1278 continue;
1279 }
1280 }
1281
1282 split->m_lastField = fieldDesc;
1283
1284 if (fieldFlags != NULL)
1285 {
1286 *fieldFlags =
1287 GetTypeFieldValueFlags(fieldDesc->GetFieldTypeHandleThrowing(),
1288 fieldDesc,
1289 split->m_fieldEnum.
1290 IsFieldFromParentClass() ?
1291 CLRDATA_FIELD_IS_INHERITED : 0,
1292 false);
1293 }
1294
1295 if ((nameBufRetLen != 0) || (nameLenRet != NULL))
1296 {
1297 LPCUTF8 szFieldName;
1298 status = fieldDesc->GetName_NoThrow(&szFieldName);
1299 if (status != S_OK)
1300 {
1301 return status;
1302 }
1303
1304 status = ConvertUtf8(
1305 szFieldName,
1306 nameBufRetLen,
1307 nameLenRet,
1308 nameBufRet);
1309 if (status != S_OK)
1310 {
1311 return status;
1312 }
1313 }
1314
1315 if (tokenScopeRet && !value)
1316 {
1317 *tokenScopeRet = new (nothrow)
1318 ClrDataModule(dac, fieldDesc->GetModule());
1319 if (!*tokenScopeRet)
1320 {
1321 return E_OUTOFMEMORY;
1322 }
1323 }
1324
1325 if (tokenRet)
1326 {
1327 *tokenRet = fieldDesc->GetMemberDef();
1328 }
1329
1330 if (fieldType)
1331 {
1332 TypeHandle fieldTypeHandle = fieldDesc->GetFieldTypeHandleThrowing();
1333 *fieldType = new (nothrow)
1334 ClrDataTypeDefinition(dac,
1335 fieldTypeHandle.GetModule(),
1336 fieldTypeHandle.GetMethodTable()->GetCl(),
1337 fieldTypeHandle);
1338 if (!*fieldType && tokenScopeRet)
1339 {
1340 delete (ClrDataModule*)*tokenScopeRet;
1341 }
1342 return *fieldType ? S_OK : E_OUTOFMEMORY;
1343 }
1344
1345 if (value)
1346 {
1347 return ClrDataValue::
1348 NewFromFieldDesc(dac,
1349 split->m_metaEnum.m_appDomain,
1350 split->m_fieldEnum.IsFieldFromParentClass() ?
1351 CLRDATA_VALUE_IS_INHERITED : 0,
1352 fieldDesc,
1353 split->m_objBase,
1354 split->m_tlsThread,
1355 NULL,
1356 value,
1357 nameBufRetLen,
1358 nameLenRet,
1359 nameBufRet,
1360 tokenScopeRet,
1361 tokenRet);
1362 }
1363
1364 return S_OK;
1365 }
1366
1367 return S_FALSE;
1368}
1369
1370HRESULT
1371SplitName::CdNextDomainField(ClrDataAccess* dac,
1372 CLRDATA_ENUM* handle,
1373 IXCLRDataValue** value)
1374{
1375 HRESULT status;
1376
1377 SplitName* split = FROM_CDENUM(SplitName, *handle);
1378 if (!split)
1379 {
1380 return E_INVALIDARG;
1381 }
1382
1383 if (split->m_metaEnum.m_appDomain)
1384 {
1385 // Use only the caller-provided app domain.
1386 return CdNextField(dac, handle, NULL, NULL, value,
1387 0, NULL, NULL, NULL, NULL);
1388 }
1389
1390 //
1391 // Splay fields across all app domains.
1392 //
1393
1394 for (;;)
1395 {
1396 if (!split->m_lastField)
1397 {
1398 // Need to fetch a field.
1399 if ((status = CdNextField(dac, handle, NULL, NULL, NULL,
1400 0, NULL, NULL, NULL, NULL)) != S_OK)
1401 {
1402 return status;
1403 }
1404
1405 split->m_metaEnum.m_domainIter.Init();
1406 }
1407
1408 if (split->m_metaEnum.m_domainIter.Next())
1409 {
1410 break;
1411 }
1412
1413 split->m_lastField = NULL;
1414 }
1415
1416 return ClrDataValue::
1417 NewFromFieldDesc(dac,
1418 split->m_metaEnum.m_domainIter.GetDomain(),
1419 split->m_fieldEnum.IsFieldFromParentClass() ?
1420 CLRDATA_VALUE_IS_INHERITED : 0,
1421 split->m_lastField,
1422 split->m_objBase,
1423 split->m_tlsThread,
1424 NULL,
1425 value,
1426 0,
1427 NULL,
1428 NULL,
1429 NULL,
1430 NULL);
1431}
1432
1433HRESULT
1434SplitName::CdStartType(__in_opt PCWSTR fullName,
1435 ULONG32 nameFlags,
1436 Module* mod,
1437 AppDomain* appDomain,
1438 IXCLRDataAppDomain* pubAppDomain,
1439 SplitName** splitRet,
1440 CLRDATA_ENUM* handle)
1441{
1442 HRESULT status;
1443 SplitName* split;
1444
1445 *handle = TO_CDENUM(NULL);
1446
1447 if ((status = SplitName::
1448 AllocAndSplitString(fullName, SPLIT_TYPE, nameFlags, 0,
1449 &split)) != S_OK)
1450 {
1451 return status;
1452 }
1453
1454 if ((status = split->m_metaEnum.
1455 Start(mod->GetMDImport(), mdtTypeDef, mdTokenNil)) != S_OK)
1456 {
1457 delete split;
1458 return status;
1459 }
1460
1461 split->m_metaEnum.m_appDomain = appDomain;
1462 if (pubAppDomain)
1463 {
1464 split->m_metaEnum.m_appDomain =
1465 ((ClrDataAppDomain*)pubAppDomain)->GetAppDomain();
1466 }
1467 split->m_module = mod;
1468
1469 *handle = TO_CDENUM(split);
1470 if (splitRet)
1471 {
1472 *splitRet = split;
1473 }
1474 return S_OK;
1475}
1476
1477HRESULT
1478SplitName::CdNextType(CLRDATA_ENUM* handle,
1479 mdTypeDef* token)
1480{
1481 SplitName* split = FROM_CDENUM(SplitName, *handle);
1482 if (!split)
1483 {
1484 return E_INVALIDARG;
1485 }
1486
1487 return split->m_metaEnum.
1488 NextTokenByName(split->m_namespaceName, split->m_typeName,
1489 split->m_nameFlags, token);
1490}
1491
1492HRESULT
1493SplitName::CdNextDomainType(CLRDATA_ENUM* handle,
1494 AppDomain** appDomain,
1495 mdTypeDef* token)
1496{
1497 SplitName* split = FROM_CDENUM(SplitName, *handle);
1498 if (!split)
1499 {
1500 return E_INVALIDARG;
1501 }
1502
1503 return split->m_metaEnum.
1504 NextDomainTokenByName(split->m_namespaceName, split->m_typeName,
1505 split->m_nameFlags, appDomain, token);
1506}
1507
1508//----------------------------------------------------------------------------
1509//
1510// DacInstanceManager.
1511//
1512// Data retrieved from the target process is cached for two reasons:
1513//
1514// 1. It may be necessary to map from the host address back to the target
1515// address. For example, if any code uses a 'this' pointer or
1516// takes the address of a field the address has to be translated from
1517// host to target. This requires instances to be held as long as
1518// they may be referenced.
1519//
1520// 2. Data is often referenced multiple times so caching is an important
1521// performance advantage.
1522//
1523// Ideally we'd like to implement a simple page cache but this is
1524// complicated by the fact that user minidump memory can have
1525// arbitrary granularity and also that the member operator (->)
1526// needs to return a pointer to an object. That means that all of
1527// the data for an object must be sequential and cannot be split
1528// at page boundaries.
1529//
1530// Data can also be accessed with different sizes. For example,
1531// a base struct can be accessed, then cast to a derived struct and
1532// accessed again with the larger derived size. The cache must
1533// be able to replace data to maintain the largest amount of data
1534// touched.
1535//
1536// We keep track of each access and the recovered memory for it.
1537// A hash on target address allows quick access to instance data
1538// by target address. The data for each access has a header on it
1539// for bookkeeping purposes, so host address to target address translation
1540// is just a matter of backing up to the header and pulling the target
1541// address from it. Keeping each access separately allows easy
1542// replacement by larger accesses.
1543//
1544//----------------------------------------------------------------------------
1545
1546DacInstanceManager::DacInstanceManager(void)
1547 : m_unusedBlock(NULL)
1548{
1549 InitEmpty();
1550}
1551
1552DacInstanceManager::~DacInstanceManager(void)
1553{
1554 // We are stopping debugging in this case, so don't save any block of memory.
1555 // Otherwise, there will be a memory leak.
1556 Flush(false);
1557}
1558
1559#if defined(DAC_HASHTABLE)
1560DAC_INSTANCE*
1561DacInstanceManager::Add(DAC_INSTANCE* inst)
1562{
1563 // Assert that we don't add NULL instances. This allows us to assert that found instances
1564 // are not NULL in DacInstanceManager::Find
1565 _ASSERTE(inst != NULL);
1566
1567 DWORD nHash = DAC_INSTANCE_HASH(inst->addr);
1568 HashInstanceKeyBlock* block = m_hash[nHash];
1569
1570 if (!block || block->firstElement == 0)
1571 {
1572
1573 HashInstanceKeyBlock* newBlock;
1574 if (block)
1575 {
1576 newBlock = (HashInstanceKeyBlock*) new (nothrow) BYTE[HASH_INSTANCE_BLOCK_ALLOC_SIZE];
1577 }
1578 else
1579 {
1580 // We allocate one big memory chunk that has a block for every index of the hash table to
1581 // improve data locality and reduce the number of allocs. In most cases, a hash bucket will
1582 // use only one block, so improving data locality across blocks (i.e. keeping the buckets of the
1583 // hash table together) should help.
1584 newBlock = (HashInstanceKeyBlock*)
1585 ClrVirtualAlloc(NULL, HASH_INSTANCE_BLOCK_ALLOC_SIZE*NumItems(m_hash), MEM_COMMIT, PAGE_READWRITE);
1586 }
1587 if (!newBlock)
1588 {
1589 return NULL;
1590 }
1591 if (block)
1592 {
1593 // We add the newest block to the start of the list assuming that most accesses are for
1594 // recently added elements.
1595 newBlock->next = block;
1596 m_hash[nHash] = newBlock; // The previously allocated block
1597 newBlock->firstElement = HASH_INSTANCE_BLOCK_NUM_ELEMENTS;
1598 block = newBlock;
1599 }
1600 else
1601 {
1602 for (DWORD j = 0; j < NumItems(m_hash); j++)
1603 {
1604 m_hash[j] = newBlock;
1605 newBlock->next = NULL; // The previously allocated block
1606 newBlock->firstElement = HASH_INSTANCE_BLOCK_NUM_ELEMENTS;
1607 newBlock = (HashInstanceKeyBlock*) (((BYTE*) newBlock) + HASH_INSTANCE_BLOCK_ALLOC_SIZE);
1608 }
1609 block = m_hash[nHash];
1610 }
1611 }
1612 _ASSERTE(block->firstElement > 0);
1613 block->firstElement--;
1614 block->instanceKeys[block->firstElement].addr = inst->addr;
1615 block->instanceKeys[block->firstElement].instance = inst;
1616
1617 inst->next = NULL;
1618 return inst;
1619}
1620#else //DAC_HASHTABLE
1621DAC_INSTANCE*
1622DacInstanceManager::Add(DAC_INSTANCE* inst)
1623{
1624 _ASSERTE(inst != NULL);
1625#ifdef _DEBUG
1626 bool isInserted = (m_hash.find(inst->addr) == m_hash.end());
1627#endif //_DEBUG
1628 DAC_INSTANCE *(&target) = m_hash[inst->addr];
1629 _ASSERTE(!isInserted || target == NULL);
1630 if( target != NULL )
1631 {
1632 //This is necessary to preserve the semantics of Supersede, however, it
1633 //is more or less dead code.
1634 inst->next = target;
1635 target = inst;
1636
1637 //verify descending order
1638 _ASSERTE(inst->size >= target->size);
1639 }
1640 else
1641 {
1642 target = inst;
1643 }
1644
1645 return inst;
1646}
1647
1648#endif // #if defined(DAC_HASHTABLE)
1649
1650
1651DAC_INSTANCE*
1652DacInstanceManager::Alloc(TADDR addr, ULONG32 size, DAC_USAGE_TYPE usage)
1653{
1654 SUPPORTS_DAC_HOST_ONLY;
1655 DAC_INSTANCE_BLOCK* block;
1656 DAC_INSTANCE* inst;
1657 ULONG32 fullSize;
1658
1659 static_assert_no_msg(sizeof(DAC_INSTANCE_BLOCK) <= DAC_INSTANCE_ALIGN);
1660 static_assert_no_msg((sizeof(DAC_INSTANCE) & (DAC_INSTANCE_ALIGN - 1)) == 0);
1661
1662 //
1663 // All allocated instances must be kept alive as long
1664 // as anybody may have a host pointer for one of them.
1665 // This means that we cannot delete an arbitrary instance
1666 // unless we are sure no pointers exist, which currently
1667 // is not possible to determine, thus we just hold everything
1668 // until a Flush. This greatly simplifies instance allocation
1669 // as we can then just sweep through large blocks rather
1670 // than having to use a real allocator. The only
1671 // complication is that we need to keep all instance
1672 // data aligned. We have guaranteed that the header will
1673 // preserve alignment of the data following if the header
1674 // is aligned, so as long as we round up all allocations
1675 // to a multiple of the alignment size everything just works.
1676 //
1677
1678 fullSize = (size + DAC_INSTANCE_ALIGN - 1) & ~(DAC_INSTANCE_ALIGN - 1);
1679 _ASSERTE(fullSize && fullSize <= 0xffffffff - 2 * sizeof(*inst));
1680 fullSize += sizeof(*inst);
1681
1682 //
1683 // Check for an existing block with space.
1684 //
1685
1686 for (block = m_blocks; block; block = block->next)
1687 {
1688 if (fullSize <= block->bytesFree)
1689 {
1690 break;
1691 }
1692 }
1693
1694 if (!block)
1695 {
1696 //
1697 // No existing block has enough space, so allocate a new
1698 // one if necessary and link it in. We know we're allocating large
1699 // blocks so directly VirtualAlloc. We save one block through a
1700 // flush so that we spend less time allocating/deallocating.
1701 //
1702
1703 ULONG32 blockSize = fullSize + DAC_INSTANCE_ALIGN;
1704 if (blockSize < DAC_INSTANCE_BLOCK_ALLOCATION)
1705 {
1706 blockSize = DAC_INSTANCE_BLOCK_ALLOCATION;
1707 }
1708
1709 // If we have a saved block and it's large enough, use it.
1710 block = m_unusedBlock;
1711 if ((block != NULL) &&
1712 ((block->bytesUsed + block->bytesFree) >= blockSize))
1713 {
1714 m_unusedBlock = NULL;
1715
1716 // Right now, we're locked to DAC_INSTANCE_BLOCK_ALLOCATION but
1717 // that might change in the future if we decide to do something
1718 // else with the size guarantee in code:DacInstanceManager::FreeAllBlocks
1719 blockSize = block->bytesUsed + block->bytesFree;
1720 }
1721 else
1722 {
1723 block = (DAC_INSTANCE_BLOCK*)
1724 ClrVirtualAlloc(NULL, blockSize, MEM_COMMIT, PAGE_READWRITE);
1725 }
1726
1727 if (!block)
1728 {
1729 return NULL;
1730 }
1731
1732 // Keep the first aligned unit for the block header.
1733 block->bytesUsed = DAC_INSTANCE_ALIGN;
1734 block->bytesFree = blockSize - DAC_INSTANCE_ALIGN;
1735
1736 block->next = m_blocks;
1737 m_blocks = block;
1738
1739 m_blockMemUsage += blockSize;
1740 }
1741
1742 inst = (DAC_INSTANCE*)((PBYTE)block + block->bytesUsed);
1743 block->bytesUsed += fullSize;
1744 _ASSERTE(block->bytesFree >= fullSize);
1745 block->bytesFree -= fullSize;
1746
1747 inst->next = NULL;
1748 inst->addr = addr;
1749 inst->size = size;
1750 inst->sig = DAC_INSTANCE_SIG;
1751 inst->usage = usage;
1752 inst->enumMem = 0;
1753 inst->MDEnumed = 0;
1754
1755 m_numInst++;
1756 m_instMemUsage += fullSize;
1757 return inst;
1758}
1759
1760void
1761DacInstanceManager::ReturnAlloc(DAC_INSTANCE* inst)
1762{
1763 SUPPORTS_DAC_HOST_ONLY;
1764 DAC_INSTANCE_BLOCK* block;
1765 DAC_INSTANCE_BLOCK * pPrevBlock;
1766 ULONG32 fullSize;
1767
1768 //
1769 // This special routine handles cleanup in
1770 // cases where an instances has been allocated
1771 // but must be returned due to a following error.
1772 // The given instance must be the last instance
1773 // in an existing block.
1774 //
1775
1776 fullSize =
1777 ((inst->size + DAC_INSTANCE_ALIGN - 1) & ~(DAC_INSTANCE_ALIGN - 1)) +
1778 sizeof(*inst);
1779
1780 pPrevBlock = NULL;
1781 for (block = m_blocks; block; pPrevBlock = block, block = block->next)
1782 {
1783 if ((PBYTE)inst == (PBYTE)block + (block->bytesUsed - fullSize))
1784 {
1785 break;
1786 }
1787 }
1788
1789 if (!block)
1790 {
1791 return;
1792 }
1793
1794 block->bytesUsed -= fullSize;
1795 block->bytesFree += fullSize;
1796 m_numInst--;
1797 m_instMemUsage -= fullSize;
1798
1799 // If the block is empty after returning the specified instance, that means this block was newly created
1800 // when this instance was allocated. We have seen cases where we are asked to allocate a
1801 // large chunk of memory only to fail to read the memory from a dump later on, i.e. when both the target
1802 // address and the size are invalid. If we keep the allocation, we'll grow the VM size unnecessarily.
1803 // Thus, release a block if it's empty and if it's not the default size (to avoid thrashing memory).
1804 // See Dev10 Dbug 812112 for more information.
1805 if ((block->bytesUsed == DAC_INSTANCE_ALIGN) &&
1806 ((block->bytesFree + block->bytesUsed) != DAC_INSTANCE_BLOCK_ALLOCATION))
1807 {
1808 // The empty block is at the beginning of the list.
1809 if (pPrevBlock == NULL)
1810 {
1811 m_blocks = block->next;
1812 }
1813 else
1814 {
1815 _ASSERTE(pPrevBlock->next == block);
1816 pPrevBlock->next = block->next;
1817 }
1818 ClrVirtualFree(block, 0, MEM_RELEASE);
1819 }
1820}
1821
1822
1823#if defined(DAC_HASHTABLE)
1824DAC_INSTANCE*
1825DacInstanceManager::Find(TADDR addr)
1826{
1827
1828#if defined(DAC_MEASURE_PERF)
1829 unsigned _int64 nStart, nEnd;
1830 g_nFindCalls++;
1831 nStart = GetCycleCount();
1832#endif // #if defined(DAC_MEASURE_PERF)
1833
1834 HashInstanceKeyBlock* block = m_hash[DAC_INSTANCE_HASH(addr)];
1835
1836#if defined(DAC_MEASURE_PERF)
1837 nEnd = GetCycleCount();
1838 g_nFindHashTotalTime += nEnd - nStart;
1839#endif // #if defined(DAC_MEASURE_PERF)
1840
1841 while (block)
1842 {
1843 DWORD nIndex = block->firstElement;
1844 for (; nIndex < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; nIndex++)
1845 {
1846 if (block->instanceKeys[nIndex].addr == addr)
1847 {
1848 #if defined(DAC_MEASURE_PERF)
1849 nEnd = GetCycleCount();
1850 g_nFindHits++;
1851 g_nFindTotalTime += nEnd - nStart;
1852 if (g_nStackWalk) g_nFindStackTotalTime += nEnd - nStart;
1853#endif // #if defined(DAC_MEASURE_PERF)
1854
1855 DAC_INSTANCE* inst = block->instanceKeys[nIndex].instance;
1856
1857 // inst should not be NULL even if the address was superseded. We search
1858 // the entries in the reverse order they were added. So we should have
1859 // found the superseding entry before this one. (Of course, if a NULL instance
1860 // has been added, this assert is meaningless. DacInstanceManager::Add
1861 // asserts that NULL instances aren't added.)
1862
1863 _ASSERTE(inst != NULL);
1864
1865 return inst;
1866 }
1867 }
1868 block = block->next;
1869 }
1870
1871#if defined(DAC_MEASURE_PERF)
1872 nEnd = GetCycleCount();
1873 g_nFindFails++;
1874 g_nFindTotalTime += nEnd - nStart;
1875 if (g_nStackWalk) g_nFindStackTotalTime += nEnd - nStart;
1876#endif // #if defined(DAC_MEASURE_PERF)
1877
1878 return NULL;
1879}
1880#else //DAC_HASHTABLE
1881DAC_INSTANCE*
1882DacInstanceManager::Find(TADDR addr)
1883{
1884 DacInstanceHashIterator iter = m_hash.find(addr);
1885 if( iter == m_hash.end() )
1886 {
1887 return NULL;
1888 }
1889 else
1890 {
1891 return iter->second;
1892 }
1893}
1894#endif // if defined(DAC_HASHTABLE)
1895
1896HRESULT
1897DacInstanceManager::Write(DAC_INSTANCE* inst, bool throwEx)
1898{
1899 HRESULT status;
1900
1901 if (inst->usage == DAC_VPTR)
1902 {
1903 // Skip over the host-side vtable pointer when
1904 // writing back.
1905 status = DacWriteAll(inst->addr + sizeof(TADDR),
1906 (PBYTE)(inst + 1) + sizeof(PVOID),
1907 inst->size - sizeof(TADDR),
1908 throwEx);
1909 }
1910 else
1911 {
1912 // Write the whole instance back.
1913 status = DacWriteAll(inst->addr, inst + 1, inst->size, throwEx);
1914 }
1915
1916 return status;
1917}
1918
1919#if defined(DAC_HASHTABLE)
1920void
1921DacInstanceManager::Supersede(DAC_INSTANCE* inst)
1922{
1923 _ASSERTE(inst != NULL);
1924
1925 //
1926 // This instance has been superseded by a larger
1927 // one and so must be removed from the hash. However,
1928 // code may be holding the instance pointer so it
1929 // can't just be deleted. Put it on a list for
1930 // later cleanup.
1931 //
1932
1933 HashInstanceKeyBlock* block = m_hash[DAC_INSTANCE_HASH(inst->addr)];
1934 while (block)
1935 {
1936 DWORD nIndex = block->firstElement;
1937 for (; nIndex < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; nIndex++)
1938 {
1939 if (block->instanceKeys[nIndex].instance == inst)
1940 {
1941 block->instanceKeys[nIndex].instance = NULL;
1942 break;
1943 }
1944 }
1945 if (nIndex < HASH_INSTANCE_BLOCK_NUM_ELEMENTS)
1946 {
1947 break;
1948 }
1949 block = block->next;
1950 }
1951
1952 AddSuperseded(inst);
1953}
1954#else //DAC_HASHTABLE
1955void
1956DacInstanceManager::Supersede(DAC_INSTANCE* inst)
1957{
1958 _ASSERTE(inst != NULL);
1959
1960 //
1961 // This instance has been superseded by a larger
1962 // one and so must be removed from the hash. However,
1963 // code may be holding the instance pointer so it
1964 // can't just be deleted. Put it on a list for
1965 // later cleanup.
1966 //
1967
1968 DacInstanceHashIterator iter = m_hash.find(inst->addr);
1969 if( iter == m_hash.end() )
1970 return;
1971
1972 DAC_INSTANCE** bucket = &(iter->second);
1973 DAC_INSTANCE* cur = *bucket;
1974 DAC_INSTANCE* prev = NULL;
1975 //walk through the chain looking for this particular instance
1976 while (cur)
1977 {
1978 if (cur == inst)
1979 {
1980 if (!prev)
1981 {
1982 *bucket = inst->next;
1983 }
1984 else
1985 {
1986 prev->next = inst->next;
1987 }
1988 break;
1989 }
1990
1991 prev = cur;
1992 cur = cur->next;
1993 }
1994
1995 AddSuperseded(inst);
1996}
1997#endif // if defined(DAC_HASHTABLE)
1998
1999// This is the default Flush() called when the DAC cache is invalidated,
2000// e.g. when we continue the debuggee process. In this case, we want to
2001// save one block of memory to avoid thrashing. See the usage of m_unusedBlock
2002// for more information.
2003void DacInstanceManager::Flush(void)
2004{
2005 Flush(true);
2006}
2007
2008void DacInstanceManager::Flush(bool fSaveBlock)
2009{
2010 SUPPORTS_DAC_HOST_ONLY;
2011
2012 //
2013 // All allocated memory is in the block
2014 // list, so just free the blocks and
2015 // forget all the internal pointers.
2016 //
2017
2018 for (;;)
2019 {
2020 FreeAllBlocks(fSaveBlock);
2021
2022 DAC_INSTANCE_PUSH* push = m_instPushed;
2023 if (!push)
2024 {
2025 break;
2026 }
2027
2028 m_instPushed = push->next;
2029 m_blocks = push->blocks;
2030 delete push;
2031 }
2032
2033 // If we are not saving any memory blocks, then clear the saved buffer block (if any) as well.
2034 if (!fSaveBlock)
2035 {
2036 if (m_unusedBlock != NULL)
2037 {
2038 ClrVirtualFree(m_unusedBlock, 0, MEM_RELEASE);
2039 m_unusedBlock = NULL;
2040 }
2041 }
2042
2043#if defined(DAC_HASHTABLE)
2044 for (int i = NumItems(m_hash) - 1; i >= 0; i--)
2045 {
2046 HashInstanceKeyBlock* block = m_hash[i];
2047 HashInstanceKeyBlock* next;
2048 while (block)
2049 {
2050 next = block->next;
2051 if (next)
2052 {
2053 delete [] block;
2054 }
2055 else if (i == 0)
2056 {
2057 ClrVirtualFree(block, 0, MEM_RELEASE);
2058 }
2059 block = next;
2060 }
2061 }
2062#else //DAC_HASHTABLE
2063 m_hash.clear();
2064#endif //DAC_HASHTABLE
2065
2066 InitEmpty();
2067}
2068
2069#if defined(DAC_HASHTABLE)
2070void
2071DacInstanceManager::ClearEnumMemMarker(void)
2072{
2073 ULONG i;
2074 DAC_INSTANCE* inst;
2075
2076 for (i = 0; i < NumItems(m_hash); i++)
2077 {
2078 HashInstanceKeyBlock* block = m_hash[i];
2079 while (block)
2080 {
2081 DWORD j;
2082 for (j = block->firstElement; j < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; j++)
2083 {
2084 inst = block->instanceKeys[j].instance;
2085 if (inst != NULL)
2086 {
2087 inst->enumMem = 0;
2088 }
2089 }
2090 block = block->next;
2091 }
2092 }
2093 for (inst = m_superseded; inst; inst = inst->next)
2094 {
2095 inst->enumMem = 0;
2096 }
2097}
2098#else //DAC_HASHTABLE
2099void
2100DacInstanceManager::ClearEnumMemMarker(void)
2101{
2102 ULONG i;
2103 DAC_INSTANCE* inst;
2104
2105 DacInstanceHashIterator end = m_hash.end();
2106 /* REVISIT_TODO Fri 10/20/2006
2107 * This might have an issue, since it might miss chained entries off of
2108 * ->next. However, ->next is going away, and for all intents and
2109 * purposes, this never happens.
2110*/
2111 for( DacInstanceHashIterator cur = m_hash.begin(); cur != end; ++cur )
2112 {
2113 cur->second->enumMem = 0;
2114 }
2115
2116 for (inst = m_superseded; inst; inst = inst->next)
2117 {
2118 inst->enumMem = 0;
2119 }
2120}
2121#endif // if defined(DAC_HASHTABLE)
2122
2123
2124#if defined(DAC_HASHTABLE)
2125//
2126//
2127// Iterating through all of the hash entry and report the memory
2128// instance to minidump
2129//
2130// This function returns the total number of bytes that it reported.
2131//
2132//
2133UINT
2134DacInstanceManager::DumpAllInstances(
2135 ICLRDataEnumMemoryRegionsCallback *pCallBack) // memory report call back
2136{
2137 ULONG i;
2138 DAC_INSTANCE* inst;
2139 UINT cbTotal = 0;
2140
2141#if defined(DAC_MEASURE_PERF)
2142 FILE* fp = fopen("c:\\dumpLog.txt", "a");
2143 int total = 0;
2144#endif // #if defined(DAC_MEASURE_PERF)
2145
2146 for (i = 0; i < NumItems(m_hash); i++)
2147 {
2148
2149#if defined(DAC_MEASURE_PERF)
2150 int numInBucket = 0;
2151#endif // #if defined(DAC_MEASURE_PERF)
2152
2153 HashInstanceKeyBlock* block = m_hash[i];
2154 while (block)
2155 {
2156 DWORD j;
2157 for (j = block->firstElement; j < HASH_INSTANCE_BLOCK_NUM_ELEMENTS; j++)
2158 {
2159 inst = block->instanceKeys[j].instance;
2160
2161 // Only report those we intended to.
2162 // So far, only metadata is excluded!
2163 //
2164 if (inst && inst->noReport == 0)
2165 {
2166 cbTotal += inst->size;
2167 HRESULT hr = pCallBack->EnumMemoryRegion(TO_CDADDR(inst->addr), inst->size);
2168 if (hr == COR_E_OPERATIONCANCELED)
2169 {
2170 ThrowHR(hr);
2171 }
2172 }
2173
2174#if defined(DAC_MEASURE_PERF)
2175 if (inst)
2176 {
2177 numInBucket++;
2178 }
2179#endif // #if defined(DAC_MEASURE_PERF)
2180 }
2181 block = block->next;
2182 }
2183
2184 #if defined(DAC_MEASURE_PERF)
2185 fprintf(fp, "%4d: %4d%s", i, numInBucket, (i+1)%5? "; " : "\n");
2186 total += numInBucket;
2187#endif // #if defined(DAC_MEASURE_PERF)
2188
2189 }
2190
2191#if defined(DAC_MEASURE_PERF)
2192 fprintf(fp, "\n\nTotal entries: %d\n\n", total);
2193 fclose(fp);
2194#endif // #if defined(DAC_MEASURE_PERF)
2195
2196 return cbTotal;
2197
2198}
2199#else //DAC_HASHTABLE
2200//
2201//
2202// Iterating through all of the hash entry and report the memory
2203// instance to minidump
2204//
2205// This function returns the total number of bytes that it reported.
2206//
2207//
2208UINT
2209DacInstanceManager::DumpAllInstances(
2210 ICLRDataEnumMemoryRegionsCallback *pCallBack) // memory report call back
2211{
2212 SUPPORTS_DAC_HOST_ONLY;
2213
2214 DAC_INSTANCE* inst;
2215 UINT cbTotal = 0;
2216
2217#if defined(DAC_MEASURE_PERF)
2218 FILE* fp = fopen("c:\\dumpLog.txt", "a");
2219#endif // #if defined(DAC_MEASURE_PERF)
2220
2221#if defined(DAC_MEASURE_PERF)
2222 int numInBucket = 0;
2223#endif // #if defined(DAC_MEASURE_PERF)
2224
2225 DacInstanceHashIterator end = m_hash.end();
2226 for (DacInstanceHashIterator cur = m_hash.begin(); end != cur; ++cur)
2227 {
2228 inst = cur->second;
2229
2230 // Only report those we intended to.
2231 // So far, only metadata is excluded!
2232 //
2233 if (inst->noReport == 0)
2234 {
2235 cbTotal += inst->size;
2236 HRESULT hr = pCallBack->EnumMemoryRegion(TO_CDADDR(inst->addr), inst->size);
2237 if (hr == COR_E_OPERATIONCANCELED)
2238 {
2239 ThrowHR(hr);
2240 }
2241 }
2242
2243#if defined(DAC_MEASURE_PERF)
2244 numInBucket++;
2245#endif // #if defined(DAC_MEASURE_PERF)
2246 }
2247
2248#if defined(DAC_MEASURE_PERF)
2249 fprintf(fp, "\n\nTotal entries: %d\n\n", numInBucket);
2250 fclose(fp);
2251#endif // #if defined(DAC_MEASURE_PERF)
2252
2253 return cbTotal;
2254
2255}
2256#endif // if defined(DAC_HASHTABLE)
2257
2258DAC_INSTANCE_BLOCK*
2259DacInstanceManager::FindInstanceBlock(DAC_INSTANCE* inst)
2260{
2261 for (DAC_INSTANCE_BLOCK* block = m_blocks; block; block = block->next)
2262 {
2263 if ((PBYTE)inst >= (PBYTE)block &&
2264 (PBYTE)inst < (PBYTE)block + block->bytesUsed)
2265 {
2266 return block;
2267 }
2268 }
2269
2270 return NULL;
2271}
2272
2273// If fSaveBlock is false, free all blocks of allocated memory. Otherwise,
2274// free all blocks except the one we save to avoid thrashing memory.
2275// Callers very frequently flush repeatedly with little memory needed in DAC
2276// so this avoids wasteful repeated allocations/deallocations.
2277// There is a very unlikely case that we'll have allocated an extremely large
2278// block; if this is the only block we will save none since this block will
2279// remain allocated.
2280void
2281DacInstanceManager::FreeAllBlocks(bool fSaveBlock)
2282{
2283 DAC_INSTANCE_BLOCK* block;
2284
2285 while ((block = m_blocks))
2286 {
2287 m_blocks = block->next;
2288
2289 // If we haven't saved our single block yet and this block is the default size
2290 // then we will save it instead of freeing it. This avoids saving an unnecessarily large
2291 // memory block.
2292 // Do *NOT* trash the byte counts. code:DacInstanceManager::Alloc
2293 // depends on them being correct when checking to see if a block is large enough.
2294 if (fSaveBlock &&
2295 (m_unusedBlock == NULL) &&
2296 ((block->bytesFree + block->bytesUsed) == DAC_INSTANCE_BLOCK_ALLOCATION))
2297 {
2298 // Just to avoid confusion, since we're keeping it around.
2299 block->next = NULL;
2300 m_unusedBlock = block;
2301 }
2302 else
2303 {
2304 ClrVirtualFree(block, 0, MEM_RELEASE);
2305 }
2306 }
2307}
2308
2309//----------------------------------------------------------------------------
2310//
2311// DacStreamManager.
2312//
2313//----------------------------------------------------------------------------
2314
2315#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
2316
2317namespace serialization { namespace bin {
2318
2319 //========================================================================
2320 // Support functions for binary serialization of simple types to a buffer:
2321 // - raw_size() returns the size in bytes of the binary representation
2322 // of a value.
2323 // - raw_serialize() copies the binary representation of a value into a
2324 // buffer.
2325 // - raw_deserialize() generates a value from its binary representation
2326 // in a buffer.
2327 // Beyond simple types the APIs below support SString instances. SStrings
2328 // are stored as UTF8 strings.
2329 //========================================================================
2330
2331 static const size_t ErrOverflow = (size_t)(-1);
2332
2333#ifndef FEATURE_PAL
2334
2335 // Template class is_blittable
2336 template <typename _Ty, typename Enable = void>
2337 struct is_blittable
2338 : std::false_type
2339 { // determines whether _Ty is blittable
2340 };
2341
2342 template <typename _Ty>
2343 struct is_blittable<_Ty, typename std::enable_if<std::is_arithmetic<_Ty>::value>::type>
2344 : std::true_type
2345 { // determines whether _Ty is blittable
2346 };
2347
2348 // allow types to declare themselves blittable by including a static bool
2349 // member "is_blittable".
2350 template <typename _Ty>
2351 struct is_blittable<_Ty, typename std::enable_if<_Ty::is_blittable>::type>
2352 : std::true_type
2353 { // determines whether _Ty is blittable
2354 };
2355
2356
2357 //========================================================================
2358 // serialization::bin::Traits<T> enables binary serialization and
2359 // deserialization of instances of T.
2360 //========================================================================
2361
2362 //
2363 // General specialization for non-blittable types - must be overridden
2364 // for each specific non-blittable type.
2365 //
2366 template <typename T, typename Enable = void>
2367 class Traits
2368 {
2369 public:
2370 static FORCEINLINE size_t
2371 raw_size(const T & val)
2372 {
2373 static_assert(false, "Non-blittable types need explicit specializations");
2374 }
2375 };
2376
2377 //
2378 // General type trait supporting serialization/deserialization of blittable
2379 // type arguments (as defined by the is_blittable<> type traits above).
2380 //
2381 template <typename T>
2382 class Traits<T, typename std::enable_if<is_blittable<T>::value>::type>
2383 {
2384#else // FEATURE_PAL
2385 template <typename T>
2386 class Traits
2387 {
2388#endif // !FEATURE_PAL
2389 public:
2390 //
2391 // raw_size() returns the size in bytes of the binary representation of a
2392 // value.
2393 //
2394 static FORCEINLINE size_t
2395 raw_size(const T & val)
2396 {
2397 return sizeof(T);
2398 }
2399
2400 //
2401 // raw_serialize() copies the binary representation of a value into a
2402 // "dest" buffer that has "destSize" bytes available.
2403 // Returns raw_size(val), or ErrOverflow if the buffer does not have
2404 // enough space to accommodate "val".
2405 //
2406 static FORCEINLINE size_t
2407 raw_serialize(BYTE* dest, size_t destSize, const T & val)
2408 {
2409 size_t cnt = raw_size(val);
2410
2411 if (destSize < cnt)
2412 {
2413 return ErrOverflow;
2414 }
2415
2416 memcpy_s(dest, destSize, &val, cnt);
2417
2418 return cnt;
2419 }
2420
2421 //
2422 // raw_deserialize() generates a value "val" from its binary
2423 // representation in a buffer "src".
2424 // Returns raw_size(val), or ErrOverflow if the buffer does not have
2425 // enough space to accommodate "val".
2426 //
2427 static FORCEINLINE size_t
2428 raw_deserialize(T & val, const BYTE* src, size_t srcSize)
2429 {
2430 size_t cnt = raw_size(*(T*)src);
2431
2432 if (srcSize < cnt)
2433 {
2434 return ErrOverflow;
2435 }
2436
2437 memcpy_s(&val, cnt, src, cnt);
2438
2439 return cnt;
2440 }
2441
2442 };
2443
2444 //
2445 // Specialization for UTF8 strings
2446 //
2447 template<>
2448 class Traits<LPCUTF8>
2449 {
2450 public:
2451 static FORCEINLINE size_t
2452 raw_size(const LPCUTF8 & val)
2453 {
2454 return strlen(val) + 1;
2455 }
2456
2457 static FORCEINLINE size_t
2458 raw_serialize(BYTE* dest, size_t destSize, const LPCUTF8 & val)
2459 {
2460 size_t cnt = raw_size(val);
2461
2462 if (destSize < cnt)
2463 {
2464 return ErrOverflow;
2465 }
2466
2467 memcpy_s(dest, destSize, &val, cnt);
2468
2469 return cnt;
2470 }
2471
2472 static FORCEINLINE size_t
2473 raw_deserialize(LPCUTF8 & val, const BYTE* src, size_t srcSize)
2474 {
2475 size_t cnt = strnlen((LPCUTF8)src, srcSize) + 1;
2476
2477 // assert we found a NULL terminated string at "src"
2478 if (srcSize < cnt)
2479 {
2480 return ErrOverflow;
2481 }
2482
2483 // we won't allocate another buffer for this string
2484 val = (LPCUTF8)src;
2485
2486 return cnt;
2487 }
2488
2489 };
2490
2491 //
2492 // Specialization for SString.
2493 // SString serialization/deserialization is performed to/from a UTF8
2494 // string.
2495 //
2496 template<>
2497 class Traits<SString>
2498 {
2499 public:
2500 static FORCEINLINE size_t
2501 raw_size(const SString & val)
2502 {
2503 StackSString s;
2504 val.ConvertToUTF8(s);
2505 // make sure to include the NULL terminator
2506 return s.GetCount() + 1;
2507 }
2508
2509 static FORCEINLINE size_t
2510 raw_serialize(BYTE* dest, size_t destSize, const SString & val)
2511 {
2512 // instead of calling raw_size() we inline it here, so we can reuse
2513 // the UTF8 string obtained below as an argument to memcpy.
2514
2515 StackSString s;
2516 val.ConvertToUTF8(s);
2517 // make sure to include the NULL terminator
2518 size_t cnt = s.GetCount() + 1;
2519
2520 if (destSize < cnt)
2521 {
2522 return ErrOverflow;
2523 }
2524
2525 memcpy_s(dest, destSize, s.GetUTF8NoConvert(), cnt);
2526
2527 return cnt;
2528 }
2529
2530 static FORCEINLINE size_t
2531 raw_deserialize(SString & val, const BYTE* src, size_t srcSize)
2532 {
2533 size_t cnt = strnlen((LPCUTF8)src, srcSize) + 1;
2534
2535 // assert we found a NULL terminated string at "src"
2536 if (srcSize < cnt)
2537 {
2538 return ErrOverflow;
2539 }
2540
2541 // a literal SString avoids a new allocation + copy
2542 SString sUtf8(SString::Utf8Literal, (LPCUTF8) src);
2543 sUtf8.ConvertToUnicode(val);
2544
2545 return cnt;
2546 }
2547
2548 };
2549
2550#ifndef FEATURE_PAL
2551 //
2552 // Specialization for SString-derived classes (like SStrings)
2553 //
2554 template<typename T>
2555 class Traits<T, typename std::enable_if<std::is_base_of<SString, T>::value>::type>
2556 : public Traits<SString>
2557 {
2558 };
2559#endif // !FEATURE_PAL
2560
2561 //
2562 // Convenience functions to allow argument type deduction
2563 //
2564 template <typename T> FORCEINLINE
2565 size_t raw_size(const T & val)
2566 { return Traits<T>::raw_size(val); }
2567
2568 template <typename T> FORCEINLINE
2569 size_t raw_serialize(BYTE* dest, size_t destSize, const T & val)
2570 { return Traits<T>::raw_serialize(dest, destSize, val); }
2571
2572 template <typename T> FORCEINLINE
2573 size_t raw_deserialize(T & val, const BYTE* src, size_t srcSize)
2574 { return Traits<T>::raw_deserialize(val, src, srcSize); }
2575
2576
2577 enum StreamBuffState
2578 {
2579 sbsOK,
2580 sbsUnrecoverable,
2581 sbsOOM = sbsUnrecoverable,
2582 };
2583
2584 //
2585 // OStreamBuff - Manages writing to an output buffer
2586 //
2587 class OStreamBuff
2588 {
2589 public:
2590 OStreamBuff(BYTE * _buff, size_t _buffsize)
2591 : buffsize(_buffsize)
2592 , buff(_buff)
2593 , crt(0)
2594 , sbs(sbsOK)
2595 { }
2596
2597 template <typename T>
2598 OStreamBuff& operator << (const T & val)
2599 {
2600 if (sbs >= sbsUnrecoverable)
2601 return *this;
2602
2603 size_t cnt = raw_serialize(buff+crt, buffsize-crt, val);
2604 if (cnt == ErrOverflow)
2605 {
2606 sbs = sbsOOM;
2607 }
2608 else
2609 {
2610 crt += cnt;
2611 }
2612
2613 return *this;
2614 }
2615
2616 inline size_t GetPos() const
2617 {
2618 return crt;
2619 }
2620
2621 inline BOOL operator!() const
2622 {
2623 return sbs >= sbsUnrecoverable;
2624 }
2625
2626 inline StreamBuffState State() const
2627 {
2628 return sbs;
2629 }
2630
2631 private:
2632 size_t buffsize; // size of buffer
2633 BYTE* buff; // buffer to stream to
2634 size_t crt; // current offset in buffer
2635 StreamBuffState sbs; // current state
2636 };
2637
2638
2639 //
2640 // OStreamBuff - Manages reading from an input buffer
2641 //
2642 class IStreamBuff
2643 {
2644 public:
2645 IStreamBuff(const BYTE* _buff, size_t _buffsize)
2646 : buffsize(_buffsize)
2647 , buff(_buff)
2648 , crt(0)
2649 , sbs(sbsOK)
2650 { }
2651
2652 template <typename T>
2653 IStreamBuff& operator >> (T & val)
2654 {
2655 if (sbs >= sbsUnrecoverable)
2656 return *this;
2657
2658 size_t cnt = raw_deserialize(val, buff+crt, buffsize-crt);
2659 if (cnt == ErrOverflow)
2660 {
2661 sbs = sbsOOM;
2662 }
2663 else
2664 {
2665 crt += cnt;
2666 }
2667
2668 return *this;
2669 }
2670
2671 inline size_t GetPos() const
2672 {
2673 return crt;
2674 }
2675
2676 inline BOOL operator!() const
2677 {
2678 return sbs >= sbsUnrecoverable;
2679 }
2680
2681 inline StreamBuffState State() const
2682 {
2683 return sbs;
2684 }
2685
2686 private:
2687 size_t buffsize; // size of buffer
2688 const BYTE * buff; // buffer to read from
2689 size_t crt; // current offset in buffer
2690 StreamBuffState sbs; // current state
2691 };
2692
2693} }
2694
2695using serialization::bin::StreamBuffState;
2696using serialization::bin::IStreamBuff;
2697using serialization::bin::OStreamBuff;
2698
2699
2700// Callback function type used by DacStreamManager to coordinate
2701// amount of available memory between multiple streamable data
2702// structures (e.g. DacEENamesStreamable)
2703typedef bool (*Reserve_Fnptr)(DWORD size, void * writeState);
2704
2705
2706//
2707// DacEENamesStreamable
2708// Stores EE struct* -> Name mappings and streams them to a
2709// streambuf when asked
2710//
2711class DacEENamesStreamable
2712{
2713private:
2714 // the hash map storing the interesting mappings of EE* -> Names
2715 MapSHash< TADDR, SString,
2716 NoRemoveSHashTraits <
2717 NonDacAwareSHashTraits< MapSHashTraits <TADDR, SString> >
2718 > > m_hash;
2719
2720 Reserve_Fnptr m_reserveFn;
2721 void *m_writeState;
2722
2723private:
2724 // signature value in the header in stream
2725 static const DWORD sig = 0x614e4545; // "EENa" - EE Name
2726
2727 // header in stream
2728 struct StreamHeader
2729 {
2730 DWORD sig; // 0x614e4545 == "EENa"
2731 DWORD cnt; // count of entries
2732
2733 static const bool is_blittable = true;
2734 };
2735
2736public:
2737 DacEENamesStreamable()
2738 : m_reserveFn(NULL)
2739 , m_writeState(NULL)
2740 {}
2741
2742 // Ensures the instance is ready for caching data and later writing
2743 // its map entries to an OStreamBuff.
2744 bool PrepareStreamForWriting(Reserve_Fnptr pfn, void * writeState)
2745 {
2746 _ASSERTE(pfn != NULL && writeState != NULL);
2747 m_reserveFn = pfn;
2748 m_writeState = writeState;
2749
2750 DWORD size = (DWORD) sizeof(StreamHeader);
2751
2752 // notify owner to reserve space for a StreamHeader
2753 return m_reserveFn(size, m_writeState);
2754 }
2755
2756 // Adds a new mapping from an EE struct pointer (e.g. MethodDesc*) to
2757 // its name
2758 bool AddEEName(TADDR taEE, const SString & eeName)
2759 {
2760 _ASSERTE(m_reserveFn != NULL && m_writeState != NULL);
2761
2762 // as a micro-optimization convert to Utf8 here as both raw_size and
2763 // raw_serialize are optimized for Utf8...
2764 StackSString seeName;
2765 eeName.ConvertToUTF8(seeName);
2766
2767 DWORD size = (DWORD)(serialization::bin::raw_size(taEE) +
2768 serialization::bin::raw_size(seeName));
2769
2770 // notify owner of the amount of space needed in the buffer
2771 if (m_reserveFn(size, m_writeState))
2772 {
2773 // if there's still space cache the entry in m_hash
2774 m_hash.AddOrReplace(KeyValuePair<TADDR, SString>(taEE, seeName));
2775 return true;
2776 }
2777 else
2778 {
2779 return false;
2780 }
2781 }
2782
2783 // Finds an EE name from a target address of an EE struct (e.g.
2784 // MethodDesc*)
2785 bool FindEEName(TADDR taEE, SString & eeName) const
2786 {
2787 return m_hash.Lookup(taEE, &eeName) == TRUE;
2788 }
2789
2790 void Clear()
2791 {
2792 m_hash.RemoveAll();
2793 }
2794
2795 // Writes a header and the hash entries to an OStreamBuff
2796 HRESULT StreamTo(OStreamBuff &out) const
2797 {
2798 StreamHeader hdr;
2799 hdr.sig = sig;
2800 hdr.cnt = (DWORD) m_hash.GetCount();
2801
2802 out << hdr;
2803
2804 auto end = m_hash.End();
2805 for (auto cur = m_hash.Begin(); end != cur; ++cur)
2806 {
2807 out << cur->Key() << cur->Value();
2808 if (!out)
2809 return E_FAIL;
2810 }
2811
2812 return S_OK;
2813 }
2814
2815 // Reads a header and the hash entries from an IStreamBuff
2816 HRESULT StreamFrom(IStreamBuff &in)
2817 {
2818 StreamHeader hdr;
2819
2820 in >> hdr; // in >> hdr.sig >> hdr.cnt;
2821
2822 if (hdr.sig != sig)
2823 return E_FAIL;
2824
2825 for (size_t i = 0; i < hdr.cnt; ++i)
2826 {
2827 TADDR taEE;
2828 SString eeName;
2829 in >> taEE >> eeName;
2830
2831 if (!in)
2832 return E_FAIL;
2833
2834 m_hash.AddOrReplace(KeyValuePair<TADDR, SString>(taEE, eeName));
2835 }
2836
2837 return S_OK;
2838 }
2839
2840};
2841
2842//================================================================================
2843// This class enables two scenarios:
2844// 1. When debugging a triage/mini-dump the class is initialized with a valid
2845// buffer in taMiniMetaDataBuff. Afterwards one can call MdCacheGetEEName to
2846// retrieve the name associated with a MethodDesc*.
2847// 2. When generating a dump one must follow this sequence:
2848// a. Initialize the DacStreamManager passing a valid (if the current
2849// debugging target is a triage/mini-dump) or empty buffer (if the
2850// current target is a live processa full or a heap dump)
2851// b. Call PrepareStreamsForWriting() before starting enumerating any memory
2852// c. Call MdCacheAddEEName() anytime we enumerate an EE structure of interest
2853// d. Call EnumStreams() as the last action in the memory enumeration method.
2854//
2855class DacStreamManager
2856{
2857public:
2858 enum eReadOrWrite
2859 {
2860 eNone, // the stream doesn't exist (target is a live process/full/heap dump)
2861 eRO, // the stream exists and we've read it (target is triage/mini-dump)
2862 eWO, // the stream doesn't exist but we're creating it
2863 // (e.g. to save a minidump from the current debugging session)
2864 eRW // the stream exists but we're generating another triage/mini-dump
2865 };
2866
2867 static const DWORD sig = 0x6d727473; // 'strm'
2868
2869 struct StreamsHeader
2870 {
2871 DWORD dwSig; // 0x6d727473 == "strm"
2872 DWORD dwTotalSize; // total size in bytes
2873 DWORD dwCntStreams; // number of streams (currently 1)
2874
2875 static const bool is_blittable = true;
2876 };
2877
2878 DacStreamManager(TADDR miniMetaDataBuffAddress, DWORD miniMetaDataBuffSizeMax)
2879 : m_MiniMetaDataBuffAddress(miniMetaDataBuffAddress)
2880 , m_MiniMetaDataBuffSizeMax(miniMetaDataBuffSizeMax)
2881 , m_rawBuffer(NULL)
2882 , m_cbAvailBuff(0)
2883 , m_rw(eNone)
2884 , m_bStreamsRead(FALSE)
2885 , m_EENames()
2886 {
2887 Initialize();
2888 }
2889
2890 ~DacStreamManager()
2891 {
2892 if (m_rawBuffer != NULL)
2893 {
2894 delete [] m_rawBuffer;
2895 }
2896 }
2897
2898 bool PrepareStreamsForWriting()
2899 {
2900 if (m_rw == eNone)
2901 m_rw = eWO;
2902 else if (m_rw == eRO)
2903 m_rw = eRW;
2904 else if (m_rw == eRW)
2905 /* nothing */;
2906 else // m_rw == eWO
2907 {
2908 // this is a second invocation from a possibly live process
2909 // clean up the map since the callstacks/exceptions may be different
2910 m_EENames.Clear();
2911 }
2912
2913 // update available count based on the header and footer sizes
2914 if (m_MiniMetaDataBuffSizeMax < sizeof(StreamsHeader))
2915 return false;
2916
2917 m_cbAvailBuff = m_MiniMetaDataBuffSizeMax - sizeof(StreamsHeader);
2918
2919 // update available count based on each stream's initial needs
2920 if (!m_EENames.PrepareStreamForWriting(&ReserveInBuffer, this))
2921 return false;
2922
2923 return true;
2924 }
2925
2926 bool MdCacheAddEEName(TADDR taEEStruct, const SString& name)
2927 {
2928 // don't cache unless we enabled "W"riting from a target that does not
2929 // already have a stream yet
2930 if (m_rw != eWO)
2931 return false;
2932
2933 m_EENames.AddEEName(taEEStruct, name);
2934 return true;
2935 }
2936
2937 HRESULT EnumStreams(IN CLRDataEnumMemoryFlags flags)
2938 {
2939 _ASSERTE(flags == CLRDATA_ENUM_MEM_MINI || flags == CLRDATA_ENUM_MEM_TRIAGE);
2940 _ASSERTE(m_rw == eWO || m_rw == eRW);
2941
2942 DWORD cbWritten = 0;
2943
2944 if (m_rw == eWO)
2945 {
2946 // only dump the stream is it wasn't already present in the target
2947 DumpAllStreams(&cbWritten);
2948 }
2949 else
2950 {
2951 cbWritten = m_MiniMetaDataBuffSizeMax;
2952 }
2953
2954 DacEnumMemoryRegion(m_MiniMetaDataBuffAddress, cbWritten, false);
2955 DacUpdateMemoryRegion(m_MiniMetaDataBuffAddress, cbWritten, m_rawBuffer);
2956
2957 return S_OK;
2958 }
2959
2960 bool MdCacheGetEEName(TADDR taEEStruct, SString & eeName)
2961 {
2962 if (!m_bStreamsRead)
2963 {
2964 ReadAllStreams();
2965 }
2966
2967 if (m_rw == eNone || m_rw == eWO)
2968 {
2969 return false;
2970 }
2971
2972 return m_EENames.FindEEName(taEEStruct, eeName);
2973 }
2974
2975private:
2976 HRESULT Initialize()
2977 {
2978 _ASSERTE(m_rw == eNone);
2979 _ASSERTE(m_rawBuffer == NULL);
2980
2981 HRESULT hr = S_OK;
2982
2983 StreamsHeader hdr;
2984 DacReadAll(dac_cast<TADDR>(m_MiniMetaDataBuffAddress),
2985 &hdr, sizeof(hdr), true);
2986
2987 // when the DAC looks at a triage dump or minidump generated using
2988 // a "minimetadata" enabled DAC, buff will point to a serialized
2989 // representation of a methoddesc->method name hashmap.
2990 if (hdr.dwSig == sig)
2991 {
2992 m_rw = eRO;
2993 m_MiniMetaDataBuffSizeMax = hdr.dwTotalSize;
2994 hr = S_OK;
2995 }
2996 else
2997 // when the DAC initializes this for the case where the target is
2998 // (a) a live process, or (b) a full dump, buff will point to a
2999 // zero initialized memory region (allocated w/ VirtualAlloc)
3000 if (hdr.dwSig == 0 && hdr.dwTotalSize == 0 && hdr.dwCntStreams == 0)
3001 {
3002 hr = S_OK;
3003 }
3004 // otherwise we may have some memory corruption. treat this as
3005 // a liveprocess/full dump
3006 else
3007 {
3008 hr = S_FALSE;
3009 }
3010
3011 BYTE * buff = new BYTE[m_MiniMetaDataBuffSizeMax];
3012 DacReadAll(dac_cast<TADDR>(m_MiniMetaDataBuffAddress),
3013 buff, m_MiniMetaDataBuffSizeMax, true);
3014
3015 m_rawBuffer = buff;
3016
3017 return hr;
3018 }
3019
3020 HRESULT DumpAllStreams(DWORD * pcbWritten)
3021 {
3022 _ASSERTE(m_rw == eWO);
3023
3024 HRESULT hr = S_OK;
3025
3026 OStreamBuff out(m_rawBuffer, m_MiniMetaDataBuffSizeMax);
3027
3028 // write header
3029 StreamsHeader hdr;
3030 hdr.dwSig = sig;
3031 hdr.dwTotalSize = m_MiniMetaDataBuffSizeMax-m_cbAvailBuff; // will update
3032 hdr.dwCntStreams = 1;
3033
3034 out << hdr;
3035
3036 // write MethodDesc->Method name map
3037 hr = m_EENames.StreamTo(out);
3038
3039 // wrap up the buffer whether we ecountered an error or not
3040 size_t cbWritten = out.GetPos();
3041 cbWritten = ALIGN_UP(cbWritten, sizeof(size_t));
3042
3043 // patch the dwTotalSize field blitted at the beginning of the buffer
3044 ((StreamsHeader*)m_rawBuffer)->dwTotalSize = (DWORD) cbWritten;
3045
3046 if (pcbWritten)
3047 *pcbWritten = (DWORD) cbWritten;
3048
3049 return hr;
3050 }
3051
3052 HRESULT ReadAllStreams()
3053 {
3054 _ASSERTE(!m_bStreamsRead);
3055
3056 if (m_rw == eNone || m_rw == eWO)
3057 {
3058 // no streams to read...
3059 m_bStreamsRead = TRUE;
3060 return S_FALSE;
3061 }
3062
3063 HRESULT hr = S_OK;
3064
3065 IStreamBuff in(m_rawBuffer, m_MiniMetaDataBuffSizeMax);
3066
3067 // read header
3068 StreamsHeader hdr;
3069 in >> hdr;
3070 _ASSERTE(hdr.dwSig == sig);
3071 _ASSERTE(hdr.dwCntStreams == 1);
3072
3073 // read EE struct pointer -> EE name map
3074 m_EENames.Clear();
3075 hr = m_EENames.StreamFrom(in);
3076
3077 m_bStreamsRead = TRUE;
3078
3079 return hr;
3080 }
3081
3082 static bool ReserveInBuffer(DWORD size, void * writeState)
3083 {
3084 DacStreamManager * pThis = reinterpret_cast<DacStreamManager*>(writeState);
3085 if (size > pThis->m_cbAvailBuff)
3086 {
3087 return false;
3088 }
3089 else
3090 {
3091 pThis->m_cbAvailBuff -= size;
3092 return true;
3093 }
3094 }
3095
3096private:
3097 TADDR m_MiniMetaDataBuffAddress; // TADDR of the buffer
3098 DWORD m_MiniMetaDataBuffSizeMax; // max size of buffer
3099 BYTE * m_rawBuffer; // inproc copy of buffer
3100 DWORD m_cbAvailBuff; // available bytes in buffer
3101 eReadOrWrite m_rw;
3102 BOOL m_bStreamsRead;
3103 DacEENamesStreamable m_EENames;
3104};
3105
3106#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3107
3108//----------------------------------------------------------------------------
3109//
3110// ClrDataAccess.
3111//
3112//----------------------------------------------------------------------------
3113
3114LONG ClrDataAccess::s_procInit;
3115
3116ClrDataAccess::ClrDataAccess(ICorDebugDataTarget * pTarget, ICLRDataTarget * pLegacyTarget/*=0*/)
3117{
3118 SUPPORTS_DAC_HOST_ONLY; // ctor does no marshalling - don't check with DacCop
3119
3120 /*
3121 * Stash the various forms of the new ICorDebugDataTarget interface
3122 */
3123 m_pTarget = pTarget;
3124 m_pTarget->AddRef();
3125
3126 HRESULT hr;
3127
3128 hr = m_pTarget->QueryInterface(__uuidof(ICorDebugMutableDataTarget),
3129 (void**)&m_pMutableTarget);
3130
3131 if (hr != S_OK)
3132 {
3133 // Create a target which always fails the write requests with CORDBG_E_TARGET_READONLY
3134 m_pMutableTarget = new ReadOnlyDataTargetFacade();
3135 m_pMutableTarget->AddRef();
3136 }
3137
3138 /*
3139 * If we have a legacy target, it means we're providing compatibility for code that used
3140 * the old ICLRDataTarget interfaces. There are still a few things (like metadata location,
3141 * GetImageBase, and VirtualAlloc) that the implementation may use which we haven't superseded
3142 * in ICorDebugDataTarget, so we still need access to the old target interfaces.
3143 * Any functionality that does exist in ICorDebugDataTarget is accessed from that interface
3144 * using the DataTargetAdapter on top of the legacy interface (to unify the calling code).
3145 * Eventually we may expose all functionality we need using ICorDebug (possibly a private
3146 * interface for things like VirtualAlloc), at which point we can stop using the legacy interfaces
3147 * completely (except in the DataTargetAdapter).
3148 */
3149 m_pLegacyTarget = NULL;
3150 m_pLegacyTarget2 = NULL;
3151 m_pLegacyTarget3 = NULL;
3152 m_legacyMetaDataLocator = NULL;
3153 m_target3 = NULL;
3154 if (pLegacyTarget != NULL)
3155 {
3156 m_pLegacyTarget = pLegacyTarget;
3157
3158 m_pLegacyTarget->AddRef();
3159
3160 m_pLegacyTarget->QueryInterface(__uuidof(ICLRDataTarget2), (void**)&m_pLegacyTarget2);
3161
3162 m_pLegacyTarget->QueryInterface(__uuidof(ICLRDataTarget3), (void**)&m_pLegacyTarget3);
3163
3164 if (pLegacyTarget->QueryInterface(__uuidof(ICLRMetadataLocator),
3165 (void**)&m_legacyMetaDataLocator) != S_OK)
3166 {
3167 // The debugger doesn't implement IMetadataLocator. Use
3168 // IXCLRDataTarget3 if that exists. Otherwise we don't need it.
3169 pLegacyTarget->QueryInterface(__uuidof(IXCLRDataTarget3),
3170 (void**)&m_target3);
3171 }
3172 }
3173
3174 m_globalBase = 0;
3175 m_refs = 1;
3176 m_instanceAge = 0;
3177 m_debugMode = GetEnvironmentVariableA("MSCORDACWKS_DEBUG", NULL, 0) != 0;
3178
3179 m_enumMemCb = NULL;
3180 m_updateMemCb = NULL;
3181 m_enumMemFlags = (CLRDataEnumMemoryFlags)-1; // invalid
3182 m_jitNotificationTable = NULL;
3183 m_gcNotificationTable = NULL;
3184
3185#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3186 m_streams = NULL;
3187#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3188
3189 // Target consistency checks are disabled by default.
3190 // See code:ClrDataAccess::SetTargetConsistencyChecks for details.
3191 m_fEnableTargetConsistencyAsserts = false;
3192
3193#ifdef _DEBUG
3194 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgDACEnableAssert))
3195 {
3196 m_fEnableTargetConsistencyAsserts = true;
3197 }
3198
3199 // Verification asserts are disabled by default because some debuggers (cdb/windbg) probe likely locations
3200 // for DAC and having this assert pop up all the time can be annoying. We let derived classes enable
3201 // this if they want. It can also be overridden at run-time with COMPlus_DbgDACAssertOnMismatch,
3202 // see ClrDataAccess::VerifyDlls for details.
3203 m_fEnableDllVerificationAsserts = false;
3204#endif
3205
3206}
3207
3208ClrDataAccess::~ClrDataAccess(void)
3209{
3210 SUPPORTS_DAC_HOST_ONLY;
3211
3212#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3213 if (m_streams)
3214 {
3215 delete m_streams;
3216 }
3217#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
3218
3219 delete [] m_jitNotificationTable;
3220 if (m_pLegacyTarget)
3221 {
3222 m_pLegacyTarget->Release();
3223 }
3224 if (m_pLegacyTarget2)
3225 {
3226 m_pLegacyTarget2->Release();
3227 }
3228 if (m_pLegacyTarget3)
3229 {
3230 m_pLegacyTarget3->Release();
3231 }
3232 if (m_legacyMetaDataLocator)
3233 {
3234 m_legacyMetaDataLocator->Release();
3235 }
3236 if (m_target3)
3237 {
3238 m_target3->Release();
3239 }
3240 m_pTarget->Release();
3241 m_pMutableTarget->Release();
3242}
3243
3244STDMETHODIMP
3245ClrDataAccess::QueryInterface(THIS_
3246 IN REFIID interfaceId,
3247 OUT PVOID* iface)
3248{
3249 void* ifaceRet;
3250
3251 if (IsEqualIID(interfaceId, IID_IUnknown) ||
3252 IsEqualIID(interfaceId, __uuidof(IXCLRDataProcess)) ||
3253 IsEqualIID(interfaceId, __uuidof(IXCLRDataProcess2)))
3254 {
3255 ifaceRet = static_cast<IXCLRDataProcess2*>(this);
3256 }
3257 else if (IsEqualIID(interfaceId, __uuidof(ICLRDataEnumMemoryRegions)))
3258 {
3259 ifaceRet = static_cast<ICLRDataEnumMemoryRegions*>(this);
3260 }
3261 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface)))
3262 {
3263 ifaceRet = static_cast<ISOSDacInterface*>(this);
3264 }
3265 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface2)))
3266 {
3267 ifaceRet = static_cast<ISOSDacInterface2*>(this);
3268 }
3269 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface3)))
3270 {
3271 ifaceRet = static_cast<ISOSDacInterface3*>(this);
3272 }
3273 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface4)))
3274 {
3275 ifaceRet = static_cast<ISOSDacInterface4*>(this);
3276 }
3277 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface5)))
3278 {
3279 ifaceRet = static_cast<ISOSDacInterface5*>(this);
3280 }
3281 else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface6)))
3282 {
3283 ifaceRet = static_cast<ISOSDacInterface6*>(this);
3284 }
3285 else
3286 {
3287 *iface = NULL;
3288 return E_NOINTERFACE;
3289 }
3290
3291 AddRef();
3292 *iface = ifaceRet;
3293 return S_OK;
3294}
3295
3296STDMETHODIMP_(ULONG)
3297ClrDataAccess::AddRef(THIS)
3298{
3299 return InterlockedIncrement(&m_refs);
3300}
3301
3302STDMETHODIMP_(ULONG)
3303ClrDataAccess::Release(THIS)
3304{
3305 SUPPORTS_DAC_HOST_ONLY;
3306 LONG newRefs = InterlockedDecrement(&m_refs);
3307 if (newRefs == 0)
3308 {
3309 delete this;
3310 }
3311 return newRefs;
3312}
3313
3314HRESULT STDMETHODCALLTYPE
3315ClrDataAccess::Flush(void)
3316{
3317 SUPPORTS_DAC_HOST_ONLY;
3318
3319 //
3320 // Free MD import objects.
3321 //
3322 m_mdImports.Flush();
3323
3324 // Free instance memory.
3325 m_instances.Flush();
3326
3327 // When the host instance cache is flushed we
3328 // update the instance age count so that
3329 // all child objects automatically become
3330 // invalid. This prevents them from using
3331 // any pointers they've kept to host instances
3332 // which are now gone.
3333 m_instanceAge++;
3334
3335 return S_OK;
3336}
3337
3338HRESULT STDMETHODCALLTYPE
3339ClrDataAccess::StartEnumTasks(
3340 /* [out] */ CLRDATA_ENUM* handle)
3341{
3342 HRESULT status;
3343
3344 DAC_ENTER();
3345
3346 EX_TRY
3347 {
3348 if (ThreadStore::s_pThreadStore)
3349 {
3350 Thread* thread = ThreadStore::GetAllThreadList(NULL, 0, 0);
3351 *handle = TO_CDENUM(thread);
3352 status = *handle ? S_OK : S_FALSE;
3353 }
3354 else
3355 {
3356 status = S_FALSE;
3357 }
3358 }
3359 EX_CATCH
3360 {
3361 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3362 {
3363 EX_RETHROW;
3364 }
3365 }
3366 EX_END_CATCH(SwallowAllExceptions)
3367
3368 DAC_LEAVE();
3369 return status;
3370}
3371
3372HRESULT STDMETHODCALLTYPE
3373ClrDataAccess::EnumTask(
3374 /* [in, out] */ CLRDATA_ENUM* handle,
3375 /* [out] */ IXCLRDataTask **task)
3376{
3377 HRESULT status;
3378
3379 DAC_ENTER();
3380
3381 EX_TRY
3382 {
3383 if (*handle)
3384 {
3385 Thread* thread = FROM_CDENUM(Thread, *handle);
3386 *task = new (nothrow) ClrDataTask(this, thread);
3387 if (*task)
3388 {
3389 thread = ThreadStore::GetAllThreadList(thread, 0, 0);
3390 *handle = TO_CDENUM(thread);
3391 status = S_OK;
3392 }
3393 else
3394 {
3395 status = E_OUTOFMEMORY;
3396 }
3397 }
3398 else
3399 {
3400 status = S_FALSE;
3401 }
3402 }
3403 EX_CATCH
3404 {
3405 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3406 {
3407 EX_RETHROW;
3408 }
3409 }
3410 EX_END_CATCH(SwallowAllExceptions)
3411
3412 DAC_LEAVE();
3413 return status;
3414}
3415
3416HRESULT STDMETHODCALLTYPE
3417ClrDataAccess::EndEnumTasks(
3418 /* [in] */ CLRDATA_ENUM handle)
3419{
3420 HRESULT status;
3421
3422 DAC_ENTER();
3423
3424 EX_TRY
3425 {
3426 // Enumerator holds no resources.
3427 status = S_OK;
3428 }
3429 EX_CATCH
3430 {
3431 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3432 {
3433 EX_RETHROW;
3434 }
3435 }
3436 EX_END_CATCH(SwallowAllExceptions)
3437
3438 DAC_LEAVE();
3439 return status;
3440}
3441
3442HRESULT STDMETHODCALLTYPE
3443ClrDataAccess::GetTaskByOSThreadID(
3444 /* [in] */ ULONG32 osThreadID,
3445 /* [out] */ IXCLRDataTask **task)
3446{
3447 HRESULT status;
3448
3449 DAC_ENTER();
3450
3451 EX_TRY
3452 {
3453 status = E_INVALIDARG;
3454 Thread* thread = DacGetThread(osThreadID);
3455 if (thread != NULL)
3456 {
3457 *task = new (nothrow) ClrDataTask(this, thread);
3458 status = *task ? S_OK : E_OUTOFMEMORY;
3459 }
3460 }
3461 EX_CATCH
3462 {
3463 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3464 {
3465 EX_RETHROW;
3466 }
3467 }
3468 EX_END_CATCH(SwallowAllExceptions)
3469
3470 DAC_LEAVE();
3471 return status;
3472}
3473
3474HRESULT STDMETHODCALLTYPE
3475ClrDataAccess::GetTaskByUniqueID(
3476 /* [in] */ ULONG64 uniqueID,
3477 /* [out] */ IXCLRDataTask **task)
3478{
3479 HRESULT status;
3480
3481 DAC_ENTER();
3482
3483 EX_TRY
3484 {
3485 Thread* thread = FindClrThreadByTaskId(uniqueID);
3486 if (thread)
3487 {
3488 *task = new (nothrow) ClrDataTask(this, thread);
3489 status = *task ? S_OK : E_OUTOFMEMORY;
3490 }
3491 else
3492 {
3493 status = E_INVALIDARG;
3494 }
3495 }
3496 EX_CATCH
3497 {
3498 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3499 {
3500 EX_RETHROW;
3501 }
3502 }
3503 EX_END_CATCH(SwallowAllExceptions)
3504
3505 DAC_LEAVE();
3506 return status;
3507}
3508
3509HRESULT STDMETHODCALLTYPE
3510ClrDataAccess::GetFlags(
3511 /* [out] */ ULONG32 *flags)
3512{
3513 HRESULT status;
3514
3515 DAC_ENTER();
3516
3517 EX_TRY
3518 {
3519 // XXX Microsoft - GC check.
3520 *flags = CLRDATA_PROCESS_DEFAULT;
3521 status = S_OK;
3522 }
3523 EX_CATCH
3524 {
3525 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3526 {
3527 EX_RETHROW;
3528 }
3529 }
3530 EX_END_CATCH(SwallowAllExceptions)
3531
3532 DAC_LEAVE();
3533 return status;
3534}
3535
3536HRESULT STDMETHODCALLTYPE
3537ClrDataAccess::IsSameObject(
3538 /* [in] */ IXCLRDataProcess* process)
3539{
3540 HRESULT status;
3541
3542 DAC_ENTER();
3543
3544 EX_TRY
3545 {
3546 status = m_pTarget == ((ClrDataAccess*)process)->m_pTarget ?
3547 S_OK : S_FALSE;
3548 }
3549 EX_CATCH
3550 {
3551 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3552 {
3553 EX_RETHROW;
3554 }
3555 }
3556 EX_END_CATCH(SwallowAllExceptions)
3557
3558 DAC_LEAVE();
3559 return status;
3560}
3561
3562HRESULT STDMETHODCALLTYPE
3563ClrDataAccess::GetManagedObject(
3564 /* [out] */ IXCLRDataValue **value)
3565{
3566 HRESULT status;
3567
3568 DAC_ENTER();
3569
3570 EX_TRY
3571 {
3572 // XXX Microsoft.
3573 status = E_NOTIMPL;
3574 }
3575 EX_CATCH
3576 {
3577 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3578 {
3579 EX_RETHROW;
3580 }
3581 }
3582 EX_END_CATCH(SwallowAllExceptions)
3583
3584 DAC_LEAVE();
3585 return status;
3586}
3587
3588HRESULT STDMETHODCALLTYPE
3589ClrDataAccess::GetDesiredExecutionState(
3590 /* [out] */ ULONG32 *state)
3591{
3592 HRESULT status;
3593
3594 DAC_ENTER();
3595
3596 EX_TRY
3597 {
3598 // XXX Microsoft.
3599 status = E_NOTIMPL;
3600 }
3601 EX_CATCH
3602 {
3603 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3604 {
3605 EX_RETHROW;
3606 }
3607 }
3608 EX_END_CATCH(SwallowAllExceptions)
3609
3610 DAC_LEAVE();
3611 return status;
3612}
3613
3614HRESULT STDMETHODCALLTYPE
3615ClrDataAccess::SetDesiredExecutionState(
3616 /* [in] */ ULONG32 state)
3617{
3618 HRESULT status;
3619
3620 DAC_ENTER();
3621
3622 EX_TRY
3623 {
3624 // XXX Microsoft.
3625 status = E_NOTIMPL;
3626 }
3627 EX_CATCH
3628 {
3629 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3630 {
3631 EX_RETHROW;
3632 }
3633 }
3634 EX_END_CATCH(SwallowAllExceptions)
3635
3636 DAC_LEAVE();
3637 return status;
3638}
3639
3640HRESULT STDMETHODCALLTYPE
3641ClrDataAccess::GetAddressType(
3642 /* [in] */ CLRDATA_ADDRESS address,
3643 /* [out] */ CLRDataAddressType* type)
3644{
3645 HRESULT status;
3646
3647 DAC_ENTER();
3648
3649 EX_TRY
3650 {
3651 // The only thing that constitutes a failure is some
3652 // dac failure while checking things.
3653 status = S_OK;
3654 TADDR taAddr = CLRDATA_ADDRESS_TO_TADDR(address);
3655 if (IsPossibleCodeAddress(taAddr) == S_OK)
3656 {
3657 if (ExecutionManager::IsManagedCode(taAddr))
3658 {
3659 *type = CLRDATA_ADDRESS_MANAGED_METHOD;
3660 goto Exit;
3661 }
3662
3663 if (StubManager::IsStub(taAddr))
3664 {
3665 *type = CLRDATA_ADDRESS_RUNTIME_UNMANAGED_STUB;
3666 goto Exit;
3667 }
3668 }
3669
3670 *type = CLRDATA_ADDRESS_UNRECOGNIZED;
3671
3672 Exit: ;
3673 }
3674 EX_CATCH
3675 {
3676 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3677 {
3678 EX_RETHROW;
3679 }
3680 }
3681 EX_END_CATCH(SwallowAllExceptions)
3682
3683 DAC_LEAVE();
3684 return status;
3685}
3686
3687HRESULT STDMETHODCALLTYPE
3688ClrDataAccess::GetRuntimeNameByAddress(
3689 /* [in] */ CLRDATA_ADDRESS address,
3690 /* [in] */ ULONG32 flags,
3691 /* [in] */ ULONG32 bufLen,
3692 /* [out] */ ULONG32 *symbolLen,
3693 /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR symbolBuf[ ],
3694 /* [out] */ CLRDATA_ADDRESS* displacement)
3695{
3696 HRESULT status;
3697
3698 DAC_ENTER();
3699
3700 EX_TRY
3701 {
3702#ifdef _TARGET_ARM_
3703 address &= ~THUMB_CODE; //workaround for windbg passing in addresses with the THUMB mode bit set
3704#endif
3705 status = RawGetMethodName(address, flags, bufLen, symbolLen, symbolBuf,
3706 displacement);
3707 }
3708 EX_CATCH
3709 {
3710 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3711 {
3712 EX_RETHROW;
3713 }
3714 }
3715 EX_END_CATCH(SwallowAllExceptions)
3716
3717 DAC_LEAVE();
3718 return status;
3719}
3720
3721HRESULT STDMETHODCALLTYPE
3722ClrDataAccess::StartEnumAppDomains(
3723 /* [out] */ CLRDATA_ENUM* handle)
3724{
3725 HRESULT status;
3726
3727 DAC_ENTER();
3728
3729 EX_TRY
3730 {
3731 AppDomainIterator* iter = new (nothrow) AppDomainIterator(FALSE);
3732 if (iter)
3733 {
3734 *handle = TO_CDENUM(iter);
3735 status = S_OK;
3736 }
3737 else
3738 {
3739 status = E_OUTOFMEMORY;
3740 }
3741 }
3742 EX_CATCH
3743 {
3744 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3745 {
3746 EX_RETHROW;
3747 }
3748 }
3749 EX_END_CATCH(SwallowAllExceptions)
3750
3751 DAC_LEAVE();
3752 return status;
3753}
3754
3755HRESULT STDMETHODCALLTYPE
3756ClrDataAccess::EnumAppDomain(
3757 /* [in, out] */ CLRDATA_ENUM* handle,
3758 /* [out] */ IXCLRDataAppDomain **appDomain)
3759{
3760 HRESULT status;
3761
3762 DAC_ENTER();
3763
3764 EX_TRY
3765 {
3766 AppDomainIterator* iter = FROM_CDENUM(AppDomainIterator, *handle);
3767 if (iter->Next())
3768 {
3769 *appDomain = new (nothrow)
3770 ClrDataAppDomain(this, iter->GetDomain());
3771 status = *appDomain ? S_OK : E_OUTOFMEMORY;
3772 }
3773 else
3774 {
3775 status = S_FALSE;
3776 }
3777 }
3778 EX_CATCH
3779 {
3780 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3781 {
3782 EX_RETHROW;
3783 }
3784 }
3785 EX_END_CATCH(SwallowAllExceptions)
3786
3787 DAC_LEAVE();
3788 return status;
3789}
3790
3791HRESULT STDMETHODCALLTYPE
3792ClrDataAccess::EndEnumAppDomains(
3793 /* [in] */ CLRDATA_ENUM handle)
3794{
3795 HRESULT status;
3796
3797 DAC_ENTER();
3798
3799 EX_TRY
3800 {
3801 AppDomainIterator* iter = FROM_CDENUM(AppDomainIterator, handle);
3802 delete iter;
3803 status = S_OK;
3804 }
3805 EX_CATCH
3806 {
3807 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3808 {
3809 EX_RETHROW;
3810 }
3811 }
3812 EX_END_CATCH(SwallowAllExceptions)
3813
3814 DAC_LEAVE();
3815 return status;
3816}
3817
3818HRESULT STDMETHODCALLTYPE
3819ClrDataAccess::GetAppDomainByUniqueID(
3820 /* [in] */ ULONG64 uniqueID,
3821 /* [out] */ IXCLRDataAppDomain **appDomain)
3822{
3823 HRESULT status;
3824
3825 DAC_ENTER();
3826
3827 EX_TRY
3828 {
3829 AppDomainIterator iter(FALSE);
3830
3831 status = E_INVALIDARG;
3832 while (iter.Next())
3833 {
3834 if (iter.GetDomain()->GetId().m_dwId == uniqueID)
3835 {
3836 *appDomain = new (nothrow)
3837 ClrDataAppDomain(this, iter.GetDomain());
3838 status = *appDomain ? S_OK : E_OUTOFMEMORY;
3839 break;
3840 }
3841 }
3842 }
3843 EX_CATCH
3844 {
3845 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3846 {
3847 EX_RETHROW;
3848 }
3849 }
3850 EX_END_CATCH(SwallowAllExceptions)
3851
3852 DAC_LEAVE();
3853 return status;
3854}
3855
3856HRESULT STDMETHODCALLTYPE
3857ClrDataAccess::StartEnumAssemblies(
3858 /* [out] */ CLRDATA_ENUM* handle)
3859{
3860 HRESULT status;
3861
3862 DAC_ENTER();
3863
3864 EX_TRY
3865 {
3866 ProcessModIter* iter = new (nothrow) ProcessModIter;
3867 if (iter)
3868 {
3869 *handle = TO_CDENUM(iter);
3870 status = S_OK;
3871 }
3872 else
3873 {
3874 status = E_OUTOFMEMORY;
3875 }
3876 }
3877 EX_CATCH
3878 {
3879 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3880 {
3881 EX_RETHROW;
3882 }
3883 }
3884 EX_END_CATCH(SwallowAllExceptions)
3885
3886 DAC_LEAVE();
3887 return status;
3888}
3889
3890HRESULT STDMETHODCALLTYPE
3891ClrDataAccess::EnumAssembly(
3892 /* [in, out] */ CLRDATA_ENUM* handle,
3893 /* [out] */ IXCLRDataAssembly **assembly)
3894{
3895 HRESULT status;
3896
3897 DAC_ENTER();
3898
3899 EX_TRY
3900 {
3901 ProcessModIter* iter = FROM_CDENUM(ProcessModIter, *handle);
3902 Assembly* assem;
3903
3904 if ((assem = iter->NextAssem()))
3905 {
3906 *assembly = new (nothrow)
3907 ClrDataAssembly(this, assem);
3908 status = *assembly ? S_OK : E_OUTOFMEMORY;
3909 }
3910 else
3911 {
3912 status = S_FALSE;
3913 }
3914 }
3915 EX_CATCH
3916 {
3917 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3918 {
3919 EX_RETHROW;
3920 }
3921 }
3922 EX_END_CATCH(SwallowAllExceptions)
3923
3924 DAC_LEAVE();
3925 return status;
3926}
3927
3928HRESULT STDMETHODCALLTYPE
3929ClrDataAccess::EndEnumAssemblies(
3930 /* [in] */ CLRDATA_ENUM handle)
3931{
3932 HRESULT status;
3933
3934 DAC_ENTER();
3935
3936 EX_TRY
3937 {
3938 ProcessModIter* iter = FROM_CDENUM(ProcessModIter, handle);
3939 delete iter;
3940 status = S_OK;
3941 }
3942 EX_CATCH
3943 {
3944 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3945 {
3946 EX_RETHROW;
3947 }
3948 }
3949 EX_END_CATCH(SwallowAllExceptions)
3950
3951 DAC_LEAVE();
3952 return status;
3953}
3954
3955HRESULT STDMETHODCALLTYPE
3956ClrDataAccess::StartEnumModules(
3957 /* [out] */ CLRDATA_ENUM* handle)
3958{
3959 HRESULT status;
3960
3961 DAC_ENTER();
3962
3963 EX_TRY
3964 {
3965 ProcessModIter* iter = new (nothrow) ProcessModIter;
3966 if (iter)
3967 {
3968 *handle = TO_CDENUM(iter);
3969 status = S_OK;
3970 }
3971 else
3972 {
3973 status = E_OUTOFMEMORY;
3974 }
3975 }
3976 EX_CATCH
3977 {
3978 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
3979 {
3980 EX_RETHROW;
3981 }
3982 }
3983 EX_END_CATCH(SwallowAllExceptions)
3984
3985 DAC_LEAVE();
3986 return status;
3987}
3988
3989HRESULT STDMETHODCALLTYPE
3990ClrDataAccess::EnumModule(
3991 /* [in, out] */ CLRDATA_ENUM* handle,
3992 /* [out] */ IXCLRDataModule **mod)
3993{
3994 HRESULT status;
3995
3996 DAC_ENTER();
3997
3998 EX_TRY
3999 {
4000 ProcessModIter* iter = FROM_CDENUM(ProcessModIter, *handle);
4001 Module* curMod;
4002
4003 if ((curMod = iter->NextModule()))
4004 {
4005 *mod = new (nothrow)
4006 ClrDataModule(this, curMod);
4007 status = *mod ? S_OK : E_OUTOFMEMORY;
4008 }
4009 else
4010 {
4011 status = S_FALSE;
4012 }
4013 }
4014 EX_CATCH
4015 {
4016 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4017 {
4018 EX_RETHROW;
4019 }
4020 }
4021 EX_END_CATCH(SwallowAllExceptions)
4022
4023 DAC_LEAVE();
4024 return status;
4025}
4026
4027HRESULT STDMETHODCALLTYPE
4028ClrDataAccess::EndEnumModules(
4029 /* [in] */ CLRDATA_ENUM handle)
4030{
4031 HRESULT status;
4032
4033 DAC_ENTER();
4034
4035 EX_TRY
4036 {
4037 ProcessModIter* iter = FROM_CDENUM(ProcessModIter, handle);
4038 delete iter;
4039 status = S_OK;
4040 }
4041 EX_CATCH
4042 {
4043 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4044 {
4045 EX_RETHROW;
4046 }
4047 }
4048 EX_END_CATCH(SwallowAllExceptions)
4049
4050 DAC_LEAVE();
4051 return status;
4052}
4053
4054HRESULT STDMETHODCALLTYPE
4055ClrDataAccess::GetModuleByAddress(
4056 /* [in] */ CLRDATA_ADDRESS address,
4057 /* [out] */ IXCLRDataModule** mod)
4058{
4059 HRESULT status;
4060
4061 DAC_ENTER();
4062
4063 EX_TRY
4064 {
4065 ProcessModIter modIter;
4066 Module* modDef;
4067
4068 while ((modDef = modIter.NextModule()))
4069 {
4070 TADDR base;
4071 ULONG32 length;
4072 PEFile* file = modDef->GetFile();
4073
4074 if ((base = PTR_TO_TADDR(file->GetLoadedImageContents(&length))))
4075 {
4076 if (TO_CDADDR(base) <= address &&
4077 TO_CDADDR(base + length) > address)
4078 {
4079 break;
4080 }
4081 }
4082 if (file->HasNativeImage())
4083 {
4084 base = PTR_TO_TADDR(file->GetLoadedNative()->GetBase());
4085 length = file->GetLoadedNative()->GetVirtualSize();
4086 if (TO_CDADDR(base) <= address &&
4087 TO_CDADDR(base + length) > address)
4088 {
4089 break;
4090 }
4091 }
4092 }
4093
4094 if (modDef)
4095 {
4096 *mod = new (nothrow)
4097 ClrDataModule(this, modDef);
4098 status = *mod ? S_OK : E_OUTOFMEMORY;
4099 }
4100 else
4101 {
4102 status = S_FALSE;
4103 }
4104 }
4105 EX_CATCH
4106 {
4107 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4108 {
4109 EX_RETHROW;
4110 }
4111 }
4112 EX_END_CATCH(SwallowAllExceptions)
4113
4114 DAC_LEAVE();
4115 return status;
4116}
4117
4118HRESULT STDMETHODCALLTYPE
4119ClrDataAccess::StartEnumMethodDefinitionsByAddress(
4120 /* [in] */ CLRDATA_ADDRESS address,
4121 /* [out] */ CLRDATA_ENUM *handle)
4122{
4123 HRESULT status;
4124
4125 DAC_ENTER();
4126
4127 EX_TRY
4128 {
4129 ProcessModIter modIter;
4130 Module* modDef;
4131
4132 while ((modDef = modIter.NextModule()))
4133 {
4134 TADDR base;
4135 ULONG32 length;
4136 PEFile* file = modDef->GetFile();
4137
4138 if ((base = PTR_TO_TADDR(file->GetLoadedImageContents(&length))))
4139 {
4140 if (TO_CDADDR(base) <= address &&
4141 TO_CDADDR(base + length) > address)
4142 {
4143 break;
4144 }
4145 }
4146 }
4147
4148 status = EnumMethodDefinitions::
4149 CdStart(modDef, true, address, handle);
4150 }
4151 EX_CATCH
4152 {
4153 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4154 {
4155 EX_RETHROW;
4156 }
4157 }
4158 EX_END_CATCH(SwallowAllExceptions)
4159
4160 DAC_LEAVE();
4161 return status;
4162}
4163
4164HRESULT STDMETHODCALLTYPE
4165ClrDataAccess::EnumMethodDefinitionByAddress(
4166 /* [out][in] */ CLRDATA_ENUM* handle,
4167 /* [out] */ IXCLRDataMethodDefinition **method)
4168{
4169 HRESULT status;
4170
4171 DAC_ENTER();
4172
4173 EX_TRY
4174 {
4175 status = EnumMethodDefinitions::CdNext(this, handle, method);
4176 }
4177 EX_CATCH
4178 {
4179 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4180 {
4181 EX_RETHROW;
4182 }
4183 }
4184 EX_END_CATCH(SwallowAllExceptions)
4185
4186 DAC_LEAVE();
4187 return status;
4188}
4189
4190HRESULT STDMETHODCALLTYPE
4191ClrDataAccess::EndEnumMethodDefinitionsByAddress(
4192 /* [in] */ CLRDATA_ENUM handle)
4193{
4194 HRESULT status;
4195
4196 DAC_ENTER();
4197
4198 EX_TRY
4199 {
4200 status = EnumMethodDefinitions::CdEnd(handle);
4201 }
4202 EX_CATCH
4203 {
4204 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4205 {
4206 EX_RETHROW;
4207 }
4208 }
4209 EX_END_CATCH(SwallowAllExceptions)
4210
4211 DAC_LEAVE();
4212 return status;
4213}
4214
4215HRESULT STDMETHODCALLTYPE
4216ClrDataAccess::StartEnumMethodInstancesByAddress(
4217 /* [in] */ CLRDATA_ADDRESS address,
4218 /* [in] */ IXCLRDataAppDomain* appDomain,
4219 /* [out] */ CLRDATA_ENUM *handle)
4220{
4221 HRESULT status;
4222
4223 DAC_ENTER();
4224
4225 EX_TRY
4226 {
4227 MethodDesc* methodDesc;
4228
4229 *handle = 0;
4230 status = S_FALSE;
4231 TADDR taddr;
4232 if( (status = TRY_CLRDATA_ADDRESS_TO_TADDR(address, &taddr)) != S_OK )
4233 {
4234 goto Exit;
4235 }
4236
4237 if (IsPossibleCodeAddress(taddr) != S_OK)
4238 {
4239 goto Exit;
4240 }
4241
4242 methodDesc = ExecutionManager::GetCodeMethodDesc(taddr);
4243 if (!methodDesc)
4244 {
4245 goto Exit;
4246 }
4247
4248 status = EnumMethodInstances::CdStart(methodDesc, appDomain,
4249 handle);
4250
4251 Exit: ;
4252 }
4253 EX_CATCH
4254 {
4255 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4256 {
4257 EX_RETHROW;
4258 }
4259 }
4260 EX_END_CATCH(SwallowAllExceptions)
4261
4262 DAC_LEAVE();
4263 return status;
4264}
4265
4266HRESULT STDMETHODCALLTYPE
4267ClrDataAccess::EnumMethodInstanceByAddress(
4268 /* [out][in] */ CLRDATA_ENUM* handle,
4269 /* [out] */ IXCLRDataMethodInstance **method)
4270{
4271 HRESULT status;
4272
4273 DAC_ENTER();
4274
4275 EX_TRY
4276 {
4277 status = EnumMethodInstances::CdNext(this, handle, method);
4278 }
4279 EX_CATCH
4280 {
4281 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4282 {
4283 EX_RETHROW;
4284 }
4285 }
4286 EX_END_CATCH(SwallowAllExceptions)
4287
4288 DAC_LEAVE();
4289 return status;
4290}
4291
4292HRESULT STDMETHODCALLTYPE
4293ClrDataAccess::EndEnumMethodInstancesByAddress(
4294 /* [in] */ CLRDATA_ENUM handle)
4295{
4296 HRESULT status;
4297
4298 DAC_ENTER();
4299
4300 EX_TRY
4301 {
4302 status = EnumMethodInstances::CdEnd(handle);
4303 }
4304 EX_CATCH
4305 {
4306 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4307 {
4308 EX_RETHROW;
4309 }
4310 }
4311 EX_END_CATCH(SwallowAllExceptions)
4312
4313 DAC_LEAVE();
4314 return status;
4315}
4316
4317HRESULT STDMETHODCALLTYPE
4318ClrDataAccess::GetDataByAddress(
4319 /* [in] */ CLRDATA_ADDRESS address,
4320 /* [in] */ ULONG32 flags,
4321 /* [in] */ IXCLRDataAppDomain* appDomain,
4322 /* [in] */ IXCLRDataTask* tlsTask,
4323 /* [in] */ ULONG32 bufLen,
4324 /* [out] */ ULONG32 *nameLen,
4325 /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ],
4326 /* [out] */ IXCLRDataValue **value,
4327 /* [out] */ CLRDATA_ADDRESS *displacement)
4328{
4329 HRESULT status;
4330
4331 if (flags != 0)
4332 {
4333 return E_INVALIDARG;
4334 }
4335
4336 DAC_ENTER();
4337
4338 EX_TRY
4339 {
4340 // XXX Microsoft.
4341 status = E_NOTIMPL;
4342 }
4343 EX_CATCH
4344 {
4345 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4346 {
4347 EX_RETHROW;
4348 }
4349 }
4350 EX_END_CATCH(SwallowAllExceptions)
4351
4352 DAC_LEAVE();
4353 return status;
4354}
4355
4356HRESULT STDMETHODCALLTYPE
4357ClrDataAccess::GetExceptionStateByExceptionRecord(
4358 /* [in] */ EXCEPTION_RECORD64 *record,
4359 /* [out] */ IXCLRDataExceptionState **exception)
4360{
4361 HRESULT status;
4362
4363 DAC_ENTER();
4364
4365 EX_TRY
4366 {
4367 // XXX Microsoft.
4368 status = E_NOTIMPL;
4369 }
4370 EX_CATCH
4371 {
4372 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4373 {
4374 EX_RETHROW;
4375 }
4376 }
4377 EX_END_CATCH(SwallowAllExceptions)
4378
4379 DAC_LEAVE();
4380 return status;
4381}
4382
4383HRESULT STDMETHODCALLTYPE
4384ClrDataAccess::TranslateExceptionRecordToNotification(
4385 /* [in] */ EXCEPTION_RECORD64 *record,
4386 /* [in] */ IXCLRDataExceptionNotification *notify)
4387{
4388 HRESULT status = E_FAIL;
4389 ClrDataModule* pubModule = NULL;
4390 ClrDataMethodInstance* pubMethodInst = NULL;
4391 ClrDataExceptionState* pubExState = NULL;
4392 GcEvtArgs pubGcEvtArgs;
4393 ULONG32 notifyType = 0;
4394 DWORD catcherNativeOffset = 0;
4395 TADDR nativeCodeLocation = NULL;
4396
4397 DAC_ENTER();
4398
4399 EX_TRY
4400 {
4401 //
4402 // We cannot hold the dac lock while calling
4403 // out as the external code can do arbitrary things.
4404 // Instead we make a pass over the exception
4405 // information and create all necessary objects.
4406 // We then leave the lock and make the callbac.
4407 //
4408
4409 TADDR exInfo[EXCEPTION_MAXIMUM_PARAMETERS];
4410 for (UINT i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
4411 {
4412 exInfo[i] = TO_TADDR(record->ExceptionInformation[i]);
4413 }
4414
4415 notifyType = DACNotify::GetType(exInfo);
4416 switch(notifyType)
4417 {
4418 case DACNotify::MODULE_LOAD_NOTIFICATION:
4419 {
4420 TADDR modulePtr;
4421
4422 if (DACNotify::ParseModuleLoadNotification(exInfo, modulePtr))
4423 {
4424 Module* clrModule = PTR_Module(modulePtr);
4425 pubModule = new (nothrow) ClrDataModule(this, clrModule);
4426 if (pubModule == NULL)
4427 {
4428 status = E_OUTOFMEMORY;
4429 }
4430 else
4431 {
4432 status = S_OK;
4433 }
4434 }
4435 break;
4436 }
4437
4438 case DACNotify::MODULE_UNLOAD_NOTIFICATION:
4439 {
4440 TADDR modulePtr;
4441
4442 if (DACNotify::ParseModuleUnloadNotification(exInfo, modulePtr))
4443 {
4444 Module* clrModule = PTR_Module(modulePtr);
4445 pubModule = new (nothrow) ClrDataModule(this, clrModule);
4446 if (pubModule == NULL)
4447 {
4448 status = E_OUTOFMEMORY;
4449 }
4450 else
4451 {
4452 status = S_OK;
4453 }
4454 }
4455 break;
4456 }
4457
4458 case DACNotify::JIT_NOTIFICATION2:
4459 {
4460 TADDR methodDescPtr;
4461
4462 if(DACNotify::ParseJITNotification(exInfo, methodDescPtr, nativeCodeLocation))
4463 {
4464 // Try and find the right appdomain
4465 MethodDesc* methodDesc = PTR_MethodDesc(methodDescPtr);
4466 BaseDomain* baseDomain = methodDesc->GetDomain();
4467 AppDomain* appDomain = NULL;
4468
4469 if (baseDomain->IsAppDomain())
4470 {
4471 appDomain = PTR_AppDomain(PTR_HOST_TO_TADDR(baseDomain));
4472 }
4473 else
4474 {
4475 // Find a likely domain, because it's the shared domain.
4476 AppDomainIterator adi(FALSE);
4477 appDomain = adi.GetDomain();
4478 }
4479
4480 pubMethodInst =
4481 new (nothrow) ClrDataMethodInstance(this,
4482 appDomain,
4483 methodDesc);
4484 if (pubMethodInst == NULL)
4485 {
4486 status = E_OUTOFMEMORY;
4487 }
4488 else
4489 {
4490 status = S_OK;
4491 }
4492 }
4493 break;
4494 }
4495
4496 case DACNotify::EXCEPTION_NOTIFICATION:
4497 {
4498 TADDR threadPtr;
4499
4500 if (DACNotify::ParseExceptionNotification(exInfo, threadPtr))
4501 {
4502 // Translation can only occur at the time of
4503 // receipt of the notify exception, so we assume
4504 // that the Thread's current exception state
4505 // is the state we want.
4506 status = ClrDataExceptionState::
4507 NewFromThread(this,
4508 PTR_Thread(threadPtr),
4509 &pubExState,
4510 NULL);
4511 }
4512 break;
4513 }
4514
4515 case DACNotify::GC_NOTIFICATION:
4516 {
4517 if (DACNotify::ParseGCNotification(exInfo, pubGcEvtArgs))
4518 {
4519 status = S_OK;
4520 }
4521 break;
4522 }
4523
4524 case DACNotify::CATCH_ENTER_NOTIFICATION:
4525 {
4526 TADDR methodDescPtr;
4527 if (DACNotify::ParseExceptionCatcherEnterNotification(exInfo, methodDescPtr, catcherNativeOffset))
4528 {
4529 // Try and find the right appdomain
4530 MethodDesc* methodDesc = PTR_MethodDesc(methodDescPtr);
4531 BaseDomain* baseDomain = methodDesc->GetDomain();
4532 AppDomain* appDomain = NULL;
4533
4534 if (baseDomain->IsAppDomain())
4535 {
4536 appDomain = PTR_AppDomain(PTR_HOST_TO_TADDR(baseDomain));
4537 }
4538 else
4539 {
4540 // Find a likely domain, because it's the shared domain.
4541 AppDomainIterator adi(FALSE);
4542 appDomain = adi.GetDomain();
4543 }
4544
4545 pubMethodInst =
4546 new (nothrow) ClrDataMethodInstance(this,
4547 appDomain,
4548 methodDesc);
4549 if (pubMethodInst == NULL)
4550 {
4551 status = E_OUTOFMEMORY;
4552 }
4553 else
4554 {
4555 status = S_OK;
4556 }
4557 }
4558 break;
4559 }
4560
4561 default:
4562 status = E_INVALIDARG;
4563 break;
4564 }
4565 }
4566 EX_CATCH
4567 {
4568 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4569 {
4570 EX_RETHROW;
4571 }
4572 }
4573 EX_END_CATCH(SwallowAllExceptions)
4574
4575 DAC_LEAVE();
4576
4577 if (status == S_OK)
4578 {
4579 IXCLRDataExceptionNotification2* notify2;
4580
4581 if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification2),
4582 (void**)&notify2) != S_OK)
4583 {
4584 notify2 = NULL;
4585 }
4586
4587 IXCLRDataExceptionNotification3* notify3;
4588 if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification3),
4589 (void**)&notify3) != S_OK)
4590 {
4591 notify3 = NULL;
4592 }
4593
4594 IXCLRDataExceptionNotification4* notify4;
4595 if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification4),
4596 (void**)&notify4) != S_OK)
4597 {
4598 notify4 = NULL;
4599 }
4600
4601 IXCLRDataExceptionNotification5* notify5;
4602 if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification5),
4603 (void**)&notify5) != S_OK)
4604 {
4605 notify5 = NULL;
4606 }
4607
4608 switch(notifyType)
4609 {
4610 case DACNotify::MODULE_LOAD_NOTIFICATION:
4611 notify->OnModuleLoaded(pubModule);
4612 break;
4613
4614 case DACNotify::MODULE_UNLOAD_NOTIFICATION:
4615 notify->OnModuleUnloaded(pubModule);
4616 break;
4617
4618 case DACNotify::JIT_NOTIFICATION2:
4619 notify->OnCodeGenerated(pubMethodInst);
4620
4621 if (notify5)
4622 {
4623 notify5->OnCodeGenerated2(pubMethodInst, TO_CDADDR(nativeCodeLocation));
4624 }
4625 break;
4626
4627 case DACNotify::EXCEPTION_NOTIFICATION:
4628 if (notify2)
4629 {
4630 notify2->OnException(pubExState);
4631 }
4632 else
4633 {
4634 status = E_INVALIDARG;
4635 }
4636 break;
4637
4638 case DACNotify::GC_NOTIFICATION:
4639 if (notify3)
4640 {
4641 notify3->OnGcEvent(pubGcEvtArgs);
4642 }
4643 break;
4644
4645 case DACNotify::CATCH_ENTER_NOTIFICATION:
4646 if (notify4)
4647 {
4648 notify4->ExceptionCatcherEnter(pubMethodInst, catcherNativeOffset);
4649 }
4650 break;
4651
4652 default:
4653 // notifyType has already been validated.
4654 _ASSERTE(FALSE);
4655 break;
4656 }
4657
4658 if (notify2)
4659 {
4660 notify2->Release();
4661 }
4662 if (notify3)
4663 {
4664 notify3->Release();
4665 }
4666 if (notify4)
4667 {
4668 notify4->Release();
4669 }
4670 if (notify5)
4671 {
4672 notify5->Release();
4673 }
4674 }
4675
4676 if (pubModule)
4677 {
4678 pubModule->Release();
4679 }
4680 if (pubMethodInst)
4681 {
4682 pubMethodInst->Release();
4683 }
4684 if (pubExState)
4685 {
4686 pubExState->Release();
4687 }
4688
4689 return status;
4690}
4691
4692HRESULT STDMETHODCALLTYPE
4693ClrDataAccess::CreateMemoryValue(
4694 /* [in] */ IXCLRDataAppDomain* appDomain,
4695 /* [in] */ IXCLRDataTask* tlsTask,
4696 /* [in] */ IXCLRDataTypeInstance* type,
4697 /* [in] */ CLRDATA_ADDRESS addr,
4698 /* [out] */ IXCLRDataValue** value)
4699{
4700 HRESULT status;
4701
4702 DAC_ENTER();
4703
4704 EX_TRY
4705 {
4706 AppDomain* dacDomain;
4707 Thread* dacThread;
4708 TypeHandle dacType;
4709 ULONG32 flags;
4710 NativeVarLocation loc;
4711
4712 dacDomain = ((ClrDataAppDomain*)appDomain)->GetAppDomain();
4713 if (tlsTask)
4714 {
4715 dacThread = ((ClrDataTask*)tlsTask)->GetThread();
4716 }
4717 else
4718 {
4719 dacThread = NULL;
4720 }
4721 dacType = ((ClrDataTypeInstance*)type)->GetTypeHandle();
4722
4723 flags = GetTypeFieldValueFlags(dacType, NULL, 0, false);
4724
4725 loc.addr = addr;
4726 loc.size = dacType.GetSize();
4727 loc.contextReg = false;
4728
4729 *value = new (nothrow)
4730 ClrDataValue(this, dacDomain, dacThread, flags,
4731 dacType, addr, 1, &loc);
4732 status = *value ? S_OK : E_OUTOFMEMORY;
4733 }
4734 EX_CATCH
4735 {
4736 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4737 {
4738 EX_RETHROW;
4739 }
4740 }
4741 EX_END_CATCH(SwallowAllExceptions)
4742
4743 DAC_LEAVE();
4744 return status;
4745}
4746
4747HRESULT STDMETHODCALLTYPE
4748ClrDataAccess::SetAllTypeNotifications(
4749 /* [in] */ IXCLRDataModule* mod,
4750 /* [in] */ ULONG32 flags)
4751{
4752 HRESULT status;
4753
4754 DAC_ENTER();
4755
4756 EX_TRY
4757 {
4758 // XXX Microsoft.
4759 status = E_NOTIMPL;
4760 }
4761 EX_CATCH
4762 {
4763 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4764 {
4765 EX_RETHROW;
4766 }
4767 }
4768 EX_END_CATCH(SwallowAllExceptions)
4769
4770 DAC_LEAVE();
4771 return status;
4772}
4773
4774HRESULT STDMETHODCALLTYPE
4775ClrDataAccess::SetAllCodeNotifications(
4776 /* [in] */ IXCLRDataModule* mod,
4777 /* [in] */ ULONG32 flags)
4778{
4779 HRESULT status;
4780
4781 DAC_ENTER();
4782
4783 EX_TRY
4784 {
4785 status = E_FAIL;
4786
4787 if (!IsValidMethodCodeNotification(flags))
4788 {
4789 status = E_INVALIDARG;
4790 }
4791 else
4792 {
4793 JITNotifications jn(GetHostJitNotificationTable());
4794 if (!jn.IsActive())
4795 {
4796 status = E_OUTOFMEMORY;
4797 }
4798 else
4799 {
4800 BOOL changedTable;
4801 TADDR modulePtr = mod ?
4802 PTR_HOST_TO_TADDR(((ClrDataModule*)mod)->GetModule()) :
4803 NULL;
4804
4805 if (jn.SetAllNotifications(modulePtr, flags, &changedTable))
4806 {
4807 if (!changedTable ||
4808 (changedTable && jn.UpdateOutOfProcTable()))
4809 {
4810 status = S_OK;
4811 }
4812 }
4813 }
4814 }
4815 }
4816 EX_CATCH
4817 {
4818 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4819 {
4820 EX_RETHROW;
4821 }
4822 }
4823 EX_END_CATCH(SwallowAllExceptions)
4824
4825 DAC_LEAVE();
4826 return status;
4827}
4828
4829HRESULT STDMETHODCALLTYPE
4830ClrDataAccess::GetTypeNotifications(
4831 /* [in] */ ULONG32 numTokens,
4832 /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[],
4833 /* [in] */ IXCLRDataModule* singleMod,
4834 /* [in, size_is(numTokens)] */ mdTypeDef tokens[],
4835 /* [out, size_is(numTokens)] */ ULONG32 flags[])
4836{
4837 HRESULT status;
4838
4839 DAC_ENTER();
4840
4841 EX_TRY
4842 {
4843 // XXX Microsoft.
4844 status = E_NOTIMPL;
4845 }
4846 EX_CATCH
4847 {
4848 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4849 {
4850 EX_RETHROW;
4851 }
4852 }
4853 EX_END_CATCH(SwallowAllExceptions)
4854
4855 DAC_LEAVE();
4856 return status;
4857}
4858
4859HRESULT STDMETHODCALLTYPE
4860ClrDataAccess::SetTypeNotifications(
4861 /* [in] */ ULONG32 numTokens,
4862 /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[],
4863 /* [in] */ IXCLRDataModule* singleMod,
4864 /* [in, size_is(numTokens)] */ mdTypeDef tokens[],
4865 /* [in, size_is(numTokens)] */ ULONG32 flags[],
4866 /* [in] */ ULONG32 singleFlags)
4867{
4868 HRESULT status;
4869
4870 DAC_ENTER();
4871
4872 EX_TRY
4873 {
4874 // XXX Microsoft.
4875 status = E_NOTIMPL;
4876 }
4877 EX_CATCH
4878 {
4879 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4880 {
4881 EX_RETHROW;
4882 }
4883 }
4884 EX_END_CATCH(SwallowAllExceptions)
4885
4886 DAC_LEAVE();
4887 return status;
4888}
4889
4890HRESULT STDMETHODCALLTYPE
4891ClrDataAccess::GetCodeNotifications(
4892 /* [in] */ ULONG32 numTokens,
4893 /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[],
4894 /* [in] */ IXCLRDataModule* singleMod,
4895 /* [in, size_is(numTokens)] */ mdMethodDef tokens[],
4896 /* [out, size_is(numTokens)] */ ULONG32 flags[])
4897{
4898 HRESULT status;
4899
4900 DAC_ENTER();
4901
4902 EX_TRY
4903 {
4904 if ((flags == NULL || tokens == NULL) ||
4905 (mods == NULL && singleMod == NULL) ||
4906 (mods != NULL && singleMod != NULL))
4907 {
4908 status = E_INVALIDARG;
4909 }
4910 else
4911 {
4912 JITNotifications jn(GetHostJitNotificationTable());
4913 if (!jn.IsActive())
4914 {
4915 status = E_OUTOFMEMORY;
4916 }
4917 else
4918 {
4919 TADDR modulePtr = NULL;
4920 if (singleMod)
4921 {
4922 modulePtr = PTR_HOST_TO_TADDR(((ClrDataModule*)singleMod)->
4923 GetModule());
4924 }
4925
4926 for (ULONG32 i = 0; i < numTokens; i++)
4927 {
4928 if (singleMod == NULL)
4929 {
4930 modulePtr =
4931 PTR_HOST_TO_TADDR(((ClrDataModule*)mods[i])->
4932 GetModule());
4933 }
4934 USHORT jt = jn.Requested(modulePtr, tokens[i]);
4935 flags[i] = jt;
4936 }
4937
4938 status = S_OK;
4939 }
4940 }
4941 }
4942 EX_CATCH
4943 {
4944 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
4945 {
4946 EX_RETHROW;
4947 }
4948 }
4949 EX_END_CATCH(SwallowAllExceptions)
4950
4951 DAC_LEAVE();
4952 return status;
4953}
4954
4955HRESULT STDMETHODCALLTYPE
4956ClrDataAccess::SetCodeNotifications(
4957 /* [in] */ ULONG32 numTokens,
4958 /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[],
4959 /* [in] */ IXCLRDataModule* singleMod,
4960 /* [in, size_is(numTokens)] */ mdMethodDef tokens[],
4961 /* [in, size_is(numTokens)] */ ULONG32 flags[],
4962 /* [in] */ ULONG32 singleFlags)
4963{
4964 HRESULT status = E_UNEXPECTED;
4965
4966 DAC_ENTER();
4967
4968 EX_TRY
4969 {
4970 if ((tokens == NULL) ||
4971 (mods == NULL && singleMod == NULL) ||
4972 (mods != NULL && singleMod != NULL))
4973 {
4974 status = E_INVALIDARG;
4975 }
4976 else
4977 {
4978 JITNotifications jn(GetHostJitNotificationTable());
4979 if (!jn.IsActive() || numTokens > jn.GetTableSize())
4980 {
4981 status = E_OUTOFMEMORY;
4982 }
4983 else
4984 {
4985 BOOL changedTable = FALSE;
4986
4987 // Are flags valid?
4988 if (flags)
4989 {
4990 for (ULONG32 check = 0; check < numTokens; check++)
4991 {
4992 if (!IsValidMethodCodeNotification(flags[check]))
4993 {
4994 status = E_INVALIDARG;
4995 goto Exit;
4996 }
4997 }
4998 }
4999 else if (!IsValidMethodCodeNotification(singleFlags))
5000 {
5001 status = E_INVALIDARG;
5002 goto Exit;
5003 }
5004
5005 TADDR modulePtr = NULL;
5006 if (singleMod)
5007 {
5008 modulePtr =
5009 PTR_HOST_TO_TADDR(((ClrDataModule*)singleMod)->
5010 GetModule());
5011 }
5012
5013 for (ULONG32 i = 0; i < numTokens; i++)
5014 {
5015 if (singleMod == NULL)
5016 {
5017 modulePtr =
5018 PTR_HOST_TO_TADDR(((ClrDataModule*)mods[i])->
5019 GetModule());
5020 }
5021
5022 USHORT curFlags = jn.Requested(modulePtr, tokens[i]);
5023 USHORT setFlags = (USHORT)(flags ? flags[i] : singleFlags);
5024
5025 if (curFlags != setFlags)
5026 {
5027 if (!jn.SetNotification(modulePtr, tokens[i],
5028 setFlags))
5029 {
5030 status = E_FAIL;
5031 goto Exit;
5032 }
5033
5034 changedTable = TRUE;
5035 }
5036 }
5037
5038 if (!changedTable ||
5039 (changedTable && jn.UpdateOutOfProcTable()))
5040 {
5041 status = S_OK;
5042 }
5043 }
5044 }
5045
5046Exit: ;
5047 }
5048 EX_CATCH
5049 {
5050 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5051 {
5052 EX_RETHROW;
5053 }
5054 }
5055 EX_END_CATCH(SwallowAllExceptions)
5056
5057 DAC_LEAVE();
5058 return status;
5059}
5060
5061HRESULT
5062ClrDataAccess::GetOtherNotificationFlags(
5063 /* [out] */ ULONG32* flags)
5064{
5065 HRESULT status;
5066
5067 DAC_ENTER();
5068
5069 EX_TRY
5070 {
5071 *flags = g_dacNotificationFlags;
5072 status = S_OK;
5073 }
5074 EX_CATCH
5075 {
5076 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5077 {
5078 EX_RETHROW;
5079 }
5080 }
5081 EX_END_CATCH(SwallowAllExceptions)
5082
5083 DAC_LEAVE();
5084 return status;
5085}
5086
5087HRESULT
5088ClrDataAccess::SetOtherNotificationFlags(
5089 /* [in] */ ULONG32 flags)
5090{
5091 HRESULT status;
5092
5093 if ((flags & ~(CLRDATA_NOTIFY_ON_MODULE_LOAD |
5094 CLRDATA_NOTIFY_ON_MODULE_UNLOAD |
5095 CLRDATA_NOTIFY_ON_EXCEPTION |
5096 CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER)) != 0)
5097 {
5098 return E_INVALIDARG;
5099 }
5100
5101 DAC_ENTER();
5102
5103 EX_TRY
5104 {
5105 g_dacNotificationFlags = flags;
5106 status = S_OK;
5107 }
5108 EX_CATCH
5109 {
5110 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5111 {
5112 EX_RETHROW;
5113 }
5114 }
5115 EX_END_CATCH(SwallowAllExceptions)
5116
5117 DAC_LEAVE();
5118 return status;
5119}
5120
5121enum
5122{
5123 STUB_BUF_FLAGS_START,
5124
5125 STUB_BUF_METHOD_JITTED,
5126 STUB_BUF_FRAME_PUSHED,
5127 STUB_BUF_STUB_MANAGER_PUSHED,
5128
5129 STUB_BUF_FLAGS_END,
5130};
5131
5132union STUB_BUF
5133{
5134 CLRDATA_FOLLOW_STUB_BUFFER apiBuf;
5135 struct
5136 {
5137 ULONG64 flags;
5138 ULONG64 addr;
5139 ULONG64 arg1;
5140 } u;
5141};
5142
5143HRESULT
5144ClrDataAccess::FollowStubStep(
5145 /* [in] */ Thread* thread,
5146 /* [in] */ ULONG32 inFlags,
5147 /* [in] */ TADDR inAddr,
5148 /* [in] */ union STUB_BUF* inBuffer,
5149 /* [out] */ TADDR* outAddr,
5150 /* [out] */ union STUB_BUF* outBuffer,
5151 /* [out] */ ULONG32* outFlags)
5152{
5153 TraceDestination trace;
5154 bool traceDone = false;
5155 BYTE* retAddr;
5156 T_CONTEXT localContext;
5157 REGDISPLAY regDisp;
5158 MethodDesc* methodDesc;
5159
5160 ZeroMemory(outBuffer, sizeof(*outBuffer));
5161
5162 if (inBuffer)
5163 {
5164 switch(inBuffer->u.flags)
5165 {
5166 case STUB_BUF_METHOD_JITTED:
5167 if (inAddr != GFN_TADDR(DACNotifyCompilationFinished))
5168 {
5169 return E_INVALIDARG;
5170 }
5171
5172 // It's possible that this notification is
5173 // for a different method, so double-check
5174 // and recycle the notification if necessary.
5175 methodDesc = PTR_MethodDesc(CORDB_ADDRESS_TO_TADDR(inBuffer->u.addr));
5176 if (methodDesc->HasNativeCode())
5177 {
5178 *outAddr = methodDesc->GetNativeCode();
5179 *outFlags = CLRDATA_FOLLOW_STUB_EXIT;
5180 return S_OK;
5181 }
5182
5183 // We didn't end up with native code so try again.
5184 trace.InitForUnjittedMethod(methodDesc);
5185 traceDone = true;
5186 break;
5187
5188 case STUB_BUF_FRAME_PUSHED:
5189 if (!thread ||
5190 inAddr != inBuffer->u.addr)
5191 {
5192 return E_INVALIDARG;
5193 }
5194
5195 trace.InitForFramePush(CORDB_ADDRESS_TO_TADDR(inBuffer->u.addr));
5196 DacGetThreadContext(thread, &localContext);
5197 thread->FillRegDisplay(&regDisp, &localContext);
5198 if (!thread->GetFrame()->
5199 TraceFrame(thread,
5200 TRUE,
5201 &trace,
5202 &regDisp))
5203 {
5204 return E_FAIL;
5205 }
5206
5207 traceDone = true;
5208 break;
5209
5210 case STUB_BUF_STUB_MANAGER_PUSHED:
5211 if (!thread ||
5212 inAddr != inBuffer->u.addr ||
5213 !inBuffer->u.arg1)
5214 {
5215 return E_INVALIDARG;
5216 }
5217
5218 trace.InitForManagerPush(CORDB_ADDRESS_TO_TADDR(inBuffer->u.addr),
5219 PTR_StubManager(CORDB_ADDRESS_TO_TADDR(inBuffer->u.arg1)));
5220 DacGetThreadContext(thread, &localContext);
5221 if (!trace.GetStubManager()->
5222 TraceManager(thread,
5223 &trace,
5224 &localContext,
5225 &retAddr))
5226 {
5227 return E_FAIL;
5228 }
5229
5230 traceDone = true;
5231 break;
5232
5233 default:
5234 return E_INVALIDARG;
5235 }
5236 }
5237
5238 if ((!traceDone &&
5239 !StubManager::TraceStub(inAddr, &trace)) ||
5240 !StubManager::FollowTrace(&trace))
5241 {
5242 return E_NOINTERFACE;
5243 }
5244
5245 switch(trace.GetTraceType())
5246 {
5247 case TRACE_UNMANAGED:
5248 case TRACE_MANAGED:
5249 // We've hit non-stub code so we're done.
5250 *outAddr = trace.GetAddress();
5251 *outFlags = CLRDATA_FOLLOW_STUB_EXIT;
5252 break;
5253
5254 case TRACE_UNJITTED_METHOD:
5255 // The stub causes jitting, so return
5256 // the address of the jit-complete routine
5257 // so that the real native address can
5258 // be picked up once the JIT is done.
5259
5260 // One special case is ngen'ed code that
5261 // needs the prestub run. This results in
5262 // an unjitted trace but no jitting will actually
5263 // occur since the code is ngen'ed. Detect
5264 // this and redirect to the actual code.
5265 methodDesc = trace.GetMethodDesc();
5266 if (methodDesc->IsPreImplemented() &&
5267 !methodDesc->IsPointingToStableNativeCode() &&
5268 !methodDesc->IsGenericMethodDefinition() &&
5269 methodDesc->HasNativeCode())
5270 {
5271 *outAddr = methodDesc->GetNativeCode();
5272 *outFlags = CLRDATA_FOLLOW_STUB_EXIT;
5273 break;
5274 }
5275
5276 *outAddr = GFN_TADDR(DACNotifyCompilationFinished);
5277 outBuffer->u.flags = STUB_BUF_METHOD_JITTED;
5278 outBuffer->u.addr = PTR_HOST_TO_TADDR(methodDesc);
5279 *outFlags = CLRDATA_FOLLOW_STUB_INTERMEDIATE;
5280 break;
5281
5282 case TRACE_FRAME_PUSH:
5283 if (!thread)
5284 {
5285 return E_INVALIDARG;
5286 }
5287
5288 *outAddr = trace.GetAddress();
5289 outBuffer->u.flags = STUB_BUF_FRAME_PUSHED;
5290 outBuffer->u.addr = trace.GetAddress();
5291 *outFlags = CLRDATA_FOLLOW_STUB_INTERMEDIATE;
5292 break;
5293
5294 case TRACE_MGR_PUSH:
5295 if (!thread)
5296 {
5297 return E_INVALIDARG;
5298 }
5299
5300 *outAddr = trace.GetAddress();
5301 outBuffer->u.flags = STUB_BUF_STUB_MANAGER_PUSHED;
5302 outBuffer->u.addr = trace.GetAddress();
5303 outBuffer->u.arg1 = PTR_HOST_TO_TADDR(trace.GetStubManager());
5304 *outFlags = CLRDATA_FOLLOW_STUB_INTERMEDIATE;
5305 break;
5306
5307 default:
5308 return E_INVALIDARG;
5309 }
5310
5311 return S_OK;
5312}
5313
5314HRESULT STDMETHODCALLTYPE
5315ClrDataAccess::FollowStub(
5316 /* [in] */ ULONG32 inFlags,
5317 /* [in] */ CLRDATA_ADDRESS inAddr,
5318 /* [in] */ CLRDATA_FOLLOW_STUB_BUFFER* _inBuffer,
5319 /* [out] */ CLRDATA_ADDRESS* outAddr,
5320 /* [out] */ CLRDATA_FOLLOW_STUB_BUFFER* _outBuffer,
5321 /* [out] */ ULONG32* outFlags)
5322{
5323 return FollowStub2(NULL, inFlags, inAddr, _inBuffer,
5324 outAddr, _outBuffer, outFlags);
5325}
5326
5327HRESULT STDMETHODCALLTYPE
5328ClrDataAccess::FollowStub2(
5329 /* [in] */ IXCLRDataTask* task,
5330 /* [in] */ ULONG32 inFlags,
5331 /* [in] */ CLRDATA_ADDRESS _inAddr,
5332 /* [in] */ CLRDATA_FOLLOW_STUB_BUFFER* _inBuffer,
5333 /* [out] */ CLRDATA_ADDRESS* _outAddr,
5334 /* [out] */ CLRDATA_FOLLOW_STUB_BUFFER* _outBuffer,
5335 /* [out] */ ULONG32* outFlags)
5336{
5337 HRESULT status;
5338
5339 if ((inFlags & ~(CLRDATA_FOLLOW_STUB_DEFAULT)) != 0)
5340 {
5341 return E_INVALIDARG;
5342 }
5343
5344 STUB_BUF* inBuffer = (STUB_BUF*)_inBuffer;
5345 STUB_BUF* outBuffer = (STUB_BUF*)_outBuffer;
5346
5347 if (inBuffer &&
5348 (inBuffer->u.flags <= STUB_BUF_FLAGS_START ||
5349 inBuffer->u.flags >= STUB_BUF_FLAGS_END))
5350 {
5351 return E_INVALIDARG;
5352 }
5353
5354 DAC_ENTER();
5355
5356 EX_TRY
5357 {
5358 STUB_BUF cycleBuf;
5359 TADDR inAddr = TO_TADDR(_inAddr);
5360 TADDR outAddr;
5361 Thread* thread = task ? ((ClrDataTask*)task)->GetThread() : NULL;
5362 ULONG32 loops = 4;
5363
5364 for (;;)
5365 {
5366 if ((status = FollowStubStep(thread,
5367 inFlags,
5368 inAddr,
5369 inBuffer,
5370 &outAddr,
5371 outBuffer,
5372 outFlags)) != S_OK)
5373 {
5374 break;
5375 }
5376
5377 // Some stub tracing just requests further iterations
5378 // of processing, so detect that case and loop.
5379 if (outAddr != inAddr)
5380 {
5381 // We can make forward progress, we're done.
5382 *_outAddr = TO_CDADDR(outAddr);
5383 break;
5384 }
5385
5386 // We need more processing. As a protection
5387 // against infinite loops in corrupted or buggy
5388 // situations, we only allow this to happen a
5389 // small number of times.
5390 if (--loops == 0)
5391 {
5392 ZeroMemory(outBuffer, sizeof(*outBuffer));
5393 status = E_FAIL;
5394 break;
5395 }
5396
5397 cycleBuf = *outBuffer;
5398 inBuffer = &cycleBuf;
5399 }
5400 }
5401 EX_CATCH
5402 {
5403 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5404 {
5405 EX_RETHROW;
5406 }
5407 }
5408 EX_END_CATCH(SwallowAllExceptions)
5409
5410 DAC_LEAVE();
5411 return status;
5412}
5413
5414#ifdef _MSC_VER
5415#pragma warning(push)
5416#pragma warning(disable:4297)
5417#endif // _MSC_VER
5418STDMETHODIMP
5419ClrDataAccess::GetGcNotification(GcEvtArgs* gcEvtArgs)
5420{
5421 HRESULT status;
5422
5423 DAC_ENTER();
5424
5425 EX_TRY
5426 {
5427 if (gcEvtArgs->typ >= GC_EVENT_TYPE_MAX)
5428 {
5429 status = E_INVALIDARG;
5430 }
5431 else
5432 {
5433 GcNotifications gn(GetHostGcNotificationTable());
5434 if (!gn.IsActive())
5435 {
5436 status = E_OUTOFMEMORY;
5437 }
5438 else
5439 {
5440 GcEvtArgs *res = gn.GetNotification(*gcEvtArgs);
5441 if (res != NULL)
5442 {
5443 *gcEvtArgs = *res;
5444 status = S_OK;
5445 }
5446 else
5447 {
5448 status = E_FAIL;
5449 }
5450 }
5451 }
5452 }
5453 EX_CATCH
5454 {
5455 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5456 {
5457 EX_RETHROW;
5458 }
5459 }
5460 EX_END_CATCH(SwallowAllExceptions)
5461
5462 DAC_LEAVE();
5463 return status;
5464}
5465
5466STDMETHODIMP
5467ClrDataAccess::SetGcNotification(IN GcEvtArgs gcEvtArgs)
5468{
5469 HRESULT status;
5470
5471 DAC_ENTER();
5472
5473 EX_TRY
5474 {
5475 if (gcEvtArgs.typ >= GC_EVENT_TYPE_MAX)
5476 {
5477 status = E_INVALIDARG;
5478 }
5479 else
5480 {
5481 GcNotifications gn(GetHostGcNotificationTable());
5482 if (!gn.IsActive())
5483 {
5484 status = E_OUTOFMEMORY;
5485 }
5486 else
5487 {
5488 if (gn.SetNotification(gcEvtArgs) && gn.UpdateOutOfProcTable())
5489 {
5490 status = S_OK;
5491 }
5492 else
5493 {
5494 status = E_FAIL;
5495 }
5496 }
5497 }
5498 }
5499 EX_CATCH
5500 {
5501 if (!DacExceptionFilter(GET_EXCEPTION(), this, &status))
5502 {
5503 EX_RETHROW;
5504 }
5505 }
5506 EX_END_CATCH(SwallowAllExceptions)
5507
5508 DAC_LEAVE();
5509 return status;
5510}
5511
5512#ifdef _MSC_VER
5513#pragma warning(pop)
5514#endif // _MSC_VER
5515
5516HRESULT
5517ClrDataAccess::Initialize(void)
5518{
5519 HRESULT hr;
5520 CLRDATA_ADDRESS base;
5521
5522 //
5523 // We do not currently support cross-platform
5524 // debugging. Verify that cross-platform is not
5525 // being attempted.
5526 //
5527
5528 // Determine our platform based on the pre-processor macros set when we were built
5529
5530#ifdef FEATURE_PAL
5531 #if defined(DBG_TARGET_X86)
5532 CorDebugPlatform hostPlatform = CORDB_PLATFORM_POSIX_X86;
5533 #elif defined(DBG_TARGET_AMD64)
5534 CorDebugPlatform hostPlatform = CORDB_PLATFORM_POSIX_AMD64;
5535 #elif defined(DBG_TARGET_ARM)
5536 CorDebugPlatform hostPlatform = CORDB_PLATFORM_POSIX_ARM;
5537 #elif defined(DBG_TARGET_ARM64)
5538 CorDebugPlatform hostPlatform = CORDB_PLATFORM_POSIX_ARM64;
5539 #else
5540 #error Unknown Processor.
5541 #endif
5542#else
5543 #if defined(DBG_TARGET_X86)
5544 CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_X86;
5545 #elif defined(DBG_TARGET_AMD64)
5546 CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_AMD64;
5547 #elif defined(DBG_TARGET_ARM)
5548 CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_ARM;
5549 #elif defined(DBG_TARGET_ARM64)
5550 CorDebugPlatform hostPlatform = CORDB_PLATFORM_WINDOWS_ARM64;
5551 #else
5552 #error Unknown Processor.
5553 #endif
5554#endif
5555
5556 CorDebugPlatform targetPlatform;
5557 IfFailRet(m_pTarget->GetPlatform(&targetPlatform));
5558
5559 if (targetPlatform != hostPlatform)
5560 {
5561 // DAC fatal error: Platform mismatch - the platform reported by the data target
5562 // is not what this version of mscordacwks.dll was built for.
5563 return CORDBG_E_UNCOMPATIBLE_PLATFORMS;
5564 }
5565
5566 //
5567 // Get the current DLL base for mscorwks globals.
5568 // In case of multiple-CLRs, there may be multiple dlls named "mscorwks".
5569 // code:OpenVirtualProcess can take the base address (clrInstanceId) to select exactly
5570 // which CLR to is being target. If so, m_globalBase will already be set.
5571 //
5572
5573 if (m_globalBase == 0)
5574 {
5575 // Caller didn't specify which CLR to debug. This supports Whidbey SOS cases, so we should
5576 // be using a legacy data target.
5577 if (m_pLegacyTarget == NULL)
5578 {
5579 DacError(E_INVALIDARG);
5580 UNREACHABLE();
5581 }
5582
5583 // Since this is Whidbey, assume there's only 1 CLR named "mscorwks.dll" and pick that.
5584 IfFailRet(m_pLegacyTarget->GetImageBase(MAIN_CLR_DLL_NAME_W, &base));
5585
5586 m_globalBase = TO_TADDR(base);
5587 }
5588
5589 // We don't need to try too hard to prevent
5590 // multiple initializations as each one will
5591 // copy the same data into the globals and so
5592 // cannot interfere with each other.
5593 if (!s_procInit)
5594 {
5595 IfFailRet(GetDacGlobals());
5596 IfFailRet(DacGetHostVtPtrs());
5597 s_procInit = true;
5598 }
5599
5600 //
5601 // DAC is now setup and ready to use
5602 //
5603
5604 // Do some validation
5605 IfFailRet(VerifyDlls());
5606
5607 // To support EH SxS, utilcode requires the base address of the runtime
5608 // as part of its initialization so that functions like "WasThrownByUs" work correctly since
5609 // they use the CLR base address to check if an exception was raised by a given instance of the runtime
5610 // or not.
5611 //
5612 // Thus, when DAC is initialized, initialize utilcode with the base address of the runtime loaded in the
5613 // target process. This is similar to work done in CorDB::SetTargetCLR for mscordbi.
5614
5615 // Initialize UtilCode for SxS scenarios
5616 CoreClrCallbacks cccallbacks;
5617 cccallbacks.m_hmodCoreCLR = (HINSTANCE)m_globalBase; // Base address of the runtime in the target process
5618 cccallbacks.m_pfnIEE = NULL;
5619 cccallbacks.m_pfnGetCORSystemDirectory = NULL;
5620 cccallbacks.m_pfnGetCLRFunction = NULL;
5621 InitUtilcode(cccallbacks);
5622
5623 return S_OK;
5624}
5625
5626Thread*
5627ClrDataAccess::FindClrThreadByTaskId(ULONG64 taskId)
5628{
5629 Thread* thread = NULL;
5630
5631 if (!ThreadStore::s_pThreadStore)
5632 {
5633 return NULL;
5634 }
5635
5636 while ((thread = ThreadStore::GetAllThreadList(thread, 0, 0)))
5637 {
5638 if (thread->GetThreadId() == (DWORD)taskId)
5639 {
5640 return thread;
5641 }
5642 }
5643
5644 return NULL;
5645}
5646
5647HRESULT
5648ClrDataAccess::IsPossibleCodeAddress(IN TADDR address)
5649{
5650 SUPPORTS_DAC;
5651 BYTE testRead;
5652 ULONG32 testDone;
5653
5654 // First do a trivial check on the readability of the
5655 // address. This makes for quick rejection of bogus
5656 // addresses that the debugger sends in when searching
5657 // stacks for return addresses.
5658 // XXX Microsoft - Will this cause problems in minidumps
5659 // where it's possible the stub is identifiable but
5660 // the stub code isn't present? Yes, but the lack
5661 // of that code could confuse the walker on its own
5662 // if it does code analysis.
5663 if ((m_pTarget->ReadVirtual(address, &testRead, sizeof(testRead),
5664 &testDone) != S_OK) ||
5665 !testDone)
5666 {
5667 return E_INVALIDARG;
5668 }
5669
5670 return S_OK;
5671}
5672
5673HRESULT
5674ClrDataAccess::GetFullMethodName(
5675 IN MethodDesc* methodDesc,
5676 IN ULONG32 symbolChars,
5677 OUT ULONG32* symbolLen,
5678 __out_ecount_part_opt(symbolChars, *symbolLen) LPWSTR symbol
5679 )
5680{
5681 StackSString s;
5682#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
5683 PAL_CPP_TRY
5684 {
5685#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
5686
5687 TypeString::AppendMethodInternal(s, methodDesc, TypeString::FormatSignature|TypeString::FormatNamespace|TypeString::FormatFullInst);
5688
5689#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
5690 }
5691 PAL_CPP_CATCH_ALL
5692 {
5693 if (!MdCacheGetEEName(dac_cast<TADDR>(methodDesc), s))
5694 {
5695 PAL_CPP_RETHROW;
5696 }
5697 }
5698 PAL_CPP_ENDTRY
5699#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
5700
5701 if (symbol)
5702 {
5703 // Copy as much as we can and truncate the rest.
5704 wcsncpy_s(symbol, symbolChars, s.GetUnicode(), _TRUNCATE);
5705 }
5706
5707 if (symbolLen)
5708 *symbolLen = s.GetCount() + 1;
5709
5710 if (symbol != NULL && symbolChars < (s.GetCount() + 1))
5711 return S_FALSE;
5712 else
5713 return S_OK;
5714}
5715
5716PCSTR
5717ClrDataAccess::GetJitHelperName(
5718 IN TADDR address,
5719 IN bool dynamicHelpersOnly /*=false*/
5720 )
5721{
5722 const static PCSTR s_rgHelperNames[] = {
5723#define JITHELPER(code,fn,sig) #code,
5724#include <jithelpers.h>
5725 };
5726 static_assert_no_msg(COUNTOF(s_rgHelperNames) == CORINFO_HELP_COUNT);
5727
5728#ifdef FEATURE_PAL
5729 if (!dynamicHelpersOnly)
5730#else
5731 if (!dynamicHelpersOnly && g_runtimeLoadedBaseAddress <= address &&
5732 address < g_runtimeLoadedBaseAddress + g_runtimeVirtualSize)
5733#endif // FEATURE_PAL
5734 {
5735 // Read the whole table from the target in one shot for better performance
5736 VMHELPDEF * pTable = static_cast<VMHELPDEF *>(
5737 PTR_READ(dac_cast<TADDR>(&hlpFuncTable), CORINFO_HELP_COUNT * sizeof(VMHELPDEF)));
5738
5739 for (int i = 0; i < CORINFO_HELP_COUNT; i++)
5740 {
5741 if (address == (TADDR)(pTable[i].pfnHelper))
5742 return s_rgHelperNames[i];
5743 }
5744 }
5745
5746 // Check if its a dynamically generated JIT helper
5747 const static CorInfoHelpFunc s_rgDynamicHCallIds[] = {
5748#define DYNAMICJITHELPER(code, fn, sig) code,
5749#define JITHELPER(code, fn,sig)
5750#include <jithelpers.h>
5751 };
5752
5753 // Read the whole table from the target in one shot for better performance
5754 VMHELPDEF * pDynamicTable = static_cast<VMHELPDEF *>(
5755 PTR_READ(dac_cast<TADDR>(&hlpDynamicFuncTable), DYNAMIC_CORINFO_HELP_COUNT * sizeof(VMHELPDEF)));
5756 for (unsigned d = 0; d < DYNAMIC_CORINFO_HELP_COUNT; d++)
5757 {
5758 if (address == (TADDR)(pDynamicTable[d].pfnHelper))
5759 {
5760 return s_rgHelperNames[s_rgDynamicHCallIds[d]];
5761 }
5762 }
5763
5764 return NULL;
5765}
5766
5767HRESULT
5768ClrDataAccess::RawGetMethodName(
5769 /* [in] */ CLRDATA_ADDRESS address,
5770 /* [in] */ ULONG32 flags,
5771 /* [in] */ ULONG32 bufLen,
5772 /* [out] */ ULONG32 *symbolLen,
5773 /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR symbolBuf[ ],
5774 /* [out] */ CLRDATA_ADDRESS* displacement)
5775{
5776#ifdef _TARGET_ARM_
5777 _ASSERTE((address & THUMB_CODE) == 0);
5778 address &= ~THUMB_CODE;
5779#endif
5780
5781 const UINT k_cch64BitHexFormat = COUNTOF("1234567812345678");
5782 HRESULT status;
5783
5784 if (flags != 0)
5785 {
5786 return E_INVALIDARG;
5787 }
5788
5789 TADDR taddr;
5790 if( (status = TRY_CLRDATA_ADDRESS_TO_TADDR(address, &taddr)) != S_OK )
5791 {
5792 return status;
5793 }
5794
5795 if ((status = IsPossibleCodeAddress(taddr)) != S_OK)
5796 {
5797 return status;
5798 }
5799
5800 PTR_StubManager pStubManager;
5801 MethodDesc* methodDesc = NULL;
5802
5803 {
5804 EECodeInfo codeInfo(TO_TADDR(address));
5805 if (codeInfo.IsValid())
5806 {
5807 if (displacement)
5808 {
5809 *displacement = codeInfo.GetRelOffset();
5810 }
5811
5812 methodDesc = codeInfo.GetMethodDesc();
5813 goto NameFromMethodDesc;
5814 }
5815 }
5816
5817 pStubManager = StubManager::FindStubManager(TO_TADDR(address));
5818 if (pStubManager != NULL)
5819 {
5820 if (displacement)
5821 {
5822 *displacement = 0;
5823 }
5824
5825 //
5826 // Special-cased stub managers
5827 //
5828#ifdef FEATURE_PREJIT
5829 if (pStubManager == RangeSectionStubManager::g_pManager)
5830 {
5831 switch (RangeSectionStubManager::GetStubKind(TO_TADDR(address)))
5832 {
5833 case STUB_CODE_BLOCK_PRECODE:
5834 goto PrecodeStub;
5835
5836 case STUB_CODE_BLOCK_JUMPSTUB:
5837 goto JumpStub;
5838
5839 default:
5840 break;
5841 }
5842 }
5843 else
5844#endif
5845 if (pStubManager == PrecodeStubManager::g_pManager)
5846 {
5847#ifdef FEATURE_PREJIT
5848 PrecodeStub:
5849#endif
5850 PCODE alignedAddress = AlignDown(TO_TADDR(address), PRECODE_ALIGNMENT);
5851
5852#ifdef _TARGET_ARM_
5853 alignedAddress += THUMB_CODE;
5854#endif
5855
5856 SIZE_T maxPrecodeSize = sizeof(StubPrecode);
5857
5858#ifdef HAS_THISPTR_RETBUF_PRECODE
5859 maxPrecodeSize = max(maxPrecodeSize, sizeof(ThisPtrRetBufPrecode));
5860#endif
5861
5862 for (SIZE_T i = 0; i < maxPrecodeSize / PRECODE_ALIGNMENT; i++)
5863 {
5864 EX_TRY
5865 {
5866 // Try to find matching precode entrypoint
5867 Precode* pPrecode = Precode::GetPrecodeFromEntryPoint(alignedAddress, TRUE);
5868 if (pPrecode != NULL)
5869 {
5870 methodDesc = pPrecode->GetMethodDesc();
5871 if (methodDesc != NULL)
5872 {
5873 if (DacValidateMD(methodDesc))
5874 {
5875 if (displacement)
5876 {
5877 *displacement = TO_TADDR(address) - PCODEToPINSTR(alignedAddress);
5878 }
5879 goto NameFromMethodDesc;
5880 }
5881 }
5882 }
5883 alignedAddress -= PRECODE_ALIGNMENT;
5884 }
5885 EX_CATCH
5886 {
5887 }
5888 EX_END_CATCH(SwallowAllExceptions)
5889 }
5890 }
5891 else
5892 if (pStubManager == JumpStubStubManager::g_pManager)
5893 {
5894#ifdef FEATURE_PREJIT
5895 JumpStub:
5896#endif
5897 PCODE pTarget = decodeBackToBackJump(TO_TADDR(address));
5898
5899 HRESULT hr = GetRuntimeNameByAddress(pTarget, flags, bufLen, symbolLen, symbolBuf, NULL);
5900 if (SUCCEEDED(hr))
5901 {
5902 return hr;
5903 }
5904
5905 PCSTR pHelperName = GetJitHelperName(pTarget);
5906 if (pHelperName != NULL)
5907 {
5908 hr = ConvertUtf8(pHelperName, bufLen, symbolLen, symbolBuf);
5909 if (FAILED(hr))
5910 return S_FALSE;
5911
5912 return hr;
5913 }
5914 }
5915
5916 static WCHAR s_wszFormatNameWithStubManager[] = W("CLRStub[%s]@%I64x");
5917
5918 LPCWSTR wszStubManagerName = pStubManager->GetStubManagerName(TO_TADDR(address));
5919 _ASSERTE(wszStubManagerName != NULL);
5920
5921 int result = _snwprintf_s(
5922 symbolBuf,
5923 bufLen,
5924 _TRUNCATE,
5925 s_wszFormatNameWithStubManager,
5926 wszStubManagerName, // Arg 1 = stub name
5927 TO_TADDR(address)); // Arg 2 = stub hex address
5928
5929 if (result != -1)
5930 {
5931 // Printf succeeded, so we have an exact char count to return
5932 if (symbolLen)
5933 {
5934 size_t cchSymbol = wcslen(symbolBuf) + 1;
5935 if (!FitsIn<ULONG32>(cchSymbol))
5936 return COR_E_OVERFLOW;
5937
5938 *symbolLen = (ULONG32) cchSymbol;
5939 }
5940 return S_OK;
5941 }
5942
5943 // Printf failed. Estimate a size that will be at least big enough to hold the name
5944 if (symbolLen)
5945 {
5946 size_t cchSymbol = COUNTOF(s_wszFormatNameWithStubManager) +
5947 wcslen(wszStubManagerName) +
5948 k_cch64BitHexFormat +
5949 1;
5950
5951 if (!FitsIn<ULONG32>(cchSymbol))
5952 return COR_E_OVERFLOW;
5953
5954 *symbolLen = (ULONG32) cchSymbol;
5955 }
5956 return S_FALSE;
5957 }
5958
5959 // Do not waste time looking up name for static helper. Debugger can get the actual name from .pdb.
5960 PCSTR pHelperName;
5961 pHelperName = GetJitHelperName(TO_TADDR(address), true /* dynamicHelpersOnly */);
5962 if (pHelperName != NULL)
5963 {
5964 if (displacement)
5965 {
5966 *displacement = 0;
5967 }
5968
5969 HRESULT hr = ConvertUtf8(pHelperName, bufLen, symbolLen, symbolBuf);
5970 if (FAILED(hr))
5971 return S_FALSE;
5972
5973 return S_OK;
5974 }
5975
5976 return E_NOINTERFACE;
5977
5978NameFromMethodDesc:
5979 if (methodDesc->GetClassification() == mcDynamic &&
5980 !methodDesc->GetSig())
5981 {
5982 // XXX Microsoft - Should this case have a more specific name?
5983 static WCHAR s_wszFormatNameAddressOnly[] = W("CLRStub@%I64x");
5984
5985 int result = _snwprintf_s(
5986 symbolBuf,
5987 bufLen,
5988 _TRUNCATE,
5989 s_wszFormatNameAddressOnly,
5990 TO_TADDR(address));
5991
5992 if (result != -1)
5993 {
5994 // Printf succeeded, so we have an exact char count to return
5995 if (symbolLen)
5996 {
5997 size_t cchSymbol = wcslen(symbolBuf) + 1;
5998 if (!FitsIn<ULONG32>(cchSymbol))
5999 return COR_E_OVERFLOW;
6000
6001 *symbolLen = (ULONG32) cchSymbol;
6002 }
6003 return S_OK;
6004 }
6005
6006 // Printf failed. Estimate a size that will be at least big enough to hold the name
6007 if (symbolLen)
6008 {
6009 size_t cchSymbol = COUNTOF(s_wszFormatNameAddressOnly) +
6010 k_cch64BitHexFormat +
6011 1;
6012
6013 if (!FitsIn<ULONG32>(cchSymbol))
6014 return COR_E_OVERFLOW;
6015
6016 *symbolLen = (ULONG32) cchSymbol;
6017 }
6018
6019 return S_FALSE;
6020 }
6021
6022 return GetFullMethodName(methodDesc, bufLen, symbolLen, symbolBuf);
6023}
6024
6025HRESULT
6026ClrDataAccess::GetMethodExtents(MethodDesc* methodDesc,
6027 METH_EXTENTS** extents)
6028{
6029 CLRDATA_ADDRESS_RANGE* curExtent;
6030
6031 {
6032 //
6033 // Get the information from the methoddesc.
6034 // We'll go through the CodeManager + JitManagers, so this should work
6035 // for all types of managed code.
6036 //
6037
6038 PCODE methodStart = methodDesc->GetNativeCode();
6039 if (!methodStart)
6040 {
6041 return E_NOINTERFACE;
6042 }
6043
6044 EECodeInfo codeInfo(methodStart);
6045 _ASSERTE(codeInfo.IsValid());
6046
6047 TADDR codeSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfoToken());
6048
6049 *extents = new (nothrow) METH_EXTENTS;
6050 if (!*extents)
6051 {
6052 return E_OUTOFMEMORY;
6053 }
6054
6055 (*extents)->numExtents = 1;
6056 curExtent = (*extents)->extents;
6057 curExtent->startAddress = TO_CDADDR(methodStart);
6058 curExtent->endAddress =
6059 curExtent->startAddress + codeSize;
6060 curExtent++;
6061 }
6062
6063 (*extents)->curExtent = 0;
6064
6065 return S_OK;
6066}
6067
6068// Allocator to pass to the debug-info-stores...
6069BYTE* DebugInfoStoreNew(void * pData, size_t cBytes)
6070{
6071 return new (nothrow) BYTE[cBytes];
6072}
6073
6074HRESULT
6075ClrDataAccess::GetMethodVarInfo(MethodDesc* methodDesc,
6076 TADDR address,
6077 ULONG32* numVarInfo,
6078 ICorDebugInfo::NativeVarInfo** varInfo,
6079 ULONG32* codeOffset)
6080{
6081 SUPPORTS_DAC;
6082 COUNT_T countNativeVarInfo;
6083 NewHolder<ICorDebugInfo::NativeVarInfo> nativeVars(NULL);
6084
6085 DebugInfoRequest request;
6086 TADDR nativeCodeStartAddr = PCODEToPINSTR(methodDesc->GetNativeCode());
6087 request.InitFromStartingAddr(methodDesc, nativeCodeStartAddr);
6088
6089 BOOL success = DebugInfoManager::GetBoundariesAndVars(
6090 request,
6091 DebugInfoStoreNew, NULL, // allocator
6092 NULL, NULL,
6093 &countNativeVarInfo, &nativeVars);
6094
6095
6096 if (!success)
6097 {
6098 return E_FAIL;
6099 }
6100
6101 if (!nativeVars || !countNativeVarInfo)
6102 {
6103 return E_NOINTERFACE;
6104 }
6105
6106 *numVarInfo = countNativeVarInfo;
6107 *varInfo = nativeVars;
6108 nativeVars.SuppressRelease(); // To prevent NewHolder from releasing the memory
6109
6110 if (codeOffset)
6111 {
6112 *codeOffset = (ULONG32)
6113 (address - nativeCodeStartAddr);
6114 }
6115 return S_OK;
6116}
6117
6118HRESULT
6119ClrDataAccess::GetMethodNativeMap(MethodDesc* methodDesc,
6120 TADDR address,
6121 ULONG32* numMap,
6122 DebuggerILToNativeMap** map,
6123 bool* mapAllocated,
6124 CLRDATA_ADDRESS* codeStart,
6125 ULONG32* codeOffset)
6126{
6127 _ASSERTE((codeOffset == NULL) || (address != NULL));
6128
6129 // Use the DebugInfoStore to get IL->Native maps.
6130 // It doesn't matter whether we're jitted, ngenned etc.
6131
6132 DebugInfoRequest request;
6133 TADDR nativeCodeStartAddr = PCODEToPINSTR(methodDesc->GetNativeCode());
6134 request.InitFromStartingAddr(methodDesc, nativeCodeStartAddr);
6135
6136
6137 // Bounds info.
6138 ULONG32 countMapCopy;
6139 NewHolder<ICorDebugInfo::OffsetMapping> mapCopy(NULL);
6140
6141 BOOL success = DebugInfoManager::GetBoundariesAndVars(
6142 request,
6143 DebugInfoStoreNew, NULL, // allocator
6144 &countMapCopy, &mapCopy,
6145 NULL, NULL);
6146
6147 if (!success)
6148 {
6149 return E_FAIL;
6150 }
6151
6152
6153 // Need to convert map formats.
6154 *numMap = countMapCopy;
6155
6156 *map = new (nothrow) DebuggerILToNativeMap[countMapCopy];
6157 if (!*map)
6158 {
6159 return E_OUTOFMEMORY;
6160 }
6161
6162 ULONG32 i;
6163 for (i = 0; i < *numMap; i++)
6164 {
6165 (*map)[i].ilOffset = mapCopy[i].ilOffset;
6166 (*map)[i].nativeStartOffset = mapCopy[i].nativeOffset;
6167 if (i > 0)
6168 {
6169 (*map)[i - 1].nativeEndOffset = (*map)[i].nativeStartOffset;
6170 }
6171 (*map)[i].source = mapCopy[i].source;
6172 }
6173 if (*numMap >= 1)
6174 {
6175 (*map)[i - 1].nativeEndOffset = 0;
6176 }
6177
6178
6179 // Update varion out params.
6180 if (codeStart)
6181 {
6182 *codeStart = TO_CDADDR(nativeCodeStartAddr);
6183 }
6184 if (codeOffset)
6185 {
6186 *codeOffset = (ULONG32)
6187 (address - nativeCodeStartAddr);
6188 }
6189
6190 *mapAllocated = true;
6191 return S_OK;
6192}
6193
6194// Get the MethodDesc for a function
6195// Arguments:
6196// Input:
6197// pModule - pointer to the module for the function
6198// memberRef - metadata token for the function
6199// Return Value:
6200// MethodDesc for the function
6201MethodDesc * ClrDataAccess::FindLoadedMethodRefOrDef(Module* pModule,
6202 mdToken memberRef)
6203{
6204 CONTRACT(MethodDesc *)
6205 {
6206 GC_NOTRIGGER;
6207 PRECONDITION(CheckPointer(pModule));
6208 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
6209 }
6210 CONTRACT_END;
6211
6212 // Must have a MemberRef or a MethodDef
6213 mdToken tkType = TypeFromToken(memberRef);
6214 _ASSERTE((tkType == mdtMemberRef) || (tkType == mdtMethodDef));
6215
6216 if (tkType == mdtMemberRef)
6217 {
6218 RETURN pModule->LookupMemberRefAsMethod(memberRef);
6219 }
6220
6221 RETURN pModule->LookupMethodDef(memberRef);
6222} // FindLoadedMethodRefOrDef
6223
6224//
6225// ReportMem - report a region of memory for dump gathering
6226//
6227// If you specify that you expect success, any failure will cause ReportMem to
6228// return false. If you do not expect success, true is always returned.
6229// This function only throws when all dump collection should be cancelled.
6230//
6231// Arguments:
6232// addr - the starting target address for the memory to report
6233// size - the length (in bytes) to report
6234// fExpectSuccess - if true (the default), then we expect that this region of memory
6235// should be fully readable. Any read errors indicate a corrupt target.
6236//
6237bool ClrDataAccess::ReportMem(TADDR addr, TSIZE_T size, bool fExpectSuccess /*= true*/)
6238{
6239 SUPPORTS_DAC_HOST_ONLY;
6240
6241 // This block of code is to help debugging blocks that we report
6242 // to minidump/heapdump. You can set break point here to view the static
6243 // variable to figure out the size of blocks that we are reporting.
6244 // Most useful is set conditional break point to catch large chuck of
6245 // memory. We will leave it here for all builds.
6246 //
6247 static TADDR debugAddr;
6248 static TSIZE_T debugSize;
6249 debugAddr = addr;
6250 debugSize = size;
6251
6252 HRESULT status;
6253 if (!addr || addr == (TADDR)-1 || !size)
6254 {
6255 if (fExpectSuccess)
6256 return false;
6257 else
6258 return true;
6259 }
6260
6261 //
6262 // Try and sanity-check the reported region of memory
6263 //
6264#ifdef _DEBUG
6265 // in debug builds, sanity-check all reports
6266 const TSIZE_T k_minSizeToCheck = 1;
6267#else
6268 // in retail builds, only sanity-check larger chunks which have the potential to waste a
6269 // lot of time and/or space. This avoids the overhead of checking for the majority of
6270 // memory regions (which are small).
6271 const TSIZE_T k_minSizeToCheck = 1024;
6272#endif
6273 if (size >= k_minSizeToCheck)
6274 {
6275 if (!IsFullyReadable(addr, size))
6276 {
6277 if (!fExpectSuccess)
6278 {
6279 // We know the read might fail (eg. we're trying to find mapped pages in
6280 // a module image), so just skip this block silently.
6281 // Note that the EnumMemoryRegion callback won't necessarily do anything if any part of
6282 // the region is unreadable, and so there is no point in calling it. For cases where we expect
6283 // the read might fail, but we want to report any partial blocks, we have to break up the region
6284 // into pages and try reporting each page anyway
6285 return true;
6286 }
6287
6288 // We're reporting bogus memory, so the target must be corrupt (or there is a issue). We should abort
6289 // reporting and continue with the next data structure (where the exception is caught),
6290 // just like we would for a DAC read error (otherwise we might do something stupid
6291 // like get into an infinite loop, or otherwise waste time with corrupt data).
6292
6293 TARGET_CONSISTENCY_CHECK(false, "Found unreadable memory while reporting memory regions for dump gathering");
6294 return false;
6295 }
6296 }
6297
6298 // Minidumps should never contain data structures that are anywhere near 4MB. If we see this, it's
6299 // probably due to memory corruption. To keep the dump small, we'll truncate the block. Note that
6300 // the size to which the block is truncated is pretty unique, so should be good evidence in a dump
6301 // that this has happened.
6302 // Note that it's hard to say what a good value would be here, or whether we should dump any of the
6303 // data structure at all. Hopefully experience will help guide this going forward.
6304 // @dbgtodo : Extend dump-gathering API to allow a dump-log to be included.
6305 const TSIZE_T kMaxMiniDumpRegion = 4*1024*1024 - 3; // 4MB-3
6306 if( size > kMaxMiniDumpRegion
6307 && (m_enumMemFlags == CLRDATA_ENUM_MEM_MINI
6308 || m_enumMemFlags == CLRDATA_ENUM_MEM_TRIAGE))
6309 {
6310 TARGET_CONSISTENCY_CHECK( false, "Dump target consistency failure - truncating minidump data structure");
6311 size = kMaxMiniDumpRegion;
6312 }
6313
6314 // track the total memory reported.
6315 m_cbMemoryReported += size;
6316
6317 // ICLRData APIs take only 32-bit sizes. In practice this will almost always be sufficient, but
6318 // in theory we might have some >4GB ranges on large 64-bit processes doing a heap dump
6319 // (for example, the code:LoaderHeap). If necessary, break up the reporting into maximum 4GB
6320 // chunks so we can use the existing API.
6321 // @dbgtodo : ICorDebugDataTarget should probably use 64-bit sizes
6322 while (size)
6323 {
6324 ULONG32 enumSize;
6325 if (size > ULONG_MAX)
6326 {
6327 enumSize = ULONG_MAX;
6328 }
6329 else
6330 {
6331 enumSize = (ULONG32)size;
6332 }
6333
6334 // Actually perform the memory reporting callback
6335 status = m_enumMemCb->EnumMemoryRegion(TO_CDADDR(addr), enumSize);
6336 if (status != S_OK)
6337 {
6338 // If dump generation was cancelled, allow us to throw upstack so we'll actually quit.
6339 if ((fExpectSuccess) && (status != COR_E_OPERATIONCANCELED))
6340 return false;
6341 }
6342
6343 // If the return value of EnumMemoryRegion is COR_E_OPERATIONCANCELED,
6344 // it means that user has requested that the minidump gathering be canceled.
6345 // To do this we throw an exception which is caught in EnumMemoryRegionsWrapper.
6346 if (status == COR_E_OPERATIONCANCELED)
6347 {
6348 ThrowHR(status);
6349 }
6350
6351 // Move onto the next chunk (if any)
6352 size -= enumSize;
6353 addr += enumSize;
6354 }
6355
6356 return true;
6357}
6358
6359
6360//
6361// DacUpdateMemoryRegion - updates/poisons a region of memory of generated dump
6362//
6363// Parameters:
6364// addr - target address of the beginning of the memory region
6365// bufferSize - number of bytes to update/poison
6366// buffer - data to be written at given target address
6367//
6368bool ClrDataAccess::DacUpdateMemoryRegion(TADDR addr, TSIZE_T bufferSize, BYTE* buffer)
6369{
6370 SUPPORTS_DAC_HOST_ONLY;
6371
6372 HRESULT status;
6373 if (!addr || addr == (TADDR)-1 || !bufferSize)
6374 {
6375 return false;
6376 }
6377
6378 // track the total memory reported.
6379 m_cbMemoryReported += bufferSize;
6380
6381 if (m_updateMemCb == NULL)
6382 {
6383 return false;
6384 }
6385
6386 // Actually perform the memory updating callback
6387 status = m_updateMemCb->UpdateMemoryRegion(TO_CDADDR(addr), (ULONG32)bufferSize, buffer);
6388 if (status != S_OK)
6389 {
6390 return false;
6391 }
6392
6393 return true;
6394}
6395
6396//
6397// Check whether a region of target memory is fully readable.
6398//
6399// Arguments:
6400// addr The base target address of the region
6401// size The size of the region to analyze
6402//
6403// Return value:
6404// True if the entire regions appears to be readable, false otherwise.
6405//
6406// Notes:
6407// The motivation here is that reporting large regions of unmapped address space to dbgeng can result in
6408// it taking a long time trying to identify a valid subrange. This can happen when the target
6409// memory is corrupt, and we enumerate a data structure with a dynamic size. Ideally we would just spec
6410// the ICLRDataEnumMemoryRegionsCallback API to require the client to fail if it detects an unmapped
6411// memory address in the region. However, we can't change the existing dbgeng code, so for now we'll
6412// rely on this heuristic here.
6413// @dbgtodo : Try and get the dbg team to change their EnumMemoryRegion behavior. See DevDiv Bugs 6265
6414//
6415bool ClrDataAccess::IsFullyReadable(TADDR taBase, TSIZE_T dwSize)
6416{
6417 // The only way we have to verify that a memory region is readable is to try reading it in it's
6418 // entirety. This is potentially expensive, so we'll rely on a heuristic that spot-checks various
6419 // points in the region.
6420
6421 // Ensure we've got something to check
6422 if( dwSize == 0 )
6423 return true;
6424
6425 // Check for overflow
6426 TADDR taEnd = DacTAddrOffset(taBase, dwSize, 1);
6427
6428 // Loop through using expontential growth, being sure to check both the first and last byte
6429 TADDR taCurr = taBase;
6430 TSIZE_T dwInc = 4096;
6431 bool bDone = false;
6432 while (!bDone)
6433 {
6434 // Try and read a byte from the target. Note that we don't use PTR_BYTE here because we don't want
6435 // the overhead of inserting entries into the DAC instance cache.
6436 BYTE b;
6437 ULONG32 dwBytesRead;
6438 HRESULT hr = m_pTarget->ReadVirtual(taCurr, &b, 1, &dwBytesRead);
6439 if( hr != S_OK || dwBytesRead < 1 )
6440 {
6441 return false;
6442 }
6443
6444 if (taEnd - taCurr <= 1)
6445 {
6446 // We just read the last byte so we're done
6447 _ASSERTE( taCurr = taEnd - 1 );
6448 bDone = true;
6449 }
6450 else if (dwInc == 0 || dwInc >= taEnd - taCurr)
6451 {
6452 // we've reached the end of the exponential series, check the last byte
6453 taCurr = taEnd - 1;
6454 }
6455 else
6456 {
6457 // advance current pointer (subtraction above ensures this won't overflow)
6458 taCurr += dwInc;
6459
6460 // double the increment for next time (or set to 0 if it's already the max)
6461 dwInc <<= 1;
6462 }
6463 }
6464 return true;
6465}
6466
6467JITNotification*
6468ClrDataAccess::GetHostJitNotificationTable()
6469{
6470 if (m_jitNotificationTable == NULL)
6471 {
6472 m_jitNotificationTable =
6473 JITNotifications::InitializeNotificationTable(1000);
6474 }
6475
6476 return m_jitNotificationTable;
6477}
6478
6479GcNotification*
6480ClrDataAccess::GetHostGcNotificationTable()
6481{
6482 if (m_gcNotificationTable == NULL)
6483 {
6484 m_gcNotificationTable =
6485 GcNotifications::InitializeNotificationTable(128);
6486 }
6487
6488 return m_gcNotificationTable;
6489}
6490
6491/* static */ bool
6492ClrDataAccess::GetMetaDataFileInfoFromPEFile(PEFile *pPEFile,
6493 DWORD &dwTimeStamp,
6494 DWORD &dwSize,
6495 DWORD &dwDataSize,
6496 DWORD &dwRvaHint,
6497 bool &isNGEN,
6498 __out_ecount(cchFilePath) LPWSTR wszFilePath,
6499 const DWORD cchFilePath)
6500{
6501 SUPPORTS_DAC_HOST_ONLY;
6502 PEImage *mdImage = NULL;
6503 PEImageLayout *layout;
6504 IMAGE_DATA_DIRECTORY *pDir = NULL;
6505 COUNT_T uniPathChars = 0;
6506
6507 isNGEN = false;
6508
6509 if (pPEFile->HasNativeImage())
6510 {
6511 mdImage = pPEFile->GetNativeImage();
6512 _ASSERTE(mdImage != NULL);
6513 layout = mdImage->GetLoadedLayout();
6514 pDir = &(layout->GetCorHeader()->MetaData);
6515 // For ngen image, the IL metadata is stored for private use. So we need to pass
6516 // the RVA hint to find it to debuggers.
6517 //
6518 if (pDir->Size != 0)
6519 {
6520 isNGEN = true;
6521 dwRvaHint = pDir->VirtualAddress;
6522 dwDataSize = pDir->Size;
6523 }
6524
6525 }
6526 if (pDir == NULL || pDir->Size == 0)
6527 {
6528 mdImage = pPEFile->GetILimage();
6529 if (mdImage != NULL)
6530 {
6531 layout = mdImage->GetLoadedLayout();
6532 pDir = &layout->GetCorHeader()->MetaData;
6533
6534 // In IL image case, we do not have any hint to IL metadata since it is stored
6535 // in the corheader.
6536 //
6537 dwRvaHint = 0;
6538 dwDataSize = pDir->Size;
6539 }
6540 else
6541 {
6542 return false;
6543 }
6544 }
6545
6546 // Do not fail if path can not be read. Triage dumps don't have paths and we want to fallback
6547 // on searching metadata from IL image.
6548 mdImage->GetPath().DacGetUnicode(cchFilePath, wszFilePath, &uniPathChars);
6549
6550 if (!mdImage->HasNTHeaders() ||
6551 !mdImage->HasCorHeader() ||
6552 !mdImage->HasLoadedLayout() ||
6553 (uniPathChars > cchFilePath))
6554 {
6555 return false;
6556 }
6557
6558 // It is possible that the module is in-memory. That is the wszFilePath here is empty.
6559 // We will try to use the module name instead in this case for hosting debugger
6560 // to find match.
6561 if (wcslen(wszFilePath) == 0)
6562 {
6563 mdImage->GetModuleFileNameHintForDAC().DacGetUnicode(cchFilePath, wszFilePath, &uniPathChars);
6564 if (uniPathChars > cchFilePath)
6565 {
6566 return false;
6567 }
6568 }
6569
6570 dwTimeStamp = layout->GetTimeDateStamp();
6571 dwSize = (ULONG32)layout->GetVirtualSize();
6572
6573 return true;
6574}
6575
6576/* static */
6577bool ClrDataAccess::GetILImageInfoFromNgenPEFile(PEFile *peFile,
6578 DWORD &dwTimeStamp,
6579 DWORD &dwSize,
6580 __out_ecount(cchFilePath) LPWSTR wszFilePath,
6581 const DWORD cchFilePath)
6582{
6583 SUPPORTS_DAC_HOST_ONLY;
6584 DWORD dwWritten = 0;
6585
6586 // use the IL File name
6587 if (!peFile->GetPath().DacGetUnicode(cchFilePath, wszFilePath, (COUNT_T *)(&dwWritten)))
6588 {
6589 // Use DAC hint to retrieve the IL name.
6590 peFile->GetModuleFileNameHint().DacGetUnicode(cchFilePath, wszFilePath, (COUNT_T *)(&dwWritten));
6591 }
6592#ifdef FEATURE_PREJIT
6593 // Need to get IL image information from cached info in the ngen image.
6594 dwTimeStamp = peFile->GetLoaded()->GetNativeVersionInfo()->sourceAssembly.timeStamp;
6595 dwSize = peFile->GetLoaded()->GetNativeVersionInfo()->sourceAssembly.ilImageSize;
6596#else
6597 dwTimeStamp = 0;
6598 dwSize = 0;
6599#endif // FEATURE_PREJIT
6600
6601 return true;
6602}
6603
6604#if defined(FEATURE_CORESYSTEM)
6605/* static */
6606// We extract "ni.dll or .ni.winmd" from the NGEM image name to obtain the IL image name.
6607// In the end we add given ilExtension.
6608// This dependecy is based on Apollo installer behavior.
6609bool ClrDataAccess::GetILImageNameFromNgenImage( LPCWSTR ilExtension,
6610 __out_ecount(cchFilePath) LPWSTR wszFilePath,
6611 const DWORD cchFilePath)
6612{
6613 if (wszFilePath == NULL || cchFilePath == 0)
6614 {
6615 return false;
6616 }
6617
6618 _wcslwr_s(wszFilePath, cchFilePath);
6619 // Find the "ni.dll" or "ni.winmd" extension (check for PEFile isWinRT something to know when is winmd or not.
6620 // If none exists use NGEN image name.
6621 //
6622 const WCHAR* ngenExtension[] = {W("ni.dll"), W("ni.winmd")};
6623
6624 for (unsigned i = 0; i < COUNTOF(ngenExtension); ++i)
6625 {
6626 if (wcslen(ilExtension) > wcslen(ngenExtension[i]))
6627 {
6628 // We should not have IL image name bigger than NGEN image.
6629 // It will not fit inside wszFilePath.
6630 continue;
6631 }
6632 LPWSTR wszFileExtension = wcsstr(wszFilePath, ngenExtension[i]);
6633 if (wszFileExtension != 0)
6634 {
6635 LPWSTR wszNextFileExtension = wszFileExtension;
6636 // Find last occurence
6637 do
6638 {
6639 wszFileExtension = wszNextFileExtension;
6640 wszNextFileExtension = wcsstr(wszFileExtension + 1, ngenExtension[i]);
6641 } while (wszNextFileExtension != 0);
6642
6643 // Overwrite ni.dll or ni.winmd with ilExtension(.dll, .winmd)
6644 if (!memcpy_s(wszFileExtension,
6645 wcslen(ngenExtension[i])*sizeof(WCHAR),
6646 ilExtension,
6647 wcslen(ilExtension)*sizeof(WCHAR)))
6648 {
6649 wszFileExtension[wcslen(ilExtension)] = '\0';
6650 return true;
6651 }
6652 }
6653 }
6654
6655 //Use ngen filename if there is no ".ni"
6656 if (wcsstr(wszFilePath, W(".ni")) == 0)
6657 {
6658 return true;
6659 }
6660
6661 return false;
6662}
6663#endif // FEATURE_CORESYSTEM
6664
6665void *
6666ClrDataAccess::GetMetaDataFromHost(PEFile* peFile,
6667 bool* isAlternate)
6668{
6669 DWORD imageTimestamp, imageSize, dataSize;
6670 void* buffer = NULL;
6671 WCHAR uniPath[MAX_LONGPATH] = {0};
6672 bool isAlt = false;
6673 bool isNGEN = false;
6674 DAC_INSTANCE* inst = NULL;
6675 HRESULT hr = S_OK;
6676 DWORD ulRvaHint;
6677 //
6678 // We always ask for the IL image metadata,
6679 // as we expect that to be more
6680 // available than others. The drawback is that
6681 // there may be differences between the IL image
6682 // metadata and native image metadata, so we
6683 // have to mark such alternate metadata so that
6684 // we can fail unsupported usage of it.
6685 //
6686
6687 // Microsoft - above comment seems to be an unimplemented thing.
6688 // The DAC_MD_IMPORT.isAlternate field gets ultimately set, but
6689 // on the searching I did, I cannot find any usage of it
6690 // other than in the ctor. Should we be doing something, or should
6691 // we remove this comment and the isAlternate field?
6692 // It's possible that test will want us to track whether we have
6693 // an IL image's metadata loaded against an NGEN'ed image
6694 // so the field remains for now.
6695
6696 if (!ClrDataAccess::GetMetaDataFileInfoFromPEFile(
6697 peFile,
6698 imageTimestamp,
6699 imageSize,
6700 dataSize,
6701 ulRvaHint,
6702 isNGEN,
6703 uniPath,
6704 NumItems(uniPath)))
6705 {
6706 return NULL;
6707 }
6708
6709 // try direct match for the image that is loaded into the managed process
6710 peFile->GetLoadedMetadata((COUNT_T *)(&dataSize));
6711
6712 DWORD allocSize = 0;
6713 if (!ClrSafeInt<DWORD>::addition(dataSize, sizeof(DAC_INSTANCE), allocSize))
6714 {
6715 DacError(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW));
6716 }
6717
6718 inst = m_instances.Alloc(0, allocSize, DAC_DPTR);
6719 if (!inst)
6720 {
6721 DacError(E_OUTOFMEMORY);
6722 return NULL;
6723 }
6724
6725 buffer = (void*)(inst + 1);
6726
6727 // APIs implemented by hosting debugger. It can use the path/filename, timestamp, and
6728 // file size to find an exact match for the image. If that fails for an ngen'ed image,
6729 // we can request the IL image which it came from.
6730 if (m_legacyMetaDataLocator)
6731 {
6732 // Legacy API implemented by hosting debugger.
6733 hr = m_legacyMetaDataLocator->GetMetadata(
6734 uniPath,
6735 imageTimestamp,
6736 imageSize,
6737 NULL, // MVID - not used yet
6738 ulRvaHint,
6739 0, // flags - reserved for future.
6740 dataSize,
6741 (BYTE*)buffer,
6742 NULL);
6743 }
6744 else
6745 {
6746 hr = m_target3->GetMetaData(
6747 uniPath,
6748 imageTimestamp,
6749 imageSize,
6750 NULL, // MVID - not used yet
6751 ulRvaHint,
6752 0, // flags - reserved for future.
6753 dataSize,
6754 (BYTE*)buffer,
6755 NULL);
6756 }
6757 if (FAILED(hr) && isNGEN)
6758 {
6759 // We failed to locate the ngen'ed image. We should try to
6760 // find the matching IL image
6761 //
6762 isAlt = true;
6763 if (!ClrDataAccess::GetILImageInfoFromNgenPEFile(
6764 peFile,
6765 imageTimestamp,
6766 imageSize,
6767 uniPath,
6768 NumItems(uniPath)))
6769 {
6770 goto ErrExit;
6771 }
6772
6773#if defined(FEATURE_CORESYSTEM)
6774 const WCHAR* ilExtension[] = {W("dll"), W("winmd")};
6775 WCHAR ngenImageName[MAX_LONGPATH] = {0};
6776 if (wcscpy_s(ngenImageName, NumItems(ngenImageName), uniPath) != 0)
6777 {
6778 goto ErrExit;
6779 }
6780 for (unsigned i = 0; i < COUNTOF(ilExtension); i++)
6781 {
6782 if (wcscpy_s(uniPath, NumItems(uniPath), ngenImageName) != 0)
6783 {
6784 goto ErrExit;
6785 }
6786 // Transform NGEN image name into IL Image name
6787 if (!GetILImageNameFromNgenImage(ilExtension[i], uniPath, NumItems(uniPath)))
6788 {
6789 goto ErrExit;
6790 }
6791#endif//FEATURE_CORESYSTEM
6792
6793 // RVA size in ngen image and IL image is the same. Because the only
6794 // different is in RVA. That is 4 bytes column fixed.
6795 //
6796
6797 // try again
6798 if (m_legacyMetaDataLocator)
6799 {
6800 hr = m_legacyMetaDataLocator->GetMetadata(
6801 uniPath,
6802 imageTimestamp,
6803 imageSize,
6804 NULL, // MVID - not used yet
6805 0, // pass zero hint here... important
6806 0, // flags - reserved for future.
6807 dataSize,
6808 (BYTE*)buffer,
6809 NULL);
6810 }
6811 else
6812 {
6813 hr = m_target3->GetMetaData(
6814 uniPath,
6815 imageTimestamp,
6816 imageSize,
6817 NULL, // MVID - not used yet
6818 0, // pass zero hint here... important
6819 0, // flags - reserved for future.
6820 dataSize,
6821 (BYTE*)buffer,
6822 NULL);
6823 }
6824#if defined(FEATURE_CORESYSTEM)
6825 if (SUCCEEDED(hr))
6826 {
6827 break;
6828 }
6829 }
6830#endif // FEATURE_CORESYSTEM
6831 }
6832
6833 if (FAILED(hr))
6834 {
6835 goto ErrExit;
6836 }
6837
6838 *isAlternate = isAlt;
6839 m_instances.AddSuperseded(inst);
6840 return buffer;
6841
6842ErrExit:
6843 if (inst != NULL)
6844 {
6845 m_instances.ReturnAlloc(inst);
6846 }
6847 return NULL;
6848}
6849
6850
6851//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6852//
6853// Given a PEFile or a ReflectionModule try to find the corresponding metadata
6854// We will first ask debugger to locate it. If fail, we will try
6855// to get it from the target process
6856//
6857//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6858IMDInternalImport*
6859ClrDataAccess::GetMDImport(const PEFile* peFile, const ReflectionModule* reflectionModule, bool throwEx)
6860{
6861 HRESULT status;
6862 PTR_CVOID mdBaseTarget = NULL;
6863 COUNT_T mdSize;
6864 IMDInternalImport* mdImport = NULL;
6865 PVOID mdBaseHost = NULL;
6866 bool isAlternate = false;
6867
6868 _ASSERTE(peFile == NULL && reflectionModule != NULL || peFile != NULL && reflectionModule == NULL);
6869 TADDR peFileAddr = (peFile != NULL) ? dac_cast<TADDR>(peFile) : dac_cast<TADDR>(reflectionModule);
6870
6871 //
6872 // Look for one we've already created.
6873 //
6874 mdImport = m_mdImports.Get(peFileAddr);
6875 if (mdImport != NULL)
6876 {
6877 return mdImport;
6878 }
6879
6880 if (peFile != NULL)
6881 {
6882 // Get the metadata size
6883 mdBaseTarget = ((PEFile*)peFile)->GetLoadedMetadata(&mdSize);
6884 }
6885 else if (reflectionModule != NULL)
6886 {
6887 // Get the metadata
6888 PTR_SBuffer metadataBuffer = reflectionModule->GetDynamicMetadataBuffer();
6889 if (metadataBuffer != PTR_NULL)
6890 {
6891 mdBaseTarget = dac_cast<PTR_CVOID>((metadataBuffer->DacGetRawBuffer()).StartAddress());
6892 mdSize = metadataBuffer->GetSize();
6893 }
6894 else
6895 {
6896 if (throwEx)
6897 {
6898 DacError(E_FAIL);
6899 }
6900 return NULL;
6901 }
6902 }
6903 else
6904 {
6905 if (throwEx)
6906 {
6907 DacError(E_FAIL);
6908 }
6909 return NULL;
6910 }
6911
6912 if (mdBaseTarget == PTR_NULL)
6913 {
6914 mdBaseHost = NULL;
6915 }
6916 else
6917 {
6918
6919 //
6920 // Maybe the target process has the metadata
6921 // Find out where the metadata for the image is
6922 // in the target's memory.
6923 //
6924 //
6925 // Read the metadata into the host process. Make sure pass in false in the last
6926 // parameter. This is only matters when producing skinny mini-dump. This will
6927 // prevent metadata gets reported into mini-dump.
6928 //
6929 mdBaseHost = DacInstantiateTypeByAddressNoReport(dac_cast<TADDR>(mdBaseTarget), mdSize,
6930 false);
6931 }
6932
6933 // Try to see if debugger can locate it
6934 if (peFile != NULL && mdBaseHost == NULL && (m_target3 || m_legacyMetaDataLocator))
6935 {
6936 // We couldn't read the metadata from memory. Ask
6937 // the target for metadata as it may be able to
6938 // provide it from some alternate means.
6939 mdBaseHost = GetMetaDataFromHost(const_cast<PEFile *>(peFile), &isAlternate);
6940 }
6941
6942 if (mdBaseHost == NULL)
6943 {
6944 // cannot locate metadata anywhere
6945 if (throwEx)
6946 {
6947 DacError(E_INVALIDARG);
6948 }
6949 return NULL;
6950 }
6951
6952 //
6953 // Open the MD interface on the host copy of the metadata.
6954 //
6955
6956 status = GetMDInternalInterface(mdBaseHost, mdSize, ofRead,
6957 IID_IMDInternalImport,
6958 (void**)&mdImport);
6959 if (status != S_OK)
6960 {
6961 if (throwEx)
6962 {
6963 DacError(status);
6964 }
6965 return NULL;
6966 }
6967
6968 //
6969 // Remember the object for this module for
6970 // possible later use.
6971 // The m_mdImports list does get cleaned up by calls to ClrDataAccess::Flush,
6972 // i.e. every time the process changes state.
6973
6974 if (m_mdImports.Add(peFileAddr, mdImport, isAlternate) == NULL)
6975 {
6976 mdImport->Release();
6977 DacError(E_OUTOFMEMORY);
6978 }
6979
6980 return mdImport;
6981}
6982
6983
6984//
6985// Set whether inconsistencies in the target should raise asserts.
6986// This overrides the default initial setting.
6987//
6988// Arguments:
6989// fEnableAsserts - whether ASSERTs in dacized code should be enabled
6990//
6991
6992void ClrDataAccess::SetTargetConsistencyChecks(bool fEnableAsserts)
6993{
6994 LIMITED_METHOD_DAC_CONTRACT;
6995 m_fEnableTargetConsistencyAsserts = fEnableAsserts;
6996}
6997
6998//
6999// Get whether inconsistencies in the target should raise asserts.
7000//
7001// Return value:
7002// whether ASSERTs in dacized code should be enabled
7003//
7004// Notes:
7005// The implementation of ASSERT accesses this via code:DacTargetConsistencyAssertsEnabled
7006//
7007// By default, this is disabled, unless COMPlus_DbgDACEnableAssert is set (see code:ClrDataAccess::ClrDataAccess).
7008// This is necessary for compatibility. For example, SOS expects to be able to scan for
7009// valid MethodTables etc. (which may cause ASSERTs), and also doesn't want ASSERTs when working
7010// with targets with corrupted memory.
7011//
7012// Calling code:ClrDataAccess::SetTargetConsistencyChecks overrides the default setting.
7013//
7014bool ClrDataAccess::TargetConsistencyAssertsEnabled()
7015{
7016 LIMITED_METHOD_DAC_CONTRACT;
7017 return m_fEnableTargetConsistencyAsserts;
7018}
7019
7020#ifdef FEATURE_CORESYSTEM
7021#define ctime_s _ctime32_s
7022#define time_t __time32_t
7023#endif
7024
7025//
7026// VerifyDlls - Validate that the mscorwks in the target matches this version of mscordacwks
7027// Only done on Windows and Mac builds at the moment.
7028// See code:CordbProcess::CordbProcess#DBIVersionChecking for more information regarding version checking.
7029//
7030HRESULT ClrDataAccess::VerifyDlls()
7031{
7032#ifndef FEATURE_PAL
7033 // Provide a knob for disabling this check if we really want to try and proceed anyway with a
7034 // DAC mismatch. DAC behavior may be arbitrarily bad - globals probably won't be at the same
7035 // address, data structures may be laid out differently, etc.
7036 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgDACSkipVerifyDlls))
7037 {
7038 return S_OK;
7039 }
7040
7041 // Read the debug directory timestamp from the target mscorwks image using DAC
7042 // Note that we don't use the PE timestamp because the PE file might be changed in ways
7043 // that don't effect the PDB (and therefore don't effect DAC). Specifically, we rebase
7044 // our DLLs at the end of a build, that changes the PE file, but not the PDB.
7045 // Note that if we wanted to be extra careful, we could read the CV contents (which includes
7046 // the GUID signature) and verify it matches. Using the timestamp is useful for helpful error
7047 // messages, and should be sufficient in any real scenario.
7048 DWORD timestamp = 0;
7049 HRESULT hr = S_OK;
7050 DAC_ENTER();
7051 EX_TRY
7052 {
7053 // Note that we don't need to worry about ensuring the image memory read by this code
7054 // is saved in a minidump. Managed minidump debugging already requires that you have
7055 // the full mscorwks.dll available at debug time (eg. windbg won't even load DAC without it).
7056 PEDecoder pedecoder(dac_cast<PTR_VOID>(m_globalBase));
7057
7058 // We use the first codeview debug directory entry since this should always refer to the single
7059 // PDB for mscorwks.dll.
7060 const UINT k_maxDebugEntries = 32; // a reasonable upper limit in case of corruption
7061 for( UINT i = 0; i < k_maxDebugEntries; i++)
7062 {
7063 PTR_IMAGE_DEBUG_DIRECTORY pDebugEntry = pedecoder.GetDebugDirectoryEntry(i);
7064
7065 // If there are no more entries, then stop
7066 if (pDebugEntry == NULL)
7067 break;
7068
7069 // Ignore non-codeview entries. Some scenarios (eg. optimized builds), there may be extra
7070 // debug directory entries at the end of some other type.
7071 if (pDebugEntry->Type == IMAGE_DEBUG_TYPE_CODEVIEW)
7072 {
7073 // Found a codeview entry - use it's timestamp for comparison
7074 timestamp = pDebugEntry->TimeDateStamp;
7075 break;
7076 }
7077 }
7078 char szMsgBuf[1024];
7079 _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE,
7080 "Failed to find any valid codeview debug directory entry in %s image",
7081 MAIN_CLR_MODULE_NAME_A);
7082 _ASSERTE_MSG(timestamp != 0, szMsgBuf);
7083 }
7084 EX_CATCH
7085 {
7086 if (!DacExceptionFilter(GET_EXCEPTION(), this, &hr))
7087 {
7088 EX_RETHROW;
7089 }
7090 }
7091 EX_END_CATCH(SwallowAllExceptions)
7092 DAC_LEAVE();
7093 if (FAILED(hr))
7094 {
7095 return hr;
7096 }
7097
7098 // Validate that we got a timestamp and it matches what the DAC table told us to expect
7099 if (timestamp == 0 || timestamp != g_dacTableInfo.dwID0)
7100 {
7101 // Timestamp mismatch. This means mscordacwks is being used with a version of
7102 // mscorwks other than the one it was built for. This will not work reliably.
7103
7104#ifdef _DEBUG
7105 // Check if verbose asserts are enabled. The default is up to the specific instantiation of
7106 // ClrDataAccess, but can be overridden (in either direction) by a COMPlus_ knob.
7107 // Note that we check this knob every time because it may be handy to turn it on in
7108 // the environment mid-flight.
7109 DWORD dwAssertDefault = m_fEnableDllVerificationAsserts ? 1 : 0;
7110 if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgDACAssertOnMismatch, dwAssertDefault))
7111 {
7112 // Output a nice error message that contains the timestamps in string format.
7113 time_t actualTime = timestamp;
7114 char szActualTime[30];
7115 ctime_s(szActualTime, sizeof(szActualTime), &actualTime);
7116
7117 time_t expectedTime = g_dacTableInfo.dwID0;
7118 char szExpectedTime[30];
7119 ctime_s(szExpectedTime, sizeof(szExpectedTime), &expectedTime);
7120
7121 // Create a nice detailed message for the assert dialog.
7122 // Note that the strings returned by ctime_s have terminating newline characters.
7123 // This is technically a TARGET_CONSISTENCY_CHECK because a corrupt target could,
7124 // in-theory, have a corrupt mscrowks PE header and cause this check to fail
7125 // unnecessarily. However, this check occurs during startup, before we know
7126 // whether target consistency checks should be enabled, so it's always enabled
7127 // at the moment.
7128
7129 char szMsgBuf[1024];
7130 _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE,
7131 "DAC fatal error: %s/mscordacwks.dll version mismatch\n\n"\
7132 "The debug directory timestamp of the loaded %s does not match the\n"\
7133 "version mscordacwks.dll was built for.\n"\
7134 "Expected %s timestamp: %s"\
7135 "Actual %s timestamp: %s\n"\
7136 "DAC will now fail to initialize with a CORDBG_E_MISMATCHED_CORWKS_AND_DACWKS_DLLS\n"\
7137 "error. If you really want to try and use the mimatched DLLs, you can disable this\n"\
7138 "check by setting COMPlus_DbgDACSkipVerifyDlls=1. However, using a mismatched DAC\n"\
7139 "DLL will usually result in arbitrary debugger failures.\n",
7140 MAIN_CLR_DLL_NAME_A,
7141 MAIN_CLR_DLL_NAME_A,
7142 MAIN_CLR_DLL_NAME_A,
7143 szExpectedTime,
7144 MAIN_CLR_DLL_NAME_A,
7145 szActualTime);
7146 _ASSERTE_MSG(false, szMsgBuf);
7147 }
7148#endif
7149
7150 // Return a specific hresult indicating this problem
7151 return CORDBG_E_MISMATCHED_CORWKS_AND_DACWKS_DLLS;
7152 }
7153#endif // FEATURE_PAL
7154
7155 return S_OK;
7156}
7157
7158#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
7159
7160void ClrDataAccess::InitStreamsForWriting(IN CLRDataEnumMemoryFlags flags)
7161{
7162 // enforce this should only be called when generating triage and mini-dumps
7163 if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE)
7164 return;
7165
7166 EX_TRY
7167 {
7168 if (m_streams == NULL)
7169 m_streams = new DacStreamManager(g_MiniMetaDataBuffAddress, g_MiniMetaDataBuffMaxSize);
7170
7171 if (!m_streams->PrepareStreamsForWriting())
7172 {
7173 delete m_streams;
7174 m_streams = NULL;
7175 }
7176 }
7177 EX_CATCH
7178 {
7179 if (m_streams != NULL)
7180 {
7181 delete m_streams;
7182 m_streams = NULL;
7183 }
7184 }
7185 EX_END_CATCH(SwallowAllExceptions)
7186}
7187
7188bool ClrDataAccess::MdCacheAddEEName(TADDR taEEStruct, const SString& name)
7189{
7190 bool result = false;
7191 EX_TRY
7192 {
7193 if (m_streams != NULL)
7194 result = m_streams->MdCacheAddEEName(taEEStruct, name);
7195 }
7196 EX_CATCH
7197 {
7198 result = false;
7199 }
7200 EX_END_CATCH(SwallowAllExceptions)
7201
7202 return result;
7203}
7204
7205void ClrDataAccess::EnumStreams(IN CLRDataEnumMemoryFlags flags)
7206{
7207 // enforce this should only be called when generating triage and mini-dumps
7208 if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE)
7209 return;
7210
7211 EX_TRY
7212 {
7213 if (m_streams != NULL)
7214 m_streams->EnumStreams(flags);
7215 }
7216 EX_CATCH
7217 {
7218 }
7219 EX_END_CATCH(SwallowAllExceptions)
7220}
7221
7222bool ClrDataAccess::MdCacheGetEEName(TADDR taEEStruct, SString & eeName)
7223{
7224 bool result = false;
7225 EX_TRY
7226 {
7227 if (m_streams == NULL)
7228 m_streams = new DacStreamManager(g_MiniMetaDataBuffAddress, g_MiniMetaDataBuffMaxSize);
7229
7230 result = m_streams->MdCacheGetEEName(taEEStruct, eeName);
7231 }
7232 EX_CATCH
7233 {
7234 result = false;
7235 }
7236 EX_END_CATCH(SwallowAllExceptions)
7237
7238 return result;
7239}
7240
7241#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
7242
7243// Needed for RT_RCDATA.
7244#define MAKEINTRESOURCE(v) MAKEINTRESOURCEW(v)
7245
7246// this funny looking double macro forces x to be macro expanded before L is prepended
7247#define _WIDE(x) _WIDE2(x)
7248#define _WIDE2(x) W(x)
7249
7250HRESULT
7251ClrDataAccess::GetDacGlobals()
7252{
7253#ifdef FEATURE_PAL
7254#ifdef DAC_TABLE_SIZE
7255 if (DAC_TABLE_SIZE != sizeof(g_dacGlobals))
7256 {
7257 return E_INVALIDARG;
7258 }
7259#endif
7260 ULONG64 dacTableAddress = m_globalBase + DAC_TABLE_RVA;
7261 if (FAILED(ReadFromDataTarget(m_pTarget, dacTableAddress, (BYTE*)&g_dacGlobals, sizeof(g_dacGlobals))))
7262 {
7263 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7264 }
7265 if (g_dacGlobals.ThreadStore__s_pThreadStore == NULL)
7266 {
7267 return CORDBG_E_UNSUPPORTED;
7268 }
7269 return S_OK;
7270#else
7271 HRESULT status = E_FAIL;
7272 DWORD rsrcRVA = 0;
7273 LPVOID rsrcData = NULL;
7274 DWORD rsrcSize = 0;
7275
7276 DWORD resourceSectionRVA = 0;
7277
7278 if (FAILED(status = GetMachineAndResourceSectionRVA(m_pTarget, m_globalBase, NULL, &resourceSectionRVA)))
7279 {
7280 _ASSERTE_MSG(false, "DAC fatal error: can't locate resource section in " MAIN_CLR_DLL_NAME_A);
7281 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7282 }
7283
7284 if (FAILED(status = GetResourceRvaFromResourceSectionRvaByName(m_pTarget, m_globalBase,
7285 resourceSectionRVA, (DWORD)RT_RCDATA, _WIDE(DACCESS_TABLE_RESOURCE), 0,
7286 &rsrcRVA, &rsrcSize)))
7287 {
7288 _ASSERTE_MSG(false, "DAC fatal error: can't locate DAC table resource in " MAIN_CLR_DLL_NAME_A);
7289 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7290 }
7291
7292 rsrcData = new (nothrow) BYTE[rsrcSize];
7293 if (rsrcData == NULL)
7294 return E_OUTOFMEMORY;
7295
7296 if (FAILED(status = ReadFromDataTarget(m_pTarget, m_globalBase + rsrcRVA, (BYTE*)rsrcData, rsrcSize)))
7297 {
7298 _ASSERTE_MSG(false, "DAC fatal error: can't load DAC table resource from " MAIN_CLR_DLL_NAME_A);
7299 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
7300 }
7301
7302
7303 PBYTE rawData = (PBYTE)rsrcData;
7304 DWORD bytesLeft = rsrcSize;
7305
7306 // Read the header
7307 struct DacTableHeader header;
7308
7309 // We currently expect the header to be 2 32-bit values and 1 16-byte value,
7310 // make sure there is no packing going on or anything.
7311 static_assert_no_msg(sizeof(DacTableHeader) == 2 * 4 + 16);
7312
7313 if (bytesLeft < sizeof(DacTableHeader))
7314 {
7315 _ASSERTE_MSG(false, "DAC fatal error: DAC table too small for header.");
7316 goto Exit;
7317 }
7318 memcpy(&header, rawData, sizeof(DacTableHeader));
7319 rawData += sizeof(DacTableHeader);
7320 bytesLeft -= sizeof(DacTableHeader);
7321
7322 // Save the table info for later use
7323 g_dacTableInfo = header.info;
7324
7325 // Sanity check that the DAC table is the size we expect.
7326 // This could fail if a different version of dacvars.h or vptr_list.h was used when building
7327 // mscordacwks.dll than when running DacTableGen.
7328
7329 if (offsetof(DacGlobals, Thread__vtAddr) != header.numGlobals * sizeof(ULONG))
7330 {
7331#ifdef _DEBUG
7332 char szMsgBuf[1024];
7333 _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE,
7334 "DAC fatal error: mismatch in number of globals in DAC table. Read from file: %d, expected: %d.",
7335 header.numGlobals,
7336 offsetof(DacGlobals, Thread__vtAddr) / sizeof(ULONG));
7337 _ASSERTE_MSG(false, szMsgBuf);
7338#endif // _DEBUG
7339
7340 status = E_INVALIDARG;
7341 goto Exit;
7342 }
7343
7344 if (sizeof(DacGlobals) != (header.numGlobals + header.numVptrs) * sizeof(ULONG))
7345 {
7346#ifdef _DEBUG
7347 char szMsgBuf[1024];
7348 _snprintf_s(szMsgBuf, sizeof(szMsgBuf), _TRUNCATE,
7349 "DAC fatal error: mismatch in number of vptrs in DAC table. Read from file: %d, expected: %d.",
7350 header.numVptrs,
7351 (sizeof(DacGlobals) - offsetof(DacGlobals, Thread__vtAddr)) / sizeof(ULONG));
7352 _ASSERTE_MSG(false, szMsgBuf);
7353#endif // _DEBUG
7354
7355 status = E_INVALIDARG;
7356 goto Exit;
7357 }
7358
7359 // Copy the DAC table into g_dacGlobals
7360 if (bytesLeft < sizeof(DacGlobals))
7361 {
7362 _ASSERTE_MSG(false, "DAC fatal error: DAC table resource too small for DacGlobals.");
7363 status = E_UNEXPECTED;
7364 goto Exit;
7365 }
7366 memcpy(&g_dacGlobals, rawData, sizeof(DacGlobals));
7367 rawData += sizeof(DacGlobals);
7368 bytesLeft -= sizeof(DacGlobals);
7369
7370 status = S_OK;
7371
7372Exit:
7373
7374 return status;
7375#endif
7376}
7377
7378#undef MAKEINTRESOURCE
7379
7380//----------------------------------------------------------------------------
7381//
7382// IsExceptionFromManagedCode - report if pExceptionRecord points to an exception belonging to the current runtime
7383//
7384// Arguments:
7385// pExceptionRecord - the exception record
7386//
7387// Return Value:
7388// TRUE if it is
7389// Otherwise, FALSE
7390//
7391//----------------------------------------------------------------------------
7392BOOL ClrDataAccess::IsExceptionFromManagedCode(EXCEPTION_RECORD* pExceptionRecord)
7393{
7394 DAC_ENTER();
7395
7396 BOOL flag = FALSE;
7397
7398 if (::IsExceptionFromManagedCode(pExceptionRecord))
7399 {
7400 flag = TRUE;
7401 }
7402
7403 DAC_LEAVE();
7404
7405 return flag;
7406}
7407
7408#ifndef FEATURE_PAL
7409
7410//----------------------------------------------------------------------------
7411//
7412// GetWatsonBuckets - retrieve Watson buckets from the specified thread
7413//
7414// Arguments:
7415// dwThreadId - the thread ID
7416// pGM - pointer to the space to store retrieved Watson buckets
7417//
7418// Return Value:
7419// S_OK if the operation is successful.
7420// or S_FALSE if Watson buckets cannot be found
7421// else detailed error code.
7422//
7423//----------------------------------------------------------------------------
7424HRESULT ClrDataAccess::GetWatsonBuckets(DWORD dwThreadId, GenericModeBlock * pGM)
7425{
7426 _ASSERTE((dwThreadId != 0) && (pGM != NULL));
7427 if ((dwThreadId == 0) || (pGM == NULL))
7428 {
7429 return E_INVALIDARG;
7430 }
7431
7432 DAC_ENTER();
7433
7434 Thread * pThread = DacGetThread(dwThreadId);
7435 _ASSERTE(pThread != NULL);
7436
7437 HRESULT hr = E_UNEXPECTED;
7438
7439 if (pThread != NULL)
7440 {
7441 hr = GetClrWatsonBucketsWorker(pThread, pGM);
7442 }
7443
7444 DAC_LEAVE();
7445 return hr;
7446}
7447
7448#endif // FEATURE_PAL
7449
7450//----------------------------------------------------------------------------
7451//
7452// CLRDataAccessCreateInstance - create and initialize a ClrDataAccess object
7453//
7454// Arguments:
7455// pLegacyTarget - data target object
7456// pClrDataAccess - ClrDataAccess object
7457//
7458// Return Value:
7459// S_OK on success, else detailed error code.
7460//
7461//----------------------------------------------------------------------------
7462STDAPI CLRDataAccessCreateInstance(ICLRDataTarget * pLegacyTarget,
7463 ClrDataAccess ** pClrDataAccess)
7464{
7465 if ((pLegacyTarget == NULL) || (pClrDataAccess == NULL))
7466 {
7467 return E_INVALIDARG;
7468 }
7469
7470 *pClrDataAccess = NULL;
7471
7472 // Create an adapter which implements the new ICorDebugDataTarget interfaces using
7473 // a legacy implementation of ICLRDataTarget
7474 // ClrDataAccess will take a take a ref on this and delete it when it's released.
7475 DataTargetAdapter * pDtAdapter = new (nothrow) DataTargetAdapter(pLegacyTarget);
7476 if (!pDtAdapter)
7477 {
7478 return E_OUTOFMEMORY;
7479 }
7480
7481 ClrDataAccess* dacClass = new (nothrow) ClrDataAccess(pDtAdapter, pLegacyTarget);
7482 if (!dacClass)
7483 {
7484 delete pDtAdapter;
7485 return E_OUTOFMEMORY;
7486 }
7487
7488 HRESULT hr = dacClass->Initialize();
7489 if (FAILED(hr))
7490 {
7491 dacClass->Release();
7492 return hr;
7493 }
7494
7495 *pClrDataAccess = dacClass;
7496 return S_OK;
7497}
7498
7499
7500//----------------------------------------------------------------------------
7501//
7502// CLRDataCreateInstance.
7503// Creates the IXClrData object
7504// This is the legacy entrypoint to DAC, used by dbgeng/dbghelp (windbg, SOS, watson, etc).
7505//
7506//----------------------------------------------------------------------------
7507#ifdef __llvm__
7508__attribute__((used))
7509#endif // __llvm__
7510STDAPI
7511CLRDataCreateInstance(REFIID iid,
7512 ICLRDataTarget * pLegacyTarget,
7513 void ** iface)
7514{
7515 if ((pLegacyTarget == NULL) || (iface == NULL))
7516 {
7517 return E_INVALIDARG;
7518 }
7519
7520 *iface = NULL;
7521 ClrDataAccess * pClrDataAccess;
7522 HRESULT hr = CLRDataAccessCreateInstance(pLegacyTarget, &pClrDataAccess);
7523 if (hr != S_OK)
7524 {
7525 return hr;
7526 }
7527
7528 hr = pClrDataAccess->QueryInterface(iid, iface);
7529
7530 pClrDataAccess->Release();
7531 return hr;
7532}
7533
7534
7535//----------------------------------------------------------------------------
7536//
7537// OutOfProcessExceptionEventGetProcessIdAndThreadId - get ProcessID and ThreadID
7538//
7539// Arguments:
7540// hProcess - process handle
7541// hThread - thread handle
7542// pPId - pointer to DWORD to store ProcessID
7543// pThreadId - pointer to DWORD to store ThreadID
7544//
7545// Return Value:
7546// TRUE if the operation is successful.
7547// FALSE if it fails
7548//
7549//----------------------------------------------------------------------------
7550BOOL OutOfProcessExceptionEventGetProcessIdAndThreadId(HANDLE hProcess, HANDLE hThread, DWORD * pPId, DWORD * pThreadId)
7551{
7552 _ASSERTE((pPId != NULL) && (pThreadId != NULL));
7553
7554#ifdef FEATURE_PAL
7555 // UNIXTODO: mikem 1/13/15 Need appropriate PAL functions for getting ids
7556 *pPId = (DWORD)hProcess;
7557 *pThreadId = (DWORD)hThread;
7558#else
7559#if !defined(FEATURE_CORESYSTEM)
7560 HMODULE hKernel32 = WszGetModuleHandle(W("kernel32.dll"));
7561#else
7562 HMODULE hKernel32 = WszGetModuleHandle(W("api-ms-win-core-processthreads-l1-1-1.dll"));
7563#endif
7564 if (hKernel32 == NULL)
7565 {
7566 return FALSE;
7567 }
7568
7569 typedef WINBASEAPI DWORD (WINAPI GET_PROCESSID_OF_THREAD)(HANDLE);
7570 GET_PROCESSID_OF_THREAD * pGetProcessIdOfThread;
7571
7572 typedef WINBASEAPI DWORD (WINAPI GET_THREADID)(HANDLE);
7573 GET_THREADID * pGetThreadId;
7574
7575 pGetProcessIdOfThread = (GET_PROCESSID_OF_THREAD *)GetProcAddress(hKernel32, "GetProcessIdOfThread");
7576 pGetThreadId = (GET_THREADID *)GetProcAddress(hKernel32, "GetThreadId");
7577
7578 // OOP callbacks are used on Win7 or later. We should have having below two APIs available.
7579 _ASSERTE((pGetProcessIdOfThread != NULL) && (pGetThreadId != NULL));
7580 if ((pGetProcessIdOfThread == NULL) || (pGetThreadId == NULL))
7581 {
7582 return FALSE;
7583 }
7584
7585 *pPId = (*pGetProcessIdOfThread)(hThread);
7586 *pThreadId = (*pGetThreadId)(hThread);
7587#endif // FEATURE_PAL
7588 return TRUE;
7589}
7590
7591// WER_RUNTIME_EXCEPTION_INFORMATION will be available from Win7 SDK once Win7 SDK is released.
7592#if !defined(WER_RUNTIME_EXCEPTION_INFORMATION)
7593typedef struct _WER_RUNTIME_EXCEPTION_INFORMATION
7594{
7595 DWORD dwSize;
7596 HANDLE hProcess;
7597 HANDLE hThread;
7598 EXCEPTION_RECORD exceptionRecord;
7599 CONTEXT context;
7600} WER_RUNTIME_EXCEPTION_INFORMATION, * PWER_RUNTIME_EXCEPTION_INFORMATION;
7601#endif // !defined(WER_RUNTIME_EXCEPTION_INFORMATION)
7602
7603
7604#ifndef FEATURE_PAL
7605
7606//----------------------------------------------------------------------------
7607//
7608// OutOfProcessExceptionEventGetWatsonBucket - retrieve Watson buckets if it is a managed exception
7609//
7610// Arguments:
7611// pContext - the context passed at helper module registration
7612// pExceptionInformation - structure that contains information about the crash
7613// pGM - pointer to the space to store retrieved Watson buckets
7614//
7615// Return Value:
7616// S_OK if the operation is successful.
7617// or S_FALSE if it is not a managed exception or Watson buckets cannot be found
7618// else detailed error code.
7619//
7620//----------------------------------------------------------------------------
7621STDAPI OutOfProcessExceptionEventGetWatsonBucket(__in PDWORD pContext,
7622 __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,
7623 __out GenericModeBlock * pGMB)
7624{
7625 HANDLE hProcess = pExceptionInformation->hProcess;
7626 HANDLE hThread = pExceptionInformation->hThread;
7627 DWORD PId, ThreadId;
7628
7629 if (!OutOfProcessExceptionEventGetProcessIdAndThreadId(hProcess, hThread, &PId, &ThreadId))
7630 {
7631 return E_FAIL;
7632 }
7633
7634 CLRDATA_ADDRESS baseAddressOfRuntime = (CLRDATA_ADDRESS)pContext;
7635 NewHolder<LiveProcDataTarget> dataTarget(NULL);
7636
7637 dataTarget = new (nothrow) LiveProcDataTarget(hProcess, PId, baseAddressOfRuntime);
7638 if (dataTarget == NULL)
7639 {
7640 return E_OUTOFMEMORY;
7641 }
7642
7643 NewHolder<ClrDataAccess> pClrDataAccess(NULL);
7644
7645 HRESULT hr = CLRDataAccessCreateInstance(dataTarget, &pClrDataAccess);
7646 if (hr != S_OK)
7647 {
7648 if (hr == S_FALSE)
7649 {
7650 return E_FAIL;
7651 }
7652 else
7653 {
7654 return hr;
7655 }
7656 }
7657
7658 if (!pClrDataAccess->IsExceptionFromManagedCode(&pExceptionInformation->exceptionRecord))
7659 {
7660 return S_FALSE;
7661 }
7662
7663 return pClrDataAccess->GetWatsonBuckets(ThreadId, pGMB);
7664}
7665
7666//----------------------------------------------------------------------------
7667//
7668// OutOfProcessExceptionEventCallback - claim the ownership of this event if current
7669// runtime threw the unhandled exception
7670//
7671// Arguments:
7672// pContext - the context passed at helper module registration
7673// pExceptionInformation - structure that contains information about the crash
7674// pbOwnershipClaimed - output parameter for claiming the ownership of this event
7675// pwszEventName - name of the event. If this is NULL, pchSize cannot be NULL.
7676// This parameter is valid only if * pbOwnershipClaimed is TRUE.
7677// pchSize - the size of the buffer pointed by pwszEventName
7678// pdwSignatureCount - the count of signature parameters. Valid values range from
7679// 0 to 10. If the value returned is greater than 10, only the
7680// 1st 10 parameters are used for bucketing parameters. This
7681// parameter is valid only if * pbOwnershipClaimed is TRUE.
7682//
7683// Return Value:
7684// S_OK on success, else detailed error code.
7685//
7686// Note:
7687// This is the 1st function that is called into by WER. This API through its out
7688// parameters, tells WER as to whether or not it is claiming the crash. If it does
7689// claim the crash, WER uses the event name specified in the string pointed to by
7690// pwszEventName for error reporting. WER then proceed to call the
7691// OutOfProcessExceptionEventSignatureCallback to get the bucketing parameters from
7692// the helper dll.
7693//
7694// This function follows the multiple call paradigms. WER may call into this function
7695// with *pwszEventName pointer set to NULL. This is to indicate to the function, that
7696// WER wants to know the buffer size needed by the function to populate the string
7697// into the buffer. The function should return E_INSUFFICIENTBUFFER with the needed
7698// buffer size in *pchSize. WER shall then allocate a buffer of size *pchSize for
7699// pwszEventName and then call this function again at which point the function should
7700// populate the string and return S_OK.
7701//
7702// Note that *pdOwnershipClaimed should be set to TRUE everytime this function is called
7703// for the helper dll to claim ownership of bucketing.
7704//
7705// The Win7 WER spec is at
7706// http://windows/windows7/docs/COSD%20Documents/Fundamentals/Feedback%20Services%20and%20Platforms/WER-CLR%20Integration%20Dev%20Spec.docx
7707//
7708// !!!READ THIS!!!
7709// Since this is called by external modules it's important that we don't let any exceptions leak out (see Win8 95224).
7710//
7711//----------------------------------------------------------------------------
7712STDAPI OutOfProcessExceptionEventCallback(__in PDWORD pContext,
7713 __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,
7714 __out BOOL * pbOwnershipClaimed,
7715 __out_ecount(*pchSize) PWSTR pwszEventName,
7716 __inout PDWORD pchSize,
7717 __out PDWORD pdwSignatureCount)
7718{
7719 SUPPORTS_DAC_HOST_ONLY;
7720
7721 if ((pContext == NULL) ||
7722 (pExceptionInformation == NULL) ||
7723 (pExceptionInformation->dwSize < sizeof(WER_RUNTIME_EXCEPTION_INFORMATION)) ||
7724 (pbOwnershipClaimed == NULL) ||
7725 (pchSize == NULL) ||
7726 (pdwSignatureCount == NULL))
7727 {
7728 return E_INVALIDARG;
7729 }
7730
7731 *pbOwnershipClaimed = FALSE;
7732
7733 GenericModeBlock gmb;
7734 HRESULT hr = E_FAIL;
7735
7736 EX_TRY
7737 {
7738 // get Watson buckets if it is a managed exception
7739 hr = OutOfProcessExceptionEventGetWatsonBucket(pContext, pExceptionInformation, &gmb);
7740 }
7741 EX_CATCH_HRESULT(hr);
7742
7743 if (hr != S_OK)
7744 {
7745 // S_FALSE means either it is not a managed exception or we do not have Watson buckets.
7746 // Since we have set pbOwnershipClaimed to FALSE, we return S_OK to WER.
7747 if (hr == S_FALSE)
7748 {
7749 hr = S_OK;
7750 }
7751
7752 return hr;
7753 }
7754
7755 if ((pwszEventName == NULL) || (*pchSize <= wcslen(gmb.wzEventTypeName)))
7756 {
7757 *pchSize = static_cast<DWORD>(wcslen(gmb.wzEventTypeName)) + 1;
7758 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
7759 }
7760
7761 // copy custom event name
7762 wcscpy_s(pwszEventName, *pchSize, gmb.wzEventTypeName);
7763 *pdwSignatureCount = GetCountBucketParamsForEvent(gmb.wzEventTypeName);
7764 *pbOwnershipClaimed = TRUE;
7765
7766 return S_OK;
7767}
7768
7769
7770//----------------------------------------------------------------------------
7771//
7772// OutOfProcessExceptionEventCallback - provide custom Watson buckets
7773//
7774// Arguments:
7775// pContext - the context passed at helper module registration
7776// pExceptionInformation - structure that contains information about the crash
7777// dwIndex - the index of the bucketing parameter being requested. Valid values are
7778// from 0 to 9
7779// pwszName - pointer to the name of the bucketing parameter
7780// pchName - pointer to character count of the pwszName buffer. If pwszName points to
7781// null, *pchName represents the buffer size (represented in number of characters)
7782// needed to populate the name in pwszName.
7783// pwszValue - pointer to the value of the pwszName bucketing parameter
7784// pchValue - pointer to the character count of the pwszValue buffer. If pwszValue points
7785// to null, *pchValue represents the buffer size (represented in number of
7786// characters) needed to populate the value in pwszValue.
7787//
7788// Return Value:
7789// S_OK on success, else detailed error code.
7790//
7791// Note:
7792// This function is called by WER only if the call to OutOfProcessExceptionEventCallback()
7793// was successful and the value of *pbOwnershipClaimed was TRUE. This function is called
7794// pdwSignatureCount times to collect the bucketing parameters from the helper dll.
7795//
7796// This function also follows the multiple call paradigm as described for the
7797// OutOfProcessExceptionEventCallback() function. The buffer sizes needed for
7798// this function are of the pwszName and pwszValue buffers.
7799//
7800// !!!READ THIS!!!
7801// Since this is called by external modules it's important that we don't let any exceptions leak out (see Win8 95224).
7802//
7803//----------------------------------------------------------------------------
7804STDAPI OutOfProcessExceptionEventSignatureCallback(__in PDWORD pContext,
7805 __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,
7806 __in DWORD dwIndex,
7807 __out_ecount(*pchName) PWSTR pwszName,
7808 __inout PDWORD pchName,
7809 __out_ecount(*pchValue) PWSTR pwszValue,
7810 __inout PDWORD pchValue)
7811{
7812 SUPPORTS_DAC_HOST_ONLY;
7813
7814 if ((pContext == NULL) ||
7815 (pExceptionInformation == NULL) ||
7816 (pExceptionInformation->dwSize < sizeof(WER_RUNTIME_EXCEPTION_INFORMATION)) ||
7817 (pchName == NULL) ||
7818 (pchValue == NULL))
7819 {
7820 return E_INVALIDARG;
7821 }
7822
7823 if ((pwszName == NULL) || (*pchName == 0))
7824 {
7825 *pchName = 1;
7826 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
7827 }
7828
7829 GenericModeBlock gmb;
7830 const PWSTR pwszBucketValues[] = {gmb.wzP1,
7831 gmb.wzP2,
7832 gmb.wzP3,
7833 gmb.wzP4,
7834 gmb.wzP5,
7835 gmb.wzP6,
7836 gmb.wzP7,
7837 gmb.wzP8,
7838 gmb.wzP9,
7839 gmb.wzP10};
7840
7841 HRESULT hr = E_FAIL;
7842
7843 EX_TRY
7844 {
7845 // get Watson buckets if it is a managed exception
7846 hr = OutOfProcessExceptionEventGetWatsonBucket(pContext, pExceptionInformation, &gmb);
7847 }
7848 EX_CATCH_HRESULT(hr);
7849
7850#ifndef FEATURE_WINDOWSPHONE
7851 // we can't assert this on phone as it's possible for the OS to kill
7852 // the faulting process before WER crash reporting has completed.
7853 _ASSERTE(hr == S_OK);
7854#else
7855 _ASSERTE(hr == S_OK || hr == CORDBG_E_READVIRTUAL_FAILURE);
7856#endif
7857 if (hr != S_OK)
7858 {
7859 // S_FALSE means either it is not a managed exception or we do not have Watson buckets.
7860 // Either case is a logic error becuase this function is called by WER only if the call
7861 // to OutOfProcessExceptionEventCallback() was successful and the value of
7862 // *pbOwnershipClaimed was TRUE.
7863 if (hr == S_FALSE)
7864 {
7865 hr = E_FAIL;
7866 }
7867
7868 return hr;
7869 }
7870
7871 DWORD paramCount = GetCountBucketParamsForEvent(gmb.wzEventTypeName);
7872
7873 if (dwIndex >= paramCount)
7874 {
7875 _ASSERTE(!"dwIndex is out of range");
7876 return E_INVALIDARG;
7877 }
7878
7879 // Return pwszName as an emptry string to let WER use localized version of "Parameter n"
7880 *pwszName = W('\0');
7881
7882 if ((pwszValue == NULL) || (*pchValue <= wcslen(pwszBucketValues[dwIndex])))
7883 {
7884 *pchValue = static_cast<DWORD>(wcslen(pwszBucketValues[dwIndex]))+ 1;
7885 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
7886 }
7887
7888 // copy custom Watson bucket value
7889 wcscpy_s(pwszValue, *pchValue, pwszBucketValues[dwIndex]);
7890
7891 return S_OK;
7892}
7893
7894#endif // FEATURE_PAL
7895
7896//----------------------------------------------------------------------------
7897//
7898// OutOfProcessExceptionEventCallback - provide custom debugger launch string
7899//
7900// Arguments:
7901// pContext - the context passed at helper module registration
7902// pExceptionInformation - structure that contains information about the crash
7903// pbCustomDebuggerNeeded - pointer to a BOOL. If this BOOL is set to TRUE, then
7904// a custom debugger launch option is needed by the
7905// process. In that case, the subsequent parameters will
7906// be meaningfully used. If this is FALSE, the subsequent
7907// parameters will be ignored.
7908// pwszDebuggerLaunch - pointer to a string that will be used to launch the debugger,
7909// if the debugger is launched. The value of this string overrides
7910// the default debugger launch string used by WER.
7911// pchSize - pointer to the character count of the pwszDebuggerLaunch buffer. If
7912// pwszDebuggerLaunch points to null, *pchSize represents the buffer size
7913// (represented in number of characters) needed to populate the debugger
7914// launch string in pwszDebuggerLaunch.
7915// pbAutoLaunchDebugger - pointer to a BOOL. If this BOOL is set to TRUE, WER will
7916// directly launch the debugger. If set to FALSE, WER will show
7917// the debug option to the user in the WER UI.
7918//
7919// Return Value:
7920// S_OK on success, else detailed error code.
7921//
7922// Note:
7923// This function is called into by WER only if the call to OutOfProcessExceptionEventCallback()
7924// was successful and the value of *pbOwnershipClaimed was TRUE. This function allows the helper
7925// dll to customize the debugger launch options including the launch string.
7926//
7927// This function also follows the multiple call paradigm as described for the
7928// OutOfProcessExceptionEventCallback() function. The buffer sizes needed for
7929// this function are of the pwszName and pwszValue buffers.
7930//
7931//----------------------------------------------------------------------------
7932STDAPI OutOfProcessExceptionEventDebuggerLaunchCallback(__in PDWORD pContext,
7933 __in const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,
7934 __out BOOL * pbCustomDebuggerNeeded,
7935 __out_ecount_opt(*pchSize) PWSTR pwszDebuggerLaunch,
7936 __inout PDWORD pchSize,
7937 __out BOOL * pbAutoLaunchDebugger)
7938{
7939 SUPPORTS_DAC_HOST_ONLY;
7940
7941 if ((pContext == NULL) ||
7942 (pExceptionInformation == NULL) ||
7943 (pExceptionInformation->dwSize < sizeof(WER_RUNTIME_EXCEPTION_INFORMATION)) ||
7944 (pbCustomDebuggerNeeded == NULL) ||
7945 (pwszDebuggerLaunch == NULL) ||
7946 (pchSize == NULL) ||
7947 (pbAutoLaunchDebugger == NULL))
7948 {
7949 return E_INVALIDARG;
7950 }
7951
7952 // Starting from CLRv4 managed debugger string and setting are unified with native debuggers.
7953 // There is no need to provide custom debugger string for WER.
7954 *pbCustomDebuggerNeeded = FALSE;
7955
7956 return S_OK;
7957}
7958
7959// DacHandleEnum
7960
7961#include "comcallablewrapper.h"
7962
7963DacHandleWalker::DacHandleWalker()
7964 : mDac(0), m_instanceAge(0), mMap(0), mIndex(0),
7965 mTypeMask(0), mGenerationFilter(-1), mChunkIndex(0), mCurr(0),
7966 mIteratorIndex(0)
7967{
7968 SUPPORTS_DAC;
7969}
7970
7971DacHandleWalker::~DacHandleWalker()
7972{
7973 SUPPORTS_DAC;
7974
7975 HandleChunkHead *curr = mHead.Next;
7976
7977 while (curr)
7978 {
7979 HandleChunkHead *tmp = curr;
7980 curr = curr->Next;
7981 delete tmp;
7982 }
7983}
7984
7985HRESULT DacHandleWalker::Init(ClrDataAccess *dac, UINT types[], UINT typeCount)
7986{
7987 SUPPORTS_DAC;
7988
7989 if (dac == NULL || types == NULL)
7990 return E_POINTER;
7991
7992 mDac = dac;
7993 m_instanceAge = dac->m_instanceAge;
7994
7995 return Init(BuildTypemask(types, typeCount));
7996}
7997
7998HRESULT DacHandleWalker::Init(ClrDataAccess *dac, UINT types[], UINT typeCount, int gen)
7999{
8000 SUPPORTS_DAC;
8001
8002 if (gen < 0 || gen > (int)*g_gcDacGlobals->max_gen)
8003 return E_INVALIDARG;
8004
8005 mGenerationFilter = gen;
8006
8007 return Init(dac, types, typeCount);
8008}
8009
8010HRESULT DacHandleWalker::Init(UINT32 typemask)
8011{
8012 SUPPORTS_DAC;
8013
8014 mMap = g_gcDacGlobals->handle_table_map;
8015 mTypeMask = typemask;
8016
8017 return S_OK;
8018}
8019
8020UINT32 DacHandleWalker::BuildTypemask(UINT types[], UINT typeCount)
8021{
8022 SUPPORTS_DAC;
8023
8024 UINT32 mask = 0;
8025
8026 for (UINT i = 0; i < typeCount; ++i)
8027 {
8028 _ASSERTE(types[i] < 32);
8029 mask |= (1 << types[i]);
8030 }
8031
8032 return mask;
8033}
8034
8035HRESULT DacHandleWalker::Next(unsigned int celt,
8036 SOSHandleData handles[],
8037 unsigned int *pceltFetched)
8038{
8039 SUPPORTS_DAC;
8040
8041 if (handles == NULL || pceltFetched == NULL)
8042 return E_POINTER;
8043
8044 SOSHelperEnter();
8045
8046 hr = DoHandleWalk<SOSHandleData, unsigned int, DacHandleWalker::EnumCallbackSOS>(celt, handles, pceltFetched);
8047
8048 SOSHelperLeave();
8049
8050 return hr;
8051}
8052
8053bool DacHandleWalker::FetchMoreHandles(HANDLESCANPROC callback)
8054{
8055 SUPPORTS_DAC;
8056
8057 // The table slots are based on the number of GC heaps in the process.
8058 int max_slots = 1;
8059
8060#ifdef FEATURE_SVR_GC
8061 if (GCHeapUtilities::IsServerHeap())
8062 max_slots = GCHeapCount();
8063#endif // FEATURE_SVR_GC
8064
8065 // Reset the Count on all cached chunks. We reuse chunks after allocating
8066 // them, and the count is the only thing which needs resetting.
8067 for (HandleChunkHead *curr = &mHead; curr; curr = curr->Next)
8068 curr->Count = 0;
8069
8070 DacHandleWalkerParam param(&mHead);
8071
8072 do
8073 {
8074 // Have we advanced past the end of the current bucket?
8075 if (mMap && mIndex >= INITIAL_HANDLE_TABLE_ARRAY_SIZE)
8076 {
8077 mIndex = 0;
8078 mMap = mMap->pNext;
8079 }
8080
8081 // Have we walked the entire handle table map?
8082 if (mMap == NULL)
8083 {
8084 mCurr = NULL;
8085 return false;
8086 }
8087
8088 if (mMap->pBuckets[mIndex] != NULL)
8089 {
8090 for (int i = 0; i < max_slots; ++i)
8091 {
8092 DPTR(dac_handle_table) hTable = mMap->pBuckets[mIndex]->pTable[i];
8093 if (hTable)
8094 {
8095 // Yikes! The handle table callbacks don't produce the handle type or
8096 // the AppDomain that we need, and it's too difficult to propogate out
8097 // these things (especially the type) without worrying about performance
8098 // implications for the GC. Instead we'll have the callback walk each
8099 // type individually. There are only a few handle types, and the handle
8100 // table has a fast-path for only walking a single type anyway.
8101 UINT32 handleType = 0;
8102 for (UINT32 mask = mTypeMask; mask; mask >>= 1, handleType++)
8103 {
8104 if (mask & 1)
8105 {
8106 dac_handle_table *pTable = hTable;
8107 PTR_AppDomain pDomain = SystemDomain::GetAppDomainAtIndex(ADIndex(pTable->uADIndex));
8108 param.AppDomain = TO_CDADDR(pDomain.GetAddr());
8109 param.Type = handleType;
8110
8111 // Either enumerate the handles regularly, or walk the handle
8112 // table as the GC does if a generation filter was requested.
8113 if (mGenerationFilter != -1)
8114 HndScanHandlesForGC(hTable, callback,
8115 (LPARAM)&param, 0,
8116 &handleType, 1,
8117 mGenerationFilter, *g_gcDacGlobals->max_gen, 0);
8118 else
8119 HndEnumHandles(hTable, &handleType, 1, callback, (LPARAM)&param, 0, FALSE);
8120 }
8121 }
8122 }
8123 }
8124 }
8125
8126 // Stop looping as soon as we have found data. We also stop if we have a failed HRESULT during
8127 // the callback (this should indicate OOM).
8128 mIndex++;
8129 } while (mHead.Count == 0 && SUCCEEDED(param.Result));
8130
8131 mCurr = mHead.Next;
8132 return true;
8133}
8134
8135
8136HRESULT DacHandleWalker::Skip(unsigned int celt)
8137{
8138 return E_NOTIMPL;
8139}
8140
8141HRESULT DacHandleWalker::Reset()
8142{
8143 return E_NOTIMPL;
8144}
8145
8146HRESULT DacHandleWalker::GetCount(unsigned int *pcelt)
8147{
8148 return E_NOTIMPL;
8149}
8150
8151
8152void DacHandleWalker::GetRefCountedHandleInfo(
8153 OBJECTREF oref, unsigned int uType,
8154 unsigned int *pRefCount, unsigned int *pJupiterRefCount, BOOL *pIsPegged, BOOL *pIsStrong)
8155{
8156 SUPPORTS_DAC;
8157
8158#ifdef FEATURE_COMINTEROP
8159 if (uType == HNDTYPE_REFCOUNTED)
8160 {
8161 // get refcount from the CCW
8162 PTR_ComCallWrapper pWrap = ComCallWrapper::GetWrapperForObject(oref);
8163 if (pWrap != NULL)
8164 {
8165 if (pRefCount)
8166 *pRefCount = (unsigned int)pWrap->GetRefCount();
8167
8168 if (pJupiterRefCount)
8169 *pJupiterRefCount = (unsigned int)pWrap->GetJupiterRefCount();
8170
8171 if (pIsPegged)
8172 *pIsPegged = pWrap->IsConsideredPegged();
8173
8174 if (pIsStrong)
8175 *pIsStrong = pWrap->IsWrapperActive();
8176
8177 return;
8178 }
8179 }
8180#endif // FEATURE_COMINTEROP
8181
8182 if (pRefCount)
8183 *pRefCount = 0;
8184
8185 if (pJupiterRefCount)
8186 *pJupiterRefCount = 0;
8187
8188 if (pIsPegged)
8189 *pIsPegged = FALSE;
8190
8191 if (pIsStrong)
8192 *pIsStrong = FALSE;
8193}
8194
8195void CALLBACK DacHandleWalker::EnumCallbackSOS(PTR_UNCHECKED_OBJECTREF handle, uintptr_t *pExtraInfo, uintptr_t param1, uintptr_t param2)
8196{
8197 SUPPORTS_DAC;
8198
8199 DacHandleWalkerParam *param = (DacHandleWalkerParam *)param1;
8200 HandleChunkHead *curr = param->Curr;
8201
8202 // If we failed on a previous call (OOM) don't keep trying to allocate, it's not going to work.
8203 if (FAILED(param->Result))
8204 return;
8205
8206 // We've moved past the size of the current chunk. We'll allocate a new chunk
8207 // and stuff the handles there. These are cleaned up by the destructor
8208 if (curr->Count >= (curr->Size/sizeof(SOSHandleData)))
8209 {
8210 if (curr->Next == NULL)
8211 {
8212 HandleChunk *next = new (nothrow) HandleChunk;
8213 if (next != NULL)
8214 {
8215 curr->Next = next;
8216 }
8217 else
8218 {
8219 param->Result = E_OUTOFMEMORY;
8220 return;
8221 }
8222 }
8223
8224 curr = param->Curr = param->Curr->Next;
8225 }
8226
8227 // Fill the current handle.
8228 SOSHandleData *dataArray = (SOSHandleData*)curr->pData;
8229 SOSHandleData &data = dataArray[curr->Count++];
8230
8231 data.Handle = TO_CDADDR(handle.GetAddr());
8232 data.Type = param->Type;
8233 if (param->Type == HNDTYPE_DEPENDENT)
8234 data.Secondary = GetDependentHandleSecondary(handle.GetAddr()).GetAddr();
8235#ifdef FEATURE_COMINTEROP
8236 else if (param->Type == HNDTYPE_WEAK_WINRT)
8237 data.Secondary = HndGetHandleExtraInfo(handle.GetAddr());
8238#endif // FEATURE_COMINTEROP
8239 else
8240 data.Secondary = 0;
8241 data.AppDomain = param->AppDomain;
8242 GetRefCountedHandleInfo((OBJECTREF)*handle, param->Type, &data.RefCount, &data.JupiterRefCount, &data.IsPegged, &data.StrongReference);
8243 data.StrongReference |= (BOOL)IsAlwaysStrongReference(param->Type);
8244}
8245
8246DacStackReferenceWalker::DacStackReferenceWalker(ClrDataAccess *dac, DWORD osThreadID)
8247 : mDac(dac), m_instanceAge(dac ? dac->m_instanceAge : 0), mThread(0), mErrors(0), mEnumerated(false),
8248 mChunkIndex(0), mCurr(0), mIteratorIndex(0)
8249{
8250 Thread *curr = NULL;
8251
8252 for (curr = ThreadStore::GetThreadList(curr);
8253 curr;
8254 curr = ThreadStore::GetThreadList(curr))
8255 {
8256 if (curr->GetOSThreadId() == osThreadID)
8257 {
8258 mThread = curr;
8259 break;
8260 }
8261 }
8262}
8263
8264DacStackReferenceWalker::~DacStackReferenceWalker()
8265{
8266 StackRefChunkHead *curr = mHead.next;
8267
8268 while (curr)
8269 {
8270 StackRefChunkHead *tmp = curr;
8271 curr = curr->next;
8272 delete tmp;
8273 }
8274}
8275
8276HRESULT DacStackReferenceWalker::Init()
8277{
8278 if (!mThread)
8279 return E_INVALIDARG;
8280 return mHeap.Init();
8281}
8282
8283HRESULT STDMETHODCALLTYPE DacStackReferenceWalker::Skip(unsigned int count)
8284{
8285 return E_NOTIMPL;
8286}
8287
8288HRESULT STDMETHODCALLTYPE DacStackReferenceWalker::Reset()
8289{
8290 return E_NOTIMPL;
8291}
8292
8293HRESULT DacStackReferenceWalker::GetCount(unsigned int *pCount)
8294{
8295 if (!pCount)
8296 return E_POINTER;
8297
8298 SOSHelperEnter();
8299
8300 if (!mEnumerated)
8301 {
8302 // Fill out our data structures.
8303 WalkStack<unsigned int, SOSStackRefData>(0, NULL, DacStackReferenceWalker::GCReportCallbackSOS, DacStackReferenceWalker::GCEnumCallbackSOS);
8304 }
8305
8306 unsigned int count = 0;
8307 for(StackRefChunkHead *curr = &mHead; curr; curr = curr->next)
8308 count += curr->count;
8309
8310 *pCount = count;
8311
8312 SOSHelperLeave();
8313 return hr;
8314}
8315
8316HRESULT DacStackReferenceWalker::Next(unsigned int count,
8317 SOSStackRefData stackRefs[],
8318 unsigned int *pFetched)
8319{
8320 if (stackRefs == NULL || pFetched == NULL)
8321 return E_POINTER;
8322
8323 SOSHelperEnter();
8324
8325 hr = DoStackWalk<unsigned int, SOSStackRefData,
8326 DacStackReferenceWalker::GCReportCallbackSOS,
8327 DacStackReferenceWalker::GCEnumCallbackSOS>
8328 (count, stackRefs, pFetched);
8329
8330 SOSHelperLeave();
8331
8332 return hr;
8333}
8334
8335HRESULT DacStackReferenceWalker::EnumerateErrors(ISOSStackRefErrorEnum **ppEnum)
8336{
8337 if (!ppEnum)
8338 return E_POINTER;
8339
8340 SOSHelperEnter();
8341
8342 if (mThread)
8343 {
8344 // Fill out our data structures.
8345 WalkStack<unsigned int, SOSStackRefData>(0, NULL, DacStackReferenceWalker::GCReportCallbackSOS, DacStackReferenceWalker::GCEnumCallbackSOS);
8346 }
8347
8348 DacStackReferenceErrorEnum *pEnum = new DacStackReferenceErrorEnum(this, mErrors);
8349 hr = pEnum->QueryInterface(__uuidof(ISOSStackRefErrorEnum), (void**)ppEnum);
8350
8351 SOSHelperLeave();
8352 return hr;
8353}
8354
8355CLRDATA_ADDRESS DacStackReferenceWalker::ReadPointer(TADDR addr)
8356{
8357 ULONG32 bytesRead = 0;
8358 TADDR result = 0;
8359 HRESULT hr = mDac->m_pTarget->ReadVirtual(addr, (BYTE*)&result, sizeof(TADDR), &bytesRead);
8360
8361 if (FAILED(hr) || (bytesRead != sizeof(TADDR)))
8362 return (CLRDATA_ADDRESS)~0;
8363
8364 return TO_CDADDR(result);
8365}
8366
8367
8368void DacStackReferenceWalker::GCEnumCallbackSOS(LPVOID hCallback, OBJECTREF *pObject, uint32_t flags, DacSlotLocation loc)
8369{
8370 GCCONTEXT *gcctx = (GCCONTEXT *)hCallback;
8371 DacScanContext *dsc = (DacScanContext*)gcctx->sc;
8372
8373 // Yuck. The GcInfoDecoder reports a local pointer for registers (as it's reading out of the REGDISPLAY
8374 // in the stack walk), and it reports a TADDR for stack locations. This is architecturally difficulty
8375 // to fix, so we are leaving it for now.
8376 TADDR addr = 0;
8377 TADDR obj = 0;
8378
8379 if (loc.targetPtr)
8380 {
8381 addr = (TADDR)pObject;
8382 obj = TO_TADDR(dsc->pWalker->ReadPointer((CORDB_ADDRESS)addr));
8383 }
8384 else
8385 {
8386 obj = pObject->GetAddr();
8387 }
8388
8389 if (flags & GC_CALL_INTERIOR)
8390 {
8391 CORDB_ADDRESS fixed_obj = 0;
8392 HRESULT hr = dsc->pWalker->mHeap.ListNearObjects((CORDB_ADDRESS)obj, NULL, &fixed_obj, NULL);
8393
8394 // If we failed...oh well, SOS won't mind. We'll just report the interior pointer as is.
8395 if (SUCCEEDED(hr))
8396 obj = TO_TADDR(fixed_obj);
8397 }
8398
8399 SOSStackRefData *data = dsc->pWalker->GetNextObject<SOSStackRefData>(dsc);
8400 if (data != NULL)
8401 {
8402 // Report where the object and where it was found.
8403 data->HasRegisterInformation = true;
8404 data->Register = loc.reg;
8405 data->Offset = loc.regOffset;
8406 data->Address = TO_CDADDR(addr);
8407 data->Object = TO_CDADDR(obj);
8408 data->Flags = flags;
8409
8410 // Report the frame that the data came from.
8411 data->StackPointer = TO_CDADDR(dsc->sp);
8412
8413 if (dsc->pFrame)
8414 {
8415 data->SourceType = SOS_StackSourceFrame;
8416 data->Source = dac_cast<PTR_Frame>(dsc->pFrame).GetAddr();
8417 }
8418 else
8419 {
8420 data->SourceType = SOS_StackSourceIP;
8421 data->Source = TO_CDADDR(dsc->pc);
8422 }
8423 }
8424}
8425
8426
8427void DacStackReferenceWalker::GCReportCallbackSOS(PTR_PTR_Object ppObj, ScanContext *sc, uint32_t flags)
8428{
8429 DacScanContext *dsc = (DacScanContext*)sc;
8430 CLRDATA_ADDRESS obj = dsc->pWalker->ReadPointer(ppObj.GetAddr());
8431
8432 if (flags & GC_CALL_INTERIOR)
8433 {
8434 CORDB_ADDRESS fixed_addr = 0;
8435 HRESULT hr = dsc->pWalker->mHeap.ListNearObjects((CORDB_ADDRESS)obj, NULL, &fixed_addr, NULL);
8436
8437 // If we failed...oh well, SOS won't mind. We'll just report the interior pointer as is.
8438 if (SUCCEEDED(hr))
8439 obj = TO_CDADDR(fixed_addr);
8440 }
8441
8442 SOSStackRefData *data = dsc->pWalker->GetNextObject<SOSStackRefData>(dsc);
8443 if (data != NULL)
8444 {
8445 data->HasRegisterInformation = false;
8446 data->Register = 0;
8447 data->Offset = 0;
8448 data->Address = ppObj.GetAddr();
8449 data->Object = obj;
8450 data->Flags = flags;
8451 data->StackPointer = TO_CDADDR(dsc->sp);
8452
8453 if (dsc->pFrame)
8454 {
8455 data->SourceType = SOS_StackSourceFrame;
8456 data->Source = dac_cast<PTR_Frame>(dsc->pFrame).GetAddr();
8457 }
8458 else
8459 {
8460 data->SourceType = SOS_StackSourceIP;
8461 data->Source = TO_CDADDR(dsc->pc);
8462 }
8463 }
8464}
8465
8466StackWalkAction DacStackReferenceWalker::Callback(CrawlFrame *pCF, VOID *pData)
8467{
8468 //
8469 // KEEP IN SYNC WITH GcStackCrawlCallBack in vm\gcscan.cpp
8470 //
8471
8472 GCCONTEXT *gcctx = (GCCONTEXT*)pData;
8473 DacScanContext *dsc = (DacScanContext*)gcctx->sc;
8474
8475 MethodDesc *pMD = pCF->GetFunction();
8476 gcctx->sc->pMD = pMD;
8477 gcctx->sc->pCurrentDomain = pCF->GetAppDomain();
8478
8479 PREGDISPLAY pRD = pCF->GetRegisterSet();
8480 dsc->sp = (TADDR)GetRegdisplaySP(pRD);;
8481 dsc->pc = PCODEToPINSTR(GetControlPC(pRD));
8482
8483 ResetPointerHolder<CrawlFrame*> rph(&gcctx->cf);
8484 gcctx->cf = pCF;
8485
8486 bool fReportGCReferences = true;
8487#if defined(WIN64EXCEPTIONS)
8488 // On Win64 and ARM, we may have unwound this crawlFrame and thus, shouldn't report the invalid
8489 // references it may contain.
8490 // todo.
8491 fReportGCReferences = pCF->ShouldCrawlframeReportGCReferences();
8492#endif // defined(WIN64EXCEPTIONS)
8493
8494 Frame *pFrame = ((DacScanContext*)gcctx->sc)->pFrame = pCF->GetFrame();
8495
8496 EX_TRY
8497 {
8498 if (fReportGCReferences)
8499 {
8500 if (pCF->IsFrameless())
8501 {
8502 ICodeManager * pCM = pCF->GetCodeManager();
8503 _ASSERTE(pCM != NULL);
8504
8505 unsigned flags = pCF->GetCodeManagerFlags();
8506
8507 pCM->EnumGcRefs(pCF->GetRegisterSet(),
8508 pCF->GetCodeInfo(),
8509 flags,
8510 dsc->pEnumFunc,
8511 pData);
8512 }
8513 else
8514 {
8515 pFrame->GcScanRoots(gcctx->f, gcctx->sc);
8516 }
8517 }
8518 }
8519 EX_CATCH
8520 {
8521 SOSStackErrorList *err = new SOSStackErrorList;
8522 err->pNext = NULL;
8523
8524 if (pFrame)
8525 {
8526 err->error.SourceType = SOS_StackSourceFrame;
8527 err->error.Source = dac_cast<PTR_Frame>(pFrame).GetAddr();
8528 }
8529 else
8530 {
8531 err->error.SourceType = SOS_StackSourceIP;
8532 err->error.Source = TO_CDADDR(dsc->pc);
8533 }
8534
8535 if (dsc->pWalker->mErrors == NULL)
8536 {
8537 dsc->pWalker->mErrors = err;
8538 }
8539 else
8540 {
8541 // This exception case should be non-existent. It only happens when there is either
8542 // a clr!Frame on the callstack which is not properly dac-ized, or when a call down
8543 // EnumGcRefs causes a data read exception. Since this is so rare, we don't worry
8544 // about making this code very efficient.
8545 SOSStackErrorList *curr = dsc->pWalker->mErrors;
8546 while (curr->pNext)
8547 curr = curr->pNext;
8548
8549 curr->pNext = err;
8550 }
8551 }
8552 EX_END_CATCH(SwallowAllExceptions)
8553
8554#if 0
8555 // todo
8556
8557 // If we're executing a LCG dynamic method then we must promote the associated resolver to ensure it
8558 // doesn't get collected and yank the method code out from under us).
8559
8560 // Be careful to only promote the reference -- we can also be called to relocate the reference and
8561 // that can lead to all sorts of problems since we could be racing for the relocation with the long
8562 // weak handle we recover the reference from. Promoting the reference is enough, the handle in the
8563 // reference will be relocated properly as long as we keep it alive till the end of the collection
8564 // as long as the reference is actually maintained by the long weak handle.
8565 if (pMD)
8566 {
8567 BOOL fMaybeCollectibleMethod = TRUE;
8568
8569 // If this is a frameless method then the jitmanager can answer the question of whether
8570 // or not this is LCG simply by looking at the heap where the code lives, however there
8571 // is also the prestub case where we need to explicitly look at the MD for stuff that isn't
8572 // ngen'd
8573 if (pCF->IsFrameless() && pMD->IsLCGMethod())
8574 {
8575 fMaybeCollectibleMethod = ExecutionManager::IsCollectibleMethod(pCF->GetMethodToken());
8576 }
8577
8578 if (fMaybeCollectibleMethod && pMD->IsLCGMethod())
8579 {
8580 PTR_Object obj = OBJECTREFToObject(pMD->AsDynamicMethodDesc()->GetLCGMethodResolver()->GetManagedResolver());
8581 dsc->pWalker->ReportObject(obj);
8582 }
8583 else
8584 {
8585 if (fMaybeCollectibleMethod)
8586 {
8587 PTR_Object obj = pMD->GetLoaderAllocator()->GetExposedObject();
8588 dsc->pWalker->ReportObject(obj);
8589 }
8590
8591 if (fReportGCReferences)
8592 {
8593 GenericParamContextType paramContextType = GENERIC_PARAM_CONTEXT_NONE;
8594
8595 if (pCF->IsFrameless())
8596 {
8597 // We need to grab the Context Type here because there are cases where the MethodDesc
8598 // is shared, and thus indicates there should be an instantion argument, but the JIT
8599 // was still allowed to optimize it away and we won't grab it below because we're not
8600 // reporting any references from this frame.
8601 paramContextType = pCF->GetCodeManager()->GetParamContextType(pCF->GetRegisterSet(), pCF->GetCodeInfo());
8602 }
8603 else
8604 {
8605 if (pMD->RequiresInstMethodDescArg())
8606 paramContextType = GENERIC_PARAM_CONTEXT_METHODDESC;
8607 else if (pMD->RequiresInstMethodTableArg())
8608 paramContextType = GENERIC_PARAM_CONTEXT_METHODTABLE;
8609 }
8610
8611 // Handle the case where the method is a static shared generic method and we need to keep the type of the generic parameters alive
8612 if (paramContextType == GENERIC_PARAM_CONTEXT_METHODDESC)
8613 {
8614 MethodDesc *pMDReal = dac_cast<PTR_MethodDesc>(pCF->GetParamTypeArg());
8615 _ASSERTE((pMDReal != NULL) || !pCF->IsFrameless());
8616 if (pMDReal != NULL)
8617 {
8618 PTR_Object obj = pMDReal->GetLoaderAllocator()->GetExposedObject();
8619 dsc->pWalker->ReportObject(obj);
8620 }
8621 }
8622 else if (paramContextType == GENERIC_PARAM_CONTEXT_METHODTABLE)
8623 {
8624 MethodTable *pMTReal = dac_cast<PTR_MethodTable>(pCF->GetParamTypeArg());
8625 _ASSERTE((pMTReal != NULL) || !pCF->IsFrameless());
8626 if (pMTReal != NULL)
8627 {
8628 PTR_Object obj = pMTReal->GetLoaderAllocator()->GetExposedObject();
8629 dsc->pWalker->ReportObject(obj);
8630 }
8631 }
8632 }
8633 }
8634 }
8635#endif
8636
8637 return SWA_CONTINUE;
8638}
8639
8640
8641DacStackReferenceErrorEnum::DacStackReferenceErrorEnum(DacStackReferenceWalker *pEnum, SOSStackErrorList *pErrors)
8642 : mEnum(pEnum), mHead(pErrors), mCurr(pErrors)
8643{
8644 _ASSERTE(mEnum);
8645
8646 if (mHead != NULL)
8647 mEnum->AddRef();
8648}
8649
8650DacStackReferenceErrorEnum::~DacStackReferenceErrorEnum()
8651{
8652 if (mHead)
8653 mEnum->Release();
8654}
8655
8656HRESULT DacStackReferenceErrorEnum::Skip(unsigned int count)
8657{
8658 unsigned int i = 0;
8659 for (i = 0; i < count && mCurr; ++i)
8660 mCurr = mCurr->pNext;
8661
8662 return i < count ? S_FALSE : S_OK;
8663}
8664
8665HRESULT DacStackReferenceErrorEnum::Reset()
8666{
8667 mCurr = mHead;
8668
8669 return S_OK;
8670}
8671
8672HRESULT DacStackReferenceErrorEnum::GetCount(unsigned int *pCount)
8673{
8674 SOSStackErrorList *curr = mHead;
8675 unsigned int count = 0;
8676
8677 while (curr)
8678 {
8679 curr = curr->pNext;
8680 count++;
8681 }
8682
8683 *pCount = count;
8684 return S_OK;
8685}
8686
8687HRESULT DacStackReferenceErrorEnum::Next(unsigned int count, SOSStackRefError ref[], unsigned int *pFetched)
8688{
8689 if (pFetched == NULL || ref == NULL)
8690 return E_POINTER;
8691
8692 unsigned int i;
8693 for (i = 0; i < count && mCurr; ++i, mCurr = mCurr->pNext)
8694 ref[i] = mCurr->error;
8695
8696 *pFetched = i;
8697 return i < count ? S_FALSE : S_OK;
8698}
8699