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