Flutter Engine
The Flutter Engine
debugger.cc
Go to the documentation of this file.
1// Copyright (c) 2012, 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/debugger.h"
6
7#include "include/dart_api.h"
8
10#include "vm/code_descriptors.h"
11#include "vm/code_patcher.h"
15#include "vm/dart_entry.h"
16#include "vm/flags.h"
17#include "vm/globals.h"
18#include "vm/isolate_reload.h"
19#include "vm/json_stream.h"
20#include "vm/kernel.h"
21#include "vm/longjump.h"
22#include "vm/message_handler.h"
23#include "vm/object.h"
24#include "vm/object_store.h"
25#include "vm/os.h"
26#include "vm/parser.h"
27#include "vm/port.h"
28#include "vm/runtime_entry.h"
29#include "vm/service.h"
30#include "vm/service_event.h"
31#include "vm/service_isolate.h"
32#include "vm/stack_frame.h"
33#include "vm/stack_trace.h"
34#include "vm/stub_code.h"
35#include "vm/symbols.h"
37#include "vm/timeline.h"
38#include "vm/token_position.h"
39#include "vm/visitor.h"
40
41#if !defined(DART_PRECOMPILED_RUNTIME)
43#endif // !defined(DART_PRECOMPILED_RUNTIME)
44
45namespace dart {
46
48 trace_debugger_stacktrace,
49 false,
50 "Trace debugger stacktrace collection");
51DEFINE_FLAG(bool, trace_rewind, false, "Trace frame rewind");
52DEFINE_FLAG(bool, verbose_debug, false, "Verbose debugger messages");
53
54DECLARE_FLAG(bool, trace_deoptimization);
55DECLARE_FLAG(bool, warn_on_pause_with_no_debugger);
56
57#ifndef PRODUCT
58
59// Create an unresolved breakpoint in given token range and script.
61 Debugger* debugger,
63 TokenPosition token_pos,
64 TokenPosition end_token_pos,
65 intptr_t requested_line_number,
66 intptr_t requested_column_number)
67 : debugger_(debugger),
68 scripts_(MallocGrowableArray<ScriptPtr>(scripts.length())),
69 url_(scripts.At(0).url()),
70 line_number_lock_(new SafepointRwLock()),
71 line_number_(-1), // lazily computed
72 token_pos_(token_pos),
73 end_token_pos_(end_token_pos),
74 next_(nullptr),
75 conditions_(nullptr),
76 requested_line_number_(requested_line_number),
77 requested_column_number_(requested_column_number),
78 code_token_pos_(TokenPosition::kNoSource) {
79 ASSERT(scripts.length() > 0);
81 for (intptr_t i = 0; i < scripts.length(); ++i) {
82 scripts_.Add(scripts.At(i).ptr());
83 }
84}
85
86// Create a latent breakpoint at given url and line number.
88 const String& url,
89 intptr_t requested_line_number,
90 intptr_t requested_column_number)
91 : debugger_(debugger),
92 scripts_(MallocGrowableArray<ScriptPtr>(0)),
93 url_(url.ptr()),
94 line_number_lock_(new SafepointRwLock()),
95 line_number_(-1), // lazily computed
96 token_pos_(TokenPosition::kNoSource),
97 end_token_pos_(TokenPosition::kNoSource),
98 next_(nullptr),
99 conditions_(nullptr),
100 requested_line_number_(requested_line_number),
101 requested_column_number_(requested_column_number),
102 code_token_pos_(TokenPosition::kNoSource) {
103 ASSERT(requested_line_number_ >= 0);
104}
105
107 Breakpoint* bpt = breakpoints();
108 while (bpt != nullptr) {
109 Breakpoint* temp = bpt;
110 bpt = bpt->next();
111 delete temp;
112 }
113}
114
116 Breakpoint* bpt = breakpoints();
117 while (bpt != nullptr) {
118 if (bpt->is_enabled()) {
119 return true;
120 }
121 bpt = bpt->next();
122 }
123 return false;
124}
125
126void BreakpointLocation::SetResolved(const Function& func,
127 TokenPosition token_pos) {
128#if defined(DEBUG)
129 const Script& func_script = Script::Handle(func.script());
130 const String& func_url = String::Handle(func_script.url());
131 const String& script_url = String::Handle(url_);
132 ASSERT(script_url.Equals(func_url));
133#endif // defined(DEBUG)
134 ASSERT(!IsLatent());
136 ASSERT(func.is_debuggable());
137 token_pos_.store(token_pos);
138 end_token_pos_.store(token_pos);
139 code_token_pos_ = token_pos;
140}
141
143 TokenPosition* pos) const {
144 if (IsLatent()) {
145 *script = Script::null();
146 *pos = TokenPosition::kNoSource;
147 } else {
148 *script = this->script();
149 *pos = token_pos_;
150 }
151}
152
154 // Compute line number lazily since it causes scanning of the script.
155 {
156 SafepointReadRwLocker sl(Thread::Current(), line_number_lock());
157 if (line_number_ >= 0) {
158 return line_number_;
159 }
160 }
161 SafepointWriteRwLocker sl(Thread::Current(), line_number_lock());
162 if (line_number_ < 0) {
163 Script::Handle(script()).GetTokenLocation(token_pos(), &line_number_);
164 }
165 return line_number_;
166}
167
169 // Only latent breakpoints can be moved.
170 ASSERT((new_bpt_location == nullptr) || bpt_location_->IsLatent());
171 bpt_location_ = new_bpt_location;
172}
173
174void Breakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
175 visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&closure_));
176}
177
178void BreakpointLocation::VisitObjectPointers(ObjectPointerVisitor* visitor) {
179 for (intptr_t i = 0; i < scripts_.length(); ++i) {
180 visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&scripts_.data()[i]));
181 }
182 visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&url_));
183
184 Breakpoint* bpt = conditions_;
185 while (bpt != nullptr) {
186 bpt->VisitObjectPointers(visitor);
187 bpt = bpt->next();
188 }
189}
190
192 JSONObject jsobj(stream);
193 jsobj.AddProperty("type", "Breakpoint");
194
195 jsobj.AddFixedServiceId("breakpoints/%" Pd "", id());
196 jsobj.AddProperty("enabled", enabled_);
197 jsobj.AddProperty("breakpointNumber", id());
198 jsobj.AddProperty("resolved", bpt_location_->IsResolved());
199 if (bpt_location_->IsResolved()) {
200 jsobj.AddLocation(bpt_location_);
201 } else {
202 jsobj.AddUnresolvedLocation(bpt_location_);
203 }
204}
205
206void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
207 visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&code_));
208 visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&saved_value_));
209}
210
211const char* CodeBreakpoint::ToCString() const {
212 if (breakpoint_locations_.length() == 0) {
213 return "unlinked breakpoint";
214 }
215
216 char buffer[1024];
217 BufferFormatter f(buffer, sizeof(buffer));
218 // Pick the first, all other should have same script/line number.
219 BreakpointLocation* breakpoint_location = breakpoint_locations_.At(0);
220 String& source_url = String::Handle(breakpoint_location->url());
221 intptr_t line_number = breakpoint_location->line_number();
222
223 f.Printf("breakpoint at %s:%" Pd, source_url.ToCString(), line_number);
225}
226
228 uword fp,
229 uword sp,
230 const Code& code,
231 const Array& deopt_frame,
232 intptr_t deopt_frame_offset)
233 : pc_(pc),
234 fp_(fp),
235 sp_(sp),
236 code_(Code::ZoneHandle(code.ptr())),
237 function_(Function::ZoneHandle(code.function())),
238 closure_(Closure::null_closure()),
239 deopt_frame_(Array::ZoneHandle(deopt_frame.ptr())),
240 deopt_frame_offset_(deopt_frame_offset),
241 kind_(kRegular),
242 desc_indices_(8),
243 pc_desc_(PcDescriptors::ZoneHandle()) {
244 ASSERT(!function_.IsNull());
245}
246
248 const Code& code,
249 const Closure& closure)
250 : pc_(pc),
251 code_(Code::ZoneHandle(code.ptr())),
252 function_(Function::ZoneHandle(code.function())),
253 closure_(Closure::ZoneHandle(closure.ptr())),
254 deopt_frame_(Array::empty_array()),
255 deopt_frame_offset_(0),
256 kind_(kAsyncAwaiter) {}
257
259 : code_(Code::ZoneHandle()),
260 function_(Function::null_function()),
261 closure_(Closure::null_closure()),
262 deopt_frame_(Array::empty_array()),
263 deopt_frame_offset_(0),
264 kind_(kind) {
266}
267
268bool Debugger::NeedsIsolateEvents() {
269 ASSERT(isolate_ == Isolate::Current());
270 return !Isolate::IsSystemIsolate(isolate_) &&
272}
273
274bool Debugger::NeedsDebugEvents() {
275 if (Isolate::Current() == nullptr) {
276 // E.g., NoActiveIsolateScope.
277 return false;
278 }
279 RELEASE_ASSERT(isolate_ == Isolate::Current());
281 return FLAG_warn_on_pause_with_no_debugger || Service::debug_stream.enabled();
282}
283
285 ASSERT(!event->IsPause()); // For pause events, call Pause instead.
286 Service::HandleEvent(event, /*enter_safepoint*/ false);
287}
288
290 return PauseRequest(ServiceEvent::kPauseInterrupted);
291}
292
294 return PauseRequest(ServiceEvent::kPausePostRequest);
295}
296
297ErrorPtr Debugger::PauseRequest(ServiceEvent::EventKind kind) {
298 if (ignore_breakpoints_ || IsPaused()) {
299 // We don't let the isolate get interrupted if we are already
300 // paused or ignoring breakpoints.
302 }
303 ServiceEvent event(isolate_, kind);
304 DebuggerStackTrace* trace = DebuggerStackTrace::Collect();
305 if (trace->Length() > 0) {
306 event.set_top_frame(trace->FrameAt(0));
307 }
308 CacheStackTraces(trace, DebuggerStackTrace::CollectAsyncAwaiters());
309 set_resume_action(kContinue);
310 Pause(&event);
311 HandleSteppingRequest();
312 ClearCachedStackTraces();
313
314 // If any error occurred while in the debug message loop, return it here.
315 NoSafepointScope no_safepoint;
316 ErrorPtr error = Thread::Current()->StealStickyError();
317 ASSERT((error == Error::null()) || error->IsUnwindError());
318 return error;
319}
320
321void Debugger::SendBreakpointEvent(ServiceEvent::EventKind kind,
322 Breakpoint* bpt) {
323 if (NeedsDebugEvents()) {
324 // TODO(turnidge): Currently we send single-shot breakpoint events
325 // to the vm service. Do we want to change this?
326 ServiceEvent event(isolate_, kind);
327 event.set_breakpoint(bpt);
329 }
330}
331
332void BreakpointLocation::AddBreakpoint(Breakpoint* bpt, Debugger* dbg) {
333 bpt->set_next(breakpoints());
334 set_breakpoints(bpt);
335 bpt->Enable();
336 dbg->group_debugger()->SyncBreakpointLocation(this);
337 dbg->SendBreakpointEvent(ServiceEvent::kBreakpointAdded, bpt);
338}
339
341 return AddBreakpoint(dbg, Closure::Handle(), /*single_shot=*/false);
342}
343
345 return AddBreakpoint(dbg, Closure::Handle(), /*single_shot=*/true);
346}
347
349 const Closure& closure,
350 bool single_shot) {
351 Breakpoint* bpt = breakpoints();
352 while (bpt != nullptr) {
353 if ((bpt->closure() == closure.ptr()) &&
354 (bpt->is_single_shot() == single_shot)) {
355 break;
356 }
357 bpt = bpt->next();
358 }
359 if (bpt == nullptr) {
360 bpt = new Breakpoint(dbg->nextId(), this, single_shot, closure);
361 AddBreakpoint(bpt, dbg);
362 }
363 return bpt;
364}
365
366static const char* QualifiedFunctionName(const Function& func) {
367 const String& func_name = String::Handle(func.name());
368 Class& func_class = Class::Handle(func.Owner());
369 String& class_name = String::Handle(func_class.Name());
370
371 return OS::SCreate(Thread::Current()->zone(), "%s%s%s",
372 func_class.IsTopLevel() ? "" : class_name.ToCString(),
373 func_class.IsTopLevel() ? "" : ".", func_name.ToCString());
374}
375
376// Returns true if the function |func| overlaps the token range
377// [|token_pos|, |end_token_pos|] in |script|.
378static bool FunctionOverlaps(const Function& func,
379 const String& script_url,
380 TokenPosition token_pos,
381 TokenPosition end_token_pos) {
382 const TokenPosition& func_start = func.token_pos();
383 if (token_pos.IsWithin(func_start, func.end_token_pos()) ||
384 func_start.IsWithin(token_pos, end_token_pos)) {
385 // Check script equality last because it allocates handles as a side effect.
386 Script& func_script = Script::Handle(func.script());
387 String& url = String::Handle(func_script.url());
388 return script_url.Equals(url);
389 }
390 return false;
391}
392
393static bool IsImplicitFunction(const Function& func) {
394 switch (func.kind()) {
395 case UntaggedFunction::kImplicitGetter:
396 case UntaggedFunction::kImplicitSetter:
397 case UntaggedFunction::kImplicitStaticGetter:
398 case UntaggedFunction::kFieldInitializer:
399 case UntaggedFunction::kMethodExtractor:
400 case UntaggedFunction::kNoSuchMethodDispatcher:
401 case UntaggedFunction::kInvokeFieldDispatcher:
402 case UntaggedFunction::kIrregexpFunction:
403 case UntaggedFunction::kRecordFieldGetter:
404 return true;
405 default:
406 if (func.token_pos() == func.end_token_pos()) {
407 // |func| could be an implicit constructor for example.
408 return true;
409 }
410 }
411 return false;
412}
413
415 DEBUG_ASSERT(code_breakpoints_lock()->IsCurrentThreadReader() ||
416 Thread::Current()->IsInStoppedMutatorsScope());
417 CodeBreakpoint* cbpt = code_breakpoints_;
418 while (cbpt != nullptr) {
419 if (func.ptr() == cbpt->function()) {
420 return true;
421 }
422 cbpt = cbpt->next_;
423 }
424 return false;
425}
426
428 auto thread = Thread::Current();
429 // Don't need to worry about the lock if mutators are stopped.
430 if (thread->IsInStoppedMutatorsScope()) {
432 } else {
435 }
436}
437
439 auto thread = Thread::Current();
441 CodeBreakpoint* cbpt = code_breakpoints_;
442 while (cbpt != nullptr) {
443 if (code.ptr() == cbpt->code_) {
444 return true;
445 }
446 cbpt = cbpt->next_;
447 }
448 return false;
449}
450
452 PrintBreakpointsListToJSONArray(breakpoint_locations_, jsarr);
453 PrintBreakpointsListToJSONArray(latent_locations_, jsarr);
454}
455
456void Debugger::PrintBreakpointsListToJSONArray(BreakpointLocation* sbpt,
457 JSONArray* jsarr) const {
458 while (sbpt != nullptr) {
459 Breakpoint* bpt = sbpt->breakpoints();
460 while (bpt != nullptr) {
461 jsarr->AddValue(bpt);
462 bpt = bpt->next();
463 }
464 sbpt = sbpt->next_;
465 }
466}
467
469 // This won't cut it when we support filtering by class, etc.
470 switch (GetExceptionPauseInfo()) {
472 jsobj->AddProperty("_exceptions", "none");
473 break;
475 jsobj->AddProperty("_exceptions", "all");
476 break;
478 jsobj->AddProperty("_exceptions", "unhandled");
479 break;
480 default:
481 UNREACHABLE();
482 }
483}
484
486 if (fp() == other_fp) {
487 return kSelf;
488 }
489 return IsCalleeFrameOf(other_fp, fp()) ? kCallee : kCaller;
490}
491
494}
495
498 return script.url();
499}
500
502 return function().script();
503}
504
506 const Class& cls = Class::Handle(function().Owner());
507 return cls.library();
508}
509
510void ActivationFrame::GetPcDescriptors() {
511 if (pc_desc_.IsNull()) {
512 pc_desc_ = code().pc_descriptors();
513 ASSERT(!pc_desc_.IsNull());
514 }
515}
516
517// If not token_pos_initialized_, compute token_pos_, try_index_ and
518// deopt_id_.
520 if (!token_pos_initialized_) {
521 token_pos_initialized_ = true;
522 token_pos_ = TokenPosition::kNoSource;
523 GetPcDescriptors();
524 PcDescriptors::Iterator iter(pc_desc_, UntaggedPcDescriptors::kAnyKind);
525 const uword pc_offset = pc_ - code().PayloadStart();
526 while (iter.MoveNext()) {
527 if (iter.PcOffset() == pc_offset) {
528 try_index_ = iter.TryIndex();
529 token_pos_ = iter.TokenPos();
530 deopt_id_ = iter.DeoptId();
531 break;
532 }
533 }
534 }
535 return token_pos_;
536}
537
538intptr_t ActivationFrame::TryIndex() {
539 if (!token_pos_initialized_) {
540 TokenPos(); // Side effect: computes token_pos_initialized_, try_index_.
541 }
542 return try_index_;
543}
544
545intptr_t ActivationFrame::DeoptId() {
546 if (!token_pos_initialized_) {
547 TokenPos(); // Side effect: computes token_pos_initialized_, try_index_.
548 }
549 return deopt_id_;
550}
551
553 // Compute line number lazily since it causes scanning of the script.
554 const TokenPosition& token_pos = TokenPos();
555 if ((line_number_ < 0) && token_pos.IsReal()) {
557 script.GetTokenLocation(token_pos, &line_number_, &column_number_);
558 }
559 return line_number_;
560}
561
563 // Compute column number lazily since it causes scanning of the script.
564 const TokenPosition& token_pos = TokenPos();
565 if ((column_number_ < 0) && token_pos.IsReal()) {
567 script.GetTokenLocation(token_pos, &line_number_, &column_number_);
568 }
569 return column_number_;
570}
571
572void ActivationFrame::GetVarDescriptors() {
573 if (var_descriptors_.IsNull()) {
574 Code& unoptimized_code = Code::Handle(function().unoptimized_code());
575 if (unoptimized_code.IsNull()) {
576 if (function().ForceOptimize()) {
577 var_descriptors_ = Object::empty_var_descriptors().ptr();
578 return;
579 }
580 Thread* thread = Thread::Current();
581 Zone* zone = thread->zone();
582 const Error& error = Error::Handle(
584 if (!error.IsNull()) {
586 }
587 unoptimized_code = function().unoptimized_code();
588 }
589 ASSERT(!unoptimized_code.IsNull());
590 var_descriptors_ = unoptimized_code.GetLocalVarDescriptors();
591 ASSERT(!var_descriptors_.IsNull());
592 }
593}
594
596 ASSERT(!function().IsNull());
598}
599
600void ActivationFrame::PrintDescriptorsError(const char* message) {
601 OS::PrintErr("Bad descriptors: %s\n", message);
602 OS::PrintErr("function %s\n", function().ToQualifiedCString());
603 OS::PrintErr("pc_ %" Px "\n", pc_);
604 OS::PrintErr("deopt_id_ %" Px "\n", deopt_id_);
605 OS::PrintErr("context_level_ %" Px "\n", context_level_);
606 OS::PrintErr("token_pos_ %s\n", token_pos_.ToCString());
607 {
608 DisassembleToStdout formatter;
609 code().Disassemble(&formatter);
610 PcDescriptors::Handle(code().pc_descriptors()).Print();
611 }
612 StackFrameIterator frames(ValidationPolicy::kDontValidateFrames,
615 StackFrame* frame = frames.NextFrame();
616 while (frame != nullptr) {
617 OS::PrintErr("%s\n", frame->ToCString());
618 frame = frames.NextFrame();
619 }
620 OS::Abort();
621}
622
623// Calculate the context level at the current pc of the frame.
625 ASSERT(kind_ == kRegular);
626 const Context& ctx = GetSavedCurrentContext();
627 if (context_level_ < 0 && !ctx.IsNull()) {
628 ASSERT(!code_.is_optimized());
629 GetVarDescriptors();
630 intptr_t deopt_id = DeoptId();
631 if (deopt_id == DeoptId::kNone) {
632 PrintDescriptorsError("Missing deopt id");
633 }
634 intptr_t var_desc_len = var_descriptors_.Length();
635 bool found = false;
636 // We store the deopt ids as real token positions.
637 const auto to_compare = TokenPosition::Deserialize(deopt_id);
638 for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
640 var_descriptors_.GetInfo(cur_idx, &var_info);
641 const int8_t kind = var_info.kind();
643 to_compare.IsWithin(var_info.begin_pos, var_info.end_pos)) {
644 context_level_ = var_info.index();
645 found = true;
646 break;
647 }
648 }
649 if (!found) {
650 PrintDescriptorsError("Missing context level in var descriptors");
651 }
652 ASSERT(context_level_ >= 0);
653 }
654 return context_level_;
655}
656
658 if (kind_ == kAsyncSuspensionMarker) {
659 return false;
660 }
661 intptr_t try_index = TryIndex();
662 const auto& handlers = ExceptionHandlers::Handle(code().exception_handlers());
663 ASSERT(!handlers.IsNull());
664 if ((try_index < 0) && !handlers.has_async_handler()) {
665 return false;
666 }
667 Array& handled_types = Array::Handle();
669 intptr_t num_handlers_checked = 0;
670 while (try_index != kInvalidTryIndex) {
671 // Detect circles in the exception handler data.
672 num_handlers_checked++;
673 ASSERT(num_handlers_checked <= handlers.num_entries());
674 // Only consider user written handlers and ignore synthesized try/catch in
675 // async methods as well as synthetic try/catch hiding inside try/finally.
676 if (!handlers.IsGenerated(try_index)) {
677 handled_types = handlers.GetHandledTypes(try_index);
678 const intptr_t num_types = handled_types.Length();
679 for (intptr_t k = 0; k < num_types; k++) {
680 type ^= handled_types.At(k);
681 ASSERT(!type.IsNull());
682 // Uninstantiated types are not added to ExceptionHandlers data.
683 ASSERT(type.IsInstantiated());
684 if (type.IsDynamicType()) {
685 return true;
686 }
687 if (exc_obj.IsInstanceOf(type, Object::null_type_arguments(),
688 Object::null_type_arguments())) {
689 return true;
690 }
691 }
692 }
693 try_index = handlers.OuterTryIndex(try_index);
694 }
695
696 return false;
697}
698
699// Get the saved current context of this activation.
701 if (!ctx_.IsNull()) return ctx_;
702 GetVarDescriptors();
703 intptr_t var_desc_len = var_descriptors_.Length();
704 Object& obj = Object::Handle();
705 for (intptr_t i = 0; i < var_desc_len; i++) {
707 var_descriptors_.GetInfo(i, &var_info);
708 const int8_t kind = var_info.kind();
710 if (FLAG_trace_debugger_stacktrace) {
711 OS::PrintErr("\tFound saved current ctx at index %d\n",
712 var_info.index());
713 }
714 const auto variable_index = VariableIndex(var_info.index());
715 obj = GetStackVar(variable_index);
716 if (obj.IsClosure()) {
717 ASSERT(function().name() == Symbols::call().ptr());
718 ASSERT(function().IsInvokeFieldDispatcher());
719 // Closure.call frames.
720 ctx_ = Closure::Cast(obj).GetContext();
721 } else if (obj.IsContext()) {
722 ctx_ = Context::Cast(obj).ptr();
723 } else {
724 ASSERT(obj.IsNull() || obj.ptr() == Object::optimized_out().ptr());
725 ctx_ = Context::null();
726 }
727 return ctx_;
728 }
729 }
730 return ctx_;
731}
732
734 const Instance& exc_obj) const {
735 if (FLAG_trace_debugger_stacktrace) {
736 OS::PrintErr("GetHandlerFrame(%s)\n", exc_obj.ToCString());
737 }
738
739 for (intptr_t frame_index = 0; frame_index < Length(); frame_index++) {
740 ActivationFrame* frame = FrameAt(frame_index);
741 const bool can_handle = frame->HandlesException(exc_obj);
742 if (FLAG_trace_debugger_stacktrace) {
743 OS::PrintErr(" #%04" Pd " (%s) %s", frame_index,
744 can_handle ? "+" : "-", frame->ToCString());
745 }
746 if (can_handle) {
747 return frame;
748 }
749 }
750 return nullptr;
751}
752
753void ActivationFrame::GetDescIndices() {
754 if (vars_initialized_) {
755 return;
756 }
757 GetVarDescriptors();
758
759 TokenPosition activation_token_pos = TokenPos();
760 if (!activation_token_pos.IsDebugPause() || kind_ != kRegular) {
761 // We don't have a token position for this frame, so can't determine
762 // which variables are visible.
763 vars_initialized_ = true;
764 return;
765 }
766
767 GrowableArray<String*> var_names(8);
768 intptr_t var_desc_len = var_descriptors_.Length();
769 for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
770 ASSERT(var_names.length() == desc_indices_.length());
771 UntaggedLocalVarDescriptors::VarInfo var_info;
772 var_descriptors_.GetInfo(cur_idx, &var_info);
773 const int8_t kind = var_info.kind();
776 continue;
777 }
778 if (!activation_token_pos.IsWithin(var_info.begin_pos, var_info.end_pos)) {
779 continue;
780 }
782 (ContextLevel() < var_info.scope_id)) {
783 // The variable is textually in scope but the context level
784 // at the activation frame's PC is lower than the context
785 // level of the variable. The context containing the variable
786 // has already been removed from the chain. This can happen when we
787 // break at a return statement, since the contexts get discarded
788 // before the debugger gets called.
789 continue;
790 }
791 // The current variable is textually in scope. Now check whether
792 // there is another local variable with the same name that shadows
793 // or is shadowed by this variable.
794 String& var_name = String::Handle(var_descriptors_.GetName(cur_idx));
795 intptr_t indices_len = desc_indices_.length();
796 bool name_match_found = false;
797 for (intptr_t i = 0; i < indices_len; i++) {
798 if (var_name.Equals(*var_names[i])) {
799 // Found two local variables with the same name. Now determine
800 // which one is shadowed.
801 name_match_found = true;
802 UntaggedLocalVarDescriptors::VarInfo i_var_info;
803 var_descriptors_.GetInfo(desc_indices_[i], &i_var_info);
804 if (i_var_info.begin_pos < var_info.begin_pos) {
805 // The variable we found earlier is in an outer scope
806 // and is shadowed by the current variable. Replace the
807 // descriptor index of the previously found variable
808 // with the descriptor index of the current variable.
809 desc_indices_[i] = cur_idx;
810 } else {
811 // The variable we found earlier is in an inner scope
812 // and shadows the current variable. Skip the current
813 // variable. (Nothing to do.)
814 }
815 break; // Stop looking for name matches.
816 }
817 }
818 if (!name_match_found) {
819 // No duplicate name found. Add the current descriptor index to the
820 // list of visible variables.
821 desc_indices_.Add(cur_idx);
822 var_names.Add(&var_name);
823 }
824 }
825 vars_initialized_ = true;
826}
827
829 GetDescIndices();
830 return desc_indices_.length();
831}
832
833DART_FORCE_INLINE static ObjectPtr GetVariableValue(uword addr) {
834 return *reinterpret_cast<ObjectPtr*>(addr);
835}
836
837// Caution: GetParameter only works for fixed parameters.
839 intptr_t num_parameters = function().num_fixed_parameters();
840 ASSERT(0 <= index && index < num_parameters);
841
842 // fp will be a nullptr if the frame isn't active on the stack.
843 if (fp() == 0) {
844 return Object::null();
845 }
846
847 if (function().MakesCopyOfParameters()) {
848 // Function parameters are copied to a fixed place in the callee's frame.
849 if (function().IsSuspendableFunction()) {
850 ++index; // Skip slot reserved for :suspend_state variable.
851 }
854 } else {
855 intptr_t reverse_index = num_parameters - index;
856 return GetVariableValue(ParamAddress(fp(), reverse_index));
857 }
858}
859
861 ASSERT(function().IsClosureFunction());
863 ASSERT(param.IsInstance());
864 ASSERT(Instance::Cast(param).IsClosure());
865 return Closure::Cast(param).ptr();
866}
867
869 ASSERT(function().IsSuspendableFunction());
871}
872
874 Object& suspend_state = Object::Handle(GetSuspendStateVar());
875 if (suspend_state.IsSuspendState()) {
876 return SuspendState::Cast(suspend_state).function_data();
877 }
878 return suspend_state.ptr();
879}
880
881ObjectPtr ActivationFrame::GetStackVar(VariableIndex variable_index) {
882 const intptr_t slot_index =
884 if (deopt_frame_.IsNull()) {
885 return GetVariableValue(LocalVarAddress(fp(), slot_index));
886 } else {
887 return deopt_frame_.At(LocalVarIndex(deopt_frame_offset_, slot_index));
888 }
889}
890
892 if (deopt_frame_.IsNull()) {
893 return true;
894 }
895 // TODO(turnidge): This is conservative. It looks at all values in
896 // the deopt_frame_ even though some of them may correspond to other
897 // inlined frames.
898 Object& obj = Object::Handle();
899 for (int i = 0; i < deopt_frame_.Length(); i++) {
900 obj = deopt_frame_.At(i);
901 if (obj.ptr() == Object::optimized_out().ptr()) {
902 return false;
903 }
904 }
905 return true;
906}
907
908void ActivationFrame::PrintContextMismatchError(intptr_t ctx_slot,
909 intptr_t frame_ctx_level,
910 intptr_t var_ctx_level) {
912 "-------------------------\n"
913 "Encountered context mismatch\n"
914 "\tctx_slot: %" Pd
915 "\n"
916 "\tframe_ctx_level: %" Pd
917 "\n"
918 "\tvar_ctx_level: %" Pd "\n\n",
919 ctx_slot, frame_ctx_level, var_ctx_level);
920
922 "-------------------------\n"
923 "Current frame:\n%s\n",
924 this->ToCString());
925
927 "-------------------------\n"
928 "Context contents:\n");
929 const Context& ctx = GetSavedCurrentContext();
930 ctx.Dump(8);
931
933 "-------------------------\n"
934 "Debugger stack trace...\n\n");
936 intptr_t num_frames = stack->Length();
937 for (intptr_t i = 0; i < num_frames; i++) {
938 ActivationFrame* frame = stack->FrameAt(i);
939 OS::PrintErr("#%04" Pd " %s", i, frame->ToCString());
940 }
941
943 "-------------------------\n"
944 "All frames...\n\n");
945 StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
948 StackFrame* frame = iterator.NextFrame();
949 intptr_t num = 0;
950 while ((frame != nullptr)) {
951 OS::PrintErr("#%04" Pd " %s\n", num++, frame->ToCString());
952 frame = iterator.NextFrame();
953 }
954}
955
957 String* name,
958 TokenPosition* declaration_token_pos,
959 TokenPosition* visible_start_token_pos,
960 TokenPosition* visible_end_token_pos,
961 Object* value) {
962 GetDescIndices();
963 ASSERT(i < desc_indices_.length());
964 intptr_t desc_index = desc_indices_[i];
965 ASSERT(name != nullptr);
966
967 *name = var_descriptors_.GetName(desc_index);
968
970 var_descriptors_.GetInfo(desc_index, &var_info);
971 ASSERT(declaration_token_pos != nullptr);
972 *declaration_token_pos = var_info.declaration_pos;
973 ASSERT(visible_start_token_pos != nullptr);
974 *visible_start_token_pos = var_info.begin_pos;
975 ASSERT(visible_end_token_pos != nullptr);
976 *visible_end_token_pos = var_info.end_pos;
977 ASSERT(value != nullptr);
978 const int8_t kind = var_info.kind();
979 const auto variable_index = VariableIndex(var_info.index());
981 *value = GetStackVar(variable_index);
982 } else {
984 *value = GetContextVar(var_info.scope_id, variable_index.value());
985 }
986}
987
988ObjectPtr ActivationFrame::GetContextVar(intptr_t var_ctx_level,
989 intptr_t ctx_slot) {
990 // The context level at the PC/token index of this activation frame.
991 intptr_t frame_ctx_level = ContextLevel();
992
993 return GetRelativeContextVar(var_ctx_level, ctx_slot, frame_ctx_level);
994}
995
996ObjectPtr ActivationFrame::GetRelativeContextVar(intptr_t var_ctx_level,
997 intptr_t ctx_slot,
998 intptr_t frame_ctx_level) {
999 const Context& ctx = GetSavedCurrentContext();
1000
1001 // It's possible that ctx was optimized out as no locals were captured by the
1002 // context. See issue #38182.
1003 if (ctx.IsNull()) {
1004 return Object::optimized_out().ptr();
1005 }
1006
1007 intptr_t level_diff = frame_ctx_level - var_ctx_level;
1008 if (level_diff == 0) {
1009 if ((ctx_slot < 0) || (ctx_slot >= ctx.num_variables())) {
1010 PrintContextMismatchError(ctx_slot, frame_ctx_level, var_ctx_level);
1011 }
1012 ASSERT((ctx_slot >= 0) && (ctx_slot < ctx.num_variables()));
1013 return ctx.At(ctx_slot);
1014 } else if (level_diff > 0) {
1015 Context& var_ctx = Context::Handle(ctx.ptr());
1016 while (level_diff > 0 && !var_ctx.IsNull()) {
1017 level_diff--;
1018 var_ctx = var_ctx.parent();
1019 }
1020 if (var_ctx.IsNull() || (ctx_slot < 0) ||
1021 (ctx_slot >= var_ctx.num_variables())) {
1022 PrintContextMismatchError(ctx_slot, frame_ctx_level, var_ctx_level);
1023 }
1024 ASSERT(!var_ctx.IsNull());
1025 ASSERT((ctx_slot >= 0) && (ctx_slot < var_ctx.num_variables()));
1026 return var_ctx.At(ctx_slot);
1027 } else {
1028 PrintContextMismatchError(ctx_slot, frame_ctx_level, var_ctx_level);
1029 return Object::null();
1030 }
1031}
1032
1034 GetDescIndices();
1035 intptr_t num_variables = desc_indices_.length();
1036 String& var_name = String::Handle();
1038 const Array& list = Array::Handle(Array::New(2 * num_variables));
1039 for (intptr_t i = 0; i < num_variables; i++) {
1040 TokenPosition ignore = TokenPosition::kNoSource;
1041 VariableAt(i, &var_name, &ignore, &ignore, &ignore, &value);
1042 list.SetAt(2 * i, var_name);
1043 list.SetAt((2 * i) + 1, value);
1044 }
1045 return list.ptr();
1046}
1047
1049 GetDescIndices();
1050 intptr_t num_variables = desc_indices_.length();
1051 String& var_name = String::Handle();
1053 for (intptr_t i = 0; i < num_variables; i++) {
1054 TokenPosition ignore = TokenPosition::kNoSource;
1055 VariableAt(i, &var_name, &ignore, &ignore, &ignore, &value);
1056 if (var_name.Equals(Symbols::This())) {
1057 return value.ptr();
1058 }
1059 }
1060 return Object::optimized_out().ptr();
1061}
1062
1063static bool IsSyntheticVariableName(const String& var_name) {
1064 return (var_name.Length() >= 1) && (var_name.CharAt(0) == ':');
1065}
1066
1067static bool IsPrivateVariableName(const String& var_name) {
1068 return (var_name.Length() >= 1) && (var_name.CharAt(0) == '_');
1069}
1070
1072 const ExternalTypedData& kernel_buffer,
1073 const Array& type_definitions,
1074 const Array& arguments,
1075 const TypeArguments& type_arguments) {
1076 auto thread = Thread::Current();
1077 auto zone = thread->zone();
1078
1079 // The expression evaluation function will get all it's captured state passed
1080 // as parameters (with `this` being the exception). As a result, we treat the
1081 // expression evaluation function as either a top-level, static or instance
1082 // method.
1083 const auto& outermost =
1084 Function::Handle(zone, function().GetOutermostFunction());
1085 const auto& klass = Class::Handle(zone, outermost.Owner());
1086 const auto& library = Library::Handle(zone, klass.library());
1087
1088 auto& receiver = Object::Handle(zone);
1089 if (!klass.IsTopLevel() && !outermost.is_static()) {
1090 receiver = GetReceiver();
1091 RELEASE_ASSERT(receiver.IsInstance() ||
1092 receiver.ptr() == Object::optimized_out().ptr());
1093 }
1094 return Instance::EvaluateCompiledExpression(thread, receiver, library, klass,
1095 kernel_buffer, type_definitions,
1096 arguments, type_arguments);
1097}
1098
1100 const GrowableObjectArray& param_names,
1101 const GrowableObjectArray& param_values,
1102 const GrowableObjectArray& type_params_names,
1103 const GrowableObjectArray& type_params_bounds,
1104 const GrowableObjectArray& type_params_defaults) {
1105 GetDescIndices();
1106 bool type_arguments_available = false;
1108 String& existing_name = String::Handle();
1110 TypeArguments& type_arguments = TypeArguments::Handle();
1111 intptr_t num_variables = desc_indices_.length();
1112 for (intptr_t i = 0; i < num_variables; i++) {
1113 TokenPosition ignore = TokenPosition::kNoSource;
1114 VariableAt(i, &name, &ignore, &ignore, &ignore, &value);
1115 if (name.Equals(Symbols::FunctionTypeArgumentsVar())) {
1116 type_arguments_available = true;
1117 type_arguments ^= value.ptr();
1118 } else if (!name.Equals(Symbols::This()) &&
1120 value.ptr() != Object::optimized_out().ptr()) {
1123 }
1124 bool conflict = false;
1125 for (intptr_t j = 0; j < param_names.Length(); j++) {
1126 existing_name ^= param_names.At(j);
1127 if (name.Equals(existing_name)) {
1128 conflict = true;
1129 break;
1130 }
1131 }
1132 // If local has the same name as a binding in the incoming scope, prefer
1133 // the one from the incoming scope, since it is logically a child scope
1134 // of the activation's current scope.
1135 if (!conflict) {
1136 param_names.Add(name);
1137 param_values.Add(value);
1138 }
1139 }
1140 }
1141
1142 if ((function().IsGeneric() || function().HasGenericParent()) &&
1143 type_arguments_available) {
1144 intptr_t num_vars = function().NumTypeArguments();
1145 type_params_names.Grow(num_vars);
1146 type_params_names.SetLength(num_vars);
1147 type_params_bounds.Grow(num_vars);
1148 type_params_bounds.SetLength(num_vars);
1149 type_params_defaults.Grow(num_vars);
1150 type_params_defaults.SetLength(num_vars);
1152 AbstractType& defaultType = AbstractType::Handle();
1153 TypeParameters& type_params = TypeParameters::Handle();
1154 Function& current = Function::Handle(function().ptr());
1155 intptr_t mapping_offset = num_vars;
1156 for (; !current.IsNull(); current = current.parent_function()) {
1157 type_params = current.type_parameters();
1158 if (type_params.IsNull()) continue;
1159 intptr_t size = current.NumTypeParameters();
1160 ASSERT(size > 0 && type_params.Length() == size);
1161 ASSERT(mapping_offset >= size);
1162 mapping_offset -= size;
1163 for (intptr_t j = 0; j < size; ++j) {
1164 name = type_params.NameAt(j);
1165 bound = type_params.BoundAt(j);
1166 defaultType = type_params.DefaultAt(j);
1167 // Write the names in backwards in terms of chain of functions.
1168 // But keep the order of names within the same function. so they
1169 // match up with the order of the types in 'type_arguments'.
1170 // Index:0 1 2 3 ...
1171 // |Names in Grandparent| |Names in Parent| ..|Names in Child|
1172 type_params_names.SetAt(mapping_offset + j, name);
1173 type_params_bounds.SetAt(mapping_offset + j, bound);
1174 type_params_defaults.SetAt(mapping_offset + j, defaultType);
1175 }
1176 }
1177 if (!type_arguments.IsNull()) {
1178 if (type_arguments.Length() == 0) {
1179 for (intptr_t i = 0; i < num_vars; ++i) {
1180 type_arguments.SetTypeAt(i, Object::dynamic_type());
1181 }
1182 }
1183 ASSERT(type_arguments.Length() == num_vars);
1184 }
1185 }
1186
1187 return type_arguments.ptr();
1188}
1189
1191 if (function().IsNull()) {
1192 return Thread::Current()->zone()->PrintToString("[ Frame kind: %s]\n",
1193 KindToCString(kind_));
1194 }
1195 const String& url = String::Handle(SourceUrl());
1196 intptr_t line = LineNumber();
1197 const char* func_name = function().ToFullyQualifiedCString();
1198 if (kind_ == kRegular) {
1199 return Thread::Current()->zone()->PrintToString(
1200 "[ Frame pc(0x%" Px " code offset:0x%" Px ") fp(0x%" Px ") sp(0x%" Px
1201 ")\n"
1202 "\tfunction = %s\n"
1203 "\turl = %s\n"
1204 "\tline = %" Pd
1205 "\n"
1206 "\tcontext = %s\n"
1207 "\tcontext level = %" Pd " ]\n",
1208 pc(), pc() - code().PayloadStart(), fp(), sp(), func_name,
1209 url.ToCString(), line, ctx_.ToCString(), ContextLevel());
1210 } else {
1211 return Thread::Current()->zone()->PrintToString(
1212 "[ Frame code function = %s\n"
1213 "\turl = %s\n"
1214 "\tline = %" Pd
1215 "\n"
1216 "\tcontext = %s]\n",
1217 func_name, url.ToCString(), line, ctx_.ToCString());
1218 }
1219}
1220
1222 if (kind_ == kRegular) {
1223 PrintToJSONObjectRegular(jsobj);
1224 } else if (kind_ == kAsyncAwaiter) {
1225 PrintToJSONObjectAsyncAwaiter(jsobj);
1226 } else if (kind_ == kAsyncSuspensionMarker) {
1227 PrintToJSONObjectAsyncSuspensionMarker(jsobj);
1228 } else {
1229 UNIMPLEMENTED();
1230 }
1231}
1232
1233void ActivationFrame::PrintToJSONObjectRegular(JSONObject* jsobj) {
1235 jsobj->AddProperty("type", "Frame");
1236 jsobj->AddProperty("kind", KindToCString(kind_));
1237 const TokenPosition& pos = TokenPos();
1238 jsobj->AddLocation(script, pos);
1239 jsobj->AddProperty("function", function());
1240 jsobj->AddProperty("code", code());
1241 {
1242 JSONArray jsvars(jsobj, "vars");
1243 const int num_vars = NumLocalVariables();
1244 for (intptr_t v = 0; v < num_vars; v++) {
1245 String& var_name = String::Handle();
1246 Instance& var_value = Instance::Handle();
1247 TokenPosition declaration_token_pos = TokenPosition::kNoSource;
1248 TokenPosition visible_start_token_pos = TokenPosition::kNoSource;
1249 TokenPosition visible_end_token_pos = TokenPosition::kNoSource;
1250 VariableAt(v, &var_name, &declaration_token_pos, &visible_start_token_pos,
1251 &visible_end_token_pos, &var_value);
1252 if (!IsSyntheticVariableName(var_name)) {
1253 JSONObject jsvar(&jsvars);
1254 jsvar.AddProperty("type", "BoundVariable");
1255 const char* scrubbed_var_name = String::ScrubName(var_name);
1256 jsvar.AddProperty("name", scrubbed_var_name);
1257 jsvar.AddProperty("value", var_value);
1258 // Where was the variable declared?
1259 jsvar.AddProperty("declarationTokenPos", declaration_token_pos);
1260 // When the variable becomes visible to the scope.
1261 jsvar.AddProperty("scopeStartTokenPos", visible_start_token_pos);
1262 // When the variable stops being visible to the scope.
1263 jsvar.AddProperty("scopeEndTokenPos", visible_end_token_pos);
1264 }
1265 }
1266 }
1267}
1268
1269void ActivationFrame::PrintToJSONObjectAsyncAwaiter(JSONObject* jsobj) {
1270 jsobj->AddProperty("type", "Frame");
1271 jsobj->AddProperty("kind", KindToCString(kind_));
1272 const Script& script = Script::Handle(SourceScript());
1273 const TokenPosition& pos = TokenPos();
1274 jsobj->AddLocation(script, pos);
1275 jsobj->AddProperty("function", function());
1276 jsobj->AddProperty("code", code());
1277}
1278
1279void ActivationFrame::PrintToJSONObjectAsyncSuspensionMarker(
1280 JSONObject* jsobj) {
1281 jsobj->AddProperty("type", "Frame");
1282 jsobj->AddProperty("kind", KindToCString(kind_));
1283 jsobj->AddProperty("marker", "AsynchronousSuspension");
1284}
1285
1287 return FLAG_show_invisible_frames || function.is_visible();
1288}
1289
1290void DebuggerStackTrace::AddActivation(ActivationFrame* frame) {
1291 if (IsFunctionVisible(frame->function())) {
1292 trace_.Add(frame);
1293 }
1294}
1295
1296void DebuggerStackTrace::AddAsyncSuspension() {
1297 // We might start asynchronous unwinding in one of the internal
1298 // dart:async functions which would make synchronous part of the
1299 // stack empty. This would not happen normally but might happen
1300 // with stress flags.
1301 if (trace_.is_empty() ||
1302 trace_.Last()->kind() != ActivationFrame::kAsyncSuspensionMarker) {
1303 trace_.Add(new ActivationFrame(ActivationFrame::kAsyncSuspensionMarker));
1304 }
1305}
1306
1307void DebuggerStackTrace::AddAsyncAwaiterFrame(uword pc,
1308 const Code& code,
1309 const Closure& closure) {
1310 trace_.Add(new ActivationFrame(pc, code, closure));
1311}
1312
1313const uint8_t kSafepointKind = UntaggedPcDescriptors::kIcCall |
1314 UntaggedPcDescriptors::kUnoptStaticCall |
1315 UntaggedPcDescriptors::kRuntimeCall;
1316
1318 BreakpointLocation* breakpoint_location,
1319 uword pc,
1321 : code_(code.ptr()),
1322 pc_(pc),
1323 enabled_count_(0),
1324 next_(nullptr),
1325 breakpoint_kind_(kind),
1326 saved_value_(Code::null()) {
1327 ASSERT(!code.IsNull());
1328 ASSERT(pc_ != 0);
1329 ASSERT((breakpoint_kind_ & kSafepointKind) != 0);
1330 AddBreakpointLocation(breakpoint_location);
1331 ASSERT(breakpoint_location->token_pos().IsReal());
1332}
1333
1335 // Make sure we don't leave patched code behind.
1336 ASSERT(!IsEnabled());
1337// Poison the data so we catch use after free errors.
1338#ifdef DEBUG
1339 code_ = Code::null();
1340 pc_ = 0ul;
1341 next_ = nullptr;
1342 breakpoint_kind_ = UntaggedPcDescriptors::kOther;
1343#endif
1344}
1345
1347 if (enabled_count_ == 0) {
1348 PatchCode();
1349 }
1350 ++enabled_count_;
1351}
1352
1354 if (enabled_count_ == 1) {
1355 RestoreCode();
1356 }
1357 --enabled_count_;
1358}
1359
1361 BreakpointLocation* breakpoint_location) {
1362 for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
1363 if (breakpoint_locations_[i] == breakpoint_location) {
1364 return true;
1365 }
1366 }
1367 return false;
1368}
1369
1371 BreakpointLocation* breakpoint_location) {
1372 for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
1373 if (breakpoint_locations_[i] == breakpoint_location) {
1374 breakpoint_locations_.EraseAt(i);
1375 return true;
1376 }
1377 }
1378 return false;
1379}
1380
1381BreakpointLocation* CodeBreakpoint::FindBreakpointForDebugger(
1382 Debugger* debugger) {
1383 for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
1384 if (breakpoint_locations_[i]->debugger() == debugger) {
1385 return breakpoint_locations_[i];
1386 }
1387 }
1388 return nullptr;
1389}
1390
1392 : isolate_group_(isolate_group),
1393 code_breakpoints_lock_(new SafepointRwLock()),
1394 code_breakpoints_(nullptr),
1395 breakpoint_locations_lock_(new SafepointRwLock()),
1396 single_stepping_set_lock_(new RwLock()),
1397 needs_breakpoint_cleanup_(false) {}
1398
1400 while (code_breakpoints_ != nullptr) {
1401 CodeBreakpoint* cbpt = code_breakpoints_;
1402 code_breakpoints_ = code_breakpoints_->next();
1403 ASSERT(!cbpt->IsEnabled());
1404 delete cbpt;
1405 }
1406}
1407
1409 : isolate_(isolate),
1410 next_id_(1),
1411 latent_locations_(nullptr),
1412 breakpoint_locations_(nullptr),
1413 resume_action_(kContinue),
1414 resume_frame_index_(-1),
1415 post_deopt_frame_index_(-1),
1416 ignore_breakpoints_(false),
1417 pause_event_(nullptr),
1418 stack_trace_(nullptr),
1419 async_awaiter_stack_trace_(nullptr),
1420 stepping_fp_(0),
1421 last_stepping_fp_(0),
1422 last_stepping_pos_(TokenPosition::kNoSource),
1423 skip_next_step_(false),
1424 exc_pause_info_(kNoPauseOnExceptions) {}
1425
1427 ASSERT(!IsPaused());
1428 ASSERT(latent_locations_ == nullptr);
1429 ASSERT(breakpoint_locations_ == nullptr);
1430 ASSERT(stack_trace_ == nullptr);
1431 ASSERT(async_awaiter_stack_trace_ == nullptr);
1432}
1433
1435 // TODO(johnmccutchan): Do not create a debugger for isolates that don't need
1436 // them. Then, assert here that isolate_ is not one of those isolates.
1437 if (Isolate::IsSystemIsolate(isolate_)) {
1438 return;
1439 }
1440 {
1442 group_debugger()->breakpoint_locations_lock());
1443 while (breakpoint_locations_ != nullptr) {
1444 BreakpointLocation* loc = breakpoint_locations_;
1445 group_debugger()->UnlinkCodeBreakpoints(loc);
1446 group_debugger()->UnregisterBreakpointLocation(loc);
1447 breakpoint_locations_ = breakpoint_locations_->next();
1448 delete loc;
1449 }
1450 while (latent_locations_ != nullptr) {
1451 BreakpointLocation* loc = latent_locations_;
1452 group_debugger()->UnlinkCodeBreakpoints(loc);
1453 group_debugger()->UnregisterBreakpointLocation(loc);
1454 latent_locations_ = latent_locations_->next();
1455 delete loc;
1456 }
1457 }
1458 if (NeedsIsolateEvents()) {
1461 }
1462}
1463
1465static bool IsAtAsyncJump(ActivationFrame* top_frame);
1466
1467bool Debugger::SetupStepOverAsyncSuspension(const char** error) {
1468 ActivationFrame* top_frame = TopDartFrame();
1469 if (!IsAtAsyncJump(top_frame)) {
1470 // Not at an async operation.
1471 if (error != nullptr) {
1472 *error = "Isolate must be paused at an async suspension point";
1473 }
1474 return false;
1475 }
1476 ASSERT(top_frame->function().IsAsyncFunction() ||
1477 top_frame->function().IsAsyncGenerator());
1478 const auto& function_data =
1480 SetBreakpointAtResumption(function_data);
1481 return true;
1482}
1483
1484static bool CanRewindFrame(intptr_t frame_index, const char** error);
1485
1487 intptr_t frame_index,
1488 const char** error) {
1489 if (error != nullptr) {
1490 *error = nullptr;
1491 }
1492 resume_frame_index_ = -1;
1493 switch (action) {
1494 case kStepInto:
1495 case kStepOver:
1496 case kStepOut:
1497 case kContinue:
1498 set_resume_action(action);
1499 return true;
1500 case kStepRewind:
1501 if (!CanRewindFrame(frame_index, error)) {
1502 return false;
1503 }
1504 set_resume_action(kStepRewind);
1505 resume_frame_index_ = frame_index;
1506 return true;
1508 return SetupStepOverAsyncSuspension(error);
1509 default:
1510 UNREACHABLE();
1511 return false;
1512 }
1513}
1514
1515// Deoptimize all functions in the isolate.
1516// TODO(hausner): Actually we only need to deoptimize those functions
1517// that inline the function that contains the newly created breakpoint.
1518// We currently don't have this info so we deoptimize all functions.
1519void Debugger::DeoptimizeWorld() {
1520#if defined(DART_PRECOMPILED_RUNTIME)
1521 UNREACHABLE();
1522#else
1523 if (FLAG_trace_deoptimization) {
1524 THR_Print("Deopt for debugger\n");
1525 }
1526 isolate_->set_has_attempted_stepping(true);
1527
1529
1530 // Iterate over all classes, deoptimize functions.
1531 // TODO(hausner): Could possibly be combined with RemoveOptimizedCode()
1532 const ClassTable& class_table = *isolate_->group()->class_table();
1533 auto thread = Thread::Current();
1534 auto isolate_group = thread->isolate_group();
1535 auto zone = thread->zone();
1536 CallSiteResetter resetter(zone);
1537 Class& cls = Class::Handle(zone);
1538 Array& functions = Array::Handle(zone);
1539 Function& function = Function::Handle(zone);
1540 Code& code = Code::Handle(zone);
1541
1542 const intptr_t num_classes = class_table.NumCids();
1543 const intptr_t num_tlc_classes = class_table.NumTopLevelCids();
1544 SafepointWriteRwLocker ml(thread, isolate_group->program_lock());
1545 for (intptr_t i = 1; i < num_classes + num_tlc_classes; i++) {
1546 const intptr_t cid =
1547 i < num_classes ? i : ClassTable::CidFromTopLevelIndex(i - num_classes);
1548 if (class_table.HasValidClassAt(cid)) {
1549 cls = class_table.At(cid);
1550
1551 // Disable optimized functions.
1552 functions = cls.functions();
1553 if (!functions.IsNull()) {
1554 intptr_t num_functions = functions.Length();
1555 for (intptr_t pos = 0; pos < num_functions; pos++) {
1556 function ^= functions.At(pos);
1557 ASSERT(!function.IsNull());
1558 // Force-optimized functions don't have unoptimized code and can't
1559 // deoptimize. Their optimized codes are still valid.
1560 if (!function.ForceOptimize()) {
1561 if (function.HasOptimizedCode()) {
1562 function.SwitchToUnoptimizedCode();
1563 }
1564 code = function.unoptimized_code();
1565 if (!code.IsNull()) {
1566 resetter.ResetSwitchableCalls(code);
1567 }
1568 }
1569 // Also disable any optimized implicit closure functions.
1570 if (function.HasImplicitClosureFunction()) {
1571 function = function.ImplicitClosureFunction();
1572 if (!function.ForceOptimize()) {
1573 if (function.HasOptimizedCode()) {
1574 function.SwitchToUnoptimizedCode();
1575 }
1576 code = function.unoptimized_code();
1577 if (!code.IsNull()) {
1578 resetter.ResetSwitchableCalls(code);
1579 }
1580 }
1581 }
1582 }
1583 }
1584 }
1585 }
1586
1587 // Disable optimized closure functions.
1589 if (!function.ForceOptimize()) {
1590 if (function.HasOptimizedCode()) {
1591 function.SwitchToUnoptimizedCode();
1592 }
1593 code = function.unoptimized_code();
1594 if (!code.IsNull()) {
1595 resetter.ResetSwitchableCalls(code);
1596 }
1597 }
1598 return true; // Continue iteration.
1599 });
1600#endif // defined(DART_PRECOMPILED_RUNTIME)
1601}
1602
1603void Debugger::RunWithStoppedDeoptimizedWorld(std::function<void()> fun) {
1604#if !defined(DART_PRECOMPILED_RUNTIME)
1605 // RELOAD_OPERATION_SCOPE is used here because is is guaranteed that
1606 // isolates at reload safepoints hold no safepoint locks.
1607 RELOAD_OPERATION_SCOPE(Thread::Current());
1608 group_debugger()->isolate_group()->RunWithStoppedMutators([&]() {
1609 DeoptimizeWorld();
1610 fun();
1611 });
1612#endif
1613}
1614
1615void Debugger::NotifySingleStepping(bool value) {
1616 if (value) {
1617 // Setting breakpoint requires unoptimized code, make sure we stop all
1618 // isolates to prevent racing reoptimization.
1619 RunWithStoppedDeoptimizedWorld([&] {
1620 isolate_->set_single_step(value);
1621 // Ensure other isolates in the isolate group keep
1622 // unoptimized code unoptimized, won't attempt to optimize it.
1623 group_debugger()->RegisterSingleSteppingDebugger(Thread::Current(), this);
1624 });
1625 } else {
1626 isolate_->set_single_step(value);
1627 group_debugger()->UnregisterSingleSteppingDebugger(Thread::Current(), this);
1628 }
1629}
1630
1633 const Code& code,
1634 const Array& deopt_frame,
1635 intptr_t deopt_frame_offset) {
1636 ASSERT(code.ContainsInstructionAt(pc));
1637 ActivationFrame* activation = new ActivationFrame(
1638 pc, frame->fp(), frame->sp(), code, deopt_frame, deopt_frame_offset);
1639 if (FLAG_trace_debugger_stacktrace) {
1640 const Context& ctx = activation->GetSavedCurrentContext();
1641 OS::PrintErr("\tUsing saved context: %s\n", ctx.ToCString());
1642 OS::PrintErr("\tLine number: %" Pd "\n", activation->LineNumber());
1643 }
1644 return activation;
1645}
1646
1647#if !defined(DART_PRECOMPILED_RUNTIME)
1648static ArrayPtr DeoptimizeToArray(Thread* thread,
1650 const Code& code) {
1651 ASSERT(code.is_optimized() && !code.is_force_optimized());
1652 Isolate* isolate = thread->isolate();
1653 // Create the DeoptContext for this deoptimization.
1654 DeoptContext* deopt_context =
1655 new DeoptContext(frame, code, DeoptContext::kDestIsAllocated, nullptr,
1656 nullptr, true, false /* deoptimizing_code */);
1657 isolate->set_deopt_context(deopt_context);
1658
1659 deopt_context->FillDestFrame();
1660 deopt_context->MaterializeDeferredObjects();
1661 const Array& dest_frame =
1662 Array::Handle(thread->zone(), deopt_context->DestFrameAsArray());
1663
1664 isolate->set_deopt_context(nullptr);
1665 delete deopt_context;
1666
1667 return dest_frame.ptr();
1668}
1669#endif // !defined(DART_PRECOMPILED_RUNTIME)
1670
1671DebuggerStackTrace* DebuggerStackTrace::Collect() {
1672 Thread* thread = Thread::Current();
1673 Zone* zone = thread->zone();
1674
1675 Code& code = Code::Handle(zone);
1676 DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8);
1677
1678 StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames, thread,
1679 StackFrameIterator::kNoCrossThreadIteration);
1680 for (StackFrame* frame = iterator.NextFrame(); frame != nullptr;
1681 frame = iterator.NextFrame()) {
1682 ASSERT(frame->IsValid());
1683 if (FLAG_trace_debugger_stacktrace) {
1684 OS::PrintErr("CollectStackTrace: visiting frame:\n\t%s\n",
1685 frame->ToCString());
1686 }
1687 if (frame->IsDartFrame()) {
1688 code = frame->LookupDartCode();
1689 stack_trace->AppendCodeFrames(frame, code);
1690 }
1691 }
1692 return stack_trace;
1693}
1694
1695// Appends at least one stack frame. Multiple frames will be appended
1696// if |code| at the frame's pc contains inlined functions.
1697void DebuggerStackTrace::AppendCodeFrames(StackFrame* frame, const Code& code) {
1698#if !defined(DART_PRECOMPILED_RUNTIME)
1699 if (code.is_optimized()) {
1700 if (code.is_force_optimized()) {
1701 if (FLAG_trace_debugger_stacktrace) {
1702 const Function& function = Function::Handle(zone_, code.function());
1703 ASSERT(!function.IsNull());
1704 OS::PrintErr(
1705 "CollectStackTrace: skipping force-optimized function: %s\n",
1706 function.ToFullyQualifiedCString());
1707 }
1708 return; // Skip frame of force-optimized (and non-debuggable) function.
1709 }
1710 // TODO(rmacnak): Use CodeSourceMap
1711 deopt_frame_ = DeoptimizeToArray(thread_, frame, code);
1712 for (InlinedFunctionsIterator it(code, frame->pc()); !it.Done();
1713 it.Advance()) {
1714 inlined_code_ = it.code();
1715 if (FLAG_trace_debugger_stacktrace) {
1716 const Function& function = Function::Handle(zone_, it.function());
1717 ASSERT(!function.IsNull());
1718 OS::PrintErr("CollectStackTrace: visiting inlined function: %s\n",
1719 function.ToFullyQualifiedCString());
1720 }
1721 intptr_t deopt_frame_offset = it.GetDeoptFpOffset();
1722 AddActivation(CollectDartFrame(it.pc(), frame, inlined_code_,
1723 deopt_frame_, deopt_frame_offset));
1724 }
1725 return;
1726 }
1727#endif // !defined(DART_PRECOMPILED_RUNTIME)
1728 AddActivation(
1729 CollectDartFrame(frame->pc(), frame, code, Object::null_array(), 0));
1730}
1731
1732DebuggerStackTrace* DebuggerStackTrace::CollectAsyncAwaiters() {
1733 Thread* thread = Thread::Current();
1734 Zone* zone = thread->zone();
1735
1736 Function& function = Function::Handle(zone);
1737
1738 constexpr intptr_t kDefaultStackAllocation = 8;
1739 auto stack_trace = new DebuggerStackTrace(kDefaultStackAllocation);
1740
1741 bool has_async = false;
1742 bool has_async_catch_error = false;
1743 StackTraceUtils::CollectFrames(
1744 thread, /*skip_frames=*/0,
1745 [&](const StackTraceUtils::Frame& frame) {
1746 if (frame.frame != nullptr) { // Synchronous portion of the stack.
1747 stack_trace->AppendCodeFrames(frame.frame, frame.code);
1748 } else {
1749 has_async = true;
1750
1751 if (frame.code.ptr() == StubCode::AsynchronousGapMarker().ptr()) {
1752 stack_trace->AddAsyncSuspension();
1753 return;
1754 }
1755
1756 // Skip invisible function frames.
1757 function ^= frame.code.function();
1758 if (!function.is_visible()) {
1759 return;
1760 }
1761
1762 const uword absolute_pc = frame.code.PayloadStart() + frame.pc_offset;
1763 stack_trace->AddAsyncAwaiterFrame(absolute_pc, frame.code,
1764 frame.closure);
1765 }
1766 },
1767 &has_async_catch_error);
1768
1769 // If the entire stack is sync, return no (async) trace.
1770 if (!has_async) {
1771 return nullptr;
1772 }
1773
1774 stack_trace->set_has_async_catch_error(has_async_catch_error);
1775
1776 return stack_trace;
1777}
1778
1780 StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
1781 Thread::Current(),
1782 StackFrameIterator::kNoCrossThreadIteration);
1784 while (true) {
1785 frame = iterator.NextFrame();
1786 RELEASE_ASSERT(frame != nullptr);
1787 if (!frame->IsDartFrame()) {
1788 continue;
1789 }
1790 Code& code = Code::Handle(frame->LookupDartCode());
1791 ActivationFrame* activation = new ActivationFrame(
1792 frame->pc(), frame->fp(), frame->sp(), code, Object::null_array(), 0);
1793 return activation;
1794 }
1795}
1796
1797DebuggerStackTrace* Debugger::StackTrace() {
1798 return (stack_trace_ != nullptr) ? stack_trace_
1799 : DebuggerStackTrace::Collect();
1800}
1801
1802DebuggerStackTrace* Debugger::AsyncAwaiterStackTrace() {
1803 return (async_awaiter_stack_trace_ != nullptr)
1804 ? async_awaiter_stack_trace_
1805 : DebuggerStackTrace::CollectAsyncAwaiters();
1806}
1807
1808DebuggerStackTrace* DebuggerStackTrace::From(const class StackTrace& ex_trace) {
1809 DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8);
1810 Function& function = Function::Handle();
1811 Object& code_object = Object::Handle();
1812 Code& code = Code::Handle();
1813
1814 const uword fp = 0;
1815 const uword sp = 0;
1816 const Array& deopt_frame = Array::Handle();
1817 const intptr_t deopt_frame_offset = -1;
1818
1819 for (intptr_t i = 0; i < ex_trace.Length(); i++) {
1820 code_object = ex_trace.CodeAtFrame(i);
1821 // Pre-allocated StackTraces may include empty slots, either (a) to indicate
1822 // where frames were omitted in the case a stack has more frames than the
1823 // pre-allocated trace (such as a stack overflow) or (b) because a stack has
1824 // fewer frames that the pre-allocated trace (such as memory exhaustion with
1825 // a shallow stack).
1826 if (!code_object.IsNull()) {
1827 code ^= code_object.ptr();
1828 ASSERT(code.IsFunctionCode());
1829 function = code.function();
1830 if (function.is_visible()) {
1831 ASSERT(function.ptr() == code.function());
1832 uword pc = code.PayloadStart() + ex_trace.PcOffsetAtFrame(i);
1833 if (code.is_optimized() && ex_trace.expand_inlined()) {
1834 // Traverse inlined frames.
1835 for (InlinedFunctionsIterator it(code, pc); !it.Done();
1836 it.Advance()) {
1837 function = it.function();
1838 code = it.code();
1839 ASSERT(function.ptr() == code.function());
1840 uword pc = it.pc();
1841 ASSERT(pc != 0);
1842 ASSERT(code.PayloadStart() <= pc);
1843 ASSERT(pc < (code.PayloadStart() + code.Size()));
1844
1845 ActivationFrame* activation = new ActivationFrame(
1846 pc, fp, sp, code, deopt_frame, deopt_frame_offset);
1847 stack_trace->AddActivation(activation);
1848 }
1849 } else {
1850 ActivationFrame* activation = new ActivationFrame(
1851 pc, fp, sp, code, deopt_frame, deopt_frame_offset);
1852 stack_trace->AddActivation(activation);
1853 }
1854 }
1855 }
1856 }
1857 return stack_trace;
1858}
1859
1860void Debugger::SetExceptionPauseInfo(Dart_ExceptionPauseInfo pause_info) {
1861 ASSERT((pause_info == kNoPauseOnExceptions) ||
1862 (pause_info == kPauseOnUnhandledExceptions) ||
1863 (pause_info == kPauseOnAllExceptions));
1864 exc_pause_info_ = pause_info;
1865}
1866
1867Dart_ExceptionPauseInfo Debugger::GetExceptionPauseInfo() const {
1868 return exc_pause_info_;
1869}
1870
1871bool Debugger::ShouldPauseOnException(DebuggerStackTrace* stack_trace,
1872 const Instance& exception) {
1873 if (exc_pause_info_ == kNoPauseOnExceptions) {
1874 return false;
1875 }
1876 if (exc_pause_info_ == kPauseOnAllExceptions) {
1877 return true;
1878 }
1879 ASSERT(exc_pause_info_ == kPauseOnUnhandledExceptions);
1880 // There might be no Dart stack if we hit an exception in the runtime, most
1881 // likely OutOfMemory.
1882 if (stack_trace->Length() == 0) {
1883 return false;
1884 }
1885 // Exceptions coming from invalid token positions should be skipped
1886 ActivationFrame* top_frame = stack_trace->FrameAt(0);
1887 if (!top_frame->TokenPos().IsReal() && top_frame->TryIndex() != -1) {
1888 return false;
1889 }
1890 ActivationFrame* handler_frame = stack_trace->GetHandlerFrame(exception);
1891 if (handler_frame == nullptr) {
1892 // Did not find an exception handler that catches this exception.
1893 // Note that this check is not precise, since we can't check
1894 // uninstantiated types, i.e. types containing type parameters.
1895 // Thus, we may report an exception as unhandled when in fact
1896 // it will be caught once we unwind the stack.
1897 return !stack_trace->has_async_catch_error();
1898 }
1899
1900 auto& handler_function = Function::Handle(handler_frame->function().ptr());
1901
1902 // If handler_frame's function is annotated with
1903 // @pragma('vm:notify-debugger-on-exception'), we specifically want to notify
1904 // the debugger of this otherwise ignored exception.
1905 if (!handler_function.IsNull() &&
1906 Library::FindPragma(Thread::Current(), /*only_core=*/false,
1907 handler_function,
1908 Symbols::vm_notify_debugger_on_exception())) {
1909 return true;
1910 }
1911 return false;
1912}
1913
1914void Debugger::PauseException(const Instance& exc) {
1915 if (FLAG_stress_async_stacks) {
1916 DebuggerStackTrace::CollectAsyncAwaiters();
1917 }
1918 // We ignore this exception event when the VM is executing code invoked
1919 // by the debugger to evaluate variables values, when we see a nested
1920 // breakpoint or exception event, or if the debugger is not
1921 // interested in exception events.
1922 if (ignore_breakpoints_ || IsPaused() ||
1923 (exc_pause_info_ == kNoPauseOnExceptions)) {
1924 return;
1925 }
1926 DebuggerStackTrace* async_awaiter_stack_trace =
1927 DebuggerStackTrace::CollectAsyncAwaiters();
1928 DebuggerStackTrace* stack_trace = DebuggerStackTrace::Collect();
1929 if (async_awaiter_stack_trace != nullptr) {
1930 if (!ShouldPauseOnException(async_awaiter_stack_trace, exc)) {
1931 return;
1932 }
1933 } else {
1934 if (!ShouldPauseOnException(stack_trace, exc)) {
1935 return;
1936 }
1937 }
1938 ServiceEvent event(isolate_, ServiceEvent::kPauseException);
1939 event.set_exception(&exc);
1940 if (stack_trace->Length() > 0) {
1941 event.set_top_frame(stack_trace->FrameAt(0));
1942 }
1943 CacheStackTraces(stack_trace, async_awaiter_stack_trace);
1944 Pause(&event);
1945 HandleSteppingRequest(); // we may get a rewind request
1946 ClearCachedStackTraces();
1947}
1948
1949// Helper that refines the resolved token pos.
1950//
1951// If |requested_column| is specified, then |exact_token_pos| must be the exact
1952// position where the user requested a column breakpoint to be set. If
1953// |requested_column| is |-1|, |exact_token_pos| must be
1954// |TokenPosition::kNoSource|.
1957 TokenPosition next_closest_token_position,
1958 TokenPosition requested_token_pos,
1959 TokenPosition last_token_pos,
1960 intptr_t requested_column,
1961 TokenPosition exact_token_pos,
1962 TokenPosition* best_fit_pos,
1963 intptr_t* best_column,
1964 intptr_t* best_line) {
1965 ASSERT(
1966 (requested_column == -1 && exact_token_pos == TokenPosition::kNoSource) ||
1967 (requested_column > -1 && exact_token_pos != TokenPosition::kNoSource));
1968
1969 intptr_t token_start_column = -1;
1970 intptr_t token_line = -1;
1971 if (requested_column >= 0) {
1972 TokenPosition ignored = TokenPosition::kNoSource;
1973 TokenPosition end_of_line_pos = TokenPosition::kNoSource;
1974 script.GetTokenLocation(pos, &token_line, &token_start_column);
1975 script.TokenRangeAtLine(token_line, &ignored, &end_of_line_pos);
1976
1977 TokenPosition token_end_pos = TokenPosition::Min(
1978 TokenPosition::Deserialize(next_closest_token_position.Pos() - 1),
1979 end_of_line_pos);
1980
1981 if ((token_end_pos.IsReal() && exact_token_pos.IsReal() &&
1982 (token_end_pos < exact_token_pos)) ||
1983 (token_start_column > *best_column)) {
1984 // We prefer the token with the lowest column number compatible with the
1985 // requested column. The current token under consideration either ends
1986 // before the requested column, and is thus incompatible with it, or
1987 // has a higher column number than the best token we've found so far, so
1988 // we reject the current token under consideration.
1989 return;
1990 }
1991 }
1992
1993 // Prefer the lowest (first) token pos.
1994 if (pos < *best_fit_pos) {
1995 *best_fit_pos = pos;
1996 *best_line = token_line;
1997 *best_column = token_start_column;
1998 }
1999}
2000
2001// Returns the best fit token position for a breakpoint.
2002//
2003// Takes a range of tokens [requested_token_pos, last_token_pos] and
2004// an optional column (requested_column). The range of tokens usually
2005// represents one line of the program text, but can represent a larger
2006// range on recursive calls.
2007//
2008// If |requested_column| is specified, then |exact_token_pos| must be the exact
2009// position where the user requested a column breakpoint to be set. If
2010// |requested_column| is |-1|, |exact_token_pos| must be
2011// |TokenPosition::kNoSource|.
2012//
2013// The best fit is found in two passes.
2014//
2015// The first pass finds a candidate token which:
2016//
2017// - is a safepoint,
2018// - has the lowest column number compatible with the requested column
2019// if a column has been specified,
2020// and:
2021// - has the lowest token position number which satisfies the above.
2022//
2023// When we consider a column number, we look for the token which
2024// intersects the desired column. For example:
2025//
2026// 1 2 3
2027// 12345678901234567890 0
2028//
2029// var x = function(function(y));
2030// ^
2031//
2032// If we request a breakpoint at column 14, the lowest column number
2033// compatible with that would for column 11 (beginning of the
2034// 'function' token) in the example above.
2035//
2036// Once this candidate token from the first pass is found, we then
2037// have a second pass which considers only those tokens on the same
2038// line as the candidate token.
2039//
2040// The second pass finds a best fit token which:
2041//
2042// - is a safepoint,
2043// - has the same column number as the candidate token (perhaps
2044// more than one token has the same column number),
2045// and:
2046// - has the lowest code address in the generated code.
2047//
2048// We prefer the lowest compiled code address, because this tends to
2049// select the first subexpression on a line. For example in a line
2050// with nested function calls f(g(x)), the call to g() will have a
2051// lower compiled code address than the call to f().
2052//
2053// If no best fit token can be found, the search is expanded,
2054// searching through the rest of the current function by calling this
2055// function recursively.
2056//
2057// TODO(turnidge): Given that we usually call this function with a
2058// token range restricted to a single line, this could be a one-pass
2059// algorithm, which would be simpler. I believe that it only needs
2060// two passes to support the recursive try-the-whole-function case.
2061// Rewrite this later, once there are more tests in place.
2063 TokenPosition requested_token_pos,
2064 TokenPosition last_token_pos,
2065 intptr_t requested_column,
2066 TokenPosition exact_token_pos) {
2067 ASSERT(!func.HasOptimizedCode());
2068 ASSERT(
2069 (requested_column == -1 && exact_token_pos == TokenPosition::kNoSource) ||
2070 (requested_column > -1 && exact_token_pos != TokenPosition::kNoSource));
2071
2072 requested_token_pos =
2073 TokenPosition::Max(requested_token_pos, func.token_pos());
2074 last_token_pos = TokenPosition::Min(last_token_pos, func.end_token_pos());
2075
2076 Zone* zone = Thread::Current()->zone();
2077 Script& script = Script::Handle(zone, func.script());
2078 Code& code = Code::Handle(zone);
2079 PcDescriptors& desc = PcDescriptors::Handle(zone);
2080 ASSERT(func.HasCode());
2081 code = func.unoptimized_code();
2082 ASSERT(!code.IsNull());
2083 desc = code.pc_descriptors();
2084
2085 // First pass: find the safe point which is closest to the beginning
2086 // of the given token range.
2087 TokenPosition best_fit_pos = TokenPosition::kMaxSource;
2088 intptr_t best_column = INT_MAX;
2089 intptr_t best_line = INT_MAX;
2090
2092 while (iter.MoveNext()) {
2093 const TokenPosition& pos = iter.TokenPos();
2094 if (pos.IsSynthetic() && pos == requested_token_pos) {
2095 // if there's a safepoint for a synthetic function start and the start
2096 // was requested, we're done.
2097 return pos;
2098 }
2099 if (!pos.IsWithin(requested_token_pos, last_token_pos)) {
2100 // Token is not in the target range.
2101 continue;
2102 }
2103 TokenPosition next_closest_token_position = TokenPosition::kMaxSource;
2104 if (requested_column >= 0) {
2105 // Find next closest safepoint
2107 while (iter2.MoveNext()) {
2108 const TokenPosition& next = iter2.TokenPos();
2109 if (!next.IsReal()) continue;
2110 if ((pos < next) && (next < next_closest_token_position)) {
2111 next_closest_token_position = next;
2112 }
2113 }
2114 }
2115 RefineBreakpointPos(script, pos, next_closest_token_position,
2116 requested_token_pos, last_token_pos, requested_column,
2117 exact_token_pos, &best_fit_pos, &best_column,
2118 &best_line);
2119 }
2120
2121 // Second pass (if we found a safe point in the first pass). Find
2122 // the token on the line which is at the best fit column (if column
2123 // was specified) and has the lowest code address.
2124 if (best_fit_pos != TokenPosition::kMaxSource) {
2125 ASSERT(best_fit_pos.IsReal());
2126 const Script& script = Script::Handle(zone, func.script());
2127 const TokenPosition begin_pos = best_fit_pos;
2128
2129 TokenPosition end_of_line_pos = TokenPosition::kNoSource;
2130 if (best_line < 0) {
2131 script.GetTokenLocation(begin_pos, &best_line);
2132 }
2133 ASSERT(best_line > 0);
2134 TokenPosition ignored = TokenPosition::kNoSource;
2135 script.TokenRangeAtLine(best_line, &ignored, &end_of_line_pos);
2136 end_of_line_pos = TokenPosition::Max(end_of_line_pos, begin_pos);
2137
2138 uword lowest_pc_offset = kUwordMax;
2140 while (iter.MoveNext()) {
2141 const TokenPosition& pos = iter.TokenPos();
2142 if (requested_column >= 0) {
2143 if (pos != best_fit_pos) {
2144 // Not an match for the requested column.
2145 continue;
2146 }
2147 } else if (!pos.IsWithin(begin_pos, end_of_line_pos)) {
2148 // Token is not on same line as best fit.
2149 continue;
2150 }
2151
2152 // Prefer the lowest pc offset.
2153 if (iter.PcOffset() < lowest_pc_offset) {
2154 lowest_pc_offset = iter.PcOffset();
2155 best_fit_pos = pos;
2156 }
2157 }
2158 return best_fit_pos;
2159 }
2160
2161 // We didn't find a safe point in the given token range. Try and
2162 // find a safe point in the remaining source code of the function.
2163 // Since we have moved to the next line of the function, we no
2164 // longer are requesting a specific column number.
2165 if (last_token_pos < func.end_token_pos()) {
2166 return ResolveBreakpointPos(func, last_token_pos, func.end_token_pos(),
2167 -1 /* no column */, TokenPosition::kNoSource);
2168 }
2169 return TokenPosition::kNoSource;
2170}
2171
2172bool BreakpointLocation::EnsureIsResolved(const Function& target_function,
2173 TokenPosition exact_token_pos) {
2174 if (IsResolved()) {
2175 return true;
2176 }
2177
2178 // Resolve source breakpoint in the newly compiled function.
2179 TokenPosition resolved_pos =
2180 ResolveBreakpointPos(target_function, token_pos(), end_token_pos(),
2181 requested_column_number(), exact_token_pos);
2182 if (!resolved_pos.IsDebugPause()) {
2183 if (FLAG_verbose_debug) {
2184 OS::PrintErr("Failed resolving breakpoint for function '%s'\n",
2185 target_function.ToFullyQualifiedCString());
2186 }
2187 return false;
2188 }
2189 TokenPosition requested_pos = token_pos();
2190 TokenPosition requested_end_pos = end_token_pos();
2191 SetResolved(target_function, resolved_pos);
2192 Breakpoint* breakpoint = breakpoints();
2193 while (breakpoint != nullptr) {
2194 if (FLAG_verbose_debug) {
2195 OS::PrintErr("Resolved breakpoint %" Pd
2196 " to pos %s, function '%s' (requested range %s-%s, "
2197 "requested col %" Pd ")\n",
2198 breakpoint->id(), token_pos().ToCString(),
2199 target_function.ToFullyQualifiedCString(),
2200 requested_pos.ToCString(), requested_end_pos.ToCString(),
2201 requested_column_number());
2202 }
2203 debugger()->SendBreakpointEvent(ServiceEvent::kBreakpointResolved,
2204 breakpoint);
2205 breakpoint = breakpoint->next();
2206 }
2207
2208 return true;
2209}
2210
2211void GroupDebugger::MakeCodeBreakpointAtUnsafe(const Function& func,
2212 BreakpointLocation* loc) {
2213 DEBUG_ASSERT(Thread::Current()->IsInStoppedMutatorsScope() ||
2214 code_breakpoints_lock()->IsCurrentThreadWriter());
2215
2216 ASSERT(loc->token_pos().IsReal());
2217 ASSERT((loc != nullptr) && loc->IsResolved());
2218 ASSERT(!func.HasOptimizedCode());
2219 ASSERT(func.HasCode());
2220 Code& code = Code::Handle(func.unoptimized_code());
2221 ASSERT(!code.IsNull());
2222 PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
2223 uword lowest_pc_offset = kUwordMax;
2224 UntaggedPcDescriptors::Kind lowest_kind = UntaggedPcDescriptors::kAnyKind;
2225 // Find the safe point with the lowest compiled code address
2226 // that maps to the token position of the source breakpoint.
2228 while (iter.MoveNext()) {
2229 if (iter.TokenPos() == loc->token_pos_) {
2230 if (iter.PcOffset() < lowest_pc_offset) {
2231 lowest_pc_offset = iter.PcOffset();
2232 lowest_kind = iter.Kind();
2233 }
2234 }
2235 }
2236 if (lowest_pc_offset == kUwordMax) {
2237 return;
2238 }
2239
2240 uword lowest_pc = code.PayloadStart() + lowest_pc_offset;
2241 CodeBreakpoint* code_bpt = GetCodeBreakpoint(lowest_pc);
2242 if (code_bpt == nullptr) {
2243 // No code breakpoint for this code exists; create one.
2244 code_bpt = new CodeBreakpoint(code, loc, lowest_pc, lowest_kind);
2245 if (FLAG_verbose_debug) {
2246 OS::PrintErr("Setting code breakpoint at pos %s pc %#" Px " offset %#" Px
2247 "\n",
2248 loc->token_pos().ToCString(), lowest_pc,
2249 lowest_pc - code.PayloadStart());
2250 }
2251 RegisterCodeBreakpoint(code_bpt);
2252 } else {
2253 if (FLAG_verbose_debug) {
2254 OS::PrintErr(
2255 "Adding location to existing code breakpoint at pos %s pc %#" Px
2256 " offset %#" Px "\n",
2257 loc->token_pos().ToCString(), lowest_pc,
2258 lowest_pc - code.PayloadStart());
2259 }
2260 if (!code_bpt->HasBreakpointLocation(loc)) {
2261 code_bpt->AddBreakpointLocation(loc);
2262 }
2263 }
2264 if (loc->AnyEnabled()) {
2265 code_bpt->Enable();
2266 }
2267}
2268
2269void GroupDebugger::MakeCodeBreakpointAt(const Function& func,
2270 BreakpointLocation* loc) {
2271 auto thread = Thread::Current();
2272 if (thread->IsInStoppedMutatorsScope()) {
2273 MakeCodeBreakpointAtUnsafe(func, loc);
2274 } else {
2275 SafepointWriteRwLocker sl(thread, code_breakpoints_lock());
2276 MakeCodeBreakpointAtUnsafe(func, loc);
2277 }
2278}
2279
2280void Debugger::FindCompiledFunctions(
2282 TokenPosition start_pos,
2283 TokenPosition end_pos,
2284 GrowableObjectArray* code_function_list) {
2285 auto thread = Thread::Current();
2286 auto zone = thread->zone();
2287 Script& script = Script::Handle(zone);
2288 for (intptr_t i = 0; i < scripts.length(); ++i) {
2289 script = scripts.At(i).ptr();
2290 ClosureFunctionsCache::ForAllClosureFunctions(
2291 [&](const Function& function) {
2292 ASSERT(!function.IsNull());
2293 if ((function.token_pos() == start_pos) &&
2294 (function.end_token_pos() == end_pos) &&
2295 (function.script() == script.ptr())) {
2296 if (function.is_debuggable() && function.HasCode()) {
2297 code_function_list->Add(function);
2298 }
2299 ASSERT(!function.HasImplicitClosureFunction());
2300 }
2301 return true; // Continue iteration.
2302 });
2303
2304 Class& cls = Class::Handle(zone);
2305 Array& functions = Array::Handle(zone);
2306 Function& function = Function::Handle(zone);
2307 Array& fields = Array::Handle(zone);
2308 Field& field = Field::Handle(zone);
2309
2310 const ClassTable& class_table = *isolate_->group()->class_table();
2311 const intptr_t num_classes = class_table.NumCids();
2312 const intptr_t num_tlc_classes = class_table.NumTopLevelCids();
2313 for (intptr_t i = 1; i < num_classes + num_tlc_classes; i++) {
2314 const intptr_t cid =
2315 i < num_classes ? i
2316 : ClassTable::CidFromTopLevelIndex(i - num_classes);
2317 if (class_table.HasValidClassAt(cid)) {
2318 cls = class_table.At(cid);
2319 // If the class is not finalized, e.g. if it hasn't been parsed
2320 // yet entirely, we can ignore it. If it contains a function with
2321 // an unresolved breakpoint, we will detect it if and when the
2322 // function gets compiled.
2323 if (!cls.is_finalized()) {
2324 continue;
2325 }
2326 // Note: we need to check the functions of this class even if
2327 // the class is defined in a different 'script'. There could
2328 // be mixin functions from the given script in this class.
2329 functions = cls.current_functions();
2330 if (!functions.IsNull()) {
2331 const intptr_t num_functions = functions.Length();
2332 for (intptr_t pos = 0; pos < num_functions; pos++) {
2333 function ^= functions.At(pos);
2334 ASSERT(!function.IsNull());
2335 bool function_added = false;
2336 if (function.is_debuggable() && function.HasCode() &&
2337 function.token_pos() == start_pos &&
2338 function.end_token_pos() == end_pos &&
2339 function.script() == script.ptr()) {
2340 code_function_list->Add(function);
2341 function_added = true;
2342 }
2343 if (function_added && function.HasImplicitClosureFunction()) {
2344 function = function.ImplicitClosureFunction();
2345 if (function.is_debuggable() && function.HasCode()) {
2346 code_function_list->Add(function);
2347 }
2348 }
2349 }
2350 }
2351 fields = cls.fields();
2352 if (!fields.IsNull()) {
2353 const intptr_t num_fields = fields.Length();
2354 for (intptr_t pos = 0; pos < num_fields; pos++) {
2355 field ^= fields.At(pos);
2356 ASSERT(!field.IsNull());
2357 if (field.Script() != script.ptr()) {
2358 continue;
2359 }
2360 if (!field.has_nontrivial_initializer()) {
2361 continue;
2362 }
2363 function = field.EnsureInitializerFunction();
2364 ASSERT(!function.IsNull());
2365 if (function.is_debuggable() && function.HasCode() &&
2366 function.token_pos() == start_pos &&
2367 function.end_token_pos() == end_pos &&
2368 function.script() == script.ptr()) {
2369 code_function_list->Add(function);
2370 }
2371 }
2372 }
2373 }
2374 }
2375 }
2376}
2377
2378static void UpdateBestFit(Function* best_fit, const Function& func) {
2379 if (best_fit->IsNull()) {
2380 *best_fit = func.ptr();
2381 } else if ((best_fit->token_pos().IsSynthetic() ||
2382 func.token_pos().IsSynthetic() ||
2383 (best_fit->token_pos() < func.token_pos())) &&
2384 (func.end_token_pos() <= best_fit->end_token_pos())) {
2385 *best_fit = func.ptr();
2386 }
2387}
2388
2389// Returns true if a best fit is found. A best fit can either be a function
2390// or a field. If it is a function, then the best fit function is returned
2391// in |best_fit|. If a best fit is a field, it means that a latent
2392// breakpoint can be set in the range |token_pos| to |last_token_pos|.
2393bool Debugger::FindBestFit(const Script& script,
2394 TokenPosition token_pos,
2395 TokenPosition last_token_pos,
2396 Function* best_fit) {
2397 auto thread = Thread::Current();
2398 auto isolate_group = thread->isolate_group();
2399 Zone* zone = thread->zone();
2400 Class& cls = Class::Handle(zone);
2401
2402 // A single script can belong to several libraries because of mixins.
2403 // Go through all libraries and for each that contains the script, try to find
2404 // a fit there.
2405 // Return the first fit found, but if a library doesn't contain a fit,
2406 // process the next one.
2407 const GrowableObjectArray& libs = GrowableObjectArray::Handle(
2408 zone, isolate_group->object_store()->libraries());
2409 Library& lib = Library::Handle(zone);
2410 for (int i = 0; i < libs.Length(); i++) {
2411 lib ^= libs.At(i);
2412 ASSERT(!lib.IsNull());
2413 const Array& scripts = Array::Handle(zone, lib.LoadedScripts());
2414 bool lib_has_script = false;
2415 for (intptr_t j = 0; j < scripts.Length(); j++) {
2416 if (scripts.At(j) == script.ptr()) {
2417 lib_has_script = true;
2418 break;
2419 }
2420 }
2421 if (!lib_has_script) {
2422 continue;
2423 }
2424
2425 if (!lib.IsDebuggable()) {
2426 if (FLAG_verbose_debug) {
2427 OS::PrintErr("Library '%s' has been marked as non-debuggable\n",
2428 lib.ToCString());
2429 }
2430 continue;
2431 }
2432
2433 const String& script_url = String::Handle(zone, script.url());
2434 ClosureFunctionsCache::ForAllClosureFunctions([&](const Function& fun) {
2435 if (fun.script() == script.ptr() &&
2436 FunctionOverlaps(fun, script_url, token_pos, last_token_pos)) {
2437 // Select the inner most closure.
2438 UpdateBestFit(best_fit, fun);
2439 }
2440 return true; // Continue iteration
2441 });
2442
2443 if (!best_fit->IsNull()) {
2444 // The inner most closure found will be the best fit. Going
2445 // over class functions below will not help in any further
2446 // narrowing.
2447 return true;
2448 }
2449
2450 Array& functions = Array::Handle(zone);
2451 Function& function = Function::Handle(zone);
2452 Array& fields = Array::Handle(zone);
2453 Field& field = Field::Handle(zone);
2454 Error& error = Error::Handle(zone);
2455
2456 const ClassTable& class_table = *isolate_->group()->class_table();
2457 const intptr_t num_classes = class_table.NumCids();
2458 const intptr_t num_tlc_classes = class_table.NumTopLevelCids();
2459 for (intptr_t i = 1; i < num_classes + num_tlc_classes; i++) {
2460 const intptr_t cid =
2461 i < num_classes ? i
2462 : ClassTable::CidFromTopLevelIndex(i - num_classes);
2463 if (!class_table.HasValidClassAt(cid)) {
2464 continue;
2465 }
2466 cls = class_table.At(cid);
2467 // This class is relevant to us only if it belongs to the
2468 // library to which |script| belongs.
2469 if (cls.library() != lib.ptr()) {
2470 continue;
2471 }
2472 // Parse class definition if not done yet.
2473 error = cls.EnsureIsFinalized(Thread::Current());
2474 if (!error.IsNull()) {
2475 // Ignore functions in this class.
2476 // TODO(hausner): Should we propagate this error? How?
2477 // EnsureIsFinalized only returns an error object if there
2478 // is no longjump base on the stack.
2479 continue;
2480 }
2481 functions = cls.current_functions();
2482 if (!functions.IsNull()) {
2483 const intptr_t num_functions = functions.Length();
2484 for (intptr_t pos = 0; pos < num_functions; pos++) {
2485 function ^= functions.At(pos);
2486 ASSERT(!function.IsNull());
2488 // Implicit functions do not have a user specifiable source
2489 // location.
2490 continue;
2491 }
2492 if (FunctionOverlaps(function, script_url, token_pos,
2493 last_token_pos)) {
2494 // Closures and inner functions within a class method are not
2495 // present in the functions of a class. Hence, we can return
2496 // right away as looking through other functions of a class
2497 // will not narrow down to any inner function/closure.
2498 *best_fit = function.ptr();
2499 return true;
2500 }
2501 }
2502 }
2503 // If none of the functions in the class contain token_pos, then we check
2504 // if it falls within a function literal initializer of a field.
2505 fields = cls.fields();
2506 if (!fields.IsNull()) {
2507 const intptr_t num_fields = fields.Length();
2508 for (intptr_t pos = 0; pos < num_fields; pos++) {
2509 TokenPosition start = TokenPosition::kNoSource;
2510 TokenPosition end = TokenPosition::kNoSource;
2511 field ^= fields.At(pos);
2512 ASSERT(!field.IsNull());
2513 if (field.Script() != script.ptr()) {
2514 // The field should be defined in the script we want to set
2515 // the breakpoint in.
2516 continue;
2517 }
2518 if (!field.has_nontrivial_initializer()) {
2519 continue;
2520 }
2521 start = field.token_pos();
2522 end = field.end_token_pos();
2523 if (token_pos.IsWithin(start, end) ||
2524 start.IsWithin(token_pos, last_token_pos)) {
2525 *best_fit = field.InitializerFunction();
2526 return true;
2527 }
2528 }
2529 }
2530 }
2531 }
2532 return false;
2533}
2534
2535// If |requested_column| is specified, then |exact_token_pos| must be the exact
2536// position where the user requested a column breakpoint to be set. If
2537// |requested_column| is |-1|, |exact_token_pos| must be
2538// |TokenPosition::kNoSource|.
2539BreakpointLocation* Debugger::SetCodeBreakpoints(
2540 const GrowableHandlePtrArray<const Script>& scripts,
2541 TokenPosition token_pos,
2542 TokenPosition last_token_pos,
2543 intptr_t requested_line,
2544 intptr_t requested_column,
2545 TokenPosition exact_token_pos,
2546 const GrowableObjectArray& functions) {
2547 ASSERT(
2548 (requested_column == -1 && exact_token_pos == TokenPosition::kNoSource) ||
2549 (requested_column > -1 && exact_token_pos != TokenPosition::kNoSource));
2550
2551 Function& function = Function::Handle();
2552 function ^= functions.At(0);
2553 TokenPosition breakpoint_pos = ResolveBreakpointPos(
2554 function, token_pos, last_token_pos, requested_column, exact_token_pos);
2555 if (!breakpoint_pos.IsReal()) {
2556 return nullptr;
2557 }
2558 const String& script_url = String::Handle(scripts.At(0).url());
2559 BreakpointLocation* loc =
2560 GetResolvedBreakpointLocation(script_url, breakpoint_pos);
2561 if (loc == nullptr) {
2562 // Find an existing unresolved breakpoint location.
2563 loc = GetBreakpointLocation(script_url, token_pos, requested_line,
2564 requested_column);
2565 }
2566 if (loc == nullptr) {
2567 loc = new BreakpointLocation(this, scripts, breakpoint_pos, breakpoint_pos,
2568 requested_line, requested_column);
2569 RegisterBreakpointLocation(loc);
2570 }
2571 // A source breakpoint for this location may already exists, but it may
2572 // not yet be resolved in code.
2573 if (loc->IsResolved()) {
2574 return loc;
2575 }
2576 loc->SetResolved(function, breakpoint_pos);
2577
2578 // Create code breakpoints for all compiled functions we found.
2579 Function& func = Function::Handle();
2580 const intptr_t num_functions = functions.Length();
2581 for (intptr_t i = 0; i < num_functions; i++) {
2582 func ^= functions.At(i);
2583 ASSERT(func.HasCode());
2584 group_debugger()->MakeCodeBreakpointAt(func, loc);
2585 }
2586 if (FLAG_verbose_debug) {
2587 intptr_t line_number = -1;
2588 intptr_t column_number = -1;
2589 scripts.At(0).GetTokenLocation(breakpoint_pos, &line_number,
2590 &column_number);
2591 OS::PrintErr("Resolved code breakpoint for function '%s' at line %" Pd
2592 " col %" Pd "\n",
2593 func.ToFullyQualifiedCString(), line_number, column_number);
2594 }
2595 return loc;
2596}
2597
2598#if !defined(DART_PRECOMPILED_RUNTIME)
2599static TokenPosition FindExactTokenPosition(const Script& script,
2600 TokenPosition start_of_line,
2601 intptr_t column_number);
2602#endif // !defined(DART_PRECOMPILED_RUNTIME)
2603
2604BreakpointLocation* Debugger::SetBreakpoint(const Script& script,
2605 TokenPosition token_pos,
2606 TokenPosition last_token_pos,
2607 intptr_t requested_line,
2608 intptr_t requested_column,
2609 const Function& function) {
2610 GrowableHandlePtrArray<const Script> scripts(Thread::Current()->zone(), 1);
2611 scripts.Add(script);
2612 return SetBreakpoint(scripts, token_pos, last_token_pos, requested_line,
2613 requested_column, function);
2614}
2615
2616BreakpointLocation* Debugger::SetBreakpoint(
2617 const GrowableHandlePtrArray<const Script>& scripts,
2618 TokenPosition token_pos,
2619 TokenPosition last_token_pos,
2620 intptr_t requested_line,
2621 intptr_t requested_column,
2622 const Function& function) {
2623 Function& func = Function::Handle();
2624 const Script& script = scripts.At(0);
2625 if (function.IsNull()) {
2626 if (!FindBestFit(script, token_pos, last_token_pos, &func)) {
2627 return nullptr;
2628 }
2629 // If func was not set (still Null), the best fit is a field.
2630 } else {
2631 func = function.ptr();
2632 if (!func.token_pos().IsReal()) {
2633 return nullptr; // Missing source positions?
2634 }
2635 }
2636
2637 if (!func.IsNull()) {
2638 // There may be more than one function object for a given function
2639 // in source code. There may be implicit closure functions, and
2640 // there may be copies of mixin functions. Collect all compiled
2641 // functions whose source code range matches exactly the best fit
2642 // function we found.
2643 GrowableObjectArray& code_functions =
2644 GrowableObjectArray::Handle(GrowableObjectArray::New());
2645 FindCompiledFunctions(scripts, func.token_pos(), func.end_token_pos(),
2646 &code_functions);
2647
2648 if (code_functions.Length() > 0) {
2649 // One or more function object containing this breakpoint location
2650 // have already been compiled. We can resolve the breakpoint now.
2651 // If requested_column is larger than zero, [token_pos, last_token_pos]
2652 // governs one single line of code.
2653 TokenPosition exact_token_pos = TokenPosition::kNoSource;
2654#if !defined(DART_PRECOMPILED_RUNTIME)
2655 if (token_pos != last_token_pos && requested_column >= 0) {
2656 exact_token_pos =
2657 FindExactTokenPosition(script, token_pos, requested_column);
2658 }
2659#endif // !defined(DART_PRECOMPILED_RUNTIME)
2660 BreakpointLocation* loc = nullptr;
2661 // Ensure that code stays deoptimized (and background compiler disabled)
2662 // until we have installed the breakpoint (at which point the compiler
2663 // will not try to optimize it anymore).
2664 RunWithStoppedDeoptimizedWorld([&] {
2665 loc = SetCodeBreakpoints(scripts, token_pos, last_token_pos,
2666 requested_line, requested_column,
2667 exact_token_pos, code_functions);
2668 });
2669 if (loc != nullptr) {
2670 return loc;
2671 }
2672 }
2673 }
2674 // There is either an uncompiled function, or an uncompiled function literal
2675 // initializer of a field at |token_pos|. Hence, Register an unresolved
2676 // breakpoint.
2677 if (FLAG_verbose_debug) {
2678 intptr_t line_number = -1;
2679 intptr_t column_number = -1;
2680 script.GetTokenLocation(token_pos, &line_number, &column_number);
2681 if (func.IsNull()) {
2682 OS::PrintErr(
2683 "Registering pending breakpoint for "
2684 "an uncompiled function literal at line %" Pd " col %" Pd "\n",
2685 line_number, column_number);
2686 } else {
2687 OS::PrintErr(
2688 "Registering pending breakpoint for "
2689 "uncompiled function '%s' at line %" Pd " col %" Pd "\n",
2690 func.ToFullyQualifiedCString(), line_number, column_number);
2691 }
2692 }
2693 const String& script_url = String::Handle(script.url());
2694 BreakpointLocation* loc = GetBreakpointLocation(
2695 script_url, token_pos, requested_line, requested_column);
2696 if (loc == nullptr) {
2697 loc = new BreakpointLocation(this, scripts, token_pos, last_token_pos,
2698 requested_line, requested_column);
2699 RegisterBreakpointLocation(loc);
2700 }
2701 return loc;
2702}
2703
2704// Synchronize the enabled/disabled state of all code breakpoints
2705// associated with the breakpoint location loc.
2706void GroupDebugger::SyncBreakpointLocation(BreakpointLocation* loc) {
2707 bool any_enabled = loc->AnyEnabled();
2708 SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
2709 CodeBreakpoint* cbpt = code_breakpoints_;
2710 while (cbpt != nullptr) {
2711 if (cbpt->HasBreakpointLocation(loc)) {
2712 if (any_enabled) {
2713 cbpt->Enable();
2714 } else {
2715 cbpt->Disable();
2716 }
2717 }
2718 cbpt = cbpt->next();
2719 }
2720}
2721
2722Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function,
2723 bool single_shot) {
2724 ASSERT(!target_function.IsNull());
2725 if (!target_function.is_debuggable()) {
2726 return nullptr;
2727 }
2728 const Script& script = Script::Handle(target_function.script());
2729 BreakpointLocation* bpt_location = SetBreakpoint(
2730 script, target_function.token_pos(), target_function.end_token_pos(), -1,
2731 -1 /* no requested line/col */, target_function);
2732 if (bpt_location == nullptr) {
2733 return nullptr;
2734 }
2735
2736 if (single_shot) {
2737 return bpt_location->AddSingleShot(this);
2738 } else {
2739 return bpt_location->AddRepeated(this);
2740 }
2741}
2742
2743Breakpoint* Debugger::SetBreakpointAtActivation(const Instance& closure,
2744 bool single_shot) {
2745 if (!closure.IsClosure()) {
2746 return nullptr;
2747 }
2748 const Function& func = Function::Handle(Closure::Cast(closure).function());
2749 const Script& script = Script::Handle(func.script());
2750 BreakpointLocation* bpt_location =
2751 SetBreakpoint(script, func.token_pos(), func.end_token_pos(), -1,
2752 -1 /* no line/col */, func);
2753 return bpt_location->AddBreakpoint(this, Closure::Cast(closure), single_shot);
2754}
2755
2756Breakpoint* Debugger::BreakpointAtActivation(const Instance& closure) {
2757 if (!closure.IsClosure()) {
2758 return nullptr;
2759 }
2760
2761 BreakpointLocation* loc = breakpoint_locations_;
2762 while (loc != nullptr) {
2763 Breakpoint* bpt = loc->breakpoints();
2764 while (bpt != nullptr) {
2765 if (closure.ptr() == bpt->closure()) {
2766 return bpt;
2767 }
2768 bpt = bpt->next();
2769 }
2770 loc = loc->next();
2771 }
2772
2773 return nullptr;
2774}
2775
2776void Debugger::SetBreakpointAtResumption(const Object& function_data) {
2777 ASSERT(!function_data.IsNull());
2778 ASSERT(function_data.IsInstance());
2779 breakpoints_at_resumption_.Add(function_data.ptr());
2780 isolate_->set_has_resumption_breakpoints(true);
2781}
2782
2783void Debugger::ResumptionBreakpoint() {
2784 ASSERT(!breakpoints_at_resumption_.is_empty());
2785 ASSERT(isolate_->has_resumption_breakpoints());
2786
2787 ActivationFrame* top_frame = TopDartFrame();
2788 ASSERT(top_frame->function().IsSuspendableFunction());
2789 const auto& function_data =
2790 Object::Handle(top_frame->GetSuspendableFunctionData());
2791
2792 for (intptr_t i = 0, n = breakpoints_at_resumption_.length(); i < n; ++i) {
2793 if (breakpoints_at_resumption_[i] == function_data.ptr()) {
2794 breakpoints_at_resumption_.RemoveAt(i);
2795 if (breakpoints_at_resumption_.is_empty()) {
2796 isolate_->set_has_resumption_breakpoints(false);
2797 }
2798 if (FLAG_verbose_debug) {
2799 OS::PrintErr(
2800 "ResumptionBreakpoint - hit a breakpoint, continue single "
2801 "stepping\n");
2802 }
2803 EnterSingleStepMode();
2804 return;
2805 }
2806 }
2807}
2808
2809Breakpoint* Debugger::SetBreakpointAtLine(const String& script_url,
2810 intptr_t line_number) {
2811 // Prevent future tests from calling this function in the wrong
2812 // execution state. If you hit this assert, consider using
2813 // Dart_SetBreakpoint instead.
2814 ASSERT(Thread::Current()->execution_state() == Thread::kThreadInVM);
2815
2816 BreakpointLocation* loc =
2817 BreakpointLocationAtLineCol(script_url, line_number, -1 /* no column */);
2818 if (loc != nullptr) {
2819 return loc->AddRepeated(this);
2820 }
2821 return nullptr;
2822}
2823
2824Breakpoint* Debugger::SetBreakpointAtLineCol(const String& script_url,
2825 intptr_t line_number,
2826 intptr_t column_number) {
2827 // Prevent future tests from calling this function in the wrong
2828 // execution state. If you hit this assert, consider using
2829 // Dart_SetBreakpoint instead.
2830 ASSERT(Thread::Current()->execution_state() == Thread::kThreadInVM);
2831
2832 BreakpointLocation* loc =
2833 BreakpointLocationAtLineCol(script_url, line_number, column_number);
2834 if (loc != nullptr) {
2835 return loc->AddRepeated(this);
2836 }
2837 return nullptr;
2838}
2839
2840BreakpointLocation* Debugger::BreakpointLocationAtLineCol(
2841 const String& script_url,
2842 intptr_t line_number,
2843 intptr_t column_number) {
2844 Zone* zone = Thread::Current()->zone();
2845 Library& lib = Library::Handle(zone);
2847 const GrowableObjectArray& libs = GrowableObjectArray::Handle(
2848 isolate_->group()->object_store()->libraries());
2849 bool is_package = script_url.StartsWith(Symbols::PackageScheme());
2850 bool is_dart_colon = script_url.StartsWith(Symbols::DartScheme());
2851 Script& script_for_lib = Script::Handle(zone);
2852 for (intptr_t i = 0; i < libs.Length(); i++) {
2853 lib ^= libs.At(i);
2854 // Ensure that all top-level members are loaded so their scripts
2855 // are available for look up. When certain script only contains
2856 // top level functions, scripts could still be loaded correctly.
2858 bool useResolvedUri = !is_package && !is_dart_colon;
2859 script_for_lib = lib.LookupScript(script_url, useResolvedUri);
2860 if (!script_for_lib.IsNull()) {
2861 scripts.Add(script_for_lib);
2862 }
2863 }
2864 if (scripts.length() == 0) {
2865 // No script found with given url. Create a latent breakpoint which
2866 // will be set if the url is loaded later.
2867 BreakpointLocation* latent_bpt =
2868 GetLatentBreakpoint(script_url, line_number, column_number);
2869 if (FLAG_verbose_debug) {
2870 OS::PrintErr(
2871 "Set latent breakpoint in url '%s' at "
2872 "line %" Pd " col %" Pd "\n",
2873 script_url.ToCString(), line_number, column_number);
2874 }
2875 return latent_bpt;
2876 }
2877 TokenPosition first_token_idx = TokenPosition::kNoSource;
2878 TokenPosition last_token_idx = TokenPosition::kNoSource;
2879 // Assume all scripts with the same URL have the same token positions.
2880 scripts.At(0).TokenRangeAtLine(line_number, &first_token_idx,
2881 &last_token_idx);
2882 if (!first_token_idx.IsReal()) {
2883 // Script does not contain the given line number.
2884 if (FLAG_verbose_debug) {
2885 OS::PrintErr("Script '%s' does not contain line number %" Pd "\n",
2886 script_url.ToCString(), line_number);
2887 }
2888 return nullptr;
2889 } else if (!last_token_idx.IsReal()) {
2890 // Line does not contain any tokens.
2891 if (FLAG_verbose_debug) {
2892 OS::PrintErr("No executable code at line %" Pd " in '%s'\n", line_number,
2893 script_url.ToCString());
2894 }
2895 return nullptr;
2896 }
2897
2898 BreakpointLocation* loc = nullptr;
2899 ASSERT(first_token_idx <= last_token_idx);
2900 while ((loc == nullptr) && (first_token_idx <= last_token_idx)) {
2901 loc = SetBreakpoint(scripts, first_token_idx, last_token_idx, line_number,
2902 column_number, Function::Handle());
2903 first_token_idx = first_token_idx.Next();
2904 }
2905 if ((loc == nullptr) && FLAG_verbose_debug) {
2906 OS::PrintErr("No executable code at line %" Pd " in '%s'\n", line_number,
2907 script_url.ToCString());
2908 }
2909 return loc;
2910}
2911
2912// Return innermost closure contained in 'function' that contains
2913// the given token position.
2914static FunctionPtr FindInnermostClosure(Zone* zone,
2915 const Function& function,
2916 TokenPosition token_pos) {
2917 ASSERT(function.end_token_pos().IsReal());
2918 const TokenPosition& func_start = function.token_pos();
2919 const Script& outer_origin = Script::Handle(zone, function.script());
2920
2921 Function& best_fit = Function::Handle(zone);
2922 ClosureFunctionsCache::ForAllClosureFunctions([&](const Function& closure) {
2923 const TokenPosition& closure_start = closure.token_pos();
2924 const TokenPosition& closure_end = closure.end_token_pos();
2925 // We're only interested in closures that have real ending token positions.
2926 // The starting token position can be synthetic.
2927 if (closure_end.IsReal() && (function.end_token_pos() > closure_end) &&
2928 (!closure_start.IsReal() || !func_start.IsReal() ||
2929 (closure_start > func_start)) &&
2930 token_pos.IsWithin(closure_start, closure_end) &&
2931 (closure.script() == outer_origin.ptr())) {
2932 UpdateBestFit(&best_fit, closure);
2933 }
2934 return true; // Continue iteration.
2935 });
2936 return best_fit.ptr();
2937}
2938
2939bool GroupDebugger::EnsureLocationIsInFunction(Zone* zone,
2940 const Function& function,
2941 BreakpointLocation* location) {
2942 const String& url = String::Handle(zone, location->url());
2943 if (!FunctionOverlaps(function, url, location->token_pos(),
2944 location->end_token_pos())) {
2945 return false;
2946 }
2947
2948 TokenPosition token_pos = location->token_pos();
2949#if !defined(DART_PRECOMPILED_RUNTIME)
2950 TokenPosition end_token_pos = location->end_token_pos();
2951 if (token_pos != end_token_pos && location->requested_column_number() >= 0) {
2952 // Narrow down the token position range to a single value
2953 // if requested column number is provided so that inner
2954 // Closure won't be missed.
2955 const Script& script = Script::Handle(location->script());
2956 token_pos = FindExactTokenPosition(script, token_pos,
2957 location->requested_column_number());
2958 }
2959#endif // !defined(DART_PRECOMPILED_RUNTIME)
2960 const Function& inner_function =
2961 Function::Handle(zone, FindInnermostClosure(zone, function, token_pos));
2962 if (!inner_function.IsNull()) {
2963 if (FLAG_verbose_debug) {
2964 OS::PrintErr(
2965 "Pending breakpoint remains unresolved in "
2966 "inner function '%s'\n",
2967 inner_function.ToFullyQualifiedCString());
2968 }
2969 return false;
2970 }
2971
2972 // There is no local function within function that contains the
2973 // breakpoint token position.
2974 return true;
2975}
2976
2977void GroupDebugger::NotifyCompilation(const Function& function) {
2978 if (!function.is_debuggable()) {
2979 return;
2980 }
2981 Function& resolved_function = Function::Handle(function.ptr());
2982 auto thread = Thread::Current();
2983 auto zone = thread->zone();
2984
2985 // Going through BreakpointLocations of all isolates and debuggers looking
2986 // for those that can be resolved and added code breakpoints at now.
2987 //
2988 // The check below is used instead of breakpoint_locations_lock acquisition.
2989 // We don't need to acquire the lock if always run with stopped mutators.
2990 // We can't acquire the lock if we run with stopped mutators as that could
2991 // result in deadlock.
2992 RELEASE_ASSERT(thread->IsInStoppedMutatorsScope());
2993 for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
2994 BreakpointLocation* location = breakpoint_locations_.At(i);
2995 if (EnsureLocationIsInFunction(zone, resolved_function, location)) {
2996 // All mutators are stopped (see RELEASE_ASSERT above). We temporarily
2997 // enter the isolate for which the breakpoint was registered.
2998 // The code path below may issue service events which will use the active
2999 // isolate's object-id ring for naming VM objects.
3000 ActiveIsolateScope active_isolate(thread,
3001 location->debugger()->isolate());
3002
3003 // Ensure the location is resolved for the original function.
3004 TokenPosition exact_token_pos = TokenPosition::kNoSource;
3005#if !defined(DART_PRECOMPILED_RUNTIME)
3006 if (location->token_pos() != location->end_token_pos() &&
3007 location->requested_column_number() >= 0) {
3008 exact_token_pos = FindExactTokenPosition(
3009 Script::Handle(location->script()), location->token_pos(),
3010 location->requested_column_number());
3011 }
3012#endif // !defined(DART_PRECOMPILED_RUNTIME)
3013 location->EnsureIsResolved(function, exact_token_pos);
3014 if (FLAG_verbose_debug) {
3015 Breakpoint* bpt = location->breakpoints();
3016 while (bpt != nullptr) {
3017 OS::PrintErr("Setting breakpoint %" Pd " for %s '%s'\n", bpt->id(),
3018 function.IsClosureFunction() ? "closure" : "function",
3019 function.ToFullyQualifiedCString());
3020 bpt = bpt->next();
3021 }
3022 }
3023 MakeCodeBreakpointAt(function, location);
3024 }
3025 }
3026}
3027
3028void GroupDebugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
3029 CodeBreakpoint* cbpt = code_breakpoints_;
3030 while (cbpt != nullptr) {
3031 cbpt->VisitObjectPointers(visitor);
3032 cbpt = cbpt->next();
3033 }
3034}
3035
3036// static
3037void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
3038 ASSERT(visitor != nullptr);
3039 BreakpointLocation* loc = breakpoint_locations_;
3040 while (loc != nullptr) {
3041 loc->VisitObjectPointers(visitor);
3042 loc = loc->next();
3043 }
3044 loc = latent_locations_;
3045 while (loc != nullptr) {
3046 loc->VisitObjectPointers(visitor);
3047 loc = loc->next();
3048 }
3049 for (intptr_t i = 0, n = breakpoints_at_resumption_.length(); i < n; ++i) {
3050 visitor->VisitPointer(&breakpoints_at_resumption_[i]);
3051 }
3052}
3053
3055 ASSERT(event->IsPause()); // Should call InvokeEventHandler instead.
3056 ASSERT(!ignore_breakpoints_); // We shouldn't get here when ignoring bpts.
3057 ASSERT(!IsPaused()); // No recursive pausing.
3058
3059 pause_event_ = event;
3060 pause_event_->UpdateTimestamp();
3061
3062 // We are about to invoke the debugger's event handler. Disable
3063 // interrupts for this thread while waiting for debug commands over
3064 // the service protocol.
3065 {
3066 Thread* thread = Thread::Current();
3067 DisableThreadInterruptsScope dtis(thread);
3068 TIMELINE_DURATION(thread, Debugger, "Debugger Pause");
3069
3070 // Send the pause event.
3071 Service::HandleEvent(event);
3072
3073 {
3074 TransitionVMToNative transition(thread);
3075 isolate_->PauseEventHandler();
3076 }
3077
3078 // Notify the service that we have resumed.
3079 const Error& error = Error::Handle(Thread::Current()->sticky_error());
3080 ASSERT(error.IsNull() || error.IsUnwindError() ||
3081 error.IsUnhandledException());
3082
3083 // Only send a resume event when the isolate is not unwinding.
3084 if (!error.IsUnwindError()) {
3085 ServiceEvent resume_event(event->isolate(), ServiceEvent::kResume);
3086 resume_event.set_top_frame(event->top_frame());
3087 Service::HandleEvent(&resume_event);
3088 }
3089 }
3090
3091 group_debugger()->Pause();
3092 pause_event_ = nullptr;
3093}
3094
3096 SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
3097 if (needs_breakpoint_cleanup_) {
3098 RemoveUnlinkedCodeBreakpoints();
3099 }
3100}
3101
3102void Debugger::EnterSingleStepMode() {
3103 ResetSteppingFramePointer();
3104 NotifySingleStepping(true);
3105}
3106
3107void Debugger::ResetSteppingFramePointer() {
3108 stepping_fp_ = 0;
3109}
3110
3111void Debugger::SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace) {
3112 if (stack_trace->Length() > 0) {
3113 stepping_fp_ = stack_trace->FrameAt(0)->fp();
3114 } else {
3115 stepping_fp_ = 0;
3116 }
3117}
3118
3119void Debugger::HandleSteppingRequest(bool skip_next_step /* = false */) {
3120 ResetSteppingFramePointer();
3121 if (resume_action_ == kStepInto) {
3122 // When single stepping, we need to deoptimize because we might be
3123 // stepping into optimized code. This happens in particular if
3124 // the isolate has been interrupted, but can happen in other cases
3125 // as well. We need to deoptimize the world in case we are about
3126 // to call an optimized function.
3127 NotifySingleStepping(true);
3128 skip_next_step_ = skip_next_step;
3129 if (FLAG_verbose_debug) {
3130 OS::PrintErr("HandleSteppingRequest - kStepInto\n");
3131 }
3132 } else if (resume_action_ == kStepOver) {
3133 NotifySingleStepping(true);
3134 skip_next_step_ = skip_next_step;
3135 SetSyncSteppingFramePointer(stack_trace_);
3136 if (FLAG_verbose_debug) {
3137 OS::PrintErr("HandleSteppingRequest - kStepOver stepping_fp=%" Px "\n",
3138 stepping_fp_);
3139 }
3140 } else if (resume_action_ == kStepOut) {
3141 // Check if we have an asynchronous awaiter for the current frame.
3142 if (async_awaiter_stack_trace_ != nullptr &&
3143 async_awaiter_stack_trace_->Length() > 2 &&
3144 async_awaiter_stack_trace_->FrameAt(1)->kind() ==
3145 ActivationFrame::kAsyncSuspensionMarker) {
3146 auto awaiter_frame = async_awaiter_stack_trace_->FrameAt(2);
3147 AsyncStepInto(awaiter_frame->closure());
3148 if (FLAG_verbose_debug) {
3149 OS::PrintErr("HandleSteppingRequest - continue to async awaiter %s\n",
3150 Function::Handle(awaiter_frame->closure().function())
3151 .ToFullyQualifiedCString());
3152 }
3153 return;
3154 }
3155
3156 // Fall through to synchronous stepping.
3157 NotifySingleStepping(true);
3158 // Find topmost caller that is debuggable.
3159 for (intptr_t i = 1; i < stack_trace_->Length(); i++) {
3160 ActivationFrame* frame = stack_trace_->FrameAt(i);
3161 if (frame->IsDebuggable()) {
3162 stepping_fp_ = frame->fp();
3163 break;
3164 }
3165 }
3166 if (FLAG_verbose_debug) {
3167 OS::PrintErr("HandleSteppingRequest- kStepOut %" Px "\n", stepping_fp_);
3168 }
3169 } else if (resume_action_ == kStepRewind) {
3170 if (FLAG_trace_rewind) {
3171 OS::PrintErr("Rewinding to frame %" Pd "\n", resume_frame_index_);
3172 OS::PrintErr(
3173 "-------------------------\n"
3174 "All frames...\n\n");
3175 StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
3176 Thread::Current(),
3177 StackFrameIterator::kNoCrossThreadIteration);
3178 StackFrame* frame = iterator.NextFrame();
3179 intptr_t num = 0;
3180 while ((frame != nullptr)) {
3181 OS::PrintErr("#%04" Pd " %s\n", num++, frame->ToCString());
3182 frame = iterator.NextFrame();
3183 }
3184 }
3185 RewindToFrame(resume_frame_index_);
3186 UNREACHABLE();
3187 }
3188}
3189
3190void Debugger::CacheStackTraces(DebuggerStackTrace* stack_trace,
3191 DebuggerStackTrace* async_awaiter_stack_trace) {
3192 ASSERT(stack_trace_ == nullptr);
3193 stack_trace_ = stack_trace;
3194 ASSERT(async_awaiter_stack_trace_ == nullptr);
3195 async_awaiter_stack_trace_ = async_awaiter_stack_trace;
3196}
3197
3198void Debugger::ClearCachedStackTraces() {
3199 stack_trace_ = nullptr;
3200 async_awaiter_stack_trace_ = nullptr;
3201}
3202
3204 intptr_t frame_index) {
3205 for (intptr_t i = frame_index + 1; i < stack->Length(); i++) {
3206 ActivationFrame* frame = stack->FrameAt(i);
3207 if (frame->IsRewindable()) {
3208 return i;
3209 }
3210 }
3211 return -1;
3212}
3213
3214// Can we rewind to the indicated frame?
3215static bool CanRewindFrame(intptr_t frame_index, const char** error) {
3216 // check rewind pc is found
3217 DebuggerStackTrace* stack = Isolate::Current()->debugger()->StackTrace();
3218 intptr_t num_frames = stack->Length();
3219 if (frame_index < 1 || frame_index >= num_frames) {
3220 if (error != nullptr) {
3221 *error = Thread::Current()->zone()->PrintToString(
3222 "Frame must be in bounds [1..%" Pd
3223 "]: "
3224 "saw %" Pd "",
3225 num_frames - 1, frame_index);
3226 }
3227 return false;
3228 }
3229 ActivationFrame* frame = stack->FrameAt(frame_index);
3230 if (!frame->IsRewindable()) {
3231 intptr_t next_index = FindNextRewindFrameIndex(stack, frame_index);
3232 if (next_index > 0) {
3233 *error = Thread::Current()->zone()->PrintToString(
3234 "Cannot rewind to frame %" Pd
3235 " due to conflicting compiler "
3236 "optimizations. "
3237 "Run the vm with --no-prune-dead-locals to disallow these "
3238 "optimizations. "
3239 "Next valid rewind frame is %" Pd ".",
3240 frame_index, next_index);
3241 } else {
3242 *error = Thread::Current()->zone()->PrintToString(
3243 "Cannot rewind to frame %" Pd
3244 " due to conflicting compiler "
3245 "optimizations. "
3246 "Run the vm with --no-prune-dead-locals to disallow these "
3247 "optimizations.",
3248 frame_index);
3249 }
3250 return false;
3251 }
3252 return true;
3253}
3254
3255// Given a return address, find the "rewind" pc, which is the pc
3256// before the corresponding call.
3257static uword LookupRewindPc(const Code& code, uword return_address) {
3258 ASSERT(!code.is_optimized());
3259 ASSERT(code.ContainsInstructionAt(return_address));
3260
3261 uword pc_offset = return_address - code.PayloadStart();
3262 const PcDescriptors& descriptors =
3263 PcDescriptors::Handle(code.pc_descriptors());
3264 PcDescriptors::Iterator iter(descriptors,
3265 UntaggedPcDescriptors::kRewind |
3266 UntaggedPcDescriptors::kIcCall |
3267 UntaggedPcDescriptors::kUnoptStaticCall);
3268 intptr_t rewind_deopt_id = -1;
3269 uword rewind_pc = 0;
3270 while (iter.MoveNext()) {
3271 if (iter.Kind() == UntaggedPcDescriptors::kRewind) {
3272 // Remember the last rewind so we don't need to iterator twice.
3273 rewind_pc = code.PayloadStart() + iter.PcOffset();
3274 rewind_deopt_id = iter.DeoptId();
3275 }
3276 if ((pc_offset == iter.PcOffset()) && (iter.DeoptId() == rewind_deopt_id)) {
3277 return rewind_pc;
3278 }
3279 }
3280 return 0;
3281}
3282
3283void Debugger::RewindToFrame(intptr_t frame_index) {
3284 Thread* thread = Thread::Current();
3285 Zone* zone = thread->zone();
3286 Code& code = Code::Handle(zone);
3287 Function& function = Function::Handle(zone);
3288
3289 // Find the requested frame.
3290 StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
3291 Thread::Current(),
3292 StackFrameIterator::kNoCrossThreadIteration);
3293 intptr_t current_frame = 0;
3294 for (StackFrame* frame = iterator.NextFrame(); frame != nullptr;
3295 frame = iterator.NextFrame()) {
3296 ASSERT(frame->IsValid());
3297 if (frame->IsDartFrame()) {
3298 code = frame->LookupDartCode();
3299 function = code.function();
3301 continue;
3302 }
3303 if (code.is_optimized()) {
3304 intptr_t sub_index = 0;
3305 for (InlinedFunctionsIterator it(code, frame->pc()); !it.Done();
3306 it.Advance()) {
3307 if (current_frame == frame_index) {
3308 RewindToOptimizedFrame(frame, code, sub_index);
3309 UNREACHABLE();
3310 }
3311 current_frame++;
3312 sub_index++;
3313 }
3314 } else {
3315 if (current_frame == frame_index) {
3316 // We are rewinding to an unoptimized frame.
3317 RewindToUnoptimizedFrame(frame, code);
3318 UNREACHABLE();
3319 }
3320 current_frame++;
3321 }
3322 }
3323 }
3324 UNIMPLEMENTED();
3325}
3326
3327void Debugger::RewindToUnoptimizedFrame(StackFrame* frame, const Code& code) {
3328 // We will be jumping out of the debugger rather than exiting this
3329 // function, so prepare the debugger state.
3330 ClearCachedStackTraces();
3331 set_resume_action(kContinue);
3332 resume_frame_index_ = -1;
3333 EnterSingleStepMode();
3334
3335 uword rewind_pc = LookupRewindPc(code, frame->pc());
3336 if (FLAG_trace_rewind && rewind_pc == 0) {
3337 OS::PrintErr("Unable to find rewind pc for pc(%" Px ")\n", frame->pc());
3338 }
3339 ASSERT(rewind_pc != 0);
3340 if (FLAG_trace_rewind) {
3341 OS::PrintErr(
3342 "===============================\n"
3343 "Rewinding to unoptimized frame:\n"
3344 " rewind_pc(0x%" Px " offset:0x%" Px ") sp(0x%" Px ") fp(0x%" Px
3345 ")\n"
3346 "===============================\n",
3347 rewind_pc, rewind_pc - code.PayloadStart(), frame->sp(), frame->fp());
3348 }
3349 Exceptions::JumpToFrame(Thread::Current(), rewind_pc, frame->sp(),
3350 frame->fp(), true /* clear lazy deopt at target */);
3351 UNREACHABLE();
3352}
3353
3354void Debugger::RewindToOptimizedFrame(StackFrame* frame,
3355 const Code& optimized_code,
3356 intptr_t sub_index) {
3357 post_deopt_frame_index_ = sub_index;
3358
3359 // We will be jumping out of the debugger rather than exiting this
3360 // function, so prepare the debugger state.
3361 ClearCachedStackTraces();
3362 set_resume_action(kContinue);
3363 resume_frame_index_ = -1;
3364 EnterSingleStepMode();
3365
3366 if (FLAG_trace_rewind) {
3367 OS::PrintErr(
3368 "===============================\n"
3369 "Deoptimizing frame for rewind:\n"
3370 " deopt_pc(0x%" Px ") sp(0x%" Px ") fp(0x%" Px
3371 ")\n"
3372 "===============================\n",
3373 frame->pc(), frame->sp(), frame->fp());
3374 }
3375 Thread* thread = Thread::Current();
3376 thread->set_resume_pc(frame->pc());
3377 uword deopt_stub_pc = StubCode::DeoptForRewind().EntryPoint();
3378 Exceptions::JumpToFrame(thread, deopt_stub_pc, frame->sp(), frame->fp(),
3379 true /* clear lazy deopt at target */);
3380 UNREACHABLE();
3381}
3382
3383void Debugger::RewindPostDeopt() {
3384 intptr_t rewind_frame = post_deopt_frame_index_;
3385 post_deopt_frame_index_ = -1;
3386 if (FLAG_trace_rewind) {
3387 OS::PrintErr("Post deopt, jumping to frame %" Pd "\n", rewind_frame);
3388 OS::PrintErr(
3389 "-------------------------\n"
3390 "All frames...\n\n");
3391 StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
3392 Thread::Current(),
3393 StackFrameIterator::kNoCrossThreadIteration);
3394 StackFrame* frame = iterator.NextFrame();
3395 intptr_t num = 0;
3396 while ((frame != nullptr)) {
3397 OS::PrintErr("#%04" Pd " %s\n", num++, frame->ToCString());
3398 frame = iterator.NextFrame();
3399 }
3400 }
3401
3402 Thread* thread = Thread::Current();
3403 Zone* zone = thread->zone();
3404 Code& code = Code::Handle(zone);
3405
3406 StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
3407 Thread::Current(),
3408 StackFrameIterator::kNoCrossThreadIteration);
3409 intptr_t current_frame = 0;
3410 for (StackFrame* frame = iterator.NextFrame(); frame != nullptr;
3411 frame = iterator.NextFrame()) {
3412 ASSERT(frame->IsValid());
3413 if (frame->IsDartFrame()) {
3414 code = frame->LookupDartCode();
3415 ASSERT(!code.is_optimized());
3416 if (current_frame == rewind_frame) {
3417 RewindToUnoptimizedFrame(frame, code);
3418 UNREACHABLE();
3419 }
3420 current_frame++;
3421 }
3422 }
3423}
3424
3425// static
3426bool Debugger::IsDebuggable(const Function& func) {
3427 if (!func.is_debuggable()) {
3428 return false;
3429 }
3430 const Class& cls = Class::Handle(func.Owner());
3431 const Library& lib = Library::Handle(cls.library());
3432 return lib.IsDebuggable();
3433}
3434
3435void GroupDebugger::RegisterSingleSteppingDebugger(Thread* thread,
3436 const Debugger* debugger) {
3437 WriteRwLocker sl(Thread::Current(), single_stepping_set_lock());
3438 single_stepping_set_.Insert(debugger);
3439}
3440
3441void GroupDebugger::UnregisterSingleSteppingDebugger(Thread* thread,
3442 const Debugger* debugger) {
3443 WriteRwLocker sl(Thread::Current(), single_stepping_set_lock());
3444 single_stepping_set_.Remove(debugger);
3445}
3446
3447bool GroupDebugger::HasBreakpointUnsafe(Thread* thread,
3448 const Function& function) {
3450 breakpoint_locations_lock()->IsCurrentThreadReader());
3451 // Check if function has any breakpoints.
3452 String& url = String::Handle(thread->zone());
3453 for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
3454 BreakpointLocation* location = breakpoint_locations_.At(i);
3455 url = location->url();
3456 if (FunctionOverlaps(function, url, location->token_pos(),
3457 location->end_token_pos())) {
3458 return true;
3459 }
3460 }
3461 return false;
3462}
3463
3464bool GroupDebugger::HasBreakpoint(Thread* thread, const Function& function) {
3465 bool hasBreakpoint = false;
3466 // Don't need to worry about the lock if mutators are stopped.
3467 if (thread->IsInStoppedMutatorsScope()) {
3468 hasBreakpoint = HasBreakpointUnsafe(thread, function);
3469 } else {
3470 SafepointReadRwLocker sl(thread, breakpoint_locations_lock());
3471 hasBreakpoint = HasBreakpointUnsafe(thread, function);
3472 }
3473 if (hasBreakpoint) {
3474 return true;
3475 }
3476
3477 // TODO(aam): do we have to iterate over both code breakpoints and
3478 // breakpoint locations? Wouldn't be sufficient to iterate over only
3479 // one list? Could you have a CodeBreakpoint without corresponding
3480 // BreakpointLocation?
3481 if (HasCodeBreakpointInFunction(function)) {
3482 return true;
3483 }
3484
3485 return false;
3486}
3487
3488bool GroupDebugger::IsDebugging(Thread* thread, const Function& function) {
3489 {
3490 ReadRwLocker ml(thread, single_stepping_set_lock());
3491 if (!single_stepping_set_.IsEmpty()) {
3492 return true;
3493 }
3494 }
3495 return HasBreakpoint(thread, function);
3496}
3497
3498void Debugger::set_resume_action(ResumeAction resume_action) {
3499 auto thread = Thread::Current();
3500 if (resume_action == kContinue) {
3501 group_debugger()->UnregisterSingleSteppingDebugger(thread, this);
3502 } else {
3503 group_debugger()->RegisterSingleSteppingDebugger(thread, this);
3504 }
3505 resume_action_ = resume_action;
3506}
3507
3508void Debugger::SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt) {
3509 set_resume_action(kContinue);
3510 ResetSteppingFramePointer();
3511 NotifySingleStepping(false);
3512 ASSERT(!IsPaused());
3513 if ((bpt != nullptr) && bpt->is_single_shot()) {
3514 RemoveBreakpoint(bpt->id());
3515 bpt = nullptr;
3516 }
3517
3518 ServiceEvent event(isolate_, ServiceEvent::kPauseBreakpoint);
3519 event.set_top_frame(top_frame);
3520 event.set_breakpoint(bpt);
3521 event.set_at_async_jump(IsAtAsyncJump(top_frame));
3522 Pause(&event);
3523}
3524
3525static bool IsAtAsyncJump(ActivationFrame* top_frame) {
3526 Zone* zone = Thread::Current()->zone();
3527 if (!top_frame->function().IsAsyncFunction() &&
3528 !top_frame->function().IsAsyncGenerator()) {
3529 return false;
3530 }
3531 const auto& pc_descriptors =
3532 PcDescriptors::Handle(zone, top_frame->code().pc_descriptors());
3533 if (pc_descriptors.IsNull()) {
3534 return false;
3535 }
3536 const TokenPosition looking_for = top_frame->TokenPos();
3537 PcDescriptors::Iterator it(pc_descriptors, UntaggedPcDescriptors::kOther);
3538 while (it.MoveNext()) {
3539 if (it.TokenPos() == looking_for &&
3540 it.YieldIndex() != UntaggedPcDescriptors::kInvalidYieldIndex) {
3541 return true;
3542 }
3543 }
3544 return false;
3545}
3546
3547ErrorPtr Debugger::PauseStepping() {
3548 ASSERT(isolate_->single_step());
3549 // Don't pause recursively.
3550 if (IsPaused()) {
3551 return Error::null();
3552 }
3553 if (skip_next_step_) {
3554 skip_next_step_ = false;
3555 return Error::null();
3556 }
3557
3558 // Check whether we are in a Dart function that the user is
3559 // interested in. If we saved the frame pointer of a stack frame
3560 // the user is interested in, we ignore the single step if we are
3561 // in a callee of that frame. Note that we assume that the stack
3562 // grows towards lower addresses.
3564 ASSERT(frame != nullptr);
3565
3566 if (stepping_fp_ != 0) {
3567 // There is an "interesting frame" set. Only pause at appropriate
3568 // locations in this frame.
3569 const ActivationFrame::Relation relation = frame->CompareTo(stepping_fp_);
3570 if (relation == ActivationFrame::kCallee) {
3571 // We are in a callee of the frame we're interested in.
3572 // Ignore this stepping break.
3573 return Error::null();
3574 } else if (relation == ActivationFrame::kCaller) {
3575 // We returned from the "interesting frame", there can be no more
3576 // stepping breaks for it. Pause at the next appropriate location
3577 // and let the user set the "interesting" frame again.
3578 ResetSteppingFramePointer();
3579 }
3580 }
3581
3582 if (!frame->IsDebuggable()) {
3583 return Error::null();
3584 }
3585 if (!frame->TokenPos().IsDebugPause()) {
3586 return Error::null();
3587 }
3588
3589 if (frame->fp() == last_stepping_fp_ &&
3590 frame->TokenPos() == last_stepping_pos_) {
3591 // Do not stop multiple times for the same token position.
3592 // Several 'debug checked' opcodes may be issued in the same token range.
3593 return Error::null();
3594 }
3595
3596 // TODO(dartbug.com/48378): Consider aligning async/async* functions
3597 // with regular function wrt the first stop in the function prologue.
3598 if ((frame->function().IsAsyncFunction() ||
3599 frame->function().IsAsyncGenerator()) &&
3600 frame->GetSuspendStateVar() == Object::null()) {
3601 return Error::null();
3602 }
3603
3604 // We are stopping in this frame at the token pos.
3605 last_stepping_fp_ = frame->fp();
3606 last_stepping_pos_ = frame->TokenPos();
3607
3608 // If there is an active breakpoint at this pc, then we should have
3609 // already bailed out of this function in the skip_next_step_ test
3610 // above.
3611 ASSERT(!group_debugger()->HasActiveBreakpoint(frame->pc()));
3612
3613 if (FLAG_verbose_debug) {
3614 OS::PrintErr(">>> single step break at %s:%" Pd ":%" Pd
3615 " (func %s token %s address %#" Px " offset %#" Px ")\n",
3616 String::Handle(frame->SourceUrl()).ToCString(),
3617 frame->LineNumber(), frame->ColumnNumber(),
3618 String::Handle(frame->QualifiedFunctionName()).ToCString(),
3619 frame->TokenPos().ToCString(), frame->pc(),
3620 frame->pc() - frame->code().PayloadStart());
3621 }
3622
3623 CacheStackTraces(DebuggerStackTrace::Collect(),
3624 DebuggerStackTrace::CollectAsyncAwaiters());
3625 SignalPausedEvent(frame, nullptr);
3626 HandleSteppingRequest();
3627 ClearCachedStackTraces();
3628
3629 // If any error occurred while in the debug message loop, return it here.
3630 return Thread::Current()->StealStickyError();
3631}
3632
3633ErrorPtr Debugger::PauseBreakpoint() {
3634 // We ignore this breakpoint when the VM is executing code invoked
3635 // by the debugger to evaluate variables values, or when we see a nested
3636 // breakpoint or exception event.
3637 if (ignore_breakpoints_ || IsPaused()) {
3638 return Error::null();
3639 }
3640 DebuggerStackTrace* stack_trace = DebuggerStackTrace::Collect();
3641 ASSERT(stack_trace->Length() > 0);
3642 ActivationFrame* top_frame = stack_trace->FrameAt(0);
3643 ASSERT(top_frame != nullptr);
3644 if (!Library::Handle(top_frame->Library()).IsDebuggable()) {
3645 return Error::null();
3646 }
3647
3648 BreakpointLocation* bpt_location = nullptr;
3649 const char* cbpt_tostring = nullptr;
3650 {
3651 SafepointReadRwLocker cbl(Thread::Current(),
3652 group_debugger()->code_breakpoints_lock());
3653 CodeBreakpoint* cbpt = nullptr;
3654 bpt_location = group_debugger()->GetBreakpointLocationFor(
3655 this, top_frame->pc(), &cbpt);
3656 if (bpt_location == nullptr) {
3657 // There might be no breakpoint locations for this isolate/debugger.
3658 return Error::null();
3659 }
3660 ASSERT(cbpt != nullptr);
3661 if (FLAG_verbose_debug) {
3662 cbpt_tostring = cbpt->ToCString();
3663 }
3664 }
3665
3666 Breakpoint* bpt_hit = bpt_location->FindHitBreakpoint(top_frame);
3667 if (bpt_hit == nullptr) {
3668 return Error::null();
3669 }
3670
3671 if (FLAG_verbose_debug) {
3672 OS::PrintErr(">>> hit %" Pd
3673 " %s"
3674 " (func %s token %s address %#" Px " offset %#" Px ")\n",
3675 bpt_hit->id(), cbpt_tostring,
3676 String::Handle(top_frame->QualifiedFunctionName()).ToCString(),
3677 bpt_location->token_pos().ToCString(), top_frame->pc(),
3678 top_frame->pc() - top_frame->code().PayloadStart());
3679 }
3680
3681 CacheStackTraces(stack_trace, DebuggerStackTrace::CollectAsyncAwaiters());
3682 SignalPausedEvent(top_frame, bpt_hit);
3683 // When we single step from a user breakpoint, our next stepping
3684 // point will be at the exact same pc. Skip it.
3685 HandleSteppingRequest(/*skip_next_step=*/true);
3686 ClearCachedStackTraces();
3687
3688 // If any error occurred while in the debug message loop, return it here.
3689 return Thread::Current()->StealStickyError();
3690}
3691
3692Breakpoint* BreakpointLocation::FindHitBreakpoint(ActivationFrame* top_frame) {
3693 // There may be more than one applicable breakpoint at this location, but we
3694 // will report only one as reached. If there is a single-shot breakpoint, we
3695 // favor it; then a closure-specific breakpoint ; then an general breakpoint.
3696
3697 // First check for a single-shot breakpoint.
3698 Breakpoint* bpt = breakpoints();
3699 while (bpt != nullptr) {
3700 if (bpt->is_single_shot() && bpt->closure() == Instance::null()) {
3701 return bpt;
3702 }
3703 bpt = bpt->next();
3704 }
3705
3706 // Now check for a closure-specific breakpoint.
3707 bpt = breakpoints();
3708 while (bpt != nullptr) {
3709 if (bpt->closure() != Instance::null() &&
3710 bpt->closure() == top_frame->GetClosure()) {
3711 return bpt;
3712 }
3713 bpt = bpt->next();
3714 }
3715
3716 // Finally, check for a general breakpoint.
3717 bpt = breakpoints();
3718 while (bpt != nullptr) {
3719 if (!bpt->is_single_shot() && bpt->closure() == Instance::null()) {
3720 return bpt;
3721 }
3722 bpt = bpt->next();
3723 }
3724
3725 return nullptr;
3726}
3727
3728void Debugger::PauseDeveloper(const String& msg) {
3729 // We ignore this breakpoint when the VM is executing code invoked
3730 // by the debugger to evaluate variables values, or when we see a nested
3731 // breakpoint or exception event.
3732 if (ignore_breakpoints_ || IsPaused()) {
3733 return;
3734 }
3735
3736 DebuggerStackTrace* stack_trace = DebuggerStackTrace::Collect();
3737 ASSERT(stack_trace->Length() > 0);
3738 CacheStackTraces(stack_trace, DebuggerStackTrace::CollectAsyncAwaiters());
3739 // TODO(johnmccutchan): Send |msg| to Observatory.
3740
3741 // We are in the native call to Developer_debugger. the developer
3742 // gets a better experience by not seeing this call. To accomplish
3743 // this, we continue execution until the call exits (step out).
3744 SetResumeAction(kStepOut);
3745 HandleSteppingRequest();
3746 ClearCachedStackTraces();
3747}
3748
3749void Debugger::NotifyIsolateCreated() {
3750 if (NeedsIsolateEvents()) {
3751 ServiceEvent event(isolate_, ServiceEvent::kIsolateStart);
3753 }
3754}
3755
3756#if !defined(DART_PRECOMPILED_RUNTIME)
3757// On single line of code with given column number,
3758// Calculate exact tokenPosition
3760 TokenPosition start_of_line,
3761 intptr_t column_number) {
3762 intptr_t line;
3763 intptr_t col;
3764 if (script.GetTokenLocation(start_of_line, &line, &col)) {
3765 return TokenPosition::Deserialize(start_of_line.Pos() +
3766 (column_number - col));
3767 }
3768 return TokenPosition::kNoSource;
3769}
3770#endif // !defined(DART_PRECOMPILED_RUNTIME)
3771
3772void Debugger::NotifyDoneLoading() {
3773 if (latent_locations_ == nullptr) {
3774 // Common, fast path.
3775 return;
3776 }
3777 auto thread = Thread::Current();
3778 auto isolate_group = thread->isolate_group();
3779 auto zone = thread->zone();
3780 Library& lib = Library::Handle(zone);
3781 Script& script = Script::Handle(zone);
3782 String& url = String::Handle(zone);
3783 BreakpointLocation* loc = latent_locations_;
3784 BreakpointLocation* prev_loc = nullptr;
3785 const GrowableObjectArray& libs =
3786 GrowableObjectArray::Handle(isolate_group->object_store()->libraries());
3787
3789 while (loc != nullptr) {
3790 url = loc->url();
3791 bool found_match = false;
3792 bool is_package = url.StartsWith(Symbols::PackageScheme());
3793 for (intptr_t i = 0; i < libs.Length(); i++) {
3794 lib ^= libs.At(i);
3795 script = lib.LookupScript(url, !is_package);
3796 if (!script.IsNull()) {
3797 scripts.Add(script);
3798 }
3799 }
3800 if (scripts.length() > 0) {
3801 // Found a script with matching url for this latent breakpoint.
3802 // Unlink the latent breakpoint from the list.
3803 found_match = true;
3804 BreakpointLocation* matched_loc = loc;
3805 loc = loc->next();
3806 if (prev_loc == nullptr) {
3807 latent_locations_ = loc;
3808 } else {
3809 prev_loc->set_next(loc);
3810 }
3811 // Now find the token range at the requested line and make a
3812 // new unresolved source breakpoint.
3813 intptr_t line_number = matched_loc->requested_line_number();
3814 intptr_t column_number = matched_loc->requested_column_number();
3815 ASSERT(line_number >= 0);
3816 TokenPosition first_token_pos = TokenPosition::kNoSource;
3817 TokenPosition last_token_pos = TokenPosition::kNoSource;
3818 scripts.At(0).TokenRangeAtLine(line_number, &first_token_pos,
3819 &last_token_pos);
3820 if (!first_token_pos.IsDebugPause() || !last_token_pos.IsDebugPause()) {
3821 // Script does not contain the given line number or there are no
3822 // tokens on the line. Drop the breakpoint silently.
3823 Breakpoint* bpt = matched_loc->breakpoints();
3824 while (bpt != nullptr) {
3825 if (FLAG_verbose_debug) {
3826 OS::PrintErr("No code found at line %" Pd
3827 ": "
3828 "dropping latent breakpoint %" Pd " in '%s'\n",
3829 line_number, bpt->id(), url.ToCString());
3830 }
3831 Breakpoint* prev = bpt;
3832 bpt = bpt->next();
3833 delete prev;
3834 }
3835 delete matched_loc;
3836 } else {
3837 // We don't expect to already have a breakpoint for this location.
3838 // If there is one, assert in debug build but silently drop
3839 // the latent breakpoint in release build.
3840 BreakpointLocation* existing_loc =
3841 GetBreakpointLocation(url, first_token_pos, -1, column_number);
3842 ASSERT(existing_loc == nullptr);
3843 if (existing_loc == nullptr) {
3844 // Create and register a new source breakpoint for the
3845 // latent breakpoint.
3846 BreakpointLocation* unresolved_loc = new BreakpointLocation(
3847 this, scripts, first_token_pos, last_token_pos, line_number,
3848 column_number);
3849 RegisterBreakpointLocation(unresolved_loc);
3850
3851 // Move breakpoints over.
3852 Breakpoint* bpt = matched_loc->breakpoints();
3853 unresolved_loc->set_breakpoints(bpt);
3854 matched_loc->set_breakpoints(nullptr);
3855 while (bpt != nullptr) {
3856 bpt->set_bpt_location(unresolved_loc);
3857 if (FLAG_verbose_debug) {
3858 OS::PrintErr(
3859 "Converted latent breakpoint "
3860 "%" Pd " in '%s' at line %" Pd " col %" Pd "\n",
3861 bpt->id(), url.ToCString(), line_number, column_number);
3862 }
3863 bpt = bpt->next();
3864 }
3865 group_debugger()->SyncBreakpointLocation(unresolved_loc);
3866 }
3867 delete matched_loc;
3868 // Break out of the iteration over loaded libraries. If the
3869 // same url has been loaded into more than one library, we
3870 // only set a breakpoint in the first one.
3871 // TODO(hausner): There is one possible pitfall here.
3872 // If the user sets a latent breakpoint using a partial url that
3873 // ends up matching more than one script, the breakpoint might
3874 // get set in the wrong script.
3875 // It would be better if we could warn the user if multiple
3876 // scripts are matching.
3877 break;
3878 }
3879 }
3880 if (!found_match) {
3881 // No matching url found in any of the libraries.
3882 if (FLAG_verbose_debug) {
3883 Breakpoint* bpt = loc->breakpoints();
3884 while (bpt != nullptr) {
3885 OS::PrintErr(
3886 "No match found for latent breakpoint id "
3887 "%" Pd " with url '%s'\n",
3888 bpt->id(), url.ToCString());
3889 bpt = bpt->next();
3890 }
3891 }
3892 loc = loc->next();
3893 }
3894 }
3895}
3896
3897// TODO(hausner): Could potentially make this faster by checking
3898// whether the call target at pc is a debugger stub.
3899bool GroupDebugger::HasActiveBreakpoint(uword pc) {
3900 SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
3901 CodeBreakpoint* cbpt = GetCodeBreakpoint(pc);
3902 return (cbpt != nullptr) && (cbpt->IsEnabled());
3903}
3904
3905CodeBreakpoint* GroupDebugger::GetCodeBreakpoint(uword breakpoint_address) {
3906 CodeBreakpoint* cbpt = code_breakpoints_;
3907 while (cbpt != nullptr) {
3908 if (cbpt->pc() == breakpoint_address) {
3909 return cbpt;
3910 }
3911 cbpt = cbpt->next();
3912 }
3913 return nullptr;
3914}
3915
3916BreakpointLocation* GroupDebugger::GetBreakpointLocationFor(
3917 Debugger* debugger,
3918 uword breakpoint_address,
3919 CodeBreakpoint** pcbpt) {
3920 ASSERT(pcbpt != nullptr);
3921 SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
3922 *pcbpt = code_breakpoints_;
3923 while (*pcbpt != nullptr) {
3924 if ((*pcbpt)->pc() == breakpoint_address) {
3925 return (*pcbpt)->FindBreakpointForDebugger(debugger);
3926 }
3927 *pcbpt = (*pcbpt)->next();
3928 }
3929 return nullptr;
3930}
3931
3932void GroupDebugger::RegisterCodeBreakpoint(CodeBreakpoint* cbpt) {
3933 ASSERT(cbpt->next() == nullptr);
3934 DEBUG_ASSERT(code_breakpoints_lock()->IsCurrentThreadWriter() ||
3935 Thread::Current()->IsInStoppedMutatorsScope());
3936 cbpt->set_next(code_breakpoints_);
3937 code_breakpoints_ = cbpt;
3938}
3939
3940CodePtr GroupDebugger::GetPatchedStubAddress(uword breakpoint_address) {
3941 SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
3942 CodeBreakpoint* cbpt = GetCodeBreakpoint(breakpoint_address);
3943 if (cbpt != nullptr) {
3944 return cbpt->OrigStubAddress();
3945 }
3946 UNREACHABLE();
3947 return Code::null();
3948}
3949
3951 SafepointWriteRwLocker sl(Thread::Current(),
3952 group_debugger()->breakpoint_locations_lock());
3953 if (bpt->is_enabled() != enable) {
3954 if (FLAG_verbose_debug) {
3955 OS::PrintErr("Setting breakpoint %" Pd " to state: %s\n", bpt->id(),
3956 enable ? "enabled" : "disabled");
3957 }
3958 enable ? bpt->Enable() : bpt->Disable();
3959 group_debugger()->SyncBreakpointLocation(bpt->bpt_location());
3960 return true;
3961 }
3962 return false;
3963}
3964
3965// Remove and delete the source breakpoint bpt and its associated
3966// code breakpoints.
3967void Debugger::RemoveBreakpoint(intptr_t bp_id) {
3968 SafepointWriteRwLocker sl(Thread::Current(),
3969 group_debugger()->breakpoint_locations_lock());
3970 if (RemoveBreakpointFromTheList(bp_id, &breakpoint_locations_)) {
3971 return;
3972 }
3973 RemoveBreakpointFromTheList(bp_id, &latent_locations_);
3974}
3975
3976// Remove and delete the source breakpoint bpt and its associated
3977// code breakpoints. Returns true, if breakpoint was found and removed,
3978// returns false, if breakpoint was not found.
3979bool Debugger::RemoveBreakpointFromTheList(intptr_t bp_id,
3980 BreakpointLocation** list) {
3981 BreakpointLocation* prev_loc = nullptr;
3982 BreakpointLocation* curr_loc = *list;
3983 while (curr_loc != nullptr) {
3984 Breakpoint* prev_bpt = nullptr;
3985 Breakpoint* curr_bpt = curr_loc->breakpoints();
3986 while (curr_bpt != nullptr) {
3987 if (curr_bpt->id() == bp_id) {
3988 if (prev_bpt == nullptr) {
3989 curr_loc->set_breakpoints(curr_bpt->next());
3990 } else {
3991 prev_bpt->set_next(curr_bpt->next());
3992 }
3993
3994 // Send event to client before the breakpoint's fields are
3995 // poisoned and deleted.
3996 SendBreakpointEvent(ServiceEvent::kBreakpointRemoved, curr_bpt);
3997
3998 curr_bpt->set_next(nullptr);
3999 curr_bpt->set_bpt_location(nullptr);
4000 // Remove possible references to the breakpoint.
4001 if (pause_event_ != nullptr && pause_event_->breakpoint() == curr_bpt) {
4002 pause_event_->set_breakpoint(nullptr);
4003 }
4004 delete curr_bpt;
4005 curr_bpt = nullptr;
4006
4007 // Delete the breakpoint location object if there are no more
4008 // breakpoints at that location.
4009 if (curr_loc->breakpoints() == nullptr) {
4010 if (prev_loc == nullptr) {
4011 *list = curr_loc->next();
4012 } else {
4013 prev_loc->set_next(curr_loc->next());
4014 }
4015
4016 if (!curr_loc->IsLatent()) {
4017 // Remove references from code breakpoints to this breakpoint
4018 // location and disable them.
4019 // Latent breakpoint locations won't have code breakpoints.
4020 group_debugger()->UnlinkCodeBreakpoints(curr_loc);
4021 }
4022 group_debugger()->UnregisterBreakpointLocation(curr_loc);
4023 BreakpointLocation* next_loc = curr_loc->next();
4024 delete curr_loc;
4025 curr_loc = next_loc;
4026 }
4027
4028 // The code breakpoints will be deleted when the VM resumes
4029 // after the pause event.
4030 return true;
4031 }
4032
4033 prev_bpt = curr_bpt;
4034 curr_bpt = curr_bpt->next();
4035 }
4036 prev_loc = curr_loc;
4037 curr_loc = curr_loc->next();
4038 }
4039 // breakpoint with bp_id does not exist, nothing to do.
4040 return false;
4041}
4042
4043void GroupDebugger::RegisterBreakpointLocation(BreakpointLocation* location) {
4044 DEBUG_ASSERT(breakpoint_locations_lock()->IsCurrentThreadWriter() ||
4045 Thread::Current()->IsInStoppedMutatorsScope());
4046 breakpoint_locations_.Add(location);
4047}
4048
4049void GroupDebugger::UnregisterBreakpointLocation(BreakpointLocation* location) {
4050 ASSERT(breakpoint_locations_lock()->IsCurrentThreadWriter());
4051 for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
4052 if (breakpoint_locations_.At(i) == location) {
4053 breakpoint_locations_.EraseAt(i);
4054 return;
4055 }
4056 }
4057}
4058
4059// Unlink code breakpoints from the given breakpoint location.
4060// They will later be deleted when control returns from the pause event
4061// callback. Also, disable the breakpoint so it no longer fires if it
4062// should be hit before it gets deleted.
4063void GroupDebugger::UnlinkCodeBreakpoints(BreakpointLocation* bpt_location) {
4064 ASSERT(bpt_location != nullptr);
4065 SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
4066 CodeBreakpoint* curr_bpt = code_breakpoints_;
4067 while (curr_bpt != nullptr) {
4068 if (curr_bpt->FindAndDeleteBreakpointLocation(bpt_location)) {
4069 curr_bpt->Disable();
4070 needs_breakpoint_cleanup_ = true;
4071 }
4072 curr_bpt = curr_bpt->next();
4073 }
4074}
4075
4076// Remove and delete unlinked code breakpoints, i.e. breakpoints that
4077// are not associated with a breakpoint location.
4078void GroupDebugger::RemoveUnlinkedCodeBreakpoints() {
4079 DEBUG_ASSERT(code_breakpoints_lock()->IsCurrentThreadWriter() ||
4080 Thread::Current()->IsInStoppedMutatorsScope());
4081 CodeBreakpoint* prev_bpt = nullptr;
4082 CodeBreakpoint* curr_bpt = code_breakpoints_;
4083 while (curr_bpt != nullptr) {
4084 if (curr_bpt->HasNoBreakpointLocations()) {
4085 if (prev_bpt == nullptr) {
4086 code_breakpoints_ = code_breakpoints_->next();
4087 } else {
4088 prev_bpt->set_next(curr_bpt->next());
4089 }
4090 CodeBreakpoint* temp_bpt = curr_bpt;
4091 curr_bpt = curr_bpt->next();
4092 delete temp_bpt;
4093 } else {
4094 prev_bpt = curr_bpt;
4095 curr_bpt = curr_bpt->next();
4096 }
4097 }
4098 needs_breakpoint_cleanup_ = false;
4099}
4100
4101BreakpointLocation* Debugger::GetResolvedBreakpointLocation(
4102 const String& script_url,
4103 TokenPosition code_token_pos) {
4104 BreakpointLocation* loc = breakpoint_locations_;
4105 String& loc_url = String::Handle();
4106 while (loc != nullptr) {
4107 loc_url = loc->url();
4108 if (script_url.Equals(loc_url) && loc->code_token_pos_ == code_token_pos) {
4109 return loc;
4110 }
4111 loc = loc->next();
4112 }
4113 return nullptr;
4114}
4115
4116BreakpointLocation* Debugger::GetBreakpointLocation(
4117 const String& script_url,
4118 TokenPosition token_pos,
4119 intptr_t requested_line,
4120 intptr_t requested_column,
4121 TokenPosition code_token_pos) {
4122 BreakpointLocation* loc = breakpoint_locations_;
4123 String& loc_url = String::Handle();
4124 while (loc != nullptr) {
4125 loc_url = loc->url();
4126 if (script_url.Equals(loc_url) &&
4127 (!token_pos.IsReal() || (loc->token_pos() == token_pos)) &&
4128 ((requested_line == -1) ||
4129 (loc->requested_line_number_ == requested_line)) &&
4130 ((requested_column == -1) ||
4131 (loc->requested_column_number_ == requested_column)) &&
4132 (!code_token_pos.IsReal() ||
4133 (loc->code_token_pos_ == code_token_pos))) {
4134 return loc;
4135 }
4136 loc = loc->next();
4137 }
4138 return nullptr;
4139}
4140
4141Breakpoint* Debugger::GetBreakpointById(intptr_t id) {
4142 Breakpoint* bpt = GetBreakpointByIdInTheList(id, breakpoint_locations_);
4143 if (bpt != nullptr) {
4144 return bpt;
4145 }
4146 return GetBreakpointByIdInTheList(id, latent_locations_);
4147}
4148
4149Breakpoint* Debugger::GetBreakpointByIdInTheList(intptr_t id,
4150 BreakpointLocation* list) {
4151 BreakpointLocation* loc = list;
4152 while (loc != nullptr) {
4153 Breakpoint* bpt = loc->breakpoints();
4154 while (bpt != nullptr) {
4155 if (bpt->id() == id) {
4156 return bpt;
4157 }
4158 bpt = bpt->next();
4159 }
4160 loc = loc->next();
4161 }
4162 return nullptr;
4163}
4164
4165void Debugger::AsyncStepInto(const Closure& awaiter) {
4166 Zone* zone = Thread::Current()->zone();
4167
4168 auto& suspend_state = SuspendState::Handle(zone);
4169 if (StackTraceUtils::GetSuspendState(awaiter, &suspend_state)) {
4170 const auto& function_data =
4171 Object::Handle(zone, suspend_state.function_data());
4172 SetBreakpointAtResumption(function_data);
4173 } else {
4174 SetBreakpointAtActivation(awaiter, /*single_shot=*/true);
4175 }
4176 Continue();
4177}
4178
4179void Debugger::Continue() {
4180 SetResumeAction(kContinue);
4181 ResetSteppingFramePointer();
4182 NotifySingleStepping(false);
4183}
4184
4185BreakpointLocation* Debugger::GetLatentBreakpoint(const String& url,
4186 intptr_t line,
4187 intptr_t column) {
4188 BreakpointLocation* loc = latent_locations_;
4189 String& bpt_url = String::Handle();
4190 while (loc != nullptr) {
4191 bpt_url = loc->url();
4192 if (bpt_url.Equals(url) && (loc->requested_line_number() == line) &&
4193 (loc->requested_column_number() == column)) {
4194 return loc;
4195 }
4196 loc = loc->next();
4197 }
4198 // No breakpoint for this location requested. Allocate new one.
4199 loc = new BreakpointLocation(this, url, line, column);
4200 loc->set_next(latent_locations_);
4201 latent_locations_ = loc;
4202 return loc;
4203}
4204
4205void Debugger::RegisterBreakpointLocationUnsafe(BreakpointLocation* loc) {
4207 group_debugger()->breakpoint_locations_lock()->IsCurrentThreadWriter() ||
4208 Thread::Current()->IsInStoppedMutatorsScope());
4209 ASSERT(loc->next() == nullptr);
4210 loc->set_next(breakpoint_locations_);
4211 breakpoint_locations_ = loc;
4212 group_debugger()->RegisterBreakpointLocation(loc);
4213}
4214
4215void Debugger::RegisterBreakpointLocation(BreakpointLocation* loc) {
4216 auto thread = Thread::Current();
4217 if (thread->IsInStoppedMutatorsScope()) {
4218 RegisterBreakpointLocationUnsafe(loc);
4219 } else {
4220 SafepointWriteRwLocker sl(thread,
4221 group_debugger()->breakpoint_locations_lock());
4222 RegisterBreakpointLocationUnsafe(loc);
4223 }
4224}
4225
4226#endif // !PRODUCT
4227
4228} // namespace dart
SkPoint pos
static float next(float f)
static float prev(float f)
#define UNREACHABLE()
Definition: assert.h:248
#define DEBUG_ASSERT(cond)
Definition: assert.h:321
#define RELEASE_ASSERT(cond)
Definition: assert.h:327
GLenum type
intptr_t ColumnNumber()
Definition: debugger.cc:562
StringPtr QualifiedFunctionName()
Definition: debugger.cc:492
const Context & GetSavedCurrentContext()
Definition: debugger.cc:700
LibraryPtr Library()
Definition: debugger.cc:505
Relation CompareTo(uword other_fp) const
Definition: debugger.cc:485
void PrintToJSONObject(JSONObject *jsobj)
Definition: debugger.cc:1221
const Function & function() const
Definition: debugger.h:318
ArrayPtr GetLocalVariables()
Definition: debugger.cc:1033
uword fp() const
Definition: debugger.h:309
ClosurePtr GetClosure()
Definition: debugger.cc:860
ActivationFrame(uword pc, uword fp, uword sp, const Code &code, const Array &deopt_frame, intptr_t deopt_frame_offset)
Definition: debugger.cc:227
uword sp() const
Definition: debugger.h:310
ObjectPtr GetParameter(intptr_t index)
Definition: debugger.cc:838
intptr_t LineNumber()
Definition: debugger.cc:552
bool IsDebuggable() const
Definition: debugger.cc:595
const char * ToCString()
Definition: debugger.cc:1190
ScriptPtr SourceScript()
Definition: debugger.cc:501
ObjectPtr GetSuspendableFunctionData()
Definition: debugger.cc:873
bool HandlesException(const Instance &exc_obj)
Definition: debugger.cc:657
ObjectPtr GetReceiver()
Definition: debugger.cc:1048
StringPtr SourceUrl()
Definition: debugger.cc:496
uword pc() const
Definition: debugger.h:308
Kind kind() const
Definition: debugger.h:306
intptr_t NumLocalVariables()
Definition: debugger.cc:828
ObjectPtr GetSuspendStateVar()
Definition: debugger.cc:868
ObjectPtr EvaluateCompiledExpression(const ExternalTypedData &kernel_data, const Array &arguments, const Array &type_definitions, const TypeArguments &type_arguments)
Definition: debugger.cc:1071
TypeArgumentsPtr BuildParameters(const GrowableObjectArray &param_names, const GrowableObjectArray &param_values, const GrowableObjectArray &type_params_names, const GrowableObjectArray &type_params_bounds, const GrowableObjectArray &type_params_defaults)
Definition: debugger.cc:1099
const Code & code() const
Definition: debugger.h:319
bool IsRewindable() const
Definition: debugger.cc:891
intptr_t ContextLevel()
Definition: debugger.cc:624
void VariableAt(intptr_t i, String *name, TokenPosition *declaration_token_pos, TokenPosition *visible_start_token_pos, TokenPosition *visible_end_token_pos, Object *value)
Definition: debugger.cc:956
TokenPosition TokenPos()
Definition: debugger.cc:519
static ArrayPtr New(intptr_t len, Heap::Space space=Heap::kNew)
Definition: object.h:10959
ObjectPtr At(intptr_t index) const
Definition: object.h:10875
intptr_t Length() const
Definition: object.h:10829
void SetAt(intptr_t index, const Object &value) const
Definition: object.h:10880
void Add(const T &value)
intptr_t length() const
TokenPosition token_pos() const
Definition: debugger.h:128
bool AnyEnabled() const
Definition: debugger.cc:115
TokenPosition end_token_pos() const
Definition: debugger.h:130
Debugger * debugger()
Definition: debugger.h:158
intptr_t requested_column_number() const
Definition: debugger.h:141
Breakpoint * AddSingleShot(Debugger *dbg)
Definition: debugger.cc:344
bool EnsureIsResolved(const Function &target_function, TokenPosition exact_token_pos)
Definition: debugger.cc:2172
StringPtr url() const
Definition: debugger.h:138
Breakpoint * AddRepeated(Debugger *dbg)
Definition: debugger.cc:340
BreakpointLocation(Debugger *debugger, const GrowableHandlePtrArray< const Script > &scripts, TokenPosition token_pos, TokenPosition end_token_pos, intptr_t requested_line_number, intptr_t requested_column_number)
Definition: debugger.cc:60
bool IsResolved() const
Definition: debugger.h:152
ScriptPtr script() const
Definition: debugger.h:132
Breakpoint * AddBreakpoint(Debugger *dbg, const Closure &closure, bool single_shot)
Definition: debugger.cc:348
void GetCodeLocation(Script *script, TokenPosition *token_pos) const
Definition: debugger.cc:142
intptr_t requested_line_number() const
Definition: debugger.h:140
bool IsLatent() const
Definition: debugger.h:153
ClosurePtr closure() const
Definition: debugger.h:68
void Enable()
Definition: debugger.h:70
BreakpointLocation * bpt_location() const
Definition: debugger.h:64
bool is_enabled() const
Definition: debugger.h:80
bool is_single_shot() const
Definition: debugger.h:67
Breakpoint * next() const
Definition: debugger.h:61
void set_bpt_location(BreakpointLocation *new_bpt_location)
Definition: debugger.cc:168
intptr_t id() const
Definition: debugger.h:60
void set_next(Breakpoint *n)
Definition: debugger.h:62
void Disable()
Definition: debugger.h:75
void PrintJSON(JSONStream *stream)
Definition: debugger.cc:191
static intptr_t CidFromTopLevelIndex(intptr_t index)
Definition: class_table.h:503
LibraryPtr library() const
Definition: object.h:1333
StringPtr Name() const
Definition: object.cc:2977
bool IsTopLevel() const
Definition: object.cc:6121
static void ForAllClosureFunctions(std::function< bool(const Function &)> callback)
FunctionPtr function() const
Definition: debugger.h:224
bool IsEnabled() const
Definition: debugger.h:235
const char * ToCString() const
Definition: debugger.cc:211
CodeBreakpoint(const Code &code, BreakpointLocation *loc, uword pc, UntaggedPcDescriptors::Kind kind)
Definition: debugger.cc:1317
CodePtr OrigStubAddress() const
bool HasNoBreakpointLocations()
Definition: debugger.h:229
bool HasBreakpointLocation(BreakpointLocation *breakpoint_location)
Definition: debugger.cc:1360
bool FindAndDeleteBreakpointLocation(BreakpointLocation *breakpoint_location)
Definition: debugger.cc:1370
uword pc() const
Definition: debugger.h:226
PcDescriptorsPtr pc_descriptors() const
Definition: object.h:6927
bool is_optimized() const
Definition: object.h:6817
LocalVarDescriptorsPtr GetLocalVarDescriptors() const
Definition: object.cc:17625
void Disassemble(DisassemblyFormatter *formatter=nullptr) const
Definition: object.cc:17849
uword PayloadStart() const
Definition: object.h:6850
static ErrorPtr EnsureUnoptimizedCode(Thread *thread, const Function &function)
Definition: compiler.cc:854
void Dump(int indent=0) const
Definition: object.cc:18548
intptr_t Length() const
Definition: debugger.h:465
ActivationFrame * GetHandlerFrame(const Instance &exc_obj) const
Definition: debugger.cc:733
static DebuggerStackTrace * Collect()
Definition: debugger.cc:1671
ActivationFrame * FrameAt(int i) const
Definition: debugger.h:467
bool has_async_catch_error() const
Definition: debugger.h:479
static DebuggerStackTrace * CollectAsyncAwaiters()
Definition: debugger.cc:1732
Dart_ExceptionPauseInfo GetExceptionPauseInfo() const
Definition: debugger.cc:1867
Debugger(Isolate *isolate)
Definition: debugger.cc:1408
bool SetResumeAction(ResumeAction action, intptr_t frame_index=1, const char **error=nullptr)
Definition: debugger.cc:1486
void PrintBreakpointsToJSONArray(JSONArray *jsarr) const
Definition: debugger.cc:451
Isolate * isolate() const
Definition: debugger.h:673
void PrintSettingsToJSONObject(JSONObject *jsobj) const
Definition: debugger.cc:468
DebuggerStackTrace * StackTrace()
Definition: debugger.cc:1797
ErrorPtr PauseInterrupted()
Definition: debugger.cc:289
ErrorPtr PausePostRequest()
Definition: debugger.cc:293
bool IsPaused() const
Definition: debugger.h:716
@ kStepOverAsyncSuspension
Definition: debugger.h:667
void SetBreakpointAtResumption(const Object &function_data)
Definition: debugger.cc:2776
void Shutdown()
Definition: debugger.cc:1434
static bool IsDebuggable(const Function &func)
Definition: debugger.cc:3426
intptr_t MaterializeDeferredObjects()
static constexpr intptr_t kNone
Definition: deopt_id.h:27
static DART_NORETURN void PropagateError(const Error &error)
Definition: exceptions.cc:1003
const char * ToFullyQualifiedCString() const
Definition: object.cc:9762
bool IsSuspendableFunction() const
Definition: object.h:3957
bool HasCode() const
Definition: object.cc:7936
FunctionPtr parent_function() const
Definition: object.cc:8167
intptr_t NumTypeArguments() const
Definition: object.cc:8853
StringPtr name() const
Definition: object.h:2992
TokenPosition token_pos() const
Definition: object.h:3446
bool HasOptimizedCode() const
Definition: object.cc:10974
intptr_t NumTypeParameters() const
Definition: object.cc:8847
ScriptPtr script() const
Definition: object.cc:10881
bool IsAsyncFunction() const
Definition: object.h:3962
bool IsAsyncGenerator() const
Definition: object.h:3972
CodePtr unoptimized_code() const
Definition: object.h:3185
TypeParametersPtr type_parameters() const
Definition: object.h:3129
ClassPtr Owner() const
Definition: object.cc:10841
intptr_t num_fixed_parameters() const
Definition: object.cc:8856
UntaggedFunction::Kind kind() const
Definition: object.h:3349
TokenPosition end_token_pos() const
Definition: object.h:3455
GroupDebugger(IsolateGroup *isolate_group)
Definition: debugger.cc:1391
bool HasCodeBreakpointInFunction(const Function &func)
Definition: debugger.cc:427
void UnregisterBreakpointLocation(BreakpointLocation *location)
Definition: debugger.cc:4049
bool HasCodeBreakpointInFunctionUnsafe(const Function &func)
Definition: debugger.cc:414
bool HasBreakpointInCode(const Code &code)
Definition: debugger.cc:438
SafepointRwLock * code_breakpoints_lock()
Definition: debugger.h:616
void UnlinkCodeBreakpoints(BreakpointLocation *bpt_location)
Definition: debugger.cc:4063
void Add(const Object &value, Heap::Space space=Heap::kNew) const
Definition: object.cc:24991
void SetLength(intptr_t value) const
Definition: object.h:11076
intptr_t Length() const
Definition: object.h:11072
ObjectPtr At(intptr_t index) const
Definition: object.h:11085
void SetAt(intptr_t index, const Object &value) const
Definition: object.h:11091
void Grow(intptr_t new_capacity, Heap::Space space=Heap::kNew) const
Definition: object.cc:25008
bool IsInstanceOf(const AbstractType &other, const TypeArguments &other_instantiator_type_arguments, const TypeArguments &other_function_type_arguments) const
Definition: object.cc:20614
ObjectPtr EvaluateCompiledExpression(const Class &klass, const ExternalTypedData &kernel_buffer, const Array &type_definitions, const Array &arguments, const TypeArguments &type_arguments) const
Definition: object.cc:4833
ClassTable * class_table() const
Definition: isolate.h:496
void set_deopt_context(DeoptContext *value)
Definition: isolate.h:1256
static bool IsSystemIsolate(const Isolate *isolate)
Definition: isolate.h:1445
static Isolate * Current()
Definition: isolate.h:986
Debugger * debugger() const
Definition: isolate.h:1108
IsolateGroup * group() const
Definition: isolate.h:1037
void set_has_attempted_stepping(bool value)
Definition: isolate.h:1424
void AddValue(bool b) const
Definition: json_stream.h:494
void void void AddLocation(const Script &script, TokenPosition token_pos, TokenPosition end_token_pos=TokenPosition::kNoSource) const
Definition: json_stream.cc:521
void AddProperty(const char *name, bool b) const
Definition: json_stream.h:395
void AddUnresolvedLocation(const BreakpointLocation *bpt_loc) const
Definition: json_stream.cc:560
void AddFixedServiceId(const char *format,...) const PRINTF_ATTRIBUTE(2
Definition: json_stream.cc:503
ScriptPtr LookupScript(const String &url, bool useResolvedUri=false) const
Definition: object.cc:14009
void EnsureTopLevelClassIsFinalized() const
Definition: object.cc:14044
bool IsDebuggable() const
Definition: object.h:5281
intptr_t Length() const
Definition: object.cc:16149
StringPtr GetName(intptr_t var_index) const
Definition: object.cc:16030
void GetInfo(intptr_t var_index, UntaggedLocalVarDescriptors::VarInfo *info) const
Definition: object.cc:16046
static void static void PrintErr(const char *format,...) PRINTF_ATTRIBUTE(1
static DART_NORETURN void Abort()
static char * SCreate(Zone *zone, const char *format,...) PRINTF_ATTRIBUTE(2
void VisitPointer(ObjectPtr *p)
Definition: visitor.h:55
static ObjectPtr null()
Definition: object.h:433
ObjectPtr ptr() const
Definition: object.h:332
void Print() const
Definition: object.cc:2620
virtual const char * ToCString() const
Definition: object.h:366
bool IsNull() const
Definition: object.h:363
static Object & Handle()
Definition: object.h:407
intptr_t YieldIndex() const
Definition: object.h:6160
TokenPosition TokenPos() const
Definition: object.h:6156
intptr_t DeoptId() const
Definition: object.h:6155
intptr_t TryIndex() const
Definition: object.h:6159
UntaggedPcDescriptors::Kind Kind() const
Definition: object.h:6161
StringPtr url() const
Definition: object.h:4932
static StreamInfo isolate_stream
Definition: service.h:180
static void HandleEvent(ServiceEvent *event, bool enter_safepoint=true)
Definition: service.cc:1206
static StreamInfo debug_stream
Definition: service.h:181
StackFrame * NextFrame()
Definition: stack_frame.cc:549
intptr_t Length() const
Definition: object.cc:26020
uword PcOffsetAtFrame(intptr_t frame_index) const
Definition: object.cc:26036
ObjectPtr CodeAtFrame(intptr_t frame_index) const
Definition: object.cc:26025
bool enabled() const
Definition: service.h:80
intptr_t Length() const
Definition: object.h:10210
static const char * ScrubName(const String &name, bool is_extension=false)
Definition: object.cc:287
bool Equals(const String &str) const
Definition: object.h:13337
static StringPtr New(const char *cstr, Heap::Space space=Heap::kNew)
Definition: object.cc:23698
uint16_t CharAt(intptr_t index) const
Definition: object.h:10259
bool StartsWith(const String &other) const
Definition: object.h:10298
static const char * ToCString(Thread *thread, StringPtr ptr)
Definition: object.cc:24126
static constexpr intptr_t kSuspendStateVarIndex
Definition: object.h:12617
static const String & This()
Definition: symbols.h:692
static StringPtr New(Thread *thread, const char *cstr)
Definition: symbols.h:723
Zone * zone() const
Definition: thread_state.h:37
static Thread * Current()
Definition: thread.h:362
DART_WARN_UNUSED_RESULT ErrorPtr StealStickyError()
Definition: thread.cc:245
Isolate * isolate() const
Definition: thread.h:534
bool IsInStoppedMutatorsScope() const
Definition: thread.h:742
bool IsDebugPause() const
bool IsSynthetic() const
intptr_t Pos() const
const char * ToCString() const
static TokenPosition Deserialize(int32_t value)
bool IsWithin(const TokenPosition &a, const TokenPosition &b) const
TokenPosition Next()
intptr_t Length() const
Definition: object.cc:7294
void SetTypeAt(intptr_t index, const AbstractType &value) const
Definition: object.cc:7323
intptr_t Length() const
Definition: object.cc:6564
StringPtr NameAt(intptr_t index) const
Definition: object.cc:6574
AbstractTypePtr DefaultAt(intptr_t index) const
Definition: object.cc:6614
AbstractTypePtr BoundAt(intptr_t index) const
Definition: object.cc:6593
int value() const
Definition: scopes.h:69
char * PrintToString(const char *format,...) PRINTF_ATTRIBUTE(2
Definition: zone.cc:313
char * MakeCopyOfString(const char *str)
Definition: zone.cc:270
#define THR_Print(format,...)
Definition: log.h:20
#define UNIMPLEMENTED
#define ASSERT(E)
double frame
Definition: examples.cpp:31
if(end==-1)
glong glong end
FlKeyEvent * event
const uint8_t uint32_t uint32_t GError ** error
uint8_t value
Dart_NativeFunction function
Definition: fuchsia.cc:51
#define RELOAD_OPERATION_SCOPE(thread_expr)
size_t length
Win32Message message
Definition: dart_vm.cc:33
static bool IsPrivateVariableName(const String &var_name)
Definition: debugger.cc:1067
void SetBreakpoint(Dart_NativeArguments args)
const char *const name
static TokenPosition ResolveBreakpointPos(const Function &func, TokenPosition requested_token_pos, TokenPosition last_token_pos, intptr_t requested_column, TokenPosition exact_token_pos)
Definition: debugger.cc:2062
static void SetBreakpointState(Thread *thread, JSONStream *js)
Definition: service.cc:5571
void DeoptimizeFunctionsOnStack()
static TokenPosition FindExactTokenPosition(const Script &script, TokenPosition start_of_line, intptr_t column_number)
Definition: debugger.cc:3759
static DART_FORCE_INLINE intptr_t LocalVarIndex(intptr_t fp_offset, intptr_t var_index)
Definition: stack_frame.h:412
static bool IsSyntheticVariableName(const String &var_name)
Definition: debugger.cc:1063
static constexpr intptr_t kDefaultStackAllocation
Definition: stacktrace.cc:19
static bool IsImplicitFunction(const Function &func)
Definition: debugger.cc:393
DART_EXPORT bool IsNull(Dart_Handle object)
const char *const class_name
static void RemoveBreakpoint(Thread *thread, JSONStream *js)
Definition: service.cc:4040
static DART_FORCE_INLINE uword ParamAddress(uword fp, intptr_t reverse_index)
Definition: stack_frame.h:417
constexpr uword kUwordMax
Definition: globals.h:519
static ArrayPtr DeoptimizeToArray(Thread *thread, StackFrame *frame, const Code &code)
Definition: debugger.cc:1648
uintptr_t uword
Definition: globals.h:501
const uint32_t fp
static void Pause(Thread *thread, JSONStream *js)
Definition: service.cc:4414
static ActivationFrame * TopDartFrame()
Definition: debugger.cc:1779
static bool FunctionOverlaps(const Function &func, const String &script_url, TokenPosition token_pos, TokenPosition end_token_pos)
Definition: debugger.cc:378
DEFINE_FLAG(bool, print_cluster_information, false, "Print information about clusters written to snapshot")
static bool CanRewindFrame(intptr_t frame_index, const char **error)
Definition: debugger.cc:3215
const intptr_t cid
static bool IsAtAsyncJump(ActivationFrame *top_frame)
Definition: debugger.cc:3525
FrameLayout runtime_frame_layout
Definition: stack_frame.cc:81
static DART_FORCE_INLINE ObjectPtr GetVariableValue(uword addr)
Definition: debugger.cc:833
static uword LookupRewindPc(const Code &code, uword return_address)
Definition: debugger.cc:3257
static DART_FORCE_INLINE uword LocalVarAddress(uword fp, intptr_t index)
Definition: stack_frame.h:429
static void RefineBreakpointPos(const Script &script, TokenPosition pos, TokenPosition next_closest_token_position, TokenPosition requested_token_pos, TokenPosition last_token_pos, intptr_t requested_column, TokenPosition exact_token_pos, TokenPosition *best_fit_pos, intptr_t *best_column, intptr_t *best_line)
Definition: debugger.cc:1955
static DART_FORCE_INLINE bool IsCalleeFrameOf(uword fp, uword other_fp)
Definition: stack_frame.h:422
static void InvokeEventHandler(ServiceEvent *event)
Definition: debugger.cc:284
const uint8_t kSafepointKind
Definition: debugger.cc:1313
static const char * QualifiedFunctionName(const Function &func)
Definition: debugger.cc:366
static FunctionPtr FindInnermostClosure(Zone *zone, const Function &function, TokenPosition token_pos)
Definition: debugger.cc:2914
static constexpr intptr_t kInvalidTryIndex
static intptr_t FindNextRewindFrameIndex(DebuggerStackTrace *stack, intptr_t frame_index)
Definition: debugger.cc:3203
static void UpdateBestFit(Function *best_fit, const Function &func)
Definition: debugger.cc:2378
Dart_ExceptionPauseInfo
Definition: debugger.h:504
@ kPauseOnUnhandledExceptions
Definition: debugger.h:506
@ kNoPauseOnExceptions
Definition: debugger.h:505
@ kPauseOnAllExceptions
Definition: debugger.h:507
static bool IsFunctionVisible(const Function &function)
Definition: debugger.cc:1286
static ActivationFrame * CollectDartFrame(uword pc, StackFrame *frame, const Code &code, const Array &deopt_frame, intptr_t deopt_frame_offset)
Definition: debugger.cc:1631
DECLARE_FLAG(bool, show_invisible_frames)
def call(args)
Definition: dom.py:159
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
std::function< void()> closure
Definition: closure.h:14
scripts
Printing Glyph Map Stats.
static constexpr Color Min(Color c, float threshold)
Definition: color.cc:132
#define Px
Definition: globals.h:410
#define Pd
Definition: globals.h:408
intptr_t FrameSlotForVariableIndex(intptr_t index) const
Definition: stack_frame.cc:89
#define TIMELINE_DURATION(thread, stream, name)
Definition: timeline.h:39