Dune Core Modules (2.6.0)

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 #ifndef DUNE_DEBUG_ALLOCATOR_HH
4 #define DUNE_DEBUG_ALLOCATOR_HH
5 
6 #include <dune/common/unused.hh>
7 #include <exception>
8 #include <typeinfo>
9 #include <vector>
10 #include <iostream>
11 #include <cstring>
12 #include <cstdint>
13 #include <cstdlib>
14 #include <new>
15 #if HAVE_SYS_MMAN_H && HAVE_MPROTECT
16 #include <sys/mman.h>
17 #else
18 enum DummyProtFlags { PROT_NONE, PROT_WRITE, PROT_READ };
19 #endif
20 
21 #include "mallocallocator.hh"
22 
23 #if not HAVE_MPROTECT
24 #error mprotect is required to use the DebugAllocator
25 #else
26 
27 namespace Dune
28 {
29 
30 #ifndef DOXYGEN // hide implementation details from doxygen
31  namespace DebugMemory
32  {
33 
34  extern const std::ptrdiff_t page_size;
35 
36  struct AllocationManager
37  {
38  typedef std::size_t size_type;
39  typedef std::ptrdiff_t difference_type;
40  typedef void* pointer;
41 
42  protected:
43  static void allocation_error(const char* msg);
44 
45  struct AllocationInfo;
46  friend struct AllocationInfo;
47 
48 #define ALLOCATION_ASSERT(A) { if (!(A)) \
49  { allocation_error("Assertion " # A " failed");\
50  }\
51 };
52 
53  struct AllocationInfo
54  {
55  AllocationInfo(const std::type_info & t) : type(&t) {}
56  const std::type_info * type;
57 
58  pointer page_ptr;
59  pointer ptr;
60  size_type pages;
61  size_type capacity;
62  size_type size;
63  bool not_free;
64  };
65 
66  typedef MallocAllocator<AllocationInfo> Alloc;
67  typedef std::vector<AllocationInfo, Alloc> AllocationList;
68  AllocationList allocation_list;
69 
70  private:
71  void memprotect(void* from, difference_type len, 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
95  std::cerr << "WARNING: memory protection not available" << std::endl;
96 #endif
97  }
98 
99  public:
100 
101  ~AllocationManager ()
102  {
103  AllocationList::iterator it;
104  bool error = false;
105  for (it=allocation_list.begin(); it!=allocation_list.end(); it++)
106  {
107  if (it->not_free)
108  {
109  std::cerr << "ERROR: found memory chunk still in use: " <<
110  it->capacity << " bytes at " << it->ptr << std::endl;
111  error = true;
112  }
113  munmap(it->page_ptr, it->pages * page_size);
114  }
115  if (error)
116  allocation_error("lost allocations");
117  }
118 
119  template<typename T>
120  T* allocate(size_type n)
121  {
122  // setup chunk info
123  AllocationInfo ai(typeid(T));
124  ai.size = n;
125  ai.capacity = n * sizeof(T);
126  ai.pages = (ai.capacity) / page_size + 2;
127  ai.not_free = true;
128  size_type overlap = ai.capacity % page_size;
129  ai.page_ptr = mmap(NULL, ai.pages * page_size,
130  PROT_READ | PROT_WRITE,
131 #ifdef __APPLE__
132  MAP_ANON | MAP_PRIVATE,
133 #else
134  MAP_ANONYMOUS | MAP_PRIVATE,
135 #endif
136  -1, 0);
137  if (MAP_FAILED == ai.page_ptr)
138  {
139  throw std::bad_alloc();
140  }
141  ai.ptr = static_cast<char*>(ai.page_ptr) + page_size - overlap;
142  // write protect memory behind the actual data
143  memprotect(static_cast<char*>(ai.page_ptr) + (ai.pages-1) * page_size,
144  page_size,
145  PROT_NONE);
146  // remember the chunk
147  allocation_list.push_back(ai);
148  // return the ptr
149  return static_cast<T*>(ai.ptr);
150  }
151 
152  template<typename T>
153  void deallocate(T* ptr, size_type n = 0) noexcept
154  {
155  // compute page address
156  void* page_ptr =
157  static_cast<void*>(
158  (char*)(ptr) - ((std::uintptr_t)(ptr) % page_size));
159  // search list
160  AllocationList::iterator it;
161  unsigned int i = 0;
162  for (it=allocation_list.begin(); it!=allocation_list.end(); it++, i++)
163  {
164  if (it->page_ptr == page_ptr)
165  {
166  // std::cout << "found memory_block in allocation " << i << std::endl;
167  // sanity checks
168  if (n != 0)
169  ALLOCATION_ASSERT(n == it->size);
170  ALLOCATION_ASSERT(ptr == it->ptr);
171  ALLOCATION_ASSERT(true == it->not_free);
172  ALLOCATION_ASSERT(typeid(T) == *(it->type));
173  // free memory
174  it->not_free = false;
175 #if DEBUG_ALLOCATOR_KEEP
176  // write protect old memory
177  memprotect(it->page_ptr,
178  (it->pages) * page_size,
179  PROT_NONE);
180 #else
181  // unprotect old memory
182  memprotect(it->page_ptr,
183  (it->pages) * page_size,
184  PROT_READ | PROT_WRITE);
185  munmap(it->page_ptr, it->pages * page_size);
186  // remove chunk info
187  allocation_list.erase(it);
188 #endif
189  return;
190  }
191  }
192  allocation_error("memory block not found");
193  }
194  };
195 #undef ALLOCATION_ASSERT
196 
197  extern AllocationManager alloc_man;
198  } // end namespace DebugMemory
199 #endif // DOXYGEN
200 
201  template<class T>
202  class DebugAllocator;
203 
204  // specialize for void
205  template <>
206  class DebugAllocator<void> {
207  public:
208  typedef void* pointer;
209  typedef const void* const_pointer;
210  // reference to void members are impossible.
211  typedef void value_type;
212  template <class U> struct rebind {
213  typedef DebugAllocator<U> other;
214  };
215  };
216 
217  // actual implementation
236  template <class T>
238  public:
239  typedef std::size_t size_type;
240  typedef std::ptrdiff_t difference_type;
241  typedef T* pointer;
242  typedef const T* const_pointer;
243  typedef T& reference;
244  typedef const T& const_reference;
245  typedef T value_type;
246  template <class U> struct rebind {
247  typedef DebugAllocator<U> other;
248  };
249 
251  DebugAllocator() noexcept {}
253  template <class U>
254  DebugAllocator(const DebugAllocator<U>&) noexcept {}
256  ~DebugAllocator() noexcept {}
257 
258  pointer address(reference x) const
259  {
260  return &x;
261  }
262  const_pointer address(const_reference x) const
263  {
264  return &x;
265  }
266 
268  pointer allocate(size_type n,
269  DebugAllocator<void>::const_pointer hint = 0)
270  {
271  DUNE_UNUSED_PARAMETER(hint);
272  return DebugMemory::alloc_man.allocate<T>(n);
273  }
274 
276  void deallocate(pointer p, size_type n)
277  {
278  DebugMemory::alloc_man.deallocate<T>(p,n);
279  }
280 
282  size_type max_size() const noexcept
283  {
284  return size_type(-1) / sizeof(T);
285  }
286 
288  void construct(pointer p, const T& val)
289  {
290  ::new((void*)p)T(val);
291  }
292 
294  template<typename ... _Args>
295  void construct(pointer p, _Args&&... __args)
296  {
297  ::new((void *)p)T(std::forward<_Args>(__args) ...);
298  }
299 
301  void destroy(pointer p)
302  {
303  p->~T();
304  }
305  };
306 }
307 
308 #ifdef DEBUG_NEW_DELETE
309 void * operator new(size_t size)
310 {
311  // try to allocate size bytes
312  void *p = Dune::DebugMemory::alloc_man.allocate<char>(size);
313 #if DEBUG_NEW_DELETE > 2
314  std::cout << "NEW " << size
315  << " -> " << p
316  << std::endl;
317 #endif
318  return p;
319 }
320 
321 void operator delete(void * p) noexcept
322 {
323 #if DEBUG_NEW_DELETE > 2
324  std::cout << "FREE " << p << std::endl;
325 #endif
326  Dune::DebugMemory::alloc_man.deallocate<char>(static_cast<char*>(p));
327 }
328 
329 #endif // DEBUG_NEW_DELETE
330 
331 #endif // HAVE_PROTECT
332 
333 #endif // DUNE_DEBUG_ALLOCATOR_HH
Allocators implementation which performs different kind of memory checks.
Definition: debugallocator.hh:237
void destroy(pointer p)
destroy an object of type T (i.e. call the destructor)
Definition: debugallocator.hh:301
DebugAllocator(const DebugAllocator< U > &) noexcept
copy construct from an other DebugAllocator, possibly for a different result type
Definition: debugallocator.hh:254
size_type max_size() const noexcept
max size for allocate
Definition: debugallocator.hh:282
~DebugAllocator() noexcept
cleanup this allocator
Definition: debugallocator.hh:256
DebugAllocator() noexcept
create a new DebugAllocator
Definition: debugallocator.hh:251
void deallocate(pointer p, size_type n)
deallocate n objects of type T at address p
Definition: debugallocator.hh:276
void construct(pointer p, _Args &&... __args)
construct an object of type T from variadic parameters
Definition: debugallocator.hh:295
void construct(pointer p, const T &val)
copy-construct an object of type T (i.e. make a placement new on p)
Definition: debugallocator.hh:288
pointer allocate(size_type n, DebugAllocator< void >::const_pointer hint=0)
allocate n objects of type T
Definition: debugallocator.hh:268
#define DUNE_UNUSED_PARAMETER(parm)
A macro to mark intentionally unused function parameters with.
Definition: unused.hh:25
Allocators that use malloc/free.
Dune namespace.
Definition: alignedallocator.hh:10
Definition of the DUNE_UNUSED macro for the case that config.h is not available.
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.80.0 (May 6, 22:30, 2024)