Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
495
497 const Script& script = Script::Handle(SourceScript());
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()) {
556 const Script& script = Script::Handle(SourceScript());
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()) {
566 const Script& script = Script::Handle(SourceScript());
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
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
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) {
1234 const Script& script = Script::Handle(SourceScript());
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|.
1955static void RefineBreakpointPos(const Script& script,
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 Function& function = Function::Handle(zone);
2306 Array& functions = Array::Handle(zone);
2307
2308 const ClassTable& class_table = *isolate_->group()->class_table();
2309 const intptr_t num_classes = class_table.NumCids();
2310 const intptr_t num_tlc_classes = class_table.NumTopLevelCids();
2311 for (intptr_t i = 1; i < num_classes + num_tlc_classes; i++) {
2312 const intptr_t cid =
2313 i < num_classes ? i
2314 : ClassTable::CidFromTopLevelIndex(i - num_classes);
2315 if (class_table.HasValidClassAt(cid)) {
2316 cls = class_table.At(cid);
2317 // If the class is not finalized, e.g. if it hasn't been parsed
2318 // yet entirely, we can ignore it. If it contains a function with
2319 // an unresolved breakpoint, we will detect it if and when the
2320 // function gets compiled.
2321 if (!cls.is_finalized()) {
2322 continue;
2323 }
2324 // Note: we need to check the functions of this class even if
2325 // the class is defined in a different 'script'. There could
2326 // be mixin functions from the given script in this class.
2327 functions = cls.current_functions();
2328 if (!functions.IsNull()) {
2329 const intptr_t num_functions = functions.Length();
2330 for (intptr_t pos = 0; pos < num_functions; pos++) {
2331 function ^= functions.At(pos);
2332 ASSERT(!function.IsNull());
2333 bool function_added = false;
2334 if (function.is_debuggable() && function.HasCode() &&
2335 function.token_pos() == start_pos &&
2336 function.end_token_pos() == end_pos &&
2337 function.script() == script.ptr()) {
2338 code_function_list->Add(function);
2339 function_added = true;
2340 }
2341 if (function_added && function.HasImplicitClosureFunction()) {
2342 function = function.ImplicitClosureFunction();
2343 if (function.is_debuggable() && function.HasCode()) {
2344 code_function_list->Add(function);
2345 }
2346 }
2347 }
2348 }
2349 }
2350 }
2351 }
2352}
2353
2354static void UpdateBestFit(Function* best_fit, const Function& func) {
2355 if (best_fit->IsNull()) {
2356 *best_fit = func.ptr();
2357 } else if ((best_fit->token_pos().IsSynthetic() ||
2358 func.token_pos().IsSynthetic() ||
2359 (best_fit->token_pos() < func.token_pos())) &&
2360 (func.end_token_pos() <= best_fit->end_token_pos())) {
2361 *best_fit = func.ptr();
2362 }
2363}
2364
2365// Returns true if a best fit is found. A best fit can either be a function
2366// or a field. If it is a function, then the best fit function is returned
2367// in |best_fit|. If a best fit is a field, it means that a latent
2368// breakpoint can be set in the range |token_pos| to |last_token_pos|.
2369bool Debugger::FindBestFit(const Script& script,
2370 TokenPosition token_pos,
2371 TokenPosition last_token_pos,
2372 Function* best_fit) {
2373 auto thread = Thread::Current();
2374 auto isolate_group = thread->isolate_group();
2375 Zone* zone = thread->zone();
2376 Class& cls = Class::Handle(zone);
2377
2378 // A single script can belong to several libraries because of mixins.
2379 // Go through all libraries and for each that contains the script, try to find
2380 // a fit there.
2381 // Return the first fit found, but if a library doesn't contain a fit,
2382 // process the next one.
2383 const GrowableObjectArray& libs = GrowableObjectArray::Handle(
2384 zone, isolate_group->object_store()->libraries());
2385 Library& lib = Library::Handle(zone);
2386 for (int i = 0; i < libs.Length(); i++) {
2387 lib ^= libs.At(i);
2388 ASSERT(!lib.IsNull());
2389 const Array& scripts = Array::Handle(zone, lib.LoadedScripts());
2390 bool lib_has_script = false;
2391 for (intptr_t j = 0; j < scripts.Length(); j++) {
2392 if (scripts.At(j) == script.ptr()) {
2393 lib_has_script = true;
2394 break;
2395 }
2396 }
2397 if (!lib_has_script) {
2398 continue;
2399 }
2400
2401 if (!lib.IsDebuggable()) {
2402 if (FLAG_verbose_debug) {
2403 OS::PrintErr("Library '%s' has been marked as non-debuggable\n",
2404 lib.ToCString());
2405 }
2406 continue;
2407 }
2408
2409 const String& script_url = String::Handle(zone, script.url());
2410 ClosureFunctionsCache::ForAllClosureFunctions([&](const Function& fun) {
2411 if (FunctionOverlaps(fun, script_url, token_pos, last_token_pos)) {
2412 // Select the inner most closure.
2413 UpdateBestFit(best_fit, fun);
2414 }
2415 return true; // Continue iteration
2416 });
2417
2418 if (!best_fit->IsNull()) {
2419 // The inner most closure found will be the best fit. Going
2420 // over class functions below will not help in any further
2421 // narrowing.
2422 return true;
2423 }
2424
2425 Array& functions = Array::Handle(zone);
2426 Function& function = Function::Handle(zone);
2427 Array& fields = Array::Handle(zone);
2428 Field& field = Field::Handle(zone);
2429 Error& error = Error::Handle(zone);
2430
2431 const ClassTable& class_table = *isolate_->group()->class_table();
2432 const intptr_t num_classes = class_table.NumCids();
2433 const intptr_t num_tlc_classes = class_table.NumTopLevelCids();
2434 for (intptr_t i = 1; i < num_classes + num_tlc_classes; i++) {
2435 const intptr_t cid =
2436 i < num_classes ? i
2437 : ClassTable::CidFromTopLevelIndex(i - num_classes);
2438 if (!class_table.HasValidClassAt(cid)) {
2439 continue;
2440 }
2441 cls = class_table.At(cid);
2442 // This class is relevant to us only if it belongs to the
2443 // library to which |script| belongs.
2444 if (cls.library() != lib.ptr()) {
2445 continue;
2446 }
2447 // Parse class definition if not done yet.
2448 error = cls.EnsureIsFinalized(Thread::Current());
2449 if (!error.IsNull()) {
2450 // Ignore functions in this class.
2451 // TODO(hausner): Should we propagate this error? How?
2452 // EnsureIsFinalized only returns an error object if there
2453 // is no longjump base on the stack.
2454 continue;
2455 }
2456 functions = cls.current_functions();
2457 if (!functions.IsNull()) {
2458 const intptr_t num_functions = functions.Length();
2459 for (intptr_t pos = 0; pos < num_functions; pos++) {
2460 function ^= functions.At(pos);
2461 ASSERT(!function.IsNull());
2463 // Implicit functions do not have a user specifiable source
2464 // location.
2465 continue;
2466 }
2467 if (FunctionOverlaps(function, script_url, token_pos,
2468 last_token_pos)) {
2469 // Closures and inner functions within a class method are not
2470 // present in the functions of a class. Hence, we can return
2471 // right away as looking through other functions of a class
2472 // will not narrow down to any inner function/closure.
2473 *best_fit = function.ptr();
2474 return true;
2475 }
2476 }
2477 }
2478 // If none of the functions in the class contain token_pos, then we
2479 // check if it falls within a function literal initializer of a field
2480 // that has not been initialized yet. If the field (and hence the
2481 // function literal initializer) has already been initialized, then
2482 // it would have been found above in the object store as a closure.
2483 fields = cls.fields();
2484 if (!fields.IsNull()) {
2485 const intptr_t num_fields = fields.Length();
2486 for (intptr_t pos = 0; pos < num_fields; pos++) {
2487 TokenPosition start = TokenPosition::kNoSource;
2488 TokenPosition end = TokenPosition::kNoSource;
2489 field ^= fields.At(pos);
2490 ASSERT(!field.IsNull());
2491 if (field.Script() != script.ptr()) {
2492 // The field should be defined in the script we want to set
2493 // the breakpoint in.
2494 continue;
2495 }
2496 if (!field.has_nontrivial_initializer()) {
2497 continue;
2498 }
2499 start = field.token_pos();
2500 end = field.end_token_pos();
2501 if (token_pos.IsWithin(start, end) ||
2502 start.IsWithin(token_pos, last_token_pos)) {
2503 return true;
2504 }
2505 }
2506 }
2507 }
2508 }
2509 return false;
2510}
2511
2512// If |requested_column| is specified, then |exact_token_pos| must be the exact
2513// position where the user requested a column breakpoint to be set. If
2514// |requested_column| is |-1|, |exact_token_pos| must be
2515// |TokenPosition::kNoSource|.
2516BreakpointLocation* Debugger::SetCodeBreakpoints(
2517 const GrowableHandlePtrArray<const Script>& scripts,
2518 TokenPosition token_pos,
2519 TokenPosition last_token_pos,
2520 intptr_t requested_line,
2521 intptr_t requested_column,
2522 TokenPosition exact_token_pos,
2523 const GrowableObjectArray& functions) {
2524 ASSERT(
2525 (requested_column == -1 && exact_token_pos == TokenPosition::kNoSource) ||
2526 (requested_column > -1 && exact_token_pos != TokenPosition::kNoSource));
2527
2528 Function& function = Function::Handle();
2529 function ^= functions.At(0);
2530 TokenPosition breakpoint_pos = ResolveBreakpointPos(
2531 function, token_pos, last_token_pos, requested_column, exact_token_pos);
2532 if (!breakpoint_pos.IsReal()) {
2533 return nullptr;
2534 }
2535 const String& script_url = String::Handle(scripts.At(0).url());
2536 BreakpointLocation* loc =
2537 GetResolvedBreakpointLocation(script_url, breakpoint_pos);
2538 if (loc == nullptr) {
2539 // Find an existing unresolved breakpoint location.
2540 loc = GetBreakpointLocation(script_url, token_pos, requested_line,
2541 requested_column);
2542 }
2543 if (loc == nullptr) {
2544 loc = new BreakpointLocation(this, scripts, breakpoint_pos, breakpoint_pos,
2545 requested_line, requested_column);
2546 RegisterBreakpointLocation(loc);
2547 }
2548 // A source breakpoint for this location may already exists, but it may
2549 // not yet be resolved in code.
2550 if (loc->IsResolved()) {
2551 return loc;
2552 }
2553 loc->SetResolved(function, breakpoint_pos);
2554
2555 // Create code breakpoints for all compiled functions we found.
2556 Function& func = Function::Handle();
2557 const intptr_t num_functions = functions.Length();
2558 for (intptr_t i = 0; i < num_functions; i++) {
2559 func ^= functions.At(i);
2560 ASSERT(func.HasCode());
2561 group_debugger()->MakeCodeBreakpointAt(func, loc);
2562 }
2563 if (FLAG_verbose_debug) {
2564 intptr_t line_number = -1;
2565 intptr_t column_number = -1;
2566 scripts.At(0).GetTokenLocation(breakpoint_pos, &line_number,
2567 &column_number);
2568 OS::PrintErr("Resolved code breakpoint for function '%s' at line %" Pd
2569 " col %" Pd "\n",
2570 func.ToFullyQualifiedCString(), line_number, column_number);
2571 }
2572 return loc;
2573}
2574
2575#if !defined(DART_PRECOMPILED_RUNTIME)
2576static TokenPosition FindExactTokenPosition(const Script& script,
2577 TokenPosition start_of_line,
2578 intptr_t column_number);
2579#endif // !defined(DART_PRECOMPILED_RUNTIME)
2580
2581BreakpointLocation* Debugger::SetBreakpoint(const Script& script,
2582 TokenPosition token_pos,
2583 TokenPosition last_token_pos,
2584 intptr_t requested_line,
2585 intptr_t requested_column,
2586 const Function& function) {
2587 GrowableHandlePtrArray<const Script> scripts(Thread::Current()->zone(), 1);
2588 scripts.Add(script);
2589 return SetBreakpoint(scripts, token_pos, last_token_pos, requested_line,
2590 requested_column, function);
2591}
2592
2593BreakpointLocation* Debugger::SetBreakpoint(
2594 const GrowableHandlePtrArray<const Script>& scripts,
2595 TokenPosition token_pos,
2596 TokenPosition last_token_pos,
2597 intptr_t requested_line,
2598 intptr_t requested_column,
2599 const Function& function) {
2600 Function& func = Function::Handle();
2601 const Script& script = scripts.At(0);
2602 if (function.IsNull()) {
2603 if (!FindBestFit(script, token_pos, last_token_pos, &func)) {
2604 return nullptr;
2605 }
2606 // If func was not set (still Null), the best fit is a field.
2607 } else {
2608 func = function.ptr();
2609 if (!func.token_pos().IsReal()) {
2610 return nullptr; // Missing source positions?
2611 }
2612 }
2613
2614 if (!func.IsNull()) {
2615 // There may be more than one function object for a given function
2616 // in source code. There may be implicit closure functions, and
2617 // there may be copies of mixin functions. Collect all compiled
2618 // functions whose source code range matches exactly the best fit
2619 // function we found.
2620 GrowableObjectArray& code_functions =
2621 GrowableObjectArray::Handle(GrowableObjectArray::New());
2622 FindCompiledFunctions(scripts, func.token_pos(), func.end_token_pos(),
2623 &code_functions);
2624
2625 if (code_functions.Length() > 0) {
2626 // One or more function object containing this breakpoint location
2627 // have already been compiled. We can resolve the breakpoint now.
2628 // If requested_column is larger than zero, [token_pos, last_token_pos]
2629 // governs one single line of code.
2630 TokenPosition exact_token_pos = TokenPosition::kNoSource;
2631#if !defined(DART_PRECOMPILED_RUNTIME)
2632 if (token_pos != last_token_pos && requested_column >= 0) {
2633 exact_token_pos =
2634 FindExactTokenPosition(script, token_pos, requested_column);
2635 }
2636#endif // !defined(DART_PRECOMPILED_RUNTIME)
2637 BreakpointLocation* loc = nullptr;
2638 // Ensure that code stays deoptimized (and background compiler disabled)
2639 // until we have installed the breakpoint (at which point the compiler
2640 // will not try to optimize it anymore).
2641 RunWithStoppedDeoptimizedWorld([&] {
2642 loc = SetCodeBreakpoints(scripts, token_pos, last_token_pos,
2643 requested_line, requested_column,
2644 exact_token_pos, code_functions);
2645 });
2646 if (loc != nullptr) {
2647 return loc;
2648 }
2649 }
2650 }
2651 // There is either an uncompiled function, or an uncompiled function literal
2652 // initializer of a field at |token_pos|. Hence, Register an unresolved
2653 // breakpoint.
2654 if (FLAG_verbose_debug) {
2655 intptr_t line_number = -1;
2656 intptr_t column_number = -1;
2657 script.GetTokenLocation(token_pos, &line_number, &column_number);
2658 if (func.IsNull()) {
2659 OS::PrintErr(
2660 "Registering pending breakpoint for "
2661 "an uncompiled function literal at line %" Pd " col %" Pd "\n",
2662 line_number, column_number);
2663 } else {
2664 OS::PrintErr(
2665 "Registering pending breakpoint for "
2666 "uncompiled function '%s' at line %" Pd " col %" Pd "\n",
2667 func.ToFullyQualifiedCString(), line_number, column_number);
2668 }
2669 }
2670 const String& script_url = String::Handle(script.url());
2671 BreakpointLocation* loc = GetBreakpointLocation(
2672 script_url, token_pos, requested_line, requested_column);
2673 if (loc == nullptr) {
2674 loc = new BreakpointLocation(this, scripts, token_pos, last_token_pos,
2675 requested_line, requested_column);
2676 RegisterBreakpointLocation(loc);
2677 }
2678 return loc;
2679}
2680
2681// Synchronize the enabled/disabled state of all code breakpoints
2682// associated with the breakpoint location loc.
2683void GroupDebugger::SyncBreakpointLocation(BreakpointLocation* loc) {
2684 bool any_enabled = loc->AnyEnabled();
2685 SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
2686 CodeBreakpoint* cbpt = code_breakpoints_;
2687 while (cbpt != nullptr) {
2688 if (cbpt->HasBreakpointLocation(loc)) {
2689 if (any_enabled) {
2690 cbpt->Enable();
2691 } else {
2692 cbpt->Disable();
2693 }
2694 }
2695 cbpt = cbpt->next();
2696 }
2697}
2698
2699Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function,
2700 bool single_shot) {
2701 ASSERT(!target_function.IsNull());
2702 if (!target_function.is_debuggable()) {
2703 return nullptr;
2704 }
2705 const Script& script = Script::Handle(target_function.script());
2706 BreakpointLocation* bpt_location = SetBreakpoint(
2707 script, target_function.token_pos(), target_function.end_token_pos(), -1,
2708 -1 /* no requested line/col */, target_function);
2709 if (bpt_location == nullptr) {
2710 return nullptr;
2711 }
2712
2713 if (single_shot) {
2714 return bpt_location->AddSingleShot(this);
2715 } else {
2716 return bpt_location->AddRepeated(this);
2717 }
2718}
2719
2720Breakpoint* Debugger::SetBreakpointAtActivation(const Instance& closure,
2721 bool single_shot) {
2722 if (!closure.IsClosure()) {
2723 return nullptr;
2724 }
2725 const Function& func = Function::Handle(Closure::Cast(closure).function());
2726 const Script& script = Script::Handle(func.script());
2727 BreakpointLocation* bpt_location =
2728 SetBreakpoint(script, func.token_pos(), func.end_token_pos(), -1,
2729 -1 /* no line/col */, func);
2730 return bpt_location->AddBreakpoint(this, Closure::Cast(closure), single_shot);
2731}
2732
2733Breakpoint* Debugger::BreakpointAtActivation(const Instance& closure) {
2734 if (!closure.IsClosure()) {
2735 return nullptr;
2736 }
2737
2738 BreakpointLocation* loc = breakpoint_locations_;
2739 while (loc != nullptr) {
2740 Breakpoint* bpt = loc->breakpoints();
2741 while (bpt != nullptr) {
2742 if (closure.ptr() == bpt->closure()) {
2743 return bpt;
2744 }
2745 bpt = bpt->next();
2746 }
2747 loc = loc->next();
2748 }
2749
2750 return nullptr;
2751}
2752
2753void Debugger::SetBreakpointAtResumption(const Object& function_data) {
2754 ASSERT(!function_data.IsNull());
2755 ASSERT(function_data.IsInstance());
2756 breakpoints_at_resumption_.Add(function_data.ptr());
2757 isolate_->set_has_resumption_breakpoints(true);
2758}
2759
2760void Debugger::ResumptionBreakpoint() {
2761 ASSERT(!breakpoints_at_resumption_.is_empty());
2762 ASSERT(isolate_->has_resumption_breakpoints());
2763
2764 ActivationFrame* top_frame = TopDartFrame();
2765 ASSERT(top_frame->function().IsSuspendableFunction());
2766 const auto& function_data =
2767 Object::Handle(top_frame->GetSuspendableFunctionData());
2768
2769 for (intptr_t i = 0, n = breakpoints_at_resumption_.length(); i < n; ++i) {
2770 if (breakpoints_at_resumption_[i] == function_data.ptr()) {
2771 breakpoints_at_resumption_.RemoveAt(i);
2772 if (breakpoints_at_resumption_.is_empty()) {
2773 isolate_->set_has_resumption_breakpoints(false);
2774 }
2775 if (FLAG_verbose_debug) {
2776 OS::PrintErr(
2777 "ResumptionBreakpoint - hit a breakpoint, continue single "
2778 "stepping\n");
2779 }
2780 EnterSingleStepMode();
2781 return;
2782 }
2783 }
2784}
2785
2786Breakpoint* Debugger::SetBreakpointAtLine(const String& script_url,
2787 intptr_t line_number) {
2788 // Prevent future tests from calling this function in the wrong
2789 // execution state. If you hit this assert, consider using
2790 // Dart_SetBreakpoint instead.
2791 ASSERT(Thread::Current()->execution_state() == Thread::kThreadInVM);
2792
2793 BreakpointLocation* loc =
2794 BreakpointLocationAtLineCol(script_url, line_number, -1 /* no column */);
2795 if (loc != nullptr) {
2796 return loc->AddRepeated(this);
2797 }
2798 return nullptr;
2799}
2800
2801Breakpoint* Debugger::SetBreakpointAtLineCol(const String& script_url,
2802 intptr_t line_number,
2803 intptr_t column_number) {
2804 // Prevent future tests from calling this function in the wrong
2805 // execution state. If you hit this assert, consider using
2806 // Dart_SetBreakpoint instead.
2807 ASSERT(Thread::Current()->execution_state() == Thread::kThreadInVM);
2808
2809 BreakpointLocation* loc =
2810 BreakpointLocationAtLineCol(script_url, line_number, column_number);
2811 if (loc != nullptr) {
2812 return loc->AddRepeated(this);
2813 }
2814 return nullptr;
2815}
2816
2817BreakpointLocation* Debugger::BreakpointLocationAtLineCol(
2818 const String& script_url,
2819 intptr_t line_number,
2820 intptr_t column_number) {
2821 Zone* zone = Thread::Current()->zone();
2822 Library& lib = Library::Handle(zone);
2824 const GrowableObjectArray& libs = GrowableObjectArray::Handle(
2825 isolate_->group()->object_store()->libraries());
2826 bool is_package = script_url.StartsWith(Symbols::PackageScheme());
2827 bool is_dart_colon = script_url.StartsWith(Symbols::DartScheme());
2828 Script& script_for_lib = Script::Handle(zone);
2829 for (intptr_t i = 0; i < libs.Length(); i++) {
2830 lib ^= libs.At(i);
2831 // Ensure that all top-level members are loaded so their scripts
2832 // are available for look up. When certain script only contains
2833 // top level functions, scripts could still be loaded correctly.
2835 bool useResolvedUri = !is_package && !is_dart_colon;
2836 script_for_lib = lib.LookupScript(script_url, useResolvedUri);
2837 if (!script_for_lib.IsNull()) {
2838 scripts.Add(script_for_lib);
2839 }
2840 }
2841 if (scripts.length() == 0) {
2842 // No script found with given url. Create a latent breakpoint which
2843 // will be set if the url is loaded later.
2844 BreakpointLocation* latent_bpt =
2845 GetLatentBreakpoint(script_url, line_number, column_number);
2846 if (FLAG_verbose_debug) {
2847 OS::PrintErr(
2848 "Set latent breakpoint in url '%s' at "
2849 "line %" Pd " col %" Pd "\n",
2850 script_url.ToCString(), line_number, column_number);
2851 }
2852 return latent_bpt;
2853 }
2854 TokenPosition first_token_idx = TokenPosition::kNoSource;
2855 TokenPosition last_token_idx = TokenPosition::kNoSource;
2856 // Assume all scripts with the same URL have the same token positions.
2857 scripts.At(0).TokenRangeAtLine(line_number, &first_token_idx,
2858 &last_token_idx);
2859 if (!first_token_idx.IsReal()) {
2860 // Script does not contain the given line number.
2861 if (FLAG_verbose_debug) {
2862 OS::PrintErr("Script '%s' does not contain line number %" Pd "\n",
2863 script_url.ToCString(), line_number);
2864 }
2865 return nullptr;
2866 } else if (!last_token_idx.IsReal()) {
2867 // Line does not contain any tokens.
2868 if (FLAG_verbose_debug) {
2869 OS::PrintErr("No executable code at line %" Pd " in '%s'\n", line_number,
2870 script_url.ToCString());
2871 }
2872 return nullptr;
2873 }
2874
2875 BreakpointLocation* loc = nullptr;
2876 ASSERT(first_token_idx <= last_token_idx);
2877 while ((loc == nullptr) && (first_token_idx <= last_token_idx)) {
2878 loc = SetBreakpoint(scripts, first_token_idx, last_token_idx, line_number,
2879 column_number, Function::Handle());
2880 first_token_idx = first_token_idx.Next();
2881 }
2882 if ((loc == nullptr) && FLAG_verbose_debug) {
2883 OS::PrintErr("No executable code at line %" Pd " in '%s'\n", line_number,
2884 script_url.ToCString());
2885 }
2886 return loc;
2887}
2888
2889// Return innermost closure contained in 'function' that contains
2890// the given token position.
2891static FunctionPtr FindInnermostClosure(Zone* zone,
2892 const Function& function,
2893 TokenPosition token_pos) {
2894 ASSERT(function.end_token_pos().IsReal());
2895 const TokenPosition& func_start = function.token_pos();
2896 const Script& outer_origin = Script::Handle(zone, function.script());
2897
2898 Function& best_fit = Function::Handle(zone);
2899 ClosureFunctionsCache::ForAllClosureFunctions([&](const Function& closure) {
2900 const TokenPosition& closure_start = closure.token_pos();
2901 const TokenPosition& closure_end = closure.end_token_pos();
2902 // We're only interested in closures that have real ending token positions.
2903 // The starting token position can be synthetic.
2904 if (closure_end.IsReal() && (function.end_token_pos() > closure_end) &&
2905 (!closure_start.IsReal() || !func_start.IsReal() ||
2906 (closure_start > func_start)) &&
2907 token_pos.IsWithin(closure_start, closure_end) &&
2908 (closure.script() == outer_origin.ptr())) {
2909 UpdateBestFit(&best_fit, closure);
2910 }
2911 return true; // Continue iteration.
2912 });
2913 return best_fit.ptr();
2914}
2915
2916bool GroupDebugger::EnsureLocationIsInFunction(Zone* zone,
2917 const Function& function,
2918 BreakpointLocation* location) {
2919 const String& url = String::Handle(zone, location->url());
2920 if (!FunctionOverlaps(function, url, location->token_pos(),
2921 location->end_token_pos())) {
2922 return false;
2923 }
2924
2925 TokenPosition token_pos = location->token_pos();
2926#if !defined(DART_PRECOMPILED_RUNTIME)
2927 TokenPosition end_token_pos = location->end_token_pos();
2928 if (token_pos != end_token_pos && location->requested_column_number() >= 0) {
2929 // Narrow down the token position range to a single value
2930 // if requested column number is provided so that inner
2931 // Closure won't be missed.
2932 const Script& script = Script::Handle(location->script());
2933 token_pos = FindExactTokenPosition(script, token_pos,
2934 location->requested_column_number());
2935 }
2936#endif // !defined(DART_PRECOMPILED_RUNTIME)
2937 const Function& inner_function =
2938 Function::Handle(zone, FindInnermostClosure(zone, function, token_pos));
2939 if (!inner_function.IsNull()) {
2940 if (FLAG_verbose_debug) {
2941 OS::PrintErr(
2942 "Pending breakpoint remains unresolved in "
2943 "inner function '%s'\n",
2944 inner_function.ToFullyQualifiedCString());
2945 }
2946 return false;
2947 }
2948
2949 // There is no local function within function that contains the
2950 // breakpoint token position.
2951 return true;
2952}
2953
2954void GroupDebugger::NotifyCompilation(const Function& function) {
2955 if (!function.is_debuggable()) {
2956 return;
2957 }
2958 Function& resolved_function = Function::Handle(function.ptr());
2959 auto thread = Thread::Current();
2960 auto zone = thread->zone();
2961
2962 // Going through BreakpointLocations of all isolates and debuggers looking
2963 // for those that can be resolved and added code breakpoints at now.
2964 //
2965 // The check below is used instead of breakpoint_locations_lock acquisition.
2966 // We don't need to acquire the lock if always run with stopped mutators.
2967 // We can't acquire the lock if we run with stopped mutators as that could
2968 // result in deadlock.
2969 RELEASE_ASSERT(thread->IsInStoppedMutatorsScope());
2970 for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
2971 BreakpointLocation* location = breakpoint_locations_.At(i);
2972 if (EnsureLocationIsInFunction(zone, resolved_function, location)) {
2973 // All mutators are stopped (see RELEASE_ASSERT above). We temporarily
2974 // enter the isolate for which the breakpoint was registered.
2975 // The code path below may issue service events which will use the active
2976 // isolate's object-id ring for naming VM objects.
2977 ActiveIsolateScope active_isolate(thread,
2978 location->debugger()->isolate());
2979
2980 // Ensure the location is resolved for the original function.
2981 TokenPosition exact_token_pos = TokenPosition::kNoSource;
2982#if !defined(DART_PRECOMPILED_RUNTIME)
2983 if (location->token_pos() != location->end_token_pos() &&
2984 location->requested_column_number() >= 0) {
2985 exact_token_pos = FindExactTokenPosition(
2986 Script::Handle(location->script()), location->token_pos(),
2987 location->requested_column_number());
2988 }
2989#endif // !defined(DART_PRECOMPILED_RUNTIME)
2990 location->EnsureIsResolved(function, exact_token_pos);
2991 if (FLAG_verbose_debug) {
2992 Breakpoint* bpt = location->breakpoints();
2993 while (bpt != nullptr) {
2994 OS::PrintErr("Setting breakpoint %" Pd " for %s '%s'\n", bpt->id(),
2995 function.IsClosureFunction() ? "closure" : "function",
2996 function.ToFullyQualifiedCString());
2997 bpt = bpt->next();
2998 }
2999 }
3000 MakeCodeBreakpointAt(function, location);
3001 }
3002 }
3003}
3004
3005void GroupDebugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
3006 CodeBreakpoint* cbpt = code_breakpoints_;
3007 while (cbpt != nullptr) {
3008 cbpt->VisitObjectPointers(visitor);
3009 cbpt = cbpt->next();
3010 }
3011}
3012
3013// static
3014void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
3015 ASSERT(visitor != nullptr);
3016 BreakpointLocation* loc = breakpoint_locations_;
3017 while (loc != nullptr) {
3018 loc->VisitObjectPointers(visitor);
3019 loc = loc->next();
3020 }
3021 loc = latent_locations_;
3022 while (loc != nullptr) {
3023 loc->VisitObjectPointers(visitor);
3024 loc = loc->next();
3025 }
3026 for (intptr_t i = 0, n = breakpoints_at_resumption_.length(); i < n; ++i) {
3027 visitor->VisitPointer(&breakpoints_at_resumption_[i]);
3028 }
3029}
3030
3031void Debugger::Pause(ServiceEvent* event) {
3032 ASSERT(event->IsPause()); // Should call InvokeEventHandler instead.
3033 ASSERT(!ignore_breakpoints_); // We shouldn't get here when ignoring bpts.
3034 ASSERT(!IsPaused()); // No recursive pausing.
3035
3036 pause_event_ = event;
3037 pause_event_->UpdateTimestamp();
3038
3039 // We are about to invoke the debugger's event handler. Disable
3040 // interrupts for this thread while waiting for debug commands over
3041 // the service protocol.
3042 {
3043 Thread* thread = Thread::Current();
3044 DisableThreadInterruptsScope dtis(thread);
3045 TIMELINE_DURATION(thread, Debugger, "Debugger Pause");
3046
3047 // Send the pause event.
3048 Service::HandleEvent(event);
3049
3050 {
3051 TransitionVMToNative transition(thread);
3052 isolate_->PauseEventHandler();
3053 }
3054
3055 // Notify the service that we have resumed.
3056 const Error& error = Error::Handle(Thread::Current()->sticky_error());
3057 ASSERT(error.IsNull() || error.IsUnwindError() ||
3058 error.IsUnhandledException());
3059
3060 // Only send a resume event when the isolate is not unwinding.
3061 if (!error.IsUnwindError()) {
3062 ServiceEvent resume_event(event->isolate(), ServiceEvent::kResume);
3063 resume_event.set_top_frame(event->top_frame());
3064 Service::HandleEvent(&resume_event);
3065 }
3066 }
3067
3068 group_debugger()->Pause();
3069 pause_event_ = nullptr;
3070}
3071
3072void GroupDebugger::Pause() {
3073 SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
3074 if (needs_breakpoint_cleanup_) {
3075 RemoveUnlinkedCodeBreakpoints();
3076 }
3077}
3078
3079void Debugger::EnterSingleStepMode() {
3080 ResetSteppingFramePointer();
3081 NotifySingleStepping(true);
3082}
3083
3084void Debugger::ResetSteppingFramePointer() {
3085 stepping_fp_ = 0;
3086}
3087
3088void Debugger::SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace) {
3089 if (stack_trace->Length() > 0) {
3090 stepping_fp_ = stack_trace->FrameAt(0)->fp();
3091 } else {
3092 stepping_fp_ = 0;
3093 }
3094}
3095
3096void Debugger::HandleSteppingRequest(bool skip_next_step /* = false */) {
3097 ResetSteppingFramePointer();
3098 if (resume_action_ == kStepInto) {
3099 // When single stepping, we need to deoptimize because we might be
3100 // stepping into optimized code. This happens in particular if
3101 // the isolate has been interrupted, but can happen in other cases
3102 // as well. We need to deoptimize the world in case we are about
3103 // to call an optimized function.
3104 NotifySingleStepping(true);
3105 skip_next_step_ = skip_next_step;
3106 if (FLAG_verbose_debug) {
3107 OS::PrintErr("HandleSteppingRequest - kStepInto\n");
3108 }
3109 } else if (resume_action_ == kStepOver) {
3110 NotifySingleStepping(true);
3111 skip_next_step_ = skip_next_step;
3112 SetSyncSteppingFramePointer(stack_trace_);
3113 if (FLAG_verbose_debug) {
3114 OS::PrintErr("HandleSteppingRequest - kStepOver stepping_fp=%" Px "\n",
3115 stepping_fp_);
3116 }
3117 } else if (resume_action_ == kStepOut) {
3118 // Check if we have an asynchronous awaiter for the current frame.
3119 if (async_awaiter_stack_trace_ != nullptr &&
3120 async_awaiter_stack_trace_->Length() > 2 &&
3121 async_awaiter_stack_trace_->FrameAt(1)->kind() ==
3122 ActivationFrame::kAsyncSuspensionMarker) {
3123 auto awaiter_frame = async_awaiter_stack_trace_->FrameAt(2);
3124 AsyncStepInto(awaiter_frame->closure());
3125 if (FLAG_verbose_debug) {
3126 OS::PrintErr("HandleSteppingRequest - continue to async awaiter %s\n",
3127 Function::Handle(awaiter_frame->closure().function())
3128 .ToFullyQualifiedCString());
3129 }
3130 return;
3131 }
3132
3133 // Fall through to synchronous stepping.
3134 NotifySingleStepping(true);
3135 // Find topmost caller that is debuggable.
3136 for (intptr_t i = 1; i < stack_trace_->Length(); i++) {
3137 ActivationFrame* frame = stack_trace_->FrameAt(i);
3138 if (frame->IsDebuggable()) {
3139 stepping_fp_ = frame->fp();
3140 break;
3141 }
3142 }
3143 if (FLAG_verbose_debug) {
3144 OS::PrintErr("HandleSteppingRequest- kStepOut %" Px "\n", stepping_fp_);
3145 }
3146 } else if (resume_action_ == kStepRewind) {
3147 if (FLAG_trace_rewind) {
3148 OS::PrintErr("Rewinding to frame %" Pd "\n", resume_frame_index_);
3149 OS::PrintErr(
3150 "-------------------------\n"
3151 "All frames...\n\n");
3152 StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
3153 Thread::Current(),
3154 StackFrameIterator::kNoCrossThreadIteration);
3155 StackFrame* frame = iterator.NextFrame();
3156 intptr_t num = 0;
3157 while ((frame != nullptr)) {
3158 OS::PrintErr("#%04" Pd " %s\n", num++, frame->ToCString());
3159 frame = iterator.NextFrame();
3160 }
3161 }
3162 RewindToFrame(resume_frame_index_);
3163 UNREACHABLE();
3164 }
3165}
3166
3167void Debugger::CacheStackTraces(DebuggerStackTrace* stack_trace,
3168 DebuggerStackTrace* async_awaiter_stack_trace) {
3169 ASSERT(stack_trace_ == nullptr);
3170 stack_trace_ = stack_trace;
3171 ASSERT(async_awaiter_stack_trace_ == nullptr);
3172 async_awaiter_stack_trace_ = async_awaiter_stack_trace;
3173}
3174
3175void Debugger::ClearCachedStackTraces() {
3176 stack_trace_ = nullptr;
3177 async_awaiter_stack_trace_ = nullptr;
3178}
3179
3181 intptr_t frame_index) {
3182 for (intptr_t i = frame_index + 1; i < stack->Length(); i++) {
3183 ActivationFrame* frame = stack->FrameAt(i);
3184 if (frame->IsRewindable()) {
3185 return i;
3186 }
3187 }
3188 return -1;
3189}
3190
3191// Can we rewind to the indicated frame?
3192static bool CanRewindFrame(intptr_t frame_index, const char** error) {
3193 // check rewind pc is found
3194 DebuggerStackTrace* stack = Isolate::Current()->debugger()->StackTrace();
3195 intptr_t num_frames = stack->Length();
3196 if (frame_index < 1 || frame_index >= num_frames) {
3197 if (error != nullptr) {
3198 *error = Thread::Current()->zone()->PrintToString(
3199 "Frame must be in bounds [1..%" Pd
3200 "]: "
3201 "saw %" Pd "",
3202 num_frames - 1, frame_index);
3203 }
3204 return false;
3205 }
3206 ActivationFrame* frame = stack->FrameAt(frame_index);
3207 if (!frame->IsRewindable()) {
3208 intptr_t next_index = FindNextRewindFrameIndex(stack, frame_index);
3209 if (next_index > 0) {
3210 *error = Thread::Current()->zone()->PrintToString(
3211 "Cannot rewind to frame %" Pd
3212 " due to conflicting compiler "
3213 "optimizations. "
3214 "Run the vm with --no-prune-dead-locals to disallow these "
3215 "optimizations. "
3216 "Next valid rewind frame is %" Pd ".",
3217 frame_index, next_index);
3218 } else {
3219 *error = Thread::Current()->zone()->PrintToString(
3220 "Cannot rewind to frame %" Pd
3221 " due to conflicting compiler "
3222 "optimizations. "
3223 "Run the vm with --no-prune-dead-locals to disallow these "
3224 "optimizations.",
3225 frame_index);
3226 }
3227 return false;
3228 }
3229 return true;
3230}
3231
3232// Given a return address, find the "rewind" pc, which is the pc
3233// before the corresponding call.
3234static uword LookupRewindPc(const Code& code, uword return_address) {
3235 ASSERT(!code.is_optimized());
3236 ASSERT(code.ContainsInstructionAt(return_address));
3237
3238 uword pc_offset = return_address - code.PayloadStart();
3239 const PcDescriptors& descriptors =
3240 PcDescriptors::Handle(code.pc_descriptors());
3241 PcDescriptors::Iterator iter(descriptors,
3242 UntaggedPcDescriptors::kRewind |
3243 UntaggedPcDescriptors::kIcCall |
3244 UntaggedPcDescriptors::kUnoptStaticCall);
3245 intptr_t rewind_deopt_id = -1;
3246 uword rewind_pc = 0;
3247 while (iter.MoveNext()) {
3248 if (iter.Kind() == UntaggedPcDescriptors::kRewind) {
3249 // Remember the last rewind so we don't need to iterator twice.
3250 rewind_pc = code.PayloadStart() + iter.PcOffset();
3251 rewind_deopt_id = iter.DeoptId();
3252 }
3253 if ((pc_offset == iter.PcOffset()) && (iter.DeoptId() == rewind_deopt_id)) {
3254 return rewind_pc;
3255 }
3256 }
3257 return 0;
3258}
3259
3260void Debugger::RewindToFrame(intptr_t frame_index) {
3261 Thread* thread = Thread::Current();
3262 Zone* zone = thread->zone();
3263 Code& code = Code::Handle(zone);
3264 Function& function = Function::Handle(zone);
3265
3266 // Find the requested frame.
3267 StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
3268 Thread::Current(),
3269 StackFrameIterator::kNoCrossThreadIteration);
3270 intptr_t current_frame = 0;
3271 for (StackFrame* frame = iterator.NextFrame(); frame != nullptr;
3272 frame = iterator.NextFrame()) {
3273 ASSERT(frame->IsValid());
3274 if (frame->IsDartFrame()) {
3275 code = frame->LookupDartCode();
3276 function = code.function();
3277 if (!IsFunctionVisible(function)) {
3278 continue;
3279 }
3280 if (code.is_optimized()) {
3281 intptr_t sub_index = 0;
3282 for (InlinedFunctionsIterator it(code, frame->pc()); !it.Done();
3283 it.Advance()) {
3284 if (current_frame == frame_index) {
3285 RewindToOptimizedFrame(frame, code, sub_index);
3286 UNREACHABLE();
3287 }
3288 current_frame++;
3289 sub_index++;
3290 }
3291 } else {
3292 if (current_frame == frame_index) {
3293 // We are rewinding to an unoptimized frame.
3294 RewindToUnoptimizedFrame(frame, code);
3295 UNREACHABLE();
3296 }
3297 current_frame++;
3298 }
3299 }
3300 }
3301 UNIMPLEMENTED();
3302}
3303
3304void Debugger::RewindToUnoptimizedFrame(StackFrame* frame, const Code& code) {
3305 // We will be jumping out of the debugger rather than exiting this
3306 // function, so prepare the debugger state.
3307 ClearCachedStackTraces();
3308 set_resume_action(kContinue);
3309 resume_frame_index_ = -1;
3310 EnterSingleStepMode();
3311
3312 uword rewind_pc = LookupRewindPc(code, frame->pc());
3313 if (FLAG_trace_rewind && rewind_pc == 0) {
3314 OS::PrintErr("Unable to find rewind pc for pc(%" Px ")\n", frame->pc());
3315 }
3316 ASSERT(rewind_pc != 0);
3317 if (FLAG_trace_rewind) {
3318 OS::PrintErr(
3319 "===============================\n"
3320 "Rewinding to unoptimized frame:\n"
3321 " rewind_pc(0x%" Px " offset:0x%" Px ") sp(0x%" Px ") fp(0x%" Px
3322 ")\n"
3323 "===============================\n",
3324 rewind_pc, rewind_pc - code.PayloadStart(), frame->sp(), frame->fp());
3325 }
3326 Exceptions::JumpToFrame(Thread::Current(), rewind_pc, frame->sp(),
3327 frame->fp(), true /* clear lazy deopt at target */);
3328 UNREACHABLE();
3329}
3330
3331void Debugger::RewindToOptimizedFrame(StackFrame* frame,
3332 const Code& optimized_code,
3333 intptr_t sub_index) {
3334 post_deopt_frame_index_ = sub_index;
3335
3336 // We will be jumping out of the debugger rather than exiting this
3337 // function, so prepare the debugger state.
3338 ClearCachedStackTraces();
3339 set_resume_action(kContinue);
3340 resume_frame_index_ = -1;
3341 EnterSingleStepMode();
3342
3343 if (FLAG_trace_rewind) {
3344 OS::PrintErr(
3345 "===============================\n"
3346 "Deoptimizing frame for rewind:\n"
3347 " deopt_pc(0x%" Px ") sp(0x%" Px ") fp(0x%" Px
3348 ")\n"
3349 "===============================\n",
3350 frame->pc(), frame->sp(), frame->fp());
3351 }
3352 Thread* thread = Thread::Current();
3353 thread->set_resume_pc(frame->pc());
3354 uword deopt_stub_pc = StubCode::DeoptForRewind().EntryPoint();
3355 Exceptions::JumpToFrame(thread, deopt_stub_pc, frame->sp(), frame->fp(),
3356 true /* clear lazy deopt at target */);
3357 UNREACHABLE();
3358}
3359
3360void Debugger::RewindPostDeopt() {
3361 intptr_t rewind_frame = post_deopt_frame_index_;
3362 post_deopt_frame_index_ = -1;
3363 if (FLAG_trace_rewind) {
3364 OS::PrintErr("Post deopt, jumping to frame %" Pd "\n", rewind_frame);
3365 OS::PrintErr(
3366 "-------------------------\n"
3367 "All frames...\n\n");
3368 StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
3369 Thread::Current(),
3370 StackFrameIterator::kNoCrossThreadIteration);
3371 StackFrame* frame = iterator.NextFrame();
3372 intptr_t num = 0;
3373 while ((frame != nullptr)) {
3374 OS::PrintErr("#%04" Pd " %s\n", num++, frame->ToCString());
3375 frame = iterator.NextFrame();
3376 }
3377 }
3378
3379 Thread* thread = Thread::Current();
3380 Zone* zone = thread->zone();
3381 Code& code = Code::Handle(zone);
3382
3383 StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
3384 Thread::Current(),
3385 StackFrameIterator::kNoCrossThreadIteration);
3386 intptr_t current_frame = 0;
3387 for (StackFrame* frame = iterator.NextFrame(); frame != nullptr;
3388 frame = iterator.NextFrame()) {
3389 ASSERT(frame->IsValid());
3390 if (frame->IsDartFrame()) {
3391 code = frame->LookupDartCode();
3392 ASSERT(!code.is_optimized());
3393 if (current_frame == rewind_frame) {
3394 RewindToUnoptimizedFrame(frame, code);
3395 UNREACHABLE();
3396 }
3397 current_frame++;
3398 }
3399 }
3400}
3401
3402// static
3403bool Debugger::IsDebuggable(const Function& func) {
3404 if (!func.is_debuggable()) {
3405 return false;
3406 }
3407 const Class& cls = Class::Handle(func.Owner());
3408 const Library& lib = Library::Handle(cls.library());
3409 return lib.IsDebuggable();
3410}
3411
3412void GroupDebugger::RegisterSingleSteppingDebugger(Thread* thread,
3413 const Debugger* debugger) {
3414 WriteRwLocker sl(Thread::Current(), single_stepping_set_lock());
3415 single_stepping_set_.Insert(debugger);
3416}
3417
3418void GroupDebugger::UnregisterSingleSteppingDebugger(Thread* thread,
3419 const Debugger* debugger) {
3420 WriteRwLocker sl(Thread::Current(), single_stepping_set_lock());
3421 single_stepping_set_.Remove(debugger);
3422}
3423
3424bool GroupDebugger::HasBreakpointUnsafe(Thread* thread,
3425 const Function& function) {
3427 breakpoint_locations_lock()->IsCurrentThreadReader());
3428 // Check if function has any breakpoints.
3429 String& url = String::Handle(thread->zone());
3430 for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
3431 BreakpointLocation* location = breakpoint_locations_.At(i);
3432 url = location->url();
3433 if (FunctionOverlaps(function, url, location->token_pos(),
3434 location->end_token_pos())) {
3435 return true;
3436 }
3437 }
3438 return false;
3439}
3440
3441bool GroupDebugger::HasBreakpoint(Thread* thread, const Function& function) {
3442 bool hasBreakpoint = false;
3443 // Don't need to worry about the lock if mutators are stopped.
3444 if (thread->IsInStoppedMutatorsScope()) {
3445 hasBreakpoint = HasBreakpointUnsafe(thread, function);
3446 } else {
3447 SafepointReadRwLocker sl(thread, breakpoint_locations_lock());
3448 hasBreakpoint = HasBreakpointUnsafe(thread, function);
3449 }
3450 if (hasBreakpoint) {
3451 return true;
3452 }
3453
3454 // TODO(aam): do we have to iterate over both code breakpoints and
3455 // breakpoint locations? Wouldn't be sufficient to iterate over only
3456 // one list? Could you have a CodeBreakpoint without corresponding
3457 // BreakpointLocation?
3458 if (HasCodeBreakpointInFunction(function)) {
3459 return true;
3460 }
3461
3462 return false;
3463}
3464
3465bool GroupDebugger::IsDebugging(Thread* thread, const Function& function) {
3466 {
3467 ReadRwLocker ml(thread, single_stepping_set_lock());
3468 if (!single_stepping_set_.IsEmpty()) {
3469 return true;
3470 }
3471 }
3472 return HasBreakpoint(thread, function);
3473}
3474
3475void Debugger::set_resume_action(ResumeAction resume_action) {
3476 auto thread = Thread::Current();
3477 if (resume_action == kContinue) {
3478 group_debugger()->UnregisterSingleSteppingDebugger(thread, this);
3479 } else {
3480 group_debugger()->RegisterSingleSteppingDebugger(thread, this);
3481 }
3482 resume_action_ = resume_action;
3483}
3484
3485void Debugger::SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt) {
3486 set_resume_action(kContinue);
3487 ResetSteppingFramePointer();
3488 NotifySingleStepping(false);
3489 ASSERT(!IsPaused());
3490 if ((bpt != nullptr) && bpt->is_single_shot()) {
3491 RemoveBreakpoint(bpt->id());
3492 bpt = nullptr;
3493 }
3494
3495 ServiceEvent event(isolate_, ServiceEvent::kPauseBreakpoint);
3496 event.set_top_frame(top_frame);
3497 event.set_breakpoint(bpt);
3498 event.set_at_async_jump(IsAtAsyncJump(top_frame));
3499 Pause(&event);
3500}
3501
3502static bool IsAtAsyncJump(ActivationFrame* top_frame) {
3503 Zone* zone = Thread::Current()->zone();
3504 if (!top_frame->function().IsAsyncFunction() &&
3505 !top_frame->function().IsAsyncGenerator()) {
3506 return false;
3507 }
3508 const auto& pc_descriptors =
3509 PcDescriptors::Handle(zone, top_frame->code().pc_descriptors());
3510 if (pc_descriptors.IsNull()) {
3511 return false;
3512 }
3513 const TokenPosition looking_for = top_frame->TokenPos();
3514 PcDescriptors::Iterator it(pc_descriptors, UntaggedPcDescriptors::kOther);
3515 while (it.MoveNext()) {
3516 if (it.TokenPos() == looking_for &&
3517 it.YieldIndex() != UntaggedPcDescriptors::kInvalidYieldIndex) {
3518 return true;
3519 }
3520 }
3521 return false;
3522}
3523
3524ErrorPtr Debugger::PauseStepping() {
3525 ASSERT(isolate_->single_step());
3526 // Don't pause recursively.
3527 if (IsPaused()) {
3528 return Error::null();
3529 }
3530 if (skip_next_step_) {
3531 skip_next_step_ = false;
3532 return Error::null();
3533 }
3534
3535 // Check whether we are in a Dart function that the user is
3536 // interested in. If we saved the frame pointer of a stack frame
3537 // the user is interested in, we ignore the single step if we are
3538 // in a callee of that frame. Note that we assume that the stack
3539 // grows towards lower addresses.
3541 ASSERT(frame != nullptr);
3542
3543 if (stepping_fp_ != 0) {
3544 // There is an "interesting frame" set. Only pause at appropriate
3545 // locations in this frame.
3546 const ActivationFrame::Relation relation = frame->CompareTo(stepping_fp_);
3547 if (relation == ActivationFrame::kCallee) {
3548 // We are in a callee of the frame we're interested in.
3549 // Ignore this stepping break.
3550 return Error::null();
3551 } else if (relation == ActivationFrame::kCaller) {
3552 // We returned from the "interesting frame", there can be no more
3553 // stepping breaks for it. Pause at the next appropriate location
3554 // and let the user set the "interesting" frame again.
3555 ResetSteppingFramePointer();
3556 }
3557 }
3558
3559 if (!frame->IsDebuggable()) {
3560 return Error::null();
3561 }
3562 if (!frame->TokenPos().IsDebugPause()) {
3563 return Error::null();
3564 }
3565
3566 if (frame->fp() == last_stepping_fp_ &&
3567 frame->TokenPos() == last_stepping_pos_) {
3568 // Do not stop multiple times for the same token position.
3569 // Several 'debug checked' opcodes may be issued in the same token range.
3570 return Error::null();
3571 }
3572
3573 // TODO(dartbug.com/48378): Consider aligning async/async* functions
3574 // with regular function wrt the first stop in the function prologue.
3575 if ((frame->function().IsAsyncFunction() ||
3576 frame->function().IsAsyncGenerator()) &&
3577 frame->GetSuspendStateVar() == Object::null()) {
3578 return Error::null();
3579 }
3580
3581 // We are stopping in this frame at the token pos.
3582 last_stepping_fp_ = frame->fp();
3583 last_stepping_pos_ = frame->TokenPos();
3584
3585 // If there is an active breakpoint at this pc, then we should have
3586 // already bailed out of this function in the skip_next_step_ test
3587 // above.
3588 ASSERT(!group_debugger()->HasActiveBreakpoint(frame->pc()));
3589
3590 if (FLAG_verbose_debug) {
3591 OS::PrintErr(">>> single step break at %s:%" Pd ":%" Pd
3592 " (func %s token %s address %#" Px " offset %#" Px ")\n",
3593 String::Handle(frame->SourceUrl()).ToCString(),
3594 frame->LineNumber(), frame->ColumnNumber(),
3595 String::Handle(frame->QualifiedFunctionName()).ToCString(),
3596 frame->TokenPos().ToCString(), frame->pc(),
3597 frame->pc() - frame->code().PayloadStart());
3598 }
3599
3600 CacheStackTraces(DebuggerStackTrace::Collect(),
3601 DebuggerStackTrace::CollectAsyncAwaiters());
3602 SignalPausedEvent(frame, nullptr);
3603 HandleSteppingRequest();
3604 ClearCachedStackTraces();
3605
3606 // If any error occurred while in the debug message loop, return it here.
3607 return Thread::Current()->StealStickyError();
3608}
3609
3610ErrorPtr Debugger::PauseBreakpoint() {
3611 // We ignore this breakpoint when the VM is executing code invoked
3612 // by the debugger to evaluate variables values, or when we see a nested
3613 // breakpoint or exception event.
3614 if (ignore_breakpoints_ || IsPaused()) {
3615 return Error::null();
3616 }
3617 DebuggerStackTrace* stack_trace = DebuggerStackTrace::Collect();
3618 ASSERT(stack_trace->Length() > 0);
3619 ActivationFrame* top_frame = stack_trace->FrameAt(0);
3620 ASSERT(top_frame != nullptr);
3621 if (!Library::Handle(top_frame->Library()).IsDebuggable()) {
3622 return Error::null();
3623 }
3624
3625 BreakpointLocation* bpt_location = nullptr;
3626 const char* cbpt_tostring = nullptr;
3627 {
3628 SafepointReadRwLocker cbl(Thread::Current(),
3629 group_debugger()->code_breakpoints_lock());
3630 CodeBreakpoint* cbpt = nullptr;
3631 bpt_location = group_debugger()->GetBreakpointLocationFor(
3632 this, top_frame->pc(), &cbpt);
3633 if (bpt_location == nullptr) {
3634 // There might be no breakpoint locations for this isolate/debugger.
3635 return Error::null();
3636 }
3637 ASSERT(cbpt != nullptr);
3638 if (FLAG_verbose_debug) {
3639 cbpt_tostring = cbpt->ToCString();
3640 }
3641 }
3642
3643 Breakpoint* bpt_hit = bpt_location->FindHitBreakpoint(top_frame);
3644 if (bpt_hit == nullptr) {
3645 return Error::null();
3646 }
3647
3648 if (FLAG_verbose_debug) {
3649 OS::PrintErr(">>> hit %" Pd
3650 " %s"
3651 " (func %s token %s address %#" Px " offset %#" Px ")\n",
3652 bpt_hit->id(), cbpt_tostring,
3653 String::Handle(top_frame->QualifiedFunctionName()).ToCString(),
3654 bpt_location->token_pos().ToCString(), top_frame->pc(),
3655 top_frame->pc() - top_frame->code().PayloadStart());
3656 }
3657
3658 CacheStackTraces(stack_trace, DebuggerStackTrace::CollectAsyncAwaiters());
3659 SignalPausedEvent(top_frame, bpt_hit);
3660 // When we single step from a user breakpoint, our next stepping
3661 // point will be at the exact same pc. Skip it.
3662 HandleSteppingRequest(/*skip_next_step=*/true);
3663 ClearCachedStackTraces();
3664
3665 // If any error occurred while in the debug message loop, return it here.
3666 return Thread::Current()->StealStickyError();
3667}
3668
3669Breakpoint* BreakpointLocation::FindHitBreakpoint(ActivationFrame* top_frame) {
3670 // There may be more than one applicable breakpoint at this location, but we
3671 // will report only one as reached. If there is a single-shot breakpoint, we
3672 // favor it; then a closure-specific breakpoint ; then an general breakpoint.
3673
3674 // First check for a single-shot breakpoint.
3675 Breakpoint* bpt = breakpoints();
3676 while (bpt != nullptr) {
3677 if (bpt->is_single_shot() && bpt->closure() == Instance::null()) {
3678 return bpt;
3679 }
3680 bpt = bpt->next();
3681 }
3682
3683 // Now check for a closure-specific breakpoint.
3684 bpt = breakpoints();
3685 while (bpt != nullptr) {
3686 if (bpt->closure() != Instance::null() &&
3687 bpt->closure() == top_frame->GetClosure()) {
3688 return bpt;
3689 }
3690 bpt = bpt->next();
3691 }
3692
3693 // Finally, check for a general breakpoint.
3694 bpt = breakpoints();
3695 while (bpt != nullptr) {
3696 if (!bpt->is_single_shot() && bpt->closure() == Instance::null()) {
3697 return bpt;
3698 }
3699 bpt = bpt->next();
3700 }
3701
3702 return nullptr;
3703}
3704
3705void Debugger::PauseDeveloper(const String& msg) {
3706 // We ignore this breakpoint when the VM is executing code invoked
3707 // by the debugger to evaluate variables values, or when we see a nested
3708 // breakpoint or exception event.
3709 if (ignore_breakpoints_ || IsPaused()) {
3710 return;
3711 }
3712
3713 DebuggerStackTrace* stack_trace = DebuggerStackTrace::Collect();
3714 ASSERT(stack_trace->Length() > 0);
3715 CacheStackTraces(stack_trace, DebuggerStackTrace::CollectAsyncAwaiters());
3716 // TODO(johnmccutchan): Send |msg| to Observatory.
3717
3718 // We are in the native call to Developer_debugger. the developer
3719 // gets a better experience by not seeing this call. To accomplish
3720 // this, we continue execution until the call exits (step out).
3721 SetResumeAction(kStepOut);
3722 HandleSteppingRequest();
3723 ClearCachedStackTraces();
3724}
3725
3726void Debugger::NotifyIsolateCreated() {
3727 if (NeedsIsolateEvents()) {
3728 ServiceEvent event(isolate_, ServiceEvent::kIsolateStart);
3730 }
3731}
3732
3733#if !defined(DART_PRECOMPILED_RUNTIME)
3734// On single line of code with given column number,
3735// Calculate exact tokenPosition
3737 TokenPosition start_of_line,
3738 intptr_t column_number) {
3739 intptr_t line;
3740 intptr_t col;
3741 if (script.GetTokenLocation(start_of_line, &line, &col)) {
3742 return TokenPosition::Deserialize(start_of_line.Pos() +
3743 (column_number - col));
3744 }
3745 return TokenPosition::kNoSource;
3746}
3747#endif // !defined(DART_PRECOMPILED_RUNTIME)
3748
3749void Debugger::NotifyDoneLoading() {
3750 if (latent_locations_ == nullptr) {
3751 // Common, fast path.
3752 return;
3753 }
3754 auto thread = Thread::Current();
3755 auto isolate_group = thread->isolate_group();
3756 auto zone = thread->zone();
3757 Library& lib = Library::Handle(zone);
3758 Script& script = Script::Handle(zone);
3759 String& url = String::Handle(zone);
3760 BreakpointLocation* loc = latent_locations_;
3761 BreakpointLocation* prev_loc = nullptr;
3762 const GrowableObjectArray& libs =
3763 GrowableObjectArray::Handle(isolate_group->object_store()->libraries());
3764
3766 while (loc != nullptr) {
3767 url = loc->url();
3768 bool found_match = false;
3769 bool is_package = url.StartsWith(Symbols::PackageScheme());
3770 for (intptr_t i = 0; i < libs.Length(); i++) {
3771 lib ^= libs.At(i);
3772 script = lib.LookupScript(url, !is_package);
3773 if (!script.IsNull()) {
3774 scripts.Add(script);
3775 }
3776 }
3777 if (scripts.length() > 0) {
3778 // Found a script with matching url for this latent breakpoint.
3779 // Unlink the latent breakpoint from the list.
3780 found_match = true;
3781 BreakpointLocation* matched_loc = loc;
3782 loc = loc->next();
3783 if (prev_loc == nullptr) {
3784 latent_locations_ = loc;
3785 } else {
3786 prev_loc->set_next(loc);
3787 }
3788 // Now find the token range at the requested line and make a
3789 // new unresolved source breakpoint.
3790 intptr_t line_number = matched_loc->requested_line_number();
3791 intptr_t column_number = matched_loc->requested_column_number();
3792 ASSERT(line_number >= 0);
3793 TokenPosition first_token_pos = TokenPosition::kNoSource;
3794 TokenPosition last_token_pos = TokenPosition::kNoSource;
3795 scripts.At(0).TokenRangeAtLine(line_number, &first_token_pos,
3796 &last_token_pos);
3797 if (!first_token_pos.IsDebugPause() || !last_token_pos.IsDebugPause()) {
3798 // Script does not contain the given line number or there are no
3799 // tokens on the line. Drop the breakpoint silently.
3800 Breakpoint* bpt = matched_loc->breakpoints();
3801 while (bpt != nullptr) {
3802 if (FLAG_verbose_debug) {
3803 OS::PrintErr("No code found at line %" Pd
3804 ": "
3805 "dropping latent breakpoint %" Pd " in '%s'\n",
3806 line_number, bpt->id(), url.ToCString());
3807 }
3808 Breakpoint* prev = bpt;
3809 bpt = bpt->next();
3810 delete prev;
3811 }
3812 delete matched_loc;
3813 } else {
3814 // We don't expect to already have a breakpoint for this location.
3815 // If there is one, assert in debug build but silently drop
3816 // the latent breakpoint in release build.
3817 BreakpointLocation* existing_loc =
3818 GetBreakpointLocation(url, first_token_pos, -1, column_number);
3819 ASSERT(existing_loc == nullptr);
3820 if (existing_loc == nullptr) {
3821 // Create and register a new source breakpoint for the
3822 // latent breakpoint.
3823 BreakpointLocation* unresolved_loc = new BreakpointLocation(
3824 this, scripts, first_token_pos, last_token_pos, line_number,
3825 column_number);
3826 RegisterBreakpointLocation(unresolved_loc);
3827
3828 // Move breakpoints over.
3829 Breakpoint* bpt = matched_loc->breakpoints();
3830 unresolved_loc->set_breakpoints(bpt);
3831 matched_loc->set_breakpoints(nullptr);
3832 while (bpt != nullptr) {
3833 bpt->set_bpt_location(unresolved_loc);
3834 if (FLAG_verbose_debug) {
3835 OS::PrintErr(
3836 "Converted latent breakpoint "
3837 "%" Pd " in '%s' at line %" Pd " col %" Pd "\n",
3838 bpt->id(), url.ToCString(), line_number, column_number);
3839 }
3840 bpt = bpt->next();
3841 }
3842 group_debugger()->SyncBreakpointLocation(unresolved_loc);
3843 }
3844 delete matched_loc;
3845 // Break out of the iteration over loaded libraries. If the
3846 // same url has been loaded into more than one library, we
3847 // only set a breakpoint in the first one.
3848 // TODO(hausner): There is one possible pitfall here.
3849 // If the user sets a latent breakpoint using a partial url that
3850 // ends up matching more than one script, the breakpoint might
3851 // get set in the wrong script.
3852 // It would be better if we could warn the user if multiple
3853 // scripts are matching.
3854 break;
3855 }
3856 }
3857 if (!found_match) {
3858 // No matching url found in any of the libraries.
3859 if (FLAG_verbose_debug) {
3860 Breakpoint* bpt = loc->breakpoints();
3861 while (bpt != nullptr) {
3862 OS::PrintErr(
3863 "No match found for latent breakpoint id "
3864 "%" Pd " with url '%s'\n",
3865 bpt->id(), url.ToCString());
3866 bpt = bpt->next();
3867 }
3868 }
3869 loc = loc->next();
3870 }
3871 }
3872}
3873
3874// TODO(hausner): Could potentially make this faster by checking
3875// whether the call target at pc is a debugger stub.
3876bool GroupDebugger::HasActiveBreakpoint(uword pc) {
3877 SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
3878 CodeBreakpoint* cbpt = GetCodeBreakpoint(pc);
3879 return (cbpt != nullptr) && (cbpt->IsEnabled());
3880}
3881
3882CodeBreakpoint* GroupDebugger::GetCodeBreakpoint(uword breakpoint_address) {
3883 CodeBreakpoint* cbpt = code_breakpoints_;
3884 while (cbpt != nullptr) {
3885 if (cbpt->pc() == breakpoint_address) {
3886 return cbpt;
3887 }
3888 cbpt = cbpt->next();
3889 }
3890 return nullptr;
3891}
3892
3893BreakpointLocation* GroupDebugger::GetBreakpointLocationFor(
3894 Debugger* debugger,
3895 uword breakpoint_address,
3896 CodeBreakpoint** pcbpt) {
3897 ASSERT(pcbpt != nullptr);
3898 SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
3899 *pcbpt = code_breakpoints_;
3900 while (*pcbpt != nullptr) {
3901 if ((*pcbpt)->pc() == breakpoint_address) {
3902 return (*pcbpt)->FindBreakpointForDebugger(debugger);
3903 }
3904 *pcbpt = (*pcbpt)->next();
3905 }
3906 return nullptr;
3907}
3908
3909void GroupDebugger::RegisterCodeBreakpoint(CodeBreakpoint* cbpt) {
3910 ASSERT(cbpt->next() == nullptr);
3911 DEBUG_ASSERT(code_breakpoints_lock()->IsCurrentThreadWriter() ||
3912 Thread::Current()->IsInStoppedMutatorsScope());
3913 cbpt->set_next(code_breakpoints_);
3914 code_breakpoints_ = cbpt;
3915}
3916
3917CodePtr GroupDebugger::GetPatchedStubAddress(uword breakpoint_address) {
3918 SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
3919 CodeBreakpoint* cbpt = GetCodeBreakpoint(breakpoint_address);
3920 if (cbpt != nullptr) {
3921 return cbpt->OrigStubAddress();
3922 }
3923 UNREACHABLE();
3924 return Code::null();
3925}
3926
3927bool Debugger::SetBreakpointState(Breakpoint* bpt, bool enable) {
3928 SafepointWriteRwLocker sl(Thread::Current(),
3929 group_debugger()->breakpoint_locations_lock());
3930 if (bpt->is_enabled() != enable) {
3931 if (FLAG_verbose_debug) {
3932 OS::PrintErr("Setting breakpoint %" Pd " to state: %s\n", bpt->id(),
3933 enable ? "enabled" : "disabled");
3934 }
3935 enable ? bpt->Enable() : bpt->Disable();
3936 group_debugger()->SyncBreakpointLocation(bpt->bpt_location());
3937 return true;
3938 }
3939 return false;
3940}
3941
3942// Remove and delete the source breakpoint bpt and its associated
3943// code breakpoints.
3944void Debugger::RemoveBreakpoint(intptr_t bp_id) {
3945 SafepointWriteRwLocker sl(Thread::Current(),
3946 group_debugger()->breakpoint_locations_lock());
3947 if (RemoveBreakpointFromTheList(bp_id, &breakpoint_locations_)) {
3948 return;
3949 }
3950 RemoveBreakpointFromTheList(bp_id, &latent_locations_);
3951}
3952
3953// Remove and delete the source breakpoint bpt and its associated
3954// code breakpoints. Returns true, if breakpoint was found and removed,
3955// returns false, if breakpoint was not found.
3956bool Debugger::RemoveBreakpointFromTheList(intptr_t bp_id,
3957 BreakpointLocation** list) {
3958 BreakpointLocation* prev_loc = nullptr;
3959 BreakpointLocation* curr_loc = *list;
3960 while (curr_loc != nullptr) {
3961 Breakpoint* prev_bpt = nullptr;
3962 Breakpoint* curr_bpt = curr_loc->breakpoints();
3963 while (curr_bpt != nullptr) {
3964 if (curr_bpt->id() == bp_id) {
3965 if (prev_bpt == nullptr) {
3966 curr_loc->set_breakpoints(curr_bpt->next());
3967 } else {
3968 prev_bpt->set_next(curr_bpt->next());
3969 }
3970
3971 // Send event to client before the breakpoint's fields are
3972 // poisoned and deleted.
3973 SendBreakpointEvent(ServiceEvent::kBreakpointRemoved, curr_bpt);
3974
3975 curr_bpt->set_next(nullptr);
3976 curr_bpt->set_bpt_location(nullptr);
3977 // Remove possible references to the breakpoint.
3978 if (pause_event_ != nullptr && pause_event_->breakpoint() == curr_bpt) {
3979 pause_event_->set_breakpoint(nullptr);
3980 }
3981 delete curr_bpt;
3982 curr_bpt = nullptr;
3983
3984 // Delete the breakpoint location object if there are no more
3985 // breakpoints at that location.
3986 if (curr_loc->breakpoints() == nullptr) {
3987 if (prev_loc == nullptr) {
3988 *list = curr_loc->next();
3989 } else {
3990 prev_loc->set_next(curr_loc->next());
3991 }
3992
3993 if (!curr_loc->IsLatent()) {
3994 // Remove references from code breakpoints to this breakpoint
3995 // location and disable them.
3996 // Latent breakpoint locations won't have code breakpoints.
3997 group_debugger()->UnlinkCodeBreakpoints(curr_loc);
3998 }
3999 group_debugger()->UnregisterBreakpointLocation(curr_loc);
4000 BreakpointLocation* next_loc = curr_loc->next();
4001 delete curr_loc;
4002 curr_loc = next_loc;
4003 }
4004
4005 // The code breakpoints will be deleted when the VM resumes
4006 // after the pause event.
4007 return true;
4008 }
4009
4010 prev_bpt = curr_bpt;
4011 curr_bpt = curr_bpt->next();
4012 }
4013 prev_loc = curr_loc;
4014 curr_loc = curr_loc->next();
4015 }
4016 // breakpoint with bp_id does not exist, nothing to do.
4017 return false;
4018}
4019
4020void GroupDebugger::RegisterBreakpointLocation(BreakpointLocation* location) {
4021 DEBUG_ASSERT(breakpoint_locations_lock()->IsCurrentThreadWriter() ||
4022 Thread::Current()->IsInStoppedMutatorsScope());
4023 breakpoint_locations_.Add(location);
4024}
4025
4026void GroupDebugger::UnregisterBreakpointLocation(BreakpointLocation* location) {
4027 ASSERT(breakpoint_locations_lock()->IsCurrentThreadWriter());
4028 for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
4029 if (breakpoint_locations_.At(i) == location) {
4030 breakpoint_locations_.EraseAt(i);
4031 return;
4032 }
4033 }
4034}
4035
4036// Unlink code breakpoints from the given breakpoint location.
4037// They will later be deleted when control returns from the pause event
4038// callback. Also, disable the breakpoint so it no longer fires if it
4039// should be hit before it gets deleted.
4040void GroupDebugger::UnlinkCodeBreakpoints(BreakpointLocation* bpt_location) {
4041 ASSERT(bpt_location != nullptr);
4042 SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
4043 CodeBreakpoint* curr_bpt = code_breakpoints_;
4044 while (curr_bpt != nullptr) {
4045 if (curr_bpt->FindAndDeleteBreakpointLocation(bpt_location)) {
4046 curr_bpt->Disable();
4047 needs_breakpoint_cleanup_ = true;
4048 }
4049 curr_bpt = curr_bpt->next();
4050 }
4051}
4052
4053// Remove and delete unlinked code breakpoints, i.e. breakpoints that
4054// are not associated with a breakpoint location.
4055void GroupDebugger::RemoveUnlinkedCodeBreakpoints() {
4056 DEBUG_ASSERT(code_breakpoints_lock()->IsCurrentThreadWriter() ||
4057 Thread::Current()->IsInStoppedMutatorsScope());
4058 CodeBreakpoint* prev_bpt = nullptr;
4059 CodeBreakpoint* curr_bpt = code_breakpoints_;
4060 while (curr_bpt != nullptr) {
4061 if (curr_bpt->HasNoBreakpointLocations()) {
4062 if (prev_bpt == nullptr) {
4063 code_breakpoints_ = code_breakpoints_->next();
4064 } else {
4065 prev_bpt->set_next(curr_bpt->next());
4066 }
4067 CodeBreakpoint* temp_bpt = curr_bpt;
4068 curr_bpt = curr_bpt->next();
4069 delete temp_bpt;
4070 } else {
4071 prev_bpt = curr_bpt;
4072 curr_bpt = curr_bpt->next();
4073 }
4074 }
4075 needs_breakpoint_cleanup_ = false;
4076}
4077
4078BreakpointLocation* Debugger::GetResolvedBreakpointLocation(
4079 const String& script_url,
4080 TokenPosition code_token_pos) {
4081 BreakpointLocation* loc = breakpoint_locations_;
4082 String& loc_url = String::Handle();
4083 while (loc != nullptr) {
4084 loc_url = loc->url();
4085 if (script_url.Equals(loc_url) && loc->code_token_pos_ == code_token_pos) {
4086 return loc;
4087 }
4088 loc = loc->next();
4089 }
4090 return nullptr;
4091}
4092
4093BreakpointLocation* Debugger::GetBreakpointLocation(
4094 const String& script_url,
4095 TokenPosition token_pos,
4096 intptr_t requested_line,
4097 intptr_t requested_column,
4098 TokenPosition code_token_pos) {
4099 BreakpointLocation* loc = breakpoint_locations_;
4100 String& loc_url = String::Handle();
4101 while (loc != nullptr) {
4102 loc_url = loc->url();
4103 if (script_url.Equals(loc_url) &&
4104 (!token_pos.IsReal() || (loc->token_pos() == token_pos)) &&
4105 ((requested_line == -1) ||
4106 (loc->requested_line_number_ == requested_line)) &&
4107 ((requested_column == -1) ||
4108 (loc->requested_column_number_ == requested_column)) &&
4109 (!code_token_pos.IsReal() ||
4110 (loc->code_token_pos_ == code_token_pos))) {
4111 return loc;
4112 }
4113 loc = loc->next();
4114 }
4115 return nullptr;
4116}
4117
4118Breakpoint* Debugger::GetBreakpointById(intptr_t id) {
4119 Breakpoint* bpt = GetBreakpointByIdInTheList(id, breakpoint_locations_);
4120 if (bpt != nullptr) {
4121 return bpt;
4122 }
4123 return GetBreakpointByIdInTheList(id, latent_locations_);
4124}
4125
4126Breakpoint* Debugger::GetBreakpointByIdInTheList(intptr_t id,
4127 BreakpointLocation* list) {
4128 BreakpointLocation* loc = list;
4129 while (loc != nullptr) {
4130 Breakpoint* bpt = loc->breakpoints();
4131 while (bpt != nullptr) {
4132 if (bpt->id() == id) {
4133 return bpt;
4134 }
4135 bpt = bpt->next();
4136 }
4137 loc = loc->next();
4138 }
4139 return nullptr;
4140}
4141
4142void Debugger::AsyncStepInto(const Closure& awaiter) {
4143 Zone* zone = Thread::Current()->zone();
4144
4145 auto& suspend_state = SuspendState::Handle(zone);
4146 if (StackTraceUtils::GetSuspendState(awaiter, &suspend_state)) {
4147 const auto& function_data =
4148 Object::Handle(zone, suspend_state.function_data());
4149 SetBreakpointAtResumption(function_data);
4150 } else {
4151 SetBreakpointAtActivation(awaiter, /*single_shot=*/true);
4152 }
4153 Continue();
4154}
4155
4156void Debugger::Continue() {
4157 SetResumeAction(kContinue);
4158 ResetSteppingFramePointer();
4159 NotifySingleStepping(false);
4160}
4161
4162BreakpointLocation* Debugger::GetLatentBreakpoint(const String& url,
4163 intptr_t line,
4164 intptr_t column) {
4165 BreakpointLocation* loc = latent_locations_;
4166 String& bpt_url = String::Handle();
4167 while (loc != nullptr) {
4168 bpt_url = loc->url();
4169 if (bpt_url.Equals(url) && (loc->requested_line_number() == line) &&
4170 (loc->requested_column_number() == column)) {
4171 return loc;
4172 }
4173 loc = loc->next();
4174 }
4175 // No breakpoint for this location requested. Allocate new one.
4176 loc = new BreakpointLocation(this, url, line, column);
4177 loc->set_next(latent_locations_);
4178 latent_locations_ = loc;
4179 return loc;
4180}
4181
4182void Debugger::RegisterBreakpointLocationUnsafe(BreakpointLocation* loc) {
4184 group_debugger()->breakpoint_locations_lock()->IsCurrentThreadWriter() ||
4185 Thread::Current()->IsInStoppedMutatorsScope());
4186 ASSERT(loc->next() == nullptr);
4187 loc->set_next(breakpoint_locations_);
4188 breakpoint_locations_ = loc;
4189 group_debugger()->RegisterBreakpointLocation(loc);
4190}
4191
4192void Debugger::RegisterBreakpointLocation(BreakpointLocation* loc) {
4193 auto thread = Thread::Current();
4194 if (thread->IsInStoppedMutatorsScope()) {
4195 RegisterBreakpointLocationUnsafe(loc);
4196 } else {
4197 SafepointWriteRwLocker sl(thread,
4198 group_debugger()->breakpoint_locations_lock());
4199 RegisterBreakpointLocationUnsafe(loc);
4200 }
4201}
4202
4203#endif // !PRODUCT
4204
4205} // 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
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:316
ArrayPtr GetLocalVariables()
Definition debugger.cc:1033
uword fp() const
Definition debugger.h:307
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:308
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:306
Kind kind() const
Definition debugger.h:304
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:317
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:10933
ObjectPtr At(intptr_t index) const
Definition object.h:10854
intptr_t Length() const
Definition object.h:10808
void SetAt(intptr_t index, const Object &value) const
Definition object.h:10858
void Add(const T &value)
intptr_t length() const
TokenPosition token_pos() const
Definition debugger.h:126
bool AnyEnabled() const
Definition debugger.cc:115
TokenPosition end_token_pos() const
Definition debugger.h:128
Debugger * debugger()
Definition debugger.h:156
intptr_t requested_column_number() const
Definition debugger.h:139
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:136
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:150
ScriptPtr script() const
Definition debugger.h:130
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:138
bool IsLatent() const
Definition debugger.h:151
ClosurePtr closure() const
Definition debugger.h:68
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 PrintJSON(JSONStream *stream)
Definition debugger.cc:191
static intptr_t CidFromTopLevelIndex(intptr_t index)
LibraryPtr library() const
Definition object.h:1335
StringPtr Name() const
Definition object.cc:3038
bool IsTopLevel() const
Definition object.cc:6176
static void ForAllClosureFunctions(std::function< bool(const Function &)> callback)
FunctionPtr function() const
Definition debugger.h:222
bool IsEnabled() const
Definition debugger.h:233
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:227
bool HasBreakpointLocation(BreakpointLocation *breakpoint_location)
Definition debugger.cc:1360
bool FindAndDeleteBreakpointLocation(BreakpointLocation *breakpoint_location)
Definition debugger.cc:1370
uword pc() const
Definition debugger.h:224
PcDescriptorsPtr pc_descriptors() const
Definition object.h:6900
bool is_optimized() const
Definition object.h:6790
LocalVarDescriptorsPtr GetLocalVarDescriptors() const
Definition object.cc:17675
void Disassemble(DisassemblyFormatter *formatter=nullptr) const
Definition object.cc:17899
uword PayloadStart() const
Definition object.h:6823
static ErrorPtr EnsureUnoptimizedCode(Thread *thread, const Function &function)
Definition compiler.cc:855
void Dump(int indent=0) const
Definition object.cc:18598
intptr_t Length() const
Definition debugger.h:463
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:465
bool has_async_catch_error() const
Definition debugger.h:477
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:671
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:714
@ kStepOverAsyncSuspension
Definition debugger.h:665
void SetBreakpointAtResumption(const Object &function_data)
Definition debugger.cc:2753
static bool IsDebuggable(const Function &func)
Definition debugger.cc:3403
intptr_t MaterializeDeferredObjects()
static constexpr intptr_t kNone
Definition deopt_id.h:27
static DART_NORETURN void PropagateError(const Error &error)
const char * ToFullyQualifiedCString() const
Definition object.cc:9820
bool IsSuspendableFunction() const
Definition object.h:3937
bool HasCode() const
Definition object.cc:7994
FunctionPtr parent_function() const
Definition object.cc:8225
intptr_t NumTypeArguments() const
Definition object.cc:8911
StringPtr name() const
Definition object.h:2972
TokenPosition token_pos() const
Definition object.h:3426
bool HasOptimizedCode() const
Definition object.cc:11032
intptr_t NumTypeParameters() const
Definition object.cc:8905
ScriptPtr script() const
Definition object.cc:10939
bool IsAsyncFunction() const
Definition object.h:3942
bool IsAsyncGenerator() const
Definition object.h:3952
CodePtr unoptimized_code() const
Definition object.h:3165
TypeParametersPtr type_parameters() const
Definition object.h:3109
ClassPtr Owner() const
Definition object.cc:10899
intptr_t num_fixed_parameters() const
Definition object.cc:8914
UntaggedFunction::Kind kind() const
Definition object.h:3329
TokenPosition end_token_pos() const
Definition object.h:3435
GroupDebugger(IsolateGroup *isolate_group)
Definition debugger.cc:1391
bool HasCodeBreakpointInFunction(const Function &func)
Definition debugger.cc:427
void UnregisterBreakpointLocation(BreakpointLocation *location)
Definition debugger.cc:4026
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:614
void UnlinkCodeBreakpoints(BreakpointLocation *bpt_location)
Definition debugger.cc:4040
void Add(const Object &value, Heap::Space space=Heap::kNew) const
Definition object.cc:25070
void SetLength(intptr_t value) const
Definition object.h:11050
intptr_t Length() const
Definition object.h:11046
ObjectPtr At(intptr_t index) const
Definition object.h:11059
void SetAt(intptr_t index, const Object &value) const
Definition object.h:11065
void Grow(intptr_t new_capacity, Heap::Space space=Heap::kNew) const
Definition object.cc:25087
bool IsInstanceOf(const AbstractType &other, const TypeArguments &other_instantiator_type_arguments, const TypeArguments &other_function_type_arguments) const
Definition object.cc:20655
ObjectPtr EvaluateCompiledExpression(const Class &klass, const ExternalTypedData &kernel_buffer, const Array &type_definitions, const Array &arguments, const TypeArguments &type_arguments) const
Definition object.cc:4888
ClassTable * class_table() const
Definition isolate.h:491
void set_deopt_context(DeoptContext *value)
Definition isolate.h:1209
static bool IsSystemIsolate(const Isolate *isolate)
Definition isolate.h:1398
static Isolate * Current()
Definition isolate.h:939
Debugger * debugger() const
Definition isolate.h:1061
IsolateGroup * group() const
Definition isolate.h:990
void set_has_attempted_stepping(bool value)
Definition isolate.h:1377
void AddValue(bool b) const
void void void AddLocation(const Script &script, TokenPosition token_pos, TokenPosition end_token_pos=TokenPosition::kNoSource) const
void AddProperty(const char *name, bool b) const
void AddUnresolvedLocation(const BreakpointLocation *bpt_loc) const
void AddFixedServiceId(const char *format,...) const PRINTF_ATTRIBUTE(2
ScriptPtr LookupScript(const String &url, bool useResolvedUri=false) const
Definition object.cc:14056
void EnsureTopLevelClassIsFinalized() const
Definition object.cc:14091
bool IsDebuggable() const
Definition object.h:5252
intptr_t Length() const
Definition object.cc:16196
StringPtr GetName(intptr_t var_index) const
Definition object.cc:16077
void GetInfo(intptr_t var_index, UntaggedLocalVarDescriptors::VarInfo *info) const
Definition object.cc:16093
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:2681
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:6133
TokenPosition TokenPos() const
Definition object.h:6129
intptr_t DeoptId() const
Definition object.h:6128
intptr_t TryIndex() const
Definition object.h:6132
UntaggedPcDescriptors::Kind Kind() const
Definition object.h:6134
StringPtr url() const
Definition object.h:4903
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
intptr_t Length() const
Definition object.cc:26099
uword PcOffsetAtFrame(intptr_t frame_index) const
Definition object.cc:26115
ObjectPtr CodeAtFrame(intptr_t frame_index) const
Definition object.cc:26104
bool enabled() const
Definition service.h:80
intptr_t Length() const
Definition object.h:10189
static const char * ScrubName(const String &name, bool is_extension=false)
Definition object.cc:287
bool Equals(const String &str) const
Definition object.h:13311
static StringPtr New(const char *cstr, Heap::Space space=Heap::kNew)
Definition object.cc:23777
uint16_t CharAt(intptr_t index) const
Definition object.h:10238
bool StartsWith(const String &other) const
Definition object.h:10277
static const char * ToCString(Thread *thread, StringPtr ptr)
Definition object.cc:24205
static constexpr intptr_t kSuspendStateVarIndex
Definition object.h:12591
static const String & This()
Definition symbols.h:691
static StringPtr New(Thread *thread, const char *cstr)
Definition symbols.h:722
Zone * zone() const
static Thread * Current()
Definition thread.h:361
DART_WARN_UNUSED_RESULT ErrorPtr StealStickyError()
Definition thread.cc:243
Isolate * isolate() const
Definition thread.h:533
bool IsInStoppedMutatorsScope() const
Definition thread.h:729
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:7352
void SetTypeAt(intptr_t index, const AbstractType &value) const
Definition object.cc:7381
intptr_t Length() const
Definition object.cc:6619
StringPtr NameAt(intptr_t index) const
Definition object.cc:6629
AbstractTypePtr DefaultAt(intptr_t index) const
Definition object.cc:6669
AbstractTypePtr BoundAt(intptr_t index) const
Definition object.cc:6648
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
glong glong end
FlKeyEvent * event
static const uint8_t buffer[]
const uint8_t uint32_t uint32_t GError ** error
uint8_t value
#define DECLARE_FLAG(type, name)
Definition flags.h:14
#define DEFINE_FLAG(type, name, default_value, comment)
Definition flags.h:16
Dart_NativeFunction function
Definition fuchsia.cc:51
#define RELOAD_OPERATION_SCOPE(thread_expr)
size_t length
Win32Message message
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
void DeoptimizeFunctionsOnStack()
static TokenPosition FindExactTokenPosition(const Script &script, TokenPosition start_of_line, intptr_t column_number)
Definition debugger.cc:3736
static DART_FORCE_INLINE intptr_t LocalVarIndex(intptr_t fp_offset, intptr_t var_index)
static bool IsSyntheticVariableName(const String &var_name)
Definition debugger.cc:1063
static bool IsImplicitFunction(const Function &func)
Definition debugger.cc:393
DART_EXPORT bool IsNull(Dart_Handle object)
const char *const class_name
static DART_FORCE_INLINE uword ParamAddress(uword fp, intptr_t reverse_index)
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 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
static bool CanRewindFrame(intptr_t frame_index, const char **error)
Definition debugger.cc:3192
const intptr_t cid
static bool IsAtAsyncJump(ActivationFrame *top_frame)
Definition debugger.cc:3502
FrameLayout runtime_frame_layout
static DART_FORCE_INLINE ObjectPtr GetVariableValue(uword addr)
Definition debugger.cc:833
static uword LookupRewindPc(const Code &code, uword return_address)
Definition debugger.cc:3234
static DART_FORCE_INLINE uword LocalVarAddress(uword fp, intptr_t index)
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)
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:2891
static constexpr intptr_t kInvalidTryIndex
static intptr_t FindNextRewindFrameIndex(DebuggerStackTrace *stack, intptr_t frame_index)
Definition debugger.cc:3180
static void UpdateBestFit(Function *best_fit, const Function &func)
Definition debugger.cc:2354
Dart_ExceptionPauseInfo
Definition debugger.h:502
@ kPauseOnUnhandledExceptions
Definition debugger.h:504
@ kNoPauseOnExceptions
Definition debugger.h:503
@ kPauseOnAllExceptions
Definition debugger.h:505
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
#define Px
Definition globals.h:410
#define Pd
Definition globals.h:408
intptr_t FrameSlotForVariableIndex(intptr_t index) const
#define TIMELINE_DURATION(thread, stream, name)
Definition timeline.h:39