• 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_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 
48 /*
49 4 byte: str_num
50 4 byte: unuse
51 {
52 4 byte: string size
53 4 byte: obj_num
54 [8 byte: obj adrr] * obj_num
55 string contents
56 } * str_num
57 */
DumpStringTable(const StringHashMap * stringTable,Stream * stream,CUnorderedMap<uint64_t,CVector<uint64_t>> & strIdMapObjVec)58 uint32_t HeapSnapshotJSONSerializer::DumpStringTable(const StringHashMap *stringTable, Stream *stream,
59                                                      CUnorderedMap<uint64_t, CVector<uint64_t>> &strIdMapObjVec)
60 {
61     ASSERT(stringTable != nullptr);
62     size_t bufSize = 8 * 1024 * 1024; // 8MB buf use for string dump
63     char *buf = new char[bufSize];
64     uint32_t secHead[] = {stringTable->GetCapcity(), 0};
65     *reinterpret_cast<uint64_t *>(buf) = *reinterpret_cast<uint64_t *>(secHead);
66     uint32_t secTotalSize = sizeof(secHead);
67     size_t offset = sizeof(secHead);
68     for (auto key : stringTable->GetOrderedKeyStorage()) {
69         auto [strId, str] = stringTable->GetStringAndIdPair(key);
70         auto objVec = strIdMapObjVec[strId];
71         uint32_t objVecSize = objVec.size() * sizeof(uint64_t);
72         uint32_t strHead[] = {str->size(), objVec.size()};
73         auto currLen = sizeof(strHead) + objVecSize + str->size() + 1;
74         if (offset + currLen > bufSize) {
75             stream->WriteBinBlock(buf, offset);
76             offset = 0;
77             if (currLen > bufSize) {
78                 delete[] buf;
79                 bufSize = currLen;
80                 buf = new char[bufSize];
81             }
82         }
83         *reinterpret_cast<uint64_t *>(buf + offset) = *reinterpret_cast<uint64_t *>(strHead);
84         offset += sizeof(strHead);
85         if (memcpy_s(buf + offset, bufSize - offset, reinterpret_cast<void *>(objVec.data()), objVecSize) != EOK) {
86             LOG_ECMA(ERROR) << "DumpStringTable: memcpy_s failed";
87             break;
88         }
89         offset += objVecSize;
90         if (memcpy_s(buf + offset, bufSize - offset, str->data(), str->size() + 1) != EOK) {
91             LOG_ECMA(ERROR) << "DumpStringTable: memcpy_s failed";
92             break;
93         }
94         offset += str->size() + 1;
95         secTotalSize += currLen;
96     }
97     auto padding = (8 - secTotalSize % 8) % 8;
98     if (offset + padding > 0) {
99         stream->WriteBinBlock(buf, offset + padding);
100     }
101     delete[] buf;
102     return secTotalSize + padding;
103 }
104 
SerializeSnapshotHeader(HeapSnapshot * snapshot,StreamWriter * writer)105 void HeapSnapshotJSONSerializer::SerializeSnapshotHeader(HeapSnapshot *snapshot, StreamWriter *writer)
106 {
107     writer->WriteString("{\"snapshot\":\n");  // 1.
108     writer->WriteString("{\"meta\":\n");      // 2.
109     // NOLINTNEXTLINE(modernize-raw-string-literal)
110     writer->WriteString("{\"node_fields\":[\"type\",\"name\",\"id\",\"self_size\",\"edge_count\",\"trace_node_id\",");
111     writer->WriteString("\"detachedness\",\"native_size\"],\n");  // 3.
112     // NOLINTNEXTLINE(modernize-raw-string-literal)
113     writer->WriteString("\"node_types\":[[\"hidden\",\"array\",\"string\",\"object\",\"code\",\"closure\",\"regexp\",");
114     // NOLINTNEXTLINE(modernize-raw-string-literal)
115     writer->WriteString("\"number\",\"native\",\"synthetic\",\"concatenated string\",\"slicedstring\",\"symbol\",");
116     // NOLINTNEXTLINE(modernize-raw-string-literal)
117     writer->WriteString("\"bigint\"],\"string\",\"number\",\"number\",\"number\",\"number\",\"number\"],\n");  // 4.
118     // NOLINTNEXTLINE(modernize-raw-string-literal)
119     writer->WriteString("\"edge_fields\":[\"type\",\"name_or_index\",\"to_node\"],\n");  // 5.
120     // NOLINTNEXTLINE(modernize-raw-string-literal)
121     writer->WriteString("\"edge_types\":[[\"context\",\"element\",\"property\",\"internal\",\"hidden\",\"shortcut\",");
122     // NOLINTNEXTLINE(modernize-raw-string-literal)
123     writer->WriteString("\"weak\"],\"string_or_number\",\"node\"],\n");  // 6.
124     // NOLINTNEXTLINE(modernize-raw-string-literal)
125     writer->WriteString("\"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\",");
126     // NOLINTNEXTLINE(modernize-raw-string-literal)
127     writer->WriteString("\"line\",\"column\"],\n");  // 7.
128     // NOLINTNEXTLINE(modernize-raw-string-literal)
129     writer->WriteString("\"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],\n");
130     // NOLINTNEXTLINE(modernize-raw-string-literal)
131     writer->WriteString("\"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],\n");  // 9.
132     // NOLINTNEXTLINE(modernize-raw-string-literal)
133     // 10.
134     writer->WriteString("\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]},\n\"node_count\":");
135     writer->WriteNumber(snapshot->GetNodeCount());                         // 11.
136     writer->WriteString(",\n\"edge_count\":");
137     writer->WriteNumber(snapshot->GetEdgeCount());                         // 12.
138     writer->WriteString(",\n\"trace_function_count\":");
139     writer->WriteNumber(snapshot->GetTrackAllocationsStack().size());   // 13.
140     writer->WriteString("\n},\n");  // 14.
141 }
142 
SerializeNodes(HeapSnapshot * snapshot,StreamWriter * writer)143 void HeapSnapshotJSONSerializer::SerializeNodes(HeapSnapshot *snapshot, StreamWriter *writer)
144 {
145     LOG_ECMA(INFO) << "HeapSnapshotJSONSerializer::SerializeNodes";
146     ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "HeapSnapshotJSONSerializer::SerializeNodes");
147     const CList<Node *> *nodes = snapshot->GetNodes();
148     const StringHashMap *stringTable = snapshot->GetEcmaStringTable();
149     ASSERT(nodes != nullptr);
150     writer->WriteString("\"nodes\":[");  // Section Header
151     size_t i = 0;
152     for (auto *node : *nodes) {
153         if (i > 0) {
154             writer->WriteChar(',');  // add comma except first line
155         }
156         writer->WriteNumber(static_cast<int>(node->GetType()));  // 1.
157         writer->WriteChar(',');
158         writer->WriteNumber(stringTable->GetStringId(node->GetName()));                      // 2.
159         writer->WriteChar(',');
160         writer->WriteNumber(node->GetId());                                                  // 3.
161         writer->WriteChar(',');
162         writer->WriteNumber(node->GetSelfSize());                                            // 4.
163         writer->WriteChar(',');
164         writer->WriteNumber(node->GetEdgeCount());                                           // 5.
165         writer->WriteChar(',');
166         writer->WriteNumber(node->GetStackTraceId());                                        // 6.
167         writer->WriteChar(',');
168         writer->WriteChar('0');                                                              // 7.detachedness default 0
169         writer->WriteChar(',');
170         writer->WriteNumber(node->GetNativeSize());
171         if (i == nodes->size() - 1) {    // add comma at last the line
172             writer->WriteString("],\n"); // 7. detachedness default
173         } else {
174             writer->WriteString("\n");   // 7.
175         }
176         i++;
177     }
178 }
179 
SerializeEdges(HeapSnapshot * snapshot,StreamWriter * writer)180 void HeapSnapshotJSONSerializer::SerializeEdges(HeapSnapshot *snapshot, StreamWriter *writer)
181 {
182     LOG_ECMA(INFO) << "HeapSnapshotJSONSerializer::SerializeEdges";
183     ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "HeapSnapshotJSONSerializer::SerializeEdges");
184     const CList<Edge *> *edges = snapshot->GetEdges();
185     const StringHashMap *stringTable = snapshot->GetEcmaStringTable();
186     ASSERT(edges != nullptr);
187     writer->WriteString("\"edges\":[");
188     size_t i = 0;
189     for (auto *edge : *edges) {
190         StringId nameOrIndex = edge->GetType() == EdgeType::ELEMENT ?
191             edge->GetIndex() : stringTable->GetStringId(edge->GetName());
192         if (i > 0) {  // add comma except the first line
193             writer->WriteChar(',');
194         }
195         writer->WriteNumber(static_cast<int>(edge->GetType()));          // 1.
196         writer->WriteChar(',');
197         writer->WriteNumber(nameOrIndex);  // 2. Use StringId
198         writer->WriteChar(',');
199 
200         if (i == edges->size() - 1) {  // add comma at last the line
201             writer->WriteNumber(edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT);  // 3.
202             writer->WriteString("],\n");
203         } else {
204             writer->WriteNumber(edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT);    // 3.
205             writer->WriteChar('\n');
206         }
207         i++;
208     }
209 }
210 
SerializeTraceFunctionInfo(HeapSnapshot * snapshot,StreamWriter * writer)211 void HeapSnapshotJSONSerializer::SerializeTraceFunctionInfo(HeapSnapshot *snapshot, StreamWriter *writer)
212 {
213     const CVector<FunctionInfo> trackAllocationsStack = snapshot->GetTrackAllocationsStack();
214     const StringHashMap *stringTable = snapshot->GetEcmaStringTable();
215 
216     writer->WriteString("\"trace_function_infos\":[");  // Empty
217     size_t i = 0;
218 
219     for (const auto &info : trackAllocationsStack) {
220         if (i > 0) {  // add comma except the first line
221             writer->WriteChar(',');
222         }
223         writer->WriteNumber(info.functionId);
224         writer->WriteChar(',');
225         CString functionName(info.functionName.c_str());
226         writer->WriteNumber(stringTable->GetStringId(&functionName));
227         writer->WriteChar(',');
228         CString scriptName(info.scriptName.c_str());
229         writer->WriteNumber(stringTable->GetStringId(&scriptName));
230         writer->WriteChar(',');
231         writer->WriteNumber(info.scriptId);
232         writer->WriteChar(',');
233         writer->WriteNumber(info.lineNumber);
234         writer->WriteChar(',');
235         writer->WriteNumber(info.columnNumber);
236         writer->WriteChar('\n');
237         i++;
238     }
239     writer->WriteString("],\n");
240 }
241 
SerializeTraceTree(HeapSnapshot * snapshot,StreamWriter * writer)242 void HeapSnapshotJSONSerializer::SerializeTraceTree(HeapSnapshot *snapshot, StreamWriter *writer)
243 {
244     writer->WriteString("\"trace_tree\":[");
245     TraceTree* tree = snapshot->GetTraceTree();
246     if ((tree != nullptr) && (snapshot->trackAllocations())) {
247         SerializeTraceNode(tree->GetRoot(), writer);
248     }
249     writer->WriteString("],\n");
250 }
251 
SerializeTraceNode(TraceNode * node,StreamWriter * writer)252 void HeapSnapshotJSONSerializer::SerializeTraceNode(TraceNode* node, StreamWriter *writer)
253 {
254     if (node == nullptr) {
255         return;
256     }
257 
258     writer->WriteNumber(node->GetId());
259     writer->WriteChar(',');
260     writer->WriteNumber(node->GetNodeIndex());
261     writer->WriteChar(',');
262     writer->WriteNumber(node->GetTotalCount());
263     writer->WriteChar(',');
264     writer->WriteNumber(node->GetTotalSize());
265     writer->WriteString(",[");
266 
267     int i = 0;
268     for (TraceNode* child : node->GetChildren()) {
269         if (i > 0) {
270             writer->WriteChar(',');
271         }
272         SerializeTraceNode(child, writer);
273         i++;
274     }
275     writer->WriteChar(']');
276 }
277 
SerializeSamples(HeapSnapshot * snapshot,StreamWriter * writer)278 void HeapSnapshotJSONSerializer::SerializeSamples(HeapSnapshot *snapshot, StreamWriter *writer)
279 {
280     writer->WriteString("\"samples\":[");
281     const CVector<TimeStamp> &timeStamps = snapshot->GetTimeStamps();
282     if (!timeStamps.empty()) {
283         auto firstTimeStamp = timeStamps[0];
284         bool isFirst = true;
285         for (auto timeStamp : timeStamps) {
286             if (!isFirst) {
287                 writer->WriteString("\n, ");
288             } else {
289                 isFirst = false;
290             }
291             writer->WriteNumber(timeStamp.GetTimeStamp() - firstTimeStamp.GetTimeStamp());
292             writer->WriteString(", ");
293             writer->WriteNumber(timeStamp.GetLastSequenceId());
294         }
295     }
296     writer->WriteString("],\n");
297 }
298 
SerializeLocations(StreamWriter * writer)299 void HeapSnapshotJSONSerializer::SerializeLocations(StreamWriter *writer)
300 {
301     writer->WriteString("\"locations\":[],\n");
302 }
303 
SerializeStringTable(HeapSnapshot * snapshot,StreamWriter * writer)304 void HeapSnapshotJSONSerializer::SerializeStringTable(HeapSnapshot *snapshot, StreamWriter *writer)
305 {
306     LOG_ECMA(INFO) << "HeapSnapshotJSONSerializer::SerializeStringTable";
307     ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "HeapSnapshotJSONSerializer::SerializeStringTable");
308     const StringHashMap *stringTable = snapshot->GetEcmaStringTable();
309     ASSERT(stringTable != nullptr);
310     writer->WriteString("\"strings\":[\"<dummy>\",\n");
311     writer->WriteString("\"\",\n");
312     writer->WriteString("\"GC roots\",\n");
313     // StringId Range from 3
314     size_t capcity = stringTable->GetCapcity();
315     ASSERT(capcity > 0);
316     size_t i = 0;
317     for (auto key : stringTable->GetOrderedKeyStorage()) {
318         if (i == capcity - 1) {
319             writer->WriteChar('\"');
320             SerializeString(stringTable->GetStringByKey(key), writer); // No Comma for the last line
321             writer->WriteString("\"\n");
322         } else {
323             writer->WriteChar('\"');
324             SerializeString(stringTable->GetStringByKey(key), writer);
325             writer->WriteString("\",\n");
326         }
327         i++;
328     }
329     writer->WriteString("]\n");
330 }
331 
SerializeString(CString * str,StreamWriter * writer)332 void HeapSnapshotJSONSerializer::SerializeString(CString *str, StreamWriter *writer)
333 {
334     if (str == nullptr || writer == nullptr) {
335         return;
336     }
337     const char *s = str->c_str();
338     while (*s != '\0') {
339         if (*s == '\"' || *s == '\\') {
340             writer->WriteChar('\\');
341             writer->WriteChar(*s);
342             s++;
343         } else if (*s == '\n') {
344             writer->WriteString("\\n");
345             s++;
346         } else if (*s == '\b') {
347             writer->WriteString("\\b");
348             s++;
349         } else if (*s == '\f') {
350             writer->WriteString("\\f");
351             s++;
352         } else if (*s == '\r') {
353             writer->WriteString("\\r");
354             s++;
355         } else if (*s == '\t') {
356             writer->WriteString("\\t");
357             s++;
358         } else if (*s > ASCII_US && *s < ASCII_DEL) {
359             writer->WriteChar(*s);
360             s++;
361         } else if (*s <= ASCII_US || *s == ASCII_DEL) {
362             // special char convert to \u unicode
363             SerializeUnicodeChar(static_cast<uint32_t>(*s), writer);
364             s++;
365         } else {
366             // convert utf-8 to \u unicode
367             size_t len = 1;
368             while (len <= UTF8_MAX_BYTES && *(s + len) != '\0') {
369                 len++;
370             }
371             auto [unicode, bytes] =
372                 base::utf_helper::ConvertUtf8ToUnicodeChar(reinterpret_cast<const uint8_t *>(s), len);
373             if (unicode == base::utf_helper::INVALID_UTF8) {
374                 LOG_ECMA(WARN) << "HeapSnapshotJSONSerializer::SerializeString, str is not utf-8";
375                 writer->WriteChar('?');
376                 s++;
377             } else {
378                 SerializeUnicodeChar(unicode, writer);
379                 s += bytes;
380             }
381         }
382     }
383 }
384 
SerializeUnicodeChar(uint32_t unicodeChar,StreamWriter * writer)385 void HeapSnapshotJSONSerializer::SerializeUnicodeChar(uint32_t unicodeChar, StreamWriter *writer)
386 {
387     static const char hexChars[] = "0123456789ABCDEF";
388     writer->WriteString("\\u");
389     writer->WriteChar(hexChars[(unicodeChar >> 0xC) & 0xF]);
390     writer->WriteChar(hexChars[(unicodeChar >> 0x8) & 0xF]);
391     writer->WriteChar(hexChars[(unicodeChar >> 0x4) & 0xF]);
392     writer->WriteChar(hexChars[unicodeChar & 0xF]);
393 }
394 
SerializerSnapshotClosure(StreamWriter * writer)395 void HeapSnapshotJSONSerializer::SerializerSnapshotClosure(StreamWriter *writer)
396 {
397     writer->WriteString("}\n");
398 }
399 }  // namespace panda::ecmascript
400