1 | #include <Common/StackTrace.h> |
2 | |
3 | #include <Common/Dwarf.h> |
4 | #include <Common/Elf.h> |
5 | #include <Common/SymbolIndex.h> |
6 | #include <Common/config.h> |
7 | #include <common/SimpleCache.h> |
8 | #include <common/demangle.h> |
9 | #include <Core/Defines.h> |
10 | |
11 | #include <cstring> |
12 | #include <filesystem> |
13 | #include <sstream> |
14 | #include <unordered_map> |
15 | |
16 | #if USE_UNWIND |
17 | # include <libunwind.h> |
18 | #endif |
19 | |
20 | std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext_t & context) |
21 | { |
22 | std::stringstream error; |
23 | switch (sig) |
24 | { |
25 | case SIGSEGV: |
26 | { |
27 | /// Print info about address and reason. |
28 | if (nullptr == info.si_addr) |
29 | error << "Address: NULL pointer." ; |
30 | else |
31 | error << "Address: " << info.si_addr; |
32 | |
33 | #if defined(__x86_64__) && !defined(__FreeBSD__) && !defined(__APPLE__) && !defined(__arm__) |
34 | auto err_mask = context.uc_mcontext.gregs[REG_ERR]; |
35 | if ((err_mask & 0x02)) |
36 | error << " Access: write." ; |
37 | else |
38 | error << " Access: read." ; |
39 | #else |
40 | UNUSED(context); |
41 | #endif |
42 | |
43 | switch (info.si_code) |
44 | { |
45 | case SEGV_ACCERR: |
46 | error << " Attempted access has violated the permissions assigned to the memory area." ; |
47 | break; |
48 | case SEGV_MAPERR: |
49 | error << " Address not mapped to object." ; |
50 | break; |
51 | default: |
52 | error << " Unknown si_code." ; |
53 | break; |
54 | } |
55 | break; |
56 | } |
57 | |
58 | case SIGBUS: |
59 | { |
60 | switch (info.si_code) |
61 | { |
62 | case BUS_ADRALN: |
63 | error << "Invalid address alignment." ; |
64 | break; |
65 | case BUS_ADRERR: |
66 | error << "Non-existant physical address." ; |
67 | break; |
68 | case BUS_OBJERR: |
69 | error << "Object specific hardware error." ; |
70 | break; |
71 | |
72 | // Linux specific |
73 | #if defined(BUS_MCEERR_AR) |
74 | case BUS_MCEERR_AR: |
75 | error << "Hardware memory error: action required." ; |
76 | break; |
77 | #endif |
78 | #if defined(BUS_MCEERR_AO) |
79 | case BUS_MCEERR_AO: |
80 | error << "Hardware memory error: action optional." ; |
81 | break; |
82 | #endif |
83 | |
84 | default: |
85 | error << "Unknown si_code." ; |
86 | break; |
87 | } |
88 | break; |
89 | } |
90 | |
91 | case SIGILL: |
92 | { |
93 | switch (info.si_code) |
94 | { |
95 | case ILL_ILLOPC: |
96 | error << "Illegal opcode." ; |
97 | break; |
98 | case ILL_ILLOPN: |
99 | error << "Illegal operand." ; |
100 | break; |
101 | case ILL_ILLADR: |
102 | error << "Illegal addressing mode." ; |
103 | break; |
104 | case ILL_ILLTRP: |
105 | error << "Illegal trap." ; |
106 | break; |
107 | case ILL_PRVOPC: |
108 | error << "Privileged opcode." ; |
109 | break; |
110 | case ILL_PRVREG: |
111 | error << "Privileged register." ; |
112 | break; |
113 | case ILL_COPROC: |
114 | error << "Coprocessor error." ; |
115 | break; |
116 | case ILL_BADSTK: |
117 | error << "Internal stack error." ; |
118 | break; |
119 | default: |
120 | error << "Unknown si_code." ; |
121 | break; |
122 | } |
123 | break; |
124 | } |
125 | |
126 | case SIGFPE: |
127 | { |
128 | switch (info.si_code) |
129 | { |
130 | case FPE_INTDIV: |
131 | error << "Integer divide by zero." ; |
132 | break; |
133 | case FPE_INTOVF: |
134 | error << "Integer overflow." ; |
135 | break; |
136 | case FPE_FLTDIV: |
137 | error << "Floating point divide by zero." ; |
138 | break; |
139 | case FPE_FLTOVF: |
140 | error << "Floating point overflow." ; |
141 | break; |
142 | case FPE_FLTUND: |
143 | error << "Floating point underflow." ; |
144 | break; |
145 | case FPE_FLTRES: |
146 | error << "Floating point inexact result." ; |
147 | break; |
148 | case FPE_FLTINV: |
149 | error << "Floating point invalid operation." ; |
150 | break; |
151 | case FPE_FLTSUB: |
152 | error << "Subscript out of range." ; |
153 | break; |
154 | default: |
155 | error << "Unknown si_code." ; |
156 | break; |
157 | } |
158 | break; |
159 | } |
160 | |
161 | case SIGTSTP: |
162 | { |
163 | error << "This is a signal used for debugging purposes by the user." ; |
164 | break; |
165 | } |
166 | } |
167 | |
168 | return error.str(); |
169 | } |
170 | |
171 | static void * getCallerAddress(const ucontext_t & context) |
172 | { |
173 | #if defined(__x86_64__) |
174 | /// Get the address at the time the signal was raised from the RIP (x86-64) |
175 | #if defined(__FreeBSD__) |
176 | return reinterpret_cast<void *>(context.uc_mcontext.mc_rip); |
177 | #elif defined(__APPLE__) |
178 | return reinterpret_cast<void *>(context.uc_mcontext->__ss.__rip); |
179 | #else |
180 | return reinterpret_cast<void *>(context.uc_mcontext.gregs[REG_RIP]); |
181 | #endif |
182 | #elif defined(__aarch64__) |
183 | return reinterpret_cast<void *>(context.uc_mcontext.pc); |
184 | #else |
185 | return nullptr; |
186 | #endif |
187 | } |
188 | |
189 | StackTrace::StackTrace() |
190 | { |
191 | tryCapture(); |
192 | } |
193 | |
194 | StackTrace::StackTrace(const ucontext_t & signal_context) |
195 | { |
196 | tryCapture(); |
197 | |
198 | void * caller_address = getCallerAddress(signal_context); |
199 | |
200 | if (size == 0 && caller_address) |
201 | { |
202 | frames[0] = caller_address; |
203 | size = 1; |
204 | } |
205 | else |
206 | { |
207 | /// Skip excessive stack frames that we have created while finding stack trace. |
208 | |
209 | for (size_t i = 0; i < size; ++i) |
210 | { |
211 | if (frames[i] == caller_address) |
212 | { |
213 | offset = i; |
214 | break; |
215 | } |
216 | } |
217 | } |
218 | } |
219 | |
220 | StackTrace::StackTrace(NoCapture) |
221 | { |
222 | } |
223 | |
224 | void StackTrace::tryCapture() |
225 | { |
226 | size = 0; |
227 | #if USE_UNWIND |
228 | size = unw_backtrace(frames.data(), capacity); |
229 | #endif |
230 | } |
231 | |
232 | size_t StackTrace::getSize() const |
233 | { |
234 | return size; |
235 | } |
236 | |
237 | size_t StackTrace::getOffset() const |
238 | { |
239 | return offset; |
240 | } |
241 | |
242 | const StackTrace::Frames & StackTrace::getFrames() const |
243 | { |
244 | return frames; |
245 | } |
246 | |
247 | |
248 | static void toStringEveryLineImpl(const StackTrace::Frames & frames, size_t offset, size_t size, std::function<void(const std::string &)> callback) |
249 | { |
250 | if (size == 0) |
251 | return callback("<Empty trace>" ); |
252 | |
253 | #if defined(__ELF__) && !defined(__FreeBSD__) |
254 | const DB::SymbolIndex & symbol_index = DB::SymbolIndex::instance(); |
255 | std::unordered_map<std::string, DB::Dwarf> dwarfs; |
256 | |
257 | std::stringstream out; |
258 | |
259 | for (size_t i = offset; i < size; ++i) |
260 | { |
261 | const void * virtual_addr = frames[i]; |
262 | auto object = symbol_index.findObject(virtual_addr); |
263 | uintptr_t virtual_offset = object ? uintptr_t(object->address_begin) : 0; |
264 | const void * physical_addr = reinterpret_cast<const void *>(uintptr_t(virtual_addr) - virtual_offset); |
265 | |
266 | out << i << ". " << physical_addr << " " ; |
267 | |
268 | auto symbol = symbol_index.findSymbol(virtual_addr); |
269 | if (symbol) |
270 | { |
271 | int status = 0; |
272 | out << demangle(symbol->name, status); |
273 | } |
274 | else |
275 | out << "?" ; |
276 | |
277 | out << " " ; |
278 | |
279 | if (object) |
280 | { |
281 | if (std::filesystem::exists(object->name)) |
282 | { |
283 | auto dwarf_it = dwarfs.try_emplace(object->name, *object->elf).first; |
284 | |
285 | DB::Dwarf::LocationInfo location; |
286 | if (dwarf_it->second.findAddress(uintptr_t(physical_addr), location, DB::Dwarf::LocationInfoMode::FAST)) |
287 | out << location.file.toString() << ":" << location.line; |
288 | } |
289 | out << " in " << object->name; |
290 | } |
291 | else |
292 | out << "?" ; |
293 | |
294 | callback(out.str()); |
295 | out.str({}); |
296 | } |
297 | #else |
298 | std::stringstream out; |
299 | |
300 | for (size_t i = offset; i < size; ++i) |
301 | { |
302 | const void * addr = frames[i]; |
303 | out << i << ". " << addr; |
304 | |
305 | callback(out.str()); |
306 | out.str({}); |
307 | } |
308 | #endif |
309 | } |
310 | |
311 | static std::string toStringImpl(const StackTrace::Frames & frames, size_t offset, size_t size) |
312 | { |
313 | std::stringstream out; |
314 | toStringEveryLineImpl(frames, offset, size, [&](const std::string & str) { out << str << '\n'; }); |
315 | return out.str(); |
316 | } |
317 | |
318 | void StackTrace::toStringEveryLine(std::function<void(const std::string &)> callback) const |
319 | { |
320 | toStringEveryLineImpl(frames, offset, size, std::move(callback)); |
321 | } |
322 | |
323 | std::string StackTrace::toString() const |
324 | { |
325 | /// Calculation of stack trace text is extremely slow. |
326 | /// We use simple cache because otherwise the server could be overloaded by trash queries. |
327 | |
328 | static SimpleCache<decltype(toStringImpl), &toStringImpl> func_cached; |
329 | return func_cached(frames, offset, size); |
330 | } |
331 | |