JavascriptStackWalker.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. #if defined(_M_IX86)
  7. #include "Language/i386/StackFrame.h"
  8. typedef Js::X86StackFrame StackFrame;
  9. #elif defined(_M_X64)
  10. #include "Language/amd64/StackFrame.h"
  11. #ifdef _WIN32 // xplat-todo
  12. #include "Language/amd64/StackFrame.inl"
  13. #endif
  14. typedef Js::Amd64StackFrame StackFrame;
  15. #elif defined(_M_ARM)
  16. #include "Language/arm/StackFrame.h"
  17. typedef Js::ArmStackFrame StackFrame;
  18. #elif defined(_M_ARM64)
  19. #include "Language/arm64/StackFrame.h"
  20. typedef Js::Arm64StackFrame StackFrame;
  21. #else
  22. #error JavascriptStackWalker is not supported on this architecture.
  23. #endif
  24. namespace Js
  25. {
  26. struct ScriptEntryExitRecord;
  27. enum InternalFrameType {
  28. InternalFrameType_None,
  29. InternalFrameType_EhFrame,
  30. InternalFrameType_LoopBody
  31. };
  32. struct AsmJsCallStackLayout
  33. {
  34. Js::JavascriptFunction * functionObject;
  35. Js::Var args[0];
  36. };
  37. class JavascriptCallStackLayout
  38. {
  39. public:
  40. Js::JavascriptFunction * functionObject;
  41. Js::CallInfo callInfo;
  42. Js::Var args[0];
  43. Js::ArgumentsObject * GetArgumentsObject() const;
  44. Js::Var * GetArgumentsObjectLocation() const;
  45. void SetArgumentsObject(Js::ArgumentsObject* obj);
  46. Js::Var* GetArgv() const;
  47. Js::Var GetOffset(int offset) const;
  48. double GetDoubleAtOffset(int offset) const;
  49. int32 GetInt32AtOffset(int offset) const;
  50. SIMDValue GetSimdValueAtOffset(int offset) const;
  51. char * GetValueChangeOffset(int offset) const;
  52. ForInObjectEnumerator * GetForInObjectEnumeratorArrayAtOffset(int offset) const;
  53. static JavascriptCallStackLayout *FromFramePointer(void *const framePointer);
  54. static void* const ToFramePointer(JavascriptCallStackLayout* callstackLayout);
  55. private:
  56. JavascriptCallStackLayout() : callInfo(0) {};
  57. };
  58. #if ENABLE_NATIVE_CODEGEN
  59. /*
  60. * The InlinedFrameStackWalker knows how to walk an inlinee's virtual frames inside a
  61. * physical frame. If the stack walker is in the inlineeFramesBeingWalked mode it
  62. * defers pretty much all functionality to its helpers. The virtual stack frames themselves
  63. * are laid out in the reverse order on the stack. So we do one pass to find out the count of
  64. * frames and another to construct an array of pointers to frames in the correct order
  65. * (top most first like a real stack). Each frame begins with a count. Since frames are laid
  66. * out continuously in memory, this is all the stack walker needs to find the next frame.
  67. * We don't maintain explicit prev, next pointers. We also clear the count field of the frame
  68. * next to the top most frame to indicate that the top most frame is, well, the top most frame.
  69. * Whenever an inlinee's code ends, the count field in its frame gets set to 0 indicating this
  70. * frame isn't valid anymore. This keeps the fast case fast and offloads the heavy lifting
  71. * to the stack walker.
  72. */
  73. class InlinedFrameWalker
  74. {
  75. public:
  76. InlinedFrameWalker()
  77. : parentFunction(nullptr)
  78. , frames(nullptr)
  79. , currentIndex(-1)
  80. , frameCount(0)
  81. {
  82. }
  83. ~InlinedFrameWalker()
  84. {
  85. Assert(!parentFunction);
  86. Assert(!this->frames);
  87. Assert(!frameCount);
  88. Assert(currentIndex == -1);
  89. }
  90. static bool FromPhysicalFrame(InlinedFrameWalker& self, StackFrame& physicalFrame, Js::ScriptFunction *parent, bool fromBailout,
  91. int loopNum, const JavascriptStackWalker * const walker, bool useInternalFrameInfo, bool noAlloc);
  92. void Close();
  93. bool Next(CallInfo& callInfo);
  94. size_t GetArgc() const;
  95. Js::Var *GetArgv(bool includeThis, bool boxArgsAndDeepCopy) const;
  96. Js::JavascriptFunction *GetFunctionObject() const;
  97. void SetFunctionObject(Js::JavascriptFunction * function);
  98. Js::Var GetArgumentsObject() const;
  99. void SetArgumentsObject(Js::Var arguments);
  100. Js::Var GetThisObject() const;
  101. bool IsCallerPhysicalFrame() const;
  102. bool IsTopMostFrame() const;
  103. int32 GetFrameIndex() const { Assert(currentIndex != -1); return currentIndex; }
  104. uint32 GetCurrentInlineeOffset() const;
  105. uint32 GetBottomMostInlineeOffset() const;
  106. Js::JavascriptFunction *GetBottomMostFunctionObject() const;
  107. void FinalizeStackValues(__in_ecount(argCount) Js::Var args[], size_t argCount, bool deepCopy) const;
  108. int32 GetFrameCount() { return frameCount; }
  109. private:
  110. enum {
  111. InlinedFrameArgIndex_This = 0,
  112. InlinedFrameArgIndex_SecondScriptArg = 1
  113. };
  114. struct InlinedFrame : public InlinedFrameLayout
  115. {
  116. Js::Var argv[0]; // It's defined here as in C++ can't have 0-size array in the base class.
  117. struct InlinedFrame *Next()
  118. {
  119. InlinedFrameLayout *next = __super::Next();
  120. return (InlinedFrame*)next;
  121. }
  122. static InlinedFrame *FromPhysicalFrame(StackFrame& currentFrame, const JavascriptStackWalker * const stackWalker, void *entry, EntryPointInfo* entryPointInfo, bool useInternalFrameInfo);
  123. };
  124. public:
  125. InlinedFrame *const GetFrameAtIndex(signed index) const;
  126. private:
  127. void Initialize(int32 frameCount, __in_ecount(frameCount) InlinedFrame **frames, Js::ScriptFunction *parent);
  128. void MoveNext();
  129. InlinedFrame *const GetCurrentFrame() const;
  130. Js::ScriptFunction *parentFunction;
  131. InlinedFrame **frames;
  132. int32 currentIndex;
  133. int32 frameCount;
  134. };
  135. class InternalFrameInfo
  136. {
  137. public:
  138. void *codeAddress;
  139. void *framePointer;
  140. size_t stackCheckCodeHeight;
  141. InternalFrameType frameType;
  142. JavascriptFunction* function;
  143. bool hasInlinedFramesOnStack;
  144. bool previousInterpreterFrameIsFromBailout;
  145. InternalFrameInfo() :
  146. codeAddress(nullptr),
  147. framePointer(nullptr),
  148. stackCheckCodeHeight((uint)-1),
  149. frameType(InternalFrameType_None),
  150. function(nullptr),
  151. hasInlinedFramesOnStack(false),
  152. previousInterpreterFrameIsFromBailout(false)
  153. {
  154. }
  155. void Clear();
  156. void Set(void *codeAddress, void *framePointer, size_t stackCheckCodeHeight, InternalFrameType frameType, JavascriptFunction* function, bool hasInlinedFramesOnStack, bool previousInterpreterFrameIsFromBailout);
  157. };
  158. #endif
  159. class JavascriptStackWalker
  160. {
  161. friend Js::ScriptContext;
  162. public:
  163. JavascriptStackWalker(ScriptContext * scriptContext, bool useEERContext = TRUE /* use leafinterpreterframe of entryexit record */, PVOID returnAddress = NULL, bool _forceFullWalk = false);
  164. #if ENABLE_NATIVE_CODEGEN
  165. ~JavascriptStackWalker() { inlinedFrameWalker.Close(); }
  166. #endif
  167. BOOL Walk(bool includeInlineFrames = true);
  168. BOOL GetCaller(_Out_opt_ JavascriptFunction ** ppFunc, bool includeInlineFrames = true);
  169. BOOL GetCallerWithoutInlinedFrames(_Out_opt_ JavascriptFunction ** ppFunc);
  170. BOOL GetNonLibraryCodeCaller(_Out_opt_ JavascriptFunction ** ppFunc);
  171. BOOL WalkToTarget(JavascriptFunction * funcTarget);
  172. BOOL WalkToArgumentsFrame(ArgumentsObject *argsObj);
  173. uint32 GetByteCodeOffset() const;
  174. BOOL IsCallerGlobalFunction() const;
  175. BOOL IsEvalCaller() const;
  176. bool IsJavascriptFrame() const { return inlinedFramesBeingWalked || isJavascriptFrame; }
  177. bool IsInlineFrame() const { return inlinedFramesBeingWalked; }
  178. bool IsBailedOutFromInlinee() const
  179. {
  180. Assert(this->IsJavascriptFrame() && this->interpreterFrame);
  181. return IsInlineFrame();
  182. }
  183. bool IsBailedOutFromFunction() const
  184. {
  185. Assert(this->IsJavascriptFrame() && this->interpreterFrame);
  186. return !!JavascriptFunction::IsNativeAddress(this->scriptContext, this->currentFrame.GetInstructionPointer());
  187. }
  188. Var GetPermanentArguments() const;
  189. void *GetCurrentCodeAddr() const;
  190. JavascriptFunction *GetCurrentFunction(bool includeInlinedFrames = true) const;
  191. void SetCurrentFunction(JavascriptFunction * function);
  192. CallInfo GetCallInfo(bool includeInlinedFrames = true) const;
  193. CallInfo GetCallInfoFromPhysicalFrame() const;
  194. void GetThis(Var *pThis, int moduleId) const;
  195. Js::Var * GetJavascriptArgs(bool boxArgsAndDeepCopy) const;
  196. void **GetCurrentArgv() const;
  197. ScriptContext* GetCurrentScriptContext() const;
  198. InterpreterStackFrame* GetCurrentInterpreterFrame() const
  199. {
  200. Assert(this->IsJavascriptFrame());
  201. return interpreterFrame;
  202. }
  203. bool GetSourcePosition(const WCHAR** sourceFileName, ULONG* line, LONG* column);
  204. static bool TryIsTopJavaScriptFrameNative(ScriptContext* scriptContext, bool* istopFrameNative, bool ignoreLibraryCode = false);
  205. static bool AlignAndCheckAddressOfReturnAddressMatch(void* addressOfReturnAddress, void* nativeLibraryEntryAddress);
  206. #if ENABLE_NATIVE_CODEGEN
  207. void ClearCachedInternalFrameInfo();
  208. void SetCachedInternalFrameInfo(InternalFrameType frameType, JavascriptFunction* function, bool hasInlinedFramesOnStack, bool prevIntFrameIsFromBailout);
  209. InternalFrameInfo GetCachedInternalFrameInfo() const { return this->lastInternalFrameInfo; }
  210. void WalkAndClearInlineeFrameCallInfoOnException(void *tryHandlerAddrOfReturnAddr);
  211. #endif
  212. bool IsCurrentPhysicalFrameForLoopBody() const;
  213. // noinline, we want to use own stack frame.
  214. static _NOINLINE BOOL GetCaller(_Out_opt_ JavascriptFunction** ppFunc, ScriptContext* scriptContext);
  215. static _NOINLINE BOOL GetCaller(_Out_opt_ JavascriptFunction** ppFunc, uint32* byteCodeOffset, ScriptContext* scriptContext);
  216. static _NOINLINE void GetThis(Var* pThis, int moduleId, ScriptContext* scriptContext);
  217. static _NOINLINE void GetThis(Var* pThis, int moduleId, JavascriptFunction* func, ScriptContext* scriptContext);
  218. static bool IsDisplayCaller(JavascriptFunction* func);
  219. bool GetDisplayCaller(_Out_opt_ JavascriptFunction ** ppFunc);
  220. PCWSTR GetCurrentNativeLibraryEntryName() const;
  221. static bool IsLibraryStackFrameEnabled(Js::ScriptContext * scriptContext);
  222. static bool IsWalkable(ScriptContext *scriptContext);
  223. // Walk frames (until walkFrame returns true)
  224. template <class WalkFrame>
  225. ushort WalkUntil(ushort stackTraceLimit, WalkFrame walkFrame, bool onlyOnDebugMode = false, bool filterDiagnosticsOM = false)
  226. {
  227. ushort frameIndex = 0;
  228. JavascriptFunction* jsFunction;
  229. BOOL foundCaller = GetNonLibraryCodeCaller(&jsFunction);
  230. while (foundCaller)
  231. {
  232. if (IsDisplayCaller(jsFunction))
  233. {
  234. bool needToPass = (!onlyOnDebugMode || jsFunction->GetScriptContext()->IsScriptContextInDebugMode())
  235. && (!filterDiagnosticsOM || !jsFunction->GetScriptContext()->IsDiagnosticsScriptContext());
  236. if (needToPass)
  237. {
  238. if (walkFrame(jsFunction, frameIndex))
  239. {
  240. break;
  241. }
  242. frameIndex++;
  243. }
  244. }
  245. foundCaller = frameIndex < stackTraceLimit && GetCaller(&jsFunction);
  246. }
  247. return frameIndex;
  248. }
  249. template <class WalkFrame>
  250. ushort WalkUntil(WalkFrame walkFrame, bool onlyOnDebugMode = false, bool filterDiagnosticsOM = false)
  251. {
  252. return WalkUntil(USHORT_MAX, walkFrame, onlyOnDebugMode, filterDiagnosticsOM);
  253. }
  254. BYTE** GetCurrentAddresOfReturnAddress() const
  255. {
  256. return (BYTE**)this->currentFrame.GetAddressOfReturnAddress();
  257. }
  258. BYTE** GetCurrentAddressOfInstructionPointer() const
  259. {
  260. return (BYTE**)this->currentFrame.GetAddressOfInstructionPointer();
  261. }
  262. void* GetInstructionPointer() const
  263. {
  264. return this->currentFrame.GetInstructionPointer();
  265. }
  266. bool GetCurrentFrameFromBailout() const
  267. {
  268. return previousInterpreterFrameIsFromBailout;
  269. }
  270. #if DBG
  271. static bool ValidateTopJitFrame(Js::ScriptContext* scriptContext);
  272. #endif
  273. private:
  274. ScriptContext *scriptContext;
  275. ScriptEntryExitRecord *entryExitRecord;
  276. const NativeLibraryEntryRecord::Entry *nativeLibraryEntry;
  277. const NativeLibraryEntryRecord::Entry *prevNativeLibraryEntry; // Saves previous nativeLibraryEntry when it moves to next
  278. InterpreterStackFrame *interpreterFrame;
  279. InterpreterStackFrame *tempInterpreterFrame;
  280. #if ENABLE_NATIVE_CODEGEN
  281. Js::InlinedFrameWalker inlinedFrameWalker;
  282. #endif
  283. CallInfo inlinedFrameCallInfo;
  284. bool inlinedFramesBeingWalked : 1;
  285. bool hasInlinedFramesOnStack : 1;
  286. bool isJavascriptFrame : 1;
  287. bool isNativeLibraryFrame : 1;
  288. bool isInitialFrame : 1; // If we need to walk the initial frame
  289. bool shouldDetectPartiallyInitializedInterpreterFrame : 1;
  290. bool previousInterpreterFrameIsFromBailout : 1;
  291. bool previousInterpreterFrameIsForLoopBody : 1;
  292. bool forceFullWalk : 1; // ignoring hasCaller
  293. Var GetThisFromFrame() const; // returns 'this' object from the physical frame
  294. Var GetCurrentArgumentsObject() const; // returns arguments object from the current frame, which may be virtual (belonging to an inlinee)
  295. void SetCurrentArgumentsObject(Var args); // sets arguments object for the current frame, which may be virtual (belonging to an inlinee)
  296. Var GetCurrentNativeArgumentsObject() const; // returns arguments object from the physical native frame
  297. void SetCurrentNativeArgumentsObject(Var args); // sets arguments object on the physical native frame
  298. bool TryGetByteCodeOffsetFromInterpreterFrame(uint32& offset) const;
  299. #if ENABLE_NATIVE_CODEGEN
  300. bool TryGetByteCodeOffsetFromNativeFrame(uint32& offset) const;
  301. bool TryGetByteCodeOffsetOfInlinee(Js::JavascriptFunction* function, uint loopNum, DWORD_PTR pCodeAddr, Js::FunctionBody** inlinee, uint32& offset, bool useInternalFrameInfo) const;
  302. uint GetLoopNumber(bool& usedInternalFrameInfo) const;
  303. bool InlinedFramesBeingWalked() const;
  304. bool HasInlinedFramesOnStack() const;
  305. bool PreviousInterpreterFrameIsFromBailout() const;
  306. InternalFrameInfo lastInternalFrameInfo;
  307. #endif
  308. mutable StackFrame currentFrame;
  309. Js::JavascriptFunction * UpdateFrame(bool includeInlineFrames);
  310. bool CheckJavascriptFrame(bool includeInlineFrames);
  311. JavascriptFunction *GetCurrentFunctionFromPhysicalFrame() const;
  312. };
  313. class AutoPushReturnAddressForStackWalker
  314. {
  315. private:
  316. ScriptContext *m_scriptContext;
  317. public:
  318. AutoPushReturnAddressForStackWalker(ScriptContext *scriptContext, void* returnAddress) : m_scriptContext(scriptContext)
  319. {
  320. scriptContext->SetFirstInterpreterFrameReturnAddress(returnAddress);
  321. }
  322. ~AutoPushReturnAddressForStackWalker()
  323. {
  324. m_scriptContext->SetFirstInterpreterFrameReturnAddress(NULL);
  325. }
  326. };
  327. }