Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkSLDebuggerSlide.cpp
Go to the documentation of this file.
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
9
16
17#include <algorithm>
18#include <cstdio>
19#include <string>
20#include <unordered_map>
21#include <unordered_set>
22#include <utility>
23#include <vector>
24
25#include "imgui.h"
26
27using namespace sk_app;
29
31 fName = "Debugger";
32 fTrace = sk_make_sp<SkSL::DebugTracePriv>();
33}
34
35void SkSLDebuggerSlide::load(SkScalar winWidth, SkScalar winHeight) {}
36
38 fTrace = sk_make_sp<SkSL::DebugTracePriv>();
39 fPlayer.reset(nullptr);
40 fPlayer.setBreakpoints(std::unordered_set<int>{});
41}
42
43void SkSLDebuggerSlide::showLoadTraceGUI() {
44 ImGui::InputText("Trace Path", fTraceFile, std::size(fTraceFile));
45 bool load = ImGui::Button("Load Debug Trace");
46
47 if (load) {
48 SkFILEStream file(fTraceFile);
49 if (!file.isValid()) {
50 ImGui::OpenPopup("Can't Open Trace");
51 } else if (!fTrace->readTrace(&file)) {
52 ImGui::OpenPopup("Invalid Trace");
53 } else {
54 // Trace loaded successfully. On the next refresh, the user will see the debug UI.
55 fPlayer.reset(fTrace);
56 fPlayer.step();
57 fRefresh = true;
58 return;
59 }
60 }
61
62 if (ImGui::BeginPopupModal("Can't Open Trace", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
63 ImGui::Text("The trace file doesn't exist.");
64 ImGui::Separator();
65 if (ImGui::Button("OK", ImVec2(120, 0))) {
66 ImGui::CloseCurrentPopup();
67 }
68 ImGui::EndPopup();
69 }
70
71 if (ImGui::BeginPopupModal("Invalid Trace", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
72 ImGui::Text("The trace data could not be parsed.");
73 ImGui::Separator();
74 if (ImGui::Button("OK", ImVec2(120, 0))) {
75 ImGui::CloseCurrentPopup();
76 }
77 ImGui::EndPopup();
78 }
79}
80
81void SkSLDebuggerSlide::showDebuggerGUI() {
82 if (ImGui::Button("Reset")) {
83 fPlayer.reset(fTrace);
84 fRefresh = true;
85 }
86 ImGui::SameLine(/*offset_from_start_x=*/0, /*spacing=*/100);
87 if (ImGui::Button("Step")) {
88 fPlayer.step();
89 fRefresh = true;
90 }
91 ImGui::SameLine();
92 if (ImGui::Button("Step Over")) {
93 fPlayer.stepOver();
94 fRefresh = true;
95 }
96 ImGui::SameLine();
97 if (ImGui::Button("Step Out")) {
98 fPlayer.stepOut();
99 fRefresh = true;
100 }
101 ImGui::SameLine(/*offset_from_start_x=*/0, /*spacing=*/100);
102 if (ImGui::Button(fPlayer.getBreakpoints().empty() ? "Run" : "Run to Breakpoint")) {
103 fPlayer.run();
104 fRefresh = true;
105 }
106
107 this->showStackTraceTable();
108 this->showVariableTable();
109 this->showCodeTable();
110}
111
112void SkSLDebuggerSlide::showCodeTable() {
113 constexpr ImGuiTableFlags kTableFlags =
114 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter |
115 ImGuiTableFlags_BordersV;
116 constexpr ImGuiTableColumnFlags kColumnFlags =
117 ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoReorder |
118 ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoSort |
119 ImGuiTableColumnFlags_NoHeaderLabel;
120
121 ImVec2 contentRect = ImGui::GetContentRegionAvail();
122 ImVec2 codeViewSize = ImVec2(0.0f, contentRect.y);
123 if (ImGui::BeginTable("Code View", /*column=*/2, kTableFlags, codeViewSize)) {
124 ImGui::TableSetupColumn("", kColumnFlags | ImGuiTableColumnFlags_WidthFixed);
125 ImGui::TableSetupColumn("Code", kColumnFlags | ImGuiTableColumnFlags_WidthStretch);
126
127 ImGuiListClipper clipper;
128 clipper.Begin(fTrace->fSource.size());
129 while (clipper.Step()) {
130 for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++) {
131 size_t humanReadableLine = row + 1;
132
133 ImGui::TableNextRow();
134 if (fPlayer.getCurrentLine() == (int)humanReadableLine) {
135 ImGui::TableSetBgColor(
136 ImGuiTableBgTarget_RowBg1,
137 ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_TextSelectedBg)));
138 }
139
140 // Show line numbers and breakpoints.
141 ImGui::TableSetColumnIndex(0);
142 const LineNumberMap& lineNumberMap = fPlayer.getLineNumbersReached();
143 LineNumberMap::const_iterator iter = lineNumberMap.find(humanReadableLine);
144 bool reachable = iter != lineNumberMap.end() && iter->second > 0;
145 bool breakpointOn = fPlayer.getBreakpoints().count(humanReadableLine);
146 if (breakpointOn) {
147 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
148 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 0.0f, 0.0f, 0.70f));
149 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.0f, 0.0f, 0.85f));
150 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
151 } else if (reachable) {
152 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 0.75f));
153 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
154 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f));
155 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.4f));
156 } else {
157 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 0.25f));
158 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
159 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
160 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
161 }
162 if (ImGui::SmallButton(SkStringPrintf("%03zu ", humanReadableLine).c_str())) {
163 if (breakpointOn) {
164 fPlayer.removeBreakpoint(humanReadableLine);
165 } else if (reachable) {
166 fPlayer.addBreakpoint(humanReadableLine);
167 }
168 }
169 ImGui::PopStyleColor(4);
170
171 // Show lines of code.
172 ImGui::TableSetColumnIndex(1);
173 ImGui::Text("%s", fTrace->fSource[row].c_str());
174 }
175 }
176
177 if (fRefresh) {
178 int linesVisible = contentRect.y / ImGui::GetTextLineHeightWithSpacing();
179 int centerLine = (fPlayer.getCurrentLine() - 1) - (linesVisible / 2);
180 centerLine = std::max(0, centerLine);
181 ImGui::SetScrollY(clipper.ItemsHeight * centerLine);
182 fRefresh = false;
183 }
184
185 ImGui::EndTable();
186 }
187}
188
189void SkSLDebuggerSlide::showStackTraceTable() {
190 constexpr ImGuiTableFlags kTableFlags =
191 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter |
192 ImGuiTableFlags_BordersV | ImGuiTableFlags_NoHostExtendX;
193 constexpr ImGuiTableColumnFlags kColumnFlags =
194 ImGuiTableColumnFlags_NoReorder | ImGuiTableColumnFlags_NoHide |
195 ImGuiTableColumnFlags_NoSort;
196
197 std::vector<int> callStack = fPlayer.getCallStack();
198
199 ImVec2 contentRect = ImGui::GetContentRegionAvail();
200 ImVec2 stackViewSize = ImVec2(contentRect.x / 3.0f,
201 ImGui::GetTextLineHeightWithSpacing() * kNumTopRows);
202 if (ImGui::BeginTable("Call Stack", /*column=*/1, kTableFlags, stackViewSize)) {
203 ImGui::TableSetupColumn("Stack", kColumnFlags);
204 ImGui::TableHeadersRow();
205
206 ImGuiListClipper clipper;
207 clipper.Begin(callStack.size());
208 while (clipper.Step()) {
209 for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++) {
210 int funcIdx = callStack.rbegin()[row];
211 SkASSERT(funcIdx >= 0 && (size_t)funcIdx < fTrace->fFuncInfo.size());
212 const SkSL::FunctionDebugInfo& funcInfo = fTrace->fFuncInfo[funcIdx];
213
214 ImGui::TableNextRow();
215 ImGui::TableSetColumnIndex(0);
216 ImGui::Text("%s", funcInfo.name.c_str());
217 }
218 }
219 ImGui::EndTable();
220 }
221
222 ImGui::SameLine();
223}
224
225void SkSLDebuggerSlide::showVariableTable() {
226 constexpr ImGuiTableFlags kTableFlags =
227 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter |
228 ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable;
229 constexpr ImGuiTableColumnFlags kColumnFlags =
230 ImGuiTableColumnFlags_NoReorder | ImGuiTableColumnFlags_NoHide |
231 ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthStretch;
232
233 int frame = fPlayer.getStackDepth() - 1;
234 std::vector<SkSL::SkSLDebugTracePlayer::VariableData> vars;
235 if (frame >= 0) {
236 vars = fPlayer.getLocalVariables(frame);
237 } else {
238 vars = fPlayer.getGlobalVariables();
239 }
240 ImVec2 varViewSize = ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * kNumTopRows);
241 if (ImGui::BeginTable("Variables", /*column=*/2, kTableFlags, varViewSize)) {
242 ImGui::TableSetupColumn("Variable", kColumnFlags);
243 ImGui::TableSetupColumn("Value", kColumnFlags);
244 ImGui::TableHeadersRow();
245 if (!vars.empty()) {
246 ImGuiListClipper clipper;
247 clipper.Begin(vars.size());
248 while (clipper.Step()) {
249 for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++) {
251 SkASSERT(var.fSlotIndex >= 0);
252 SkASSERT((size_t)var.fSlotIndex < fTrace->fSlotInfo.size());
253 const SkSL::SlotDebugInfo& slotInfo = fTrace->fSlotInfo[var.fSlotIndex];
254
255 ImGui::TableNextRow();
256 if (var.fDirty) {
257 // Highlight recently-changed variables.
258 ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1,
259 ImGui::GetColorU32(ImVec4{0.0f, 1.0f, 0.0f, 0.20f}));
260 }
261 ImGui::TableSetColumnIndex(0);
262 ImGui::Text("%s%s", slotInfo.name.c_str(),
263 fTrace->getSlotComponentSuffix(var.fSlotIndex).c_str());
264 ImGui::TableSetColumnIndex(1);
265 ImGui::Text("%s",
266 fTrace->slotValueToString(var.fSlotIndex, var.fValue).c_str());
267 }
268 }
269 }
270 ImGui::EndTable();
271 }
272}
273
274void SkSLDebuggerSlide::showRootGUI() {
275 if (fTrace->fSource.empty()) {
276 this->showLoadTraceGUI();
277 return;
278 }
279
280 this->showDebuggerGUI();
281}
282
284 canvas->clear(SK_ColorWHITE);
285 ImGui::Begin("Debugger", nullptr, ImGuiWindowFlags_AlwaysVerticalScrollbar);
286 this->showRootGUI();
287 ImGui::End();
288}
289
290bool SkSLDebuggerSlide::animate(double nanos) {
291 return true;
292}
#define SkASSERT(cond)
Definition SkAssert.h:116
constexpr SkColor SK_ColorWHITE
Definition SkColor.h:122
SkSL::SkSLDebugTracePlayer::LineNumberMap LineNumberMap
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
void clear(SkColor color)
Definition SkCanvas.h:1199
void unload() override
bool animate(double nanos) override
void draw(SkCanvas *canvas) override
void load(SkScalar winWidth, SkScalar winHeight) override
bool readTrace(SkStream *r)
std::vector< FunctionDebugInfo > fFuncInfo
std::vector< std::string > fSource
std::vector< SlotDebugInfo > fSlotInfo
std::string slotValueToString(int slotIndex, double value) const
std::string getSlotComponentSuffix(int slotIndex) const
const BreakpointSet & getBreakpoints()
const LineNumberMap & getLineNumbersReached() const
void reset(sk_sp< DebugTracePriv > trace)
std::vector< VariableData > getGlobalVariables() const
std::vector< VariableData > getLocalVariables(int stackFrameIndex) const
std::unordered_map< int, int > LineNumberMap
void setBreakpoints(std::unordered_set< int > breakpointLines)
std::vector< int > getCallStack() const
SkString fName
Definition Slide.h:54
double frame
Definition examples.cpp:31
float SkScalar
Definition extension.cpp:12