Index: runtime/vm/debugger.cc |
=================================================================== |
--- runtime/vm/debugger.cc (revision 24627) |
+++ runtime/vm/debugger.cc (working copy) |
@@ -773,6 +773,7 @@ |
code_breakpoints_(NULL), |
resume_action_(kContinue), |
ignore_breakpoints_(false), |
+ in_event_notification_(false), |
exc_pause_info_(kNoPauseOnExceptions) { |
} |
@@ -780,6 +781,7 @@ |
Debugger::~Debugger() { |
PortMap::ClosePort(isolate_id_); |
isolate_id_ = ILLEGAL_ISOLATE_ID; |
+ ASSERT(!in_event_notification_); |
ASSERT(src_breakpoints_ == NULL); |
ASSERT(code_breakpoints_ == NULL); |
ASSERT(stack_trace_ == NULL); |
@@ -815,7 +817,21 @@ |
return Function::null(); |
} |
+void Debugger::SetSingleStep() { |
+ isolate_->set_single_step(true); |
+ resume_action_ = kSingleStep; |
+} |
+void Debugger::SetStepOver() { |
+ isolate_->set_single_step(false); |
+ resume_action_ = kStepOver; |
+} |
+ |
+void Debugger::SetStepOut() { |
+ isolate_->set_single_step(false); |
+ resume_action_ = kStepOut; |
+} |
+ |
RawFunction* Debugger::ResolveFunction(const Library& library, |
const String& class_name, |
const String& function_name) { |
@@ -869,6 +885,10 @@ |
void Debugger::InstrumentForStepping(const Function& target_function) { |
+ if (target_function.is_native()) { |
+ // Can't instrument native functions. |
+ return; |
+ } |
if (!target_function.HasCode()) { |
Compiler::CompileFunction(target_function); |
// If there were any errors, ignore them silently and return without |
@@ -966,6 +986,24 @@ |
} |
+ActivationFrame* Debugger::TopDartFrame() const { |
+ StackFrameIterator iterator(false); |
+ StackFrame* frame = iterator.NextFrame(); |
+ while ((frame != NULL) && !frame->IsDartFrame()) { |
+ frame = iterator.NextFrame(); |
+ } |
+ Code& code = Code::Handle(isolate_, frame->LookupDartCode()); |
+ ActivationFrame* activation = |
+ new ActivationFrame(frame->pc(), frame->fp(), frame->sp(), code); |
+ return activation; |
+} |
+ |
+ |
+DebuggerStackTrace* Debugger::StackTrace() { |
+ return (stack_trace_ != NULL) ? stack_trace_ : CollectStackTrace(); |
+} |
+ |
+ |
void Debugger::SetExceptionPauseInfo(Dart_ExceptionPauseInfo pause_info) { |
ASSERT((pause_info == kNoPauseOnExceptions) || |
(pause_info == kPauseOnUnhandledExceptions) || |
@@ -1007,7 +1045,7 @@ |
// breakpoint or exception event, or if the debugger is not |
// interested in exception events. |
if (ignore_breakpoints_ || |
- (stack_trace_ != NULL) || |
+ in_event_notification_ || |
(event_handler_ == NULL) || |
(exc_pause_info_ == kNoPauseOnExceptions)) { |
return; |
@@ -1023,7 +1061,6 @@ |
DebuggerEvent event; |
event.type = kExceptionThrown; |
event.exception = &exc; |
- ASSERT(event_handler_ != NULL); |
(*event_handler_)(&event); |
stack_trace_ = NULL; |
obj_cache_ = NULL; // Remote object cache is zone allocated. |
@@ -1506,11 +1543,69 @@ |
} |
+void Debugger::SignalPausedEvent(ActivationFrame* top_frame) { |
+ resume_action_ = kContinue; |
+ isolate_->set_single_step(false); |
+ ASSERT(!in_event_notification_); |
+ ASSERT(obj_cache_ == NULL); |
+ in_event_notification_ = true; |
+ obj_cache_ = new RemoteObjectCache(64); |
+ DebuggerEvent event; |
+ event.type = kBreakpointReached; |
+ event.top_frame = top_frame; |
+ (*event_handler_)(&event); |
+ in_event_notification_ = false; |
+ obj_cache_ = NULL; // Remote object cache is zone allocated. |
+} |
+ |
+ |
+void Debugger::SingleStepCallback() { |
+ ASSERT(resume_action_ == kSingleStep); |
+ ASSERT(isolate_->single_step()); |
+ // We can't get here unless the debugger event handler enabled |
+ // single stepping. |
+ ASSERT(event_handler_ != NULL); |
+ // Don't pause recursively. |
+ if (in_event_notification_) return; |
+ |
+ // Check whether we are in a Dart function that the user is |
+ // interested in. |
+ ActivationFrame* frame = TopDartFrame(); |
+ ASSERT(frame != NULL); |
+ const Function& func = frame->function(); |
+ if (!IsDebuggable(func)) { |
+ return; |
+ } |
+ |
+ if (FLAG_verbose_debug) { |
+ OS::Print(">>> single step break at %s:%"Pd" (func %s token %"Pd")\n", |
+ String::Handle(frame->SourceUrl()).ToCString(), |
+ frame->LineNumber(), |
+ String::Handle(frame->QualifiedFunctionName()).ToCString(), |
+ frame->TokenPos()); |
+ } |
+ |
+ stack_trace_ = CollectStackTrace(); |
+ SignalPausedEvent(frame); |
+ |
+ RemoveInternalBreakpoints(); |
+ if (resume_action_ == kStepOver) { |
+ InstrumentForStepping(func); |
+ } else if (resume_action_ == kStepOut) { |
+ if (stack_trace_->Length() > 1) { |
+ ActivationFrame* caller_frame = stack_trace_->ActivationFrameAt(1); |
+ InstrumentForStepping(caller_frame->function()); |
+ } |
+ } |
+ stack_trace_ = NULL; |
+} |
+ |
+ |
void Debugger::SignalBpReached() { |
// We ignore this breakpoint when the VM is executing code invoked |
// by the debugger to evaluate variables values, or when we see a nested |
// breakpoint or exception event. |
- if (ignore_breakpoints_ || (stack_trace_ != NULL)) { |
+ if (ignore_breakpoints_ || in_event_notification_) { |
return; |
} |
DebuggerStackTrace* stack_trace = CollectStackTrace(); |
@@ -1519,9 +1614,15 @@ |
ASSERT(top_frame != NULL); |
CodeBreakpoint* bpt = GetCodeBreakpoint(top_frame->pc()); |
ASSERT(bpt != NULL); |
+ |
+ bool report_bp = true; |
+ if (bpt->IsInternal() && !IsDebuggable(top_frame->function())) { |
+ report_bp = false; |
+ } |
if (FLAG_verbose_debug) { |
- OS::Print(">>> hit %s breakpoint at %s:%"Pd" " |
+ OS::Print(">>> %s %s breakpoint at %s:%"Pd" " |
"(token %"Pd") (address %#"Px")\n", |
+ report_bp ? "hit" : "ignore", |
bpt->IsInternal() ? "internal" : "user", |
String::Handle(bpt->SourceUrl()).ToCString(), |
bpt->LineNumber(), |
@@ -1529,117 +1630,34 @@ |
top_frame->pc()); |
} |
- resume_action_ = kContinue; |
- if (event_handler_ != NULL) { |
- ASSERT(stack_trace_ == NULL); |
- ASSERT(obj_cache_ == NULL); |
- obj_cache_ = new RemoteObjectCache(64); |
+ if (report_bp && (event_handler_ != NULL)) { |
stack_trace_ = stack_trace; |
- DebuggerEvent event; |
- event.type = kBreakpointReached; |
- ASSERT(stack_trace->Length() > 0); |
- event.top_frame = stack_trace->ActivationFrameAt(0); |
- (*event_handler_)(&event); |
+ SignalPausedEvent(top_frame); |
stack_trace_ = NULL; |
- obj_cache_ = NULL; // Remote object cache is zone allocated. |
} |
- Function& currently_instrumented_func = Function::Handle(); |
- if (bpt->IsInternal()) { |
- currently_instrumented_func = bpt->function(); |
- } |
Function& func_to_instrument = Function::Handle(); |
- if (resume_action_ == kContinue) { |
- // Nothing to do here, any potential instrumentation will be removed |
- // below. |
- } else if (resume_action_ == kStepOver) { |
- func_to_instrument = bpt->function(); |
+ if (resume_action_ == kStepOver) { |
if (bpt->breakpoint_kind_ == PcDescriptors::kReturn) { |
- // If we are at the function return, do a StepOut action. |
- if (stack_trace->Length() > 1) { |
- ActivationFrame* caller_frame = stack_trace->ActivationFrameAt(1); |
- func_to_instrument = caller_frame->function().raw(); |
- } |
- } |
- } else if (resume_action_ == kStepInto) { |
- // If the call target is not debuggable, we treat StepInto like |
- // a StepOver, that is we instrument the current function. |
- if (bpt->breakpoint_kind_ == PcDescriptors::kIcCall) { |
+ // Step over return is converted into a single step so we break at |
+ // the caller. |
+ SetSingleStep(); |
+ } else { |
func_to_instrument = bpt->function(); |
- ICData& ic_data = ICData::Handle(); |
- const Code& code = |
- Code::Handle(Function::Handle(bpt->function_).unoptimized_code()); |
- CodePatcher::GetInstanceCallAt(bpt->pc_, code, &ic_data); |
- ArgumentsDescriptor |
- args_descriptor(Array::Handle(ic_data.arguments_descriptor())); |
- ActivationFrame* top_frame = stack_trace->ActivationFrameAt(0); |
- intptr_t num_args = args_descriptor.Count(); |
- Instance& receiver = |
- Instance::Handle(top_frame->GetInstanceCallReceiver(num_args)); |
- Code& target_code = |
- Code::Handle(ResolveCompileInstanceCallTarget(receiver, ic_data)); |
- if (!target_code.IsNull()) { |
- Function& callee = Function::Handle(target_code.function()); |
- if (IsDebuggable(callee)) { |
- func_to_instrument = callee.raw(); |
- } |
- } |
- } else if (bpt->breakpoint_kind_ == PcDescriptors::kUnoptStaticCall) { |
- func_to_instrument = bpt->function(); |
- const Code& code = Code::Handle(func_to_instrument.CurrentCode()); |
- ASSERT(!code.is_optimized()); |
- const Function& callee = Function::Handle( |
- CodePatcher::GetUnoptimizedStaticCallAt(bpt->pc_, code, NULL)); |
- ASSERT(!callee.IsNull()); |
- if (IsDebuggable(callee)) { |
- func_to_instrument = callee.raw(); |
- } |
- } else if (bpt->breakpoint_kind_ == PcDescriptors::kClosureCall) { |
- func_to_instrument = bpt->function(); |
- const Code& code = Code::Handle(func_to_instrument.CurrentCode()); |
- ArgumentsDescriptor args_desc(Array::Handle( |
- CodePatcher::GetClosureArgDescAt(bpt->pc_, code))); |
- ActivationFrame* top_frame = stack_trace->ActivationFrameAt(0); |
- const Object& receiver = |
- Object::Handle(top_frame->GetClosureObject(args_desc.Count())); |
- if (!receiver.IsNull()) { |
- // Verify that the class of receiver is a closure class |
- // by checking that signature_function() is not null. |
- const Class& receiver_class = Class::Handle(receiver.clazz()); |
- if (receiver_class.IsSignatureClass()) { |
- Function& closure_func = |
- Function::Handle(Closure::function(Instance::Cast(receiver))); |
- if (IsDebuggable(closure_func)) { |
- func_to_instrument = closure_func.raw(); |
- } |
- } |
- // If the receiver is not a closure, then the runtime will attempt |
- // to invoke the "call" method on the object if one exists. |
- // TODO(hausner): find call method and intrument it for stepping. |
- } |
- } else if (bpt->breakpoint_kind_ == PcDescriptors::kRuntimeCall) { |
- // This is just a call to the runtime, not Dart code. Stepping |
- // into not possible, just treat like StepOver. |
- func_to_instrument = bpt->function(); |
- } else { |
- ASSERT(bpt->breakpoint_kind_ == PcDescriptors::kReturn); |
- // Treat like stepping out to caller. |
- if (stack_trace->Length() > 1) { |
- ActivationFrame* caller_frame = stack_trace->ActivationFrameAt(1); |
- func_to_instrument = caller_frame->function().raw(); |
- } |
} |
- } else { |
- ASSERT(resume_action_ == kStepOut); |
- // Set stepping breakpoints in the caller. |
+ } else if (resume_action_ == kStepOut) { |
if (stack_trace->Length() > 1) { |
ActivationFrame* caller_frame = stack_trace->ActivationFrameAt(1); |
func_to_instrument = caller_frame->function().raw(); |
} |
+ } else { |
+ ASSERT((resume_action_ == kContinue) || (resume_action_ == kSingleStep)); |
+ // Nothing to do here. Any potential instrumentation will be removed |
+ // below. Single stepping is handled by the single step callback. |
} |
if (func_to_instrument.IsNull() || |
- (func_to_instrument.raw() != currently_instrumented_func.raw())) { |
+ (func_to_instrument.raw() != bpt->function())) { |
RemoveInternalBreakpoints(); // *bpt is now invalid. |
if (!func_to_instrument.IsNull()) { |
InstrumentForStepping(func_to_instrument); |