00001
00002
00003 #ifndef DUNE_DEBUG_ALLOCATOR_HH
00004 #define DUNE_DEBUG_ALLOCATOR_HH
00005
00006 #include <dune/common/unused.hh>
00007 #include <exception>
00008 #include <typeinfo>
00009 #include <vector>
00010 #include <iostream>
00011 #include <cstring>
00012 #include <cstdint>
00013 #include <cstdlib>
00014 #include <new>
00015 #if HAVE_SYS_MMAN_H && HAVE_MPROTECT
00016 #include <sys/mman.h>
00017 #else
00018 enum DummyProtFlags { PROT_NONE, PROT_WRITE, PROT_READ };
00019 #endif
00020
00021 #include "mallocallocator.hh"
00022
00023 namespace Dune
00024 {
00025
00026 #ifndef DOXYGEN // hide implementation details from doxygen
00027 namespace DebugMemory
00028 {
00029
00030 extern const std::ptrdiff_t page_size;
00031
00032 struct AllocationManager
00033 {
00034 typedef std::size_t size_type;
00035 typedef std::ptrdiff_t difference_type;
00036 typedef void* pointer;
00037
00038 protected:
00039 static void allocation_error(const char* msg);
00040
00041 struct AllocationInfo;
00042 friend struct AllocationInfo;
00043
00044 #define ALLOCATION_ASSERT(A) { if (!(A)) \
00045 { allocation_error("Assertion " # A " failed");\
00046 }\
00047 };
00048
00049 struct AllocationInfo
00050 {
00051 AllocationInfo(const std::type_info & t) : type(&t) {}
00052 const std::type_info * type;
00053
00054 pointer page_ptr;
00055 pointer ptr;
00056 size_type pages;
00057 size_type capacity;
00058 size_type size;
00059 bool not_free;
00060 };
00061
00062 typedef MallocAllocator<AllocationInfo> Alloc;
00063 typedef std::vector<AllocationInfo, Alloc> AllocationList;
00064 AllocationList allocation_list;
00065
00066 private:
00067 void memprotect(void* from, difference_type len, int prot)
00068 {
00069 #if HAVE_SYS_MMAN_H && HAVE_MPROTECT
00070 int result = mprotect(from, len, prot);
00071 if (result == -1)
00072 {
00073
00074 std::cerr << "ERROR: (" << result << ": " << strerror(result) << ")" << std::endl;
00075 std::cerr << " Failed to ";
00076 if (prot == PROT_NONE)
00077 std::cerr << "protect ";
00078 else
00079 std::cerr << "unprotect ";
00080 std::cerr << "memory range: "
00081 << from << ", "
00082 << static_cast<void*>(
00083 static_cast<char*>(from) + len)
00084 << std::endl;
00085 abort();
00086 }
00087 #else
00088 DUNE_UNUSED_PARAMETER(from);
00089 DUNE_UNUSED_PARAMETER(len);
00090 DUNE_UNUSED_PARAMETER(prot);
00091 std::cerr << "WARNING: memory protection not available" << std::endl;
00092 #endif
00093 }
00094
00095 public:
00096
00097 ~AllocationManager ()
00098 {
00099 AllocationList::iterator it;
00100 bool error = false;
00101 for (it=allocation_list.begin(); it!=allocation_list.end(); it++)
00102 {
00103 if (it->not_free)
00104 {
00105 std::cerr << "ERROR: found memory chunk still in use: " <<
00106 it->capacity << " bytes at " << it->ptr << std::endl;
00107 error = true;
00108 }
00109 munmap(it->page_ptr, it->pages * page_size);
00110 }
00111 if (error)
00112 allocation_error("lost allocations");
00113 }
00114
00115 template<typename T>
00116 T* allocate(size_type n) throw(std::bad_alloc)
00117 {
00118
00119 AllocationInfo ai(typeid(T));
00120 ai.size = n;
00121 ai.capacity = n * sizeof(T);
00122 ai.pages = (ai.capacity) / page_size + 2;
00123 ai.not_free = true;
00124 size_type overlap = ai.capacity % page_size;
00125 ai.page_ptr = mmap(NULL, ai.pages * page_size,
00126 PROT_READ | PROT_WRITE,
00127 #ifdef __APPLE__
00128 MAP_ANON | MAP_PRIVATE,
00129 #else
00130 MAP_ANONYMOUS | MAP_PRIVATE,
00131 #endif
00132 -1, 0);
00133 if (MAP_FAILED == ai.page_ptr)
00134 {
00135 throw std::bad_alloc();
00136 }
00137 ai.ptr = static_cast<char*>(ai.page_ptr) + page_size - overlap;
00138
00139 memprotect(static_cast<char*>(ai.page_ptr) + (ai.pages-1) * page_size,
00140 page_size,
00141 PROT_NONE);
00142
00143 allocation_list.push_back(ai);
00144
00145 return static_cast<T*>(ai.ptr);
00146 }
00147
00148 template<typename T>
00149 void deallocate(T* ptr, size_type n = 0) throw()
00150 {
00151
00152 void* page_ptr =
00153 static_cast<void*>(
00154 (char*)(ptr) - ((std::uintptr_t)(ptr) % page_size));
00155
00156 AllocationList::iterator it;
00157 unsigned int i = 0;
00158 for (it=allocation_list.begin(); it!=allocation_list.end(); it++, i++)
00159 {
00160 if (it->page_ptr == page_ptr)
00161 {
00162
00163
00164 if (n != 0)
00165 ALLOCATION_ASSERT(n == it->size);
00166 ALLOCATION_ASSERT(ptr == it->ptr);
00167 ALLOCATION_ASSERT(true == it->not_free);
00168 ALLOCATION_ASSERT(typeid(T) == *(it->type));
00169
00170 it->not_free = false;
00171 #if DEBUG_ALLOCATOR_KEEP
00172
00173 memprotect(it->page_ptr,
00174 (it->pages) * page_size,
00175 PROT_NONE);
00176 #else
00177
00178 memprotect(it->page_ptr,
00179 (it->pages) * page_size,
00180 PROT_READ | PROT_WRITE);
00181 munmap(it->page_ptr, it->pages * page_size);
00182
00183 allocation_list.erase(it);
00184 #endif
00185 return;
00186 }
00187 }
00188 allocation_error("memory block not found");
00189 }
00190 };
00191 #undef ALLOCATION_ASSERT
00192
00193 extern AllocationManager alloc_man;
00194 }
00195 #endif // DOXYGEN
00196
00197 template<class T>
00198 class DebugAllocator;
00199
00200
00201 template <>
00202 class DebugAllocator<void> {
00203 public:
00204 typedef void* pointer;
00205 typedef const void* const_pointer;
00206
00207 typedef void value_type;
00208 template <class U> struct rebind {
00209 typedef DebugAllocator<U> other;
00210 };
00211 };
00212
00213
00232 template <class T>
00233 class DebugAllocator {
00234 public:
00235 typedef std::size_t size_type;
00236 typedef std::ptrdiff_t difference_type;
00237 typedef T* pointer;
00238 typedef const T* const_pointer;
00239 typedef T& reference;
00240 typedef const T& const_reference;
00241 typedef T value_type;
00242 template <class U> struct rebind {
00243 typedef DebugAllocator<U> other;
00244 };
00245
00247 DebugAllocator() throw() {}
00249 template <class U>
00250 DebugAllocator(const DebugAllocator<U>&) throw() {}
00252 ~DebugAllocator() throw() {}
00253
00254 pointer address(reference x) const
00255 {
00256 return &x;
00257 }
00258 const_pointer address(const_reference x) const
00259 {
00260 return &x;
00261 }
00262
00264 pointer allocate(size_type n,
00265 DebugAllocator<void>::const_pointer hint = 0)
00266 {
00267 DUNE_UNUSED_PARAMETER(hint);
00268 return DebugMemory::alloc_man.allocate<T>(n);
00269 }
00270
00272 void deallocate(pointer p, size_type n)
00273 {
00274 DebugMemory::alloc_man.deallocate<T>(p,n);
00275 }
00276
00278 size_type max_size() const throw()
00279 {
00280 return size_type(-1) / sizeof(T);
00281 }
00282
00284 void construct(pointer p, const T& val)
00285 {
00286 ::new((void*)p)T(val);
00287 }
00288
00290 template<typename ... _Args>
00291 void construct(pointer p, _Args&&... __args)
00292 {
00293 ::new((void *)p)T(std::forward<_Args>(__args) ...);
00294 }
00295
00297 void destroy(pointer p)
00298 {
00299 p->~T();
00300 }
00301 };
00302 }
00303
00304 #ifdef DEBUG_NEW_DELETE
00305 void * operator new(size_t size)
00306 {
00307
00308 void *p = Dune::DebugMemory::alloc_man.allocate<char>(size);
00309 #if DEBUG_NEW_DELETE > 2
00310 std::cout << "NEW " << size
00311 << " -> " << p
00312 << std::endl;
00313 #endif
00314 return p;
00315 }
00316
00317 void operator delete(void * p) throw()
00318 {
00319 #if DEBUG_NEW_DELETE > 2
00320 std::cout << "FREE " << p << std::endl;
00321 #endif
00322 Dune::DebugMemory::alloc_man.deallocate<char>(static_cast<char*>(p));
00323 }
00324
00325 #endif // DEBUG_NEW_DELETE
00326
00327 #endif // DUNE_DEBUG_ALLOCATOR_HH