Flutter Engine
The Flutter Engine
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.");
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.");
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 SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
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
static float max(float r, float g, float b)
Definition: hsl.cpp:49
std::string void void auto Separator()
Definition: SkSLString.h:30
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
Definition: __init__.py:1