mmgr.cpp

来自「ftgl-2.1.2 夸平台的opengl显示字体」· C++ 代码 · 共 1,668 行 · 第 1/5 页

CPP
1,668
字号
// ---------------------------------------------------------------------------------------------------------------------------------// -DOC- When tracking down a difficult bug, use this routine to force a breakpoint on a specific allocation count// ---------------------------------------------------------------------------------------------------------------------------------void	m_breakOnAllocation(unsigned int count){	breakOnAllocationCount = count;}// ---------------------------------------------------------------------------------------------------------------------------------// Used by the macros// ---------------------------------------------------------------------------------------------------------------------------------void	m_setOwner(const char *file, const unsigned int line, const char *func){	// You're probably wondering about this...	//	// It's important for this memory manager to primarily work with global new/delete in their original forms (i.e. with	// no extra parameters.) In order to do this, we use macros that call this function prior to operators new & delete. This	// is fine... usually. Here's what actually happens when you use this macro to delete an object:	//	// m_setOwner(__FILE__, __LINE__, __FUNCTION__) --> object::~object() --> delete	//	// Note that the compiler inserts a call to the object's destructor just prior to calling our overridden operator delete.	// But what happens when we delete an object whose destructor deletes another object, whose desctuctor deletes another	// object? Here's a diagram (indentation follows stack depth):	//	// m_setOwner(...) -> ~obj1()                          // original call to delete obj1	//     m_setOwner(...) -> ~obj2()                      // obj1's destructor deletes obj2	//         m_setOwner(...) -> ~obj3()                  // obj2's destructor deletes obj3	//             ...                                     // obj3's destructor just does some stuff	//         delete                                      // back in obj2's destructor, we call delete	//     delete                                          // back in obj1's destructor, we call delete	// delete                                              // back to our original call, we call delete	//	// Because m_setOwner() just sets up some static variables (below) it's important that each call to m_setOwner() and	// successive calls to new/delete alternate. However, in this case, three calls to m_setOwner() happen in succession	// followed by three calls to delete in succession (with a few calls to destructors mixed in for fun.) This means that	// only the final call to delete (in this chain of events) will have the proper reporting, and the first two in the chain	// will not have ANY owner-reporting information. The deletes will still work fine, we just won't know who called us.	//	// "Then build a stack, my friend!" you might think... but it's a very common thing that people will be working with third-	// party libraries (including MFC under Windows) which is not compiled with this memory manager's macros. In those cases,	// m_setOwner() is never called, and rightfully should not have the proper trace-back information. So if one of the	// destructors in the chain ends up being a call to a delete from a non-mmgr-compiled library, the stack will get confused.	//	// I've been unable to find a solution to this problem, but at least we can detect it and report the data before we	// lose it. That's what this is all about. It makes it somewhat confusing to read in the logs, but at least ALL the	// information is present...	//	// There's a caveat here... The compiler is not required to call operator delete if the value being deleted is NULL.	// In this case, any call to delete with a NULL will sill call m_setOwner(), which will make m_setOwner() think that	// there is a destructor chain becuase we setup the variables, but nothing gets called to clear them. Because of this	// we report a "Possible destructor chain".	//	// Thanks to J. Woznack (from Kodiak Interactive Software Studios -- www.kodiakgames.com) for pointing this out.	if (sourceLine && alwaysLogAll)	{		log("[I] NOTE! Possible destructor chain: previous owner is %s", ownerString(sourceFile, sourceLine, sourceFunc));	}	// Okay... save this stuff off so we can keep track of the caller	sourceFile = file;	sourceLine = line;	sourceFunc = func;}// ---------------------------------------------------------------------------------------------------------------------------------static	void	resetGlobals(){	sourceFile = "??";	sourceLine = 0;	sourceFunc = "??";}// ---------------------------------------------------------------------------------------------------------------------------------// Global new/new[]//// These are the standard new/new[] operators. They are merely interface functions that operate like normal new/new[], but use our// memory tracking routines.// ---------------------------------------------------------------------------------------------------------------------------------void	*operator new(size_t reportedSize){	#ifdef TEST_MEMORY_MANAGER	log("[D] ENTER: new");	#endif	// Save these off...	const	char		*file = sourceFile;	const	unsigned int	line = sourceLine;	const	char		*func = sourceFunc;	// ANSI says: allocation requests of 0 bytes will still return a valid value	if (reportedSize == 0) reportedSize = 1;	// ANSI says: loop continuously because the error handler could possibly free up some memory	for(;;)	{		// Try the allocation		void	*ptr = m_allocator(file, line, func, m_alloc_new, reportedSize);		if (ptr)		{			#ifdef TEST_MEMORY_MANAGER			log("[D] EXIT : new");			#endif			return ptr;		}		// There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then		// set it back again.				std::new_handler	nh = std::set_new_handler(0);		std::set_new_handler(nh);		// If there is an error handler, call it		if (nh)		{			(*nh)();		}		// Otherwise, throw the exception		else		{			#ifdef TEST_MEMORY_MANAGER			log("[D] EXIT : new");			#endif			throw std::bad_alloc();		}	}}// ---------------------------------------------------------------------------------------------------------------------------------void	*operator new[](size_t reportedSize){	#ifdef TEST_MEMORY_MANAGER	log("[D] ENTER: new[]");	#endif	// Save these off...	const	char		*file = sourceFile;	const	unsigned int	line = sourceLine;	const	char		*func = sourceFunc;	// The ANSI standard says that allocation requests of 0 bytes will still return a valid value	if (reportedSize == 0) reportedSize = 1;	// ANSI says: loop continuously because the error handler could possibly free up some memory	for(;;)	{		// Try the allocation		void	*ptr = m_allocator(file, line, func, m_alloc_new_array, reportedSize);		if (ptr)		{			#ifdef TEST_MEMORY_MANAGER			log("[D] EXIT : new[]");			#endif			return ptr;		}		// There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then		// set it back again.		std::new_handler	nh = std::set_new_handler(0);		std::set_new_handler(nh);		// If there is an error handler, call it		if (nh)		{			(*nh)();		}		// Otherwise, throw the exception		else		{			#ifdef TEST_MEMORY_MANAGER			log("[D] EXIT : new[]");			#endif			throw std::bad_alloc();		}	}}// ---------------------------------------------------------------------------------------------------------------------------------// Other global new/new[]//// These are the standard new/new[] operators as used by Microsoft's memory tracker. We don't want them interfering with our memory// tracking efforts. Like the previous versions, these are merely interface functions that operate like normal new/new[], but use// our memory tracking routines.// ---------------------------------------------------------------------------------------------------------------------------------void	*operator new(size_t reportedSize, const char *sourceFile, int sourceLine){	#ifdef TEST_MEMORY_MANAGER	log("[D] ENTER: new");	#endif	// The ANSI standard says that allocation requests of 0 bytes will still return a valid value	if (reportedSize == 0) reportedSize = 1;	// ANSI says: loop continuously because the error handler could possibly free up some memory	for(;;)	{		// Try the allocation		void	*ptr = m_allocator(sourceFile, sourceLine, "??", m_alloc_new, reportedSize);		if (ptr)		{			#ifdef TEST_MEMORY_MANAGER			log("[D] EXIT : new");			#endif			return ptr;		}		// There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then		// set it back again.		std::new_handler	nh = std::set_new_handler(0);		std::set_new_handler(nh);		// If there is an error handler, call it		if (nh)		{			(*nh)();		}		// Otherwise, throw the exception		else		{			#ifdef TEST_MEMORY_MANAGER			log("[D] EXIT : new");			#endif			throw std::bad_alloc();		}	}}// ---------------------------------------------------------------------------------------------------------------------------------void	*operator new[](size_t reportedSize, const char *sourceFile, int sourceLine){	#ifdef TEST_MEMORY_MANAGER	log("[D] ENTER: new[]");	#endif	// The ANSI standard says that allocation requests of 0 bytes will still return a valid value	if (reportedSize == 0) reportedSize = 1;	// ANSI says: loop continuously because the error handler could possibly free up some memory	for(;;)	{		// Try the allocation		void	*ptr = m_allocator(sourceFile, sourceLine, "??", m_alloc_new_array, reportedSize);		if (ptr)		{			#ifdef TEST_MEMORY_MANAGER			log("[D] EXIT : new[]");			#endif			return ptr;		}		// There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then		// set it back again.		std::new_handler	nh = std::set_new_handler(0);		std::set_new_handler(nh);		// If there is an error handler, call it		if (nh)		{			(*nh)();		}		// Otherwise, throw the exception		else		{			#ifdef TEST_MEMORY_MANAGER			log("[D] EXIT : new[]");			#endif			throw std::bad_alloc();		}	}}// ---------------------------------------------------------------------------------------------------------------------------------// Global delete/delete[]//// These are the standard delete/delete[] operators. They are merely interface functions that operate like normal delete/delete[],// but use our memory tracking routines.// ---------------------------------------------------------------------------------------------------------------------------------void	operator delete(void *reportedAddress){	#ifdef TEST_MEMORY_MANAGER	log("[D] ENTER: delete");	#endif	// ANSI says: delete & delete[] allow NULL pointers (they do nothing)	if (reportedAddress) m_deallocator(sourceFile, sourceLine, sourceFunc, m_alloc_delete, reportedAddress);	else if (alwaysLogAll) log("[-] ----- %8s of NULL                      by %s", allocationTypes[m_alloc_delete], ownerString(sourceFile, sourceLine, sourceFunc));	// Resetting the globals insures that if at some later time, somebody calls our memory manager from an unknown	// source (i.e. they didn't include our H file) then we won't think it was the last allocation.	resetGlobals();	#ifdef TEST_MEMORY_MANAGER

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?