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
20std::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
171static 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
189StackTrace::StackTrace()
190{
191 tryCapture();
192}
193
194StackTrace::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
220StackTrace::StackTrace(NoCapture)
221{
222}
223
224void StackTrace::tryCapture()
225{
226 size = 0;
227#if USE_UNWIND
228 size = unw_backtrace(frames.data(), capacity);
229#endif
230}
231
232size_t StackTrace::getSize() const
233{
234 return size;
235}
236
237size_t StackTrace::getOffset() const
238{
239 return offset;
240}
241
242const StackTrace::Frames & StackTrace::getFrames() const
243{
244 return frames;
245}
246
247
248static 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
311static 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
318void StackTrace::toStringEveryLine(std::function<void(const std::string &)> callback) const
319{
320 toStringEveryLineImpl(frames, offset, size, std::move(callback));
321}
322
323std::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