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