diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp')
-rw-r--r-- | contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp | 277 |
1 files changed, 197 insertions, 80 deletions
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp index f6042937a4ff..993523e06736 100644 --- a/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp +++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp @@ -33,7 +33,7 @@ #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanStepInRange.h" #include "lldb/Target/Trace.h" -#include "lldb/Target/TraceInstructionDumper.h" +#include "lldb/Target/TraceDumper.h" #include "lldb/Utility/State.h" using namespace lldb; @@ -125,6 +125,58 @@ public: Options *GetOptions() override { return &m_options; } + llvm::Optional<std::string> GetRepeatCommand(Args ¤t_args, + uint32_t idx) override { + llvm::StringRef count_opt("--count"); + llvm::StringRef start_opt("--start"); + + // If no "count" was provided, we are dumping the entire backtrace, so + // there isn't a repeat command. So we search for the count option in + // the args, and if we find it, we make a copy and insert or modify the + // start option's value to start count indices greater. + + Args copy_args(current_args); + size_t num_entries = copy_args.GetArgumentCount(); + // These two point at the index of the option value if found. + size_t count_idx = 0; + size_t start_idx = 0; + size_t count_val = 0; + size_t start_val = 0; + + for (size_t idx = 0; idx < num_entries; idx++) { + llvm::StringRef arg_string = copy_args[idx].ref(); + if (arg_string.equals("-c") || count_opt.startswith(arg_string)) { + idx++; + if (idx == num_entries) + return llvm::None; + count_idx = idx; + if (copy_args[idx].ref().getAsInteger(0, count_val)) + return llvm::None; + } else if (arg_string.equals("-s") || start_opt.startswith(arg_string)) { + idx++; + if (idx == num_entries) + return llvm::None; + start_idx = idx; + if (copy_args[idx].ref().getAsInteger(0, start_val)) + return llvm::None; + } + } + if (count_idx == 0) + return llvm::None; + + std::string new_start_val = llvm::formatv("{0}", start_val + count_val); + if (start_idx == 0) { + copy_args.AppendArgument(start_opt); + copy_args.AppendArgument(new_start_val); + } else { + copy_args.ReplaceArgumentAtIndex(start_idx, new_start_val); + } + std::string repeat_command; + if (!copy_args.GetQuotedCommandString(repeat_command)) + return llvm::None; + return repeat_command; + } + protected: void DoExtendedBacktrace(Thread *thread, CommandReturnObject &result) { SystemRuntime *runtime = thread->GetProcess()->GetSystemRuntime(); @@ -932,8 +984,8 @@ protected: thread->GetStackFrameAtIndex(m_options.m_frame_idx).get(); if (frame == nullptr) { result.AppendErrorWithFormat( - "Frame index %u is out of range for thread %u.\n", - m_options.m_frame_idx, m_options.m_thread_idx); + "Frame index %u is out of range for thread id %" PRIu64 ".\n", + m_options.m_frame_idx, thread->GetID()); return false; } @@ -950,9 +1002,8 @@ protected: if (line_table == nullptr) { result.AppendErrorWithFormat("Failed to resolve the line table for " - "frame %u of thread index %u.\n", - m_options.m_frame_idx, - m_options.m_thread_idx); + "frame %u of thread id %" PRIu64 ".\n", + m_options.m_frame_idx, thread->GetID()); return false; } @@ -960,7 +1011,14 @@ protected: uint32_t index_ptr = 0, end_ptr; std::vector<addr_t> address_list; - // Find the beginning & end index of the + // Find the beginning & end index of the function, but first make + // sure it is valid: + if (!sc.function) { + result.AppendErrorWithFormat("Have debug information but no " + "function info - can't get until range."); + return false; + } + AddressRange fun_addr_range = sc.function->GetAddressRange(); Address fun_start_addr = fun_addr_range.GetBaseAddress(); line_table->FindLineEntryByAddress(fun_start_addr, function_start, @@ -1031,13 +1089,18 @@ protected: return false; } } else { - result.AppendErrorWithFormat( - "Frame index %u of thread %u has no debug information.\n", - m_options.m_frame_idx, m_options.m_thread_idx); + result.AppendErrorWithFormat("Frame index %u of thread id %" PRIu64 + " has no debug information.\n", + m_options.m_frame_idx, thread->GetID()); return false; } - process->GetThreadList().SetSelectedThreadByID(m_options.m_thread_idx); + if (!process->GetThreadList().SetSelectedThreadByID(thread->GetID())) { + result.AppendErrorWithFormat( + "Failed to set the selected thread to thread id %" PRIu64 ".\n", + thread->GetID()); + return false; + } StreamString stream; Status error; @@ -1973,7 +2036,7 @@ public: unsigned i = 0; for (llvm::StringRef plugin_name = - PluginManager::GetTraceExporterPluginNameAtIndex(i++); + PluginManager::GetTraceExporterPluginNameAtIndex(i); !plugin_name.empty(); plugin_name = PluginManager::GetTraceExporterPluginNameAtIndex(i++)) { if (ThreadTraceExportCommandCreator command_creator = @@ -2040,8 +2103,7 @@ public: #define LLDB_OPTIONS_thread_trace_dump_instructions #include "CommandOptions.inc" -class CommandObjectTraceDumpInstructions - : public CommandObjectIterateOverThreads { +class CommandObjectTraceDumpInstructions : public CommandObjectParsed { public: class CommandOptions : public Options { public: @@ -2066,6 +2128,10 @@ public: m_count = count; break; } + case 'a': { + m_count = std::numeric_limits<decltype(m_count)>::max(); + break; + } case 's': { int32_t skip; if (option_arg.empty() || option_arg.getAsInteger(0, skip) || skip < 0) @@ -2073,19 +2139,50 @@ public: "invalid integer value for option '%s'", option_arg.str().c_str()); else - m_skip = skip; + m_dumper_options.skip = skip; + break; + } + case 'i': { + uint64_t id; + if (option_arg.empty() || option_arg.getAsInteger(0, id)) + error.SetErrorStringWithFormat( + "invalid integer value for option '%s'", + option_arg.str().c_str()); + else + m_dumper_options.id = id; + break; + } + case 'F': { + m_output_file.emplace(option_arg); break; } case 'r': { - m_raw = true; + m_dumper_options.raw = true; break; } case 'f': { - m_forwards = true; + m_dumper_options.forwards = true; break; } case 't': { - m_show_tsc = true; + m_dumper_options.show_tsc = true; + break; + } + case 'e': { + m_dumper_options.show_events = true; + break; + } + case 'j': { + m_dumper_options.json = true; + break; + } + case 'J': { + m_dumper_options.pretty_print_json = true; + m_dumper_options.json = true; + break; + } + case 'C': { + m_continue = true; break; } default: @@ -2096,10 +2193,9 @@ public: void OptionParsingStarting(ExecutionContext *execution_context) override { m_count = kDefaultCount; - m_skip = 0; - m_raw = false; - m_forwards = false; - m_show_tsc = false; + m_continue = false; + m_output_file = llvm::None; + m_dumper_options = {}; } llvm::ArrayRef<OptionDefinition> GetDefinitions() override { @@ -2110,92 +2206,113 @@ public: // Instance variables to hold the values for command options. size_t m_count; - size_t m_skip; - bool m_raw; - bool m_forwards; - bool m_show_tsc; + size_t m_continue; + llvm::Optional<FileSpec> m_output_file; + TraceDumperOptions m_dumper_options; }; CommandObjectTraceDumpInstructions(CommandInterpreter &interpreter) - : CommandObjectIterateOverThreads( + : CommandObjectParsed( interpreter, "thread trace dump instructions", - "Dump the traced instructions for one or more threads. If no " - "threads are specified, show the current thread. Use the " - "thread-index \"all\" to see all threads.", + "Dump the traced instructions for one thread. If no " + "thread is specified, show the current thread.", nullptr, - eCommandRequiresProcess | eCommandTryTargetAPILock | - eCommandProcessMustBeLaunched | eCommandProcessMustBePaused | - eCommandProcessMustBeTraced), - m_create_repeat_command_just_invoked(false) {} + eCommandRequiresProcess | eCommandRequiresThread | + eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | + eCommandProcessMustBePaused | eCommandProcessMustBeTraced) { + CommandArgumentData thread_arg{eArgTypeThreadIndex, eArgRepeatOptional}; + m_arguments.push_back({thread_arg}); + } ~CommandObjectTraceDumpInstructions() override = default; Options *GetOptions() override { return &m_options; } - const char *GetRepeatCommand(Args ¤t_command_args, - uint32_t index) override { - current_command_args.GetCommandString(m_repeat_command); - m_create_repeat_command_just_invoked = true; - return m_repeat_command.c_str(); + llvm::Optional<std::string> GetRepeatCommand(Args ¤t_command_args, + uint32_t index) override { + std::string cmd; + current_command_args.GetCommandString(cmd); + if (cmd.find(" --continue") == std::string::npos) + cmd += " --continue"; + return cmd; } protected: - bool DoExecute(Args &args, CommandReturnObject &result) override { - if (!IsRepeatCommand()) - m_dumpers.clear(); - - bool status = CommandObjectIterateOverThreads::DoExecute(args, result); + ThreadSP GetThread(Args &args, CommandReturnObject &result) { + if (args.GetArgumentCount() == 0) + return m_exe_ctx.GetThreadSP(); - m_create_repeat_command_just_invoked = false; - return status; - } + const char *arg = args.GetArgumentAtIndex(0); + uint32_t thread_idx; - bool IsRepeatCommand() { - return !m_repeat_command.empty() && !m_create_repeat_command_just_invoked; + if (!llvm::to_integer(arg, thread_idx)) { + result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n", + arg); + return nullptr; + } + ThreadSP thread_sp = + m_exe_ctx.GetProcessRef().GetThreadList().FindThreadByIndexID( + thread_idx); + if (!thread_sp) + result.AppendErrorWithFormat("no thread with index: \"%s\"\n", arg); + return thread_sp; } - bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override { - Stream &s = result.GetOutputStream(); + bool DoExecute(Args &args, CommandReturnObject &result) override { + ThreadSP thread_sp = GetThread(args, result); + if (!thread_sp) { + result.AppendError("invalid thread\n"); + return false; + } - const TraceSP &trace_sp = m_exe_ctx.GetTargetSP()->GetTrace(); - ThreadSP thread_sp = - m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid); + if (m_options.m_continue && m_last_id) { + // We set up the options to continue one instruction past where + // the previous iteration stopped. + m_options.m_dumper_options.skip = 1; + m_options.m_dumper_options.id = m_last_id; + } - if (!m_dumpers.count(thread_sp->GetID())) { - lldb::TraceCursorUP cursor_up = trace_sp->GetCursor(*thread_sp); - // Set up the cursor and return the presentation index of the first - // instruction to dump after skipping instructions. - auto setUpCursor = [&]() { - cursor_up->SetForwards(m_options.m_forwards); - if (m_options.m_forwards) - return cursor_up->Seek(m_options.m_skip, TraceCursor::SeekType::Set); - return -cursor_up->Seek(-m_options.m_skip, TraceCursor::SeekType::End); - }; + llvm::Expected<TraceCursorUP> cursor_or_error = + m_exe_ctx.GetTargetSP()->GetTrace()->CreateNewCursor(*thread_sp); - int initial_index = setUpCursor(); + if (!cursor_or_error) { + result.AppendError(llvm::toString(cursor_or_error.takeError())); + return false; + } + TraceCursorUP &cursor_up = *cursor_or_error; - auto dumper = std::make_unique<TraceInstructionDumper>( - std::move(cursor_up), initial_index, m_options.m_raw, - m_options.m_show_tsc); + if (m_options.m_dumper_options.id && + !cursor_up->HasId(*m_options.m_dumper_options.id)) { + result.AppendError("invalid instruction id\n"); + return false; + } - // This happens when the seek value was more than the number of available - // instructions. - if (std::abs(initial_index) < (int)m_options.m_skip) - dumper->SetNoMoreData(); + llvm::Optional<StreamFile> out_file; + if (m_options.m_output_file) { + out_file.emplace(m_options.m_output_file->GetPath().c_str(), + File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate, + lldb::eFilePermissionsFileDefault); + } - m_dumpers[thread_sp->GetID()] = std::move(dumper); + if (m_options.m_continue && !m_last_id) { + // We need to stop processing data when we already ran out of instructions + // in a previous command. We can fake this by setting the cursor past the + // end of the trace. + cursor_up->Seek(1, TraceCursor::SeekType::End); } - m_dumpers[thread_sp->GetID()]->DumpInstructions(s, m_options.m_count); + TraceDumper dumper(std::move(cursor_up), + out_file ? *out_file : result.GetOutputStream(), + m_options.m_dumper_options); + + m_last_id = dumper.DumpInstructions(m_options.m_count); return true; } CommandOptions m_options; - - // Repeat command helpers - std::string m_repeat_command; - bool m_create_repeat_command_just_invoked; - std::map<lldb::tid_t, std::unique_ptr<TraceInstructionDumper>> m_dumpers; + // Last traversed id used to continue a repeat command. None means + // that all the trace has been consumed. + llvm::Optional<lldb::user_id_t> m_last_id; }; // CommandObjectTraceDumpInfo @@ -2249,7 +2366,7 @@ public: : CommandObjectIterateOverThreads( interpreter, "thread trace dump info", "Dump the traced information for one or more threads. If no " - "threads are specified, show the current thread. Use the " + "threads are specified, show the current thread. Use the " "thread-index \"all\" to see all threads.", nullptr, eCommandRequiresProcess | eCommandTryTargetAPILock | |