1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/dfx/hprof/heap_snapshot_json_serializer.h"
17
18 #include "ecmascript/dfx/hprof/heap_snapshot.h"
19
20 namespace panda::ecmascript {
21
Serialize(HeapSnapshot * snapshot,Stream * stream)22 bool HeapSnapshotJSONSerializer::Serialize(HeapSnapshot *snapshot, Stream *stream)
23 {
24 // Serialize Node/Edge/String-Table
25 LOG_ECMA(INFO) << "HeapSnapshotJSONSerializer::Serialize begin";
26 ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "HeapSnapshotJSONSerializer::Serialize", "");
27 ASSERT(snapshot->GetNodes() != nullptr && snapshot->GetEdges() != nullptr &&
28 snapshot->GetEcmaStringTable() != nullptr);
29 auto writer = new StreamWriter(stream);
30
31 SerializeSnapshotHeader(snapshot, writer); // 1.
32 SerializeNodes(snapshot, writer); // 2.
33 SerializeEdges(snapshot, writer); // 3.
34 SerializeTraceFunctionInfo(snapshot, writer); // 4.
35 SerializeTraceTree(snapshot, writer); // 5.
36 SerializeSamples(snapshot, writer); // 6.
37 SerializeLocations(writer); // 7.
38 SerializeStringTable(snapshot, writer); // 8.
39 SerializerSnapshotClosure(writer); // 9.
40 writer->End();
41
42 delete writer;
43
44 LOG_ECMA(INFO) << "HeapSnapshotJSONSerializer::Serialize exit";
45 return true;
46 }
47
SerializeSnapshotHeader(HeapSnapshot * snapshot,StreamWriter * writer)48 void HeapSnapshotJSONSerializer::SerializeSnapshotHeader(HeapSnapshot *snapshot, StreamWriter *writer)
49 {
50 writer->WriteString("{\"snapshot\":\n"); // 1.
51 writer->WriteString("{\"meta\":\n"); // 2.
52 // NOLINTNEXTLINE(modernize-raw-string-literal)
53 writer->WriteString("{\"node_fields\":[\"type\",\"name\",\"id\",\"self_size\",\"edge_count\",\"trace_node_id\",");
54 writer->WriteString("\"detachedness\",\"native_size\"],\n"); // 3.
55 // NOLINTNEXTLINE(modernize-raw-string-literal)
56 writer->WriteString("\"node_types\":[[\"hidden\",\"array\",\"string\",\"object\",\"code\",\"closure\",\"regexp\",");
57 // NOLINTNEXTLINE(modernize-raw-string-literal)
58 writer->WriteString("\"number\",\"native\",\"synthetic\",\"concatenated string\",\"slicedstring\",\"symbol\",");
59 // NOLINTNEXTLINE(modernize-raw-string-literal)
60 writer->WriteString("\"bigint\",\"framework\"],\"string\",\"number\",\"number\",\"number\",\"number\",");
61 // NOLINTNEXTLINE(modernize-raw-string-literal)
62 writer->WriteString("\"number\"],\n"); // 4.
63 // NOLINTNEXTLINE(modernize-raw-string-literal)
64 writer->WriteString("\"edge_fields\":[\"type\",\"name_or_index\",\"to_node\"],\n"); // 5.
65 // NOLINTNEXTLINE(modernize-raw-string-literal)
66 writer->WriteString("\"edge_types\":[[\"context\",\"element\",\"property\",\"internal\",\"hidden\",\"shortcut\",");
67 // NOLINTNEXTLINE(modernize-raw-string-literal)
68 writer->WriteString("\"weak\"],\"string_or_number\",\"node\"],\n"); // 6.
69 // NOLINTNEXTLINE(modernize-raw-string-literal)
70 writer->WriteString("\"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\",");
71 // NOLINTNEXTLINE(modernize-raw-string-literal)
72 writer->WriteString("\"line\",\"column\"],\n"); // 7.
73 // NOLINTNEXTLINE(modernize-raw-string-literal)
74 writer->WriteString("\"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],\n");
75 // NOLINTNEXTLINE(modernize-raw-string-literal)
76 writer->WriteString("\"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],\n"); // 9.
77 // NOLINTNEXTLINE(modernize-raw-string-literal)
78 // 10.
79 writer->WriteString("\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]},\n\"node_count\":");
80 writer->WriteNumber(snapshot->GetNodeCount()); // 11.
81 writer->WriteString(",\n\"edge_count\":");
82 writer->WriteNumber(snapshot->GetEdgeCount()); // 12.
83 writer->WriteString(",\n\"trace_function_count\":");
84 writer->WriteNumber(snapshot->GetTrackAllocationsStack().size()); // 13.
85 writer->WriteString("\n},\n"); // 14.
86 }
87
SerializeNodes(HeapSnapshot * snapshot,StreamWriter * writer)88 void HeapSnapshotJSONSerializer::SerializeNodes(HeapSnapshot *snapshot, StreamWriter *writer)
89 {
90 LOG_ECMA(INFO) << "HeapSnapshotJSONSerializer::SerializeNodes";
91 ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "HeapSnapshotJSONSerializer::SerializeNodes", "");
92 const CList<Node *> *nodes = snapshot->GetNodes();
93 const StringHashMap *stringTable = snapshot->GetEcmaStringTable();
94 ASSERT(nodes != nullptr);
95 writer->WriteString("\"nodes\":["); // Section Header
96 size_t i = 0;
97 for (auto *node : *nodes) {
98 if (i > 0) {
99 writer->WriteChar(','); // add comma except first line
100 }
101 writer->WriteNumber(static_cast<int>(node->GetType())); // 1.
102 writer->WriteChar(',');
103 writer->WriteNumber(stringTable->GetStringId(node->GetName())); // 2.
104 writer->WriteChar(',');
105 writer->WriteNumber(node->GetId()); // 3.
106 writer->WriteChar(',');
107 writer->WriteNumber(node->GetSelfSize()); // 4.
108 writer->WriteChar(',');
109 writer->WriteNumber(node->GetEdgeCount()); // 5.
110 writer->WriteChar(',');
111 writer->WriteNumber(node->GetStackTraceId()); // 6.
112 writer->WriteChar(',');
113 writer->WriteChar('0'); // 7.detachedness default 0
114 writer->WriteChar(',');
115 writer->WriteNumber(node->GetNativeSize());
116 if (i == nodes->size() - 1) { // add comma at last the line
117 writer->WriteString("],\n"); // 7. detachedness default
118 } else {
119 writer->WriteString("\n"); // 7.
120 }
121 i++;
122 }
123 }
124
SerializeEdges(HeapSnapshot * snapshot,StreamWriter * writer)125 void HeapSnapshotJSONSerializer::SerializeEdges(HeapSnapshot *snapshot, StreamWriter *writer)
126 {
127 LOG_ECMA(INFO) << "HeapSnapshotJSONSerializer::SerializeEdges";
128 ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "HeapSnapshotJSONSerializer::SerializeEdges", "");
129 const CList<Edge *> *edges = snapshot->GetEdges();
130 const StringHashMap *stringTable = snapshot->GetEcmaStringTable();
131 ASSERT(edges != nullptr);
132 writer->WriteString("\"edges\":[");
133 size_t i = 0;
134 for (auto *edge : *edges) {
135 StringId nameOrIndex = edge->GetType() == EdgeType::ELEMENT ?
136 edge->GetIndex() : stringTable->GetStringId(edge->GetName());
137 if (i > 0) { // add comma except the first line
138 writer->WriteChar(',');
139 }
140 writer->WriteNumber(static_cast<int>(edge->GetType())); // 1.
141 writer->WriteChar(',');
142 writer->WriteNumber(nameOrIndex); // 2. Use StringId
143 writer->WriteChar(',');
144
145 if (i == edges->size() - 1) { // add comma at last the line
146 writer->WriteNumber(edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT); // 3.
147 writer->WriteString("],\n");
148 } else {
149 writer->WriteNumber(edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT); // 3.
150 writer->WriteChar('\n');
151 }
152 i++;
153 }
154 }
155
SerializeTraceFunctionInfo(HeapSnapshot * snapshot,StreamWriter * writer)156 void HeapSnapshotJSONSerializer::SerializeTraceFunctionInfo(HeapSnapshot *snapshot, StreamWriter *writer)
157 {
158 const CVector<FunctionInfo> trackAllocationsStack = snapshot->GetTrackAllocationsStack();
159 const StringHashMap *stringTable = snapshot->GetEcmaStringTable();
160
161 writer->WriteString("\"trace_function_infos\":["); // Empty
162 size_t i = 0;
163
164 for (const auto &info : trackAllocationsStack) {
165 if (i > 0) { // add comma except the first line
166 writer->WriteChar(',');
167 }
168 writer->WriteNumber(info.functionId);
169 writer->WriteChar(',');
170 CString functionName(info.functionName.c_str());
171 writer->WriteNumber(stringTable->GetStringId(&functionName));
172 writer->WriteChar(',');
173 CString scriptName(info.scriptName.c_str());
174 writer->WriteNumber(stringTable->GetStringId(&scriptName));
175 writer->WriteChar(',');
176 writer->WriteNumber(info.scriptId);
177 writer->WriteChar(',');
178 writer->WriteNumber(info.lineNumber);
179 writer->WriteChar(',');
180 writer->WriteNumber(info.columnNumber);
181 writer->WriteChar('\n');
182 i++;
183 }
184 writer->WriteString("],\n");
185 }
186
SerializeTraceTree(HeapSnapshot * snapshot,StreamWriter * writer)187 void HeapSnapshotJSONSerializer::SerializeTraceTree(HeapSnapshot *snapshot, StreamWriter *writer)
188 {
189 writer->WriteString("\"trace_tree\":[");
190 TraceTree* tree = snapshot->GetTraceTree();
191 if ((tree != nullptr) && (snapshot->trackAllocations())) {
192 SerializeTraceNode(tree->GetRoot(), writer);
193 }
194 writer->WriteString("],\n");
195 }
196
SerializeTraceNode(TraceNode * node,StreamWriter * writer)197 void HeapSnapshotJSONSerializer::SerializeTraceNode(TraceNode* node, StreamWriter *writer)
198 {
199 if (node == nullptr) {
200 return;
201 }
202
203 writer->WriteNumber(node->GetId());
204 writer->WriteChar(',');
205 writer->WriteNumber(node->GetNodeIndex());
206 writer->WriteChar(',');
207 writer->WriteNumber(node->GetTotalCount());
208 writer->WriteChar(',');
209 writer->WriteNumber(node->GetTotalSize());
210 writer->WriteString(",[");
211
212 int i = 0;
213 for (TraceNode* child : node->GetChildren()) {
214 if (i > 0) {
215 writer->WriteChar(',');
216 }
217 SerializeTraceNode(child, writer);
218 i++;
219 }
220 writer->WriteChar(']');
221 }
222
SerializeSamples(HeapSnapshot * snapshot,StreamWriter * writer)223 void HeapSnapshotJSONSerializer::SerializeSamples(HeapSnapshot *snapshot, StreamWriter *writer)
224 {
225 writer->WriteString("\"samples\":[");
226 const CVector<TimeStamp> &timeStamps = snapshot->GetTimeStamps();
227 if (!timeStamps.empty()) {
228 auto firstTimeStamp = timeStamps[0];
229 bool isFirst = true;
230 for (auto timeStamp : timeStamps) {
231 if (!isFirst) {
232 writer->WriteString("\n, ");
233 } else {
234 isFirst = false;
235 }
236 writer->WriteNumber(timeStamp.GetTimeStamp() - firstTimeStamp.GetTimeStamp());
237 writer->WriteString(", ");
238 writer->WriteNumber(timeStamp.GetLastSequenceId());
239 }
240 }
241 writer->WriteString("],\n");
242 }
243
SerializeLocations(StreamWriter * writer)244 void HeapSnapshotJSONSerializer::SerializeLocations(StreamWriter *writer)
245 {
246 writer->WriteString("\"locations\":[],\n");
247 }
248
SerializeStringTable(HeapSnapshot * snapshot,StreamWriter * writer)249 void HeapSnapshotJSONSerializer::SerializeStringTable(HeapSnapshot *snapshot, StreamWriter *writer)
250 {
251 LOG_ECMA(INFO) << "HeapSnapshotJSONSerializer::SerializeStringTable";
252 ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK,
253 "HeapSnapshotJSONSerializer::SerializeStringTable", "");
254 const StringHashMap *stringTable = snapshot->GetEcmaStringTable();
255 ASSERT(stringTable != nullptr);
256 writer->WriteString("\"strings\":[\"<dummy>\",\n");
257 writer->WriteString("\"\",\n");
258 writer->WriteString("\"GC roots\",\n");
259 // StringId Range from 3
260 size_t capcity = stringTable->GetCapcity();
261 ASSERT(capcity > 0);
262 size_t i = 0;
263 for (auto key : stringTable->GetOrderedKeyStorage()) {
264 if (i == capcity - 1) {
265 writer->WriteChar('\"');
266 SerializeString(stringTable->GetStringByKey(key), writer); // No Comma for the last line
267 writer->WriteString("\"\n");
268 } else {
269 writer->WriteChar('\"');
270 SerializeString(stringTable->GetStringByKey(key), writer);
271 writer->WriteString("\",\n");
272 }
273 i++;
274 }
275 writer->WriteString("]\n");
276 }
277
SerializeString(CString * str,StreamWriter * writer)278 void HeapSnapshotJSONSerializer::SerializeString(CString *str, StreamWriter *writer)
279 {
280 if (str == nullptr || writer == nullptr) {
281 return;
282 }
283 const char *s = str->c_str();
284 while (*s != '\0') {
285 if (*s == '\"' || *s == '\\') {
286 writer->WriteChar('\\');
287 writer->WriteChar(*s);
288 s++;
289 } else if (*s == '\n') {
290 writer->WriteString("\\n");
291 s++;
292 } else if (*s == '\b') {
293 writer->WriteString("\\b");
294 s++;
295 } else if (*s == '\f') {
296 writer->WriteString("\\f");
297 s++;
298 } else if (*s == '\r') {
299 writer->WriteString("\\r");
300 s++;
301 } else if (*s == '\t') {
302 writer->WriteString("\\t");
303 s++;
304 } else if (*s > ASCII_US && *s < ASCII_DEL) {
305 writer->WriteChar(*s);
306 s++;
307 } else if (*s <= ASCII_US || *s == ASCII_DEL) {
308 // special char convert to \u unicode
309 SerializeUnicodeChar(static_cast<uint32_t>(*s), writer);
310 s++;
311 } else {
312 // convert utf-8 to \u unicode
313 size_t len = 1;
314 while (len <= UTF8_MAX_BYTES && *(s + len) != '\0') {
315 len++;
316 }
317 auto [unicode, bytes] =
318 common::utf_helper::ConvertUtf8ToUnicodeChar(reinterpret_cast<const uint8_t *>(s), len);
319 if (unicode == common::utf_helper::INVALID_UTF8) {
320 LOG_ECMA(WARN) << "HeapSnapshotJSONSerializer::SerializeString, str is not utf-8";
321 writer->WriteChar('?');
322 s++;
323 } else {
324 SerializeUnicodeChar(unicode, writer);
325 s += bytes;
326 }
327 }
328 }
329 }
330
SerializeUnicodeChar(uint32_t unicodeChar,StreamWriter * writer)331 void HeapSnapshotJSONSerializer::SerializeUnicodeChar(uint32_t unicodeChar, StreamWriter *writer)
332 {
333 static const char hexChars[] = "0123456789ABCDEF";
334 writer->WriteString("\\u");
335 writer->WriteChar(hexChars[(unicodeChar >> 0xC) & 0xF]);
336 writer->WriteChar(hexChars[(unicodeChar >> 0x8) & 0xF]);
337 writer->WriteChar(hexChars[(unicodeChar >> 0x4) & 0xF]);
338 writer->WriteChar(hexChars[unicodeChar & 0xF]);
339 }
340
SerializerSnapshotClosure(StreamWriter * writer)341 void HeapSnapshotJSONSerializer::SerializerSnapshotClosure(StreamWriter *writer)
342 {
343 writer->WriteString("}\n");
344 }
345 } // namespace panda::ecmascript
346