mib created this revision. mib added reviewers: jingham, friss, LLDB. mib added a project: LLDB. Herald added subscribers: lldb-commits, jfb, mgorny.
When a thread stops on an abort frame, the recognizer will unwind the stack to look for the frame from which the assert was triggered. Once located, it will set this frame as the current one and change the stop reason. To do so, the StackFrameRecognizer class holds a "Most Relevant Frame" and a StopInfo attribute. 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 Signed-off-by: Med Ismail Bennani <medismail.benn...@gmail.com> Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D73303 Files: lldb/include/lldb/Target/AbortRecognizer.h lldb/include/lldb/Target/StackFrameRecognizer.h lldb/include/lldb/Target/StopInfo.h lldb/include/lldb/Target/Thread.h lldb/source/Target/AbortRecognizer.cpp lldb/source/Target/CMakeLists.txt lldb/source/Target/Process.cpp lldb/source/Target/StopInfo.cpp lldb/source/Target/Thread.cpp lldb/test/Shell/Recognizer/Inputs/abort.c lldb/test/Shell/Recognizer/abort.test
Index: lldb/test/Shell/Recognizer/abort.test =================================================================== --- /dev/null +++ lldb/test/Shell/Recognizer/abort.test @@ -0,0 +1,10 @@ +# UNSUPPORTED: system-windows +# RUN: %clang_host -g -O0 %S/Inputs/abort.c -o %t.out +# RUN: %lldb -b -s %s %t.out | FileCheck %s +run +# CHECK: thread #{{.*}}stop reason = signal SIGABRT +frame info +# CHECK: frame #4: {{.*}}`main at abort.c +frame recognizer info 0 +# CHECK: frame 0 is recognized by Abort StackFrame Recognizer +q Index: lldb/test/Shell/Recognizer/Inputs/abort.c =================================================================== --- /dev/null +++ lldb/test/Shell/Recognizer/Inputs/abort.c @@ -0,0 +1,9 @@ +#include <assert.h> + +int main() { + int a = 42; + assert(a == 42); + a--; + assert(a == 42); + return 0; +} Index: lldb/source/Target/Thread.cpp =================================================================== --- lldb/source/Target/Thread.cpp +++ lldb/source/Target/Thread.cpp @@ -573,9 +573,42 @@ m_state = state; } +void Thread::ApplyMostRelevantFrames() { + if (!m_curr_frames_sp) + return; + + auto cur_frame = m_curr_frames_sp->GetFrameAtIndex(0); + SymbolContext sym_ctx = cur_frame->GetSymbolContext(eSymbolContextEverything); + + auto recognized_frame_sp = cur_frame->GetRecognizedFrame(); + + if (!recognized_frame_sp) + return; + + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET); + + if (StackFrameSP most_relevant_frame_sp = + recognized_frame_sp->GetMostRelevantFrame()) { + LLDB_LOG(log, "Found most relevant frame {0}", + most_relevant_frame_sp->GetFrameIndex()); + SetSelectedFrame(most_relevant_frame_sp.get()); + } else { + LLDB_LOG(log, "No relevant frame!"); + } + + if (StopInfoSP stop_info_sp = recognized_frame_sp->GetStopInfo()) { + LLDB_LOG(log, "Found stop reason {0}", stop_info_sp->GetStopReason()); + SetStopInfo(stop_info_sp); + } else { + LLDB_LOG(log, "No stop reason for recognized frame!"); + } +} + void Thread::WillStop() { ThreadPlan *current_plan = GetCurrentPlan(); + ApplyMostRelevantFrames(); + // FIXME: I may decide to disallow threads with no plans. In which // case this should go to an assert. Index: lldb/source/Target/StopInfo.cpp =================================================================== --- lldb/source/Target/StopInfo.cpp +++ lldb/source/Target/StopInfo.cpp @@ -1016,6 +1016,23 @@ } }; +// StopInfoRecognizedFrame + +class StopInfoRecognizedFrame : public StopInfoException { +public: + StopInfoRecognizedFrame(Thread &thread, const char *description) + : StopInfoException(thread, description) {} + + ~StopInfoRecognizedFrame() override = default; + + const char *GetDescription() override { + if (m_description.empty()) + return "recognized frame:"; + else + return m_description.c_str(); + } +}; + // StopInfoThreadPlan class StopInfoThreadPlan : public StopInfo { @@ -1133,6 +1150,12 @@ return StopInfoSP(new StopInfoException(thread, description)); } +StopInfoSP +StopInfo::CreateStopReasonForRecognizedFrame(Thread &thread, + const char *description) { + return StopInfoSP(new StopInfoRecognizedFrame(thread, description)); +} + StopInfoSP StopInfo::CreateStopReasonWithExec(Thread &thread) { return StopInfoSP(new StopInfoExec(thread)); } Index: lldb/source/Target/Process.cpp =================================================================== --- lldb/source/Target/Process.cpp +++ 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/AbortRecognizer.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/InstrumentationRuntime.h" #include "lldb/Target/JITLoader.h" @@ -538,6 +539,8 @@ target_sp->GetPlatform()->GetDefaultMemoryCacheLineSize(); if (!value_sp->OptionWasSet() && platform_cache_line_size != 0) value_sp->SetUInt64Value(platform_cache_line_size); + + RegisterAbortRecognizer(this); } Process::~Process() { @@ -934,11 +937,27 @@ Debugger &debugger = process_sp->GetTarget().GetDebugger(); if (debugger.GetTargetList().GetSelectedTarget().get() == &process_sp->GetTarget()) { + ThreadSP cur_thread = process_sp->GetThreadList().GetSelectedThread(); + + if (!cur_thread || !cur_thread->IsValid()) + return false; + + uint32_t mrf_idx = 0; + + auto frame_sp = cur_thread->GetStackFrameAtIndex(mrf_idx); + + if (RecognizedStackFrameSP recognized_frame_sp = + frame_sp->GetRecognizedFrame()) + if (StackFrameSP mrf_sp = + recognized_frame_sp->GetMostRelevantFrame()) + mrf_idx = mrf_sp->GetFrameIndex(); + const bool only_threads_with_stop_reason = true; - const uint32_t start_frame = 0; + const uint32_t start_frame = mrf_idx; 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, Index: lldb/source/Target/CMakeLists.txt =================================================================== --- lldb/source/Target/CMakeLists.txt +++ lldb/source/Target/CMakeLists.txt @@ -8,6 +8,7 @@ add_lldb_library(lldbTarget ABI.cpp + AbortRecognizer.cpp ExecutionContext.cpp InstrumentationRuntime.cpp InstrumentationRuntimeStopInfo.cpp Index: lldb/source/Target/AbortRecognizer.cpp =================================================================== --- /dev/null +++ lldb/source/Target/AbortRecognizer.cpp @@ -0,0 +1,186 @@ +//===-- AbortRecognizer.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 +// +//===----------------------------------------------------------------------===// + +#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/AbortRecognizer.h" + +using namespace lldb; +using namespace lldb_private; + +llvm::Optional<std::tuple<FileSpec, ConstString>> +AbortRecognizerHandler::GetAbortLocation(Process *process) { + Target &target = process->GetTarget(); + + std::string module_name; + std::string 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, "Abort Recognizer::GetAbortLocation Unsupported OS"); + return llvm::None; + } + + return std::make_tuple(FileSpec(module_name), ConstString(symbol_name)); +} + +llvm::Optional<std::tuple<FileSpec, ConstString>> +AbortRecognizerHandler::GetAssertLocation(Process *process) { + Target &target = process->GetTarget(); + + std::string module_name; + std::string 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, "Abort Recognizer::GetAbortLocation Unsupported OS"); + return llvm::None; + } + + return std::make_tuple(FileSpec(module_name), ConstString(symbol_name)); +} + +#pragma mark Frame recognizers + +AbortRecognizedStackFrame::AbortRecognizedStackFrame( + ThreadSP thread_sp, FileSpec module_spec, ConstString function_name) { + const uint32_t frames_to_fetch = 10; + StackFrameSP prev_frame_sp = nullptr; + + // Fetch most relevant frame + for (uint32_t i = 0; i < frames_to_fetch; i++) { + prev_frame_sp = thread_sp->GetStackFrameAtIndex(i); + + 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().GetFilename() == + module_spec.GetFilename() && + sym_ctx.GetFunctionName() == function_name) { + if (i < frames_to_fetch - 1) + m_most_relevant_frame = thread_sp->GetStackFrameAtIndex(i + 1); + else + m_most_relevant_frame = thread_sp->GetStackFrameAtIndex(i); + break; + } + } + + // FIXME: This breaks several tests. + // m_stop_info_sp = StopInfo::CreateStopReasonForRecognizedFrame( + // *thread_sp.get(), "hit program assert"); + + m_stop_info_sp = nullptr; +} + +lldb::RecognizedStackFrameSP +AbortFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame) { + // Check if called with Frame #0 + if (frame->GetFrameIndex()) + return RecognizedStackFrameSP(); + + ThreadSP thread_sp = frame->GetThread(); + ProcessSP process_sp = thread_sp->GetProcess(); + + // Fetch abort location + auto abort_location = + AbortRecognizerHandler::GetAbortLocation(process_sp.get()); + + if (!abort_location.hasValue()) + return RecognizedStackFrameSP(); + + FileSpec module_spec; + ConstString function_name; + std::tie(module_spec, function_name) = *abort_location; + + SymbolContext sc = frame->GetSymbolContext(eSymbolContextModule); + + // Check if frame is in abort module + if (!sc.module_sp || + sc.module_sp->GetFileSpec().GetFilename() != module_spec.GetFilename()) + return RecognizedStackFrameSP(); + + // Fetch assert location + auto assert_location = + AbortRecognizerHandler::GetAssertLocation(process_sp.get()); + + if (!assert_location.hasValue()) + return RecognizedStackFrameSP(); + + std::tie(module_spec, function_name) = *assert_location; + + // Pass assert location to AbortRecognizedStackFrame to set as most relevant + // frame. + return lldb::RecognizedStackFrameSP( + new AbortRecognizedStackFrame(thread_sp, module_spec, function_name)); +}; + +lldb::StackFrameSP AbortRecognizedStackFrame::GetMostRelevantFrame() { + return m_most_relevant_frame; +} + +lldb::StopInfoSP AbortRecognizedStackFrame::GetStopInfo() { + return m_stop_info_sp; +} + +namespace lldb_private { + +void RegisterAbortRecognizer(Process *process) { + static llvm::once_flag g_once_flag; + llvm::call_once(g_once_flag, [process]() { + auto abort_location = AbortRecognizerHandler::GetAbortLocation(process); + + if (!abort_location.hasValue()) + return; + + FileSpec module_spec; + ConstString function_name; + std::tie(module_spec, function_name) = *abort_location; + + StackFrameRecognizerManager::AddRecognizer( + StackFrameRecognizerSP(new AbortFrameRecognizer()), + module_spec.GetFilename(), function_name, false); + }); +} + +} // namespace lldb_private Index: lldb/include/lldb/Target/Thread.h =================================================================== --- lldb/include/lldb/Target/Thread.h +++ lldb/include/lldb/Target/Thread.h @@ -216,6 +216,8 @@ virtual void RefreshStateAfterStop() = 0; + void ApplyMostRelevantFrames(); + void WillStop(); bool ShouldStop(Event *event_ptr); Index: lldb/include/lldb/Target/StopInfo.h =================================================================== --- lldb/include/lldb/Target/StopInfo.h +++ lldb/include/lldb/Target/StopInfo.h @@ -127,6 +127,9 @@ static lldb::StopInfoSP CreateStopReasonWithException(Thread &thread, const char *description); + static lldb::StopInfoSP + CreateStopReasonForRecognizedFrame(Thread &thread, const char *description); + static lldb::StopInfoSP CreateStopReasonWithExec(Thread &thread); static lldb::ValueObjectSP Index: lldb/include/lldb/Target/StackFrameRecognizer.h =================================================================== --- lldb/include/lldb/Target/StackFrameRecognizer.h +++ 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 @@ virtual lldb::ValueObjectSP GetExceptionObject() { return lldb::ValueObjectSP(); } + virtual lldb::StackFrameSP GetMostRelevantFrame() { return nullptr; }; + virtual lldb::StopInfoSP GetStopInfo() { return nullptr; } virtual ~RecognizedStackFrame(){}; protected: Index: lldb/include/lldb/Target/AbortRecognizer.h =================================================================== --- /dev/null +++ lldb/include/lldb/Target/AbortRecognizer.h @@ -0,0 +1,56 @@ +//===-- AbortRecognizer.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_AbortRegognizer_h_ +#define liblldb_AbortRegognizer_h_ + +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrameRecognizer.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/FileSpec.h" + +#include <tuple> + +namespace lldb_private { + +void RegisterAbortRecognizer(Process *process); + +class AbortRecognizerHandler { +public: + ~AbortRecognizerHandler() = default; + + static llvm::Optional<std::tuple<FileSpec, ConstString>> + GetAbortLocation(Process *process_sp); + static llvm::Optional<std::tuple<FileSpec, ConstString>> + GetAssertLocation(Process *process_sp); +}; + +#pragma mark Frame recognizers + +class AbortRecognizedStackFrame : public RecognizedStackFrame { +public: + AbortRecognizedStackFrame(lldb::ThreadSP thread_sp, FileSpec module_spec, + ConstString function_name); + lldb::StackFrameSP GetMostRelevantFrame() override; + lldb::StopInfoSP GetStopInfo() override; + +private: + lldb::StackFrameSP m_most_relevant_frame; + lldb::StopInfoSP m_stop_info_sp; +}; + +class AbortFrameRecognizer : public StackFrameRecognizer { +public: + std::string GetName() override { return "Abort StackFrame Recognizer"; } + lldb::RecognizedStackFrameSP + RecognizeFrame(lldb::StackFrameSP frame) override; +}; + +} // namespace lldb_private + +#endif // liblldb_AbortRecognizer_h_
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits