Dune Core Modules (unstable)

debugallocator.hh
1// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2// vi: set et ts=4 sw=2 sts=2:
3// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root
4// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
5#ifndef DUNE_DEBUG_ALLOCATOR_HH
6#define DUNE_DEBUG_ALLOCATOR_HH
7
8#if __has_include(<sys/mman.h>)
9
10#include <sys/mman.h>
11#define HAVE_SYS_MMAN_H 1
12#define HAVE_MPROTECT 1
13
14#include <exception>
15#include <typeinfo>
16#include <vector>
17#include <iostream>
18#include <cstring>
19#include <cstdint>
20#include <cstdlib>
21#include <new>
22
23#include "mallocallocator.hh"
24
25namespace Dune
26{
27
28#ifndef DOXYGEN // hide implementation details from doxygen
29 namespace DebugMemory
30 {
31
32 extern const std::ptrdiff_t page_size;
33
34 struct AllocationManager
35 {
36 typedef std::size_t size_type;
37 typedef std::ptrdiff_t difference_type;
38 typedef void* pointer;
39
40 protected:
41 static void allocation_error(const char* msg);
42
43 struct AllocationInfo;
44 friend struct AllocationInfo;
45
46#define ALLOCATION_ASSERT(A) { if (!(A)) \
47 { allocation_error("Assertion " # A " failed");\
48 }\
49};
50
51 struct AllocationInfo
52 {
53 AllocationInfo(const std::type_info & t) : type(&t) {}
54 const std::type_info * type;
55
56 pointer page_ptr;
57 pointer ptr;
58 size_type pages;
59 size_type capacity;
60 size_type size;
61 bool not_free;
62 };
63
64 typedef MallocAllocator<AllocationInfo> Alloc;
65 typedef std::vector<AllocationInfo, Alloc> AllocationList;
66 AllocationList allocation_list;
67
68 private:
69 void memprotect([[maybe_unused]] void* from,
70 [[maybe_unused]] difference_type len,
71 [[maybe_unused]] int prot)
72 {
73#if HAVE_SYS_MMAN_H && HAVE_MPROTECT
74 int result = mprotect(from, len, prot);
75 if (result == -1)
76 {
77
78 std::cerr << "ERROR: (" << result << ": " << strerror(result) << ")" << std::endl;
79 std::cerr << " Failed to ";
80 if (prot == PROT_NONE)
81 std::cerr << "protect ";
82 else
83 std::cerr << "unprotect ";
84 std::cerr << "memory range: "
85 << from << ", "
86 << static_cast<void*>(
87 static_cast<char*>(from) + len)
88 << std::endl;
89 abort();
90 }
91#else
92 std::cerr << "WARNING: memory protection not available" << std::endl;
93#endif
94 }
95
96 public:
97
98 ~AllocationManager ()
99 {
100 AllocationList::iterator it;
101 bool error = false;
102 for (it=allocation_list.begin(); it!=allocation_list.end(); it++)
103 {
104 if (it->not_free)
105 {
106 std::cerr << "ERROR: found memory chunk still in use: " <<
107 it->capacity << " bytes at " << it->ptr << std::endl;
108 error = true;
109 }
110 munmap(it->page_ptr, it->pages * page_size);
111 }
112 if (error)
113 allocation_error("lost allocations");
114 }
115
116 template<typename T>
117 T* allocate(size_type n)
118 {
119 // setup chunk info
120 AllocationInfo ai(typeid(T));
121 ai.size = n;
122 ai.capacity = n * sizeof(T);
123 ai.pages = (ai.capacity) / page_size + 2;
124 ai.not_free = true;
125 size_type overlap = ai.capacity % page_size;
126 ai.page_ptr = mmap(NULL, ai.pages * page_size,
127 PROT_READ | PROT_WRITE,
128#ifdef __APPLE__
129 MAP_ANON | MAP_PRIVATE,
130#else
131 MAP_ANONYMOUS | MAP_PRIVATE,
132#endif
133 -1, 0);
134 if (MAP_FAILED == ai.page_ptr)
135 {
136 throw std::bad_alloc();
137 }
138 ai.ptr = static_cast<char*>(ai.page_ptr) + page_size - overlap;
139 // write protect memory behind the actual data
140 memprotect(static_cast<char*>(ai.page_ptr) + (ai.pages-1) * page_size,
141 page_size,
142 PROT_NONE);
143 // remember the chunk
144 allocation_list.push_back(ai);
145 // return the ptr
146 return static_cast<T*>(ai.ptr);
147 }
148
149 template<typename T>
150 void deallocate(T* ptr, size_type n = 0) noexcept
151 {
152 // compute page address
153 void* page_ptr =
154 static_cast<void*>(
155 (char*)(ptr) - ((std::uintptr_t)(ptr) % page_size));
156 // search list
157 AllocationList::iterator it;
158 unsigned int i = 0;
159 for (it=allocation_list.begin(); it!=allocation_list.end(); it++, i++)
160 {
161 if (it->page_ptr == page_ptr)
162 {
163 // std::cout << "found memory_block in allocation " << i << std::endl;
164 // sanity checks
165 if (n != 0)
166 ALLOCATION_ASSERT(n == it->size);
167 ALLOCATION_ASSERT(ptr == it->ptr);
168 ALLOCATION_ASSERT(true == it->not_free);
169 ALLOCATION_ASSERT(typeid(T) == *(it->type));
170 // free memory
171 it->not_free = false;
172#if DEBUG_ALLOCATOR_KEEP
173 // write protect old memory
174 memprotect(it->page_ptr,
175 (it->pages) * page_size,
176 PROT_NONE);
177#else
178 // unprotect old memory
179 memprotect(it->page_ptr,
180 (it->pages) * page_size,
181 PROT_READ | PROT_WRITE);
182 munmap(it->page_ptr, it->pages * page_size);
183 // remove chunk info
184 allocation_list.erase(it);
185#endif
186 return;
187 }
188 }
189 allocation_error("memory block not found");
190 }
191 };
192#undef ALLOCATION_ASSERT
193
194 extern AllocationManager alloc_man;
195 } // end namespace DebugMemory
196#endif // DOXYGEN
197
198 template<class T>
199 class DebugAllocator;
200
201 // specialize for void
202 template <>
203 class DebugAllocator<void> {
204 public:
205 typedef void* pointer;
206 typedef const void* const_pointer;
207 // reference to void members are impossible.
208 typedef void value_type;
209 template <class U> struct rebind {
210 typedef DebugAllocator<U> other;
211 };
212 };
213
214 // actual implementation
233 template <class T>
234 class DebugAllocator {
235 public:
236 typedef std::size_t size_type;
237 typedef std::ptrdiff_t difference_type;
238 typedef T* pointer;
239 typedef const T* const_pointer;
240 typedef T& reference;
241 typedef const T& const_reference;
242 typedef T value_type;
243 template <class U> struct rebind {
244 typedef DebugAllocator<U> other;
245 };
246
248 DebugAllocator() noexcept {}
250 template <class U>
251 DebugAllocator(const DebugAllocator<U>&) noexcept {}
253 ~DebugAllocator() noexcept {}
254
255 pointer address(reference x) const
256 {
257 return &x;
258 }
259 const_pointer address(const_reference x) const
260 {
261 return &x;
262 }
263
265 pointer allocate(size_type n,
266 [[maybe_unused]] DebugAllocator<void>::const_pointer hint = 0)
267 {
268 return DebugMemory::alloc_man.allocate<T>(n);
269 }
270
272 void deallocate(pointer p, size_type n)
273 {
274 DebugMemory::alloc_man.deallocate<T>(p,n);
275 }
276
278 size_type max_size() const noexcept
279 {
280 return size_type(-1) / sizeof(T);
281 }
282
284 void construct(pointer p, const T& val)
285 {
286 ::new((void*)p)T(val);
287 }
288
290 template<typename ... Args>
291 void construct(pointer p, Args&&... args)
292 {
293 ::new((void *)p)T(std::forward<Args>(args) ...);
294 }
295
297 void destroy(pointer p)
298 {
299 p->~T();
300 }
301 };
302
304 template<class T>
305 constexpr bool
306 operator==(const DebugAllocator<T> &, const DebugAllocator<T> &)
307 {
308 return true;
309 }
310
312 template<class T>
313 constexpr bool
314 operator!=(const DebugAllocator<T> &, const DebugAllocator<T> &)
315 {
316 return false;
317 }
318}
319
320#ifdef DEBUG_NEW_DELETE
321void * operator new(size_t size)
322{
323 // try to allocate size bytes
324 void *p = Dune::DebugMemory::alloc_man.allocate<char>(size);
325#if DEBUG_NEW_DELETE > 2
326 std::cout << "NEW " << size
327 << " -> " << p
328 << std::endl;
329#endif
330 return p;
331}
332
333void operator delete(void * p) noexcept
334{
335#if DEBUG_NEW_DELETE > 2
336 std::cout << "FREE " << p << std::endl;
337#endif
338 Dune::DebugMemory::alloc_man.deallocate<char>(static_cast<char*>(p));
339}
340
341void operator delete(void * p, size_t size) noexcept
342{
343#if DEBUG_NEW_DELETE > 2
344 std::cout << "FREE " << p << std::endl;
345#endif
346 Dune::DebugMemory::alloc_man.deallocate<char>(static_cast<char*>(p), size);
347}
348
349#endif // DEBUG_NEW_DELETE
350
351#endif // __has_include(<sys/mman.h>)
352
353#endif // DUNE_DEBUG_ALLOCATOR_HH
EnableIfInterOperable< T1, T2, bool >::type operator==(const ForwardIteratorFacade< T1, V1, R1, D > &lhs, const ForwardIteratorFacade< T2, V2, R2, D > &rhs)
Checks for equality.
Definition: iteratorfacades.hh:238
EnableIfInterOperable< T1, T2, bool >::type operator!=(const ForwardIteratorFacade< T1, V1, R1, D > &lhs, const ForwardIteratorFacade< T2, V2, R2, D > &rhs)
Checks for inequality.
Definition: iteratorfacades.hh:260
Allocators that use malloc/free.
Dune namespace.
Definition: alignedallocator.hh:13
constexpr std::integral_constant< std::size_t, sizeof...(II)> size(std::integer_sequence< T, II... >)
Return the size of the sequence.
Definition: integersequence.hh:75
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.111.3 (Dec 21, 23:30, 2024)