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 @@ ...@@ -22,6 +22,7 @@
#include "exception.h" #include "exception.h"
#include "debug.h" #include "debug.h"
#include <kj/compat/gtest.h> #include <kj/compat/gtest.h>
#include <stdexcept>
namespace kj { namespace kj {
namespace _ { // private namespace _ { // private
...@@ -56,6 +57,36 @@ TEST(Exception, RunCatchingExceptions) { ...@@ -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 #if !KJ_NO_EXCEPTIONS
// We skip this test when exceptions are disabled because making it no-exceptions-safe defeats // 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 // the purpose of the test: recoverable exceptions won't throw inside a destructor in the first
......
...@@ -35,6 +35,13 @@ ...@@ -35,6 +35,13 @@
#endif #endif
#include "io.h" #include "io.h"
#if !KJ_NO_RTTI
#include <typeinfo>
#if __GNUC__
#include <cxxabi.h>
#endif
#endif
#if (__linux__ && __GLIBC__ && !__UCLIBC__) || __APPLE__ #if (__linux__ && __GLIBC__ && !__UCLIBC__) || __APPLE__
#define KJ_HAS_BACKTRACE 1 #define KJ_HAS_BACKTRACE 1
#include <execinfo.h> #include <execinfo.h>
...@@ -834,7 +841,13 @@ void throwRecoverableException(kj::Exception&& exception, uint ignoreCount) { ...@@ -834,7 +841,13 @@ void throwRecoverableException(kj::Exception&& exception, uint ignoreCount) {
namespace _ { // private 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 // 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, // count of uncaught exceptions. This function is part of the C++ ABI implementation used on Linux,
...@@ -858,17 +871,16 @@ struct FakeEhGlobals { ...@@ -858,17 +871,16 @@ struct FakeEhGlobals {
uint uncaughtExceptions; uint uncaughtExceptions;
}; };
// Because of the 'extern "C"', the symbol name is not mangled and thus the namespace is effectively // LLVM's libstdc++ doesn't declare __cxa_get_globals in its cxxabi.h. GNU does. Because it is
// ignored for linking. Thus it doesn't matter that we are declaring __cxa_get_globals() in a // extern "C", the compiler wills get upset if we re-declare it even in a different namespace.
// different namespace from the ABI's definition. #if _LIBCPPABI_VERSION
extern "C" { extern "C" void* __cxa_get_globals();
FakeEhGlobals* __cxa_get_globals(); #else
} using abi::__cxa_get_globals;
#endif
uint uncaughtExceptionCount() { uint uncaughtExceptionCount() {
// TODO(perf): Use __cxa_get_globals_fast()? Requires that __cxa_get_globals() has been called return reinterpret_cast<FakeEhGlobals*>(__cxa_get_globals())->uncaughtExceptions;
// from somewhere.
return __cxa_get_globals()->uncaughtExceptions;
} }
#elif _MSC_VER #elif _MSC_VER
...@@ -918,6 +930,26 @@ void UnwindDetector::catchExceptionsAsSecondaryFaults(_::Runnable& runnable) con ...@@ -918,6 +930,26 @@ void UnwindDetector::catchExceptionsAsSecondaryFaults(_::Runnable& runnable) con
runCatchingExceptions(runnable); 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 namespace _ { // private
class RecoverableExceptionCatcher: public ExceptionCallback { class RecoverableExceptionCatcher: public ExceptionCallback {
...@@ -960,8 +992,12 @@ Maybe<Exception> runCatchingExceptions(Runnable& runnable) noexcept { ...@@ -960,8 +992,12 @@ Maybe<Exception> runCatchingExceptions(Runnable& runnable) noexcept {
return Exception(Exception::Type::FAILED, return Exception(Exception::Type::FAILED,
"(unknown)", -1, str("std::exception: ", e.what())); "(unknown)", -1, str("std::exception: ", e.what()));
} catch (...) { } catch (...) {
return Exception(Exception::Type::FAILED, #if __GNUC__ && !KJ_NO_RTTI
"(unknown)", -1, str("Unknown non-KJ exception.")); 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 #endif
} }
......
...@@ -373,4 +373,10 @@ kj::StringPtr trimSourceFilename(kj::StringPtr filename); ...@@ -373,4 +373,10 @@ kj::StringPtr trimSourceFilename(kj::StringPtr filename);
// Given a source code file name, trim off noisy prefixes like "src/" or // Given a source code file name, trim off noisy prefixes like "src/" or
// "/ekam-provider/canonical/". // "/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 } // 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