diff options
-rw-r--r-- | lldb/include/lldb/API/SBWatchpoint.h | 6 | ||||
-rw-r--r-- | lldb/include/lldb/Breakpoint/Watchpoint.h | 35 | ||||
-rw-r--r-- | lldb/scripts/Python/interface/SBWatchpoint.i | 6 | ||||
-rw-r--r-- | lldb/source/API/SBWatchpoint.cpp | 21 | ||||
-rw-r--r-- | lldb/source/Breakpoint/Watchpoint.cpp | 65 | ||||
-rw-r--r-- | lldb/source/Commands/CommandObjectWatchpoint.cpp | 151 | ||||
-rw-r--r-- | lldb/source/Commands/CommandObjectWatchpoint.h | 52 | ||||
-rw-r--r-- | lldb/source/Target/StopInfo.cpp | 118 | ||||
-rw-r--r-- | lldb/test/functionalities/watchpoint/multiple_threads/main.cpp | 4 |
9 files changed, 445 insertions, 13 deletions
diff --git a/lldb/include/lldb/API/SBWatchpoint.h b/lldb/include/lldb/API/SBWatchpoint.h index 7cfc6233d0b..539dc4147fa 100644 --- a/lldb/include/lldb/API/SBWatchpoint.h +++ b/lldb/include/lldb/API/SBWatchpoint.h @@ -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); diff --git a/lldb/include/lldb/Breakpoint/Watchpoint.h b/lldb/include/lldb/Breakpoint/Watchpoint.h index 71df5b2c50b..1478bfd70c1 100644 --- a/lldb/include/lldb/Breakpoint/Watchpoint.h +++ b/lldb/include/lldb/Breakpoint/Watchpoint.h @@ -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(); diff --git a/lldb/scripts/Python/interface/SBWatchpoint.i b/lldb/scripts/Python/interface/SBWatchpoint.i index 03e7edb3167..75259310271 100644 --- a/lldb/scripts/Python/interface/SBWatchpoint.i +++ b/lldb/scripts/Python/interface/SBWatchpoint.i @@ -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); }; diff --git a/lldb/source/API/SBWatchpoint.cpp b/lldb/source/API/SBWatchpoint.cpp index de43364f049..ec2dc128f15 100644 --- a/lldb/source/API/SBWatchpoint.cpp +++ b/lldb/source/API/SBWatchpoint.cpp @@ -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) { diff --git a/lldb/source/Breakpoint/Watchpoint.cpp b/lldb/source/Breakpoint/Watchpoint.cpp index b5d3f68a8a2..d9bf4337f3e 100644 --- a/lldb/source/Breakpoint/Watchpoint.cpp +++ b/lldb/source/Breakpoint/Watchpoint.cpp @@ -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; +} + diff --git a/lldb/source/Commands/CommandObjectWatchpoint.cpp b/lldb/source/Commands/CommandObjectWatchpoint.cpp index 02b43a31073..a6163df24bb 100644 --- a/lldb/source/Commands/CommandObjectWatchpoint.cpp +++ b/lldb/source/Commands/CommandObjectWatchpoint.cpp @@ -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(); +} diff --git a/lldb/source/Commands/CommandObjectWatchpoint.h b/lldb/source/Commands/CommandObjectWatchpoint.h index 508188eff1a..399aa1a775c 100644 --- a/lldb/source/Commands/CommandObjectWatchpoint.h +++ b/lldb/source/Commands/CommandObjectWatchpoint.h @@ -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_ diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp index 83847478540..c422a1d290b 100644 --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -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 () { diff --git a/lldb/test/functionalities/watchpoint/multiple_threads/main.cpp b/lldb/test/functionalities/watchpoint/multiple_threads/main.cpp index 66c473b75de..534e0a54b89 100644 --- a/lldb/test/functionalities/watchpoint/multiple_threads/main.cpp +++ b/lldb/test/functionalities/watchpoint/multiple_threads/main.cpp @@ -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); |