bgianfo updated this revision to Diff 101664.
bgianfo added a comment.
Update test to user thread.StepOut() instead of thread.Resume().
https://reviews.llvm.org/D33426
Files:
include/lldb/Core/Debugger.h
include/lldb/Target/StackFrame.h
include/lldb/Target/StackFrameList.h
include/lldb/Target/Thread.h
packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py
packages/Python/lldbsuite/test/functionalities/thread/num_threads/main.cpp
source/Commands/CommandObjectThread.cpp
source/Core/Debugger.cpp
source/Target/StackFrame.cpp
source/Target/StackFrameList.cpp
source/Target/Thread.cpp
Index: source/Target/Thread.cpp
===================================================================
--- source/Target/Thread.cpp
+++ source/Target/Thread.cpp
@@ -1913,47 +1913,50 @@
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 bool show_frame_unique = only_stacks;
const char *selected_frame_marker = nullptr;
- if (num_frames == 1 ||
+ if (num_frames == 1 || only_stacks ||
(GetID() != GetProcess()->GetThreadList().GetSelectedThread()->GetID()))
strm.IndentMore();
else
selected_frame_marker = "* ";
num_frames_shown = GetStackFrameList()->GetStatus(
strm, start_frame, num_frames, show_frame_info, num_frames_with_source,
- selected_frame_marker);
+ show_frame_unique, selected_frame_marker);
if (num_frames == 1)
strm.IndentLess();
strm.IndentLess();
Index: source/Target/StackFrameList.cpp
===================================================================
--- source/Target/StackFrameList.cpp
+++ source/Target/StackFrameList.cpp
@@ -801,7 +801,7 @@
size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
uint32_t num_frames, bool show_frame_info,
- uint32_t num_frames_with_source,
+ uint32_t num_frames_with_source, bool show_unique,
const char *selected_frame_marker) {
size_t num_frames_displayed = 0;
@@ -842,7 +842,7 @@
if (!frame_sp->GetStatus(strm, show_frame_info,
num_frames_with_source > (first_frame - frame_idx),
- marker))
+ show_unique, marker))
break;
++num_frames_displayed;
}
Index: source/Target/StackFrame.cpp
===================================================================
--- source/Target/StackFrame.cpp
+++ source/Target/StackFrame.cpp
@@ -1744,6 +1744,7 @@
}
void StackFrame::DumpUsingSettingsFormat(Stream *strm,
+ bool show_unique,
const char *frame_marker) {
if (strm == nullptr)
return;
@@ -1757,8 +1758,13 @@
const FormatEntity::Entry *frame_format = nullptr;
Target *target = exe_ctx.GetTargetPtr();
- if (target)
- frame_format = target->GetDebugger().GetFrameFormat();
+ if (target) {
+ if (show_unique) {
+ frame_format = target->GetDebugger().GetFrameFormatUnique();
+ } else {
+ frame_format = target->GetDebugger().GetFrameFormat();
+ }
+ }
if (frame_format && FormatEntity::Format(*frame_format, s, &m_sc, &exe_ctx,
nullptr, nullptr, false, false)) {
strm->PutCString(s.GetString());
@@ -1840,11 +1846,10 @@
}
bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
- const char *frame_marker) {
-
+ bool show_unique, const char *frame_marker) {
if (show_frame_info) {
strm.Indent();
- DumpUsingSettingsFormat(&strm, frame_marker);
+ DumpUsingSettingsFormat(&strm, show_unique, frame_marker);
}
if (show_source) {
Index: source/Core/Debugger.cpp
===================================================================
--- source/Core/Debugger.cpp
+++ source/Core/Debugger.cpp
@@ -112,6 +112,12 @@
"{ " \
"${module.file.basename}{`${function.name-with-args}" \
"{${frame.no-debug}${function.pc-offset}}}}"
+
+#define MODULE_WITH_FUNC_NO_ARGS \
+ "{ " \
+ "${module.file.basename}{`${function.name-without-args}" \
+ "{${frame.no-debug}${function.pc-offset}}}}"
+
#define FILE_AND_LINE "{ at ${line.file.basename}:${line.number}}"
#define IS_OPTIMIZED "{${function.is-optimized} [opt]}"
@@ -141,6 +147,11 @@
"frame #${frame.index}: ${frame.pc}" MODULE_WITH_FUNC FILE_AND_LINE \
IS_OPTIMIZED "\\n"
+#define DEFAULT_FRAME_FORMAT_NO_ARGS \
+ "frame #${frame.index}: ${frame.pc}" MODULE_WITH_FUNC_NO_ARGS FILE_AND_LINE \
+ IS_OPTIMIZED "\\n"
+
+
// Three parts to this disassembly format specification:
// 1. If this is a new function/symbol (no previous symbol/function), print
// dylib`funcname:\n
@@ -236,7 +247,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."},
@@ -257,6 +268,10 @@
{"escape-non-printables", OptionValue::eTypeBoolean, true, true, nullptr,
nullptr, "If true, LLDB will automatically escape non-printable and "
"escape characters when formatting strings."},
+ {"frame-format-unique", OptionValue::eTypeFormatEntity, true, 0,
+ DEFAULT_FRAME_FORMAT_NO_ARGS, nullptr,
+ "The default frame format string to use when displaying stack frame"
+ "information for threads from thread backtrace unique."},
{nullptr, OptionValue::eTypeInvalid, true, 0, nullptr, nullptr, nullptr}};
enum {
@@ -282,7 +297,8 @@
ePropertyAutoIndent,
ePropertyPrintDecls,
ePropertyTabSize,
- ePropertyEscapeNonPrintables
+ ePropertyEscapeNonPrintables,
+ ePropertyFrameFormatUnique,
};
LoadPluginCallbackType Debugger::g_load_plugin_callback = nullptr;
@@ -358,6 +374,11 @@
return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
}
+const FormatEntity::Entry *Debugger::GetFrameFormatUnique() const {
+ const uint32_t idx = ePropertyFrameFormatUnique;
+ return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+}
+
bool Debugger::GetNotifyVoid() const {
const uint32_t idx = ePropertyNotiftVoid;
return m_collection_sp->GetPropertyAtIndexAsBoolean(
Index: source/Commands/CommandObjectThread.cpp
===================================================================
--- source/Commands/CommandObjectThread.cpp
+++ source/Commands/CommandObjectThread.cpp
@@ -42,10 +42,42 @@
using namespace lldb_private;
//-------------------------------------------------------------------------
-// CommandObjectThreadBacktrace
+// 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,
@@ -57,20 +89,23 @@
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
// result from JIT-ing
// code while iterating over the (locked) ThreadSP list.
std::vector<lldb::tid_t> 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 +143,51 @@
}
}
- 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::set<UniqueStack> 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();
+ for (const UniqueStack& stack: unique_stacks) {
+ // List the common thread ID's
+ const std::vector<uint32_t>& thread_index_ids = stack.GetUniqueThreadIndexIDs();
+ strm.Printf("%lu thread(s) ", thread_index_ids.size());
+ for (const uint32_t &thread_index_id : thread_index_ids) {
+ 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;
+ }
+ }
+ } 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 +200,42 @@
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.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<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;
};
@@ -218,9 +319,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 grouped by unique call stacks.",
nullptr,
eCommandRequiresProcess | eCommandRequiresThread |
eCommandTryTargetAPILock | eCommandProcessMustBeLaunched |
@@ -270,11 +371,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: packages/Python/lldbsuite/test/functionalities/thread/num_threads/main.cpp
===================================================================
--- packages/Python/lldbsuite/test/functionalities/thread/num_threads/main.cpp
+++ packages/Python/lldbsuite/test/functionalities/thread/num_threads/main.cpp
@@ -1,23 +1,27 @@
+#include "pseudo_barrier.h"
#include <condition_variable>
#include <mutex>
#include <thread>
+#include <vector>
std::mutex mutex;
std::condition_variable cond;
+pseudo_barrier_t thread3_barrier;
void *
thread3(void *input)
{
- std::unique_lock<std::mutex> lock(mutex);
- cond.notify_all(); // Set break point at this line.
+ pseudo_barrier_wait(thread3_barrier);
+ std::unique_lock<std::mutex> lock(mutex); // Set thread3 break point on lock at this line.
+ cond.notify_all(); // Set thread3 break point on notify_all at this line.
return NULL;
}
void *
thread2(void *input)
{
std::unique_lock<std::mutex> lock(mutex);
- cond.notify_all();
+ cond.notify_all(); // release main thread
cond.wait(lock);
return NULL;
}
@@ -36,15 +40,23 @@
std::unique_lock<std::mutex> lock(mutex);
std::thread thread_1(thread1, nullptr);
- cond.wait(lock);
+ cond.wait(lock); // wait for thread2
- std::thread thread_3(thread3, nullptr);
- cond.wait(lock);
+ pseudo_barrier_init(thread3_barrier, 10);
+
+ std::vector<std::thread> thread_3s;
+ for (int i = 0; i < 10; i++) {
+ thread_3s.push_back(std::thread(thread3, nullptr));
+ }
+
+ cond.wait(lock); // wait for thread_3s
lock.unlock();
thread_1.join();
- thread_3.join();
+ for (auto &t : thread_3s){
+ t.join();
+ }
return 0;
}
Index: packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py
===================================================================
--- packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py
+++ packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py
@@ -19,26 +19,27 @@
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
- # Find the line number to break inside main().
- self.line = line_number('main.cpp', '// Set break point at this line.')
+ # Find the line numbers for our break points.
+ self.thread3_notify_all_line = line_number('main.cpp', '// Set thread3 break point on notify_all at this line.')
+ self.thread3_before_lock_line = line_number('main.cpp', '// Set thread3 break point on lock at this line.')
- def test(self):
+ def test_number_of_threads(self):
"""Test number of threads."""
self.build()
exe = os.path.join(os.getcwd(), "a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
# This should create a breakpoint with 1 location.
lldbutil.run_break_set_by_file_and_line(
- self, "main.cpp", self.line, num_expected_locations=1)
+ self, "main.cpp", self.thread3_notify_all_line, num_expected_locations=1)
- # The breakpoint list should show 3 locations.
+ # The breakpoint list should show 1 location.
self.expect(
"breakpoint list -f",
"Breakpoint location shown correctly",
substrs=[
"1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" %
- self.line])
+ self.thread3_notify_all_line])
# Run the program.
self.runCmd("run", RUN_SUCCEEDED)
@@ -57,5 +58,60 @@
# Using std::thread may involve extra threads, so we assert that there are
# at least 4 rather than exactly 4.
self.assertTrue(
- num_threads >= 4,
+ num_threads >= 13,
'Number of expected threads and actual threads do not match.')
+
+ def test_unique_stacks(self):
+ """Test backtrace unique with multiple threads executing the same stack."""
+ self.build()
+ exe = os.path.join(os.getcwd(), "a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+
+ # Set a break point on the thread3 notify all (should get hit on threads 4-13).
+ lldbutil.run_break_set_by_file_and_line(
+ self, "main.cpp", self.thread3_before_lock_line, num_expected_locations=1)
+
+ # Run the program.
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ # Stopped once.
+ self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+ substrs=["stop reason = breakpoint 1."])
+
+ process = self.process()
+
+ # Get the number of threads
+ num_threads = process.GetNumThreads()
+
+ # Using std::thread may involve extra threads, so we assert that there are
+ # at least 10 thread3's rather than exactly 10.
+ self.assertTrue(
+ num_threads >= 10,
+ 'Number of expected threads and actual threads do not match.')
+
+ # Attempt to walk each of the thread's executing the thread3 function to
+ # the same breakpoint.
+ expect_threads = ""
+ for i in range(num_threads):
+ thread = process.GetThreadAtIndex(i)
+ self.assertTrue(thread.IsValid())
+ if not "thread3" in thread.GetFrameAtIndex(0).GetFunctionName():
+ continue
+
+ # If we aren't stopped out the thread breakpoint try to resume.
+ if thread.GetStopReason() != lldb.eStopReasonBreakpoint:
+ thread.StepOut()
+
+ self.assertEqual(thread.GetStopReason(), lldb.eStopReasonBreakpoint)
+
+ expect_threads += " #%d"%(i+1)
+
+ # Construct our expected back trace string
+ expect_string = "10 thread(s)%s" % (expect_threads)
+
+ # Now that we are stopped, we should have 10 threads waiting in the
+ # thread3 function. All of these threads should show as one stack.
+ self.expect("thread backtrace unique",
+ "Backtrace with unique stack shown correctly",
+ substrs=[expect_string,
+ "main.cpp:%d"%self.thread3_before_lock_line])
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: include/lldb/Target/StackFrameList.h
===================================================================
--- include/lldb/Target/StackFrameList.h
+++ include/lldb/Target/StackFrameList.h
@@ -70,7 +70,7 @@
size_t GetStatus(Stream &strm, uint32_t first_frame, uint32_t num_frames,
bool show_frame_info, uint32_t num_frames_with_source,
- const char *frame_marker = nullptr);
+ bool show_unique = false, const char *frame_marker = nullptr);
protected:
friend class Thread;
Index: include/lldb/Target/StackFrame.h
===================================================================
--- include/lldb/Target/StackFrame.h
+++ include/lldb/Target/StackFrame.h
@@ -342,10 +342,13 @@
/// @param [in] strm
/// The Stream to print the description to.
///
+ /// @param [in] show_unique
+ /// Whether to print the function arguments or not for backtrace unique.
+ ///
/// @param [in] frame_marker
/// Optional string that will be prepended to the frame output description.
//------------------------------------------------------------------
- void DumpUsingSettingsFormat(Stream *strm,
+ void DumpUsingSettingsFormat(Stream *strm, bool show_unique = false,
const char *frame_marker = nullptr);
//------------------------------------------------------------------
@@ -375,14 +378,18 @@
/// @param[in] show_source
/// If true, print source or disassembly as per the user's settings.
///
+ /// @param[in] show_unique
+ /// If true, print using backtrace unique style, without function
+ /// arguments as per the user's settings.
+ ///
/// @param[in] frame_marker
/// Passed to DumpUsingSettingsFormat() for the frame info printing.
///
/// @return
/// Returns true if successful.
//------------------------------------------------------------------
bool GetStatus(Stream &strm, bool show_frame_info, bool show_source,
- const char *frame_marker = nullptr);
+ bool show_unique = false, const char *frame_marker = nullptr);
//------------------------------------------------------------------
/// Query whether this frame is a concrete frame on the call stack,
Index: include/lldb/Core/Debugger.h
===================================================================
--- include/lldb/Core/Debugger.h
+++ include/lldb/Core/Debugger.h
@@ -249,6 +249,8 @@
const FormatEntity::Entry *GetFrameFormat() const;
+ const FormatEntity::Entry *GetFrameFormatUnique() const;
+
const FormatEntity::Entry *GetThreadFormat() const;
const FormatEntity::Entry *GetThreadStopFormat() const;
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits