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