dune-common  2.4
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 #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
19 #endif
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(void* from, difference_type len, int prot)
68  {
69 #if HAVE_SYS_MMAN_H && HAVE_MPROTECT
70  int result = mprotect(from, len, prot);
71  if (result == -1)
72  {
73 
74  std::cerr << "ERROR: (" << result << ": " << strerror(result) << ")" << std::endl;
75  std::cerr << " Failed to ";
76  if (prot == PROT_NONE)
77  std::cerr << "protect ";
78  else
79  std::cerr << "unprotect ";
80  std::cerr << "memory range: "
81  << from << ", "
82  << static_cast<void*>(
83  static_cast<char*>(from) + len)
84  << std::endl;
85  abort();
86  }
87 #else
91  std::cerr << "WARNING: memory protection not available" << std::endl;
92 #endif
93  }
94 
95  public:
96 
97  ~AllocationManager ()
98  {
99  AllocationList::iterator it;
100  bool error = false;
101  for (it=allocation_list.begin(); it!=allocation_list.end(); it++)
102  {
103  if (it->not_free)
104  {
105  std::cerr << "ERROR: found memory chunk still in use: " <<
106  it->capacity << " bytes at " << it->ptr << std::endl;
107  error = true;
108  }
109  free(it->page_ptr);
110  }
111  if (error)
112  allocation_error("lost allocations");
113  }
114 
115  template<typename T>
116  T* allocate(size_type n) throw(std::bad_alloc)
117  {
118  // setup chunk info
119  AllocationInfo ai(typeid(T));
120  ai.size = n;
121  ai.capacity = n * sizeof(T);
122  ai.pages = (ai.capacity) / page_size + 2;
123  ai.not_free = true;
124  size_type overlap = ai.capacity % page_size;
125  int result = posix_memalign(&(ai.page_ptr), page_size, ai.pages * page_size);
126  if (0 != result)
127  {
128  throw std::bad_alloc();
129  }
130  ai.ptr = static_cast<char*>(ai.page_ptr) + page_size - overlap;
131  // write protect memory behind the actual data
132  memprotect(static_cast<char*>(ai.page_ptr) + (ai.pages-1) * page_size,
133  page_size,
134  PROT_NONE);
135  // remember the chunk
136  allocation_list.push_back(ai);
137  // return the ptr
138  return static_cast<T*>(ai.ptr);
139  }
140 
141  template<typename T>
142  void deallocate(T* ptr, size_type n = 0) throw()
143  {
144  // compute page address
145  void* page_ptr =
146  static_cast<void*>(
147  (char*)(ptr) - ((std::uintptr_t)(ptr) % page_size));
148  // search list
149  AllocationList::iterator it;
150  unsigned int i = 0;
151  for (it=allocation_list.begin(); it!=allocation_list.end(); it++, i++)
152  {
153  if (it->page_ptr == page_ptr)
154  {
155  // std::cout << "found memory_block in allocation " << i << std::endl;
156  // sanity checks
157  if (n != 0)
158  ALLOCATION_ASSERT(n == it->size);
159  ALLOCATION_ASSERT(ptr == it->ptr);
160  ALLOCATION_ASSERT(true == it->not_free);
161  ALLOCATION_ASSERT(typeid(T) == *(it->type));
162  // free memory
163  it->not_free = false;
164 #if DEBUG_ALLOCATOR_KEEP
165  // write protect old memory
166  memprotect(it->page_ptr,
167  (it->pages) * page_size,
168  PROT_NONE);
169 #else
170  // unprotect old memory
171  memprotect(it->page_ptr,
172  (it->pages) * page_size,
174  std::free(it->page_ptr);
175  // remove chunk info
176  allocation_list.erase(it);
177 #endif
178  return;
179  }
180  }
181  allocation_error("memory block not found");
182  }
183  };
184 #undef ALLOCATION_ASSERT
185 
186  extern AllocationManager alloc_man;
187  } // end namespace DebugMemory
188 #endif // DOXYGEN
189 
190  template<class T>
192 
193  // specialize for void
194  template <>
195  class DebugAllocator<void> {
196  public:
197  typedef void* pointer;
198  typedef const void* const_pointer;
199  // reference to void members are impossible.
200  typedef void value_type;
201  template <class U> struct rebind {
203  };
204  };
205 
206  // actual implementation
225  template <class T>
226  class DebugAllocator {
227  public:
228  typedef std::size_t size_type;
229  typedef std::ptrdiff_t difference_type;
230  typedef T* pointer;
231  typedef const T* const_pointer;
232  typedef T& reference;
233  typedef const T& const_reference;
234  typedef T value_type;
235  template <class U> struct rebind {
237  };
238 
240  DebugAllocator() throw() {}
242  template <class U>
245  ~DebugAllocator() throw() {}
246 
247  pointer address(reference x) const
248  {
249  return &x;
250  }
251  const_pointer address(const_reference x) const
252  {
253  return &x;
254  }
255 
257  pointer allocate(size_type n,
259  {
260  DUNE_UNUSED_PARAMETER(hint);
261  return DebugMemory::alloc_man.allocate<T>(n);
262  }
263 
265  void deallocate(pointer p, size_type n)
266  {
267  DebugMemory::alloc_man.deallocate<T>(p,n);
268  }
269 
271  size_type max_size() const throw()
272  {
273  return size_type(-1) / sizeof(T);
274  }
275 
277  void construct(pointer p, const T& val)
278  {
279  ::new((void*)p)T(val);
280  }
281 
283  template<typename ... _Args>
284  void construct(pointer p, _Args&&... __args)
285  {
286  ::new((void *)p)T(std::forward<_Args>(__args) ...);
287  }
288 
290  void destroy(pointer p)
291  {
292  p->~T();
293  }
294  };
295 }
296 
297 #ifdef DEBUG_NEW_DELETE
298 void * operator new(size_t size) throw(std::bad_alloc)
299 {
300  // try to allocate size bytes
301  void *p = Dune::DebugMemory::alloc_man.allocate<char>(size);
302 #if DEBUG_NEW_DELETE > 2
303  std::cout << "NEW " << size
304  << " -> " << p
305  << std::endl;
306 #endif
307  return p;
308 }
309 
310 void operator delete(void * p) throw()
311 {
312 #if DEBUG_NEW_DELETE > 2
313  std::cout << "FREE " << p << std::endl;
314 #endif
315  Dune::DebugMemory::alloc_man.deallocate<char>(static_cast<char*>(p));
316 }
317 
318 #endif // DEBUG_NEW_DELETE
319 
320 #endif // DUNE_DEBUG_ALLOCATOR_HH
T value_type
Definition: debugallocator.hh:234
const void * const_pointer
Definition: debugallocator.hh:198
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:277
Definition: debugallocator.hh:235
DebugAllocator()
create a new DebugAllocator
Definition: debugallocator.hh:240
const T * const_pointer
Definition: debugallocator.hh:231
pointer address(reference x) const
Definition: debugallocator.hh:247
pointer allocate(size_type n, DebugAllocator< void >::const_pointer hint=0)
allocate n objects of type T
Definition: debugallocator.hh:257
void * pointer
Definition: debugallocator.hh:197
std::size_t size_type
Definition: debugallocator.hh:228
void destroy(pointer p)
destroy an object of type T (i.e. call the destructor)
Definition: debugallocator.hh:290
std::ptrdiff_t difference_type
Definition: debugallocator.hh:229
Definition: debugallocator.hh:18
T & reference
Definition: debugallocator.hh:232
const T & const_reference
Definition: debugallocator.hh:233
T t
Definition: alignment.hh:34
Definition of the DUNE_UNUSED macro for the case that config.h is not available.
Dune namespace.
Definition: alignment.hh:9
void deallocate(pointer p, size_type n)
deallocate n objects of type T at address p
Definition: debugallocator.hh:265
Allocators implementation which performs different kind of memory checks.
Definition: debugallocator.hh:191
DebugAllocator(const DebugAllocator< U > &)
copy construct from an other DebugAllocator, possibly for a different result type ...
Definition: debugallocator.hh:243
void value_type
Definition: debugallocator.hh:200
DummyProtFlags
Definition: debugallocator.hh:18
Definition: debugallocator.hh:18
#define DUNE_UNUSED_PARAMETER(parm)
A macro to mark intentional unused function parameters with.
Definition: unused.hh:18
~DebugAllocator()
cleanup this allocator
Definition: debugallocator.hh:245
size_type max_size() const
max size for allocate
Definition: debugallocator.hh:271
DebugAllocator< U > other
Definition: debugallocator.hh:236
DebugAllocator< U > other
Definition: debugallocator.hh:202
const_pointer address(const_reference x) const
Definition: debugallocator.hh:251
Allocators that use malloc/free.
T * pointer
Definition: debugallocator.hh:230
void construct(pointer p, _Args &&...__args)
construct an object of type T from variadic parameters
Definition: debugallocator.hh:284
Definition: debugallocator.hh:18