diff options
author | Billy Donahue <billy.donahue@mongodb.com> | 2019-09-20 22:07:41 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-09-20 22:07:41 +0000 |
commit | 668d07c416dfcd62f245211d7f9a74898ece59fb (patch) | |
tree | b18850aa2ca9dc9c8070b57c381527496ca3a3f9 /src/mongo/util/stacktrace_posix.cpp | |
parent | 328d1877ce3ab19538d8f19eee8f8867d90742e8 (diff) | |
download | mongo-668d07c416dfcd62f245211d7f9a74898ece59fb.tar.gz |
SERVER-42029 Combine stacktrace posix and unwind files
Diffstat (limited to 'src/mongo/util/stacktrace_posix.cpp')
-rw-r--r-- | src/mongo/util/stacktrace_posix.cpp | 707 |
1 files changed, 288 insertions, 419 deletions
diff --git a/src/mongo/util/stacktrace_posix.cpp b/src/mongo/util/stacktrace_posix.cpp index 29775d035b1..7a63e6c26f0 100644 --- a/src/mongo/util/stacktrace_posix.cpp +++ b/src/mongo/util/stacktrace_posix.cpp @@ -32,43 +32,55 @@ #include "mongo/platform/basic.h" #include "mongo/util/stacktrace.h" +#include "mongo/util/stacktrace_somap.h" +#include <array> +#include <boost/optional.hpp> +#include <climits> #include <cstdlib> #include <dlfcn.h> +#include <iomanip> #include <iostream> #include <string> -#include <sys/utsname.h> #include "mongo/base/init.h" #include "mongo/config.h" -#include "mongo/db/jsobj.h" -#include "mongo/util/hex.h" +#include "mongo/platform/compiler_gcc.h" #include "mongo/util/log.h" -#include "mongo/util/str.h" +#include "mongo/util/scopeguard.h" #include "mongo/util/version.h" -#if defined(MONGO_CONFIG_HAVE_EXECINFO_BACKTRACE) +#define MONGO_STACKTRACE_BACKEND_LIBUNWIND 1 +#define MONGO_STACKTRACE_BACKEND_EXECINFO 2 +#define MONGO_STACKTRACE_BACKEND_NONE 3 + +#if defined(MONGO_USE_LIBUNWIND) +#define MONGO_STACKTRACE_BACKEND MONGO_STACKTRACE_BACKEND_LIBUNWIND +#elif defined(MONGO_CONFIG_HAVE_EXECINFO_BACKTRACE) +#define MONGO_STACKTRACE_BACKEND MONGO_STACKTRACE_BACKEND_EXECINFO +#else +#define MONGO_STACKTRACE_BACKEND MONGO_STACKTRACE_BACKEND_NONE +#endif + +#if MONGO_STACKTRACE_BACKEND == MONGO_STACKTRACE_BACKEND_LIBUNWIND +#include <libunwind.h> +#elif MONGO_STACKTRACE_BACKEND == MONGO_STACKTRACE_BACKEND_EXECINFO #include <execinfo.h> -#elif defined(__sun) -#include <ucontext.h> #endif namespace mongo { +namespace stacktrace_detail { -namespace { -/// Maximum number of stack frames to appear in a backtrace. -const int maxBackTraceFrames = 100; +constexpr int kFrameMax = 100; +constexpr size_t kSymbolMax = 512; +constexpr StringData kUnknownFileName = "???"_sd; -/// Optional string containing extra unwinding information. Should take the form of a -/// JSON document. -std::string* soMapJson = nullptr; +struct StackTraceOptions { + bool withProcessInfo = true; + bool withHumanReadable = true; +}; -/** - * Returns the "basename" of a path. The returned StringData is valid until the data referenced - * by "path" goes out of scope or mutates. - * - * E.g., for "/foo/bar/my.txt", returns "my.txt". - */ +// E.g., for "/foo/bar/my.txt", returns "my.txt". StringData getBaseName(StringData path) { size_t lastSlash = path.rfind('/'); if (lastSlash == std::string::npos) @@ -76,80 +88,55 @@ StringData getBaseName(StringData path) { return path.substr(lastSlash + 1); } -// All platforms we build on have execinfo.h and we use backtrace() directly, with one exception -#if defined(MONGO_CONFIG_HAVE_EXECINFO_BACKTRACE) -using ::backtrace; +struct NameBase { + StringData name; + uintptr_t base; +}; + +// Metadata about an instruction address. +// Beyond that, it may have an enclosing shared object file. +// Further, it may have an enclosing symbol within that shared object. +struct AddressMetadata { + uintptr_t address{}; + boost::optional<NameBase> soFile{}; + boost::optional<NameBase> symbol{}; +}; -// On Solaris 10, there is no execinfo.h, so we need to emulate it. -// Solaris 11 has execinfo.h, and this code doesn't get used. -#elif defined(__sun) -class WalkcontextCallback { +class IterationIface { public: - WalkcontextCallback(uintptr_t* array, int size) - : _position(0), _count(size), _addresses(array) {} - - // This callback function is called from C code, and so must not throw exceptions - // - static int callbackFunction(uintptr_t address, - int signalNumber, - WalkcontextCallback* thisContext) { - if (thisContext->_position < thisContext->_count) { - thisContext->_addresses[thisContext->_position++] = address; - return 0; - } - return 1; - } - int getCount() const { - return static_cast<int>(_position); - } - -private: - size_t _position; - size_t _count; - uintptr_t* _addresses; + enum Flags { + kRaw = 0, + kSymbolic = 1, // Also gather symbolic metadata. + }; + + virtual ~IterationIface() = default; + virtual void start(Flags f) = 0; + virtual bool done() const = 0; + virtual const AddressMetadata& deref() const = 0; + virtual void advance() = 0; }; -typedef int (*WalkcontextCallbackFunc)(uintptr_t address, int signalNumber, void* thisContext); +template <typename T> +struct Quoted { + explicit Quoted(const T& v) : v(v) {} -int backtrace(void** array, int size) { - WalkcontextCallback walkcontextCallback(reinterpret_cast<uintptr_t*>(array), size); - ucontext_t context; - if (getcontext(&context) != 0) { - return 0; + friend std::ostream& operator<<(std::ostream& os, const Quoted& q) { + return os << "\"" << q.v << "\""; } - int wcReturn = walkcontext( - &context, - reinterpret_cast<WalkcontextCallbackFunc>(WalkcontextCallback::callbackFunction), - static_cast<void*>(&walkcontextCallback)); - if (wcReturn == 0) { - return walkcontextCallback.getCount(); - } - return 0; -} -#else -// On unsupported platforms, we print an error instead of printing a stacktrace. -#define MONGO_NO_BACKTRACE -#endif - -} // namespace -#if defined(MONGO_NO_BACKTRACE) -void printStackTrace(std::ostream& os) { - os << "This platform does not support printing stacktraces" << std::endl; -} + const T& v; +}; -#else /** * Prints a stack backtrace for the current thread to the specified ostream. * - * Does not malloc, does not throw. - * * The format of the backtrace is: * - * ----- BEGIN BACKTRACE ----- - * JSON backtrace - * Human-readable backtrace - * ----- END BACKTRACE ----- + * hexAddresses ... // space-separated + * ----- BEGIN BACKTRACE ----- + * {backtrace:..., processInfo:...} // json + * Human-readable backtrace + * ----- END BACKTRACE ----- * * The JSON backtrace will be a JSON object with a "backtrace" field, and optionally others. * The "backtrace" field is an array, whose elements are frame objects. A frame object has a @@ -159,396 +146,278 @@ void printStackTrace(std::ostream& os) { * The JSON backtrace may optionally contain additional information useful to a backtrace * analysis tool. For example, on Linux it contains a subobject named "somap", describing * the objects referenced in the "b" fields of the "backtrace" list. - * - * @param os ostream& to receive printed stack backtrace */ -void printStackTrace(std::ostream& os) { - static const char unknownFileName[] = "???"; - void* addresses[maxBackTraceFrames]; - Dl_info dlinfoForFrames[maxBackTraceFrames]; - - //////////////////////////////////////////////////////////// - // Get the backtrace addresses. - //////////////////////////////////////////////////////////// - - const int addressCount = backtrace(addresses, maxBackTraceFrames); - if (addressCount == 0) { - const int err = errno; - os << "Unable to collect backtrace addresses (errno: " << err << ' ' << strerror(err) << ')' - << std::endl; - return; - } - - //////////////////////////////////////////////////////////// - // Collect symbol information for each backtrace address. - //////////////////////////////////////////////////////////// - +void printStackTraceGeneric(IterationIface& source, + std::ostream& os, + const StackTraceOptions& options) { + // TODO: make this signal-safe. os << std::hex << std::uppercase; - for (int i = 0; i < addressCount; ++i) { - Dl_info& dlinfo(dlinfoForFrames[i]); - if (!dladdr(addresses[i], &dlinfo)) { - dlinfo.dli_fname = unknownFileName; - dlinfo.dli_fbase = nullptr; - dlinfo.dli_sname = nullptr; - dlinfo.dli_saddr = nullptr; - } - os << ' ' << addresses[i]; + auto sg = makeGuard([&] { os << std::dec << std::nouppercase; }); + // First, just the raw backtrace addresses. + for (source.start(source.kRaw); !source.done(); source.advance()) { + const auto& f = source.deref(); + os << ' ' << f.address; } os << "\n----- BEGIN BACKTRACE -----\n"; - //////////////////////////////////////////////////////////// // Display the JSON backtrace - //////////////////////////////////////////////////////////// - - os << "{\"backtrace\":["; - for (int i = 0; i < addressCount; ++i) { - const Dl_info& dlinfo = dlinfoForFrames[i]; - const uintptr_t fileOffset = uintptr_t(addresses[i]) - uintptr_t(dlinfo.dli_fbase); - if (i) - os << ','; - os << "{\"b\":\"" << uintptr_t(dlinfo.dli_fbase) << "\",\"o\":\"" << fileOffset; - if (dlinfo.dli_sname) { - os << "\",\"s\":\"" << dlinfo.dli_sname; + { + os << "{"; + os << Quoted("backtrace") << ":"; + { + os << "["; + StringData frameComma; + for (source.start(source.kSymbolic); !source.done(); source.advance()) { + const auto& f = source.deref(); + os << frameComma; + frameComma = ","_sd; + { + os << "{"; + StringData fieldSep = ""; + auto addField = [&](StringData k, auto&& v) { + os << fieldSep << Quoted(k) << ":" << Quoted(v); + fieldSep = ","_sd; + }; + if (f.soFile) { + // Cast because integers obey `uppercase` and pointers don't. + addField("b", f.soFile->base); + addField("o", (f.address - f.soFile->base)); + if (f.symbol) { + addField("s", f.symbol->name); + } + } else { + addField("b", uintptr_t{0}); + addField("o", f.address); + } + os << "}"; + } + } + os << "]"; + } + if (options.withProcessInfo) { + if (auto soMap = globalSharedObjectMapInfo()) + os << "," << Quoted("processInfo") << ":" << soMap->json(); } - os << "\"}"; + os << "}"; } - os << ']'; - - if (soMapJson) - os << ",\"processInfo\":" << *soMapJson; - os << "}\n"; - - //////////////////////////////////////////////////////////// + os << "\n"; // Display the human-readable trace - //////////////////////////////////////////////////////////// - for (int i = 0; i < addressCount; ++i) { - Dl_info& dlinfo(dlinfoForFrames[i]); - os << ' '; - if (dlinfo.dli_fbase) { - os << getBaseName(dlinfo.dli_fname) << '('; - if (dlinfo.dli_sname) { - const uintptr_t offset = uintptr_t(addresses[i]) - uintptr_t(dlinfo.dli_saddr); - os << dlinfo.dli_sname << "+0x" << offset; + if (options.withHumanReadable) { + for (source.start(source.kSymbolic); !source.done(); source.advance()) { + const auto& f = source.deref(); + os << " "; + if (f.soFile) { + os << getBaseName(f.soFile->name); + os << "("; + if (f.symbol) { + os << f.symbol->name << "+0x" << (f.address - f.symbol->base); + } else { + // No symbol, so fall back to the `soFile` offset. + os << "+0x" << (f.address - f.soFile->base); + } + os << ")"; } else { - const uintptr_t offset = uintptr_t(addresses[i]) - uintptr_t(dlinfo.dli_fbase); - os << "+0x" << offset; + // Not even shared object information, just punt with unknown filename. + os << kUnknownFileName; } - os << ')'; - } else { - os << unknownFileName; + os << " [0x" << f.address << "]\n"; } - os << " [" << addresses[i] << ']' << std::endl; } - - os << std::dec << std::nouppercase; - os << "----- END BACKTRACE -----" << std::endl; -} - -#endif - -void printStackTraceFromSignal(std::ostream& os) { - printStackTrace(os); + os << "----- END BACKTRACE -----\n"; } -// From here down, a copy of stacktrace_unwind.cpp. -namespace { - -void addOSComponentsToSoMap(BSONObjBuilder* soMap); - -/** - * Builds the "soMapJson" string, which is a JSON encoding of various pieces of information - * about a running process, including the map from load addresses to shared objects loaded at - * those addresses. - */ -MONGO_INITIALIZER(ExtractSOMap)(InitializerContext*) { - BSONObjBuilder soMap; - - auto&& vii = VersionInfoInterface::instance(VersionInfoInterface::NotEnabledAction::kFallback); - soMap << "mongodbVersion" << vii.version(); - soMap << "gitVersion" << vii.gitVersion(); - soMap << "compiledModules" << vii.modules(); - - struct utsname unameData; - if (!uname(&unameData)) { - BSONObjBuilder unameBuilder(soMap.subobjStart("uname")); - unameBuilder << "sysname" << unameData.sysname << "release" << unameData.release - << "version" << unameData.version << "machine" << unameData.machine; +void mergeDlInfo(AddressMetadata& f) { + Dl_info dli; + // `man dladdr`: + // On success, these functions return a nonzero value. If the address + // specified in addr could be matched to a shared object, but not to a + // symbol in the shared object, then the info->dli_sname and + // info->dli_saddr fields are set to NULL. + if (dladdr(reinterpret_cast<void*>(f.address), &dli) == 0) { + return; // f.address doesn't map to a shared object + } + if (!f.soFile) { + f.soFile = NameBase{dli.dli_fname, reinterpret_cast<uintptr_t>(dli.dli_fbase)}; + } + if (!f.symbol) { + if (dli.dli_saddr) { + // matched to a symbol in the shared object + f.symbol = NameBase{dli.dli_sname, reinterpret_cast<uintptr_t>(dli.dli_saddr)}; + } } - addOSComponentsToSoMap(&soMap); - soMapJson = new std::string(soMap.done().jsonString(Strict)); - return Status::OK(); } -} // namespace -} // namespace mongo - -#if defined(__linux__) +#if MONGO_STACKTRACE_BACKEND == MONGO_STACKTRACE_BACKEND_LIBUNWIND -#include <elf.h> -#include <link.h> +class Iteration : public IterationIface { +public: + explicit Iteration(std::ostream& os, bool fromSignal) : _os(os), _fromSignal(fromSignal) { + if (int r = unw_getcontext(&_context); r < 0) { + _os << "unw_getcontext: " << unw_strerror(r) << std::endl; + _failed = true; + } + } -namespace mongo { -namespace { +private: + void start(Flags f) override { + _flags = f; + _end = false; -/** - * Rounds a byte offset up to the next highest offset that is aligned with an ELF Word. - */ -size_t roundUpToElfWordAlignment(size_t offset) { - static const size_t elfWordSizeBytes = sizeof(ElfW(Word)); - return (offset + (elfWordSizeBytes - 1)) & ~(elfWordSizeBytes - 1); -} + if (_failed) { + _end = true; + return; + } + int r = unw_init_local2(&_cursor, &_context, _fromSignal ? UNW_INIT_SIGNAL_FRAME : 0); + if (r < 0) { + _os << "unw_init_local2: " << unw_strerror(r) << std::endl; + _end = true; + return; + } + _load(); + } -/** - * Returns the size in bytes of an ELF note entry with the given header. - */ -size_t getNoteSizeBytes(const ElfW(Nhdr) & noteHeader) { - return sizeof(noteHeader) + roundUpToElfWordAlignment(noteHeader.n_namesz) + - roundUpToElfWordAlignment(noteHeader.n_descsz); -} + bool done() const override { + return _end; + } -/** - * Returns true of the given ELF program header refers to a runtime-readable segment. - */ -bool isSegmentMappedReadable(const ElfW(Phdr) & phdr) { - return phdr.p_flags & PF_R; -} + const AddressMetadata& deref() const override { + return _f; + } -/** - * Processes an ELF Phdr for a NOTE segment, updating "soInfo". - * - * Looks for the GNU Build ID NOTE, and adds a buildId field to soInfo if it finds one. - */ -void processNoteSegment(const dl_phdr_info& info, const ElfW(Phdr) & phdr, BSONObjBuilder* soInfo) { -#ifdef NT_GNU_BUILD_ID - const char* const notesBegin = reinterpret_cast<const char*>(info.dlpi_addr) + phdr.p_vaddr; - const char* const notesEnd = notesBegin + phdr.p_memsz; - ElfW(Nhdr) noteHeader; - for (const char* notesCurr = notesBegin; (notesCurr + sizeof(noteHeader)) < notesEnd; - notesCurr += getNoteSizeBytes(noteHeader)) { - memcpy(¬eHeader, notesCurr, sizeof(noteHeader)); - if (noteHeader.n_type != NT_GNU_BUILD_ID) - continue; - const char* const noteNameBegin = notesCurr + sizeof(noteHeader); - if (StringData(noteNameBegin, noteHeader.n_namesz - 1) != ELF_NOTE_GNU) { - continue; + void advance() override { + int r = unw_step(&_cursor); + if (r <= 0) { + if (r < 0) { + _os << "error: unw_step: " << unw_strerror(r) << std::endl; + } + _end = true; + } + if (!_end) { + _load(); } - const char* const noteDescBegin = - noteNameBegin + roundUpToElfWordAlignment(noteHeader.n_namesz); - soInfo->append("buildId", toHex(noteDescBegin, noteHeader.n_descsz)); } -#endif -} -/** - * Processes an ELF Phdr for a LOAD segment, updating "soInfo". - * - * The goal of this operation is to find out if the current object is an executable or a shared - * object, by looking for the LOAD segment that maps the first several bytes of the file (the - * ELF header). If it's an executable, this method updates soInfo with the load address of the - * segment - */ -void processLoadSegment(const dl_phdr_info& info, const ElfW(Phdr) & phdr, BSONObjBuilder* soInfo) { - if (phdr.p_offset) - return; - if (phdr.p_memsz < sizeof(ElfW(Ehdr))) - return; + void _load() { + _f = {}; + unw_word_t pc; + if (int r = unw_get_reg(&_cursor, UNW_REG_IP, &pc); r < 0) { + _os << "unw_get_reg: " << unw_strerror(r) << std::endl; + _end = true; + return; + } + if (pc == 0) { + _end = true; + return; + } + _f.address = pc; + if (_flags & kSymbolic) { + unw_word_t offset; + if (int r = unw_get_proc_name(&_cursor, _symbolBuf, sizeof(_symbolBuf), &offset); + r < 0) { + _os << "unw_get_proc_name(" << _f.address << "): " << unw_strerror(r) << std::endl; + } else { + _f.symbol = NameBase{_symbolBuf, _f.address - offset}; + } + mergeDlInfo(_f); + } + } - // Segment includes beginning of file and is large enough to hold the ELF header - ElfW(Ehdr) eHeader; - memcpy(&eHeader, reinterpret_cast<const char*>(info.dlpi_addr) + phdr.p_vaddr, sizeof(eHeader)); + std::ostream& _os; + bool _fromSignal; - std::string quotedFileName = "\"" + str::escape(info.dlpi_name) + "\""; + Flags _flags; + AddressMetadata _f{}; - if (memcmp(&eHeader.e_ident[0], ELFMAG, SELFMAG)) { - warning() << "Bad ELF magic number in image of " << quotedFileName; - return; - } + bool _failed = false; + bool _end = false; -#if defined(__ELF_NATIVE_CLASS) -#define ARCH_BITS __ELF_NATIVE_CLASS -#else //__ELF_NATIVE_CLASS -#if defined(__x86_64__) || defined(__aarch64__) -#define ARCH_BITS 64 -#elif defined(__arm__) -#define ARCH_BITS 32 -#else -#error Unknown target architecture. -#endif //__aarch64__ -#endif //__ELF_NATIVE_CLASS - -#define MKELFCLASS(N) _MKELFCLASS(N) -#define _MKELFCLASS(N) ELFCLASS##N - if (eHeader.e_ident[EI_CLASS] != MKELFCLASS(ARCH_BITS)) { - warning() << "Expected elf file class of " << quotedFileName << " to be " - << MKELFCLASS(ARCH_BITS) << "(" << ARCH_BITS << "-bit), but found " - << int(eHeader.e_ident[4]); - return; - } + unw_context_t _context; + unw_cursor_t _cursor; -#undef ARCH_BITS + char _symbolBuf[kSymbolMax]; +}; - if (eHeader.e_ident[EI_VERSION] != EV_CURRENT) { - warning() << "Wrong ELF version in " << quotedFileName << ". Expected " << EV_CURRENT - << " but found " << int(eHeader.e_ident[EI_VERSION]); - return; - } +MONGO_COMPILER_NOINLINE +void printStackTrace(std::ostream& os, bool fromSignal) { + Iteration iteration(os, fromSignal); + printStackTraceGeneric(iteration, os, StackTraceOptions{}); +} - soInfo->append("elfType", eHeader.e_type); +#elif MONGO_STACKTRACE_BACKEND == MONGO_STACKTRACE_BACKEND_EXECINFO - switch (eHeader.e_type) { - case ET_EXEC: - break; - case ET_DYN: - return; - default: - warning() << "Surprised to find " << quotedFileName << " is ELF file of type " - << eHeader.e_type; +class Iteration : public IterationIface { +public: + explicit Iteration(std::ostream& os, bool fromSignal) { + _n = ::backtrace(_addresses.data(), _addresses.size()); + if (_n == 0) { + int err = errno; + os << "Unable to collect backtrace addresses (errno: " << err << ' ' << strerror(err) + << ')' << std::endl; return; + } } - soInfo->append("b", integerToHex(phdr.p_vaddr)); -} +private: + void start(Flags f) override { + _flags = f; + _i = 0; + if (!done()) + _load(); + } + bool done() const override { + return _i >= _n; + } + const AddressMetadata& deref() const override { + return _f; + } + void advance() override { + ++_i; + if (!done()) + _load(); + } -/** - * Callback that processes an ELF object linked into the current address space. - * - * Used by dl_iterate_phdr in ExtractSOMap, below, to build up the list of linked - * objects. - * - * Each entry built by an invocation of ths function may have the following fields: - * * "b", the base address at which an object is loaded. - * * "path", the path on the file system to the object. - * * "buildId", the GNU Build ID of the object. - * * "elfType", the ELF type of the object, typically 2 or 3 (executable or SO). - * - * At post-processing time, the buildId field can be used to identify the file containing - * debug symbols for objects loaded at the given "laodAddr", which in turn can be used with - * the "backtrace" displayed in printStackTrace to get detailed unwind information. - */ -int outputSOInfo(dl_phdr_info* info, size_t sz, void* data) { - BSONObjBuilder soInfo(reinterpret_cast<BSONArrayBuilder*>(data)->subobjStart()); - if (info->dlpi_addr) - soInfo.append("b", integerToHex(ElfW(Addr)(info->dlpi_addr))); - if (info->dlpi_name && *info->dlpi_name) - soInfo.append("path", info->dlpi_name); - - for (ElfW(Half) i = 0; i < info->dlpi_phnum; ++i) { - const ElfW(Phdr) & phdr(info->dlpi_phdr[i]); - if (!isSegmentMappedReadable(phdr)) - continue; - switch (phdr.p_type) { - case PT_NOTE: - processNoteSegment(*info, phdr, &soInfo); - break; - case PT_LOAD: - processLoadSegment(*info, phdr, &soInfo); - break; - default: - break; + void _load() { + _f = {}; + _f.address = reinterpret_cast<uintptr_t>(_addresses[_i]); + if (_flags & kSymbolic) { + mergeDlInfo(_f); } } - return 0; -} - -void addOSComponentsToSoMap(BSONObjBuilder* soMap) { - BSONArrayBuilder soList(soMap->subarrayStart("somap")); - dl_iterate_phdr(outputSOInfo, &soList); - soList.done(); -} -} // namespace + Flags _flags; + AddressMetadata _f; -} // namespace mongo + std::array<void*, kFrameMax> _addresses; + size_t _n = 0; + size_t _i = 0; +}; -#elif defined(__APPLE__) && defined(__MACH__) +MONGO_COMPILER_NOINLINE +void printStackTrace(std::ostream& os, bool fromSignal) { + Iteration iteration(os, fromSignal); + printStackTraceGeneric(iteration, os, StackTraceOptions{}); +} -#include <mach-o/dyld.h> -#include <mach-o/ldsyms.h> -#include <mach-o/loader.h> +#elif MONGO_STACKTRACE_BACKEND == MONGO_STACKTRACE_BACKEND_NONE -namespace mongo { -namespace { -const char* lcNext(const char* lcCurr) { - const load_command* cmd = reinterpret_cast<const load_command*>(lcCurr); - return lcCurr + cmd->cmdsize; +MONGO_COMPILER_NOINLINE +void printStackTrace(std::ostream& os, bool fromSignal) { + os << "This platform does not support printing stacktraces" << std::endl; } -uint32_t lcType(const char* lcCurr) { - const load_command* cmd = reinterpret_cast<const load_command*>(lcCurr); - return cmd->cmd; -} +#endif // MONGO_STACKTRACE_BACKEND -template <typename SegmentCommandType> -bool maybeAppendLoadAddr(BSONObjBuilder* soInfo, const SegmentCommandType* segmentCommand) { - if (StringData(SEG_TEXT) != segmentCommand->segname) { - return false; - } - *soInfo << "vmaddr" << integerToHex(segmentCommand->vmaddr); - return true; +} // namespace stacktrace_detail + +MONGO_COMPILER_NOINLINE +void printStackTrace(std::ostream& os) { + stacktrace_detail::printStackTrace(os, false); } -void addOSComponentsToSoMap(BSONObjBuilder* soMap) { - const uint32_t numImages = _dyld_image_count(); - BSONArrayBuilder soList(soMap->subarrayStart("somap")); - for (uint32_t i = 0; i < numImages; ++i) { - BSONObjBuilder soInfo(soList.subobjStart()); - const char* name = _dyld_get_image_name(i); - if (name) - soInfo << "path" << name; - const mach_header* header = _dyld_get_image_header(i); - if (!header) - continue; - size_t headerSize; - if (header->magic == MH_MAGIC) { - headerSize = sizeof(mach_header); - } else if (header->magic == MH_MAGIC_64) { - headerSize = sizeof(mach_header_64); - } else { - continue; - } - soInfo << "machType" << static_cast<int32_t>(header->filetype); - soInfo << "b" << integerToHex(reinterpret_cast<intptr_t>(header)); - const char* const loadCommandsBegin = reinterpret_cast<const char*>(header) + headerSize; - const char* const loadCommandsEnd = loadCommandsBegin + header->sizeofcmds; - - // Search the "load command" data in the Mach object for the entry encoding the UUID of the - // object, and for the __TEXT segment. Adding the "vmaddr" field of the __TEXT segment load - // command of an executable or dylib to an offset in that library provides an address - // suitable to passing to atos or llvm-symbolizer for symbolization. - // - // See, for example, http://lldb.llvm.org/symbolication.html. - bool foundTextSegment = false; - for (const char* lcCurr = loadCommandsBegin; lcCurr < loadCommandsEnd; - lcCurr = lcNext(lcCurr)) { - switch (lcType(lcCurr)) { - case LC_UUID: { - const auto uuidCmd = reinterpret_cast<const uuid_command*>(lcCurr); - soInfo << "buildId" << toHex(uuidCmd->uuid, 16); - break; - } - case LC_SEGMENT_64: - if (!foundTextSegment) { - foundTextSegment = maybeAppendLoadAddr( - &soInfo, reinterpret_cast<const segment_command_64*>(lcCurr)); - } - break; - case LC_SEGMENT: - if (!foundTextSegment) { - foundTextSegment = maybeAppendLoadAddr( - &soInfo, reinterpret_cast<const segment_command*>(lcCurr)); - } - break; - } - } - } +MONGO_COMPILER_NOINLINE +void printStackTraceFromSignal(std::ostream& os) { + stacktrace_detail::printStackTrace(os, true); } -} // namespace -} // namespace mongo -#else -namespace mongo { -namespace { -void addOSComponentsToSoMap(BSONObjBuilder* soMap) {} -} // namespace + } // namespace mongo -#endif |