//===-- StopInfo.cpp ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/lldb-python.h" #include "lldb/Target/StopInfo.h" // C Includes // C++ Includes #include <string> // Other libraries and framework includes // Project includes #include "lldb/Core/Log.h" #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/StreamString.h" #include "lldb/Expression/ClangUserExpression.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/Process.h" #include "lldb/Target/UnixSignals.h" using namespace lldb; using namespace lldb_private; StopInfo::StopInfo (Thread &thread, uint64_t value) : m_thread_wp (thread.shared_from_this()), m_stop_id (thread.GetProcess()->GetStopID()), m_resume_id (thread.GetProcess()->GetResumeID()), m_value (value), m_override_should_notify (eLazyBoolCalculate), m_override_should_stop (eLazyBoolCalculate) { } bool StopInfo::IsValid () const { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) return thread_sp->GetProcess()->GetStopID() == m_stop_id; return false; } void StopInfo::MakeStopInfoValid () { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { m_stop_id = thread_sp->GetProcess()->GetStopID(); m_resume_id = thread_sp->GetProcess()->GetResumeID(); } } bool StopInfo::HasTargetRunSinceMe () { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { lldb::StateType ret_type = thread_sp->GetProcess()->GetPrivateState(); if (ret_type == eStateRunning) { return true; } else if (ret_type == eStateStopped) { // This is a little tricky. We want to count "run and stopped again before you could // ask this question as a "TRUE" answer to HasTargetRunSinceMe. But we don't want to // include any running of the target done for expressions. So we track both resumes, // and resumes caused by expressions, and check if there are any resumes NOT caused // by expressions. uint32_t curr_resume_id = thread_sp->GetProcess()->GetResumeID(); uint32_t last_user_expression_id = thread_sp->GetProcess()->GetLastUserExpressionResumeID (); if (curr_resume_id == m_resume_id) { return false; } else if (curr_resume_id > last_user_expression_id) { return true; } } } return false; } //---------------------------------------------------------------------- // StopInfoBreakpoint //---------------------------------------------------------------------- namespace lldb_private { class StopInfoBreakpoint : public StopInfo { public: StopInfoBreakpoint (Thread &thread, break_id_t break_id) : StopInfo (thread, break_id), m_description(), m_should_stop (false), m_should_stop_is_valid (false), m_should_perform_action (true), m_address (LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID), m_was_one_shot (false) { StoreBPInfo(); } StopInfoBreakpoint (Thread &thread, break_id_t break_id, bool should_stop) : StopInfo (thread, break_id), m_description(), m_should_stop (should_stop), m_should_stop_is_valid (true), m_should_perform_action (true), m_address (LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID), m_was_one_shot (false) { StoreBPInfo(); } void StoreBPInfo () { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value)); if (bp_site_sp) { if (bp_site_sp->GetNumberOfOwners() == 1) { BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(0); if (bp_loc_sp) { m_break_id = bp_loc_sp->GetBreakpoint().GetID(); m_was_one_shot = bp_loc_sp->GetBreakpoint().IsOneShot(); } } m_address = bp_site_sp->GetLoadAddress(); } } } virtual ~StopInfoBreakpoint () { } virtual StopReason GetStopReason () const { return eStopReasonBreakpoint; } virtual bool ShouldStopSynchronous (Event *event_ptr) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { if (!m_should_stop_is_valid) { // Only check once if we should stop at a breakpoint BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value)); if (bp_site_sp) { ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0)); StoppointCallbackContext context (event_ptr, exe_ctx, true); m_should_stop = bp_site_sp->ShouldStop (&context); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::%s could not find breakpoint site id: %" PRId64 "...", __FUNCTION__, m_value); m_should_stop = true; } m_should_stop_is_valid = true; } return m_should_stop; } return false; } virtual bool DoShouldNotify (Event *event_ptr) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value)); if (bp_site_sp) { bool all_internal = true; for (uint32_t i = 0; i < bp_site_sp->GetNumberOfOwners(); i++) { if (!bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint().IsInternal()) { all_internal = false; break; } } return all_internal == false; } } return true; } virtual const char * GetDescription () { if (m_description.empty()) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value)); if (bp_site_sp) { StreamString strm; // If we have just hit an internal breakpoint, and it has a kind description, print that instead of the // full breakpoint printing: if (bp_site_sp->IsInternal()) { size_t num_owners = bp_site_sp->GetNumberOfOwners(); for (size_t idx = 0; idx < num_owners; idx++) { const char *kind = bp_site_sp->GetOwnerAtIndex(idx)->GetBreakpoint().GetBreakpointKind(); if (kind != NULL) { m_description.assign (kind); return kind; } } } strm.Printf("breakpoint "); bp_site_sp->GetDescription(&strm, eDescriptionLevelBrief); m_description.swap (strm.GetString()); } else { StreamString strm; if (m_break_id != LLDB_INVALID_BREAK_ID) { BreakpointSP break_sp = thread_sp->GetProcess()->GetTarget().GetBreakpointByID(m_break_id); if (break_sp) { if (break_sp->IsInternal()) { const char *kind = break_sp->GetBreakpointKind(); if (kind) strm.Printf ("internal %s breakpoint(%d).", kind, m_break_id); else strm.Printf ("internal breakpoint(%d).", m_break_id); } else { strm.Printf ("breakpoint %d.", m_break_id); } } else { if (m_was_one_shot) strm.Printf ("one-shot breakpoint %d", m_break_id); else strm.Printf ("breakpoint %d which has been deleted.", m_break_id); } } else if (m_address == LLDB_INVALID_ADDRESS) strm.Printf("breakpoint site %" PRIi64 " which has been deleted - unknown address", m_value); else strm.Printf("breakpoint site %" PRIi64 " which has been deleted - was at 0x%" PRIx64, m_value, m_address); m_description.swap (strm.GetString()); } } } return m_description.c_str(); } protected: bool ShouldStop (Event *event_ptr) { // This just reports the work done by PerformAction or the synchronous stop. It should // only ever get called after they have had a chance to run. assert (m_should_stop_is_valid); return m_should_stop; } virtual void PerformAction (Event *event_ptr) { if (!m_should_perform_action) return; m_should_perform_action = false; ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); if (!thread_sp->IsValid()) { // This shouldn't ever happen, but just in case, don't do more harm. if (log) { log->Printf ("PerformAction got called with an invalid thread."); } m_should_stop = true; m_should_stop_is_valid = true; return; } BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value)); if (bp_site_sp) { size_t num_owners = bp_site_sp->GetNumberOfOwners(); if (num_owners == 0) { m_should_stop = true; } else { // We go through each location, and test first its condition. If the condition says to stop, // then we run the callback for that location. If that callback says to stop as well, then // we set m_should_stop to true; we are going to stop. // But we still want to give all the breakpoints whose conditions say we are going to stop a // chance to run their callbacks. // Of course if any callback restarts the target by putting "continue" in the callback, then // we're going to restart, without running the rest of the callbacks. And in this case we will // end up not stopping even if another location said we should stop. But that's better than not // running all the callbacks. m_should_stop = false; ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0)); Process *process = exe_ctx.GetProcessPtr(); if (process->GetModIDRef().IsLastResumeForUserExpression()) { // If we are in the middle of evaluating an expression, don't run asynchronous breakpoint commands or // expressions. That could lead to infinite recursion if the command or condition re-calls the function // with this breakpoint. // TODO: We can keep a list of the breakpoints we've seen while running expressions in the nested // PerformAction calls that can arise when the action runs a function that hits another breakpoint, // and only stop running commands when we see the same breakpoint hit a second time. m_should_stop_is_valid = true; if (log) log->Printf ("StopInfoBreakpoint::PerformAction - Hit a breakpoint while running an expression," " not running commands to avoid recursion."); bool ignoring_breakpoints = process->GetIgnoreBreakpointsInExpressions(); if (ignoring_breakpoints) { m_should_stop = false; // Internal breakpoints will always stop. for (size_t j = 0; j < num_owners; j++) { lldb::BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(j); if (bp_loc_sp->GetBreakpoint().IsInternal()) { m_should_stop = true; break; } } } else { m_should_stop = true; } if (log) log->Printf ("StopInfoBreakpoint::PerformAction - in expression, continuing: %s.", m_should_stop ? "true" : "false"); process->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf("Warning: hit breakpoint while " "running function, skipping commands and conditions to prevent recursion."); return; } StoppointCallbackContext context (event_ptr, exe_ctx, false); // Let's copy the breakpoint locations out of the site and store them in a local list. That way if // one of the breakpoint actions changes the site, then we won't be operating on a bad list. BreakpointLocationCollection site_locations; for (size_t j = 0; j < num_owners; j++) site_locations.Add(bp_site_sp->GetOwnerAtIndex(j)); for (size_t j = 0; j < num_owners; j++) { lldb::BreakpointLocationSP bp_loc_sp = site_locations.GetByIndex(j); // If another action disabled this breakpoint or its location, then don't run the actions. if (!bp_loc_sp->IsEnabled() || !bp_loc_sp->GetBreakpoint().IsEnabled()) continue; // The breakpoint site may have many locations associated with it, not all of them valid for // this thread. Skip the ones that aren't: if (!bp_loc_sp->ValidForThisThread(thread_sp.get())) continue; // First run the condition for the breakpoint. If that says we should stop, then we'll run // the callback for the breakpoint. If the callback says we shouldn't stop that will win. if (bp_loc_sp->GetConditionText() != NULL) { Error condition_error; bool condition_says_stop = bp_loc_sp->ConditionSaysStop(exe_ctx, condition_error); if (!condition_error.Success()) { Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger(); StreamSP error_sp = debugger.GetAsyncErrorStream (); error_sp->Printf ("Stopped due to an error evaluating condition of breakpoint "); bp_loc_sp->GetDescription (error_sp.get(), eDescriptionLevelBrief); error_sp->Printf (": \"%s\"", bp_loc_sp->GetConditionText()); error_sp->EOL(); const char *err_str = condition_error.AsCString("<Unknown Error>"); if (log) log->Printf("Error evaluating condition: \"%s\"\n", err_str); error_sp->PutCString (err_str); error_sp->EOL(); error_sp->Flush(); // If the condition fails to be parsed or run, we should stop. condition_says_stop = true; } else { if (!condition_says_stop) continue; } } bool callback_says_stop; // FIXME: For now the callbacks have to run in async mode - the first time we restart we need // to get out of there. So set it here. // When we figure out how to nest breakpoint hits then this will change. Debugger &debugger = thread_sp->CalculateTarget()->GetDebugger(); bool old_async = debugger.GetAsyncExecution(); debugger.SetAsyncExecution (true); callback_says_stop = bp_loc_sp->InvokeCallback (&context); debugger.SetAsyncExecution (old_async); if (callback_says_stop) m_should_stop = true; // If we are going to stop for this breakpoint, then remove the breakpoint. if (callback_says_stop && bp_loc_sp && bp_loc_sp->GetBreakpoint().IsOneShot()) { thread_sp->GetProcess()->GetTarget().RemoveBreakpointByID (bp_loc_sp->GetBreakpoint().GetID()); } // Also make sure that the callback hasn't continued the target. // If it did, when we'll set m_should_start to false and get out of here. if (HasTargetRunSinceMe ()) { m_should_stop = false; break; } } } // We've figured out what this stop wants to do, so mark it as valid so we don't compute it again. m_should_stop_is_valid = true; } else { m_should_stop = true; m_should_stop_is_valid = true; Log * log_process(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log_process) log_process->Printf ("Process::%s could not find breakpoint site id: %" PRId64 "...", __FUNCTION__, m_value); } if (log) log->Printf ("Process::%s returning from action with m_should_stop: %d.", __FUNCTION__, m_should_stop); } } 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. lldb::addr_t m_address; // We use this to capture the breakpoint site address when we create the StopInfo, // in case somebody deletes it between the time the StopInfo is made and the // description is asked for. lldb::break_id_t m_break_id; bool m_was_one_shot; }; //---------------------------------------------------------------------- // StopInfoWatchpoint //---------------------------------------------------------------------- class StopInfoWatchpoint : public StopInfo { public: // Make sure watchpoint is properly disabled and subsequently enabled while performing watchpoint actions. class WatchpointSentry { public: WatchpointSentry(Process *p, Watchpoint *w): process(p), watchpoint(w) { if (process && watchpoint) { const bool notify = false; watchpoint->TurnOnEphemeralMode(); process->DisableWatchpoint(watchpoint, notify); } } ~WatchpointSentry() { if (process && watchpoint) { if (!watchpoint->IsDisabledDuringEphemeralMode()) { const bool notify = false; process->EnableWatchpoint(watchpoint, notify); } watchpoint->TurnOffEphemeralMode(); } } private: Process *process; Watchpoint *watchpoint; }; StopInfoWatchpoint (Thread &thread, break_id_t watch_id) : StopInfo(thread, watch_id), m_description(), m_should_stop(false), m_should_stop_is_valid(false) { } virtual ~StopInfoWatchpoint () { } virtual StopReason GetStopReason () const { return eStopReasonWatchpoint; } virtual const char * GetDescription () { if (m_description.empty()) { StreamString strm; strm.Printf("watchpoint %" PRIi64, m_value); m_description.swap (strm.GetString()); } return m_description.c_str(); } protected: virtual bool ShouldStopSynchronous (Event *event_ptr) { // ShouldStop() method is idempotent and should not affect hit count. // See Process::RunPrivateStateThread()->Process()->HandlePrivateEvent() // -->Process()::ShouldBroadcastEvent()->ThreadList::ShouldStop()-> // Thread::ShouldStop()->ThreadPlanBase::ShouldStop()-> // StopInfoWatchpoint::ShouldStop() and // Event::DoOnRemoval()->Process::ProcessEventData::DoOnRemoval()-> // StopInfoWatchpoint::PerformAction(). if (m_should_stop_is_valid) return m_should_stop; ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { WatchpointSP wp_sp (thread_sp->CalculateTarget()->GetWatchpointList().FindByID(GetValue())); if (wp_sp) { // Check if we should stop at a watchpoint. ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0)); StoppointCallbackContext context (event_ptr, exe_ctx, true); m_should_stop = wp_sp->ShouldStop (&context); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::%s could not find watchpoint location id: %" PRId64 "...", __FUNCTION__, GetValue()); m_should_stop = true; } } m_should_stop_is_valid = true; return m_should_stop; } bool ShouldStop (Event *event_ptr) { // This just reports the work done by PerformAction or the synchronous stop. It should // only ever get called after they have had a chance to run. assert (m_should_stop_is_valid); return m_should_stop; } virtual void PerformAction (Event *event_ptr) { Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS); // We're going to calculate if we should stop or not in some way during the course of // this code. Also by default we're going to stop, so set that here. m_should_stop = true; ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { WatchpointSP wp_sp (thread_sp->CalculateTarget()->GetWatchpointList().FindByID(GetValue())); if (wp_sp) { ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0)); Process* process = exe_ctx.GetProcessPtr(); // This sentry object makes sure the current watchpoint is disabled while performing watchpoint actions, // and it is then enabled after we are finished. WatchpointSentry sentry(process, wp_sp.get()); { // check if this process is running on an architecture where watchpoints trigger // before the associated instruction runs. if so, disable the WP, single-step and then // re-enable the watchpoint if (process) { uint32_t num; bool wp_triggers_after; if (process->GetWatchpointSupportInfo(num, wp_triggers_after).Success()) { if (!wp_triggers_after) { StopInfoSP stored_stop_info_sp = thread_sp->GetStopInfo(); assert (stored_stop_info_sp.get() == this); ThreadPlanSP new_plan_sp(thread_sp->QueueThreadPlanForStepSingleInstruction(false, // step-over false, // abort_other_plans true)); // stop_other_threads new_plan_sp->SetIsMasterPlan (true); new_plan_sp->SetOkayToDiscard (false); new_plan_sp->SetPrivate (true); process->GetThreadList().SetSelectedThreadByID (thread_sp->GetID()); process->Resume (); process->WaitForProcessToStop (NULL); process->GetThreadList().SetSelectedThreadByID (thread_sp->GetID()); thread_sp->SetStopInfo(stored_stop_info_sp); } } } } if (m_should_stop && wp_sp->GetConditionText() != NULL) { // We need to make sure the user sees any parse errors in their condition, so we'll hook the // constructor errors up to the debugger's Async I/O. ExecutionResults result_code; ValueObjectSP result_value_sp; const bool unwind_on_error = true; const bool ignore_breakpoints = true; Error error; result_code = ClangUserExpression::EvaluateWithError (exe_ctx, eExecutionPolicyOnlyWhenNeeded, lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny, unwind_on_error, ignore_breakpoints, wp_sp->GetConditionText(), NULL, result_value_sp, error, true, ClangUserExpression::kDefaultTimeout); if (result_code == eExecutionCompleted) { if (result_value_sp) { Scalar scalar_value; if (result_value_sp->ResolveValue (scalar_value)) { if (scalar_value.ULongLong(1) == 0) { // We have been vetoed. This takes precedence over querying // the watchpoint whether it should stop (aka ignore count and // friends). See also StopInfoWatchpoint::ShouldStop() as well // as Process::ProcessEventData::DoOnRemoval(). m_should_stop = false; } else m_should_stop = true; if (log) log->Printf("Condition successfully evaluated, result is %s.\n", m_should_stop ? "true" : "false"); } else { m_should_stop = true; if (log) log->Printf("Failed to get an integer result from the expression."); } } } else { Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger(); StreamSP error_sp = debugger.GetAsyncErrorStream (); error_sp->Printf ("Stopped due to an error evaluating condition of watchpoint "); wp_sp->GetDescription (error_sp.get(), eDescriptionLevelBrief); error_sp->Printf (": \"%s\"", wp_sp->GetConditionText()); error_sp->EOL(); const char *err_str = error.AsCString("<Unknown Error>"); if (log) log->Printf("Error evaluating condition: \"%s\"\n", err_str); error_sp->PutCString (err_str); error_sp->EOL(); error_sp->Flush(); // If the condition fails to be parsed or run, we should stop. m_should_stop = true; } } // If the condition says to stop, we run the callback to further decide whether to stop. if (m_should_stop) { StoppointCallbackContext context (event_ptr, exe_ctx, false); bool stop_requested = wp_sp->InvokeCallback (&context); // Also make sure that the callback hasn't continued the target. // If it did, when we'll set m_should_stop to false and get out of here. if (HasTargetRunSinceMe ()) m_should_stop = false; if (m_should_stop && !stop_requested) { // We have been vetoed by the callback mechanism. m_should_stop = false; } } // Finally, if we are going to stop, print out the new & old values: if (m_should_stop) { wp_sp->CaptureWatchedValue(exe_ctx); Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger(); StreamSP output_sp = debugger.GetAsyncOutputStream (); wp_sp->DumpSnapshots(output_sp.get()); output_sp->EOL(); output_sp->Flush(); } } else { Log * log_process(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log_process) log_process->Printf ("Process::%s could not find watchpoint id: %" PRId64 "...", __FUNCTION__, m_value); } if (log) log->Printf ("Process::%s returning from action with m_should_stop: %d.", __FUNCTION__, m_should_stop); m_should_stop_is_valid = true; } } private: std::string m_description; bool m_should_stop; bool m_should_stop_is_valid; }; //---------------------------------------------------------------------- // StopInfoUnixSignal //---------------------------------------------------------------------- class StopInfoUnixSignal : public StopInfo { public: StopInfoUnixSignal (Thread &thread, int signo) : StopInfo (thread, signo) { } virtual ~StopInfoUnixSignal () { } virtual StopReason GetStopReason () const { return eStopReasonSignal; } virtual bool ShouldStopSynchronous (Event *event_ptr) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) return thread_sp->GetProcess()->GetUnixSignals().GetShouldStop (m_value); return false; } virtual bool ShouldStop (Event *event_ptr) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) return thread_sp->GetProcess()->GetUnixSignals().GetShouldStop (m_value); return false; } // If should stop returns false, check if we should notify of this event virtual bool DoShouldNotify (Event *event_ptr) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { bool should_notify = thread_sp->GetProcess()->GetUnixSignals().GetShouldNotify (m_value); if (should_notify) { StreamString strm; strm.Printf ("thread %d received signal: %s", thread_sp->GetIndexID(), thread_sp->GetProcess()->GetUnixSignals().GetSignalAsCString (m_value)); Process::ProcessEventData::AddRestartedReason(event_ptr, strm.GetData()); } return should_notify; } return true; } virtual void WillResume (lldb::StateType resume_state) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { if (thread_sp->GetProcess()->GetUnixSignals().GetShouldSuppress(m_value) == false) thread_sp->SetResumeSignal(m_value); } } virtual const char * GetDescription () { if (m_description.empty()) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { StreamString strm; const char *signal_name = thread_sp->GetProcess()->GetUnixSignals().GetSignalAsCString (m_value); if (signal_name) strm.Printf("signal %s", signal_name); else strm.Printf("signal %" PRIi64, m_value); m_description.swap (strm.GetString()); } } return m_description.c_str(); } }; //---------------------------------------------------------------------- // StopInfoTrace //---------------------------------------------------------------------- class StopInfoTrace : public StopInfo { public: StopInfoTrace (Thread &thread) : StopInfo (thread, LLDB_INVALID_UID) { } virtual ~StopInfoTrace () { } virtual StopReason GetStopReason () const { return eStopReasonTrace; } virtual const char * GetDescription () { if (m_description.empty()) return "trace"; else return m_description.c_str(); } }; //---------------------------------------------------------------------- // StopInfoException //---------------------------------------------------------------------- class StopInfoException : public StopInfo { public: StopInfoException (Thread &thread, const char *description) : StopInfo (thread, LLDB_INVALID_UID) { if (description) SetDescription (description); } virtual ~StopInfoException () { } virtual StopReason GetStopReason () const { return eStopReasonException; } virtual const char * GetDescription () { if (m_description.empty()) return "exception"; else return m_description.c_str(); } }; //---------------------------------------------------------------------- // StopInfoThreadPlan //---------------------------------------------------------------------- class StopInfoThreadPlan : public StopInfo { public: StopInfoThreadPlan (ThreadPlanSP &plan_sp, ValueObjectSP &return_valobj_sp) : StopInfo (plan_sp->GetThread(), LLDB_INVALID_UID), m_plan_sp (plan_sp), m_return_valobj_sp (return_valobj_sp) { } virtual ~StopInfoThreadPlan () { } virtual StopReason GetStopReason () const { return eStopReasonPlanComplete; } virtual const char * GetDescription () { if (m_description.empty()) { StreamString strm; m_plan_sp->GetDescription (&strm, eDescriptionLevelBrief); m_description.swap (strm.GetString()); } return m_description.c_str(); } ValueObjectSP GetReturnValueObject() { return m_return_valobj_sp; } protected: virtual bool ShouldStop (Event *event_ptr) { if (m_plan_sp) return m_plan_sp->ShouldStop(event_ptr); else return StopInfo::ShouldStop(event_ptr); } private: ThreadPlanSP m_plan_sp; ValueObjectSP m_return_valobj_sp; }; class StopInfoExec : public StopInfo { public: StopInfoExec (Thread &thread) : StopInfo (thread, LLDB_INVALID_UID), m_performed_action (false) { } virtual ~StopInfoExec () { } virtual StopReason GetStopReason () const { return eStopReasonExec; } virtual const char * GetDescription () { return "exec"; } protected: virtual void PerformAction (Event *event_ptr) { // Only perform the action once if (m_performed_action) return; m_performed_action = true; ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) thread_sp->GetProcess()->DidExec(); } bool m_performed_action; }; } // namespace lldb_private StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID (Thread &thread, break_id_t break_id) { return StopInfoSP (new StopInfoBreakpoint (thread, break_id)); } StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID (Thread &thread, break_id_t break_id, bool should_stop) { return StopInfoSP (new StopInfoBreakpoint (thread, break_id, should_stop)); } StopInfoSP StopInfo::CreateStopReasonWithWatchpointID (Thread &thread, break_id_t watch_id) { return StopInfoSP (new StopInfoWatchpoint (thread, watch_id)); } StopInfoSP StopInfo::CreateStopReasonWithSignal (Thread &thread, int signo) { return StopInfoSP (new StopInfoUnixSignal (thread, signo)); } StopInfoSP StopInfo::CreateStopReasonToTrace (Thread &thread) { return StopInfoSP (new StopInfoTrace (thread)); } StopInfoSP StopInfo::CreateStopReasonWithPlan (ThreadPlanSP &plan_sp, ValueObjectSP return_valobj_sp) { return StopInfoSP (new StopInfoThreadPlan (plan_sp, return_valobj_sp)); } StopInfoSP StopInfo::CreateStopReasonWithException (Thread &thread, const char *description) { return StopInfoSP (new StopInfoException (thread, description)); } StopInfoSP StopInfo::CreateStopReasonWithExec (Thread &thread) { return StopInfoSP (new StopInfoExec (thread)); } ValueObjectSP StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp) { if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonPlanComplete) { StopInfoThreadPlan *plan_stop_info = static_cast<StopInfoThreadPlan *>(stop_info_sp.get()); return plan_stop_info->GetReturnValueObject(); } else return ValueObjectSP(); }