aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Commands/CommandObjectThreadUtil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Commands/CommandObjectThreadUtil.cpp')
-rw-r--r--lldb/source/Commands/CommandObjectThreadUtil.cpp158
1 files changed, 158 insertions, 0 deletions
diff --git a/lldb/source/Commands/CommandObjectThreadUtil.cpp b/lldb/source/Commands/CommandObjectThreadUtil.cpp
new file mode 100644
index 0000000..b93698c
--- /dev/null
+++ b/lldb/source/Commands/CommandObjectThreadUtil.cpp
@@ -0,0 +1,158 @@
+//===-- CommandObjectThreadUtil.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectThreadUtil.h"
+
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+CommandObjectIterateOverThreads::CommandObjectIterateOverThreads(
+ CommandInterpreter &interpreter, const char *name, const char *help,
+ const char *syntax, uint32_t flags)
+ : CommandObjectParsed(interpreter, name, help, syntax, flags) {}
+
+bool CommandObjectIterateOverThreads::DoExecute(Args &command,
+ CommandReturnObject &result) {
+ 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();
+}
+
+bool CommandObjectIterateOverThreads::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;
+}