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// STACKWALK.CPP
5
6
7
8#include "common.h"
9#include "frames.h"
10#include "threads.h"
11#include "stackwalk.h"
12#include "excep.h"
13#include "eetwain.h"
14#include "codeman.h"
15#include "eeconfig.h"
16#include "stackprobe.h"
17#include "dbginterface.h"
18#include "generics.h"
19#ifdef FEATURE_INTERPRETER
20#include "interpreter.h"
21#endif // FEATURE_INTERPRETER
22
23#ifdef WIN64EXCEPTIONS
24#define PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
25#endif
26
27CrawlFrame::CrawlFrame()
28{
29 LIMITED_METHOD_DAC_CONTRACT;
30 pCurGSCookie = NULL;
31 pFirstGSCookie = NULL;
32 isCachedMethod = FALSE;
33}
34
35Assembly* CrawlFrame::GetAssembly()
36{
37 WRAPPER_NO_CONTRACT;
38
39 Assembly *pAssembly = NULL;
40 Frame *pF = GetFrame();
41
42 if (pF != NULL)
43 pAssembly = pF->GetAssembly();
44
45 if (pAssembly == NULL && pFunc != NULL)
46 pAssembly = pFunc->GetModule()->GetAssembly();
47
48 return pAssembly;
49}
50
51#ifndef DACCESS_COMPILE
52OBJECTREF* CrawlFrame::GetAddrOfSecurityObject()
53{
54 CONTRACTL {
55 NOTHROW;
56 GC_NOTRIGGER;
57 } CONTRACTL_END;
58
59 if (isFrameless)
60 {
61 _ASSERTE(pFunc);
62
63#if defined(_TARGET_X86_)
64 if (isCachedMethod)
65 {
66 return pSecurityObject;
67 }
68 else
69#endif // _TARGET_X86_
70 {
71 return (static_cast <OBJECTREF*>(GetCodeManager()->GetAddrOfSecurityObject(this)));
72 }
73 }
74 else
75 {
76#ifdef FEATURE_INTERPRETER
77 // Check for an InterpreterFrame.
78 Frame* pFrm = GetFrame();
79 if (pFrm != NULL && pFrm->GetVTablePtr() == InterpreterFrame::GetMethodFrameVPtr())
80 {
81#ifdef DACCESS_COMPILE
82 // TBD: DACize the interpreter.
83 return NULL;
84#else
85 return dac_cast<PTR_InterpreterFrame>(pFrm)->GetInterpreter()->GetAddressOfSecurityObject();
86#endif
87 }
88 // Otherwise...
89#endif // FEATURE_INTERPRETER
90
91 /*ISSUE: Are there any other functions holding a security desc? */
92 if (pFunc && (pFunc->IsIL() || pFunc->IsNoMetadata()))
93 return dac_cast<PTR_FramedMethodFrame>
94 (pFrame)->GetAddrOfSecurityDesc();
95 }
96 return NULL;
97}
98#endif
99
100BOOL CrawlFrame::IsInCalleesFrames(LPVOID stackPointer)
101{
102 LIMITED_METHOD_CONTRACT;
103#ifdef FEATURE_INTERPRETER
104 Frame* pFrm = GetFrame();
105 if (pFrm != NULL && pFrm->GetVTablePtr() == InterpreterFrame::GetMethodFrameVPtr())
106 {
107#ifdef DACCESS_COMPILE
108 // TBD: DACize the interpreter.
109 return NULL;
110#else
111 return dac_cast<PTR_InterpreterFrame>(pFrm)->GetInterpreter()->IsInCalleesFrames(stackPointer);
112#endif
113 }
114 else if (pFunc != NULL)
115 {
116 return ::IsInCalleesFrames(GetRegisterSet(), stackPointer);
117 }
118 else
119 {
120 return FALSE;
121 }
122#else
123 return ::IsInCalleesFrames(GetRegisterSet(), stackPointer);
124#endif
125}
126
127#ifdef FEATURE_INTERPRETER
128MethodDesc* CrawlFrame::GetFunction()
129{
130 LIMITED_METHOD_DAC_CONTRACT;
131 STATIC_CONTRACT_SO_TOLERANT;
132 if (pFunc != NULL)
133 {
134 return pFunc;
135 }
136 else
137 {
138 Frame* pFrm = GetFrame();
139 if (pFrm != NULL && pFrm->GetVTablePtr() == InterpreterFrame::GetMethodFrameVPtr())
140 {
141#ifdef DACCESS_COMPILE
142 // TBD: DACize the interpreter.
143 return NULL;
144#else
145 return dac_cast<PTR_InterpreterFrame>(pFrm)->GetInterpreter()->GetMethodDesc();
146#endif
147 }
148 else
149 {
150 return NULL;
151 }
152 }
153}
154#endif // FEATURE_INTERPRETER
155
156OBJECTREF CrawlFrame::GetThisPointer()
157{
158 CONTRACTL {
159 NOTHROW;
160 GC_NOTRIGGER;
161 MODE_COOPERATIVE;
162 SUPPORTS_DAC;
163 } CONTRACTL_END;
164
165 if (!pFunc || pFunc->IsStatic() || pFunc->GetMethodTable()->IsValueType())
166 return NULL;
167
168 // As discussed in the specification comment at the declaration, the precondition, unfortunately,
169 // differs by architecture. @TODO: fix this.
170#if defined(_TARGET_X86_)
171 _ASSERTE_MSG((pFunc->IsSharedByGenericInstantiations() && pFunc->AcquiresInstMethodTableFromThis())
172 || pFunc->IsSynchronized(),
173 "Precondition");
174#else
175 _ASSERTE_MSG(pFunc->IsSharedByGenericInstantiations() && pFunc->AcquiresInstMethodTableFromThis(), "Precondition");
176#endif
177
178 if (isFrameless)
179 {
180 return GetCodeManager()->GetInstance(pRD,
181 &codeInfo);
182 }
183 else
184 {
185 _ASSERTE(pFrame);
186 _ASSERTE(pFunc);
187 /*ISSUE: we already know that we have (at least) a method */
188 /* might need adjustment as soon as we solved the
189 jit-helper frame question
190 */
191 //<TODO>@TODO: What about other calling conventions?
192// _ASSERT(pFunc()->GetCallSig()->CALLING CONVENTION);</TODO>
193
194#ifdef _TARGET_AMD64_
195 // @TODO: PORT: we need to find the this pointer without triggering a GC
196 // or find a way to make this method GC_TRIGGERS
197 return NULL;
198#else
199 return (dac_cast<PTR_FramedMethodFrame>(pFrame))->GetThis();
200#endif // _TARGET_AMD64_
201 }
202}
203
204
205//-----------------------------------------------------------------------------
206// Get the "Ambient SP" from a CrawlFrame.
207// This will be null if there is no Ambient SP (eg, in the prolog / epilog,
208// or on certain platforms),
209//-----------------------------------------------------------------------------
210TADDR CrawlFrame::GetAmbientSPFromCrawlFrame()
211{
212 SUPPORTS_DAC;
213#if defined(_TARGET_X86_)
214 // we set nesting level to zero because it won't be used for esp-framed methods,
215 // and zero is at least valid for ebp based methods (where we won't use the ambient esp anyways)
216 DWORD nestingLevel = 0;
217 return GetCodeManager()->GetAmbientSP(
218 GetRegisterSet(),
219 GetCodeInfo(),
220 GetRelOffset(),
221 nestingLevel,
222 GetCodeManState()
223 );
224
225#elif defined(_TARGET_ARM_)
226 return GetRegisterSet()->pCurrentContext->Sp;
227#else
228 return NULL;
229#endif
230}
231
232
233PTR_VOID CrawlFrame::GetParamTypeArg()
234{
235 CONTRACTL {
236 NOTHROW;
237 GC_NOTRIGGER;
238 SUPPORTS_DAC;
239 } CONTRACTL_END;
240
241 if (isFrameless)
242 {
243 return GetCodeManager()->GetParamTypeArg(pRD,
244 &codeInfo);
245 }
246 else
247 {
248#ifdef FEATURE_INTERPRETER
249 if (pFrame != NULL && pFrame->GetVTablePtr() == InterpreterFrame::GetMethodFrameVPtr())
250 {
251#ifdef DACCESS_COMPILE
252 // TBD: DACize the interpreter.
253 return NULL;
254#else
255 return dac_cast<PTR_InterpreterFrame>(pFrame)->GetInterpreter()->GetParamTypeArg();
256#endif
257 }
258 // Otherwise...
259#endif // FEATURE_INTERPRETER
260
261 if (!pFunc || !pFunc->RequiresInstArg())
262 {
263 return NULL;
264 }
265
266#ifdef _WIN64
267 if (!pFunc->IsSharedByGenericInstantiations() ||
268 !(pFunc->RequiresInstMethodTableArg() || pFunc->RequiresInstMethodDescArg()))
269 {
270 // win64 can only return the param type arg if the method is shared code
271 // and actually has a param type arg
272 return NULL;
273 }
274#endif // _WIN64
275
276 _ASSERTE(pFrame);
277 _ASSERTE(pFunc);
278 return (dac_cast<PTR_FramedMethodFrame>(pFrame))->GetParamTypeArg();
279 }
280}
281
282
283
284// [pClassInstantiation] : Always filled in, though may be set to NULL if no inst.
285// [pMethodInst] : Always filled in, though may be set to NULL if no inst.
286void CrawlFrame::GetExactGenericInstantiations(Instantiation *pClassInst,
287 Instantiation *pMethodInst)
288{
289
290 CONTRACTL {
291 NOTHROW;
292 GC_NOTRIGGER;
293 PRECONDITION(CheckPointer(pClassInst));
294 PRECONDITION(CheckPointer(pMethodInst));
295 } CONTRACTL_END;
296
297 TypeHandle specificClass;
298 MethodDesc* specificMethod;
299
300 BOOL ret = Generics::GetExactInstantiationsOfMethodAndItsClassFromCallInformation(
301 GetFunction(),
302 GetExactGenericArgsToken(),
303 &specificClass,
304 &specificMethod);
305
306 if (!ret)
307 {
308 _ASSERTE(!"Cannot return exact class instantiation when we are requested to.");
309 }
310
311 *pClassInst = specificMethod->GetExactClassInstantiation(specificClass);
312 *pMethodInst = specificMethod->GetMethodInstantiation();
313}
314
315PTR_VOID CrawlFrame::GetExactGenericArgsToken()
316{
317
318 CONTRACTL {
319 NOTHROW;
320 GC_NOTRIGGER;
321 SUPPORTS_DAC;
322 } CONTRACTL_END;
323
324 MethodDesc* pFunc = GetFunction();
325
326 if (!pFunc || !pFunc->IsSharedByGenericInstantiations())
327 return NULL;
328
329 if (pFunc->AcquiresInstMethodTableFromThis())
330 {
331 OBJECTREF obj = GetThisPointer();
332 if (obj == NULL)
333 return NULL;
334 return obj->GetMethodTable();
335 }
336 else
337 {
338 _ASSERTE(pFunc->RequiresInstArg());
339 return GetParamTypeArg();
340 }
341}
342
343 /* Is this frame at a safe spot for GC?
344 */
345bool CrawlFrame::IsGcSafe()
346{
347 CONTRACTL {
348 NOTHROW;
349 GC_NOTRIGGER;
350 SUPPORTS_DAC;
351 } CONTRACTL_END;
352
353 return GetCodeManager()->IsGcSafe(&codeInfo, GetRelOffset());
354}
355
356#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
357bool CrawlFrame::HasTailCalls()
358{
359 CONTRACTL {
360 NOTHROW;
361 GC_NOTRIGGER;
362 SUPPORTS_DAC;
363 } CONTRACTL_END;
364
365 return GetCodeManager()->HasTailCalls(&codeInfo);
366}
367#endif // _TARGET_ARM_ || _TARGET_ARM64_
368
369inline void CrawlFrame::GotoNextFrame()
370{
371 CONTRACTL {
372 NOTHROW;
373 GC_NOTRIGGER;
374 SUPPORTS_DAC;
375 } CONTRACTL_END;
376
377 //
378 // Update app domain if this frame caused a transition
379 //
380
381 AppDomain *pRetDomain = pFrame->GetReturnDomain();
382 if (pRetDomain != NULL)
383 pAppDomain = pRetDomain;
384 pFrame = pFrame->Next();
385
386 if (pFrame != FRAME_TOP)
387 {
388 SetCurGSCookie(Frame::SafeGetGSCookiePtr(pFrame));
389 }
390}
391
392//******************************************************************************
393
394// For asynchronous stackwalks, the thread being walked may not be suspended.
395// It could cause a buffer-overrun while the stack-walk is in progress.
396// To detect this, we can only use data that is guarded by a GSCookie
397// that has been recently checked.
398// This function should be called after doing any time-consuming activity
399// during stack-walking to reduce the window in which a buffer-overrun
400// could cause an problems.
401//
402// To keep things simple, we do this checking even for synchronous stack-walks.
403void CrawlFrame::CheckGSCookies()
404{
405 WRAPPER_NO_CONTRACT;
406 SUPPORTS_DAC;
407
408#if !defined(DACCESS_COMPILE)
409 if (pFirstGSCookie == NULL)
410 return;
411
412 if (*pFirstGSCookie != GetProcessGSCookie())
413 DoJITFailFast();
414
415 if(*pCurGSCookie != GetProcessGSCookie())
416 DoJITFailFast();
417#endif // !DACCESS_COMPILE
418}
419
420void CrawlFrame::SetCurGSCookie(GSCookie * pGSCookie)
421{
422 WRAPPER_NO_CONTRACT;
423 SUPPORTS_DAC;
424
425#if !defined(DACCESS_COMPILE)
426 if (pGSCookie == NULL)
427 DoJITFailFast();
428
429 pCurGSCookie = pGSCookie;
430 if (pFirstGSCookie == NULL)
431 pFirstGSCookie = pGSCookie;
432
433 CheckGSCookies();
434#endif // !DACCESS_COMPILE
435}
436
437#if defined(WIN64EXCEPTIONS)
438bool CrawlFrame::IsFilterFunclet()
439{
440 WRAPPER_NO_CONTRACT;
441
442 if (!IsFrameless())
443 {
444 return false;
445 }
446
447 if (!isFilterFuncletCached)
448 {
449 isFilterFunclet = GetJitManager()->IsFilterFunclet(&codeInfo) != 0;
450 isFilterFuncletCached = true;
451 }
452
453 return isFilterFunclet;
454}
455
456#endif // WIN64EXCEPTIONS
457
458//******************************************************************************
459#if defined(ELIMINATE_FEF)
460//******************************************************************************
461// Advance to the next ExInfo. Typically done when an ExInfo has been used and
462// should not be used again.
463//******************************************************************************
464void ExInfoWalker::WalkOne()
465{
466 LIMITED_METHOD_CONTRACT;
467 SUPPORTS_DAC;
468
469 if (m_pExInfo)
470 {
471 LOG((LF_EH, LL_INFO10000, "ExInfoWalker::WalkOne: advancing ExInfo chain: pExInfo:%p, pContext:%p; prev:%p, pContext:%p\n",
472 m_pExInfo, m_pExInfo->m_pContext, m_pExInfo->m_pPrevNestedInfo, m_pExInfo->m_pPrevNestedInfo?m_pExInfo->m_pPrevNestedInfo->m_pContext:0));
473 m_pExInfo = m_pExInfo->m_pPrevNestedInfo;
474 }
475} // void ExInfoWalker::WalkOne()
476
477//******************************************************************************
478// Attempt to find an ExInfo with a pContext that is higher (older) than
479// a given minimum location. (It is the pContext's SP that is relevant.)
480//******************************************************************************
481void ExInfoWalker::WalkToPosition(
482 TADDR taMinimum, // Starting point of stack walk.
483 BOOL bPopFrames) // If true, ResetUseExInfoForStackwalk on each exinfo.
484{
485 LIMITED_METHOD_CONTRACT;
486 SUPPORTS_DAC;
487
488 while (m_pExInfo &&
489 ((GetSPFromContext() < taMinimum) ||
490 (GetSPFromContext() == NULL)) )
491 {
492 // Try the next ExInfo, if there is one.
493 LOG((LF_EH, LL_INFO10000,
494 "ExInfoWalker::WalkToPosition: searching ExInfo chain: m_pExInfo:%p, pContext:%p; \
495 prev:%p, pContext:%p; pStartFrame:%p\n",
496 m_pExInfo,
497 m_pExInfo->m_pContext,
498 m_pExInfo->m_pPrevNestedInfo,
499 (m_pExInfo->m_pPrevNestedInfo ? m_pExInfo->m_pPrevNestedInfo->m_pContext : 0),
500 taMinimum));
501
502 if (bPopFrames)
503 { // If caller asked for it, reset the bit which indicates that this ExInfo marks a fault from managed code.
504 // This is done so that the fault can be effectively "unwound" from the stack, similarly to how Frames
505 // are unlinked from the Frame chain.
506 m_pExInfo->m_ExceptionFlags.ResetUseExInfoForStackwalk();
507 }
508 m_pExInfo = m_pExInfo->m_pPrevNestedInfo;
509 }
510 // At this point, m_pExInfo is NULL, or points to a pContext that is greater than taMinimum.
511} // void ExInfoWalker::WalkToPosition()
512
513//******************************************************************************
514// Attempt to find an ExInfo with a pContext that has an IP in managed code.
515//******************************************************************************
516void ExInfoWalker::WalkToManaged()
517{
518 CONTRACTL
519 {
520 NOTHROW;
521 GC_NOTRIGGER;
522 SO_TOLERANT;
523 MODE_ANY;
524 SUPPORTS_DAC;
525 }
526 CONTRACTL_END;
527
528
529 while (m_pExInfo)
530 {
531 // See if the current ExInfo has a CONTEXT that "returns" to managed code, and, if so, exit the loop.
532 if (m_pExInfo->m_ExceptionFlags.UseExInfoForStackwalk() &&
533 GetContext() &&
534 ExecutionManager::IsManagedCode(GetIP(GetContext())))
535 {
536 break;
537 }
538 // No, so skip to next, if any.
539 LOG((LF_EH, LL_INFO1000, "ExInfoWalker::WalkToManaged: searching for ExInfo->managed: m_pExInfo:%p, pContext:%p, sp:%p; prev:%p, pContext:%p\n",
540 m_pExInfo,
541 GetContext(),
542 GetSPFromContext(),
543 m_pExInfo->m_pPrevNestedInfo,
544 m_pExInfo->m_pPrevNestedInfo?m_pExInfo->m_pPrevNestedInfo->m_pContext:0));
545 m_pExInfo = m_pExInfo->m_pPrevNestedInfo;
546 }
547 // At this point, m_pExInfo is NULL, or points to a pContext that has an IP in managed code.
548} // void ExInfoWalker::WalkToManaged()
549#endif // defined(ELIMINATE_FEF)
550
551#ifdef WIN64EXCEPTIONS
552// static
553UINT_PTR Thread::VirtualUnwindCallFrame(PREGDISPLAY pRD, EECodeInfo* pCodeInfo /*= NULL*/)
554{
555 CONTRACTL
556 {
557 NOTHROW;
558 GC_NOTRIGGER;
559
560 PRECONDITION(GetControlPC(pRD) == GetIP(pRD->pCurrentContext));
561 SO_TOLERANT;
562 }
563 CONTRACTL_END;
564
565 if (pRD->IsCallerContextValid)
566 {
567 // We already have the caller's frame context
568 // We just switch the pointers
569 PT_CONTEXT temp = pRD->pCurrentContext;
570 pRD->pCurrentContext = pRD->pCallerContext;
571 pRD->pCallerContext = temp;
572
573 PT_KNONVOLATILE_CONTEXT_POINTERS tempPtrs = pRD->pCurrentContextPointers;
574 pRD->pCurrentContextPointers = pRD->pCallerContextPointers;
575 pRD->pCallerContextPointers = tempPtrs;
576 }
577 else
578 {
579 VirtualUnwindCallFrame(pRD->pCurrentContext, pRD->pCurrentContextPointers, pCodeInfo);
580 }
581
582 SyncRegDisplayToCurrentContext(pRD);
583 pRD->IsCallerContextValid = FALSE;
584 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
585
586 return pRD->ControlPC;
587}
588
589
590// static
591PCODE Thread::VirtualUnwindCallFrame(T_CONTEXT* pContext,
592 T_KNONVOLATILE_CONTEXT_POINTERS* pContextPointers /*= NULL*/,
593 EECodeInfo * pCodeInfo /*= NULL*/)
594{
595 CONTRACTL
596 {
597 NOTHROW;
598 GC_NOTRIGGER;
599 PRECONDITION(CheckPointer(pContext, NULL_NOT_OK));
600 PRECONDITION(CheckPointer(pContextPointers, NULL_OK));
601 SO_TOLERANT;
602 SUPPORTS_DAC;
603 }
604 CONTRACTL_END;
605
606 PCODE uControlPc = GetIP(pContext);
607
608#if !defined(DACCESS_COMPILE)
609 UINT_PTR uImageBase;
610 PT_RUNTIME_FUNCTION pFunctionEntry;
611
612 if (pCodeInfo == NULL)
613 {
614#ifndef FEATURE_PAL
615 pFunctionEntry = RtlLookupFunctionEntry(uControlPc,
616 ARM_ONLY((DWORD*))(&uImageBase),
617 NULL);
618#else // !FEATURE_PAL
619 EECodeInfo codeInfo;
620
621 codeInfo.Init(uControlPc);
622 pFunctionEntry = codeInfo.GetFunctionEntry();
623 uImageBase = (UINT_PTR)codeInfo.GetModuleBase();
624#endif // !FEATURE_PAL
625 }
626 else
627 {
628 pFunctionEntry = pCodeInfo->GetFunctionEntry();
629 uImageBase = (UINT_PTR)pCodeInfo->GetModuleBase();
630
631 // RUNTIME_FUNCTION of cold code just points to the RUNTIME_FUNCTION of hot code. The unwinder
632 // expects this indirection to be resolved, so we use RUNTIME_FUNCTION of the hot code even
633 // if we are in cold code.
634
635#if defined(_DEBUG) && !defined(FEATURE_PAL)
636 UINT_PTR uImageBaseFromOS;
637 PT_RUNTIME_FUNCTION pFunctionEntryFromOS;
638
639 pFunctionEntryFromOS = RtlLookupFunctionEntry(uControlPc,
640 ARM_ONLY((DWORD*))(&uImageBaseFromOS),
641 NULL);
642
643 // Note that he address returned from the OS is different from the one we have computed
644 // when unwind info is registered using RtlAddGrowableFunctionTable. Compare RUNTIME_FUNCTION content.
645 _ASSERTE( (uImageBase == uImageBaseFromOS) && (memcmp(pFunctionEntry, pFunctionEntryFromOS, sizeof(RUNTIME_FUNCTION)) == 0) );
646#endif // _DEBUG && !FEATURE_PAL
647 }
648
649 if (pFunctionEntry)
650 {
651 uControlPc = VirtualUnwindNonLeafCallFrame(pContext, pContextPointers, pFunctionEntry, uImageBase);
652 }
653 else
654 {
655 uControlPc = VirtualUnwindLeafCallFrame(pContext);
656 }
657#else // DACCESS_COMPILE
658 // We can't use RtlVirtualUnwind() from out-of-process. Instead, we call code:DacUnwindStackFrame,
659 // which is similar to StackWalk64().
660 if (DacUnwindStackFrame(pContext, pContextPointers) == TRUE)
661 {
662 uControlPc = GetIP(pContext);
663 }
664 else
665 {
666 ThrowHR(CORDBG_E_TARGET_INCONSISTENT);
667 }
668#endif // !DACCESS_COMPILE
669
670 return uControlPc;
671}
672
673#ifndef DACCESS_COMPILE
674
675// static
676PCODE Thread::VirtualUnwindLeafCallFrame(T_CONTEXT* pContext)
677{
678 PCODE uControlPc;
679
680#if defined(_DEBUG) && !defined(FEATURE_PAL)
681 UINT_PTR uImageBase;
682
683 PT_RUNTIME_FUNCTION pFunctionEntry = RtlLookupFunctionEntry((UINT_PTR)GetIP(pContext),
684 ARM_ONLY((DWORD*))(&uImageBase),
685 NULL);
686
687 CONSISTENCY_CHECK(NULL == pFunctionEntry);
688#endif // _DEBUG && !FEATURE_PAL
689
690#if defined(_TARGET_AMD64_)
691
692 uControlPc = *(ULONGLONG*)pContext->Rsp;
693 pContext->Rsp += sizeof(ULONGLONG);
694
695#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
696
697 uControlPc = TADDR(pContext->Lr);
698
699#else
700 PORTABILITY_ASSERT("Thread::VirtualUnwindLeafCallFrame");
701 uControlPc = NULL;
702#endif
703
704 SetIP(pContext, uControlPc);
705
706
707 return uControlPc;
708}
709
710// static
711PCODE Thread::VirtualUnwindNonLeafCallFrame(T_CONTEXT* pContext, KNONVOLATILE_CONTEXT_POINTERS* pContextPointers,
712 PT_RUNTIME_FUNCTION pFunctionEntry, UINT_PTR uImageBase)
713{
714 CONTRACTL
715 {
716 NOTHROW;
717 GC_NOTRIGGER;
718 PRECONDITION(CheckPointer(pContext, NULL_NOT_OK));
719 PRECONDITION(CheckPointer(pContextPointers, NULL_OK));
720 PRECONDITION(CheckPointer(pFunctionEntry, NULL_OK));
721 SO_TOLERANT;
722 }
723 CONTRACTL_END;
724
725 PCODE uControlPc = GetIP(pContext);
726#ifdef BIT64
727 UINT64 EstablisherFrame;
728#else // BIT64
729 DWORD EstablisherFrame;
730#endif // BIT64
731 PVOID HandlerData;
732
733 if (NULL == pFunctionEntry)
734 {
735#ifndef FEATURE_PAL
736 pFunctionEntry = RtlLookupFunctionEntry(uControlPc,
737 ARM_ONLY((DWORD*))(&uImageBase),
738 NULL);
739#endif
740 if (NULL == pFunctionEntry)
741 {
742 return NULL;
743 }
744 }
745
746 RtlVirtualUnwind(NULL,
747 uImageBase,
748 uControlPc,
749 pFunctionEntry,
750 pContext,
751 &HandlerData,
752 &EstablisherFrame,
753 pContextPointers);
754
755 uControlPc = GetIP(pContext);
756 return uControlPc;
757}
758
759// static
760UINT_PTR Thread::VirtualUnwindToFirstManagedCallFrame(T_CONTEXT* pContext)
761{
762 CONTRACTL
763 {
764 NOTHROW;
765 GC_NOTRIGGER;
766 SO_TOLERANT;
767 }
768 CONTRACTL_END;
769
770 PCODE uControlPc = GetIP(pContext);
771
772 // unwind out of this function and out of our caller to
773 // get our caller's PSP, or our caller's caller's SP.
774 while (!ExecutionManager::IsManagedCode(uControlPc))
775 {
776#ifndef FEATURE_PAL
777 uControlPc = VirtualUnwindCallFrame(pContext);
778#else // !FEATURE_PAL
779 BOOL success = PAL_VirtualUnwind(pContext, NULL);
780 if (!success)
781 {
782 _ASSERTE(!"Thread::VirtualUnwindToFirstManagedCallFrame: PAL_VirtualUnwind failed");
783 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
784 }
785
786 uControlPc = GetIP(pContext);
787
788 if (uControlPc == 0)
789 {
790 break;
791 }
792#endif // !FEATURE_PAL
793 }
794
795 return uControlPc;
796}
797
798#endif // !DACCESS_COMPILE
799#endif // WIN64EXCEPTIONS
800
801#ifdef _DEBUG
802void Thread::DebugLogStackWalkInfo(CrawlFrame* pCF, __in_z LPCSTR pszTag, UINT32 uFramesProcessed)
803{
804 LIMITED_METHOD_CONTRACT;
805 SUPPORTS_DAC;
806 if (pCF->isFrameless)
807 {
808 LPCSTR pszType = "";
809
810#ifdef WIN64EXCEPTIONS
811 if (pCF->IsFunclet())
812 {
813 pszType = "[funclet]";
814 }
815 else
816#endif // WIN64EXCEPTIONS
817 if (pCF->pFunc->IsNoMetadata())
818 {
819 pszType = "[no metadata]";
820 }
821
822 LOG((LF_GCROOTS, LL_INFO10000, "STACKWALK: [%03x] %s: FRAMELESS: PC=" FMT_ADDR " SP=" FMT_ADDR " method=%s %s\n",
823 uFramesProcessed,
824 pszTag,
825 DBG_ADDR(GetControlPC(pCF->pRD)),
826 DBG_ADDR(GetRegdisplaySP(pCF->pRD)),
827 pCF->pFunc->m_pszDebugMethodName,
828 pszType));
829 }
830 else if (pCF->isNativeMarker)
831 {
832 LOG((LF_GCROOTS, LL_INFO10000, "STACKWALK: [%03x] %s: NATIVE : PC=" FMT_ADDR " SP=" FMT_ADDR "\n",
833 uFramesProcessed,
834 pszTag,
835 DBG_ADDR(GetControlPC(pCF->pRD)),
836 DBG_ADDR(GetRegdisplaySP(pCF->pRD))));
837 }
838 else if (pCF->isNoFrameTransition)
839 {
840 LOG((LF_GCROOTS, LL_INFO10000, "STACKWALK: [%03x] %s: NO_FRAME : PC=" FMT_ADDR " SP=" FMT_ADDR "\n",
841 uFramesProcessed,
842 pszTag,
843 DBG_ADDR(GetControlPC(pCF->pRD)),
844 DBG_ADDR(GetRegdisplaySP(pCF->pRD))));
845 }
846 else
847 {
848 LOG((LF_GCROOTS, LL_INFO10000, "STACKWALK: [%03x] %s: EXPLICIT : PC=" FMT_ADDR " SP=" FMT_ADDR " Frame=" FMT_ADDR" vtbl=" FMT_ADDR "\n",
849 uFramesProcessed,
850 pszTag,
851 DBG_ADDR(GetControlPC(pCF->pRD)),
852 DBG_ADDR(GetRegdisplaySP(pCF->pRD)),
853 DBG_ADDR(pCF->pFrame),
854 DBG_ADDR((pCF->pFrame != FRAME_TOP) ? pCF->pFrame->GetVTablePtr() : NULL)));
855 }
856}
857#endif // _DEBUG
858
859StackWalkAction Thread::MakeStackwalkerCallback(
860 CrawlFrame* pCF,
861 PSTACKWALKFRAMESCALLBACK pCallback,
862 VOID* pData
863 DEBUG_ARG(UINT32 uFramesProcessed))
864{
865 INDEBUG(DebugLogStackWalkInfo(pCF, "CALLBACK", uFramesProcessed));
866
867 // Since we may be asynchronously walking another thread's stack,
868 // check (frequently) for stack-buffer-overrun corruptions
869 pCF->CheckGSCookies();
870
871 // Since the stackwalker callback may execute arbitrary managed code and possibly
872 // not even return (in the case of exception unwinding), explicitly clear the
873 // stackwalker thread state indicator around the callback.
874
875 CLEAR_THREAD_TYPE_STACKWALKER();
876
877 StackWalkAction swa = pCallback(pCF, (VOID*)pData);
878
879 SET_THREAD_TYPE_STACKWALKER(this);
880
881 pCF->CheckGSCookies();
882
883#ifdef _DEBUG
884 if (swa == SWA_ABORT)
885 {
886 LOG((LF_GCROOTS, LL_INFO10000, "STACKWALK: SWA_ABORT: callback aborted the stackwalk\n"));
887 }
888#endif // _DEBUG
889
890 return swa;
891}
892
893
894#if !defined(DACCESS_COMPILE) && defined(_TARGET_X86_) && !defined(WIN64EXCEPTIONS)
895#define STACKWALKER_MAY_POP_FRAMES
896#endif
897
898
899StackWalkAction Thread::StackWalkFramesEx(
900 PREGDISPLAY pRD, // virtual register set at crawl start
901 PSTACKWALKFRAMESCALLBACK pCallback,
902 VOID *pData,
903 unsigned flags,
904 PTR_Frame pStartFrame
905 )
906{
907 // Note: there are cases (i.e., exception handling) where we may never return from this function. This means
908 // that any C++ destructors pushed in this function will never execute, and it means that this function can
909 // never have a dynamic contract.
910 STATIC_CONTRACT_WRAPPER;
911 STATIC_CONTRACT_SO_INTOLERANT;
912 SCAN_IGNORE_THROW; // see contract above
913 SCAN_IGNORE_TRIGGER; // see contract above
914
915 _ASSERTE(pRD);
916 _ASSERTE(pCallback);
917
918 // when POPFRAMES we don't want to allow GC trigger.
919 // The only method that guarantees this now is COMPlusUnwindCallback
920#ifdef STACKWALKER_MAY_POP_FRAMES
921 ASSERT(!(flags & POPFRAMES) || pCallback == (PSTACKWALKFRAMESCALLBACK) COMPlusUnwindCallback);
922 ASSERT(!(flags & POPFRAMES) || pRD->pContextForUnwind != NULL);
923 ASSERT(!(flags & POPFRAMES) || (this == GetThread() && PreemptiveGCDisabled()));
924#else // STACKWALKER_MAY_POP_FRAMES
925 ASSERT(!(flags & POPFRAMES));
926#endif // STACKWALKER_MAY_POP_FRAMES
927
928 // We haven't set the stackwalker thread type flag yet, so it shouldn't be set. Only
929 // exception to this is if the current call is made by a hijacking profiler which
930 // redirected this thread while it was previously in the middle of another stack walk
931#ifdef PROFILING_SUPPORTED
932 _ASSERTE(CORProfilerStackSnapshotEnabled() || !IsStackWalkerThread());
933#else
934 _ASSERTE(!IsStackWalkerThread());
935#endif
936
937 StackWalkAction retVal = SWA_FAILED;
938
939 {
940 // SCOPE: Remember that we're walking the stack.
941 //
942 // Normally, we'd use a holder (ClrFlsThreadTypeSwitch) to temporarily set this
943 // flag in the thread state, but we can't in this function, since C++ destructors
944 // are forbidden when this is called for exception handling (which causes
945 // MakeStackwalkerCallback() not to return). Note that in exception handling
946 // cases, we will have already cleared the stack walker thread state indicator inside
947 // MakeStackwalkerCallback(), so we will be properly cleaned up.
948#if !defined(DACCESS_COMPILE)
949 PVOID pStackWalkThreadOrig = ClrFlsGetValue(TlsIdx_StackWalkerWalkingThread);
950#endif
951 SET_THREAD_TYPE_STACKWALKER(this);
952
953 StackFrameIterator iter;
954 if (iter.Init(this, pStartFrame, pRD, flags) == TRUE)
955 {
956 while (iter.IsValid())
957 {
958 retVal = MakeStackwalkerCallback(&iter.m_crawl, pCallback, pData DEBUG_ARG(iter.m_uFramesProcessed));
959 if (retVal == SWA_ABORT)
960 {
961 break;
962 }
963
964 retVal = iter.Next();
965 if (retVal == SWA_FAILED)
966 {
967 break;
968 }
969 }
970 }
971
972 SET_THREAD_TYPE_STACKWALKER(pStackWalkThreadOrig);
973 }
974
975 return retVal;
976} // StackWalkAction Thread::StackWalkFramesEx()
977
978StackWalkAction Thread::StackWalkFrames(PSTACKWALKFRAMESCALLBACK pCallback,
979 VOID *pData,
980 unsigned flags,
981 PTR_Frame pStartFrame)
982{
983 // Note: there are cases (i.e., exception handling) where we may never return from this function. This means
984 // that any C++ destructors pushed in this function will never execute, and it means that this function can
985 // never have a dynamic contract.
986 STATIC_CONTRACT_WRAPPER;
987 _ASSERTE((flags & THREAD_IS_SUSPENDED) == 0 || (flags & ALLOW_ASYNC_STACK_WALK));
988
989 T_CONTEXT ctx;
990 REGDISPLAY rd;
991 bool fUseInitRegDisplay;
992
993#ifndef DACCESS_COMPILE
994 _ASSERTE(GetThread() == this || (flags & ALLOW_ASYNC_STACK_WALK));
995 BOOL fDebuggerHasInitialContext = (GetFilterContext() != NULL);
996 BOOL fProfilerHasInitialContext = (GetProfilerFilterContext() != NULL);
997
998 // If this walk is seeded by a profiler, then the walk better be done by the profiler
999 _ASSERTE(!fProfilerHasInitialContext || (flags & PROFILER_DO_STACK_SNAPSHOT));
1000
1001 fUseInitRegDisplay = fDebuggerHasInitialContext || fProfilerHasInitialContext;
1002#else
1003 fUseInitRegDisplay = true;
1004#endif
1005
1006 if(fUseInitRegDisplay)
1007 {
1008 if (GetProfilerFilterContext() != NULL)
1009 {
1010 if (!InitRegDisplay(&rd, GetProfilerFilterContext(), TRUE))
1011 {
1012 LOG((LF_CORPROF, LL_INFO100, "**PROF: InitRegDisplay(&rd, GetProfilerFilterContext() failure leads to SWA_FAILED.\n"));
1013 return SWA_FAILED;
1014 }
1015 }
1016 else
1017 {
1018 if (!InitRegDisplay(&rd, &ctx, FALSE))
1019 {
1020 LOG((LF_CORPROF, LL_INFO100, "**PROF: InitRegDisplay(&rd, &ctx, FALSE) failure leads to SWA_FAILED.\n"));
1021 return SWA_FAILED;
1022 }
1023 }
1024 }
1025 else
1026 {
1027 // Initialize the context
1028 memset(&ctx, 0x00, sizeof(T_CONTEXT));
1029 SetIP(&ctx, 0);
1030 SetSP(&ctx, 0);
1031 SetFP(&ctx, 0);
1032 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK starting with partial context\n"));
1033 FillRegDisplay(&rd, &ctx);
1034 }
1035
1036#ifdef STACKWALKER_MAY_POP_FRAMES
1037 if (flags & POPFRAMES)
1038 rd.pContextForUnwind = &ctx;
1039#endif
1040
1041 return StackWalkFramesEx(&rd, pCallback, pData, flags, pStartFrame);
1042}
1043
1044StackWalkAction StackWalkFunctions(Thread * thread,
1045 PSTACKWALKFRAMESCALLBACK pCallback,
1046 VOID * pData)
1047{
1048 // Note: there are cases (i.e., exception handling) where we may never return from this function. This means
1049 // that any C++ destructors pushed in this function will never execute, and it means that this function can
1050 // never have a dynamic contract.
1051 STATIC_CONTRACT_WRAPPER;
1052
1053 return thread->StackWalkFrames(pCallback, pData, FUNCTIONSONLY);
1054}
1055
1056// ----------------------------------------------------------------------------
1057// StackFrameIterator::StackFrameIterator
1058//
1059// Description:
1060// This constructor is for the usage pattern of creating an uninitialized StackFrameIterator and then
1061// calling Init() on it.
1062//
1063// Assumptions:
1064// * The caller needs to call Init() with the correct arguments before using the StackFrameIterator.
1065//
1066
1067StackFrameIterator::StackFrameIterator()
1068{
1069 LIMITED_METHOD_CONTRACT;
1070 SUPPORTS_DAC;
1071 CommonCtor(NULL, NULL, 0xbaadf00d);
1072} // StackFrameIterator::StackFrameIterator()
1073
1074// ----------------------------------------------------------------------------
1075// StackFrameIterator::StackFrameIterator
1076//
1077// Description:
1078// This constructor is for the usage pattern of creating an initialized StackFrameIterator and then
1079// calling ResetRegDisp() on it.
1080//
1081// Arguments:
1082// * pThread - the thread to walk
1083// * pFrame - the starting explicit frame; NULL means use the top explicit frame from the frame chain
1084// * flags - the stackwalk flags
1085//
1086// Assumptions:
1087// * The caller can call ResetRegDisp() to use the StackFrameIterator without calling Init() first.
1088//
1089
1090StackFrameIterator::StackFrameIterator(Thread * pThread, PTR_Frame pFrame, ULONG32 flags)
1091{
1092 SUPPORTS_DAC;
1093 CommonCtor(pThread, pFrame, flags);
1094} // StackFrameIterator::StackFrameIterator()
1095
1096// ----------------------------------------------------------------------------
1097// StackFrameIterator::CommonCtor
1098//
1099// Description:
1100// This is a helper for the two constructors.
1101//
1102// Arguments:
1103// * pThread - the thread to walk
1104// * pFrame - the starting explicit frame; NULL means use the top explicit frame from the frame chain
1105// * flags - the stackwalk flags
1106//
1107
1108void StackFrameIterator::CommonCtor(Thread * pThread, PTR_Frame pFrame, ULONG32 flags)
1109{
1110 WRAPPER_NO_CONTRACT;
1111 SUPPORTS_DAC;
1112
1113 INDEBUG(m_uFramesProcessed = 0);
1114
1115 m_frameState = SFITER_UNINITIALIZED;
1116 m_pThread = pThread;
1117
1118 m_pStartFrame = pFrame;
1119#if defined(_DEBUG)
1120 if (m_pStartFrame != NULL)
1121 {
1122 m_pRealStartFrame = m_pStartFrame;
1123 }
1124 else if (m_pThread != NULL)
1125 {
1126 m_pRealStartFrame = m_pThread->GetFrame();
1127 }
1128 else
1129 {
1130 m_pRealStartFrame = NULL;
1131 }
1132#endif // _DEBUG
1133
1134 m_flags = flags;
1135 m_codeManFlags = (ICodeManagerFlags)0;
1136
1137 m_pCachedGSCookie = NULL;
1138
1139#if defined(WIN64EXCEPTIONS)
1140 m_sfParent = StackFrame();
1141 ResetGCRefReportingState();
1142 m_fDidFuncletReportGCReferences = true;
1143#endif // WIN64EXCEPTIONS
1144
1145#if defined(RECORD_RESUMABLE_FRAME_SP)
1146 m_pvResumableFrameTargetSP = NULL;
1147#endif
1148} // StackFrameIterator::CommonCtor()
1149
1150//---------------------------------------------------------------------------------------
1151//
1152// Initialize the iterator. Note that the iterator has thread-affinity,
1153// and the stackwalk flags cannot be changed once the iterator is created.
1154// Depending on the flags, initialization may involve unwinding to a frame of interest.
1155// The unwinding could fail.
1156//
1157// Arguments:
1158// pThread - the thread to walk
1159// pFrame - the starting explicit frame; NULL means use the top explicit frame from
1160// pThread->GetFrame()
1161// pRegDisp - the initial REGDISPLAY
1162// flags - the stackwalk flags
1163//
1164// Return Value:
1165// Returns true if the initialization is successful. The initialization could fail because
1166// we fail to unwind.
1167//
1168// Notes:
1169// Do not do anything funky between initializing a StackFrameIterator and actually using it.
1170// In particular, do not resume the thread. We only unhijack the thread once in Init().
1171// Refer to StackWalkFramesEx() for the typical usage pattern.
1172//
1173
1174BOOL StackFrameIterator::Init(Thread * pThread,
1175 PTR_Frame pFrame,
1176 PREGDISPLAY pRegDisp,
1177 ULONG32 flags)
1178{
1179 WRAPPER_NO_CONTRACT;
1180 SUPPORTS_DAC;
1181
1182 _ASSERTE(pThread != NULL);
1183 _ASSERTE(pRegDisp != NULL);
1184
1185#if !defined(DACCESS_COMPILE)
1186 // When the LIGHTUNWIND flag is set, we use the stack walk cache.
1187 // On x64, accesses to the stack walk cache are synchronized by
1188 // a CrstStatic, which may need to call back into the host.
1189 _ASSERTE(CanThisThreadCallIntoHost() || (flags & LIGHTUNWIND) == 0);
1190#endif // DACCESS_COMPILE
1191
1192#ifdef WIN64EXCEPTIONS
1193 _ASSERTE(!(flags & POPFRAMES));
1194 _ASSERTE(pRegDisp->pCurrentContext);
1195#endif // WIN64EXCEPTIONS
1196
1197 BEGIN_FORBID_TYPELOAD();
1198
1199#ifdef FEATURE_HIJACK
1200 // We can't crawl the stack of a thread that currently has a hijack pending
1201 // (since the hijack routine won't be recognized by any code manager). So we
1202 // undo any hijack, the EE will re-attempt it later.
1203
1204#if !defined(DACCESS_COMPILE)
1205 // OOP stackwalks need to deal with hijacked threads in a special way.
1206 pThread->UnhijackThread();
1207#endif // !DACCESS_COMPILE
1208
1209#endif // FEATURE_HIJACK
1210
1211 // FRAME_TOP and NULL must be distinct values. This assert
1212 // will fire if someone changes this.
1213 static_assert_no_msg(FRAME_TOP_VALUE != NULL);
1214
1215 m_frameState = SFITER_UNINITIALIZED;
1216
1217 m_pThread = pThread;
1218 m_flags = flags;
1219
1220 ResetCrawlFrame();
1221
1222 m_pStartFrame = pFrame;
1223 if (m_pStartFrame)
1224 {
1225 m_crawl.pFrame = m_pStartFrame;
1226 }
1227 else
1228 {
1229 m_crawl.pFrame = m_pThread->GetFrame();
1230 _ASSERTE(m_crawl.pFrame != NULL);
1231 }
1232 INDEBUG(m_pRealStartFrame = m_crawl.pFrame);
1233
1234 if (m_crawl.pFrame != FRAME_TOP && !(m_flags & SKIP_GSCOOKIE_CHECK))
1235 {
1236 m_crawl.SetCurGSCookie(Frame::SafeGetGSCookiePtr(m_crawl.pFrame));
1237 }
1238
1239 m_crawl.pRD = pRegDisp;
1240 m_crawl.pAppDomain = pThread->GetDomain(INDEBUG(flags & PROFILER_DO_STACK_SNAPSHOT));
1241
1242 m_codeManFlags = (ICodeManagerFlags)((flags & QUICKUNWIND) ? 0 : UpdateAllRegs);
1243 m_scanFlag = ExecutionManager::GetScanFlags();
1244
1245#if defined(ELIMINATE_FEF)
1246 // Walk the ExInfo chain, past any specified starting frame.
1247 m_exInfoWalk.Init(&(pThread->GetExceptionState()->m_currentExInfo));
1248 // false means don't reset UseExInfoForStackwalk
1249 m_exInfoWalk.WalkToPosition(dac_cast<TADDR>(m_pStartFrame), false);
1250#endif // ELIMINATE_FEF
1251
1252 //
1253 // These fields are used in the iteration and will be updated on a per-frame basis:
1254 //
1255 // EECodeInfo m_cachedCodeInfo;
1256 //
1257 // GSCookie * m_pCachedGSCookie;
1258 //
1259 // StackFrame m_sfParent;
1260 //
1261 // LPVOID m_pvResumableFrameTargetSP;
1262 //
1263
1264 // process the REGDISPLAY and stop at the first frame
1265 ProcessIp(GetControlPC(m_crawl.pRD));
1266 ProcessCurrentFrame();
1267
1268 // advance to the next frame which matches the stackwalk flags
1269 StackWalkAction retVal = Filter();
1270
1271 END_FORBID_TYPELOAD();
1272
1273 return (retVal == SWA_CONTINUE);
1274} // StackFrameIterator::Init()
1275
1276//---------------------------------------------------------------------------------------
1277//
1278// Reset the stackwalk iterator with the specified REGDISPLAY.
1279// The caller is responsible for making sure the REGDISPLAY is valid.
1280// This function is very similar to Init(), except that this function takes a REGDISPLAY
1281// to seed the stackwalk. This function may also unwind depending on the flags, and the
1282// unwinding may fail.
1283//
1284// Arguments:
1285// pRegDisp - new REGDISPLAY
1286// bool - whether the REGDISPLAY is for the leaf frame
1287//
1288// Return Value:
1289// Returns true if the reset is successful. The reset could fail because
1290// we fail to unwind.
1291//
1292// Assumptions:
1293// The REGDISPLAY is valid for the thread which the iterator has affinity to.
1294//
1295
1296BOOL StackFrameIterator::ResetRegDisp(PREGDISPLAY pRegDisp,
1297 bool fIsFirst)
1298{
1299 WRAPPER_NO_CONTRACT;
1300 SUPPORTS_DAC;
1301
1302 // It is invalid to reset a stackwalk if we are popping frames along the way.
1303 ASSERT(!(m_flags & POPFRAMES));
1304
1305 BEGIN_FORBID_TYPELOAD();
1306
1307 m_frameState = SFITER_UNINITIALIZED;
1308
1309 // Make sure the StackFrameIterator has been initialized properly.
1310 _ASSERTE(m_pThread != NULL);
1311 _ASSERTE(m_flags != 0xbaadf00d);
1312
1313 ResetCrawlFrame();
1314
1315 m_crawl.isFirst = fIsFirst;
1316
1317 if (m_pStartFrame)
1318 {
1319 m_crawl.pFrame = m_pStartFrame;
1320 }
1321 else
1322 {
1323 m_crawl.pFrame = m_pThread->GetFrame();
1324 _ASSERTE(m_crawl.pFrame != NULL);
1325 }
1326
1327 if (m_crawl.pFrame != FRAME_TOP && !(m_flags & SKIP_GSCOOKIE_CHECK))
1328 {
1329 m_crawl.SetCurGSCookie(Frame::SafeGetGSCookiePtr(m_crawl.pFrame));
1330 }
1331
1332 m_crawl.pRD = pRegDisp;
1333
1334 // we initialize the appdomain to be the current domain, but this nees to be updated below
1335 m_crawl.pAppDomain = m_crawl.pThread->GetDomain(INDEBUG(m_flags & PROFILER_DO_STACK_SNAPSHOT));
1336
1337 m_codeManFlags = (ICodeManagerFlags)((m_flags & QUICKUNWIND) ? 0 : UpdateAllRegs);
1338
1339 // make sure the REGDISPLAY is synchronized with the CONTEXT
1340 UpdateRegDisp();
1341
1342 PCODE curPc = GetControlPC(pRegDisp);
1343 ProcessIp(curPc);
1344
1345 // loop the frame chain to find the closet explicit frame which is lower than the specificed REGDISPLAY
1346 // (stack grows up towards lower address)
1347 if (m_crawl.pFrame != FRAME_TOP)
1348 {
1349 TADDR curSP = GetRegdisplaySP(m_crawl.pRD);
1350
1351#ifdef PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
1352 if (m_crawl.IsFrameless())
1353 {
1354 // On 64-bit and ARM, we stop at the explicit frames contained in a managed stack frame
1355 // before the managed stack frame itself.
1356 EECodeManager::EnsureCallerContextIsValid(m_crawl.pRD, NULL);
1357 curSP = GetSP(m_crawl.pRD->pCallerContext);
1358 }
1359#endif // PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
1360
1361#if defined(_TARGET_X86_)
1362 // special processing on x86; see below for more information
1363 TADDR curEBP = GetRegdisplayFP(m_crawl.pRD);
1364
1365 CONTEXT tmpCtx;
1366 REGDISPLAY tmpRD;
1367 CopyRegDisplay(m_crawl.pRD, &tmpRD, &tmpCtx);
1368#endif // _TARGET_X86_
1369
1370 //
1371 // The basic idea is to loop the frame chain until we find an explicit frame whose address is below
1372 // (close to the root) the SP in the specified REGDISPLAY. This works well on WIN64 platforms.
1373 // However, on x86, in M2U transitions, the Windows debuggers will pass us an incorrect REGDISPLAY
1374 // for the managed stack frame at the M2U boundary. The REGDISPLAY is obtained by unwinding the
1375 // marshaling stub, and it contains an SP which is actually higher (closer to the leaf) than the
1376 // address of the transition frame. It is as if the explicit frame is not contained in the stack
1377 // frame of any method. Here's an example:
1378 //
1379 // ChildEBP
1380 // 0012e884 ntdll32!DbgBreakPoint
1381 // 0012e89c CLRStub[StubLinkStub]@1f0ac1e
1382 // 0012e8a4 invalid ESP of Foo() according to the REGDISPLAY specified by the debuggers
1383 // 0012e8b4 address of transition frame (NDirectMethodFrameStandalone)
1384 // 0012e8c8 real ESP of Foo() according to the transition frame
1385 // 0012e8d8 managed!Dummy.Foo()+0x20
1386 //
1387 // The original implementation of ResetRegDisp() compares the return address of the transition frame
1388 // and the IP in the specified REGDISPLAY to work around this problem. However, even this comparison
1389 // is not enough because we may have recursive pinvoke calls on the stack (albeit an unlikely
1390 // scenario). So in addition to the IP comparison, we also check EBP. Note that this does not
1391 // require managed stack frames to be EBP-framed.
1392 //
1393
1394 while (m_crawl.pFrame != FRAME_TOP)
1395 {
1396 // this check is sufficient on WIN64
1397 if (dac_cast<TADDR>(m_crawl.pFrame) >= curSP)
1398 {
1399#if defined(_TARGET_X86_)
1400 // check the IP
1401 if (m_crawl.pFrame->GetReturnAddress() != curPc)
1402 {
1403 break;
1404 }
1405 else
1406 {
1407 // unwind the REGDISPLAY using the transition frame and check the EBP
1408 m_crawl.pFrame->UpdateRegDisplay(&tmpRD);
1409 if (GetRegdisplayFP(&tmpRD) != curEBP)
1410 {
1411 break;
1412 }
1413 }
1414#else // !_TARGET_X86_
1415 break;
1416#endif // !_TARGET_X86_
1417 }
1418
1419 // if the REGDISPLAY represents the managed stack frame at a M2U transition boundary,
1420 // update the flags on the CrawlFrame and the REGDISPLAY
1421 PCODE frameRetAddr = m_crawl.pFrame->GetReturnAddress();
1422 if (frameRetAddr == curPc)
1423 {
1424 unsigned uFrameAttribs = m_crawl.pFrame->GetFrameAttribs();
1425
1426 m_crawl.isFirst = ((uFrameAttribs & Frame::FRAME_ATTR_RESUMABLE) != 0);
1427 m_crawl.isInterrupted = ((uFrameAttribs & Frame::FRAME_ATTR_EXCEPTION) != 0);
1428
1429 if (m_crawl.isInterrupted)
1430 {
1431 m_crawl.hasFaulted = ((uFrameAttribs & Frame::FRAME_ATTR_FAULTED) != 0);
1432 m_crawl.isIPadjusted = ((uFrameAttribs & Frame::FRAME_ATTR_OUT_OF_LINE) != 0);
1433 }
1434
1435 m_crawl.pFrame->UpdateRegDisplay(m_crawl.pRD);
1436
1437 _ASSERTE(curPc == GetControlPC(m_crawl.pRD));
1438 }
1439
1440 // this call also updates the appdomain if the explicit frame is a ContextTransitionFrame
1441 m_crawl.GotoNextFrame();
1442 }
1443 }
1444
1445#if defined(ELIMINATE_FEF)
1446 // Similarly, we need to walk the ExInfos.
1447 m_exInfoWalk.Init(&(m_crawl.pThread->GetExceptionState()->m_currentExInfo));
1448 // false means don't reset UseExInfoForStackwalk
1449 m_exInfoWalk.WalkToPosition(GetRegdisplaySP(m_crawl.pRD), false);
1450#endif // ELIMINATE_FEF
1451
1452 // now that everything is at where it should be, update the CrawlFrame
1453 ProcessCurrentFrame();
1454
1455 // advance to the next frame which matches the stackwalk flags
1456 StackWalkAction retVal = Filter();
1457
1458 END_FORBID_TYPELOAD();
1459
1460 return (retVal == SWA_CONTINUE);
1461} // StackFrameIterator::ResetRegDisp()
1462
1463
1464//---------------------------------------------------------------------------------------
1465//
1466// Reset the CrawlFrame owned by the iterator. Used by both Init() and ResetRegDisp().
1467//
1468// Assumptions:
1469// this->m_pThread and this->m_flags have been initialized.
1470//
1471// Notes:
1472// In addition, the following fields are not reset. The caller must update them:
1473// pFrame, pFunc, pAppDomain, pRD
1474//
1475// Fields updated by ProcessIp():
1476// isFrameless, and codeInfo
1477//
1478// Fields updated by ProcessCurrentFrame():
1479// codeManState
1480//
1481
1482void StackFrameIterator::ResetCrawlFrame()
1483{
1484 WRAPPER_NO_CONTRACT;
1485 SUPPORTS_DAC;
1486
1487 INDEBUG(memset(&(m_crawl.pFunc), 0xCC, sizeof(m_crawl.pFunc)));
1488
1489 m_crawl.isFirst = true;
1490 m_crawl.isInterrupted = false;
1491 m_crawl.hasFaulted = false;
1492 m_crawl.isIPadjusted = false; // can be removed
1493
1494 m_crawl.isNativeMarker = false;
1495 m_crawl.isProfilerDoStackSnapshot = !!(this->m_flags & PROFILER_DO_STACK_SNAPSHOT);
1496 m_crawl.isNoFrameTransition = false;
1497
1498 m_crawl.taNoFrameTransitionMarker = NULL;
1499
1500#if defined(WIN64EXCEPTIONS)
1501 m_crawl.isFilterFunclet = false;
1502 m_crawl.isFilterFuncletCached = false;
1503 m_crawl.fShouldParentToFuncletSkipReportingGCReferences = false;
1504 m_crawl.fShouldParentFrameUseUnwindTargetPCforGCReporting = false;
1505#endif // WIN64EXCEPTIONS
1506
1507 m_crawl.pThread = this->m_pThread;
1508
1509 m_crawl.pSecurityObject = NULL;
1510 m_crawl.isCachedMethod = false;
1511 m_crawl.stackWalkCache.ClearEntry();
1512
1513 m_crawl.pCurGSCookie = NULL;
1514 m_crawl.pFirstGSCookie = NULL;
1515}
1516
1517//---------------------------------------------------------------------------------------
1518//
1519// This function represents whether the iterator has reached the root of the stack or not.
1520// It can be used as the loop-terminating condition for the iterator.
1521//
1522// Return Value:
1523// Returns true if there is more frames on the stack to walk.
1524//
1525
1526BOOL StackFrameIterator::IsValid(void)
1527{
1528 WRAPPER_NO_CONTRACT;
1529 SUPPORTS_DAC;
1530
1531 // There is more to iterate if the stackwalk is currently in managed code,
1532 // or if there are frames left.
1533 // If there is an ExInfo with a pContext, it may substitute for a Frame,
1534 // if the ExInfo is due to an exception in managed code.
1535 if (!m_crawl.isFrameless && m_crawl.pFrame == FRAME_TOP)
1536 {
1537 // if we are stopped at a native marker frame, we can still advance at least once more
1538 if (m_frameState == SFITER_NATIVE_MARKER_FRAME)
1539 {
1540 _ASSERTE(m_crawl.isNativeMarker);
1541 return TRUE;
1542 }
1543
1544#if defined(ELIMINATE_FEF)
1545 // Not in managed code, and no frames left -- check for an ExInfo.
1546 // @todo: check for exception?
1547 m_exInfoWalk.WalkToManaged();
1548 if (m_exInfoWalk.GetContext())
1549 return TRUE;
1550#endif // ELIMINATE_FEF
1551
1552#ifdef _DEBUG
1553 // Try to ensure that the frame chain did not change underneath us.
1554 // In particular, is thread's starting frame the same as it was when
1555 // we started?
1556 //DevDiv 168789: In GCStress >= 4 two threads could race on triggering GC;
1557 // if the one that just made p/invoke call is second and hits the trap instruction
1558 // before call to syncronize with GC, it will push a frame [ResumableFrame on Unix
1559 // and RedirectedThreadFrame on Windows] concurrently with GC stackwalking.
1560 // In normal case (no GCStress), after p/invoke, IL_STUB will check if GC is in progress and syncronize.
1561 BOOL bRedirectedPinvoke = FALSE;
1562
1563#ifdef FEATURE_HIJACK
1564 bRedirectedPinvoke = ((GCStress<cfg_instr>::IsEnabled()) &&
1565 (m_pRealStartFrame != NULL) &&
1566 (m_pRealStartFrame != FRAME_TOP) &&
1567 (m_pRealStartFrame->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr()) &&
1568 (m_pThread->GetFrame() != NULL) &&
1569 (m_pThread->GetFrame() != FRAME_TOP) &&
1570 ((m_pThread->GetFrame()->GetVTablePtr() == ResumableFrame::GetMethodFrameVPtr()) ||
1571 (m_pThread->GetFrame()->GetVTablePtr() == RedirectedThreadFrame::GetMethodFrameVPtr())));
1572#endif // FEATURE_HIJACK
1573
1574 _ASSERTE( (m_pStartFrame != NULL) ||
1575 (m_flags & POPFRAMES) ||
1576 (m_pRealStartFrame == m_pThread->GetFrame()) ||
1577 (bRedirectedPinvoke));
1578#endif //_DEBUG
1579
1580 return FALSE;
1581 }
1582
1583 return TRUE;
1584} // StackFrameIterator::IsValid()
1585
1586//---------------------------------------------------------------------------------------
1587//
1588// Advance to the next frame according to the stackwalk flags. If the iterator is stopped
1589// at some place not specified by the stackwalk flags, this function will automatically advance
1590// to the next frame.
1591//
1592// Return Value:
1593// SWA_CONTINUE (== SWA_DONE) if the iterator is successful in advancing to the next frame
1594// SWA_FAILED if an operation performed by the iterator fails
1595//
1596// Notes:
1597// This function returns SWA_DONE when advancing from the last frame to becoming invalid.
1598// It returns SWA_FAILED if the iterator is invalid.
1599//
1600
1601StackWalkAction StackFrameIterator::Next(void)
1602{
1603 WRAPPER_NO_CONTRACT;
1604 SUPPORTS_DAC;
1605
1606 if (!IsValid())
1607 {
1608 return SWA_FAILED;
1609 }
1610
1611 BEGIN_FORBID_TYPELOAD();
1612
1613 StackWalkAction retVal = NextRaw();
1614 if (retVal == SWA_CONTINUE)
1615 {
1616 retVal = Filter();
1617 }
1618
1619 END_FORBID_TYPELOAD();
1620 return retVal;
1621}
1622
1623//---------------------------------------------------------------------------------------
1624//
1625// Check whether we should stop at the current frame given the stackwalk flags.
1626// If not, continue advancing to the next frame.
1627//
1628// Return Value:
1629// Returns SWA_CONTINUE (== SWA_DONE) if the iterator is invalid or if no automatic advancing is done.
1630// Otherwise returns whatever the last call to NextRaw() returns.
1631//
1632
1633StackWalkAction StackFrameIterator::Filter(void)
1634{
1635 WRAPPER_NO_CONTRACT;
1636 SUPPORTS_DAC;
1637
1638 bool fStop = false;
1639 bool fSkippingFunclet = false;
1640
1641#if defined(WIN64EXCEPTIONS)
1642 bool fRecheckCurrentFrame = false;
1643 bool fSkipFuncletCallback = true;
1644#endif // defined(WIN64EXCEPTIONS)
1645
1646 StackWalkAction retVal = SWA_CONTINUE;
1647
1648 while (IsValid())
1649 {
1650 fStop = false;
1651 fSkippingFunclet = false;
1652
1653#if defined(WIN64EXCEPTIONS)
1654 ExceptionTracker* pTracker = m_crawl.pThread->GetExceptionState()->GetCurrentExceptionTracker();
1655 fRecheckCurrentFrame = false;
1656 fSkipFuncletCallback = true;
1657
1658 // by default, there is no funclet for the current frame
1659 // that reported GC references
1660 m_crawl.fShouldParentToFuncletSkipReportingGCReferences = false;
1661
1662 // By default, assume that we are going to report GC references for this
1663 // CrawlFrame
1664 m_crawl.fShouldCrawlframeReportGCReferences = true;
1665
1666 // By default, assume that parent frame is going to report GC references from
1667 // the actual location reported by the stack walk.
1668 m_crawl.fShouldParentFrameUseUnwindTargetPCforGCReporting = false;
1669
1670 if (!m_sfParent.IsNull())
1671 {
1672 // we are now skipping frames to get to the funclet's parent
1673 fSkippingFunclet = true;
1674 }
1675#endif // WIN64EXCEPTIONS
1676
1677 switch (m_frameState)
1678 {
1679 case SFITER_FRAMELESS_METHOD:
1680#if defined(WIN64EXCEPTIONS)
1681ProcessFuncletsForGCReporting:
1682 do
1683 {
1684 // When enumerating GC references for "liveness" reporting, depending upon the architecture,
1685 // the responsibility of who reports what varies:
1686 //
1687 // 1) On ARM, ARM64, and X64 (using RyuJIT), the funclet reports all references belonging
1688 // to itself and its parent method. This is indicated by the WantsReportOnlyLeaf flag being
1689 // set in the GC information for a function.
1690 //
1691 // 2) X64 (using JIT64) has the reporting distributed between the funclets and the parent method.
1692 // If some reference(s) get double reported, JIT64 can handle that by playing conservative.
1693 // JIT64 does NOT set the WantsReportOnlyLeaf flag in the function GC information.
1694 //
1695 // 3) On ARM, the reporting is done by funclets (if present). Otherwise, the primary method
1696 // does it.
1697 //
1698 // 4) x86 behaves like (1)
1699 //
1700 // For non-x86, the GcStackCrawlCallBack is invoked with a new flag indicating that
1701 // the stackwalk is being done for GC reporting purposes - this flag is GC_FUNCLET_REFERENCE_REPORTING.
1702 // The presence of this flag influences how the stackwalker will enumerate frames; which frames will
1703 // result in the callback being invoked; etc. The idea is that we want to report only the
1704 // relevant frames via the callback that are active on the callstack. This removes the need to
1705 // double report (even though JIT64 does it), reporting of dead frames, and makes the
1706 // design of reference reporting more consistent (and easier to understand) across architectures.
1707 //
1708 // The algorithm is as follows (at a conceptual level):
1709 //
1710 // 1) For each enumerated managed (frameless) frame, check if it is a funclet or not.
1711 // 1.1) If it is not a funclet, pass the frame to the callback and goto (2).
1712 // 1.2) If it is a funclet, we preserve the callerSP of the parent frame where the funclet was invoked from.
1713 // Pass the funclet to the callback.
1714 // 1.3) For filter funclets, we enumerate all frames until we reach the parent. Once the parent is reached,
1715 // pass it to the callback with a flag indicating that its corresponding funclet has already performed
1716 // the reporting.
1717 // 1.4) For non-filter funclets, we skip all the frames until we reach the parent. Once the parent is reached,
1718 // pass it to the callback with a flag indicating that its corresponding funclet has already performed
1719 // the reporting.
1720 // 1.5) If we see non-filter funclets while processing a filter funclet, then goto (1.4). Once we have reached the
1721 // parent of the non-filter funclet, resume filter funclet processing as described in (1.3).
1722 // 2) If another frame is enumerated, goto (1). Otherwise, stackwalk is complete.
1723 //
1724 // Note: When a flag is passed to the callback indicating that the funclet for a parent frame has already
1725 // reported the references, RyuJIT will simply do nothing and return from the callback.
1726 // JIT64, on the other hand, will ignore the flag and perform reporting (again).
1727 //
1728 // Note: For non-filter funclets there is a small window during unwind where we have conceptually unwound past a
1729 // funclet but have not yet reached the parent/handling frame. In this case we might need the parent to
1730 // report its GC roots. See comments around use of m_fDidFuncletReportGCReferences for more details.
1731 //
1732 // Needless to say, all applicable (read: active) explicit frames are also processed.
1733
1734 // Check if we are in the mode of enumerating GC references (or not)
1735 if (m_flags & GC_FUNCLET_REFERENCE_REPORTING)
1736 {
1737#ifdef FEATURE_PAL
1738 // For interleaved exception handling on non-windows systems, we need to find out if the current frame
1739 // was a caller of an already executed exception handler based on the previous exception trackers.
1740 // The handler funclet frames are already gone from the stack, so the exception trackers are the
1741 // only source of evidence about it.
1742 // This is different from Windows where the full stack is preserved until an exception is fully handled
1743 // and so we can detect it just from walking the stack.
1744 // The filter funclet frames are different, they behave the same way on Windows and Unix. They can be present
1745 // on the stack when we reach their parent frame if the filter hasn't finished running yet or they can be
1746 // gone if the filter completed running, either succesfully or with unhandled exception.
1747 // So the special handling below ignores trackers belonging to filter clauses.
1748 bool fProcessingFilterFunclet = !m_sfFuncletParent.IsNull() && !(m_fProcessNonFilterFunclet || m_fProcessIntermediaryNonFilterFunclet);
1749 if (!fRecheckCurrentFrame && !fSkippingFunclet && (pTracker != NULL) && !fProcessingFilterFunclet)
1750 {
1751 // The stack walker is not skipping frames now, which means it didn't find a funclet frame that
1752 // would require skipping the current frame. If we find a tracker with caller of actual handling
1753 // frame matching the current frame, it means that the funclet stack frame was reclaimed.
1754 StackFrame sfFuncletParent;
1755 ExceptionTracker* pCurrTracker = pTracker;
1756
1757 bool hasFuncletStarted = pTracker->GetEHClauseInfo()->IsManagedCodeEntered();
1758
1759 while (pCurrTracker != NULL)
1760 {
1761 // Ignore exception trackers for filter clauses, since their frames are handled the same way as on Windows
1762 if (pCurrTracker->GetEHClauseInfo()->GetClauseType() != COR_PRF_CLAUSE_FILTER)
1763 {
1764 if (hasFuncletStarted)
1765 {
1766 sfFuncletParent = pCurrTracker->GetCallerOfEnclosingClause();
1767 if (!sfFuncletParent.IsNull() && ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, sfFuncletParent))
1768 {
1769 break;
1770 }
1771 }
1772
1773 sfFuncletParent = pCurrTracker->GetCallerOfCollapsedEnclosingClause();
1774 if (!sfFuncletParent.IsNull() && ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, sfFuncletParent))
1775 {
1776 break;
1777 }
1778 }
1779
1780 // Funclets handling exception for trackers older than the current one were always started,
1781 // since the current tracker was created due to an exception in the funclet belonging to
1782 // the previous tracker.
1783 hasFuncletStarted = true;
1784 pCurrTracker = pCurrTracker->GetPreviousExceptionTracker();
1785 }
1786
1787 if (pCurrTracker != NULL)
1788 {
1789 // The current frame is a parent of a funclet that was already unwound and removed from the stack
1790 // Set the members the same way we would set them on Windows when we
1791 // would detect this just from stack walking.
1792 m_sfParent = sfFuncletParent;
1793 m_sfFuncletParent = sfFuncletParent;
1794 m_fProcessNonFilterFunclet = true;
1795 m_fDidFuncletReportGCReferences = false;
1796 fSkippingFunclet = true;
1797 }
1798 }
1799#endif // FEATURE_PAL
1800
1801 fRecheckCurrentFrame = false;
1802 // Do we already have a reference to a funclet parent?
1803 if (!m_sfFuncletParent.IsNull())
1804 {
1805 // Have we been processing a filter funclet without encountering any non-filter funclets?
1806 if ((m_fProcessNonFilterFunclet == false) && (m_fProcessIntermediaryNonFilterFunclet == false))
1807 {
1808 // Yes, we have. Check the current frame and if it is the parent we are looking for,
1809 // clear the flag indicating that its funclet has already reported the GC references (see
1810 // below comment for Dev11 376329 explaining why we do this).
1811 if (ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, m_sfFuncletParent))
1812 {
1813 STRESS_LOG2(LF_GCROOTS, LL_INFO100,
1814 "STACKWALK: Reached parent of filter funclet @ CallerSP: %p, m_crawl.pFunc = %p\n",
1815 m_sfFuncletParent.SP, m_crawl.pFunc);
1816
1817 // Dev11 376329 - ARM: GC hole during filter funclet dispatch.
1818 // Filters are invoked during the first pass so we cannot skip
1819 // reporting the parent frame since it's still live. Normally
1820 // this would cause double reporting, however for filters the JIT
1821 // will report all GC roots as pinned to alleviate this problem.
1822 // Note that JIT64 does not have this problem since it always
1823 // reports the parent frame (this flag is essentially ignored)
1824 // so it's safe to make this change for all (non-x86) architectures.
1825 m_crawl.fShouldParentToFuncletSkipReportingGCReferences = false;
1826 ResetGCRefReportingState();
1827
1828 // We have reached the parent of the filter funclet.
1829 // It is possible this is another funclet (e.g. a catch/fault/finally),
1830 // so reexamine this frame and see if it needs any skipping.
1831 fRecheckCurrentFrame = true;
1832 }
1833 else
1834 {
1835 // When processing filter funclets, until we reach the parent frame
1836 // we should be seeing only non--filter-funclet frames. This is because
1837 // exceptions cannot escape filter funclets. Thus, there can be no frameless frames
1838 // between the filter funclet and its parent.
1839 _ASSERTE(!m_crawl.IsFilterFunclet());
1840 if (m_crawl.IsFunclet())
1841 {
1842 // This is a non-filter funclet encountered when processing a filter funclet.
1843 // In such a case, we will deliver a callback for it and skip frames until we reach
1844 // its parent. Once there, we will resume frame enumeration for finding
1845 // parent of the filter funclet we were originally processing.
1846 m_sfIntermediaryFuncletParent = ExceptionTracker::FindParentStackFrameForStackWalk(&m_crawl, true);
1847 _ASSERTE(!m_sfIntermediaryFuncletParent.IsNull());
1848 m_fProcessIntermediaryNonFilterFunclet = true;
1849
1850 // Set the parent frame so that the funclet skipping logic (further below)
1851 // can use it.
1852 m_sfParent = m_sfIntermediaryFuncletParent;
1853 fSkipFuncletCallback = false;
1854 }
1855 }
1856 }
1857 }
1858 else
1859 {
1860 _ASSERTE(m_sfFuncletParent.IsNull());
1861
1862 // We don't have any funclet parent reference. Check if the current frame represents a funclet.
1863 if (m_crawl.IsFunclet())
1864 {
1865 // Get a reference to the funclet's parent frame.
1866 m_sfFuncletParent = ExceptionTracker::FindParentStackFrameForStackWalk(&m_crawl, true);
1867
1868 if (m_sfFuncletParent.IsNull())
1869 {
1870 // This can only happen if the funclet (and its parent) have been unwound.
1871 _ASSERTE(ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException(&m_crawl));
1872 }
1873 else
1874 {
1875 // We should have found the funclet's parent stackframe
1876 _ASSERTE(!m_sfFuncletParent.IsNull());
1877
1878 bool fIsFilterFunclet = m_crawl.IsFilterFunclet();
1879
1880 STRESS_LOG4(LF_GCROOTS, LL_INFO100,
1881 "STACKWALK: Found %sFilter funclet @ SP: %p, m_crawl.pFunc = %p; FuncletParentCallerSP: %p\n",
1882 (fIsFilterFunclet) ? "" : "Non-", GetRegdisplaySP(m_crawl.GetRegisterSet()), m_crawl.pFunc, m_sfFuncletParent.SP);
1883
1884 if (!fIsFilterFunclet)
1885 {
1886 m_fProcessNonFilterFunclet = true;
1887
1888 // Set the parent frame so that the funclet skipping logic (further below)
1889 // can use it.
1890 m_sfParent = m_sfFuncletParent;
1891
1892 // For non-filter funclets, we will make the callback for the funclet
1893 // but skip all the frames until we reach the parent method. When we do,
1894 // we will make a callback for it as well and then continue to make callbacks
1895 // for all upstack frames, until we reach another funclet or the top of the stack
1896 // is reached.
1897 fSkipFuncletCallback = false;
1898 }
1899 else
1900 {
1901 _ASSERTE(fIsFilterFunclet);
1902 m_fProcessNonFilterFunclet = false;
1903
1904 // Nothing more to do as we have come across a filter funclet. In this case, we will:
1905 //
1906 // 1) Get a reference to the parent frame
1907 // 2) Report the funclet
1908 // 3) Continue to report the parent frame, along with a flag that funclet has been reported (see above)
1909 // 4) Continue to report all upstack frames
1910 }
1911 }
1912 } // end if (m_crawl.IsFunclet())
1913 }
1914 } // end if (m_flags & GC_FUNCLET_REFERENCE_REPORTING)
1915 }
1916 while(fRecheckCurrentFrame == true);
1917
1918 if ((m_fProcessNonFilterFunclet == true) || (m_fProcessIntermediaryNonFilterFunclet == true) || (m_flags & (FUNCTIONSONLY | SKIPFUNCLETS)))
1919 {
1920 bool fSkipFrameDueToUnwind = false;
1921
1922 if (m_flags & GC_FUNCLET_REFERENCE_REPORTING)
1923 {
1924 // When a nested exception escapes, it will unwind past a funclet. In addition, it will
1925 // unwind the frame chain up to the funclet. When that happens, we'll basically lose
1926 // all the stack frames higher than and equal to the funclet. We can't skip funclets in
1927 // the usual way because the first frame we see won't be a funclet. It will be something
1928 // which has conceptually been unwound. We need to use the information on the
1929 // ExceptionTracker to determine if a stack frame is in the unwound stack region.
1930 //
1931 // If we are enumerating frames for GC reporting and we determined that
1932 // the current frame needs to be reported, ensure that it has not already
1933 // been unwound by the active exception. If it has been, then we will set a flag
1934 // indicating that its references need not be reported. The CrawlFrame, however,
1935 // will still be passed to the GC stackwalk callback in case it represents a dynamic
1936 // method, to allow the GC to keep that method alive.
1937 if (ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException(&m_crawl))
1938 {
1939 // Invoke the GC callback for this crawlframe (to keep any dynamic methods alive) but do not report its references.
1940 m_crawl.fShouldCrawlframeReportGCReferences = false;
1941 fSkipFrameDueToUnwind = true;
1942
1943 if (m_crawl.IsFunclet() && !fSkippingFunclet)
1944 {
1945 // we have come across a funclet that has been unwound and we haven't yet started to
1946 // look for its parent. in such a case, the funclet will not have anything to report
1947 // so set the corresponding flag to indicate so.
1948
1949 _ASSERTE(m_fDidFuncletReportGCReferences);
1950 m_fDidFuncletReportGCReferences = false;
1951
1952 STRESS_LOG0(LF_GCROOTS, LL_INFO100, "Unwound funclet will skip reporting references\n");
1953 }
1954 }
1955 }
1956 else if (m_flags & (FUNCTIONSONLY | SKIPFUNCLETS))
1957 {
1958 if (ExceptionTracker::IsInStackRegionUnwoundByCurrentException(&m_crawl))
1959 {
1960 // don't stop here
1961 fSkipFrameDueToUnwind = true;
1962 }
1963 }
1964
1965 if (fSkipFrameDueToUnwind)
1966 {
1967 if (m_flags & GC_FUNCLET_REFERENCE_REPORTING)
1968 {
1969 // Check if we are skipping frames.
1970 if (!m_sfParent.IsNull())
1971 {
1972 // Check if our have reached our target method frame.
1973 // IsMaxVal() is a special value to indicate that we should skip one frame.
1974 if (m_sfParent.IsMaxVal() ||
1975 ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, m_sfParent))
1976 {
1977 // Reset flag as we have reached target method frame so no more skipping required
1978 fSkippingFunclet = false;
1979
1980 // We've finished skipping as told. Now check again.
1981
1982 if ((m_fProcessIntermediaryNonFilterFunclet == true) || (m_fProcessNonFilterFunclet == true))
1983 {
1984 STRESS_LOG2(LF_GCROOTS, LL_INFO100,
1985 "STACKWALK: Reached parent of non-filter funclet @ CallerSP: %p, m_crawl.pFunc = %p\n",
1986 m_sfParent.SP, m_crawl.pFunc);
1987
1988 // landing here indicates that the funclet's parent has been unwound so
1989 // this will always be true, no need to predicate on the state of the funclet
1990 m_crawl.fShouldParentToFuncletSkipReportingGCReferences = true;
1991
1992 // we've reached the parent so reset our state
1993 m_fDidFuncletReportGCReferences = true;
1994
1995 ResetGCRefReportingState(m_fProcessIntermediaryNonFilterFunclet);
1996 }
1997
1998 m_sfParent.Clear();
1999
2000 if (m_crawl.IsFunclet())
2001 {
2002 // We've hit a funclet.
2003 // Since we are in GC reference reporting mode,
2004 // then avoid code duplication and go to
2005 // funclet processing.
2006 fRecheckCurrentFrame = true;
2007 goto ProcessFuncletsForGCReporting;
2008 }
2009 }
2010 }
2011 } // end if (m_flags & GC_FUNCLET_REFERENCE_REPORTING)
2012
2013 if (m_crawl.fShouldCrawlframeReportGCReferences)
2014 {
2015 // Skip the callback for this frame - we don't do this for unwound frames encountered
2016 // in GC stackwalk since they may represent dynamic methods whose resolver objects
2017 // the GC may need to keep alive.
2018 break;
2019 }
2020 }
2021 else
2022 {
2023 _ASSERTE(!fSkipFrameDueToUnwind);
2024
2025 // Check if we are skipping frames.
2026 if (!m_sfParent.IsNull())
2027 {
2028 // Check if we have reached our target method frame.
2029 // IsMaxVal() is a special value to indicate that we should skip one frame.
2030 if (m_sfParent.IsMaxVal() ||
2031 ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, m_sfParent))
2032 {
2033 // We've finished skipping as told. Now check again.
2034 if ((m_fProcessIntermediaryNonFilterFunclet == true) || (m_fProcessNonFilterFunclet == true))
2035 {
2036 // If we are here, we should be in GC reference reporting mode.
2037 _ASSERTE(m_flags & GC_FUNCLET_REFERENCE_REPORTING);
2038
2039 STRESS_LOG2(LF_GCROOTS, LL_INFO100,
2040 "STACKWALK: Reached parent of non-filter funclet @ CallerSP: %p, m_crawl.pFunc = %p\n",
2041 m_sfParent.SP, m_crawl.pFunc);
2042
2043 // by default a funclet's parent won't report its GC roots since they would have already
2044 // been reported by the funclet. however there is a small window during unwind before
2045 // control returns to the OS where we might require the parent to report. more below.
2046 bool shouldSkipReporting = true;
2047
2048 if (!m_fDidFuncletReportGCReferences)
2049 {
2050 // we have reached the parent frame of the funclet which didn't report roots since it was already unwound.
2051 // check if the parent frame of the funclet is also handling an exception. if it is, then we will need to
2052 // report roots for it since the catch handler may use references inside it.
2053
2054 STRESS_LOG0(LF_GCROOTS, LL_INFO100,
2055 "STACKWALK: Reached parent of funclet which didn't report GC roots, since funclet is already unwound.\n");
2056
2057 if (pTracker->GetCallerOfActualHandlingFrame() == m_sfFuncletParent)
2058 {
2059 // we should not skip reporting for this parent frame
2060 shouldSkipReporting = false;
2061
2062 // now that we've found the parent that will report roots reset our state.
2063 m_fDidFuncletReportGCReferences = true;
2064
2065 // After funclet gets unwound parent will begin to report gc references. Reporting GC references
2066 // using the IP of throw in parent method can crash application. Parent could have locals objects
2067 // which might not have been reported by funclet as live and would have already been collected
2068 // when funclet was on stack. Now if parent starts using IP of throw to report gc references it
2069 // would report garbage values as live objects. So instead parent can use the IP of the resume
2070 // address of catch funclet to report live GC references.
2071 m_crawl.fShouldParentFrameUseUnwindTargetPCforGCReporting = true;
2072 // Store catch clause info. Helps retrieve IP of resume address.
2073 m_crawl.ehClauseForCatch = pTracker->GetEHClauseForCatch();
2074
2075 STRESS_LOG3(LF_GCROOTS, LL_INFO100,
2076 "STACKWALK: Parent of funclet which didn't report GC roots is handling an exception at 0x%p"
2077 "(EH handler range [%x, %x) ), so we need to specially report roots to ensure variables alive"
2078 " in its handler stay live.\n",
2079 pTracker->GetCatchToCallPC(), m_crawl.ehClauseForCatch.HandlerStartPC,
2080 m_crawl.ehClauseForCatch.HandlerEndPC);
2081 }
2082 else if (!m_crawl.IsFunclet())
2083 {
2084 // we've reached the parent and it's not handling an exception, it's also not
2085 // a funclet so reset our state. note that we cannot reset the state when the
2086 // parent is a funclet since the leaf funclet didn't report any references and
2087 // we might have a catch handler below us that might contain GC roots.
2088 m_fDidFuncletReportGCReferences = true;
2089 }
2090
2091 STRESS_LOG4(LF_GCROOTS, LL_INFO100,
2092 "Funclet didn't report references: handling frame: %p, m_sfFuncletParent = %p, is funclet: %d, skip reporting %d\n",
2093 pTracker->GetEstablisherOfActualHandlingFrame().SP, m_sfFuncletParent.SP, m_crawl.IsFunclet(), shouldSkipReporting);
2094 }
2095 m_crawl.fShouldParentToFuncletSkipReportingGCReferences = shouldSkipReporting;
2096
2097 ResetGCRefReportingState(m_fProcessIntermediaryNonFilterFunclet);
2098 }
2099
2100 m_sfParent.Clear();
2101 }
2102 } // end if (!m_sfParent.IsNull())
2103
2104 if (m_sfParent.IsNull() && m_crawl.IsFunclet())
2105 {
2106 // We've hit a funclet.
2107 if (m_flags & GC_FUNCLET_REFERENCE_REPORTING)
2108 {
2109 // If we are in GC reference reporting mode,
2110 // then avoid code duplication and go to
2111 // funclet processing.
2112 fRecheckCurrentFrame = true;
2113 goto ProcessFuncletsForGCReporting;
2114 }
2115 else
2116 {
2117 // Start skipping frames.
2118 m_sfParent = ExceptionTracker::FindParentStackFrameForStackWalk(&m_crawl);
2119 }
2120
2121 // m_sfParent can be NULL if the current funclet is a filter,
2122 // in which case we shouldn't skip the frames.
2123 }
2124
2125 // If we're skipping frames due to a funclet on the stack
2126 // or this is an IL stub (which don't get reported when
2127 // FUNCTIONSONLY is set) we skip the callback.
2128 //
2129 // The only exception is the GC reference reporting mode -
2130 // for it, we will callback for the funclet so that references
2131 // are reported and then continue to skip all frames between the funclet
2132 // and its parent, eventually making a callback for the parent as well.
2133 if (m_flags & (FUNCTIONSONLY | SKIPFUNCLETS))
2134 {
2135 if (!m_sfParent.IsNull() || m_crawl.pFunc->IsILStub())
2136 {
2137 STRESS_LOG4(LF_GCROOTS, LL_INFO100,
2138 "STACKWALK: %s: not making callback for this frame, SPOfParent = %p, \
2139 isILStub = %d, m_crawl.pFunc = %pM\n",
2140 (!m_sfParent.IsNull() ? "SKIPPING_TO_FUNCLET_PARENT" : "IS_IL_STUB"),
2141 m_sfParent.SP,
2142 (m_crawl.pFunc->IsILStub() ? 1 : 0),
2143 m_crawl.pFunc);
2144
2145 // don't stop here
2146 break;
2147 }
2148 }
2149 else if (fSkipFuncletCallback && (m_flags & GC_FUNCLET_REFERENCE_REPORTING))
2150 {
2151 if (!m_sfParent.IsNull())
2152 {
2153 STRESS_LOG4(LF_GCROOTS, LL_INFO100,
2154 "STACKWALK: %s: not making callback for this frame, SPOfParent = %p, \
2155 isILStub = %d, m_crawl.pFunc = %pM\n",
2156 (!m_sfParent.IsNull() ? "SKIPPING_TO_FUNCLET_PARENT" : "IS_IL_STUB"),
2157 m_sfParent.SP,
2158 (m_crawl.pFunc->IsILStub() ? 1 : 0),
2159 m_crawl.pFunc);
2160
2161 // don't stop here
2162 break;
2163 }
2164 }
2165 }
2166 }
2167 else if (m_flags & GC_FUNCLET_REFERENCE_REPORTING)
2168 {
2169 // If we are enumerating frames for GC reporting and we determined that
2170 // the current frame needs to be reported, ensure that it has not already
2171 // been unwound by the active exception. If it has been, then we will
2172 // simply skip it and not deliver a callback for it.
2173 if (ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException(&m_crawl))
2174 {
2175 // Invoke the GC callback for this crawlframe (to keep any dynamic methods alive) but do not report its references.
2176 m_crawl.fShouldCrawlframeReportGCReferences = false;
2177 }
2178 }
2179
2180#else // WIN64EXCEPTIONS
2181 // Skip IL stubs
2182 if (m_flags & FUNCTIONSONLY)
2183 {
2184 if (m_crawl.pFunc->IsILStub())
2185 {
2186 LOG((LF_GCROOTS, LL_INFO100000,
2187 "STACKWALK: IS_IL_STUB: not making callback for this frame, m_crawl.pFunc = %s\n",
2188 m_crawl.pFunc->m_pszDebugMethodName));
2189
2190 // don't stop here
2191 break;
2192 }
2193 }
2194#endif // WIN64EXCEPTIONS
2195
2196 fStop = true;
2197 break;
2198
2199 case SFITER_FRAME_FUNCTION:
2200 //
2201 // fall through
2202 //
2203
2204 case SFITER_SKIPPED_FRAME_FUNCTION:
2205 if (!fSkippingFunclet)
2206 {
2207#if defined(WIN64EXCEPTIONS)
2208 if (m_flags & GC_FUNCLET_REFERENCE_REPORTING)
2209 {
2210 // If we are enumerating frames for GC reporting and we determined that
2211 // the current frame needs to be reported, ensure that it has not already
2212 // been unwound by the active exception. If it has been, then we will
2213 // simply skip it and not deliver a callback for it.
2214 if (ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException(&m_crawl))
2215 {
2216 // Invoke the GC callback for this crawlframe (to keep any dynamic methods alive) but do not report its references.
2217 m_crawl.fShouldCrawlframeReportGCReferences = false;
2218 }
2219 }
2220 else if (m_flags & (FUNCTIONSONLY | SKIPFUNCLETS))
2221 {
2222 // See the comment above for IsInStackRegionUnwoundByCurrentException().
2223 if (ExceptionTracker::IsInStackRegionUnwoundByCurrentException(&m_crawl))
2224 {
2225 // don't stop here
2226 break;
2227 }
2228 }
2229#endif // WIN64EXCEPTIONS
2230 if ( (m_crawl.pFunc != NULL) || !(m_flags & FUNCTIONSONLY) )
2231 {
2232 fStop = true;
2233 }
2234 }
2235 break;
2236
2237 case SFITER_NO_FRAME_TRANSITION:
2238 if (!fSkippingFunclet)
2239 {
2240 if (m_flags & NOTIFY_ON_NO_FRAME_TRANSITIONS)
2241 {
2242 _ASSERTE(m_crawl.isNoFrameTransition == true);
2243 fStop = true;
2244 }
2245 }
2246 break;
2247
2248 case SFITER_NATIVE_MARKER_FRAME:
2249 if (!fSkippingFunclet)
2250 {
2251 if (m_flags & NOTIFY_ON_U2M_TRANSITIONS)
2252 {
2253 _ASSERTE(m_crawl.isNativeMarker == true);
2254 fStop = true;
2255 }
2256 }
2257 break;
2258
2259 case SFITER_INITIAL_NATIVE_CONTEXT:
2260 if (!fSkippingFunclet)
2261 {
2262 if (m_flags & NOTIFY_ON_INITIAL_NATIVE_CONTEXT)
2263 {
2264 fStop = true;
2265 }
2266 }
2267 break;
2268
2269 default:
2270 UNREACHABLE();
2271 }
2272
2273 if (fStop)
2274 {
2275 break;
2276 }
2277 else
2278 {
2279 INDEBUG(m_crawl.pThread->DebugLogStackWalkInfo(&m_crawl, "FILTER ", m_uFramesProcessed));
2280 retVal = NextRaw();
2281 if (retVal != SWA_CONTINUE)
2282 {
2283 break;
2284 }
2285 }
2286 }
2287
2288 return retVal;
2289}
2290
2291//---------------------------------------------------------------------------------------
2292//
2293// Advance to the next frame and stop, regardless of the stackwalk flags.
2294//
2295// Return Value:
2296// SWA_CONTINUE (== SWA_DONE) if the iterator is successful in advancing to the next frame
2297// SWA_FAILED if an operation performed by the iterator fails
2298//
2299// Assumptions:
2300// The caller has checked that the iterator is valid.
2301//
2302// Notes:
2303// This function returns SWA_DONE when advancing from the last frame to becoming invalid.
2304//
2305
2306StackWalkAction StackFrameIterator::NextRaw(void)
2307{
2308 WRAPPER_NO_CONTRACT;
2309 SUPPORTS_DAC;
2310
2311 _ASSERTE(IsValid());
2312
2313 INDEBUG(m_uFramesProcessed++);
2314
2315 StackWalkAction retVal = SWA_CONTINUE;
2316
2317 if (m_frameState == SFITER_SKIPPED_FRAME_FUNCTION)
2318 {
2319#if !defined(_TARGET_X86_) && defined(_DEBUG)
2320 // make sure we're not skipping a different transition
2321 if (m_crawl.pFrame->NeedsUpdateRegDisplay())
2322 {
2323 CONSISTENCY_CHECK(m_crawl.pFrame->IsTransitionToNativeFrame());
2324 if (m_crawl.pFrame->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr())
2325 {
2326 // ControlPC may be different as the InlinedCallFrame stays active throughout
2327 // the STOP_FOR_GC callout but we can use the stack/frame pointer for the assert.
2328 PTR_InlinedCallFrame pICF = dac_cast<PTR_InlinedCallFrame>(m_crawl.pFrame);
2329 CONSISTENCY_CHECK((GetRegdisplaySP(m_crawl.pRD) == (TADDR)pICF->GetCallSiteSP())
2330 || (GetFP(m_crawl.pRD->pCurrentContext) == pICF->GetCalleeSavedFP()));
2331 }
2332 else
2333 {
2334 CONSISTENCY_CHECK(GetControlPC(m_crawl.pRD) == m_crawl.pFrame->GetReturnAddress());
2335 }
2336 }
2337#endif // !defined(_TARGET_X86_) && defined(_DEBUG)
2338
2339#if defined(STACKWALKER_MAY_POP_FRAMES)
2340 if (m_flags & POPFRAMES)
2341 {
2342 _ASSERTE(m_crawl.pFrame == m_crawl.pThread->GetFrame());
2343
2344 // If we got here, the current frame chose not to handle the
2345 // exception. Give it a chance to do any termination work
2346 // before we pop it off.
2347
2348 CLEAR_THREAD_TYPE_STACKWALKER();
2349 END_FORBID_TYPELOAD();
2350
2351 m_crawl.pFrame->ExceptionUnwind();
2352
2353 BEGIN_FORBID_TYPELOAD();
2354 SET_THREAD_TYPE_STACKWALKER(m_pThread);
2355
2356 // Pop off this frame and go on to the next one.
2357 m_crawl.GotoNextFrame();
2358
2359 // When StackWalkFramesEx is originally called, we ensure
2360 // that if POPFRAMES is set that the thread is in COOP mode
2361 // and that running thread is walking itself. Thus, this
2362 // COOP assertion is safe.
2363 BEGIN_GCX_ASSERT_COOP;
2364 m_crawl.pThread->SetFrame(m_crawl.pFrame);
2365 END_GCX_ASSERT_COOP;
2366 }
2367 else
2368#endif // STACKWALKER_MAY_POP_FRAMES
2369 {
2370 // go to the next frame
2371 m_crawl.GotoNextFrame();
2372 }
2373
2374 // check for skipped frames again
2375 if (CheckForSkippedFrames())
2376 {
2377 // there are more skipped explicit frames
2378 _ASSERTE(m_frameState == SFITER_SKIPPED_FRAME_FUNCTION);
2379 goto Cleanup;
2380 }
2381 else
2382 {
2383#ifndef PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
2384 // On x86, we process a managed stack frame before processing any explicit frames contained in it.
2385 // So when we are done with the skipped explicit frame, we have already processed the managed
2386 // stack frame, and it is time to move onto the next stack frame.
2387 PostProcessingForManagedFrames();
2388 if (m_frameState == SFITER_NATIVE_MARKER_FRAME)
2389 {
2390 goto Cleanup;
2391 }
2392#else // !PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
2393 // We are done handling the skipped explicit frame at this point. So move on to the
2394 // managed stack frame.
2395 m_crawl.isFrameless = true;
2396 m_crawl.codeInfo = m_cachedCodeInfo;
2397 m_crawl.pFunc = m_crawl.codeInfo.GetMethodDesc();
2398
2399
2400 PreProcessingForManagedFrames();
2401 goto Cleanup;
2402#endif // PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
2403 }
2404 }
2405 else if (m_frameState == SFITER_FRAMELESS_METHOD)
2406 {
2407 // Now find out if we need to leave monitors
2408
2409#ifdef _TARGET_X86_
2410 //
2411 // For non-x86 platforms, the JIT generates try/finally to leave monitors; for x86, the VM handles the monitor
2412 //
2413#if defined(STACKWALKER_MAY_POP_FRAMES)
2414 if (m_flags & POPFRAMES)
2415 {
2416 BEGIN_GCX_ASSERT_COOP;
2417
2418 if (m_crawl.pFunc->IsSynchronized())
2419 {
2420 MethodDesc * pMD = m_crawl.pFunc;
2421 OBJECTREF orUnwind = NULL;
2422
2423 if (m_crawl.GetCodeManager()->IsInSynchronizedRegion(m_crawl.GetRelOffset(),
2424 m_crawl.GetGCInfoToken(),
2425 m_crawl.GetCodeManagerFlags()))
2426 {
2427 if (pMD->IsStatic())
2428 {
2429 MethodTable * pMT = pMD->GetMethodTable();
2430 orUnwind = pMT->GetManagedClassObjectIfExists();
2431
2432 _ASSERTE(orUnwind != NULL);
2433 }
2434 else
2435 {
2436 orUnwind = m_crawl.GetCodeManager()->GetInstance(
2437 m_crawl.pRD,
2438 m_crawl.GetCodeInfo());
2439 }
2440
2441 _ASSERTE(orUnwind != NULL);
2442 VALIDATEOBJECTREF(orUnwind);
2443
2444 if (orUnwind != NULL)
2445 {
2446 orUnwind->LeaveObjMonitorAtException();
2447 }
2448 }
2449 }
2450
2451 END_GCX_ASSERT_COOP;
2452 }
2453#endif // STACKWALKER_MAY_POP_FRAMES
2454#endif // _TARGET_X86_
2455
2456#if !defined(ELIMINATE_FEF)
2457 // FaultingExceptionFrame is special case where it gets
2458 // pushed on the stack after the frame is running
2459 _ASSERTE((m_crawl.pFrame == FRAME_TOP) ||
2460 ((TADDR)GetRegdisplaySP(m_crawl.pRD) < dac_cast<TADDR>(m_crawl.pFrame)) ||
2461 (m_crawl.pFrame->GetVTablePtr() == FaultingExceptionFrame::GetMethodFrameVPtr()) ||
2462 (m_crawl.pFrame->GetVTablePtr() == ContextTransitionFrame::GetMethodFrameVPtr()));
2463#endif // !defined(ELIMINATE_FEF)
2464
2465 // Get rid of the frame (actually, it isn't really popped)
2466
2467 LOG((LF_GCROOTS, LL_EVERYTHING, "STACKWALK: [%03x] about to unwind for '%s', SP:" FMT_ADDR ", IP:" FMT_ADDR "\n",
2468 m_uFramesProcessed,
2469 m_crawl.pFunc->m_pszDebugMethodName,
2470 DBG_ADDR(GetRegdisplaySP(m_crawl.pRD)),
2471 DBG_ADDR(GetControlPC(m_crawl.pRD))));
2472
2473#if !defined(DACCESS_COMPILE) && defined(HAS_QUICKUNWIND)
2474 StackwalkCacheEntry *pCacheEntry = m_crawl.GetStackwalkCacheEntry();
2475 if (pCacheEntry != NULL)
2476 {
2477 _ASSERTE(m_crawl.stackWalkCache.Enabled() && (m_flags & LIGHTUNWIND));
2478
2479 // lightened schema: take stack unwind info from stackwalk cache
2480 EECodeManager::QuickUnwindStackFrame(m_crawl.pRD, pCacheEntry, EECodeManager::UnwindCurrentStackFrame);
2481 }
2482 else
2483#endif // !DACCESS_COMPILE && HAS_QUICKUNWIND
2484 {
2485#if !defined(DACCESS_COMPILE)
2486 // non-optimized stack unwind schema, doesn't use StackwalkCache
2487 UINT_PTR curSP = (UINT_PTR)GetRegdisplaySP(m_crawl.pRD);
2488 UINT_PTR curIP = (UINT_PTR)GetControlPC(m_crawl.pRD);
2489#endif // !DACCESS_COMPILE
2490
2491 bool fInsertCacheEntry = m_crawl.stackWalkCache.Enabled() &&
2492 (m_flags & LIGHTUNWIND) &&
2493 (m_pCachedGSCookie == NULL);
2494
2495 // Is this a dynamic method. Dynamic methods can be GC collected and so IP to method mapping
2496 // is not persistent. Therefore do not cache information for this frame.
2497 BOOL isCollectableMethod = ExecutionManager::IsCollectibleMethod(m_crawl.GetMethodToken());
2498 if(isCollectableMethod)
2499 fInsertCacheEntry = FALSE;
2500
2501 StackwalkCacheUnwindInfo unwindInfo;
2502
2503 if (!m_crawl.GetCodeManager()->UnwindStackFrame(
2504 m_crawl.pRD,
2505 &m_cachedCodeInfo,
2506 m_codeManFlags
2507 | m_crawl.GetCodeManagerFlags()
2508 | ((m_flags & PROFILER_DO_STACK_SNAPSHOT) ? SpeculativeStackwalk : 0),
2509 &m_crawl.codeManState,
2510 (fInsertCacheEntry ? &unwindInfo : NULL)))
2511 {
2512 LOG((LF_CORPROF, LL_INFO100, "**PROF: m_crawl.GetCodeManager()->UnwindStackFrame failure leads to SWA_FAILED.\n"));
2513 retVal = SWA_FAILED;
2514 goto Cleanup;
2515 }
2516
2517#if !defined(DACCESS_COMPILE)
2518 // store into hashtable if fits, otherwise just use old schema
2519 if (fInsertCacheEntry)
2520 {
2521 //
2522 // information we add to cache, consists of two parts:
2523 // 1. SPOffset - locals, etc. of current method, adding which to current ESP we get to retAddr ptr
2524 // 2. argSize - size of pushed function arguments, the rest we need to add to get new ESP
2525 // we have to store two parts of ESP delta, since we need to update pPC also, and so require retAddr ptr
2526 //
2527 // newSP = oldSP + SPOffset + sizeof(PTR) + argSize
2528 //
2529 UINT_PTR SPOffset = (UINT_PTR)GetRegdisplayStackMark(m_crawl.pRD) - curSP;
2530 UINT_PTR argSize = (UINT_PTR)GetRegdisplaySP(m_crawl.pRD) - curSP - SPOffset - sizeof(void*);
2531
2532 StackwalkCacheEntry cacheEntry = {0};
2533 if (cacheEntry.Init(
2534 curIP,
2535 SPOffset,
2536 &unwindInfo,
2537 argSize))
2538 {
2539 m_crawl.stackWalkCache.Insert(&cacheEntry);
2540 }
2541 }
2542#endif // !DACCESS_COMPILE
2543 }
2544
2545#define FAIL_IF_SPECULATIVE_WALK(condition) \
2546 if (m_flags & PROFILER_DO_STACK_SNAPSHOT) \
2547 { \
2548 if (!(condition)) \
2549 { \
2550 LOG((LF_CORPROF, LL_INFO100, "**PROF: " #condition " failure leads to SWA_FAILED.\n")); \
2551 retVal = SWA_FAILED; \
2552 goto Cleanup; \
2553 } \
2554 } \
2555 else \
2556 { \
2557 _ASSERTE(condition); \
2558 }
2559
2560 // When the stackwalk is seeded with a profiler context, the context
2561 // might be bogus. Check the stack pointer and the program counter for validity here.
2562 // (Note that these checks are not strictly necessary since we are able
2563 // to recover from AVs during profiler stackwalk.)
2564
2565 PTR_VOID newSP = PTR_VOID((TADDR)GetRegdisplaySP(m_crawl.pRD));
2566#ifndef NO_FIXED_STACK_LIMIT
2567 FAIL_IF_SPECULATIVE_WALK(newSP >= m_crawl.pThread->GetCachedStackLimit());
2568#endif // !NO_FIXED_STACK_LIMIT
2569 FAIL_IF_SPECULATIVE_WALK(newSP < m_crawl.pThread->GetCachedStackBase());
2570
2571#undef FAIL_IF_SPECULATIVE_WALK
2572
2573 LOG((LF_GCROOTS, LL_EVERYTHING, "STACKWALK: [%03x] finished unwind for '%s', SP:" FMT_ADDR \
2574 ", IP:" FMT_ADDR "\n",
2575 m_uFramesProcessed,
2576 m_crawl.pFunc->m_pszDebugMethodName,
2577 DBG_ADDR(GetRegdisplaySP(m_crawl.pRD)),
2578 DBG_ADDR(GetControlPC(m_crawl.pRD))));
2579
2580 m_crawl.isFirst = FALSE;
2581 m_crawl.isInterrupted = FALSE;
2582 m_crawl.hasFaulted = FALSE;
2583 m_crawl.isIPadjusted = FALSE;
2584
2585#ifndef PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
2586 // remember, x86 handles the managed stack frame before the explicit frames contained in it
2587 if (CheckForSkippedFrames())
2588 {
2589 _ASSERTE(m_frameState == SFITER_SKIPPED_FRAME_FUNCTION);
2590 goto Cleanup;
2591 }
2592#endif // !PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
2593
2594 PostProcessingForManagedFrames();
2595 if (m_frameState == SFITER_NATIVE_MARKER_FRAME)
2596 {
2597 goto Cleanup;
2598 }
2599 }
2600 else if (m_frameState == SFITER_FRAME_FUNCTION)
2601 {
2602 Frame* pInlinedFrame = NULL;
2603
2604 if (InlinedCallFrame::FrameHasActiveCall(m_crawl.pFrame))
2605 {
2606 pInlinedFrame = m_crawl.pFrame;
2607 }
2608
2609 unsigned uFrameAttribs = m_crawl.pFrame->GetFrameAttribs();
2610
2611 // Special resumable frames make believe they are on top of the stack.
2612 m_crawl.isFirst = (uFrameAttribs & Frame::FRAME_ATTR_RESUMABLE) != 0;
2613
2614 // If the frame is a subclass of ExceptionFrame,
2615 // then we know this is interrupted.
2616 m_crawl.isInterrupted = (uFrameAttribs & Frame::FRAME_ATTR_EXCEPTION) != 0;
2617
2618 if (m_crawl.isInterrupted)
2619 {
2620 m_crawl.hasFaulted = (uFrameAttribs & Frame::FRAME_ATTR_FAULTED) != 0;
2621 m_crawl.isIPadjusted = (uFrameAttribs & Frame::FRAME_ATTR_OUT_OF_LINE) != 0;
2622 _ASSERTE(!m_crawl.hasFaulted || !m_crawl.isIPadjusted); // both cant be set together
2623 }
2624
2625 //
2626 // Update app domain if this frame caused a transition.
2627 //
2628
2629 AppDomain *retDomain = m_crawl.pFrame->GetReturnDomain();
2630 if (retDomain != NULL)
2631 {
2632 m_crawl.pAppDomain = retDomain;
2633 }
2634
2635 PCODE adr = m_crawl.pFrame->GetReturnAddress();
2636 _ASSERTE(adr != (PCODE)POISONC);
2637
2638 _ASSERTE(!pInlinedFrame || adr);
2639
2640 if (adr)
2641 {
2642 ProcessIp(adr);
2643
2644 _ASSERTE(m_crawl.GetCodeInfo()->IsValid() || !pInlinedFrame);
2645
2646 if (m_crawl.isFrameless)
2647 {
2648 m_crawl.pFrame->UpdateRegDisplay(m_crawl.pRD);
2649
2650#if defined(RECORD_RESUMABLE_FRAME_SP)
2651 CONSISTENCY_CHECK(NULL == m_pvResumableFrameTargetSP);
2652
2653 if (m_crawl.isFirst)
2654 {
2655 if (m_flags & THREAD_IS_SUSPENDED)
2656 {
2657 _ASSERTE(m_crawl.isProfilerDoStackSnapshot);
2658
2659 // abort the stackwalk, we can't proceed without risking deadlock
2660 retVal = SWA_FAILED;
2661 goto Cleanup;
2662 }
2663
2664 // we are about to unwind, which may take a lock, so the thread
2665 // better not be suspended.
2666 CONSISTENCY_CHECK(!(m_flags & THREAD_IS_SUSPENDED));
2667
2668#if !defined(DACCESS_COMPILE)
2669 if (m_crawl.stackWalkCache.Enabled() && (m_flags & LIGHTUNWIND))
2670 {
2671 m_crawl.isCachedMethod = m_crawl.stackWalkCache.Lookup((UINT_PTR)adr);
2672 }
2673#endif // DACCESS_COMPILE
2674
2675 EECodeManager::EnsureCallerContextIsValid(m_crawl.pRD, m_crawl.GetStackwalkCacheEntry());
2676 m_pvResumableFrameTargetSP = (LPVOID)GetSP(m_crawl.pRD->pCallerContext);
2677 }
2678#endif // RECORD_RESUMABLE_FRAME_SP
2679
2680
2681#if defined(_DEBUG) && !defined(DACCESS_COMPILE) && !defined(WIN64EXCEPTIONS)
2682 // We are transitioning from unmanaged code to managed code... lets do some validation of our
2683 // EH mechanism on platforms that we can.
2684 VerifyValidTransitionFromManagedCode(m_crawl.pThread, &m_crawl);
2685#endif // _DEBUG && !DACCESS_COMPILE && !WIN64EXCEPTIONS
2686 }
2687 }
2688
2689 if (!pInlinedFrame)
2690 {
2691#if defined(STACKWALKER_MAY_POP_FRAMES)
2692 if (m_flags & POPFRAMES)
2693 {
2694 // If we got here, the current frame chose not to handle the
2695 // exception. Give it a chance to do any termination work
2696 // before we pop it off.
2697
2698 CLEAR_THREAD_TYPE_STACKWALKER();
2699 END_FORBID_TYPELOAD();
2700
2701 m_crawl.pFrame->ExceptionUnwind();
2702
2703 BEGIN_FORBID_TYPELOAD();
2704 SET_THREAD_TYPE_STACKWALKER(m_pThread);
2705
2706 // Pop off this frame and go on to the next one.
2707 m_crawl.GotoNextFrame();
2708
2709 // When StackWalkFramesEx is originally called, we ensure
2710 // that if POPFRAMES is set that the thread is in COOP mode
2711 // and that running thread is walking itself. Thus, this
2712 // COOP assertion is safe.
2713 BEGIN_GCX_ASSERT_COOP;
2714 m_crawl.pThread->SetFrame(m_crawl.pFrame);
2715 END_GCX_ASSERT_COOP;
2716 }
2717 else
2718#endif // STACKWALKER_MAY_POP_FRAMES
2719 {
2720 // Go to the next frame.
2721 m_crawl.GotoNextFrame();
2722 }
2723 }
2724 }
2725#if defined(ELIMINATE_FEF)
2726 else if (m_frameState == SFITER_NO_FRAME_TRANSITION)
2727 {
2728 PostProcessingForNoFrameTransition();
2729 }
2730#endif // ELIMINATE_FEF
2731 else if (m_frameState == SFITER_NATIVE_MARKER_FRAME)
2732 {
2733 m_crawl.isNativeMarker = false;
2734 }
2735 else if (m_frameState == SFITER_INITIAL_NATIVE_CONTEXT)
2736 {
2737 // nothing to do here
2738 }
2739 else
2740 {
2741 _ASSERTE(m_frameState == SFITER_UNINITIALIZED);
2742 _ASSERTE(!"StackFrameIterator::NextRaw() called when the iterator is uninitialized. \
2743 Should never get here.");
2744 retVal = SWA_FAILED;
2745 goto Cleanup;
2746 }
2747
2748 ProcessCurrentFrame();
2749
2750Cleanup:
2751#if defined(_DEBUG)
2752 if (retVal == SWA_FAILED)
2753 {
2754 LOG((LF_GCROOTS, LL_INFO10000, "STACKWALK: SWA_FAILED: couldn't start stackwalk\n"));
2755 }
2756#endif // _DEBUG
2757
2758 return retVal;
2759} // StackFrameIterator::NextRaw()
2760
2761//---------------------------------------------------------------------------------------
2762//
2763// Synchronizing the REGDISPLAY to the current CONTEXT stored in the REGDISPLAY.
2764// This is an nop on non-WIN64 platforms.
2765//
2766
2767void StackFrameIterator::UpdateRegDisp(void)
2768{
2769 WRAPPER_NO_CONTRACT;
2770 SUPPORTS_DAC;
2771
2772 WIN64_ONLY(SyncRegDisplayToCurrentContext(m_crawl.pRD));
2773} // StackFrameIterator::UpdateRegDisp()
2774
2775//---------------------------------------------------------------------------------------
2776//
2777// Check whether the specified Ip is in managed code and update the CrawlFrame accordingly.
2778// This function updates isFrameless, JitManagerInstance.
2779//
2780// Arguments:
2781// Ip - IP to be processed
2782//
2783
2784void StackFrameIterator::ProcessIp(PCODE Ip)
2785{
2786 CONTRACTL
2787 {
2788 NOTHROW;
2789 GC_NOTRIGGER;
2790 SO_TOLERANT;
2791 SUPPORTS_DAC;
2792 } CONTRACTL_END;
2793
2794 // Re-initialize codeInfo with new IP
2795 m_crawl.codeInfo.Init(Ip, m_scanFlag);
2796
2797 m_crawl.isFrameless = !!m_crawl.codeInfo.IsValid();
2798} // StackFrameIterator::ProcessIp()
2799
2800//---------------------------------------------------------------------------------------
2801//
2802// Update the CrawlFrame to represent where we have stopped. This is called after advancing
2803// to a new frame.
2804//
2805// Notes:
2806// This function and everything it calls must not rely on m_frameState, which could have become invalid
2807// when we advance the iterator before calling this function.
2808//
2809
2810void StackFrameIterator::ProcessCurrentFrame(void)
2811{
2812 WRAPPER_NO_CONTRACT;
2813 SUPPORTS_DAC;
2814
2815 bool fDone = false;
2816
2817 m_crawl.CheckGSCookies();
2818
2819 // Since we have advanced the iterator, the frame state represents the previous frame state,
2820 // not the current one. This is important to keep in mind. Ideally we should just assert that
2821 // the frame state has been set to invalid upon entry to this function, but we need the previous frame
2822 // state to decide if we should stop at an native stack frame.
2823
2824 // If we just do a simple check for native code here, we will loop forever.
2825 if (m_frameState == SFITER_UNINITIALIZED)
2826 {
2827 // "!IsFrameless()" normally implies that the CrawlFrame is at an explicit frame. Here we are using it
2828 // to detect whether the CONTEXT is in managed code or not. Ideally we should have a enum on the
2829 // CrawlFrame to indicate the various types of "frames" the CrawlFrame can stop at.
2830 //
2831 // If the CONTEXT is in native code and the StackFrameIterator is uninitialized, then it must be
2832 // an initial native CONTEXT passed to the StackFrameIterator when it is created or
2833 // when ResetRegDisp() is called.
2834 if (!m_crawl.IsFrameless())
2835 {
2836 m_frameState = SFITER_INITIAL_NATIVE_CONTEXT;
2837 fDone = true;
2838 }
2839 }
2840 else
2841 {
2842 // Clear the frame state. It will be set before we return from this function.
2843 m_frameState = SFITER_UNINITIALIZED;
2844 }
2845
2846 // Check for the case of an exception in managed code, and resync the stack walk
2847 // from the exception context.
2848#if defined(ELIMINATE_FEF)
2849 if (!fDone && !m_crawl.IsFrameless() && m_exInfoWalk.GetExInfo())
2850 {
2851 // We are currently walking ("lost") in unmanaged code. We can recover
2852 // from a) the next Frame record, or b) an exception context.
2853 // Recover from the exception context if all of these are true:
2854 // - it "returns" to managed code
2855 // - if is lower (newer) than the next Frame record
2856 // - the stack walk has not already passed by it
2857 //
2858 // The ExInfo walker is initialized to be higher than the pStartFrame, and
2859 // as we unwind managed (frameless) functions, we keep eliminating any
2860 // ExInfos that are passed in the stackwalk.
2861 //
2862 // So, here we need to find the next ExInfo that "returns" to managed code,
2863 // and then choose the lower of that ExInfo and the next Frame.
2864 m_exInfoWalk.WalkToManaged();
2865 TADDR pContextSP = m_exInfoWalk.GetSPFromContext();
2866
2867 //@todo: check the exception code for a fault?
2868
2869 // If there was a pContext that is higher than the SP and starting frame...
2870 if (pContextSP)
2871 {
2872 PTR_CONTEXT pContext = m_exInfoWalk.GetContext();
2873
2874 LOG((LF_EH, LL_INFO10000, "STACKWALK: considering resync from pContext(%p), fault(%08X), sp(%p); \
2875 pStartFrame(%p); cf.pFrame(%p), cf.SP(%p)\n",
2876 pContext, m_exInfoWalk.GetFault(), pContextSP,
2877 m_pStartFrame, dac_cast<TADDR>(m_crawl.pFrame), GetRegdisplaySP(m_crawl.pRD)));
2878
2879 // If the pContext is lower (newer) than the CrawlFrame's Frame*, try to use
2880 // the pContext.
2881 // There are still a few cases in which a FaultingExceptionFrame is linked in. If
2882 // the next frame is one of them, we don't want to override it. THIS IS PROBABLY BAD!!!
2883 if ( (pContextSP < dac_cast<TADDR>(m_crawl.pFrame)) &&
2884 ((m_crawl.GetFrame() == FRAME_TOP) ||
2885 (m_crawl.GetFrame()->GetVTablePtr() != FaultingExceptionFrame::GetMethodFrameVPtr() ) ) )
2886 {
2887 //
2888 // If the REGDISPLAY represents an unmanaged stack frame above (closer to the leaf than) an
2889 // ExInfo without any intervening managed stack frame, then we will stop at the no-frame
2890 // transition protected by the ExInfo. However, if the unmanaged stack frame is the one
2891 // immediately above the faulting managed stack frame, we want to continue the stackwalk
2892 // with the faulting managed stack frame. So we do not stop in this case.
2893 //
2894 // However, just comparing EBP is not enough. The OS exception handler
2895 // (KiUserExceptionDispatcher()) does not use an EBP frame. So if we just compare the EBP
2896 // we will think that the OS excpetion handler is the one we want to claim. Instead,
2897 // we should also check the current IP, which because of the way unwinding work and
2898 // how the OS exception handler behaves is actually going to be the stack limit of the
2899 // current thread. This is of course a workaround and is dependent on the OS behaviour.
2900 //
2901
2902 PCODE curPC = GetControlPC(m_crawl.pRD);
2903 if ((m_crawl.pRD->pEbp != NULL ) &&
2904 (m_exInfoWalk.GetEBPFromContext() == GetRegdisplayFP(m_crawl.pRD)) &&
2905 ((m_crawl.pThread->GetCachedStackLimit() <= PTR_VOID(curPC)) &&
2906 (PTR_VOID(curPC) < m_crawl.pThread->GetCachedStackBase())))
2907 {
2908 // restore the CONTEXT saved by the ExInfo and continue on to the faulting
2909 // managed stack frame
2910 PostProcessingForNoFrameTransition();
2911 }
2912 else
2913 {
2914 // we stop stop at the no-frame transition
2915 m_frameState = SFITER_NO_FRAME_TRANSITION;
2916 m_crawl.isNoFrameTransition = true;
2917 m_crawl.taNoFrameTransitionMarker = pContextSP;
2918 fDone = true;
2919 }
2920 }
2921 }
2922 }
2923#endif // defined(ELIMINATE_FEF)
2924
2925 if (!fDone)
2926 {
2927 // returns SWA_DONE if there is no more frames to walk
2928 if (!IsValid())
2929 {
2930 LOG((LF_GCROOTS, LL_INFO10000, "STACKWALK: SWA_DONE: reached the end of the stack\n"));
2931 m_frameState = SFITER_DONE;
2932 return;
2933 }
2934
2935 m_crawl.codeManState.dwIsSet = 0;
2936#if defined(_DEBUG)
2937 memset((void *)m_crawl.codeManState.stateBuf, 0xCD,
2938 sizeof(m_crawl.codeManState.stateBuf));
2939#endif // _DEBUG
2940
2941 if (m_crawl.isFrameless)
2942 {
2943 //------------------------------------------------------------------------
2944 // This must be a JITed/managed native method. There is no explicit frame.
2945 //------------------------------------------------------------------------
2946
2947#if !defined(DACCESS_COMPILE)
2948 m_crawl.isCachedMethod = FALSE;
2949 if (m_crawl.stackWalkCache.Enabled() && (m_flags & LIGHTUNWIND))
2950 {
2951 m_crawl.isCachedMethod = m_crawl.stackWalkCache.Lookup((UINT_PTR)GetControlPC(m_crawl.pRD));
2952 _ASSERTE (m_crawl.isCachedMethod != m_crawl.stackWalkCache.IsEmpty());
2953
2954 m_crawl.pSecurityObject = NULL;
2955#if defined(_TARGET_X86_)
2956 if (m_crawl.isCachedMethod && m_crawl.stackWalkCache.m_CacheEntry.HasSecurityObject())
2957 {
2958 // pCallback will use this to save time on GetAddrOfSecurityObject
2959 StackwalkCacheUnwindInfo stackwalkCacheUnwindInfo(&m_crawl.stackWalkCache.m_CacheEntry);
2960 m_crawl.pSecurityObject = EECodeManager::GetAddrOfSecurityObjectFromCachedInfo(
2961 m_crawl.pRD,
2962 &stackwalkCacheUnwindInfo);
2963 }
2964#endif // _TARGET_X86_
2965 }
2966#endif // DACCESS_COMPILE
2967
2968
2969#if defined(WIN64EXCEPTIONS)
2970 m_crawl.isFilterFuncletCached = false;
2971#endif // WIN64EXCEPTIONS
2972
2973 m_crawl.pFunc = m_crawl.codeInfo.GetMethodDesc();
2974
2975 // Cache values which may be updated by CheckForSkippedFrames()
2976 m_cachedCodeInfo = m_crawl.codeInfo;
2977
2978#ifdef PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
2979 // On non-X86, we want to process the skipped explicit frames before the managed stack frame
2980 // containing them.
2981 if (CheckForSkippedFrames())
2982 {
2983 _ASSERTE(m_frameState == SFITER_SKIPPED_FRAME_FUNCTION);
2984 }
2985 else
2986#endif // PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
2987 {
2988 PreProcessingForManagedFrames();
2989 _ASSERTE(m_frameState == SFITER_FRAMELESS_METHOD);
2990 }
2991 }
2992 else
2993 {
2994 INDEBUG(m_crawl.pThread->DebugLogStackWalkInfo(&m_crawl, "CONSIDER", m_uFramesProcessed));
2995
2996 _ASSERTE(m_crawl.pFrame != FRAME_TOP);
2997
2998 m_crawl.pFunc = m_crawl.pFrame->GetFunction();
2999
3000 m_frameState = SFITER_FRAME_FUNCTION;
3001 }
3002 }
3003
3004 _ASSERTE(m_frameState != SFITER_UNINITIALIZED);
3005} // StackFrameIterator::ProcessCurrentFrame()
3006
3007//---------------------------------------------------------------------------------------
3008//
3009// If an explicit frame is allocated in a managed stack frame (e.g. an inlined pinvoke call),
3010// we may have skipped an explicit frame. This function checks for them.
3011//
3012// Return Value:
3013// Returns true if there are skipped frames.
3014//
3015// Notes:
3016// x86 wants to stop at the skipped stack frames after the containing managed stack frame, but
3017// WIN64 wants to stop before. I don't think x86 actually has any good reason for this, except
3018// because it doesn't unwind one frame ahead of time like WIN64 does. This means that we don't
3019// have the caller SP on x86.
3020//
3021
3022BOOL StackFrameIterator::CheckForSkippedFrames(void)
3023{
3024 WRAPPER_NO_CONTRACT;
3025 SUPPORTS_DAC;
3026
3027 BOOL fHandleSkippedFrames = FALSE;
3028 TADDR pvReferenceSP;
3029
3030 // Can the caller handle skipped frames;
3031 fHandleSkippedFrames = (m_flags & HANDLESKIPPEDFRAMES);
3032
3033#ifndef PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
3034 pvReferenceSP = GetRegdisplaySP(m_crawl.pRD);
3035#else // !PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
3036 // Order the Frames relative to the caller SP of the methods
3037 // this makes it so that any Frame that is in a managed call
3038 // frame will be reported before its containing method.
3039
3040 // This should always succeed! If it doesn't, it's a bug somewhere else!
3041 EECodeManager::EnsureCallerContextIsValid(m_crawl.pRD, m_crawl.GetStackwalkCacheEntry(), &m_cachedCodeInfo);
3042 pvReferenceSP = GetSP(m_crawl.pRD->pCallerContext);
3043#endif // PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME
3044
3045 if ( !( (m_crawl.pFrame != FRAME_TOP) &&
3046 (dac_cast<TADDR>(m_crawl.pFrame) < pvReferenceSP) )
3047 )
3048 {
3049 return FALSE;
3050 }
3051
3052 LOG((LF_GCROOTS, LL_EVERYTHING, "STACKWALK: CheckForSkippedFrames\n"));
3053
3054 // We might have skipped past some Frames.
3055 // This happens with InlinedCallFrames and if we unwound
3056 // out of a finally in managed code or for ContextTransitionFrames
3057 // that are inserted into the managed call stack.
3058 while ( (m_crawl.pFrame != FRAME_TOP) &&
3059 (dac_cast<TADDR>(m_crawl.pFrame) < pvReferenceSP)
3060 )
3061 {
3062 BOOL fReportInteropMD =
3063 // If we see InlinedCallFrame in certain IL stubs, we should report the MD that
3064 // was passed to the stub as its secret argument. This is the true interop MD.
3065 // Note that code:InlinedCallFrame.GetFunction may return NULL in this case because
3066 // the call is made using the CALLI instruction.
3067 m_crawl.pFrame != FRAME_TOP &&
3068 m_crawl.pFrame->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr() &&
3069 m_crawl.pFunc != NULL &&
3070 m_crawl.pFunc->IsILStub() &&
3071 m_crawl.pFunc->AsDynamicMethodDesc()->HasMDContextArg();
3072
3073 if (fHandleSkippedFrames
3074#ifdef _TARGET_X86_
3075 || // On x86 we have already reported the InlinedCallFrame, don't report it again.
3076 (InlinedCallFrame::FrameHasActiveCall(m_crawl.pFrame) && !fReportInteropMD)
3077#endif // _TARGET_X86_
3078 )
3079 {
3080 m_crawl.GotoNextFrame();
3081#ifdef STACKWALKER_MAY_POP_FRAMES
3082 if (m_flags & POPFRAMES)
3083 {
3084 // When StackWalkFramesEx is originally called, we ensure
3085 // that if POPFRAMES is set that the thread is in COOP mode
3086 // and that running thread is walking itself. Thus, this
3087 // COOP assertion is safe.
3088 BEGIN_GCX_ASSERT_COOP;
3089 m_crawl.pThread->SetFrame(m_crawl.pFrame);
3090 END_GCX_ASSERT_COOP;
3091 }
3092#endif // STACKWALKER_MAY_POP_FRAMES
3093 }
3094 else
3095 {
3096 m_crawl.isFrameless = false;
3097
3098 if (fReportInteropMD)
3099 {
3100 m_crawl.pFunc = ((PTR_InlinedCallFrame)m_crawl.pFrame)->GetActualInteropMethodDesc();
3101 _ASSERTE(m_crawl.pFunc != NULL);
3102 _ASSERTE(m_crawl.pFunc->SanityCheck());
3103 }
3104 else
3105 {
3106 m_crawl.pFunc = m_crawl.pFrame->GetFunction();
3107 }
3108
3109 INDEBUG(m_crawl.pThread->DebugLogStackWalkInfo(&m_crawl, "CONSIDER", m_uFramesProcessed));
3110
3111 m_frameState = SFITER_SKIPPED_FRAME_FUNCTION;
3112 return TRUE;
3113 }
3114 }
3115
3116 return FALSE;
3117} // StackFrameIterator::CheckForSkippedFrames()
3118
3119//---------------------------------------------------------------------------------------
3120//
3121// Perform the necessary tasks before stopping at a managed stack frame. This is mostly validation work.
3122//
3123
3124void StackFrameIterator::PreProcessingForManagedFrames(void)
3125{
3126 WRAPPER_NO_CONTRACT;
3127 SUPPORTS_DAC;
3128
3129#if defined(RECORD_RESUMABLE_FRAME_SP)
3130 if (m_pvResumableFrameTargetSP)
3131 {
3132 // We expect that if we saw a resumable frame, the next managed
3133 // IP that we see will be the one the resumable frame took us to.
3134
3135 // However, because we might visit intervening explicit Frames
3136 // that will clear the .isFirst flag, we need to set it back here.
3137
3138 CONSISTENCY_CHECK(m_crawl.pRD->IsCallerContextValid);
3139 CONSISTENCY_CHECK((LPVOID)GetSP(m_crawl.pRD->pCallerContext) == m_pvResumableFrameTargetSP);
3140 m_pvResumableFrameTargetSP = NULL;
3141 m_crawl.isFirst = true;
3142 }
3143#endif // RECORD_RESUMABLE_FRAME_SP
3144
3145#if !defined(DACCESS_COMPILE)
3146 m_pCachedGSCookie = (GSCookie*)m_crawl.GetCodeManager()->GetGSCookieAddr(
3147 m_crawl.pRD,
3148 &m_crawl.codeInfo,
3149 &m_crawl.codeManState);
3150#endif // !DACCESS_COMPILE
3151
3152 if (!(m_flags & SKIP_GSCOOKIE_CHECK) && m_pCachedGSCookie)
3153 {
3154 m_crawl.SetCurGSCookie(m_pCachedGSCookie);
3155 }
3156
3157 INDEBUG(m_crawl.pThread->DebugLogStackWalkInfo(&m_crawl, "CONSIDER", m_uFramesProcessed));
3158
3159#if defined(_DEBUG) && !defined(WIN64EXCEPTIONS) && !defined(DACCESS_COMPILE)
3160 //
3161 // VM is responsible for synchronization on non-funclet EH model.
3162 //
3163 // m_crawl.GetThisPointer() requires full unwind
3164 // In GC's relocate phase, objects is not verifiable
3165 if ( !(m_flags & (LIGHTUNWIND | QUICKUNWIND | ALLOW_INVALID_OBJECTS)) &&
3166 m_crawl.pFunc->IsSynchronized() &&
3167 !m_crawl.pFunc->IsStatic() &&
3168 m_crawl.GetCodeManager()->IsInSynchronizedRegion(m_crawl.GetRelOffset(),
3169 m_crawl.GetGCInfoToken(),
3170 m_crawl.GetCodeManagerFlags()))
3171 {
3172 BEGIN_GCX_ASSERT_COOP;
3173
3174 OBJECTREF obj = m_crawl.GetThisPointer();
3175
3176 _ASSERTE(obj != NULL);
3177 VALIDATEOBJECTREF(obj);
3178
3179 DWORD threadId = 0;
3180 DWORD acquisitionCount = 0;
3181 _ASSERTE(obj->GetThreadOwningMonitorLock(&threadId, &acquisitionCount) &&
3182 (threadId == m_crawl.pThread->GetThreadId()));
3183
3184 END_GCX_ASSERT_COOP;
3185 }
3186#endif // _DEBUG && !WIN64EXCEPTIONS && !DACCESS_COMPILE
3187
3188 m_frameState = SFITER_FRAMELESS_METHOD;
3189} // StackFrameIterator::PreProcessingForManagedFrames()
3190
3191//---------------------------------------------------------------------------------------
3192//
3193// Perform the necessary tasks after stopping at a managed stack frame and unwinding to its caller.
3194// This includes advancing the ExInfo and checking whether the new IP is managed.
3195//
3196
3197void StackFrameIterator::PostProcessingForManagedFrames(void)
3198{
3199 CONTRACTL
3200 {
3201 NOTHROW;
3202 GC_NOTRIGGER;
3203 SO_TOLERANT;
3204 MODE_ANY;
3205 SUPPORTS_DAC;
3206 }
3207 CONTRACTL_END;
3208
3209
3210#if defined(ELIMINATE_FEF)
3211 // As with frames, we may have unwound past a ExInfo.pContext. This
3212 // can happen when unwinding from a handler that rethrew the exception.
3213 // Skip any ExInfo.pContext records that may no longer be valid.
3214 // If Frames would be unlinked from the Frame chain, also reset the UseExInfoForStackwalk bit
3215 // on the ExInfo.
3216 m_exInfoWalk.WalkToPosition(GetRegdisplaySP(m_crawl.pRD), (m_flags & POPFRAMES));
3217#endif // ELIMINATE_FEF
3218
3219 ProcessIp(GetControlPC(m_crawl.pRD));
3220
3221 // if we have unwound to a native stack frame, stop and set the frame state accordingly
3222 if (!m_crawl.isFrameless)
3223 {
3224 m_frameState = SFITER_NATIVE_MARKER_FRAME;
3225 m_crawl.isNativeMarker = true;
3226 }
3227} // StackFrameIterator::PostProcessingForManagedFrames()
3228
3229//---------------------------------------------------------------------------------------
3230//
3231// Perform the necessary tasks after stopping at a no-frame transition. This includes loading
3232// the CONTEXT stored in the ExInfo and updating the REGDISPLAY to the faulting managed stack frame.
3233//
3234
3235void StackFrameIterator::PostProcessingForNoFrameTransition()
3236{
3237 CONTRACTL
3238 {
3239 NOTHROW;
3240 GC_NOTRIGGER;
3241 SO_TOLERANT;
3242 MODE_ANY;
3243 SUPPORTS_DAC;
3244 }
3245 CONTRACTL_END;
3246
3247#if defined(ELIMINATE_FEF)
3248 PTR_CONTEXT pContext = m_exInfoWalk.GetContext();
3249
3250 // Get the JitManager for the managed address.
3251 m_crawl.codeInfo.Init(GetIP(pContext), m_scanFlag);
3252 _ASSERTE(m_crawl.codeInfo.IsValid());
3253
3254 STRESS_LOG4(LF_EH, LL_INFO100, "STACKWALK: resync from pContext(%p); pStartFrame(%p), \
3255 cf.pFrame(%p), cf.SP(%p)\n",
3256 dac_cast<TADDR>(pContext), dac_cast<TADDR>(m_pStartFrame), dac_cast<TADDR>(m_crawl.pFrame),
3257 GetRegdisplaySP(m_crawl.pRD));
3258
3259 // Update the RegDisplay from the context info.
3260 FillRegDisplay(m_crawl.pRD, pContext);
3261
3262 // Now we know where we are, and it's "frameless", aka managed.
3263 m_crawl.isFrameless = true;
3264
3265 // Flags the same as from a FaultingExceptionFrame.
3266 m_crawl.isInterrupted = 1;
3267 m_crawl.hasFaulted = 1;
3268 m_crawl.isIPadjusted = 0;
3269
3270#if defined(STACKWALKER_MAY_POP_FRAMES)
3271 // If Frames would be unlinked from the Frame chain, also reset the UseExInfoForStackwalk bit
3272 // on the ExInfo.
3273 if (m_flags & POPFRAMES)
3274 {
3275 m_exInfoWalk.GetExInfo()->m_ExceptionFlags.ResetUseExInfoForStackwalk();
3276 }
3277#endif // STACKWALKER_MAY_POP_FRAMES
3278
3279 // Done with this ExInfo.
3280 m_exInfoWalk.WalkOne();
3281
3282 m_crawl.isNoFrameTransition = false;
3283 m_crawl.taNoFrameTransitionMarker = NULL;
3284#endif // ELIMINATE_FEF
3285} // StackFrameIterator::PostProcessingForNoFrameTransition()
3286
3287
3288#if defined(_TARGET_AMD64_) && !defined(DACCESS_COMPILE)
3289static CrstStatic g_StackwalkCacheLock; // Global StackwalkCache lock; only used on AMD64
3290EXTERN_C void moveOWord(LPVOID src, LPVOID target);
3291#endif // _TARGET_AMD64_
3292
3293/*
3294 copies 64-bit *src to *target, atomically accessing the data
3295 requires 64-bit alignment for atomic load/store
3296*/
3297inline static void atomicMoveCacheEntry(UINT64* src, UINT64* target)
3298{
3299 LIMITED_METHOD_CONTRACT;
3300
3301#ifdef _TARGET_X86_
3302 // the most negative value is used a sort of integer infinity
3303 // value, so it have to be avoided
3304 _ASSERTE(*src != 0x8000000000000000);
3305 __asm
3306 {
3307 mov eax, src
3308 fild qword ptr [eax]
3309 mov eax, target
3310 fistp qword ptr [eax]
3311 }
3312#elif defined(_TARGET_AMD64_) && !defined(DACCESS_COMPILE)
3313 // On AMD64 there's no way to move 16 bytes atomically, so we need to take a lock before calling moveOWord().
3314 CrstHolder ch(&g_StackwalkCacheLock);
3315 moveOWord(src, target);
3316#endif
3317}
3318
3319/*
3320============================================================
3321Here is an implementation of StackwalkCache class, used to optimize performance
3322of stack walking. Currently each CrawlFrame has a StackwalkCache member, which implements
3323functionality for caching already walked methods (see Thread::StackWalkFramesEx).
3324See class and corresponding types declaration at stackwalktypes.h
3325We do use global cache g_StackwalkCache[] with InterlockCompareExchange, fitting
3326each cache entry into 8 bytes.
3327============================================================
3328*/
3329
3330#ifndef DACCESS_COMPILE
3331#define LOG_NUM_OF_CACHE_ENTRIES 10
3332#else
3333// Stack walk cache is disabled in DAC - save space
3334#define LOG_NUM_OF_CACHE_ENTRIES 0
3335#endif
3336#define NUM_OF_CACHE_ENTRIES (1 << LOG_NUM_OF_CACHE_ENTRIES)
3337
3338static StackwalkCacheEntry g_StackwalkCache[NUM_OF_CACHE_ENTRIES] = {}; // Global StackwalkCache
3339
3340#ifdef DACCESS_COMPILE
3341const BOOL StackwalkCache::s_Enabled = FALSE;
3342#else
3343BOOL StackwalkCache::s_Enabled = FALSE;
3344
3345/*
3346 StackwalkCache class constructor.
3347 Set "enable/disable optimization" flag according to registry key.
3348*/
3349StackwalkCache::StackwalkCache()
3350{
3351 CONTRACTL {
3352 NOTHROW;
3353 GC_NOTRIGGER;
3354 } CONTRACTL_END;
3355
3356 ClearEntry();
3357
3358 static BOOL stackwalkCacheEnableChecked = FALSE;
3359 if (!stackwalkCacheEnableChecked)
3360 {
3361 // We can enter this block on multiple threads because of racing.
3362 // However, that is OK since this operation is idempotent
3363
3364 s_Enabled = ((g_pConfig->DisableStackwalkCache() == 0) &&
3365 // disable cache if for some reason it is not aligned
3366 IS_ALIGNED((void*)&g_StackwalkCache[0], STACKWALK_CACHE_ENTRY_ALIGN_BOUNDARY));
3367 stackwalkCacheEnableChecked = TRUE;
3368 }
3369}
3370
3371#endif // #ifndef DACCESS_COMPILE
3372
3373// static
3374void StackwalkCache::Init()
3375{
3376#if defined(_TARGET_AMD64_) && !defined(DACCESS_COMPILE)
3377 g_StackwalkCacheLock.Init(CrstSecurityStackwalkCache, CRST_UNSAFE_ANYMODE);
3378#endif // _TARGET_AMD64_
3379}
3380
3381/*
3382 Returns efficient hash table key based on provided IP.
3383 CPU architecture dependent.
3384*/
3385inline unsigned StackwalkCache::GetKey(UINT_PTR IP)
3386{
3387 LIMITED_METHOD_CONTRACT;
3388 return (unsigned)(((IP >> LOG_NUM_OF_CACHE_ENTRIES) ^ IP) & (NUM_OF_CACHE_ENTRIES-1));
3389}
3390
3391/*
3392 Looks into cache and returns StackwalkCache entry, if current IP is cached.
3393 JIT team guarantees the same ESP offset for the same IPs for different call chains.
3394*/
3395BOOL StackwalkCache::Lookup(UINT_PTR IP)
3396{
3397 CONTRACTL {
3398 NOTHROW;
3399 GC_NOTRIGGER;
3400 } CONTRACTL_END;
3401
3402#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
3403 _ASSERTE(Enabled());
3404 _ASSERTE(IP);
3405
3406 unsigned hkey = GetKey(IP);
3407 _ASSERTE(IS_ALIGNED((void*)&g_StackwalkCache[hkey], STACKWALK_CACHE_ENTRY_ALIGN_BOUNDARY));
3408 // Don't care about m_CacheEntry access atomicity, since it's private to this
3409 // stackwalk/thread
3410 atomicMoveCacheEntry((UINT64*)&g_StackwalkCache[hkey], (UINT64*)&m_CacheEntry);
3411
3412#ifdef _DEBUG
3413 if (IP != m_CacheEntry.IP)
3414 {
3415 ClearEntry();
3416 }
3417#endif
3418
3419 return (IP == m_CacheEntry.IP);
3420#else // _TARGET_X86_
3421 return FALSE;
3422#endif // _TARGET_X86_
3423}
3424
3425/*
3426 Caches data provided for current IP.
3427*/
3428void StackwalkCache::Insert(StackwalkCacheEntry *pCacheEntry)
3429{
3430 CONTRACTL {
3431 NOTHROW;
3432 GC_NOTRIGGER;
3433 } CONTRACTL_END;
3434
3435 _ASSERTE(Enabled());
3436 _ASSERTE(pCacheEntry);
3437
3438 unsigned hkey = GetKey(pCacheEntry->IP);
3439 _ASSERTE(IS_ALIGNED((void*)&g_StackwalkCache[hkey], STACKWALK_CACHE_ENTRY_ALIGN_BOUNDARY));
3440 atomicMoveCacheEntry((UINT64*)pCacheEntry, (UINT64*)&g_StackwalkCache[hkey]);
3441}
3442
3443// static
3444void StackwalkCache::Invalidate(LoaderAllocator * pLoaderAllocator)
3445{
3446 CONTRACTL {
3447 NOTHROW;
3448 GC_NOTRIGGER;
3449 } CONTRACTL_END;
3450
3451 if (!s_Enabled)
3452 return;
3453
3454 /* Note that we could just flush the entries corresponding to
3455 pDomain if we wanted to get fancy. To keep things simple for now,
3456 we just invalidate everything
3457 */
3458
3459 ZeroMemory(PVOID(&g_StackwalkCache), sizeof(g_StackwalkCache));
3460}
3461
3462//----------------------------------------------------------------------------
3463//
3464// SetUpRegdisplayForStackWalk - set up Regdisplay for a stack walk
3465//
3466// Arguments:
3467// pThread - pointer to the managed thread to be crawled
3468// pContext - pointer to the context
3469// pRegdisplay - pointer to the REGDISPLAY to be filled
3470//
3471// Return Value:
3472// None
3473//
3474//----------------------------------------------------------------------------
3475void SetUpRegdisplayForStackWalk(Thread * pThread, T_CONTEXT * pContext, REGDISPLAY * pRegdisplay)
3476{
3477 CONTRACTL {
3478 NOTHROW;
3479 GC_NOTRIGGER;
3480 SUPPORTS_DAC;
3481 } CONTRACTL_END;
3482
3483 // @dbgtodo filter CONTEXT- The filter CONTEXT will be removed in V3.0.
3484 T_CONTEXT * pFilterContext = pThread->GetFilterContext();
3485 _ASSERTE(!(pFilterContext && ISREDIRECTEDTHREAD(pThread)));
3486
3487 if (pFilterContext != NULL)
3488 {
3489 FillRegDisplay(pRegdisplay, pFilterContext);
3490 }
3491 else
3492 {
3493 ZeroMemory(pContext, sizeof(*pContext));
3494 FillRegDisplay(pRegdisplay, pContext);
3495
3496 if (ISREDIRECTEDTHREAD(pThread))
3497 {
3498 pThread->GetFrame()->UpdateRegDisplay(pRegdisplay);
3499 }
3500 }
3501}
3502