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