| 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 | |