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