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