xref: /aosp_15_r20/external/skia/src/sksl/tracing/SkSLDebugTracePriv.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/SkSLDebugTracePriv.h"
9 
10 #include "include/core/SkData.h"
11 #include "include/core/SkRefCnt.h"
12 #include "include/core/SkStream.h"
13 #include "include/core/SkTypes.h"
14 #include "src/core/SkStreamPriv.h"
15 #include "src/sksl/ir/SkSLType.h"
16 #include "src/utils/SkJSON.h"
17 #include "src/utils/SkJSONWriter.h"
18 
19 #include <cstdio>
20 #include <cstring>
21 #include <sstream>
22 #include <string>
23 #include <string_view>
24 
25 static constexpr char kTraceVersion[] = "20220209";
26 
27 namespace SkSL {
28 
getSlotComponentSuffix(int slotIndex) const29 std::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 
interpretValueBits(int slotIndex,int32_t valueBits) const49 double DebugTracePriv::interpretValueBits(int slotIndex, int32_t valueBits) const {
50     SkASSERT(slotIndex >= 0);
51     SkASSERT((size_t)slotIndex < fSlotInfo.size());
52     switch (fSlotInfo[slotIndex].numberKind) {
53         case SkSL::Type::NumberKind::kUnsigned: {
54             uint32_t uintValue;
55             static_assert(sizeof(uintValue) == sizeof(valueBits));
56             memcpy(&uintValue, &valueBits, sizeof(uintValue));
57             return uintValue;
58         }
59         case SkSL::Type::NumberKind::kFloat: {
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 
slotValueToString(int slotIndex,double value) const71 std::string DebugTracePriv::slotValueToString(int slotIndex, double value) const {
72     SkASSERT(slotIndex >= 0);
73     SkASSERT((size_t)slotIndex < fSlotInfo.size());
74     switch (fSlotInfo[slotIndex].numberKind) {
75         case SkSL::Type::NumberKind::kBoolean: {
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 
getSlotValue(int slotIndex,int32_t valueBits) const86 std::string DebugTracePriv::getSlotValue(int slotIndex, int32_t valueBits) const {
87     return this->slotValueToString(slotIndex, this->interpretValueBits(slotIndex, valueBits));
88 }
89 
setTraceCoord(const SkIPoint & coord)90 void DebugTracePriv::setTraceCoord(const SkIPoint& coord) {
91     fTraceCoord = coord;
92 }
93 
setSource(const std::string & source)94 void 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 
dump(SkWStream * o) const103 void DebugTracePriv::dump(SkWStream* o) const {
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) {
155                 case SkSL::TraceInfo::Op::kLine:
156                     o->writeText(indent.c_str());
157                     o->writeText("line ");
158                     o->writeDecAsText(data0);
159                     break;
160 
161                 case SkSL::TraceInfo::Op::kVar: {
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                 }
170                 case SkSL::TraceInfo::Op::kEnter:
171                     o->writeText(indent.c_str());
172                     o->writeText("enter ");
173                     o->writeText(fFuncInfo[data0].name.c_str());
174                     indent += "  ";
175                     break;
176 
177                 case SkSL::TraceInfo::Op::kExit:
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 
184                 case SkSL::TraceInfo::Op::kScope:
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 
writeTrace(SkWStream * w) const202 void DebugTracePriv::writeTrace(SkWStream* w) const {
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 
readTrace(SkStream * r)270 bool DebugTracePriv::readTrace(SkStream* r) {
271     sk_sp<SkData> data = SkCopyStreamToData(r);
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({});
347         FunctionDebugInfo& info = fFuncInfo.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
390