Add a commnad to set a condition for a watchpoint. Example:
watchpoint modify -c 'global==5' modifies the last created watchpoint so that the condition expression is evaluated at the stop point to decide whether we should proceed with the stopping. Also add SBWatchpont::SetCondition(const char *condition) to set condition programmatically. Test cases to come later. llvm-svn: 142227
This commit is contained in:
parent
a7e0b90897
commit
16dcf718d3
|
@ -63,6 +63,12 @@ public:
|
|||
void
|
||||
SetIgnoreCount (uint32_t n);
|
||||
|
||||
const char *
|
||||
GetCondition ();
|
||||
|
||||
void
|
||||
SetCondition (const char *condition);
|
||||
|
||||
bool
|
||||
GetDescription (lldb::SBStream &description, DescriptionLevel level);
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ public:
|
|||
void SetIgnoreCount (uint32_t n);
|
||||
void SetWatchpointType (uint32_t type);
|
||||
bool SetCallback (WatchpointHitCallback callback, void *callback_baton);
|
||||
void ClearCallback();
|
||||
void SetDeclInfo (std::string &str);
|
||||
void GetDescription (Stream *s, lldb::DescriptionLevel level);
|
||||
void Dump (Stream *s) const;
|
||||
|
@ -60,6 +61,39 @@ public:
|
|||
Target &GetTarget() { return *m_target; }
|
||||
const Error &GetError() { return m_error; }
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Invoke the callback action when the watchpoint is hit.
|
||||
///
|
||||
/// @param[in] context
|
||||
/// Described the watchpoint event.
|
||||
///
|
||||
/// @return
|
||||
/// \b true if the target should stop at this watchpoint and \b false not.
|
||||
//------------------------------------------------------------------
|
||||
bool
|
||||
InvokeCallback (StoppointCallbackContext *context);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Condition
|
||||
//------------------------------------------------------------------
|
||||
//------------------------------------------------------------------
|
||||
/// Set the breakpoint's condition.
|
||||
///
|
||||
/// @param[in] condition
|
||||
/// The condition expression to evaluate when the breakpoint is hit.
|
||||
/// Pass in NULL to clear the condition.
|
||||
//------------------------------------------------------------------
|
||||
void SetCondition (const char *condition);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Return a pointer to the text of the condition expression.
|
||||
///
|
||||
/// @return
|
||||
/// A pointer to the condition expression text, or NULL if no
|
||||
// condition has been set.
|
||||
//------------------------------------------------------------------
|
||||
const char *GetConditionText () const;
|
||||
|
||||
private:
|
||||
friend class Target;
|
||||
|
||||
|
@ -78,6 +112,7 @@ private:
|
|||
std::string m_decl_str; // Declaration information, if any.
|
||||
Error m_error; // An error object describing errors creating watchpoint.
|
||||
|
||||
std::auto_ptr<ClangUserExpression> m_condition_ap; // The condition to test.
|
||||
|
||||
static lldb::break_id_t
|
||||
GetNextID();
|
||||
|
|
|
@ -66,6 +66,12 @@ public:
|
|||
void
|
||||
SetIgnoreCount (uint32_t n);
|
||||
|
||||
const char *
|
||||
GetCondition ();
|
||||
|
||||
void
|
||||
SetCondition (const char *condition);
|
||||
|
||||
bool
|
||||
GetDescription (lldb::SBStream &description, DescriptionLevel level);
|
||||
};
|
||||
|
|
|
@ -205,6 +205,27 @@ SBWatchpoint::SetIgnoreCount (uint32_t n)
|
|||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
SBWatchpoint::GetCondition ()
|
||||
{
|
||||
if (m_opaque_sp)
|
||||
{
|
||||
Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
|
||||
return m_opaque_sp->GetConditionText ();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
SBWatchpoint::SetCondition (const char *condition)
|
||||
{
|
||||
if (m_opaque_sp)
|
||||
{
|
||||
Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
|
||||
m_opaque_sp->SetCondition (condition);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SBWatchpoint::GetDescription (SBStream &description, DescriptionLevel level)
|
||||
{
|
||||
|
|
|
@ -13,7 +13,12 @@
|
|||
// C++ Includes
|
||||
// Other libraries and framework includes
|
||||
// Project includes
|
||||
#include "lldb/Breakpoint/StoppointCallbackContext.h"
|
||||
#include "lldb/Core/Stream.h"
|
||||
#include "lldb/Target/Process.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
#include "lldb/Target/ThreadSpec.h"
|
||||
#include "lldb/Target/ThreadPlanTestCondition.h"
|
||||
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
@ -82,16 +87,7 @@ Watchpoint::ShouldStop (StoppointCallbackContext *context)
|
|||
if (m_hit_count <= GetIgnoreCount())
|
||||
return false;
|
||||
|
||||
uint32_t access = 0;
|
||||
if (m_watch_was_read)
|
||||
access |= LLDB_WATCH_TYPE_READ;
|
||||
if (m_watch_was_written)
|
||||
access |= LLDB_WATCH_TYPE_WRITE;
|
||||
|
||||
if (m_callback)
|
||||
return m_callback(m_callback_baton, context, GetID(), access);
|
||||
else
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -124,8 +120,12 @@ Watchpoint::DumpWithLevel(Stream *s, lldb::DescriptionLevel description_level) c
|
|||
m_watch_read ? "r" : "",
|
||||
m_watch_write ? "w" : "");
|
||||
|
||||
if (description_level >= lldb::eDescriptionLevelFull)
|
||||
s->Printf("\n declare @ '%s'", m_decl_str.c_str());
|
||||
if (description_level >= lldb::eDescriptionLevelFull) {
|
||||
if (m_decl_str.c_str())
|
||||
s->Printf("\n declare @ '%s'", m_decl_str.c_str());
|
||||
if (GetConditionText())
|
||||
s->Printf("\n condition = '%s'", GetConditionText());
|
||||
}
|
||||
|
||||
if (description_level >= lldb::eDescriptionLevelVerbose)
|
||||
if (m_callback)
|
||||
|
@ -184,3 +184,44 @@ Watchpoint::SetIgnoreCount (uint32_t n)
|
|||
{
|
||||
m_ignore_count = n;
|
||||
}
|
||||
|
||||
bool
|
||||
Watchpoint::InvokeCallback (StoppointCallbackContext *context)
|
||||
{
|
||||
if (m_callback && context->is_synchronous)
|
||||
{
|
||||
uint32_t access = 0;
|
||||
if (m_watch_was_read)
|
||||
access |= LLDB_WATCH_TYPE_READ;
|
||||
if (m_watch_was_written)
|
||||
access |= LLDB_WATCH_TYPE_WRITE;
|
||||
return m_callback(m_callback_baton, context, GetID(), access);
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Watchpoint::SetCondition (const char *condition)
|
||||
{
|
||||
if (condition == NULL || condition[0] == '\0')
|
||||
{
|
||||
if (m_condition_ap.get())
|
||||
m_condition_ap.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pass NULL for expr_prefix (no translation-unit level definitions).
|
||||
m_condition_ap.reset(new ClangUserExpression (condition, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
Watchpoint::GetConditionText () const
|
||||
{
|
||||
if (m_condition_ap.get())
|
||||
return m_condition_ap->GetUserText();
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -156,18 +156,21 @@ CommandObjectMultiwordWatchpoint::CommandObjectMultiwordWatchpoint(CommandInterp
|
|||
CommandObjectSP disable_command_object (new CommandObjectWatchpointDisable (interpreter));
|
||||
CommandObjectSP delete_command_object (new CommandObjectWatchpointDelete (interpreter));
|
||||
CommandObjectSP ignore_command_object (new CommandObjectWatchpointIgnore (interpreter));
|
||||
CommandObjectSP modify_command_object (new CommandObjectWatchpointModify (interpreter));
|
||||
|
||||
list_command_object->SetCommandName ("watchpoint list");
|
||||
enable_command_object->SetCommandName("watchpoint enable");
|
||||
disable_command_object->SetCommandName("watchpoint disable");
|
||||
delete_command_object->SetCommandName("watchpoint delete");
|
||||
ignore_command_object->SetCommandName("watchpoint ignore");
|
||||
modify_command_object->SetCommandName("watchpoint modify");
|
||||
|
||||
status = LoadSubCommand ("list", list_command_object);
|
||||
status = LoadSubCommand ("enable", enable_command_object);
|
||||
status = LoadSubCommand ("disable", disable_command_object);
|
||||
status = LoadSubCommand ("delete", delete_command_object);
|
||||
status = LoadSubCommand ("ignore", ignore_command_object);
|
||||
status = LoadSubCommand ("modify", modify_command_object);
|
||||
}
|
||||
|
||||
CommandObjectMultiwordWatchpoint::~CommandObjectMultiwordWatchpoint()
|
||||
|
@ -693,3 +696,151 @@ CommandObjectWatchpointIgnore::Execute(Args& args, CommandReturnObject &result)
|
|||
return result.Succeeded();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// CommandObjectWatchpointModify::CommandOptions
|
||||
//-------------------------------------------------------------------------
|
||||
#pragma mark Modify::CommandOptions
|
||||
|
||||
CommandObjectWatchpointModify::CommandOptions::CommandOptions(CommandInterpreter &interpreter) :
|
||||
Options (interpreter),
|
||||
m_condition (),
|
||||
m_condition_passed (false)
|
||||
{
|
||||
}
|
||||
|
||||
CommandObjectWatchpointModify::CommandOptions::~CommandOptions ()
|
||||
{
|
||||
}
|
||||
|
||||
OptionDefinition
|
||||
CommandObjectWatchpointModify::CommandOptions::g_option_table[] =
|
||||
{
|
||||
{ LLDB_OPT_SET_ALL, false, "condition", 'c', required_argument, NULL, NULL, eArgTypeExpression, "The watchpoint stops only if this condition expression evaluates to true."},
|
||||
{ 0, false, NULL, 0 , 0, NULL, 0, eArgTypeNone, NULL }
|
||||
};
|
||||
|
||||
const OptionDefinition*
|
||||
CommandObjectWatchpointModify::CommandOptions::GetDefinitions ()
|
||||
{
|
||||
return g_option_table;
|
||||
}
|
||||
|
||||
Error
|
||||
CommandObjectWatchpointModify::CommandOptions::SetOptionValue (uint32_t option_idx, const char *option_arg)
|
||||
{
|
||||
Error error;
|
||||
char short_option = (char) m_getopt_table[option_idx].val;
|
||||
|
||||
switch (short_option)
|
||||
{
|
||||
case 'c':
|
||||
if (option_arg != NULL)
|
||||
m_condition.assign (option_arg);
|
||||
else
|
||||
m_condition.clear();
|
||||
m_condition_passed = true;
|
||||
break;
|
||||
default:
|
||||
error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option);
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
CommandObjectWatchpointModify::CommandOptions::OptionParsingStarting ()
|
||||
{
|
||||
m_condition.clear();
|
||||
m_condition_passed = false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// CommandObjectWatchpointModify
|
||||
//-------------------------------------------------------------------------
|
||||
#pragma mark Modify
|
||||
|
||||
CommandObjectWatchpointModify::CommandObjectWatchpointModify (CommandInterpreter &interpreter) :
|
||||
CommandObject (interpreter,
|
||||
"watchpoint modify",
|
||||
"Modify the options on a watchpoint or set of watchpoints in the executable. "
|
||||
"If no watchpoint is specified, act on the last created watchpoint. "
|
||||
"Passing an empty argument clears the modification.",
|
||||
NULL),
|
||||
m_options (interpreter)
|
||||
{
|
||||
CommandArgumentEntry arg;
|
||||
CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange);
|
||||
// Add the entry for the first argument for this command to the object's arguments vector.
|
||||
m_arguments.push_back (arg);
|
||||
}
|
||||
|
||||
CommandObjectWatchpointModify::~CommandObjectWatchpointModify ()
|
||||
{
|
||||
}
|
||||
|
||||
Options *
|
||||
CommandObjectWatchpointModify::GetOptions ()
|
||||
{
|
||||
return &m_options;
|
||||
}
|
||||
|
||||
bool
|
||||
CommandObjectWatchpointModify::Execute
|
||||
(
|
||||
Args& args,
|
||||
CommandReturnObject &result
|
||||
)
|
||||
{
|
||||
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
||||
if (!CheckTargetForWatchpointOperations(target, result))
|
||||
return false;
|
||||
|
||||
Mutex::Locker locker;
|
||||
target->GetWatchpointList().GetListMutex(locker);
|
||||
|
||||
const WatchpointList &watchpoints = target->GetWatchpointList();
|
||||
|
||||
size_t num_watchpoints = watchpoints.GetSize();
|
||||
|
||||
if (num_watchpoints == 0)
|
||||
{
|
||||
result.AppendError("No watchpoints exist to be modified.");
|
||||
result.SetStatus(eReturnStatusFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.GetArgumentCount() == 0)
|
||||
{
|
||||
WatchpointSP wp_sp = target->GetLastCreatedWatchpoint();
|
||||
wp_sp->SetCondition(m_options.m_condition.c_str());
|
||||
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Particular watchpoints selected; set condition on them.
|
||||
std::vector<uint32_t> wp_ids;
|
||||
if (!VerifyWatchpointIDs(args, wp_ids))
|
||||
{
|
||||
result.AppendError("Invalid watchpoints specification.");
|
||||
result.SetStatus(eReturnStatusFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
const size_t size = wp_ids.size();
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
WatchpointSP wp_sp = watchpoints.FindByID(wp_ids[i]);
|
||||
if (wp_sp)
|
||||
{
|
||||
wp_sp->SetCondition(m_options.m_condition.c_str());
|
||||
++count;
|
||||
}
|
||||
}
|
||||
result.AppendMessageWithFormat("%d watchpoints modified.\n",count);
|
||||
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
||||
}
|
||||
|
||||
return result.Succeeded();
|
||||
}
|
||||
|
|
|
@ -190,6 +190,58 @@ private:
|
|||
CommandOptions m_options;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// CommandObjectWatchpointModify
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
class CommandObjectWatchpointModify : public CommandObject
|
||||
{
|
||||
public:
|
||||
|
||||
CommandObjectWatchpointModify (CommandInterpreter &interpreter);
|
||||
|
||||
virtual
|
||||
~CommandObjectWatchpointModify ();
|
||||
|
||||
virtual bool
|
||||
Execute (Args& command,
|
||||
CommandReturnObject &result);
|
||||
|
||||
virtual Options *
|
||||
GetOptions ();
|
||||
|
||||
class CommandOptions : public Options
|
||||
{
|
||||
public:
|
||||
|
||||
CommandOptions (CommandInterpreter &interpreter);
|
||||
|
||||
virtual
|
||||
~CommandOptions ();
|
||||
|
||||
virtual Error
|
||||
SetOptionValue (uint32_t option_idx, const char *option_arg);
|
||||
|
||||
void
|
||||
OptionParsingStarting ();
|
||||
|
||||
const OptionDefinition*
|
||||
GetDefinitions ();
|
||||
|
||||
// Options table: Required for subclasses of Options.
|
||||
|
||||
static OptionDefinition g_option_table[];
|
||||
|
||||
// Instance variables to hold the values for command options.
|
||||
|
||||
std::string m_condition;
|
||||
bool m_condition_passed;
|
||||
};
|
||||
|
||||
private:
|
||||
CommandOptions m_options;
|
||||
};
|
||||
|
||||
} // namespace lldb_private
|
||||
|
||||
#endif // liblldb_CommandObjectWatchpoint_h_
|
||||
|
|
|
@ -346,6 +346,12 @@ public:
|
|||
ShouldStop (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;
|
||||
|
||||
|
@ -376,6 +382,118 @@ public:
|
|||
return m_should_stop;
|
||||
}
|
||||
|
||||
// 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 =
|
||||
m_thread.GetProcess().GetTarget().GetWatchpointList().FindByID(GetValue());
|
||||
if (wp_sp)
|
||||
{
|
||||
StoppointCallbackContext context (event_ptr,
|
||||
&m_thread.GetProcess(),
|
||||
&m_thread,
|
||||
m_thread.GetStackFrameAtIndex(0).get(),
|
||||
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_start to false and get out of here.
|
||||
if (GetPrivateState() == eStateRunning)
|
||||
m_should_stop = false;
|
||||
|
||||
if (m_should_stop && !stop_requested)
|
||||
{
|
||||
// We have been vetoed.
|
||||
m_should_stop = false;
|
||||
}
|
||||
|
||||
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.
|
||||
StoppointCallbackContext context (event_ptr,
|
||||
&m_thread.GetProcess(),
|
||||
&m_thread,
|
||||
m_thread.GetStackFrameAtIndex(0).get(),
|
||||
false);
|
||||
ExecutionResults result_code;
|
||||
ValueObjectSP result_value_sp;
|
||||
const bool discard_on_error = true;
|
||||
Error error;
|
||||
result_code = ClangUserExpression::EvaluateWithError (context.exe_ctx,
|
||||
eExecutionPolicyAlways,
|
||||
discard_on_error,
|
||||
wp_sp->GetConditionText(),
|
||||
NULL,
|
||||
result_value_sp,
|
||||
error);
|
||||
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 = context.exe_ctx.GetTargetRef().GetDebugger();
|
||||
StreamSP error_sp = debugger.GetAsyncErrorStream ();
|
||||
error_sp->Printf ("Stopped due to an error evaluating condition of breakpoint ");
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
virtual const char *
|
||||
GetDescription ()
|
||||
{
|
||||
|
|
|
@ -30,8 +30,10 @@ access_pool (uint32_t flag)
|
|||
::pthread_mutex_lock (&g_access_mutex);
|
||||
|
||||
uint32_t old_val = g_val;
|
||||
if (flag != 0)
|
||||
if (flag != 0) {
|
||||
printf("changing g_val to %d...\n", (old_val + 1));
|
||||
g_val = old_val + 1;
|
||||
}
|
||||
|
||||
if (flag == 0)
|
||||
::pthread_mutex_unlock (&g_access_mutex);
|
||||
|
|
Loading…
Reference in New Issue