summaryrefslogtreecommitdiff
path: root/src/vm/stackwalk.h
blob: a30f51407d4f63cf187aaa323d424cc029efea63 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.


/* This is a poor man's implementation of virtual methods. */
/* The purpose of pCrawlFrame is to abstract (at least for the most common cases
   from the fact that not all methods are "framed" (basically all methods in
   "native" code are "unframed"). That way the job for the enumerator callbacks
   becomes much simpler (i.e. more transparent and hopefully less error prone).
   Two call-backs still need to distinguish between the two types: GC and exception.
   Both of these call-backs need to do really different things; for frameless methods
   they need to go through the codemanager and use the resp. apis.

   The reason for not implementing virtual methods on crawlFrame is solely because of
   the way exception handling is implemented (it does a "long jump" and bypasses
   the enumerator (stackWalker) when it finds a matching frame. By doing so couldn't
   properly destruct the dynamically created instance of CrawlFrame.
*/

#ifndef __stackwalk_h__
#define __stackwalk_h__

#include "eetwain.h"
#include "stackwalktypes.h"

class Frame;
class CrawlFrame;
class ICodeManager;
class IJitManager;
struct EE_ILEXCEPTION;
class AppDomain;

// This define controls handling of faults in managed code.  If it is defined,
//  the exception is handled (retried, actually), with a FaultingExceptionFrame
//  on the stack.  The FEF is used for unwinding.  If not defined, the unwinding
//  uses the exception context.
#define USE_FEF // to mark where code needs to be changed to eliminate the FEF
#if defined(_TARGET_X86_) && !defined(FEATURE_PAL)
 #undef USE_FEF // Turn off the FEF use on x86.
 #define ELIMINATE_FEF
#else
 #if defined(ELIMINATE_FEF)
  #undef ELIMINATE_FEF
 #endif 
#endif // _TARGET_X86_ && !FEATURE_PAL

#if defined(WIN64EXCEPTIONS)
#define RECORD_RESUMABLE_FRAME_SP
#endif

//************************************************************************
// Enumerate all functions.
//************************************************************************

/* This enumerator is meant to be used for the most common cases, i.e. to
   enumerate just all the functions of the requested thread. It is just a
   cover for the "real" enumerator.
 */

StackWalkAction StackWalkFunctions(Thread * thread, PSTACKWALKFRAMESCALLBACK pCallback, VOID * pData);

/*<TODO>@ISSUE: Maybe use a define instead?</TODO>
#define StackWalkFunctions(thread, callBack, userdata) thread->StackWalkFrames(METHODSONLY, (callBack),(userData))
*/


class CrawlFrame
{
public:

#ifdef _TARGET_X86_
    friend StackWalkAction TAStackCrawlCallBack(CrawlFrame* pCf, void* data);
#endif // _TARGET_X86_

    //************************************************************************
    // Functions available for the callbacks (using the current pCrawlFrame)
    //************************************************************************

    /* Widely used/benign functions */

    /* Is this a function? */
    /* Returns either a MethodDesc* or NULL for "non-function" frames */
            //<TODO>@TODO: what will it return for transition frames?</TODO>

#ifdef FEATURE_INTERPRETER
    MethodDesc *GetFunction();
#else // FEATURE_INTERPRETER
    inline MethodDesc *GetFunction()
    {
        LIMITED_METHOD_DAC_CONTRACT;
        return pFunc;
    }
#endif
 

    Assembly *GetAssembly();

    /* Returns either a Frame * (for "framed items) or
       Returns NULL for frameless functions
     */
    inline Frame* GetFrame()       // will return NULL for "frameless methods"
    {
        LIMITED_METHOD_DAC_CONTRACT;

        if (isFrameless)
            return NULL;
        else
            return pFrame;
    }

    BOOL IsInCalleesFrames(LPVOID stackPointer);

#ifndef DACCESS_COMPILE
    /* Returns address of the securityobject stored in the current function (method?)
       Returns NULL if
            - not a function OR
            - function (method?) hasn't reserved any room for it
              (which is an error)
     */
    OBJECTREF * GetAddrOfSecurityObject();
#endif // DACCESS_COMPILE

    // Fetch the extra type argument passed in some cases
    PTR_VOID GetParamTypeArg();

    /* Returns the "this" pointer of the method of the current frame -- at least in some cases.
       Returns NULL if the current frame does not have a method, or that method is not an instance method of a class type.
       Otherwise, the semantics currently depend, unfortunately, on the architecture.  On non-x86 architectures,
       should only be called for methods where the generic instantiation context is found via the this pointer (so that
       this information will be encoded in the GC Info).  On x86, can be called for this case, or if the method
       is synchronized.
     */
    OBJECTREF GetThisPointer();

    /*
        Returns ambient Stack pointer for this crawlframe. 
        Must be a frameless method.
        Returns NULL if not available (includes prolog + epilog).
        This is safe to call on all methods, but it may return 
        garbage if the method does not have an ambient SP (eg, ebp-based methods). 
        x86 is the only platform using ambient SP.        
    */
    TADDR GetAmbientSPFromCrawlFrame();

    void GetExactGenericInstantiations(Instantiation *pClassInst, 
                                       Instantiation *pMethodInst);

    /* Returns extra information required to reconstruct exact generic parameters,
       if any. 
       Returns NULL if
            - no extra information is required (i.e. the code is non-shared, which 
              you can tell from the MethodDesc)
            - the extra information is not available (i.e. optimized away or codegen problem)
       Returns a MethodTable if the pMD returned by GetFunction satisfies RequiresInstMethodTableArg,
       and returns a MethodDesc if the pMD returned by GetFunction satisfies RequiresInstMethodDescArg.
       These together carry the exact instantiation information.
     */
    PTR_VOID GetExactGenericArgsToken();

    inline CodeManState * GetCodeManState() { LIMITED_METHOD_DAC_CONTRACT; return & codeManState; }
    /*
       IF YOU USE ANY OF THE SUBSEQUENT FUNCTIONS, YOU NEED TO REALLY UNDERSTAND THE
       STACK-WALKER (INCLUDING UNWINDING OF METHODS IN MANAGED NATIVE CODE)!
       YOU ALSO NEED TO UNDERSTAND THAT THESE FUNCTIONS MIGHT CHANGE ON AN AS-NEED BASIS.
     */

    /* The rest are meant to be used only by the exception catcher and the GC call-back  */

    /* Is currently a frame available? */
    /* conceptually returns (GetFrame(pCrawlFrame) == NULL)
     */
    inline bool IsFrameless()
    {
        LIMITED_METHOD_DAC_CONTRACT;

        return isFrameless;
    }


    /* Is it the current active (top-most) frame 
     */
    inline bool IsActiveFrame()
    {
        LIMITED_METHOD_DAC_CONTRACT;

        return isFirst;
    }

    /* Is it the current active function (top-most frame)
       asserts for non-functions, should be used for managed native code only
     */
    inline bool IsActiveFunc()
    {
        LIMITED_METHOD_DAC_CONTRACT;

        return (pFunc && isFirst);
    }

    /* Is it the current active function (top-most frame)
       which faulted or threw an exception ?
       asserts for non-functions, should be used for managed native code only
     */
    bool IsInterrupted()
    {
        LIMITED_METHOD_DAC_CONTRACT;

        return (pFunc && isInterrupted /* && isFrameless?? */);
    }

    /* Is it the current active function (top-most frame) which faulted ?
       asserts for non-functions, should be used for managed native code only
     */
    bool HasFaulted()
    {
        LIMITED_METHOD_DAC_CONTRACT;

        return (pFunc && hasFaulted /* && isFrameless?? */);
    }

    /* Is this CrawlFrame just marking that we're in native code?
       Such frames are only provided when the stackwalk is inited w/ NOTIFY_ON_U2M_TRANSITIONS.
       The only use of these crawlframes is to get the Regdisplay.
     */
    bool IsNativeMarker()
    {
        LIMITED_METHOD_DAC_CONTRACT;
        return isNativeMarker;
    }

    /* x86 does not always push a FaultingExceptionFrame on the stack when there is a native exception 
       (e.g. a breakpoint).  In this case, it relies on the CONTEXT stored on the ExInfo to resume
       the stackwalk at the managed stack frame which has faulted.  
       
       This flag is set when the stackwalker is stopped at such a no-explicit-frame transition.  Conceptually 
       this is just like stopping at a transition frame.  Note that the stackwalker only stops at no-frame 
       transition if NOTIFY_ON_NO_FRAME_TRANSITIONS is set.
     */
    bool IsNoFrameTransition()
    {
        LIMITED_METHOD_DAC_CONTRACT;
        return isNoFrameTransition;
    }

    // A no-frame transition is one protected by an ExInfo.  It's an optimization on x86 to avoid pushing a 
    // FaultingExceptionFrame (FEF).  Thus, for all intents and purposes, we should treat a no-frame 
    // transition as a FEF.  This function returns a stack address for the no-frame transition to substitute 
    // as the frame address of a FEF.  It's currently only used by the debugger stackwalker.
    TADDR GetNoFrameTransitionMarker()
    {
        LIMITED_METHOD_DAC_CONTRACT;
        return (isNoFrameTransition ? taNoFrameTransitionMarker : NULL);
    }

    /* Has the IP been adjusted to a point where it is safe to do GC ?
       (for OutOfLineThrownExceptionFrame)
       asserts for non-functions, should be used for managed native code only
     */
    bool IsIPadjusted()
    {
        LIMITED_METHOD_DAC_CONTRACT;

        return (pFunc && isIPadjusted /* && isFrameless?? */);
    }

    /* Gets the ICodeMangerFlags for the current frame */

    unsigned GetCodeManagerFlags()
    {
        CONTRACTL {
            NOTHROW;
            GC_NOTRIGGER;
            SUPPORTS_DAC;
        } CONTRACTL_END;

        unsigned flags = 0;

        if (IsActiveFunc())
            flags |= ActiveStackFrame;

        if (IsInterrupted())
        {
            flags |= ExecutionAborted;

            if (!HasFaulted() && !IsIPadjusted())
            {
                _ASSERTE(!(flags & ActiveStackFrame));
                flags |= AbortingCall;
            }
        }

#if defined(WIN64EXCEPTIONS)
        if (ShouldParentToFuncletSkipReportingGCReferences())
        {
            flags |= ParentOfFuncletStackFrame;
        }
#endif // defined(WIN64EXCEPTIONS)

        return flags;
    }

    AppDomain *GetAppDomain()
    {
        LIMITED_METHOD_DAC_CONTRACT;

        return pAppDomain;
    }

    /* Is this frame at a safe spot for GC?
     */
    bool IsGcSafe();

#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
    bool HasTailCalls();
#endif // _TARGET_ARM_ || _TARGET_ARM64_

    PREGDISPLAY GetRegisterSet()
    {
        LIMITED_METHOD_DAC_CONTRACT;

        // We would like to make the following assertion, but it is legitimately
        // violated when we perform a crawl to find the return address for a hijack.
        // _ASSERTE(isFrameless);
        return pRD;
    }

    EECodeInfo * GetCodeInfo()
    {
        LIMITED_METHOD_DAC_CONTRACT;

        // This assumes that CrawlFrame is host-only structure with DACCESS_COMPILE
        // and thus it always returns the host address.
        return &codeInfo;
    }

    GCInfoToken GetGCInfoToken()
    {
        LIMITED_METHOD_DAC_CONTRACT;
        _ASSERTE(isFrameless);
        return codeInfo.GetGCInfoToken();
    }

    PTR_VOID GetGCInfo()
    {
        LIMITED_METHOD_DAC_CONTRACT;
        _ASSERTE(isFrameless);
        return codeInfo.GetGCInfo();
    }

    const METHODTOKEN& GetMethodToken()
    {
        LIMITED_METHOD_DAC_CONTRACT;
        _ASSERTE(isFrameless);
        return codeInfo.GetMethodToken();
    }

    unsigned GetRelOffset()
    {
        LIMITED_METHOD_DAC_CONTRACT;
        _ASSERTE(isFrameless);
        return codeInfo.GetRelOffset();
    }

    IJitManager*  GetJitManager()
    {
        LIMITED_METHOD_DAC_CONTRACT;
        _ASSERTE(isFrameless);
        return codeInfo.GetJitManager();
    }

    ICodeManager* GetCodeManager()
    {
        LIMITED_METHOD_DAC_CONTRACT;
        _ASSERTE(isFrameless);
        return codeInfo.GetCodeManager();
    }

    inline StackwalkCacheEntry* GetStackwalkCacheEntry()
    {
        LIMITED_METHOD_CONTRACT;
        _ASSERTE (isCachedMethod != stackWalkCache.IsEmpty());
        if (isCachedMethod && stackWalkCache.m_CacheEntry.IsSafeToUseCache())
        {
            return &(stackWalkCache.m_CacheEntry);
        }
        else
        {
            return NULL;
        }
    }

    void CheckGSCookies();

#if defined(WIN64EXCEPTIONS)
    bool IsFunclet()
    {
        WRAPPER_NO_CONTRACT;

        if (!IsFrameless())
            return false;

        return !!codeInfo.IsFunclet();
    }

    bool IsFilterFunclet();

    // Indicates if the funclet has already reported GC
    // references (or not). This will return true if
    // we come across the parent frame of a funclet
    // that is active on the stack.
    bool ShouldParentToFuncletSkipReportingGCReferences()
    {
        LIMITED_METHOD_CONTRACT;
        return fShouldParentToFuncletSkipReportingGCReferences;
    }
    
    bool ShouldCrawlframeReportGCReferences()
    {
        LIMITED_METHOD_CONTRACT;

        return fShouldCrawlframeReportGCReferences;
    }
    
    bool ShouldParentToFuncletUseUnwindTargetLocationForGCReporting()
    {
        LIMITED_METHOD_CONTRACT;
        return fShouldParentFrameUseUnwindTargetPCforGCReporting;
    }

    const EE_ILEXCEPTION_CLAUSE& GetEHClauseForCatch()
    {
        return ehClauseForCatch;
    }

#endif // WIN64EXCEPTIONS

protected:
    // CrawlFrames are temporarily created by the enumerator.
    // Do not create one from C++. This protected constructor polices this rule.
    CrawlFrame();

    void SetCurGSCookie(GSCookie * pGSCookie);

private:

    friend class Thread;
    friend class EECodeManager;
    friend class StackFrameIterator;
#ifdef WIN64EXCEPTIONS
    friend class ExceptionTracker;
#endif // WIN64EXCEPTIONS

    CodeManState      codeManState;

    bool              isFrameless;
    bool              isFirst;

    // The next three fields are only valid for managed stack frames.  They are set using attributes 
    // on explicit frames, and they are reset after processing each managed stack frame.
    bool              isInterrupted;
    bool              hasFaulted;
    bool              isIPadjusted;

    bool              isNativeMarker;
    bool              isProfilerDoStackSnapshot;
    bool              isNoFrameTransition;
    TADDR             taNoFrameTransitionMarker;    // see code:CrawlFrame.GetNoFrameTransitionMarker
    PTR_Frame         pFrame;
    MethodDesc       *pFunc;

    // the rest is only used for "frameless methods"
    AppDomain        *pAppDomain;
    PREGDISPLAY       pRD; // "thread context"/"virtual register set"

    EECodeInfo        codeInfo;
#if defined(WIN64EXCEPTIONS)
    bool              isFilterFunclet;
    bool              isFilterFuncletCached;
    bool              fShouldParentToFuncletSkipReportingGCReferences;
    bool              fShouldCrawlframeReportGCReferences;
    bool              fShouldParentFrameUseUnwindTargetPCforGCReporting;
    EE_ILEXCEPTION_CLAUSE ehClauseForCatch;
#endif //WIN64EXCEPTIONS
    Thread*           pThread;

    // fields used for stackwalk cache
    OBJECTREF         *pSecurityObject;
    BOOL              isCachedMethod;
    StackwalkCache    stackWalkCache;

    GSCookie         *pCurGSCookie;
    GSCookie         *pFirstGSCookie;

    friend class Frame; // added to allow 'friend void CrawlFrame::GotoNextFrame();' declaration in class Frame, frames.h
    void GotoNextFrame();
};

void GcEnumObject(LPVOID pData, OBJECTREF *pObj);
StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData);

#if defined(ELIMINATE_FEF)
//******************************************************************************
// This class is used to help use exception context records to resync a 
//  stackwalk, when managed code has generated an exception (eg, AV, zerodiv.,,)
// Such an exception causes a transition from the managed code into unmanaged
//  OS and runtime code, but without the benefit of any Frame.  This code helps
//  the stackwalker simulate the effect that such a frame would have.
// In particular, this class has methods to walk the chain of ExInfos, looking
//  for records with pContext pointers with certain characteristics.  The 
//  characteristics that are important are the location in the stack (ie, is a
//  given pContext relevant at a particular point in the stack walk), and 
//  whether the pContext was generated in managed code.
//******************************************************************************
class ExInfoWalker
{
public:
    ExInfoWalker() : m_pExInfo(0) { SUPPORTS_DAC; }
    void Init (ExInfo *pExInfo) { SUPPORTS_DAC; m_pExInfo = pExInfo; }
    // Skip one ExInfo.
    void WalkOne();
    // Attempt to find an ExInfo with a pContext that is higher (older) than
    //  a given minimum location.
    void WalkToPosition(TADDR taMinimum, BOOL bPopFrames);
    // Attempt to find an ExInfo with a pContext that has an IP in managed code.
    void WalkToManaged();
    // Return current ExInfo's m_pContext, or NULL if no m_pExInfo.
    PTR_CONTEXT GetContext() { SUPPORTS_DAC; return m_pExInfo ? m_pExInfo->m_pContext : NULL; }
    // Useful to see if there is more on the ExInfo chain.
    ExInfo* GetExInfo() { SUPPORTS_DAC; return m_pExInfo; }

    // helper functions for retrieving information from the exception CONTEXT
    TADDR GetSPFromContext() 
    {
        LIMITED_METHOD_CONTRACT;
        SUPPORTS_DAC;  
        return dac_cast<TADDR>((m_pExInfo && m_pExInfo->m_pContext) ? GetSP(m_pExInfo->m_pContext) : PTR_NULL); 
    }

    TADDR GetEBPFromContext() 
    {
        LIMITED_METHOD_CONTRACT;
        SUPPORTS_DAC;  
        return dac_cast<TADDR>((m_pExInfo && m_pExInfo->m_pContext) ? GetFP(m_pExInfo->m_pContext) : PTR_NULL); 
    }

    DWORD GetFault() { SUPPORTS_DAC; return m_pExInfo ? m_pExInfo->m_pExceptionRecord->ExceptionCode : 0; }

private:
    ExInfo      *m_pExInfo;
};  // class ExInfoWalker
#endif // ELIMINATE_FEF


//---------------------------------------------------------------------------------------
//
// This iterator class walks the stack of a managed thread.  Where the iterator stops depends on the 
// stackwalk flags. 
//
// Notes:
//    This class works both in-process and out-of-process (e.g. DAC).
//

class StackFrameIterator
{
public:
    // This constructor is for the usage pattern of creating an uninitialized StackFrameIterator and then
    // calling Init() on it.
    StackFrameIterator(void);

    // This constructor is for the usage pattern of creating an initialized StackFrameIterator and then
    // calling ResetRegDisp() on it.
    StackFrameIterator(Thread * pThread, PTR_Frame pFrame, ULONG32 flags);

    //
    // We should consider merging Init() and ResetRegDisp().
    // 

    // Initialize the iterator.  Note that the iterator has thread-affinity, 
    // and the stackwalk flags cannot be changed once the iterator is created.
    BOOL Init(Thread *      pThread,
              PTR_Frame     pFrame,
              PREGDISPLAY   pRegDisp,
              ULONG32       flags);

    // Reset the iterator to the specified REGDISPLAY.  The caller must ensure that the REGDISPLAY is valid.
    BOOL ResetRegDisp(PREGDISPLAY pRegDisp, 
                      bool        fIsFirst);

    // @dbgtodo  inspection - This function should be removed once the Windows debuggers stop using the old DAC API.
    void SetIsFirstFrame(bool isFirst)
    {
        LIMITED_METHOD_CONTRACT;
        m_crawl.isFirst = isFirst;
    }

    // whether the iterator has reached the root of the stack or not
    BOOL IsValid(void);

    // advance to the next frame according to the stackwalk flags
    StackWalkAction Next(void);

    enum FrameState
    {
        SFITER_UNINITIALIZED,               // uninitialized
        SFITER_FRAMELESS_METHOD,            // managed stack frame
        SFITER_FRAME_FUNCTION,              // explicit frame
        SFITER_SKIPPED_FRAME_FUNCTION,      // skipped explicit frame
        SFITER_NO_FRAME_TRANSITION,         // no-frame transition (currently used for ExInfo only)
        SFITER_NATIVE_MARKER_FRAME,         // the native stack frame immediately below (stack grows up)
                                            // a managed stack region
        SFITER_INITIAL_NATIVE_CONTEXT,      // initial native seed CONTEXT 
        SFITER_DONE,                        // the iterator has reached the end of the stack
    };
    FrameState GetFrameState() {LIMITED_METHOD_DAC_CONTRACT; return m_frameState;}

    CrawlFrame m_crawl;

#if defined(_DEBUG)
    // used in logging
    UINT32 m_uFramesProcessed;
#endif // _DEBUG
    
private:
    // This is a helper for the two constructors.
    void CommonCtor(Thread * pThread, PTR_Frame pFrame, ULONG32 flags);

    // Reset the CrawlFrame owned by the iterator.  Used by both Init() and ResetRegDisp().
    void ResetCrawlFrame(void);

    // Check whether we should stop at the current frame given the stackwalk flags.  
    // If not, continue advancing to the next frame.
    StackWalkAction Filter(void);

    // Advance to the next frame regardless of the stackwalk flags.  This is used by Next() and Filter().
    StackWalkAction NextRaw(void);

    // sync the REGDISPLAY to the current CONTEXT
    void UpdateRegDisp(void);

    // Check whether the IP is managed code.  This function updates the following fields on CrawlFrame:
    // JitManagerInstance and isFrameless.
    void ProcessIp(PCODE Ip);

    // Update the CrawlFrame to represent where we have stopped.  
    // This is called after advancing to a new frame.
    void ProcessCurrentFrame(void);

    // If an explicit frame is allocated in a managed stack frame (e.g. an inlined pinvoke call),
    // we may have skipped an explicit frame.  This function checks for them.
    BOOL CheckForSkippedFrames(void);

    // Perform the necessary tasks before stopping at a managed stack frame.  This is mostly validation work.
    void PreProcessingForManagedFrames(void);

    // Perform the necessary tasks after stopping at a managed stack frame and unwinding to its caller.
    // This includes advancing the ExInfo and checking whether the new IP is managed.
    void PostProcessingForManagedFrames(void);

    // Perform the necessary tasks after stopping at a no-frame transition.  This includes loading 
    // the CONTEXT stored in the ExInfo and updating the REGDISPLAY to the faulting managed stack frame.
    void PostProcessingForNoFrameTransition(void);

#if defined(WIN64EXCEPTIONS)    
    void ResetGCRefReportingState(bool ResetOnlyIntermediaryState = false)
    {
        LIMITED_METHOD_CONTRACT;
        
        if (!ResetOnlyIntermediaryState)
        {
            m_sfFuncletParent = StackFrame();
            m_fProcessNonFilterFunclet = false;
        }
        
        m_sfIntermediaryFuncletParent = StackFrame();
        m_fProcessIntermediaryNonFilterFunclet = false;
    }
#endif // defined(WIN64EXCEPTIONS)    

    // Iteration state.
    FrameState m_frameState;

    // Initial state.  Must be preserved for restarting.
    Thread * m_pThread;                      // Thread on which to walk.

    PTR_Frame m_pStartFrame;                  // Frame* passed to Init

    // This is the real starting explicit frame.  If m_pStartFrame is NULL, 
    // then this is equal to m_pThread->GetFrame().  Otherwise this is equal to m_pStartFrame.
    INDEBUG(PTR_Frame m_pRealStartFrame);     

    ULONG32               m_flags;          // StackWalkFrames flags.
    ICodeManagerFlags     m_codeManFlags;
    ExecutionManager::ScanFlag m_scanFlag;

    // the following fields are used to cache information about a managed stack frame 
    // when we need to stop for skipped explicit frames
    EECodeInfo     m_cachedCodeInfo;

    GSCookie *     m_pCachedGSCookie;

#if defined(ELIMINATE_FEF)
    ExInfoWalker m_exInfoWalk;
#endif // ELIMINATE_FEF

#if defined(WIN64EXCEPTIONS)
    // used in funclet-skipping
    StackFrame    m_sfParent;
    
    // Used in GC reference enumeration mode
    StackFrame    m_sfFuncletParent;
    bool          m_fProcessNonFilterFunclet;
    StackFrame    m_sfIntermediaryFuncletParent;
    bool          m_fProcessIntermediaryNonFilterFunclet;
    bool          m_fDidFuncletReportGCReferences;
#endif // WIN64EXCEPTIONS

#if defined(RECORD_RESUMABLE_FRAME_SP)
    LPVOID m_pvResumableFrameTargetSP;
#endif // RECORD_RESUMABLE_FRAME_SP
};

void SetUpRegdisplayForStackWalk(Thread * pThread, T_CONTEXT * pContext, REGDISPLAY * pRegdisplay);

#endif