I also used to get e-mails when a test failed and I was on the changes list. But I haven’t gotten any failure e-mails. Does that only happen for some of the bots (or has that stopped working) or should I look to my filters?
Jim > On Sep 30, 2020, at 10:40 AM, Jim Ingham via lldb-commits > <lldb-commits@lists.llvm.org> wrote: > > It looks like all the failing builds are incremental builds. Can you kick > off a clean build and see if that also gets a failure? I’m worried that all > the SWIG bits aren’t getting rebuilt. The code that is failing here is not > at all time dependent, or particularly platform specific, so it would be odd > for it to fail on ubuntu but not anywhere else. > > Jim > > >> On Sep 30, 2020, at 5:50 AM, Pavel Labath <pa...@labath.sk> wrote: >> >> It looks like the new test (TestStopHookScripted.py) is flaky: >> http://lab.llvm.org:8011/builders/lldb-x86_64-debian/builds/18360/steps/test/logs/stdio >> http://lab.llvm.org:8011/builders/lldb-aarch64-ubuntu/builds/9484/steps/test/logs/stdio >> http://lab.llvm.org:8011/builders/lldb-aarch64-ubuntu/builds/9504/steps/test/logs/stdio >> http://lab.llvm.org:8011/builders/lldb-aarch64-ubuntu/builds/9505/steps/test/logs/stdio >> >> On 29/09/2020 21:01, Jim Ingham via lldb-commits wrote: >>> >>> Author: Jim Ingham >>> Date: 2020-09-29T12:01:14-07:00 >>> New Revision: 1b1d9815987a753f2f3524cfad050b85972dae5b >>> >>> URL: >>> https://github.com/llvm/llvm-project/commit/1b1d9815987a753f2f3524cfad050b85972dae5b >>> DIFF: >>> https://github.com/llvm/llvm-project/commit/1b1d9815987a753f2f3524cfad050b85972dae5b.diff >>> >>> LOG: Revert "Revert "Add the ability to write target stop-hooks using the >>> ScriptInterpreter."" >>> >>> This reverts commit f775fe59640a2e837ad059a8f40e26989d4f9831. >>> >>> I fixed a return type error in the original patch that was causing a test >>> failure. >>> Also added a REQUIRES: python to the shell test so we'll skip this for >>> people who build lldb w/o Python. >>> Also added another test for the error printing. >>> >>> Added: >>> lldb/test/API/commands/target/stop-hooks/TestStopHookScripted.py >>> lldb/test/API/commands/target/stop-hooks/stop_hook.py >>> lldb/test/Shell/Commands/Inputs/stop_hook.py >>> lldb/test/Shell/Commands/command-stop-hook-output.test >>> >>> Modified: >>> lldb/bindings/python/python-swigsafecast.swig >>> lldb/bindings/python/python-wrapper.swig >>> lldb/docs/use/python-reference.rst >>> lldb/include/lldb/Interpreter/ScriptInterpreter.h >>> lldb/include/lldb/Symbol/SymbolContext.h >>> lldb/include/lldb/Target/Target.h >>> lldb/source/Commands/CommandObjectTarget.cpp >>> lldb/source/Commands/Options.td >>> lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp >>> lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h >>> lldb/source/Symbol/SymbolContext.cpp >>> lldb/source/Target/Target.cpp >>> lldb/test/API/commands/target/stop-hooks/TestStopHooks.py >>> lldb/test/API/commands/target/stop-hooks/main.c >>> lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp >>> >>> Removed: >>> >>> >>> >>> ################################################################################ >>> diff --git a/lldb/bindings/python/python-swigsafecast.swig >>> b/lldb/bindings/python/python-swigsafecast.swig >>> index d5cafbfa67cb..091fc29b1057 100644 >>> --- a/lldb/bindings/python/python-swigsafecast.swig >>> +++ b/lldb/bindings/python/python-swigsafecast.swig >>> @@ -152,3 +152,10 @@ SBTypeToSWIGWrapper (lldb::SBSymbolContext* sym_ctx_sb) >>> { >>> return SWIG_NewPointerObj((void *) sym_ctx_sb, >>> SWIGTYPE_p_lldb__SBSymbolContext, 0); >>> } >>> + >>> +template <> >>> +PyObject* >>> +SBTypeToSWIGWrapper (lldb::SBStream* stream_sb) >>> +{ >>> + return SWIG_NewPointerObj((void *) stream_sb, >>> SWIGTYPE_p_lldb__SBStream, 0); >>> +} >>> >>> diff --git a/lldb/bindings/python/python-wrapper.swig >>> b/lldb/bindings/python/python-wrapper.swig >>> index 516590ed5771..c00deba6073b 100644 >>> --- a/lldb/bindings/python/python-wrapper.swig >>> +++ b/lldb/bindings/python/python-wrapper.swig >>> @@ -468,6 +468,127 @@ LLDBSwigPythonCallBreakpointResolver >>> return ret_val; >>> } >>> >>> +SWIGEXPORT void * >>> +LLDBSwigPythonCreateScriptedStopHook >>> +( >>> + lldb::TargetSP target_sp, >>> + const char *python_class_name, >>> + const char *session_dictionary_name, >>> + lldb_private::StructuredDataImpl *args_impl, >>> + Status &error >>> +) >>> +{ >>> + if (python_class_name == NULL || python_class_name[0] == '\0') { >>> + error.SetErrorString("Empty class name."); >>> + Py_RETURN_NONE; >>> + } >>> + if (!session_dictionary_name) { >>> + error.SetErrorString("No session dictionary"); >>> + Py_RETURN_NONE; >>> + } >>> + >>> + PyErr_Cleaner py_err_cleaner(true); >>> + >>> + auto dict = >>> + PythonModule::MainModule().ResolveName<PythonDictionary>( >>> + session_dictionary_name); >>> + auto pfunc = >>> + PythonObject::ResolveNameWithDictionary<PythonCallable>( >>> + python_class_name, dict); >>> + >>> + if (!pfunc.IsAllocated()) { >>> + error.SetErrorStringWithFormat("Could not find class: %s.", >>> + python_class_name); >>> + return nullptr; >>> + } >>> + >>> + lldb::SBTarget *target_val >>> + = new lldb::SBTarget(target_sp); >>> + >>> + PythonObject target_arg(PyRefType::Owned, >>> SBTypeToSWIGWrapper(target_val)); >>> + >>> + lldb::SBStructuredData *args_value = new >>> lldb::SBStructuredData(args_impl); >>> + PythonObject args_arg(PyRefType::Owned, >>> SBTypeToSWIGWrapper(args_value)); >>> + >>> + PythonObject result = pfunc(target_arg, args_arg, dict); >>> + >>> + if (result.IsAllocated()) >>> + { >>> + // Check that the handle_stop callback is defined: >>> + auto callback_func = >>> result.ResolveName<PythonCallable>("handle_stop"); >>> + if (callback_func.IsAllocated()) { >>> + if (auto args_info = callback_func.GetArgInfo()) { >>> + size_t num_args = (*args_info).max_positional_args; >>> + if (num_args != 2) { >>> + error.SetErrorStringWithFormat("Wrong number of args for " >>> + "handle_stop callback, should be 2 (excluding self), got: >>> %d", >>> + num_args); >>> + Py_RETURN_NONE; >>> + } else >>> + return result.release(); >>> + } else { >>> + error.SetErrorString("Couldn't get num arguments for >>> handle_stop " >>> + "callback."); >>> + Py_RETURN_NONE; >>> + } >>> + return result.release(); >>> + } >>> + else { >>> + error.SetErrorStringWithFormat("Class \"%s\" is missing the >>> required " >>> + "handle_stop callback.", >>> + python_class_name); >>> + result.release(); >>> + } >>> + } >>> + Py_RETURN_NONE; >>> +} >>> + >>> +SWIGEXPORT bool >>> +LLDBSwigPythonStopHookCallHandleStop >>> +( >>> + void *implementor, >>> + lldb::ExecutionContextRefSP exc_ctx_sp, >>> + lldb::StreamSP stream >>> +) >>> +{ >>> + // handle_stop will return a bool with the meaning "should_stop"... >>> + // If you return nothing we'll assume we are going to stop. >>> + // Also any errors should return true, since we should stop on error. >>> + >>> + PyErr_Cleaner py_err_cleaner(false); >>> + PythonObject self(PyRefType::Borrowed, >>> static_cast<PyObject*>(implementor)); >>> + auto pfunc = self.ResolveName<PythonCallable>("handle_stop"); >>> + >>> + if (!pfunc.IsAllocated()) >>> + return true; >>> + >>> + PythonObject result; >>> + lldb::SBExecutionContext sb_exc_ctx(exc_ctx_sp); >>> + PythonObject exc_ctx_arg(PyRefType::Owned, >>> SBTypeToSWIGWrapper(sb_exc_ctx)); >>> + lldb::SBStream sb_stream; >>> + PythonObject sb_stream_arg(PyRefType::Owned, >>> + SBTypeToSWIGWrapper(sb_stream)); >>> + result = pfunc(exc_ctx_arg, sb_stream_arg); >>> + >>> + if (PyErr_Occurred()) >>> + { >>> + stream->PutCString("Python error occurred handling stop-hook."); >>> + PyErr_Print(); >>> + PyErr_Clear(); >>> + return true; >>> + } >>> + >>> + // Now add the result to the output stream. SBStream only >>> + // makes an internally help StreamString which I can't interpose, so I >>> + // have to copy it over here. >>> + stream->PutCString(sb_stream.GetData()); >>> + >>> + if (result.get() == Py_False) >>> + return false; >>> + else >>> + return true; >>> +} >>> + >>> // wrapper that calls an optional instance member of an object taking no >>> arguments >>> static PyObject* >>> LLDBSwigPython_CallOptionalMember >>> >>> diff --git a/lldb/docs/use/python-reference.rst >>> b/lldb/docs/use/python-reference.rst >>> index 8c76ef1a0830..60474c94f185 100644 >>> --- a/lldb/docs/use/python-reference.rst >>> +++ b/lldb/docs/use/python-reference.rst >>> @@ -819,3 +819,49 @@ When the program is stopped at the beginning of the >>> 'read' function in libc, we >>> frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read >>> (lldb) frame variable >>> (int) fd = 3 >>> + >>> + Writing Target Stop-Hooks in Python: >>> + ------------------------------------ >>> + >>> + Stop hooks fire whenever the process stops just before control is >>> returned to the >>> + user. Stop hooks can either be a set of lldb command-line commands, or >>> can >>> + be implemented by a suitably defined Python class. The Python based >>> stop-hooks >>> + can also be passed as set of -key -value pairs when they are added, and >>> those >>> + will get packaged up into a SBStructuredData Dictionary and passed to the >>> + constructor of the Python object managing the stop hook. This allows for >>> + parametrization of the stop hooks. >>> + >>> + To add a Python-based stop hook, first define a class with the following >>> methods: >>> + >>> ++--------------------+---------------------------------------+------------------------------------------------------------------------------------------------------------------+ >>> +| Name | Arguments | Description >>> >>> | >>> ++--------------------+---------------------------------------+------------------------------------------------------------------------------------------------------------------+ >>> +| **__init__** | **target: lldb.SBTarget** | This is the >>> constructor for the new stop-hook. >>> | >>> +| | **extra_args: lldb.SBStructuredData** | >>> >>> | >>> +| | | >>> >>> | >>> +| | | **target** >>> is the SBTarget to which the stop hook is added. >>> | >>> +| | | >>> >>> | >>> +| | | >>> **extra_args** is an SBStructuredData object that the user can pass in when >>> creating instances of this | >>> +| | | breakpoint. >>> It is not required, but allows for reuse of stop-hook classes. >>> | >>> ++--------------------+---------------------------------------+------------------------------------------------------------------------------------------------------------------+ >>> +| **handle_stop** | **exe_ctx: lldb.SBExecutionContext** | This is the >>> called when the target stops. >>> | >>> +| | **stream: lldb.SBStream** | >>> >>> | >>> +| | | **exe_ctx** >>> argument will be filled with the current stop point for which the stop hook >>> is | >>> +| | | being >>> evaluated. >>> | >>> +| | | >>> >>> | >>> +| | | **stream** >>> an lldb.SBStream, anything written to this stream will be written to the >>> debugger console. | >>> +| | | >>> >>> | >>> +| | | The return >>> value is a "Should Stop" vote from this thread. If the method returns >>> either True or no return | >>> +| | | this thread >>> votes to stop. If it returns False, then the thread votes to continue >>> after all the stop-hooks | >>> +| | | are >>> evaluated. >>> | >>> +| | | Note, the >>> --auto-continue flag to 'target stop-hook add' overrides a True return >>> value from the method. | >>> ++--------------------+---------------------------------------+------------------------------------------------------------------------------------------------------------------+ >>> + >>> +To use this class in lldb, run the command: >>> + >>> +:: >>> + >>> + (lldb) command script import MyModule.py >>> + (lldb) target stop-hook add -P MyModule.MyStopHook -k first -v 1 -k >>> second -v 2 >>> + >>> +where MyModule.py is the file containing the class definition MyStopHook. >>> >>> diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h >>> b/lldb/include/lldb/Interpreter/ScriptInterpreter.h >>> index 491923e6a6c4..c38786fd50d4 100644 >>> --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h >>> +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h >>> @@ -298,6 +298,23 @@ class ScriptInterpreter : public PluginInterface { >>> return lldb::eSearchDepthModule; >>> } >>> >>> + virtual StructuredData::GenericSP >>> + CreateScriptedStopHook(lldb::TargetSP target_sp, const char *class_name, >>> + StructuredDataImpl *args_data, Status &error) { >>> + error.SetErrorString("Creating scripted stop-hooks with the current " >>> + "script interpreter is not supported."); >>> + return StructuredData::GenericSP(); >>> + } >>> + >>> + // This dispatches to the handle_stop method of the stop-hook class. It >>> + // returns a "should_stop" bool. >>> + virtual bool >>> + ScriptedStopHookHandleStop(StructuredData::GenericSP implementor_sp, >>> + ExecutionContext &exc_ctx, >>> + lldb::StreamSP stream_sp) { >>> + return true; >>> + } >>> + >>> virtual StructuredData::ObjectSP >>> LoadPluginModule(const FileSpec &file_spec, lldb_private::Status &error) { >>> return StructuredData::ObjectSP(); >>> >>> diff --git a/lldb/include/lldb/Symbol/SymbolContext.h >>> b/lldb/include/lldb/Symbol/SymbolContext.h >>> index cc49ce51c713..0f99364596c2 100644 >>> --- a/lldb/include/lldb/Symbol/SymbolContext.h >>> +++ b/lldb/include/lldb/Symbol/SymbolContext.h >>> @@ -340,7 +340,7 @@ class SymbolContextSpecifier { >>> >>> void Clear(); >>> >>> - bool SymbolContextMatches(SymbolContext &sc); >>> + bool SymbolContextMatches(const SymbolContext &sc); >>> >>> bool AddressMatches(lldb::addr_t addr); >>> >>> >>> diff --git a/lldb/include/lldb/Target/Target.h >>> b/lldb/include/lldb/Target/Target.h >>> index 92904682ffb6..94c6ebeac10d 100644 >>> --- a/lldb/include/lldb/Target/Target.h >>> +++ b/lldb/include/lldb/Target/Target.h >>> @@ -28,6 +28,7 @@ >>> #include "lldb/Target/ExecutionContextScope.h" >>> #include "lldb/Target/PathMappingList.h" >>> #include "lldb/Target/SectionLoadHistory.h" >>> +#include "lldb/Target/ThreadSpec.h" >>> #include "lldb/Utility/ArchSpec.h" >>> #include "lldb/Utility/Broadcaster.h" >>> #include "lldb/Utility/LLDBAssert.h" >>> @@ -508,6 +509,8 @@ class Target : public >>> std::enable_shared_from_this<Target>, >>> >>> static void SetDefaultArchitecture(const ArchSpec &arch); >>> >>> + bool IsDummyTarget() const { return m_is_dummy_target; } >>> + >>> /// Find a binary on the system and return its Module, >>> /// or return an existing Module that is already in the Target. >>> /// >>> @@ -1139,23 +1142,27 @@ class Target : public >>> std::enable_shared_from_this<Target>, >>> class StopHook : public UserID { >>> public: >>> StopHook(const StopHook &rhs); >>> + virtual ~StopHook() = default; >>> >>> - ~StopHook(); >>> - >>> - StringList *GetCommandPointer() { return &m_commands; } >>> - >>> - const StringList &GetCommands() { return m_commands; } >>> + enum class StopHookKind : uint32_t { CommandBased = 0, ScriptBased }; >>> >>> lldb::TargetSP &GetTarget() { return m_target_sp; } >>> >>> - void SetCommands(StringList &in_commands) { m_commands = in_commands; } >>> - >>> // Set the specifier. The stop hook will own the specifier, and is >>> // responsible for deleting it when we're done. >>> void SetSpecifier(SymbolContextSpecifier *specifier); >>> >>> SymbolContextSpecifier *GetSpecifier() { return m_specifier_sp.get(); } >>> >>> + bool ExecutionContextPasses(const ExecutionContext &exe_ctx); >>> + >>> + // Called on stop, this gets passed the ExecutionContext for each "stop >>> + // with a reason" thread. It should add to the stream whatever text it >>> + // wants to show the user, and return False to indicate it wants the >>> target >>> + // not to stop. >>> + virtual bool HandleStop(ExecutionContext &exe_ctx, >>> + lldb::StreamSP output) = 0; >>> + >>> // Set the Thread Specifier. The stop hook will own the thread >>> specifier, >>> // and is responsible for deleting it when we're done. >>> void SetThreadSpecifier(ThreadSpec *specifier); >>> @@ -1173,26 +1180,79 @@ class Target : public >>> std::enable_shared_from_this<Target>, >>> bool GetAutoContinue() const { return m_auto_continue; } >>> >>> void GetDescription(Stream *s, lldb::DescriptionLevel level) const; >>> + virtual void GetSubclassDescription(Stream *s, >>> + lldb::DescriptionLevel level) >>> const = 0; >>> >>> - private: >>> + protected: >>> lldb::TargetSP m_target_sp; >>> - StringList m_commands; >>> lldb::SymbolContextSpecifierSP m_specifier_sp; >>> std::unique_ptr<ThreadSpec> m_thread_spec_up; >>> bool m_active = true; >>> bool m_auto_continue = false; >>> >>> + StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid); >>> + }; >>> + >>> + class StopHookCommandLine : public StopHook { >>> + public: >>> + virtual ~StopHookCommandLine() = default; >>> + >>> + StringList &GetCommands() { return m_commands; } >>> + void SetActionFromString(const std::string &strings); >>> + void SetActionFromStrings(const std::vector<std::string> &strings); >>> + >>> + bool HandleStop(ExecutionContext &exc_ctx, >>> + lldb::StreamSP output_sp) override; >>> + void GetSubclassDescription(Stream *s, >>> + lldb::DescriptionLevel level) const >>> override; >>> + >>> + private: >>> + StringList m_commands; >>> // Use CreateStopHook to make a new empty stop hook. The >>> GetCommandPointer >>> // and fill it with commands, and SetSpecifier to set the specifier >>> shared >>> // pointer (can be null, that will match anything.) >>> - StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid); >>> + StopHookCommandLine(lldb::TargetSP target_sp, lldb::user_id_t uid) >>> + : StopHook(target_sp, uid) {} >>> + friend class Target; >>> + }; >>> + >>> + class StopHookScripted : public StopHook { >>> + public: >>> + virtual ~StopHookScripted() = default; >>> + bool HandleStop(ExecutionContext &exc_ctx, lldb::StreamSP output) >>> override; >>> + >>> + Status SetScriptCallback(std::string class_name, >>> + StructuredData::ObjectSP extra_args_sp); >>> + >>> + void GetSubclassDescription(Stream *s, >>> + lldb::DescriptionLevel level) const >>> override; >>> + >>> + private: >>> + std::string m_class_name; >>> + /// This holds the dictionary of keys & values that can be used to >>> + /// parametrize any given callback's behavior. >>> + StructuredDataImpl *m_extra_args; // We own this structured data, >>> + // but the SD itself manages the UP. >>> + /// This holds the python callback object. >>> + StructuredData::GenericSP m_implementation_sp; >>> + >>> + /// Use CreateStopHook to make a new empty stop hook. The >>> GetCommandPointer >>> + /// and fill it with commands, and SetSpecifier to set the specifier >>> shared >>> + /// pointer (can be null, that will match anything.) >>> + StopHookScripted(lldb::TargetSP target_sp, lldb::user_id_t uid) >>> + : StopHook(target_sp, uid) {} >>> friend class Target; >>> }; >>> + >>> typedef std::shared_ptr<StopHook> StopHookSP; >>> >>> - // Add an empty stop hook to the Target's stop hook list, and returns a >>> - // shared pointer to it in new_hook. Returns the id of the new hook. >>> - StopHookSP CreateStopHook(); >>> + /// Add an empty stop hook to the Target's stop hook list, and returns a >>> + /// shared pointer to it in new_hook. Returns the id of the new hook. >>> + StopHookSP CreateStopHook(StopHook::StopHookKind kind); >>> + >>> + /// If you tried to create a stop hook, and that failed, call this to >>> + /// remove the stop hook, as it will also reset the stop hook counter. >>> + void UndoCreateStopHook(lldb::user_id_t uid); >>> >>> void RunStopHooks(); >>> >>> >>> diff --git a/lldb/source/Commands/CommandObjectTarget.cpp >>> b/lldb/source/Commands/CommandObjectTarget.cpp >>> index 431c2f3a19f0..98285289e3a9 100644 >>> --- a/lldb/source/Commands/CommandObjectTarget.cpp >>> +++ b/lldb/source/Commands/CommandObjectTarget.cpp >>> @@ -24,6 +24,7 @@ >>> #include "lldb/Interpreter/OptionGroupFile.h" >>> #include "lldb/Interpreter/OptionGroupFormat.h" >>> #include "lldb/Interpreter/OptionGroupPlatform.h" >>> +#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h" >>> #include "lldb/Interpreter/OptionGroupString.h" >>> #include "lldb/Interpreter/OptionGroupUInt64.h" >>> #include "lldb/Interpreter/OptionGroupUUID.h" >>> @@ -4442,10 +4443,10 @@ class CommandObjectTargetSymbols : public >>> CommandObjectMultiword { >>> class CommandObjectTargetStopHookAdd : public CommandObjectParsed, >>> public IOHandlerDelegateMultiline { >>> public: >>> - class CommandOptions : public Options { >>> + class CommandOptions : public OptionGroup { >>> public: >>> CommandOptions() >>> - : Options(), m_line_start(0), m_line_end(UINT_MAX), >>> + : OptionGroup(), m_line_start(0), m_line_end(UINT_MAX), >>> m_func_name_type_mask(eFunctionNameTypeAuto), >>> m_sym_ctx_specified(false), m_thread_specified(false), >>> m_use_one_liner(false), m_one_liner() {} >>> @@ -4459,7 +4460,8 @@ class CommandObjectTargetStopHookAdd : public >>> CommandObjectParsed, >>> 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_target_stop_hook_add_options[option_idx].short_option; >>> >>> switch (short_option) { >>> case 'c': >>> @@ -4589,20 +4591,75 @@ class CommandObjectTargetStopHookAdd : public >>> CommandObjectParsed, >>> // Instance variables to hold the values for one_liner options. >>> bool m_use_one_liner; >>> std::vector<std::string> m_one_liner; >>> + >>> bool m_auto_continue; >>> }; >>> >>> CommandObjectTargetStopHookAdd(CommandInterpreter &interpreter) >>> : CommandObjectParsed(interpreter, "target stop-hook add", >>> - "Add a hook to be executed when the target >>> stops.", >>> + "Add a hook to be executed when the target >>> stops." >>> + "The hook can either be a list of commands or >>> an " >>> + "appropriately defined Python class. You can >>> also " >>> + "add filters so the hook only runs a certain >>> stop " >>> + "points.", >>> "target stop-hook add"), >>> IOHandlerDelegateMultiline("DONE", >>> >>> IOHandlerDelegate::Completion::LLDBCommand), >>> - m_options() {} >>> + m_options(), m_python_class_options("scripted stop-hook", true, >>> 'P') { >>> + SetHelpLong( >>> + R"( >>> +Command Based stop-hooks: >>> +------------------------- >>> + Stop hooks can run a list of lldb commands by providing one or more >>> + --one-line-command options. The commands will get run in the order they >>> are >>> + added. Or you can provide no commands, in which case you will enter a >>> + command editor where you can enter the commands to be run. >>> + >>> +Python Based Stop Hooks: >>> +------------------------ >>> + Stop hooks can be implemented with a suitably defined Python class, >>> whose name >>> + is passed in the --python-class option. >>> + >>> + When the stop hook is added, the class is initialized by calling: >>> + >>> + def __init__(self, target, extra_args, dict): >>> + >>> + target: The target that the stop hook is being added to. >>> + extra_args: An SBStructuredData Dictionary filled with the -key -value >>> + option pairs passed to the command. >>> + dict: An implementation detail provided by lldb. >>> + >>> + Then when the stop-hook triggers, lldb will run the 'handle_stop' >>> method. >>> + The method has the signature: >>> + >>> + def handle_stop(self, exe_ctx, stream): >>> + >>> + exe_ctx: An SBExecutionContext for the thread that has stopped. >>> + stream: An SBStream, anything written to this stream will be printed >>> in the >>> + the stop message when the process stops. >>> + >>> + Return Value: The method returns "should_stop". If should_stop is >>> false >>> + from all the stop hook executions on threads that stopped >>> + with a reason, then the process will continue. Note >>> that this >>> + will happen only after all the stop hooks are run. >>> + >>> +Filter Options: >>> +--------------- >>> + Stop hooks can be set to always run, or to only run when the stopped >>> thread >>> + matches the filter options passed on the command line. The available >>> filter >>> + options include a shared library or a thread or queue specification, >>> + a line range in a source file, a function name or a class name. >>> + )"); >>> + m_all_options.Append(&m_python_class_options, >>> + LLDB_OPT_SET_1 | LLDB_OPT_SET_2, >>> + LLDB_OPT_SET_FROM_TO(4, 6)); >>> + m_all_options.Append(&m_options); >>> + m_all_options.Finalize(); >>> + } >>> >>> ~CommandObjectTargetStopHookAdd() override = default; >>> >>> - Options *GetOptions() override { return &m_options; } >>> + Options *GetOptions() override { return &m_all_options; } >>> >>> protected: >>> void IOHandlerActivated(IOHandler &io_handler, bool interactive) override { >>> @@ -4626,10 +4683,15 @@ class CommandObjectTargetStopHookAdd : public >>> CommandObjectParsed, >>> error_sp->Flush(); >>> } >>> Target *target = GetDebugger().GetSelectedTarget().get(); >>> - if (target) >>> - target->RemoveStopHookByID(m_stop_hook_sp->GetID()); >>> + if (target) { >>> + target->UndoCreateStopHook(m_stop_hook_sp->GetID()); >>> + } >>> } else { >>> - m_stop_hook_sp->GetCommandPointer()->SplitIntoLines(line); >>> + // The IOHandler editor is only for command lines stop hooks: >>> + Target::StopHookCommandLine *hook_ptr = >>> + static_cast<Target::StopHookCommandLine >>> *>(m_stop_hook_sp.get()); >>> + >>> + hook_ptr->SetActionFromString(line); >>> StreamFileSP output_sp(io_handler.GetOutputStreamFileSP()); >>> if (output_sp) { >>> output_sp->Printf("Stop hook #%" PRIu64 " added.\n", >>> @@ -4646,7 +4708,10 @@ class CommandObjectTargetStopHookAdd : public >>> CommandObjectParsed, >>> m_stop_hook_sp.reset(); >>> >>> Target &target = GetSelectedOrDummyTarget(); >>> - Target::StopHookSP new_hook_sp = target.CreateStopHook(); >>> + Target::StopHookSP new_hook_sp = >>> + target.CreateStopHook(m_python_class_options.GetName().empty() ? >>> + Target::StopHook::StopHookKind::CommandBased >>> + : >>> Target::StopHook::StopHookKind::ScriptBased); >>> >>> // First step, make the specifier. >>> std::unique_ptr<SymbolContextSpecifier> specifier_up; >>> @@ -4715,11 +4780,30 @@ class CommandObjectTargetStopHookAdd : public >>> CommandObjectParsed, >>> >>> new_hook_sp->SetAutoContinue(m_options.m_auto_continue); >>> if (m_options.m_use_one_liner) { >>> - // Use one-liners. >>> - for (auto cmd : m_options.m_one_liner) >>> - new_hook_sp->GetCommandPointer()->AppendString(cmd.c_str()); >>> + // This is a command line stop hook: >>> + Target::StopHookCommandLine *hook_ptr = >>> + static_cast<Target::StopHookCommandLine *>(new_hook_sp.get()); >>> + hook_ptr->SetActionFromStrings(m_options.m_one_liner); >>> result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n", >>> new_hook_sp->GetID()); >>> + } else if (!m_python_class_options.GetName().empty()) { >>> + // This is a scripted stop hook: >>> + Target::StopHookScripted *hook_ptr = >>> + static_cast<Target::StopHookScripted *>(new_hook_sp.get()); >>> + Status error = hook_ptr->SetScriptCallback( >>> + m_python_class_options.GetName(), >>> + m_python_class_options.GetStructuredData()); >>> + if (error.Success()) >>> + result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n", >>> + new_hook_sp->GetID()); >>> + else { >>> + // FIXME: Set the stop hook ID counter back. >>> + result.AppendErrorWithFormat("Couldn't add stop hook: %s", >>> + error.AsCString()); >>> + result.SetStatus(eReturnStatusFailed); >>> + target.UndoCreateStopHook(new_hook_sp->GetID()); >>> + return false; >>> + } >>> } else { >>> m_stop_hook_sp = new_hook_sp; >>> m_interpreter.GetLLDBCommandsFromIOHandler("> ", // Prompt >>> @@ -4732,6 +4816,9 @@ class CommandObjectTargetStopHookAdd : public >>> CommandObjectParsed, >>> >>> private: >>> CommandOptions m_options; >>> + OptionGroupPythonClassWithDict m_python_class_options; >>> + OptionGroupOptions m_all_options; >>> + >>> Target::StopHookSP m_stop_hook_sp; >>> }; >>> >>> >>> diff --git a/lldb/source/Commands/Options.td >>> b/lldb/source/Commands/Options.td >>> index 8c83fd20a366..ad2f5fdae8e7 100644 >>> --- a/lldb/source/Commands/Options.td >>> +++ b/lldb/source/Commands/Options.td >>> @@ -879,7 +879,7 @@ let Command = "target modules lookup" in { >>> } >>> >>> let Command = "target stop hook add" in { >>> - def target_stop_hook_add_one_liner : Option<"one-liner", "o">, >>> + def target_stop_hook_add_one_liner : Option<"one-liner", "o">, >>> GroupRange<1,3>, >>> Arg<"OneLiner">, Desc<"Add a command for the stop hook. Can be >>> specified " >>> "more than once, and commands will be run in the order they appear.">; >>> def target_stop_hook_add_shlib : Option<"shlib", "s">, Arg<"ShlibName">, >>> @@ -897,19 +897,19 @@ let Command = "target stop hook add" in { >>> def target_stop_hook_add_queue_name : Option<"queue-name", "q">, >>> Arg<"QueueName">, Desc<"The stop hook is run only for threads in the >>> queue " >>> "whose name is given by this argument.">; >>> - def target_stop_hook_add_file : Option<"file", "f">, Group<1>, >>> + def target_stop_hook_add_file : Option<"file", "f">, Groups<[1,4]>, >>> Arg<"Filename">, Desc<"Specify the source file within which the >>> stop-hook " >>> "is to be run.">, Completion<"SourceFile">; >>> - def target_stop_hook_add_start_line : Option<"start-line", "l">, >>> Group<1>, >>> + def target_stop_hook_add_start_line : Option<"start-line", "l">, >>> Groups<[1,4]>, >>> Arg<"LineNum">, Desc<"Set the start of the line range for which the " >>> "stop-hook is to be run.">; >>> - def target_stop_hook_add_end_line : Option<"end-line", "e">, Group<1>, >>> + def target_stop_hook_add_end_line : Option<"end-line", "e">, >>> Groups<[1,4]>, >>> Arg<"LineNum">, Desc<"Set the end of the line range for which the >>> stop-hook" >>> " is to be run.">; >>> - def target_stop_hook_add_classname : Option<"classname", "c">, Group<2>, >>> + def target_stop_hook_add_classname : Option<"classname", "c">, >>> Groups<[2,5]>, >>> Arg<"ClassName">, >>> Desc<"Specify the class within which the stop-hook is to be run.">; >>> - def target_stop_hook_add_name : Option<"name", "n">, Group<3>, >>> + def target_stop_hook_add_name : Option<"name", "n">, Groups<[3,6]>, >>> Arg<"FunctionName">, Desc<"Set the function name within which the stop >>> hook" >>> " will be run.">, Completion<"Symbol">; >>> def target_stop_hook_add_auto_continue : Option<"auto-continue", "G">, >>> >>> diff --git >>> a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp >>> b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp >>> index 9f56b4fa60a5..f67572c1f029 100644 >>> --- >>> a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp >>> +++ >>> b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp >>> @@ -127,6 +127,16 @@ extern "C" unsigned int >>> LLDBSwigPythonCallBreakpointResolver(void *implementor, const char >>> *method_name, >>> lldb_private::SymbolContext *sym_ctx); >>> >>> +extern "C" void *LLDBSwigPythonCreateScriptedStopHook( >>> + TargetSP target_sp, const char *python_class_name, >>> + const char *session_dictionary_name, lldb_private::StructuredDataImpl >>> *args, >>> + lldb_private::Status &error); >>> + >>> +extern "C" bool >>> +LLDBSwigPythonStopHookCallHandleStop(void *implementor, >>> + lldb::ExecutionContextRefSP exc_ctx, >>> + lldb::StreamSP stream); >>> + >>> extern "C" size_t LLDBSwigPython_CalculateNumChildren(void *implementor, >>> uint32_t max); >>> >>> @@ -1979,6 +1989,60 @@ >>> ScriptInterpreterPythonImpl::ScriptedBreakpointResolverSearchDepth( >>> return lldb::eSearchDepthModule; >>> } >>> >>> +StructuredData::GenericSP >>> ScriptInterpreterPythonImpl::CreateScriptedStopHook( >>> + TargetSP target_sp, const char *class_name, StructuredDataImpl >>> *args_data, >>> + Status &error) { >>> + >>> + if (!target_sp) { >>> + error.SetErrorString("No target for scripted stop-hook."); >>> + return StructuredData::GenericSP(); >>> + } >>> + >>> + if (class_name == nullptr || class_name[0] == '\0') { >>> + error.SetErrorString("No class name for scripted stop-hook."); >>> + return StructuredData::GenericSP(); >>> + } >>> + >>> + ScriptInterpreter *script_interpreter = >>> m_debugger.GetScriptInterpreter(); >>> + ScriptInterpreterPythonImpl *python_interpreter = >>> + static_cast<ScriptInterpreterPythonImpl *>(script_interpreter); >>> + >>> + if (!script_interpreter) { >>> + error.SetErrorString("No script interpreter for scripted stop-hook."); >>> + return StructuredData::GenericSP(); >>> + } >>> + >>> + void *ret_val; >>> + >>> + { >>> + Locker py_lock(this, >>> + Locker::AcquireLock | Locker::InitSession | >>> Locker::NoSTDIN); >>> + >>> + ret_val = LLDBSwigPythonCreateScriptedStopHook( >>> + target_sp, class_name, >>> python_interpreter->m_dictionary_name.c_str(), >>> + args_data, error); >>> + } >>> + >>> + return StructuredData::GenericSP(new StructuredPythonObject(ret_val)); >>> +} >>> + >>> +bool ScriptInterpreterPythonImpl::ScriptedStopHookHandleStop( >>> + StructuredData::GenericSP implementor_sp, ExecutionContext &exc_ctx, >>> + lldb::StreamSP stream_sp) { >>> + assert(implementor_sp && >>> + "can't call a stop hook with an invalid implementor"); >>> + assert(stream_sp && "can't call a stop hook with an invalid stream"); >>> + >>> + Locker py_lock(this, >>> + Locker::AcquireLock | Locker::InitSession | >>> Locker::NoSTDIN); >>> + >>> + lldb::ExecutionContextRefSP exc_ctx_ref_sp(new >>> ExecutionContextRef(exc_ctx)); >>> + >>> + bool ret_val = LLDBSwigPythonStopHookCallHandleStop( >>> + implementor_sp->GetValue(), exc_ctx_ref_sp, stream_sp); >>> + return ret_val; >>> +} >>> + >>> StructuredData::ObjectSP >>> ScriptInterpreterPythonImpl::LoadPluginModule(const FileSpec &file_spec, >>> lldb_private::Status &error) { >>> >>> diff --git >>> a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h >>> >>> b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h >>> index 22b2c8152eac..f89c3d461f7f 100644 >>> --- >>> a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h >>> +++ >>> b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h >>> @@ -105,6 +105,14 @@ class ScriptInterpreterPythonImpl : public >>> ScriptInterpreterPython { >>> lldb::SearchDepth ScriptedBreakpointResolverSearchDepth( >>> StructuredData::GenericSP implementor_sp) override; >>> >>> + StructuredData::GenericSP >>> + CreateScriptedStopHook(lldb::TargetSP target_sp, const char *class_name, >>> + StructuredDataImpl *args_data, Status &error) >>> override; >>> + >>> + bool ScriptedStopHookHandleStop(StructuredData::GenericSP implementor_sp, >>> + ExecutionContext &exc_ctx, >>> + lldb::StreamSP stream_sp) override; >>> + >>> StructuredData::GenericSP >>> CreateFrameRecognizer(const char *class_name) override; >>> >>> >>> diff --git a/lldb/source/Symbol/SymbolContext.cpp >>> b/lldb/source/Symbol/SymbolContext.cpp >>> index 51f56704cca6..f20dc61996e0 100644 >>> --- a/lldb/source/Symbol/SymbolContext.cpp >>> +++ b/lldb/source/Symbol/SymbolContext.cpp >>> @@ -1010,11 +1010,15 @@ void SymbolContextSpecifier::Clear() { >>> m_type = eNothingSpecified; >>> } >>> >>> -bool SymbolContextSpecifier::SymbolContextMatches(SymbolContext &sc) { >>> +bool SymbolContextSpecifier::SymbolContextMatches(const SymbolContext &sc) >>> { >>> if (m_type == eNothingSpecified) >>> return true; >>> >>> - if (m_target_sp.get() != sc.target_sp.get()) >>> + // Only compare targets if this specifier has one and it's not the Dummy >>> + // target. Otherwise if a specifier gets made in the dummy target and >>> + // copied over we'll artificially fail the comparision. >>> + if (m_target_sp && !m_target_sp->IsDummyTarget() && >>> + m_target_sp != sc.target_sp) >>> return false; >>> >>> if (m_type & eModuleSpecified) { >>> >>> diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp >>> index a529df998ba7..a5250ddcef74 100644 >>> --- a/lldb/source/Target/Target.cpp >>> +++ b/lldb/source/Target/Target.cpp >>> @@ -2484,13 +2484,28 @@ ClangModulesDeclVendor >>> *Target::GetClangModulesDeclVendor() { >>> return m_clang_modules_decl_vendor_up.get(); >>> } >>> >>> -Target::StopHookSP Target::CreateStopHook() { >>> +Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind) { >>> lldb::user_id_t new_uid = ++m_stop_hook_next_id; >>> - Target::StopHookSP stop_hook_sp(new StopHook(shared_from_this(), >>> new_uid)); >>> + Target::StopHookSP stop_hook_sp; >>> + switch (kind) { >>> + case StopHook::StopHookKind::CommandBased: >>> + stop_hook_sp.reset(new StopHookCommandLine(shared_from_this(), >>> new_uid)); >>> + break; >>> + case StopHook::StopHookKind::ScriptBased: >>> + stop_hook_sp.reset(new StopHookScripted(shared_from_this(), new_uid)); >>> + break; >>> + } >>> m_stop_hooks[new_uid] = stop_hook_sp; >>> return stop_hook_sp; >>> } >>> >>> +void Target::UndoCreateStopHook(lldb::user_id_t user_id) { >>> + if (!RemoveStopHookByID(user_id)) >>> + return; >>> + if (user_id == m_stop_hook_next_id) >>> + m_stop_hook_next_id--; >>> +} >>> + >>> bool Target::RemoveStopHookByID(lldb::user_id_t user_id) { >>> size_t num_removed = m_stop_hooks.erase(user_id); >>> return (num_removed != 0); >>> @@ -2546,25 +2561,18 @@ void Target::RunStopHooks() { >>> if (m_stop_hooks.empty()) >>> return; >>> >>> - StopHookCollection::iterator pos, end = m_stop_hooks.end(); >>> - >>> // If there aren't any active stop hooks, don't bother either. >>> - // Also see if any of the active hooks want to auto-continue. >>> bool any_active_hooks = false; >>> - bool auto_continue = false; >>> for (auto hook : m_stop_hooks) { >>> if (hook.second->IsActive()) { >>> any_active_hooks = true; >>> - auto_continue |= hook.second->GetAutoContinue(); >>> + break; >>> } >>> } >>> if (!any_active_hooks) >>> return; >>> >>> - CommandReturnObject result(m_debugger.GetUseColor()); >>> - >>> std::vector<ExecutionContext> exc_ctx_with_reasons; >>> - std::vector<SymbolContext> sym_ctx_with_reasons; >>> >>> ThreadList &cur_threadlist = m_process_sp->GetThreadList(); >>> size_t num_threads = cur_threadlist.GetSize(); >>> @@ -2572,10 +2580,8 @@ void Target::RunStopHooks() { >>> lldb::ThreadSP cur_thread_sp = cur_threadlist.GetThreadAtIndex(i); >>> if (cur_thread_sp->ThreadStoppedForAReason()) { >>> lldb::StackFrameSP cur_frame_sp = >>> cur_thread_sp->GetStackFrameAtIndex(0); >>> - exc_ctx_with_reasons.push_back(ExecutionContext( >>> - m_process_sp.get(), cur_thread_sp.get(), cur_frame_sp.get())); >>> - sym_ctx_with_reasons.push_back( >>> - cur_frame_sp->GetSymbolContext(eSymbolContextEverything)); >>> + exc_ctx_with_reasons.emplace_back(m_process_sp.get(), >>> cur_thread_sp.get(), >>> + cur_frame_sp.get()); >>> } >>> } >>> >>> @@ -2584,91 +2590,86 @@ void Target::RunStopHooks() { >>> if (num_exe_ctx == 0) >>> return; >>> >>> - result.SetImmediateOutputStream(m_debugger.GetAsyncOutputStream()); >>> - result.SetImmediateErrorStream(m_debugger.GetAsyncErrorStream()); >>> + StreamSP output_sp = m_debugger.GetAsyncOutputStream(); >>> >>> - bool keep_going = true; >>> + bool auto_continue = false; >>> bool hooks_ran = false; >>> bool print_hook_header = (m_stop_hooks.size() != 1); >>> bool print_thread_header = (num_exe_ctx != 1); >>> - bool did_restart = false; >>> + bool should_stop = false; >>> + bool somebody_restarted = false; >>> >>> - for (pos = m_stop_hooks.begin(); keep_going && pos != end; pos++) { >>> - // result.Clear(); >>> - StopHookSP cur_hook_sp = (*pos).second; >>> + for (auto stop_entry : m_stop_hooks) { >>> + StopHookSP cur_hook_sp = stop_entry.second; >>> if (!cur_hook_sp->IsActive()) >>> continue; >>> >>> bool any_thread_matched = false; >>> - for (size_t i = 0; keep_going && i < num_exe_ctx; i++) { >>> - if ((cur_hook_sp->GetSpecifier() == nullptr || >>> - cur_hook_sp->GetSpecifier()->SymbolContextMatches( >>> - sym_ctx_with_reasons[i])) && >>> - (cur_hook_sp->GetThreadSpecifier() == nullptr || >>> - cur_hook_sp->GetThreadSpecifier()->ThreadPassesBasicTests( >>> - exc_ctx_with_reasons[i].GetThreadRef()))) { >>> - if (!hooks_ran) { >>> - hooks_ran = true; >>> - } >>> - if (print_hook_header && !any_thread_matched) { >>> - const char *cmd = >>> - (cur_hook_sp->GetCommands().GetSize() == 1 >>> - ? cur_hook_sp->GetCommands().GetStringAtIndex(0) >>> - : nullptr); >>> - if (cmd) >>> - result.AppendMessageWithFormat("\n- Hook %" PRIu64 " (%s)\n", >>> - cur_hook_sp->GetID(), cmd); >>> - else >>> - result.AppendMessageWithFormat("\n- Hook %" PRIu64 "\n", >>> - cur_hook_sp->GetID()); >>> - any_thread_matched = true; >>> - } >>> + for (auto exc_ctx : exc_ctx_with_reasons) { >>> + // We detect somebody restarted in the stop-hook loop, and broke out >>> of >>> + // that loop back to here. So break out of here too. >>> + if (somebody_restarted) >>> + break; >>> >>> - if (print_thread_header) >>> - result.AppendMessageWithFormat( >>> - "-- Thread %d\n", >>> - exc_ctx_with_reasons[i].GetThreadPtr()->GetIndexID()); >>> - >>> - CommandInterpreterRunOptions options; >>> - options.SetStopOnContinue(true); >>> - options.SetStopOnError(true); >>> - options.SetEchoCommands(false); >>> - options.SetPrintResults(true); >>> - options.SetPrintErrors(true); >>> - options.SetAddToHistory(false); >>> - >>> - // Force Async: >>> - bool old_async = GetDebugger().GetAsyncExecution(); >>> - GetDebugger().SetAsyncExecution(true); >>> - GetDebugger().GetCommandInterpreter().HandleCommands( >>> - cur_hook_sp->GetCommands(), &exc_ctx_with_reasons[i], options, >>> - result); >>> - GetDebugger().SetAsyncExecution(old_async); >>> - // If the command started the target going again, we should bag >>> out of >>> - // running the stop hooks. >>> - if ((result.GetStatus() == eReturnStatusSuccessContinuingNoResult) >>> || >>> - (result.GetStatus() == eReturnStatusSuccessContinuingResult)) { >>> - // But only complain if there were more stop hooks to do: >>> - StopHookCollection::iterator tmp = pos; >>> - if (++tmp != end) >>> - result.AppendMessageWithFormat( >>> - "\nAborting stop hooks, hook %" PRIu64 >>> - " set the program running.\n" >>> - " Consider using '-G true' to make " >>> - "stop hooks auto-continue.\n", >>> - cur_hook_sp->GetID()); >>> - keep_going = false; >>> - did_restart = true; >>> - } >>> + if (!cur_hook_sp->ExecutionContextPasses(exc_ctx)) >>> + continue; >>> + >>> + // We only consult the auto-continue for a stop hook if it matched >>> the >>> + // specifier. >>> + auto_continue |= cur_hook_sp->GetAutoContinue(); >>> + >>> + if (!hooks_ran) >>> + hooks_ran = true; >>> + >>> + if (print_hook_header && !any_thread_matched) { >>> + StreamString s; >>> + cur_hook_sp->GetDescription(&s, eDescriptionLevelBrief); >>> + if (s.GetSize() != 0) >>> + output_sp->Printf("\n- Hook %" PRIu64 " (%s)\n", >>> cur_hook_sp->GetID(), >>> + s.GetData()); >>> + else >>> + output_sp->Printf("\n- Hook %" PRIu64 "\n", >>> cur_hook_sp->GetID()); >>> + any_thread_matched = true; >>> + } >>> + >>> + if (print_thread_header) >>> + output_sp->Printf("-- Thread %d\n", >>> + exc_ctx.GetThreadPtr()->GetIndexID()); >>> + >>> + bool this_should_stop = cur_hook_sp->HandleStop(exc_ctx, output_sp); >>> + // If this hook is set to auto-continue that should override the >>> + // HandleStop result... >>> + if (cur_hook_sp->GetAutoContinue()) >>> + this_should_stop = false; >>> + >>> + // If anybody wanted to stop, we should all stop. >>> + if (!should_stop) >>> + should_stop = this_should_stop; >>> + >>> + // We don't have a good way to prohibit people from restarting the >>> target >>> + // willy nilly in a stop hook. So see if the private state is >>> running >>> + // here and bag out if it is. >>> + // FIXME: when we are doing non-stop mode for realz we'll have to >>> instead >>> + // track each thread, and only bag out if a thread is set running. >>> + if (m_process_sp->GetPrivateState() != eStateStopped) { >>> + output_sp->Printf("\nAborting stop hooks, hook %" PRIu64 >>> + " set the program running.\n" >>> + " Consider using '-G true' to make " >>> + "stop hooks auto-continue.\n", >>> + cur_hook_sp->GetID()); >>> + somebody_restarted = true; >>> + break; >>> } >>> } >>> } >>> + >>> + output_sp->Flush(); >>> + >>> // Finally, if auto-continue was requested, do it now: >>> - if (!did_restart && auto_continue) >>> + // We only compute should_stop against the hook results if a hook got to >>> run >>> + // which is why we have to do this conjoint test. >>> + if (!somebody_restarted && ((hooks_ran && !should_stop) || >>> auto_continue)) >>> m_process_sp->PrivateResume(); >>> - >>> - result.GetImmediateOutputStream()->Flush(); >>> - result.GetImmediateErrorStream()->Flush(); >>> } >>> >>> const TargetPropertiesSP &Target::GetGlobalProperties() { >>> @@ -3128,20 +3129,17 @@ void Target::FinalizeFileActions(ProcessLaunchInfo >>> &info) { >>> >>> // Target::StopHook >>> Target::StopHook::StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid) >>> - : UserID(uid), m_target_sp(target_sp), m_commands(), m_specifier_sp(), >>> + : UserID(uid), m_target_sp(target_sp), m_specifier_sp(), >>> m_thread_spec_up() {} >>> >>> Target::StopHook::StopHook(const StopHook &rhs) >>> : UserID(rhs.GetID()), m_target_sp(rhs.m_target_sp), >>> - m_commands(rhs.m_commands), m_specifier_sp(rhs.m_specifier_sp), >>> - m_thread_spec_up(), m_active(rhs.m_active), >>> - m_auto_continue(rhs.m_auto_continue) { >>> + m_specifier_sp(rhs.m_specifier_sp), m_thread_spec_up(), >>> + m_active(rhs.m_active), m_auto_continue(rhs.m_auto_continue) { >>> if (rhs.m_thread_spec_up) >>> m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up); >>> } >>> >>> -Target::StopHook::~StopHook() = default; >>> - >>> void Target::StopHook::SetSpecifier(SymbolContextSpecifier *specifier) { >>> m_specifier_sp.reset(specifier); >>> } >>> @@ -3150,8 +3148,31 @@ void Target::StopHook::SetThreadSpecifier(ThreadSpec >>> *specifier) { >>> m_thread_spec_up.reset(specifier); >>> } >>> >>> +bool Target::StopHook::ExecutionContextPasses(const ExecutionContext >>> &exc_ctx) { >>> + SymbolContextSpecifier *specifier = GetSpecifier(); >>> + if (!specifier) >>> + return true; >>> + >>> + bool will_run = true; >>> + if (exc_ctx.GetFramePtr()) >>> + will_run = GetSpecifier()->SymbolContextMatches( >>> + exc_ctx.GetFramePtr()->GetSymbolContext(eSymbolContextEverything)); >>> + if (will_run && GetThreadSpecifier() != nullptr) >>> + will_run = >>> + >>> GetThreadSpecifier()->ThreadPassesBasicTests(exc_ctx.GetThreadRef()); >>> + >>> + return will_run; >>> +} >>> + >>> void Target::StopHook::GetDescription(Stream *s, >>> lldb::DescriptionLevel level) const { >>> + >>> + // For brief descriptions, only print the subclass description: >>> + if (level == eDescriptionLevelBrief) { >>> + GetSubclassDescription(s, level); >>> + return; >>> + } >>> + >>> unsigned indent_level = s->GetIndentLevel(); >>> >>> s->SetIndentLevel(indent_level + 2); >>> @@ -3182,15 +3203,148 @@ void Target::StopHook::GetDescription(Stream *s, >>> s->PutCString("\n"); >>> s->SetIndentLevel(indent_level + 2); >>> } >>> + GetSubclassDescription(s, level); >>> +} >>> >>> +void Target::StopHookCommandLine::GetSubclassDescription( >>> + Stream *s, lldb::DescriptionLevel level) const { >>> + // The brief description just prints the first command. >>> + if (level == eDescriptionLevelBrief) { >>> + if (m_commands.GetSize() == 1) >>> + s->PutCString(m_commands.GetStringAtIndex(0)); >>> + return; >>> + } >>> s->Indent("Commands: \n"); >>> - s->SetIndentLevel(indent_level + 4); >>> + s->SetIndentLevel(s->GetIndentLevel() + 4); >>> uint32_t num_commands = m_commands.GetSize(); >>> for (uint32_t i = 0; i < num_commands; i++) { >>> s->Indent(m_commands.GetStringAtIndex(i)); >>> s->PutCString("\n"); >>> } >>> - s->SetIndentLevel(indent_level); >>> + s->SetIndentLevel(s->GetIndentLevel() - 4); >>> +} >>> + >>> +// Target::StopHookCommandLine >>> +void Target::StopHookCommandLine::SetActionFromString(const std::string >>> &string) { >>> + GetCommands().SplitIntoLines(string); >>> +} >>> + >>> +void Target::StopHookCommandLine::SetActionFromStrings( >>> + const std::vector<std::string> &strings) { >>> + for (auto string : strings) >>> + GetCommands().AppendString(string.c_str()); >>> +} >>> + >>> +bool Target::StopHookCommandLine::HandleStop(ExecutionContext &exc_ctx, >>> + StreamSP output_sp) { >>> + assert(exc_ctx.GetTargetPtr() && "Can't call PerformAction on a context " >>> + "with no target"); >>> + >>> + if (!m_commands.GetSize()) >>> + return true; >>> + >>> + CommandReturnObject result(false); >>> + result.SetImmediateOutputStream(output_sp); >>> + Debugger &debugger = exc_ctx.GetTargetPtr()->GetDebugger(); >>> + CommandInterpreterRunOptions options; >>> + options.SetStopOnContinue(true); >>> + options.SetStopOnError(true); >>> + options.SetEchoCommands(false); >>> + options.SetPrintResults(true); >>> + options.SetPrintErrors(true); >>> + options.SetAddToHistory(false); >>> + >>> + // Force Async: >>> + bool old_async = debugger.GetAsyncExecution(); >>> + debugger.SetAsyncExecution(true); >>> + debugger.GetCommandInterpreter().HandleCommands(GetCommands(), &exc_ctx, >>> + options, result); >>> + debugger.SetAsyncExecution(old_async); >>> + >>> + return true; >>> +} >>> + >>> +// Target::StopHookScripted >>> +Status Target::StopHookScripted::SetScriptCallback( >>> + std::string class_name, StructuredData::ObjectSP extra_args_sp) { >>> + Status error; >>> + >>> + ScriptInterpreter *script_interp = >>> + GetTarget()->GetDebugger().GetScriptInterpreter(); >>> + if (!script_interp) { >>> + error.SetErrorString("No script interpreter installed."); >>> + return error; >>> + } >>> + >>> + m_class_name = class_name; >>> + >>> + m_extra_args = new StructuredDataImpl(); >>> + >>> + if (extra_args_sp) >>> + m_extra_args->SetObjectSP(extra_args_sp); >>> + >>> + m_implementation_sp = script_interp->CreateScriptedStopHook( >>> + GetTarget(), m_class_name.c_str(), m_extra_args, error); >>> + >>> + return error; >>> +} >>> + >>> +bool Target::StopHookScripted::HandleStop(ExecutionContext &exc_ctx, >>> + StreamSP output_sp) { >>> + assert(exc_ctx.GetTargetPtr() && "Can't call HandleStop on a context " >>> + "with no target"); >>> + >>> + ScriptInterpreter *script_interp = >>> + GetTarget()->GetDebugger().GetScriptInterpreter(); >>> + if (!script_interp) >>> + return true; >>> + >>> + bool should_stop = script_interp->ScriptedStopHookHandleStop( >>> + m_implementation_sp, exc_ctx, output_sp); >>> + >>> + return should_stop; >>> +} >>> + >>> +void Target::StopHookScripted::GetSubclassDescription( >>> + Stream *s, lldb::DescriptionLevel level) const { >>> + if (level == eDescriptionLevelBrief) { >>> + s->PutCString(m_class_name); >>> + return; >>> + } >>> + s->Indent("Class:"); >>> + s->Printf("%s\n", m_class_name.c_str()); >>> + >>> + // Now print the extra args: >>> + // FIXME: We should use StructuredData.GetDescription on the m_extra_args >>> + // but that seems to rely on some printing plugin that doesn't exist. >>> + if (!m_extra_args->IsValid()) >>> + return; >>> + StructuredData::ObjectSP object_sp = m_extra_args->GetObjectSP(); >>> + if (!object_sp || !object_sp->IsValid()) >>> + return; >>> + >>> + StructuredData::Dictionary *as_dict = object_sp->GetAsDictionary(); >>> + if (!as_dict || !as_dict->IsValid()) >>> + return; >>> + >>> + uint32_t num_keys = as_dict->GetSize(); >>> + if (num_keys == 0) >>> + return; >>> + >>> + s->Indent("Args:\n"); >>> + s->SetIndentLevel(s->GetIndentLevel() + 4); >>> + >>> + auto print_one_element = [&s](ConstString key, >>> + StructuredData::Object *object) { >>> + s->Indent(); >>> + s->Printf("%s : %s\n", key.GetCString(), >>> + object->GetStringValue().str().c_str()); >>> + return true; >>> + }; >>> + >>> + as_dict->ForEach(print_one_element); >>> + >>> + s->SetIndentLevel(s->GetIndentLevel() - 4); >>> } >>> >>> static constexpr OptionEnumValueElement g_dynamic_value_types[] = { >>> >>> diff --git >>> a/lldb/test/API/commands/target/stop-hooks/TestStopHookScripted.py >>> b/lldb/test/API/commands/target/stop-hooks/TestStopHookScripted.py >>> new file mode 100644 >>> index 000000000000..e650778fe8e3 >>> --- /dev/null >>> +++ b/lldb/test/API/commands/target/stop-hooks/TestStopHookScripted.py >>> @@ -0,0 +1,146 @@ >>> +""" >>> +Test stop hook functionality >>> +""" >>> + >>> + >>> + >>> +import lldb >>> +import lldbsuite.test.lldbutil as lldbutil >>> +from lldbsuite.test.lldbtest import * >>> + >>> + >>> +class TestStopHooks(TestBase): >>> + >>> + mydir = TestBase.compute_mydir(__file__) >>> + >>> + # If your test case doesn't stress debug info, the >>> + # set this to true. That way it won't be run once for >>> + # each debug info format. >>> + NO_DEBUG_INFO_TESTCASE = True >>> + >>> + def setUp(self): >>> + TestBase.setUp(self) >>> + self.build() >>> + self.main_source_file = lldb.SBFileSpec("main.c") >>> + full_path = os.path.join(self.getSourceDir(), "main.c") >>> + self.main_start_line = line_number(full_path, "main()") >>> + >>> + def test_bad_handler(self): >>> + """Test that we give a good error message when the handler is >>> bad""" >>> + self.script_setup() >>> + result = lldb.SBCommandReturnObject() >>> + >>> + # First try the wrong number of args handler: >>> + command = "target stop-hook add -P stop_hook.bad_handle_stop" >>> + self.interp.HandleCommand(command, result) >>> + self.assertFalse(result.Succeeded(), "Set the target stop hook") >>> + self.assertIn("Wrong number of args", result.GetError(), "Got the >>> wrong number of args error") >>> + >>> + # Next the no handler at all handler: >>> + command = "target stop-hook add -P stop_hook.no_handle_stop" >>> + >>> + self.interp.HandleCommand(command, result) >>> + self.assertFalse(result.Succeeded(), "Set the target stop hook") >>> + self.assertIn('Class "stop_hook.no_handle_stop" is missing the >>> required handle_stop callback', result.GetError(), "Got the right error") >>> + >>> + def test_stop_hooks_scripted(self): >>> + """Test that a scripted stop hook works with no specifiers""" >>> + self.stop_hooks_scripted(5) >>> + >>> + def test_stop_hooks_scripted_right_func(self): >>> + """Test that a scripted stop hook fires when there is a function >>> match""" >>> + self.stop_hooks_scripted(5, "-n step_out_of_me") >>> + >>> + def test_stop_hooks_scripted_wrong_func(self): >>> + """Test that a scripted stop hook doesn't fire when the function >>> does not match""" >>> + self.stop_hooks_scripted(0, "-n main") >>> + >>> + def test_stop_hooks_scripted_right_lines(self): >>> + """Test that a scripted stop hook fires when there is a function >>> match""" >>> + self.stop_hooks_scripted(5, "-f main.c -l 1 -e >>> %d"%(self.main_start_line)) >>> + >>> + def test_stop_hooks_scripted_wrong_lines(self): >>> + """Test that a scripted stop hook doesn't fire when the function >>> does not match""" >>> + self.stop_hooks_scripted(0, "-f main.c -l %d -e >>> 100"%(self.main_start_line)) >>> + >>> + def test_stop_hooks_scripted_auto_continue(self): >>> + """Test that the --auto-continue flag works""" >>> + self.do_test_auto_continue(False) >>> + >>> + def test_stop_hooks_scripted_return_false(self): >>> + """Test that the returning False from a stop hook works""" >>> + self.do_test_auto_continue(True) >>> + >>> + def do_test_auto_continue(self, return_true): >>> + """Test that auto-continue works.""" >>> + # We set auto-continue to 1 but the stop hook only applies to >>> step_out_of_me, >>> + # so we should end up stopped in main, having run the expression >>> only once. >>> + self.script_setup() >>> + >>> + result = lldb.SBCommandReturnObject() >>> + >>> + if return_true: >>> + command = "target stop-hook add -P stop_hook.stop_handler -k >>> increment -v 5 -k return_false -v 1 -n step_out_of_me" >>> + else: >>> + command = "target stop-hook add -G 1 -P stop_hook.stop_handler >>> -k increment -v 5 -n step_out_of_me" >>> + >>> + self.interp.HandleCommand(command, result) >>> + self.assertTrue(result.Succeeded, "Set the target stop hook") >>> + >>> + # First run to main. If we go straight to the first stop hook hit, >>> + # run_to_source_breakpoint will fail because we aren't at original >>> breakpoint >>> + >>> + (target, process, thread, bkpt) = >>> lldbutil.run_to_source_breakpoint(self, >>> + "Stop here first", >>> self.main_source_file) >>> + >>> + # Now set the breakpoint on step_out_of_me, and make sure we run >>> the >>> + # expression, then continue back to main. >>> + bkpt = target.BreakpointCreateBySourceRegex("Set a breakpoint here >>> and step out", self.main_source_file) >>> + self.assertTrue(bkpt.GetNumLocations() > 0, "Got breakpoints in >>> step_out_of_me") >>> + process.Continue() >>> + >>> + var = target.FindFirstGlobalVariable("g_var") >>> + self.assertTrue(var.IsValid()) >>> + self.assertEqual(var.GetValueAsUnsigned(), 5, "Updated g_var") >>> + >>> + func_name = process.GetSelectedThread().frames[0].GetFunctionName() >>> + self.assertEqual("main", func_name, "Didn't stop at the expected >>> function.") >>> + >>> + def script_setup(self): >>> + self.interp = self.dbg.GetCommandInterpreter() >>> + result = lldb.SBCommandReturnObject() >>> + >>> + # Bring in our script file: >>> + script_name = os.path.join(self.getSourceDir(), "stop_hook.py") >>> + command = "command script import " + script_name >>> + self.interp.HandleCommand(command, result) >>> + self.assertTrue(result.Succeeded(), "com scr imp failed: >>> %s"%(result.GetError())) >>> + >>> + # set a breakpoint at the end of main to catch our auto-continue >>> tests. >>> + # Do it in the dummy target so it will get copied to our target >>> even when >>> + # we don't have a chance to stop. >>> + dummy_target = self.dbg.GetDummyTarget() >>> + dummy_target.BreakpointCreateBySourceRegex("return result", >>> self.main_source_file) >>> + >>> + >>> + def stop_hooks_scripted(self, g_var_value, specifier = None): >>> + self.script_setup() >>> + >>> + result = lldb.SBCommandReturnObject() >>> + >>> + command = "target stop-hook add -P stop_hook.stop_handler -k >>> increment -v 5 " >>> + if specifier: >>> + command += specifier >>> + >>> + self.interp.HandleCommand(command, result) >>> + self.assertTrue(result.Succeeded, "Set the target stop hook") >>> + (target, process, thread, bkpt) = >>> lldbutil.run_to_source_breakpoint(self, >>> + "Set a breakpoint here", >>> self.main_source_file) >>> + # At this point we've hit our stop hook so we should have run our >>> expression, >>> + # which increments g_var by the amount specified by the increment >>> key's value. >>> + while process.GetState() == lldb.eStateRunning: >>> + continue >>> + >>> + var = target.FindFirstGlobalVariable("g_var") >>> + self.assertTrue(var.IsValid()) >>> + self.assertEqual(var.GetValueAsUnsigned(), g_var_value, "Updated >>> g_var") >>> >>> diff --git a/lldb/test/API/commands/target/stop-hooks/TestStopHooks.py >>> b/lldb/test/API/commands/target/stop-hooks/TestStopHooks.py >>> index 64686afe627d..43447a845156 100644 >>> --- a/lldb/test/API/commands/target/stop-hooks/TestStopHooks.py >>> +++ b/lldb/test/API/commands/target/stop-hooks/TestStopHooks.py >>> @@ -1,5 +1,5 @@ >>> """ >>> -Test that stop hooks trigger on "step-out" >>> +Test stop hook functionality >>> """ >>> >>> >>> @@ -18,10 +18,15 @@ class TestStopHooks(TestBase): >>> # each debug info format. >>> NO_DEBUG_INFO_TESTCASE = True >>> >>> - def test_stop_hooks_step_out(self): >>> - """Test that stop hooks fire on step-out.""" >>> + def setUp(self): >>> + TestBase.setUp(self) >>> self.build() >>> self.main_source_file = lldb.SBFileSpec("main.c") >>> + full_path = os.path.join(self.getSourceDir(), "main.c") >>> + self.main_start_line = line_number(full_path, "main()") >>> + >>> + def test_stop_hooks_step_out(self): >>> + """Test that stop hooks fire on step-out.""" >>> self.step_out_test() >>> >>> def step_out_test(self): >>> @@ -37,4 +42,3 @@ def step_out_test(self): >>> self.assertTrue(var.IsValid()) >>> self.assertEqual(var.GetValueAsUnsigned(), 1, "Updated g_var") >>> >>> - >>> >>> diff --git a/lldb/test/API/commands/target/stop-hooks/main.c >>> b/lldb/test/API/commands/target/stop-hooks/main.c >>> index d08ad14776b5..16bfc0ce5db6 100644 >>> --- a/lldb/test/API/commands/target/stop-hooks/main.c >>> +++ b/lldb/test/API/commands/target/stop-hooks/main.c >>> @@ -10,5 +10,6 @@ int step_out_of_me() >>> int >>> main() >>> { >>> - return step_out_of_me(); >>> + int result = step_out_of_me(); // Stop here first >>> + return result; >>> } >>> >>> diff --git a/lldb/test/API/commands/target/stop-hooks/stop_hook.py >>> b/lldb/test/API/commands/target/stop-hooks/stop_hook.py >>> new file mode 100644 >>> index 000000000000..1abc2bdeeb31 >>> --- /dev/null >>> +++ b/lldb/test/API/commands/target/stop-hooks/stop_hook.py >>> @@ -0,0 +1,49 @@ >>> +import lldb >>> + >>> +class stop_handler: >>> + def __init__(self, target, extra_args, dict): >>> + self.extra_args = extra_args >>> + self.target = target >>> + self.counter = 0 >>> + ret_val = self.extra_args.GetValueForKey("return_false") >>> + if ret_val: >>> + self.ret_val = False >>> + else: >>> + self.ret_val = True >>> + >>> + def handle_stop(self, exe_ctx, stream): >>> + self.counter += 1 >>> + stream.Print("I have stopped %d times.\n"%(self.counter)) >>> + increment = 1 >>> + value = self.extra_args.GetValueForKey("increment") >>> + if value: >>> + incr_as_str = value.GetStringValue(100) >>> + increment = int(incr_as_str) >>> + else: >>> + stream.Print("Could not find increment in extra_args\n") >>> + frame = exe_ctx.GetFrame() >>> + expression = "g_var += %d"%(increment) >>> + expr_result = frame.EvaluateExpression(expression) >>> + if not expr_result.GetError().Success(): >>> + stream.Print("Error running expression: >>> %s"%(expr_result.GetError().GetCString())) >>> + value = exe_ctx.target.FindFirstGlobalVariable("g_var") >>> + if not value.IsValid(): >>> + stream.Print("Didn't get a valid value for g_var.") >>> + else: >>> + int_val = value.GetValueAsUnsigned() >>> + stream.Print("Returning value: %d from >>> handle_stop.\n"%(self.ret_val)) >>> + return self.ret_val >>> + >>> +class bad_handle_stop: >>> + def __init__(self, target, extra_args, dict): >>> + print("I am okay") >>> + >>> + def handle_stop(self): >>> + print("I am bad") >>> + >>> +class no_handle_stop: >>> + def __init__(self, target, extra_args, dict): >>> + print("I am okay") >>> + >>> + >>> + >>> >>> diff --git a/lldb/test/Shell/Commands/Inputs/stop_hook.py >>> b/lldb/test/Shell/Commands/Inputs/stop_hook.py >>> new file mode 100644 >>> index 000000000000..e319ca9ec5bc >>> --- /dev/null >>> +++ b/lldb/test/Shell/Commands/Inputs/stop_hook.py >>> @@ -0,0 +1,10 @@ >>> +import lldb >>> + >>> +class stop_handler: >>> + def __init__(self, target, extra_args, dict): >>> + self.extra_args = extra_args >>> + self.target = target >>> + >>> + def handle_stop(self, exe_ctx, stream): >>> + stream.Print("I did indeed run\n") >>> + return True >>> >>> diff --git a/lldb/test/Shell/Commands/command-stop-hook-output.test >>> b/lldb/test/Shell/Commands/command-stop-hook-output.test >>> new file mode 100644 >>> index 000000000000..7890bb3ca5e7 >>> --- /dev/null >>> +++ b/lldb/test/Shell/Commands/command-stop-hook-output.test >>> @@ -0,0 +1,19 @@ >>> +# REQUIRES: python >>> +# RUN: %clang_host -g %S/Inputs/main.c -o %t >>> +# RUN: %lldb %t -O 'command script import %S/Inputs/stop_hook.py' -s %s -o >>> exit | FileCheck %s >>> + >>> +b main >>> +# CHECK-LABEL: b main >>> +# CHECK: Breakpoint 1: where = {{.*}}`main >>> + >>> +target stop-hook add -P stop_hook.stop_handler >>> +# CHECK-LABEL: target stop-hook add -P stop_hook.stop_handler >>> +# CHECK: Stop hook #1 added. >>> + >>> +run >>> +# CHECK-LABEL: run >>> +# CHECK: I did indeed run >>> +# CHECK: Process {{.*}} stopped >>> +# CHECK: stop reason = breakpoint 1 >>> +# CHECK: frame #0: {{.*}}`main at main.c >>> + >>> >>> diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp >>> b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp >>> index f661835d191b..58ddf0c40a26 100644 >>> --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp >>> +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp >>> @@ -254,3 +254,17 @@ LLDBSWIGPython_GetDynamicSetting(void *module, const >>> char *setting, >>> const lldb::TargetSP &target_sp) { >>> return nullptr; >>> } >>> + >>> +extern "C" void *LLDBSwigPythonCreateScriptedStopHook( >>> + lldb::TargetSP target_sp, const char *python_class_name, >>> + const char *session_dictionary_name, >>> + lldb_private::StructuredDataImpl *args_impl, Status &error) { >>> + return nullptr; >>> +} >>> + >>> +extern "C" bool >>> +LLDBSwigPythonStopHookCallHandleStop(void *implementor, >>> + lldb::ExecutionContextRefSP >>> exc_ctx_sp, >>> + lldb::StreamSP stream) { >>> + return false; >>> +} >>> >>> >>> >>> _______________________________________________ >>> lldb-commits mailing list >>> lldb-commits@lists.llvm.org >>> https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits >>> >> > > _______________________________________________ > lldb-commits mailing list > lldb-commits@lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits