diff --git a/runtime/observatory/lib/src/app/application.dart b/runtime/observatory/lib/src/app/application.dart index ff45043c2da..7b2217a683d 100644 --- a/runtime/observatory/lib/src/app/application.dart +++ b/runtime/observatory/lib/src/app/application.dart @@ -89,6 +89,7 @@ class ObservatoryApplication extends Observable { void _onEvent(ServiceEvent event) { switch(event.kind) { case ServiceEvent.kIsolateStart: + case ServiceEvent.kIsolateRunnable: case ServiceEvent.kIsolateUpdate: case ServiceEvent.kBreakpointAdded: case ServiceEvent.kBreakpointResolved: diff --git a/runtime/observatory/lib/src/elements/debugger.dart b/runtime/observatory/lib/src/elements/debugger.dart index 78d9cf5deac..9b3bced72e5 100644 --- a/runtime/observatory/lib/src/elements/debugger.dart +++ b/runtime/observatory/lib/src/elements/debugger.dart @@ -1482,10 +1482,11 @@ class ObservatoryDebugger extends Debugger { } break; - case ServiceEvent.kIsolateStart: + case ServiceEvent.kIsolateRunnable: case ServiceEvent.kGraph: case ServiceEvent.kGC: case ServiceEvent.kInspect: + // Ignore. break; case ServiceEvent.kLogging: diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart index a8d5f4ff26a..d2dade6a58f 100644 --- a/runtime/observatory/lib/src/service/object.dart +++ b/runtime/observatory/lib/src/service/object.dart @@ -1334,6 +1334,7 @@ class Isolate extends ServiceObjectOwner with Coverage { void _onEvent(ServiceEvent event) { switch(event.kind) { case ServiceEvent.kIsolateStart: + case ServiceEvent.kIsolateRunnable: case ServiceEvent.kIsolateExit: case ServiceEvent.kInspect: // Handled elsewhere. @@ -1706,6 +1707,7 @@ Level _findLogLevel(int value) { class ServiceEvent extends ServiceObject { /// The possible 'kind' values. static const kIsolateStart = 'IsolateStart'; + static const kIsolateRunnable = 'IsolateRunnable'; static const kIsolateExit = 'IsolateExit'; static const kIsolateUpdate = 'IsolateUpdate'; static const kPauseStart = 'PauseStart'; diff --git a/runtime/observatory/tests/service/isolate_lifecycle_test.dart b/runtime/observatory/tests/service/isolate_lifecycle_test.dart index 4495269b596..a3ad5400f1b 100644 --- a/runtime/observatory/tests/service/isolate_lifecycle_test.dart +++ b/runtime/observatory/tests/service/isolate_lifecycle_test.dart @@ -4,6 +4,7 @@ // VMOptions=--error_on_bad_type --error_on_bad_override import 'dart:async'; +import 'dart:developer'; import 'dart:isolate' as I; import 'package:observatory/service_io.dart'; @@ -18,7 +19,8 @@ final isolates = []; void spawnEntry(int i) { } -Future before() async { +Future during() async { + debugger(); // Spawn spawnCount long lived isolates. for (var i = 0; i < spawnCount; i++) { var isolate = await I.Isolate.spawn(spawnEntry, i); @@ -27,9 +29,6 @@ Future before() async { print('spawned all isolates'); } -Future during() async { -} - int numPaused(vm) { int paused = 0; for (var isolate in vm.isolates) { @@ -41,21 +40,34 @@ int numPaused(vm) { } var tests = [ + (VM vm) async { + expect(vm.isolates.length, 1); + await hasStoppedAtBreakpoint(vm.isolates[0]); + }, + (VM vm) async { Completer completer = new Completer(); var stream = await vm.getEventStream(VM.kIsolateStream); - if (vm.isolates.length < spawnCount + 1) { - var subscription; - subscription = stream.listen((ServiceEvent event) { - if (event.kind == ServiceEvent.kIsolateStart) { - if (vm.isolates.length == (spawnCount + 1)) { - subscription.cancel(); - completer.complete(null); - } - } - }); - await completer.future; - } + var subscription; + int startCount = 0; + int runnableCount = 0; + subscription = stream.listen((ServiceEvent event) { + if (event.kind == ServiceEvent.kIsolateStart) { + startCount++; + } + if (event.kind == ServiceEvent.kIsolateRunnable) { + runnableCount++; + } + if (runnableCount == spawnCount) { + subscription.cancel(); + completer.complete(null); + } + }); + expect(vm.isolates.length, 1); + vm.isolates[0].resume(); + await completer.future; + expect(startCount, spawnCount); + expect(runnableCount, spawnCount); expect(vm.isolates.length, spawnCount + 1); }, @@ -124,6 +136,5 @@ var tests = [ ]; main(args) async => runVMTests(args, tests, - testeeBefore: before, testeeConcurrent: during, pause_on_exit: true); diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc index 4119234a1e1..fbc389725bf 100644 --- a/runtime/vm/isolate.cc +++ b/runtime/vm/isolate.cc @@ -1015,6 +1015,10 @@ bool Isolate::MakeRunnable() { event->Instant("Runnable"); event->Complete(); } + if (Service::isolate_stream.enabled()) { + ServiceEvent runnableEvent(this, ServiceEvent::kIsolateRunnable); + Service::HandleEvent(&runnableEvent); + } return true; } @@ -1704,7 +1708,9 @@ void Isolate::PrintJSON(JSONStream* stream, bool ref) { const Library& lib = Library::Handle(object_store()->root_library()); - jsobj.AddProperty("rootLib", lib); + if (!lib.IsNull()) { + jsobj.AddProperty("rootLib", lib); + } timer_list().PrintTimersToJSONProperty(&jsobj); { diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md index e18ed9ab93c..17f46a83688 100644 --- a/runtime/vm/service/service.md +++ b/runtime/vm/service/service.md @@ -620,7 +620,7 @@ The _streamId_ parameter may have the following published values: streamId | event types provided -------- | ----------- -Isolate | IsolateStart, IsolateExit, IsolateUpdate +Isolate | IsolateStart, IsolateRunnable, IsolateExit, IsolateUpdate Debug | PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted, PauseException, Resume, BreakpointAdded, BreakpointResolved, BreakpointRemoved, Inspect GC | GC @@ -1017,6 +1017,9 @@ enum EventKind { // Notification that a new isolate has started. IsolateStart, + // Notification that an isolate is ready to run. + IsolateRunnable, + // Notification that an isolate has exited. IsolateExit, @@ -1581,9 +1584,6 @@ class Isolate extends Response { // Suitable to pass to DateTime.fromMillisecondsSinceEpoch. int startTime; - // The entry function for this isolate. - @Function entry [optional]; - // The number of live ports for this isolate. int livePorts; @@ -1594,17 +1594,26 @@ class Isolate extends Response { // running, this will be a resume event. Event pauseEvent; - // The error that is causing this isolate to exit, if applicable. - Error error [optional]; + // The entry function for this isolate. + // + // Guaranteed to be initialized when the IsolateRunnable event fires. + @Function entry [optional]; // The root library for this isolate. - @Library rootLib; + // + // Guaranteed to be initialized when the IsolateRunnable event fires. + @Library rootLib [optional]; // A list of all libraries for this isolate. + // + // Guaranteed to be initialized when the IsolateRunnable event fires. @Library[] libraries; // A list of all breakpoints for this isolate. Breakpoint[] breakpoints; + + // The error that is causing this isolate to exit, if applicable. + Error error [optional]; } ``` diff --git a/runtime/vm/service_event.cc b/runtime/vm/service_event.cc index ce1e7fb0490..9c9868a933e 100644 --- a/runtime/vm/service_event.cc +++ b/runtime/vm/service_event.cc @@ -62,6 +62,8 @@ const char* ServiceEvent::KindAsCString() const { switch (kind()) { case kIsolateStart: return "IsolateStart"; + case kIsolateRunnable: + return "IsolateRunnable"; case kIsolateExit: return "IsolateExit"; case kIsolateUpdate: @@ -106,6 +108,7 @@ const char* ServiceEvent::KindAsCString() const { const char* ServiceEvent::stream_id() const { switch (kind()) { case kIsolateStart: + case kIsolateRunnable: case kIsolateExit: case kIsolateUpdate: return Service::isolate_stream.id(); diff --git a/runtime/vm/service_event.h b/runtime/vm/service_event.h index 80c3b697bc1..576e634f11c 100644 --- a/runtime/vm/service_event.h +++ b/runtime/vm/service_event.h @@ -15,6 +15,7 @@ class ServiceEvent { public: enum EventKind { kIsolateStart, // New isolate has started + kIsolateRunnable, // Isolate is ready to run kIsolateExit, // Isolate has exited kIsolateUpdate, // Isolate identity information has changed