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