Flutter Engine
The Flutter Engine
SkSLDebugTracePriv.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
10#include "include/core/SkData.h"
16#include "src/utils/SkJSON.h"
18
19#include <cstdio>
20#include <cstring>
21#include <sstream>
22#include <string>
23#include <string_view>
24
25static constexpr char kTraceVersion[] = "20220209";
26
27namespace SkSL {
28
29std::string DebugTracePriv::getSlotComponentSuffix(int slotIndex) const {
30 const SkSL::SlotDebugInfo& slot = fSlotInfo[slotIndex];
31
32 if (slot.rows > 1) {
33 return "[" + std::to_string(slot.componentIndex / slot.rows) +
34 "][" + std::to_string(slot.componentIndex % slot.rows) +
35 "]";
36 }
37 if (slot.columns > 1) {
38 switch (slot.componentIndex) {
39 case 0: return ".x";
40 case 1: return ".y";
41 case 2: return ".z";
42 case 3: return ".w";
43 default: return "[???]";
44 }
45 }
46 return {};
47}
48
49double DebugTracePriv::interpretValueBits(int slotIndex, int32_t valueBits) const {
50 SkASSERT(slotIndex >= 0);
51 SkASSERT((size_t)slotIndex < fSlotInfo.size());
52 switch (fSlotInfo[slotIndex].numberKind) {
54 uint32_t uintValue;
55 static_assert(sizeof(uintValue) == sizeof(valueBits));
56 memcpy(&uintValue, &valueBits, sizeof(uintValue));
57 return uintValue;
58 }
60 float floatValue;
61 static_assert(sizeof(floatValue) == sizeof(valueBits));
62 memcpy(&floatValue, &valueBits, sizeof(floatValue));
63 return floatValue;
64 }
65 default: {
66 return valueBits;
67 }
68 }
69}
70
71std::string DebugTracePriv::slotValueToString(int slotIndex, double value) const {
72 SkASSERT(slotIndex >= 0);
73 SkASSERT((size_t)slotIndex < fSlotInfo.size());
74 switch (fSlotInfo[slotIndex].numberKind) {
76 return value ? "true" : "false";
77 }
78 default: {
79 char buffer[32];
80 snprintf(buffer, std::size(buffer), "%.8g", value);
81 return buffer;
82 }
83 }
84}
85
86std::string DebugTracePriv::getSlotValue(int slotIndex, int32_t valueBits) const {
87 return this->slotValueToString(slotIndex, this->interpretValueBits(slotIndex, valueBits));
88}
89
91 fTraceCoord = coord;
92}
93
94void DebugTracePriv::setSource(const std::string& source) {
95 fSource.clear();
96 std::stringstream stream{source};
97 while (stream.good()) {
98 fSource.push_back({});
99 std::getline(stream, fSource.back(), '\n');
100 }
101}
102
104 for (size_t index = 0; index < fSlotInfo.size(); ++index) {
105 const SlotDebugInfo& info = fSlotInfo[index];
106
107 o->writeText("$");
108 o->writeDecAsText(index);
109 o->writeText(" = ");
110 o->writeText(info.name.c_str());
111 o->writeText(" (");
112 switch (info.numberKind) {
113 case Type::NumberKind::kFloat: o->writeText("float"); break;
114 case Type::NumberKind::kSigned: o->writeText("int"); break;
115 case Type::NumberKind::kUnsigned: o->writeText("uint"); break;
116 case Type::NumberKind::kBoolean: o->writeText("bool"); break;
117 case Type::NumberKind::kNonnumeric: o->writeText("???"); break;
118 }
119 if (info.rows * info.columns > 1) {
120 o->writeDecAsText(info.columns);
121 if (info.rows != 1) {
122 o->writeText("x");
123 o->writeDecAsText(info.rows);
124 }
125 o->writeText(" : ");
126 o->writeText("slot ");
127 o->writeDecAsText(info.componentIndex + 1);
128 o->writeText("/");
129 o->writeDecAsText(info.rows * info.columns);
130 }
131 o->writeText(", L");
132 o->writeDecAsText(info.line);
133 o->writeText(")");
134 o->newline();
135 }
136
137 for (size_t index = 0; index < fFuncInfo.size(); ++index) {
138 const FunctionDebugInfo& info = fFuncInfo[index];
139
140 o->writeText("F");
141 o->writeDecAsText(index);
142 o->writeText(" = ");
143 o->writeText(info.name.c_str());
144 o->newline();
145 }
146
147 o->newline();
148
149 if (!fTraceInfo.empty()) {
150 std::string indent = "";
151 for (const SkSL::TraceInfo& traceInfo : fTraceInfo) {
152 int data0 = traceInfo.data[0];
153 int data1 = traceInfo.data[1];
154 switch (traceInfo.op) {
156 o->writeText(indent.c_str());
157 o->writeText("line ");
158 o->writeDecAsText(data0);
159 break;
160
162 const SlotDebugInfo& slot = fSlotInfo[data0];
163 o->writeText(indent.c_str());
164 o->writeText(slot.name.c_str());
165 o->writeText(this->getSlotComponentSuffix(data0).c_str());
166 o->writeText(" = ");
167 o->writeText(this->getSlotValue(data0, data1).c_str());
168 break;
169 }
171 o->writeText(indent.c_str());
172 o->writeText("enter ");
173 o->writeText(fFuncInfo[data0].name.c_str());
174 indent += " ";
175 break;
176
178 indent.resize(indent.size() - 2);
179 o->writeText(indent.c_str());
180 o->writeText("exit ");
181 o->writeText(fFuncInfo[data0].name.c_str());
182 break;
183
185 for (int delta = data0; delta < 0; ++delta) {
186 indent.pop_back();
187 }
188 o->writeText(indent.c_str());
189 o->writeText("scope ");
190 o->writeText((data0 >= 0) ? "+" : "");
191 o->writeDecAsText(data0);
192 for (int delta = data0; delta > 0; --delta) {
193 indent.push_back(' ');
194 }
195 break;
196 }
197 o->newline();
198 }
199 }
200}
201
203 SkJSONWriter json(w);
204
205 json.beginObject(); // root
206 json.appendNString("version", kTraceVersion);
207 json.beginArray("source");
208
209 for (const std::string& line : fSource) {
210 json.appendString(line);
211 }
212
213 json.endArray(); // code
214 json.beginArray("slots");
215
216 for (size_t index = 0; index < fSlotInfo.size(); ++index) {
217 const SlotDebugInfo& info = fSlotInfo[index];
218
219 json.beginObject();
220 json.appendString("name", info.name.data(), info.name.size());
221 json.appendS32("columns", info.columns);
222 json.appendS32("rows", info.rows);
223 json.appendS32("index", info.componentIndex);
224 if (info.groupIndex != info.componentIndex) {
225 json.appendS32("groupIdx", info.groupIndex);
226 }
227 json.appendS32("kind", (int)info.numberKind);
228 json.appendS32("line", info.line);
229 if (info.fnReturnValue >= 0) {
230 json.appendS32("retval", info.fnReturnValue);
231 }
232 json.endObject();
233 }
234
235 json.endArray(); // slots
236 json.beginArray("functions");
237
238 for (size_t index = 0; index < fFuncInfo.size(); ++index) {
239 const FunctionDebugInfo& info = fFuncInfo[index];
240
241 json.beginObject();
242 json.appendString("name", info.name);
243 json.endObject();
244 }
245
246 json.endArray(); // functions
247 json.beginArray("trace");
248
249 for (size_t index = 0; index < fTraceInfo.size(); ++index) {
250 const TraceInfo& trace = fTraceInfo[index];
251 json.beginArray();
252 json.appendS32((int)trace.op);
253
254 // Skip trailing zeros in the data (since most ops only use one value).
255 int lastDataIdx = std::size(trace.data) - 1;
256 while (lastDataIdx >= 0 && !trace.data[lastDataIdx]) {
257 --lastDataIdx;
258 }
259 for (int dataIdx = 0; dataIdx <= lastDataIdx; ++dataIdx) {
260 json.appendS32(trace.data[dataIdx]);
261 }
262 json.endArray();
263 }
264
265 json.endArray(); // trace
266 json.endObject(); // root
267 json.flush();
268}
269
272 skjson::DOM json(reinterpret_cast<const char*>(data->bytes()), data->size());
273 const skjson::ObjectValue* root = json.root();
274 if (!root) {
275 return false;
276 }
277
278 const skjson::StringValue* version = (*root)["version"];
279 if (!version || version->str() != kTraceVersion) {
280 return false;
281 }
282
283 const skjson::ArrayValue* source = (*root)["source"];
284 if (!source) {
285 return false;
286 }
287
288 fSource.clear();
289 for (const skjson::StringValue* line : *source) {
290 if (!line) {
291 return false;
292 }
293 fSource.push_back(line->begin());
294 }
295
296 const skjson::ArrayValue* slots = (*root)["slots"];
297 if (!slots) {
298 return false;
299 }
300
301 fSlotInfo.clear();
302 for (const skjson::ObjectValue* element : *slots) {
303 if (!element) {
304 return false;
305 }
306
307 // Grow the slot array to hold this element.
308 fSlotInfo.push_back({});
309 SlotDebugInfo& info = fSlotInfo.back();
310
311 // Populate the SlotInfo with our JSON data.
312 const skjson::StringValue* name = (*element)["name"];
313 const skjson::NumberValue* columns = (*element)["columns"];
314 const skjson::NumberValue* rows = (*element)["rows"];
315 const skjson::NumberValue* index = (*element)["index"];
316 const skjson::NumberValue* groupIdx = (*element)["groupIdx"];
317 const skjson::NumberValue* kind = (*element)["kind"];
318 const skjson::NumberValue* line = (*element)["line"];
319 const skjson::NumberValue* retval = (*element)["retval"];
320 if (!name || !columns || !rows || !index || !kind || !line) {
321 return false;
322 }
323
324 info.name = name->begin();
325 info.columns = **columns;
326 info.rows = **rows;
327 info.componentIndex = **index;
328 info.groupIndex = groupIdx ? **groupIdx : info.componentIndex;
329 info.numberKind = (SkSL::Type::NumberKind)(int)**kind;
330 info.line = **line;
331 info.fnReturnValue = retval ? **retval : -1;
332 }
333
334 const skjson::ArrayValue* functions = (*root)["functions"];
335 if (!functions) {
336 return false;
337 }
338
339 fFuncInfo.clear();
340 for (const skjson::ObjectValue* element : *functions) {
341 if (!element) {
342 return false;
343 }
344
345 // Grow the function array to hold this element.
346 fFuncInfo.push_back({});
348
349 // Populate the FunctionInfo with our JSON data.
350 const skjson::StringValue* name = (*element)["name"];
351 if (!name) {
352 return false;
353 }
354
355 info.name = name->begin();
356 }
357
358 const skjson::ArrayValue* trace = (*root)["trace"];
359 if (!trace) {
360 return false;
361 }
362
363 fTraceInfo.clear();
364 fTraceInfo.reserve(trace->size());
365 for (const skjson::ArrayValue* element : *trace) {
366 fTraceInfo.push_back(TraceInfo{});
367 TraceInfo& info = fTraceInfo.back();
368
369 if (!element || element->size() < 1 || element->size() > (1 + std::size(info.data))) {
370 return false;
371 }
372 const skjson::NumberValue* opVal = (*element)[0];
373 if (!opVal) {
374 return false;
375 }
376 info.op = (TraceInfo::Op)(int)**opVal;
377 for (size_t elemIdx = 1; elemIdx < element->size(); ++elemIdx) {
378 const skjson::NumberValue* dataVal = (*element)[elemIdx];
379 if (!dataVal) {
380 return false;
381 }
382 info.data[elemIdx - 1] = **dataVal;
383 }
384 }
385
386 return true;
387}
388
389} // namespace SkSL
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
#define SkASSERT(cond)
Definition: SkAssert.h:116
static constexpr char kTraceVersion[]
sk_sp< SkData > SkCopyStreamToData(SkStream *stream)
Definition: SkStream.cpp:937
void appendS32(int32_t value)
Definition: SkJSONWriter.h:237
void appendNString(char const (&value)[N])
Definition: SkJSONWriter.h:220
void beginArray(const char *name=nullptr, bool multiline=true)
Definition: SkJSONWriter.h:146
void beginObject(const char *name=nullptr, bool multiline=true)
Definition: SkJSONWriter.h:114
void endObject()
Definition: SkJSONWriter.h:126
void endArray()
Definition: SkJSONWriter.h:158
void flush()
Definition: SkJSONWriter.h:78
void appendString(const char *value, size_t size)
Definition: SkJSONWriter.h:176
void writeTrace(SkWStream *w) const override
std::string getSlotValue(int slotIndex, int32_t value) const
bool readTrace(SkStream *r)
std::vector< FunctionDebugInfo > fFuncInfo
void setSource(const std::string &source)
std::vector< TraceInfo > fTraceInfo
void setTraceCoord(const SkIPoint &coord)
std::vector< std::string > fSource
std::vector< SlotDebugInfo > fSlotInfo
double interpretValueBits(int slotIndex, int32_t valueBits) const
void dump(SkWStream *o) const override
std::string slotValueToString(int slotIndex, double value) const
std::string getSlotComponentSuffix(int slotIndex) const
bool writeDecAsText(int32_t)
Definition: SkStream.cpp:81
bool writeText(const char text[])
Definition: SkStream.h:247
bool newline()
Definition: SkStream.h:252
const Value & root() const
Definition: SkJSON.h:362
size_t size() const
Definition: SkJSON.h:262
SkBitmap source
Definition: examples.cpp:28
uint8_t value
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
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
string root
Definition: scale_cpu.py:20
static SkString to_string(int n)
Definition: nanobench.cpp:119
SkScalar w
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63