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

source code of flutter_engine/third_party/dart/runtime/vm/debugger.cc