1// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#if defined(DART_PRECOMPILER)
6
7#include "vm/v8_snapshot_writer.h"
8
9#include "vm/dart.h"
10#include "vm/os.h"
11
12namespace dart {
13
14const char* ZoneString(Zone* Z, const char* str) {
15 const intptr_t len = strlen(str) + 1;
16 char* dest = Z->Alloc<char>(len);
17 snprintf(dest, len, "%s", str);
18 return dest;
19}
20
21V8SnapshotProfileWriter::V8SnapshotProfileWriter(Zone* zone)
22 : zone_(zone),
23 node_types_(zone_),
24 edge_types_(zone_),
25 strings_(zone),
26 roots_(zone_) {
27 node_types_.Insert({"Unknown", kUnknown});
28 node_types_.Insert({"ArtificialRoot", kArtificialRoot});
29
30 edge_types_.Insert({"context", kContext});
31 edge_types_.Insert({"element", kElement});
32 edge_types_.Insert({"property", kProperty});
33 edge_types_.Insert({"internal", kInternal});
34
35 strings_.Insert({"<unknown>", kUnknownString});
36 strings_.Insert({"<artificial root>", kArtificialRootString});
37
38 nodes_.Insert({ArtificialRootId(),
39 {
40 kArtificialRoot,
41 kArtificialRootString,
42 ArtificialRootId(),
43 0,
44 nullptr,
45 0,
46 }});
47}
48
49void V8SnapshotProfileWriter::SetObjectTypeAndName(ObjectId object_id,
50 const char* type,
51 const char* name) {
52 ASSERT(type != nullptr);
53 NodeInfo* info = EnsureId(object_id);
54
55 if (!node_types_.HasKey(type)) {
56 node_types_.Insert({ZoneString(zone_, type), node_types_.Size()});
57 }
58
59 intptr_t type_id = node_types_.LookupValue(type);
60 ASSERT(info->type == kUnknown || info->type == type_id);
61 info->type = type_id;
62 if (name != nullptr) {
63 info->name = EnsureString(name);
64 } else {
65 info->name =
66 EnsureString(OS::SCreate(zone_, "Unnamed [%s] %s", type, "(nil)"));
67 }
68}
69
70void V8SnapshotProfileWriter::AttributeBytesTo(ObjectId object_id,
71 size_t num_bytes) {
72 EnsureId(object_id)->self_size += num_bytes;
73}
74
75void V8SnapshotProfileWriter::AttributeReferenceTo(ObjectId object_id,
76 Reference reference) {
77 EnsureId(reference.to_object_id);
78 NodeInfo* info = EnsureId(object_id);
79
80 ASSERT(reference.offset_or_name >= 0);
81 info->edges->Add({
82 static_cast<intptr_t>(reference.reference_type == Reference::kElement
83 ? kElement
84 : kProperty),
85 reference.offset_or_name,
86 reference.to_object_id,
87 });
88 ++edge_count_;
89}
90
91V8SnapshotProfileWriter::NodeInfo V8SnapshotProfileWriter::DefaultNode(
92 ObjectId object_id) {
93 return {
94 kUnknown,
95 kUnknownString,
96 object_id,
97 0,
98 new (zone_) ZoneGrowableArray<EdgeInfo>(zone_, 0),
99 -1,
100 };
101}
102
103const V8SnapshotProfileWriter::NodeInfo&
104V8SnapshotProfileWriter::ArtificialRoot() {
105 return nodes_.Lookup(ArtificialRootId())->value;
106}
107
108V8SnapshotProfileWriter::NodeInfo* V8SnapshotProfileWriter::EnsureId(
109 ObjectId object_id) {
110 if (!nodes_.HasKey(object_id)) {
111 NodeInfo info = DefaultNode(object_id);
112 nodes_.Insert({object_id, info});
113 }
114 return &nodes_.Lookup(object_id)->value;
115}
116
117intptr_t V8SnapshotProfileWriter::EnsureString(const char* str) {
118 if (!strings_.HasKey(str)) {
119 strings_.Insert({ZoneString(zone_, str), strings_.Size()});
120 return strings_.Size() - 1;
121 }
122 return strings_.LookupValue(str);
123}
124
125void V8SnapshotProfileWriter::WriteNodeInfo(JSONWriter* writer,
126 const NodeInfo& info) {
127 writer->PrintValue(info.type);
128 writer->PrintValue(info.name);
129 writer->PrintValue(NodeIdFor(info.id));
130 writer->PrintValue(info.self_size);
131 // The artificial root has 'nullptr' edges, it actually points to all the
132 // roots.
133 writer->PrintValue64(info.edges != nullptr ? info.edges->length()
134 : roots_.Size());
135 writer->PrintNewline();
136}
137
138void V8SnapshotProfileWriter::WriteEdgeInfo(JSONWriter* writer,
139 const EdgeInfo& info) {
140 writer->PrintValue64(info.type);
141 writer->PrintValue64(info.name_or_index);
142 writer->PrintValue64(nodes_.LookupValue(info.to_node).offset);
143 writer->PrintNewline();
144}
145
146void V8SnapshotProfileWriter::AddRoot(ObjectId object_id,
147 const char* name /*= nullptr*/) {
148 EnsureId(object_id);
149 // HeapSnapshotWorker.HeapSnapshot.calculateDistances (from HeapSnapshot.js)
150 // assumes that the root does not have more than one edge to any other node
151 // (most likely an oversight).
152 if (roots_.HasKey(object_id)) return;
153
154 ObjectIdToNodeInfoTraits::Pair pair;
155 pair.key = object_id;
156 pair.value = NodeInfo{
157 0, name != nullptr ? EnsureString(name) : -1, object_id, 0, nullptr, 0};
158 roots_.Insert(pair);
159}
160
161void V8SnapshotProfileWriter::WriteStringsTable(
162 JSONWriter* writer,
163 const DirectChainedHashMap<StringToIntMapTraits>& map) {
164 const char** strings = zone_->Alloc<const char*>(map.Size());
165 StringToIntMapTraits::Pair* pair = nullptr;
166 auto it = map.GetIterator();
167 while ((pair = it.Next()) != nullptr) {
168 ASSERT(pair->value >= 0 && pair->value < map.Size());
169 strings[pair->value] = pair->key;
170 }
171 for (intptr_t i = 0; i < map.Size(); ++i) {
172 writer->PrintValue(strings[i]);
173 writer->PrintNewline();
174 }
175}
176
177void V8SnapshotProfileWriter::Write(JSONWriter* writer) {
178 writer->OpenObject();
179
180 writer->OpenObject("snapshot");
181 {
182 writer->OpenObject("meta");
183
184 {
185 writer->OpenArray("node_fields");
186 writer->PrintValue("type");
187 writer->PrintValue("name");
188 writer->PrintValue("id");
189 writer->PrintValue("self_size");
190 writer->PrintValue("edge_count");
191 writer->CloseArray();
192 }
193
194 {
195 writer->OpenArray("node_types");
196 {
197 writer->OpenArray();
198 WriteStringsTable(writer, node_types_);
199 writer->CloseArray();
200 }
201 writer->CloseArray();
202 }
203
204 {
205 writer->OpenArray("edge_fields");
206 writer->PrintValue("type");
207 writer->PrintValue("name_or_index");
208 writer->PrintValue("to_node");
209 writer->CloseArray();
210 }
211
212 {
213 writer->OpenArray("edge_types");
214 {
215 writer->OpenArray();
216 WriteStringsTable(writer, edge_types_);
217 writer->CloseArray();
218 }
219 writer->CloseArray();
220 }
221
222 writer->CloseObject();
223
224 writer->PrintProperty64("node_count", nodes_.Size());
225 writer->PrintProperty64("edge_count", edge_count_ + roots_.Size());
226 }
227 writer->CloseObject();
228
229 {
230 writer->OpenArray("nodes");
231 // Write the artificial root node.
232 WriteNodeInfo(writer, ArtificialRoot());
233 intptr_t offset = kNumNodeFields;
234 ObjectIdToNodeInfoTraits::Pair* entry = nullptr;
235 auto it = nodes_.GetIterator();
236 while ((entry = it.Next()) != nullptr) {
237 ASSERT(entry->key == entry->value.id);
238 if (entry->value.id == ArtificialRootId()) {
239 continue; // Written separately above.
240 }
241 entry->value.offset = offset;
242 WriteNodeInfo(writer, entry->value);
243 offset += kNumNodeFields;
244 }
245 writer->CloseArray();
246 }
247
248 {
249 writer->OpenArray("edges");
250
251 // Write references from the artificial root to the actual roots.
252 ObjectIdToNodeInfoTraits::Pair* entry = nullptr;
253 auto roots_it = roots_.GetIterator();
254 for (int i = 0; (entry = roots_it.Next()) != nullptr; ++i) {
255 if (entry->value.name != -1) {
256 WriteEdgeInfo(writer, {kProperty, entry->value.name, entry->key});
257 } else {
258 WriteEdgeInfo(writer, {kInternal, i, entry->key});
259 }
260 }
261
262 auto nodes_it = nodes_.GetIterator();
263 while ((entry = nodes_it.Next()) != nullptr) {
264 if (entry->value.edges == nullptr) {
265 continue; // Artificial root, its edges are written separately above.
266 }
267
268 for (intptr_t i = 0; i < entry->value.edges->length(); ++i) {
269 WriteEdgeInfo(writer, entry->value.edges->At(i));
270 }
271 }
272
273 writer->CloseArray();
274 }
275
276 {
277 writer->OpenArray("strings");
278 WriteStringsTable(writer, strings_);
279 writer->CloseArray();
280 }
281
282 writer->CloseObject();
283}
284
285void V8SnapshotProfileWriter::Write(const char* filename) {
286 JSONWriter json;
287 Write(&json);
288
289 auto file_open = Dart::file_open_callback();
290 auto file_write = Dart::file_write_callback();
291 auto file_close = Dart::file_close_callback();
292 if ((file_open == nullptr) || (file_write == nullptr) ||
293 (file_close == nullptr)) {
294 OS::PrintErr("Could not access file callbacks to write snapshot profile.");
295 return;
296 }
297
298 auto file = file_open(filename, /*write=*/true);
299 if (file == nullptr) {
300 OS::PrintErr("Failed to open file %s\n", filename);
301 } else {
302 char* output = nullptr;
303 intptr_t output_length = 0;
304 json.Steal(&output, &output_length);
305 file_write(output, output_length, file);
306 free(output);
307 file_close(file);
308 }
309}
310
311} // namespace dart
312
313#endif
314