1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | |
6 | /* This is a poor man's implementation of virtual methods. */ |
7 | /* The purpose of pCrawlFrame is to abstract (at least for the most common cases |
8 | from the fact that not all methods are "framed" (basically all methods in |
9 | "native" code are "unframed"). That way the job for the enumerator callbacks |
10 | becomes much simpler (i.e. more transparent and hopefully less error prone). |
11 | Two call-backs still need to distinguish between the two types: GC and exception. |
12 | Both of these call-backs need to do really different things; for frameless methods |
13 | they need to go through the codemanager and use the resp. apis. |
14 | |
15 | The reason for not implementing virtual methods on crawlFrame is solely because of |
16 | the way exception handling is implemented (it does a "long jump" and bypasses |
17 | the enumerator (stackWalker) when it finds a matching frame. By doing so couldn't |
18 | properly destruct the dynamically created instance of CrawlFrame. |
19 | */ |
20 | |
21 | #ifndef __stackwalk_h__ |
22 | #define __stackwalk_h__ |
23 | |
24 | #include "eetwain.h" |
25 | #include "stackwalktypes.h" |
26 | |
27 | class Frame; |
28 | class CrawlFrame; |
29 | class ICodeManager; |
30 | class IJitManager; |
31 | struct EE_ILEXCEPTION; |
32 | class AppDomain; |
33 | |
34 | // This define controls handling of faults in managed code. If it is defined, |
35 | // the exception is handled (retried, actually), with a FaultingExceptionFrame |
36 | // on the stack. The FEF is used for unwinding. If not defined, the unwinding |
37 | // uses the exception context. |
38 | #define USE_FEF // to mark where code needs to be changed to eliminate the FEF |
39 | #if defined(_TARGET_X86_) && !defined(FEATURE_PAL) |
40 | #undef USE_FEF // Turn off the FEF use on x86. |
41 | #define ELIMINATE_FEF |
42 | #else |
43 | #if defined(ELIMINATE_FEF) |
44 | #undef ELIMINATE_FEF |
45 | #endif |
46 | #endif // _TARGET_X86_ && !FEATURE_PAL |
47 | |
48 | #if defined(WIN64EXCEPTIONS) |
49 | #define RECORD_RESUMABLE_FRAME_SP |
50 | #endif |
51 | |
52 | //************************************************************************ |
53 | // Enumerate all functions. |
54 | //************************************************************************ |
55 | |
56 | /* This enumerator is meant to be used for the most common cases, i.e. to |
57 | enumerate just all the functions of the requested thread. It is just a |
58 | cover for the "real" enumerator. |
59 | */ |
60 | |
61 | StackWalkAction StackWalkFunctions(Thread * thread, PSTACKWALKFRAMESCALLBACK pCallback, VOID * pData); |
62 | |
63 | /*<TODO>@ISSUE: Maybe use a define instead?</TODO> |
64 | #define StackWalkFunctions(thread, callBack, userdata) thread->StackWalkFrames(METHODSONLY, (callBack),(userData)) |
65 | */ |
66 | |
67 | |
68 | class CrawlFrame |
69 | { |
70 | public: |
71 | |
72 | #ifdef _TARGET_X86_ |
73 | friend StackWalkAction TAStackCrawlCallBack(CrawlFrame* pCf, void* data); |
74 | #endif // _TARGET_X86_ |
75 | |
76 | //************************************************************************ |
77 | // Functions available for the callbacks (using the current pCrawlFrame) |
78 | //************************************************************************ |
79 | |
80 | /* Widely used/benign functions */ |
81 | |
82 | /* Is this a function? */ |
83 | /* Returns either a MethodDesc* or NULL for "non-function" frames */ |
84 | //<TODO>@TODO: what will it return for transition frames?</TODO> |
85 | |
86 | #ifdef FEATURE_INTERPRETER |
87 | MethodDesc *GetFunction(); |
88 | #else // FEATURE_INTERPRETER |
89 | inline MethodDesc *GetFunction() |
90 | { |
91 | LIMITED_METHOD_DAC_CONTRACT; |
92 | return pFunc; |
93 | } |
94 | #endif |
95 | |
96 | |
97 | Assembly *GetAssembly(); |
98 | |
99 | /* Returns either a Frame * (for "framed items) or |
100 | Returns NULL for frameless functions |
101 | */ |
102 | inline Frame* GetFrame() // will return NULL for "frameless methods" |
103 | { |
104 | LIMITED_METHOD_DAC_CONTRACT; |
105 | |
106 | if (isFrameless) |
107 | return NULL; |
108 | else |
109 | return pFrame; |
110 | } |
111 | |
112 | BOOL IsInCalleesFrames(LPVOID stackPointer); |
113 | |
114 | #ifndef DACCESS_COMPILE |
115 | /* Returns address of the securityobject stored in the current function (method?) |
116 | Returns NULL if |
117 | - not a function OR |
118 | - function (method?) hasn't reserved any room for it |
119 | (which is an error) |
120 | */ |
121 | OBJECTREF * GetAddrOfSecurityObject(); |
122 | #endif // DACCESS_COMPILE |
123 | |
124 | // Fetch the extra type argument passed in some cases |
125 | PTR_VOID GetParamTypeArg(); |
126 | |
127 | /* Returns the "this" pointer of the method of the current frame -- at least in some cases. |
128 | Returns NULL if the current frame does not have a method, or that method is not an instance method of a class type. |
129 | Otherwise, the semantics currently depend, unfortunately, on the architecture. On non-x86 architectures, |
130 | should only be called for methods where the generic instantiation context is found via the this pointer (so that |
131 | this information will be encoded in the GC Info). On x86, can be called for this case, or if the method |
132 | is synchronized. |
133 | */ |
134 | OBJECTREF GetThisPointer(); |
135 | |
136 | /* |
137 | Returns ambient Stack pointer for this crawlframe. |
138 | Must be a frameless method. |
139 | Returns NULL if not available (includes prolog + epilog). |
140 | This is safe to call on all methods, but it may return |
141 | garbage if the method does not have an ambient SP (eg, ebp-based methods). |
142 | x86 is the only platform using ambient SP. |
143 | */ |
144 | TADDR GetAmbientSPFromCrawlFrame(); |
145 | |
146 | void GetExactGenericInstantiations(Instantiation *pClassInst, |
147 | Instantiation *pMethodInst); |
148 | |
149 | /* Returns extra information required to reconstruct exact generic parameters, |
150 | if any. |
151 | Returns NULL if |
152 | - no extra information is required (i.e. the code is non-shared, which |
153 | you can tell from the MethodDesc) |
154 | - the extra information is not available (i.e. optimized away or codegen problem) |
155 | Returns a MethodTable if the pMD returned by GetFunction satisfies RequiresInstMethodTableArg, |
156 | and returns a MethodDesc if the pMD returned by GetFunction satisfies RequiresInstMethodDescArg. |
157 | These together carry the exact instantiation information. |
158 | */ |
159 | PTR_VOID GetExactGenericArgsToken(); |
160 | |
161 | inline CodeManState * GetCodeManState() { LIMITED_METHOD_DAC_CONTRACT; return & codeManState; } |
162 | /* |
163 | IF YOU USE ANY OF THE SUBSEQUENT FUNCTIONS, YOU NEED TO REALLY UNDERSTAND THE |
164 | STACK-WALKER (INCLUDING UNWINDING OF METHODS IN MANAGED NATIVE CODE)! |
165 | YOU ALSO NEED TO UNDERSTAND THAT THESE FUNCTIONS MIGHT CHANGE ON AN AS-NEED BASIS. |
166 | */ |
167 | |
168 | /* The rest are meant to be used only by the exception catcher and the GC call-back */ |
169 | |
170 | /* Is currently a frame available? */ |
171 | /* conceptually returns (GetFrame(pCrawlFrame) == NULL) |
172 | */ |
173 | inline bool IsFrameless() |
174 | { |
175 | LIMITED_METHOD_DAC_CONTRACT; |
176 | |
177 | return isFrameless; |
178 | } |
179 | |
180 | |
181 | /* Is it the current active (top-most) frame |
182 | */ |
183 | inline bool IsActiveFrame() |
184 | { |
185 | LIMITED_METHOD_DAC_CONTRACT; |
186 | |
187 | return isFirst; |
188 | } |
189 | |
190 | /* Is it the current active function (top-most frame) |
191 | asserts for non-functions, should be used for managed native code only |
192 | */ |
193 | inline bool IsActiveFunc() |
194 | { |
195 | LIMITED_METHOD_DAC_CONTRACT; |
196 | |
197 | return (pFunc && isFirst); |
198 | } |
199 | |
200 | /* Is it the current active function (top-most frame) |
201 | which faulted or threw an exception ? |
202 | asserts for non-functions, should be used for managed native code only |
203 | */ |
204 | bool IsInterrupted() |
205 | { |
206 | LIMITED_METHOD_DAC_CONTRACT; |
207 | |
208 | return (pFunc && isInterrupted /* && isFrameless?? */); |
209 | } |
210 | |
211 | /* Is it the current active function (top-most frame) which faulted ? |
212 | asserts for non-functions, should be used for managed native code only |
213 | */ |
214 | bool HasFaulted() |
215 | { |
216 | LIMITED_METHOD_DAC_CONTRACT; |
217 | |
218 | return (pFunc && hasFaulted /* && isFrameless?? */); |
219 | } |
220 | |
221 | /* Is this CrawlFrame just marking that we're in native code? |
222 | Such frames are only provided when the stackwalk is inited w/ NOTIFY_ON_U2M_TRANSITIONS. |
223 | The only use of these crawlframes is to get the Regdisplay. |
224 | */ |
225 | bool IsNativeMarker() |
226 | { |
227 | LIMITED_METHOD_DAC_CONTRACT; |
228 | return isNativeMarker; |
229 | } |
230 | |
231 | /* x86 does not always push a FaultingExceptionFrame on the stack when there is a native exception |
232 | (e.g. a breakpoint). In this case, it relies on the CONTEXT stored on the ExInfo to resume |
233 | the stackwalk at the managed stack frame which has faulted. |
234 | |
235 | This flag is set when the stackwalker is stopped at such a no-explicit-frame transition. Conceptually |
236 | this is just like stopping at a transition frame. Note that the stackwalker only stops at no-frame |
237 | transition if NOTIFY_ON_NO_FRAME_TRANSITIONS is set. |
238 | */ |
239 | bool IsNoFrameTransition() |
240 | { |
241 | LIMITED_METHOD_DAC_CONTRACT; |
242 | return isNoFrameTransition; |
243 | } |
244 | |
245 | // A no-frame transition is one protected by an ExInfo. It's an optimization on x86 to avoid pushing a |
246 | // FaultingExceptionFrame (FEF). Thus, for all intents and purposes, we should treat a no-frame |
247 | // transition as a FEF. This function returns a stack address for the no-frame transition to substitute |
248 | // as the frame address of a FEF. It's currently only used by the debugger stackwalker. |
249 | TADDR GetNoFrameTransitionMarker() |
250 | { |
251 | LIMITED_METHOD_DAC_CONTRACT; |
252 | return (isNoFrameTransition ? taNoFrameTransitionMarker : NULL); |
253 | } |
254 | |
255 | /* Has the IP been adjusted to a point where it is safe to do GC ? |
256 | (for OutOfLineThrownExceptionFrame) |
257 | asserts for non-functions, should be used for managed native code only |
258 | */ |
259 | bool IsIPadjusted() |
260 | { |
261 | LIMITED_METHOD_DAC_CONTRACT; |
262 | |
263 | return (pFunc && isIPadjusted /* && isFrameless?? */); |
264 | } |
265 | |
266 | /* Gets the ICodeMangerFlags for the current frame */ |
267 | |
268 | unsigned GetCodeManagerFlags() |
269 | { |
270 | CONTRACTL { |
271 | NOTHROW; |
272 | GC_NOTRIGGER; |
273 | SUPPORTS_DAC; |
274 | } CONTRACTL_END; |
275 | |
276 | unsigned flags = 0; |
277 | |
278 | if (IsActiveFunc()) |
279 | flags |= ActiveStackFrame; |
280 | |
281 | if (IsInterrupted()) |
282 | { |
283 | flags |= ExecutionAborted; |
284 | |
285 | if (!HasFaulted() && !IsIPadjusted()) |
286 | { |
287 | _ASSERTE(!(flags & ActiveStackFrame)); |
288 | flags |= AbortingCall; |
289 | } |
290 | } |
291 | |
292 | #if defined(WIN64EXCEPTIONS) |
293 | if (ShouldParentToFuncletSkipReportingGCReferences()) |
294 | { |
295 | flags |= ParentOfFuncletStackFrame; |
296 | } |
297 | #endif // defined(WIN64EXCEPTIONS) |
298 | |
299 | return flags; |
300 | } |
301 | |
302 | AppDomain *GetAppDomain() |
303 | { |
304 | LIMITED_METHOD_DAC_CONTRACT; |
305 | |
306 | return pAppDomain; |
307 | } |
308 | |
309 | /* Is this frame at a safe spot for GC? |
310 | */ |
311 | bool IsGcSafe(); |
312 | |
313 | #if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) |
314 | bool HasTailCalls(); |
315 | #endif // _TARGET_ARM_ || _TARGET_ARM64_ |
316 | |
317 | PREGDISPLAY GetRegisterSet() |
318 | { |
319 | LIMITED_METHOD_DAC_CONTRACT; |
320 | |
321 | // We would like to make the following assertion, but it is legitimately |
322 | // violated when we perform a crawl to find the return address for a hijack. |
323 | // _ASSERTE(isFrameless); |
324 | return pRD; |
325 | } |
326 | |
327 | EECodeInfo * GetCodeInfo() |
328 | { |
329 | LIMITED_METHOD_DAC_CONTRACT; |
330 | |
331 | // This assumes that CrawlFrame is host-only structure with DACCESS_COMPILE |
332 | // and thus it always returns the host address. |
333 | return &codeInfo; |
334 | } |
335 | |
336 | GCInfoToken GetGCInfoToken() |
337 | { |
338 | LIMITED_METHOD_DAC_CONTRACT; |
339 | _ASSERTE(isFrameless); |
340 | return codeInfo.GetGCInfoToken(); |
341 | } |
342 | |
343 | PTR_VOID GetGCInfo() |
344 | { |
345 | LIMITED_METHOD_DAC_CONTRACT; |
346 | _ASSERTE(isFrameless); |
347 | return codeInfo.GetGCInfo(); |
348 | } |
349 | |
350 | const METHODTOKEN& GetMethodToken() |
351 | { |
352 | LIMITED_METHOD_DAC_CONTRACT; |
353 | _ASSERTE(isFrameless); |
354 | return codeInfo.GetMethodToken(); |
355 | } |
356 | |
357 | unsigned GetRelOffset() |
358 | { |
359 | LIMITED_METHOD_DAC_CONTRACT; |
360 | _ASSERTE(isFrameless); |
361 | return codeInfo.GetRelOffset(); |
362 | } |
363 | |
364 | IJitManager* GetJitManager() |
365 | { |
366 | LIMITED_METHOD_DAC_CONTRACT; |
367 | _ASSERTE(isFrameless); |
368 | return codeInfo.GetJitManager(); |
369 | } |
370 | |
371 | ICodeManager* GetCodeManager() |
372 | { |
373 | LIMITED_METHOD_DAC_CONTRACT; |
374 | _ASSERTE(isFrameless); |
375 | return codeInfo.GetCodeManager(); |
376 | } |
377 | |
378 | inline StackwalkCacheEntry* GetStackwalkCacheEntry() |
379 | { |
380 | LIMITED_METHOD_CONTRACT; |
381 | _ASSERTE (isCachedMethod != stackWalkCache.IsEmpty()); |
382 | if (isCachedMethod && stackWalkCache.m_CacheEntry.IsSafeToUseCache()) |
383 | { |
384 | return &(stackWalkCache.m_CacheEntry); |
385 | } |
386 | else |
387 | { |
388 | return NULL; |
389 | } |
390 | } |
391 | |
392 | void CheckGSCookies(); |
393 | |
394 | #if defined(WIN64EXCEPTIONS) |
395 | bool IsFunclet() |
396 | { |
397 | WRAPPER_NO_CONTRACT; |
398 | |
399 | if (!IsFrameless()) |
400 | return false; |
401 | |
402 | return !!codeInfo.IsFunclet(); |
403 | } |
404 | |
405 | bool IsFilterFunclet(); |
406 | |
407 | // Indicates if the funclet has already reported GC |
408 | // references (or not). This will return true if |
409 | // we come across the parent frame of a funclet |
410 | // that is active on the stack. |
411 | bool ShouldParentToFuncletSkipReportingGCReferences() |
412 | { |
413 | LIMITED_METHOD_CONTRACT; |
414 | return fShouldParentToFuncletSkipReportingGCReferences; |
415 | } |
416 | |
417 | bool ShouldCrawlframeReportGCReferences() |
418 | { |
419 | LIMITED_METHOD_CONTRACT; |
420 | |
421 | return fShouldCrawlframeReportGCReferences; |
422 | } |
423 | |
424 | bool ShouldParentToFuncletUseUnwindTargetLocationForGCReporting() |
425 | { |
426 | LIMITED_METHOD_CONTRACT; |
427 | return fShouldParentFrameUseUnwindTargetPCforGCReporting; |
428 | } |
429 | |
430 | const EE_ILEXCEPTION_CLAUSE& GetEHClauseForCatch() |
431 | { |
432 | return ehClauseForCatch; |
433 | } |
434 | |
435 | #endif // WIN64EXCEPTIONS |
436 | |
437 | protected: |
438 | // CrawlFrames are temporarily created by the enumerator. |
439 | // Do not create one from C++. This protected constructor polices this rule. |
440 | CrawlFrame(); |
441 | |
442 | void SetCurGSCookie(GSCookie * pGSCookie); |
443 | |
444 | private: |
445 | |
446 | friend class Thread; |
447 | friend class EECodeManager; |
448 | friend class StackFrameIterator; |
449 | #ifdef WIN64EXCEPTIONS |
450 | friend class ExceptionTracker; |
451 | #endif // WIN64EXCEPTIONS |
452 | |
453 | CodeManState codeManState; |
454 | |
455 | bool isFrameless; |
456 | bool isFirst; |
457 | |
458 | // The next three fields are only valid for managed stack frames. They are set using attributes |
459 | // on explicit frames, and they are reset after processing each managed stack frame. |
460 | bool isInterrupted; |
461 | bool hasFaulted; |
462 | bool isIPadjusted; |
463 | |
464 | bool isNativeMarker; |
465 | bool isProfilerDoStackSnapshot; |
466 | bool isNoFrameTransition; |
467 | TADDR taNoFrameTransitionMarker; // see code:CrawlFrame.GetNoFrameTransitionMarker |
468 | PTR_Frame pFrame; |
469 | MethodDesc *pFunc; |
470 | |
471 | // the rest is only used for "frameless methods" |
472 | AppDomain *pAppDomain; |
473 | PREGDISPLAY pRD; // "thread context"/"virtual register set" |
474 | |
475 | EECodeInfo codeInfo; |
476 | #if defined(WIN64EXCEPTIONS) |
477 | bool isFilterFunclet; |
478 | bool isFilterFuncletCached; |
479 | bool fShouldParentToFuncletSkipReportingGCReferences; |
480 | bool fShouldCrawlframeReportGCReferences; |
481 | bool fShouldParentFrameUseUnwindTargetPCforGCReporting; |
482 | EE_ILEXCEPTION_CLAUSE ehClauseForCatch; |
483 | #endif //WIN64EXCEPTIONS |
484 | Thread* pThread; |
485 | |
486 | // fields used for stackwalk cache |
487 | OBJECTREF *pSecurityObject; |
488 | BOOL isCachedMethod; |
489 | StackwalkCache stackWalkCache; |
490 | |
491 | GSCookie *pCurGSCookie; |
492 | GSCookie *pFirstGSCookie; |
493 | |
494 | friend class Frame; // added to allow 'friend void CrawlFrame::GotoNextFrame();' declaration in class Frame, frames.h |
495 | void GotoNextFrame(); |
496 | }; |
497 | |
498 | void GcEnumObject(LPVOID pData, OBJECTREF *pObj); |
499 | StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData); |
500 | |
501 | #if defined(ELIMINATE_FEF) |
502 | //****************************************************************************** |
503 | // This class is used to help use exception context records to resync a |
504 | // stackwalk, when managed code has generated an exception (eg, AV, zerodiv.,,) |
505 | // Such an exception causes a transition from the managed code into unmanaged |
506 | // OS and runtime code, but without the benefit of any Frame. This code helps |
507 | // the stackwalker simulate the effect that such a frame would have. |
508 | // In particular, this class has methods to walk the chain of ExInfos, looking |
509 | // for records with pContext pointers with certain characteristics. The |
510 | // characteristics that are important are the location in the stack (ie, is a |
511 | // given pContext relevant at a particular point in the stack walk), and |
512 | // whether the pContext was generated in managed code. |
513 | //****************************************************************************** |
514 | class ExInfoWalker |
515 | { |
516 | public: |
517 | ExInfoWalker() : m_pExInfo(0) { SUPPORTS_DAC; } |
518 | void Init (ExInfo *pExInfo) { SUPPORTS_DAC; m_pExInfo = pExInfo; } |
519 | // Skip one ExInfo. |
520 | void WalkOne(); |
521 | // Attempt to find an ExInfo with a pContext that is higher (older) than |
522 | // a given minimum location. |
523 | void WalkToPosition(TADDR taMinimum, BOOL bPopFrames); |
524 | // Attempt to find an ExInfo with a pContext that has an IP in managed code. |
525 | void WalkToManaged(); |
526 | // Return current ExInfo's m_pContext, or NULL if no m_pExInfo. |
527 | PTR_CONTEXT GetContext() { SUPPORTS_DAC; return m_pExInfo ? m_pExInfo->m_pContext : NULL; } |
528 | // Useful to see if there is more on the ExInfo chain. |
529 | ExInfo* GetExInfo() { SUPPORTS_DAC; return m_pExInfo; } |
530 | |
531 | // helper functions for retrieving information from the exception CONTEXT |
532 | TADDR GetSPFromContext() |
533 | { |
534 | LIMITED_METHOD_CONTRACT; |
535 | SUPPORTS_DAC; |
536 | return dac_cast<TADDR>((m_pExInfo && m_pExInfo->m_pContext) ? GetSP(m_pExInfo->m_pContext) : PTR_NULL); |
537 | } |
538 | |
539 | TADDR GetEBPFromContext() |
540 | { |
541 | LIMITED_METHOD_CONTRACT; |
542 | SUPPORTS_DAC; |
543 | return dac_cast<TADDR>((m_pExInfo && m_pExInfo->m_pContext) ? GetFP(m_pExInfo->m_pContext) : PTR_NULL); |
544 | } |
545 | |
546 | DWORD GetFault() { SUPPORTS_DAC; return m_pExInfo ? m_pExInfo->m_pExceptionRecord->ExceptionCode : 0; } |
547 | |
548 | private: |
549 | ExInfo *m_pExInfo; |
550 | }; // class ExInfoWalker |
551 | #endif // ELIMINATE_FEF |
552 | |
553 | |
554 | //--------------------------------------------------------------------------------------- |
555 | // |
556 | // This iterator class walks the stack of a managed thread. Where the iterator stops depends on the |
557 | // stackwalk flags. |
558 | // |
559 | // Notes: |
560 | // This class works both in-process and out-of-process (e.g. DAC). |
561 | // |
562 | |
563 | class StackFrameIterator |
564 | { |
565 | public: |
566 | // This constructor is for the usage pattern of creating an uninitialized StackFrameIterator and then |
567 | // calling Init() on it. |
568 | StackFrameIterator(void); |
569 | |
570 | // This constructor is for the usage pattern of creating an initialized StackFrameIterator and then |
571 | // calling ResetRegDisp() on it. |
572 | StackFrameIterator(Thread * pThread, PTR_Frame pFrame, ULONG32 flags); |
573 | |
574 | // |
575 | // We should consider merging Init() and ResetRegDisp(). |
576 | // |
577 | |
578 | // Initialize the iterator. Note that the iterator has thread-affinity, |
579 | // and the stackwalk flags cannot be changed once the iterator is created. |
580 | BOOL Init(Thread * pThread, |
581 | PTR_Frame pFrame, |
582 | PREGDISPLAY pRegDisp, |
583 | ULONG32 flags); |
584 | |
585 | // Reset the iterator to the specified REGDISPLAY. The caller must ensure that the REGDISPLAY is valid. |
586 | BOOL ResetRegDisp(PREGDISPLAY pRegDisp, |
587 | bool fIsFirst); |
588 | |
589 | // @dbgtodo inspection - This function should be removed once the Windows debuggers stop using the old DAC API. |
590 | void SetIsFirstFrame(bool isFirst) |
591 | { |
592 | LIMITED_METHOD_CONTRACT; |
593 | m_crawl.isFirst = isFirst; |
594 | } |
595 | |
596 | // whether the iterator has reached the root of the stack or not |
597 | BOOL IsValid(void); |
598 | |
599 | // advance to the next frame according to the stackwalk flags |
600 | StackWalkAction Next(void); |
601 | |
602 | enum FrameState |
603 | { |
604 | SFITER_UNINITIALIZED, // uninitialized |
605 | SFITER_FRAMELESS_METHOD, // managed stack frame |
606 | SFITER_FRAME_FUNCTION, // explicit frame |
607 | SFITER_SKIPPED_FRAME_FUNCTION, // skipped explicit frame |
608 | SFITER_NO_FRAME_TRANSITION, // no-frame transition (currently used for ExInfo only) |
609 | SFITER_NATIVE_MARKER_FRAME, // the native stack frame immediately below (stack grows up) |
610 | // a managed stack region |
611 | SFITER_INITIAL_NATIVE_CONTEXT, // initial native seed CONTEXT |
612 | SFITER_DONE, // the iterator has reached the end of the stack |
613 | }; |
614 | FrameState GetFrameState() {LIMITED_METHOD_DAC_CONTRACT; return m_frameState;} |
615 | |
616 | CrawlFrame m_crawl; |
617 | |
618 | #if defined(_DEBUG) |
619 | // used in logging |
620 | UINT32 m_uFramesProcessed; |
621 | #endif // _DEBUG |
622 | |
623 | private: |
624 | // This is a helper for the two constructors. |
625 | void CommonCtor(Thread * pThread, PTR_Frame pFrame, ULONG32 flags); |
626 | |
627 | // Reset the CrawlFrame owned by the iterator. Used by both Init() and ResetRegDisp(). |
628 | void ResetCrawlFrame(void); |
629 | |
630 | // Check whether we should stop at the current frame given the stackwalk flags. |
631 | // If not, continue advancing to the next frame. |
632 | StackWalkAction Filter(void); |
633 | |
634 | // Advance to the next frame regardless of the stackwalk flags. This is used by Next() and Filter(). |
635 | StackWalkAction (void); |
636 | |
637 | // sync the REGDISPLAY to the current CONTEXT |
638 | void UpdateRegDisp(void); |
639 | |
640 | // Check whether the IP is managed code. This function updates the following fields on CrawlFrame: |
641 | // JitManagerInstance and isFrameless. |
642 | void ProcessIp(PCODE Ip); |
643 | |
644 | // Update the CrawlFrame to represent where we have stopped. |
645 | // This is called after advancing to a new frame. |
646 | void ProcessCurrentFrame(void); |
647 | |
648 | // If an explicit frame is allocated in a managed stack frame (e.g. an inlined pinvoke call), |
649 | // we may have skipped an explicit frame. This function checks for them. |
650 | BOOL CheckForSkippedFrames(void); |
651 | |
652 | // Perform the necessary tasks before stopping at a managed stack frame. This is mostly validation work. |
653 | void PreProcessingForManagedFrames(void); |
654 | |
655 | // Perform the necessary tasks after stopping at a managed stack frame and unwinding to its caller. |
656 | // This includes advancing the ExInfo and checking whether the new IP is managed. |
657 | void PostProcessingForManagedFrames(void); |
658 | |
659 | // Perform the necessary tasks after stopping at a no-frame transition. This includes loading |
660 | // the CONTEXT stored in the ExInfo and updating the REGDISPLAY to the faulting managed stack frame. |
661 | void PostProcessingForNoFrameTransition(void); |
662 | |
663 | #if defined(WIN64EXCEPTIONS) |
664 | void ResetGCRefReportingState(bool ResetOnlyIntermediaryState = false) |
665 | { |
666 | LIMITED_METHOD_CONTRACT; |
667 | |
668 | if (!ResetOnlyIntermediaryState) |
669 | { |
670 | m_sfFuncletParent = StackFrame(); |
671 | m_fProcessNonFilterFunclet = false; |
672 | } |
673 | |
674 | m_sfIntermediaryFuncletParent = StackFrame(); |
675 | m_fProcessIntermediaryNonFilterFunclet = false; |
676 | } |
677 | #endif // defined(WIN64EXCEPTIONS) |
678 | |
679 | // Iteration state. |
680 | FrameState m_frameState; |
681 | |
682 | // Initial state. Must be preserved for restarting. |
683 | Thread * m_pThread; // Thread on which to walk. |
684 | |
685 | PTR_Frame m_pStartFrame; // Frame* passed to Init |
686 | |
687 | // This is the real starting explicit frame. If m_pStartFrame is NULL, |
688 | // then this is equal to m_pThread->GetFrame(). Otherwise this is equal to m_pStartFrame. |
689 | INDEBUG(PTR_Frame m_pRealStartFrame); |
690 | |
691 | ULONG32 m_flags; // StackWalkFrames flags. |
692 | ICodeManagerFlags m_codeManFlags; |
693 | ExecutionManager::ScanFlag m_scanFlag; |
694 | |
695 | // the following fields are used to cache information about a managed stack frame |
696 | // when we need to stop for skipped explicit frames |
697 | EECodeInfo m_cachedCodeInfo; |
698 | |
699 | GSCookie * m_pCachedGSCookie; |
700 | |
701 | #if defined(ELIMINATE_FEF) |
702 | ExInfoWalker m_exInfoWalk; |
703 | #endif // ELIMINATE_FEF |
704 | |
705 | #if defined(WIN64EXCEPTIONS) |
706 | // used in funclet-skipping |
707 | StackFrame m_sfParent; |
708 | |
709 | // Used in GC reference enumeration mode |
710 | StackFrame m_sfFuncletParent; |
711 | bool m_fProcessNonFilterFunclet; |
712 | StackFrame m_sfIntermediaryFuncletParent; |
713 | bool m_fProcessIntermediaryNonFilterFunclet; |
714 | bool m_fDidFuncletReportGCReferences; |
715 | #endif // WIN64EXCEPTIONS |
716 | |
717 | #if defined(RECORD_RESUMABLE_FRAME_SP) |
718 | LPVOID m_pvResumableFrameTargetSP; |
719 | #endif // RECORD_RESUMABLE_FRAME_SP |
720 | }; |
721 | |
722 | void SetUpRegdisplayForStackWalk(Thread * pThread, T_CONTEXT * pContext, REGDISPLAY * pRegdisplay); |
723 | |
724 | #endif |
725 | |