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
~HeapSnapshotJSONSerializer()23 HeapSnapshotJSONSerializer::~HeapSnapshotJSONSerializer()
24 {
25 if (!writer_) {
26 delete writer_;
27 }
28 }
29
Serialize(HeapSnapshot * snapshot,Stream * stream)30 bool HeapSnapshotJSONSerializer::Serialize(HeapSnapshot *snapshot, Stream *stream)
31 {
32 // Serialize Node/Edge/String-Table
33 LOG_ECMA(ERROR) << "HeapSnapshotJSONSerializer::Serialize begin";
34 snapshot_ = snapshot;
35 ASSERT(snapshot_->GetNodes() != nullptr && snapshot_->GetEdges() != nullptr &&
36 snapshot_->GetEcmaStringTable() != nullptr);
37 writer_ = new StreamWriter(stream);
38
39 SerializeSnapshotHeader(); // 1.
40 SerializeNodes(); // 2.
41 SerializeEdges(); // 3.
42 SerializeTraceFunctionInfo(); // 4.
43 SerializeTraceTree(); // 5.
44 SerializeSamples(); // 6.
45 SerializeLocations(); // 7.
46 SerializeStringTable(); // 8.
47 SerializerSnapshotClosure(); // 9.
48 writer_->End();
49
50 LOG_ECMA(ERROR) << "HeapSnapshotJSONSerializer::Serialize exit";
51 return true;
52 }
53
SerializeSnapshotHeader()54 void HeapSnapshotJSONSerializer::SerializeSnapshotHeader()
55 {
56 writer_->Write("{\"snapshot\":\n"); // 1.
57 writer_->Write("{\"meta\":\n"); // 2.
58 // NOLINTNEXTLINE(modernize-raw-string-literal)
59 writer_->Write("{\"node_fields\":[\"type\",\"name\",\"id\",\"self_size\",\"edge_count\",\"trace_node_id\",");
60 writer_->Write("\"detachedness\"],\n"); // 3.
61 // NOLINTNEXTLINE(modernize-raw-string-literal)
62 writer_->Write("\"node_types\":[[\"hidden\",\"array\",\"string\",\"object\",\"code\",\"closure\",\"regexp\",");
63 // NOLINTNEXTLINE(modernize-raw-string-literal)
64 writer_->Write("\"number\",\"native\",\"synthetic\",\"concatenated string\",\"slicedstring\",\"symbol\",");
65 // NOLINTNEXTLINE(modernize-raw-string-literal)
66 writer_->Write("\"bigint\"],\"string\",\"number\",\"number\",\"number\",\"number\",\"number\"],\n"); // 4.
67 // NOLINTNEXTLINE(modernize-raw-string-literal)
68 writer_->Write("\"edge_fields\":[\"type\",\"name_or_index\",\"to_node\"],\n"); // 5.
69 // NOLINTNEXTLINE(modernize-raw-string-literal)
70 writer_->Write("\"edge_types\":[[\"context\",\"element\",\"property\",\"internal\",\"hidden\",\"shortcut\",");
71 // NOLINTNEXTLINE(modernize-raw-string-literal)
72 writer_->Write("\"weak\"],\"string_or_number\",\"node\"],\n"); // 6.
73 // NOLINTNEXTLINE(modernize-raw-string-literal)
74 writer_->Write("\"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\",");
75 // NOLINTNEXTLINE(modernize-raw-string-literal)
76 writer_->Write("\"line\",\"column\"],\n"); // 7.
77 // NOLINTNEXTLINE(modernize-raw-string-literal)
78 writer_->Write("\"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],\n");
79 // NOLINTNEXTLINE(modernize-raw-string-literal)
80 writer_->Write("\"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],\n"); // 9.
81 // NOLINTNEXTLINE(modernize-raw-string-literal)
82 // 10.
83 writer_->Write("\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]},\n\"node_count\":");
84 writer_->Write(snapshot_->GetNodeCount()); // 11.
85 writer_->Write(",\n\"edge_count\":");
86 writer_->Write(snapshot_->GetEdgeCount()); // 12.
87 writer_->Write(",\n\"trace_function_count\":");
88 writer_->Write(snapshot_->GetTrackAllocationsStack().size()); // 13.
89 writer_->Write("\n},\n"); // 14.
90 }
91
SerializeNodes()92 void HeapSnapshotJSONSerializer::SerializeNodes()
93 {
94 const CList<Node *> *nodes = snapshot_->GetNodes();
95 const StringHashMap *stringTable = snapshot_->GetEcmaStringTable();
96 ASSERT(nodes != nullptr);
97 writer_->Write("\"nodes\":["); // Section Header
98 size_t i = 0;
99 for (auto *node : *nodes) {
100 if (i > 0) {
101 writer_->Write(","); // add comma except first line
102 }
103 writer_->Write(static_cast<int>(NodeTypeConverter::Convert(node->GetType()))); // 1.
104 writer_->Write(",");
105 writer_->Write(stringTable->GetStringId(node->GetName())); // 2.
106 writer_->Write(",");
107 writer_->Write(node->GetId()); // 3.
108 writer_->Write(",");
109 writer_->Write(node->GetSelfSize()); // 4.
110 writer_->Write(",");
111 writer_->Write(node->GetEdgeCount()); // 5.
112 writer_->Write(",");
113 writer_->Write(node->GetStackTraceId()); // 6.
114 writer_->Write(",");
115 if (i == nodes->size() - 1) { // add comma at last the line
116 writer_->Write("0],\n"); // 7. detachedness default
117 } else {
118 writer_->Write("0\n"); // 7.
119 }
120 i++;
121 }
122 }
123
SerializeEdges()124 void HeapSnapshotJSONSerializer::SerializeEdges()
125 {
126 const CList<Edge *> *edges = snapshot_->GetEdges();
127 const StringHashMap *stringTable = snapshot_->GetEcmaStringTable();
128 ASSERT(edges != nullptr);
129 writer_->Write("\"edges\":[");
130 size_t i = 0;
131 for (auto *edge : *edges) {
132 if (i > 0) { // add comma except the first line
133 writer_->Write(",");
134 }
135 writer_->Write(static_cast<int>(edge->GetType())); // 1.
136 writer_->Write(",");
137 writer_->Write(stringTable->GetStringId(edge->GetName())); // 2. Use StringId
138 writer_->Write(",");
139
140 if (i == edges->size() - 1) { // add comma at last the line
141 writer_->Write(edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT); // 3.
142 writer_->Write("],\n");
143 } else {
144 writer_->Write(edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT); // 3.
145 writer_->Write("\n");
146 }
147 i++;
148 }
149 }
150
SerializeTraceFunctionInfo()151 void HeapSnapshotJSONSerializer::SerializeTraceFunctionInfo()
152 {
153 const CVector<FunctionInfo> trackAllocationsStack = snapshot_->GetTrackAllocationsStack();
154 const StringHashMap *stringTable = snapshot_->GetEcmaStringTable();
155
156 writer_->Write("\"trace_function_infos\":["); // Empty
157 size_t i = 0;
158
159 for (const auto &info : trackAllocationsStack) {
160 if (i > 0) { // add comma except the first line
161 writer_->Write(",");
162 }
163 writer_->Write(info.functionId);
164 writer_->Write(",");
165 CString functionName(info.functionName.c_str());
166 writer_->Write(stringTable->GetStringId(&functionName));
167 writer_->Write(",");
168 CString scriptName(info.scriptName.c_str());
169 writer_->Write(stringTable->GetStringId(&scriptName));
170 writer_->Write(",");
171 writer_->Write(info.scriptId);
172 writer_->Write(",");
173 writer_->Write(info.columnNumber);
174 writer_->Write(",");
175 writer_->Write(info.lineNumber);
176 writer_->Write("\n");
177 i++;
178 }
179 writer_->Write("],\n");
180 }
181
SerializeTraceTree()182 void HeapSnapshotJSONSerializer::SerializeTraceTree()
183 {
184 writer_->Write("\"trace_tree\":[");
185 TraceTree* tree = snapshot_->GetTraceTree();
186 if ((tree != nullptr) && (snapshot_->trackAllocations())) {
187 SerializeTraceNode(tree->GetRoot());
188 }
189 writer_->Write("],\n");
190 }
191
SerializeTraceNode(TraceNode * node)192 void HeapSnapshotJSONSerializer::SerializeTraceNode(TraceNode* node)
193 {
194 if (node == nullptr) {
195 return;
196 }
197
198 writer_->Write(node->GetId());
199 writer_->Write(",");
200 writer_->Write(node->GetNodeIndex());
201 writer_->Write(",");
202 writer_->Write(node->GetTotalCount());
203 writer_->Write(",");
204 writer_->Write(node->GetTotalSize());
205 writer_->Write(",[");
206
207 int i = 0;
208 for (TraceNode* child : node->GetChildren()) {
209 if (i > 0) {
210 writer_->Write(",");
211 }
212 SerializeTraceNode(child);
213 i++;
214 }
215 writer_->Write("]");
216 }
217
SerializeSamples()218 void HeapSnapshotJSONSerializer::SerializeSamples()
219 {
220 writer_->Write("\"samples\":[");
221 const CVector<TimeStamp> &timeStamps = snapshot_->GetTimeStamps();
222 if (!timeStamps.empty()) {
223 auto firstTimeStamp = timeStamps[0];
224 bool isFirst = true;
225 for (auto timeStamp : timeStamps) {
226 if (!isFirst) {
227 writer_->Write("\n, ");
228 } else {
229 isFirst = false;
230 }
231 writer_->Write(timeStamp.GetTimeStamp() - firstTimeStamp.GetTimeStamp());
232 writer_->Write(", ");
233 writer_->Write(timeStamp.GetLastSequenceId());
234 }
235 }
236 writer_->Write("],\n");
237 }
238
SerializeLocations()239 void HeapSnapshotJSONSerializer::SerializeLocations()
240 {
241 writer_->Write("\"locations\":[],\n");
242 }
243
SerializeStringTable()244 void HeapSnapshotJSONSerializer::SerializeStringTable()
245 {
246 const StringHashMap *stringTable = snapshot_->GetEcmaStringTable();
247 ASSERT(stringTable != nullptr);
248 writer_->Write("\"strings\":[\"<dummy>\",\n");
249 writer_->Write("\"\",\n");
250 writer_->Write("\"GC roots\",\n");
251 // StringId Range from 3
252 size_t capcity = stringTable->GetCapcity();
253 size_t i = 0;
254 for (auto key : stringTable->GetOrderedKeyStorage()) {
255 if (i == capcity - 1) {
256 writer_->Write("\"");
257 writer_->Write(*(stringTable->GetStringByKey(key))); // No Comma for the last line
258 writer_->Write("\"\n");
259 } else {
260 writer_->Write("\"");
261 writer_->Write(*(stringTable->GetStringByKey(key)));
262 writer_->Write("\",\n");
263 }
264 i++;
265 }
266 writer_->Write("]\n");
267 }
268
SerializerSnapshotClosure()269 void HeapSnapshotJSONSerializer::SerializerSnapshotClosure()
270 {
271 writer_->Write("}\n");
272 }
273 } // namespace panda::ecmascript
274