https://github.com/mdko updated https://github.com/llvm/llvm-project/pull/73596
>From 97a6e23c85457a14c91c5800fa03bb872e6f1fa6 Mon Sep 17 00:00:00 2001 From: Michael Christensen <mchristen...@meta.com> Date: Mon, 27 Nov 2023 12:49:24 -0800 Subject: [PATCH 1/5] Add option to pass thread ID to thread select command --- lldb/source/Commands/CommandObjectThread.cpp | 59 +++++++++++++++++-- lldb/source/Commands/Options.td | 5 ++ .../thread/select/TestThreadSelect.py | 23 +++++++- 3 files changed, 78 insertions(+), 9 deletions(-) diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index a9f5a4f8a4fbd71..9384df319cc221d 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -1129,8 +1129,44 @@ class CommandObjectThreadUntil : public CommandObjectParsed { // CommandObjectThreadSelect +#define LLDB_OPTIONS_thread_select +#include "CommandOptions.inc" + class CommandObjectThreadSelect : public CommandObjectParsed { public: + class CommandOptions : public Options { + public: + CommandOptions() { OptionParsingStarting(nullptr); } + + ~CommandOptions() override = default; + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_thread_id = false; + } + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + const int short_option = m_getopt_table[option_idx].val; + switch (short_option) { + case 't': { + m_thread_id = true; + break; + } + + default: + llvm_unreachable("Unimplemented option"); + } + + return {}; + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::ArrayRef(g_thread_select_options); + } + + bool m_thread_id; + }; + CommandObjectThreadSelect(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "thread select", "Change the currently selected thread.", nullptr, @@ -1165,6 +1201,8 @@ class CommandObjectThreadSelect : public CommandObjectParsed { nullptr); } + Options *GetOptions() override { return &m_options; } + protected: void DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); @@ -1173,22 +1211,29 @@ class CommandObjectThreadSelect : public CommandObjectParsed { return; } else if (command.GetArgumentCount() != 1) { result.AppendErrorWithFormat( - "'%s' takes exactly one thread index argument:\nUsage: %s\n", - m_cmd_name.c_str(), m_cmd_syntax.c_str()); + "'%s' takes exactly one thread %s argument:\nUsage: %s\n", + m_cmd_name.c_str(), m_options.m_thread_id ? "ID" : "index", + m_cmd_syntax.c_str()); return; } uint32_t index_id; if (!llvm::to_integer(command.GetArgumentAtIndex(0), index_id)) { - result.AppendErrorWithFormat("Invalid thread index '%s'", + result.AppendErrorWithFormat("Invalid thread %s '%s'", + m_options.m_thread_id ? "ID" : "index", command.GetArgumentAtIndex(0)); return; } - Thread *new_thread = - process->GetThreadList().FindThreadByIndexID(index_id).get(); + Thread *new_thread = nullptr; + if (m_options.m_thread_id) { + new_thread = process->GetThreadList().FindThreadByID(index_id).get(); + } else { + new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get(); + } if (new_thread == nullptr) { - result.AppendErrorWithFormat("invalid thread #%s.\n", + result.AppendErrorWithFormat("invalid thread %s%s.\n", + m_options.m_thread_id ? "ID " : "#", command.GetArgumentAtIndex(0)); return; } @@ -1196,6 +1241,8 @@ class CommandObjectThreadSelect : public CommandObjectParsed { process->GetThreadList().SetSelectedThreadByID(new_thread->GetID(), true); result.SetStatus(eReturnStatusSuccessFinishNoResult); } + + CommandOptions m_options; }; // CommandObjectThreadList diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index 542c78be5a12dad..23886046df8f673 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -1117,6 +1117,11 @@ let Command = "thread plan list" in { Desc<"Display thread plans for unreported threads">; } +let Command = "thread select" in { + def thread_thread_id : Option<"thread_id", "t">, + Desc<"Provide a thread ID instead of a thread index.">; +} + let Command = "thread trace dump function calls" in { def thread_trace_dump_function_calls_file : Option<"file", "F">, Group<1>, Arg<"Filename">, diff --git a/lldb/test/API/commands/thread/select/TestThreadSelect.py b/lldb/test/API/commands/thread/select/TestThreadSelect.py index 91f8909471bf2bb..4d01b82d9d947e5 100644 --- a/lldb/test/API/commands/thread/select/TestThreadSelect.py +++ b/lldb/test/API/commands/thread/select/TestThreadSelect.py @@ -12,17 +12,34 @@ def test_invalid_arg(self): self, "// break here", lldb.SBFileSpec("main.cpp") ) - self.expect( - "thread select -1", error=True, startstr="error: Invalid thread index '-1'" - ) self.expect( "thread select 0x1ffffffff", error=True, startstr="error: Invalid thread index '0x1ffffffff'", ) + self.expect( + "thread select -t 0x1ffffffff", + error=True, + startstr="error: Invalid thread ID '0x1ffffffff'", + ) # Parses but not a valid thread id. self.expect( "thread select 0xffffffff", error=True, startstr="error: invalid thread #0xffffffff.", ) + self.expect( + "thread select -t 0xffffffff", + error=True, + startstr="error: invalid thread ID 0xffffffff.", + ) + + def test_thread_select_tid(self): + self.build() + + lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("main.cpp") + ) + self.runCmd( + "thread select -t %d" % self.thread().GetThreadID(), + ) >From 34045b9b2e04e01fed142ad2d7f4503e69646c9f Mon Sep 17 00:00:00 2001 From: Michael Christensen <mchristen...@meta.com> Date: Tue, 28 Nov 2023 17:44:03 -0800 Subject: [PATCH 2/5] Code review changes -- update thread-id argument name, separate into separate group, and format --- lldb/source/Commands/CommandObjectThread.cpp | 72 +++++++++++-------- lldb/source/Commands/Options.td | 4 +- .../thread/select/TestThreadSelect.py | 16 ++++- 3 files changed, 58 insertions(+), 34 deletions(-) diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index 9384df319cc221d..9f535a1b6c944f3 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -1134,22 +1134,25 @@ class CommandObjectThreadUntil : public CommandObjectParsed { class CommandObjectThreadSelect : public CommandObjectParsed { public: - class CommandOptions : public Options { + class OptionGroupThreadSelect : public OptionGroup { public: - CommandOptions() { OptionParsingStarting(nullptr); } + OptionGroupThreadSelect() { OptionParsingStarting(nullptr); } - ~CommandOptions() override = default; + ~OptionGroupThreadSelect() override = default; void OptionParsingStarting(ExecutionContext *execution_context) override { - m_thread_id = false; + m_thread_id = LLDB_INVALID_THREAD_ID; } Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { - const int short_option = m_getopt_table[option_idx].val; + const int short_option = g_thread_select_options[option_idx].short_option; switch (short_option) { case 't': { - m_thread_id = true; + if (option_arg.getAsInteger(0, m_thread_id)) { + m_thread_id = LLDB_INVALID_THREAD_ID; + return Status("Invalid thread ID: '%s'.", option_arg.str().c_str()); + } break; } @@ -1164,7 +1167,7 @@ class CommandObjectThreadSelect : public CommandObjectParsed { return llvm::ArrayRef(g_thread_select_options); } - bool m_thread_id; + lldb::tid_t m_thread_id; }; CommandObjectThreadSelect(CommandInterpreter &interpreter) @@ -1179,6 +1182,7 @@ class CommandObjectThreadSelect : public CommandObjectParsed { // Define the first (and only) variant of this arg. thread_idx_arg.arg_type = eArgTypeThreadIndex; thread_idx_arg.arg_repetition = eArgRepeatPlain; + thread_idx_arg.arg_opt_set_association = LLDB_OPT_SET_1; // There is only one variant this argument could be; put it into the // argument entry. @@ -1186,6 +1190,9 @@ class CommandObjectThreadSelect : public CommandObjectParsed { // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); + + m_option_group.Append(&m_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_2); + m_option_group.Finalize(); } ~CommandObjectThreadSelect() override = default; @@ -1201,7 +1208,7 @@ class CommandObjectThreadSelect : public CommandObjectParsed { nullptr); } - Options *GetOptions() override { return &m_options; } + Options *GetOptions() override { return &m_option_group; } protected: void DoExecute(Args &command, CommandReturnObject &result) override { @@ -1209,40 +1216,47 @@ class CommandObjectThreadSelect : public CommandObjectParsed { if (process == nullptr) { result.AppendError("no process"); return; - } else if (command.GetArgumentCount() != 1) { + } else if (m_options.m_thread_id == LLDB_INVALID_THREAD_ID && command.GetArgumentCount() != 1) { result.AppendErrorWithFormat( - "'%s' takes exactly one thread %s argument:\nUsage: %s\n", - m_cmd_name.c_str(), m_options.m_thread_id ? "ID" : "index", - m_cmd_syntax.c_str()); + "'%s' takes exactly one thread index argument, or a thread ID option:\nUsage: %s\n", + m_cmd_name.c_str(), m_cmd_syntax.c_str()); return; - } - - uint32_t index_id; - if (!llvm::to_integer(command.GetArgumentAtIndex(0), index_id)) { - result.AppendErrorWithFormat("Invalid thread %s '%s'", - m_options.m_thread_id ? "ID" : "index", - command.GetArgumentAtIndex(0)); + } else if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID && command.GetArgumentCount() != 0) { + result.AppendErrorWithFormat( + "'%s' cannot take both a thread ID option and a thread index argument:\nUsage: %s\n", + m_cmd_name.c_str(), m_cmd_syntax.c_str()); return; } Thread *new_thread = nullptr; - if (m_options.m_thread_id) { - new_thread = process->GetThreadList().FindThreadByID(index_id).get(); + if (command.GetArgumentCount() == 1) { + uint32_t index_id; + if (!llvm::to_integer(command.GetArgumentAtIndex(0), index_id)) { + result.AppendErrorWithFormat("Invalid thread index '%s'", + command.GetArgumentAtIndex(0)); + return; + } + new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get(); + if (new_thread == nullptr) { + result.AppendErrorWithFormat("Invalid thread #%s.\n", + command.GetArgumentAtIndex(0)); + return; + } } else { - new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get(); - } - if (new_thread == nullptr) { - result.AppendErrorWithFormat("invalid thread %s%s.\n", - m_options.m_thread_id ? "ID " : "#", - command.GetArgumentAtIndex(0)); - return; + new_thread = process->GetThreadList().FindThreadByID(m_options.m_thread_id).get(); + if (new_thread == nullptr) { + result.AppendErrorWithFormat("Invalid thread ID %lu.\n", + m_options.m_thread_id); + return; + } } process->GetThreadList().SetSelectedThreadByID(new_thread->GetID(), true); result.SetStatus(eReturnStatusSuccessFinishNoResult); } - CommandOptions m_options; + OptionGroupThreadSelect m_options; + OptionGroupOptions m_option_group; }; // CommandObjectThreadList diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index 23886046df8f673..558f2fbf962842a 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -1118,8 +1118,8 @@ let Command = "thread plan list" in { } let Command = "thread select" in { - def thread_thread_id : Option<"thread_id", "t">, - Desc<"Provide a thread ID instead of a thread index.">; + def thread_select_thread_id : Option<"thread-id", "t">, Group<2>, + Arg<"ThreadID">, Desc<"Provide a thread ID instead of a thread index.">; } let Command = "thread trace dump function calls" in { diff --git a/lldb/test/API/commands/thread/select/TestThreadSelect.py b/lldb/test/API/commands/thread/select/TestThreadSelect.py index 4d01b82d9d947e5..2345dc2b2b36a90 100644 --- a/lldb/test/API/commands/thread/select/TestThreadSelect.py +++ b/lldb/test/API/commands/thread/select/TestThreadSelect.py @@ -20,18 +20,28 @@ def test_invalid_arg(self): self.expect( "thread select -t 0x1ffffffff", error=True, - startstr="error: Invalid thread ID '0x1ffffffff'", + startstr="error: Invalid thread ID", + ) + self.expect( + "thread select 1 2 3", + error=True, + startstr="error: 'thread select' takes exactly one thread index argument, or a thread ID option:", + ) + self.expect( + "thread select -t 1234 1", + error=True, + startstr="error: 'thread select' cannot take both a thread ID option and a thread index argument:", ) # Parses but not a valid thread id. self.expect( "thread select 0xffffffff", error=True, - startstr="error: invalid thread #0xffffffff.", + startstr="error: Invalid thread #0xffffffff.", ) self.expect( "thread select -t 0xffffffff", error=True, - startstr="error: invalid thread ID 0xffffffff.", + startstr="error: Invalid thread ID", ) def test_thread_select_tid(self): >From 3ef88cd6c82dd08fcbf504283b70049628feb462 Mon Sep 17 00:00:00 2001 From: Michael Christensen <mchristen...@meta.com> Date: Tue, 28 Nov 2023 17:59:24 -0800 Subject: [PATCH 3/5] Fix formatting --- lldb/source/Commands/CommandObjectThread.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index 9f535a1b6c944f3..35618551c2f05e3 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -1216,15 +1216,18 @@ class CommandObjectThreadSelect : public CommandObjectParsed { if (process == nullptr) { result.AppendError("no process"); return; - } else if (m_options.m_thread_id == LLDB_INVALID_THREAD_ID && command.GetArgumentCount() != 1) { + } else if (m_options.m_thread_id == LLDB_INVALID_THREAD_ID && + command.GetArgumentCount() != 1) { result.AppendErrorWithFormat( - "'%s' takes exactly one thread index argument, or a thread ID option:\nUsage: %s\n", + "'%s' takes exactly one thread index argument, or a thread ID " + "option:\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); return; - } else if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID && command.GetArgumentCount() != 0) { - result.AppendErrorWithFormat( - "'%s' cannot take both a thread ID option and a thread index argument:\nUsage: %s\n", - m_cmd_name.c_str(), m_cmd_syntax.c_str()); + } else if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID && + command.GetArgumentCount() != 0) { + result.AppendErrorWithFormat("'%s' cannot take both a thread ID option " + "and a thread index argument:\nUsage: %s\n", + m_cmd_name.c_str(), m_cmd_syntax.c_str()); return; } @@ -1239,11 +1242,12 @@ class CommandObjectThreadSelect : public CommandObjectParsed { new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get(); if (new_thread == nullptr) { result.AppendErrorWithFormat("Invalid thread #%s.\n", - command.GetArgumentAtIndex(0)); + command.GetArgumentAtIndex(0)); return; } } else { - new_thread = process->GetThreadList().FindThreadByID(m_options.m_thread_id).get(); + new_thread = + process->GetThreadList().FindThreadByID(m_options.m_thread_id).get(); if (new_thread == nullptr) { result.AppendErrorWithFormat("Invalid thread ID %lu.\n", m_options.m_thread_id); >From e30a155562439f42feea990f6c109f53c218047e Mon Sep 17 00:00:00 2001 From: Michael Christensen <mchristen...@meta.com> Date: Tue, 28 Nov 2023 22:56:09 -0800 Subject: [PATCH 4/5] Code review changes -- update help text and usage text, fix format specifier, add thread ID completion --- .../lldb/Interpreter/CommandCompletions.h | 3 +++ .../Interpreter/CommandOptionArgumentTable.h | 1 + lldb/include/lldb/lldb-enumerations.h | 3 ++- lldb/source/Commands/CommandCompletions.cpp | 18 ++++++++++++++++++ lldb/source/Commands/CommandObjectThread.cpp | 7 ++++--- lldb/source/Commands/Options.td | 3 ++- .../commands/thread/select/TestThreadSelect.py | 2 +- 7 files changed, 31 insertions(+), 6 deletions(-) diff --git a/lldb/include/lldb/Interpreter/CommandCompletions.h b/lldb/include/lldb/Interpreter/CommandCompletions.h index a89a5be95b801ce..c7292b3b1471a8b 100644 --- a/lldb/include/lldb/Interpreter/CommandCompletions.h +++ b/lldb/include/lldb/Interpreter/CommandCompletions.h @@ -120,6 +120,9 @@ class CommandCompletions { CompletionRequest &request, SearchFilter *searcher); + static void ThreadIDs(CommandInterpreter &interpreter, + CompletionRequest &request, SearchFilter *searcher); + /// This completer works for commands whose only arguments are a command path. /// It isn't tied to an argument type because it completes not on a single /// argument but on the sequence of arguments, so you have to invoke it by diff --git a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h index 4bf71393bb07dca..46d2a43a8e0c415 100644 --- a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h +++ b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h @@ -191,6 +191,7 @@ static constexpr OptionEnumValueElement g_completion_type[] = { "Completes to a remote disk directory."}, {lldb::eTypeCategoryNameCompletion, "type-category-name", "Completes to a type category name."}, + {lldb::eThreadIDCompletion, "thread-id", "Completes to a thread ID."}, {lldb::eCustomCompletion, "custom", "Custom completion."}, }; diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 633a3ee696c2087..3345c0659ae6721 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -1296,10 +1296,11 @@ enum CompletionType { eRemoteDiskFileCompletion = (1u << 22), eRemoteDiskDirectoryCompletion = (1u << 23), eTypeCategoryNameCompletion = (1u << 24), + eThreadIDCompletion = (1u << 25), // This item serves two purposes. It is the last element in the enum, so // you can add custom enums starting from here in your Option class. Also // if you & in this bit the base code will not process the option. - eCustomCompletion = (1u << 25) + eCustomCompletion = (1u << 26) }; } // namespace lldb diff --git a/lldb/source/Commands/CommandCompletions.cpp b/lldb/source/Commands/CommandCompletions.cpp index 4d7e3d7f2497bb1..6b9bf1e3ca573fd 100644 --- a/lldb/source/Commands/CommandCompletions.cpp +++ b/lldb/source/Commands/CommandCompletions.cpp @@ -83,6 +83,7 @@ bool CommandCompletions::InvokeCommonCompletionCallbacks( CommandCompletions::RemoteDiskDirectories}, {lldb::eTypeCategoryNameCompletion, CommandCompletions::TypeCategoryNames}, + {lldb::eThreadIDCompletion, CommandCompletions::ThreadIDs}, {lldb::CompletionType::eNoCompletion, nullptr} // This one has to be last in the list. }; @@ -807,6 +808,23 @@ void CommandCompletions::TypeCategoryNames(CommandInterpreter &interpreter, }); } +void CommandCompletions::ThreadIDs(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher) { + const ExecutionContext &exe_ctx = interpreter.GetExecutionContext(); + if (!exe_ctx.HasProcessScope()) + return; + + ThreadList &threads = exe_ctx.GetProcessPtr()->GetThreadList(); + lldb::ThreadSP thread_sp; + for (uint32_t idx = 0; (thread_sp = threads.GetThreadAtIndex(idx)); ++idx) { + StreamString strm; + thread_sp->GetStatus(strm, 0, 1, 1, true); + request.TryCompleteCurrentArg(std::to_string(thread_sp->GetID()), + strm.GetString()); + } +} + void CommandCompletions::CompleteModifiableCmdPathArgs( CommandInterpreter &interpreter, CompletionRequest &request, OptionElementVector &opt_element_vector) { diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index 35618551c2f05e3..dd9fab4bbddabc7 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -1172,7 +1172,8 @@ class CommandObjectThreadSelect : public CommandObjectParsed { CommandObjectThreadSelect(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "thread select", - "Change the currently selected thread.", nullptr, + "Change the currently selected thread.", + "thread select <thread-index> (or -t <thread-id>)", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { @@ -1241,7 +1242,7 @@ class CommandObjectThreadSelect : public CommandObjectParsed { } new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get(); if (new_thread == nullptr) { - result.AppendErrorWithFormat("Invalid thread #%s.\n", + result.AppendErrorWithFormat("Invalid thread index #%s.\n", command.GetArgumentAtIndex(0)); return; } @@ -1249,7 +1250,7 @@ class CommandObjectThreadSelect : public CommandObjectParsed { new_thread = process->GetThreadList().FindThreadByID(m_options.m_thread_id).get(); if (new_thread == nullptr) { - result.AppendErrorWithFormat("Invalid thread ID %lu.\n", + result.AppendErrorWithFormat("Invalid thread ID %" PRIu64 ".\n", m_options.m_thread_id); return; } diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index 558f2fbf962842a..ed3167727bcd326 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -1119,7 +1119,8 @@ let Command = "thread plan list" in { let Command = "thread select" in { def thread_select_thread_id : Option<"thread-id", "t">, Group<2>, - Arg<"ThreadID">, Desc<"Provide a thread ID instead of a thread index.">; + Arg<"ThreadID">, Completion<"ThreadID">, + Desc<"Provide a thread ID instead of a thread index.">; } let Command = "thread trace dump function calls" in { diff --git a/lldb/test/API/commands/thread/select/TestThreadSelect.py b/lldb/test/API/commands/thread/select/TestThreadSelect.py index 2345dc2b2b36a90..2a2cef4a905c926 100644 --- a/lldb/test/API/commands/thread/select/TestThreadSelect.py +++ b/lldb/test/API/commands/thread/select/TestThreadSelect.py @@ -36,7 +36,7 @@ def test_invalid_arg(self): self.expect( "thread select 0xffffffff", error=True, - startstr="error: Invalid thread #0xffffffff.", + startstr="error: Invalid thread index #0xffffffff.", ) self.expect( "thread select -t 0xffffffff", _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits