diff options
author | Jim Ingham <jingham@apple.com> | 2011-02-08 05:20:59 +0000 |
---|---|---|
committer | Jim Ingham <jingham@apple.com> | 2011-02-08 05:20:59 +0000 |
commit | 0f16e73a76503fb252f36b90c2e1a7901ab6a9a4 (patch) | |
tree | 07ebe5ee9d4bc518a0b77af967d572b3aa1a4f57 | |
parent | 87e5ff02a1248cf18c8ca7430df0ab7d55643e00 (diff) | |
download | bcm5719-llvm-0f16e73a76503fb252f36b90c2e1a7901ab6a9a4.tar.gz bcm5719-llvm-0f16e73a76503fb252f36b90c2e1a7901ab6a9a4.zip |
Rework the RunThreadPlan event handling to use Event Hijacking not stopping the event thread. Also clarify the logic of the function.
llvm-svn: 125083
-rw-r--r-- | lldb/include/lldb/Target/Process.h | 25 | ||||
-rw-r--r-- | lldb/include/lldb/Target/Thread.h | 10 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp | 4 | ||||
-rw-r--r-- | lldb/source/Target/Process.cpp | 560 | ||||
-rw-r--r-- | lldb/source/Target/StopInfo.cpp | 12 | ||||
-rw-r--r-- | lldb/source/Target/Thread.cpp | 64 | ||||
-rw-r--r-- | lldb/source/Target/ThreadPlanBase.cpp | 9 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp | 8 |
8 files changed, 485 insertions, 207 deletions
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 009d265f75c..cd86f26bffe 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -1676,6 +1676,26 @@ public: PeekAtStateChangedEvents (); + class + ProcessEventHijacker + { + public: + ProcessEventHijacker (Process &process, Listener *listener) : + m_process (process), + m_listener (listener) + { + m_process.HijackProcessEvents (listener); + } + ~ProcessEventHijacker () + { + m_process.RestoreProcessEvents(); + } + + private: + Process &m_process; + Listener *m_listener; + }; + friend class ProcessEventHijacker; //------------------------------------------------------------------ /// If you need to ensure that you and only you will hear about some public /// event, then make a new listener, set to listen to process events, and @@ -1892,6 +1912,11 @@ protected: DISALLOW_COPY_AND_ASSIGN (MemoryCache); }; + bool + HijackPrivateProcessEvents (Listener *listener); + + void + RestorePrivateProcessEvents (); //------------------------------------------------------------------ // Member variables diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index 31cf439fcb8..a573c174cad 100644 --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -553,6 +553,16 @@ public: ThreadPlan * GetCurrentPlan (); +private: + bool + PlanIsBasePlan (ThreadPlan *plan_ptr) + { + if (m_plan_stack.size() == 0) + return false; + return m_plan_stack[0].get() == plan_ptr; + } +public: + //------------------------------------------------------------------ /// Gets the inner-most plan that was popped off the plan stack in the /// most recent stop. Useful for printing the stop reason accurately. diff --git a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp index 9467801500e..378b6347d21 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp @@ -13,6 +13,7 @@ #include "lldb/Core/ArchSpec.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/StreamString.h" +#include "lldb/Core/State.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" @@ -88,6 +89,9 @@ ThreadGDBRemote::WillResume (StateType resume_state) Thread::WillResume(resume_state); int signo = GetResumeSignal(); + lldb::LogSP log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + log->Printf ("Resuming thread: %4.4x with state: %s.", GetID(), StateAsCString(resume_state)); switch (resume_state) { diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 9b5718a0adf..9d58fb06346 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -393,6 +393,23 @@ Process::RestoreProcessEvents () RestoreBroadcaster(); } +bool +Process::HijackPrivateProcessEvents (Listener *listener) +{ + if (listener != NULL) + { + return m_private_state_broadcaster.HijackBroadcaster(listener, eBroadcastBitStateChanged); + } + else + return false; +} + +void +Process::RestorePrivateProcessEvents () +{ + m_private_state_broadcaster.RestoreBroadcaster(); +} + StateType Process::WaitForStateChangedEvents (const TimeValue *timeout, EventSP &event_sp) { @@ -1827,7 +1844,8 @@ Process::Halt () { // Pause our private state thread so we can ensure no one else eats // the stop event out from under us. - PausePrivateStateThread(); + Listener halt_listener ("lldb.process.halt_listener"); + HijackPrivateProcessEvents(&halt_listener); EventSP event_sp; Error error (WillHalt()); @@ -1856,16 +1874,17 @@ Process::Halt () // a halt command. if (caused_stop) { - // Wait for 2 seconds for the process to stop. + // Wait for 1 second for the process to stop. TimeValue timeout_time; timeout_time = TimeValue::Now(); timeout_time.OffsetWithSeconds(1); - StateType state = WaitForStateChangedEventsPrivate (&timeout_time, event_sp); + bool got_event = halt_listener.WaitForEvent (&timeout_time, event_sp); + StateType state = ProcessEventData::GetStateFromEvent(event_sp.get()); - if (state == eStateInvalid) + if (!got_event || state == eStateInvalid) { // We timeout out and didn't get a stop event... - error.SetErrorString ("Halt timed out."); + error.SetErrorStringWithFormat ("Halt timed out. State = %s", StateAsCString(GetState())); } else { @@ -1890,7 +1909,7 @@ Process::Halt () } } // Resume our private state thread before we post the event (if any) - ResumePrivateStateThread(); + RestorePrivateProcessEvents(); // Post any event we might have consumed. If all goes well, we will have // stopped the process, intercepted the event and set the interrupted @@ -2728,8 +2747,7 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, if (m_private_state.GetValue() != eStateStopped) { errors.Printf ("RunThreadPlan called while the private state was not stopped."); - // REMOVE BEAR TRAP... - // abort(); + return lldb::eExecutionSetupError; } // Save this value for restoration of the execution context after we run @@ -2755,73 +2773,190 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, exe_ctx.thread->QueueThreadPlan(thread_plan_sp, true); Listener listener("lldb.process.listener.run-thread-plan"); - exe_ctx.process->HijackProcessEvents(&listener); + + // This process event hijacker Hijacks the Public events and its destructor makes sure that the process events get + // restored on exit to the function. + + ProcessEventHijacker run_thread_plan_hijacker (*this, &listener); lldb::LogSP log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS)); if (log) { StreamString s; thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); - log->Printf ("Process::RunThreadPlan(): Resuming thread %u - 0x%4.4x to run thread plan \"%s\".", exe_ctx.thread->GetIndexID(), exe_ctx.thread->GetID(), s.GetData()); + log->Printf ("Process::RunThreadPlan(): Resuming thread %u - 0x%4.4x to run thread plan \"%s\".", + exe_ctx.thread->GetIndexID(), + exe_ctx.thread->GetID(), + s.GetData()); } - Error resume_error = exe_ctx.process->Resume (); - if (!resume_error.Success()) - { - errors.Printf("Error resuming inferior: \"%s\".\n", resume_error.AsCString()); - exe_ctx.process->RestoreProcessEvents(); - return lldb::eExecutionSetupError; - } - - // We need to call the function synchronously, so spin waiting for it to return. - // If we get interrupted while executing, we're going to lose our context, and - // won't be able to gather the result at this point. - // We set the timeout AFTER the resume, since the resume takes some time and we - // don't want to charge that to the timeout. + bool got_event; + lldb::EventSP event_sp; + lldb::StateType stop_state = lldb::eStateInvalid; TimeValue* timeout_ptr = NULL; TimeValue real_timeout; - if (single_thread_timeout_usec != 0) - { - real_timeout = TimeValue::Now(); - real_timeout.OffsetWithMicroSeconds(single_thread_timeout_usec); - timeout_ptr = &real_timeout; - } + bool first_timeout = true; + bool do_resume = true; while (1) { - lldb::EventSP event_sp; - lldb::StateType stop_state = lldb::eStateInvalid; + // We usually want to resume the process if we get to the top of the loop. + // The only exception is if we get two running events with no intervening + // stop, which can happen, we will just wait for then next stop event. + + if (do_resume) + { + // Do the initial resume and wait for the running event before going further. + + Error resume_error = exe_ctx.process->Resume (); + if (!resume_error.Success()) + { + errors.Printf("Error resuming inferior: \"%s\".\n", resume_error.AsCString()); + return_value = lldb::eExecutionSetupError; + break; + } + + real_timeout = TimeValue::Now(); + real_timeout.OffsetWithMicroSeconds(500000); + timeout_ptr = &real_timeout; + + got_event = listener.WaitForEvent(NULL, event_sp); + if (!got_event) + { + if (log) + log->Printf("Didn't get any event after initial resume, exiting."); + + errors.Printf("Didn't get any event after initial resume, exiting."); + return_value = lldb::eExecutionSetupError; + break; + } + + stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + if (stop_state != eStateRunning) + { + if (log) + log->Printf("Didn't get running event after initial resume, got %s instead.", StateAsCString(stop_state)); + + errors.Printf("Didn't get running event after initial resume, got %s instead.", StateAsCString(stop_state)); + return_value = lldb::eExecutionSetupError; + break; + } + + if (log) + log->Printf ("Resuming succeeded."); + // We need to call the function synchronously, so spin waiting for it to return. + // If we get interrupted while executing, we're going to lose our context, and + // won't be able to gather the result at this point. + // We set the timeout AFTER the resume, since the resume takes some time and we + // don't want to charge that to the timeout. + + if (single_thread_timeout_usec != 0) + { + real_timeout = TimeValue::Now(); + if (first_timeout) + real_timeout.OffsetWithMicroSeconds(single_thread_timeout_usec); + else + real_timeout.OffsetWithSeconds(10); + + timeout_ptr = &real_timeout; + } + } + else + { + if (log) + log->Printf ("Handled an extra running event."); + do_resume = true; + } + // Now wait for the process to stop again: - bool got_event = listener.WaitForEvent (timeout_ptr, event_sp); + stop_state = lldb::eStateInvalid; + event_sp.reset(); + got_event = listener.WaitForEvent (timeout_ptr, event_sp); - if (!got_event) + if (got_event) { - // Right now this is the only way to tell we've timed out... - // We should interrupt the process here... + if (event_sp.get()) + { + bool keep_going = false; + stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + if (log) + log->Printf("In while loop, got event: %s.", StateAsCString(stop_state)); + + switch (stop_state) + { + case lldb::eStateStopped: + // Yay, we're done. + if (log) + log->Printf ("Execution completed successfully."); + return_value = lldb::eExecutionCompleted; + break; + case lldb::eStateCrashed: + if (log) + log->Printf ("Execution crashed."); + return_value = lldb::eExecutionInterrupted; + break; + case lldb::eStateRunning: + do_resume = false; + keep_going = true; + break; + default: + if (log) + log->Printf("Execution stopped with unexpected state: %s.", StateAsCString(stop_state)); + return_value = lldb::eExecutionInterrupted; + break; + } + if (keep_going) + continue; + else + break; + } + else + { + if (log) + log->Printf ("got_event was true, but the event pointer was null. How odd..."); + return_value = lldb::eExecutionInterrupted; + break; + } + } + else + { + // If we didn't get an event that means we've timed out... + // We will interrupt the process here. Depending on what we were asked to do we will + // either exit, or try with all threads running for the same timeout. // Not really sure what to do if Halt fails here... + if (log) { if (try_all_threads) - log->Printf ("Process::RunThreadPlan(): Running function with timeout: %d timed out, trying with all threads enabled.", - single_thread_timeout_usec); + { + if (first_timeout) + log->Printf ("Process::RunThreadPlan(): Running function with timeout: %d timed out, " + "trying with all threads enabled.", + single_thread_timeout_usec); + else + log->Printf ("Process::RunThreadPlan(): Restarting function with all threads enabled " + "and timeout: %d timed out.", + single_thread_timeout_usec); + } else - log->Printf ("Process::RunThreadPlan(): Running function with timeout: %d timed out, abandoning execution.", + log->Printf ("Process::RunThreadPlan(): Running function with timeout: %d timed out, " + "halt and abandoning execution.", single_thread_timeout_usec); } Error halt_error = exe_ctx.process->Halt(); - if (halt_error.Success()) { - timeout_ptr = NULL; if (log) log->Printf ("Process::RunThreadPlan(): Halt succeeded."); - // Between the time that we got the timeout and the time we halted, but target - // might have actually completed the plan. If so, we're done. Note, I call WFE here with a short - // timeout to - got_event = listener.WaitForEvent(NULL, event_sp); + // If halt succeeds, it always produces a stopped event. Wait for that: + + real_timeout = TimeValue::Now(); + real_timeout.OffsetWithMicroSeconds(500000); + + got_event = listener.WaitForEvent(&real_timeout, event_sp); if (got_event) { @@ -2829,184 +2964,253 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, if (log) { log->Printf ("Process::RunThreadPlan(): Stopped with event: %s", StateAsCString(stop_state)); - if (stop_state == lldb::eStateStopped && Process::ProcessEventData::GetInterruptedFromEvent(event_sp.get())) + if (stop_state == lldb::eStateStopped + && Process::ProcessEventData::GetInterruptedFromEvent(event_sp.get())) log->Printf (" Event was the Halt interruption event."); } - if (exe_ctx.thread->IsThreadPlanDone (thread_plan_sp.get())) + if (stop_state == lldb::eStateStopped) { - if (log) - log->Printf ("Process::RunThreadPlan(): Even though we timed out, the call plan was done. Exiting wait loop."); - return_value = lldb::eExecutionCompleted; - break; - } + // Between the time we initiated the Halt and the time we delivered it, the process could have + // already finished its job. Check that here: + + if (exe_ctx.thread->IsThreadPlanDone (thread_plan_sp.get())) + { + if (log) + log->Printf ("Process::RunThreadPlan(): Even though we timed out, the call plan was done. " + "Exiting wait loop."); + return_value = lldb::eExecutionCompleted; + break; + } - if (try_all_threads - && (stop_state == lldb::eStateStopped && Process::ProcessEventData::GetInterruptedFromEvent (event_sp.get()))) - { + if (!try_all_threads) + { + if (log) + log->Printf ("try_all_threads was false, we stopped so now we're quitting."); + return_value = lldb::eExecutionInterrupted; + break; + } - thread_plan_sp->SetStopOthers (false); - if (log) - log->Printf ("Process::RunThreadPlan(): About to resume."); + if (first_timeout) + { + // Set all the other threads to run, and return to the top of the loop, which will continue; + first_timeout = false; + thread_plan_sp->SetStopOthers (false); + if (log) + log->Printf ("Process::RunThreadPlan(): About to resume."); - exe_ctx.process->Resume(); - continue; - } - else - { - exe_ctx.process->RestoreProcessEvents (); - return lldb::eExecutionInterrupted; + continue; + } + else + { + // Running all threads failed, so return Interrupted. + if (log) + log->Printf("Process::RunThreadPlan(): running all threads timed out."); + return_value = lldb::eExecutionInterrupted; + break; + } } } + else + { if (log) + log->Printf("Process::RunThreadPlan(): halt said it succeeded, but I got no event. " + "I'm getting out of here passing Interrupted."); + return_value = lldb::eExecutionInterrupted; + break; + } } else { - + // This branch is to work around some problems with gdb-remote's Halt. It is a little racy, and can return + // an error from halt, but if you wait a bit you'll get a stopped event anyway. if (log) - log->Printf ("Process::RunThreadPlan(): halt failed: error = \"%s\", I'm just going to wait a little longer and see if the world gets nicer to me.", - halt_error.AsCString()); -// abort(); - - if (single_thread_timeout_usec != 0) + log->Printf ("Process::RunThreadPlan(): halt failed: error = \"%s\", I'm just going to wait a little longer and see if I get a stopped event.", + halt_error.AsCString()); + real_timeout = TimeValue::Now(); + real_timeout.OffsetWithMicroSeconds(500000); + timeout_ptr = &real_timeout; + got_event = listener.WaitForEvent(&real_timeout, event_sp); + if (!got_event || event_sp.get() == NULL) { - real_timeout = TimeValue::Now(); - real_timeout.OffsetWithMicroSeconds(single_thread_timeout_usec); - timeout_ptr = &real_timeout; + // This is not going anywhere, bag out. + if (log) + log->Printf ("Process::RunThreadPlan(): halt failed: and waiting for the stopped event failed."); + return_value = lldb::eExecutionInterrupted; + break; + } + else + { + stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + if (log) + log->Printf ("Process::RunThreadPlan(): halt failed: but then I got a stopped event. Whatever..."); + if (stop_state == lldb::eStateStopped) + { + // Between the time we initiated the Halt and the time we delivered it, the process could have + // already finished its job. Check that here: + + if (exe_ctx.thread->IsThreadPlanDone (thread_plan_sp.get())) + { + if (log) + log->Printf ("Process::RunThreadPlan(): Even though we timed out, the call plan was done. " + "Exiting wait loop."); + return_value = lldb::eExecutionCompleted; + break; + } + + if (first_timeout) + { + // Set all the other threads to run, and return to the top of the loop, which will continue; + first_timeout = false; + thread_plan_sp->SetStopOthers (false); + if (log) + log->Printf ("Process::RunThreadPlan(): About to resume."); + + continue; + } + else + { + // Running all threads failed, so return Interrupted. + if (log) + log->Printf("Process::RunThreadPlan(): running all threads timed out."); + return_value = lldb::eExecutionInterrupted; + break; + } + } + else + { + log->Printf ("Process::RunThreadPlan(): halt failed, I waited and didn't get" + " a stopped event, instead got %s.", StateAsCString(stop_state)); + return_value = lldb::eExecutionInterrupted; + break; + } } - continue; } } - stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + } // END WAIT LOOP + + // Now do some processing on the results of the run: + if (return_value == eExecutionInterrupted) + { if (log) - log->Printf("Process::RunThreadPlan(): got event: %s.", StateAsCString(stop_state)); - - if (stop_state == lldb::eStateRunning || stop_state == lldb::eStateStepping) - continue; - - if (exe_ctx.thread->IsThreadPlanDone (thread_plan_sp.get())) { - if (log) - log->Printf("Process::RunThreadPlan(): thread plan is done"); - return_value = lldb::eExecutionCompleted; - break; - } - else if (exe_ctx.thread->WasThreadPlanDiscarded (thread_plan_sp.get())) - { - if (log) - log->Printf("Process::RunThreadPlan(): thread plan was discarded"); - return_value = lldb::eExecutionDiscarded; - break; - } - else - { - if (log) + StreamString s; + if (event_sp) + event_sp->Dump (&s); + else { - StreamString s; - if (event_sp) - event_sp->Dump (&s); - else + log->Printf ("Process::RunThreadPlan(): Stop event that interrupted us is NULL."); + } + + StreamString ts; + + const char *event_explanation; + + do + { + const Process::ProcessEventData *event_data = Process::ProcessEventData::GetEventDataFromEvent (event_sp.get()); + + if (!event_data) { - log->Printf ("Process::RunThreadPlan(): Stop event that interrupted us is NULL."); + event_explanation = "<no event data>"; + break; } + + Process *process = event_data->GetProcessSP().get(); - StreamString ts; - - const char *event_explanation; + if (!process) + { + event_explanation = "<no process>"; + break; + } + + ThreadList &thread_list = process->GetThreadList(); + + uint32_t num_threads = thread_list.GetSize(); + uint32_t thread_index; + + ts.Printf("<%u threads> ", num_threads); - do + for (thread_index = 0; + thread_index < num_threads; + ++thread_index) { - const Process::ProcessEventData *event_data = Process::ProcessEventData::GetEventDataFromEvent (event_sp.get()); - - if (!event_data) - { - event_explanation = "<no event data>"; - break; - } + Thread *thread = thread_list.GetThreadAtIndex(thread_index).get(); - Process *process = event_data->GetProcessSP().get(); - - if (!process) + if (!thread) { - event_explanation = "<no process>"; - break; + ts.Printf("<?> "); + continue; } - ThreadList &thread_list = process->GetThreadList(); - - uint32_t num_threads = thread_list.GetSize(); - uint32_t thread_index; - - ts.Printf("<%u threads> ", num_threads); + ts.Printf("<0x%4.4x ", thread->GetID()); + RegisterContext *register_context = thread->GetRegisterContext().get(); - for (thread_index = 0; - thread_index < num_threads; - ++thread_index) - { - Thread *thread = thread_list.GetThreadAtIndex(thread_index).get(); - - if (!thread) - { - ts.Printf("<?> "); - continue; - } - - ts.Printf("<0x%4.4x ", thread->GetID()); - RegisterContext *register_context = thread->GetRegisterContext().get(); - - if (register_context) - ts.Printf("[ip 0x%llx] ", register_context->GetPC()); - else - ts.Printf("[ip unknown] "); - - lldb::StopInfoSP stop_info_sp = thread->GetStopInfo(); - if (stop_info_sp) - { - const char *stop_desc = stop_info_sp->GetDescription(); - if (stop_desc) - ts.PutCString (stop_desc); - } - ts.Printf(">"); - } + if (register_context) + ts.Printf("[ip 0x%llx] ", register_context->GetPC()); + else + ts.Printf("[ip unknown] "); - event_explanation = ts.GetData(); - } while (0); - - // See if any of the threads that stopped think we ought to stop. Otherwise continue on. - if (!GetThreadList().ShouldStop(event_sp.get())) - { - if (log) - log->Printf("Process::RunThreadPlan(): execution interrupted, but nobody wanted to stop, so we continued: %s %s", - s.GetData(), event_explanation); - if (single_thread_timeout_usec != 0) + lldb::StopInfoSP stop_info_sp = thread->GetStopInfo(); + if (stop_info_sp) { - real_timeout = TimeValue::Now(); - real_timeout.OffsetWithMicroSeconds(single_thread_timeout_usec); - timeout_ptr = &real_timeout; + const char *stop_desc = stop_info_sp->GetDescription(); + if (stop_desc) + ts.PutCString (stop_desc); } - - continue; + ts.Printf(">"); } - else - { - if (log) - log->Printf("Process::RunThreadPlan(): execution interrupted: %s %s", s.GetData(), event_explanation); - } - } + + event_explanation = ts.GetData(); + } while (0); + if (log) + log->Printf("Process::RunThreadPlan(): execution interrupted: %s %s", s.GetData(), event_explanation); + if (discard_on_error && thread_plan_sp) { exe_ctx.thread->DiscardThreadPlansUpToPlan (thread_plan_sp); } - return_value = lldb::eExecutionInterrupted; - break; } } - - if (exe_ctx.process) - exe_ctx.process->RestoreProcessEvents (); + else if (return_value == eExecutionSetupError) + { + if (log) + log->Printf("Process::RunThreadPlan(): execution set up error."); + if (discard_on_error && thread_plan_sp) + { + exe_ctx.thread->DiscardThreadPlansUpToPlan (thread_plan_sp); + } + } + else + { + if (exe_ctx.thread->IsThreadPlanDone (thread_plan_sp.get())) + { + if (log) + log->Printf("Process::RunThreadPlan(): thread plan is done"); + return_value = lldb::eExecutionCompleted; + } + else if (exe_ctx.thread->WasThreadPlanDiscarded (thread_plan_sp.get())) + { + if (log) + log->Printf("Process::RunThreadPlan(): thread plan was discarded"); + return_value = lldb::eExecutionDiscarded; + } + else + { + if (log) + log->Printf("Process::RunThreadPlan(): thread plan stopped in mid course"); + if (discard_on_error && thread_plan_sp) + { + if (log) + log->Printf("Process::RunThreadPlan(): discarding thread plan 'cause discard_on_error is set."); + exe_ctx.thread->DiscardThreadPlansUpToPlan (thread_plan_sp); + } + } + } + // Thread we ran the function in may have gone away because we ran the target // Check that it's still there. exe_ctx.thread = exe_ctx.process->GetThreadList().FindThreadByIndexID(tid, true).get(); diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp index 05d7f3bad4f..33d9a3217f4 100644 --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -59,7 +59,8 @@ public: StopInfo (thread, break_id), m_description(), m_should_stop (false), - m_should_stop_is_valid (false) + m_should_stop_is_valid (false), + m_should_perform_action (true) { } @@ -67,7 +68,8 @@ public: StopInfo (thread, break_id), m_description(), m_should_stop (should_stop), - m_should_stop_is_valid (true) + m_should_stop_is_valid (true), + m_should_perform_action (true) { } @@ -115,6 +117,10 @@ public: virtual void PerformAction (Event *event_ptr) { + if (!m_should_perform_action) + return; + m_should_perform_action = false; + BreakpointSiteSP bp_site_sp (m_thread.GetProcess().GetBreakpointSiteList().FindByID (m_value)); if (bp_site_sp) { @@ -189,6 +195,8 @@ private: std::string m_description; bool m_should_stop; bool m_should_stop_is_valid; + bool m_should_perform_action; // Since we are trying to preserve the "state" of the system even if we run functions + // etc. behind the users backs, we need to make sure we only REALLY perform the action once. }; diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index de108acf708..af2de6beb72 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -259,38 +259,56 @@ Thread::ShouldStop (Event* event_ptr) if (current_plan->PlanExplainsStop()) { bool over_ride_stop = current_plan->ShouldAutoContinue(event_ptr); - while (1) + + // We're starting from the base plan, so just let it decide; + if (PlanIsBasePlan(current_plan)) + { + should_stop = current_plan->ShouldStop (event_ptr); + if (log) + log->Printf("Base plan says should stop: %d.", current_plan->GetName(), should_stop); + } + else { - should_stop = current_plan->ShouldStop(event_ptr); - if (current_plan->MischiefManaged()) + // Otherwise, don't let the base plan override what the other plans say to do, since + // presumably if there were other plans they would know what to do... + while (1) { - if (should_stop) - current_plan->WillStop(); - - // If a Master Plan wants to stop, and wants to stick on the stack, we let it. - // Otherwise, see if the plan's parent wants to stop. - - if (should_stop && current_plan->IsMasterPlan() && !current_plan->OkayToDiscard()) - { - PopPlan(); + if (PlanIsBasePlan(current_plan)) break; - } - else + + should_stop = current_plan->ShouldStop(event_ptr); + if (log) + log->Printf("Plan %s should stop: %d.", current_plan->GetName(), should_stop); + if (current_plan->MischiefManaged()) { + if (should_stop) + current_plan->WillStop(); - PopPlan(); + // If a Master Plan wants to stop, and wants to stick on the stack, we let it. + // Otherwise, see if the plan's parent wants to stop. - current_plan = GetCurrentPlan(); - if (current_plan == NULL) + if (should_stop && current_plan->IsMasterPlan() && !current_plan->OkayToDiscard()) { + PopPlan(); break; } - } + else + { - } - else - { - break; + PopPlan(); + + current_plan = GetCurrentPlan(); + if (current_plan == NULL) + { + break; + } + } + + } + else + { + break; + } } } if (over_ride_stop) @@ -476,7 +494,6 @@ Thread::GetCompletedPlan () bool Thread::IsThreadPlanDone (ThreadPlan *plan) { - ThreadPlanSP empty_plan_sp; if (!m_completed_plan_stack.empty()) { for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) @@ -491,7 +508,6 @@ Thread::IsThreadPlanDone (ThreadPlan *plan) bool Thread::WasThreadPlanDiscarded (ThreadPlan *plan) { - ThreadPlanSP empty_plan_sp; if (!m_discarded_plan_stack.empty()) { for (int i = m_discarded_plan_stack.size() - 1; i >= 0; i--) diff --git a/lldb/source/Target/ThreadPlanBase.cpp b/lldb/source/Target/ThreadPlanBase.cpp index b83306cb62a..7529991a7bd 100644 --- a/lldb/source/Target/ThreadPlanBase.cpp +++ b/lldb/source/Target/ThreadPlanBase.cpp @@ -18,6 +18,7 @@ #include "lldb/Breakpoint/BreakpointSite.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Core/Log.h" #include "lldb/Core/Stream.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" @@ -82,6 +83,8 @@ ThreadPlanBase::ShouldStop (Event *event_ptr) m_stop_vote = eVoteYes; m_run_vote = eVoteYes; + LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + StopInfoSP stop_info_sp = GetPrivateStopReason(); if (stop_info_sp) { @@ -101,6 +104,8 @@ ThreadPlanBase::ShouldStop (Event *event_ptr) // If we are going to stop for a breakpoint, then unship the other plans // at this point. Don't force the discard, however, so Master plans can stay // in place if they want to. + if (log) + log->Printf("Base plan discarding thread plans for thread tid = 0x%4.4x (breakpoint hit.)", m_thread.GetID()); m_thread.DiscardThreadPlans(false); return true; } @@ -127,12 +132,16 @@ ThreadPlanBase::ShouldStop (Event *event_ptr) case eStopReasonException: // If we crashed, discard thread plans and stop. Don't force the discard, however, // since on rerun the target may clean up this exception and continue normally from there. + if (log) + log->Printf("Base plan discarding thread plans for thread tid = 0x%4.4x (exception.)", m_thread.GetID()); m_thread.DiscardThreadPlans(false); return true; case eStopReasonSignal: if (stop_info_sp->ShouldStop(event_ptr)) { + if (log) + log->Printf("Base plan discarding thread plans for thread tid = 0x%4.4x (signal.)", m_thread.GetID()); m_thread.DiscardThreadPlans(false); return true; } diff --git a/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp b/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp index 27c0016c4fe..c08d92034f2 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp @@ -252,13 +252,15 @@ MachThreadList::UpdateThreadList(MachProcess *process, bool update, MachThreadLi thread_sp.reset(new MachThread(process, tid)); // Add the new thread regardless of its is user ready state... - if (new_threads) - new_threads->push_back(thread_sp); - // Make sure the thread is ready to be displayed and shown to users // before we add this thread to our list... if (thread_sp->IsUserReady()) + { + if (new_threads) + new_threads->push_back(thread_sp); + currThreads.push_back(thread_sp); + } } } |