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/hprof/heap_snapshot_json_serializer.h"
17 #include "ecmascript/mem/c_containers.h"
18 #include "ecmascript/hprof/heap_snapshot.h"
19 #include "ecmascript/hprof/string_hashmap.h"
20
21 namespace panda::ecmascript {
Serialize(HeapSnapShot * snapShot,const CString & fileName)22 bool HeapSnapShotJSONSerializer::Serialize(HeapSnapShot *snapShot, const CString &fileName)
23 {
24 // Serialize Node/Edge/String-Table
25 LOG(ERROR, RUNTIME) << "HeapSnapShotJSONSerializer::Serialize begin";
26 snapShot_ = snapShot;
27 ASSERT(snapShot_->GetNodes() != nullptr && snapShot_->GetEdges() != nullptr &&
28 snapShot_->GetEcmaStringTable() != nullptr);
29 stringBuffer_.str(""); // Clear Buffer
30
31 SerializeSnapShotHeader(); // 1.
32 SerializeNodes(); // 2.
33 SerializeEdges(); // 3.
34 SerializeTraceFunctionInfo(); // 4.
35 SerializeTraceTree(); // 5.
36 SerializeSamples(); // 6.
37 SerializeLocations(); // 7.
38 SerializeStringTable(); // 8.
39 SerializerSnapShotClosure(); // 9.
40
41 WriteJSON(fileName); // 10.
42 LOG(ERROR, RUNTIME) << "HeapSnapShotJSONSerializer::Serialize exit";
43 return true;
44 }
45
SerializeSnapShotHeader()46 void HeapSnapShotJSONSerializer::SerializeSnapShotHeader()
47 {
48 stringBuffer_ << "{\"snapshot\":\n"; // 1.
49 stringBuffer_ << "{\"meta\":\n"; // 2.
50 // NOLINTNEXTLINE(modernize-raw-string-literal)
51 stringBuffer_ << "{\"node_fields\":[\"type\",\"name\",\"id\",\"self_size\",\"edge_count\",\"trace_node_id\",";
52 stringBuffer_ << "\"detachedness\"],\n"; // 3.
53 // NOLINTNEXTLINE(modernize-raw-string-literal)
54 stringBuffer_ << "\"node_types\":[[\"hidden\",\"array\",\"string\",\"object\",\"code\",\"closure\",\"regexp\",";
55 // NOLINTNEXTLINE(modernize-raw-string-literal)
56 stringBuffer_ << "\"number\",\"native\",\"synthetic\",\"concatenated string\",\"slicedstring\",\"symbol\",";
57 // NOLINTNEXTLINE(modernize-raw-string-literal)
58 stringBuffer_ << "\"bigint\"],\"string\",\"number\",\"number\",\"number\",\"number\",\"number\"],\n"; // 4.
59 // NOLINTNEXTLINE(modernize-raw-string-literal)
60 stringBuffer_ << "\"edge_fields\":[\"type\",\"name_or_index\",\"to_node\"],\n"; // 5.
61 // NOLINTNEXTLINE(modernize-raw-string-literal)
62 stringBuffer_ << "\"edge_types\":[[\"context\",\"element\",\"property\",\"internal\",\"hidden\",\"shortcut\",";
63 // NOLINTNEXTLINE(modernize-raw-string-literal)
64 stringBuffer_ << "\"weak\"],\"string_or_number\",\"node\"],\n"; // 6.
65 // NOLINTNEXTLINE(modernize-raw-string-literal)
66 stringBuffer_ << "\"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\",";
67 // NOLINTNEXTLINE(modernize-raw-string-literal)
68 stringBuffer_ << "\"line\",\"column\"],\n"; // 7.
69 // NOLINTNEXTLINE(modernize-raw-string-literal)
70 stringBuffer_ << "\"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],\n";
71 // NOLINTNEXTLINE(modernize-raw-string-literal)
72 stringBuffer_ << "\"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],\n"; // 9.
73 // NOLINTNEXTLINE(modernize-raw-string-literal)
74 stringBuffer_ << "\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]},\n"; // 10.
75 stringBuffer_ << "\"node_count\":" << snapShot_->GetNodeCount() << ",\n"; // 11.
76 stringBuffer_ << "\"edge_count\":" << snapShot_->GetEdgeCount() << ",\n"; // 12.
77 stringBuffer_ << "\"trace_function_count\":"
78 << "0\n"; // 13.
79 stringBuffer_ << "},\n"; // 14.
80 }
81
SerializeNodes()82 void HeapSnapShotJSONSerializer::SerializeNodes()
83 {
84 const CList<Node *> *nodes = snapShot_->GetNodes();
85 const StringHashMap *stringTable = snapShot_->GetEcmaStringTable();
86 ASSERT(nodes != nullptr);
87 stringBuffer_ << "\"nodes\":["; // Section Header
88 size_t i = 0;
89 for (auto *node : *nodes) {
90 if (i > 0) {
91 stringBuffer_ << ","; // add comma except first line
92 }
93 stringBuffer_ << static_cast<int>(NodeTypeConverter::Convert(node->GetType())) << ","; // 1.
94 stringBuffer_ << stringTable->GetStringId(node->GetName()) << ","; // 2.
95 stringBuffer_ << node->GetId() << ","; // 3.
96 stringBuffer_ << node->GetSelfSize() << ","; // 4.
97 stringBuffer_ << node->GetEdgeCount() << ","; // 5.
98 stringBuffer_ << node->GetStackTraceId() << ","; // 6.
99 if (i == nodes->size() - 1) { // add comma at last the line
100 stringBuffer_ << "0"
101 << "],\n"; // 7. detachedness default
102 } else {
103 stringBuffer_ << "0\n"; // 7.
104 }
105 i++;
106 }
107 }
108
SerializeEdges()109 void HeapSnapShotJSONSerializer::SerializeEdges()
110 {
111 const CVector<Edge *> *edges = snapShot_->GetEdges();
112 const StringHashMap *stringTable = snapShot_->GetEcmaStringTable();
113 ASSERT(edges != nullptr);
114 stringBuffer_ << "\"edges\":[";
115 size_t i = 0;
116 for (auto *edge : *edges) {
117 if (i > 0) { // add comma except the first line
118 stringBuffer_ << ",";
119 }
120 stringBuffer_ << static_cast<int>(edge->GetType()) << ","; // 1.
121 stringBuffer_ << stringTable->GetStringId(edge->GetName()) << ","; // 2. Use StringId
122
123 if (i == edges->size() - 1) { // add comma at last the line
124 stringBuffer_ << edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT << "],\n"; // 3.
125 } else {
126 stringBuffer_ << edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT << "\n"; // 3.
127 }
128 i++;
129 }
130 }
131
SerializeTraceFunctionInfo()132 void HeapSnapShotJSONSerializer::SerializeTraceFunctionInfo()
133 {
134 stringBuffer_ << "\"trace_function_infos\":[],\n"; // Empty
135 }
136
SerializeTraceTree()137 void HeapSnapShotJSONSerializer::SerializeTraceTree()
138 {
139 stringBuffer_ << "\"trace_tree\":[],\n"; // Empty
140 }
141
SerializeSamples()142 void HeapSnapShotJSONSerializer::SerializeSamples()
143 {
144 stringBuffer_ << "\"samples\":[";
145 const CVector<TimeStamp> &timeStamps = snapShot_->GetTimeStamps();
146 if (!timeStamps.empty()) {
147 auto firstTimeStamp = timeStamps[0];
148 bool isFirst = true;
149 for (auto timeStamp : timeStamps) {
150 if (!isFirst) {
151 stringBuffer_ << "\n, ";
152 } else {
153 isFirst = false;
154 }
155 stringBuffer_ << timeStamp.GetTimeStamp() - firstTimeStamp.GetTimeStamp() << ", ";
156 stringBuffer_ << timeStamp.GetLastSequenceId();
157 }
158 }
159 stringBuffer_ << "],\n";
160 }
161
SerializeLocations()162 void HeapSnapShotJSONSerializer::SerializeLocations()
163 {
164 stringBuffer_ << "\"locations\":[],\n";
165 }
166
SerializeStringTable()167 void HeapSnapShotJSONSerializer::SerializeStringTable()
168 {
169 const StringHashMap *stringTable = snapShot_->GetEcmaStringTable();
170 ASSERT(stringTable != nullptr);
171 stringBuffer_ << "\"strings\":[\"<dummy>\",\n";
172 stringBuffer_ << "\"\",\n";
173 stringBuffer_ << "\"GC roots\",\n";
174 // StringId Range from 3
175 size_t capcity = stringTable->GetCapcity();
176 size_t i = 0;
177 for (auto key : stringTable->GetOrderedKeyStorage()) {
178 if (i == capcity - 1) {
179 stringBuffer_ << "\"" << *(stringTable->GetStringByKey(key)) << "\"\n"; // No Comma for the last line
180 } else {
181 stringBuffer_ << "\"" << *(stringTable->GetStringByKey(key)) << "\",\n";
182 }
183 i++;
184 }
185 stringBuffer_ << "]\n";
186 }
187
SerializerSnapShotClosure()188 void HeapSnapShotJSONSerializer::SerializerSnapShotClosure()
189 {
190 stringBuffer_ << "}\n";
191 }
192
WriteJSON(const CString & fileName)193 void HeapSnapShotJSONSerializer::WriteJSON(const CString &fileName)
194 {
195 std::string fName(fileName);
196 LOG(ERROR, RUNTIME) << "HeapSnapShotJSONSerializer::WriteJSON" << fName;
197 outputStream_.open(fName, std::ios::out);
198 if (!outputStream_.good()) {
199 LOG_ECMA(ERROR) << "open file failed";
200 return;
201 }
202 outputStream_ << stringBuffer_.str();
203 outputStream_.close();
204 outputStream_.clear(); // Make sure the next open operation success
205 }
206 } // namespace panda::ecmascript
207