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 | |
6 | #include "common.h" |
7 | |
8 | /*******************************************************************/ |
9 | /* The following routines used to exist in all builds so they could called from the |
10 | * debugger before we had strike. |
11 | * Now most of them are only included in debug builds for diagnostics purposes. |
12 | */ |
13 | /*******************************************************************/ |
14 | |
15 | #include "stdlib.h" |
16 | |
17 | BOOL isMemoryReadable(const TADDR start, unsigned len) |
18 | { |
19 | CONTRACTL |
20 | { |
21 | NOTHROW; |
22 | GC_NOTRIGGER; |
23 | SO_TOLERANT; |
24 | } |
25 | CONTRACTL_END; |
26 | |
27 | #if !defined(DACCESS_COMPILE) && defined(FEATURE_PAL) |
28 | |
29 | return PAL_ProbeMemory((PVOID)start, len, FALSE); |
30 | |
31 | #else // !DACCESS_COMPILE && FEATURE_PAL |
32 | |
33 | // |
34 | // To accomplish this in a no-throw way, we have to touch each and every page |
35 | // and see if it is in memory or not. |
36 | // |
37 | |
38 | // |
39 | // Touch the first and last bytes. |
40 | // |
41 | char buff; |
42 | |
43 | #ifdef DACCESS_COMPILE |
44 | if (DacReadAll(start, &buff, 1, false) != S_OK) |
45 | { |
46 | return 0; |
47 | } |
48 | #else |
49 | if (ReadProcessMemory(GetCurrentProcess(), (PVOID)start, &buff, 1, 0) == 0) |
50 | { |
51 | return 0; |
52 | } |
53 | #endif |
54 | |
55 | TADDR location; |
56 | |
57 | location = start + (len - 1); |
58 | |
59 | #ifdef DACCESS_COMPILE |
60 | if (DacReadAll(location, &buff, 1, false) != S_OK) |
61 | { |
62 | return 0; |
63 | } |
64 | #else |
65 | if (ReadProcessMemory(GetCurrentProcess(), (PVOID)location, |
66 | &buff, 1, 0) == 0) |
67 | { |
68 | return 0; |
69 | } |
70 | #endif |
71 | |
72 | // |
73 | // Now we have to loop thru each and every page in between and touch them. |
74 | // |
75 | location = start; |
76 | while (len > GetOsPageSize()) |
77 | { |
78 | location += GetOsPageSize(); |
79 | len -= GetOsPageSize(); |
80 | |
81 | #ifdef DACCESS_COMPILE |
82 | if (DacReadAll(location, &buff, 1, false) != S_OK) |
83 | { |
84 | return 0; |
85 | } |
86 | #else |
87 | if (ReadProcessMemory(GetCurrentProcess(), (PVOID)location, |
88 | &buff, 1, 0) == 0) |
89 | { |
90 | return 0; |
91 | } |
92 | #endif |
93 | } |
94 | |
95 | return 1; |
96 | #endif // !DACCESS_COMPILE && FEATURE_PAL |
97 | } |
98 | |
99 | |
100 | /*******************************************************************/ |
101 | /* check to see if 'retAddr' is a valid return address (it points to |
102 | someplace that has a 'call' right before it), If possible it is |
103 | it returns the address that was called in whereCalled */ |
104 | |
105 | bool isRetAddr(TADDR retAddr, TADDR* whereCalled) |
106 | { |
107 | CONTRACTL |
108 | { |
109 | NOTHROW; |
110 | GC_NOTRIGGER; |
111 | SO_NOT_MAINLINE; |
112 | } |
113 | CONTRACTL_END; |
114 | |
115 | // don't waste time values clearly out of range |
116 | if (retAddr < (TADDR)BOT_MEMORY || retAddr > (TADDR)TOP_MEMORY) |
117 | { |
118 | return false; |
119 | } |
120 | |
121 | PTR_BYTE spot = PTR_BYTE(retAddr); |
122 | if (!isMemoryReadable(dac_cast<TADDR>(spot) - 7, 7)) |
123 | { |
124 | return(false); |
125 | } |
126 | |
127 | // Note this is possible to be spoofed, but pretty unlikely |
128 | *whereCalled = 0; |
129 | // call XXXXXXXX |
130 | if (spot[-5] == 0xE8) |
131 | { |
132 | *whereCalled = *(PTR_DWORD(retAddr - 4)) + retAddr; |
133 | return(true); |
134 | } |
135 | |
136 | // call [XXXXXXXX] |
137 | if (spot[-6] == 0xFF && (spot[-5] == 025)) |
138 | { |
139 | if (isMemoryReadable(*(PTR_TADDR(retAddr - 4)), 4)) |
140 | { |
141 | *whereCalled = *(PTR_TADDR(*(PTR_TADDR(retAddr - 4)))); |
142 | return(true); |
143 | } |
144 | } |
145 | |
146 | // call [REG+XX] |
147 | if (spot[-3] == 0xFF && (spot[-2] & ~7) == 0120 && (spot[-2] & 7) != 4) |
148 | { |
149 | return(true); |
150 | } |
151 | |
152 | if (spot[-4] == 0xFF && spot[-3] == 0124) // call [ESP+XX] |
153 | { |
154 | return(true); |
155 | } |
156 | |
157 | // call [REG+XXXX] |
158 | if (spot[-6] == 0xFF && (spot[-5] & ~7) == 0220 && (spot[-5] & 7) != 4) |
159 | { |
160 | return(true); |
161 | } |
162 | |
163 | if (spot[-7] == 0xFF && spot[-6] == 0224) // call [ESP+XXXX] |
164 | { |
165 | return(true); |
166 | } |
167 | |
168 | // call [REG] |
169 | if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0020 && (spot[-1] & 7) != 4 && (spot[-1] & 7) != 5) |
170 | { |
171 | return(true); |
172 | } |
173 | |
174 | // call REG |
175 | if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0320 && (spot[-1] & 7) != 4) |
176 | { |
177 | return(true); |
178 | } |
179 | |
180 | // There are other cases, but I don't believe they are used. |
181 | return(false); |
182 | } |
183 | |
184 | /* |
185 | * The remaining methods are included in debug builds only |
186 | */ |
187 | #ifdef _DEBUG |
188 | |
189 | #ifndef DACCESS_COMPILE |
190 | void *DumpEnvironmentBlock(void) |
191 | { |
192 | CONTRACTL |
193 | { |
194 | NOTHROW; |
195 | GC_NOTRIGGER; |
196 | } |
197 | CONTRACTL_END; |
198 | |
199 | LPTSTR lpszVariable; |
200 | lpszVariable = (LPTSTR)WszGetEnvironmentStrings(); |
201 | |
202 | while (*lpszVariable) |
203 | { |
204 | fprintf(stderr, "%c" , *lpszVariable++); |
205 | } |
206 | |
207 | fprintf(stderr, "\n" ); |
208 | |
209 | return WszGetEnvironmentStrings(); |
210 | } |
211 | |
212 | #if defined(_TARGET_X86_) && !defined(FEATURE_PAL) |
213 | /*******************************************************************/ |
214 | // Dump the SEH chain to stderr |
215 | void PrintSEHChain(void) |
216 | { |
217 | CONTRACTL |
218 | { |
219 | NOTHROW; |
220 | GC_NOTRIGGER; |
221 | } |
222 | CONTRACTL_END; |
223 | |
224 | EXCEPTION_REGISTRATION_RECORD* pEHR = GetCurrentSEHRecord(); |
225 | |
226 | while (pEHR != NULL && pEHR != EXCEPTION_CHAIN_END) |
227 | { |
228 | fprintf(stderr, "pEHR:0x%x Handler:0x%x\n" , (size_t)pEHR, (size_t)pEHR->Handler); |
229 | pEHR = pEHR->Next; |
230 | } |
231 | } |
232 | #endif // _TARGET_X86_ |
233 | |
234 | /*******************************************************************/ |
235 | MethodDesc* IP2MD(ULONG_PTR IP) |
236 | { |
237 | CONTRACTL |
238 | { |
239 | NOTHROW; |
240 | GC_NOTRIGGER; |
241 | } |
242 | CONTRACTL_END; |
243 | |
244 | return ExecutionManager::GetCodeMethodDesc((PCODE)IP); |
245 | } |
246 | |
247 | /*******************************************************************/ |
248 | /* if addr is a valid method table, return a pointer to it */ |
249 | MethodTable* AsMethodTable(size_t addr) |
250 | { |
251 | CONTRACTL |
252 | { |
253 | NOTHROW; |
254 | GC_NOTRIGGER; |
255 | DEBUG_ONLY; |
256 | } |
257 | CONTRACTL_END; |
258 | |
259 | MethodTable* pValidMT = NULL; |
260 | |
261 | EX_TRY |
262 | { |
263 | MethodTable* pMT = (MethodTable*) addr; |
264 | |
265 | if (isMemoryReadable((TADDR)pMT, sizeof(MethodTable))) |
266 | { |
267 | EEClass* cls = pMT->GetClass_NoLogging(); |
268 | |
269 | if (isMemoryReadable((TADDR)cls, sizeof(EEClass)) && |
270 | (cls->GetMethodTable() == pMT)) |
271 | { |
272 | pValidMT = pMT; |
273 | } |
274 | } |
275 | } |
276 | EX_CATCH |
277 | { |
278 | } |
279 | EX_END_CATCH(SwallowAllExceptions) |
280 | |
281 | return(pValidMT); |
282 | } |
283 | |
284 | /*******************************************************************/ |
285 | /* if addr is a valid method table, return a pointer to it */ |
286 | MethodDesc* AsMethodDesc(size_t addr) |
287 | { |
288 | CONTRACTL |
289 | { |
290 | NOTHROW; |
291 | GC_NOTRIGGER; |
292 | DEBUG_ONLY; |
293 | } |
294 | CONTRACTL_END; |
295 | |
296 | if (!IS_ALIGNED(addr, sizeof(void*))) |
297 | return(0); |
298 | |
299 | MethodDesc* pValidMD = NULL; |
300 | |
301 | // We try to avoid the most AVs by explicitit calls to isMemoryReadable below, but rare cases can still get through |
302 | // if we are unlucky. |
303 | AVInRuntimeImplOkayHolder AVOkay; |
304 | |
305 | EX_TRY |
306 | { |
307 | MethodDesc* pMD = (MethodDesc*) addr; |
308 | |
309 | if (isMemoryReadable((TADDR)pMD, sizeof(MethodDesc))) |
310 | { |
311 | MethodDescChunk *chunk = pMD->GetMethodDescChunk(); |
312 | |
313 | if (isMemoryReadable((TADDR)chunk, sizeof(MethodDescChunk))) |
314 | { |
315 | RelativeFixupPointer<PTR_MethodTable> * ppMT = chunk->GetMethodTablePtr(); |
316 | |
317 | // The MethodTable is stored as a RelativeFixupPointer which does an |
318 | // extra indirection if the address is tagged (the low bit is set). |
319 | // That could AV if we don't check it first. |
320 | |
321 | if (!ppMT->IsTagged((TADDR)ppMT) || isMemoryReadable((TADDR)ppMT->GetValuePtr(), sizeof(MethodTable*))) |
322 | { |
323 | if (AsMethodTable((size_t)RelativeFixupPointer<PTR_MethodTable>::GetValueAtPtr((TADDR)ppMT)) != 0) |
324 | { |
325 | pValidMD = pMD; |
326 | } |
327 | } |
328 | } |
329 | } |
330 | } |
331 | EX_CATCH |
332 | { |
333 | } |
334 | EX_END_CATCH(SwallowAllExceptions) |
335 | |
336 | |
337 | return(pValidMD); |
338 | } |
339 | |
340 | |
341 | // This function will return NULL if the buffer is not large enough. |
342 | /*******************************************************************/ |
343 | |
344 | wchar_t* formatMethodTable(MethodTable* pMT, |
345 | __out_z __inout_ecount(bufSize) wchar_t* buff, |
346 | DWORD bufSize) |
347 | { |
348 | CONTRACTL |
349 | { |
350 | NOTHROW; |
351 | GC_NOTRIGGER; |
352 | } |
353 | CONTRACTL_END; |
354 | |
355 | if(bufSize == 0) |
356 | { |
357 | return NULL; |
358 | } |
359 | |
360 | buff[ bufSize - 1] = W('\0'); |
361 | |
362 | DefineFullyQualifiedNameForClass(); |
363 | |
364 | LPCUTF8 clsName = GetFullyQualifiedNameForClass(pMT); |
365 | |
366 | if (clsName != 0) |
367 | { |
368 | if(_snwprintf_s(buff, bufSize - 1, _TRUNCATE, W("%S" ), clsName) < 0) |
369 | { |
370 | return NULL; |
371 | } |
372 | |
373 | buff[ bufSize - 1] = W('\0'); |
374 | |
375 | } |
376 | return(buff); |
377 | } |
378 | |
379 | /*******************************************************************/ |
380 | // This function will return NULL if the buffer is not large enough, otherwise it will |
381 | // return the buffer position for next write. |
382 | /*******************************************************************/ |
383 | |
384 | wchar_t* formatMethodDesc(MethodDesc* pMD, |
385 | __out_z __inout_ecount(bufSize) wchar_t* buff, |
386 | DWORD bufSize) |
387 | { |
388 | CONTRACTL |
389 | { |
390 | NOTHROW; |
391 | GC_NOTRIGGER; |
392 | } |
393 | CONTRACTL_END; |
394 | |
395 | if(bufSize == 0) |
396 | { |
397 | return NULL; |
398 | } |
399 | |
400 | buff = formatMethodTable(pMD->GetMethodTable(), buff, bufSize); |
401 | if(buff == NULL) |
402 | { |
403 | return NULL; |
404 | } |
405 | |
406 | buff[bufSize - 1] = W('\0'); // this will guarantee the buffer is also NULL-terminated |
407 | if(_snwprintf_s( &buff[wcslen(buff)] , bufSize - wcslen(buff) - 1, _TRUNCATE, W("::%S" ), pMD->GetName()) < 0) |
408 | { |
409 | return NULL; |
410 | } |
411 | |
412 | #ifdef _DEBUG |
413 | if (pMD->m_pszDebugMethodSignature) |
414 | { |
415 | if(_snwprintf_s(&buff[wcslen(buff)], |
416 | bufSize - wcslen(buff) - 1, |
417 | _TRUNCATE, |
418 | W(" %S" ), |
419 | pMD->m_pszDebugMethodSignature) < 0) |
420 | { |
421 | return NULL; |
422 | } |
423 | |
424 | } |
425 | #endif |
426 | |
427 | if(_snwprintf_s(&buff[wcslen(buff)], bufSize - wcslen(buff) - 1, _TRUNCATE, W("(%x)" ), (size_t)pMD) < 0) |
428 | { |
429 | return NULL; |
430 | } |
431 | |
432 | return(buff); |
433 | } |
434 | |
435 | |
436 | |
437 | |
438 | /*******************************************************************/ |
439 | /* dump the stack, pretty printing IL methods if possible. This |
440 | routine is very robust. It will never cause an access violation |
441 | and it always find return addresses if they are on the stack |
442 | (it may find some spurious ones however). */ |
443 | |
444 | int dumpStack(BYTE* topOfStack, unsigned len) |
445 | { |
446 | CONTRACTL |
447 | { |
448 | THROWS; |
449 | GC_NOTRIGGER; |
450 | } |
451 | CONTRACTL_END; |
452 | |
453 | size_t* top = (size_t*) topOfStack; |
454 | size_t* end = (size_t*) &topOfStack[len]; |
455 | |
456 | size_t* ptr = (size_t*) (((size_t) top) & ~3); // make certain dword aligned. |
457 | TADDR whereCalled; |
458 | |
459 | WszOutputDebugString(W("***************************************************\n" )); |
460 | |
461 | CQuickBytes qb; |
462 | |
463 | int nLen = MAX_CLASSNAME_LENGTH * 4 + 400; // this should be enough |
464 | |
465 | wchar_t *buff = (wchar_t *) qb.AllocThrows(nLen * sizeof(wchar_t)); |
466 | |
467 | while (ptr < end) |
468 | { |
469 | buff[nLen - 1] = W('\0'); |
470 | |
471 | wchar_t* buffPtr = buff; |
472 | |
473 | // stop if we hit unmapped pages |
474 | if (!isMemoryReadable((TADDR)ptr, sizeof(TADDR))) |
475 | { |
476 | break; |
477 | } |
478 | |
479 | if (isRetAddr((TADDR)*ptr, &whereCalled)) |
480 | { |
481 | if (_snwprintf_s(buffPtr, buff+NumItems(buff)-buffPtr-1, _TRUNCATE, W("STK[%08X] = %08X " ), (size_t)ptr, *ptr) <0) |
482 | { |
483 | return(0); |
484 | } |
485 | |
486 | buffPtr += wcslen(buffPtr); |
487 | |
488 | const wchar_t* kind = W("RETADDR " ); |
489 | |
490 | // Is this a stub (is the return address a MethodDesc? |
491 | MethodDesc* ftn = AsMethodDesc(*ptr); |
492 | |
493 | if (ftn != 0) |
494 | { |
495 | |
496 | kind = W(" MD PARAM" ); |
497 | |
498 | // If another true return address is not directly before it, it is just |
499 | // a methodDesc param. |
500 | TADDR prevRetAddr = ptr[1]; |
501 | |
502 | if (isRetAddr(prevRetAddr, &whereCalled) && AsMethodDesc(prevRetAddr) == 0) |
503 | { |
504 | kind = W("STUBCALL" ); |
505 | } |
506 | else |
507 | { |
508 | // Is it the magic sequence used by CallDescr? |
509 | if (isMemoryReadable(prevRetAddr - sizeof(short), |
510 | sizeof(short)) && |
511 | ((short*) prevRetAddr)[-1] == 0x5A59) // Pop ECX POP EDX |
512 | { |
513 | kind = W("STUBCALL" ); |
514 | } |
515 | |
516 | } |
517 | |
518 | } |
519 | else // Is it some other code the EE knows about? |
520 | { |
521 | ftn = ExecutionManager::GetCodeMethodDesc((PCODE)(*ptr)); |
522 | } |
523 | |
524 | if(_snwprintf_s(buffPtr, buff+ nLen -buffPtr-1, _TRUNCATE, W("%s " ), kind) < 0) |
525 | { |
526 | return(0); |
527 | } |
528 | |
529 | buffPtr += wcslen(buffPtr); |
530 | |
531 | if (ftn != 0) |
532 | { |
533 | // buffer is not large enough |
534 | if( formatMethodDesc(ftn, buffPtr, static_cast<DWORD>(buff+ nLen -buffPtr-1)) == NULL) |
535 | { |
536 | return(0); |
537 | } |
538 | |
539 | buffPtr += wcslen(buffPtr); |
540 | } |
541 | else |
542 | { |
543 | wcsncpy_s(buffPtr, nLen - (buffPtr - buff), W("<UNKNOWN FTN>" ), _TRUNCATE); |
544 | buffPtr += wcslen(buffPtr); |
545 | } |
546 | |
547 | if (whereCalled != 0) |
548 | { |
549 | if(_snwprintf_s(buffPtr, buff+ nLen -buffPtr-1, _TRUNCATE, W(" Caller called Entry %X" ), whereCalled) <0) |
550 | { |
551 | return(0); |
552 | } |
553 | |
554 | buffPtr += wcslen(buffPtr); |
555 | } |
556 | |
557 | wcsncpy_s(buffPtr, nLen - (buffPtr - buff), W("\n" ), _TRUNCATE); |
558 | buffPtr += wcslen(buffPtr); |
559 | WszOutputDebugString(buff); |
560 | } |
561 | |
562 | MethodTable* pMT = AsMethodTable(*ptr); |
563 | if (pMT != 0) |
564 | { |
565 | buffPtr = buff; |
566 | if( _snwprintf_s(buffPtr, buff+ nLen -buffPtr-1, _TRUNCATE, W("STK[%08X] = %08X MT PARAM " ), (size_t)ptr, *ptr ) <0) |
567 | { |
568 | return(0); |
569 | } |
570 | |
571 | buffPtr += wcslen(buffPtr); |
572 | |
573 | if( formatMethodTable(pMT, buffPtr, static_cast<DWORD>(buff+ nLen -buffPtr-1)) == NULL) |
574 | { |
575 | return(0); |
576 | } |
577 | |
578 | buffPtr += wcslen(buffPtr); |
579 | |
580 | wcsncpy_s(buffPtr, nLen - (buffPtr - buff), W("\n" ), _TRUNCATE); |
581 | WszOutputDebugString(buff); |
582 | |
583 | } |
584 | |
585 | ptr++; |
586 | |
587 | } // while |
588 | |
589 | return(0); |
590 | } |
591 | |
592 | /*******************************************************************/ |
593 | /* dump the stack from the current ESP. Stop when we reach a 64K |
594 | boundary */ |
595 | int DumpCurrentStack() |
596 | { |
597 | CONTRACTL |
598 | { |
599 | THROWS; |
600 | GC_NOTRIGGER; |
601 | } |
602 | CONTRACTL_END; |
603 | |
604 | #ifdef _TARGET_X86_ |
605 | BYTE* top = (BYTE *)GetCurrentSP(); |
606 | |
607 | // go back at most 64K, it will stop if we go off the |
608 | // top to unmapped memory |
609 | return(dumpStack(top, 0xFFFF)); |
610 | #else |
611 | _ASSERTE(!"@NYI - DumpCurrentStack(DebugHelp.cpp)" ); |
612 | return 0; |
613 | #endif // _TARGET_X86_ |
614 | } |
615 | |
616 | /*******************************************************************/ |
617 | WCHAR* StringVal(STRINGREF objref) |
618 | { |
619 | CONTRACTL |
620 | { |
621 | THROWS; |
622 | GC_NOTRIGGER; |
623 | } |
624 | CONTRACTL_END; |
625 | |
626 | return(objref->GetBuffer()); |
627 | } |
628 | |
629 | LPCUTF8 NameForMethodTable(UINT_PTR pMT) |
630 | { |
631 | CONTRACTL |
632 | { |
633 | NOTHROW; |
634 | GC_NOTRIGGER; |
635 | } |
636 | CONTRACTL_END; |
637 | |
638 | DefineFullyQualifiedNameForClass(); |
639 | LPCUTF8 clsName = GetFullyQualifiedNameForClass(((MethodTable*)pMT)); |
640 | // Note we're returning local stack space - this should be OK for using in the debugger though |
641 | return clsName; |
642 | } |
643 | |
644 | LPCUTF8 ClassNameForObject(UINT_PTR obj) |
645 | { |
646 | CONTRACTL |
647 | { |
648 | NOTHROW; |
649 | GC_NOTRIGGER; |
650 | } |
651 | CONTRACTL_END; |
652 | |
653 | return(NameForMethodTable((UINT_PTR)(((Object*)obj)->GetMethodTable()))); |
654 | } |
655 | |
656 | LPCUTF8 ClassNameForOBJECTREF(OBJECTREF obj) |
657 | { |
658 | CONTRACTL |
659 | { |
660 | NOTHROW; |
661 | GC_NOTRIGGER; |
662 | } |
663 | CONTRACTL_END; |
664 | |
665 | return(ClassNameForObject((UINT_PTR)(OBJECTREFToObject(obj)))); |
666 | } |
667 | |
668 | LPCUTF8 NameForMethodDesc(UINT_PTR pMD) |
669 | { |
670 | CONTRACTL |
671 | { |
672 | NOTHROW; |
673 | GC_NOTRIGGER; |
674 | } |
675 | CONTRACTL_END; |
676 | |
677 | return(((MethodDesc*)pMD)->GetName()); |
678 | } |
679 | |
680 | LPCUTF8 ClassNameForMethodDesc(UINT_PTR pMD) |
681 | { |
682 | CONTRACTL |
683 | { |
684 | NOTHROW; |
685 | GC_NOTRIGGER; |
686 | } |
687 | CONTRACTL_END; |
688 | |
689 | DefineFullyQualifiedNameForClass (); |
690 | return GetFullyQualifiedNameForClass(((MethodDesc*)pMD)->GetMethodTable()); |
691 | } |
692 | |
693 | PCCOR_SIGNATURE RawSigForMethodDesc(MethodDesc* pMD) |
694 | { |
695 | CONTRACTL |
696 | { |
697 | NOTHROW; |
698 | GC_NOTRIGGER; |
699 | } |
700 | CONTRACTL_END; |
701 | |
702 | return(pMD->GetSig()); |
703 | } |
704 | |
705 | Thread * CurrentThreadInfo () |
706 | { |
707 | CONTRACTL |
708 | { |
709 | NOTHROW; |
710 | GC_NOTRIGGER; |
711 | } |
712 | CONTRACTL_END; |
713 | |
714 | return GetThread (); |
715 | } |
716 | |
717 | AppDomain *GetAppDomainForObject(UINT_PTR obj) |
718 | { |
719 | CONTRACTL |
720 | { |
721 | NOTHROW; |
722 | GC_NOTRIGGER; |
723 | } |
724 | CONTRACTL_END; |
725 | |
726 | return ((Object*)obj)->GetAppDomain(); |
727 | } |
728 | |
729 | ADIndex GetAppDomainIndexForObject(UINT_PTR obj) |
730 | { |
731 | CONTRACTL |
732 | { |
733 | NOTHROW; |
734 | GC_NOTRIGGER; |
735 | } |
736 | CONTRACTL_END; |
737 | |
738 | return ((Object*)obj)->GetHeader()->GetAppDomainIndex(); |
739 | } |
740 | |
741 | AppDomain *GetAppDomainForObjectHeader(UINT_PTR hdr) |
742 | { |
743 | CONTRACTL |
744 | { |
745 | NOTHROW; |
746 | GC_NOTRIGGER; |
747 | } |
748 | CONTRACTL_END; |
749 | |
750 | ADIndex indx = ((ObjHeader*)hdr)->GetAppDomainIndex(); |
751 | if (!indx.m_dwIndex) |
752 | { |
753 | return NULL; |
754 | } |
755 | |
756 | return SystemDomain::GetAppDomainAtIndex(indx); |
757 | } |
758 | |
759 | ADIndex GetAppDomainIndexForObjectHeader(UINT_PTR hdr) |
760 | { |
761 | CONTRACTL |
762 | { |
763 | NOTHROW; |
764 | GC_NOTRIGGER; |
765 | } |
766 | CONTRACTL_END; |
767 | |
768 | return ((ObjHeader*)hdr)->GetAppDomainIndex(); |
769 | } |
770 | |
771 | SyncBlock *GetSyncBlockForObject(UINT_PTR obj) |
772 | { |
773 | CONTRACTL |
774 | { |
775 | NOTHROW; |
776 | GC_NOTRIGGER; |
777 | } |
778 | CONTRACTL_END; |
779 | |
780 | return ((Object*)obj)->GetHeader()->PassiveGetSyncBlock(); |
781 | } |
782 | |
783 | /*******************************************************************/ |
784 | void PrintMethodTable(UINT_PTR pMT) |
785 | { |
786 | CONTRACTL |
787 | { |
788 | NOTHROW; |
789 | GC_TRIGGERS; |
790 | } |
791 | CONTRACTL_END; |
792 | |
793 | MethodTable * p = (MethodTable *)pMT; |
794 | |
795 | DefineFullyQualifiedNameForClass(); |
796 | LPCUTF8 name = GetFullyQualifiedNameForClass(p); |
797 | p->DebugDumpVtable(name, true); |
798 | p->DebugDumpFieldLayout(name, true); |
799 | p->DebugDumpGCDesc(name, true); |
800 | } |
801 | |
802 | void PrintTableForMethodDesc(UINT_PTR pMD) |
803 | { |
804 | CONTRACTL |
805 | { |
806 | NOTHROW; |
807 | GC_TRIGGERS; |
808 | } |
809 | CONTRACTL_END; |
810 | |
811 | PrintMethodTable((UINT_PTR) ((MethodDesc *)pMD)->GetMethodTable() ); |
812 | } |
813 | |
814 | void PrintException(OBJECTREF pObjectRef) |
815 | { |
816 | CONTRACTL |
817 | { |
818 | THROWS; |
819 | GC_NOTRIGGER; |
820 | } |
821 | CONTRACTL_END; |
822 | |
823 | |
824 | if(pObjectRef == NULL) |
825 | { |
826 | return; |
827 | } |
828 | |
829 | GCPROTECT_BEGIN(pObjectRef); |
830 | |
831 | if (!IsException(pObjectRef->GetMethodTable())) |
832 | { |
833 | printf("Specified object is not an exception object.\n" ); |
834 | } |
835 | else |
836 | { |
837 | MethodDescCallSite internalToString(METHOD__EXCEPTION__INTERNAL_TO_STRING, &pObjectRef); |
838 | |
839 | ARG_SLOT arg[1] = { |
840 | ObjToArgSlot(pObjectRef) |
841 | }; |
842 | |
843 | STRINGREF str = internalToString.Call_RetSTRINGREF(arg); |
844 | |
845 | if(str->GetBuffer() != NULL) |
846 | { |
847 | WszOutputDebugString(str->GetBuffer()); |
848 | } |
849 | } |
850 | |
851 | GCPROTECT_END(); |
852 | } |
853 | |
854 | void PrintException(UINT_PTR pObject) |
855 | { |
856 | CONTRACTL |
857 | { |
858 | NOTHROW; |
859 | GC_NOTRIGGER; |
860 | } |
861 | CONTRACTL_END; |
862 | |
863 | OBJECTREF pObjectRef = NULL; |
864 | GCPROTECT_BEGIN(pObjectRef); |
865 | GCPROTECT_END(); |
866 | } |
867 | |
868 | /*******************************************************************/ |
869 | /* sends a current stack trace to the debug window */ |
870 | |
871 | const char* FormatSig(MethodDesc* pMD, AppDomain *pDomain, AllocMemTracker *pamTracker); |
872 | |
873 | struct PrintCallbackData { |
874 | BOOL toStdout; |
875 | BOOL withAppDomain; |
876 | #ifdef _DEBUG |
877 | BOOL toLOG; |
878 | #endif |
879 | }; |
880 | |
881 | StackWalkAction PrintStackTraceCallback(CrawlFrame* pCF, VOID* pData) |
882 | { |
883 | CONTRACTL |
884 | { |
885 | DISABLED(NOTHROW); |
886 | DISABLED(GC_TRIGGERS); |
887 | } |
888 | CONTRACTL_END; |
889 | |
890 | CONTRACT_VIOLATION(ThrowsViolation); |
891 | |
892 | MethodDesc* pMD = pCF->GetFunction(); |
893 | const int nLen = 2048 - 1; // keep one character for "\n" |
894 | wchar_t *buff = (wchar_t*)alloca((nLen + 1) * sizeof(wchar_t)); |
895 | buff[0] = 0; |
896 | buff[nLen-1] = W('\0'); // make sure the buffer is always NULL-terminated |
897 | |
898 | PrintCallbackData *pCBD = (PrintCallbackData *)pData; |
899 | |
900 | if (pMD != 0) |
901 | { |
902 | MethodTable * pMT = pMD->GetMethodTable(); |
903 | |
904 | if (pCBD->withAppDomain) |
905 | { |
906 | if(_snwprintf_s(&buff[wcslen(buff)], |
907 | nLen - wcslen(buff) - 1, |
908 | _TRUNCATE, |
909 | W("{[%3.3x] %s} " ), |
910 | pCF->GetAppDomain()->GetId().m_dwId, |
911 | pCF->GetAppDomain()->GetFriendlyName(FALSE)) < 0) |
912 | { |
913 | return SWA_CONTINUE; |
914 | } |
915 | } |
916 | |
917 | DefineFullyQualifiedNameForClass(); |
918 | |
919 | LPCUTF8 clsName = GetFullyQualifiedNameForClass(pMT); |
920 | |
921 | if (clsName != 0) |
922 | { |
923 | if(_snwprintf_s(&buff[wcslen(buff)], nLen - wcslen(buff) - 1, _TRUNCATE, W("%S::" ), clsName) < 0) |
924 | { |
925 | return SWA_CONTINUE; |
926 | } |
927 | } |
928 | |
929 | // This prematurely suppressrelease'd AmTracker will leak any memory allocated by FormatSig. |
930 | // But this routine is diagnostic aid, not customer-reachable so we won't bother to plug. |
931 | AllocMemTracker dummyAmTracker; |
932 | |
933 | int buffLen = _snwprintf_s(&buff[wcslen(buff)], |
934 | nLen - wcslen(buff) - 1, |
935 | _TRUNCATE, |
936 | W("%S %S " ), |
937 | pMD->GetName(), |
938 | FormatSig(pMD, pCF->GetAppDomain(), &dummyAmTracker)); |
939 | |
940 | dummyAmTracker.SuppressRelease(); |
941 | if (buffLen < 0 ) |
942 | { |
943 | return SWA_CONTINUE; |
944 | } |
945 | |
946 | |
947 | if (pCF->IsFrameless() && pCF->GetJitManager() != 0) { |
948 | |
949 | PREGDISPLAY regs = pCF->GetRegisterSet(); |
950 | |
951 | DWORD offset = pCF->GetRelOffset(); |
952 | |
953 | TADDR start = pCF->GetCodeInfo()->GetStartAddress(); |
954 | |
955 | if(_snwprintf_s(&buff[wcslen(buff)], |
956 | nLen - wcslen(buff) - 1, |
957 | _TRUNCATE, |
958 | W("JIT ESP:%X MethStart:%X EIP:%X(rel %X)" ), |
959 | (size_t)GetRegdisplaySP(regs), |
960 | (size_t)start, |
961 | (size_t)GetControlPC(regs), |
962 | offset) < 0) |
963 | { |
964 | return SWA_CONTINUE; |
965 | } |
966 | |
967 | } |
968 | else |
969 | { |
970 | |
971 | if(_snwprintf_s(&buff[wcslen(buff)], nLen - wcslen(buff) - 1, _TRUNCATE, W("EE implemented" )) < 0) |
972 | { |
973 | return SWA_CONTINUE; |
974 | } |
975 | } |
976 | |
977 | } |
978 | else |
979 | { |
980 | Frame* frame = pCF->GetFrame(); |
981 | |
982 | if(_snwprintf_s(&buff[wcslen(buff)], |
983 | nLen - wcslen(buff) - 1, |
984 | _TRUNCATE, |
985 | W("EE Frame is" ) LFMT_ADDR, |
986 | (size_t)DBG_ADDR(frame)) < 0) |
987 | { |
988 | return SWA_CONTINUE; |
989 | } |
990 | } |
991 | |
992 | if (pCBD->toStdout) |
993 | { |
994 | wcscat_s(buff, nLen + 1, W("\n" )); |
995 | PrintToStdOutW(buff); |
996 | } |
997 | #ifdef _DEBUG |
998 | else if (pCBD->toLOG) |
999 | { |
1000 | MAKE_ANSIPTR_FROMWIDE(sbuff, buff); |
1001 | // For LogSpewAlways to work rightr the "\n" (newline) |
1002 | // must be in the fmt string not part of the args |
1003 | LogSpewAlways(" %s\n" , sbuff); |
1004 | } |
1005 | #endif |
1006 | else |
1007 | { |
1008 | wcscat_s(buff, nLen + 1, W("\n" )); |
1009 | WszOutputDebugString(buff); |
1010 | } |
1011 | |
1012 | return SWA_CONTINUE; |
1013 | } |
1014 | |
1015 | void PrintStackTrace() |
1016 | { |
1017 | CONTRACTL |
1018 | { |
1019 | DISABLED(NOTHROW); |
1020 | DISABLED(GC_TRIGGERS); |
1021 | } |
1022 | CONTRACTL_END; |
1023 | |
1024 | WszOutputDebugString(W("***************************************************\n" )); |
1025 | PrintCallbackData cbd = {0, 0}; |
1026 | GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); |
1027 | } |
1028 | |
1029 | void PrintStackTraceToStdout() |
1030 | { |
1031 | CONTRACTL |
1032 | { |
1033 | DISABLED(NOTHROW); |
1034 | DISABLED(GC_TRIGGERS); |
1035 | } |
1036 | CONTRACTL_END; |
1037 | |
1038 | PrintCallbackData cbd = {1, 0}; |
1039 | GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); |
1040 | } |
1041 | |
1042 | #ifdef _DEBUG |
1043 | void PrintStackTraceToLog() |
1044 | { |
1045 | CONTRACTL |
1046 | { |
1047 | DISABLED(NOTHROW); |
1048 | DISABLED(GC_TRIGGERS); |
1049 | } |
1050 | CONTRACTL_END; |
1051 | |
1052 | PrintCallbackData cbd = {0, 0, 1}; |
1053 | GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); |
1054 | } |
1055 | #endif |
1056 | |
1057 | void PrintStackTraceWithAD() |
1058 | { |
1059 | CONTRACTL |
1060 | { |
1061 | DISABLED(NOTHROW); |
1062 | DISABLED(GC_TRIGGERS); |
1063 | } |
1064 | CONTRACTL_END; |
1065 | |
1066 | WszOutputDebugString(W("***************************************************\n" )); |
1067 | PrintCallbackData cbd = {0, 1}; |
1068 | GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); |
1069 | } |
1070 | |
1071 | void PrintStackTraceWithADToStdout() |
1072 | { |
1073 | CONTRACTL |
1074 | { |
1075 | DISABLED(NOTHROW); |
1076 | DISABLED(GC_TRIGGERS); |
1077 | } |
1078 | CONTRACTL_END; |
1079 | |
1080 | PrintCallbackData cbd = {1, 1}; |
1081 | GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); |
1082 | } |
1083 | |
1084 | #ifdef _DEBUG |
1085 | void PrintStackTraceWithADToLog() |
1086 | { |
1087 | CONTRACTL |
1088 | { |
1089 | NOTHROW; |
1090 | GC_NOTRIGGER; |
1091 | } |
1092 | CONTRACTL_END; |
1093 | |
1094 | PrintCallbackData cbd = {0, 1, 1}; |
1095 | GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); |
1096 | } |
1097 | |
1098 | void PrintStackTraceWithADToLog(Thread *pThread) |
1099 | { |
1100 | CONTRACTL |
1101 | { |
1102 | DISABLED(NOTHROW); |
1103 | DISABLED(GC_TRIGGERS); |
1104 | } |
1105 | CONTRACTL_END; |
1106 | |
1107 | PrintCallbackData cbd = {0, 1, 1}; |
1108 | pThread->StackWalkFrames(PrintStackTraceCallback, &cbd, ALLOW_ASYNC_STACK_WALK, 0); |
1109 | } |
1110 | #endif |
1111 | |
1112 | /*******************************************************************/ |
1113 | // Get the system or current domain from the thread. |
1114 | BaseDomain* GetSystemDomain() |
1115 | { |
1116 | CONTRACTL |
1117 | { |
1118 | NOTHROW; |
1119 | GC_NOTRIGGER; |
1120 | } |
1121 | CONTRACTL_END; |
1122 | |
1123 | return SystemDomain::System(); |
1124 | } |
1125 | |
1126 | AppDomain* GetCurrentDomain() |
1127 | { |
1128 | CONTRACTL |
1129 | { |
1130 | NOTHROW; |
1131 | GC_NOTRIGGER; |
1132 | } |
1133 | CONTRACTL_END; |
1134 | |
1135 | return SystemDomain::GetCurrentDomain(); |
1136 | } |
1137 | |
1138 | void PrintDomainName(size_t ob) |
1139 | { |
1140 | CONTRACTL |
1141 | { |
1142 | THROWS; |
1143 | GC_NOTRIGGER; |
1144 | } |
1145 | CONTRACTL_END; |
1146 | |
1147 | AppDomain* dm = (AppDomain*) ob; |
1148 | LPCWSTR st = dm->GetFriendlyName(FALSE); |
1149 | |
1150 | if(st != NULL) |
1151 | { |
1152 | WszOutputDebugString(st); |
1153 | } |
1154 | else |
1155 | { |
1156 | WszOutputDebugString(W("<Domain with no Name>" )); |
1157 | } |
1158 | } |
1159 | |
1160 | #if defined(_TARGET_X86_) |
1161 | |
1162 | #include "gcdump.h" |
1163 | |
1164 | #include "../gcdump/i386/gcdumpx86.cpp" |
1165 | |
1166 | #include "../gcdump/gcdump.cpp" |
1167 | |
1168 | /*********************************************************************/ |
1169 | void printfToDbgOut(const char* fmt, ...) |
1170 | { |
1171 | CONTRACTL |
1172 | { |
1173 | NOTHROW; |
1174 | GC_NOTRIGGER; |
1175 | } |
1176 | CONTRACTL_END; |
1177 | |
1178 | va_list args; |
1179 | va_start(args, fmt); |
1180 | |
1181 | char buffer[4096]; |
1182 | _vsnprintf_s(buffer, COUNTOF(buffer), _TRUNCATE, fmt, args); |
1183 | |
1184 | va_end(args); |
1185 | OutputDebugStringA( buffer ); |
1186 | } |
1187 | |
1188 | void DumpGCInfo(MethodDesc* method) |
1189 | { |
1190 | CONTRACTL |
1191 | { |
1192 | NOTHROW; |
1193 | GC_NOTRIGGER; |
1194 | } |
1195 | CONTRACTL_END; |
1196 | |
1197 | PCODE methodStart = method->GetNativeCode(); |
1198 | |
1199 | if (methodStart == 0) |
1200 | { |
1201 | return; |
1202 | } |
1203 | |
1204 | EECodeInfo codeInfo(methodStart); |
1205 | _ASSERTE(codeInfo.GetRelOffset() == 0); |
1206 | |
1207 | ICodeManager* codeMan = codeInfo.GetCodeManager(); |
1208 | GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken(); |
1209 | |
1210 | unsigned methodSize = (unsigned)codeMan->GetFunctionSize(gcInfoToken); |
1211 | |
1212 | GCDump gcDump(gcInfoToken.Version); |
1213 | PTR_CBYTE gcInfo = PTR_CBYTE(gcInfoToken.Info); |
1214 | |
1215 | gcDump.gcPrintf = printfToDbgOut; |
1216 | |
1217 | InfoHdr header; |
1218 | |
1219 | printfToDbgOut ("Method info block:\n" ); |
1220 | gcInfo += gcDump.DumpInfoHdr(gcInfo, &header, &methodSize, 0); |
1221 | |
1222 | printfToDbgOut ("\n" ); |
1223 | printfToDbgOut ("Pointer table:\n" ); |
1224 | |
1225 | gcInfo += gcDump.DumpGCTable(gcInfo, header, methodSize, 0); |
1226 | } |
1227 | |
1228 | void DumpGCInfoMD(size_t method) |
1229 | { |
1230 | CONTRACTL |
1231 | { |
1232 | NOTHROW; |
1233 | GC_NOTRIGGER; |
1234 | } |
1235 | CONTRACTL_END; |
1236 | |
1237 | DumpGCInfo((MethodDesc*) method); |
1238 | } |
1239 | #endif |
1240 | |
1241 | |
1242 | #ifdef LOGGING |
1243 | void LogStackTrace() |
1244 | { |
1245 | WRAPPER_NO_CONTRACT; |
1246 | |
1247 | PrintCallbackData cbd = {0, 0, 1}; |
1248 | GetThread()->StackWalkFrames(PrintStackTraceCallback, &cbd,ALLOW_ASYNC_STACK_WALK, 0); |
1249 | } |
1250 | #endif |
1251 | |
1252 | #endif // #ifndef DACCESS_COMPILE |
1253 | #endif //_DEBUG |
1254 | |