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,7 +42,7 @@
using namespace lldb_private;
//-------------------------------------------------------------------------
-// CommandObjectThreadBacktrace
+// CommandObjectIterateOverThreads
//-------------------------------------------------------------------------
class CommandObjectIterateOverThreads : public CommandObjectParsed {
@@ -291,6 +291,122 @@
CommandOptions m_options;
};
+//-------------------------------------------------------------------------
+// CommandObjectUniqueThreadStacks
+//-------------------------------------------------------------------------
+
+class CommandObjectUniqueThreadStacks : public CommandObjectParsed {
+
+ class UniqueStack {
+
+ public:
+ UniqueStack(std::stack
stackFrames, Thread* thread)
+ : m_stackFrames(stackFrames) {
+ m_threads.push_back(thread);
+ }
+
+ void AddThread(Thread* thread) {
+ m_threads.push_back(thread);
+ }
+
+ const std::vector& GetUniqueThreads() const {
+ return m_threads;
+ }
+
+ Thread* GetRepresentativeThread() const {
+ return m_threads.front();
+ }
+
+ bool IsEqual(std::stack stackFrames) const {
+ return stackFrames == m_stackFrames;
+ }
+
+ protected:
+ std::vector m_threads;
+ std::stack m_stackFrames;
+ };
+
+public:
+ CommandObjectUniqueThreadStacks(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "thread unique-stacks",
+ "Show unique call stacks for all threads in the process.",
+ nullptr,
+ eCommandRequiresThread | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused)
+ {
+ }
+
+ ~CommandObjectUniqueThreadStacks() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+
+ std::vector uniqueStacks;
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ // Iterate over threads, finding unique stack buckets.
+ for (ThreadSP thread_sp : process->Threads()) {
+ Thread *thread = thread_sp.get();
+ BucketThread(thread, uniqueStacks);
+ }
+
+ const uint32_t start_frame = 0;
+ const uint32_t num_frames = UINT32_MAX;
+ const uint32_t num_frames_with_source = 0;// UINT32_MAX;
+ const bool stop_format = true;
+ const bool just_stack = true;
+
+ // Output all of the unique stack frames.
+ Stream &strm = result.GetOutputStream();
+ strm.IndentMore();
+ for (const UniqueStack& stack: uniqueStacks) {
+ // List the common thread ID's
+ strm.Indent("thread(s) ");
+ for (Thread* thread : stack.GetUniqueThreads()) {
+ strm.Printf("#%lu ", thread->GetID());
+ }
+ strm.EOL();
+
+ // List the shared call stack for this set of threads
+ stack.GetRepresentativeThread()->GetStatus(strm, start_frame,
+ num_frames, num_frames_with_source,
+ stop_format, just_stack);
+ }
+ strm.IndentLess();
+
+ return result.Succeeded();
+ }
+
+protected:
+
+ void BucketThread(Thread* thread, std::vector& uniqueStacks) {
+ // Grab each frame's address
+ std::stack stackFrames;
+ const uint32_t frameCount = thread->GetStackFrameCount();
+ for (uint32_t frameIndex = 0; frameIndex < frameCount; frameIndex++)
+ {
+ const Address stackFrameAddress = thread->GetStackFrameAtIndex(frameIndex)->GetFrameCodeAddress();
+ stackFrames.push(stackFrameAddress);
+ }
+
+ // Try to match the threads stack to and existing thread.
+ bool foundMatch = false;
+ for (UniqueStack& uniqueStack : uniqueStacks)
+ {
+ if (uniqueStack.IsEqual(stackFrames)) {
+ foundMatch = true;
+ uniqueStack.AddThread(thread);
+ }
+ }
+
+ // We failed to find an existing unique stack, so create a new one.
+ if (!foundMatch) {
+ UniqueStack newUniqueStack(stackFrames, thread);
+ uniqueStacks.push_back(newUniqueStack);
+ }
+ }
+};
+
enum StepScope { eStepScopeSource, eStepScopeInstruction };
static OptionEnumValueElement g_tri_running_mode[] = {
@@ -1925,6 +2041,8 @@
"thread []") {
LoadSubCommand("backtrace", CommandObjectSP(new CommandObjectThreadBacktrace(
interpreter)));
+ LoadSubCommand("unique-stacks",
+ CommandObjectSP(new CommandObjectUniqueThreadStacks(interpreter)));
LoadSubCommand("continue",
CommandObjectSP(new CommandObjectThreadContinue(interpreter)));
LoadSubCommand("list",
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