JavascriptStackWalker.h 15 KB

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