Files
sdk/runtime/vm/service_event.cc
T
Carlo Bernaschina 5534568ff1 Make service protocol respect isolate lifecycle
Status:
- Currently the getIsolate API tries to guess the status of the isolate
  when it is in between the IsolateRunnable and PauseStart, by reporting
  it as already in the PauseStart status.
  This can potentially make flaky all the tests that uses PauseStart to
  synchronize with the actual status of the Isolate.
- In the previous situation the timestamp of the event is guessed too,
  potentially reporting a wrong value.
- Even with the previous fix the is still a race condition that can lead
  to a getIsolate responde with a PauseStart status to be sent before
  the actual PauseStart event.

Changes:
- Fallback to None pause event if the isolate is in between
  IsolateRunnable and PauseStart, avoiding double posting.
- Send the PauseStart event before the actual status change, partially
  avoiding the race conditions.
- Set the pause timestamp before the actual status change, fully
  avoiding the race condition.

Closes https://github.com/dart-lang/sdk/issues/28624

R=asiva@google.com, rmacnak@google.com

Review-Url: https://codereview.chromium.org/3006883002 .
2017-08-30 17:09:36 -07:00

279 lines
7.9 KiB
C++

// Copyright (c) 2015, 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.
#include "vm/service_event.h"
#include "vm/debugger.h"
#include "vm/message_handler.h"
#include "vm/service_isolate.h"
namespace dart {
#ifndef PRODUCT
ServiceEvent::ServiceEvent(Isolate* isolate, EventKind event_kind)
: isolate_(isolate),
kind_(event_kind),
embedder_kind_(NULL),
embedder_stream_id_(NULL),
breakpoint_(NULL),
top_frame_(NULL),
timeline_event_block_(NULL),
extension_rpc_(NULL),
exception_(NULL),
reload_error_(NULL),
spawn_token_(NULL),
spawn_error_(NULL),
at_async_jump_(false),
inspectee_(NULL),
gc_stats_(NULL),
bytes_(NULL),
bytes_length_(0),
timestamp_(OS::GetCurrentTimeMillis()) {
// We should never generate events for the vm or service isolates.
ASSERT(isolate_ != Dart::vm_isolate());
ASSERT(isolate == NULL ||
!ServiceIsolate::IsServiceIsolateDescendant(isolate_));
if ((event_kind == ServiceEvent::kPauseStart) ||
(event_kind == ServiceEvent::kPauseExit)) {
timestamp_ = isolate->message_handler()->paused_timestamp();
} else if (event_kind == ServiceEvent::kResume) {
timestamp_ = isolate->last_resume_timestamp();
}
ASSERT(timestamp_ > -1);
}
void ServiceEvent::UpdateTimestamp() {
timestamp_ = OS::GetCurrentTimeMillis();
}
const char* ServiceEvent::KindAsCString() const {
switch (kind()) {
case kVMUpdate:
return "VMUpdate";
case kIsolateStart:
return "IsolateStart";
case kIsolateRunnable:
return "IsolateRunnable";
case kIsolateExit:
return "IsolateExit";
case kIsolateUpdate:
return "IsolateUpdate";
case kServiceExtensionAdded:
return "ServiceExtensionAdded";
case kIsolateReload:
return "IsolateReload";
case kIsolateSpawn:
return "IsolateSpawn";
case kPauseStart:
return "PauseStart";
case kPauseExit:
return "PauseExit";
case kPauseBreakpoint:
return "PauseBreakpoint";
case kPauseInterrupted:
return "PauseInterrupted";
case kPauseException:
return "PauseException";
case kPausePostRequest:
return "PausePostRequest";
case kNone:
return "None";
case kResume:
return "Resume";
case kBreakpointAdded:
return "BreakpointAdded";
case kBreakpointResolved:
return "BreakpointResolved";
case kBreakpointRemoved:
return "BreakpointRemoved";
case kGC:
return "GC"; // TODO(koda): Change to GarbageCollected.
case kInspect:
return "Inspect";
case kEmbedder:
return embedder_kind();
case kLogging:
return "_Logging";
case kDebuggerSettingsUpdate:
return "_DebuggerSettingsUpdate";
case kIllegal:
return "Illegal";
case kExtension:
return "Extension";
case kTimelineEvents:
return "TimelineEvents";
default:
UNREACHABLE();
return "Unknown";
}
}
const StreamInfo* ServiceEvent::stream_info() const {
switch (kind()) {
case kVMUpdate:
return &Service::vm_stream;
case kIsolateStart:
case kIsolateRunnable:
case kIsolateExit:
case kIsolateUpdate:
case kIsolateReload:
case kIsolateSpawn:
case kServiceExtensionAdded:
return &Service::isolate_stream;
case kPauseStart:
case kPauseExit:
case kPauseBreakpoint:
case kPauseInterrupted:
case kPauseException:
case kPausePostRequest:
case kNone:
case kResume:
case kBreakpointAdded:
case kBreakpointResolved:
case kBreakpointRemoved:
case kInspect:
case kDebuggerSettingsUpdate:
return &Service::debug_stream;
case kGC:
return &Service::gc_stream;
case kLogging:
return &Service::logging_stream;
case kExtension:
return &Service::extension_stream;
case kTimelineEvents:
return &Service::timeline_stream;
case kEmbedder:
return NULL;
default:
UNREACHABLE();
return NULL;
}
}
const char* ServiceEvent::stream_id() const {
const StreamInfo* stream = stream_info();
if (stream == NULL) {
ASSERT(kind() == kEmbedder);
return embedder_stream_id_;
} else {
return stream->id();
}
}
void ServiceEvent::PrintJSON(JSONStream* js) const {
JSONObject jsobj(js);
PrintJSONHeader(&jsobj);
if (kind() == kIsolateReload) {
if (reload_error_ == NULL) {
jsobj.AddProperty("status", "success");
} else {
jsobj.AddProperty("status", "failure");
jsobj.AddProperty("reloadError", *(reload_error()));
}
}
if (kind() == kIsolateSpawn) {
ASSERT(spawn_token() != NULL);
jsobj.AddPropertyStr("spawnToken", *(spawn_token()));
if (spawn_error_ == NULL) {
jsobj.AddProperty("status", "success");
} else {
jsobj.AddProperty("status", "failure");
jsobj.AddPropertyStr("spawnError", *(spawn_error()));
}
}
if (kind() == kServiceExtensionAdded) {
ASSERT(extension_rpc_ != NULL);
jsobj.AddProperty("extensionRPC", extension_rpc_->ToCString());
}
if (kind() == kPauseBreakpoint) {
JSONArray jsarr(&jsobj, "pauseBreakpoints");
// TODO(rmacnak): If we are paused at more than one breakpoint,
// provide it here.
if (breakpoint() != NULL) {
jsarr.AddValue(breakpoint());
}
} else {
if (breakpoint() != NULL) {
jsobj.AddProperty("breakpoint", breakpoint());
}
}
if (kind() == kTimelineEvents) {
jsobj.AddProperty("timelineEvents", timeline_event_block_);
}
if (kind() == kDebuggerSettingsUpdate) {
JSONObject jssettings(&jsobj, "_debuggerSettings");
isolate()->debugger()->PrintSettingsToJSONObject(&jssettings);
}
if ((top_frame() != NULL) && Isolate::Current()->compilation_allowed()) {
JSONObject jsFrame(&jsobj, "topFrame");
top_frame()->PrintToJSONObject(&jsFrame);
intptr_t index = 0; // Avoid ambiguity in call to AddProperty.
jsFrame.AddProperty("index", index);
}
if (exception() != NULL) {
jsobj.AddProperty("exception", *(exception()));
}
if (at_async_jump()) {
jsobj.AddProperty("atAsyncSuspension", true);
}
if (inspectee() != NULL) {
jsobj.AddProperty("inspectee", *(inspectee()));
}
if (gc_stats() != NULL) {
jsobj.AddProperty("reason", Heap::GCReasonToString(gc_stats()->reason_));
isolate()->heap()->PrintToJSONObject(Heap::kNew, &jsobj);
isolate()->heap()->PrintToJSONObject(Heap::kOld, &jsobj);
}
if (bytes() != NULL) {
jsobj.AddPropertyBase64("bytes", bytes(), bytes_length());
}
if (kind() == kLogging) {
JSONObject logRecord(&jsobj, "logRecord");
logRecord.AddProperty64("sequenceNumber", log_record_.sequence_number);
logRecord.AddPropertyTimeMillis("time", log_record_.timestamp);
logRecord.AddProperty64("level", log_record_.level);
logRecord.AddProperty("loggerName", *(log_record_.name));
logRecord.AddProperty("message", *(log_record_.message));
logRecord.AddProperty("zone", *(log_record_.zone));
logRecord.AddProperty("error", *(log_record_.error));
logRecord.AddProperty("stackTrace", *(log_record_.stack_trace));
}
if (kind() == kExtension) {
js->AppendSerializedObject("extensionData",
extension_event_.event_data->ToCString());
}
}
void ServiceEvent::PrintJSONHeader(JSONObject* jsobj) const {
ASSERT(jsobj != NULL);
jsobj->AddProperty("type", "Event");
jsobj->AddProperty("kind", KindAsCString());
if (kind() == kExtension) {
ASSERT(extension_event_.event_kind != NULL);
jsobj->AddProperty("extensionKind",
extension_event_.event_kind->ToCString());
}
if (isolate() == NULL) {
jsobj->AddPropertyVM("vm");
} else {
jsobj->AddProperty("isolate", isolate());
}
ASSERT(timestamp_ != -1);
jsobj->AddPropertyTimeMillis("timestamp", timestamp_);
}
#endif // !PRODUCT
} // namespace dart