Index: include/lldb/Target/Thread.h
===================================================================
--- include/lldb/Target/Thread.h
+++ include/lldb/Target/Thread.h
@@ -1164,7 +1164,7 @@
size_t GetStatus(Stream &strm, uint32_t start_frame, uint32_t num_frames,
uint32_t num_frames_with_source,
- bool stop_format);
+ bool stop_format, bool only_stacks = false);
size_t GetStackFrameStatus(Stream &strm, uint32_t first_frame,
uint32_t num_frames, bool show_frame_info,
Index: source/Commands/CommandObjectThread.cpp
===================================================================
--- source/Commands/CommandObjectThread.cpp
+++ source/Commands/CommandObjectThread.cpp
@@ -42,10 +42,40 @@
using namespace lldb_private;
//-------------------------------------------------------------------------
-// CommandObjectThreadBacktrace
+// CommandObjectIterateOverThreads
//-------------------------------------------------------------------------
class CommandObjectIterateOverThreads : public CommandObjectParsed {
+
+ class UniqueStack {
+
+ public:
+ UniqueStack(std::stack
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) {
+ m_thread_index_ids.push_back(thread_index_id);
+ }
+
+ const std::vector& GetUniqueThreadIndexIDs() const {
+ return m_thread_index_ids;
+ }
+
+ lldb::tid_t GetRepresentativeThread() const {
+ return m_thread_index_ids.front();
+ }
+
+ bool IsEqual(std::stack stack_frames) const {
+ return stack_frames == m_stack_frames;
+ }
+
+ protected:
+ std::vector m_thread_index_ids;
+ std::stack m_stack_frames;
+ };
+
public:
CommandObjectIterateOverThreads(CommandInterpreter &interpreter,
const char *name, const char *help,
@@ -57,11 +87,15 @@
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 (!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
@@ -69,8 +103,7 @@
// code while iterating over the (locked) ThreadSP list.
std::vector tids;
- if (command.GetArgumentCount() == 1 &&
- ::strcmp(command.GetArgumentAtIndex(0), "all") == 0) {
+ if (all_threads || m_unique_stacks) {
Process *process = m_exe_ctx.GetProcessPtr();
for (ThreadSP thread_sp : process->Threads())
@@ -108,20 +141,52 @@
}
}
- 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;
+ if (m_unique_stacks) {
+ // Iterate over threads, finding unique stack buckets.
+ std::vector unique_stacks;
+ for (const lldb::tid_t &tid : tids) {
+ if (!BucketThread(tid, unique_stacks, result)) {
+ return false;
+ }
+ }
- ++idx;
+ // Write the thread id's and unique call stacks to the output stream
+ Stream &strm = result.GetOutputStream();
+ Process *process = m_exe_ctx.GetProcessPtr();
+ strm.IndentMore();
+ for (const UniqueStack& stack: unique_stacks) {
+ // List the common thread ID's
+ strm.Indent("thread(s) ");
+ for (const uint32_t &thread_index_id : stack.GetUniqueThreadIndexIDs()) {
+ strm.Printf("#%u ", 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;
+ }
+ }
+ strm.IndentLess();
+ } 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.
@@ -134,7 +199,47 @@
virtual bool HandleOneThread(lldb::tid_t, CommandReturnObject &result) = 0;
+ bool BucketThread(lldb::tid_t tid,
+ std::vector& 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.AppendErrorWithFormat("Failed to process thread# %lu.\n", tid);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Collect the each frame's address for this call-stack
+ std::stack 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 Address frame_address = frame_sp->GetFrameCodeAddress();
+ stack_frames.push(frame_address);
+ }
+
+ // Try to match the threads stack to and existing entry.
+ bool found_match = false;
+ for (UniqueStack& unique_stack : unique_stacks) {
+ if (unique_stack.IsEqual(stack_frames)) {
+ found_match = true;
+ unique_stack.AddThread(thread->GetIndexID());
+ break;
+ }
+ }
+
+ // We failed to find an existing unique stack, so create a new one.
+ if (!found_match) {
+ UniqueStack new_unique_stack(stack_frames, thread->GetIndexID());
+ unique_stacks.push_back(new_unique_stack);
+ }
+ return true;
+ }
+
ReturnStatus m_success_return = eReturnStatusSuccessFinishResult;
+ bool m_unique_stacks = false;
bool m_add_return = true;
};
@@ -218,9 +323,9 @@
: CommandObjectIterateOverThreads(
interpreter, "thread backtrace",
"Show thread call stacks. Defaults to the current thread, thread "
- "indexes can be specified as arguments. Use the thread-index "
- "\"all\" "
- "to see all threads.",
+ "indexes can be specified as arguments.\n"
+ "Use the thread-index \"all\" to see all threads.\n"
+ "Use the thread-index \"unique\" to see threads with unique call stacks.",
nullptr,
eCommandRequiresProcess | eCommandRequiresThread |
eCommandTryTargetAPILock | eCommandProcessMustBeLaunched |
@@ -270,11 +375,14 @@
Stream &strm = result.GetOutputStream();
+ // Only dump stack info if we processing unique stacks.
+ const bool only_stacks = m_unique_stacks;
+
// Don't show source context when doing backtraces.
const uint32_t num_frames_with_source = 0;
const bool stop_format = true;
if (!thread->GetStatus(strm, m_options.m_start, m_options.m_count,
- num_frames_with_source, stop_format)) {
+ num_frames_with_source, stop_format, only_stacks)) {
result.AppendErrorWithFormat(
"error displaying backtrace for thread: \"0x%4.4x\"\n",
thread->GetIndexID());
Index: source/Core/Debugger.cpp
===================================================================
--- source/Core/Debugger.cpp
+++ source/Core/Debugger.cpp
@@ -236,7 +236,7 @@
"when displaying thread information."},
{"thread-stop-format", OptionValue::eTypeFormatEntity, true, 0,
DEFAULT_THREAD_STOP_FORMAT, nullptr, "The default thread format "
- "string to usewhen displaying thread "
+ "string to use when displaying thread "
"information as part of the stop display."},
{"use-external-editor", OptionValue::eTypeBoolean, true, false, nullptr,
nullptr, "Whether to use an external editor or not."},
Index: source/Target/Thread.cpp
===================================================================
--- source/Target/Thread.cpp
+++ source/Target/Thread.cpp
@@ -1912,39 +1912,42 @@
size_t Thread::GetStatus(Stream &strm, uint32_t start_frame,
uint32_t num_frames, uint32_t num_frames_with_source,
- bool stop_format) {
- ExecutionContext exe_ctx(shared_from_this());
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- size_t num_frames_shown = 0;
- strm.Indent();
- bool is_selected = false;
- if (process) {
- if (process->GetThreadList().GetSelectedThread().get() == this)
- is_selected = true;
- }
- strm.Printf("%c ", is_selected ? '*' : ' ');
- if (target && target->GetDebugger().GetUseExternalEditor()) {
- StackFrameSP frame_sp = GetStackFrameAtIndex(start_frame);
- if (frame_sp) {
- SymbolContext frame_sc(
- frame_sp->GetSymbolContext(eSymbolContextLineEntry));
- if (frame_sc.line_entry.line != 0 && frame_sc.line_entry.file) {
- Host::OpenFileInExternalEditor(frame_sc.line_entry.file,
- frame_sc.line_entry.line);
+ bool stop_format, bool only_stacks) {
+
+ if (!only_stacks) {
+ ExecutionContext exe_ctx(shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ strm.Indent();
+ bool is_selected = false;
+ if (process) {
+ if (process->GetThreadList().GetSelectedThread().get() == this)
+ is_selected = true;
+ }
+ strm.Printf("%c ", is_selected ? '*' : ' ');
+ if (target && target->GetDebugger().GetUseExternalEditor()) {
+ StackFrameSP frame_sp = GetStackFrameAtIndex(start_frame);
+ if (frame_sp) {
+ SymbolContext frame_sc(
+ frame_sp->GetSymbolContext(eSymbolContextLineEntry));
+ if (frame_sc.line_entry.line != 0 && frame_sc.line_entry.file) {
+ Host::OpenFileInExternalEditor(frame_sc.line_entry.file,
+ frame_sc.line_entry.line);
+ }
}
}
- }
- DumpUsingSettingsFormat(strm, start_frame, stop_format);
+ DumpUsingSettingsFormat(strm, start_frame, stop_format);
+ }
+ size_t num_frames_shown = 0;
if (num_frames > 0) {
strm.IndentMore();
const bool show_frame_info = true;
const char *selected_frame_marker = nullptr;
- if (num_frames == 1 ||
+ if (num_frames == 1 || only_stacks ||
(GetID() != GetProcess()->GetThreadList().GetSelectedThread()->GetID()))
strm.IndentMore();
else