Make UncaughtExceptionCounter public
authorAdrian Hamza <adriah@fb.com>
Tue, 27 Dec 2016 22:18:20 +0000 (14:18 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Tue, 27 Dec 2016 22:34:44 +0000 (14:34 -0800)
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

folly/Makefile.am
folly/UncaughtExceptions.h [new file with mode: 0644]
folly/detail/UncaughtExceptionCounter.h
folly/test/Makefile.am
folly/test/UncaughtExceptionsTest.cpp [new file with mode: 0644]

index 0277a34171d1a4327f26f9fcb3a609b266c37d65..085418cea2631833da012541b46ecf90cf82f7b7 100644 (file)
@@ -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 (file)
index 0000000..e45c52d
--- /dev/null
@@ -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 <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
index 0fb76c7a3d035751ada22a0bbf2b35a5f3ede5b7..139e991a8a7a3e006ec424bb23441fc350f2e0b3 100644 (file)
 
 #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
index e082dbb3f677ba2fab7bb70aee3658776278dd78..f2785b38704c0760e4cc8995c72c04dd0691e90e 100644 (file)
@@ -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 (file)
index 0000000..6a49d65
--- /dev/null
@@ -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 <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);
+}