diff --git a/dtool/Config.FreeBSD.pp b/dtool/Config.FreeBSD.pp index 1707104718..db0559c4af 100644 --- a/dtool/Config.FreeBSD.pp +++ b/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 diff --git a/dtool/Config.Irix.pp b/dtool/Config.Irix.pp index 086bed4830..02328e3232 100644 --- a/dtool/Config.Irix.pp +++ b/dtool/Config.Irix.pp @@ -140,9 +140,6 @@ // Do we have RTTI (and )? #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 diff --git a/dtool/Config.Linux.pp b/dtool/Config.Linux.pp index d55bf437d9..51364e924a 100644 --- a/dtool/Config.Linux.pp +++ b/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 diff --git a/dtool/Config.OSX.pp b/dtool/Config.OSX.pp index 10d1346c42..b551d6e84d 100644 --- a/dtool/Config.OSX.pp +++ b/dtool/Config.OSX.pp @@ -154,9 +154,6 @@ // Do we have RTTI (and )? #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 diff --git a/dtool/Config.Win32.pp b/dtool/Config.Win32.pp index 03a7c87f8d..e54ec3839f 100644 --- a/dtool/Config.Win32.pp +++ b/dtool/Config.Win32.pp @@ -149,9 +149,6 @@ // Do we have RTTI (and )? #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 diff --git a/dtool/Config.pp b/dtool/Config.pp index 31fa44bf0d..7c65fc12fd 100644 --- a/dtool/Config.pp +++ b/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 diff --git a/dtool/LocalSetup.pp b/dtool/LocalSetup.pp index 804b27224e..346323c169 100644 --- a/dtool/LocalSetup.pp +++ b/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 diff --git a/dtool/src/dtoolbase/Sources.pp b/dtool/src/dtoolbase/Sources.pp index ff71eb899a..aefab312d3 100644 --- a/dtool/src/dtoolbase/Sources.pp +++ b/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 \ diff --git a/dtool/src/dtoolbase/dallocator.T b/dtool/src/dtoolbase/dallocator.T deleted file mode 100644 index 08c2747147..0000000000 --- a/dtool/src/dtoolbase/dallocator.T +++ /dev/null @@ -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 -// panda3d@yahoogroups.com . -// -//////////////////////////////////////////////////////////////////// - - -template -INLINE dallocator:: -dallocator() throw() { -} - -template -INLINE TYPENAME dallocator::pointer dallocator:: -allocate(TYPENAME dallocator::size_type n, allocator::const_pointer) { - return (TYPENAME dallocator::pointer)(*global_operator_new)(n * sizeof(Type)); -} - -template -INLINE void dallocator:: -deallocate(TYPENAME dallocator::pointer p, TYPENAME dallocator::size_type) { - (*global_operator_delete)(p); -} diff --git a/dtool/src/dtoolbase/dallocator.h b/dtool/src/dtoolbase/dallocator.h deleted file mode 100644 index 165fac34c1..0000000000 --- a/dtool/src/dtoolbase/dallocator.h +++ /dev/null @@ -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 -// panda3d-general@lists.sourceforge.net . -// -//////////////////////////////////////////////////////////////////// - -#ifndef DALLOCATOR_H -#define DALLOCATOR_H -#include - -#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 dallocator : public allocator { -public: - // There seems to be a bug in VC++ 2003 that requires these typedefs - // to be made explicitly. - typedef TYPENAME allocator::pointer pointer; - typedef TYPENAME allocator::reference reference; - typedef TYPENAME allocator::const_pointer const_pointer; - typedef TYPENAME allocator::const_reference const_reference; - typedef TYPENAME allocator::size_type size_type; - - INLINE dallocator() throw(); - - // template member functions in VC++ can only be defined in-class. - template - INLINE dallocator(const dallocator ©) throw() { } - - INLINE pointer allocate(size_type n, allocator::const_pointer hint = 0); - INLINE void deallocate(pointer p, size_type n); - - template struct rebind { - typedef dallocator other; - }; -}; - -#include "dallocator.T" - -#endif // USE_STL_ALLOCATOR - -#endif - diff --git a/dtool/src/dtoolbase/deletedChain.T b/dtool/src/dtoolbase/deletedChain.T index 1f00eb20a1..f6df5c3198 100644 --- a/dtool/src/dtoolbase/deletedChain.T +++ b/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 INLINE Type *DeletedChain:: -node_to_type(TVOLATILE TYPENAME DeletedChain::ObjectNode *node) { +node_to_type(TYPENAME DeletedChain::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:: 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 diff --git a/dtool/src/dtoolbase/deletedChain.h b/dtool/src/dtoolbase/deletedChain.h index cc68ff460c..82e3e3c220 100644 --- a/dtool/src/dtoolbase/deletedChain.h +++ b/dtool/src/dtoolbase/deletedChain.h @@ -28,15 +28,12 @@ #include "typeHandle.h" #include -#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 }; //////////////////////////////////////////////////////////////////// diff --git a/dtool/src/dtoolbase/dtoolbase.cxx b/dtool/src/dtoolbase/dtoolbase.cxx index 54494d06b5..d62467393c 100644 --- a/dtool/src/dtoolbase/dtoolbase.cxx +++ b/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; -///////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// 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. // -// 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(); +// 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) diff --git a/dtool/src/dtoolbase/dtoolbase_cc.h b/dtool/src/dtoolbase/dtoolbase_cc.h index 8d23ee4d34..33b44eefe7 100644 --- a/dtool/src/dtoolbase/dtoolbase_cc.h +++ b/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) diff --git a/dtool/src/dtoolbase/dtoolbase_composite1.cxx b/dtool/src/dtoolbase/dtoolbase_composite1.cxx index 362e661ab8..cd5bdf6f18 100644 --- a/dtool/src/dtoolbase/dtoolbase_composite1.cxx +++ b/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" diff --git a/dtool/src/dtoolbase/memoryBase.h b/dtool/src/dtoolbase/memoryBase.h index 20a0160c89..ef95b010ef 100644 --- a/dtool/src/dtoolbase/memoryBase.h +++ b/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 *) { \ } diff --git a/dtool/src/dtoolbase/memoryHook.I b/dtool/src/dtoolbase/memoryHook.I new file mode 100644 index 0000000000..b57048fcf5 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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 diff --git a/dtool/src/dtoolbase/memoryHook.cxx b/dtool/src/dtoolbase/memoryHook.cxx new file mode 100644 index 0000000000..e594c3ea2e --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "memoryHook.h" + +#ifdef WIN32 + +// Windows case. +#define WIN32_LEAN_AND_MEAN +#include + +#else + +// Posix case. +#include +#include +#include + +#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 ©) : + _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 *) { +} diff --git a/dtool/src/dtoolbase/memoryHook.h b/dtool/src/dtoolbase/memoryHook.h new file mode 100644 index 0000000000..df8b1dbf49 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 ©); + + 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 diff --git a/dtool/src/dtoolbase/neverFreeMemory.cxx b/dtool/src/dtoolbase/neverFreeMemory.cxx index 0f7ef85fae..e8da139649 100644 --- a/dtool/src/dtoolbase/neverFreeMemory.cxx +++ b/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 - -#else - -// Posix case. -#include -#include -#include - -#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); diff --git a/dtool/src/dtoolbase/neverFreeMemory.h b/dtool/src/dtoolbase/neverFreeMemory.h index 2d144fb4d0..d64102ab91 100644 --- a/dtool/src/dtoolbase/neverFreeMemory.h +++ b/dtool/src/dtoolbase/neverFreeMemory.h @@ -69,7 +69,6 @@ private: typedef set Pages; Pages _pages; - size_t _page_size; size_t _total_alloc; size_t _total_used; MutexImpl _lock; diff --git a/dtool/src/dtoolbase/pallocator.T b/dtool/src/dtoolbase/pallocator.T index 65ea19ae68..a37ea66a81 100644 --- a/dtool/src/dtoolbase/pallocator.T +++ b/dtool/src/dtoolbase/pallocator.T @@ -55,7 +55,7 @@ allocate(TYPENAME pallocator_array::size_type n, TYPENAME allocator: // 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::pointer)(*global_operator_new)(alloc_size); + void *ptr = (TYPENAME pallocator_array::pointer)PANDA_MALLOC(alloc_size); *((size_t *)ptr) = alloc_size; return (TYPENAME pallocator_array::pointer)(((size_t *)ptr) + 1); #else @@ -71,7 +71,7 @@ deallocate(TYPENAME pallocator_array::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 diff --git a/dtool/src/dtoolbase/pallocator.h b/dtool/src/dtoolbase/pallocator.h index 1f9febb398..34d109ed0d 100644 --- a/dtool/src/dtoolbase/pallocator.h +++ b/dtool/src/dtoolbase/pallocator.h @@ -21,6 +21,7 @@ #include #include "dtoolbase.h" +#include "memoryHook.h" #include "deletedChain.h" #include "typeHandle.h" @@ -60,7 +61,7 @@ public: typedef TYPENAME allocator::const_reference const_reference; typedef TYPENAME allocator::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 diff --git a/dtool/src/dtoolbase/typeHandle.cxx b/dtool/src/dtoolbase/typeHandle.cxx index fb3512802e..ff7aed2cb7 100644 --- a/dtool/src/dtoolbase/typeHandle.cxx +++ b/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); } } diff --git a/dtool/src/prc/configVariableManager.cxx b/dtool/src/prc/configVariableManager.cxx index f90b7b3138..3c5eb24f4a 100644 --- a/dtool/src/prc/configVariableManager.cxx +++ b/dtool/src/prc/configVariableManager.cxx @@ -35,6 +35,7 @@ ConfigVariableManager *ConfigVariableManager::_global_ptr = NULL; //////////////////////////////////////////////////////////////////// ConfigVariableManager:: ConfigVariableManager() { + init_memory_hook(); } //////////////////////////////////////////////////////////////////// diff --git a/dtool/src/prc/notify.cxx b/dtool/src/prc/notify.cxx index 95303b1a71..75b17e811c 100644 --- a/dtool/src/prc/notify.cxx +++ b/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; diff --git a/dtool/src/prc/notifyCategory.h b/dtool/src/prc/notifyCategory.h index 0966e16eb1..75b75eae06 100644 --- a/dtool/src/prc/notifyCategory.h +++ b/dtool/src/prc/notifyCategory.h @@ -24,6 +24,7 @@ #include "notifySeverity.h" #include "configVariableEnum.h" #include "configFlags.h" +#include "memoryBase.h" #include @@ -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); diff --git a/dtool/src/prc/pnotify.h b/dtool/src/prc/pnotify.h index 41ec1c4690..485f46f2a8 100644 --- a/dtool/src/prc/pnotify.h +++ b/dtool/src/prc/pnotify.h @@ -20,7 +20,6 @@ #define NOTIFY_H #include "dtoolbase.h" - #include "notifySeverity.h" #include diff --git a/panda/src/egg/eggNode.h b/panda/src/egg/eggNode.h index 4ecdd0afa0..a4d432d03c 100644 --- a/panda/src/egg/eggNode.h +++ b/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(); diff --git a/panda/src/express/memoryUsage.I b/panda/src/express/memoryUsage.I index 397a49a344..18f37da28e 100644 --- a/panda/src/express/memoryUsage.I +++ b/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 -// 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. -//////////////////////////////////////////////////////////////////// -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 -} - -//////////////////////////////////////////////////////////////////// -// Function: MemoryUsage::get_cpp_size +// Function: MemoryUsage::get_total_cpp_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. +// consumed by C++ objects, including the memory +// previously frozen. //////////////////////////////////////////////////////////////////// INLINE size_t MemoryUsage:: -get_cpp_size() { - return get_global_ptr()->ns_get_cpp_size(); +get_total_cpp_size() { + return get_global_ptr()->_total_cpp_size; } //////////////////////////////////////////////////////////////////// -// Function: MemoryUsage::has_interpreter_size +// Function: MemoryUsage::get_panda_heap_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 +// heap 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_heap_size() { + return get_global_ptr()->_total_heap_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::get_panda_mmap_size +// Access: Public, Static +// Description: Returns the total number of bytes allocated from the +// virtual memory pool from code within Panda. +//////////////////////////////////////////////////////////////////// +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; + } } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/express/memoryUsage.cxx b/panda/src/express/memoryUsage.cxx index 853bb26e88..5562672245 100644 --- a/panda/src/express/memoryUsage.cxx +++ b/panda/src/express/memoryUsage.cxx @@ -33,11 +33,7 @@ #include "config_express.h" #include -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 > CountSorter; + typedef vector 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 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 ©) : 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 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 diff --git a/panda/src/express/memoryUsage.h b/panda/src/express/memoryUsage.h index 6e3c087e22..a880ddb1d9 100644 --- a/panda/src/express/memoryUsage.h +++ b/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 ©); 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, dallocator > > Table; + typedef map Table; Table _table; // This table indexes the individual MemoryInfo objects, for unique // iteration. - typedef set, dallocator > InfoSet; + typedef set 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, dallocator > > Counts; + typedef map 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; }; diff --git a/panda/src/gobj/vertexDataPage.cxx b/panda/src/gobj/vertexDataPage.cxx index 3063fb4754..b1c5176cdf 100644 --- a/panda/src/gobj/vertexDataPage.cxx +++ b/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 #endif -#ifdef WIN32 - -// Windows case (VirtualAlloc) -#define WIN32_LEAN_AND_MEAN -#include - -#else - -// Posix case (mmap) -#include -#include -#include - -#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); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 36819d7624..40e22c4cde 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/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(); diff --git a/panda/src/pipeline/threadSimpleImpl.cxx b/panda/src/pipeline/threadSimpleImpl.cxx index ee37636a8b..8d29ce896b 100644 --- a/panda/src/pipeline/threadSimpleImpl.cxx +++ b/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 - -#else - -// Posix case (mmap) -#include -#include -#include - -#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; diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index 2b590b8f7f..770303bf9c 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/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 diff --git a/panda/src/pstatclient/pStatClient.h b/panda/src/pstatclient/pStatClient.h index f47720b160..957903c959 100644 --- a/panda/src/pstatclient/pStatClient.h +++ b/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; diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index 2d2f7d86d4..05ef9af0cf 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/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 } },