[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 <rmacnak@google.com> Reviewed-by: Alexander Aprelev <aam@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
8bb5ced986
commit
d9048340ba
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 = <IsolateTest>[
|
||||
(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<SnapshotObject> 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);
|
||||
@@ -90,8 +90,8 @@ var tests = <IsolateTest>[
|
||||
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 = <IsolateTest>[
|
||||
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));
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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 = <IsolateTest>[
|
||||
(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<SnapshotObject> 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);
|
||||
@@ -90,8 +90,8 @@ var tests = <IsolateTest>[
|
||||
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 = <IsolateTest>[
|
||||
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));
|
||||
|
||||
+15
-9
@@ -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<ObjectPtr*>(¤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<ObjectPtr*>(&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
|
||||
|
||||
@@ -713,6 +713,7 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
|
||||
// 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);
|
||||
|
||||
+141
-19
@@ -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<uintptr_t>(static_cast<BoolPtr>(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<const char*>(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);
|
||||
|
||||
@@ -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_) \
|
||||
|
||||
Reference in New Issue
Block a user