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