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