diff options
Diffstat (limited to 'lldb/source/Plugins/LanguageRuntime')
4 files changed, 209 insertions, 1 deletions
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt index 1717b0a..a27bcef 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt @@ -1,5 +1,6 @@ add_lldb_library(lldbPluginCPPRuntime CPPLanguageRuntime.cpp + VerboseTrapFrameRecognizer.cpp LINK_LIBS lldbCore diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp index 21a5ebe..913678b 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -12,6 +12,7 @@ #include <memory> #include "CPPLanguageRuntime.h" +#include "VerboseTrapFrameRecognizer.h" #include "llvm/ADT/StringRef.h" @@ -107,12 +108,15 @@ public: CPPLanguageRuntime::CPPLanguageRuntime(Process *process) : LanguageRuntime(process) { - if (process) + if (process) { process->GetTarget().GetFrameRecognizerManager().AddRecognizer( StackFrameRecognizerSP(new LibCXXFrameRecognizer()), {}, std::make_shared<RegularExpression>("^std::__[^:]*::"), /*mangling_preference=*/Mangled::ePreferDemangledWithoutArguments, /*first_instruction_only=*/false); + + RegisterVerboseTrapFrameRecognizer(*process); + } } bool CPPLanguageRuntime::IsAllowedRuntimeValue(ConstString name) { diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/VerboseTrapFrameRecognizer.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/VerboseTrapFrameRecognizer.cpp new file mode 100644 index 0000000..730aba5 --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/VerboseTrapFrameRecognizer.cpp @@ -0,0 +1,156 @@ +#include "VerboseTrapFrameRecognizer.h" + +#include "lldb/Core/Module.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrameRecognizer.h" +#include "lldb/Target/Target.h" + +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +#include "clang/CodeGen/ModuleBuilder.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +/// The 0th frame is the artificial inline frame generated to store +/// the verbose_trap message. So, starting with the current parent frame, +/// find the first frame that's not inside of the STL. +static StackFrameSP FindMostRelevantFrame(Thread &selected_thread) { + // Defensive upper-bound of when we stop walking up the frames in + // case we somehow ended up looking at an infinite recursion. + const size_t max_stack_depth = 128; + + // Start at parent frame. + size_t stack_idx = 1; + StackFrameSP most_relevant_frame_sp = + selected_thread.GetStackFrameAtIndex(stack_idx); + + while (most_relevant_frame_sp && stack_idx <= max_stack_depth) { + auto const &sc = + most_relevant_frame_sp->GetSymbolContext(eSymbolContextEverything); + ConstString frame_name = sc.GetFunctionName(); + if (!frame_name) + return nullptr; + + // Found a frame outside of the `std` namespace. That's the + // first frame in user-code that ended up triggering the + // verbose_trap. Hence that's the one we want to display. + if (!frame_name.GetStringRef().starts_with("std::")) + return most_relevant_frame_sp; + + ++stack_idx; + most_relevant_frame_sp = selected_thread.GetStackFrameAtIndex(stack_idx); + } + + return nullptr; +} + +VerboseTrapRecognizedStackFrame::VerboseTrapRecognizedStackFrame( + StackFrameSP most_relevant_frame_sp, std::string stop_desc) + : m_most_relevant_frame(most_relevant_frame_sp) { + m_stop_desc = std::move(stop_desc); +} + +lldb::RecognizedStackFrameSP +VerboseTrapFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) { + if (frame_sp->GetFrameIndex()) + return {}; + + ThreadSP thread_sp = frame_sp->GetThread(); + ProcessSP process_sp = thread_sp->GetProcess(); + + StackFrameSP most_relevant_frame_sp = FindMostRelevantFrame(*thread_sp); + + if (!most_relevant_frame_sp) { + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG( + log, + "Failed to find most relevant frame: Hit unwinding bound (1 frame)!"); + return {}; + } + + SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextEverything); + + if (!sc.block) + return {}; + + // The runtime error is set as the function name in the inlined function info + // of frame #0 by the compiler + const InlineFunctionInfo *inline_info = nullptr; + Block *inline_block = sc.block->GetContainingInlinedBlock(); + + if (!inline_block) + return {}; + + inline_info = sc.block->GetInlinedFunctionInfo(); + + if (!inline_info) + return {}; + + auto func_name = inline_info->GetName().GetStringRef(); + if (func_name.empty()) + return {}; + + static auto trap_regex = + llvm::Regex(llvm::formatv("^{0}\\$(.*)\\$(.*)$", ClangTrapPrefix).str()); + SmallVector<llvm::StringRef, 3> matches; + std::string regex_err_msg; + if (!trap_regex.match(func_name, &matches, ®ex_err_msg)) { + LLDB_LOGF(GetLog(LLDBLog::Unwind), + "Failed to parse match trap regex for '%s': %s", func_name.data(), + regex_err_msg.c_str()); + + return {}; + } + + // For `__clang_trap_msg$category$message$` we expect 3 matches: + // 1. entire string + // 2. category + // 3. message + if (matches.size() != 3) { + LLDB_LOGF(GetLog(LLDBLog::Unwind), + "Unexpected function name format. Expected '<trap prefix>$<trap " + "category>$<trap message>'$ but got: '%s'.", + func_name.data()); + + return {}; + } + + auto category = matches[1]; + auto message = matches[2]; + + std::string stop_reason = + category.empty() ? "<empty category>" : category.str(); + if (!message.empty()) { + stop_reason += ": "; + stop_reason += message.str(); + } + + return std::make_shared<VerboseTrapRecognizedStackFrame>( + most_relevant_frame_sp, std::move(stop_reason)); +} + +lldb::StackFrameSP VerboseTrapRecognizedStackFrame::GetMostRelevantFrame() { + return m_most_relevant_frame; +} + +namespace lldb_private { + +void RegisterVerboseTrapFrameRecognizer(Process &process) { + RegularExpressionSP module_regex_sp = nullptr; + auto symbol_regex_sp = std::make_shared<RegularExpression>( + llvm::formatv("^{0}", ClangTrapPrefix).str()); + + StackFrameRecognizerSP srf_recognizer_sp = + std::make_shared<VerboseTrapFrameRecognizer>(); + + process.GetTarget().GetFrameRecognizerManager().AddRecognizer( + srf_recognizer_sp, module_regex_sp, symbol_regex_sp, + Mangled::ePreferDemangled, false); +} + +} // namespace lldb_private diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/VerboseTrapFrameRecognizer.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/VerboseTrapFrameRecognizer.h new file mode 100644 index 0000000..7d7020f --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/VerboseTrapFrameRecognizer.h @@ -0,0 +1,47 @@ +//===-- VerboseTrapFrameRecognizer.h --------------------------------------===// +// +// 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 LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_C_PLUS_PLUS_VERBOSETRAPFRAMERECOGNIZER_H +#define LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_C_PLUS_PLUS_VERBOSETRAPFRAMERECOGNIZER_H + +#include "lldb/Target/StackFrameRecognizer.h" + +namespace lldb_private { + +void RegisterVerboseTrapFrameRecognizer(Process &process); + +/// Holds the stack frame that caused the Verbose trap and the inlined stop +/// reason message. +class VerboseTrapRecognizedStackFrame : public RecognizedStackFrame { +public: + VerboseTrapRecognizedStackFrame(lldb::StackFrameSP most_relevant_frame_sp, + std::string stop_desc); + + lldb::StackFrameSP GetMostRelevantFrame() override; + +private: + lldb::StackFrameSP m_most_relevant_frame; +}; + +/// When a thread stops, it checks the current frame contains a +/// Verbose Trap diagnostic. If so, it returns a \a +/// VerboseTrapRecognizedStackFrame holding the diagnostic a stop reason +/// description with and the parent frame as the most relavant frame. +class VerboseTrapFrameRecognizer : public StackFrameRecognizer { +public: + std::string GetName() override { + return "Verbose Trap StackFrame Recognizer"; + } + + lldb::RecognizedStackFrameSP + RecognizeFrame(lldb::StackFrameSP frame) override; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_C_PLUS_PLUS_VERBOSETRAPFRAMERECOGNIZER_H |
