| 1 | #include "lldb/Target/AssertFrameRecognizer.h" |
| 2 | #include "lldb/Core/Module.h" |
| 3 | #include "lldb/Symbol/Function.h" |
| 4 | #include "lldb/Symbol/SymbolContext.h" |
| 5 | #include "lldb/Symbol/SymbolLocation.h" |
| 6 | #include "lldb/Target/Process.h" |
| 7 | #include "lldb/Target/StackFrameList.h" |
| 8 | #include "lldb/Target/Target.h" |
| 9 | #include "lldb/Target/Thread.h" |
| 10 | #include "lldb/Utility/LLDBLog.h" |
| 11 | |
| 12 | using namespace llvm; |
| 13 | using namespace lldb; |
| 14 | using namespace lldb_private; |
| 15 | |
| 16 | namespace lldb_private { |
| 17 | /// Fetches the abort frame location depending on the current platform. |
| 18 | /// |
| 19 | /// \param[in] os |
| 20 | /// The target's os type. |
| 21 | /// \param[in,out] location |
| 22 | /// The struct that will contain the abort module spec and symbol names. |
| 23 | /// \return |
| 24 | /// \b true, if the platform is supported |
| 25 | /// \b false, otherwise. |
| 26 | bool GetAbortLocation(llvm::Triple::OSType os, SymbolLocation &location) { |
| 27 | switch (os) { |
| 28 | case llvm::Triple::Darwin: |
| 29 | case llvm::Triple::MacOSX: |
| 30 | location.module_spec = FileSpec("libsystem_kernel.dylib" ); |
| 31 | location.symbols.push_back(x: ConstString("__pthread_kill" )); |
| 32 | break; |
| 33 | case llvm::Triple::Linux: |
| 34 | location.module_spec = FileSpec("libc.so.6" ); |
| 35 | location.symbols.push_back(x: ConstString("raise" )); |
| 36 | location.symbols.push_back(x: ConstString("__GI_raise" )); |
| 37 | location.symbols.push_back(x: ConstString("gsignal" )); |
| 38 | location.symbols.push_back(x: ConstString("pthread_kill" )); |
| 39 | location.symbols_are_regex = true; |
| 40 | break; |
| 41 | default: |
| 42 | Log *log = GetLog(mask: LLDBLog::Unwind); |
| 43 | LLDB_LOG(log, "AssertFrameRecognizer::GetAbortLocation Unsupported OS" ); |
| 44 | return false; |
| 45 | } |
| 46 | |
| 47 | return true; |
| 48 | } |
| 49 | |
| 50 | /// Fetches the assert frame location depending on the current platform. |
| 51 | /// |
| 52 | /// \param[in] os |
| 53 | /// The target's os type. |
| 54 | /// \param[in,out] location |
| 55 | /// The struct that will contain the assert module spec and symbol names. |
| 56 | /// \return |
| 57 | /// \b true, if the platform is supported |
| 58 | /// \b false, otherwise. |
| 59 | bool GetAssertLocation(llvm::Triple::OSType os, SymbolLocation &location) { |
| 60 | switch (os) { |
| 61 | case llvm::Triple::Darwin: |
| 62 | case llvm::Triple::MacOSX: |
| 63 | location.module_spec = FileSpec("libsystem_c.dylib" ); |
| 64 | location.symbols.push_back(x: ConstString("__assert_rtn" )); |
| 65 | break; |
| 66 | case llvm::Triple::Linux: |
| 67 | location.module_spec = FileSpec("libc.so.6" ); |
| 68 | location.symbols.push_back(x: ConstString("__assert_fail" )); |
| 69 | location.symbols.push_back(x: ConstString("__GI___assert_fail" )); |
| 70 | break; |
| 71 | default: |
| 72 | Log *log = GetLog(mask: LLDBLog::Unwind); |
| 73 | LLDB_LOG(log, "AssertFrameRecognizer::GetAssertLocation Unsupported OS" ); |
| 74 | return false; |
| 75 | } |
| 76 | |
| 77 | return true; |
| 78 | } |
| 79 | |
| 80 | void RegisterAssertFrameRecognizer(Process *process) { |
| 81 | Target &target = process->GetTarget(); |
| 82 | llvm::Triple::OSType os = target.GetArchitecture().GetTriple().getOS(); |
| 83 | SymbolLocation location; |
| 84 | |
| 85 | if (!GetAbortLocation(os, location)) |
| 86 | return; |
| 87 | |
| 88 | if (!location.symbols_are_regex) { |
| 89 | target.GetFrameRecognizerManager().AddRecognizer( |
| 90 | recognizer: std::make_shared<AssertFrameRecognizer>(), |
| 91 | module: location.module_spec.GetFilename(), symbols: location.symbols, |
| 92 | symbol_mangling: Mangled::ePreferDemangled, |
| 93 | /*first_instruction_only*/ false); |
| 94 | return; |
| 95 | } |
| 96 | std::string module_re = "^" ; |
| 97 | for (char c : location.module_spec.GetFilename().GetStringRef()) { |
| 98 | if (c == '.') |
| 99 | module_re += '\\'; |
| 100 | module_re += c; |
| 101 | } |
| 102 | module_re += '$'; |
| 103 | std::string symbol_re = "^(" ; |
| 104 | for (auto it = location.symbols.cbegin(); it != location.symbols.cend(); |
| 105 | ++it) { |
| 106 | if (it != location.symbols.cbegin()) |
| 107 | symbol_re += '|'; |
| 108 | symbol_re += it->GetStringRef(); |
| 109 | } |
| 110 | // Strip the trailing @VER symbol version. |
| 111 | symbol_re += ")(@.*)?$" ; |
| 112 | target.GetFrameRecognizerManager().AddRecognizer( |
| 113 | recognizer: std::make_shared<AssertFrameRecognizer>(), |
| 114 | module: std::make_shared<RegularExpression>(args: std::move(module_re)), |
| 115 | symbol: std::make_shared<RegularExpression>(args: std::move(symbol_re)), |
| 116 | symbol_mangling: Mangled::ePreferDemangled, |
| 117 | /*first_instruction_only*/ false); |
| 118 | } |
| 119 | |
| 120 | } // namespace lldb_private |
| 121 | |
| 122 | lldb::RecognizedStackFrameSP |
| 123 | AssertFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) { |
| 124 | ThreadSP thread_sp = frame_sp->GetThread(); |
| 125 | ProcessSP process_sp = thread_sp->GetProcess(); |
| 126 | Target &target = process_sp->GetTarget(); |
| 127 | llvm::Triple::OSType os = target.GetArchitecture().GetTriple().getOS(); |
| 128 | SymbolLocation location; |
| 129 | |
| 130 | if (!GetAssertLocation(os, location)) |
| 131 | return RecognizedStackFrameSP(); |
| 132 | |
| 133 | const uint32_t frames_to_fetch = 6; |
| 134 | const uint32_t last_frame_index = frames_to_fetch - 1; |
| 135 | StackFrameSP prev_frame_sp = nullptr; |
| 136 | |
| 137 | // Fetch most relevant frame |
| 138 | for (uint32_t frame_index = 0; frame_index < frames_to_fetch; frame_index++) { |
| 139 | prev_frame_sp = thread_sp->GetStackFrameAtIndex(idx: frame_index); |
| 140 | |
| 141 | if (!prev_frame_sp) { |
| 142 | Log *log = GetLog(mask: LLDBLog::Unwind); |
| 143 | LLDB_LOG(log, "Abort Recognizer: Hit unwinding bound ({1} frames)!" , |
| 144 | frames_to_fetch); |
| 145 | break; |
| 146 | } |
| 147 | |
| 148 | SymbolContext sym_ctx = |
| 149 | prev_frame_sp->GetSymbolContext(resolve_scope: eSymbolContextEverything); |
| 150 | |
| 151 | if (!sym_ctx.module_sp || |
| 152 | !sym_ctx.module_sp->GetFileSpec().FileEquals(other: location.module_spec)) |
| 153 | continue; |
| 154 | |
| 155 | ConstString func_name = sym_ctx.GetFunctionName(); |
| 156 | |
| 157 | if (llvm::is_contained(Range&: location.symbols, Element: func_name)) { |
| 158 | // We go a frame beyond the assert location because the most relevant |
| 159 | // frame for the user is the one in which the assert function was called. |
| 160 | // If the assert location is the last frame fetched, then it is set as |
| 161 | // the most relevant frame. |
| 162 | |
| 163 | StackFrameSP most_relevant_frame_sp = thread_sp->GetStackFrameAtIndex( |
| 164 | idx: std::min(a: frame_index + 1, b: last_frame_index)); |
| 165 | |
| 166 | // Pass assert location to AbortRecognizedStackFrame to set as most |
| 167 | // relevant frame. |
| 168 | return lldb::RecognizedStackFrameSP( |
| 169 | new AssertRecognizedStackFrame(most_relevant_frame_sp)); |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | return RecognizedStackFrameSP(); |
| 174 | } |
| 175 | |
| 176 | AssertRecognizedStackFrame::AssertRecognizedStackFrame( |
| 177 | StackFrameSP most_relevant_frame_sp) |
| 178 | : m_most_relevant_frame(most_relevant_frame_sp) { |
| 179 | m_stop_desc = "hit program assert" ; |
| 180 | } |
| 181 | |
| 182 | lldb::StackFrameSP AssertRecognizedStackFrame::GetMostRelevantFrame() { |
| 183 | return m_most_relevant_frame; |
| 184 | } |
| 185 | |