Browse Source

max-heap-size

David Rose 18 years ago
parent
commit
c11e8411d9

+ 42 - 12
dtool/src/dtoolbase/memoryHook.cxx

@@ -135,6 +135,7 @@ MemoryHook() {
   _total_heap_array_size = 0;
   _total_heap_array_size = 0;
   _requested_heap_size = 0;
   _requested_heap_size = 0;
   _total_mmap_size = 0;
   _total_mmap_size = 0;
+  _max_heap_size = ~(size_t)0;
 #endif
 #endif
 }
 }
 
 
@@ -152,6 +153,7 @@ MemoryHook(const MemoryHook &copy) :
   _total_heap_array_size = copy._total_heap_array_size;
   _total_heap_array_size = copy._total_heap_array_size;
   _requested_heap_size = copy._requested_heap_size;
   _requested_heap_size = copy._requested_heap_size;
   _total_mmap_size = copy._total_mmap_size;
   _total_mmap_size = copy._total_mmap_size;
+  _max_heap_size = copy._max_heap_size;
 #endif
 #endif
 
 
   ((MutexImpl &)copy._lock).lock();
   ((MutexImpl &)copy._lock).lock();
@@ -183,12 +185,6 @@ MemoryHook::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void *MemoryHook::
 void *MemoryHook::
 heap_alloc_single(size_t size) {
 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
 #ifdef MEMORY_HOOK_MALLOC_LOCK
   _lock.lock();
   _lock.lock();
   void *alloc = call_malloc(inflate_size(size));
   void *alloc = call_malloc(inflate_size(size));
@@ -202,6 +198,17 @@ heap_alloc_single(size_t size) {
     abort();
     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);
   return alloc_to_ptr(alloc, size);
 }
 }
 
 
@@ -244,12 +251,6 @@ heap_free_single(void *ptr) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void *MemoryHook::
 void *MemoryHook::
 heap_alloc_array(size_t size) {
 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
 #ifdef MEMORY_HOOK_MALLOC_LOCK
   _lock.lock();
   _lock.lock();
   void *alloc = call_malloc(inflate_size(size));
   void *alloc = call_malloc(inflate_size(size));
@@ -263,6 +264,17 @@ heap_alloc_array(size_t size) {
     abort();
     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);
   return alloc_to_ptr(alloc, size);
 }
 }
 
 
@@ -490,3 +502,21 @@ get_deleted_chain(size_t buffer_size) {
   _lock.release();
   _lock.release();
   return chain;
   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

+ 7 - 1
dtool/src/dtoolbase/memoryHook.h

@@ -82,7 +82,13 @@ protected:
   TVOLATILE PN_int32 _total_heap_array_size;
   TVOLATILE PN_int32 _total_heap_array_size;
   TVOLATILE PN_int32 _requested_heap_size;
   TVOLATILE PN_int32 _requested_heap_size;
   TVOLATILE PN_int32 _total_mmap_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:
 private:
   size_t _page_size;
   size_t _page_size;

+ 5 - 5
panda/src/express/memoryUsage.I

@@ -137,7 +137,7 @@ get_total_cpp_size() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE size_t MemoryUsage::
 INLINE size_t MemoryUsage::
 get_panda_heap_single_size() {
 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::
 INLINE size_t MemoryUsage::
 get_panda_heap_array_size() {
 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() {
 get_panda_heap_overhead() {
 #if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
 #if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
   MemoryUsage *mu = get_global_ptr();
   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
 #else
   return 0;
   return 0;
 #endif
 #endif
@@ -177,7 +177,7 @@ get_panda_heap_overhead() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE size_t MemoryUsage::
 INLINE size_t MemoryUsage::
 get_panda_mmap_size() {
 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)
 #if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
     return mu->_requested_heap_size;
     return mu->_requested_heap_size;
 #else
 #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
 #endif
   }
   }
 }
 }

+ 86 - 13
panda/src/express/memoryUsage.cxx

@@ -382,8 +382,34 @@ MemoryUsage(const MemoryHook &copy) : MemoryHook(copy) {
      PRC_DESC("Set this to true to enable full-force tracking of C++ allocations "
      PRC_DESC("Set this to true to enable full-force tracking of C++ allocations "
               "and recordkeeping by type.  It's quite expensive."));
               "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;
   _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
 #ifdef USE_MEMORY_NOWRAPPERS
 #error Cannot compile MemoryUsage without malloc wrappers!
 #error Cannot compile MemoryUsage without malloc wrappers!
 #endif
 #endif
@@ -405,6 +431,37 @@ MemoryUsage(const MemoryHook &copy) : MemoryHook(copy) {
   _total_size = 0;
   _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
 //     Function: MemoryUsage::get_global_ptr
 //       Access: Private, Static
 //       Access: Private, Static
@@ -464,6 +521,16 @@ ns_record_pointer(ReferenceCount *ptr) {
     // that we also protect ourselves against a possible recursive
     // that we also protect ourselves against a possible recursive
     // call in TrueClock::get_global_ptr().
     // call in TrueClock::get_global_ptr().
     _recursion_protect = false;
     _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;
     Table::iterator ti;
     ti = _table.find(ptr);
     ti = _table.find(ptr);
     if (ti == _table.end()) {
     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;
       return;
     }
     }
 
 
@@ -514,10 +583,12 @@ ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
     Table::iterator ti;
     Table::iterator ti;
     ti = _table.find(ptr);
     ti = _table.find(ptr);
     if (ti == _table.end()) {
     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;
       return;
     }
     }
 
 
@@ -541,11 +612,13 @@ ns_remove_pointer(ReferenceCount *ptr) {
     Table::iterator ti;
     Table::iterator ti;
     ti = _table.find(ptr);
     ti = _table.find(ptr);
     if (ti == _table.end()) {
     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;
       return;
     }
     }
 
 

+ 7 - 0
panda/src/express/memoryUsage.h

@@ -91,6 +91,9 @@ PUBLISHED:
   INLINE static void show_current_ages();
   INLINE static void show_current_ages();
   INLINE static void show_trend_ages();
   INLINE static void show_trend_ages();
 
 
+protected:
+  virtual void overflow_heap_size();
+
 private:
 private:
   MemoryUsage(const MemoryHook &copy);
   MemoryUsage(const MemoryHook &copy);
   static MemoryUsage *get_global_ptr();
   static MemoryUsage *get_global_ptr();
@@ -183,7 +186,11 @@ private:
 
 
 
 
   bool _track_memory_usage;
   bool _track_memory_usage;
+  bool _startup_track_memory_usage;
   bool _count_memory_usage;
   bool _count_memory_usage;
+  bool _report_memory_usage;
+  double _report_memory_interval;
+  double _last_report_time;
 
 
   static bool _recursion_protect;
   static bool _recursion_protect;
 };
 };