Commit 1e039743 authored by Adrian Hamza's avatar Adrian Hamza Committed by Facebook Github Bot

Make UncaughtExceptionCounter public

Summary: Make UncaughtExceptionCounter public by moving it from folly/detail to folly. I am adding a scope performance counter that works in a similar way to SCOPE_EXIT/SCOPE_ERROR and I need the UncaughtExceptionCounter functionality.

Reviewed By: yfeldblum, ericniebler

Differential Revision: D4342783

fbshipit-source-id: a1848e89cbb6340e2ac48adabf7bf76cece1b86d
parent 8f0aa1f8
......@@ -372,6 +372,7 @@ nobase_follyinclude_HEADERS = \
Try.h \
Unicode.h \
Function.h \
UncaughtExceptions.h \
Unit.h \
Uri.h \
Uri-inl.h \
......
/*
* Copyright 2016 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <exception>
#if defined(__GNUG__) || defined(__CLANG__)
#define FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS
namespace __cxxabiv1 {
// forward declaration (originally defined in unwind-cxx.h from from libstdc++)
struct __cxa_eh_globals;
// declared in cxxabi.h from libstdc++-v3
extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
}
#elif defined(_MSC_VER) && (_MSC_VER >= 1400) && \
(_MSC_VER < 1900) // MSVC++ 8.0 or greater
#define FOLLY_EXCEPTION_COUNT_USE_GETPTD
// forward declaration (originally defined in mtdll.h from MSVCRT)
struct _tiddata;
extern "C" _tiddata* _getptd(); // declared in mtdll.h from MSVCRT
#elif defined(_MSC_VER) && (_MSC_VER >= 1900) // MSVC++ 2015
#define FOLLY_EXCEPTION_COUNT_USE_STD
#else
// Raise an error when trying to use this on unsupported platforms.
#error "Unsupported platform, don't include this header."
#endif
namespace folly {
/**
* Returns the number of uncaught exceptions.
*
* This function is based on Evgeny Panasyuk's implementation from here:
* http://fburl.com/15190026
*/
inline int uncaught_exceptions() noexcept {
#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS)
// __cxa_get_globals returns a __cxa_eh_globals* (defined in unwind-cxx.h).
// The offset below returns __cxa_eh_globals::uncaughtExceptions.
return *(reinterpret_cast<unsigned int*>(static_cast<char*>(
static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*)));
#elif defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD)
// _getptd() returns a _tiddata* (defined in mtdll.h).
// The offset below returns _tiddata::_ProcessingThrow.
return *(reinterpret_cast<int*>(static_cast<char*>(
static_cast<void*>(_getptd())) + sizeof(void*) * 28 + 0x4 * 8));
#elif defined(FOLLY_EXCEPTION_COUNT_USE_STD)
return std::uncaught_exceptions();
#endif
}
} // namespaces
......@@ -18,27 +18,7 @@
#include <exception>
#if defined(__GNUG__) || defined(__CLANG__)
#define FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS
namespace __cxxabiv1 {
// forward declaration (originally defined in unwind-cxx.h from from libstdc++)
struct __cxa_eh_globals;
// declared in cxxabi.h from libstdc++-v3
extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
}
#elif defined(_MSC_VER) && (_MSC_VER >= 1400) && \
(_MSC_VER < 1900) // MSVC++ 8.0 or greater
#define FOLLY_EXCEPTION_COUNT_USE_GETPTD
// forward declaration (originally defined in mtdll.h from MSVCRT)
struct _tiddata;
extern "C" _tiddata* _getptd(); // declared in mtdll.h from MSVCRT
#elif defined(_MSC_VER) && (_MSC_VER >= 1900) // MSVC++ 2015
#define FOLLY_EXCEPTION_COUNT_USE_STD
#else
// Raise an error when trying to use this on unsupported platforms.
#error "Unsupported platform, don't include this header."
#endif
#include <folly/UncaughtExceptions.h>
namespace folly { namespace detail {
......@@ -54,41 +34,17 @@ namespace folly { namespace detail {
class UncaughtExceptionCounter {
public:
UncaughtExceptionCounter() noexcept
: exceptionCount_(getUncaughtExceptionCount()) {}
: exceptionCount_(folly::uncaught_exceptions()) {}
UncaughtExceptionCounter(const UncaughtExceptionCounter& other) noexcept
: exceptionCount_(other.exceptionCount_) {}
bool isNewUncaughtException() noexcept {
return getUncaughtExceptionCount() > exceptionCount_;
return folly::uncaught_exceptions() > exceptionCount_;
}
private:
int getUncaughtExceptionCount() noexcept;
int exceptionCount_;
};
/**
* Returns the number of uncaught exceptions.
*
* This function is based on Evgeny Panasyuk's implementation from here:
* http://fburl.com/15190026
*/
inline int UncaughtExceptionCounter::getUncaughtExceptionCount() noexcept {
#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS)
// __cxa_get_globals returns a __cxa_eh_globals* (defined in unwind-cxx.h).
// The offset below returns __cxa_eh_globals::uncaughtExceptions.
return *(reinterpret_cast<unsigned int*>(static_cast<char*>(
static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*)));
#elif defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD)
// _getptd() returns a _tiddata* (defined in mtdll.h).
// The offset below returns _tiddata::_ProcessingThrow.
return *(reinterpret_cast<int*>(static_cast<char*>(
static_cast<void*>(_getptd())) + sizeof(void*) * 28 + 0x4 * 8));
#elif defined(FOLLY_EXCEPTION_COUNT_USE_STD)
return std::uncaught_exceptions();
#endif
}
}} // namespaces
......@@ -249,6 +249,10 @@ try_test_SOURCES = TryTest.cpp
try_test_LDADD = libfollytestmain.la
TESTS += try_test
uncaught_exceptions_test_SOURCES = UncaughtExceptionsTest.cpp
uncaught_exceptions_test_LDADD = libfollytestmain.la
TESTS += uncaught_exceptions_test
unit_test_SOURCES = UnitTest.cpp
unit_test_LDADD = libfollytestmain.la
TESTS += unit_test
......
/*
* Copyright 2016 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/Conv.h>
#include <folly/UncaughtExceptions.h>
#include <folly/portability/GTest.h>
#include <glog/logging.h>
/*
* Test helper class, when goes out of scope it validaes that
* folly::uncaught_exceptions() returns the specified
* value.
*/
class Validator {
public:
Validator(int expectedCount, const std::string& msg)
: expectedCount_(expectedCount), msg_(msg) {}
// Automatic validation during destruction.
~Validator() {
validate();
}
// Invoke to validate explicitly.
void validate() {
LOG(INFO) << msg_ << ", expected " << expectedCount_ << ", is "
<< folly::uncaught_exceptions();
EXPECT_EQ(expectedCount_, folly::uncaught_exceptions()) << msg_;
}
private:
const int32_t expectedCount_;
const std::string msg_;
};
TEST(UncaughtExceptions, no_exception) {
Validator validator(0, "no_exception");
}
TEST(UncaughtExceptions, no_uncaught_exception) {
Validator validator(0, "no_uncaught_exception");
try {
throw std::runtime_error("exception");
} catch (const std::runtime_error& e) {
validator.validate();
}
}
TEST(UncaughtExceptions, one_uncaught_exception) {
try {
Validator validator(1, "one_uncaught_exception");
throw std::runtime_error("exception");
} catch (const std::runtime_error& e) {
}
}
TEST(UncaughtExceptions, catch_rethrow) {
try {
Validator validatorOuter(1, "catch_rethrow_outer");
try {
Validator validatorInner(1, "catch_rethrow_inner");
throw std::runtime_error("exception");
} catch (const std::runtime_error& e) {
EXPECT_EQ(0, folly::uncaught_exceptions());
Validator validatorRethrow(1, "catch_rethrow");
throw;
}
} catch (const std::runtime_error& e) {
EXPECT_EQ(0, folly::uncaught_exceptions());
}
}
[[noreturn]] void throwingFunction() {
Validator validator(1, "one_uncaught_exception_in_function");
throw std::runtime_error("exception");
}
TEST(UncaughtExceptions, one_uncaught_exception_in_function) {
EXPECT_THROW({ throwingFunction(); }, std::runtime_error);
}
/*
* Test helper class. Generates N wrapped classes/objects.
* The destructor N of the most outer class creates the N-1
* object, and N - 1 object destructor creating the N-2 object,
* and so on. Each destructor throws an exception after creating
* the inner object on the stack, thus the number of exceptions
* accumulates while the stack is unwinding. It's used to test
* the folly::uncaught_exceptions() with value >= 2.
*/
template <size_t N, size_t I = N>
struct ThrowInDestructor {
using InnerThrowInDestructor = ThrowInDestructor<N, I - 1>;
ThrowInDestructor() {}
~ThrowInDestructor() {
try {
InnerThrowInDestructor stackObjectThrowingOnUnwind;
Validator validator(
N - I + 1, "validating in " + folly::to<std::string>(I));
LOG(INFO) << "throwing in ~ThrowInDestructor " << I;
throw std::logic_error("inner");
} catch (const std::logic_error& e) {
LOG(INFO) << "catching in ~ThrowInDestructor " << I << " expecting "
<< N - I << ", it is " << folly::uncaught_exceptions();
EXPECT_EQ(N - I, folly::uncaught_exceptions());
}
}
};
/*
* Terminate recursion
*/
template <size_t N>
struct ThrowInDestructor<N, 0> {
ThrowInDestructor() = default;
~ThrowInDestructor() = default;
};
TEST(UncaughtExceptions, two_uncaught_exceptions) {
ThrowInDestructor<2> twoUncaughtExceptions;
}
TEST(UncaughtExceptions, ten_uncaught_exceptions) {
ThrowInDestructor<10> twoUncaughtExceptions;
}
struct ThrowingConstructor {
[[noreturn]] ThrowingConstructor() noexcept(false) {
throw std::runtime_error("exception");
}
};
struct InheritsThrowingConstructor : public Validator,
public ThrowingConstructor {
InheritsThrowingConstructor() try
: Validator(1, "one_exception_in_ctor_initializer_expression"),
ThrowingConstructor() {
} catch (...) {
// This is being re-thrown once the catch block ends, so I guess
// it's similar to a catch/throw; (re-throw) behavior and thus the value
// is 0.
EXPECT_EQ(0, folly::uncaught_exceptions());
}
};
TEST(UncaughtExceptions, one_exception_in_ctor_initializer_expression) {
EXPECT_THROW(
{ InheritsThrowingConstructor inheritsThrowingConstructor; },
std::runtime_error);
}
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