Reapply "Per-closure breakpoints; restructure breakpoint implementation to keep a list of conditions."

Fix copying breakpoints over to a new location when a latent location is resolved.

Fix bad assumption that BreakpointLocationAtLine always returns non-null result.

Review URL: https://codereview.chromium.org//1149983003
This commit is contained in:
Ryan Macnak 2015-05-27 09:59:33 -07:00
parent dfee2bf45d
commit 74dfae69e3
11 changed files with 821 additions and 302 deletions

View file

@ -1206,6 +1206,11 @@ class Isolate extends ServiceObjectOwner with Coverage {
{ 'functionId': function.id }); { 'functionId': function.id });
} }
Future<ServiceObject> addBreakOnActivation(Instance closure) {
return invokeRpc('_addBreakpointAtActivation',
{ 'objectId': closure.id });
}
Future removeBreakpoint(Breakpoint bpt) { Future removeBreakpoint(Breakpoint bpt) {
return invokeRpc('removeBreakpoint', return invokeRpc('removeBreakpoint',
{ 'breakpointId': bpt.id }); { 'breakpointId': bpt.id });

View file

@ -0,0 +1,191 @@
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// VMOptions=--compile-all --error_on_bad_type --error_on_bad_override
import 'package:observatory/service_io.dart';
import 'package:unittest/unittest.dart';
import 'test_helper.dart';
import 'dart:async';
genRepeater(value) {
return () => print(value);
}
genRepeaterNamed(value) {
return ({x, y}) => print(value);
}
var r1;
var r2;
var r3;
var r1_named;
var r2_named;
var r3_named;
void testeeSetup() {
// These closures have the same function.
r1 = genRepeater('r1');
r2 = genRepeater('r2');
r3 = genRepeater('r3');
// These closures have the same function.
r1_named = genRepeaterNamed('r1_named');
r2_named = genRepeaterNamed('r2_named');
r3_named = genRepeaterNamed('r3_named');
}
void testeeDo() {
r1();
r2();
r3();
}
void testeeDoNamed() {
r1_named(y: 'Not a closure', x: 'Not a closure');
r2_named(y: 'Not a closure', x: 'Not a closure');
r3_named(y: 'Not a closure', x: 'Not a closure');
}
var tests = [
(Isolate isolate) async {
var rootLib = await isolate.rootLibrary.load();
var breaksHit = 0;
var subscription;
subscription = isolate.vm.events.stream.listen((ServiceEvent event) {
if (event.eventType == ServiceEvent.kPauseBreakpoint) {
print("Hit breakpoint ${event.breakpoint}");
breaksHit++;
isolate.resume();
}
});
valueOfField(String name) {
return rootLib.variables.singleWhere((v) => v.name == name).value;
}
var r1Ref = valueOfField('r1');
var r2Ref = valueOfField('r2');
var r3Ref = valueOfField('r3');
var bpt1 = await isolate.addBreakOnActivation(r1Ref);
print("Added breakpoint $bpt1");
expect(bpt1 is Breakpoint, isTrue);
expect(breaksHit, equals(0));
print("testeeDo()");
var res = await rootLib.evaluate("testeeDo()");
expect(res is Instance, isTrue); // Not error.
expect(breaksHit, equals(1));
await isolate.removeBreakpoint(bpt1);
print("Removed breakpoint $bpt1");
print("testeeDo()");
res = await rootLib.evaluate("testeeDo()");
expect(res is Instance, isTrue); // Not error.
expect(breaksHit, equals(1));
await subscription.cancel();
},
(Isolate isolate) async {
var rootLib = await isolate.rootLibrary.load();
var breaksHit = 0;
var subscription;
subscription = isolate.vm.events.stream.listen((ServiceEvent event) {
if (event.eventType == ServiceEvent.kPauseBreakpoint) {
print("Hit breakpoint ${event.breakpoint}");
breaksHit++;
isolate.resume();
}
});
valueOfField(String name) {
return rootLib.variables.singleWhere((v) => v.name == name).value;
}
var r1Ref = valueOfField('r1_named');
var r2Ref = valueOfField('r2_named');
var r3Ref = valueOfField('r3_named');
var bpt1 = await isolate.addBreakOnActivation(r1Ref);
print("Added breakpoint $bpt1");
expect(bpt1 is Breakpoint, isTrue);
expect(breaksHit, equals(0));
print("testeeDoNamed()");
var res = await rootLib.evaluate("testeeDoNamed()");
expect(res is Instance, isTrue); // Not error.
expect(breaksHit, equals(1));
await isolate.removeBreakpoint(bpt1);
print("Removed breakpoint $bpt1");
print("testeeDoNamed()");
res = await rootLib.evaluate("testeeDoNamed()");
expect(res is Instance, isTrue); // Not error.
expect(breaksHit, equals(1));
await subscription.cancel();
},
(Isolate isolate) async {
var rootLib = await isolate.rootLibrary.load();
var breaksHit = 0;
var subscription;
subscription = isolate.vm.events.stream.listen((ServiceEvent event) {
if (event.eventType == ServiceEvent.kPauseBreakpoint) {
print("Hit breakpoint ${event.breakpoint}");
breaksHit++;
isolate.resume();
}
});
valueOfField(String name) {
return rootLib.variables.singleWhere((v) => v.name == name).value;
}
var r1Ref = valueOfField('r1');
var r2Ref = valueOfField('r2');
var r3Ref = valueOfField('r3');
var bpt1 = await isolate.addBreakOnActivation(r1Ref);
print("Added breakpoint $bpt1");
expect(bpt1 is Breakpoint, isTrue);
expect(breaksHit, equals(0));
print("testeeDo()");
var res = await rootLib.evaluate("testeeDo()");
expect(res is Instance, isTrue); // Not error.
expect(breaksHit, equals(1));
var bpt2 = await isolate.addBreakOnActivation(r2Ref);
print("Added breakpoint $bpt2");
expect(bpt2 is Breakpoint, isTrue);
expect(breaksHit, equals(1));
print("testeeDo()");
res = await rootLib.evaluate("testeeDo()");
expect(res is Instance, isTrue); // Not error.
expect(breaksHit, equals(3));
await isolate.removeBreakpoint(bpt1);
print("Removed breakpoint $bpt1");
print("testeeDo()");
res = await rootLib.evaluate("testeeDo()");
expect(res is Instance, isTrue); // Not error.
expect(breaksHit, equals(4));
await isolate.removeBreakpoint(bpt2);
print("Removed breakpoint $bpt2");
print("testeeDo()");
res = await rootLib.evaluate("testeeDo()");
expect(res is Instance, isTrue); // Not error.
expect(breaksHit, equals(4));
await subscription.cancel();
},
];
main(args) => runIsolateTests(args, tests, testeeBefore: testeeSetup);

View file

@ -108,6 +108,9 @@ void runIsolateTests(List<String> mainArgs,
} else { } else {
var process = new _TestLauncher(); var process = new _TestLauncher();
process.launch(pause_on_exit).then((port) { process.launch(pause_on_exit).then((port) {
if (mainArgs.contains("--gdb")) {
port = 8181;
}
String addr = 'ws://localhost:$port/ws'; String addr = 'ws://localhost:$port/ws';
var testIndex = 1; var testIndex = 1;
var totalTests = tests.length; var totalTests = tests.length;
@ -192,6 +195,9 @@ Future runVMTests(List<String> mainArgs,
} else { } else {
var process = new _TestLauncher(); var process = new _TestLauncher();
process.launch(pause_on_exit).then((port) async { process.launch(pause_on_exit).then((port) async {
if (mainArgs.contains("--gdb")) {
port = 8181;
}
String addr = 'ws://localhost:$port/ws'; String addr = 'ws://localhost:$port/ws';
var testIndex = 1; var testIndex = 1;
var totalTests = tests.length; var totalTests = tests.length;

View file

@ -63,58 +63,54 @@ class RemoteObjectCache : public ZoneAllocated {
// Create an unresolved breakpoint in given token range and script. // Create an unresolved breakpoint in given token range and script.
SourceBreakpoint::SourceBreakpoint(intptr_t id, BreakpointLocation::BreakpointLocation(const Script& script,
const Script& script,
intptr_t token_pos, intptr_t token_pos,
intptr_t end_token_pos) intptr_t end_token_pos)
: id_(id), : script_(script.raw()),
script_(script.raw()),
url_(script.url()), url_(script.url()),
token_pos_(token_pos), token_pos_(token_pos),
end_token_pos_(end_token_pos), end_token_pos_(end_token_pos),
is_resolved_(false), is_resolved_(false),
is_enabled_(false),
is_one_shot_(false),
next_(NULL), next_(NULL),
conditions_(NULL),
function_(Function::null()), function_(Function::null()),
line_number_(-1) { line_number_(-1) {
ASSERT(id_ > 0);
ASSERT(!script.IsNull()); ASSERT(!script.IsNull());
ASSERT(token_pos_ >= 0); ASSERT(token_pos_ >= 0);
} }
// Create a latent breakpoint at given url and line number. // Create a latent breakpoint at given url and line number.
SourceBreakpoint::SourceBreakpoint(intptr_t id, BreakpointLocation::BreakpointLocation(const String& url,
const String& url,
intptr_t line_number) intptr_t line_number)
: id_(id), : script_(Script::null()),
script_(Script::null()),
url_(url.raw()), url_(url.raw()),
token_pos_(-1), token_pos_(-1),
end_token_pos_(-1), end_token_pos_(-1),
is_resolved_(false), is_resolved_(false),
is_enabled_(false),
is_one_shot_(false),
next_(NULL), next_(NULL),
conditions_(NULL),
function_(Function::null()), function_(Function::null()),
line_number_(line_number) { line_number_(line_number) {
ASSERT(id >= 0);
ASSERT(line_number_ >= 0); ASSERT(line_number_ >= 0);
} }
void SourceBreakpoint::Enable() {
is_enabled_ = true; BreakpointLocation::~BreakpointLocation() {
Isolate::Current()->debugger()->SyncBreakpoint(this); Breakpoint* bpt = breakpoints();
while (bpt != NULL) {
Breakpoint* temp = bpt;
bpt = bpt->next();
delete temp;
}
} }
void SourceBreakpoint::Disable() { bool BreakpointLocation::AnyEnabled() const {
is_enabled_ = false; return breakpoints() != NULL;
Isolate::Current()->debugger()->SyncBreakpoint(this);
} }
void SourceBreakpoint::SetResolved(const Function& func, intptr_t token_pos) { void BreakpointLocation::SetResolved(const Function& func, intptr_t token_pos) {
ASSERT(!IsLatent()); ASSERT(!IsLatent());
ASSERT(func.script() == script_); ASSERT(func.script() == script_);
ASSERT((func.token_pos() <= token_pos) && ASSERT((func.token_pos() <= token_pos) &&
@ -131,7 +127,7 @@ void SourceBreakpoint::SetResolved(const Function& func, intptr_t token_pos) {
// TODO(hausner): Get rid of library parameter. A source breakpoint location // TODO(hausner): Get rid of library parameter. A source breakpoint location
// does not imply a library, since the same source code can be included // does not imply a library, since the same source code can be included
// in more than one library, e.g. the text location of mixin functions. // in more than one library, e.g. the text location of mixin functions.
void SourceBreakpoint::GetCodeLocation(Library* lib, void BreakpointLocation::GetCodeLocation(Library* lib,
Script* script, Script* script,
intptr_t* pos) { intptr_t* pos) {
if (IsLatent()) { if (IsLatent()) {
@ -153,7 +149,7 @@ void SourceBreakpoint::GetCodeLocation(Library* lib,
} }
intptr_t SourceBreakpoint::LineNumber() { intptr_t BreakpointLocation::LineNumber() {
// Latent breakpoints must have a requested line number >= 0. // Latent breakpoints must have a requested line number >= 0.
ASSERT(!IsLatent() || line_number_ >= 0); ASSERT(!IsLatent() || line_number_ >= 0);
// Compute line number lazily since it causes scanning of the script. // Compute line number lazily since it causes scanning of the script.
@ -165,14 +161,31 @@ intptr_t SourceBreakpoint::LineNumber() {
} }
void SourceBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { void Breakpoint::set_bpt_location(BreakpointLocation* new_bpt_location) {
visitor->VisitPointer(reinterpret_cast<RawObject**>(&script_)); ASSERT(bpt_location_->IsLatent()); // Only reason to move.
visitor->VisitPointer(reinterpret_cast<RawObject**>(&url_)); bpt_location_ = new_bpt_location;
visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_));
} }
void SourceBreakpoint::PrintJSON(JSONStream* stream) { void Breakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
visitor->VisitPointer(reinterpret_cast<RawObject**>(&closure_));
}
void BreakpointLocation::VisitObjectPointers(ObjectPointerVisitor* visitor) {
visitor->VisitPointer(reinterpret_cast<RawObject**>(&script_));
visitor->VisitPointer(reinterpret_cast<RawObject**>(&url_));
visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_));
Breakpoint* bpt = conditions_;
while (bpt != NULL) {
bpt -> VisitObjectPointers(visitor);
bpt = bpt->next();
}
}
void Breakpoint::PrintJSON(JSONStream* stream) {
Isolate* isolate = Isolate::Current(); Isolate* isolate = Isolate::Current();
JSONObject jsobj(stream); JSONObject jsobj(stream);
@ -180,12 +193,12 @@ void SourceBreakpoint::PrintJSON(JSONStream* stream) {
jsobj.AddFixedServiceId("breakpoints/%" Pd "", id()); jsobj.AddFixedServiceId("breakpoints/%" Pd "", id());
jsobj.AddProperty("breakpointNumber", id()); jsobj.AddProperty("breakpointNumber", id());
jsobj.AddProperty("resolved", IsResolved()); jsobj.AddProperty("resolved", bpt_location_->IsResolved());
Library& library = Library::Handle(isolate); Library& library = Library::Handle(isolate);
Script& script = Script::Handle(isolate); Script& script = Script::Handle(isolate);
intptr_t token_pos; intptr_t token_pos;
GetCodeLocation(&library, &script, &token_pos); bpt_location_->GetCodeLocation(&library, &script, &token_pos);
{ {
JSONObject location(&jsobj, "location"); JSONObject location(&jsobj, "location");
location.AddProperty("type", "Location"); location.AddProperty("type", "Location");
@ -294,7 +307,7 @@ void Debugger::SignalIsolateInterrupted() {
// The vm service handles breakpoint notifications in a different way // The vm service handles breakpoint notifications in a different way
// than the regular debugger breakpoint notifications. // than the regular debugger breakpoint notifications.
static void SendServiceBreakpointEvent(ServiceEvent::EventType type, static void SendServiceBreakpointEvent(ServiceEvent::EventType type,
SourceBreakpoint* bpt) { Breakpoint* bpt) {
if (Service::NeedsEvents()) { if (Service::NeedsEvents()) {
ServiceEvent service_event(Isolate::Current(), type); ServiceEvent service_event(Isolate::Current(), type);
service_event.set_breakpoint(bpt); service_event.set_breakpoint(bpt);
@ -303,6 +316,65 @@ static void SendServiceBreakpointEvent(ServiceEvent::EventType type,
} }
void BreakpointLocation::AddBreakpoint(Breakpoint* bpt, Debugger* dbg) {
bpt->set_next(breakpoints());
set_breakpoints(bpt);
dbg->SyncBreakpointLocation(this);
if (IsResolved()) {
dbg->SignalBpResolved(bpt);
}
SendServiceBreakpointEvent(ServiceEvent::kBreakpointAdded, bpt);
}
Breakpoint* BreakpointLocation::AddRepeated(Debugger* dbg) {
Breakpoint* bpt = breakpoints();
while (bpt != NULL) {
if (bpt->IsRepeated()) break;
bpt = bpt->next();
}
if (bpt == NULL) {
bpt = new Breakpoint(dbg->nextId(), this);
bpt->SetIsRepeated();
AddBreakpoint(bpt, dbg);
}
return bpt;
}
Breakpoint* BreakpointLocation::AddSingleShot(Debugger* dbg) {
Breakpoint* bpt = breakpoints();
while (bpt != NULL) {
if (bpt->IsSingleShot()) break;
bpt = bpt->next();
}
if (bpt == NULL) {
bpt = new Breakpoint(dbg->nextId(), this);
bpt->SetIsSingleShot();
AddBreakpoint(bpt, dbg);
}
return bpt;
}
Breakpoint* BreakpointLocation::AddPerClosure(Debugger* dbg,
const Instance& closure) {
Breakpoint* bpt = breakpoints();
while (bpt != NULL) {
if (bpt->IsPerClosure() && bpt->closure() == closure.raw()) break;
bpt = bpt->next();
}
if (bpt == NULL) {
bpt = new Breakpoint(dbg->nextId(), this);
bpt->SetIsPerClosure(closure);
AddBreakpoint(bpt, dbg);
}
return bpt;
}
const char* Debugger::QualifiedFunctionName(const Function& func) { const char* Debugger::QualifiedFunctionName(const Function& func) {
const String& func_name = String::Handle(func.name()); const String& func_name = String::Handle(func.name());
Class& func_class = Class::Handle(func.Owner()); Class& func_class = Class::Handle(func.Owner());
@ -343,7 +415,7 @@ bool Debugger::HasBreakpoint(const Function& func) {
// range of the function. This may be a false positive: the breakpoint // range of the function. This may be a false positive: the breakpoint
// might be inside a local closure. // might be inside a local closure.
Script& script = Script::Handle(isolate_); Script& script = Script::Handle(isolate_);
SourceBreakpoint* sbpt = src_breakpoints_; BreakpointLocation* sbpt = breakpoint_locations_;
while (sbpt != NULL) { while (sbpt != NULL) {
script = sbpt->script(); script = sbpt->script();
if (FunctionContains(func, script, sbpt->token_pos())) { if (FunctionContains(func, script, sbpt->token_pos())) {
@ -377,9 +449,13 @@ bool Debugger::HasBreakpoint(const Code& code) {
void Debugger::PrintBreakpointsToJSONArray(JSONArray* jsarr) const { void Debugger::PrintBreakpointsToJSONArray(JSONArray* jsarr) const {
SourceBreakpoint* sbpt = src_breakpoints_; BreakpointLocation* sbpt = breakpoint_locations_;
while (sbpt != NULL) { while (sbpt != NULL) {
jsarr->AddValue(sbpt); Breakpoint* bpt = sbpt->breakpoints();
while (bpt != NULL) {
jsarr->AddValue(bpt);
bpt = bpt->next();
}
sbpt = sbpt->next_; sbpt = sbpt->next_;
} }
} }
@ -676,6 +752,34 @@ intptr_t ActivationFrame::NumLocalVariables() {
} }
RawObject* ActivationFrame::GetParameter(intptr_t index) {
intptr_t num_parameters = function().num_fixed_parameters();
ASSERT(0 <= index && index < num_parameters);
intptr_t reverse_index = num_parameters - index;
if (function().NumOptionalParameters() > 0) {
// If the function has optional parameters, the first positional parameter
// can be in a number of places in the caller's frame depending on how many
// were actually supplied at the call site, but they are copied to a fixed
// place in the callee's frame.
uword var_address = fp() + ((kFirstLocalSlotFromFp - index) * kWordSize);
return reinterpret_cast<RawObject*>(
*reinterpret_cast<uword*>(var_address));
} else {
uword var_address = fp() + (kParamEndSlotFromFp * kWordSize)
+ (reverse_index * kWordSize);
return reinterpret_cast<RawObject*>(
*reinterpret_cast<uword*>(var_address));
}
}
RawObject* ActivationFrame::GetClosure() {
ASSERT(function().IsClosureFunction());
return GetParameter(0);
}
RawObject* ActivationFrame::GetLocalVar(intptr_t slot_index) { RawObject* ActivationFrame::GetLocalVar(intptr_t slot_index) {
if (deopt_frame_.IsNull()) { if (deopt_frame_.IsNull()) {
uword var_address = fp() + slot_index * kWordSize; uword var_address = fp() + slot_index * kWordSize;
@ -946,7 +1050,7 @@ CodeBreakpoint::CodeBreakpoint(const Code& code,
pc_(pc), pc_(pc),
line_number_(-1), line_number_(-1),
is_enabled_(false), is_enabled_(false),
src_bpt_(NULL), bpt_location_(NULL),
next_(NULL), next_(NULL),
breakpoint_kind_(kind), breakpoint_kind_(kind),
saved_value_(0) { saved_value_(0) {
@ -964,7 +1068,7 @@ CodeBreakpoint::~CodeBreakpoint() {
#ifdef DEBUG #ifdef DEBUG
code_ = Code::null(); code_ = Code::null();
pc_ = 0ul; pc_ = 0ul;
src_bpt_ = NULL; bpt_location_ = NULL;
next_ = NULL; next_ = NULL;
breakpoint_kind_ = RawPcDescriptors::kOther; breakpoint_kind_ = RawPcDescriptors::kOther;
#endif #endif
@ -1043,8 +1147,8 @@ Debugger::Debugger()
isolate_id_(ILLEGAL_ISOLATE_ID), isolate_id_(ILLEGAL_ISOLATE_ID),
initialized_(false), initialized_(false),
next_id_(1), next_id_(1),
latent_breakpoints_(NULL), latent_locations_(NULL),
src_breakpoints_(NULL), breakpoint_locations_(NULL),
code_breakpoints_(NULL), code_breakpoints_(NULL),
resume_action_(kContinue), resume_action_(kContinue),
ignore_breakpoints_(false), ignore_breakpoints_(false),
@ -1059,8 +1163,8 @@ Debugger::Debugger()
Debugger::~Debugger() { Debugger::~Debugger() {
isolate_id_ = ILLEGAL_ISOLATE_ID; isolate_id_ = ILLEGAL_ISOLATE_ID;
ASSERT(!IsPaused()); ASSERT(!IsPaused());
ASSERT(latent_breakpoints_ == NULL); ASSERT(latent_locations_ == NULL);
ASSERT(src_breakpoints_ == NULL); ASSERT(breakpoint_locations_ == NULL);
ASSERT(code_breakpoints_ == NULL); ASSERT(code_breakpoints_ == NULL);
ASSERT(stack_trace_ == NULL); ASSERT(stack_trace_ == NULL);
ASSERT(obj_cache_ == NULL); ASSERT(obj_cache_ == NULL);
@ -1068,14 +1172,14 @@ Debugger::~Debugger() {
void Debugger::Shutdown() { void Debugger::Shutdown() {
while (src_breakpoints_ != NULL) { while (breakpoint_locations_ != NULL) {
SourceBreakpoint* bpt = src_breakpoints_; BreakpointLocation* bpt = breakpoint_locations_;
src_breakpoints_ = src_breakpoints_->next(); breakpoint_locations_ = breakpoint_locations_->next();
delete bpt; delete bpt;
} }
while (latent_breakpoints_ != NULL) { while (latent_locations_ != NULL) {
SourceBreakpoint* bpt = latent_breakpoints_; BreakpointLocation* bpt = latent_locations_;
latent_breakpoints_ = latent_breakpoints_->next(); latent_locations_ = latent_locations_->next();
delete bpt; delete bpt;
} }
while (code_breakpoints_ != NULL) { while (code_breakpoints_ != NULL) {
@ -1193,8 +1297,8 @@ void Debugger::DeoptimizeWorld() {
} }
void Debugger::SignalBpResolved(SourceBreakpoint* bpt) { void Debugger::SignalBpResolved(Breakpoint* bpt) {
if (HasEventHandler() && !bpt->IsOneShot()) { if (HasEventHandler() && !bpt->IsSingleShot()) {
DebuggerEvent event(isolate_, DebuggerEvent::kBreakpointResolved); DebuggerEvent event(isolate_, DebuggerEvent::kBreakpointResolved);
event.set_breakpoint(bpt); event.set_breakpoint(bpt);
InvokeEventHandler(&event); InvokeEventHandler(&event);
@ -1512,9 +1616,9 @@ intptr_t Debugger::ResolveBreakpointPos(const Function& func,
void Debugger::MakeCodeBreakpointAt(const Function& func, void Debugger::MakeCodeBreakpointAt(const Function& func,
SourceBreakpoint* bpt) { BreakpointLocation* loc) {
ASSERT(bpt->token_pos_ != Scanner::kNoSourcePos); ASSERT(loc->token_pos_ != Scanner::kNoSourcePos);
ASSERT((bpt != NULL) && bpt->IsResolved()); ASSERT((loc != NULL) && loc->IsResolved());
ASSERT(!func.HasOptimizedCode()); ASSERT(!func.HasOptimizedCode());
Code& code = Code::Handle(func.unoptimized_code()); Code& code = Code::Handle(func.unoptimized_code());
ASSERT(!code.IsNull()); ASSERT(!code.IsNull());
@ -1525,7 +1629,7 @@ void Debugger::MakeCodeBreakpointAt(const Function& func,
// that maps to the token position of the source breakpoint. // that maps to the token position of the source breakpoint.
PcDescriptors::Iterator iter(desc, kSafepointKind); PcDescriptors::Iterator iter(desc, kSafepointKind);
while (iter.MoveNext()) { while (iter.MoveNext()) {
if (iter.TokenPos() == bpt->token_pos_) { if (iter.TokenPos() == loc->token_pos_) {
if (iter.PcOffset() < lowest_pc_offset) { if (iter.PcOffset() < lowest_pc_offset) {
lowest_pc_offset = iter.PcOffset(); lowest_pc_offset = iter.PcOffset();
lowest_kind = iter.Kind(); lowest_kind = iter.Kind();
@ -1539,12 +1643,12 @@ void Debugger::MakeCodeBreakpointAt(const Function& func,
CodeBreakpoint* code_bpt = GetCodeBreakpoint(lowest_pc); CodeBreakpoint* code_bpt = GetCodeBreakpoint(lowest_pc);
if (code_bpt == NULL) { if (code_bpt == NULL) {
// No code breakpoint for this code exists; create one. // No code breakpoint for this code exists; create one.
code_bpt = new CodeBreakpoint(code, bpt->token_pos_, code_bpt = new CodeBreakpoint(code, loc->token_pos_,
lowest_pc, lowest_kind); lowest_pc, lowest_kind);
RegisterCodeBreakpoint(code_bpt); RegisterCodeBreakpoint(code_bpt);
} }
code_bpt->set_src_bpt(bpt); code_bpt->set_bpt_location(loc);
if (bpt->IsEnabled()) { if (loc->AnyEnabled()) {
code_bpt->Enable(); code_bpt->Enable();
} }
} }
@ -1696,7 +1800,7 @@ RawFunction* Debugger::FindBestFit(const Script& script,
} }
SourceBreakpoint* Debugger::SetBreakpoint(const Script& script, BreakpointLocation* Debugger::SetBreakpoint(const Script& script,
intptr_t token_pos, intptr_t token_pos,
intptr_t last_token_pos) { intptr_t last_token_pos) {
Function& func = Function::Handle(isolate_); Function& func = Function::Handle(isolate_);
@ -1724,14 +1828,14 @@ SourceBreakpoint* Debugger::SetBreakpoint(const Script& script,
intptr_t breakpoint_pos = intptr_t breakpoint_pos =
ResolveBreakpointPos(func, token_pos, last_token_pos); ResolveBreakpointPos(func, token_pos, last_token_pos);
if (breakpoint_pos >= 0) { if (breakpoint_pos >= 0) {
SourceBreakpoint* bpt = GetSourceBreakpoint(script, breakpoint_pos); BreakpointLocation* bpt = GetBreakpointLocation(script, breakpoint_pos);
if (bpt != NULL) { if (bpt != NULL) {
// A source breakpoint for this location already exists. // A source breakpoint for this location already exists.
return bpt; return bpt;
} }
bpt = new SourceBreakpoint(nextId(), script, token_pos, last_token_pos); bpt = new BreakpointLocation(script, token_pos, last_token_pos);
bpt->SetResolved(func, breakpoint_pos); bpt->SetResolved(func, breakpoint_pos);
RegisterSourceBreakpoint(bpt); RegisterBreakpointLocation(bpt);
// Create code breakpoints for all compiled functions we found. // Create code breakpoints for all compiled functions we found.
const intptr_t num_functions = functions.Length(); const intptr_t num_functions = functions.Length();
@ -1740,7 +1844,6 @@ SourceBreakpoint* Debugger::SetBreakpoint(const Script& script,
ASSERT(func.HasCode()); ASSERT(func.HasCode());
MakeCodeBreakpointAt(func, bpt); MakeCodeBreakpointAt(func, bpt);
} }
bpt->Enable();
if (FLAG_verbose_debug) { if (FLAG_verbose_debug) {
intptr_t line_number; intptr_t line_number;
script.GetTokenLocation(breakpoint_pos, &line_number, NULL); script.GetTokenLocation(breakpoint_pos, &line_number, NULL);
@ -1749,8 +1852,6 @@ SourceBreakpoint* Debugger::SetBreakpoint(const Script& script,
func.ToFullyQualifiedCString(), func.ToFullyQualifiedCString(),
line_number); line_number);
} }
SignalBpResolved(bpt);
SendServiceBreakpointEvent(ServiceEvent::kBreakpointAdded, bpt);
return bpt; return bpt;
} }
} }
@ -1764,24 +1865,24 @@ SourceBreakpoint* Debugger::SetBreakpoint(const Script& script,
func.ToFullyQualifiedCString(), func.ToFullyQualifiedCString(),
line_number); line_number);
} }
SourceBreakpoint* bpt = GetSourceBreakpoint(script, token_pos); BreakpointLocation* bpt = GetBreakpointLocation(script, token_pos);
if (bpt == NULL) { if (bpt == NULL) {
bpt = new SourceBreakpoint(nextId(), script, token_pos, last_token_pos); bpt = new BreakpointLocation(script, token_pos, last_token_pos);
RegisterSourceBreakpoint(bpt); RegisterBreakpointLocation(bpt);
SendServiceBreakpointEvent(ServiceEvent::kBreakpointAdded, bpt);
} }
bpt->Enable();
return bpt; return bpt;
} }
// Synchronize the enabled/disabled state of all code breakpoints // Synchronize the enabled/disabled state of all code breakpoints
// associated with the source breakpoint bpt. // associated with the breakpoint location loc.
void Debugger::SyncBreakpoint(SourceBreakpoint* bpt) { void Debugger::SyncBreakpointLocation(BreakpointLocation* loc) {
bool any_enabled = loc->AnyEnabled();
CodeBreakpoint* cbpt = code_breakpoints_; CodeBreakpoint* cbpt = code_breakpoints_;
while (cbpt != NULL) { while (cbpt != NULL) {
if (bpt == cbpt->src_bpt()) { if (loc == cbpt->bpt_location()) {
if (bpt->IsEnabled()) { if (any_enabled) {
cbpt->Enable(); cbpt->Enable();
} else { } else {
cbpt->Disable(); cbpt->Disable();
@ -1795,10 +1896,7 @@ void Debugger::SyncBreakpoint(SourceBreakpoint* bpt) {
RawError* Debugger::OneTimeBreakAtEntry(const Function& target_function) { RawError* Debugger::OneTimeBreakAtEntry(const Function& target_function) {
LongJumpScope jump; LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) { if (setjmp(*jump.Set()) == 0) {
SourceBreakpoint* bpt = SetBreakpointAtEntry(target_function); SetBreakpointAtEntry(target_function, true);
if (bpt != NULL) {
bpt->SetIsOneShot();
}
return Error::null(); return Error::null();
} else { } else {
return isolate_->object_store()->sticky_error(); return isolate_->object_store()->sticky_error();
@ -1806,20 +1904,50 @@ RawError* Debugger::OneTimeBreakAtEntry(const Function& target_function) {
} }
SourceBreakpoint* Debugger::SetBreakpointAtEntry( Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function,
const Function& target_function) { bool single_shot) {
ASSERT(!target_function.IsNull()); ASSERT(!target_function.IsNull());
if (!target_function.is_debuggable()) { if (!target_function.is_debuggable()) {
return NULL; return NULL;
} }
const Script& script = Script::Handle(target_function.script()); const Script& script = Script::Handle(target_function.script());
return SetBreakpoint(script, BreakpointLocation* bpt_location =
SetBreakpoint(script,
target_function.token_pos(), target_function.token_pos(),
target_function.end_token_pos()); target_function.end_token_pos());
if (single_shot) {
return bpt_location->AddSingleShot(this);
} else {
return bpt_location->AddRepeated(this);
}
} }
SourceBreakpoint* Debugger::SetBreakpointAtLine(const String& script_url, Breakpoint* Debugger::SetBreakpointAtActivation(
const Instance& closure) {
if (!closure.IsClosure()) {
return NULL;
}
const Function& func = Function::Handle(Closure::function(closure));
const Script& script = Script::Handle(func.script());
BreakpointLocation* bpt = SetBreakpoint(script,
func.token_pos(),
func.end_token_pos());
return bpt->AddPerClosure(this, closure);
}
Breakpoint* Debugger::SetBreakpointAtLine(const String& script_url,
intptr_t line_number) {
BreakpointLocation* loc = BreakpointLocationAtLine(script_url, line_number);
if (loc != NULL) {
return loc->AddRepeated(this);
}
return NULL;
}
BreakpointLocation* Debugger::BreakpointLocationAtLine(const String& script_url,
intptr_t line_number) { intptr_t line_number) {
Library& lib = Library::Handle(isolate_); Library& lib = Library::Handle(isolate_);
Script& script = Script::Handle(isolate_); Script& script = Script::Handle(isolate_);
@ -1837,7 +1965,8 @@ SourceBreakpoint* Debugger::SetBreakpointAtLine(const String& script_url,
if (scripts.Length() == 0) { if (scripts.Length() == 0) {
// No script found with given url. Create a latent breakpoint which // No script found with given url. Create a latent breakpoint which
// will be set if the url is loaded later. // will be set if the url is loaded later.
SourceBreakpoint* latent_bpt = GetLatentBreakpoint(script_url, line_number); BreakpointLocation* latent_bpt = GetLatentBreakpoint(script_url,
line_number);
if (FLAG_verbose_debug) { if (FLAG_verbose_debug) {
OS::Print("Set latent breakpoint in url '%s' at line %" Pd "\n", OS::Print("Set latent breakpoint in url '%s' at line %" Pd "\n",
script_url.ToCString(), script_url.ToCString(),
@ -1870,7 +1999,7 @@ SourceBreakpoint* Debugger::SetBreakpointAtLine(const String& script_url,
return NULL; return NULL;
} }
SourceBreakpoint* bpt = NULL; BreakpointLocation* bpt = NULL;
ASSERT(first_token_idx <= last_token_idx); ASSERT(first_token_idx <= last_token_idx);
while ((bpt == NULL) && (first_token_idx <= last_token_idx)) { while ((bpt == NULL) && (first_token_idx <= last_token_idx)) {
bpt = SetBreakpoint(script, first_token_idx, last_token_idx); bpt = SetBreakpoint(script, first_token_idx, last_token_idx);
@ -2084,12 +2213,12 @@ RawArray* Debugger::GetGlobalFields(const Library& lib) {
// static // static
void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) { void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
ASSERT(visitor != NULL); ASSERT(visitor != NULL);
SourceBreakpoint* bpt = src_breakpoints_; BreakpointLocation* bpt = breakpoint_locations_;
while (bpt != NULL) { while (bpt != NULL) {
bpt->VisitObjectPointers(visitor); bpt->VisitObjectPointers(visitor);
bpt = bpt->next(); bpt = bpt->next();
} }
bpt = latent_breakpoints_; bpt = latent_locations_;
while (bpt != NULL) { while (bpt != NULL) {
bpt->VisitObjectPointers(visitor); bpt->VisitObjectPointers(visitor);
bpt = bpt->next(); bpt = bpt->next();
@ -2172,13 +2301,13 @@ bool Debugger::IsDebuggable(const Function& func) {
void Debugger::SignalPausedEvent(ActivationFrame* top_frame, void Debugger::SignalPausedEvent(ActivationFrame* top_frame,
SourceBreakpoint* bpt) { Breakpoint* bpt) {
resume_action_ = kContinue; resume_action_ = kContinue;
stepping_fp_ = 0; stepping_fp_ = 0;
isolate_->set_single_step(false); isolate_->set_single_step(false);
ASSERT(!IsPaused()); ASSERT(!IsPaused());
ASSERT(obj_cache_ == NULL); ASSERT(obj_cache_ == NULL);
if ((bpt != NULL) && bpt->IsOneShot()) { if ((bpt != NULL) && bpt->IsSingleShot()) {
RemoveBreakpoint(bpt->id()); RemoveBreakpoint(bpt->id());
bpt = NULL; bpt = NULL;
} }
@ -2260,25 +2389,73 @@ void Debugger::SignalBpReached() {
ASSERT(stack_trace->Length() > 0); ASSERT(stack_trace->Length() > 0);
ActivationFrame* top_frame = stack_trace->FrameAt(0); ActivationFrame* top_frame = stack_trace->FrameAt(0);
ASSERT(top_frame != NULL); ASSERT(top_frame != NULL);
CodeBreakpoint* bpt = GetCodeBreakpoint(top_frame->pc()); CodeBreakpoint* cbpt = GetCodeBreakpoint(top_frame->pc());
ASSERT(bpt != NULL); ASSERT(cbpt != NULL);
BreakpointLocation* bpt_location = cbpt->bpt_location_;
Breakpoint* bpt_hit = NULL;
// There may be more than one applicable breakpoint at this location, but we
// will report only one as reached. If there is a single-shot breakpoint, we
// favor it; then a closure-specific breakpoint ; then an general breakpoint.
if (bpt_location != NULL) {
Breakpoint* bpt = bpt_location->breakpoints();
while (bpt != NULL) {
if (bpt->IsSingleShot()) {
bpt_hit = bpt;
break;
}
bpt = bpt->next();
}
if (bpt_hit == NULL) {
bpt = bpt_location->breakpoints();
while (bpt != NULL) {
if (bpt->IsPerClosure()) {
Object& closure = Object::Handle(top_frame->GetClosure());
ASSERT(closure.IsInstance());
ASSERT(Instance::Cast(closure).IsClosure());
if (closure.raw() == bpt->closure()) {
bpt_hit = bpt;
break;
}
}
bpt = bpt->next();
}
}
if (bpt_hit == NULL) {
bpt = bpt_location->breakpoints();
while (bpt != NULL) {
if (bpt->IsRepeated()) {
bpt_hit = bpt;
break;
}
bpt = bpt->next();
}
}
}
if (bpt_hit == NULL) {
return;
}
if (FLAG_verbose_debug) { if (FLAG_verbose_debug) {
OS::Print(">>> hit %s breakpoint at %s:%" Pd " " OS::Print(">>> hit %s breakpoint at %s:%" Pd " "
"(token %" Pd ") (address %#" Px ")\n", "(token %" Pd ") (address %#" Px ")\n",
bpt->IsInternal() ? "internal" : "user", cbpt->IsInternal() ? "internal" : "user",
String::Handle(bpt->SourceUrl()).ToCString(), String::Handle(cbpt->SourceUrl()).ToCString(),
bpt->LineNumber(), cbpt->LineNumber(),
bpt->token_pos(), cbpt->token_pos(),
top_frame->pc()); top_frame->pc());
} }
ASSERT(stack_trace_ == NULL); ASSERT(stack_trace_ == NULL);
stack_trace_ = stack_trace; stack_trace_ = stack_trace;
SignalPausedEvent(top_frame, bpt->src_bpt_); SignalPausedEvent(top_frame, bpt_hit);
HandleSteppingRequest(stack_trace_); HandleSteppingRequest(stack_trace_);
stack_trace_ = NULL; stack_trace_ = NULL;
if (bpt->IsInternal()) { if (cbpt->IsInternal()) {
RemoveInternalBreakpoints(); RemoveInternalBreakpoints();
} }
} }
@ -2364,7 +2541,7 @@ RawFunction* Debugger::FindInnermostClosure(const Function& function,
void Debugger::NotifyCompilation(const Function& func) { void Debugger::NotifyCompilation(const Function& func) {
if (src_breakpoints_ == NULL) { if (breakpoint_locations_ == NULL) {
// Return with minimal overhead if there are no breakpoints. // Return with minimal overhead if there are no breakpoints.
return; return;
} }
@ -2377,13 +2554,13 @@ void Debugger::NotifyCompilation(const Function& func) {
// Iterate over all source breakpoints to check whether breakpoints // Iterate over all source breakpoints to check whether breakpoints
// need to be set in the newly compiled function. // need to be set in the newly compiled function.
Script& script = Script::Handle(isolate_); Script& script = Script::Handle(isolate_);
for (SourceBreakpoint* bpt = src_breakpoints_; for (BreakpointLocation* loc = breakpoint_locations_;
bpt != NULL; loc != NULL;
bpt = bpt->next()) { loc = loc->next()) {
script = bpt->script(); script = loc->script();
if (FunctionContains(func, script, bpt->token_pos())) { if (FunctionContains(func, script, loc->token_pos())) {
Function& inner_function = Function::Handle(isolate_); Function& inner_function = Function::Handle(isolate_);
inner_function = FindInnermostClosure(func, bpt->token_pos()); inner_function = FindInnermostClosure(func, loc->token_pos());
if (!inner_function.IsNull()) { if (!inner_function.IsNull()) {
// The local function of a function we just compiled cannot // The local function of a function we just compiled cannot
// be compiled already. // be compiled already.
@ -2402,10 +2579,10 @@ void Debugger::NotifyCompilation(const Function& func) {
// There is no local function within func that contains the // There is no local function within func that contains the
// breakpoint token position. Resolve the breakpoint if necessary // breakpoint token position. Resolve the breakpoint if necessary
// and set the code breakpoints. // and set the code breakpoints.
if (!bpt->IsResolved()) { if (!loc->IsResolved()) {
// Resolve source breakpoint in the newly compiled function. // Resolve source breakpoint in the newly compiled function.
intptr_t bp_pos = intptr_t bp_pos =
ResolveBreakpointPos(func, bpt->token_pos(), bpt->end_token_pos()); ResolveBreakpointPos(func, loc->token_pos(), loc->end_token_pos());
if (bp_pos < 0) { if (bp_pos < 0) {
if (FLAG_verbose_debug) { if (FLAG_verbose_debug) {
OS::Print("Failed resolving breakpoint for function '%s'\n", OS::Print("Failed resolving breakpoint for function '%s'\n",
@ -2413,50 +2590,58 @@ void Debugger::NotifyCompilation(const Function& func) {
} }
continue; continue;
} }
intptr_t requested_pos = bpt->token_pos(); intptr_t requested_pos = loc->token_pos();
intptr_t requested_end_pos = bpt->end_token_pos(); intptr_t requested_end_pos = loc->end_token_pos();
bpt->SetResolved(func, bp_pos); loc->SetResolved(func, bp_pos);
Breakpoint* bpt = loc->breakpoints();
while (bpt != NULL) {
if (FLAG_verbose_debug) { if (FLAG_verbose_debug) {
OS::Print("Resolved BP %" Pd " to pos %" Pd ", line %" Pd ", " OS::Print("Resolved BP %" Pd " to pos %" Pd ", line %" Pd ", "
"function '%s' (requested range %" Pd "-%" Pd ")\n", "function '%s' (requested range %" Pd "-%" Pd ")\n",
bpt->id(), bpt->id(),
bpt->token_pos(), loc->token_pos(),
bpt->LineNumber(), loc->LineNumber(),
func.ToFullyQualifiedCString(), func.ToFullyQualifiedCString(),
requested_pos, requested_pos,
requested_end_pos); requested_end_pos);
} }
SignalBpResolved(bpt); SignalBpResolved(bpt);
SendServiceBreakpointEvent(ServiceEvent::kBreakpointResolved, bpt); SendServiceBreakpointEvent(ServiceEvent::kBreakpointResolved, bpt);
bpt = bpt->next();
} }
ASSERT(bpt->IsResolved()); }
ASSERT(loc->IsResolved());
if (FLAG_verbose_debug) { if (FLAG_verbose_debug) {
Breakpoint* bpt = loc->breakpoints();
while (bpt != NULL) {
OS::Print("Setting breakpoint %" Pd " at line %" Pd " for %s '%s'\n", OS::Print("Setting breakpoint %" Pd " at line %" Pd " for %s '%s'\n",
bpt->id(), bpt->id(),
bpt->LineNumber(), loc->LineNumber(),
func.IsClosureFunction() ? "closure" : "function", func.IsClosureFunction() ? "closure" : "function",
String::Handle(func.name()).ToCString()); String::Handle(func.name()).ToCString());
bpt = bpt->next();
} }
MakeCodeBreakpointAt(func, bpt); }
MakeCodeBreakpointAt(func, loc);
} }
} }
} }
void Debugger::NotifyDoneLoading() { void Debugger::NotifyDoneLoading() {
if (latent_breakpoints_ == NULL) { if (latent_locations_ == NULL) {
// Common, fast path. // Common, fast path.
return; return;
} }
Library& lib = Library::Handle(isolate_); Library& lib = Library::Handle(isolate_);
Script& script = Script::Handle(isolate_); Script& script = Script::Handle(isolate_);
String& url = String::Handle(isolate_); String& url = String::Handle(isolate_);
SourceBreakpoint* bpt = latent_breakpoints_; BreakpointLocation* loc = latent_locations_;
SourceBreakpoint* prev_bpt = NULL; BreakpointLocation* prev_loc = NULL;
const GrowableObjectArray& libs = const GrowableObjectArray& libs =
GrowableObjectArray::Handle(isolate_->object_store()->libraries()); GrowableObjectArray::Handle(isolate_->object_store()->libraries());
while (bpt != NULL) { while (loc != NULL) {
url = bpt->url(); url = loc->url();
bool found_match = false; bool found_match = false;
for (intptr_t i = 0; i < libs.Length(); i++) { for (intptr_t i = 0; i < libs.Length(); i++) {
lib ^= libs.At(i); lib ^= libs.At(i);
@ -2465,16 +2650,16 @@ void Debugger::NotifyDoneLoading() {
// Found a script with matching url for this latent breakpoint. // Found a script with matching url for this latent breakpoint.
// Unlink the latent breakpoint from the list. // Unlink the latent breakpoint from the list.
found_match = true; found_match = true;
SourceBreakpoint* matched_bpt = bpt; BreakpointLocation* matched_loc = loc;
bpt = bpt->next(); loc = loc->next();
if (prev_bpt == NULL) { if (prev_loc == NULL) {
latent_breakpoints_ = bpt; latent_locations_ = loc;
} else { } else {
prev_bpt->set_next(bpt); prev_loc->set_next(loc);
} }
// Now find the token range at the requested line and make a // Now find the token range at the requested line and make a
// new unresolved source breakpoint. // new unresolved source breakpoint.
intptr_t line_number = matched_bpt->LineNumber(); intptr_t line_number = matched_loc->LineNumber();
ASSERT(line_number >= 0); ASSERT(line_number >= 0);
intptr_t first_token_pos, last_token_pos; intptr_t first_token_pos, last_token_pos;
script.TokenRangeAtLine(line_number, &first_token_pos, &last_token_pos); script.TokenRangeAtLine(line_number, &first_token_pos, &last_token_pos);
@ -2482,40 +2667,54 @@ void Debugger::NotifyDoneLoading() {
(last_token_pos < 0)) { (last_token_pos < 0)) {
// Script does not contain the given line number or there are no // Script does not contain the given line number or there are no
// tokens on the line. Drop the breakpoint silently. // tokens on the line. Drop the breakpoint silently.
Breakpoint* bpt = matched_loc->breakpoints();
while (bpt != NULL) {
if (FLAG_verbose_debug) { if (FLAG_verbose_debug) {
OS::Print("No code found at line %" Pd ": " OS::Print("No code found at line %" Pd ": "
"dropping latent breakpoint %" Pd " in '%s'\n", "dropping latent breakpoint %" Pd " in '%s'\n",
line_number, line_number,
matched_bpt->id(), bpt->id(),
url.ToCString()); url.ToCString());
} }
delete matched_bpt; Breakpoint* prev = bpt;
bpt = bpt->next();
delete prev;
}
delete matched_loc;
} else { } else {
// We don't expect to already have a breakpoint for this location. // We don't expect to already have a breakpoint for this location.
// If there is one, assert in debug build but silently drop // If there is one, assert in debug build but silently drop
// the latent breakpoint in release build. // the latent breakpoint in release build.
SourceBreakpoint* existing_bpt = BreakpointLocation* existing_loc =
GetSourceBreakpoint(script, first_token_pos); GetBreakpointLocation(script, first_token_pos);
ASSERT(existing_bpt == NULL); ASSERT(existing_loc == NULL);
if (existing_bpt == NULL) { if (existing_loc == NULL) {
// Create and register a new source breakpoint for the // Create and register a new source breakpoint for the
// latent breakpoint. // latent breakpoint.
SourceBreakpoint* unresolved_bpt = BreakpointLocation* unresolved_loc =
new SourceBreakpoint(matched_bpt->id(), new BreakpointLocation(script,
script,
first_token_pos, first_token_pos,
last_token_pos); last_token_pos);
RegisterSourceBreakpoint(unresolved_bpt); RegisterBreakpointLocation(unresolved_loc);
unresolved_bpt->Enable();
// Move breakpoints over.
Breakpoint* bpt = matched_loc->breakpoints();
unresolved_loc->set_breakpoints(bpt);
matched_loc->set_breakpoints(NULL);
while (bpt != NULL) {
bpt->set_bpt_location(unresolved_loc);
if (FLAG_verbose_debug) { if (FLAG_verbose_debug) {
OS::Print("Converted latent breakpoint " OS::Print("Converted latent breakpoint "
"%" Pd " in '%s' at line %" Pd "\n", "%" Pd " in '%s' at line %" Pd "\n",
matched_bpt->id(), bpt->id(),
url.ToCString(), url.ToCString(),
line_number); line_number);
} }
bpt = bpt->next();
} }
delete matched_bpt; SyncBreakpointLocation(unresolved_loc);
}
delete matched_loc;
// Break out of the iteration over loaded libraries. If the // Break out of the iteration over loaded libraries. If the
// same url has been loaded into more than one library, we // same url has been loaded into more than one library, we
// only set a breakpoint in the first one. // only set a breakpoint in the first one.
@ -2532,14 +2731,18 @@ void Debugger::NotifyDoneLoading() {
if (!found_match) { if (!found_match) {
// No matching url found in any of the libraries. // No matching url found in any of the libraries.
if (FLAG_verbose_debug) { if (FLAG_verbose_debug) {
Breakpoint* bpt = loc->breakpoints();
while (bpt != NULL) {
OS::Print("No match found for latent breakpoint id " OS::Print("No match found for latent breakpoint id "
"%" Pd " with url '%s'\n", "%" Pd " with url '%s'\n",
bpt->id(), bpt->id(),
url.ToCString()); url.ToCString());
}
bpt = bpt->next(); bpt = bpt->next();
} }
} }
loc = loc->next();
}
}
} }
@ -2576,21 +2779,20 @@ uword Debugger::GetPatchedStubAddress(uword breakpoint_address) {
// Remove and delete the source breakpoint bpt and its associated // Remove and delete the source breakpoint bpt and its associated
// code breakpoints. // code breakpoints.
void Debugger::RemoveBreakpoint(intptr_t bp_id) { void Debugger::RemoveBreakpoint(intptr_t bp_id) {
SourceBreakpoint* prev_bpt = NULL; BreakpointLocation* prev_loc = NULL;
SourceBreakpoint* curr_bpt = src_breakpoints_; BreakpointLocation* curr_loc = breakpoint_locations_;
while (curr_loc != NULL) {
Breakpoint* prev_bpt = NULL;
Breakpoint* curr_bpt = curr_loc->breakpoints();
while (curr_bpt != NULL) { while (curr_bpt != NULL) {
if (curr_bpt->id() == bp_id) { if (curr_bpt->id() == bp_id) {
if (prev_bpt == NULL) { if (prev_bpt == NULL) {
src_breakpoints_ = src_breakpoints_->next(); curr_loc->set_breakpoints(curr_bpt->next());
} else { } else {
prev_bpt->set_next(curr_bpt->next()); prev_bpt->set_next(curr_bpt->next());
} }
SendServiceBreakpointEvent(ServiceEvent::kBreakpointRemoved, curr_bpt);
// Remove references from code breakpoints to this source breakpoint, SendServiceBreakpointEvent(ServiceEvent::kBreakpointRemoved, curr_bpt);
// and disable the code breakpoints.
UnlinkCodeBreakpoints(curr_bpt);
delete curr_bpt;
// Remove references from the current debugger pause event. // Remove references from the current debugger pause event.
if (pause_event_ != NULL && if (pause_event_ != NULL &&
@ -2600,9 +2802,27 @@ void Debugger::RemoveBreakpoint(intptr_t bp_id) {
} }
return; return;
} }
prev_bpt = curr_bpt; prev_bpt = curr_bpt;
curr_bpt = curr_bpt->next(); curr_bpt = curr_bpt->next();
} }
if (curr_loc->breakpoints() == NULL) {
if (prev_loc == NULL) {
breakpoint_locations_ = breakpoint_locations_->next();
} else {
prev_loc->set_next(curr_loc->next());
}
// Remove references from code breakpoints to this source breakpoint,
// and disable the code breakpoints.
UnlinkCodeBreakpoints(curr_loc);
delete curr_loc;
}
prev_loc = curr_loc;
curr_loc = curr_loc->next();
}
// bpt is not a registered breakpoint, nothing to do. // bpt is not a registered breakpoint, nothing to do.
} }
@ -2612,13 +2832,13 @@ void Debugger::RemoveBreakpoint(intptr_t bp_id) {
// returns from the user-defined breakpoint callback. Also, disable the // returns from the user-defined breakpoint callback. Also, disable the
// breakpoint so it no longer fires if it should be hit before it gets // breakpoint so it no longer fires if it should be hit before it gets
// deleted. // deleted.
void Debugger::UnlinkCodeBreakpoints(SourceBreakpoint* src_bpt) { void Debugger::UnlinkCodeBreakpoints(BreakpointLocation* bpt_location) {
ASSERT(src_bpt != NULL); ASSERT(bpt_location != NULL);
CodeBreakpoint* curr_bpt = code_breakpoints_; CodeBreakpoint* curr_bpt = code_breakpoints_;
while (curr_bpt != NULL) { while (curr_bpt != NULL) {
if (curr_bpt->src_bpt() == src_bpt) { if (curr_bpt->bpt_location() == bpt_location) {
curr_bpt->Disable(); curr_bpt->Disable();
curr_bpt->set_src_bpt(NULL); curr_bpt->set_bpt_location(NULL);
} }
curr_bpt = curr_bpt->next(); curr_bpt = curr_bpt->next();
} }
@ -2631,7 +2851,7 @@ void Debugger::RemoveInternalBreakpoints() {
CodeBreakpoint* prev_bpt = NULL; CodeBreakpoint* prev_bpt = NULL;
CodeBreakpoint* curr_bpt = code_breakpoints_; CodeBreakpoint* curr_bpt = code_breakpoints_;
while (curr_bpt != NULL) { while (curr_bpt != NULL) {
if (curr_bpt->src_bpt() == NULL) { if (curr_bpt->bpt_location() == NULL) {
if (prev_bpt == NULL) { if (prev_bpt == NULL) {
code_breakpoints_ = code_breakpoints_->next(); code_breakpoints_ = code_breakpoints_->next();
} else { } else {
@ -2649,9 +2869,9 @@ void Debugger::RemoveInternalBreakpoints() {
} }
SourceBreakpoint* Debugger::GetSourceBreakpoint(const Script& script, BreakpointLocation* Debugger::GetBreakpointLocation(const Script& script,
intptr_t token_pos) { intptr_t token_pos) {
SourceBreakpoint* bpt = src_breakpoints_; BreakpointLocation* bpt = breakpoint_locations_;
while (bpt != NULL) { while (bpt != NULL) {
if ((bpt->script_ == script.raw()) && (bpt->token_pos_ == token_pos)) { if ((bpt->script_ == script.raw()) && (bpt->token_pos_ == token_pos)) {
return bpt; return bpt;
@ -2662,21 +2882,25 @@ SourceBreakpoint* Debugger::GetSourceBreakpoint(const Script& script,
} }
SourceBreakpoint* Debugger::GetBreakpointById(intptr_t id) { Breakpoint* Debugger::GetBreakpointById(intptr_t id) {
SourceBreakpoint* bpt = src_breakpoints_; BreakpointLocation* loc = breakpoint_locations_;
while (loc != NULL) {
Breakpoint* bpt = loc->breakpoints();
while (bpt != NULL) { while (bpt != NULL) {
if (bpt->id() == id) { if (bpt->id() == id) {
return bpt; return bpt;
} }
bpt = bpt->next(); bpt = bpt->next();
} }
loc = loc->next();
}
return NULL; return NULL;
} }
SourceBreakpoint* Debugger::GetLatentBreakpoint(const String& url, BreakpointLocation* Debugger::GetLatentBreakpoint(const String& url,
intptr_t line) { intptr_t line) {
SourceBreakpoint* bpt = latent_breakpoints_; BreakpointLocation* bpt = latent_locations_;
String& bpt_url = String::Handle(isolate_); String& bpt_url = String::Handle(isolate_);
while (bpt != NULL) { while (bpt != NULL) {
bpt_url = bpt->url(); bpt_url = bpt->url();
@ -2686,17 +2910,17 @@ SourceBreakpoint* Debugger::GetLatentBreakpoint(const String& url,
bpt = bpt->next(); bpt = bpt->next();
} }
// No breakpint for this url and line requested. Allocate new one. // No breakpint for this url and line requested. Allocate new one.
bpt = new SourceBreakpoint(nextId(), url, line); bpt = new BreakpointLocation(url, line);
bpt->set_next(latent_breakpoints_); bpt->set_next(latent_locations_);
latent_breakpoints_ = bpt; latent_locations_ = bpt;
return bpt; return bpt;
} }
void Debugger::RegisterSourceBreakpoint(SourceBreakpoint* bpt) { void Debugger::RegisterBreakpointLocation(BreakpointLocation* bpt) {
ASSERT(bpt->next() == NULL); ASSERT(bpt->next() == NULL);
bpt->set_next(src_breakpoints_); bpt->set_next(breakpoint_locations_);
src_breakpoints_ = bpt; breakpoint_locations_ = bpt;
} }

View file

@ -18,12 +18,74 @@ class JSONArray;
class JSONStream; class JSONStream;
class ObjectPointerVisitor; class ObjectPointerVisitor;
class RemoteObjectCache; class RemoteObjectCache;
class SourceBreakpoint; class BreakpointLocation;
class StackFrame; class StackFrame;
// SourceBreakpoint represents a user-specified breakpoint location in // A user-defined breakpoint, which either fires once, for a particular closure,
// Dart source. There may be more than one CodeBreakpoint object per // or always. The API's notion of a breakpoint corresponds to this object.
// SourceBreakpoint. class Breakpoint {
public:
Breakpoint(intptr_t id, BreakpointLocation* bpt_location)
: id_(id),
kind_(Breakpoint::kNone),
next_(NULL),
closure_(Instance::null()),
bpt_location_(bpt_location) {}
intptr_t id() const { return id_; }
Breakpoint* next() const { return next_; }
void set_next(Breakpoint* n) { next_ = n; }
BreakpointLocation* bpt_location() const { return bpt_location_; }
void set_bpt_location(BreakpointLocation* new_bpt_location);
bool IsRepeated() const { return kind_ == kRepeated; }
bool IsSingleShot() const { return kind_ == kSingleShot; }
bool IsPerClosure() const { return kind_ == kPerClosure; }
RawInstance* closure() const { return closure_; }
void SetIsRepeated() {
ASSERT(kind_ == kNone);
kind_ = kRepeated;
}
void SetIsSingleShot() {
ASSERT(kind_ == kNone);
kind_ = kSingleShot;
}
void SetIsPerClosure(const Instance& closure) {
ASSERT(kind_ == kNone);
kind_ = kPerClosure;
closure_ = closure.raw();
}
void PrintJSON(JSONStream* stream);
private:
void VisitObjectPointers(ObjectPointerVisitor* visitor);
enum ConditionKind {
kNone,
kRepeated,
kSingleShot,
kPerClosure,
};
intptr_t id_;
ConditionKind kind_;
Breakpoint* next_;
RawInstance* closure_;
BreakpointLocation* bpt_location_;
friend class BreakpointLocation;
DISALLOW_COPY_AND_ASSIGN(Breakpoint);
};
// BreakpointLocation represents a collection of breakpoint conditions at the
// same token position in Dart source. There may be more than one CodeBreakpoint
// object per BreakpointLocation.
// An unresolved breakpoint is one where the underlying code has not // An unresolved breakpoint is one where the underlying code has not
// been compiled yet. Since the code has not been compiled, we don't know // been compiled yet. Since the code has not been compiled, we don't know
// the definitive source location yet. The requested source location may // the definitive source location yet. The requested source location may
@ -32,22 +94,21 @@ class StackFrame;
// that is not loaded in the VM when the breakpoint is requested. // that is not loaded in the VM when the breakpoint is requested.
// When a script with matching url is loaded, a latent breakpoint // When a script with matching url is loaded, a latent breakpoint
// becomes an unresolved breakpoint. // becomes an unresolved breakpoint.
class SourceBreakpoint { class BreakpointLocation {
public: public:
// Create a new unresolved breakpoint. // Create a new unresolved breakpoint.
SourceBreakpoint(intptr_t id, BreakpointLocation(const Script& script,
const Script& script,
intptr_t token_pos, intptr_t token_pos,
intptr_t end_token_pos); intptr_t end_token_pos);
// Create a new latent breakpoint. // Create a new latent breakpoint.
SourceBreakpoint(intptr_t id, BreakpointLocation(const String& url,
const String& url,
intptr_t line_number); intptr_t line_number);
~BreakpointLocation();
RawFunction* function() const { return function_; } RawFunction* function() const { return function_; }
intptr_t token_pos() const { return token_pos_; } intptr_t token_pos() const { return token_pos_; }
intptr_t end_token_pos() const { return end_token_pos_; } intptr_t end_token_pos() const { return end_token_pos_; }
intptr_t id() const { return id_; }
RawScript* script() const { return script_; } RawScript* script() const { return script_; }
RawString* url() const { return url_; } RawString* url() const { return url_; }
@ -55,45 +116,46 @@ class SourceBreakpoint {
void GetCodeLocation(Library* lib, Script* script, intptr_t* token_pos); void GetCodeLocation(Library* lib, Script* script, intptr_t* token_pos);
void Enable(); Breakpoint* AddRepeated(Debugger* dbg);
void Disable(); Breakpoint* AddSingleShot(Debugger* dbg);
bool IsEnabled() const { return is_enabled_; } Breakpoint* AddPerClosure(Debugger* dbg, const Instance& closure);
bool AnyEnabled() const;
bool IsResolved() const { return is_resolved_; } bool IsResolved() const { return is_resolved_; }
bool IsLatent() const { return token_pos_ < 0; } bool IsLatent() const { return token_pos_ < 0; }
bool IsOneShot() const { return is_one_shot_; }
void SetIsOneShot() { is_one_shot_ = true; }
void PrintJSON(JSONStream* stream);
private: private:
void VisitObjectPointers(ObjectPointerVisitor* visitor); void VisitObjectPointers(ObjectPointerVisitor* visitor);
void SetResolved(const Function& func, intptr_t token_pos); void SetResolved(const Function& func, intptr_t token_pos);
void set_next(SourceBreakpoint* value) { next_ = value; }
SourceBreakpoint* next() const { return this->next_; }
const intptr_t id_; BreakpointLocation* next() const { return this->next_; }
void set_next(BreakpointLocation* value) { next_ = value; }
void AddBreakpoint(Breakpoint* bpt, Debugger* dbg);
Breakpoint* breakpoints() const { return this->conditions_; }
void set_breakpoints(Breakpoint* head) { this->conditions_ = head; }
RawScript* script_; RawScript* script_;
RawString* url_; RawString* url_;
intptr_t token_pos_; intptr_t token_pos_;
intptr_t end_token_pos_; intptr_t end_token_pos_;
bool is_resolved_; bool is_resolved_;
bool is_enabled_; BreakpointLocation* next_;
bool is_one_shot_; Breakpoint* conditions_;
SourceBreakpoint* next_;
// Valid for resolved breakpoints: // Valid for resolved breakpoints:
RawFunction* function_; RawFunction* function_;
intptr_t line_number_; intptr_t line_number_;
friend class Debugger; friend class Debugger;
DISALLOW_COPY_AND_ASSIGN(SourceBreakpoint); DISALLOW_COPY_AND_ASSIGN(BreakpointLocation);
}; };
// CodeBreakpoint represents a location in compiled code. There may be // CodeBreakpoint represents a location in compiled code. There may be
// more than one CodeBreakpoint for one SourceBreakpoint, e.g. when a // more than one CodeBreakpoint for one BreakpointLocation, e.g. when a
// function gets compiled as a regular function and as a closure. // function gets compiled as a regular function and as a closure.
class CodeBreakpoint { class CodeBreakpoint {
public: public:
@ -106,7 +168,7 @@ class CodeBreakpoint {
RawFunction* function() const; RawFunction* function() const;
uword pc() const { return pc_; } uword pc() const { return pc_; }
intptr_t token_pos() const { return token_pos_; } intptr_t token_pos() const { return token_pos_; }
bool IsInternal() const { return src_bpt_ == NULL; } bool IsInternal() const { return bpt_location_ == NULL; }
RawScript* SourceCode(); RawScript* SourceCode();
RawString* SourceUrl(); RawString* SourceUrl();
@ -121,8 +183,8 @@ class CodeBreakpoint {
private: private:
void VisitObjectPointers(ObjectPointerVisitor* visitor); void VisitObjectPointers(ObjectPointerVisitor* visitor);
SourceBreakpoint* src_bpt() const { return src_bpt_; } BreakpointLocation* bpt_location() const { return bpt_location_; }
void set_src_bpt(SourceBreakpoint* value) { src_bpt_ = value; } void set_bpt_location(BreakpointLocation* value) { bpt_location_ = value; }
void set_next(CodeBreakpoint* value) { next_ = value; } void set_next(CodeBreakpoint* value) { next_ = value; }
CodeBreakpoint* next() const { return this->next_; } CodeBreakpoint* next() const { return this->next_; }
@ -136,7 +198,7 @@ class CodeBreakpoint {
intptr_t line_number_; intptr_t line_number_;
bool is_enabled_; bool is_enabled_;
SourceBreakpoint* src_bpt_; BreakpointLocation* bpt_location_;
CodeBreakpoint* next_; CodeBreakpoint* next_;
RawPcDescriptors::Kind breakpoint_kind_; RawPcDescriptors::Kind breakpoint_kind_;
@ -194,6 +256,8 @@ class ActivationFrame : public ZoneAllocated {
Object* value); Object* value);
RawArray* GetLocalVariables(); RawArray* GetLocalVariables();
RawObject* GetParameter(intptr_t index);
RawObject* GetClosure();
RawObject* GetReceiver(); RawObject* GetReceiver();
const Context& GetSavedCurrentContext(); const Context& GetSavedCurrentContext();
@ -273,11 +337,6 @@ class DebuggerStackTrace : public ZoneAllocated {
}; };
typedef void BreakpointHandler(Dart_Port isolate_id,
SourceBreakpoint* bpt,
DebuggerStackTrace* stack);
class DebuggerEvent { class DebuggerEvent {
public: public:
enum EventType { enum EventType {
@ -315,11 +374,11 @@ class DebuggerEvent {
top_frame_ = frame; top_frame_ = frame;
} }
SourceBreakpoint* breakpoint() const { Breakpoint* breakpoint() const {
ASSERT(type_ == kBreakpointReached || type_ == kBreakpointResolved); ASSERT(type_ == kBreakpointReached || type_ == kBreakpointResolved);
return breakpoint_; return breakpoint_;
} }
void set_breakpoint(SourceBreakpoint* bpt) { void set_breakpoint(Breakpoint* bpt) {
ASSERT(type_ == kBreakpointReached || type_ == kBreakpointResolved); ASSERT(type_ == kBreakpointReached || type_ == kBreakpointResolved);
breakpoint_ = bpt; breakpoint_ = bpt;
} }
@ -341,7 +400,7 @@ class DebuggerEvent {
Isolate* isolate_; Isolate* isolate_;
EventType type_; EventType type_;
ActivationFrame* top_frame_; ActivationFrame* top_frame_;
SourceBreakpoint* breakpoint_; Breakpoint* breakpoint_;
const Object* exception_; const Object* exception_;
}; };
@ -365,15 +424,21 @@ class Debugger {
const String& function_name); const String& function_name);
// Set breakpoint at closest location to function entry. // Set breakpoint at closest location to function entry.
SourceBreakpoint* SetBreakpointAtEntry(const Function& target_function); Breakpoint* SetBreakpointAtEntry(const Function& target_function,
bool single_shot);
Breakpoint* SetBreakpointAtActivation(const Instance& closure);
// TODO(turnidge): script_url may no longer be specific enough. // TODO(turnidge): script_url may no longer be specific enough.
SourceBreakpoint* SetBreakpointAtLine(const String& script_url, Breakpoint* SetBreakpointAtLine(const String& script_url,
intptr_t line_number); intptr_t line_number);
RawError* OneTimeBreakAtEntry(const Function& target_function); RawError* OneTimeBreakAtEntry(const Function& target_function);
BreakpointLocation* BreakpointLocationAtLine(const String& script_url,
intptr_t line_number);
void RemoveBreakpoint(intptr_t bp_id); void RemoveBreakpoint(intptr_t bp_id);
SourceBreakpoint* GetBreakpointById(intptr_t id); Breakpoint* GetBreakpointById(intptr_t id);
void SetStepOver(); void SetStepOver();
void SetSingleStep(); void SetSingleStep();
@ -476,22 +541,22 @@ class Debugger {
intptr_t requested_token_pos, intptr_t requested_token_pos,
intptr_t last_token_pos); intptr_t last_token_pos);
void DeoptimizeWorld(); void DeoptimizeWorld();
SourceBreakpoint* SetBreakpoint(const Script& script, BreakpointLocation* SetBreakpoint(const Script& script,
intptr_t token_pos, intptr_t token_pos,
intptr_t last_token_pos); intptr_t last_token_pos);
void RemoveInternalBreakpoints(); void RemoveInternalBreakpoints();
void UnlinkCodeBreakpoints(SourceBreakpoint* src_bpt); void UnlinkCodeBreakpoints(BreakpointLocation* bpt_location);
SourceBreakpoint* GetLatentBreakpoint(const String& url, intptr_t line); BreakpointLocation* GetLatentBreakpoint(const String& url, intptr_t line);
void RegisterSourceBreakpoint(SourceBreakpoint* bpt); void RegisterBreakpointLocation(BreakpointLocation* bpt);
void RegisterCodeBreakpoint(CodeBreakpoint* bpt); void RegisterCodeBreakpoint(CodeBreakpoint* bpt);
SourceBreakpoint* GetSourceBreakpoint(const Script& script, BreakpointLocation* GetBreakpointLocation(const Script& script,
intptr_t token_pos); intptr_t token_pos);
void MakeCodeBreakpointAt(const Function& func, void MakeCodeBreakpointAt(const Function& func,
SourceBreakpoint* bpt); BreakpointLocation* bpt);
// Returns NULL if no breakpoint exists for the given address. // Returns NULL if no breakpoint exists for the given address.
CodeBreakpoint* GetCodeBreakpoint(uword breakpoint_address); CodeBreakpoint* GetCodeBreakpoint(uword breakpoint_address);
void SyncBreakpoint(SourceBreakpoint* bpt); void SyncBreakpointLocation(BreakpointLocation* loc);
ActivationFrame* TopDartFrame() const; ActivationFrame* TopDartFrame() const;
static ActivationFrame* CollectDartFrame(Isolate* isolate, static ActivationFrame* CollectDartFrame(Isolate* isolate,
@ -504,9 +569,9 @@ class Debugger {
StackFrame* frame, StackFrame* frame,
const Code& code); const Code& code);
static DebuggerStackTrace* CollectStackTrace(); static DebuggerStackTrace* CollectStackTrace();
void SignalBpResolved(SourceBreakpoint *bpt); void SignalBpResolved(Breakpoint *bpt);
void SignalPausedEvent(ActivationFrame* top_frame, void SignalPausedEvent(ActivationFrame* top_frame,
SourceBreakpoint* bpt); Breakpoint* bpt);
intptr_t nextId() { return next_id_++; } intptr_t nextId() { return next_id_++; }
@ -531,8 +596,8 @@ class Debugger {
// ID number generator. // ID number generator.
intptr_t next_id_; intptr_t next_id_;
SourceBreakpoint* latent_breakpoints_; BreakpointLocation* latent_locations_;
SourceBreakpoint* src_breakpoints_; BreakpointLocation* breakpoint_locations_;
CodeBreakpoint* code_breakpoints_; CodeBreakpoint* code_breakpoints_;
// Tells debugger what to do when resuming execution after a breakpoint. // Tells debugger what to do when resuming execution after a breakpoint.
@ -565,7 +630,7 @@ class Debugger {
static EventHandler* event_handler_; static EventHandler* event_handler_;
friend class Isolate; friend class Isolate;
friend class SourceBreakpoint; friend class BreakpointLocation;
DISALLOW_COPY_AND_ASSIGN(Debugger); DISALLOW_COPY_AND_ASSIGN(Debugger);
}; };

View file

@ -130,13 +130,13 @@ static void DebuggerEventHandler(DebuggerEvent* event) {
} }
} else if (event->type() == DebuggerEvent::kBreakpointResolved) { } else if (event->type() == DebuggerEvent::kBreakpointResolved) {
if (bp_resolved_handler != NULL) { if (bp_resolved_handler != NULL) {
SourceBreakpoint* bpt = event->breakpoint(); Breakpoint* bpt = event->breakpoint();
ASSERT(bpt != NULL); ASSERT(bpt != NULL);
Dart_CodeLocation location; Dart_CodeLocation location;
Library& library = Library::Handle(isolate); Library& library = Library::Handle(isolate);
Script& script = Script::Handle(isolate); Script& script = Script::Handle(isolate);
intptr_t token_pos; intptr_t token_pos;
bpt->GetCodeLocation(&library, &script, &token_pos); bpt->bpt_location()->GetCodeLocation(&library, &script, &token_pos);
location.script_url = Api::NewHandle(isolate, script.url()); location.script_url = Api::NewHandle(isolate, script.url());
location.library_id = library.index(); location.library_id = library.index();
location.token_pos = token_pos; location.token_pos = token_pos;
@ -341,7 +341,7 @@ DART_EXPORT Dart_Handle Dart_SetBreakpoint(
Debugger* debugger = isolate->debugger(); Debugger* debugger = isolate->debugger();
ASSERT(debugger != NULL); ASSERT(debugger != NULL);
SourceBreakpoint* bpt = Breakpoint* bpt =
debugger->SetBreakpointAtLine(script_url, line_number); debugger->SetBreakpointAtLine(script_url, line_number);
if (bpt == NULL) { if (bpt == NULL) {
return Api::NewError("%s: could not set breakpoint at line %" Pd " in '%s'", return Api::NewError("%s: could not set breakpoint at line %" Pd " in '%s'",
@ -357,12 +357,12 @@ DART_EXPORT Dart_Handle Dart_GetBreakpointURL(intptr_t bp_id) {
Debugger* debugger = isolate->debugger(); Debugger* debugger = isolate->debugger();
ASSERT(debugger != NULL); ASSERT(debugger != NULL);
SourceBreakpoint* bpt = debugger->GetBreakpointById(bp_id); Breakpoint* bpt = debugger->GetBreakpointById(bp_id);
if (bpt == NULL) { if (bpt == NULL) {
return Api::NewError("%s: breakpoint with id %" Pd " does not exist", return Api::NewError("%s: breakpoint with id %" Pd " does not exist",
CURRENT_FUNC, bp_id); CURRENT_FUNC, bp_id);
} }
return Api::NewHandle(isolate, bpt->url()); return Api::NewHandle(isolate, bpt->bpt_location()->url());
} }
@ -372,12 +372,12 @@ DART_EXPORT Dart_Handle Dart_GetBreakpointLine(intptr_t bp_id) {
Debugger* debugger = isolate->debugger(); Debugger* debugger = isolate->debugger();
ASSERT(debugger != NULL); ASSERT(debugger != NULL);
SourceBreakpoint* bpt = debugger->GetBreakpointById(bp_id); Breakpoint* bpt = debugger->GetBreakpointById(bp_id);
if (bpt == NULL) { if (bpt == NULL) {
return Api::NewError("%s: breakpoint with id %" Pd " does not exist", return Api::NewError("%s: breakpoint with id %" Pd " does not exist",
CURRENT_FUNC, bp_id); CURRENT_FUNC, bp_id);
} }
return Dart_NewInteger(bpt->LineNumber()); return Dart_NewInteger(bpt->bpt_location()->LineNumber());
} }
@ -411,7 +411,7 @@ DART_EXPORT Dart_Handle Dart_SetBreakpointAtEntry(
function_name.ToCString()); function_name.ToCString());
} }
SourceBreakpoint* bpt = debugger->SetBreakpointAtEntry(bp_target); Breakpoint* bpt = debugger->SetBreakpointAtEntry(bp_target, false);
if (bpt == NULL) { if (bpt == NULL) {
const char* target_name = Debugger::QualifiedFunctionName(bp_target); const char* target_name = Debugger::QualifiedFunctionName(bp_target);
return Api::NewError("%s: no breakpoint location found in '%s'", return Api::NewError("%s: no breakpoint location found in '%s'",

View file

@ -327,7 +327,7 @@ void JSONStream::PrintValue(const Object& o, bool ref) {
} }
void JSONStream::PrintValue(SourceBreakpoint* bpt) { void JSONStream::PrintValue(Breakpoint* bpt) {
PrintCommaIfNeeded(); PrintCommaIfNeeded();
bpt->PrintJSON(this); bpt->PrintJSON(this);
} }
@ -415,7 +415,7 @@ void JSONStream::PrintProperty(const char* name, const ServiceEvent* event) {
} }
void JSONStream::PrintProperty(const char* name, SourceBreakpoint* bpt) { void JSONStream::PrintProperty(const char* name, Breakpoint* bpt) {
PrintPropertyName(name); PrintPropertyName(name);
PrintValue(bpt); PrintValue(bpt);
} }

View file

@ -22,7 +22,7 @@ class MessageQueue;
class Metric; class Metric;
class Object; class Object;
class ServiceEvent; class ServiceEvent;
class SourceBreakpoint; class Breakpoint;
class String; class String;
class Zone; class Zone;
@ -115,7 +115,7 @@ class JSONStream : ValueObject {
void PrintValueNoEscape(const char* s); void PrintValueNoEscape(const char* s);
void PrintfValue(const char* format, ...) PRINTF_ATTRIBUTE(2, 3); void PrintfValue(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
void PrintValue(const Object& o, bool ref = true); void PrintValue(const Object& o, bool ref = true);
void PrintValue(SourceBreakpoint* bpt); void PrintValue(Breakpoint* bpt);
void PrintValue(const ServiceEvent* event); void PrintValue(const ServiceEvent* event);
void PrintValue(Metric* metric); void PrintValue(Metric* metric);
void PrintValue(MessageQueue* queue); void PrintValue(MessageQueue* queue);
@ -135,7 +135,7 @@ class JSONStream : ValueObject {
void PrintProperty(const char* name, const Object& o, bool ref = true); void PrintProperty(const char* name, const Object& o, bool ref = true);
void PrintProperty(const char* name, const ServiceEvent* event); void PrintProperty(const char* name, const ServiceEvent* event);
void PrintProperty(const char* name, SourceBreakpoint* bpt); void PrintProperty(const char* name, Breakpoint* bpt);
void PrintProperty(const char* name, Metric* metric); void PrintProperty(const char* name, Metric* metric);
void PrintProperty(const char* name, MessageQueue* queue); void PrintProperty(const char* name, MessageQueue* queue);
void PrintProperty(const char* name, Isolate* isolate); void PrintProperty(const char* name, Isolate* isolate);
@ -216,7 +216,7 @@ class JSONObject : public ValueObject {
void AddProperty(const char* name, const ServiceEvent* event) const { void AddProperty(const char* name, const ServiceEvent* event) const {
stream_->PrintProperty(name, event); stream_->PrintProperty(name, event);
} }
void AddProperty(const char* name, SourceBreakpoint* bpt) const { void AddProperty(const char* name, Breakpoint* bpt) const {
stream_->PrintProperty(name, bpt); stream_->PrintProperty(name, bpt);
} }
void AddProperty(const char* name, Metric* metric) const { void AddProperty(const char* name, Metric* metric) const {
@ -267,7 +267,7 @@ class JSONArray : public ValueObject {
void AddValue(Isolate* isolate, bool ref = true) const { void AddValue(Isolate* isolate, bool ref = true) const {
stream_->PrintValue(isolate, ref); stream_->PrintValue(isolate, ref);
} }
void AddValue(SourceBreakpoint* bpt) const { void AddValue(Breakpoint* bpt) const {
stream_->PrintValue(bpt); stream_->PrintValue(bpt);
} }
void AddValue(const ServiceEvent* event) const { void AddValue(const ServiceEvent* event) const {

View file

@ -4182,7 +4182,7 @@ TEST_CASE(FunctionWithBreakpointNotInlined) {
EXPECT(func_b.CanBeInlined()); EXPECT(func_b.CanBeInlined());
// After setting a breakpoint in a function A.b, it is no longer inlineable. // After setting a breakpoint in a function A.b, it is no longer inlineable.
SourceBreakpoint* bpt = Breakpoint* bpt =
Isolate::Current()->debugger()->SetBreakpointAtLine(name, Isolate::Current()->debugger()->SetBreakpointAtLine(name,
kBreakpointLine); kBreakpointLine);
ASSERT(bpt != NULL); ASSERT(bpt != NULL);

View file

@ -1196,7 +1196,7 @@ static void PrintSentinel(JSONStream* js, SentinelType sentinel_type) {
} }
static SourceBreakpoint* LookupBreakpoint(Isolate* isolate, const char* id) { static Breakpoint* LookupBreakpoint(Isolate* isolate, const char* id) {
size_t end_pos = strcspn(id, "/"); size_t end_pos = strcspn(id, "/");
if (end_pos == strlen(id)) { if (end_pos == strlen(id)) {
return NULL; return NULL;
@ -1204,7 +1204,7 @@ static SourceBreakpoint* LookupBreakpoint(Isolate* isolate, const char* id) {
const char* rest = id + end_pos + 1; // +1 for '/'. const char* rest = id + end_pos + 1; // +1 for '/'.
if (strncmp("breakpoints", id, end_pos) == 0) { if (strncmp("breakpoints", id, end_pos) == 0) {
intptr_t bpt_id = 0; intptr_t bpt_id = 0;
SourceBreakpoint* bpt = NULL; Breakpoint* bpt = NULL;
if (GetIntegerId(rest, &bpt_id)) { if (GetIntegerId(rest, &bpt_id)) {
bpt = isolate->debugger()->GetBreakpointById(bpt_id); bpt = isolate->debugger()->GetBreakpointById(bpt_id);
} }
@ -1808,7 +1808,7 @@ static bool AddBreakpoint(Isolate* isolate, JSONStream* js) {
} }
const Script& script = Script::Cast(obj); const Script& script = Script::Cast(obj);
const String& script_url = String::Handle(script.url()); const String& script_url = String::Handle(script.url());
SourceBreakpoint* bpt = Breakpoint* bpt =
isolate->debugger()->SetBreakpointAtLine(script_url, line); isolate->debugger()->SetBreakpointAtLine(script_url, line);
if (bpt == NULL) { if (bpt == NULL) {
js->PrintError(kNoBreakAtLine, NULL); js->PrintError(kNoBreakAtLine, NULL);
@ -1834,8 +1834,34 @@ static bool AddBreakpointAtEntry(Isolate* isolate, JSONStream* js) {
return true; return true;
} }
const Function& function = Function::Cast(obj); const Function& function = Function::Cast(obj);
SourceBreakpoint* bpt = Breakpoint* bpt =
isolate->debugger()->SetBreakpointAtEntry(function); isolate->debugger()->SetBreakpointAtEntry(function, false);
if (bpt == NULL) {
js->PrintError(kNoBreakAtFunction, NULL);
return true;
}
bpt->PrintJSON(js);
return true;
}
static const MethodParameter* add_breakpoint_at_activation_params[] = {
ISOLATE_PARAMETER,
new IdParameter("objectId", true),
NULL,
};
static bool AddBreakpointAtActivation(Isolate* isolate, JSONStream* js) {
const char* object_id = js->LookupParam("objectId");
Object& obj = Object::Handle(LookupHeapObject(isolate, object_id, NULL));
if (obj.raw() == Object::sentinel().raw() || !obj.IsInstance()) {
PrintInvalidParamError(js, "objectId");
return true;
}
const Instance& closure = Instance::Cast(obj);
Breakpoint* bpt =
isolate->debugger()->SetBreakpointAtActivation(closure);
if (bpt == NULL) { if (bpt == NULL) {
js->PrintError(kNoBreakAtFunction, NULL); js->PrintError(kNoBreakAtFunction, NULL);
return true; return true;
@ -1857,7 +1883,7 @@ static bool RemoveBreakpoint(Isolate* isolate, JSONStream* js) {
return true; return true;
} }
const char* bpt_id = js->LookupParam("breakpointId"); const char* bpt_id = js->LookupParam("breakpointId");
SourceBreakpoint* bpt = LookupBreakpoint(isolate, bpt_id); Breakpoint* bpt = LookupBreakpoint(isolate, bpt_id);
if (bpt == NULL) { if (bpt == NULL) {
fprintf(stderr, "ERROR1"); fprintf(stderr, "ERROR1");
PrintInvalidParamError(js, "breakpointId"); PrintInvalidParamError(js, "breakpointId");
@ -2402,7 +2428,7 @@ static bool GetObject(Isolate* isolate, JSONStream* js) {
} }
// Handle non-heap objects. // Handle non-heap objects.
SourceBreakpoint* bpt = LookupBreakpoint(isolate, id); Breakpoint* bpt = LookupBreakpoint(isolate, id);
if (bpt != NULL) { if (bpt != NULL) {
bpt->PrintJSON(js); bpt->PrintJSON(js);
return true; return true;
@ -2592,6 +2618,8 @@ static ServiceMethodDescriptor service_methods_[] = {
add_breakpoint_params }, add_breakpoint_params },
{ "addBreakpointAtEntry", AddBreakpointAtEntry, { "addBreakpointAtEntry", AddBreakpointAtEntry,
add_breakpoint_at_entry_params }, add_breakpoint_at_entry_params },
{ "_addBreakpointAtActivation", AddBreakpointAtActivation,
add_breakpoint_at_activation_params },
{ "clearCpuProfile", ClearCpuProfile, { "clearCpuProfile", ClearCpuProfile,
clear_cpu_profile_params }, clear_cpu_profile_params },
{ "evaluate", Evaluate, { "evaluate", Evaluate,

View file

@ -50,10 +50,10 @@ class ServiceEvent {
EventType type() const { return type_; } EventType type() const { return type_; }
SourceBreakpoint* breakpoint() const { Breakpoint* breakpoint() const {
return breakpoint_; return breakpoint_;
} }
void set_breakpoint(SourceBreakpoint* bpt) { void set_breakpoint(Breakpoint* bpt) {
ASSERT(type() == kPauseBreakpoint || ASSERT(type() == kPauseBreakpoint ||
type() == kBreakpointAdded || type() == kBreakpointAdded ||
type() == kBreakpointResolved || type() == kBreakpointResolved ||
@ -103,7 +103,7 @@ class ServiceEvent {
private: private:
Isolate* isolate_; Isolate* isolate_;
EventType type_; EventType type_;
SourceBreakpoint* breakpoint_; Breakpoint* breakpoint_;
ActivationFrame* top_frame_; ActivationFrame* top_frame_;
const Object* exception_; const Object* exception_;
const Object* inspectee_; const Object* inspectee_;