diff --git a/dtool/src/dtoolbase/memoryHook.cxx b/dtool/src/dtoolbase/memoryHook.cxx index c11dde4f34..b99938a6b2 100644 --- a/dtool/src/dtoolbase/memoryHook.cxx +++ b/dtool/src/dtoolbase/memoryHook.cxx @@ -135,6 +135,7 @@ MemoryHook() { _total_heap_array_size = 0; _requested_heap_size = 0; _total_mmap_size = 0; + _max_heap_size = ~(size_t)0; #endif } @@ -152,6 +153,7 @@ MemoryHook(const MemoryHook ©) : _total_heap_array_size = copy._total_heap_array_size; _requested_heap_size = copy._requested_heap_size; _total_mmap_size = copy._total_mmap_size; + _max_heap_size = copy._max_heap_size; #endif ((MutexImpl &)copy._lock).lock(); @@ -183,12 +185,6 @@ MemoryHook:: //////////////////////////////////////////////////////////////////// void *MemoryHook:: heap_alloc_single(size_t size) { -#ifdef DO_MEMORY_USAGE - // In the DO_MEMORY_USAGE case, we want to track the total size of - // allocated bytes on the heap. - AtomicAdjust::add(_total_heap_single_size, (PN_int32)size); -#endif // DO_MEMORY_USAGE - #ifdef MEMORY_HOOK_MALLOC_LOCK _lock.lock(); void *alloc = call_malloc(inflate_size(size)); @@ -202,6 +198,17 @@ heap_alloc_single(size_t size) { abort(); } +#ifdef DO_MEMORY_USAGE + // In the DO_MEMORY_USAGE case, we want to track the total size of + // allocated bytes on the heap. + AtomicAdjust::add(_total_heap_single_size, (PN_int32)size); + if ((size_t)AtomicAdjust::get(_total_heap_single_size) + + (size_t)AtomicAdjust::get(_total_heap_array_size) > + _max_heap_size) { + overflow_heap_size(); + } +#endif // DO_MEMORY_USAGE + return alloc_to_ptr(alloc, size); } @@ -244,12 +251,6 @@ heap_free_single(void *ptr) { //////////////////////////////////////////////////////////////////// void *MemoryHook:: heap_alloc_array(size_t size) { -#ifdef DO_MEMORY_USAGE - // In the DO_MEMORY_USAGE case, we want to track the total size of - // allocated bytes on the heap. - AtomicAdjust::add(_total_heap_array_size, (PN_int32)size); -#endif // DO_MEMORY_USAGE - #ifdef MEMORY_HOOK_MALLOC_LOCK _lock.lock(); void *alloc = call_malloc(inflate_size(size)); @@ -263,6 +264,17 @@ heap_alloc_array(size_t size) { abort(); } +#ifdef DO_MEMORY_USAGE + // In the DO_MEMORY_USAGE case, we want to track the total size of + // allocated bytes on the heap. + AtomicAdjust::add(_total_heap_array_size, (PN_int32)size); + if ((size_t)AtomicAdjust::get(_total_heap_single_size) + + (size_t)AtomicAdjust::get(_total_heap_array_size) > + _max_heap_size) { + overflow_heap_size(); + } +#endif // DO_MEMORY_USAGE + return alloc_to_ptr(alloc, size); } @@ -490,3 +502,21 @@ get_deleted_chain(size_t buffer_size) { _lock.release(); return chain; } + +#ifdef DO_MEMORY_USAGE +//////////////////////////////////////////////////////////////////// +// Function: MemoryHook::overflow_heap_size +// Access: Protected, Virtual +// Description: This callback method is called whenever the total +// allocated heap size exceeds _max_heap_size. It's +// mainly intended for reporting memory leaks, on the +// assumption that once we cross some specified +// threshold, we're just leaking memory. +// +// The implementation for this method is in MemoryUsage. +//////////////////////////////////////////////////////////////////// +void MemoryHook:: +overflow_heap_size() { + _max_heap_size = ~(size_t)0; +} +#endif // DO_MEMORY_USAGE diff --git a/dtool/src/dtoolbase/memoryHook.h b/dtool/src/dtoolbase/memoryHook.h index 199f033666..88bb50a276 100644 --- a/dtool/src/dtoolbase/memoryHook.h +++ b/dtool/src/dtoolbase/memoryHook.h @@ -82,7 +82,13 @@ protected: TVOLATILE PN_int32 _total_heap_array_size; TVOLATILE PN_int32 _requested_heap_size; TVOLATILE PN_int32 _total_mmap_size; -#endif + + // If the allocated heap size crosses this threshold, we call + // overflow_heap_size(). + size_t _max_heap_size; + + virtual void overflow_heap_size(); +#endif // DO_MEMORY_USAGE private: size_t _page_size; diff --git a/panda/src/express/memoryUsage.I b/panda/src/express/memoryUsage.I index 3a81abc457..8507f809b5 100644 --- a/panda/src/express/memoryUsage.I +++ b/panda/src/express/memoryUsage.I @@ -137,7 +137,7 @@ get_total_cpp_size() { //////////////////////////////////////////////////////////////////// INLINE size_t MemoryUsage:: get_panda_heap_single_size() { - return get_global_ptr()->_total_heap_single_size; + return AtomicAdjust::get(get_global_ptr()->_total_heap_single_size); } //////////////////////////////////////////////////////////////////// @@ -148,7 +148,7 @@ get_panda_heap_single_size() { //////////////////////////////////////////////////////////////////// INLINE size_t MemoryUsage:: get_panda_heap_array_size() { - return get_global_ptr()->_total_heap_array_size; + return AtomicAdjust::get(get_global_ptr()->_total_heap_array_size); } //////////////////////////////////////////////////////////////////// @@ -163,7 +163,7 @@ INLINE size_t MemoryUsage:: get_panda_heap_overhead() { #if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2) MemoryUsage *mu = get_global_ptr(); - return mu->_requested_heap_size - mu->_total_heap_single_size - mu->_total_heap_array_size; + return AtomicAdjust::get(mu->_requested_heap_size) - AtomicAdjust::get(mu->_total_heap_single_size) - AtomicAdjust::get(mu->_total_heap_array_size); #else return 0; #endif @@ -177,7 +177,7 @@ get_panda_heap_overhead() { //////////////////////////////////////////////////////////////////// INLINE size_t MemoryUsage:: get_panda_mmap_size() { - return get_global_ptr()->_total_mmap_size; + return AtomicAdjust::get(get_global_ptr()->_total_mmap_size); } //////////////////////////////////////////////////////////////////// @@ -246,7 +246,7 @@ get_total_size() { #if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2) return mu->_requested_heap_size; #else - return mu->_total_heap_single_size + mu->_total_heap_array_size; + return AtomicAdjust::get(mu->_total_heap_single_size) + AtomicAdjust::get(mu->_total_heap_array_size); #endif } } diff --git a/panda/src/express/memoryUsage.cxx b/panda/src/express/memoryUsage.cxx index 4a2053b8ca..63a14b705c 100644 --- a/panda/src/express/memoryUsage.cxx +++ b/panda/src/express/memoryUsage.cxx @@ -382,8 +382,34 @@ MemoryUsage(const MemoryHook ©) : MemoryHook(copy) { PRC_DESC("Set this to true to enable full-force tracking of C++ allocations " "and recordkeeping by type. It's quite expensive.")); + // Since enabling this after startup might cause bogus errors, we'd + // like to know if this happened, so we can squelch those error + // messages. + _startup_track_memory_usage = _track_memory_usage; + + _report_memory_usage = ConfigVariableBool + ("report-memory-usage", false, + PRC_DESC("Set this true to enable automatic reporting of allocated objects " + "at the interval specified by report-memory-interval. This also " + "requires track-memory-usage.")); + _report_memory_interval = ConfigVariableDouble + ("report-memory-interval", 5.0, + PRC_DESC("This is the interval, in seconds, for reports of currently allocated " + "memory, when report-memory-usage is true.")); + _last_report_time = 0.0; + _count_memory_usage = false; + int max_heap_size = ConfigVariableInt + ("max-heap-size", 0, + PRC_DESC("If this is nonzero, it is the maximum number of bytes expected " + "to be allocated on the heap before we enter report-memory-usage " + "mode automatically. The assumption is that once this limit " + "has been crossed, we must be leaking.")); + if (max_heap_size != 0) { + _max_heap_size = (size_t)max_heap_size; + } + #ifdef USE_MEMORY_NOWRAPPERS #error Cannot compile MemoryUsage without malloc wrappers! #endif @@ -405,6 +431,37 @@ MemoryUsage(const MemoryHook ©) : MemoryHook(copy) { _total_size = 0; } +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::overflow_heap_size +// Access: Protected, Virtual +// Description: This callback method is called whenever the total +// allocated heap size exceeds _max_heap_size. It's +// mainly intended for reporting memory leaks, on the +// assumption that once we cross some specified +// threshold, we're just leaking memory. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +overflow_heap_size() { + MemoryHook::overflow_heap_size(); + + express_cat.error() + << "Total allocated memory has reached " + << get_panda_heap_single_size() + get_panda_heap_array_size() + << " bytes." + << "\n heap single: " << get_panda_heap_single_size() + << "\n heap array: " << get_panda_heap_array_size() + << "\n heap overhead: " << get_panda_heap_overhead() + << "\n mmap: " << get_panda_mmap_size() + << "\n interpreter: " << get_interpreter_size() + << "\n external: " << get_external_size() + << "\n total: " << get_total_size() + << "\n"; + + // Turn on spamful debugging. + _track_memory_usage = true; + _report_memory_usage = true; +} + //////////////////////////////////////////////////////////////////// // Function: MemoryUsage::get_global_ptr // Access: Private, Static @@ -464,6 +521,16 @@ ns_record_pointer(ReferenceCount *ptr) { // that we also protect ourselves against a possible recursive // call in TrueClock::get_global_ptr(). _recursion_protect = false; + + if (_report_memory_usage) { + double now = TrueClock::get_global_ptr()->get_long_time(); + if (now - _last_report_time > _report_memory_interval) { + _last_report_time = now; + express_cat.info() + << "*** Current memory usage: " << get_total_size() << "\n"; + show_current_types(); + } + } } } @@ -482,10 +549,12 @@ ns_update_type(ReferenceCount *ptr, TypeHandle type) { Table::iterator ti; ti = _table.find(ptr); if (ti == _table.end()) { - express_cat.error() - << "Attempt to update type to " << type << " for unrecorded pointer " - << (void *)ptr << "!\n"; - nassertv(false); + if (_startup_track_memory_usage) { + express_cat.error() + << "Attempt to update type to " << type << " for unrecorded pointer " + << (void *)ptr << "!\n"; + nassertv(false); + } return; } @@ -514,10 +583,12 @@ ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) { Table::iterator ti; ti = _table.find(ptr); if (ti == _table.end()) { - express_cat.error() - << "Attempt to update type to " << typed_ptr->get_type() - << " for unrecorded pointer " - << (void *)ptr << "!\n"; + if (_startup_track_memory_usage) { + express_cat.error() + << "Attempt to update type to " << typed_ptr->get_type() + << " for unrecorded pointer " + << (void *)ptr << "!\n"; + } return; } @@ -541,11 +612,13 @@ ns_remove_pointer(ReferenceCount *ptr) { Table::iterator ti; ti = _table.find(ptr); if (ti == _table.end()) { - express_cat.error() - << "Attempt to remove pointer " << (void *)ptr - << ", not in table.\n" - << "Possibly a double-destruction.\n"; - nassertv(false); + if (_startup_track_memory_usage) { + express_cat.error() + << "Attempt to remove pointer " << (void *)ptr + << ", not in table.\n" + << "Possibly a double-destruction.\n"; + nassertv(false); + } return; } diff --git a/panda/src/express/memoryUsage.h b/panda/src/express/memoryUsage.h index 694b11fa3e..97f1ebbf15 100644 --- a/panda/src/express/memoryUsage.h +++ b/panda/src/express/memoryUsage.h @@ -91,6 +91,9 @@ PUBLISHED: INLINE static void show_current_ages(); INLINE static void show_trend_ages(); +protected: + virtual void overflow_heap_size(); + private: MemoryUsage(const MemoryHook ©); static MemoryUsage *get_global_ptr(); @@ -183,7 +186,11 @@ private: bool _track_memory_usage; + bool _startup_track_memory_usage; bool _count_memory_usage; + bool _report_memory_usage; + double _report_memory_interval; + double _last_report_time; static bool _recursion_protect; };