1 /*
2 * Copyright (c) 2024 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/rawheap_translate/serializer.h"
17 #include "ecmascript/dfx/hprof/rawheap_translate/string_hashmap.h"
18
19 namespace rawheap_translate {
Initialize(const std::string & filePath)20 bool StreamWriter::Initialize(const std::string &filePath)
21 {
22 if (filePath.empty() || filePath.size() > PATH_MAX) {
23 LOG_ERROR("filename is illegal!");
24 return false;
25 }
26
27 fileStream_.open(filePath, std::ios::out);
28 if (fileStream_.fail()) {
29 LOG_ERROR("FileStream: open file failed");
30 return false;
31 }
32
33 return true;
34 }
35
WriteString(const std::string & str)36 void StreamWriter::WriteString(const std::string &str)
37 {
38 MaybeWriteChunk();
39 auto len = str.size();
40 if (len == 0) {
41 return;
42 }
43 const char *cur = str.c_str();
44 const char *end = cur + len;
45 while (cur < end) {
46 int dstSize = chunkSize_ - current_;
47 int writeSize = std::min(static_cast<int>(end - cur), dstSize);
48 if (writeSize <= 0) {
49 break;
50 }
51 if (memcpy_s(chunk_.data() + current_, dstSize, cur, writeSize) != EOK) {
52 LOG_ERROR("StreamWriter::WriteString: memcpy_s failed!");
53 break;
54 }
55 cur += writeSize;
56 current_ += writeSize;
57 MaybeWriteChunk();
58 }
59 }
60
EndOfStream()61 void StreamWriter::EndOfStream()
62 {
63 if (current_ > 0) {
64 std::string str(chunk_.data(), current_);
65 fileStream_ << str;
66 current_ = 0;
67 }
68 if (fileStream_.is_open()) {
69 fileStream_.close();
70 }
71 }
72
Serialize(RawHeapTranslate * snapshot,StreamWriter * writer)73 bool HeapSnapshotJSONSerializer::Serialize(RawHeapTranslate *snapshot, StreamWriter *writer)
74 {
75 // Serialize Node/Edge/String-Table
76 LOG_INFO("HeapSnapshotJSONSerializer::Serialize begin");
77
78 SerializeSnapshotHeader(snapshot, writer); // 1.
79 SerializeNodes(snapshot, writer); // 2.
80 SerializeEdges(snapshot, writer); // 3.
81
82 writer->WriteString("\"trace_function_infos\":[],"); // 4.
83 writer->WriteString("\"trace_tree\":[],");
84 writer->WriteString("\"samples\":[],");
85 writer->WriteString("\"locations\":[],\n");
86
87 SerializeStringTable(snapshot, writer); // 8.
88 SerializerSnapshotClosure(writer); // 9.
89
90 LOG_INFO("HeapSnapshotJSONSerializer::Serialize exit");
91 return true;
92 }
93
SerializeSnapshotHeader(RawHeapTranslate * snapshot,StreamWriter * writer)94 void HeapSnapshotJSONSerializer::SerializeSnapshotHeader(RawHeapTranslate *snapshot, StreamWriter *writer)
95 {
96 writer->WriteString("{\"snapshot\":\n"); // 1.
97 writer->WriteString("{\"meta\":\n"); // 2.
98 // NOLINTNEXTLINE(modernize-raw-string-literal)
99 writer->WriteString("{\"node_fields\":[\"type\",\"name\",\"id\",\"self_size\",\"edge_count\",\"trace_node_id\",");
100 writer->WriteString("\"detachedness\",\"native_size\"],\n"); // 3.
101 // NOLINTNEXTLINE(modernize-raw-string-literal)
102 writer->WriteString("\"node_types\":[[\"hidden\",\"array\",\"string\",\"object\",\"code\",\"closure\",\"regexp\",");
103 // NOLINTNEXTLINE(modernize-raw-string-literal)
104 writer->WriteString("\"number\",\"native\",\"synthetic\",\"concatenated string\",\"slicedstring\",\"symbol\",");
105 // NOLINTNEXTLINE(modernize-raw-string-literal)
106 writer->WriteString("\"bigint\"],\"string\",\"number\",\"number\",\"number\",\"number\",\"number\"],\n"); // 4.
107 // NOLINTNEXTLINE(modernize-raw-string-literal)
108 writer->WriteString("\"edge_fields\":[\"type\",\"name_or_index\",\"to_node\"],\n"); // 5.
109 // NOLINTNEXTLINE(modernize-raw-string-literal)
110 writer->WriteString("\"edge_types\":[[\"context\",\"element\",\"property\",\"internal\",\"hidden\",\"shortcut\",");
111 // NOLINTNEXTLINE(modernize-raw-string-literal)
112 writer->WriteString("\"weak\"],\"string_or_number\",\"node\"],\n"); // 6.
113 // NOLINTNEXTLINE(modernize-raw-string-literal)
114 writer->WriteString("\"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\",");
115 // NOLINTNEXTLINE(modernize-raw-string-literal)
116 writer->WriteString("\"line\",\"column\"],\n"); // 7.
117 // NOLINTNEXTLINE(modernize-raw-string-literal)
118 writer->WriteString("\"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],\n");
119 // NOLINTNEXTLINE(modernize-raw-string-literal)
120 writer->WriteString("\"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],\n"); // 9.
121 // NOLINTNEXTLINE(modernize-raw-string-literal)
122 // 10.
123 writer->WriteString("\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]},\n\"node_count\":");
124 writer->WriteNumber(snapshot->GetNodeCount()); // 11.
125 writer->WriteString(",\n\"edge_count\":");
126 writer->WriteNumber(snapshot->GetEdgeCount()); // 12.
127 writer->WriteString(",\n\"trace_function_count\":");
128 writer->WriteNumber(0); // 13.
129 writer->WriteString("\n},\n"); // 14.
130 }
131
SerializeNodes(RawHeapTranslate * snapshot,StreamWriter * writer)132 void HeapSnapshotJSONSerializer::SerializeNodes(RawHeapTranslate *snapshot, StreamWriter *writer)
133 {
134 auto nodes = snapshot->GetNodes();
135 writer->WriteString("\"nodes\":["); // Section Header
136 size_t i = 0;
137 for (auto node : nodes) {
138 if (i > 0) {
139 writer->WriteChar(','); // add comma except first line
140 }
141 writer->WriteNumber(node->type); // 1.
142 writer->WriteChar(',');
143 writer->WriteNumber(node->strId); // 2.
144 writer->WriteChar(',');
145 writer->WriteNumber(node->nodeId); // 3.
146 writer->WriteChar(',');
147 writer->WriteNumber(node->size); // 4.
148 writer->WriteChar(',');
149 writer->WriteNumber(node->edgeCount); // 5.
150 writer->WriteChar(',');
151 writer->WriteNumber(0); // 6.
152 writer->WriteChar(',');
153 writer->WriteChar('0'); // 7.detachedness default 0
154 writer->WriteChar(',');
155 writer->WriteNumber(node->nativeSize);
156 if (i == nodes.size() - 1) { // add comma at last the line
157 writer->WriteString("],\n"); // 7. detachedness default
158 } else {
159 writer->WriteString("\n"); // 7.
160 }
161 i++;
162 }
163 }
164
SerializeEdges(RawHeapTranslate * snapshot,StreamWriter * writer)165 void HeapSnapshotJSONSerializer::SerializeEdges(RawHeapTranslate *snapshot, StreamWriter *writer)
166 {
167 auto edges = snapshot->GetEdges();
168 writer->WriteString("\"edges\":[");
169 size_t i = 0;
170 for (auto edge : edges) {
171 if (i > 0) { // add comma except the first line
172 writer->WriteChar(',');
173 }
174 writer->WriteNumber(static_cast<int>(edge->type)); // 1.
175 writer->WriteChar(',');
176 writer->WriteNumber(static_cast<int>(edge->nameOrIndex)); // 2. Use StringId
177 writer->WriteChar(',');
178
179 if (i == edges.size() - 1) { // add comma at last the line
180 writer->WriteNumber(edge->to->index * NODE_FIELD_COUNT); // 3.
181 writer->WriteString("],\n");
182 } else {
183 writer->WriteNumber(edge->to->index * NODE_FIELD_COUNT); // 3.
184 writer->WriteChar('\n');
185 }
186 i++;
187 }
188 }
189
SerializeStringTable(RawHeapTranslate * snapshot,StreamWriter * writer)190 void HeapSnapshotJSONSerializer::SerializeStringTable(RawHeapTranslate *snapshot, StreamWriter *writer)
191 {
192 auto stringTable = snapshot->GetEcmaStringTable();
193 writer->WriteString("\"strings\":[\"<dummy>\",\n");
194 writer->WriteString("\"\",\n");
195 writer->WriteString("\"GC roots\",\n");
196 // StringId Range from 3
197 size_t capcity = stringTable->GetCapcity();
198 if (capcity <= 0) {
199 return;
200 }
201 size_t i = 0;
202 for (auto key : stringTable->GetOrderedKeyStorage()) {
203 if (i == capcity - 1) {
204 writer->WriteChar('\"');
205 SerializeString(stringTable->GetStringByKey(key).c_str(), writer); // No Comma for the last line
206 writer->WriteString("\"\n");
207 } else {
208 writer->WriteChar('\"');
209 SerializeString(stringTable->GetStringByKey(key).c_str(), writer);
210 writer->WriteString("\",\n");
211 }
212 i++;
213 }
214 writer->WriteString("]\n");
215 }
216
SerializeString(const char * str,StreamWriter * writer)217 void HeapSnapshotJSONSerializer::SerializeString(const char *str, StreamWriter *writer)
218 {
219 if (str == nullptr || writer == nullptr) {
220 return;
221 }
222 const char *s = str;
223 while (*s != '\0') {
224 if (*s == '\"' || *s == '\\') {
225 writer->WriteChar('\\');
226 writer->WriteChar(*s);
227 s++;
228 } else if (*s == '\n') {
229 writer->WriteString("\\n");
230 s++;
231 } else if (*s == '\b') {
232 writer->WriteString("\\b");
233 s++;
234 } else if (*s == '\f') {
235 writer->WriteString("\\f");
236 s++;
237 } else if (*s == '\r') {
238 writer->WriteString("\\r");
239 s++;
240 } else if (*s == '\t') {
241 writer->WriteString("\\t");
242 s++;
243 } else if (*s > ASCII_US && *s < ASCII_DEL) {
244 writer->WriteChar(*s);
245 s++;
246 } else {
247 writer->WriteChar(*s);
248 s++;
249 }
250 }
251 }
252
SerializerSnapshotClosure(StreamWriter * writer)253 void HeapSnapshotJSONSerializer::SerializerSnapshotClosure(StreamWriter *writer)
254 {
255 writer->WriteString("}\n");
256 }
257
258 } // namespace rawheap_translate
259