[vm/aot] Update serializer for V8's "snapshot profile" format
Fixes https://github.com/dart-lang/sdk/issues/37183 Addresses https://github.com/dart-lang/sdk/issues/37126 Change-Id: I1c1e516ac2bcc45e46059f4439f16aaee6d824fe Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-debug-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105302 Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
@@ -85,9 +85,37 @@ class V8SnapshotProfile extends Graph<int> {
|
||||
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<int> roots = <int>{};
|
||||
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<int> enqueued = {root};
|
||||
final dfs = <int>[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<int> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)}.");
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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<StringToIntMapTraits> node_types_;
|
||||
DirectChainedHashMap<StringToIntMapTraits> edge_types_;
|
||||
DirectChainedHashMap<StringToIntMapTraits> strings_;
|
||||
ZoneGrowableArray<ObjectId> 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<ObjectIdToNodeInfoTraits> roots_;
|
||||
|
||||
size_t edge_count_ = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user