📄 mmgr.cpp
字号:
// ---------------------------------------------------------------------------------------------------------------------------------// // // _ __ ___ _ __ ___ __ _ _ __ ___ _ __ _ __ // | '_ ` _ \| '_ ` _ \ / _` | '__| / __| '_ \| '_ \ // | | | | | | | | | | | (_| | | _ | (__| |_) | |_) |// |_| |_| |_|_| |_| |_|\__, |_| (_) \___| .__/| .__/ // __/ | | | | | // |___/ |_| |_| //// Memory manager & tracking software//// Best viewed with 8-character tabs and (at least) 132 columns//// ---------------------------------------------------------------------------------------------------------------------------------//// Restrictions & freedoms pertaining to usage and redistribution of this software://// * This software is 100% free// * If you use this software (in part or in whole) you must credit the author.// * This software may not be re-distributed (in part or in whole) in a modified// form without clear documentation on how to obtain a copy of the original work.// * You may not use this software to directly or indirectly cause harm to others.// * This software is provided as-is and without warrantee. Use at your own risk.//// For more information, visit HTTP://www.FluidStudios.com//// ---------------------------------------------------------------------------------------------------------------------------------// Originally created on 12/22/2000 by Paul Nettle//// Copyright 2000, Fluid Studios, Inc., all rights reserved.// ---------------------------------------------------------------------------------------------------------------------------------//// !!IMPORTANT!!//// This software is self-documented with periodic comments. Before you start using this software, perform a search for the string// "-DOC-" to locate pertinent information about how to use this software.//// You are also encouraged to read the comment blocks throughout this source file. They will help you understand how this memory// tracking software works, so you can better utilize it within your applications.//// NOTES://// 1. This code purposely uses no external routines that allocate RAM (other than the raw allocation routines, such as malloc). We// do this because we want this to be as self-contained as possible. As an example, we don't use assert, because when running// under WIN32, the assert brings up a dialog box, which allocates RAM. Doing this in the middle of an allocation would be bad.//// 2. When trying to override new/delete under MFC (which has its own version of global new/delete) the linker will complain. In// order to fix this error, use the compiler option: /FORCE, which will force it to build an executable even with linker errors.// Be sure to check those errors each time you compile, otherwise, you may miss a valid linker error.//// 3. If you see something that looks odd to you or seems like a strange way of going about doing something, then consider that this// code was carefully thought out. If something looks odd, then just assume I've got a good reason for doing it that way (an// example is the use of the class MemStaticTimeTracker.)//// 4. With MFC applications, you will need to comment out any occurance of "#define new DEBUG_NEW" from all source files.//// 5. Include file dependencies are _very_important_ for getting the MMGR to integrate nicely into your application. Be careful if// you're including standard includes from within your own project inclues; that will break this very specific dependency order. // It should look like this://// #include <stdio.h> // Standard includes MUST come first// #include <stdlib.h> //// #include <streamio> ////// #include "mmgr.h" // mmgr.h MUST come next//// #include "myfile1.h" // Project includes MUST come last// #include "myfile2.h" //// #include "myfile3.h" ////// ---------------------------------------------------------------------------------------------------------------------------------//#include "stdafx.h"#include <iostream>#include <stdio.h>#include <stdlib.h>#include <assert.h>#include <string.h>#include <time.h>#include <stdarg.h>#include <new>
#ifndef WIN32#include <unistd.h>#endif#include "mmgr.h"// ---------------------------------------------------------------------------------------------------------------------------------// -DOC- If you're like me, it's hard to gain trust in foreign code. This memory manager will try to INDUCE your code to crash (for// very good reasons... like making bugs obvious as early as possible.) Some people may be inclined to remove this memory tracking// software if it causes crashes that didn't exist previously. In reality, these new crashes are the BEST reason for using this// software!//// Whether this software causes your application to crash, or if it reports errors, you need to be able to TRUST this software. To// this end, you are given some very simple debugging tools.// // The quickest way to locate problems is to enable the STRESS_TEST macro (below.) This should catch 95% of the crashes before they// occur by validating every allocation each time this memory manager performs an allocation function. If that doesn't work, keep// reading...//// If you enable the TEST_MEMORY_MANAGER #define (below), this memory manager will log an entry in the memory.log file each time it// enters and exits one of its primary allocation handling routines. Each call that succeeds should place an "ENTER" and an "EXIT"// into the log. If the program crashes within the memory manager, it will log an "ENTER", but not an "EXIT". The log will also// report the name of the routine.//// Just because this memory manager crashes does not mean that there is a bug here! First, an application could inadvertantly damage// the heap, causing malloc(), realloc() or free() to crash. Also, an application could inadvertantly damage some of the memory used// by this memory tracking software, causing it to crash in much the same way that a damaged heap would affect the standard// allocation routines.//// In the event of a crash within this code, the first thing you'll want to do is to locate the actual line of code that is// crashing. You can do this by adding log() entries throughout the routine that crashes, repeating this process until you narrow// in on the offending line of code. If the crash happens in a standard C allocation routine (i.e. malloc, realloc or free) don't// bother contacting me, your application has damaged the heap. You can help find the culprit in your code by enabling the// STRESS_TEST macro (below.)//// If you truely suspect a bug in this memory manager (and you had better be sure about it! :) you can contact me at// midnight@FluidStudios.com. Before you do, however, check for a newer version at://// http://www.FluidStudios.com/publications.html//// When using this debugging aid, make sure that you are NOT setting the alwaysLogAll variable on, otherwise the log could be// cluttered and hard to read.// ---------------------------------------------------------------------------------------------------------------------------------//#define TEST_MEMORY_MANAGER// ---------------------------------------------------------------------------------------------------------------------------------// -DOC- Enable this sucker if you really want to stress-test your app's memory usage, or to help find hard-to-find bugs// ---------------------------------------------------------------------------------------------------------------------------------#define STRESS_TEST// ---------------------------------------------------------------------------------------------------------------------------------// -DOC- Enable this sucker if you want to stress-test your app's error-handling. Set RANDOM_FAIL to the percentage of failures you// want to test with (0 = none, >100 = all failures).// ---------------------------------------------------------------------------------------------------------------------------------//#define RANDOM_FAILURE 10.0// ---------------------------------------------------------------------------------------------------------------------------------// -DOC- Locals -- modify these flags to suit your needs// ---------------------------------------------------------------------------------------------------------------------------------#ifdef STRESS_TEST static const unsigned int hashBits = 12; static bool randomWipe = true; static bool alwaysValidateAll = true; static bool alwaysLogAll = true; static bool alwaysWipeAll = true; static bool cleanupLogOnFirstRun = true; static const unsigned int paddingSize = 1024; // An extra 8K per allocation!#else static const unsigned int hashBits = 12; static bool randomWipe = false; static bool alwaysValidateAll = false; static bool alwaysLogAll = false; static bool alwaysWipeAll = true; static bool cleanupLogOnFirstRun = true; static const unsigned int paddingSize = 4;#endif// ---------------------------------------------------------------------------------------------------------------------------------// We define our own assert, because we don't want to bring up an assertion dialog, since that allocates RAM. Our new assert// simply declares a forced breakpoint.//// The BEOS assert added by Arvid Norberg <arvid@iname.com>.// ---------------------------------------------------------------------------------------------------------------------------------#ifdef WIN32 #ifdef _DEBUG #define m_assert(x) if ((x) == false) __asm { int 3 } #else #define m_assert(x) {} #endif#elif defined(__BEOS__) #ifdef DEBUG extern void debugger(const char *message); #define m_assert(x) if ((x) == false) debugger("mmgr: assert failed") #else #define m_assert(x) {} #endif#else // Linux uses assert, which we can use safely, since it doesn't bring up a dialog within the program. #define m_assert(cond) assert(cond)#endif// ---------------------------------------------------------------------------------------------------------------------------------// Here, we turn off our macros because any place in this source file where the word 'new' or the word 'delete' (etc.)// appear will be expanded by the macro. So to avoid problems using them within this source file, we'll just #undef them.// ---------------------------------------------------------------------------------------------------------------------------------#undef new#undef delete#undef malloc#undef calloc#undef realloc#undef free// ---------------------------------------------------------------------------------------------------------------------------------// Defaults for the constants & statics in the MemoryManager class// ---------------------------------------------------------------------------------------------------------------------------------const unsigned int m_alloc_unknown = 0;const unsigned int m_alloc_new = 1;const unsigned int m_alloc_new_array = 2;const unsigned int m_alloc_malloc = 3;const unsigned int m_alloc_calloc = 4;const unsigned int m_alloc_realloc = 5;const unsigned int m_alloc_delete = 6;const unsigned int m_alloc_delete_array = 7;const unsigned int m_alloc_free = 8;// ---------------------------------------------------------------------------------------------------------------------------------// -DOC- Get to know these values. They represent the values that will be used to fill unused and deallocated RAM.// ---------------------------------------------------------------------------------------------------------------------------------static unsigned int prefixPattern = 0xbaadf00d; // Fill pattern for bytes preceeding allocated blocksstatic unsigned int postfixPattern = 0xdeadc0de; // Fill pattern for bytes following allocated blocksstatic unsigned int unusedPattern = 0xfeedface; // Fill pattern for freshly allocated blocksstatic unsigned int releasedPattern = 0xdeadbeef; // Fill pattern for deallocated blocks// ---------------------------------------------------------------------------------------------------------------------------------// Other locals// ---------------------------------------------------------------------------------------------------------------------------------static const unsigned int hashSize = 1 << hashBits;static const char *allocationTypes[] = {"Unknown", "new", "new[]", "malloc", "calloc", "realloc", "delete", "delete[]", "free"};static sAllocUnit *hashTable[hashSize];static sAllocUnit *reservoir;static unsigned int currentAllocationCount = 0;static unsigned int breakOnAllocationCount = 0;static sMStats stats;static const char *sourceFile = "??";static const char *sourceFunc = "??";static unsigned int sourceLine = 0;static bool staticDeinitTime = false;static sAllocUnit **reservoirBuffer = NULL;static unsigned int reservoirBufferSize = 0;static const char *memoryLogFile = "memory.log";static const char *memoryLeakLogFile = "memleaks.log";static void doCleanupLogOnFirstRun();// ---------------------------------------------------------------------------------------------------------------------------------// Local functions only// ---------------------------------------------------------------------------------------------------------------------------------static void log(const char *format, ...){ // Build the buffer static char buffer[2048]; va_list ap; va_start(ap, format); vsprintf(buffer, format, ap); va_end(ap); // Cleanup the log? if (cleanupLogOnFirstRun) doCleanupLogOnFirstRun(); // Open the log file FILE *fp = fopen(memoryLogFile, "ab"); // If you hit this assert, then the memory logger is unable to log information to a file (can't open the file for some // reason.) You can interrogate the variable 'buffer' to see what was supposed to be logged (but won't be.) m_assert(fp); if (!fp) return; // Spit out the data to the log fprintf(fp, "%s\r\n", buffer); fclose(fp);}// ---------------------------------------------------------------------------------------------------------------------------------static void doCleanupLogOnFirstRun(){ if (cleanupLogOnFirstRun) { unlink(memoryLogFile); cleanupLogOnFirstRun = false; // Print a header for the log time_t t = time(NULL); log("--------------------------------------------------------------------------------"); log(""); log(" %s - Memory logging file created on %s", memoryLogFile, asctime(localtime(&t))); log("--------------------------------------------------------------------------------"); log(""); log("This file contains a log of all memory operations performed during the last run."); log(""); log("Interrogate this file to track errors or to help track down memory-related"); log("issues. You can do this by tracing the allocations performed by a specific owner"); log("or by tracking a specific address through a series of allocations and"); log("reallocations."); log(""); log("There is a lot of useful information here which, when used creatively, can be"); log("extremely helpful."); log(""); log("Note that the following guides are used throughout this file:"); log(""); log(" [!] - Error"); log(" [+] - Allocation"); log(" [~] - Reallocation"); log(" [-] - Deallocation"); log(" [I] - Generic information"); log(" [F] - Failure induced for the purpose of stress-testing your application"); log(" [D] - Information used for debugging this memory manager"); log(""); log("...so, to find all errors in the file, search for \"[!]\""); log(""); log("--------------------------------------------------------------------------------"); }}// ---------------------------------------------------------------------------------------------------------------------------------static const char *sourceFileStripper(const char *sourceFile){ char *ptr = strrchr(sourceFile, '\\'); if (ptr) return ptr + 1; ptr = strrchr(sourceFile, '/'); if (ptr) return ptr + 1; return sourceFile;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -