• 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 
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