Do a better job of detecting when a breakpoint command has set the target running again (except you have to ignore

cases where the breakpoint runs expressions, those don't count as really "running again").

llvm-svn: 144064
This commit is contained in:
Jim Ingham 2011-11-08 03:00:11 +00:00
parent 5ad3ebaefd
commit 0faa43f964
7 changed files with 174 additions and 27 deletions

View File

@ -890,8 +890,11 @@ class ProcessModID
friend bool operator== (const ProcessModID &lhs, const ProcessModID &rhs); friend bool operator== (const ProcessModID &lhs, const ProcessModID &rhs);
public: public:
ProcessModID () : ProcessModID () :
m_stop_id (0), m_stop_id (0),
m_memory_id (0) m_resume_id (0),
m_memory_id (0),
m_last_user_expression_resume (0),
m_running_user_expression (false)
{} {}
ProcessModID (const ProcessModID &rhs) : ProcessModID (const ProcessModID &rhs) :
@ -911,11 +914,22 @@ public:
~ProcessModID () {} ~ProcessModID () {}
void BumpStopID () { m_stop_id++; } void BumpStopID () {
m_stop_id++;
}
void BumpMemoryID () { m_memory_id++; } void BumpMemoryID () { m_memory_id++; }
void BumpResumeID () {
m_resume_id++;
if (m_running_user_expression > 0)
m_last_user_expression_resume = m_resume_id;
}
uint32_t GetStopID() const { return m_stop_id; } uint32_t GetStopID() const { return m_stop_id; }
uint32_t GetMemoryID () const { return m_memory_id; } uint32_t GetMemoryID () const { return m_memory_id; }
uint32_t GetResumeID () const { return m_resume_id; }
uint32_t GetLastUserExpressionResumeID () const { return m_last_user_expression_resume; }
bool MemoryIDEqual (const ProcessModID &compare) const bool MemoryIDEqual (const ProcessModID &compare) const
{ {
@ -936,9 +950,23 @@ public:
{ {
return m_stop_id != UINT32_MAX; return m_stop_id != UINT32_MAX;
} }
void
SetRunningUserExpression (bool on)
{
// REMOVEME printf ("Setting running user expression %s at resume id %d - value: %d.\n", on ? "on" : "off", m_resume_id, m_running_user_expression);
if (on)
m_running_user_expression++;
else
m_running_user_expression--;
}
private: private:
uint32_t m_stop_id; uint32_t m_stop_id;
uint32_t m_resume_id;
uint32_t m_memory_id; uint32_t m_memory_id;
uint32_t m_last_user_expression_resume;
uint32_t m_running_user_expression;
}; };
inline bool operator== (const ProcessModID &lhs, const ProcessModID &rhs) inline bool operator== (const ProcessModID &lhs, const ProcessModID &rhs)
{ {
@ -1957,12 +1985,30 @@ public:
return m_mod_id; return m_mod_id;
} }
const ProcessModID &
GetModIDRef () const
{
return m_mod_id;
}
uint32_t uint32_t
GetStopID () const GetStopID () const
{ {
return m_mod_id.GetStopID(); return m_mod_id.GetStopID();
} }
uint32_t
GetResumeID () const
{
return m_mod_id.GetResumeID();
}
uint32_t
GetLastUserExpressionResumeID () const
{
return m_mod_id.GetLastUserExpressionResumeID();
}
//------------------------------------------------------------------ //------------------------------------------------------------------
/// Set accessor for the process exit status (return code). /// Set accessor for the process exit status (return code).
/// ///
@ -2608,6 +2654,9 @@ public:
return true; return true;
} }
void
SetRunningUserExpression (bool on);
//------------------------------------------------------------------ //------------------------------------------------------------------
// lldb::ExecutionContextScope pure virtual functions // lldb::ExecutionContextScope pure virtual functions
//------------------------------------------------------------------ //------------------------------------------------------------------

View File

@ -22,6 +22,7 @@ namespace lldb_private {
class StopInfo class StopInfo
{ {
friend class Process;
public: public:
//------------------------------------------------------------------ //------------------------------------------------------------------
// Constructors and Destructors // Constructors and Destructors
@ -136,13 +137,13 @@ protected:
//------------------------------------------------------------------ //------------------------------------------------------------------
Thread & m_thread; // The thread corresponding to the stop reason. Thread & m_thread; // The thread corresponding to the stop reason.
uint32_t m_stop_id; // The process stop ID for which this stop info is valid uint32_t m_stop_id; // The process stop ID for which this stop info is valid
uint32_t m_resume_id; // This is the resume ID when we made this stop ID.
uint64_t m_value; // A generic value that can be used for things pertaining to this stop info uint64_t m_value; // A generic value that can be used for things pertaining to this stop info
std::string m_description; // A textual description describing this stop. std::string m_description; // A textual description describing this stop.
// This provides an accessor to the PrivateEventState of the process for StopInfo's w/o having to make each // This determines whether the target has run since this stop info.
// StopInfo subclass a friend of Process. // N.B. running to evaluate a user expression does not count.
lldb::StateType bool HasTargetRunSinceMe ();
GetPrivateState ();
private: private:
friend class Thread; friend class Thread;

View File

@ -567,6 +567,9 @@ ClangUserExpression::Execute (Stream &error_stream,
if (log) if (log)
log->Printf("-- [ClangUserExpression::Execute] Execution of expression begins --"); log->Printf("-- [ClangUserExpression::Execute] Execution of expression begins --");
if (exe_ctx.GetProcessPtr())
exe_ctx.GetProcessPtr()->SetRunningUserExpression(true);
ExecutionResults execution_result = exe_ctx.GetProcessRef().RunThreadPlan (exe_ctx, ExecutionResults execution_result = exe_ctx.GetProcessRef().RunThreadPlan (exe_ctx,
call_plan_sp, call_plan_sp,
stop_others, stop_others,
@ -575,6 +578,9 @@ ClangUserExpression::Execute (Stream &error_stream,
single_thread_timeout_usec, single_thread_timeout_usec,
error_stream); error_stream);
if (exe_ctx.GetProcessPtr())
exe_ctx.GetProcessPtr()->SetRunningUserExpression(false);
if (log) if (log)
log->Printf("-- [ClangUserExpression::Execute] Execution of expression completed --"); log->Printf("-- [ClangUserExpression::Execute] Execution of expression completed --");

View File

@ -1156,6 +1156,12 @@ Process::SetPrivateState (StateType new_state)
} }
} }
void
Process::SetRunningUserExpression (bool on)
{
m_mod_id.SetRunningUserExpression (on);
}
addr_t addr_t
Process::GetImageInfoAddress() Process::GetImageInfoAddress()
{ {
@ -2425,6 +2431,7 @@ Process::Resume ()
// to see if they are suppoed to start back up with a signal. // to see if they are suppoed to start back up with a signal.
if (m_thread_list.WillResume()) if (m_thread_list.WillResume())
{ {
m_mod_id.BumpResumeID();
error = DoResume(); error = DoResume();
if (error.Success()) if (error.Success())
{ {
@ -2986,18 +2993,47 @@ Process::ProcessEventData::DoOnRemoval (Event *event_ptr)
// If we're stopped and haven't restarted, then do the breakpoint commands here: // If we're stopped and haven't restarted, then do the breakpoint commands here:
if (m_state == eStateStopped && ! m_restarted) if (m_state == eStateStopped && ! m_restarted)
{ {
int num_threads = m_process_sp->GetThreadList().GetSize(); ThreadList &curr_thread_list = m_process_sp->GetThreadList();
int num_threads = curr_thread_list.GetSize();
int idx; int idx;
// The actions might change one of the thread's stop_info's opinions about whether we should // The actions might change one of the thread's stop_info's opinions about whether we should
// stop the process, so we need to query that as we go. // stop the process, so we need to query that as we go.
// One other complication here, is that we try to catch any case where the target has run (except for expressions)
// and immediately exit, but if we get that wrong (which is possible) then the thread list might have changed, and
// that would cause our iteration here to crash. We could make a copy of the thread list, but we'd really like
// to also know if it has changed at all, so we make up a vector of the thread ID's and check what we get back
// against this list & bag out if anything differs.
std::vector<lldb::tid_t> thread_index_array(num_threads);
for (idx = 0; idx < num_threads; ++idx)
thread_index_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetIndexID();
bool still_should_stop = true; bool still_should_stop = true;
for (idx = 0; idx < num_threads; ++idx) for (idx = 0; idx < num_threads; ++idx)
{ {
lldb::ThreadSP thread_sp = m_process_sp->GetThreadList().GetThreadAtIndex(idx); curr_thread_list = m_process_sp->GetThreadList();
if (curr_thread_list.GetSize() != num_threads)
{
lldb::LogSP log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS));
log->Printf("Number of threads changed from %d to %d while processing event.", num_threads, curr_thread_list.GetSize());
break;
}
lldb::ThreadSP thread_sp = curr_thread_list.GetThreadAtIndex(idx);
if (thread_sp->GetIndexID() != thread_index_array[idx])
{
lldb::LogSP log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS));
log->Printf("The thread at position %d changed from %d to %d while processing event.",
idx,
thread_index_array[idx],
thread_sp->GetIndexID());
break;
}
StopInfoSP stop_info_sp = thread_sp->GetStopInfo (); StopInfoSP stop_info_sp = thread_sp->GetStopInfo ();
if (stop_info_sp) if (stop_info_sp)
{ {
@ -3006,7 +3042,9 @@ Process::ProcessEventData::DoOnRemoval (Event *event_ptr)
// event so that whoever is receiving it will know to wait for the running event and reflect // event so that whoever is receiving it will know to wait for the running event and reflect
// that state appropriately. // that state appropriately.
// We also need to stop processing actions, since they aren't expecting the target to be running. // We also need to stop processing actions, since they aren't expecting the target to be running.
if (m_process_sp->GetPrivateState() == eStateRunning)
// FIXME: we might have run.
if (stop_info_sp->HasTargetRunSinceMe())
{ {
SetRestarted (true); SetRestarted (true);
break; break;

View File

@ -35,6 +35,7 @@ using namespace lldb_private;
StopInfo::StopInfo (Thread &thread, uint64_t value) : StopInfo::StopInfo (Thread &thread, uint64_t value) :
m_thread (thread), m_thread (thread),
m_stop_id (thread.GetProcess().GetStopID()), m_stop_id (thread.GetProcess().GetStopID()),
m_resume_id (thread.GetProcess().GetResumeID()),
m_value (value) m_value (value)
{ {
} }
@ -49,12 +50,37 @@ void
StopInfo::MakeStopInfoValid () StopInfo::MakeStopInfoValid ()
{ {
m_stop_id = m_thread.GetProcess().GetStopID(); m_stop_id = m_thread.GetProcess().GetStopID();
m_resume_id = m_thread.GetProcess().GetResumeID();
} }
lldb::StateType bool
StopInfo::GetPrivateState () StopInfo::HasTargetRunSinceMe ()
{ {
return m_thread.GetProcess().GetPrivateState(); lldb::StateType ret_type = m_thread.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 = m_thread.GetProcess().GetResumeID();
uint32_t last_user_expression_id = m_thread.GetProcess().GetLastUserExpressionResumeID ();
if (curr_resume_id == m_resume_id)
{
return false;
}
else if (curr_resume_id > last_user_expression_id)
{
return true;
}
}
return false;
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -161,7 +187,7 @@ public:
// However we want to run all the callbacks, except of course if one of them actually // However we want to run all the callbacks, except of course if one of them actually
// resumes the target. // resumes the target.
// So we use stop_requested to track what we're were asked to do. // So we use stop_requested to track what we're were asked to do.
bool stop_requested = true; bool stop_requested = false;
for (size_t j = 0; j < num_owners; j++) for (size_t j = 0; j < num_owners; j++)
{ {
lldb::BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(j); lldb::BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(j);
@ -170,11 +196,30 @@ public:
&m_thread, &m_thread,
m_thread.GetStackFrameAtIndex(0).get(), m_thread.GetStackFrameAtIndex(0).get(),
false); false);
stop_requested = bp_loc_sp->InvokeCallback (&context); bool callback_return;
// 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 stack breakpoint hits then this will change.
Debugger &debugger = m_thread.GetProcess().GetTarget().GetDebugger();
bool old_async = debugger.GetAsyncExecution();
debugger.SetAsyncExecution (true);
callback_return = bp_loc_sp->InvokeCallback (&context);
debugger.SetAsyncExecution (old_async);
if (callback_return)
stop_requested = true;
// Also make sure that the callback hasn't continued the target. // 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 it did, when we'll set m_should_start to false and get out of here.
if (GetPrivateState() == eStateRunning) if (HasTargetRunSinceMe ())
{
m_should_stop = false; m_should_stop = false;
break;
}
} }
if (m_should_stop && !stop_requested) if (m_should_stop && !stop_requested)
@ -423,7 +468,7 @@ public:
bool stop_requested = wp_sp->InvokeCallback (&context); bool stop_requested = wp_sp->InvokeCallback (&context);
// Also make sure that the callback hasn't continued the target. // 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 it did, when we'll set m_should_start to false and get out of here.
if (GetPrivateState() == eStateRunning) if (HasTargetRunSinceMe ())
m_should_stop = false; m_should_stop = false;
if (m_should_stop && !stop_requested) if (m_should_stop && !stop_requested)

View File

@ -71,6 +71,7 @@ class ConditionalBreakTestCase(TestBase):
# The 10 in range(10) is just an arbitrary number, which means we would # The 10 in range(10) is just an arbitrary number, which means we would
# like to try for at most 10 times. # like to try for at most 10 times.
for j in range(10): for j in range(10):
print "j is: ", j
thread = process.GetThreadAtIndex(0) thread = process.GetThreadAtIndex(0)
if thread.GetNumFrames() >= 2: if thread.GetNumFrames() >= 2:
@ -102,9 +103,13 @@ class ConditionalBreakTestCase(TestBase):
# executable, sets the breakpoint on c(), and adds the callback for the # executable, sets the breakpoint on c(), and adds the callback for the
# breakpoint such that lldb only stops when the caller of c() is a(). # breakpoint such that lldb only stops when the caller of c() is a().
# the "my" package that defines the date() function. # the "my" package that defines the date() function.
print "About to source .lldb"
self.runCmd("command source .lldb") self.runCmd("command source .lldb")
print "About to run."
self.runCmd("run", RUN_SUCCEEDED) self.runCmd("run", RUN_SUCCEEDED)
print "Done running"
# The stop reason of the thread should be breakpoint. # The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,

View File

@ -7,7 +7,8 @@ def stop_if_called_from_a():
dbg = lldb.SBDebugger.FindDebuggerWithID(lldb.debugger_unique_id) dbg = lldb.SBDebugger.FindDebuggerWithID(lldb.debugger_unique_id)
# Perform synchronous interaction with the debugger. # Perform synchronous interaction with the debugger.
dbg.SetAsync(False) old_async = dbg.GetAsync()
dbg.SetAsync(True)
# Retrieve the target, process, and the only thread. # Retrieve the target, process, and the only thread.
target = dbg.GetSelectedTarget() target = dbg.GetSelectedTarget()
@ -18,17 +19,19 @@ def stop_if_called_from_a():
# of the leaf function c() is a(). If it's not the right caller, we ask the # of the leaf function c() is a(). If it's not the right caller, we ask the
# command interpreter to continue execution. # command interpreter to continue execution.
#print >> sys.stdout, "Checking call frames..." print >> sys.stdout, "Checking call frames..."
#lldbutil.print_stacktrace(thread) lldbutil.print_stacktrace(thread)
should_stop = True
if thread.GetNumFrames() >= 2: if thread.GetNumFrames() >= 2:
funcs = lldbutil.get_function_names(thread) funcs = lldbutil.get_function_names(thread)
#print >> sys.stdout, funcs[0], "called from", funcs[1] print >> sys.stdout, funcs[0], "called from", funcs[1]
if (funcs[0] == 'c' and funcs[1] == 'a'): if (funcs[0] == 'c' and funcs[1] == 'a'):
#print >> sys.stdout, "Stopped at c() with immediate caller as a()." should_stop = True
pass
else: else:
#print >> sys.stdout, "Continuing..."
process.Continue() process.Continue()
should_stop = False
dbg.SetAsync(old_async)
return should_stop
return True