blob: c261d85c0e84ca938e39e7431e187b10c43dc4b6 [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/sksl/tracing/SkVMDebugTrace.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkTypes.h"
#include "src/utils/SkBitSet.h"
#include <optional>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace SkSL {
/**
* Plays back a SkVM debug trace, allowing its contents to be viewed like a traditional debugger.
*/
class SkVMDebugTracePlayer {
public:
/** Resets playback to the start of the trace. Breakpoints are not cleared. */
void reset(sk_sp<SkVMDebugTrace> trace);
/** Advances the simulation to the next Line op. */
void step();
/**
* Advances the simulation to the next Line op, skipping past matched Enter/Exit pairs.
* Breakpoints will also stop the simulation even if we haven't reached an Exit.
*/
void stepOver();
/**
* Advances the simulation until we exit from the current stack frame.
* Breakpoints will also stop the simulation even if we haven't left the stack frame.
*/
void stepOut();
/** Advances the simulation until we hit a breakpoint, or the trace completes. */
void run();
/** Breakpoints will force the simulation to stop whenever a desired line is reached. */
void setBreakpoints(std::unordered_set<int> breakpointLines);
void addBreakpoint(int line);
void removeBreakpoint(int line);
using BreakpointSet = std::unordered_set<int>;
const BreakpointSet& getBreakpoints() { return fBreakpointLines; }
/** Returns true if we have reached the end of the trace. */
bool traceHasCompleted() const;
/** Returns true if there is a breakpoint set at the current line. */
bool atBreakpoint() const;
/** Retrieves the cursor position. */
size_t cursor() { return fCursor; }
/** Retrieves the current line. */
int32_t getCurrentLine() const;
/** Retrieves the current line for a given stack frame. */
int32_t getCurrentLineInStackFrame(int stackFrameIndex) const;
/** Returns the call stack as an array of FunctionInfo indices. */
std::vector<int> getCallStack() const;
/** Returns the size of the call stack. */
int getStackDepth() const;
/**
* Returns every line number reached inside this debug trace, along with the remaining number of
* times that this trace will reach it. e.g. {100, 2} means line 100 will be reached twice.
*/
using LineNumberMap = std::unordered_map<int, int>;
const LineNumberMap& getLineNumbersReached() const { return fLineNumbers; }
/** Returns variables from a stack frame, or from global scope. */
struct VariableData {
int fSlotIndex;
bool fDirty; // has this slot been written-to since the last step call?
double fValue; // value in slot (with type-conversion applied)
};
std::vector<VariableData> getLocalVariables(int stackFrameIndex) const;
std::vector<VariableData> getGlobalVariables() const;
private:
/**
* Executes the trace op at the passed-in cursor position. Returns true if we've reached a line
* or exit trace op, which indicate a stopping point.
*/
bool execute(size_t position);
/**
* Cleans up temporary state between steps, such as the dirty mask and function return values.
*/
void tidyState();
/** Updates fWriteTime for the entire variable at a given slot. */
void updateVariableWriteTime(int slotIdx, size_t writeTime);
/** Returns a vector of the indices and values of each slot that is enabled in `bits`. */
std::vector<VariableData> getVariablesForDisplayMask(const SkBitSet& bits) const;
struct StackFrame {
int32_t fFunction; // from fFuncInfo
int32_t fLine; // our current line number within the function
SkBitSet fDisplayMask; // the variable slots which have been touched in this function
};
struct Slot {
int32_t fValue; // values in each slot
int fScope; // the scope value of each slot
size_t fWriteTime; // when was the variable in this slot most recently written?
// (by cursor position)
};
sk_sp<SkVMDebugTrace> fDebugTrace;
size_t fCursor = 0; // position of the read head
int fScope = 0; // the current scope depth (as tracked by
// trace_scope)
std::vector<Slot> fSlots; // the array of all slots
std::vector<StackFrame> fStack; // the execution stack
std::optional<SkBitSet> fDirtyMask; // variable slots touched during the most-recently
// executed step
std::optional<SkBitSet> fReturnValues; // variable slots containing return values
LineNumberMap fLineNumbers; // holds [line number, the remaining number of
// times to reach this line during the trace]
BreakpointSet fBreakpointLines; // all breakpoints set by setBreakpointLines
};
} // namespace SkSL