Track line reachability on a step-by-step basis.

Since we know the entire flow of execution ahead of time, at any point
during trace playback, we can know if a line will be reached again or
not. We no longer highlight lines as reachable (or allow setting
breakpoints) if the line will not be reached again during trace
playback.

Change-Id: Iff563b13e2f6efb5d4f2ff37215f2ff4fb5945ed
Bug: skia:12666
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/486496
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
This commit is contained in:
John Stiles 2021-12-22 13:56:21 -05:00 committed by SkCQ
parent 1ba283e6b4
commit 87ced29082
4 changed files with 62 additions and 28 deletions

View File

@ -13,6 +13,7 @@ void SkVMDebugTracePlayer::reset(sk_sp<SkVMDebugTrace> debugTrace) {
size_t nslots = debugTrace ? debugTrace->fSlotInfo.size() : 0;
fDebugTrace = debugTrace;
fCursor = 0;
fScope = 0;
fSlots.clear();
fSlots.resize(nslots, {/*fValue=*/0,
/*fScope=*/INT_MAX,
@ -32,7 +33,7 @@ void SkVMDebugTracePlayer::reset(sk_sp<SkVMDebugTrace> debugTrace) {
for (const SkVMTraceInfo& trace : fDebugTrace->fTraceInfo) {
if (trace.op == SkVMTraceInfo::Op::kLine) {
fLineNumbers.insert(trace.data[0]);
fLineNumbers[trace.data[0]] += 1;
}
}
}
@ -193,7 +194,9 @@ bool SkVMDebugTracePlayer::execute(size_t position) {
int lineNumber = trace.data[0];
SkASSERT(lineNumber >= 0);
SkASSERT((size_t)lineNumber < fDebugTrace->fSource.size());
SkASSERT(fLineNumbers[lineNumber] > 0);
fStack.back().fLine = lineNumber;
fLineNumbers[lineNumber] -= 1;
return true;
}
case SkVMTraceInfo::Op::kVar: { // data: slot, value

View File

@ -9,6 +9,7 @@
#include "src/sksl/tracing/SkVMDebugTrace.h"
#include "src/utils/SkBitSet.h"
#include <unordered_map>
#include <unordered_set>
namespace SkSL {
@ -43,7 +44,8 @@ public:
void setBreakpoints(std::unordered_set<int> breakpointLines);
void addBreakpoint(int line);
void removeBreakpoint(int line);
const std::unordered_set<int>& getBreakpoints() { return fBreakpointLines; }
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;
@ -63,8 +65,12 @@ public:
/** Returns the size of the call stack. */
int getStackDepth() const;
/** Returns every line number actually reached in the debug trace. */
const std::unordered_set<int>& getLineNumbersReached() const { return fLineNumbers; }
/**
* 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 {
@ -104,17 +110,18 @@ private:
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
skstd::optional<SkBitSet> fDirtyMask; // variable slots touched during the most-recently
// executed step
skstd::optional<SkBitSet> fReturnValues; // variable slots containing return values
std::unordered_set<int> fLineNumbers; // every line number reached during execution
std::unordered_set<int> fBreakpointLines; // all breakpoints set by setBreakpointLines
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
skstd::optional<SkBitSet> fDirtyMask; // variable slots touched during the most-recently
// executed step
skstd::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

View File

@ -13,6 +13,8 @@
#include "tests/Test.h"
using LineNumberMap = SkSL::SkVMDebugTracePlayer::LineNumberMap;
static sk_sp<SkSL::SkVMDebugTrace> make_trace(skiatest::Reporter* r, SkSL::String src) {
SkSL::ShaderCaps caps;
SkSL::Compiler compiler(&caps);
@ -110,7 +112,7 @@ int main() { // Line 2
REPORTER_ASSERT(r, !player.traceHasCompleted());
REPORTER_ASSERT(r, player.getCallStack().empty());
REPORTER_ASSERT(r, player.getGlobalVariables().empty());
REPORTER_ASSERT(r, player.getLineNumbersReached() == std::unordered_set<int>{3});
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 1}}));
player.step();
@ -190,7 +192,7 @@ int main() { // Line 8
REPORTER_ASSERT(r, !player.traceHasCompleted());
REPORTER_ASSERT(r, player.getCallStack().empty());
REPORTER_ASSERT(r, player.getGlobalVariables().empty());
REPORTER_ASSERT(r, (player.getLineNumbersReached() == std::unordered_set<int>{3, 6, 9}));
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 1}, {6, 1}, {9, 1}}));
player.step();
@ -261,8 +263,9 @@ int main() { // Line 6
SkSL::SkVMDebugTracePlayer player;
player.reset(trace);
REPORTER_ASSERT(r, (player.getLineNumbersReached() ==
std::unordered_set<int>{3, 4, 7, 8, 9, 10, 11, 12}));
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 1}, {4, 1}, {7, 1},
{8, 1}, {9, 1}, {10, 1},
{11, 1}, {12, 1}}));
player.step();
REPORTER_ASSERT(r, player.getCurrentLine() == 7);
@ -345,8 +348,9 @@ int main() { // Line 2
SkSL::SkVMDebugTracePlayer player;
player.reset(trace);
REPORTER_ASSERT(r, (player.getLineNumbersReached() ==
std::unordered_set<int>{3, 4, 5, 6, 10, 14, 16}));
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 1}, {4, 1}, {5, 1},
{6, 1}, {10, 1}, {14, 1},
{16, 1}}));
player.step();
REPORTER_ASSERT(r, player.getCurrentLine() == 3);
@ -397,34 +401,49 @@ int main() { // Line 2
SkSL::SkVMDebugTracePlayer player;
player.reset(trace);
REPORTER_ASSERT(r, (player.getLineNumbersReached() == std::unordered_set<int>{3, 4, 5, 7}));
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 1}, {4, 3}, {5, 2},
{7, 1}}));
player.step();
REPORTER_ASSERT(r, player.getCurrentLine() == 3);
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 0}, {4, 3}, {5, 2},
{7, 1}}));
REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "");
player.step();
REPORTER_ASSERT(r, player.getCurrentLine() == 4);
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 0}, {4, 2}, {5, 2},
{7, 1}}));
REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##val = 0");
player.step();
REPORTER_ASSERT(r, player.getCurrentLine() == 5);
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 0}, {4, 2}, {5, 1},
{7, 1}}));
REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##x = 1, val = 0");
player.step();
REPORTER_ASSERT(r, player.getCurrentLine() == 4);
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 0}, {4, 1}, {5, 1},
{7, 1}}));
REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##val = 1, x = 1");
player.step();
REPORTER_ASSERT(r, player.getCurrentLine() == 5);
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 0}, {4, 1}, {5, 0},
{7, 1}}));
REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##x = 2, val = 1");
player.step();
REPORTER_ASSERT(r, player.getCurrentLine() == 4);
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 0}, {4, 0}, {5, 0},
{7, 1}}));
REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##val = 2, x = 2");
player.step();
REPORTER_ASSERT(r, player.getCurrentLine() == 7);
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 0}, {4, 0}, {5, 0},
{7, 0}}));
REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "val = 2");
player.step();
@ -448,8 +467,8 @@ int main() { // Line 9
)");
SkSL::SkVMDebugTracePlayer player;
player.reset(trace);
REPORTER_ASSERT(r, (player.getLineNumbersReached() ==
std::unordered_set<int>{3, 4, 5, 6, 7, 10}));
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 1}, {4, 1}, {5, 1},
{6, 1}, {7, 1}, {10, 1}}));
player.step();
// We should now be inside main.
@ -510,8 +529,10 @@ int main() { // Line 2
)");
SkSL::SkVMDebugTracePlayer player;
player.reset(trace);
REPORTER_ASSERT(r, (player.getLineNumbersReached() ==
std::unordered_set<int>{3, 5, 7, 9, 11, 13, 15, 17, 19, 20}));
REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 1}, {5, 1}, {7, 1},
{9, 1}, {11, 1}, {13, 1},
{15, 1}, {17, 1}, {19, 1},
{20, 1}}));
player.step();
// We should now be inside main.

View File

@ -15,6 +15,7 @@
#include "imgui.h"
using namespace sk_app;
using LineNumberMap = SkSL::SkVMDebugTracePlayer::LineNumberMap;
///////////////////////////////////////////////////////////////////////////////
@ -130,8 +131,10 @@ void SkSLDebuggerSlide::showCodeTable() {
// Show line numbers and breakpoints.
ImGui::TableSetColumnIndex(0);
bool reachable = fPlayer.getLineNumbersReached().count(humanReadableLine);
bool breakpointOn = reachable && fPlayer.getBreakpoints().count(humanReadableLine);
const LineNumberMap& lineNumberMap = fPlayer.getLineNumbersReached();
LineNumberMap::const_iterator iter = lineNumberMap.find(humanReadableLine);
bool reachable = iter != lineNumberMap.end() && iter->second > 0;
bool breakpointOn = fPlayer.getBreakpoints().count(humanReadableLine);
if (breakpointOn) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 0.0f, 0.0f, 0.70f));