summaryrefslogtreecommitdiff
path: root/src/mongo/util
diff options
context:
space:
mode:
authorBilly Donahue <billy.donahue@mongodb.com>2019-11-08 21:58:51 +0000
committerevergreen <evergreen@mongodb.com>2019-11-08 21:58:51 +0000
commitaeed74375c24c59fb6fb725691fe9ce547a810ab (patch)
tree0a62d349d598e51c8bbe7cf6aff5719366f5669e /src/mongo/util
parent124ad1c022f20dbeade4d67947e328dfe4b04e20 (diff)
downloadmongo-aeed74375c24c59fb6fb725691fe9ce547a810ab.tar.gz
SERVER-44378 groundwork for upcoming stacktrace API
Diffstat (limited to 'src/mongo/util')
-rw-r--r--src/mongo/util/SConscript17
-rw-r--r--src/mongo/util/exception_filter_win32.cpp2
-rw-r--r--src/mongo/util/signal_handlers_synchronous.cpp2
-rw-r--r--src/mongo/util/stacktrace.cpp23
-rw-r--r--src/mongo/util/stacktrace.h29
-rw-r--r--src/mongo/util/stacktrace_bm.cpp141
-rw-r--r--src/mongo/util/stacktrace_json.cpp64
-rw-r--r--src/mongo/util/stacktrace_json.h50
-rw-r--r--src/mongo/util/stacktrace_posix.cpp77
-rw-r--r--src/mongo/util/stacktrace_test.cpp60
-rw-r--r--src/mongo/util/stacktrace_windows.cpp44
-rw-r--r--src/mongo/util/stacktrace_windows.h47
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