// Copyright 2018 Hans Dembinski // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include struct tracing_allocator_db : std::pair { template auto& at() { return map_[&BOOST_CORE_TYPEID(T)]; } void clear() { map_.clear(); this->first = 0; this->second = 0; } int failure_countdown = -1; bool tracing = false; template void log(Ts&&... ts) { if (!tracing) return; log_impl(std::forward(ts)...); std::cerr << std::endl; } std::size_t size() const { return map_.size(); } private: using map_t = std::unordered_map>; map_t map_; BOOST_ATTRIBUTE_UNUSED inline void log_impl() {} template void log_impl(T&& t, Ts&&... ts) { std::cerr << t; log_impl(std::forward(ts)...); } }; template struct tracing_allocator { using value_type = T; tracing_allocator_db* db = nullptr; tracing_allocator() noexcept = default; tracing_allocator(const tracing_allocator&) noexcept = default; tracing_allocator(tracing_allocator&&) noexcept = default; tracing_allocator(tracing_allocator_db& x) noexcept : db(&x) {} template tracing_allocator(const tracing_allocator& a) noexcept : db(a.db) {} template tracing_allocator& operator=(const tracing_allocator& a) noexcept { db = a.db; return *this; } ~tracing_allocator() noexcept {} T* allocate(std::size_t n) { if (db) { if (db->failure_countdown >= 0) { const auto count = db->failure_countdown--; db->log("allocator +", n, " ", boost::histogram::detail::type_name(), " [failure in ", count, "]"); if (count == 0) BOOST_THROW_EXCEPTION(std::bad_alloc{}); } else db->log("allocator +", n, " ", boost::histogram::detail::type_name()); auto& p = db->at(); p.first += static_cast(n); p.second += static_cast(n); db->first += static_cast(n * sizeof(T)); db->second += static_cast(n * sizeof(T)); } return static_cast(::operator new(n * sizeof(T))); } void deallocate(T* p, std::size_t n) { if (db) { db->at().first -= static_cast(n); db->first -= static_cast(n * sizeof(T)); db->log("allocator -", n, " ", boost::histogram::detail::type_name()); } ::operator delete((void*)p); } template void construct(T* p, Ts&&... ts) { if (db) { if (db->failure_countdown >= 0) { const auto count = db->failure_countdown--; db->log("allocator construct ", boost::histogram::detail::type_name(), "[ failure in ", count, "]"); if (count == 0) BOOST_THROW_EXCEPTION(std::bad_alloc{}); } else db->log("allocator construct ", boost::histogram::detail::type_name()); } ::new (static_cast(p)) T(std::forward(ts)...); } void destroy(T* p) { if (db) db->log("allocator destroy ", boost::histogram::detail::type_name()); p->~T(); } }; template constexpr bool operator==(const tracing_allocator&, const tracing_allocator&) noexcept { return true; } template constexpr bool operator!=(const tracing_allocator& t, const tracing_allocator& u) noexcept { return !operator==(t, u); }