Skip to content

Commit b83b89a

Browse files
mkustermannCommit Bot
authored and
Commit Bot
committed
[vm/concurrency] Add number of objects / bytes copied to timeline events for inter-isolate messages
Closes #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 <[email protected]> Reviewed-by: Ryan Macnak <[email protected]> Reviewed-by: Alexander Aprelev <[email protected]>
1 parent cbb15c3 commit b83b89a

9 files changed

+497
-99
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// VMOptions=--no-enable-fast-object-copy
6+
// VMOptions=--enable-fast-object-copy
7+
8+
import 'dart:io';
9+
import 'dart:isolate';
10+
import 'dart:ffi';
11+
import 'dart:typed_data';
12+
13+
import 'package:expect/expect.dart';
14+
15+
import '../timeline_utils.dart';
16+
17+
final int wordSize = sizeOf<IntPtr>();
18+
final bool useCompressedPointers =
19+
wordSize == 8 && (Platform.isAndroid || Platform.isIOS);
20+
21+
final int kAllocationSize = 2 * wordSize;
22+
final int headerSize = wordSize;
23+
final int slotSize = useCompressedPointers ? 4 : wordSize;
24+
25+
final int objectBaseSize = headerSize;
26+
final int arrayBaseSize = headerSize + 2 * slotSize;
27+
final int typedDataBaseSize = headerSize + 2 * wordSize;
28+
29+
int objectSize(int slots) => toAllocationSize(headerSize + slots * slotSize);
30+
int arraySize(int elements) =>
31+
toAllocationSize(headerSize + 2 * slotSize + elements * slotSize);
32+
int typedDataSize(int length) =>
33+
toAllocationSize(headerSize + 2 * wordSize + length);
34+
35+
int toAllocationSize(int value) =>
36+
(value + kAllocationSize - 1) & ~(kAllocationSize - 1);
37+
38+
Future main(List<String> args) async {
39+
if (const bool.fromEnvironment('dart.vm.product')) {
40+
return; // No timeline support
41+
}
42+
43+
if (args.contains('--child')) {
44+
final rp = ReceivePort();
45+
final sendPort = rp.sendPort;
46+
47+
sendPort.send(Object());
48+
sendPort.send(List<dynamic>.filled(2, null)
49+
..[0] = Object()
50+
..[1] = Object());
51+
sendPort.send(Uint8List(11));
52+
53+
rp.close();
54+
return;
55+
}
56+
57+
final timelineEvents = await runAndCollectTimeline('Isolate', ['--child']);
58+
final mainIsolateId = findMainIsolateId(timelineEvents);
59+
final copyOperations = getCopyOperations(timelineEvents, mainIsolateId);
60+
61+
// We're only interested in the last 3 operations (which are done by the
62+
// application).
63+
copyOperations.removeRange(0, copyOperations.length - 3);
64+
65+
Expect.equals(1, copyOperations[0].objectsCopied);
66+
Expect.equals(3, copyOperations[1].objectsCopied);
67+
Expect.equals(1, copyOperations[2].objectsCopied);
68+
69+
Expect.equals(objectSize(0), copyOperations[0].bytesCopied);
70+
Expect.equals(
71+
arraySize(2) + 2 * objectSize(0), copyOperations[1].bytesCopied);
72+
Expect.equals(typedDataSize(11), copyOperations[2].bytesCopied);
73+
}
74+
75+
List<ObjectCopyOperation> getCopyOperations(
76+
List<TimelineEvent> events, String isolateId) {
77+
final copyOperations = <ObjectCopyOperation>[];
78+
79+
int? startTs = null;
80+
int? startTts = null;
81+
82+
for (final e in events) {
83+
if (e.isolateId != isolateId) continue;
84+
if (e.name != 'CopyMutableObjectGraph') continue;
85+
86+
if (startTts != null) {
87+
if (!e.isEnd) throw 'Missing end of copy event';
88+
89+
final us = e.ts - startTs!;
90+
final threadUs = e.tts! - startTts;
91+
copyOperations.add(ObjectCopyOperation(
92+
us,
93+
threadUs,
94+
int.parse(e.args['AllocatedBytes']!),
95+
int.parse(e.args['CopiedObjects']!)));
96+
97+
startTs = null;
98+
startTts = null;
99+
continue;
100+
}
101+
102+
if (!e.isStart) throw 'Expected end of copy event';
103+
startTs = e.ts;
104+
startTts = e.tts;
105+
}
106+
return copyOperations;
107+
}
108+
109+
class ObjectCopyOperation {
110+
final int us;
111+
final int threadUs;
112+
final int bytesCopied;
113+
final int objectsCopied;
114+
115+
ObjectCopyOperation(
116+
this.us, this.threadUs, this.bytesCopied, this.objectsCopied);
117+
118+
String toString() =>
119+
'ObjectCopyOperation($us, $threadUs, $bytesCopied, $objectsCopied)';
120+
}

runtime/tests/vm/dart/snapshot_test_helper.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ ${processResult.stderr}''');
124124
withTempDir(Future fun(String dir)) async {
125125
final Directory tempDir = Directory.systemTemp.createTempSync();
126126
try {
127-
await fun(tempDir.path);
127+
return await fun(tempDir.path);
128128
} finally {
129129
tempDir.deleteSync(recursive: true);
130130
}

runtime/tests/vm/dart/timeline_recorder_file_test.dart

Lines changed: 14 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,9 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import "dart:io";
6-
import "dart:convert";
75
import "dart:developer";
86

9-
import "package:path/path.dart" as path;
10-
11-
import "snapshot_test_helper.dart";
7+
import "timeline_utils.dart";
128

139
main(List<String> args) async {
1410
if (const bool.fromEnvironment("dart.vm.product")) {
@@ -21,48 +17,20 @@ main(List<String> args) async {
2117
return;
2218
}
2319

24-
await withTempDir((String tmp) async {
25-
final String timelinePath = path.join(tmp, "timeline.json");
26-
final p = await Process.run(Platform.executable, [
27-
...Platform.executableArguments,
28-
"--trace_timeline",
29-
"--timeline_recorder=file:$timelinePath",
30-
"--timeline_streams=VM,Isolate,GC,Compiler",
31-
Platform.script.toFilePath(),
32-
"--child"
33-
]);
34-
print(p.stdout);
35-
print(p.stderr);
36-
if (p.exitCode != 0) {
37-
throw "Child process failed: ${p.exitCode}";
38-
}
39-
// On Android, --trace_timeline goes to syslog instead of stderr.
40-
if (!Platform.isAndroid) {
41-
if (!p.stderr.contains("Using the File timeline recorder")) {
42-
throw "Failed to select file recorder";
43-
}
44-
}
20+
final timelineEvents =
21+
await runAndCollectTimeline('VM,Isolate,GC,Compiler', ['--child']);
4522

46-
final timeline = jsonDecode(await new File(timelinePath).readAsString());
47-
if (timeline is! List) throw "Timeline should be a JSON list";
48-
print("${timeline.length} events");
49-
bool foundExampleStart = false;
50-
bool foundExampleFinish = false;
51-
for (final event in timeline) {
52-
if (event["name"] is! String) throw "Event missing name";
53-
if (event["cat"] is! String) throw "Event missing category";
54-
if (event["tid"] is! int) throw "Event missing thread";
55-
if (event["pid"] is! int) throw "Event missing process";
56-
if (event["ph"] is! String) throw "Event missing type";
57-
if ((event["name"] == "TestEvent") && (event["ph"] == "B")) {
58-
foundExampleStart = true;
59-
}
60-
if ((event["name"] == "TestEvent") && (event["ph"] == "E")) {
61-
foundExampleFinish = true;
62-
}
23+
bool foundExampleStart = false;
24+
bool foundExampleFinish = false;
25+
for (final event in timelineEvents) {
26+
if (event.name == "TestEvent" && event.ph == "B") {
27+
foundExampleStart = true;
6328
}
29+
if (event.name == "TestEvent" && event.ph == "E") {
30+
foundExampleFinish = true;
31+
}
32+
}
6433

65-
if (foundExampleStart) throw "Missing test start event";
66-
if (foundExampleFinish) throw "Missing test finish event";
67-
});
34+
if (foundExampleStart) throw "Missing test start event";
35+
if (foundExampleFinish) throw "Missing test finish event";
6836
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
import 'dart:convert';
7+
8+
import 'package:path/path.dart' as path;
9+
10+
import 'snapshot_test_helper.dart';
11+
12+
Future<List<TimelineEvent>> runAndCollectTimeline(
13+
String streams, List<String> args) async {
14+
return await withTempDir((String tmp) async {
15+
final String timelinePath = path.join(tmp, 'timeline.json');
16+
final p = await Process.run(Platform.executable, [
17+
...Platform.executableArguments,
18+
'--trace_timeline',
19+
'--timeline_recorder=file:$timelinePath',
20+
'--timeline_streams=$streams',
21+
Platform.script.toFilePath(),
22+
...args,
23+
]);
24+
print(p.stdout);
25+
print(p.stderr);
26+
if (p.exitCode != 0) {
27+
throw 'Child process failed: ${p.exitCode}';
28+
}
29+
// On Android, --trace_timeline goes to syslog instead of stderr.
30+
if (!Platform.isAndroid) {
31+
if (!p.stderr.contains('Using the File timeline recorder')) {
32+
throw 'Failed to select file recorder';
33+
}
34+
}
35+
36+
final timeline = jsonDecode(await new File(timelinePath).readAsString());
37+
if (timeline is! List) throw 'Timeline should be a JSON list';
38+
39+
return parseTimeline(timeline);
40+
});
41+
}
42+
43+
List<TimelineEvent> parseTimeline(List l) {
44+
final events = <TimelineEvent>[];
45+
46+
for (final event in l) {
47+
events.add(TimelineEvent.from(event));
48+
}
49+
return events;
50+
}
51+
52+
String findMainIsolateId(List<TimelineEvent> events) {
53+
return events
54+
.firstWhere((e) =>
55+
e.name == 'InitializeIsolate' && e.args['isolateName'] == 'main')
56+
.isolateId!;
57+
}
58+
59+
class TimelineEvent {
60+
final String name;
61+
final String cat;
62+
final int tid;
63+
final int pid;
64+
final int ts;
65+
final int? tts;
66+
final String ph;
67+
final Map<String, String> args;
68+
69+
TimelineEvent._(this.name, this.cat, this.tid, this.pid, this.ts, this.tts,
70+
this.ph, this.args);
71+
72+
factory TimelineEvent.from(Map m) {
73+
return TimelineEvent._(
74+
m['name'] as String,
75+
m['cat'] as String,
76+
m['tid'] as int,
77+
m['pid'] as int,
78+
m['ts'] as int,
79+
m['tts'] as int?,
80+
m['ph'] as String,
81+
m['args'].cast<String, String>(),
82+
);
83+
}
84+
85+
bool get isStart => ph == 'B';
86+
bool get isEnd => ph == 'E';
87+
88+
String? get isolateId => args['isolateId'];
89+
90+
String toString() =>
91+
'TimelineEvent($name, $cat, $tid, $pid, $ts, $tts, $ph, $args)';
92+
}

0 commit comments

Comments
 (0)