Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d0a4676ab |
34 changed files with 3847 additions and 0 deletions
42
Allocators/outdated/README.md
Normal file
42
Allocators/outdated/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Profiling
|
||||
|
||||
## Memory Leaks
|
||||
|
||||
Example program with memory leaks:
|
||||
|
||||
```c++
|
||||
#include "allocators.h"
|
||||
|
||||
void test_call22() { new int; }
|
||||
void test_call21() { new float; }
|
||||
|
||||
void test_call11() {
|
||||
test_call21();
|
||||
test_call22();
|
||||
}
|
||||
|
||||
int main(char argc, char* argv[]) {
|
||||
tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleAllocator, NULL };
|
||||
tp::ModuleManifest TestModule("Test", NULL, NULL, ModuleDependencies);
|
||||
TestModule.initialize();
|
||||
|
||||
test_call11();
|
||||
|
||||
TestModule.deinitialize();
|
||||
}
|
||||
|
||||
```
|
||||
If memory leaks were detected it will be loged in the output console.
|
||||
|
||||

|
||||
|
||||
Also debug.memleaks file will be generated in the working directory that can be viewved with MemLeaks Viewver.
|
||||
|
||||

|
||||
|
||||
|
||||
## Memory Usage Analisys
|
||||
Currently outdated
|
||||
|
||||
## Benchmarks
|
||||
Currently outdated
|
||||
26
Allocators/outdated/inc/Allocators.hpp
Normal file
26
Allocators/outdated/inc/Allocators.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "HeapAllocator.hpp"
|
||||
#include "ChunkAllocator.hpp"
|
||||
#include "PoolAllocator.hpp"
|
||||
|
||||
namespace tp {
|
||||
extern ModuleManifest gModuleAllocator;
|
||||
};
|
||||
|
||||
inline void* operator new(size_t aSize, void* aWhere) noexcept { return aWhere; }
|
||||
|
||||
void* operator new(size_t aSize);
|
||||
void* operator new[](size_t _Size);
|
||||
void operator delete(void* aPtr);
|
||||
void operator delete[](void* aPtr);
|
||||
|
||||
void* operator new(size_t aSize, tp::HeapAlloc& aAlloc);
|
||||
void* operator new[](size_t _Size, tp::HeapAlloc& aAlloc);
|
||||
void operator delete(void* aPtr, tp::HeapAlloc& aAlloc);
|
||||
void operator delete[](void* aPtr, tp::HeapAlloc& aAlloc);
|
||||
|
||||
void* operator new(size_t aSize, tp::HeapAllocGlobal& aAlloc);
|
||||
void* operator new[](size_t _Size, tp::HeapAllocGlobal& aAlloc);
|
||||
void operator delete(void* aPtr, tp::HeapAllocGlobal& aAlloc);
|
||||
void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc);
|
||||
33
Allocators/outdated/inc/ChunkAllocator.hpp
Normal file
33
Allocators/outdated/inc/ChunkAllocator.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "PublicConfig.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
// Chunk Allocator
|
||||
// Constant time allocations and deallocations in any order.
|
||||
// Memory blocks are fixed in size and number of blocks can not exceed given parameter.
|
||||
struct ChunkAlloc {
|
||||
|
||||
ChunkAlloc(ualni aBlockSize, void* aMemory, ualni aMemSize);
|
||||
ChunkAlloc(ualni aBlockSize, ualni aNBlocks);
|
||||
|
||||
void* allocate();
|
||||
void deallocate(void* aPtr);
|
||||
bool isFull();
|
||||
bool isEmpty();
|
||||
|
||||
~ChunkAlloc();
|
||||
|
||||
private:
|
||||
ualni mBSize; // Size of data in aligned units
|
||||
ualni mNBlocks;
|
||||
|
||||
ualni* mBuff;
|
||||
ualni* mNextBlock = 0;
|
||||
ualni mNFreeBlocks;
|
||||
ualni mNInitBlocks = 0;
|
||||
bool mOwnBuff = false;
|
||||
};
|
||||
};
|
||||
20
Allocators/outdated/inc/HeapAllocator.hpp
Normal file
20
Allocators/outdated/inc/HeapAllocator.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "HeapAllocatorGlobal.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
struct HeapAlloc {
|
||||
|
||||
#ifdef MEM_DEBUG
|
||||
HeapAllocGlobal mAlloc;
|
||||
|
||||
ualni mNumAllocations = 0;
|
||||
struct MemHeadLocal* mEntry = NULL;
|
||||
#endif
|
||||
|
||||
void* allocate(ualni aBlockSize);
|
||||
void deallocate(void* aPtr);
|
||||
~HeapAlloc();
|
||||
};
|
||||
};
|
||||
19
Allocators/outdated/inc/HeapAllocatorGlobal.hpp
Normal file
19
Allocators/outdated/inc/HeapAllocatorGlobal.hpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "PublicConfig.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
struct HeapAllocGlobal {
|
||||
|
||||
#ifdef MEM_DEBUG
|
||||
static ualni mNumAllocations;
|
||||
static struct MemHead* mEntry;
|
||||
#endif
|
||||
|
||||
static void* allocate(ualni aBlockSize);
|
||||
static void deallocate(void* aPtr);
|
||||
~HeapAllocGlobal();
|
||||
};
|
||||
};
|
||||
13
Allocators/outdated/inc/PickAllocator.hpp
Normal file
13
Allocators/outdated/inc/PickAllocator.hpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "HeapAllocatorGlobal.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
struct PickAlloc {
|
||||
PickAlloc();
|
||||
void* allocate(ualni aBlockSize);
|
||||
void deallocate(void* aPtr);
|
||||
~PickAlloc();
|
||||
};
|
||||
};
|
||||
42
Allocators/outdated/inc/PoolAllocator.hpp
Normal file
42
Allocators/outdated/inc/PoolAllocator.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include "ChunkAllocator.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
// Pool Allocator
|
||||
// Overcomes chunk allocator fixed number of max allocations
|
||||
struct PoolAlloc {
|
||||
|
||||
PoolAlloc(ualni aBlockSize, ualni aChunkSize);
|
||||
void* allocate();
|
||||
void deallocate(void* aPtr);
|
||||
~PoolAlloc();
|
||||
|
||||
private:
|
||||
|
||||
struct Chunk : public ChunkAlloc {
|
||||
Chunk(ualni aBlockSize, ualni aChunlSize) : ChunkAlloc(aBlockSize, aChunlSize) {}
|
||||
Chunk* mNext = NULL;
|
||||
Chunk* mPrev = NULL;
|
||||
};
|
||||
|
||||
struct Chunks {
|
||||
void add(Chunk*);
|
||||
void remove(Chunk*);
|
||||
Chunk* find(void* aPtr);
|
||||
Chunk* findNotFull();
|
||||
Chunk** mBuff = NULL;
|
||||
ualni mUsedLen = 0;
|
||||
ualni mLen = 0;
|
||||
|
||||
private:
|
||||
Chunk** findUtil(Chunk** aLeft, Chunk** aRight, void* aPtr);
|
||||
};
|
||||
|
||||
Chunks mChunks;
|
||||
Chunk* mFreeChunk = NULL;
|
||||
ualni mBlockSize;
|
||||
ualni mChunkSize;
|
||||
};
|
||||
};
|
||||
12
Allocators/outdated/inc/PrivateConfig.hpp
Normal file
12
Allocators/outdated/inc/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
|
||||
3
Allocators/outdated/inc/PublicConfig.hpp
Normal file
3
Allocators/outdated/inc/PublicConfig.hpp
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
#define MEM_DEBUG // Memory Debugging
|
||||
43
Allocators/outdated/inc/StackTrace.hpp
Normal file
43
Allocators/outdated/inc/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;
|
||||
};
|
||||
25
Allocators/outdated/src/Allocators.cpp
Normal file
25
Allocators/outdated/src/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
Allocators/outdated/src/ChunkAllocator.cpp
Normal file
113
Allocators/outdated/src/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
Allocators/outdated/src/HeapAllocator.cpp
Normal file
72
Allocators/outdated/src/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
Allocators/outdated/src/HeapAllocatorGlobal.cpp
Normal file
146
Allocators/outdated/src/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
Allocators/outdated/src/PickAllocator.cpp
Normal file
1
Allocators/outdated/src/PickAllocator.cpp
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
132
Allocators/outdated/src/PoolAllocator.cpp
Normal file
132
Allocators/outdated/src/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() {
|
||||
}
|
||||
186
Allocators/outdated/src/StackTrace.cpp
Normal file
186
Allocators/outdated/src/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) {
|
||||
|
||||
}
|
||||
291
Allocators/outdated/tests/alloctests.cpp
Normal file
291
Allocators/outdated/tests/alloctests.cpp
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "allocators.h"
|
||||
|
||||
struct test_struct {
|
||||
tp::alni val = 0;
|
||||
|
||||
test_struct() { val = 1; }
|
||||
~test_struct() { val = -1; }
|
||||
|
||||
bool operator==(const test_struct& in) { return in.val == val; }
|
||||
};
|
||||
|
||||
template <tp::alni size>
|
||||
struct allocator_test {
|
||||
test_struct data[size];
|
||||
bool is_allocated[size];
|
||||
tp::alni n_loaded = 0;
|
||||
|
||||
test_struct* allocations[size];
|
||||
|
||||
tp::AbstractAllocator* alloc;
|
||||
tp::AbstractAllocator* parent_alloc;
|
||||
const char* allocator_name = NULL;
|
||||
|
||||
tp::alni rand_idx(bool state) {
|
||||
RAND:
|
||||
|
||||
tp::alni idx = (tp::alni)(tp::randf() * (size + 1));
|
||||
CLAMP(idx, 0, size - 1);
|
||||
|
||||
if (state == is_allocated[idx]) {
|
||||
goto RAND;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
allocator_test(tp::AbstractAllocator* palloc, const char* pallocator_name,
|
||||
tp::AbstractAllocator* p_parent_alloc) {
|
||||
allocator_name = pallocator_name;
|
||||
parent_alloc = p_parent_alloc;
|
||||
alloc = palloc;
|
||||
for (tp::alni i = 0; i < size; i++) {
|
||||
RAND:
|
||||
tp::alni val = tp::alni(tp::randf() * (size + 100.f));
|
||||
for (tp::alni check_idx = 0; check_idx < size; check_idx++) {
|
||||
if (data[check_idx].val == val) {
|
||||
goto RAND;
|
||||
}
|
||||
}
|
||||
|
||||
data[i].val = val;
|
||||
is_allocated[i] = false;
|
||||
allocations[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void verify_integrity() {
|
||||
// verify data integrity
|
||||
for (tp::alni i = 0; i < size; i++) {
|
||||
if (is_allocated[i]) {
|
||||
assert(*allocations[i] == data[i] && "data is currupted\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (alloc->isWrapSupport()) assert(!alloc->isWrapCorrupted());
|
||||
if (parent_alloc && parent_alloc->isWrapSupport())
|
||||
assert(!parent_alloc->isWrapCorrupted());
|
||||
|
||||
verify_sizes();
|
||||
}
|
||||
|
||||
void verify_sizes() {
|
||||
return;
|
||||
#ifdef MEM_TRACE
|
||||
assert(alloc->sizeInuse() == n_loaded * sizeof(test_struct) &&
|
||||
"invalid inuse size\n");
|
||||
assert(alloc->sizeReserved() >= n_loaded * (tp::alni)sizeof(test_struct) &&
|
||||
"invalid reserved size\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void load_item(tp::alni idx) {
|
||||
if (!is_allocated[idx]) {
|
||||
allocations[idx] = new (alloc) test_struct();
|
||||
|
||||
assert(allocations[idx] && "allocator returned NULL");
|
||||
|
||||
allocations[idx]->val = data[idx].val;
|
||||
is_allocated[idx] = true;
|
||||
n_loaded++;
|
||||
verify_integrity();
|
||||
}
|
||||
}
|
||||
|
||||
void unload_item(tp::alni idx) {
|
||||
if (is_allocated[idx]) {
|
||||
verify_integrity();
|
||||
delete allocations[idx];
|
||||
is_allocated[idx] = false;
|
||||
n_loaded--;
|
||||
verify_integrity();
|
||||
}
|
||||
}
|
||||
|
||||
void change_states(tp::Range<tp::alni> rg, bool state, bool reversed = false,
|
||||
bool random = false) {
|
||||
for (auto i : rg) {
|
||||
tp::alni idx = i;
|
||||
|
||||
if (random) {
|
||||
idx = rand_idx(state);
|
||||
}
|
||||
else if (reversed) {
|
||||
idx = size - i - 1;
|
||||
}
|
||||
|
||||
(state) ? load_item(idx) : unload_item(idx);
|
||||
}
|
||||
}
|
||||
|
||||
// full down-up load then up-down unload
|
||||
void test1() {
|
||||
change_states({ 0, size }, 1);
|
||||
change_states({ 0, size }, 0, true);
|
||||
}
|
||||
|
||||
// full down-up load then down-up unload
|
||||
void test2() {
|
||||
change_states({ 0, size }, 1);
|
||||
change_states({ 0, size }, 0);
|
||||
}
|
||||
|
||||
// full random load then random unload
|
||||
void test3() {
|
||||
change_states({ 0, size }, 1, 0, 1);
|
||||
change_states({ 0, size }, 0, 0, 1);
|
||||
}
|
||||
|
||||
// multipul tests 1-3
|
||||
void test4() {
|
||||
test1();
|
||||
test1();
|
||||
|
||||
test2();
|
||||
test2();
|
||||
|
||||
test3();
|
||||
test3();
|
||||
}
|
||||
|
||||
static tp::alnf sineupf(tp::alnf asize, tp::alnf x, bool reverse) {
|
||||
tp::alnf end = 4 * PI;
|
||||
tp::alnf a = (2 / 7.f) * asize;
|
||||
tp::alnf b = end / asize;
|
||||
|
||||
tp::alni c = ((-1 * reverse) + (1 * !reverse));
|
||||
tp::alnf c1 = (x - (end * reverse)) / b;
|
||||
tp::alnf c2 = (a * sin(x - (end * reverse)));
|
||||
tp::alnf out = c1 + c2;
|
||||
return c * out;
|
||||
}
|
||||
|
||||
// sin load & sin unload with ~1/2 drop factor
|
||||
void test5() {
|
||||
tp::alnf end = 4 * PI;
|
||||
tp::alnf step = end / 4.f;
|
||||
|
||||
for (char i = 0; i < 2; i++) {
|
||||
for (tp::alnf x = 0; x <= end; x += step) {
|
||||
tp::alni target_alloc_count = (tp::alni)ceil(sineupf(size, x, i));
|
||||
CLAMP(target_alloc_count, 0, size);
|
||||
|
||||
while (n_loaded > target_alloc_count) {
|
||||
unload_item(rand_idx(0));
|
||||
}
|
||||
while (n_loaded < target_alloc_count) {
|
||||
load_item(rand_idx(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MEM_WRAP
|
||||
void check_wrap(tp::alni offset, bool after) {
|
||||
CLAMP(offset, 1, WRAP_LEN);
|
||||
|
||||
test_struct* ts = allocations[rand_idx(0)];
|
||||
tp::alni shift = (sizeof(test_struct) * after) + (offset - 1) * after -
|
||||
offset * (!after);
|
||||
tp::uint1* address = (((tp::uint1*)ts) + shift);
|
||||
|
||||
tp::uint1 val = *address;
|
||||
*address = 5;
|
||||
assert(alloc->isWrapCorrupted());
|
||||
*address = val;
|
||||
}
|
||||
#endif
|
||||
|
||||
// mem guards test
|
||||
void test6() {
|
||||
change_states({ 0, size }, 1);
|
||||
|
||||
#ifdef MEM_WRAP
|
||||
for (tp::alni after = 0; after < 2; after++) {
|
||||
for (tp::alni offset = 1; offset <= WRAP_LEN; offset++) {
|
||||
check_wrap(offset, after);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
change_states({ 0, size }, 0);
|
||||
}
|
||||
|
||||
void run_tests() {
|
||||
try {
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
test5();
|
||||
if (alloc->isWrapSupport()) {
|
||||
test6();
|
||||
}
|
||||
|
||||
printf("%s - passed\n", allocator_name);
|
||||
if (!alloc->isWrapSupport()) {
|
||||
printf(" WARNING: %s has no wrap support!! \n", allocator_name);
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
printf("%s - failed\n", allocator_name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void heap_alloc_test() {
|
||||
tp::alloc_init(); {
|
||||
try {
|
||||
allocator_test<150> hatest(tp::heapalloc, "heap allocator", NULL);
|
||||
hatest.run_tests();
|
||||
}
|
||||
catch (...) {
|
||||
printf("heap alloc failed\n");
|
||||
}
|
||||
} tp::alloc_uninit();
|
||||
}
|
||||
|
||||
void chunk_alloc_test() {
|
||||
tp::alloc_init(); {
|
||||
try {
|
||||
tp::ChunkAlloc calloc(sizeof(test_struct), 50);
|
||||
allocator_test<50> ca_test(&calloc, "chunk allocator", tp::heapalloc);
|
||||
ca_test.run_tests();
|
||||
}
|
||||
catch (...) {
|
||||
printf("chunk alloc failed\n");
|
||||
}
|
||||
} tp::alloc_uninit();
|
||||
}
|
||||
|
||||
void pool_alloc_test() {
|
||||
tp::alloc_init(); {
|
||||
try {
|
||||
tp::PoolAlloc palloc(sizeof(test_struct), 50);
|
||||
allocator_test<150> pa_test(&palloc, "pool allocator", tp::heapalloc);
|
||||
pa_test.run_tests();
|
||||
}
|
||||
catch (...) {
|
||||
printf("chunk alloc failed\n");
|
||||
}
|
||||
} tp::alloc_uninit();
|
||||
}
|
||||
|
||||
void allocators_test() {
|
||||
|
||||
printf("running tests on alocators:\n");
|
||||
|
||||
heap_alloc_test();
|
||||
chunk_alloc_test();
|
||||
pool_alloc_test();
|
||||
}
|
||||
|
||||
|
||||
CROSSPLATFORM_MAIN;
|
||||
int main(int argc, char* argv[]) {
|
||||
tp::print_env_info();
|
||||
allocators_test();
|
||||
}
|
||||
3
Allocators/outdated/tests/alloctests.h
Normal file
3
Allocators/outdated/tests/alloctests.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
#pragma once
|
||||
void allocators_test();
|
||||
18
Allocators/outdated/tests/test.cpp
Normal file
18
Allocators/outdated/tests/test.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
#include "allocators.hpp"
|
||||
|
||||
int main() {
|
||||
|
||||
tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleAllocator, NULL };
|
||||
tp::ModuleManifest TestModule("Test", NULL, NULL, ModuleDependencies);
|
||||
if (!TestModule.initialize()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
tp::HeapAllocGlobal alloc;
|
||||
|
||||
int* val = new(alloc) int();
|
||||
delete(alloc, val);
|
||||
|
||||
TestModule.deinitialize();
|
||||
}
|
||||
172
Allocators/outdated/tools/MemBenchmark/benchmark_simple.cpp
Normal file
172
Allocators/outdated/tools/MemBenchmark/benchmark_simple.cpp
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
|
||||
|
||||
#include "allocators.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "timer.h"
|
||||
|
||||
struct AllocSpeedTets {
|
||||
const char* test_desc = NULL;
|
||||
virtual void exec() {};
|
||||
};
|
||||
|
||||
struct Test1 : AllocSpeedTets {
|
||||
Test1() {
|
||||
test_desc = "allocate lots of memory of same sizes, then free it all";
|
||||
}
|
||||
|
||||
const int len = 1000;
|
||||
int size = 100;
|
||||
void* buff[1000];
|
||||
|
||||
void exec_util(tp::AbstractAllocator* alloc) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
buff[i] = alloc->Alloc(size);
|
||||
}
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
alloc->Free(buff[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void exec() {
|
||||
tp::time_ms start, end;
|
||||
|
||||
tp::HeapAlloc malloc;
|
||||
tp::PoolAlloc pool(0, 0);
|
||||
tp::ChunkAlloc chunck(0, 0);
|
||||
tp::PickAlloc pick;
|
||||
|
||||
start = tp::get_time();
|
||||
|
||||
end = tp::get_time();
|
||||
printf("Malloc : %lli ms \n", end - start);
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Pool : %lli ms \n", end - start);
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Chunk : %lli ms \n", end - start);
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Pick : %lli ms \n", end - start);
|
||||
}
|
||||
};
|
||||
|
||||
struct Test2 : AllocSpeedTets {
|
||||
Test2() {
|
||||
test_desc = "allocate lots of memory of different sizes, then free it all";
|
||||
}
|
||||
|
||||
void exec() {
|
||||
tp::time_ms start, end;
|
||||
|
||||
tp::HeapAlloc malloc;
|
||||
tp::PoolAlloc pool(0, 0);
|
||||
tp::ChunkAlloc chunck(0, 0);
|
||||
tp::PickAlloc pick;
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Malloc : %lli ms \n", end - start);
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Pool : %lli ms \n", end - start);
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Chunk : %lli ms \n", end - start);
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Pick : %lli ms \n", end - start);
|
||||
}
|
||||
};
|
||||
|
||||
struct Test3 : AllocSpeedTets {
|
||||
Test3() {
|
||||
test_desc = "allocate only a few blocks of memory, free them, and repeat this loop several times \
|
||||
\n (repeat for same - sized blocks and different - sized blocks)\n";
|
||||
}
|
||||
|
||||
void exec() {
|
||||
tp::time_ms start, end;
|
||||
|
||||
tp::HeapAlloc malloc;
|
||||
tp::PoolAlloc pool(0, 0);
|
||||
tp::ChunkAlloc chunck(0, 0);
|
||||
tp::PickAlloc pick;
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Malloc : %lli ms \n", end - start);
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Pool : %lli ms \n", end - start);
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Chunk : %lli ms \n", end - start);
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Pick : %lli ms \n", end - start);
|
||||
}
|
||||
};
|
||||
|
||||
struct Test4 : AllocSpeedTets {
|
||||
Test4() {
|
||||
test_desc = "allocate lots of memory of different sizes, free half of it(e.g.the even allocations), then allocateand free memory in a loop\n";
|
||||
}
|
||||
|
||||
void exec() {
|
||||
tp::time_ms start, end;
|
||||
|
||||
tp::HeapAlloc malloc;
|
||||
tp::PoolAlloc pool(0, 0);
|
||||
tp::ChunkAlloc chunck(0, 0);
|
||||
tp::PickAlloc pick;
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Malloc : %lli ms \n", end - start);
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Pool : %lli ms \n", end - start);
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Chunk : %lli ms \n", end - start);
|
||||
|
||||
start = tp::get_time();
|
||||
end = tp::get_time();
|
||||
printf("Pick : %lli ms \n", end - start);
|
||||
}
|
||||
};
|
||||
|
||||
void banckmark() {
|
||||
|
||||
tp::alloc_init();
|
||||
|
||||
const int tests_len = 1;
|
||||
AllocSpeedTets* tests[] = {
|
||||
new Test1(),
|
||||
new Test2(),
|
||||
new Test3(),
|
||||
new Test4(),
|
||||
};
|
||||
|
||||
for (int i = 0; i < tests_len; i++) {
|
||||
printf("Test %i: \n %s\n", i, tests[i]->test_desc);
|
||||
tests[i]->exec();
|
||||
|
||||
delete tests[i];
|
||||
}
|
||||
|
||||
tp::alloc_uninit();
|
||||
}
|
||||
563
Allocators/outdated/tools/MemBenchmark/benchmarker.cpp
Normal file
563
Allocators/outdated/tools/MemBenchmark/benchmarker.cpp
Normal file
|
|
@ -0,0 +1,563 @@
|
|||
|
||||
#include "benchmarker.h"
|
||||
|
||||
#include "implot.h"
|
||||
#include "ImGuiUtils.h"
|
||||
|
||||
tp::alni hash(const tp::string& val) {
|
||||
return tp::hash(val.cstr());
|
||||
}
|
||||
|
||||
config glb_cfg = {
|
||||
|
||||
.live_update = false,
|
||||
|
||||
.heap = true,
|
||||
.pool = true,
|
||||
.chunk = true,
|
||||
|
||||
.current_sizing_pattern = 0,
|
||||
.current_loading_pattern = 0,
|
||||
.current_ordering_pattern = 0,
|
||||
|
||||
.update = 0,
|
||||
|
||||
.time_per_instruction = true,
|
||||
.mem_per_instruction = true,
|
||||
.avreging = 1,
|
||||
|
||||
.chunk_bsize = 100,
|
||||
.chunk_blen = 100,
|
||||
|
||||
.pool_bsize = 100,
|
||||
.pool_blen = 100,
|
||||
};
|
||||
|
||||
benchmarker::benchmarker() /*: ImGui::CompleteApp()*/ {
|
||||
|
||||
i_count = 0;
|
||||
pattern_out = NULL;
|
||||
out.reserve(3);
|
||||
|
||||
patterns.put("constant", new const_pattern());
|
||||
patterns.put("linear", new linear_pattern());
|
||||
patterns.put("random", new random_pattern());
|
||||
}
|
||||
|
||||
void benchmarker::output_draw() {
|
||||
if (ImGui::Begin("Overview")) {
|
||||
|
||||
if (is_output) {
|
||||
if (ImGui::TreeNode("total time")) {
|
||||
for (auto& i : tp::Range(0, 3)) {
|
||||
if (out[i]) {
|
||||
if (out[i]->total_time < 1000) {
|
||||
ImGui::Text("%s : %f 10e-9", out[i]->alloc_type, out[i]->total_time);
|
||||
} else if (out[i]->total_time < 1000000) {
|
||||
ImGui::Text("%s : %f 10e-6", out[i]->alloc_type, out[i]->total_time / 1000.f);
|
||||
} else if (out[i]->total_time < 1000000000) {
|
||||
ImGui::Text("%s : %f 10e-3", out[i]->alloc_type, out[i]->total_time / 1000000.f);
|
||||
} else {
|
||||
ImGui::Text("%s : %f ", out[i]->alloc_type, out[i]->total_time / 1000000000.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("failures")) {
|
||||
if ((out[0] && out[0]->failed) || (out[1] && out[1]->failed) || (out[2] && out[2]->failed)) {
|
||||
for (auto& i : tp::Range(0, 3)) {
|
||||
if (out[i] && out[i]->failed)
|
||||
ImGui::Text("%s failed", out[i]->alloc_type);
|
||||
}
|
||||
} else {
|
||||
ImGui::Text("all succeeded");
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
|
||||
if (cfg.time_per_instruction) {
|
||||
if (ImGui::Begin("Graphs")) {
|
||||
if (is_output) {
|
||||
if (ImPlot::BeginPlot("Time(ns) vs Alloc / Free inst")) {
|
||||
if (cfg.heap) ImPlot::PlotLine("halloc", x_axis, out[0]->time.buff(), (int) i_count);
|
||||
if (cfg.pool) ImPlot::PlotLine("pool", x_axis, out[1]->time.buff(), (int) i_count);
|
||||
if (cfg.chunk) ImPlot::PlotLine("chunk", x_axis, out[2]->time.buff(), (int) i_count);
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (cfg.mem_per_instruction) {
|
||||
if (ImGui::Begin("Graphs")) {
|
||||
if (is_output) {
|
||||
if (ImPlot::BeginPlot("Memory (bytes) at Instruction state")) {
|
||||
if (cfg.heap) ImPlot::PlotLine("halloc", x_axis, out[0]->mem.buff(), (int) i_count);
|
||||
if (cfg.pool) ImPlot::PlotLine("pool", x_axis, out[1]->mem.buff(), (int) i_count);
|
||||
if (cfg.chunk) ImPlot::PlotLine("chunk", x_axis, out[2]->mem.buff(), (int) i_count);
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (ImGui::Begin("Pattern")) {
|
||||
if (is_output) {
|
||||
if (ImPlot::BeginPlot("Alloc/Free instruction info vs instruction idx")) {
|
||||
if (cfg.current_sizing_pattern) ImPlot::PlotLine("allocated item size", x_axis, pattern_out->alloc_size.buff(), (int) i_count);
|
||||
if (cfg.current_ordering_pattern) ImPlot::PlotLine("used data item idx", x_axis, pattern_out->data_idx.buff(), (int) i_count);
|
||||
if (cfg.current_loading_pattern) ImPlot::PlotLine("total items allocated", x_axis, pattern_out->items_loaded.buff(), (int) i_count);
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
benchmarker::~benchmarker() {
|
||||
for (auto iter : patterns) {
|
||||
delete iter->val;
|
||||
}
|
||||
clear_out();
|
||||
}
|
||||
|
||||
void benchmarker::select_pattern() {
|
||||
if (patterns.size()) {
|
||||
const char** pattern_names = new const char* [patterns.size()];
|
||||
|
||||
const char* current_opattern_name = NULL;
|
||||
const char* current_lpattern_name = NULL;
|
||||
const char* current_spattern_name = NULL;
|
||||
|
||||
for (auto pattern_name : patterns) {
|
||||
pattern_names[pattern_name.entry_idx] = pattern_name->key.cstr();
|
||||
if (pattern_name->val == glb_cfg.current_ordering_pattern)
|
||||
current_opattern_name = pattern_name->key.cstr();
|
||||
if (pattern_name->val == glb_cfg.current_sizing_pattern)
|
||||
current_spattern_name = pattern_name->key.cstr();
|
||||
if (pattern_name->val == glb_cfg.current_loading_pattern)
|
||||
current_lpattern_name = pattern_name->key.cstr();
|
||||
}
|
||||
|
||||
if (ImGui::BeginCombo("Ordering", current_opattern_name)) {
|
||||
for (int n = 0; n < patterns.size(); n++) {
|
||||
bool is_selected = (current_opattern_name == pattern_names[n]);
|
||||
if (ImGui::Selectable(pattern_names[n], is_selected)) {
|
||||
current_opattern_name = pattern_names[n];
|
||||
glb_cfg.current_ordering_pattern = patterns.get(pattern_names[n]);
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
if (ImGui::BeginCombo("Loading", current_lpattern_name)) {
|
||||
for (int n = 0; n < patterns.size(); n++) {
|
||||
bool is_selected = (current_lpattern_name == pattern_names[n]);
|
||||
if (ImGui::Selectable(pattern_names[n], is_selected)) {
|
||||
current_lpattern_name = pattern_names[n];
|
||||
glb_cfg.current_loading_pattern = patterns.get(pattern_names[n]);
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
if (ImGui::BeginCombo("Sizing", current_spattern_name)) {
|
||||
for (int n = 0; n < patterns.size(); n++) {
|
||||
bool is_selected = (current_spattern_name == pattern_names[n]);
|
||||
if (ImGui::Selectable(pattern_names[n], is_selected)) {
|
||||
current_spattern_name = pattern_names[n];
|
||||
glb_cfg.current_sizing_pattern = patterns.get(pattern_names[n]);
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
delete pattern_names;
|
||||
}
|
||||
}
|
||||
|
||||
void benchmarker::pattern_combo(const char*& current) {
|
||||
if (patterns.size()) {
|
||||
const char** pattern_names = new const char* [patterns.size()];
|
||||
|
||||
for (auto pattern_name : patterns) {
|
||||
pattern_names[pattern_name.entry_idx] = pattern_name->key.cstr();
|
||||
}
|
||||
|
||||
if (ImGui::BeginCombo("Patterns", current)) {
|
||||
for (int n = 0; n < patterns.size(); n++) {
|
||||
bool is_selected = (current == pattern_names[n]);
|
||||
if (ImGui::Selectable(pattern_names[n], is_selected)) {
|
||||
current = pattern_names[n];
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
delete pattern_names;
|
||||
}
|
||||
}
|
||||
|
||||
void benchmarker::pattern_generator() {
|
||||
|
||||
|
||||
static tp::alni selected_idx = -1;
|
||||
static pattern* child_pattern_active = NULL;
|
||||
|
||||
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.1f);
|
||||
|
||||
pattern* pattern_edit = NULL;
|
||||
if (ImGui::Begin("Pattern Generator")) {
|
||||
|
||||
if (ImGui::SubMenuBegin("Selector", 1)) {
|
||||
|
||||
const char* prev_pattern = pattern_generator_active;
|
||||
pattern_combo(pattern_generator_active);
|
||||
|
||||
child_pattern_active = (prev_pattern == pattern_generator_active) ? child_pattern_active : NULL;
|
||||
|
||||
if (pattern_generator_active) {
|
||||
tp::alni idx = patterns.presents(pattern_generator_active);
|
||||
if (!MAP_VALID_IDX(idx)) {
|
||||
pattern_edit = 0;
|
||||
pattern_generator_active = 0;
|
||||
} else {
|
||||
pattern_edit = patterns[idx];
|
||||
}
|
||||
}
|
||||
|
||||
//ImGui::Separator();
|
||||
|
||||
static char create_name[100] = {"new pattern name"};
|
||||
ImGui::InputText("pattern name", create_name, 100);
|
||||
|
||||
if (ImGui::Button("Create")) {
|
||||
if (!patterns.presents(create_name)) {
|
||||
pattern* new_pt = new pattern();
|
||||
new_pt->build_in = false;
|
||||
new_pt->pattern_name = create_name;
|
||||
tp::string id = create_name;
|
||||
id.capture();
|
||||
patterns.put(id, new_pt);
|
||||
} else {
|
||||
ImGui::Notify("Such Pattern Already Exists", 3);
|
||||
}
|
||||
}
|
||||
|
||||
if (pattern_edit) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Delete")) {
|
||||
if (pattern_edit->build_in) {
|
||||
ImGui::Notify("Cant Remove Built-in Patterns", 3);
|
||||
} else {
|
||||
patterns.remove(pattern_generator_active);
|
||||
delete pattern_edit;
|
||||
pattern_edit = NULL;
|
||||
pattern_generator_active = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Rename")) {
|
||||
if (pattern_edit->build_in) {
|
||||
ImGui::Notify("Cant Rename Built-in Patterns", 3);
|
||||
} else {
|
||||
patterns.remove(pattern_generator_active);
|
||||
patterns.put(create_name, pattern_edit);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::SubMenuEnd(1);
|
||||
}
|
||||
|
||||
if (pattern_generator_active) {
|
||||
tp::alni idx = patterns.presents(pattern_generator_active);
|
||||
if (!MAP_VALID_IDX(idx)) {
|
||||
pattern_edit = 0;
|
||||
pattern_generator_active = 0;
|
||||
} else {
|
||||
pattern_edit = patterns[idx];
|
||||
}
|
||||
}
|
||||
|
||||
if (!pattern_edit) {
|
||||
ImGui::Text("Select pattern to edit or create one");
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::SubMenuBegin("Preview", 1)) {
|
||||
static float preview_res_scale = 0.05f;
|
||||
int resolution = (int) (1000 * preview_res_scale);
|
||||
CLAMP(resolution, 3, 10000);
|
||||
float* x_axis = new float[resolution];
|
||||
float* y_axis = new float[resolution];
|
||||
|
||||
float x = 0;
|
||||
float step = 1.f / (resolution - 1);
|
||||
for (tp::alni idx = 0; idx < resolution; idx++) {
|
||||
x_axis[idx] = x;
|
||||
y_axis[idx] = (tp::flt4) pattern_edit->get_y(&patterns, x);
|
||||
x += step;
|
||||
}
|
||||
|
||||
|
||||
if (ImPlot::BeginPlot("Pattern")) {
|
||||
ImPlot::PlotLine("toggle", x_axis, y_axis, resolution);
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
|
||||
delete x_axis;
|
||||
delete y_axis;
|
||||
ImGui::SliderFloat("graph resolution", &preview_res_scale, 0.f, 1.f);
|
||||
ImGui::SubMenuEnd(1);
|
||||
}
|
||||
|
||||
if (ImGui::SubMenuBegin("Compositor", 1)) {
|
||||
|
||||
if (!pattern_edit->build_in) {
|
||||
|
||||
if (ImGui::SubMenuBegin("Child Patterns", 2)) {
|
||||
ImGui::BeginListBox("");
|
||||
for (tp::alni idx = 0; idx < pattern_edit->regions.length(); idx++) {
|
||||
ImGui::PushID((int) idx);
|
||||
if (ImGui::Button(pattern_edit->regions[idx].name.cstr())) {
|
||||
child_pattern_active = patterns.get(pattern_edit->regions[idx].name);
|
||||
selected_idx = idx;
|
||||
}
|
||||
if (selected_idx == idx) {
|
||||
ImGui::SameLine(); ImGui::Text(" - Active");
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
|
||||
if (selected_idx >= pattern_edit->regions.length()) {
|
||||
selected_idx = -1;
|
||||
}
|
||||
|
||||
static const char* append_pattern = NULL;
|
||||
bool add = ImGui::Button(" + ");
|
||||
ImGui::SameLine();
|
||||
pattern_combo(append_pattern);
|
||||
if (add) {
|
||||
if (append_pattern) {
|
||||
pattern_edit->regions.pushBack(child_pattern(append_pattern));
|
||||
} else {
|
||||
ImGui::Notify("Select a Pattern to Append");
|
||||
}
|
||||
}
|
||||
|
||||
if (child_pattern_active && selected_idx != -1) {
|
||||
|
||||
if (ImGui::Button(" Up ")) {
|
||||
if (selected_idx > 0) {
|
||||
tp::string tmp = pattern_edit->regions[selected_idx].name;
|
||||
pattern_edit->regions[selected_idx] = pattern_edit->regions[selected_idx - 1];
|
||||
pattern_edit->regions[selected_idx - 1] = tmp;
|
||||
selected_idx--;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Down")) {
|
||||
if (selected_idx < pattern_edit->regions.length() - 1) {
|
||||
tp::string tmp = pattern_edit->regions[selected_idx].name;
|
||||
pattern_edit->regions[selected_idx] = pattern_edit->regions[selected_idx + 1];
|
||||
pattern_edit->regions[selected_idx + 1] = tmp;
|
||||
selected_idx++;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Remove")) {
|
||||
pattern_edit->regions.remove(selected_idx);
|
||||
selected_idx = pattern_edit->regions.length() - 1;
|
||||
}
|
||||
} else {
|
||||
ImGui::Text("Select Child Pattern");
|
||||
}
|
||||
ImGui::SubMenuEnd(2);
|
||||
}
|
||||
|
||||
if (child_pattern_active && selected_idx != -1) {
|
||||
if (ImGui::SubMenuBegin("child Pattern Properties", 2)) {
|
||||
ImGui::SliderFloat("Point", &pattern_edit->regions[selected_idx].point, 0.f, 1.f);
|
||||
ImGui::SliderFloat("Lower lim", &pattern_edit->regions[selected_idx].lowerlim, 0.f, 1.f);
|
||||
ImGui::SliderFloat("Upper lim", &pattern_edit->regions[selected_idx].uppernlim, 0.f, 1.f);
|
||||
if (child_pattern_active->build_in) {
|
||||
}
|
||||
ImGui::SubMenuEnd(2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ImGui::Text("Can't Edit Built-In Patterns");
|
||||
}
|
||||
ImGui::SubMenuEnd(1);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void benchmarker::MainDrawTick() {
|
||||
|
||||
analize(glb_cfg);
|
||||
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
if (ImGui::WindowEditor("Properties")) {
|
||||
|
||||
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f);
|
||||
|
||||
{
|
||||
if (ImGui::Button("Run")) {
|
||||
glb_cfg.update = true;
|
||||
}
|
||||
|
||||
bool prev_val = glb_cfg.live_update;
|
||||
ImGui::SameLine(); ImGui::Checkbox("Live Update", &glb_cfg.live_update);
|
||||
if (prev_val != glb_cfg.live_update) {
|
||||
glb_cfg.update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::SubMenuBegin("General", 1)) {
|
||||
ImGui::InputInt("Averaging", &glb_cfg.avreging, 1, 100); ImGui::ToolTip("Number of tests to be averaged");
|
||||
ImGui::Checkbox("Collect time per instruction", &glb_cfg.time_per_instruction);
|
||||
ImGui::Checkbox("Collect mem per instruction", &glb_cfg.mem_per_instruction);
|
||||
|
||||
ImGui::SubMenuEnd(1);
|
||||
}
|
||||
|
||||
if (ImGui::SubMenuBegin("Testing Pattern", 1)) {
|
||||
|
||||
select_pattern();
|
||||
|
||||
ImGui::InputInt("size", &glb_cfg.pt_scale.size, 1, 100); ImGui::ToolTip("Y Scale of sizing pattern");
|
||||
ImGui::InputInt("items", &glb_cfg.pt_scale.items, 1, 100); ImGui::ToolTip("Y Scale of ordering and loading patterns");
|
||||
ImGui::InputInt("iterations", &glb_cfg.pt_scale.iterations, 1, 100); ImGui::ToolTip("X Scale of all patterns");
|
||||
|
||||
ImGui::SubMenuEnd(1);
|
||||
}
|
||||
|
||||
if (ImGui::SubMenuBegin("Allocators", 1)) {
|
||||
ImGui::Checkbox("heap", &glb_cfg.heap); ImGui::SameLine(); ImGui::Checkbox("pool", &glb_cfg.pool); ImGui::SameLine(); ImGui::Checkbox("chunk", &glb_cfg.chunk);
|
||||
|
||||
if (glb_cfg.chunk && ImGui::SubMenuBegin("Chunk", 2)) {
|
||||
ImGui::InputInt("size", &glb_cfg.chunk_bsize, 1, 100); ImGui::ToolTip("Size of a slot in the chunk buffer");
|
||||
ImGui::InputInt("length", &glb_cfg.chunk_blen, 1, 100); ImGui::ToolTip("Number of slots in the chunk buffer");
|
||||
ImGui::SubMenuEnd(2);
|
||||
}
|
||||
if (glb_cfg.pool && ImGui::SubMenuBegin("Pool", 2)) {
|
||||
ImGui::ToolTip("Generalized use of chunk allocator");
|
||||
ImGui::InputInt("size", &glb_cfg.pool_bsize, 1, 100);
|
||||
ImGui::InputInt("length", &glb_cfg.pool_blen, 1, 100);
|
||||
ImGui::SubMenuEnd(2);
|
||||
}
|
||||
ImGui::SubMenuEnd(1);
|
||||
}
|
||||
|
||||
//ImGui::PopItemWidth();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
output_draw();
|
||||
|
||||
pattern_generator();
|
||||
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
|
||||
void benchmarker::analize(config pcfg) {
|
||||
|
||||
if (!((pcfg.update) || (!(this->cfg == pcfg) && cfg.live_update))) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->cfg = pcfg;
|
||||
|
||||
clear_out();
|
||||
|
||||
if (!pattern_analizer.init(&patterns, cfg.current_loading_pattern, cfg.current_ordering_pattern, cfg.current_sizing_pattern, &cfg.pt_scale)) {
|
||||
if (pcfg.update) ImGui::Notify("invalid pattern configuration", 3);
|
||||
glb_cfg.update = false;
|
||||
cfg.update = false;
|
||||
is_output = false;
|
||||
return;
|
||||
}
|
||||
|
||||
reserve_out(&pattern_analizer);
|
||||
|
||||
for (tp::alni iter = 0; iter < pcfg.avreging; iter++) {
|
||||
init_allocators(pcfg);
|
||||
if (cfg.heap) collect(&pattern_analizer, halloc, out[0]);
|
||||
if (cfg.pool) collect(&pattern_analizer, palloc, out[1]);
|
||||
if (cfg.chunk) collect(&pattern_analizer, calloc, out[2]);
|
||||
dest_allocators();
|
||||
}
|
||||
|
||||
for (auto& i : tp::Range(0, 3)) {
|
||||
if (out[i]) out[i]->scale_all(1.f / pcfg.avreging);
|
||||
}
|
||||
|
||||
i_count = pattern_analizer.max_iterations();
|
||||
if (x_axis) {
|
||||
delete x_axis;
|
||||
x_axis = NULL;
|
||||
}
|
||||
x_axis = new tp::alnf[i_count];
|
||||
for (tp::alni iter = 0; iter < i_count; iter++) {
|
||||
x_axis[iter] = (tp::alnf) iter;
|
||||
}
|
||||
|
||||
glb_cfg.update = false;
|
||||
cfg.update = false;
|
||||
is_output = true;
|
||||
}
|
||||
|
||||
void benchmarker::init_allocators(config& pcfg) {
|
||||
if (cfg.heap) halloc = new tp::HeapAlloc();
|
||||
if (cfg.pool) palloc = new tp::PoolAlloc(pcfg.pool_bsize, pcfg.pool_blen);
|
||||
if (cfg.chunk) calloc = new tp::ChunkAlloc(pcfg.chunk_bsize, pcfg.chunk_blen);
|
||||
}
|
||||
|
||||
void benchmarker::dest_allocators() {
|
||||
try {
|
||||
if (cfg.heap) delete halloc;
|
||||
if (cfg.pool) delete palloc;
|
||||
if (cfg.chunk) delete calloc;
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
halloc = NULL;
|
||||
palloc = NULL;
|
||||
calloc = NULL;
|
||||
}
|
||||
|
||||
void benchmarker::clear_out() {
|
||||
for (auto i : tp::Range(0, 3)) {
|
||||
if (out[i]) {
|
||||
delete out[i];
|
||||
out[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (pattern_out) delete pattern_out;
|
||||
pattern_out = NULL;
|
||||
}
|
||||
|
||||
void benchmarker::reserve_out(test_pattern* pattern) {
|
||||
pattern_out = new pattern_histogram(pattern);
|
||||
if (cfg.heap) out[0] = new allocator_histogram(pattern, "heap", cfg.time_per_instruction, cfg.mem_per_instruction);
|
||||
if (cfg.pool) out[1] = new allocator_histogram(pattern, "pool", cfg.time_per_instruction, cfg.mem_per_instruction);
|
||||
if (cfg.chunk) out[2] = new allocator_histogram(pattern, "chunk", cfg.time_per_instruction, cfg.mem_per_instruction);
|
||||
}
|
||||
100
Allocators/outdated/tools/MemBenchmark/benchmarker.h
Normal file
100
Allocators/outdated/tools/MemBenchmark/benchmarker.h
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "patterns.h"
|
||||
|
||||
#include "allocators.h"
|
||||
#include "array.h"
|
||||
|
||||
#include "gl.h"
|
||||
#include "glcommon.h"
|
||||
#include "window.h"
|
||||
|
||||
enum class load_type {
|
||||
LINEAR,
|
||||
SINE,
|
||||
STEPS,
|
||||
RANDOM
|
||||
};
|
||||
|
||||
enum class order_type {
|
||||
LINEAR,
|
||||
LINEAR_REVERSED,
|
||||
RANDOM,
|
||||
};
|
||||
|
||||
struct config {
|
||||
// general
|
||||
bool live_update = false;
|
||||
|
||||
bool heap = true;
|
||||
bool pool = true;
|
||||
bool chunk = true;
|
||||
|
||||
// pattern
|
||||
pattern* current_sizing_pattern = 0;
|
||||
pattern* current_loading_pattern = 0;
|
||||
pattern* current_ordering_pattern = 0;
|
||||
pattern_scale pt_scale;
|
||||
|
||||
tp::alni update = 0;
|
||||
bool time_per_instruction = 0;
|
||||
bool mem_per_instruction = 0;
|
||||
int avreging;
|
||||
|
||||
// allocators
|
||||
int chunk_bsize = 0;
|
||||
int chunk_blen = 0;
|
||||
|
||||
int pool_bsize = 0;
|
||||
int pool_blen = 0;
|
||||
|
||||
bool operator==(const config& in) {
|
||||
return tp::memequal(this, (config*)(&in), sizeof(config));
|
||||
}
|
||||
};
|
||||
|
||||
struct benchmarker {
|
||||
|
||||
config cfg;
|
||||
bool is_output = false;
|
||||
|
||||
// allocators
|
||||
tp::HeapAlloc* halloc = NULL;
|
||||
tp::PoolAlloc* palloc = NULL;
|
||||
tp::ChunkAlloc* calloc = NULL;
|
||||
|
||||
|
||||
tp::Array<allocator_histogram*> out;
|
||||
pattern_histogram* pattern_out;
|
||||
|
||||
tp::alni i_count;
|
||||
tp::alnf* x_axis = NULL;
|
||||
|
||||
tp::HashMap<pattern*, tp::string> patterns;
|
||||
|
||||
pattern_reader pattern_analizer;
|
||||
|
||||
const char* pattern_generator_active = NULL;
|
||||
|
||||
benchmarker();
|
||||
~benchmarker();
|
||||
|
||||
test_pattern* get_pattern(config& cfg);
|
||||
|
||||
void pattern_combo(const char*&);
|
||||
void analize(config cfg);
|
||||
void select_pattern();
|
||||
void output_draw();
|
||||
|
||||
void MainDrawTick();
|
||||
|
||||
void pattern_generator();
|
||||
|
||||
void init_allocators(config& cfg);
|
||||
|
||||
void dest_allocators();
|
||||
|
||||
void clear_out();
|
||||
void reserve_out(test_pattern* pattern);
|
||||
};
|
||||
119
Allocators/outdated/tools/MemBenchmark/collector.cpp
Normal file
119
Allocators/outdated/tools/MemBenchmark/collector.cpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
|
||||
#include "collector.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
allocator_histogram::allocator_histogram(test_pattern* pt, const char* alloc_type, bool p_time_per_inst, bool p_mem_per_inst) {
|
||||
this->alloc_type = alloc_type;
|
||||
time_per_inst = p_time_per_inst;
|
||||
mem_per_inst = p_mem_per_inst;
|
||||
|
||||
if (time_per_inst) {
|
||||
time.reserve(pt->max_iterations());
|
||||
}
|
||||
if (mem_per_inst) {
|
||||
mem.reserve(pt->max_iterations());
|
||||
}
|
||||
data.reserve(pt->data_count());
|
||||
|
||||
total_time = 0;
|
||||
failed = false;
|
||||
}
|
||||
|
||||
void allocator_histogram::scale_all(tp::alnf fac) {
|
||||
for (auto& iter : time) {
|
||||
iter.data() = fac * iter.data();
|
||||
}
|
||||
for (auto& iter : mem) {
|
||||
iter.data() = fac * iter.data();
|
||||
}
|
||||
total_time = fac * total_time;
|
||||
}
|
||||
|
||||
allocator_histogram::~allocator_histogram() {
|
||||
}
|
||||
|
||||
void allocator_histogram::mark_resourses_usage(tp::alni idx, tp::alnf p_time, tp::alni p_mem, bool add) {
|
||||
if (mem.length() > idx)
|
||||
add ? mem[idx] += (tp::alnf)p_mem : mem[idx] = (tp::alnf)p_mem;
|
||||
if (time.length() > idx)
|
||||
add ? time[idx] += p_time : time[idx] = p_time;
|
||||
}
|
||||
|
||||
bool execute_instruction(tp::AbstractAllocator* alloc, bool load, tp::alni size, tp::uint1*& data) {
|
||||
bool failed = false;
|
||||
try {
|
||||
if (load) {
|
||||
if (!data) {
|
||||
data = (tp::uint1*)alloc->Alloc(size);
|
||||
if (!data) {
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (data) {
|
||||
alloc->Free(data);
|
||||
data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
failed = true;
|
||||
}
|
||||
return !failed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void collect(test_pattern* pattern, tp::AbstractAllocator* alloc, allocator_histogram* histogram) {
|
||||
|
||||
tp::alni iter_idx = 0;
|
||||
tp::alni nimtems_loaded = 0;
|
||||
|
||||
auto total_st = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (iter_idx = 0; iter_idx < pattern->max_iterations(); iter_idx++) {
|
||||
|
||||
tp::alni target_nimtems_loaded = pattern->pick_alloc_count(iter_idx);
|
||||
tp::alni data_idx = pattern->pick_idx(iter_idx);
|
||||
tp::alni load_size = pattern->pick_size(iter_idx);
|
||||
|
||||
auto iter_st = std::chrono::high_resolution_clock::now();
|
||||
while (nimtems_loaded != target_nimtems_loaded) {
|
||||
bool load = nimtems_loaded < target_nimtems_loaded;
|
||||
if (!execute_instruction(alloc, load, load_size, histogram->data[data_idx])) {
|
||||
histogram->mark_resourses_usage(iter_idx, 0, 0, 0);
|
||||
histogram->failed = true;
|
||||
break;
|
||||
}
|
||||
nimtems_loaded += (tp::alni)load + (-1 * (tp::alni)(!load));
|
||||
}
|
||||
auto iter_nd = std::chrono::high_resolution_clock::now();
|
||||
tp::alni dur = std::chrono::duration_cast<std::chrono::nanoseconds>(iter_nd - iter_st).count();
|
||||
histogram->mark_resourses_usage(iter_idx, tp::alnf(dur), alloc->sizeReserved(), 1);
|
||||
}
|
||||
|
||||
// clear all out
|
||||
for (iter_idx = 0; iter_idx < pattern->max_iterations(); iter_idx++) {
|
||||
if (histogram->data[iter_idx]) {
|
||||
execute_instruction(alloc, false, 0, histogram->data[iter_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
auto total_nd = std::chrono::high_resolution_clock::now();
|
||||
tp::alni dur = std::chrono::duration_cast<std::chrono::nanoseconds>(total_nd - total_st).count();
|
||||
histogram->total_time += dur;
|
||||
}
|
||||
|
||||
pattern_histogram::pattern_histogram(test_pattern* pt) {
|
||||
alloc_size.reserve(pt->max_iterations());
|
||||
data_idx.reserve(pt->max_iterations());
|
||||
items_loaded.reserve(pt->max_iterations());
|
||||
|
||||
for (auto& i : tp::Range(0, pt->max_iterations())) {
|
||||
alloc_size[i] = (tp::alnf) pt->pick_size(i);
|
||||
data_idx[i] = (tp::alnf)pt->pick_idx(i);
|
||||
items_loaded[i] = (tp::alnf)pt->pick_alloc_count(i);
|
||||
}
|
||||
}
|
||||
43
Allocators/outdated/tools/MemBenchmark/collector.h
Normal file
43
Allocators/outdated/tools/MemBenchmark/collector.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "array.h"
|
||||
|
||||
class test_pattern {
|
||||
public:
|
||||
virtual tp::alni pick_size(tp::alni iter) { return 0; };
|
||||
virtual tp::alni max_size() { return 0; };
|
||||
virtual tp::alni pick_alloc_count(tp::alni iter) { return 0; };
|
||||
virtual tp::alni max_iterations() { return 0; };
|
||||
virtual tp::alni pick_idx(tp::alni iter) { return 0; };
|
||||
virtual tp::alni data_count() { return 0; };
|
||||
};
|
||||
|
||||
struct pattern_histogram {
|
||||
tp::Array<tp::alnf> alloc_size;
|
||||
tp::Array<tp::alnf> data_idx;
|
||||
tp::Array<tp::alnf> items_loaded;
|
||||
|
||||
pattern_histogram(test_pattern* pt);
|
||||
};
|
||||
|
||||
struct allocator_histogram {
|
||||
const char* alloc_type;
|
||||
|
||||
tp::alnf total_time;
|
||||
tp::Array<tp::alnf> time;
|
||||
tp::Array<tp::alnf> mem;
|
||||
bool failed;
|
||||
|
||||
tp::Array<tp::uint1*> data;
|
||||
bool time_per_inst;
|
||||
bool mem_per_inst;
|
||||
|
||||
allocator_histogram(test_pattern* pt, const char* alloc_type, bool time_per_inst, bool mem_per_inst);
|
||||
~allocator_histogram();
|
||||
|
||||
void mark_resourses_usage(tp::alni idx, tp::alnf time, tp::alni mem, bool add);
|
||||
void scale_all(tp::alnf fac);
|
||||
};
|
||||
|
||||
void collect(test_pattern* pattern, tp::AbstractAllocator* alloc, allocator_histogram* histogram);
|
||||
0
Allocators/outdated/tools/MemBenchmark/patterns.cpp
Normal file
0
Allocators/outdated/tools/MemBenchmark/patterns.cpp
Normal file
174
Allocators/outdated/tools/MemBenchmark/patterns.h
Normal file
174
Allocators/outdated/tools/MemBenchmark/patterns.h
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "strings.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "collector.h"
|
||||
|
||||
tp::alni hash(const tp::string& val);
|
||||
|
||||
#include "map.h"
|
||||
|
||||
enum class leav_pattern_type {
|
||||
LINEAR,
|
||||
RANDOM,
|
||||
SINE,
|
||||
CONST,
|
||||
};
|
||||
|
||||
struct child_pattern {
|
||||
child_pattern() {}
|
||||
|
||||
child_pattern(tp::string _name) { name = _name; }
|
||||
|
||||
float uppernlim = 1.f;
|
||||
float lowerlim = 0.f;
|
||||
float point = 1.f;
|
||||
tp::string name;
|
||||
};
|
||||
|
||||
struct pattern {
|
||||
|
||||
leav_pattern_type type = leav_pattern_type::CONST;
|
||||
tp::string pattern_name;
|
||||
|
||||
tp::Array<child_pattern> regions;
|
||||
bool build_in = true;
|
||||
|
||||
pattern() {
|
||||
}
|
||||
|
||||
tp::alnf get_y(tp::HashMap<pattern*, tp::string>* patterns, tp::alnf x) {
|
||||
assert(x <= 1.0001f && x >= -0.00001f);
|
||||
|
||||
if (!regions.length()) {
|
||||
return pure_get_y(x);
|
||||
}
|
||||
|
||||
float offset = 0.f;
|
||||
for (tp::alni i = 0; i < regions.length(); i++) {
|
||||
tp::alni idx = patterns->presents(regions[i].name);
|
||||
if (!MAP_VALID_IDX(idx)) {
|
||||
return 0.f;
|
||||
}
|
||||
pattern* child = (*patterns)[idx];
|
||||
assert(child);
|
||||
|
||||
float range = regions[i].point * (1.f - offset);
|
||||
if (offset + range > x) {
|
||||
return regions[i].lowerlim +
|
||||
(child->get_y(patterns, (x - offset) / range) *
|
||||
(regions[i].uppernlim - regions[i].lowerlim));
|
||||
}
|
||||
offset += range;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual tp::alnf pure_get_y(tp::alnf x) { return 0; }
|
||||
|
||||
~pattern() {}
|
||||
};
|
||||
|
||||
// -------------------- build-in patterns ---------------------------- //
|
||||
|
||||
struct const_pattern : pattern {
|
||||
float val = 1.f;
|
||||
const_pattern() {
|
||||
type = leav_pattern_type::CONST;
|
||||
pattern_name = "const";
|
||||
build_in = true;
|
||||
}
|
||||
|
||||
tp::alnf pure_get_y(tp::alnf x) override { return val; }
|
||||
};
|
||||
|
||||
struct linear_pattern : pattern {
|
||||
bool reversed = false;
|
||||
linear_pattern() {
|
||||
type = leav_pattern_type::LINEAR;
|
||||
pattern_name = "linear";
|
||||
build_in = true;
|
||||
}
|
||||
|
||||
tp::alnf pure_get_y(tp::alnf x) override { return reversed ? 1.f - x : x; }
|
||||
};
|
||||
|
||||
struct random_pattern : pattern {
|
||||
random_pattern() {
|
||||
type = leav_pattern_type::RANDOM;
|
||||
build_in = true;
|
||||
}
|
||||
|
||||
tp::alnf pure_get_y(tp::alnf x) override { return tp::randf(); }
|
||||
};
|
||||
|
||||
// -------------------- build-in patterns end ---------------------------- //
|
||||
|
||||
struct pattern_scale {
|
||||
int items = 0;
|
||||
int size = 0;
|
||||
int iterations = 0;
|
||||
};
|
||||
|
||||
class pattern_reader : public test_pattern {
|
||||
public:
|
||||
bool init(tp::HashMap<pattern*, tp::string>* p_patterns, pattern* p_lpattern,
|
||||
pattern* p_opattern, pattern* p_spattern, pattern_scale* p_scale) {
|
||||
lpattern = p_lpattern;
|
||||
opattern = p_opattern;
|
||||
spattern = p_spattern;
|
||||
|
||||
scale = p_scale;
|
||||
|
||||
patterns = p_patterns;
|
||||
return verify_rulles();
|
||||
}
|
||||
|
||||
bool verify_rulles() {
|
||||
if (!lpattern || !opattern || !spattern) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool out = true;
|
||||
out &= scale->items > 0;
|
||||
out &= scale->size > 0;
|
||||
out &= scale->iterations > 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
tp::HashMap<pattern*, tp::string>* patterns;
|
||||
|
||||
pattern* lpattern = NULL;
|
||||
pattern* opattern = NULL;
|
||||
pattern* spattern = NULL;
|
||||
|
||||
pattern_scale* scale = 0;
|
||||
|
||||
tp::alnf get_x_val(tp::alni iter) {
|
||||
tp::alnf out = iter / (tp::alnf)scale->iterations;
|
||||
return out > 1 ? 1.f : out;
|
||||
}
|
||||
|
||||
tp::alni pick_size(tp::alni iter) override {
|
||||
return (tp::alni)(scale->size * spattern->get_y(patterns, get_x_val(iter)));
|
||||
}
|
||||
|
||||
tp::alni pick_alloc_count(tp::alni iter) override {
|
||||
return (tp::alni)(scale->items * lpattern->get_y(patterns, get_x_val(iter)));
|
||||
}
|
||||
|
||||
tp::alni pick_idx(tp::alni iter) override {
|
||||
tp::alni out = (tp::alni) (scale->items * opattern->get_y(patterns, get_x_val(iter)));
|
||||
CLAMP(out, 0, scale->items - 1);
|
||||
return out;
|
||||
}
|
||||
|
||||
tp::alni max_size() override { return scale->size; }
|
||||
|
||||
tp::alni max_iterations() override { return scale->iterations; }
|
||||
|
||||
tp::alni data_count() override { return scale->iterations; }
|
||||
};
|
||||
93
Allocators/outdated/tools/MemLeaks/MemleaksEntry.cpp
Normal file
93
Allocators/outdated/tools/MemLeaks/MemleaksEntry.cpp
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
|
||||
#include "SimpleGui.h"
|
||||
#include "debugui.h"
|
||||
#include "memleaks.h"
|
||||
#include "timer.h"
|
||||
|
||||
struct MemLeaksGUI {
|
||||
|
||||
tp::glw::Window window;
|
||||
tp::glw::DebugUI ui;
|
||||
tp::glw::Canvas canvas;
|
||||
tp::glw::WindowSimpleInputs inputs;
|
||||
tp::glw::WindowsHeaderWidget header;
|
||||
tp::MemLeaksData mLeaks;
|
||||
tp::MemLeaksTreeView mView;
|
||||
tp::halnf mHeaderHeight = 5.5f;
|
||||
tp::Timer timer;
|
||||
|
||||
MemLeaksGUI(const char* path) : window(), ui(window, debuguiCallBack, this), mLeaks(path), timer(10) {
|
||||
canvas.setCol1({ 0.5f, 0.5f, 0.5f, 0.5f });
|
||||
window.mAppearence.mHiden = false;
|
||||
mView.setTarget(&mLeaks);
|
||||
}
|
||||
|
||||
void proc() {
|
||||
window.pollEvents();
|
||||
inputs.update(window, 1.f);
|
||||
|
||||
header.header_rec = { (inputs.mWindowSizeMM.x - 60.f) / 2.f, 3.f, 60.f, mHeaderHeight };
|
||||
mView.rect = { 0.f, 0.f, inputs.mWindowSizeMM.x, inputs.mWindowSizeMM.y };
|
||||
|
||||
mView.proc();
|
||||
|
||||
if (inputs.MoveUp()) {
|
||||
mView.zoom_factor -= 0.2f;
|
||||
}
|
||||
else if (inputs.MoveDown()) {
|
||||
mView.zoom_factor += 0.2f;
|
||||
}
|
||||
mView.zoom_factor = tp::clamp(mView.zoom_factor, 0.f, 1.f);
|
||||
|
||||
mView.vieport_crs = inputs.mCrs;
|
||||
mView.vieport_crs_delta = inputs.mCrsmDelta * -1.f;
|
||||
mView.mouse_down = inputs.Activated();
|
||||
mView.mouse_hold = inputs.Anticipating();
|
||||
mView.mouse_up = inputs.Selected();
|
||||
|
||||
header.proc(window, inputs);
|
||||
}
|
||||
|
||||
void draw() {
|
||||
window.beginDraw(); {
|
||||
canvas.beginDraw({ { 0, 0 }, inputs.mWindowSizeMM }, window.mDevice.mDPMM, 1.f); {
|
||||
mView.draw(&canvas);
|
||||
header.draw(canvas);
|
||||
} canvas.endDraw();
|
||||
ui.drawDebugUI(window.mDevice.mDPMM);
|
||||
} window.endDraw();
|
||||
}
|
||||
|
||||
void run() {
|
||||
while (!window.mEvents.mClose) {
|
||||
proc();
|
||||
if (window.mEvents.mRedraw) {
|
||||
draw();
|
||||
}
|
||||
|
||||
timer.wait();
|
||||
timer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void debugui() {}
|
||||
static void debuguiCallBack(void* self) { ((MemLeaksGUI*)self)->debugui(); }
|
||||
~MemLeaksGUI() {}
|
||||
};
|
||||
|
||||
int main(char argc, char* argv[]) {
|
||||
tp::set_working_dir();
|
||||
|
||||
tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleGlw, NULL };
|
||||
tp::ModuleManifest TestModule("Test", NULL, NULL, ModuleDependencies);
|
||||
if (!TestModule.initialize()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
{
|
||||
MemLeaksGUI gui(argc > 1 ? argv[1] : "rsc/debug.memleaks");
|
||||
gui.run();
|
||||
}
|
||||
|
||||
TestModule.deinitialize();
|
||||
}
|
||||
403
Allocators/outdated/tools/MemLeaks/memleaks.cpp
Normal file
403
Allocators/outdated/tools/MemLeaks/memleaks.cpp
Normal file
|
|
@ -0,0 +1,403 @@
|
|||
|
||||
#include "memleaks.h"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void MemLeaksData::Frame::dfs(Frame& frame, void (*exec)(Frame& frame, void* custom), void* custom) {
|
||||
if (frame.flag == Frame::Flags::INUSE) { return; }
|
||||
frame.flag = Frame::Flags::INUSE;
|
||||
exec(frame, custom);
|
||||
for (auto call_info : frame.callers) {
|
||||
dfs(*call_info->val.caller, exec, custom);
|
||||
}
|
||||
frame.flag = Frame::Flags::NONE;
|
||||
}
|
||||
|
||||
MemLeaksData::MemLeaksData(tp::string afilename) {
|
||||
filename = afilename;
|
||||
load_leaks();
|
||||
|
||||
rootframe.name = "root";
|
||||
}
|
||||
|
||||
void MemLeaksData::increase_caller_count(Frame& frame, FrameId caller_id) {
|
||||
auto idx = frame.callers.presents(caller_id);
|
||||
if (idx) {
|
||||
frame.callers.getSlotVal(idx).count++;
|
||||
}
|
||||
else {
|
||||
Frame* caller_frame = &frames.get(caller_id);
|
||||
frame.callers.put(caller_id, { caller_frame, 1 });
|
||||
}
|
||||
}
|
||||
|
||||
void MemLeaksData::calc_max_level(Frame& frame, tp::alni& max_level) {
|
||||
frame.flag = Frame::Flags::INUSE;
|
||||
|
||||
for (auto call_info : frame.callers) {
|
||||
auto caller = call_info->val.caller;
|
||||
if (caller->flag != Frame::Flags::INUSE) {
|
||||
if (caller->depth_level < frame.depth_level + 1) {
|
||||
caller->depth_level = frame.depth_level + 1;
|
||||
max_level = MAX(caller->depth_level, max_level);
|
||||
calc_max_level(*call_info->val.caller, max_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frame.flag = Frame::Flags::NONE;
|
||||
}
|
||||
|
||||
void MemLeaksData::count_level_users(Frame& frame, tp::alni& max_level, tp::Array<LevelInfo>& levels) {
|
||||
if (frame.flag2 == 1) {
|
||||
return;
|
||||
}
|
||||
frame.flag2 = 1;
|
||||
levels[frame.depth_level].count_users += 1;
|
||||
for (auto call_info : frame.callers) {
|
||||
count_level_users(*call_info->val.caller, max_level, levels);
|
||||
}
|
||||
}
|
||||
|
||||
void MemLeaksData::apply_position(Frame& frame, tp::Array<LevelInfo>& levels) {
|
||||
if (frame.flag2 == 1) {
|
||||
return;
|
||||
}
|
||||
frame.flag2 = 1;
|
||||
|
||||
|
||||
auto x = -(levels[frame.depth_level].count_users - 1) * sapacing.x / 2 + sapacing.x * levels[frame.depth_level].used_idx;
|
||||
frame.tree_view_pos.assign(x, frame.depth_level * sapacing.y);
|
||||
levels[frame.depth_level].used_idx++;
|
||||
|
||||
for (auto call_info : frame.callers) {
|
||||
apply_position(*call_info->val.caller, levels);
|
||||
}
|
||||
};
|
||||
|
||||
void MemLeaksData::construct_tree() {
|
||||
using namespace tp;
|
||||
|
||||
// construct tree
|
||||
for (auto leak : leaks) {
|
||||
increase_caller_count(rootframe, leak.data()[0]);
|
||||
for (auto i : Range<alni>(leak->length() - 1)) {
|
||||
increase_caller_count(frames.get(leak.data()[i]), leak.data()[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
tp::alni max_depth_level = 0;
|
||||
calc_max_level(rootframe, max_depth_level);
|
||||
|
||||
levels.reserve(max_depth_level + 1);
|
||||
|
||||
count_level_users(rootframe, max_depth_level, levels);
|
||||
Frame::dfs(rootframe, [](Frame& frame, void*) { frame.flag2 = 0; });
|
||||
|
||||
apply_position(rootframe, levels);
|
||||
Frame::dfs(rootframe, [](Frame& frame, void*) { frame.flag2 = 0; });
|
||||
}
|
||||
|
||||
void MemLeaksData::make_connections() {
|
||||
tp::halni len = 0;
|
||||
for (auto& frame : frames) {
|
||||
len += frame.iter->val.callers.size();
|
||||
}
|
||||
mConnections.reserve(len);
|
||||
tp::halni idx = 0;
|
||||
for (auto& frame : frames) {
|
||||
for (auto& caller : frame.iter->val.callers) {
|
||||
caller.iter->val.caller;
|
||||
mConnections[idx] = { caller.iter->val.caller, &frame.iter->val, caller.iter->val.count };
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemLeaksData::load_leaks() {
|
||||
using namespace tp;
|
||||
|
||||
File log(filename.cstr(), osfile_openflags::LOAD);
|
||||
|
||||
if (!log.opened) {
|
||||
status = LoadStatus::INVALID_FILE_PATH;
|
||||
return;
|
||||
}
|
||||
|
||||
log.Preload();
|
||||
|
||||
const char logo[] = "memleaks\0";
|
||||
const char logo_len = 10;
|
||||
char logo_loaded[logo_len];
|
||||
|
||||
log.read_bytes(logo_loaded, logo_len);
|
||||
|
||||
if (!tp::memequal(logo_loaded, logo, logo_len)) {
|
||||
status = LoadStatus::INVALID_FILE_FORMAT;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
alni leaks_len;
|
||||
log.read<alni>(&leaks_len);
|
||||
|
||||
leaks.reserve(leaks_len);
|
||||
|
||||
Frame frame;
|
||||
|
||||
for (alni idx = 0; idx < leaks_len; idx++) {
|
||||
|
||||
tp::alni frames_len;
|
||||
log.read<tp::alni>(&frames_len);
|
||||
|
||||
leaks[idx].reserve(frames_len);
|
||||
|
||||
for (alni frame_idx = 0; frame_idx < frames_len; frame_idx++) {
|
||||
|
||||
FrameId id;
|
||||
|
||||
|
||||
log.read<tp::ualni>(&id);
|
||||
leaks[idx][frame_idx] = id;
|
||||
|
||||
//if (frames.presents(id)) {
|
||||
//continue;
|
||||
//}
|
||||
|
||||
frame.name.load(&log);
|
||||
frame.file.load(&log);
|
||||
log.read<tp::ualni>(&frame.line);
|
||||
|
||||
frame.id = id;
|
||||
|
||||
frames.put(id, frame);
|
||||
}
|
||||
}
|
||||
|
||||
construct_tree();
|
||||
make_connections();
|
||||
|
||||
}
|
||||
catch (...) {
|
||||
status = LoadStatus::INTERNAL_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
status = LoadStatus::DONE;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------- Tree View Drawer -------------------------------------- //
|
||||
|
||||
void MemLeaksTreeView::setTarget(MemLeaksData* aLeaks) {
|
||||
leaks = aLeaks;
|
||||
selected_node = NULL;
|
||||
}
|
||||
|
||||
void MemLeaksTreeView::proc() {
|
||||
if (!leaks) {
|
||||
return;
|
||||
}
|
||||
|
||||
// handle selection
|
||||
if (mouse_down) {
|
||||
selected_node = NULL;
|
||||
|
||||
void (*find_selected)(Frame & frame, void* vec) = [](Frame& frame, void* self_ptr) {
|
||||
auto self = (MemLeaksTreeView*)self_ptr;
|
||||
|
||||
auto node_rec = self->nodeBoundsScaled(frame);
|
||||
//auto vieport = self->rect;
|
||||
|
||||
if (node_rec.inside(self->vieport_crs)) {
|
||||
self->selected_node = &frame;
|
||||
}
|
||||
};
|
||||
|
||||
Frame::dfs(leaks->rootframe, find_selected, this);
|
||||
}
|
||||
|
||||
// handle mouse drag
|
||||
if (vieport_crs_delta != 0.f && rect.inside(vieport_crs) && mouse_hold) {
|
||||
if (selected_node) {
|
||||
selected_node->tree_view_pos -= vieport_crs_delta / (tp::halnf)scaleval;
|
||||
}
|
||||
else {
|
||||
tree_view_pos -= vieport_crs_delta / (tp::halnf)scaleval;
|
||||
}
|
||||
}
|
||||
|
||||
// calc scale fac
|
||||
{
|
||||
// zoom_factor :
|
||||
// 0 - 3 nodes visiable
|
||||
// 1 - all nodes visiable
|
||||
|
||||
auto max_tree_size = MAX(tree_size.x, tree_size.y);
|
||||
auto max_node_size = MAX(node_size.x, node_size.y);
|
||||
auto min_view_size = MIN(rect.z, rect.w) - 100;
|
||||
|
||||
auto max = min_view_size / max_tree_size;
|
||||
auto min = min_view_size / max_node_size;
|
||||
|
||||
scaleval = (max - min) * zoom_factor + min;
|
||||
}
|
||||
|
||||
// calc tree size
|
||||
void (*execf)(Frame & frame, void* vec) = [](Frame& frame, void* vecp) {
|
||||
auto tree_min_max_pos = (tp::rectf*)vecp;
|
||||
// if smaller than min
|
||||
if (tree_min_max_pos->v1.x > frame.tree_view_pos.x) {
|
||||
tree_min_max_pos->v1.x = frame.tree_view_pos.x;
|
||||
}
|
||||
if (tree_min_max_pos->v1.y > frame.tree_view_pos.y) {
|
||||
tree_min_max_pos->v1.y = frame.tree_view_pos.y;
|
||||
}
|
||||
// if bigger than max
|
||||
if (tree_min_max_pos->v2.x < frame.tree_view_pos.x) {
|
||||
tree_min_max_pos->v2.x = frame.tree_view_pos.x;
|
||||
}
|
||||
if (tree_min_max_pos->v2.y < frame.tree_view_pos.y) {
|
||||
tree_min_max_pos->v2.y = frame.tree_view_pos.y;
|
||||
}
|
||||
};
|
||||
|
||||
tree_size = 0;
|
||||
tp::rectf tree_min_max_pos = { FLT_MAX, FLT_MIN };
|
||||
Frame::dfs(leaks->rootframe, execf, &tree_min_max_pos);
|
||||
tree_size = tree_min_max_pos.v2 - tree_min_max_pos.v1;
|
||||
tree_size += node_size;
|
||||
}
|
||||
|
||||
void MemLeaksTreeView::draw(tp::glw::Canvas* drawer) {
|
||||
|
||||
drawer->setCol1(col.bg);
|
||||
drawer->rect(rect, 2.f);
|
||||
|
||||
drawer->setCol1(col.text);
|
||||
if (!leaks) {
|
||||
drawer->text("Not Loaded", rect, 5);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (leaks->status) {
|
||||
case MemLeaksData::LoadStatus::INVALID_FILE_PATH:
|
||||
drawer->text("INVALID_FILE_PATH", rect, 5);
|
||||
return;
|
||||
case MemLeaksData::LoadStatus::INVALID_FILE_FORMAT:
|
||||
drawer->text("INVALID_FILE_FORMAT", rect, 5);
|
||||
return;
|
||||
case MemLeaksData::LoadStatus::INTERNAL_ERROR:
|
||||
drawer->text("INTERNAL_ERROR", rect, 5);
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& frame : leaks->frames) {
|
||||
drawNode(frame.iter->val, drawer);
|
||||
}
|
||||
|
||||
for (auto& connection : leaks->mConnections) {
|
||||
drawConnection(*connection.data().caller, *connection.data().target, connection.data().count, drawer);
|
||||
}
|
||||
|
||||
if (selected_node) {
|
||||
auto flag = tp::glw::Canvas::Align(tp::glw::Canvas::LEFT | tp::glw::Canvas::TOP);
|
||||
tp::rectf info_rec = rect;
|
||||
info_rec.pos.x += 5;
|
||||
info_rec.pos.y += 25;
|
||||
drawer->text(selected_node->name, info_rec, 4, flag);
|
||||
info_rec.pos.y += 10;
|
||||
drawer->text(selected_node->file, info_rec, 4, flag);
|
||||
info_rec.pos.y += 10;
|
||||
drawer->text(tp::halni(selected_node->line), info_rec, 4, flag);
|
||||
}
|
||||
}
|
||||
|
||||
void MemLeaksTreeView::drawNode(Frame& node, tp::glw::Canvas* drawer) {
|
||||
auto rec = nodeBoundsScaled(node);
|
||||
|
||||
// outside
|
||||
if (!rec.overlap(rect)) {
|
||||
node.flag = Frame::Flags::NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
rec.clamp(rect);
|
||||
|
||||
drawer->setCol1((&node == selected_node) ? col.node_active : col.node);
|
||||
drawer->setCol2(col.node_outline);
|
||||
drawer->rect(rec, node_rounding, outline_size);
|
||||
|
||||
drawer->setCol1(col.text);
|
||||
auto prev = drawer->mClamping;
|
||||
rec.pos.x += 2;
|
||||
rec.size.x -= 4;
|
||||
drawer->setClamping(rec);
|
||||
drawer->text(node.name, rec, text_size, tp::glw::Canvas::LEFT_MIDDLE);
|
||||
drawer->setClamping(prev);
|
||||
}
|
||||
|
||||
void MemLeaksTreeView::drawConnection(Frame& from, Frame& to, tp::halni call_count, tp::glw::Canvas* drawer) {
|
||||
|
||||
node_size *= 1.1;
|
||||
auto rec1 = nodeBoundsScaled(from);
|
||||
auto rec2 = nodeBoundsScaled(to);
|
||||
node_size /= 1.1;
|
||||
|
||||
auto c1 = scalePoint(from.tree_view_pos + tree_view_pos);
|
||||
auto c2 = scalePoint(to.tree_view_pos + tree_view_pos);
|
||||
|
||||
rec1.clamp_outside(c1, c2);
|
||||
rec2.clamp_outside(c1, c2);
|
||||
|
||||
auto viewport = rect;
|
||||
|
||||
if (!viewport.clamp_inside(c1, c2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dir = (c2 - c1).unitv();
|
||||
auto side = dir.normal() * arrow_size * scaleval;
|
||||
|
||||
auto ab = c2 - dir * arrow_size * scaleval;
|
||||
auto ae = c2;
|
||||
auto al = ab + side;
|
||||
auto ar = ab - side;
|
||||
|
||||
if (&from == selected_node || &to == selected_node) {
|
||||
//col = ImColor(ImGui::GetStyle().Colors[ImGuiCol_ButtonActive]);
|
||||
}
|
||||
|
||||
drawer->setCol1(col.arrow);
|
||||
|
||||
drawer->trig({ ae.x, ae.y }, { al.x, al.y }, { ar.x, ar.y });
|
||||
drawer->line({ c1.x, c1.y }, { c2.x, c2.y }, line_thik);
|
||||
|
||||
if (&from == selected_node || &to == selected_node) {
|
||||
auto text_pos = c1 + (dir * (tp::halnf)(c1 - c2).length() * 0.8);
|
||||
tp::string count = call_count;
|
||||
drawer->setCol1(col.text);
|
||||
drawer->text(count.cstr(), { text_pos.x, text_pos.y, 0, 0 }, text_size);
|
||||
}
|
||||
}
|
||||
|
||||
tp::vec2f MemLeaksTreeView::scalePoint(const tp::vec2f in) {
|
||||
return in * (tp::halnf)scaleval + rect.pos + rect.size / 2;
|
||||
}
|
||||
|
||||
tp::rectf MemLeaksTreeView::nodeBounds(Frame& node) {
|
||||
tp::rectf out;
|
||||
out.pos = node.tree_view_pos + tree_view_pos;
|
||||
out.pos -= node_size / 2;
|
||||
out.size = node_size;
|
||||
return out;
|
||||
}
|
||||
|
||||
tp::rectf MemLeaksTreeView::nodeBoundsScaled(Frame& node) {
|
||||
auto out = nodeBounds(node);
|
||||
auto p1 = scalePoint(out.pos);
|
||||
auto p3 = scalePoint(out.p3());
|
||||
out.pos = p1;
|
||||
out.size = p3 - p1;
|
||||
return out;
|
||||
}
|
||||
137
Allocators/outdated/tools/MemLeaks/memleaks.h
Normal file
137
Allocators/outdated/tools/MemLeaks/memleaks.h
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
|
||||
#include "map.h"
|
||||
#include "array.h"
|
||||
#include "stringt.h"
|
||||
#include "rect.h"
|
||||
#include "color.h"
|
||||
#include "canvas.h"
|
||||
|
||||
namespace tp {
|
||||
|
||||
struct MemLeaksData {
|
||||
|
||||
typedef tp::ualni FrameId;
|
||||
typedef tp::Array<FrameId> MemLeak;
|
||||
|
||||
struct Frame;
|
||||
|
||||
struct CallerInfo {
|
||||
Frame* caller = NULL;
|
||||
tp::ualni count = 0;
|
||||
};
|
||||
|
||||
struct Frame {
|
||||
tp::string name;
|
||||
tp::string file;
|
||||
tp::ualni line = 0;
|
||||
tp::ualni id = 0;
|
||||
|
||||
tp::HashMap<CallerInfo, FrameId> callers;
|
||||
|
||||
tp::vec2f tree_view_pos = 0;
|
||||
bool collapced = true;
|
||||
|
||||
enum class Flags {
|
||||
NONE,
|
||||
INUSE,
|
||||
} flag = Flags::NONE;
|
||||
|
||||
tp::alni flag2 = 0;
|
||||
tp::alni depth_level = 0;
|
||||
|
||||
static void dfs(Frame& frame, void (*exec)(Frame& frame, void* custom), void* custom = 0);
|
||||
};
|
||||
|
||||
Frame rootframe;
|
||||
tp::HashMap<Frame, FrameId> frames;
|
||||
tp::Array<MemLeak> leaks;
|
||||
|
||||
struct Connection {
|
||||
Frame* caller = NULL;
|
||||
Frame* target = NULL;
|
||||
tp::ualni count = 0;
|
||||
};
|
||||
|
||||
tp::Array<Connection> mConnections;
|
||||
|
||||
tp::string filename;
|
||||
tp::vec2f sapacing = { 60.f, 15.f };
|
||||
|
||||
enum class LoadStatus {
|
||||
DONE,
|
||||
INVALID_FILE_PATH,
|
||||
INVALID_FILE_FORMAT,
|
||||
INTERNAL_ERROR,
|
||||
} status;
|
||||
|
||||
MemLeaksData(tp::string afilename);
|
||||
|
||||
private:
|
||||
|
||||
struct LevelInfo {
|
||||
tp::alni count_users;
|
||||
tp::alni used_idx;
|
||||
};
|
||||
|
||||
tp::Array<LevelInfo> levels;
|
||||
|
||||
void increase_caller_count(Frame& frame, FrameId caller_id);
|
||||
void calc_max_level(Frame& frame, tp::alni& max_level);
|
||||
void count_level_users(Frame& frame, tp::alni& max_level, tp::Array<LevelInfo>& levels);
|
||||
void apply_position(Frame& frame, tp::Array<LevelInfo>& levels);
|
||||
void construct_tree();
|
||||
void load_leaks();
|
||||
void make_connections();
|
||||
};
|
||||
|
||||
struct MemLeaksTreeView {
|
||||
|
||||
// inputs
|
||||
tp::halnf zoom_factor = 1.f;
|
||||
tp::rectf rect = 0.f;
|
||||
tp::vec2f vieport_crs = 0.f;
|
||||
tp::vec2f vieport_crs_delta = 0.f;
|
||||
bool mouse_down = false;
|
||||
bool mouse_hold = false;
|
||||
bool mouse_up = false;
|
||||
|
||||
// appearence
|
||||
struct Colors {
|
||||
tp::rgba node = { 0.1f, 0.1f, 0.1f, 1.f };
|
||||
tp::rgba node_outline = { 0.5f, 0.5f, 0.5f, 1.f };
|
||||
tp::rgba node_active = { 0.2f, 0.2f, 0.2f, 1.f };
|
||||
tp::rgba bg = { 0.15f, 0.15f, 0.15f, 1.f };
|
||||
tp::rgba text = { 0.9f, 0.9f, 0.9f, 1.f };
|
||||
tp::rgba arrow = { 0.5f, 0.5f, 0.5f, 1.f };
|
||||
} col;
|
||||
|
||||
tp::halnf text_size = 3;
|
||||
tp::halnf arrow_size = 2;
|
||||
tp::halnf outline_size = 0.5f;
|
||||
tp::halnf line_thik = 0.2f;
|
||||
tp::halnf node_rounding = 1;
|
||||
|
||||
void setTarget(MemLeaksData* aLeaks);
|
||||
|
||||
void proc();
|
||||
void draw(tp::glw::Canvas* drawer);
|
||||
|
||||
private:
|
||||
|
||||
typedef MemLeaksData::Frame Frame;
|
||||
|
||||
MemLeaksData* leaks = NULL;
|
||||
Frame* selected_node = NULL;
|
||||
bool inside_node = false;
|
||||
tp::alnf scaleval = 1.0;
|
||||
tp::vec2f tree_size = 0.f;
|
||||
tp::vec2f tree_view_pos = { 0.f, -50.f };
|
||||
tp::vec2f node_size = { 50, 10 };
|
||||
|
||||
void drawNode(Frame& node, tp::glw::Canvas* drawer);
|
||||
void drawConnection(Frame& from, Frame& to, tp::halni call_count, tp::glw::Canvas* drawer);
|
||||
tp::vec2f scalePoint(const tp::vec2f in);
|
||||
tp::rectf nodeBounds(Frame& node);
|
||||
tp::rectf nodeBoundsScaled(Frame& node);
|
||||
};
|
||||
};
|
||||
1
Allocators/outdated/tools/MemUsage/MemUsageEntry.cpp
Normal file
1
Allocators/outdated/tools/MemUsage/MemUsageEntry.cpp
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
401
Allocators/outdated/tools/MemUsage/memusage.cpp
Normal file
401
Allocators/outdated/tools/MemUsage/memusage.cpp
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
|
||||
|
||||
#include "GuiWindow.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "implot.h"
|
||||
#include "nanovg.h"
|
||||
|
||||
#include "strings.h"
|
||||
#include "filesystem.h"
|
||||
#include "array.h"
|
||||
#include "list.h"
|
||||
#include "map.h"
|
||||
|
||||
#include "intersections.h"
|
||||
|
||||
#include "pickalloc_analizer.h"
|
||||
#include "pickalloc_cfg.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
class Memusage : public tp::GuiWindow {
|
||||
|
||||
tp::string filename;
|
||||
|
||||
static const char logo_len = 16;
|
||||
const char logo[logo_len] = "memusage\0\0\0\0\0\0\0";
|
||||
char logo_loaded[logo_len];
|
||||
|
||||
PickAllocConfig cfg;
|
||||
|
||||
enum class LoadStatus {
|
||||
NONE,
|
||||
DONE,
|
||||
INVALID_FILE_PATH,
|
||||
INVALID_FILE_FORMAT,
|
||||
INTERNAL_ERROR,
|
||||
} status;
|
||||
|
||||
typedef tp::PickAllocDataAnalizer::Event Event;
|
||||
typedef tp::alni SizeKey;
|
||||
|
||||
struct Timeline {
|
||||
tp::Array<tp::alnf> size;
|
||||
tp::Array<tp::alnf> time;
|
||||
tp::ualni peak = 0;
|
||||
tp::ualni avereging = 0;
|
||||
|
||||
void recordEvents(tp::Array<Event>& events) {
|
||||
tp::ualni total_mem_usage = 0;
|
||||
|
||||
size.extend(events.length());
|
||||
time.extend(events.length());
|
||||
|
||||
for (auto ev : events) {
|
||||
|
||||
if (ev->dealloc) {
|
||||
total_mem_usage -= ev->size;
|
||||
} else {
|
||||
total_mem_usage += ev->size;
|
||||
}
|
||||
|
||||
time[ev.idx()] = (tp::alnf) ev->time;
|
||||
size[ev.idx()] = (tp::alnf) total_mem_usage;
|
||||
|
||||
if (peak < total_mem_usage) {
|
||||
peak = total_mem_usage;
|
||||
}
|
||||
}
|
||||
|
||||
avereging = 1;
|
||||
}
|
||||
|
||||
void to_seconds() {
|
||||
for (auto tm : time) {
|
||||
tm.data() /= 1000;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Contains events of fixed size
|
||||
struct Group {
|
||||
tp::ualni group_size = 0;
|
||||
tp::Array<Event> events;
|
||||
|
||||
tp::ualni flag = 0;
|
||||
|
||||
Timeline timeline;
|
||||
|
||||
Group(tp::ualni grp_size) : group_size(grp_size) {}
|
||||
};
|
||||
|
||||
tp::HashMap<Group*, SizeKey> groups;
|
||||
tp::Array<Group*> groups_sorted;
|
||||
|
||||
public:
|
||||
|
||||
Memusage(const char* filename) : GuiWindow() {
|
||||
|
||||
this->mHeader.mAppTitle = "Mamusage Analizer";
|
||||
this->mHeader.mPadding = 10;
|
||||
tp::string file_path = __FILE__;
|
||||
tp::make_dir_str(file_path.get_writable());
|
||||
tp::make_dir_str(file_path.get_writable());
|
||||
this->mRscDirectory = file_path + "/";
|
||||
this->mGuiRootIsWindow = false;
|
||||
LoadFonts();
|
||||
|
||||
this->filename = filename;
|
||||
}
|
||||
|
||||
~Memusage() {
|
||||
for (auto tl : groups) {
|
||||
delete tl->val;
|
||||
}
|
||||
|
||||
if (cfg.mAllocationSizes) delete[] cfg.mAllocationSizes;
|
||||
if (cfg.mChunkLengths) delete[] cfg.mChunkLengths;
|
||||
}
|
||||
|
||||
void run() {
|
||||
|
||||
// display status
|
||||
for (auto i : tp::Range(2)) {
|
||||
procInputs();
|
||||
beginDraw();
|
||||
if (mNativeWindow.mEvents.mRedraw) {
|
||||
DrawCallback();
|
||||
}
|
||||
endDraw();
|
||||
mNativeWindow.mEvents.mRedraw = true;
|
||||
}
|
||||
|
||||
while (!mNativeWindow.mEvents.mTerminate) {
|
||||
tp::Timer timer(5);
|
||||
procInputs();
|
||||
beginDraw();
|
||||
if (mNativeWindow.mEvents.mRedraw) {
|
||||
DrawCallback();
|
||||
}
|
||||
endDraw();
|
||||
timer.wait();
|
||||
|
||||
if (status == LoadStatus::NONE) {
|
||||
openFile();
|
||||
mNativeWindow.mEvents.mRedraw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
tp::ualni fileContentSize(tp::File& file) {
|
||||
return file.size() - fileContentStartIdx();
|
||||
}
|
||||
|
||||
tp::ualni fileContentStartIdx() {
|
||||
return logo_len;
|
||||
}
|
||||
|
||||
void readFile(tp::File& file) {
|
||||
using namespace tp;
|
||||
|
||||
// read cfg
|
||||
file.read(&cfg.mCapacity);
|
||||
cfg.mAllocationSizes = new tp::int4[cfg.mCapacity];
|
||||
cfg.mChunkLengths = new tp::int4[cfg.mCapacity];
|
||||
|
||||
file.read_bytes((tp::int1*)cfg.mAllocationSizes, sizeof(tp::int4) * cfg.mCapacity);
|
||||
file.read_bytes((tp::int1*)cfg.mChunkLengths, sizeof(tp::int4) * cfg.mCapacity);
|
||||
|
||||
// read evens
|
||||
tp::ualni events_length;
|
||||
file.read(&events_length);
|
||||
|
||||
// one pass to collect data about groups in 'flag'
|
||||
auto events_adress = file.adress;
|
||||
for (tp::ualni i = 0; i < events_length; i++) {
|
||||
Event evnt;
|
||||
file.read(&evnt);
|
||||
auto idx = groups.presents(evnt.size);
|
||||
Group* group = NULL;
|
||||
if (idx) {
|
||||
group = groups.getSlotVal(idx);
|
||||
} else {
|
||||
group = new Group(evnt.size);
|
||||
groups.put(evnt.size, group);
|
||||
}
|
||||
group->flag++;
|
||||
}
|
||||
for (auto grp : groups) {
|
||||
grp->val->events.reserve(grp->val->flag);
|
||||
grp->val->flag = 0;
|
||||
}
|
||||
file.adress = events_adress;
|
||||
|
||||
// read actual events
|
||||
for (tp::ualni idx = 0; idx < events_length; idx++) {
|
||||
Event evnt;
|
||||
file.read(&evnt);
|
||||
auto grp = groups.get(evnt.size);
|
||||
grp->events[grp->flag] = evnt;
|
||||
grp->flag++;
|
||||
}
|
||||
|
||||
// initialize additional structure for sorting
|
||||
groups_sorted.reserve(groups.size());
|
||||
for (auto grp : groups) {
|
||||
groups_sorted[grp.entry_idx] = grp->val;
|
||||
}
|
||||
|
||||
// generate timelines
|
||||
for (auto grp : groups_sorted) {
|
||||
grp.data()->timeline.recordEvents(grp.data()->events);
|
||||
grp.data()->timeline.to_seconds();
|
||||
}
|
||||
}
|
||||
|
||||
void openFile() {
|
||||
using namespace tp;
|
||||
File log(filename.cstr(), osfile_openflags::LOAD);
|
||||
|
||||
if (!log.opened) {
|
||||
status = LoadStatus::INVALID_FILE_PATH;
|
||||
return;
|
||||
}
|
||||
|
||||
log.read_bytes(logo_loaded, logo_len);
|
||||
if (!tp::memequal(logo_loaded, logo, logo_len)) {
|
||||
status = LoadStatus::INVALID_FILE_FORMAT;
|
||||
return;
|
||||
}
|
||||
|
||||
log.Preload();
|
||||
|
||||
try {
|
||||
readFile(log);
|
||||
status = LoadStatus::DONE;
|
||||
} catch (...) {
|
||||
status = LoadStatus::INTERNAL_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void HeaderDrawCallback() override {
|
||||
using namespace ImGui;
|
||||
Button("Analize");
|
||||
Button("Save To Header File");
|
||||
}
|
||||
|
||||
void DrawCallback() {
|
||||
|
||||
auto pos = this->mNativeWindow.mAppearence.mSize / 2;
|
||||
if (status != LoadStatus::DONE) {
|
||||
|
||||
nvgFillColor(mNvg, nvgRGB(250, 250, 250));
|
||||
nvgFontSize(mNvg, 24);
|
||||
nvgTextAlign(mNvg, NVG_ALIGN_MIDDLE | NVG_ALIGN_CENTER);
|
||||
|
||||
switch (status) {
|
||||
case LoadStatus::NONE:
|
||||
nvgText(mNvg, pos.x, pos.y, "Loading...", 0);
|
||||
break;
|
||||
|
||||
case LoadStatus::INVALID_FILE_PATH:
|
||||
nvgText(mNvg, pos.x, pos.y, "Invalid File Path", 0);
|
||||
break;
|
||||
|
||||
case LoadStatus::INVALID_FILE_FORMAT:
|
||||
nvgText(mNvg, pos.x, pos.y, "Invalid File Format", 0);
|
||||
break;
|
||||
|
||||
case LoadStatus::INTERNAL_ERROR:
|
||||
nvgText(mNvg, pos.x, pos.y, "Internal Error", 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
drawEditor();
|
||||
}
|
||||
|
||||
enum class GroupSorting {
|
||||
LARGER_SIZE,
|
||||
SMALLER_SIZE,
|
||||
LARGER_TOTAL_SIZE,
|
||||
SMALLER_TOTAL_SIZE,
|
||||
} sorting = GroupSorting::LARGER_SIZE;
|
||||
|
||||
void sortGroups(GroupSorting type) {
|
||||
if (type == sorting) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case Memusage::GroupSorting::LARGER_SIZE:
|
||||
groups_sorted.sort([](Group* const& i1, Group* const& i2) {
|
||||
return i1->group_size > i2->group_size;
|
||||
});
|
||||
break;
|
||||
case Memusage::GroupSorting::SMALLER_SIZE:
|
||||
groups_sorted.sort([](Group* const& i1, Group* const& i2) {
|
||||
return i1->group_size < i2->group_size;
|
||||
});
|
||||
break;
|
||||
case Memusage::GroupSorting::LARGER_TOTAL_SIZE:
|
||||
break;
|
||||
case Memusage::GroupSorting::SMALLER_TOTAL_SIZE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sorting = type;
|
||||
}
|
||||
|
||||
Group* mGroupActive = NULL;
|
||||
|
||||
void groupView() {
|
||||
using namespace ImGui;
|
||||
int tmp;
|
||||
|
||||
const char* names[] = {
|
||||
{"Larger Group Size"},
|
||||
{"Smaller Group Size"},
|
||||
//{"LARGER_TOTAL_SIZE"},
|
||||
//{"SMALER_TOTAL_SIZE"},
|
||||
};
|
||||
|
||||
tmp = (int) sorting;
|
||||
Combo(" ", &tmp, names, 2);
|
||||
Separator();
|
||||
sortGroups((GroupSorting) tmp);
|
||||
|
||||
for (auto grp : groups_sorted) {
|
||||
if (Selectable(tp::string((tp::alni)grp.data()->group_size).cstr())) {
|
||||
mGroupActive = grp.data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawTimeline(const Timeline& timeline) {
|
||||
if (ImPlot::BeginPlot(" Plot ", {-1, -1}, ImPlotFlags_CanvasOnly | ImPlotFlags_AntiAliased)) {
|
||||
ImPlot::PlotLine("Memory Usage", timeline.time.buff(), timeline.size.buff(), (tp::halni) timeline.time.length());
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
}
|
||||
|
||||
void infoView() {
|
||||
using namespace ImGui;
|
||||
if (!mGroupActive) {
|
||||
Text("No Group Selected");
|
||||
return;
|
||||
}
|
||||
|
||||
Text("Total Events : %i", mGroupActive->events.length());
|
||||
|
||||
Separator();
|
||||
|
||||
drawTimeline(mGroupActive->timeline);
|
||||
}
|
||||
|
||||
void drawEditor() {
|
||||
using namespace ImGui;
|
||||
|
||||
Begin("Groups View");
|
||||
groupView();
|
||||
End();
|
||||
|
||||
Begin("Info");
|
||||
infoView();
|
||||
End();
|
||||
}
|
||||
};
|
||||
|
||||
int main(char argc, char* argv[]) {
|
||||
|
||||
if (argc != 2) {
|
||||
printf("invalid arguments");
|
||||
return 0;
|
||||
}
|
||||
|
||||
//ImPlot::
|
||||
|
||||
Memusage::InitializeTypes();
|
||||
|
||||
{
|
||||
Memusage app(argv[1]);
|
||||
|
||||
ImPlot::CreateContext();
|
||||
|
||||
app.run();
|
||||
|
||||
ImPlot::DestroyContext();
|
||||
|
||||
printf("internal error");
|
||||
}
|
||||
|
||||
Memusage::UnInitializeTypes();
|
||||
|
||||
tp::terminate();
|
||||
}
|
||||
401
Allocators/outdated/tools/MemUsage/memusage.h
Normal file
401
Allocators/outdated/tools/MemUsage/memusage.h
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
|
||||
|
||||
#include "GuiWindow.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "implot.h"
|
||||
#include "nanovg.h"
|
||||
|
||||
#include "strings.h"
|
||||
#include "filesystem.h"
|
||||
#include "array.h"
|
||||
#include "list.h"
|
||||
#include "map.h"
|
||||
|
||||
#include "intersections.h"
|
||||
|
||||
#include "pickalloc_analizer.h"
|
||||
#include "pickalloc_cfg.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
class Memusage : public tp::GuiWindow {
|
||||
|
||||
tp::string filename;
|
||||
|
||||
static const char logo_len = 16;
|
||||
const char logo[logo_len] = "memusage\0\0\0\0\0\0\0";
|
||||
char logo_loaded[logo_len];
|
||||
|
||||
PickAllocConfig cfg;
|
||||
|
||||
enum class LoadStatus {
|
||||
NONE,
|
||||
DONE,
|
||||
INVALID_FILE_PATH,
|
||||
INVALID_FILE_FORMAT,
|
||||
INTERNAL_ERROR,
|
||||
} status;
|
||||
|
||||
typedef tp::PickAllocDataAnalizer::Event Event;
|
||||
typedef tp::alni SizeKey;
|
||||
|
||||
struct Timeline {
|
||||
tp::Array<tp::alnf> size;
|
||||
tp::Array<tp::alnf> time;
|
||||
tp::ualni peak = 0;
|
||||
tp::ualni avereging = 0;
|
||||
|
||||
void recordEvents(tp::Array<Event>& events) {
|
||||
tp::ualni total_mem_usage = 0;
|
||||
|
||||
size.extend(events.length());
|
||||
time.extend(events.length());
|
||||
|
||||
for (auto ev : events) {
|
||||
|
||||
if (ev->dealloc) {
|
||||
total_mem_usage -= ev->size;
|
||||
} else {
|
||||
total_mem_usage += ev->size;
|
||||
}
|
||||
|
||||
time[ev.idx()] = (tp::alnf) ev->time;
|
||||
size[ev.idx()] = (tp::alnf) total_mem_usage;
|
||||
|
||||
if (peak < total_mem_usage) {
|
||||
peak = total_mem_usage;
|
||||
}
|
||||
}
|
||||
|
||||
avereging = 1;
|
||||
}
|
||||
|
||||
void to_seconds() {
|
||||
for (auto tm : time) {
|
||||
tm.data() /= 1000;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Contains events of fixed size
|
||||
struct Group {
|
||||
tp::ualni group_size = 0;
|
||||
tp::Array<Event> events;
|
||||
|
||||
tp::ualni flag = 0;
|
||||
|
||||
Timeline timeline;
|
||||
|
||||
Group(tp::ualni grp_size) : group_size(grp_size) {}
|
||||
};
|
||||
|
||||
tp::HashMap<Group*, SizeKey> groups;
|
||||
tp::Array<Group*> groups_sorted;
|
||||
|
||||
public:
|
||||
|
||||
Memusage(const char* filename) : GuiWindow() {
|
||||
|
||||
this->mHeader.mAppTitle = "Mamusage Analizer";
|
||||
this->mHeader.mPadding = 10;
|
||||
tp::string file_path = __FILE__;
|
||||
tp::make_dir_str(file_path.get_writable());
|
||||
tp::make_dir_str(file_path.get_writable());
|
||||
this->mRscDirectory = file_path + "/";
|
||||
this->mGuiRootIsWindow = false;
|
||||
LoadFonts();
|
||||
|
||||
this->filename = filename;
|
||||
}
|
||||
|
||||
~Memusage() {
|
||||
for (auto tl : groups) {
|
||||
delete tl->val;
|
||||
}
|
||||
|
||||
if (cfg.mAllocationSizes) delete[] cfg.mAllocationSizes;
|
||||
if (cfg.mChunkLengths) delete[] cfg.mChunkLengths;
|
||||
}
|
||||
|
||||
void run() {
|
||||
|
||||
// display status
|
||||
for (auto i : tp::Range(2)) {
|
||||
procInputs();
|
||||
beginDraw();
|
||||
if (mNativeWindow.mEvents.mRedraw) {
|
||||
DrawCallback();
|
||||
}
|
||||
endDraw();
|
||||
mNativeWindow.mEvents.mRedraw = true;
|
||||
}
|
||||
|
||||
while (!mNativeWindow.mEvents.mTerminate) {
|
||||
tp::Timer timer(5);
|
||||
procInputs();
|
||||
beginDraw();
|
||||
if (mNativeWindow.mEvents.mRedraw) {
|
||||
DrawCallback();
|
||||
}
|
||||
endDraw();
|
||||
timer.wait();
|
||||
|
||||
if (status == LoadStatus::NONE) {
|
||||
openFile();
|
||||
mNativeWindow.mEvents.mRedraw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
tp::ualni fileContentSize(tp::File& file) {
|
||||
return file.size() - fileContentStartIdx();
|
||||
}
|
||||
|
||||
tp::ualni fileContentStartIdx() {
|
||||
return logo_len;
|
||||
}
|
||||
|
||||
void readFile(tp::File& file) {
|
||||
using namespace tp;
|
||||
|
||||
// read cfg
|
||||
file.read(&cfg.mCapacity);
|
||||
cfg.mAllocationSizes = new tp::int4[cfg.mCapacity];
|
||||
cfg.mChunkLengths = new tp::int4[cfg.mCapacity];
|
||||
|
||||
file.read_bytes((tp::int1*)cfg.mAllocationSizes, sizeof(tp::int4) * cfg.mCapacity);
|
||||
file.read_bytes((tp::int1*)cfg.mChunkLengths, sizeof(tp::int4) * cfg.mCapacity);
|
||||
|
||||
// read evens
|
||||
tp::ualni events_length;
|
||||
file.read(&events_length);
|
||||
|
||||
// one pass to collect data about groups in 'flag'
|
||||
auto events_adress = file.adress;
|
||||
for (tp::ualni i = 0; i < events_length; i++) {
|
||||
Event evnt;
|
||||
file.read(&evnt);
|
||||
auto idx = groups.presents(evnt.size);
|
||||
Group* group = NULL;
|
||||
if (idx) {
|
||||
group = groups.getSlotVal(idx);
|
||||
} else {
|
||||
group = new Group(evnt.size);
|
||||
groups.put(evnt.size, group);
|
||||
}
|
||||
group->flag++;
|
||||
}
|
||||
for (auto grp : groups) {
|
||||
grp->val->events.reserve(grp->val->flag);
|
||||
grp->val->flag = 0;
|
||||
}
|
||||
file.adress = events_adress;
|
||||
|
||||
// read actual events
|
||||
for (tp::ualni idx = 0; idx < events_length; idx++) {
|
||||
Event evnt;
|
||||
file.read(&evnt);
|
||||
auto grp = groups.get(evnt.size);
|
||||
grp->events[grp->flag] = evnt;
|
||||
grp->flag++;
|
||||
}
|
||||
|
||||
// initialize additional structure for sorting
|
||||
groups_sorted.reserve(groups.size());
|
||||
for (auto grp : groups) {
|
||||
groups_sorted[grp.entry_idx] = grp->val;
|
||||
}
|
||||
|
||||
// generate timelines
|
||||
for (auto grp : groups_sorted) {
|
||||
grp.data()->timeline.recordEvents(grp.data()->events);
|
||||
grp.data()->timeline.to_seconds();
|
||||
}
|
||||
}
|
||||
|
||||
void openFile() {
|
||||
using namespace tp;
|
||||
File log(filename.cstr(), osfile_openflags::LOAD);
|
||||
|
||||
if (!log.opened) {
|
||||
status = LoadStatus::INVALID_FILE_PATH;
|
||||
return;
|
||||
}
|
||||
|
||||
log.read_bytes(logo_loaded, logo_len);
|
||||
if (!tp::memequal(logo_loaded, logo, logo_len)) {
|
||||
status = LoadStatus::INVALID_FILE_FORMAT;
|
||||
return;
|
||||
}
|
||||
|
||||
log.Preload();
|
||||
|
||||
try {
|
||||
readFile(log);
|
||||
status = LoadStatus::DONE;
|
||||
} catch (...) {
|
||||
status = LoadStatus::INTERNAL_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void HeaderDrawCallback() override {
|
||||
using namespace ImGui;
|
||||
Button("Analize");
|
||||
Button("Save To Header File");
|
||||
}
|
||||
|
||||
void DrawCallback() {
|
||||
|
||||
auto pos = this->mNativeWindow.mAppearence.mSize / 2;
|
||||
if (status != LoadStatus::DONE) {
|
||||
|
||||
nvgFillColor(mNvg, nvgRGB(250, 250, 250));
|
||||
nvgFontSize(mNvg, 24);
|
||||
nvgTextAlign(mNvg, NVG_ALIGN_MIDDLE | NVG_ALIGN_CENTER);
|
||||
|
||||
switch (status) {
|
||||
case LoadStatus::NONE:
|
||||
nvgText(mNvg, pos.x, pos.y, "Loading...", 0);
|
||||
break;
|
||||
|
||||
case LoadStatus::INVALID_FILE_PATH:
|
||||
nvgText(mNvg, pos.x, pos.y, "Invalid File Path", 0);
|
||||
break;
|
||||
|
||||
case LoadStatus::INVALID_FILE_FORMAT:
|
||||
nvgText(mNvg, pos.x, pos.y, "Invalid File Format", 0);
|
||||
break;
|
||||
|
||||
case LoadStatus::INTERNAL_ERROR:
|
||||
nvgText(mNvg, pos.x, pos.y, "Internal Error", 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
drawEditor();
|
||||
}
|
||||
|
||||
enum class GroupSorting {
|
||||
LARGER_SIZE,
|
||||
SMALLER_SIZE,
|
||||
LARGER_TOTAL_SIZE,
|
||||
SMALLER_TOTAL_SIZE,
|
||||
} sorting = GroupSorting::LARGER_SIZE;
|
||||
|
||||
void sortGroups(GroupSorting type) {
|
||||
if (type == sorting) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case Memusage::GroupSorting::LARGER_SIZE:
|
||||
groups_sorted.sort([](Group* const& i1, Group* const& i2) {
|
||||
return i1->group_size > i2->group_size;
|
||||
});
|
||||
break;
|
||||
case Memusage::GroupSorting::SMALLER_SIZE:
|
||||
groups_sorted.sort([](Group* const& i1, Group* const& i2) {
|
||||
return i1->group_size < i2->group_size;
|
||||
});
|
||||
break;
|
||||
case Memusage::GroupSorting::LARGER_TOTAL_SIZE:
|
||||
break;
|
||||
case Memusage::GroupSorting::SMALLER_TOTAL_SIZE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sorting = type;
|
||||
}
|
||||
|
||||
Group* mGroupActive = NULL;
|
||||
|
||||
void groupView() {
|
||||
using namespace ImGui;
|
||||
int tmp;
|
||||
|
||||
const char* names[] = {
|
||||
{"Larger Group Size"},
|
||||
{"Smaller Group Size"},
|
||||
//{"LARGER_TOTAL_SIZE"},
|
||||
//{"SMALER_TOTAL_SIZE"},
|
||||
};
|
||||
|
||||
tmp = (int) sorting;
|
||||
Combo(" ", &tmp, names, 2);
|
||||
Separator();
|
||||
sortGroups((GroupSorting) tmp);
|
||||
|
||||
for (auto grp : groups_sorted) {
|
||||
if (Selectable(tp::string((tp::alni)grp.data()->group_size).cstr())) {
|
||||
mGroupActive = grp.data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawTimeline(const Timeline& timeline) {
|
||||
if (ImPlot::BeginPlot(" Plot ", {-1, -1}, ImPlotFlags_CanvasOnly | ImPlotFlags_AntiAliased)) {
|
||||
ImPlot::PlotLine("Memory Usage", timeline.time.buff(), timeline.size.buff(), (tp::halni) timeline.time.length());
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
}
|
||||
|
||||
void infoView() {
|
||||
using namespace ImGui;
|
||||
if (!mGroupActive) {
|
||||
Text("No Group Selected");
|
||||
return;
|
||||
}
|
||||
|
||||
Text("Total Events : %i", mGroupActive->events.length());
|
||||
|
||||
Separator();
|
||||
|
||||
drawTimeline(mGroupActive->timeline);
|
||||
}
|
||||
|
||||
void drawEditor() {
|
||||
using namespace ImGui;
|
||||
|
||||
Begin("Groups View");
|
||||
groupView();
|
||||
End();
|
||||
|
||||
Begin("Info");
|
||||
infoView();
|
||||
End();
|
||||
}
|
||||
};
|
||||
|
||||
int main(char argc, char* argv[]) {
|
||||
|
||||
if (argc != 2) {
|
||||
printf("invalid arguments");
|
||||
return 0;
|
||||
}
|
||||
|
||||
//ImPlot::
|
||||
|
||||
Memusage::InitializeTypes();
|
||||
|
||||
{
|
||||
Memusage app(argv[1]);
|
||||
|
||||
ImPlot::CreateContext();
|
||||
|
||||
app.run();
|
||||
|
||||
ImPlot::DestroyContext();
|
||||
|
||||
printf("internal error");
|
||||
}
|
||||
|
||||
Memusage::UnInitializeTypes();
|
||||
|
||||
tp::terminate();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue