DbgInternal.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  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. auto prevFlags = object->mObjectFlags;
  175. if ((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0)
  176. object->mObjectFlags = (BfObjectFlags)((object->mObjectFlags & ~BfObjectFlag_StackAlloc) | BfObjectFlag_Deleted);
  177. #ifdef BF_GC_SUPPORTED
  178. if ((prevFlags & BfObjectFlag_Allocated) != 0)
  179. gBFGC.ObjectDeleteRequested(object);
  180. #endif
  181. }
  182. int GetStackTrace(void **result, int max_depth, int skip_count);
  183. void BfLog(const char* fmt ...);
  184. static const int cMaxStackTraceCount = 1024;
  185. struct PendingAllocState
  186. {
  187. bool mHasData;
  188. void* mStackTrace[cMaxStackTraceCount];
  189. int mStackTraceCount;
  190. int mMetadataBytes;
  191. bool mIsLargeAlloc;
  192. bool IsSmall(intptr curAllocBytes)
  193. {
  194. if ((mStackTraceCount > 255) || (mMetadataBytes > 255))
  195. return false;
  196. const intptr maxSmallObjectSize = ((intptr)1 << ((sizeof(intptr) - 2) * 8)) - 1;
  197. if (curAllocBytes <= maxSmallObjectSize)
  198. return true;
  199. intptr objBytes = curAllocBytes - mStackTraceCount * sizeof(intptr) - mMetadataBytes;
  200. return (objBytes < maxSmallObjectSize);
  201. }
  202. };
  203. static __thread PendingAllocState gPendingAllocState = { 0 };
  204. void Internal::Dbg_ReserveMetadataBytes(intptr metadataBytes, intptr& curAllocBytes)
  205. {
  206. bool isSmall = gPendingAllocState.IsSmall(curAllocBytes);
  207. gPendingAllocState.mMetadataBytes += (int)metadataBytes;
  208. curAllocBytes += metadataBytes;
  209. if ((isSmall) && (gPendingAllocState.mMetadataBytes > 255))
  210. {
  211. // We just went to 'small' to not small
  212. curAllocBytes += sizeof(intptr);
  213. }
  214. }
  215. void* Internal::Dbg_GetMetadata(bf::System::Object* obj)
  216. {
  217. return NULL;
  218. }
  219. intptr Internal::Dbg_PrepareStackTrace(intptr baseAllocSize, intptr maxStackTraceDepth)
  220. {
  221. intptr allocSize = 0;
  222. if (maxStackTraceDepth > 1)
  223. {
  224. int capturedTraceCount = BF_CAPTURE_STACK(1, (intptr*)gPendingAllocState.mStackTrace, min((int)maxStackTraceDepth, 1024));
  225. gPendingAllocState.mStackTraceCount = capturedTraceCount;
  226. const intptr maxSmallObjectSize = ((intptr)1 << ((sizeof(intptr) - 2) * 8)) - 1;
  227. if ((capturedTraceCount > 255) || (baseAllocSize >= maxSmallObjectSize))
  228. {
  229. gPendingAllocState.mIsLargeAlloc = true;
  230. allocSize += (1 + capturedTraceCount) * sizeof(intptr);
  231. }
  232. else
  233. {
  234. gPendingAllocState.mIsLargeAlloc = false;
  235. allocSize += capturedTraceCount * sizeof(intptr);
  236. }
  237. }
  238. return allocSize;
  239. }
  240. bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::Reflection::TypeInstance* typeInst, intptr size)
  241. {
  242. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  243. Object* result;
  244. intptr allocSize = size;
  245. uint8* allocBytes = (uint8*)BfObjectAllocate(allocSize, typeInst->_GetType());
  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. uint8* allocBytes = (uint8*)BfObjectAllocate(allocSize, classVData->mType);
  288. result = (bf::System::Object*)(allocBytes);
  289. }
  290. else
  291. {
  292. #if BF_USE_STOMP_ALLOC
  293. result = (bf::System::Object*)StompAlloc(allocSize);
  294. #elif BF_TRACK_SIZES
  295. sHighestId = BF_MAX(sHighestId, classVData->mType->mTypeId);
  296. uint8* allocPtr = (uint8*)malloc(size + 16);
  297. *((int*)allocPtr) = size;
  298. sAllocSizes[classVData->mType->mTypeId] += size;
  299. result = (bf::System::Object*)(allocPtr + 16);
  300. #else
  301. if ((BFRTFLAGS & BfRtFlags_DebugAlloc) != 0)
  302. {
  303. uint8* allocBytes = (uint8*)BfRawAllocate(allocSize, &sObjectAllocData, NULL, 0);
  304. result = (bf::System::Object*)allocBytes;
  305. }
  306. else
  307. {
  308. uint8* allocBytes = (uint8*)BFRTCALLBACKS.Alloc(allocSize);
  309. result = (bf::System::Object*)allocBytes;
  310. }
  311. #endif
  312. }
  313. #ifndef BFRT_NODBGFLAGS
  314. if ((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0)
  315. {
  316. // The order is very important here-
  317. // Once we set mDbgAllocInfo, the memory will be recognized by the GC as being a valid object.
  318. // There's a race condition with the alloc flags, however-- if the GC pauses our thread after we write
  319. // the mClassVData but before mDbgAllocInfo is set, then the object won't be marked with the new mark id
  320. // and it will appear as a leak during the Sweep. Thus, we leave the sAllocFlags off until AFTER
  321. // mDbgAllocInfo is set, so that the object will be ignored during sweeping unless we get all the way
  322. // through.
  323. intptr dbgAllocInfo;
  324. auto classVDataVal = (intptr)classVData | (intptr)BfObjectFlag_Allocated;
  325. if (maxStackTraceDepth <= 1)
  326. dbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  327. else
  328. {
  329. if (largeAllocInfo)
  330. {
  331. classVDataVal |= (intptr)BfObjectFlag_AllocInfo;
  332. dbgAllocInfo = size;
  333. *(intptr*)((uint8*)result + size) = capturedTraceCount;
  334. memcpy((uint8*)result + size + sizeof(intptr), stackTrace, capturedTraceCount * sizeof(intptr));
  335. }
  336. else
  337. {
  338. classVDataVal |= (intptr)BfObjectFlag_AllocInfo_Short;
  339. dbgAllocInfo = (size << 16) | capturedTraceCount;
  340. memcpy((uint8*)result + size, stackTrace, capturedTraceCount * sizeof(intptr));
  341. }
  342. }
  343. result->mClassVData = classVDataVal;
  344. 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
  345. result->mDbgAllocInfo = dbgAllocInfo;
  346. BF_FULL_MEMORY_FENCE();
  347. // 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
  348. BfpSystem_InterlockedCompareExchangePtr((uintptr*)&result->mClassVData, (uintptr)classVDataVal, classVDataVal | BFGC::sAllocFlags);
  349. //result->mClassVData = (result->mClassVData & ~BF_OBJECTFLAG_MARK_ID_MASK) | BFGC::sAllocFlags;
  350. }
  351. else
  352. #endif
  353. result->mClassVData = (intptr)classVData;
  354. //OutputDebugStrF("Object %@ ClassVData %@\n", result, classVData);
  355. #ifdef DBG_OBJECTEND
  356. *(uint32*)((uint8*)result + size) = 0xBFBFBFBF;
  357. #endif
  358. return result;
  359. }
  360. void Internal::Dbg_ObjectStackInit(bf::System::Object* result, bf::System::ClassVData* classVData)
  361. {
  362. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  363. result->mClassVData = (intptr)classVData | (intptr)BfObjectFlag_StackAlloc;
  364. #ifndef BFRT_NODBGFLAGS
  365. result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  366. #endif
  367. }
  368. static void SetupDbgAllocInfo(bf::System::Object* result, intptr origSize)
  369. {
  370. #ifndef BFRT_NODBGFLAGS
  371. if (gPendingAllocState.mStackTraceCount == 0)
  372. {
  373. result->mDbgAllocInfo = 1; // Must have a value
  374. return;
  375. }
  376. if (gPendingAllocState.mStackTraceCount == 1)
  377. {
  378. result->mDbgAllocInfo = (intptr)gPendingAllocState.mStackTrace[0];
  379. return;
  380. }
  381. if (gPendingAllocState.mIsLargeAlloc)
  382. {
  383. result->mClassVData |= (intptr)BfObjectFlag_AllocInfo;
  384. result->mDbgAllocInfo = origSize;
  385. *(intptr*)((uint8*)result + origSize) = gPendingAllocState.mStackTraceCount;
  386. memcpy((uint8*)result + origSize + sizeof(intptr), gPendingAllocState.mStackTrace, gPendingAllocState.mStackTraceCount * sizeof(intptr));
  387. }
  388. else
  389. {
  390. result->mClassVData |= (intptr)BfObjectFlag_AllocInfo_Short;
  391. result->mDbgAllocInfo = (origSize << 16) | gPendingAllocState.mStackTraceCount;
  392. memcpy((uint8*)result + origSize, gPendingAllocState.mStackTrace, gPendingAllocState.mStackTraceCount * sizeof(intptr));
  393. }
  394. #endif
  395. }
  396. void Internal::Dbg_ObjectCreated(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData)
  397. {
  398. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  399. #ifndef BFRT_NODBGFLAGS
  400. BF_ASSERT_REL((result->mClassVData & ~(BfObjectFlag_Allocated | BfObjectFlag_Mark3)) == (intptr)classVData);
  401. result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  402. #endif
  403. }
  404. void Internal::Dbg_ObjectCreatedEx(bf::System::Object* result, intptr origSize, bf::System::ClassVData* classVData)
  405. {
  406. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  407. #ifndef BFRT_NODBGFLAGS
  408. BF_ASSERT_REL((result->mClassVData & ~(BfObjectFlag_Allocated | BfObjectFlag_Mark3)) == (intptr)classVData);
  409. SetupDbgAllocInfo(result, origSize);
  410. #endif
  411. }
  412. void Internal::Dbg_ObjectAllocated(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData)
  413. {
  414. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  415. result->mClassVData = (intptr)classVData;
  416. #ifndef BFRT_NODBGFLAGS
  417. result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
  418. #endif
  419. }
  420. void Internal::Dbg_ObjectAllocatedEx(bf::System::Object* result, intptr origSize, bf::System::ClassVData* classVData)
  421. {
  422. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  423. result->mClassVData = (intptr)classVData;
  424. SetupDbgAllocInfo(result, origSize);
  425. }
  426. void Internal::Dbg_ObjectPreDelete(bf::System::Object* object)
  427. {
  428. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  429. #ifndef BFRT_NODBGFLAGS
  430. const char* errorPtr = NULL;
  431. if ((object->mObjectFlags & BfObjectFlag_StackAlloc) != 0)
  432. {
  433. if ((object->mObjectFlags & BfObjectFlag_Allocated) == 0)
  434. errorPtr = "Attempting to delete stack-allocated object";
  435. else
  436. errorPtr = "Deleting an object that was detected as leaked (internal error)";
  437. }
  438. else if ((object->mObjectFlags & BfObjectFlag_AppendAlloc) != 0)
  439. errorPtr = "Attempting to delete append-allocated object, use 'delete append' statement instead of 'delete'";
  440. else if ((object->mObjectFlags & BfObjectFlag_Allocated) == 0)
  441. {
  442. errorPtr = "Attempting to delete custom-allocated object without specifying allocator";
  443. #if _WIN32
  444. MEMORY_BASIC_INFORMATION stackInfo = { 0 };
  445. VirtualQuery(object, &stackInfo, sizeof(MEMORY_BASIC_INFORMATION));
  446. if ((stackInfo.Protect & PAGE_READONLY) != 0)
  447. errorPtr = "Attempting to delete read-only object";
  448. #endif
  449. }
  450. else if ((object->mObjectFlags & BfObjectFlag_Deleted) != 0)
  451. errorPtr = "Attempting second delete on object";
  452. if (errorPtr != NULL)
  453. {
  454. Beefy::String errorStr = errorPtr;
  455. Beefy::String typeName = object->GetTypeName();
  456. errorStr += "\x1";
  457. errorStr += StrFormat("LEAK\t0x%@\n", object);
  458. errorStr += StrFormat(" (%s)0x%@\n", typeName.c_str(), object);
  459. SETUP_ERROR(errorStr.c_str(), 2);
  460. BF_DEBUG_BREAK();
  461. BFRTCALLBACKS.DebugMessageData_Fatal();
  462. return;
  463. }
  464. #endif
  465. }
  466. void Internal::Dbg_ObjectPreCustomDelete(bf::System::Object* object)
  467. {
  468. BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
  469. if ((object->mObjectFlags & BfObjectFlag_AppendAlloc) != 0)
  470. return;
  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. }
  487. }
  488. void* Internal::Dbg_RawAlloc(intptr size, DbgRawAllocData* rawAllocData)
  489. {
  490. void* stackTrace[1024];
  491. int capturedTraceCount = 0;
  492. #ifndef BFRT_NODBGFLAGS
  493. if (rawAllocData->mMaxStackTrace == 1)
  494. {
  495. stackTrace[0] = BF_RETURN_ADDRESS;
  496. capturedTraceCount = 1;
  497. }
  498. else if (rawAllocData->mMaxStackTrace > 1)
  499. {
  500. capturedTraceCount = BF_CAPTURE_STACK(1, (intptr*)stackTrace, min(rawAllocData->mMaxStackTrace, 1024));
  501. }
  502. #endif
  503. return BfRawAllocate(size, rawAllocData, stackTrace, capturedTraceCount);
  504. }
  505. void* Internal::Dbg_RawMarkedAlloc(intptr size, void* markFunc)
  506. {
  507. return BfRawAllocate(size, &sEmptyAllocData, NULL, 0);
  508. }
  509. void* Internal::Dbg_RawMarkedArrayAlloc(intptr elemCount, intptr elemStride, void* markFunc)
  510. {
  511. return BfRawAllocate(elemCount * elemStride, &sEmptyAllocData, NULL, 0);
  512. }
  513. void* Internal::Dbg_RawAlloc(intptr size)
  514. {
  515. return BfRawAllocate(size, &sEmptyAllocData, NULL, 0);
  516. }
  517. void* Internal::Dbg_RawObjectAlloc(intptr size)
  518. {
  519. return BfRawAllocate(size, &sObjectAllocData, NULL, 0);
  520. }
  521. void Internal::Dbg_RawFree(void* ptr)
  522. {
  523. BfRawFree(ptr);
  524. }