123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- #include "../Common.h"
- #include "CritSect.h"
- #include "util/Dictionary.h"
- #ifdef DEF_BF_ALLOCDEBUG
- #define USE_BF_ALLOCDEBUG
- #endif
- #pragma warning(disable:4996)
- #include <cstdio>
- #include "AllocDebug.h"
- USING_NS_BF;
- //////////////////////////////////////////////////////////////////////////
- #define USE_STOMP
- #define STOMP_MAGIC 0xBF12BF34
- #ifdef BF_PLATFORM_WINDOWS
- class BfBitSet
- {
- public:
- uint32* mBits;
- public:
- BfBitSet(int numBits);
- ~BfBitSet();
- bool IsSet(int idx);
- void Set(int idx);
- void Clear(int idx);
- };
- BfBitSet::BfBitSet(int numBits)
- {
- int numInts = (numBits + 31) / 32;
- mBits = new uint32[numInts];
- memset(mBits, 0, numInts * 4);
- }
- BfBitSet::~BfBitSet()
- {
- delete mBits;
- }
- bool BfBitSet::IsSet(int idx)
- {
- return (mBits[idx / 32] & (1 << (idx % 32))) != 0;
- }
- void BfBitSet::Set(int idx)
- {
- mBits[idx / 32] |= (1 << (idx % 32));
- }
- void BfBitSet::Clear(int idx)
- {
- mBits[idx / 32] &= ~(1 << (idx % 32));
- }
- struct SA_AllocHeader
- {
- int mNumPages;
- int mMagic;
- };
- struct SA_AllocRange
- {
- public:
- static const int PAGE_SIZE = 4096;
- static const int NUM_PAGES = 0x8000;
- uint8* mMemory;
- int mSize;
- int mLastUsedIdx;
- BfBitSet mUsedBits;
- public:
- SA_AllocRange() : mUsedBits(NUM_PAGES)
- {
- mMemory = (uint8*)::VirtualAlloc(0, PAGE_SIZE * NUM_PAGES, MEM_RESERVE, PAGE_READWRITE);
- mSize = 0;
- mLastUsedIdx = -1;
- }
- ~SA_AllocRange()
- {
- if (mMemory != NULL)
- ::VirtualFree(mMemory, 0, MEM_RELEASE);
- }
- int FindFreeRange(int numPages, int from, int to)
- {
- int lastUsedIdx = from - 1;
- for (int pageIdx = from; pageIdx < to; pageIdx++)
- {
- if (mUsedBits.IsSet(pageIdx))
- {
- lastUsedIdx = pageIdx;
- }
- else if (pageIdx - lastUsedIdx >= numPages)
- {
- return lastUsedIdx + 1;
- }
- }
- return -1;
- }
- void* Alloc(int size)
- {
- int numPages = (size + sizeof(SA_AllocHeader) + PAGE_SIZE - 1) / PAGE_SIZE;
- int startIdx = FindFreeRange(numPages, mLastUsedIdx + 1, NUM_PAGES);
- if (startIdx == -1)
- startIdx = FindFreeRange(numPages, 0, mLastUsedIdx);
- if (startIdx == -1)
- return NULL;
- mLastUsedIdx = startIdx + numPages - 1;
- for (int markIdx = startIdx; markIdx < startIdx + numPages; markIdx++)
- {
- mUsedBits.Set(markIdx);
- }
- uint8* ptr = mMemory + startIdx*PAGE_SIZE;
- auto allocHeader = (SA_AllocHeader*)ptr;
- ::VirtualAlloc(ptr, numPages * PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE);
- allocHeader->mNumPages = numPages;
- allocHeader->mMagic = STOMP_MAGIC;
- int alignedOffset = sizeof(SA_AllocHeader);
- bool alignAtEnd = true;
- if (alignAtEnd)
- {
- alignedOffset = (PAGE_SIZE - (size % PAGE_SIZE));
- if (alignedOffset < sizeof(SA_AllocHeader))
- {
- // For cases where the alloc size (mod PAGE_SIZE) is almost equal to the page size, we need to bump the offset into the next page
- // so we don't clobber the SA_AllocHeader
- alignedOffset += PAGE_SIZE;
- }
- }
- return ptr + alignedOffset;
- }
- bool Free(void* ptr, bool leaveAllocated = false)
- {
- uint8* memPtr = (uint8*)ptr;
- if ((memPtr < mMemory) || (memPtr >= mMemory + PAGE_SIZE * NUM_PAGES))
- return false;
- int pageStart = (int)(memPtr - mMemory) / PAGE_SIZE;
- int pageOfs = (int)(memPtr - mMemory) % PAGE_SIZE;
- if (pageOfs < sizeof(SA_AllocHeader))
- pageStart--; // We actually allocated on the previous page
- SA_AllocHeader* allocHeader = (SA_AllocHeader*)(mMemory + pageStart*PAGE_SIZE);
- assert(allocHeader->mMagic == STOMP_MAGIC);
- if (leaveAllocated)
- {
- DWORD oldProtect;
- ::VirtualProtect(allocHeader, allocHeader->mNumPages * PAGE_SIZE, /*PAGE_NOACCESS*/PAGE_READONLY, &oldProtect);
- }
- else
- {
- for (int pageIdx = pageStart; pageIdx < pageStart + allocHeader->mNumPages; pageIdx++)
- {
- assert(mUsedBits.IsSet(pageIdx));
- mUsedBits.Clear(pageIdx);
- }
- ::VirtualFree(allocHeader, allocHeader->mNumPages * PAGE_SIZE, MEM_DECOMMIT);
- }
- return true;
- }
- };
- static std::list<SA_AllocRange>* gAllocRanges = NULL;
- static CritSect* gSA_CritSect = NULL;
- static bool gStompInside = false;
- void StompInit()
- {
- gStompInside = true;
- gAllocRanges = new std::list<SA_AllocRange>();
- gSA_CritSect = new CritSect();
- gStompInside = false;
- }
- void* StompAlloc(int size)
- {
- //return malloc(size);
- if (gStompInside)
- return malloc(size);
- if (gSA_CritSect == NULL)
- StompInit();
- AutoCrit autoCrit(*gSA_CritSect);
- if (gStompInside)
- return malloc(size);
- gStompInside = true;
- while (true)
- {
- for (auto itr = gAllocRanges->rbegin(); itr != gAllocRanges->rend(); itr++)
- {
- auto& alloc = *itr;
- void* result = alloc.Alloc(size);
- if (result != NULL)
- {
- gStompInside = false;
- return result;
- }
- }
- gAllocRanges->resize(gAllocRanges->size() + 1);
- }
- gStompInside = false;
- }
- void StompFree(void* addr)
- {
- if (gSA_CritSect == NULL)
- StompInit();
- AutoCrit autoCrit(*gSA_CritSect);
-
- bool leaveAllocated = true;
- for (auto& alloc : *gAllocRanges)
- {
- if (alloc.Free(addr, leaveAllocated))
- return;
- }
- free(addr);
- //assert("Invalid address" == 0);
- }
- //////////////////////////////////////////////////////////////////////////
- struct AllocRecord
- {
- uint8_t* mPtr;
- int mSize;
- const char* mAllocFileName;
- int mAllocLineNum;
- int mAllocTransactionIdx;
- const char* mFreeFileName;
- int mFreeLineNum;
- int mFreeTransactionIdx;
- int mHashAtFree;
- };
- int gDbgHeapTransactionIdx = 1;
- int64 gDbgHeapAllocBytes = 0;
- int64 gDbgHeapFreedBytes = 0;
- std::vector<AllocRecord> gDbgHeapRecords;
- #define ALLOC_PADDING 8
- #define ALLOC_MAGIC 0xBF
- static int DbgHeapHashMemory(void* ptr, int size)
- {
- int curHash = 0;
- uint8_t* curHashPtr = (uint8_t*) ptr;
- for (int i = 0; i < size; i++)
- {
- curHash = ((curHash ^ *curHashPtr) << 5) - curHash;
- curHashPtr++;
- }
- return curHash;
- }
- static void* DbgHeapAlloc(std::size_t size, const char* fileName, int lineNum)
- {
- gDbgHeapAllocBytes += size;
- if (gDbgHeapRecords.size() == 0)
- gDbgHeapRecords.reserve(8192);
- AllocRecord allocRecord;
- allocRecord.mAllocFileName = fileName;
- allocRecord.mAllocLineNum = lineNum;
- allocRecord.mAllocTransactionIdx = gDbgHeapTransactionIdx++;
- allocRecord.mFreeFileName = NULL;
- allocRecord.mFreeLineNum = 0;
- allocRecord.mHashAtFree = 0;
- allocRecord.mFreeTransactionIdx = 0;
- allocRecord.mSize = (int)size;
- allocRecord.mPtr = new uint8_t[ALLOC_PADDING + size + ALLOC_PADDING];
- memset(allocRecord.mPtr, ALLOC_MAGIC, ALLOC_PADDING + size + ALLOC_PADDING);
- gDbgHeapRecords.push_back(allocRecord);
- return allocRecord.mPtr + ALLOC_PADDING;
- }
- void DbgHeapFree(const void* ptr, const char* fileName, int lineNum)
- {
- if (ptr == NULL)
- return;
- bool found = false;
- for (int i = 0; i < (int) gDbgHeapRecords.size(); i++)
- {
- auto& allocRecord = gDbgHeapRecords[i];
- if (allocRecord.mPtr + ALLOC_PADDING == ptr)
- {
- assert(allocRecord.mFreeTransactionIdx == 0);
- gDbgHeapFreedBytes += allocRecord.mSize;
- allocRecord.mFreeFileName = fileName;
- allocRecord.mFreeLineNum = lineNum;
- allocRecord.mFreeTransactionIdx = gDbgHeapTransactionIdx++;;
- allocRecord.mHashAtFree = DbgHeapHashMemory(allocRecord.mPtr + ALLOC_PADDING, allocRecord.mSize);
- return;
- }
- }
- assert("Not found" == 0);
- }
- #endif
- #ifdef DEF_BF_ALLOCDEBUG
- void DbgHeapCheck()
- {
- for (int i = 0; i < (int) gDbgHeapRecords.size(); i++)
- {
- auto& allocRecord = gDbgHeapRecords[i];
- for (int i = 0; i < ALLOC_PADDING; i++)
- {
- assert(allocRecord.mPtr[i] == ALLOC_MAGIC);
- assert(allocRecord.mPtr[ALLOC_PADDING + allocRecord.mSize + ALLOC_PADDING - i - 1] == ALLOC_MAGIC);
- }
- if (allocRecord.mFreeTransactionIdx != 0)
- {
- int curHash = DbgHeapHashMemory(allocRecord.mPtr + ALLOC_PADDING, allocRecord.mSize);
- assert(allocRecord.mHashAtFree == curHash);
- }
- }
- #ifndef BF_MINGW
- _CrtCheckMemory();
- #endif
- }
- void DbgHeapCheckLeaks()
- {
- OutputDebugStringA("DbgHeapCheckLeaks____________________\n");
- for (int i = 0; i < (int) gDbgHeapRecords.size(); i++)
- {
- auto& allocRecord = gDbgHeapRecords[i];
- if (allocRecord.mFreeTransactionIdx == 0)
- {
- char str[1024];
- sprintf(str, "Alloc #%d in %s on line %d\n", allocRecord.mAllocTransactionIdx, allocRecord.mAllocFileName, allocRecord.mAllocLineNum);
- OutputDebugStringA(str);
- sprintf(str, " %08X", allocRecord.mAllocTransactionIdx /*, allocRecord.mAllocFileName, allocRecord.mAllocLineNum*/);
- for (int i = 0; i < std::min(allocRecord.mSize, 16); i++)
- sprintf(str + strlen(str), " %02X", allocRecord.mPtr[i + ALLOC_PADDING]);
- strcat(str, "\n");
- OutputDebugStringA(str);
- delete allocRecord.mPtr;
- }
- }
- gDbgHeapRecords.clear();
- #ifndef BF_MINGW
- _CrtDumpMemoryLeaks();
- #endif
- }
- #pragma push_macro("new")
- #undef new
- void* __cdecl operator new(std::size_t size)
- {
- #ifdef USE_STOMP
- return StompAlloc((int)size);
- #else
- return DbgHeapAlloc(size, "", 0);
- #endif
- }
- void* __cdecl operator new(std::size_t size, const char* fileName, int lineNum)
- {
- #ifdef USE_STOMP
- return StompAlloc((int)size);
- #else
- return DbgHeapAlloc(size, fileName, lineNum);
- #endif
- }
- void operator delete(void* ptr, const char* fileName, int lineNum)
- {
- #ifdef USE_STOMP
- StompFree(ptr);
- #else
- DbgHeapFree(ptr, fileName, lineNum);
- #endif
- }
- void operator delete[](void* ptr, const char* fileName, int lineNum)
- {
- #ifdef USE_STOMP
- StompFree(ptr);
- #else
- DbgHeapFree(ptr, fileName, lineNum);
- #endif
- }
- void operator delete(void* ptr)
- {
- #ifdef USE_STOMP
- StompFree(ptr);
- #else
- DbgHeapFree(ptr, "", 0);
- #endif
- }
- void operator delete[](void* ptr)
- {
- #ifdef USE_STOMP
- StompFree(ptr);
- #else
- DbgHeapFree(ptr, "", 0);
- #endif
- }
- #pragma pop_macro("new")
- #endif //USE_BF_ALLOCDEBUG
- static CritSect gCritSect;
- static Dictionary<String, int> gAllocDicts;
- void BpAllocName(const char* str, int size)
- {
- AutoCrit autoCrit(gCritSect);
- int* sizePtr;
- gAllocDicts.TryAdd(String(str), NULL, &sizePtr);
- *sizePtr += size;
- }
- void BpDump()
- {
- AutoCrit autoCrit(gCritSect);
- int totalSize = 0;
- OutputDebugStr("BeDump:\n");
- String str;
- for (auto& kv : gAllocDicts)
- {
- str.Clear();
- str.Append(' ');
- str.Append(kv.mKey);
- str.Append(' ');
- while (str.mLength < 32)
- str.Append(' ');
- str += StrFormat("%8dk\n", (kv.mValue + 1023) / 1024);
- totalSize += kv.mValue;
- OutputDebugStr(str);
- }
- str.Clear();
- str.Clear();
- str.Append(" TOTAL ");
- while (str.mLength < 32)
- str.Append(' ');
- str += StrFormat("%8dk\n", (totalSize + 1023) / 1024);
- OutputDebugStr(str);
- }
|