From d9048340ba899dc00d2800ab311d79d23a73063f Mon Sep 17 00:00:00 2001 From: Ryan Macnak Date: Tue, 17 Aug 2021 16:49:32 +0000 Subject: [PATCH] [vm, service] Include each isolate a separate synthetic object in heap snapshots. Fix labels for sentinel, transition_sentinel, ImmutableArray, LinkedHashMap and LinkedHashSet. Display unlimited children when look at successors, as the worst case is the size of the field table. For predecessors and dominators, the worst case is the whole heap. TEST=ci Change-Id: I6fcfdfb0833d58c9ac4f586b823244d817aeba27 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/209841 Commit-Queue: Ryan Macnak Reviewed-by: Alexander Aprelev --- .../lib/src/elements/heap_snapshot.dart | 2 +- .../observatory/lib/src/service/object.dart | 8 +- .../object_graph_isolate_group_test.dart | 70 ++++++++ .../tests/service/object_graph_vm_test.dart | 8 +- .../lib/src/elements/heap_snapshot.dart | 2 +- .../observatory_2/lib/src/service/object.dart | 4 - .../object_graph_isolate_group_test.dart | 72 ++++++++ .../tests/service_2/object_graph_vm_test.dart | 8 +- runtime/vm/isolate.cc | 24 ++- runtime/vm/isolate.h | 1 + runtime/vm/object_graph.cc | 160 +++++++++++++++--- runtime/vm/raw_object_fields.cc | 20 ++- 12 files changed, 325 insertions(+), 54 deletions(-) create mode 100644 runtime/observatory/tests/service/object_graph_isolate_group_test.dart create mode 100644 runtime/observatory_2/tests/service_2/object_graph_isolate_group_test.dart diff --git a/runtime/observatory/lib/src/elements/heap_snapshot.dart b/runtime/observatory/lib/src/elements/heap_snapshot.dart index 1ddb59eac51..e83ec9ea5bd 100644 --- a/runtime/observatory/lib/src/elements/heap_snapshot.dart +++ b/runtime/observatory/lib/src/elements/heap_snapshot.dart @@ -1197,7 +1197,7 @@ class HeapSnapshotElement extends CustomElement implements Renderable { static Iterable _getChildrenSuccessor(nodeDynamic) { SnapshotObject node = nodeDynamic; - return node.successors.take(kMaxChildren).toList(); + return node.successors.toList(); } static Iterable _getChildrenPredecessor(nodeDynamic) { diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart index 7d83c714e73..df4a4cd8fe4 100644 --- a/runtime/observatory/lib/src/service/object.dart +++ b/runtime/observatory/lib/src/service/object.dart @@ -676,8 +676,6 @@ abstract class VM extends ServiceObjectOwner implements M.VM { String targetCPU = 'unknown'; String embedder = 'unknown'; int architectureBits = 0; - bool assertsEnabled = false; - bool typeChecksEnabled = false; int nativeZoneMemoryUsage = 0; int pid = 0; int mallocUsed = 0; @@ -1050,8 +1048,6 @@ abstract class VM extends ServiceObjectOwner implements M.VM { maxRSS = map['_maxRSS']; currentRSS = map['_currentRSS']; profileVM = map['_profilerMode'] == 'VM'; - assertsEnabled = map['_assertsEnabled']; - typeChecksEnabled = map['_typeChecksEnabled']; _removeDeadIsolates([ ...map['isolates'], ...map['systemIsolates'], @@ -1550,7 +1546,7 @@ class Isolate extends ServiceObjectOwner implements M.Isolate { // There are sometimes isolate refs in ServiceEvents. return vm.getFromMap(map); } - String mapId = map['id']; + String? mapId = map['id']; var obj = (mapId != null) ? _cache[mapId] : null; if (obj != null) { obj.updateFromServiceMap(map); @@ -1559,7 +1555,7 @@ class Isolate extends ServiceObjectOwner implements M.Isolate { // Build the object from the map directly. obj = ServiceObject._fromMap(this, map); if ((obj != null) && obj.canCache) { - _cache[mapId] = obj; + _cache[mapId!] = obj; } return obj; } diff --git a/runtime/observatory/tests/service/object_graph_isolate_group_test.dart b/runtime/observatory/tests/service/object_graph_isolate_group_test.dart new file mode 100644 index 00000000000..d325f755a0f --- /dev/null +++ b/runtime/observatory/tests/service/object_graph_isolate_group_test.dart @@ -0,0 +1,70 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// VMOptions=--enable_isolate_groups + +import 'dart:isolate' as isolate; +import 'package:observatory/object_graph.dart'; +import 'package:observatory/service_io.dart'; +import 'package:test/test.dart'; +import 'test_helper.dart'; + +// Make sure these fields are not removed by the tree shaker. +@pragma("vm:entry-point") +dynamic bigGlobal; + +child(message) { + var bigString = message[0] as String; + var replyPort = message[1] as isolate.SendPort; + bigGlobal = bigString; + replyPort.send(null); + new isolate.RawReceivePort(); // Keep child alive. +} + +void script() { + var bigString = "x" * (1 << 20); + var port; + for (var i = 0; i < 2; i++) { + port = new isolate.RawReceivePort((_) => port.close()); + isolate.Isolate.spawn(child, [bigString, port.sendPort]); + } + bigGlobal = bigString; + print("Ready"); +} + +var tests = [ + (Isolate isolate) async { + var graph = await isolate.fetchHeapSnapshot().done; + + // We are assuming the big string is the largest in the heap, and that it + // was shared/pass-by-pointer. + List strings = graph.objects + .where((SnapshotObject obj) => obj.klass.name == "_OneByteString") + .toList(); + strings.sort((u, v) => v.shallowSize - u.shallowSize); + SnapshotObject bigString = strings[0]; + print("bigString: $bigString"); + expect(bigString.shallowSize, greaterThanOrEqualTo(1 << 20)); + + int matchingPredecessors = 0; + for (SnapshotObject predecessor in bigString.predecessors) { + print("predecessor $predecessor ${predecessor.label}"); + if (predecessor.label.contains("bigGlobal") && + predecessor.klass.name.contains("Isolate")) { + matchingPredecessors++; + } + } + + for (SnapshotObject object in graph.objects) { + if (object.klass.name.contains("Isolate")) { + print("$object / ${object.description}"); + } + } + + // Parent and two children. Seeing all 3 means we visited all the field tables. + expect(matchingPredecessors, equals(3)); + } +]; + +main(args) => runIsolateTests(args, tests, testeeBefore: script); diff --git a/runtime/observatory/tests/service/object_graph_vm_test.dart b/runtime/observatory/tests/service/object_graph_vm_test.dart index a4bf3fec15f..8a8e7ec81fd 100644 --- a/runtime/observatory/tests/service/object_graph_vm_test.dart +++ b/runtime/observatory/tests/service/object_graph_vm_test.dart @@ -90,8 +90,8 @@ var tests = [ int internalSum = 0; int externalSum = 0; for (SnapshotObject instance in klass.instances) { - if (instance == graph.root) { - // The root may have 0 self size. + if (instance == graph.root || instance.klass.name.contains("Isolate")) { + // The root and fake root subdivisions have 0 self size. expect(instance.internalSize, greaterThanOrEqualTo(0)); expect(instance.externalSize, greaterThanOrEqualTo(0)); expect(instance.shallowSize, greaterThanOrEqualTo(0)); @@ -122,8 +122,8 @@ var tests = [ int internalSum = 0; int externalSum = 0; for (SnapshotObject instance in graph.objects) { - if (instance == graph.root) { - // The root may have 0 self size. + if (instance == graph.root || instance.klass.name.contains("Isolate")) { + // The root and fake root subdivisions have 0 self size. expect(instance.internalSize, greaterThanOrEqualTo(0)); expect(instance.externalSize, greaterThanOrEqualTo(0)); expect(instance.shallowSize, greaterThanOrEqualTo(0)); diff --git a/runtime/observatory_2/lib/src/elements/heap_snapshot.dart b/runtime/observatory_2/lib/src/elements/heap_snapshot.dart index bfe8412921a..4fde1305da9 100644 --- a/runtime/observatory_2/lib/src/elements/heap_snapshot.dart +++ b/runtime/observatory_2/lib/src/elements/heap_snapshot.dart @@ -1196,7 +1196,7 @@ class HeapSnapshotElement extends CustomElement implements Renderable { static Iterable _getChildrenSuccessor(nodeDynamic) { SnapshotObject node = nodeDynamic; - return node.successors.take(kMaxChildren).toList(); + return node.successors.toList(); } static Iterable _getChildrenPredecessor(nodeDynamic) { diff --git a/runtime/observatory_2/lib/src/service/object.dart b/runtime/observatory_2/lib/src/service/object.dart index 14d72c27214..5e220dcf4fb 100644 --- a/runtime/observatory_2/lib/src/service/object.dart +++ b/runtime/observatory_2/lib/src/service/object.dart @@ -677,8 +677,6 @@ abstract class VM extends ServiceObjectOwner implements M.VM { String targetCPU; String embedder; int architectureBits; - bool assertsEnabled = false; - bool typeChecksEnabled = false; int nativeZoneMemoryUsage = 0; int pid = 0; int mallocUsed = 0; @@ -1053,8 +1051,6 @@ abstract class VM extends ServiceObjectOwner implements M.VM { maxRSS = map['_maxRSS']; currentRSS = map['_currentRSS']; profileVM = map['_profilerMode'] == 'VM'; - assertsEnabled = map['_assertsEnabled']; - typeChecksEnabled = map['_typeChecksEnabled']; _removeDeadIsolates([ ...map['isolates'], ...map['systemIsolates'], diff --git a/runtime/observatory_2/tests/service_2/object_graph_isolate_group_test.dart b/runtime/observatory_2/tests/service_2/object_graph_isolate_group_test.dart new file mode 100644 index 00000000000..b9ddf82e051 --- /dev/null +++ b/runtime/observatory_2/tests/service_2/object_graph_isolate_group_test.dart @@ -0,0 +1,72 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// VMOptions=--enable_isolate_groups + +// @dart = 2.7 + +import 'dart:isolate' as isolate; +import 'package:observatory_2/object_graph.dart'; +import 'package:observatory_2/service_io.dart'; +import 'package:test/test.dart'; +import 'test_helper.dart'; + +// Make sure these fields are not removed by the tree shaker. +@pragma("vm:entry-point") +dynamic bigGlobal; + +child(message) { + var bigString = message[0] as String; + var replyPort = message[1] as isolate.SendPort; + bigGlobal = bigString; + replyPort.send(null); + new isolate.RawReceivePort(); // Keep child alive. +} + +void script() { + var bigString = "x" * (1 << 20); + var port; + for (var i = 0; i < 2; i++) { + port = new isolate.RawReceivePort((_) => port.close()); + isolate.Isolate.spawn(child, [bigString, port.sendPort]); + } + bigGlobal = bigString; + print("Ready"); +} + +var tests = [ + (Isolate isolate) async { + var graph = await isolate.fetchHeapSnapshot().done; + + // We are assuming the big string is the largest in the heap, and that it + // was shared/pass-by-pointer. + List strings = graph.objects + .where((SnapshotObject obj) => obj.klass.name == "_OneByteString") + .toList(); + strings.sort((u, v) => v.shallowSize - u.shallowSize); + SnapshotObject bigString = strings[0]; + print("bigString: $bigString"); + expect(bigString.shallowSize, greaterThanOrEqualTo(1 << 20)); + + int matchingPredecessors = 0; + for (SnapshotObject predecessor in bigString.predecessors) { + print("predecessor $predecessor ${predecessor.label}"); + if (predecessor.label.contains("bigGlobal") && + predecessor.klass.name.contains("Isolate")) { + matchingPredecessors++; + } + } + + for (SnapshotObject object in graph.objects) { + if (object.klass.name.contains("Isolate")) { + print("$object / ${object.description}"); + } + } + + // Parent and two children. Seeing all 3 means we visited all the field tables. + expect(matchingPredecessors, equals(3)); + } +]; + +main(args) => runIsolateTests(args, tests, testeeBefore: script); diff --git a/runtime/observatory_2/tests/service_2/object_graph_vm_test.dart b/runtime/observatory_2/tests/service_2/object_graph_vm_test.dart index d1cb655cf8c..81be62eaea1 100644 --- a/runtime/observatory_2/tests/service_2/object_graph_vm_test.dart +++ b/runtime/observatory_2/tests/service_2/object_graph_vm_test.dart @@ -90,8 +90,8 @@ var tests = [ int internalSum = 0; int externalSum = 0; for (SnapshotObject instance in klass.instances) { - if (instance == graph.root) { - // The root may have 0 self size. + if (instance == graph.root || instance.klass.name.contains("Isolate")) { + // The root and fake root subdivisions have 0 self size. expect(instance.internalSize, greaterThanOrEqualTo(0)); expect(instance.externalSize, greaterThanOrEqualTo(0)); expect(instance.shallowSize, greaterThanOrEqualTo(0)); @@ -122,8 +122,8 @@ var tests = [ int internalSum = 0; int externalSum = 0; for (SnapshotObject instance in graph.objects) { - if (instance == graph.root) { - // The root may have 0 self size. + if (instance == graph.root || instance.klass.name.contains("Isolate")) { + // The root and fake root subdivisions have 0 self size. expect(instance.internalSize, greaterThanOrEqualTo(0)); expect(instance.externalSize, greaterThanOrEqualTo(0)); expect(instance.shallowSize, greaterThanOrEqualTo(0)); diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc index 35236ef6a6c..b1967f0cac2 100644 --- a/runtime/vm/isolate.cc +++ b/runtime/vm/isolate.cc @@ -2628,16 +2628,18 @@ void Isolate::VisitObjectPointers(ObjectPointerVisitor* visitor, ValidationPolicy validate_frames) { ASSERT(visitor != nullptr); + // Visit objects in the field table. + // N.B.: The heap snapshot writer requires visiting the field table first, so + // that the pointer visitation order aligns with order of field name metadata. + if (!visitor->trace_values_through_fields()) { + field_table()->VisitObjectPointers(visitor); + } + // Visit objects in the isolate object store. if (isolate_object_store() != nullptr) { isolate_object_store()->VisitObjectPointers(visitor); } - // Visit objects in the field table. - if (!visitor->trace_values_through_fields()) { - field_table()->VisitObjectPointers(visitor); - } - visitor->clear_gc_root_type(); // Visit the objects directly referenced from the isolate structure. visitor->VisitPointer(reinterpret_cast(¤t_tag_)); @@ -2810,14 +2812,19 @@ void IsolateGroup::RunWithStoppedMutatorsCallable( void IsolateGroup::VisitObjectPointers(ObjectPointerVisitor* visitor, ValidationPolicy validate_frames) { + VisitSharedPointers(visitor); + for (Isolate* isolate : isolates_) { + isolate->VisitObjectPointers(visitor, validate_frames); + } + VisitStackPointers(visitor, validate_frames); +} + +void IsolateGroup::VisitSharedPointers(ObjectPointerVisitor* visitor) { // if class table is shared, it's stored on isolate group if (class_table() != nullptr) { // Visit objects in the class table. class_table()->VisitObjectPointers(visitor); } - for (Isolate* isolate : isolates_) { - isolate->VisitObjectPointers(visitor, validate_frames); - } api_state()->VisitObjectPointersUnlocked(visitor); // Visit objects in the object store. if (object_store() != nullptr) { @@ -2825,7 +2832,6 @@ void IsolateGroup::VisitObjectPointers(ObjectPointerVisitor* visitor, } visitor->VisitPointer(reinterpret_cast(&saved_unlinked_calls_)); initial_field_table()->VisitObjectPointers(visitor); - VisitStackPointers(visitor, validate_frames); // Visit the boxed_field_list_. // 'boxed_field_list_' access via mutator and background compilation threads diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h index d7c8e5d4078..8b4f88b1ed7 100644 --- a/runtime/vm/isolate.h +++ b/runtime/vm/isolate.h @@ -713,6 +713,7 @@ class IsolateGroup : public IntrusiveDListEntry { // running, and the visitor must not allocate. void VisitObjectPointers(ObjectPointerVisitor* visitor, ValidationPolicy validate_frames); + void VisitSharedPointers(ObjectPointerVisitor* visitor); void VisitStackPointers(ObjectPointerVisitor* visitor, ValidationPolicy validate_frames); void VisitObjectIdRingPointers(ObjectPointerVisitor* visitor); diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc index 40e55a83f8c..749c0f3836a 100644 --- a/runtime/vm/object_graph.cc +++ b/runtime/vm/object_graph.cc @@ -812,8 +812,6 @@ class Pass1Visitor : public ObjectVisitor, HandleVisitor(Thread::Current()), writer_(writer) {} - virtual bool trace_values_through_fields() const { return true; } - void VisitObject(ObjectPtr obj) { if (obj->IsPseudoObject()) return; @@ -865,6 +863,13 @@ enum NonReferenceDataTags { static const intptr_t kMaxStringElements = 128; +enum ExtraCids { + kRootExtraCid = 1, // 1-origin + kIsolateExtraCid = 2, + + kNumExtraCids = 2, +}; + class Pass2Visitor : public ObjectVisitor, public ObjectPointerVisitor, public HandleVisitor { @@ -876,13 +881,11 @@ class Pass2Visitor : public ObjectVisitor, isolate_group_(thread()->isolate_group()), writer_(writer) {} - virtual bool trace_values_through_fields() const { return true; } - void VisitObject(ObjectPtr obj) { if (obj->IsPseudoObject()) return; intptr_t cid = obj->GetClassId(); - writer_->WriteUnsigned(cid); + writer_->WriteUnsigned(cid + kNumExtraCids); writer_->WriteUnsigned(discount_sizes_ ? 0 : obj->untag()->HeapSize()); if (cid == kNullCid) { @@ -891,6 +894,16 @@ class Pass2Visitor : public ObjectVisitor, writer_->WriteUnsigned(kBoolData); writer_->WriteUnsigned( static_cast(static_cast(obj)->untag()->value_)); + } else if (cid == kSentinelCid) { + if (obj == Object::sentinel().ptr()) { + writer_->WriteUnsigned(kNameData); + writer_->WriteUtf8("uninitialized"); + } else if (obj == Object::transition_sentinel().ptr()) { + writer_->WriteUnsigned(kNameData); + writer_->WriteUtf8("initializing"); + } else { + writer_->WriteUnsigned(kNoData); + } } else if (cid == kSmiCid) { UNREACHABLE(); } else if (cid == kMintCid) { @@ -1073,6 +1086,16 @@ class Pass2Visitor : public ObjectVisitor, } } + void CountExtraRefs(intptr_t count) { + ASSERT(!writing_); + counted_ += count; + } + void WriteExtraRef(intptr_t oid) { + ASSERT(writing_); + written_++; + writer_->WriteUnsigned(oid); + } + private: IsolateGroup* isolate_group_; HeapSnapshotWriter* const writer_; @@ -1105,6 +1128,36 @@ class Pass3Visitor : public ObjectVisitor { DISALLOW_COPY_AND_ASSIGN(Pass3Visitor); }; +class CollectStaticFieldNames : public ObjectVisitor { + public: + CollectStaticFieldNames(intptr_t field_table_size, + const char** field_table_names) + : ObjectVisitor(), + field_table_size_(field_table_size), + field_table_names_(field_table_names), + field_(Field::Handle()) {} + + void VisitObject(ObjectPtr obj) { + if (obj->IsField()) { + field_ ^= obj; + if (field_.is_static()) { + intptr_t id = field_.field_id(); + if (id > 0) { + ASSERT(id < field_table_size_); + field_table_names_[id] = field_.UserVisibleNameCString(); + } + } + } + } + + private: + intptr_t field_table_size_; + const char** field_table_names_; + Field& field_; + + DISALLOW_COPY_AND_ASSIGN(CollectStaticFieldNames); +}; + void HeapSnapshotWriter::Write() { HeapIterationScope iteration(thread()); @@ -1134,7 +1187,46 @@ void HeapSnapshotWriter::Write() { Array& fields = Array::Handle(); Field& field = Field::Handle(); - WriteUnsigned(class_count_); + intptr_t field_table_size = isolate()->field_table()->NumFieldIds(); + const char** field_table_names = + thread()->zone()->Alloc(field_table_size); + for (intptr_t i = 0; i < field_table_size; i++) { + field_table_names[i] = nullptr; + } + { + CollectStaticFieldNames visitor(field_table_size, field_table_names); + iteration.IterateObjects(&visitor); + } + + WriteUnsigned(class_count_ + kNumExtraCids); + { + ASSERT(kRootExtraCid == 1); + WriteUnsigned(0); // Flags + WriteUtf8("Root"); // Name + WriteUtf8(""); // Library name + WriteUtf8(""); // Library uri + WriteUtf8(""); // Reserved + WriteUnsigned(0); // Field count + } + { + ASSERT(kIsolateExtraCid == 2); + WriteUnsigned(0); // Flags + WriteUtf8("Isolate"); // Name + WriteUtf8(""); // Library name + WriteUtf8(""); // Library uri + WriteUtf8(""); // Reserved + + WriteUnsigned(field_table_size); // Field count + for (intptr_t i = 0; i < field_table_size; i++) { + intptr_t flags = 1; // Strong. + WriteUnsigned(flags); + WriteUnsigned(i); // Index. + const char* name = field_table_names[i]; + WriteUtf8(name == nullptr ? "" : name); + WriteUtf8(""); // Reserved + } + } + ASSERT(kNumExtraCids == 2); for (intptr_t cid = 1; cid <= class_count_; cid++) { if (!class_table->HasValidClassAt(cid)) { WriteUnsigned(0); // Flags @@ -1227,13 +1319,22 @@ void HeapSnapshotWriter::Write() { SetupCountingPages(); + intptr_t num_isolates = 0; { Pass1Visitor visitor(this); - // Root "object". + // Root "objects". ++object_count_; - isolate()->VisitObjectPointers(&visitor, - ValidationPolicy::kDontValidateFrames); + isolate_group()->VisitSharedPointers(&visitor); + isolate_group()->ForEachIsolate( + [&](Isolate* isolate) { + ++object_count_; + isolate->VisitObjectPointers(&visitor, + ValidationPolicy::kDontValidateFrames); + ++num_isolates; + }, + /*at_safepoint=*/true); + CountReferences(num_isolates); // Heap objects. iteration.IterateVMIsolateObjects(&visitor); @@ -1249,16 +1350,35 @@ void HeapSnapshotWriter::Write() { WriteUnsigned(reference_count_); WriteUnsigned(object_count_); - // Root "object". - WriteUnsigned(0); // cid - WriteUnsigned(0); // shallowSize - WriteUnsigned(kNoData); - visitor.DoCount(); - isolate()->VisitObjectPointers(&visitor, - ValidationPolicy::kDontValidateFrames); - visitor.DoWrite(); - isolate()->VisitObjectPointers(&visitor, - ValidationPolicy::kDontValidateFrames); + // Root "objects". + { + WriteUnsigned(kRootExtraCid); + WriteUnsigned(0); // shallowSize + WriteUnsigned(kNoData); + visitor.DoCount(); + isolate_group()->VisitSharedPointers(&visitor); + visitor.CountExtraRefs(num_isolates); + visitor.DoWrite(); + isolate_group()->VisitSharedPointers(&visitor); + for (intptr_t i = 0; i < num_isolates; i++) { + visitor.WriteExtraRef(i + 2); // 0 = sentinel, 1 = root, 2+ = isolates + } + } + isolate_group()->ForEachIsolate( + [&](Isolate* isolate) { + WriteUnsigned(kIsolateExtraCid); + WriteUnsigned(0); // shallowSize + WriteUnsigned(kNameData); + WriteUtf8( + OS::SCreate(thread()->zone(), "%" Pd64, isolate->main_port())); + visitor.DoCount(); + isolate->VisitObjectPointers(&visitor, + ValidationPolicy::kDontValidateFrames); + visitor.DoWrite(); + isolate->VisitObjectPointers(&visitor, + ValidationPolicy::kDontValidateFrames); + }, + /*at_safepoint=*/true); // Heap objects. visitor.set_discount_sizes(true); @@ -1277,6 +1397,8 @@ void HeapSnapshotWriter::Write() { // Handle root object. WriteUnsigned(0); + isolate_group()->ForEachIsolate([&](Isolate* isolate) { WriteUnsigned(0); }, + /*at_safepoint=*/true); // Handle visit rest of the objects. iteration.IterateVMIsolateObjects(&visitor); diff --git a/runtime/vm/raw_object_fields.cc b/runtime/vm/raw_object_fields.cc index 0c64af6e50b..186f5107a5a 100644 --- a/runtime/vm/raw_object_fields.cc +++ b/runtime/vm/raw_object_fields.cc @@ -156,15 +156,23 @@ namespace dart { F(String, length_) \ F(Array, type_arguments_) \ F(Array, length_) \ + F(ImmutableArray, type_arguments_) \ + F(ImmutableArray, length_) \ F(GrowableObjectArray, type_arguments_) \ F(GrowableObjectArray, length_) \ F(GrowableObjectArray, data_) \ - F(LinkedHashBase, type_arguments_) \ - F(LinkedHashBase, index_) \ - F(LinkedHashBase, hash_mask_) \ - F(LinkedHashBase, data_) \ - F(LinkedHashBase, used_data_) \ - F(LinkedHashBase, deleted_keys_) \ + F(LinkedHashMap, type_arguments_) \ + F(LinkedHashMap, index_) \ + F(LinkedHashMap, hash_mask_) \ + F(LinkedHashMap, data_) \ + F(LinkedHashMap, used_data_) \ + F(LinkedHashSet, deleted_keys_) \ + F(LinkedHashSet, type_arguments_) \ + F(LinkedHashSet, index_) \ + F(LinkedHashSet, hash_mask_) \ + F(LinkedHashSet, data_) \ + F(LinkedHashSet, used_data_) \ + F(LinkedHashSet, deleted_keys_) \ F(TypedData, length_) \ F(ExternalTypedData, length_) \ F(ReceivePort, send_port_) \