#include #include #include #include #include #include #include #include #include #include std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext_t & context) { std::stringstream error; switch (sig) { case SIGSEGV: { /// Print info about address and reason. if (nullptr == info.si_addr) error << "Address: NULL pointer."; else error << "Address: " << info.si_addr; #if defined(__x86_64__) && !defined(__FreeBSD__) && !defined(__APPLE__) auto err_mask = context.uc_mcontext.gregs[REG_ERR]; if ((err_mask & 0x02)) error << " Access: write."; else error << " Access: read."; #endif switch (info.si_code) { case SEGV_ACCERR: error << " Attempted access has violated the permissions assigned to the memory area."; break; case SEGV_MAPERR: error << " Address not mapped to object."; break; default: error << " Unknown si_code."; break; } break; } case SIGBUS: { switch (info.si_code) { case BUS_ADRALN: error << "Invalid address alignment."; break; case BUS_ADRERR: error << "Non-existant physical address."; break; case BUS_OBJERR: error << "Object specific hardware error."; break; // Linux specific #if defined(BUS_MCEERR_AR) case BUS_MCEERR_AR: error << "Hardware memory error: action required."; break; #endif #if defined(BUS_MCEERR_AO) case BUS_MCEERR_AO: error << "Hardware memory error: action optional."; break; #endif default: error << "Unknown si_code."; break; } break; } case SIGILL: { switch (info.si_code) { case ILL_ILLOPC: error << "Illegal opcode."; break; case ILL_ILLOPN: error << "Illegal operand."; break; case ILL_ILLADR: error << "Illegal addressing mode."; break; case ILL_ILLTRP: error << "Illegal trap."; break; case ILL_PRVOPC: error << "Privileged opcode."; break; case ILL_PRVREG: error << "Privileged register."; break; case ILL_COPROC: error << "Coprocessor error."; break; case ILL_BADSTK: error << "Internal stack error."; break; default: error << "Unknown si_code."; break; } break; } case SIGFPE: { switch (info.si_code) { case FPE_INTDIV: error << "Integer divide by zero."; break; case FPE_INTOVF: error << "Integer overflow."; break; case FPE_FLTDIV: error << "Floating point divide by zero."; break; case FPE_FLTOVF: error << "Floating point overflow."; break; case FPE_FLTUND: error << "Floating point underflow."; break; case FPE_FLTRES: error << "Floating point inexact result."; break; case FPE_FLTINV: error << "Floating point invalid operation."; break; case FPE_FLTSUB: error << "Subscript out of range."; break; default: error << "Unknown si_code."; break; } break; } } return error.str(); } static void * getCallerAddress(const ucontext_t & context) { #if defined(__x86_64__) /// Get the address at the time the signal was raised from the RIP (x86-64) #if defined(__FreeBSD__) return reinterpret_cast(context.uc_mcontext.mc_rip); #elif defined(__APPLE__) return reinterpret_cast(context.uc_mcontext->__ss.__rip); #else return reinterpret_cast(context.uc_mcontext.gregs[REG_RIP]); #endif #elif defined(__aarch64__) return reinterpret_cast(context.uc_mcontext.pc); #else return nullptr; #endif } StackTrace::StackTrace() { tryCapture(); } StackTrace::StackTrace(const ucontext_t & signal_context) { tryCapture(); void * caller_address = getCallerAddress(signal_context); if (size == 0 && caller_address) { frames[0] = caller_address; size = 1; } else { /// Skip excessive stack frames that we have created while finding stack trace. for (size_t i = 0; i < size; ++i) { if (frames[i] == caller_address) { offset = i; break; } } } } StackTrace::StackTrace(NoCapture) { } #if USE_UNWIND extern "C" int unw_backtrace(void **, int); #endif void StackTrace::tryCapture() { size = 0; #if USE_UNWIND size = unw_backtrace(frames.data(), capacity); #endif } size_t StackTrace::getSize() const { return size; } size_t StackTrace::getOffset() const { return offset; } const StackTrace::Frames & StackTrace::getFrames() const { return frames; } static std::string toStringImpl(const StackTrace::Frames & frames, size_t offset, size_t size) { if (size == 0) return ""; const DB::SymbolIndex & symbol_index = DB::SymbolIndex::instance(); std::unordered_map dwarfs; std::stringstream out; for (size_t i = offset; i < size; ++i) { const void * addr = frames[i]; out << "#" << i << " " << addr << " "; auto symbol = symbol_index.findSymbol(addr); if (symbol) { int status = 0; out << demangle(symbol->name, status); } else out << "?"; out << " "; if (auto object = symbol_index.findObject(addr)) { if (std::filesystem::exists(object->name)) { auto dwarf_it = dwarfs.try_emplace(object->name, *object->elf).first; DB::Dwarf::LocationInfo location; if (dwarf_it->second.findAddress(uintptr_t(addr) - uintptr_t(object->address_begin), location, DB::Dwarf::LocationInfoMode::FAST)) out << location.file.toString() << ":" << location.line; else out << object->name; } } else out << "?"; out << "\n"; } return out.str(); } std::string StackTrace::toString() const { /// Calculation of stack trace text is extremely slow. /// We use simple cache because otherwise the server could be overloaded by trash queries. static SimpleCache func_cached; return func_cached(frames, offset, size); }