2019-07-29 22:26:44 +00:00
|
|
|
#include <Common/StackTrace.h>
|
2019-08-28 20:49:37 +00:00
|
|
|
|
2020-05-29 21:36:47 +00:00
|
|
|
#include <Core/Defines.h>
|
2019-07-29 22:26:44 +00:00
|
|
|
#include <Common/Dwarf.h>
|
|
|
|
#include <Common/Elf.h>
|
2020-05-29 21:36:47 +00:00
|
|
|
#include <Common/SymbolIndex.h>
|
2020-06-01 18:10:19 +00:00
|
|
|
#include <Common/MemorySanitizer.h>
|
2019-08-28 20:49:37 +00:00
|
|
|
#include <common/SimpleCache.h>
|
|
|
|
#include <common/demangle.h>
|
|
|
|
|
|
|
|
#include <cstring>
|
2019-07-29 22:26:44 +00:00
|
|
|
#include <filesystem>
|
2019-08-28 20:49:37 +00:00
|
|
|
#include <sstream>
|
2019-07-29 22:26:44 +00:00
|
|
|
#include <unordered_map>
|
2019-06-28 18:06:38 +00:00
|
|
|
|
2020-04-16 12:31:57 +00:00
|
|
|
#if !defined(ARCADIA_BUILD)
|
|
|
|
# include <Common/config.h>
|
|
|
|
#endif
|
|
|
|
|
2019-08-28 20:49:37 +00:00
|
|
|
#if USE_UNWIND
|
2020-04-16 12:31:57 +00:00
|
|
|
# include <libunwind.h>
|
2019-08-28 20:49:37 +00:00
|
|
|
#endif
|
2019-06-28 18:06:38 +00:00
|
|
|
|
|
|
|
std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext_t & context)
|
|
|
|
{
|
|
|
|
std::stringstream error;
|
|
|
|
switch (sig)
|
|
|
|
{
|
2020-05-30 08:01:15 +00:00
|
|
|
case SIGSEGV:
|
|
|
|
{
|
2019-06-28 18:06:38 +00:00
|
|
|
/// Print info about address and reason.
|
|
|
|
if (nullptr == info.si_addr)
|
|
|
|
error << "Address: NULL pointer.";
|
|
|
|
else
|
|
|
|
error << "Address: " << info.si_addr;
|
|
|
|
|
2019-10-30 07:01:53 +00:00
|
|
|
#if defined(__x86_64__) && !defined(__FreeBSD__) && !defined(__APPLE__) && !defined(__arm__)
|
2019-06-28 18:06:38 +00:00
|
|
|
auto err_mask = context.uc_mcontext.gregs[REG_ERR];
|
|
|
|
if ((err_mask & 0x02))
|
|
|
|
error << " Access: write.";
|
|
|
|
else
|
|
|
|
error << " Access: read.";
|
2019-08-04 00:19:03 +00:00
|
|
|
#else
|
|
|
|
UNUSED(context);
|
2019-06-28 18:06:38 +00:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
2020-05-30 08:01:15 +00:00
|
|
|
case SIGBUS:
|
|
|
|
{
|
2019-06-28 18:06:38 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-05-30 08:01:15 +00:00
|
|
|
case SIGILL:
|
|
|
|
{
|
2019-06-28 18:06:38 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-05-30 08:01:15 +00:00
|
|
|
case SIGFPE:
|
|
|
|
{
|
2019-06-28 18:06:38 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-05-30 08:01:15 +00:00
|
|
|
case SIGTSTP:
|
|
|
|
{
|
2019-12-02 17:29:19 +00:00
|
|
|
error << "This is a signal used for debugging purposes by the user.";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-12-02 11:29:52 +00:00
|
|
|
|
2019-06-28 18:06:38 +00:00
|
|
|
return error.str();
|
|
|
|
}
|
|
|
|
|
2019-07-31 21:40:29 +00:00
|
|
|
static void * getCallerAddress(const ucontext_t & context)
|
2019-06-28 18:06:38 +00:00
|
|
|
{
|
|
|
|
#if defined(__x86_64__)
|
|
|
|
/// Get the address at the time the signal was raised from the RIP (x86-64)
|
2020-05-29 21:36:47 +00:00
|
|
|
# if defined(__FreeBSD__)
|
2019-06-28 18:06:38 +00:00
|
|
|
return reinterpret_cast<void *>(context.uc_mcontext.mc_rip);
|
2020-05-29 21:36:47 +00:00
|
|
|
# elif defined(__APPLE__)
|
2019-06-28 18:06:38 +00:00
|
|
|
return reinterpret_cast<void *>(context.uc_mcontext->__ss.__rip);
|
2020-05-29 21:36:47 +00:00
|
|
|
# else
|
2019-06-28 18:06:38 +00:00
|
|
|
return reinterpret_cast<void *>(context.uc_mcontext.gregs[REG_RIP]);
|
2020-05-29 21:36:47 +00:00
|
|
|
# endif
|
2019-06-28 18:06:38 +00:00
|
|
|
#elif defined(__aarch64__)
|
|
|
|
return reinterpret_cast<void *>(context.uc_mcontext.pc);
|
2019-07-29 22:26:44 +00:00
|
|
|
#else
|
2019-06-28 18:06:38 +00:00
|
|
|
return nullptr;
|
2019-07-29 22:26:44 +00:00
|
|
|
#endif
|
2019-06-28 18:06:38 +00:00
|
|
|
}
|
|
|
|
|
2020-05-27 19:11:04 +00:00
|
|
|
static void symbolize(const void * const * frame_pointers, size_t offset, size_t size, StackTrace::Frames & frames)
|
|
|
|
{
|
|
|
|
#if defined(__ELF__) && !defined(__FreeBSD__)
|
|
|
|
|
|
|
|
const DB::SymbolIndex & symbol_index = DB::SymbolIndex::instance();
|
|
|
|
std::unordered_map<std::string, DB::Dwarf> dwarfs;
|
|
|
|
|
2020-05-29 21:36:47 +00:00
|
|
|
for (size_t i = 0; i < offset; ++i)
|
|
|
|
{
|
2020-05-27 19:11:04 +00:00
|
|
|
frames.value()[i].virtual_addr = frame_pointers[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = offset; i < size; ++i)
|
|
|
|
{
|
|
|
|
StackTrace::Frame & current_frame = frames.value()[i];
|
|
|
|
current_frame.virtual_addr = frame_pointers[i];
|
|
|
|
const auto * object = symbol_index.findObject(current_frame.virtual_addr);
|
|
|
|
uintptr_t virtual_offset = object ? uintptr_t(object->address_begin) : 0;
|
|
|
|
current_frame.physical_addr = reinterpret_cast<void *>(uintptr_t(current_frame.virtual_addr) - virtual_offset);
|
|
|
|
|
|
|
|
if (object)
|
|
|
|
{
|
|
|
|
current_frame.object = object->name;
|
|
|
|
if (std::filesystem::exists(current_frame.object.value()))
|
|
|
|
{
|
|
|
|
auto dwarf_it = dwarfs.try_emplace(object->name, *object->elf).first;
|
|
|
|
|
|
|
|
DB::Dwarf::LocationInfo location;
|
2020-05-29 21:36:47 +00:00
|
|
|
if (dwarf_it->second.findAddress(uintptr_t(current_frame.physical_addr), location, DB::Dwarf::LocationInfoMode::FAST))
|
|
|
|
{
|
2020-05-27 19:11:04 +00:00
|
|
|
current_frame.file = location.file.toString();
|
|
|
|
current_frame.line = location.line;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
current_frame.object = "?";
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto * symbol = symbol_index.findSymbol(current_frame.virtual_addr);
|
|
|
|
if (symbol)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
current_frame.symbol = demangle(symbol->name, status);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
current_frame.symbol = "?";
|
|
|
|
}
|
|
|
|
}
|
2020-05-29 21:36:47 +00:00
|
|
|
#else
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
{
|
2020-05-27 19:11:04 +00:00
|
|
|
frames.value()[i].virtual_addr = frame_pointers[i];
|
|
|
|
}
|
|
|
|
UNUSED(offset);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-01 22:11:11 +00:00
|
|
|
StackTrace::StackTrace()
|
|
|
|
{
|
|
|
|
tryCapture();
|
|
|
|
}
|
2019-06-28 18:06:38 +00:00
|
|
|
|
2019-07-01 22:11:11 +00:00
|
|
|
StackTrace::StackTrace(const ucontext_t & signal_context)
|
2019-06-28 18:06:38 +00:00
|
|
|
{
|
2019-07-01 22:11:11 +00:00
|
|
|
tryCapture();
|
2019-06-28 18:06:38 +00:00
|
|
|
|
2019-07-31 21:40:29 +00:00
|
|
|
void * caller_address = getCallerAddress(signal_context);
|
|
|
|
|
|
|
|
if (size == 0 && caller_address)
|
2019-07-01 22:11:11 +00:00
|
|
|
{
|
2020-05-27 19:11:04 +00:00
|
|
|
frame_pointers[0] = caller_address;
|
2019-07-31 21:40:29 +00:00
|
|
|
size = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/// Skip excessive stack frames that we have created while finding stack trace.
|
|
|
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
{
|
2020-05-27 19:11:04 +00:00
|
|
|
if (frame_pointers[i] == caller_address)
|
2019-07-31 21:40:29 +00:00
|
|
|
{
|
|
|
|
offset = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-06-28 18:06:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-01 22:11:11 +00:00
|
|
|
StackTrace::StackTrace(NoCapture)
|
|
|
|
{
|
2019-06-28 18:06:38 +00:00
|
|
|
}
|
|
|
|
|
2019-07-01 22:11:11 +00:00
|
|
|
void StackTrace::tryCapture()
|
|
|
|
{
|
|
|
|
size = 0;
|
|
|
|
#if USE_UNWIND
|
2020-05-27 19:11:04 +00:00
|
|
|
size = unw_backtrace(frame_pointers.data(), capacity);
|
|
|
|
__msan_unpoison(frame_pointers.data(), size * sizeof(frame_pointers[0]));
|
2019-07-01 22:11:11 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t StackTrace::getSize() const
|
|
|
|
{
|
2019-06-28 18:06:38 +00:00
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2019-07-31 21:40:29 +00:00
|
|
|
size_t StackTrace::getOffset() const
|
2019-07-01 22:11:11 +00:00
|
|
|
{
|
2019-07-31 21:40:29 +00:00
|
|
|
return offset;
|
2019-06-28 18:06:38 +00:00
|
|
|
}
|
|
|
|
|
2020-05-27 19:11:04 +00:00
|
|
|
const StackTrace::FramePointers & StackTrace::getFramePointers() const
|
|
|
|
{
|
|
|
|
return frame_pointers;
|
|
|
|
}
|
|
|
|
|
2019-07-31 21:40:29 +00:00
|
|
|
const StackTrace::Frames & StackTrace::getFrames() const
|
2019-06-28 18:06:38 +00:00
|
|
|
{
|
2020-05-29 21:36:47 +00:00
|
|
|
if (!frames.has_value())
|
|
|
|
{
|
2020-05-27 19:11:04 +00:00
|
|
|
frames = {{}};
|
|
|
|
symbolize(frame_pointers.data(), offset, size, frames);
|
|
|
|
}
|
2019-07-31 21:40:29 +00:00
|
|
|
return frames;
|
2019-06-28 18:06:38 +00:00
|
|
|
}
|
|
|
|
|
2020-05-29 21:36:47 +00:00
|
|
|
static void
|
|
|
|
toStringEveryLineImpl(const StackTrace::Frames & frames, size_t offset, size_t size, std::function<void(const std::string &)> callback)
|
2019-06-28 18:06:38 +00:00
|
|
|
{
|
|
|
|
if (size == 0)
|
2019-08-17 22:39:26 +00:00
|
|
|
return callback("<Empty trace>");
|
2019-06-28 18:06:38 +00:00
|
|
|
|
2019-07-29 22:26:44 +00:00
|
|
|
std::stringstream out;
|
2019-06-28 18:06:38 +00:00
|
|
|
|
2019-07-31 21:40:29 +00:00
|
|
|
for (size_t i = offset; i < size; ++i)
|
2019-06-28 18:06:38 +00:00
|
|
|
{
|
2020-05-29 21:36:47 +00:00
|
|
|
const StackTrace::Frame & current_frame = frames.value()[i];
|
2020-01-28 14:39:24 +00:00
|
|
|
out << i << ". ";
|
2019-06-28 18:06:38 +00:00
|
|
|
|
2020-05-27 19:11:04 +00:00
|
|
|
if (current_frame.file.has_value() && current_frame.line.has_value())
|
2019-07-29 22:26:44 +00:00
|
|
|
{
|
2020-05-27 19:11:04 +00:00
|
|
|
out << current_frame.file.value() << ":" << current_frame.line.value() << ": ";
|
2020-01-28 14:39:24 +00:00
|
|
|
}
|
|
|
|
|
2020-05-27 19:11:04 +00:00
|
|
|
if (current_frame.symbol.has_value())
|
2020-01-28 14:39:24 +00:00
|
|
|
{
|
2020-05-27 19:11:04 +00:00
|
|
|
out << current_frame.symbol.value();
|
2019-06-28 18:06:38 +00:00
|
|
|
}
|
2019-07-29 22:26:44 +00:00
|
|
|
|
2020-05-27 19:11:04 +00:00
|
|
|
out << " @ " << current_frame.physical_addr;
|
2020-05-29 21:36:47 +00:00
|
|
|
if (current_frame.object.has_value())
|
|
|
|
{
|
2020-05-27 19:11:04 +00:00
|
|
|
out << " in " << current_frame.object.value();
|
|
|
|
}
|
2019-08-21 00:48:34 +00:00
|
|
|
|
|
|
|
callback(out.str());
|
|
|
|
out.str({});
|
|
|
|
}
|
2019-08-17 22:39:26 +00:00
|
|
|
}
|
2019-06-28 18:06:38 +00:00
|
|
|
|
2020-05-27 19:11:04 +00:00
|
|
|
static std::string toStringImpl(const void * const * frame_pointers, size_t offset, size_t size)
|
2019-08-17 22:39:26 +00:00
|
|
|
{
|
|
|
|
std::stringstream out;
|
2020-05-27 19:11:04 +00:00
|
|
|
StackTrace::Frames frames{};
|
|
|
|
frames = {{}};
|
|
|
|
symbolize(frame_pointers, offset, size, frames);
|
2019-08-17 22:39:26 +00:00
|
|
|
toStringEveryLineImpl(frames, offset, size, [&](const std::string & str) { out << str << '\n'; });
|
2019-07-29 22:26:44 +00:00
|
|
|
return out.str();
|
2019-06-28 18:06:38 +00:00
|
|
|
}
|
2019-07-31 21:40:29 +00:00
|
|
|
|
2019-08-17 22:39:26 +00:00
|
|
|
void StackTrace::toStringEveryLine(std::function<void(const std::string &)> callback) const
|
|
|
|
{
|
2020-05-27 19:11:04 +00:00
|
|
|
toStringEveryLineImpl(getFrames(), offset, size, std::move(callback));
|
|
|
|
}
|
|
|
|
|
2020-05-29 21:36:47 +00:00
|
|
|
void StackTrace::resetFrames()
|
|
|
|
{
|
2020-05-27 19:11:04 +00:00
|
|
|
frames.reset();
|
2019-08-17 22:39:26 +00:00
|
|
|
}
|
|
|
|
|
2020-05-27 19:11:04 +00:00
|
|
|
|
2019-07-31 21:40:29 +00:00
|
|
|
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<decltype(toStringImpl), &toStringImpl> func_cached;
|
2020-05-27 19:11:04 +00:00
|
|
|
return func_cached(frame_pointers.data(), offset, size);
|
2019-07-31 21:40:29 +00:00
|
|
|
}
|
2020-01-02 06:56:53 +00:00
|
|
|
|
2020-05-30 07:59:43 +00:00
|
|
|
std::string StackTrace::toString(void ** frame_pointers_, size_t offset, size_t size)
|
2020-01-02 06:56:53 +00:00
|
|
|
{
|
2020-05-30 07:59:43 +00:00
|
|
|
__msan_unpoison(frame_pointers_, size * sizeof(*frame_pointers_));
|
2020-01-06 02:15:31 +00:00
|
|
|
|
2020-05-27 19:11:04 +00:00
|
|
|
StackTrace::FramePointers frame_pointers_copy{};
|
2020-01-02 06:56:53 +00:00
|
|
|
for (size_t i = 0; i < size; ++i)
|
2020-05-30 07:59:43 +00:00
|
|
|
frame_pointers_copy[i] = frame_pointers_[i];
|
2020-01-02 06:56:53 +00:00
|
|
|
|
|
|
|
static SimpleCache<decltype(toStringImpl), &toStringImpl> func_cached;
|
2020-05-27 19:11:04 +00:00
|
|
|
return func_cached(frame_pointers_copy.data(), offset, size);
|
2020-01-02 06:56:53 +00:00
|
|
|
}
|