Add an auto-continue flag to breakpoints & locations.
authorJim Ingham <jingham@apple.com>
Thu, 3 Aug 2017 18:13:24 +0000 (18:13 +0000)
committerJim Ingham <jingham@apple.com>
Thu, 3 Aug 2017 18:13:24 +0000 (18:13 +0000)
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

16 files changed:
lldb/include/lldb/API/SBBreakpoint.h
lldb/include/lldb/API/SBBreakpointLocation.h
lldb/include/lldb/Breakpoint/Breakpoint.h
lldb/include/lldb/Breakpoint/BreakpointLocation.h
lldb/include/lldb/Breakpoint/BreakpointOptions.h
lldb/include/lldb/lldb-enumerations.h
lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py
lldb/scripts/interface/SBBreakpoint.i
lldb/scripts/interface/SBBreakpointLocation.i
lldb/source/API/SBBreakpoint.cpp
lldb/source/API/SBBreakpointLocation.cpp
lldb/source/Breakpoint/Breakpoint.cpp
lldb/source/Breakpoint/BreakpointLocation.cpp
lldb/source/Breakpoint/BreakpointOptions.cpp
lldb/source/Commands/CommandObjectBreakpoint.cpp
lldb/source/Target/StopInfo.cpp

index 9abc9cd..bdf2f65 100644 (file)
@@ -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();
index c8fa4a1..5b942f3 100644 (file)
@@ -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);
 
index 4124133..6deaeaa 100644 (file)
@@ -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.
index 2a94f3c..b68a9ff 100644 (file)
@@ -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
index cb379a9..b0276cb 100644 (file)
@@ -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.
 };
index 14bae6c..c6cd988 100644 (file)
@@ -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!!
 };
index e67a633..cb4aead 100644 (file)
@@ -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")
index e3b1eea..76ddd72 100644 (file)
@@ -153,6 +153,10 @@ public:
     const char *
     GetCondition ();
 
+    void SetAutoContinue(bool auto_continue);
+
+    bool GetAutoContinue();
+
     void
     SetThreadID (lldb::tid_t sb_thread_id);
 
index 5609c1f..7a9a15d 100644 (file)
@@ -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.
index bf96032..c7d6d6c 100644 (file)
@@ -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();
index f5e0b91..2678e1e 100644 (file)
@@ -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));
index 043e5e8..fd606aa 100644 (file)
@@ -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;
index 15865be..f59c334 100644 (file)
@@ -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);
index 3e8d3dd..7159688 100644 (file)
@@ -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);
 
index 266864d..d53e681 100644 (file)
@@ -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);
           }
         }
       }
index 6af5ce1..8dbcec9 100644 (file)
@@ -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.