OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/service.h" | 5 #include "vm/service.h" |
6 | 6 |
7 #include "include/dart_api.h" | 7 #include "include/dart_api.h" |
8 #include "platform/globals.h" | 8 #include "platform/globals.h" |
9 | 9 |
10 #include "vm/compiler.h" | 10 #include "vm/compiler.h" |
11 #include "vm/coverage.h" | 11 #include "vm/coverage.h" |
12 #include "vm/cpu.h" | 12 #include "vm/cpu.h" |
13 #include "vm/dart_api_impl.h" | 13 #include "vm/dart_api_impl.h" |
14 #include "vm/dart_entry.h" | 14 #include "vm/dart_entry.h" |
15 #include "vm/debugger.h" | 15 #include "vm/debugger.h" |
16 #include "vm/isolate.h" | 16 #include "vm/isolate.h" |
17 #include "vm/lockers.h" | 17 #include "vm/lockers.h" |
18 #include "vm/message.h" | 18 #include "vm/message.h" |
19 #include "vm/message_handler.h" | 19 #include "vm/message_handler.h" |
20 #include "vm/native_entry.h" | 20 #include "vm/native_entry.h" |
21 #include "vm/native_arguments.h" | 21 #include "vm/native_arguments.h" |
22 #include "vm/object.h" | 22 #include "vm/object.h" |
23 #include "vm/object_graph.h" | 23 #include "vm/object_graph.h" |
24 #include "vm/object_id_ring.h" | 24 #include "vm/object_id_ring.h" |
25 #include "vm/object_store.h" | 25 #include "vm/object_store.h" |
26 #include "vm/parser.h" | 26 #include "vm/parser.h" |
27 #include "vm/port.h" | 27 #include "vm/port.h" |
28 #include "vm/profiler_service.h" | 28 #include "vm/profiler_service.h" |
29 #include "vm/reusable_handles.h" | 29 #include "vm/reusable_handles.h" |
30 #include "vm/service_isolate.h" | |
30 #include "vm/stack_frame.h" | 31 #include "vm/stack_frame.h" |
31 #include "vm/symbols.h" | 32 #include "vm/symbols.h" |
32 #include "vm/unicode.h" | 33 #include "vm/unicode.h" |
33 #include "vm/version.h" | 34 #include "vm/version.h" |
34 | 35 |
35 namespace dart { | 36 namespace dart { |
36 | 37 |
37 DEFINE_FLAG(bool, trace_service, false, "Trace VM service requests."); | 38 DECLARE_FLAG(bool, trace_service); |
38 DEFINE_FLAG(bool, trace_service_pause_events, false, | 39 DECLARE_FLAG(bool, trace_service_pause_events); |
39 "Trace VM service isolate pause events."); | |
40 DECLARE_FLAG(bool, enable_type_checks); | 40 DECLARE_FLAG(bool, enable_type_checks); |
41 DECLARE_FLAG(bool, enable_asserts); | 41 DECLARE_FLAG(bool, enable_asserts); |
42 | 42 |
43 struct ResourcesEntry { | 43 EmbedderServiceHandler* Service::isolate_service_handler_head_ = NULL; |
44 const char* path_; | 44 EmbedderServiceHandler* Service::root_service_handler_head_ = NULL; |
turnidge
2015/02/12 18:08:57
Add a TODO to combine isolate and root service han
Cutch
2015/02/12 18:18:47
Done.
| |
45 const char* resource_; | 45 uint32_t Service::event_mask_ = 0; |
46 int length_; | 46 struct ServiceMethodDescriptor; |
47 ServiceMethodDescriptor* FindMethod(const char* method_name); | |
48 | |
49 static uint8_t* allocator(uint8_t* ptr, intptr_t old_size, intptr_t new_size) { | |
50 void* new_ptr = realloc(reinterpret_cast<void*>(ptr), new_size); | |
51 return reinterpret_cast<uint8_t*>(new_ptr); | |
52 } | |
53 | |
54 static void PrintRequest(const JSONObject& obj, JSONStream* js) { | |
55 JSONObject jsobj(&obj, "request"); | |
56 jsobj.AddProperty("method", js->method()); | |
57 { | |
58 JSONArray jsarr(&jsobj, "param_keys"); | |
59 for (intptr_t i = 0; i < js->num_params(); i++) { | |
60 jsarr.AddValue(js->GetParamKey(i)); | |
61 } | |
62 } | |
63 { | |
64 JSONArray jsarr(&jsobj, "param_values"); | |
65 for (intptr_t i = 0; i < js->num_params(); i++) { | |
66 jsarr.AddValue(js->GetParamValue(i)); | |
67 } | |
68 } | |
69 } | |
70 | |
71 | |
72 static void PrintError(JSONStream* js, | |
73 const char* format, ...) { | |
74 Isolate* isolate = Isolate::Current(); | |
75 | |
76 va_list args; | |
77 va_start(args, format); | |
78 intptr_t len = OS::VSNPrint(NULL, 0, format, args); | |
79 va_end(args); | |
80 | |
81 char* buffer = isolate->current_zone()->Alloc<char>(len + 1); | |
82 va_list args2; | |
83 va_start(args2, format); | |
84 OS::VSNPrint(buffer, (len + 1), format, args2); | |
85 va_end(args2); | |
86 | |
87 JSONObject jsobj(js); | |
88 jsobj.AddProperty("type", "Error"); | |
89 jsobj.AddProperty("message", buffer); | |
90 PrintRequest(jsobj, js); | |
91 } | |
92 | |
93 | |
94 static void PrintMissingParamError(JSONStream* js, | |
95 const char* param) { | |
96 PrintError(js, "%s expects the '%s' parameter", | |
97 js->method(), param); | |
98 } | |
99 | |
100 | |
101 static void PrintInvalidParamError(JSONStream* js, | |
102 const char* param) { | |
103 PrintError(js, "%s: invalid '%s' parameter: %s", | |
104 js->method(), param, js->LookupParam(param)); | |
105 } | |
106 | |
107 | |
108 static void PrintUnrecognizedMethodError(JSONStream* js) { | |
109 PrintError(js, "unrecognized method: %s", js->method()); | |
110 } | |
111 | |
112 | |
113 static void PrintErrorWithKind(JSONStream* js, | |
114 const char* kind, | |
115 const char* format, ...) { | |
116 Isolate* isolate = Isolate::Current(); | |
117 | |
118 va_list args; | |
119 va_start(args, format); | |
120 intptr_t len = OS::VSNPrint(NULL, 0, format, args); | |
121 va_end(args); | |
122 | |
123 char* buffer = isolate->current_zone()->Alloc<char>(len + 1); | |
124 va_list args2; | |
125 va_start(args2, format); | |
126 OS::VSNPrint(buffer, (len + 1), format, args2); | |
127 va_end(args2); | |
128 | |
129 JSONObject jsobj(js); | |
130 jsobj.AddProperty("type", "Error"); | |
131 jsobj.AddProperty("id", ""); | |
132 jsobj.AddProperty("kind", kind); | |
133 jsobj.AddProperty("message", buffer); | |
134 PrintRequest(jsobj, js); | |
135 } | |
136 | |
137 | |
138 static bool GetIntegerId(const char* s, intptr_t* id, int base = 10) { | |
139 if ((s == NULL) || (*s == '\0')) { | |
140 // Empty string. | |
141 return false; | |
142 } | |
143 if (id == NULL) { | |
144 // No id pointer. | |
145 return false; | |
146 } | |
147 intptr_t r = 0; | |
148 char* end_ptr = NULL; | |
149 r = strtol(s, &end_ptr, base); | |
150 if (end_ptr == s) { | |
151 // String was not advanced at all, cannot be valid. | |
152 return false; | |
153 } | |
154 *id = r; | |
155 return true; | |
156 } | |
157 | |
158 | |
159 static bool GetUnsignedIntegerId(const char* s, uintptr_t* id, int base = 10) { | |
160 if ((s == NULL) || (*s == '\0')) { | |
161 // Empty string. | |
162 return false; | |
163 } | |
164 if (id == NULL) { | |
165 // No id pointer. | |
166 return false; | |
167 } | |
168 uintptr_t r = 0; | |
169 char* end_ptr = NULL; | |
170 r = strtoul(s, &end_ptr, base); | |
171 if (end_ptr == s) { | |
172 // String was not advanced at all, cannot be valid. | |
173 return false; | |
174 } | |
175 *id = r; | |
176 return true; | |
177 } | |
178 | |
179 | |
180 static bool GetInteger64Id(const char* s, int64_t* id, int base = 10) { | |
181 if ((s == NULL) || (*s == '\0')) { | |
182 // Empty string. | |
183 return false; | |
184 } | |
185 if (id == NULL) { | |
186 // No id pointer. | |
187 return false; | |
188 } | |
189 int64_t r = 0; | |
190 char* end_ptr = NULL; | |
191 r = strtoll(s, &end_ptr, base); | |
192 if (end_ptr == s) { | |
193 // String was not advanced at all, cannot be valid. | |
194 return false; | |
195 } | |
196 *id = r; | |
197 return true; | |
198 } | |
199 | |
200 | |
201 // Scans the string until the '-' character. Returns pointer to string | |
202 // at '-' character. Returns NULL if not found. | |
203 static const char* ScanUntilDash(const char* s) { | |
204 if ((s == NULL) || (*s == '\0')) { | |
205 // Empty string. | |
206 return NULL; | |
207 } | |
208 while (*s != '\0') { | |
209 if (*s == '-') { | |
210 return s; | |
211 } | |
212 s++; | |
213 } | |
214 return NULL; | |
215 } | |
216 | |
217 | |
218 static bool GetCodeId(const char* s, int64_t* timestamp, uword* address) { | |
219 if ((s == NULL) || (*s == '\0')) { | |
220 // Empty string. | |
221 return false; | |
222 } | |
223 if ((timestamp == NULL) || (address == NULL)) { | |
224 // Bad arguments. | |
225 return false; | |
226 } | |
227 // Extract the timestamp. | |
228 if (!GetInteger64Id(s, timestamp, 16) || (*timestamp < 0)) { | |
229 return false; | |
230 } | |
231 s = ScanUntilDash(s); | |
232 if (s == NULL) { | |
233 return false; | |
234 } | |
235 // Skip the dash. | |
236 s++; | |
237 // Extract the PC. | |
238 if (!GetUnsignedIntegerId(s, address, 16)) { | |
239 return false; | |
240 } | |
241 return true; | |
242 } | |
243 | |
244 | |
245 class MethodParameter { | |
turnidge
2015/02/12 18:08:57
Consider adding standalone unit tests for BoolPara
Cutch
2015/02/12 18:18:47
I've added a TODO.
| |
246 public: | |
247 MethodParameter(const char* name, bool required) | |
248 : name_(name), required_(required) { | |
249 } | |
250 | |
251 virtual ~MethodParameter() { } | |
252 | |
253 virtual bool Validate(const char* value) const { | |
254 return true; | |
255 } | |
256 | |
257 const char* name() const { | |
258 return name_; | |
259 } | |
260 | |
261 bool required() const { | |
262 return required_; | |
263 } | |
264 | |
265 private: | |
266 const char* name_; | |
267 bool required_; | |
47 }; | 268 }; |
48 | 269 |
49 extern ResourcesEntry __service_resources_[]; | 270 |
50 | 271 class BoolParameter : public MethodParameter { |
51 class Resources { | |
52 public: | 272 public: |
53 static const int kNoSuchInstance = -1; | 273 BoolParameter(const char* name, bool required) |
54 static int ResourceLookup(const char* path, const char** resource) { | 274 : MethodParameter(name, required) { |
55 ResourcesEntry* table = ResourceTable(); | 275 } |
56 for (int i = 0; table[i].path_ != NULL; i++) { | 276 |
57 const ResourcesEntry& entry = table[i]; | 277 virtual bool Validate(const char* value) const { |
58 if (strcmp(path, entry.path_) == 0) { | 278 if (value == NULL) { |
59 *resource = entry.resource_; | 279 return false; |
60 ASSERT(entry.length_ > 0); | 280 } |
61 return entry.length_; | 281 return (strcmp("true", value) == 0) || (strcmp("false", value) == 0); |
282 } | |
283 | |
284 static bool Interpret(const char* value) { | |
285 return strcmp("true", value) == 0; | |
286 } | |
287 }; | |
288 | |
289 | |
290 class IdParameter : public MethodParameter { | |
291 public: | |
292 IdParameter(const char* name, bool required) | |
293 : MethodParameter(name, required) { | |
294 } | |
295 | |
296 virtual bool Validate(const char* value) const { | |
297 return (value != NULL); | |
298 } | |
299 }; | |
300 | |
301 | |
302 #define ISOLATE_PARAMETER new IdParameter("isolateId", true) | |
303 | |
304 | |
305 class EnumParameter : public MethodParameter { | |
306 public: | |
307 EnumParameter(const char* name, bool required, const char** enums) | |
308 : MethodParameter(name, required), | |
309 enums_(enums) { | |
310 } | |
311 | |
312 virtual bool Validate(const char* value) const { | |
313 if (value == NULL) { | |
314 return true; | |
315 } | |
316 for (intptr_t i = 0; enums_[i] != NULL; i++) { | |
317 if (strcmp(value, enums_[i]) == 0) { | |
318 return true; | |
62 } | 319 } |
63 } | 320 } |
64 return kNoSuchInstance; | 321 return false; |
65 } | |
66 | |
67 static const char* Path(int idx) { | |
68 ASSERT(idx >= 0); | |
69 ResourcesEntry* entry = At(idx); | |
70 if (entry == NULL) { | |
71 return NULL; | |
72 } | |
73 ASSERT(entry->path_ != NULL); | |
74 return entry->path_; | |
75 } | |
76 | |
77 static int Length(int idx) { | |
78 ASSERT(idx >= 0); | |
79 ResourcesEntry* entry = At(idx); | |
80 if (entry == NULL) { | |
81 return kNoSuchInstance; | |
82 } | |
83 ASSERT(entry->path_ != NULL); | |
84 return entry->length_; | |
85 } | |
86 | |
87 static const uint8_t* Resource(int idx) { | |
88 ASSERT(idx >= 0); | |
89 ResourcesEntry* entry = At(idx); | |
90 if (entry == NULL) { | |
91 return NULL; | |
92 } | |
93 return reinterpret_cast<const uint8_t*>(entry->resource_); | |
94 } | 322 } |
95 | 323 |
96 private: | 324 private: |
97 static ResourcesEntry* At(int idx) { | 325 const char** enums_; |
98 ASSERT(idx >= 0); | 326 }; |
99 ResourcesEntry* table = ResourceTable(); | 327 |
100 for (int i = 0; table[i].path_ != NULL; i++) { | 328 |
101 if (idx == i) { | 329 // If the key is not found, this function returns the last element in the |
102 return &table[i]; | 330 // values array. This can be used to encode the default value. |
331 template<typename T> | |
332 T EnumMapper(const char* value, const char** enums, T* values) { | |
333 ASSERT(value != NULL); | |
334 intptr_t i = 0; | |
335 for (i = 0; enums[i] != NULL; i++) { | |
336 if (strcmp(value, enums[i]) == 0) { | |
337 return values[i]; | |
338 } | |
339 } | |
340 // Default value. | |
341 return values[i]; | |
342 } | |
343 | |
344 | |
345 typedef bool (*ServiceMethodEntry)(Isolate* isolate, JSONStream* js); | |
346 | |
347 | |
348 struct ServiceMethodDescriptor { | |
349 const char* name; | |
350 const ServiceMethodEntry entry; | |
351 const MethodParameter* const * parameters; | |
352 }; | |
353 | |
354 | |
355 static bool ValidateParameters(const MethodParameter* const* parameters, | |
356 JSONStream* js) { | |
357 if (parameters == NULL) { | |
358 return true; | |
359 } | |
360 for (intptr_t i = 0; parameters[i] != NULL; i++) { | |
361 const MethodParameter* parameter = parameters[i]; | |
362 const char* name = parameter->name(); | |
363 const bool required = parameter->required(); | |
364 const char* value = js->LookupParam(name); | |
365 const bool has_parameter = (value != NULL); | |
366 if (required && !has_parameter) { | |
367 PrintMissingParamError(js, name); | |
368 return false; | |
369 } | |
370 if (!parameter->Validate(value)) { | |
371 PrintInvalidParamError(js, name); | |
372 return false; | |
373 } | |
374 } | |
turnidge
2015/02/12 18:08:57
We don't complain about unexpected extra parameter
| |
375 return true; | |
376 } | |
377 | |
378 | |
379 void Service::InvokeMethod(Isolate* isolate, const Array& msg) { | |
380 ASSERT(isolate != NULL); | |
381 ASSERT(!msg.IsNull()); | |
382 ASSERT(msg.Length() == 5); | |
383 | |
384 { | |
385 StackZone zone(isolate); | |
386 HANDLESCOPE(isolate); | |
387 | |
388 Instance& reply_port = Instance::Handle(isolate); | |
389 String& method_name = String::Handle(isolate); | |
390 Array& param_keys = Array::Handle(isolate); | |
391 Array& param_values = Array::Handle(isolate); | |
392 reply_port ^= msg.At(1); | |
393 method_name ^= msg.At(2); | |
394 param_keys ^= msg.At(3); | |
395 param_values ^= msg.At(4); | |
396 | |
397 ASSERT(!method_name.IsNull()); | |
398 ASSERT(!param_keys.IsNull()); | |
399 ASSERT(!param_values.IsNull()); | |
400 ASSERT(param_keys.Length() == param_values.Length()); | |
401 | |
402 if (!reply_port.IsSendPort()) { | |
403 FATAL("SendPort expected."); | |
404 } | |
405 | |
406 JSONStream js; | |
407 js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), | |
408 method_name, param_keys, param_values); | |
409 | |
410 const char* c_method_name = method_name.ToCString(); | |
411 | |
412 ServiceMethodDescriptor* method = FindMethod(c_method_name); | |
413 if (method != NULL) { | |
414 if (!ValidateParameters(method->parameters, &js)) { | |
415 js.PostReply(); | |
416 return; | |
103 } | 417 } |
104 } | 418 if (method->entry(isolate, &js)) { |
105 return NULL; | 419 js.PostReply(); |
106 } | 420 } |
107 | 421 return; |
108 static ResourcesEntry* ResourceTable() { | 422 } |
109 return &__service_resources_[0]; | 423 |
110 } | 424 EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(c_method_name); |
111 | 425 if (handler == NULL) { |
112 DISALLOW_ALLOCATION(); | 426 handler = FindRootEmbedderHandler(c_method_name); |
113 DISALLOW_IMPLICIT_CONSTRUCTORS(Resources); | 427 } |
114 }; | 428 |
429 if (handler != NULL) { | |
430 EmbedderHandleMessage(handler, &js); | |
431 js.PostReply(); | |
432 return; | |
433 } | |
434 | |
435 PrintUnrecognizedMethodError(&js); | |
436 js.PostReply(); | |
437 return; | |
438 } | |
439 } | |
440 | |
441 | |
442 void Service::HandleRootMessage(const Array& msg_instance) { | |
443 Isolate* isolate = Isolate::Current(); | |
444 InvokeMethod(isolate, msg_instance); | |
445 } | |
446 | |
447 | |
448 void Service::HandleIsolateMessage(Isolate* isolate, const Array& msg) { | |
449 ASSERT(isolate != NULL); | |
450 InvokeMethod(isolate, msg); | |
451 } | |
452 | |
453 | |
454 bool Service::EventMaskHas(uint32_t mask) { | |
455 return (event_mask_ & mask) != 0; | |
456 } | |
457 | |
458 | |
459 bool Service::NeedsDebuggerEvents() { | |
460 return ServiceIsolate::IsRunning() && EventMaskHas(kEventFamilyDebugMask); | |
461 } | |
462 | |
463 | |
464 bool Service::NeedsGCEvents() { | |
465 return ServiceIsolate::IsRunning() && EventMaskHas(kEventFamilyGCMask); | |
466 } | |
467 | |
468 | |
469 void Service::SetEventMask(uint32_t mask) { | |
470 event_mask_ = mask; | |
471 } | |
472 | |
473 | |
474 void Service::SendEvent(intptr_t eventId, const Object& eventMessage) { | |
475 if (!ServiceIsolate::IsRunning()) { | |
476 return; | |
477 } | |
478 Isolate* isolate = Isolate::Current(); | |
479 ASSERT(isolate != NULL); | |
480 HANDLESCOPE(isolate); | |
481 | |
482 // Construct a list of the form [eventId, eventMessage]. | |
483 const Array& list = Array::Handle(Array::New(2)); | |
484 ASSERT(!list.IsNull()); | |
485 list.SetAt(0, Integer::Handle(Integer::New(eventId))); | |
486 list.SetAt(1, eventMessage); | |
487 | |
488 // Push the event to port_. | |
489 uint8_t* data = NULL; | |
490 MessageWriter writer(&data, &allocator, false); | |
491 writer.WriteMessage(list); | |
492 intptr_t len = writer.BytesWritten(); | |
493 if (FLAG_trace_service) { | |
494 OS::Print("vm-service: Pushing event of type %" Pd ", len %" Pd "\n", | |
495 eventId, len); | |
496 } | |
497 // TODO(turnidge): For now we ignore failure to send an event. Revisit? | |
498 PortMap::PostMessage( | |
499 new Message(ServiceIsolate::Port(), data, len, Message::kNormalPriority)); | |
500 } | |
501 | |
502 | |
503 void Service::SendEvent(intptr_t eventId, | |
504 const String& meta, | |
505 const uint8_t* data, | |
506 intptr_t size) { | |
507 // Bitstream: [meta data size (big-endian 64 bit)] [meta data (UTF-8)] [data] | |
508 const intptr_t meta_bytes = Utf8::Length(meta); | |
509 const intptr_t total_bytes = sizeof(uint64_t) + meta_bytes + size; | |
510 const TypedData& message = TypedData::Handle( | |
511 TypedData::New(kTypedDataUint8ArrayCid, total_bytes)); | |
512 intptr_t offset = 0; | |
513 // TODO(koda): Rename these methods SetHostUint64, etc. | |
514 message.SetUint64(0, Utils::HostToBigEndian64(meta_bytes)); | |
515 offset += sizeof(uint64_t); | |
516 { | |
517 NoGCScope no_gc; | |
518 meta.ToUTF8(static_cast<uint8_t*>(message.DataAddr(offset)), meta_bytes); | |
519 offset += meta_bytes; | |
520 } | |
521 // TODO(koda): It would be nice to avoid this copy (requires changes to | |
522 // MessageWriter code). | |
523 { | |
524 NoGCScope no_gc; | |
525 memmove(message.DataAddr(offset), data, size); | |
526 offset += size; | |
527 } | |
528 ASSERT(offset == total_bytes); | |
529 SendEvent(eventId, message); | |
530 } | |
531 | |
532 | |
533 void Service::HandleGCEvent(GCEvent* event) { | |
534 JSONStream js; | |
535 event->PrintJSON(&js); | |
536 const String& message = String::Handle(String::New(js.ToCString())); | |
537 SendEvent(kEventFamilyGC, message); | |
538 } | |
539 | |
540 | |
541 void Service::HandleDebuggerEvent(DebuggerEvent* event) { | |
542 JSONStream js; | |
543 event->PrintJSON(&js); | |
544 const String& message = String::Handle(String::New(js.ToCString())); | |
545 SendEvent(kEventFamilyDebug, message); | |
546 } | |
115 | 547 |
116 | 548 |
117 class EmbedderServiceHandler { | 549 class EmbedderServiceHandler { |
118 public: | 550 public: |
119 explicit EmbedderServiceHandler(const char* name) : name_(NULL), | 551 explicit EmbedderServiceHandler(const char* name) : name_(NULL), |
120 callback_(NULL), | 552 callback_(NULL), |
121 user_data_(NULL), | 553 user_data_(NULL), |
122 next_(NULL) { | 554 next_(NULL) { |
123 ASSERT(name != NULL); | 555 ASSERT(name != NULL); |
124 name_ = strdup(name); | 556 name_ = strdup(name); |
(...skipping 21 matching lines...) Expand all Loading... | |
146 } | 578 } |
147 | 579 |
148 private: | 580 private: |
149 char* name_; | 581 char* name_; |
150 Dart_ServiceRequestCallback callback_; | 582 Dart_ServiceRequestCallback callback_; |
151 void* user_data_; | 583 void* user_data_; |
152 EmbedderServiceHandler* next_; | 584 EmbedderServiceHandler* next_; |
153 }; | 585 }; |
154 | 586 |
155 | 587 |
156 class LibraryCoverageFilter : public CoverageFilter { | 588 void Service::EmbedderHandleMessage(EmbedderServiceHandler* handler, |
157 public: | 589 JSONStream* js) { |
158 explicit LibraryCoverageFilter(const Library& lib) : lib_(lib) {} | 590 ASSERT(handler != NULL); |
159 bool ShouldOutputCoverageFor(const Library& lib, | 591 Dart_ServiceRequestCallback callback = handler->callback(); |
160 const Script& script, | 592 ASSERT(callback != NULL); |
161 const Class& cls, | 593 const char* r = NULL; |
162 const Function& func) const { | 594 const char* name = js->method(); |
163 return lib.raw() == lib_.raw(); | 595 const char** keys = js->param_keys(); |
164 } | 596 const char** values = js->param_values(); |
165 private: | 597 r = callback(name, keys, values, js->num_params(), handler->user_data()); |
166 const Library& lib_; | 598 ASSERT(r != NULL); |
167 }; | 599 // TODO(johnmccutchan): Allow for NULL returns? |
168 | 600 TextBuffer* buffer = js->buffer(); |
169 | 601 buffer->AddString(r); |
170 class ScriptCoverageFilter : public CoverageFilter { | 602 free(const_cast<char*>(r)); |
171 public: | 603 } |
172 explicit ScriptCoverageFilter(const Script& script) | 604 |
173 : script_(script) {} | 605 |
174 bool ShouldOutputCoverageFor(const Library& lib, | 606 void Service::RegisterIsolateEmbedderCallback( |
175 const Script& script, | 607 const char* name, |
176 const Class& cls, | 608 Dart_ServiceRequestCallback callback, |
177 const Function& func) const { | 609 void* user_data) { |
178 return script.raw() == script_.raw(); | 610 if (name == NULL) { |
179 } | 611 return; |
180 private: | 612 } |
181 const Script& script_; | 613 EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(name); |
182 }; | 614 if (handler != NULL) { |
183 | 615 // Update existing handler entry. |
184 | 616 handler->set_callback(callback); |
185 class ClassCoverageFilter : public CoverageFilter { | 617 handler->set_user_data(user_data); |
186 public: | 618 return; |
187 explicit ClassCoverageFilter(const Class& cls) : cls_(cls) {} | 619 } |
188 bool ShouldOutputCoverageFor(const Library& lib, | 620 // Create a new handler. |
189 const Script& script, | 621 handler = new EmbedderServiceHandler(name); |
190 const Class& cls, | 622 handler->set_callback(callback); |
191 const Function& func) const { | 623 handler->set_user_data(user_data); |
192 return cls.raw() == cls_.raw(); | 624 |
193 } | 625 // Insert into isolate_service_handler_head_ list. |
194 private: | 626 handler->set_next(isolate_service_handler_head_); |
195 const Class& cls_; | 627 isolate_service_handler_head_ = handler; |
196 }; | 628 } |
197 | 629 |
198 | 630 |
199 class FunctionCoverageFilter : public CoverageFilter { | 631 EmbedderServiceHandler* Service::FindIsolateEmbedderHandler( |
200 public: | 632 const char* name) { |
201 explicit FunctionCoverageFilter(const Function& func) : func_(func) {} | 633 EmbedderServiceHandler* current = isolate_service_handler_head_; |
202 bool ShouldOutputCoverageFor(const Library& lib, | 634 while (current != NULL) { |
203 const Script& script, | 635 if (strcmp(name, current->name()) == 0) { |
204 const Class& cls, | 636 return current; |
205 const Function& func) const { | 637 } |
206 return func.raw() == func_.raw(); | 638 current = current->next(); |
207 } | |
208 private: | |
209 const Function& func_; | |
210 }; | |
211 | |
212 | |
213 static uint8_t* allocator(uint8_t* ptr, intptr_t old_size, intptr_t new_size) { | |
214 void* new_ptr = realloc(reinterpret_cast<void*>(ptr), new_size); | |
215 return reinterpret_cast<uint8_t*>(new_ptr); | |
216 } | |
217 | |
218 | |
219 static void SendIsolateServiceMessage(Dart_NativeArguments args) { | |
220 NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); | |
221 Isolate* isolate = arguments->isolate(); | |
222 StackZone zone(isolate); | |
223 HANDLESCOPE(isolate); | |
224 GET_NON_NULL_NATIVE_ARGUMENT(SendPort, sp, arguments->NativeArgAt(0)); | |
225 GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(1)); | |
226 | |
227 // Set the type of the OOB message. | |
228 message.SetAt(0, Smi::Handle(isolate, Smi::New(Message::kServiceOOBMsg))); | |
229 | |
230 // Serialize message. | |
231 uint8_t* data = NULL; | |
232 MessageWriter writer(&data, &allocator, false); | |
233 writer.WriteMessage(message); | |
234 | |
235 // TODO(turnidge): Throw an exception when the return value is false? | |
236 PortMap::PostMessage(new Message(sp.Id(), data, writer.BytesWritten(), | |
237 Message::kOOBPriority)); | |
238 } | |
239 | |
240 | |
241 static void SendRootServiceMessage(Dart_NativeArguments args) { | |
242 NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); | |
243 Isolate* isolate = arguments->isolate(); | |
244 StackZone zone(isolate); | |
245 HANDLESCOPE(isolate); | |
246 GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(0)); | |
247 Service::HandleRootMessage(message); | |
248 } | |
249 | |
250 | |
251 class ScopeStopwatch : public ValueObject { | |
252 public: | |
253 explicit ScopeStopwatch(const char* name) : name_(name) { | |
254 start_ = OS::GetCurrentTimeMicros(); | |
255 } | |
256 | |
257 int64_t GetElapsed() const { | |
258 int64_t end = OS::GetCurrentTimeMicros(); | |
259 ASSERT(end >= start_); | |
260 return end - start_; | |
261 } | |
262 | |
263 ~ScopeStopwatch() { | |
264 int64_t elapsed = GetElapsed(); | |
265 OS::Print("[%" Pd "] %s took %" Pd64 " micros.\n", | |
266 OS::ProcessId(), name_, elapsed); | |
267 } | |
268 | |
269 private: | |
270 const char* name_; | |
271 int64_t start_; | |
272 }; | |
273 | |
274 | |
275 bool Service::IsRunning() { | |
276 MonitorLocker ml(monitor_); | |
277 return (service_port_ != ILLEGAL_PORT) && (service_isolate_ != NULL); | |
278 } | |
279 | |
280 | |
281 void Service::SetServicePort(Dart_Port port) { | |
282 MonitorLocker ml(monitor_); | |
283 service_port_ = port; | |
284 } | |
285 | |
286 | |
287 void Service::SetServiceIsolate(Isolate* isolate) { | |
288 MonitorLocker ml(monitor_); | |
289 service_isolate_ = isolate; | |
290 if (service_isolate_ != NULL) { | |
291 service_isolate_->is_service_isolate_ = true; | |
292 } | |
293 } | |
294 | |
295 | |
296 bool Service::HasServiceIsolate() { | |
297 MonitorLocker ml(monitor_); | |
298 return service_isolate_ != NULL; | |
299 } | |
300 | |
301 | |
302 bool Service::IsServiceIsolate(Isolate* isolate) { | |
303 MonitorLocker ml(monitor_); | |
304 return isolate == service_isolate_; | |
305 } | |
306 | |
307 Dart_Port Service::WaitForLoadPort() { | |
308 MonitorLocker ml(monitor_); | |
309 | |
310 while (initializing_ && (load_port_ == ILLEGAL_PORT)) { | |
311 ml.Wait(); | |
312 } | |
313 | |
314 return load_port_; | |
315 } | |
316 | |
317 | |
318 Dart_Port Service::LoadPort() { | |
319 MonitorLocker ml(monitor_); | |
320 return load_port_; | |
321 } | |
322 | |
323 | |
324 void Service::SetLoadPort(Dart_Port port) { | |
325 MonitorLocker ml(monitor_); | |
326 load_port_ = port; | |
327 } | |
328 | |
329 | |
330 void Service::SetEventMask(uint32_t mask) { | |
331 event_mask_ = mask; | |
332 } | |
333 | |
334 | |
335 static void SetEventMask(Dart_NativeArguments args) { | |
336 NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); | |
337 Isolate* isolate = arguments->isolate(); | |
338 StackZone zone(isolate); | |
339 HANDLESCOPE(isolate); | |
340 GET_NON_NULL_NATIVE_ARGUMENT(Integer, mask, arguments->NativeArgAt(0)); | |
341 Service::SetEventMask(mask.AsTruncatedUint32Value()); | |
342 } | |
343 | |
344 | |
345 // These must be kept in sync with service/constants.dart | |
346 #define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1 | |
347 #define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2 | |
348 | |
349 | |
350 static RawArray* MakeServiceControlMessage(Dart_Port port_id, intptr_t code, | |
351 const String& name) { | |
352 const Array& list = Array::Handle(Array::New(4)); | |
353 ASSERT(!list.IsNull()); | |
354 const Integer& code_int = Integer::Handle(Integer::New(code)); | |
355 const Integer& port_int = Integer::Handle(Integer::New(port_id)); | |
356 const SendPort& send_port = SendPort::Handle(SendPort::New(port_id)); | |
357 list.SetAt(0, code_int); | |
358 list.SetAt(1, port_int); | |
359 list.SetAt(2, send_port); | |
360 list.SetAt(3, name); | |
361 return list.raw(); | |
362 } | |
363 | |
364 | |
365 class RegisterRunningIsolatesVisitor : public IsolateVisitor { | |
366 public: | |
367 explicit RegisterRunningIsolatesVisitor(Isolate* service_isolate) | |
368 : IsolateVisitor(), | |
369 register_function_(Function::Handle(service_isolate)), | |
370 service_isolate_(service_isolate) { | |
371 ASSERT(Service::IsServiceIsolate(Isolate::Current())); | |
372 // Get library. | |
373 const String& library_url = Symbols::DartVMService(); | |
374 ASSERT(!library_url.IsNull()); | |
375 const Library& library = | |
376 Library::Handle(Library::LookupLibrary(library_url)); | |
377 ASSERT(!library.IsNull()); | |
378 // Get function. | |
379 const String& function_name = | |
380 String::Handle(String::New("_registerIsolate")); | |
381 ASSERT(!function_name.IsNull()); | |
382 register_function_ = library.LookupFunctionAllowPrivate(function_name); | |
383 ASSERT(!register_function_.IsNull()); | |
384 } | |
385 | |
386 virtual void VisitIsolate(Isolate* isolate) { | |
387 ASSERT(Service::IsServiceIsolate(Isolate::Current())); | |
388 if (Service::IsServiceIsolate(isolate) || | |
389 (isolate == Dart::vm_isolate())) { | |
390 // We do not register the service or vm isolate. | |
391 return; | |
392 } | |
393 // Setup arguments for call. | |
394 Dart_Port port_id = isolate->main_port(); | |
395 const Integer& port_int = Integer::Handle(Integer::New(port_id)); | |
396 ASSERT(!port_int.IsNull()); | |
397 const SendPort& send_port = SendPort::Handle(SendPort::New(port_id)); | |
398 const String& name = String::Handle(String::New(isolate->name())); | |
399 ASSERT(!name.IsNull()); | |
400 const Array& args = Array::Handle(Array::New(3)); | |
401 ASSERT(!args.IsNull()); | |
402 args.SetAt(0, port_int); | |
403 args.SetAt(1, send_port); | |
404 args.SetAt(2, name); | |
405 Object& r = Object::Handle(service_isolate_); | |
406 r = DartEntry::InvokeFunction(register_function_, args); | |
407 if (FLAG_trace_service) { | |
408 OS::Print("vm-service: Isolate %s %" Pd64 " registered.\n", | |
409 name.ToCString(), | |
410 port_id); | |
411 } | |
412 ASSERT(!r.IsError()); | |
413 } | |
414 | |
415 private: | |
416 Function& register_function_; | |
417 Isolate* service_isolate_; | |
418 }; | |
419 | |
420 | |
421 static Dart_Port ExtractPort(Isolate* isolate, Dart_Handle receivePort) { | |
422 const ReceivePort& rp = Api::UnwrapReceivePortHandle(isolate, receivePort); | |
423 if (rp.IsNull()) { | |
424 return ILLEGAL_PORT; | |
425 } | |
426 return rp.Id(); | |
427 } | |
428 | |
429 | |
430 static void OnStart(Dart_NativeArguments args) { | |
431 NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); | |
432 Isolate* isolate = arguments->isolate(); | |
433 StackZone zone(isolate); | |
434 HANDLESCOPE(isolate); | |
435 { | |
436 if (FLAG_trace_service) { | |
437 OS::Print("vm-service: Booting dart:vmservice library.\n"); | |
438 } | |
439 // Boot the dart:vmservice library. | |
440 Dart_EnterScope(); | |
441 Dart_Handle url_str = | |
442 Dart_NewStringFromCString(Symbols::Name(Symbols::kDartVMServiceId)); | |
443 Dart_Handle library = Dart_LookupLibrary(url_str); | |
444 ASSERT(Dart_IsLibrary(library)); | |
445 Dart_Handle result = | |
446 Dart_Invoke(library, Dart_NewStringFromCString("boot"), 0, NULL); | |
447 ASSERT(!Dart_IsError(result)); | |
448 Dart_Port port = ExtractPort(isolate, result); | |
449 ASSERT(port != ILLEGAL_PORT); | |
450 Service::SetServicePort(port); | |
451 Dart_ExitScope(); | |
452 } | |
453 | |
454 { | |
455 if (FLAG_trace_service) { | |
456 OS::Print("vm-service: Registering running isolates.\n"); | |
457 } | |
458 // Register running isolates with service. | |
459 RegisterRunningIsolatesVisitor register_isolates(isolate); | |
460 Isolate::VisitIsolates(®ister_isolates); | |
461 } | |
462 } | |
463 | |
464 | |
465 struct VmServiceNativeEntry { | |
466 const char* name; | |
467 int num_arguments; | |
468 Dart_NativeFunction function; | |
469 }; | |
470 | |
471 | |
472 static VmServiceNativeEntry _VmServiceNativeEntries[] = { | |
473 {"VMService_SendIsolateServiceMessage", 2, SendIsolateServiceMessage}, | |
474 {"VMService_SendRootServiceMessage", 1, SendRootServiceMessage}, | |
475 {"VMService_SetEventMask", 1, SetEventMask}, | |
476 {"VMService_OnStart", 0, OnStart }, | |
477 }; | |
478 | |
479 | |
480 static Dart_NativeFunction VmServiceNativeResolver(Dart_Handle name, | |
481 int num_arguments, | |
482 bool* auto_setup_scope) { | |
483 const Object& obj = Object::Handle(Api::UnwrapHandle(name)); | |
484 if (!obj.IsString()) { | |
485 return NULL; | |
486 } | |
487 const char* function_name = obj.ToCString(); | |
488 ASSERT(function_name != NULL); | |
489 ASSERT(auto_setup_scope != NULL); | |
490 *auto_setup_scope = true; | |
491 intptr_t n = | |
492 sizeof(_VmServiceNativeEntries) / sizeof(_VmServiceNativeEntries[0]); | |
493 for (intptr_t i = 0; i < n; i++) { | |
494 VmServiceNativeEntry entry = _VmServiceNativeEntries[i]; | |
495 if ((strcmp(function_name, entry.name) == 0) && | |
496 (num_arguments == entry.num_arguments)) { | |
497 return entry.function; | |
498 } | |
499 } | 639 } |
500 return NULL; | 640 return NULL; |
501 } | 641 } |
502 | 642 |
503 const char* Service::kIsolateName = "vm-service"; | 643 |
504 EmbedderServiceHandler* Service::isolate_service_handler_head_ = NULL; | 644 void Service::RegisterRootEmbedderCallback( |
505 EmbedderServiceHandler* Service::root_service_handler_head_ = NULL; | 645 const char* name, |
506 Isolate* Service::service_isolate_ = NULL; | 646 Dart_ServiceRequestCallback callback, |
507 Dart_Port Service::service_port_ = ILLEGAL_PORT; | 647 void* user_data) { |
508 Dart_Port Service::load_port_ = ILLEGAL_PORT; | 648 if (name == NULL) { |
509 Dart_IsolateCreateCallback Service::create_callback_ = NULL; | |
510 Monitor* Service::monitor_ = NULL; | |
511 bool Service::initializing_ = true; | |
512 uint32_t Service::event_mask_ = 0; | |
513 | |
514 | |
515 bool Service::IsServiceIsolateName(const char* name) { | |
516 ASSERT(name != NULL); | |
517 return strcmp(name, kIsolateName) == 0; | |
518 } | |
519 | |
520 | |
521 bool Service::SendIsolateStartupMessage() { | |
522 if (!IsRunning()) { | |
523 return false; | |
524 } | |
525 Isolate* isolate = Isolate::Current(); | |
526 if (IsServiceIsolate(isolate)) { | |
527 return false; | |
528 } | |
529 ASSERT(isolate != NULL); | |
530 HANDLESCOPE(isolate); | |
531 const String& name = String::Handle(String::New(isolate->name())); | |
532 ASSERT(!name.IsNull()); | |
533 const Array& list = Array::Handle( | |
534 MakeServiceControlMessage(Dart_GetMainPortId(), | |
535 VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID, | |
536 name)); | |
537 ASSERT(!list.IsNull()); | |
538 uint8_t* data = NULL; | |
539 MessageWriter writer(&data, &allocator, false); | |
540 writer.WriteMessage(list); | |
541 intptr_t len = writer.BytesWritten(); | |
542 if (FLAG_trace_service) { | |
543 OS::Print("vm-service: Isolate %s %" Pd64 " registered.\n", | |
544 name.ToCString(), | |
545 Dart_GetMainPortId()); | |
546 } | |
547 return PortMap::PostMessage( | |
548 new Message(service_port_, data, len, Message::kNormalPriority)); | |
549 } | |
550 | |
551 | |
552 bool Service::SendIsolateShutdownMessage() { | |
553 if (!IsRunning()) { | |
554 return false; | |
555 } | |
556 Isolate* isolate = Isolate::Current(); | |
557 if (IsServiceIsolate(isolate)) { | |
558 return false; | |
559 } | |
560 ASSERT(isolate != NULL); | |
561 HANDLESCOPE(isolate); | |
562 const String& name = String::Handle(String::New(isolate->name())); | |
563 ASSERT(!name.IsNull()); | |
564 const Array& list = Array::Handle( | |
565 MakeServiceControlMessage(Dart_GetMainPortId(), | |
566 VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID, | |
567 name)); | |
568 ASSERT(!list.IsNull()); | |
569 uint8_t* data = NULL; | |
570 MessageWriter writer(&data, &allocator, false); | |
571 writer.WriteMessage(list); | |
572 intptr_t len = writer.BytesWritten(); | |
573 if (FLAG_trace_service) { | |
574 OS::Print("vm-service: Isolate %s %" Pd64 " deregistered.\n", | |
575 name.ToCString(), | |
576 Dart_GetMainPortId()); | |
577 } | |
578 return PortMap::PostMessage( | |
579 new Message(service_port_, data, len, Message::kNormalPriority)); | |
580 } | |
581 | |
582 | |
583 Dart_Handle Service::GetSource(const char* name) { | |
584 ASSERT(name != NULL); | |
585 int i = 0; | |
586 while (true) { | |
587 const char* path = Resources::Path(i); | |
588 if (path == NULL) { | |
589 break; | |
590 } | |
591 ASSERT(*path != '\0'); | |
592 // Skip the '/'. | |
593 path++; | |
594 if (strcmp(name, path) == 0) { | |
595 const uint8_t* str = Resources::Resource(i); | |
596 intptr_t length = Resources::Length(i); | |
597 return Dart_NewStringFromUTF8(str, length); | |
598 } | |
599 i++; | |
600 } | |
601 return Dart_Null(); | |
602 } | |
603 | |
604 | |
605 Dart_Handle Service::LibraryTagHandler(Dart_LibraryTag tag, | |
606 Dart_Handle library, | |
607 Dart_Handle url) { | |
608 if (tag == Dart_kCanonicalizeUrl) { | |
609 // url is already canonicalized. | |
610 return url; | |
611 } | |
612 if (tag != Dart_kSourceTag) { | |
613 FATAL("Service::LibraryTagHandler encountered an unexpected tag."); | |
614 } | |
615 ASSERT(tag == Dart_kSourceTag); | |
616 const char* url_string = NULL; | |
617 Dart_Handle result = Dart_StringToCString(url, &url_string); | |
618 if (Dart_IsError(result)) { | |
619 return result; | |
620 } | |
621 Dart_Handle source = GetSource(url_string); | |
622 if (Dart_IsError(source)) { | |
623 return source; | |
624 } | |
625 return Dart_LoadSource(library, url, source, 0, 0); | |
626 } | |
627 | |
628 | |
629 void Service::MaybeInjectVMServiceLibrary(Isolate* isolate) { | |
630 ASSERT(isolate != NULL); | |
631 ASSERT(isolate->name() != NULL); | |
632 if (!Service::IsServiceIsolateName(isolate->name())) { | |
633 // Not service isolate. | |
634 return; | 649 return; |
635 } | 650 } |
636 if (HasServiceIsolate()) { | 651 EmbedderServiceHandler* handler = FindRootEmbedderHandler(name); |
637 // Service isolate already exists. | 652 if (handler != NULL) { |
653 // Update existing handler entry. | |
654 handler->set_callback(callback); | |
655 handler->set_user_data(user_data); | |
638 return; | 656 return; |
639 } | 657 } |
640 SetServiceIsolate(isolate); | 658 // Create a new handler. |
641 | 659 handler = new EmbedderServiceHandler(name); |
642 StackZone zone(isolate); | 660 handler->set_callback(callback); |
643 HANDLESCOPE(isolate); | 661 handler->set_user_data(user_data); |
644 | 662 |
645 // Register dart:vmservice library. | 663 // Insert into root_service_handler_head_ list. |
646 const String& url_str = String::Handle(Symbols::DartVMService().raw()); | 664 handler->set_next(root_service_handler_head_); |
647 const Library& library = Library::Handle(Library::New(url_str)); | 665 root_service_handler_head_ = handler; |
648 library.Register(); | 666 } |
649 library.set_native_entry_resolver(VmServiceNativeResolver); | 667 |
650 | 668 |
651 // Temporarily install our library tag handler. | 669 EmbedderServiceHandler* Service::FindRootEmbedderHandler( |
652 isolate->set_library_tag_handler(LibraryTagHandler); | 670 const char* name) { |
653 | 671 EmbedderServiceHandler* current = root_service_handler_head_; |
654 // Get script source. | 672 while (current != NULL) { |
655 const char* resource = NULL; | 673 if (strcmp(name, current->name()) == 0) { |
656 const char* path = "/vmservice.dart"; | 674 return current; |
657 intptr_t r = Resources::ResourceLookup(path, &resource); | 675 } |
658 ASSERT(r != Resources::kNoSuchInstance); | 676 current = current->next(); |
659 ASSERT(resource != NULL); | 677 } |
660 const String& source_str = String::Handle( | 678 return NULL; |
661 String::FromUTF8(reinterpret_cast<const uint8_t*>(resource), r)); | 679 } |
662 ASSERT(!source_str.IsNull()); | 680 |
663 const Script& script = Script::Handle( | 681 |
664 isolate, Script::New(url_str, source_str, RawScript::kLibraryTag)); | |
665 | |
666 // Compile script. | |
667 Dart_EnterScope(); // Need to enter scope for tag handler. | |
668 library.SetLoadInProgress(); | |
669 const Error& error = Error::Handle(isolate, | |
670 Compiler::Compile(library, script)); | |
671 ASSERT(error.IsNull()); | |
672 Dart_Handle result = Dart_FinalizeLoading(false); | |
673 ASSERT(!Dart_IsError(result)); | |
674 Dart_ExitScope(); | |
675 | |
676 // Uninstall our library tag handler. | |
677 isolate->set_library_tag_handler(NULL); | |
678 } | |
679 | |
680 | |
681 void Service::FinishedInitializing() { | |
682 MonitorLocker ml(monitor_); | |
683 initializing_ = false; | |
684 ml.NotifyAll(); | |
685 } | |
686 | |
687 | |
688 static void ShutdownIsolate(uword parameter) { | |
689 Isolate* isolate = reinterpret_cast<Isolate*>(parameter); | |
690 ASSERT(Service::IsServiceIsolate(isolate)); | |
691 { | |
692 // Print the error if there is one. This may execute dart code to | |
693 // print the exception object, so we need to use a StartIsolateScope. | |
694 StartIsolateScope start_scope(isolate); | |
695 StackZone zone(isolate); | |
696 HandleScope handle_scope(isolate); | |
697 Error& error = Error::Handle(); | |
698 error = isolate->object_store()->sticky_error(); | |
699 if (!error.IsNull()) { | |
700 OS::PrintErr("vm-service: Error: %s\n", error.ToErrorCString()); | |
701 } | |
702 Dart::RunShutdownCallback(); | |
703 } | |
704 { | |
705 // Shut the isolate down. | |
706 SwitchIsolateScope switch_scope(isolate); | |
707 Dart::ShutdownIsolate(); | |
708 } | |
709 Service::SetServiceIsolate(NULL); | |
710 Service::SetServicePort(ILLEGAL_PORT); | |
711 if (FLAG_trace_service) { | |
712 OS::Print("vm-service: Shutdown.\n"); | |
713 } | |
714 } | |
715 | |
716 | |
717 class RunServiceTask : public ThreadPool::Task { | |
718 public: | |
719 virtual void Run() { | |
720 ASSERT(Isolate::Current() == NULL); | |
721 char* error = NULL; | |
722 Isolate* isolate = NULL; | |
723 | |
724 Dart_IsolateCreateCallback create_callback = Service::create_callback(); | |
725 // TODO(johnmccutchan): Support starting up service isolate without embedder | |
726 // provided isolate creation callback. | |
727 if (create_callback == NULL) { | |
728 Service::FinishedInitializing(); | |
729 return; | |
730 } | |
731 | |
732 isolate = | |
733 reinterpret_cast<Isolate*>(create_callback(Service::kIsolateName, | |
734 NULL, | |
735 NULL, | |
736 NULL, | |
737 &error)); | |
738 if (isolate == NULL) { | |
739 OS::PrintErr("vm-service: Isolate creation error: %s\n", error); | |
740 Service::FinishedInitializing(); | |
741 return; | |
742 } | |
743 | |
744 Isolate::SetCurrent(NULL); | |
745 | |
746 RunMain(isolate); | |
747 | |
748 Service::FinishedInitializing(); | |
749 | |
750 isolate->message_handler()->Run(Dart::thread_pool(), | |
751 NULL, | |
752 ShutdownIsolate, | |
753 reinterpret_cast<uword>(isolate)); | |
754 } | |
755 | |
756 protected: | |
757 void RunMain(Isolate* isolate) { | |
758 StartIsolateScope iso_scope(isolate); | |
759 StackZone zone(isolate); | |
760 HANDLESCOPE(isolate); | |
761 // Invoke main which will return the loadScriptPort. | |
762 const Library& root_library = | |
763 Library::Handle(isolate, isolate->object_store()->root_library()); | |
764 if (root_library.IsNull()) { | |
765 if (FLAG_trace_service) { | |
766 OS::Print("vm-service: Embedder did not install a script."); | |
767 } | |
768 // Service isolate is not supported by embedder. | |
769 return; | |
770 } | |
771 ASSERT(!root_library.IsNull()); | |
772 const String& entry_name = String::Handle(isolate, String::New("main")); | |
773 ASSERT(!entry_name.IsNull()); | |
774 const Function& entry = | |
775 Function::Handle(isolate, | |
776 root_library.LookupFunctionAllowPrivate(entry_name)); | |
777 if (entry.IsNull()) { | |
778 // Service isolate is not supported by embedder. | |
779 if (FLAG_trace_service) { | |
780 OS::Print("vm-service: Embedder did not provide a main function."); | |
781 } | |
782 return; | |
783 } | |
784 ASSERT(!entry.IsNull()); | |
785 const Object& result = | |
786 Object::Handle(isolate, | |
787 DartEntry::InvokeFunction(entry, | |
788 Object::empty_array())); | |
789 ASSERT(!result.IsNull()); | |
790 if (result.IsError()) { | |
791 // Service isolate did not initialize properly. | |
792 if (FLAG_trace_service) { | |
793 const Error& error = Error::Cast(result); | |
794 OS::Print("vm-service: Calling main resulted in an error: %s", | |
795 error.ToErrorCString()); | |
796 } | |
797 return; | |
798 } | |
799 ASSERT(result.IsReceivePort()); | |
800 const ReceivePort& rp = ReceivePort::Cast(result); | |
801 Service::SetLoadPort(rp.Id()); | |
802 } | |
803 }; | |
804 | |
805 | |
806 void Service::RunService() { | |
807 ASSERT(monitor_ == NULL); | |
808 monitor_ = new Monitor(); | |
809 ASSERT(monitor_ != NULL); | |
810 // Grab the isolate create callback here to avoid race conditions with tests | |
811 // that change this after Dart_Initialize returns. | |
812 create_callback_ = Isolate::CreateCallback(); | |
813 Dart::thread_pool()->Run(new RunServiceTask()); | |
814 } | |
815 | |
816 // A handler for a per-isolate request. | |
817 // | |
818 // If a handler returns true, the reply is complete and ready to be | |
819 // posted. If a handler returns false, then it is responsible for | |
820 // posting the reply (this can be used for asynchronous delegation of | |
821 // the response handling). | |
822 typedef bool (*IsolateMessageHandler)(Isolate* isolate, JSONStream* stream); | |
823 | |
824 struct IsolateMessageHandlerEntry { | |
825 const char* method; | |
826 IsolateMessageHandler handler; | |
827 }; | |
828 | |
829 static IsolateMessageHandler FindIsolateMessageHandler(const char* method); | |
830 | |
831 | |
832 // A handler for a root (vm-global) request. | |
833 // | |
834 // If a handler returns true, the reply is complete and ready to be | |
835 // posted. If a handler returns false, then it is responsible for | |
836 // posting the reply (this can be used for asynchronous delegation of | |
837 // the response handling). | |
838 typedef bool (*RootMessageHandler)(JSONStream* stream); | |
839 | |
840 struct RootMessageHandlerEntry { | |
841 const char* method; | |
842 RootMessageHandler handler; | |
843 }; | |
844 | |
845 static RootMessageHandler FindRootMessageHandler(const char* method); | |
846 | |
847 | |
848 static void PrintRequest(const JSONObject& obj, JSONStream* js) { | |
849 JSONObject jsobj(&obj, "request"); | |
850 jsobj.AddProperty("method", js->method()); | |
851 { | |
852 JSONArray jsarr(&jsobj, "param_keys"); | |
853 for (intptr_t i = 0; i < js->num_params(); i++) { | |
854 jsarr.AddValue(js->GetParamKey(i)); | |
855 } | |
856 } | |
857 { | |
858 JSONArray jsarr(&jsobj, "param_values"); | |
859 for (intptr_t i = 0; i < js->num_params(); i++) { | |
860 jsarr.AddValue(js->GetParamValue(i)); | |
861 } | |
862 } | |
863 } | |
864 | |
865 | |
866 static void PrintError(JSONStream* js, | |
867 const char* format, ...) { | |
868 Isolate* isolate = Isolate::Current(); | |
869 | |
870 va_list args; | |
871 va_start(args, format); | |
872 intptr_t len = OS::VSNPrint(NULL, 0, format, args); | |
873 va_end(args); | |
874 | |
875 char* buffer = isolate->current_zone()->Alloc<char>(len + 1); | |
876 va_list args2; | |
877 va_start(args2, format); | |
878 OS::VSNPrint(buffer, (len + 1), format, args2); | |
879 va_end(args2); | |
880 | |
881 JSONObject jsobj(js); | |
882 jsobj.AddProperty("type", "Error"); | |
883 jsobj.AddProperty("message", buffer); | |
884 PrintRequest(jsobj, js); | |
885 } | |
886 | |
887 | |
888 static void PrintMissingParamError(JSONStream* js, | |
889 const char* param) { | |
890 PrintError(js, "%s expects the '%s' parameter", | |
891 js->method(), param); | |
892 } | |
893 | |
894 | |
895 static void PrintInvalidParamError(JSONStream* js, | |
896 const char* param) { | |
897 PrintError(js, "%s: invalid '%s' parameter: %s", | |
898 js->method(), param, js->LookupParam(param)); | |
899 } | |
900 | |
901 | |
902 static void PrintErrorWithKind(JSONStream* js, | |
903 const char* kind, | |
904 const char* format, ...) { | |
905 Isolate* isolate = Isolate::Current(); | |
906 | |
907 va_list args; | |
908 va_start(args, format); | |
909 intptr_t len = OS::VSNPrint(NULL, 0, format, args); | |
910 va_end(args); | |
911 | |
912 char* buffer = isolate->current_zone()->Alloc<char>(len + 1); | |
913 va_list args2; | |
914 va_start(args2, format); | |
915 OS::VSNPrint(buffer, (len + 1), format, args2); | |
916 va_end(args2); | |
917 | |
918 JSONObject jsobj(js); | |
919 jsobj.AddProperty("type", "Error"); | |
920 jsobj.AddProperty("id", ""); | |
921 jsobj.AddProperty("kind", kind); | |
922 jsobj.AddProperty("message", buffer); | |
923 PrintRequest(jsobj, js); | |
924 } | |
925 | |
926 | |
927 void Service::HandleIsolateMessage(Isolate* isolate, const Array& msg) { | |
928 ASSERT(isolate != NULL); | |
929 ASSERT(!msg.IsNull()); | |
930 ASSERT(msg.Length() == 5); | |
931 | |
932 { | |
933 StackZone zone(isolate); | |
934 HANDLESCOPE(isolate); | |
935 | |
936 Instance& reply_port = Instance::Handle(isolate); | |
937 String& method = String::Handle(isolate); | |
938 Array& param_keys = Array::Handle(isolate); | |
939 Array& param_values = Array::Handle(isolate); | |
940 reply_port ^= msg.At(1); | |
941 method ^= msg.At(2); | |
942 param_keys ^= msg.At(3); | |
943 param_values ^= msg.At(4); | |
944 | |
945 ASSERT(!method.IsNull()); | |
946 ASSERT(!param_keys.IsNull()); | |
947 ASSERT(!param_values.IsNull()); | |
948 ASSERT(param_keys.Length() == param_values.Length()); | |
949 | |
950 if (!reply_port.IsSendPort()) { | |
951 FATAL("SendPort expected."); | |
952 } | |
953 | |
954 IsolateMessageHandler handler = | |
955 FindIsolateMessageHandler(method.ToCString()); | |
956 { | |
957 JSONStream js; | |
958 js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), | |
959 method, param_keys, param_values); | |
960 if (handler == NULL) { | |
961 // Check for an embedder handler. | |
962 EmbedderServiceHandler* e_handler = | |
963 FindIsolateEmbedderHandler(method.ToCString()); | |
964 if (e_handler != NULL) { | |
965 EmbedderHandleMessage(e_handler, &js); | |
966 } else { | |
967 if (FindRootMessageHandler(method.ToCString()) != NULL) { | |
968 PrintError(&js, "%s does not expect the 'isolateId' parameter", | |
969 method.ToCString()); | |
970 } else { | |
971 PrintError(&js, "Unrecognized method: %s", method.ToCString()); | |
972 } | |
973 } | |
974 js.PostReply(); | |
975 } else { | |
976 if (handler(isolate, &js)) { | |
977 // Handler returns true if the reply is ready to be posted. | |
978 // TODO(johnmccutchan): Support asynchronous replies. | |
979 js.PostReply(); | |
980 } | |
981 } | |
982 } | |
983 } | |
984 } | |
985 | |
986 | |
987 static bool HandleIsolate(Isolate* isolate, JSONStream* js) { | 682 static bool HandleIsolate(Isolate* isolate, JSONStream* js) { |
988 isolate->PrintJSON(js, false); | 683 isolate->PrintJSON(js, false); |
989 return true; | 684 return true; |
990 } | 685 } |
991 | 686 |
992 | 687 |
993 static bool HandleIsolateGetStack(Isolate* isolate, JSONStream* js) { | 688 static bool HandleIsolateGetStack(Isolate* isolate, JSONStream* js) { |
994 DebuggerStackTrace* stack = isolate->debugger()->StackTrace(); | 689 DebuggerStackTrace* stack = isolate->debugger()->StackTrace(); |
995 JSONObject jsobj(js); | 690 JSONObject jsobj(js); |
996 jsobj.AddProperty("type", "Stack"); | 691 jsobj.AddProperty("type", "Stack"); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1041 return HandleCommonEcho(&jsobj, js); | 736 return HandleCommonEcho(&jsobj, js); |
1042 } | 737 } |
1043 | 738 |
1044 | 739 |
1045 static bool HandleIsolateEcho(Isolate* isolate, JSONStream* js) { | 740 static bool HandleIsolateEcho(Isolate* isolate, JSONStream* js) { |
1046 JSONObject jsobj(js); | 741 JSONObject jsobj(js); |
1047 return HandleCommonEcho(&jsobj, js); | 742 return HandleCommonEcho(&jsobj, js); |
1048 } | 743 } |
1049 | 744 |
1050 | 745 |
1051 static bool GetIntegerId(const char* s, intptr_t* id, int base = 10) { | |
1052 if ((s == NULL) || (*s == '\0')) { | |
1053 // Empty string. | |
1054 return false; | |
1055 } | |
1056 if (id == NULL) { | |
1057 // No id pointer. | |
1058 return false; | |
1059 } | |
1060 intptr_t r = 0; | |
1061 char* end_ptr = NULL; | |
1062 r = strtol(s, &end_ptr, base); | |
1063 if (end_ptr == s) { | |
1064 // String was not advanced at all, cannot be valid. | |
1065 return false; | |
1066 } | |
1067 *id = r; | |
1068 return true; | |
1069 } | |
1070 | |
1071 | |
1072 static bool GetUnsignedIntegerId(const char* s, uintptr_t* id, int base = 10) { | |
1073 if ((s == NULL) || (*s == '\0')) { | |
1074 // Empty string. | |
1075 return false; | |
1076 } | |
1077 if (id == NULL) { | |
1078 // No id pointer. | |
1079 return false; | |
1080 } | |
1081 uintptr_t r = 0; | |
1082 char* end_ptr = NULL; | |
1083 r = strtoul(s, &end_ptr, base); | |
1084 if (end_ptr == s) { | |
1085 // String was not advanced at all, cannot be valid. | |
1086 return false; | |
1087 } | |
1088 *id = r; | |
1089 return true; | |
1090 } | |
1091 | |
1092 | |
1093 static bool GetInteger64Id(const char* s, int64_t* id, int base = 10) { | |
1094 if ((s == NULL) || (*s == '\0')) { | |
1095 // Empty string. | |
1096 return false; | |
1097 } | |
1098 if (id == NULL) { | |
1099 // No id pointer. | |
1100 return false; | |
1101 } | |
1102 int64_t r = 0; | |
1103 char* end_ptr = NULL; | |
1104 r = strtoll(s, &end_ptr, base); | |
1105 if (end_ptr == s) { | |
1106 // String was not advanced at all, cannot be valid. | |
1107 return false; | |
1108 } | |
1109 *id = r; | |
1110 return true; | |
1111 } | |
1112 | |
1113 // Scans the string until the '-' character. Returns pointer to string | |
1114 // at '-' character. Returns NULL if not found. | |
1115 static const char* ScanUntilDash(const char* s) { | |
1116 if ((s == NULL) || (*s == '\0')) { | |
1117 // Empty string. | |
1118 return NULL; | |
1119 } | |
1120 while (*s != '\0') { | |
1121 if (*s == '-') { | |
1122 return s; | |
1123 } | |
1124 s++; | |
1125 } | |
1126 return NULL; | |
1127 } | |
1128 | |
1129 | |
1130 static bool GetCodeId(const char* s, int64_t* timestamp, uword* address) { | |
1131 if ((s == NULL) || (*s == '\0')) { | |
1132 // Empty string. | |
1133 return false; | |
1134 } | |
1135 if ((timestamp == NULL) || (address == NULL)) { | |
1136 // Bad arguments. | |
1137 return false; | |
1138 } | |
1139 // Extract the timestamp. | |
1140 if (!GetInteger64Id(s, timestamp, 16) || (*timestamp < 0)) { | |
1141 return false; | |
1142 } | |
1143 s = ScanUntilDash(s); | |
1144 if (s == NULL) { | |
1145 return false; | |
1146 } | |
1147 // Skip the dash. | |
1148 s++; | |
1149 // Extract the PC. | |
1150 if (!GetUnsignedIntegerId(s, address, 16)) { | |
1151 return false; | |
1152 } | |
1153 return true; | |
1154 } | |
1155 | |
1156 | |
1157 static bool ContainsNonInstance(const Object& obj) { | 746 static bool ContainsNonInstance(const Object& obj) { |
1158 if (obj.IsArray()) { | 747 if (obj.IsArray()) { |
1159 const Array& array = Array::Cast(obj); | 748 const Array& array = Array::Cast(obj); |
1160 Object& element = Object::Handle(); | 749 Object& element = Object::Handle(); |
1161 for (intptr_t i = 0; i < array.Length(); ++i) { | 750 for (intptr_t i = 0; i < array.Length(); ++i) { |
1162 element = array.At(i); | 751 element = array.At(i); |
1163 if (!(element.IsInstance() || element.IsNull())) { | 752 if (!(element.IsInstance() || element.IsNull())) { |
1164 return true; | 753 return true; |
1165 } | 754 } |
1166 } | 755 } |
(...skipping 748 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1915 JSONObject jsobj(js); | 1504 JSONObject jsobj(js); |
1916 jsobj.AddProperty("type", "InstanceSet"); | 1505 jsobj.AddProperty("type", "InstanceSet"); |
1917 jsobj.AddProperty("id", "instance_set"); | 1506 jsobj.AddProperty("id", "instance_set"); |
1918 jsobj.AddProperty("totalCount", count); | 1507 jsobj.AddProperty("totalCount", count); |
1919 jsobj.AddProperty("sampleCount", storage.Length()); | 1508 jsobj.AddProperty("sampleCount", storage.Length()); |
1920 jsobj.AddProperty("sample", storage); | 1509 jsobj.AddProperty("sample", storage); |
1921 return true; | 1510 return true; |
1922 } | 1511 } |
1923 | 1512 |
1924 | 1513 |
1514 class LibraryCoverageFilter : public CoverageFilter { | |
1515 public: | |
1516 explicit LibraryCoverageFilter(const Library& lib) : lib_(lib) {} | |
1517 bool ShouldOutputCoverageFor(const Library& lib, | |
1518 const Script& script, | |
1519 const Class& cls, | |
1520 const Function& func) const { | |
1521 return lib.raw() == lib_.raw(); | |
1522 } | |
1523 private: | |
1524 const Library& lib_; | |
1525 }; | |
1526 | |
1527 | |
1528 class ScriptCoverageFilter : public CoverageFilter { | |
1529 public: | |
1530 explicit ScriptCoverageFilter(const Script& script) | |
1531 : script_(script) {} | |
1532 bool ShouldOutputCoverageFor(const Library& lib, | |
1533 const Script& script, | |
1534 const Class& cls, | |
1535 const Function& func) const { | |
1536 return script.raw() == script_.raw(); | |
1537 } | |
1538 private: | |
1539 const Script& script_; | |
1540 }; | |
1541 | |
1542 | |
1543 class ClassCoverageFilter : public CoverageFilter { | |
1544 public: | |
1545 explicit ClassCoverageFilter(const Class& cls) : cls_(cls) {} | |
1546 bool ShouldOutputCoverageFor(const Library& lib, | |
1547 const Script& script, | |
1548 const Class& cls, | |
1549 const Function& func) const { | |
1550 return cls.raw() == cls_.raw(); | |
1551 } | |
1552 private: | |
1553 const Class& cls_; | |
1554 }; | |
1555 | |
1556 | |
1557 class FunctionCoverageFilter : public CoverageFilter { | |
1558 public: | |
1559 explicit FunctionCoverageFilter(const Function& func) : func_(func) {} | |
1560 bool ShouldOutputCoverageFor(const Library& lib, | |
1561 const Script& script, | |
1562 const Class& cls, | |
1563 const Function& func) const { | |
1564 return func.raw() == func_.raw(); | |
1565 } | |
1566 private: | |
1567 const Function& func_; | |
1568 }; | |
1569 | |
1570 | |
1925 static bool HandleIsolateGetCoverage(Isolate* isolate, JSONStream* js) { | 1571 static bool HandleIsolateGetCoverage(Isolate* isolate, JSONStream* js) { |
1926 if (!js->HasParam("targetId")) { | 1572 if (!js->HasParam("targetId")) { |
1927 CodeCoverage::PrintJSON(isolate, js, NULL); | 1573 CodeCoverage::PrintJSON(isolate, js, NULL); |
1928 return true; | 1574 return true; |
1929 } | 1575 } |
1930 const char* target_id = js->LookupParam("targetId"); | 1576 const char* target_id = js->LookupParam("targetId"); |
1931 Object& obj = Object::Handle(LookupHeapObject(isolate, target_id, NULL)); | 1577 Object& obj = Object::Handle(LookupHeapObject(isolate, target_id, NULL)); |
1932 if (obj.raw() == Object::sentinel().raw()) { | 1578 if (obj.raw() == Object::sentinel().raw()) { |
1933 PrintInvalidParamError(js, "targetId"); | 1579 PrintInvalidParamError(js, "targetId"); |
1934 return true; | 1580 return true; |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2148 strncmp(metric_id, kNativeMetricIdPrefix, kNativeMetricIdPrefixLen) == 0; | 1794 strncmp(metric_id, kNativeMetricIdPrefix, kNativeMetricIdPrefixLen) == 0; |
2149 if (native_metric) { | 1795 if (native_metric) { |
2150 const char* id = metric_id + kNativeMetricIdPrefixLen; | 1796 const char* id = metric_id + kNativeMetricIdPrefixLen; |
2151 return HandleNativeMetric(isolate, js, id); | 1797 return HandleNativeMetric(isolate, js, id); |
2152 } | 1798 } |
2153 const char* id = metric_id + kMetricIdPrefixLen; | 1799 const char* id = metric_id + kMetricIdPrefixLen; |
2154 return HandleDartMetric(isolate, js, id); | 1800 return HandleDartMetric(isolate, js, id); |
2155 } | 1801 } |
2156 | 1802 |
2157 | 1803 |
2158 static bool HandleVMGetMetricList(JSONStream* js) { | 1804 static bool HandleVMGetMetricList(Isolate* isolate, JSONStream* js) { |
2159 return false; | 1805 return false; |
2160 } | 1806 } |
2161 | 1807 |
2162 | 1808 |
2163 static bool HandleVMGetMetric(JSONStream* js) { | 1809 static bool HandleVMGetMetric(Isolate* isolate, JSONStream* js) { |
2164 const char* metric_id = js->LookupParam("metricId"); | 1810 const char* metric_id = js->LookupParam("metricId"); |
2165 if (metric_id == NULL) { | 1811 if (metric_id == NULL) { |
2166 PrintMissingParamError(js, "metricId"); | 1812 PrintMissingParamError(js, "metricId"); |
2167 } | 1813 } |
2168 return false; | 1814 return false; |
2169 } | 1815 } |
2170 | 1816 |
2171 | 1817 |
2172 static bool HandleIsolateResume(Isolate* isolate, JSONStream* js) { | 1818 static bool HandleIsolateResume(Isolate* isolate, JSONStream* js) { |
2173 const char* step_param = js->LookupParam("step"); | 1819 const char* step_param = js->LookupParam("step"); |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2235 | 1881 |
2236 | 1882 |
2237 static bool HandleIsolateGetTagProfile(Isolate* isolate, JSONStream* js) { | 1883 static bool HandleIsolateGetTagProfile(Isolate* isolate, JSONStream* js) { |
2238 JSONObject miniProfile(js); | 1884 JSONObject miniProfile(js); |
2239 miniProfile.AddProperty("type", "TagProfile"); | 1885 miniProfile.AddProperty("type", "TagProfile"); |
2240 miniProfile.AddProperty("id", "profile/tag"); | 1886 miniProfile.AddProperty("id", "profile/tag"); |
2241 isolate->vm_tag_counters()->PrintToJSONObject(&miniProfile); | 1887 isolate->vm_tag_counters()->PrintToJSONObject(&miniProfile); |
2242 return true; | 1888 return true; |
2243 } | 1889 } |
2244 | 1890 |
1891 | |
1892 static const char* tags_enum_names[] = { | |
1893 "None", | |
1894 "UserVM", | |
1895 "UserOnly", | |
1896 "VMUser", | |
1897 "VMOnly", | |
1898 NULL, | |
1899 }; | |
1900 | |
1901 | |
1902 static ProfilerService::TagOrder tags_enum_values[] = { | |
1903 ProfilerService::kNoTags, | |
1904 ProfilerService::kUserVM, | |
1905 ProfilerService::kUser, | |
1906 ProfilerService::kVMUser, | |
1907 ProfilerService::kVM, | |
1908 ProfilerService::kNoTags, // Default value. | |
1909 }; | |
1910 | |
1911 | |
1912 static const MethodParameter* cpu_profile_params[] = { | |
1913 ISOLATE_PARAMETER, | |
1914 new EnumParameter("tags", true, tags_enum_names), | |
1915 NULL, | |
1916 }; | |
1917 | |
1918 | |
2245 static bool HandleIsolateGetCpuProfile(Isolate* isolate, JSONStream* js) { | 1919 static bool HandleIsolateGetCpuProfile(Isolate* isolate, JSONStream* js) { |
2246 ProfilerService::TagOrder tag_order = ProfilerService::kUserVM; | 1920 ProfilerService::TagOrder tag_order = |
2247 if (js->HasParam("tags")) { | 1921 EnumMapper(js->LookupParam("tags"), tags_enum_names, tags_enum_values); |
2248 if (js->ParamIs("tags", "None")) { | |
2249 tag_order = ProfilerService::kNoTags; | |
2250 } else if (js->ParamIs("tags", "UserVM")) { | |
2251 tag_order = ProfilerService::kUserVM; | |
2252 } else if (js->ParamIs("tags", "UserOnly")) { | |
2253 tag_order = ProfilerService::kUser; | |
2254 } else if (js->ParamIs("tags", "VMUser")) { | |
2255 tag_order = ProfilerService::kVMUser; | |
2256 } else if (js->ParamIs("tags", "VMOnly")) { | |
2257 tag_order = ProfilerService::kVM; | |
2258 } else { | |
2259 PrintInvalidParamError(js, "tags"); | |
2260 return true; | |
2261 } | |
2262 } | |
2263 ProfilerService::PrintJSON(js, tag_order); | 1922 ProfilerService::PrintJSON(js, tag_order); |
2264 return true; | 1923 return true; |
2265 } | 1924 } |
2266 | 1925 |
2267 | 1926 |
2268 static bool HandleIsolateGetAllocationProfile(Isolate* isolate, | 1927 static bool HandleIsolateGetAllocationProfile(Isolate* isolate, |
2269 JSONStream* js) { | 1928 JSONStream* js) { |
2270 bool should_reset_accumulator = false; | 1929 bool should_reset_accumulator = false; |
2271 bool should_collect = false; | 1930 bool should_collect = false; |
2272 if (js->HasParam("reset")) { | 1931 if (js->HasParam("reset")) { |
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2466 if (!type_args.IsNull()) { | 2125 if (!type_args.IsNull()) { |
2467 if (!only_with_instantiations || type_args.HasInstantiations()) { | 2126 if (!only_with_instantiations || type_args.HasInstantiations()) { |
2468 members.AddValue(type_args); | 2127 members.AddValue(type_args); |
2469 } | 2128 } |
2470 } | 2129 } |
2471 } | 2130 } |
2472 return true; | 2131 return true; |
2473 } | 2132 } |
2474 | 2133 |
2475 | 2134 |
2476 static IsolateMessageHandlerEntry isolate_handlers_new[] = { | |
2477 { "getIsolate", HandleIsolate }, | |
2478 { "getObject", HandleIsolateGetObject }, | |
2479 { "getObjectByAddress", HandleIsolateGetObjectByAddress }, | |
2480 { "getBreakpoints", HandleIsolateGetBreakpoints }, | |
2481 { "pause", HandleIsolatePause }, | |
2482 { "resume", HandleIsolateResume }, | |
2483 { "getStack", HandleIsolateGetStack }, | |
2484 { "getCpuProfile", HandleIsolateGetCpuProfile }, | |
2485 { "getTagProfile", HandleIsolateGetTagProfile }, | |
2486 { "getAllocationProfile", HandleIsolateGetAllocationProfile }, | |
2487 { "getHeapMap", HandleIsolateGetHeapMap }, | |
2488 { "addBreakpoint", HandleIsolateAddBreakpoint }, | |
2489 { "removeBreakpoint", HandleIsolateRemoveBreakpoint }, | |
2490 { "getCoverage", HandleIsolateGetCoverage }, | |
2491 { "eval", HandleIsolateEval }, | |
2492 { "getRetainedSize", HandleIsolateGetRetainedSize }, | |
2493 { "getRetainingPath", HandleIsolateGetRetainingPath }, | |
2494 { "getInboundReferences", HandleIsolateGetInboundReferences }, | |
2495 { "getInstances", HandleIsolateGetInstances }, | |
2496 { "requestHeapSnapshot", HandleIsolateRequestHeapSnapshot }, | |
2497 { "getClassList", HandleIsolateGetClassList }, | |
2498 { "getTypeArgumentsList", HandleIsolateGetTypeArgumentsList }, | |
2499 { "getIsolateMetricList", HandleIsolateGetMetricList }, | |
2500 { "getIsolateMetric", HandleIsolateGetMetric }, | |
2501 { "_echo", HandleIsolateEcho }, | |
2502 { "_triggerEchoEvent", HandleIsolateTriggerEchoEvent }, | |
2503 { "_respondWithMalformedJson", HandleIsolateRespondWithMalformedJson }, | |
2504 { "_respondWithMalformedObject", HandleIsolateRespondWithMalformedObject }, | |
2505 }; | |
2506 | |
2507 | |
2508 static IsolateMessageHandler FindIsolateMessageHandler(const char* method) { | |
2509 intptr_t num_message_handlers = sizeof(isolate_handlers_new) / | |
2510 sizeof(isolate_handlers_new[0]); | |
2511 for (intptr_t i = 0; i < num_message_handlers; i++) { | |
2512 const IsolateMessageHandlerEntry& entry = isolate_handlers_new[i]; | |
2513 if (strcmp(method, entry.method) == 0) { | |
2514 return entry.handler; | |
2515 } | |
2516 } | |
2517 if (FLAG_trace_service) { | |
2518 OS::Print("Service has no isolate message handler for <%s>\n", method); | |
2519 } | |
2520 return NULL; | |
2521 } | |
2522 | |
2523 | |
2524 void Service::HandleRootMessage(const Instance& msg_instance) { | |
2525 Isolate* isolate = Isolate::Current(); | |
2526 ASSERT(!msg_instance.IsNull()); | |
2527 ASSERT(msg_instance.IsArray()); | |
2528 | |
2529 { | |
2530 StackZone zone(isolate); | |
2531 HANDLESCOPE(isolate); | |
2532 | |
2533 const Array& msg = Array::Cast(msg_instance); | |
2534 ASSERT(msg.Length() == 5); | |
2535 | |
2536 Instance& reply_port = Instance::Handle(isolate); | |
2537 String& method = String::Handle(isolate); | |
2538 Array& param_keys = Array::Handle(isolate); | |
2539 Array& param_values = Array::Handle(isolate); | |
2540 reply_port ^= msg.At(1); | |
2541 method ^= msg.At(2); | |
2542 param_keys ^= msg.At(3); | |
2543 param_values ^= msg.At(4); | |
2544 | |
2545 ASSERT(!method.IsNull()); | |
2546 ASSERT(!param_keys.IsNull()); | |
2547 ASSERT(!param_values.IsNull()); | |
2548 ASSERT(param_keys.Length() == param_values.Length()); | |
2549 | |
2550 if (!reply_port.IsSendPort()) { | |
2551 FATAL("SendPort expected."); | |
2552 } | |
2553 | |
2554 RootMessageHandler handler = | |
2555 FindRootMessageHandler(method.ToCString()); | |
2556 { | |
2557 JSONStream js; | |
2558 js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), | |
2559 method, param_keys, param_values); | |
2560 if (handler == NULL) { | |
2561 // Check for an embedder handler. | |
2562 EmbedderServiceHandler* e_handler = | |
2563 FindRootEmbedderHandler(method.ToCString()); | |
2564 if (e_handler != NULL) { | |
2565 EmbedderHandleMessage(e_handler, &js); | |
2566 } else { | |
2567 if (FindIsolateMessageHandler(method.ToCString()) != NULL) { | |
2568 PrintMissingParamError(&js, "isolateId"); | |
2569 } else { | |
2570 PrintError(&js, "Unrecognized method: %s", method.ToCString()); | |
2571 } | |
2572 } | |
2573 js.PostReply(); | |
2574 } else { | |
2575 if (handler(&js)) { | |
2576 // Handler returns true if the reply is ready to be posted. | |
2577 // TODO(johnmccutchan): Support asynchronous replies. | |
2578 js.PostReply(); | |
2579 } | |
2580 } | |
2581 } | |
2582 } | |
2583 } | |
2584 | |
2585 | |
2586 static bool HandleRootEcho(JSONStream* js) { | |
2587 JSONObject jsobj(js); | |
2588 return HandleCommonEcho(&jsobj, js); | |
2589 } | |
2590 | |
2591 | |
2592 class ServiceIsolateVisitor : public IsolateVisitor { | 2135 class ServiceIsolateVisitor : public IsolateVisitor { |
2593 public: | 2136 public: |
2594 explicit ServiceIsolateVisitor(JSONArray* jsarr) | 2137 explicit ServiceIsolateVisitor(JSONArray* jsarr) |
2595 : jsarr_(jsarr) { | 2138 : jsarr_(jsarr) { |
2596 } | 2139 } |
2597 | 2140 |
2598 virtual ~ServiceIsolateVisitor() {} | 2141 virtual ~ServiceIsolateVisitor() {} |
2599 | 2142 |
2600 void VisitIsolate(Isolate* isolate) { | 2143 void VisitIsolate(Isolate* isolate) { |
2601 if (isolate != Dart::vm_isolate() && !Service::IsServiceIsolate(isolate)) { | 2144 if ((isolate != Dart::vm_isolate()) && |
2145 !ServiceIsolate::IsServiceIsolate(isolate)) { | |
2602 jsarr_->AddValue(isolate); | 2146 jsarr_->AddValue(isolate); |
2603 } | 2147 } |
2604 } | 2148 } |
2605 | 2149 |
2606 private: | 2150 private: |
2607 JSONArray* jsarr_; | 2151 JSONArray* jsarr_; |
2608 }; | 2152 }; |
2609 | 2153 |
2610 | 2154 |
2611 static bool HandleVM(JSONStream* js) { | 2155 static bool HandleVM(Isolate* isolate, JSONStream* js) { |
2612 Isolate* isolate = Isolate::Current(); | |
2613 JSONObject jsobj(js); | 2156 JSONObject jsobj(js); |
2614 jsobj.AddProperty("type", "VM"); | 2157 jsobj.AddProperty("type", "VM"); |
2615 jsobj.AddProperty("id", "vm"); | 2158 jsobj.AddProperty("id", "vm"); |
2616 jsobj.AddProperty("architectureBits", static_cast<intptr_t>(kBitsPerWord)); | 2159 jsobj.AddProperty("architectureBits", static_cast<intptr_t>(kBitsPerWord)); |
2617 jsobj.AddProperty("targetCPU", CPU::Id()); | 2160 jsobj.AddProperty("targetCPU", CPU::Id()); |
2618 jsobj.AddProperty("hostCPU", HostCPUFeatures::hardware()); | 2161 jsobj.AddProperty("hostCPU", HostCPUFeatures::hardware()); |
2619 jsobj.AddPropertyF("date", "%" Pd64 "", OS::GetCurrentTimeMillis()); | 2162 jsobj.AddPropertyF("date", "%" Pd64 "", OS::GetCurrentTimeMillis()); |
2620 jsobj.AddProperty("version", Version::String()); | 2163 jsobj.AddProperty("version", Version::String()); |
2621 // Send pid as a string because it allows us to avoid any issues with | 2164 // Send pid as a string because it allows us to avoid any issues with |
2622 // pids > 53-bits (when consumed by JavaScript). | 2165 // pids > 53-bits (when consumed by JavaScript). |
(...skipping 10 matching lines...) Expand all Loading... | |
2633 // Construct the isolate list. | 2176 // Construct the isolate list. |
2634 { | 2177 { |
2635 JSONArray jsarr(&jsobj, "isolates"); | 2178 JSONArray jsarr(&jsobj, "isolates"); |
2636 ServiceIsolateVisitor visitor(&jsarr); | 2179 ServiceIsolateVisitor visitor(&jsarr); |
2637 Isolate::VisitIsolates(&visitor); | 2180 Isolate::VisitIsolates(&visitor); |
2638 } | 2181 } |
2639 return true; | 2182 return true; |
2640 } | 2183 } |
2641 | 2184 |
2642 | 2185 |
2643 static bool HandleVMFlagList(JSONStream* js) { | 2186 static bool HandleVMFlagList(Isolate* isolate, JSONStream* js) { |
2644 Flags::PrintJSON(js); | 2187 Flags::PrintJSON(js); |
2645 return true; | 2188 return true; |
2646 } | 2189 } |
2647 | 2190 |
2648 | 2191 |
2649 static bool HandleVMSetFlag(JSONStream* js) { | 2192 static bool HandleVMSetFlag(Isolate* isolate, JSONStream* js) { |
2650 const char* flag_name = js->LookupParam("name"); | 2193 const char* flag_name = js->LookupParam("name"); |
2651 if (flag_name == NULL) { | 2194 if (flag_name == NULL) { |
2652 PrintMissingParamError(js, "name"); | 2195 PrintMissingParamError(js, "name"); |
2653 return true; | 2196 return true; |
2654 } | 2197 } |
2655 const char* flag_value = js->LookupParam("value"); | 2198 const char* flag_value = js->LookupParam("value"); |
2656 if (flag_value == NULL) { | 2199 if (flag_value == NULL) { |
2657 PrintMissingParamError(js, "value"); | 2200 PrintMissingParamError(js, "value"); |
2658 return true; | 2201 return true; |
2659 } | 2202 } |
2660 JSONObject jsobj(js); | 2203 JSONObject jsobj(js); |
2661 const char* error = NULL; | 2204 const char* error = NULL; |
2662 if (Flags::SetFlag(flag_name, flag_value, &error)) { | 2205 if (Flags::SetFlag(flag_name, flag_value, &error)) { |
2663 jsobj.AddProperty("type", "Success"); | 2206 jsobj.AddProperty("type", "Success"); |
2664 jsobj.AddProperty("id", ""); | 2207 jsobj.AddProperty("id", ""); |
2665 return true; | 2208 return true; |
2666 } else { | 2209 } else { |
2667 jsobj.AddProperty("type", "Failure"); | 2210 jsobj.AddProperty("type", "Failure"); |
2668 jsobj.AddProperty("id", ""); | 2211 jsobj.AddProperty("id", ""); |
2669 jsobj.AddProperty("message", error); | 2212 jsobj.AddProperty("message", error); |
2670 return true; | 2213 return true; |
2671 } | 2214 } |
2672 } | 2215 } |
2673 | 2216 |
2674 | 2217 |
2675 static RootMessageHandlerEntry root_handlers_new[] = { | 2218 static ServiceMethodDescriptor service_methods_[] = { |
2676 { "getVM", HandleVM }, | 2219 { "_echo", HandleIsolateEcho, NULL }, |
2677 { "getFlagList", HandleVMFlagList }, | 2220 { "_respondWithMalformedJson", HandleIsolateRespondWithMalformedJson, NULL }, |
2678 { "setFlag", HandleVMSetFlag }, | 2221 { "_respondWithMalformedObject", |
2679 { "getVMMetricList", HandleVMGetMetricList }, | 2222 HandleIsolateRespondWithMalformedObject, NULL }, |
2680 { "getVMMetric", HandleVMGetMetric }, | 2223 { "_triggerEchoEvent", HandleIsolateTriggerEchoEvent, NULL }, |
2681 { "_echo", HandleRootEcho }, | 2224 { "addBreakpoint", HandleIsolateAddBreakpoint, NULL }, |
2225 { "eval", HandleIsolateEval, NULL }, | |
2226 { "getAllocationProfile", HandleIsolateGetAllocationProfile, NULL }, | |
2227 { "getBreakpoints", HandleIsolateGetBreakpoints, NULL }, | |
2228 { "getClassList", HandleIsolateGetClassList, NULL }, | |
2229 { "getCoverage", HandleIsolateGetCoverage, NULL }, | |
2230 { "getCpuProfile", HandleIsolateGetCpuProfile, cpu_profile_params, }, | |
turnidge
2015/02/12 18:08:57
Would be nice to add validators for everything as
Cutch
2015/02/12 18:18:47
Partially done. Only the isolate parameter is list
| |
2231 { "getFlagList", HandleVMFlagList , NULL}, | |
2232 { "getHeapMap", HandleIsolateGetHeapMap, NULL }, | |
2233 { "getInboundReferences", HandleIsolateGetInboundReferences, NULL }, | |
2234 { "getInstances", HandleIsolateGetInstances, NULL }, | |
2235 { "getIsolate", HandleIsolate, NULL }, | |
2236 { "getIsolateMetric", HandleIsolateGetMetric, NULL }, | |
2237 { "getIsolateMetricList", HandleIsolateGetMetricList, NULL }, | |
2238 { "getObject", HandleIsolateGetObject, NULL }, | |
2239 { "getObjectByAddress", HandleIsolateGetObjectByAddress, NULL }, | |
2240 { "getRetainedSize", HandleIsolateGetRetainedSize, NULL }, | |
2241 { "getRetainingPath", HandleIsolateGetRetainingPath, NULL }, | |
2242 { "getStack", HandleIsolateGetStack, NULL }, | |
2243 { "getTagProfile", HandleIsolateGetTagProfile, NULL }, | |
2244 { "getTypeArgumentsList", HandleIsolateGetTypeArgumentsList, NULL }, | |
2245 { "getVM", HandleVM , NULL}, | |
2246 { "getVMMetric", HandleVMGetMetric , NULL}, | |
2247 { "getVMMetricList", HandleVMGetMetricList , NULL}, | |
2248 { "pause", HandleIsolatePause, NULL }, | |
2249 { "removeBreakpoint", HandleIsolateRemoveBreakpoint, NULL }, | |
2250 { "resume", HandleIsolateResume, NULL }, | |
2251 { "requestHeapSnapshot", HandleIsolateRequestHeapSnapshot, NULL }, | |
2252 { "setFlag", HandleVMSetFlag , NULL}, | |
2682 }; | 2253 }; |
2683 | 2254 |
2684 | 2255 |
2685 static RootMessageHandler FindRootMessageHandler(const char* method) { | 2256 ServiceMethodDescriptor* FindMethod(const char* method_name) { |
2686 intptr_t num_message_handlers = sizeof(root_handlers_new) / | 2257 intptr_t num_methods = sizeof(service_methods_) / |
2687 sizeof(root_handlers_new[0]); | 2258 sizeof(service_methods_[0]); |
2688 for (intptr_t i = 0; i < num_message_handlers; i++) { | 2259 for (intptr_t i = 0; i < num_methods; i++) { |
2689 const RootMessageHandlerEntry& entry = root_handlers_new[i]; | 2260 ServiceMethodDescriptor& method = service_methods_[i]; |
2690 if (strcmp(method, entry.method) == 0) { | 2261 if (strcmp(method_name, method.name) == 0) { |
2691 return entry.handler; | 2262 return &method; |
2692 } | 2263 } |
2693 } | 2264 } |
2694 if (FLAG_trace_service) { | |
2695 OS::Print("vm-service: No root message handler for <%s>.\n", method); | |
2696 } | |
2697 return NULL; | 2265 return NULL; |
2698 } | 2266 } |
2699 | 2267 |
2700 | 2268 |
2701 void Service::SendEvent(intptr_t eventId, const Object& eventMessage) { | |
2702 if (!IsRunning()) { | |
2703 return; | |
2704 } | |
2705 Isolate* isolate = Isolate::Current(); | |
2706 ASSERT(isolate != NULL); | |
2707 HANDLESCOPE(isolate); | |
2708 | |
2709 // Construct a list of the form [eventId, eventMessage]. | |
2710 const Array& list = Array::Handle(Array::New(2)); | |
2711 ASSERT(!list.IsNull()); | |
2712 list.SetAt(0, Integer::Handle(Integer::New(eventId))); | |
2713 list.SetAt(1, eventMessage); | |
2714 | |
2715 // Push the event to port_. | |
2716 uint8_t* data = NULL; | |
2717 MessageWriter writer(&data, &allocator, false); | |
2718 writer.WriteMessage(list); | |
2719 intptr_t len = writer.BytesWritten(); | |
2720 if (FLAG_trace_service) { | |
2721 OS::Print("vm-service: Pushing event of type %" Pd ", len %" Pd "\n", | |
2722 eventId, len); | |
2723 } | |
2724 // TODO(turnidge): For now we ignore failure to send an event. Revisit? | |
2725 PortMap::PostMessage( | |
2726 new Message(service_port_, data, len, Message::kNormalPriority)); | |
2727 } | |
2728 | |
2729 | |
2730 void Service::SendEvent(intptr_t eventId, | |
2731 const String& meta, | |
2732 const uint8_t* data, | |
2733 intptr_t size) { | |
2734 // Bitstream: [meta data size (big-endian 64 bit)] [meta data (UTF-8)] [data] | |
2735 const intptr_t meta_bytes = Utf8::Length(meta); | |
2736 const intptr_t total_bytes = sizeof(uint64_t) + meta_bytes + size; | |
2737 const TypedData& message = TypedData::Handle( | |
2738 TypedData::New(kTypedDataUint8ArrayCid, total_bytes)); | |
2739 intptr_t offset = 0; | |
2740 // TODO(koda): Rename these methods SetHostUint64, etc. | |
2741 message.SetUint64(0, Utils::HostToBigEndian64(meta_bytes)); | |
2742 offset += sizeof(uint64_t); | |
2743 { | |
2744 NoGCScope no_gc; | |
2745 meta.ToUTF8(static_cast<uint8_t*>(message.DataAddr(offset)), meta_bytes); | |
2746 offset += meta_bytes; | |
2747 } | |
2748 // TODO(koda): It would be nice to avoid this copy (requires changes to | |
2749 // MessageWriter code). | |
2750 { | |
2751 NoGCScope no_gc; | |
2752 memmove(message.DataAddr(offset), data, size); | |
2753 offset += size; | |
2754 } | |
2755 ASSERT(offset == total_bytes); | |
2756 SendEvent(eventId, message); | |
2757 } | |
2758 | |
2759 | |
2760 void Service::HandleGCEvent(GCEvent* event) { | |
2761 JSONStream js; | |
2762 event->PrintJSON(&js); | |
2763 const String& message = String::Handle(String::New(js.ToCString())); | |
2764 SendEvent(kEventFamilyGC, message); | |
2765 } | |
2766 | |
2767 | |
2768 void Service::HandleDebuggerEvent(DebuggerEvent* event) { | |
2769 JSONStream js; | |
2770 event->PrintJSON(&js); | |
2771 const String& message = String::Handle(String::New(js.ToCString())); | |
2772 SendEvent(kEventFamilyDebug, message); | |
2773 } | |
2774 | |
2775 | |
2776 void Service::EmbedderHandleMessage(EmbedderServiceHandler* handler, | |
2777 JSONStream* js) { | |
2778 ASSERT(handler != NULL); | |
2779 Dart_ServiceRequestCallback callback = handler->callback(); | |
2780 ASSERT(callback != NULL); | |
2781 const char* r = NULL; | |
2782 const char* name = js->method(); | |
2783 const char** keys = js->param_keys(); | |
2784 const char** values = js->param_values(); | |
2785 r = callback(name, keys, values, js->num_params(), handler->user_data()); | |
2786 ASSERT(r != NULL); | |
2787 // TODO(johnmccutchan): Allow for NULL returns? | |
2788 TextBuffer* buffer = js->buffer(); | |
2789 buffer->AddString(r); | |
2790 free(const_cast<char*>(r)); | |
2791 } | |
2792 | |
2793 | |
2794 void Service::RegisterIsolateEmbedderCallback( | |
2795 const char* name, | |
2796 Dart_ServiceRequestCallback callback, | |
2797 void* user_data) { | |
2798 if (name == NULL) { | |
2799 return; | |
2800 } | |
2801 EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(name); | |
2802 if (handler != NULL) { | |
2803 // Update existing handler entry. | |
2804 handler->set_callback(callback); | |
2805 handler->set_user_data(user_data); | |
2806 return; | |
2807 } | |
2808 // Create a new handler. | |
2809 handler = new EmbedderServiceHandler(name); | |
2810 handler->set_callback(callback); | |
2811 handler->set_user_data(user_data); | |
2812 | |
2813 // Insert into isolate_service_handler_head_ list. | |
2814 handler->set_next(isolate_service_handler_head_); | |
2815 isolate_service_handler_head_ = handler; | |
2816 } | |
2817 | |
2818 | |
2819 EmbedderServiceHandler* Service::FindIsolateEmbedderHandler( | |
2820 const char* name) { | |
2821 EmbedderServiceHandler* current = isolate_service_handler_head_; | |
2822 while (current != NULL) { | |
2823 if (strcmp(name, current->name()) == 0) { | |
2824 return current; | |
2825 } | |
2826 current = current->next(); | |
2827 } | |
2828 return NULL; | |
2829 } | |
2830 | |
2831 | |
2832 void Service::RegisterRootEmbedderCallback( | |
2833 const char* name, | |
2834 Dart_ServiceRequestCallback callback, | |
2835 void* user_data) { | |
2836 if (name == NULL) { | |
2837 return; | |
2838 } | |
2839 EmbedderServiceHandler* handler = FindRootEmbedderHandler(name); | |
2840 if (handler != NULL) { | |
2841 // Update existing handler entry. | |
2842 handler->set_callback(callback); | |
2843 handler->set_user_data(user_data); | |
2844 return; | |
2845 } | |
2846 // Create a new handler. | |
2847 handler = new EmbedderServiceHandler(name); | |
2848 handler->set_callback(callback); | |
2849 handler->set_user_data(user_data); | |
2850 | |
2851 // Insert into root_service_handler_head_ list. | |
2852 handler->set_next(root_service_handler_head_); | |
2853 root_service_handler_head_ = handler; | |
2854 } | |
2855 | |
2856 | |
2857 EmbedderServiceHandler* Service::FindRootEmbedderHandler( | |
2858 const char* name) { | |
2859 EmbedderServiceHandler* current = root_service_handler_head_; | |
2860 while (current != NULL) { | |
2861 if (strcmp(name, current->name()) == 0) { | |
2862 return current; | |
2863 } | |
2864 current = current->next(); | |
2865 } | |
2866 return NULL; | |
2867 } | |
2868 | |
2869 } // namespace dart | 2269 } // namespace dart |
OLD | NEW |