• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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