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