Panda3D

memoryUsage.cxx

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 &copy) : 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
 All Classes Functions Variables Enumerations