00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078 #pragma warning(disable : 4996)
00079 #pragma warning(disable : 4311)
00080
00081 #include <iostream>
00082 #include <stdio.h>
00083 #include <stdlib.h>
00084 #include <assert.h>
00085 #include <string.h>
00086 #include <time.h>
00087 #include <stdarg.h>
00088 #include <new>
00089
00090 #ifndef WIN32
00091 #include <unistd.h>
00092 #endif
00093
00094 #include "mmgr.h"
00095
00096 #ifdef USE_MMGR
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155 #ifdef STRESS_TEST
00156 static const unsigned int hashBits = 12;
00157 static bool randomWipe = true;
00158 static bool alwaysValidateAll = true;
00159 static bool alwaysLogAll = true;
00160 static bool alwaysWipeAll = true;
00161 static bool cleanupLogOnFirstRun = true;
00162 static const unsigned int paddingSize = 1024;
00163 #else
00164 static const unsigned int hashBits = 12;
00165 static bool randomWipe = false;
00166 static bool alwaysValidateAll = false;
00167 static bool alwaysLogAll = false;
00168 static bool alwaysWipeAll = true;
00169 static bool cleanupLogOnFirstRun = true;
00170 static const unsigned int paddingSize = 4;
00171 #endif
00172
00173
00174
00175
00176
00177
00178
00179
00180 #ifdef WIN32
00181 #ifdef _DEBUG
00182 #define m_assert(x) if ((x) == false) __asm { int 3 }
00183 #else
00184 #define m_assert(x) {}
00185 #endif
00186 #elif defined(__BEOS__)
00187 #ifdef DEBUG
00188 extern void debugger(const char *message);
00189 #define m_assert(x) if ((x) == false) debugger("mmgr: assert failed")
00190 #else
00191 #define m_assert(x) {}
00192 #endif
00193 #else // Linux uses assert, which we can use safely, since it doesn't bring up a dialog within the program.
00194 #define m_assert(cond) assert(cond)
00195 #endif
00196
00197
00198
00199
00200
00201
00202 #undef new
00203 #undef delete
00204 #undef malloc
00205 #undef calloc
00206 #undef realloc
00207 #undef free
00208
00209
00210
00211
00212
00213 const unsigned int m_alloc_unknown = 0;
00214 const unsigned int m_alloc_new = 1;
00215 const unsigned int m_alloc_new_array = 2;
00216 const unsigned int m_alloc_malloc = 3;
00217 const unsigned int m_alloc_calloc = 4;
00218 const unsigned int m_alloc_realloc = 5;
00219 const unsigned int m_alloc_delete = 6;
00220 const unsigned int m_alloc_delete_array = 7;
00221 const unsigned int m_alloc_free = 8;
00222
00223
00224
00225
00226
00227 static unsigned int prefixPattern = 0xbaadf00d;
00228 static unsigned int postfixPattern = 0xdeadc0de;
00229 static unsigned int unusedPattern = 0xfeedface;
00230 static unsigned int releasedPattern = 0xdeadbeef;
00231
00232
00233
00234
00235
00236 static const unsigned int hashSize = 1 << hashBits;
00237 static const char *allocationTypes[] = {"Unknown",
00238 "new", "new[]", "malloc", "calloc",
00239 "realloc", "delete", "delete[]", "free"};
00240 static sAllocUnit *hashTable[hashSize];
00241 static sAllocUnit *reservoir;
00242 static unsigned int currentAllocationCount = 0;
00243 static unsigned int breakOnAllocationCount = 0;
00244 static sMStats stats;
00245 static const char *sourceFile = "??";
00246 static const char *sourceFunc = "??";
00247 static unsigned int sourceLine = 0;
00248 static bool staticDeinitTime = false;
00249 static sAllocUnit **reservoirBuffer = NULL;
00250 static unsigned int reservoirBufferSize = 0;
00251 static const char *memoryLogFile = "memory.log";
00252 static const char *memoryLeakLogFile = "memleaks.log";
00253 static void doCleanupLogOnFirstRun();
00254
00255
00256
00257
00258
00259 static void log(const char *format, ...)
00260 {
00261
00262
00263 if (cleanupLogOnFirstRun) doCleanupLogOnFirstRun();
00264
00265
00266
00267 static char buffer[2048];
00268 va_list ap;
00269 va_start(ap, format);
00270 vsprintf(buffer, format, ap);
00271 va_end(ap);
00272
00273
00274
00275 FILE *fp = fopen(memoryLogFile, "ab");
00276
00277
00278
00279 m_assert(fp);
00280
00281 if (!fp) return;
00282
00283
00284
00285 fprintf(fp, "%s\r\n", buffer);
00286 fclose(fp);
00287 }
00288
00289
00290
00291 static void doCleanupLogOnFirstRun()
00292 {
00293 if (cleanupLogOnFirstRun)
00294 {
00295 unlink(memoryLogFile);
00296 cleanupLogOnFirstRun = false;
00297
00298
00299
00300 time_t t = time(NULL);
00301 log("--------------------------------------------------------------------------------");
00302 log("");
00303 log(" %s - Memory logging file created on %s", memoryLogFile, asctime(localtime(&t)));
00304 log("--------------------------------------------------------------------------------");
00305 log("");
00306 log("This file contains a log of all memory operations performed during the last run.");
00307 log("");
00308 log("Interrogate this file to track errors or to help track down memory-related");
00309 log("issues. You can do this by tracing the allocations performed by a specific owner");
00310 log("or by tracking a specific address through a series of allocations and");
00311 log("reallocations.");
00312 log("");
00313 log("There is a lot of useful information here which, when used creatively, can be");
00314 log("extremely helpful.");
00315 log("");
00316 log("Note that the following guides are used throughout this file:");
00317 log("");
00318 log(" [!] - Error");
00319 log(" [+] - Allocation");
00320 log(" [~] - Reallocation");
00321 log(" [-] - Deallocation");
00322 log(" [I] - Generic information");
00323 log(" [F] - Failure induced for the purpose of stress-testing your application");
00324 log(" [D] - Information used for debugging this memory manager");
00325 log("");
00326 log("...so, to find all errors in the file, search for \"[!]\"");
00327 log("");
00328 log("--------------------------------------------------------------------------------");
00329 }
00330 }
00331
00332
00333
00334 static const char *sourceFileStripper(const char *sourceFile)
00335 {
00336 char *ptr = (char*)strrchr(sourceFile, '\\');
00337 if (ptr) return ptr + 1;
00338 ptr = (char*)strrchr(sourceFile, '/');
00339 if (ptr) return ptr + 1;
00340 return sourceFile;
00341 }
00342
00343
00344
00345 static const char *ownerString(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc)
00346 {
00347 static char str[90];
00348 memset(str, 0, sizeof(str));
00349 sprintf(str, "%s(%05d)::%s", sourceFileStripper(sourceFile), sourceLine, sourceFunc);
00350 return str;
00351 }
00352
00353
00354
00355 static const char *insertCommas(unsigned int value)
00356 {
00357 static char str[30];
00358 memset(str, 0, sizeof(str));
00359
00360 sprintf(str, "%u", value);
00361 if (strlen(str) > 3)
00362 {
00363 memmove(&str[strlen(str)-3], &str[strlen(str)-4], 4);
00364 str[strlen(str) - 4] = ',';
00365 }
00366 if (strlen(str) > 7)
00367 {
00368 memmove(&str[strlen(str)-7], &str[strlen(str)-8], 8);
00369 str[strlen(str) - 8] = ',';
00370 }
00371 if (strlen(str) > 11)
00372 {
00373 memmove(&str[strlen(str)-11], &str[strlen(str)-12], 12);
00374 str[strlen(str) - 12] = ',';
00375 }
00376
00377 return str;
00378 }
00379
00380
00381
00382 static const char *memorySizeString(unsigned long size)
00383 {
00384 static char str[90];
00385 if (size > (1024*1024)) sprintf(str, "%10s (%7.2fM)", insertCommas(size), static_cast<float>(size) / (1024.0f * 1024.0f));
00386 else if (size > 1024) sprintf(str, "%10s (%7.2fK)", insertCommas(size), static_cast<float>(size) / 1024.0f);
00387 else sprintf(str, "%10s bytes ", insertCommas(size));
00388 return str;
00389 }
00390
00391
00392
00393 static sAllocUnit *findAllocUnit(const void *reportedAddress)
00394 {
00395
00396 m_assert(reportedAddress != NULL);
00397
00398
00399
00400
00401
00402 unsigned int hashIndex = (reinterpret_cast<unsigned int>(const_cast<void *>(reportedAddress)) >> 4) & (hashSize - 1);
00403 sAllocUnit *ptr = hashTable[hashIndex];
00404 while(ptr)
00405 {
00406 if (ptr->reportedAddress == reportedAddress) return ptr;
00407 ptr = ptr->next;
00408 }
00409
00410 return NULL;
00411 }
00412
00413
00414
00415 static size_t calculateActualSize(const size_t reportedSize)
00416 {
00417
00418
00419
00420
00421 return reportedSize + paddingSize * sizeof(long) * 2;
00422 }
00423
00424
00425
00426 static size_t calculateReportedSize(const size_t actualSize)
00427 {
00428
00429
00430
00431
00432 return actualSize - paddingSize * sizeof(long) * 2;
00433 }
00434
00435
00436
00437 static void *calculateReportedAddress(const void *actualAddress)
00438 {
00439
00440
00441 if (!actualAddress) return NULL;
00442
00443
00444
00445 return reinterpret_cast<void *>(const_cast<char *>(reinterpret_cast<const char *>(actualAddress) + sizeof(long) * paddingSize));
00446 }
00447
00448
00449
00450 static void wipeWithPattern(sAllocUnit *allocUnit, unsigned long pattern, const unsigned int originalReportedSize = 0)
00451 {
00452
00453
00454
00455
00456
00457 if (randomWipe)
00458 {
00459 pattern = ((rand() & 0xff) << 24) | ((rand() & 0xff) << 16) | ((rand() & 0xff) << 8) | (rand() & 0xff);
00460 }
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471 if (alwaysWipeAll && allocUnit->reportedSize > originalReportedSize)
00472 {
00473
00474
00475 long *lptr = reinterpret_cast<long *>(reinterpret_cast<char *>(allocUnit->reportedAddress) + originalReportedSize);
00476 int length = static_cast<int>(allocUnit->reportedSize - originalReportedSize);
00477 int i;
00478 for (i = 0; i < (length >> 2); i++, lptr++)
00479 {
00480 *lptr = pattern;
00481 }
00482
00483
00484
00485 unsigned int shiftCount = 0;
00486 char *cptr = reinterpret_cast<char *>(lptr);
00487 for (i = 0; i < (length & 0x3); i++, cptr++, shiftCount += 8)
00488 {
00489 *cptr = static_cast<char>((pattern & (0xff << shiftCount)) >> shiftCount);
00490 }
00491 }
00492
00493
00494
00495 long *pre = reinterpret_cast<long *>(allocUnit->actualAddress);
00496 long *post = reinterpret_cast<long *>(reinterpret_cast<char *>(allocUnit->actualAddress) + allocUnit->actualSize - paddingSize * sizeof(long));
00497 for (unsigned int i = 0; i < paddingSize; i++, pre++, post++)
00498 {
00499 *pre = prefixPattern;
00500 *post = postfixPattern;
00501 }
00502 }
00503
00504
00505
00506 static void dumpAllocations(FILE *fp)
00507 {
00508 fprintf(fp, "Alloc. Addr Size Addr Size BreakOn BreakOn \r\n");
00509 fprintf(fp, "Number Reported Reported Actual Actual Unused Method Dealloc Realloc Allocated by \r\n");
00510 fprintf(fp, "------ ---------- ---------- ---------- ---------- ---------- -------- ------- ------- --------------------------------------------------- \r\n");
00511
00512
00513 for (unsigned int i = 0; i < hashSize; i++)
00514 {
00515 sAllocUnit *ptr = hashTable[i];
00516 while(ptr)
00517 {
00518 fprintf(fp, "%06d 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X %-8s %c %c %s\r\n",
00519 ptr->allocationNumber,
00520 reinterpret_cast<unsigned int>(ptr->reportedAddress), ptr->reportedSize,
00521 reinterpret_cast<unsigned int>(ptr->actualAddress), ptr->actualSize,
00522 m_calcUnused(ptr),
00523 allocationTypes[ptr->allocationType],
00524 ptr->breakOnDealloc ? 'Y':'N',
00525 ptr->breakOnRealloc ? 'Y':'N',
00526 ownerString(ptr->sourceFile, ptr->sourceLine, ptr->sourceFunc));
00527 ptr = ptr->next;
00528 }
00529 }
00530 }
00531
00532
00533
00534 static void dumpLeakReport()
00535 {
00536
00537
00538 FILE *fp = fopen(memoryLeakLogFile, "w+b");
00539
00540
00541
00542 m_assert(fp);
00543 if (!fp) return;
00544
00545
00546
00547
00548
00549 static char timeString[25];
00550 memset(timeString, 0, sizeof(timeString));
00551 time_t t = time(NULL);
00552 struct tm *tme = localtime(&t);
00553 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
00554 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);
00555 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
00556 fprintf(fp, "\r\n");
00557 fprintf(fp, "\r\n");
00558 if (stats.totalAllocUnitCount)
00559 {
00560 fprintf(fp, "%d memory leak%s found:\r\n", stats.totalAllocUnitCount, stats.totalAllocUnitCount == 1 ? "":"s");
00561 }
00562 else
00563 {
00564 fprintf(fp, "Congratulations! No memory leaks found!\r\n");
00565
00566
00567
00568 if (reservoirBuffer)
00569 {
00570 for (unsigned int i = 0; i < reservoirBufferSize; i++)
00571 {
00572 free(reservoirBuffer[i]);
00573 }
00574 free(reservoirBuffer);
00575 reservoirBuffer = 0;
00576 reservoirBufferSize = 0;
00577 reservoir = NULL;
00578 }
00579 }
00580 fprintf(fp, "\r\n");
00581
00582 if (stats.totalAllocUnitCount)
00583 {
00584 dumpAllocations(fp);
00585 }
00586
00587 fclose(fp);
00588 }
00589
00590
00591
00592
00593
00594 class MemStaticTimeTracker
00595 {
00596 public:
00597 MemStaticTimeTracker() {doCleanupLogOnFirstRun();}
00598 ~MemStaticTimeTracker() {staticDeinitTime = true; dumpLeakReport();}
00599 };
00600 static MemStaticTimeTracker mstt;
00601
00602
00603
00604
00605
00606 bool &m_alwaysValidateAll()
00607 {
00608
00609 return alwaysValidateAll;
00610 }
00611
00612
00613
00614 bool &m_alwaysLogAll()
00615 {
00616
00617 return alwaysLogAll;
00618 }
00619
00620
00621
00622 bool &m_alwaysWipeAll()
00623 {
00624
00625 return alwaysWipeAll;
00626 }
00627
00628
00629
00630 bool &m_randomeWipe()
00631 {
00632
00633 return randomWipe;
00634 }
00635
00636
00637
00638
00639
00640
00641 bool &m_breakOnRealloc(void *reportedAddress)
00642 {
00643
00644
00645 sAllocUnit *au = findAllocUnit(reportedAddress);
00646
00647
00648
00649 m_assert(au != NULL);
00650
00651
00652
00653 m_assert(au->allocationType == m_alloc_malloc ||
00654 au->allocationType == m_alloc_calloc ||
00655 au->allocationType == m_alloc_realloc);
00656
00657 return au->breakOnRealloc;
00658 }
00659
00660
00661
00662
00663
00664
00665 bool &m_breakOnDealloc(void *reportedAddress)
00666 {
00667
00668
00669 sAllocUnit *au = findAllocUnit(reportedAddress);
00670
00671
00672
00673 m_assert(au != NULL);
00674
00675 return au->breakOnDealloc;
00676 }
00677
00678
00679
00680
00681
00682 void m_breakOnAllocation(unsigned int count)
00683 {
00684 breakOnAllocationCount = count;
00685 }
00686
00687
00688
00689
00690
00691 void m_setOwner(const char *file, const unsigned int line, const char *func)
00692 {
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735 if (sourceLine && alwaysLogAll)
00736 {
00737 log("[I] NOTE! Possible destructor chain: previous owner is %s", ownerString(sourceFile, sourceLine, sourceFunc));
00738 }
00739
00740
00741
00742 sourceFile = file;
00743 sourceLine = line;
00744 sourceFunc = func;
00745 }
00746
00747
00748
00749 static void resetGlobals()
00750 {
00751 sourceFile = "??";
00752 sourceLine = 0;
00753 sourceFunc = "??";
00754 }
00755
00756
00757
00758
00759
00760
00761
00762
00763 void *operator new(size_t reportedSize)
00764 {
00765 #ifdef TEST_MEMORY_MANAGER
00766 log("[D] ENTER: new");
00767 #endif
00768
00769
00770
00771 const char *file = sourceFile;
00772 const unsigned int line = sourceLine;
00773 const char *func = sourceFunc;
00774
00775
00776
00777 if (reportedSize == 0) reportedSize = 1;
00778
00779
00780
00781 for(;;)
00782 {
00783
00784
00785 void *ptr = m_allocator(file, line, func, m_alloc_new, reportedSize);
00786 if (ptr)
00787 {
00788 #ifdef TEST_MEMORY_MANAGER
00789 log("[D] EXIT : new");
00790 #endif
00791 return ptr;
00792 }
00793
00794
00795
00796
00797 new_handler nh = std::set_new_handler(0);
00798 std::set_new_handler(nh);
00799
00800
00801
00802 if (nh)
00803 {
00804 (*nh)();
00805 }
00806
00807
00808
00809 else
00810 {
00811 #ifdef TEST_MEMORY_MANAGER
00812 log("[D] EXIT : new");
00813 #endif
00814 throw std::bad_alloc();
00815 }
00816 }
00817 }
00818
00819
00820
00821 void *operator new[](size_t reportedSize)
00822 {
00823 #ifdef TEST_MEMORY_MANAGER
00824 log("[D] ENTER: new[]");
00825 #endif
00826
00827
00828
00829 const char *file = sourceFile;
00830 const unsigned int line = sourceLine;
00831 const char *func = sourceFunc;
00832
00833
00834
00835 if (reportedSize == 0) reportedSize = 1;
00836
00837
00838
00839 for(;;)
00840 {
00841
00842
00843 void *ptr = m_allocator(file, line, func, m_alloc_new_array, reportedSize);
00844 if (ptr)
00845 {
00846 #ifdef TEST_MEMORY_MANAGER
00847 log("[D] EXIT : new[]");
00848 #endif
00849 return ptr;
00850 }
00851
00852
00853
00854
00855 new_handler nh = std::set_new_handler(0);
00856 std::set_new_handler(nh);
00857
00858
00859
00860 if (nh)
00861 {
00862 (*nh)();
00863 }
00864
00865
00866
00867 else
00868 {
00869 #ifdef TEST_MEMORY_MANAGER
00870 log("[D] EXIT : new[]");
00871 #endif
00872 throw std::bad_alloc();
00873 }
00874 }
00875 }
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885 void *operator new(size_t reportedSize, const char *sourceFile, int sourceLine)
00886 {
00887 #ifdef TEST_MEMORY_MANAGER
00888 log("[D] ENTER: new");
00889 #endif
00890
00891
00892
00893 if (reportedSize == 0) reportedSize = 1;
00894
00895
00896
00897 for(;;)
00898 {
00899
00900
00901 void *ptr = m_allocator(sourceFile, sourceLine, "??", m_alloc_new, reportedSize);
00902 if (ptr)
00903 {
00904 #ifdef TEST_MEMORY_MANAGER
00905 log("[D] EXIT : new");
00906 #endif
00907 return ptr;
00908 }
00909
00910
00911
00912
00913 new_handler nh = std::set_new_handler(0);
00914 std::set_new_handler(nh);
00915
00916
00917
00918 if (nh)
00919 {
00920 (*nh)();
00921 }
00922
00923
00924
00925 else
00926 {
00927 #ifdef TEST_MEMORY_MANAGER
00928 log("[D] EXIT : new");
00929 #endif
00930 throw std::bad_alloc();
00931 }
00932 }
00933 }
00934
00935
00936
00937 void *operator new[](size_t reportedSize, const char *sourceFile, int sourceLine)
00938 {
00939 #ifdef TEST_MEMORY_MANAGER
00940 log("[D] ENTER: new[]");
00941 #endif
00942
00943
00944
00945 if (reportedSize == 0) reportedSize = 1;
00946
00947
00948
00949 for(;;)
00950 {
00951
00952
00953 void *ptr = m_allocator(sourceFile, sourceLine, "??", m_alloc_new_array, reportedSize);
00954 if (ptr)
00955 {
00956 #ifdef TEST_MEMORY_MANAGER
00957 log("[D] EXIT : new[]");
00958 #endif
00959 return ptr;
00960 }
00961
00962
00963
00964
00965 new_handler nh = std::set_new_handler(0);
00966 std::set_new_handler(nh);
00967
00968
00969
00970 if (nh)
00971 {
00972 (*nh)();
00973 }
00974
00975
00976
00977 else
00978 {
00979 #ifdef TEST_MEMORY_MANAGER
00980 log("[D] EXIT : new[]");
00981 #endif
00982 throw std::bad_alloc();
00983 }
00984 }
00985 }
00986
00987
00988
00989
00990
00991
00992
00993
00994 void operator delete(void *reportedAddress)
00995 {
00996 #ifdef TEST_MEMORY_MANAGER
00997 log("[D] ENTER: delete");
00998 #endif
00999
01000
01001
01002 if (reportedAddress) m_deallocator(sourceFile, sourceLine, sourceFunc, m_alloc_delete, reportedAddress);
01003 else if (alwaysLogAll) log("[-] ----- %8s of NULL by %s", allocationTypes[m_alloc_delete], ownerString(sourceFile, sourceLine, sourceFunc));
01004
01005
01006
01007
01008 resetGlobals();
01009
01010 #ifdef TEST_MEMORY_MANAGER
01011 log("[D] EXIT : delete");
01012 #endif
01013 }
01014
01015
01016
01017 void operator delete[](void *reportedAddress)
01018 {
01019 #ifdef TEST_MEMORY_MANAGER
01020 log("[D] ENTER: delete[]");
01021 #endif
01022
01023
01024
01025 if (reportedAddress) m_deallocator(sourceFile, sourceLine, sourceFunc, m_alloc_delete_array, reportedAddress);
01026 else if (alwaysLogAll)
01027 log("[-] ----- %8s of NULL by %s", allocationTypes[m_alloc_delete_array], ownerString(sourceFile, sourceLine, sourceFunc));
01028
01029
01030
01031
01032 resetGlobals();
01033
01034 #ifdef TEST_MEMORY_MANAGER
01035 log("[D] EXIT : delete[]");
01036 #endif
01037 }
01038
01039
01040
01041
01042
01043 void *m_allocator(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc, const unsigned int allocationType, const size_t reportedSize)
01044 {
01045 try
01046 {
01047 #ifdef TEST_MEMORY_MANAGER
01048 log("[D] ENTER: m_allocator()");
01049 #endif
01050
01051
01052
01053 currentAllocationCount++;
01054
01055
01056
01057 if (alwaysLogAll) log("[+] %05d %8s of size 0x%08X(%08d) by %s", currentAllocationCount, allocationTypes[allocationType], reportedSize, reportedSize, ownerString(sourceFile, sourceLine, sourceFunc));
01058
01059
01060 m_assert(currentAllocationCount != breakOnAllocationCount);
01061
01062
01063
01064 if (!reservoir)
01065 {
01066
01067
01068 reservoir = (sAllocUnit *) malloc(sizeof(sAllocUnit) * 256);
01069
01070
01071
01072 m_assert(reservoir != NULL);
01073
01074
01075
01076 if (reservoir == NULL) throw "Unable to allocate RAM for internal memory tracking data";
01077
01078
01079
01080 memset(reservoir, 0, sizeof(sAllocUnit) * 256);
01081 for (unsigned int i = 0; i < 256 - 1; i++)
01082 {
01083 reservoir[i].next = &reservoir[i+1];
01084 }
01085
01086
01087
01088 sAllocUnit **temp = (sAllocUnit **) realloc(reservoirBuffer, (reservoirBufferSize + 1) * sizeof(sAllocUnit *));
01089 m_assert(temp);
01090 if (temp)
01091 {
01092 reservoirBuffer = temp;
01093 reservoirBuffer[reservoirBufferSize++] = reservoir;
01094 }
01095 }
01096
01097
01098 m_assert(reservoir != NULL);
01099
01100
01101
01102 sAllocUnit *au = reservoir;
01103 reservoir = au->next;
01104
01105
01106
01107 memset(au, 0, sizeof(sAllocUnit));
01108 au->actualSize = calculateActualSize(reportedSize);
01109 #ifdef RANDOM_FAILURE
01110 double a = rand();
01111 double b = RAND_MAX / 100.0 * RANDOM_FAILURE;
01112 if (a > b)
01113 {
01114 au->actualAddress = malloc(au->actualSize);
01115 }
01116 else
01117 {
01118 log("[F] Random faiure");
01119 au->actualAddress = NULL;
01120 }
01121 #else
01122 au->actualAddress = malloc(au->actualSize);
01123 #endif
01124 au->reportedSize = reportedSize;
01125 au->reportedAddress = calculateReportedAddress(au->actualAddress);
01126 au->allocationType = allocationType;
01127 au->sourceLine = sourceLine;
01128 au->allocationNumber = currentAllocationCount;
01129 if (sourceFile) strncpy(au->sourceFile, sourceFileStripper(sourceFile), sizeof(au->sourceFile) - 1);
01130 else strcpy (au->sourceFile, "??");
01131 if (sourceFunc) strncpy(au->sourceFunc, sourceFunc, sizeof(au->sourceFunc) - 1);
01132 else strcpy (au->sourceFunc, "??");
01133
01134
01135
01136 #ifndef RANDOM_FAILURE
01137
01138
01139 m_assert(au->actualAddress != NULL);
01140 #endif
01141
01142 if (au->actualAddress == NULL)
01143 {
01144 throw "Request for allocation failed. Out of memory.";
01145 }
01146
01147
01148
01149 m_assert(allocationType != m_alloc_unknown);
01150
01151
01152
01153 unsigned int hashIndex = (reinterpret_cast<unsigned int>(au->reportedAddress) >> 4) & (hashSize - 1);
01154 if (hashTable[hashIndex]) hashTable[hashIndex]->prev = au;
01155 au->next = hashTable[hashIndex];
01156 au->prev = NULL;
01157 hashTable[hashIndex] = au;
01158
01159
01160
01161 stats.totalReportedMemory += static_cast<unsigned int>(au->reportedSize);
01162 stats.totalActualMemory += static_cast<unsigned int>(au->actualSize);
01163 stats.totalAllocUnitCount++;
01164 if (stats.totalReportedMemory > stats.peakReportedMemory) stats.peakReportedMemory = stats.totalReportedMemory;
01165 if (stats.totalActualMemory > stats.peakActualMemory) stats.peakActualMemory = stats.totalActualMemory;
01166 if (stats.totalAllocUnitCount > stats.peakAllocUnitCount) stats.peakAllocUnitCount = stats.totalAllocUnitCount;
01167 stats.accumulatedReportedMemory += static_cast<unsigned int>(au->reportedSize);
01168 stats.accumulatedActualMemory += static_cast<unsigned int>(au->actualSize);
01169 stats.accumulatedAllocUnitCount++;
01170
01171
01172
01173 wipeWithPattern(au, unusedPattern);
01174
01175
01176
01177 if (allocationType == m_alloc_calloc)
01178 {
01179 memset(au->reportedAddress, 0, au->reportedSize);
01180 }
01181
01182
01183
01184 if (alwaysValidateAll) m_validateAllAllocUnits();
01185
01186
01187
01188 if (alwaysLogAll) log("[+] ----> addr 0x%08X", reinterpret_cast<unsigned int>(au->reportedAddress));
01189
01190
01191
01192
01193 resetGlobals();
01194
01195
01196
01197 #ifdef TEST_MEMORY_MANAGER
01198 log("[D] EXIT : m_allocator()");
01199 #endif
01200
01201 return au->reportedAddress;
01202 }
01203 catch(const char *err)
01204 {
01205
01206
01207 log("[!] %s", err);
01208 resetGlobals();
01209
01210 #ifdef TEST_MEMORY_MANAGER
01211 log("[D] EXIT : m_allocator()");
01212 #endif
01213
01214 return NULL;
01215 }
01216 }
01217
01218
01219
01220
01221
01222 void *m_reallocator(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc, const unsigned int reallocationType, const size_t reportedSize, void *reportedAddress)
01223 {
01224 try
01225 {
01226 #ifdef TEST_MEMORY_MANAGER
01227 log("[D] ENTER: m_reallocator()");
01228 #endif
01229
01230
01231
01232 if (!reportedAddress)
01233 {
01234 return m_allocator(sourceFile, sourceLine, sourceFunc, reallocationType, reportedSize);
01235 }
01236
01237
01238
01239 currentAllocationCount++;
01240
01241
01242 m_assert(currentAllocationCount != breakOnAllocationCount);
01243
01244
01245
01246 if (alwaysLogAll) log("[~] %05d %8s of size 0x%08X(%08d) by %s", currentAllocationCount, allocationTypes[reallocationType], reportedSize, reportedSize, ownerString(sourceFile, sourceLine, sourceFunc));
01247
01248
01249
01250 sAllocUnit *au = findAllocUnit(reportedAddress);
01251
01252
01253 m_assert(au != NULL);
01254 if (au == NULL) throw "Request to reallocate RAM that was never allocated";
01255
01256
01257
01258 m_assert(m_validateAllocUnit(au));
01259
01260
01261
01262 m_assert(reallocationType != m_alloc_unknown);
01263
01264
01265
01266 m_assert(au->allocationType == m_alloc_malloc ||
01267 au->allocationType == m_alloc_calloc ||
01268 au->allocationType == m_alloc_realloc);
01269
01270
01271
01272
01273 m_assert(au->breakOnRealloc == false);
01274
01275
01276
01277 unsigned int originalReportedSize = static_cast<unsigned int>(au->reportedSize);
01278
01279 if (alwaysLogAll) log("[~] ----> from 0x%08X(%08d)", originalReportedSize, originalReportedSize);
01280
01281
01282
01283 void *oldReportedAddress = reportedAddress;
01284 size_t newActualSize = calculateActualSize(reportedSize);
01285 void *newActualAddress = NULL;
01286 #ifdef RANDOM_FAILURE
01287 double a = rand();
01288 double b = RAND_MAX / 100.0 * RANDOM_FAILURE;
01289 if (a > b)
01290 {
01291 newActualAddress = realloc(au->actualAddress, newActualSize);
01292 }
01293 else
01294 {
01295 log("[F] Random faiure");
01296 }
01297 #else
01298 newActualAddress = realloc(au->actualAddress, newActualSize);
01299 #endif
01300
01301
01302
01303 #ifndef RANDOM_FAILURE
01304
01305
01306
01307 m_assert(newActualAddress);
01308 #endif
01309
01310 if (!newActualAddress) throw "Request for reallocation failed. Out of memory.";
01311
01312
01313
01314 stats.totalReportedMemory -= static_cast<unsigned int>(au->reportedSize);
01315 stats.totalActualMemory -= static_cast<unsigned int>(au->actualSize);
01316
01317
01318
01319 au->actualSize = newActualSize;
01320 au->actualAddress = newActualAddress;
01321 au->reportedSize = calculateReportedSize(newActualSize);
01322 au->reportedAddress = calculateReportedAddress(newActualAddress);
01323 au->allocationType = reallocationType;
01324 au->sourceLine = sourceLine;
01325 au->allocationNumber = currentAllocationCount;
01326 if (sourceFile) strncpy(au->sourceFile, sourceFileStripper(sourceFile), sizeof(au->sourceFile) - 1);
01327 else strcpy (au->sourceFile, "??");
01328 if (sourceFunc) strncpy(au->sourceFunc, sourceFunc, sizeof(au->sourceFunc) - 1);
01329 else strcpy (au->sourceFunc, "??");
01330
01331
01332
01333 unsigned int hashIndex = static_cast<unsigned int>(-1);
01334 if (oldReportedAddress != au->reportedAddress)
01335 {
01336
01337
01338 {
01339 unsigned int hashIndex = (reinterpret_cast<unsigned int>(oldReportedAddress) >> 4) & (hashSize - 1);
01340 if (hashTable[hashIndex] == au)
01341 {
01342 hashTable[hashIndex] = hashTable[hashIndex]->next;
01343 }
01344 else
01345 {
01346 if (au->prev) au->prev->next = au->next;
01347 if (au->next) au->next->prev = au->prev;
01348 }
01349 }
01350
01351
01352
01353 hashIndex = (reinterpret_cast<unsigned int>(au->reportedAddress) >> 4) & (hashSize - 1);
01354 if (hashTable[hashIndex]) hashTable[hashIndex]->prev = au;
01355 au->next = hashTable[hashIndex];
01356 au->prev = NULL;
01357 hashTable[hashIndex] = au;
01358 }
01359
01360
01361
01362 stats.totalReportedMemory += static_cast<unsigned int>(au->reportedSize);
01363 stats.totalActualMemory += static_cast<unsigned int>(au->actualSize);
01364 if (stats.totalReportedMemory > stats.peakReportedMemory) stats.peakReportedMemory = stats.totalReportedMemory;
01365 if (stats.totalActualMemory > stats.peakActualMemory) stats.peakActualMemory = stats.totalActualMemory;
01366 int deltaReportedSize = static_cast<int>(reportedSize - originalReportedSize);
01367 if (deltaReportedSize > 0)
01368 {
01369 stats.accumulatedReportedMemory += deltaReportedSize;
01370 stats.accumulatedActualMemory += deltaReportedSize;
01371 }
01372
01373
01374
01375 wipeWithPattern(au, unusedPattern, originalReportedSize);
01376
01377
01378
01379 m_assert(m_validateAllocUnit(au));
01380
01381
01382
01383 if (alwaysValidateAll) m_validateAllAllocUnits();
01384
01385
01386
01387 if (alwaysLogAll) log("[~] ----> addr 0x%08X", reinterpret_cast<unsigned int>(au->reportedAddress));
01388
01389
01390
01391
01392 resetGlobals();
01393
01394
01395
01396 #ifdef TEST_MEMORY_MANAGER
01397 log("[D] EXIT : m_reallocator()");
01398 #endif
01399
01400 return au->reportedAddress;
01401 }
01402 catch(const char *err)
01403 {
01404
01405
01406 log("[!] %s", err);
01407 resetGlobals();
01408
01409 #ifdef TEST_MEMORY_MANAGER
01410 log("[D] EXIT : m_reallocator()");
01411 #endif
01412
01413 return NULL;
01414 }
01415 }
01416
01417
01418
01419
01420
01421 void m_deallocator(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc, const unsigned int deallocationType, const void *reportedAddress)
01422 {
01423 try
01424 {
01425 #ifdef TEST_MEMORY_MANAGER
01426 log("[D] ENTER: m_deallocator()");
01427 #endif
01428
01429
01430
01431 if (alwaysLogAll) log("[-] ----- %8s of addr 0x%08X by %s", allocationTypes[deallocationType], reinterpret_cast<unsigned int>(const_cast<void *>(reportedAddress)), ownerString(sourceFile, sourceLine, sourceFunc));
01432
01433
01434
01435
01436
01437 if (reportedAddress)
01438 {
01439
01440
01441 sAllocUnit *au = findAllocUnit(reportedAddress);
01442
01443
01444 m_assert(au != NULL);
01445 if (au == NULL) throw "Request to deallocate RAM that was never allocated";
01446
01447
01448
01449 m_assert(m_validateAllocUnit(au));
01450
01451
01452
01453 m_assert(deallocationType != m_alloc_unknown);
01454
01455
01456
01457 m_assert((deallocationType == m_alloc_delete && au->allocationType == m_alloc_new ) ||
01458 (deallocationType == m_alloc_delete_array && au->allocationType == m_alloc_new_array) ||
01459 (deallocationType == m_alloc_free && au->allocationType == m_alloc_malloc ) ||
01460 (deallocationType == m_alloc_free && au->allocationType == m_alloc_calloc ) ||
01461 (deallocationType == m_alloc_free && au->allocationType == m_alloc_realloc ) ||
01462 (deallocationType == m_alloc_unknown ) );
01463
01464
01465
01466 m_assert(au->breakOnDealloc == false);
01467
01468
01469
01470
01471 wipeWithPattern(au, releasedPattern);
01472
01473
01474
01475 free(au->actualAddress);
01476
01477
01478
01479 unsigned int hashIndex = (reinterpret_cast<unsigned int>(au->reportedAddress) >> 4) & (hashSize - 1);
01480 if (hashTable[hashIndex] == au)
01481 {
01482 hashTable[hashIndex] = au->next;
01483 }
01484 else
01485 {
01486 if (au->prev) au->prev->next = au->next;
01487 if (au->next) au->next->prev = au->prev;
01488 }
01489
01490
01491
01492 stats.totalReportedMemory -= static_cast<unsigned int>(au->reportedSize);
01493 stats.totalActualMemory -= static_cast<unsigned int>(au->actualSize);
01494 stats.totalAllocUnitCount--;
01495
01496
01497
01498 memset(au, 0, sizeof(sAllocUnit));
01499 au->next = reservoir;
01500 reservoir = au;
01501 }
01502
01503
01504
01505
01506 resetGlobals();
01507
01508
01509
01510 if (alwaysValidateAll) m_validateAllAllocUnits();
01511
01512
01513
01514 if (staticDeinitTime) dumpLeakReport();
01515 }
01516 catch(const char *err)
01517 {
01518
01519
01520 log("[!] %s", err);
01521 resetGlobals();
01522 }
01523
01524 #ifdef TEST_MEMORY_MANAGER
01525 log("[D] EXIT : m_deallocator()");
01526 #endif
01527 }
01528
01529
01530
01531
01532
01533
01534 bool m_validateAddress(const void *reportedAddress)
01535 {
01536
01537
01538 return findAllocUnit(reportedAddress) != NULL;
01539 }
01540
01541
01542
01543 bool m_validateAllocUnit(const sAllocUnit *allocUnit)
01544 {
01545
01546
01547 long *pre = reinterpret_cast<long *>(allocUnit->actualAddress);
01548 long *post = reinterpret_cast<long *>((char *)allocUnit->actualAddress + allocUnit->actualSize - paddingSize * sizeof(long));
01549 bool errorFlag = false;
01550 for (unsigned int i = 0; i < paddingSize; i++, pre++, post++)
01551 {
01552 if (*pre != (long) prefixPattern)
01553 {
01554 log("[!] A memory allocation unit was corrupt because of an underrun:");
01555 m_dumpAllocUnit(allocUnit, " ");
01556 errorFlag = true;
01557 }
01558
01559
01560
01561
01562 m_assert(*pre == static_cast<long>(prefixPattern));
01563
01564 if (*post != static_cast<long>(postfixPattern))
01565 {
01566 log("[!] A memory allocation unit was corrupt because of an overrun:");
01567 m_dumpAllocUnit(allocUnit, " ");
01568 errorFlag = true;
01569 }
01570
01571
01572
01573
01574 m_assert(*post == static_cast<long>(postfixPattern));
01575 }
01576
01577
01578
01579 return !errorFlag;
01580 }
01581
01582
01583
01584 bool m_validateAllAllocUnits()
01585 {
01586
01587
01588 unsigned int errors = 0;
01589 unsigned int allocCount = 0;
01590 for (unsigned int i = 0; i < hashSize; i++)
01591 {
01592 sAllocUnit *ptr = hashTable[i];
01593 while(ptr)
01594 {
01595 allocCount++;
01596 if (!m_validateAllocUnit(ptr)) errors++;
01597 ptr = ptr->next;
01598 }
01599 }
01600
01601
01602
01603 if (allocCount != stats.totalAllocUnitCount)
01604 {
01605 log("[!] Memory tracking hash table corrupt!");
01606 errors++;
01607 }
01608
01609
01610
01611
01612
01613
01614 m_assert(allocCount == stats.totalAllocUnitCount);
01615
01616
01617
01618 m_assert(errors == 0);
01619
01620
01621
01622 if (errors) log("[!] While validting all allocation units, %d allocation unit(s) were found to have problems", errors);
01623
01624
01625
01626 return errors != 0;
01627 }
01628
01629
01630
01631
01632
01633 unsigned int m_calcUnused(const sAllocUnit *allocUnit)
01634 {
01635 const unsigned long *ptr = reinterpret_cast<const unsigned long *>(allocUnit->reportedAddress);
01636 unsigned int count = 0;
01637
01638 for (unsigned int i = 0; i < allocUnit->reportedSize; i += sizeof(long), ptr++)
01639 {
01640 if (*ptr == unusedPattern) count += sizeof(long);
01641 }
01642
01643 return count;
01644 }
01645
01646
01647
01648 unsigned int m_calcAllUnused()
01649 {
01650
01651
01652 unsigned int total = 0;
01653 for (unsigned int i = 0; i < hashSize; i++)
01654 {
01655 sAllocUnit *ptr = hashTable[i];
01656 while(ptr)
01657 {
01658 total += m_calcUnused(ptr);
01659 ptr = ptr->next;
01660 }
01661 }
01662
01663 return total;
01664 }
01665
01666
01667
01668
01669
01670 void m_dumpAllocUnit(const sAllocUnit *allocUnit, const char *prefix)
01671 {
01672 log("[I] %sAddress (reported): %010p", prefix, allocUnit->reportedAddress);
01673 log("[I] %sAddress (actual) : %010p", prefix, allocUnit->actualAddress);
01674 log("[I] %sSize (reported) : 0x%08X (%s)", prefix, static_cast<unsigned int>(allocUnit->reportedSize), memorySizeString(static_cast<unsigned int>(allocUnit->reportedSize)));
01675 log("[I] %sSize (actual) : 0x%08X (%s)", prefix, static_cast<unsigned int>(allocUnit->actualSize), memorySizeString(static_cast<unsigned int>(allocUnit->actualSize)));
01676 log("[I] %sOwner : %s(%d)::%s", prefix, allocUnit->sourceFile, allocUnit->sourceLine, allocUnit->sourceFunc);
01677 log("[I] %sAllocation type : %s", prefix, allocationTypes[allocUnit->allocationType]);
01678 log("[I] %sAllocation number : %d", prefix, allocUnit->allocationNumber);
01679 }
01680
01681
01682
01683 void m_dumpMemoryReport(const char *filename, const bool overwrite)
01684 {
01685
01686
01687 FILE *fp = NULL;
01688
01689 if (overwrite) fp = fopen(filename, "w+b");
01690 else fp = fopen(filename, "ab");
01691
01692
01693
01694 m_assert(fp);
01695 if (!fp) return;
01696
01697
01698
01699 static char timeString[25];
01700 memset(timeString, 0, sizeof(timeString));
01701 time_t t = time(NULL);
01702 struct tm *tme = localtime(&t);
01703 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
01704 fprintf(fp, "| Memory 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);
01705 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
01706 fprintf(fp, "\r\n");
01707 fprintf(fp, "\r\n");
01708
01709
01710
01711 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
01712 fprintf(fp, "| T O T A L S |\r\n");
01713 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
01714 fprintf(fp, " Allocation unit count: %10s\r\n", insertCommas(stats.totalAllocUnitCount));
01715 fprintf(fp, " Reported to application: %s\r\n", memorySizeString(stats.totalReportedMemory));
01716 fprintf(fp, " Actual total memory in use: %s\r\n", memorySizeString(stats.totalActualMemory));
01717 fprintf(fp, " Memory tracking overhead: %s\r\n", memorySizeString(stats.totalActualMemory - stats.totalReportedMemory));
01718 fprintf(fp, "\r\n");
01719
01720 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
01721 fprintf(fp, "| P E A K S |\r\n");
01722 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
01723 fprintf(fp, " Allocation unit count: %10s\r\n", insertCommas(stats.peakAllocUnitCount));
01724 fprintf(fp, " Reported to application: %s\r\n", memorySizeString(stats.peakReportedMemory));
01725 fprintf(fp, " Actual: %s\r\n", memorySizeString(stats.peakActualMemory));
01726 fprintf(fp, " Memory tracking overhead: %s\r\n", memorySizeString(stats.peakActualMemory - stats.peakReportedMemory));
01727 fprintf(fp, "\r\n");
01728
01729 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
01730 fprintf(fp, "| A C C U M U L A T E D |\r\n");
01731 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
01732 fprintf(fp, " Allocation unit count: %s\r\n", memorySizeString(stats.accumulatedAllocUnitCount));
01733 fprintf(fp, " Reported to application: %s\r\n", memorySizeString(stats.accumulatedReportedMemory));
01734 fprintf(fp, " Actual: %s\r\n", memorySizeString(stats.accumulatedActualMemory));
01735 fprintf(fp, "\r\n");
01736
01737 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
01738 fprintf(fp, "| U N U S E D |\r\n");
01739 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
01740 fprintf(fp, " Memory allocated but not in use: %s\r\n", memorySizeString(m_calcAllUnused()));
01741 fprintf(fp, "\r\n");
01742
01743 dumpAllocations(fp);
01744
01745 fclose(fp);
01746 }
01747
01748
01749
01750 sMStats m_getMemoryStatistics()
01751 {
01752 return stats;
01753 }
01754
01755 #endif
01756
01757
01758
01759