Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/express/memoryUsage.cxx

Go to the documentation of this file.
00001 // Filename: memoryUsage.cxx
00002 // Created by:  drose (25May00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #ifdef DO_MEMORY_USAGE
00020 
00021 #include "memoryUsagePointers.h"
00022 #include "trueClock.h"
00023 #include "typedReferenceCount.h"
00024 
00025 #include "memoryUsage.h"
00026 #include "interrogate_request.h"
00027 
00028 #if defined(WIN32_VC) && defined(_DEBUG)
00029 #include <crtdbg.h>
00030 #endif
00031 
00032 #include "config_express.h"
00033 #include <algorithm>
00034 
00035 // This flag is set true in is_counting() mode to indicate that the
00036 // malloc operation is coming from C++ operator new or delete.
00037 static bool _is_cpp_operator = false;
00038 
00039 // This flag is used to protect the operator new/delete handlers
00040 // against recursive entry.
00041 static bool _recursion_protect = false;
00042 
00043 MemoryUsage *MemoryUsage::_global_ptr = (MemoryUsage *)NULL;
00044 
00045 // The cutoff ages, in seconds, for the various buckets in the AgeHistogram.
00046 double MemoryUsage::AgeHistogram::_cutoff[MemoryUsage::AgeHistogram::num_buckets] = {
00047   0.0,
00048   0.1,
00049   1.0,
00050   10.0,
00051   60.0,
00052 };
00053 
00054 
00055 ////////////////////////////////////////////////////////////////////
00056 //     Function: MemoryUsage::TypeHistogram::add_info
00057 //       Access: Public
00058 //  Description: Adds a single entry to the histogram.
00059 ////////////////////////////////////////////////////////////////////
00060 void MemoryUsage::TypeHistogram::
00061 add_info(TypeHandle type, MemoryInfo &info) {
00062   _counts[type].add_info(info);
00063 }
00064 
00065 
00066 // This class is a temporary class used only in TypeHistogram::show(),
00067 // below, to sort the types in descending order by counts.
00068 class TypeHistogramCountSorter {
00069 public:
00070   TypeHistogramCountSorter(const MemoryUsagePointerCounts &count, 
00071                            TypeHandle type) :
00072     _count(count),
00073     _type(type)
00074   {
00075   }
00076   bool operator < (const TypeHistogramCountSorter &other) const {
00077     return other._count < _count;
00078   }
00079   MemoryUsagePointerCounts _count;
00080   TypeHandle _type;
00081 };
00082 
00083 ////////////////////////////////////////////////////////////////////
00084 //     Function: MemoryUsage::TypeHistogram::show
00085 //       Access: Public
00086 //  Description: Shows the contents of the histogram to nout.
00087 ////////////////////////////////////////////////////////////////////
00088 void MemoryUsage::TypeHistogram::
00089 show() const {
00090   // First, copy the relevant information to a vector so we can sort
00091   // by counts.  Don't use a pvector.
00092   typedef vector<TypeHistogramCountSorter, dallocator<TypeHistogramCountSorter> > CountSorter;
00093   CountSorter count_sorter;
00094   Counts::const_iterator ci;
00095   for (ci = _counts.begin(); ci != _counts.end(); ++ci) {
00096     count_sorter.push_back
00097       (TypeHistogramCountSorter((*ci).second, (*ci).first));
00098   }
00099 
00100   sort(count_sorter.begin(), count_sorter.end());
00101 
00102   CountSorter::const_iterator vi;
00103   for (vi = count_sorter.begin(); vi != count_sorter.end(); ++vi) {
00104     TypeHandle type = (*vi)._type;
00105     if (type == TypeHandle::none()) {
00106       nout << "unknown";
00107     } else {
00108       nout << type;
00109     }
00110     nout << " : " << (*vi)._count << "\n";
00111   }
00112 }
00113 
00114 ////////////////////////////////////////////////////////////////////
00115 //     Function: MemoryUsage::TypeHistogram::clear
00116 //       Access: Public
00117 //  Description: Resets the histogram in preparation for new data.
00118 ////////////////////////////////////////////////////////////////////
00119 void MemoryUsage::TypeHistogram::
00120 clear() {
00121   _counts.clear();
00122 }
00123 
00124 ////////////////////////////////////////////////////////////////////
00125 //     Function: MemoryUsage::AgeHistogram::Constructor
00126 //       Access: Public
00127 //  Description:
00128 ////////////////////////////////////////////////////////////////////
00129 MemoryUsage::AgeHistogram::
00130 AgeHistogram() {
00131   clear();
00132 }
00133 
00134 ////////////////////////////////////////////////////////////////////
00135 //     Function: MemoryUsage::AgeHistogram::add_info
00136 //       Access: Public
00137 //  Description: Adds a single entry to the histogram.
00138 ////////////////////////////////////////////////////////////////////
00139 void MemoryUsage::AgeHistogram::
00140 add_info(double age, MemoryInfo &info) {
00141   int bucket = choose_bucket(age);
00142   nassertv(bucket >= 0 && bucket < num_buckets);
00143   _counts[bucket].add_info(info);
00144 }
00145 
00146 ////////////////////////////////////////////////////////////////////
00147 //     Function: MemoryUsage::AgeHistogram::show
00148 //       Access: Public
00149 //  Description: Shows the contents of the histogram to nout.
00150 ////////////////////////////////////////////////////////////////////
00151 void MemoryUsage::AgeHistogram::
00152 show() const {
00153   for (int i = 0; i < num_buckets - 1; i++) {
00154     nout << _cutoff[i] << " to " << _cutoff[i + 1] << " seconds old : ";
00155     _counts[i].output(nout);
00156     nout << "\n";
00157   }
00158   nout << _cutoff[num_buckets - 1] << " seconds old and up : ";
00159   _counts[num_buckets - 1].output(nout);
00160   nout << "\n";
00161 }
00162 
00163 ////////////////////////////////////////////////////////////////////
00164 //     Function: MemoryUsage::AgeHistogram::clear
00165 //       Access: Public
00166 //  Description: Resets the histogram in preparation for new data.
00167 ////////////////////////////////////////////////////////////////////
00168 void MemoryUsage::AgeHistogram::
00169 clear() {
00170   for (int i = 0; i < num_buckets; i++) {
00171     _counts[i].clear();
00172   }
00173 }
00174 
00175 ////////////////////////////////////////////////////////////////////
00176 //     Function: MemoryUsage::AgeHistogram::choose_bucket
00177 //       Access: Private
00178 //  Description:
00179 ////////////////////////////////////////////////////////////////////
00180 int MemoryUsage::AgeHistogram::
00181 choose_bucket(double age) const {
00182   for (int i = num_buckets - 1; i >= 0; i--) {
00183     if (age >= _cutoff[i]) {
00184       return i;
00185     }
00186   }
00187   express_cat.error()
00188     << "No suitable bucket for age " << age << "\n";
00189   return 0;
00190 }
00191 
00192 #if defined(__GNUC__)
00193 
00194 ////////////////////////////////////////////////////////////////////
00195 //     Function: MemoryUsage::record_pointer
00196 //       Access: Public, Static
00197 //  Description: Indicates that the given pointer has been recently
00198 //               allocated.
00199 ////////////////////////////////////////////////////////////////////
00200 void MemoryUsage::
00201 record_pointer(ReferenceCount *ptr) {
00202   get_global_ptr()->ns_record_pointer(ptr);
00203 }
00204 
00205 ////////////////////////////////////////////////////////////////////
00206 //     Function: MemoryUsage::update_type
00207 //       Access: Public, Static
00208 //  Description: Associates the indicated type with the given pointer.
00209 //               This should be called by functions (e.g. the
00210 //               constructor) that know more specifically what type of
00211 //               thing we've got; otherwise, the MemoryUsage database
00212 //               will know only that it's a "ReferenceCount".
00213 ////////////////////////////////////////////////////////////////////
00214 void MemoryUsage::
00215 update_type(ReferenceCount *ptr, TypeHandle type) {
00216   get_global_ptr()->ns_update_type(ptr, type);
00217 }
00218 
00219 ////////////////////////////////////////////////////////////////////
00220 //     Function: MemoryUsage::update_type
00221 //       Access: Public, Static
00222 //  Description: Associates the indicated type with the given pointer.
00223 //               This flavor of update_type() also passes in the
00224 //               pointer as a TypedObject, and useful for objects that
00225 //               are, in fact, TypedObjects.  Once the MemoryUsage
00226 //               database has the pointer as a TypedObject it doesn't
00227 //               need any more help.
00228 ////////////////////////////////////////////////////////////////////
00229 void MemoryUsage::
00230 update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
00231   get_global_ptr()->ns_update_type(ptr, typed_ptr);
00232 }
00233 
00234 ////////////////////////////////////////////////////////////////////
00235 //     Function: MemoryUsage::remove_pointer
00236 //       Access: Public, Static
00237 //  Description: Indicates that the given pointer has been recently
00238 //               freed.
00239 ////////////////////////////////////////////////////////////////////
00240 void MemoryUsage::
00241 remove_pointer(ReferenceCount *ptr) {
00242   get_global_ptr()->ns_remove_pointer(ptr);
00243 }
00244 
00245 #endif // __GNUC__
00246 
00247 ////////////////////////////////////////////////////////////////////
00248 //     Function: MemoryUsage::operator_new_handler
00249 //       Access: Public, Static
00250 //  Description: This is set up as a global handler function (by
00251 //               redefining a function pointer in Dtool) for the
00252 //               operator new function.  If track-memory-usage is
00253 //               enabled, this function will be called whenever any
00254 //               new operator within the Panda source is invoked.
00255 ////////////////////////////////////////////////////////////////////
00256 void *MemoryUsage::
00257 operator_new_handler(size_t size) {
00258   void *ptr;
00259 
00260   if (_recursion_protect) {
00261     ptr = default_operator_new(size);
00262 
00263   } else {
00264     MemoryUsage *mu = get_global_ptr();
00265     if (mu->_track_memory_usage) {
00266       ptr = default_operator_new(size);
00267       get_global_ptr()->ns_record_void_pointer(ptr, size);
00268       
00269     } else {
00270       _is_cpp_operator = true;
00271       ptr = default_operator_new(size);
00272       _is_cpp_operator = false;
00273     }
00274   }
00275 
00276   return ptr;
00277 }
00278 
00279 ////////////////////////////////////////////////////////////////////
00280 //     Function: MemoryUsage::operator_delete_handler
00281 //       Access: Public, Static
00282 //  Description: This is set up as a global handler function (by
00283 //               redefining a function pointer in Dtool) for the
00284 //               operator delete function.  If track-memory-usage is
00285 //               enabled, this function will be called whenever any
00286 //               delete operator within the Panda source is invoked.
00287 ////////////////////////////////////////////////////////////////////
00288 void MemoryUsage::
00289 operator_delete_handler(void *ptr) {
00290   if (_recursion_protect) {
00291     default_operator_delete(ptr);
00292 
00293   } else {
00294     MemoryUsage *mu = get_global_ptr();
00295     if (mu->_track_memory_usage) {
00296       mu->ns_remove_void_pointer(ptr);
00297       default_operator_delete(ptr);
00298     } else {
00299       _is_cpp_operator = true;
00300       default_operator_delete(ptr);
00301       _is_cpp_operator = false;
00302     }
00303   }
00304 }
00305 
00306 #if defined(WIN32_VC) && defined(_DEBUG)
00307 ////////////////////////////////////////////////////////////////////
00308 //     Function: MemoryUsage::win32_malloc_hook
00309 //       Access: Public, Static
00310 //  Description: This callback is attached to the Win32 debug malloc
00311 //               system to be called whenever a pointer is allocated,
00312 //               reallocated, or freed.  It's used to track the total
00313 //               memory allocated via calls to malloc().
00314 ////////////////////////////////////////////////////////////////////
00315 int MemoryUsage::
00316 win32_malloc_hook(int alloc_type, void *ptr, 
00317                   size_t size, int block_use, long request, 
00318                   const unsigned char *filename, int line) {
00319   if (!_recursion_protect) {
00320     MemoryUsage *mu = get_global_ptr();
00321     if (mu->_count_memory_usage) {
00322       int increment = 0;
00323       switch (alloc_type) {
00324       case _HOOK_ALLOC:
00325         increment = size;
00326         break;
00327         
00328       case _HOOK_REALLOC:
00329         increment = size - _msize(ptr);
00330         break;
00331         
00332       case _HOOK_FREE:
00333         increment = - ((int)_msize(ptr));
00334         break;
00335       }
00336       
00337       mu->_total_size += increment;
00338 
00339       /*
00340         This isn't working reliably right now.
00341       if (_is_cpp_operator) {
00342         mu->_cpp_size += increment;
00343       }
00344       */
00345 
00346 #ifdef TRACK_IN_INTERPRETER
00347       if (in_interpreter) {
00348         mu->_interpreter_size += increment;
00349       }
00350 #endif
00351     }
00352   }
00353 
00354   return true;
00355 }
00356 #endif  // WIN32_VC && _DEBUG
00357 
00358 
00359 
00360 ////////////////////////////////////////////////////////////////////
00361 //     Function: MemoryUsage::Constructor
00362 //       Access: Private
00363 //  Description:
00364 ////////////////////////////////////////////////////////////////////
00365 MemoryUsage::
00366 MemoryUsage() {
00367   // We must get these variables here instead of in
00368   // config_express.cxx, because we need to know it at static init
00369   // time, and who knows when the code in config_express will be
00370   // executed.
00371 
00372   // track-memory-usage should be true to enable full-force tracking
00373   // of C++ allocations and recordkeeping by type.  It's quite
00374   // expensive.
00375   _track_memory_usage =
00376     config_express.GetBool("track-memory-usage", false);
00377 
00378   // count-memory-usage is a much lighter-weight version, and only
00379   // tracks the total memory allocation.  However, it only works for
00380   // certain build environments.
00381 #if defined(WIN32_VC) && defined(_DEBUG)
00382   _count_memory_usage = config_express.GetBool("count-memory-usage", false);
00383 #else
00384   _count_memory_usage = false;
00385 #endif
00386 
00387   if (_track_memory_usage) {
00388     // Redefine the global pointers for operator new and operator
00389     // delete (these pointers are defined up in DTOOL) to vector into
00390     // this class.
00391     global_operator_new = &operator_new_handler;
00392     global_operator_delete = &operator_delete_handler;
00393   }
00394 
00395 #if defined(WIN32_VC) && defined(_DEBUG)
00396   if (_count_memory_usage) {
00397     global_operator_new = &operator_new_handler;
00398     global_operator_delete = &operator_delete_handler;
00399     _CrtSetAllocHook(&win32_malloc_hook);
00400   }
00401 #endif
00402 
00403   _freeze_index = 0;
00404   _count = 0;
00405   _current_cpp_size = 0;
00406   _cpp_size = 0;
00407   _interpreter_size = 0;
00408   _total_size = 0;
00409 }
00410 
00411 ////////////////////////////////////////////////////////////////////
00412 //     Function: MemoryUsage::get_global_ptr
00413 //       Access: Private, Static
00414 //  Description: Returns the pointer to the only MemoryUsage object in
00415 //               the world.
00416 ////////////////////////////////////////////////////////////////////
00417 MemoryUsage *MemoryUsage::
00418 get_global_ptr() {
00419   if (_global_ptr == (MemoryUsage *)NULL) {
00420     _global_ptr = new MemoryUsage;
00421   }
00422   return _global_ptr;
00423 }
00424 
00425 
00426 ////////////////////////////////////////////////////////////////////
00427 //     Function: MemoryUsage::ns_record_pointer
00428 //       Access: Private
00429 //  Description: Indicates that the given pointer has been recently
00430 //               allocated.
00431 ////////////////////////////////////////////////////////////////////
00432 void MemoryUsage::
00433 ns_record_pointer(ReferenceCount *ptr) {
00434   if (_track_memory_usage) {
00435     // We have to protect modifications to the table from recursive
00436     // calls by toggling _recursion_protect while we adjust it.
00437     _recursion_protect = true;
00438     pair<Table::iterator, bool> insert_result =
00439       _table.insert(Table::value_type((void *)ptr, MemoryInfo()));
00440     _recursion_protect = false;
00441     
00442     // This shouldn't fail.
00443     assert(insert_result.first != _table.end());
00444 
00445     if (insert_result.second) {
00446       _count++;
00447     }
00448 
00449     MemoryInfo &info = (*insert_result.first).second;
00450 
00451     // We shouldn't already have a ReferenceCount pointer.
00452     if ((info._flags & MemoryInfo::F_got_ref) != 0) {
00453       express_cat.error()
00454         << "Pointer " << (void *)ptr << " recorded twice!\n";
00455     }
00456 
00457     info._void_ptr = (void *)ptr;
00458     info._ref_ptr = ptr;
00459     info._static_type = ReferenceCount::get_class_type();
00460     info._dynamic_type = ReferenceCount::get_class_type();
00461     info._time = TrueClock::get_ptr()->get_long_time();
00462     info._freeze_index = _freeze_index;
00463     info._flags |= (MemoryInfo::F_reconsider_dynamic_type | MemoryInfo::F_got_ref);
00464   }
00465 }
00466 
00467 ////////////////////////////////////////////////////////////////////
00468 //     Function: MemoryUsage::ns_update_type
00469 //       Access: Private
00470 //  Description: Associates the indicated type with the given pointer.
00471 //               This should be called by functions (e.g. the
00472 //               constructor) that know more specifically what type of
00473 //               thing we've got; otherwise, the MemoryUsage database
00474 //               will know only that it's a "ReferenceCount".
00475 ////////////////////////////////////////////////////////////////////
00476 void MemoryUsage::
00477 ns_update_type(ReferenceCount *ptr, TypeHandle type) {
00478   if (_track_memory_usage) {
00479     Table::iterator ti;
00480     ti = _table.find(ptr);
00481     if (ti == _table.end()) {
00482       express_cat.error()
00483         << "Attempt to update type to " << type << " for unrecorded pointer "
00484         << (void *)ptr << "!\n";
00485       return;
00486     }
00487 
00488     MemoryInfo &info = (*ti).second;
00489     info.update_type_handle(info._static_type, type);
00490     info.determine_dynamic_type();
00491 
00492     consolidate_void_ptr(info);
00493   }
00494 }
00495 
00496 ////////////////////////////////////////////////////////////////////
00497 //     Function: MemoryUsage::ns_update_type
00498 //       Access: Private
00499 //  Description: Associates the indicated type with the given pointer.
00500 //               This flavor of update_type() also passes in the
00501 //               pointer as a TypedObject, and useful for objects that
00502 //               are, in fact, TypedObjects.  Once the MemoryUsage
00503 //               database has the pointer as a TypedObject it doesn't
00504 //               need any more help.
00505 ////////////////////////////////////////////////////////////////////
00506 void MemoryUsage::
00507 ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
00508   if (_track_memory_usage) {
00509     Table::iterator ti;
00510     ti = _table.find(ptr);
00511     if (ti == _table.end()) {
00512       express_cat.error()
00513         << "Attempt to update type to " << typed_ptr->get_type()
00514         << " for unrecorded pointer "
00515         << (void *)ptr << "!\n";
00516       return;
00517     }
00518 
00519     MemoryInfo &info = (*ti).second;
00520     info._typed_ptr = typed_ptr;
00521     info.determine_dynamic_type();
00522 
00523     consolidate_void_ptr(info);
00524   }
00525 }
00526 
00527 ////////////////////////////////////////////////////////////////////
00528 //     Function: MemoryUsage::ns_remove_pointer
00529 //       Access: Private
00530 //  Description: Indicates that the given pointer has been recently
00531 //               freed.
00532 ////////////////////////////////////////////////////////////////////
00533 void MemoryUsage::
00534 ns_remove_pointer(ReferenceCount *ptr) {
00535   if (_track_memory_usage) {
00536     Table::iterator ti;
00537     ti = _table.find(ptr);
00538     if (ti == _table.end()) {
00539       express_cat.error()
00540         << "Attempt to remove pointer " << (void *)ptr
00541         << ", not in table.\n"
00542         << "Possibly a double-destruction.\n";
00543       nassertv(false);
00544       return;
00545     }
00546 
00547     MemoryInfo &info = (*ti).second;
00548 
00549     if ((info._flags & MemoryInfo::F_got_ref) == 0) {
00550       express_cat.error()
00551         << "Pointer " << (void *)ptr << " deleted twice!\n";
00552       return;
00553     }
00554 
00555     info._flags &= ~MemoryInfo::F_got_ref;
00556 
00557     // Since the pointer has been destructed, we can't safely call its
00558     // TypedObject virtual methods any more.  Better clear out the
00559     // typed_ptr for good measure.
00560     info._typed_ptr = (TypedObject *)NULL;
00561 
00562     if (info._freeze_index == _freeze_index) {
00563       double now = TrueClock::get_ptr()->get_long_time();
00564 
00565       // We have to protect modifications to the table from recursive
00566       // calls by toggling _recursion_protect while we adjust it.
00567       _recursion_protect = true;
00568       _trend_types.add_info(info.get_type(), info);
00569       _trend_ages.add_info(now - info._time, info);
00570       _recursion_protect = false;
00571     }
00572 
00573     if ((info._flags & (MemoryInfo::F_got_ref | MemoryInfo::F_got_void)) == 0) {
00574       // If we don't expect to call any more remove_*_pointer on this
00575       // pointer, remove it from the table.
00576       if (info._freeze_index == _freeze_index) {
00577         _count--;
00578         _current_cpp_size -= info._size;
00579       }
00580       _cpp_size -= info._size;
00581 
00582       // We have to protect modifications to the table from recursive
00583       // calls by toggling _recursion_protect while we adjust it.
00584       _recursion_protect = true;
00585       _table.erase(ti);
00586       _recursion_protect = false;
00587     }
00588   }
00589 }
00590 
00591 ////////////////////////////////////////////////////////////////////
00592 //     Function: MemoryUsage::ns_record_void_pointer
00593 //       Access: Private
00594 //  Description: Records a pointer that's not even necessarily a
00595 //               ReferenceCount object (but for which we know the size
00596 //               of the allocated structure).
00597 ////////////////////////////////////////////////////////////////////
00598 void MemoryUsage::
00599 ns_record_void_pointer(void *ptr, size_t size) {
00600   if (_track_memory_usage) {
00601     // We have to protect modifications to the table from recursive
00602     // calls by toggling _recursion_protect while we adjust it.
00603     _recursion_protect = true;
00604     pair<Table::iterator, bool> insert_result =
00605       _table.insert(Table::value_type((void *)ptr, MemoryInfo()));
00606     _recursion_protect = false;
00607 
00608     assert(insert_result.first != _table.end());
00609 
00610     if (insert_result.second) {
00611       _count++;
00612     }
00613 
00614     MemoryInfo &info = (*insert_result.first).second;
00615 
00616     // We shouldn't already have a void pointer.
00617     if ((info._flags & MemoryInfo::F_got_void) != 0) {
00618       express_cat.error()
00619         << "Pointer " << (void *)ptr << " recorded twice!\n";
00620     }
00621 
00622     if (info._freeze_index == _freeze_index) {
00623       _current_cpp_size += size - info._size;
00624     } else {
00625       _current_cpp_size += size;
00626     }
00627     _cpp_size += size - info._size;
00628 
00629     info._void_ptr = ptr;
00630     info._size = size;
00631     info._time = TrueClock::get_ptr()->get_long_time();
00632     info._freeze_index = _freeze_index;
00633     info._flags |= (MemoryInfo::F_got_void | MemoryInfo::F_size_known);
00634   }
00635 }
00636 
00637 ////////////////////////////////////////////////////////////////////
00638 //     Function: MemoryUsage::ns_remove_void_pointer
00639 //       Access: Private
00640 //  Description: Removes a pointer previously recorded via
00641 //               record_void_pointer.
00642 ////////////////////////////////////////////////////////////////////
00643 void MemoryUsage::
00644 ns_remove_void_pointer(void *ptr) {
00645   if (_track_memory_usage) {
00646     Table::iterator ti;
00647     ti = _table.find(ptr);
00648     if (ti == _table.end()) {
00649       // The pointer we tried to delete was not recorded in the table.
00650 
00651       // We can't report this as an error, because (a) we might have
00652       // removed the void pointer entry already when we consolidated,
00653       // and (b) a few objects might have been created during static
00654       // init time, before we grabbed the operator new/delete function
00655       // handlers.
00656       return;
00657     }
00658 
00659     MemoryInfo &info = (*ti).second;
00660 
00661     if ((info._flags & MemoryInfo::F_got_void) == 0) {
00662       express_cat.error()
00663         << "Pointer " << (void *)ptr << " deleted twice!\n";
00664       return;
00665     }
00666 
00667     if ((info._flags & MemoryInfo::F_got_ref) != 0) {
00668       express_cat.error()
00669         << "Pointer " << (void *)ptr << " did not destruct before being deleted!\n";
00670     }
00671 
00672     info._flags &= ~MemoryInfo::F_got_void;
00673 
00674     if ((info._flags & (MemoryInfo::F_got_ref | MemoryInfo::F_got_void)) == 0) {
00675       // If we don't expect to call any more remove_*_pointer on this
00676       // pointer, remove it from the table.
00677 
00678       if (info._freeze_index == _freeze_index) {
00679         _count--;
00680         _current_cpp_size -= info._size;
00681       }
00682       _cpp_size -= info._size;
00683 
00684       // We have to protect modifications to the table from recursive
00685       // calls by toggling _recursion_protect while we adjust it.
00686       _recursion_protect = true;
00687       _table.erase(ti);
00688       _recursion_protect = false;
00689     }
00690   }
00691 }
00692 
00693 ////////////////////////////////////////////////////////////////////
00694 //     Function: MemoryUsage::ns_get_current_cpp_size
00695 //       Access: Private
00696 //  Description: Returns the total number of bytes of allocated memory
00697 //               via the C++ operators new and delete as counted,
00698 //               not including the memory previously frozen.
00699 ////////////////////////////////////////////////////////////////////
00700 size_t MemoryUsage::
00701 ns_get_current_cpp_size() {
00702   return _current_cpp_size;
00703 }
00704 
00705 ////////////////////////////////////////////////////////////////////
00706 //     Function: MemoryUsage::ns_get_cpp_size
00707 //       Access: Private
00708 //  Description: Returns the total number of bytes of allocated memory
00709 //               via the C++ operators new and delete as counted,
00710 //               including the memory previously frozen.
00711 ////////////////////////////////////////////////////////////////////
00712 size_t MemoryUsage::
00713 ns_get_cpp_size() {
00714   return _cpp_size;
00715 }
00716 
00717 ////////////////////////////////////////////////////////////////////
00718 //     Function: MemoryUsage::ns_get_interpreter_size
00719 //       Access: Private
00720 //  Description: Returns the total number of bytes of allocated memory
00721 //               while the high-level languange code is running.  This
00722 //               number is only meaningful if both Panda and the
00723 //               high-level language are single-threaded, and running
00724 //               in the same thread.
00725 ////////////////////////////////////////////////////////////////////
00726 size_t MemoryUsage::
00727 ns_get_interpreter_size() {
00728   return _interpreter_size;
00729 }
00730 
00731 ////////////////////////////////////////////////////////////////////
00732 //     Function: MemoryUsage::ns_get_total_size
00733 //       Access: Private
00734 //  Description: Returns the total size of the dynamic heap, as nearly
00735 //               as can be determined, including all allocated memory
00736 //               if possible, in addition to that tracked by
00737 //               get_cpp_size().
00738 ////////////////////////////////////////////////////////////////////
00739 size_t MemoryUsage::
00740 ns_get_total_size() {
00741   return _total_size;
00742 }
00743 
00744 ////////////////////////////////////////////////////////////////////
00745 //     Function: MemoryUsage::ns_get_num_pointers
00746 //       Access: Private
00747 //  Description: Returns the number of pointers currently active.
00748 ////////////////////////////////////////////////////////////////////
00749 int MemoryUsage::
00750 ns_get_num_pointers() {
00751   nassertr(_track_memory_usage, 0);
00752   return _count;
00753 }
00754 
00755 ////////////////////////////////////////////////////////////////////
00756 //     Function: MemoryUsage::ns_get_pointers
00757 //       Access: Private
00758 //  Description: Fills the indicated MemoryUsagePointers with the set
00759 //               of all pointers currently active.
00760 ////////////////////////////////////////////////////////////////////
00761 void MemoryUsage::
00762 ns_get_pointers(MemoryUsagePointers &result) {
00763   nassertv(_track_memory_usage);
00764   result.clear();
00765 
00766   double now = TrueClock::get_ptr()->get_long_time();
00767   Table::iterator ti;
00768   for (ti = _table.begin(); ti != _table.end(); ++ti) {
00769     MemoryInfo &info = (*ti).second;
00770     if (info._freeze_index == _freeze_index &&
00771         info._ref_ptr != (ReferenceCount *)NULL) {
00772       result.add_entry(info._ref_ptr, info._typed_ptr, info.get_type(),
00773                        now - info._time);
00774     }
00775   }
00776 }
00777 
00778 ////////////////////////////////////////////////////////////////////
00779 //     Function: MemoryUsage::ns_get_pointers_of_type
00780 //       Access: Private
00781 //  Description: Fills the indicated MemoryUsagePointers with the set
00782 //               of all pointers of the indicated type currently
00783 //               active.
00784 ////////////////////////////////////////////////////////////////////
00785 void MemoryUsage::
00786 ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) {
00787   nassertv(_track_memory_usage);
00788   result.clear();
00789 
00790   double now = TrueClock::get_ptr()->get_long_time();
00791   Table::iterator ti;
00792   for (ti = _table.begin(); ti != _table.end(); ++ti) {
00793     MemoryInfo &info = (*ti).second;
00794     if (info._freeze_index == _freeze_index &&
00795         info._ref_ptr != (ReferenceCount *)NULL) {
00796       TypeHandle info_type = info.get_type();
00797       if (info_type != TypeHandle::none() &&
00798           info_type.is_derived_from(type)) {
00799         result.add_entry(info._ref_ptr, info._typed_ptr, info_type,
00800                          now - info._time);
00801       }
00802     }
00803   }
00804 }
00805 
00806 ////////////////////////////////////////////////////////////////////
00807 //     Function: MemoryUsage::ns_get_pointers_of_age
00808 //       Access: Private
00809 //  Description: Fills the indicated MemoryUsagePointers with the set
00810 //               of all pointers that were allocated within the range
00811 //               of the indicated number of seconds ago.
00812 ////////////////////////////////////////////////////////////////////
00813 void MemoryUsage::
00814 ns_get_pointers_of_age(MemoryUsagePointers &result,
00815                        double from, double to) {
00816   nassertv(_track_memory_usage);
00817   result.clear();
00818 
00819   double now = TrueClock::get_ptr()->get_long_time();
00820   Table::iterator ti;
00821   for (ti = _table.begin(); ti != _table.end(); ++ti) {
00822     MemoryInfo &info = (*ti).second;
00823     if (info._freeze_index == _freeze_index &&
00824         info._ref_ptr != (ReferenceCount *)NULL) {
00825       double age = now - info._time;
00826       if ((age >= from && age <= to) ||
00827           (age >= to && age <= from)) {
00828         result.add_entry(info._ref_ptr, info._typed_ptr, info.get_type(), age);
00829       }
00830     }
00831   }
00832 }
00833 
00834 ////////////////////////////////////////////////////////////////////
00835 //     Function: MemoryUsage::ns_get_pointers_with_zero_count
00836 //       Access: Private
00837 //  Description: Fills the indicated MemoryUsagePointers with the set
00838 //               of all currently active pointers (that is, pointers
00839 //               allocated since the last call to freeze(), and not
00840 //               yet freed) that have a zero reference count.
00841 //
00842 //               Generally, an undeleted pointer with a zero reference
00843 //               count means its reference count has never been
00844 //               incremented beyond zero (since once it has been
00845 //               incremented, the only way it can return to zero would
00846 //               free the pointer).  This may include objects that are
00847 //               allocated statically or on the stack, which are never
00848 //               intended to be deleted.  Or, it might represent a
00849 //               programmer or compiler error.
00850 //
00851 //               This function has the side-effect of incrementing
00852 //               each of their reference counts by one, thus
00853 //               preventing them from ever being freed--but since they
00854 //               hadn't been freed anyway, probably no additional harm
00855 //               is done.
00856 ////////////////////////////////////////////////////////////////////
00857 void MemoryUsage::
00858 ns_get_pointers_with_zero_count(MemoryUsagePointers &result) {
00859   nassertv(_track_memory_usage);
00860   result.clear();
00861 
00862   double now = TrueClock::get_ptr()->get_long_time();
00863   Table::iterator ti;
00864   for (ti = _table.begin(); ti != _table.end(); ++ti) {
00865     MemoryInfo &info = (*ti).second;
00866     if (info._freeze_index == _freeze_index && 
00867         info._ref_ptr != (ReferenceCount *)NULL) {
00868       if (info._ref_ptr->get_ref_count() == 0) {
00869         info._ref_ptr->ref();
00870         result.add_entry(info._ref_ptr, info._typed_ptr, info.get_type(),
00871                          now - info._time);
00872       }
00873     }
00874   }
00875 }
00876 
00877 ////////////////////////////////////////////////////////////////////
00878 //     Function: MemoryUsage::ns_freeze
00879 //       Access: Private
00880 //  Description: 'Freezes' all pointers currently stored so that they
00881 //               are no longer reported; only newly allocate pointers
00882 //               from this point on will appear in future information
00883 //               requests.  This makes it easier to differentiate
00884 //               between continuous leaks and one-time memory
00885 //               allocations.
00886 ////////////////////////////////////////////////////////////////////
00887 void MemoryUsage::
00888 ns_freeze() {
00889   _count = 0;
00890   _current_cpp_size = 0;
00891   _trend_types.clear();
00892   _trend_ages.clear();
00893   _freeze_index++;
00894 }
00895 
00896 ////////////////////////////////////////////////////////////////////
00897 //     Function: MemoryUsage::ns_show_current_types
00898 //       Access: Private
00899 //  Description: Shows the breakdown of types of all of the
00900 //               active pointers.
00901 ////////////////////////////////////////////////////////////////////
00902 void MemoryUsage::
00903 ns_show_current_types() {
00904   nassertv(_track_memory_usage);
00905   // We have to protect modifications to the table from recursive
00906   // calls by toggling _recursion_protect while we adjust it.
00907   _recursion_protect = true;
00908 
00909   TypeHistogram hist;
00910   
00911   Table::iterator ti;
00912   for (ti = _table.begin(); ti != _table.end(); ++ti) {
00913     MemoryInfo &info = (*ti).second;
00914     if (info._freeze_index == _freeze_index) {
00915       hist.add_info(info.get_type(), info);
00916     }
00917   }
00918 
00919   hist.show();
00920   _recursion_protect = false;
00921 }
00922 
00923 ////////////////////////////////////////////////////////////////////
00924 //     Function: MemoryUsage::ns_show_trend_types
00925 //       Access: Private
00926 //  Description: Shows the breakdown of types of all of the
00927 //               pointers allocated and freed since the last call to
00928 //               freeze().
00929 ////////////////////////////////////////////////////////////////////
00930 void MemoryUsage::
00931 ns_show_trend_types() {
00932   _trend_types.show();
00933 }
00934 
00935 ////////////////////////////////////////////////////////////////////
00936 //     Function: MemoryUsage::ns_show_current_ages
00937 //       Access: Private
00938 //  Description: Shows the breakdown of ages of all of the
00939 //               active pointers.
00940 ////////////////////////////////////////////////////////////////////
00941 void MemoryUsage::
00942 ns_show_current_ages() {
00943   nassertv(_track_memory_usage);
00944 
00945   // We have to protect modifications to the table from recursive
00946   // calls by toggling _recursion_protect while we adjust it.
00947   _recursion_protect = true;
00948 
00949   AgeHistogram hist;
00950   double now = TrueClock::get_ptr()->get_long_time();
00951 
00952   Table::iterator ti;
00953   for (ti = _table.begin(); ti != _table.end(); ++ti) {
00954     MemoryInfo &info = (*ti).second;
00955     if (info._freeze_index == _freeze_index) {
00956       hist.add_info(now - info._time, info);
00957     }
00958   }
00959 
00960   hist.show();
00961 
00962   _recursion_protect = false;
00963 }
00964 
00965 ////////////////////////////////////////////////////////////////////
00966 //     Function: MemoryUsage::ns_show_trend_ages
00967 //       Access: Private
00968 //  Description: Shows the breakdown of ages of all of the
00969 //               pointers allocated and freed since the last call to
00970 //               freeze().
00971 ////////////////////////////////////////////////////////////////////
00972 void MemoryUsage::
00973 ns_show_trend_ages() {
00974   _trend_ages.show();
00975 }
00976 
00977 ////////////////////////////////////////////////////////////////////
00978 //     Function: MemoryUsage::consolidate_void_ptr
00979 //       Access: Private
00980 //  Description: If the size information has not yet been determined
00981 //               for this pointer, checks to see if it has possible
00982 //               been recorded under the TypedObject pointer (this
00983 //               will happen when the class inherits from TypedObject
00984 //               before ReferenceCount, e.g. TypedReferenceCount).
00985 ////////////////////////////////////////////////////////////////////
00986 void MemoryUsage::
00987 consolidate_void_ptr(MemoryInfo &info) {
00988   if (info.is_size_known()) {
00989     // We already know the size, so no sweat.
00990     return;
00991   }
00992 
00993   if (info.get_typed_ptr() == (TypedObject *)NULL) {
00994     // We don't have a typed pointer for this thing yet.
00995     return;
00996   }
00997 
00998   void *typed_ptr = (void *)info.get_typed_ptr();
00999 
01000   if (typed_ptr == (void *)info.get_ref_ptr()) {
01001     // The TypedObject pointer is the same pointer as the
01002     // ReferenceCount pointer, so there's no point in looking it up
01003     // separately.  Actually, this really shouldn't even be possible.
01004     return;
01005   }
01006 
01007   Table::iterator ti;
01008   ti = _table.find(typed_ptr);
01009   if (ti == _table.end()) {
01010     // No entry for the typed pointer, either.
01011     return;
01012   }
01013 
01014   // We do have an entry!  Copy over the relevant pieces.
01015   MemoryInfo &typed_info = (*ti).second;
01016 
01017   if (typed_info.is_size_known()) {
01018     info._size = typed_info.get_size();
01019     info._flags |= MemoryInfo::F_size_known;
01020     if (typed_info._freeze_index == _freeze_index) {
01021       _current_cpp_size += info._size;
01022     }
01023   }
01024 
01025   // The typed_ptr is clearly the more accurate pointer to the
01026   // beginning of the structure.
01027   info._void_ptr = typed_ptr;
01028 
01029   // Now that we've consolidated the pointers, remove the void pointer
01030   // entry.
01031   if (info._freeze_index == _freeze_index) {
01032     _count--;
01033     _current_cpp_size -= info._size;
01034   }
01035     
01036   // We have to protect modifications to the table from recursive
01037   // calls by toggling _recursion_protect while we adjust it.
01038   _recursion_protect = true;
01039   _table.erase(ti);
01040   _recursion_protect = false;
01041 }
01042 
01043 
01044 #endif  // DO_MEMORY_USAGE

Generated on Fri May 2 00:38:24 2003 for Panda by doxygen1.3