• 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 #include "ecmascript/dfx/hprof/string_hashmap.h"
20 
21 namespace panda::ecmascript {
22 
Serialize(HeapSnapshot * snapshot,Stream * stream)23 bool HeapSnapshotJSONSerializer::Serialize(HeapSnapshot *snapshot, Stream *stream)
24 {
25     // Serialize Node/Edge/String-Table
26     LOG_ECMA(INFO) << "HeapSnapshotJSONSerializer::Serialize begin";
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\"],\"string\",\"number\",\"number\",\"number\",\"number\",\"number\"],\n");  // 4.
61     // NOLINTNEXTLINE(modernize-raw-string-literal)
62     writer->WriteString("\"edge_fields\":[\"type\",\"name_or_index\",\"to_node\"],\n");  // 5.
63     // NOLINTNEXTLINE(modernize-raw-string-literal)
64     writer->WriteString("\"edge_types\":[[\"context\",\"element\",\"property\",\"internal\",\"hidden\",\"shortcut\",");
65     // NOLINTNEXTLINE(modernize-raw-string-literal)
66     writer->WriteString("\"weak\"],\"string_or_number\",\"node\"],\n");  // 6.
67     // NOLINTNEXTLINE(modernize-raw-string-literal)
68     writer->WriteString("\"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\",");
69     // NOLINTNEXTLINE(modernize-raw-string-literal)
70     writer->WriteString("\"line\",\"column\"],\n");  // 7.
71     // NOLINTNEXTLINE(modernize-raw-string-literal)
72     writer->WriteString("\"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],\n");
73     // NOLINTNEXTLINE(modernize-raw-string-literal)
74     writer->WriteString("\"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],\n");  // 9.
75     // NOLINTNEXTLINE(modernize-raw-string-literal)
76     // 10.
77     writer->WriteString("\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]},\n\"node_count\":");
78     writer->WriteNumber(snapshot->GetNodeCount());                         // 11.
79     writer->WriteString(",\n\"edge_count\":");
80     writer->WriteNumber(snapshot->GetEdgeCount());                         // 12.
81     writer->WriteString(",\n\"trace_function_count\":");
82     writer->WriteNumber(snapshot->GetTrackAllocationsStack().size());   // 13.
83     writer->WriteString("\n},\n");  // 14.
84 }
85 
SerializeNodes(HeapSnapshot * snapshot,StreamWriter * writer)86 void HeapSnapshotJSONSerializer::SerializeNodes(HeapSnapshot *snapshot, StreamWriter *writer)
87 {
88     const CList<Node *> *nodes = snapshot->GetNodes();
89     const StringHashMap *stringTable = snapshot->GetEcmaStringTable();
90     ASSERT(nodes != nullptr);
91     writer->WriteString("\"nodes\":[");  // Section Header
92     size_t i = 0;
93     for (auto *node : *nodes) {
94         if (i > 0) {
95             writer->WriteChar(',');  // add comma except first line
96         }
97         writer->WriteNumber(static_cast<int>(node->GetType()));  // 1.
98         writer->WriteChar(',');
99         writer->WriteNumber(stringTable->GetStringId(node->GetName()));                      // 2.
100         writer->WriteChar(',');
101         writer->WriteNumber(node->GetId());                                                  // 3.
102         writer->WriteChar(',');
103         writer->WriteNumber(node->GetSelfSize());                                            // 4.
104         writer->WriteChar(',');
105         writer->WriteNumber(node->GetEdgeCount());                                           // 5.
106         writer->WriteChar(',');
107         writer->WriteNumber(node->GetStackTraceId());                                        // 6.
108         writer->WriteChar(',');
109         writer->WriteChar('0');                                                              // 7.detachedness default 0
110         writer->WriteChar(',');
111         writer->WriteNumber(node->GetNativeSize());
112         if (i == nodes->size() - 1) {    // add comma at last the line
113             writer->WriteString("],\n"); // 7. detachedness default
114         } else {
115             writer->WriteString("\n");   // 7.
116         }
117         i++;
118     }
119 }
120 
SerializeEdges(HeapSnapshot * snapshot,StreamWriter * writer)121 void HeapSnapshotJSONSerializer::SerializeEdges(HeapSnapshot *snapshot, StreamWriter *writer)
122 {
123     const CList<Edge *> *edges = snapshot->GetEdges();
124     const StringHashMap *stringTable = snapshot->GetEcmaStringTable();
125     ASSERT(edges != nullptr);
126     writer->WriteString("\"edges\":[");
127     size_t i = 0;
128     for (auto *edge : *edges) {
129         StringId nameOrIndex = edge->GetType() == EdgeType::ELEMENT ?
130             edge->GetIndex() : stringTable->GetStringId(edge->GetName());
131         if (i > 0) {  // add comma except the first line
132             writer->WriteChar(',');
133         }
134         writer->WriteNumber(static_cast<int>(edge->GetType()));          // 1.
135         writer->WriteChar(',');
136         writer->WriteNumber(nameOrIndex);  // 2. Use StringId
137         writer->WriteChar(',');
138 
139         if (i == edges->size() - 1) {  // add comma at last the line
140             writer->WriteNumber(edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT);  // 3.
141             writer->WriteString("],\n");
142         } else {
143             writer->WriteNumber(edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT);    // 3.
144             writer->WriteChar('\n');
145         }
146         i++;
147     }
148 }
149 
SerializeTraceFunctionInfo(HeapSnapshot * snapshot,StreamWriter * writer)150 void HeapSnapshotJSONSerializer::SerializeTraceFunctionInfo(HeapSnapshot *snapshot, StreamWriter *writer)
151 {
152     const CVector<FunctionInfo> trackAllocationsStack = snapshot->GetTrackAllocationsStack();
153     const StringHashMap *stringTable = snapshot->GetEcmaStringTable();
154 
155     writer->WriteString("\"trace_function_infos\":[");  // Empty
156     size_t i = 0;
157 
158     for (const auto &info : trackAllocationsStack) {
159         if (i > 0) {  // add comma except the first line
160             writer->WriteChar(',');
161         }
162         writer->WriteNumber(info.functionId);
163         writer->WriteChar(',');
164         CString functionName(info.functionName.c_str());
165         writer->WriteNumber(stringTable->GetStringId(&functionName));
166         writer->WriteChar(',');
167         CString scriptName(info.scriptName.c_str());
168         writer->WriteNumber(stringTable->GetStringId(&scriptName));
169         writer->WriteChar(',');
170         writer->WriteNumber(info.scriptId);
171         writer->WriteChar(',');
172         writer->WriteNumber(info.lineNumber);
173         writer->WriteChar(',');
174         writer->WriteNumber(info.columnNumber);
175         writer->WriteChar('\n');
176         i++;
177     }
178     writer->WriteString("],\n");
179 }
180 
SerializeTraceTree(HeapSnapshot * snapshot,StreamWriter * writer)181 void HeapSnapshotJSONSerializer::SerializeTraceTree(HeapSnapshot *snapshot, StreamWriter *writer)
182 {
183     writer->WriteString("\"trace_tree\":[");
184     TraceTree* tree = snapshot->GetTraceTree();
185     if ((tree != nullptr) && (snapshot->trackAllocations())) {
186         SerializeTraceNode(tree->GetRoot(), writer);
187     }
188     writer->WriteString("],\n");
189 }
190 
SerializeTraceNode(TraceNode * node,StreamWriter * writer)191 void HeapSnapshotJSONSerializer::SerializeTraceNode(TraceNode* node, StreamWriter *writer)
192 {
193     if (node == nullptr) {
194         return;
195     }
196 
197     writer->WriteNumber(node->GetId());
198     writer->WriteChar(',');
199     writer->WriteNumber(node->GetNodeIndex());
200     writer->WriteChar(',');
201     writer->WriteNumber(node->GetTotalCount());
202     writer->WriteChar(',');
203     writer->WriteNumber(node->GetTotalSize());
204     writer->WriteString(",[");
205 
206     int i = 0;
207     for (TraceNode* child : node->GetChildren()) {
208         if (i > 0) {
209             writer->WriteChar(',');
210         }
211         SerializeTraceNode(child, writer);
212         i++;
213     }
214     writer->WriteChar(']');
215 }
216 
SerializeSamples(HeapSnapshot * snapshot,StreamWriter * writer)217 void HeapSnapshotJSONSerializer::SerializeSamples(HeapSnapshot *snapshot, StreamWriter *writer)
218 {
219     writer->WriteString("\"samples\":[");
220     const CVector<TimeStamp> &timeStamps = snapshot->GetTimeStamps();
221     if (!timeStamps.empty()) {
222         auto firstTimeStamp = timeStamps[0];
223         bool isFirst = true;
224         for (auto timeStamp : timeStamps) {
225             if (!isFirst) {
226                 writer->WriteString("\n, ");
227             } else {
228                 isFirst = false;
229             }
230             writer->WriteNumber(timeStamp.GetTimeStamp() - firstTimeStamp.GetTimeStamp());
231             writer->WriteString(", ");
232             writer->WriteNumber(timeStamp.GetLastSequenceId());
233         }
234     }
235     writer->WriteString("],\n");
236 }
237 
SerializeLocations(StreamWriter * writer)238 void HeapSnapshotJSONSerializer::SerializeLocations(StreamWriter *writer)
239 {
240     writer->WriteString("\"locations\":[],\n");
241 }
242 
SerializeStringTable(HeapSnapshot * snapshot,StreamWriter * writer)243 void HeapSnapshotJSONSerializer::SerializeStringTable(HeapSnapshot *snapshot, StreamWriter *writer)
244 {
245     const StringHashMap *stringTable = snapshot->GetEcmaStringTable();
246     ASSERT(stringTable != nullptr);
247     writer->WriteString("\"strings\":[\"<dummy>\",\n");
248     writer->WriteString("\"\",\n");
249     writer->WriteString("\"GC roots\",\n");
250     // StringId Range from 3
251     size_t capcity = stringTable->GetCapcity();
252     size_t i = 0;
253     for (auto key : stringTable->GetOrderedKeyStorage()) {
254         if (i == capcity - 1) {
255             writer->WriteChar('\"');
256             SerializeString(stringTable->GetStringByKey(key), writer); // No Comma for the last line
257             writer->WriteString("\"\n");
258         } else {
259             writer->WriteChar('\"');
260             SerializeString(stringTable->GetStringByKey(key), writer);
261             writer->WriteString("\",\n");
262         }
263         i++;
264     }
265     writer->WriteString("]\n");
266 }
267 
SerializeString(CString * str,StreamWriter * writer)268 void HeapSnapshotJSONSerializer::SerializeString(CString *str, StreamWriter *writer)
269 {
270     const char *s = str->c_str();
271     while (*s != '\0') {
272         if (*s == '\"' || *s == '\\') {
273             writer->WriteChar('\\');
274             writer->WriteChar(*s);
275             s++;
276         } else if (*s == '\n') {
277             writer->WriteString("\\n");
278             s++;
279         } else if (*s == '\b') {
280             writer->WriteString("\\b");
281             s++;
282         } else if (*s == '\f') {
283             writer->WriteString("\\f");
284             s++;
285         } else if (*s == '\r') {
286             writer->WriteString("\\r");
287             s++;
288         } else if (*s == '\t') {
289             writer->WriteString("\\t");
290             s++;
291         } else if (*s > ASCII_US && *s < ASCII_DEL) {
292             writer->WriteChar(*s);
293             s++;
294         } else if (*s <= ASCII_US || *s == ASCII_DEL) {
295             // special char convert to \u unicode
296             SerializeUnicodeChar(static_cast<uint32_t>(*s), writer);
297             s++;
298         } else {
299             // convert utf-8 to \u unicode
300             size_t len = 1;
301             while (len <= UTF8_MAX_BYTES && *(s + len) != '\0') {
302                 len++;
303             }
304             auto [unicode, bytes] =
305                 base::utf_helper::ConvertUtf8ToUnicodeChar(reinterpret_cast<const uint8_t *>(s), len);
306             if (unicode == base::utf_helper::INVALID_UTF8) {
307                 LOG_ECMA(WARN) << "HeapSnapshotJSONSerializer::SerializeString, str is not utf-8";
308                 writer->WriteChar('?');
309                 s++;
310             } else {
311                 SerializeUnicodeChar(unicode, writer);
312                 s += bytes;
313             }
314         }
315     }
316 }
317 
SerializeUnicodeChar(uint32_t unicodeChar,StreamWriter * writer)318 void HeapSnapshotJSONSerializer::SerializeUnicodeChar(uint32_t unicodeChar, StreamWriter *writer)
319 {
320     static const char hexChars[] = "0123456789ABCDEF";
321     writer->WriteString("\\u");
322     writer->WriteChar(hexChars[(unicodeChar >> 0xC) & 0xF]);
323     writer->WriteChar(hexChars[(unicodeChar >> 0x8) & 0xF]);
324     writer->WriteChar(hexChars[(unicodeChar >> 0x4) & 0xF]);
325     writer->WriteChar(hexChars[unicodeChar & 0xF]);
326 }
327 
SerializerSnapshotClosure(StreamWriter * writer)328 void HeapSnapshotJSONSerializer::SerializerSnapshotClosure(StreamWriter *writer)
329 {
330     writer->WriteString("}\n");
331 }
332 }  // namespace panda::ecmascript
333