Panda3D
|
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 ©) : 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