PageRenderTime 59ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/mmgr.cpp

https://github.com/bramstein/ui-test
C++ | 1752 lines | 912 code | 363 blank | 477 comment | 168 complexity | a1bbe2bf379713ebf81fe0e941a468fe MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. // ---------------------------------------------------------------------------------------------------------------------------------
  2. //
  3. //
  4. // _ __ ___ _ __ ___ __ _ _ __ ___ _ __ _ __
  5. // | '_ ` _ \| '_ ` _ \ / _` | '__| / __| '_ \| '_ \
  6. // | | | | | | | | | | | (_| | | _ | (__| |_) | |_) |
  7. // |_| |_| |_|_| |_| |_|\__, |_| (_) \___| .__/| .__/
  8. // __/ | | | | |
  9. // |___/ |_| |_|
  10. //
  11. // Memory manager & tracking software
  12. //
  13. // Best viewed with 8-character tabs and (at least) 132 columns
  14. //
  15. // ---------------------------------------------------------------------------------------------------------------------------------
  16. //
  17. // Restrictions & freedoms pertaining to usage and redistribution of this software:
  18. //
  19. // * This software is 100% free
  20. // * If you use this software (in part or in whole) you must credit the author.
  21. // * This software may not be re-distributed (in part or in whole) in a modified
  22. // form without clear documentation on how to obtain a copy of the original work.
  23. // * You may not use this software to directly or indirectly cause harm to others.
  24. // * This software is provided as-is and without warrantee. Use at your own risk.
  25. //
  26. // For more information, visit HTTP://www.FluidStudios.com
  27. //
  28. // ---------------------------------------------------------------------------------------------------------------------------------
  29. // Originally created on 12/22/2000 by Paul Nettle
  30. //
  31. // Copyright 2000, Fluid Studios, Inc., all rights reserved.
  32. // ---------------------------------------------------------------------------------------------------------------------------------
  33. //
  34. // !!IMPORTANT!!
  35. //
  36. // This software is self-documented with periodic comments. Before you start using this software, perform a search for the string
  37. // "-DOC-" to locate pertinent information about how to use this software.
  38. //
  39. // You are also encouraged to read the comment blocks throughout this source file. They will help you understand how this memory
  40. // tracking software works, so you can better utilize it within your applications.
  41. //
  42. // NOTES:
  43. //
  44. // 1. If you get compiler errors having to do with set_new_handler, then go through this source and search/replace
  45. // "std::set_new_handler" with "set_new_handler".
  46. //
  47. // 2. This code purposely uses no external routines that allocate RAM (other than the raw allocation routines, such as malloc). We
  48. // do this because we want this to be as self-contained as possible. As an example, we don't use assert, because when running
  49. // under WIN32, the assert brings up a dialog box, which allocates RAM. Doing this in the middle of an allocation would be bad.
  50. //
  51. // 3. When trying to override new/delete under MFC (which has its own version of global new/delete) the linker will complain. In
  52. // order to fix this error, use the compiler option: /FORCE, which will force it to build an executable even with linker errors.
  53. // Be sure to check those errors each time you compile, otherwise, you may miss a valid linker error.
  54. //
  55. // 4. If you see something that looks odd to you or seems like a strange way of going about doing something, then consider that this
  56. // code was carefully thought out. If something looks odd, then just assume I've got a good reason for doing it that way (an
  57. // example is the use of the class MemStaticTimeTracker.)
  58. //
  59. // 5. With MFC applications, you will need to comment out any occurance of "#define new DEBUG_NEW" from all source files.
  60. //
  61. // 6. Include file dependencies are _very_important_ for getting the MMGR to integrate nicely into your application. Be careful if
  62. // you're including standard includes from within your own project inclues; that will break this very specific dependency order.
  63. // It should look like this:
  64. //
  65. // #include <stdio.h> // Standard includes MUST come first
  66. // #include <stdlib.h> //
  67. // #include <streamio> //
  68. //
  69. // #include "mmgr.h" // mmgr.h MUST come next
  70. //
  71. // #include "myfile1.h" // Project includes MUST come last
  72. // #include "myfile2.h" //
  73. // #include "myfile3.h" //
  74. //
  75. // ---------------------------------------------------------------------------------------------------------------------------------
  76. //#include "stdafx.h"
  77. #include <iostream>
  78. #include <stdio.h>
  79. #include <stdlib.h>
  80. #include <assert.h>
  81. #include <string.h>
  82. #include <time.h>
  83. #include <stdarg.h>
  84. #include <new>
  85. #ifndef WIN32
  86. #include <unistd.h>
  87. #endif
  88. #include "mmgr.h"
  89. // ---------------------------------------------------------------------------------------------------------------------------------
  90. // -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
  91. // very good reasons... like making bugs obvious as early as possible.) Some people may be inclined to remove this memory tracking
  92. // software if it causes crashes that didn't exist previously. In reality, these new crashes are the BEST reason for using this
  93. // software!
  94. //
  95. // Whether this software causes your application to crash, or if it reports errors, you need to be able to TRUST this software. To
  96. // this end, you are given some very simple debugging tools.
  97. //
  98. // The quickest way to locate problems is to enable the STRESS_TEST macro (below.) This should catch 95% of the crashes before they
  99. // occur by validating every allocation each time this memory manager performs an allocation function. If that doesn't work, keep
  100. // reading...
  101. //
  102. // If you enable the TEST_MEMORY_MANAGER #define (below), this memory manager will log an entry in the memory.log file each time it
  103. // enters and exits one of its primary allocation handling routines. Each call that succeeds should place an "ENTER" and an "EXIT"
  104. // into the log. If the program crashes within the memory manager, it will log an "ENTER", but not an "EXIT". The log will also
  105. // report the name of the routine.
  106. //
  107. // Just because this memory manager crashes does not mean that there is a bug here! First, an application could inadvertantly damage
  108. // the heap, causing malloc(), realloc() or free() to crash. Also, an application could inadvertantly damage some of the memory used
  109. // by this memory tracking software, causing it to crash in much the same way that a damaged heap would affect the standard
  110. // allocation routines.
  111. //
  112. // 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
  113. // crashing. You can do this by adding log() entries throughout the routine that crashes, repeating this process until you narrow
  114. // 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
  115. // bother contacting me, your application has damaged the heap. You can help find the culprit in your code by enabling the
  116. // STRESS_TEST macro (below.)
  117. //
  118. // If you truely suspect a bug in this memory manager (and you had better be sure about it! :) you can contact me at
  119. // midnight@FluidStudios.com. Before you do, however, check for a newer version at:
  120. //
  121. // http://www.FluidStudios.com/publications.html
  122. //
  123. // When using this debugging aid, make sure that you are NOT setting the alwaysLogAll variable on, otherwise the log could be
  124. // cluttered and hard to read.
  125. // ---------------------------------------------------------------------------------------------------------------------------------
  126. //#define TEST_MEMORY_MANAGER
  127. // ---------------------------------------------------------------------------------------------------------------------------------
  128. // -DOC- Enable this sucker if you really want to stress-test your app's memory usage, or to help find hard-to-find bugs
  129. // ---------------------------------------------------------------------------------------------------------------------------------
  130. //#define STRESS_TEST
  131. // ---------------------------------------------------------------------------------------------------------------------------------
  132. // -DOC- Enable this sucker if you want to stress-test your app's error-handling. Set RANDOM_FAIL to the percentage of failures you
  133. // want to test with (0 = none, >100 = all failures).
  134. // ---------------------------------------------------------------------------------------------------------------------------------
  135. //#define RANDOM_FAILURE 10.0
  136. // ---------------------------------------------------------------------------------------------------------------------------------
  137. // -DOC- Locals -- modify these flags to suit your needs
  138. // ---------------------------------------------------------------------------------------------------------------------------------
  139. #ifdef STRESS_TEST
  140. static const unsigned int hashBits = 12;
  141. static bool randomWipe = true;
  142. static bool alwaysValidateAll = true;
  143. static bool alwaysLogAll = true;
  144. static bool alwaysWipeAll = true;
  145. static bool cleanupLogOnFirstRun = true;
  146. static const unsigned int paddingSize = 1024; // An extra 8K per allocation!
  147. #else
  148. static const unsigned int hashBits = 12;
  149. static bool randomWipe = false;
  150. static bool alwaysValidateAll = false;
  151. static bool alwaysLogAll = false;
  152. static bool alwaysWipeAll = true;
  153. static bool cleanupLogOnFirstRun = true;
  154. static const unsigned int paddingSize = 4;
  155. #endif
  156. // ---------------------------------------------------------------------------------------------------------------------------------
  157. // We define our own assert, because we don't want to bring up an assertion dialog, since that allocates RAM. Our new assert
  158. // simply declares a forced breakpoint.
  159. //
  160. // The BEOS assert added by Arvid Norberg <arvid@iname.com>.
  161. // ---------------------------------------------------------------------------------------------------------------------------------
  162. #ifdef WIN32
  163. #ifdef _DEBUG
  164. #define m_assert(x) if ((x) == false) __asm { int 3 }
  165. #else
  166. #define m_assert(x) {}
  167. #endif
  168. #elif defined(__BEOS__)
  169. #ifdef DEBUG
  170. extern void debugger(const char *message);
  171. #define m_assert(x) if ((x) == false) debugger("mmgr: assert failed")
  172. #else
  173. #define m_assert(x) {}
  174. #endif
  175. #else // Linux uses assert, which we can use safely, since it doesn't bring up a dialog within the program.
  176. #define m_assert(cond) assert(cond)
  177. #endif
  178. // ---------------------------------------------------------------------------------------------------------------------------------
  179. // Here, we turn off our macros because any place in this source file where the word 'new' or the word 'delete' (etc.)
  180. // appear will be expanded by the macro. So to avoid problems using them within this source file, we'll just #undef them.
  181. // ---------------------------------------------------------------------------------------------------------------------------------
  182. #undef new
  183. #undef delete
  184. #undef malloc
  185. #undef calloc
  186. #undef realloc
  187. #undef free
  188. // ---------------------------------------------------------------------------------------------------------------------------------
  189. // Defaults for the constants & statics in the MemoryManager class
  190. // ---------------------------------------------------------------------------------------------------------------------------------
  191. const unsigned int m_alloc_unknown = 0;
  192. const unsigned int m_alloc_new = 1;
  193. const unsigned int m_alloc_new_array = 2;
  194. const unsigned int m_alloc_malloc = 3;
  195. const unsigned int m_alloc_calloc = 4;
  196. const unsigned int m_alloc_realloc = 5;
  197. const unsigned int m_alloc_delete = 6;
  198. const unsigned int m_alloc_delete_array = 7;
  199. const unsigned int m_alloc_free = 8;
  200. // ---------------------------------------------------------------------------------------------------------------------------------
  201. // -DOC- Get to know these values. They represent the values that will be used to fill unused and deallocated RAM.
  202. // ---------------------------------------------------------------------------------------------------------------------------------
  203. static unsigned int prefixPattern = 0xbaadf00d; // Fill pattern for bytes preceeding allocated blocks
  204. static unsigned int postfixPattern = 0xdeadc0de; // Fill pattern for bytes following allocated blocks
  205. static unsigned int unusedPattern = 0xfeedface; // Fill pattern for freshly allocated blocks
  206. static unsigned int releasedPattern = 0xdeadbeef; // Fill pattern for deallocated blocks
  207. // ---------------------------------------------------------------------------------------------------------------------------------
  208. // Other locals
  209. // ---------------------------------------------------------------------------------------------------------------------------------
  210. static const unsigned int hashSize = 1 << hashBits;
  211. static const char *allocationTypes[] = {"Unknown",
  212. "new", "new[]", "malloc", "calloc",
  213. "realloc", "delete", "delete[]", "free"};
  214. static sAllocUnit *hashTable[hashSize];
  215. static sAllocUnit *reservoir;
  216. static unsigned int currentAllocationCount = 0;
  217. static unsigned int breakOnAllocationCount = 0;
  218. static sMStats stats;
  219. static const char *sourceFile = "??";
  220. static const char *sourceFunc = "??";
  221. static unsigned int sourceLine = 0;
  222. static bool staticDeinitTime = false;
  223. static sAllocUnit **reservoirBuffer = NULL;
  224. static unsigned int reservoirBufferSize = 0;
  225. static const char *memoryLogFile = "memory.log";
  226. static const char *memoryLeakLogFile = "memleaks.log";
  227. static void doCleanupLogOnFirstRun();
  228. // ---------------------------------------------------------------------------------------------------------------------------------
  229. // Local functions only
  230. // ---------------------------------------------------------------------------------------------------------------------------------
  231. static void log(const char *format, ...)
  232. {
  233. // Cleanup the log?
  234. if (cleanupLogOnFirstRun) doCleanupLogOnFirstRun();
  235. // Build the buffer
  236. static char buffer[2048];
  237. va_list ap;
  238. va_start(ap, format);
  239. vsprintf(buffer, format, ap);
  240. va_end(ap);
  241. // Open the log file
  242. FILE *fp = fopen(memoryLogFile, "ab");
  243. // If you hit this assert, then the memory logger is unable to log information to a file (can't open the file for some
  244. // reason.) You can interrogate the variable 'buffer' to see what was supposed to be logged (but won't be.)
  245. m_assert(fp);
  246. if (!fp) return;
  247. // Spit out the data to the log
  248. fprintf(fp, "%s\r\n", buffer);
  249. fclose(fp);
  250. }
  251. // ---------------------------------------------------------------------------------------------------------------------------------
  252. static void doCleanupLogOnFirstRun()
  253. {
  254. if (cleanupLogOnFirstRun)
  255. {
  256. unlink(memoryLogFile);
  257. cleanupLogOnFirstRun = false;
  258. // Print a header for the log
  259. time_t t = time(NULL);
  260. log("--------------------------------------------------------------------------------");
  261. log("");
  262. log(" %s - Memory logging file created on %s", memoryLogFile, asctime(localtime(&t)));
  263. log("--------------------------------------------------------------------------------");
  264. log("");
  265. log("This file contains a log of all memory operations performed during the last run.");
  266. log("");
  267. log("Interrogate this file to track errors or to help track down memory-related");
  268. log("issues. You can do this by tracing the allocations performed by a specific owner");
  269. log("or by tracking a specific address through a series of allocations and");
  270. log("reallocations.");
  271. log("");
  272. log("There is a lot of useful information here which, when used creatively, can be");
  273. log("extremely helpful.");
  274. log("");
  275. log("Note that the following guides are used throughout this file:");
  276. log("");
  277. log(" [!] - Error");
  278. log(" [+] - Allocation");
  279. log(" [~] - Reallocation");
  280. log(" [-] - Deallocation");
  281. log(" [I] - Generic information");
  282. log(" [F] - Failure induced for the purpose of stress-testing your application");
  283. log(" [D] - Information used for debugging this memory manager");
  284. log("");
  285. log("...so, to find all errors in the file, search for \"[!]\"");
  286. log("");
  287. log("--------------------------------------------------------------------------------");
  288. }
  289. }
  290. // ---------------------------------------------------------------------------------------------------------------------------------
  291. static const char *sourceFileStripper(const char *sourceFile)
  292. {
  293. char *ptr = strrchr(sourceFile, '\\');
  294. if (ptr) return ptr + 1;
  295. ptr = strrchr(sourceFile, '/');
  296. if (ptr) return ptr + 1;
  297. return sourceFile;
  298. }
  299. // ---------------------------------------------------------------------------------------------------------------------------------
  300. static const char *ownerString(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc)
  301. {
  302. static char str[90];
  303. memset(str, 0, sizeof(str));
  304. sprintf(str, "%s(%05d)::%s", sourceFileStripper(sourceFile), sourceLine, sourceFunc);
  305. return str;
  306. }
  307. // ---------------------------------------------------------------------------------------------------------------------------------
  308. static const char *insertCommas(unsigned int value)
  309. {
  310. static char str[30];
  311. memset(str, 0, sizeof(str));
  312. sprintf(str, "%u", value);
  313. if (strlen(str) > 3)
  314. {
  315. memmove(&str[strlen(str)-3], &str[strlen(str)-4], 4);
  316. str[strlen(str) - 4] = ',';
  317. }
  318. if (strlen(str) > 7)
  319. {
  320. memmove(&str[strlen(str)-7], &str[strlen(str)-8], 8);
  321. str[strlen(str) - 8] = ',';
  322. }
  323. if (strlen(str) > 11)
  324. {
  325. memmove(&str[strlen(str)-11], &str[strlen(str)-12], 12);
  326. str[strlen(str) - 12] = ',';
  327. }
  328. return str;
  329. }
  330. // ---------------------------------------------------------------------------------------------------------------------------------
  331. static const char *memorySizeString(unsigned long size)
  332. {
  333. static char str[90];
  334. if (size > (1024*1024)) sprintf(str, "%10s (%7.2fM)", insertCommas(size), static_cast<float>(size) / (1024.0f * 1024.0f));
  335. else if (size > 1024) sprintf(str, "%10s (%7.2fK)", insertCommas(size), static_cast<float>(size) / 1024.0f);
  336. else sprintf(str, "%10s bytes ", insertCommas(size));
  337. return str;
  338. }
  339. // ---------------------------------------------------------------------------------------------------------------------------------
  340. static sAllocUnit *findAllocUnit(const void *reportedAddress)
  341. {
  342. // Just in case...
  343. m_assert(reportedAddress != NULL);
  344. // Use the address to locate the hash index. Note that we shift off the lower four bits. This is because most allocated
  345. // addresses will be on four-, eight- or even sixteen-byte boundaries. If we didn't do this, the hash index would not have
  346. // very good coverage.
  347. unsigned int hashIndex = (reinterpret_cast<unsigned int>(const_cast<void *>(reportedAddress)) >> 4) & (hashSize - 1);
  348. sAllocUnit *ptr = hashTable[hashIndex];
  349. while(ptr)
  350. {
  351. if (ptr->reportedAddress == reportedAddress) return ptr;
  352. ptr = ptr->next;
  353. }
  354. return NULL;
  355. }
  356. // ---------------------------------------------------------------------------------------------------------------------------------
  357. static size_t calculateActualSize(const size_t reportedSize)
  358. {
  359. // We use DWORDS as our padding, and a long is guaranteed to be 4 bytes, but an int is not (ANSI defines an int as
  360. // being the standard word size for a processor; on a 32-bit machine, that's 4 bytes, but on a 64-bit machine, it's
  361. // 8 bytes, which means an int can actually be larger than a long.)
  362. return reportedSize + paddingSize * sizeof(long) * 2;
  363. }
  364. // ---------------------------------------------------------------------------------------------------------------------------------
  365. static size_t calculateReportedSize(const size_t actualSize)
  366. {
  367. // We use DWORDS as our padding, and a long is guaranteed to be 4 bytes, but an int is not (ANSI defines an int as
  368. // being the standard word size for a processor; on a 32-bit machine, that's 4 bytes, but on a 64-bit machine, it's
  369. // 8 bytes, which means an int can actually be larger than a long.)
  370. return actualSize - paddingSize * sizeof(long) * 2;
  371. }
  372. // ---------------------------------------------------------------------------------------------------------------------------------
  373. static void *calculateReportedAddress(const void *actualAddress)
  374. {
  375. // We allow this...
  376. if (!actualAddress) return NULL;
  377. // JUst account for the padding
  378. return reinterpret_cast<void *>(const_cast<char *>(reinterpret_cast<const char *>(actualAddress) + sizeof(long) * paddingSize));
  379. }
  380. // ---------------------------------------------------------------------------------------------------------------------------------
  381. static void wipeWithPattern(sAllocUnit *allocUnit, unsigned long pattern, const unsigned int originalReportedSize = 0)
  382. {
  383. // For a serious test run, we use wipes of random a random value. However, if this causes a crash, we don't want it to
  384. // crash in a differnt place each time, so we specifically DO NOT call srand. If, by chance your program calls srand(),
  385. // you may wish to disable that when running with a random wipe test. This will make any crashes more consistent so they
  386. // can be tracked down easier.
  387. if (randomWipe)
  388. {
  389. pattern = ((rand() & 0xff) << 24) | ((rand() & 0xff) << 16) | ((rand() & 0xff) << 8) | (rand() & 0xff);
  390. }
  391. // -DOC- We should wipe with 0's if we're not in debug mode, so we can help hide bugs if possible when we release the
  392. // product. So uncomment the following line for releases.
  393. //
  394. // Note that the "alwaysWipeAll" should be turned on for this to have effect, otherwise it won't do much good. But we'll
  395. // leave it this way (as an option) because this does slow things down.
  396. // pattern = 0;
  397. // This part of the operation is optional
  398. if (alwaysWipeAll && allocUnit->reportedSize > originalReportedSize)
  399. {
  400. // Fill the bulk
  401. long *lptr = reinterpret_cast<long *>(reinterpret_cast<char *>(allocUnit->reportedAddress) + originalReportedSize);
  402. int length = static_cast<int>(allocUnit->reportedSize - originalReportedSize);
  403. int i;
  404. for (i = 0; i < (length >> 2); i++, lptr++)
  405. {
  406. *lptr = pattern;
  407. }
  408. // Fill the remainder
  409. unsigned int shiftCount = 0;
  410. char *cptr = reinterpret_cast<char *>(lptr);
  411. for (i = 0; i < (length & 0x3); i++, cptr++, shiftCount += 8)
  412. {
  413. *cptr = static_cast<char>((pattern & (0xff << shiftCount)) >> shiftCount);
  414. }
  415. }
  416. // Write in the prefix/postfix bytes
  417. long *pre = reinterpret_cast<long *>(allocUnit->actualAddress);
  418. long *post = reinterpret_cast<long *>(reinterpret_cast<char *>(allocUnit->actualAddress) + allocUnit->actualSize - paddingSize * sizeof(long));
  419. for (unsigned int i = 0; i < paddingSize; i++, pre++, post++)
  420. {
  421. *pre = prefixPattern;
  422. *post = postfixPattern;
  423. }
  424. }
  425. // ---------------------------------------------------------------------------------------------------------------------------------
  426. static void dumpAllocations(FILE *fp)
  427. {
  428. fprintf(fp, "Alloc. Addr Size Addr Size BreakOn BreakOn \r\n");
  429. fprintf(fp, "Number Reported Reported Actual Actual Unused Method Dealloc Realloc Allocated by \r\n");
  430. fprintf(fp, "------ ---------- ---------- ---------- ---------- ---------- -------- ------- ------- --------------------------------------------------- \r\n");
  431. for (unsigned int i = 0; i < hashSize; i++)
  432. {
  433. sAllocUnit *ptr = hashTable[i];
  434. while(ptr)
  435. {
  436. fprintf(fp, "%06d 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X %-8s %c %c %s\r\n",
  437. ptr->allocationNumber,
  438. reinterpret_cast<unsigned int>(ptr->reportedAddress), ptr->reportedSize,
  439. reinterpret_cast<unsigned int>(ptr->actualAddress), ptr->actualSize,
  440. m_calcUnused(ptr),
  441. allocationTypes[ptr->allocationType],
  442. ptr->breakOnDealloc ? 'Y':'N',
  443. ptr->breakOnRealloc ? 'Y':'N',
  444. ownerString(ptr->sourceFile, ptr->sourceLine, ptr->sourceFunc));
  445. ptr = ptr->next;
  446. }
  447. }
  448. }
  449. // ---------------------------------------------------------------------------------------------------------------------------------
  450. static void dumpLeakReport()
  451. {
  452. // Open the report file
  453. FILE *fp = fopen(memoryLeakLogFile, "w+b");
  454. // If you hit this assert, then the memory report generator is unable to log information to a file (can't open the file for
  455. // some reason.)
  456. m_assert(fp);
  457. if (!fp) return;
  458. // Any leaks?
  459. // Header
  460. static char timeString[25];
  461. memset(timeString, 0, sizeof(timeString));
  462. time_t t = time(NULL);
  463. struct tm *tme = localtime(&t);
  464. fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
  465. fprintf(fp, "| Memory leak report for: %02d/%02d/%04d %02d:%02d:%02d |\r\n", tme->tm_mon + 1, tme->tm_mday, tme->tm_year + 1900, tme->tm_hour, tme->tm_min, tme->tm_sec);
  466. fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
  467. fprintf(fp, "\r\n");
  468. fprintf(fp, "\r\n");
  469. if (stats.totalAllocUnitCount)
  470. {
  471. fprintf(fp, "%d memory leak%s found:\r\n", stats.totalAllocUnitCount, stats.totalAllocUnitCount == 1 ? "":"s");
  472. }
  473. else
  474. {
  475. fprintf(fp, "Congratulations! No memory leaks found!\r\n");
  476. // We can finally free up our own memory allocations
  477. if (reservoirBuffer)
  478. {
  479. for (unsigned int i = 0; i < reservoirBufferSize; i++)
  480. {
  481. free(reservoirBuffer[i]);
  482. }
  483. free(reservoirBuffer);
  484. reservoirBuffer = 0;
  485. reservoirBufferSize = 0;
  486. reservoir = NULL;
  487. }
  488. }
  489. fprintf(fp, "\r\n");
  490. if (stats.totalAllocUnitCount)
  491. {
  492. dumpAllocations(fp);
  493. }
  494. fclose(fp);
  495. }
  496. // ---------------------------------------------------------------------------------------------------------------------------------
  497. // We use a static class to let us know when we're in the midst of static deinitialization
  498. // ---------------------------------------------------------------------------------------------------------------------------------
  499. class MemStaticTimeTracker
  500. {
  501. public:
  502. MemStaticTimeTracker() {doCleanupLogOnFirstRun();}
  503. ~MemStaticTimeTracker() {staticDeinitTime = true; dumpLeakReport();}
  504. };
  505. static MemStaticTimeTracker mstt;
  506. // ---------------------------------------------------------------------------------------------------------------------------------
  507. // -DOC- Flags & options -- Call these routines to enable/disable the following options
  508. // ---------------------------------------------------------------------------------------------------------------------------------
  509. bool &m_alwaysValidateAll()
  510. {
  511. // Force a validation of all allocation units each time we enter this software
  512. return alwaysValidateAll;
  513. }
  514. // ---------------------------------------------------------------------------------------------------------------------------------
  515. bool &m_alwaysLogAll()
  516. {
  517. // Force a log of every allocation & deallocation into memory.log
  518. return alwaysLogAll;
  519. }
  520. // ---------------------------------------------------------------------------------------------------------------------------------
  521. bool &m_alwaysWipeAll()
  522. {
  523. // Force this software to always wipe memory with a pattern when it is being allocated/dallocated
  524. return alwaysWipeAll;
  525. }
  526. // ---------------------------------------------------------------------------------------------------------------------------------
  527. bool &m_randomeWipe()
  528. {
  529. // Force this software to use a random pattern when wiping memory -- good for stress testing
  530. return randomWipe;
  531. }
  532. // ---------------------------------------------------------------------------------------------------------------------------------
  533. // -DOC- Simply call this routine with the address of an allocated block of RAM, to cause it to force a breakpoint when it is
  534. // reallocated.
  535. // ---------------------------------------------------------------------------------------------------------------------------------
  536. bool &m_breakOnRealloc(void *reportedAddress)
  537. {
  538. // Locate the existing allocation unit
  539. sAllocUnit *au = findAllocUnit(reportedAddress);
  540. // If you hit this assert, you tried to set a breakpoint on reallocation for an address that doesn't exist. Interrogate the
  541. // stack frame or the variable 'au' to see which allocation this is.
  542. m_assert(au != NULL);
  543. // If you hit this assert, you tried to set a breakpoint on reallocation for an address that wasn't allocated in a way that
  544. // is compatible with reallocation.
  545. m_assert(au->allocationType == m_alloc_malloc ||
  546. au->allocationType == m_alloc_calloc ||
  547. au->allocationType == m_alloc_realloc);
  548. return au->breakOnRealloc;
  549. }
  550. // ---------------------------------------------------------------------------------------------------------------------------------
  551. // -DOC- Simply call this routine with the address of an allocated block of RAM, to cause it to force a breakpoint when it is
  552. // deallocated.
  553. // ---------------------------------------------------------------------------------------------------------------------------------
  554. bool &m_breakOnDealloc(void *reportedAddress)
  555. {
  556. // Locate the existing allocation unit
  557. sAllocUnit *au = findAllocUnit(reportedAddress);
  558. // If you hit this assert, you tried to set a breakpoint on deallocation for an address that doesn't exist. Interrogate the
  559. // stack frame or the variable 'au' to see which allocation this is.
  560. m_assert(au != NULL);
  561. return au->breakOnDealloc;
  562. }
  563. // ---------------------------------------------------------------------------------------------------------------------------------
  564. // -DOC- When tracking down a difficult bug, use this routine to force a breakpoint on a specific allocation count
  565. // ---------------------------------------------------------------------------------------------------------------------------------
  566. void m_breakOnAllocation(unsigned int count)
  567. {
  568. breakOnAllocationCount = count;
  569. }
  570. // ---------------------------------------------------------------------------------------------------------------------------------
  571. // Used by the macros
  572. // ---------------------------------------------------------------------------------------------------------------------------------
  573. void m_setOwner(const char *file, const unsigned int line, const char *func)
  574. {
  575. // You're probably wondering about this...
  576. //
  577. // It's important for this memory manager to primarily work with global new/delete in their original forms (i.e. with
  578. // no extra parameters.) In order to do this, we use macros that call this function prior to operators new & delete. This
  579. // is fine... usually. Here's what actually happens when you use this macro to delete an object:
  580. //
  581. // m_setOwner(__FILE__, __LINE__, __FUNCTION__) --> object::~object() --> delete
  582. //
  583. // Note that the compiler inserts a call to the object's destructor just prior to calling our overridden operator delete.
  584. // But what happens when we delete an object whose destructor deletes another object, whose desctuctor deletes another
  585. // object? Here's a diagram (indentation follows stack depth):
  586. //
  587. // m_setOwner(...) -> ~obj1() // original call to delete obj1
  588. // m_setOwner(...) -> ~obj2() // obj1's destructor deletes obj2
  589. // m_setOwner(...) -> ~obj3() // obj2's destructor deletes obj3
  590. // ... // obj3's destructor just does some stuff
  591. // delete // back in obj2's destructor, we call delete
  592. // delete // back in obj1's destructor, we call delete
  593. // delete // back to our original call, we call delete
  594. //
  595. // Because m_setOwner() just sets up some static variables (below) it's important that each call to m_setOwner() and
  596. // successive calls to new/delete alternate. However, in this case, three calls to m_setOwner() happen in succession
  597. // followed by three calls to delete in succession (with a few calls to destructors mixed in for fun.) This means that
  598. // only the final call to delete (in this chain of events) will have the proper reporting, and the first two in the chain
  599. // will not have ANY owner-reporting information. The deletes will still work fine, we just won't know who called us.
  600. //
  601. // "Then build a stack, my friend!" you might think... but it's a very common thing that people will be working with third-
  602. // party libraries (including MFC under Windows) which is not compiled with this memory manager's macros. In those cases,
  603. // m_setOwner() is never called, and rightfully should not have the proper trace-back information. So if one of the
  604. // destructors in the chain ends up being a call to a delete from a non-mmgr-compiled library, the stack will get confused.
  605. //
  606. // I've been unable to find a solution to this problem, but at least we can detect it and report the data before we
  607. // lose it. That's what this is all about. It makes it somewhat confusing to read in the logs, but at least ALL the
  608. // information is present...
  609. //
  610. // There's a caveat here... The compiler is not required to call operator delete if the value being deleted is NULL.
  611. // In this case, any call to delete with a NULL will sill call m_setOwner(), which will make m_setOwner() think that
  612. // there is a destructor chain becuase we setup the variables, but nothing gets called to clear them. Because of this
  613. // we report a "Possible destructor chain".
  614. //
  615. // Thanks to J. Woznack (from Kodiak Interactive Software Studios -- www.kodiakgames.com) for pointing this out.
  616. if (sourceLine && alwaysLogAll)
  617. {
  618. log("[I] NOTE! Possible destructor chain: previous owner is %s", ownerString(sourceFile, sourceLine, sourceFunc));
  619. }
  620. // Okay... save this stuff off so we can keep track of the caller
  621. sourceFile = file;
  622. sourceLine = line;
  623. sourceFunc = func;
  624. }
  625. // ---------------------------------------------------------------------------------------------------------------------------------
  626. static void resetGlobals()
  627. {
  628. sourceFile = "??";
  629. sourceLine = 0;
  630. sourceFunc = "??";
  631. }
  632. // ---------------------------------------------------------------------------------------------------------------------------------
  633. // Global new/new[]
  634. //
  635. // These are the standard new/new[] operators. They are merely interface functions that operate like normal new/new[], but use our
  636. // memory tracking routines.
  637. // ---------------------------------------------------------------------------------------------------------------------------------
  638. void *operator new(size_t reportedSize)
  639. {
  640. #ifdef TEST_MEMORY_MANAGER
  641. log("[D] ENTER: new");
  642. #endif
  643. // Save these off...
  644. const char *file = sourceFile;
  645. const unsigned int line = sourceLine;
  646. const char *func = sourceFunc;
  647. // ANSI says: allocation requests of 0 bytes will still return a valid value
  648. if (reportedSize == 0) reportedSize = 1;
  649. // ANSI says: loop continuously because the error handler could possibly free up some memory
  650. for(;;)
  651. {
  652. // Try the allocation
  653. void *ptr = m_allocator(file, line, func, m_alloc_new, reportedSize);
  654. if (ptr)
  655. {
  656. #ifdef TEST_MEMORY_MANAGER
  657. log("[D] EXIT : new");
  658. #endif
  659. return ptr;
  660. }
  661. // There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then
  662. // set it back again.
  663. new_handler nh = std::set_new_handler(0);
  664. std::set_new_handler(nh);
  665. // If there is an error handler, call it
  666. if (nh)
  667. {
  668. (*nh)();
  669. }
  670. // Otherwise, throw the exception
  671. else
  672. {
  673. #ifdef TEST_MEMORY_MANAGER
  674. log("[D] EXIT : new");
  675. #endif
  676. throw std::bad_alloc();
  677. }
  678. }
  679. }
  680. // ---------------------------------------------------------------------------------------------------------------------------------
  681. void *operator new[](size_t reportedSize)
  682. {
  683. #ifdef TEST_MEMORY_MANAGER
  684. log("[D] ENTER: new[]");
  685. #endif
  686. // Save these off...
  687. const char *file = sourceFile;
  688. const unsigned int line = sourceLine;
  689. const char *func = sourceFunc;
  690. // The ANSI standard says that allocation requests of 0 bytes will still return a valid value
  691. if (reportedSize == 0) reportedSize = 1;
  692. // ANSI says: loop continuously because the error handler could possibly free up some memory
  693. for(;;)
  694. {
  695. // Try the allocation
  696. void *ptr = m_allocator(file, line, func, m_alloc_new_array, reportedSize);
  697. if (ptr)
  698. {
  699. #ifdef TEST_MEMORY_MANAGER
  700. log("[D] EXIT : new[]");
  701. #endif
  702. return ptr;
  703. }
  704. // There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then
  705. // set it back again.
  706. new_handler nh = std::set_new_handler(0);
  707. std::set_new_handler(nh);
  708. // If there is an error handler, call it
  709. if (nh)
  710. {
  711. (*nh)();
  712. }
  713. // Otherwise, throw the exception
  714. else
  715. {
  716. #ifdef TEST_MEMORY_MANAGER
  717. log("[D] EXIT : new[]");
  718. #endif
  719. throw std::bad_alloc();
  720. }
  721. }
  722. }
  723. // ---------------------------------------------------------------------------------------------------------------------------------
  724. // Other global new/new[]
  725. //
  726. // These are the standard new/new[] operators as used by Microsoft's memory tracker. We don't want them interfering with our memory
  727. // tracking efforts. Like the previous versions, these are merely interface functions that operate like normal new/new[], but use
  728. // our memory tracking routines.
  729. // ---------------------------------------------------------------------------------------------------------------------------------
  730. void *operator new(size_t reportedSize, const char *sourceFile, int sourceLine)
  731. {
  732. #ifdef TEST_MEMORY_MANAGER
  733. log("[D] ENTER: new");
  734. #endif
  735. // The ANSI standard says that allocation requests of 0 bytes will still return a valid value
  736. if (reportedSize == 0) reportedSize = 1;
  737. // ANSI says: loop continuously because the error handler could possibly free up some memory
  738. for(;;)
  739. {
  740. // Try the allocation
  741. void *ptr = m_allocator(sourceFile, sourceLine, "??", m_alloc_new, reportedSize);
  742. if (ptr)
  743. {
  744. #ifdef TEST_MEMORY_MANAGER
  745. log("[D] EXIT : new");
  746. #endif
  747. return ptr;
  748. }
  749. // There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then
  750. // set it back again.
  751. new_handler nh = std::set_new_handler(0);
  752. std::set_new_handler(nh);
  753. // If there is an error handler, call it
  754. if (nh)
  755. {
  756. (*nh)();
  757. }
  758. // Otherwise, throw the exception
  759. else
  760. {
  761. #ifdef TEST_MEMORY_MANAGER
  762. log("[D] EXIT : new");
  763. #endif
  764. throw std::bad_alloc();
  765. }
  766. }
  767. }
  768. // ---------------------------------------------------------------------------------------------------------------------------------
  769. void *operator new[](size_t reportedSize, const char *sourceFile, int sourceLine)
  770. {
  771. #ifdef TEST_MEMORY_MANAGER
  772. log("[D] ENTER: new[]");
  773. #endif
  774. // The ANSI standard says that allocation requests of 0 bytes will still return a valid value
  775. if (reportedSize == 0) reportedSize = 1;
  776. // ANSI says: loop continuously because the error handler could possibly free up some memory
  777. for(;;)
  778. {
  779. // Try the allocation
  780. void *ptr = m_allocator(sourceFile, sourceLine, "??", m_alloc_new_array, reportedSize);
  781. if (ptr)
  782. {
  783. #ifdef TEST_MEMORY_MANAGER
  784. log("[D] EXIT : new[]");
  785. #endif
  786. return ptr;
  787. }
  788. // There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then
  789. // set it back again.
  790. new_handler nh = std::set_new_handler(0);
  791. std::set_new_handler(nh);
  792. // If there is an error handler, call it
  793. if (nh)
  794. {
  795. (*nh)();
  796. }
  797. // Otherwise, throw the exception
  798. else
  799. {
  800. #ifdef TEST_MEMORY_MANAGER
  801. log("[D] EXIT : new[]");
  802. #endif
  803. throw std::bad_alloc();
  804. }
  805. }
  806. }
  807. // ---------------------------------------------------------------------------------------------------------------------------------
  808. // Global delete/delete[]
  809. //
  810. // These are the standard delete/delete[] operators. They are merely interface functions that operate like normal delete/delete[],
  811. // but use our memory tracking routines.
  812. // ---------------------------------------------------------------------------------------------------------------------------------
  813. void operator delete(void *reportedAddress)
  814. {
  815. #ifdef TEST_MEMORY_MANAGER
  816. log("[D] ENTER: delete");
  817. #endif
  818. // ANSI says: delete & delete[] allow NULL pointers (they do nothing)
  819. if (reportedAddress) m_deallocator(sourceFile, sourceLine, sourceFunc, m_alloc_delete, reportedAddress);
  820. else if (alwaysLogAll) log("[-] ----- %8s of NULL by %s", allocationTypes[m_alloc_delete], ownerString(sourceFile, sourceLine, sourceFunc));
  821. // Resetting the globals insures that if at some later time, somebody calls our memory manager from an unknown
  822. // source (i.e. they didn't include our H file) then we won't think it was the last allocation.
  823. resetGlobals();
  824. #ifdef TEST_MEMORY_MANAGER
  825. log("[D] EXIT : delete");
  826. #endif
  827. }
  828. // ---------------------------------------------------------------------------------------------------------------------------------
  829. void operator delete[](void *reportedAddress)
  830. {
  831. #ifdef TEST_MEMORY_MANAGER
  832. log("[D] ENTER: delete[]");
  833. #endif
  834. // ANSI says: delete & delete[] allow NULL pointers (they do nothing)
  835. if (reportedAddress) m_deallocator(sourceFile, sourceLine, sourceFunc, m_alloc_delete_array, reportedAddress);
  836. else if (alwaysLogAll)
  837. log("[-] ----- %8s of NULL by %s", allocationTypes[m_alloc_delete_array], ownerString(sourceFile, sourceLine, sourceFunc));
  838. // Resetting the globals insures that if at some later time, somebody calls our memory manager from an unknown
  839. // source (i.e. they didn't include our H file) then we won't think it was the last allocation.
  840. resetGlobals();
  841. #ifdef TEST_MEMORY_MANAGER
  842. log("[D] EXIT : delete[]");
  843. #endif
  844. }
  845. // ---------------------------------------------------------------------------------------------------------------------------------
  846. // Allocate memory and track it
  847. // ---------------------------------------------------------------------------------------------------------------------------------
  848. void *m_allocator(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc, const unsigned int allocationType, const size_t reportedSize)
  849. {
  850. try
  851. {
  852. #ifdef TEST_MEMORY_MANAGER
  853. log("[D] ENTER: m_allocator()");
  854. #endif
  855. // Increase our allocation count
  856. currentAllocationCount++;
  857. // Log the request
  858. if (alwaysLogAll) log("[+] %05d %8s of size 0x%08X(%08d) by %s", currentAllocationCount, allocationTypes[allocationType], reportedSize, reportedSize, ownerString(sourceFile, sourceLine, sourceFunc));
  859. // If you hit this assert, you requested a breakpoint on a specific allocation count
  860. m_assert(currentAllocationCount != breakOnAllocationCount);
  861. // If necessary, grow the reservoir of unused allocation units
  862. if (!reservoir)
  863. {
  864. // Allocate 256 reservoir elements
  865. reservoir = (sAllocUnit *) malloc(sizeof(sAllocUnit) * 256);
  866. // If you hit this assert, then the memory manager failed to allocate internal memory for tracking the
  867. // allocations
  868. m_assert(reservoir != NULL);
  869. // Danger Will Robinson!
  870. if (reservoir == NULL) throw "Unable to allocate RAM for internal memory tracking data";
  871. // Build a linked-list of the elements in our reservoir
  872. memset(reservoir, 0, sizeof(sAllocUnit) * 256);
  873. for (unsigned int i = 0; i < 256 - 1; i++)
  874. {
  875. reservoir[i].next = &reservoir[i+1];
  876. }
  877. // Add this address to our reservoirBuffer so we can free it later
  878. sAllocUnit **temp = (sAllocUnit **) realloc(reservoirBuffer, (reservoirBufferSize + 1) * sizeof(sAllocUnit *));
  879. m_assert(temp);
  880. if (temp)
  881. {
  882. reservoirBuffer = temp;
  883. reservoirBuffer[reservoirBufferSize++] = reservoir;
  884. }
  885. }
  886. // Logical flow says this should never happen...
  887. m_assert(reservoir != NULL);
  888. // Grab a new allocaton unit from the front of the reservoir
  889. sAllocUnit *au = reservoir;
  890. reservoir = au->next;
  891. // Populate it with some real data
  892. memset(au, 0, sizeof(sAllocUnit));
  893. au->actualSize = calculateActualSize(reportedSize);
  894. #ifdef RANDOM_FAILURE
  895. double a = rand();
  896. double b = RAND_MAX / 100.0 * RANDOM_FAILURE;
  897. if (a > b)
  898. {
  899. au->actualAddress = malloc(au->actualSize);
  900. }
  901. else
  902. {
  903. log("[F] Random faiure");
  904. au->actualAddress = NULL;
  905. }
  906. #else
  907. au->actualAddress = malloc(au->actualSize);
  908. #endif
  909. au->reportedSize = reportedSize;
  910. au->reportedAddress = calculateReportedAddress(au->actualAddress);
  911. au->allocationType = allocationType;
  912. au->sourceLine = sourceLine;
  913. au->allocationNumber = currentAllocationCount;
  914. if (sourceFile) strncpy(au->sourceFile, sourceFileStripper(sourceFile), sizeof(au->sourceFile) - 1);
  915. else strcpy (au->sourceFile, "??");
  916. if (sourceFunc) strncpy(au->sourceFunc, sourceFunc, sizeof(au->sourceFunc) - 1);
  917. else strcpy (au->sourceFunc, "??");
  918. // We don't want to assert with random failures, because we want the application to deal with them.
  919. #ifndef RANDOM_FAILURE
  920. // If you hit this assert, then the requested allocation simply failed (you're out of memory.) Interrogate the
  921. // variable 'au' or the stack frame to see what you were trying to do.
  922. m_assert(au->actualAddress != NULL);
  923. #endif
  924. if (au->actualAddress == NULL)
  925. {
  926. throw "Request for allocation failed. Out of memory.";
  927. }
  928. // If you hit this assert, then this allocation was made from a source that isn't setup to use this memory tracking
  929. // software, use the stack frame to locate the source and include our H file.
  930. m_assert(allocationType != m_alloc_unknown);
  931. // Insert the new allocation into the hash table
  932. unsigned int hashIndex = (reinterpret_cast<unsigned int>(au->reportedAddress) >> 4) & (hashSize - 1);
  933. if (hashTable[hashIndex]) hashTable[hashIndex]->prev = au;
  934. au->next = hashTable[hashIndex];
  935. au->prev = NULL;
  936. hashTable[hashIndex] = au;
  937. // Account for the new allocatin unit in our stats
  938. stats.totalReportedMemory += static_cast<unsigned int>(au->reportedSize);
  939. stats.totalActualMemory += static_cast<unsigned int>(au->actualSize);
  940. stats.totalAllocUnitCount++;
  941. if (stats.totalReportedMemory > stats.peakReportedMemory) stats.peakReportedMemory = stats.totalReportedMemory;
  942. if (stats.totalActualMemory > stats.peakActualMemory) stats.peakActualMemory = stats.totalActualMemory;
  943. if (stats.totalAllocUnitCount > stats.peakAllocUnitCount) stats.peakAllocUnitCount = stats.totalAllocUnitCount;
  944. stats.accumulatedReportedMemory += static_cast<unsigned int>(au->reportedSize);
  945. stats.accumulatedActualMemory += static_cast<unsigned int>(au->actualSize);
  946. stats.accumulatedAllocUnitCount++;
  947. // Prepare the allocation unit for use (wipe it with recognizable garbage)
  948. wipeWithPattern(au, unusedPattern);
  949. // calloc() expects the reported memory address range to be filled with 0's
  950. if (allocationType == m_alloc_calloc)
  951. {
  952. memset(au->reportedAddress, 0, au->reportedSize);
  953. }
  954. // Validate every single allocated unit in memory
  955. if (alwaysValidateAll) m_validateAllAllocUnits();
  956. // Log the result
  957. if (alwaysLogAll) log("[+] ----> addr 0x%08X", reinterpret_cast<unsigned int>(au->reportedAddress));
  958. // Resetting the globals insures that if at some later time, somebody calls our memory manager from an unknown
  959. // source (i.e. they didn't include our H file) then we won't think it was the last allocation.
  960. resetGlobals();
  961. // Return the (reported) address of the new allocation unit
  962. #ifdef TEST_MEMORY_MANAGER
  963. log("[D] EXIT : m_allocator()");
  964. #endif
  965. return au->reportedAddress;
  966. }
  967. catch(const char *err)
  968. {
  969. // Deal with the errors
  970. log("[!] %s", err);
  971. resetGlobals();
  972. #ifdef TEST_MEMORY_MANAGER
  973. log("[D] EXIT : m_allocator()");
  974. #endif
  975. return NULL;
  976. }
  977. }
  978. // ---------------------------------------------------------------------------------------------------------------------------------
  979. // Reallocate memory and track it
  980. // ---------------------------------------------------------------------------------------------------------------------------------
  981. void *m_reallocator(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc, const unsigned int reallocationType, const size_t reportedSize, void *reportedAddress)
  982. {
  983. try
  984. {
  985. #ifdef TEST_MEMORY_MANAGER
  986. log("[D] ENTER: m_reallocator()");
  987. #endif
  988. // C…

Large files files are truncated, but you can click here to view the full file