| 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 // TODO(johnmccutchan): Unify embedder service handler lists and their APIs. |
| 44 const char* path_; | 44 EmbedderServiceHandler* Service::isolate_service_handler_head_ = NULL; |
| 45 const char* resource_; | 45 EmbedderServiceHandler* Service::root_service_handler_head_ = NULL; |
| 46 int length_; | 46 uint32_t Service::event_mask_ = 0; |
| 47 }; | 47 struct ServiceMethodDescriptor; |
| 48 | 48 ServiceMethodDescriptor* FindMethod(const char* method_name); |
| 49 extern ResourcesEntry __service_resources_[]; | 49 |
| 50 | 50 static uint8_t* allocator(uint8_t* ptr, intptr_t old_size, intptr_t new_size) { |
| 51 class Resources { | 51 void* new_ptr = realloc(reinterpret_cast<void*>(ptr), new_size); |
| 52 return reinterpret_cast<uint8_t*>(new_ptr); |
| 53 } |
| 54 |
| 55 static void PrintRequest(const JSONObject& obj, JSONStream* js) { |
| 56 JSONObject jsobj(&obj, "request"); |
| 57 jsobj.AddProperty("method", js->method()); |
| 58 { |
| 59 JSONArray jsarr(&jsobj, "param_keys"); |
| 60 for (intptr_t i = 0; i < js->num_params(); i++) { |
| 61 jsarr.AddValue(js->GetParamKey(i)); |
| 62 } |
| 63 } |
| 64 { |
| 65 JSONArray jsarr(&jsobj, "param_values"); |
| 66 for (intptr_t i = 0; i < js->num_params(); i++) { |
| 67 jsarr.AddValue(js->GetParamValue(i)); |
| 68 } |
| 69 } |
| 70 } |
| 71 |
| 72 |
| 73 static void PrintError(JSONStream* js, |
| 74 const char* format, ...) { |
| 75 Isolate* isolate = Isolate::Current(); |
| 76 |
| 77 va_list args; |
| 78 va_start(args, format); |
| 79 intptr_t len = OS::VSNPrint(NULL, 0, format, args); |
| 80 va_end(args); |
| 81 |
| 82 char* buffer = isolate->current_zone()->Alloc<char>(len + 1); |
| 83 va_list args2; |
| 84 va_start(args2, format); |
| 85 OS::VSNPrint(buffer, (len + 1), format, args2); |
| 86 va_end(args2); |
| 87 |
| 88 JSONObject jsobj(js); |
| 89 jsobj.AddProperty("type", "Error"); |
| 90 jsobj.AddProperty("message", buffer); |
| 91 PrintRequest(jsobj, js); |
| 92 } |
| 93 |
| 94 |
| 95 static void PrintMissingParamError(JSONStream* js, |
| 96 const char* param) { |
| 97 PrintError(js, "%s expects the '%s' parameter", |
| 98 js->method(), param); |
| 99 } |
| 100 |
| 101 |
| 102 static void PrintInvalidParamError(JSONStream* js, |
| 103 const char* param) { |
| 104 PrintError(js, "%s: invalid '%s' parameter: %s", |
| 105 js->method(), param, js->LookupParam(param)); |
| 106 } |
| 107 |
| 108 |
| 109 static void PrintUnrecognizedMethodError(JSONStream* js) { |
| 110 PrintError(js, "unrecognized method: %s", js->method()); |
| 111 } |
| 112 |
| 113 |
| 114 static void PrintErrorWithKind(JSONStream* js, |
| 115 const char* kind, |
| 116 const char* format, ...) { |
| 117 Isolate* isolate = Isolate::Current(); |
| 118 |
| 119 va_list args; |
| 120 va_start(args, format); |
| 121 intptr_t len = OS::VSNPrint(NULL, 0, format, args); |
| 122 va_end(args); |
| 123 |
| 124 char* buffer = isolate->current_zone()->Alloc<char>(len + 1); |
| 125 va_list args2; |
| 126 va_start(args2, format); |
| 127 OS::VSNPrint(buffer, (len + 1), format, args2); |
| 128 va_end(args2); |
| 129 |
| 130 JSONObject jsobj(js); |
| 131 jsobj.AddProperty("type", "Error"); |
| 132 jsobj.AddProperty("id", ""); |
| 133 jsobj.AddProperty("kind", kind); |
| 134 jsobj.AddProperty("message", buffer); |
| 135 PrintRequest(jsobj, js); |
| 136 } |
| 137 |
| 138 |
| 139 static bool GetIntegerId(const char* s, intptr_t* id, int base = 10) { |
| 140 if ((s == NULL) || (*s == '\0')) { |
| 141 // Empty string. |
| 142 return false; |
| 143 } |
| 144 if (id == NULL) { |
| 145 // No id pointer. |
| 146 return false; |
| 147 } |
| 148 intptr_t r = 0; |
| 149 char* end_ptr = NULL; |
| 150 r = strtol(s, &end_ptr, base); |
| 151 if (end_ptr == s) { |
| 152 // String was not advanced at all, cannot be valid. |
| 153 return false; |
| 154 } |
| 155 *id = r; |
| 156 return true; |
| 157 } |
| 158 |
| 159 |
| 160 static bool GetUnsignedIntegerId(const char* s, uintptr_t* id, int base = 10) { |
| 161 if ((s == NULL) || (*s == '\0')) { |
| 162 // Empty string. |
| 163 return false; |
| 164 } |
| 165 if (id == NULL) { |
| 166 // No id pointer. |
| 167 return false; |
| 168 } |
| 169 uintptr_t r = 0; |
| 170 char* end_ptr = NULL; |
| 171 r = strtoul(s, &end_ptr, base); |
| 172 if (end_ptr == s) { |
| 173 // String was not advanced at all, cannot be valid. |
| 174 return false; |
| 175 } |
| 176 *id = r; |
| 177 return true; |
| 178 } |
| 179 |
| 180 |
| 181 static bool GetInteger64Id(const char* s, int64_t* id, int base = 10) { |
| 182 if ((s == NULL) || (*s == '\0')) { |
| 183 // Empty string. |
| 184 return false; |
| 185 } |
| 186 if (id == NULL) { |
| 187 // No id pointer. |
| 188 return false; |
| 189 } |
| 190 int64_t r = 0; |
| 191 char* end_ptr = NULL; |
| 192 r = strtoll(s, &end_ptr, base); |
| 193 if (end_ptr == s) { |
| 194 // String was not advanced at all, cannot be valid. |
| 195 return false; |
| 196 } |
| 197 *id = r; |
| 198 return true; |
| 199 } |
| 200 |
| 201 |
| 202 // Scans the string until the '-' character. Returns pointer to string |
| 203 // at '-' character. Returns NULL if not found. |
| 204 static const char* ScanUntilDash(const char* s) { |
| 205 if ((s == NULL) || (*s == '\0')) { |
| 206 // Empty string. |
| 207 return NULL; |
| 208 } |
| 209 while (*s != '\0') { |
| 210 if (*s == '-') { |
| 211 return s; |
| 212 } |
| 213 s++; |
| 214 } |
| 215 return NULL; |
| 216 } |
| 217 |
| 218 |
| 219 static bool GetCodeId(const char* s, int64_t* timestamp, uword* address) { |
| 220 if ((s == NULL) || (*s == '\0')) { |
| 221 // Empty string. |
| 222 return false; |
| 223 } |
| 224 if ((timestamp == NULL) || (address == NULL)) { |
| 225 // Bad arguments. |
| 226 return false; |
| 227 } |
| 228 // Extract the timestamp. |
| 229 if (!GetInteger64Id(s, timestamp, 16) || (*timestamp < 0)) { |
| 230 return false; |
| 231 } |
| 232 s = ScanUntilDash(s); |
| 233 if (s == NULL) { |
| 234 return false; |
| 235 } |
| 236 // Skip the dash. |
| 237 s++; |
| 238 // Extract the PC. |
| 239 if (!GetUnsignedIntegerId(s, address, 16)) { |
| 240 return false; |
| 241 } |
| 242 return true; |
| 243 } |
| 244 |
| 245 |
| 246 // TODO(johnmccutchan): Split into separate file and write unit tests. |
| 247 class MethodParameter { |
| 52 public: | 248 public: |
| 53 static const int kNoSuchInstance = -1; | 249 MethodParameter(const char* name, bool required) |
| 54 static int ResourceLookup(const char* path, const char** resource) { | 250 : name_(name), required_(required) { |
| 55 ResourcesEntry* table = ResourceTable(); | 251 } |
| 56 for (int i = 0; table[i].path_ != NULL; i++) { | 252 |
| 57 const ResourcesEntry& entry = table[i]; | 253 virtual ~MethodParameter() { } |
| 58 if (strcmp(path, entry.path_) == 0) { | 254 |
| 59 *resource = entry.resource_; | 255 virtual bool Validate(const char* value) const { |
| 60 ASSERT(entry.length_ > 0); | 256 return true; |
| 61 return entry.length_; | 257 } |
| 258 |
| 259 const char* name() const { |
| 260 return name_; |
| 261 } |
| 262 |
| 263 bool required() const { |
| 264 return required_; |
| 265 } |
| 266 |
| 267 private: |
| 268 const char* name_; |
| 269 bool required_; |
| 270 }; |
| 271 |
| 272 |
| 273 class NoSuchParameter : public MethodParameter { |
| 274 public: |
| 275 explicit NoSuchParameter(const char* name) |
| 276 : MethodParameter(name, false) { |
| 277 } |
| 278 |
| 279 virtual bool Validate(const char* value) const { |
| 280 return (value == NULL); |
| 281 } |
| 282 }; |
| 283 |
| 284 |
| 285 #define NO_ISOLATE_PARAMETER new NoSuchParameter("isolateId") |
| 286 |
| 287 |
| 288 class BoolParameter : public MethodParameter { |
| 289 public: |
| 290 BoolParameter(const char* name, bool required) |
| 291 : MethodParameter(name, required) { |
| 292 } |
| 293 |
| 294 virtual bool Validate(const char* value) const { |
| 295 if (value == NULL) { |
| 296 return false; |
| 297 } |
| 298 return (strcmp("true", value) == 0) || (strcmp("false", value) == 0); |
| 299 } |
| 300 |
| 301 static bool Interpret(const char* value) { |
| 302 return strcmp("true", value) == 0; |
| 303 } |
| 304 }; |
| 305 |
| 306 |
| 307 class IdParameter : public MethodParameter { |
| 308 public: |
| 309 IdParameter(const char* name, bool required) |
| 310 : MethodParameter(name, required) { |
| 311 } |
| 312 |
| 313 virtual bool Validate(const char* value) const { |
| 314 return (value != NULL); |
| 315 } |
| 316 }; |
| 317 |
| 318 |
| 319 #define ISOLATE_PARAMETER new IdParameter("isolateId", true) |
| 320 |
| 321 |
| 322 class EnumParameter : public MethodParameter { |
| 323 public: |
| 324 EnumParameter(const char* name, bool required, const char** enums) |
| 325 : MethodParameter(name, required), |
| 326 enums_(enums) { |
| 327 } |
| 328 |
| 329 virtual bool Validate(const char* value) const { |
| 330 if (value == NULL) { |
| 331 return true; |
| 332 } |
| 333 for (intptr_t i = 0; enums_[i] != NULL; i++) { |
| 334 if (strcmp(value, enums_[i]) == 0) { |
| 335 return true; |
| 62 } | 336 } |
| 63 } | 337 } |
| 64 return kNoSuchInstance; | 338 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 } | 339 } |
| 95 | 340 |
| 96 private: | 341 private: |
| 97 static ResourcesEntry* At(int idx) { | 342 const char** enums_; |
| 98 ASSERT(idx >= 0); | 343 }; |
| 99 ResourcesEntry* table = ResourceTable(); | 344 |
| 100 for (int i = 0; table[i].path_ != NULL; i++) { | 345 |
| 101 if (idx == i) { | 346 // If the key is not found, this function returns the last element in the |
| 102 return &table[i]; | 347 // values array. This can be used to encode the default value. |
| 348 template<typename T> |
| 349 T EnumMapper(const char* value, const char** enums, T* values) { |
| 350 ASSERT(value != NULL); |
| 351 intptr_t i = 0; |
| 352 for (i = 0; enums[i] != NULL; i++) { |
| 353 if (strcmp(value, enums[i]) == 0) { |
| 354 return values[i]; |
| 355 } |
| 356 } |
| 357 // Default value. |
| 358 return values[i]; |
| 359 } |
| 360 |
| 361 |
| 362 typedef bool (*ServiceMethodEntry)(Isolate* isolate, JSONStream* js); |
| 363 |
| 364 |
| 365 struct ServiceMethodDescriptor { |
| 366 const char* name; |
| 367 const ServiceMethodEntry entry; |
| 368 const MethodParameter* const * parameters; |
| 369 }; |
| 370 |
| 371 |
| 372 // TODO(johnmccutchan): Do we reject unexpected parameters? |
| 373 static bool ValidateParameters(const MethodParameter* const* parameters, |
| 374 JSONStream* js) { |
| 375 if (parameters == NULL) { |
| 376 return true; |
| 377 } |
| 378 for (intptr_t i = 0; parameters[i] != NULL; i++) { |
| 379 const MethodParameter* parameter = parameters[i]; |
| 380 const char* name = parameter->name(); |
| 381 const bool required = parameter->required(); |
| 382 const char* value = js->LookupParam(name); |
| 383 const bool has_parameter = (value != NULL); |
| 384 if (required && !has_parameter) { |
| 385 PrintMissingParamError(js, name); |
| 386 return false; |
| 387 } |
| 388 if (!parameter->Validate(value)) { |
| 389 PrintInvalidParamError(js, name); |
| 390 return false; |
| 391 } |
| 392 } |
| 393 return true; |
| 394 } |
| 395 |
| 396 |
| 397 void Service::InvokeMethod(Isolate* isolate, const Array& msg) { |
| 398 ASSERT(isolate != NULL); |
| 399 ASSERT(!msg.IsNull()); |
| 400 ASSERT(msg.Length() == 5); |
| 401 |
| 402 { |
| 403 StackZone zone(isolate); |
| 404 HANDLESCOPE(isolate); |
| 405 |
| 406 Instance& reply_port = Instance::Handle(isolate); |
| 407 String& method_name = String::Handle(isolate); |
| 408 Array& param_keys = Array::Handle(isolate); |
| 409 Array& param_values = Array::Handle(isolate); |
| 410 reply_port ^= msg.At(1); |
| 411 method_name ^= msg.At(2); |
| 412 param_keys ^= msg.At(3); |
| 413 param_values ^= msg.At(4); |
| 414 |
| 415 ASSERT(!method_name.IsNull()); |
| 416 ASSERT(!param_keys.IsNull()); |
| 417 ASSERT(!param_values.IsNull()); |
| 418 ASSERT(param_keys.Length() == param_values.Length()); |
| 419 |
| 420 if (!reply_port.IsSendPort()) { |
| 421 FATAL("SendPort expected."); |
| 422 } |
| 423 |
| 424 JSONStream js; |
| 425 js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), |
| 426 method_name, param_keys, param_values); |
| 427 |
| 428 const char* c_method_name = method_name.ToCString(); |
| 429 |
| 430 ServiceMethodDescriptor* method = FindMethod(c_method_name); |
| 431 if (method != NULL) { |
| 432 if (!ValidateParameters(method->parameters, &js)) { |
| 433 js.PostReply(); |
| 434 return; |
| 103 } | 435 } |
| 104 } | 436 if (method->entry(isolate, &js)) { |
| 105 return NULL; | 437 js.PostReply(); |
| 106 } | 438 } |
| 107 | 439 return; |
| 108 static ResourcesEntry* ResourceTable() { | 440 } |
| 109 return &__service_resources_[0]; | 441 |
| 110 } | 442 EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(c_method_name); |
| 111 | 443 if (handler == NULL) { |
| 112 DISALLOW_ALLOCATION(); | 444 handler = FindRootEmbedderHandler(c_method_name); |
| 113 DISALLOW_IMPLICIT_CONSTRUCTORS(Resources); | 445 } |
| 114 }; | 446 |
| 447 if (handler != NULL) { |
| 448 EmbedderHandleMessage(handler, &js); |
| 449 js.PostReply(); |
| 450 return; |
| 451 } |
| 452 |
| 453 PrintUnrecognizedMethodError(&js); |
| 454 js.PostReply(); |
| 455 return; |
| 456 } |
| 457 } |
| 458 |
| 459 |
| 460 void Service::HandleRootMessage(const Array& msg_instance) { |
| 461 Isolate* isolate = Isolate::Current(); |
| 462 InvokeMethod(isolate, msg_instance); |
| 463 } |
| 464 |
| 465 |
| 466 void Service::HandleIsolateMessage(Isolate* isolate, const Array& msg) { |
| 467 ASSERT(isolate != NULL); |
| 468 InvokeMethod(isolate, msg); |
| 469 } |
| 470 |
| 471 |
| 472 bool Service::EventMaskHas(uint32_t mask) { |
| 473 return (event_mask_ & mask) != 0; |
| 474 } |
| 475 |
| 476 |
| 477 bool Service::NeedsDebuggerEvents() { |
| 478 return ServiceIsolate::IsRunning() && EventMaskHas(kEventFamilyDebugMask); |
| 479 } |
| 480 |
| 481 |
| 482 bool Service::NeedsGCEvents() { |
| 483 return ServiceIsolate::IsRunning() && EventMaskHas(kEventFamilyGCMask); |
| 484 } |
| 485 |
| 486 |
| 487 void Service::SetEventMask(uint32_t mask) { |
| 488 event_mask_ = mask; |
| 489 } |
| 490 |
| 491 |
| 492 void Service::SendEvent(intptr_t eventId, const Object& eventMessage) { |
| 493 if (!ServiceIsolate::IsRunning()) { |
| 494 return; |
| 495 } |
| 496 Isolate* isolate = Isolate::Current(); |
| 497 ASSERT(isolate != NULL); |
| 498 HANDLESCOPE(isolate); |
| 499 |
| 500 // Construct a list of the form [eventId, eventMessage]. |
| 501 const Array& list = Array::Handle(Array::New(2)); |
| 502 ASSERT(!list.IsNull()); |
| 503 list.SetAt(0, Integer::Handle(Integer::New(eventId))); |
| 504 list.SetAt(1, eventMessage); |
| 505 |
| 506 // Push the event to port_. |
| 507 uint8_t* data = NULL; |
| 508 MessageWriter writer(&data, &allocator, false); |
| 509 writer.WriteMessage(list); |
| 510 intptr_t len = writer.BytesWritten(); |
| 511 if (FLAG_trace_service) { |
| 512 OS::Print("vm-service: Pushing event of type %" Pd ", len %" Pd "\n", |
| 513 eventId, len); |
| 514 } |
| 515 // TODO(turnidge): For now we ignore failure to send an event. Revisit? |
| 516 PortMap::PostMessage( |
| 517 new Message(ServiceIsolate::Port(), data, len, Message::kNormalPriority)); |
| 518 } |
| 519 |
| 520 |
| 521 void Service::SendEvent(intptr_t eventId, |
| 522 const String& meta, |
| 523 const uint8_t* data, |
| 524 intptr_t size) { |
| 525 // Bitstream: [meta data size (big-endian 64 bit)] [meta data (UTF-8)] [data] |
| 526 const intptr_t meta_bytes = Utf8::Length(meta); |
| 527 const intptr_t total_bytes = sizeof(uint64_t) + meta_bytes + size; |
| 528 const TypedData& message = TypedData::Handle( |
| 529 TypedData::New(kTypedDataUint8ArrayCid, total_bytes)); |
| 530 intptr_t offset = 0; |
| 531 // TODO(koda): Rename these methods SetHostUint64, etc. |
| 532 message.SetUint64(0, Utils::HostToBigEndian64(meta_bytes)); |
| 533 offset += sizeof(uint64_t); |
| 534 { |
| 535 NoGCScope no_gc; |
| 536 meta.ToUTF8(static_cast<uint8_t*>(message.DataAddr(offset)), meta_bytes); |
| 537 offset += meta_bytes; |
| 538 } |
| 539 // TODO(koda): It would be nice to avoid this copy (requires changes to |
| 540 // MessageWriter code). |
| 541 { |
| 542 NoGCScope no_gc; |
| 543 memmove(message.DataAddr(offset), data, size); |
| 544 offset += size; |
| 545 } |
| 546 ASSERT(offset == total_bytes); |
| 547 SendEvent(eventId, message); |
| 548 } |
| 549 |
| 550 |
| 551 void Service::HandleGCEvent(GCEvent* event) { |
| 552 JSONStream js; |
| 553 event->PrintJSON(&js); |
| 554 const String& message = String::Handle(String::New(js.ToCString())); |
| 555 SendEvent(kEventFamilyGC, message); |
| 556 } |
| 557 |
| 558 |
| 559 void Service::HandleDebuggerEvent(DebuggerEvent* event) { |
| 560 JSONStream js; |
| 561 event->PrintJSON(&js); |
| 562 const String& message = String::Handle(String::New(js.ToCString())); |
| 563 SendEvent(kEventFamilyDebug, message); |
| 564 } |
| 115 | 565 |
| 116 | 566 |
| 117 class EmbedderServiceHandler { | 567 class EmbedderServiceHandler { |
| 118 public: | 568 public: |
| 119 explicit EmbedderServiceHandler(const char* name) : name_(NULL), | 569 explicit EmbedderServiceHandler(const char* name) : name_(NULL), |
| 120 callback_(NULL), | 570 callback_(NULL), |
| 121 user_data_(NULL), | 571 user_data_(NULL), |
| 122 next_(NULL) { | 572 next_(NULL) { |
| 123 ASSERT(name != NULL); | 573 ASSERT(name != NULL); |
| 124 name_ = strdup(name); | 574 name_ = strdup(name); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 146 } | 596 } |
| 147 | 597 |
| 148 private: | 598 private: |
| 149 char* name_; | 599 char* name_; |
| 150 Dart_ServiceRequestCallback callback_; | 600 Dart_ServiceRequestCallback callback_; |
| 151 void* user_data_; | 601 void* user_data_; |
| 152 EmbedderServiceHandler* next_; | 602 EmbedderServiceHandler* next_; |
| 153 }; | 603 }; |
| 154 | 604 |
| 155 | 605 |
| 156 class LibraryCoverageFilter : public CoverageFilter { | 606 void Service::EmbedderHandleMessage(EmbedderServiceHandler* handler, |
| 157 public: | 607 JSONStream* js) { |
| 158 explicit LibraryCoverageFilter(const Library& lib) : lib_(lib) {} | 608 ASSERT(handler != NULL); |
| 159 bool ShouldOutputCoverageFor(const Library& lib, | 609 Dart_ServiceRequestCallback callback = handler->callback(); |
| 160 const Script& script, | 610 ASSERT(callback != NULL); |
| 161 const Class& cls, | 611 const char* r = NULL; |
| 162 const Function& func) const { | 612 const char* name = js->method(); |
| 163 return lib.raw() == lib_.raw(); | 613 const char** keys = js->param_keys(); |
| 164 } | 614 const char** values = js->param_values(); |
| 165 private: | 615 r = callback(name, keys, values, js->num_params(), handler->user_data()); |
| 166 const Library& lib_; | 616 ASSERT(r != NULL); |
| 167 }; | 617 // TODO(johnmccutchan): Allow for NULL returns? |
| 168 | 618 TextBuffer* buffer = js->buffer(); |
| 169 | 619 buffer->AddString(r); |
| 170 class ScriptCoverageFilter : public CoverageFilter { | 620 free(const_cast<char*>(r)); |
| 171 public: | 621 } |
| 172 explicit ScriptCoverageFilter(const Script& script) | 622 |
| 173 : script_(script) {} | 623 |
| 174 bool ShouldOutputCoverageFor(const Library& lib, | 624 void Service::RegisterIsolateEmbedderCallback( |
| 175 const Script& script, | 625 const char* name, |
| 176 const Class& cls, | 626 Dart_ServiceRequestCallback callback, |
| 177 const Function& func) const { | 627 void* user_data) { |
| 178 return script.raw() == script_.raw(); | 628 if (name == NULL) { |
| 179 } | 629 return; |
| 180 private: | 630 } |
| 181 const Script& script_; | 631 EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(name); |
| 182 }; | 632 if (handler != NULL) { |
| 183 | 633 // Update existing handler entry. |
| 184 | 634 handler->set_callback(callback); |
| 185 class ClassCoverageFilter : public CoverageFilter { | 635 handler->set_user_data(user_data); |
| 186 public: | 636 return; |
| 187 explicit ClassCoverageFilter(const Class& cls) : cls_(cls) {} | 637 } |
| 188 bool ShouldOutputCoverageFor(const Library& lib, | 638 // Create a new handler. |
| 189 const Script& script, | 639 handler = new EmbedderServiceHandler(name); |
| 190 const Class& cls, | 640 handler->set_callback(callback); |
| 191 const Function& func) const { | 641 handler->set_user_data(user_data); |
| 192 return cls.raw() == cls_.raw(); | 642 |
| 193 } | 643 // Insert into isolate_service_handler_head_ list. |
| 194 private: | 644 handler->set_next(isolate_service_handler_head_); |
| 195 const Class& cls_; | 645 isolate_service_handler_head_ = handler; |
| 196 }; | 646 } |
| 197 | 647 |
| 198 | 648 |
| 199 class FunctionCoverageFilter : public CoverageFilter { | 649 EmbedderServiceHandler* Service::FindIsolateEmbedderHandler( |
| 200 public: | 650 const char* name) { |
| 201 explicit FunctionCoverageFilter(const Function& func) : func_(func) {} | 651 EmbedderServiceHandler* current = isolate_service_handler_head_; |
| 202 bool ShouldOutputCoverageFor(const Library& lib, | 652 while (current != NULL) { |
| 203 const Script& script, | 653 if (strcmp(name, current->name()) == 0) { |
| 204 const Class& cls, | 654 return current; |
| 205 const Function& func) const { | 655 } |
| 206 return func.raw() == func_.raw(); | 656 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 } | 657 } |
| 500 return NULL; | 658 return NULL; |
| 501 } | 659 } |
| 502 | 660 |
| 503 const char* Service::kIsolateName = "vm-service"; | 661 |
| 504 EmbedderServiceHandler* Service::isolate_service_handler_head_ = NULL; | 662 void Service::RegisterRootEmbedderCallback( |
| 505 EmbedderServiceHandler* Service::root_service_handler_head_ = NULL; | 663 const char* name, |
| 506 Isolate* Service::service_isolate_ = NULL; | 664 Dart_ServiceRequestCallback callback, |
| 507 Dart_Port Service::service_port_ = ILLEGAL_PORT; | 665 void* user_data) { |
| 508 Dart_Port Service::load_port_ = ILLEGAL_PORT; | 666 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; | 667 return; |
| 635 } | 668 } |
| 636 if (HasServiceIsolate()) { | 669 EmbedderServiceHandler* handler = FindRootEmbedderHandler(name); |
| 637 // Service isolate already exists. | 670 if (handler != NULL) { |
| 671 // Update existing handler entry. |
| 672 handler->set_callback(callback); |
| 673 handler->set_user_data(user_data); |
| 638 return; | 674 return; |
| 639 } | 675 } |
| 640 SetServiceIsolate(isolate); | 676 // Create a new handler. |
| 641 | 677 handler = new EmbedderServiceHandler(name); |
| 642 StackZone zone(isolate); | 678 handler->set_callback(callback); |
| 643 HANDLESCOPE(isolate); | 679 handler->set_user_data(user_data); |
| 644 | 680 |
| 645 // Register dart:vmservice library. | 681 // Insert into root_service_handler_head_ list. |
| 646 const String& url_str = String::Handle(Symbols::DartVMService().raw()); | 682 handler->set_next(root_service_handler_head_); |
| 647 const Library& library = Library::Handle(Library::New(url_str)); | 683 root_service_handler_head_ = handler; |
| 648 library.Register(); | 684 } |
| 649 library.set_native_entry_resolver(VmServiceNativeResolver); | 685 |
| 650 | 686 |
| 651 // Temporarily install our library tag handler. | 687 EmbedderServiceHandler* Service::FindRootEmbedderHandler( |
| 652 isolate->set_library_tag_handler(LibraryTagHandler); | 688 const char* name) { |
| 653 | 689 EmbedderServiceHandler* current = root_service_handler_head_; |
| 654 // Get script source. | 690 while (current != NULL) { |
| 655 const char* resource = NULL; | 691 if (strcmp(name, current->name()) == 0) { |
| 656 const char* path = "/vmservice.dart"; | 692 return current; |
| 657 intptr_t r = Resources::ResourceLookup(path, &resource); | 693 } |
| 658 ASSERT(r != Resources::kNoSuchInstance); | 694 current = current->next(); |
| 659 ASSERT(resource != NULL); | 695 } |
| 660 const String& source_str = String::Handle( | 696 return NULL; |
| 661 String::FromUTF8(reinterpret_cast<const uint8_t*>(resource), r)); | 697 } |
| 662 ASSERT(!source_str.IsNull()); | 698 |
| 663 const Script& script = Script::Handle( | 699 |
| 664 isolate, Script::New(url_str, source_str, RawScript::kLibraryTag)); | 700 static const MethodParameter* get_isolate_params[] = { |
| 665 | 701 ISOLATE_PARAMETER, |
| 666 // Compile script. | 702 NULL, |
| 667 Dart_EnterScope(); // Need to enter scope for tag handler. | 703 }; |
| 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 | 704 |
| 986 | 705 |
| 987 static bool HandleIsolate(Isolate* isolate, JSONStream* js) { | 706 static bool HandleIsolate(Isolate* isolate, JSONStream* js) { |
| 988 isolate->PrintJSON(js, false); | 707 isolate->PrintJSON(js, false); |
| 989 return true; | 708 return true; |
| 990 } | 709 } |
| 991 | 710 |
| 992 | 711 |
| 712 static const MethodParameter* get_stack_params[] = { |
| 713 ISOLATE_PARAMETER, |
| 714 NULL, |
| 715 }; |
| 716 |
| 717 |
| 993 static bool HandleIsolateGetStack(Isolate* isolate, JSONStream* js) { | 718 static bool HandleIsolateGetStack(Isolate* isolate, JSONStream* js) { |
| 994 DebuggerStackTrace* stack = isolate->debugger()->StackTrace(); | 719 DebuggerStackTrace* stack = isolate->debugger()->StackTrace(); |
| 995 JSONObject jsobj(js); | 720 JSONObject jsobj(js); |
| 996 jsobj.AddProperty("type", "Stack"); | 721 jsobj.AddProperty("type", "Stack"); |
| 997 JSONArray jsarr(&jsobj, "frames"); | 722 JSONArray jsarr(&jsobj, "frames"); |
| 998 intptr_t num_frames = stack->Length(); | 723 intptr_t num_frames = stack->Length(); |
| 999 for (intptr_t i = 0; i < num_frames; i++) { | 724 for (intptr_t i = 0; i < num_frames; i++) { |
| 1000 ActivationFrame* frame = stack->FrameAt(i); | 725 ActivationFrame* frame = stack->FrameAt(i); |
| 1001 JSONObject jsobj(&jsarr); | 726 JSONObject jsobj(&jsarr); |
| 1002 frame->PrintToJSONObject(&jsobj); | 727 frame->PrintToJSONObject(&jsobj); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1041 return HandleCommonEcho(&jsobj, js); | 766 return HandleCommonEcho(&jsobj, js); |
| 1042 } | 767 } |
| 1043 | 768 |
| 1044 | 769 |
| 1045 static bool HandleIsolateEcho(Isolate* isolate, JSONStream* js) { | 770 static bool HandleIsolateEcho(Isolate* isolate, JSONStream* js) { |
| 1046 JSONObject jsobj(js); | 771 JSONObject jsobj(js); |
| 1047 return HandleCommonEcho(&jsobj, js); | 772 return HandleCommonEcho(&jsobj, js); |
| 1048 } | 773 } |
| 1049 | 774 |
| 1050 | 775 |
| 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) { | 776 static bool ContainsNonInstance(const Object& obj) { |
| 1158 if (obj.IsArray()) { | 777 if (obj.IsArray()) { |
| 1159 const Array& array = Array::Cast(obj); | 778 const Array& array = Array::Cast(obj); |
| 1160 Object& element = Object::Handle(); | 779 Object& element = Object::Handle(); |
| 1161 for (intptr_t i = 0; i < array.Length(); ++i) { | 780 for (intptr_t i = 0; i < array.Length(); ++i) { |
| 1162 element = array.At(i); | 781 element = array.At(i); |
| 1163 if (!(element.IsInstance() || element.IsNull())) { | 782 if (!(element.IsInstance() || element.IsNull())) { |
| 1164 return true; | 783 return true; |
| 1165 } | 784 } |
| 1166 } | 785 } |
| (...skipping 422 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1589 // We nil out the array after generating the response to prevent | 1208 // We nil out the array after generating the response to prevent |
| 1590 // reporting suprious references when repeatedly looking for the | 1209 // reporting suprious references when repeatedly looking for the |
| 1591 // references to an object. | 1210 // references to an object. |
| 1592 path.SetAt(i * 2, Object::null_object()); | 1211 path.SetAt(i * 2, Object::null_object()); |
| 1593 } | 1212 } |
| 1594 } | 1213 } |
| 1595 return true; | 1214 return true; |
| 1596 } | 1215 } |
| 1597 | 1216 |
| 1598 | 1217 |
| 1218 static const MethodParameter* get_inbound_references_params[] = { |
| 1219 ISOLATE_PARAMETER, |
| 1220 NULL, |
| 1221 }; |
| 1222 |
| 1223 |
| 1599 static bool HandleIsolateGetInboundReferences(Isolate* isolate, | 1224 static bool HandleIsolateGetInboundReferences(Isolate* isolate, |
| 1600 JSONStream* js) { | 1225 JSONStream* js) { |
| 1601 const char* target_id = js->LookupParam("targetId"); | 1226 const char* target_id = js->LookupParam("targetId"); |
| 1602 if (target_id == NULL) { | 1227 if (target_id == NULL) { |
| 1603 PrintMissingParamError(js, "targetId"); | 1228 PrintMissingParamError(js, "targetId"); |
| 1604 return true; | 1229 return true; |
| 1605 } | 1230 } |
| 1606 const char* limit_cstr = js->LookupParam("limit"); | 1231 const char* limit_cstr = js->LookupParam("limit"); |
| 1607 if (target_id == NULL) { | 1232 if (target_id == NULL) { |
| 1608 PrintMissingParamError(js, "limit"); | 1233 PrintMissingParamError(js, "limit"); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1687 // We nil out the array after generating the response to prevent | 1312 // We nil out the array after generating the response to prevent |
| 1688 // reporting spurious references when looking for inbound references | 1313 // reporting spurious references when looking for inbound references |
| 1689 // after looking for a retaining path. | 1314 // after looking for a retaining path. |
| 1690 for (intptr_t i = 0; i < limit; ++i) { | 1315 for (intptr_t i = 0; i < limit; ++i) { |
| 1691 path.SetAt(i * 2, Object::null_object()); | 1316 path.SetAt(i * 2, Object::null_object()); |
| 1692 } | 1317 } |
| 1693 | 1318 |
| 1694 return true; | 1319 return true; |
| 1695 } | 1320 } |
| 1696 | 1321 |
| 1322 |
| 1323 static const MethodParameter* get_retaining_path_params[] = { |
| 1324 ISOLATE_PARAMETER, |
| 1325 NULL, |
| 1326 }; |
| 1327 |
| 1328 |
| 1697 static bool HandleIsolateGetRetainingPath(Isolate* isolate, | 1329 static bool HandleIsolateGetRetainingPath(Isolate* isolate, |
| 1698 JSONStream* js) { | 1330 JSONStream* js) { |
| 1699 const char* target_id = js->LookupParam("targetId"); | 1331 const char* target_id = js->LookupParam("targetId"); |
| 1700 if (target_id == NULL) { | 1332 if (target_id == NULL) { |
| 1701 PrintMissingParamError(js, "targetId"); | 1333 PrintMissingParamError(js, "targetId"); |
| 1702 return true; | 1334 return true; |
| 1703 } | 1335 } |
| 1704 const char* limit_cstr = js->LookupParam("limit"); | 1336 const char* limit_cstr = js->LookupParam("limit"); |
| 1705 if (target_id == NULL) { | 1337 if (target_id == NULL) { |
| 1706 PrintMissingParamError(js, "limit"); | 1338 PrintMissingParamError(js, "limit"); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1730 "attempt to find a retaining path for an expired object\n"); | 1362 "attempt to find a retaining path for an expired object\n"); |
| 1731 return true; | 1363 return true; |
| 1732 } | 1364 } |
| 1733 PrintInvalidParamError(js, "targetId"); | 1365 PrintInvalidParamError(js, "targetId"); |
| 1734 return true; | 1366 return true; |
| 1735 } | 1367 } |
| 1736 return PrintRetainingPath(isolate, &obj, limit, js); | 1368 return PrintRetainingPath(isolate, &obj, limit, js); |
| 1737 } | 1369 } |
| 1738 | 1370 |
| 1739 | 1371 |
| 1372 static const MethodParameter* get_retained_size_params[] = { |
| 1373 ISOLATE_PARAMETER, |
| 1374 NULL, |
| 1375 }; |
| 1376 |
| 1377 |
| 1740 static bool HandleIsolateGetRetainedSize(Isolate* isolate, JSONStream* js) { | 1378 static bool HandleIsolateGetRetainedSize(Isolate* isolate, JSONStream* js) { |
| 1741 const char* target_id = js->LookupParam("targetId"); | 1379 const char* target_id = js->LookupParam("targetId"); |
| 1742 if (target_id == NULL) { | 1380 if (target_id == NULL) { |
| 1743 PrintMissingParamError(js, "targetId"); | 1381 PrintMissingParamError(js, "targetId"); |
| 1744 return true; | 1382 return true; |
| 1745 } | 1383 } |
| 1746 ObjectIdRing::LookupResult lookup_result; | 1384 ObjectIdRing::LookupResult lookup_result; |
| 1747 Object& obj = Object::Handle(LookupHeapObject(isolate, target_id, | 1385 Object& obj = Object::Handle(LookupHeapObject(isolate, target_id, |
| 1748 &lookup_result)); | 1386 &lookup_result)); |
| 1749 if (obj.raw() == Object::sentinel().raw()) { | 1387 if (obj.raw() == Object::sentinel().raw()) { |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1777 result.PrintJSON(js, true); | 1415 result.PrintJSON(js, true); |
| 1778 return true; | 1416 return true; |
| 1779 } | 1417 } |
| 1780 PrintError(js, "%s: Invalid 'targetId' parameter value: " | 1418 PrintError(js, "%s: Invalid 'targetId' parameter value: " |
| 1781 "id '%s' does not correspond to a " | 1419 "id '%s' does not correspond to a " |
| 1782 "library, class, or instance", js->method(), target_id); | 1420 "library, class, or instance", js->method(), target_id); |
| 1783 return true; | 1421 return true; |
| 1784 } | 1422 } |
| 1785 | 1423 |
| 1786 | 1424 |
| 1425 static const MethodParameter* eval_params[] = { |
| 1426 ISOLATE_PARAMETER, |
| 1427 NULL, |
| 1428 }; |
| 1429 |
| 1430 |
| 1787 static bool HandleIsolateEval(Isolate* isolate, JSONStream* js) { | 1431 static bool HandleIsolateEval(Isolate* isolate, JSONStream* js) { |
| 1788 const char* target_id = js->LookupParam("targetId"); | 1432 const char* target_id = js->LookupParam("targetId"); |
| 1789 if (target_id == NULL) { | 1433 if (target_id == NULL) { |
| 1790 PrintMissingParamError(js, "targetId"); | 1434 PrintMissingParamError(js, "targetId"); |
| 1791 return true; | 1435 return true; |
| 1792 } | 1436 } |
| 1793 const char* expr = js->LookupParam("expression"); | 1437 const char* expr = js->LookupParam("expression"); |
| 1794 if (expr == NULL) { | 1438 if (expr == NULL) { |
| 1795 PrintMissingParamError(js, "expression"); | 1439 PrintMissingParamError(js, "expression"); |
| 1796 return true; | 1440 return true; |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1869 | 1513 |
| 1870 intptr_t count() const { return count_; } | 1514 intptr_t count() const { return count_; } |
| 1871 | 1515 |
| 1872 private: | 1516 private: |
| 1873 const Class& cls_; | 1517 const Class& cls_; |
| 1874 const Array& storage_; | 1518 const Array& storage_; |
| 1875 intptr_t count_; | 1519 intptr_t count_; |
| 1876 }; | 1520 }; |
| 1877 | 1521 |
| 1878 | 1522 |
| 1523 static const MethodParameter* get_instances_params[] = { |
| 1524 ISOLATE_PARAMETER, |
| 1525 NULL, |
| 1526 }; |
| 1527 |
| 1528 |
| 1879 static bool HandleIsolateGetInstances(Isolate* isolate, JSONStream* js) { | 1529 static bool HandleIsolateGetInstances(Isolate* isolate, JSONStream* js) { |
| 1880 const char* target_id = js->LookupParam("classId"); | 1530 const char* target_id = js->LookupParam("classId"); |
| 1881 if (target_id == NULL) { | 1531 if (target_id == NULL) { |
| 1882 PrintMissingParamError(js, "classId"); | 1532 PrintMissingParamError(js, "classId"); |
| 1883 return true; | 1533 return true; |
| 1884 } | 1534 } |
| 1885 const char* limit_cstr = js->LookupParam("limit"); | 1535 const char* limit_cstr = js->LookupParam("limit"); |
| 1886 if (target_id == NULL) { | 1536 if (target_id == NULL) { |
| 1887 PrintMissingParamError(js, "limit"); | 1537 PrintMissingParamError(js, "limit"); |
| 1888 return true; | 1538 return true; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 1915 JSONObject jsobj(js); | 1565 JSONObject jsobj(js); |
| 1916 jsobj.AddProperty("type", "InstanceSet"); | 1566 jsobj.AddProperty("type", "InstanceSet"); |
| 1917 jsobj.AddProperty("id", "instance_set"); | 1567 jsobj.AddProperty("id", "instance_set"); |
| 1918 jsobj.AddProperty("totalCount", count); | 1568 jsobj.AddProperty("totalCount", count); |
| 1919 jsobj.AddProperty("sampleCount", storage.Length()); | 1569 jsobj.AddProperty("sampleCount", storage.Length()); |
| 1920 jsobj.AddProperty("sample", storage); | 1570 jsobj.AddProperty("sample", storage); |
| 1921 return true; | 1571 return true; |
| 1922 } | 1572 } |
| 1923 | 1573 |
| 1924 | 1574 |
| 1575 class LibraryCoverageFilter : public CoverageFilter { |
| 1576 public: |
| 1577 explicit LibraryCoverageFilter(const Library& lib) : lib_(lib) {} |
| 1578 bool ShouldOutputCoverageFor(const Library& lib, |
| 1579 const Script& script, |
| 1580 const Class& cls, |
| 1581 const Function& func) const { |
| 1582 return lib.raw() == lib_.raw(); |
| 1583 } |
| 1584 private: |
| 1585 const Library& lib_; |
| 1586 }; |
| 1587 |
| 1588 |
| 1589 class ScriptCoverageFilter : public CoverageFilter { |
| 1590 public: |
| 1591 explicit ScriptCoverageFilter(const Script& script) |
| 1592 : script_(script) {} |
| 1593 bool ShouldOutputCoverageFor(const Library& lib, |
| 1594 const Script& script, |
| 1595 const Class& cls, |
| 1596 const Function& func) const { |
| 1597 return script.raw() == script_.raw(); |
| 1598 } |
| 1599 private: |
| 1600 const Script& script_; |
| 1601 }; |
| 1602 |
| 1603 |
| 1604 class ClassCoverageFilter : public CoverageFilter { |
| 1605 public: |
| 1606 explicit ClassCoverageFilter(const Class& cls) : cls_(cls) {} |
| 1607 bool ShouldOutputCoverageFor(const Library& lib, |
| 1608 const Script& script, |
| 1609 const Class& cls, |
| 1610 const Function& func) const { |
| 1611 return cls.raw() == cls_.raw(); |
| 1612 } |
| 1613 private: |
| 1614 const Class& cls_; |
| 1615 }; |
| 1616 |
| 1617 |
| 1618 class FunctionCoverageFilter : public CoverageFilter { |
| 1619 public: |
| 1620 explicit FunctionCoverageFilter(const Function& func) : func_(func) {} |
| 1621 bool ShouldOutputCoverageFor(const Library& lib, |
| 1622 const Script& script, |
| 1623 const Class& cls, |
| 1624 const Function& func) const { |
| 1625 return func.raw() == func_.raw(); |
| 1626 } |
| 1627 private: |
| 1628 const Function& func_; |
| 1629 }; |
| 1630 |
| 1631 |
| 1632 static const MethodParameter* get_coverage_params[] = { |
| 1633 ISOLATE_PARAMETER, |
| 1634 NULL, |
| 1635 }; |
| 1636 |
| 1637 |
| 1925 static bool HandleIsolateGetCoverage(Isolate* isolate, JSONStream* js) { | 1638 static bool HandleIsolateGetCoverage(Isolate* isolate, JSONStream* js) { |
| 1926 if (!js->HasParam("targetId")) { | 1639 if (!js->HasParam("targetId")) { |
| 1927 CodeCoverage::PrintJSON(isolate, js, NULL); | 1640 CodeCoverage::PrintJSON(isolate, js, NULL); |
| 1928 return true; | 1641 return true; |
| 1929 } | 1642 } |
| 1930 const char* target_id = js->LookupParam("targetId"); | 1643 const char* target_id = js->LookupParam("targetId"); |
| 1931 Object& obj = Object::Handle(LookupHeapObject(isolate, target_id, NULL)); | 1644 Object& obj = Object::Handle(LookupHeapObject(isolate, target_id, NULL)); |
| 1932 if (obj.raw() == Object::sentinel().raw()) { | 1645 if (obj.raw() == Object::sentinel().raw()) { |
| 1933 PrintInvalidParamError(js, "targetId"); | 1646 PrintInvalidParamError(js, "targetId"); |
| 1934 return true; | 1647 return true; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1953 CodeCoverage::PrintJSON(isolate, js, &ff); | 1666 CodeCoverage::PrintJSON(isolate, js, &ff); |
| 1954 return true; | 1667 return true; |
| 1955 } | 1668 } |
| 1956 PrintError(js, "%s: Invalid 'targetId' parameter value: " | 1669 PrintError(js, "%s: Invalid 'targetId' parameter value: " |
| 1957 "id '%s' does not correspond to a " | 1670 "id '%s' does not correspond to a " |
| 1958 "script, library, class, or function", js->method(), target_id); | 1671 "script, library, class, or function", js->method(), target_id); |
| 1959 return true; | 1672 return true; |
| 1960 } | 1673 } |
| 1961 | 1674 |
| 1962 | 1675 |
| 1676 static const MethodParameter* add_breakpoint_params[] = { |
| 1677 ISOLATE_PARAMETER, |
| 1678 NULL, |
| 1679 }; |
| 1680 |
| 1681 |
| 1963 static bool HandleIsolateAddBreakpoint(Isolate* isolate, JSONStream* js) { | 1682 static bool HandleIsolateAddBreakpoint(Isolate* isolate, JSONStream* js) { |
| 1964 if (!js->HasParam("line")) { | 1683 if (!js->HasParam("line")) { |
| 1965 PrintMissingParamError(js, "line"); | 1684 PrintMissingParamError(js, "line"); |
| 1966 return true; | 1685 return true; |
| 1967 } | 1686 } |
| 1968 const char* line_param = js->LookupParam("line"); | 1687 const char* line_param = js->LookupParam("line"); |
| 1969 intptr_t line = -1; | 1688 intptr_t line = -1; |
| 1970 if (!GetIntegerId(line_param, &line)) { | 1689 if (!GetIntegerId(line_param, &line)) { |
| 1971 PrintInvalidParamError(js, "line"); | 1690 PrintInvalidParamError(js, "line"); |
| 1972 return true; | 1691 return true; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1983 isolate->debugger()->SetBreakpointAtLine(script_url, line); | 1702 isolate->debugger()->SetBreakpointAtLine(script_url, line); |
| 1984 if (bpt == NULL) { | 1703 if (bpt == NULL) { |
| 1985 PrintError(js, "Unable to set breakpoint at line %s", line_param); | 1704 PrintError(js, "Unable to set breakpoint at line %s", line_param); |
| 1986 return true; | 1705 return true; |
| 1987 } | 1706 } |
| 1988 bpt->PrintJSON(js); | 1707 bpt->PrintJSON(js); |
| 1989 return true; | 1708 return true; |
| 1990 } | 1709 } |
| 1991 | 1710 |
| 1992 | 1711 |
| 1712 static const MethodParameter* remove_breakpoint_params[] = { |
| 1713 ISOLATE_PARAMETER, |
| 1714 NULL, |
| 1715 }; |
| 1716 |
| 1717 |
| 1993 static bool HandleIsolateRemoveBreakpoint(Isolate* isolate, JSONStream* js) { | 1718 static bool HandleIsolateRemoveBreakpoint(Isolate* isolate, JSONStream* js) { |
| 1994 if (!js->HasParam("breakpointId")) { | 1719 if (!js->HasParam("breakpointId")) { |
| 1995 PrintMissingParamError(js, "breakpointId"); | 1720 PrintMissingParamError(js, "breakpointId"); |
| 1996 return true; | 1721 return true; |
| 1997 } | 1722 } |
| 1998 const char* bpt_id = js->LookupParam("breakpointId"); | 1723 const char* bpt_id = js->LookupParam("breakpointId"); |
| 1999 SourceBreakpoint* bpt = LookupBreakpoint(isolate, bpt_id); | 1724 SourceBreakpoint* bpt = LookupBreakpoint(isolate, bpt_id); |
| 2000 if (bpt == NULL) { | 1725 if (bpt == NULL) { |
| 2001 fprintf(stderr, "ERROR1"); | 1726 fprintf(stderr, "ERROR1"); |
| 2002 PrintInvalidParamError(js, "breakpointId"); | 1727 PrintInvalidParamError(js, "breakpointId"); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 2019 const String& metrics_cls_name = | 1744 const String& metrics_cls_name = |
| 2020 String::Handle(isolate, String::New("Metrics")); | 1745 String::Handle(isolate, String::New("Metrics")); |
| 2021 ASSERT(!metrics_cls_name.IsNull()); | 1746 ASSERT(!metrics_cls_name.IsNull()); |
| 2022 const Class& metrics_cls = | 1747 const Class& metrics_cls = |
| 2023 Class::Handle(isolate, prof_lib.LookupClass(metrics_cls_name)); | 1748 Class::Handle(isolate, prof_lib.LookupClass(metrics_cls_name)); |
| 2024 ASSERT(!metrics_cls.IsNull()); | 1749 ASSERT(!metrics_cls.IsNull()); |
| 2025 return metrics_cls.raw(); | 1750 return metrics_cls.raw(); |
| 2026 } | 1751 } |
| 2027 | 1752 |
| 2028 | 1753 |
| 1754 |
| 2029 static bool HandleNativeMetricsList(Isolate* isolate, JSONStream* js) { | 1755 static bool HandleNativeMetricsList(Isolate* isolate, JSONStream* js) { |
| 2030 JSONObject obj(js); | 1756 JSONObject obj(js); |
| 2031 obj.AddProperty("type", "MetricList"); | 1757 obj.AddProperty("type", "MetricList"); |
| 2032 { | 1758 { |
| 2033 JSONArray metrics(&obj, "metrics"); | 1759 JSONArray metrics(&obj, "metrics"); |
| 2034 Metric* current = isolate->metrics_list_head(); | 1760 Metric* current = isolate->metrics_list_head(); |
| 2035 while (current != NULL) { | 1761 while (current != NULL) { |
| 2036 metrics.AddValue(current); | 1762 metrics.AddValue(current); |
| 2037 current = current->next(); | 1763 current = current->next(); |
| 2038 } | 1764 } |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2099 ASSERT(result.IsString()); | 1825 ASSERT(result.IsString()); |
| 2100 TextBuffer* buffer = js->buffer(); | 1826 TextBuffer* buffer = js->buffer(); |
| 2101 buffer->AddString(String::Cast(result).ToCString()); | 1827 buffer->AddString(String::Cast(result).ToCString()); |
| 2102 return true; | 1828 return true; |
| 2103 } | 1829 } |
| 2104 PrintError(js, "Dart Metric %s not found\n", id); | 1830 PrintError(js, "Dart Metric %s not found\n", id); |
| 2105 return true; | 1831 return true; |
| 2106 } | 1832 } |
| 2107 | 1833 |
| 2108 | 1834 |
| 1835 static const MethodParameter* get_metric_list_params[] = { |
| 1836 ISOLATE_PARAMETER, |
| 1837 NULL, |
| 1838 }; |
| 1839 |
| 1840 |
| 2109 static bool HandleIsolateGetMetricList(Isolate* isolate, JSONStream* js) { | 1841 static bool HandleIsolateGetMetricList(Isolate* isolate, JSONStream* js) { |
| 2110 bool native_metrics = false; | 1842 bool native_metrics = false; |
| 2111 if (js->HasParam("type")) { | 1843 if (js->HasParam("type")) { |
| 2112 if (js->ParamIs("type", "Native")) { | 1844 if (js->ParamIs("type", "Native")) { |
| 2113 native_metrics = true; | 1845 native_metrics = true; |
| 2114 } else if (js->ParamIs("type", "Dart")) { | 1846 } else if (js->ParamIs("type", "Dart")) { |
| 2115 native_metrics = false; | 1847 native_metrics = false; |
| 2116 } else { | 1848 } else { |
| 2117 PrintInvalidParamError(js, "type"); | 1849 PrintInvalidParamError(js, "type"); |
| 2118 return true; | 1850 return true; |
| 2119 } | 1851 } |
| 2120 } else { | 1852 } else { |
| 2121 PrintMissingParamError(js, "type"); | 1853 PrintMissingParamError(js, "type"); |
| 2122 return true; | 1854 return true; |
| 2123 } | 1855 } |
| 2124 if (native_metrics) { | 1856 if (native_metrics) { |
| 2125 return HandleNativeMetricsList(isolate, js); | 1857 return HandleNativeMetricsList(isolate, js); |
| 2126 } | 1858 } |
| 2127 return HandleDartMetricsList(isolate, js); | 1859 return HandleDartMetricsList(isolate, js); |
| 2128 } | 1860 } |
| 2129 | 1861 |
| 2130 | 1862 |
| 1863 static const MethodParameter* get_metric_params[] = { |
| 1864 ISOLATE_PARAMETER, |
| 1865 NULL, |
| 1866 }; |
| 1867 |
| 1868 |
| 2131 static bool HandleIsolateGetMetric(Isolate* isolate, JSONStream* js) { | 1869 static bool HandleIsolateGetMetric(Isolate* isolate, JSONStream* js) { |
| 2132 const char* metric_id = js->LookupParam("metricId"); | 1870 const char* metric_id = js->LookupParam("metricId"); |
| 2133 if (metric_id == NULL) { | 1871 if (metric_id == NULL) { |
| 2134 PrintMissingParamError(js, "metricId"); | 1872 PrintMissingParamError(js, "metricId"); |
| 2135 return true; | 1873 return true; |
| 2136 } | 1874 } |
| 2137 // Verify id begins with "metrics/". | 1875 // Verify id begins with "metrics/". |
| 2138 static const char* kMetricIdPrefix = "metrics/"; | 1876 static const char* kMetricIdPrefix = "metrics/"; |
| 2139 static intptr_t kMetricIdPrefixLen = strlen(kMetricIdPrefix); | 1877 static intptr_t kMetricIdPrefixLen = strlen(kMetricIdPrefix); |
| 2140 if (strncmp(metric_id, kMetricIdPrefix, kMetricIdPrefixLen) != 0) { | 1878 if (strncmp(metric_id, kMetricIdPrefix, kMetricIdPrefixLen) != 0) { |
| 2141 PrintError(js, "Metric %s not found\n", metric_id); | 1879 PrintError(js, "Metric %s not found\n", metric_id); |
| 2142 } | 1880 } |
| 2143 // Check if id begins with "metrics/native/". | 1881 // Check if id begins with "metrics/native/". |
| 2144 static const char* kNativeMetricIdPrefix = "metrics/native/"; | 1882 static const char* kNativeMetricIdPrefix = "metrics/native/"; |
| 2145 static intptr_t kNativeMetricIdPrefixLen = strlen(kNativeMetricIdPrefix); | 1883 static intptr_t kNativeMetricIdPrefixLen = strlen(kNativeMetricIdPrefix); |
| 2146 const bool native_metric = | 1884 const bool native_metric = |
| 2147 strncmp(metric_id, kNativeMetricIdPrefix, kNativeMetricIdPrefixLen) == 0; | 1885 strncmp(metric_id, kNativeMetricIdPrefix, kNativeMetricIdPrefixLen) == 0; |
| 2148 if (native_metric) { | 1886 if (native_metric) { |
| 2149 const char* id = metric_id + kNativeMetricIdPrefixLen; | 1887 const char* id = metric_id + kNativeMetricIdPrefixLen; |
| 2150 return HandleNativeMetric(isolate, js, id); | 1888 return HandleNativeMetric(isolate, js, id); |
| 2151 } | 1889 } |
| 2152 const char* id = metric_id + kMetricIdPrefixLen; | 1890 const char* id = metric_id + kMetricIdPrefixLen; |
| 2153 return HandleDartMetric(isolate, js, id); | 1891 return HandleDartMetric(isolate, js, id); |
| 2154 } | 1892 } |
| 2155 | 1893 |
| 2156 | 1894 |
| 2157 static bool HandleVMGetMetricList(JSONStream* js) { | 1895 static const MethodParameter* get_vm_metric_list_params[] = { |
| 1896 NO_ISOLATE_PARAMETER, |
| 1897 NULL, |
| 1898 }; |
| 1899 |
| 1900 |
| 1901 static bool HandleVMGetMetricList(Isolate* isolate, JSONStream* js) { |
| 2158 return false; | 1902 return false; |
| 2159 } | 1903 } |
| 2160 | 1904 |
| 2161 | 1905 |
| 2162 static bool HandleVMGetMetric(JSONStream* js) { | 1906 static const MethodParameter* get_vm_metric_params[] = { |
| 1907 NO_ISOLATE_PARAMETER, |
| 1908 NULL, |
| 1909 }; |
| 1910 |
| 1911 |
| 1912 static bool HandleVMGetMetric(Isolate* isolate, JSONStream* js) { |
| 2163 const char* metric_id = js->LookupParam("metricId"); | 1913 const char* metric_id = js->LookupParam("metricId"); |
| 2164 if (metric_id == NULL) { | 1914 if (metric_id == NULL) { |
| 2165 PrintMissingParamError(js, "metricId"); | 1915 PrintMissingParamError(js, "metricId"); |
| 2166 } | 1916 } |
| 2167 return false; | 1917 return false; |
| 2168 } | 1918 } |
| 2169 | 1919 |
| 2170 | 1920 |
| 1921 static const MethodParameter* resume_params[] = { |
| 1922 ISOLATE_PARAMETER, |
| 1923 NULL, |
| 1924 }; |
| 1925 |
| 1926 |
| 2171 static bool HandleIsolateResume(Isolate* isolate, JSONStream* js) { | 1927 static bool HandleIsolateResume(Isolate* isolate, JSONStream* js) { |
| 2172 const char* step_param = js->LookupParam("step"); | 1928 const char* step_param = js->LookupParam("step"); |
| 2173 if (isolate->message_handler()->paused_on_start()) { | 1929 if (isolate->message_handler()->paused_on_start()) { |
| 2174 isolate->message_handler()->set_pause_on_start(false); | 1930 isolate->message_handler()->set_pause_on_start(false); |
| 2175 JSONObject jsobj(js); | 1931 JSONObject jsobj(js); |
| 2176 jsobj.AddProperty("type", "Success"); | 1932 jsobj.AddProperty("type", "Success"); |
| 2177 jsobj.AddProperty("id", ""); | 1933 jsobj.AddProperty("id", ""); |
| 2178 { | 1934 { |
| 2179 DebuggerEvent resumeEvent(isolate, DebuggerEvent::kIsolateResumed); | 1935 DebuggerEvent resumeEvent(isolate, DebuggerEvent::kIsolateResumed); |
| 2180 Service::HandleDebuggerEvent(&resumeEvent); | 1936 Service::HandleDebuggerEvent(&resumeEvent); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 2207 jsobj.AddProperty("type", "Success"); | 1963 jsobj.AddProperty("type", "Success"); |
| 2208 jsobj.AddProperty("id", ""); | 1964 jsobj.AddProperty("id", ""); |
| 2209 return true; | 1965 return true; |
| 2210 } | 1966 } |
| 2211 | 1967 |
| 2212 PrintError(js, "VM was not paused"); | 1968 PrintError(js, "VM was not paused"); |
| 2213 return true; | 1969 return true; |
| 2214 } | 1970 } |
| 2215 | 1971 |
| 2216 | 1972 |
| 1973 static const MethodParameter* get_breakpoints_params[] = { |
| 1974 ISOLATE_PARAMETER, |
| 1975 NULL, |
| 1976 }; |
| 1977 |
| 1978 |
| 2217 static bool HandleIsolateGetBreakpoints(Isolate* isolate, JSONStream* js) { | 1979 static bool HandleIsolateGetBreakpoints(Isolate* isolate, JSONStream* js) { |
| 2218 JSONObject jsobj(js); | 1980 JSONObject jsobj(js); |
| 2219 jsobj.AddProperty("type", "BreakpointList"); | 1981 jsobj.AddProperty("type", "BreakpointList"); |
| 2220 JSONArray jsarr(&jsobj, "breakpoints"); | 1982 JSONArray jsarr(&jsobj, "breakpoints"); |
| 2221 isolate->debugger()->PrintBreakpointsToJSONArray(&jsarr); | 1983 isolate->debugger()->PrintBreakpointsToJSONArray(&jsarr); |
| 2222 return true; | 1984 return true; |
| 2223 } | 1985 } |
| 2224 | 1986 |
| 2225 | 1987 |
| 1988 static const MethodParameter* pause_params[] = { |
| 1989 ISOLATE_PARAMETER, |
| 1990 NULL, |
| 1991 }; |
| 1992 |
| 1993 |
| 2226 static bool HandleIsolatePause(Isolate* isolate, JSONStream* js) { | 1994 static bool HandleIsolatePause(Isolate* isolate, JSONStream* js) { |
| 2227 // TODO(turnidge): Don't double-interrupt the isolate here. | 1995 // TODO(turnidge): Don't double-interrupt the isolate here. |
| 2228 isolate->ScheduleInterrupts(Isolate::kApiInterrupt); | 1996 isolate->ScheduleInterrupts(Isolate::kApiInterrupt); |
| 2229 JSONObject jsobj(js); | 1997 JSONObject jsobj(js); |
| 2230 jsobj.AddProperty("type", "Success"); | 1998 jsobj.AddProperty("type", "Success"); |
| 2231 jsobj.AddProperty("id", ""); | 1999 jsobj.AddProperty("id", ""); |
| 2232 return true; | 2000 return true; |
| 2233 } | 2001 } |
| 2234 | 2002 |
| 2235 | 2003 |
| 2004 static const MethodParameter* get_tag_profile_params[] = { |
| 2005 ISOLATE_PARAMETER, |
| 2006 NULL, |
| 2007 }; |
| 2008 |
| 2009 |
| 2236 static bool HandleIsolateGetTagProfile(Isolate* isolate, JSONStream* js) { | 2010 static bool HandleIsolateGetTagProfile(Isolate* isolate, JSONStream* js) { |
| 2237 JSONObject miniProfile(js); | 2011 JSONObject miniProfile(js); |
| 2238 miniProfile.AddProperty("type", "TagProfile"); | 2012 miniProfile.AddProperty("type", "TagProfile"); |
| 2239 miniProfile.AddProperty("id", "profile/tag"); | 2013 miniProfile.AddProperty("id", "profile/tag"); |
| 2240 isolate->vm_tag_counters()->PrintToJSONObject(&miniProfile); | 2014 isolate->vm_tag_counters()->PrintToJSONObject(&miniProfile); |
| 2241 return true; | 2015 return true; |
| 2242 } | 2016 } |
| 2243 | 2017 |
| 2018 |
| 2019 static const char* tags_enum_names[] = { |
| 2020 "None", |
| 2021 "UserVM", |
| 2022 "UserOnly", |
| 2023 "VMUser", |
| 2024 "VMOnly", |
| 2025 NULL, |
| 2026 }; |
| 2027 |
| 2028 |
| 2029 static ProfilerService::TagOrder tags_enum_values[] = { |
| 2030 ProfilerService::kNoTags, |
| 2031 ProfilerService::kUserVM, |
| 2032 ProfilerService::kUser, |
| 2033 ProfilerService::kVMUser, |
| 2034 ProfilerService::kVM, |
| 2035 ProfilerService::kNoTags, // Default value. |
| 2036 }; |
| 2037 |
| 2038 |
| 2039 static const MethodParameter* get_cpu_profile_params[] = { |
| 2040 ISOLATE_PARAMETER, |
| 2041 new EnumParameter("tags", true, tags_enum_names), |
| 2042 NULL, |
| 2043 }; |
| 2044 |
| 2045 |
| 2244 static bool HandleIsolateGetCpuProfile(Isolate* isolate, JSONStream* js) { | 2046 static bool HandleIsolateGetCpuProfile(Isolate* isolate, JSONStream* js) { |
| 2245 ProfilerService::TagOrder tag_order = ProfilerService::kUserVM; | 2047 ProfilerService::TagOrder tag_order = |
| 2246 if (js->HasParam("tags")) { | 2048 EnumMapper(js->LookupParam("tags"), tags_enum_names, tags_enum_values); |
| 2247 if (js->ParamIs("tags", "None")) { | |
| 2248 tag_order = ProfilerService::kNoTags; | |
| 2249 } else if (js->ParamIs("tags", "UserVM")) { | |
| 2250 tag_order = ProfilerService::kUserVM; | |
| 2251 } else if (js->ParamIs("tags", "UserOnly")) { | |
| 2252 tag_order = ProfilerService::kUser; | |
| 2253 } else if (js->ParamIs("tags", "VMUser")) { | |
| 2254 tag_order = ProfilerService::kVMUser; | |
| 2255 } else if (js->ParamIs("tags", "VMOnly")) { | |
| 2256 tag_order = ProfilerService::kVM; | |
| 2257 } else { | |
| 2258 PrintInvalidParamError(js, "tags"); | |
| 2259 return true; | |
| 2260 } | |
| 2261 } | |
| 2262 ProfilerService::PrintJSON(js, tag_order); | 2049 ProfilerService::PrintJSON(js, tag_order); |
| 2263 return true; | 2050 return true; |
| 2264 } | 2051 } |
| 2265 | 2052 |
| 2266 | 2053 |
| 2054 static const MethodParameter* get_allocation_profile_params[] = { |
| 2055 ISOLATE_PARAMETER, |
| 2056 NULL, |
| 2057 }; |
| 2058 |
| 2059 |
| 2267 static bool HandleIsolateGetAllocationProfile(Isolate* isolate, | 2060 static bool HandleIsolateGetAllocationProfile(Isolate* isolate, |
| 2268 JSONStream* js) { | 2061 JSONStream* js) { |
| 2269 bool should_reset_accumulator = false; | 2062 bool should_reset_accumulator = false; |
| 2270 bool should_collect = false; | 2063 bool should_collect = false; |
| 2271 if (js->HasParam("reset")) { | 2064 if (js->HasParam("reset")) { |
| 2272 if (js->ParamIs("reset", "true")) { | 2065 if (js->ParamIs("reset", "true")) { |
| 2273 should_reset_accumulator = true; | 2066 should_reset_accumulator = true; |
| 2274 } else { | 2067 } else { |
| 2275 PrintInvalidParamError(js, "reset"); | 2068 PrintInvalidParamError(js, "reset"); |
| 2276 return true; | 2069 return true; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 2290 } | 2083 } |
| 2291 if (should_collect) { | 2084 if (should_collect) { |
| 2292 isolate->UpdateLastAllocationProfileGCTimestamp(); | 2085 isolate->UpdateLastAllocationProfileGCTimestamp(); |
| 2293 isolate->heap()->CollectAllGarbage(); | 2086 isolate->heap()->CollectAllGarbage(); |
| 2294 } | 2087 } |
| 2295 isolate->class_table()->AllocationProfilePrintJSON(js); | 2088 isolate->class_table()->AllocationProfilePrintJSON(js); |
| 2296 return true; | 2089 return true; |
| 2297 } | 2090 } |
| 2298 | 2091 |
| 2299 | 2092 |
| 2093 static const MethodParameter* get_heap_map_params[] = { |
| 2094 ISOLATE_PARAMETER, |
| 2095 NULL, |
| 2096 }; |
| 2097 |
| 2098 |
| 2300 static bool HandleIsolateGetHeapMap(Isolate* isolate, JSONStream* js) { | 2099 static bool HandleIsolateGetHeapMap(Isolate* isolate, JSONStream* js) { |
| 2301 isolate->heap()->PrintHeapMapToJSONStream(isolate, js); | 2100 isolate->heap()->PrintHeapMapToJSONStream(isolate, js); |
| 2302 return true; | 2101 return true; |
| 2303 } | 2102 } |
| 2304 | 2103 |
| 2305 | 2104 |
| 2105 static const MethodParameter* request_heap_snapshot_params[] = { |
| 2106 ISOLATE_PARAMETER, |
| 2107 NULL, |
| 2108 }; |
| 2109 |
| 2110 |
| 2306 static bool HandleIsolateRequestHeapSnapshot(Isolate* isolate, JSONStream* js) { | 2111 static bool HandleIsolateRequestHeapSnapshot(Isolate* isolate, JSONStream* js) { |
| 2307 Service::SendGraphEvent(isolate); | 2112 Service::SendGraphEvent(isolate); |
| 2308 // TODO(koda): Provide some id that ties this request to async response(s). | 2113 // TODO(koda): Provide some id that ties this request to async response(s). |
| 2309 JSONObject jsobj(js); | 2114 JSONObject jsobj(js); |
| 2310 jsobj.AddProperty("type", "OK"); | 2115 jsobj.AddProperty("type", "OK"); |
| 2311 jsobj.AddProperty("id", "ok"); | 2116 jsobj.AddProperty("id", "ok"); |
| 2312 return true; | 2117 return true; |
| 2313 } | 2118 } |
| 2314 | 2119 |
| 2315 | 2120 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 2346 } | 2151 } |
| 2347 uword obj_begin = RawObject::ToAddr(obj); | 2152 uword obj_begin = RawObject::ToAddr(obj); |
| 2348 uword obj_end = obj_begin + obj->Size(); | 2153 uword obj_end = obj_begin + obj->Size(); |
| 2349 return obj_begin <= addr_ && addr_ < obj_end; | 2154 return obj_begin <= addr_ && addr_ < obj_end; |
| 2350 } | 2155 } |
| 2351 private: | 2156 private: |
| 2352 uword addr_; | 2157 uword addr_; |
| 2353 }; | 2158 }; |
| 2354 | 2159 |
| 2355 | 2160 |
| 2161 static const MethodParameter* get_object_by_address_params[] = { |
| 2162 ISOLATE_PARAMETER, |
| 2163 NULL, |
| 2164 }; |
| 2165 |
| 2166 |
| 2356 static bool HandleIsolateGetObjectByAddress(Isolate* isolate, JSONStream* js) { | 2167 static bool HandleIsolateGetObjectByAddress(Isolate* isolate, JSONStream* js) { |
| 2357 const char* addr_str = js->LookupParam("address"); | 2168 const char* addr_str = js->LookupParam("address"); |
| 2358 if (addr_str == NULL) { | 2169 if (addr_str == NULL) { |
| 2359 PrintMissingParamError(js, "address"); | 2170 PrintMissingParamError(js, "address"); |
| 2360 return true; | 2171 return true; |
| 2361 } | 2172 } |
| 2362 | 2173 |
| 2363 // Handle heap objects. | 2174 // Handle heap objects. |
| 2364 uword addr = 0; | 2175 uword addr = 0; |
| 2365 if (!GetUnsignedIntegerId(addr_str, &addr, 16)) { | 2176 if (!GetUnsignedIntegerId(addr_str, &addr, 16)) { |
| (...skipping 27 matching lines...) Expand all Loading... |
| 2393 | 2204 |
| 2394 | 2205 |
| 2395 static bool HandleIsolateRespondWithMalformedObject(Isolate* isolate, | 2206 static bool HandleIsolateRespondWithMalformedObject(Isolate* isolate, |
| 2396 JSONStream* js) { | 2207 JSONStream* js) { |
| 2397 JSONObject jsobj(js); | 2208 JSONObject jsobj(js); |
| 2398 jsobj.AddProperty("bart", "simpson"); | 2209 jsobj.AddProperty("bart", "simpson"); |
| 2399 return true; | 2210 return true; |
| 2400 } | 2211 } |
| 2401 | 2212 |
| 2402 | 2213 |
| 2214 static const MethodParameter* get_object_params[] = { |
| 2215 ISOLATE_PARAMETER, |
| 2216 NULL, |
| 2217 }; |
| 2218 |
| 2219 |
| 2403 static bool HandleIsolateGetObject(Isolate* isolate, JSONStream* js) { | 2220 static bool HandleIsolateGetObject(Isolate* isolate, JSONStream* js) { |
| 2404 const char* id = js->LookupParam("objectId"); | 2221 const char* id = js->LookupParam("objectId"); |
| 2405 if (id == NULL) { | 2222 if (id == NULL) { |
| 2406 PrintMissingParamError(js, "objectId"); | 2223 PrintMissingParamError(js, "objectId"); |
| 2407 return true; | 2224 return true; |
| 2408 } | 2225 } |
| 2409 | 2226 |
| 2410 // Handle heap objects. | 2227 // Handle heap objects. |
| 2411 ObjectIdRing::LookupResult lookup_result; | 2228 ObjectIdRing::LookupResult lookup_result; |
| 2412 const Object& obj = | 2229 const Object& obj = |
| (...skipping 15 matching lines...) Expand all Loading... |
| 2428 if (bpt != NULL) { | 2245 if (bpt != NULL) { |
| 2429 bpt->PrintJSON(js); | 2246 bpt->PrintJSON(js); |
| 2430 return true; | 2247 return true; |
| 2431 } | 2248 } |
| 2432 | 2249 |
| 2433 PrintError(js, "Unrecognized object id: %s\n", id); | 2250 PrintError(js, "Unrecognized object id: %s\n", id); |
| 2434 return true; | 2251 return true; |
| 2435 } | 2252 } |
| 2436 | 2253 |
| 2437 | 2254 |
| 2255 static const MethodParameter* get_class_list_params[] = { |
| 2256 ISOLATE_PARAMETER, |
| 2257 NULL, |
| 2258 }; |
| 2259 |
| 2260 |
| 2438 static bool HandleIsolateGetClassList(Isolate* isolate, JSONStream* js) { | 2261 static bool HandleIsolateGetClassList(Isolate* isolate, JSONStream* js) { |
| 2439 ClassTable* table = isolate->class_table(); | 2262 ClassTable* table = isolate->class_table(); |
| 2440 JSONObject jsobj(js); | 2263 JSONObject jsobj(js); |
| 2441 table->PrintToJSONObject(&jsobj); | 2264 table->PrintToJSONObject(&jsobj); |
| 2442 return true; | 2265 return true; |
| 2443 } | 2266 } |
| 2444 | 2267 |
| 2445 | 2268 |
| 2269 static const MethodParameter* get_type_arguments_list_params[] = { |
| 2270 ISOLATE_PARAMETER, |
| 2271 NULL, |
| 2272 }; |
| 2273 |
| 2274 |
| 2446 static bool HandleIsolateGetTypeArgumentsList(Isolate* isolate, | 2275 static bool HandleIsolateGetTypeArgumentsList(Isolate* isolate, |
| 2447 JSONStream* js) { | 2276 JSONStream* js) { |
| 2448 bool only_with_instantiations = false; | 2277 bool only_with_instantiations = false; |
| 2449 if (js->ParamIs("onlyWithInstantiations", "true")) { | 2278 if (js->ParamIs("onlyWithInstantiations", "true")) { |
| 2450 only_with_instantiations = true; | 2279 only_with_instantiations = true; |
| 2451 } | 2280 } |
| 2452 ObjectStore* object_store = isolate->object_store(); | 2281 ObjectStore* object_store = isolate->object_store(); |
| 2453 const Array& table = Array::Handle(object_store->canonical_type_arguments()); | 2282 const Array& table = Array::Handle(object_store->canonical_type_arguments()); |
| 2454 ASSERT(table.Length() > 0); | 2283 ASSERT(table.Length() > 0); |
| 2455 TypeArguments& type_args = TypeArguments::Handle(); | 2284 TypeArguments& type_args = TypeArguments::Handle(); |
| 2456 const intptr_t table_size = table.Length() - 1; | 2285 const intptr_t table_size = table.Length() - 1; |
| 2457 const intptr_t table_used = Smi::Value(Smi::RawCast(table.At(table_size))); | 2286 const intptr_t table_used = Smi::Value(Smi::RawCast(table.At(table_size))); |
| 2458 JSONObject jsobj(js); | 2287 JSONObject jsobj(js); |
| 2459 jsobj.AddProperty("type", "TypeArgumentsList"); | 2288 jsobj.AddProperty("type", "TypeArgumentsList"); |
| 2460 jsobj.AddProperty("canonicalTypeArgumentsTableSize", table_size); | 2289 jsobj.AddProperty("canonicalTypeArgumentsTableSize", table_size); |
| 2461 jsobj.AddProperty("canonicalTypeArgumentsTableUsed", table_used); | 2290 jsobj.AddProperty("canonicalTypeArgumentsTableUsed", table_used); |
| 2462 JSONArray members(&jsobj, "typeArguments"); | 2291 JSONArray members(&jsobj, "typeArguments"); |
| 2463 for (intptr_t i = 0; i < table_size; i++) { | 2292 for (intptr_t i = 0; i < table_size; i++) { |
| 2464 type_args ^= table.At(i); | 2293 type_args ^= table.At(i); |
| 2465 if (!type_args.IsNull()) { | 2294 if (!type_args.IsNull()) { |
| 2466 if (!only_with_instantiations || type_args.HasInstantiations()) { | 2295 if (!only_with_instantiations || type_args.HasInstantiations()) { |
| 2467 members.AddValue(type_args); | 2296 members.AddValue(type_args); |
| 2468 } | 2297 } |
| 2469 } | 2298 } |
| 2470 } | 2299 } |
| 2471 return true; | 2300 return true; |
| 2472 } | 2301 } |
| 2473 | 2302 |
| 2474 | 2303 |
| 2475 static IsolateMessageHandlerEntry isolate_handlers_new[] = { | |
| 2476 { "getIsolate", HandleIsolate }, | |
| 2477 { "getObject", HandleIsolateGetObject }, | |
| 2478 { "getObjectByAddress", HandleIsolateGetObjectByAddress }, | |
| 2479 { "getBreakpoints", HandleIsolateGetBreakpoints }, | |
| 2480 { "pause", HandleIsolatePause }, | |
| 2481 { "resume", HandleIsolateResume }, | |
| 2482 { "getStack", HandleIsolateGetStack }, | |
| 2483 { "getCpuProfile", HandleIsolateGetCpuProfile }, | |
| 2484 { "getTagProfile", HandleIsolateGetTagProfile }, | |
| 2485 { "getAllocationProfile", HandleIsolateGetAllocationProfile }, | |
| 2486 { "getHeapMap", HandleIsolateGetHeapMap }, | |
| 2487 { "addBreakpoint", HandleIsolateAddBreakpoint }, | |
| 2488 { "removeBreakpoint", HandleIsolateRemoveBreakpoint }, | |
| 2489 { "getCoverage", HandleIsolateGetCoverage }, | |
| 2490 { "eval", HandleIsolateEval }, | |
| 2491 { "getRetainedSize", HandleIsolateGetRetainedSize }, | |
| 2492 { "getRetainingPath", HandleIsolateGetRetainingPath }, | |
| 2493 { "getInboundReferences", HandleIsolateGetInboundReferences }, | |
| 2494 { "getInstances", HandleIsolateGetInstances }, | |
| 2495 { "requestHeapSnapshot", HandleIsolateRequestHeapSnapshot }, | |
| 2496 { "getClassList", HandleIsolateGetClassList }, | |
| 2497 { "getTypeArgumentsList", HandleIsolateGetTypeArgumentsList }, | |
| 2498 { "getIsolateMetricList", HandleIsolateGetMetricList }, | |
| 2499 { "getIsolateMetric", HandleIsolateGetMetric }, | |
| 2500 { "_echo", HandleIsolateEcho }, | |
| 2501 { "_triggerEchoEvent", HandleIsolateTriggerEchoEvent }, | |
| 2502 { "_respondWithMalformedJson", HandleIsolateRespondWithMalformedJson }, | |
| 2503 { "_respondWithMalformedObject", HandleIsolateRespondWithMalformedObject }, | |
| 2504 }; | |
| 2505 | |
| 2506 | |
| 2507 static IsolateMessageHandler FindIsolateMessageHandler(const char* method) { | |
| 2508 intptr_t num_message_handlers = sizeof(isolate_handlers_new) / | |
| 2509 sizeof(isolate_handlers_new[0]); | |
| 2510 for (intptr_t i = 0; i < num_message_handlers; i++) { | |
| 2511 const IsolateMessageHandlerEntry& entry = isolate_handlers_new[i]; | |
| 2512 if (strcmp(method, entry.method) == 0) { | |
| 2513 return entry.handler; | |
| 2514 } | |
| 2515 } | |
| 2516 if (FLAG_trace_service) { | |
| 2517 OS::Print("Service has no isolate message handler for <%s>\n", method); | |
| 2518 } | |
| 2519 return NULL; | |
| 2520 } | |
| 2521 | |
| 2522 | |
| 2523 void Service::HandleRootMessage(const Instance& msg_instance) { | |
| 2524 Isolate* isolate = Isolate::Current(); | |
| 2525 ASSERT(!msg_instance.IsNull()); | |
| 2526 ASSERT(msg_instance.IsArray()); | |
| 2527 | |
| 2528 { | |
| 2529 StackZone zone(isolate); | |
| 2530 HANDLESCOPE(isolate); | |
| 2531 | |
| 2532 const Array& msg = Array::Cast(msg_instance); | |
| 2533 ASSERT(msg.Length() == 5); | |
| 2534 | |
| 2535 Instance& reply_port = Instance::Handle(isolate); | |
| 2536 String& method = String::Handle(isolate); | |
| 2537 Array& param_keys = Array::Handle(isolate); | |
| 2538 Array& param_values = Array::Handle(isolate); | |
| 2539 reply_port ^= msg.At(1); | |
| 2540 method ^= msg.At(2); | |
| 2541 param_keys ^= msg.At(3); | |
| 2542 param_values ^= msg.At(4); | |
| 2543 | |
| 2544 ASSERT(!method.IsNull()); | |
| 2545 ASSERT(!param_keys.IsNull()); | |
| 2546 ASSERT(!param_values.IsNull()); | |
| 2547 ASSERT(param_keys.Length() == param_values.Length()); | |
| 2548 | |
| 2549 if (!reply_port.IsSendPort()) { | |
| 2550 FATAL("SendPort expected."); | |
| 2551 } | |
| 2552 | |
| 2553 RootMessageHandler handler = | |
| 2554 FindRootMessageHandler(method.ToCString()); | |
| 2555 { | |
| 2556 JSONStream js; | |
| 2557 js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), | |
| 2558 method, param_keys, param_values); | |
| 2559 if (handler == NULL) { | |
| 2560 // Check for an embedder handler. | |
| 2561 EmbedderServiceHandler* e_handler = | |
| 2562 FindRootEmbedderHandler(method.ToCString()); | |
| 2563 if (e_handler != NULL) { | |
| 2564 EmbedderHandleMessage(e_handler, &js); | |
| 2565 } else { | |
| 2566 if (FindIsolateMessageHandler(method.ToCString()) != NULL) { | |
| 2567 PrintMissingParamError(&js, "isolateId"); | |
| 2568 } else { | |
| 2569 PrintError(&js, "Unrecognized method: %s", method.ToCString()); | |
| 2570 } | |
| 2571 } | |
| 2572 js.PostReply(); | |
| 2573 } else { | |
| 2574 if (handler(&js)) { | |
| 2575 // Handler returns true if the reply is ready to be posted. | |
| 2576 // TODO(johnmccutchan): Support asynchronous replies. | |
| 2577 js.PostReply(); | |
| 2578 } | |
| 2579 } | |
| 2580 } | |
| 2581 } | |
| 2582 } | |
| 2583 | |
| 2584 | |
| 2585 static bool HandleRootEcho(JSONStream* js) { | |
| 2586 JSONObject jsobj(js); | |
| 2587 return HandleCommonEcho(&jsobj, js); | |
| 2588 } | |
| 2589 | |
| 2590 | |
| 2591 class ServiceIsolateVisitor : public IsolateVisitor { | 2304 class ServiceIsolateVisitor : public IsolateVisitor { |
| 2592 public: | 2305 public: |
| 2593 explicit ServiceIsolateVisitor(JSONArray* jsarr) | 2306 explicit ServiceIsolateVisitor(JSONArray* jsarr) |
| 2594 : jsarr_(jsarr) { | 2307 : jsarr_(jsarr) { |
| 2595 } | 2308 } |
| 2596 | 2309 |
| 2597 virtual ~ServiceIsolateVisitor() {} | 2310 virtual ~ServiceIsolateVisitor() {} |
| 2598 | 2311 |
| 2599 void VisitIsolate(Isolate* isolate) { | 2312 void VisitIsolate(Isolate* isolate) { |
| 2600 if (isolate != Dart::vm_isolate() && !Service::IsServiceIsolate(isolate)) { | 2313 if ((isolate != Dart::vm_isolate()) && |
| 2314 !ServiceIsolate::IsServiceIsolate(isolate)) { |
| 2601 jsarr_->AddValue(isolate); | 2315 jsarr_->AddValue(isolate); |
| 2602 } | 2316 } |
| 2603 } | 2317 } |
| 2604 | 2318 |
| 2605 private: | 2319 private: |
| 2606 JSONArray* jsarr_; | 2320 JSONArray* jsarr_; |
| 2607 }; | 2321 }; |
| 2608 | 2322 |
| 2609 | 2323 |
| 2610 static bool HandleVM(JSONStream* js) { | 2324 static const MethodParameter* get_vm_params[] = { |
| 2611 Isolate* isolate = Isolate::Current(); | 2325 NO_ISOLATE_PARAMETER, |
| 2326 NULL, |
| 2327 }; |
| 2328 |
| 2329 |
| 2330 static bool HandleVM(Isolate* isolate, JSONStream* js) { |
| 2612 JSONObject jsobj(js); | 2331 JSONObject jsobj(js); |
| 2613 jsobj.AddProperty("type", "VM"); | 2332 jsobj.AddProperty("type", "VM"); |
| 2614 jsobj.AddProperty("id", "vm"); | 2333 jsobj.AddProperty("id", "vm"); |
| 2615 jsobj.AddProperty("architectureBits", static_cast<intptr_t>(kBitsPerWord)); | 2334 jsobj.AddProperty("architectureBits", static_cast<intptr_t>(kBitsPerWord)); |
| 2616 jsobj.AddProperty("targetCPU", CPU::Id()); | 2335 jsobj.AddProperty("targetCPU", CPU::Id()); |
| 2617 jsobj.AddProperty("hostCPU", HostCPUFeatures::hardware()); | 2336 jsobj.AddProperty("hostCPU", HostCPUFeatures::hardware()); |
| 2618 jsobj.AddPropertyF("date", "%" Pd64 "", OS::GetCurrentTimeMillis()); | 2337 jsobj.AddPropertyF("date", "%" Pd64 "", OS::GetCurrentTimeMillis()); |
| 2619 jsobj.AddProperty("version", Version::String()); | 2338 jsobj.AddProperty("version", Version::String()); |
| 2620 // Send pid as a string because it allows us to avoid any issues with | 2339 // Send pid as a string because it allows us to avoid any issues with |
| 2621 // pids > 53-bits (when consumed by JavaScript). | 2340 // pids > 53-bits (when consumed by JavaScript). |
| (...skipping 10 matching lines...) Expand all Loading... |
| 2632 // Construct the isolate list. | 2351 // Construct the isolate list. |
| 2633 { | 2352 { |
| 2634 JSONArray jsarr(&jsobj, "isolates"); | 2353 JSONArray jsarr(&jsobj, "isolates"); |
| 2635 ServiceIsolateVisitor visitor(&jsarr); | 2354 ServiceIsolateVisitor visitor(&jsarr); |
| 2636 Isolate::VisitIsolates(&visitor); | 2355 Isolate::VisitIsolates(&visitor); |
| 2637 } | 2356 } |
| 2638 return true; | 2357 return true; |
| 2639 } | 2358 } |
| 2640 | 2359 |
| 2641 | 2360 |
| 2642 static bool HandleVMFlagList(JSONStream* js) { | 2361 static const MethodParameter* get_flag_list_params[] = { |
| 2362 NO_ISOLATE_PARAMETER, |
| 2363 NULL, |
| 2364 }; |
| 2365 |
| 2366 |
| 2367 static bool HandleVMFlagList(Isolate* isolate, JSONStream* js) { |
| 2643 Flags::PrintJSON(js); | 2368 Flags::PrintJSON(js); |
| 2644 return true; | 2369 return true; |
| 2645 } | 2370 } |
| 2646 | 2371 |
| 2647 | 2372 |
| 2648 static bool HandleVMSetFlag(JSONStream* js) { | 2373 static const MethodParameter* set_flags_params[] = { |
| 2374 NO_ISOLATE_PARAMETER, |
| 2375 NULL, |
| 2376 }; |
| 2377 |
| 2378 |
| 2379 static bool HandleVMSetFlag(Isolate* isolate, JSONStream* js) { |
| 2649 const char* flag_name = js->LookupParam("name"); | 2380 const char* flag_name = js->LookupParam("name"); |
| 2650 if (flag_name == NULL) { | 2381 if (flag_name == NULL) { |
| 2651 PrintMissingParamError(js, "name"); | 2382 PrintMissingParamError(js, "name"); |
| 2652 return true; | 2383 return true; |
| 2653 } | 2384 } |
| 2654 const char* flag_value = js->LookupParam("value"); | 2385 const char* flag_value = js->LookupParam("value"); |
| 2655 if (flag_value == NULL) { | 2386 if (flag_value == NULL) { |
| 2656 PrintMissingParamError(js, "value"); | 2387 PrintMissingParamError(js, "value"); |
| 2657 return true; | 2388 return true; |
| 2658 } | 2389 } |
| 2659 JSONObject jsobj(js); | 2390 JSONObject jsobj(js); |
| 2660 const char* error = NULL; | 2391 const char* error = NULL; |
| 2661 if (Flags::SetFlag(flag_name, flag_value, &error)) { | 2392 if (Flags::SetFlag(flag_name, flag_value, &error)) { |
| 2662 jsobj.AddProperty("type", "Success"); | 2393 jsobj.AddProperty("type", "Success"); |
| 2663 jsobj.AddProperty("id", ""); | 2394 jsobj.AddProperty("id", ""); |
| 2664 return true; | 2395 return true; |
| 2665 } else { | 2396 } else { |
| 2666 jsobj.AddProperty("type", "Failure"); | 2397 jsobj.AddProperty("type", "Failure"); |
| 2667 jsobj.AddProperty("id", ""); | 2398 jsobj.AddProperty("id", ""); |
| 2668 jsobj.AddProperty("message", error); | 2399 jsobj.AddProperty("message", error); |
| 2669 return true; | 2400 return true; |
| 2670 } | 2401 } |
| 2671 } | 2402 } |
| 2672 | 2403 |
| 2673 | 2404 |
| 2674 static RootMessageHandlerEntry root_handlers_new[] = { | 2405 static ServiceMethodDescriptor service_methods_[] = { |
| 2675 { "getVM", HandleVM }, | 2406 { "_echo", HandleIsolateEcho, |
| 2676 { "getFlagList", HandleVMFlagList }, | 2407 NULL }, |
| 2677 { "setFlag", HandleVMSetFlag }, | 2408 { "_respondWithMalformedJson", HandleIsolateRespondWithMalformedJson, |
| 2678 { "getVMMetricList", HandleVMGetMetricList }, | 2409 NULL }, |
| 2679 { "getVMMetric", HandleVMGetMetric }, | 2410 { "_respondWithMalformedObject", HandleIsolateRespondWithMalformedObject, |
| 2680 { "_echo", HandleRootEcho }, | 2411 NULL }, |
| 2412 { "_triggerEchoEvent", HandleIsolateTriggerEchoEvent, |
| 2413 NULL }, |
| 2414 { "addBreakpoint", HandleIsolateAddBreakpoint, |
| 2415 add_breakpoint_params }, |
| 2416 { "eval", HandleIsolateEval, |
| 2417 eval_params }, |
| 2418 { "getAllocationProfile", HandleIsolateGetAllocationProfile, |
| 2419 get_allocation_profile_params }, |
| 2420 { "getBreakpoints", HandleIsolateGetBreakpoints, |
| 2421 get_breakpoints_params }, |
| 2422 { "getClassList", HandleIsolateGetClassList, |
| 2423 get_class_list_params }, |
| 2424 { "getCoverage", HandleIsolateGetCoverage, |
| 2425 get_coverage_params }, |
| 2426 { "getCpuProfile", HandleIsolateGetCpuProfile, |
| 2427 get_cpu_profile_params }, |
| 2428 { "getFlagList", HandleVMFlagList , |
| 2429 get_flag_list_params }, |
| 2430 { "getHeapMap", HandleIsolateGetHeapMap, |
| 2431 get_heap_map_params }, |
| 2432 { "getInboundReferences", HandleIsolateGetInboundReferences, |
| 2433 get_inbound_references_params }, |
| 2434 { "getInstances", HandleIsolateGetInstances, |
| 2435 get_instances_params }, |
| 2436 { "getIsolate", HandleIsolate, |
| 2437 get_isolate_params }, |
| 2438 { "getIsolateMetric", HandleIsolateGetMetric, |
| 2439 get_metric_params }, |
| 2440 { "getIsolateMetricList", HandleIsolateGetMetricList, |
| 2441 get_metric_list_params }, |
| 2442 { "getObject", HandleIsolateGetObject, |
| 2443 get_object_params }, |
| 2444 { "getObjectByAddress", HandleIsolateGetObjectByAddress, |
| 2445 get_object_by_address_params }, |
| 2446 { "getRetainedSize", HandleIsolateGetRetainedSize, |
| 2447 get_retained_size_params }, |
| 2448 { "getRetainingPath", HandleIsolateGetRetainingPath, |
| 2449 get_retaining_path_params }, |
| 2450 { "getStack", HandleIsolateGetStack, |
| 2451 get_stack_params }, |
| 2452 { "getTagProfile", HandleIsolateGetTagProfile, |
| 2453 get_tag_profile_params }, |
| 2454 { "getTypeArgumentsList", HandleIsolateGetTypeArgumentsList, |
| 2455 get_type_arguments_list_params }, |
| 2456 { "getVM", HandleVM , |
| 2457 get_vm_params }, |
| 2458 { "getVMMetric", HandleVMGetMetric, |
| 2459 get_vm_metric_params }, |
| 2460 { "getVMMetricList", HandleVMGetMetricList, |
| 2461 get_vm_metric_list_params }, |
| 2462 { "pause", HandleIsolatePause, |
| 2463 pause_params }, |
| 2464 { "removeBreakpoint", HandleIsolateRemoveBreakpoint, |
| 2465 remove_breakpoint_params }, |
| 2466 { "resume", HandleIsolateResume, |
| 2467 resume_params }, |
| 2468 { "requestHeapSnapshot", HandleIsolateRequestHeapSnapshot, |
| 2469 request_heap_snapshot_params }, |
| 2470 { "setFlag", HandleVMSetFlag , |
| 2471 set_flags_params }, |
| 2681 }; | 2472 }; |
| 2682 | 2473 |
| 2683 | 2474 |
| 2684 static RootMessageHandler FindRootMessageHandler(const char* method) { | 2475 ServiceMethodDescriptor* FindMethod(const char* method_name) { |
| 2685 intptr_t num_message_handlers = sizeof(root_handlers_new) / | 2476 intptr_t num_methods = sizeof(service_methods_) / |
| 2686 sizeof(root_handlers_new[0]); | 2477 sizeof(service_methods_[0]); |
| 2687 for (intptr_t i = 0; i < num_message_handlers; i++) { | 2478 for (intptr_t i = 0; i < num_methods; i++) { |
| 2688 const RootMessageHandlerEntry& entry = root_handlers_new[i]; | 2479 ServiceMethodDescriptor& method = service_methods_[i]; |
| 2689 if (strcmp(method, entry.method) == 0) { | 2480 if (strcmp(method_name, method.name) == 0) { |
| 2690 return entry.handler; | 2481 return &method; |
| 2691 } | 2482 } |
| 2692 } | 2483 } |
| 2693 if (FLAG_trace_service) { | |
| 2694 OS::Print("vm-service: No root message handler for <%s>.\n", method); | |
| 2695 } | |
| 2696 return NULL; | 2484 return NULL; |
| 2697 } | 2485 } |
| 2698 | 2486 |
| 2699 | 2487 |
| 2700 void Service::SendEvent(intptr_t eventId, const Object& eventMessage) { | |
| 2701 if (!IsRunning()) { | |
| 2702 return; | |
| 2703 } | |
| 2704 Isolate* isolate = Isolate::Current(); | |
| 2705 ASSERT(isolate != NULL); | |
| 2706 HANDLESCOPE(isolate); | |
| 2707 | |
| 2708 // Construct a list of the form [eventId, eventMessage]. | |
| 2709 const Array& list = Array::Handle(Array::New(2)); | |
| 2710 ASSERT(!list.IsNull()); | |
| 2711 list.SetAt(0, Integer::Handle(Integer::New(eventId))); | |
| 2712 list.SetAt(1, eventMessage); | |
| 2713 | |
| 2714 // Push the event to port_. | |
| 2715 uint8_t* data = NULL; | |
| 2716 MessageWriter writer(&data, &allocator, false); | |
| 2717 writer.WriteMessage(list); | |
| 2718 intptr_t len = writer.BytesWritten(); | |
| 2719 if (FLAG_trace_service) { | |
| 2720 OS::Print("vm-service: Pushing event of type %" Pd ", len %" Pd "\n", | |
| 2721 eventId, len); | |
| 2722 } | |
| 2723 // TODO(turnidge): For now we ignore failure to send an event. Revisit? | |
| 2724 PortMap::PostMessage( | |
| 2725 new Message(service_port_, data, len, Message::kNormalPriority)); | |
| 2726 } | |
| 2727 | |
| 2728 | |
| 2729 void Service::SendEvent(intptr_t eventId, | |
| 2730 const String& meta, | |
| 2731 const uint8_t* data, | |
| 2732 intptr_t size) { | |
| 2733 // Bitstream: [meta data size (big-endian 64 bit)] [meta data (UTF-8)] [data] | |
| 2734 const intptr_t meta_bytes = Utf8::Length(meta); | |
| 2735 const intptr_t total_bytes = sizeof(uint64_t) + meta_bytes + size; | |
| 2736 const TypedData& message = TypedData::Handle( | |
| 2737 TypedData::New(kTypedDataUint8ArrayCid, total_bytes)); | |
| 2738 intptr_t offset = 0; | |
| 2739 // TODO(koda): Rename these methods SetHostUint64, etc. | |
| 2740 message.SetUint64(0, Utils::HostToBigEndian64(meta_bytes)); | |
| 2741 offset += sizeof(uint64_t); | |
| 2742 { | |
| 2743 NoGCScope no_gc; | |
| 2744 meta.ToUTF8(static_cast<uint8_t*>(message.DataAddr(offset)), meta_bytes); | |
| 2745 offset += meta_bytes; | |
| 2746 } | |
| 2747 // TODO(koda): It would be nice to avoid this copy (requires changes to | |
| 2748 // MessageWriter code). | |
| 2749 { | |
| 2750 NoGCScope no_gc; | |
| 2751 memmove(message.DataAddr(offset), data, size); | |
| 2752 offset += size; | |
| 2753 } | |
| 2754 ASSERT(offset == total_bytes); | |
| 2755 SendEvent(eventId, message); | |
| 2756 } | |
| 2757 | |
| 2758 | |
| 2759 void Service::HandleGCEvent(GCEvent* event) { | |
| 2760 JSONStream js; | |
| 2761 event->PrintJSON(&js); | |
| 2762 const String& message = String::Handle(String::New(js.ToCString())); | |
| 2763 SendEvent(kEventFamilyGC, message); | |
| 2764 } | |
| 2765 | |
| 2766 | |
| 2767 void Service::HandleDebuggerEvent(DebuggerEvent* event) { | |
| 2768 JSONStream js; | |
| 2769 event->PrintJSON(&js); | |
| 2770 const String& message = String::Handle(String::New(js.ToCString())); | |
| 2771 SendEvent(kEventFamilyDebug, message); | |
| 2772 } | |
| 2773 | |
| 2774 | |
| 2775 void Service::EmbedderHandleMessage(EmbedderServiceHandler* handler, | |
| 2776 JSONStream* js) { | |
| 2777 ASSERT(handler != NULL); | |
| 2778 Dart_ServiceRequestCallback callback = handler->callback(); | |
| 2779 ASSERT(callback != NULL); | |
| 2780 const char* r = NULL; | |
| 2781 const char* name = js->method(); | |
| 2782 const char** keys = js->param_keys(); | |
| 2783 const char** values = js->param_values(); | |
| 2784 r = callback(name, keys, values, js->num_params(), handler->user_data()); | |
| 2785 ASSERT(r != NULL); | |
| 2786 // TODO(johnmccutchan): Allow for NULL returns? | |
| 2787 TextBuffer* buffer = js->buffer(); | |
| 2788 buffer->AddString(r); | |
| 2789 free(const_cast<char*>(r)); | |
| 2790 } | |
| 2791 | |
| 2792 | |
| 2793 void Service::RegisterIsolateEmbedderCallback( | |
| 2794 const char* name, | |
| 2795 Dart_ServiceRequestCallback callback, | |
| 2796 void* user_data) { | |
| 2797 if (name == NULL) { | |
| 2798 return; | |
| 2799 } | |
| 2800 EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(name); | |
| 2801 if (handler != NULL) { | |
| 2802 // Update existing handler entry. | |
| 2803 handler->set_callback(callback); | |
| 2804 handler->set_user_data(user_data); | |
| 2805 return; | |
| 2806 } | |
| 2807 // Create a new handler. | |
| 2808 handler = new EmbedderServiceHandler(name); | |
| 2809 handler->set_callback(callback); | |
| 2810 handler->set_user_data(user_data); | |
| 2811 | |
| 2812 // Insert into isolate_service_handler_head_ list. | |
| 2813 handler->set_next(isolate_service_handler_head_); | |
| 2814 isolate_service_handler_head_ = handler; | |
| 2815 } | |
| 2816 | |
| 2817 | |
| 2818 EmbedderServiceHandler* Service::FindIsolateEmbedderHandler( | |
| 2819 const char* name) { | |
| 2820 EmbedderServiceHandler* current = isolate_service_handler_head_; | |
| 2821 while (current != NULL) { | |
| 2822 if (strcmp(name, current->name()) == 0) { | |
| 2823 return current; | |
| 2824 } | |
| 2825 current = current->next(); | |
| 2826 } | |
| 2827 return NULL; | |
| 2828 } | |
| 2829 | |
| 2830 | |
| 2831 void Service::RegisterRootEmbedderCallback( | |
| 2832 const char* name, | |
| 2833 Dart_ServiceRequestCallback callback, | |
| 2834 void* user_data) { | |
| 2835 if (name == NULL) { | |
| 2836 return; | |
| 2837 } | |
| 2838 EmbedderServiceHandler* handler = FindRootEmbedderHandler(name); | |
| 2839 if (handler != NULL) { | |
| 2840 // Update existing handler entry. | |
| 2841 handler->set_callback(callback); | |
| 2842 handler->set_user_data(user_data); | |
| 2843 return; | |
| 2844 } | |
| 2845 // Create a new handler. | |
| 2846 handler = new EmbedderServiceHandler(name); | |
| 2847 handler->set_callback(callback); | |
| 2848 handler->set_user_data(user_data); | |
| 2849 | |
| 2850 // Insert into root_service_handler_head_ list. | |
| 2851 handler->set_next(root_service_handler_head_); | |
| 2852 root_service_handler_head_ = handler; | |
| 2853 } | |
| 2854 | |
| 2855 | |
| 2856 EmbedderServiceHandler* Service::FindRootEmbedderHandler( | |
| 2857 const char* name) { | |
| 2858 EmbedderServiceHandler* current = root_service_handler_head_; | |
| 2859 while (current != NULL) { | |
| 2860 if (strcmp(name, current->name()) == 0) { | |
| 2861 return current; | |
| 2862 } | |
| 2863 current = current->next(); | |
| 2864 } | |
| 2865 return NULL; | |
| 2866 } | |
| 2867 | |
| 2868 } // namespace dart | 2488 } // namespace dart |
| OLD | NEW |