| // 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)'; |
| } |