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 | |