[vm/concurrency] Add number of objects / bytes copied to timeline events for inter-isolate messages
Closes https://github.com/dart-lang/sdk/issues/48591
TEST=vm/dart{,_2}/isolates/fast_object_copy_timeline_test
Change-Id: I1de3a6f0d8a31450e45f689e0d67358285204a71
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/245167
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
diff --git a/runtime/tests/vm/dart/isolates/fast_object_copy_timeline_test.dart b/runtime/tests/vm/dart/isolates/fast_object_copy_timeline_test.dart
new file mode 100644
index 0000000..72b4932
--- /dev/null
+++ b/runtime/tests/vm/dart/isolates/fast_object_copy_timeline_test.dart
@@ -0,0 +1,120 @@
+// Copyright (c) 2022, 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=--no-enable-fast-object-copy
+// VMOptions=--enable-fast-object-copy
+
+import 'dart:io';
+import 'dart:isolate';
+import 'dart:ffi';
+import 'dart:typed_data';
+
+import 'package:expect/expect.dart';
+
+import '../timeline_utils.dart';
+
+final int wordSize = sizeOf<IntPtr>();
+final bool useCompressedPointers =
+ wordSize == 8 && (Platform.isAndroid || Platform.isIOS);
+
+final int kAllocationSize = 2 * wordSize;
+final int headerSize = wordSize;
+final int slotSize = useCompressedPointers ? 4 : wordSize;
+
+final int objectBaseSize = headerSize;
+final int arrayBaseSize = headerSize + 2 * slotSize;
+final int typedDataBaseSize = headerSize + 2 * wordSize;
+
+int objectSize(int slots) => toAllocationSize(headerSize + slots * slotSize);
+int arraySize(int elements) =>
+ toAllocationSize(headerSize + 2 * slotSize + elements * slotSize);
+int typedDataSize(int length) =>
+ toAllocationSize(headerSize + 2 * wordSize + length);
+
+int toAllocationSize(int value) =>
+ (value + kAllocationSize - 1) & ~(kAllocationSize - 1);
+
+Future main(List<String> args) async {
+ if (const bool.fromEnvironment('dart.vm.product')) {
+ return; // No timeline support
+ }
+
+ if (args.contains('--child')) {
+ final rp = ReceivePort();
+ final sendPort = rp.sendPort;
+
+ sendPort.send(Object());
+ sendPort.send(List<dynamic>.filled(2, null)
+ ..[0] = Object()
+ ..[1] = Object());
+ sendPort.send(Uint8List(11));
+
+ rp.close();
+ return;
+ }
+
+ final timelineEvents = await runAndCollectTimeline('Isolate', ['--child']);
+ final mainIsolateId = findMainIsolateId(timelineEvents);
+ final copyOperations = getCopyOperations(timelineEvents, mainIsolateId);
+
+ // We're only interested in the last 3 operations (which are done by the
+ // application).
+ copyOperations.removeRange(0, copyOperations.length - 3);
+
+ Expect.equals(1, copyOperations[0].objectsCopied);
+ Expect.equals(3, copyOperations[1].objectsCopied);
+ Expect.equals(1, copyOperations[2].objectsCopied);
+
+ Expect.equals(objectSize(0), copyOperations[0].bytesCopied);
+ Expect.equals(
+ arraySize(2) + 2 * objectSize(0), copyOperations[1].bytesCopied);
+ Expect.equals(typedDataSize(11), copyOperations[2].bytesCopied);
+}
+
+List<ObjectCopyOperation> getCopyOperations(
+ List<TimelineEvent> events, String isolateId) {
+ final copyOperations = <ObjectCopyOperation>[];
+
+ int? startTs = null;
+ int? startTts = null;
+
+ for (final e in events) {
+ if (e.isolateId != isolateId) continue;
+ if (e.name != 'CopyMutableObjectGraph') continue;
+
+ if (startTts != null) {
+ if (!e.isEnd) throw 'Missing end of copy event';
+
+ final us = e.ts - startTs!;
+ final threadUs = e.tts! - startTts;
+ copyOperations.add(ObjectCopyOperation(
+ us,
+ threadUs,
+ int.parse(e.args['AllocatedBytes']!),
+ int.parse(e.args['CopiedObjects']!)));
+
+ startTs = null;
+ startTts = null;
+ continue;
+ }
+
+ if (!e.isStart) throw 'Expected end of copy event';
+ startTs = e.ts;
+ startTts = e.tts;
+ }
+ return copyOperations;
+}
+
+class ObjectCopyOperation {
+ final int us;
+ final int threadUs;
+ final int bytesCopied;
+ final int objectsCopied;
+
+ ObjectCopyOperation(
+ this.us, this.threadUs, this.bytesCopied, this.objectsCopied);
+
+ String toString() =>
+ 'ObjectCopyOperation($us, $threadUs, $bytesCopied, $objectsCopied)';
+}
diff --git a/runtime/tests/vm/dart/snapshot_test_helper.dart b/runtime/tests/vm/dart/snapshot_test_helper.dart
index 80d3344..a6890dd 100644
--- a/runtime/tests/vm/dart/snapshot_test_helper.dart
+++ b/runtime/tests/vm/dart/snapshot_test_helper.dart
@@ -124,7 +124,7 @@
withTempDir(Future fun(String dir)) async {
final Directory tempDir = Directory.systemTemp.createTempSync();
try {
- await fun(tempDir.path);
+ return await fun(tempDir.path);
} finally {
tempDir.deleteSync(recursive: true);
}
diff --git a/runtime/tests/vm/dart/timeline_recorder_file_test.dart b/runtime/tests/vm/dart/timeline_recorder_file_test.dart
index 851a68a..7310754 100644
--- a/runtime/tests/vm/dart/timeline_recorder_file_test.dart
+++ b/runtime/tests/vm/dart/timeline_recorder_file_test.dart
@@ -2,13 +2,9 @@
// 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.
-import "dart:io";
-import "dart:convert";
import "dart:developer";
-import "package:path/path.dart" as path;
-
-import "snapshot_test_helper.dart";
+import "timeline_utils.dart";
main(List<String> args) async {
if (const bool.fromEnvironment("dart.vm.product")) {
@@ -21,48 +17,20 @@
return;
}
- await withTempDir((String tmp) async {
- final String timelinePath = path.join(tmp, "timeline.json");
- final p = await Process.run(Platform.executable, [
- ...Platform.executableArguments,
- "--trace_timeline",
- "--timeline_recorder=file:$timelinePath",
- "--timeline_streams=VM,Isolate,GC,Compiler",
- Platform.script.toFilePath(),
- "--child"
- ]);
- print(p.stdout);
- print(p.stderr);
- if (p.exitCode != 0) {
- throw "Child process failed: ${p.exitCode}";
- }
- // On Android, --trace_timeline goes to syslog instead of stderr.
- if (!Platform.isAndroid) {
- if (!p.stderr.contains("Using the File timeline recorder")) {
- throw "Failed to select file recorder";
- }
- }
+ final timelineEvents =
+ await runAndCollectTimeline('VM,Isolate,GC,Compiler', ['--child']);
- final timeline = jsonDecode(await new File(timelinePath).readAsString());
- if (timeline is! List) throw "Timeline should be a JSON list";
- print("${timeline.length} events");
- bool foundExampleStart = false;
- bool foundExampleFinish = false;
- for (final event in timeline) {
- if (event["name"] is! String) throw "Event missing name";
- if (event["cat"] is! String) throw "Event missing category";
- if (event["tid"] is! int) throw "Event missing thread";
- if (event["pid"] is! int) throw "Event missing process";
- if (event["ph"] is! String) throw "Event missing type";
- if ((event["name"] == "TestEvent") && (event["ph"] == "B")) {
- foundExampleStart = true;
- }
- if ((event["name"] == "TestEvent") && (event["ph"] == "E")) {
- foundExampleFinish = true;
- }
+ bool foundExampleStart = false;
+ bool foundExampleFinish = false;
+ for (final event in timelineEvents) {
+ if (event.name == "TestEvent" && event.ph == "B") {
+ foundExampleStart = true;
}
+ if (event.name == "TestEvent" && event.ph == "E") {
+ foundExampleFinish = true;
+ }
+ }
- if (foundExampleStart) throw "Missing test start event";
- if (foundExampleFinish) throw "Missing test finish event";
- });
+ if (foundExampleStart) throw "Missing test start event";
+ if (foundExampleFinish) throw "Missing test finish event";
}
diff --git a/runtime/tests/vm/dart/timeline_utils.dart b/runtime/tests/vm/dart/timeline_utils.dart
new file mode 100644
index 0000000..cf0cddc
--- /dev/null
+++ b/runtime/tests/vm/dart/timeline_utils.dart
@@ -0,0 +1,92 @@
+// Copyright (c) 2022, 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.
+
+import 'dart:io';
+import 'dart:convert';
+
+import 'package:path/path.dart' as path;
+
+import 'snapshot_test_helper.dart';
+
+Future<List<TimelineEvent>> runAndCollectTimeline(
+ String streams, List<String> args) async {
+ return await withTempDir((String tmp) async {
+ final String timelinePath = path.join(tmp, 'timeline.json');
+ final p = await Process.run(Platform.executable, [
+ ...Platform.executableArguments,
+ '--trace_timeline',
+ '--timeline_recorder=file:$timelinePath',
+ '--timeline_streams=$streams',
+ Platform.script.toFilePath(),
+ ...args,
+ ]);
+ print(p.stdout);
+ print(p.stderr);
+ if (p.exitCode != 0) {
+ throw 'Child process failed: ${p.exitCode}';
+ }
+ // On Android, --trace_timeline goes to syslog instead of stderr.
+ if (!Platform.isAndroid) {
+ if (!p.stderr.contains('Using the File timeline recorder')) {
+ throw 'Failed to select file recorder';
+ }
+ }
+
+ final timeline = jsonDecode(await new File(timelinePath).readAsString());
+ if (timeline is! List) throw 'Timeline should be a JSON list';
+
+ return parseTimeline(timeline);
+ });
+}
+
+List<TimelineEvent> parseTimeline(List l) {
+ final events = <TimelineEvent>[];
+
+ for (final event in l) {
+ events.add(TimelineEvent.from(event));
+ }
+ return events;
+}
+
+String findMainIsolateId(List<TimelineEvent> events) {
+ return events
+ .firstWhere((e) =>
+ e.name == 'InitializeIsolate' && e.args['isolateName'] == 'main')
+ .isolateId!;
+}
+
+class TimelineEvent {
+ final String name;
+ final String cat;
+ final int tid;
+ final int pid;
+ final int ts;
+ final int? tts;
+ final String ph;
+ final Map<String, String> args;
+
+ TimelineEvent._(this.name, this.cat, this.tid, this.pid, this.ts, this.tts,
+ this.ph, this.args);
+
+ factory TimelineEvent.from(Map m) {
+ return TimelineEvent._(
+ m['name'] as String,
+ m['cat'] as String,
+ m['tid'] as int,
+ m['pid'] as int,
+ m['ts'] as int,
+ m['tts'] as int?,
+ m['ph'] as String,
+ m['args'].cast<String, String>(),
+ );
+ }
+
+ bool get isStart => ph == 'B';
+ bool get isEnd => ph == 'E';
+
+ String? get isolateId => args['isolateId'];
+
+ String toString() =>
+ 'TimelineEvent($name, $cat, $tid, $pid, $ts, $tts, $ph, $args)';
+}
diff --git a/runtime/tests/vm/dart_2/isolates/fast_object_copy_timeline_test.dart b/runtime/tests/vm/dart_2/isolates/fast_object_copy_timeline_test.dart
new file mode 100644
index 0000000..9030c78
--- /dev/null
+++ b/runtime/tests/vm/dart_2/isolates/fast_object_copy_timeline_test.dart
@@ -0,0 +1,122 @@
+// Copyright (c) 2022, 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.
+
+// @dart = 2.9
+
+// VMOptions=--no-enable-fast-object-copy
+// VMOptions=--enable-fast-object-copy
+
+import 'dart:io';
+import 'dart:isolate';
+import 'dart:ffi';
+import 'dart:typed_data';
+
+import 'package:expect/expect.dart';
+
+import '../timeline_utils.dart';
+
+final int wordSize = sizeOf<IntPtr>();
+final bool useCompressedPointers =
+ wordSize == 8 && (Platform.isAndroid || Platform.isIOS);
+
+final int kAllocationSize = 2 * wordSize;
+final int headerSize = wordSize;
+final int slotSize = useCompressedPointers ? 4 : wordSize;
+
+final int objectBaseSize = headerSize;
+final int arrayBaseSize = headerSize + 2 * slotSize;
+final int typedDataBaseSize = headerSize + 2 * wordSize;
+
+int objectSize(int slots) => toAllocationSize(headerSize + slots * slotSize);
+int arraySize(int elements) =>
+ toAllocationSize(headerSize + 2 * slotSize + elements * slotSize);
+int typedDataSize(int length) =>
+ toAllocationSize(headerSize + 2 * wordSize + length);
+
+int toAllocationSize(int value) =>
+ (value + kAllocationSize - 1) & ~(kAllocationSize - 1);
+
+Future main(List<String> args) async {
+ if (const bool.fromEnvironment('dart.vm.product')) {
+ return; // No timeline support
+ }
+
+ if (args.contains('--child')) {
+ final rp = ReceivePort();
+ final sendPort = rp.sendPort;
+
+ sendPort.send(Object());
+ sendPort.send(List<dynamic>.filled(2, null)
+ ..[0] = Object()
+ ..[1] = Object());
+ sendPort.send(Uint8List(11));
+
+ rp.close();
+ return;
+ }
+
+ final timelineEvents = await runAndCollectTimeline('Isolate', ['--child']);
+ final mainIsolateId = findMainIsolateId(timelineEvents);
+ final copyOperations = getCopyOperations(timelineEvents, mainIsolateId);
+
+ // We're only interested in the last 3 operations (which are done by the
+ // application).
+ copyOperations.removeRange(0, copyOperations.length - 3);
+
+ Expect.equals(1, copyOperations[0].objectsCopied);
+ Expect.equals(3, copyOperations[1].objectsCopied);
+ Expect.equals(1, copyOperations[2].objectsCopied);
+
+ Expect.equals(objectSize(0), copyOperations[0].bytesCopied);
+ Expect.equals(
+ arraySize(2) + 2 * objectSize(0), copyOperations[1].bytesCopied);
+ Expect.equals(typedDataSize(11), copyOperations[2].bytesCopied);
+}
+
+List<ObjectCopyOperation> getCopyOperations(
+ List<TimelineEvent> events, String isolateId) {
+ final copyOperations = <ObjectCopyOperation>[];
+
+ int startTs = null;
+ int startTts = null;
+
+ for (final e in events) {
+ if (e.isolateId != isolateId) continue;
+ if (e.name != 'CopyMutableObjectGraph') continue;
+
+ if (startTts != null) {
+ if (!e.isEnd) throw 'Missing end of copy event';
+
+ final us = e.ts - startTs;
+ final threadUs = e.tts - startTts;
+ copyOperations.add(ObjectCopyOperation(
+ us,
+ threadUs,
+ int.parse(e.args['AllocatedBytes']),
+ int.parse(e.args['CopiedObjects'])));
+
+ startTs = null;
+ startTts = null;
+ continue;
+ }
+
+ if (!e.isStart) throw 'Expected end of copy event';
+ startTs = e.ts;
+ startTts = e.tts;
+ }
+ return copyOperations;
+}
+
+class ObjectCopyOperation {
+ final int us;
+ final int threadUs;
+ final int bytesCopied;
+ final int objectsCopied;
+
+ ObjectCopyOperation(
+ this.us, this.threadUs, this.bytesCopied, this.objectsCopied);
+
+ String toString() =>
+ 'ObjectCopyOperation($us, $threadUs, $bytesCopied, $objectsCopied)';
+}
diff --git a/runtime/tests/vm/dart_2/snapshot_test_helper.dart b/runtime/tests/vm/dart_2/snapshot_test_helper.dart
index b57d04d..d790c4a 100644
--- a/runtime/tests/vm/dart_2/snapshot_test_helper.dart
+++ b/runtime/tests/vm/dart_2/snapshot_test_helper.dart
@@ -126,7 +126,7 @@
withTempDir(Future fun(String dir)) async {
final Directory tempDir = Directory.systemTemp.createTempSync();
try {
- await fun(tempDir.path);
+ return await fun(tempDir.path);
} finally {
tempDir.deleteSync(recursive: true);
}
diff --git a/runtime/tests/vm/dart_2/timeline_recorder_file_test.dart b/runtime/tests/vm/dart_2/timeline_recorder_file_test.dart
index e49877c..e308ebd 100644
--- a/runtime/tests/vm/dart_2/timeline_recorder_file_test.dart
+++ b/runtime/tests/vm/dart_2/timeline_recorder_file_test.dart
@@ -4,13 +4,9 @@
// @dart = 2.9
-import "dart:io";
-import "dart:convert";
import "dart:developer";
-import "package:path/path.dart" as path;
-
-import "snapshot_test_helper.dart";
+import "timeline_utils.dart";
main(List<String> args) async {
if (const bool.fromEnvironment("dart.vm.product")) {
@@ -23,48 +19,25 @@
return;
}
- await withTempDir((String tmp) async {
- final String timelinePath = path.join(tmp, "timeline.json");
- final p = await Process.run(Platform.executable, [
- ...Platform.executableArguments,
- "--trace_timeline",
- "--timeline_recorder=file:$timelinePath",
- "--timeline_streams=VM,Isolate,GC,Compiler",
- Platform.script.toFilePath(),
- "--child"
- ]);
- print(p.stdout);
- print(p.stderr);
- if (p.exitCode != 0) {
- throw "Child process failed: ${p.exitCode}";
- }
- // On Android, --trace_timeline goes to syslog instead of stderr.
- if (!Platform.isAndroid) {
- if (!p.stderr.contains("Using the File timeline recorder")) {
- throw "Failed to select file recorder";
- }
- }
+ final timelineEvents =
+ await runAndCollectTimeline('VM,Isolate,GC,Compiler', ['--child']);
- final timeline = jsonDecode(await new File(timelinePath).readAsString());
- if (timeline is! List) throw "Timeline should be a JSON list";
- print("${timeline.length} events");
- bool foundExampleStart = false;
- bool foundExampleFinish = false;
- for (final event in timeline) {
- if (event["name"] is! String) throw "Event missing name";
- if (event["cat"] is! String) throw "Event missing category";
- if (event["tid"] is! int) throw "Event missing thread";
- if (event["pid"] is! int) throw "Event missing process";
- if (event["ph"] is! String) throw "Event missing type";
- if ((event["name"] == "TestEvent") && (event["ph"] == "B")) {
- foundExampleStart = true;
- }
- if ((event["name"] == "TestEvent") && (event["ph"] == "E")) {
- foundExampleFinish = true;
- }
+ bool foundExampleStart = false;
+ bool foundExampleFinish = false;
+ for (final event in timelineEvents) {
+ if (event.name is! String) throw "Event missing name";
+ if (event.cat is! String) throw "Event missing category";
+ if (event.tid is! int) throw "Event missing thread";
+ if (event.pid is! int) throw "Event missing process";
+ if (event.ph is! String) throw "Event missing type";
+ if (event.name == "TestEvent" && event.ph == "B") {
+ foundExampleStart = true;
}
+ if (event.name == "TestEvent" && event.ph == "E") {
+ foundExampleFinish = true;
+ }
+ }
- if (foundExampleStart) throw "Missing test start event";
- if (foundExampleFinish) throw "Missing test finish event";
- });
+ if (foundExampleStart) throw "Missing test start event";
+ if (foundExampleFinish) throw "Missing test finish event";
}
diff --git a/runtime/tests/vm/dart_2/timeline_utils.dart b/runtime/tests/vm/dart_2/timeline_utils.dart
new file mode 100644
index 0000000..3041293
--- /dev/null
+++ b/runtime/tests/vm/dart_2/timeline_utils.dart
@@ -0,0 +1,94 @@
+// Copyright (c) 2022, 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.
+
+// @dart = 2.9
+
+import 'dart:io';
+import 'dart:convert';
+
+import 'package:path/path.dart' as path;
+
+import 'snapshot_test_helper.dart';
+
+Future<List<TimelineEvent>> runAndCollectTimeline(
+ String streams, List<String> args) async {
+ return await withTempDir((String tmp) async {
+ final String timelinePath = path.join(tmp, 'timeline.json');
+ final p = await Process.run(Platform.executable, [
+ ...Platform.executableArguments,
+ '--trace_timeline',
+ '--timeline_recorder=file:$timelinePath',
+ '--timeline_streams=$streams',
+ Platform.script.toFilePath(),
+ ...args,
+ ]);
+ print(p.stdout);
+ print(p.stderr);
+ if (p.exitCode != 0) {
+ throw 'Child process failed: ${p.exitCode}';
+ }
+ // On Android, --trace_timeline goes to syslog instead of stderr.
+ if (!Platform.isAndroid) {
+ if (!p.stderr.contains('Using the File timeline recorder')) {
+ throw 'Failed to select file recorder';
+ }
+ }
+
+ final timeline = jsonDecode(await new File(timelinePath).readAsString());
+ if (timeline is! List) throw 'Timeline should be a JSON list';
+
+ return parseTimeline(timeline);
+ });
+}
+
+List<TimelineEvent> parseTimeline(List l) {
+ final events = <TimelineEvent>[];
+
+ for (final event in l) {
+ events.add(TimelineEvent.from(event));
+ }
+ return events;
+}
+
+String findMainIsolateId(List<TimelineEvent> events) {
+ return events
+ .firstWhere((e) =>
+ e.name == 'InitializeIsolate' && e.args['isolateName'] == 'main')
+ .args['isolateId'];
+}
+
+class TimelineEvent {
+ final String name;
+ final String cat;
+ final int tid;
+ final int pid;
+ final int ts;
+ final int tts;
+ final String ph;
+ final Map<String, String> args;
+
+ TimelineEvent._(this.name, this.cat, this.tid, this.pid, this.ts, this.tts,
+ this.ph, this.args);
+
+ factory TimelineEvent.from(Map m) {
+ return TimelineEvent._(
+ m['name'] as String,
+ m['cat'] as String,
+ m['tid'] as int,
+ m['pid'] as int,
+ m['ts'] as int,
+ m['tts'] as int,
+ m['ph'] as String,
+ m['args'].cast<String, String>(),
+ );
+ }
+
+ bool get isStart => ph == 'B';
+ bool get isEnd => ph == 'E';
+
+ String get isolateId => args['isolateId'];
+
+ String toString() =>
+ 'TimelineEvent($name, $cat, $tid, $pid, $ts, $tts, $ph, $args)';
+}
diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc
index 86d3c8e..43c35b3 100644
--- a/runtime/vm/object_graph_copy.cc
+++ b/runtime/vm/object_graph_copy.cc
@@ -12,6 +12,7 @@
#include "vm/object_store.h"
#include "vm/snapshot.h"
#include "vm/symbols.h"
+#include "vm/timeline.h"
#define Z zone_
@@ -422,7 +423,7 @@
return raw_from_to_[id + 1];
}
- void Insert(ObjectPtr from, ObjectPtr to) {
+ void Insert(ObjectPtr from, ObjectPtr to, intptr_t size) {
ASSERT(ForwardedObject(from) == Marker());
ASSERT(raw_from_to_.length() == raw_from_to_.length());
const auto id = raw_from_to_.length();
@@ -430,6 +431,7 @@
raw_from_to_.Resize(id + 2);
raw_from_to_[id] = from;
raw_from_to_[id + 1] = to;
+ allocated_bytes += size;
}
void AddTransferable(TransferableTypedDataPtr from,
@@ -460,6 +462,7 @@
GrowableArray<WeakPropertyPtr> raw_weak_properties_;
GrowableArray<WeakReferencePtr> raw_weak_references_;
intptr_t fill_cursor_ = 0;
+ intptr_t allocated_bytes = 0;
DISALLOW_COPY_AND_ASSIGN(FastForwardMap);
};
@@ -482,13 +485,14 @@
return from_to_[id + 1]->ptr();
}
- void Insert(ObjectPtr from, ObjectPtr to) {
+ void Insert(ObjectPtr from, ObjectPtr to, intptr_t size) {
ASSERT(ForwardedObject(from) == Marker());
const auto id = from_to_.length();
SetObjectId(from, id);
from_to_.Resize(id + 2);
from_to_[id] = &Object::Handle(Z, from);
from_to_[id + 1] = &Object::Handle(Z, to);
+ allocated_bytes += size;
}
void AddTransferable(const TransferableTypedData& from,
@@ -541,6 +545,7 @@
GrowableArray<const WeakProperty*> weak_properties_;
GrowableArray<const WeakReference*> weak_references_;
intptr_t fill_cursor_ = 0;
+ intptr_t allocated_bytes = 0;
DISALLOW_COPY_AND_ASSIGN(SlowForwardMap);
};
@@ -780,7 +785,7 @@
const uword alloc = new_space_->TryAllocate(thread_, size);
if (alloc != 0) {
ObjectPtr to(reinterpret_cast<UntaggedObject*>(alloc));
- fast_forward_map_.Insert(from, to);
+ fast_forward_map_.Insert(from, to, size);
if (IsExternalTypedDataClassId(cid)) {
SetNewSpaceTaggingWord(to, cid, header_size);
@@ -986,7 +991,7 @@
size = from.ptr().untag()->HeapSize();
}
ObjectPtr to = AllocateObject(cid, size);
- slow_forward_map_.Insert(from.ptr(), to);
+ slow_forward_map_.Insert(from.ptr(), to, size);
UpdateLengthField(cid, from.ptr(), to);
if (cid == kArrayCid && !Heap::IsAllocatableInNewSpace(size)) {
to.untag()->SetCardRememberedBitUnsynchronized();
@@ -1847,6 +1852,10 @@
return result.ptr();
}
+ intptr_t allocated_bytes() { return allocated_bytes_; }
+
+ intptr_t copied_objects() { return copied_objects_; }
+
private:
ObjectPtr CopyObjectGraphInternal(const Object& root,
const char* volatile* exception_msg) {
@@ -1885,6 +1894,11 @@
result_array.SetAt(2, fast_object_copy_.tmp_);
HandlifyExternalTypedData();
HandlifyTransferables();
+ allocated_bytes_ =
+ fast_object_copy_.fast_forward_map_.allocated_bytes;
+ copied_objects_ =
+ fast_object_copy_.fast_forward_map_.fill_cursor_ / 2 -
+ /*null_entry=*/1;
return result_array.ptr();
}
@@ -1924,6 +1938,9 @@
result_array.SetAt(0, result);
result_array.SetAt(1, slow_object_copy_.objects_to_rehash_);
result_array.SetAt(2, slow_object_copy_.expandos_to_rehash_);
+ allocated_bytes_ = slow_object_copy_.slow_forward_map_.allocated_bytes;
+ copied_objects_ =
+ slow_object_copy_.slow_forward_map_.fill_cursor_ / 2 - /*null_entry=*/1;
return result_array.ptr();
}
@@ -1940,6 +1957,7 @@
HandlifyExpandosToReHash();
HandlifyFromToObjects();
slow_forward_map.fill_cursor_ = fast_forward_map.fill_cursor_;
+ slow_forward_map.allocated_bytes = fast_forward_map.allocated_bytes;
}
void MakeUninitializedNewSpaceObjectsGCSafe() {
@@ -2029,12 +2047,23 @@
Zone* zone_;
FastObjectCopy fast_object_copy_;
SlowObjectCopy slow_object_copy_;
+ intptr_t copied_objects_ = 0;
+ intptr_t allocated_bytes_ = 0;
};
ObjectPtr CopyMutableObjectGraph(const Object& object) {
auto thread = Thread::Current();
+ TIMELINE_DURATION(thread, Isolate, "CopyMutableObjectGraph");
ObjectGraphCopier copier(thread);
- return copier.CopyObjectGraph(object);
+ ObjectPtr result = copier.CopyObjectGraph(object);
+#if defined(SUPPORT_TIMELINE)
+ if (tbes.enabled()) {
+ tbes.SetNumArguments(2);
+ tbes.FormatArgument(0, "CopiedObjects", "%" Pd, copier.copied_objects());
+ tbes.FormatArgument(1, "AllocatedBytes", "%" Pd, copier.allocated_bytes());
+ }
+#endif
+ return result;
}
} // namespace dart