|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // 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 | 
|  |  | 
|  | #ifdef _MSC_VER | 
|  | #pragma warning(push) | 
|  | #pragma warning(disable: 4640) // '%s' construction of local static object is not thread safe (/Zc:threadSafeInit-) | 
|  | #endif // _MSC_VER | 
|  | inline MemCounter* getGlobalMemCounter() { | 
|  | static MemCounter counter((MemCounter::MemCounterCtorArg_())); | 
|  | return &counter; | 
|  | } | 
|  | #ifdef _MSC_VER | 
|  | #pragma warning(pop) | 
|  | #endif | 
|  |  | 
|  | 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 */ |