Commit dd9f39e2 authored by Kenton Varda's avatar Kenton Varda

Add API to disable stack traces and use it to speed up fuzz-test and enable it on Windows.

On Linux this cuts 56% off the run time of the fuzz test (aka 2.28x speedup).

On Windows this makes it practical to run the fuzz test at all -- previously it was wayyyyyyy too slow.
parent c68ecf4f
......@@ -28,12 +28,6 @@
#include <kj/miniposix.h>
#include "test-util.h"
#if !_WIN32
// This test is super-slow on Windows seemingly due to generating exception stack traces being
// expensive.
//
// TODO(perf): Maybe create an API to disable stack traces, and use it here.
namespace capnp {
namespace _ { // private
namespace {
......@@ -47,6 +41,16 @@ bool skipFuzzTest() {
}
}
class DisableStackTraces: public kj::ExceptionCallback {
// This test generates a lot of exceptions. Performing a backtrace on each one can be slow,
// especially on Windows (where it is very, very slow). So, disable them.
public:
StackTraceMode stackTraceMode() override {
return StackTraceMode::NONE;
}
};
uint64_t traverse(AnyPointer::Reader reader);
uint64_t traverse(AnyStruct::Reader reader);
uint64_t traverse(AnyList::Reader reader);
......@@ -163,6 +167,7 @@ struct StructChecker {
KJ_TEST("fuzz-test struct pointer") {
if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder;
builder.getRoot<TestAllTypes>().setTextField("foo");
......@@ -183,6 +188,7 @@ struct ListChecker {
KJ_TEST("fuzz-test list pointer") {
if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder;
auto list = builder.getRoot<AnyPointer>().initAs<List<uint32_t>>(2);
......@@ -207,6 +213,7 @@ struct StructListChecker {
KJ_TEST("fuzz-test struct list pointer") {
if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder;
auto list = builder.getRoot<AnyPointer>().initAs<List<test::TestAllTypes>>(2);
......@@ -230,6 +237,7 @@ struct TextChecker {
KJ_TEST("fuzz-test text pointer") {
if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder;
builder.template getRoot<AnyPointer>().setAs<Text>("foo");
......@@ -238,6 +246,7 @@ KJ_TEST("fuzz-test text pointer") {
KJ_TEST("fuzz-test far pointer") {
if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder(1, AllocationStrategy::FIXED_SIZE);
initTestMessage(builder.getRoot<TestAllTypes>());
......@@ -251,6 +260,7 @@ KJ_TEST("fuzz-test far pointer") {
KJ_TEST("fuzz-test double-far pointer") {
if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder(1, AllocationStrategy::FIXED_SIZE);
......@@ -274,5 +284,3 @@ KJ_TEST("fuzz-test double-far pointer") {
} // namespace
} // namespace _ (private)
} // namespace capnp
#endif
......@@ -45,7 +45,7 @@
#include <dbghelp.h>
#endif
#if (__linux__ || __APPLE__) && defined(KJ_DEBUG)
#if (__linux__ || __APPLE__)
#include <stdio.h>
#include <pthread.h>
#endif
......@@ -156,6 +156,10 @@ ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount,
#endif
ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) {
if (getExceptionCallback().stackTraceMode() == ExceptionCallback::StackTraceMode::NONE) {
return nullptr;
}
#if _WIN32 && _M_X64
CONTEXT context;
RtlCaptureContext(&context);
......@@ -170,11 +174,11 @@ ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) {
String stringifyStackTrace(ArrayPtr<void* const> trace) {
if (trace.size() == 0) return nullptr;
if (getExceptionCallback().stackTraceMode() != ExceptionCallback::StackTraceMode::FULL) {
return nullptr;
}
#ifndef KJ_DEBUG
return nullptr;
#elif _WIN32 && _M_X64 && _MSC_VER
#if _WIN32 && _M_X64 && _MSC_VER
// Try to get file/line using SymGetLineFromAddr64(). We don't bother if we aren't on MSVC since
// this requires MSVC debug info.
......@@ -641,6 +645,10 @@ void ExceptionCallback::logMessage(
next.logMessage(severity, file, line, contextDepth, mv(text));
}
ExceptionCallback::StackTraceMode ExceptionCallback::stackTraceMode() {
return next.stackTraceMode();
}
class ExceptionCallback::RootExceptionCallback: public ExceptionCallback {
public:
RootExceptionCallback(): ExceptionCallback(*this) {}
......@@ -687,6 +695,14 @@ public:
}
}
StackTraceMode stackTraceMode() override {
#ifdef KJ_DEBUG
return StackTraceMode::FULL;
#else
return StackTraceMode::ADDRESS_ONLY;
#endif
}
private:
void logException(LogSeverity severity, Exception&& e) {
// We intentionally go back to the top exception callback on the stack because we don't want to
......
......@@ -193,6 +193,29 @@ public:
//
// The global default implementation writes the text to stderr.
enum class StackTraceMode {
FULL,
// Stringifying a stack trace will attempt to determine source file and line numbers. This may
// be expensive. For example, on Linux, this shells out to `addr2line`.
//
// This is the default in debug builds.
ADDRESS_ONLY,
// Stringifying a stack trace will only generate a list of code addresses.
//
// This is the default in release builds.
NONE
// Generating a stack trace will always return an empty array.
//
// This avoids ever unwinding the stack. On Windows in particular, the stack unwinding library
// has been observed to be pretty slow, so exception-heavy code might benefit significantly
// from this setting. (But exceptions should be rare...)
};
virtual StackTraceMode stackTraceMode();
// Returns the current preferred stack trace mode.
protected:
ExceptionCallback& next;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment