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