mirror of
https://github.com/darlinghq/darling-libcxx.git
synced 2024-12-14 07:18:38 +00:00
8c4dc32424
git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@273819 91177308-0d34-0410-b5e6-96231b3b80d8
323 lines
7.7 KiB
C++
323 lines
7.7 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
|
// Source Licenses. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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 last_new_size;
|
|
|
|
int outstanding_array_new;
|
|
int new_array_called;
|
|
int delete_array_called;
|
|
int last_new_array_size;
|
|
|
|
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 deleteCalled(void * p)
|
|
{
|
|
assert(p);
|
|
--outstanding_new;
|
|
++delete_called;
|
|
}
|
|
|
|
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 deleteArrayCalled(void * p)
|
|
{
|
|
assert(p);
|
|
--outstanding_array_new;
|
|
++delete_array_called;
|
|
}
|
|
|
|
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;
|
|
last_new_size = 0;
|
|
|
|
outstanding_array_new = 0;
|
|
new_array_called = 0;
|
|
delete_array_called = 0;
|
|
last_new_array_size = 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 checkLastNewSizeEq(int n) const
|
|
{
|
|
return disable_checking || n == last_new_size;
|
|
}
|
|
|
|
bool checkLastNewSizeNotEq(int n) const
|
|
{
|
|
return disable_checking || n != last_new_size;
|
|
}
|
|
|
|
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 checkLastNewArraySizeEq(int n) const
|
|
{
|
|
return disable_checking || n == last_new_array_size;
|
|
}
|
|
|
|
bool checkLastNewArraySizeNotEq(int n) const
|
|
{
|
|
return disable_checking || n != last_new_array_size;
|
|
}
|
|
};
|
|
|
|
#ifdef DISABLE_NEW_COUNT
|
|
const bool MemCounter::disable_checking = true;
|
|
#else
|
|
const bool MemCounter::disable_checking = false;
|
|
#endif
|
|
|
|
MemCounter globalMemCounter((MemCounter::MemCounterCtorArg_()));
|
|
|
|
#ifndef DISABLE_NEW_COUNT
|
|
void* operator new(std::size_t s) throw(std::bad_alloc)
|
|
{
|
|
globalMemCounter.newCalled(s);
|
|
void* ret = std::malloc(s);
|
|
if (ret == nullptr)
|
|
detail::throw_bad_alloc_helper();
|
|
return ret;
|
|
}
|
|
|
|
void operator delete(void* p) throw()
|
|
{
|
|
globalMemCounter.deleteCalled(p);
|
|
std::free(p);
|
|
}
|
|
|
|
|
|
void* operator new[](std::size_t s) throw(std::bad_alloc)
|
|
{
|
|
globalMemCounter.newArrayCalled(s);
|
|
return operator new(s);
|
|
}
|
|
|
|
|
|
void operator delete[](void* p) throw()
|
|
{
|
|
globalMemCounter.deleteArrayCalled(p);
|
|
operator delete(p);
|
|
}
|
|
|
|
#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(m_outstanding_new_on_init));
|
|
std::size_t Expect = m_new_count_on_init + m_req_alloc;
|
|
assert(globalMemCounter.checkNewCalledEq(Expect) ||
|
|
(!m_exactly && globalMemCounter.checkNewCalledGreaterThan(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 */
|