diff options
author | Walter Erquinigo <a20012251@gmail.com> | 2020-10-26 21:22:06 -0700 |
---|---|---|
committer | Walter Erquinigo <a20012251@gmail.com> | 2020-11-18 18:24:36 -0800 |
commit | fb19f11ef47bc479d42c11450817c5e855a9830b (patch) | |
tree | 54a34a6be75b39928683e89ad76ee4d9e8395968 /lldb/source/Commands/CommandObjectThread.cpp | |
parent | 25f5406f087579d43ca9a82dee7f3e76f0691bad (diff) | |
download | llvm-fb19f11ef47bc479d42c11450817c5e855a9830b.zip llvm-fb19f11ef47bc479d42c11450817c5e855a9830b.tar.gz llvm-fb19f11ef47bc479d42c11450817c5e855a9830b.tar.bz2 |
[trace][intel-pt] Scaffold the 'thread trace start | stop' commands
Depends on D90490.
The stop command is simple and invokes the new method Trace::StopTracingThread(thread).
On the other hand, the start command works by delegating its implementation to a CommandObject provided by the Trace plugin. This is necessary because each trace plugin needs different options for this command. There's even the chance that a Trace plugin can't support live tracing, but instead supports offline decoding and analysis, which means that "thread trace dump instructions" works but "thread trace start" doest. Because of this and a few other reasons, it's better to have each plugin provide this implementation.
Besides, I'm using the GetSupportedTraceType method introduced in D90490 to quickly infer what's the trace plug-in that works for the current process.
As an implementation note, I moved CommandObjectIterateOverThreads to its header so that I can use it from the IntelPT plugin. Besides, the actual start and stop logic for intel-pt is not part of this diff.
Reviewed By: clayborg
Differential Revision: https://reviews.llvm.org/D90729
Diffstat (limited to 'lldb/source/Commands/CommandObjectThread.cpp')
-rw-r--r-- | lldb/source/Commands/CommandObjectThread.cpp | 305 |
1 files changed, 103 insertions, 202 deletions
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index 5aa77de..f4ce5cc 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -10,6 +10,8 @@ #include <sstream> +#include "CommandObjectThreadUtil.h" +#include "lldb/Core/PluginManager.h" #include "lldb/Core/ValueObject.h" #include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandInterpreter.h" @@ -34,202 +36,6 @@ using namespace lldb; using namespace lldb_private; -// CommandObjectIterateOverThreads - -class CommandObjectIterateOverThreads : public CommandObjectParsed { - - class UniqueStack { - - public: - UniqueStack(std::stack<lldb::addr_t> stack_frames, uint32_t thread_index_id) - : m_stack_frames(stack_frames) { - m_thread_index_ids.push_back(thread_index_id); - } - - void AddThread(uint32_t thread_index_id) const { - m_thread_index_ids.push_back(thread_index_id); - } - - const std::vector<uint32_t> &GetUniqueThreadIndexIDs() const { - return m_thread_index_ids; - } - - lldb::tid_t GetRepresentativeThread() const { - return m_thread_index_ids.front(); - } - - friend bool inline operator<(const UniqueStack &lhs, - const UniqueStack &rhs) { - return lhs.m_stack_frames < rhs.m_stack_frames; - } - - protected: - // Mark the thread index as mutable, as we don't care about it from a const - // perspective, we only care about m_stack_frames so we keep our std::set - // sorted. - mutable std::vector<uint32_t> m_thread_index_ids; - std::stack<lldb::addr_t> m_stack_frames; - }; - -public: - CommandObjectIterateOverThreads(CommandInterpreter &interpreter, - const char *name, const char *help, - const char *syntax, uint32_t flags) - : CommandObjectParsed(interpreter, name, help, syntax, flags) {} - - ~CommandObjectIterateOverThreads() override = default; - - bool DoExecute(Args &command, CommandReturnObject &result) override { - result.SetStatus(m_success_return); - - bool all_threads = false; - if (command.GetArgumentCount() == 0) { - Thread *thread = m_exe_ctx.GetThreadPtr(); - if (!thread || !HandleOneThread(thread->GetID(), result)) - return false; - return result.Succeeded(); - } else if (command.GetArgumentCount() == 1) { - all_threads = ::strcmp(command.GetArgumentAtIndex(0), "all") == 0; - m_unique_stacks = ::strcmp(command.GetArgumentAtIndex(0), "unique") == 0; - } - - // Use tids instead of ThreadSPs to prevent deadlocking problems which - // result from JIT-ing code while iterating over the (locked) ThreadSP - // list. - std::vector<lldb::tid_t> tids; - - if (all_threads || m_unique_stacks) { - Process *process = m_exe_ctx.GetProcessPtr(); - - for (ThreadSP thread_sp : process->Threads()) - tids.push_back(thread_sp->GetID()); - } else { - const size_t num_args = command.GetArgumentCount(); - Process *process = m_exe_ctx.GetProcessPtr(); - - std::lock_guard<std::recursive_mutex> guard( - process->GetThreadList().GetMutex()); - - for (size_t i = 0; i < num_args; i++) { - uint32_t thread_idx; - if (!llvm::to_integer(command.GetArgumentAtIndex(i), thread_idx)) { - result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n", - command.GetArgumentAtIndex(i)); - result.SetStatus(eReturnStatusFailed); - return false; - } - - ThreadSP thread = - process->GetThreadList().FindThreadByIndexID(thread_idx); - - if (!thread) { - result.AppendErrorWithFormat("no thread with index: \"%s\"\n", - command.GetArgumentAtIndex(i)); - result.SetStatus(eReturnStatusFailed); - return false; - } - - tids.push_back(thread->GetID()); - } - } - - if (m_unique_stacks) { - // Iterate over threads, finding unique stack buckets. - std::set<UniqueStack> unique_stacks; - for (const lldb::tid_t &tid : tids) { - if (!BucketThread(tid, unique_stacks, result)) { - return false; - } - } - - // Write the thread id's and unique call stacks to the output stream - Stream &strm = result.GetOutputStream(); - Process *process = m_exe_ctx.GetProcessPtr(); - for (const UniqueStack &stack : unique_stacks) { - // List the common thread ID's - const std::vector<uint32_t> &thread_index_ids = - stack.GetUniqueThreadIndexIDs(); - strm.Format("{0} thread(s) ", thread_index_ids.size()); - for (const uint32_t &thread_index_id : thread_index_ids) { - strm.Format("#{0} ", thread_index_id); - } - strm.EOL(); - - // List the shared call stack for this set of threads - uint32_t representative_thread_id = stack.GetRepresentativeThread(); - ThreadSP thread = process->GetThreadList().FindThreadByIndexID( - representative_thread_id); - if (!HandleOneThread(thread->GetID(), result)) { - return false; - } - } - } else { - uint32_t idx = 0; - for (const lldb::tid_t &tid : tids) { - if (idx != 0 && m_add_return) - result.AppendMessage(""); - - if (!HandleOneThread(tid, result)) - return false; - - ++idx; - } - } - return result.Succeeded(); - } - -protected: - // Override this to do whatever you need to do for one thread. - // - // If you return false, the iteration will stop, otherwise it will proceed. - // The result is set to m_success_return (defaults to - // eReturnStatusSuccessFinishResult) before the iteration, so you only need - // to set the return status in HandleOneThread if you want to indicate an - // error. If m_add_return is true, a blank line will be inserted between each - // of the listings (except the last one.) - - virtual bool HandleOneThread(lldb::tid_t, CommandReturnObject &result) = 0; - - bool BucketThread(lldb::tid_t tid, std::set<UniqueStack> &unique_stacks, - CommandReturnObject &result) { - // Grab the corresponding thread for the given thread id. - Process *process = m_exe_ctx.GetProcessPtr(); - Thread *thread = process->GetThreadList().FindThreadByID(tid).get(); - if (thread == nullptr) { - result.AppendErrorWithFormatv("Failed to process thread #{0}.\n", tid); - result.SetStatus(eReturnStatusFailed); - return false; - } - - // Collect the each frame's address for this call-stack - std::stack<lldb::addr_t> stack_frames; - const uint32_t frame_count = thread->GetStackFrameCount(); - for (uint32_t frame_index = 0; frame_index < frame_count; frame_index++) { - const lldb::StackFrameSP frame_sp = - thread->GetStackFrameAtIndex(frame_index); - const lldb::addr_t pc = frame_sp->GetStackID().GetPC(); - stack_frames.push(pc); - } - - uint32_t thread_index_id = thread->GetIndexID(); - UniqueStack new_unique_stack(stack_frames, thread_index_id); - - // Try to match the threads stack to and existing entry. - std::set<UniqueStack>::iterator matching_stack = - unique_stacks.find(new_unique_stack); - if (matching_stack != unique_stacks.end()) { - matching_stack->AddThread(thread_index_id); - } else { - unique_stacks.insert(new_unique_stack); - } - return true; - } - - ReturnStatus m_success_return = eReturnStatusSuccessFinishResult; - bool m_unique_stacks = false; - bool m_add_return = true; -}; - // CommandObjectThreadBacktrace #define LLDB_OPTIONS_thread_backtrace #include "CommandOptions.inc" @@ -2170,6 +1976,101 @@ public: // Next are the subcommands of CommandObjectMultiwordTrace +// CommandObjectTraceStart + +/// This class works by delegating the logic to the actual trace plug-in that +/// can support the current process. +class CommandObjectTraceStart : public CommandObjectProxy { +public: + CommandObjectTraceStart(CommandInterpreter &interpreter) + : CommandObjectProxy(interpreter, "thread trace start", + "Start tracing threads with the corresponding trace " + "plug-in for the current process.", + "thread trace start [<trace-options>]") {} + +protected: + llvm::Expected<CommandObjectSP> DoGetProxyCommandObject() { + ProcessSP process_sp = m_interpreter.GetExecutionContext().GetProcessSP(); + + if (!process_sp) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Process not available."); + if (!process_sp->IsAlive()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Process must be launched."); + + llvm::Expected<TraceTypeInfo> trace_type = + process_sp->GetSupportedTraceType(); + + if (!trace_type) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), "Tracing is not supported. %s", + llvm::toString(trace_type.takeError()).c_str()); + + CommandObjectSP delegate_sp = + PluginManager::GetTraceStartCommand(trace_type->name, m_interpreter); + if (!delegate_sp) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "No trace plug-in matches the specified type: \"%s\"", + trace_type->name.c_str()); + return delegate_sp; + } + + CommandObject *GetProxyCommandObject() override { + if (llvm::Expected<CommandObjectSP> delegate = DoGetProxyCommandObject()) { + m_delegate_sp = *delegate; + m_delegate_error.clear(); + return m_delegate_sp.get(); + } else { + m_delegate_sp.reset(); + m_delegate_error = llvm::toString(delegate.takeError()); + return nullptr; + } + } + +private: + llvm::StringRef GetUnsupportedError() override { return m_delegate_error; } + + CommandObjectSP m_delegate_sp; + std::string m_delegate_error; +}; + +// CommandObjectTraceStop + +class CommandObjectTraceStop : public CommandObjectIterateOverThreads { +public: + CommandObjectTraceStop(CommandInterpreter &interpreter) + : CommandObjectIterateOverThreads( + interpreter, "thread trace stop", + "Stop tracing threads. " + "Defaults to the current thread. Thread indices can be " + "specified as arguments.\n Use the thread-index \"all\" to trace " + "all threads.", + "thread trace stop [<thread-index> <thread-index> ...]", + eCommandRequiresProcess | eCommandTryTargetAPILock | + eCommandProcessMustBeLaunched | eCommandProcessMustBePaused | + eCommandProcessMustBeTraced) {} + + ~CommandObjectTraceStop() override = default; + + bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override { + const Thread &thread = + *m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid); + Trace &trace = *m_exe_ctx.GetTargetSP()->GetTrace(); + + if (llvm::Error err = trace.StopTracingThread(thread)) { + result.AppendErrorWithFormat("Failed stopping thread %" PRIu64 ": %s\n", + tid, toString(std::move(err)).c_str()); + result.SetStatus(eReturnStatusFailed); + } + + // We don't return false on errors to try to stop as many threads as + // possible. + return true; + } +}; + // CommandObjectTraceDumpInstructions #define LLDB_OPTIONS_thread_trace_dump_instructions #include "CommandOptions.inc" @@ -2247,7 +2148,8 @@ public: "thread-index \"all\" to see all threads.", nullptr, eCommandRequiresProcess | eCommandTryTargetAPILock | - eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), + eCommandProcessMustBeLaunched | eCommandProcessMustBePaused | + eCommandProcessMustBeTraced), m_options(), m_create_repeat_command_just_invoked(false) {} ~CommandObjectTraceDumpInstructions() override = default; @@ -2278,11 +2180,6 @@ protected: bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override { const TraceSP &trace_sp = m_exe_ctx.GetTargetSP()->GetTrace(); - if (!trace_sp) { - result.SetError("error: this thread is not being traced"); - return false; - } - ThreadSP thread_sp = m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid); @@ -2333,6 +2230,10 @@ public: "thread trace <subcommand> [<subcommand objects>]") { LoadSubCommand("dump", CommandObjectSP(new CommandObjectMultiwordTraceDump( interpreter))); + LoadSubCommand("start", + CommandObjectSP(new CommandObjectTraceStart(interpreter))); + LoadSubCommand("stop", + CommandObjectSP(new CommandObjectTraceStop(interpreter))); } ~CommandObjectMultiwordTrace() override = default; |