From 2b7f32892b76cdfbe075300a5bf4a52e1b674bc7 Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani Date: Tue, 4 Feb 2020 17:53:26 +0100 Subject: [PATCH] [lldb/Target] Add Assert StackFrame Recognizer When a thread stops, this checks depending on the platform if the top frame is an abort stack frame. If so, it looks for an assert stack frame in the upper frames and set it as the most relavant frame when found. To do so, the StackFrameRecognizer class holds a "Most Relevant Frame" and a "cooked" stop reason description. When the thread is about to stop, it checks if the current frame is recognized, and if so, it fetches the recognized frame's attributes and applies them. rdar://58528686 Differential Revision: https://reviews.llvm.org/D73303 Signed-off-by: Med Ismail Bennani --- lldb/docs/use/formatting.rst | 4 +- lldb/include/lldb/Core/FormatEntity.h | 1 + lldb/include/lldb/Target/AssertFrameRecognizer.h | 54 ++++++ lldb/include/lldb/Target/StackFrameRecognizer.h | 5 + lldb/include/lldb/Target/Thread.h | 6 + .../inferior-assert/TestInferiorAssert.py | 31 ++-- .../lang/objc/exceptions/TestObjCExceptions.py | 4 +- lldb/source/API/SBThread.cpp | 3 +- lldb/source/Core/FormatEntity.cpp | 28 ++- .../ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp | 2 + lldb/source/Target/AssertFrameRecognizer.cpp | 195 +++++++++++++++++++++ lldb/source/Target/CMakeLists.txt | 1 + lldb/source/Target/Process.cpp | 11 +- lldb/source/Target/StackFrameRecognizer.cpp | 4 +- lldb/source/Target/Thread.cpp | 55 ++++++ lldb/test/Shell/Recognizer/Inputs/assert.c | 9 + lldb/test/Shell/Recognizer/assert.test | 13 ++ 17 files changed, 400 insertions(+), 26 deletions(-) create mode 100644 lldb/include/lldb/Target/AssertFrameRecognizer.h create mode 100644 lldb/source/Target/AssertFrameRecognizer.cpp create mode 100644 lldb/test/Shell/Recognizer/Inputs/assert.c create mode 100644 lldb/test/Shell/Recognizer/assert.test diff --git a/lldb/docs/use/formatting.rst b/lldb/docs/use/formatting.rst index 939c4e1..e2644d5 100644 --- a/lldb/docs/use/formatting.rst +++ b/lldb/docs/use/formatting.rst @@ -134,7 +134,9 @@ A complete list of currently supported format string variables is listed below: +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ``thread.queue`` | The queue name of the thread if the target OS supports dispatch queues | +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``thread.stop-reason`` | A textual reason each thread stopped | +| ``thread.stop-reason`` | A textual reason why the thread stopped. If the thread have a recognized frame, this displays its recognized stop reason. Otherwise, gets the stop info description. | ++---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``thread.stop-reason-raw`` | A textual reason why the thread stopped. Always returns stop info description. | +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ``thread.return-value`` | The return value of the latest step operation (currently only for step-out.) | +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h index 8ee320b0..106b070 100644 --- a/lldb/include/lldb/Core/FormatEntity.h +++ b/lldb/include/lldb/Core/FormatEntity.h @@ -61,6 +61,7 @@ public: ThreadName, ThreadQueue, ThreadStopReason, + ThreadStopReasonRaw, ThreadReturnValue, ThreadCompletedExpression, ScriptThread, diff --git a/lldb/include/lldb/Target/AssertFrameRecognizer.h b/lldb/include/lldb/Target/AssertFrameRecognizer.h new file mode 100644 index 0000000..02899b8 --- /dev/null +++ b/lldb/include/lldb/Target/AssertFrameRecognizer.h @@ -0,0 +1,54 @@ +//===-- AssertFrameRecognizer.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 +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AssertFrameRecognizer_h_ +#define liblldb_AssertFrameRecognizer_h_ + +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrameRecognizer.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/FileSpec.h" + +#include + +namespace lldb_private { + +/// Registers the assert stack frame recognizer. +/// +/// \param[in] process +/// The process that is currently asserting. This will give us information on +/// the target and the platform. +void RegisterAssertFrameRecognizer(Process *process); + +/// \class AssertRecognizedStackFrame +/// +/// Holds the stack frame where the assert is called from. +class AssertRecognizedStackFrame : public RecognizedStackFrame { +public: + AssertRecognizedStackFrame(lldb::StackFrameSP most_relevant_frame_sp); + lldb::StackFrameSP GetMostRelevantFrame() override; + +private: + lldb::StackFrameSP m_most_relevant_frame; +}; + +/// \class AssertFrameRecognizer +/// +/// When a thread stops, it checks depending on the platform if the top frame is +/// an abort stack frame. If so, it looks for an assert stack frame in the upper +/// frames and set it as the most relavant frame when found. +class AssertFrameRecognizer : public StackFrameRecognizer { +public: + std::string GetName() override { return "Assert StackFrame Recognizer"; } + lldb::RecognizedStackFrameSP + RecognizeFrame(lldb::StackFrameSP frame_sp) override; +}; + +} // namespace lldb_private + +#endif // liblldb_AssertFrameRecognizer_h_ diff --git a/lldb/include/lldb/Target/StackFrameRecognizer.h b/lldb/include/lldb/Target/StackFrameRecognizer.h index 2021ac5..b509e07 100644 --- a/lldb/include/lldb/Target/StackFrameRecognizer.h +++ b/lldb/include/lldb/Target/StackFrameRecognizer.h @@ -12,6 +12,7 @@ #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectList.h" #include "lldb/Symbol/VariableList.h" +#include "lldb/Target/StopInfo.h" #include "lldb/Utility/StructuredData.h" #include "lldb/lldb-private-forward.h" #include "lldb/lldb-public.h" @@ -33,10 +34,14 @@ public: virtual lldb::ValueObjectSP GetExceptionObject() { return lldb::ValueObjectSP(); } + virtual lldb::StackFrameSP GetMostRelevantFrame() { return nullptr; }; virtual ~RecognizedStackFrame(){}; + std::string GetStopDescription() { return m_stop_desc; } + protected: lldb::ValueObjectListSP m_arguments; + std::string m_stop_desc; }; /// \class StackFrameRecognizer diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index cdc8d39..e68ef08 100644 --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -216,6 +216,12 @@ public: virtual void RefreshStateAfterStop() = 0; + void SelectMostRelevantFrame(); + + std::string GetStopDescription(); + + std::string GetStopDescriptionRaw(); + void WillStop(); bool ShouldStop(Event *event_ptr); diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py b/lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py index 36fdc86..edbfe70 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py @@ -107,16 +107,22 @@ class AssertingInferiorTestCase(TestBase): if matched: # On android until API-16 the abort() call ended in a sigsegv # instead of in a sigabrt - stop_reason = 'stop reason = signal SIGSEGV' + stop_reason = 'signal SIGSEGV' else: - stop_reason = 'stop reason = signal SIGABRT' + stop_reason = 'signal SIGABRT' + + target = self.dbg.GetTargetAtIndex(0) + process = target.GetProcess() + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal) + + pattern = "stop reason = (" + stop_reason + "|hit program assert)" # The stop reason of the thread should be an abort signal or exception. self.expect("thread list", STOPPED_DUE_TO_ASSERT, - substrs=['stopped', - stop_reason]) + patterns=[pattern], + substrs=['stopped']) - return stop_reason + return pattern def setUp(self): # Call super's setUp(). @@ -134,12 +140,13 @@ class AssertingInferiorTestCase(TestBase): # And it should report a backtrace that includes the assert site. self.expect("thread backtrace all", - substrs=[stop_reason, 'main', 'argc', 'argv']) + patterns=[stop_reason], + substrs=['main', 'argc', 'argv']) # And it should report the correct line number. self.expect("thread backtrace all", - substrs=[stop_reason, - 'main.c:%d' % self.line]) + patterns=[stop_reason], + substrs=['main.c:%d' % self.line]) def inferior_asserting_python(self): """Inferior asserts upon launching; lldb should catch the event and stop.""" @@ -173,6 +180,10 @@ class AssertingInferiorTestCase(TestBase): self.runCmd("run", RUN_SUCCEEDED) self.check_stop_reason() + # change current frame to frame 0, since recognizer might have selected + # different frame. + self.runCmd("frame select 0", RUN_SUCCEEDED) + # lldb should be able to read from registers from the inferior after # asserting. lldbplatformutil.check_first_register_readable(self) @@ -311,5 +322,5 @@ class AssertingInferiorTestCase(TestBase): # And it should report the correct line number. self.expect("thread backtrace all", - substrs=[stop_reason, - 'main.c:%d' % self.line]) + patterns=[stop_reason], + substrs=['main.c:%d' % self.line]) diff --git a/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py b/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py index ce9ee8e..f7d18e6 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py +++ b/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py @@ -25,8 +25,8 @@ class ObjCExceptionsTestCase(TestBase): launch_info = lldb.SBLaunchInfo(["a.out", "0"]) lldbutil.run_to_name_breakpoint(self, "objc_exception_throw", launch_info=launch_info) - self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, - substrs=['stopped', 'stop reason = breakpoint']) + self.expect("thread list", + substrs=['stopped', 'stop reason = hit Objective-C exception']) self.expect('thread exception', substrs=[ '(NSException *) exception = ', diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp index 6fe4f66..b215cdd 100644 --- a/lldb/source/API/SBThread.cpp +++ b/lldb/source/API/SBThread.cpp @@ -325,7 +325,8 @@ size_t SBThread::GetStopDescription(char *dst, size_t dst_len) { StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo(); if (stop_info_sp) { - const char *stop_desc = stop_info_sp->GetDescription(); + const char *stop_desc = + exe_ctx.GetThreadPtr()->GetStopDescription().data(); if (stop_desc) { if (dst) return ::snprintf(dst, dst_len, "%s", stop_desc); diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 0ba333c..442d3d1 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -166,6 +166,7 @@ static FormatEntity::Entry::Definition g_thread_child_entries[] = { ENTRY("queue", ThreadQueue), ENTRY("name", ThreadName), ENTRY("stop-reason", ThreadStopReason), + ENTRY("stop-reason-raw", ThreadStopReasonRaw), ENTRY("return-value", ThreadReturnValue), ENTRY("completed-expression", ThreadCompletedExpression), }; @@ -328,6 +329,7 @@ const char *FormatEntity::Entry::TypeToCString(Type t) { ENUM_TO_CSTR(ThreadName); ENUM_TO_CSTR(ThreadQueue); ENUM_TO_CSTR(ThreadStopReason); + ENUM_TO_CSTR(ThreadStopReasonRaw); ENUM_TO_CSTR(ThreadReturnValue); ENUM_TO_CSTR(ThreadCompletedExpression); ENUM_TO_CSTR(ScriptThread); @@ -1273,15 +1275,23 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, case Entry::Type::ThreadStopReason: if (exe_ctx) { - Thread *thread = exe_ctx->GetThreadPtr(); - if (thread) { - StopInfoSP stop_info_sp = thread->GetStopInfo(); - if (stop_info_sp && stop_info_sp->IsValid()) { - const char *cstr = stop_info_sp->GetDescription(); - if (cstr && cstr[0]) { - s.PutCString(cstr); - return true; - } + if (Thread *thread = exe_ctx->GetThreadPtr()) { + std::string stop_description = thread->GetStopDescription(); + if (!stop_description.empty()) { + s.PutCString(stop_description); + return true; + } + } + } + return false; + + case Entry::Type::ThreadStopReasonRaw: + if (exe_ctx) { + if (Thread *thread = exe_ctx->GetThreadPtr()) { + std::string stop_description = thread->GetStopDescriptionRaw(); + if (!stop_description.empty()) { + s.PutCString(stop_description); + return true; } } } diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index 3331a5f..6acc231 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -2666,6 +2666,8 @@ class ObjCExceptionRecognizedStackFrame : public RecognizedStackFrame { m_arguments = ValueObjectListSP(new ValueObjectList()); m_arguments->Append(exception); + + m_stop_desc = "hit Objective-C exception"; } ValueObjectSP exception; diff --git a/lldb/source/Target/AssertFrameRecognizer.cpp b/lldb/source/Target/AssertFrameRecognizer.cpp new file mode 100644 index 0000000..58829be --- /dev/null +++ b/lldb/source/Target/AssertFrameRecognizer.cpp @@ -0,0 +1,195 @@ +#include "lldb/Core/Module.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrameList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Logging.h" + +#include "lldb/Target/AssertFrameRecognizer.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +namespace lldb_private { +/// Checkes if the module containing a symbol has debug info. +/// +/// \param[in] target +/// The target containing the module. +/// \param[in] module_spec +/// The module spec that should contain the symbol. +/// \param[in] symbol_name +/// The symbol's name that should be contained in the debug info. +/// \return +/// If \b true the symbol was found, \b false otherwise. +bool ModuleHasDebugInfo(Target &target, FileSpec &module_spec, + StringRef symbol_name) { + ModuleSP module_sp = target.GetImages().FindFirstModule(module_spec); + + if (!module_sp) + return false; + + return module_sp->FindFirstSymbolWithNameAndType(ConstString(symbol_name)); +} + +/// Fetches the abort frame location depending on the current platform. +/// +/// \param[in] process_sp +/// The process that is currently aborting. This will give us information on +/// the target and the platform. +/// \return +/// If the platform is supported, returns an optional tuple containing +/// the abort module as a \a FileSpec and the symbol name as a \a StringRef. +/// Otherwise, returns \a llvm::None. +llvm::Optional> +GetAbortLocation(Process *process) { + Target &target = process->GetTarget(); + + FileSpec module_spec; + StringRef symbol_name; + + switch (target.GetArchitecture().GetTriple().getOS()) { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + module_spec = FileSpec("libsystem_kernel.dylib"); + symbol_name = "__pthread_kill"; + break; + case llvm::Triple::Linux: + module_spec = FileSpec("libc.so.6"); + symbol_name = "__GI_raise"; + if (!ModuleHasDebugInfo(target, module_spec, symbol_name)) + symbol_name = "raise"; + break; + default: + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); + LLDB_LOG(log, "AssertFrameRecognizer::GetAbortLocation Unsupported OS"); + return llvm::None; + } + + return std::make_tuple(module_spec, symbol_name); +} + +/// Fetches the assert frame location depending on the current platform. +/// +/// \param[in] process_sp +/// The process that is currently asserting. This will give us information on +/// the target and the platform. +/// \return +/// If the platform is supported, returns an optional tuple containing +/// the asserting frame module as a \a FileSpec and the symbol name as a \a +/// StringRef. +/// Otherwise, returns \a llvm::None. +llvm::Optional> +GetAssertLocation(Process *process) { + Target &target = process->GetTarget(); + + FileSpec module_spec; + StringRef symbol_name; + + switch (target.GetArchitecture().GetTriple().getOS()) { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + module_spec = FileSpec("libsystem_c.dylib"); + symbol_name = "__assert_rtn"; + break; + case llvm::Triple::Linux: + module_spec = FileSpec("libc.so.6"); + symbol_name = "__GI___assert_fail"; + if (!ModuleHasDebugInfo(target, module_spec, symbol_name)) + symbol_name = "__assert_fail"; + break; + default: + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); + LLDB_LOG(log, "AssertFrameRecognizer::GetAssertLocation Unsupported OS"); + return llvm::None; + } + + return std::make_tuple(module_spec, symbol_name); +} + +void RegisterAssertFrameRecognizer(Process *process) { + static llvm::once_flag g_once_flag; + llvm::call_once(g_once_flag, [process]() { + auto abort_location = GetAbortLocation(process); + + if (!abort_location.hasValue()) + return; + + FileSpec module_spec; + StringRef function_name; + std::tie(module_spec, function_name) = *abort_location; + + StackFrameRecognizerManager::AddRecognizer( + StackFrameRecognizerSP(new AssertFrameRecognizer()), + module_spec.GetFilename(), ConstString(function_name), false); + }); +} + +} // namespace lldb_private + +lldb::RecognizedStackFrameSP +AssertFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) { + ThreadSP thread_sp = frame_sp->GetThread(); + ProcessSP process_sp = thread_sp->GetProcess(); + + auto assert_location = GetAssertLocation(process_sp.get()); + + if (!assert_location.hasValue()) + return RecognizedStackFrameSP(); + + FileSpec module_spec; + StringRef function_name; + std::tie(module_spec, function_name) = *assert_location; + + const uint32_t frames_to_fetch = 5; + const uint32_t last_frame_index = frames_to_fetch - 1; + StackFrameSP prev_frame_sp = nullptr; + + // Fetch most relevant frame + for (uint32_t frame_index = 0; frame_index < frames_to_fetch; frame_index++) { + prev_frame_sp = thread_sp->GetStackFrameAtIndex(frame_index); + + if (!prev_frame_sp) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); + LLDB_LOG(log, "Abort Recognizer: Hit unwinding bound ({1} frames)!", + frames_to_fetch); + break; + } + + SymbolContext sym_ctx = + prev_frame_sp->GetSymbolContext(eSymbolContextEverything); + + if (sym_ctx.module_sp->GetFileSpec().FileEquals(module_spec) && + sym_ctx.GetFunctionName() == ConstString(function_name)) { + + // We go a frame beyond the assert location because the most relevant + // frame for the user is the one in which the assert function was called. + // If the assert location is the last frame fetched, then it is set as + // the most relevant frame. + + StackFrameSP most_relevant_frame_sp = thread_sp->GetStackFrameAtIndex( + std::min(frame_index + 1, last_frame_index)); + + // Pass assert location to AbortRecognizedStackFrame to set as most + // relevant frame. + return lldb::RecognizedStackFrameSP( + new AssertRecognizedStackFrame(most_relevant_frame_sp)); + } + } + + return RecognizedStackFrameSP(); +}; + +AssertRecognizedStackFrame::AssertRecognizedStackFrame( + StackFrameSP most_relevant_frame_sp) + : m_most_relevant_frame(most_relevant_frame_sp) { + m_stop_desc = "hit program assert"; +} + +lldb::StackFrameSP AssertRecognizedStackFrame::GetMostRelevantFrame() { + return m_most_relevant_frame; +} diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt index 12ae2a9..8930654 100644 --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -8,6 +8,7 @@ lldb_tablegen(TargetPropertiesEnum.inc -gen-lldb-property-enum-defs add_lldb_library(lldbTarget ABI.cpp + AssertFrameRecognizer.cpp ExecutionContext.cpp InstrumentationRuntime.cpp InstrumentationRuntimeStopInfo.cpp diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 3de6fe2..8038200 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -38,6 +38,7 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/ABI.h" +#include "lldb/Target/AssertFrameRecognizer.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/InstrumentationRuntime.h" #include "lldb/Target/JITLoader.h" @@ -538,6 +539,8 @@ Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp, target_sp->GetPlatform()->GetDefaultMemoryCacheLineSize(); if (!value_sp->OptionWasSet() && platform_cache_line_size != 0) value_sp->SetUInt64Value(platform_cache_line_size); + + RegisterAssertFrameRecognizer(this); } Process::~Process() { @@ -934,11 +937,17 @@ bool Process::HandleProcessStateChangedEvent(const EventSP &event_sp, Debugger &debugger = process_sp->GetTarget().GetDebugger(); if (debugger.GetTargetList().GetSelectedTarget().get() == &process_sp->GetTarget()) { + ThreadSP thread_sp = process_sp->GetThreadList().GetSelectedThread(); + + if (!thread_sp || !thread_sp->IsValid()) + return false; + const bool only_threads_with_stop_reason = true; - const uint32_t start_frame = 0; + const uint32_t start_frame = thread_sp->GetSelectedFrameIndex(); const uint32_t num_frames = 1; const uint32_t num_frames_with_source = 1; const bool stop_format = true; + process_sp->GetStatus(*stream); process_sp->GetThreadStatus(*stream, only_threads_with_stop_reason, start_frame, num_frames, diff --git a/lldb/source/Target/StackFrameRecognizer.cpp b/lldb/source/Target/StackFrameRecognizer.cpp index 57d1b3d..67a320a 100644 --- a/lldb/source/Target/StackFrameRecognizer.cpp +++ b/lldb/source/Target/StackFrameRecognizer.cpp @@ -92,8 +92,8 @@ public: } StackFrameRecognizerSP GetRecognizerForFrame(StackFrameSP frame) { - const SymbolContext &symctx = - frame->GetSymbolContext(eSymbolContextModule | eSymbolContextFunction); + const SymbolContext &symctx = frame->GetSymbolContext( + eSymbolContextModule | eSymbolContextFunction | eSymbolContextSymbol); ConstString function_name = symctx.GetFunctionName(); ModuleSP module_sp = symctx.module_sp; if (!module_sp) return StackFrameRecognizerSP(); diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index 331bbed..00f8b5a 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -573,9 +573,64 @@ void Thread::SetState(StateType state) { m_state = state; } +std::string Thread::GetStopDescription() { + StackFrameSP frame_sp = GetStackFrameAtIndex(0); + + if (!frame_sp) + return GetStopDescriptionRaw(); + + auto recognized_frame_sp = frame_sp->GetRecognizedFrame(); + + if (!recognized_frame_sp) + return GetStopDescriptionRaw(); + + std::string recognized_stop_description = + recognized_frame_sp->GetStopDescription(); + + if (!recognized_stop_description.empty()) + return recognized_stop_description; + + return GetStopDescriptionRaw(); +} + +std::string Thread::GetStopDescriptionRaw() { + StopInfoSP stop_info_sp = GetStopInfo(); + std::string raw_stop_description; + if (stop_info_sp && stop_info_sp->IsValid()) + raw_stop_description = stop_info_sp->GetDescription(); + return raw_stop_description; +} + +void Thread::SelectMostRelevantFrame() { + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD); + + auto frames_list_sp = GetStackFrameList(); + + // Only the top frame should be recognized. + auto frame_sp = frames_list_sp->GetFrameAtIndex(0); + + auto recognized_frame_sp = frame_sp->GetRecognizedFrame(); + + if (!recognized_frame_sp) { + LLDB_LOG(log, "Frame #0 not recognized"); + return; + } + + if (StackFrameSP most_relevant_frame_sp = + recognized_frame_sp->GetMostRelevantFrame()) { + LLDB_LOG(log, "Found most relevant frame at index {0}", + most_relevant_frame_sp->GetFrameIndex()); + SetSelectedFrame(most_relevant_frame_sp.get()); + } else { + LLDB_LOG(log, "No relevant frame!"); + } +} + void Thread::WillStop() { ThreadPlan *current_plan = GetCurrentPlan(); + SelectMostRelevantFrame(); + // FIXME: I may decide to disallow threads with no plans. In which // case this should go to an assert. diff --git a/lldb/test/Shell/Recognizer/Inputs/assert.c b/lldb/test/Shell/Recognizer/Inputs/assert.c new file mode 100644 index 0000000..6121335 --- /dev/null +++ b/lldb/test/Shell/Recognizer/Inputs/assert.c @@ -0,0 +1,9 @@ +#include + +int main() { + int a = 42; + assert(a == 42); + a--; + assert(a == 42); + return 0; +} diff --git a/lldb/test/Shell/Recognizer/assert.test b/lldb/test/Shell/Recognizer/assert.test new file mode 100644 index 0000000..9b4aa21 --- /dev/null +++ b/lldb/test/Shell/Recognizer/assert.test @@ -0,0 +1,13 @@ +# UNSUPPORTED: system-windows +# RUN: %clang_host -g -O0 %S/Inputs/assert.c -o %t.out +# RUN: %lldb -b -s %s %t.out | FileCheck %s +run +# CHECK: thread #{{.*}}stop reason = hit program assert +frame info +# CHECK: frame #{{.*}}`main at assert.c +frame recognizer info 0 +# CHECK: frame 0 is recognized by Assert StackFrame Recognizer +set set thread-format "{${thread.stop-reason-raw}}\n" +thread info +# CHECK: signal SIGABRT +q -- 2.7.4