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 //     Function: MemoryHook::Constructor
00112 //       Access: Public
00113 //  Description: 
00114 ////////////////////////////////////////////////////////////////////
00115 MemoryHook::
00116 MemoryHook() {
00117 #ifdef WIN32
00118 
00119   // Windows case.
00120   SYSTEM_INFO sysinfo;
00121   GetSystemInfo(&sysinfo);
00122 
00123   _page_size = (size_t)sysinfo.dwPageSize;
00124 
00125 #else
00126 
00127   // Posix case.
00128   _page_size = sysconf(_SC_PAGESIZE);
00129 
00130 #endif  // WIN32
00131 
00132 #ifdef DO_MEMORY_USAGE
00133   _total_heap_single_size = 0;
00134   _total_heap_array_size = 0;
00135   _requested_heap_size = 0;
00136   _total_mmap_size = 0;
00137   _max_heap_size = ~(size_t)0;
00138 #endif
00139 }
00140 
00141 ////////////////////////////////////////////////////////////////////
00142 //     Function: MemoryHook::Copy Constructor
00143 //       Access: Public
00144 //  Description: 
00145 ////////////////////////////////////////////////////////////////////
00146 MemoryHook::
00147 MemoryHook(const MemoryHook &copy) :
00148   _page_size(copy._page_size)
00149 {
00150 #ifdef DO_MEMORY_USAGE
00151   _total_heap_single_size = copy._total_heap_single_size;
00152   _total_heap_array_size = copy._total_heap_array_size;
00153   _requested_heap_size = copy._requested_heap_size;
00154   _total_mmap_size = copy._total_mmap_size;
00155   _max_heap_size = copy._max_heap_size;
00156 #endif
00157 
00158   ((MutexImpl &)copy._lock).acquire();
00159   _deleted_chains = copy._deleted_chains;
00160   ((MutexImpl &)copy._lock).release();
00161 }
00162 
00163 ////////////////////////////////////////////////////////////////////
00164 //     Function: MemoryHook::Destructor
00165 //       Access: Public, Virtual
00166 //  Description: 
00167 ////////////////////////////////////////////////////////////////////
00168 MemoryHook::
00169 ~MemoryHook() {
00170   // Really, we only have this destructor to shut up gcc about the
00171   // virtual functions warning.
00172 }
00173 
00174 ////////////////////////////////////////////////////////////////////
00175 //     Function: MemoryHook::heap_alloc_single
00176 //       Access: Public, Virtual
00177 //  Description: Allocates a block of memory from the heap, similar to
00178 //               malloc().  This will never return NULL; it will abort
00179 //               instead if memory is not available.
00180 //
00181 //               This particular function should be used to allocate
00182 //               memory for a single object, as opposed to an array.
00183 //               The only difference is in the bookkeeping.
00184 ////////////////////////////////////////////////////////////////////
00185 void *MemoryHook::
00186 heap_alloc_single(size_t size) {
00187   size_t inflated_size = inflate_size(size);
00188 
00189 #ifdef MEMORY_HOOK_MALLOC_LOCK
00190   _lock.acquire();
00191   void *alloc = call_malloc(inflated_size);
00192   _lock.release();
00193 #else
00194   void *alloc = call_malloc(inflated_size);
00195 #endif
00196 
00197   while (alloc == (void *)NULL) {
00198     alloc_fail(inflated_size);
00199 #ifdef MEMORY_HOOK_MALLOC_LOCK
00200     _lock.acquire();
00201     alloc = call_malloc(inflated_size);
00202     _lock.release();
00203 #else
00204     alloc = call_malloc(inflated_size);
00205 #endif
00206   }
00207 
00208 #ifdef DO_MEMORY_USAGE
00209   // In the DO_MEMORY_USAGE case, we want to track the total size of
00210   // allocated bytes on the heap.
00211   AtomicAdjust::add(_total_heap_single_size, (AtomicAdjust::Integer)size);
00212   if ((size_t)AtomicAdjust::get(_total_heap_single_size) + 
00213       (size_t)AtomicAdjust::get(_total_heap_array_size) >
00214       _max_heap_size) {
00215     overflow_heap_size();
00216   }
00217 #endif  // DO_MEMORY_USAGE
00218 
00219   void *ptr = alloc_to_ptr(alloc, size);
00220   assert(ptr >= alloc && (char *)ptr + size <= (char *)alloc + inflated_size);
00221   return ptr;
00222 }
00223 
00224 ////////////////////////////////////////////////////////////////////
00225 //     Function: MemoryHook::heap_free_single
00226 //       Access: Public, Virtual
00227 //  Description: Releases a block of memory previously allocated via
00228 //               heap_alloc_single.
00229 ////////////////////////////////////////////////////////////////////
00230 void MemoryHook::
00231 heap_free_single(void *ptr) {
00232   size_t size;
00233   void *alloc = ptr_to_alloc(ptr, size);
00234 
00235 #ifdef DO_MEMORY_USAGE
00236   assert((int)size <= _total_heap_single_size);
00237   AtomicAdjust::add(_total_heap_single_size, -(AtomicAdjust::Integer)size);
00238 #endif  // DO_MEMORY_USAGE
00239 
00240 #ifdef MEMORY_HOOK_MALLOC_LOCK
00241   _lock.acquire();
00242   call_free(alloc);
00243   _lock.release();
00244 #else
00245   call_free(alloc);
00246 #endif
00247 }
00248 
00249 ////////////////////////////////////////////////////////////////////
00250 //     Function: MemoryHook::heap_alloc_array
00251 //       Access: Public, Virtual
00252 //  Description: Allocates a block of memory from the heap, similar to
00253 //               malloc().  This will never return NULL; it will abort
00254 //               instead if memory is not available.
00255 //
00256 //               This particular function should be used to allocate
00257 //               memory for an array of objects, as opposed to a
00258 //               single object.  The only difference is in the
00259 //               bookkeeping.
00260 ////////////////////////////////////////////////////////////////////
00261 void *MemoryHook::
00262 heap_alloc_array(size_t size) {
00263   size_t inflated_size = inflate_size(size);
00264 
00265 #ifdef MEMORY_HOOK_MALLOC_LOCK
00266   _lock.acquire();
00267   void *alloc = call_malloc(inflated_size);
00268   _lock.release();
00269 #else
00270   void *alloc = call_malloc(inflated_size);
00271 #endif
00272 
00273   while (alloc == (void *)NULL) {
00274     alloc_fail(inflated_size);
00275 #ifdef MEMORY_HOOK_MALLOC_LOCK
00276     _lock.acquire();
00277     alloc = call_malloc(inflated_size);
00278     _lock.release();
00279 #else
00280     alloc = call_malloc(inflated_size);
00281 #endif
00282   }
00283 
00284 #ifdef DO_MEMORY_USAGE
00285   // In the DO_MEMORY_USAGE case, we want to track the total size of
00286   // allocated bytes on the heap.
00287   AtomicAdjust::add(_total_heap_array_size, (AtomicAdjust::Integer)size);
00288   if ((size_t)AtomicAdjust::get(_total_heap_single_size) + 
00289       (size_t)AtomicAdjust::get(_total_heap_array_size) >
00290       _max_heap_size) {
00291     overflow_heap_size();
00292   }
00293 #endif  // DO_MEMORY_USAGE
00294 
00295   void *ptr = alloc_to_ptr(alloc, size);
00296   assert(ptr >= alloc && (char *)ptr + size <= (char *)alloc + inflated_size);
00297   return ptr;
00298 }
00299 
00300 ////////////////////////////////////////////////////////////////////
00301 //     Function: MemoryHook::heap_realloc_array
00302 //       Access: Public, Virtual
00303 //  Description: Resizes a block of memory previously returned from
00304 //               heap_alloc_array.
00305 ////////////////////////////////////////////////////////////////////
00306 void *MemoryHook::
00307 heap_realloc_array(void *ptr, size_t size) {
00308   size_t orig_size;
00309   void *alloc = ptr_to_alloc(ptr, orig_size);
00310 
00311 #ifdef DO_MEMORY_USAGE
00312   assert((AtomicAdjust::Integer)orig_size <= _total_heap_array_size);
00313   AtomicAdjust::add(_total_heap_array_size, (AtomicAdjust::Integer)size-(AtomicAdjust::Integer)orig_size);
00314 #endif  // DO_MEMORY_USAGE
00315 
00316   size_t inflated_size = inflate_size(size);
00317 
00318   void *alloc1 = alloc;
00319 #ifdef MEMORY_HOOK_MALLOC_LOCK
00320   _lock.acquire();
00321   alloc1 = call_realloc(alloc1, inflated_size);
00322   _lock.release();
00323 #else
00324   alloc1 = call_realloc(alloc1, inflated_size);
00325 #endif
00326 
00327   while (alloc1 == (void *)NULL) {
00328     alloc_fail(inflated_size);
00329     
00330     // Recover the original pointer.
00331     alloc1 = alloc;
00332 
00333 #ifdef MEMORY_HOOK_MALLOC_LOCK
00334     _lock.acquire();
00335     alloc1 = call_realloc(alloc1, inflated_size);
00336     _lock.release();
00337 #else
00338     alloc1 = call_realloc(alloc1, inflated_size);
00339 #endif
00340   }
00341 
00342   void *ptr1 = alloc_to_ptr(alloc1, size);
00343   assert(ptr1 >= alloc1 && (char *)ptr1 + size <= (char *)alloc1 + inflated_size);
00344 #if defined(MEMORY_HOOK_DO_ALIGN)
00345   // We might have to shift the memory to account for the new offset
00346   // due to the alignment.
00347   size_t orig_delta = (char *)ptr - (char *)alloc;
00348   size_t new_delta = (char *)ptr1 - (char *)alloc1;
00349   if (orig_delta != new_delta) {
00350     memmove((char *)alloc1 + new_delta, (char *)alloc1 + orig_delta, min(size, orig_size));
00351   }
00352 #endif  // MEMORY_HOOK_DO_ALIGN
00353   return ptr1;
00354 }
00355 
00356 ////////////////////////////////////////////////////////////////////
00357 //     Function: MemoryHook::heap_free_array
00358 //       Access: Public, Virtual
00359 //  Description: Releases a block of memory previously allocated via
00360 //               heap_alloc_array.
00361 ////////////////////////////////////////////////////////////////////
00362 void MemoryHook::
00363 heap_free_array(void *ptr) {
00364   size_t size;
00365   void *alloc = ptr_to_alloc(ptr, size);
00366 
00367 #ifdef DO_MEMORY_USAGE
00368   assert((int)size <= _total_heap_array_size);
00369   AtomicAdjust::add(_total_heap_array_size, -(AtomicAdjust::Integer)size);
00370 #endif  // DO_MEMORY_USAGE
00371 
00372 #ifdef MEMORY_HOOK_MALLOC_LOCK
00373   _lock.acquire();
00374   call_free(alloc);
00375   _lock.release();
00376 #else
00377   call_free(alloc);
00378 #endif
00379 }
00380 
00381 ////////////////////////////////////////////////////////////////////
00382 //     Function: MemoryHook::heap_trim
00383 //       Access: Public
00384 //  Description: Attempts to release memory back to the system, if
00385 //               possible.  The pad argument is the minimum amount of
00386 //               unused memory to keep in the heap (against future
00387 //               allocations).  Any memory above that may be released
00388 //               to the system, reducing the memory size of this
00389 //               process.  There is no guarantee that any memory may
00390 //               be released.
00391 //
00392 //               Returns true if any memory was actually released,
00393 //               false otherwise.
00394 ////////////////////////////////////////////////////////////////////
00395 bool MemoryHook::
00396 heap_trim(size_t pad) {
00397   bool trimmed = false;
00398 
00399 #if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
00400   // Since malloc_trim() isn't standard C, we can't be sure it exists
00401   // on a given platform.  But if we're using dlmalloc, we know we
00402   // have dlmalloc_trim.
00403   _lock.acquire();
00404   if (dlmalloc_trim(pad)) {
00405     trimmed = true;
00406   }
00407   _lock.release();
00408 #endif
00409 
00410 #ifdef WIN32
00411   // Also, on Windows we have _heapmin().
00412   if (_heapmin() == 0) {
00413     trimmed = true;
00414   }
00415 #endif
00416 
00417   return trimmed;
00418 }
00419 
00420 ////////////////////////////////////////////////////////////////////
00421 //     Function: MemoryHook::mmap_alloc
00422 //       Access: Public, Virtual
00423 //  Description: Allocates a raw page or pages of memory directly from
00424 //               the OS.  This will be in a different address space
00425 //               from the memory allocated by heap_alloc(), and so it
00426 //               won't contribute to fragmentation of that memory.
00427 //
00428 //               The allocation size must be an integer multiple of
00429 //               the page size.  Use round_to_page_size() if there is
00430 //               any doubt.
00431 //
00432 //               If allow_exec is true, the memory will be flagged so
00433 //               that it is legal to execute code that has been
00434 //               written to this memory.
00435 ////////////////////////////////////////////////////////////////////
00436 void *MemoryHook::
00437 mmap_alloc(size_t size, bool allow_exec) {
00438   assert((size % _page_size) == 0);
00439 
00440 #ifdef DO_MEMORY_USAGE
00441   _total_mmap_size += size;
00442 #endif
00443 
00444 #ifdef WIN32
00445 
00446   // Windows case.
00447   void *ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE,
00448                            allow_exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
00449   if (ptr == (void *)NULL) {
00450     DWORD err = GetLastError();
00451     cerr << "Couldn't allocate memory page of size " << size << ": ";
00452 
00453     PVOID buffer;
00454     DWORD length = 
00455       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
00456                     NULL, err, 0, (LPTSTR)&buffer, 0, NULL);
00457     if (length != 0) {
00458       cerr << (char *)buffer << "\n";
00459     } else {
00460       cerr << "Error code " << err << "\n";
00461     }
00462     LocalFree(buffer);
00463     abort();
00464   }
00465 
00466   return ptr;
00467 
00468 #else
00469 
00470   // Posix case.
00471   int prot = PROT_READ | PROT_WRITE;
00472   if (allow_exec) {
00473     prot |= PROT_EXEC;
00474   }
00475   void *ptr = mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
00476   if (ptr == (void *)-1) {
00477     perror("mmap");
00478     abort();
00479   }
00480 
00481   return ptr;
00482 
00483 #endif  // WIN32
00484 }
00485 
00486 ////////////////////////////////////////////////////////////////////
00487 //     Function: MemoryHook::mmap_free
00488 //       Access: Public, Virtual
00489 //  Description: Frees a block of memory previously allocated via
00490 //               mmap_alloc().  You must know how large the block was.
00491 ////////////////////////////////////////////////////////////////////
00492 void MemoryHook::
00493 mmap_free(void *ptr, size_t size) {
00494   assert((size % _page_size) == 0);
00495 
00496 #ifdef DO_MEMORY_USAGE
00497   assert((int)size <= _total_mmap_size);
00498   _total_mmap_size -= size;
00499 #endif
00500 
00501 #ifdef WIN32
00502   VirtualFree(ptr, 0, MEM_RELEASE);
00503 #else  
00504   munmap(ptr, size);
00505 #endif
00506 }
00507 
00508 ////////////////////////////////////////////////////////////////////
00509 //     Function: MemoryHook::mark_pointer
00510 //       Access: Public, Virtual
00511 //  Description: This special method exists only to provide a callback
00512 //               hook into MemoryUsage.  It indicates that the
00513 //               indicated pointer, allocated from somewhere other
00514 //               than a call to heap_alloc(), now contains a pointer
00515 //               to the indicated ReferenceCount object.  If orig_size
00516 //               is 0, it indicates that the ReferenceCount object has
00517 //               been destroyed.
00518 ////////////////////////////////////////////////////////////////////
00519 void MemoryHook::
00520 mark_pointer(void *, size_t, ReferenceCount *) {
00521 }
00522 
00523 ////////////////////////////////////////////////////////////////////
00524 //     Function: MemoryHook::get_deleted_chain
00525 //       Access: Public
00526 //  Description: Returns a pointer to a global DeletedBufferChain
00527 //               object suitable for allocating arrays of the
00528 //               indicated size.  There is one unique
00529 //               DeletedBufferChain object for every different size.
00530 ////////////////////////////////////////////////////////////////////
00531 DeletedBufferChain *MemoryHook::
00532 get_deleted_chain(size_t buffer_size) {
00533   DeletedBufferChain *chain;
00534 
00535   _lock.acquire();
00536   DeletedChains::iterator dci = _deleted_chains.find(buffer_size);
00537   if (dci != _deleted_chains.end()) {
00538     chain = (*dci).second;
00539   } else {
00540     // Once allocated, this DeletedBufferChain object is never deleted.
00541     chain = new DeletedBufferChain(buffer_size);
00542     _deleted_chains.insert(DeletedChains::value_type(buffer_size, chain));
00543   }
00544   
00545   _lock.release();
00546   return chain;
00547 }
00548 
00549 ////////////////////////////////////////////////////////////////////
00550 //     Function: MemoryHook::alloc_fail
00551 //       Access: Protected, Virtual
00552 //  Description: This callback method is called whenever a low-level
00553 //               call to call_malloc() has returned NULL, indicating
00554 //               failure.
00555 //
00556 //               Since this method is called very low-level, and may
00557 //               be in the middle of any number of critical sections,
00558 //               it will be difficult for this callback initiate any
00559 //               emergency high-level operation to make more memory
00560 //               available.  However, this module is set up to assume
00561 //               that that's what this method does, and will make
00562 //               another alloc attempt after it returns.  Probably the
00563 //               only sensible thing this method can do, however, is
00564 //               just to display a message and abort.
00565 ////////////////////////////////////////////////////////////////////
00566 void MemoryHook::
00567 alloc_fail(size_t attempted_size) {
00568   cerr << "Out of memory allocating " << attempted_size << " bytes\n";
00569   abort();
00570 }
00571 
00572 #ifdef DO_MEMORY_USAGE
00573 ////////////////////////////////////////////////////////////////////
00574 //     Function: MemoryHook::overflow_heap_size
00575 //       Access: Protected, Virtual
00576 //  Description: This callback method is called whenever the total
00577 //               allocated heap size exceeds _max_heap_size.  It's
00578 //               mainly intended for reporting memory leaks, on the
00579 //               assumption that once we cross some specified
00580 //               threshold, we're just leaking memory.
00581 //
00582 //               The implementation for this method is in MemoryUsage.
00583 ////////////////////////////////////////////////////////////////////
00584 void MemoryHook::
00585 overflow_heap_size() {
00586   _max_heap_size = ~(size_t)0;
00587 }
00588 #endif  // DO_MEMORY_USAGE
 All Classes Functions Variables Enumerations