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
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/service.h"
6
7#include <memory>
8#include <utility>
9
10#include "include/dart_api.h"
11#include "include/dart_native_api.h"
12#include "platform/globals.h"
13
14#include "platform/unicode.h"
15#include "vm/base64.h"
16#include "vm/compiler/jit/compiler.h"
17#include "vm/cpu.h"
18#include "vm/dart_api_impl.h"
19#include "vm/dart_api_message.h"
20#include "vm/dart_api_state.h"
21#include "vm/dart_entry.h"
22#include "vm/debugger.h"
23#include "vm/heap/safepoint.h"
24#include "vm/isolate.h"
25#include "vm/kernel_isolate.h"
26#include "vm/lockers.h"
27#include "vm/malloc_hooks.h"
28#include "vm/message.h"
29#include "vm/message_handler.h"
30#include "vm/native_arguments.h"
31#include "vm/native_entry.h"
32#include "vm/native_symbol.h"
33#include "vm/object.h"
34#include "vm/object_graph.h"
35#include "vm/object_id_ring.h"
36#include "vm/object_store.h"
37#include "vm/parser.h"
38#include "vm/port.h"
39#include "vm/profiler.h"
40#include "vm/profiler_service.h"
41#include "vm/reusable_handles.h"
42#include "vm/service_event.h"
43#include "vm/service_isolate.h"
44#include "vm/source_report.h"
45#include "vm/stack_frame.h"
46#include "vm/symbols.h"
47#include "vm/timeline.h"
48#include "vm/type_table.h"
49#include "vm/version.h"
50
51namespace dart {
52
53#define Z (T->zone())
54
55DECLARE_FLAG(bool, trace_service);
56DECLARE_FLAG(bool, trace_service_pause_events);
57DECLARE_FLAG(bool, profile_vm);
58DEFINE_FLAG(charp,
59 vm_name,
60 "vm",
61 "The default name of this vm as reported by the VM service "
62 "protocol");
63
64DEFINE_FLAG(bool,
65 warn_on_pause_with_no_debugger,
66 false,
67 "Print a message when an isolate is paused but there is no "
68 "debugger attached.");
69
70#ifndef PRODUCT
71// The name of this of this vm as reported by the VM service protocol.
72static char* vm_name = NULL;
73
74static const char* GetVMName() {
75 if (vm_name == NULL) {
76 return FLAG_vm_name;
77 }
78 return vm_name;
79}
80
81ServiceIdZone::ServiceIdZone() {}
82
83ServiceIdZone::~ServiceIdZone() {}
84
85RingServiceIdZone::RingServiceIdZone()
86 : ring_(NULL), policy_(ObjectIdRing::kAllocateId) {}
87
88RingServiceIdZone::~RingServiceIdZone() {}
89
90void RingServiceIdZone::Init(ObjectIdRing* ring,
91 ObjectIdRing::IdPolicy policy) {
92 ring_ = ring;
93 policy_ = policy;
94}
95
96char* RingServiceIdZone::GetServiceId(const Object& obj) {
97 ASSERT(ring_ != NULL);
98 Thread* thread = Thread::Current();
99 Zone* zone = thread->zone();
100 ASSERT(zone != NULL);
101 const intptr_t id = ring_->GetIdForObject(obj.raw(), policy_);
102 return zone->PrintToString("objects/%" Pd "", id);
103}
104
105// TODO(johnmccutchan): Unify embedder service handler lists and their APIs.
106EmbedderServiceHandler* Service::isolate_service_handler_head_ = NULL;
107EmbedderServiceHandler* Service::root_service_handler_head_ = NULL;
108struct ServiceMethodDescriptor;
109const ServiceMethodDescriptor* FindMethod(const char* method_name);
110
111// Support for streams defined in embedders.
112Dart_ServiceStreamListenCallback Service::stream_listen_callback_ = NULL;
113Dart_ServiceStreamCancelCallback Service::stream_cancel_callback_ = NULL;
114Dart_GetVMServiceAssetsArchive Service::get_service_assets_callback_ = NULL;
115Dart_EmbedderInformationCallback Service::embedder_information_callback_ = NULL;
116
117// These are the set of streams known to the core VM.
118StreamInfo Service::vm_stream("VM");
119StreamInfo Service::isolate_stream("Isolate");
120StreamInfo Service::debug_stream("Debug");
121StreamInfo Service::gc_stream("GC");
122StreamInfo Service::echo_stream("_Echo");
123StreamInfo Service::heapsnapshot_stream("HeapSnapshot");
124StreamInfo Service::logging_stream("Logging");
125StreamInfo Service::extension_stream("Extension");
126StreamInfo Service::timeline_stream("Timeline");
127
128const uint8_t* Service::dart_library_kernel_ = NULL;
129intptr_t Service::dart_library_kernel_len_ = 0;
130
131static StreamInfo* streams_[] = {
132 &Service::vm_stream, &Service::isolate_stream,
133 &Service::debug_stream, &Service::gc_stream,
134 &Service::echo_stream, &Service::heapsnapshot_stream,
135 &Service::logging_stream, &Service::extension_stream,
136 &Service::timeline_stream};
137
138bool Service::ListenStream(const char* stream_id) {
139 if (FLAG_trace_service) {
140 OS::PrintErr("vm-service: starting stream '%s'\n", stream_id);
141 }
142 intptr_t num_streams = sizeof(streams_) / sizeof(streams_[0]);
143 for (intptr_t i = 0; i < num_streams; i++) {
144 if (strcmp(stream_id, streams_[i]->id()) == 0) {
145 streams_[i]->set_enabled(true);
146 return true;
147 }
148 }
149 if (stream_listen_callback_ != nullptr) {
150 Thread* T = Thread::Current();
151 TransitionVMToNative transition(T);
152 return (*stream_listen_callback_)(stream_id);
153 }
154 return false;
155}
156
157void Service::CancelStream(const char* stream_id) {
158 if (FLAG_trace_service) {
159 OS::PrintErr("vm-service: stopping stream '%s'\n", stream_id);
160 }
161 intptr_t num_streams = sizeof(streams_) / sizeof(streams_[0]);
162 for (intptr_t i = 0; i < num_streams; i++) {
163 if (strcmp(stream_id, streams_[i]->id()) == 0) {
164 streams_[i]->set_enabled(false);
165 return;
166 }
167 }
168 if (stream_cancel_callback_ != nullptr) {
169 Thread* T = Thread::Current();
170 TransitionVMToNative transition(T);
171 return (*stream_cancel_callback_)(stream_id);
172 }
173}
174
175ObjectPtr Service::RequestAssets() {
176 Thread* T = Thread::Current();
177 Object& object = Object::Handle();
178 {
179 Api::Scope api_scope(T);
180 Dart_Handle handle;
181 {
182 TransitionVMToNative transition(T);
183 if (get_service_assets_callback_ == NULL) {
184 return Object::null();
185 }
186 handle = get_service_assets_callback_();
187 if (Dart_IsError(handle)) {
188 Dart_PropagateError(handle);
189 }
190 }
191 object = Api::UnwrapHandle(handle);
192 }
193 if (object.IsNull()) {
194 return Object::null();
195 }
196 if (!object.IsTypedData()) {
197 const String& error_message = String::Handle(
198 String::New("An implementation of Dart_GetVMServiceAssetsArchive "
199 "should return a Uint8Array or null."));
200 const Error& error = Error::Handle(ApiError::New(error_message));
201 Exceptions::PropagateError(error);
202 return Object::null();
203 }
204 const TypedData& typed_data = TypedData::Cast(object);
205 if (typed_data.ElementSizeInBytes() != 1) {
206 const String& error_message = String::Handle(
207 String::New("An implementation of Dart_GetVMServiceAssetsArchive "
208 "should return a Uint8Array or null."));
209 const Error& error = Error::Handle(ApiError::New(error_message));
210 Exceptions::PropagateError(error);
211 return Object::null();
212 }
213 return object.raw();
214}
215
216static void PrintMissingParamError(JSONStream* js, const char* param) {
217 js->PrintError(kInvalidParams, "%s expects the '%s' parameter", js->method(),
218 param);
219}
220
221static void PrintInvalidParamError(JSONStream* js, const char* param) {
222 js->PrintError(kInvalidParams, "%s: invalid '%s' parameter: %s", js->method(),
223 param, js->LookupParam(param));
224}
225
226static void PrintUnrecognizedMethodError(JSONStream* js) {
227 js->PrintError(kMethodNotFound, NULL);
228}
229
230static void PrintSuccess(JSONStream* js) {
231 JSONObject jsobj(js);
232 jsobj.AddProperty("type", "Success");
233}
234
235static bool CheckDebuggerDisabled(Thread* thread, JSONStream* js) {
236#if defined(DART_PRECOMPILED_RUNTIME)
237 js->PrintError(kFeatureDisabled, "Debugger is disabled in AOT mode.");
238 return true;
239#else
240 if (thread->isolate()->debugger() == NULL) {
241 js->PrintError(kFeatureDisabled, "Debugger is disabled.");
242 return true;
243 }
244 return false;
245#endif
246}
247
248static bool CheckCompilerDisabled(Thread* thread, JSONStream* js) {
249#if defined(DART_PRECOMPILED_RUNTIME)
250 js->PrintError(kFeatureDisabled, "Compiler is disabled in AOT mode.");
251 return true;
252#else
253 return false;
254#endif
255}
256
257static bool CheckProfilerDisabled(Thread* thread, JSONStream* js) {
258 if (!FLAG_profiler) {
259 js->PrintError(kFeatureDisabled, "Profiler is disabled.");
260 return true;
261 }
262 return false;
263}
264
265static bool CheckNativeAllocationProfilerDisabled(Thread* thread,
266 JSONStream* js) {
267 if (CheckProfilerDisabled(thread, js)) {
268 return true;
269 }
270 if (!FLAG_profiler_native_memory) {
271 js->PrintError(kFeatureDisabled, "Native memory profiling is disabled.");
272 return true;
273 }
274 return false;
275}
276
277static bool GetIntegerId(const char* s, intptr_t* id, int base = 10) {
278 if ((s == NULL) || (*s == '\0')) {
279 // Empty string.
280 return false;
281 }
282 if (id == NULL) {
283 // No id pointer.
284 return false;
285 }
286 intptr_t r = 0;
287 char* end_ptr = NULL;
288#if defined(ARCH_IS_32_BIT)
289 r = strtol(s, &end_ptr, base);
290#else
291 r = strtoll(s, &end_ptr, base);
292#endif
293 if (end_ptr == s) {
294 // String was not advanced at all, cannot be valid.
295 return false;
296 }
297 *id = r;
298 return true;
299}
300
301static bool GetUnsignedIntegerId(const char* s, uintptr_t* id, int base = 10) {
302 if ((s == NULL) || (*s == '\0')) {
303 // Empty string.
304 return false;
305 }
306 if (id == NULL) {
307 // No id pointer.
308 return false;
309 }
310 uintptr_t r = 0;
311 char* end_ptr = NULL;
312#if defined(ARCH_IS_32_BIT)
313 r = strtoul(s, &end_ptr, base);
314#else
315 r = strtoull(s, &end_ptr, base);
316#endif
317 if (end_ptr == s) {
318 // String was not advanced at all, cannot be valid.
319 return false;
320 }
321 *id = r;
322 return true;
323}
324
325static bool GetInteger64Id(const char* s, int64_t* id, int base = 10) {
326 if ((s == NULL) || (*s == '\0')) {
327 // Empty string.
328 return false;
329 }
330 if (id == NULL) {
331 // No id pointer.
332 return false;
333 }
334 int64_t r = 0;
335 char* end_ptr = NULL;
336 r = strtoll(s, &end_ptr, base);
337 if (end_ptr == s) {
338 // String was not advanced at all, cannot be valid.
339 return false;
340 }
341 *id = r;
342 return true;
343}
344
345// Scans the string until the '-' character. Returns pointer to string
346// at '-' character. Returns NULL if not found.
347static const char* ScanUntilDash(const char* s) {
348 if ((s == NULL) || (*s == '\0')) {
349 // Empty string.
350 return NULL;
351 }
352 while (*s != '\0') {
353 if (*s == '-') {
354 return s;
355 }
356 s++;
357 }
358 return NULL;
359}
360
361static bool GetCodeId(const char* s, int64_t* timestamp, uword* address) {
362 if ((s == NULL) || (*s == '\0')) {
363 // Empty string.
364 return false;
365 }
366 if ((timestamp == NULL) || (address == NULL)) {
367 // Bad arguments.
368 return false;
369 }
370 // Extract the timestamp.
371 if (!GetInteger64Id(s, timestamp, 16) || (*timestamp < 0)) {
372 return false;
373 }
374 s = ScanUntilDash(s);
375 if (s == NULL) {
376 return false;
377 }
378 // Skip the dash.
379 s++;
380 // Extract the PC.
381 if (!GetUnsignedIntegerId(s, address, 16)) {
382 return false;
383 }
384 return true;
385}
386
387// Verifies that |s| begins with |prefix| and then calls |GetIntegerId| on
388// the remainder of |s|.
389static bool GetPrefixedIntegerId(const char* s,
390 const char* prefix,
391 intptr_t* service_id) {
392 if (s == NULL) {
393 return false;
394 }
395 ASSERT(prefix != NULL);
396 const intptr_t kInputLen = strlen(s);
397 const intptr_t kPrefixLen = strlen(prefix);
398 ASSERT(kPrefixLen > 0);
399 if (kInputLen <= kPrefixLen) {
400 return false;
401 }
402 if (strncmp(s, prefix, kPrefixLen) != 0) {
403 return false;
404 }
405 // Prefix satisfied. Move forward.
406 s += kPrefixLen;
407 // Attempt to read integer id.
408 return GetIntegerId(s, service_id);
409}
410
411static bool IsValidClassId(Isolate* isolate, intptr_t cid) {
412 ASSERT(isolate != NULL);
413 ClassTable* class_table = isolate->class_table();
414 ASSERT(class_table != NULL);
415 return class_table->IsValidIndex(cid) && class_table->HasValidClassAt(cid);
416}
417
418static ClassPtr GetClassForId(Isolate* isolate, intptr_t cid) {
419 ASSERT(isolate == Isolate::Current());
420 ASSERT(isolate != NULL);
421 ClassTable* class_table = isolate->class_table();
422 ASSERT(class_table != NULL);
423 return class_table->At(cid);
424}
425
426// TODO(johnmccutchan): Split into separate file and write unit tests.
427class MethodParameter {
428 public:
429 MethodParameter(const char* name, bool required)
430 : name_(name), required_(required) {}
431
432 virtual ~MethodParameter() {}
433
434 virtual bool Validate(const char* value) const { return true; }
435
436 virtual bool ValidateObject(const Object& value) const { return true; }
437
438 const char* name() const { return name_; }
439
440 bool required() const { return required_; }
441
442 virtual void PrintError(const char* name,
443 const char* value,
444 JSONStream* js) const {
445 PrintInvalidParamError(js, name);
446 }
447
448 virtual void PrintErrorObject(const char* name,
449 const Object& value,
450 JSONStream* js) const {
451 PrintInvalidParamError(js, name);
452 }
453
454 private:
455 const char* name_;
456 bool required_;
457};
458
459class DartStringParameter : public MethodParameter {
460 public:
461 DartStringParameter(const char* name, bool required)
462 : MethodParameter(name, required) {}
463
464 virtual bool ValidateObject(const Object& value) const {
465 return value.IsString();
466 }
467};
468
469class DartListParameter : public MethodParameter {
470 public:
471 DartListParameter(const char* name, bool required)
472 : MethodParameter(name, required) {}
473
474 virtual bool ValidateObject(const Object& value) const {
475 return value.IsArray() || value.IsGrowableObjectArray();
476 }
477};
478
479class NoSuchParameter : public MethodParameter {
480 public:
481 explicit NoSuchParameter(const char* name) : MethodParameter(name, false) {}
482
483 virtual bool Validate(const char* value) const { return (value == NULL); }
484
485 virtual bool ValidateObject(const Object& value) const {
486 return value.IsNull();
487 }
488};
489
490class BoolParameter : public MethodParameter {
491 public:
492 BoolParameter(const char* name, bool required)
493 : MethodParameter(name, required) {}
494
495 virtual bool Validate(const char* value) const {
496 if (value == NULL) {
497 return false;
498 }
499 return (strcmp("true", value) == 0) || (strcmp("false", value) == 0);
500 }
501
502 static bool Parse(const char* value, bool default_value = false) {
503 if (value == NULL) {
504 return default_value;
505 }
506 return strcmp("true", value) == 0;
507 }
508};
509
510class UIntParameter : public MethodParameter {
511 public:
512 UIntParameter(const char* name, bool required)
513 : MethodParameter(name, required) {}
514
515 virtual bool Validate(const char* value) const {
516 if (value == NULL) {
517 return false;
518 }
519 for (const char* cp = value; *cp != '\0'; cp++) {
520 if (*cp < '0' || *cp > '9') {
521 return false;
522 }
523 }
524 return true;
525 }
526
527 static uintptr_t Parse(const char* value) {
528 if (value == NULL) {
529 return -1;
530 }
531 char* end_ptr = NULL;
532 uintptr_t result = strtoul(value, &end_ptr, 10);
533 ASSERT(*end_ptr == '\0'); // Parsed full string
534 return result;
535 }
536};
537
538class Int64Parameter : public MethodParameter {
539 public:
540 Int64Parameter(const char* name, bool required)
541 : MethodParameter(name, required) {}
542
543 virtual bool Validate(const char* value) const {
544 if (value == NULL) {
545 return false;
546 }
547 for (const char* cp = value; *cp != '\0'; cp++) {
548 if ((*cp < '0' || *cp > '9') && (*cp != '-')) {
549 return false;
550 }
551 }
552 return true;
553 }
554
555 static int64_t Parse(const char* value, int64_t default_value = -1) {
556 if ((value == NULL) || (*value == '\0')) {
557 return default_value;
558 }
559 char* end_ptr = NULL;
560 int64_t result = strtoll(value, &end_ptr, 10);
561 ASSERT(*end_ptr == '\0'); // Parsed full string
562 return result;
563 }
564};
565
566class UInt64Parameter : public MethodParameter {
567 public:
568 UInt64Parameter(const char* name, bool required)
569 : MethodParameter(name, required) {}
570
571 virtual bool Validate(const char* value) const {
572 if (value == NULL) {
573 return false;
574 }
575 for (const char* cp = value; *cp != '\0'; cp++) {
576 if ((*cp < '0' || *cp > '9') && (*cp != '-')) {
577 return false;
578 }
579 }
580 return true;
581 }
582
583 static uint64_t Parse(const char* value, uint64_t default_value = 0) {
584 if ((value == NULL) || (*value == '\0')) {
585 return default_value;
586 }
587 char* end_ptr = NULL;
588 uint64_t result = strtoull(value, &end_ptr, 10);
589 ASSERT(*end_ptr == '\0'); // Parsed full string
590 return result;
591 }
592};
593
594class IdParameter : public MethodParameter {
595 public:
596 IdParameter(const char* name, bool required)
597 : MethodParameter(name, required) {}
598
599 virtual bool Validate(const char* value) const { return (value != NULL); }
600};
601
602class StringParameter : public MethodParameter {
603 public:
604 StringParameter(const char* name, bool required)
605 : MethodParameter(name, required) {}
606
607 virtual bool Validate(const char* value) const { return (value != NULL); }
608};
609
610class RunnableIsolateParameter : public MethodParameter {
611 public:
612 explicit RunnableIsolateParameter(const char* name)
613 : MethodParameter(name, true) {}
614
615 virtual bool Validate(const char* value) const {
616 Isolate* isolate = Isolate::Current();
617 return (value != NULL) && (isolate != NULL) && (isolate->is_runnable());
618 }
619
620 virtual void PrintError(const char* name,
621 const char* value,
622 JSONStream* js) const {
623 js->PrintError(kIsolateMustBeRunnable,
624 "Isolate must be runnable before this request is made.");
625 }
626};
627
628#define ISOLATE_PARAMETER new IdParameter("isolateId", true)
629#define ISOLATE_GROUP_PARAMETER new IdParameter("isolateGroupId", true)
630#define NO_ISOLATE_PARAMETER new NoSuchParameter("isolateId")
631#define RUNNABLE_ISOLATE_PARAMETER new RunnableIsolateParameter("isolateId")
632
633class EnumParameter : public MethodParameter {
634 public:
635 EnumParameter(const char* name, bool required, const char* const* enums)
636 : MethodParameter(name, required), enums_(enums) {}
637
638 virtual bool Validate(const char* value) const {
639 if (value == NULL) {
640 return true;
641 }
642 for (intptr_t i = 0; enums_[i] != NULL; i++) {
643 if (strcmp(value, enums_[i]) == 0) {
644 return true;
645 }
646 }
647 return false;
648 }
649
650 private:
651 const char* const* enums_;
652};
653
654// If the key is not found, this function returns the last element in the
655// values array. This can be used to encode the default value.
656template <typename T>
657T EnumMapper(const char* value, const char* const* enums, T* values) {
658 ASSERT(value != NULL);
659 intptr_t i = 0;
660 for (i = 0; enums[i] != NULL; i++) {
661 if (strcmp(value, enums[i]) == 0) {
662 return values[i];
663 }
664 }
665 // Default value.
666 return values[i];
667}
668
669class EnumListParameter : public MethodParameter {
670 public:
671 EnumListParameter(const char* name, bool required, const char* const* enums)
672 : MethodParameter(name, required), enums_(enums) {}
673
674 virtual bool Validate(const char* value) const {
675 return ElementCount(value) >= 0;
676 }
677
678 const char** Parse(Zone* zone, const char* value_in) const {
679 const char* kJsonChars = " \t\r\n[,]";
680
681 // Make a writeable copy of the value.
682 char* value = zone->MakeCopyOfString(value_in);
683 intptr_t element_count = ElementCount(value);
684 intptr_t element_pos = 0;
685
686 // Allocate our element array. +1 for NULL terminator.
687 char** elements = zone->Alloc<char*>(element_count + 1);
688 elements[element_count] = NULL;
689
690 // Parse the string destructively. Build the list of elements.
691 while (element_pos < element_count) {
692 // Skip to the next element.
693 value += strspn(value, kJsonChars);
694
695 intptr_t len = strcspn(value, kJsonChars);
696 ASSERT(len > 0); // We rely on the parameter being validated already.
697 value[len] = '\0';
698 elements[element_pos++] = value;
699
700 // Advance. +1 for null terminator.
701 value += (len + 1);
702 }
703 return const_cast<const char**>(elements);
704 }
705
706 private:
707 // For now observatory enums are ascii letters plus underscore.
708 static bool IsEnumChar(char c) {
709 return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) ||
710 (c == '_'));
711 }
712
713 // Returns number of elements in the list. -1 on parse error.
714 intptr_t ElementCount(const char* value) const {
715 const char* kJsonWhitespaceChars = " \t\r\n";
716 if (value == NULL) {
717 return -1;
718 }
719 const char* cp = value;
720 cp += strspn(cp, kJsonWhitespaceChars);
721 if (*cp++ != '[') {
722 // Missing initial [.
723 return -1;
724 }
725 bool closed = false;
726 bool element_allowed = true;
727 intptr_t element_count = 0;
728 while (true) {
729 // Skip json whitespace.
730 cp += strspn(cp, kJsonWhitespaceChars);
731 switch (*cp) {
732 case '\0':
733 return closed ? element_count : -1;
734 case ']':
735 closed = true;
736 cp++;
737 break;
738 case ',':
739 if (element_allowed) {
740 return -1;
741 }
742 element_allowed = true;
743 cp++;
744 break;
745 default:
746 if (!element_allowed) {
747 return -1;
748 }
749 bool valid_enum = false;
750 const char* id_start = cp;
751 while (IsEnumChar(*cp)) {
752 cp++;
753 }
754 if (cp == id_start) {
755 // Empty identifier, something like this [,].
756 return -1;
757 }
758 intptr_t id_len = cp - id_start;
759 if (enums_ != NULL) {
760 for (intptr_t i = 0; enums_[i] != NULL; i++) {
761 intptr_t len = strlen(enums_[i]);
762 if (len == id_len && strncmp(id_start, enums_[i], len) == 0) {
763 element_count++;
764 valid_enum = true;
765 element_allowed = false; // we need a comma first.
766 break;
767 }
768 }
769 }
770 if (!valid_enum) {
771 return -1;
772 }
773 break;
774 }
775 }
776 }
777
778 const char* const* enums_;
779};
780
781typedef bool (*ServiceMethodEntry)(Thread* thread, JSONStream* js);
782
783struct ServiceMethodDescriptor {
784 const char* name;
785 const ServiceMethodEntry entry;
786 const MethodParameter* const* parameters;
787};
788
789// TODO(johnmccutchan): Do we reject unexpected parameters?
790static bool ValidateParameters(const MethodParameter* const* parameters,
791 JSONStream* js) {
792 if (parameters == NULL) {
793 return true;
794 }
795 if (js->NumObjectParameters() > 0) {
796 Object& value = Object::Handle();
797 for (intptr_t i = 0; parameters[i] != NULL; i++) {
798 const MethodParameter* parameter = parameters[i];
799 const char* name = parameter->name();
800 const bool required = parameter->required();
801 value = js->LookupObjectParam(name);
802 const bool has_parameter = !value.IsNull();
803 if (required && !has_parameter) {
804 PrintMissingParamError(js, name);
805 return false;
806 }
807 if (has_parameter && !parameter->ValidateObject(value)) {
808 parameter->PrintErrorObject(name, value, js);
809 return false;
810 }
811 }
812 } else {
813 for (intptr_t i = 0; parameters[i] != NULL; i++) {
814 const MethodParameter* parameter = parameters[i];
815 const char* name = parameter->name();
816 const bool required = parameter->required();
817 const char* value = js->LookupParam(name);
818 const bool has_parameter = (value != NULL);
819 if (required && !has_parameter) {
820 PrintMissingParamError(js, name);
821 return false;
822 }
823 if (has_parameter && !parameter->Validate(value)) {
824 parameter->PrintError(name, value, js);
825 return false;
826 }
827 }
828 }
829 return true;
830}
831
832void Service::PostError(const String& method_name,
833 const Array& parameter_keys,
834 const Array& parameter_values,
835 const Instance& reply_port,
836 const Instance& id,
837 const Error& error) {
838 Thread* T = Thread::Current();
839 StackZone zone(T);
840 HANDLESCOPE(T);
841 JSONStream js;
842 js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), id, method_name,
843 parameter_keys, parameter_values);
844 js.PrintError(kExtensionError, "Error in extension `%s`: %s", js.method(),
845 error.ToErrorCString());
846 js.PostReply();
847}
848
849ErrorPtr Service::InvokeMethod(Isolate* I,
850 const Array& msg,
851 bool parameters_are_dart_objects) {
852 Thread* T = Thread::Current();
853 ASSERT(I == T->isolate());
854 ASSERT(I != NULL);
855 ASSERT(T->execution_state() == Thread::kThreadInVM);
856 ASSERT(!msg.IsNull());
857 ASSERT(msg.Length() == 6);
858
859 {
860 StackZone zone(T);
861 HANDLESCOPE(T);
862
863 Instance& reply_port = Instance::Handle(Z);
864 Instance& seq = String::Handle(Z);
865 String& method_name = String::Handle(Z);
866 Array& param_keys = Array::Handle(Z);
867 Array& param_values = Array::Handle(Z);
868 reply_port ^= msg.At(1);
869 seq ^= msg.At(2);
870 method_name ^= msg.At(3);
871 param_keys ^= msg.At(4);
872 param_values ^= msg.At(5);
873
874 ASSERT(!method_name.IsNull());
875 ASSERT(seq.IsNull() || seq.IsString() || seq.IsNumber());
876 ASSERT(!param_keys.IsNull());
877 ASSERT(!param_values.IsNull());
878 ASSERT(param_keys.Length() == param_values.Length());
879
880 // We expect a reply port unless there is a null sequence id,
881 // which indicates that no reply should be sent. We use this in
882 // tests.
883 if (!seq.IsNull() && !reply_port.IsSendPort()) {
884 FATAL("SendPort expected.");
885 }
886
887 JSONStream js;
888 Dart_Port reply_port_id =
889 (reply_port.IsNull() ? ILLEGAL_PORT : SendPort::Cast(reply_port).Id());
890 js.Setup(zone.GetZone(), reply_port_id, seq, method_name, param_keys,
891 param_values, parameters_are_dart_objects);
892
893 // RPC came in with a custom service id zone.
894 const char* id_zone_param = js.LookupParam("_idZone");
895
896 if (id_zone_param != NULL) {
897 // Override id zone.
898 if (strcmp("default", id_zone_param) == 0) {
899 // Ring with eager id allocation. This is the default ring and default
900 // policy.
901 // Nothing to do.
902 } else if (strcmp("default.reuse", id_zone_param) == 0) {
903 // Change the default ring's policy.
904 RingServiceIdZone* zone =
905 reinterpret_cast<RingServiceIdZone*>(js.id_zone());
906 zone->set_policy(ObjectIdRing::kReuseId);
907 } else {
908 // TODO(johnmccutchan): Support creating, deleting, and selecting
909 // custom service id zones.
910 // For now, always return an error.
911 PrintInvalidParamError(&js, "_idZone");
912 js.PostReply();
913 return T->StealStickyError();
914 }
915 }
916 const char* c_method_name = method_name.ToCString();
917
918 const ServiceMethodDescriptor* method = FindMethod(c_method_name);
919 if (method != NULL) {
920 if (!ValidateParameters(method->parameters, &js)) {
921 js.PostReply();
922 return T->StealStickyError();
923 }
924 if (method->entry(T, &js)) {
925 js.PostReply();
926 } else {
927 // NOTE(turnidge): All message handlers currently return true,
928 // so this case shouldn't be reached, at present.
929 UNIMPLEMENTED();
930 }
931 return T->StealStickyError();
932 }
933
934 EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(c_method_name);
935 if (handler == NULL) {
936 handler = FindRootEmbedderHandler(c_method_name);
937 }
938
939 if (handler != NULL) {
940 EmbedderHandleMessage(handler, &js);
941 return T->StealStickyError();
942 }
943
944 const Instance& extension_handler =
945 Instance::Handle(Z, I->LookupServiceExtensionHandler(method_name));
946 if (!extension_handler.IsNull()) {
947 ScheduleExtensionHandler(extension_handler, method_name, param_keys,
948 param_values, reply_port, seq);
949 // Schedule was successful. Extension code will post a reply
950 // asynchronously.
951 return T->StealStickyError();
952 }
953
954 PrintUnrecognizedMethodError(&js);
955 js.PostReply();
956 return T->StealStickyError();
957 }
958}
959
960ErrorPtr Service::HandleRootMessage(const Array& msg_instance) {
961 Isolate* isolate = Isolate::Current();
962 return InvokeMethod(isolate, msg_instance);
963}
964
965ErrorPtr Service::HandleObjectRootMessage(const Array& msg_instance) {
966 Isolate* isolate = Isolate::Current();
967 return InvokeMethod(isolate, msg_instance, true);
968}
969
970ErrorPtr Service::HandleIsolateMessage(Isolate* isolate, const Array& msg) {
971 ASSERT(isolate != NULL);
972 const Error& error = Error::Handle(InvokeMethod(isolate, msg));
973 return MaybePause(isolate, error);
974}
975
976static void Finalizer(void* isolate_callback_data,
977 Dart_WeakPersistentHandle handle,
978 void* buffer) {
979 free(buffer);
980}
981
982void Service::SendEvent(const char* stream_id,
983 const char* event_type,
984 uint8_t* bytes,
985 intptr_t bytes_length) {
986 Thread* thread = Thread::Current();
987 Isolate* isolate = thread->isolate();
988 ASSERT(isolate != NULL);
989 ASSERT(FLAG_show_invisible_isolates ||
990 !Isolate::IsVMInternalIsolate(isolate));
991
992 if (FLAG_trace_service) {
993 OS::PrintErr(
994 "vm-service: Pushing ServiceEvent(isolate='%s', "
995 "isolateId='" ISOLATE_SERVICE_ID_FORMAT_STRING
996 "', kind='%s',"
997 " len=%" Pd ") to stream %s\n",
998 isolate->name(), static_cast<int64_t>(isolate->main_port()), event_type,
999 bytes_length, stream_id);
1000 }
1001
1002 bool result;
1003 {
1004 Dart_CObject cbytes;
1005 cbytes.type = Dart_CObject_kExternalTypedData;
1006 cbytes.value.as_external_typed_data.type = Dart_TypedData_kUint8;
1007 cbytes.value.as_external_typed_data.length = bytes_length;
1008 cbytes.value.as_external_typed_data.data = bytes;
1009 cbytes.value.as_external_typed_data.peer = bytes;
1010 cbytes.value.as_external_typed_data.callback = Finalizer;
1011
1012 Dart_CObject cstream_id;
1013 cstream_id.type = Dart_CObject_kString;
1014 cstream_id.value.as_string = const_cast<char*>(stream_id);
1015
1016 Dart_CObject* elements[2];
1017 elements[0] = &cstream_id;
1018 elements[1] = &cbytes;
1019 Dart_CObject message;
1020 message.type = Dart_CObject_kArray;
1021 message.value.as_array.length = 2;
1022 message.value.as_array.values = elements;
1023
1024 ApiMessageWriter writer;
1025 std::unique_ptr<Message> msg = writer.WriteCMessage(
1026 &message, ServiceIsolate::Port(), Message::kNormalPriority);
1027 if (msg == nullptr) {
1028 result = false;
1029 } else {
1030 result = PortMap::PostMessage(std::move(msg));
1031 }
1032 }
1033
1034 if (!result) {
1035 free(bytes);
1036 }
1037}
1038
1039void Service::SendEventWithData(const char* stream_id,
1040 const char* event_type,
1041 intptr_t reservation,
1042 const char* metadata,
1043 intptr_t metadata_size,
1044 uint8_t* data,
1045 intptr_t data_size) {
1046 ASSERT(kInt32Size + metadata_size <= reservation);
1047 // Using a SPACE creates valid JSON. Our goal here is to prevent the memory
1048 // overhead of copying to concatenate metadata and payload together by
1049 // over-allocating to underlying buffer before we know how long the metadata
1050 // will be.
1051 memset(data, ' ', reservation);
1052 reinterpret_cast<uint32_t*>(data)[0] = reservation;
1053 memmove(&(reinterpret_cast<uint32_t*>(data)[1]), metadata, metadata_size);
1054 Service::SendEvent(stream_id, event_type, data, data_size);
1055}
1056
1057static void ReportPauseOnConsole(ServiceEvent* event) {
1058 const char* name = event->isolate()->name();
1059 const int64_t main_port = static_cast<int64_t>(event->isolate()->main_port());
1060 switch (event->kind()) {
1061 case ServiceEvent::kPauseStart:
1062 OS::PrintErr("vm-service: isolate(%" Pd64
1063 ") '%s' has no debugger attached and is paused at start.",
1064 main_port, name);
1065 break;
1066 case ServiceEvent::kPauseExit:
1067 OS::PrintErr("vm-service: isolate(%" Pd64
1068 ") '%s' has no debugger attached and is paused at exit.",
1069 main_port, name);
1070 break;
1071 case ServiceEvent::kPauseException:
1072 OS::PrintErr(
1073 "vm-service: isolate (%" Pd64
1074 ") '%s' has no debugger attached and is paused due to exception.",
1075 main_port, name);
1076 break;
1077 case ServiceEvent::kPauseInterrupted:
1078 OS::PrintErr(
1079 "vm-service: isolate (%" Pd64
1080 ") '%s' has no debugger attached and is paused due to interrupt.",
1081 main_port, name);
1082 break;
1083 case ServiceEvent::kPauseBreakpoint:
1084 OS::PrintErr("vm-service: isolate (%" Pd64
1085 ") '%s' has no debugger attached and is paused.",
1086 main_port, name);
1087 break;
1088 case ServiceEvent::kPausePostRequest:
1089 OS::PrintErr("vm-service: isolate (%" Pd64
1090 ") '%s' has no debugger attached and is paused post reload.",
1091 main_port, name);
1092 break;
1093 default:
1094 UNREACHABLE();
1095 break;
1096 }
1097 if (!ServiceIsolate::IsRunning()) {
1098 OS::PrintErr(" Start the vm-service to debug.\n");
1099 } else if (ServiceIsolate::server_address() == NULL) {
1100 OS::PrintErr(" Connect to Observatory to debug.\n");
1101 } else {
1102 OS::PrintErr(" Connect to Observatory at %s to debug.\n",
1103 ServiceIsolate::server_address());
1104 }
1105 const Error& err = Error::Handle(Thread::Current()->sticky_error());
1106 if (!err.IsNull()) {
1107 OS::PrintErr("%s\n", err.ToErrorCString());
1108 }
1109}
1110
1111void Service::HandleEvent(ServiceEvent* event) {
1112 if (event->stream_info() != NULL && !event->stream_info()->enabled()) {
1113 if (FLAG_warn_on_pause_with_no_debugger && event->IsPause()) {
1114 // If we are about to pause a running program which has no
1115 // debugger connected, tell the user about it.
1116 ReportPauseOnConsole(event);
1117 }
1118 // Ignore events when no one is listening to the event stream.
1119 return;
1120 } else if (event->stream_info() != NULL &&
1121 FLAG_warn_on_pause_with_no_debugger && event->IsPause()) {
1122 ReportPauseOnConsole(event);
1123 }
1124 if (!ServiceIsolate::IsRunning()) {
1125 return;
1126 }
1127 JSONStream js;
1128 const char* stream_id = event->stream_id();
1129 ASSERT(stream_id != NULL);
1130 {
1131 JSONObject jsobj(&js);
1132 jsobj.AddProperty("jsonrpc", "2.0");
1133 jsobj.AddProperty("method", "streamNotify");
1134 JSONObject params(&jsobj, "params");
1135 params.AddProperty("streamId", stream_id);
1136 params.AddProperty("event", event);
1137 }
1138 PostEvent(event->isolate(), stream_id, event->KindAsCString(), &js);
1139
1140 // Post event to the native Service Stream handlers if set.
1141 if (event->stream_info() != nullptr &&
1142 event->stream_info()->consumer() != nullptr) {
1143 auto length = js.buffer()->length();
1144 event->stream_info()->consumer()(
1145 reinterpret_cast<uint8_t*>(js.buffer()->buffer()), length);
1146 }
1147}
1148
1149void Service::PostEvent(Isolate* isolate,
1150 const char* stream_id,
1151 const char* kind,
1152 JSONStream* event) {
1153 ASSERT(stream_id != NULL);
1154 ASSERT(kind != NULL);
1155 ASSERT(event != NULL);
1156
1157 if (FLAG_trace_service) {
1158 if (isolate != NULL) {
1159 OS::PrintErr(
1160 "vm-service: Pushing ServiceEvent(isolate='%s', "
1161 "isolateId='" ISOLATE_SERVICE_ID_FORMAT_STRING
1162 "', kind='%s') to stream %s\n",
1163 isolate->name(), static_cast<int64_t>(isolate->main_port()), kind,
1164 stream_id);
1165 } else {
1166 OS::PrintErr(
1167 "vm-service: Pushing ServiceEvent(isolate='<no current isolate>', "
1168 "kind='%s') to stream %s\n",
1169 kind, stream_id);
1170 }
1171 }
1172
1173 // Message is of the format [<stream id>, <json string>].
1174 //
1175 // Build the event message in the C heap to avoid dart heap
1176 // allocation. This method can be called while we have acquired a
1177 // direct pointer to typed data, so we can't allocate here.
1178 Dart_CObject list_cobj;
1179 Dart_CObject* list_values[2];
1180 list_cobj.type = Dart_CObject_kArray;
1181 list_cobj.value.as_array.length = 2;
1182 list_cobj.value.as_array.values = list_values;
1183
1184 Dart_CObject stream_id_cobj;
1185 stream_id_cobj.type = Dart_CObject_kString;
1186 stream_id_cobj.value.as_string = const_cast<char*>(stream_id);
1187 list_values[0] = &stream_id_cobj;
1188
1189 Dart_CObject json_cobj;
1190 json_cobj.type = Dart_CObject_kString;
1191 json_cobj.value.as_string = const_cast<char*>(event->ToCString());
1192 list_values[1] = &json_cobj;
1193
1194 ApiMessageWriter writer;
1195 std::unique_ptr<Message> msg = writer.WriteCMessage(
1196 &list_cobj, ServiceIsolate::Port(), Message::kNormalPriority);
1197 if (msg != nullptr) {
1198 PortMap::PostMessage(std::move(msg));
1199 }
1200}
1201
1202class EmbedderServiceHandler {
1203 public:
1204 explicit EmbedderServiceHandler(const char* name)
1205 : name_(NULL), callback_(NULL), user_data_(NULL), next_(NULL) {
1206 ASSERT(name != NULL);
1207 name_ = Utils::StrDup(name);
1208 }
1209
1210 ~EmbedderServiceHandler() { free(name_); }
1211
1212 const char* name() const { return name_; }
1213
1214 Dart_ServiceRequestCallback callback() const { return callback_; }
1215 void set_callback(Dart_ServiceRequestCallback callback) {
1216 callback_ = callback;
1217 }
1218
1219 void* user_data() const { return user_data_; }
1220 void set_user_data(void* user_data) { user_data_ = user_data; }
1221
1222 EmbedderServiceHandler* next() const { return next_; }
1223 void set_next(EmbedderServiceHandler* next) { next_ = next; }
1224
1225 private:
1226 char* name_;
1227 Dart_ServiceRequestCallback callback_;
1228 void* user_data_;
1229 EmbedderServiceHandler* next_;
1230};
1231
1232void Service::EmbedderHandleMessage(EmbedderServiceHandler* handler,
1233 JSONStream* js) {
1234 ASSERT(handler != NULL);
1235 Dart_ServiceRequestCallback callback = handler->callback();
1236 ASSERT(callback != NULL);
1237 const char* response = NULL;
1238 bool success;
1239 {
1240 TransitionVMToNative transition(Thread::Current());
1241 success = callback(js->method(), js->param_keys(), js->param_values(),
1242 js->num_params(), handler->user_data(), &response);
1243 }
1244 ASSERT(response != NULL);
1245 if (!success) {
1246 js->SetupError();
1247 }
1248 js->buffer()->AddString(response);
1249 js->PostReply();
1250 free(const_cast<char*>(response));
1251}
1252
1253void Service::RegisterIsolateEmbedderCallback(
1254 const char* name,
1255 Dart_ServiceRequestCallback callback,
1256 void* user_data) {
1257 if (name == NULL) {
1258 return;
1259 }
1260 EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(name);
1261 if (handler != NULL) {
1262 // Update existing handler entry.
1263 handler->set_callback(callback);
1264 handler->set_user_data(user_data);
1265 return;
1266 }
1267 // Create a new handler.
1268 handler = new EmbedderServiceHandler(name);
1269 handler->set_callback(callback);
1270 handler->set_user_data(user_data);
1271
1272 // Insert into isolate_service_handler_head_ list.
1273 handler->set_next(isolate_service_handler_head_);
1274 isolate_service_handler_head_ = handler;
1275}
1276
1277EmbedderServiceHandler* Service::FindIsolateEmbedderHandler(const char* name) {
1278 EmbedderServiceHandler* current = isolate_service_handler_head_;
1279 while (current != NULL) {
1280 if (strcmp(name, current->name()) == 0) {
1281 return current;
1282 }
1283 current = current->next();
1284 }
1285 return NULL;
1286}
1287
1288void Service::RegisterRootEmbedderCallback(const char* name,
1289 Dart_ServiceRequestCallback callback,
1290 void* user_data) {
1291 if (name == NULL) {
1292 return;
1293 }
1294 EmbedderServiceHandler* handler = FindRootEmbedderHandler(name);
1295 if (handler != NULL) {
1296 // Update existing handler entry.
1297 handler->set_callback(callback);
1298 handler->set_user_data(user_data);
1299 return;
1300 }
1301 // Create a new handler.
1302 handler = new EmbedderServiceHandler(name);
1303 handler->set_callback(callback);
1304 handler->set_user_data(user_data);
1305
1306 // Insert into root_service_handler_head_ list.
1307 handler->set_next(root_service_handler_head_);
1308 root_service_handler_head_ = handler;
1309}
1310
1311void Service::SetEmbedderStreamCallbacks(
1312 Dart_ServiceStreamListenCallback listen_callback,
1313 Dart_ServiceStreamCancelCallback cancel_callback) {
1314 stream_listen_callback_ = listen_callback;
1315 stream_cancel_callback_ = cancel_callback;
1316}
1317
1318void Service::SetNativeServiceStreamCallback(Dart_NativeStreamConsumer consumer,
1319 const char* stream_id) {
1320 for (auto stream : streams_) {
1321 if (stream->id() == stream_id) {
1322 stream->set_consumer(consumer);
1323 }
1324 }
1325 // Enable stream.
1326 ListenStream(stream_id);
1327}
1328
1329void Service::SetGetServiceAssetsCallback(
1330 Dart_GetVMServiceAssetsArchive get_service_assets) {
1331 get_service_assets_callback_ = get_service_assets;
1332}
1333
1334void Service::SetEmbedderInformationCallback(
1335 Dart_EmbedderInformationCallback callback) {
1336 embedder_information_callback_ = callback;
1337}
1338
1339int64_t Service::CurrentRSS() {
1340 if (embedder_information_callback_ == NULL) {
1341 return -1;
1342 }
1343 Dart_EmbedderInformation info = {
1344 0, // version
1345 NULL, // name
1346 0, // max_rss
1347 0 // current_rss
1348 };
1349 embedder_information_callback_(&info);
1350 ASSERT(info.version == DART_EMBEDDER_INFORMATION_CURRENT_VERSION);
1351 return info.current_rss;
1352}
1353
1354int64_t Service::MaxRSS() {
1355 if (embedder_information_callback_ == NULL) {
1356 return -1;
1357 }
1358 Dart_EmbedderInformation info = {
1359 0, // version
1360 NULL, // name
1361 0, // max_rss
1362 0 // current_rss
1363 };
1364 embedder_information_callback_(&info);
1365 ASSERT(info.version == DART_EMBEDDER_INFORMATION_CURRENT_VERSION);
1366 return info.max_rss;
1367}
1368
1369void Service::SetDartLibraryKernelForSources(const uint8_t* kernel_bytes,
1370 intptr_t kernel_length) {
1371 dart_library_kernel_ = kernel_bytes;
1372 dart_library_kernel_len_ = kernel_length;
1373}
1374
1375EmbedderServiceHandler* Service::FindRootEmbedderHandler(const char* name) {
1376 EmbedderServiceHandler* current = root_service_handler_head_;
1377 while (current != NULL) {
1378 if (strcmp(name, current->name()) == 0) {
1379 return current;
1380 }
1381 current = current->next();
1382 }
1383 return NULL;
1384}
1385
1386void Service::ScheduleExtensionHandler(const Instance& handler,
1387 const String& method_name,
1388 const Array& parameter_keys,
1389 const Array& parameter_values,
1390 const Instance& reply_port,
1391 const Instance& id) {
1392 ASSERT(!handler.IsNull());
1393 ASSERT(!method_name.IsNull());
1394 ASSERT(!parameter_keys.IsNull());
1395 ASSERT(!parameter_values.IsNull());
1396 ASSERT(!reply_port.IsNull());
1397 Isolate* isolate = Isolate::Current();
1398 ASSERT(isolate != NULL);
1399 isolate->AppendServiceExtensionCall(handler, method_name, parameter_keys,
1400 parameter_values, reply_port, id);
1401}
1402
1403static const MethodParameter* get_isolate_params[] = {
1404 ISOLATE_PARAMETER, NULL,
1405};
1406
1407static bool GetIsolate(Thread* thread, JSONStream* js) {
1408 thread->isolate()->PrintJSON(js, false);
1409 return true;
1410}
1411
1412static const MethodParameter* get_isolate_group_params[] = {
1413 ISOLATE_GROUP_PARAMETER,
1414 NULL,
1415};
1416
1417enum SentinelType {
1418 kCollectedSentinel,
1419 kExpiredSentinel,
1420 kFreeSentinel,
1421};
1422
1423static void PrintSentinel(JSONStream* js, SentinelType sentinel_type) {
1424 JSONObject jsobj(js);
1425 jsobj.AddProperty("type", "Sentinel");
1426 switch (sentinel_type) {
1427 case kCollectedSentinel:
1428 jsobj.AddProperty("kind", "Collected");
1429 jsobj.AddProperty("valueAsString", "<collected>");
1430 break;
1431 case kExpiredSentinel:
1432 jsobj.AddProperty("kind", "Expired");
1433 jsobj.AddProperty("valueAsString", "<expired>");
1434 break;
1435 case kFreeSentinel:
1436 jsobj.AddProperty("kind", "Free");
1437 jsobj.AddProperty("valueAsString", "<free>");
1438 break;
1439 default:
1440 UNIMPLEMENTED();
1441 break;
1442 }
1443}
1444
1445static void ActOnIsolateGroup(JSONStream* js,
1446 std::function<void(IsolateGroup*)> visitor) {
1447 const String& prefix =
1448 String::Handle(String::New(ISOLATE_GROUP_SERVICE_ID_PREFIX));
1449
1450 const String& s =
1451 String::Handle(String::New(js->LookupParam("isolateGroupId")));
1452 if (!s.StartsWith(prefix)) {
1453 PrintInvalidParamError(js, "isolateGroupId");
1454 return;
1455 }
1456 uint64_t isolate_group_id = UInt64Parameter::Parse(
1457 String::Handle(String::SubString(s, prefix.Length())).ToCString());
1458 IsolateGroup::RunWithIsolateGroup(
1459 isolate_group_id,
1460 [&visitor](IsolateGroup* isolate_group) { visitor(isolate_group); },
1461 /*if_not_found=*/[&js]() { PrintSentinel(js, kExpiredSentinel); });
1462}
1463
1464static bool GetIsolateGroup(Thread* thread, JSONStream* js) {
1465 ActOnIsolateGroup(js, [&](IsolateGroup* isolate_group) {
1466 isolate_group->PrintJSON(js, false);
1467 });
1468 return true;
1469}
1470
1471static const MethodParameter* get_memory_usage_params[] = {
1472 ISOLATE_PARAMETER,
1473 NULL,
1474};
1475
1476static bool GetMemoryUsage(Thread* thread, JSONStream* js) {
1477 thread->isolate()->PrintMemoryUsageJSON(js);
1478 return true;
1479}
1480
1481static const MethodParameter* get_isolate_group_memory_usage_params[] = {
1482 ISOLATE_GROUP_PARAMETER,
1483 NULL,
1484};
1485
1486static bool GetIsolateGroupMemoryUsage(Thread* thread, JSONStream* js) {
1487 ActOnIsolateGroup(js, [&](IsolateGroup* isolate_group) {
1488 isolate_group->PrintMemoryUsageJSON(js);
1489 });
1490 return true;
1491}
1492
1493static const MethodParameter* get_scripts_params[] = {
1494 RUNNABLE_ISOLATE_PARAMETER,
1495 NULL,
1496};
1497
1498static bool GetScripts(Thread* thread, JSONStream* js) {
1499 Isolate* isolate = thread->isolate();
1500 Zone* zone = thread->zone();
1501 ASSERT(isolate != NULL);
1502
1503 const GrowableObjectArray& libs =
1504 GrowableObjectArray::Handle(zone, isolate->object_store()->libraries());
1505 intptr_t num_libs = libs.Length();
1506
1507 Library& lib = Library::Handle(zone);
1508 Array& scripts = Array::Handle(zone);
1509 Script& script = Script::Handle(zone);
1510
1511 JSONObject jsobj(js);
1512 {
1513 jsobj.AddProperty("type", "ScriptList");
1514 JSONArray script_array(&jsobj, "scripts");
1515 for (intptr_t i = 0; i < num_libs; i++) {
1516 lib ^= libs.At(i);
1517 ASSERT(!lib.IsNull());
1518 scripts = lib.LoadedScripts();
1519 for (intptr_t j = 0; j < scripts.Length(); j++) {
1520 script ^= scripts.At(j);
1521 ASSERT(!script.IsNull());
1522 script_array.AddValue(script);
1523 }
1524 }
1525 }
1526 return true;
1527}
1528
1529static const MethodParameter* get_stack_params[] = {
1530 RUNNABLE_ISOLATE_PARAMETER,
1531 NULL,
1532};
1533
1534static bool GetStack(Thread* thread, JSONStream* js) {
1535 if (CheckDebuggerDisabled(thread, js)) {
1536 return true;
1537 }
1538
1539 Isolate* isolate = thread->isolate();
1540 DebuggerStackTrace* stack = isolate->debugger()->StackTrace();
1541 DebuggerStackTrace* async_causal_stack =
1542 isolate->debugger()->AsyncCausalStackTrace();
1543 DebuggerStackTrace* awaiter_stack = isolate->debugger()->AwaiterStackTrace();
1544 // Do we want the complete script object and complete local variable objects?
1545 // This is true for dump requests.
1546 JSONObject jsobj(js);
1547 jsobj.AddProperty("type", "Stack");
1548 {
1549 JSONArray jsarr(&jsobj, "frames");
1550
1551 intptr_t num_frames = stack->Length();
1552 for (intptr_t i = 0; i < num_frames; i++) {
1553 ActivationFrame* frame = stack->FrameAt(i);
1554 JSONObject jsobj(&jsarr);
1555 frame->PrintToJSONObject(&jsobj);
1556 jsobj.AddProperty("index", i);
1557 }
1558 }
1559
1560 if (async_causal_stack != NULL) {
1561 JSONArray jsarr(&jsobj, "asyncCausalFrames");
1562 intptr_t num_frames = async_causal_stack->Length();
1563 for (intptr_t i = 0; i < num_frames; i++) {
1564 ActivationFrame* frame = async_causal_stack->FrameAt(i);
1565 JSONObject jsobj(&jsarr);
1566 frame->PrintToJSONObject(&jsobj);
1567 jsobj.AddProperty("index", i);
1568 }
1569 }
1570
1571 if (awaiter_stack != NULL) {
1572 JSONArray jsarr(&jsobj, "awaiterFrames");
1573 intptr_t num_frames = awaiter_stack->Length();
1574 for (intptr_t i = 0; i < num_frames; i++) {
1575 ActivationFrame* frame = awaiter_stack->FrameAt(i);
1576 JSONObject jsobj(&jsarr);
1577 frame->PrintToJSONObject(&jsobj);
1578 jsobj.AddProperty("index", i);
1579 }
1580 }
1581
1582 {
1583 MessageHandler::AcquiredQueues aq(isolate->message_handler());
1584 jsobj.AddProperty("messages", aq.queue());
1585 }
1586
1587 return true;
1588}
1589
1590static bool HandleCommonEcho(JSONObject* jsobj, JSONStream* js) {
1591 jsobj->AddProperty("type", "_EchoResponse");
1592 if (js->HasParam("text")) {
1593 jsobj->AddProperty("text", js->LookupParam("text"));
1594 }
1595 return true;
1596}
1597
1598void Service::SendEchoEvent(Isolate* isolate, const char* text) {
1599 JSONStream js;
1600 {
1601 JSONObject jsobj(&js);
1602 jsobj.AddProperty("jsonrpc", "2.0");
1603 jsobj.AddProperty("method", "streamNotify");
1604 {
1605 JSONObject params(&jsobj, "params");
1606 params.AddProperty("streamId", echo_stream.id());
1607 {
1608 JSONObject event(&params, "event");
1609 event.AddProperty("type", "Event");
1610 event.AddProperty("kind", "_Echo");
1611 event.AddProperty("isolate", isolate);
1612 if (text != NULL) {
1613 event.AddProperty("text", text);
1614 }
1615 event.AddPropertyTimeMillis("timestamp", OS::GetCurrentTimeMillis());
1616 }
1617 }
1618 }
1619
1620 intptr_t reservation = js.buffer()->length() + sizeof(int32_t);
1621 intptr_t data_size = reservation + 3;
1622 uint8_t* data = reinterpret_cast<uint8_t*>(malloc(data_size));
1623 data[reservation + 0] = 0;
1624 data[reservation + 1] = 128;
1625 data[reservation + 2] = 255;
1626 SendEventWithData(echo_stream.id(), "_Echo", reservation,
1627 js.buffer()->buffer(), js.buffer()->length(), data,
1628 data_size);
1629}
1630
1631static bool TriggerEchoEvent(Thread* thread, JSONStream* js) {
1632 if (Service::echo_stream.enabled()) {
1633 Service::SendEchoEvent(thread->isolate(), js->LookupParam("text"));
1634 }
1635 JSONObject jsobj(js);
1636 return HandleCommonEcho(&jsobj, js);
1637}
1638
1639static bool Echo(Thread* thread, JSONStream* js) {
1640 JSONObject jsobj(js);
1641 return HandleCommonEcho(&jsobj, js);
1642}
1643
1644static bool ContainsNonInstance(const Object& obj) {
1645 if (obj.IsArray()) {
1646 const Array& array = Array::Cast(obj);
1647 Object& element = Object::Handle();
1648 for (intptr_t i = 0; i < array.Length(); ++i) {
1649 element = array.At(i);
1650 if (!(element.IsInstance() || element.IsNull())) {
1651 return true;
1652 }
1653 }
1654 return false;
1655 } else if (obj.IsGrowableObjectArray()) {
1656 const GrowableObjectArray& array = GrowableObjectArray::Cast(obj);
1657 Object& element = Object::Handle();
1658 for (intptr_t i = 0; i < array.Length(); ++i) {
1659 element = array.At(i);
1660 if (!(element.IsInstance() || element.IsNull())) {
1661 return true;
1662 }
1663 }
1664 return false;
1665 } else {
1666 return !(obj.IsInstance() || obj.IsNull());
1667 }
1668}
1669
1670static ObjectPtr LookupObjectId(Thread* thread,
1671 const char* arg,
1672 ObjectIdRing::LookupResult* kind) {
1673 *kind = ObjectIdRing::kValid;
1674 if (strncmp(arg, "int-", 4) == 0) {
1675 arg += 4;
1676 int64_t value = 0;
1677 if (!OS::StringToInt64(arg, &value) || !Smi::IsValid(value)) {
1678 *kind = ObjectIdRing::kInvalid;
1679 return Object::null();
1680 }
1681 const Integer& obj =
1682 Integer::Handle(thread->zone(), Smi::New(static_cast<intptr_t>(value)));
1683 return obj.raw();
1684 } else if (strcmp(arg, "bool-true") == 0) {
1685 return Bool::True().raw();
1686 } else if (strcmp(arg, "bool-false") == 0) {
1687 return Bool::False().raw();
1688 } else if (strcmp(arg, "null") == 0) {
1689 return Object::null();
1690 }
1691
1692 ObjectIdRing* ring = thread->isolate()->EnsureObjectIdRing();
1693 intptr_t id = -1;
1694 if (!GetIntegerId(arg, &id)) {
1695 *kind = ObjectIdRing::kInvalid;
1696 return Object::null();
1697 }
1698 return ring->GetObjectForId(id, kind);
1699}
1700
1701static ObjectPtr LookupClassMembers(Thread* thread,
1702 const Class& klass,
1703 char** parts,
1704 int num_parts) {
1705 auto isolate = thread->isolate();
1706 auto zone = thread->zone();
1707
1708 if (num_parts != 4) {
1709 return Object::sentinel().raw();
1710 }
1711
1712 const char* encoded_id = parts[3];
1713 auto& id = String::Handle(String::New(encoded_id));
1714 id = String::DecodeIRI(id);
1715 if (id.IsNull()) {
1716 return Object::sentinel().raw();
1717 }
1718
1719 if (strcmp(parts[2], "fields") == 0) {
1720 // Field ids look like: "classes/17/fields/name"
1721 const auto& field = Field::Handle(klass.LookupField(id));
1722 if (field.IsNull()) {
1723 return Object::sentinel().raw();
1724 }
1725 return field.raw();
1726 }
1727 if (strcmp(parts[2], "functions") == 0) {
1728 // Function ids look like: "classes/17/functions/name"
1729 const auto& function = Function::Handle(klass.LookupFunction(id));
1730 if (function.IsNull()) {
1731 return Object::sentinel().raw();
1732 }
1733 return function.raw();
1734 }
1735 if (strcmp(parts[2], "implicit_closures") == 0) {
1736 // Function ids look like: "classes/17/implicit_closures/11"
1737 intptr_t id;
1738 if (!GetIntegerId(parts[3], &id)) {
1739 return Object::sentinel().raw();
1740 }
1741 const auto& func =
1742 Function::Handle(zone, klass.ImplicitClosureFunctionFromIndex(id));
1743 if (func.IsNull()) {
1744 return Object::sentinel().raw();
1745 }
1746 return func.raw();
1747 }
1748 if (strcmp(parts[2], "dispatchers") == 0) {
1749 // Dispatcher Function ids look like: "classes/17/dispatchers/11"
1750 intptr_t id;
1751 if (!GetIntegerId(parts[3], &id)) {
1752 return Object::sentinel().raw();
1753 }
1754 const auto& func =
1755 Function::Handle(zone, klass.InvocationDispatcherFunctionFromIndex(id));
1756 if (func.IsNull()) {
1757 return Object::sentinel().raw();
1758 }
1759 return func.raw();
1760 }
1761 if (strcmp(parts[2], "closures") == 0) {
1762 // Closure ids look like: "classes/17/closures/11"
1763 intptr_t id;
1764 if (!GetIntegerId(parts[3], &id)) {
1765 return Object::sentinel().raw();
1766 }
1767 Function& func = Function::Handle(zone);
1768 func = isolate->ClosureFunctionFromIndex(id);
1769 if (func.IsNull()) {
1770 return Object::sentinel().raw();
1771 }
1772 return func.raw();
1773 }
1774
1775 UNREACHABLE();
1776 return Object::sentinel().raw();
1777}
1778
1779static ObjectPtr LookupHeapObjectLibraries(Isolate* isolate,
1780 char** parts,
1781 int num_parts) {
1782 // Library ids look like "libraries/35"
1783 if (num_parts < 2) {
1784 return Object::sentinel().raw();
1785 }
1786 const GrowableObjectArray& libs =
1787 GrowableObjectArray::Handle(isolate->object_store()->libraries());
1788 ASSERT(!libs.IsNull());
1789 const String& id = String::Handle(String::New(parts[1]));
1790 // Scan for private key.
1791 String& private_key = String::Handle();
1792 Library& lib = Library::Handle();
1793 bool lib_found = false;
1794 for (intptr_t i = 0; i < libs.Length(); i++) {
1795 lib ^= libs.At(i);
1796 ASSERT(!lib.IsNull());
1797 private_key = lib.private_key();
1798 if (private_key.Equals(id)) {
1799 lib_found = true;
1800 break;
1801 }
1802 }
1803 if (!lib_found) {
1804 return Object::sentinel().raw();
1805 }
1806
1807 const auto& klass = Class::Handle(lib.toplevel_class());
1808 ASSERT(!klass.IsNull());
1809
1810 if (num_parts == 2) {
1811 return lib.raw();
1812 }
1813 if (strcmp(parts[2], "fields") == 0) {
1814 // Library field ids look like: "libraries/17/fields/name"
1815 return LookupClassMembers(Thread::Current(), klass, parts, num_parts);
1816 }
1817 if (strcmp(parts[2], "functions") == 0) {
1818 // Library function ids look like: "libraries/17/functions/name"
1819 return LookupClassMembers(Thread::Current(), klass, parts, num_parts);
1820 }
1821 if (strcmp(parts[2], "closures") == 0) {
1822 // Library function ids look like: "libraries/17/closures/name"
1823 return LookupClassMembers(Thread::Current(), klass, parts, num_parts);
1824 }
1825 if (strcmp(parts[2], "implicit_closures") == 0) {
1826 // Library function ids look like: "libraries/17/implicit_closures/name"
1827 return LookupClassMembers(Thread::Current(), klass, parts, num_parts);
1828 }
1829
1830 if (strcmp(parts[2], "scripts") == 0) {
1831 // Script ids look like "libraries/35/scripts/library%2Furl.dart/12345"
1832 if (num_parts != 5) {
1833 return Object::sentinel().raw();
1834 }
1835 const String& id = String::Handle(String::New(parts[3]));
1836 ASSERT(!id.IsNull());
1837 // The id is the url of the script % encoded, decode it.
1838 const String& requested_url = String::Handle(String::DecodeIRI(id));
1839
1840 // Each script id is tagged with a load time.
1841 int64_t timestamp;
1842 if (!GetInteger64Id(parts[4], &timestamp, 16) || (timestamp < 0)) {
1843 return Object::sentinel().raw();
1844 }
1845
1846 Script& script = Script::Handle();
1847 String& script_url = String::Handle();
1848 const Array& loaded_scripts = Array::Handle(lib.LoadedScripts());
1849 ASSERT(!loaded_scripts.IsNull());
1850 intptr_t i;
1851 for (i = 0; i < loaded_scripts.Length(); i++) {
1852 script ^= loaded_scripts.At(i);
1853 ASSERT(!script.IsNull());
1854 script_url = script.url();
1855 if (script_url.Equals(requested_url) &&
1856 (timestamp == script.load_timestamp())) {
1857 return script.raw();
1858 }
1859 }
1860 }
1861
1862 // Not found.
1863 return Object::sentinel().raw();
1864}
1865
1866static ObjectPtr LookupHeapObjectClasses(Thread* thread,
1867 char** parts,
1868 int num_parts) {
1869 // Class ids look like: "classes/17"
1870 if (num_parts < 2) {
1871 return Object::sentinel().raw();
1872 }
1873 Isolate* isolate = thread->isolate();
1874 Zone* zone = thread->zone();
1875 ClassTable* table = isolate->class_table();
1876 intptr_t id;
1877 if (!GetIntegerId(parts[1], &id) || !table->IsValidIndex(id)) {
1878 return Object::sentinel().raw();
1879 }
1880 Class& cls = Class::Handle(zone, table->At(id));
1881 if (num_parts == 2) {
1882 return cls.raw();
1883 }
1884 if (strcmp(parts[2], "closures") == 0) {
1885 // Closure ids look like: "classes/17/closures/11"
1886 return LookupClassMembers(thread, cls, parts, num_parts);
1887 } else if (strcmp(parts[2], "fields") == 0) {
1888 // Field ids look like: "classes/17/fields/name"
1889 return LookupClassMembers(thread, cls, parts, num_parts);
1890 } else if (strcmp(parts[2], "functions") == 0) {
1891 // Function ids look like: "classes/17/functions/name"
1892 return LookupClassMembers(thread, cls, parts, num_parts);
1893 } else if (strcmp(parts[2], "implicit_closures") == 0) {
1894 // Function ids look like: "classes/17/implicit_closures/11"
1895 return LookupClassMembers(thread, cls, parts, num_parts);
1896 } else if (strcmp(parts[2], "dispatchers") == 0) {
1897 // Dispatcher Function ids look like: "classes/17/dispatchers/11"
1898 return LookupClassMembers(thread, cls, parts, num_parts);
1899 } else if (strcmp(parts[2], "types") == 0) {
1900 // Type ids look like: "classes/17/types/11"
1901 if (num_parts != 4) {
1902 return Object::sentinel().raw();
1903 }
1904 intptr_t id;
1905 if (!GetIntegerId(parts[3], &id)) {
1906 return Object::sentinel().raw();
1907 }
1908 if (id != 0) {
1909 return Object::sentinel().raw();
1910 }
1911 const Type& type = Type::Handle(zone, cls.DeclarationType());
1912 if (!type.IsNull()) {
1913 return type.raw();
1914 }
1915 }
1916
1917 // Not found.
1918 return Object::sentinel().raw();
1919}
1920
1921static ObjectPtr LookupHeapObjectTypeArguments(Thread* thread,
1922 char** parts,
1923 int num_parts) {
1924 Isolate* isolate = thread->isolate();
1925 // TypeArguments ids look like: "typearguments/17"
1926 if (num_parts < 2) {
1927 return Object::sentinel().raw();
1928 }
1929 intptr_t id;
1930 if (!GetIntegerId(parts[1], &id)) {
1931 return Object::sentinel().raw();
1932 }
1933 ObjectStore* object_store = isolate->object_store();
1934 const Array& table =
1935 Array::Handle(thread->zone(), object_store->canonical_type_arguments());
1936 ASSERT(table.Length() > 0);
1937 const intptr_t table_size = table.Length() - 1;
1938 if ((id < 0) || (id >= table_size) || (table.At(id) == Object::null())) {
1939 return Object::sentinel().raw();
1940 }
1941 return table.At(id);
1942}
1943
1944static ObjectPtr LookupHeapObjectCode(Isolate* isolate,
1945 char** parts,
1946 int num_parts) {
1947 if (num_parts != 2) {
1948 return Object::sentinel().raw();
1949 }
1950 uword pc;
1951 static const char* const kCollectedPrefix = "collected-";
1952 static intptr_t kCollectedPrefixLen = strlen(kCollectedPrefix);
1953 static const char* const kNativePrefix = "native-";
1954 static const intptr_t kNativePrefixLen = strlen(kNativePrefix);
1955 static const char* const kReusedPrefix = "reused-";
1956 static const intptr_t kReusedPrefixLen = strlen(kReusedPrefix);
1957 const char* id = parts[1];
1958 if (strncmp(kCollectedPrefix, id, kCollectedPrefixLen) == 0) {
1959 if (!GetUnsignedIntegerId(&id[kCollectedPrefixLen], &pc, 16)) {
1960 return Object::sentinel().raw();
1961 }
1962 // TODO(turnidge): Return "collected" instead.
1963 return Object::null();
1964 }
1965 if (strncmp(kNativePrefix, id, kNativePrefixLen) == 0) {
1966 if (!GetUnsignedIntegerId(&id[kNativePrefixLen], &pc, 16)) {
1967 return Object::sentinel().raw();
1968 }
1969 // TODO(johnmccutchan): Support native Code.
1970 return Object::null();
1971 }
1972 if (strncmp(kReusedPrefix, id, kReusedPrefixLen) == 0) {
1973 if (!GetUnsignedIntegerId(&id[kReusedPrefixLen], &pc, 16)) {
1974 return Object::sentinel().raw();
1975 }
1976 // TODO(turnidge): Return "expired" instead.
1977 return Object::null();
1978 }
1979 int64_t timestamp = 0;
1980 if (!GetCodeId(id, &timestamp, &pc) || (timestamp < 0)) {
1981 return Object::sentinel().raw();
1982 }
1983 Code& code = Code::Handle(Code::FindCode(pc, timestamp));
1984 if (!code.IsNull()) {
1985 return code.raw();
1986 }
1987 Bytecode& bytecode = Bytecode::Handle(Bytecode::FindCode(pc));
1988 if (!bytecode.IsNull()) {
1989 return bytecode.raw();
1990 }
1991
1992 // Not found.
1993 return Object::sentinel().raw();
1994}
1995
1996static ObjectPtr LookupHeapObjectMessage(Thread* thread,
1997 char** parts,
1998 int num_parts) {
1999 if (num_parts != 2) {
2000 return Object::sentinel().raw();
2001 }
2002 uword message_id = 0;
2003 if (!GetUnsignedIntegerId(parts[1], &message_id, 16)) {
2004 return Object::sentinel().raw();
2005 }
2006 MessageHandler::AcquiredQueues aq(thread->isolate()->message_handler());
2007 Message* message = aq.queue()->FindMessageById(message_id);
2008 if (message == NULL) {
2009 // The user may try to load an expired message.
2010 return Object::sentinel().raw();
2011 }
2012 if (message->IsRaw()) {
2013 return message->raw_obj();
2014 } else {
2015 MessageSnapshotReader reader(message, thread);
2016 return reader.ReadObject();
2017 }
2018}
2019
2020static ObjectPtr LookupHeapObject(Thread* thread,
2021 const char* id_original,
2022 ObjectIdRing::LookupResult* result) {
2023 char* id = thread->zone()->MakeCopyOfString(id_original);
2024
2025 // Parse the id by splitting at each '/'.
2026 const int MAX_PARTS = 8;
2027 char* parts[MAX_PARTS];
2028 int num_parts = 0;
2029 int i = 0;
2030 int start_pos = 0;
2031 while (id[i] != '\0') {
2032 if (id[i] == '/') {
2033 id[i++] = '\0';
2034 parts[num_parts++] = &id[start_pos];
2035 if (num_parts == MAX_PARTS) {
2036 break;
2037 }
2038 start_pos = i;
2039 } else {
2040 i++;
2041 }
2042 }
2043 if (num_parts < MAX_PARTS) {
2044 parts[num_parts++] = &id[start_pos];
2045 }
2046
2047 if (result != NULL) {
2048 *result = ObjectIdRing::kValid;
2049 }
2050
2051 Isolate* isolate = thread->isolate();
2052 if (strcmp(parts[0], "objects") == 0) {
2053 // Object ids look like "objects/1123"
2054 Object& obj = Object::Handle(thread->zone());
2055 ObjectIdRing::LookupResult lookup_result;
2056 obj = LookupObjectId(thread, parts[1], &lookup_result);
2057 if (lookup_result != ObjectIdRing::kValid) {
2058 if (result != NULL) {
2059 *result = lookup_result;
2060 }
2061 return Object::sentinel().raw();
2062 }
2063 return obj.raw();
2064
2065 } else if (strcmp(parts[0], "libraries") == 0) {
2066 return LookupHeapObjectLibraries(isolate, parts, num_parts);
2067 } else if (strcmp(parts[0], "classes") == 0) {
2068 return LookupHeapObjectClasses(thread, parts, num_parts);
2069 } else if (strcmp(parts[0], "typearguments") == 0) {
2070 return LookupHeapObjectTypeArguments(thread, parts, num_parts);
2071 } else if (strcmp(parts[0], "code") == 0) {
2072 return LookupHeapObjectCode(isolate, parts, num_parts);
2073 } else if (strcmp(parts[0], "messages") == 0) {
2074 return LookupHeapObjectMessage(thread, parts, num_parts);
2075 }
2076
2077 // Not found.
2078 return Object::sentinel().raw();
2079}
2080
2081static Breakpoint* LookupBreakpoint(Isolate* isolate,
2082 const char* id,
2083 ObjectIdRing::LookupResult* result) {
2084 *result = ObjectIdRing::kInvalid;
2085 size_t end_pos = strcspn(id, "/");
2086 if (end_pos == strlen(id)) {
2087 return NULL;
2088 }
2089 const char* rest = id + end_pos + 1; // +1 for '/'.
2090 if (strncmp("breakpoints", id, end_pos) == 0) {
2091 intptr_t bpt_id = 0;
2092 Breakpoint* bpt = NULL;
2093 if (GetIntegerId(rest, &bpt_id)) {
2094 bpt = isolate->debugger()->GetBreakpointById(bpt_id);
2095 if (bpt != nullptr) {
2096 *result = ObjectIdRing::kValid;
2097 return bpt;
2098 }
2099 if (bpt_id < isolate->debugger()->limitBreakpointId()) {
2100 *result = ObjectIdRing::kCollected;
2101 return NULL;
2102 }
2103 }
2104 }
2105 return NULL;
2106}
2107
2108static bool PrintInboundReferences(Thread* thread,
2109 Object* target,
2110 intptr_t limit,
2111 JSONStream* js) {
2112 ObjectGraph graph(thread);
2113 Array& path = Array::Handle(Array::New(limit * 2));
2114 intptr_t length = graph.InboundReferences(target, path);
2115 JSONObject jsobj(js);
2116 jsobj.AddProperty("type", "InboundReferences");
2117 {
2118 JSONArray elements(&jsobj, "references");
2119 Object& source = Object::Handle();
2120 Smi& slot_offset = Smi::Handle();
2121 Class& source_class = Class::Handle();
2122 Field& field = Field::Handle();
2123 Array& parent_field_map = Array::Handle();
2124 limit = Utils::Minimum(limit, length);
2125 for (intptr_t i = 0; i < limit; ++i) {
2126 JSONObject jselement(&elements);
2127 source = path.At(i * 2);
2128 slot_offset ^= path.At((i * 2) + 1);
2129
2130 jselement.AddProperty("source", source);
2131 if (source.IsArray()) {
2132 intptr_t element_index =
2133 slot_offset.Value() - (Array::element_offset(0) >> kWordSizeLog2);
2134 jselement.AddProperty("parentListIndex", element_index);
2135 } else if (source.IsInstance()) {
2136 source_class = source.clazz();
2137 parent_field_map = source_class.OffsetToFieldMap();
2138 intptr_t offset = slot_offset.Value();
2139 if (offset > 0 && offset < parent_field_map.Length()) {
2140 field ^= parent_field_map.At(offset);
2141 jselement.AddProperty("parentField", field);
2142 }
2143 } else {
2144 intptr_t element_index = slot_offset.Value();
2145 jselement.AddProperty("_parentWordOffset", element_index);
2146 }
2147 }
2148 }
2149
2150 // We nil out the array after generating the response to prevent
2151 // reporting suprious references when repeatedly looking for the
2152 // references to an object.
2153 for (intptr_t i = 0; i < path.Length(); i++) {
2154 path.SetAt(i, Object::null_object());
2155 }
2156
2157 return true;
2158}
2159
2160static const MethodParameter* get_inbound_references_params[] = {
2161 RUNNABLE_ISOLATE_PARAMETER, NULL,
2162};
2163
2164static bool GetInboundReferences(Thread* thread, JSONStream* js) {
2165 const char* target_id = js->LookupParam("targetId");
2166 if (target_id == NULL) {
2167 PrintMissingParamError(js, "targetId");
2168 return true;
2169 }
2170 const char* limit_cstr = js->LookupParam("limit");
2171 if (limit_cstr == NULL) {
2172 PrintMissingParamError(js, "limit");
2173 return true;
2174 }
2175 intptr_t limit;
2176 if (!GetIntegerId(limit_cstr, &limit)) {
2177 PrintInvalidParamError(js, "limit");
2178 return true;
2179 }
2180
2181 Object& obj = Object::Handle(thread->zone());
2182 ObjectIdRing::LookupResult lookup_result;
2183 {
2184 HANDLESCOPE(thread);
2185 obj = LookupHeapObject(thread, target_id, &lookup_result);
2186 }
2187 if (obj.raw() == Object::sentinel().raw()) {
2188 if (lookup_result == ObjectIdRing::kCollected) {
2189 PrintSentinel(js, kCollectedSentinel);
2190 } else if (lookup_result == ObjectIdRing::kExpired) {
2191 PrintSentinel(js, kExpiredSentinel);
2192 } else {
2193 PrintInvalidParamError(js, "targetId");
2194 }
2195 return true;
2196 }
2197 return PrintInboundReferences(thread, &obj, limit, js);
2198}
2199
2200static bool PrintRetainingPath(Thread* thread,
2201 Object* obj,
2202 intptr_t limit,
2203 JSONStream* js) {
2204 ObjectGraph graph(thread);
2205 Array& path = Array::Handle(Array::New(limit * 2));
2206 auto result = graph.RetainingPath(obj, path);
2207 intptr_t length = result.length;
2208 JSONObject jsobj(js);
2209 jsobj.AddProperty("type", "RetainingPath");
2210 jsobj.AddProperty("length", length);
2211 jsobj.AddProperty("gcRootType", result.gc_root_type);
2212 JSONArray elements(&jsobj, "elements");
2213 Object& element = Object::Handle();
2214 Smi& slot_offset = Smi::Handle();
2215 Class& element_class = Class::Handle();
2216 Array& element_field_map = Array::Handle();
2217 LinkedHashMap& map = LinkedHashMap::Handle();
2218 Array& map_data = Array::Handle();
2219 Field& field = Field::Handle();
2220 String& name = String::Handle();
2221 limit = Utils::Minimum(limit, length);
2222 for (intptr_t i = 0; i < limit; ++i) {
2223 JSONObject jselement(&elements);
2224 element = path.At(i * 2);
2225 jselement.AddProperty("value", element);
2226 // Interpret the word offset from parent as list index, map key
2227 // or instance field.
2228 if (i > 0) {
2229 slot_offset ^= path.At((i * 2) - 1);
2230 if (element.IsArray() || element.IsGrowableObjectArray()) {
2231 intptr_t element_index =
2232 slot_offset.Value() - (Array::element_offset(0) >> kWordSizeLog2);
2233 jselement.AddProperty("parentListIndex", element_index);
2234 } else if (element.IsLinkedHashMap()) {
2235 map = static_cast<LinkedHashMapPtr>(path.At(i * 2));
2236 map_data = map.data();
2237 intptr_t element_index =
2238 slot_offset.Value() - (Array::element_offset(0) >> kWordSizeLog2);
2239 LinkedHashMap::Iterator iterator(map);
2240 while (iterator.MoveNext()) {
2241 if (iterator.CurrentKey() == map_data.At(element_index) ||
2242 iterator.CurrentValue() == map_data.At(element_index)) {
2243 element = iterator.CurrentKey();
2244 jselement.AddProperty("parentMapKey", element);
2245 break;
2246 }
2247 }
2248 } else if (element.IsInstance()) {
2249 element_class = element.clazz();
2250 element_field_map = element_class.OffsetToFieldMap();
2251 intptr_t offset = slot_offset.Value();
2252 if (offset > 0 && offset < element_field_map.Length()) {
2253 field ^= element_field_map.At(offset);
2254 ASSERT(!field.IsNull());
2255 // TODO(bkonyi): check for mapping between C++ name and Dart name (V8
2256 // snapshot writer?)
2257 name ^= field.name();
2258 jselement.AddProperty("parentField", name.ToCString());
2259 }
2260 } else {
2261 intptr_t element_index = slot_offset.Value();
2262 jselement.AddProperty("_parentWordOffset", element_index);
2263 }
2264 }
2265 }
2266
2267 // We nil out the array after generating the response to prevent
2268 // reporting spurious references when looking for inbound references
2269 // after looking for a retaining path.
2270 for (intptr_t i = 0; i < path.Length(); i++) {
2271 path.SetAt(i, Object::null_object());
2272 }
2273
2274 return true;
2275}
2276
2277static const MethodParameter* get_retaining_path_params[] = {
2278 RUNNABLE_ISOLATE_PARAMETER, NULL,
2279};
2280
2281static bool GetRetainingPath(Thread* thread, JSONStream* js) {
2282 const char* target_id = js->LookupParam("targetId");
2283 if (target_id == NULL) {
2284 PrintMissingParamError(js, "targetId");
2285 return true;
2286 }
2287 const char* limit_cstr = js->LookupParam("limit");
2288 if (limit_cstr == NULL) {
2289 PrintMissingParamError(js, "limit");
2290 return true;
2291 }
2292 intptr_t limit;
2293 if (!GetIntegerId(limit_cstr, &limit)) {
2294 PrintInvalidParamError(js, "limit");
2295 return true;
2296 }
2297
2298 Object& obj = Object::Handle(thread->zone());
2299 ObjectIdRing::LookupResult lookup_result;
2300 {
2301 HANDLESCOPE(thread);
2302 obj = LookupHeapObject(thread, target_id, &lookup_result);
2303 }
2304 if (obj.raw() == Object::sentinel().raw()) {
2305 if (lookup_result == ObjectIdRing::kCollected) {
2306 PrintSentinel(js, kCollectedSentinel);
2307 } else if (lookup_result == ObjectIdRing::kExpired) {
2308 PrintSentinel(js, kExpiredSentinel);
2309 } else {
2310 PrintInvalidParamError(js, "targetId");
2311 }
2312 return true;
2313 }
2314 return PrintRetainingPath(thread, &obj, limit, js);
2315}
2316
2317static const MethodParameter* get_retained_size_params[] = {
2318 RUNNABLE_ISOLATE_PARAMETER, new IdParameter("targetId", true), NULL,
2319};
2320
2321static bool GetRetainedSize(Thread* thread, JSONStream* js) {
2322 const char* target_id = js->LookupParam("targetId");
2323 ASSERT(target_id != NULL);
2324 ObjectIdRing::LookupResult lookup_result;
2325 Object& obj =
2326 Object::Handle(LookupHeapObject(thread, target_id, &lookup_result));
2327 if (obj.raw() == Object::sentinel().raw()) {
2328 if (lookup_result == ObjectIdRing::kCollected) {
2329 PrintSentinel(js, kCollectedSentinel);
2330 } else if (lookup_result == ObjectIdRing::kExpired) {
2331 PrintSentinel(js, kExpiredSentinel);
2332 } else {
2333 PrintInvalidParamError(js, "targetId");
2334 }
2335 return true;
2336 }
2337 // TODO(rmacnak): There is no way to get the size retained by a class object.
2338 // SizeRetainedByClass should be a separate RPC.
2339 if (obj.IsClass()) {
2340 const Class& cls = Class::Cast(obj);
2341 ObjectGraph graph(thread);
2342 intptr_t retained_size = graph.SizeRetainedByClass(cls.id());
2343 const Object& result = Object::Handle(Integer::New(retained_size));
2344 result.PrintJSON(js, true);
2345 return true;
2346 }
2347
2348 ObjectGraph graph(thread);
2349 intptr_t retained_size = graph.SizeRetainedByInstance(obj);
2350 const Object& result = Object::Handle(Integer::New(retained_size));
2351 result.PrintJSON(js, true);
2352 return true;
2353}
2354
2355static const MethodParameter* get_reachable_size_params[] = {
2356 RUNNABLE_ISOLATE_PARAMETER, new IdParameter("targetId", true), NULL,
2357};
2358
2359static bool GetReachableSize(Thread* thread, JSONStream* js) {
2360 const char* target_id = js->LookupParam("targetId");
2361 ASSERT(target_id != NULL);
2362 ObjectIdRing::LookupResult lookup_result;
2363 Object& obj =
2364 Object::Handle(LookupHeapObject(thread, target_id, &lookup_result));
2365 if (obj.raw() == Object::sentinel().raw()) {
2366 if (lookup_result == ObjectIdRing::kCollected) {
2367 PrintSentinel(js, kCollectedSentinel);
2368 } else if (lookup_result == ObjectIdRing::kExpired) {
2369 PrintSentinel(js, kExpiredSentinel);
2370 } else {
2371 PrintInvalidParamError(js, "targetId");
2372 }
2373 return true;
2374 }
2375 // TODO(rmacnak): There is no way to get the size retained by a class object.
2376 // SizeRetainedByClass should be a separate RPC.
2377 if (obj.IsClass()) {
2378 const Class& cls = Class::Cast(obj);
2379 ObjectGraph graph(thread);
2380 intptr_t retained_size = graph.SizeReachableByClass(cls.id());
2381 const Object& result = Object::Handle(Integer::New(retained_size));
2382 result.PrintJSON(js, true);
2383 return true;
2384 }
2385
2386 ObjectGraph graph(thread);
2387 intptr_t retained_size = graph.SizeReachableByInstance(obj);
2388 const Object& result = Object::Handle(Integer::New(retained_size));
2389 result.PrintJSON(js, true);
2390 return true;
2391}
2392
2393static const MethodParameter* invoke_params[] = {
2394 RUNNABLE_ISOLATE_PARAMETER,
2395 NULL,
2396};
2397
2398static bool Invoke(Thread* thread, JSONStream* js) {
2399 const char* receiver_id = js->LookupParam("targetId");
2400 if (receiver_id == NULL) {
2401 PrintMissingParamError(js, "targetId");
2402 return true;
2403 }
2404 const char* selector_cstr = js->LookupParam("selector");
2405 if (selector_cstr == NULL) {
2406 PrintMissingParamError(js, "selector");
2407 return true;
2408 }
2409 const char* argument_ids = js->LookupParam("argumentIds");
2410 if (argument_ids == NULL) {
2411 PrintMissingParamError(js, "argumentIds");
2412 return true;
2413 }
2414
2415#if !defined(DART_PRECOMPILED_RUNTIME)
2416 bool disable_breakpoints =
2417 BoolParameter::Parse(js->LookupParam("disableBreakpoints"), false);
2418 DisableBreakpointsScope db(thread->isolate()->debugger(),
2419 disable_breakpoints);
2420#endif
2421
2422 Zone* zone = thread->zone();
2423 ObjectIdRing::LookupResult lookup_result;
2424 Object& receiver = Object::Handle(
2425 zone, LookupHeapObject(thread, receiver_id, &lookup_result));
2426 if (receiver.raw() == Object::sentinel().raw()) {
2427 if (lookup_result == ObjectIdRing::kCollected) {
2428 PrintSentinel(js, kCollectedSentinel);
2429 } else if (lookup_result == ObjectIdRing::kExpired) {
2430 PrintSentinel(js, kExpiredSentinel);
2431 } else {
2432 PrintInvalidParamError(js, "targetId");
2433 }
2434 return true;
2435 }
2436
2437 const GrowableObjectArray& growable_args =
2438 GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
2439
2440 bool is_instance = (receiver.IsInstance() || receiver.IsNull()) &&
2441 !ContainsNonInstance(receiver);
2442 if (is_instance) {
2443 growable_args.Add(receiver);
2444 }
2445
2446 intptr_t n = strlen(argument_ids);
2447 if ((n < 2) || (argument_ids[0] != '[') || (argument_ids[n - 1] != ']')) {
2448 PrintInvalidParamError(js, "argumentIds");
2449 return true;
2450 }
2451 if (n > 2) {
2452 intptr_t start = 1;
2453 while (start < n) {
2454 intptr_t end = start;
2455 while ((argument_ids[end + 1] != ',') && (argument_ids[end + 1] != ']')) {
2456 end++;
2457 }
2458 if (end == start) {
2459 // Empty element.
2460 PrintInvalidParamError(js, "argumentIds");
2461 return true;
2462 }
2463
2464 const char* argument_id =
2465 zone->MakeCopyOfStringN(&argument_ids[start], end - start + 1);
2466
2467 ObjectIdRing::LookupResult lookup_result;
2468 Object& argument = Object::Handle(
2469 zone, LookupHeapObject(thread, argument_id, &lookup_result));
2470 // Invoke only accepts Instance arguments.
2471 if (!(argument.IsInstance() || argument.IsNull()) ||
2472 ContainsNonInstance(argument)) {
2473 PrintInvalidParamError(js, "argumentIds");
2474 return true;
2475 }
2476 if (argument.raw() == Object::sentinel().raw()) {
2477 if (lookup_result == ObjectIdRing::kCollected) {
2478 PrintSentinel(js, kCollectedSentinel);
2479 } else if (lookup_result == ObjectIdRing::kExpired) {
2480 PrintSentinel(js, kExpiredSentinel);
2481 } else {
2482 PrintInvalidParamError(js, "argumentIds");
2483 }
2484 return true;
2485 }
2486 growable_args.Add(argument);
2487
2488 start = end + 3;
2489 }
2490 }
2491
2492 const String& selector = String::Handle(zone, String::New(selector_cstr));
2493 const Array& args =
2494 Array::Handle(zone, Array::MakeFixedLength(growable_args));
2495 const Array& arg_names = Object::empty_array();
2496
2497 if (receiver.IsLibrary()) {
2498 const Library& lib = Library::Cast(receiver);
2499 const Object& result =
2500 Object::Handle(zone, lib.Invoke(selector, args, arg_names));
2501 result.PrintJSON(js, true);
2502 return true;
2503 }
2504 if (receiver.IsClass()) {
2505 const Class& cls = Class::Cast(receiver);
2506 const Object& result =
2507 Object::Handle(zone, cls.Invoke(selector, args, arg_names));
2508 result.PrintJSON(js, true);
2509 return true;
2510 }
2511 if (is_instance) {
2512 // We don't use Instance::Cast here because it doesn't allow null.
2513 Instance& instance = Instance::Handle(zone);
2514 instance ^= receiver.raw();
2515 const Object& result =
2516 Object::Handle(zone, instance.Invoke(selector, args, arg_names));
2517 result.PrintJSON(js, true);
2518 return true;
2519 }
2520 js->PrintError(kInvalidParams,
2521 "%s: invalid 'targetId' parameter: "
2522 "Cannot invoke against a VM-internal object",
2523 js->method());
2524 return true;
2525}
2526
2527static const MethodParameter* evaluate_params[] = {
2528 RUNNABLE_ISOLATE_PARAMETER, NULL,
2529};
2530
2531static bool IsAlpha(char c) {
2532 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
2533}
2534static bool IsAlphaOrDollar(char c) {
2535 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '$');
2536}
2537static bool IsAlphaNum(char c) {
2538 return (c >= '0' && c <= '9') || IsAlpha(c);
2539}
2540static bool IsAlphaNumOrDollar(char c) {
2541 return (c >= '0' && c <= '9') || IsAlphaOrDollar(c);
2542}
2543static bool IsWhitespace(char c) {
2544 return c <= ' ';
2545}
2546static bool IsObjectIdChar(char c) {
2547 return IsAlphaNum(c) || c == '/' || c == '-' || c == '@' || c == '%';
2548}
2549
2550// TODO(vm-service): Consider whether we should pass structured objects in
2551// service messages instead of always flattening them to C strings.
2552static bool ParseScope(const char* scope,
2553 GrowableArray<const char*>* names,
2554 GrowableArray<const char*>* ids) {
2555 Zone* zone = Thread::Current()->zone();
2556 const char* c = scope;
2557 if (*c++ != '{') return false;
2558
2559 for (;;) {
2560 while (IsWhitespace(*c)) {
2561 c++;
2562 }
2563
2564 if (*c == '}') return true;
2565
2566 const char* name = c;
2567 if (!IsAlphaOrDollar(*c)) return false;
2568 while (IsAlphaNumOrDollar(*c)) {
2569 c++;
2570 }
2571 names->Add(zone->MakeCopyOfStringN(name, c - name));
2572
2573 while (IsWhitespace(*c)) {
2574 c++;
2575 }
2576
2577 if (*c++ != ':') return false;
2578
2579 while (IsWhitespace(*c)) {
2580 c++;
2581 }
2582
2583 const char* id = c;
2584 if (!IsObjectIdChar(*c)) return false;
2585 while (IsObjectIdChar(*c)) {
2586 c++;
2587 }
2588 ids->Add(zone->MakeCopyOfStringN(id, c - id));
2589
2590 while (IsWhitespace(*c)) {
2591 c++;
2592 }
2593 if (*c == ',') c++;
2594 }
2595
2596 return false;
2597}
2598
2599static bool BuildScope(Thread* thread,
2600 JSONStream* js,
2601 const GrowableObjectArray& names,
2602 const GrowableObjectArray& values) {
2603 const char* scope = js->LookupParam("scope");
2604 GrowableArray<const char*> cnames;
2605 GrowableArray<const char*> cids;
2606 if (scope != NULL) {
2607 if (!ParseScope(scope, &cnames, &cids)) {
2608 PrintInvalidParamError(js, "scope");
2609 return true;
2610 }
2611 String& name = String::Handle();
2612 Object& obj = Object::Handle();
2613 for (intptr_t i = 0; i < cids.length(); i++) {
2614 ObjectIdRing::LookupResult lookup_result;
2615 obj = LookupHeapObject(thread, cids[i], &lookup_result);
2616 if (obj.raw() == Object::sentinel().raw()) {
2617 if (lookup_result == ObjectIdRing::kCollected) {
2618 PrintSentinel(js, kCollectedSentinel);
2619 } else if (lookup_result == ObjectIdRing::kExpired) {
2620 PrintSentinel(js, kExpiredSentinel);
2621 } else {
2622 PrintInvalidParamError(js, "targetId");
2623 }
2624 return true;
2625 }
2626 if ((!obj.IsInstance() && !obj.IsNull()) || ContainsNonInstance(obj)) {
2627 js->PrintError(kInvalidParams,
2628 "%s: invalid scope 'targetId' parameter: "
2629 "Cannot evaluate against a VM-internal object",
2630 js->method());
2631 return true;
2632 }
2633 name = String::New(cnames[i]);
2634 names.Add(name);
2635 values.Add(obj);
2636 }
2637 }
2638 return false;
2639}
2640
2641static bool Evaluate(Thread* thread, JSONStream* js) {
2642 // If a compilation service is available, this RPC invocation will have been
2643 // intercepted by RunningIsolates.routeRequest.
2644 js->PrintError(
2645 kExpressionCompilationError,
2646 "%s: No compilation service available; cannot evaluate from source.",
2647 js->method());
2648 return true;
2649}
2650
2651static const MethodParameter* build_expression_evaluation_scope_params[] = {
2652 RUNNABLE_ISOLATE_PARAMETER,
2653 new IdParameter("frameIndex", false),
2654 new IdParameter("targetId", false),
2655 NULL,
2656};
2657
2658static bool BuildExpressionEvaluationScope(Thread* thread, JSONStream* js) {
2659 if (CheckDebuggerDisabled(thread, js)) {
2660 return true;
2661 }
2662
2663 Isolate* isolate = thread->isolate();
2664 DebuggerStackTrace* stack = isolate->debugger()->StackTrace();
2665 intptr_t framePos = UIntParameter::Parse(js->LookupParam("frameIndex"));
2666 if (framePos >= stack->Length()) {
2667 PrintInvalidParamError(js, "frameIndex");
2668 return true;
2669 }
2670
2671 Zone* zone = thread->zone();
2672 const GrowableObjectArray& param_names =
2673 GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
2674 const GrowableObjectArray& param_values =
2675 GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
2676 const GrowableObjectArray& type_params_names =
2677 GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
2678 String& klass_name = String::Handle(zone);
2679 String& library_uri = String::Handle(zone);
2680 bool isStatic = false;
2681
2682 if (BuildScope(thread, js, param_names, param_values)) {
2683 return true;
2684 }
2685
2686 if (js->HasParam("frameIndex")) {
2687 // building scope in the context of a given frame
2688 DebuggerStackTrace* stack = isolate->debugger()->StackTrace();
2689 intptr_t framePos = UIntParameter::Parse(js->LookupParam("frameIndex"));
2690 if (framePos >= stack->Length()) {
2691 PrintInvalidParamError(js, "frameIndex");
2692 return true;
2693 }
2694
2695 ActivationFrame* frame = stack->FrameAt(framePos);
2696 frame->BuildParameters(param_names, param_values, type_params_names);
2697
2698 if (frame->function().is_static()) {
2699 const Class& cls = Class::Handle(zone, frame->function().Owner());
2700 if (!cls.IsTopLevel()) {
2701 klass_name = cls.UserVisibleName();
2702 }
2703 library_uri = Library::Handle(zone, cls.library()).url();
2704 isStatic = true;
2705 } else {
2706 const Class& method_cls = Class::Handle(zone, frame->function().origin());
2707 library_uri = Library::Handle(zone, method_cls.library()).url();
2708 klass_name = method_cls.UserVisibleName();
2709 isStatic = false;
2710 }
2711 } else {
2712 // building scope in the context of a given object
2713 if (!js->HasParam("targetId")) {
2714 js->PrintError(kInvalidParams,
2715 "Either targetId or frameIndex has to be provided.");
2716 return true;
2717 }
2718 const char* target_id = js->LookupParam("targetId");
2719
2720 ObjectIdRing::LookupResult lookup_result;
2721 Object& obj = Object::Handle(
2722 zone, LookupHeapObject(thread, target_id, &lookup_result));
2723 if (obj.raw() == Object::sentinel().raw()) {
2724 PrintInvalidParamError(js, "targetId");
2725 return true;
2726 }
2727 if (obj.IsLibrary()) {
2728 const Library& lib = Library::Cast(obj);
2729 library_uri = lib.url();
2730 isStatic = true;
2731 } else if (obj.IsClass() || ((obj.IsInstance() || obj.IsNull()) &&
2732 !ContainsNonInstance(obj))) {
2733 Class& cls = Class::Handle(zone);
2734 if (obj.IsClass()) {
2735 cls ^= obj.raw();
2736 isStatic = true;
2737 } else {
2738 Instance& instance = Instance::Handle(zone);
2739 instance ^= obj.raw();
2740 cls = instance.clazz();
2741 isStatic = false;
2742 }
2743 if (!cls.IsTopLevel() &&
2744 (cls.id() < kInstanceCid || cls.id() == kTypeArgumentsCid)) {
2745 js->PrintError(
2746 kInvalidParams,
2747 "Expressions can be evaluated only with regular Dart instances");
2748 return true;
2749 }
2750
2751 if (!cls.IsTopLevel()) {
2752 klass_name = cls.UserVisibleName();
2753 }
2754 library_uri = Library::Handle(zone, cls.library()).url();
2755 } else {
2756 js->PrintError(kInvalidParams,
2757 "%s: invalid 'targetId' parameter: "
2758 "Cannot evaluate against a VM-internal object",
2759 js->method());
2760 }
2761 }
2762
2763 JSONObject report(js);
2764 {
2765 JSONArray jsonParamNames(&report, "param_names");
2766
2767 String& param_name = String::Handle(zone);
2768 for (intptr_t i = 0; i < param_names.Length(); i++) {
2769 param_name ^= param_names.At(i);
2770 jsonParamNames.AddValue(param_name.ToCString());
2771 }
2772 }
2773
2774 {
2775 JSONArray jsonTypeParamsNames(&report, "type_params_names");
2776 String& type_param_name = String::Handle(zone);
2777 for (intptr_t i = 0; i < type_params_names.Length(); i++) {
2778 type_param_name ^= type_params_names.At(i);
2779 jsonTypeParamsNames.AddValue(type_param_name.ToCString());
2780 }
2781 }
2782 report.AddProperty("libraryUri", library_uri.ToCString());
2783 if (!klass_name.IsNull()) {
2784 report.AddProperty("klass", klass_name.ToCString());
2785 }
2786 report.AddProperty("isStatic", isStatic);
2787
2788 return true;
2789}
2790
2791#if !defined(DART_PRECOMPILED_RUNTIME)
2792// Parse comma-separated list of values, put them into values
2793static bool ParseCSVList(const char* csv_list,
2794 const GrowableObjectArray& values) {
2795 Zone* zone = Thread::Current()->zone();
2796 String& s = String::Handle(zone);
2797 const char* c = csv_list;
2798 if (*c++ != '[') return false;
2799 while (IsWhitespace(*c) && *c != '\0') {
2800 c++;
2801 }
2802 while (*c != '\0') {
2803 const char* value = c;
2804 while (*c != '\0' && *c != ']' && *c != ',' && !IsWhitespace(*c)) {
2805 c++;
2806 }
2807 if (c > value) {
2808 s = String::New(zone->MakeCopyOfStringN(value, c - value));
2809 values.Add(s);
2810 }
2811 switch (*c) {
2812 case '\0':
2813 return false;
2814 case ',':
2815 c++;
2816 break;
2817 case ']':
2818 return true;
2819 }
2820 while (IsWhitespace(*c) && *c != '\0') {
2821 c++;
2822 }
2823 }
2824 return false;
2825}
2826#endif
2827
2828static const MethodParameter* compile_expression_params[] = {
2829 RUNNABLE_ISOLATE_PARAMETER,
2830 new StringParameter("expression", true),
2831 new StringParameter("definitions", false),
2832 new StringParameter("typeDefinitions", false),
2833 new StringParameter("libraryUri", true),
2834 new StringParameter("klass", false),
2835 new BoolParameter("isStatic", false),
2836 NULL,
2837};
2838
2839static bool CompileExpression(Thread* thread, JSONStream* js) {
2840#if defined(DART_PRECOMPILED_RUNTIME)
2841 js->PrintError(kFeatureDisabled, "Debugger is disabled in AOT mode.");
2842 return true;
2843#else
2844 if (CheckDebuggerDisabled(thread, js)) {
2845 return true;
2846 }
2847
2848 if (!KernelIsolate::IsRunning() && !KernelIsolate::Start()) {
2849 js->PrintError(
2850 kExpressionCompilationError,
2851 "%s: No compilation service available; cannot evaluate from source.",
2852 js->method());
2853 return true;
2854 }
2855
2856 const char* klass = js->LookupParam("klass");
2857 bool is_static =
2858 BoolParameter::Parse(js->LookupParam("isStatic"), (klass == nullptr));
2859
2860 const GrowableObjectArray& params =
2861 GrowableObjectArray::Handle(thread->zone(), GrowableObjectArray::New());
2862 if (!ParseCSVList(js->LookupParam("definitions"), params)) {
2863 PrintInvalidParamError(js, "definitions");
2864 return true;
2865 }
2866
2867 const GrowableObjectArray& type_params =
2868 GrowableObjectArray::Handle(thread->zone(), GrowableObjectArray::New());
2869 if (!ParseCSVList(js->LookupParam("typeDefinitions"), type_params)) {
2870 PrintInvalidParamError(js, "typedDefinitions");
2871 return true;
2872 }
2873
2874 const uint8_t* kernel_buffer = Service::dart_library_kernel();
2875 const intptr_t kernel_buffer_len = Service::dart_library_kernel_length();
2876
2877 Dart_KernelCompilationResult compilation_result =
2878 KernelIsolate::CompileExpressionToKernel(
2879 kernel_buffer, kernel_buffer_len, js->LookupParam("expression"),
2880 Array::Handle(Array::MakeFixedLength(params)),
2881 Array::Handle(Array::MakeFixedLength(type_params)),
2882 js->LookupParam("libraryUri"), js->LookupParam("klass"), is_static);
2883
2884 if (compilation_result.status != Dart_KernelCompilationStatus_Ok) {
2885 js->PrintError(kExpressionCompilationError, "%s", compilation_result.error);
2886 free(compilation_result.error);
2887 return true;
2888 }
2889
2890 const uint8_t* kernel_bytes = compilation_result.kernel;
2891 intptr_t kernel_length = compilation_result.kernel_size;
2892 ASSERT(kernel_bytes != NULL);
2893
2894 JSONObject report(js);
2895 report.AddPropertyBase64("kernelBytes", kernel_bytes, kernel_length);
2896 return true;
2897#endif
2898}
2899
2900static const MethodParameter* evaluate_compiled_expression_params[] = {
2901 RUNNABLE_ISOLATE_PARAMETER,
2902 new UIntParameter("frameIndex", false),
2903 new IdParameter("targetId", false),
2904 new StringParameter("kernelBytes", true),
2905 NULL,
2906};
2907
2908ExternalTypedDataPtr DecodeKernelBuffer(const char* kernel_buffer_base64) {
2909 intptr_t kernel_length;
2910 uint8_t* kernel_buffer = DecodeBase64(kernel_buffer_base64, &kernel_length);
2911 return ExternalTypedData::NewFinalizeWithFree(kernel_buffer, kernel_length);
2912}
2913
2914static bool EvaluateCompiledExpression(Thread* thread, JSONStream* js) {
2915 if (CheckDebuggerDisabled(thread, js)) {
2916 return true;
2917 }
2918
2919 Isolate* isolate = thread->isolate();
2920
2921 bool disable_breakpoints =
2922 BoolParameter::Parse(js->LookupParam("disableBreakpoints"), false);
2923 DisableBreakpointsScope db(isolate->debugger(), disable_breakpoints);
2924
2925 DebuggerStackTrace* stack = isolate->debugger()->StackTrace();
2926 intptr_t frame_pos = UIntParameter::Parse(js->LookupParam("frameIndex"));
2927 if (frame_pos >= stack->Length()) {
2928 PrintInvalidParamError(js, "frameIndex");
2929 return true;
2930 }
2931 Zone* zone = thread->zone();
2932 const GrowableObjectArray& param_names =
2933 GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
2934 const GrowableObjectArray& param_values =
2935 GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
2936 if (BuildScope(thread, js, param_names, param_values)) {
2937 return true;
2938 }
2939 const GrowableObjectArray& type_params_names =
2940 GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
2941
2942 const ExternalTypedData& kernel_data = ExternalTypedData::Handle(
2943 zone, DecodeKernelBuffer(js->LookupParam("kernelBytes")));
2944
2945 if (js->HasParam("frameIndex")) {
2946 DebuggerStackTrace* stack = isolate->debugger()->StackTrace();
2947 intptr_t frame_pos = UIntParameter::Parse(js->LookupParam("frameIndex"));
2948 if (frame_pos >= stack->Length()) {
2949 PrintInvalidParamError(js, "frameIndex");
2950 return true;
2951 }
2952
2953 ActivationFrame* frame = stack->FrameAt(frame_pos);
2954 TypeArguments& type_arguments = TypeArguments::Handle(
2955 zone,
2956 frame->BuildParameters(param_names, param_values, type_params_names));
2957
2958 const Object& result = Object::Handle(
2959 zone,
2960 frame->EvaluateCompiledExpression(
2961 kernel_data,
2962 Array::Handle(zone, Array::MakeFixedLength(type_params_names)),
2963 Array::Handle(zone, Array::MakeFixedLength(param_values)),
2964 type_arguments));
2965 result.PrintJSON(js, true);
2966 return true;
2967 } else {
2968 // evaluating expression in the context of a given object
2969 if (!js->HasParam("targetId")) {
2970 js->PrintError(kInvalidParams,
2971 "Either targetId or frameIndex has to be provided.");
2972 return true;
2973 }
2974 const char* target_id = js->LookupParam("targetId");
2975 ObjectIdRing::LookupResult lookup_result;
2976 Object& obj = Object::Handle(
2977 zone, LookupHeapObject(thread, target_id, &lookup_result));
2978 if (obj.raw() == Object::sentinel().raw()) {
2979 if (lookup_result == ObjectIdRing::kCollected) {
2980 PrintSentinel(js, kCollectedSentinel);
2981 } else if (lookup_result == ObjectIdRing::kExpired) {
2982 PrintSentinel(js, kExpiredSentinel);
2983 } else {
2984 PrintInvalidParamError(js, "targetId");
2985 }
2986 return true;
2987 }
2988 TypeArguments& type_arguments = TypeArguments::Handle(zone);
2989 if (obj.IsLibrary()) {
2990 const Library& lib = Library::Cast(obj);
2991 const Object& result = Object::Handle(
2992 zone,
2993 lib.EvaluateCompiledExpression(
2994 kernel_data,
2995 Array::Handle(zone, Array::MakeFixedLength(type_params_names)),
2996 Array::Handle(zone, Array::MakeFixedLength(param_values)),
2997 type_arguments));
2998 result.PrintJSON(js, true);
2999 return true;
3000 }
3001 if (obj.IsClass()) {
3002 const Class& cls = Class::Cast(obj);
3003 const Object& result = Object::Handle(
3004 zone,
3005 cls.EvaluateCompiledExpression(
3006 kernel_data,
3007 Array::Handle(zone, Array::MakeFixedLength(type_params_names)),
3008 Array::Handle(zone, Array::MakeFixedLength(param_values)),
3009 type_arguments));
3010 result.PrintJSON(js, true);
3011 return true;
3012 }
3013 if ((obj.IsInstance() || obj.IsNull()) && !ContainsNonInstance(obj)) {
3014 // We don't use Instance::Cast here because it doesn't allow null.
3015 Instance& instance = Instance::Handle(zone);
3016 instance ^= obj.raw();
3017 const Class& receiver_cls = Class::Handle(zone, instance.clazz());
3018 const Object& result = Object::Handle(
3019 zone,
3020 instance.EvaluateCompiledExpression(
3021 receiver_cls, kernel_data,
3022 Array::Handle(zone, Array::MakeFixedLength(type_params_names)),
3023 Array::Handle(zone, Array::MakeFixedLength(param_values)),
3024 type_arguments));
3025 result.PrintJSON(js, true);
3026 return true;
3027 }
3028 js->PrintError(kInvalidParams,
3029 "%s: invalid 'targetId' parameter: "
3030 "Cannot evaluate against a VM-internal object",
3031 js->method());
3032 return true;
3033 }
3034}
3035
3036static const MethodParameter* evaluate_in_frame_params[] = {
3037 RUNNABLE_ISOLATE_PARAMETER, new UIntParameter("frameIndex", true),
3038 new MethodParameter("expression", true), NULL,
3039};
3040
3041static bool EvaluateInFrame(Thread* thread, JSONStream* js) {
3042 // If a compilation service is available, this RPC invocation will have been
3043 // intercepted by RunningIsolates.routeRequest.
3044 js->PrintError(
3045 kExpressionCompilationError,
3046 "%s: No compilation service available; cannot evaluate from source.",
3047 js->method());
3048 return true;
3049}
3050
3051class GetInstancesVisitor : public ObjectGraph::Visitor {
3052 public:
3053 GetInstancesVisitor(const Class& cls,
3054 ZoneGrowableHandlePtrArray<Object>* storage,
3055 intptr_t limit)
3056 : cls_(cls), storage_(storage), limit_(limit), count_(0) {}
3057
3058 virtual Direction VisitObject(ObjectGraph::StackIterator* it) {
3059 ObjectPtr raw_obj = it->Get();
3060 if (raw_obj->IsPseudoObject()) {
3061 return kProceed;
3062 }
3063 Thread* thread = Thread::Current();
3064 REUSABLE_OBJECT_HANDLESCOPE(thread);
3065 Object& obj = thread->ObjectHandle();
3066 obj = raw_obj;
3067 if (obj.GetClassId() == cls_.id()) {
3068 if (count_ < limit_) {
3069 storage_->Add(Object::Handle(raw_obj));
3070 }
3071 ++count_;
3072 }
3073 return kProceed;
3074 }
3075
3076 intptr_t count() const { return count_; }
3077
3078 private:
3079 const Class& cls_;
3080 ZoneGrowableHandlePtrArray<Object>* storage_;
3081 const intptr_t limit_;
3082 intptr_t count_;
3083};
3084
3085static const MethodParameter* get_instances_params[] = {
3086 RUNNABLE_ISOLATE_PARAMETER, NULL,
3087};
3088
3089static bool GetInstances(Thread* thread, JSONStream* js) {
3090 const char* object_id = js->LookupParam("objectId");
3091 if (object_id == NULL) {
3092 PrintMissingParamError(js, "objectId");
3093 return true;
3094 }
3095 const char* limit_cstr = js->LookupParam("limit");
3096 if (limit_cstr == NULL) {
3097 PrintMissingParamError(js, "limit");
3098 return true;
3099 }
3100 intptr_t limit;
3101 if (!GetIntegerId(limit_cstr, &limit)) {
3102 PrintInvalidParamError(js, "limit");
3103 return true;
3104 }
3105
3106 const Object& obj = Object::Handle(LookupHeapObject(thread, object_id, NULL));
3107 if (obj.raw() == Object::sentinel().raw() || !obj.IsClass()) {
3108 PrintInvalidParamError(js, "objectId");
3109 return true;
3110 }
3111 const Class& cls = Class::Cast(obj);
3112
3113 // Ensure the array and handles created below are promptly destroyed.
3114 StackZone zone(thread);
3115 HANDLESCOPE(thread);
3116
3117 ZoneGrowableHandlePtrArray<Object> storage(thread->zone(), limit);
3118 GetInstancesVisitor visitor(cls, &storage, limit);
3119 {
3120 ObjectGraph graph(thread);
3121 HeapIterationScope iteration_scope(Thread::Current(), true);
3122 graph.IterateObjects(&visitor);
3123 }
3124 intptr_t count = visitor.count();
3125 JSONObject jsobj(js);
3126 jsobj.AddProperty("type", "InstanceSet");
3127 jsobj.AddProperty("totalCount", count);
3128 {
3129 JSONArray samples(&jsobj, "instances");
3130 for (int i = 0; (i < limit) && (i < count); i++) {
3131 samples.AddValue(storage.At(i));
3132 }
3133 }
3134 return true;
3135}
3136
3137#if !defined(DART_PRECOMPILED_RUNTIME)
3138static const char* const report_enum_names[] = {
3139 SourceReport::kCallSitesStr,
3140 SourceReport::kCoverageStr,
3141 SourceReport::kPossibleBreakpointsStr,
3142 SourceReport::kProfileStr,
3143 NULL,
3144};
3145#endif
3146
3147static const MethodParameter* get_source_report_params[] = {
3148#if !defined(DART_PRECOMPILED_RUNTIME)
3149 RUNNABLE_ISOLATE_PARAMETER,
3150 new EnumListParameter("reports", true, report_enum_names),
3151 new IdParameter("scriptId", false),
3152 new UIntParameter("tokenPos", false),
3153 new UIntParameter("endTokenPos", false),
3154 new BoolParameter("forceCompile", false),
3155#endif
3156 NULL,
3157};
3158
3159static bool GetSourceReport(Thread* thread, JSONStream* js) {
3160#if defined(DART_PRECOMPILED_RUNTIME)
3161 js->PrintError(kFeatureDisabled, "disabled in AOT mode and PRODUCT.");
3162 return false;
3163#else
3164 if (CheckCompilerDisabled(thread, js)) {
3165 return true;
3166 }
3167
3168 const char* reports_str = js->LookupParam("reports");
3169 const EnumListParameter* reports_parameter =
3170 static_cast<const EnumListParameter*>(get_source_report_params[1]);
3171 const char** reports = reports_parameter->Parse(thread->zone(), reports_str);
3172 intptr_t report_set = 0;
3173 while (*reports != NULL) {
3174 if (strcmp(*reports, SourceReport::kCallSitesStr) == 0) {
3175 report_set |= SourceReport::kCallSites;
3176 } else if (strcmp(*reports, SourceReport::kCoverageStr) == 0) {
3177 report_set |= SourceReport::kCoverage;
3178 } else if (strcmp(*reports, SourceReport::kPossibleBreakpointsStr) == 0) {
3179 report_set |= SourceReport::kPossibleBreakpoints;
3180 } else if (strcmp(*reports, SourceReport::kProfileStr) == 0) {
3181 report_set |= SourceReport::kProfile;
3182 }
3183 reports++;
3184 }
3185
3186 SourceReport::CompileMode compile_mode = SourceReport::kNoCompile;
3187 if (BoolParameter::Parse(js->LookupParam("forceCompile"), false)) {
3188 compile_mode = SourceReport::kForceCompile;
3189 }
3190
3191 Script& script = Script::Handle();
3192 intptr_t start_pos = UIntParameter::Parse(js->LookupParam("tokenPos"));
3193 intptr_t end_pos = UIntParameter::Parse(js->LookupParam("endTokenPos"));
3194
3195 if (js->HasParam("scriptId")) {
3196 // Get the target script.
3197 const char* script_id_param = js->LookupParam("scriptId");
3198 const Object& obj =
3199 Object::Handle(LookupHeapObject(thread, script_id_param, NULL));
3200 if (obj.raw() == Object::sentinel().raw() || !obj.IsScript()) {
3201 PrintInvalidParamError(js, "scriptId");
3202 return true;
3203 }
3204 script ^= obj.raw();
3205 } else {
3206 if (js->HasParam("tokenPos")) {
3207 js->PrintError(
3208 kInvalidParams,
3209 "%s: the 'tokenPos' parameter requires the 'scriptId' parameter",
3210 js->method());
3211 return true;
3212 }
3213 if (js->HasParam("endTokenPos")) {
3214 js->PrintError(
3215 kInvalidParams,
3216 "%s: the 'endTokenPos' parameter requires the 'scriptId' parameter",
3217 js->method());
3218 return true;
3219 }
3220 }
3221 SourceReport report(report_set, compile_mode);
3222 report.PrintJSON(js, script, TokenPosition(start_pos),
3223 TokenPosition(end_pos));
3224 return true;
3225#endif // !DART_PRECOMPILED_RUNTIME
3226}
3227
3228static const MethodParameter* reload_sources_params[] = {
3229 RUNNABLE_ISOLATE_PARAMETER,
3230 new BoolParameter("force", false),
3231 new BoolParameter("pause", false),
3232 new StringParameter("rootLibUri", false),
3233 new StringParameter("packagesUri", false),
3234 NULL,
3235};
3236
3237static bool ReloadSources(Thread* thread, JSONStream* js) {
3238#if defined(DART_PRECOMPILED_RUNTIME)
3239 js->PrintError(kFeatureDisabled, "Compiler is disabled in AOT mode.");
3240 return true;
3241#else
3242 if (CheckCompilerDisabled(thread, js)) {
3243 return true;
3244 }
3245
3246 IsolateGroup* isolate_group = thread->isolate_group();
3247 if (isolate_group->library_tag_handler() == nullptr) {
3248 js->PrintError(kFeatureDisabled,
3249 "A library tag handler must be installed.");
3250 return true;
3251 }
3252 // TODO(dartbug.com/36097): We need to change the "reloadSources" service-api
3253 // call to accept an isolate group instead of an isolate.
3254 Isolate* isolate = thread->isolate();
3255 if ((isolate->sticky_error() != Error::null()) ||
3256 (Thread::Current()->sticky_error() != Error::null())) {
3257 js->PrintError(kIsolateReloadBarred,
3258 "This isolate cannot reload sources anymore because there "
3259 "was an unhandled exception error. Restart the isolate.");
3260 return true;
3261 }
3262 if (isolate_group->IsReloading()) {
3263 js->PrintError(kIsolateIsReloading, "This isolate is being reloaded.");
3264 return true;
3265 }
3266 if (!isolate->CanReload()) {
3267 js->PrintError(kFeatureDisabled,
3268 "This isolate cannot reload sources right now.");
3269 return true;
3270 }
3271 const bool force_reload =
3272 BoolParameter::Parse(js->LookupParam("force"), false);
3273
3274 isolate_group->ReloadSources(js, force_reload, js->LookupParam("rootLibUri"),
3275 js->LookupParam("packagesUri"));
3276
3277 Service::CheckForPause(isolate, js);
3278
3279 return true;
3280#endif
3281}
3282
3283void Service::CheckForPause(Isolate* isolate, JSONStream* stream) {
3284 // Should we pause?
3285 isolate->set_should_pause_post_service_request(
3286 BoolParameter::Parse(stream->LookupParam("pause"), false));
3287}
3288
3289ErrorPtr Service::MaybePause(Isolate* isolate, const Error& error) {
3290 // Don't pause twice.
3291 if (!isolate->IsPaused()) {
3292 if (isolate->should_pause_post_service_request()) {
3293 isolate->set_should_pause_post_service_request(false);
3294 if (!error.IsNull()) {
3295 // Before pausing, restore the sticky error. The debugger will return it
3296 // from PausePostRequest.
3297 Thread::Current()->set_sticky_error(error);
3298 }
3299 return isolate->PausePostRequest();
3300 }
3301 }
3302 return error.raw();
3303}
3304
3305static bool AddBreakpointCommon(Thread* thread,
3306 JSONStream* js,
3307 const String& script_uri) {
3308 if (CheckDebuggerDisabled(thread, js)) {
3309 return true;
3310 }
3311
3312 const char* line_param = js->LookupParam("line");
3313 intptr_t line = UIntParameter::Parse(line_param);
3314 const char* col_param = js->LookupParam("column");
3315 intptr_t col = -1;
3316 if (col_param != NULL) {
3317 col = UIntParameter::Parse(col_param);
3318 if (col == 0) {
3319 // Column number is 1-based.
3320 PrintInvalidParamError(js, "column");
3321 return true;
3322 }
3323 }
3324 ASSERT(!script_uri.IsNull());
3325 Breakpoint* bpt = NULL;
3326 bpt = thread->isolate()->debugger()->SetBreakpointAtLineCol(script_uri, line,
3327 col);
3328 if (bpt == NULL) {
3329 js->PrintError(kCannotAddBreakpoint,
3330 "%s: Cannot add breakpoint at line '%s'", js->method(),
3331 line_param);
3332 return true;
3333 }
3334 bpt->PrintJSON(js);
3335 return true;
3336}
3337
3338static const MethodParameter* add_breakpoint_params[] = {
3339 RUNNABLE_ISOLATE_PARAMETER,
3340 new IdParameter("scriptId", true),
3341 new UIntParameter("line", true),
3342 new UIntParameter("column", false),
3343 NULL,
3344};
3345
3346static bool AddBreakpoint(Thread* thread, JSONStream* js) {
3347 if (CheckDebuggerDisabled(thread, js)) {
3348 return true;
3349 }
3350
3351 const char* script_id_param = js->LookupParam("scriptId");
3352 Object& obj = Object::Handle(LookupHeapObject(thread, script_id_param, NULL));
3353 if (obj.raw() == Object::sentinel().raw() || !obj.IsScript()) {
3354 PrintInvalidParamError(js, "scriptId");
3355 return true;
3356 }
3357 const Script& script = Script::Cast(obj);
3358 const String& script_uri = String::Handle(script.url());
3359 ASSERT(!script_uri.IsNull());
3360 return AddBreakpointCommon(thread, js, script_uri);
3361}
3362
3363static const MethodParameter* add_breakpoint_with_script_uri_params[] = {
3364 RUNNABLE_ISOLATE_PARAMETER,
3365 new IdParameter("scriptUri", true),
3366 new UIntParameter("line", true),
3367 new UIntParameter("column", false),
3368 NULL,
3369};
3370
3371static bool AddBreakpointWithScriptUri(Thread* thread, JSONStream* js) {
3372 if (CheckDebuggerDisabled(thread, js)) {
3373 return true;
3374 }
3375
3376 const char* script_uri_param = js->LookupParam("scriptUri");
3377 const String& script_uri = String::Handle(String::New(script_uri_param));
3378 return AddBreakpointCommon(thread, js, script_uri);
3379}
3380
3381static const MethodParameter* add_breakpoint_at_entry_params[] = {
3382 RUNNABLE_ISOLATE_PARAMETER, new IdParameter("functionId", true), NULL,
3383};
3384
3385static bool AddBreakpointAtEntry(Thread* thread, JSONStream* js) {
3386 if (CheckDebuggerDisabled(thread, js)) {
3387 return true;
3388 }
3389
3390 const char* function_id = js->LookupParam("functionId");
3391 Object& obj = Object::Handle(LookupHeapObject(thread, function_id, NULL));
3392 if (obj.raw() == Object::sentinel().raw() || !obj.IsFunction()) {
3393 PrintInvalidParamError(js, "functionId");
3394 return true;
3395 }
3396 const Function& function = Function::Cast(obj);
3397 Breakpoint* bpt =
3398 thread->isolate()->debugger()->SetBreakpointAtEntry(function, false);
3399 if (bpt == NULL) {
3400 js->PrintError(kCannotAddBreakpoint,
3401 "%s: Cannot add breakpoint at function '%s'", js->method(),
3402 function.ToCString());
3403 return true;
3404 }
3405 bpt->PrintJSON(js);
3406 return true;
3407}
3408
3409static const MethodParameter* add_breakpoint_at_activation_params[] = {
3410 RUNNABLE_ISOLATE_PARAMETER, new IdParameter("objectId", true), NULL,
3411};
3412
3413static bool AddBreakpointAtActivation(Thread* thread, JSONStream* js) {
3414 if (CheckDebuggerDisabled(thread, js)) {
3415 return true;
3416 }
3417
3418 const char* object_id = js->LookupParam("objectId");
3419 Object& obj = Object::Handle(LookupHeapObject(thread, object_id, NULL));
3420 if (obj.raw() == Object::sentinel().raw() || !obj.IsInstance()) {
3421 PrintInvalidParamError(js, "objectId");
3422 return true;
3423 }
3424 const Instance& closure = Instance::Cast(obj);
3425 Breakpoint* bpt =
3426 thread->isolate()->debugger()->SetBreakpointAtActivation(closure, false);
3427 if (bpt == NULL) {
3428 js->PrintError(kCannotAddBreakpoint,
3429 "%s: Cannot add breakpoint at activation", js->method());
3430 return true;
3431 }
3432 bpt->PrintJSON(js);
3433 return true;
3434}
3435
3436static const MethodParameter* remove_breakpoint_params[] = {
3437 RUNNABLE_ISOLATE_PARAMETER, NULL,
3438};
3439
3440static bool RemoveBreakpoint(Thread* thread, JSONStream* js) {
3441 if (CheckDebuggerDisabled(thread, js)) {
3442 return true;
3443 }
3444
3445 if (!js->HasParam("breakpointId")) {
3446 PrintMissingParamError(js, "breakpointId");
3447 return true;
3448 }
3449 const char* bpt_id = js->LookupParam("breakpointId");
3450 ObjectIdRing::LookupResult lookup_result;
3451 Isolate* isolate = thread->isolate();
3452 Breakpoint* bpt = LookupBreakpoint(isolate, bpt_id, &lookup_result);
3453 // TODO(turnidge): Should we return a different error for bpts whic
3454 // have been already removed?
3455 if (bpt == NULL) {
3456 PrintInvalidParamError(js, "breakpointId");
3457 return true;
3458 }
3459 isolate->debugger()->RemoveBreakpoint(bpt->id());
3460 PrintSuccess(js);
3461 return true;
3462}
3463
3464static ClassPtr GetMetricsClass(Thread* thread) {
3465 Zone* zone = thread->zone();
3466 const Library& prof_lib = Library::Handle(zone, Library::DeveloperLibrary());
3467 ASSERT(!prof_lib.IsNull());
3468 const String& metrics_cls_name = String::Handle(zone, String::New("Metrics"));
3469 ASSERT(!metrics_cls_name.IsNull());
3470 const Class& metrics_cls =
3471 Class::Handle(zone, prof_lib.LookupClass(metrics_cls_name));
3472 ASSERT(!metrics_cls.IsNull());
3473 return metrics_cls.raw();
3474}
3475
3476static bool HandleNativeMetricsList(Thread* thread, JSONStream* js) {
3477 JSONObject obj(js);
3478 obj.AddProperty("type", "MetricList");
3479 {
3480 JSONArray metrics(&obj, "metrics");
3481
3482 auto isolate = thread->isolate();
3483#define ADD_METRIC(type, variable, name, unit) \
3484 metrics.AddValue(isolate->Get##variable##Metric());
3485 ISOLATE_METRIC_LIST(ADD_METRIC);
3486#undef ADD_METRIC
3487
3488 auto isolate_group = thread->isolate_group();
3489#define ADD_METRIC(type, variable, name, unit) \
3490 metrics.AddValue(isolate_group->Get##variable##Metric());
3491 ISOLATE_GROUP_METRIC_LIST(ADD_METRIC);
3492#undef ADD_METRIC
3493 }
3494 return true;
3495}
3496
3497static bool HandleNativeMetric(Thread* thread, JSONStream* js, const char* id) {
3498 auto isolate = thread->isolate();
3499#define ADD_METRIC(type, variable, name, unit) \
3500 if (strcmp(id, name) == 0) { \
3501 isolate->Get##variable##Metric()->PrintJSON(js); \
3502 return true; \
3503 }
3504 ISOLATE_METRIC_LIST(ADD_METRIC);
3505#undef ADD_METRIC
3506
3507 auto isolate_group = thread->isolate_group();
3508#define ADD_METRIC(type, variable, name, unit) \
3509 if (strcmp(id, name) == 0) { \
3510 isolate_group->Get##variable##Metric()->PrintJSON(js); \
3511 return true; \
3512 }
3513 ISOLATE_GROUP_METRIC_LIST(ADD_METRIC);
3514#undef ADD_METRIC
3515
3516 PrintInvalidParamError(js, "metricId");
3517 return true;
3518}
3519
3520static bool HandleDartMetricsList(Thread* thread, JSONStream* js) {
3521 Zone* zone = thread->zone();
3522 const Class& metrics_cls = Class::Handle(zone, GetMetricsClass(thread));
3523 const String& print_metrics_name =
3524 String::Handle(String::New("_printMetrics"));
3525 ASSERT(!print_metrics_name.IsNull());
3526 const Function& print_metrics = Function::Handle(
3527 zone, metrics_cls.LookupStaticFunctionAllowPrivate(print_metrics_name));
3528 ASSERT(!print_metrics.IsNull());
3529 const Array& args = Object::empty_array();
3530 const Object& result =
3531 Object::Handle(zone, DartEntry::InvokeFunction(print_metrics, args));
3532 ASSERT(!result.IsNull());
3533 ASSERT(result.IsString());
3534 TextBuffer* buffer = js->buffer();
3535 buffer->AddString(String::Cast(result).ToCString());
3536 return true;
3537}
3538
3539static bool HandleDartMetric(Thread* thread, JSONStream* js, const char* id) {
3540 Zone* zone = thread->zone();
3541 const Class& metrics_cls = Class::Handle(zone, GetMetricsClass(thread));
3542 const String& print_metric_name = String::Handle(String::New("_printMetric"));
3543 ASSERT(!print_metric_name.IsNull());
3544 const Function& print_metric = Function::Handle(
3545 zone, metrics_cls.LookupStaticFunctionAllowPrivate(print_metric_name));
3546 ASSERT(!print_metric.IsNull());
3547 const String& arg0 = String::Handle(String::New(id));
3548 ASSERT(!arg0.IsNull());
3549 const Array& args = Array::Handle(Array::New(1));
3550 ASSERT(!args.IsNull());
3551 args.SetAt(0, arg0);
3552 const Object& result =
3553 Object::Handle(zone, DartEntry::InvokeFunction(print_metric, args));
3554 if (!result.IsNull()) {
3555 ASSERT(result.IsString());
3556 TextBuffer* buffer = js->buffer();
3557 buffer->AddString(String::Cast(result).ToCString());
3558 return true;
3559 }
3560 PrintInvalidParamError(js, "metricId");
3561 return true;
3562}
3563
3564static const MethodParameter* get_isolate_metric_list_params[] = {
3565 RUNNABLE_ISOLATE_PARAMETER, NULL,
3566};
3567
3568static bool GetIsolateMetricList(Thread* thread, JSONStream* js) {
3569 bool native_metrics = false;
3570 if (js->HasParam("type")) {
3571 if (js->ParamIs("type", "Native")) {
3572 native_metrics = true;
3573 } else if (js->ParamIs("type", "Dart")) {
3574 native_metrics = false;
3575 } else {
3576 PrintInvalidParamError(js, "type");
3577 return true;
3578 }
3579 } else {
3580 PrintMissingParamError(js, "type");
3581 return true;
3582 }
3583 if (native_metrics) {
3584 return HandleNativeMetricsList(thread, js);
3585 }
3586 return HandleDartMetricsList(thread, js);
3587}
3588
3589static const MethodParameter* get_isolate_metric_params[] = {
3590 RUNNABLE_ISOLATE_PARAMETER, NULL,
3591};
3592
3593static bool GetIsolateMetric(Thread* thread, JSONStream* js) {
3594 const char* metric_id = js->LookupParam("metricId");
3595 if (metric_id == NULL) {
3596 PrintMissingParamError(js, "metricId");
3597 return true;
3598 }
3599 // Verify id begins with "metrics/".
3600 static const char* const kMetricIdPrefix = "metrics/";
3601 static intptr_t kMetricIdPrefixLen = strlen(kMetricIdPrefix);
3602 if (strncmp(metric_id, kMetricIdPrefix, kMetricIdPrefixLen) != 0) {
3603 PrintInvalidParamError(js, "metricId");
3604 return true;
3605 }
3606 // Check if id begins with "metrics/native/".
3607 static const char* const kNativeMetricIdPrefix = "metrics/native/";
3608 static intptr_t kNativeMetricIdPrefixLen = strlen(kNativeMetricIdPrefix);
3609 const bool native_metric =
3610 strncmp(metric_id, kNativeMetricIdPrefix, kNativeMetricIdPrefixLen) == 0;
3611 if (native_metric) {
3612 const char* id = metric_id + kNativeMetricIdPrefixLen;
3613 return HandleNativeMetric(thread, js, id);
3614 }
3615 const char* id = metric_id + kMetricIdPrefixLen;
3616 return HandleDartMetric(thread, js, id);
3617}
3618
3619static const MethodParameter* get_vm_metric_list_params[] = {
3620 NO_ISOLATE_PARAMETER, NULL,
3621};
3622
3623static bool GetVMMetricList(Thread* thread, JSONStream* js) {
3624 return false;
3625}
3626
3627static const MethodParameter* get_vm_metric_params[] = {
3628 NO_ISOLATE_PARAMETER, NULL,
3629};
3630
3631static bool GetVMMetric(Thread* thread, JSONStream* js) {
3632 const char* metric_id = js->LookupParam("metricId");
3633 if (metric_id == NULL) {
3634 PrintMissingParamError(js, "metricId");
3635 }
3636 return false;
3637}
3638
3639static const char* const timeline_streams_enum_names[] = {
3640 "all",
3641#define DEFINE_NAME(name, unused) #name,
3642 TIMELINE_STREAM_LIST(DEFINE_NAME)
3643#undef DEFINE_NAME
3644 NULL};
3645
3646static const MethodParameter* set_vm_timeline_flags_params[] = {
3647 NO_ISOLATE_PARAMETER,
3648 new EnumListParameter("recordedStreams",
3649 false,
3650 timeline_streams_enum_names),
3651 NULL,
3652};
3653
3654#if defined(SUPPORT_TIMELINE)
3655static bool HasStream(const char** recorded_streams, const char* stream) {
3656 while (*recorded_streams != NULL) {
3657 if ((strstr(*recorded_streams, "all") != NULL) ||
3658 (strstr(*recorded_streams, stream) != NULL)) {
3659 return true;
3660 }
3661 recorded_streams++;
3662 }
3663 return false;
3664}
3665#endif
3666
3667static bool SetVMTimelineFlags(Thread* thread, JSONStream* js) {
3668#if !defined(SUPPORT_TIMELINE)
3669 PrintSuccess(js);
3670 return true;
3671#else
3672 Isolate* isolate = thread->isolate();
3673 ASSERT(isolate != NULL);
3674 StackZone zone(thread);
3675
3676 const EnumListParameter* recorded_streams_param =
3677 static_cast<const EnumListParameter*>(set_vm_timeline_flags_params[1]);
3678
3679 const char* recorded_streams_str = js->LookupParam("recordedStreams");
3680 const char** recorded_streams =
3681 recorded_streams_param->Parse(thread->zone(), recorded_streams_str);
3682
3683#define SET_ENABLE_STREAM(name, unused) \
3684 Timeline::SetStream##name##Enabled(HasStream(recorded_streams, #name));
3685 TIMELINE_STREAM_LIST(SET_ENABLE_STREAM);
3686#undef SET_ENABLE_STREAM
3687
3688 // Notify clients that the set of subscribed streams has been updated.
3689 if (Service::timeline_stream.enabled()) {
3690 ServiceEvent event(NULL, ServiceEvent::kTimelineStreamSubscriptionsUpdate);
3691 Service::HandleEvent(&event);
3692 }
3693
3694 PrintSuccess(js);
3695
3696 return true;
3697#endif
3698}
3699
3700static const MethodParameter* get_vm_timeline_flags_params[] = {
3701 NO_ISOLATE_PARAMETER, NULL,
3702};
3703
3704static bool GetVMTimelineFlags(Thread* thread, JSONStream* js) {
3705#if !defined(SUPPORT_TIMELINE)
3706 JSONObject obj(js);
3707 obj.AddProperty("type", "TimelineFlags");
3708 return true;
3709#else
3710 Isolate* isolate = thread->isolate();
3711 ASSERT(isolate != NULL);
3712 StackZone zone(thread);
3713 Timeline::PrintFlagsToJSON(js);
3714 return true;
3715#endif
3716}
3717
3718static const MethodParameter* get_vm_timeline_micros_params[] = {
3719 NO_ISOLATE_PARAMETER, NULL,
3720};
3721
3722static bool GetVMTimelineMicros(Thread* thread, JSONStream* js) {
3723 JSONObject obj(js);
3724 obj.AddProperty("type", "Timestamp");
3725 obj.AddPropertyTimeMicros("timestamp", OS::GetCurrentMonotonicMicros());
3726 return true;
3727}
3728
3729static const MethodParameter* clear_vm_timeline_params[] = {
3730 NO_ISOLATE_PARAMETER, NULL,
3731};
3732
3733static bool ClearVMTimeline(Thread* thread, JSONStream* js) {
3734 Isolate* isolate = thread->isolate();
3735 ASSERT(isolate != NULL);
3736 StackZone zone(thread);
3737
3738 Timeline::Clear();
3739
3740 PrintSuccess(js);
3741
3742 return true;
3743}
3744
3745static const MethodParameter* get_vm_timeline_params[] = {
3746 NO_ISOLATE_PARAMETER, new Int64Parameter("timeOriginMicros", false),
3747 new Int64Parameter("timeExtentMicros", false), NULL,
3748};
3749
3750static bool GetVMTimeline(Thread* thread, JSONStream* js) {
3751 Isolate* isolate = thread->isolate();
3752 ASSERT(isolate != NULL);
3753 StackZone zone(thread);
3754 Timeline::ReclaimCachedBlocksFromThreads();
3755 TimelineEventRecorder* timeline_recorder = Timeline::recorder();
3756 // TODO(johnmccutchan): Return an error.
3757 ASSERT(timeline_recorder != NULL);
3758 const char* name = timeline_recorder->name();
3759 if ((strcmp(name, FUCHSIA_RECORDER_NAME) == 0) ||
3760 (strcmp(name, SYSTRACE_RECORDER_NAME) == 0)) {
3761 js->PrintError(kInvalidTimelineRequest,
3762 "A recorder of type \"%s\" is "
3763 "currently in use. As a result, timeline events are handled "
3764 "by the OS rather than the VM. See the VM service "
3765 "documentation for more details on where timeline events "
3766 "can be found for this recorder type.",
3767 timeline_recorder->name());
3768 return true;
3769 }
3770 int64_t time_origin_micros =
3771 Int64Parameter::Parse(js->LookupParam("timeOriginMicros"));
3772 int64_t time_extent_micros =
3773 Int64Parameter::Parse(js->LookupParam("timeExtentMicros"));
3774 TimelineEventFilter filter(time_origin_micros, time_extent_micros);
3775 timeline_recorder->PrintJSON(js, &filter);
3776 return true;
3777}
3778
3779static const char* const step_enum_names[] = {
3780 "None", "Into", "Over", "Out", "Rewind", "OverAsyncSuspension", NULL,
3781};
3782
3783static const Debugger::ResumeAction step_enum_values[] = {
3784 Debugger::kContinue, Debugger::kStepInto,
3785 Debugger::kStepOver, Debugger::kStepOut,
3786 Debugger::kStepRewind, Debugger::kStepOverAsyncSuspension,
3787 Debugger::kContinue, // Default value
3788};
3789
3790static const MethodParameter* resume_params[] = {
3791 RUNNABLE_ISOLATE_PARAMETER,
3792 new EnumParameter("step", false, step_enum_names),
3793 new UIntParameter("frameIndex", false), NULL,
3794};
3795
3796static bool Resume(Thread* thread, JSONStream* js) {
3797 const char* step_param = js->LookupParam("step");
3798 Debugger::ResumeAction step = Debugger::kContinue;
3799 if (step_param != NULL) {
3800 step = EnumMapper(step_param, step_enum_names, step_enum_values);
3801 }
3802 intptr_t frame_index = 1;
3803 const char* frame_index_param = js->LookupParam("frameIndex");
3804 if (frame_index_param != NULL) {
3805 if (step != Debugger::kStepRewind) {
3806 // Only rewind supports the frameIndex parameter.
3807 js->PrintError(
3808 kInvalidParams,
3809 "%s: the 'frameIndex' parameter can only be used when rewinding",
3810 js->method());
3811 return true;
3812 }
3813 frame_index = UIntParameter::Parse(js->LookupParam("frameIndex"));
3814 }
3815 Isolate* isolate = thread->isolate();
3816 if (isolate->message_handler()->is_paused_on_start()) {
3817 // If the user is issuing a 'Over' or an 'Out' step, that is the
3818 // same as a regular resume request.
3819 if (step == Debugger::kStepInto) {
3820 isolate->debugger()->EnterSingleStepMode();
3821 }
3822 isolate->message_handler()->set_should_pause_on_start(false);
3823 isolate->SetResumeRequest();
3824 if (Service::debug_stream.enabled()) {
3825 ServiceEvent event(isolate, ServiceEvent::kResume);
3826 Service::HandleEvent(&event);
3827 }
3828 PrintSuccess(js);
3829 return true;
3830 }
3831 if (isolate->message_handler()->should_pause_on_start()) {
3832 isolate->message_handler()->set_should_pause_on_start(false);
3833 isolate->SetResumeRequest();
3834 if (Service::debug_stream.enabled()) {
3835 ServiceEvent event(isolate, ServiceEvent::kResume);
3836 Service::HandleEvent(&event);
3837 }
3838 PrintSuccess(js);
3839 return true;
3840 }
3841 if (isolate->message_handler()->is_paused_on_exit()) {
3842 isolate->message_handler()->set_should_pause_on_exit(false);
3843 isolate->SetResumeRequest();
3844 // We don't send a resume event because we will be exiting.
3845 PrintSuccess(js);
3846 return true;
3847 }
3848 if (isolate->debugger()->PauseEvent() == NULL) {
3849 js->PrintError(kIsolateMustBePaused, NULL);
3850 return true;
3851 }
3852
3853 const char* error = NULL;
3854 if (!isolate->debugger()->SetResumeAction(step, frame_index, &error)) {
3855 js->PrintError(kCannotResume, "%s", error);
3856 return true;
3857 }
3858 isolate->SetResumeRequest();
3859 PrintSuccess(js);
3860 return true;
3861}
3862
3863static const MethodParameter* kill_params[] = {
3864 RUNNABLE_ISOLATE_PARAMETER,
3865 NULL,
3866};
3867
3868static bool Kill(Thread* thread, JSONStream* js) {
3869 const String& msg =
3870 String::Handle(String::New("isolate terminated by Kill service request"));
3871 const UnwindError& error = UnwindError::Handle(UnwindError::New(msg));
3872 error.set_is_user_initiated(true);
3873 Thread::Current()->set_sticky_error(error);
3874 PrintSuccess(js);
3875 return true;
3876}
3877
3878static const MethodParameter* pause_params[] = {
3879 RUNNABLE_ISOLATE_PARAMETER, NULL,
3880};
3881
3882static bool Pause(Thread* thread, JSONStream* js) {
3883 // TODO(turnidge): This interrupt message could have been sent from
3884 // the service isolate directly, but would require some special case
3885 // code. That would prevent this isolate getting double-interrupted
3886 // with OOB messages.
3887 Isolate* isolate = thread->isolate();
3888 isolate->SendInternalLibMessage(Isolate::kInterruptMsg,
3889 isolate->pause_capability());
3890 PrintSuccess(js);
3891 return true;
3892}
3893
3894static const MethodParameter* enable_profiler_params[] = {
3895 NULL,
3896};
3897
3898static bool EnableProfiler(Thread* thread, JSONStream* js) {
3899 if (!FLAG_profiler) {
3900 FLAG_profiler = true;
3901 Profiler::Init();
3902 }
3903 PrintSuccess(js);
3904 return true;
3905}
3906
3907static const MethodParameter* get_tag_profile_params[] = {
3908 RUNNABLE_ISOLATE_PARAMETER, NULL,
3909};
3910
3911static bool GetTagProfile(Thread* thread, JSONStream* js) {
3912 JSONObject miniProfile(js);
3913 miniProfile.AddProperty("type", "TagProfile");
3914 thread->isolate()->vm_tag_counters()->PrintToJSONObject(&miniProfile);
3915 return true;
3916}
3917
3918static const MethodParameter* get_cpu_samples_params[] = {
3919 RUNNABLE_ISOLATE_PARAMETER,
3920 new Int64Parameter("timeOriginMicros", false),
3921 new Int64Parameter("timeExtentMicros", false),
3922 NULL,
3923};
3924
3925static bool GetCpuSamples(Thread* thread, JSONStream* js) {
3926 int64_t time_origin_micros =
3927 Int64Parameter::Parse(js->LookupParam("timeOriginMicros"));
3928 int64_t time_extent_micros =
3929 Int64Parameter::Parse(js->LookupParam("timeExtentMicros"));
3930 const bool include_code_samples =
3931 BoolParameter::Parse(js->LookupParam("_code"), false);
3932 if (CheckProfilerDisabled(thread, js)) {
3933 return true;
3934 }
3935 ProfilerService::PrintJSON(js, time_origin_micros, time_extent_micros,
3936 include_code_samples);
3937 return true;
3938}
3939
3940static const MethodParameter* get_allocation_samples_params[] = {
3941 RUNNABLE_ISOLATE_PARAMETER,
3942 new IdParameter("classId", false),
3943 new Int64Parameter("timeOriginMicros", false),
3944 new Int64Parameter("timeExtentMicros", false),
3945 NULL,
3946};
3947
3948static bool GetAllocationSamples(Thread* thread, JSONStream* js) {
3949 int64_t time_origin_micros =
3950 Int64Parameter::Parse(js->LookupParam("timeOriginMicros"));
3951 int64_t time_extent_micros =
3952 Int64Parameter::Parse(js->LookupParam("timeExtentMicros"));
3953 const char* class_id = js->LookupParam("classId");
3954 intptr_t cid = -1;
3955 GetPrefixedIntegerId(class_id, "classes/", &cid);
3956 Isolate* isolate = thread->isolate();
3957 if (IsValidClassId(isolate, cid)) {
3958 if (CheckProfilerDisabled(thread, js)) {
3959 return true;
3960 }
3961 const Class& cls = Class::Handle(GetClassForId(isolate, cid));
3962 ProfilerService::PrintAllocationJSON(js, cls, time_origin_micros,
3963 time_extent_micros);
3964 } else {
3965 PrintInvalidParamError(js, "classId");
3966 }
3967 return true;
3968}
3969
3970static const MethodParameter* get_native_allocation_samples_params[] = {
3971 NO_ISOLATE_PARAMETER,
3972 new Int64Parameter("timeOriginMicros", false),
3973 new Int64Parameter("timeExtentMicros", false),
3974 NULL,
3975};
3976
3977static bool GetNativeAllocationSamples(Thread* thread, JSONStream* js) {
3978 int64_t time_origin_micros =
3979 Int64Parameter::Parse(js->LookupParam("timeOriginMicros"));
3980 int64_t time_extent_micros =
3981 Int64Parameter::Parse(js->LookupParam("timeExtentMicros"));
3982 bool include_code_samples =
3983 BoolParameter::Parse(js->LookupParam("_code"), false);
3984#if defined(DEBUG)
3985 Isolate::Current()->heap()->CollectAllGarbage();
3986#endif
3987 if (CheckNativeAllocationProfilerDisabled(thread, js)) {
3988 return true;
3989 }
3990 ProfilerService::PrintNativeAllocationJSON(
3991 js, time_origin_micros, time_extent_micros, include_code_samples);
3992 return true;
3993}
3994
3995static const MethodParameter* clear_cpu_samples_params[] = {
3996 RUNNABLE_ISOLATE_PARAMETER,
3997 NULL,
3998};
3999
4000static bool ClearCpuSamples(Thread* thread, JSONStream* js) {
4001 ProfilerService::ClearSamples();
4002 PrintSuccess(js);
4003 return true;
4004}
4005
4006static bool GetAllocationProfileImpl(Thread* thread,
4007 JSONStream* js,
4008 bool internal) {
4009 bool should_reset_accumulator = false;
4010 bool should_collect = false;
4011 if (js->HasParam("reset")) {
4012 if (js->ParamIs("reset", "true")) {
4013 should_reset_accumulator = true;
4014 } else {
4015 PrintInvalidParamError(js, "reset");
4016 return true;
4017 }
4018 }
4019 if (js->HasParam("gc")) {
4020 if (js->ParamIs("gc", "true")) {
4021 should_collect = true;
4022 } else {
4023 PrintInvalidParamError(js, "gc");
4024 return true;
4025 }
4026 }
4027 auto isolate = thread->isolate();
4028 auto isolate_group = thread->isolate_group();
4029 if (should_reset_accumulator) {
4030 isolate_group->UpdateLastAllocationProfileAccumulatorResetTimestamp();
4031 }
4032 if (should_collect) {
4033 isolate_group->UpdateLastAllocationProfileGCTimestamp();
4034 isolate_group->heap()->CollectAllGarbage();
4035 }
4036 isolate->class_table()->AllocationProfilePrintJSON(js, internal);
4037 return true;
4038}
4039
4040static const MethodParameter* get_allocation_profile_params[] = {
4041 RUNNABLE_ISOLATE_PARAMETER,
4042 NULL,
4043};
4044
4045static bool GetAllocationProfilePublic(Thread* thread, JSONStream* js) {
4046 return GetAllocationProfileImpl(thread, js, false);
4047}
4048
4049static bool GetAllocationProfile(Thread* thread, JSONStream* js) {
4050 return GetAllocationProfileImpl(thread, js, true);
4051}
4052
4053static const MethodParameter* collect_all_garbage_params[] = {
4054 RUNNABLE_ISOLATE_PARAMETER,
4055 NULL,
4056};
4057
4058static bool CollectAllGarbage(Thread* thread, JSONStream* js) {
4059 Isolate* isolate = thread->isolate();
4060 isolate->heap()->CollectAllGarbage(Heap::kDebugging);
4061 PrintSuccess(js);
4062 return true;
4063}
4064
4065static const MethodParameter* get_heap_map_params[] = {
4066 RUNNABLE_ISOLATE_PARAMETER, NULL,
4067};
4068
4069static bool GetHeapMap(Thread* thread, JSONStream* js) {
4070 Isolate* isolate = thread->isolate();
4071 if (js->HasParam("gc")) {
4072 if (js->ParamIs("gc", "scavenge")) {
4073 isolate->heap()->CollectGarbage(Heap::kScavenge, Heap::kDebugging);
4074 } else if (js->ParamIs("gc", "mark-sweep")) {
4075 isolate->heap()->CollectGarbage(Heap::kMarkSweep, Heap::kDebugging);
4076 } else if (js->ParamIs("gc", "mark-compact")) {
4077 isolate->heap()->CollectGarbage(Heap::kMarkCompact, Heap::kDebugging);
4078 } else {
4079 PrintInvalidParamError(js, "gc");
4080 return true;
4081 }
4082 }
4083 isolate->heap()->PrintHeapMapToJSONStream(isolate, js);
4084 return true;
4085}
4086
4087static const MethodParameter* request_heap_snapshot_params[] = {
4088 RUNNABLE_ISOLATE_PARAMETER,
4089 NULL,
4090};
4091
4092static bool RequestHeapSnapshot(Thread* thread, JSONStream* js) {
4093 if (Service::heapsnapshot_stream.enabled()) {
4094 HeapSnapshotWriter writer(thread);
4095 writer.Write();
4096 }
4097 // TODO(koda): Provide some id that ties this request to async response(s).
4098 PrintSuccess(js);
4099 return true;
4100}
4101
4102static intptr_t GetProcessMemoryUsageHelper(JSONStream* js) {
4103 JSONObject response(js);
4104 response.AddProperty("type", "ProcessMemoryUsage");
4105
4106 JSONObject rss(&response, "root");
4107 rss.AddPropertyF("name", "Process %" Pd "", OS::ProcessId());
4108 rss.AddProperty("description", "Resident set size");
4109 rss.AddProperty64("size", Service::CurrentRSS());
4110 JSONArray rss_children(&rss, "children");
4111
4112 JSONObject vm(&rss_children);
4113 intptr_t vm_size = 0;
4114 {
4115 JSONArray vm_children(&vm, "children");
4116
4117 {
4118 JSONObject profiler(&vm_children);
4119 profiler.AddProperty("name", "Profiler");
4120 profiler.AddProperty("description",
4121 "Samples from the Dart VM's profiler");
4122 intptr_t size = Profiler::Size();
4123 vm_size += size;
4124 profiler.AddProperty64("size", size);
4125 JSONArray(&profiler, "children");
4126 }
4127
4128 {
4129 JSONObject timeline(&vm_children);
4130 timeline.AddProperty("name", "Timeline");
4131 timeline.AddProperty(
4132 "description",
4133 "Timeline events from dart:developer and Dart_TimelineEvent");
4134 intptr_t size = Timeline::recorder()->Size();
4135 vm_size += size;
4136 timeline.AddProperty64("size", size);
4137 JSONArray(&timeline, "children");
4138 }
4139
4140 {
4141 JSONObject zone(&vm_children);
4142 zone.AddProperty("name", "Zone");
4143 zone.AddProperty("description", "Arena allocation in the Dart VM");
4144 intptr_t size = Zone::Size();
4145 vm_size += size;
4146 zone.AddProperty64("size", size);
4147 JSONArray(&zone, "children");
4148 }
4149
4150 {
4151 JSONObject semi(&vm_children);
4152 semi.AddProperty("name", "SemiSpace Cache");
4153 semi.AddProperty("description", "Cached heap regions");
4154 intptr_t size = SemiSpace::CachedSize();
4155 vm_size += size;
4156 semi.AddProperty64("size", size);
4157 JSONArray(&semi, "children");
4158 }
4159
4160 IsolateGroup::ForEach([&vm_children, &vm_size](IsolateGroup* isolate_group) {
4161 // Note: new_space()->CapacityInWords() includes memory that hasn't been
4162 // allocated from the OS yet.
4163 int64_t capacity = (isolate_group->heap()->new_space()->UsedInWords() +
4164 isolate_group->heap()->old_space()->CapacityInWords()) *
4165 kWordSize;
4166 int64_t used = isolate_group->heap()->TotalUsedInWords() * kWordSize;
4167 int64_t free = capacity - used;
4168
4169 JSONObject group(&vm_children);
4170 group.AddPropertyF("name", "IsolateGroup %s",
4171 isolate_group->source()->name);
4172 group.AddProperty("description", "Dart heap capacity");
4173 vm_size += capacity;
4174 group.AddProperty64("size", capacity);
4175 JSONArray group_children(&group, "children");
4176
4177 {
4178 JSONObject jsused(&group_children);
4179 jsused.AddProperty("name", "Used");
4180 jsused.AddProperty("description", "");
4181 jsused.AddProperty64("size", used);
4182 JSONArray(&jsused, "children");
4183 }
4184
4185 {
4186 JSONObject jsfree(&group_children);
4187 jsfree.AddProperty("name", "Free");
4188 jsfree.AddProperty("description", "");
4189 jsfree.AddProperty64("size", free);
4190 JSONArray(&jsfree, "children");
4191 }
4192 });
4193 } // vm_children
4194
4195 vm.AddProperty("name", "Dart VM");
4196 vm.AddProperty("description", "");
4197 vm.AddProperty64("size", vm_size);
4198
4199 return vm_size;
4200}
4201
4202static const MethodParameter* get_process_memory_usage_params[] = {
4203 NULL,
4204};
4205
4206static bool GetProcessMemoryUsage(Thread* thread, JSONStream* js) {
4207 GetProcessMemoryUsageHelper(js);
4208 return true;
4209}
4210
4211void Service::SendInspectEvent(Isolate* isolate, const Object& inspectee) {
4212 if (!Service::debug_stream.enabled()) {
4213 return;
4214 }
4215 ServiceEvent event(isolate, ServiceEvent::kInspect);
4216 event.set_inspectee(&inspectee);
4217 Service::HandleEvent(&event);
4218}
4219
4220void Service::SendEmbedderEvent(Isolate* isolate,
4221 const char* stream_id,
4222 const char* event_kind,
4223 const uint8_t* bytes,
4224 intptr_t bytes_len) {
4225 if (!Service::debug_stream.enabled()) {
4226 return;
4227 }
4228 ServiceEvent event(isolate, ServiceEvent::kEmbedder);
4229 event.set_embedder_kind(event_kind);
4230 event.set_embedder_stream_id(stream_id);
4231 event.set_bytes(bytes, bytes_len);
4232 Service::HandleEvent(&event);
4233}
4234
4235void Service::SendLogEvent(Isolate* isolate,
4236 int64_t sequence_number,
4237 int64_t timestamp,
4238 intptr_t level,
4239 const String& name,
4240 const String& message,
4241 const Instance& zone,
4242 const Object& error,
4243 const Instance& stack_trace) {
4244 if (!Service::logging_stream.enabled()) {
4245 return;
4246 }
4247 ServiceEvent::LogRecord log_record;
4248 log_record.sequence_number = sequence_number;
4249 log_record.timestamp = timestamp;
4250 log_record.level = level;
4251 log_record.name = &name;
4252 log_record.message = &message;
4253 log_record.zone = &zone;
4254 log_record.error = &error;
4255 log_record.stack_trace = &stack_trace;
4256 ServiceEvent event(isolate, ServiceEvent::kLogging);
4257 event.set_log_record(log_record);
4258 Service::HandleEvent(&event);
4259}
4260
4261void Service::SendExtensionEvent(Isolate* isolate,
4262 const String& event_kind,
4263 const String& event_data) {
4264 if (!Service::extension_stream.enabled()) {
4265 return;
4266 }
4267 ServiceEvent::ExtensionEvent extension_event;
4268 extension_event.event_kind = &event_kind;
4269 extension_event.event_data = &event_data;
4270 ServiceEvent event(isolate, ServiceEvent::kExtension);
4271 event.set_extension_event(extension_event);
4272 Service::HandleEvent(&event);
4273}
4274
4275static const MethodParameter* get_persistent_handles_params[] = {
4276 ISOLATE_PARAMETER, NULL,
4277};
4278
4279template <typename T>
4280class PersistentHandleVisitor : public HandleVisitor {
4281 public:
4282 PersistentHandleVisitor(Thread* thread, JSONArray* handles)
4283 : HandleVisitor(thread), handles_(handles) {
4284 ASSERT(handles_ != NULL);
4285 }
4286
4287 void Append(PersistentHandle* persistent_handle) {
4288 JSONObject obj(handles_);
4289 obj.AddProperty("type", "_PersistentHandle");
4290 const Object& object = Object::Handle(persistent_handle->raw());
4291 obj.AddProperty("object", object);
4292 }
4293
4294 void Append(FinalizablePersistentHandle* weak_persistent_handle) {
4295 if (!weak_persistent_handle->raw()->IsHeapObject()) {
4296 return; // Free handle.
4297 }
4298
4299 JSONObject obj(handles_);
4300 obj.AddProperty("type", "_WeakPersistentHandle");
4301 const Object& object = Object::Handle(weak_persistent_handle->raw());
4302 obj.AddProperty("object", object);
4303 obj.AddPropertyF(
4304 "peer", "0x%" Px "",
4305 reinterpret_cast<uintptr_t>(weak_persistent_handle->peer()));
4306 obj.AddPropertyF("callbackAddress", "0x%" Px "",
4307 weak_persistent_handle->callback_address());
4308 // Attempt to include a native symbol name.
4309 char* name = NativeSymbolResolver::LookupSymbolName(
4310 weak_persistent_handle->callback_address(), nullptr);
4311 obj.AddProperty("callbackSymbolName", (name == nullptr) ? "" : name);
4312 if (name != nullptr) {
4313 NativeSymbolResolver::FreeSymbolName(name);
4314 }
4315 obj.AddPropertyF("externalSize", "%" Pd "",
4316 weak_persistent_handle->external_size());
4317 }
4318
4319 protected:
4320 virtual void VisitHandle(uword addr) {
4321 T* handle = reinterpret_cast<T*>(addr);
4322 Append(handle);
4323 }
4324
4325 JSONArray* handles_;
4326};
4327
4328static bool GetPersistentHandles(Thread* thread, JSONStream* js) {
4329 Isolate* isolate = thread->isolate();
4330 ASSERT(isolate != NULL);
4331
4332 ApiState* api_state = isolate->group()->api_state();
4333 ASSERT(api_state != NULL);
4334
4335 {
4336 JSONObject obj(js);
4337 obj.AddProperty("type", "_PersistentHandles");
4338 // Persistent handles.
4339 {
4340 JSONArray persistent_handles(&obj, "persistentHandles");
4341 api_state->RunWithLockedPersistentHandles(
4342 [&](PersistentHandles& handles) {
4343 PersistentHandleVisitor<FinalizablePersistentHandle> visitor(
4344 thread, &persistent_handles);
4345 handles.Visit(&visitor);
4346 });
4347 }
4348 // Weak persistent handles.
4349 {
4350 JSONArray weak_persistent_handles(&obj, "weakPersistentHandles");
4351 api_state->RunWithLockedWeakPersistentHandles(
4352 [&](FinalizablePersistentHandles& handles) {
4353 PersistentHandleVisitor<FinalizablePersistentHandle> visitor(
4354 thread, &weak_persistent_handles);
4355 handles.VisitHandles(&visitor);
4356 });
4357 }
4358 }
4359
4360 return true;
4361}
4362
4363static const MethodParameter* get_ports_params[] = {
4364 RUNNABLE_ISOLATE_PARAMETER, NULL,
4365};
4366
4367static bool GetPorts(Thread* thread, JSONStream* js) {
4368 MessageHandler* message_handler = thread->isolate()->message_handler();
4369 PortMap::PrintPortsForMessageHandler(message_handler, js);
4370 return true;
4371}
4372
4373static bool RespondWithMalformedJson(Thread* thread, JSONStream* js) {
4374 JSONObject jsobj(js);
4375 jsobj.AddProperty("a", "a");
4376 JSONObject jsobj1(js);
4377 jsobj1.AddProperty("a", "a");
4378 JSONObject jsobj2(js);
4379 jsobj2.AddProperty("a", "a");
4380 JSONObject jsobj3(js);
4381 jsobj3.AddProperty("a", "a");
4382 return true;
4383}
4384
4385static bool RespondWithMalformedObject(Thread* thread, JSONStream* js) {
4386 JSONObject jsobj(js);
4387 jsobj.AddProperty("bart", "simpson");
4388 return true;
4389}
4390
4391static const MethodParameter* get_object_params[] = {
4392 RUNNABLE_ISOLATE_PARAMETER, new UIntParameter("offset", false),
4393 new UIntParameter("count", false), NULL,
4394};
4395
4396static bool GetObject(Thread* thread, JSONStream* js) {
4397 const char* id = js->LookupParam("objectId");
4398 if (id == NULL) {
4399 PrintMissingParamError(js, "objectId");
4400 return true;
4401 }
4402 if (js->HasParam("offset")) {
4403 intptr_t value = UIntParameter::Parse(js->LookupParam("offset"));
4404 if (value < 0) {
4405 PrintInvalidParamError(js, "offset");
4406 return true;
4407 }
4408 js->set_offset(value);
4409 }
4410 if (js->HasParam("count")) {
4411 intptr_t value = UIntParameter::Parse(js->LookupParam("count"));
4412 if (value < 0) {
4413 PrintInvalidParamError(js, "count");
4414 return true;
4415 }
4416 js->set_count(value);
4417 }
4418
4419 // Handle heap objects.
4420 ObjectIdRing::LookupResult lookup_result;
4421 Object& obj = Object::Handle(LookupHeapObject(thread, id, &lookup_result));
4422 if (obj.raw() != Object::sentinel().raw()) {
4423#if !defined(DART_PRECOMPILED_RUNTIME)
4424 // If obj is a script from dart:* and doesn't have source loaded, try and
4425 // load the source before sending the response.
4426 if (obj.IsScript()) {
4427 const Script& script = Script::Cast(obj);
4428 script.LookupSourceAndLineStarts(thread->zone());
4429 if (!script.HasSource() && script.IsPartOfDartColonLibrary() &&
4430 Service::HasDartLibraryKernelForSources()) {
4431 const uint8_t* kernel_buffer = Service::dart_library_kernel();
4432 const intptr_t kernel_buffer_len =
4433 Service::dart_library_kernel_length();
4434 script.LoadSourceFromKernel(kernel_buffer, kernel_buffer_len);
4435 }
4436 }
4437#endif // !defined(DART_PRECOMPILED_RUNTIME)
4438 // We found a heap object for this id. Return it.
4439 obj.PrintJSON(js, false);
4440 return true;
4441 } else if (lookup_result == ObjectIdRing::kCollected) {
4442 PrintSentinel(js, kCollectedSentinel);
4443 return true;
4444 } else if (lookup_result == ObjectIdRing::kExpired) {
4445 PrintSentinel(js, kExpiredSentinel);
4446 return true;
4447 }
4448
4449 // Handle non-heap objects.
4450 Breakpoint* bpt = LookupBreakpoint(thread->isolate(), id, &lookup_result);
4451 if (bpt != NULL) {
4452 bpt->PrintJSON(js);
4453 return true;
4454 } else if (lookup_result == ObjectIdRing::kCollected) {
4455 PrintSentinel(js, kCollectedSentinel);
4456 return true;
4457 }
4458
4459 PrintInvalidParamError(js, "objectId");
4460 return true;
4461}
4462
4463static const MethodParameter* get_object_store_params[] = {
4464 RUNNABLE_ISOLATE_PARAMETER, NULL,
4465};
4466
4467static bool GetObjectStore(Thread* thread, JSONStream* js) {
4468 JSONObject jsobj(js);
4469 thread->isolate()->object_store()->PrintToJSONObject(&jsobj);
4470 return true;
4471}
4472
4473static const MethodParameter* get_isolate_object_store_params[] = {
4474 RUNNABLE_ISOLATE_PARAMETER,
4475 NULL,
4476};
4477
4478static bool GetIsolateObjectStore(Thread* thread, JSONStream* js) {
4479 JSONObject jsobj(js);
4480 thread->isolate()->isolate_object_store()->PrintToJSONObject(&jsobj);
4481 return true;
4482}
4483
4484static const MethodParameter* get_class_list_params[] = {
4485 RUNNABLE_ISOLATE_PARAMETER, NULL,
4486};
4487
4488static bool GetClassList(Thread* thread, JSONStream* js) {
4489 ClassTable* table = thread->isolate()->class_table();
4490 JSONObject jsobj(js);
4491 table->PrintToJSONObject(&jsobj);
4492 return true;
4493}
4494
4495static const MethodParameter* get_type_arguments_list_params[] = {
4496 RUNNABLE_ISOLATE_PARAMETER, NULL,
4497};
4498
4499static bool GetTypeArgumentsList(Thread* thread, JSONStream* js) {
4500 bool only_with_instantiations = false;
4501 if (js->ParamIs("onlyWithInstantiations", "true")) {
4502 only_with_instantiations = true;
4503 }
4504 Zone* zone = thread->zone();
4505 ObjectStore* object_store = thread->isolate()->object_store();
4506 CanonicalTypeArgumentsSet typeargs_table(
4507 zone, object_store->canonical_type_arguments());
4508 const intptr_t table_size = typeargs_table.NumEntries();
4509 const intptr_t table_used = typeargs_table.NumOccupied();
4510 const Array& typeargs_array =
4511 Array::Handle(zone, HashTables::ToArray(typeargs_table, false));
4512 ASSERT(typeargs_array.Length() == table_used);
4513 TypeArguments& typeargs = TypeArguments::Handle(zone);
4514 JSONObject jsobj(js);
4515 jsobj.AddProperty("type", "TypeArgumentsList");
4516 jsobj.AddProperty("canonicalTypeArgumentsTableSize", table_size);
4517 jsobj.AddProperty("canonicalTypeArgumentsTableUsed", table_used);
4518 JSONArray members(&jsobj, "typeArguments");
4519 for (intptr_t i = 0; i < table_used; i++) {
4520 typeargs ^= typeargs_array.At(i);
4521 if (!typeargs.IsNull()) {
4522 if (!only_with_instantiations || typeargs.HasInstantiations()) {
4523 members.AddValue(typeargs);
4524 }
4525 }
4526 }
4527 typeargs_table.Release();
4528 return true;
4529}
4530
4531static const MethodParameter* get_version_params[] = {
4532 NO_ISOLATE_PARAMETER, NULL,
4533};
4534
4535static bool GetVersion(Thread* thread, JSONStream* js) {
4536 JSONObject jsobj(js);
4537 jsobj.AddProperty("type", "Version");
4538 jsobj.AddProperty("major",
4539 static_cast<intptr_t>(SERVICE_PROTOCOL_MAJOR_VERSION));
4540 jsobj.AddProperty("minor",
4541 static_cast<intptr_t>(SERVICE_PROTOCOL_MINOR_VERSION));
4542 jsobj.AddProperty("_privateMajor", static_cast<intptr_t>(0));
4543 jsobj.AddProperty("_privateMinor", static_cast<intptr_t>(0));
4544 return true;
4545}
4546
4547class ServiceIsolateVisitor : public IsolateVisitor {
4548 public:
4549 explicit ServiceIsolateVisitor(JSONArray* jsarr) : jsarr_(jsarr) {}
4550 virtual ~ServiceIsolateVisitor() {}
4551
4552 void VisitIsolate(Isolate* isolate) {
4553 if (FLAG_show_invisible_isolates || !IsVMInternalIsolate(isolate)) {
4554 jsarr_->AddValue(isolate);
4555 }
4556 }
4557
4558 private:
4559 JSONArray* jsarr_;
4560};
4561
4562static const MethodParameter* get_vm_params[] = {
4563 NO_ISOLATE_PARAMETER, NULL,
4564};
4565
4566void Service::PrintJSONForEmbedderInformation(JSONObject *jsobj) {
4567 if (embedder_information_callback_ != NULL) {
4568 Dart_EmbedderInformation info = {
4569 0, // version
4570 NULL, // name
4571 -1, // max_rss
4572 -1 // current_rss
4573 };
4574 embedder_information_callback_(&info);
4575 ASSERT(info.version == DART_EMBEDDER_INFORMATION_CURRENT_VERSION);
4576 if (info.name != NULL) {
4577 jsobj->AddProperty("_embedder", info.name);
4578 }
4579 if (info.max_rss >= 0) {
4580 jsobj->AddProperty64("_maxRSS", info.max_rss);
4581 }
4582 if (info.current_rss >= 0) {
4583 jsobj->AddProperty64("_currentRSS", info.current_rss);
4584 }
4585 }
4586}
4587
4588void Service::PrintJSONForVM(JSONStream* js, bool ref) {
4589 JSONObject jsobj(js);
4590 jsobj.AddProperty("type", (ref ? "@VM" : "VM"));
4591 jsobj.AddProperty("name", GetVMName());
4592 if (ref) {
4593 return;
4594 }
4595 jsobj.AddProperty("architectureBits", static_cast<intptr_t>(kBitsPerWord));
4596 jsobj.AddProperty("hostCPU", HostCPUFeatures::hardware());
4597 jsobj.AddProperty("operatingSystem", OS::Name());
4598 jsobj.AddProperty("targetCPU", CPU::Id());
4599 jsobj.AddProperty("version", Version::String());
4600 jsobj.AddProperty("_profilerMode", FLAG_profile_vm ? "VM" : "Dart");
4601 jsobj.AddProperty64("_nativeZoneMemoryUsage",
4602 ApiNativeScope::current_memory_usage());
4603 jsobj.AddProperty64("pid", OS::ProcessId());
4604 jsobj.AddPropertyTimeMillis(
4605 "startTime", OS::GetCurrentTimeMillis() - Dart::UptimeMillis());
4606 MallocHooks::PrintToJSONObject(&jsobj);
4607 PrintJSONForEmbedderInformation(&jsobj);
4608 // Construct the isolate and isolate_groups list.
4609 {
4610 JSONArray jsarr(&jsobj, "isolates");
4611 ServiceIsolateVisitor visitor(&jsarr);
4612 Isolate::VisitIsolates(&visitor);
4613 }
4614 {
4615 JSONArray jsarr_isolate_groups(&jsobj, "isolateGroups");
4616 IsolateGroup::ForEach([&jsarr_isolate_groups](IsolateGroup* isolate_group) {
4617 bool has_internal = false;
4618 isolate_group->ForEachIsolate([&has_internal](Isolate* isolate) {
4619 if (Isolate::IsVMInternalIsolate(isolate)) {
4620 has_internal = true;
4621 }
4622 });
4623 if (FLAG_show_invisible_isolates || !has_internal) {
4624 jsarr_isolate_groups.AddValue(isolate_group);
4625 }
4626 });
4627 }
4628 {
4629 JSONStream discard_js;
4630 intptr_t vm_memory = GetProcessMemoryUsageHelper(&discard_js);
4631 jsobj.AddProperty("_currentMemory", vm_memory);
4632 }
4633}
4634
4635static bool GetVM(Thread* thread, JSONStream* js) {
4636 Service::PrintJSONForVM(js, false);
4637 return true;
4638}
4639
4640static const char* exception_pause_mode_names[] = {
4641 "All", "None", "Unhandled", NULL,
4642};
4643
4644static Dart_ExceptionPauseInfo exception_pause_mode_values[] = {
4645 kPauseOnAllExceptions, kNoPauseOnExceptions, kPauseOnUnhandledExceptions,
4646 kInvalidExceptionPauseInfo,
4647};
4648
4649static const MethodParameter* set_exception_pause_mode_params[] = {
4650 ISOLATE_PARAMETER,
4651 new EnumParameter("mode", true, exception_pause_mode_names), NULL,
4652};
4653
4654static bool SetExceptionPauseMode(Thread* thread, JSONStream* js) {
4655 const char* mode = js->LookupParam("mode");
4656 if (mode == NULL) {
4657 PrintMissingParamError(js, "mode");
4658 return true;
4659 }
4660 Dart_ExceptionPauseInfo info =
4661 EnumMapper(mode, exception_pause_mode_names, exception_pause_mode_values);
4662 if (info == kInvalidExceptionPauseInfo) {
4663 PrintInvalidParamError(js, "mode");
4664 return true;
4665 }
4666 Isolate* isolate = thread->isolate();
4667 isolate->debugger()->SetExceptionPauseInfo(info);
4668 if (Service::debug_stream.enabled()) {
4669 ServiceEvent event(isolate, ServiceEvent::kDebuggerSettingsUpdate);
4670 Service::HandleEvent(&event);
4671 }
4672 PrintSuccess(js);
4673 return true;
4674}
4675
4676static const MethodParameter* get_flag_list_params[] = {
4677 NO_ISOLATE_PARAMETER, NULL,
4678};
4679
4680static bool GetFlagList(Thread* thread, JSONStream* js) {
4681 Flags::PrintJSON(js);
4682 return true;
4683}
4684
4685static const MethodParameter* set_flags_params[] = {
4686 NO_ISOLATE_PARAMETER, NULL,
4687};
4688
4689static bool SetFlag(Thread* thread, JSONStream* js) {
4690 const char* flag_name = js->LookupParam("name");
4691 if (flag_name == NULL) {
4692 PrintMissingParamError(js, "name");
4693 return true;
4694 }
4695 const char* flag_value = js->LookupParam("value");
4696 if (flag_value == NULL) {
4697 PrintMissingParamError(js, "value");
4698 return true;
4699 }
4700
4701 if (Flags::Lookup(flag_name) == NULL) {
4702 JSONObject jsobj(js);
4703 jsobj.AddProperty("type", "Error");
4704 jsobj.AddProperty("message", "Cannot set flag: flag not found");
4705 return true;
4706 }
4707
4708 // Changing most flags at runtime is dangerous because e.g., it may leave the
4709 // behavior generated code and the runtime out of sync.
4710 const uintptr_t kProfilePeriodIndex = 3;
4711 const uintptr_t kProfilerIndex = 4;
4712 const char* kAllowedFlags[] = {
4713 "pause_isolates_on_start",
4714 "pause_isolates_on_exit",
4715 "pause_isolates_on_unhandled_exceptions",
4716 "profile_period",
4717 "profiler",
4718 };
4719
4720 bool allowed = false;
4721 bool profile_period = false;
4722 bool profiler = false;
4723 for (size_t i = 0; i < ARRAY_SIZE(kAllowedFlags); i++) {
4724 if (strcmp(flag_name, kAllowedFlags[i]) == 0) {
4725 allowed = true;
4726 profile_period = (i == kProfilePeriodIndex);
4727 profiler = (i == kProfilerIndex);
4728 break;
4729 }
4730 }
4731
4732 if (!allowed) {
4733 JSONObject jsobj(js);
4734 jsobj.AddProperty("type", "Error");
4735 jsobj.AddProperty("message", "Cannot set flag: cannot change at runtime");
4736 return true;
4737 }
4738
4739 const char* error = NULL;
4740 if (Flags::SetFlag(flag_name, flag_value, &error)) {
4741 PrintSuccess(js);
4742 if (profile_period) {
4743 // FLAG_profile_period has already been set to the new value. Now we need
4744 // to notify the ThreadInterrupter to pick up the change.
4745 Profiler::UpdateSamplePeriod();
4746 } else if (profiler) {
4747 // FLAG_profiler has already been set to the new value.
4748 Profiler::UpdateRunningState();
4749 }
4750 if (Service::vm_stream.enabled()) {
4751 ServiceEvent event(NULL, ServiceEvent::kVMFlagUpdate);
4752 event.set_flag_name(flag_name);
4753 event.set_flag_new_value(flag_value);
4754 Service::HandleEvent(&event);
4755 }
4756 return true;
4757 } else {
4758 JSONObject jsobj(js);
4759 jsobj.AddProperty("type", "Error");
4760 jsobj.AddProperty("message", error);
4761 return true;
4762 }
4763}
4764
4765static const MethodParameter* set_library_debuggable_params[] = {
4766 RUNNABLE_ISOLATE_PARAMETER, new IdParameter("libraryId", true),
4767 new BoolParameter("isDebuggable", true), NULL,
4768};
4769
4770static bool SetLibraryDebuggable(Thread* thread, JSONStream* js) {
4771 const char* lib_id = js->LookupParam("libraryId");
4772 ObjectIdRing::LookupResult lookup_result;
4773 Object& obj =
4774 Object::Handle(LookupHeapObject(thread, lib_id, &lookup_result));
4775 const bool is_debuggable =
4776 BoolParameter::Parse(js->LookupParam("isDebuggable"), false);
4777 if (obj.IsLibrary()) {
4778 const Library& lib = Library::Cast(obj);
4779 lib.set_debuggable(is_debuggable);
4780 PrintSuccess(js);
4781 return true;
4782 }
4783 PrintInvalidParamError(js, "libraryId");
4784 return true;
4785}
4786
4787static const MethodParameter* set_name_params[] = {
4788 ISOLATE_PARAMETER, new MethodParameter("name", true), NULL,
4789};
4790
4791static bool SetName(Thread* thread, JSONStream* js) {
4792 Isolate* isolate = thread->isolate();
4793 isolate->set_name(js->LookupParam("name"));
4794 if (Service::isolate_stream.enabled()) {
4795 ServiceEvent event(isolate, ServiceEvent::kIsolateUpdate);
4796 Service::HandleEvent(&event);
4797 }
4798 PrintSuccess(js);
4799 return true;
4800}
4801
4802static const MethodParameter* set_vm_name_params[] = {
4803 NO_ISOLATE_PARAMETER, new MethodParameter("name", true), NULL,
4804};
4805
4806static bool SetVMName(Thread* thread, JSONStream* js) {
4807 const char* name_param = js->LookupParam("name");
4808 free(vm_name);
4809 vm_name = Utils::StrDup(name_param);
4810 if (Service::vm_stream.enabled()) {
4811 ServiceEvent event(NULL, ServiceEvent::kVMUpdate);
4812 Service::HandleEvent(&event);
4813 }
4814 PrintSuccess(js);
4815 return true;
4816}
4817
4818static const MethodParameter* set_trace_class_allocation_params[] = {
4819 RUNNABLE_ISOLATE_PARAMETER, new IdParameter("classId", true),
4820 new BoolParameter("enable", true), NULL,
4821};
4822
4823static bool SetTraceClassAllocation(Thread* thread, JSONStream* js) {
4824 if (CheckCompilerDisabled(thread, js)) {
4825 return true;
4826 }
4827
4828 const char* class_id = js->LookupParam("classId");
4829 const bool enable = BoolParameter::Parse(js->LookupParam("enable"));
4830 intptr_t cid = -1;
4831 GetPrefixedIntegerId(class_id, "classes/", &cid);
4832 Isolate* isolate = thread->isolate();
4833 if (!IsValidClassId(isolate, cid)) {
4834 PrintInvalidParamError(js, "classId");
4835 return true;
4836 }
4837 const Class& cls = Class::Handle(GetClassForId(isolate, cid));
4838 ASSERT(!cls.IsNull());
4839 cls.SetTraceAllocation(enable);
4840 PrintSuccess(js);
4841 return true;
4842}
4843
4844static const MethodParameter* get_default_classes_aliases_params[] = {
4845 NO_ISOLATE_PARAMETER, NULL,
4846};
4847
4848static bool GetDefaultClassesAliases(Thread* thread, JSONStream* js) {
4849 JSONObject jsobj(js);
4850 jsobj.AddProperty("type", "ClassesAliasesMap");
4851
4852 JSONObject map(&jsobj, "map");
4853
4854#define DEFINE_ADD_VALUE_F(id) \
4855 internals.AddValueF("classes/%" Pd, static_cast<intptr_t>(id));
4856#define DEFINE_ADD_VALUE_F_CID(clazz) DEFINE_ADD_VALUE_F(k##clazz##Cid)
4857 {
4858 JSONArray internals(&map, "<VM Internals>");
4859 for (intptr_t id = kClassCid; id < kInstanceCid; ++id) {
4860 DEFINE_ADD_VALUE_F(id);
4861 }
4862 DEFINE_ADD_VALUE_F_CID(LibraryPrefix);
4863 }
4864 {
4865 JSONArray internals(&map, "Type");
4866 for (intptr_t id = kAbstractTypeCid; id <= kTypeParameterCid; ++id) {
4867 DEFINE_ADD_VALUE_F(id);
4868 }
4869 }
4870 {
4871 JSONArray internals(&map, "Object");
4872 DEFINE_ADD_VALUE_F_CID(Instance);
4873 }
4874 {
4875 JSONArray internals(&map, "Closure");
4876 DEFINE_ADD_VALUE_F_CID(Closure);
4877 DEFINE_ADD_VALUE_F_CID(Context);
4878 }
4879 {
4880 JSONArray internals(&map, "Int");
4881 for (intptr_t id = kIntegerCid; id <= kMintCid; ++id) {
4882 DEFINE_ADD_VALUE_F(id);
4883 }
4884 }
4885 {
4886 JSONArray internals(&map, "Double");
4887 DEFINE_ADD_VALUE_F_CID(Double);
4888 }
4889 {
4890 JSONArray internals(&map, "String");
4891 CLASS_LIST_STRINGS(DEFINE_ADD_VALUE_F_CID)
4892 }
4893 {
4894 JSONArray internals(&map, "List");
4895 CLASS_LIST_ARRAYS(DEFINE_ADD_VALUE_F_CID)
4896 DEFINE_ADD_VALUE_F_CID(GrowableObjectArray)
4897 DEFINE_ADD_VALUE_F_CID(ByteBuffer)
4898 }
4899 {
4900 JSONArray internals(&map, "Map");
4901 DEFINE_ADD_VALUE_F_CID(LinkedHashMap)
4902 }
4903#define DEFINE_ADD_MAP_KEY(clazz) \
4904 { \
4905 JSONArray internals(&map, #clazz); \
4906 DEFINE_ADD_VALUE_F_CID(TypedData##clazz) \
4907 DEFINE_ADD_VALUE_F_CID(TypedData##clazz) \
4908 DEFINE_ADD_VALUE_F_CID(ExternalTypedData##clazz) \
4909 }
4910 CLASS_LIST_TYPED_DATA(DEFINE_ADD_MAP_KEY)
4911#undef DEFINE_ADD_MAP_KEY
4912#define DEFINE_ADD_MAP_KEY(clazz) \
4913 { \
4914 JSONArray internals(&map, #clazz); \
4915 DEFINE_ADD_VALUE_F_CID(Ffi##clazz) \
4916 }
4917 CLASS_LIST_FFI(DEFINE_ADD_MAP_KEY)
4918#undef DEFINE_ADD_MAP_KEY
4919#undef DEFINE_ADD_VALUE_F_CID
4920#undef DEFINE_ADD_VALUE_F
4921
4922 return true;
4923}
4924
4925// clang-format off
4926static const ServiceMethodDescriptor service_methods_[] = {
4927 { "_echo", Echo,
4928 NULL },
4929 { "_respondWithMalformedJson", RespondWithMalformedJson,
4930 NULL },
4931 { "_respondWithMalformedObject", RespondWithMalformedObject,
4932 NULL },
4933 { "_triggerEchoEvent", TriggerEchoEvent,
4934 NULL },
4935 { "addBreakpoint", AddBreakpoint,
4936 add_breakpoint_params },
4937 { "addBreakpointWithScriptUri", AddBreakpointWithScriptUri,
4938 add_breakpoint_with_script_uri_params },
4939 { "addBreakpointAtEntry", AddBreakpointAtEntry,
4940 add_breakpoint_at_entry_params },
4941 { "_addBreakpointAtActivation", AddBreakpointAtActivation,
4942 add_breakpoint_at_activation_params },
4943 { "_buildExpressionEvaluationScope", BuildExpressionEvaluationScope,
4944 build_expression_evaluation_scope_params },
4945 { "clearCpuSamples", ClearCpuSamples,
4946 clear_cpu_samples_params },
4947 { "clearVMTimeline", ClearVMTimeline,
4948 clear_vm_timeline_params, },
4949 { "_compileExpression", CompileExpression, compile_expression_params },
4950 { "_enableProfiler", EnableProfiler,
4951 enable_profiler_params, },
4952 { "evaluate", Evaluate,
4953 evaluate_params },
4954 { "evaluateInFrame", EvaluateInFrame,
4955 evaluate_in_frame_params },
4956 { "_getAllocationProfile", GetAllocationProfile,
4957 get_allocation_profile_params },
4958 { "getAllocationProfile", GetAllocationProfilePublic,
4959 get_allocation_profile_params },
4960 { "_getAllocationSamples", GetAllocationSamples,
4961 get_allocation_samples_params },
4962 { "_getNativeAllocationSamples", GetNativeAllocationSamples,
4963 get_native_allocation_samples_params },
4964 { "getClassList", GetClassList,
4965 get_class_list_params },
4966 { "getCpuSamples", GetCpuSamples,
4967 get_cpu_samples_params },
4968 { "getFlagList", GetFlagList,
4969 get_flag_list_params },
4970 { "_getHeapMap", GetHeapMap,
4971 get_heap_map_params },
4972 { "getInboundReferences", GetInboundReferences,
4973 get_inbound_references_params },
4974 { "getInstances", GetInstances,
4975 get_instances_params },
4976 { "getIsolate", GetIsolate,
4977 get_isolate_params },
4978 { "_getIsolateObjectStore", GetIsolateObjectStore,
4979 get_isolate_object_store_params },
4980 { "getIsolateGroup", GetIsolateGroup,
4981 get_isolate_group_params },
4982 { "getMemoryUsage", GetMemoryUsage,
4983 get_memory_usage_params },
4984 { "getIsolateGroupMemoryUsage", GetIsolateGroupMemoryUsage,
4985 get_isolate_group_memory_usage_params },
4986 { "_getIsolateMetric", GetIsolateMetric,
4987 get_isolate_metric_params },
4988 { "_getIsolateMetricList", GetIsolateMetricList,
4989 get_isolate_metric_list_params },
4990 { "getObject", GetObject,
4991 get_object_params },
4992 { "_getObjectStore", GetObjectStore,
4993 get_object_store_params },
4994 { "_getPersistentHandles", GetPersistentHandles,
4995 get_persistent_handles_params, },
4996 { "_getPorts", GetPorts,
4997 get_ports_params },
4998 { "getProcessMemoryUsage", GetProcessMemoryUsage,
4999 get_process_memory_usage_params },
5000 { "_getReachableSize", GetReachableSize,
5001 get_reachable_size_params },
5002 { "_getRetainedSize", GetRetainedSize,
5003 get_retained_size_params },
5004 { "getRetainingPath", GetRetainingPath,
5005 get_retaining_path_params },
5006 { "getScripts", GetScripts,
5007 get_scripts_params },
5008 { "getSourceReport", GetSourceReport,
5009 get_source_report_params },
5010 { "getStack", GetStack,
5011 get_stack_params },
5012 { "_getTagProfile", GetTagProfile,
5013 get_tag_profile_params },
5014 { "_getTypeArgumentsList", GetTypeArgumentsList,
5015 get_type_arguments_list_params },
5016 { "getVersion", GetVersion,
5017 get_version_params },
5018 { "getVM", GetVM,
5019 get_vm_params },
5020 { "_getVMMetric", GetVMMetric,
5021 get_vm_metric_params },
5022 { "_getVMMetricList", GetVMMetricList,
5023 get_vm_metric_list_params },
5024 { "getVMTimeline", GetVMTimeline,
5025 get_vm_timeline_params },
5026 { "getVMTimelineFlags", GetVMTimelineFlags,
5027 get_vm_timeline_flags_params },
5028 { "getVMTimelineMicros", GetVMTimelineMicros,
5029 get_vm_timeline_micros_params },
5030 { "invoke", Invoke, invoke_params },
5031 { "kill", Kill, kill_params },
5032 { "pause", Pause,
5033 pause_params },
5034 { "removeBreakpoint", RemoveBreakpoint,
5035 remove_breakpoint_params },
5036 { "reloadSources", ReloadSources,
5037 reload_sources_params },
5038 { "_reloadSources", ReloadSources,
5039 reload_sources_params },
5040 { "resume", Resume,
5041 resume_params },
5042 { "requestHeapSnapshot", RequestHeapSnapshot,
5043 request_heap_snapshot_params },
5044 { "_evaluateCompiledExpression", EvaluateCompiledExpression,
5045 evaluate_compiled_expression_params },
5046 { "setExceptionPauseMode", SetExceptionPauseMode,
5047 set_exception_pause_mode_params },
5048 { "setFlag", SetFlag,
5049 set_flags_params },
5050 { "setLibraryDebuggable", SetLibraryDebuggable,
5051 set_library_debuggable_params },
5052 { "setName", SetName,
5053 set_name_params },
5054 { "_setTraceClassAllocation", SetTraceClassAllocation,
5055 set_trace_class_allocation_params },
5056 { "setVMName", SetVMName,
5057 set_vm_name_params },
5058 { "setVMTimelineFlags", SetVMTimelineFlags,
5059 set_vm_timeline_flags_params },
5060 { "_collectAllGarbage", CollectAllGarbage,
5061 collect_all_garbage_params },
5062 { "_getDefaultClassesAliases", GetDefaultClassesAliases,
5063 get_default_classes_aliases_params },
5064};
5065// clang-format on
5066
5067const ServiceMethodDescriptor* FindMethod(const char* method_name) {
5068 intptr_t num_methods = sizeof(service_methods_) / sizeof(service_methods_[0]);
5069 for (intptr_t i = 0; i < num_methods; i++) {
5070 const ServiceMethodDescriptor& method = service_methods_[i];
5071 if (strcmp(method_name, method.name) == 0) {
5072 return &method;
5073 }
5074 }
5075 return NULL;
5076}
5077
5078#endif // !PRODUCT
5079
5080} // namespace dart
5081