Unverified Commit 236e54d5 authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #645 from capnproto/unknown-exception

When catching an unknown exception type, at least log the type.
parents b0775d6d f8ccf160
......@@ -22,6 +22,7 @@
#include "exception.h"
#include "debug.h"
#include <kj/compat/gtest.h>
#include <stdexcept>
namespace kj {
namespace _ { // private
......@@ -56,6 +57,36 @@ TEST(Exception, RunCatchingExceptions) {
}
}
#if !KJ_NO_EXCEPTIONS
TEST(Exception, RunCatchingExceptionsStdException) {
Maybe<Exception> e = kj::runCatchingExceptions([&]() {
throw std::logic_error("foo");
});
KJ_IF_MAYBE(ex, e) {
EXPECT_EQ("std::exception: foo", ex->getDescription());
} else {
ADD_FAILURE() << "Expected exception";
}
}
TEST(Exception, RunCatchingExceptionsOtherException) {
Maybe<Exception> e = kj::runCatchingExceptions([&]() {
throw 123;
});
KJ_IF_MAYBE(ex, e) {
#if __GNUC__ && !KJ_NO_RTTI
EXPECT_EQ("unknown non-KJ exception of type: int", ex->getDescription());
#else
EXPECT_EQ("unknown non-KJ exception", ex->getDescription());
#endif
} else {
ADD_FAILURE() << "Expected exception";
}
}
#endif
#if !KJ_NO_EXCEPTIONS
// We skip this test when exceptions are disabled because making it no-exceptions-safe defeats
// the purpose of the test: recoverable exceptions won't throw inside a destructor in the first
......
......@@ -35,6 +35,13 @@
#endif
#include "io.h"
#if !KJ_NO_RTTI
#include <typeinfo>
#if __GNUC__
#include <cxxabi.h>
#endif
#endif
#if (__linux__ && __GLIBC__ && !__UCLIBC__) || __APPLE__
#define KJ_HAS_BACKTRACE 1
#include <execinfo.h>
......@@ -834,7 +841,13 @@ void throwRecoverableException(kj::Exception&& exception, uint ignoreCount) {
namespace _ { // private
#if __GNUC__
#if __cplusplus >= 201703L
uint uncaughtExceptionCount() {
return std::uncaught_exceptions();
}
#elif __GNUC__
// Horrible -- but working -- hack: We can dig into __cxa_get_globals() in order to extract the
// count of uncaught exceptions. This function is part of the C++ ABI implementation used on Linux,
......@@ -858,17 +871,16 @@ struct FakeEhGlobals {
uint uncaughtExceptions;
};
// Because of the 'extern "C"', the symbol name is not mangled and thus the namespace is effectively
// ignored for linking. Thus it doesn't matter that we are declaring __cxa_get_globals() in a
// different namespace from the ABI's definition.
extern "C" {
FakeEhGlobals* __cxa_get_globals();
}
// LLVM's libstdc++ doesn't declare __cxa_get_globals in its cxxabi.h. GNU does. Because it is
// extern "C", the compiler wills get upset if we re-declare it even in a different namespace.
#if _LIBCPPABI_VERSION
extern "C" void* __cxa_get_globals();
#else
using abi::__cxa_get_globals;
#endif
uint uncaughtExceptionCount() {
// TODO(perf): Use __cxa_get_globals_fast()? Requires that __cxa_get_globals() has been called
// from somewhere.
return __cxa_get_globals()->uncaughtExceptions;
return reinterpret_cast<FakeEhGlobals*>(__cxa_get_globals())->uncaughtExceptions;
}
#elif _MSC_VER
......@@ -918,6 +930,26 @@ void UnwindDetector::catchExceptionsAsSecondaryFaults(_::Runnable& runnable) con
runCatchingExceptions(runnable);
}
#if __GNUC__ && !KJ_NO_RTTI
static kj::String demangleTypeName(const char* name) {
if (name == nullptr) return kj::heapString("(nil)");
int status;
char* buf = abi::__cxa_demangle(name, nullptr, nullptr, &status);
kj::String result = kj::heapString(buf == nullptr ? name : buf);
free(buf);
return kj::mv(result);
}
kj::String getCaughtExceptionType() {
return demangleTypeName(abi::__cxa_current_exception_type()->name());
}
#else
kj::String getCaughtExceptionType() {
return kj::heapString("(unknown)");
}
#endif
namespace _ { // private
class RecoverableExceptionCatcher: public ExceptionCallback {
......@@ -960,8 +992,12 @@ Maybe<Exception> runCatchingExceptions(Runnable& runnable) noexcept {
return Exception(Exception::Type::FAILED,
"(unknown)", -1, str("std::exception: ", e.what()));
} catch (...) {
return Exception(Exception::Type::FAILED,
"(unknown)", -1, str("Unknown non-KJ exception."));
#if __GNUC__ && !KJ_NO_RTTI
return Exception(Exception::Type::FAILED, "(unknown)", -1, str(
"unknown non-KJ exception of type: ", getCaughtExceptionType()));
#else
return Exception(Exception::Type::FAILED, "(unknown)", -1, str("unknown non-KJ exception"));
#endif
}
#endif
}
......
......@@ -373,4 +373,10 @@ kj::StringPtr trimSourceFilename(kj::StringPtr filename);
// Given a source code file name, trim off noisy prefixes like "src/" or
// "/ekam-provider/canonical/".
kj::String getCaughtExceptionType();
// Utility function which attempts to return the human-readable type name of the exception
// currently being thrown. This can be called inside a catch block, including a catch (...) block,
// for the purpose of error logging. This function is best-effort; on some platforms it may simply
// return "(unknown)".
} // namespace kj
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