Index: runtime/vm/debugger.cc |
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc |
index 6d11f18cb0e70ec60874aadfde2c190163296789..64476a9afbd0f84740c3c5222f05f3801209bbe8 100644 |
--- a/runtime/vm/debugger.cc |
+++ b/runtime/vm/debugger.cc |
@@ -263,114 +263,51 @@ ActivationFrame::ActivationFrame( |
} |
-void DebuggerEvent::UpdateTimestamp() { |
- timestamp_ = OS::GetCurrentTimeMillis(); |
+bool Debugger::NeedsIsolateEvents() { |
+ return ((isolate_ != Dart::vm_isolate()) && |
+ !ServiceIsolate::IsServiceIsolateDescendant(isolate_) && |
+ ((event_handler_ != NULL) || Service::isolate_stream.enabled())); |
} |
-bool Debugger::HasAnyEventHandler() { |
- return ((event_handler_ != NULL) || |
- Service::isolate_stream.enabled() || |
+bool Debugger::NeedsDebugEvents() { |
+ ASSERT(isolate_ != Dart::vm_isolate() && |
+ !ServiceIsolate::IsServiceIsolateDescendant(isolate_)); |
+ return (FLAG_warn_on_pause_with_no_debugger || |
+ (event_handler_ != NULL) || |
Service::debug_stream.enabled()); |
} |
-bool Debugger::HasDebugEventHandler() { |
- return ((event_handler_ != NULL) || |
- Service::debug_stream.enabled()); |
-} |
- |
- |
-static bool ServiceNeedsDebuggerEvent(DebuggerEvent::EventType type) { |
- switch (type) { |
- case DebuggerEvent::kBreakpointResolved: |
- // kBreakpointResolved events are handled differently in the vm |
- // service, so suppress them here. |
- return false; |
- |
- case DebuggerEvent::kBreakpointReached: |
- case DebuggerEvent::kExceptionThrown: |
- case DebuggerEvent::kIsolateInterrupted: |
- return (Service::debug_stream.enabled() || |
- FLAG_warn_on_pause_with_no_debugger); |
+void Debugger::InvokeEventHandler(ServiceEvent* event) { |
+ ASSERT(!event->IsPause()); // For pause events, call Pause instead. |
+ Service::HandleEvent(event); |
- case DebuggerEvent::kIsolateCreated: |
- case DebuggerEvent::kIsolateShutdown: |
- return Service::isolate_stream.enabled(); |
- |
- default: |
- UNREACHABLE(); |
- return false; |
- } |
-} |
- |
- |
-void Debugger::InvokeEventHandler(DebuggerEvent* event) { |
- // Give the event to the Service first, as the debugger event handler |
- // may go into a message loop and the Service will not. |
- // |
- // kBreakpointResolved events are handled differently in the vm |
- // service, so suppress them here. |
- if (ServiceNeedsDebuggerEvent(event->type())) { |
- ServiceEvent service_event(event); |
- Service::HandleEvent(&service_event); |
- } |
- |
- { |
+ // Call the embedder's event handler, if it exists. |
+ if (event_handler_ != NULL) { |
TransitionVMToNative transition(Thread::Current()); |
- if ((FLAG_steal_breakpoints || (event_handler_ == NULL)) && |
- event->IsPauseEvent()) { |
- // We allow the embedder's default breakpoint handler to be overridden. |
- isolate_->PauseEventHandler(); |
- } else if (event_handler_ != NULL) { |
- (*event_handler_)(event); |
- } |
- } |
- |
- if (ServiceNeedsDebuggerEvent(event->type()) && event->IsPauseEvent()) { |
- // If we were paused, notify the service that we have resumed. |
- const Error& error = |
- Error::Handle(Thread::Current()->sticky_error()); |
- ASSERT(error.IsNull() || |
- error.IsUnwindError() || |
- error.IsUnhandledException()); |
- |
- // Only send a resume event when the isolate is not unwinding. |
- if (!error.IsUnwindError()) { |
- ServiceEvent service_event(event->isolate(), ServiceEvent::kResume); |
- service_event.set_top_frame(event->top_frame()); |
- Service::HandleEvent(&service_event); |
- } |
+ (*event_handler_)(event); |
} |
} |
-void Debugger::SignalIsolateEvent(DebuggerEvent::EventType type) { |
- if (HasAnyEventHandler()) { |
- DebuggerEvent event(isolate_, type); |
- ASSERT(event.isolate_id() != ILLEGAL_ISOLATE_ID); |
- if (type == DebuggerEvent::kIsolateInterrupted) { |
- DebuggerStackTrace* trace = CollectStackTrace(); |
- if (trace->Length() > 0) { |
- event.set_top_frame(trace->FrameAt(0)); |
- } |
- ASSERT(stack_trace_ == NULL); |
- stack_trace_ = trace; |
- resume_action_ = kContinue; |
- Pause(&event); |
- HandleSteppingRequest(trace); |
- stack_trace_ = NULL; |
- } else { |
- InvokeEventHandler(&event); |
- } |
+RawError* Debugger::PauseInterrupted() { |
+ if (ignore_breakpoints_ || IsPaused()) { |
+ // We don't let the isolate get interrupted if we are already |
+ // paused or ignoring breakpoints. |
+ return Error::null(); |
} |
-} |
- |
- |
-RawError* Debugger::SignalIsolateInterrupted() { |
- if (HasDebugEventHandler()) { |
- SignalIsolateEvent(DebuggerEvent::kIsolateInterrupted); |
+ ServiceEvent event(isolate_, ServiceEvent::kPauseInterrupted); |
+ DebuggerStackTrace* trace = CollectStackTrace(); |
+ if (trace->Length() > 0) { |
+ event.set_top_frame(trace->FrameAt(0)); |
} |
+ ASSERT(stack_trace_ == NULL); |
+ stack_trace_ = trace; |
+ resume_action_ = kContinue; |
+ Pause(&event); |
+ HandleSteppingRequest(trace); |
+ stack_trace_ = NULL; |
// If any error occurred while in the debug message loop, return it here. |
const Error& error = Error::Handle(Thread::Current()->sticky_error()); |
@@ -380,14 +317,14 @@ RawError* Debugger::SignalIsolateInterrupted() { |
} |
-// The vm service handles breakpoint notifications in a different way |
-// than the regular debugger breakpoint notifications. |
-static void SendServiceBreakpointEvent(ServiceEvent::EventKind kind, |
- Breakpoint* bpt) { |
- if (Service::debug_stream.enabled()) { |
- ServiceEvent service_event(Isolate::Current(), kind); |
- service_event.set_breakpoint(bpt); |
- Service::HandleEvent(&service_event); |
+void Debugger::SendBreakpointEvent(ServiceEvent::EventKind kind, |
+ Breakpoint* bpt) { |
+ if (NeedsDebugEvents()) { |
+ // TODO(turnidge): Currently we send single-shot breakpoint events |
+ // to the vm service. Do we want to change this? |
+ ServiceEvent event(isolate_, kind); |
+ event.set_breakpoint(bpt); |
+ InvokeEventHandler(&event); |
} |
} |
@@ -397,11 +334,7 @@ void BreakpointLocation::AddBreakpoint(Breakpoint* bpt, Debugger* dbg) { |
set_breakpoints(bpt); |
dbg->SyncBreakpointLocation(this); |
- |
- if (IsResolved()) { |
- dbg->SignalBpResolved(bpt); |
- } |
- SendServiceBreakpointEvent(ServiceEvent::kBreakpointAdded, bpt); |
+ dbg->SendBreakpointEvent(ServiceEvent::kBreakpointAdded, bpt); |
} |
@@ -1270,7 +1203,6 @@ Debugger::Debugger() |
: isolate_(NULL), |
isolate_id_(ILLEGAL_ISOLATE_ID), |
initialized_(false), |
- creation_message_sent_(false), |
next_id_(1), |
latent_locations_(NULL), |
breakpoint_locations_(NULL), |
@@ -1322,10 +1254,9 @@ void Debugger::Shutdown() { |
bpt->Disable(); |
delete bpt; |
} |
- // Signal isolate shutdown event, but only if we previously sent the creation |
- // event. |
- if (creation_message_sent_) { |
- SignalIsolateEvent(DebuggerEvent::kIsolateShutdown); |
+ if (NeedsIsolateEvents()) { |
+ ServiceEvent event(isolate_, ServiceEvent::kIsolateExit); |
+ InvokeEventHandler(&event); |
} |
} |
@@ -1451,15 +1382,6 @@ void Debugger::DeoptimizeWorld() { |
} |
-void Debugger::SignalBpResolved(Breakpoint* bpt) { |
- if (HasDebugEventHandler() && !bpt->IsSingleShot()) { |
- DebuggerEvent event(isolate_, DebuggerEvent::kBreakpointResolved); |
- event.set_breakpoint(bpt); |
- InvokeEventHandler(&event); |
- } |
-} |
- |
- |
ActivationFrame* Debugger::CollectDartFrame(Isolate* isolate, |
uword pc, |
StackFrame* frame, |
@@ -1667,7 +1589,7 @@ bool Debugger::ShouldPauseOnException(DebuggerStackTrace* stack_trace, |
} |
-void Debugger::SignalExceptionThrown(const Instance& exc) { |
+void Debugger::PauseException(const Instance& exc) { |
// We ignore this exception event when the VM is executing code invoked |
// by the debugger to evaluate variables values, when we see a nested |
// breakpoint or exception event, or if the debugger is not |
@@ -1681,7 +1603,7 @@ void Debugger::SignalExceptionThrown(const Instance& exc) { |
if (!ShouldPauseOnException(stack_trace, exc)) { |
return; |
} |
- DebuggerEvent event(isolate_, DebuggerEvent::kExceptionThrown); |
+ ServiceEvent event(isolate_, ServiceEvent::kPauseException); |
event.set_exception(&exc); |
if (stack_trace->Length() > 0) { |
event.set_top_frame(stack_trace->FrameAt(0)); |
@@ -2236,6 +2158,11 @@ Breakpoint* Debugger::BreakpointAtActivation(const Instance& closure) { |
Breakpoint* Debugger::SetBreakpointAtLine(const String& script_url, |
intptr_t line_number) { |
+ // Prevent future tests from calling this function in the wrong |
+ // execution state. If you hit this assert, consider using |
+ // Dart_SetBreakpoint instead. |
+ ASSERT(Thread::Current()->execution_state() == Thread::kThreadInVM); |
+ |
BreakpointLocation* loc = |
BreakpointLocationAtLineCol(script_url, line_number, -1 /* no column */); |
if (loc != NULL) { |
@@ -2248,6 +2175,11 @@ Breakpoint* Debugger::SetBreakpointAtLine(const String& script_url, |
Breakpoint* Debugger::SetBreakpointAtLineCol(const String& script_url, |
intptr_t line_number, |
intptr_t column_number) { |
+ // Prevent future tests from calling this function in the wrong |
+ // execution state. If you hit this assert, consider using |
+ // Dart_SetBreakpoint instead. |
+ ASSERT(Thread::Current()->execution_state() == Thread::kThreadInVM); |
+ |
BreakpointLocation* loc = BreakpointLocationAtLineCol(script_url, |
line_number, |
column_number); |
@@ -2558,23 +2490,52 @@ void Debugger::SetEventHandler(EventHandler* handler) { |
} |
-void Debugger::Pause(DebuggerEvent* event) { |
- ASSERT(!IsPaused()); // No recursive pausing. |
+void Debugger::Pause(ServiceEvent* event) { |
+ ASSERT(event->IsPause()); // Should call InvokeEventHandler instead. |
+ ASSERT(!ignore_breakpoints_); // We shouldn't get here when ignoring bpts. |
+ ASSERT(!IsPaused()); // No recursive pausing. |
ASSERT(obj_cache_ == NULL); |
pause_event_ = event; |
pause_event_->UpdateTimestamp(); |
obj_cache_ = new RemoteObjectCache(64); |
- // We are about to invoke the debuggers event handler. Disable interrupts |
- // for this thread while waiting for debug commands over the service protocol. |
+ // We are about to invoke the debugger's event handler. Disable |
+ // interrupts for this thread while waiting for debug commands over |
+ // the service protocol. |
{ |
Thread* thread = Thread::Current(); |
DisableThreadInterruptsScope dtis(thread); |
TimelineDurationScope tds(thread, |
Timeline::GetDebuggerStream(), |
"Debugger Pause"); |
- InvokeEventHandler(event); |
+ |
+ // Send the pause event. |
+ Service::HandleEvent(event); |
+ |
+ { |
+ TransitionVMToNative transition(Thread::Current()); |
+ if (FLAG_steal_breakpoints || (event_handler_ == NULL)) { |
+ // We allow the embedder's default breakpoint handler to be overridden. |
+ isolate_->PauseEventHandler(); |
+ } else if (event_handler_ != NULL) { |
+ (*event_handler_)(event); |
+ } |
+ } |
+ |
+ // Notify the service that we have resumed. |
+ const Error& error = |
+ Error::Handle(Thread::Current()->sticky_error()); |
+ ASSERT(error.IsNull() || |
+ error.IsUnwindError() || |
+ error.IsUnhandledException()); |
+ |
+ // Only send a resume event when the isolate is not unwinding. |
+ if (!error.IsUnwindError()) { |
+ ServiceEvent resume_event(event->isolate(), ServiceEvent::kResume); |
+ resume_event.set_top_frame(event->top_frame()); |
+ Service::HandleEvent(&resume_event); |
+ } |
} |
pause_event_ = NULL; |
@@ -2657,7 +2618,7 @@ void Debugger::SignalPausedEvent(ActivationFrame* top_frame, |
bpt = NULL; |
} |
- DebuggerEvent event(isolate_, DebuggerEvent::kBreakpointReached); |
+ ServiceEvent event(isolate_, ServiceEvent::kPauseBreakpoint); |
event.set_top_frame(top_frame); |
event.set_breakpoint(bpt); |
event.set_at_async_jump(IsAtAsyncJump(top_frame)); |
@@ -2684,7 +2645,7 @@ bool Debugger::IsAtAsyncJump(ActivationFrame* top_frame) { |
return false; |
} |
-RawError* Debugger::DebuggerStepCallback() { |
+RawError* Debugger::PauseStepping() { |
ASSERT(isolate_->single_step()); |
// Don't pause recursively. |
if (IsPaused()) { |
@@ -2741,7 +2702,7 @@ RawError* Debugger::DebuggerStepCallback() { |
ASSERT(stack_trace_ == NULL); |
stack_trace_ = CollectStackTrace(); |
// If this step callback is part of stepping over an await statement, |
- // we saved the synthetic async breakpoint in SignalBpReached. We report |
+ // we saved the synthetic async breakpoint in PauseBreakpoint. We report |
// that we are paused at that breakpoint and then delete it after continuing. |
SignalPausedEvent(frame, synthetic_async_breakpoint_); |
if (synthetic_async_breakpoint_ != NULL) { |
@@ -2758,7 +2719,7 @@ RawError* Debugger::DebuggerStepCallback() { |
} |
-RawError* Debugger::SignalBpReached() { |
+RawError* Debugger::PauseBreakpoint() { |
// 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. |
@@ -2878,7 +2839,7 @@ RawError* Debugger::SignalBpReached() { |
} |
-void Debugger::BreakHere(const String& msg) { |
+void Debugger::PauseDeveloper(const String& msg) { |
// 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. |
@@ -2886,7 +2847,7 @@ void Debugger::BreakHere(const String& msg) { |
return; |
} |
- if (!HasDebugEventHandler()) { |
+ if (!NeedsDebugEvents()) { |
OS::Print("Hit debugger!"); |
} |
@@ -2897,9 +2858,9 @@ void Debugger::BreakHere(const String& msg) { |
// TODO(johnmccutchan): Send |msg| to Observatory. |
- // We are in the native call to Debugger_breakHere or Debugger_breakHereIf, |
- // the developer gets a better experience by not seeing this call. To |
- // accomplish this, we continue execution until the call exits (step out). |
+ // We are in the native call to Developer_debugger. the developer |
+ // gets a better experience by not seeing this call. To accomplish |
+ // this, we continue execution until the call exits (step out). |
SetStepOut(); |
HandleSteppingRequest(stack_trace_); |
@@ -2914,19 +2875,17 @@ void Debugger::Initialize(Isolate* isolate) { |
isolate_ = isolate; |
// Use the isolate's control port as the isolate_id for debugging. |
- // This port will be used as a unique ID to represent the isolate in the |
- // debugger wire protocol messages. |
+ // This port will be used as a unique ID to represent the isolate in |
+ // the debugger embedder api. |
isolate_id_ = isolate_->main_port(); |
initialized_ = true; |
} |
void Debugger::NotifyIsolateCreated() { |
- // Signal isolate creation event. |
- if ((isolate_ != Dart::vm_isolate()) && |
- !ServiceIsolate::IsServiceIsolateDescendant(isolate_)) { |
- SignalIsolateEvent(DebuggerEvent::kIsolateCreated); |
- creation_message_sent_ = true; |
+ if (NeedsIsolateEvents()) { |
+ ServiceEvent event(isolate_, ServiceEvent::kIsolateStart); |
+ InvokeEventHandler(&event); |
} |
} |
@@ -3028,8 +2987,7 @@ void Debugger::NotifyCompilation(const Function& func) { |
requested_end_pos.ToCString(), |
loc->requested_column_number()); |
} |
- SignalBpResolved(bpt); |
- SendServiceBreakpointEvent(ServiceEvent::kBreakpointResolved, bpt); |
+ SendBreakpointEvent(ServiceEvent::kBreakpointResolved, bpt); |
bpt = bpt->next(); |
} |
} |
@@ -3218,12 +3176,10 @@ void Debugger::RemoveBreakpoint(intptr_t bp_id) { |
prev_bpt->set_next(curr_bpt->next()); |
} |
- SendServiceBreakpointEvent(ServiceEvent::kBreakpointRemoved, curr_bpt); |
+ SendBreakpointEvent(ServiceEvent::kBreakpointRemoved, curr_bpt); |
// Remove references from the current debugger pause event. |
- if (pause_event_ != NULL && |
- pause_event_->type() == DebuggerEvent::kBreakpointReached && |
- pause_event_->breakpoint() == curr_bpt) { |
+ if (pause_event_ != NULL && pause_event_->breakpoint() == curr_bpt) { |
pause_event_->set_breakpoint(NULL); |
} |
break; |