dune-common  2.9.0
debugallocator.hh
Go to the documentation of this file.
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 (C) 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 
25 namespace 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
321 void * 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 
333 void 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 
341 void 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
Allocators that use malloc/free.
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:259
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:237
Dune namespace.
Definition: alignedallocator.hh:13