1 #include "lldb/Core/Module.h" 2 #include "lldb/Symbol/Function.h" 3 #include "lldb/Symbol/SymbolContext.h" 4 #include "lldb/Target/Process.h" 5 #include "lldb/Target/StackFrameList.h" 6 #include "lldb/Target/Target.h" 7 #include "lldb/Target/Thread.h" 8 9 #include "lldb/Utility/Log.h" 10 #include "lldb/Utility/Logging.h" 11 12 #include "lldb/Target/AssertFrameRecognizer.h" 13 14 using namespace llvm; 15 using namespace lldb; 16 using namespace lldb_private; 17 18 namespace lldb_private { 19 /// Checkes if the module containing a symbol has debug info. 20 /// 21 /// \param[in] target 22 /// The target containing the module. 23 /// \param[in] module_spec 24 /// The module spec that should contain the symbol. 25 /// \param[in] symbol_name 26 /// The symbol's name that should be contained in the debug info. 27 /// \return 28 /// If \b true the symbol was found, \b false otherwise. 29 bool ModuleHasDebugInfo(Target &target, FileSpec &module_spec, 30 StringRef symbol_name) { 31 ModuleSP module_sp = target.GetImages().FindFirstModule(module_spec); 32 33 if (!module_sp) 34 return false; 35 36 return module_sp->FindFirstSymbolWithNameAndType(ConstString(symbol_name)); 37 } 38 39 /// Fetches the abort frame location depending on the current platform. 40 /// 41 /// \param[in] process_sp 42 /// The process that is currently aborting. This will give us information on 43 /// the target and the platform. 44 /// \return 45 /// If the platform is supported, returns an optional tuple containing 46 /// the abort module as a \a FileSpec and the symbol name as a \a StringRef. 47 /// Otherwise, returns \a llvm::None. 48 llvm::Optional<std::tuple<FileSpec, StringRef>> 49 GetAbortLocation(Process *process) { 50 Target &target = process->GetTarget(); 51 52 FileSpec module_spec; 53 StringRef symbol_name; 54 55 switch (target.GetArchitecture().GetTriple().getOS()) { 56 case llvm::Triple::Darwin: 57 case llvm::Triple::MacOSX: 58 module_spec = FileSpec("libsystem_kernel.dylib"); 59 symbol_name = "__pthread_kill"; 60 break; 61 case llvm::Triple::Linux: 62 module_spec = FileSpec("libc.so.6"); 63 symbol_name = "__GI_raise"; 64 if (!ModuleHasDebugInfo(target, module_spec, symbol_name)) 65 symbol_name = "raise"; 66 break; 67 default: 68 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); 69 LLDB_LOG(log, "AssertFrameRecognizer::GetAbortLocation Unsupported OS"); 70 return llvm::None; 71 } 72 73 return std::make_tuple(module_spec, symbol_name); 74 } 75 76 /// Fetches the assert frame location depending on the current platform. 77 /// 78 /// \param[in] process_sp 79 /// The process that is currently asserting. This will give us information on 80 /// the target and the platform. 81 /// \return 82 /// If the platform is supported, returns an optional tuple containing 83 /// the asserting frame module as a \a FileSpec and the symbol name as a \a 84 /// StringRef. 85 /// Otherwise, returns \a llvm::None. 86 llvm::Optional<std::tuple<FileSpec, StringRef>> 87 GetAssertLocation(Process *process) { 88 Target &target = process->GetTarget(); 89 90 FileSpec module_spec; 91 StringRef symbol_name; 92 93 switch (target.GetArchitecture().GetTriple().getOS()) { 94 case llvm::Triple::Darwin: 95 case llvm::Triple::MacOSX: 96 module_spec = FileSpec("libsystem_c.dylib"); 97 symbol_name = "__assert_rtn"; 98 break; 99 case llvm::Triple::Linux: 100 module_spec = FileSpec("libc.so.6"); 101 symbol_name = "__GI___assert_fail"; 102 if (!ModuleHasDebugInfo(target, module_spec, symbol_name)) 103 symbol_name = "__assert_fail"; 104 break; 105 default: 106 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); 107 LLDB_LOG(log, "AssertFrameRecognizer::GetAssertLocation Unsupported OS"); 108 return llvm::None; 109 } 110 111 return std::make_tuple(module_spec, symbol_name); 112 } 113 114 void RegisterAssertFrameRecognizer(Process *process) { 115 static llvm::once_flag g_once_flag; 116 llvm::call_once(g_once_flag, [process]() { 117 auto abort_location = GetAbortLocation(process); 118 119 if (!abort_location.hasValue()) 120 return; 121 122 FileSpec module_spec; 123 StringRef function_name; 124 std::tie(module_spec, function_name) = *abort_location; 125 126 StackFrameRecognizerManager::AddRecognizer( 127 StackFrameRecognizerSP(new AssertFrameRecognizer()), 128 module_spec.GetFilename(), ConstString(function_name), false); 129 }); 130 } 131 132 } // namespace lldb_private 133 134 lldb::RecognizedStackFrameSP 135 AssertFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) { 136 ThreadSP thread_sp = frame_sp->GetThread(); 137 ProcessSP process_sp = thread_sp->GetProcess(); 138 139 auto assert_location = GetAssertLocation(process_sp.get()); 140 141 if (!assert_location.hasValue()) 142 return RecognizedStackFrameSP(); 143 144 FileSpec module_spec; 145 StringRef function_name; 146 std::tie(module_spec, function_name) = *assert_location; 147 148 const uint32_t frames_to_fetch = 5; 149 const uint32_t last_frame_index = frames_to_fetch - 1; 150 StackFrameSP prev_frame_sp = nullptr; 151 152 // Fetch most relevant frame 153 for (uint32_t frame_index = 0; frame_index < frames_to_fetch; frame_index++) { 154 prev_frame_sp = thread_sp->GetStackFrameAtIndex(frame_index); 155 156 if (!prev_frame_sp) { 157 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); 158 LLDB_LOG(log, "Abort Recognizer: Hit unwinding bound ({1} frames)!", 159 frames_to_fetch); 160 break; 161 } 162 163 SymbolContext sym_ctx = 164 prev_frame_sp->GetSymbolContext(eSymbolContextEverything); 165 166 if (sym_ctx.module_sp->GetFileSpec().FileEquals(module_spec) && 167 sym_ctx.GetFunctionName() == ConstString(function_name)) { 168 169 // We go a frame beyond the assert location because the most relevant 170 // frame for the user is the one in which the assert function was called. 171 // If the assert location is the last frame fetched, then it is set as 172 // the most relevant frame. 173 174 StackFrameSP most_relevant_frame_sp = thread_sp->GetStackFrameAtIndex( 175 std::min(frame_index + 1, last_frame_index)); 176 177 // Pass assert location to AbortRecognizedStackFrame to set as most 178 // relevant frame. 179 return lldb::RecognizedStackFrameSP( 180 new AssertRecognizedStackFrame(most_relevant_frame_sp)); 181 } 182 } 183 184 return RecognizedStackFrameSP(); 185 }; 186 187 AssertRecognizedStackFrame::AssertRecognizedStackFrame( 188 StackFrameSP most_relevant_frame_sp) 189 : m_most_relevant_frame(most_relevant_frame_sp) { 190 m_stop_desc = "hit program assert"; 191 } 192 193 lldb::StackFrameSP AssertRecognizedStackFrame::GetMostRelevantFrame() { 194 return m_most_relevant_frame; 195 } 196