xref: /llvm-project/lldb/source/Target/VerboseTrapFrameRecognizer.cpp (revision bca507387ae1945137214ec7fb80b709927ee6e8)
1 #include "lldb/Target/VerboseTrapFrameRecognizer.h"
2 
3 #include "lldb/Core/Module.h"
4 #include "lldb/Symbol/Function.h"
5 #include "lldb/Symbol/SymbolContext.h"
6 #include "lldb/Target/Process.h"
7 #include "lldb/Target/StackFrameRecognizer.h"
8 #include "lldb/Target/Target.h"
9 
10 #include "lldb/Utility/LLDBLog.h"
11 #include "lldb/Utility/Log.h"
12 
13 #include "clang/CodeGen/ModuleBuilder.h"
14 
15 using namespace llvm;
16 using namespace lldb;
17 using namespace lldb_private;
18 
19 /// The 0th frame is the artificial inline frame generated to store
20 /// the verbose_trap message. So, starting with the current parent frame,
21 /// find the first frame that's not inside of the STL.
22 static StackFrameSP FindMostRelevantFrame(Thread &selected_thread) {
23   // Defensive upper-bound of when we stop walking up the frames in
24   // case we somehow ended up looking at an infinite recursion.
25   const size_t max_stack_depth = 128;
26 
27   // Start at parent frame.
28   size_t stack_idx = 1;
29   StackFrameSP most_relevant_frame_sp =
30       selected_thread.GetStackFrameAtIndex(stack_idx);
31 
32   while (most_relevant_frame_sp && stack_idx <= max_stack_depth) {
33     auto const &sc =
34         most_relevant_frame_sp->GetSymbolContext(eSymbolContextEverything);
35     ConstString frame_name = sc.GetFunctionName();
36     if (!frame_name)
37       return nullptr;
38 
39     // Found a frame outside of the `std` namespace. That's the
40     // first frame in user-code that ended up triggering the
41     // verbose_trap. Hence that's the one we want to display.
42     if (!frame_name.GetStringRef().starts_with("std::"))
43       return most_relevant_frame_sp;
44 
45     ++stack_idx;
46     most_relevant_frame_sp = selected_thread.GetStackFrameAtIndex(stack_idx);
47   }
48 
49   return nullptr;
50 }
51 
52 VerboseTrapRecognizedStackFrame::VerboseTrapRecognizedStackFrame(
53     StackFrameSP most_relevant_frame_sp, std::string stop_desc)
54     : m_most_relevant_frame(most_relevant_frame_sp) {
55   m_stop_desc = std::move(stop_desc);
56 }
57 
58 lldb::RecognizedStackFrameSP
59 VerboseTrapFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) {
60   if (frame_sp->GetFrameIndex())
61     return {};
62 
63   ThreadSP thread_sp = frame_sp->GetThread();
64   ProcessSP process_sp = thread_sp->GetProcess();
65 
66   StackFrameSP most_relevant_frame_sp = FindMostRelevantFrame(*thread_sp);
67 
68   if (!most_relevant_frame_sp) {
69     Log *log = GetLog(LLDBLog::Unwind);
70     LLDB_LOG(
71         log,
72         "Failed to find most relevant frame: Hit unwinding bound (1 frame)!");
73     return {};
74   }
75 
76   SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
77 
78   if (!sc.block)
79     return {};
80 
81   // The runtime error is set as the function name in the inlined function info
82   // of frame #0 by the compiler
83   const InlineFunctionInfo *inline_info = nullptr;
84   Block *inline_block = sc.block->GetContainingInlinedBlock();
85 
86   if (!inline_block)
87     return {};
88 
89   inline_info = sc.block->GetInlinedFunctionInfo();
90 
91   if (!inline_info)
92     return {};
93 
94   auto func_name = inline_info->GetName().GetStringRef();
95   if (func_name.empty())
96     return {};
97 
98   static auto trap_regex =
99       llvm::Regex(llvm::formatv("^{0}\\$(.*)\\$(.*)$", ClangTrapPrefix).str());
100   SmallVector<llvm::StringRef, 3> matches;
101   std::string regex_err_msg;
102   if (!trap_regex.match(func_name, &matches, &regex_err_msg)) {
103     LLDB_LOGF(GetLog(LLDBLog::Unwind),
104               "Failed to parse match trap regex for '%s': %s", func_name.data(),
105               regex_err_msg.c_str());
106 
107     return {};
108   }
109 
110   // For `__clang_trap_msg$category$message$` we expect 3 matches:
111   // 1. entire string
112   // 2. category
113   // 3. message
114   if (matches.size() != 3) {
115     LLDB_LOGF(GetLog(LLDBLog::Unwind),
116               "Unexpected function name format. Expected '<trap prefix>$<trap "
117               "category>$<trap message>'$ but got: '%s'.",
118               func_name.data());
119 
120     return {};
121   }
122 
123   auto category = matches[1];
124   auto message = matches[2];
125 
126   std::string stop_reason =
127       category.empty() ? "<empty category>" : category.str();
128   if (!message.empty()) {
129     stop_reason += ": ";
130     stop_reason += message.str();
131   }
132 
133   return std::make_shared<VerboseTrapRecognizedStackFrame>(
134       most_relevant_frame_sp, std::move(stop_reason));
135 }
136 
137 lldb::StackFrameSP VerboseTrapRecognizedStackFrame::GetMostRelevantFrame() {
138   return m_most_relevant_frame;
139 }
140 
141 namespace lldb_private {
142 
143 void RegisterVerboseTrapFrameRecognizer(Process &process) {
144   RegularExpressionSP module_regex_sp = nullptr;
145   auto symbol_regex_sp = std::make_shared<RegularExpression>(
146       llvm::formatv("^{0}", ClangTrapPrefix).str());
147 
148   StackFrameRecognizerSP srf_recognizer_sp =
149       std::make_shared<VerboseTrapFrameRecognizer>();
150 
151   process.GetTarget().GetFrameRecognizerManager().AddRecognizer(
152       srf_recognizer_sp, module_regex_sp, symbol_regex_sp,
153       Mangled::ePreferDemangled, false);
154 }
155 
156 } // namespace lldb_private
157