blob: e5b6b562a4a46ffed6c65d5c394fee0f587a09dc [file] [log] [blame]
// 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=--verbose_debug --enable-isolate-groups
//
// Tests breakpoint pausing and resuming with many isolates running and pausing
// simultaneously.
import 'dart:async';
import 'dart:developer';
import 'dart:isolate' as dart_isolate;
import 'package:observatory_2/service_io.dart';
import 'package:test/test.dart';
import 'service_test_common.dart';
import 'test_helper.dart';
const int LINE_A = 23;
const int LINE_B = 35;
const int LINE_C = 41;
foo(args) { // LINE_A
print('${dart_isolate.Isolate.current.debugName}: $args');
final sendPort = args[0] as dart_isolate.SendPort;
final int i = args[1] as int;
sendPort.send('reply from foo: $i');
}
int nIsolates = -1;
testMain() async {
final rps = List<dart_isolate.ReceivePort>.generate(
nIsolates, (i) => dart_isolate.ReceivePort());
debugger(); // LINE_B
for (int i = 0; i < nIsolates; i++) {
await dart_isolate.Isolate.spawn(foo, [rps[i].sendPort, i],
debugName: "foo$i");
}
print(await Future.wait(rps.map((rp) => rp.first)));
debugger(); // LINE_C
}
final completerAtFoo = List<Completer>.generate(nIsolates, (_) => Completer());
int completerCount = 0;
final tests = <IsolateTest>[
hasPausedAtStart,
resumeIsolate,
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_B + 1),
(Isolate isolate) async {
// Set up a listener to wait for child isolate launch and breakpoint events.
final stream = await isolate.vm.getEventStream(VM.kDebugStream);
var subscription;
subscription = stream.listen((ServiceEvent event) async {
switch (event.kind) {
case ServiceEvent.kPauseStart:
final childIsolate = event.isolate;
await childIsolate.reload();
for (Library lib in childIsolate.libraries) {
await lib.load();
if (lib.uri
.endsWith('break_on_function_many_child_isolates_test.dart')) {
final foo = lib.functions.singleWhere((f) => f.name == 'foo');
final bpt = await childIsolate.addBreakpointAtEntry(foo);
expect(bpt is Breakpoint, isTrue);
break;
}
}
childIsolate.resume();
break;
case ServiceEvent.kPauseBreakpoint:
final name = event.isolate.name;
if (!name.startsWith('foo')) {
break;
}
final childIsolate = event.isolate;
final ndx = int.parse(name.substring('foo'.length));
final stack = await childIsolate.getStack();
final top = stack['frames'][0];
final script = await top.location.script.load() as Script;
expect(script.tokenToLine(top.location.tokenPos), equals(LINE_A));
childIsolate.resume();
if ((++completerCount) == nIsolates) {
subscription.cancel();
}
completerAtFoo[ndx].complete();
break;
}
});
},
resumeIsolate,
(Isolate isolate) async {
await Future.wait(completerAtFoo.map((c) => c.future));
},
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_C + 1),
resumeIsolate,
];
Future runIsolateBreakpointPauseTest(args, nIsolates_) {
nIsolates = nIsolates_;
return runIsolateTests(args, tests,
testeeConcurrent: testMain, pause_on_start: true);
}
main(args) async {
await runIsolateBreakpointPauseTest(args, /*nIsolates=*/ 30);
}