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
8 #include "src/sksl/tracing/SkVMDebugTrace.h"
9
10 #ifdef SKSL_ENABLE_TRACING
11
12 #include "include/core/SkData.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkStream.h"
15 #include "include/core/SkTypes.h"
16 #include "src/core/SkStreamPriv.h"
17 #include "src/sksl/ir/SkSLType.h"
18 #include "src/utils/SkJSON.h"
19 #include "src/utils/SkJSONWriter.h"
20
21 #include <cstdio>
22 #include <cstring>
23 #include <sstream>
24 #include <string>
25 #include <string_view>
26 #include <utility>
27
28 static constexpr char kTraceVersion[] = "20220209";
29
30 namespace SkSL {
31
getSlotComponentSuffix(int slotIndex) const32 std::string SkVMDebugTrace::getSlotComponentSuffix(int slotIndex) const {
33 const SkSL::SlotDebugInfo& slot = fSlotInfo[slotIndex];
34
35 if (slot.rows > 1) {
36 return "[" + std::to_string(slot.componentIndex / slot.rows) +
37 "][" + std::to_string(slot.componentIndex % slot.rows) +
38 "]";
39 }
40 if (slot.columns > 1) {
41 switch (slot.componentIndex) {
42 case 0: return ".x";
43 case 1: return ".y";
44 case 2: return ".z";
45 case 3: return ".w";
46 default: return "[???]";
47 }
48 }
49 return {};
50 }
51
interpretValueBits(int slotIndex,int32_t valueBits) const52 double SkVMDebugTrace::interpretValueBits(int slotIndex, int32_t valueBits) const {
53 SkASSERT(slotIndex >= 0);
54 SkASSERT((size_t)slotIndex < fSlotInfo.size());
55 switch (fSlotInfo[slotIndex].numberKind) {
56 case SkSL::Type::NumberKind::kUnsigned: {
57 uint32_t uintValue;
58 static_assert(sizeof(uintValue) == sizeof(valueBits));
59 memcpy(&uintValue, &valueBits, sizeof(uintValue));
60 return uintValue;
61 }
62 case SkSL::Type::NumberKind::kFloat: {
63 float floatValue;
64 static_assert(sizeof(floatValue) == sizeof(valueBits));
65 memcpy(&floatValue, &valueBits, sizeof(floatValue));
66 return floatValue;
67 }
68 default: {
69 return valueBits;
70 }
71 }
72 }
73
slotValueToString(int slotIndex,double value) const74 std::string SkVMDebugTrace::slotValueToString(int slotIndex, double value) const {
75 SkASSERT(slotIndex >= 0);
76 SkASSERT((size_t)slotIndex < fSlotInfo.size());
77 switch (fSlotInfo[slotIndex].numberKind) {
78 case SkSL::Type::NumberKind::kBoolean: {
79 return value ? "true" : "false";
80 }
81 default: {
82 char buffer[32];
83 snprintf(buffer, std::size(buffer), "%.8g", value);
84 return buffer;
85 }
86 }
87 }
88
getSlotValue(int slotIndex,int32_t valueBits) const89 std::string SkVMDebugTrace::getSlotValue(int slotIndex, int32_t valueBits) const {
90 return this->slotValueToString(slotIndex, this->interpretValueBits(slotIndex, valueBits));
91 }
92
setTraceCoord(const SkIPoint & coord)93 void SkVMDebugTrace::setTraceCoord(const SkIPoint& coord) {
94 fTraceCoord = coord;
95 }
96
setSource(std::string source)97 void SkVMDebugTrace::setSource(std::string source) {
98 fSource.clear();
99 std::stringstream stream{std::move(source)};
100 while (stream.good()) {
101 fSource.push_back({});
102 std::getline(stream, fSource.back(), '\n');
103 }
104 }
105
dump(SkWStream * o) const106 void SkVMDebugTrace::dump(SkWStream* o) const {
107 for (size_t index = 0; index < fSlotInfo.size(); ++index) {
108 const SlotDebugInfo& info = fSlotInfo[index];
109
110 o->writeText("$");
111 o->writeDecAsText(index);
112 o->writeText(" = ");
113 o->writeText(info.name.c_str());
114 o->writeText(" (");
115 switch (info.numberKind) {
116 case Type::NumberKind::kFloat: o->writeText("float"); break;
117 case Type::NumberKind::kSigned: o->writeText("int"); break;
118 case Type::NumberKind::kUnsigned: o->writeText("uint"); break;
119 case Type::NumberKind::kBoolean: o->writeText("bool"); break;
120 case Type::NumberKind::kNonnumeric: o->writeText("???"); break;
121 }
122 if (info.rows * info.columns > 1) {
123 o->writeDecAsText(info.columns);
124 if (info.rows != 1) {
125 o->writeText("x");
126 o->writeDecAsText(info.rows);
127 }
128 o->writeText(" : ");
129 o->writeText("slot ");
130 o->writeDecAsText(info.componentIndex + 1);
131 o->writeText("/");
132 o->writeDecAsText(info.rows * info.columns);
133 }
134 o->writeText(", L");
135 o->writeDecAsText(info.line);
136 o->writeText(")");
137 o->newline();
138 }
139
140 for (size_t index = 0; index < fFuncInfo.size(); ++index) {
141 const FunctionDebugInfo& info = fFuncInfo[index];
142
143 o->writeText("F");
144 o->writeDecAsText(index);
145 o->writeText(" = ");
146 o->writeText(info.name.c_str());
147 o->newline();
148 }
149
150 o->newline();
151
152 if (!fTraceInfo.empty()) {
153 std::string indent = "";
154 for (const SkSL::SkVMTraceInfo& traceInfo : fTraceInfo) {
155 int data0 = traceInfo.data[0];
156 int data1 = traceInfo.data[1];
157 switch (traceInfo.op) {
158 case SkSL::SkVMTraceInfo::Op::kLine:
159 o->writeText(indent.c_str());
160 o->writeText("line ");
161 o->writeDecAsText(data0);
162 break;
163
164 case SkSL::SkVMTraceInfo::Op::kVar: {
165 const SlotDebugInfo& slot = fSlotInfo[data0];
166 o->writeText(indent.c_str());
167 o->writeText(slot.name.c_str());
168 o->writeText(this->getSlotComponentSuffix(data0).c_str());
169 o->writeText(" = ");
170 o->writeText(this->getSlotValue(data0, data1).c_str());
171 break;
172 }
173 case SkSL::SkVMTraceInfo::Op::kEnter:
174 o->writeText(indent.c_str());
175 o->writeText("enter ");
176 o->writeText(fFuncInfo[data0].name.c_str());
177 indent += " ";
178 break;
179
180 case SkSL::SkVMTraceInfo::Op::kExit:
181 indent.resize(indent.size() - 2);
182 o->writeText(indent.c_str());
183 o->writeText("exit ");
184 o->writeText(fFuncInfo[data0].name.c_str());
185 break;
186
187 case SkSL::SkVMTraceInfo::Op::kScope:
188 for (int delta = data0; delta < 0; ++delta) {
189 indent.pop_back();
190 }
191 o->writeText(indent.c_str());
192 o->writeText("scope ");
193 o->writeText((data0 >= 0) ? "+" : "");
194 o->writeDecAsText(data0);
195 for (int delta = data0; delta > 0; --delta) {
196 indent.push_back(' ');
197 }
198 break;
199 }
200 o->newline();
201 }
202 }
203 }
204
writeTrace(SkWStream * w) const205 void SkVMDebugTrace::writeTrace(SkWStream* w) const {
206 SkJSONWriter json(w);
207
208 json.beginObject(); // root
209 json.appendNString("version", kTraceVersion);
210 json.beginArray("source");
211
212 for (const std::string& line : fSource) {
213 json.appendString(line);
214 }
215
216 json.endArray(); // code
217 json.beginArray("slots");
218
219 for (size_t index = 0; index < fSlotInfo.size(); ++index) {
220 const SlotDebugInfo& info = fSlotInfo[index];
221
222 json.beginObject();
223 json.appendString("name", info.name.data(), info.name.size());
224 json.appendS32("columns", info.columns);
225 json.appendS32("rows", info.rows);
226 json.appendS32("index", info.componentIndex);
227 if (info.groupIndex != info.componentIndex) {
228 json.appendS32("groupIdx", info.groupIndex);
229 }
230 json.appendS32("kind", (int)info.numberKind);
231 json.appendS32("line", info.line);
232 if (info.fnReturnValue >= 0) {
233 json.appendS32("retval", info.fnReturnValue);
234 }
235 json.endObject();
236 }
237
238 json.endArray(); // slots
239 json.beginArray("functions");
240
241 for (size_t index = 0; index < fFuncInfo.size(); ++index) {
242 const FunctionDebugInfo& info = fFuncInfo[index];
243
244 json.beginObject();
245 json.appendString("name", info.name);
246 json.endObject();
247 }
248
249 json.endArray(); // functions
250 json.beginArray("trace");
251
252 for (size_t index = 0; index < fTraceInfo.size(); ++index) {
253 const SkVMTraceInfo& trace = fTraceInfo[index];
254 json.beginArray();
255 json.appendS32((int)trace.op);
256
257 // Skip trailing zeros in the data (since most ops only use one value).
258 int lastDataIdx = std::size(trace.data) - 1;
259 while (lastDataIdx >= 0 && !trace.data[lastDataIdx]) {
260 --lastDataIdx;
261 }
262 for (int dataIdx = 0; dataIdx <= lastDataIdx; ++dataIdx) {
263 json.appendS32(trace.data[dataIdx]);
264 }
265 json.endArray();
266 }
267
268 json.endArray(); // trace
269 json.endObject(); // root
270 json.flush();
271 }
272
readTrace(SkStream * r)273 bool SkVMDebugTrace::readTrace(SkStream* r) {
274 sk_sp<SkData> data = SkCopyStreamToData(r);
275 skjson::DOM json(reinterpret_cast<const char*>(data->bytes()), data->size());
276 const skjson::ObjectValue* root = json.root();
277 if (!root) {
278 return false;
279 }
280
281 const skjson::StringValue* version = (*root)["version"];
282 if (!version || version->str() != kTraceVersion) {
283 return false;
284 }
285
286 const skjson::ArrayValue* source = (*root)["source"];
287 if (!source) {
288 return false;
289 }
290
291 fSource.clear();
292 for (const skjson::StringValue* line : *source) {
293 if (!line) {
294 return false;
295 }
296 fSource.push_back(line->begin());
297 }
298
299 const skjson::ArrayValue* slots = (*root)["slots"];
300 if (!slots) {
301 return false;
302 }
303
304 fSlotInfo.clear();
305 for (const skjson::ObjectValue* element : *slots) {
306 if (!element) {
307 return false;
308 }
309
310 // Grow the slot array to hold this element.
311 fSlotInfo.push_back({});
312 SlotDebugInfo& info = fSlotInfo.back();
313
314 // Populate the SlotInfo with our JSON data.
315 const skjson::StringValue* name = (*element)["name"];
316 const skjson::NumberValue* columns = (*element)["columns"];
317 const skjson::NumberValue* rows = (*element)["rows"];
318 const skjson::NumberValue* index = (*element)["index"];
319 const skjson::NumberValue* groupIdx = (*element)["groupIdx"];
320 const skjson::NumberValue* kind = (*element)["kind"];
321 const skjson::NumberValue* line = (*element)["line"];
322 const skjson::NumberValue* retval = (*element)["retval"];
323 if (!name || !columns || !rows || !index || !kind || !line) {
324 return false;
325 }
326
327 info.name = name->begin();
328 info.columns = **columns;
329 info.rows = **rows;
330 info.componentIndex = **index;
331 info.groupIndex = groupIdx ? **groupIdx : info.componentIndex;
332 info.numberKind = (SkSL::Type::NumberKind)(int)**kind;
333 info.line = **line;
334 info.fnReturnValue = retval ? **retval : -1;
335 }
336
337 const skjson::ArrayValue* functions = (*root)["functions"];
338 if (!functions) {
339 return false;
340 }
341
342 fFuncInfo.clear();
343 for (const skjson::ObjectValue* element : *functions) {
344 if (!element) {
345 return false;
346 }
347
348 // Grow the function array to hold this element.
349 fFuncInfo.push_back({});
350 FunctionDebugInfo& info = fFuncInfo.back();
351
352 // Populate the FunctionInfo with our JSON data.
353 const skjson::StringValue* name = (*element)["name"];
354 if (!name) {
355 return false;
356 }
357
358 info.name = name->begin();
359 }
360
361 const skjson::ArrayValue* trace = (*root)["trace"];
362 if (!trace) {
363 return false;
364 }
365
366 fTraceInfo.clear();
367 fTraceInfo.reserve(trace->size());
368 for (const skjson::ArrayValue* element : *trace) {
369 fTraceInfo.push_back(SkVMTraceInfo{});
370 SkVMTraceInfo& info = fTraceInfo.back();
371
372 if (!element || element->size() < 1 || element->size() > (1 + std::size(info.data))) {
373 return false;
374 }
375 const skjson::NumberValue* opVal = (*element)[0];
376 if (!opVal) {
377 return false;
378 }
379 info.op = (SkVMTraceInfo::Op)(int)**opVal;
380 for (size_t elemIdx = 1; elemIdx < element->size(); ++elemIdx) {
381 const skjson::NumberValue* dataVal = (*element)[elemIdx];
382 if (!dataVal) {
383 return false;
384 }
385 info.data[elemIdx - 1] = **dataVal;
386 }
387 }
388
389 return true;
390 }
391
392 } // namespace SkSL
393
394 #else // SKSL_ENABLE_TRACING
395
396 #include <string>
397
398 namespace SkSL {
setTraceCoord(const SkIPoint & coord)399 void SkVMDebugTrace::setTraceCoord(const SkIPoint &coord) {}
400
setSource(std::string source)401 void SkVMDebugTrace::setSource(std::string source) {}
402
readTrace(SkStream * r)403 bool SkVMDebugTrace::readTrace(SkStream *r) { return false; }
404
writeTrace(SkWStream * w) const405 void SkVMDebugTrace::writeTrace(SkWStream *w) const {}
406
dump(SkWStream * o) const407 void SkVMDebugTrace::dump(SkWStream *o) const {}
408
getSlotComponentSuffix(int slotIndex) const409 std::string SkVMDebugTrace::getSlotComponentSuffix(int slotIndex) const { return ""; }
410
getSlotValue(int slotIndex,int32_t value) const411 std::string SkVMDebugTrace::getSlotValue(int slotIndex, int32_t value) const { return ""; }
412
interpretValueBits(int slotIndex,int32_t valueBits) const413 double SkVMDebugTrace::interpretValueBits(int slotIndex, int32_t valueBits) const { return 0; }
414
slotValueToString(int slotIndex,double value) const415 std::string SkVMDebugTrace::slotValueToString(int slotIndex, double value) const { return ""; }
416 }
417 #endif
418