DbgInternal.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  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. intptr 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. intptr 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. (void)classVData;
  249. #ifndef BFRT_NODBGFLAGS
  250. intptr dbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  251. result->mClassVData = (intptr)classVData | (intptr)BfObjectFlag_Allocated /*| BFGC::sAllocFlags*/;
  252. 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
  253. result->mDbgAllocInfo = dbgAllocInfo;
  254. BF_FULL_MEMORY_FENCE();
  255. result->mClassVData = (result->mClassVData & ~BF_OBJECTFLAG_MARK_ID_MASK) | BFGC::sAllocFlags;
  256. #endif
  257. return result;
  258. }
  259. //#define DBG_OBJECTEND
  260. bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData, intptr size, intptr align, intptr maxStackTraceDepth)
  261. {
  262. void* stackTrace[1024];
  263. int capturedTraceCount = 0;
  264. intptr allocSize = size;
  265. bool largeAllocInfo = false;
  266. if ((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0)
  267. {
  268. if (maxStackTraceDepth > 1)
  269. {
  270. capturedTraceCount = BF_CAPTURE_STACK(1, (intptr*)stackTrace, min((int)maxStackTraceDepth, 1024));
  271. const intptr maxSmallObjectSize = ((intptr)1 << ((sizeof(intptr) - 2) * 8)) - 1;
  272. if ((capturedTraceCount > 255) || (size >= maxSmallObjectSize))
  273. {
  274. largeAllocInfo = true;
  275. allocSize += (1 + capturedTraceCount) * sizeof(intptr);
  276. }
  277. else
  278. allocSize += capturedTraceCount * sizeof(intptr);
  279. }
  280. }
  281. #ifdef DBG_OBJECTEND
  282. allocSize += 4;
  283. #endif
  284. bf::System::Object* result;
  285. if ((BFRTFLAGS & BfRtFlags_LeakCheck) != 0)
  286. {
  287. allocSize = BF_ALIGN(allocSize, align);
  288. uint8* allocBytes = (uint8*)BfObjectAllocate(allocSize, classVData->mType);
  289. // int dataOffset = (int)(sizeof(intptr) * 2);
  290. // memset(allocBytes + dataOffset, 0, size - dataOffset);
  291. result = (bf::System::Object*)(allocBytes);
  292. }
  293. else
  294. {
  295. #if BF_USE_STOMP_ALLOC
  296. result = (bf::System::Object*)StompAlloc(allocSize);
  297. #elif BF_TRACK_SIZES
  298. sHighestId = BF_MAX(sHighestId, classVData->mType->mTypeId);
  299. uint8* allocPtr = (uint8*)malloc(size + 16);
  300. *((int*)allocPtr) = size;
  301. sAllocSizes[classVData->mType->mTypeId] += size;
  302. result = (bf::System::Object*)(allocPtr + 16);
  303. #else
  304. if ((BFRTFLAGS & BfRtFlags_DebugAlloc) != 0)
  305. {
  306. uint8* allocBytes = (uint8*)BfRawAllocate(allocSize, &sObjectAllocData, NULL, 0);
  307. result = (bf::System::Object*)allocBytes;
  308. }
  309. else
  310. {
  311. uint8* allocBytes = (uint8*)BFRTCALLBACKS.Alloc(allocSize);
  312. result = (bf::System::Object*)allocBytes;
  313. }
  314. #endif
  315. }
  316. #ifndef BFRT_NODBGFLAGS
  317. if ((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0)
  318. {
  319. // The order is very important here-
  320. // Once we set mDbgAllocInfo, the memory will be recognized by the GC as being a valid object.
  321. // There's a race condition with the alloc flags, however-- if the GC pauses our thread after we write
  322. // the mClassVData but before mDbgAllocInfo is set, then the object won't be marked with the new mark id
  323. // and it will appear as a leak during the Sweep. Thus, we leave the sAllocFlags off until AFTER
  324. // mDbgAllocInfo is set, so that the object will be ignored during sweeping unless we get all the way
  325. // through.
  326. intptr dbgAllocInfo;
  327. auto classVDataVal = (intptr)classVData | (intptr)BfObjectFlag_Allocated;
  328. if (maxStackTraceDepth <= 1)
  329. dbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  330. else
  331. {
  332. if (largeAllocInfo)
  333. {
  334. classVDataVal |= (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. classVDataVal |= (intptr)BfObjectFlag_AllocInfo_Short;
  342. dbgAllocInfo = (size << 16) | capturedTraceCount;
  343. memcpy((uint8*)result + size, stackTrace, capturedTraceCount * sizeof(intptr));
  344. }
  345. }
  346. result->mClassVData = classVDataVal;
  347. 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
  348. result->mDbgAllocInfo = dbgAllocInfo;
  349. BF_FULL_MEMORY_FENCE();
  350. // 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
  351. BfpSystem_InterlockedCompareExchangePtr((uintptr*)&result->mClassVData, (uintptr)classVDataVal, classVDataVal | BFGC::sAllocFlags);
  352. //result->mClassVData = (result->mClassVData & ~BF_OBJECTFLAG_MARK_ID_MASK) | BFGC::sAllocFlags;
  353. }
  354. else
  355. #endif
  356. result->mClassVData = (intptr)classVData;
  357. //OutputDebugStrF("Object %@ ClassVData %@\n", result, classVData);
  358. #ifdef DBG_OBJECTEND
  359. *(uint32*)((uint8*)result + size) = 0xBFBFBFBF;
  360. #endif
  361. return result;
  362. }
  363. void Internal::Dbg_ObjectStackInit(bf::System::Object* result, bf::System::ClassVData* classVData)
  364. {
  365. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  366. result->mClassVData = (intptr)classVData | (intptr)BfObjectFlag_StackAlloc;
  367. #ifndef BFRT_NODBGFLAGS
  368. result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  369. #endif
  370. }
  371. static void SetupDbgAllocInfo(bf::System::Object* result, intptr origSize)
  372. {
  373. #ifndef BFRT_NODBGFLAGS
  374. if (gPendingAllocState.mStackTraceCount == 0)
  375. {
  376. result->mDbgAllocInfo = 1; // Must have a value
  377. return;
  378. }
  379. if (gPendingAllocState.mStackTraceCount == 1)
  380. {
  381. result->mDbgAllocInfo = (intptr)gPendingAllocState.mStackTrace[0];
  382. return;
  383. }
  384. if (gPendingAllocState.mIsLargeAlloc)
  385. {
  386. result->mClassVData |= (intptr)BfObjectFlag_AllocInfo;
  387. result->mDbgAllocInfo = origSize;
  388. *(intptr*)((uint8*)result + origSize) = gPendingAllocState.mStackTraceCount;
  389. memcpy((uint8*)result + origSize + sizeof(intptr), gPendingAllocState.mStackTrace, gPendingAllocState.mStackTraceCount * sizeof(intptr));
  390. }
  391. else
  392. {
  393. result->mClassVData |= (intptr)BfObjectFlag_AllocInfo_Short;
  394. result->mDbgAllocInfo = (origSize << 16) | gPendingAllocState.mStackTraceCount;
  395. memcpy((uint8*)result + origSize, gPendingAllocState.mStackTrace, gPendingAllocState.mStackTraceCount * sizeof(intptr));
  396. }
  397. #endif
  398. }
  399. void Internal::Dbg_ObjectCreated(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData)
  400. {
  401. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  402. #ifndef BFRT_NODBGFLAGS
  403. BF_ASSERT_REL((result->mClassVData & ~(BfObjectFlag_Allocated | BfObjectFlag_Mark3)) == (intptr)classVData);
  404. result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  405. #endif
  406. }
  407. void Internal::Dbg_ObjectCreatedEx(bf::System::Object* result, intptr origSize, bf::System::ClassVData* classVData)
  408. {
  409. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  410. #ifndef BFRT_NODBGFLAGS
  411. BF_ASSERT_REL((result->mClassVData & ~(BfObjectFlag_Allocated | BfObjectFlag_Mark3)) == (intptr)classVData);
  412. SetupDbgAllocInfo(result, origSize);
  413. #endif
  414. }
  415. void Internal::Dbg_ObjectAllocated(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData)
  416. {
  417. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  418. result->mClassVData = (intptr)classVData;
  419. #ifndef BFRT_NODBGFLAGS
  420. result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  421. #endif
  422. }
  423. void Internal::Dbg_ObjectAllocatedEx(bf::System::Object* result, intptr origSize, bf::System::ClassVData* classVData)
  424. {
  425. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  426. result->mClassVData = (intptr)classVData;
  427. SetupDbgAllocInfo(result, origSize);
  428. }
  429. void Internal::Dbg_ObjectPreDelete(bf::System::Object* object)
  430. {
  431. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  432. #ifndef BFRT_NODBGFLAGS
  433. const char* errorPtr = NULL;
  434. if ((object->mObjectFlags & BfObjectFlag_StackAlloc) != 0)
  435. {
  436. if ((object->mObjectFlags & BfObjectFlag_Allocated) == 0)
  437. errorPtr = "Attempting to delete stack-allocated object";
  438. else
  439. errorPtr = "Deleting an object that was detected as leaked (internal error)";
  440. }
  441. else if ((object->mObjectFlags & BfObjectFlag_AppendAlloc) != 0)
  442. errorPtr = "Attempting to delete append-allocated object, use 'delete append' statement instead of 'delete'";
  443. else if ((object->mObjectFlags & BfObjectFlag_Allocated) == 0)
  444. {
  445. errorPtr = "Attempting to delete custom-allocated object without specifying allocator";
  446. #if _WIN32
  447. MEMORY_BASIC_INFORMATION stackInfo = { 0 };
  448. VirtualQuery(object, &stackInfo, sizeof(MEMORY_BASIC_INFORMATION));
  449. if ((stackInfo.Protect & PAGE_READONLY) != 0)
  450. errorPtr = "Attempting to delete read-only object";
  451. #endif
  452. }
  453. else if ((object->mObjectFlags & BfObjectFlag_Deleted) != 0)
  454. errorPtr = "Attempting second delete on object";
  455. if (errorPtr != NULL)
  456. {
  457. Beefy::String errorStr = errorPtr;
  458. Beefy::String typeName = object->GetTypeName();
  459. errorStr += "\x1";
  460. errorStr += StrFormat("LEAK\t0x%@\n", object);
  461. errorStr += StrFormat(" (%s)0x%@\n", typeName.c_str(), object);
  462. SETUP_ERROR(errorStr.c_str(), 2);
  463. BF_DEBUG_BREAK();
  464. BFRTCALLBACKS.DebugMessageData_Fatal();
  465. return;
  466. }
  467. #endif
  468. }
  469. void Internal::Dbg_ObjectPreCustomDelete(bf::System::Object* object)
  470. {
  471. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  472. if ((object->mObjectFlags & BfObjectFlag_AppendAlloc) != 0)
  473. return;
  474. const char* errorPtr = NULL;
  475. if ((object->mObjectFlags & BfObjectFlag_StackAlloc) != 0)
  476. errorPtr = "Attempting to delete stack-allocated object";
  477. if ((object->mObjectFlags & BfObjectFlag_Deleted) != 0)
  478. errorPtr = "Attempting second delete on object";
  479. if (errorPtr != NULL)
  480. {
  481. Beefy::String errorStr = errorPtr;
  482. Beefy::String typeName = object->GetTypeName();
  483. errorStr += "\x1";
  484. errorStr += StrFormat("LEAK\t0x%@\n", object);
  485. errorStr += StrFormat(" (%s)0x%@\n", typeName.c_str(), object);
  486. SETUP_ERROR(errorStr.c_str(), 2);
  487. BF_DEBUG_BREAK();
  488. BFRTCALLBACKS.DebugMessageData_Fatal();
  489. }
  490. }
  491. void* Internal::Dbg_RawAlloc(intptr size, DbgRawAllocData* rawAllocData)
  492. {
  493. void* stackTrace[1024];
  494. int capturedTraceCount = 0;
  495. #ifndef BFRT_NODBGFLAGS
  496. if (rawAllocData->mMaxStackTrace == 1)
  497. {
  498. stackTrace[0] = BF_RETURN_ADDRESS;
  499. capturedTraceCount = 1;
  500. }
  501. else if (rawAllocData->mMaxStackTrace > 1)
  502. {
  503. capturedTraceCount = BF_CAPTURE_STACK(1, (intptr*)stackTrace, min(rawAllocData->mMaxStackTrace, 1024));
  504. }
  505. #endif
  506. return BfRawAllocate(size, rawAllocData, stackTrace, capturedTraceCount);
  507. }
  508. void* Internal::Dbg_RawMarkedAlloc(intptr size, void* markFunc)
  509. {
  510. return BfRawAllocate(size, &sEmptyAllocData, NULL, 0);
  511. }
  512. void* Internal::Dbg_RawMarkedArrayAlloc(intptr elemCount, intptr elemStride, void* markFunc)
  513. {
  514. return BfRawAllocate(elemCount * elemStride, &sEmptyAllocData, NULL, 0);
  515. }
  516. void* Internal::Dbg_RawAlloc(intptr size)
  517. {
  518. return BfRawAllocate(size, &sEmptyAllocData, NULL, 0);
  519. }
  520. void* Internal::Dbg_RawObjectAlloc(intptr size)
  521. {
  522. return BfRawAllocate(size, &sObjectAllocData, NULL, 0);
  523. }
  524. void Internal::Dbg_RawFree(void* ptr)
  525. {
  526. BfRawFree(ptr);
  527. }