DbgInternal.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. #pragma warning(disable:4996)
  2. #define HEAPHOOK
  3. #include <stdio.h>
  4. //#include <malloc.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. //#define OBJECT_GUARD_END_SIZE 8
  8. #define OBJECT_GUARD_END_SIZE 0
  9. //#define BF_USE_STOMP_ALLOC 1
  10. #ifdef _MSC_VER
  11. #include <intrin.h>
  12. #pragma intrinsic(_ReturnAddress)
  13. #define BF_RETURN_ADDRESS _ReturnAddress()
  14. #else
  15. #define BF_RETURN_ADDRESS __builtin_return_address(0)
  16. #endif
  17. #include "BeefySysLib/Common.h"
  18. #include "../rt/BfObjects.h"
  19. #include "gc.h"
  20. #include "../rt/StompAlloc.h"
  21. #include "BeefySysLib/platform/PlatformHelper.h"
  22. USING_NS_BF;
  23. #ifdef BF_PLATFORM_WINDOWS
  24. bf::System::Runtime::BfRtCallbacks gBfRtDbgCallbacks;
  25. BfRtFlags gBfRtDbgFlags = (BfRtFlags)0;
  26. #endif
  27. namespace bf
  28. {
  29. namespace System
  30. {
  31. class Object;
  32. class Exception;
  33. class Internal
  34. {
  35. public:
  36. BFRT_EXPORT static intptr Dbg_PrepareStackTrace(intptr baseAllocSize, intptr maxStackTraceDepth);
  37. BFRT_EXPORT void Dbg_ReserveMetadataBytes(intptr metadataBytes, intptr& curAllocBytes);
  38. BFRT_EXPORT void* Dbg_GetMetadata(System::Object* obj);
  39. BFRT_EXPORT static void Dbg_ObjectCreated(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData);
  40. BFRT_EXPORT static void Dbg_ObjectCreatedEx(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData);
  41. BFRT_EXPORT static void Dbg_ObjectAllocated(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData);
  42. BFRT_EXPORT static void Dbg_ObjectAllocatedEx(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData);
  43. BFRT_EXPORT static Object* Dbg_ObjectAlloc(bf::System::Reflection::TypeInstance* typeInst, intptr size);
  44. BFRT_EXPORT static Object* Dbg_ObjectAlloc(bf::System::ClassVData* classVData, intptr size, intptr align, intptr maxStackTraceDept);
  45. BFRT_EXPORT static void Dbg_MarkObjectDeleted(bf::System::Object* obj);
  46. BFRT_EXPORT static void Dbg_ObjectStackInit(bf::System::Object* result, bf::System::ClassVData* classVData);
  47. BFRT_EXPORT static void Dbg_ObjectPreDelete(bf::System::Object* obj);
  48. BFRT_EXPORT static void Dbg_ObjectPreCustomDelete(bf::System::Object* obj);
  49. BFRT_EXPORT static void* Dbg_RawMarkedAlloc(intptr size, void* markFunc);
  50. BFRT_EXPORT static void* Dbg_RawMarkedArrayAlloc(intptr elemCount, intptr elemSize, void* markFunc);
  51. BFRT_EXPORT static void* Dbg_RawAlloc(intptr size);
  52. BFRT_EXPORT static void* Dbg_RawObjectAlloc(intptr size);
  53. BFRT_EXPORT static void* Dbg_RawAlloc(intptr size, DbgRawAllocData* rawAllocData);
  54. BFRT_EXPORT static void Dbg_RawFree(void* ptr);
  55. };
  56. namespace IO
  57. {
  58. class File
  59. {
  60. private:
  61. BFRT_EXPORT static bool Exists(char* fileName);
  62. };
  63. class Directory
  64. {
  65. private:
  66. BFRT_EXPORT static bool Exists(char* fileName);
  67. };
  68. }
  69. namespace Diagnostics
  70. {
  71. namespace Contracts
  72. {
  73. class Contract
  74. {
  75. public:
  76. enum ContractFailureKind : uint8
  77. {
  78. ContractFailureKind_Precondition,
  79. //[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Postcondition")]
  80. ContractFailureKind_Postcondition,
  81. //[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Postcondition")]
  82. ContractFailureKind_PostconditionOnException,
  83. ContractFailureKind_Invariant,
  84. ContractFailureKind_Assert,
  85. ContractFailureKind_Assume,
  86. };
  87. private:
  88. BFRT_EXPORT static void ReportFailure(ContractFailureKind failureKind, char* userMessage, int userMessageLen, char* conditionText, int conditionTextLen);
  89. };
  90. }
  91. class Debug
  92. {
  93. private:
  94. BFRT_EXPORT static void Write(char* str, intptr strLen);
  95. };
  96. }
  97. namespace FFI
  98. {
  99. enum FFIABI : int32;
  100. enum FFIResult : int32;
  101. struct FFIType;
  102. struct FFILIB
  103. {
  104. struct FFICIF;
  105. BFRT_EXPORT static void* ClosureAlloc(intptr size, void** outFunc);
  106. BFRT_EXPORT static FFIResult PrepCif(FFICIF* cif, FFIABI abi, int32 nargs, FFIType* rtype, FFIType** argTypes);
  107. BFRT_EXPORT static void Call(FFICIF* cif, void* funcPtr, void* rvalue, void** args);
  108. };
  109. }
  110. }
  111. }
  112. //#define BF_TRACK_SIZES 1
  113. #if BF_TRACK_SIZES
  114. static int sAllocSizes[1024 * 1024];
  115. static int sHighestId = 0;
  116. #endif
  117. using namespace bf::System;
  118. /*static void* MallocHook(size_t size, const void *caller)
  119. {
  120. printf("MallocHook\n");
  121. return NULL;
  122. }*/
  123. /*static int __cdecl HeapHook(int a, size_t b, void* c, void** d)
  124. {
  125. printf("Heap Hook\n");
  126. return 0;
  127. }*/
  128. //////////////////////////////////////////////////////////////////////////
  129. Beefy::StringT<0> gDbgErrorString;
  130. extern DbgRawAllocData sEmptyAllocData;
  131. extern DbgRawAllocData sObjectAllocData;
  132. #define SETUP_ERROR(str, skip) gDbgErrorString = str; BFRTCALLBACKS.DebugMessageData_SetupError(str, skip)
  133. #ifdef BF_PLATFORM_WINDOWS
  134. #define BF_CAPTURE_STACK(skipCount, outFrames, wantCount) (int)RtlCaptureStackBackTrace(skipCount, wantCount, (void**)outFrames, NULL)
  135. #else
  136. #define BF_CAPTURE_STACK(skipCount, outFrames, wantCount) BfpStack_CaptureBackTrace(skipCount, outFrames, wantCount)
  137. #endif
  138. static void GetCrashInfo()
  139. {
  140. if (!gDbgErrorString.IsEmpty())
  141. {
  142. Beefy::String debugStr;
  143. debugStr += "Beef Error: ";
  144. debugStr += gDbgErrorString;
  145. BfpSystem_AddCrashInfo(debugStr.c_str());
  146. }
  147. }
  148. void bf::System::Runtime::Dbg_Init(int version, int flags, BfRtCallbacks* callbacks)
  149. {
  150. #ifndef BFRTMERGED
  151. if (version != BFRT_VERSION)
  152. {
  153. BfpSystem_FatalError(StrFormat("BeefDbg build version '%d' does not match requested version '%d'", BFRT_VERSION, version).c_str(), "BEEF FATAL ERROR");
  154. }
  155. if (gBfRtDbgCallbacks.Alloc != NULL)
  156. {
  157. BfpSystem_FatalError(StrFormat("BeefDbg already initialized. Multiple executable modules in the same process cannot dynamically link to the Beef debug runtime.").c_str(), "BEEF FATAL ERROR");
  158. }
  159. gBfRtDbgCallbacks = *callbacks;
  160. gBfRtDbgFlags = (BfRtFlags)flags;
  161. #ifdef BF_GC_SUPPORTED
  162. gGCDbgData.mDbgFlags = gBfRtDbgFlags;
  163. #endif
  164. #endif
  165. }
  166. void* bf::System::Runtime::Dbg_GetCrashInfoFunc()
  167. {
  168. return *(void**)&GetCrashInfo;
  169. }
  170. //////////////////////////////////////////////////////////////////////////
  171. void Internal::Dbg_MarkObjectDeleted(bf::System::Object* object)
  172. {
  173. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  174. if ((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0)
  175. object->mObjectFlags = (BfObjectFlags)((object->mObjectFlags & ~BfObjectFlag_StackAlloc) | BfObjectFlag_Deleted);
  176. #ifdef BF_GC_SUPPORTED
  177. gBFGC.ObjectDeleteRequested(object);
  178. #endif
  179. }
  180. int GetStackTrace(void **result, int max_depth, int skip_count);
  181. void BfLog(const char* fmt ...);
  182. static const int cMaxStackTraceCount = 1024;
  183. struct PendingAllocState
  184. {
  185. bool mHasData;
  186. void* mStackTrace[cMaxStackTraceCount];
  187. int mStackTraceCount;
  188. int mMetadataBytes;
  189. bool mIsLargeAlloc;
  190. bool IsSmall(intptr curAllocBytes)
  191. {
  192. if ((mStackTraceCount > 255) || (mMetadataBytes > 255))
  193. return false;
  194. const intptr maxSmallObjectSize = ((intptr)1 << ((sizeof(intptr) - 2) * 8)) - 1;
  195. if (curAllocBytes <= maxSmallObjectSize)
  196. return true;
  197. intptr objBytes = curAllocBytes - mStackTraceCount * sizeof(intptr) - mMetadataBytes;
  198. return (objBytes < maxSmallObjectSize);
  199. }
  200. };
  201. static __thread PendingAllocState gPendingAllocState = { 0 };
  202. void Internal::Dbg_ReserveMetadataBytes(intptr metadataBytes, intptr& curAllocBytes)
  203. {
  204. bool isSmall = gPendingAllocState.IsSmall(curAllocBytes);
  205. gPendingAllocState.mMetadataBytes += (int)metadataBytes;
  206. curAllocBytes += metadataBytes;
  207. if ((isSmall) && (gPendingAllocState.mMetadataBytes > 255))
  208. {
  209. // We just went to 'small' to not small
  210. curAllocBytes += sizeof(intptr);
  211. }
  212. }
  213. void* Internal::Dbg_GetMetadata(bf::System::Object* obj)
  214. {
  215. return NULL;
  216. }
  217. intptr Internal::Dbg_PrepareStackTrace(intptr baseAllocSize, intptr maxStackTraceDepth)
  218. {
  219. int allocSize = 0;
  220. if (maxStackTraceDepth > 1)
  221. {
  222. int capturedTraceCount = BF_CAPTURE_STACK(1, (intptr*)gPendingAllocState.mStackTrace, min((int)maxStackTraceDepth, 1024));
  223. gPendingAllocState.mStackTraceCount = capturedTraceCount;
  224. const intptr maxSmallObjectSize = ((intptr)1 << ((sizeof(intptr) - 2) * 8)) - 1;
  225. if ((capturedTraceCount > 255) || (baseAllocSize >= maxSmallObjectSize))
  226. {
  227. gPendingAllocState.mIsLargeAlloc = true;
  228. allocSize += (1 + capturedTraceCount) * sizeof(intptr);
  229. }
  230. else
  231. {
  232. gPendingAllocState.mIsLargeAlloc = false;
  233. allocSize += capturedTraceCount * sizeof(intptr);
  234. }
  235. }
  236. return allocSize;
  237. }
  238. bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::Reflection::TypeInstance* typeInst, intptr size)
  239. {
  240. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  241. Object* result;
  242. int allocSize = BF_ALIGN(size, typeInst->mInstAlign);
  243. uint8* allocBytes = (uint8*)BfObjectAllocate(allocSize, typeInst->_GetType());
  244. // int dataOffset = (int)(sizeof(intptr) * 2);
  245. // memset(allocBytes + dataOffset, 0, size - dataOffset);
  246. result = (bf::System::Object*)allocBytes;
  247. auto classVData = typeInst->mTypeClassVData;
  248. #ifndef BFRT_NODBGFLAGS
  249. intptr dbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  250. result->mClassVData = (intptr)classVData | (intptr)BfObjectFlag_Allocated /*| BFGC::sAllocFlags*/;
  251. BF_FULL_MEMORY_FENCE(); // Since we depend on mDbAllocInfo to determine if we are allocated, we need to set this last after we're set up
  252. result->mDbgAllocInfo = dbgAllocInfo;
  253. BF_FULL_MEMORY_FENCE();
  254. result->mClassVData = (result->mClassVData & ~BF_OBJECTFLAG_MARK_ID_MASK) | BFGC::sAllocFlags;
  255. #endif
  256. return result;
  257. }
  258. //#define DBG_OBJECTEND
  259. bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData, intptr size, intptr align, intptr maxStackTraceDepth)
  260. {
  261. void* stackTrace[1024];
  262. int capturedTraceCount = 0;
  263. intptr allocSize = size;
  264. bool largeAllocInfo = false;
  265. if ((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0)
  266. {
  267. if (maxStackTraceDepth > 1)
  268. {
  269. capturedTraceCount = BF_CAPTURE_STACK(1, (intptr*)stackTrace, min((int)maxStackTraceDepth, 1024));
  270. const intptr maxSmallObjectSize = ((intptr)1 << ((sizeof(intptr) - 2) * 8)) - 1;
  271. if ((capturedTraceCount > 255) || (size >= maxSmallObjectSize))
  272. {
  273. largeAllocInfo = true;
  274. allocSize += (1 + capturedTraceCount) * sizeof(intptr);
  275. }
  276. else
  277. allocSize += capturedTraceCount * sizeof(intptr);
  278. }
  279. }
  280. #ifdef DBG_OBJECTEND
  281. allocSize += 4;
  282. #endif
  283. bf::System::Object* result;
  284. if ((BFRTFLAGS & BfRtFlags_LeakCheck) != 0)
  285. {
  286. allocSize = BF_ALIGN(allocSize, align);
  287. uint8* allocBytes = (uint8*)BfObjectAllocate(allocSize, classVData->mType);
  288. // int dataOffset = (int)(sizeof(intptr) * 2);
  289. // memset(allocBytes + dataOffset, 0, size - dataOffset);
  290. result = (bf::System::Object*)(allocBytes);
  291. }
  292. else
  293. {
  294. #if BF_USE_STOMP_ALLOC
  295. result = (bf::System::Object*)StompAlloc(allocSize);
  296. #elif BF_TRACK_SIZES
  297. sHighestId = BF_MAX(sHighestId, classVData->mType->mTypeId);
  298. uint8* allocPtr = (uint8*)malloc(size + 16);
  299. *((int*)allocPtr) = size;
  300. sAllocSizes[classVData->mType->mTypeId] += size;
  301. result = (bf::System::Object*)(allocPtr + 16);
  302. #else
  303. if ((BFRTFLAGS & BfRtFlags_DebugAlloc) != 0)
  304. {
  305. uint8* allocBytes = (uint8*)BfRawAllocate(allocSize, &sObjectAllocData, NULL, 0);
  306. result = (bf::System::Object*)allocBytes;
  307. }
  308. else
  309. {
  310. uint8* allocBytes = (uint8*)BFRTCALLBACKS.Alloc(allocSize);
  311. result = (bf::System::Object*)allocBytes;
  312. }
  313. #endif
  314. }
  315. #ifndef BFRT_NODBGFLAGS
  316. if ((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0)
  317. {
  318. // The order is very important here-
  319. // Once we set mDbgAllocInfo, the memory will be recognized by the GC as being a valid object.
  320. // There's a race condition with the alloc flags, however-- if the GC pauses our thread after we write
  321. // the mClassVData but before mDbgAllocInfo is set, then the object won't be marked with the new mark id
  322. // and it will appear as a leak during the Sweep. Thus, we leave the sAllocFlags off until AFTER
  323. // mDbgAllocInfo is set, so that the object will be ignored during sweeping unless we get all the way
  324. // through.
  325. intptr dbgAllocInfo;
  326. auto classVDataVal = (intptr)classVData | (intptr)BfObjectFlag_Allocated;
  327. result->mClassVData = classVDataVal;
  328. if (maxStackTraceDepth <= 1)
  329. dbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  330. else
  331. {
  332. if (largeAllocInfo)
  333. {
  334. result->mClassVData |= (intptr)BfObjectFlag_AllocInfo;
  335. dbgAllocInfo = size;
  336. *(intptr*)((uint8*)result + size) = capturedTraceCount;
  337. memcpy((uint8*)result + size + sizeof(intptr), stackTrace, capturedTraceCount * sizeof(intptr));
  338. }
  339. else
  340. {
  341. result->mClassVData |= (intptr)BfObjectFlag_AllocInfo_Short;
  342. dbgAllocInfo = (size << 16) | capturedTraceCount;
  343. memcpy((uint8*)result + size, stackTrace, capturedTraceCount * sizeof(intptr));
  344. }
  345. }
  346. BF_FULL_MEMORY_FENCE(); // Since we depend on mDbAllocInfo to determine if we are allocated, we need to set this last after we're set up
  347. result->mDbgAllocInfo = dbgAllocInfo;
  348. BF_FULL_MEMORY_FENCE();
  349. // If the GC has already set the correct mark id then we don't need want to overwrite it - we could have an old value
  350. BfpSystem_InterlockedCompareExchangePtr((uintptr*)&result->mClassVData, (uintptr)classVDataVal, classVDataVal | BFGC::sAllocFlags);
  351. //result->mClassVData = (result->mClassVData & ~BF_OBJECTFLAG_MARK_ID_MASK) | BFGC::sAllocFlags;
  352. }
  353. else
  354. #endif
  355. result->mClassVData = (intptr)classVData;
  356. //OutputDebugStrF("Object %@ ClassVData %@\n", result, classVData);
  357. #ifdef DBG_OBJECTEND
  358. *(uint32*)((uint8*)result + size) = 0xBFBFBFBF;
  359. #endif
  360. return result;
  361. }
  362. void Internal::Dbg_ObjectStackInit(bf::System::Object* result, bf::System::ClassVData* classVData)
  363. {
  364. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  365. result->mClassVData = (intptr)classVData | (intptr)BfObjectFlag_StackAlloc;
  366. #ifndef BFRT_NODBGFLAGS
  367. result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  368. #endif
  369. }
  370. static void SetupDbgAllocInfo(bf::System::Object* result, intptr origSize)
  371. {
  372. #ifndef BFRT_NODBGFLAGS
  373. if (gPendingAllocState.mStackTraceCount == 0)
  374. {
  375. result->mDbgAllocInfo = 1; // Must have a value
  376. return;
  377. }
  378. if (gPendingAllocState.mStackTraceCount == 1)
  379. {
  380. result->mDbgAllocInfo = (intptr)gPendingAllocState.mStackTrace[0];
  381. return;
  382. }
  383. if (gPendingAllocState.mIsLargeAlloc)
  384. {
  385. result->mClassVData |= (intptr)BfObjectFlag_AllocInfo;
  386. result->mDbgAllocInfo = origSize;
  387. *(intptr*)((uint8*)result + origSize) = gPendingAllocState.mStackTraceCount;
  388. memcpy((uint8*)result + origSize + sizeof(intptr), gPendingAllocState.mStackTrace, gPendingAllocState.mStackTraceCount * sizeof(intptr));
  389. }
  390. else
  391. {
  392. result->mClassVData |= (intptr)BfObjectFlag_AllocInfo_Short;
  393. result->mDbgAllocInfo = (origSize << 16) | gPendingAllocState.mStackTraceCount;
  394. memcpy((uint8*)result + origSize, gPendingAllocState.mStackTrace, gPendingAllocState.mStackTraceCount * sizeof(intptr));
  395. }
  396. #endif
  397. }
  398. void Internal::Dbg_ObjectCreated(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData)
  399. {
  400. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  401. #ifndef BFRT_NODBGFLAGS
  402. BF_ASSERT_REL((result->mClassVData & ~(BfObjectFlag_Allocated | BfObjectFlag_Mark3)) == (intptr)classVData);
  403. result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  404. #endif
  405. }
  406. void Internal::Dbg_ObjectCreatedEx(bf::System::Object* result, intptr origSize, bf::System::ClassVData* classVData)
  407. {
  408. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  409. #ifndef BFRT_NODBGFLAGS
  410. BF_ASSERT_REL((result->mClassVData & ~(BfObjectFlag_Allocated | BfObjectFlag_Mark3)) == (intptr)classVData);
  411. SetupDbgAllocInfo(result, origSize);
  412. #endif
  413. }
  414. void Internal::Dbg_ObjectAllocated(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData)
  415. {
  416. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  417. result->mClassVData = (intptr)classVData;
  418. #ifndef BFRT_NODBGFLAGS
  419. result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  420. #endif
  421. }
  422. void Internal::Dbg_ObjectAllocatedEx(bf::System::Object* result, intptr origSize, bf::System::ClassVData* classVData)
  423. {
  424. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  425. result->mClassVData = (intptr)classVData;
  426. SetupDbgAllocInfo(result, origSize);
  427. }
  428. void Internal::Dbg_ObjectPreDelete(bf::System::Object* object)
  429. {
  430. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  431. #ifndef BFRT_NODBGFLAGS
  432. const char* errorPtr = NULL;
  433. if ((object->mObjectFlags & BfObjectFlag_StackAlloc) != 0)
  434. {
  435. if ((object->mObjectFlags & BfObjectFlag_Allocated) == 0)
  436. errorPtr = "Attempting to delete stack-allocated object";
  437. else
  438. errorPtr = "Deleting an object that was detected as leaked (internal error)";
  439. }
  440. else if ((object->mObjectFlags & BfObjectFlag_AppendAlloc) != 0)
  441. errorPtr = "Attempting to delete append-allocated object, use 'delete append' statement instead of 'delete'";
  442. else if ((object->mObjectFlags & BfObjectFlag_Allocated) == 0)
  443. {
  444. errorPtr = "Attempting to delete custom-allocated object without specifying allocator";
  445. #if _WIN32
  446. MEMORY_BASIC_INFORMATION stackInfo = { 0 };
  447. VirtualQuery(object, &stackInfo, sizeof(MEMORY_BASIC_INFORMATION));
  448. if ((stackInfo.Protect & PAGE_READONLY) != 0)
  449. errorPtr = "Attempting to delete read-only object";
  450. #endif
  451. }
  452. else if ((object->mObjectFlags & BfObjectFlag_Deleted) != 0)
  453. errorPtr = "Attempting second delete on object";
  454. if (errorPtr != NULL)
  455. {
  456. Beefy::String errorStr = errorPtr;
  457. Beefy::String typeName = object->GetTypeName();
  458. errorStr += "\x1";
  459. errorStr += StrFormat("LEAK\t0x%@\n", object);
  460. errorStr += StrFormat(" (%s)0x%@\n", typeName.c_str(), object);
  461. SETUP_ERROR(errorStr.c_str(), 2);
  462. BF_DEBUG_BREAK();
  463. BFRTCALLBACKS.DebugMessageData_Fatal();
  464. return;
  465. }
  466. #endif
  467. }
  468. void Internal::Dbg_ObjectPreCustomDelete(bf::System::Object* object)
  469. {
  470. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  471. const char* errorPtr = NULL;
  472. if ((object->mObjectFlags & BfObjectFlag_StackAlloc) != 0)
  473. errorPtr = "Attempting to delete stack-allocated object";
  474. if ((object->mObjectFlags & BfObjectFlag_Deleted) != 0)
  475. errorPtr = "Attempting second delete on object";
  476. if (errorPtr != NULL)
  477. {
  478. Beefy::String errorStr = errorPtr;
  479. Beefy::String typeName = object->GetTypeName();
  480. errorStr += "\x1";
  481. errorStr += StrFormat("LEAK\t0x%@\n", object);
  482. errorStr += StrFormat(" (%s)0x%@\n", typeName.c_str(), object);
  483. SETUP_ERROR(errorStr.c_str(), 2);
  484. BF_DEBUG_BREAK();
  485. BFRTCALLBACKS.DebugMessageData_Fatal();
  486. return;
  487. }
  488. }
  489. void* Internal::Dbg_RawAlloc(intptr size, DbgRawAllocData* rawAllocData)
  490. {
  491. void* stackTrace[1024];
  492. int capturedTraceCount = 0;
  493. #ifndef BFRT_NODBGFLAGS
  494. if (rawAllocData->mMaxStackTrace == 1)
  495. {
  496. stackTrace[0] = BF_RETURN_ADDRESS;
  497. capturedTraceCount = 1;
  498. }
  499. else if (rawAllocData->mMaxStackTrace > 1)
  500. {
  501. capturedTraceCount = BF_CAPTURE_STACK(1, (intptr*)stackTrace, min(rawAllocData->mMaxStackTrace, 1024));
  502. }
  503. #endif
  504. return BfRawAllocate(size, rawAllocData, stackTrace, capturedTraceCount);
  505. }
  506. void* Internal::Dbg_RawMarkedAlloc(intptr size, void* markFunc)
  507. {
  508. return BfRawAllocate(size, &sEmptyAllocData, NULL, 0);
  509. }
  510. void* Internal::Dbg_RawMarkedArrayAlloc(intptr elemCount, intptr elemStride, void* markFunc)
  511. {
  512. return BfRawAllocate(elemCount * elemStride, &sEmptyAllocData, NULL, 0);
  513. }
  514. void* Internal::Dbg_RawAlloc(intptr size)
  515. {
  516. return BfRawAllocate(size, &sEmptyAllocData, NULL, 0);
  517. }
  518. void* Internal::Dbg_RawObjectAlloc(intptr size)
  519. {
  520. return BfRawAllocate(size, &sObjectAllocData, NULL, 0);
  521. }
  522. void Internal::Dbg_RawFree(void* ptr)
  523. {
  524. BfRawFree(ptr);
  525. }