From 03a6b858fde5c644ec16b1fddd8e10aa9ef3f0ad Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani Date: Thu, 23 Jan 2020 15:11:53 +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 | 55 +++++++ lldb/include/lldb/Target/StackFrameRecognizer.h | 3 + lldb/include/lldb/Target/Thread.h | 6 + .../inferior-assert/TestInferiorAssert.py | 29 +++- .../lang/objc/exceptions/TestObjCExceptions.py | 4 +- lldb/source/API/SBThread.cpp | 3 +- lldb/source/Core/FormatEntity.cpp | 28 ++-- .../ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp | 4 + lldb/source/Target/AssertFrameRecognizer.cpp | 173 +++++++++++++++++++++ lldb/source/Target/CMakeLists.txt | 1 + lldb/source/Target/Process.cpp | 11 +- lldb/source/Target/StackFrameRecognizer.cpp | 2 +- lldb/source/Target/Thread.cpp | 55 +++++++ lldb/test/Shell/Recognizer/Inputs/assert.c | 9 ++ lldb/test/Shell/Recognizer/assert.test | 13 ++ 17 files changed, 380 insertions(+), 21 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..4acfefa --- /dev/null +++ b/lldb/include/lldb/Target/AssertFrameRecognizer.h @@ -0,0 +1,55 @@ +//===-- 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; + llvm::StringRef GetStopDescription() 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..59febd5 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,6 +34,8 @@ public: virtual lldb::ValueObjectSP GetExceptionObject() { return lldb::ValueObjectSP(); } + virtual lldb::StackFrameSP GetMostRelevantFrame() { return nullptr; }; + virtual llvm::StringRef GetStopDescription() { return ""; } virtual ~RecognizedStackFrame(){}; protected: diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index cdc8d39..1a24e2c 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(); + + llvm::StringRef GetStopDescription(); + + llvm::StringRef 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..ab81bde 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py @@ -104,12 +104,13 @@ class AssertingInferiorTestCase(TestBase): def check_stop_reason(self): matched = lldbplatformutil.match_android_device( self.getArchitecture(), valid_api_levels=list(range(1, 16 + 1))) - 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' - else: - stop_reason = 'stop reason = signal SIGABRT' + + target = self.dbg.GetTargetAtIndex(0) + process = target.GetProcess() + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal) + + stop_reason = 'stop reason = ' + thread.GetStopDescription(256) # The stop reason of the thread should be an abort signal or exception. self.expect("thread list", STOPPED_DUE_TO_ASSERT, @@ -173,6 +174,22 @@ 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. + target = self.dbg.GetTargetAtIndex(0) + self.assertTrue(target, VALID_TARGET) + + process = target.GetProcess() + self.assertTrue(process.IsValid(), "current process is valid") + + thread = process.GetSelectedThread() + self.assertTrue(thread.IsValid(), "current thread is valid") + + thread.SetSelectedFrame(0) + frame = thread.GetSelectedFrame() + + self.assertTrue(thread.GetFrameAtIndex(0) == frame, "Frame #0 selected") + # lldb should be able to read from registers from the inferior after # asserting. lldbplatformutil.check_first_register_readable(self) 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 f90e931..9516d72 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 4d43df9..e47f109 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()) { + llvm::StringRef 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()) { + llvm::StringRef 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 e5c816d..beb5b13 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -2673,6 +2673,10 @@ class ObjCExceptionRecognizedStackFrame : public RecognizedStackFrame { ValueObjectSP exception; lldb::ValueObjectSP GetExceptionObject() override { return exception; } + + llvm::StringRef GetStopDescription() override { + return "hit Objective-C exception"; + } }; class ObjCExceptionThrowFrameRecognizer : public StackFrameRecognizer { diff --git a/lldb/source/Target/AssertFrameRecognizer.cpp b/lldb/source/Target/AssertFrameRecognizer.cpp new file mode 100644 index 0000000..489cba1 --- /dev/null +++ b/lldb/source/Target/AssertFrameRecognizer.cpp @@ -0,0 +1,173 @@ +#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 { +/// 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(); + + StringRef module_name; + StringRef symbol_name; + + switch (target.GetArchitecture().GetTriple().getOS()) { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + module_name = "libsystem_kernel.dylib"; + symbol_name = "__pthread_kill"; + break; + case llvm::Triple::Linux: + module_name = "libc.so.6"; + symbol_name = "__GI_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(FileSpec(module_name), 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(); + + StringRef module_name; + StringRef symbol_name; + + switch (target.GetArchitecture().GetTriple().getOS()) { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + module_name = "libsystem_c.dylib"; + symbol_name = "__assert_rtn"; + break; + case llvm::Triple::Linux: + module_name = "libc.so.6"; + symbol_name = "__GI___assert_fail"; + break; + default: + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); + LLDB_LOG(log, "AssertFrameRecognizer::GetAbortLocation Unsupported OS"); + return llvm::None; + } + + return std::make_tuple(FileSpec(module_name), 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 + +AssertRecognizedStackFrame::AssertRecognizedStackFrame( + StackFrameSP most_relevant_frame_sp) + : m_most_relevant_frame(most_relevant_frame_sp) {} + +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(); +}; + +lldb::StackFrameSP AssertRecognizedStackFrame::GetMostRelevantFrame() { + return m_most_relevant_frame; +} + +llvm::StringRef AssertRecognizedStackFrame::GetStopDescription() { + return "hit program assert"; +} 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 b2213d7..9dd6af9 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 fa71ef7..296f2e4 100644 --- a/lldb/source/Target/StackFrameRecognizer.cpp +++ b/lldb/source/Target/StackFrameRecognizer.cpp @@ -92,7 +92,7 @@ public: StackFrameRecognizerSP GetRecognizerForFrame(StackFrameSP frame) { const SymbolContext &symctx = - frame->GetSymbolContext(eSymbolContextModule | eSymbolContextFunction); + frame->GetSymbolContext(eSymbolContextEverything); 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 82930de..75df531 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; } +llvm::StringRef 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(); + + llvm::StringRef recognized_stop_description = + recognized_frame_sp->GetStopDescription(); + + if (!recognized_stop_description.empty()) + return recognized_stop_description; + + return GetStopDescriptionRaw(); +} + +llvm::StringRef Thread::GetStopDescriptionRaw() { + StopInfoSP stop_info_sp = GetStopInfo(); + llvm::StringRef 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