Panda3D
|
00001 // Filename: memoryUsage.cxx 00002 // Created by: drose (25May00) 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 "memoryUsage.h" 00016 00017 #ifdef DO_MEMORY_USAGE 00018 00019 #include "memoryUsagePointers.h" 00020 #include "trueClock.h" 00021 #include "typedReferenceCount.h" 00022 #include "mutexImpl.h" 00023 #include "interrogate_request.h" 00024 00025 #if (defined(WIN32_VC) || defined (WIN64_VC)) && defined(_DEBUG) 00026 #include <crtdbg.h> 00027 #endif 00028 00029 #include "config_express.h" 00030 #include <algorithm> 00031 #include <iterator> 00032 00033 MemoryUsage *MemoryUsage::_global_ptr; 00034 00035 // This flag is used to protect the operator new/delete handlers 00036 // against recursive entry. 00037 bool MemoryUsage::_recursion_protect = false; 00038 00039 // The cutoff ages, in seconds, for the various buckets in the AgeHistogram. 00040 double MemoryUsage::AgeHistogram::_cutoff[MemoryUsage::AgeHistogram::num_buckets] = { 00041 0.0, 00042 0.1, 00043 1.0, 00044 10.0, 00045 60.0, 00046 }; 00047 00048 00049 //////////////////////////////////////////////////////////////////// 00050 // Function: MemoryUsage::TypeHistogram::add_info 00051 // Access: Public 00052 // Description: Adds a single entry to the histogram. 00053 //////////////////////////////////////////////////////////////////// 00054 void MemoryUsage::TypeHistogram:: 00055 add_info(TypeHandle type, MemoryInfo *info) { 00056 _counts[type].add_info(info); 00057 } 00058 00059 00060 // This class is a temporary class used only in TypeHistogram::show(), 00061 // below, to sort the types in descending order by counts. 00062 class TypeHistogramCountSorter { 00063 public: 00064 TypeHistogramCountSorter(const MemoryUsagePointerCounts &count, 00065 TypeHandle type) : 00066 _count(count), 00067 _type(type) 00068 { 00069 } 00070 bool operator < (const TypeHistogramCountSorter &other) const { 00071 return other._count < _count; 00072 } 00073 MemoryUsagePointerCounts _count; 00074 TypeHandle _type; 00075 }; 00076 00077 //////////////////////////////////////////////////////////////////// 00078 // Function: MemoryUsage::TypeHistogram::show 00079 // Access: Public 00080 // Description: Shows the contents of the histogram to nout. 00081 //////////////////////////////////////////////////////////////////// 00082 void MemoryUsage::TypeHistogram:: 00083 show() const { 00084 // First, copy the relevant information to a vector so we can sort 00085 // by counts. Don't use a pvector. 00086 typedef vector<TypeHistogramCountSorter> CountSorter; 00087 CountSorter count_sorter; 00088 Counts::const_iterator ci; 00089 for (ci = _counts.begin(); ci != _counts.end(); ++ci) { 00090 count_sorter.push_back 00091 (TypeHistogramCountSorter((*ci).second, (*ci).first)); 00092 } 00093 00094 sort(count_sorter.begin(), count_sorter.end()); 00095 00096 CountSorter::const_iterator vi; 00097 for (vi = count_sorter.begin(); vi != count_sorter.end(); ++vi) { 00098 TypeHandle type = (*vi)._type; 00099 if (type == TypeHandle::none()) { 00100 nout << "unknown"; 00101 } else { 00102 nout << type; 00103 } 00104 nout << " : " << (*vi)._count << "\n"; 00105 } 00106 } 00107 00108 //////////////////////////////////////////////////////////////////// 00109 // Function: MemoryUsage::TypeHistogram::clear 00110 // Access: Public 00111 // Description: Resets the histogram in preparation for new data. 00112 //////////////////////////////////////////////////////////////////// 00113 void MemoryUsage::TypeHistogram:: 00114 clear() { 00115 _counts.clear(); 00116 } 00117 00118 //////////////////////////////////////////////////////////////////// 00119 // Function: MemoryUsage::AgeHistogram::Constructor 00120 // Access: Public 00121 // Description: 00122 //////////////////////////////////////////////////////////////////// 00123 MemoryUsage::AgeHistogram:: 00124 AgeHistogram() { 00125 clear(); 00126 } 00127 00128 //////////////////////////////////////////////////////////////////// 00129 // Function: MemoryUsage::AgeHistogram::add_info 00130 // Access: Public 00131 // Description: Adds a single entry to the histogram. 00132 //////////////////////////////////////////////////////////////////// 00133 void MemoryUsage::AgeHistogram:: 00134 add_info(double age, MemoryInfo *info) { 00135 int bucket = choose_bucket(age); 00136 nassertv(bucket >= 0 && bucket < num_buckets); 00137 _counts[bucket].add_info(info); 00138 } 00139 00140 //////////////////////////////////////////////////////////////////// 00141 // Function: MemoryUsage::AgeHistogram::show 00142 // Access: Public 00143 // Description: Shows the contents of the histogram to nout. 00144 //////////////////////////////////////////////////////////////////// 00145 void MemoryUsage::AgeHistogram:: 00146 show() const { 00147 for (int i = 0; i < num_buckets - 1; i++) { 00148 nout << _cutoff[i] << " to " << _cutoff[i + 1] << " seconds old : "; 00149 _counts[i].output(nout); 00150 nout << "\n"; 00151 } 00152 nout << _cutoff[num_buckets - 1] << " seconds old and up : "; 00153 _counts[num_buckets - 1].output(nout); 00154 nout << "\n"; 00155 } 00156 00157 //////////////////////////////////////////////////////////////////// 00158 // Function: MemoryUsage::AgeHistogram::clear 00159 // Access: Public 00160 // Description: Resets the histogram in preparation for new data. 00161 //////////////////////////////////////////////////////////////////// 00162 void MemoryUsage::AgeHistogram:: 00163 clear() { 00164 for (int i = 0; i < num_buckets; i++) { 00165 _counts[i].clear(); 00166 } 00167 } 00168 00169 //////////////////////////////////////////////////////////////////// 00170 // Function: MemoryUsage::AgeHistogram::choose_bucket 00171 // Access: Private 00172 // Description: 00173 //////////////////////////////////////////////////////////////////// 00174 int MemoryUsage::AgeHistogram:: 00175 choose_bucket(double age) const { 00176 for (int i = num_buckets - 1; i >= 0; i--) { 00177 if (age >= _cutoff[i]) { 00178 return i; 00179 } 00180 } 00181 express_cat.error() 00182 << "No suitable bucket for age " << age << "\n"; 00183 return 0; 00184 } 00185 00186 //////////////////////////////////////////////////////////////////// 00187 // Function: MemoryUsage::heap_alloc_single 00188 // Access: Public, Virtual 00189 // Description: Allocates a block of memory from the heap, similar to 00190 // malloc(). This will never return NULL; it will abort 00191 // instead if memory is not available. 00192 //////////////////////////////////////////////////////////////////// 00193 void *MemoryUsage:: 00194 heap_alloc_single(size_t size) { 00195 void *ptr; 00196 00197 if (_recursion_protect) { 00198 ptr = MemoryHook::heap_alloc_single(size); 00199 if (express_cat.is_spam()) { 00200 express_cat.spam() 00201 << "Allocating pointer " << (void *)ptr 00202 << " during recursion protect.\n"; 00203 } 00204 00205 } else { 00206 if (_track_memory_usage) { 00207 ptr = MemoryHook::heap_alloc_single(size); 00208 /* 00209 if (express_cat.is_spam()) { 00210 express_cat.spam() 00211 << "Allocating pointer " << (void *)ptr 00212 << " of size " << size << ".\n"; 00213 } 00214 */ 00215 00216 get_global_ptr()->ns_record_void_pointer(ptr, size); 00217 00218 } else { 00219 ptr = MemoryHook::heap_alloc_single(size); 00220 } 00221 } 00222 00223 return ptr; 00224 } 00225 00226 //////////////////////////////////////////////////////////////////// 00227 // Function: MemoryUsage::heap_free_single 00228 // Access: Public, Virtual 00229 // Description: Releases a block of memory previously allocated via 00230 // heap_alloc_single. 00231 //////////////////////////////////////////////////////////////////// 00232 void MemoryUsage:: 00233 heap_free_single(void *ptr) { 00234 if (_recursion_protect) { 00235 if (express_cat.is_spam()) { 00236 express_cat.spam() 00237 << "Deleting pointer " << (void *)ptr 00238 << " during recursion protect.\n"; 00239 } 00240 MemoryHook::heap_free_single(ptr); 00241 00242 } else { 00243 if (_track_memory_usage) { 00244 /* 00245 if (express_cat.is_spam()) { 00246 express_cat.spam() 00247 << "Removing pointer " << (void *)ptr << "\n"; 00248 } 00249 */ 00250 ns_remove_void_pointer(ptr); 00251 MemoryHook::heap_free_single(ptr); 00252 } else { 00253 MemoryHook::heap_free_single(ptr); 00254 } 00255 } 00256 } 00257 00258 //////////////////////////////////////////////////////////////////// 00259 // Function: MemoryUsage::heap_alloc_array 00260 // Access: Public, Virtual 00261 // Description: Allocates a block of memory from the heap, similar to 00262 // malloc(). This will never return NULL; it will abort 00263 // instead if memory is not available. 00264 //////////////////////////////////////////////////////////////////// 00265 void *MemoryUsage:: 00266 heap_alloc_array(size_t size) { 00267 void *ptr; 00268 00269 if (_recursion_protect) { 00270 ptr = MemoryHook::heap_alloc_array(size); 00271 if (express_cat.is_spam()) { 00272 express_cat.spam() 00273 << "Allocating array pointer " << (void *)ptr 00274 << " during recursion protect.\n"; 00275 } 00276 00277 } else { 00278 if (_track_memory_usage) { 00279 ptr = MemoryHook::heap_alloc_array(size); 00280 /* 00281 if (express_cat.is_spam()) { 00282 express_cat.spam() 00283 << "Allocating array pointer " << (void *)ptr 00284 << " of size " << size << ".\n"; 00285 } 00286 */ 00287 00288 get_global_ptr()->ns_record_void_pointer(ptr, size); 00289 00290 } else { 00291 ptr = MemoryHook::heap_alloc_array(size); 00292 } 00293 } 00294 00295 return ptr; 00296 } 00297 00298 //////////////////////////////////////////////////////////////////// 00299 // Function: MemoryUsage::heap_realloc_array 00300 // Access: Public, Virtual 00301 // Description: Resizes a block of memory previously returned from 00302 // heap_alloc_array. 00303 //////////////////////////////////////////////////////////////////// 00304 void *MemoryUsage:: 00305 heap_realloc_array(void *ptr, size_t size) { 00306 if (_recursion_protect) { 00307 ptr = MemoryHook::heap_realloc_array(ptr, size); 00308 if (express_cat.is_spam()) { 00309 express_cat.spam() 00310 << "Reallocating array pointer " << (void *)ptr 00311 << " during recursion protect.\n"; 00312 } 00313 00314 } else { 00315 if (_track_memory_usage) { 00316 get_global_ptr()->ns_remove_void_pointer(ptr); 00317 ptr = MemoryHook::heap_realloc_array(ptr, size); 00318 /* 00319 if (express_cat.is_spam()) { 00320 express_cat.spam() 00321 << "Reallocating array pointer " << (void *)ptr 00322 << " to size " << size << ".\n"; 00323 } 00324 */ 00325 00326 get_global_ptr()->ns_record_void_pointer(ptr, size); 00327 00328 } else { 00329 ptr = MemoryHook::heap_realloc_array(ptr, size); 00330 } 00331 } 00332 00333 return ptr; 00334 } 00335 00336 //////////////////////////////////////////////////////////////////// 00337 // Function: MemoryUsage::heap_free_array 00338 // Access: Public, Virtual 00339 // Description: Releases a block of memory previously allocated via 00340 // heap_alloc_array. 00341 //////////////////////////////////////////////////////////////////// 00342 void MemoryUsage:: 00343 heap_free_array(void *ptr) { 00344 if (_recursion_protect) { 00345 if (express_cat.is_spam()) { 00346 express_cat.spam() 00347 << "Deleting pointer " << (void *)ptr 00348 << " during recursion protect.\n"; 00349 } 00350 MemoryHook::heap_free_array(ptr); 00351 00352 } else { 00353 if (_track_memory_usage) { 00354 /* 00355 if (express_cat.is_spam()) { 00356 express_cat.spam() 00357 << "Removing pointer " << (void *)ptr << "\n"; 00358 } 00359 */ 00360 ns_remove_void_pointer(ptr); 00361 MemoryHook::heap_free_array(ptr); 00362 } else { 00363 MemoryHook::heap_free_array(ptr); 00364 } 00365 } 00366 } 00367 00368 //////////////////////////////////////////////////////////////////// 00369 // Function: MemoryUsage::mark_pointer 00370 // Access: Public, Virtual 00371 // Description: This special method exists only to provide a callback 00372 // hook into MemoryUsage. It indicates that the 00373 // indicated pointer, allocated from somewhere other 00374 // than a call to heap_alloc(), now contains a pointer 00375 // to the indicated ReferenceCount object. If orig_size 00376 // is 0, it indicates that the ReferenceCount object has 00377 // been destroyed. 00378 //////////////////////////////////////////////////////////////////// 00379 void MemoryUsage:: 00380 mark_pointer(void *ptr, size_t size, ReferenceCount *ref_ptr) { 00381 if (_recursion_protect || !_track_memory_usage) { 00382 return; 00383 } 00384 00385 if (express_cat.is_spam()) { 00386 express_cat.spam() 00387 << "Marking pointer " << ptr << ", size " << size 00388 << ", ref_ptr = " << ref_ptr << "\n"; 00389 } 00390 00391 if (size != 0) { 00392 // We're recording this pointer as now in use. 00393 ns_record_void_pointer(ptr, size); 00394 00395 if (ref_ptr != (ReferenceCount *)NULL) { 00396 // Make the pointer typed. This is particularly necessary in 00397 // case the ref_ptr is a different value than the base void 00398 // pointer; this may be our only opportunity to associate the 00399 // two pointers. 00400 Table::iterator ti; 00401 ti = _table.find(ptr); 00402 nassertv(ti != _table.end()); 00403 MemoryInfo *info = (*ti).second; 00404 00405 info->_ref_ptr = ref_ptr; 00406 info->_static_type = ReferenceCount::get_class_type(); 00407 info->_dynamic_type = ReferenceCount::get_class_type(); 00408 info->_flags |= MemoryInfo::F_reconsider_dynamic_type; 00409 00410 if (ref_ptr != ptr) { 00411 _recursion_protect = true; 00412 00413 pair<Table::iterator, bool> insert_result = 00414 _table.insert(Table::value_type((void *)ref_ptr, info)); 00415 assert(insert_result.first != _table.end()); 00416 if (!insert_result.second) { 00417 express_cat.warning() 00418 << "Attempt to mark pointer " << ptr << " as ReferenceCount " 00419 << ref_ptr << ", which was already allocated.\n"; 00420 } 00421 00422 _recursion_protect = false; 00423 } 00424 } 00425 00426 } else { 00427 // We're removing this pointer from use. 00428 ns_remove_void_pointer(ptr); 00429 } 00430 } 00431 00432 #if (defined(WIN32_VC) || defined (WIN64_VC))&& defined(_DEBUG) 00433 //////////////////////////////////////////////////////////////////// 00434 // Function: MemoryUsage::win32_malloc_hook 00435 // Access: Public, Static 00436 // Description: This callback is attached to the Win32 debug malloc 00437 // system to be called whenever a pointer is allocated, 00438 // reallocated, or freed. It's used to track the total 00439 // memory allocated via calls to malloc(). 00440 //////////////////////////////////////////////////////////////////// 00441 int MemoryUsage:: 00442 win32_malloc_hook(int alloc_type, void *ptr, 00443 size_t size, int block_use, long request, 00444 const unsigned char *filename, int line) { 00445 MemoryUsage *mu = get_global_ptr(); 00446 int increment = 0; 00447 switch (alloc_type) { 00448 case _HOOK_ALLOC: 00449 increment = size; 00450 break; 00451 00452 case _HOOK_REALLOC: 00453 increment = size - _msize(ptr); 00454 break; 00455 00456 case _HOOK_FREE: 00457 increment = - ((int)_msize(ptr)); 00458 break; 00459 } 00460 00461 mu->_total_size += increment; 00462 return true; 00463 } 00464 #endif // WIN32_VC && _DEBUG 00465 00466 00467 00468 //////////////////////////////////////////////////////////////////// 00469 // Function: MemoryUsage::Constructor 00470 // Access: Private 00471 // Description: 00472 //////////////////////////////////////////////////////////////////// 00473 MemoryUsage:: 00474 MemoryUsage(const MemoryHook ©) : MemoryHook(copy) { 00475 // We must get these variables here instead of in 00476 // config_express.cxx, because we need to know it at static init 00477 // time, and who knows when the code in config_express will be 00478 // executed. 00479 00480 _track_memory_usage = ConfigVariableBool 00481 ("track-memory-usage", false, 00482 PRC_DESC("Set this to true to enable full-force tracking of C++ allocations " 00483 "and recordkeeping by type. It's quite expensive.")); 00484 00485 // Since enabling this after startup might cause bogus errors, we'd 00486 // like to know if this happened, so we can squelch those error 00487 // messages. 00488 _startup_track_memory_usage = _track_memory_usage; 00489 00490 // Make sure the express category has been instantiated. 00491 express_cat->is_info(); 00492 00493 _report_memory_usage = ConfigVariableBool 00494 ("report-memory-usage", false, 00495 PRC_DESC("Set this true to enable automatic reporting of allocated objects " 00496 "at the interval specified by report-memory-interval. This also " 00497 "requires track-memory-usage.")); 00498 _report_memory_interval = ConfigVariableDouble 00499 ("report-memory-interval", 5.0, 00500 PRC_DESC("This is the interval, in seconds, for reports of currently allocated " 00501 "memory, when report-memory-usage is true.")); 00502 _last_report_time = 0.0; 00503 00504 _count_memory_usage = false; 00505 00506 PN_int64 max_heap_size = ConfigVariableInt64 00507 ("max-heap-size", 0, 00508 PRC_DESC("If this is nonzero, it is the maximum number of bytes expected " 00509 "to be allocated on the heap before we enter report-memory-usage " 00510 "mode automatically. The assumption is that once this limit " 00511 "has been crossed, we must be leaking.")); 00512 if (max_heap_size != 0) { 00513 _max_heap_size = (size_t)max_heap_size; 00514 } 00515 00516 #ifdef USE_MEMORY_NOWRAPPERS 00517 #error Cannot compile MemoryUsage without malloc wrappers! 00518 #endif 00519 00520 #if (defined(WIN32_VC) || defined(WIN64_VC)) && defined(_DEBUG) 00521 // On a debug Windows build, we can set this malloc hook which 00522 // allows tracking every malloc call, even from subordinate 00523 // libraries. 00524 _CrtSetAllocHook(&win32_malloc_hook); 00525 _count_memory_usage = true; 00526 #endif 00527 00528 _info_set_dirty = false; 00529 _freeze_index = 0; 00530 _count = 0; 00531 _current_cpp_size = 0; 00532 _total_cpp_size = 0; 00533 _total_size = 0; 00534 } 00535 00536 //////////////////////////////////////////////////////////////////// 00537 // Function: MemoryUsage::overflow_heap_size 00538 // Access: Protected, Virtual 00539 // Description: This callback method is called whenever the total 00540 // allocated heap size exceeds _max_heap_size. It's 00541 // mainly intended for reporting memory leaks, on the 00542 // assumption that once we cross some specified 00543 // threshold, we're just leaking memory. 00544 //////////////////////////////////////////////////////////////////// 00545 void MemoryUsage:: 00546 overflow_heap_size() { 00547 MemoryHook::overflow_heap_size(); 00548 00549 express_cat.error() 00550 << "Total allocated memory has reached " 00551 << get_panda_heap_single_size() + get_panda_heap_array_size() 00552 << " bytes." 00553 << "\n heap single: " << get_panda_heap_single_size() 00554 << "\n heap array: " << get_panda_heap_array_size() 00555 << "\n heap overhead: " << get_panda_heap_overhead() 00556 << "\n mmap: " << get_panda_mmap_size() 00557 << "\n external: " << get_external_size() 00558 << "\n total: " << get_total_size() 00559 << "\n"; 00560 00561 // Turn on spamful debugging. 00562 _track_memory_usage = true; 00563 _report_memory_usage = true; 00564 } 00565 00566 //////////////////////////////////////////////////////////////////// 00567 // Function: MemoryUsage::get_global_ptr 00568 // Access: Private, Static 00569 // Description: Returns the pointer to the only MemoryUsage object in 00570 // the world. 00571 //////////////////////////////////////////////////////////////////// 00572 MemoryUsage *MemoryUsage:: 00573 get_global_ptr() { 00574 if (_global_ptr == (MemoryUsage *)NULL) { 00575 init_memory_hook(); 00576 _global_ptr = new MemoryUsage(*memory_hook); 00577 memory_hook = _global_ptr; 00578 } 00579 00580 return _global_ptr; 00581 } 00582 00583 00584 //////////////////////////////////////////////////////////////////// 00585 // Function: MemoryUsage::ns_record_pointer 00586 // Access: Private 00587 // Description: Indicates that the given pointer has been recently 00588 // allocated. 00589 //////////////////////////////////////////////////////////////////// 00590 void MemoryUsage:: 00591 ns_record_pointer(ReferenceCount *ptr) { 00592 if (_track_memory_usage) { 00593 // We have to protect modifications to the table from recursive 00594 // calls by toggling _recursion_protect while we adjust it. 00595 _recursion_protect = true; 00596 pair<Table::iterator, bool> insert_result = 00597 _table.insert(Table::value_type((void *)ptr, (MemoryInfo *)NULL)); 00598 00599 // This shouldn't fail. 00600 assert(insert_result.first != _table.end()); 00601 00602 if (insert_result.second) { 00603 (*insert_result.first).second = new MemoryInfo; 00604 _info_set_dirty = true; 00605 ++_count; 00606 } 00607 00608 MemoryInfo *info = (*insert_result.first).second; 00609 00610 // We might already have a ReferenceCount pointer, thanks to a 00611 // previous call to mark_pointer(). 00612 nassertv(info->_ref_ptr == NULL || info->_ref_ptr == ptr); 00613 00614 info->_ref_ptr = ptr; 00615 info->_static_type = ReferenceCount::get_class_type(); 00616 info->_dynamic_type = ReferenceCount::get_class_type(); 00617 info->_time = TrueClock::get_global_ptr()->get_long_time(); 00618 info->_freeze_index = _freeze_index; 00619 info->_flags |= MemoryInfo::F_reconsider_dynamic_type; 00620 00621 // We close the recursion_protect flag all the way down here, so 00622 // that we also protect ourselves against a possible recursive 00623 // call in TrueClock::get_global_ptr(). 00624 _recursion_protect = false; 00625 00626 if (_report_memory_usage) { 00627 double now = TrueClock::get_global_ptr()->get_long_time(); 00628 if (now - _last_report_time > _report_memory_interval) { 00629 _last_report_time = now; 00630 express_cat.info() 00631 << "*** Current memory usage: " << get_total_size() << "\n"; 00632 show_current_types(); 00633 } 00634 } 00635 } 00636 } 00637 00638 //////////////////////////////////////////////////////////////////// 00639 // Function: MemoryUsage::ns_update_type 00640 // Access: Private 00641 // Description: Associates the indicated type with the given pointer. 00642 // This should be called by functions (e.g. the 00643 // constructor) that know more specifically what type of 00644 // thing we've got; otherwise, the MemoryUsage database 00645 // will know only that it's a "ReferenceCount". 00646 //////////////////////////////////////////////////////////////////// 00647 void MemoryUsage:: 00648 ns_update_type(ReferenceCount *ptr, TypeHandle type) { 00649 if (_track_memory_usage) { 00650 Table::iterator ti; 00651 ti = _table.find(ptr); 00652 if (ti == _table.end()) { 00653 if (_startup_track_memory_usage) { 00654 express_cat.error() 00655 << "Attempt to update type to " << type << " for unrecorded pointer " 00656 << (void *)ptr << "!\n"; 00657 nassertv(false); 00658 } 00659 return; 00660 } 00661 00662 MemoryInfo *info = (*ti).second; 00663 00664 info->update_type_handle(info->_static_type, type); 00665 info->determine_dynamic_type(); 00666 00667 consolidate_void_ptr(info); 00668 } 00669 } 00670 00671 //////////////////////////////////////////////////////////////////// 00672 // Function: MemoryUsage::ns_update_type 00673 // Access: Private 00674 // Description: Associates the indicated type with the given pointer. 00675 // This flavor of update_type() also passes in the 00676 // pointer as a TypedObject, and useful for objects that 00677 // are, in fact, TypedObjects. Once the MemoryUsage 00678 // database has the pointer as a TypedObject it doesn't 00679 // need any more help. 00680 //////////////////////////////////////////////////////////////////// 00681 void MemoryUsage:: 00682 ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) { 00683 if (_track_memory_usage) { 00684 Table::iterator ti; 00685 ti = _table.find(ptr); 00686 if (ti == _table.end()) { 00687 if (_startup_track_memory_usage) { 00688 express_cat.error() 00689 << "Attempt to update type to " << typed_ptr->get_type() 00690 << " for unrecorded pointer " 00691 << (void *)ptr << "!\n"; 00692 } 00693 return; 00694 } 00695 00696 MemoryInfo *info = (*ti).second; 00697 info->_typed_ptr = typed_ptr; 00698 info->determine_dynamic_type(); 00699 00700 consolidate_void_ptr(info); 00701 } 00702 } 00703 00704 //////////////////////////////////////////////////////////////////// 00705 // Function: MemoryUsage::ns_remove_pointer 00706 // Access: Private 00707 // Description: Indicates that the given pointer has been recently 00708 // freed. 00709 //////////////////////////////////////////////////////////////////// 00710 void MemoryUsage:: 00711 ns_remove_pointer(ReferenceCount *ptr) { 00712 if (_track_memory_usage) { 00713 Table::iterator ti; 00714 ti = _table.find(ptr); 00715 if (ti == _table.end()) { 00716 if (_startup_track_memory_usage) { 00717 express_cat.error() 00718 << "Attempt to remove pointer " << (void *)ptr 00719 << ", not in table.\n" 00720 << "Possibly a double-destruction.\n"; 00721 nassertv(false); 00722 } 00723 return; 00724 } 00725 00726 MemoryInfo *info = (*ti).second; 00727 00728 if (info->_ref_ptr == NULL) { 00729 express_cat.error() 00730 << "Pointer " << (void *)ptr << " deleted twice!\n"; 00731 return; 00732 } 00733 nassertv(info->_ref_ptr == ptr); 00734 00735 if (express_cat.is_spam()) { 00736 express_cat.spam() 00737 << "Removing ReferenceCount pointer " << (void *)ptr << "\n"; 00738 } 00739 00740 info->_ref_ptr = (ReferenceCount *)NULL; 00741 info->_typed_ptr = (TypedObject *)NULL; 00742 00743 if (info->_freeze_index == _freeze_index) { 00744 double now = TrueClock::get_global_ptr()->get_long_time(); 00745 00746 // We have to protect modifications to the table from recursive 00747 // calls by toggling _recursion_protect while we adjust it. 00748 _recursion_protect = true; 00749 _trend_types.add_info(info->get_type(), info); 00750 _trend_ages.add_info(now - info->_time, info); 00751 _recursion_protect = false; 00752 } 00753 00754 if (ptr != info->_void_ptr || info->_void_ptr == NULL) { 00755 // Remove the entry from the table. 00756 00757 // We have to protect modifications to the table from recursive 00758 // calls by toggling _recursion_protect while we adjust it. 00759 _recursion_protect = true; 00760 _table.erase(ti); 00761 _recursion_protect = false; 00762 00763 if (info->_void_ptr == NULL) { 00764 // That was the last entry. Remove it altogether. 00765 _total_cpp_size -= info->_size; 00766 if (info->_freeze_index == _freeze_index) { 00767 _current_cpp_size -= info->_size; 00768 _count--; 00769 } 00770 00771 _info_set_dirty = true; 00772 delete info; 00773 } 00774 } 00775 } 00776 } 00777 00778 //////////////////////////////////////////////////////////////////// 00779 // Function: MemoryUsage::ns_record_void_pointer 00780 // Access: Private 00781 // Description: Records a pointer that's not even necessarily a 00782 // ReferenceCount object (but for which we know the size 00783 // of the allocated structure). 00784 //////////////////////////////////////////////////////////////////// 00785 void MemoryUsage:: 00786 ns_record_void_pointer(void *ptr, size_t size) { 00787 if (_track_memory_usage) { 00788 if (express_cat.is_spam()) { 00789 express_cat.spam() 00790 << "Recording void pointer " << (void *)ptr << "\n"; 00791 } 00792 00793 // We have to protect modifications to the table from recursive 00794 // calls by toggling _recursion_protect while we adjust it. 00795 00796 _recursion_protect = true; 00797 pair<Table::iterator, bool> insert_result = 00798 _table.insert(Table::value_type((void *)ptr, (MemoryInfo *)NULL)); 00799 00800 assert(insert_result.first != _table.end()); 00801 00802 if (insert_result.second) { 00803 (*insert_result.first).second = new MemoryInfo; 00804 _info_set_dirty = true; 00805 ++_count; 00806 } 00807 00808 MemoryInfo *info = (*insert_result.first).second; 00809 00810 // We shouldn't already have a void pointer. 00811 if (info->_void_ptr != (void *)NULL) { 00812 express_cat.error() 00813 << "Void pointer " << (void *)ptr << " recorded twice!\n"; 00814 nassertv(false); 00815 } 00816 00817 if (info->_freeze_index == _freeze_index) { 00818 _current_cpp_size += size - info->_size; 00819 } else { 00820 _current_cpp_size += size; 00821 } 00822 _total_cpp_size += size - info->_size; 00823 00824 info->_void_ptr = ptr; 00825 info->_size = size; 00826 info->_time = TrueClock::get_global_ptr()->get_long_time(); 00827 info->_freeze_index = _freeze_index; 00828 info->_flags |= MemoryInfo::F_size_known; 00829 00830 // We close the recursion_protect flag all the way down here, so 00831 // that we also protect ourselves against a possible recursive 00832 // call in TrueClock::get_global_ptr(). 00833 _recursion_protect = false; 00834 } 00835 } 00836 00837 //////////////////////////////////////////////////////////////////// 00838 // Function: MemoryUsage::ns_remove_void_pointer 00839 // Access: Private 00840 // Description: Removes a pointer previously recorded via 00841 // record_void_pointer. 00842 //////////////////////////////////////////////////////////////////// 00843 void MemoryUsage:: 00844 ns_remove_void_pointer(void *ptr) { 00845 if (_track_memory_usage) { 00846 if (express_cat.is_spam()) { 00847 express_cat.spam() 00848 << "Removing void pointer " << (void *)ptr << "\n"; 00849 } 00850 00851 Table::iterator ti; 00852 ti = _table.find(ptr); 00853 if (ti == _table.end()) { 00854 // The pointer we tried to delete was not recorded in the table. 00855 00856 // We can't report this as an error, because (a) we might have 00857 // removed the void pointer entry already when we consolidated, 00858 // and (b) a few objects might have been created during static 00859 // init time, before we grabbed the operator new/delete function 00860 // handlers. 00861 return; 00862 } 00863 00864 MemoryInfo *info = (*ti).second; 00865 00866 if (info->_void_ptr == (void *)NULL) { 00867 express_cat.error() 00868 << "Pointer " << (void *)ptr << " deleted twice!\n"; 00869 return; 00870 } 00871 nassertv(info->_void_ptr == ptr); 00872 00873 if (info->_ref_ptr != (ReferenceCount *)NULL) { 00874 express_cat.error() 00875 << "Pointer " << (void *)ptr 00876 << " did not destruct before being deleted!\n"; 00877 if (info->_ref_ptr != ptr) { 00878 remove_pointer(info->_ref_ptr); 00879 } 00880 } 00881 00882 info->_void_ptr = NULL; 00883 00884 // Remove it from the table. 00885 00886 // We have to protect modifications to the table from recursive 00887 // calls by toggling _recursion_protect while we adjust it. 00888 _recursion_protect = true; 00889 _table.erase(ti); 00890 _recursion_protect = false; 00891 00892 _total_cpp_size -= info->_size; 00893 if (info->_freeze_index == _freeze_index) { 00894 --_count; 00895 _current_cpp_size -= info->_size; 00896 } 00897 00898 _info_set_dirty = true; 00899 delete info; 00900 } 00901 } 00902 00903 //////////////////////////////////////////////////////////////////// 00904 // Function: MemoryUsage::ns_get_num_pointers 00905 // Access: Private 00906 // Description: Returns the number of pointers currently active. 00907 //////////////////////////////////////////////////////////////////// 00908 int MemoryUsage:: 00909 ns_get_num_pointers() { 00910 nassertr(_track_memory_usage, 0); 00911 return _count; 00912 } 00913 00914 //////////////////////////////////////////////////////////////////// 00915 // Function: MemoryUsage::ns_get_pointers 00916 // Access: Private 00917 // Description: Fills the indicated MemoryUsagePointers with the set 00918 // of all pointers currently active. 00919 //////////////////////////////////////////////////////////////////// 00920 void MemoryUsage:: 00921 ns_get_pointers(MemoryUsagePointers &result) { 00922 nassertv(_track_memory_usage); 00923 result.clear(); 00924 00925 if (_info_set_dirty) { 00926 refresh_info_set(); 00927 } 00928 00929 double now = TrueClock::get_global_ptr()->get_long_time(); 00930 InfoSet::iterator si; 00931 for (si = _info_set.begin(); si != _info_set.end(); ++si) { 00932 MemoryInfo *info = (*si); 00933 if (info->_freeze_index == _freeze_index && 00934 info->_ref_ptr != (ReferenceCount *)NULL) { 00935 result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(), 00936 now - info->_time); 00937 } 00938 } 00939 } 00940 00941 //////////////////////////////////////////////////////////////////// 00942 // Function: MemoryUsage::ns_get_pointers_of_type 00943 // Access: Private 00944 // Description: Fills the indicated MemoryUsagePointers with the set 00945 // of all pointers of the indicated type currently 00946 // active. 00947 //////////////////////////////////////////////////////////////////// 00948 void MemoryUsage:: 00949 ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) { 00950 nassertv(_track_memory_usage); 00951 result.clear(); 00952 00953 if (_info_set_dirty) { 00954 refresh_info_set(); 00955 } 00956 00957 double now = TrueClock::get_global_ptr()->get_long_time(); 00958 InfoSet::iterator si; 00959 for (si = _info_set.begin(); si != _info_set.end(); ++si) { 00960 MemoryInfo *info = (*si); 00961 if (info->_freeze_index == _freeze_index && 00962 info->_ref_ptr != (ReferenceCount *)NULL) { 00963 TypeHandle info_type = info->get_type(); 00964 if (info_type != TypeHandle::none() && 00965 info_type.is_derived_from(type)) { 00966 result.add_entry(info->_ref_ptr, info->_typed_ptr, info_type, 00967 now - info->_time); 00968 } 00969 } 00970 } 00971 } 00972 00973 //////////////////////////////////////////////////////////////////// 00974 // Function: MemoryUsage::ns_get_pointers_of_age 00975 // Access: Private 00976 // Description: Fills the indicated MemoryUsagePointers with the set 00977 // of all pointers that were allocated within the range 00978 // of the indicated number of seconds ago. 00979 //////////////////////////////////////////////////////////////////// 00980 void MemoryUsage:: 00981 ns_get_pointers_of_age(MemoryUsagePointers &result, 00982 double from, double to) { 00983 nassertv(_track_memory_usage); 00984 result.clear(); 00985 00986 if (_info_set_dirty) { 00987 refresh_info_set(); 00988 } 00989 00990 double now = TrueClock::get_global_ptr()->get_long_time(); 00991 InfoSet::iterator si; 00992 for (si = _info_set.begin(); si != _info_set.end(); ++si) { 00993 MemoryInfo *info = (*si); 00994 if (info->_freeze_index == _freeze_index && 00995 info->_ref_ptr != (ReferenceCount *)NULL) { 00996 double age = now - info->_time; 00997 if ((age >= from && age <= to) || 00998 (age >= to && age <= from)) { 00999 result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(), age); 01000 } 01001 } 01002 } 01003 } 01004 01005 //////////////////////////////////////////////////////////////////// 01006 // Function: MemoryUsage::ns_get_pointers_with_zero_count 01007 // Access: Private 01008 // Description: Fills the indicated MemoryUsagePointers with the set 01009 // of all currently active pointers (that is, pointers 01010 // allocated since the last call to freeze(), and not 01011 // yet freed) that have a zero reference count. 01012 // 01013 // Generally, an undeleted pointer with a zero reference 01014 // count means its reference count has never been 01015 // incremented beyond zero (since once it has been 01016 // incremented, the only way it can return to zero would 01017 // free the pointer). This may include objects that are 01018 // allocated statically or on the stack, which are never 01019 // intended to be deleted. Or, it might represent a 01020 // programmer or compiler error. 01021 // 01022 // This function has the side-effect of incrementing 01023 // each of their reference counts by one, thus 01024 // preventing them from ever being freed--but since they 01025 // hadn't been freed anyway, probably no additional harm 01026 // is done. 01027 //////////////////////////////////////////////////////////////////// 01028 void MemoryUsage:: 01029 ns_get_pointers_with_zero_count(MemoryUsagePointers &result) { 01030 nassertv(_track_memory_usage); 01031 result.clear(); 01032 01033 if (_info_set_dirty) { 01034 refresh_info_set(); 01035 } 01036 01037 double now = TrueClock::get_global_ptr()->get_long_time(); 01038 InfoSet::iterator si; 01039 for (si = _info_set.begin(); si != _info_set.end(); ++si) { 01040 MemoryInfo *info = (*si); 01041 if (info->_freeze_index == _freeze_index && 01042 info->_ref_ptr != (ReferenceCount *)NULL) { 01043 if (info->_ref_ptr->get_ref_count() == 0) { 01044 info->_ref_ptr->ref(); 01045 result.add_entry(info->_ref_ptr, info->_typed_ptr, info->get_type(), 01046 now - info->_time); 01047 } 01048 } 01049 } 01050 } 01051 01052 //////////////////////////////////////////////////////////////////// 01053 // Function: MemoryUsage::ns_freeze 01054 // Access: Private 01055 // Description: 'Freezes' all pointers currently stored so that they 01056 // are no longer reported; only newly allocate pointers 01057 // from this point on will appear in future information 01058 // requests. This makes it easier to differentiate 01059 // between continuous leaks and one-time memory 01060 // allocations. 01061 //////////////////////////////////////////////////////////////////// 01062 void MemoryUsage:: 01063 ns_freeze() { 01064 _count = 0; 01065 _current_cpp_size = 0; 01066 _trend_types.clear(); 01067 _trend_ages.clear(); 01068 _freeze_index++; 01069 } 01070 01071 //////////////////////////////////////////////////////////////////// 01072 // Function: MemoryUsage::ns_show_current_types 01073 // Access: Private 01074 // Description: Shows the breakdown of types of all of the 01075 // active pointers. 01076 //////////////////////////////////////////////////////////////////// 01077 void MemoryUsage:: 01078 ns_show_current_types() { 01079 nassertv(_track_memory_usage); 01080 TypeHistogram hist; 01081 01082 if (_info_set_dirty) { 01083 refresh_info_set(); 01084 } 01085 01086 _recursion_protect = true; 01087 InfoSet::iterator si; 01088 for (si = _info_set.begin(); si != _info_set.end(); ++si) { 01089 MemoryInfo *info = (*si); 01090 if (info->_freeze_index == _freeze_index) { 01091 hist.add_info(info->get_type(), info); 01092 } 01093 } 01094 hist.show(); 01095 _recursion_protect = false; 01096 } 01097 01098 //////////////////////////////////////////////////////////////////// 01099 // Function: MemoryUsage::ns_show_trend_types 01100 // Access: Private 01101 // Description: Shows the breakdown of types of all of the 01102 // pointers allocated and freed since the last call to 01103 // freeze(). 01104 //////////////////////////////////////////////////////////////////// 01105 void MemoryUsage:: 01106 ns_show_trend_types() { 01107 _trend_types.show(); 01108 } 01109 01110 //////////////////////////////////////////////////////////////////// 01111 // Function: MemoryUsage::ns_show_current_ages 01112 // Access: Private 01113 // Description: Shows the breakdown of ages of all of the 01114 // active pointers. 01115 //////////////////////////////////////////////////////////////////// 01116 void MemoryUsage:: 01117 ns_show_current_ages() { 01118 nassertv(_track_memory_usage); 01119 01120 AgeHistogram hist; 01121 double now = TrueClock::get_global_ptr()->get_long_time(); 01122 01123 _recursion_protect = true; 01124 InfoSet::iterator si; 01125 for (si = _info_set.begin(); si != _info_set.end(); ++si) { 01126 MemoryInfo *info = (*si); 01127 if (info->_freeze_index == _freeze_index) { 01128 hist.add_info(now - info->_time, info); 01129 } 01130 } 01131 01132 hist.show(); 01133 _recursion_protect = false; 01134 } 01135 01136 //////////////////////////////////////////////////////////////////// 01137 // Function: MemoryUsage::ns_show_trend_ages 01138 // Access: Private 01139 // Description: Shows the breakdown of ages of all of the 01140 // pointers allocated and freed since the last call to 01141 // freeze(). 01142 //////////////////////////////////////////////////////////////////// 01143 void MemoryUsage:: 01144 ns_show_trend_ages() { 01145 _trend_ages.show(); 01146 } 01147 01148 //////////////////////////////////////////////////////////////////// 01149 // Function: MemoryUsage::consolidate_void_ptr 01150 // Access: Private 01151 // Description: If the size information has not yet been determined 01152 // for this pointer, checks to see if it has possibly 01153 // been recorded under the TypedObject pointer (this 01154 // will happen when the class inherits from TypedObject 01155 // before ReferenceCount, e.g. TypedReferenceCount). 01156 //////////////////////////////////////////////////////////////////// 01157 void MemoryUsage:: 01158 consolidate_void_ptr(MemoryInfo *info) { 01159 if (info->is_size_known()) { 01160 // We already know the size, so no sweat. 01161 return; 01162 } 01163 01164 if (info->_typed_ptr == (TypedObject *)NULL) { 01165 // We don't have a typed pointer for this thing yet. 01166 return; 01167 } 01168 01169 TypedObject *typed_ptr = info->_typed_ptr; 01170 01171 if ((void *)typed_ptr == (void *)info->_ref_ptr) { 01172 // The TypedObject pointer is the same pointer as the 01173 // ReferenceCount pointer, so there's no point in looking it up 01174 // separately. Actually, this really shouldn't even be possible. 01175 return; 01176 } 01177 01178 nassertv(info->_void_ptr == NULL); 01179 01180 Table::iterator ti; 01181 ti = _table.find(typed_ptr); 01182 if (ti == _table.end()) { 01183 // No entry for the typed pointer, either. 01184 return; 01185 } 01186 01187 // We do have an entry! Copy over the relevant pieces. 01188 MemoryInfo *typed_info = (*ti).second; 01189 01190 nassertv(typed_info->_void_ptr == typed_ptr && 01191 typed_info->_ref_ptr == NULL); 01192 01193 info->_void_ptr = typed_info->_void_ptr; 01194 if (typed_info->is_size_known()) { 01195 info->_size = typed_info->get_size(); 01196 info->_flags |= MemoryInfo::F_size_known; 01197 if (typed_info->_freeze_index == _freeze_index) { 01198 _current_cpp_size += info->_size; 01199 } 01200 } 01201 01202 // Now that we've consolidated the pointers, remove the entry for 01203 // the typed pointer. 01204 if (info->_freeze_index == _freeze_index) { 01205 _count--; 01206 _current_cpp_size -= info->_size; 01207 } 01208 01209 _info_set_dirty = true; 01210 delete typed_info; 01211 01212 (*ti).second = info; 01213 } 01214 01215 //////////////////////////////////////////////////////////////////// 01216 // Function: MemoryUsage::refresh_info_set 01217 // Access: Private 01218 // Description: Recomputes the _info_set table, if necessary. This 01219 // table stores a unique entry for each MemoryInfo 01220 // object in _table. 01221 //////////////////////////////////////////////////////////////////// 01222 void MemoryUsage:: 01223 refresh_info_set() { 01224 if (!_info_set_dirty) { 01225 return; 01226 } 01227 01228 // We have to protect modifications to the table from recursive 01229 // calls by toggling _recursion_protect while we adjust it. 01230 _recursion_protect = true; 01231 01232 _info_set.clear(); 01233 Table::iterator ti; 01234 for (ti = _table.begin(); ti != _table.end(); ++ti) { 01235 _info_set.insert((*ti).second); 01236 } 01237 01238 _recursion_protect = false; 01239 01240 _info_set_dirty = false; 01241 } 01242 01243 01244 #endif // DO_MEMORY_USAGE