From e1c9a3781b97858b53a69fd4ed0c96506b70b8f8 Mon Sep 17 00:00:00 2001 From: Misaki Date: Wed, 25 Mar 2026 18:15:04 +0900 Subject: [PATCH] feat(allocation): add debug layer for allocation tracking Adds a debug layer for memory allocation tracking, enabled in DEBUG builds via ENABLE_DEBUG_LAYER. When active, allocations and reallocations in FreeListAllocator store stack traces and maintain linked lists of allocation headers for enhanced debugging. Memory is optionally cleared on allocation, and debug metadata is properly cleaned up on free. Updates allocation logic to support debug and non-debug modes. Also updates AssemblyVersion to 1.5.4 and revises Program.cs to use the new allocation manager initialization and FreeList allocator. --- .../Buffer/AllocationManager.cs | 82 ++++++++++++++++++- .../Misaki.HighPerformance.LowLevel.csproj | 2 +- Misaki.HighPerformance.Test/Program.cs | 17 ++-- 3 files changed, 88 insertions(+), 13 deletions(-) diff --git a/Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs b/Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs index 5cfedba..b8dd0e4 100644 --- a/Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs +++ b/Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs @@ -1,3 +1,7 @@ +#if DEBUG +#define ENABLE_DEBUG_LAYER +#endif + using Misaki.HighPerformance.Collections; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -338,7 +342,7 @@ public static unsafe class AllocationManager for (var i = 0; i < s_stackCount; i++) { - Free(s_pStackBuffers[i]); + Munmap(s_pStackBuffers[i], s_threadLocalStackDefaultSize); } } } @@ -366,6 +370,33 @@ public static unsafe class AllocationManager private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle) { var selfPtr = (FreeListAllocator*)instance; +#if ENABLE_DEBUG_LAYER + var pad = alignment == 0 ? (nuint)IntPtr.Size : alignment; + var total = size + (nuint)sizeof(AllocationHeader) + (pad - 1); + var basePtr = selfPtr->_freeList.Allocate(total, pad, allocationOption); + if (basePtr == null) + { + *pHandle = MemoryHandle.Invalid; + return null; + } + + var user = AlignUp((byte*)basePtr + (nuint)sizeof(AllocationHeader), pad); + var header = (AllocationHeader*)(user - (nuint)sizeof(AllocationHeader)); + + header->basePtr = basePtr; + header->userSize = size; + HeaderSetHandle(header, GCHandle.Alloc(new StackTrace(2, true))); + + LinkHeader(header); + + if (allocationOption.HasFlag(AllocationOption.Clear)) + { + MemClear(user, size); + } + + *pHandle = AddAllocation(user); + return user; +#else var ptr = selfPtr->_freeList.Allocate(size, alignment, allocationOption); if (ptr == null) { @@ -375,6 +406,7 @@ public static unsafe class AllocationManager *pHandle = AddAllocation(ptr); return ptr; +#endif } private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle) @@ -385,6 +417,41 @@ public static unsafe class AllocationManager } var selfPtr = (FreeListAllocator*)instance; + +#if ENABLE_DEBUG_LAYER + var oldHeader = (AllocationHeader*)((byte*)ptr - (nuint)sizeof(AllocationHeader)); + var handle = HeaderGetHandle(oldHeader); + + var pad = alignment == 0 ? (nuint)IntPtr.Size : alignment; + var total = newSize + (nuint)sizeof(AllocationHeader) + (pad - 1); + var newBase = selfPtr->_freeList.Allocate(total, pad, allocationOption); + if (newBase == null) + { + return null; + } + + var newUser = AlignUp((byte*)newBase + (nuint)sizeof(AllocationHeader), pad); + var newHeader = (AllocationHeader*)(newUser - (nuint)sizeof(AllocationHeader)); + + newHeader->basePtr = newBase; + newHeader->userSize = newSize; + HeaderSetHandle(newHeader, handle); + + LinkHeader(newHeader); + + MemCpy(newUser, ptr, Math.Min(oldSize, newSize)); + if (allocationOption.HasFlag(AllocationOption.Clear) && newSize > oldSize) + { + MemClear(newUser + oldSize, newSize - oldSize); + } + + UnlinkHeader(oldHeader); + selfPtr->_freeList.Free(oldHeader->basePtr); + RemoveAllocation(*pHandle); + + *pHandle = AddAllocation(newUser); + return newUser; +#else var newPtr = selfPtr->_freeList.Allocate(newSize, alignment, allocationOption); if (newPtr == null) { @@ -398,6 +465,7 @@ public static unsafe class AllocationManager *pHandle = AddAllocation(newPtr); return newPtr; +#endif } private static bool IsValid(void* instance, MemoryHandle handle) @@ -408,7 +476,14 @@ public static unsafe class AllocationManager private static void Free(void* instance, void* ptr, MemoryHandle handle) { var selfPtr = (FreeListAllocator*)instance; +#if ENABLE_DEBUG_LAYER + var header = (AllocationHeader*)((byte*)ptr - (nuint)sizeof(AllocationHeader)); + UnlinkHeader(header); + HeaderFreeHandle(header); + selfPtr->_freeList.Free(header->basePtr); +#else selfPtr->_freeList.Free(ptr); +#endif RemoveAllocation(handle); } @@ -430,11 +505,10 @@ public static unsafe class AllocationManager private static ConcurrentSlotMap s_allocations = null!; - public static readonly MemoryHandle MagicHandle = new MemoryHandle(int.MinValue, int.MinValue); - private static bool s_initialized; + private static nuint s_threadLocalStackDefaultSize; - internal static nuint s_threadLocalStackDefaultSize; + public static readonly MemoryHandle MagicHandle = new MemoryHandle(int.MinValue, int.MinValue); /// /// Gets the number of live tracked heap allocations. diff --git a/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj b/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj index 5829706..378abdd 100644 --- a/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj +++ b/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj @@ -7,7 +7,7 @@ true true Misaki - 1.5.3 + 1.5.4 $(AssemblyVersion) https://git.personalnas.com/Misaki/Misaki.HighPerformance.git https://git.personalnas.com/Misaki/Misaki.HighPerformance.git diff --git a/Misaki.HighPerformance.Test/Program.cs b/Misaki.HighPerformance.Test/Program.cs index 68728a8..fd50f7e 100644 --- a/Misaki.HighPerformance.Test/Program.cs +++ b/Misaki.HighPerformance.Test/Program.cs @@ -32,13 +32,14 @@ using Misaki.HighPerformance.LowLevel.Collections; // } //} -using var pool = new MemoryPool(new VirtualStack.CreationOpts() { reserveCapacity = 1024 * 1024 }); -using var scope = pool.Allocator.CreateScope(pool.AllocationHandle); - -var arr = new UnsafeArray(1000, scope.AllocationHandle); -for (var i = 0; i < arr.Length; i++) +var opts = new AllocationManagerInitOpts { - Console.WriteLine(arr[i]); -} + ArenaCapacity = 1024 * 1024, + StackCapacity = 1024 * 1024, + FreeListConcurrencyLevel = 1 +}; -arr.Dispose(); +AllocationManager.Initialize(opts); + +var arr = new UnsafeArray(10, Allocator.FreeList); +AllocationManager.Dispose();