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