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, ®ex_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