OVR_Allocator.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  1. /************************************************************************************
  2. Filename : OVR_Allocator.cpp
  3. Content : Installable memory allocator implementation
  4. Created : September 19, 2012
  5. Notes :
  6. Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
  7. Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
  8. you may not use the Oculus VR Rift SDK except in compliance with the License,
  9. which is provided at the time of installation or download, or which
  10. otherwise accompanies this software in either electronic or hard copy form.
  11. You may obtain a copy of the License at
  12. http://www.oculusvr.com/licenses/LICENSE-3.2
  13. Unless required by applicable law or agreed to in writing, the Oculus VR SDK
  14. distributed under the License is distributed on an "AS IS" BASIS,
  15. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. See the License for the specific language governing permissions and
  17. limitations under the License.
  18. ************************************************************************************/
  19. #include "OVR_Allocator.h"
  20. #include "OVR_DebugHelp.h"
  21. #include "Kernel/OVR_Std.h"
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <exception>
  25. #ifdef OVR_OS_MAC
  26. #include <stdlib.h>
  27. #include <malloc/malloc.h>
  28. #else
  29. #include <malloc.h>
  30. #endif
  31. #if defined(OVR_OS_MS)
  32. #include "OVR_Win32_IncludeWindows.h"
  33. #else
  34. #include <unistd.h>
  35. #include <sys/mman.h>
  36. #include <execinfo.h>
  37. #endif
  38. // This will cause an assertion to trip whenever an allocation occurs outside of our
  39. // custom allocator. This helps track down allocations that are not being done
  40. // correctly via OVR_ALLOC().
  41. // #define OVR_HUNT_UNTRACKED_ALLOCS
  42. namespace OVR {
  43. bad_alloc::bad_alloc(const char* description) OVR_NOEXCEPT
  44. {
  45. if(description)
  46. OVR_strlcpy(Description, description, sizeof(Description));
  47. else
  48. Description[0] = '\0';
  49. OVR_strlcat(Description, " at ", sizeof(Description));
  50. // read the current backtrace
  51. // We cannot attempt to symbolize this here as that would attempt to
  52. // allocate memory. That would be unwise within a bad_alloc exception.
  53. void* backtrace_data[20];
  54. char addressDescription[256] = {}; // Write into this temporary instead of member Description in case an exception is thrown.
  55. #if defined(OVR_OS_MS)
  56. int count = CaptureStackBackTrace(2, sizeof(backtrace_data)/sizeof(backtrace_data[0]), backtrace_data, nullptr);
  57. #else
  58. int count = backtrace(backtrace_data, sizeof(backtrace_data)/sizeof(backtrace_data[0]));
  59. #endif
  60. for(int i = 0; i < count; ++i)
  61. {
  62. char address[(sizeof(void*) * 2) + 1 + 1]; // hex address string plus possible space plus null terminator.
  63. OVR_snprintf(address, sizeof(address), "%x%s", backtrace_data[i], (i + 1 < count) ? " " : "");
  64. OVR_strlcat(addressDescription, address, sizeof(addressDescription));
  65. }
  66. OVR_strlcat(Description, addressDescription, sizeof(Description));
  67. }
  68. //-----------------------------------------------------------------------------------
  69. // ***** Allocator
  70. Allocator* Allocator::GetInstance()
  71. {
  72. static Allocator* pAllocator = nullptr;
  73. if(!pAllocator)
  74. {
  75. static DefaultAllocator defaultAllocator;
  76. pAllocator = &defaultAllocator;
  77. // This is restricted to X64 builds due address space exhaustion in 32-bit builds
  78. #if defined(OVR_BUILD_DEBUG) && defined(OVR_CPU_X86_64)
  79. static DebugPageAllocator debugAllocator;
  80. #if defined(OVR_CC_MSVC)
  81. // Make _CrtIsValidHeapPointer always return true. The VC++ concurrency library has a bug in that
  82. // it's calling _CrtIsValidHeapPointer, which is invalid and recommended against by Microsoft themselves.
  83. // We need to deal with this nevertheless. The problem is that the VC++ concurrency library is
  84. // calling _CrtIsValidHeapPointer on the default heap instead of the current heap (DebugPageAllocator).
  85. // So we modify the _CrtIsValidHeapPointer implementation to always return true. The primary risk
  86. // with this change is that there's some code somewhere that uses it for a non-diagnostic purpose.
  87. // However this os Oculus-debug-internal and so has no effect on any formally published software.
  88. if(OVR::KillCdeclFunction(_CrtIsValidHeapPointer, true)) // If we can successfully kill _CrtIsValidHeapPointer, use our debug allocator.
  89. pAllocator = &debugAllocator;
  90. #endif // OVR_CC_MSVC
  91. #endif
  92. }
  93. return pAllocator;
  94. }
  95. // Default AlignedAlloc implementation will delegate to Alloc/Free after doing rounding.
  96. void* Allocator::AllocAligned(size_t size, size_t align)
  97. {
  98. OVR_ASSERT((align & (align-1)) == 0);
  99. align = (align > sizeof(size_t)) ? align : sizeof(size_t);
  100. size_t p = (size_t)Alloc(size+align);
  101. size_t aligned = 0;
  102. if (p)
  103. {
  104. aligned = (size_t(p) + align-1) & ~(align-1);
  105. if (aligned == p)
  106. aligned += align;
  107. *(((size_t*)aligned)-1) = aligned-p;
  108. }
  109. return (void*)(aligned);
  110. }
  111. void Allocator::FreeAligned(void* p)
  112. {
  113. if (!p)
  114. return;
  115. size_t src = size_t(p) - *(((size_t*)p) - 1);
  116. Free((void*)src);
  117. }
  118. //------------------------------------------------------------------------
  119. // ***** Default Allocator
  120. // This allocator is created and used if no other allocator is installed.
  121. // Default allocator delegates to system malloc.
  122. void* DefaultAllocator::Alloc(size_t size)
  123. {
  124. void* p = malloc(size);
  125. trackAlloc(p, size);
  126. return p;
  127. }
  128. void* DefaultAllocator::AllocDebug(size_t size, const char* file, unsigned line)
  129. {
  130. void* p;
  131. #if defined(OVR_CC_MSVC) && defined(_CRTDBG_MAP_ALLOC)
  132. p = _malloc_dbg(size, _NORMAL_BLOCK, file, line);
  133. #else
  134. OVR_UNUSED2(file, line); // should be here for debugopt config
  135. p = malloc(size);
  136. #endif
  137. trackAlloc(p, size);
  138. return p;
  139. }
  140. void* DefaultAllocator::Realloc(void* p, size_t newSize)
  141. {
  142. void* newP = realloc(p, newSize);
  143. // This used to more efficiently check if (newp != p) but static analyzers were erroneously flagging this.
  144. if (newP) // Need to check newP because realloc doesn't free p unless it returns a valid newP.
  145. {
  146. #if !defined(__clang_analyzer__) // The analyzer complains that we are using p after it was freed.
  147. untrackAlloc(p);
  148. #endif
  149. }
  150. trackAlloc(newP, newSize);
  151. return newP;
  152. }
  153. void DefaultAllocator::Free(void *p)
  154. {
  155. untrackAlloc(p);
  156. return free(p);
  157. }
  158. // System block allocator:
  159. void* SafeMMapAlloc(size_t size)
  160. {
  161. #if defined(OVR_OS_MS)
  162. return VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); // size is rounded up to a page. // Returned memory is 0-filled.
  163. #elif defined(OVR_OS_MAC) || defined(OVR_OS_UNIX)
  164. #if !defined(MAP_FAILED)
  165. #define MAP_FAILED ((void*)-1)
  166. #endif
  167. void* result = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); // Returned memory is 0-filled.
  168. if(result == MAP_FAILED) // mmap returns MAP_FAILED (-1) upon failure.
  169. result = nullptr;
  170. return result;
  171. #endif
  172. }
  173. void SafeMMapFree(const void* memory, size_t size)
  174. {
  175. #if defined(OVR_OS_MS)
  176. OVR_UNUSED(size);
  177. VirtualFree(const_cast<void*>(memory), 0, MEM_RELEASE);
  178. #elif defined(OVR_OS_MAC) || defined(OVR_OS_UNIX)
  179. size_t pageSize = getpagesize();
  180. size = (((size + (pageSize - 1)) / pageSize) * pageSize);
  181. munmap(const_cast<void*>(memory), size); // Must supply the size to munmap.
  182. #endif
  183. }
  184. //------------------------------------------------------------------------
  185. // ***** SetLeakTracking
  186. static bool IsLeakTracking = false;
  187. void Allocator::SetLeakTracking(bool enabled)
  188. {
  189. #if defined(OVR_OS_WIN32) && !defined(OVR_OS_WIN64)
  190. // HACK: Currently 32-bit leak tracing is too slow to run in real-time on Windows.
  191. // Note: We can possibly fix this by making a custom Win32 backtrace function which
  192. // takes advantage of the fact that we have enabled stack frames in all builds.
  193. enabled = false;
  194. #endif
  195. if (enabled)
  196. {
  197. SymbolLookup::Initialize();
  198. }
  199. IsLeakTracking = enabled;
  200. }
  201. bool Allocator::IsTrackingLeaks()
  202. {
  203. return IsLeakTracking;
  204. }
  205. //------------------------------------------------------------------------
  206. // ***** Track Allocations
  207. struct TrackedAlloc
  208. {
  209. TrackedAlloc* pNext;
  210. TrackedAlloc* pPrev;
  211. void* pAlloc;
  212. void* Callstack[64];
  213. uint32_t FrameCount;
  214. uint32_t Size;
  215. };
  216. static uint32_t PointerHash(const void* p)
  217. {
  218. uintptr_t key = (uintptr_t)p;
  219. #ifdef OVR_64BIT_POINTERS
  220. key = (~key) + (key << 18);
  221. key = key ^ (key >> 31);
  222. key = key * 21;
  223. key = key ^ (key >> 11);
  224. key = key + (key << 6);
  225. key = key ^ (key >> 22);
  226. #else
  227. key = (key ^ 61) ^ (key >> 16);
  228. key = key + (key << 3);
  229. key = key ^ (key >> 4);
  230. key = key * 0x27d4eb2d;
  231. key = key ^ (key >> 15);
  232. #endif
  233. return (uint32_t)key;
  234. }
  235. #define OVR_HASH_BITS 10
  236. #define OVR_HASH_SIZE (1 << OVR_HASH_BITS)
  237. #define OVR_HASH_MASK (OVR_HASH_SIZE - 1)
  238. static TrackedAlloc* AllocHashMap[OVR_HASH_SIZE] = {nullptr};
  239. static SymbolLookup Symbols;
  240. void Allocator::trackAlloc(void* p, size_t size)
  241. {
  242. if (!p || !IsLeakTracking)
  243. return;
  244. TrackedAlloc* tracked = (TrackedAlloc*)malloc(sizeof(TrackedAlloc));
  245. tracked->pAlloc = p;
  246. tracked->FrameCount = (uint32_t)Symbols.GetBacktrace(tracked->Callstack, OVR_ARRAY_COUNT(tracked->Callstack), 2);
  247. tracked->Size = (uint32_t)size;
  248. uint32_t key = PointerHash(p) & OVR_HASH_MASK;
  249. // Hold track lock and verify leak tracking is still going on
  250. Lock::Locker locker(&TrackLock);
  251. if (!IsLeakTracking)
  252. {
  253. free(tracked);
  254. return;
  255. }
  256. // Insert into the hash map
  257. TrackedAlloc* head = AllocHashMap[key];
  258. tracked->pPrev = nullptr;
  259. tracked->pNext = head;
  260. if (head)
  261. {
  262. head->pPrev = tracked;
  263. }
  264. AllocHashMap[key] = tracked;
  265. }
  266. void Allocator::untrackAlloc(void* p)
  267. {
  268. if (!p || !IsLeakTracking)
  269. return;
  270. uint32_t key = PointerHash(p) & OVR_HASH_MASK;
  271. Lock::Locker locker(&TrackLock);
  272. TrackedAlloc* head = AllocHashMap[key];
  273. for (TrackedAlloc* t = head; t; t = t->pNext)
  274. {
  275. if (t->pAlloc == p)
  276. {
  277. if (t->pPrev)
  278. {
  279. t->pPrev->pNext = t->pNext;
  280. }
  281. if (t->pNext)
  282. {
  283. t->pNext->pPrev = t->pPrev;
  284. }
  285. if (head == t)
  286. {
  287. AllocHashMap[key] = t->pNext;
  288. }
  289. free(t);
  290. break;
  291. }
  292. }
  293. }
  294. int Allocator::DumpMemory()
  295. {
  296. const bool symbolLookupWasInitialized = SymbolLookup::IsInitialized();
  297. const bool symbolLookupAvailable = SymbolLookup::Initialize();
  298. if(!symbolLookupWasInitialized) // If SymbolLookup::Initialize was the first time being initialized, we need to refresh the Symbols view of modules, etc.
  299. Symbols.Refresh();
  300. // If we're dumping while LibOVR is running, then we should hold the lock.
  301. Allocator* pAlloc = Allocator::GetInstance();
  302. // It's possible this is being called after the Allocator was shut down, at which
  303. // point we assume we are the only instance that can be executing at his time.
  304. Lock* lock = pAlloc ? &pAlloc->TrackLock : nullptr;
  305. if (lock)
  306. lock->DoLock();
  307. int leakCount = 0;
  308. const size_t leakReportBufferSize = 8192;
  309. char* leakReportBuffer = nullptr;
  310. for (int i = 0; i < OVR_HASH_SIZE; ++i)
  311. {
  312. for (TrackedAlloc* t = AllocHashMap[i]; t; t = t->pNext)
  313. {
  314. if (!leakReportBuffer) // Lazy allocate this, as it wouldn't be needed unless we had a leak, which we aim to be an unusual case.
  315. {
  316. leakReportBuffer = static_cast<char*>(SafeMMapAlloc(leakReportBufferSize));
  317. if (!leakReportBuffer)
  318. break;
  319. }
  320. char line[2048];
  321. OVR_snprintf(line, OVR_ARRAY_COUNT(line), "[Leak] ** Detected leaked allocation at %p (size = %u) (%d frames)\n", t->pAlloc, (unsigned)t->Size, (unsigned)t->FrameCount);
  322. OVR_strlcat(leakReportBuffer, line, leakReportBufferSize);
  323. for (size_t j = 0; j < t->FrameCount; ++j)
  324. {
  325. SymbolInfo symbolInfo;
  326. if (symbolLookupAvailable && Symbols.LookupSymbol((uint64_t)t->Callstack[j], symbolInfo) && (symbolInfo.filePath[0] || symbolInfo.function[0]))
  327. {
  328. if (symbolInfo.filePath[0])
  329. OVR_snprintf(line, OVR_ARRAY_COUNT(line), "%s(%d): %s\n", symbolInfo.filePath, symbolInfo.fileLineNumber, symbolInfo.function[0] ? symbolInfo.function : "(unknown function)");
  330. else
  331. OVR_snprintf(line, OVR_ARRAY_COUNT(line), "%p (unknown source file): %s\n", t->Callstack[j], symbolInfo.function);
  332. }
  333. else
  334. {
  335. OVR_snprintf(line, OVR_ARRAY_COUNT(line), "%p (symbols unavailable)\n", t->Callstack[j]);
  336. }
  337. OVR_strlcat(leakReportBuffer, line, leakReportBufferSize);
  338. }
  339. // There are some leaks that aren't real because they are allocated by the Standard Library at runtime but
  340. // aren't freed until shutdown. We don't want to report those, and so we filter them out here.
  341. const char* ignoredPhrases[] = { "Concurrency::details" /*add any additional strings here*/ };
  342. for(size_t j = 0; j < OVR_ARRAY_COUNT(ignoredPhrases); ++j)
  343. {
  344. if (strstr(leakReportBuffer, ignoredPhrases[j])) // If we should ignore this leak...
  345. {
  346. leakReportBuffer[0] = '\0';
  347. }
  348. }
  349. if (leakReportBuffer[0]) // If we are to report this as a bonafide leak...
  350. {
  351. ++leakCount;
  352. LogText("%s\n", leakReportBuffer);
  353. }
  354. }
  355. }
  356. if (leakReportBuffer)
  357. {
  358. SafeMMapFree(leakReportBuffer, leakReportBufferSize);
  359. leakReportBuffer = nullptr;
  360. }
  361. if (lock)
  362. lock->Unlock();
  363. if(symbolLookupAvailable)
  364. SymbolLookup::Shutdown();
  365. return leakCount;
  366. }
  367. //------------------------------------------------------------------------
  368. // ***** DebugPageAllocator
  369. static size_t AlignSizeUp(size_t value, size_t alignment)
  370. {
  371. return ((value + (alignment - 1)) & ~(alignment - 1));
  372. }
  373. static size_t AlignSizeDown(size_t value, size_t alignment)
  374. {
  375. return (value & ~(alignment - 1));
  376. }
  377. template <typename Pointer>
  378. Pointer AlignPointerUp(Pointer p, size_t alignment)
  379. {
  380. return reinterpret_cast<Pointer>(((reinterpret_cast<size_t>(p) + (alignment - 1)) & ~(alignment - 1)));
  381. }
  382. template <typename Pointer>
  383. Pointer AlignPointerDown(Pointer p, size_t alignment)
  384. {
  385. return reinterpret_cast<Pointer>(reinterpret_cast<size_t>(p) & ~(alignment-1));
  386. }
  387. const size_t kFreedBlockArrayMaxSizeDefault = 16384;
  388. #if defined(OVR_HUNT_UNTRACKED_ALLOCS)
  389. static const char* WhiteList[] = {
  390. "OVR_Allocator.cpp",
  391. "OVR_Log.cpp",
  392. "crtw32", // Ignore CRT internal allocations
  393. nullptr
  394. };
  395. static int YourAllocHook(int, void *,
  396. size_t, int, long,
  397. const unsigned char *szFileName, int)
  398. {
  399. if (!szFileName)
  400. {
  401. return TRUE;
  402. }
  403. for (int i = 0; WhiteList[i] != nullptr; ++i)
  404. {
  405. if (strstr((const char*)szFileName, WhiteList[i]) != 0)
  406. {
  407. return TRUE;
  408. }
  409. }
  410. OVR_ASSERT(false);
  411. return FALSE;
  412. }
  413. #endif // OVR_HUNT_UNTRACKED_ALLOCS
  414. DebugPageAllocator::DebugPageAllocator()
  415. : FreedBlockArray(nullptr)
  416. , FreedBlockArrayMaxSize(0)
  417. , FreedBlockArraySize(0)
  418. , FreedBlockArrayOldest(0)
  419. , AllocationCount(0)
  420. , OverrunPageEnabled(true)
  421. #if defined(OVR_BUILD_DEBUG)
  422. , OverrunGuardBytesEnabled(true)
  423. #else
  424. , OverrunGuardBytesEnabled(false)
  425. #endif
  426. //PageSize(0)
  427. , Lock()
  428. {
  429. #if defined(OVR_HUNT_UNTRACKED_ALLOCS)
  430. _CrtSetAllocHook(YourAllocHook);
  431. #endif // OVR_HUNT_UNTRACKED_ALLOCS
  432. #if defined(_WIN32)
  433. SYSTEM_INFO systemInfo;
  434. GetSystemInfo(&systemInfo);
  435. PageSize = (size_t)systemInfo.dwPageSize;
  436. #else
  437. PageSize = 4096;
  438. #endif
  439. SetMaxDelayedFreeCount(kFreedBlockArrayMaxSizeDefault);
  440. }
  441. DebugPageAllocator::~DebugPageAllocator()
  442. {
  443. Shutdown();
  444. }
  445. void DebugPageAllocator::Init()
  446. {
  447. // Nothing to do.
  448. }
  449. void DebugPageAllocator::Shutdown()
  450. {
  451. Lock::Locker autoLock(&Lock);
  452. for(size_t i = 0; i < FreedBlockArraySize; i++)
  453. {
  454. if(FreedBlockArray[i].BlockPtr)
  455. {
  456. FreePageMemory(FreedBlockArray[i].BlockPtr, FreedBlockArray[i].BlockSize);
  457. FreedBlockArray[i].Clear();
  458. }
  459. }
  460. SetMaxDelayedFreeCount(0);
  461. FreedBlockArraySize = 0;
  462. FreedBlockArrayOldest = 0;
  463. }
  464. void DebugPageAllocator::EnableOverrunDetection(bool enableOverrunDetection, bool enableOverrunGuardBytes)
  465. {
  466. // Assert that no allocations have been made, which is a requirement for changing these properties.
  467. // Otherwise future deallocations of these allocations can fail to work properly because these
  468. // settings have changed behind their back.
  469. OVR_ASSERT_M(AllocationCount == 0, "DebugPageAllocator::EnableOverrunDetection called when DebugPageAllocator is not in a newly initialized state.");
  470. OverrunPageEnabled = enableOverrunDetection;
  471. OverrunGuardBytesEnabled = (enableOverrunDetection && enableOverrunGuardBytes); // Set OverrunGuardBytesEnabled to false if enableOverrunDetection is false.
  472. }
  473. void DebugPageAllocator::SetMaxDelayedFreeCount(size_t maxDelayedFreeCount)
  474. {
  475. if(FreedBlockArray)
  476. {
  477. SafeMMapFree(FreedBlockArray, FreedBlockArrayMaxSize * sizeof(Block));
  478. FreedBlockArrayMaxSize = 0;
  479. }
  480. if(maxDelayedFreeCount)
  481. {
  482. FreedBlockArray = (Block*)SafeMMapAlloc(maxDelayedFreeCount * sizeof(Block));
  483. OVR_ASSERT(FreedBlockArray);
  484. if(FreedBlockArray)
  485. {
  486. FreedBlockArrayMaxSize = maxDelayedFreeCount;
  487. #if defined(OVR_BUILD_DEBUG)
  488. memset(FreedBlockArray, 0, maxDelayedFreeCount * sizeof(Block));
  489. #endif
  490. }
  491. }
  492. }
  493. size_t DebugPageAllocator::GetMaxDelayedFreeCount() const
  494. {
  495. return FreedBlockArrayMaxSize;
  496. }
  497. void* DebugPageAllocator::Alloc(size_t size)
  498. {
  499. #if defined(_WIN32)
  500. return AllocAligned(size, DefaultAlignment);
  501. #else
  502. void* p = malloc(size);
  503. trackAlloc(p, size);
  504. return p;
  505. #endif
  506. }
  507. void* DebugPageAllocator::AllocAligned(size_t size, size_t align)
  508. {
  509. #if defined(_WIN32)
  510. OVR_ASSERT(align <= PageSize);
  511. Lock::Locker autoLock(&Lock);
  512. if(align < DefaultAlignment)
  513. align = DefaultAlignment;
  514. // The actual needed size may be a little less than this, but it's hard to tell how the size and alignments will play out.
  515. size_t maxRequiredSize = AlignSizeUp(size, align) + SizeStorageSize;
  516. if(align > SizeStorageSize)
  517. {
  518. // Must do: more sophisticated fitting, as maxRequiredSize is potentially too small.
  519. OVR_ASSERT(SizeStorageSize <= align);
  520. }
  521. size_t blockSize = AlignSizeUp(maxRequiredSize, PageSize);
  522. if(OverrunPageEnabled)
  523. blockSize += PageSize; // We add another page which will be uncommitted, so any read or write with it will except.
  524. void* pBlockPtr;
  525. if((FreedBlockArraySize == FreedBlockArrayMaxSize) && FreedBlockArrayMaxSize && // If there is an old block we can recycle...
  526. (FreedBlockArray[FreedBlockArrayOldest].BlockSize == blockSize)) // We require it to be the exact size, as there would be some headaches for us if it was over-sized.
  527. {
  528. pBlockPtr = EnablePageMemory(FreedBlockArray[FreedBlockArrayOldest].BlockPtr, blockSize); // Convert this memory from PAGE_NOACCESS back to PAGE_READWRITE.
  529. FreedBlockArray[FreedBlockArrayOldest].Clear();
  530. if(++FreedBlockArrayOldest == FreedBlockArrayMaxSize)
  531. FreedBlockArrayOldest = 0;
  532. }
  533. else
  534. {
  535. pBlockPtr = AllocCommittedPageMemory(blockSize); // Allocate a new block of one or more pages (via VirtualAlloc).
  536. }
  537. if(pBlockPtr)
  538. {
  539. void* pUserPtr = GetUserPosition(pBlockPtr, blockSize, size, align);
  540. size_t* pSizePos = GetSizePosition(pUserPtr);
  541. pSizePos[UserSizeIndex] = size;
  542. pSizePos[BlockSizeIndex] = blockSize;
  543. AllocationCount++;
  544. trackAlloc(pUserPtr, size);
  545. return pUserPtr;
  546. }
  547. return nullptr;
  548. #else
  549. OVR_ASSERT_AND_UNUSED(align <= DefaultAlignment, align);
  550. return DebugPageAllocator::Alloc(size);
  551. #endif
  552. }
  553. size_t DebugPageAllocator::GetUserSize(const void* p)
  554. {
  555. #if defined(_WIN32)
  556. return GetSizePosition(p)[UserSizeIndex];
  557. #elif defined(__APPLE__)
  558. return malloc_size(p);
  559. #else
  560. return malloc_usable_size(const_cast<void*>(p));
  561. #endif
  562. }
  563. size_t DebugPageAllocator::GetBlockSize(const void* p)
  564. {
  565. #if defined(_WIN32)
  566. return GetSizePosition(p)[BlockSizeIndex];
  567. #else
  568. OVR_UNUSED(p);
  569. return 0;
  570. #endif
  571. }
  572. size_t* DebugPageAllocator::GetSizePosition(const void* p)
  573. {
  574. // No thread safety required as per our design, as we assume that anybody
  575. // who owns a pointer returned by Alloc cannot have another thread take it away.
  576. // We assume the pointer is a valid pointer allocated by this allocator.
  577. // We store some size values into the memory returned to the user, a few bytes before it.
  578. size_t value = reinterpret_cast<size_t>(p);
  579. size_t valuePos = (value - SizeStorageSize);
  580. size_t* pSize = reinterpret_cast<size_t*>(valuePos);
  581. return pSize;
  582. }
  583. void* DebugPageAllocator::Realloc(void* p, size_t newSize)
  584. {
  585. #if defined(_WIN32)
  586. return ReallocAligned(p, newSize, DefaultAlignment);
  587. #else
  588. void* newP = realloc(p, newSize);
  589. if(newP) // Need to check newP because realloc doesn't free p unless it returns a valid newP.
  590. {
  591. #if !defined(__clang_analyzer__) // The analyzer complains that we are using p after it was freed.
  592. untrackAlloc(p);
  593. #endif
  594. }
  595. trackAlloc(newP, newSize);
  596. return newP;
  597. #endif
  598. }
  599. void* DebugPageAllocator::ReallocAligned(void* p, size_t newSize, size_t newAlign)
  600. {
  601. #if defined(_WIN32)
  602. // The ISO C99 standard states:
  603. // The realloc function deallocates the old object pointed to by ptr and
  604. // returns a pointer to a new object that has the size specified by size.
  605. // The contents of the new object shall be the same as that of the old
  606. // object prior to deallocation, up to the lesser of the new and old sizes.
  607. // Any bytes in the new object beyond the size of the old object have
  608. // indeterminate values.
  609. //
  610. // If ptr is a null pointer, the realloc function behaves like the malloc
  611. // function for the specified size. Otherwise, if ptr does not match a
  612. // pointer earlier returned by the calloc, malloc, or realloc function,
  613. // or if the space has been deallocated by a call to the free or realloc
  614. // function, the behavior is undefined. If memory for the new object
  615. // cannot be allocated, the old object is not deallocated and its value
  616. // is unchanged.
  617. //
  618. // The realloc function returns a pointer to the new object (which may have
  619. // the same value as a pointer to the old object), or a null pointer if
  620. // the new object could not be allocated.
  621. // A mutex lock isn't required, as the functions below will handle it internally.
  622. // But having it here is a little more efficient because it woudl otherwise be
  623. // locked and unlocked multiple times below, with possible context switches in between.
  624. Lock::Locker autoLock(&Lock);
  625. void* pReturn = nullptr;
  626. if(p)
  627. {
  628. if(newSize)
  629. {
  630. pReturn = AllocAligned(newSize, newAlign);
  631. if(pReturn)
  632. {
  633. size_t prevSize = GetUserSize(p);
  634. if(newSize > prevSize)
  635. newSize = prevSize;
  636. memcpy(pReturn, p, newSize);
  637. Free(p);
  638. } // Else fall through, leaving p's memory unmodified and returning nullptr.
  639. }
  640. else
  641. {
  642. Free(p);
  643. }
  644. }
  645. else if(newSize)
  646. {
  647. pReturn = AllocAligned(newSize, newAlign);
  648. }
  649. return pReturn;
  650. #else
  651. OVR_ASSERT_AND_UNUSED(newAlign <= DefaultAlignment, newAlign);
  652. return DebugPageAllocator::Realloc(p, newSize);
  653. #endif
  654. }
  655. void DebugPageAllocator::Free(void *p)
  656. {
  657. #if defined(_WIN32)
  658. if(p)
  659. {
  660. // Creating a scope for the lock
  661. {
  662. Lock::Locker autoLock(&Lock);
  663. if(FreedBlockArrayMaxSize) // If we have a delayed free list...
  664. {
  665. // We don't free the page(s) associated with this but rather put them in the FreedBlockArray in an inaccessible state for later freeing.
  666. // We do this because we don't want those pages to be available again in the near future, so we can detect use-after-free misakes.
  667. Block* pBlockNew;
  668. if(FreedBlockArraySize == FreedBlockArrayMaxSize) // If we have reached freed block capacity... we can start purging old elements from it as a circular queue.
  669. {
  670. pBlockNew = &FreedBlockArray[FreedBlockArrayOldest];
  671. // The oldest element in the container is FreedBlockArrayOldest.
  672. if(pBlockNew->BlockPtr) // Currently this should always be true.
  673. {
  674. FreePageMemory(pBlockNew->BlockPtr, pBlockNew->BlockSize);
  675. pBlockNew->Clear();
  676. }
  677. if(++FreedBlockArrayOldest == FreedBlockArrayMaxSize)
  678. FreedBlockArrayOldest = 0;
  679. }
  680. else // Else we are still building the container and not yet treating it a circular.
  681. {
  682. pBlockNew = &FreedBlockArray[FreedBlockArraySize++];
  683. }
  684. pBlockNew->BlockPtr = GetBlockPtr(p);
  685. pBlockNew->BlockSize = GetBlockSize(p);
  686. #if defined(OVR_BUILD_DEBUG)
  687. if(OverrunGuardBytesEnabled) // If we have extra bytes at the end of the user's allocation between it and an inaccessible guard page...
  688. {
  689. const size_t userSize = GetUserSize(p);
  690. const uint8_t* pUserEnd = (static_cast<uint8_t*>(p) + userSize);
  691. const uint8_t* pPageEnd = AlignPointerUp(pUserEnd, PageSize);
  692. while(pUserEnd != pPageEnd)
  693. {
  694. if(*pUserEnd++ != GuardFillByte)
  695. {
  696. OVR_FAIL();
  697. break;
  698. }
  699. }
  700. }
  701. #endif
  702. DisablePageMemory(pBlockNew->BlockPtr, pBlockNew->BlockSize); // Make it so that future attempts to use this memory result in an exception.
  703. }
  704. else
  705. {
  706. FreePageMemory(GetBlockPtr(p), GetBlockSize(p));
  707. }
  708. AllocationCount--;
  709. }
  710. untrackAlloc(p);
  711. }
  712. #else
  713. untrackAlloc(p);
  714. return free(p);
  715. #endif
  716. }
  717. void DebugPageAllocator::FreeAligned(void* p)
  718. {
  719. return Free(p);
  720. }
  721. // Converts a user pointer to the beginning of its page.
  722. void* DebugPageAllocator::GetBlockPtr(void* p)
  723. {
  724. // We store size info before p in memory, and this will, by design, be always somewhere within
  725. // the first page of a block of pages. So just align down to the beginning of its page.
  726. return AlignPointerDown(GetSizePosition(p), PageSize);
  727. }
  728. void* DebugPageAllocator::GetUserPosition(void* pPageMemory, size_t blockSize, size_t userSize, size_t userAlignment)
  729. {
  730. uint8_t* pUserPosition;
  731. if(OverrunPageEnabled)
  732. {
  733. // We need to return the highest position within the page memory that fits the user size while being aligned to userAlignment.
  734. const size_t pageEnd = reinterpret_cast<size_t>(pPageMemory) + (blockSize - PageSize); // pageEnd points to the beginning of the final guard page.
  735. const size_t userPosition = AlignSizeDown(pageEnd - userSize, userAlignment);
  736. pUserPosition = reinterpret_cast<uint8_t*>(userPosition);
  737. OVR_ASSERT((userPosition + userSize) <= pageEnd);
  738. // If userSize is not a multiple of userAlignment then there will be (userAlignment - userSize) bytes
  739. // of unused memory between the user allocated space and the end of the page. There is no way around having this.
  740. // For example, a user allocation of 3 bytes with 8 byte alignment will leave 5 unused bytes at the end of the page.
  741. // We optionally fill those unused bytes with a pattern and upon Free verify that the pattern is undisturbed.
  742. // This won't detect reads or writes in that area immediately as with reads or writes beyond that, but it will
  743. // at least detect them at some point (e.g. upon Free).
  744. #if defined(OVR_BUILD_DEBUG)
  745. if(OverrunGuardBytesEnabled)
  746. {
  747. uint8_t* const pUserEnd = (pUserPosition + userSize);
  748. const size_t remainingByteCount = (reinterpret_cast<uint8_t*>(pageEnd) - pUserEnd);
  749. if(remainingByteCount) // If there are any left-over bytes...
  750. memset(pUserEnd, GuardFillByte, remainingByteCount);
  751. }
  752. #endif
  753. }
  754. else
  755. {
  756. // We need to return the first position in the first page after SizeStorageSize bytes which is aligned to userAlignment.
  757. const size_t lowestPossiblePos = reinterpret_cast<size_t>(pPageMemory) + SizeStorageSize;
  758. const size_t userPosition = AlignSizeUp(lowestPossiblePos, userAlignment);
  759. pUserPosition = reinterpret_cast<uint8_t*>(userPosition);
  760. OVR_ASSERT((userPosition + userSize) <= (reinterpret_cast<size_t>(pPageMemory) + blockSize));
  761. }
  762. // Assert that the returned user pointer (actually the size info before it) will be within the first page.
  763. // This is important because it verifieds that we haven't wasted memory and because
  764. // our functionality for telling the start of the page block depends on it.
  765. OVR_ASSERT(AlignPointerDown(GetSizePosition(pUserPosition), PageSize) == pPageMemory);
  766. return pUserPosition;
  767. }
  768. void* DebugPageAllocator::AllocCommittedPageMemory(size_t blockSize)
  769. {
  770. #if defined(_WIN32)
  771. void* p;
  772. if(OverrunPageEnabled)
  773. {
  774. // We need to make it so that last page is MEM_RESERVE and the previous pages are MEM_COMMIT + PAGE_READWRITE.
  775. OVR_ASSERT(blockSize > PageSize); // There should always be at least one extra page.
  776. // Reserve blockSize amount of pages.
  777. // We could possibly use PAGE_GUARD here for the last page. This differs from simply leaving it reserved
  778. // because the OS will generate a one-time-only gaurd page exception. We probabl don't want this, as it's
  779. // more useful for maintaining your own stack than for catching unintended overruns.
  780. p = VirtualAlloc(nullptr, blockSize, MEM_RESERVE, PAGE_READWRITE);
  781. if(p)
  782. {
  783. // Commit all but the last page. Leave the last page as merely reserved so that reads from or writes
  784. // to it result in an immediate exception.
  785. p = VirtualAlloc(p, blockSize - PageSize, MEM_COMMIT, PAGE_READWRITE);
  786. }
  787. }
  788. else
  789. {
  790. // We need to make it so that all pages are MEM_COMMIT + PAGE_READWRITE.
  791. p = VirtualAlloc(nullptr, blockSize, MEM_COMMIT, PAGE_READWRITE);
  792. }
  793. #if defined(OVR_BUILD_DEBUG)
  794. if(!p)
  795. {
  796. // To consider: Make a generic OVRKernel function for formatting system errors. We could move
  797. // the OVRError GetSysErrorCodeString from LibOVR/OVRError.h to LibOVRKernel/OVR_DebugHelp.h
  798. DWORD dwLastError = GetLastError();
  799. WCHAR osError[256];
  800. DWORD osErrorBufferCapacity = OVR_ARRAY_COUNT(osError);
  801. CHAR reportedError[384];
  802. DWORD length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, (DWORD)dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), osError, osErrorBufferCapacity, nullptr);
  803. if (length)
  804. {
  805. std::string errorBuff = UCSStringToUTF8String(osError, length + 1);
  806. OVR_snprintf(reportedError, OVR_ARRAY_COUNT(reportedError), "DebugPageAllocator: VirtualAlloc failed with error: %s", errorBuff);
  807. }
  808. else
  809. {
  810. OVR_snprintf(reportedError, OVR_ARRAY_COUNT(reportedError), "DebugPageAllocator: VirtualAlloc failed with error: %d.", dwLastError);
  811. }
  812. //LogError("%s", reportedError); Disabled because this call turns around and allocates memory, yet we may be in a broken or exhausted memory situation.
  813. OVR_FAIL_M(reportedError);
  814. }
  815. #endif
  816. return p;
  817. #else
  818. OVR_UNUSED2(blockSize, OverrunPageEnabled);
  819. return nullptr;
  820. #endif
  821. }
  822. // We convert disabled page memory (see DisablePageMemory) to enabled page memory. The output is the same
  823. // as with AllocPageMemory.
  824. void* DebugPageAllocator::EnablePageMemory(void* pPageMemory, size_t blockSize)
  825. {
  826. #if defined(_WIN32)
  827. // Make sure the entire range of memory is of type PAGE_READWRITE.
  828. DWORD dwPrevAccess = 0;
  829. BOOL result = VirtualProtect(pPageMemory, OverrunPageEnabled ? (blockSize - PageSize) : blockSize, PAGE_READWRITE, &dwPrevAccess);
  830. OVR_ASSERT_AND_UNUSED(result, result);
  831. #else
  832. OVR_UNUSED3(pPageMemory, blockSize, OverrunPageEnabled);
  833. #endif
  834. return pPageMemory;
  835. }
  836. void DebugPageAllocator::DisablePageMemory(void* pPageMemory, size_t blockSize)
  837. {
  838. #if defined(_WIN32)
  839. // Disable access to the page(s). It's faster for us to change the page access than it is to decommit or free the pages.
  840. // However, this results in more committed physical memory usage than we would need if we instead decommitted the memory.
  841. DWORD dwPrevAccesss = 0;
  842. BOOL result = VirtualProtect(pPageMemory, OverrunPageEnabled ? (blockSize - PageSize) : blockSize, PAGE_NOACCESS, &dwPrevAccesss);
  843. OVR_ASSERT_AND_UNUSED(result, result);
  844. #else
  845. OVR_UNUSED2(pPageMemory, blockSize);
  846. #endif
  847. }
  848. void DebugPageAllocator::FreePageMemory(void* pPageMemory, size_t /*blockSize*/)
  849. {
  850. #if defined(_WIN32)
  851. BOOL result = VirtualFree(pPageMemory, 0, MEM_RELEASE);
  852. OVR_ASSERT_AND_UNUSED(result, result);
  853. #else
  854. OVR_UNUSED(pPageMemory);
  855. #endif
  856. }
  857. } // namespace OVR