// 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" #include "vm/timeline.h" namespace dart { #ifndef PRODUCT ServiceEvent::ServiceEvent(EventKind event_kind) : ServiceEvent(nullptr, nullptr, event_kind) {} ServiceEvent::ServiceEvent(IsolateGroup* isolate_group, EventKind event_kind) : ServiceEvent(isolate_group, nullptr, event_kind) {} ServiceEvent::ServiceEvent(Isolate* isolate, EventKind event_kind) : ServiceEvent(isolate != nullptr ? isolate->group() : nullptr, isolate, event_kind) {} ServiceEvent::ServiceEvent(IsolateGroup* isolate_group, Isolate* isolate, EventKind event_kind) : isolate_(isolate), isolate_group_(isolate_group), kind_(event_kind), flag_name_(nullptr), flag_new_value_(nullptr), previous_tag_(nullptr), updated_tag_(nullptr), embedder_kind_(nullptr), embedder_stream_id_(nullptr), breakpoint_(nullptr), top_frame_(nullptr), timeline_event_block_(nullptr), extension_rpc_(nullptr), exception_(nullptr), reload_error_(nullptr), spawn_token_(nullptr), spawn_error_(nullptr), at_async_jump_(false), inspectee_(nullptr), gc_stats_(nullptr), bytes_(nullptr), bytes_length_(0), timestamp_(OS::GetCurrentTimeMillis()) { // We should never generate events for the vm isolate as it is never reported // over the service. ASSERT(isolate_ != Dart::vm_isolate()); // VM internal isolates should never post service events. However, the Isolate // service object uses a service event to represent the current running state // of the isolate, so we need to allow for system isolates to create resume // and none events for this purpose. The resume event represents a running // isolate and the none event is returned for an isolate that has not yet // been marked as runnable (see "pauseEvent" in Isolate::PrintJSON). ASSERT(isolate == NULL || !Isolate::IsVMInternalIsolate(isolate) || (Isolate::IsVMInternalIsolate(isolate) && (event_kind == ServiceEvent::kResume || event_kind == ServiceEvent::kNone || // VM service can print Observatory information to Stdout or Stderr // which are embedder streams. event_kind == ServiceEvent::kEmbedder || event_kind == ServiceEvent::kCpuSamples))); 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 kVMFlagUpdate: return "VMFlagUpdate"; 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 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 kBreakpointUpdated: return "BreakpointUpdated"; 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"; case kTimelineStreamSubscriptionsUpdate: return "TimelineStreamSubscriptionsUpdate"; case kUserTagChanged: return "UserTagChanged"; case kCpuSamples: return "CpuSamples"; default: UNREACHABLE(); return "Unknown"; } } const StreamInfo* ServiceEvent::stream_info() const { switch (kind()) { case kVMUpdate: case kVMFlagUpdate: return &Service::vm_stream; case kIsolateStart: case kIsolateRunnable: case kIsolateExit: case kIsolateUpdate: case kIsolateReload: 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 kBreakpointUpdated: 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: case kTimelineStreamSubscriptionsUpdate: return &Service::timeline_stream; case kEmbedder: return nullptr; case kCpuSamples: case kUserTagChanged: return &Service::profiler_stream; default: UNREACHABLE(); return nullptr; } } 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() == kVMFlagUpdate) { jsobj.AddProperty("flag", flag_name()); // For backwards compatibility, "new_value" is also provided. jsobj.AddProperty("newValue", flag_new_value()); } if (kind() == kUserTagChanged) { jsobj.AddProperty("previousTag", previous_tag()); jsobj.AddProperty("updatedTag", updated_tag()); } if (kind() == kIsolateReload) { if (reload_error_ == NULL) { jsobj.AddProperty("status", "success"); } else { jsobj.AddProperty("status", "failure"); jsobj.AddProperty("reloadError", *(reload_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() == kTimelineStreamSubscriptionsUpdate) { JSONArray arr(&jsobj, "updatedStreams"); Timeline::PrintFlagsToJSONArray(&arr); } if (kind() == kDebuggerSettingsUpdate) { JSONObject jssettings(&jsobj, "_debuggerSettings"); isolate()->debugger()->PrintSettingsToJSONObject(&jssettings); } #if !defined(DART_PRECOMPILED_RUNTIME) if (top_frame() != nullptr) { JSONObject jsFrame(&jsobj, "topFrame"); top_frame()->PrintToJSONObject(&jsFrame); intptr_t index = 0; // Avoid ambiguity in call to AddProperty. jsFrame.AddProperty("index", index); } #endif 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_group()->heap()->PrintToJSONObject(Heap::kNew, &jsobj); isolate_group()->heap()->PrintToJSONObject(Heap::kOld, &jsobj); } if (bytes() != NULL) { jsobj.AddPropertyBase64("bytes", bytes(), bytes_length()); } if (kind() == kLogging) { JSONObject logRecord(&jsobj, "logRecord"); logRecord.AddProperty("type", "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()); } if (kind() == kCpuSamples) { JSONObject cpu_profile(&jsobj, "cpuSamples"); cpu_profile_->PrintProfileJSON(&cpu_profile, /*include_code_samples=*/false, /*is_event=*/true); } } 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