1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
|
// Copyright (C) 2015-2025 Jonathan Müller and foonathan/memory contributors
// SPDX-License-Identifier: Zlib
#ifndef FOONATHAN_MEMORY_DEBUG_HELPERS_HPP_INCLUDED
#define FOONATHAN_MEMORY_DEBUG_HELPERS_HPP_INCLUDED
#include <atomic>
#include <type_traits>
#include "../config.hpp"
namespace foonathan
{
namespace memory
{
enum class debug_magic : unsigned char;
struct allocator_info;
namespace detail
{
using debug_fill_enabled = std::integral_constant<bool, FOONATHAN_MEMORY_DEBUG_FILL>;
constexpr std::size_t debug_fence_size =
FOONATHAN_MEMORY_DEBUG_FILL ? FOONATHAN_MEMORY_DEBUG_FENCE : 0u;
#if FOONATHAN_MEMORY_DEBUG_FILL
// fills size bytes of memory with debug_magic
void debug_fill(void* memory, std::size_t size, debug_magic m) noexcept;
// returns nullptr if memory is filled with debug_magic
// else returns pointer to mismatched byte
void* debug_is_filled(void* memory, std::size_t size, debug_magic m) noexcept;
// fills fence, new and fence
// returns after fence
void* debug_fill_new(void* memory, std::size_t node_size,
std::size_t fence_size = debug_fence_size) noexcept;
// fills free memory and returns memory starting at fence
void* debug_fill_free(void* memory, std::size_t node_size,
std::size_t fence_size = debug_fence_size) noexcept;
// fills internal memory
void debug_fill_internal(void* memory, std::size_t size, bool free) noexcept;
#else
inline void debug_fill(void*, std::size_t, debug_magic) noexcept {}
inline void* debug_is_filled(void*, std::size_t, debug_magic) noexcept
{
return nullptr;
}
inline void* debug_fill_new(void* memory, std::size_t, std::size_t) noexcept
{
return memory;
}
inline void* debug_fill_free(void* memory, std::size_t, std::size_t) noexcept
{
return static_cast<char*>(memory);
}
inline void debug_fill_internal(void*, std::size_t, bool) noexcept {}
#endif
void debug_handle_invalid_ptr(const allocator_info& info, void* ptr);
// validates given ptr by evaluating the Functor
// if the Functor returns false, calls the debug_leak_checker
// note: ptr is just used as the information passed to the invalid ptr handler
template <class Functor>
void debug_check_pointer(Functor condition, const allocator_info& info, void* ptr)
{
#if FOONATHAN_MEMORY_DEBUG_POINTER_CHECK
if (!condition())
debug_handle_invalid_ptr(info, ptr);
#else
(void)ptr;
(void)condition;
(void)info;
#endif
}
// validates ptr by using a more expensive double-dealloc check
template <class Functor>
void debug_check_double_dealloc(Functor condition, const allocator_info& info,
void* ptr)
{
#if FOONATHAN_MEMORY_DEBUG_DOUBLE_DEALLOC_CHECK
debug_check_pointer(condition, info, ptr);
#else
(void)condition;
(void)info;
(void)ptr;
#endif
}
void debug_handle_memory_leak(const allocator_info& info, std::ptrdiff_t amount);
// does no leak checking, null overhead
template <class Handler>
class no_leak_checker
{
public:
no_leak_checker() noexcept {}
no_leak_checker(no_leak_checker&&) noexcept {}
~no_leak_checker() noexcept {}
no_leak_checker& operator=(no_leak_checker&&) noexcept
{
return *this;
}
void on_allocate(std::size_t) noexcept {}
void on_deallocate(std::size_t) noexcept {}
};
// does leak checking per-object
// leak is detected upon destructor
template <class Handler>
class object_leak_checker : Handler
{
public:
object_leak_checker() noexcept : allocated_(0) {}
object_leak_checker(object_leak_checker&& other) noexcept
: allocated_(other.allocated_)
{
other.allocated_ = 0;
}
~object_leak_checker() noexcept
{
if (allocated_ != 0)
this->operator()(allocated_);
}
object_leak_checker& operator=(object_leak_checker&& other) noexcept
{
allocated_ = other.allocated_;
other.allocated_ = 0;
return *this;
}
void on_allocate(std::size_t size) noexcept
{
allocated_ += std::ptrdiff_t(size);
}
void on_deallocate(std::size_t size) noexcept
{
allocated_ -= std::ptrdiff_t(size);
}
private:
std::ptrdiff_t allocated_;
};
// does leak checking on a global basis
// call macro FOONATHAN_MEMORY_GLOBAL_LEAK_CHECKER(handler, var_name) in the header
// when last counter gets destroyed, leak is detected
template <class Handler>
class global_leak_checker_impl
{
public:
struct counter : Handler
{
counter()
{
++no_counter_objects_;
}
~counter()
{
--no_counter_objects_;
if (no_counter_objects_ == 0u && allocated_ != 0u)
this->operator()(allocated_);
}
};
global_leak_checker_impl() noexcept {}
global_leak_checker_impl(global_leak_checker_impl&&) noexcept {}
~global_leak_checker_impl() noexcept {}
global_leak_checker_impl& operator=(global_leak_checker_impl&&) noexcept
{
return *this;
}
void on_allocate(std::size_t size) noexcept
{
allocated_ += std::ptrdiff_t(size);
}
void on_deallocate(std::size_t size) noexcept
{
allocated_ -= std::ptrdiff_t(size);
}
private:
static std::atomic<std::size_t> no_counter_objects_;
static std::atomic<std::ptrdiff_t> allocated_;
};
template <class Handler>
std::atomic<std::size_t> global_leak_checker_impl<Handler>::no_counter_objects_(0u);
template <class Handler>
std::atomic<std::ptrdiff_t> global_leak_checker_impl<Handler>::allocated_(0);
#if FOONATHAN_MEMORY_DEBUG_LEAK_CHECK
template <class Handler>
using global_leak_checker = global_leak_checker_impl<Handler>;
#define FOONATHAN_MEMORY_GLOBAL_LEAK_CHECKER(handler, var_name) \
static foonathan::memory::detail::global_leak_checker<handler>::counter var_name;
#else
template <class Handler>
using global_leak_checker = no_leak_checker<int>; // only one instantiation
#define FOONATHAN_MEMORY_GLOBAL_LEAK_CHECKER(handler, var_name)
#endif
#if FOONATHAN_MEMORY_DEBUG_LEAK_CHECK
template <class Handler>
using default_leak_checker = object_leak_checker<Handler>;
#else
template <class Handler>
using default_leak_checker = no_leak_checker<Handler>;
#endif
} // namespace detail
} // namespace memory
} // namespace foonathan
#endif // FOONATHAN_MEMORY_DEBUG_HELPERS_HPP_INCLUDED
|