Browse Source

move some low-level mem tracking into MemoryHook, more mem tracking fixes

David Rose 18 years ago
parent
commit
d52eb37b14
38 changed files with 704 additions and 819 deletions
  1. 0 3
      dtool/Config.FreeBSD.pp
  2. 0 3
      dtool/Config.Irix.pp
  3. 0 3
      dtool/Config.Linux.pp
  4. 0 3
      dtool/Config.OSX.pp
  5. 0 3
      dtool/Config.Win32.pp
  6. 0 5
      dtool/Config.pp
  7. 0 4
      dtool/LocalSetup.pp
  8. 3 2
      dtool/src/dtoolbase/Sources.pp
  9. 0 35
      dtool/src/dtoolbase/dallocator.T
  10. 0 76
      dtool/src/dtoolbase/dallocator.h
  11. 7 54
      dtool/src/dtoolbase/deletedChain.T
  12. 9 16
      dtool/src/dtoolbase/deletedChain.h
  13. 26 122
      dtool/src/dtoolbase/dtoolbase.cxx
  14. 14 52
      dtool/src/dtoolbase/dtoolbase_cc.h
  15. 1 0
      dtool/src/dtoolbase/dtoolbase_composite1.cxx
  16. 5 4
      dtool/src/dtoolbase/memoryBase.h
  17. 75 0
      dtool/src/dtoolbase/memoryHook.I
  18. 294 0
      dtool/src/dtoolbase/memoryHook.cxx
  19. 62 0
      dtool/src/dtoolbase/memoryHook.h
  20. 3 73
      dtool/src/dtoolbase/neverFreeMemory.cxx
  21. 0 1
      dtool/src/dtoolbase/neverFreeMemory.h
  22. 2 2
      dtool/src/dtoolbase/pallocator.T
  23. 2 1
      dtool/src/dtoolbase/pallocator.h
  24. 4 0
      dtool/src/dtoolbase/typeHandle.cxx
  25. 1 0
      dtool/src/prc/configVariableManager.cxx
  26. 1 0
      dtool/src/prc/notify.cxx
  27. 2 1
      dtool/src/prc/notifyCategory.h
  28. 0 1
      dtool/src/prc/pnotify.h
  29. 1 0
      panda/src/egg/eggNode.h
  30. 50 57
      panda/src/express/memoryUsage.I
  31. 87 184
      panda/src/express/memoryUsage.cxx
  32. 17 23
      panda/src/express/memoryUsage.h
  33. 3 34
      panda/src/gobj/vertexDataPage.cxx
  34. 2 0
      panda/src/pgraph/pandaNode.h
  35. 3 31
      panda/src/pipeline/threadSimpleImpl.cxx
  36. 18 18
      panda/src/pstatclient/pStatClient.cxx
  37. 8 6
      panda/src/pstatclient/pStatClient.h
  38. 4 2
      panda/src/pstatclient/pStatProperties.cxx

+ 0 - 3
dtool/Config.FreeBSD.pp

@@ -152,9 +152,6 @@
 
 #define HAVE_POSIX_THREADS 1
 
-// Must global operator new and delete functions throw exceptions?
-#define GLOBAL_OPERATOR_NEW_EXCEPTIONS 1
-
 // Modern versions of gcc do support the latest STL allocator
 // definitions.
 #define USE_STL_ALLOCATOR 1

+ 0 - 3
dtool/Config.Irix.pp

@@ -140,9 +140,6 @@
 // Do we have RTTI (and <typeinfo>)?
 #define HAVE_RTTI 1
 
-// Must global operator new and delete functions throw exceptions?
-#define GLOBAL_OPERATOR_NEW_EXCEPTIONS
-
 // The Irix compiler doesn't support the modern STL allocator.
 #define USE_STL_ALLOCATOR
 

+ 0 - 3
dtool/Config.Linux.pp

@@ -154,9 +154,6 @@
 // We need 64-bit file i/o
 #define __USE_LARGEFILE64 1
 
-// Must global operator new and delete functions throw exceptions?
-#define GLOBAL_OPERATOR_NEW_EXCEPTIONS 1
-
 // Modern versions of gcc do support the latest STL allocator
 // definitions.
 #define USE_STL_ALLOCATOR 1

+ 0 - 3
dtool/Config.OSX.pp

@@ -154,9 +154,6 @@
 // Do we have RTTI (and <typeinfo>)?
 #define HAVE_RTTI 1
 
-// Must global operator new and delete functions throw exceptions?
-#define GLOBAL_OPERATOR_NEW_EXCEPTIONS 1
-
 // Modern versions of gcc do support the latest STL allocator
 // definitions.
 #define USE_STL_ALLOCATOR 1

+ 0 - 3
dtool/Config.Win32.pp

@@ -149,9 +149,6 @@
 // Do we have RTTI (and <typeinfo>)?
 #define HAVE_RTTI 1
 
-// Must global operator new and delete functions throw exceptions?
-#define GLOBAL_OPERATOR_NEW_EXCEPTIONS
-
 // MSVC7 does support the latest STL allocator definitions.
 #define USE_STL_ALLOCATOR 1
 

+ 0 - 5
dtool/Config.pp

@@ -348,11 +348,6 @@
 // available, even if it is unused.
 #defer DO_MEMORY_USAGE $[<= $[OPTIMIZE], 3]
 
-// Should we attempt to override the global new and delete operators?
-// It turns out this is usually a bad idea, but may be useful for
-// development.
-#define REDEFINE_GLOBAL_OPERATOR_NEW
-
 // This option compiles in support for simulating network delay via
 // the min-lag and max-lag prc variables.  It adds a tiny bit of
 // overhead even when it is not activated, so it is typically enabled

+ 0 - 4
dtool/LocalSetup.pp

@@ -309,7 +309,6 @@ $[cdefine TRACK_IN_INTERPRETER]
 
 /* Define if we want to enable track-memory-usage.  */
 $[cdefine DO_MEMORY_USAGE]
-$[cdefine REDEFINE_GLOBAL_OPERATOR_NEW]
 
 /* Define if we want to enable min-lag and max-lag.  */
 $[cdefine SIMULATE_NETWORK_DELAY]
@@ -494,9 +493,6 @@ $[cdefine USE_TAU]
 /* Define if needed to have 64-bit file i/o */
 $[cdefine __USE_LARGEFILE64]
 
-/* Must global operator new and delete functions throw exceptions? */
-$[cdefine GLOBAL_OPERATOR_NEW_EXCEPTIONS]
-
 /* Which memory allocation scheme should we use? */
 #define USE_MEMORY_DLMALLOC
 #define USE_MEMORY_PTMALLOC2

+ 3 - 2
dtool/src/dtoolbase/Sources.pp

@@ -14,12 +14,12 @@
     atomicAdjustPosixImpl.h atomicAdjustPosixImpl.I \
     atomicAdjustWin32Impl.h atomicAdjustWin32Impl.I \
     cmath.I cmath.h \
-    dallocator.T dallocator.h \
     deletedChain.h deletedChain.T \
     dtoolbase.h dtoolbase_cc.h dtoolsymbols.h \
     fakestringstream.h \
     indent.I indent.h indent.cxx \
     memoryBase.h \
+    memoryHook.h memoryHook.I \
     mutexImpl.h \
     mutexDummyImpl.h mutexDummyImpl.I \
     mutexPosixImpl.h mutexPosixImpl.I \
@@ -47,6 +47,7 @@
     atomicAdjustWin32Impl.cxx \
     dtoolbase.cxx \
     memoryBase.cxx \
+    memoryHook.cxx \
     mutexDummyImpl.cxx \
     mutexPosixImpl.cxx \
     mutexWin32Impl.cxx \
@@ -65,11 +66,11 @@
     atomicAdjustPosixImpl.h atomicAdjustPosixImpl.I \
     atomicAdjustWin32Impl.h atomicAdjustWin32Impl.I \
     cmath.I cmath.h \
-    dallocator.T dallocator.h \
     deletedChain.h deletedChain.T \
     dtoolbase.h dtoolbase_cc.h dtoolsymbols.h fakestringstream.h \
     indent.I indent.h \
     memoryBase.h \
+    memoryHook.h memoryHook.I \
     mutexImpl.h \
     mutexDummyImpl.h mutexDummyImpl.I \
     mutexPosixImpl.h mutexPosixImpl.I \

+ 0 - 35
dtool/src/dtoolbase/dallocator.T

@@ -1,35 +0,0 @@
-// Filename: dallocator.T
-// Created by:  drose (05Jun01)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://www.panda3d.org/license.txt .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-
-template<class Type>
-INLINE dallocator<Type>::
-dallocator() throw() {
-}
-
-template<class Type>
-INLINE TYPENAME dallocator<Type>::pointer dallocator<Type>::
-allocate(TYPENAME dallocator<Type>::size_type n, allocator<void>::const_pointer) {
-  return (TYPENAME dallocator<Type>::pointer)(*global_operator_new)(n * sizeof(Type));
-}
-
-template<class Type>
-INLINE void dallocator<Type>::
-deallocate(TYPENAME dallocator<Type>::pointer p, TYPENAME dallocator<Type>::size_type) {
-  (*global_operator_delete)(p);
-}

+ 0 - 76
dtool/src/dtoolbase/dallocator.h

@@ -1,76 +0,0 @@
-// Filename: dallocator.h
-// Created by:  drose (05Jun01)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#ifndef DALLOCATOR_H
-#define DALLOCATOR_H
-#include <memory>
-
-#include "dtoolbase.h"
-
-////////////////////////////////////////////////////////////////////
-//       Class : dallocator
-// Description : This is similar to pallocator, but always uses the
-//               default new and delete handlers defined in
-//               dtoolbase_cc.h; it never calls the hooks assigned by
-//               redefining global_operator_new, etc.
-//
-//               This is needed in those rare cases when we need to
-//               allocate memory for STL without going through the
-//               callback hooks, for instance to implement STL tables
-//               within the MemoryUsage class itself.
-////////////////////////////////////////////////////////////////////
-
-#ifndef USE_STL_ALLOCATOR
-// If we're not trying to make custom allocators (either we don't know
-// what kind of syntax this STL library wants, or we're compiling with
-// OPTIMIZE 4), then simply use the standard allocator.
-#define dallocator allocator
-
-#else
-
-template<class Type>
-class dallocator : public allocator<Type> {
-public:
-  // There seems to be a bug in VC++ 2003 that requires these typedefs
-  // to be made explicitly.
-  typedef TYPENAME allocator<Type>::pointer pointer;
-  typedef TYPENAME allocator<Type>::reference reference;
-  typedef TYPENAME allocator<Type>::const_pointer const_pointer;
-  typedef TYPENAME allocator<Type>::const_reference const_reference;
-  typedef TYPENAME allocator<Type>::size_type size_type;
-
-  INLINE dallocator() throw();
-
-  // template member functions in VC++ can only be defined in-class.
-  template<class U>
-  INLINE dallocator(const dallocator<U> &copy) throw() { }
-
-  INLINE pointer allocate(size_type n, allocator<void>::const_pointer hint = 0);
-  INLINE void deallocate(pointer p, size_type n);
-
-  template<class U> struct rebind { 
-    typedef dallocator<U> other; 
-  };
-};
-
-#include "dallocator.T"
-
-#endif  // USE_STL_ALLOCATOR
-
-#endif
-

+ 7 - 54
dtool/src/dtoolbase/deletedChain.T

@@ -36,9 +36,8 @@ allocate(size_t size, TypeHandle type_handle) {
   alloc_size += sizeof(PN_int32);
 #endif  // NDEBUG
 
-  TVOLATILE ObjectNode *obj;
+  ObjectNode *obj;
 
-#ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE
   init_lock();
   _lock->lock();
   if (_deleted_chain != (ObjectNode *)NULL) {
@@ -56,36 +55,13 @@ allocate(size_t size, TypeHandle type_handle) {
 #ifdef DO_MEMORY_USAGE
     type_handle.dec_memory_usage(TypeHandle::MC_deleted_chain_inactive, alloc_size);
     type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_active, alloc_size);
-    (*global_mark_pointer)(ptr, max(size, sizeof(ObjectNode)), make_ref_ptr(ptr));
+    memory_hook->mark_pointer(ptr, max(size, sizeof(ObjectNode)), make_ref_ptr(ptr));
 #endif  // DO_MEMORY_USAGE
 
     return ptr;
   }
   _lock->release();
 
-#else  // DELETED_CHAIN_USE_ATOMIC_EXCHANGE
-  obj = _deleted_chain;
-  while (obj != (ObjectNode *)NULL) {
-    TVOLATILE ObjectNode *next = obj->_next;
-    TVOLATILE ObjectNode *result = (ObjectNode *)AtomicAdjust::compare_and_exchange_ptr((void * TVOLATILE &)_deleted_chain, (void *)obj, (void *)next);
-    if (result == obj) {
-      // We got it.
-#ifdef DO_MEMORY_USAGE
-      type_handle.dec_memory_usage(TypeHandle::MC_deleted_chain_inactive, alloc_size);
-      type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_active, alloc_size);
-#endif  // DO_MEMORY_USAGE
-#ifdef USE_DELETEDCHAINFLAG
-      PN_int32 orig_flag = AtomicAdjust::compare_and_exchange(obj->_flag, DCF_deleted, DCF_alive);
-      assert(orig_flag == (PN_int32)DCF_deleted);
-#endif  // NDEBUG
-      return node_to_type(obj);
-    }
-    // Someone else grabbed the top link first.  Try again.
-    obj = _deleted_chain;
-  }
-
-#endif  // DELETED_CHAIN_USE_ATOMIC_EXCHANGE
-
   // If we get here, the deleted_chain is empty; we have to allocate a
   // new object from the system pool.
 
@@ -100,7 +76,7 @@ allocate(size_t size, TypeHandle type_handle) {
 
 #ifdef DO_MEMORY_USAGE
   type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_active, alloc_size);
-  (*global_mark_pointer)(ptr, max(size, sizeof(ObjectNode)), make_ref_ptr(ptr));
+  memory_hook->mark_pointer(ptr, max(size, sizeof(ObjectNode)), make_ref_ptr(ptr));
 #endif  // DO_MEMORY_USAGE
 
   return ptr;
@@ -120,7 +96,7 @@ deallocate(Type *ptr, TypeHandle type_handle) {
 #ifdef DO_MEMORY_USAGE
   size_t alloc_size = sizeof(Type);
 
-  (*global_mark_pointer)(ptr, 0, make_ref_ptr(ptr));
+  memory_hook->mark_pointer(ptr, 0, make_ref_ptr(ptr));
 
 #ifdef USE_DELETEDCHAINFLAG
   // In development mode, we also need to reserve space for _flag.
@@ -131,7 +107,7 @@ deallocate(Type *ptr, TypeHandle type_handle) {
 
 #endif  // DO_MEMORY_USAGE
 
-  TVOLATILE ObjectNode *obj = type_to_node(ptr);
+  ObjectNode *obj = type_to_node(ptr);
 
 #ifdef USE_DELETEDCHAINFLAG
   PN_int32 orig_flag = AtomicAdjust::compare_and_exchange(obj->_flag, DCF_alive, DCF_deleted);
@@ -144,7 +120,6 @@ deallocate(Type *ptr, TypeHandle type_handle) {
   assert(orig_flag == (PN_int32)DCF_alive);
 #endif  // NDEBUG
 
-#ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE
   // We have to test the lock again, even though we must have
   // allocated this object at some point, because Windows might make
   // multiple different DeletedChain instances for a particular Type,
@@ -157,20 +132,6 @@ deallocate(Type *ptr, TypeHandle type_handle) {
   _deleted_chain = obj;
 
   _lock->release();
-
-#else  // DELETED_CHAIN_USE_ATOMIC_EXCHANGE
-
-  TVOLATILE ObjectNode *result;
-  TVOLATILE ObjectNode *next;
-
-  do {
-    next = _deleted_chain;
-    obj->_next = next;
-    result = type_to_node(AtomicAdjust::compare_and_exchange_ptr((void * TVOLATILE &)_deleted_chain, (void *)next, (void *)obj));
-    // Keep trying until no one else got to _deleted_chain first.
-  } while (result != next);
-
-#endif  // DELETED_CHAIN_USE_ATOMIC_EXCHANGE
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -192,7 +153,7 @@ validate(const Type *ptr) {
   }
 
 #ifdef USE_DELETEDCHAINFLAG
-  TVOLATILE const ObjectNode *obj = type_to_node((Type *)ptr);
+  const ObjectNode *obj = type_to_node((Type *)ptr);
   return AtomicAdjust::get(obj->_flag) == DCF_alive;
 #else
   return true;
@@ -240,7 +201,7 @@ make_ref_ptr(ReferenceCount *ptr) {
 ////////////////////////////////////////////////////////////////////
 template<class Type>
 INLINE Type *DeletedChain<Type>::
-node_to_type(TVOLATILE TYPENAME DeletedChain<Type>::ObjectNode *node) {
+node_to_type(TYPENAME DeletedChain<Type>::ObjectNode *node) {
 #ifdef USE_DELETEDCHAINFLAG
   // In development mode, we increment the pointer so that the
   // returned data does not overlap our _flags member.
@@ -267,7 +228,6 @@ type_to_node(Type *ptr) {
 #endif  // NDEBUG
 }
 
-#ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE
 ////////////////////////////////////////////////////////////////////
 //     Function: DeletedChain::init_lock
 //       Access: Private
@@ -280,9 +240,7 @@ init_lock() {
     do_init_lock(_lock);
   }
 }
-#endif  // DELETED_CHAIN_USE_ATOMIC_EXCHANGE
 
-#ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE
 ////////////////////////////////////////////////////////////////////
 //     Function: DeletedChain::do_init_lock
 //       Access: Private
@@ -299,10 +257,6 @@ void DeletedChain<Type>::
 do_init_lock(MutexImpl *&lock) {
   MutexImpl *new_lock = new MutexImpl;
 
-  // Even though DELETED_CHAIN_USE_ATOMIC_EXCHANGE is not true, we
-  // will take advantage of the atomic exchange operation here, at
-  // startup.  We have to rely on something, after all, before we have
-  // created the first mutex.
   MutexImpl *result;
   result = (MutexImpl *)AtomicAdjust::compare_and_exchange_ptr((void * TVOLATILE &)lock, (void *)NULL, (void *)new_lock);
   
@@ -312,7 +266,6 @@ do_init_lock(MutexImpl *&lock) {
   
   assert(lock != (MutexImpl *)NULL);
 }
-#endif  // DELETED_CHAIN_USE_ATOMIC_EXCHANGE
 
 ////////////////////////////////////////////////////////////////////
 //     Function: StaticDeletedChain::allocate

+ 9 - 16
dtool/src/dtoolbase/deletedChain.h

@@ -28,15 +28,12 @@
 #include "typeHandle.h"
 #include <assert.h>
 
-#ifdef HAVE_ATOMIC_COMPARE_AND_EXCHANGE_PTR
-// Actually, there appears to be a (maybe fatal) flaw in our
-// implementation of DeletedChain via the atomic exchange operation.
-// Specifically, a pointer may be removed from the head of the chain,
-// then the same pointer reinserted in the chain, while another thread
-// is waiting; and that thread will not detect the change.  For now,
-// then, let's not use this implementation, and fall back to the mutex.
-//#define DELETED_CHAIN_USE_ATOMIC_EXCHANGE
-#endif
+// Though it's tempting, it doesn't seem to be possible to implement
+// DeletedChain via the atomic exchange operation.  Specifically, a
+// pointer may be removed from the head of the chain, then the same
+// pointer reinserted in the chain, while another thread is waiting;
+// and that thread will not detect the change.  So instead, we always
+// use a mutex.
 
 #ifndef NDEBUG
 // In development mode, we define USE_DELETEDCHAINFLAG, which
@@ -105,21 +102,17 @@ private:
     // pointer returned (unlike _flag, above).  It's only used when
     // the object is deleted, so there's no harm in sharing space with
     // the undeleted object.
-    TVOLATILE ObjectNode * TVOLATILE _next;
+    ObjectNode *_next;
   };
 
-  static INLINE Type *node_to_type(TVOLATILE ObjectNode *node);
+  static INLINE Type *node_to_type(ObjectNode *node);
   static INLINE ObjectNode *type_to_node(Type *ptr);
 
-  TVOLATILE ObjectNode * TVOLATILE _deleted_chain;
+  ObjectNode *_deleted_chain;
   
-#ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE
-  // If we don't have atomic compare-and-exchange, we need to use a
-  // Mutex to protect the above linked list.
   INLINE void init_lock();
   void do_init_lock(MutexImpl *&lock);
   MutexImpl *_lock;
-#endif
 };
 
 ////////////////////////////////////////////////////////////////////

+ 26 - 122
dtool/src/dtoolbase/dtoolbase.cxx

@@ -17,139 +17,43 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "dtoolbase.h"
+#include "memoryHook.h"
 
 #if defined(USE_TAU) && defined(WIN32)
 // Hack around tau's lack of DLL export declarations for Profiler class.
 bool __tau_shutdown = false;
 #endif
 
+MemoryHook *memory_hook;
 
-/////////////////////////////////////////////////////////////////////
-//
-// Memory manager: DLMALLOC
-//
-// This is Doug Lea's memory manager.  It is very fast,
-// but it is not thread-safe.
-//
-/////////////////////////////////////////////////////////////////////
-
-#if defined(USE_MEMORY_DLMALLOC)
-
-#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
-#error Cannot use dlmalloc library with threading enabled!
-#endif
-
-#define USE_DL_PREFIX 1
-#define NO_MALLINFO 1
-#include "dlmalloc.h"
-
-void *default_operator_new(size_t size) {
-  void *ptr = dlmalloc(size);
-  if (ptr == (void *)NULL) {
-    cerr << "Out of memory!\n";
-    abort();
+////////////////////////////////////////////////////////////////////
+//     Function: init_memory_hook
+//  Description: Any code that might need to use PANDA_MALLOC or
+//               PANDA_FREE, or any methods of the global memory_hook
+//               object, at static init time, should ensure that it
+//               calls init_memory_hook() first to ensure that the
+//               pointer has been properly initialized.  There is no
+//               harm in calling this function more than once.
+//
+//               There is no need to call this function other than at
+//               static init time.
+////////////////////////////////////////////////////////////////////
+void
+init_memory_hook() {
+  if (memory_hook == NULL) {
+    memory_hook = new MemoryHook;
   }
-  return ptr;
-}
-
-void default_operator_delete(void *ptr) {
-  dlfree(ptr);
 }
 
-void default_mark_pointer(void *, size_t, ReferenceCount *) {
-}
-
-void *(*global_operator_new)(size_t size) = &default_operator_new;
-void (*global_operator_delete)(void *ptr) = &default_operator_delete;
-void (*global_mark_pointer)(void *ptr, size_t size, ReferenceCount *ref_ptr) = &default_mark_pointer;
-
-/////////////////////////////////////////////////////////////////////
-//
-// Memory manager: PTMALLOC2
-//
-// Ptmalloc2 is a derivative of Doug Lea's memory manager that was 
-// made thread-safe by Wolfram Gloger, then was ported to windows by
-// Niall Douglas.  It is not quite as fast as dlmalloc (because the
-// thread-safety constructs take a certain amount of CPU time), but
-// it's still much faster than the windows allocator.
-//
-/////////////////////////////////////////////////////////////////////
-
-#elif defined(USE_MEMORY_PTMALLOC2) && !defined(linux)
-// This doesn't appear to work in Linux; perhaps it is clashing with
-// the system library.  On Linux, fall through to the next case
-// instead.
-
-#define USE_DL_PREFIX 1
-#define NO_MALLINFO 1
-#include "ptmalloc2_smp.c"
-
-void *default_operator_new(size_t size) {
-  void *ptr = dlmalloc(size);
-  if (ptr == (void *)NULL) {
-    cerr << "Out of memory!\n";
-    abort();
+// Here's a quick way to ensure the above function is called at least
+// once at static init time.
+class InitMemoryHook {
+public:
+  InitMemoryHook() {
+    init_memory_hook();
   }
-  return ptr;
-}
-
-void default_operator_delete(void *ptr) {
-  dlfree(ptr);
-}
-
-void default_mark_pointer(void *, size_t, ReferenceCount *) {
-}
-
-void *(*global_operator_new)(size_t size) = &default_operator_new;
-void (*global_operator_delete)(void *ptr) = &default_operator_delete;
-void (*global_mark_pointer)(void *ptr, size_t size, ReferenceCount *ref_ptr) = &default_mark_pointer;
-
-/////////////////////////////////////////////////////////////////////
-//
-// Memory manager: MALLOC
-//
-// This option uses the built-in system allocator.  This is a good
-// choice on linux, but it's a terrible choice on windows.
-//
-/////////////////////////////////////////////////////////////////////
-
-#elif defined(USE_MEMORY_MALLOC) || defined(USE_MEMORY_PTMALLOC2)
-
-void *default_operator_new(size_t size) {
-  void *ptr = malloc(size);
-  if (ptr == (void *)NULL) {
-    cerr << "Out of memory!\n";
-    abort();
-  }
-  return ptr;
-}
-
-void default_operator_delete(void *ptr) {
-  free(ptr);
-}
-
-void default_mark_pointer(void *, size_t, ReferenceCount *) {
-}
-
-void *(*global_operator_new)(size_t size) = &default_operator_new;
-void (*global_operator_delete)(void *ptr) = &default_operator_delete;
-void (*global_mark_pointer)(void *ptr, size_t size, ReferenceCount *ref_ptr) = &default_mark_pointer;
-
-/////////////////////////////////////////////////////////////////////
-//
-// Memory manager: NOWRAPPERS
-//
-// Not only do we use the built-in system definitions for new and
-// delete, but we don't even wrap them.  This removes a tiny bit of
-// extra overhead from the above option, but prevents Panda's
-// MemoryUsage class from being able to track memory allocations.
-//
-/////////////////////////////////////////////////////////////////////
-
-#else
-
-#endif  // USE_MEMORY_*
-
+};
+static InitMemoryHook _imh_object;
 
 #if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
 

+ 14 - 52
dtool/src/dtoolbase/dtoolbase_cc.h

@@ -142,59 +142,21 @@ typedef ios::seekdir ios_seekdir;
 // level.
 class ReferenceCount;
 
-// Now redefine global operators new and delete so we can optionally
-// provide custom handlers for them.  The MemoryUsage class in Panda
-// takes advantage of this to track the size of allocated pointers.
+// We need a pointer to a global MemoryHook object, to manage all
+// malloc and free requests from Panda.  See the comments in
+// MemoryHook itself.
+class MemoryHook;
+EXPCL_DTOOL extern MemoryHook *memory_hook;
+EXPCL_DTOOL void init_memory_hook();
+
+// Now redefine some handy macros to hook into the above MemoryHook
+// object.
 #ifndef USE_MEMORY_NOWRAPPERS
-EXPCL_DTOOL void *default_operator_new(size_t size);
-EXPCL_DTOOL void default_operator_delete(void *ptr);
-EXPCL_DTOOL void default_mark_pointer(void *ptr, size_t orig_size,
-                                      ReferenceCount *ref_ptr);
-
-extern EXPCL_DTOOL void *(*global_operator_new)(size_t size);
-extern EXPCL_DTOOL void (*global_operator_delete)(void *ptr);
-extern EXPCL_DTOOL void (*global_mark_pointer)(void *ptr, size_t size,
-                                               ReferenceCount *ref_ptr);
-
-#ifdef REDEFINE_GLOBAL_OPERATOR_NEW
-#ifdef GLOBAL_OPERATOR_NEW_EXCEPTIONS
-
-// Redefinitions of operator new/delete, for O1 - O3 builds (!NDEBUG)
-// only.  These flavors are for modern compilers that expect these
-// function prototypes to handle exceptions.
-INLINE void *operator new(size_t size) throw (std::bad_alloc) {
-  return (*global_operator_new)(size);
-}
-INLINE void *operator new[](size_t size) throw (std::bad_alloc) {
-  return (*global_operator_new)(size);
-}
-
-INLINE void operator delete(void *ptr) throw() {
-  (*global_operator_delete)(ptr);
-}
-INLINE void operator delete[](void *ptr) throw() {
-  (*global_operator_delete)(ptr);
-}
-#else   // GLOBAL_OPERATOR_NEW_EXCEPTIONS
-
-// The same definitions as above, for compilers that don't expect
-// exception handing in global operator new/delete functions.
-INLINE void *operator new(size_t size) {
-  return (*global_operator_new)(size);
-}
-INLINE void *operator new[](size_t size) {
-  return (*global_operator_new)(size);
-}
-
-INLINE void operator delete(void *ptr) {
-  (*global_operator_delete)(ptr);
-}
-INLINE void operator delete[](void *ptr) {
-  (*global_operator_delete)(ptr);
-}
-
-#endif  // GLOBAL_OPERATOR_NEW_EXCEPTIONS
-#endif  // REDEFINE_GLOBAL_OPERATOR_NEW
+#define PANDA_MALLOC(size) (memory_hook->heap_alloc(size))
+#define PANDA_FREE(ptr) memory_hook->heap_free(ptr)
+#else
+#define PANDA_MALLOC(size) malloc(size)
+#define PANDA_FREE(ptr) free(ptr)
 #endif  // USE_MEMORY_NOWRAPPERS
 
 #if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)

+ 1 - 0
dtool/src/dtoolbase/dtoolbase_composite1.cxx

@@ -5,4 +5,5 @@
 #include "atomicAdjustWin32Impl.cxx"
 #include "dtoolbase.cxx"
 #include "memoryBase.cxx"
+#include "memoryHook.cxx"
 #include "mutexDummyImpl.cxx"

+ 5 - 4
dtool/src/dtoolbase/memoryBase.h

@@ -20,6 +20,7 @@
 #define MEMORYBASE_H
 
 #include "dtoolbase.h"
+#include "memoryHook.h"
 
 // Place this macro within a class definition to define appropriate
 // operator new and delete methods that hook into the MemoryInfo class
@@ -31,24 +32,24 @@
 
 #define ALLOC_MEMORY_BASE                                    \
   inline void *operator new(size_t size) {                   \
-    return (*global_operator_new)(size);                     \
+    return PANDA_MALLOC(size);                     \
   }                                                          \
   inline void *operator new(size_t size, void *ptr) {        \
     return ptr;                                              \
   }                                                          \
   inline void operator delete(void *ptr) {                   \
-    (*global_operator_delete)(ptr);                          \
+    PANDA_FREE(ptr);                          \
   }                                                          \
   inline void operator delete(void *ptr, void *) {           \
   }                                                          \
   inline void *operator new[](size_t size) {                 \
-    return (*global_operator_new)(size);                     \
+    return PANDA_MALLOC(size);                     \
   }                                                          \
   inline void *operator new[](size_t size, void *ptr) {      \
     return ptr;                                              \
   }                                                          \
   inline void operator delete[](void *ptr) {                 \
-    (*global_operator_delete)(ptr);                          \
+    PANDA_FREE(ptr);                          \
   }                                                          \
   inline void operator delete[](void *, void *) {            \
   }

+ 75 - 0
dtool/src/dtoolbase/memoryHook.I

@@ -0,0 +1,75 @@
+// Filename: memoryHook.I
+// Created by:  drose (28Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MemoryHook::get_page_size
+//       Access: Public
+//  Description: Returns the operating system page size.  This is the
+//               minimum granularity required for calls to
+//               mmap_alloc().  Also see round_up_to_page_size().
+////////////////////////////////////////////////////////////////////
+INLINE size_t MemoryHook::
+get_page_size() const {
+  return _page_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MemoryHook::round_up_to_page_size
+//       Access: Public
+//  Description: Rounds the indicated size request up to the next
+//               larger multiple of page_size, to qualify it for a
+//               call to mmap_alloc().
+////////////////////////////////////////////////////////////////////
+INLINE size_t MemoryHook::
+round_up_to_page_size(size_t size) const {
+  return  ((size + _page_size - 1) / _page_size) * _page_size;
+}
+
+#ifdef DO_MEMORY_USAGE
+////////////////////////////////////////////////////////////////////
+//     Function: MemoryHook::alloc_to_ptr
+//       Access: Private
+//  Description: Converts an allocated pointer to a pointer returnable
+//               to the application.  Stuffs size in the first n bytes
+//               of the allocated space.
+////////////////////////////////////////////////////////////////////
+INLINE void *MemoryHook::
+alloc_to_ptr(void *alloc, size_t size) {
+  size_t *root = (size_t *)alloc;
+  root[0] = size;
+  return (void *)(root + 1);
+}
+#endif  // DO_MEMORY_USAGE
+
+#ifdef DO_MEMORY_USAGE
+////////////////////////////////////////////////////////////////////
+//     Function: MemoryHook::ptr_to_alloc
+//       Access: Private
+//  Description: Converts an application pointer back to the original
+//               allocated pointer.  Extracts size from the first n
+//               bytes of the allocated space.
+////////////////////////////////////////////////////////////////////
+INLINE void *MemoryHook::
+ptr_to_alloc(void *ptr, size_t &size) {
+  size_t *root = (size_t *)ptr;
+  root -= 1;
+  size = root[0];
+  return (void *)root;
+}
+#endif  // DO_MEMORY_USAGE

+ 294 - 0
dtool/src/dtoolbase/memoryHook.cxx

@@ -0,0 +1,294 @@
+// Filename: memoryHook.cxx
+// Created by:  drose (28Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "memoryHook.h"
+
+#ifdef WIN32
+
+// Windows case.
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#else
+
+// Posix case.
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#endif  // WIN32
+
+
+#if defined(USE_MEMORY_DLMALLOC)
+
+/////////////////////////////////////////////////////////////////////
+//
+// Memory manager: DLMALLOC
+//
+// This is Doug Lea's memory manager.  It is very fast,
+// but it is not thread-safe.
+//
+/////////////////////////////////////////////////////////////////////
+
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+#error Cannot use dlmalloc library with threading enabled!
+#endif
+
+#define USE_DL_PREFIX 1
+#define NO_MALLINFO 1
+#include "dlmalloc.h"
+
+#define call_malloc dlmalloc
+#define call_free dlfree
+
+#elif defined(USE_MEMORY_PTMALLOC2) && !defined(linux)
+// This doesn't appear to work in Linux; perhaps it is clashing with
+// the system library.  On Linux, fall through to the next case
+// instead.
+
+/////////////////////////////////////////////////////////////////////
+//
+// Memory manager: PTMALLOC2
+//
+// Ptmalloc2 is a derivative of Doug Lea's memory manager that was 
+// made thread-safe by Wolfram Gloger, then was ported to windows by
+// Niall Douglas.  It is not quite as fast as dlmalloc (because the
+// thread-safety constructs take a certain amount of CPU time), but
+// it's still much faster than the windows allocator.
+//
+/////////////////////////////////////////////////////////////////////
+
+#define USE_DL_PREFIX 1
+#define NO_MALLINFO 1
+#include "ptmalloc2_smp.c"
+
+#define call_malloc dlmalloc
+#define call_free dlfree
+
+#else
+
+/////////////////////////////////////////////////////////////////////
+//
+// Memory manager: MALLOC
+//
+// This option uses the built-in system allocator.  This is a good
+// choice on linux, but it's a terrible choice on windows.
+//
+/////////////////////////////////////////////////////////////////////
+
+#define call_malloc malloc
+#define call_free free
+
+#endif  // USE_MEMORY_*
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MemoryHook::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+MemoryHook::
+MemoryHook() {
+#ifdef WIN32
+
+  // Windows case.
+  SYSTEM_INFO sysinfo;
+  GetSystemInfo(&sysinfo);
+
+  _page_size = (size_t)sysinfo.dwPageSize;
+
+#else
+
+  // Posix case.
+  _page_size = getpagesize();
+
+#endif  // WIN32
+
+#ifdef DO_MEMORY_USAGE
+  _total_heap_size = 0;
+  _total_mmap_size = 0;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MemoryHook::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+MemoryHook::
+MemoryHook(const MemoryHook &copy) :
+  _page_size(copy._page_size)
+{
+#ifdef DO_MEMORY_USAGE
+  _total_heap_size = copy._total_heap_size;
+  _total_mmap_size = copy._total_mmap_size;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MemoryHook::heap_alloc
+//       Access: Public, Virtual
+//  Description: Allocates a block of memory from the heap, similar to
+//               malloc().  This will never return NULL; it will abort
+//               instead if memory is not available.
+////////////////////////////////////////////////////////////////////
+void *MemoryHook::
+heap_alloc(size_t size) {
+  void *ptr;
+
+#ifdef DO_MEMORY_USAGE
+  // In the DO_MEMORY_USAGE case, we want to track the total size of
+  // allocated bytes on the heap.
+  _total_heap_size += size;
+  ptr = alloc_to_ptr(call_malloc(size + sizeof(size)), size);
+#else
+  ptr = call_malloc(size);
+#endif  // DO_MEMORY_USAGE
+
+  if (ptr == (void *)NULL) {
+    cerr << "Out of memory!\n";
+    abort();
+  }
+  return ptr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MemoryHook::heap_free
+//       Access: Public, Virtual
+//  Description: Releases a block of memory previously allocated via
+//               heap_alloc.
+////////////////////////////////////////////////////////////////////
+void MemoryHook::
+heap_free(void *ptr) {
+
+#ifdef DO_MEMORY_USAGE
+  // In the DO_MEMORY_USAGE case, we want to track the total size of
+  // allocated bytes on the heap.
+  size_t size;
+  ptr = ptr_to_alloc(ptr, size);
+  assert(size <= _total_heap_size);
+  _total_heap_size -= size;
+
+#endif  // DO_MEMORY_USAGE
+
+  call_free(ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MemoryHook::mmap_alloc
+//       Access: Public, Virtual
+//  Description: Allocates a raw page or pages of memory directly from
+//               the OS.  This will be in a different address space
+//               from the memory allocated by heap_alloc(), and so it
+//               won't contribute to fragmentation of that memory.
+//
+//               The allocation size must be an integer multiple of
+//               the page size.  Use round_to_page_size() if there is
+//               any doubt.
+//
+//               If allow_exec is true, the memory will be flagged so
+//               that it is legal to execute code that has been
+//               written to this memory.
+////////////////////////////////////////////////////////////////////
+void *MemoryHook::
+mmap_alloc(size_t size, bool allow_exec) {
+  assert((size % _page_size) == 0);
+
+#ifdef DO_MEMORY_USAGE
+  _total_mmap_size += size;
+#endif
+
+#ifdef WIN32
+
+  // Windows case.
+  void *ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE,
+                           allow_exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
+  if (ptr == (void *)NULL) {
+    DWORD err = GetLastError();
+    cerr << "Couldn't allocate memory page of size " << needed_size
+         << ": ";
+
+    PVOID buffer;
+    DWORD length = 
+      FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+                    NULL, err, 0, (LPTSTR)&buffer, 0, NULL);
+    if (length != 0) {
+      cerr << (char *)buffer << "\n";
+    } else {
+      cerr << "Error code " << err << "\n";
+    }
+    LocalFree(buffer);
+    abort();
+  }
+
+  return ptr;
+
+#else
+
+  // Posix case.
+  int prot = PROT_READ | PROT_WRITE;
+  if (allow_exec) {
+    prot |= PROT_EXEC;
+  }
+  void *ptr = mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
+  if (ptr == (void *)-1) {
+    perror("mmap");
+    abort();
+  }
+
+  return ptr;
+
+#endif  // WIN32
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MemoryHook::mmap_free
+//       Access: Public, Virtual
+//  Description: Frees a block of memory previously allocated via
+//               mmap_alloc().  You must know how large the block was.
+////////////////////////////////////////////////////////////////////
+void MemoryHook::
+mmap_free(void *ptr, size_t size) {
+  assert((size % _page_size) == 0);
+
+#ifdef DO_MEMORY_USAGE
+  assert(size <= _total_mmap_size);
+  _total_mmap_size -= size;
+#endif
+
+#ifdef WIN32
+  VirtualFree(ptr, 0, MEM_RELEASE);
+#else  
+  munmap(ptr, size);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MemoryHook::mark_pointer
+//       Access: Public, Virtual
+//  Description: This special method exists only to provide a callback
+//               hook into MemoryUsage.  It indicates that the
+//               indicated pointer, allocated from somewhere other
+//               than a call to heap_alloc(), now contains a pointer
+//               to the indicated ReferenceCount object.  If orig_size
+//               is 0, it indicates that the ReferenceCount object has
+//               been destroyed.
+////////////////////////////////////////////////////////////////////
+void MemoryHook::
+mark_pointer(void *, size_t, ReferenceCount *) {
+}

+ 62 - 0
dtool/src/dtoolbase/memoryHook.h

@@ -0,0 +1,62 @@
+// Filename: memoryHook.h
+// Created by:  drose (28Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef MEMORYHOOK_H
+#define MEMORYHOOK_H
+
+#include "dtoolbase.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : MemoryHook
+// Description : This class is provided to allow the MemoryUsage class
+//               in Panda to insert callback hooks to track the size
+//               of allocated pointers.  Every low-level alloc and
+//               free operation (except in production builds) should
+//               vector through this class to facilitate that.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOL MemoryHook {
+public:
+  MemoryHook();
+  MemoryHook(const MemoryHook &copy);
+
+  virtual void *heap_alloc(size_t size);
+  virtual void heap_free(void *ptr);
+
+  virtual void *mmap_alloc(size_t size, bool allow_exec);
+  virtual void mmap_free(void *ptr, size_t size);
+  INLINE size_t get_page_size() const;
+  INLINE size_t round_up_to_page_size(size_t size) const;
+
+  virtual void mark_pointer(void *ptr, size_t orig_size, ReferenceCount *ref_ptr);
+
+private:
+  size_t _page_size;
+
+#ifdef DO_MEMORY_USAGE
+  INLINE static void *alloc_to_ptr(void *alloc, size_t size);
+  INLINE static void *ptr_to_alloc(void *ptr, size_t &size);
+
+protected:
+  size_t _total_heap_size;
+  size_t _total_mmap_size;
+#endif
+};
+
+#include "memoryHook.I"
+
+#endif

+ 3 - 73
dtool/src/dtoolbase/neverFreeMemory.cxx

@@ -18,21 +18,7 @@
 
 #include "neverFreeMemory.h"
 #include "atomicAdjust.h"
-
-#ifdef WIN32
-
-// Windows case.
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-#else
-
-// Posix case.
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-
-#endif  // WIN32
+#include "memoryHook.h"
 
 NeverFreeMemory * TVOLATILE NeverFreeMemory::_global_ptr;
 
@@ -52,23 +38,6 @@ NeverFreeMemory::
 NeverFreeMemory() {
   _total_alloc = 0;
   _total_used = 0;
-
-  _page_size = 1;
-
-#ifdef WIN32
-
-  // Windows case.
-  SYSTEM_INFO sysinfo;
-  GetSystemInfo(&sysinfo);
-
-  _page_size = (size_t)sysinfo.dwPageSize;
-
-#else
-
-  // Posix case.
-  _page_size = getpagesize();
-
-#endif  // WIN32
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -100,47 +69,8 @@ ns_alloc(size_t size) {
   // We have to allocate a new page.  Allocate at least min_page_size
   // bytes, and then round that up to the next _page_size bytes.
   size_t needed_size = max(size, min_page_size);
-  needed_size = ((needed_size + _page_size - 1) / _page_size) * _page_size;
-  void *start = NULL;
-
-#ifdef WIN32
-
-  // Windows case.
-  start = VirtualAlloc(NULL, needed_size, MEM_COMMIT | MEM_RESERVE,
-                       PAGE_READWRITE);
-  if (start == (void *)NULL) {
-    DWORD err = GetLastError();
-    cerr << "Couldn't allocate memory page of size " << needed_size
-         << ": ";
-
-    PVOID buffer;
-    DWORD length = 
-      FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-                    NULL, err, 0, (LPTSTR)&buffer, 0, NULL);
-    if (length != 0) {
-      cerr << (char *)buffer << "\n";
-    } else {
-      cerr << "Error code " << err << "\n";
-    }
-    LocalFree(buffer);
-  }
-
-#else
-
-  // Posix case.
-  start = mmap(NULL, needed_size, PROT_READ | PROT_WRITE, 
-               MAP_PRIVATE | MAP_ANON, -1, 0);
-  if (start == (void *)-1) {
-    perror("mmap");
-    start = NULL;
-  }
-
-#endif  // WIN32
-
-  if (start == NULL) {
-    start = malloc(needed_size);
-  }
-
+  needed_size = memory_hook->round_up_to_page_size(needed_size);
+  void *start = memory_hook->mmap_alloc(needed_size, false);
   _total_alloc += needed_size;
 
   Page page(start, needed_size);

+ 0 - 1
dtool/src/dtoolbase/neverFreeMemory.h

@@ -69,7 +69,6 @@ private:
   typedef set<Page> Pages;
   Pages _pages;
 
-  size_t _page_size;
   size_t _total_alloc;
   size_t _total_used;
   MutexImpl _lock;

+ 2 - 2
dtool/src/dtoolbase/pallocator.T

@@ -55,7 +55,7 @@ allocate(TYPENAME pallocator_array<Type>::size_type n, TYPENAME allocator<void>:
   // We also need to store the total number of bytes we allocated.
   alloc_size += sizeof(size_t);
   _type_handle.inc_memory_usage(TypeHandle::MC_array, (int)alloc_size);
-  void *ptr = (TYPENAME pallocator_array<Type>::pointer)(*global_operator_new)(alloc_size);
+  void *ptr = (TYPENAME pallocator_array<Type>::pointer)PANDA_MALLOC(alloc_size);
   *((size_t *)ptr) = alloc_size;
   return (TYPENAME pallocator_array<Type>::pointer)(((size_t *)ptr) + 1);
 #else
@@ -71,7 +71,7 @@ deallocate(TYPENAME pallocator_array<Type>::pointer p, TYPENAME pallocator_array
   // Now we need to recover the total number of bytes.
   size_t alloc_size = *(((size_t *)p) - 1);
   _type_handle.dec_memory_usage(TypeHandle::MC_array, (int)alloc_size);
-  (*global_operator_delete)(((size_t *)p) - 1);
+  PANDA_FREE(((size_t *)p) - 1);
 #else
   free(p);
 #endif  // DO_MEMORY_USAGE

+ 2 - 1
dtool/src/dtoolbase/pallocator.h

@@ -21,6 +21,7 @@
 
 #include <memory>
 #include "dtoolbase.h"
+#include "memoryHook.h"
 #include "deletedChain.h"
 #include "typeHandle.h"
 
@@ -60,7 +61,7 @@ public:
   typedef TYPENAME allocator<Type>::const_reference const_reference;
   typedef TYPENAME allocator<Type>::size_type size_type;
 
-  INLINE pallocator_single(TypeHandle type_handle = TypeHandle::none()) throw();
+  INLINE pallocator_single(TypeHandle type_handle) throw();
 
   // template member functions in VC++ can only be defined in-class.
   template<class U>

+ 4 - 0
dtool/src/dtoolbase/typeHandle.cxx

@@ -59,6 +59,7 @@ inc_memory_usage(MemoryClass memory_class, int size) {
     TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL);
     assert(rnode != (TypeRegistryNode *)NULL);
     AtomicAdjust::add(rnode->_memory_usage[memory_class], (PN_int32)size);
+    //    cerr << *this << ".inc(" << memory_class << ", " << size << ") -> " << rnode->_memory_usage[memory_class] << "\n";
     assert(rnode->_memory_usage[memory_class] >= 0);
   }
 }
@@ -78,6 +79,9 @@ dec_memory_usage(MemoryClass memory_class, int size) {
     TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL);
     assert(rnode != (TypeRegistryNode *)NULL);
     AtomicAdjust::add(rnode->_memory_usage[memory_class], -(PN_int32)size);
+    if (rnode->_memory_usage[memory_class] < 0) {
+      cerr << *this << ".dec(" << memory_class << ", " << size << ") -> " << rnode->_memory_usage[memory_class] << "\n";
+    }
     assert(rnode->_memory_usage[memory_class] >= 0);
   }
 }

+ 1 - 0
dtool/src/prc/configVariableManager.cxx

@@ -35,6 +35,7 @@ ConfigVariableManager *ConfigVariableManager::_global_ptr = NULL;
 ////////////////////////////////////////////////////////////////////
 ConfigVariableManager::
 ConfigVariableManager() {
+  init_memory_hook();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 0
dtool/src/prc/notify.cxx

@@ -380,6 +380,7 @@ write_string(const string &str) {
 Notify *Notify::
 ptr() {
   if (_global_ptr == (Notify *)NULL) {
+    init_memory_hook();
     _global_ptr = new Notify;
   }
   return _global_ptr;

+ 2 - 1
dtool/src/prc/notifyCategory.h

@@ -24,6 +24,7 @@
 #include "notifySeverity.h"
 #include "configVariableEnum.h"
 #include "configFlags.h"
+#include "memoryBase.h"
 
 #include <vector>
 
@@ -36,7 +37,7 @@
 //               created within a package if a finer grain of control
 //               is required.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_DTOOLCONFIG NotifyCategory : public ConfigFlags {
+class EXPCL_DTOOLCONFIG NotifyCategory : public MemoryBase, public ConfigFlags {
 private:
   NotifyCategory(const string &fullname, const string &basename,
                  NotifyCategory *parent);

+ 0 - 1
dtool/src/prc/pnotify.h

@@ -20,7 +20,6 @@
 #define NOTIFY_H
 
 #include "dtoolbase.h"
-
 #include "notifySeverity.h"
 #include <map>
 

+ 1 - 0
panda/src/egg/eggNode.h

@@ -143,6 +143,7 @@ public:
     EggNamedObject::init_type();
     register_type(_type_handle, "EggNode",
                   EggNamedObject::get_class_type());
+    MatrixFrame::init_type();
   }
   virtual TypeHandle get_type() const {
     return get_class_type();

+ 50 - 57
panda/src/express/memoryUsage.I

@@ -109,61 +109,46 @@ is_counting() {
 //     Function: MemoryUsage::get_current_cpp_size
 //       Access: Public, Static
 //  Description: Returns the total number of bytes of allocated memory
-//               via the C++ operators new and delete as counted,
-//               not including the memory previously frozen.
+//               consumed by C++ objects, not including the memory
+//               previously frozen.
 ////////////////////////////////////////////////////////////////////
 INLINE size_t MemoryUsage::
 get_current_cpp_size() {
-  return get_global_ptr()->ns_get_current_cpp_size();
+  return get_global_ptr()->_current_cpp_size;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: MemoryUsage::has_cpp_size
+//     Function: MemoryUsage::get_total_cpp_size
 //       Access: Public, Static
-//  Description: Returns true if the value returned by
-//               get_cpp_size() is meaningful on this particular
-//               system with this particular configuration, false
-//               otherwise.
+//  Description: Returns the total number of bytes of allocated memory
+//               consumed by C++ objects, including the memory
+//               previously frozen.
 ////////////////////////////////////////////////////////////////////
-INLINE bool MemoryUsage::
-has_cpp_size() {
-#if defined(WIN32_VC) && defined(_DEBUG)
-  // Windows in debug mode can count C++ size without having to do a
-  // full track.
-  return is_counting();
-#else
-  // Other systems require the full hammer.
-  return is_tracking();
-#endif
+INLINE size_t MemoryUsage::
+get_total_cpp_size() {
+  return get_global_ptr()->_total_cpp_size;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: MemoryUsage::get_cpp_size
+//     Function: MemoryUsage::get_panda_heap_size
 //       Access: Public, Static
-//  Description: Returns the total number of bytes of allocated memory
-//               via the C++ operators new and delete as counted,
-//               including the memory previously frozen.
+//  Description: Returns the total number of bytes allocated from the
+//               heap from code within Panda.
 ////////////////////////////////////////////////////////////////////
 INLINE size_t MemoryUsage::
-get_cpp_size() {
-  return get_global_ptr()->ns_get_cpp_size();
+get_panda_heap_size() {
+  return get_global_ptr()->_total_heap_size;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: MemoryUsage::has_interpreter_size
+//     Function: MemoryUsage::get_panda_mmap_size
 //       Access: Public, Static
-//  Description: Returns true if the value returned by
-//               get_interpreter_size() is meaningful on this particular
-//               system with this particular configuration, false
-//               otherwise.
+//  Description: Returns the total number of bytes allocated from the
+//               virtual memory pool from code within Panda.
 ////////////////////////////////////////////////////////////////////
-INLINE bool MemoryUsage::
-has_interpreter_size() {
-#ifdef TRACK_IN_INTERPRETER
-  return is_counting();
-#else
-  return false;
-#endif
+INLINE size_t MemoryUsage::
+get_panda_mmap_size() {
+  return get_global_ptr()->_total_mmap_size;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -174,43 +159,51 @@ has_interpreter_size() {
 //               number is only meaningful if both Panda and the
 //               high-level language are single-threaded, and running
 //               in the same thread.
+//
+//               This number is only available if Panda is able to
+//               hook into the actual heap callback.
 ////////////////////////////////////////////////////////////////////
 INLINE size_t MemoryUsage::
 get_interpreter_size() {
-  return get_global_ptr()->ns_get_interpreter_size();
+  return get_global_ptr()->_interpreter_size;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: MemoryUsage::has_total_size
+//     Function: MemoryUsage::get_external_size
 //       Access: Public, Static
-//  Description: Returns true if the value returned by
-//               get_total_size() is meaningful on this particular
-//               system with this particular configuration, false
-//               otherwise.
+//  Description: Returns the total number of bytes of allocated memory
+//               in the heap that Panda didn't seem to be responsible
+//               for.  This includes a few bytes for very low-level
+//               objects (like ConfigVariables) that cannot use Panda
+//               memory tracking because they are so very low-level.
+//
+//               This number is only available if Panda is able to
+//               hook into the actual heap callback.
 ////////////////////////////////////////////////////////////////////
-INLINE bool MemoryUsage::
-has_total_size() {
-#if defined(WIN32_VC) && defined(_DEBUG)
-  // Windows in debug mode can count total size without having to do a
-  // full track.
-  return is_counting();
-#else
-  // Other systems require the full hammer.
-  return is_tracking();
-#endif
+INLINE size_t MemoryUsage::
+get_external_size() {
+  MemoryUsage *mu = get_global_ptr();
+  if (mu->_count_memory_usage) {
+    return mu->_total_size - mu->_total_heap_size;
+  } else {
+    return 0;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: MemoryUsage::get_total_size
 //       Access: Public, Static
-//  Description: Returns the total size of the dynamic heap, as nearly
-//               as can be determined, including all allocated memory
-//               if possible, in addition to that tracked by
-//               get_cpp_size().
+//  Description: Returns the total size of allocated memory consumed
+//               by the process, as nearly as can be determined.
 ////////////////////////////////////////////////////////////////////
 INLINE size_t MemoryUsage::
 get_total_size() {
-  return get_global_ptr()->ns_get_total_size();
+  MemoryUsage *mu = get_global_ptr();
+  if (mu->_count_memory_usage) {
+    return mu->_total_size;
+  } else {
+    return mu->_total_heap_size;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 87 - 184
panda/src/express/memoryUsage.cxx

@@ -33,11 +33,7 @@
 #include "config_express.h"
 #include <algorithm>
 
-MemoryUsage *MemoryUsage::_global_ptr = (MemoryUsage *)NULL;
-
-// This flag is set true in is_counting() mode to indicate that the
-// malloc operation is coming from C++ operator new or delete.
-bool MemoryUsage::_is_cpp_operator = false;
+MemoryUsage *MemoryUsage::_global_ptr;
 
 // This flag is used to protect the operator new/delete handlers
 // against recursive entry.
@@ -90,7 +86,7 @@ void MemoryUsage::TypeHistogram::
 show() const {
   // First, copy the relevant information to a vector so we can sort
   // by counts.  Don't use a pvector.
-  typedef vector<TypeHistogramCountSorter, dallocator<TypeHistogramCountSorter> > CountSorter;
+  typedef vector<TypeHistogramCountSorter> CountSorter;
   CountSorter count_sorter;
   Counts::const_iterator ci;
   for (ci = _counts.begin(); ci != _counts.end(); ++ci) {
@@ -191,20 +187,18 @@ choose_bucket(double age) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: MemoryUsage::operator_new_handler
-//       Access: Public, Static
-//  Description: This is set up as a global handler function (by
-//               redefining a function pointer in Dtool) for the
-//               operator new function.  If track-memory-usage is
-//               enabled, this function will be called whenever any
-//               new operator within the Panda source is invoked.
+//     Function: MemoryUsage::heap_alloc
+//       Access: Public, Virtual
+//  Description: Allocates a block of memory from the heap, similar to
+//               malloc().  This will never return NULL; it will abort
+//               instead if memory is not available.
 ////////////////////////////////////////////////////////////////////
 void *MemoryUsage::
-operator_new_handler(size_t size) {
+heap_alloc(size_t size) {
   void *ptr;
 
   if (_recursion_protect) {
-    ptr = default_operator_new(size);
+    ptr = MemoryHook::heap_alloc(size);
     if (express_cat.is_spam()) {
       express_cat.spam()
         << "Allocating pointer " << (void *)ptr
@@ -212,9 +206,8 @@ operator_new_handler(size_t size) {
     }
 
   } else {
-    MemoryUsage *mu = get_global_ptr();
-    if (mu->_track_memory_usage) {
-      ptr = default_operator_new(size);
+    if (_track_memory_usage) {
+      ptr = MemoryHook::heap_alloc(size);
       /*
       if (express_cat.is_spam()) {
         express_cat.spam()
@@ -226,9 +219,7 @@ operator_new_handler(size_t size) {
       get_global_ptr()->ns_record_void_pointer(ptr, size);
 
     } else {
-      _is_cpp_operator = true;
-      ptr = default_operator_new(size);
-      _is_cpp_operator = false;
+      ptr = MemoryHook::heap_alloc(size);
     }
   }
 
@@ -236,58 +227,98 @@ operator_new_handler(size_t size) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: MemoryUsage::operator_delete_handler
-//       Access: Public, Static
-//  Description: This is set up as a global handler function (by
-//               redefining a function pointer in Dtool) for the
-//               operator delete function.  If track-memory-usage is
-//               enabled, this function will be called whenever any
-//               delete operator within the Panda source is invoked.
+//     Function: MemoryUsage::heap_free
+//       Access: Public, Virtual
+//  Description: Releases a block of memory previously allocated via
+//               heap_alloc.
 ////////////////////////////////////////////////////////////////////
 void MemoryUsage::
-operator_delete_handler(void *ptr) {
+heap_free(void *ptr) {
   if (_recursion_protect) {
     if (express_cat.is_spam()) {
       express_cat.spam()
         << "Deleting pointer " << (void *)ptr
         << " during recursion protect.\n";
     }
-    default_operator_delete(ptr);
+    MemoryHook::heap_free(ptr);
 
   } else {
-    MemoryUsage *mu = get_global_ptr();
-    if (mu->_track_memory_usage) {
+    if (_track_memory_usage) {
       /*
       if (express_cat.is_spam()) {
         express_cat.spam()
           << "Removing pointer " << (void *)ptr << "\n";
       }
       */
-      mu->ns_remove_void_pointer(ptr);
-      default_operator_delete(ptr);
+      ns_remove_void_pointer(ptr);
+      MemoryHook::heap_free(ptr);
     } else {
-      _is_cpp_operator = true;
-      default_operator_delete(ptr);
-      _is_cpp_operator = false;
+      MemoryHook::heap_free(ptr);
     }
   }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: MemoryUsage::mark_pointer_handler
-//       Access: Public, Static
-//  Description: This special function is similar to operator_new_handler,
-//               but instead of allocating new memory it simply marks
-//               existing memory as already having been allocated.
+//     Function: MemoryUsage::mark_pointer
+//       Access: Public, Virtual
+//  Description: This special method exists only to provide a callback
+//               hook into MemoryUsage.  It indicates that the
+//               indicated pointer, allocated from somewhere other
+//               than a call to heap_alloc(), now contains a pointer
+//               to the indicated ReferenceCount object.  If orig_size
+//               is 0, it indicates that the ReferenceCount object has
+//               been destroyed.
 ////////////////////////////////////////////////////////////////////
 void MemoryUsage::
-mark_pointer_handler(void *ptr, size_t size, 
-                     ReferenceCount *ref_ptr) {
-  if (!_recursion_protect) {
-    MemoryUsage *mu = get_global_ptr();
-    if (mu->_track_memory_usage) {
-      mu->ns_mark_pointer(ptr, size, ref_ptr);
+mark_pointer(void *ptr, size_t size, ReferenceCount *ref_ptr) {
+  if (_recursion_protect || !_track_memory_usage) {
+    return;
+  }
+
+  if (express_cat.is_spam()) {
+    express_cat.spam()
+      << "Marking pointer " << ptr << ", size " << size 
+      << ", ref_ptr = " << ref_ptr << "\n";
+  }
+
+  if (size != 0) {
+    // We're recording this pointer as now in use.
+    ns_record_void_pointer(ptr, size);
+
+    if (ref_ptr != (ReferenceCount *)NULL) {
+      // Make the pointer typed.  This is particularly necessary in
+      // case the ref_ptr is a different value than the base void
+      // pointer; this may be our only opportunity to associate the
+      // two pointers.
+      Table::iterator ti;
+      ti = _table.find(ptr);
+      nassertv(ti != _table.end());
+      MemoryInfo *info = (*ti).second;
+
+      info->_ref_ptr = ref_ptr;
+      info->_static_type = ReferenceCount::get_class_type();
+      info->_dynamic_type = ReferenceCount::get_class_type();
+      info->_flags |= MemoryInfo::F_reconsider_dynamic_type;
+      
+      if (ref_ptr != ptr) {
+        _recursion_protect = true;
+        
+        pair<Table::iterator, bool> insert_result =
+          _table.insert(Table::value_type((void *)ref_ptr, info));
+        assert(insert_result.first != _table.end());
+        if (!insert_result.second) {
+          express_cat.warning()
+            << "Attempt to mark pointer " << ptr << " as ReferenceCount "
+            << ref_ptr << ", which was already allocated.\n";
+        }
+        
+        _recursion_protect = false;
+      }
     }
+
+  } else {
+    // We're removing this pointer from use.
+    ns_remove_void_pointer(ptr);
   }
 }
 
@@ -324,13 +355,6 @@ win32_malloc_hook(int alloc_type, void *ptr,
       
       mu->_total_size += increment;
 
-      /*
-        This isn't working reliably right now.
-      if (_is_cpp_operator) {
-        mu->_cpp_size += increment;
-      }
-      */
-
 #ifdef TRACK_IN_INTERPRETER
       if (in_interpreter) {
         mu->_interpreter_size += increment;
@@ -351,7 +375,7 @@ win32_malloc_hook(int alloc_type, void *ptr,
 //  Description:
 ////////////////////////////////////////////////////////////////////
 MemoryUsage::
-MemoryUsage() {
+MemoryUsage(const MemoryHook &copy) : MemoryHook(copy) {
   // We must get these variables here instead of in
   // config_express.cxx, because we need to know it at static init
   // time, and who knows when the code in config_express will be
@@ -378,20 +402,8 @@ MemoryUsage() {
 #error Cannot compile MemoryUsage without malloc wrappers!
 #endif
 
-  if (_track_memory_usage) {
-    // Redefine the global pointers for operator new and operator
-    // delete (these pointers are defined up in DTOOL) to vector into
-    // this class.
-    global_operator_new = &operator_new_handler;
-    global_operator_delete = &operator_delete_handler;
-    global_mark_pointer = &mark_pointer_handler;
-  }
-
 #if defined(WIN32_VC) && defined(_DEBUG)
   if (_count_memory_usage) {
-    global_operator_new = &operator_new_handler;
-    global_operator_delete = &operator_delete_handler;
-    global_mark_pointer = &mark_pointer_handler;
     _CrtSetAllocHook(&win32_malloc_hook);
   }
 #endif
@@ -400,7 +412,7 @@ MemoryUsage() {
   _freeze_index = 0;
   _count = 0;
   _current_cpp_size = 0;
-  _cpp_size = 0;
+  _total_cpp_size = 0;
   _interpreter_size = 0;
   _total_size = 0;
 }
@@ -414,7 +426,9 @@ MemoryUsage() {
 MemoryUsage *MemoryUsage::
 get_global_ptr() {
   if (_global_ptr == (MemoryUsage *)NULL) {
-    _global_ptr = new MemoryUsage;
+    init_memory_hook();
+    _global_ptr = new MemoryUsage(*memory_hook);
+    memory_hook = _global_ptr;
   }
 
   return _global_ptr;
@@ -586,7 +600,7 @@ ns_remove_pointer(ReferenceCount *ptr) {
 
       if (info->_void_ptr == NULL) {
         // That was the last entry.  Remove it altogether.
-        _cpp_size -= info->_size;
+        _total_cpp_size -= info->_size;
         if (info->_freeze_index == _freeze_index) {
           _current_cpp_size -= info->_size;
           _count--;
@@ -643,7 +657,7 @@ ns_record_void_pointer(void *ptr, size_t size) {
     } else {
       _current_cpp_size += size;
     }
-    _cpp_size += size - info->_size;
+    _total_cpp_size += size - info->_size;
 
     info->_void_ptr = ptr;
     info->_size = size;
@@ -713,7 +727,7 @@ ns_remove_void_pointer(void *ptr) {
     _table.erase(ti);
     _recursion_protect = false;
 
-    _cpp_size -= info->_size;
+    _total_cpp_size -= info->_size;
     if (info->_freeze_index == _freeze_index) {
       --_count;
       _current_cpp_size -= info->_size;
@@ -724,117 +738,6 @@ ns_remove_void_pointer(void *ptr) {
   }
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: MemoryUsage::ns_mark_pointer
-//       Access: Private
-//  Description: 
-////////////////////////////////////////////////////////////////////
-void MemoryUsage::
-ns_mark_pointer(void *ptr, size_t size, ReferenceCount *ref_ptr) {
-  if (express_cat.is_spam()) {
-    express_cat.spam()
-      << "Marking pointer " << ptr << ", size " << size 
-      << ", ref_ptr = " << ref_ptr << "\n";
-  }
-
-  if (size != 0) {
-    // We're recording this pointer as now in use.
-    ns_record_void_pointer(ptr, size);
-
-    if (ref_ptr != (ReferenceCount *)NULL) {
-      // Make the pointer typed.  This is particularly necessary in
-      // case the ref_ptr is a different value than the base void
-      // pointer; this may be our only opportunity to associate the
-      // two pointers.
-      Table::iterator ti;
-      ti = _table.find(ptr);
-      nassertv(ti != _table.end());
-      MemoryInfo *info = (*ti).second;
-
-      info->_ref_ptr = ref_ptr;
-      info->_static_type = ReferenceCount::get_class_type();
-      info->_dynamic_type = ReferenceCount::get_class_type();
-      info->_flags |= MemoryInfo::F_reconsider_dynamic_type;
-      
-      if (ref_ptr != ptr) {
-        _recursion_protect = true;
-        
-        pair<Table::iterator, bool> insert_result =
-          _table.insert(Table::value_type((void *)ref_ptr, info));
-        assert(insert_result.first != _table.end());
-        if (!insert_result.second) {
-          express_cat.warning()
-            << "Attempt to mark pointer " << ptr << " as ReferenceCount "
-            << ref_ptr << ", which was already allocated.\n";
-        }
-        
-        _recursion_protect = false;
-      }
-    }
-
-  } else {
-    // We're removing this pointer from use.
-    ns_remove_void_pointer(ptr);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MemoryUsage::ns_get_current_cpp_size
-//       Access: Private
-//  Description: Returns the total number of bytes of allocated memory
-//               via the C++ operators new and delete as counted,
-//               not including the memory previously frozen.
-////////////////////////////////////////////////////////////////////
-size_t MemoryUsage::
-ns_get_current_cpp_size() {
-  return _current_cpp_size;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MemoryUsage::ns_get_cpp_size
-//       Access: Private
-//  Description: Returns the total number of bytes of allocated memory
-//               via the C++ operators new and delete as counted,
-//               including the memory previously frozen.
-////////////////////////////////////////////////////////////////////
-size_t MemoryUsage::
-ns_get_cpp_size() {
-  return _cpp_size;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MemoryUsage::ns_get_interpreter_size
-//       Access: Private
-//  Description: Returns the total number of bytes of allocated memory
-//               while the high-level languange code is running.  This
-//               number is only meaningful if both Panda and the
-//               high-level language are single-threaded, and running
-//               in the same thread.
-////////////////////////////////////////////////////////////////////
-size_t MemoryUsage::
-ns_get_interpreter_size() {
-  return _interpreter_size;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MemoryUsage::ns_get_total_size
-//       Access: Private
-//  Description: Returns the total size of the dynamic heap, as nearly
-//               as can be determined, including all allocated memory
-//               if possible, in addition to that tracked by
-//               get_cpp_size().
-////////////////////////////////////////////////////////////////////
-size_t MemoryUsage::
-ns_get_total_size() {
-#if defined(WIN32_VC) && defined(_DEBUG)
-  return _total_size;
-#else
-  // If we aren't tracking _total_size, report _cpp_size as the next
-  // best thing.
-  return _cpp_size;
-#endif
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: MemoryUsage::ns_get_num_pointers
 //       Access: Private

+ 17 - 23
panda/src/express/memoryUsage.h

@@ -27,7 +27,7 @@
 #include "memoryInfo.h"
 #include "memoryUsagePointerCounts.h"
 #include "pmap.h"
-#include "dallocator.h"
+#include "memoryHook.h"
 
 class ReferenceCount;
 class MemoryUsagePointers;
@@ -42,7 +42,7 @@ class MemoryUsagePointers;
 //               When compiled with NDEBUG set, this entire class does
 //               nothing and compiles to nothing.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDAEXPRESS MemoryUsage {
+class EXPCL_PANDAEXPRESS MemoryUsage : public MemoryHook {
 public:
   INLINE static bool get_track_memory_usage();
 
@@ -52,10 +52,9 @@ public:
   INLINE static void remove_pointer(ReferenceCount *ptr);
 
 public:
-  static void *operator_new_handler(size_t size);
-  static void operator_delete_handler(void *ptr);
-  static void mark_pointer_handler(void *ptr, size_t size, 
-                                   ReferenceCount *ref_ptr);
+  virtual void *heap_alloc(size_t size);
+  virtual void heap_free(void *ptr);
+  virtual void mark_pointer(void *ptr, size_t orig_size, ReferenceCount *ref_ptr);
 
 #if defined(WIN32_VC) && defined(_DEBUG)
   static int win32_malloc_hook(int alloc_type, void *ptr, 
@@ -67,11 +66,12 @@ PUBLISHED:
   INLINE static bool is_tracking();
   INLINE static bool is_counting();
   INLINE static size_t get_current_cpp_size();
-  INLINE static bool has_cpp_size();
-  INLINE static size_t get_cpp_size();
-  INLINE static bool has_interpreter_size();
+  INLINE static size_t get_total_cpp_size();
+
+  INLINE static size_t get_panda_heap_size();
+  INLINE static size_t get_panda_mmap_size();
   INLINE static size_t get_interpreter_size();
-  INLINE static bool has_total_size();
+  INLINE static size_t get_external_size();
   INLINE static size_t get_total_size();
 
   INLINE static int get_num_pointers();
@@ -90,7 +90,7 @@ PUBLISHED:
   INLINE static void show_trend_ages();
 
 private:
-  MemoryUsage();
+  MemoryUsage(const MemoryHook &copy);
   static MemoryUsage *get_global_ptr();
 
   void ns_record_pointer(ReferenceCount *ptr);
@@ -100,11 +100,7 @@ private:
 
   void ns_record_void_pointer(void *ptr, size_t size);
   void ns_remove_void_pointer(void *ptr);
-  void ns_mark_pointer(void *ptr, size_t size, ReferenceCount *ref_ptr);
 
-  size_t ns_get_current_cpp_size();
-  size_t ns_get_cpp_size();
-  size_t ns_get_interpreter_size();
   size_t ns_get_total_size();
   int ns_get_num_pointers();
   void ns_get_pointers(MemoryUsagePointers &result);
@@ -127,8 +123,8 @@ private:
 
   // We shouldn't use a pmap, since that would be recursive!
   // Actually, it turns out that it doesn't matter, since somehow the
-  // pallocator gets used even though we specify dallocator here, so
-  // we have to make special code that handles the recursion anyway.
+  // pallocator gets used even though we don't specify it here, so we
+  // have to make special code that handles the recursion anyway.
 
   // This table stores up to two entiries for each MemoryInfo object:
   // one for the void pointer (the pointer to the beginning of the
@@ -138,19 +134,19 @@ private:
   // pointers, while others may be stored under only one pointer or
   // the other.  We don't store an entry for an object's TypedObject
   // pointer.
-  typedef map<void *, MemoryInfo *, less<void *>, dallocator<pair<void * const, MemoryInfo *> > > Table;
+  typedef map<void *, MemoryInfo *> Table;
   Table _table;
 
   // This table indexes the individual MemoryInfo objects, for unique
   // iteration.
-  typedef set<MemoryInfo *, less<MemoryInfo *>, dallocator<MemoryInfo *> > InfoSet;
+  typedef set<MemoryInfo *> InfoSet;
   InfoSet _info_set;
   bool _info_set_dirty;
 
   int _freeze_index;
   int _count;
   size_t _current_cpp_size;
-  size_t _cpp_size;
+  size_t _total_cpp_size;
   size_t _interpreter_size;
   size_t _total_size;
 
@@ -162,8 +158,7 @@ private:
 
   private:
     // Cannot use a pmap, since that would be recursive!
-    typedef map<TypeHandle, MemoryUsagePointerCounts, 
-                less<TypeHandle>, dallocator<pair<const TypeHandle, MemoryUsagePointerCounts> > > Counts;
+    typedef map<TypeHandle, MemoryUsagePointerCounts> Counts;
     Counts _counts;
   };
   TypeHistogram _trend_types;
@@ -188,7 +183,6 @@ private:
   bool _track_memory_usage;
   bool _count_memory_usage;
 
-  static bool _is_cpp_operator;
   static bool _recursion_protect;
 };
 

+ 3 - 34
panda/src/gobj/vertexDataPage.cxx

@@ -22,26 +22,12 @@
 #include "vertexDataBook.h"
 #include "pStatTimer.h"
 #include "mutexHolder.h"
+#include "memoryHook.h"
 
 #ifdef HAVE_ZLIB
 #include <zlib.h>
 #endif
 
-#ifdef WIN32
-
-// Windows case (VirtualAlloc)
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-#else
-
-// Posix case (mmap)
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-
-#endif
-
 ConfigVariableInt max_resident_vertex_data
 ("max-resident-vertex-data", -1,
  PRC_DESC("Specifies the maximum number of bytes of all vertex data "
@@ -634,19 +620,7 @@ make_save_file() {
 unsigned char *VertexDataPage::
 alloc_page_data(size_t page_size) const {
   get_class_type().inc_memory_usage(TypeHandle::MC_array, page_size);
-  
-  unsigned char *buffer;
-#ifdef WIN32
-  // Windows case.
-  buffer = (unsigned char *)VirtualAlloc(NULL, page_size, MEM_COMMIT | MEM_RESERVE,
-                                         PAGE_READWRITE);
-#else
-  // Posix case.
-  buffer = (unsigned char *)mmap(NULL, page_size, PROT_READ | PROT_WRITE, 
-                                 MAP_PRIVATE | MAP_ANON, -1, 0);
-#endif
-
-  return buffer;
+  return (unsigned char *)memory_hook->mmap_alloc(page_size, false);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -657,12 +631,7 @@ alloc_page_data(size_t page_size) const {
 void VertexDataPage::
 free_page_data(unsigned char *page_data, size_t page_size) const {
   get_class_type().dec_memory_usage(TypeHandle::MC_array, page_size);
-
-#ifdef WIN32
-  VirtualFree(page_data, 0, MEM_RELEASE);
-#else  
-  munmap(page_data, page_size);
-#endif
+  memory_hook->mmap_free(page_data, page_size);
 }
  
 ////////////////////////////////////////////////////////////////////

+ 2 - 0
panda/src/pgraph/pandaNode.h

@@ -665,6 +665,8 @@ public:
                   TypedWritable::get_class_type(),
                   ReferenceCount::get_class_type());
     CData::init_type();
+    Down::init_type();
+    Up::init_type();
   }
   virtual TypeHandle get_type() const {
     return get_class_type();

+ 3 - 31
panda/src/pipeline/threadSimpleImpl.cxx

@@ -24,21 +24,6 @@
 #include "threadSimpleManager.h"
 #include "thread.h"
 
-#ifdef WIN32
-
-// Windows case (VirtualAlloc)
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-#else
-
-// Posix case (mmap)
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-
-#endif
-
 ThreadSimpleImpl *volatile ThreadSimpleImpl::_st_this;
 
 ////////////////////////////////////////////////////////////////////
@@ -76,11 +61,7 @@ ThreadSimpleImpl::
   nassertv(_status != S_running);
 
   if (_stack != (void *)NULL) {
-#ifdef WIN32
-    VirtualFree(_stack, 0, MEM_RELEASE);
-#else  
-    munmap(_stack, _stack_size);
-#endif
+    memory_hook->mmap_free(_stack, _stack_size);
   }
 }
 
@@ -113,17 +94,8 @@ start(ThreadPriority priority, bool joinable) {
   nassertr(_status == S_new, false);
 
   nassertr(_stack == NULL, false);
-  _stack_size = (size_t)thread_stack_size;
-
-#ifdef WIN32
-  // Windows case.
-  _stack = (unsigned char *)VirtualAlloc(NULL, _stack_size, MEM_COMMIT | MEM_RESERVE,
-                                         PAGE_EXECUTE_READWRITE);
-#else
-  // Posix case.
-  _stack = (unsigned char *)mmap(NULL, _stack_size, PROT_READ | PROT_WRITE | PROT_EXEC, 
-                                 MAP_PRIVATE | MAP_ANON, -1, 0);
-#endif
+  _stack_size = memory_hook->round_up_to_page_size((size_t)thread_stack_size);
+  _stack = (unsigned char *)memory_hook->mmap_alloc(_stack_size, true);
 
   _joinable = joinable;
   _status = S_running;

+ 18 - 18
panda/src/pstatclient/pStatClient.cxx

@@ -33,12 +33,14 @@
 #include "clockObject.h"
 #include "neverFreeMemory.h"
 
-PStatCollector PStatClient::_nf_unused_size_pcollector("Track memory 1:NeverFree:Unused");
-PStatCollector PStatClient::_nf_other_size_pcollector("Track memory 1:NeverFree:Other");
-PStatCollector PStatClient::_cpp_other_size_pcollector("Track memory 1:Heap:Other");
-PStatCollector PStatClient::_total_size_pcollector("Track memory 2");
-PStatCollector PStatClient::_cpp_size_pcollector("Track memory 2:C++");
-PStatCollector PStatClient::_interpreter_size_pcollector("Track memory 2:Interpreter");
+PStatCollector PStatClient::_t1_nf_unused_size_pcollector("Track memory 1:NeverFree:Unused");
+PStatCollector PStatClient::_t1_nf_other_size_pcollector("Track memory 1:NeverFree:Other");
+PStatCollector PStatClient::_t1_cpp_other_size_pcollector("Track memory 1:Dynamic:Other");
+PStatCollector PStatClient::_t2_total_size_pcollector("Track memory 2");
+PStatCollector PStatClient::_t2_heap_size_pcollector("Track memory 2:Heap");
+PStatCollector PStatClient::_t2_mmap_size_pcollector("Track memory 2:MMap");
+PStatCollector PStatClient::_t2_interpreter_size_pcollector("Track memory 2:Interpreter");
+PStatCollector PStatClient::_t2_external_size_pcollector("Track memory 2:External");
 PStatCollector PStatClient::_pstats_pcollector("*:PStats");
 PStatCollector PStatClient::_clock_wait_pcollector("Wait:Clock Wait:Sleep");
 PStatCollector PStatClient::_clock_busy_wait_pcollector("Wait:Clock Wait:Spin");
@@ -215,14 +217,12 @@ main_tick() {
 
 #ifdef DO_MEMORY_USAGE
   if (is_connected()) {
-    if (MemoryUsage::has_total_size()) {
-      _total_size_pcollector.set_level(MemoryUsage::get_total_size());
-    }
-    if (MemoryUsage::has_cpp_size()) {
-      _cpp_size_pcollector.set_level(MemoryUsage::get_cpp_size());
-    }
-    if (MemoryUsage::has_interpreter_size()) {
-      _interpreter_size_pcollector.set_level(MemoryUsage::get_interpreter_size());
+    if (MemoryUsage::is_tracking()) {
+      _t2_total_size_pcollector.set_level(MemoryUsage::get_total_size());
+      _t2_heap_size_pcollector.set_level(MemoryUsage::get_panda_heap_size());
+      _t2_mmap_size_pcollector.set_level(MemoryUsage::get_panda_mmap_size());
+      _t2_interpreter_size_pcollector.set_level(MemoryUsage::get_interpreter_size());
+      _t2_external_size_pcollector.set_level(MemoryUsage::get_external_size());
     }
     
     TypeRegistry *type_reg = TypeRegistry::ptr();
@@ -277,7 +277,7 @@ main_tick() {
               break;
 
             default:
-              category = "Heap";
+              category = "Dynamic";
             }
             ostringstream strm;
             strm << "Track memory 1:" << category << ":" << type << ":" << mc;
@@ -298,12 +298,12 @@ main_tick() {
       }
     }
 
-    _nf_unused_size_pcollector.set_level(NeverFreeMemory::get_total_unused());
+    _t1_nf_unused_size_pcollector.set_level(NeverFreeMemory::get_total_unused());
 
     // The remaining amount--all collectors smaller than 0.1% of the
     // total--go into "other".
-    _nf_other_size_pcollector.set_level(nf_other_usage);
-    _cpp_other_size_pcollector.set_level(cpp_other_usage);
+    _t1_nf_other_size_pcollector.set_level(nf_other_usage);
+    _t1_cpp_other_size_pcollector.set_level(cpp_other_usage);
   }
 #endif  // DO_MEMORY_USAGE
 

+ 8 - 6
panda/src/pstatclient/pStatClient.h

@@ -227,12 +227,14 @@ private:
 
   PStatClientImpl *_impl;
 
-  static PStatCollector _nf_unused_size_pcollector;
-  static PStatCollector _nf_other_size_pcollector;
-  static PStatCollector _cpp_other_size_pcollector;
-  static PStatCollector _total_size_pcollector;
-  static PStatCollector _cpp_size_pcollector;
-  static PStatCollector _interpreter_size_pcollector;
+  static PStatCollector _t1_nf_unused_size_pcollector;
+  static PStatCollector _t1_nf_other_size_pcollector;
+  static PStatCollector _t1_cpp_other_size_pcollector;
+  static PStatCollector _t2_total_size_pcollector;
+  static PStatCollector _t2_heap_size_pcollector;
+  static PStatCollector _t2_mmap_size_pcollector;
+  static PStatCollector _t2_interpreter_size_pcollector;
+  static PStatCollector _t2_external_size_pcollector;
   static PStatCollector _pstats_pcollector;
   static PStatCollector _clock_wait_pcollector;
   static PStatCollector _clock_busy_wait_pcollector;

+ 4 - 2
panda/src/pstatclient/pStatProperties.cxx

@@ -197,11 +197,13 @@ static LevelCollectorProperties level_properties[] = {
   { 1, "Occlusion tests",                  { 0.9, 0.8, 0.3 },  "", 500.0 },
   { 1, "Occlusion results",                { 0.3, 0.9, 0.8 },  "", 500.0 },
   { 1, "Track memory 1",                   { 0.5, 1.0, 0.5 },  "MB", 64, 1048576 },
-  { 1, "Track memory 1:Heap",              { 0.8, 0.2, 1.0 } },
+  { 1, "Track memory 1:Dynamic",           { 0.8, 0.2, 1.0 } },
   { 1, "Track memory 1:NeverFree",         { 0.2, 0.5, 0.8 } },
   { 1, "Track memory 2",                   { 0.5, 1.0, 0.5 },  "MB", 64, 1048576 },
-  { 1, "Track memory 2:C++",               { 0.2, 0.2, 1.0 } },
+  { 1, "Track memory 2:Heap",              { 0.2, 0.2, 1.0 } },
+  { 1, "Track memory 2:MMap",              { 0.9, 0.4, 0.7 } },
   { 1, "Track memory 2:Interpreter",       { 0.8, 0.2, 0.5 } },
+  { 1, "Track memory 2:External",          { 1.0, 1.0, 0.1 } },
   { 1, "Vertex Data",                      { 1.0, 0.4, 0.0 },  "MB", 64, 1048576 },
   { 1, "Vertex Data:Independent",          { 0.9, 0.1, 0.9 } },
   { 1, "Vertex Data:Small",                { 0.2, 0.3, 0.4 } },