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 + -
显示快捷键?