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