1 /*
2 * Copyright 2024 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 #include "tools/sksltrace/SkSLTraceUtils.h"
8
9 #include "include/core/SkData.h"
10 #include "include/core/SkRefCnt.h"
11 #include "include/core/SkTypes.h"
12 #include "modules/jsonreader/SkJSONReader.h"
13 #include "src/core/SkStreamPriv.h"
14 #include "src/sksl/ir/SkSLType.h"
15 #include "src/sksl/tracing/SkSLDebugTracePriv.h"
16 #include "src/utils/SkJSONWriter.h"
17
18 #include <cstdint>
19 #include <cstdio>
20 #include <string>
21 #include <string_view>
22 #include <vector>
23
24 static constexpr char kTraceVersion[] = "20220209";
25
26 namespace SkSLTraceUtils {
27
WriteTrace(const SkSL::DebugTracePriv & src,SkWStream * w)28 void WriteTrace(const SkSL::DebugTracePriv& src, SkWStream* w) {
29 SkJSONWriter json(w);
30
31 json.beginObject(); // root
32 json.appendNString("version", kTraceVersion);
33 json.beginArray("source");
34
35 for (const std::string& line : src.fSource) {
36 json.appendString(line);
37 }
38
39 json.endArray(); // code
40 json.beginArray("slots");
41
42 for (size_t index = 0; index < src.fSlotInfo.size(); ++index) {
43 const SkSL::SlotDebugInfo& info = src.fSlotInfo[index];
44
45 json.beginObject();
46 json.appendString("name", info.name.data(), info.name.size());
47 json.appendS32("columns", info.columns);
48 json.appendS32("rows", info.rows);
49 json.appendS32("index", info.componentIndex);
50 if (info.groupIndex != info.componentIndex) {
51 json.appendS32("groupIdx", info.groupIndex);
52 }
53 json.appendS32("kind", (int)info.numberKind);
54 json.appendS32("line", info.line);
55 if (info.fnReturnValue >= 0) {
56 json.appendS32("retval", info.fnReturnValue);
57 }
58 json.endObject();
59 }
60
61 json.endArray(); // slots
62 json.beginArray("functions");
63
64 for (size_t index = 0; index < src.fFuncInfo.size(); ++index) {
65 const SkSL::FunctionDebugInfo& info = src.fFuncInfo[index];
66
67 json.beginObject();
68 json.appendString("name", info.name);
69 json.endObject();
70 }
71
72 json.endArray(); // functions
73 json.beginArray("trace");
74
75 for (size_t index = 0; index < src.fTraceInfo.size(); ++index) {
76 const SkSL::TraceInfo& trace = src.fTraceInfo[index];
77 json.beginArray();
78 json.appendS32((int)trace.op);
79
80 // Skip trailing zeros in the data (since most ops only use one value).
81 int lastDataIdx = std::size(trace.data) - 1;
82 while (lastDataIdx >= 0 && !trace.data[lastDataIdx]) {
83 --lastDataIdx;
84 }
85 for (int dataIdx = 0; dataIdx <= lastDataIdx; ++dataIdx) {
86 json.appendS32(trace.data[dataIdx]);
87 }
88 json.endArray();
89 }
90
91 json.endArray(); // trace
92 json.endObject(); // root
93 json.flush();
94 }
95
ReadTrace(SkStream * r)96 sk_sp<SkSL::DebugTracePriv> ReadTrace(SkStream* r) {
97 sk_sp<SkData> data = SkCopyStreamToData(r);
98 skjson::DOM json(reinterpret_cast<const char*>(data->bytes()), data->size());
99 const skjson::ObjectValue* root = json.root();
100 if (!root) {
101 return nullptr;
102 }
103
104 const skjson::StringValue* version = (*root)["version"];
105 if (!version || version->str() != kTraceVersion) {
106 return nullptr;
107 }
108
109 const skjson::ArrayValue* source = (*root)["source"];
110 if (!source) {
111 return nullptr;
112 }
113
114 auto dst = sk_make_sp<SkSL::DebugTracePriv>();
115 for (const skjson::StringValue* line : *source) {
116 if (!line) {
117 return nullptr;
118 }
119 dst->fSource.push_back(line->begin());
120 }
121
122 const skjson::ArrayValue* slots = (*root)["slots"];
123 if (!slots) {
124 return nullptr;
125 }
126 for (const skjson::ObjectValue* element : *slots) {
127 if (!element) {
128 return nullptr;
129 }
130
131 // Grow the slot array to hold this element.
132 dst->fSlotInfo.push_back({});
133 SkSL::SlotDebugInfo& info = dst->fSlotInfo.back();
134
135 // Populate the SlotInfo with our JSON data.
136 const skjson::StringValue* name = (*element)["name"];
137 const skjson::NumberValue* columns = (*element)["columns"];
138 const skjson::NumberValue* rows = (*element)["rows"];
139 const skjson::NumberValue* index = (*element)["index"];
140 const skjson::NumberValue* groupIdx = (*element)["groupIdx"];
141 const skjson::NumberValue* kind = (*element)["kind"];
142 const skjson::NumberValue* line = (*element)["line"];
143 const skjson::NumberValue* retval = (*element)["retval"];
144 if (!name || !columns || !rows || !index || !kind || !line) {
145 return nullptr;
146 }
147
148 info.name = name->begin();
149 info.columns = **columns;
150 info.rows = **rows;
151 info.componentIndex = **index;
152 info.groupIndex = groupIdx ? **groupIdx : info.componentIndex;
153 info.numberKind = (SkSL::Type::NumberKind)(int)**kind;
154 info.line = **line;
155 info.fnReturnValue = retval ? **retval : -1;
156 }
157
158 const skjson::ArrayValue* functions = (*root)["functions"];
159 if (!functions) {
160 return nullptr;
161 }
162
163 for (const skjson::ObjectValue* element : *functions) {
164 if (!element) {
165 return nullptr;
166 }
167
168 // Grow the function array to hold this element.
169 dst->fFuncInfo.push_back({});
170 SkSL::FunctionDebugInfo& info = dst->fFuncInfo.back();
171
172 // Populate the FunctionInfo with our JSON data.
173 const skjson::StringValue* name = (*element)["name"];
174 if (!name) {
175 return nullptr;
176 }
177
178 info.name = name->begin();
179 }
180
181 const skjson::ArrayValue* trace = (*root)["trace"];
182 if (!trace) {
183 return nullptr;
184 }
185
186 dst->fTraceInfo.reserve(trace->size());
187 for (const skjson::ArrayValue* element : *trace) {
188 dst->fTraceInfo.push_back(SkSL::TraceInfo{});
189 SkSL::TraceInfo& info = dst->fTraceInfo.back();
190
191 if (!element || element->size() < 1 || element->size() > (1 + std::size(info.data))) {
192 return nullptr;
193 }
194 const skjson::NumberValue* opVal = (*element)[0];
195 if (!opVal) {
196 return nullptr;
197 }
198 info.op = (SkSL::TraceInfo::Op)(int)**opVal;
199 for (size_t elemIdx = 1; elemIdx < element->size(); ++elemIdx) {
200 const skjson::NumberValue* dataVal = (*element)[elemIdx];
201 if (!dataVal) {
202 return nullptr;
203 }
204 info.data[elemIdx - 1] = **dataVal;
205 }
206 }
207
208 return dst;
209 }
210
211 } // namespace SkSLTraceUtils
212