teak-llvm/libcxx/test/support/count_new.hpp
Chandler Carruth 57b08b0944 Update more file headers across all of the LLVM projects in the monorepo
to reflect the new license. These used slightly different spellings that
defeated my regular expressions.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351648
2019-01-19 10:56:40 +00:00

486 lines
12 KiB
C++

//===----------------------------------------------------------------------===//
//
// 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_HPP
#define COUNT_NEW_HPP
# 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_HPP */