Initial
This commit is contained in:
commit
d4c558a59a
34 changed files with 3850 additions and 0 deletions
25
private/Allocators.cpp
Normal file
25
private/Allocators.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
#include "allocators.hpp"
|
||||
|
||||
#include "filesystem.h"
|
||||
|
||||
static tp::ModuleManifest* sModuleDependencies[] = { &tp::gModuleFilesystem, NULL };
|
||||
tp::ModuleManifest tp::gModuleAllocator = ModuleManifest("Allocators", NULL, NULL, sModuleDependencies);
|
||||
|
||||
|
||||
//void* operator new(size_t aSize, void* aWhere) noexcept { return aWhere; }
|
||||
|
||||
void* operator new(size_t aSize) { return tp::HeapAllocGlobal::allocate(aSize); }
|
||||
void* operator new[](size_t aSize) { return tp::HeapAllocGlobal::allocate(aSize); }
|
||||
void operator delete(void* aPtr) { tp::HeapAllocGlobal::deallocate(aPtr); }
|
||||
void operator delete[](void* aPtr) { tp::HeapAllocGlobal::deallocate(aPtr); }
|
||||
|
||||
void* operator new(size_t aSize, tp::HeapAlloc& aAlloc) { return aAlloc.allocate(aSize); }
|
||||
void* operator new[](size_t aSize, tp::HeapAlloc& aAlloc) { return aAlloc.allocate(aSize); }
|
||||
void operator delete(void* aPtr, tp::HeapAlloc& aAlloc) { aAlloc.deallocate(aPtr); }
|
||||
void operator delete[](void* aPtr, tp::HeapAlloc& aAlloc) { aAlloc.deallocate(aPtr); }
|
||||
|
||||
void* operator new(size_t aSize, tp::HeapAllocGlobal& aAlloc) { return aAlloc.allocate(aSize); }
|
||||
void* operator new[](size_t aSize, tp::HeapAllocGlobal& aAlloc) { return aAlloc.allocate(aSize); }
|
||||
void operator delete(void* aPtr, tp::HeapAllocGlobal& aAlloc) { aAlloc.deallocate(aPtr); }
|
||||
void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc) { aAlloc.deallocate(aPtr); }
|
||||
113
private/ChunkAllocator.cpp
Normal file
113
private/ChunkAllocator.cpp
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
|
||||
/*
|
||||
*
|
||||
* Implementation uses embedded one-directional linked list to track free blocks.
|
||||
* The embedded part ensures that there is no memory overhead on block specifically.
|
||||
* Linked list is initialized iteratively on each allocation if it has not been already.
|
||||
|
||||
* Allocating:
|
||||
* 1) updating list entry to stored in the entry itself next free pointer
|
||||
* 2) returning entry before (1).
|
||||
*
|
||||
* Deallocating:
|
||||
* 1) assigning list entry value to the deleted block
|
||||
* 2) updating list entry to that block.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ChunkAllocator.hpp"
|
||||
#include "PrivateConfig.hpp"
|
||||
|
||||
#include "HeapAllocatorGlobal.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
enum : ualni {
|
||||
ALIGNED_SIZE = ENV_ALNI_SIZE_B,
|
||||
WRAP_SIZE_ALN = MEM_WRAP_SIZE,
|
||||
WRAP_SIZE = WRAP_SIZE_ALN * ALIGNED_SIZE,
|
||||
WRAP_VAL = MEM_WRAP_FILL_VAL,
|
||||
CLEAR_ALLOC_VAL = MEM_CLEAR_ON_ALLOC_VAL,
|
||||
CLEAR_DEALLOC_VAL = MEM_CLEAR_ON_DEALLOC_VAL,
|
||||
};
|
||||
|
||||
ChunkAlloc::ChunkAlloc(ualni aBlockSize, void* aMemory, ualni aMemSize) {
|
||||
auto const temp = aBlockSize / ALIGNED_SIZE;
|
||||
mBSize = ((aBlockSize % ALIGNED_SIZE) ? temp + 1 : temp) + WRAP_SIZE_ALN * 2;
|
||||
mBuff = (ualni*) aMemory;
|
||||
mNBlocks = (aMemSize / ALIGNED_SIZE) / mBSize;
|
||||
mNFreeBlocks = mNBlocks;
|
||||
}
|
||||
|
||||
ChunkAlloc::ChunkAlloc(ualni aBlockSize, ualni aNBlocks) {
|
||||
auto const temp = aBlockSize / ALIGNED_SIZE;
|
||||
mBSize = ((aBlockSize % ALIGNED_SIZE) ? temp + 1 : temp) + WRAP_SIZE_ALN * 2;
|
||||
mNBlocks = aNBlocks;
|
||||
mNFreeBlocks = mNBlocks;
|
||||
mBuff = (ualni*)HeapAllocGlobal::allocate(mNBlocks * mBSize * ALIGNED_SIZE);
|
||||
mOwnBuff = true;
|
||||
}
|
||||
|
||||
void* ChunkAlloc::allocate() {
|
||||
RelAssert(mNFreeBlocks && "Out Of Memory");
|
||||
|
||||
// 1) PreInitialize blocks
|
||||
if (mNInitBlocks < mNBlocks) {
|
||||
*(mBuff + mNInitBlocks * mBSize) = (ualni)(mBuff + (mNInitBlocks++) * mBSize);
|
||||
}
|
||||
|
||||
// 2) Find free block and update next free block
|
||||
auto data = mNextBlock;
|
||||
mNextBlock = (ualni*)(*data);
|
||||
mNFreeBlocks--;
|
||||
|
||||
#ifdef MEM_DEBUG
|
||||
// 3) Fill Wrap and offset data
|
||||
auto wrap_top = data;
|
||||
data += WRAP_SIZE_ALN;
|
||||
auto wrap_bottom = data + mBSize;
|
||||
|
||||
memsetv(wrap_top, WRAP_SIZE, WRAP_VAL);
|
||||
memsetv(wrap_bottom, WRAP_SIZE, WRAP_VAL);
|
||||
|
||||
// 4) Clear data
|
||||
#ifdef MEM_CLEAR_ON_ALLOC
|
||||
memsetv(data, mBSize * ALIGNED_SIZE, CLEAR_ALLOC_VAL);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void ChunkAlloc::deallocate(void* aPtr) {
|
||||
auto block = (ualni*)aPtr;
|
||||
|
||||
#ifdef MEM_DEBUG
|
||||
// 3) Check Wrap and offset data
|
||||
auto wrap_bottom = block + mBSize;
|
||||
auto wrap_top = block - WRAP_SIZE_ALN;
|
||||
block = wrap_top;
|
||||
|
||||
// 3) Check the wrap
|
||||
RelAssert(memequalv(wrap_top, WRAP_SIZE, WRAP_VAL) && memequalv(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!");
|
||||
|
||||
// 4) Clear data
|
||||
#ifdef MEM_CLEAR_ON_ALLOC
|
||||
memsetv(aPtr, mBSize * ALIGNED_SIZE, CLEAR_DEALLOC_VAL);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
(*block) = (ualni)mNextBlock;
|
||||
mNextBlock = block;
|
||||
mNFreeBlocks++;
|
||||
}
|
||||
|
||||
bool ChunkAlloc::isFull() { return !mNFreeBlocks; }
|
||||
bool ChunkAlloc::isEmpty() { return mNFreeBlocks == mNBlocks; }
|
||||
|
||||
ChunkAlloc::~ChunkAlloc() {
|
||||
if (mOwnBuff) {
|
||||
HeapAllocGlobal::deallocate(mBuff);
|
||||
}
|
||||
}
|
||||
72
private/HeapAllocator.cpp
Normal file
72
private/HeapAllocator.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
#include "heapallocator.hpp"
|
||||
#include "PrivateConfig.hpp"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
#if not defined(MEM_DEBUG)
|
||||
// ----------------------- Release Implementation ---------------------------- //
|
||||
void* HeapAlloc::allocate(ualni aBlockSize) { return malloc(aBlockSize); }
|
||||
void HeapAlloc::deallocate(void* aPtr) { free(aPtr); }
|
||||
HeapAlloc::~HeapAlloc() {}
|
||||
#else
|
||||
|
||||
namespace tp {
|
||||
struct MemHeadLocal {
|
||||
MemHeadLocal* mPrev;
|
||||
MemHeadLocal* mNext;
|
||||
};
|
||||
};
|
||||
|
||||
void* HeapAlloc::allocate(ualni aBlockSize) {
|
||||
auto head = (MemHeadLocal*)mAlloc.allocate(aBlockSize + sizeof(MemHeadLocal));
|
||||
auto out = head + 1;
|
||||
|
||||
mNumAllocations++;
|
||||
if (mEntry) {
|
||||
head->mNext = mEntry->mNext;
|
||||
head->mPrev = mEntry->mPrev;
|
||||
if (mEntry->mNext) mEntry->mNext->mPrev = head;
|
||||
if (mEntry->mPrev) mEntry->mPrev->mNext = head;
|
||||
}
|
||||
else {
|
||||
head->mNext = NULL;
|
||||
head->mPrev = NULL;
|
||||
}
|
||||
mEntry = head;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void HeapAlloc::deallocate(void* aPtr) {
|
||||
auto head = ((MemHeadLocal*)(aPtr)) - 1;
|
||||
|
||||
mNumAllocations--;
|
||||
if (mEntry->mNext) mEntry->mNext->mPrev = mEntry->mPrev;
|
||||
if (mEntry->mPrev) mEntry->mPrev->mNext = mEntry->mNext;
|
||||
if (head == mEntry) {
|
||||
if (mEntry->mNext) {
|
||||
mEntry = mEntry->mNext;
|
||||
}
|
||||
else {
|
||||
mEntry = mEntry->mNext;
|
||||
}
|
||||
}
|
||||
|
||||
mAlloc.deallocate(head);
|
||||
}
|
||||
|
||||
HeapAlloc::~HeapAlloc() {
|
||||
if (mNumAllocations) {
|
||||
DBG_BREAK("Destruction of not freed Allocator");
|
||||
|
||||
#ifdef MEM_STACK_TRACE
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
146
private/HeapAllocatorGlobal.cpp
Normal file
146
private/HeapAllocatorGlobal.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
|
||||
#include "heapallocator.hpp"
|
||||
#include "PrivateConfig.hpp"
|
||||
|
||||
#include "StackTrace.hpp"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
#if not defined(MEM_DEBUG)
|
||||
|
||||
// ----------------------- Release Implementation ---------------------------- //
|
||||
void* HeapAllocGlobal::allocate(ualni aBlockSize) { return malloc(aBlockSize); }
|
||||
void HeapAllocGlobal::deallocate(void* aPtr) { free(aPtr); }
|
||||
HeapAllocGlobal::~HeapAllocGlobal() {};
|
||||
|
||||
#else
|
||||
|
||||
tp::MemHead* tp::HeapAllocGlobal::mEntry = NULL;
|
||||
tp::ualni tp::HeapAllocGlobal::mNumAllocations = NULL;
|
||||
|
||||
// ----------------------- Debug Implementation ---------------------------- //
|
||||
// |----------------|
|
||||
// | MemHead |
|
||||
// |----------------|
|
||||
// | wrap top |
|
||||
// |----------------| - Allocated Block Layout
|
||||
// | data |
|
||||
// |----------------|
|
||||
// | wrap bottom |
|
||||
// |----------------|
|
||||
|
||||
namespace tp {
|
||||
struct MemHead {
|
||||
MemHead* mPrev;
|
||||
MemHead* mNext;
|
||||
ualni mBlockSize;
|
||||
#ifdef MEM_STACK_TRACE
|
||||
CallStackSnapshots::StackShapshot mCallStack;
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
enum : ualni {
|
||||
ALIGNED_SIZE = ENV_ALNI_SIZE_B,
|
||||
WRAP_SIZE = MEM_WRAP_SIZE * ALIGNED_SIZE,
|
||||
WRAP_VAL = MEM_WRAP_FILL_VAL,
|
||||
HEAD_SIZE = sizeof(MemHead),
|
||||
CLEAR_ALLOC_VAL = MEM_CLEAR_ON_ALLOC_VAL,
|
||||
CLEAR_DEALLOC_VAL = MEM_CLEAR_ON_DEALLOC_VAL,
|
||||
};
|
||||
|
||||
void* HeapAllocGlobal::allocate(ualni aBlockSize) {
|
||||
|
||||
static_assert(HEAD_SIZE % ALIGNED_SIZE == 0, "Heap Allocator Configuration Error");
|
||||
|
||||
if (aBlockSize % ALIGNED_SIZE) {
|
||||
aBlockSize = (aBlockSize / ALIGNED_SIZE + 1) * ALIGNED_SIZE;
|
||||
}
|
||||
|
||||
// 1) Allocate the block
|
||||
auto head = (MemHead*)malloc(aBlockSize + WRAP_SIZE * 2 + HEAD_SIZE);
|
||||
auto wrap_top = (int1*)(head + 1);
|
||||
auto data = wrap_top + WRAP_SIZE;
|
||||
auto wrap_bottom = data + aBlockSize;
|
||||
|
||||
if (!head) { return NULL; }
|
||||
head->mBlockSize = aBlockSize;
|
||||
|
||||
// 2) Link with existing blocks
|
||||
mNumAllocations++;
|
||||
if (mEntry) {
|
||||
head->mNext = mEntry->mNext;
|
||||
head->mPrev = mEntry->mPrev;
|
||||
if (mEntry->mNext) mEntry->mNext->mPrev = head;
|
||||
if (mEntry->mPrev) mEntry->mPrev->mNext = head;
|
||||
}
|
||||
else {
|
||||
head->mNext = NULL;
|
||||
head->mPrev = NULL;
|
||||
}
|
||||
mEntry = head;
|
||||
|
||||
// 3) Wrap fill
|
||||
memsetv(wrap_top, WRAP_SIZE, WRAP_VAL);
|
||||
memsetv(wrap_bottom, WRAP_SIZE, WRAP_VAL);
|
||||
|
||||
// 4) Trace the stack
|
||||
#ifdef MEM_STACK_TRACE
|
||||
head->mCallStack = gCallStackSnapshots.capture();
|
||||
#endif
|
||||
|
||||
// 5) clear data
|
||||
#ifdef MEM_CLEAR_ON_ALLOC
|
||||
memsetv(data, aBlockSize, CLEAR_ALLOC_VAL);
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void HeapAllocGlobal::deallocate(void* aPtr) {
|
||||
// 1) Restore the pointers
|
||||
auto head = ((MemHead*)(aPtr)) - 1;
|
||||
auto wrap_top = (int1*)(head + 1);
|
||||
auto data = wrap_top + WRAP_SIZE;
|
||||
auto wrap_bottom = data + head->mBlockSize;
|
||||
|
||||
// 2) Unlink with blocks
|
||||
mNumAllocations--;
|
||||
if (mEntry->mNext) mEntry->mNext->mPrev = mEntry->mPrev;
|
||||
if (mEntry->mPrev) mEntry->mPrev->mNext = mEntry->mNext;
|
||||
if (head == mEntry) {
|
||||
if (mEntry->mNext) {
|
||||
mEntry = mEntry->mNext;
|
||||
}
|
||||
else {
|
||||
mEntry = mEntry->mNext;
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Check the wrap
|
||||
RelAssert(memequalv(wrap_top, WRAP_SIZE, WRAP_VAL) && memequalv(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!");
|
||||
|
||||
// 4) clear data
|
||||
#ifdef MEM_CLEAR_ON_ALLOC
|
||||
memsetv(data, head->mBlockSize, CLEAR_DEALLOC_VAL);
|
||||
#endif
|
||||
|
||||
// 5) free the block
|
||||
free(aPtr);
|
||||
}
|
||||
|
||||
HeapAllocGlobal::~HeapAllocGlobal() {
|
||||
// 1) Check for not deallocated memory
|
||||
if (mNumAllocations) {
|
||||
DBG_BREAK("Destruction of not freed Allocator");
|
||||
|
||||
#ifdef MEM_STACK_TRACE
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
1
private/PickAllocator.cpp
Normal file
1
private/PickAllocator.cpp
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
132
private/PoolAllocator.cpp
Normal file
132
private/PoolAllocator.cpp
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
|
||||
/*
|
||||
*
|
||||
Implementation:
|
||||
* Adding chunk pointer to each chunk to form one-directional list that keeps track of free chunk
|
||||
* Storing ordered pointers to chunks in order to find desired chunk from delete pointer on deallocation in log time
|
||||
*
|
||||
* Allocations:
|
||||
* 1) allocate with chunk stored in list entry
|
||||
* 2) ...
|
||||
*
|
||||
* Deallocations:
|
||||
* 1) binary-search with delete pointer to find desired chunk
|
||||
* 2) ...
|
||||
*
|
||||
*/
|
||||
|
||||
#include "allocators.hpp"
|
||||
#include "PrivateConfig.hpp"
|
||||
|
||||
void tp::PoolAlloc::Chunks::add(Chunk* aChunk) {
|
||||
// ensure order
|
||||
auto smaller_address = findUtil(mBuff, mBuff + mUsedLen, aChunk);
|
||||
for (auto iter = mBuff + mUsedLen; iter != smaller_address; iter--) { *(iter + 1) = *iter; }
|
||||
|
||||
*(smaller_address + 1) = aChunk;
|
||||
mUsedLen++;
|
||||
|
||||
// check for buff overflow
|
||||
if (mUsedLen == mLen) {
|
||||
auto prevBuff = mBuff;
|
||||
mBuff = (Chunk**)HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen * 2);
|
||||
memcp(mBuff, prevBuff, sizeof(Chunk*) * mUsedLen);
|
||||
mLen *= 2;
|
||||
HeapAllocGlobal::deallocate(prevBuff);
|
||||
}
|
||||
}
|
||||
|
||||
void tp::PoolAlloc::Chunks::remove(Chunk* aChunk) {
|
||||
// ensure order
|
||||
auto del_address = findUtil(mBuff, mBuff + mUsedLen, aChunk);
|
||||
for (auto iter = del_address; iter != mBuff + mUsedLen; iter++) { *iter = *(iter + 1); }
|
||||
|
||||
mUsedLen--;
|
||||
|
||||
// check for buff low usage
|
||||
if ((halnf)mUsedLen / mLen < 0.25f) {
|
||||
auto prevBuff = mBuff;
|
||||
mBuff = (Chunk**)HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen / 2);
|
||||
memcp(mBuff, prevBuff, sizeof(Chunk*) * mUsedLen);
|
||||
mLen /= 2;
|
||||
HeapAllocGlobal::deallocate(prevBuff);
|
||||
}
|
||||
}
|
||||
|
||||
tp::PoolAlloc::Chunk* tp::PoolAlloc::Chunks::find(void* aPtr) {
|
||||
return *findUtil(mBuff, mBuff + mUsedLen, aPtr);
|
||||
}
|
||||
|
||||
tp::PoolAlloc::Chunk* tp::PoolAlloc::Chunks::findNotFull() {
|
||||
for (ualni idx = 0; idx < mUsedLen; idx++) {
|
||||
if (!mBuff[idx]->isFull()) {
|
||||
return mBuff[idx];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tp::PoolAlloc::Chunk** tp::PoolAlloc::Chunks::findUtil(Chunk** aLeft, Chunk** aRight, void* aPtr) {
|
||||
auto range = ualni(aRight - aLeft);
|
||||
if (range == 1) { return aLeft; }
|
||||
auto middle = aLeft + range / 2;
|
||||
return (aPtr > *middle) ? findUtil(middle, aRight, aPtr) : findUtil(aLeft, middle, aPtr);
|
||||
}
|
||||
|
||||
|
||||
tp::PoolAlloc::PoolAlloc(ualni aBlockSize, ualni aChunkSize) : mBlockSize(aBlockSize), mChunkSize(aChunkSize) {}
|
||||
|
||||
void* tp::PoolAlloc::allocate() {
|
||||
|
||||
if (!mFreeChunk || mFreeChunk->isFull()) {
|
||||
auto new_free_chunk = mChunks.findNotFull();
|
||||
|
||||
if (!new_free_chunk) {
|
||||
|
||||
new_free_chunk = new ((Chunk*)HeapAllocGlobal::allocate(sizeof(Chunk))) Chunk(mBlockSize, mChunkSize);
|
||||
RelAssert(new_free_chunk);
|
||||
mChunks.add(new_free_chunk);
|
||||
|
||||
if (mFreeChunk) {
|
||||
new_free_chunk->mNext = mFreeChunk->mNext;
|
||||
new_free_chunk->mPrev = mFreeChunk->mPrev;
|
||||
if (mFreeChunk->mNext) mFreeChunk->mNext->mPrev = new_free_chunk;
|
||||
if (mFreeChunk->mPrev) mFreeChunk->mPrev->mNext = new_free_chunk;
|
||||
}
|
||||
else {
|
||||
new_free_chunk->mNext = NULL;
|
||||
new_free_chunk->mPrev = NULL;
|
||||
}
|
||||
|
||||
mFreeChunk = new_free_chunk;
|
||||
}
|
||||
}
|
||||
|
||||
return mFreeChunk->allocate();
|
||||
}
|
||||
|
||||
void tp::PoolAlloc::deallocate(void* aPtr) {
|
||||
auto chunk = mChunks.find(aPtr);
|
||||
chunk->deallocate(aPtr);
|
||||
|
||||
if (mFreeChunk->isEmpty()) {
|
||||
|
||||
Chunk* new_chunk = NULL;
|
||||
for (ualni idx = 0; idx < mChunks.mUsedLen; idx++) {
|
||||
if (!mChunks.mBuff[idx]->isFull() && new_chunk != mFreeChunk) {
|
||||
new_chunk = mChunks.mBuff[idx];
|
||||
}
|
||||
}
|
||||
|
||||
if (new_chunk) {
|
||||
if (mFreeChunk->mNext) mFreeChunk->mNext->mPrev = mFreeChunk->mPrev;
|
||||
if (mFreeChunk->mPrev) mFreeChunk->mPrev->mNext = mFreeChunk->mNext;
|
||||
mChunks.remove(mFreeChunk);
|
||||
HeapAllocGlobal::deallocate(mFreeChunk);
|
||||
mFreeChunk = new_chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tp::PoolAlloc::~PoolAlloc() {
|
||||
}
|
||||
12
private/PrivateConfig.hpp
Normal file
12
private/PrivateConfig.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "PublicConfig.hpp"
|
||||
|
||||
#define MEM_WRAP_SIZE 8 // Wrapping Size in aligned units
|
||||
#define MEM_WRAP_FILL_VAL 0xBB // Wrapping Fill Value
|
||||
#define MEM_CLEAR_ON_ALLOC // Clear data on allocation
|
||||
#define MEM_CLEAR_ON_DEALLOC // Clear data on free
|
||||
#define MEM_CLEAR_ON_DEALLOC_VAL 0xAA // Clear data on free
|
||||
#define MEM_CLEAR_ON_ALLOC_VAL 0xCC // Clear data on free
|
||||
#define MEM_STACK_TRACE // Save stack on allocation call
|
||||
#define MEM_STACK_TRACE_MAX_DEPTH 32 // Call stack max depth
|
||||
186
private/StackTrace.cpp
Normal file
186
private/StackTrace.cpp
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
|
||||
#include "StackTrace.hpp"
|
||||
#include "PrivateConfig.hpp"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
|
||||
using namespace tp;
|
||||
|
||||
tp::CallStackSnapshots tp::gCallStackSnapshots;
|
||||
|
||||
// ----------------------- Dict ----------------------- //
|
||||
|
||||
CallStackSnapshots::SnapshotsDict::SnapshotsDict() {
|
||||
mTable = (StackShapshot*)malloc(sizeof(StackShapshot) * mSize);
|
||||
memsetv(mTable, sizeof(StackShapshot) * mSize, 0);
|
||||
}
|
||||
|
||||
CallStackSnapshots::StackShapshot CallStackSnapshots::SnapshotsDict::getSlot(ualni aIdx) {
|
||||
return mTable[aIdx];
|
||||
}
|
||||
|
||||
alni CallStackSnapshots::SnapshotsDict::presents(StackShapshot aPtr) {
|
||||
return findSlotRead(aPtr);
|
||||
}
|
||||
|
||||
void CallStackSnapshots::SnapshotsDict::put(StackShapshot aPtr) {
|
||||
auto idx = findSlotWrite(aPtr);
|
||||
mTable[idx] = aPtr;
|
||||
mEntries++;
|
||||
|
||||
if ((halnf)mEntries / mSize > 2.f / 3.f) {
|
||||
resize();
|
||||
}
|
||||
}
|
||||
|
||||
void CallStackSnapshots::SnapshotsDict::resize() {
|
||||
alni nslots_old = mSize;
|
||||
auto table_old = mTable;
|
||||
|
||||
mSize *= 2;
|
||||
mTable = (StackShapshot*)malloc(sizeof(StackShapshot) * mSize);
|
||||
memsetv(mTable, sizeof(StackShapshot) * mSize, 0);
|
||||
mEntries = 0;
|
||||
|
||||
for (alni i = 0; i < nslots_old; i++) {
|
||||
if (!table_old[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
alni idx = findSlotWrite(table_old[i]);
|
||||
mTable[idx] = table_old[i];
|
||||
mEntries++;
|
||||
}
|
||||
|
||||
delete[] table_old;
|
||||
}
|
||||
|
||||
alni CallStackSnapshots::SnapshotsDict::findSlotRead(StackShapshot key) {
|
||||
ualni const hased_key = hash(key);
|
||||
ualni const mask = mSize - 1;
|
||||
ualni const shift = (hased_key >> 5) & ~1;
|
||||
alni idx = hased_key & mask;
|
||||
NEXT:
|
||||
if (!mTable[idx]) {
|
||||
return -1;
|
||||
}
|
||||
if (compare(mTable[idx], key)) {
|
||||
return idx;
|
||||
}
|
||||
SKIP:
|
||||
idx = ((5 * idx) + 1 + shift) & mask;
|
||||
goto NEXT;
|
||||
}
|
||||
|
||||
ualni CallStackSnapshots::SnapshotsDict::findSlotWrite(StackShapshot key) {
|
||||
ualni const hased_key = hash(key);
|
||||
ualni const mask = mSize - 1;
|
||||
ualni const shift = (hased_key >> 5) & ~1;
|
||||
ualni idx = hased_key & mask;
|
||||
NEXT:
|
||||
if (!mTable[idx]) {
|
||||
return idx;
|
||||
}
|
||||
idx = ((5 * idx) + 1 + shift) & mask;
|
||||
goto NEXT;
|
||||
}
|
||||
|
||||
CallStackSnapshots::StackShapshot CallStackSnapshots::SnapshotsDict::newStackSnapshot(ualni aDepth) {
|
||||
auto const size = sizeof(FramePointer) * (aDepth + 1);
|
||||
auto out = (StackShapshot*)malloc(size);
|
||||
memsetv(out, size, 0);
|
||||
}
|
||||
|
||||
ualni CallStackSnapshots::SnapshotsDict::hash(StackShapshot snapshot) {
|
||||
ualni out = 0;
|
||||
for (FramePointer* iter = snapshot; iter; iter++) { out += *iter; }
|
||||
return out;
|
||||
}
|
||||
|
||||
bool CallStackSnapshots::SnapshotsDict::compare(StackShapshot left, StackShapshot right) {
|
||||
FramePointer* iter_left = left;
|
||||
FramePointer* iter_right = right;
|
||||
do {
|
||||
if (*iter_left != *iter_right) {
|
||||
return false;
|
||||
}
|
||||
iter_left++;
|
||||
iter_right++;
|
||||
} while (iter_left && iter_right);
|
||||
if (*iter_left != *iter_right) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CallStackSnapshots::SnapshotsDict::~SnapshotsDict() {
|
||||
for (ualni idx = 0; idx < mSize; idx++) {
|
||||
if (mTable[idx]) {
|
||||
free(mTable[idx]);
|
||||
}
|
||||
}
|
||||
free(mTable);
|
||||
}
|
||||
|
||||
// ----------------------- CallStackSnapshots ----------------------- //
|
||||
|
||||
CallStackSnapshots::StackShapshot CallStackSnapshots::getStack(ualni& len) {
|
||||
enum { MAX_DEPTH = MEM_STACK_TRACE_MAX_DEPTH };
|
||||
static FramePointer pointers[MAX_DEPTH];
|
||||
len = 0;
|
||||
|
||||
CONTEXT context;
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
STACKFRAME64 stackFrame;
|
||||
ZeroMemory(&stackFrame, sizeof(STACKFRAME64));
|
||||
stackFrame.AddrPC.Mode = AddrModeFlat;
|
||||
stackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||
stackFrame.AddrStack.Mode = AddrModeFlat;
|
||||
stackFrame.AddrPC.Offset = context.Rip;
|
||||
stackFrame.AddrFrame.Offset = context.Rbp;
|
||||
stackFrame.AddrStack.Offset = context.Rsp;
|
||||
|
||||
HANDLE processHandle = GetCurrentProcess();
|
||||
HANDLE threadHandle = GetCurrentThread();
|
||||
|
||||
while (
|
||||
len < MAX_DEPTH &&
|
||||
StackWalk64(IMAGE_FILE_MACHINE_AMD64, processHandle, threadHandle, &stackFrame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)
|
||||
)
|
||||
{
|
||||
pointers[len] = stackFrame.AddrFrame.Offset;
|
||||
len++;
|
||||
}
|
||||
|
||||
return pointers;
|
||||
}
|
||||
|
||||
CallStackSnapshots::StackShapshot CallStackSnapshots::capture() {
|
||||
ualni len;
|
||||
auto stack = getStack(len);
|
||||
auto idx = mSnapshots.presents(stack);
|
||||
|
||||
if (idx == -1) {
|
||||
auto new_snapshot = mSnapshots.newStackSnapshot(len + 1);
|
||||
memcp(new_snapshot, stack, len * sizeof(FramePointer));
|
||||
new_snapshot[len] = 0;
|
||||
mSnapshots.put(new_snapshot);
|
||||
return new_snapshot;
|
||||
}
|
||||
|
||||
return mSnapshots.getSlot(idx);
|
||||
}
|
||||
|
||||
void CallStackSnapshots::saveToFile(StackShapshot* snapshots, const char* filepath) {
|
||||
|
||||
}
|
||||
|
||||
CallStackSnapshots::StackShapshot* CallStackSnapshots::loadFromFile(const char* filepath) {
|
||||
|
||||
}
|
||||
43
private/StackTrace.hpp
Normal file
43
private/StackTrace.hpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
namespace tp {
|
||||
|
||||
struct CallStackSnapshots {
|
||||
|
||||
typedef alni FramePointer;
|
||||
typedef FramePointer* StackShapshot; // NULL Terminated Frames
|
||||
|
||||
StackShapshot capture();
|
||||
|
||||
void saveToFile(StackShapshot* snapshots, const char* filepath);
|
||||
StackShapshot* loadFromFile(const char* filepath);
|
||||
|
||||
private:
|
||||
|
||||
struct SnapshotsDict {
|
||||
SnapshotsDict();
|
||||
StackShapshot newStackSnapshot(ualni aDepth);
|
||||
void put(StackShapshot aPtr);
|
||||
StackShapshot getSlot(ualni aIdx);
|
||||
alni presents(StackShapshot aPtr);
|
||||
~SnapshotsDict();
|
||||
|
||||
private:
|
||||
StackShapshot* mTable = NULL;
|
||||
uhalni mSize = 512;
|
||||
uhalni mEntries = 0;
|
||||
|
||||
ualni hash(StackShapshot snapshot);
|
||||
bool compare(StackShapshot left, StackShapshot right);
|
||||
alni findSlotRead(StackShapshot key);
|
||||
ualni findSlotWrite(StackShapshot key);
|
||||
void resize();
|
||||
} mSnapshots;
|
||||
|
||||
StackShapshot getStack(ualni& len);
|
||||
};
|
||||
|
||||
extern CallStackSnapshots gCallStackSnapshots;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue