2010-08-04 09:40:35 +08:00
|
|
|
//===-- 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/Target/StopInfo.h"
|
|
|
|
|
|
|
|
// C Includes
|
|
|
|
// C++ Includes
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
// Other libraries and framework includes
|
|
|
|
// Project includes
|
|
|
|
#include "lldb/Core/Log.h"
|
2010-11-19 02:52:36 +08:00
|
|
|
#include "lldb/Breakpoint/Breakpoint.h"
|
2010-08-04 09:40:35 +08:00
|
|
|
#include "lldb/Breakpoint/BreakpointLocation.h"
|
|
|
|
#include "lldb/Breakpoint/StoppointCallbackContext.h"
|
2011-10-14 08:42:25 +08:00
|
|
|
#include "lldb/Breakpoint/Watchpoint.h"
|
2011-08-09 10:12:22 +08:00
|
|
|
#include "lldb/Core/Debugger.h"
|
2010-08-04 09:40:35 +08:00
|
|
|
#include "lldb/Core/StreamString.h"
|
2011-08-09 10:12:22 +08:00
|
|
|
#include "lldb/Expression/ClangUserExpression.h"
|
|
|
|
#include "lldb/Target/Target.h"
|
2010-08-04 09:40:35 +08:00
|
|
|
#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 (thread),
|
2012-02-21 08:09:25 +08:00
|
|
|
m_stop_id (thread.GetProcess()->GetStopID()),
|
|
|
|
m_resume_id (thread.GetProcess()->GetResumeID()),
|
2010-08-04 09:40:35 +08:00
|
|
|
m_value (value)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
StopInfo::IsValid () const
|
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
return m_thread.GetProcess()->GetStopID() == m_stop_id;
|
2010-08-04 09:40:35 +08:00
|
|
|
}
|
|
|
|
|
2011-01-20 10:03:18 +08:00
|
|
|
void
|
|
|
|
StopInfo::MakeStopInfoValid ()
|
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
m_stop_id = m_thread.GetProcess()->GetStopID();
|
|
|
|
m_resume_id = m_thread.GetProcess()->GetResumeID();
|
2011-01-20 10:03:18 +08:00
|
|
|
}
|
|
|
|
|
2011-11-08 11:00:11 +08:00
|
|
|
bool
|
|
|
|
StopInfo::HasTargetRunSinceMe ()
|
2011-08-09 10:12:22 +08:00
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
lldb::StateType ret_type = m_thread.GetProcess()->GetPrivateState();
|
2011-11-08 11:00:11 +08:00
|
|
|
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.
|
|
|
|
|
2012-02-21 08:09:25 +08:00
|
|
|
uint32_t curr_resume_id = m_thread.GetProcess()->GetResumeID();
|
|
|
|
uint32_t last_user_expression_id = m_thread.GetProcess()->GetLastUserExpressionResumeID ();
|
2011-11-08 11:00:11 +08:00
|
|
|
if (curr_resume_id == m_resume_id)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (curr_resume_id > last_user_expression_id)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2011-08-09 10:12:22 +08:00
|
|
|
}
|
|
|
|
|
2010-08-04 09:40:35 +08:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// StopInfoBreakpoint
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
2011-08-09 10:12:22 +08:00
|
|
|
namespace lldb_private
|
|
|
|
{
|
2010-08-04 09:40:35 +08:00
|
|
|
class StopInfoBreakpoint : public StopInfo
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
StopInfoBreakpoint (Thread &thread, break_id_t break_id) :
|
|
|
|
StopInfo (thread, break_id),
|
|
|
|
m_description(),
|
|
|
|
m_should_stop (false),
|
2011-02-08 13:20:59 +08:00
|
|
|
m_should_stop_is_valid (false),
|
2011-10-28 09:12:19 +08:00
|
|
|
m_should_perform_action (true),
|
|
|
|
m_address (LLDB_INVALID_ADDRESS)
|
2010-08-04 09:40:35 +08:00
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
BreakpointSiteSP bp_site_sp (m_thread.GetProcess()->GetBreakpointSiteList().FindByID (m_value));
|
2011-10-28 09:12:19 +08:00
|
|
|
if (bp_site_sp)
|
|
|
|
{
|
|
|
|
m_address = bp_site_sp->GetLoadAddress();
|
|
|
|
}
|
2010-08-04 09:40:35 +08:00
|
|
|
}
|
|
|
|
|
2010-10-15 07:45:03 +08:00
|
|
|
StopInfoBreakpoint (Thread &thread, break_id_t break_id, bool should_stop) :
|
|
|
|
StopInfo (thread, break_id),
|
|
|
|
m_description(),
|
|
|
|
m_should_stop (should_stop),
|
2011-02-08 13:20:59 +08:00
|
|
|
m_should_stop_is_valid (true),
|
2011-10-28 09:12:19 +08:00
|
|
|
m_should_perform_action (true),
|
|
|
|
m_address (LLDB_INVALID_ADDRESS)
|
2010-10-15 07:45:03 +08:00
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
BreakpointSiteSP bp_site_sp (m_thread.GetProcess()->GetBreakpointSiteList().FindByID (m_value));
|
2011-10-28 09:12:19 +08:00
|
|
|
if (bp_site_sp)
|
|
|
|
{
|
|
|
|
m_address = bp_site_sp->GetLoadAddress();
|
|
|
|
}
|
2010-10-15 07:45:03 +08:00
|
|
|
}
|
|
|
|
|
2010-08-04 09:40:35 +08:00
|
|
|
virtual ~StopInfoBreakpoint ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual StopReason
|
|
|
|
GetStopReason () const
|
|
|
|
{
|
|
|
|
return eStopReasonBreakpoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool
|
2012-04-21 05:16:56 +08:00
|
|
|
ShouldStopSynchronous (Event *event_ptr)
|
2010-08-04 09:40:35 +08:00
|
|
|
{
|
|
|
|
if (!m_should_stop_is_valid)
|
|
|
|
{
|
|
|
|
// Only check once if we should stop at a breakpoint
|
2012-02-21 08:09:25 +08:00
|
|
|
BreakpointSiteSP bp_site_sp (m_thread.GetProcess()->GetBreakpointSiteList().FindByID (m_value));
|
2010-08-04 09:40:35 +08:00
|
|
|
if (bp_site_sp)
|
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
ExecutionContext exe_ctx (m_thread.GetStackFrameAtIndex(0));
|
|
|
|
StoppointCallbackContext context (event_ptr, exe_ctx, true);
|
2010-08-04 09:40:35 +08:00
|
|
|
m_should_stop = bp_site_sp->ShouldStop (&context);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-11-06 09:53:30 +08:00
|
|
|
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
|
2010-08-04 09:40:35 +08:00
|
|
|
|
|
|
|
if (log)
|
|
|
|
log->Printf ("Process::%s could not find breakpoint site id: %lld...", __FUNCTION__, m_value);
|
|
|
|
|
|
|
|
m_should_stop = true;
|
|
|
|
}
|
|
|
|
m_should_stop_is_valid = true;
|
|
|
|
}
|
|
|
|
return m_should_stop;
|
|
|
|
}
|
2010-08-10 08:59:59 +08:00
|
|
|
|
2012-04-21 05:16:56 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-08-10 08:59:59 +08:00
|
|
|
virtual void
|
|
|
|
PerformAction (Event *event_ptr)
|
|
|
|
{
|
2011-02-08 13:20:59 +08:00
|
|
|
if (!m_should_perform_action)
|
|
|
|
return;
|
|
|
|
m_should_perform_action = false;
|
|
|
|
|
2011-08-09 10:12:22 +08:00
|
|
|
LogSP log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS);
|
|
|
|
|
2012-02-21 08:09:25 +08:00
|
|
|
BreakpointSiteSP bp_site_sp (m_thread.GetProcess()->GetBreakpointSiteList().FindByID (m_value));
|
2011-12-09 12:17:31 +08:00
|
|
|
|
2010-08-10 08:59:59 +08:00
|
|
|
if (bp_site_sp)
|
|
|
|
{
|
|
|
|
size_t num_owners = bp_site_sp->GetNumberOfOwners();
|
2011-12-09 12:17:31 +08:00
|
|
|
|
|
|
|
if (num_owners == 0)
|
2010-08-10 08:59:59 +08:00
|
|
|
{
|
2011-12-09 12:17:31 +08:00
|
|
|
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;
|
|
|
|
|
2012-02-21 08:09:25 +08:00
|
|
|
ExecutionContext exe_ctx (m_thread.GetStackFrameAtIndex(0));
|
|
|
|
StoppointCallbackContext context (event_ptr, exe_ctx, false);
|
2011-08-09 10:12:22 +08:00
|
|
|
|
|
|
|
for (size_t j = 0; j < num_owners; j++)
|
|
|
|
{
|
|
|
|
lldb::BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(j);
|
2011-12-09 12:17:31 +08:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
bool condition_says_stop = true;
|
2011-08-09 10:12:22 +08:00
|
|
|
if (bp_loc_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.
|
|
|
|
|
|
|
|
ValueObjectSP result_valobj_sp;
|
|
|
|
|
|
|
|
ExecutionResults result_code;
|
|
|
|
ValueObjectSP result_value_sp;
|
|
|
|
const bool discard_on_error = true;
|
|
|
|
Error error;
|
2012-02-21 08:09:25 +08:00
|
|
|
result_code = ClangUserExpression::EvaluateWithError (exe_ctx,
|
2012-04-21 05:16:56 +08:00
|
|
|
eExecutionPolicyOnlyWhenNeeded,
|
2011-11-08 07:35:40 +08:00
|
|
|
lldb::eLanguageTypeUnknown,
|
2011-12-22 06:22:58 +08:00
|
|
|
ClangUserExpression::eResultTypeAny,
|
This patch modifies the expression parser to allow it
to execute expressions even in the absence of a process.
This allows expressions to run in situations where the
target cannot run -- e.g., to perform calculations based
on type information, or to inspect a binary's static
data.
This modification touches the following files:
lldb-private-enumerations.h
Introduce a new enum specifying the policy for
processing an expression. Some expressions should
always be JITted, for example if they are functions
that will be used over and over again. Some
expressions should always be interpreted, for
example if the target is unsafe to run. For most,
it is acceptable to JIT them, but interpretation
is preferable when possible.
Target.[h,cpp]
Have EvaluateExpression now accept the new enum.
ClangExpressionDeclMap.[cpp,h]
Add support for the IR interpreter and also make
the ClangExpressionDeclMap more robust in the
absence of a process.
ClangFunction.[cpp,h]
Add support for the new enum.
IRInterpreter.[cpp,h]
New implementation.
ClangUserExpression.[cpp,h]
Add support for the new enum, and for running
expressions in the absence of a process.
ClangExpression.h
Remove references to the old DWARF-based method
of evaluating expressions, because it has been
superseded for now.
ClangUtilityFunction.[cpp,h]
Add support for the new enum.
ClangExpressionParser.[cpp,h]
Add support for the new enum, remove references
to DWARF, and add support for checking whether
the expression could be evaluated statically.
IRForTarget.[h,cpp]
Add support for the new enum, and add utility
functions to support the interpreter.
IRToDWARF.cpp
Removed
CommandObjectExpression.cpp
Remove references to the obsolete -i option.
Process.cpp
Modify calls to ClangUserExpression::Evaluate
to pass the correct enum (for dlopen/dlclose)
SBValue.cpp
Add support for the new enum.
SBFrame.cpp
Add support for he new enum.
BreakpointOptions.cpp
Add support for the new enum.
llvm-svn: 139772
2011-09-15 10:13:07 +08:00
|
|
|
discard_on_error,
|
|
|
|
bp_loc_sp->GetConditionText(),
|
|
|
|
NULL,
|
|
|
|
result_value_sp,
|
|
|
|
error);
|
2011-08-09 10:12:22 +08:00
|
|
|
if (result_code == eExecutionCompleted)
|
|
|
|
{
|
|
|
|
if (result_value_sp)
|
|
|
|
{
|
|
|
|
Scalar scalar_value;
|
|
|
|
if (result_value_sp->ResolveValue (scalar_value))
|
|
|
|
{
|
|
|
|
if (scalar_value.ULongLong(1) == 0)
|
2011-12-09 12:17:31 +08:00
|
|
|
condition_says_stop = false;
|
2011-08-09 10:12:22 +08:00
|
|
|
else
|
2011-12-09 12:17:31 +08:00
|
|
|
condition_says_stop = true;
|
2011-08-09 10:12:22 +08:00
|
|
|
if (log)
|
|
|
|
log->Printf("Condition successfully evaluated, result is %s.\n",
|
|
|
|
m_should_stop ? "true" : "false");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-12-09 12:17:31 +08:00
|
|
|
condition_says_stop = true;
|
2011-08-09 10:12:22 +08:00
|
|
|
if (log)
|
|
|
|
log->Printf("Failed to get an integer result from the expression.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
|
2011-08-09 10:12:22 +08:00
|
|
|
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 = 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.
|
2011-12-09 12:17:31 +08:00
|
|
|
condition_says_stop = true;
|
2011-08-09 10:12:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-09 12:17:31 +08:00
|
|
|
// If this location's condition says we should aren't going to stop,
|
|
|
|
// then don't run the callback for this location.
|
|
|
|
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.
|
|
|
|
|
2012-02-21 08:09:25 +08:00
|
|
|
Debugger &debugger = m_thread.CalculateTarget()->GetDebugger();
|
2011-12-09 12:17:31 +08:00
|
|
|
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;
|
|
|
|
|
|
|
|
// 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;
|
2011-08-09 10:12:22 +08:00
|
|
|
break;
|
2011-12-09 12:17:31 +08:00
|
|
|
}
|
2011-08-09 10:12:22 +08:00
|
|
|
}
|
2010-08-10 08:59:59 +08:00
|
|
|
}
|
2011-12-09 12:17:31 +08:00
|
|
|
// 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;
|
|
|
|
|
2010-08-10 08:59:59 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-12-09 12:17:31 +08:00
|
|
|
m_should_stop = true;
|
|
|
|
m_should_stop_is_valid = true;
|
2010-11-06 09:53:30 +08:00
|
|
|
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
|
2010-08-10 08:59:59 +08:00
|
|
|
|
|
|
|
if (log)
|
|
|
|
log->Printf ("Process::%s could not find breakpoint site id: %lld...", __FUNCTION__, m_value);
|
|
|
|
}
|
2011-08-09 10:12:22 +08:00
|
|
|
if (log)
|
|
|
|
log->Printf ("Process::%s returning from action with m_should_stop: %d.", __FUNCTION__, m_should_stop);
|
2010-08-10 08:59:59 +08:00
|
|
|
}
|
2010-08-04 09:40:35 +08:00
|
|
|
|
|
|
|
virtual bool
|
|
|
|
ShouldNotify (Event *event_ptr)
|
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
BreakpointSiteSP bp_site_sp (m_thread.GetProcess()->GetBreakpointSiteList().FindByID (m_value));
|
2010-08-04 09:40:35 +08:00
|
|
|
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())
|
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
BreakpointSiteSP bp_site_sp (m_thread.GetProcess()->GetBreakpointSiteList().FindByID (m_value));
|
2010-08-10 08:59:59 +08:00
|
|
|
if (bp_site_sp)
|
|
|
|
{
|
|
|
|
StreamString strm;
|
|
|
|
strm.Printf("breakpoint ");
|
|
|
|
bp_site_sp->GetDescription(&strm, eDescriptionLevelBrief);
|
|
|
|
m_description.swap (strm.GetString());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
StreamString strm;
|
2011-10-28 09:12:19 +08:00
|
|
|
if (m_address == LLDB_INVALID_ADDRESS)
|
|
|
|
strm.Printf("breakpoint site %lli which has been deleted - unknown address", m_value);
|
|
|
|
else
|
|
|
|
strm.Printf("breakpoint site %lli which has been deleted - was at 0x%llx", m_value, m_address);
|
2010-08-10 08:59:59 +08:00
|
|
|
m_description.swap (strm.GetString());
|
|
|
|
}
|
2010-08-04 09:40:35 +08:00
|
|
|
}
|
|
|
|
return m_description.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::string m_description;
|
|
|
|
bool m_should_stop;
|
|
|
|
bool m_should_stop_is_valid;
|
2011-02-08 13:20:59 +08:00
|
|
|
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.
|
2011-10-28 09:12:19 +08:00
|
|
|
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.
|
2010-08-04 09:40:35 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// StopInfoWatchpoint
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
class StopInfoWatchpoint : public StopInfo
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
StopInfoWatchpoint (Thread &thread, break_id_t watch_id) :
|
2011-09-22 06:47:15 +08:00
|
|
|
StopInfo(thread, watch_id),
|
|
|
|
m_description(),
|
|
|
|
m_should_stop(false),
|
|
|
|
m_should_stop_is_valid(false)
|
2010-08-04 09:40:35 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~StopInfoWatchpoint ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual StopReason
|
|
|
|
GetStopReason () const
|
|
|
|
{
|
|
|
|
return eStopReasonWatchpoint;
|
|
|
|
}
|
|
|
|
|
2011-09-22 06:47:15 +08:00
|
|
|
virtual bool
|
|
|
|
ShouldStop (Event *event_ptr)
|
|
|
|
{
|
|
|
|
// ShouldStop() method is idempotent and should not affect hit count.
|
2011-10-18 02:58:00 +08:00
|
|
|
// See Process::RunPrivateStateThread()->Process()->HandlePrivateEvent()
|
|
|
|
// -->Process()::ShouldBroadcastEvent()->ThreadList::ShouldStop()->
|
|
|
|
// Thread::ShouldStop()->ThreadPlanBase::ShouldStop()->
|
|
|
|
// StopInfoWatchpoint::ShouldStop() and
|
|
|
|
// Event::DoOnRemoval()->Process::ProcessEventData::DoOnRemoval()->
|
|
|
|
// StopInfoWatchpoint::PerformAction().
|
2011-09-22 06:47:15 +08:00
|
|
|
if (m_should_stop_is_valid)
|
|
|
|
return m_should_stop;
|
|
|
|
|
2011-10-14 08:42:25 +08:00
|
|
|
WatchpointSP wp_sp =
|
2012-02-21 08:09:25 +08:00
|
|
|
m_thread.CalculateTarget()->GetWatchpointList().FindByID(GetValue());
|
2011-10-14 08:42:25 +08:00
|
|
|
if (wp_sp)
|
2011-09-22 06:47:15 +08:00
|
|
|
{
|
|
|
|
// Check if we should stop at a watchpoint.
|
2012-02-21 08:09:25 +08:00
|
|
|
ExecutionContext exe_ctx (m_thread.GetStackFrameAtIndex(0));
|
|
|
|
StoppointCallbackContext context (event_ptr, exe_ctx, true);
|
2011-10-14 08:42:25 +08:00
|
|
|
m_should_stop = wp_sp->ShouldStop (&context);
|
2011-09-22 06:47:15 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
|
|
|
|
|
|
|
|
if (log)
|
|
|
|
log->Printf ("Process::%s could not find watchpoint location id: %lld...",
|
|
|
|
__FUNCTION__, GetValue());
|
|
|
|
|
|
|
|
m_should_stop = true;
|
|
|
|
}
|
|
|
|
m_should_stop_is_valid = true;
|
|
|
|
return m_should_stop;
|
|
|
|
}
|
|
|
|
|
2011-10-18 02:58:00 +08:00
|
|
|
// Perform any action that is associated with this stop. This is done as the
|
|
|
|
// Event is removed from the event queue.
|
|
|
|
virtual void
|
|
|
|
PerformAction (Event *event_ptr)
|
|
|
|
{
|
|
|
|
LogSP log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS);
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
WatchpointSP wp_sp =
|
2012-02-21 08:09:25 +08:00
|
|
|
m_thread.CalculateTarget()->GetWatchpointList().FindByID(GetValue());
|
2011-10-18 02:58:00 +08:00
|
|
|
if (wp_sp)
|
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
ExecutionContext exe_ctx (m_thread.GetStackFrameAtIndex(0));
|
2012-07-14 07:18:48 +08:00
|
|
|
{
|
|
|
|
// 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
|
|
|
|
Process* process = exe_ctx.GetProcessPtr();
|
|
|
|
if (process)
|
|
|
|
{
|
|
|
|
uint32_t num; bool wp_triggers_after;
|
|
|
|
if (process->GetWatchpointSupportInfo(num, wp_triggers_after).Success())
|
|
|
|
{
|
|
|
|
if (!wp_triggers_after)
|
|
|
|
{
|
|
|
|
process->DisableWatchpoint(wp_sp.get());
|
|
|
|
|
|
|
|
ThreadPlan *new_plan = m_thread.QueueThreadPlanForStepSingleInstruction(false, // step-over
|
|
|
|
false, // abort_other_plans
|
|
|
|
true); // stop_other_threads
|
|
|
|
new_plan->SetIsMasterPlan (true);
|
|
|
|
new_plan->SetOkayToDiscard (false);
|
|
|
|
process->GetThreadList().SetSelectedThreadByID (m_thread.GetID());
|
|
|
|
process->Resume ();
|
|
|
|
process->WaitForProcessToStop (NULL);
|
|
|
|
process->GetThreadList().SetSelectedThreadByID (m_thread.GetID());
|
|
|
|
MakeStopInfoValid(); // make sure we do not fail to stop because of the single-step taken above
|
|
|
|
|
|
|
|
process->EnableWatchpoint(wp_sp.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-02-21 08:09:25 +08:00
|
|
|
StoppointCallbackContext context (event_ptr, exe_ctx, false);
|
2011-10-18 02:58:00 +08:00
|
|
|
|
|
|
|
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 discard_on_error = true;
|
|
|
|
Error error;
|
2012-02-21 08:09:25 +08:00
|
|
|
result_code = ClangUserExpression::EvaluateWithError (exe_ctx,
|
2011-10-18 02:58:00 +08:00
|
|
|
eExecutionPolicyAlways,
|
2011-11-08 07:35:40 +08:00
|
|
|
lldb::eLanguageTypeUnknown,
|
2011-12-22 06:22:58 +08:00
|
|
|
ClangUserExpression::eResultTypeAny,
|
2011-10-18 02:58:00 +08:00
|
|
|
discard_on_error,
|
|
|
|
wp_sp->GetConditionText(),
|
|
|
|
NULL,
|
|
|
|
result_value_sp,
|
2012-07-17 07:10:35 +08:00
|
|
|
error,
|
|
|
|
500000);
|
2011-10-18 02:58:00 +08:00
|
|
|
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
|
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
|
2011-10-18 02:58:00 +08:00
|
|
|
StreamSP error_sp = debugger.GetAsyncErrorStream ();
|
2012-01-25 07:19:25 +08:00
|
|
|
error_sp->Printf ("Stopped due to an error evaluating condition of watchpoint ");
|
2011-10-18 02:58:00 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2012-08-10 07:10:57 +08:00
|
|
|
|
|
|
|
// If the condition says to stop, we run the callback to further decide whether to stop.
|
|
|
|
if (m_should_stop)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2011-10-18 02:58:00 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
|
|
|
|
|
|
|
|
if (log)
|
|
|
|
log->Printf ("Process::%s could not find watchpoint id: %lld...", __FUNCTION__, m_value);
|
|
|
|
}
|
|
|
|
if (log)
|
|
|
|
log->Printf ("Process::%s returning from action with m_should_stop: %d.", __FUNCTION__, m_should_stop);
|
|
|
|
}
|
|
|
|
|
2010-08-04 09:40:35 +08:00
|
|
|
virtual const char *
|
|
|
|
GetDescription ()
|
|
|
|
{
|
|
|
|
if (m_description.empty())
|
|
|
|
{
|
|
|
|
StreamString strm;
|
|
|
|
strm.Printf("watchpoint %lli", m_value);
|
|
|
|
m_description.swap (strm.GetString());
|
|
|
|
}
|
|
|
|
return m_description.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::string m_description;
|
2011-09-22 06:47:15 +08:00
|
|
|
bool m_should_stop;
|
|
|
|
bool m_should_stop_is_valid;
|
2010-08-04 09:40:35 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// StopInfoUnixSignal
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
class StopInfoUnixSignal : public StopInfo
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
StopInfoUnixSignal (Thread &thread, int signo) :
|
2011-06-04 09:26:29 +08:00
|
|
|
StopInfo (thread, signo)
|
2010-08-04 09:40:35 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~StopInfoUnixSignal ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
virtual StopReason
|
|
|
|
GetStopReason () const
|
|
|
|
{
|
|
|
|
return eStopReasonSignal;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool
|
|
|
|
ShouldStop (Event *event_ptr)
|
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
return m_thread.GetProcess()->GetUnixSignals().GetShouldStop (m_value);
|
2010-08-04 09:40:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If should stop returns false, check if we should notify of this event
|
|
|
|
virtual bool
|
|
|
|
ShouldNotify (Event *event_ptr)
|
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
return m_thread.GetProcess()->GetUnixSignals().GetShouldNotify (m_value);
|
2010-08-04 09:40:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
WillResume (lldb::StateType resume_state)
|
|
|
|
{
|
2012-02-21 08:09:25 +08:00
|
|
|
if (m_thread.GetProcess()->GetUnixSignals().GetShouldSuppress(m_value) == false)
|
2010-08-04 09:40:35 +08:00
|
|
|
m_thread.SetResumeSignal(m_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual const char *
|
|
|
|
GetDescription ()
|
|
|
|
{
|
|
|
|
if (m_description.empty())
|
|
|
|
{
|
|
|
|
StreamString strm;
|
2012-02-21 08:09:25 +08:00
|
|
|
const char *signal_name = m_thread.GetProcess()->GetUnixSignals().GetSignalAsCString (m_value);
|
2010-08-04 09:40:35 +08:00
|
|
|
if (signal_name)
|
2010-10-04 09:05:56 +08:00
|
|
|
strm.Printf("signal %s", signal_name);
|
2010-08-04 09:40:35 +08:00
|
|
|
else
|
2010-10-04 09:05:56 +08:00
|
|
|
strm.Printf("signal %lli", m_value);
|
2010-08-04 09:40:35 +08:00
|
|
|
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 ()
|
|
|
|
{
|
2011-06-04 09:26:29 +08:00
|
|
|
if (m_description.empty())
|
2010-08-04 09:40:35 +08:00
|
|
|
return "trace";
|
2011-06-04 09:26:29 +08:00
|
|
|
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();
|
2010-08-04 09:40:35 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// StopInfoThreadPlan
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
class StopInfoThreadPlan : public StopInfo
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
2011-12-17 09:35:57 +08:00
|
|
|
StopInfoThreadPlan (ThreadPlanSP &plan_sp, ValueObjectSP &return_valobj_sp) :
|
2010-08-04 09:40:35 +08:00
|
|
|
StopInfo (plan_sp->GetThread(), LLDB_INVALID_UID),
|
2011-12-17 09:35:57 +08:00
|
|
|
m_plan_sp (plan_sp),
|
|
|
|
m_return_valobj_sp (return_valobj_sp)
|
2010-08-04 09:40:35 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
2011-12-17 09:35:57 +08:00
|
|
|
|
|
|
|
ValueObjectSP
|
|
|
|
GetReturnValueObject()
|
|
|
|
{
|
|
|
|
return m_return_valobj_sp;
|
|
|
|
}
|
2010-08-04 09:40:35 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
ThreadPlanSP m_plan_sp;
|
2011-12-17 09:35:57 +08:00
|
|
|
ValueObjectSP m_return_valobj_sp;
|
2010-08-04 09:40:35 +08:00
|
|
|
};
|
2011-08-09 10:12:22 +08:00
|
|
|
} // namespace lldb_private
|
2010-08-04 09:40:35 +08:00
|
|
|
|
|
|
|
StopInfoSP
|
|
|
|
StopInfo::CreateStopReasonWithBreakpointSiteID (Thread &thread, break_id_t break_id)
|
|
|
|
{
|
|
|
|
return StopInfoSP (new StopInfoBreakpoint (thread, break_id));
|
|
|
|
}
|
|
|
|
|
2010-10-15 07:45:03 +08:00
|
|
|
StopInfoSP
|
|
|
|
StopInfo::CreateStopReasonWithBreakpointSiteID (Thread &thread, break_id_t break_id, bool should_stop)
|
|
|
|
{
|
|
|
|
return StopInfoSP (new StopInfoBreakpoint (thread, break_id, should_stop));
|
|
|
|
}
|
|
|
|
|
2010-08-04 09:40:35 +08:00
|
|
|
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
|
2011-12-17 09:35:57 +08:00
|
|
|
StopInfo::CreateStopReasonWithPlan (ThreadPlanSP &plan_sp, ValueObjectSP return_valobj_sp)
|
2010-08-04 09:40:35 +08:00
|
|
|
{
|
2011-12-17 09:35:57 +08:00
|
|
|
return StopInfoSP (new StopInfoThreadPlan (plan_sp, return_valobj_sp));
|
2010-08-04 09:40:35 +08:00
|
|
|
}
|
2011-06-04 09:26:29 +08:00
|
|
|
|
|
|
|
StopInfoSP
|
|
|
|
StopInfo::CreateStopReasonWithException (Thread &thread, const char *description)
|
|
|
|
{
|
|
|
|
return StopInfoSP (new StopInfoException (thread, description));
|
|
|
|
}
|
2011-12-17 09:35:57 +08:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|