diff --git a/pkg/vm/lib/v8_snapshot_profile.dart b/pkg/vm/lib/v8_snapshot_profile.dart index ec01774c8b9..efda4c157e5 100644 --- a/pkg/vm/lib/v8_snapshot_profile.dart +++ b/pkg/vm/lib/v8_snapshot_profile.dart @@ -85,9 +85,37 @@ class V8SnapshotProfile extends Graph { Expect.equals(snapshot["node_count"], _parseNodes(top["nodes"])); Expect.equals(snapshot["edge_count"], _parseEdges(top["edges"])); + _verifyRoot(); + _calculateFromEdges(); } + void _verifyRoot() { + // HeapSnapshotWorker.HeapSnapshot.calculateDistances (from HeapSnapshot.js) + // assumes that the root does not have more than one edge to any other node + // (most likely an oversight). + final Set roots = {}; + for (final edge in _toEdges[root]) { + final int to = edge.nodeOffset; + Expect.isTrue(!roots.contains(to)); + roots.add(to); + } + + // Check that all nodes are reachable from the root (offset 0). + final Set enqueued = {root}; + final dfs = [root]; + while (!dfs.isEmpty) { + final next = dfs.removeLast(); + for (final edge in _toEdges[next]) { + if (!enqueued.contains(edge.nodeOffset)) { + enqueued.add(edge.nodeOffset); + dfs.add(edge.nodeOffset); + } + } + } + Expect.equals(enqueued.length, nodeCount); + } + void _parseMetadata(Map meta) { final List nodeFields = meta["node_fields"]; nodeFields.forEach(_nodeFields.add); @@ -160,7 +188,8 @@ class V8SnapshotProfile extends Graph { final int nameOrIndex = edges[edgeOffset + nameOrIndexIndex]; if (_edgeTypes[type] == "property") { Expect.isTrue(0 <= nameOrIndex && nameOrIndex < _strings.length); - } else if (_edgeTypes[type] == "element") { + } else if (_edgeTypes[type] == "element" || + _edgeTypes[type] == "context") { Expect.isTrue(nameOrIndex >= 0); } diff --git a/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart b/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart index 266adde15e5..fa99a622a42 100644 --- a/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart +++ b/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart @@ -149,6 +149,11 @@ testMacros(String sdkRoot) async { match = matchComplete(field, line); if (match != null && currentClass != null) { + if (fields[currentClass] == null) { + hasMissingFields = true; + print("$currentClass is missing entirely."); + continue; + } if (!fields[currentClass].contains(match.group(2))) { hasMissingFields = true; print("$currentClass is missing ${match.group(2)}."); diff --git a/runtime/vm/raw_object_fields.cc b/runtime/vm/raw_object_fields.cc index 5728dadc6ec..c4aac7c0ac0 100644 --- a/runtime/vm/raw_object_fields.cc +++ b/runtime/vm/raw_object_fields.cc @@ -109,6 +109,7 @@ namespace dart { F(Bytecode, function_) \ F(Bytecode, exception_handlers_) \ F(Bytecode, pc_descriptors_) \ + F(Bytecode, closures_) \ F(ExceptionHandlers, handled_types_data_) \ F(Context, parent_) \ F(SingleTargetCache, target_) \ @@ -191,7 +192,13 @@ namespace dart { F(Pointer, type_arguments_) \ F(Pointer, c_memory_address_) \ F(DynamicLibrary, handle_) \ - F(FfiTrampolineData, signature_type_) + F(FfiTrampolineData, signature_type_) \ + F(FfiTrampolineData, c_signature_) \ + F(FfiTrampolineData, callback_target_) \ + F(TypedDataBase, data_) \ + F(TypedDataBase, length_) \ + F(TypedDataView, typed_data_) \ + F(TypedDataView, offset_in_bytes_) OffsetsTable::OffsetsTable(Zone* zone) : cached_offsets_(zone) { for (intptr_t i = 0; offsets_table[i].class_id != -1; ++i) { diff --git a/runtime/vm/v8_snapshot_writer.cc b/runtime/vm/v8_snapshot_writer.cc index 2ddd3e0db8b..b8ef3286e59 100644 --- a/runtime/vm/v8_snapshot_writer.cc +++ b/runtime/vm/v8_snapshot_writer.cc @@ -23,7 +23,7 @@ V8SnapshotProfileWriter::V8SnapshotProfileWriter(Zone* zone) node_types_(zone_), edge_types_(zone_), strings_(zone), - roots_(zone_, 100) { + roots_(zone_) { node_types_.Insert({"Unknown", kUnknown}); node_types_.Insert({"ArtificialRoot", kArtificialRoot}); @@ -128,7 +128,7 @@ void V8SnapshotProfileWriter::WriteNodeInfo(JSONWriter* writer, // The artificial root has 'nullptr' edges, it actually points to all the // roots. writer->PrintValue64(info.edges != nullptr ? info.edges->length() - : roots_.length()); + : roots_.Size()); writer->PrintNewline(); } @@ -142,7 +142,15 @@ void V8SnapshotProfileWriter::WriteEdgeInfo(JSONWriter* writer, void V8SnapshotProfileWriter::AddRoot(ObjectId object_id) { EnsureId(object_id); - roots_.Add(object_id); + // HeapSnapshotWorker.HeapSnapshot.calculateDistances (from HeapSnapshot.js) + // assumes that the root does not have more than one edge to any other node + // (most likely an oversight). + if (roots_.HasKey(object_id)) return; + + ObjectIdToNodeInfoTraits::Pair pair; + pair.key = object_id; + pair.value = NodeInfo{0, 0, object_id, 0, nullptr, 0}; + roots_.Insert(pair); } void V8SnapshotProfileWriter::WriteStringsTable( @@ -210,7 +218,7 @@ void V8SnapshotProfileWriter::Write(JSONWriter* writer) { writer->PrintProperty64("node_count", nodes_.Size() + 1 /* artificial root */); - writer->PrintProperty64("edge_count", edge_count_ + roots_.length()); + writer->PrintProperty64("edge_count", edge_count_ + roots_.Size()); } writer->CloseObject(); @@ -234,13 +242,14 @@ void V8SnapshotProfileWriter::Write(JSONWriter* writer) { writer->OpenArray("edges"); // Write references from the artificial root to the actual roots. - for (intptr_t i = 0; i < roots_.length(); ++i) { - WriteEdgeInfo(writer, {kElement, i, roots_[i]}); + ObjectIdToNodeInfoTraits::Pair* entry = nullptr; + auto roots_it = roots_.GetIterator(); + for (int i = 0; (entry = roots_it.Next()) != nullptr; ++i) { + WriteEdgeInfo(writer, {kElement, i, entry->key}); } - ObjectIdToNodeInfoTraits::Pair* entry = nullptr; - auto it = nodes_.GetIterator(); - while ((entry = it.Next()) != nullptr) { + auto nodes_it = nodes_.GetIterator(); + while ((entry = nodes_it.Next()) != nullptr) { for (intptr_t i = 0; i < entry->value.edges->length(); ++i) { WriteEdgeInfo(writer, entry->value.edges->At(i)); } diff --git a/runtime/vm/v8_snapshot_writer.h b/runtime/vm/v8_snapshot_writer.h index fa9ee85a349..63d061743b2 100644 --- a/runtime/vm/v8_snapshot_writer.h +++ b/runtime/vm/v8_snapshot_writer.h @@ -10,6 +10,7 @@ #include "platform/assert.h" #include "vm/allocation.h" #include "vm/hash_map.h" +#include "vm/hash_table.h" #include "vm/json_writer.h" #include "vm/object.h" @@ -201,7 +202,12 @@ class V8SnapshotProfileWriter : public ZoneAllocated { DirectChainedHashMap node_types_; DirectChainedHashMap edge_types_; DirectChainedHashMap strings_; - ZoneGrowableArray roots_; + + // We don't have a zone-allocated hash set, so we just re-use the type for + // nodes_ even though we don't need to access the node info (and fill it with + // dummy values). + DirectChainedHashMap roots_; + size_t edge_count_ = 0; #endif };