1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | //***************************************************************************** |
5 | // File: frameinfo.h |
6 | // |
7 | |
8 | // |
9 | // Debugger stack walker |
10 | // |
11 | //***************************************************************************** |
12 | |
13 | #ifndef FRAMEINFO_H_ |
14 | #define FRAMEINFO_H_ |
15 | |
16 | /* ========================================================================= */ |
17 | |
18 | /* ------------------------------------------------------------------------- * |
19 | * Classes |
20 | * ------------------------------------------------------------------------- */ |
21 | |
22 | class DebuggerJitInfo; |
23 | |
24 | // struct FrameInfo: Contains the information that will be handed to |
25 | // DebuggerStackCallback functions (along with their own, individual |
26 | // pData pointers). |
27 | // |
28 | // Frame *frame: The current explicit frame. NULL implies that |
29 | // the method frame is frameless, meaning either unmanaged or managed. This |
30 | // is set to be FRAME_TOP (0xFFffFFff) if the frame is the topmost, EE |
31 | // placed frame. |
32 | // |
33 | // MethodDesc *md: MetdhodDesc for the method that's |
34 | // executing in this method frame. Will be NULL if there is no MethodDesc |
35 | // If we're in generic code this may be a representative (i.e. canonical) |
36 | // MD, and extra information is available in the exactGenericArgsToken. |
37 | // For explicit frames, this may point to the method the explicit frame refers to |
38 | // (i.e. the method being jitted, or the interface method being called through |
39 | // COM interop), however it must always point to a method within the same |
40 | // domain of the explicit frame. Therefore, it is not used to point to the target of |
41 | // FuncEval frames since the target may be in a different domain. |
42 | // |
43 | // void *fp: frame pointer. Actually filled in from |
44 | // caller (parent) frame, so the DebuggerStackWalkProc must delay |
45 | // the user callback for one frame. This is not technically necessary on WIN64, but |
46 | // we follow the x86 model to keep things simpler. We should really consider changing |
47 | // the real stackwalker on x86 to unwind one frame ahead of time like the 64-bit one. |
48 | struct FrameInfo |
49 | { |
50 | public: |
51 | Frame *frame; |
52 | MethodDesc *md; |
53 | |
54 | // the register set of the frame being reported |
55 | REGDISPLAY registers; |
56 | FramePointer fp; |
57 | |
58 | // This field is propagated to the right side to become CordbRegisterSet::m_quicklyUnwind. |
59 | // If it is true, then the registers reported in the REGDISPLAY are invalid. It is only set to |
60 | // true in InitForEnterManagedChain(). In that case, we are passing a NULL REGDISPLAY anyway. |
61 | // This is such a misnomer. |
62 | bool quickUnwind; |
63 | |
64 | // Set to true if we are dealing with an internal explicit frame. Currently this is only true |
65 | // for prestub frames, security frames, funceval frames, and certain debugger-specific frames |
66 | // (e.g. DebuggerClassInitMarkFrame, DebuggerSecurityCodeMarkFrame). |
67 | // This affects HasMethodFrame() below. |
68 | bool internal; |
69 | |
70 | // whether the state contained in the FrameInfo represents a managed or unmanaged method frame/stub/chain; |
71 | // corresponds to ICorDebugChain::IsManaged() |
72 | bool managed; |
73 | |
74 | // Native offset from beginning of the method. |
75 | ULONG relOffset; |
76 | |
77 | // The ambient stackpointer. This can be use to compute esp-relative local variables, |
78 | // which can be common in frameless methods. |
79 | TADDR ambientSP; |
80 | |
81 | // These two fields are only set for managed method frames. |
82 | IJitManager *pIJM; |
83 | METHODTOKEN MethodToken; |
84 | |
85 | // This represents the current domain of the frame itself, and which |
86 | // the method specified by 'md' is executing in. |
87 | AppDomain *currentAppDomain; |
88 | |
89 | // only set for stackwalking, not stepping |
90 | void *exactGenericArgsToken; |
91 | |
92 | #if defined(WIN64EXCEPTIONS) |
93 | // This field is only used on IA64 to determine which registers are available and |
94 | // whether we need to adjust the IP. |
95 | bool fIsLeaf; |
96 | |
97 | // These two fields are used for funclets. |
98 | bool fIsFunclet; |
99 | bool fIsFilter; |
100 | |
101 | bool IsFuncletFrame() { return fIsFunclet; } |
102 | bool IsFilterFrame() { return fIsFilter; } |
103 | bool IsNonFilterFuncletFrame() { return (fIsFunclet && !fIsFilter); } |
104 | #endif // WIN64EXCEPTIONS |
105 | |
106 | |
107 | // A ridiculous flag that is targetting a very narrow fix at issue 650903 (4.5.1/Blue). |
108 | // This is set when the currently walked frame is a ComPlusMethodFrameGeneric. If the |
109 | // dude doing the walking is trying to ignore such frames (see |
110 | // code:ControllerStackInfo::m_suppressUMChainFromComPlusMethodFrameGeneric), AND |
111 | // this is set, then the walker just continues on to the next frame, without |
112 | // erroneously identifying this frame as the target frame. Only used during "Step |
113 | // Out" to a managed frame (i.e., managed-only debugging). |
114 | bool fIgnoreThisFrameIfSuppressingUMChainFromComPlusMethodFrameGeneric; |
115 | |
116 | // In addition to a Method, a FrameInfo may also represent either a Chain or a Stub (but not both). |
117 | // chainReason corresponds to ICorDebugChain::GetReason(). |
118 | CorDebugChainReason chainReason; |
119 | CorDebugInternalFrameType eStubFrameType; |
120 | |
121 | // Helpers for initializing a FrameInfo for a chain or a stub frame. |
122 | void InitForM2UInternalFrame(CrawlFrame * pCF); |
123 | void InitForU2MInternalFrame(CrawlFrame * pCF); |
124 | void InitForADTransition(CrawlFrame * pCF); |
125 | void InitForDynamicMethod(CrawlFrame * pCF); |
126 | void InitForFuncEval(CrawlFrame * pCF); |
127 | void InitForThreadStart(Thread *thread, REGDISPLAY * pRDSrc); |
128 | void InitForUMChain(FramePointer fpRoot, REGDISPLAY * pRDSrc); |
129 | void InitForEnterManagedChain(FramePointer fpRoot); |
130 | |
131 | // Does this FrameInfo represent a method frame? (aka a frameless frame) |
132 | // This may be combined w/ both StubFrames and ChainMarkers. |
133 | bool HasMethodFrame() { return md != NULL && !internal; } |
134 | |
135 | // Is this frame for a stub? |
136 | // This is mutually exclusive w/ Chain Markers. |
137 | // StubFrames may also have a method frame as a "hint". Ex, a stub frame for a |
138 | // M2U transition may have the Method for the Managed Wrapper for the unmanaged call. |
139 | // Stub frames map to internal frames on the RS. They use the same enum |
140 | // (CorDebugInternalFrameType) to represent the type of the frame. |
141 | bool HasStubFrame() { return eStubFrameType != STUBFRAME_NONE; } |
142 | |
143 | // Does this FrameInfo mark the start of a new chain? (A Frame info may both |
144 | // start a chain and represent a method) |
145 | bool HasChainMarker() { return chainReason != CHAIN_NONE; } |
146 | |
147 | // Helper functions for retrieving the DJI and the DMI |
148 | DebuggerJitInfo * GetJitInfoFromFrame(); |
149 | DebuggerMethodInfo * GetMethodInfoFromFrameOrThrow(); |
150 | |
151 | // Debug helper which nops in retail; and asserts invariants in debug. |
152 | #ifdef _DEBUG |
153 | void AssertValid(); |
154 | |
155 | // Debug helpers to get name of frame. Useful in asserts + log statements. |
156 | LPCUTF8 DbgGetClassName(); |
157 | LPCUTF8 DbgGetMethodName(); |
158 | #endif |
159 | |
160 | protected: |
161 | // These are common internal helpers shared by the other Init*() helpers above. |
162 | void InitForScratchFrameInfo(); |
163 | void InitFromStubHelper(CrawlFrame * pCF, MethodDesc * pMDHint, CorDebugInternalFrameType type); |
164 | |
165 | }; |
166 | |
167 | //StackWalkAction (*DebuggerStackCallback): This callback will |
168 | // be invoked by DebuggerWalkStackProc at each method frame and explicit frame, passing the FrameInfo |
169 | // and callback-defined pData to the method. The callback then returns a |
170 | // SWA - if SWA_ABORT is returned then the walk stops immediately. If |
171 | // SWA_CONTINUE is called, then the frame is walked & the next higher frame |
172 | // will be used. If the current explicit frame is at the root of the stack, then |
173 | // in the next iteration, DSC will be invoked with FrameInfo::frame == FRAME_TOP |
174 | typedef StackWalkAction (*DebuggerStackCallback)(FrameInfo *frame, void *pData); |
175 | |
176 | //StackWalkAction DebuggerWalkStack(): Sets up everything for a |
177 | // stack walk for the debugger, starts the stack walk (via |
178 | // g_pEEInterface->StackWalkFramesEx), then massages the output. Note that it |
179 | // takes a DebuggerStackCallback as an argument, but at each method frame and explicit frame |
180 | // DebuggerWalkStackProc gets called, which in turn calls the |
181 | // DebuggerStackCallback. |
182 | // Thread * thread: the thread on which to do a stackwalk |
183 | // void *targetFP: If you're looking for a specific frame, then |
184 | // this should be set to the fp for that frame, and the callback won't |
185 | // be called until that frame is reached. Otherwise, set it to LEAF_MOST_FRAME & |
186 | // the callback will be called on every frame. |
187 | // CONTEXT *context: Never NULL, b/c the callbacks require the |
188 | // CONTEXT as a place to store some information. Either it points to an |
189 | // uninitialized CONTEXT (contextValid should be false), or |
190 | // a pointer to a valid CONTEXT for the thread. If it's NULL, InitRegDisplay |
191 | // will fill it in for us, so we shouldn't go out of our way to set this up. |
192 | // bool contextValid: TRUE if context points to a valid CONTEXT, FALSE |
193 | // otherwise. |
194 | // DebuggerStackCallback pCallback: User supplied callback to |
195 | // be invoked at every frame that's at targetFP or higher. |
196 | // void *pData: User supplied data that we shuffle around, |
197 | // and then hand to pCallback. |
198 | // BOOL fIgnoreNonmethodFrames: Generally true for end user stackwalking (e.g. displaying a stack trace) and |
199 | // false for stepping (e.g. stepping out). |
200 | |
201 | StackWalkAction DebuggerWalkStack(Thread *thread, |
202 | FramePointer targetFP, |
203 | T_CONTEXT *pContext, |
204 | BOOL contextValid, |
205 | DebuggerStackCallback pCallback, |
206 | void *pData, |
207 | BOOL fIgnoreNonmethodFrames); |
208 | |
209 | #endif // FRAMEINFO_H_ |
210 | |