1 /* 2 * Copyright 2021 Google LLC 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 #ifndef SkSLDebugTracePlayer_DEFINED 8 #define SkSLDebugTracePlayer_DEFINED 9 10 #include "src/sksl/tracing/SkSLDebugTracePriv.h" 11 12 #include "include/core/SkRefCnt.h" 13 #include "include/core/SkTypes.h" 14 #include "src/utils/SkBitSet.h" 15 16 #include <cstddef> 17 #include <cstdint> 18 #include <optional> 19 #include <unordered_map> 20 #include <unordered_set> 21 #include <vector> 22 23 namespace SkSL { 24 25 /** 26 * Plays back a SkSL debug trace, allowing its contents to be viewed like a traditional debugger. 27 */ 28 class SkSLDebugTracePlayer { 29 public: 30 /** Resets playback to the start of the trace. Breakpoints are not cleared. */ 31 void reset(sk_sp<DebugTracePriv> trace); 32 33 /** Advances the simulation to the next Line op. */ 34 void step(); 35 36 /** 37 * Advances the simulation to the next Line op, skipping past matched Enter/Exit pairs. 38 * Breakpoints will also stop the simulation even if we haven't reached an Exit. 39 */ 40 void stepOver(); 41 42 /** 43 * Advances the simulation until we exit from the current stack frame. 44 * Breakpoints will also stop the simulation even if we haven't left the stack frame. 45 */ 46 void stepOut(); 47 48 /** Advances the simulation until we hit a breakpoint, or the trace completes. */ 49 void run(); 50 51 /** Breakpoints will force the simulation to stop whenever a desired line is reached. */ 52 void setBreakpoints(std::unordered_set<int> breakpointLines); 53 void addBreakpoint(int line); 54 void removeBreakpoint(int line); 55 using BreakpointSet = std::unordered_set<int>; getBreakpoints()56 const BreakpointSet& getBreakpoints() { return fBreakpointLines; } 57 58 /** Returns true if we have reached the end of the trace. */ 59 bool traceHasCompleted() const; 60 61 /** Returns true if there is a breakpoint set at the current line. */ 62 bool atBreakpoint() const; 63 64 /** Retrieves the cursor position. */ cursor()65 size_t cursor() { return fCursor; } 66 67 /** Retrieves the current line. */ 68 int32_t getCurrentLine() const; 69 70 /** Retrieves the current line for a given stack frame. */ 71 int32_t getCurrentLineInStackFrame(int stackFrameIndex) const; 72 73 /** Returns the call stack as an array of FunctionInfo indices. */ 74 std::vector<int> getCallStack() const; 75 76 /** Returns the size of the call stack. */ 77 int getStackDepth() const; 78 79 /** 80 * Returns every line number reached inside this debug trace, along with the remaining number of 81 * times that this trace will reach it. e.g. {100, 2} means line 100 will be reached twice. 82 */ 83 using LineNumberMap = std::unordered_map<int, int>; getLineNumbersReached()84 const LineNumberMap& getLineNumbersReached() const { return fLineNumbers; } 85 86 /** Returns variables from a stack frame, or from global scope. */ 87 struct VariableData { 88 int fSlotIndex; 89 bool fDirty; // has this slot been written-to since the last step call? 90 double fValue; // value in slot (with type-conversion applied) 91 }; 92 std::vector<VariableData> getLocalVariables(int stackFrameIndex) const; 93 std::vector<VariableData> getGlobalVariables() const; 94 95 private: 96 /** 97 * Executes the trace op at the passed-in cursor position. Returns true if we've reached a line 98 * or exit trace op, which indicate a stopping point. 99 */ 100 bool execute(size_t position); 101 102 /** 103 * Cleans up temporary state between steps, such as the dirty mask and function return values. 104 */ 105 void tidyState(); 106 107 /** Updates fWriteTime for the entire variable at a given slot. */ 108 void updateVariableWriteTime(int slotIdx, size_t writeTime); 109 110 /** Returns a vector of the indices and values of each slot that is enabled in `bits`. */ 111 std::vector<VariableData> getVariablesForDisplayMask(const SkBitSet& bits) const; 112 113 struct StackFrame { 114 int32_t fFunction; // from fFuncInfo 115 int32_t fLine; // our current line number within the function 116 SkBitSet fDisplayMask; // the variable slots which have been touched in this function 117 }; 118 struct Slot { 119 int32_t fValue; // values in each slot 120 int fScope; // the scope value of each slot 121 size_t fWriteTime; // when was the variable in this slot most recently written? 122 // (by cursor position) 123 }; 124 sk_sp<DebugTracePriv> fDebugTrace; 125 size_t fCursor = 0; // position of the read head 126 int fScope = 0; // the current scope depth (as tracked by 127 // trace_scope) 128 std::vector<Slot> fSlots; // the array of all slots 129 std::vector<StackFrame> fStack; // the execution stack 130 std::optional<SkBitSet> fDirtyMask; // variable slots touched during the most-recently 131 // executed step 132 std::optional<SkBitSet> fReturnValues; // variable slots containing return values 133 LineNumberMap fLineNumbers; // holds [line number, the remaining number of 134 // times to reach this line during the trace] 135 BreakpointSet fBreakpointLines; // all breakpoints set by setBreakpointLines 136 }; 137 138 } // namespace SkSL 139 140 #endif // SkSLDebugTracePlayer_DEFINED 141