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