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