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: stackwalktypes.h
6//
7
8// ============================================================================
9// Contains types used by stackwalk.h.
10
11
12#ifndef __STACKWALKTYPES_H__
13#define __STACKWALKTYPES_H__
14
15class CrawlFrame;
16struct RangeSection;
17struct StackwalkCacheEntry;
18
19//
20// This type should be used internally inside the code manager only. EECodeInfo should
21// be used in general code instead. Ideally, we would replace all uses of METHODTOKEN
22// with EECodeInfo.
23//
24struct METHODTOKEN
25{
26 METHODTOKEN(RangeSection * pRangeSection, TADDR pCodeHeader)
27 : m_pRangeSection(pRangeSection), m_pCodeHeader(pCodeHeader)
28 {
29 }
30
31 METHODTOKEN()
32 {
33 }
34
35 // Cache of RangeSection containing the code to avoid redundant lookups.
36 RangeSection * m_pRangeSection;
37
38 // CodeHeader* for EEJitManager
39 // PTR_RUNTIME_FUNCTION for managed native code
40 TADDR m_pCodeHeader;
41
42 BOOL IsNull() const
43 {
44 return m_pCodeHeader == NULL;
45 }
46};
47
48//************************************************************************
49// Stack walking
50//************************************************************************
51enum StackCrawlMark
52{
53 LookForMe = 0,
54 LookForMyCaller = 1,
55 LookForMyCallersCaller = 2,
56 LookForThread = 3
57};
58
59enum StackWalkAction
60{
61 SWA_CONTINUE = 0, // continue walking
62 SWA_ABORT = 1, // stop walking, early out in "failure case"
63 SWA_FAILED = 2 // couldn't walk stack
64};
65
66#define SWA_DONE SWA_CONTINUE
67
68
69// Pointer to the StackWalk callback function.
70typedef StackWalkAction (*PSTACKWALKFRAMESCALLBACK)(
71 CrawlFrame *pCF, //
72 VOID* pData // Caller's private data
73
74);
75
76/******************************************************************************
77 StackwalkCache: new class implements stackwalk perf optimization features.
78 StackwalkCacheEntry array: very simple per thread hash table, keeping cached data.
79 StackwalkCacheUnwindInfo: used by EECodeManager::UnwindStackFrame to return
80 stackwalk cache flags.
81 Cf. Ilyakoz for any questions.
82*/
83
84struct StackwalkCacheUnwindInfo
85{
86#if defined(_TARGET_AMD64_)
87 ULONG RBPOffset;
88 ULONG RSPOffsetFromUnwindInfo;
89#else // !_TARGET_AMD64_
90 size_t securityObjectOffset; // offset of SecurityObject. 0 if there is no security object
91 BOOL fUseEbp; // Is EBP modified by the method - either for a frame-pointer or for a scratch-register?
92 BOOL fUseEbpAsFrameReg; // use EBP as the frame pointer?
93#endif // !_TARGET_AMD64_
94
95 inline StackwalkCacheUnwindInfo() { SUPPORTS_DAC; ZeroMemory(this, sizeof(StackwalkCacheUnwindInfo)); }
96 StackwalkCacheUnwindInfo(StackwalkCacheEntry * pCacheEntry);
97};
98
99//************************************************************************
100
101#if defined(_WIN64)
102 #define STACKWALK_CACHE_ENTRY_ALIGN_BOUNDARY 0x10
103#else // !_WIN64
104 #define STACKWALK_CACHE_ENTRY_ALIGN_BOUNDARY 0x8
105#endif // !_WIN64
106
107struct
108DECLSPEC_ALIGN(STACKWALK_CACHE_ENTRY_ALIGN_BOUNDARY)
109StackwalkCacheEntry
110{
111 //
112 // don't rearrange the fields, so that invalid value 0x8000000000000000 will never appear
113 // as StackwalkCacheEntry, it's required for atomicMOVQ using FILD/FISTP instructions
114 //
115 UINT_PTR IP;
116#if !defined(_TARGET_AMD64_)
117 WORD ESPOffset:15; // stack offset (frame size + pending arguments + etc)
118 WORD securityObjectOffset:3;// offset of SecurityObject. 0 if there is no security object
119 WORD fUseEbp:1; // For ESP methods, is EBP touched at all?
120 WORD fUseEbpAsFrameReg:1; // use EBP as the frame register?
121 WORD argSize:11; // size of args pushed on stack
122#else // _TARGET_AMD64_
123 DWORD RSPOffset;
124 DWORD RBPOffset;
125#endif // _TARGET_AMD64_
126
127 inline BOOL Init(UINT_PTR IP,
128 UINT_PTR SPOffset,
129 StackwalkCacheUnwindInfo *pUnwindInfo,
130 UINT_PTR argSize)
131 {
132 LIMITED_METHOD_CONTRACT;
133
134 this->IP = IP;
135
136#if defined(_TARGET_X86_)
137 this->ESPOffset = SPOffset;
138 this->argSize = argSize;
139
140 this->securityObjectOffset = (WORD)pUnwindInfo->securityObjectOffset;
141 _ASSERTE(this->securityObjectOffset == pUnwindInfo->securityObjectOffset);
142
143 this->fUseEbp = pUnwindInfo->fUseEbp;
144 this->fUseEbpAsFrameReg = pUnwindInfo->fUseEbpAsFrameReg;
145 _ASSERTE(!fUseEbpAsFrameReg || fUseEbp);
146
147 // return success if we fit SPOffset and argSize into
148 return ((this->ESPOffset == SPOffset) &&
149 (this->argSize == argSize));
150#elif defined(_TARGET_AMD64_)
151 // The size of a stack frame is guaranteed to fit in 4 bytes, so we don't need to check RSPOffset and RBPOffset.
152
153 // The actual SP offset may be bigger than the offset we get from the unwind info because of stack allocations.
154 _ASSERTE(SPOffset >= pUnwindInfo->RSPOffsetFromUnwindInfo);
155
156 _ASSERTE(FitsIn<DWORD>(SPOffset));
157 this->RSPOffset = static_cast<DWORD>(SPOffset);
158 _ASSERTE(FitsIn<DWORD>(pUnwindInfo->RBPOffset + (SPOffset - pUnwindInfo->RSPOffsetFromUnwindInfo)));
159 this->RBPOffset = static_cast<DWORD>(pUnwindInfo->RBPOffset + (SPOffset - pUnwindInfo->RSPOffsetFromUnwindInfo));
160 return TRUE;
161#else // !_TARGET_X86_ && !_TARGET_AMD64_
162 return FALSE;
163#endif // !_TARGET_X86_ && !_TARGET_AMD64_
164 }
165
166 inline BOOL HasSecurityObject()
167 {
168 LIMITED_METHOD_CONTRACT;
169
170#if defined(_TARGET_X86_)
171 return securityObjectOffset != 0;
172#else // !_TARGET_X86_
173 // On AMD64 we don't save anything by grabbing the security object before it is needed. This is because
174 // we need to crack the GC info in order to find the security object, and to unwind we only need to
175 // crack the unwind info.
176 return FALSE;
177#endif // !_TARGET_X86_
178 }
179
180 inline BOOL IsSafeToUseCache()
181 {
182 LIMITED_METHOD_CONTRACT;
183
184#if defined(_TARGET_X86_)
185 return (!fUseEbp || fUseEbpAsFrameReg);
186#elif defined(_TARGET_AMD64_)
187 return TRUE;
188#else // !_TARGET_X86_ && !_TARGET_AMD64_
189 return FALSE;
190#endif // !_TARGET_X86_ && !_TARGET_AMD64_
191 }
192};
193
194#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
195static_assert_no_msg(sizeof(StackwalkCacheEntry) == 2 * sizeof(UINT_PTR));
196#endif // _TARGET_X86_ || _TARGET_AMD64_
197
198//************************************************************************
199
200class StackwalkCache
201{
202 friend struct _DacGlobals;
203
204 public:
205 BOOL Lookup(UINT_PTR IP);
206 void Insert(StackwalkCacheEntry *pCacheEntry);
207 inline void ClearEntry () { LIMITED_METHOD_DAC_CONTRACT; m_CacheEntry.IP = 0; }
208 inline BOOL Enabled() { LIMITED_METHOD_DAC_CONTRACT; return s_Enabled; };
209 inline BOOL IsEmpty () { LIMITED_METHOD_CONTRACT; return m_CacheEntry.IP == 0; }
210
211#ifndef DACCESS_COMPILE
212 StackwalkCache();
213#endif
214 static void Init();
215
216 StackwalkCacheEntry m_CacheEntry; // local copy of Global Cache entry for current IP
217
218 static void Invalidate(LoaderAllocator * pLoaderAllocator);
219
220 private:
221 unsigned GetKey(UINT_PTR IP);
222
223#ifdef DACCESS_COMPILE
224 // DAC can't rely on the cache here
225 const static BOOL s_Enabled;
226#else
227 static BOOL s_Enabled;
228#endif
229};
230
231//************************************************************************
232
233inline StackwalkCacheUnwindInfo::StackwalkCacheUnwindInfo(StackwalkCacheEntry * pCacheEntry)
234{
235 LIMITED_METHOD_CONTRACT;
236
237#if defined(_TARGET_AMD64_)
238 RBPOffset = pCacheEntry->RBPOffset;
239#else // !_TARGET_AMD64_
240 securityObjectOffset = pCacheEntry->securityObjectOffset;
241 fUseEbp = pCacheEntry->fUseEbp;
242 fUseEbpAsFrameReg = pCacheEntry->fUseEbpAsFrameReg;
243#endif // !_TARGET_AMD64_
244}
245
246#endif // __STACKWALKTYPES_H__
247