diff options
author | Billy Donahue <billy.donahue@mongodb.com> | 2019-11-08 21:58:51 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-11-08 21:58:51 +0000 |
commit | aeed74375c24c59fb6fb725691fe9ce547a810ab (patch) | |
tree | 0a62d349d598e51c8bbe7cf6aff5719366f5669e /src/mongo | |
parent | 124ad1c022f20dbeade4d67947e328dfe4b04e20 (diff) | |
download | mongo-aeed74375c24c59fb6fb725691fe9ce547a810ab.tar.gz |
SERVER-44378 groundwork for upcoming stacktrace API
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/util/SConscript | 17 | ||||
-rw-r--r-- | src/mongo/util/exception_filter_win32.cpp | 2 | ||||
-rw-r--r-- | src/mongo/util/signal_handlers_synchronous.cpp | 2 | ||||
-rw-r--r-- | src/mongo/util/stacktrace.cpp | 23 | ||||
-rw-r--r-- | src/mongo/util/stacktrace.h | 29 | ||||
-rw-r--r-- | src/mongo/util/stacktrace_bm.cpp | 141 | ||||
-rw-r--r-- | src/mongo/util/stacktrace_json.cpp | 64 | ||||
-rw-r--r-- | src/mongo/util/stacktrace_json.h | 50 | ||||
-rw-r--r-- | src/mongo/util/stacktrace_posix.cpp | 77 | ||||
-rw-r--r-- | src/mongo/util/stacktrace_test.cpp | 60 | ||||
-rw-r--r-- | src/mongo/util/stacktrace_windows.cpp | 44 | ||||
-rw-r--r-- | src/mongo/util/stacktrace_windows.h | 47 |
12 files changed, 393 insertions, 163 deletions
diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript index 13da10e0e27..fcf9cc775ab 100644 --- a/src/mongo/util/SConscript +++ b/src/mongo/util/SConscript @@ -628,13 +628,18 @@ generateItoATable = env.Command( ) env.Alias('generated-sources', generateItoATable) -env.CppUnitTest( +stacktraceEnv = env.Clone() +if use_libunwind: + stacktraceEnv.InjectThirdParty(libraries=['unwind']) + +stacktraceEnv.CppUnitTest( target='stacktrace_test', - source=[ - 'stacktrace_test.cpp', - ], - LIBDEPS=[ - ], + source='stacktrace_test.cpp', +) + +stacktraceEnv.Benchmark( + target='stacktrace_bm', + source='stacktrace_bm.cpp', ) if use_libunwind: diff --git a/src/mongo/util/exception_filter_win32.cpp b/src/mongo/util/exception_filter_win32.cpp index ab65db4adde..bda23d4e122 100644 --- a/src/mongo/util/exception_filter_win32.cpp +++ b/src/mongo/util/exception_filter_win32.cpp @@ -46,7 +46,7 @@ #include "mongo/util/exit_code.h" #include "mongo/util/log.h" #include "mongo/util/quick_exit.h" -#include "mongo/util/stacktrace.h" +#include "mongo/util/stacktrace_windows.h" #include "mongo/util/text.h" namespace mongo { diff --git a/src/mongo/util/signal_handlers_synchronous.cpp b/src/mongo/util/signal_handlers_synchronous.cpp index 67a4e14dd68..4e47aed9e6f 100644 --- a/src/mongo/util/signal_handlers_synchronous.cpp +++ b/src/mongo/util/signal_handlers_synchronous.cpp @@ -182,7 +182,7 @@ void writeMallocFreeStreamToLog() { // must hold MallocFreeOStreamGuard to call void printSignalAndBacktrace(int signalNum) { mallocFreeOStream << "Got signal: " << signalNum << " (" << strsignal(signalNum) << ").\n"; - printStackTraceFromSignal(mallocFreeOStream); + printStackTrace(mallocFreeOStream); writeMallocFreeStreamToLog(); } diff --git a/src/mongo/util/stacktrace.cpp b/src/mongo/util/stacktrace.cpp index 0650de92848..abf4b840cb8 100644 --- a/src/mongo/util/stacktrace.cpp +++ b/src/mongo/util/stacktrace.cpp @@ -27,28 +27,7 @@ * it in the license file. */ -// stacktrace_${TARGET_OS_FAMILY}.cpp sets default log component to kControl. -// Setting kDefault to preserve previous behavior in (defunct) getStacktraceLogger(). -#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault - #include "mongo/util/stacktrace.h" #include "mongo/platform/basic.h" -#include "mongo/util/log.h" - -namespace mongo { - -void printStackTrace() { - // NOTE: We disable long-line truncation for the stack trace, because the JSON representation of - // the stack trace can sometimes exceed the long line limit. - printStackTrace(log().setIsTruncatable(false).stream()); -} - -#if defined(_WIN32) - -void printWindowsStackTrace(CONTEXT& context) { - printWindowsStackTrace(context, log().stream()); -} - -#endif // defined(_WIN32) -} // namespace mongo +namespace mongo {} // namespace mongo diff --git a/src/mongo/util/stacktrace.h b/src/mongo/util/stacktrace.h index dcb0b29b2f9..2be3842fc1a 100644 --- a/src/mongo/util/stacktrace.h +++ b/src/mongo/util/stacktrace.h @@ -35,43 +35,30 @@ #include <iosfwd> -#if defined(_WIN32) -#include "mongo/platform/windows_basic.h" // for CONTEXT -#endif - #include "mongo/base/string_data.h" namespace mongo { +namespace stack_trace { + +const size_t kFrameMax = 100; + /** Abstract sink onto which stacktrace is piecewise emitted. */ -class StackTraceSink { +class Sink { public: - StackTraceSink& operator<<(StringData v) { - doWrite(v); - return *this; - } - - StackTraceSink& operator<<(uint64_t v) { + Sink& operator<<(StringData v) { doWrite(v); return *this; } private: virtual void doWrite(StringData v) = 0; - virtual void doWrite(uint64_t v) = 0; }; +} // namespace stack_trace + // Print stack trace information to "os", default to the log stream. void printStackTrace(std::ostream& os); void printStackTrace(); -// Signal-safe variant. -void printStackTraceFromSignal(std::ostream& os); - -#if defined(_WIN32) -// Print stack trace (using a specified stack context) to "os", default to the log stream. -void printWindowsStackTrace(CONTEXT& context, std::ostream& os); -void printWindowsStackTrace(CONTEXT& context); -#endif - } // namespace mongo diff --git a/src/mongo/util/stacktrace_bm.cpp b/src/mongo/util/stacktrace_bm.cpp new file mode 100644 index 00000000000..8638e9de058 --- /dev/null +++ b/src/mongo/util/stacktrace_bm.cpp @@ -0,0 +1,141 @@ +/** + * Copyright (C) 2019-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + + +#include "mongo/platform/basic.h" + +#include <array> +#include <cstdint> +#include <functional> +#include <iostream> +#include <limits> +#include <random> +#include <sstream> +#include <string> +#include <vector> + +#include <benchmark/benchmark.h> + +#include "mongo/base/string_data.h" +#include "mongo/config.h" +#include "mongo/util/decimal_counter.h" +#include "mongo/util/stacktrace.h" + +#if defined(MONGO_USE_LIBUNWIND) +#include <libunwind.h> +#endif + + +namespace mongo { +namespace { + +struct RecursionParam { + std::function<void()> f; + std::uint64_t n; +}; + +// Re-enters itself 'p.n' times to synthesize a deep call stack, then invokes `p.f()`. +MONGO_COMPILER_NOINLINE int recursionTest(RecursionParam& p, std::uint64_t i = 0) { + if (i == p.n) { + p.f(); + } else { + recursionTest(p, i + 1); + } + return 0; +} + +void BM_Baseline(benchmark::State& state) { + size_t items = 0; + RecursionParam param; + size_t i = 0; + param.n = state.range(0); + param.f = [&] { ++i; }; + for (auto _ : state) { + benchmark::DoNotOptimize(recursionTest(param)); + ++items; + } + state.SetItemsProcessed(items); +} +BENCHMARK(BM_Baseline)->Range(1, 100); + +void BM_Print(benchmark::State& state) { + size_t items = 0; + RecursionParam param; + std::ostringstream os; + param.n = state.range(0); + param.f = [&] { + os.clear(); + printStackTrace(os); + items += param.n; + }; + for (auto _ : state) { + benchmark::DoNotOptimize(recursionTest(param)); + } + state.SetItemsProcessed(items); +} +BENCHMARK(BM_Print)->Range(1, 100); + +#if defined(MONGO_USE_LIBUNWIND) +void BM_CursorSteps(benchmark::State& state) { + size_t items = 0; + RecursionParam param; + std::ostringstream os; + param.n = state.range(0); + param.f = [&] { + unw_context_t context; + if (int r = unw_getcontext(&context); r < 0) { + std::cerr << "unw_getcontext: " << unw_strerror(r) << "\n"; + return; + } + unw_cursor_t cursor; + if (int r = unw_init_local(&cursor, &context); r < 0) { + std::cerr << "unw_init_local: " << unw_strerror(r) << "\n"; + return; + } + while (true) { + ++items; // count each unw_step as an item + if (int r = unw_step(&cursor); r < 0) { + std::cerr << "unw_step: " << unw_strerror(r) << "\n"; + return; + } else if (r == 0) { + break; + } + } + }; + for (auto _ : state) { + benchmark::DoNotOptimize(recursionTest(param)); + } + state.SetItemsProcessed(items); +} +BENCHMARK(BM_CursorSteps)->Range(1, 100); +#endif // MONGO_USE_LIBUNWIND + + +} // namespace +} // namespace mongo diff --git a/src/mongo/util/stacktrace_json.cpp b/src/mongo/util/stacktrace_json.cpp index bec3b22cb5f..96bae510bb4 100644 --- a/src/mongo/util/stacktrace_json.cpp +++ b/src/mongo/util/stacktrace_json.cpp @@ -34,12 +34,11 @@ #include "mongo/bson/bsonobj.h" #include "mongo/util/assert_util.h" -namespace mongo::stacktrace_detail { +namespace mongo::stack_trace { namespace { -constexpr StringData kHexDigits = "0123456789ABCDEF"_sd; /** - * Wrapper that streams a string-like object to a StackTraceSink, surrounded by double + * Wrapper that streams a string-like object to a Sink, surrounded by double * quotes. */ template <typename T> @@ -47,7 +46,7 @@ class Quoted { public: explicit Quoted(const T& v) : _v(v) {} - friend StackTraceSink& operator<<(StackTraceSink& sink, const Quoted& q) { + friend Sink& operator<<(Sink& sink, const Quoted& q) { return sink << kQuote << q._v << kQuote; } @@ -55,30 +54,45 @@ private: static constexpr StringData kQuote = "\""_sd; const T& _v; }; -} // namespace -StringData Hex::toHex(uint64_t x, Buf& buf) { - char* data = buf.data(); - size_t nBuf = buf.size(); - char* p = data + nBuf; +template <size_t base> +StringData kDigits; +template <> +constexpr StringData kDigits<16> = "0123456789ABCDEF"_sd; +template <> +constexpr StringData kDigits<10> = "0123456789"_sd; + +template <size_t base, typename Buf> +StringData toNumericBase(uint64_t x, Buf& buf) { + auto it = buf.rbegin(); if (!x) { - *--p = '0'; + *it++ = '0'; } else { - for (int d = 0; d < 16; ++d) { - if (!x) - break; - *--p = kHexDigits[x & 0xf]; - x >>= 4; + for (; x && it != buf.rend(); ++it) { + *it = kDigits<base>[x % base]; + x /= base; } } - return StringData(p, data + nBuf - p); + const char* p = buf.data() + (it.base() - buf.begin()); + size_t n = it - buf.rbegin(); + return StringData(p, n); +} + +} // namespace + +StringData Dec::toDec(uint64_t x, Buf& buf) { + return toNumericBase<10>(x, buf); +} + +StringData Hex::toHex(uint64_t x, Buf& buf) { + return toNumericBase<16>(x, buf); } uint64_t Hex::fromHex(StringData s) { uint64_t x = 0; for (char c : s) { char uc = std::toupper(static_cast<unsigned char>(c)); - if (size_t pos = kHexDigits.find(uc); pos == std::string::npos) { + if (size_t pos = kDigits<16>.find(uc); pos == std::string::npos) { return x; } else { x <<= 4; @@ -90,8 +104,10 @@ uint64_t Hex::fromHex(StringData s) { CheapJson::Value::Value(CheapJson* env, Kind k) : _env(env), _kind(k) { if (_kind == kObj) { + _env->indent(); _env->_sink << "{"; } else if (_kind == kArr) { + _env->indent(); _env->_sink << "["; } } @@ -99,8 +115,10 @@ CheapJson::Value::Value(CheapJson* env, Kind k) : _env(env), _kind(k) { CheapJson::Value::~Value() { if (_kind == kObj) { _env->_sink << "}"; + _env->dedent(); } else if (_kind == kArr) { _env->_sink << "]"; + _env->dedent(); } } @@ -128,7 +146,7 @@ void CheapJson::Value::append(StringData v) { void CheapJson::Value::append(uint64_t v) { _next(); - _env->_sink << v; + _env->_sink << Dec(v); } void CheapJson::Value::append(const BSONElement& be) { @@ -165,12 +183,18 @@ void CheapJson::Value::_copyBsonElementValue(const BSONElement& be) { void CheapJson::Value::_next() { _env->_sink << _sep; _sep = ","_sd; + if (_env->_pretty && (_kind == kObj || _kind == kArr)) { + _env->_sink << "\n"_sd; + for (int i = 0; i < _env->_indent; ++i) { + _env->_sink << " "_sd; + } + } } auto CheapJson::doc() -> Value { return Value(this); } -CheapJson::CheapJson(StackTraceSink& sink) : _sink(sink) {} +CheapJson::CheapJson(Sink& sink) : _sink(sink) {} -} // namespace mongo::stacktrace_detail +} // namespace mongo::stack_trace diff --git a/src/mongo/util/stacktrace_json.h b/src/mongo/util/stacktrace_json.h index 0ad322d20f5..3917e093422 100644 --- a/src/mongo/util/stacktrace_json.h +++ b/src/mongo/util/stacktrace_json.h @@ -36,16 +36,16 @@ #include "mongo/bson/bsonelement.h" #include "mongo/util/stacktrace.h" -namespace mongo::stacktrace_detail { +namespace mongo::stack_trace { /** * A utility for uint64_t <=> uppercase hex string conversions. It * can be used to produce a StringData. * - * sink << Hex(x).str(); // as a temporary + * sink << Hex(x); // as a temporary * * Hex hx(x); - * StringData sd = hx.str() // sd storage is in `hx`. + * StringData sd = hx; // sd storage is in `hx`. */ class Hex { public: @@ -57,7 +57,26 @@ public: explicit Hex(uint64_t x) : _str(toHex(x, _buf)) {} - StringData str() const { + operator StringData() const { + return _str; + } + +private: + Buf _buf; + StringData _str; +}; + +class Dec { +public: + using Buf = std::array<char, 20>; // ceil(64*log10(2)) + + static StringData toDec(uint64_t x, Buf& buf); + + static uint64_t fromDec(StringData s); + + explicit Dec(uint64_t x) : _str(toDec(x, _buf)) {} + + operator StringData() const { return _str; } @@ -71,13 +90,26 @@ class CheapJson { public: class Value; - explicit CheapJson(StackTraceSink& sink); + explicit CheapJson(Sink& sink); // Create an empty JSON document. Value doc(); + void pretty() { + _pretty = true; + } + private: - StackTraceSink& _sink; + void indent() { + ++_indent; + } + void dedent() { + --_indent; + } + + bool _pretty = false; + int _indent = 0; + Sink& _sink; }; /** @@ -121,6 +153,10 @@ public: */ void append(const BSONElement& be); + CheapJson* env() const { + return _env; + } + private: enum Kind { kNop, // A blank Value, not an aggregate, emits no punctuation. Can emit one element. @@ -138,4 +174,4 @@ private: StringData _sep; // Emitted upon append. Starts empty, then set to ",". }; -} // namespace mongo::stacktrace_detail +} // namespace mongo::stack_trace diff --git a/src/mongo/util/stacktrace_posix.cpp b/src/mongo/util/stacktrace_posix.cpp index c24fb71e092..f66d2ed01a9 100644 --- a/src/mongo/util/stacktrace_posix.cpp +++ b/src/mongo/util/stacktrace_posix.cpp @@ -51,9 +51,9 @@ #include "mongo/util/stacktrace_json.h" #include "mongo/util/version.h" +#define MONGO_STACKTRACE_BACKEND_NONE 0 #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 @@ -70,14 +70,14 @@ #endif namespace mongo { -namespace stacktrace_detail { +namespace stack_trace { namespace { constexpr int kFrameMax = 100; constexpr size_t kSymbolMax = 512; constexpr StringData kUnknownFileName = "???"_sd; -class OstreamJsonSink : public StackTraceSink { +class OstreamJsonSink : public Sink { public: explicit OstreamJsonSink(std::ostream& os) : _os(os) {} @@ -85,9 +85,6 @@ private: void doWrite(StringData v) override { _os << v; } - void doWrite(uint64_t v) override { - _os << v; - } std::ostream& _os; }; @@ -192,11 +189,9 @@ ArrayAndSize<uint64_t, kFrameMax> uniqueBases(IterationIface& source) { return bases; } -void printRawAddrsLine(IterationIface& source, - StackTraceSink& sink, - const StackTraceOptions& options) { +void printRawAddrsLine(IterationIface& source, Sink& sink, const StackTraceOptions& options) { for (source.start(source.kRaw); !source.done(); source.advance()) { - sink << " " << Hex(source.deref().address).str(); + sink << " " << Hex(source.deref().address); } } @@ -208,8 +203,8 @@ void appendJsonBacktrace(IterationIface& source, const auto& f = source.deref(); uint64_t base = f.soFile ? f.soFile->base : 0; CheapJson::Value frameObj = frames.appendObj(); - frameObj.appendKey("b").append(Hex(base).str()); - frameObj.appendKey("o").append(Hex(f.address - base).str()); + frameObj.appendKey("b").append(Hex(base)); + frameObj.appendKey("o").append(Hex(f.address - base)); if (f.symbol) { frameObj.appendKey("s").append(f.symbol->name); } @@ -262,9 +257,7 @@ void appendJsonProcessInfo(IterationIface& source, } } -void printHumanReadable(IterationIface& source, - StackTraceSink& sink, - const StackTraceOptions& options) { +void printHumanReadable(IterationIface& source, Sink& sink, const StackTraceOptions& options) { for (source.start(source.kSymbolic); !source.done(); source.advance()) { const auto& f = source.deref(); sink << " "; @@ -272,17 +265,17 @@ void printHumanReadable(IterationIface& source, sink << getBaseName(f.soFile->name); sink << "("; if (f.symbol) { - sink << f.symbol->name << "+0x" << Hex(f.address - f.symbol->base).str(); + sink << f.symbol->name << "+0x" << Hex(f.address - f.symbol->base); } else { // No symbol, so fall back to the `soFile` offset. - sink << "+0x" << Hex(f.address - f.soFile->base).str(); + sink << "+0x" << Hex(f.address - f.soFile->base); } sink << ")"; } else { // Not even shared object information, just punt with unknown filename (SERVER-43551) sink << kUnknownFileName; } - sink << " [0x" << Hex(f.address).str() << "]\n"; + sink << " [0x" << Hex(f.address) << "]\n"; } } @@ -306,9 +299,7 @@ void printHumanReadable(IterationIface& source, * analysis tool. For example, on Linux it contains a subobject named "somap", describing * the objects referenced in the "b" fields of the "backtrace" list. */ -void printStackTraceGeneric(IterationIface& source, - StackTraceSink& sink, - const StackTraceOptions& options) { +void printStackTraceGeneric(IterationIface& source, Sink& sink, const StackTraceOptions& options) { // TODO(SERVER-42670): make this asynchronous signal safe. printRawAddrsLine(source, sink, options); sink << "\n----- BEGIN BACKTRACE -----\n"; @@ -351,8 +342,7 @@ void mergeDlInfo(AddressMetadata& f) { class Iteration : public IterationIface { public: - explicit Iteration(StackTraceSink& sink, bool fromSignal) - : _sink(sink), _fromSignal(fromSignal) { + explicit Iteration(Sink& sink) : _sink(sink) { if (int r = unw_getcontext(&_context); r < 0) { _sink << "unw_getcontext: " << unw_strerror(r) << "\n"; _failed = true; @@ -368,9 +358,9 @@ private: _end = true; return; } - int r = unw_init_local2(&_cursor, &_context, _fromSignal ? UNW_INIT_SIGNAL_FRAME : 0); + int r = unw_init_local(&_cursor, &_context); if (r < 0) { - _sink << "unw_init_local2: " << unw_strerror(r) << "\n"; + _sink << "unw_init_local: " << unw_strerror(r) << "\n"; _end = true; return; } @@ -415,7 +405,8 @@ private: unw_word_t offset; if (int r = unw_get_proc_name(&_cursor, _symbolBuf, sizeof(_symbolBuf), &offset); r < 0) { - _sink << "unw_get_proc_name(" << _f.address << "): " << unw_strerror(r) << "\n"; + _sink << "unw_get_proc_name(" << Hex(_f.address) << "): " << unw_strerror(r) + << "\n"; } else { _f.symbol = NameBase{_symbolBuf, _f.address - offset}; } @@ -423,8 +414,7 @@ private: } } - StackTraceSink& _sink; - bool _fromSignal; + Sink& _sink; Flags _flags; AddressMetadata _f{}; @@ -439,8 +429,8 @@ private: }; MONGO_COMPILER_NOINLINE -void printStackTrace(StackTraceSink& sink, bool fromSignal) { - Iteration iteration(sink, fromSignal); +void printStackTrace(Sink& sink) { + Iteration iteration(sink); printStackTraceGeneric(iteration, sink, StackTraceOptions{}); } @@ -448,12 +438,12 @@ void printStackTrace(StackTraceSink& sink, bool fromSignal) { class Iteration : public IterationIface { public: - explicit Iteration(StackTraceSink& sink, bool fromSignal) { + explicit Iteration(Sink& sink) { _n = ::backtrace(_addresses.data(), _addresses.size()); if (_n == 0) { int err = errno; - sink << "Unable to collect backtrace addresses (errno: " << err << " " << strerror(err) - << ")\n"; + sink << "Unable to collect backtrace addresses (errno: " << Dec(err) << " " + << strerror(err) << ")\n"; return; } } @@ -494,33 +484,34 @@ private: }; MONGO_COMPILER_NOINLINE -void printStackTrace(StackTraceSink& sink, bool fromSignal) { - Iteration iteration(sink, fromSignal); +void printStackTrace(Sink& sink) { + Iteration iteration(sink); printStackTraceGeneric(iteration, sink, StackTraceOptions{}); } #elif MONGO_STACKTRACE_BACKEND == MONGO_STACKTRACE_BACKEND_NONE MONGO_COMPILER_NOINLINE -void printStackTrace(StackTraceSink& sink, bool fromSignal) { +void printStackTrace(Sink& sink) { sink << "This platform does not support printing stacktraces\n"; } #endif // MONGO_STACKTRACE_BACKEND } // namespace -} // namespace stacktrace_detail +} // namespace stack_trace MONGO_COMPILER_NOINLINE void printStackTrace(std::ostream& os) { - stacktrace_detail::OstreamJsonSink sink{os}; - stacktrace_detail::printStackTrace(sink, false); + stack_trace::OstreamJsonSink sink{os}; + stack_trace::printStackTrace(sink); } -MONGO_COMPILER_NOINLINE -void printStackTraceFromSignal(std::ostream& os) { - stacktrace_detail::OstreamJsonSink sink{os}; - stacktrace_detail::printStackTrace(sink, true); +void printStackTrace() { + // NOTE: We disable long-line truncation for the stack trace, because the JSON representation of + // the stack trace can sometimes exceed the long line limit. + printStackTrace(log().setIsTruncatable(false).stream()); } + } // namespace mongo diff --git a/src/mongo/util/stacktrace_test.cpp b/src/mongo/util/stacktrace_test.cpp index 3e1c47d3ade..6387aedf7f7 100644 --- a/src/mongo/util/stacktrace_test.cpp +++ b/src/mongo/util/stacktrace_test.cpp @@ -394,7 +394,7 @@ TEST(StackTrace, EarlyTraceSanity) { } } -class StringSink : public StackTraceSink { +class StringSink : public stack_trace::Sink { public: StringSink(std::string& s) : _s{s} {} @@ -403,10 +403,6 @@ private: format_to(std::back_inserter(_s), FMT_STRING("{}"), v); } - void doWrite(uint64_t v) override { - format_to(std::back_inserter(_s), FMT_STRING("{:d}"), v); - } - std::string& _s; }; @@ -416,33 +412,35 @@ public: }; TEST_F(CheapJsonTest, Appender) { + using Dec = stack_trace::Dec; + using Hex = stack_trace::Hex; std::string s; StringSink sink{s}; sink << "Hello" - << ":" << 0 << ":" << 255 << ":" << 1234567890; - ASSERT_EQ(s, "Hello:0:255:1234567890"); + << ":" << Dec(0) << ":" << Hex(255) << ":" << Dec(1234567890); + ASSERT_EQ(s, "Hello:0:FF:1234567890"); } TEST_F(CheapJsonTest, Hex) { - using Hex = stacktrace_detail::Hex; - ASSERT_EQ(Hex(0).str(), "0"); - ASSERT_EQ(Hex(0xffff).str(), "FFFF"); - ASSERT_EQ(Hex(0xfff0).str(), "FFF0"); - ASSERT_EQ(Hex(0x8000'0000'0000'0000).str(), "8000000000000000"); + using Hex = stack_trace::Hex; + ASSERT_EQ(StringData(Hex(0)), "0"); + ASSERT_EQ(StringData(Hex(0xffff)), "FFFF"); + ASSERT_EQ(Hex(0xfff0), "FFF0"); + ASSERT_EQ(Hex(0x8000'0000'0000'0000), "8000000000000000"); ASSERT_EQ(Hex::fromHex("FFFF"), 0xffff); ASSERT_EQ(Hex::fromHex("0"), 0); ASSERT_EQ(Hex::fromHex("FFFFFFFFFFFFFFFF"), 0xffff'ffff'ffff'ffff); std::string s; StringSink sink{s}; - sink << Hex(0xffff).str(); + sink << Hex(0xffff); ASSERT_EQ(s, R"(FFFF)"); } TEST_F(CheapJsonTest, DocumentObject) { std::string s; StringSink sink{s}; - stacktrace_detail::CheapJson env{sink}; + stack_trace::CheapJson env{sink}; auto doc = env.doc(); ASSERT_EQ(s, ""); { @@ -455,7 +453,7 @@ TEST_F(CheapJsonTest, DocumentObject) { TEST_F(CheapJsonTest, ScalarStringData) { std::string s; StringSink sink{s}; - stacktrace_detail::CheapJson env{sink}; + stack_trace::CheapJson env{sink}; auto doc = env.doc(); doc.append(123); ASSERT_EQ(s, R"(123)"); @@ -464,7 +462,7 @@ TEST_F(CheapJsonTest, ScalarStringData) { TEST_F(CheapJsonTest, ScalarInt) { std::string s; StringSink sink{s}; - stacktrace_detail::CheapJson env{sink}; + stack_trace::CheapJson env{sink}; auto doc = env.doc(); doc.append("hello"); ASSERT_EQ(s, R"("hello")"); @@ -473,7 +471,7 @@ TEST_F(CheapJsonTest, ScalarInt) { TEST_F(CheapJsonTest, ObjectNesting) { std::string s; StringSink sink{s}; - stacktrace_detail::CheapJson env{sink}; + stack_trace::CheapJson env{sink}; auto doc = env.doc(); { auto obj = doc.appendObj(); @@ -489,7 +487,7 @@ TEST_F(CheapJsonTest, ObjectNesting) { TEST_F(CheapJsonTest, Arrays) { std::string s; StringSink sink{s}; - stacktrace_detail::CheapJson env{sink}; + stack_trace::CheapJson env{sink}; auto doc = env.doc(); { auto obj = doc.appendObj(); @@ -508,7 +506,7 @@ TEST_F(CheapJsonTest, Arrays) { TEST_F(CheapJsonTest, AppendBSONElement) { std::string s; StringSink sink{s}; - stacktrace_detail::CheapJson env{sink}; + stack_trace::CheapJson env{sink}; { auto obj = env.doc().appendObj(); for (auto& e : fromjson(R"({"a":1,"arr":[2,123],"emptyO":{},"emptyA":[]})")) @@ -517,5 +515,29 @@ TEST_F(CheapJsonTest, AppendBSONElement) { ASSERT_EQ(s, R"({"a":1,"arr":[2,123],"emptyO":{},"emptyA":[]})"); } +TEST_F(CheapJsonTest, Pretty) { + std::string s; + StringSink sink{s}; + stack_trace::CheapJson env{sink}; + env.pretty(); + auto doc = env.doc(); + { + auto obj = doc.appendObj(); + obj.appendKey("headerKey").append(255); + { + auto inner = obj.appendKey("inner").appendObj(); + inner.appendKey("innerKey").append("hi"); + } + obj.appendKey("footerKey").append(123); + } + + ASSERT_EQ(s, R"({ + "headerKey":255, + "inner":{ + "innerKey":"hi"}, + "footerKey":123})"_sd); +} + + } // namespace } // namespace mongo diff --git a/src/mongo/util/stacktrace_windows.cpp b/src/mongo/util/stacktrace_windows.cpp index 00e734b0555..89fbb4ffcbe 100644 --- a/src/mongo/util/stacktrace_windows.cpp +++ b/src/mongo/util/stacktrace_windows.cpp @@ -57,7 +57,8 @@ namespace mongo { namespace { -const auto kPathBufferSize = 1024; + +const size_t kPathBufferSize = 1024; // On Windows the symbol handler must be initialized at process startup and cleaned up at shutdown. // This class wraps up that logic and gives access to the process handle associated with the @@ -136,8 +137,6 @@ MONGO_INITIALIZER(IntializeSymbolHandler)(::mongo::InitializerContext* ctx) { return Status::OK(); } -} // namespace - /** * Get the display name of the executable module containing the specified address. * @@ -231,26 +230,8 @@ struct TraceItem { std::string symbolAndOffset; }; -static const int maxBackTraceFrames = 100; +} // namespace -/** - * Print a stack backtrace for the current thread to the specified ostream. - */ -void printStackTrace(std::ostream& os) { - CONTEXT context; - memset(&context, 0, sizeof(context)); - context.ContextFlags = CONTEXT_CONTROL; - RtlCaptureContext(&context); - printWindowsStackTrace(context, os); -} - -/** - * Print a stack backtrace for the current thread to the specified ostream, signal-safe variant. - * (Currently the same as printStackTrace.) - */ -void printStackTraceFromSignal(std::ostream& os) { - printStackTrace(os); -} /** * Print stack trace (using a specified stack context) to "os" @@ -300,7 +281,7 @@ void printWindowsStackTrace(CONTEXT& context, std::ostream& os) { TraceItem traceItem; size_t moduleWidth = 0; size_t sourceWidth = 0; - for (size_t i = 0; i < maxBackTraceFrames; ++i) { + for (size_t i = 0; i < stack_trace::kFrameMax; ++i) { BOOL ret = StackWalk64(imageType, symbolHandler.getHandle(), GetCurrentThread(), @@ -350,4 +331,21 @@ void printWindowsStackTrace(CONTEXT& context, std::ostream& os) { } } +void printWindowsStackTrace(CONTEXT& context) { + printWindowsStackTrace(context, log(logger::LogComponent::kDefault).stream()); +} + +void printStackTrace(std::ostream& os) { + CONTEXT context; + memset(&context, 0, sizeof(context)); + context.ContextFlags = CONTEXT_CONTROL; + RtlCaptureContext(&context); + printWindowsStackTrace(context, os); +} + +void printStackTrace() { + // Disable truncation to accomodate our large JSON representation. + printStackTrace(log(logger::LogComponent::kDefault).setIsTruncatable(false).stream()); +} + } // namespace mongo diff --git a/src/mongo/util/stacktrace_windows.h b/src/mongo/util/stacktrace_windows.h new file mode 100644 index 00000000000..28a54bc7ee7 --- /dev/null +++ b/src/mongo/util/stacktrace_windows.h @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2019-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#if defined(_WIN32) + +#include <iosfwd> + +#include "mongo/platform/windows_basic.h" // for CONTEXT + +namespace mongo { + +// Print stack trace (using a specified stack context) to "os", default to the +// LogComponent::kControl stream. +void printWindowsStackTrace(CONTEXT& context, std::ostream& os); +void printWindowsStackTrace(CONTEXT& context); + +} // namespace mongo + +#endif |