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