aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Commands/CommandObjectThread.cpp
diff options
context:
space:
mode:
authorWalter Erquinigo <a20012251@gmail.com>2020-10-26 21:22:06 -0700
committerWalter Erquinigo <a20012251@gmail.com>2020-11-18 18:24:36 -0800
commitfb19f11ef47bc479d42c11450817c5e855a9830b (patch)
tree54a34a6be75b39928683e89ad76ee4d9e8395968 /lldb/source/Commands/CommandObjectThread.cpp
parent25f5406f087579d43ca9a82dee7f3e76f0691bad (diff)
downloadllvm-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.cpp305
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;