AllocDebug.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. #include "../Common.h"
  2. #include "CritSect.h"
  3. #include "util/Dictionary.h"
  4. #ifdef DEF_BF_ALLOCDEBUG
  5. #define USE_BF_ALLOCDEBUG
  6. #endif
  7. #pragma warning(disable:4996)
  8. #include <cstdio>
  9. #include "AllocDebug.h"
  10. USING_NS_BF;
  11. //////////////////////////////////////////////////////////////////////////
  12. #define USE_STOMP
  13. #define STOMP_MAGIC 0xBF12BF34
  14. #ifdef BF_PLATFORM_WINDOWS
  15. class BfBitSet
  16. {
  17. public:
  18. uint32* mBits;
  19. public:
  20. BfBitSet(int numBits);
  21. ~BfBitSet();
  22. bool IsSet(int idx);
  23. void Set(int idx);
  24. void Clear(int idx);
  25. };
  26. BfBitSet::BfBitSet(int numBits)
  27. {
  28. int numInts = (numBits + 31) / 32;
  29. mBits = new uint32[numInts];
  30. memset(mBits, 0, numInts * 4);
  31. }
  32. BfBitSet::~BfBitSet()
  33. {
  34. delete mBits;
  35. }
  36. bool BfBitSet::IsSet(int idx)
  37. {
  38. return (mBits[idx / 32] & (1 << (idx % 32))) != 0;
  39. }
  40. void BfBitSet::Set(int idx)
  41. {
  42. mBits[idx / 32] |= (1 << (idx % 32));
  43. }
  44. void BfBitSet::Clear(int idx)
  45. {
  46. mBits[idx / 32] &= ~(1 << (idx % 32));
  47. }
  48. struct SA_AllocHeader
  49. {
  50. int mNumPages;
  51. int mMagic;
  52. };
  53. struct SA_AllocRange
  54. {
  55. public:
  56. static const int PAGE_SIZE = 4096;
  57. static const int NUM_PAGES = 0x8000;
  58. uint8* mMemory;
  59. int mSize;
  60. int mLastUsedIdx;
  61. BfBitSet mUsedBits;
  62. public:
  63. SA_AllocRange() : mUsedBits(NUM_PAGES)
  64. {
  65. mMemory = (uint8*)::VirtualAlloc(0, PAGE_SIZE * NUM_PAGES, MEM_RESERVE, PAGE_READWRITE);
  66. mSize = 0;
  67. mLastUsedIdx = -1;
  68. }
  69. ~SA_AllocRange()
  70. {
  71. if (mMemory != NULL)
  72. ::VirtualFree(mMemory, 0, MEM_RELEASE);
  73. }
  74. int FindFreeRange(int numPages, int from, int to)
  75. {
  76. int lastUsedIdx = from - 1;
  77. for (int pageIdx = from; pageIdx < to; pageIdx++)
  78. {
  79. if (mUsedBits.IsSet(pageIdx))
  80. {
  81. lastUsedIdx = pageIdx;
  82. }
  83. else if (pageIdx - lastUsedIdx >= numPages)
  84. {
  85. return lastUsedIdx + 1;
  86. }
  87. }
  88. return -1;
  89. }
  90. void* Alloc(int size)
  91. {
  92. int numPages = (size + sizeof(SA_AllocHeader) + PAGE_SIZE - 1) / PAGE_SIZE;
  93. int startIdx = FindFreeRange(numPages, mLastUsedIdx + 1, NUM_PAGES);
  94. if (startIdx == -1)
  95. startIdx = FindFreeRange(numPages, 0, mLastUsedIdx);
  96. if (startIdx == -1)
  97. return NULL;
  98. mLastUsedIdx = startIdx + numPages - 1;
  99. for (int markIdx = startIdx; markIdx < startIdx + numPages; markIdx++)
  100. {
  101. mUsedBits.Set(markIdx);
  102. }
  103. uint8* ptr = mMemory + startIdx*PAGE_SIZE;
  104. auto allocHeader = (SA_AllocHeader*)ptr;
  105. ::VirtualAlloc(ptr, numPages * PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE);
  106. allocHeader->mNumPages = numPages;
  107. allocHeader->mMagic = STOMP_MAGIC;
  108. int alignedOffset = sizeof(SA_AllocHeader);
  109. bool alignAtEnd = true;
  110. if (alignAtEnd)
  111. {
  112. alignedOffset = (PAGE_SIZE - (size % PAGE_SIZE));
  113. if (alignedOffset < sizeof(SA_AllocHeader))
  114. {
  115. // 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
  116. // so we don't clobber the SA_AllocHeader
  117. alignedOffset += PAGE_SIZE;
  118. }
  119. }
  120. return ptr + alignedOffset;
  121. }
  122. bool Free(void* ptr, bool leaveAllocated = false)
  123. {
  124. uint8* memPtr = (uint8*)ptr;
  125. if ((memPtr < mMemory) || (memPtr >= mMemory + PAGE_SIZE * NUM_PAGES))
  126. return false;
  127. int pageStart = (int)(memPtr - mMemory) / PAGE_SIZE;
  128. int pageOfs = (int)(memPtr - mMemory) % PAGE_SIZE;
  129. if (pageOfs < sizeof(SA_AllocHeader))
  130. pageStart--; // We actually allocated on the previous page
  131. SA_AllocHeader* allocHeader = (SA_AllocHeader*)(mMemory + pageStart*PAGE_SIZE);
  132. assert(allocHeader->mMagic == STOMP_MAGIC);
  133. if (leaveAllocated)
  134. {
  135. DWORD oldProtect;
  136. ::VirtualProtect(allocHeader, allocHeader->mNumPages * PAGE_SIZE, /*PAGE_NOACCESS*/PAGE_READONLY, &oldProtect);
  137. }
  138. else
  139. {
  140. for (int pageIdx = pageStart; pageIdx < pageStart + allocHeader->mNumPages; pageIdx++)
  141. {
  142. assert(mUsedBits.IsSet(pageIdx));
  143. mUsedBits.Clear(pageIdx);
  144. }
  145. ::VirtualFree(allocHeader, allocHeader->mNumPages * PAGE_SIZE, MEM_DECOMMIT);
  146. }
  147. return true;
  148. }
  149. };
  150. static std::list<SA_AllocRange>* gAllocRanges = NULL;
  151. static CritSect* gSA_CritSect = NULL;
  152. static bool gStompInside = false;
  153. void StompInit()
  154. {
  155. gStompInside = true;
  156. gAllocRanges = new std::list<SA_AllocRange>();
  157. gSA_CritSect = new CritSect();
  158. gStompInside = false;
  159. }
  160. void* StompAlloc(int size)
  161. {
  162. //return malloc(size);
  163. if (gStompInside)
  164. return malloc(size);
  165. if (gSA_CritSect == NULL)
  166. StompInit();
  167. AutoCrit autoCrit(*gSA_CritSect);
  168. if (gStompInside)
  169. return malloc(size);
  170. gStompInside = true;
  171. while (true)
  172. {
  173. for (auto itr = gAllocRanges->rbegin(); itr != gAllocRanges->rend(); itr++)
  174. {
  175. auto& alloc = *itr;
  176. void* result = alloc.Alloc(size);
  177. if (result != NULL)
  178. {
  179. gStompInside = false;
  180. return result;
  181. }
  182. }
  183. gAllocRanges->resize(gAllocRanges->size() + 1);
  184. }
  185. gStompInside = false;
  186. }
  187. void StompFree(void* addr)
  188. {
  189. if (gSA_CritSect == NULL)
  190. StompInit();
  191. AutoCrit autoCrit(*gSA_CritSect);
  192. bool leaveAllocated = true;
  193. for (auto& alloc : *gAllocRanges)
  194. {
  195. if (alloc.Free(addr, leaveAllocated))
  196. return;
  197. }
  198. free(addr);
  199. //assert("Invalid address" == 0);
  200. }
  201. //////////////////////////////////////////////////////////////////////////
  202. struct AllocRecord
  203. {
  204. uint8_t* mPtr;
  205. int mSize;
  206. const char* mAllocFileName;
  207. int mAllocLineNum;
  208. int mAllocTransactionIdx;
  209. const char* mFreeFileName;
  210. int mFreeLineNum;
  211. int mFreeTransactionIdx;
  212. int mHashAtFree;
  213. };
  214. int gDbgHeapTransactionIdx = 1;
  215. int64 gDbgHeapAllocBytes = 0;
  216. int64 gDbgHeapFreedBytes = 0;
  217. std::vector<AllocRecord> gDbgHeapRecords;
  218. #define ALLOC_PADDING 8
  219. #define ALLOC_MAGIC 0xBF
  220. static int DbgHeapHashMemory(void* ptr, int size)
  221. {
  222. int curHash = 0;
  223. uint8_t* curHashPtr = (uint8_t*) ptr;
  224. for (int i = 0; i < size; i++)
  225. {
  226. curHash = ((curHash ^ *curHashPtr) << 5) - curHash;
  227. curHashPtr++;
  228. }
  229. return curHash;
  230. }
  231. static void* DbgHeapAlloc(std::size_t size, const char* fileName, int lineNum)
  232. {
  233. gDbgHeapAllocBytes += size;
  234. if (gDbgHeapRecords.size() == 0)
  235. gDbgHeapRecords.reserve(8192);
  236. AllocRecord allocRecord;
  237. allocRecord.mAllocFileName = fileName;
  238. allocRecord.mAllocLineNum = lineNum;
  239. allocRecord.mAllocTransactionIdx = gDbgHeapTransactionIdx++;
  240. allocRecord.mFreeFileName = NULL;
  241. allocRecord.mFreeLineNum = 0;
  242. allocRecord.mHashAtFree = 0;
  243. allocRecord.mFreeTransactionIdx = 0;
  244. allocRecord.mSize = (int)size;
  245. allocRecord.mPtr = new uint8_t[ALLOC_PADDING + size + ALLOC_PADDING];
  246. memset(allocRecord.mPtr, ALLOC_MAGIC, ALLOC_PADDING + size + ALLOC_PADDING);
  247. gDbgHeapRecords.push_back(allocRecord);
  248. return allocRecord.mPtr + ALLOC_PADDING;
  249. }
  250. void DbgHeapFree(const void* ptr, const char* fileName, int lineNum)
  251. {
  252. if (ptr == NULL)
  253. return;
  254. bool found = false;
  255. for (int i = 0; i < (int) gDbgHeapRecords.size(); i++)
  256. {
  257. auto& allocRecord = gDbgHeapRecords[i];
  258. if (allocRecord.mPtr + ALLOC_PADDING == ptr)
  259. {
  260. assert(allocRecord.mFreeTransactionIdx == 0);
  261. gDbgHeapFreedBytes += allocRecord.mSize;
  262. allocRecord.mFreeFileName = fileName;
  263. allocRecord.mFreeLineNum = lineNum;
  264. allocRecord.mFreeTransactionIdx = gDbgHeapTransactionIdx++;;
  265. allocRecord.mHashAtFree = DbgHeapHashMemory(allocRecord.mPtr + ALLOC_PADDING, allocRecord.mSize);
  266. return;
  267. }
  268. }
  269. assert("Not found" == 0);
  270. }
  271. #endif
  272. #ifdef DEF_BF_ALLOCDEBUG
  273. void DbgHeapCheck()
  274. {
  275. for (int i = 0; i < (int) gDbgHeapRecords.size(); i++)
  276. {
  277. auto& allocRecord = gDbgHeapRecords[i];
  278. for (int i = 0; i < ALLOC_PADDING; i++)
  279. {
  280. assert(allocRecord.mPtr[i] == ALLOC_MAGIC);
  281. assert(allocRecord.mPtr[ALLOC_PADDING + allocRecord.mSize + ALLOC_PADDING - i - 1] == ALLOC_MAGIC);
  282. }
  283. if (allocRecord.mFreeTransactionIdx != 0)
  284. {
  285. int curHash = DbgHeapHashMemory(allocRecord.mPtr + ALLOC_PADDING, allocRecord.mSize);
  286. assert(allocRecord.mHashAtFree == curHash);
  287. }
  288. }
  289. #ifndef BF_MINGW
  290. _CrtCheckMemory();
  291. #endif
  292. }
  293. void DbgHeapCheckLeaks()
  294. {
  295. OutputDebugStringA("DbgHeapCheckLeaks____________________\n");
  296. for (int i = 0; i < (int) gDbgHeapRecords.size(); i++)
  297. {
  298. auto& allocRecord = gDbgHeapRecords[i];
  299. if (allocRecord.mFreeTransactionIdx == 0)
  300. {
  301. char str[1024];
  302. sprintf(str, "Alloc #%d in %s on line %d\n", allocRecord.mAllocTransactionIdx, allocRecord.mAllocFileName, allocRecord.mAllocLineNum);
  303. OutputDebugStringA(str);
  304. sprintf(str, " %08X", allocRecord.mAllocTransactionIdx /*, allocRecord.mAllocFileName, allocRecord.mAllocLineNum*/);
  305. for (int i = 0; i < std::min(allocRecord.mSize, 16); i++)
  306. sprintf(str + strlen(str), " %02X", allocRecord.mPtr[i + ALLOC_PADDING]);
  307. strcat(str, "\n");
  308. OutputDebugStringA(str);
  309. delete allocRecord.mPtr;
  310. }
  311. }
  312. gDbgHeapRecords.clear();
  313. #ifndef BF_MINGW
  314. _CrtDumpMemoryLeaks();
  315. #endif
  316. }
  317. #pragma push_macro("new")
  318. #undef new
  319. void* __cdecl operator new(std::size_t size)
  320. {
  321. #ifdef USE_STOMP
  322. return StompAlloc((int)size);
  323. #else
  324. return DbgHeapAlloc(size, "", 0);
  325. #endif
  326. }
  327. void* __cdecl operator new(std::size_t size, const char* fileName, int lineNum)
  328. {
  329. #ifdef USE_STOMP
  330. return StompAlloc((int)size);
  331. #else
  332. return DbgHeapAlloc(size, fileName, lineNum);
  333. #endif
  334. }
  335. void operator delete(void* ptr, const char* fileName, int lineNum)
  336. {
  337. #ifdef USE_STOMP
  338. StompFree(ptr);
  339. #else
  340. DbgHeapFree(ptr, fileName, lineNum);
  341. #endif
  342. }
  343. void operator delete[](void* ptr, const char* fileName, int lineNum)
  344. {
  345. #ifdef USE_STOMP
  346. StompFree(ptr);
  347. #else
  348. DbgHeapFree(ptr, fileName, lineNum);
  349. #endif
  350. }
  351. void operator delete(void* ptr)
  352. {
  353. #ifdef USE_STOMP
  354. StompFree(ptr);
  355. #else
  356. DbgHeapFree(ptr, "", 0);
  357. #endif
  358. }
  359. void operator delete[](void* ptr)
  360. {
  361. #ifdef USE_STOMP
  362. StompFree(ptr);
  363. #else
  364. DbgHeapFree(ptr, "", 0);
  365. #endif
  366. }
  367. #pragma pop_macro("new")
  368. #endif //USE_BF_ALLOCDEBUG
  369. static CritSect gCritSect;
  370. static Dictionary<String, int> gAllocDicts;
  371. void BpAllocName(const char* str, int size)
  372. {
  373. AutoCrit autoCrit(gCritSect);
  374. int* sizePtr;
  375. gAllocDicts.TryAdd(String(str), NULL, &sizePtr);
  376. *sizePtr += size;
  377. }
  378. void BpDump()
  379. {
  380. AutoCrit autoCrit(gCritSect);
  381. int totalSize = 0;
  382. OutputDebugStr("BeDump:\n");
  383. String str;
  384. for (auto& kv : gAllocDicts)
  385. {
  386. str.Clear();
  387. str.Append(' ');
  388. str.Append(kv.mKey);
  389. str.Append(' ');
  390. while (str.mLength < 32)
  391. str.Append(' ');
  392. str += StrFormat("%8dk\n", (kv.mValue + 1023) / 1024);
  393. totalSize += kv.mValue;
  394. OutputDebugStr(str);
  395. }
  396. str.Clear();
  397. str.Clear();
  398. str.Append(" TOTAL ");
  399. while (str.mLength < 32)
  400. str.Append(' ');
  401. str += StrFormat("%8dk\n", (totalSize + 1023) / 1024);
  402. OutputDebugStr(str);
  403. }