blob: f3b44b1e586602274f4dc9c620e2aaf53473e69d [file] [log] [blame]
// 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 ||
Platform.executable.contains('64C'));
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>[];
TimelineEvent? start = null;
for (final e in events) {
if (e.isolateId != isolateId) continue;
if (e.name != 'CopyMutableObjectGraph') continue;
if (start != null) {
if (!e.isEnd) throw 'Missing end of copy event';
final us = e.ts - start.ts;
final threadUs = e.tts != null ? (e.tts! - start.tts!) : 0;
copyOperations.add(ObjectCopyOperation(
us,
threadUs,
int.parse(e.args['AllocatedBytes']!),
int.parse(e.args['CopiedObjects']!)));
start = null;
continue;
}
if (!e.isStart) throw 'Expected end of copy event';
start = e;
}
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)';
}