• 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/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