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:
Johnny Chen 2011-10-17 18:58:00 +00:00
parent a7e0b90897
commit 16dcf718d3
9 changed files with 445 additions and 13 deletions

View File

@ -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);

View File

@ -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();

View File

@ -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);
};

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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_

View File

@ -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 ()
{

View File

@ -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);