diff options
Diffstat (limited to 'lldb')
| -rw-r--r-- | lldb/cmake/modules/FindLuaAndSwig.cmake | 1 | ||||
| -rw-r--r-- | lldb/include/lldb/Target/Target.h | 81 | ||||
| -rw-r--r-- | lldb/source/Commands/CommandCompletions.cpp | 4 | ||||
| -rw-r--r-- | lldb/source/Commands/CommandObjectBreakpoint.cpp | 4 | ||||
| -rw-r--r-- | lldb/source/Commands/CommandObjectTarget.cpp | 63 | ||||
| -rw-r--r-- | lldb/source/Commands/Options.td | 10 | ||||
| -rw-r--r-- | lldb/source/Host/common/File.cpp | 23 | ||||
| -rw-r--r-- | lldb/source/Host/common/Socket.cpp | 4 | ||||
| -rw-r--r-- | lldb/source/Target/Target.cpp | 77 | ||||
| -rw-r--r-- | lldb/test/API/lua_api/TestLuaAPI.py | 4 | ||||
| -rw-r--r-- | lldb/test/Shell/ExecControl/StopHook/stop-hook-list.test | 70 |
11 files changed, 258 insertions, 83 deletions
diff --git a/lldb/cmake/modules/FindLuaAndSwig.cmake b/lldb/cmake/modules/FindLuaAndSwig.cmake index 33fadb2..c5df29e 100644 --- a/lldb/cmake/modules/FindLuaAndSwig.cmake +++ b/lldb/cmake/modules/FindLuaAndSwig.cmake @@ -34,6 +34,7 @@ else() FOUND_VAR LUAANDSWIG_FOUND REQUIRED_VARS + LUA_EXECUTABLE LUA_LIBRARIES LUA_INCLUDE_DIR LUA_VERSION_MINOR diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index f4a0923..c375df2 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -1356,7 +1356,11 @@ public: StopHook(const StopHook &rhs); virtual ~StopHook() = default; - enum class StopHookKind : uint32_t { CommandBased = 0, ScriptBased }; + enum class StopHookKind : uint32_t { + CommandBased = 0, + ScriptBased, + CodeBased, + }; enum class StopHookResult : uint32_t { KeepStopped = 0, RequestContinue, @@ -1403,6 +1407,12 @@ public: bool GetRunAtInitialStop() const { return m_at_initial_stop; } + void SetSuppressOutput(bool suppress_output) { + m_suppress_output = suppress_output; + } + + bool GetSuppressOutput() const { return m_suppress_output; } + void GetDescription(Stream &s, lldb::DescriptionLevel level) const; virtual void GetSubclassDescription(Stream &s, lldb::DescriptionLevel level) const = 0; @@ -1414,6 +1424,7 @@ public: bool m_active = true; bool m_auto_continue = false; bool m_at_initial_stop = true; + bool m_suppress_output = false; StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid); }; @@ -1433,8 +1444,8 @@ public: 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 + // Use CreateStopHook to make a new empty stop hook. Use SetActionFromString + // to fill it with commands, and SetSpecifier to set the specifier shared // pointer (can be null, that will match anything.) StopHookCommandLine(lldb::TargetSP target_sp, lldb::user_id_t uid) : StopHook(target_sp, uid) {} @@ -1460,19 +1471,56 @@ public: StructuredDataImpl m_extra_args; lldb::ScriptedStopHookInterfaceSP m_interface_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.) + /// Use CreateStopHook to make a new empty stop hook. Use SetScriptCallback + /// to set the script to execute, 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; }; + class StopHookCoded : public StopHook { + public: + ~StopHookCoded() override = default; + + using HandleStopCallback = StopHookResult(ExecutionContext &exc_ctx, + lldb::StreamSP output); + + void SetCallback(llvm::StringRef name, HandleStopCallback *callback) { + m_name = name; + m_callback = callback; + } + + StopHookResult HandleStop(ExecutionContext &exc_ctx, + lldb::StreamSP output) override { + return m_callback(exc_ctx, output); + } + + void GetSubclassDescription(Stream &s, + lldb::DescriptionLevel level) const override { + s.Indent(); + s.Printf("%s (built-in)\n", m_name.c_str()); + } + + private: + std::string m_name; + HandleStopCallback *m_callback; + + /// Use CreateStopHook to make a new empty stop hook. Use SetCallback to set + /// the callback to execute, and SetSpecifier to set the specifier shared + /// pointer (can be null, that will match anything.) + StopHookCoded(lldb::TargetSP target_sp, lldb::user_id_t uid) + : StopHook(target_sp, uid) {} + friend class Target; + }; + + void RegisterInternalStopHooks(); + 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(StopHook::StopHookKind kind); + /// shared pointer to the new hook. + StopHookSP CreateStopHook(StopHook::StopHookKind kind, bool internal = false); /// 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. @@ -1484,8 +1532,6 @@ public: // control over the process for the first time. bool RunStopHooks(bool at_initial_stop = false); - size_t GetStopHookSize(); - bool SetSuppresStopHooks(bool suppress) { bool old_value = m_suppress_stop_hooks; m_suppress_stop_hooks = suppress; @@ -1504,19 +1550,7 @@ public: void SetAllStopHooksActiveState(bool active_state); - size_t GetNumStopHooks() const { return m_stop_hooks.size(); } - - StopHookSP GetStopHookAtIndex(size_t index) { - if (index >= GetNumStopHooks()) - return StopHookSP(); - StopHookCollection::iterator pos = m_stop_hooks.begin(); - - while (index > 0) { - pos++; - index--; - } - return (*pos).second; - } + const std::vector<StopHookSP> GetStopHooks(bool internal = false) const; lldb::PlatformSP GetPlatform() { return m_platform_sp; } @@ -1656,6 +1690,7 @@ protected: typedef std::map<lldb::user_id_t, StopHookSP> StopHookCollection; StopHookCollection m_stop_hooks; lldb::user_id_t m_stop_hook_next_id; + std::vector<StopHookSP> m_internal_stop_hooks; uint32_t m_latest_stop_hook_id; /// This records the last natural stop at /// which we ran a stop-hook. bool m_valid; diff --git a/lldb/source/Commands/CommandCompletions.cpp b/lldb/source/Commands/CommandCompletions.cpp index b2fc893..c60d303 100644 --- a/lldb/source/Commands/CommandCompletions.cpp +++ b/lldb/source/Commands/CommandCompletions.cpp @@ -777,13 +777,11 @@ void CommandCompletions::StopHookIDs(CommandInterpreter &interpreter, if (!target_sp) return; - const size_t num = target_sp->GetNumStopHooks(); - for (size_t idx = 0; idx < num; ++idx) { + for (auto &stophook_sp : target_sp->GetStopHooks()) { StreamString strm; // The value 11 is an offset to make the completion description looks // neater. strm.SetIndentLevel(11); - const Target::StopHookSP stophook_sp = target_sp->GetStopHookAtIndex(idx); stophook_sp->GetDescription(strm, lldb::eDescriptionLevelInitial); request.TryCompleteCurrentArg(std::to_string(stophook_sp->GetID()), strm.GetString()); diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp index de0a7e7..5a55126 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -1114,9 +1114,7 @@ public: CommandObjectBreakpointList(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "breakpoint list", - "List some or all breakpoints at configurable levels of detail.", - nullptr) { - CommandArgumentData bp_id_arg; + "List some or all breakpoints at configurable levels of detail.") { // Define the first (and only) variant of this arg. AddSimpleArgumentList(eArgTypeBreakpointID, eArgRepeatOptional); diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index c59d028..8de6521 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -5223,33 +5223,72 @@ private: #pragma mark CommandObjectTargetStopHookList // CommandObjectTargetStopHookList +#define LLDB_OPTIONS_target_stop_hook_list +#include "CommandOptions.inc" class CommandObjectTargetStopHookList : public CommandObjectParsed { public: CommandObjectTargetStopHookList(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "target stop-hook list", - "List all stop-hooks.", "target stop-hook list") {} + "List all stop-hooks.") {} ~CommandObjectTargetStopHookList() override = default; + Options *GetOptions() override { return &m_options; } + + class CommandOptions : public Options { + public: + CommandOptions() = default; + ~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; + + switch (short_option) { + case 'i': + m_internal = true; + break; + default: + llvm_unreachable("Unimplemented option"); + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_internal = false; + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::ArrayRef(g_target_stop_hook_list_options); + } + + // Instance variables to hold the values for command options. + bool m_internal = false; + }; + protected: void DoExecute(Args &command, CommandReturnObject &result) override { Target &target = GetTarget(); - size_t num_hooks = target.GetNumStopHooks(); - if (num_hooks == 0) { - result.GetOutputStream().PutCString("No stop hooks.\n"); - } else { - for (size_t i = 0; i < num_hooks; i++) { - Target::StopHookSP this_hook = target.GetStopHookAtIndex(i); - if (i > 0) - result.GetOutputStream().PutCString("\n"); - this_hook->GetDescription(result.GetOutputStream(), - eDescriptionLevelFull); - } + bool printed_hook = false; + for (auto &hook : target.GetStopHooks(m_options.m_internal)) { + if (printed_hook) + result.GetOutputStream().PutCString("\n"); + hook->GetDescription(result.GetOutputStream(), eDescriptionLevelFull); + printed_hook = true; } + + if (!printed_hook) + result.GetOutputStream().PutCString("No stop hooks.\n"); + result.SetStatus(eReturnStatusSuccessFinishResult); } + +private: + CommandOptions m_options; }; #pragma mark CommandObjectMultiwordTargetStopHooks diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index a9f054e..ed06131 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -77,7 +77,7 @@ let Command = "breakpoint list" in { // FIXME: We need to add an "internal" command, and then add this sort of // thing to it. But I need to see it for now, and don't want to wait. def blist_internal : Option<"internal", "i">, - Desc<"Show debugger ${i}nternal breakpoints">; + Desc<"Show debugger ${i}nternal breakpoints.">; def blist_brief : Option<"brief", "b">, Group<1>, Desc<"Give a ${b}rief description of the breakpoint (no " @@ -1686,7 +1686,7 @@ let Command = "target modules lookup" in { "match, if a best match is available.">; } -let Command = "target stop hook add" in { +let Command = "target stop_hook add" in { def target_stop_hook_add_one_liner : Option<"one-liner", "o">, GroupRange<1, 3>, @@ -1762,6 +1762,12 @@ let Command = "target stop hook add" in { "Defaults to true.">; } +let Command = "target stop_hook list" in { + def target_stop_hook_list_internal + : Option<"internal", "i">, + Desc<"Show debugger ${i}nternal stop hooks.">; +} + let Command = "thread backtrace" in { def thread_backtrace_count : Option<"count", "c">, Group<1>, diff --git a/lldb/source/Host/common/File.cpp b/lldb/source/Host/common/File.cpp index 1272f13..65b75bd 100644 --- a/lldb/source/Host/common/File.cpp +++ b/lldb/source/Host/common/File.cpp @@ -81,18 +81,17 @@ File::GetStreamOpenModeFromOptions(File::OpenOptions options) { Expected<File::OpenOptions> File::GetOptionsFromMode(llvm::StringRef mode) { OpenOptions opts = llvm::StringSwitch<OpenOptions>(mode) - .Cases("r", "rb", eOpenOptionReadOnly) - .Cases("w", "wb", eOpenOptionWriteOnly) - .Cases("a", "ab", - eOpenOptionWriteOnly | eOpenOptionAppend | - eOpenOptionCanCreate) - .Cases("r+", "rb+", "r+b", eOpenOptionReadWrite) - .Cases("w+", "wb+", "w+b", - eOpenOptionReadWrite | eOpenOptionCanCreate | - eOpenOptionTruncate) - .Cases("a+", "ab+", "a+b", - eOpenOptionReadWrite | eOpenOptionAppend | - eOpenOptionCanCreate) + .Cases({"r", "rb"}, eOpenOptionReadOnly) + .Cases({"w", "wb"}, eOpenOptionWriteOnly) + .Cases({"a", "ab"}, eOpenOptionWriteOnly | eOpenOptionAppend | + eOpenOptionCanCreate) + .Cases({"r+", "rb+", "r+b"}, eOpenOptionReadWrite) + .Cases({"w+", "wb+", "w+b"}, eOpenOptionReadWrite | + eOpenOptionCanCreate | + eOpenOptionTruncate) + .Cases({"a+", "ab+", "a+b"}, eOpenOptionReadWrite | + eOpenOptionAppend | + eOpenOptionCanCreate) .Default(eOpenOptionInvalid); if (opts != eOpenOptionInvalid) return opts; diff --git a/lldb/source/Host/common/Socket.cpp b/lldb/source/Host/common/Socket.cpp index bc3d849..eb333f7 100644 --- a/lldb/source/Host/common/Socket.cpp +++ b/lldb/source/Host/common/Socket.cpp @@ -500,13 +500,13 @@ Socket::GetProtocolAndMode(llvm::StringRef scheme) { return llvm::StringSwitch<std::optional<ProtocolModePair>>(scheme) .Case("listen", ProtocolModePair{SocketProtocol::ProtocolTcp, SocketMode::ModeAccept}) - .Cases("accept", "unix-accept", + .Cases({"accept", "unix-accept"}, ProtocolModePair{SocketProtocol::ProtocolUnixDomain, SocketMode::ModeAccept}) .Case("unix-abstract-accept", ProtocolModePair{SocketProtocol::ProtocolUnixAbstract, SocketMode::ModeAccept}) - .Cases("connect", "tcp-connect", "connection", + .Cases({"connect", "tcp-connect", "connection"}, ProtocolModePair{SocketProtocol::ProtocolTcp, SocketMode::ModeConnect}) .Case("udp", ProtocolModePair{SocketProtocol::ProtocolTcp, diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index e224a12..d070c3d 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -183,8 +183,8 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch, m_watchpoint_list(), m_process_sp(), m_search_filter_sp(), m_image_search_paths(ImageSearchPathsChanged, this), m_source_manager_up(), m_stop_hooks(), m_stop_hook_next_id(0), - m_latest_stop_hook_id(0), m_valid(true), m_suppress_stop_hooks(false), - m_is_dummy_target(is_dummy_target), + m_internal_stop_hooks(), m_latest_stop_hook_id(0), m_valid(true), + m_suppress_stop_hooks(false), m_is_dummy_target(is_dummy_target), m_target_unique_id(g_target_unique_id++), m_frame_recognizer_manager_up( std::make_unique<StackFrameRecognizerManager>()) { @@ -217,6 +217,7 @@ Target::~Target() { void Target::PrimeFromDummyTarget(Target &target) { m_stop_hooks = target.m_stop_hooks; m_stop_hook_next_id = target.m_stop_hook_next_id; + m_internal_stop_hooks = target.m_internal_stop_hooks; for (const auto &breakpoint_sp : target.m_breakpoint_list.Breakpoints()) { if (breakpoint_sp->IsInternal()) @@ -383,6 +384,7 @@ void Target::Destroy() { m_image_search_paths.Clear(notify); m_stop_hooks.clear(); m_stop_hook_next_id = 0; + m_internal_stop_hooks.clear(); m_suppress_stop_hooks = false; m_repl_map.clear(); Args signal_args; @@ -3041,8 +3043,9 @@ SourceManager &Target::GetSourceManager() { return *m_source_manager_up; } -Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind) { - lldb::user_id_t new_uid = ++m_stop_hook_next_id; +Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind, + bool internal) { + user_id_t new_uid = (internal ? LLDB_INVALID_UID : ++m_stop_hook_next_id); Target::StopHookSP stop_hook_sp; switch (kind) { case StopHook::StopHookKind::CommandBased: @@ -3051,8 +3054,14 @@ Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind) { case StopHook::StopHookKind::ScriptBased: stop_hook_sp.reset(new StopHookScripted(shared_from_this(), new_uid)); break; + case StopHook::StopHookKind::CodeBased: + stop_hook_sp.reset(new StopHookCoded(shared_from_this(), new_uid)); + break; } - m_stop_hooks[new_uid] = stop_hook_sp; + if (internal) + m_internal_stop_hooks.push_back(stop_hook_sp); + else + m_stop_hooks[new_uid] = stop_hook_sp; return stop_hook_sp; } @@ -3098,6 +3107,23 @@ void Target::SetAllStopHooksActiveState(bool active_state) { } } +// FIXME: Ideally we would like to return a `const &` (const reference) instead +// of creating copy here, but that is not possible due to different container +// types. In C++20, we should be able to use `std::ranges::views::values` to +// adapt the key-pair entries in the `std::map` (behind `StopHookCollection`) +// to avoid creating the copy. +const std::vector<Target::StopHookSP> +Target::GetStopHooks(bool internal) const { + if (internal) + return m_internal_stop_hooks; + + std::vector<StopHookSP> stop_hooks; + for (auto &[_, hook] : m_stop_hooks) + stop_hooks.push_back(hook); + + return stop_hooks; +} + bool Target::RunStopHooks(bool at_initial_stop) { if (m_suppress_stop_hooks) return false; @@ -3111,16 +3137,20 @@ bool Target::RunStopHooks(bool at_initial_stop) { if (!(state == eStateStopped || state == eStateAttaching)) return false; - if (m_stop_hooks.empty()) - return false; + auto is_active = [at_initial_stop](StopHookSP hook) { + bool should_run_now = (!at_initial_stop || hook->GetRunAtInitialStop()); + return hook->IsActive() && should_run_now; + }; - bool no_active_hooks = - llvm::none_of(m_stop_hooks, [at_initial_stop](auto &p) { - bool should_run_now = - !at_initial_stop || p.second->GetRunAtInitialStop(); - return p.second->IsActive() && should_run_now; - }); - if (no_active_hooks) + // Create list of active internal and user stop hooks. + std::vector<StopHookSP> active_hooks; + llvm::copy_if(m_internal_stop_hooks, std::back_inserter(active_hooks), + is_active); + for (auto &[_, hook] : m_stop_hooks) { + if (is_active(hook)) + active_hooks.push_back(hook); + } + if (active_hooks.empty()) return false; // Make sure we check that we are not stopped because of us running a user @@ -3169,24 +3199,21 @@ bool Target::RunStopHooks(bool at_initial_stop) { StreamSP output_sp = m_debugger.GetAsyncOutputStream(); auto on_exit = llvm::make_scope_exit([output_sp] { output_sp->Flush(); }); - bool print_hook_header = (m_stop_hooks.size() != 1); - bool print_thread_header = (num_exe_ctx != 1); + size_t num_hooks_with_output = llvm::count_if( + active_hooks, [](auto h) { return !h->GetSuppressOutput(); }); + bool print_hook_header = (num_hooks_with_output > 1); + bool print_thread_header = (num_exe_ctx > 1); bool should_stop = false; bool requested_continue = false; - for (auto stop_entry : m_stop_hooks) { - StopHookSP cur_hook_sp = stop_entry.second; - if (!cur_hook_sp->IsActive()) - continue; - if (at_initial_stop && !cur_hook_sp->GetRunAtInitialStop()) - continue; - + for (auto cur_hook_sp : active_hooks) { bool any_thread_matched = false; for (auto exc_ctx : exc_ctx_with_reasons) { if (!cur_hook_sp->ExecutionContextPasses(exc_ctx)) continue; - if (print_hook_header && !any_thread_matched) { + bool suppress_output = cur_hook_sp->GetSuppressOutput(); + if (print_hook_header && !any_thread_matched && !suppress_output) { StreamString s; cur_hook_sp->GetDescription(s, eDescriptionLevelBrief); if (s.GetSize() != 0) @@ -3197,7 +3224,7 @@ bool Target::RunStopHooks(bool at_initial_stop) { any_thread_matched = true; } - if (print_thread_header) + if (print_thread_header && !suppress_output) output_sp->Printf("-- Thread %d\n", exc_ctx.GetThreadPtr()->GetIndexID()); diff --git a/lldb/test/API/lua_api/TestLuaAPI.py b/lldb/test/API/lua_api/TestLuaAPI.py index 4ac795d..e78ed9d 100644 --- a/lldb/test/API/lua_api/TestLuaAPI.py +++ b/lldb/test/API/lua_api/TestLuaAPI.py @@ -158,7 +158,9 @@ class TestLuaAPI(TestBase): return tests def test_lua_api(self): - if "LUA_EXECUTABLE" not in os.environ or len(os.environ["LUA_EXECUTABLE"]) == 0: + if "LUA_EXECUTABLE" not in os.environ or not os.path.exists( + os.environ["LUA_EXECUTABLE"] + ): self.skipTest("Lua API tests could not find Lua executable.") return lua_executable = os.environ["LUA_EXECUTABLE"] diff --git a/lldb/test/Shell/ExecControl/StopHook/stop-hook-list.test b/lldb/test/Shell/ExecControl/StopHook/stop-hook-list.test new file mode 100644 index 0000000..42d0a67 --- /dev/null +++ b/lldb/test/Shell/ExecControl/StopHook/stop-hook-list.test @@ -0,0 +1,70 @@ +# Test stop hook user ID assignment, ordering, and printing. +# +# RUN: %lldb -b -s %s | FileCheck %s + +# Create some stop hooks +target stop-hook add -o 'print "Hello"' +target stop-hook add -o 'print "world,"' +target stop-hook add -o 'print "nice"' +target stop-hook add -o 'print "weather"' +target stop-hook add -o 'print "today!"' + +# Print hooks +target stop-hook list + +# CHECK: (lldb) target stop-hook list +# CHECK: Hook: 1 +# CHECK: "Hello" +# CHECK: Hook: 2 +# CHECK: "world," +# CHECK: Hook: 3 +# CHECK: "nice" +# CHECK: Hook: 4 +# CHECK: "weather" +# CHECK: Hook: 5 +# CHECK: "today!" + +# Delete last hook, then add new one +target stop-hook delete 5 +target stop-hook add -o 'print "Sunshine,"' + +# Stop hook gets new user ID (it is not reused) +# CHECK: (lldb) target stop-hook add -o 'print "Sunshine,"' +# CHECK: Stop hook #6 added. + +target stop-hook list +# CHECK: (lldb) target stop-hook list +# CHECK: Hook: 4 +# CHECK-NOT: Hook: 5 +# CHECK: Hook: 6 + +# Add a few more hooks +target stop-hook add -o 'print "rain,"' +target stop-hook add -o 'print "and wind!"' +target stop-hook add -o 'print "It is all okay!"' +# CHECK: Stop hook #7 added. +# CHECK: Stop hook #8 added. +# CHECK: Stop hook #9 added. + +# Delete a few hooks +target stop-hook delete 1 +target stop-hook delete 3 +target stop-hook delete 7 +target stop-hook delete 9 + +# Check that the list is still well-ordered +target stop-hook list +# CHECK: (lldb) target stop-hook list +# CHECK-NOT: Hook: 1 +# CHECK: Hook: 2 +# CHECK: "world," +# CHECK-NOT: Hook: 3 +# CHECK: Hook: 4 +# CHECK: "weather" +# CHECK-NOT: Hook: 5 +# CHECK: Hook: 6 +# CHECK: "Sunshine," +# CHECK-NOT: Hook: 7 +# CHECK: Hook: 8 +# CHECK: "and wind!" +# CHECK-NOT: Hook: 9 |
