File: debug_helpers.hpp

package info (click to toggle)
foonathan-memory 0.7.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,644 kB
  • sloc: cpp: 12,425; xml: 139; sh: 48; makefile: 25
file content (234 lines) | stat: -rw-r--r-- 8,311 bytes parent folder | download
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