Panda3D

memoryHook.cxx

00001 // Filename: memoryHook.cxx
00002 // Created by:  drose (28Jun07)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "memoryHook.h"
00016 #include "deletedBufferChain.h"
00017 #include <stdlib.h>
00018 
00019 #ifdef WIN32
00020 
00021 // Windows case.
00022 #define WIN32_LEAN_AND_MEAN
00023 #include <windows.h>
00024 
00025 #else
00026 
00027 // Posix case.
00028 #include <unistd.h>
00029 #include <sys/types.h>
00030 #include <sys/mman.h>
00031 
00032 #ifndef MAP_ANON
00033 #define MAP_ANON 0x1000
00034 #endif
00035 
00036 #endif  // WIN32
00037 
00038 
00039 #if defined(USE_MEMORY_DLMALLOC)
00040 
00041 /////////////////////////////////////////////////////////////////////
00042 //
00043 // Memory manager: DLMALLOC
00044 //
00045 // This is Doug Lea's memory manager.  It is very fast, but it is not
00046 // thread-safe.  However, we provide thread locking within MemoryHook.
00047 //
00048 /////////////////////////////////////////////////////////////////////
00049 
00050 #define USE_DL_PREFIX 1
00051 #define NO_MALLINFO 1
00052 #ifdef _DEBUG
00053   #define DEBUG 1
00054 #endif
00055 #include "dlmalloc.h"
00056 #include "dlmalloc_src.cxx"
00057 
00058 #define call_malloc dlmalloc
00059 #define call_realloc dlrealloc
00060 #define call_free dlfree
00061 #define MEMORY_HOOK_MALLOC_LOCK 1
00062 
00063 #elif defined(USE_MEMORY_PTMALLOC2)
00064 // This doesn't appear to work in Linux; perhaps it is clashing with
00065 // the system library.  It also doesn't appear to be thread-safe on
00066 // OSX.
00067 
00068 /////////////////////////////////////////////////////////////////////
00069 //
00070 // Memory manager: PTMALLOC2
00071 //
00072 // Ptmalloc2 is a derivative of Doug Lea's memory manager that was
00073 // made thread-safe by Wolfram Gloger, then was ported to windows by
00074 // Niall Douglas.  It is not quite as fast as dlmalloc (because the
00075 // thread-safety constructs take a certain amount of CPU time), but
00076 // it's still much faster than the windows allocator.
00077 //
00078 /////////////////////////////////////////////////////////////////////
00079 
00080 #define USE_DL_PREFIX 1
00081 #define NO_MALLINFO 1
00082 #ifdef _DEBUG
00083   #define MALLOC_DEBUG 2
00084 #endif
00085 #include "ptmalloc2_smp_src.cxx"
00086 
00087 #define call_malloc dlmalloc
00088 #define call_realloc dlrealloc
00089 #define call_free dlfree
00090 #undef MEMORY_HOOK_MALLOC_LOCK
00091 
00092 #else
00093 
00094 /////////////////////////////////////////////////////////////////////
00095 //
00096 // Memory manager: MALLOC
00097 //
00098 // This option uses the built-in system allocator.  This is a good
00099 // choice on linux, but it's a terrible choice on windows.
00100 //
00101 /////////////////////////////////////////////////////////////////////
00102 
00103 #define call_malloc malloc
00104 #define call_realloc realloc
00105 #define call_free free
00106 #undef MEMORY_HOOK_MALLOC_LOCK
00107 
00108 #endif  // USE_MEMORY_*
00109 
00110 
00111 ////////////////////////////////////////////////////////////////////
00112 //     Function: MemoryHook::Constructor
00113 //       Access: Public
00114 //  Description: 
00115 ////////////////////////////////////////////////////////////////////
00116 MemoryHook::
00117 MemoryHook() {
00118 #ifdef WIN32
00119 
00120   // Windows case.
00121   SYSTEM_INFO sysinfo;
00122   GetSystemInfo(&sysinfo);
00123 
00124   _page_size = (size_t)sysinfo.dwPageSize;
00125 
00126 #else
00127 
00128   // Posix case.
00129   _page_size = sysconf(_SC_PAGESIZE);
00130 
00131 #endif  // WIN32
00132 
00133 #ifdef DO_MEMORY_USAGE
00134   _total_heap_single_size = 0;
00135   _total_heap_array_size = 0;
00136   _requested_heap_size = 0;
00137   _total_mmap_size = 0;
00138   _max_heap_size = ~(size_t)0;
00139 #endif
00140 }
00141 
00142 ////////////////////////////////////////////////////////////////////
00143 //     Function: MemoryHook::Copy Constructor
00144 //       Access: Public
00145 //  Description: 
00146 ////////////////////////////////////////////////////////////////////
00147 MemoryHook::
00148 MemoryHook(const MemoryHook &copy) :
00149   _page_size(copy._page_size)
00150 {
00151 #ifdef DO_MEMORY_USAGE
00152   _total_heap_single_size = copy._total_heap_single_size;
00153   _total_heap_array_size = copy._total_heap_array_size;
00154   _requested_heap_size = copy._requested_heap_size;
00155   _total_mmap_size = copy._total_mmap_size;
00156   _max_heap_size = copy._max_heap_size;
00157 #endif
00158 
00159   ((MutexImpl &)copy._lock).acquire();
00160   _deleted_chains = copy._deleted_chains;
00161   ((MutexImpl &)copy._lock).release();
00162 }
00163 
00164 ////////////////////////////////////////////////////////////////////
00165 //     Function: MemoryHook::Destructor
00166 //       Access: Public, Virtual
00167 //  Description: 
00168 ////////////////////////////////////////////////////////////////////
00169 MemoryHook::
00170 ~MemoryHook() {
00171   // Really, we only have this destructor to shut up gcc about the
00172   // virtual functions warning.
00173 }
00174 
00175 ////////////////////////////////////////////////////////////////////
00176 //     Function: MemoryHook::heap_alloc_single
00177 //       Access: Public, Virtual
00178 //  Description: Allocates a block of memory from the heap, similar to
00179 //               malloc().  This will never return NULL; it will abort
00180 //               instead if memory is not available.
00181 //
00182 //               This particular function should be used to allocate
00183 //               memory for a single object, as opposed to an array.
00184 //               The only difference is in the bookkeeping.
00185 ////////////////////////////////////////////////////////////////////
00186 void *MemoryHook::
00187 heap_alloc_single(size_t size) {
00188 #ifdef MEMORY_HOOK_MALLOC_LOCK
00189   _lock.acquire();
00190   void *alloc = call_malloc(inflate_size(size));
00191   _lock.release();
00192 #else
00193   void *alloc = call_malloc(inflate_size(size));
00194 #endif
00195 
00196   while (alloc == (void *)NULL) {
00197     alloc_fail();
00198 #ifdef MEMORY_HOOK_MALLOC_LOCK
00199     _lock.acquire();
00200     alloc = call_malloc(inflate_size(size));
00201     _lock.release();
00202 #else
00203     alloc = call_malloc(inflate_size(size));
00204 #endif
00205   }
00206 
00207 #ifdef DO_MEMORY_USAGE
00208   // In the DO_MEMORY_USAGE case, we want to track the total size of
00209   // allocated bytes on the heap.
00210   AtomicAdjust::add(_total_heap_single_size, (AtomicAdjust::Integer)size);
00211   if ((size_t)AtomicAdjust::get(_total_heap_single_size) + 
00212       (size_t)AtomicAdjust::get(_total_heap_array_size) >
00213       _max_heap_size) {
00214     overflow_heap_size();
00215   }
00216 #endif  // DO_MEMORY_USAGE
00217 
00218   return alloc_to_ptr(alloc, size);
00219 }
00220 
00221 ////////////////////////////////////////////////////////////////////
00222 //     Function: MemoryHook::heap_free_single
00223 //       Access: Public, Virtual
00224 //  Description: Releases a block of memory previously allocated via
00225 //               heap_alloc_single.
00226 ////////////////////////////////////////////////////////////////////
00227 void MemoryHook::
00228 heap_free_single(void *ptr) {
00229   size_t size;
00230   void *alloc = ptr_to_alloc(ptr, size);
00231 
00232 #ifdef DO_MEMORY_USAGE
00233   assert((int)size <= _total_heap_single_size);
00234   AtomicAdjust::add(_total_heap_single_size, -(AtomicAdjust::Integer)size);
00235 #endif  // DO_MEMORY_USAGE
00236 
00237 #ifdef MEMORY_HOOK_MALLOC_LOCK
00238   _lock.acquire();
00239   call_free(alloc);
00240   _lock.release();
00241 #else
00242   call_free(alloc);
00243 #endif
00244 }
00245 
00246 ////////////////////////////////////////////////////////////////////
00247 //     Function: MemoryHook::heap_alloc_array
00248 //       Access: Public, Virtual
00249 //  Description: Allocates a block of memory from the heap, similar to
00250 //               malloc().  This will never return NULL; it will abort
00251 //               instead if memory is not available.
00252 //
00253 //               This particular function should be used to allocate
00254 //               memory for an array of objects, as opposed to a
00255 //               single object.  The only difference is in the
00256 //               bookkeeping.
00257 ////////////////////////////////////////////////////////////////////
00258 void *MemoryHook::
00259 heap_alloc_array(size_t size) {
00260 #ifdef MEMORY_HOOK_MALLOC_LOCK
00261   _lock.acquire();
00262   void *alloc = call_malloc(inflate_size(size));
00263   _lock.release();
00264 #else
00265   void *alloc = call_malloc(inflate_size(size));
00266 #endif
00267 
00268   while (alloc == (void *)NULL) {
00269     alloc_fail();
00270 #ifdef MEMORY_HOOK_MALLOC_LOCK
00271     _lock.acquire();
00272     alloc = call_malloc(inflate_size(size));
00273     _lock.release();
00274 #else
00275     alloc = call_malloc(inflate_size(size));
00276 #endif
00277   }
00278 
00279 #ifdef DO_MEMORY_USAGE
00280   // In the DO_MEMORY_USAGE case, we want to track the total size of
00281   // allocated bytes on the heap.
00282   AtomicAdjust::add(_total_heap_array_size, (AtomicAdjust::Integer)size);
00283   if ((size_t)AtomicAdjust::get(_total_heap_single_size) + 
00284       (size_t)AtomicAdjust::get(_total_heap_array_size) >
00285       _max_heap_size) {
00286     overflow_heap_size();
00287   }
00288 #endif  // DO_MEMORY_USAGE
00289 
00290   return alloc_to_ptr(alloc, size);
00291 }
00292 
00293 ////////////////////////////////////////////////////////////////////
00294 //     Function: MemoryHook::heap_realloc_array
00295 //       Access: Public, Virtual
00296 //  Description: Resizes a block of memory previously returned from
00297 //               heap_alloc_array.
00298 ////////////////////////////////////////////////////////////////////
00299 void *MemoryHook::
00300 heap_realloc_array(void *ptr, size_t size) {
00301   size_t orig_size;
00302   void *alloc = ptr_to_alloc(ptr, orig_size);
00303 
00304 #ifdef DO_MEMORY_USAGE
00305   assert((AtomicAdjust::Integer)orig_size <= _total_heap_array_size);
00306   AtomicAdjust::add(_total_heap_array_size, (AtomicAdjust::Integer)size-(AtomicAdjust::Integer)orig_size);
00307 #endif  // DO_MEMORY_USAGE
00308 
00309 #ifdef MEMORY_HOOK_MALLOC_LOCK
00310   _lock.acquire();
00311   alloc = call_realloc(alloc, inflate_size(size));
00312   _lock.release();
00313 #else
00314   alloc = call_realloc(alloc, inflate_size(size));
00315 #endif
00316 
00317   while (alloc == (void *)NULL) {
00318     alloc_fail();
00319     
00320     // Recover the original pointer.
00321     alloc = ptr_to_alloc(ptr, orig_size);
00322 
00323 #ifdef MEMORY_HOOK_MALLOC_LOCK
00324     _lock.acquire();
00325     alloc = call_realloc(alloc, inflate_size(size));
00326     _lock.release();
00327 #else
00328     alloc = call_realloc(alloc, inflate_size(size));
00329 #endif
00330   }
00331 
00332   return alloc_to_ptr(alloc, size);
00333 }
00334 
00335 ////////////////////////////////////////////////////////////////////
00336 //     Function: MemoryHook::heap_free_array
00337 //       Access: Public, Virtual
00338 //  Description: Releases a block of memory previously allocated via
00339 //               heap_alloc_array.
00340 ////////////////////////////////////////////////////////////////////
00341 void MemoryHook::
00342 heap_free_array(void *ptr) {
00343   size_t size;
00344   void *alloc = ptr_to_alloc(ptr, size);
00345 
00346 #ifdef DO_MEMORY_USAGE
00347   assert((int)size <= _total_heap_array_size);
00348   AtomicAdjust::add(_total_heap_array_size, -(AtomicAdjust::Integer)size);
00349 #endif  // DO_MEMORY_USAGE
00350 
00351 #ifdef MEMORY_HOOK_MALLOC_LOCK
00352   _lock.acquire();
00353   call_free(alloc);
00354   _lock.release();
00355 #else
00356   call_free(alloc);
00357 #endif
00358 }
00359 
00360 ////////////////////////////////////////////////////////////////////
00361 //     Function: MemoryHook::heap_trim
00362 //       Access: Public
00363 //  Description: Attempts to release memory back to the system, if
00364 //               possible.  The pad argument is the minimum amount of
00365 //               unused memory to keep in the heap (against future
00366 //               allocations).  Any memory above that may be released
00367 //               to the system, reducing the memory size of this
00368 //               process.  There is no guarantee that any memory may
00369 //               be released.
00370 //
00371 //               Returns true if any memory was actually released,
00372 //               false otherwise.
00373 ////////////////////////////////////////////////////////////////////
00374 bool MemoryHook::
00375 heap_trim(size_t pad) {
00376   bool trimmed = false;
00377 
00378 #if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
00379   // Since malloc_trim() isn't standard C, we can't be sure it exists
00380   // on a given platform.  But if we're using dlmalloc, we know we
00381   // have dlmalloc_trim.
00382   _lock.acquire();
00383   if (dlmalloc_trim(pad)) {
00384     trimmed = true;
00385   }
00386   _lock.release();
00387 #endif
00388 
00389 #ifdef WIN32
00390   // Also, on Windows we have _heapmin().
00391   if (_heapmin() == 0) {
00392     trimmed = true;
00393   }
00394 #endif
00395 
00396   return trimmed;
00397 }
00398 
00399 ////////////////////////////////////////////////////////////////////
00400 //     Function: MemoryHook::mmap_alloc
00401 //       Access: Public, Virtual
00402 //  Description: Allocates a raw page or pages of memory directly from
00403 //               the OS.  This will be in a different address space
00404 //               from the memory allocated by heap_alloc(), and so it
00405 //               won't contribute to fragmentation of that memory.
00406 //
00407 //               The allocation size must be an integer multiple of
00408 //               the page size.  Use round_to_page_size() if there is
00409 //               any doubt.
00410 //
00411 //               If allow_exec is true, the memory will be flagged so
00412 //               that it is legal to execute code that has been
00413 //               written to this memory.
00414 ////////////////////////////////////////////////////////////////////
00415 void *MemoryHook::
00416 mmap_alloc(size_t size, bool allow_exec) {
00417   assert((size % _page_size) == 0);
00418 
00419 #ifdef DO_MEMORY_USAGE
00420   _total_mmap_size += size;
00421 #endif
00422 
00423 #ifdef WIN32
00424 
00425   // Windows case.
00426   void *ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE,
00427                            allow_exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
00428   if (ptr == (void *)NULL) {
00429     DWORD err = GetLastError();
00430     cerr << "Couldn't allocate memory page of size " << size << ": ";
00431 
00432     PVOID buffer;
00433     DWORD length = 
00434       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
00435                     NULL, err, 0, (LPTSTR)&buffer, 0, NULL);
00436     if (length != 0) {
00437       cerr << (char *)buffer << "\n";
00438     } else {
00439       cerr << "Error code " << err << "\n";
00440     }
00441     LocalFree(buffer);
00442     abort();
00443   }
00444 
00445   return ptr;
00446 
00447 #else
00448 
00449   // Posix case.
00450   int prot = PROT_READ | PROT_WRITE;
00451   if (allow_exec) {
00452     prot |= PROT_EXEC;
00453   }
00454   void *ptr = mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
00455   if (ptr == (void *)-1) {
00456     perror("mmap");
00457     abort();
00458   }
00459 
00460   return ptr;
00461 
00462 #endif  // WIN32
00463 }
00464 
00465 ////////////////////////////////////////////////////////////////////
00466 //     Function: MemoryHook::mmap_free
00467 //       Access: Public, Virtual
00468 //  Description: Frees a block of memory previously allocated via
00469 //               mmap_alloc().  You must know how large the block was.
00470 ////////////////////////////////////////////////////////////////////
00471 void MemoryHook::
00472 mmap_free(void *ptr, size_t size) {
00473   assert((size % _page_size) == 0);
00474 
00475 #ifdef DO_MEMORY_USAGE
00476   assert((int)size <= _total_mmap_size);
00477   _total_mmap_size -= size;
00478 #endif
00479 
00480 #ifdef WIN32
00481   VirtualFree(ptr, 0, MEM_RELEASE);
00482 #else  
00483   munmap(ptr, size);
00484 #endif
00485 }
00486 
00487 ////////////////////////////////////////////////////////////////////
00488 //     Function: MemoryHook::mark_pointer
00489 //       Access: Public, Virtual
00490 //  Description: This special method exists only to provide a callback
00491 //               hook into MemoryUsage.  It indicates that the
00492 //               indicated pointer, allocated from somewhere other
00493 //               than a call to heap_alloc(), now contains a pointer
00494 //               to the indicated ReferenceCount object.  If orig_size
00495 //               is 0, it indicates that the ReferenceCount object has
00496 //               been destroyed.
00497 ////////////////////////////////////////////////////////////////////
00498 void MemoryHook::
00499 mark_pointer(void *, size_t, ReferenceCount *) {
00500 }
00501 
00502 ////////////////////////////////////////////////////////////////////
00503 //     Function: MemoryHook::get_deleted_chain
00504 //       Access: Public
00505 //  Description: Returns a pointer to a global DeletedBufferChain
00506 //               object suitable for allocating arrays of the
00507 //               indicated size.  There is one unique
00508 //               DeletedBufferChain object for every different size.
00509 ////////////////////////////////////////////////////////////////////
00510 DeletedBufferChain *MemoryHook::
00511 get_deleted_chain(size_t buffer_size) {
00512   DeletedBufferChain *chain;
00513 
00514   _lock.acquire();
00515   DeletedChains::iterator dci = _deleted_chains.find(buffer_size);
00516   if (dci != _deleted_chains.end()) {
00517     chain = (*dci).second;
00518   } else {
00519     // Once allocated, this DeletedBufferChain object is never deleted.
00520     chain = new DeletedBufferChain(buffer_size);
00521     _deleted_chains.insert(DeletedChains::value_type(buffer_size, chain));
00522   }
00523   
00524   _lock.release();
00525   return chain;
00526 }
00527 
00528 ////////////////////////////////////////////////////////////////////
00529 //     Function: MemoryHook::alloc_fail
00530 //       Access: Protected, Virtual
00531 //  Description: This callback method is called whenever a low-level
00532 //               call to call_malloc() has returned NULL, indicating
00533 //               failure.
00534 //
00535 //               Since this method is called very low-level, and may
00536 //               be in the middle of any number of critical sections,
00537 //               it will be difficult for this callback initiate any
00538 //               emergency high-level operation to make more memory
00539 //               available.  However, this module is set up to assume
00540 //               that that's what this method does, and will make
00541 //               another alloc attempt after it returns.  Probably the
00542 //               only sensible thing this method can do, however, is
00543 //               just to display a message and abort.
00544 ////////////////////////////////////////////////////////////////////
00545 void MemoryHook::
00546 alloc_fail() {
00547   cerr << "Out of memory!\n";
00548   abort();
00549 }
00550 
00551 #ifdef DO_MEMORY_USAGE
00552 ////////////////////////////////////////////////////////////////////
00553 //     Function: MemoryHook::overflow_heap_size
00554 //       Access: Protected, Virtual
00555 //  Description: This callback method is called whenever the total
00556 //               allocated heap size exceeds _max_heap_size.  It's
00557 //               mainly intended for reporting memory leaks, on the
00558 //               assumption that once we cross some specified
00559 //               threshold, we're just leaking memory.
00560 //
00561 //               The implementation for this method is in MemoryUsage.
00562 ////////////////////////////////////////////////////////////////////
00563 void MemoryHook::
00564 overflow_heap_size() {
00565   _max_heap_size = ~(size_t)0;
00566 }
00567 #endif  // DO_MEMORY_USAGE
 All Classes Functions Variables Enumerations