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