summaryrefslogtreecommitdiffstats
path: root/libcxx/test/support/count_new.h
diff options
context:
space:
mode:
Diffstat (limited to 'libcxx/test/support/count_new.h')
-rw-r--r--libcxx/test/support/count_new.h485
1 files changed, 485 insertions, 0 deletions
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
new file mode 100644
index 00000000000..9313d028bb9
--- /dev/null
+++ b/libcxx/test/support/count_new.h
@@ -0,0 +1,485 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef COUNT_NEW_H
+#define COUNT_NEW_H
+
+# include <cstdlib>
+# include <cassert>
+# include <new>
+
+#include "test_macros.h"
+
+#if defined(TEST_HAS_SANITIZERS)
+#define DISABLE_NEW_COUNT
+#endif
+
+namespace detail
+{
+ TEST_NORETURN
+ inline void throw_bad_alloc_helper() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ throw std::bad_alloc();
+#else
+ std::abort();
+#endif
+ }
+}
+
+class MemCounter
+{
+public:
+ // Make MemCounter super hard to accidentally construct or copy.
+ class MemCounterCtorArg_ {};
+ explicit MemCounter(MemCounterCtorArg_) { reset(); }
+
+private:
+ MemCounter(MemCounter const &);
+ MemCounter & operator=(MemCounter const &);
+
+public:
+ // All checks return true when disable_checking is enabled.
+ static const bool disable_checking;
+
+ // Disallow any allocations from occurring. Useful for testing that
+ // code doesn't perform any allocations.
+ bool disable_allocations;
+
+ // number of allocations to throw after. Default (unsigned)-1. If
+ // throw_after has the default value it will never be decremented.
+ static const unsigned never_throw_value = static_cast<unsigned>(-1);
+ unsigned throw_after;
+
+ int outstanding_new;
+ int new_called;
+ int delete_called;
+ int aligned_new_called;
+ int aligned_delete_called;
+ std::size_t last_new_size;
+ std::size_t last_new_align;
+ std::size_t last_delete_align;
+
+ int outstanding_array_new;
+ int new_array_called;
+ int delete_array_called;
+ int aligned_new_array_called;
+ int aligned_delete_array_called;
+ std::size_t last_new_array_size;
+ std::size_t last_new_array_align;
+ std::size_t last_delete_array_align;
+
+public:
+ void newCalled(std::size_t s)
+ {
+ assert(disable_allocations == false);
+ assert(s);
+ if (throw_after == 0) {
+ throw_after = never_throw_value;
+ detail::throw_bad_alloc_helper();
+ } else if (throw_after != never_throw_value) {
+ --throw_after;
+ }
+ ++new_called;
+ ++outstanding_new;
+ last_new_size = s;
+ }
+
+ void alignedNewCalled(std::size_t s, std::size_t a) {
+ newCalled(s);
+ ++aligned_new_called;
+ last_new_align = a;
+ }
+
+ void deleteCalled(void * p)
+ {
+ assert(p);
+ --outstanding_new;
+ ++delete_called;
+ }
+
+ void alignedDeleteCalled(void *p, std::size_t a) {
+ deleteCalled(p);
+ ++aligned_delete_called;
+ last_delete_align = a;
+ }
+
+ void newArrayCalled(std::size_t s)
+ {
+ assert(disable_allocations == false);
+ assert(s);
+ if (throw_after == 0) {
+ throw_after = never_throw_value;
+ detail::throw_bad_alloc_helper();
+ } else {
+ // don't decrement throw_after here. newCalled will end up doing that.
+ }
+ ++outstanding_array_new;
+ ++new_array_called;
+ last_new_array_size = s;
+ }
+
+ void alignedNewArrayCalled(std::size_t s, std::size_t a) {
+ newArrayCalled(s);
+ ++aligned_new_array_called;
+ last_new_array_align = a;
+ }
+
+ void deleteArrayCalled(void * p)
+ {
+ assert(p);
+ --outstanding_array_new;
+ ++delete_array_called;
+ }
+
+ void alignedDeleteArrayCalled(void * p, std::size_t a) {
+ deleteArrayCalled(p);
+ ++aligned_delete_array_called;
+ last_delete_array_align = a;
+ }
+
+ void disableAllocations()
+ {
+ disable_allocations = true;
+ }
+
+ void enableAllocations()
+ {
+ disable_allocations = false;
+ }
+
+ void reset()
+ {
+ disable_allocations = false;
+ throw_after = never_throw_value;
+
+ outstanding_new = 0;
+ new_called = 0;
+ delete_called = 0;
+ aligned_new_called = 0;
+ aligned_delete_called = 0;
+ last_new_size = 0;
+ last_new_align = 0;
+
+ outstanding_array_new = 0;
+ new_array_called = 0;
+ delete_array_called = 0;
+ aligned_new_array_called = 0;
+ aligned_delete_array_called = 0;
+ last_new_array_size = 0;
+ last_new_array_align = 0;
+ }
+
+public:
+ bool checkOutstandingNewEq(int n) const
+ {
+ return disable_checking || n == outstanding_new;
+ }
+
+ bool checkOutstandingNewNotEq(int n) const
+ {
+ return disable_checking || n != outstanding_new;
+ }
+
+ bool checkNewCalledEq(int n) const
+ {
+ return disable_checking || n == new_called;
+ }
+
+ bool checkNewCalledNotEq(int n) const
+ {
+ return disable_checking || n != new_called;
+ }
+
+ bool checkNewCalledGreaterThan(int n) const
+ {
+ return disable_checking || new_called > n;
+ }
+
+ bool checkDeleteCalledEq(int n) const
+ {
+ return disable_checking || n == delete_called;
+ }
+
+ bool checkDeleteCalledNotEq(int n) const
+ {
+ return disable_checking || n != delete_called;
+ }
+
+ bool checkAlignedNewCalledEq(int n) const
+ {
+ return disable_checking || n == aligned_new_called;
+ }
+
+ bool checkAlignedNewCalledNotEq(int n) const
+ {
+ return disable_checking || n != aligned_new_called;
+ }
+
+ bool checkAlignedNewCalledGreaterThan(int n) const
+ {
+ return disable_checking || aligned_new_called > n;
+ }
+
+ bool checkAlignedDeleteCalledEq(int n) const
+ {
+ return disable_checking || n == aligned_delete_called;
+ }
+
+ bool checkAlignedDeleteCalledNotEq(int n) const
+ {
+ return disable_checking || n != aligned_delete_called;
+ }
+
+ bool checkLastNewSizeEq(std::size_t n) const
+ {
+ return disable_checking || n == last_new_size;
+ }
+
+ bool checkLastNewSizeNotEq(std::size_t n) const
+ {
+ return disable_checking || n != last_new_size;
+ }
+
+ bool checkLastNewAlignEq(std::size_t n) const
+ {
+ return disable_checking || n == last_new_align;
+ }
+
+ bool checkLastNewAlignNotEq(std::size_t n) const
+ {
+ return disable_checking || n != last_new_align;
+ }
+
+ bool checkLastDeleteAlignEq(std::size_t n) const
+ {
+ return disable_checking || n == last_delete_align;
+ }
+
+ bool checkLastDeleteAlignNotEq(std::size_t n) const
+ {
+ return disable_checking || n != last_delete_align;
+ }
+
+ bool checkOutstandingArrayNewEq(int n) const
+ {
+ return disable_checking || n == outstanding_array_new;
+ }
+
+ bool checkOutstandingArrayNewNotEq(int n) const
+ {
+ return disable_checking || n != outstanding_array_new;
+ }
+
+ bool checkNewArrayCalledEq(int n) const
+ {
+ return disable_checking || n == new_array_called;
+ }
+
+ bool checkNewArrayCalledNotEq(int n) const
+ {
+ return disable_checking || n != new_array_called;
+ }
+
+ bool checkDeleteArrayCalledEq(int n) const
+ {
+ return disable_checking || n == delete_array_called;
+ }
+
+ bool checkDeleteArrayCalledNotEq(int n) const
+ {
+ return disable_checking || n != delete_array_called;
+ }
+
+ bool checkAlignedNewArrayCalledEq(int n) const
+ {
+ return disable_checking || n == aligned_new_array_called;
+ }
+
+ bool checkAlignedNewArrayCalledNotEq(int n) const
+ {
+ return disable_checking || n != aligned_new_array_called;
+ }
+
+ bool checkAlignedNewArrayCalledGreaterThan(int n) const
+ {
+ return disable_checking || aligned_new_array_called > n;
+ }
+
+ bool checkAlignedDeleteArrayCalledEq(int n) const
+ {
+ return disable_checking || n == aligned_delete_array_called;
+ }
+
+ bool checkAlignedDeleteArrayCalledNotEq(int n) const
+ {
+ return disable_checking || n != aligned_delete_array_called;
+ }
+
+ bool checkLastNewArraySizeEq(std::size_t n) const
+ {
+ return disable_checking || n == last_new_array_size;
+ }
+
+ bool checkLastNewArraySizeNotEq(std::size_t n) const
+ {
+ return disable_checking || n != last_new_array_size;
+ }
+
+ bool checkLastNewArrayAlignEq(std::size_t n) const
+ {
+ return disable_checking || n == last_new_array_align;
+ }
+
+ bool checkLastNewArrayAlignNotEq(std::size_t n) const
+ {
+ return disable_checking || n != last_new_array_align;
+ }
+};
+
+#ifdef DISABLE_NEW_COUNT
+ const bool MemCounter::disable_checking = true;
+#else
+ const bool MemCounter::disable_checking = false;
+#endif
+
+inline MemCounter* getGlobalMemCounter() {
+ static MemCounter counter((MemCounter::MemCounterCtorArg_()));
+ return &counter;
+}
+
+MemCounter &globalMemCounter = *getGlobalMemCounter();
+
+#ifndef DISABLE_NEW_COUNT
+void* operator new(std::size_t s) TEST_THROW_SPEC(std::bad_alloc)
+{
+ getGlobalMemCounter()->newCalled(s);
+ void* ret = std::malloc(s);
+ if (ret == nullptr)
+ detail::throw_bad_alloc_helper();
+ return ret;
+}
+
+void operator delete(void* p) TEST_NOEXCEPT
+{
+ getGlobalMemCounter()->deleteCalled(p);
+ std::free(p);
+}
+
+void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc)
+{
+ getGlobalMemCounter()->newArrayCalled(s);
+ return operator new(s);
+}
+
+void operator delete[](void* p) TEST_NOEXCEPT
+{
+ getGlobalMemCounter()->deleteArrayCalled(p);
+ operator delete(p);
+}
+
+#ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
+#if defined(_LIBCPP_MSVCRT_LIKE) || \
+ (!defined(_LIBCPP_VERSION) && defined(_WIN32))
+#define USE_ALIGNED_ALLOC
+#endif
+
+void* operator new(std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
+ const std::size_t a = static_cast<std::size_t>(av);
+ getGlobalMemCounter()->alignedNewCalled(s, a);
+ void *ret;
+#ifdef USE_ALIGNED_ALLOC
+ ret = _aligned_malloc(s, a);
+#else
+ posix_memalign(&ret, a, s);
+#endif
+ if (ret == nullptr)
+ detail::throw_bad_alloc_helper();
+ return ret;
+}
+
+void operator delete(void *p, std::align_val_t av) TEST_NOEXCEPT {
+ const std::size_t a = static_cast<std::size_t>(av);
+ getGlobalMemCounter()->alignedDeleteCalled(p, a);
+ if (p) {
+#ifdef USE_ALIGNED_ALLOC
+ ::_aligned_free(p);
+#else
+ ::free(p);
+#endif
+ }
+}
+
+void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
+ const std::size_t a = static_cast<std::size_t>(av);
+ getGlobalMemCounter()->alignedNewArrayCalled(s, a);
+ return operator new(s, av);
+}
+
+void operator delete[](void *p, std::align_val_t av) TEST_NOEXCEPT {
+ const std::size_t a = static_cast<std::size_t>(av);
+ getGlobalMemCounter()->alignedDeleteArrayCalled(p, a);
+ return operator delete(p, av);
+}
+
+#endif // TEST_HAS_NO_ALIGNED_ALLOCATION
+
+#endif // DISABLE_NEW_COUNT
+
+struct DisableAllocationGuard {
+ explicit DisableAllocationGuard(bool disable = true) : m_disabled(disable)
+ {
+ // Don't re-disable if already disabled.
+ if (globalMemCounter.disable_allocations == true) m_disabled = false;
+ if (m_disabled) globalMemCounter.disableAllocations();
+ }
+
+ void release() {
+ if (m_disabled) globalMemCounter.enableAllocations();
+ m_disabled = false;
+ }
+
+ ~DisableAllocationGuard() {
+ release();
+ }
+
+private:
+ bool m_disabled;
+
+ DisableAllocationGuard(DisableAllocationGuard const&);
+ DisableAllocationGuard& operator=(DisableAllocationGuard const&);
+};
+
+struct RequireAllocationGuard {
+ explicit RequireAllocationGuard(std::size_t RequireAtLeast = 1)
+ : m_req_alloc(RequireAtLeast),
+ m_new_count_on_init(globalMemCounter.new_called),
+ m_outstanding_new_on_init(globalMemCounter.outstanding_new),
+ m_exactly(false)
+ {
+ }
+
+ void requireAtLeast(std::size_t N) { m_req_alloc = N; m_exactly = false; }
+ void requireExactly(std::size_t N) { m_req_alloc = N; m_exactly = true; }
+
+ ~RequireAllocationGuard() {
+ assert(globalMemCounter.checkOutstandingNewEq(static_cast<int>(m_outstanding_new_on_init)));
+ std::size_t Expect = m_new_count_on_init + m_req_alloc;
+ assert(globalMemCounter.checkNewCalledEq(static_cast<int>(Expect)) ||
+ (!m_exactly && globalMemCounter.checkNewCalledGreaterThan(static_cast<int>(Expect))));
+ }
+
+private:
+ std::size_t m_req_alloc;
+ const std::size_t m_new_count_on_init;
+ const std::size_t m_outstanding_new_on_init;
+ bool m_exactly;
+ RequireAllocationGuard(RequireAllocationGuard const&);
+ RequireAllocationGuard& operator=(RequireAllocationGuard const&);
+};
+
+#endif /* COUNT_NEW_H */
OpenPOWER on IntegriCloud