summaryrefslogtreecommitdiff
path: root/src/mongo/util/stacktrace_posix.cpp
diff options
context:
space:
mode:
authorBilly Donahue <billy.donahue@mongodb.com>2019-09-20 22:07:41 +0000
committerevergreen <evergreen@mongodb.com>2019-09-20 22:07:41 +0000
commit668d07c416dfcd62f245211d7f9a74898ece59fb (patch)
treeb18850aa2ca9dc9c8070b57c381527496ca3a3f9 /src/mongo/util/stacktrace_posix.cpp
parent328d1877ce3ab19538d8f19eee8f8867d90742e8 (diff)
downloadmongo-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.cpp707
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(&noteHeader, 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