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 | |
37 | CRITICAL_SECTION g_dacCritSec; |
38 | ClrDataAccess* g_dacImpl; |
39 | HINSTANCE g_thisModule; |
40 | |
41 | extern VOID STDMETHODCALLTYPE TLS_FreeMasterSlotIndex(); |
42 | |
43 | EXTERN_C BOOL WINAPI |
44 | DllMain(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 | |
96 | HINSTANCE |
97 | GetModuleInst(void) |
98 | { |
99 | return g_thisModule; |
100 | } |
101 | |
102 | HRESULT |
103 | ConvertUtf8(__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 | |
128 | HRESULT |
129 | AllocUtf8(__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 | |
172 | HRESULT |
173 | GetFullClassNameFromMetadata(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 | |
186 | HRESULT |
187 | GetFullMethodNameFromMetadata(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 | |
238 | HRESULT |
239 | SplitFullName(__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 | |
411 | int |
412 | CompareUtf8(__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 | |
429 | HRESULT |
430 | MetaEnum::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 | |
458 | void |
459 | MetaEnum::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 | |
480 | HRESULT |
481 | MetaEnum::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 | |
552 | HRESULT |
553 | MetaEnum::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 | |
596 | HRESULT |
597 | MetaEnum::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 | |
629 | HRESULT |
630 | MetaEnum::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 | |
676 | HRESULT |
677 | MetaEnum::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 | |
733 | SplitName::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 | |
743 | void |
744 | SplitName::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 | |
756 | void |
757 | SplitName::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 | |
772 | HRESULT |
773 | SplitName::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 | |
798 | FORCEINLINE |
799 | WCHAR* 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. |
818 | HRESULT 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 | |
835 | bool |
836 | SplitName::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 | |
870 | Retry: |
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 | |
947 | bool |
948 | SplitName::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 | |
979 | bool |
980 | SplitName::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 | |
1008 | HRESULT |
1009 | SplitName::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 | |
1038 | HRESULT |
1039 | SplitName::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 | |
1121 | HRESULT |
1122 | SplitName::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 | |
1136 | HRESULT |
1137 | SplitName::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 | |
1152 | HRESULT |
1153 | SplitName::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 | |
1248 | HRESULT |
1249 | SplitName::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 | |
1370 | HRESULT |
1371 | SplitName::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 | |
1433 | HRESULT |
1434 | SplitName::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 | |
1477 | HRESULT |
1478 | SplitName::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 | |
1492 | HRESULT |
1493 | SplitName::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 | |
1546 | DacInstanceManager::DacInstanceManager(void) |
1547 | : m_unusedBlock(NULL) |
1548 | { |
1549 | InitEmpty(); |
1550 | } |
1551 | |
1552 | DacInstanceManager::~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) |
1560 | DAC_INSTANCE* |
1561 | DacInstanceManager::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 |
1621 | DAC_INSTANCE* |
1622 | DacInstanceManager::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 | |
1651 | DAC_INSTANCE* |
1652 | DacInstanceManager::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 | |
1760 | void |
1761 | DacInstanceManager::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) |
1824 | DAC_INSTANCE* |
1825 | DacInstanceManager::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 |
1881 | DAC_INSTANCE* |
1882 | DacInstanceManager::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 | |
1896 | HRESULT |
1897 | DacInstanceManager::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) |
1920 | void |
1921 | DacInstanceManager::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 |
1955 | void |
1956 | DacInstanceManager::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. |
2003 | void DacInstanceManager::Flush(void) |
2004 | { |
2005 | Flush(true); |
2006 | } |
2007 | |
2008 | void 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) |
2070 | void |
2071 | DacInstanceManager::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 |
2099 | void |
2100 | DacInstanceManager::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 | // |
2133 | UINT |
2134 | DacInstanceManager::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 | // |
2208 | UINT |
2209 | DacInstanceManager::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 | |
2258 | DAC_INSTANCE_BLOCK* |
2259 | DacInstanceManager::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. |
2280 | void |
2281 | DacInstanceManager::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 | |
2317 | namespace 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 | |
2695 | using serialization::bin::StreamBuffState; |
2696 | using serialization::bin::IStreamBuff; |
2697 | using 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) |
2703 | typedef 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 | // |
2711 | class DacEENamesStreamable |
2712 | { |
2713 | private: |
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 | |
2723 | private: |
2724 | // signature value in the header in stream |
2725 | static const DWORD sig = 0x614e4545; // "EENa" - EE Name |
2726 | |
2727 | // header in stream |
2728 | struct |
2729 | { |
2730 | DWORD ; // 0x614e4545 == "EENa" |
2731 | DWORD ; // count of entries |
2732 | |
2733 | static const bool = true; |
2734 | }; |
2735 | |
2736 | public: |
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 | // |
2855 | class DacStreamManager |
2856 | { |
2857 | public: |
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 |
2870 | { |
2871 | DWORD ; // 0x6d727473 == "strm" |
2872 | DWORD ; // total size in bytes |
2873 | DWORD ; // number of streams (currently 1) |
2874 | |
2875 | static const bool = 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 | |
2975 | private: |
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 | |
3096 | private: |
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 | |
3114 | LONG ClrDataAccess::s_procInit; |
3115 | |
3116 | ClrDataAccess::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 | |
3208 | ClrDataAccess::~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 | |
3244 | STDMETHODIMP |
3245 | ClrDataAccess::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 | |
3296 | STDMETHODIMP_(ULONG) |
3297 | ClrDataAccess::AddRef(THIS) |
3298 | { |
3299 | return InterlockedIncrement(&m_refs); |
3300 | } |
3301 | |
3302 | STDMETHODIMP_(ULONG) |
3303 | ClrDataAccess::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 | |
3314 | HRESULT STDMETHODCALLTYPE |
3315 | ClrDataAccess::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 | |
3338 | HRESULT STDMETHODCALLTYPE |
3339 | ClrDataAccess::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 | |
3372 | HRESULT STDMETHODCALLTYPE |
3373 | ClrDataAccess::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 | |
3416 | HRESULT STDMETHODCALLTYPE |
3417 | ClrDataAccess::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 | |
3442 | HRESULT STDMETHODCALLTYPE |
3443 | ClrDataAccess::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 | |
3474 | HRESULT STDMETHODCALLTYPE |
3475 | ClrDataAccess::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 | |
3509 | HRESULT STDMETHODCALLTYPE |
3510 | ClrDataAccess::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 | |
3536 | HRESULT STDMETHODCALLTYPE |
3537 | ClrDataAccess::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 | |
3562 | HRESULT STDMETHODCALLTYPE |
3563 | ClrDataAccess::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 | |
3588 | HRESULT STDMETHODCALLTYPE |
3589 | ClrDataAccess::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 | |
3614 | HRESULT STDMETHODCALLTYPE |
3615 | ClrDataAccess::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 | |
3640 | HRESULT STDMETHODCALLTYPE |
3641 | ClrDataAccess::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 | |
3687 | HRESULT STDMETHODCALLTYPE |
3688 | ClrDataAccess::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 | |
3721 | HRESULT STDMETHODCALLTYPE |
3722 | ClrDataAccess::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 | |
3755 | HRESULT STDMETHODCALLTYPE |
3756 | ClrDataAccess::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 | |
3791 | HRESULT STDMETHODCALLTYPE |
3792 | ClrDataAccess::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 | |
3818 | HRESULT STDMETHODCALLTYPE |
3819 | ClrDataAccess::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 | |
3856 | HRESULT STDMETHODCALLTYPE |
3857 | ClrDataAccess::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 | |
3890 | HRESULT STDMETHODCALLTYPE |
3891 | ClrDataAccess::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 | |
3928 | HRESULT STDMETHODCALLTYPE |
3929 | ClrDataAccess::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 | |
3955 | HRESULT STDMETHODCALLTYPE |
3956 | ClrDataAccess::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 | |
3989 | HRESULT STDMETHODCALLTYPE |
3990 | ClrDataAccess::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 | |
4027 | HRESULT STDMETHODCALLTYPE |
4028 | ClrDataAccess::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 | |
4054 | HRESULT STDMETHODCALLTYPE |
4055 | ClrDataAccess::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 | |
4118 | HRESULT STDMETHODCALLTYPE |
4119 | ClrDataAccess::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 | |
4164 | HRESULT STDMETHODCALLTYPE |
4165 | ClrDataAccess::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 | |
4190 | HRESULT STDMETHODCALLTYPE |
4191 | ClrDataAccess::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 | |
4215 | HRESULT STDMETHODCALLTYPE |
4216 | ClrDataAccess::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 | |
4266 | HRESULT STDMETHODCALLTYPE |
4267 | ClrDataAccess::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 | |
4292 | HRESULT STDMETHODCALLTYPE |
4293 | ClrDataAccess::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 | |
4317 | HRESULT STDMETHODCALLTYPE |
4318 | ClrDataAccess::GetDataByAddress( |
4319 | /* [in] */ CLRDATA_ADDRESS address, |
4320 | /* [in] */ ULONG32 flags, |
4321 | /* [in] */ IXCLRDataAppDomain* appDomain, |
4322 | /* [in] */ IXCLRDataTask* tlsTask, |
4323 | /* [in] */ ULONG32 bufLen, |
4324 | /* [out] */ ULONG32 *nameLen, |
4325 | /* [size_is][out] */ __out_ecount_part_opt(bufLen, *nameLen) WCHAR nameBuf[ ], |
4326 | /* [out] */ IXCLRDataValue **value, |
4327 | /* [out] */ CLRDATA_ADDRESS *displacement) |
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 | |
4356 | HRESULT STDMETHODCALLTYPE |
4357 | ClrDataAccess::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 | |
4383 | HRESULT STDMETHODCALLTYPE |
4384 | ClrDataAccess::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**)¬ify2) != S_OK) |
4583 | { |
4584 | notify2 = NULL; |
4585 | } |
4586 | |
4587 | IXCLRDataExceptionNotification3* notify3; |
4588 | if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification3), |
4589 | (void**)¬ify3) != S_OK) |
4590 | { |
4591 | notify3 = NULL; |
4592 | } |
4593 | |
4594 | IXCLRDataExceptionNotification4* notify4; |
4595 | if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification4), |
4596 | (void**)¬ify4) != S_OK) |
4597 | { |
4598 | notify4 = NULL; |
4599 | } |
4600 | |
4601 | IXCLRDataExceptionNotification5* notify5; |
4602 | if (notify->QueryInterface(__uuidof(IXCLRDataExceptionNotification5), |
4603 | (void**)¬ify5) != 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 | |
4692 | HRESULT STDMETHODCALLTYPE |
4693 | ClrDataAccess::CreateMemoryValue( |
4694 | /* [in] */ IXCLRDataAppDomain* appDomain, |
4695 | /* [in] */ IXCLRDataTask* tlsTask, |
4696 | /* [in] */ IXCLRDataTypeInstance* type, |
4697 | /* [in] */ CLRDATA_ADDRESS addr, |
4698 | /* [out] */ IXCLRDataValue** value) |
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 | |
4747 | HRESULT STDMETHODCALLTYPE |
4748 | ClrDataAccess::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 | |
4774 | HRESULT STDMETHODCALLTYPE |
4775 | ClrDataAccess::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 | |
4829 | HRESULT STDMETHODCALLTYPE |
4830 | ClrDataAccess::GetTypeNotifications( |
4831 | /* [in] */ ULONG32 numTokens, |
4832 | /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[], |
4833 | /* [in] */ IXCLRDataModule* singleMod, |
4834 | /* [in, size_is(numTokens)] */ mdTypeDef tokens[], |
4835 | /* [out, size_is(numTokens)] */ ULONG32 flags[]) |
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 | |
4859 | HRESULT STDMETHODCALLTYPE |
4860 | ClrDataAccess::SetTypeNotifications( |
4861 | /* [in] */ ULONG32 numTokens, |
4862 | /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[], |
4863 | /* [in] */ IXCLRDataModule* singleMod, |
4864 | /* [in, size_is(numTokens)] */ mdTypeDef tokens[], |
4865 | /* [in, size_is(numTokens)] */ ULONG32 flags[], |
4866 | /* [in] */ ULONG32 singleFlags) |
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 | |
4890 | HRESULT STDMETHODCALLTYPE |
4891 | ClrDataAccess::GetCodeNotifications( |
4892 | /* [in] */ ULONG32 numTokens, |
4893 | /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[], |
4894 | /* [in] */ IXCLRDataModule* singleMod, |
4895 | /* [in, size_is(numTokens)] */ mdMethodDef tokens[], |
4896 | /* [out, size_is(numTokens)] */ ULONG32 flags[]) |
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 | |
4955 | HRESULT STDMETHODCALLTYPE |
4956 | ClrDataAccess::SetCodeNotifications( |
4957 | /* [in] */ ULONG32 numTokens, |
4958 | /* [in, size_is(numTokens)] */ IXCLRDataModule* mods[], |
4959 | /* [in] */ IXCLRDataModule* singleMod, |
4960 | /* [in, size_is(numTokens)] */ mdMethodDef tokens[], |
4961 | /* [in, size_is(numTokens)] */ ULONG32 flags[], |
4962 | /* [in] */ ULONG32 singleFlags) |
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 | |
5046 | Exit: ; |
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 | |
5061 | HRESULT |
5062 | ClrDataAccess::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 | |
5087 | HRESULT |
5088 | ClrDataAccess::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 | |
5121 | enum |
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 | |
5132 | union 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 | |
5143 | HRESULT |
5144 | ClrDataAccess::FollowStubStep( |
5145 | /* [in] */ Thread* thread, |
5146 | /* [in] */ ULONG32 inFlags, |
5147 | /* [in] */ TADDR inAddr, |
5148 | /* [in] */ union STUB_BUF* inBuffer, |
5149 | /* [out] */ TADDR* outAddr, |
5150 | /* [out] */ union STUB_BUF* outBuffer, |
5151 | /* [out] */ ULONG32* outFlags) |
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(®Disp, &localContext); |
5198 | if (!thread->GetFrame()-> |
5199 | TraceFrame(thread, |
5200 | TRUE, |
5201 | &trace, |
5202 | ®Disp)) |
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 | |
5314 | HRESULT STDMETHODCALLTYPE |
5315 | ClrDataAccess::FollowStub( |
5316 | /* [in] */ ULONG32 inFlags, |
5317 | /* [in] */ CLRDATA_ADDRESS inAddr, |
5318 | /* [in] */ CLRDATA_FOLLOW_STUB_BUFFER* _inBuffer, |
5319 | /* [out] */ CLRDATA_ADDRESS* outAddr, |
5320 | /* [out] */ CLRDATA_FOLLOW_STUB_BUFFER* _outBuffer, |
5321 | /* [out] */ ULONG32* outFlags) |
5322 | { |
5323 | return FollowStub2(NULL, inFlags, inAddr, _inBuffer, |
5324 | outAddr, _outBuffer, outFlags); |
5325 | } |
5326 | |
5327 | HRESULT STDMETHODCALLTYPE |
5328 | ClrDataAccess::FollowStub2( |
5329 | /* [in] */ IXCLRDataTask* task, |
5330 | /* [in] */ ULONG32 inFlags, |
5331 | /* [in] */ CLRDATA_ADDRESS _inAddr, |
5332 | /* [in] */ CLRDATA_FOLLOW_STUB_BUFFER* _inBuffer, |
5333 | /* [out] */ CLRDATA_ADDRESS* _outAddr, |
5334 | /* [out] */ CLRDATA_FOLLOW_STUB_BUFFER* _outBuffer, |
5335 | /* [out] */ ULONG32* outFlags) |
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 |
5418 | STDMETHODIMP |
5419 | ClrDataAccess::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 | |
5466 | STDMETHODIMP |
5467 | ClrDataAccess::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 | |
5516 | HRESULT |
5517 | ClrDataAccess::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 | |
5626 | Thread* |
5627 | ClrDataAccess::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 | |
5647 | HRESULT |
5648 | ClrDataAccess::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 | |
5673 | HRESULT |
5674 | ClrDataAccess::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 | |
5716 | PCSTR |
5717 | ClrDataAccess::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 | |
5767 | HRESULT |
5768 | ClrDataAccess::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 | |
5978 | NameFromMethodDesc: |
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 | |
6025 | HRESULT |
6026 | ClrDataAccess::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... |
6069 | BYTE* DebugInfoStoreNew(void * pData, size_t cBytes) |
6070 | { |
6071 | return new (nothrow) BYTE[cBytes]; |
6072 | } |
6073 | |
6074 | HRESULT |
6075 | ClrDataAccess::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 | |
6118 | HRESULT |
6119 | ClrDataAccess::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 |
6201 | MethodDesc * 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 | // |
6237 | bool 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 | // |
6368 | bool 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 | // |
6415 | bool 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 | |
6467 | JITNotification* |
6468 | ClrDataAccess::GetHostJitNotificationTable() |
6469 | { |
6470 | if (m_jitNotificationTable == NULL) |
6471 | { |
6472 | m_jitNotificationTable = |
6473 | JITNotifications::InitializeNotificationTable(1000); |
6474 | } |
6475 | |
6476 | return m_jitNotificationTable; |
6477 | } |
6478 | |
6479 | GcNotification* |
6480 | ClrDataAccess::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 |
6492 | ClrDataAccess::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 */ |
6577 | bool 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. |
6609 | bool 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 | |
6665 | void * |
6666 | ClrDataAccess::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 | |
6842 | ErrExit: |
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 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
6858 | IMDInternalImport* |
6859 | ClrDataAccess::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 | |
6992 | void 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 | // |
7014 | bool 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 | // |
7030 | HRESULT 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 | |
7160 | void 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 | |
7188 | bool 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 | |
7205 | void 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 | |
7222 | bool 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 | |
7250 | HRESULT |
7251 | ClrDataAccess::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 | |
7372 | Exit: |
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 | //---------------------------------------------------------------------------- |
7392 | BOOL 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 | //---------------------------------------------------------------------------- |
7424 | HRESULT 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 | //---------------------------------------------------------------------------- |
7462 | STDAPI 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__ |
7510 | STDAPI |
7511 | CLRDataCreateInstance(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 | //---------------------------------------------------------------------------- |
7550 | BOOL 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) |
7593 | typedef 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 | //---------------------------------------------------------------------------- |
7621 | STDAPI 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 | //---------------------------------------------------------------------------- |
7712 | STDAPI 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 | //---------------------------------------------------------------------------- |
7804 | STDAPI 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 | //---------------------------------------------------------------------------- |
7932 | STDAPI 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 | |
7963 | DacHandleWalker::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 | |
7971 | DacHandleWalker::~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 | |
7985 | HRESULT 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 | |
7998 | HRESULT 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 | |
8010 | HRESULT 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 | |
8020 | UINT32 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 | |
8035 | HRESULT 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 | |
8053 | bool 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)¶m, 0, |
8116 | &handleType, 1, |
8117 | mGenerationFilter, *g_gcDacGlobals->max_gen, 0); |
8118 | else |
8119 | HndEnumHandles(hTable, &handleType, 1, callback, (LPARAM)¶m, 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 | |
8136 | HRESULT DacHandleWalker::Skip(unsigned int celt) |
8137 | { |
8138 | return E_NOTIMPL; |
8139 | } |
8140 | |
8141 | HRESULT DacHandleWalker::Reset() |
8142 | { |
8143 | return E_NOTIMPL; |
8144 | } |
8145 | |
8146 | HRESULT DacHandleWalker::GetCount(unsigned int *pcelt) |
8147 | { |
8148 | return E_NOTIMPL; |
8149 | } |
8150 | |
8151 | |
8152 | void 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 | |
8195 | void CALLBACK DacHandleWalker::EnumCallbackSOS(PTR_UNCHECKED_OBJECTREF handle, uintptr_t *, 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 | |
8246 | DacStackReferenceWalker::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 | |
8264 | DacStackReferenceWalker::~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 | |
8276 | HRESULT DacStackReferenceWalker::Init() |
8277 | { |
8278 | if (!mThread) |
8279 | return E_INVALIDARG; |
8280 | return mHeap.Init(); |
8281 | } |
8282 | |
8283 | HRESULT STDMETHODCALLTYPE DacStackReferenceWalker::Skip(unsigned int count) |
8284 | { |
8285 | return E_NOTIMPL; |
8286 | } |
8287 | |
8288 | HRESULT STDMETHODCALLTYPE DacStackReferenceWalker::Reset() |
8289 | { |
8290 | return E_NOTIMPL; |
8291 | } |
8292 | |
8293 | HRESULT 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 | |
8316 | HRESULT 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 | |
8335 | HRESULT 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 | |
8355 | CLRDATA_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 | |
8368 | void 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 | |
8427 | void 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 | |
8466 | StackWalkAction 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 | |
8641 | DacStackReferenceErrorEnum::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 | |
8650 | DacStackReferenceErrorEnum::~DacStackReferenceErrorEnum() |
8651 | { |
8652 | if (mHead) |
8653 | mEnum->Release(); |
8654 | } |
8655 | |
8656 | HRESULT 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 | |
8665 | HRESULT DacStackReferenceErrorEnum::Reset() |
8666 | { |
8667 | mCurr = mHead; |
8668 | |
8669 | return S_OK; |
8670 | } |
8671 | |
8672 | HRESULT 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 | |
8687 | HRESULT 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 | |