DbgInternal.cpp 20 KB

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