diff options
author | Jim Ingham <jingham@apple.com> | 2017-08-03 18:13:24 +0000 |
---|---|---|
committer | Jim Ingham <jingham@apple.com> | 2017-08-03 18:13:24 +0000 |
commit | f08f5c99262ff9eaa08956334accbb2614b0f7a2 (patch) | |
tree | 7627308ccb3fc7ce52a6ea0ea45c0d948fcd66af | |
parent | f0cadcd9f385d36dc751cdb32476b32ec43306b5 (diff) | |
download | bcm5719-llvm-f08f5c99262ff9eaa08956334accbb2614b0f7a2.tar.gz bcm5719-llvm-f08f5c99262ff9eaa08956334accbb2614b0f7a2.zip |
Add an auto-continue flag to breakpoints & locations.
You can get a breakpoint to auto-continue by adding "continue"
as a command, but that has the disadvantage that if you hit two
breakpoints simultaneously, the continue will force the process
to continue, and maybe even forstalling the commands on the other.
The auto-continue flag means the breakpoints can negotiate about
whether to stop.
Writing tests, I wanted to supply some commands when I made the
breakpoints, so I also added that ability.
llvm-svn: 309969
-rw-r--r-- | lldb/include/lldb/API/SBBreakpoint.h | 4 | ||||
-rw-r--r-- | lldb/include/lldb/API/SBBreakpointLocation.h | 4 | ||||
-rw-r--r-- | lldb/include/lldb/Breakpoint/Breakpoint.h | 12 | ||||
-rw-r--r-- | lldb/include/lldb/Breakpoint/BreakpointLocation.h | 13 | ||||
-rw-r--r-- | lldb/include/lldb/Breakpoint/BreakpointOptions.h | 33 | ||||
-rw-r--r-- | lldb/include/lldb/lldb-enumerations.h | 4 | ||||
-rw-r--r-- | lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py | 31 | ||||
-rw-r--r-- | lldb/scripts/interface/SBBreakpoint.i | 4 | ||||
-rw-r--r-- | lldb/scripts/interface/SBBreakpointLocation.i | 4 | ||||
-rw-r--r-- | lldb/source/API/SBBreakpoint.cpp | 19 | ||||
-rw-r--r-- | lldb/source/API/SBBreakpointLocation.cpp | 19 | ||||
-rw-r--r-- | lldb/source/Breakpoint/Breakpoint.cpp | 8 | ||||
-rw-r--r-- | lldb/source/Breakpoint/BreakpointLocation.cpp | 13 | ||||
-rw-r--r-- | lldb/source/Breakpoint/BreakpointOptions.cpp | 38 | ||||
-rw-r--r-- | lldb/source/Commands/CommandObjectBreakpoint.cpp | 53 | ||||
-rw-r--r-- | lldb/source/Target/StopInfo.cpp | 43 |
16 files changed, 273 insertions, 29 deletions
diff --git a/lldb/include/lldb/API/SBBreakpoint.h b/lldb/include/lldb/API/SBBreakpoint.h index 9abc9cd39dc..bdf2f650b52 100644 --- a/lldb/include/lldb/API/SBBreakpoint.h +++ b/lldb/include/lldb/API/SBBreakpoint.h @@ -70,6 +70,10 @@ public: const char *GetCondition(); + void SetAutoContinue(bool auto_continue); + + bool GetAutoContinue(); + void SetThreadID(lldb::tid_t sb_thread_id); lldb::tid_t GetThreadID(); diff --git a/lldb/include/lldb/API/SBBreakpointLocation.h b/lldb/include/lldb/API/SBBreakpointLocation.h index c8fa4a1934d..5b942f362ea 100644 --- a/lldb/include/lldb/API/SBBreakpointLocation.h +++ b/lldb/include/lldb/API/SBBreakpointLocation.h @@ -47,6 +47,10 @@ public: void SetCondition(const char *condition); const char *GetCondition(); + + void SetAutoContinue(bool auto_continue); + + bool GetAutoContinue(); void SetScriptCallbackFunction(const char *callback_function_name); diff --git a/lldb/include/lldb/Breakpoint/Breakpoint.h b/lldb/include/lldb/Breakpoint/Breakpoint.h index 41241334dcf..6deaeaad2b9 100644 --- a/lldb/include/lldb/Breakpoint/Breakpoint.h +++ b/lldb/include/lldb/Breakpoint/Breakpoint.h @@ -421,6 +421,18 @@ public: bool IsOneShot() const; //------------------------------------------------------------------ + /// If \a auto_continue is \b true, breakpoint will auto-continue when on hit. + //------------------------------------------------------------------ + void SetAutoContinue(bool auto_continue); + + //------------------------------------------------------------------ + /// Check the AutoContinue state. + /// @return + /// \b true if the breakpoint is set to auto-continue, \b false otherwise. + //------------------------------------------------------------------ + bool IsAutoContinue() const; + + //------------------------------------------------------------------ /// Set the valid thread to be checked when the breakpoint is hit. /// @param[in] thread_id /// If this thread hits the breakpoint, we stop, otherwise not. diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocation.h b/lldb/include/lldb/Breakpoint/BreakpointLocation.h index 2a94f3c2a2b..b68a9ffad04 100644 --- a/lldb/include/lldb/Breakpoint/BreakpointLocation.h +++ b/lldb/include/lldb/Breakpoint/BreakpointLocation.h @@ -107,6 +107,19 @@ public: bool IsEnabled() const; //------------------------------------------------------------------ + /// If \a auto_continue is \b true, set the breakpoint to continue when hit. + //------------------------------------------------------------------ + void SetAutoContinue(bool auto_continue); + + //------------------------------------------------------------------ + /// Check the AutoContinue state. + /// + /// @return + /// \b true if the breakpoint is set to auto-continue, \b false if not. + //------------------------------------------------------------------ + bool IsAutoContinue() const; + + //------------------------------------------------------------------ /// Return the current Ignore Count. /// /// @return diff --git a/lldb/include/lldb/Breakpoint/BreakpointOptions.h b/lldb/include/lldb/Breakpoint/BreakpointOptions.h index cb379a9103a..b0276cb1568 100644 --- a/lldb/include/lldb/Breakpoint/BreakpointOptions.h +++ b/lldb/include/lldb/Breakpoint/BreakpointOptions.h @@ -38,12 +38,13 @@ friend class Breakpoint; public: enum OptionKind { - eCallback = 1 << 0, - eEnabled = 1 << 1, - eOneShot = 1 << 2, - eIgnoreCount = 1 << 3, - eThreadSpec = 1 << 4, - eCondition = 1 << 5 + eCallback = 1 << 0, + eEnabled = 1 << 1, + eOneShot = 1 << 2, + eIgnoreCount = 1 << 3, + eThreadSpec = 1 << 4, + eCondition = 1 << 5, + eAutoContinue = 1 << 6 }; struct CommandData { CommandData() @@ -112,7 +113,8 @@ public: /// //------------------------------------------------------------------ BreakpointOptions(const char *condition, bool enabled = true, - int32_t ignore = 0, bool one_shot = false); + int32_t ignore = 0, bool one_shot = false, + bool auto_continue = false); virtual ~BreakpointOptions(); @@ -296,6 +298,21 @@ public: } //------------------------------------------------------------------ + /// Check the auto-continue state. + /// @return + /// \b true if the breakpoint is set to auto-continue, \b false otherwise. + //------------------------------------------------------------------ + bool IsAutoContinue() const { return m_auto_continue; } + + //------------------------------------------------------------------ + /// Set the auto-continue state. + //------------------------------------------------------------------ + void SetAutoContinue(bool auto_continue) { + m_auto_continue = auto_continue; + m_set_flags.Set(eAutoContinue); + } + + //------------------------------------------------------------------ /// Check the One-shot state. /// @return /// \b true if the breakpoint is one-shot, \b false otherwise. @@ -394,6 +411,7 @@ protected: IgnoreCount, EnabledState, OneShotState, + AutoContinue, LastOptionName }; static const char *g_option_names[(size_t)OptionNames::LastOptionName]; @@ -424,6 +442,7 @@ private: std::string m_condition_text; // The condition to test. size_t m_condition_text_hash; // Its hash, so that locations know when the // condition is updated. + bool m_auto_continue; // If set, auto-continue from breakpoint. Flags m_set_flags; // Which options are set at this level. Drawn // from BreakpointOptions::SetOptionsFlags. }; diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 14bae6ca206..c6cd98806a2 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -380,7 +380,8 @@ FLAGS_ENUM(BreakpointEventType){ eBreakpointEventTypeCommandChanged = (1u << 8), eBreakpointEventTypeConditionChanged = (1u << 9), eBreakpointEventTypeIgnoreChanged = (1u << 10), - eBreakpointEventTypeThreadChanged = (1u << 11)}; + eBreakpointEventTypeThreadChanged = (1u << 11), + eBreakpointEventTypeAutoContinueChanged = (1u << 12)}; FLAGS_ENUM(WatchpointEventType){ eWatchpointEventTypeInvalidType = (1u << 0), @@ -566,6 +567,7 @@ enum CommandArgumentType { eArgTypeWatchpointIDRange, eArgTypeWatchType, eArgRawInput, + eArgTypeCommand, eArgTypeLastArg // Always keep this entry as the last entry in this // enumeration!! }; diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py b/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py index e67a6332d9d..cb4aeadbf93 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py @@ -24,12 +24,21 @@ class BreakpointCommandTestCase(TestBase): cls.RemoveTempFile("output2.txt") @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528") - def test(self): + def test_breakpoint_command_sequence(self): """Test a sequence of breakpoint command add, list, and delete.""" self.build() self.breakpoint_command_sequence() + + @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528") + def test_script_parameters(self): + """Test a sequence of breakpoint command add, list, and delete.""" + self.build() self.breakpoint_command_script_parameters() + def test_commands_on_creation(self): + self.build() + self.breakpoint_commands_on_creation() + def setUp(self): # Call super's setUp(). TestBase.setUp(self) @@ -268,3 +277,23 @@ class BreakpointCommandTestCase(TestBase): # Now remove 'output-2.txt' os.remove('output-2.txt') + + def breakpoint_commands_on_creation(self): + """Test that setting breakpoint commands when creating the breakpoint works""" + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target.IsValid(), "Created an invalid target.") + + # Add a breakpoint. + lldbutil.run_break_set_by_file_and_line( + self, "main.c", self.line, num_expected_locations=1, loc_exact=True, + extra_options='-d bt -d "thread list" -d continue') + + bkpt = target.FindBreakpointByID(1) + self.assertTrue(bkpt.IsValid(), "Couldn't find breakpoint 1") + com_list = lldb.SBStringList() + bkpt.GetCommandLineCommands(com_list) + self.assertEqual(com_list.GetSize(), 3, "Got the wrong number of commands") + self.assertEqual(com_list.GetStringAtIndex(0), "bt", "First bt") + self.assertEqual(com_list.GetStringAtIndex(1), "thread list", "Next thread list") + self.assertEqual(com_list.GetStringAtIndex(2), "continue", "Last continue") diff --git a/lldb/scripts/interface/SBBreakpoint.i b/lldb/scripts/interface/SBBreakpoint.i index e3b1eea0630..76ddd7259e9 100644 --- a/lldb/scripts/interface/SBBreakpoint.i +++ b/lldb/scripts/interface/SBBreakpoint.i @@ -153,6 +153,10 @@ public: const char * GetCondition (); + void SetAutoContinue(bool auto_continue); + + bool GetAutoContinue(); + void SetThreadID (lldb::tid_t sb_thread_id); diff --git a/lldb/scripts/interface/SBBreakpointLocation.i b/lldb/scripts/interface/SBBreakpointLocation.i index 5609c1f84f8..7a9a15db8ff 100644 --- a/lldb/scripts/interface/SBBreakpointLocation.i +++ b/lldb/scripts/interface/SBBreakpointLocation.i @@ -73,6 +73,10 @@ public: const char * GetCondition (); + bool GetAutoContinue(); + + void SetAutoContinue(bool auto_continue); + %feature("docstring", " //------------------------------------------------------------------ /// Set the callback to the given Python function name. diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp index bf9603248d7..c7d6d6c5b18 100644 --- a/lldb/source/API/SBBreakpoint.cpp +++ b/lldb/source/API/SBBreakpoint.cpp @@ -264,6 +264,25 @@ const char *SBBreakpoint::GetCondition() { return nullptr; } +void SBBreakpoint::SetAutoContinue(bool auto_continue) { + BreakpointSP bkpt_sp = GetSP(); + if (bkpt_sp) { + std::lock_guard<std::recursive_mutex> guard( + bkpt_sp->GetTarget().GetAPIMutex()); + bkpt_sp->SetAutoContinue(auto_continue); + } +} + +bool SBBreakpoint::GetAutoContinue() { + BreakpointSP bkpt_sp = GetSP(); + if (bkpt_sp) { + std::lock_guard<std::recursive_mutex> guard( + bkpt_sp->GetTarget().GetAPIMutex()); + return bkpt_sp->IsAutoContinue(); + } + return nullptr; +} + uint32_t SBBreakpoint::GetHitCount() const { uint32_t count = 0; BreakpointSP bkpt_sp = GetSP(); diff --git a/lldb/source/API/SBBreakpointLocation.cpp b/lldb/source/API/SBBreakpointLocation.cpp index f5e0b91483e..2678e1ea758 100644 --- a/lldb/source/API/SBBreakpointLocation.cpp +++ b/lldb/source/API/SBBreakpointLocation.cpp @@ -149,6 +149,25 @@ const char *SBBreakpointLocation::GetCondition() { return NULL; } +void SBBreakpointLocation::SetAutoContinue(bool auto_continue) { + BreakpointLocationSP loc_sp = GetSP(); + if (loc_sp) { + std::lock_guard<std::recursive_mutex> guard( + loc_sp->GetTarget().GetAPIMutex()); + loc_sp->SetAutoContinue(auto_continue); + } +} + +bool SBBreakpointLocation::GetAutoContinue() { + BreakpointLocationSP loc_sp = GetSP(); + if (loc_sp) { + std::lock_guard<std::recursive_mutex> guard( + loc_sp->GetTarget().GetAPIMutex()); + return loc_sp->IsAutoContinue(); + } + return NULL; +} + void SBBreakpointLocation::SetScriptCallbackFunction( const char *callback_function_name) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); diff --git a/lldb/source/Breakpoint/Breakpoint.cpp b/lldb/source/Breakpoint/Breakpoint.cpp index 043e5e8f591..fd606aa83d6 100644 --- a/lldb/source/Breakpoint/Breakpoint.cpp +++ b/lldb/source/Breakpoint/Breakpoint.cpp @@ -348,6 +348,14 @@ void Breakpoint::SetOneShot(bool one_shot) { m_options_up->SetOneShot(one_shot); } +bool Breakpoint::IsAutoContinue() const { + return m_options_up->IsAutoContinue(); +} + +void Breakpoint::SetAutoContinue(bool auto_continue) { + m_options_up->SetAutoContinue(auto_continue); +} + void Breakpoint::SetThreadID(lldb::tid_t thread_id) { if (m_options_up->GetThreadSpec()->GetTID() == thread_id) return; diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp index 15865be3f9a..f59c334fe5c 100644 --- a/lldb/source/Breakpoint/BreakpointLocation.cpp +++ b/lldb/source/Breakpoint/BreakpointLocation.cpp @@ -93,6 +93,19 @@ void BreakpointLocation::SetEnabled(bool enabled) { : eBreakpointEventTypeDisabled); } +bool BreakpointLocation::IsAutoContinue() const { + if (m_options_ap + && m_options_ap->IsOptionSet(BreakpointOptions::eAutoContinue)) + return m_options_ap->IsAutoContinue(); + else + return m_owner.IsAutoContinue(); +} + +void BreakpointLocation::SetAutoContinue(bool auto_continue) { + GetLocationOptions()->SetAutoContinue(auto_continue); + SendBreakpointLocationChangedEvent(eBreakpointEventTypeAutoContinueChanged); +} + void BreakpointLocation::SetThreadID(lldb::tid_t thread_id) { if (thread_id != LLDB_INVALID_THREAD_ID) GetLocationOptions()->SetThreadID(thread_id); diff --git a/lldb/source/Breakpoint/BreakpointOptions.cpp b/lldb/source/Breakpoint/BreakpointOptions.cpp index 3e8d3dd41f3..7159688102d 100644 --- a/lldb/source/Breakpoint/BreakpointOptions.cpp +++ b/lldb/source/Breakpoint/BreakpointOptions.cpp @@ -114,7 +114,8 @@ BreakpointOptions::CommandData::CreateFromStructuredData( const char *BreakpointOptions::g_option_names[( size_t)BreakpointOptions::OptionNames::LastOptionName]{ - "ConditionText", "IgnoreCount", "EnabledState", "OneShotState"}; + "ConditionText", "IgnoreCount", + "EnabledState", "OneShotState", "AutoContinue"}; bool BreakpointOptions::NullCallback(void *baton, StoppointCallbackContext *context, @@ -130,20 +131,22 @@ BreakpointOptions::BreakpointOptions(bool all_flags_set) : m_callback(BreakpointOptions::NullCallback), m_callback_baton_sp(), m_baton_is_command_baton(false), m_callback_is_synchronous(false), m_enabled(true), m_one_shot(false), m_ignore_count(0), m_thread_spec_ap(), - m_condition_text(), m_condition_text_hash(0), + m_condition_text(), m_condition_text_hash(0), m_auto_continue(false), m_set_flags() { if (all_flags_set) m_set_flags.Set(~((Flags::ValueType) 0)); } BreakpointOptions::BreakpointOptions(const char *condition, bool enabled, - int32_t ignore, bool one_shot) + int32_t ignore, bool one_shot, + bool auto_continue) : m_callback(nullptr), m_baton_is_command_baton(false), m_callback_is_synchronous(false), m_enabled(enabled), m_one_shot(one_shot), m_ignore_count(ignore), m_condition_text(condition), - m_condition_text_hash(0) + m_condition_text_hash(0), m_auto_continue(auto_continue) { - m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot | eCondition); + m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot + | eCondition | eAutoContinue); } //---------------------------------------------------------------------- @@ -155,6 +158,7 @@ BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs) m_callback_is_synchronous(rhs.m_callback_is_synchronous), m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot), m_ignore_count(rhs.m_ignore_count), m_thread_spec_ap(), + m_auto_continue(rhs.m_auto_continue), m_set_flags(rhs.m_set_flags) { if (rhs.m_thread_spec_ap.get() != nullptr) m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); @@ -178,6 +182,7 @@ operator=(const BreakpointOptions &rhs) { m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); m_condition_text = rhs.m_condition_text; m_condition_text_hash = rhs.m_condition_text_hash; + m_auto_continue = rhs.m_auto_continue; m_set_flags = rhs.m_set_flags; return *this; } @@ -192,6 +197,7 @@ std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData( Status &error) { bool enabled = true; bool one_shot = false; + bool auto_continue = false; int32_t ignore_count = 0; llvm::StringRef condition_ref(""); Flags set_options; @@ -219,6 +225,17 @@ std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData( set_options.Set(eOneShot); } + key = GetKey(OptionNames::AutoContinue); + if (key) { + success = options_dict.GetValueForKeyAsBoolean(key, auto_continue); + if (!success) { + error.SetErrorStringWithFormat("%s key is not a boolean.", + GetKey(OptionNames::AutoContinue)); + return nullptr; + } + set_options.Set(eAutoContinue); + } + key = GetKey(OptionNames::IgnoreCount); if (key) { success = options_dict.GetValueForKeyAsInteger(key, ignore_count); @@ -257,7 +274,8 @@ std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData( } auto bp_options = llvm::make_unique<BreakpointOptions>( - condition_ref.str().c_str(), enabled, ignore_count, one_shot); + condition_ref.str().c_str(), enabled, + ignore_count, one_shot, auto_continue); if (cmd_data_up.get()) { if (cmd_data_up->interpreter == eScriptLanguageNone) bp_options->SetCommandDataCallback(cmd_data_up); @@ -315,6 +333,9 @@ StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() { if (m_set_flags.Set(eOneShot)) options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState), m_one_shot); + if (m_set_flags.Set(eAutoContinue)) + options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue), + m_auto_continue); if (m_set_flags.Set(eIgnoreCount)) options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount), m_ignore_count); @@ -471,7 +492,7 @@ void BreakpointOptions::GetDescription(Stream *s, // print // anything if there are: - if (m_ignore_count != 0 || !m_enabled || m_one_shot || + if (m_ignore_count != 0 || !m_enabled || m_one_shot || m_auto_continue || (GetThreadSpecNoCreate() != nullptr && GetThreadSpecNoCreate()->HasSpecification())) { if (level == lldb::eDescriptionLevelVerbose) { @@ -491,6 +512,9 @@ void BreakpointOptions::GetDescription(Stream *s, if (m_one_shot) s->Printf("one-shot "); + if (m_auto_continue) + s->Printf("auto-continue "); + if (m_thread_spec_ap.get()) m_thread_spec_ap->GetDescription(s, level); diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp index 266864d1a1f..d53e681514d 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -60,7 +60,9 @@ static OptionDefinition g_breakpoint_set_options[] = { "multiple times to specify multiple shared libraries." }, { LLDB_OPT_SET_ALL, false, "ignore-count", 'i', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCount, "Set the number of times this breakpoint is skipped before stopping." }, { LLDB_OPT_SET_ALL, false, "one-shot", 'o', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "The breakpoint is deleted the first time it causes a stop." }, + { LLDB_OPT_SET_ALL, false, "auto-continue", 'G', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "The breakpoint will auto-continue after running its commands." }, { LLDB_OPT_SET_ALL, false, "condition", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeExpression, "The breakpoint stops only if this condition expression evaluates to true." }, + { LLDB_OPT_SET_ALL, false, "command", 'd', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCommand, "A command to run when the breakpoint is hit, can be provided more than once, the commands will get run in order left to right." }, { LLDB_OPT_SET_ALL, false, "thread-index", 'x', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadIndex, "The breakpoint stops only for the thread whose indeX matches this argument." }, { LLDB_OPT_SET_ALL, false, "thread-id", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadID, "The breakpoint stops only for the thread whose TID matches this argument." }, { LLDB_OPT_SET_ALL, false, "thread-name", 'T', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadName, "The breakpoint stops only for the thread whose thread name matches this " @@ -208,6 +210,10 @@ public: m_condition.assign(option_arg); break; + case 'd': + m_commands.push_back(option_arg); + break; + case 'D': m_use_dummy = true; break; @@ -255,6 +261,15 @@ public: m_func_names.push_back(option_arg); m_func_name_type_mask |= eFunctionNameTypeFull; break; + + case 'G' : { + bool success; + m_auto_continue = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat( + "Invalid boolean value for auto-continue option: '%s'", + option_arg.str().c_str()); + } break; case 'h': { bool success; @@ -445,6 +460,8 @@ public: m_exception_extra_args.Clear(); m_move_to_nearest_code = eLazyBoolCalculate; m_source_regex_func_names.clear(); + m_commands.clear(); + m_auto_continue = false; } llvm::ArrayRef<OptionDefinition> GetDefinitions() override { @@ -482,6 +499,8 @@ public: Args m_exception_extra_args; LazyBool m_move_to_nearest_code; std::unordered_set<std::string> m_source_regex_func_names; + std::vector<std::string> m_commands; + bool m_auto_continue; }; protected: @@ -719,6 +738,18 @@ protected: } bp->SetOneShot(m_options.m_one_shot); + bp->SetAutoContinue(m_options.m_auto_continue); + + if (!m_options.m_commands.empty()) + { + auto cmd_data = llvm::make_unique<BreakpointOptions::CommandData>(); + + for (std::string &str : m_options.m_commands) + cmd_data->user_source.AppendString(str); + + cmd_data->stop_on_error = true; + bp->GetOptions()->SetCommandDataCallback(cmd_data); + } } if (bp) { @@ -802,6 +833,7 @@ static OptionDefinition g_breakpoint_modify_options[] = { { LLDB_OPT_SET_1, false, "enable", 'e', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable the breakpoint." }, { LLDB_OPT_SET_2, false, "disable", 'd', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Disable the breakpoint." }, { LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Sets Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets." }, + { LLDB_OPT_SET_ALL, false, "auto-continue", 'G', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "The breakpoint will auto-continue after running its commands." }, // clang-format on }; @@ -865,6 +897,17 @@ public: m_enable_passed = true; m_enable_value = true; break; + case 'G': { + bool value, success; + value = Args::StringToBoolean(option_arg, false, &success); + if (success) { + m_auto_continue_passed = true; + m_auto_continue = value; + } else + error.SetErrorStringWithFormat( + "invalid boolean value '%s' passed for -G option", + option_arg.str().c_str()); + } break; case 'i': if (option_arg.getAsInteger(0, m_ignore_count)) error.SetErrorStringWithFormat("invalid ignore count '%s'", @@ -938,6 +981,8 @@ public: m_condition_passed = false; m_one_shot_passed = false; m_use_dummy = false; + m_auto_continue = false; + m_auto_continue_passed = false; } llvm::ArrayRef<OptionDefinition> GetDefinitions() override { @@ -962,6 +1007,8 @@ public: bool m_condition_passed; bool m_one_shot_passed; bool m_use_dummy; + bool m_auto_continue; + bool m_auto_continue_passed; }; protected: @@ -1013,6 +1060,9 @@ protected: if (m_options.m_condition_passed) location->SetCondition(m_options.m_condition.c_str()); + + if (m_options.m_auto_continue_passed) + location->SetAutoContinue(m_options.m_auto_continue); } } else { if (m_options.m_thread_id_passed) @@ -1035,6 +1085,9 @@ protected: if (m_options.m_condition_passed) bp->SetCondition(m_options.m_condition.c_str()); + + if (m_options.m_auto_continue_passed) + bp->SetAutoContinue(m_options.m_auto_continue); } } } diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp index 6af5ce1b2eb..8dbcec9fa81 100644 --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -393,7 +393,10 @@ protected: for (size_t j = 0; j < num_owners; j++) { lldb::BreakpointLocationSP bp_loc_sp = site_locations.GetByIndex(j); - + StreamString loc_desc; + if (log) { + bp_loc_sp->GetDescription(&loc_desc, eDescriptionLevelBrief); + } // If another action disabled this breakpoint or its location, then // don't run the actions. if (!bp_loc_sp->IsEnabled() || @@ -405,16 +408,16 @@ protected: // this thread. Skip the ones that aren't: if (!bp_loc_sp->ValidForThisThread(thread_sp.get())) { if (log) { - StreamString s; - bp_loc_sp->GetDescription(&s, eDescriptionLevelBrief); log->Printf("Breakpoint %s hit on thread 0x%llx but it was not " "for this thread, continuing.", - s.GetData(), static_cast<unsigned long long>( + loc_desc.GetData(), static_cast<unsigned long long>( thread_sp->GetID())); } continue; } + internal_breakpoint = bp_loc_sp->GetBreakpoint().IsInternal(); + // First run the precondition, but since the precondition is per // breakpoint, only run it once // per breakpoint. @@ -458,11 +461,10 @@ protected: error_sp->Flush(); } else { if (log) { - StreamString s; - bp_loc_sp->GetDescription(&s, eDescriptionLevelBrief); log->Printf("Condition evaluated for breakpoint %s on thread " "0x%llx conditon_says_stop: %i.", - s.GetData(), static_cast<unsigned long long>( + loc_desc.GetData(), + static_cast<unsigned long long>( thread_sp->GetID()), condition_says_stop); } @@ -477,7 +479,26 @@ protected: } } - bool callback_says_stop; + // Check the auto-continue bit on the location, do this before the + // callback since it may change this, but that would be for the + // NEXT hit. Note, you might think you could check auto-continue + // before the condition, and not evaluate the condition if it says + // to continue. But failing the condition means the breakpoint was + // effectively NOT HIT. So these two states are different. + bool auto_continue_says_stop = true; + if (bp_loc_sp->IsAutoContinue()) + { + if (log) + log->Printf("Continuing breakpoint %s as AutoContinue was set.", + loc_desc.GetData()); + // We want this stop reported, so you will know we auto-continued + // but only for external breakpoints: + if (!internal_breakpoint) + thread_sp->SetShouldReportStop(eVoteYes); + auto_continue_says_stop = false; + } + + bool callback_says_stop = true; // FIXME: For now the callbacks have to run in async mode - the // first time we restart we need @@ -493,11 +514,8 @@ protected: debugger.SetAsyncExecution(old_async); - if (callback_says_stop) + if (callback_says_stop && auto_continue_says_stop) m_should_stop = true; - - if (m_should_stop && !bp_loc_sp->GetBreakpoint().IsInternal()) - internal_breakpoint = false; // If we are going to stop for this breakpoint, then remove the // breakpoint. @@ -506,7 +524,6 @@ protected: thread_sp->GetProcess()->GetTarget().RemoveBreakpointByID( bp_loc_sp->GetBreakpoint().GetID()); } - // 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. |