jingham created this revision.
jingham added reviewers: JDevlieghere, clayborg.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

For example, it is pretty easy to write a breakpoint command that implements 
"stop when my caller is Foo", and it is pretty easy to write a breakpoint 
command that implements "stop when my caller is Bar".  But there's no way to 
write a generic "stop when my caller is..." function, and then specify the 
caller when you add the command.  With this patch, you can pass this data in a 
SBStructuredData dictionary.  That will get stored in the PythonCommandBaton 
for the breakpoint, and passed to the implementation function (if it has the 
right signature) when the breakpoint is hit.  Then in lldb, you can say:

(lldb) break com add -F caller_is -k caller_name -v Foo

More generally this will allow us to write reusable Python breakpoint commands.


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D68671

Files:
  lldb/include/lldb/API/SBBreakpoint.h
  lldb/include/lldb/API/SBBreakpointLocation.h
  lldb/include/lldb/API/SBBreakpointName.h
  lldb/include/lldb/API/SBStructuredData.h
  lldb/include/lldb/Interpreter/OptionGroupPythonClassWithDict.h
  lldb/include/lldb/Interpreter/ScriptInterpreter.h
  
lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py
  
lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py
  lldb/scripts/Python/python-wrapper.swig
  lldb/scripts/interface/SBBreakpoint.i
  lldb/scripts/interface/SBBreakpointLocation.i
  lldb/scripts/interface/SBBreakpointName.i
  lldb/source/API/SBBreakpoint.cpp
  lldb/source/API/SBBreakpointLocation.cpp
  lldb/source/API/SBBreakpointName.cpp
  lldb/source/Commands/CommandObjectBreakpoint.cpp
  lldb/source/Commands/CommandObjectBreakpointCommand.cpp
  lldb/source/Commands/CommandObjectThread.cpp
  lldb/source/Commands/Options.td
  lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp
  lldb/source/Interpreter/ScriptInterpreter.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
  lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp

Index: lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
===================================================================
--- lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -62,7 +62,8 @@
 extern "C" bool LLDBSwigPythonBreakpointCallbackFunction(
     const char *python_function_name, const char *session_dictionary_name,
     const lldb::StackFrameSP &sb_frame,
-    const lldb::BreakpointLocationSP &sb_bp_loc) {
+    const lldb::BreakpointLocationSP &sb_bp_loc,
+    StructuredDataImpl *args_impl) {
   return false;
 }
 
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -179,8 +179,10 @@
   Status GenerateFunction(const char *signature,
                           const StringList &input) override;
 
-  Status GenerateBreakpointCommandCallbackData(StringList &input,
-                                               std::string &output) override;
+  Status GenerateBreakpointCommandCallbackData(
+      StringList &input,
+      std::string &output,
+      StructuredData::ObjectSP extra_args_sp) override;
 
   bool GenerateWatchpointCommandCallbackData(StringList &input,
                                              std::string &output) override;
@@ -244,14 +246,21 @@
   Status SetBreakpointCommandCallback(BreakpointOptions *bp_options,
                                       const char *callback_body) override;
 
-  void SetBreakpointCommandCallbackFunction(BreakpointOptions *bp_options,
-                                            const char *function_name) override;
+  void SetBreakpointCommandCallbackFunction(
+      BreakpointOptions *bp_options,
+      const char *function_name,
+      StructuredData::ObjectSP extra_args_sp) override;
 
   /// This one is for deserialization:
   Status SetBreakpointCommandCallback(
       BreakpointOptions *bp_options,
       std::unique_ptr<BreakpointOptions::CommandData> &data_up) override;
 
+  Status SetBreakpointCommandCallback(
+      BreakpointOptions *bp_options, 
+       const char *command_body_text,
+       StructuredData::ObjectSP extra_args_sp);
+
   /// Set a one-liner as the callback for the watchpoint.
   void SetWatchpointCommandCallback(WatchpointOptions *wp_options,
                                     const char *oneliner) override;
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
@@ -17,6 +17,7 @@
 
 #include "lldb/Breakpoint/BreakpointOptions.h"
 #include "lldb/Core/IOHandler.h"
+#include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Interpreter/ScriptInterpreter.h"
 #include "lldb/lldb-private.h"
 
@@ -34,6 +35,13 @@
     CommandDataPython() : BreakpointOptions::CommandData() {
       interpreter = lldb::eScriptLanguagePython;
     }
+    CommandDataPython(StructuredData::ObjectSP extra_args_sp) :
+        BreakpointOptions::CommandData(),
+        m_extra_args_up(new StructuredDataImpl()) {
+        interpreter = lldb::eScriptLanguagePython;
+        m_extra_args_up->SetObjectSP(extra_args_sp);
+    }
+    lldb::StructuredDataImplUP m_extra_args_up;
   };
 
   ScriptInterpreterPython(Debugger &debugger)
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -74,7 +74,8 @@
 extern "C" bool LLDBSwigPythonBreakpointCallbackFunction(
     const char *python_function_name, const char *session_dictionary_name,
     const lldb::StackFrameSP &sb_frame,
-    const lldb::BreakpointLocationSP &sb_bp_loc);
+    const lldb::BreakpointLocationSP &sb_bp_loc,
+    StructuredDataImpl *args_impl);
 
 extern "C" bool LLDBSwigPythonWatchpointCallbackFunction(
     const char *python_function_name, const char *session_dictionary_name,
@@ -551,8 +552,10 @@
         break;
       data_up->user_source.SplitIntoLines(data);
 
+      StructuredData::ObjectSP empty_args_sp;
       if (GenerateBreakpointCommandCallbackData(data_up->user_source,
-                                                data_up->script_source)
+                                                data_up->script_source,
+                                                empty_args_sp)
               .Success()) {
         auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>(
             std::move(data_up));
@@ -1258,21 +1261,29 @@
 }
 
 void ScriptInterpreterPythonImpl::SetBreakpointCommandCallbackFunction(
-    BreakpointOptions *bp_options, const char *function_name) {
+    BreakpointOptions *bp_options, const char *function_name,
+    StructuredData::ObjectSP extra_args_sp) {
   // For now just cons up a oneliner that calls the provided function.
   std::string oneliner("return ");
   oneliner += function_name;
-  oneliner += "(frame, bp_loc, internal_dict)";
-  m_debugger.GetScriptInterpreter()->SetBreakpointCommandCallback(
-      bp_options, oneliner.c_str());
+  
+  if (extra_args_sp && extra_args_sp->GetAsDictionary()
+      && extra_args_sp->GetAsDictionary()->GetSize() > 0)
+    oneliner += "(frame, bp_loc, extra_args, internal_dict)";
+  else
+    oneliner += "(frame, bp_loc, internal_dict)";
+
+  SetBreakpointCommandCallback(bp_options, oneliner.c_str(), extra_args_sp);
 }
 
 Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
     BreakpointOptions *bp_options,
     std::unique_ptr<BreakpointOptions::CommandData> &cmd_data_up) {
   Status error;
+  StructuredData::ObjectSP empty_args_sp;
   error = GenerateBreakpointCommandCallbackData(cmd_data_up->user_source,
-                                                cmd_data_up->script_source);
+                                                cmd_data_up->script_source,
+                                                empty_args_sp);
   if (error.Fail()) {
     return error;
   }
@@ -1283,11 +1294,16 @@
   return error;
 }
 
-// Set a Python one-liner as the callback for the breakpoint.
 Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
     BreakpointOptions *bp_options, const char *command_body_text) {
-  auto data_up = std::make_unique<CommandDataPython>();
+  return SetBreakpointCommandCallback(bp_options, command_body_text, {});
+}
 
+// Set a Python one-liner as the callback for the breakpoint.
+Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
+    BreakpointOptions *bp_options, const char *command_body_text,
+    StructuredData::ObjectSP extra_args_sp) {
+  auto data_up = std::make_unique<CommandDataPython>(extra_args_sp);
   // Split the command_body_text into lines, and pass that to
   // GenerateBreakpointCommandCallbackData.  That will wrap the body in an
   // auto-generated function, and return the function name in script_source.
@@ -1295,7 +1311,8 @@
 
   data_up->user_source.SplitIntoLines(command_body_text);
   Status error = GenerateBreakpointCommandCallbackData(data_up->user_source,
-                                                       data_up->script_source);
+                                                       data_up->script_source,
+                                                       extra_args_sp);
   if (error.Success()) {
     auto baton_sp =
         std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up));
@@ -2117,7 +2134,8 @@
 }
 
 Status ScriptInterpreterPythonImpl::GenerateBreakpointCommandCallbackData(
-    StringList &user_input, std::string &output) {
+    StringList &user_input, std::string &output,
+    StructuredData::ObjectSP extra_args_sp) {
   static uint32_t num_created_functions = 0;
   user_input.RemoveBlankLines();
   StreamString sstr;
@@ -2129,8 +2147,13 @@
 
   std::string auto_generated_function_name(GenerateUniqueName(
       "lldb_autogen_python_bp_callback_func_", num_created_functions));
-  sstr.Printf("def %s (frame, bp_loc, internal_dict):",
-              auto_generated_function_name.c_str());
+  if (extra_args_sp && extra_args_sp->GetAsDictionary()
+      && extra_args_sp->GetAsDictionary()->GetSize()) 
+    sstr.Printf("def %s (frame, bp_loc, extra_args, internal_dict):",
+                auto_generated_function_name.c_str());
+  else
+    sstr.Printf("def %s (frame, bp_loc, internal_dict):",
+                auto_generated_function_name.c_str());
 
   error = GenerateFunction(sstr.GetData(), user_input);
   if (!error.Success())
@@ -2250,7 +2273,8 @@
           ret_val = LLDBSwigPythonBreakpointCallbackFunction(
               python_function_name,
               python_interpreter->m_dictionary_name.c_str(), stop_frame_sp,
-              bp_loc_sp);
+              bp_loc_sp,
+              bp_option_data->m_extra_args_up.get());
         }
         return ret_val;
       }
Index: lldb/source/Interpreter/ScriptInterpreter.cpp
===================================================================
--- lldb/source/Interpreter/ScriptInterpreter.cpp
+++ lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -83,9 +83,11 @@
 
 void ScriptInterpreter::SetBreakpointCommandCallbackFunction(
     std::vector<BreakpointOptions *> &bp_options_vec,
-    const char *function_name) {
+    const char *function_name,
+    StructuredData::ObjectSP extra_args_sp) {
   for (BreakpointOptions *bp_options : bp_options_vec) {
-    SetBreakpointCommandCallbackFunction(bp_options, function_name);
+    SetBreakpointCommandCallbackFunction(bp_options, function_name, 
+                                         extra_args_sp);
   }
 }
 
Index: lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp
===================================================================
--- lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp
+++ lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp
@@ -15,30 +15,29 @@
 
 OptionGroupPythonClassWithDict::OptionGroupPythonClassWithDict
     (const char *class_use,
+     bool is_class,
      int class_option,
      int key_option, 
-     int value_option,
-     const char *class_long_option,
-     const char *key_long_option,
-     const char *value_long_option,
-     bool required) {
-  m_key_usage_text.assign("The key for a key/value pair passed to the class"
-                            " that implements a ");
+     int value_option) : OptionGroup(), m_is_class(is_class) {
+  m_key_usage_text.assign("The key for a key/value pair passed to the "
+                          "implementation of a ");
   m_key_usage_text.append(class_use);
   m_key_usage_text.append(".  Pairs can be specified more than once.");
   
-  m_value_usage_text.assign("The value for a previous key in the pair passed to"
-                            " the class that implements a ");
+  m_value_usage_text.assign("The value for the previous key in the pair passed "
+                            "to the implementation of a ");
   m_value_usage_text.append(class_use);
   m_value_usage_text.append(".  Pairs can be specified more than once.");
   
-  m_class_usage_text.assign("The name of the class that will manage a ");
+  m_class_usage_text.assign("The name of the ");
+  m_class_usage_text.append(m_is_class ? "class" : "function");
+  m_class_usage_text.append(" that will manage a ");
   m_class_usage_text.append(class_use);
   m_class_usage_text.append(".");
   
   m_option_definition[0].usage_mask = LLDB_OPT_SET_1;
-  m_option_definition[0].required = required;
-  m_option_definition[0].long_option = class_long_option;
+  m_option_definition[0].required = true;
+  m_option_definition[0].long_option = "script-class";
   m_option_definition[0].short_option = class_option;
   m_option_definition[0].validator = nullptr;
   m_option_definition[0].option_has_arg = OptionParser::eRequiredArgument;
@@ -47,9 +46,9 @@
   m_option_definition[0].argument_type = eArgTypePythonClass;
   m_option_definition[0].usage_text = m_class_usage_text.data();
 
-  m_option_definition[1].usage_mask = LLDB_OPT_SET_1;
-  m_option_definition[1].required = required;
-  m_option_definition[1].long_option = key_long_option;
+  m_option_definition[1].usage_mask = LLDB_OPT_SET_2;
+  m_option_definition[1].required = false;
+  m_option_definition[1].long_option = "structured-data-key";
   m_option_definition[1].short_option = key_option;
   m_option_definition[1].validator = nullptr;
   m_option_definition[1].option_has_arg = OptionParser::eRequiredArgument;
@@ -58,9 +57,9 @@
   m_option_definition[1].argument_type = eArgTypeNone;
   m_option_definition[1].usage_text = m_key_usage_text.data();
 
-  m_option_definition[2].usage_mask = LLDB_OPT_SET_1;
-  m_option_definition[2].required = required;
-  m_option_definition[2].long_option = value_long_option;
+  m_option_definition[2].usage_mask = LLDB_OPT_SET_2;
+  m_option_definition[2].required = false;
+  m_option_definition[2].long_option = "structured-data-value";
   m_option_definition[2].short_option = value_option;
   m_option_definition[2].validator = nullptr;
   m_option_definition[2].option_has_arg = OptionParser::eRequiredArgument;
@@ -68,6 +67,18 @@
   m_option_definition[2].completion_type = 0;
   m_option_definition[2].argument_type = eArgTypeNone;
   m_option_definition[2].usage_text = m_value_usage_text.data();
+  
+  m_option_definition[3].usage_mask = LLDB_OPT_SET_3;
+  m_option_definition[3].required = true;
+  m_option_definition[3].long_option = "script-function";
+  m_option_definition[3].short_option = class_option;
+  m_option_definition[3].validator = nullptr;
+  m_option_definition[3].option_has_arg = OptionParser::eRequiredArgument;
+  m_option_definition[3].enum_values = {};
+  m_option_definition[3].completion_type = 0;
+  m_option_definition[3].argument_type = eArgTypePythonFunction;
+  m_option_definition[3].usage_text = m_class_usage_text.data();
+
 }
 
 OptionGroupPythonClassWithDict::~OptionGroupPythonClassWithDict() {}
@@ -79,7 +90,7 @@
   Status error;
   switch (option_idx) {
   case 0: {
-    m_class_name.assign(option_arg);
+    m_name.assign(option_arg);
   } break;
   case 1: {
       if (m_current_key.empty())
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -268,10 +268,6 @@
     EnumArg<"None", "ScriptOptionEnum()">,
     Desc<"Specify the language for the commands - if none is specified, the "
     "lldb command interpreter will be used.">;
-  def breakpoint_add_python_function : Option<"python-function", "F">,
-    Group<2>, Arg<"PythonFunction">,
-    Desc<"Give the name of a Python function to run as command for this "
-    "breakpoint. Be sure to give a module name if appropriate.">;
   def breakpoint_add_dummy_breakpoints : Option<"dummy-breakpoints", "D">,
     Desc<"Sets Dummy breakpoints - i.e. breakpoints set before a file is "
     "provided, which prime new targets.">;
@@ -904,9 +900,6 @@
   def thread_step_scope_step_in_target : Option<"step-in-target", "t">,
     Group<1>, Arg<"FunctionName">, Desc<"The name of the directly called "
     "function step in should stop at when stepping into.">;
-  def thread_step_scope_python_class : Option<"python-class", "C">, Group<2>,
-    Arg<"PythonClass">, Desc<"The name of the class that will manage this step "
-    "- only supported for Scripted Step.">;
 }
 
 let Command = "thread until" in {
Index: lldb/source/Commands/CommandObjectThread.cpp
===================================================================
--- lldb/source/Commands/CommandObjectThread.cpp
+++ lldb/source/Commands/CommandObjectThread.cpp
@@ -546,7 +546,9 @@
     m_arguments.push_back(arg);
     
     if (step_type == eStepTypeScripted) {
-      m_all_options.Append(&m_class_options, LLDB_OPT_SET_1, LLDB_OPT_SET_1);
+      m_all_options.Append(&m_class_options, 
+                           LLDB_OPT_SET_1 | LLDB_OPT_SET_2, 
+                           LLDB_OPT_SET_1);
     }
     m_all_options.Append(&m_options);
     m_all_options.Finalize();
@@ -596,15 +598,15 @@
     }
 
     if (m_step_type == eStepTypeScripted) {
-      if (m_class_options.GetClassName().empty()) {
+      if (m_class_options.GetName().empty()) {
         result.AppendErrorWithFormat("empty class name for scripted step.");
         result.SetStatus(eReturnStatusFailed);
         return false;
       } else if (!GetDebugger().GetScriptInterpreter()->CheckObjectExists(
-                     m_class_options.GetClassName().c_str())) {
+                     m_class_options.GetName().c_str())) {
         result.AppendErrorWithFormat(
             "class for scripted step: \"%s\" does not exist.",
-            m_class_options.GetClassName().c_str());
+            m_class_options.GetName().c_str());
         result.SetStatus(eReturnStatusFailed);
         return false;
       }
@@ -720,7 +722,7 @@
           m_options.m_step_out_avoid_no_debug);
     } else if (m_step_type == eStepTypeScripted) {
       new_plan_sp = thread->QueueThreadPlanForStepScripted(
-          abort_other_plans, m_class_options.GetClassName().c_str(), 
+          abort_other_plans, m_class_options.GetName().c_str(), 
           m_class_options.GetStructuredData(),
           bool_stop_other_threads, new_plan_status);
     } else {
Index: lldb/source/Commands/CommandObjectBreakpointCommand.cpp
===================================================================
--- lldb/source/Commands/CommandObjectBreakpointCommand.cpp
+++ lldb/source/Commands/CommandObjectBreakpointCommand.cpp
@@ -17,6 +17,7 @@
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/CommandReturnObject.h"
 #include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Target/Thread.h"
 #include "lldb/Utility/State.h"
@@ -66,7 +67,7 @@
                             nullptr),
         IOHandlerDelegateMultiline("DONE",
                                    IOHandlerDelegate::Completion::LLDBCommand),
-        m_options() {
+        m_options(), m_func_options("breakpoint command", false, 'F') {
     SetHelpLong(
         R"(
 General information about entering breakpoint commands
@@ -201,6 +202,11 @@
         "Final Note: A warning that no breakpoint command was generated when there \
 are no syntax errors may indicate that a function was declared but never called.");
 
+    m_all_options.Append(&m_options);
+    m_all_options.Append(&m_func_options, LLDB_OPT_SET_2 | LLDB_OPT_SET_3, 
+                         LLDB_OPT_SET_2);
+    m_all_options.Finalize();
+    
     CommandArgumentEntry arg;
     CommandArgumentData bp_id_arg;
 
@@ -218,7 +224,7 @@
 
   ~CommandObjectBreakpointCommandAdd() override = default;
 
-  Options *GetOptions() override { return &m_options; }
+  Options *GetOptions() override { return &m_all_options; }
 
   void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
     StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
@@ -269,19 +275,20 @@
     }
   }
 
-  class CommandOptions : public Options {
+  class CommandOptions : public OptionGroup {
   public:
     CommandOptions()
-        : Options(), m_use_commands(false), m_use_script_language(false),
+        : OptionGroup(), m_use_commands(false), m_use_script_language(false),
           m_script_language(eScriptLanguageNone), m_use_one_liner(false),
-          m_one_liner(), m_function_name() {}
+          m_one_liner() {}
 
     ~CommandOptions() override = default;
 
     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                           ExecutionContext *execution_context) override {
       Status error;
-      const int short_option = m_getopt_table[option_idx].val;
+      const int short_option 
+          = g_breakpoint_command_add_options[option_idx].short_option;
 
       switch (short_option) {
       case 'o':
@@ -313,12 +320,6 @@
               option_arg.str().c_str());
       } break;
 
-      case 'F':
-        m_use_one_liner = false;
-        m_use_script_language = true;
-        m_function_name.assign(option_arg);
-        break;
-
       case 'D':
         m_use_dummy = true;
         break;
@@ -337,7 +338,6 @@
       m_use_one_liner = false;
       m_stop_on_error = true;
       m_one_liner.clear();
-      m_function_name.clear();
       m_use_dummy = false;
     }
 
@@ -355,7 +355,6 @@
     bool m_use_one_liner;
     std::string m_one_liner;
     bool m_stop_on_error;
-    std::string m_function_name;
     bool m_use_dummy;
   };
 
@@ -372,12 +371,9 @@
       return false;
     }
 
-    if (!m_options.m_use_script_language &&
-        !m_options.m_function_name.empty()) {
-      result.AppendError("need to enable scripting to have a function run as a "
-                         "breakpoint command");
-      result.SetStatus(eReturnStatusFailed);
-      return false;
+    if (!m_func_options.GetName().empty()) {
+      m_options.m_use_one_liner = false;
+      m_options.m_use_script_language = true;
     }
 
     BreakpointIDList valid_bp_ids;
@@ -421,9 +417,10 @@
         if (m_options.m_use_one_liner) {
           script_interp->SetBreakpointCommandCallback(
               m_bp_options_vec, m_options.m_one_liner.c_str());
-        } else if (!m_options.m_function_name.empty()) {
+        } else if (!m_func_options.GetName().empty()) {
           script_interp->SetBreakpointCommandCallbackFunction(
-              m_bp_options_vec, m_options.m_function_name.c_str());
+              m_bp_options_vec, m_func_options.GetName().c_str(),
+              m_func_options.GetStructuredData());
         } else {
           script_interp->CollectDataForBreakpointCommandCallback(
               m_bp_options_vec, result);
@@ -443,6 +440,9 @@
 
 private:
   CommandOptions m_options;
+  OptionGroupPythonClassWithDict m_func_options;
+  OptionGroupOptions m_all_options;
+
   std::vector<BreakpointOptions *> m_bp_options_vec; // This stores the
                                                      // breakpoint options that
                                                      // we are currently
Index: lldb/source/Commands/CommandObjectBreakpoint.cpp
===================================================================
--- lldb/source/Commands/CommandObjectBreakpoint.cpp
+++ lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -242,10 +242,10 @@
             interpreter, "breakpoint set",
             "Sets a breakpoint or set of breakpoints in the executable.",
             "breakpoint set <cmd-options>"),
-        m_bp_opts(), m_python_class_options("scripted breakpoint", 'P'),
+        m_bp_opts(), m_python_class_options("scripted breakpoint", true, 'P'),
         m_options() {
     // We're picking up all the normal options, commands and disable.
-    m_all_options.Append(&m_python_class_options, LLDB_OPT_SET_1,
+    m_all_options.Append(&m_python_class_options, LLDB_OPT_SET_1|LLDB_OPT_SET_2,
                          LLDB_OPT_SET_11);
     m_all_options.Append(&m_bp_opts,
                          LLDB_OPT_SET_1 | LLDB_OPT_SET_3 | LLDB_OPT_SET_4,
@@ -543,7 +543,7 @@
 
     BreakpointSetType break_type = eSetTypeInvalid;
 
-    if (!m_python_class_options.GetClassName().empty())
+    if (!m_python_class_options.GetName().empty())
       break_type = eSetTypeScripted;
     else if (m_options.m_line_num != 0)
       break_type = eSetTypeFileAndLine;
@@ -699,7 +699,7 @@
 
       Status error;
       bp_sp = target.CreateScriptedBreakpoint(
-          m_python_class_options.GetClassName().c_str(), &(m_options.m_modules),
+          m_python_class_options.GetName().c_str(), &(m_options.m_modules),
           &(m_options.m_filenames), false, m_options.m_hardware,
           m_python_class_options.GetStructuredData(), &error);
       if (error.Fail()) {
Index: lldb/source/API/SBBreakpointName.cpp
===================================================================
--- lldb/source/API/SBBreakpointName.cpp
+++ lldb/source/API/SBBreakpointName.cpp
@@ -12,11 +12,13 @@
 #include "lldb/API/SBError.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBStringList.h"
+#include "lldb/API/SBStructuredData.h"
 #include "lldb/API/SBTarget.h"
 
 #include "lldb/Breakpoint/BreakpointName.h"
 #include "lldb/Breakpoint/StoppointCallbackContext.h"
 #include "lldb/Core/Debugger.h"
+#include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/ScriptInterpreter.h"
 #include "lldb/Target/Target.h"
@@ -565,9 +567,19 @@
 }
 
 void SBBreakpointName::SetScriptCallbackFunction(
-    const char *callback_function_name) {
+  const char *callback_function_name) {
+LLDB_RECORD_METHOD(void, SBBreakpointName, SetScriptCallbackFunction,
+                   (const char *), callback_function_name);
+  SBStructuredData empty_args;
+  SetScriptCallbackFunction(callback_function_name, empty_args);
+}
+
+void SBBreakpointName::SetScriptCallbackFunction(
+    const char *callback_function_name, 
+    SBStructuredData &extra_args) {
   LLDB_RECORD_METHOD(void, SBBreakpointName, SetScriptCallbackFunction,
-                     (const char *), callback_function_name);
+                     (const char *, SBStructuredData &), 
+                     callback_function_name, extra_args);
 
   BreakpointName *bp_name = GetBreakpointName();
   if (!bp_name)
@@ -581,7 +593,9 @@
       ->GetDebugger()
       .GetScriptInterpreter()
       ->SetBreakpointCommandCallbackFunction(&bp_options,
-                                             callback_function_name);
+                                             callback_function_name,
+                                             extra_args.m_impl_up
+                                                 ->GetObjectSP());
   UpdateName(*bp_name);
 }
 
@@ -728,6 +742,8 @@
                        (lldb::SBStream &));
   LLDB_REGISTER_METHOD(void, SBBreakpointName, SetScriptCallbackFunction,
                        (const char *));
+  LLDB_REGISTER_METHOD(void, SBBreakpointName, SetScriptCallbackFunction,
+                       (const char *, SBStructuredData &));
   LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpointName, SetScriptCallbackBody,
                        (const char *));
   LLDB_REGISTER_METHOD_CONST(bool, SBBreakpointName, GetAllowList, ());
Index: lldb/source/API/SBBreakpointLocation.cpp
===================================================================
--- lldb/source/API/SBBreakpointLocation.cpp
+++ lldb/source/API/SBBreakpointLocation.cpp
@@ -12,12 +12,14 @@
 #include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBDefines.h"
 #include "lldb/API/SBStream.h"
+#include "lldb/API/SBStructuredData.h"
 #include "lldb/API/SBStringList.h"
 
 #include "lldb/Breakpoint/Breakpoint.h"
 #include "lldb/Breakpoint/BreakpointLocation.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/StreamFile.h"
+#include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/ScriptInterpreter.h"
 #include "lldb/Target/Target.h"
@@ -207,9 +209,17 @@
 }
 
 void SBBreakpointLocation::SetScriptCallbackFunction(
-    const char *callback_function_name) {
+  const char *callback_function_name) {
+LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction,
+                   (const char *), callback_function_name);
+}
+
+void SBBreakpointLocation::SetScriptCallbackFunction(
+    const char *callback_function_name,
+    SBStructuredData &extra_args) {
   LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction,
-                     (const char *), callback_function_name);
+                     (const char *, SBStructuredData &), 
+                     callback_function_name, extra_args);
 
   BreakpointLocationSP loc_sp = GetSP();
 
@@ -222,7 +232,9 @@
         .GetDebugger()
         .GetScriptInterpreter()
         ->SetBreakpointCommandCallbackFunction(bp_options,
-                                               callback_function_name);
+                                               callback_function_name,
+                                               extra_args.m_impl_up
+                                                   ->GetObjectSP());
   }
 }
 
@@ -482,6 +494,8 @@
   LLDB_REGISTER_METHOD(bool, SBBreakpointLocation, GetAutoContinue, ());
   LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction,
                        (const char *));
+  LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction,
+                       (const char *, SBStructuredData &));
   LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpointLocation,
                        SetScriptCallbackBody, (const char *));
   LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetCommandLineCommands,
Index: lldb/source/API/SBBreakpoint.cpp
===================================================================
--- lldb/source/API/SBBreakpoint.cpp
+++ lldb/source/API/SBBreakpoint.cpp
@@ -14,6 +14,7 @@
 #include "lldb/API/SBProcess.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBStringList.h"
+#include "lldb/API/SBStructuredData.h"
 #include "lldb/API/SBThread.h"
 
 #include "lldb/Breakpoint/Breakpoint.h"
@@ -25,6 +26,7 @@
 #include "lldb/Core/Address.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/StreamFile.h"
+#include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/ScriptInterpreter.h"
 #include "lldb/Target/Process.h"
@@ -590,9 +592,18 @@
 }
 
 void SBBreakpoint::SetScriptCallbackFunction(
-    const char *callback_function_name) {
+  const char *callback_function_name) {
+LLDB_RECORD_METHOD(void, SBBreakpoint, SetScriptCallbackFunction,
+                   (const char *), callback_function_name);
+  SBStructuredData empty_args;
+  SetScriptCallbackFunction(callback_function_name, empty_args);
+}
+
+void SBBreakpoint::SetScriptCallbackFunction(
+    const char *callback_function_name,
+    SBStructuredData &extra_args) {
   LLDB_RECORD_METHOD(void, SBBreakpoint, SetScriptCallbackFunction,
-                     (const char *), callback_function_name);
+  (const char *, SBStructuredData &), callback_function_name, extra_args);
 
   BreakpointSP bkpt_sp = GetSP();
 
@@ -604,7 +615,9 @@
         .GetDebugger()
         .GetScriptInterpreter()
         ->SetBreakpointCommandCallbackFunction(bp_options,
-                                               callback_function_name);
+                                               callback_function_name,
+                                               extra_args.m_impl_up
+                                                   ->GetObjectSP());
   }
 }
 
@@ -992,6 +1005,8 @@
                        (lldb::SBAddress &));
   LLDB_REGISTER_METHOD(void, SBBreakpoint, SetScriptCallbackFunction,
                        (const char *));
+  LLDB_REGISTER_METHOD(void, SBBreakpoint, SetScriptCallbackFunction,
+                       (const char *, SBStructuredData &));
   LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpoint, SetScriptCallbackBody,
                        (const char *));
   LLDB_REGISTER_METHOD(bool, SBBreakpoint, AddName, (const char *));
Index: lldb/scripts/interface/SBBreakpointName.i
===================================================================
--- lldb/scripts/interface/SBBreakpointName.i
+++ lldb/scripts/interface/SBBreakpointName.i
@@ -84,6 +84,10 @@
 
   void SetScriptCallbackFunction(const char *callback_function_name);
 
+  void
+  SetScriptCallbackFunction (const char *callback_function_name,
+                             SBStructuredData &extra_args);
+
   void SetCommandLineCommands(SBStringList &commands);
 
   bool GetCommandLineCommands(SBStringList &commands);
Index: lldb/scripts/interface/SBBreakpointLocation.i
===================================================================
--- lldb/scripts/interface/SBBreakpointLocation.i
+++ lldb/scripts/interface/SBBreakpointLocation.i
@@ -73,10 +73,19 @@
     void SetAutoContinue(bool auto_continue);
 
     %feature("docstring", "
-    Set the callback to the given Python function name.") SetScriptCallbackFunction;
+    Set the callback to the given Python function name.
+    The function takes three arguments (frame, bp_loc, dict).") SetScriptCallbackFunction;
     void
     SetScriptCallbackFunction (const char *callback_function_name);
 
+    %feature("docstring", "
+    Set the name of the script function to be called when the breakpoint is hit.
+    To use this variant, the function should take (frame, bp_loc, extra_args, dict) and
+    when the breakpoint is hit the extra_args will be passed to the callback function.") SetScriptCallbackFunction;
+    void
+    SetScriptCallbackFunction (const char *callback_function_name,
+                               SBStructuredData &extra_args);
+
     %feature("docstring", "
     Provide the body for the script function to be called when the breakpoint location is hit.
     The body will be wrapped in a function, which be passed two arguments:
Index: lldb/scripts/interface/SBBreakpoint.i
===================================================================
--- lldb/scripts/interface/SBBreakpoint.i
+++ lldb/scripts/interface/SBBreakpoint.i
@@ -179,6 +179,14 @@
     void
     SetScriptCallbackFunction (const char *callback_function_name);
 
+    %feature("docstring", "
+    Set the name of the script function to be called when the breakpoint is hit.
+    To use this variant, the function should take (frame, bp_loc, extra_args, dict) and
+    when the breakpoint is hit the extra_args will be passed to the callback function.") SetScriptCallbackFunction;
+    void
+    SetScriptCallbackFunction (const char *callback_function_name,
+                               SBStructuredData &extra_args);
+
     %feature("docstring", "
     Provide the body for the script function to be called when the breakpoint is hit.
     The body will be wrapped in a function, which be passed two arguments:
Index: lldb/scripts/Python/python-wrapper.swig
===================================================================
--- lldb/scripts/Python/python-wrapper.swig
+++ lldb/scripts/Python/python-wrapper.swig
@@ -45,7 +45,8 @@
     const char *python_function_name,
     const char *session_dictionary_name,
     const lldb::StackFrameSP& frame_sp,
-    const lldb::BreakpointLocationSP& bp_loc_sp
+    const lldb::BreakpointLocationSP& bp_loc_sp,
+    lldb_private::StructuredDataImpl *args_impl
 )
 {
     using namespace lldb_private;
@@ -63,7 +64,19 @@
 
     PythonObject frame_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_frame));
     PythonObject bp_loc_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_bp_loc));
-    PythonObject result = pfunc(frame_arg, bp_loc_arg, dict);
+
+    PythonObject result;
+    // If the called function doesn't take extra_args, drop them here:
+    auto arg_info = pfunc.GetNumArguments();
+    if (arg_info.count == 3)
+      result = pfunc(frame_arg, bp_loc_arg, dict);
+    else if (arg_info.count == 4) {
+        lldb::SBStructuredData *args_value = new lldb::SBStructuredData(args_impl);
+        PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(args_value));
+        result = pfunc(frame_arg, bp_loc_arg, args_arg, dict);      
+    } else {
+      return stop_at_breakpoint;
+    }
 
     if (result.get() == Py_False)
         stop_at_breakpoint = false;
Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py
+++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py
@@ -3,3 +3,13 @@
 
 def function(frame, bp_loc, dict):
     side_effect.bktptcmd = "function was here"
+
+def another_function(frame, bp_loc, extra_args, dict):
+    se_value = extra_args.GetValueForKey("side_effect")
+    se_string = se_value.GetStringValue(100)
+    side_effect.fancy = se_string
+
+def a_third_function(frame, bp_loc, extra_args, dict):
+    se_value = extra_args.GetValueForKey("side_effect")
+    se_string = se_value.GetStringValue(100)
+    side_effect.fancier = se_string
Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py
+++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py
@@ -43,6 +43,14 @@
             "Set break point at this line.", self.main_source_spec)
         self.assertTrue(func_bkpt, VALID_BREAKPOINT)
 
+        fancy_bkpt = self.target.BreakpointCreateBySourceRegex(
+            "Set break point at this line.", self.main_source_spec)
+        self.assertTrue(fancy_bkpt, VALID_BREAKPOINT)
+
+        fancier_bkpt = self.target.BreakpointCreateBySourceRegex(
+            "Set break point at this line.", self.main_source_spec)
+        self.assertTrue(fancier_bkpt, VALID_BREAKPOINT)
+
         # Also test that setting a source regex breakpoint with an empty file
         # spec list sets it on all files:
         no_files_bkpt = self.target.BreakpointCreateBySourceRegex(
@@ -79,10 +87,21 @@
             "command script import --allow-reload ./bktptcmd.py")
         func_bkpt.SetScriptCallbackFunction("bktptcmd.function")
 
+        extra_args = lldb.SBStructuredData()
+        stream = lldb.SBStream()
+        stream.Print('{"side_effect" : "I am fancy"}')
+        extra_args.SetFromJSON(stream)
+        fancy_bkpt.SetScriptCallbackFunction("bktptcmd.another_function", extra_args)
+
+        id = fancier_bkpt.GetID()
+        self.expect("breakpoint command add -F bktptcmd.a_third_function -k side_effect -v 'I am fancier' %d"%(id))
+                    
         # Clear out canary variables
         side_effect.bktptcmd = None
         side_effect.callback = None
-
+        side_effect.fancy    = None
+        side_effect.fancier  = None
+        
         # Now launch the process, and do not stop at entry point.
         self.process = self.target.LaunchSimple(
             None, None, self.get_process_working_directory())
@@ -97,3 +116,5 @@
 
         self.assertEquals("callback was here", side_effect.callback)
         self.assertEquals("function was here", side_effect.bktptcmd)
+        self.assertEquals("I am fancy", side_effect.fancy)
+        self.assertEquals("I am fancier", side_effect.fancier)
Index: lldb/include/lldb/Interpreter/ScriptInterpreter.h
===================================================================
--- lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -117,8 +117,10 @@
     return error;
   }
 
-  virtual Status GenerateBreakpointCommandCallbackData(StringList &input,
-                                                       std::string &output) {
+  virtual Status GenerateBreakpointCommandCallbackData(
+      StringList &input,
+      std::string &output,
+      StructuredData::ObjectSP extra_args_sp) {
     Status error;
     error.SetErrorString("not implemented");
     return error;
@@ -310,12 +312,15 @@
 
   void SetBreakpointCommandCallbackFunction(
       std::vector<BreakpointOptions *> &bp_options_vec,
-      const char *function_name);
+      const char *function_name, 
+      StructuredData::ObjectSP extra_args_sp);
 
-  /// Set a one-liner as the callback for the breakpoint.
+  /// Set a script function as the callback for the breakpoint.
   virtual void
-  SetBreakpointCommandCallbackFunction(BreakpointOptions *bp_options,
-                                       const char *function_name) {}
+  SetBreakpointCommandCallbackFunction(
+      BreakpointOptions *bp_options,
+      const char *function_name,
+      StructuredData::ObjectSP extra_args_sp) {}
 
   /// Set a one-liner as the callback for the watchpoint.
   virtual void SetWatchpointCommandCallback(WatchpointOptions *wp_options,
Index: lldb/include/lldb/Interpreter/OptionGroupPythonClassWithDict.h
===================================================================
--- lldb/include/lldb/Interpreter/OptionGroupPythonClassWithDict.h
+++ lldb/include/lldb/Interpreter/OptionGroupPythonClassWithDict.h
@@ -24,13 +24,10 @@
 class OptionGroupPythonClassWithDict : public OptionGroup {
 public:
   OptionGroupPythonClassWithDict(const char *class_use,
+                      bool is_class = true,
                       int class_option = 'C',
                       int key_option = 'k', 
-                      int value_option = 'v',
-                      const char *class_long_option = "python-class",
-                      const char *key_long_option = "python-class-key",
-                      const char *value_long_option = "python-class-value",
-                      bool required = false);
+                      int value_option = 'v');
                       
   ~OptionGroupPythonClassWithDict() override;
 
@@ -48,16 +45,17 @@
   const StructuredData::DictionarySP GetStructuredData() {
     return m_dict_sp;
   }
-  const std::string &GetClassName() {
-    return m_class_name;
+  const std::string &GetName() {
+    return m_name;
   }
 
 protected:
-  std::string m_class_name;
+  std::string m_name;
   std::string m_current_key;
   StructuredData::DictionarySP m_dict_sp;
   std::string m_class_usage_text, m_key_usage_text, m_value_usage_text;
-  OptionDefinition m_option_definition[3];
+  bool m_is_class;
+  OptionDefinition m_option_definition[4];
 };
 
 } // namespace lldb_private
Index: lldb/include/lldb/API/SBStructuredData.h
===================================================================
--- lldb/include/lldb/API/SBStructuredData.h
+++ lldb/include/lldb/API/SBStructuredData.h
@@ -93,6 +93,9 @@
   friend class SBTarget;
   friend class SBThread;
   friend class SBThreadPlan;
+  friend class SBBreakpoint;
+  friend class SBBreakpointLocation;
+  friend class SBBreakpointName;
 
   StructuredDataImplUP m_impl_up;
 };
Index: lldb/include/lldb/API/SBBreakpointName.h
===================================================================
--- lldb/include/lldb/API/SBBreakpointName.h
+++ lldb/include/lldb/API/SBBreakpointName.h
@@ -85,6 +85,9 @@
 
   void SetScriptCallbackFunction(const char *callback_function_name);
 
+  void SetScriptCallbackFunction(const char *callback_function_name,
+                                 SBStructuredData &extra_args);
+
   void SetCommandLineCommands(SBStringList &commands);
 
   bool GetCommandLineCommands(SBStringList &commands);
Index: lldb/include/lldb/API/SBBreakpointLocation.h
===================================================================
--- lldb/include/lldb/API/SBBreakpointLocation.h
+++ lldb/include/lldb/API/SBBreakpointLocation.h
@@ -55,6 +55,9 @@
 
   void SetScriptCallbackFunction(const char *callback_function_name);
 
+  void SetScriptCallbackFunction(const char *callback_function_name,
+                                 SBStructuredData &extra_args);
+
   SBError SetScriptCallbackBody(const char *script_body_text);
   
   void SetCommandLineCommands(SBStringList &commands);
Index: lldb/include/lldb/API/SBBreakpoint.h
===================================================================
--- lldb/include/lldb/API/SBBreakpoint.h
+++ lldb/include/lldb/API/SBBreakpoint.h
@@ -94,6 +94,9 @@
 
   void SetScriptCallbackFunction(const char *callback_function_name);
 
+  void SetScriptCallbackFunction(const char *callback_function_name,
+                                 SBStructuredData &extra_args);
+
   void SetCommandLineCommands(SBStringList &commands);
 
   bool GetCommandLineCommands(SBStringList &commands);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to