• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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