Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
v8_snapshot_writer.cc
Go to the documentation of this file.
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
6
7#include "vm/dart.h"
8#include "vm/os.h"
9
10namespace dart {
11
12const V8SnapshotProfileWriter::ObjectId
14
15#if defined(DART_PRECOMPILER)
16
18 : zone_(zone),
19 nodes_(zone_),
20 node_types_(zone_),
21 edge_types_(zone_),
22 strings_(zone_),
23 roots_(zone_) {
24 intptr_t idx = edge_types_.Add("context");
25 ASSERT_EQUAL(idx, static_cast<intptr_t>(Edge::Type::kContext));
26 idx = edge_types_.Add("element");
27 ASSERT_EQUAL(idx, static_cast<intptr_t>(Edge::Type::kElement));
28 idx = edge_types_.Add("property");
29 ASSERT_EQUAL(idx, static_cast<intptr_t>(Edge::Type::kProperty));
30 idx = edge_types_.Add("internal");
31 ASSERT_EQUAL(idx, static_cast<intptr_t>(Edge::Type::kInternal));
32
33 SetObjectTypeAndName(kArtificialRootId, "ArtificialRoot",
34 "<artificial root>");
35}
36
37void V8SnapshotProfileWriter::SetObjectTypeAndName(const ObjectId& object_id,
38 const char* type,
39 const char* name) {
40 ASSERT(type != nullptr);
41 NodeInfo* info = EnsureId(object_id);
42 const intptr_t type_index = node_types_.Add(type);
43 if (info->type != kInvalidString && info->type != type_index) {
44 FATAL("Attempting to assign mismatching type %s to node %s", type,
45 info->ToCString(this, zone_));
46 }
47 info->type = type_index;
48 // Don't overwrite any existing name.
49 if (info->name == kInvalidString) {
50 info->name = strings_.Add(name);
51 }
52}
53
54void V8SnapshotProfileWriter::AttributeBytesTo(const ObjectId& object_id,
55 size_t num_bytes) {
56 EnsureId(object_id)->self_size += num_bytes;
57}
58
59void V8SnapshotProfileWriter::AttributeReferenceTo(
60 const ObjectId& from_object_id,
61 const Reference& reference,
62 const ObjectId& to_object_id) {
63 ASSERT(reference.IsElement() ? reference.offset >= 0
64 : reference.name != nullptr);
65 EnsureId(to_object_id);
66 const Edge edge(this, reference);
67 EnsureId(from_object_id)->AddEdge(edge, to_object_id);
68}
69
70void V8SnapshotProfileWriter::AttributeDroppedReferenceTo(
71 const ObjectId& from_object_id,
72 const Reference& reference,
73 const ObjectId& to_object_id,
74 const ObjectId& replacement_object_id) {
75 ASSERT(to_object_id.IsArtificial());
76 ASSERT(!replacement_object_id.IsArtificial());
77 ASSERT(reference.IsElement() ? reference.offset >= 0
78 : reference.name != nullptr);
79
80 // The target node is added normally.
81 AttributeReferenceTo(from_object_id, reference, to_object_id);
82
83 EnsureId(replacement_object_id);
84 // Put the replacement node at an invalid offset or name that can still be
85 // associated with the real one. For offsets, this is the negative offset.
86 // For names, it's the name prefixed with ":replacement_".
87 Reference replacement_reference =
88 reference.IsElement() ? Reference::Element(-reference.offset)
89 : Reference::Property(OS::SCreate(
90 zone_, ":replacement_%s", reference.name));
91 const Edge replacement_edge(this, replacement_reference);
92 EnsureId(from_object_id)->AddEdge(replacement_edge, replacement_object_id);
93}
94
95bool V8SnapshotProfileWriter::HasId(const ObjectId& object_id) {
96 return nodes_.HasKey(object_id);
97}
98
99V8SnapshotProfileWriter::NodeInfo* V8SnapshotProfileWriter::EnsureId(
100 const ObjectId& object_id) {
101 if (!HasId(object_id)) {
102 nodes_.Insert(NodeInfo(this, object_id));
103 }
104 return nodes_.Lookup(object_id);
105}
106
107const char* V8SnapshotProfileWriter::NodeInfo::ToCString(
108 V8SnapshotProfileWriter* profile_writer,
109 Zone* zone) const {
110 JSONWriter writer;
111 WriteDebug(profile_writer, &writer);
112 return OS::SCreate(zone, "%s", writer.buffer()->buffer());
113}
114
115void V8SnapshotProfileWriter::NodeInfo::Write(
116 V8SnapshotProfileWriter* profile_writer,
117 JSONWriter* writer) const {
118 ASSERT(id.space() != IdSpace::kInvalid);
119 if (type == kInvalidString) {
120 FATAL("No type given for node %s", id.ToCString(profile_writer->zone_));
121 }
122 writer->PrintValue(type);
123 if (name != kInvalidString) {
124 writer->PrintValue(name);
125 } else {
126 ASSERT(profile_writer != nullptr);
127 // If we don't already have a name for the node, we lazily create a default
128 // one. This is safe since the strings table is written out after the nodes.
129 const intptr_t name = profile_writer->strings_.AddFormatted(
130 "Unnamed [%s] (nil)", profile_writer->node_types_.At(type));
131 writer->PrintValue(name);
132 }
133 id.Write(writer);
134 writer->PrintValue(self_size);
135 writer->PrintValue64(edges->Length());
136}
137
138void V8SnapshotProfileWriter::NodeInfo::WriteDebug(
139 V8SnapshotProfileWriter* profile_writer,
140 JSONWriter* writer) const {
141 writer->OpenObject();
142 if (type != kInvalidString) {
143 writer->PrintProperty("type", profile_writer->node_types_.At(type));
144 }
145 if (name != kInvalidString) {
146 writer->PrintProperty("name", profile_writer->strings_.At(name));
147 }
148 id.WriteDebug(writer, "id");
149 writer->PrintProperty("self_size", self_size);
150 edges->WriteDebug(profile_writer, writer, "edges");
151 writer->CloseObject();
152}
153
154const char* V8SnapshotProfileWriter::ObjectId::ToCString(Zone* zone) const {
155 JSONWriter writer;
156 WriteDebug(&writer);
157 return OS::SCreate(zone, "%s", writer.buffer()->buffer());
158}
159
160void V8SnapshotProfileWriter::ObjectId::Write(JSONWriter* writer,
161 const char* property) const {
162 if (property != nullptr) {
163 writer->PrintProperty64(property, encoded_);
164 } else {
165 writer->PrintValue64(encoded_);
166 }
167}
168
169void V8SnapshotProfileWriter::ObjectId::WriteDebug(JSONWriter* writer,
170 const char* property) const {
171 writer->OpenObject(property);
172 writer->PrintProperty("space", IdSpaceToCString(space()));
173 writer->PrintProperty64("nonce", nonce());
174 writer->CloseObject();
175}
176
177const char* V8SnapshotProfileWriter::ObjectId::IdSpaceToCString(IdSpace space) {
178 switch (space) {
179 case IdSpace::kInvalid:
180 return "Invalid";
181 case IdSpace::kSnapshot:
182 return "Snapshot";
183 case IdSpace::kVmText:
184 return "VmText";
185 case IdSpace::kIsolateText:
186 return "IsolateText";
187 case IdSpace::kVmData:
188 return "VmData";
189 case IdSpace::kIsolateData:
190 return "IsolateData";
191 case IdSpace::kArtificial:
192 return "Artificial";
193 default:
194 UNREACHABLE();
195 }
196}
197
198const char* V8SnapshotProfileWriter::EdgeMap::ToCString(
199 V8SnapshotProfileWriter* profile_writer,
200 Zone* zone) const {
201 JSONWriter writer;
202 WriteDebug(profile_writer, &writer);
203 return OS::SCreate(zone, "%s", writer.buffer()->buffer());
204}
205
206void V8SnapshotProfileWriter::EdgeMap::WriteDebug(
207 V8SnapshotProfileWriter* profile_writer,
208 JSONWriter* writer,
209 const char* property) const {
210 writer->OpenArray(property);
211 auto edge_it = GetIterator();
212 while (auto const pair = edge_it.Next()) {
213 pair->edge.WriteDebug(profile_writer, writer, pair->target);
214 }
215 writer->CloseArray();
216}
217
218void V8SnapshotProfileWriter::Edge::Write(
219 V8SnapshotProfileWriter* profile_writer,
220 JSONWriter* writer,
221 const ObjectId& target_id) const {
222 ASSERT(type != Type::kInvalid);
223 writer->PrintValue64(static_cast<intptr_t>(type));
224 writer->PrintValue64(name_or_offset);
225 auto const target = profile_writer->nodes_.LookupValue(target_id);
226 writer->PrintValue64(target.offset());
227}
228
229void V8SnapshotProfileWriter::Edge::WriteDebug(
230 V8SnapshotProfileWriter* profile_writer,
231 JSONWriter* writer,
232 const ObjectId& target_id) const {
233 writer->OpenObject();
234 if (type != Type::kInvalid) {
235 writer->PrintProperty(
236 "type", profile_writer->edge_types_.At(static_cast<intptr_t>(type)));
237 }
238 if (type == Type::kProperty) {
239 writer->PrintProperty("name", profile_writer->strings_.At(name_or_offset));
240 } else {
241 writer->PrintProperty64("offset", name_or_offset);
242 }
243 auto const target = profile_writer->nodes_.LookupValue(target_id);
244 target.id.WriteDebug(writer, "target");
245 writer->CloseObject();
246}
247
248void V8SnapshotProfileWriter::AddRoot(const ObjectId& object_id,
249 const char* name) {
250 // HeapSnapshotWorker.HeapSnapshot.calculateDistances (from HeapSnapshot.js)
251 // assumes that the root does not have more than one edge to any other node
252 // (most likely an oversight).
253 if (roots_.HasKey(object_id)) return;
254 roots_.Insert(object_id);
255
256 auto const str_index = strings_.Add(name);
257 auto const root = nodes_.Lookup(kArtificialRootId);
258 ASSERT(root != nullptr);
259 root->AddEdge(str_index != kInvalidString
260 ? Edge(this, Edge::Type::kProperty, str_index)
261 : Edge(this, Edge::Type::kInternal, root->edges->Length()),
262 object_id);
263}
264
265intptr_t V8SnapshotProfileWriter::StringsTable::Add(const char* str) {
266 if (str == nullptr) return kInvalidString;
267 if (auto const kv = index_map_.Lookup(str)) {
268 return kv->value;
269 }
270 const char* new_str = OS::SCreate(zone_, "%s", str);
271 const intptr_t index = strings_.length();
272 strings_.Add(new_str);
273 index_map_.Insert({new_str, index});
274 return index;
275}
276
277intptr_t V8SnapshotProfileWriter::StringsTable::AddFormatted(const char* fmt,
278 ...) {
279 va_list args;
280 va_start(args, fmt);
281 const char* str = OS::VSCreate(zone_, fmt, args);
282 va_end(args);
283 if (auto const kv = index_map_.Lookup(str)) {
284 return kv->value;
285 }
286 const intptr_t index = strings_.length();
287 strings_.Add(str);
288 index_map_.Insert({str, index});
289 return index;
290}
291
292const char* V8SnapshotProfileWriter::StringsTable::At(intptr_t index) const {
293 if (index > strings_.length()) return nullptr;
294 return strings_[index];
295}
296
297void V8SnapshotProfileWriter::StringsTable::Write(JSONWriter* writer,
298 const char* property) const {
299 writer->OpenArray(property);
300 for (auto const str : strings_) {
301 writer->PrintValue(str);
302 writer->PrintNewline();
303 }
304 writer->CloseArray();
305}
306
307void V8SnapshotProfileWriter::Write(JSONWriter* writer) {
308 writer->OpenObject();
309
310 writer->OpenObject("snapshot");
311 {
312 writer->OpenObject("meta");
313
314 {
315 writer->OpenArray("node_fields");
316 writer->PrintValue("type");
317 writer->PrintValue("name");
318 writer->PrintValue("id");
319 writer->PrintValue("self_size");
320 writer->PrintValue("edge_count");
321 writer->CloseArray();
322 }
323
324 {
325 writer->OpenArray("node_types");
326 node_types_.Write(writer);
327 writer->CloseArray();
328 }
329
330 {
331 writer->OpenArray("edge_fields");
332 writer->PrintValue("type");
333 writer->PrintValue("name_or_index");
334 writer->PrintValue("to_node");
335 writer->CloseArray();
336 }
337
338 {
339 writer->OpenArray("edge_types");
340 edge_types_.Write(writer);
341 writer->CloseArray();
342 }
343
344 writer->CloseObject();
345
346 writer->PrintProperty64("node_count", nodes_.Size());
347 {
348 intptr_t edge_count = 0;
349 auto nodes_it = nodes_.GetIterator();
350 while (auto const info = nodes_it.Next()) {
351 // All nodes should have an edge map, though it may be empty.
352 ASSERT(info->edges != nullptr);
353 edge_count += info->edges->Length();
354 }
355 writer->PrintProperty64("edge_count", edge_count);
356 }
357 }
358 writer->CloseObject();
359
360 {
361 writer->OpenArray("nodes");
362 // Always write the information for the artificial root first.
363 auto const root = nodes_.Lookup(kArtificialRootId);
364 ASSERT(root != nullptr);
365 intptr_t offset = 0;
366 root->set_offset(offset);
367 root->Write(this, writer);
368 offset += kNumNodeFields;
369 auto nodes_it = nodes_.GetIterator();
370 for (auto entry = nodes_it.Next(); entry != nullptr;
371 entry = nodes_it.Next()) {
372 if (entry->id == kArtificialRootId) continue;
373 entry->set_offset(offset);
374 entry->Write(this, writer);
375 offset += kNumNodeFields;
376 }
377 writer->CloseArray();
378 }
379
380 {
381 auto write_edges = [&](const NodeInfo& info) {
382 auto edges_it = info.edges->GetIterator();
383 while (auto const pair = edges_it.Next()) {
384 pair->edge.Write(this, writer, pair->target);
385 }
386 };
387 writer->OpenArray("edges");
388 // Always write the information for the artificial root first.
389 auto const root = nodes_.Lookup(kArtificialRootId);
390 ASSERT(root != nullptr);
391 write_edges(*root);
392 auto nodes_it = nodes_.GetIterator();
393 while (auto const entry = nodes_it.Next()) {
394 if (entry->id == kArtificialRootId) continue;
395 write_edges(*entry);
396 }
397 writer->CloseArray();
398 }
399
400 // Must happen after any calls to WriteNodeInfo, as those calls may add more
401 // strings.
402 strings_.Write(writer, "strings");
403
404 writer->CloseObject();
405}
406
407void V8SnapshotProfileWriter::Write(const char* filename) {
408 JSONWriter json;
409 Write(&json);
410
411 auto file_open = Dart::file_open_callback();
412 auto file_write = Dart::file_write_callback();
413 auto file_close = Dart::file_close_callback();
414 if ((file_open == nullptr) || (file_write == nullptr) ||
415 (file_close == nullptr)) {
416 OS::PrintErr("warning: Could not access file callbacks.");
417 return;
418 }
419
420 auto file = file_open(filename, /*write=*/true);
421 if (file == nullptr) {
422 OS::PrintErr("warning: Failed to write snapshot profile: %s\n", filename);
423 } else {
424 char* output = nullptr;
425 intptr_t output_length = 0;
426 json.Steal(&output, &output_length);
427 file_write(output, output_length, file);
428 free(output);
429 file_close(file);
430 }
431}
432
433#endif
434
435} // namespace dart
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
#define UNREACHABLE()
Definition assert.h:248
#define ASSERT_EQUAL(expected, actual)
Definition assert.h:309
static const ObjectId kArtificialRootId
#define ASSERT(E)
#define FATAL(error)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint32_t * target
const char * name
Definition fuchsia.cc:50
va_start(args, format)
va_end(args)
static SkString fmt(SkColor4f c)
Definition p3.cpp:43
Point offset