mirror of
https://github.com/dart-lang/sdk
synced 2024-09-20 06:21:32 +00:00
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:
parent
dfee2bf45d
commit
74dfae69e3
|
@ -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 });
|
||||||
|
|
191
runtime/observatory/tests/service/break_on_activation_test.dart
Normal file
191
runtime/observatory/tests/service/break_on_activation_test.dart
Normal 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);
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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'",
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
Loading…
Reference in a new issue