TTExecutionInfo.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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 ENABLE_TTD
  7. namespace TTD
  8. {
  9. //An exception class for controlled aborts from the runtime to the toplevel TTD control loop
  10. class TTDebuggerAbortException
  11. {
  12. private:
  13. //An integer code to describe the reason for the abort -- 0 invalid, 1 end of log, 2 request etime move, 3 uncaught exception (propagate to top-level)
  14. const uint32 m_abortCode;
  15. //An optional target event time -- intent is interpreted based on the abort code
  16. const int64 m_optEventTime;
  17. //An optional move mode value -- should be built by host we just propagate it
  18. const int64 m_optMoveMode;
  19. //An optional -- and static string message to include
  20. const char16* m_staticAbortMessage;
  21. TTDebuggerAbortException(uint32 abortCode, int64 optEventTime, int64 optMoveMode, const char16* staticAbortMessage);
  22. public:
  23. ~TTDebuggerAbortException();
  24. static TTDebuggerAbortException CreateAbortEndOfLog(const char16* staticMessage);
  25. static TTDebuggerAbortException CreateTopLevelAbortRequest(int64 targetEventTime, int64 moveMode, const char16* staticMessage);
  26. static TTDebuggerAbortException CreateUncaughtExceptionAbortRequest(int64 targetEventTime, const char16* staticMessage);
  27. bool IsEndOfLog() const;
  28. bool IsEventTimeMove() const;
  29. bool IsTopLevelException() const;
  30. int64 GetTargetEventTime() const;
  31. int64 GetMoveMode() const;
  32. const char16* GetStaticAbortMessage() const;
  33. };
  34. //A struct for tracking time events in a single method
  35. struct SingleCallCounter
  36. {
  37. Js::FunctionBody* Function;
  38. #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
  39. const char16* Name; //only added for debugging can get rid of later.
  40. #endif
  41. uint64 FunctionTime; //The function time when the function was called
  42. uint64 LoopTime; //The current loop taken time for the function
  43. int32 LastStatementIndex; //The previously executed statement
  44. uint64 LastStatementLoopTime; //The previously executed statement
  45. int32 CurrentStatementIndex; //The currently executing statement
  46. uint64 CurrentStatementLoopTime; //The currently executing statement
  47. //bytecode range of the current stmt
  48. uint32 CurrentStatementBytecodeMin;
  49. uint32 CurrentStatementBytecodeMax;
  50. };
  51. //A class to represent a source location
  52. class TTDebuggerSourceLocation
  53. {
  54. private:
  55. //The script context logid the breakpoint goes with
  56. TTD_LOG_PTR_ID m_sourceScriptLogId;
  57. //The id of the breakpoint (or -1 if no id is associated)
  58. int64 m_bpId;
  59. //The time aware parts of this location
  60. int64 m_etime; //-1 indicates an INVALID location
  61. int64 m_ftime; //-1 indicates any ftime is OK
  62. int64 m_ltime; //-1 indicates any ltime is OK
  63. //The top-level body this source location is contained in
  64. uint32 m_topLevelBodyId;
  65. //The position of the function in the document
  66. uint32 m_functionLine;
  67. uint32 m_functionColumn;
  68. //The location in the fnuction
  69. uint32 m_line;
  70. uint32 m_column;
  71. //Update the specific body of this location from the root body and line number info
  72. Js::FunctionBody* UpdatePostInflateFunctionBody_Helper(Js::FunctionBody* rootBody) const;
  73. public:
  74. TTDebuggerSourceLocation();
  75. TTDebuggerSourceLocation(const TTDebuggerSourceLocation& other);
  76. ~TTDebuggerSourceLocation();
  77. TTDebuggerSourceLocation& operator= (const TTDebuggerSourceLocation& other);
  78. #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
  79. void PrintToConsole() const;
  80. #endif
  81. void Initialize();
  82. bool HasValue() const;
  83. bool HasTTDTimeValue() const;
  84. void Clear();
  85. void SetLocationCopy(const TTDebuggerSourceLocation& other);
  86. void SetLocationFromFrame(int64 topLevelETime, const SingleCallCounter& callFrame);
  87. void SetLocationFromFunctionEntryAnyTime(int64 topLevelETime, Js::FunctionBody* body);
  88. void SetLocationFull(int64 etime, int64 ftime, int64 ltime, Js::FunctionBody* body, ULONG line, LONG column);
  89. void SetLocationFullRaw(TTD_LOG_PTR_ID sourceScriptLogId, int64 etime, int64 ftime, int64 ltime, uint32 topLevelBodyId, uint32 functionLine, uint32 functionColumn, ULONG line, LONG column);
  90. void SetLocationWithBP(int64 bpId, Js::FunctionBody* body, ULONG line, LONG column);
  91. int64 GetRootEventTime() const;
  92. int64 GetFunctionTime() const;
  93. int64 GetLoopTime() const;
  94. TTD_LOG_PTR_ID GetScriptLogTagId() const;
  95. Js::FunctionBody* LoadFunctionBodyIfPossible(Js::ScriptContext* ctx) const;
  96. int64 GetBPId() const;
  97. uint32 GetTopLevelBodyId() const;
  98. uint32 GetFunctionSourceLine() const;
  99. uint32 GetFunctionSourceColumn() const;
  100. uint32 GetSourceLine() const;
  101. uint32 GetSourceColumn() const;
  102. //return true if the two locations refer to the same time/place
  103. static bool AreSameLocations(const TTDebuggerSourceLocation& sl1, const TTDebuggerSourceLocation& sl2);
  104. static bool AreSameLocations_PlaceOnly(const TTDebuggerSourceLocation& sl1, const TTDebuggerSourceLocation& sl2);
  105. };
  106. //A by value class representing the state of the last returned from location in execution (return x or exception)
  107. class TTLastReturnLocationInfo
  108. {
  109. private:
  110. bool m_isExceptionFrame;
  111. SingleCallCounter m_lastFrame;
  112. public:
  113. TTLastReturnLocationInfo();
  114. void SetReturnLocation(const SingleCallCounter& cframe);
  115. void SetExceptionLocation(const SingleCallCounter& cframe);
  116. bool IsDefined() const;
  117. bool IsReturnLocation() const;
  118. bool IsExceptionLocation() const;
  119. const SingleCallCounter& GetLocation() const;
  120. void Clear();
  121. void ClearReturnOnly();
  122. void ClearExceptionOnly();
  123. };
  124. //A by value class that we use to track the last statement to hit (and abort at) in the inner loop replay
  125. class TTInnerLoopLastStatementInfo
  126. {
  127. private:
  128. //The time aware parts of this location -- same meaning as in TTDebuggerSourceLocation
  129. int64 m_eventTime; //-1 indicates an INVALID location
  130. int64 m_functionTime;
  131. int64 m_loopTime;
  132. //The location in the function
  133. uint32 m_line;
  134. uint32 m_column;
  135. public:
  136. TTInnerLoopLastStatementInfo();
  137. TTInnerLoopLastStatementInfo(const TTInnerLoopLastStatementInfo& lsi);
  138. void SetLastLine(int64 etime, int64 ftime, int64 ltime, uint32 line, uint32 column);
  139. bool IsEnabled() const;
  140. bool CheckLastTimeMatch(int64 etime, int64 ftime, int64 ltime) const;
  141. bool CheckLineColumnMatch(uint32 line, uint32 column) const;
  142. };
  143. //////////////////
  144. //This class manages exeuction info for our replay execution including
  145. //call-stack, current statements, breakpoints, loaded code, etc.
  146. class ExecutionInfoManager
  147. {
  148. private:
  149. //Stash the current top-level event time here
  150. int64 m_topLevelCallbackEventTime;
  151. //A counter (per event dispatch) which holds the current value for the function counter
  152. uint64 m_runningFunctionTimeCtr;
  153. //Array of call counters (used as stack)
  154. JsUtil::List<SingleCallCounter, HeapAllocator> m_callStack;
  155. //A set containing every top level body that we have notified the debugger of (so we don't tell them multiple times)
  156. JsUtil::BaseHashSet<uint32, HeapAllocator> m_debuggerNotifiedTopLevelBodies;
  157. //The most recently executed statement before return -- normal return or exception
  158. //We clear this after executing any following statements so this can be used for:
  159. // - Step back to uncaught exception
  160. // - Step to last statement in previous event
  161. // - Step back *into* possible if either case is true
  162. TTLastReturnLocationInfo m_lastReturnLocation;
  163. //Always keep the last exception location as well -- even if it is caught
  164. bool m_lastExceptionPropagating;
  165. TTDebuggerSourceLocation m_lastExceptionLocation;
  166. //A flag indicating if we want to break on the entry to the user code
  167. bool m_breakOnFirstUserCode;
  168. //A pending TTDBP we want to set and move to
  169. TTDebuggerSourceLocation m_pendingTTDBP;
  170. int64 m_pendingTTDMoveMode;
  171. //The bp we are actively moving to in TT mode
  172. int64 m_activeBPId;
  173. bool m_shouldRemoveWhenDone;
  174. TTDebuggerSourceLocation m_activeTTDBP;
  175. //The last breakpoint seen in the most recent scan
  176. bool m_hitContinueSearchBP;
  177. TTDebuggerSourceLocation m_continueBreakPoint;
  178. //Used to preserve breakpoints accross inflate operations
  179. JsUtil::List<TTDebuggerSourceLocation*, HeapAllocator> m_unRestoredBreakpoints;
  180. //Info for innerloop last line in replay mode
  181. TTInnerLoopLastStatementInfo m_innerloopLastLocation;
  182. #if ENABLE_BASIC_TRACE || ENABLE_FULL_BC_TRACE
  183. TraceLogger m_diagnosticLogger;
  184. #endif
  185. //A special check to see if we are in the process of a time-travel move and do not want to stop at any breakpoints
  186. static bool ShouldSuppressBreakpointsForTimeTravelMove(TTDMode mode);
  187. //A special check to see if we are in the process of a time-travel move and do not want to stop at any breakpoints
  188. static bool ShouldRecordBreakpointsDuringTimeTravelScan(TTDMode mode);
  189. public:
  190. ExecutionInfoManager(const TTInnerLoopLastStatementInfo& lsi);
  191. ~ExecutionInfoManager();
  192. #if ENABLE_BASIC_TRACE || ENABLE_FULL_BC_TRACE
  193. //Get the trace logger for this
  194. TraceLogger* GetTraceLogger();
  195. #endif
  196. //get the top call counter from the stack
  197. const SingleCallCounter& GetTopCallCounter() const;
  198. SingleCallCounter& GetTopCallCounter();
  199. //get the caller for the top call counter that is user code from the stack (e.g. stack -2)
  200. bool TryGetTopCallCallerCounter(SingleCallCounter& caller) const;
  201. //Log a function call
  202. void PushCallEvent(Js::JavascriptFunction* function, uint32 argc, Js::Var* argv, bool isInFinally);
  203. //Log a function return in normal case and exception
  204. void PopCallEvent(Js::JavascriptFunction* function, Js::Var result);
  205. void PopCallEventException(Js::JavascriptFunction* function);
  206. void ProcessCatchInfoForLastExecutedStatements();
  207. //Set that we want to break on the execution of the first user code
  208. void SetBreakOnFirstUserCode();
  209. //Set the requested breakpoint and move mode in debugger stepping
  210. void SetPendingTTDResetToCurrentPosition();
  211. void SetPendingTTDToTarget(const TTDebuggerSourceLocation& dsl);
  212. void SetPendingTTDStepBackMove();
  213. void SetPendingTTDStepBackIntoMove();
  214. void SetPendingTTDReverseContinueMove(uint64 moveflag);
  215. void SetPendingTTDUnhandledException();
  216. //Process the breakpoint info as we enter a break statement and return true if we actually want to break
  217. bool ProcessBPInfoPreBreak(Js::FunctionBody* fb, const EventLog* elog);
  218. //Process the breakpoint info as we resume from a break statement
  219. void ProcessBPInfoPostBreak(Js::FunctionBody* fb);
  220. //When scanning we don't want to add the active BP as a seen breakpoint *unless* is it is also explicitly added as a breakpoint by the user
  221. bool IsLocationActiveBPAndNotExplicitBP(const TTDebuggerSourceLocation& current) const;
  222. //When scanning add the current location as a BP location
  223. void AddCurrentLocationDuringScan(int64 topLevelEventTime);
  224. //After a scan set the pending BP to the earliest breakpoint before the given current pending BP location and return true
  225. //If no such BP location then return false
  226. bool TryFindAndSetPreviousBP();
  227. //Get the target event time for the pending TTD breakpoint
  228. int64 GetPendingTTDBPTargetEventTime() const;
  229. //Load and restore all the breakpoints in the manager before and after we create new script contexts
  230. void LoadPreservedBPInfo(ThreadContext* threadContext);
  231. //When we load in a top level function we need to add any breakpoints associated with it
  232. void AddPreservedBPsForTopLevelLoad(uint32 topLevelBodyId, Js::FunctionBody* body);
  233. //Update the loop count information
  234. void UpdateLoopCountInfo();
  235. //
  236. //TODO: This is not great performance wise
  237. //
  238. //For debugging we currently brute force track the current/last source statements executed
  239. void UpdateCurrentStatementInfo(uint bytecodeOffset);
  240. //Support for replay and checking the last source line info
  241. void ManageLastSourceInfoChecks(Js::FunctionBody* fb, bool bpDisable);
  242. bool IsLastSourceLineEnabled() const;
  243. bool IsFinalSourceLine() const;
  244. //Get the current time/position info for the debugger -- all out arguments are optional (nullptr if you don't care)
  245. void GetTimeAndPositionForDebugger(TTDebuggerSourceLocation& sourceLocation) const;
  246. #if ENABLE_OBJECT_SOURCE_TRACKING
  247. void GetTimeAndPositionForDiagnosticObjectTracking(DiagnosticOrigin& originInfo) const;
  248. #endif
  249. //Get the previous statement time/position for the debugger -- return false if this is the first statement of the event handler
  250. bool GetPreviousTimeAndPositionForDebugger(TTDebuggerSourceLocation& sourceLocation) const;
  251. //Get the last (uncaught or just caught) exception time/position for the debugger -- if the last return action was an exception and we have not made any additional calls
  252. //Otherwise get the last statement executed call time/position for the debugger
  253. void GetLastExecutedTimeAndPositionForDebugger(TTDebuggerSourceLocation& sourceLocation) const;
  254. void GetLastExceptionTimeAndPositionForDebugger(TTDebuggerSourceLocation& sourceLocation) const;
  255. //Ensure the call stack is clear and counters are zeroed appropriately
  256. void ResetCallStackForTopLevelCall(int64 topLevelCallbackEventTime);
  257. //Setup and tear down info on the bp location in the active interval the reverse continue was invoked from
  258. //We don't want to find a BP after the current statement so we need to track this
  259. void SetBPInfoForActiveSegmentContinueScan(ThreadContextTTD* ttdThreadContext);
  260. void ClearBPInfoForActiveSegmentContinueScan(ThreadContextTTD* ttdThreadContext);
  261. //Transfer the pending bp info to the active BP info (and set the BP) as needed to make sure BP is hit in reverse step operations
  262. void SetActiveBP_Helper(ThreadContextTTD* ttdThreadContext);
  263. void SetActiveBPInfoAsNeeded(ThreadContextTTD* ttdThreadContext);
  264. void ClearActiveBPInfo(ThreadContextTTD* ttdThreadContext);
  265. //After loading script process it to (1) notify the debugger if needed and (2) set any breakpoints we need
  266. void ProcessScriptLoad(Js::ScriptContext* ctx, uint32 topLevelBodyId, Js::FunctionBody* body, Js::Utf8SourceInfo* utf8SourceInfo, CompileScriptException* se);
  267. void ProcessScriptLoad_InflateReuseBody(uint32 topLevelBodyId, Js::FunctionBody* body);
  268. };
  269. //A class to ensure that even when exceptions are thrown the pop action for the TTD call stack is executed -- defined after EventLog so we can refer to it in the .h file
  270. class TTDExceptionFramePopper
  271. {
  272. private:
  273. ExecutionInfoManager* m_eimanager;
  274. Js::JavascriptFunction* m_function;
  275. public:
  276. TTDExceptionFramePopper()
  277. : m_eimanager(nullptr), m_function(nullptr)
  278. {
  279. ;
  280. }
  281. ~TTDExceptionFramePopper()
  282. {
  283. //we didn't clear this so an exception was thrown and we are propagating
  284. if(this->m_eimanager != nullptr)
  285. {
  286. //if it doesn't have an exception frame then this is the frame where the exception was thrown so record our info
  287. this->m_eimanager->PopCallEventException(this->m_function);
  288. }
  289. }
  290. void PushInfo(ExecutionInfoManager* eimanager, Js::JavascriptFunction* function)
  291. {
  292. this->m_eimanager = eimanager; //set the log info so if the pop isn't called the destructor will record propagation
  293. this->m_function = function;
  294. }
  295. void PopInfo()
  296. {
  297. this->m_eimanager = nullptr; //normal pop (no exception) just clear so destructor nops
  298. }
  299. };
  300. }
  301. #endif