From: Adrian Hamza Date: Tue, 27 Dec 2016 22:18:20 +0000 (-0800) Subject: Make UncaughtExceptionCounter public X-Git-Tag: v2017.03.06.00~159 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=1e03974395c7007e75ed7847ad7d28b36a1c3a1b;p=folly.git 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 --- diff --git a/folly/Makefile.am b/folly/Makefile.am index 0277a341..085418ce 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -372,6 +372,7 @@ nobase_follyinclude_HEADERS = \ Try.h \ Unicode.h \ Function.h \ + UncaughtExceptions.h \ Unit.h \ Uri.h \ Uri-inl.h \ diff --git a/folly/UncaughtExceptions.h b/folly/UncaughtExceptions.h new file mode 100644 index 00000000..e45c52df --- /dev/null +++ b/folly/UncaughtExceptions.h @@ -0,0 +1,66 @@ +/* + * 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 + +#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(static_cast( + static_cast(__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(static_cast( + static_cast(_getptd())) + sizeof(void*) * 28 + 0x4 * 8)); +#elif defined(FOLLY_EXCEPTION_COUNT_USE_STD) + return std::uncaught_exceptions(); +#endif +} + +} // namespaces diff --git a/folly/detail/UncaughtExceptionCounter.h b/folly/detail/UncaughtExceptionCounter.h index 0fb76c7a..139e991a 100644 --- a/folly/detail/UncaughtExceptionCounter.h +++ b/folly/detail/UncaughtExceptionCounter.h @@ -18,27 +18,7 @@ #include -#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 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(static_cast( - static_cast(__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(static_cast( - static_cast(_getptd())) + sizeof(void*) * 28 + 0x4 * 8)); -#elif defined(FOLLY_EXCEPTION_COUNT_USE_STD) - return std::uncaught_exceptions(); -#endif -} - }} // namespaces diff --git a/folly/test/Makefile.am b/folly/test/Makefile.am index e082dbb3..f2785b38 100644 --- a/folly/test/Makefile.am +++ b/folly/test/Makefile.am @@ -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 diff --git a/folly/test/UncaughtExceptionsTest.cpp b/folly/test/UncaughtExceptionsTest.cpp new file mode 100644 index 00000000..6a49d65d --- /dev/null +++ b/folly/test/UncaughtExceptionsTest.cpp @@ -0,0 +1,164 @@ +/* + * 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 +#include +#include +#include + +/* + * 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 +struct ThrowInDestructor { + using InnerThrowInDestructor = ThrowInDestructor; + ThrowInDestructor() {} + + ~ThrowInDestructor() { + try { + InnerThrowInDestructor stackObjectThrowingOnUnwind; + Validator validator( + N - I + 1, "validating in " + folly::to(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 +struct ThrowInDestructor { + 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); +}