| /* | |
| * 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 "include/core/SkRefCnt.h" | |
| #include "src/base/SkArenaAlloc.h" | |
| #include "src/core/SkRasterPipeline.h" | |
| #include "src/sksl/SkSLCompiler.h" | |
| #include "src/sksl/SkSLProgramKind.h" | |
| #include "src/sksl/SkSLProgramSettings.h" | |
| #include "src/sksl/SkSLString.h" | |
| #include "src/sksl/SkSLUtil.h" | |
| #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h" | |
| #include "src/sksl/codegen/SkSLRasterPipelineCodeGenerator.h" | |
| #include "src/sksl/ir/SkSLFunctionDeclaration.h" | |
| #include "src/sksl/ir/SkSLProgram.h" // IWYU pragma: keep | |
| #include "src/sksl/tracing/SkSLDebugTracePlayer.h" | |
| #include "src/sksl/tracing/SkSLDebugTracePriv.h" | |
| #include "tests/Test.h" | |
| #include <cstddef> | |
| #include <memory> | |
| #include <string> | |
| #include <unordered_map> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <vector> | |
| #if defined(SKSL_ENABLE_TRACING) | |
| using LineNumberMap = SkSL::SkSLDebugTracePlayer::LineNumberMap; | |
| static sk_sp<SkSL::DebugTracePriv> make_trace(skiatest::Reporter* r, | |
| std::string src) { | |
| auto debugTrace = sk_make_sp<SkSL::DebugTracePriv>(); | |
| SkSL::ShaderCaps caps; | |
| SkSL::Compiler compiler(&caps); | |
| SkSL::ProgramSettings settings; | |
| settings.fOptimize = false; | |
| std::unique_ptr<SkSL::Program> program = | |
| compiler.convertProgram(SkSL::ProgramKind::kRuntimeShader, std::move(src), settings); | |
| REPORTER_ASSERT(r, program); | |
| if (program) { | |
| const SkSL::FunctionDeclaration* main = program->getFunction("main"); | |
| REPORTER_ASSERT(r, main); | |
| if (main) { | |
| // Compile our program. | |
| SkArenaAlloc alloc(/*firstHeapAllocation=*/1000); | |
| SkRasterPipeline pipeline(&alloc); | |
| std::unique_ptr<SkSL::RP::Program> rasterProg = SkSL::MakeRasterPipelineProgram( | |
| *program, *main->definition(), debugTrace.get(), /*writeTraceOps=*/true); | |
| REPORTER_ASSERT(r, rasterProg); | |
| if (rasterProg) { | |
| // Append the SkSL program to the raster pipeline, and run it at xy=(0.5, 0.5). | |
| static constexpr float kCoordinates[4] = {0.5f, 0.5f, 0.0f, 1.0f}; | |
| pipeline.append_constant_color(&alloc, kCoordinates); | |
| rasterProg->appendStages(&pipeline, | |
| &alloc, | |
| /*callbacks=*/nullptr, | |
| /*uniforms=*/{}); | |
| pipeline.run(0, 0, 1, 1); | |
| } | |
| } | |
| } | |
| return debugTrace; | |
| } | |
| static std::string make_stack_string(const SkSL::DebugTracePriv& trace, | |
| const SkSL::SkSLDebugTracePlayer& player) { | |
| std::vector<int> callStack = player.getCallStack(); | |
| std::string text; | |
| const char* separator = ""; | |
| for (int frame : callStack) { | |
| text += separator; | |
| separator = " -> "; | |
| if (frame >= 0 && (size_t)frame < trace.fFuncInfo.size()) { | |
| text += trace.fFuncInfo[frame].name; | |
| } else { | |
| text += "???"; | |
| } | |
| } | |
| return text; | |
| } | |
| static std::string make_vars_string( | |
| const SkSL::DebugTracePriv& trace, | |
| const std::vector<SkSL::SkSLDebugTracePlayer::VariableData>& vars) { | |
| std::string text; | |
| auto separator = SkSL::String::Separator(); | |
| for (const SkSL::SkSLDebugTracePlayer::VariableData& var : vars) { | |
| text += separator(); | |
| if (var.fSlotIndex < 0 || (size_t)var.fSlotIndex >= trace.fSlotInfo.size()) { | |
| text += "???"; | |
| continue; | |
| } | |
| const SkSL::SlotDebugInfo& slot = trace.fSlotInfo[var.fSlotIndex]; | |
| text += var.fDirty ? "##" : ""; | |
| text += slot.name; | |
| text += trace.getSlotComponentSuffix(var.fSlotIndex); | |
| text += " = "; | |
| text += trace.slotValueToString(var.fSlotIndex, var.fValue); | |
| } | |
| return text; | |
| } | |
| static std::string make_local_vars_string(const SkSL::DebugTracePriv& trace, | |
| const SkSL::SkSLDebugTracePlayer& player) { | |
| int frame = player.getStackDepth() - 1; | |
| return make_vars_string(trace, player.getLocalVariables(frame)); | |
| } | |
| static std::string make_global_vars_string(const SkSL::DebugTracePriv& trace, | |
| const SkSL::SkSLDebugTracePlayer& player) { | |
| return make_vars_string(trace, player.getGlobalVariables()); | |
| } | |
| DEF_TEST(SkSLTracePlayerCanResetToNull, r) { | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(nullptr); | |
| // We should be in a reasonable state. | |
| REPORTER_ASSERT(r, player.cursor() == 0); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == -1); | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, player.getCallStack().empty()); | |
| REPORTER_ASSERT(r, player.getGlobalVariables().empty()); | |
| REPORTER_ASSERT(r, player.getLineNumbersReached().empty()); | |
| } | |
| DEF_TEST(SkSLTracePlayerHelloWorld, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| half4 main(float2 xy) { // Line 2 | |
| return half4(2 + 2); // Line 3 | |
| } // Line 4 | |
| )"); | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| // We have not started tracing yet. | |
| REPORTER_ASSERT(r, player.cursor() == 0); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == -1); | |
| REPORTER_ASSERT(r, !player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, player.getCallStack().empty()); | |
| REPORTER_ASSERT(r, player.getGlobalVariables().empty()); | |
| REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 1}})); | |
| player.step(); | |
| // We should now be inside main. | |
| REPORTER_ASSERT(r, player.cursor() > 0); | |
| REPORTER_ASSERT(r, !player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 3); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT(r, player.getGlobalVariables().empty()); | |
| REPORTER_ASSERT(r, player.getLocalVariables(0).size() == 2); // xy | |
| player.step(); | |
| // We have now completed the trace. | |
| REPORTER_ASSERT(r, player.cursor() > 0); | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == -1); | |
| REPORTER_ASSERT(r, player.getCallStack().empty()); | |
| REPORTER_ASSERT(r, | |
| make_global_vars_string(*trace, player) == | |
| "##[main].result.x = 4, ##[main].result.y = 4, ##[main].result.z = " | |
| "4, ##[main].result.w = 4"); | |
| } | |
| DEF_TEST(SkSLTracePlayerReset, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| half4 main(float2 xy) { // Line 2 | |
| return half4(2 + 2); // Line 3 | |
| } // Line 4 | |
| )"); | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| // We have not started tracing yet. | |
| REPORTER_ASSERT(r, player.cursor() == 0); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == -1); | |
| REPORTER_ASSERT(r, !player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, player.getCallStack().empty()); | |
| REPORTER_ASSERT(r, player.getGlobalVariables().empty()); | |
| player.step(); | |
| // We should now be inside main. | |
| REPORTER_ASSERT(r, player.cursor() > 0); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 3); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT(r, player.getGlobalVariables().empty()); | |
| REPORTER_ASSERT(r, player.getLocalVariables(0).size() == 2); // xy | |
| player.reset(trace); | |
| // We should be back to square one. | |
| REPORTER_ASSERT(r, player.cursor() == 0); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == -1); | |
| REPORTER_ASSERT(r, !player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, player.getCallStack().empty()); | |
| REPORTER_ASSERT(r, player.getGlobalVariables().empty()); | |
| } | |
| DEF_TEST(SkSLTracePlayerFunctions, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| int fnB() { // Line 2 | |
| return 2 + 2; // Line 3 | |
| } // Line 4 | |
| int fnA() { // Line 5 | |
| return fnB(); // Line 6 | |
| } // Line 7 | |
| half4 main(float2 xy) { // Line 8 | |
| return half4(fnA()); // Line 9 | |
| } // Line 10 | |
| )"); | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| // We have not started tracing yet. | |
| REPORTER_ASSERT(r, player.cursor() == 0); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == -1); | |
| REPORTER_ASSERT(r, !player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, player.getCallStack().empty()); | |
| REPORTER_ASSERT(r, player.getGlobalVariables().empty()); | |
| REPORTER_ASSERT(r, player.getLineNumbersReached() == LineNumberMap({{3, 1}, {6, 1}, {9, 1}})); | |
| player.step(); | |
| // We should now be inside main. | |
| REPORTER_ASSERT(r, !player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 9); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT(r, player.getGlobalVariables().empty()); | |
| REPORTER_ASSERT(r, player.getLocalVariables(0).size() == 2); // xy | |
| player.stepOver(); | |
| // We should now have completed execution. | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == -1); | |
| REPORTER_ASSERT(r, player.getCallStack().empty()); | |
| REPORTER_ASSERT(r, | |
| make_global_vars_string(*trace, player) == | |
| "##[main].result.x = 4, ##[main].result.y = 4, ##[main].result.z = " | |
| "4, ##[main].result.w = 4"); | |
| // Watch the stack grow and shrink as single-step. | |
| player.reset(trace); | |
| player.step(); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT(r, player.getCurrentLineInStackFrame(0) == 9); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##xy.x = 0.5, ##xy.y = 0.5"); | |
| REPORTER_ASSERT(r, make_global_vars_string(*trace, player) == ""); | |
| player.step(); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy) -> int fnA()"); | |
| REPORTER_ASSERT(r, player.getCurrentLineInStackFrame(0) == 9); | |
| REPORTER_ASSERT(r, player.getCurrentLineInStackFrame(1) == 6); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == ""); | |
| REPORTER_ASSERT(r, make_global_vars_string(*trace, player) == ""); | |
| player.step(); | |
| REPORTER_ASSERT( | |
| r, | |
| make_stack_string(*trace, player) == "half4 main(float2 xy) -> int fnA() -> int fnB()"); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == ""); | |
| REPORTER_ASSERT(r, make_global_vars_string(*trace, player) == ""); | |
| REPORTER_ASSERT(r, player.getCurrentLineInStackFrame(0) == 9); | |
| REPORTER_ASSERT(r, player.getCurrentLineInStackFrame(1) == 6); | |
| REPORTER_ASSERT(r, player.getCurrentLineInStackFrame(2) == 3); | |
| player.step(); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy) -> int fnA()"); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##[fnB].result = 4"); | |
| REPORTER_ASSERT(r, make_global_vars_string(*trace, player) == ""); | |
| player.step(); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT( | |
| r, | |
| make_local_vars_string(*trace, player) == "##[fnA].result = 4, xy.x = 0.5, xy.y = 0.5"); | |
| REPORTER_ASSERT(r, make_global_vars_string(*trace, player) == ""); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, | |
| make_global_vars_string(*trace, player) == | |
| "##[main].result.x = 4, ##[main].result.y = 4, ##[main].result.z = " | |
| "4, ##[main].result.w = 4"); | |
| } | |
| DEF_TEST(SkSLTracePlayerVariables, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| float func() { // Line 2 | |
| float x = 4, y = 5, z = 6; // Line 3 | |
| return z; // Line 4 | |
| } // Line 5 | |
| half4 main(float2 xy) { // Line 6 | |
| int a = 123; // Line 7 | |
| bool b = true; // Line 8 | |
| func(); // Line 9 | |
| float4 c = float4(0, 0.5, 1, -1); // Line 10 | |
| float3x3 d = float3x3(2); // Line 11 | |
| return half4(a); // Line 12 | |
| } // Line 13 | |
| )"); | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| 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); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##xy.x = 0.5, ##xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 8); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == "##a = 123, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 9); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "##b = true, a = 123, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 3); | |
| REPORTER_ASSERT(r, | |
| make_stack_string(*trace, player) == "half4 main(float2 xy) -> float func()"); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == ""); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 4); | |
| REPORTER_ASSERT(r, | |
| make_stack_string(*trace, player) == "half4 main(float2 xy) -> float func()"); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##z = 6, ##y = 5, ##x = 4"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 9); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "##[func].result = 6, b = true, a = 123, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 10); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT( | |
| r, | |
| make_local_vars_string(*trace, player) == "b = true, a = 123, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 11); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "##c.x = 0, ##c.y = 0.5, ##c.z = 1, ##c.w = -1, b = true, a = 123, " | |
| "xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 12); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "##d[0][0] = 2, ##d[0][1] = 0, ##d[0][2] = 0, " | |
| "##d[1][0] = 0, ##d[1][1] = 2, ##d[1][2] = 0, " | |
| "##d[2][0] = 0, ##d[2][1] = 0, ##d[2][2] = 2, " | |
| "c.x = 0, c.y = 0.5, c.z = 1, c.w = -1, b = true, a = 123, " | |
| "xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == ""); | |
| REPORTER_ASSERT(r, | |
| make_global_vars_string(*trace, player) == | |
| "##[main].result.x = 123, ##[main].result.y = 123, " | |
| "##[main].result.z = 123, ##[main].result.w = 123"); | |
| } | |
| DEF_TEST(SkSLTracePlayerVariableGroups, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| struct S { int x, y, z; }; // Line 2 | |
| half4 main(float2 xy) { // Line 3 | |
| S s; // Line 4 | |
| int arr[3]; // Line 5 | |
| s.y = 1; // Line 6 | |
| arr[1] = 2; // Line 7 | |
| s.x = 3; // Line 8 | |
| arr[2] = 4; // Line 9 | |
| return half4(0); // Line 10 | |
| } // Line 11 | |
| )"); | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 4); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##xy.x = 0.5, ##xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 5); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "##s.x = 0, ##s.y = 0, ##s.z = 0, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 6); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "##arr[0] = 0, ##arr[1] = 0, ##arr[2] = 0, s.x = 0, s.y = 0, s.z = " | |
| "0, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 7); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "s.x = 0, ##s.y = 1, s.z = 0, arr[0] = 0, arr[1] = 0, arr[2] = 0, " | |
| "xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 8); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "arr[0] = 0, ##arr[1] = 2, arr[2] = 0, s.x = 0, s.y = 1, s.z = 0, " | |
| "xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 9); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "##s.x = 3, s.y = 1, s.z = 0, arr[0] = 0, arr[1] = 2, arr[2] = 0, " | |
| "xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 10); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "arr[0] = 0, arr[1] = 2, ##arr[2] = 4, s.x = 3, s.y = 1, s.z = 0, " | |
| "xy.x = 0.5, xy.y = 0.5"); | |
| } | |
| DEF_TEST(SkSLTracePlayerIfStatement, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| half4 main(float2 xy) { // Line 2 | |
| int val; // Line 3 | |
| if (true) { // Line 4 | |
| int temp = 1; // Line 5 | |
| val = temp; // Line 6 | |
| } else { // Line 7 | |
| val = 2; // Line 8 | |
| } // Line 9 | |
| if (false) { // Line 10 | |
| int temp = 3; // Line 11 | |
| val = temp; // Line 12 | |
| } else { // Line 13 | |
| val = 4; // Line 14 | |
| } // Line 15 | |
| return half4(val); // Line 16 | |
| } // Line 17 | |
| )"); | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| 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); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##xy.x = 0.5, ##xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 4); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == "##val = 0, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 5); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "val = 0, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 6); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "##temp = 1, val = 0, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| // We skip over the false-branch. | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 10); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == "##val = 1, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| // We skip over the true-branch. | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 14); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "val = 1, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 16); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == "##val = 4, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, | |
| make_global_vars_string(*trace, player) == | |
| "##[main].result.x = 4, ##[main].result.y = 4, ##[main].result.z = " | |
| "4, ##[main].result.w = 4"); | |
| } | |
| DEF_TEST(SkSLTracePlayerForLoop, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| half4 main(float2 xy) { // Line 2 | |
| int val = 0; // Line 3 | |
| for (int x = 1; x < 3; ++x) { // Line 4 | |
| val = x; // Line 5 | |
| } // Line 6 | |
| return half4(val); // Line 7 | |
| } // Line 8 | |
| )"); | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| 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) == "##xy.x = 0.5, ##xy.y = 0.5"); | |
| 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, xy.x = 0.5, xy.y = 0.5"); | |
| 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, xy.x = 0.5, xy.y = 0.5"); | |
| 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, xy.x = 0.5, xy.y = 0.5"); | |
| 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, xy.x = 0.5, xy.y = 0.5"); | |
| 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, xy.x = 0.5, xy.y = 0.5"); | |
| 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, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, | |
| make_global_vars_string(*trace, player) == | |
| "##[main].result.x = 2, ##[main].result.y = 2, ##[main].result.z = " | |
| "2, ##[main].result.w = 2"); | |
| } | |
| DEF_TEST(SkSLTracePlayerStepOut, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| int fn() { // Line 2 | |
| int a = 11; // Line 3 | |
| int b = 22; // Line 4 | |
| int c = 33; // Line 5 | |
| int d = 44; // Line 6 | |
| return d; // Line 7 | |
| } // Line 8 | |
| half4 main(float2 xy) { // Line 9 | |
| return half4(fn()); // Line 10 | |
| } // Line 11 | |
| )"); | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| 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. | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 10); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| player.step(); | |
| // We should now be inside fn. | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 3); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy) -> int fn()"); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == ""); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 4); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy) -> int fn()"); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##a = 11"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 5); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy) -> int fn()"); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##b = 22, a = 11"); | |
| player.stepOut(); | |
| // We should now be back inside main(), right where we left off. | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 10); | |
| REPORTER_ASSERT(r, make_stack_string(*trace, player) == "half4 main(float2 xy)"); | |
| REPORTER_ASSERT( | |
| r, | |
| make_local_vars_string(*trace, player) == "##[fn].result = 44, xy.x = 0.5, xy.y = 0.5"); | |
| player.stepOut(); | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| REPORTER_ASSERT(r, | |
| make_global_vars_string(*trace, player) == | |
| "##[main].result.x = 44, ##[main].result.y = 44, ##[main].result.z " | |
| "= 44, ##[main].result.w = 44"); | |
| } | |
| DEF_TEST(SkSLTracePlayerVariableScope, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| half4 main(float2 xy) { // Line 2 | |
| int a = 1; // Line 3 | |
| { // Line 4 | |
| int b = 2; // Line 5 | |
| { // Line 6 | |
| int c = 3; // Line 7 | |
| } // Line 8 | |
| int d = 4; // Line 9 | |
| } // Line 10 | |
| int e = 5; // Line 11 | |
| { // Line 12 | |
| int f = 6; // Line 13 | |
| { // Line 14 | |
| int g = 7; // Line 15 | |
| } // Line 16 | |
| int h = 8; // Line 17 | |
| } // Line 18 | |
| int i = 9; // Line 19 | |
| return half4(0); // Line 20 | |
| } | |
| )"); | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| 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. | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 3); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##xy.x = 0.5, ##xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 5); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "##a = 1, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 7); | |
| REPORTER_ASSERT( | |
| r, make_local_vars_string(*trace, player) == "##b = 2, a = 1, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 9); | |
| REPORTER_ASSERT( | |
| r, make_local_vars_string(*trace, player) == "b = 2, a = 1, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 11); | |
| REPORTER_ASSERT(r, make_local_vars_string(*trace, player) == "a = 1, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 13); | |
| REPORTER_ASSERT( | |
| r, make_local_vars_string(*trace, player) == "##e = 5, a = 1, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 15); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "##f = 6, e = 5, a = 1, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 17); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "f = 6, e = 5, a = 1, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 19); | |
| REPORTER_ASSERT( | |
| r, make_local_vars_string(*trace, player) == "e = 5, a = 1, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 20); | |
| REPORTER_ASSERT(r, | |
| make_local_vars_string(*trace, player) == | |
| "##i = 9, e = 5, a = 1, xy.x = 0.5, xy.y = 0.5"); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| } | |
| DEF_TEST(SkSLTracePlayerNestedBlocks, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| half4 main(float2 xy) { // Line 2 | |
| {{{{{{{ // Line 3 | |
| int a, b; // Line 4 | |
| }}}}}}} // Line 5 | |
| return half4(0); // Line 6 | |
| } | |
| )"); | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 4); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 6); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| } | |
| DEF_TEST(SkSLTracePlayerSwitchStatement, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| half4 main(float2 xy) { // Line 2 | |
| int x = 2; // Line 3 | |
| switch (x) { // Line 4 | |
| case 1: // Line 5 | |
| break; // Line 6 | |
| case 2: // Line 7 | |
| ++x; // Line 8 | |
| case 3: // Line 9 | |
| break; // Line 10 | |
| case 4: // Line 11 | |
| } // Line 12 | |
| return half4(x); // Line 13 | |
| } | |
| )"); | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 3); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 4); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 8); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 10); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 13); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| } | |
| DEF_TEST(SkSLTracePlayerBreakpoint, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| int counter = 0; // Line 2 | |
| void func() { // Line 3 | |
| --counter; // Line 4 BREAKPOINT 4 5 | |
| } // Line 5 | |
| half4 main(float2 xy) { // Line 6 | |
| for (int x = 1; x <= 3; ++x) { // Line 7 | |
| ++counter; // Line 8 BREAKPOINT 1 2 3 | |
| } // Line 9 | |
| func(); // Line 10 | |
| func(); // Line 11 | |
| ++counter; // Line 12 BREAKPOINT 6 | |
| return half4(counter); // Line 13 | |
| } // Line 14 | |
| )"); | |
| // Run the simulation with a variety of breakpoints set. | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| player.setBreakpoints(std::unordered_set<int>{8, 13, 20}); | |
| player.run(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 8); | |
| player.run(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 8); | |
| player.setBreakpoints(std::unordered_set<int>{1, 4, 8}); | |
| player.run(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 8); | |
| player.run(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 4); | |
| player.setBreakpoints(std::unordered_set<int>{4, 12, 14}); | |
| player.run(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 4); | |
| player.run(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 12); | |
| player.run(); | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| // Run the simulation again with no breakpoints set. We should reach the end of the trace | |
| // instantly. | |
| player.reset(trace); | |
| player.setBreakpoints(std::unordered_set<int>{}); | |
| REPORTER_ASSERT(r, !player.traceHasCompleted()); | |
| player.run(); | |
| REPORTER_ASSERT(r, player.traceHasCompleted()); | |
| } | |
| DEF_TEST(SkSLTracePlayerStepOverWithBreakpoint, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| int counter = 0; // Line 2 | |
| void func() { // Line 3 | |
| ++counter; // Line 4 BREAKPOINT | |
| } // Line 5 | |
| half4 main(float2 xy) { // Line 6 | |
| func(); // Line 7 | |
| return half4(counter); // Line 8 | |
| } // Line 9 | |
| )"); | |
| // Try stepping over with no breakpoint set; we will step over. | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 7); | |
| player.stepOver(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 8); | |
| // Try stepping over with a breakpoint set; we will stop at the breakpoint. | |
| player.reset(trace); | |
| player.setBreakpoints(std::unordered_set<int>{4}); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 7); | |
| player.stepOver(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 4); | |
| } | |
| DEF_TEST(SkSLTracePlayerStepOutWithBreakpoint, r) { | |
| sk_sp<SkSL::DebugTracePriv> trace = make_trace(r, | |
| R"( // Line 1 | |
| int counter = 0; // Line 2 | |
| void func() { // Line 3 | |
| ++counter; // Line 4 | |
| ++counter; // Line 5 | |
| ++counter; // Line 6 BREAKPOINT | |
| } // Line 7 | |
| half4 main(float2 xy) { // Line 8 | |
| func(); // Line 9 | |
| return half4(counter); // Line 10 | |
| } // Line 11 | |
| )"); | |
| // Try stepping out with no breakpoint set; we will step out. | |
| SkSL::SkSLDebugTracePlayer player; | |
| player.reset(trace); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 9); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 4); | |
| player.stepOut(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 9); | |
| // Try stepping out with a breakpoint set; we will stop at the breakpoint. | |
| player.reset(trace); | |
| player.setBreakpoints(std::unordered_set<int>{6}); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 9); | |
| player.step(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 4); | |
| player.stepOut(); | |
| REPORTER_ASSERT(r, player.getCurrentLine() == 6); | |
| } | |
| #endif // defined(SKSL_ENABLE_TRACING) |