1 | // Copyright (c) 2015, 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 | #include "vm/service_event.h" |
6 | |
7 | #include "vm/debugger.h" |
8 | #include "vm/message_handler.h" |
9 | #include "vm/service_isolate.h" |
10 | #include "vm/timeline.h" |
11 | |
12 | namespace dart { |
13 | |
14 | #ifndef PRODUCT |
15 | |
16 | ServiceEvent::ServiceEvent(Isolate* isolate, EventKind event_kind) |
17 | : isolate_(isolate), |
18 | kind_(event_kind), |
19 | flag_name_(NULL), |
20 | flag_new_value_(NULL), |
21 | embedder_kind_(NULL), |
22 | embedder_stream_id_(NULL), |
23 | breakpoint_(NULL), |
24 | top_frame_(NULL), |
25 | timeline_event_block_(NULL), |
26 | extension_rpc_(NULL), |
27 | exception_(NULL), |
28 | reload_error_(NULL), |
29 | spawn_token_(NULL), |
30 | spawn_error_(NULL), |
31 | at_async_jump_(false), |
32 | inspectee_(NULL), |
33 | gc_stats_(NULL), |
34 | bytes_(NULL), |
35 | bytes_length_(0), |
36 | timestamp_(OS::GetCurrentTimeMillis()) { |
37 | // We should never generate events for the vm or service isolates. |
38 | ASSERT(isolate_ != Dart::vm_isolate()); |
39 | ASSERT(isolate == NULL || FLAG_show_invisible_isolates || |
40 | !Isolate::IsVMInternalIsolate(isolate)); |
41 | |
42 | if ((event_kind == ServiceEvent::kPauseStart) || |
43 | (event_kind == ServiceEvent::kPauseExit)) { |
44 | timestamp_ = isolate->message_handler()->paused_timestamp(); |
45 | } else if (event_kind == ServiceEvent::kResume) { |
46 | timestamp_ = isolate->last_resume_timestamp(); |
47 | } |
48 | ASSERT(timestamp_ > -1); |
49 | } |
50 | |
51 | void ServiceEvent::UpdateTimestamp() { |
52 | timestamp_ = OS::GetCurrentTimeMillis(); |
53 | } |
54 | |
55 | const char* ServiceEvent::KindAsCString() const { |
56 | switch (kind()) { |
57 | case kVMUpdate: |
58 | return "VMUpdate" ; |
59 | case kVMFlagUpdate: |
60 | return "VMFlagUpdate" ; |
61 | case kIsolateStart: |
62 | return "IsolateStart" ; |
63 | case kIsolateRunnable: |
64 | return "IsolateRunnable" ; |
65 | case kIsolateExit: |
66 | return "IsolateExit" ; |
67 | case kIsolateUpdate: |
68 | return "IsolateUpdate" ; |
69 | case kServiceExtensionAdded: |
70 | return "ServiceExtensionAdded" ; |
71 | case kIsolateReload: |
72 | return "IsolateReload" ; |
73 | case kPauseStart: |
74 | return "PauseStart" ; |
75 | case kPauseExit: |
76 | return "PauseExit" ; |
77 | case kPauseBreakpoint: |
78 | return "PauseBreakpoint" ; |
79 | case kPauseInterrupted: |
80 | return "PauseInterrupted" ; |
81 | case kPauseException: |
82 | return "PauseException" ; |
83 | case kPausePostRequest: |
84 | return "PausePostRequest" ; |
85 | case kNone: |
86 | return "None" ; |
87 | case kResume: |
88 | return "Resume" ; |
89 | case kBreakpointAdded: |
90 | return "BreakpointAdded" ; |
91 | case kBreakpointResolved: |
92 | return "BreakpointResolved" ; |
93 | case kBreakpointRemoved: |
94 | return "BreakpointRemoved" ; |
95 | case kGC: |
96 | return "GC" ; // TODO(koda): Change to GarbageCollected. |
97 | case kInspect: |
98 | return "Inspect" ; |
99 | case kEmbedder: |
100 | return embedder_kind(); |
101 | case kLogging: |
102 | return "Logging" ; |
103 | case kDebuggerSettingsUpdate: |
104 | return "_DebuggerSettingsUpdate" ; |
105 | case kIllegal: |
106 | return "Illegal" ; |
107 | case kExtension: |
108 | return "Extension" ; |
109 | case kTimelineEvents: |
110 | return "TimelineEvents" ; |
111 | case kTimelineStreamSubscriptionsUpdate: |
112 | return "TimelineStreamSubscriptionsUpdate" ; |
113 | default: |
114 | UNREACHABLE(); |
115 | return "Unknown" ; |
116 | } |
117 | } |
118 | |
119 | const StreamInfo* ServiceEvent::stream_info() const { |
120 | switch (kind()) { |
121 | case kVMUpdate: |
122 | case kVMFlagUpdate: |
123 | return &Service::vm_stream; |
124 | |
125 | case kIsolateStart: |
126 | case kIsolateRunnable: |
127 | case kIsolateExit: |
128 | case kIsolateUpdate: |
129 | case kIsolateReload: |
130 | case kServiceExtensionAdded: |
131 | return &Service::isolate_stream; |
132 | |
133 | case kPauseStart: |
134 | case kPauseExit: |
135 | case kPauseBreakpoint: |
136 | case kPauseInterrupted: |
137 | case kPauseException: |
138 | case kPausePostRequest: |
139 | case kNone: |
140 | case kResume: |
141 | case kBreakpointAdded: |
142 | case kBreakpointResolved: |
143 | case kBreakpointRemoved: |
144 | case kInspect: |
145 | case kDebuggerSettingsUpdate: |
146 | return &Service::debug_stream; |
147 | |
148 | case kGC: |
149 | return &Service::gc_stream; |
150 | |
151 | case kLogging: |
152 | return &Service::logging_stream; |
153 | |
154 | case kExtension: |
155 | return &Service::extension_stream; |
156 | |
157 | case kTimelineEvents: |
158 | case kTimelineStreamSubscriptionsUpdate: |
159 | return &Service::timeline_stream; |
160 | |
161 | case kEmbedder: |
162 | return NULL; |
163 | |
164 | default: |
165 | UNREACHABLE(); |
166 | return NULL; |
167 | } |
168 | } |
169 | |
170 | const char* ServiceEvent::stream_id() const { |
171 | const StreamInfo* stream = stream_info(); |
172 | if (stream == NULL) { |
173 | ASSERT(kind() == kEmbedder); |
174 | return embedder_stream_id_; |
175 | } else { |
176 | return stream->id(); |
177 | } |
178 | } |
179 | |
180 | void ServiceEvent::PrintJSON(JSONStream* js) const { |
181 | JSONObject jsobj(js); |
182 | PrintJSONHeader(&jsobj); |
183 | if (kind() == kVMFlagUpdate) { |
184 | jsobj.AddProperty("flag" , flag_name()); |
185 | // For backwards compatibility, "new_value" is also provided. |
186 | // TODO(bkonyi): remove when service protocol major version is incremented. |
187 | ASSERT(SERVICE_PROTOCOL_MAJOR_VERSION == 3); |
188 | jsobj.AddProperty("new_value" , flag_new_value()); |
189 | jsobj.AddProperty("newValue" , flag_new_value()); |
190 | } |
191 | if (kind() == kIsolateReload) { |
192 | if (reload_error_ == NULL) { |
193 | jsobj.AddProperty("status" , "success" ); |
194 | } else { |
195 | jsobj.AddProperty("status" , "failure" ); |
196 | jsobj.AddProperty("reloadError" , *(reload_error())); |
197 | } |
198 | } |
199 | if (kind() == kServiceExtensionAdded) { |
200 | ASSERT(extension_rpc_ != NULL); |
201 | jsobj.AddProperty("extensionRPC" , extension_rpc_->ToCString()); |
202 | } |
203 | if (kind() == kPauseBreakpoint) { |
204 | JSONArray jsarr(&jsobj, "pauseBreakpoints" ); |
205 | // TODO(rmacnak): If we are paused at more than one breakpoint, |
206 | // provide it here. |
207 | if (breakpoint() != NULL) { |
208 | jsarr.AddValue(breakpoint()); |
209 | } |
210 | } else { |
211 | if (breakpoint() != NULL) { |
212 | jsobj.AddProperty("breakpoint" , breakpoint()); |
213 | } |
214 | } |
215 | if (kind() == kTimelineEvents) { |
216 | jsobj.AddProperty("timelineEvents" , timeline_event_block_); |
217 | } |
218 | if (kind() == kTimelineStreamSubscriptionsUpdate) { |
219 | JSONArray arr(&jsobj, "updatedStreams" ); |
220 | Timeline::PrintFlagsToJSONArray(&arr); |
221 | } |
222 | if (kind() == kDebuggerSettingsUpdate) { |
223 | JSONObject jssettings(&jsobj, "_debuggerSettings" ); |
224 | isolate()->debugger()->PrintSettingsToJSONObject(&jssettings); |
225 | } |
226 | #if !defined(DART_PRECOMPILED_RUNTIME) |
227 | if (top_frame() != nullptr) { |
228 | JSONObject jsFrame(&jsobj, "topFrame" ); |
229 | top_frame()->PrintToJSONObject(&jsFrame); |
230 | intptr_t index = 0; // Avoid ambiguity in call to AddProperty. |
231 | jsFrame.AddProperty("index" , index); |
232 | } |
233 | #endif |
234 | if (exception() != NULL) { |
235 | jsobj.AddProperty("exception" , *(exception())); |
236 | } |
237 | if (at_async_jump()) { |
238 | jsobj.AddProperty("atAsyncSuspension" , true); |
239 | } |
240 | if (inspectee() != NULL) { |
241 | jsobj.AddProperty("inspectee" , *(inspectee())); |
242 | } |
243 | if (gc_stats() != NULL) { |
244 | jsobj.AddProperty("reason" , Heap::GCReasonToString(gc_stats()->reason_)); |
245 | isolate()->heap()->PrintToJSONObject(Heap::kNew, &jsobj); |
246 | isolate()->heap()->PrintToJSONObject(Heap::kOld, &jsobj); |
247 | } |
248 | if (bytes() != NULL) { |
249 | jsobj.AddPropertyBase64("bytes" , bytes(), bytes_length()); |
250 | } |
251 | if (kind() == kLogging) { |
252 | JSONObject logRecord(&jsobj, "logRecord" ); |
253 | logRecord.AddProperty("type" , "LogRecord" ); |
254 | logRecord.AddProperty64("sequenceNumber" , log_record_.sequence_number); |
255 | logRecord.AddPropertyTimeMillis("time" , log_record_.timestamp); |
256 | logRecord.AddProperty64("level" , log_record_.level); |
257 | logRecord.AddProperty("loggerName" , *(log_record_.name)); |
258 | logRecord.AddProperty("message" , *(log_record_.message)); |
259 | logRecord.AddProperty("zone" , *(log_record_.zone)); |
260 | logRecord.AddProperty("error" , *(log_record_.error)); |
261 | logRecord.AddProperty("stackTrace" , *(log_record_.stack_trace)); |
262 | } |
263 | if (kind() == kExtension) { |
264 | js->AppendSerializedObject("extensionData" , |
265 | extension_event_.event_data->ToCString()); |
266 | } |
267 | } |
268 | |
269 | void ServiceEvent::(JSONObject* jsobj) const { |
270 | ASSERT(jsobj != NULL); |
271 | jsobj->AddProperty("type" , "Event" ); |
272 | jsobj->AddProperty("kind" , KindAsCString()); |
273 | if (kind() == kExtension) { |
274 | ASSERT(extension_event_.event_kind != NULL); |
275 | jsobj->AddProperty("extensionKind" , |
276 | extension_event_.event_kind->ToCString()); |
277 | } |
278 | if (isolate() == NULL) { |
279 | jsobj->AddPropertyVM("vm" ); |
280 | } else { |
281 | jsobj->AddProperty("isolate" , isolate()); |
282 | } |
283 | ASSERT(timestamp_ != -1); |
284 | jsobj->AddPropertyTimeMillis("timestamp" , timestamp_); |
285 | } |
286 | |
287 | #endif // !PRODUCT |
288 | |
289 | } // namespace dart |
290 | |