Index: runtime/vm/debugger.cc |
=================================================================== |
--- runtime/vm/debugger.cc (revision 4638) |
+++ runtime/vm/debugger.cc (working copy) |
@@ -5,6 +5,7 @@ |
#include "vm/debugger.h" |
#include "vm/code_index_table.h" |
+#include "vm/code_generator.h" |
#include "vm/code_patcher.h" |
#include "vm/compiler.h" |
#include "vm/dart_entry.h" |
@@ -28,8 +29,8 @@ |
: function_(func.raw()), |
pc_desc_index_(pc_desc_index), |
pc_(0), |
- saved_bytes_(0), |
line_number_(-1), |
+ is_patched_(false), |
next_(NULL) { |
Code& code = Code::Handle(func.code()); |
ASSERT(!code.IsNull()); // Function must be compiled. |
@@ -39,6 +40,7 @@ |
ASSERT(this->token_index_ > 0); |
this->pc_ = desc.PC(pc_desc_index); |
ASSERT(this->pc_ != 0); |
+ this->breakpoint_kind_ = desc.DescriptorKind(pc_desc_index); |
} |
@@ -72,8 +74,8 @@ |
} |
-ActivationFrame::ActivationFrame(uword pc, uword fp) |
- : pc_(pc), fp_(fp), |
+ActivationFrame::ActivationFrame(uword pc, uword fp, uword sp) |
+ : pc_(pc), fp_(fp), sp_(sp), |
function_(Function::ZoneHandle()), |
token_index_(-1), |
line_number_(-1), |
@@ -278,11 +280,75 @@ |
} |
+void Breakpoint::PatchCode() { |
+ ASSERT(!is_patched_); |
+ switch (breakpoint_kind_) { |
+ case PcDescriptors::kIcCall: { |
+ int num_args, num_named_args; |
+ CodePatcher::GetInstanceCallAt(pc_, |
+ NULL, &num_args, &num_named_args, |
+ &saved_bytes_.target_address_); |
+ CodePatcher::PatchInstanceCallAt( |
+ pc_, StubCode::BreakpointDynamicEntryPoint()); |
+ break; |
+ } |
+ case PcDescriptors::kFuncCall: { |
+ Function& func = Function::Handle(); |
+ CodePatcher::GetStaticCallAt(pc_, &func, &saved_bytes_.target_address_); |
+ CodePatcher::PatchStaticCallAt(pc_, |
+ StubCode::BreakpointStaticEntryPoint()); |
+ break; |
+ } |
+ case PcDescriptors::kReturn: |
+ PatchFunctionReturn(); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ is_patched_ = true; |
+} |
+ |
+ |
+void Breakpoint::RestoreCode() { |
+ ASSERT(is_patched_); |
+ switch (breakpoint_kind_) { |
+ case PcDescriptors::kIcCall: |
+ CodePatcher::PatchInstanceCallAt(pc_, saved_bytes_.target_address_); |
+ break; |
+ case PcDescriptors::kFuncCall: |
+ CodePatcher::PatchStaticCallAt(pc_, saved_bytes_.target_address_); |
+ break; |
+ case PcDescriptors::kReturn: |
+ RestoreFunctionReturn(); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ is_patched_ = false; |
+} |
+ |
+void Breakpoint::SetActive(bool value) { |
+ if (value && !is_patched_) { |
+ PatchCode(); |
+ return; |
+ } |
+ if (!value && is_patched_) { |
+ RestoreCode(); |
+ } |
+} |
+ |
+ |
+bool Breakpoint::IsActive() { |
+ return is_patched_; |
+} |
+ |
+ |
Debugger::Debugger() |
: isolate_(NULL), |
initialized_(false), |
bp_handler_(NULL), |
- breakpoints_(NULL) { |
+ breakpoints_(NULL), |
+ resume_action_(kContinue) { |
} |
@@ -345,6 +411,37 @@ |
} |
+void Debugger::InstrumentForStepping(const Function &target_function) { |
+ if (!target_function.HasCode()) { |
+ Compiler::CompileFunction(target_function); |
+ // If there were any errors, ignore them silently and return without |
+ // adding breakpoints to target. |
+ if (!target_function.HasCode()) { |
+ return; |
+ } |
+ } |
+ Code& code = Code::Handle(target_function.code()); |
+ ASSERT(!code.IsNull()); |
+ PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
+ for (int i = 0; i < desc.Length(); i++) { |
+ Breakpoint* bpt = GetBreakpoint(desc.PC(i)); |
+ if (bpt != NULL) { |
+ // There is already a breakpoint for this address. Leave it alone. |
+ continue; |
+ } |
+ PcDescriptors::Kind kind = desc.DescriptorKind(i); |
+ if ((kind == PcDescriptors::kIcCall) || |
+ (kind == PcDescriptors::kFuncCall) || |
+ (kind == PcDescriptors::kReturn)) { |
+ bpt = new Breakpoint(target_function, i); |
+ bpt->set_temporary(true); |
+ bpt->PatchCode(); |
+ RegisterBreakpoint(bpt); |
+ } |
+ } |
+} |
+ |
+ |
// TODO(hausner): Distinguish between newly created breakpoints and |
// returning a breakpoint that already exists? |
Breakpoint* Debugger::SetBreakpoint(const Function& target_function, |
@@ -368,38 +465,18 @@ |
if (desc.TokenIndex(i) < token_index) { |
continue; |
} |
+ Breakpoint* bpt = GetBreakpoint(desc.PC(i)); |
+ if (bpt != NULL) { |
+ // Found existing breakpoint. |
+ return bpt; |
+ } |
PcDescriptors::Kind kind = desc.DescriptorKind(i); |
- Breakpoint* bpt = NULL; |
- if (kind == PcDescriptors::kIcCall) { |
- bpt = GetBreakpoint(desc.PC(i)); |
- if (bpt != NULL) { |
- // There is an existing breakpoint at this token position. |
- break; |
- } |
+ if ((kind == PcDescriptors::kIcCall) || |
+ (kind == PcDescriptors::kFuncCall) || |
+ (kind == PcDescriptors::kReturn)) { |
bpt = new Breakpoint(target_function, i); |
- String& func_name = String::Handle(); |
- int num_args, num_named_args; |
- CodePatcher::GetInstanceCallAt(desc.PC(i), |
- &func_name, &num_args, &num_named_args, &bpt->saved_bytes_); |
- CodePatcher::PatchInstanceCallAt( |
- desc.PC(i), StubCode::BreakpointDynamicEntryPoint()); |
+ bpt->PatchCode(); |
RegisterBreakpoint(bpt); |
- } else if (kind == PcDescriptors::kOther) { |
- if ((desc.TokenIndex(i) > 0) && CodePatcher::IsDartCall(desc.PC(i))) { |
- bpt = GetBreakpoint(desc.PC(i)); |
- if (bpt != NULL) { |
- // There is an existing breakpoint at this token position. |
- break; |
- } |
- bpt = new Breakpoint(target_function, i); |
- Function& func = Function::Handle(); |
- CodePatcher::GetStaticCallAt(desc.PC(i), &func, &bpt->saved_bytes_); |
- CodePatcher::PatchStaticCallAt( |
- desc.PC(i), StubCode::BreakpointStaticEntryPoint()); |
- RegisterBreakpoint(bpt); |
- } |
- } |
- if (bpt != NULL) { |
if (verbose) { |
OS::Print("Setting breakpoint at '%s' line %d (PC %p)\n", |
String::Handle(bpt->SourceUrl()).ToCString(), |
@@ -414,18 +491,7 @@ |
void Debugger::UnsetBreakpoint(Breakpoint* bpt) { |
- const Function& func = Function::Handle(bpt->function()); |
- const Code& code = Code::Handle(func.code()); |
- PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
- intptr_t desc_index = bpt->pc_desc_index(); |
- ASSERT(desc_index < desc.Length()); |
- ASSERT(bpt->pc() == desc.PC(desc_index)); |
- PcDescriptors::Kind kind = desc.DescriptorKind(desc_index); |
- if (kind == PcDescriptors::kIcCall) { |
- CodePatcher::PatchInstanceCallAt(desc.PC(desc_index), bpt->saved_bytes_); |
- } else { |
- CodePatcher::PatchStaticCallAt(desc.PC(desc_index), bpt->saved_bytes_); |
- } |
+ bpt->SetActive(false); |
} |
@@ -610,7 +676,8 @@ |
Breakpoint* bpt = GetBreakpoint(frame->pc()); |
ASSERT(bpt != NULL); |
if (verbose) { |
- OS::Print(">>> Breakpoint at %s:%d (Address %p)\n", |
+ OS::Print(">>> %s breakpoint at %s:%d (Address %p)\n", |
+ bpt->is_temporary() ? "hit temp" : "hit user", |
bpt ? String::Handle(bpt->SourceUrl()).ToCString() : "?", |
bpt ? bpt->LineNumber() : 0, |
frame->pc()); |
@@ -620,14 +687,67 @@ |
ASSERT(frame->IsValid()); |
ASSERT(frame->IsDartFrame()); |
ActivationFrame* activation = |
- new ActivationFrame(frame->pc(), frame->fp()); |
+ new ActivationFrame(frame->pc(), frame->fp(), frame->sp()); |
stack_trace->AddActivation(activation); |
frame = iterator.NextFrame(); |
} |
+ resume_action_ = kContinue; |
if (bp_handler_ != NULL) { |
(*bp_handler_)(bpt, stack_trace); |
} |
+ |
+ if (resume_action_ == kContinue) { |
+ RemoveTemporaryBreakpoints(); |
+ } else if (resume_action_ == kStepOver) { |
+ Function& func = Function::Handle(bpt->function()); |
+ if (bpt->breakpoint_kind_ == PcDescriptors::kReturn) { |
+ // If we are at the function return, do a StepOut action. |
+ if (stack_trace->Length() > 1) { |
+ ActivationFrame* caller = stack_trace->ActivationFrameAt(1); |
+ func = caller->DartFunction().raw(); |
+ RemoveTemporaryBreakpoints(); |
+ } |
+ } |
+ InstrumentForStepping(func); |
+ } else if (resume_action_ == kStepInto) { |
+ RemoveTemporaryBreakpoints(); |
+ if (bpt->breakpoint_kind_ == PcDescriptors::kIcCall) { |
+ int num_args, num_named_args; |
+ uword target; |
+ CodePatcher::GetInstanceCallAt(bpt->pc_, NULL, |
+ &num_args, &num_named_args, &target); |
+ ActivationFrame* top_frame = stack_trace->ActivationFrameAt(0); |
+ Instance& receiver = Instance::Handle( |
+ top_frame->GetInstanceCallReceiver(num_args)); |
+ Code& code = Code::Handle( |
+ ResolveCompileInstanceCallTarget(isolate_, receiver)); |
+ if (!code.IsNull()) { |
+ Function& callee = Function::Handle(code.function()); |
+ InstrumentForStepping(callee); |
+ } |
+ } else if (bpt->breakpoint_kind_ == PcDescriptors::kFuncCall) { |
+ Function& callee = Function::Handle(); |
+ uword target; |
+ CodePatcher::GetStaticCallAt(bpt->pc_, &callee, &target); |
+ InstrumentForStepping(callee); |
+ } else { |
+ ASSERT(bpt->breakpoint_kind_ == PcDescriptors::kReturn); |
+ // Treat like stepping out to caller. |
+ if (stack_trace->Length() > 1) { |
+ ActivationFrame* caller = stack_trace->ActivationFrameAt(1); |
+ InstrumentForStepping(caller->DartFunction()); |
+ } |
+ } |
+ } else { |
+ ASSERT(resume_action_ == kStepOut); |
+ // Set temporary breakpoints in the caller. |
+ RemoveTemporaryBreakpoints(); |
+ if (stack_trace->Length() > 1) { |
+ ActivationFrame* caller = stack_trace->ActivationFrameAt(1); |
+ InstrumentForStepping(caller->DartFunction()); |
+ } |
+ } |
} |
@@ -675,6 +795,28 @@ |
} |
+void Debugger::RemoveTemporaryBreakpoints() { |
+ Breakpoint* prev_bpt = NULL; |
+ Breakpoint* curr_bpt = breakpoints_; |
+ while (curr_bpt != NULL) { |
+ if (curr_bpt->is_temporary()) { |
+ if (prev_bpt == NULL) { |
+ breakpoints_ = breakpoints_->next(); |
+ } else { |
+ prev_bpt->set_next(curr_bpt->next()); |
+ } |
+ Breakpoint* temp_bpt = curr_bpt; |
+ curr_bpt = curr_bpt->next(); |
+ UnsetBreakpoint(temp_bpt); |
+ delete temp_bpt; |
+ } else { |
+ prev_bpt = curr_bpt; |
+ curr_bpt = curr_bpt->next(); |
+ } |
+ } |
+} |
+ |
+ |
Breakpoint* Debugger::GetBreakpointByFunction(const Function& func, |
intptr_t token_index) { |
Breakpoint* bpt = this->breakpoints_; |