This commit is contained in:
Ilusha 2024-03-15 23:42:10 +03:00
parent 00d8fa0886
commit 65cb26a627
34 changed files with 288 additions and 264 deletions

View file

@ -5,11 +5,11 @@ file(GLOB SOURCES "./private/*.cpp")
file(GLOB HEADERS "./public/*.hpp") file(GLOB HEADERS "./public/*.hpp")
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS}) add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC ./public/) target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
target_link_libraries(${PROJECT_NAME} PUBLIC Utils) target_link_libraries(${PROJECT_NAME} PUBLIC Callstack)
### -------------------------- Tests -------------------------- ### ### -------------------------- Tests -------------------------- ###
enable_testing() enable_testing()
file(GLOB TEST_SOURCES "./tests/*.cpp") file(GLOB TEST_SOURCES "./tests/*.cpp")
add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES}) add_executable(Tests${PROJECT_NAME} ${TEST_SOURCES})
target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} UnitTest++) target_link_libraries(Tests${PROJECT_NAME} ${PROJECT_NAME} UnitTest++)
add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests) add_test(NAME Tests${PROJECT_NAME} COMMAND Tests${PROJECT_NAME})

View file

@ -13,9 +13,11 @@ static bool init(const tp::ModuleManifest* self) {
return true; return true;
} }
static void deinit(const tp::ModuleManifest* self) { tp::HeapAllocGlobal::checkLeaks(); } static void deinit(const tp::ModuleManifest* self) {
tp::HeapAllocGlobal::checkLeaks();
}
static tp::ModuleManifest* sModuleDependencies[] = { &tp::gModuleUtils, nullptr }; static tp::ModuleManifest* sModuleDependencies[] = { nullptr };
tp::ModuleManifest tp::gModuleAllocators = ModuleManifest("Allocators", init, deinit, sModuleDependencies); tp::ModuleManifest tp::gModuleAllocators = ModuleManifest("Allocators", init, deinit, sModuleDependencies);
void* operator new(size_t aSize) { return tp::HeapAllocGlobal::allocate(aSize); } void* operator new(size_t aSize) { return tp::HeapAllocGlobal::allocate(aSize); }

View file

@ -3,7 +3,7 @@
#include "HeapAllocatorGlobal.hpp" #include "HeapAllocatorGlobal.hpp"
#include "PrivateConfig.hpp" #include "PrivateConfig.hpp"
#include "Debugging.hpp" #include "Callstack.hpp"
#include "Utils.hpp" #include "Utils.hpp"
#include <cstdio> #include <cstdio>
@ -33,7 +33,11 @@ tp::ualni tp::HeapAllocGlobal::mNumAllocations = 0;
std::mutex tp::HeapAllocGlobal::mMutex; std::mutex tp::HeapAllocGlobal::mMutex;
bool tp::HeapAllocGlobal::mIgnore = true; bool tp::HeapAllocGlobal::mIgnore = true;
// ----------------------- Debug Implementation ---------------------------- // #ifdef MEM_STACK_TRACE
tp::CallStackCapture tp::HeapAllocGlobal::mCallstack;
#endif
// ----------------------- Debug Implementation ---------------------------- //
// |----------------| // |----------------|
// | MemHead | // | MemHead |
// |----------------| // |----------------|
@ -105,8 +109,7 @@ ALLOCATE:
// 3) Trace the stack // 3) Trace the stack
#ifdef MEM_STACK_TRACE #ifdef MEM_STACK_TRACE
// check if somewhat decides to call new within static variable initialization // check if somewhat decides to call new within static variable initialization
if (gCSCapture) head->mCallStack = gCSCapture->getSnapshot(); head->mCallStack = mCallstack.getSnapshot();
else head->mCallStack = nullptr;
#endif #endif
mMutex.unlock(); mMutex.unlock();
@ -146,12 +149,16 @@ void HeapAllocGlobal::deallocate(void* aPtr) {
if (!head->mIgnored) { if (!head->mIgnored) {
// 3) Check the wrap // 3) Check the wrap
if (memCompareVal(wrap_top, WRAP_SIZE, WRAP_VAL)) { if (memCompareVal(wrap_top, WRAP_SIZE, WRAP_VAL)) {
CallStackCapture::printSnapshot(head->mCallStack); #ifdef MEM_STACK_TRACE
mCallstack.printSnapshot(head->mCallStack);
#endif
ASSERT(!"Allocated Block Wrap Corrupted!") ASSERT(!"Allocated Block Wrap Corrupted!")
} }
if (memCompareVal(wrap_bottom, WRAP_SIZE, WRAP_VAL)) { if (memCompareVal(wrap_bottom, WRAP_SIZE, WRAP_VAL)) {
CallStackCapture::printSnapshot(head->mCallStack); #ifdef MEM_STACK_TRACE
mCallstack.printSnapshot(head->mCallStack);
#endif
ASSERT(!"Allocated Block Wrap Corrupted!") ASSERT(!"Allocated Block Wrap Corrupted!")
} }
@ -176,7 +183,7 @@ bool HeapAllocGlobal::checkLeaks() {
#ifdef MEM_STACK_TRACE #ifdef MEM_STACK_TRACE
for (auto iter = mEntry; iter; iter = iter->mPrev) { for (auto iter = mEntry; iter; iter = iter->mPrev) {
if (!iter->mIgnored) CallStackCapture::printSnapshot(iter->mCallStack); if (!iter->mIgnored) mCallstack.printSnapshot(iter->mCallStack);
} }
#endif #endif

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "Module.hpp" #include "Callstack.hpp"
#include <mutex> #include <mutex>
namespace tp { namespace tp {
@ -12,6 +12,9 @@ namespace tp {
static struct MemHead* mEntry; static struct MemHead* mEntry;
static std::mutex mMutex; static std::mutex mMutex;
static bool mIgnore; static bool mIgnore;
#ifdef MEM_STACK_TRACE // Save stack on allocation call
static CallStackCapture mCallstack;
#endif
#endif #endif
public: public:

View file

@ -6,5 +6,4 @@
#define MEM_CLEAR_ON_DEALLOC // Clear data on free #define MEM_CLEAR_ON_DEALLOC // Clear data on free
#define MEM_CLEAR_ON_DEALLOC_VAL 0xAA // 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_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 #define MEM_STACK_TRACE_MAX_DEPTH 32 // Call stack max depth

View file

@ -234,7 +234,7 @@ SUITE(Allocators) {
int main() { int main() {
tp::ModuleManifest* deps[] = { &tp::gModuleAllocators, &tp::gModuleUtils, nullptr }; tp::ModuleManifest* deps[] = { &tp::gModuleAllocators, nullptr };
tp::ModuleManifest testModule("AllocatorsTest", nullptr, nullptr, deps); tp::ModuleManifest testModule("AllocatorsTest", nullptr, nullptr, deps);
if (!testModule.initialize()) { if (!testModule.initialize()) {

View file

@ -12,7 +12,7 @@ project(ModulesRoot)
include(CMakeOptions.txt) include(CMakeOptions.txt)
add_subdirectory(Modules) add_subdirectory(Modules)
add_subdirectory(Utils) add_subdirectory(Callstack)
add_subdirectory(Containers) add_subdirectory(Containers)
add_subdirectory(Math) add_subdirectory(Math)
add_subdirectory(Allocators) add_subdirectory(Allocators)

View file

@ -1,4 +1,5 @@
option(MODULES_MEMORY_DEBUG "Debug memory" ON) option(MODULES_MEMORY_DEBUG "Debug memory" ON)
option(MODULES_MEMORY_DEBUG_STACK_TRACE "Record stack info on memory debug" ON)
set(WINDOWS_LIBRARIES "../../ModulesWindowsLibraries" CACHE STRING "Svn repository with windows libraries https://svn.riouxsvn.com/moduleswindowsl") set(WINDOWS_LIBRARIES "../../ModulesWindowsLibraries" CACHE STRING "Svn repository with windows libraries https://svn.riouxsvn.com/moduleswindowsl")
@ -6,4 +7,8 @@ if (MODULES_MEMORY_DEBUG)
add_compile_definitions(MEM_DEBUG) add_compile_definitions(MEM_DEBUG)
endif () endif ()
if (MODULES_MEMORY_DEBUG_STACK_TRACE)
add_compile_definitions(MEM_STACK_TRACE)
endif ()
enable_testing() enable_testing()

View file

@ -1,16 +1,14 @@
project(Utils) project(Callstack)
### ---------------------- Static Library --------------------- ### ### ---------------------- Static Library --------------------- ###
file(GLOB SOURCES "./private/*.cpp") file(GLOB SOURCES "./private/*.cpp")
add_library(${PROJECT_NAME} STATIC ${SOURCES}) add_library(${PROJECT_NAME} STATIC ${SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC ./public/) target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
### ---------------------- Dependencies --------------------- ###
target_link_libraries(${PROJECT_NAME} PUBLIC Containers) target_link_libraries(${PROJECT_NAME} PUBLIC Containers)
### -------------------------- Tests -------------------------- ### ### -------------------------- Tests -------------------------- ###
enable_testing()
file(GLOB TEST_SOURCES "./tests/*.cpp") file(GLOB TEST_SOURCES "./tests/*.cpp")
add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES}) add_executable(Tests${PROJECT_NAME} ${TEST_SOURCES})
target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} UnitTest++) target_link_libraries(Tests${PROJECT_NAME} ${PROJECT_NAME} UnitTest++)
add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests) add_test(NAME Tests${PROJECT_NAME} COMMAND Tests${PROJECT_NAME})

View file

@ -1,19 +1,15 @@
#include "Debugging.hpp" #include "Callstack.hpp"
#include "Utils.hpp"
#include <iostream> #include <iostream>
using namespace tp; using namespace tp;
CallStackCapture* tp::gCSCapture = nullptr; // frame to reference on no sapce left
static void errorNoSpaceLeft() {
void initializeCallStackCapture() { gCSCapture = new (malloc(sizeof(CallStackCapture))) CallStackCapture(); } // to disable optimization of function
static int dummy;
void deinitializeCallStackCapture() { dummy = 10;
gCSCapture->~CallStackCapture();
free(gCSCapture);
gCSCapture = nullptr;
} }
ualni CallStackCapture::CallStack::getDepth() const { ualni CallStackCapture::CallStack::getDepth() const {
@ -24,14 +20,7 @@ ualni CallStackCapture::CallStack::getDepth() const {
} }
len++; len++;
} }
ualni stripedLen = 0; return len++;
for (auto i = FRAMES_TO_SKIP_START; i < len - FRAMES_TO_SKIP_END; i++) {
if (!frames[i]) {
break;
}
stripedLen++;
}
return stripedLen;
} }
ualni CallStackCapture::hashCallStack(CallStackKey key) { ualni CallStackCapture::hashCallStack(CallStackKey key) {
@ -44,7 +33,7 @@ ualni CallStackCapture::hashCallStack(CallStackKey key) {
} }
bool CallStackCapture::CallStackKey::operator==(const CallStackCapture::CallStackKey& in) const { bool CallStackCapture::CallStackKey::operator==(const CallStackCapture::CallStackKey& in) const {
for (auto i : Range(MAX_CALL_DEPTH_CAPTURE)) { for (ualni i = 0; i < MAX_CALL_DEPTH_CAPTURE; i++) {
if (cs->frames[i] != in.cs->frames[i]) { if (cs->frames[i] != in.cs->frames[i]) {
return false; return false;
} }
@ -60,26 +49,26 @@ CallStackCapture::CallStackCapture() {
static_assert(MAX_CALL_DEPTH_CAPTURE >= 1); static_assert(MAX_CALL_DEPTH_CAPTURE >= 1);
static_assert(MAX_CALL_CAPTURES_MEM_SIZE_MB > 0); static_assert(MAX_CALL_CAPTURES_MEM_SIZE_MB > 0);
static_assert(MAX_DEBUG_INFO_LEN > sizeof("unresolved")); static_assert(MAX_DEBUG_INFO_LEN > sizeof("unresolved"));
static_assert(FRAMES_TO_SKIP_END >= 0 && FRAMES_TO_SKIP_START >= 0);
static_assert(MAX_CALL_DEPTH_CAPTURE > FRAMES_TO_SKIP_START + FRAMES_TO_SKIP_END);
static_assert(MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024 > sizeof(CallStack)); static_assert(MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024 > sizeof(CallStack));
MODULE_SANITY_CHECK(gModuleUtils)
mBuffLoad = 0; mBuffLoad = 0;
mBuffLen = (MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024) / sizeof(CallStack); mBuffLen = STACKS_LENGTH;
mBuff = (CallStack*) malloc(mBuffLen * sizeof(CallStack)); mBuff = (CallStack*) malloc(mBuffLen * sizeof(CallStack));
mErrorSnapshot.frames[0] = (alni) & errorNoSpaceLeft;
platformInit();
} }
const CallStackCapture::CallStack* CallStackCapture::getSnapshot() { const CallStackCapture::CallStack* CallStackCapture::getSnapshot() {
MODULE_SANITY_CHECK(gModuleUtils)
if (mBuffLoad > mBuffLen) { if (mBuffLoad > mBuffLen) {
static CallStack cs; return &mErrorSnapshot;
cs.frames[0] = 0;
return &cs;
} }
CallStack* cs = &mBuff[mBuffLoad]; CallStack* cs = &mBuff[mBuffLoad];
platformWriteStackTrace(cs); platformWriteStackTrace(cs);
auto idx = mSnapshots.presents({ cs }); auto idx = mSnapshots.presents({ cs });
@ -213,7 +202,7 @@ void CallStackCapture::printSnapshot(const CallStack* snapshot) {
printf("CallStack: \n"); printf("CallStack: \n");
if (snapshot) { if (snapshot) {
for (auto frame : *snapshot) { for (auto frame : *snapshot) {
auto symbols = gCSCapture->getSymbols(frame.getFrame()); auto symbols = getSymbols(frame.getFrame());
printf(" %s ----- %s:%llu\n", symbols->getFunc(), symbols->getFile(), symbols->getLine()); printf(" %s ----- %s:%llu\n", symbols->getFunc(), symbols->getFile(), symbols->getLine());
} }
} }
@ -227,15 +216,112 @@ void CallStackCapture::logAll() {
} }
#else #else
void CallStackCapture::platformWriteStackTrace(CallStack* stack) { stack->frames[0] = 0; } #include <windows.h>
void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) { #include <dbghelp.h>
std::strcpy(out->file, "unresolved"); #include <stdio.h>
std::strcpy(out->function, "unresolved");
#pragma comment(lib, "dbghelp.lib")
void CallStackCapture::platformInit() {
HANDLE process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
} }
void CallStackCapture::platformDeinit() {
HANDLE process = GetCurrentProcess();
SymCleanup(process);
}
void CallStackCapture::platformWriteStackTrace(CallStack* stack) {
DWORD hash;
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
STACKFRAME64 stackFrame;
memset(&stackFrame, 0, sizeof(STACKFRAME64));
#ifdef _M_IX86
hash = IMAGE_FILE_MACHINE_I386;
stackFrame.AddrPC.Offset = context.Eip;
stackFrame.AddrPC.Mode = AddrModeFlat;
stackFrame.AddrFrame.Offset = context.Ebp;
stackFrame.AddrFrame.Mode = AddrModeFlat;
stackFrame.AddrStack.Offset = context.Esp;
stackFrame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
hash = IMAGE_FILE_MACHINE_AMD64;
stackFrame.AddrPC.Offset = context.Rip;
stackFrame.AddrPC.Mode = AddrModeFlat;
stackFrame.AddrFrame.Offset = context.Rbp;
stackFrame.AddrFrame.Mode = AddrModeFlat;
stackFrame.AddrStack.Offset = context.Rsp;
stackFrame.AddrStack.Mode = AddrModeFlat;
#endif
for (int frameIndex = 0; frameIndex < MAX_CALL_DEPTH_CAPTURE; ++frameIndex) {
if (!StackWalk64(
hash,
GetCurrentProcess(),
GetCurrentThread(),
&stackFrame,
&context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL
)) {
stack->frames[frameIndex] = 0;
break;
}
stack->frames[frameIndex] = (alni) (void*) stackFrame.AddrPC.Offset;
}
stack->frames[MAX_CALL_DEPTH_CAPTURE - 1] = 0;
}
void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) {
HANDLE process = GetCurrentProcess();
IMAGEHLP_LINE64 lineInfo;
DWORD displacement;
lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, (DWORD64) frame, &displacement, &lineInfo)) {
strcpy(out->file, lineInfo.FileName);
out->line = lineInfo.LineNumber;
} else {
strcpy(out->file, "unresolved");
out->line = 0;
}
DWORD64 displacementSym = 0;
char symbolBuffer[sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH] = { 0 };
IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*) symbolBuffer;
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
symbol->MaxNameLength = MAX_PATH;
if (SymGetSymFromAddr64(process, (DWORD64) frame, &displacementSym, symbol)) {
strcpy(out->function, symbol->Name);
} else {
strcpy(out->function, "unresolved");
}
}
void CallStackCapture::printSnapshot(const CallStack* snapshot) { void CallStackCapture::printSnapshot(const CallStack* snapshot) {
printf("CallStack: \n"); printf("CallStack: \n");
printf("Call stack debugging is not supported on this platform"); if (snapshot) {
for (int i = 0; i < snapshot->getDepth(); ++i) {
DebugSymbols symbols;
platformWriteDebugSymbols(snapshot->frames[i], &symbols);
printf(" %s ----- %s:%lu\n", symbols.function, symbols.file, symbols.line);
}
}
printf("\n"); printf("\n");
} }
void CallStackCapture::logAll() { printf("Call stack debugging is not supported on this platform"); }
void CallStackCapture::logAll() {
for (auto cs : *this) {
printSnapshot(cs.getCallStack());
}
}
#endif #endif

View file

@ -3,18 +3,18 @@
#include "Environment.hpp" #include "Environment.hpp"
#include "Map.hpp" #include "Map.hpp"
#define MAX_CALL_DEPTH_CAPTURE 128
#define MAX_CALL_CAPTURES_MEM_SIZE_MB 16
#define MAX_DEBUG_INFO_LEN 63
#define FRAMES_TO_SKIP_START 2
#define FRAMES_TO_SKIP_END 3
namespace tp { namespace tp {
class CallStackCapture { class CallStackCapture {
public: public:
typedef tp::alni FramePointer; typedef tp::alni FramePointer;
enum {
MAX_CALL_DEPTH_CAPTURE = 16,
MAX_CALL_CAPTURES_MEM_SIZE_MB = 32,
MAX_DEBUG_INFO_LEN = 64,
};
class CallStack { class CallStack {
friend CallStackCapture; friend CallStackCapture;
FramePointer frames[MAX_CALL_DEPTH_CAPTURE]; FramePointer frames[MAX_CALL_DEPTH_CAPTURE];
@ -26,18 +26,19 @@ namespace tp {
const FramePointer* mFrame; const FramePointer* mFrame;
public: public:
explicit Iterator(const FramePointer* frame) : explicit Iterator(const FramePointer* frame) : mFrame(frame){};
mFrame(frame){};
FramePointer getFrame() { return *mFrame; } FramePointer getFrame() { return *mFrame; }
bool operator==(const Iterator& in) const { return in.mFrame == mFrame; } bool operator==(const Iterator& in) const { return in.mFrame == mFrame; }
void operator++() { mFrame++; } void operator++() { mFrame++; }
const Iterator& operator*() const { return *this; } const Iterator& operator*() const { return *this; }
}; };
[[nodiscard]] Iterator begin() const { return Iterator(frames + FRAMES_TO_SKIP_START); } [[nodiscard]] Iterator begin() const { return Iterator(frames); }
[[nodiscard]] Iterator end() const { return Iterator(frames + FRAMES_TO_SKIP_START + getDepth()); } [[nodiscard]] Iterator end() const { return Iterator(frames + getDepth()); }
}; };
enum { STACKS_LENGTH = (MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024) / sizeof(CallStack) };
class DebugSymbols { class DebugSymbols {
friend CallStackCapture; friend CallStackCapture;
char function[MAX_DEBUG_INFO_LEN + 1] = { 0 }; char function[MAX_DEBUG_INFO_LEN + 1] = { 0 };
@ -57,44 +58,15 @@ namespace tp {
[[nodiscard]] const CallStack* getSnapshot(); [[nodiscard]] const CallStack* getSnapshot();
const DebugSymbols* getSymbols(FramePointer fp); const DebugSymbols* getSymbols(FramePointer fp);
static void printSnapshot(const CallStack* snapshot); void printSnapshot(const CallStack* snapshot);
void logAll(); void logAll();
public:
template <class Saver>
void write(Saver& file) {
file.write(mBuffLoad);
for (auto cs : *this) {
file.write(cs.getCallStack()->getDepth());
for (auto frame : *cs.getCallStack()) {
file.write((ualni) frame.getFrame());
}
}
file.write(mSymbols);
}
// independent of the configuration
template <class Loader>
void read(Loader& file) {
clear();
ualni loadLen;
file.read(loadLen);
for (auto cs = loadLen; cs; cs--) {
ualni callStackLen;
file.read(callStackLen);
for (auto fp = callStackLen; fp; fp--) {
// --
}
}
}
public: public:
class Iterator { class Iterator {
const CallStack* mSnapshot; const CallStack* mSnapshot;
public: public:
explicit Iterator(const CallStack* start) : explicit Iterator(const CallStack* start) : mSnapshot(start) {};
mSnapshot(start){};
const CallStack* getCallStack() { return mSnapshot; } const CallStack* getCallStack() { return mSnapshot; }
bool operator==(const Iterator& in) const { return in.mSnapshot == mSnapshot; } bool operator==(const Iterator& in) const { return in.mSnapshot == mSnapshot; }
void operator++() { mSnapshot++; } void operator++() { mSnapshot++; }
@ -113,16 +85,19 @@ namespace tp {
static void platformWriteStackTrace(CallStack* stack); static void platformWriteStackTrace(CallStack* stack);
static void platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out); static void platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out);
[[nodiscard]] static ualni hashCallStack(CallStackKey key); [[nodiscard]] static ualni hashCallStack(CallStackKey key);
void platformInit();
void platformDeinit();
void clear(); void clear();
private: private:
CallStack mErrorSnapshot;
ualni mBuffLen; ualni mBuffLen;
ualni mBuffLoad; ualni mBuffLoad;
CallStack* mBuff; CallStack* mBuff;
Map<CallStackKey, CallStack*, DefaultAllocator, hashCallStack> mSnapshots; Map<CallStackKey, CallStack*, DefaultAllocator, hashCallStack> mSnapshots;
Map<FramePointer, DebugSymbols> mSymbols; Map<FramePointer, DebugSymbols> mSymbols;
}; };
extern CallStackCapture* gCSCapture;
} }

44
Callstack/tests/Test.cpp Normal file
View file

@ -0,0 +1,44 @@
#include "UnitTest++/UnitTest++.h"
#include "Callstack.hpp"
#include <cstdio>
using namespace tp;
void common(CallStackCapture& cs) { auto tmp = cs.getSnapshot(); }
void first(CallStackCapture& cs) {
common(cs);
common(cs);
common(cs);
}
void second(CallStackCapture& cs) {
common(cs);
common(cs);
common(cs);
common(cs);
}
void third(CallStackCapture& cs) {
common(cs);
common(cs);
}
void root(CallStackCapture& cs) {
first(cs);
second(cs);
third(cs);
}
SUITE(Utils) {
TEST(CallStackCapture) {
CallStackCapture callstack;
root(callstack);
callstack.logAll();
}
}
int main() { return UnitTest::RunAllTests(); }

View file

@ -8,8 +8,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
target_link_libraries(${PROJECT_NAME} PUBLIC Modules) target_link_libraries(${PROJECT_NAME} PUBLIC Modules)
### -------------------------- Tests -------------------------- ### ### -------------------------- Tests -------------------------- ###
enable_testing()
file(GLOB TEST_SOURCES "./tests/*.cpp") file(GLOB TEST_SOURCES "./tests/*.cpp")
add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES}) add_executable(Tests${PROJECT_NAME} ${TEST_SOURCES})
target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Allocators UnitTest++) target_link_libraries(Tests${PROJECT_NAME} ${PROJECT_NAME} UnitTest++)
add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests) add_test(NAME Tests${PROJECT_NAME} COMMAND Tests${PROJECT_NAME})

View file

@ -5,9 +5,6 @@
namespace tp { namespace tp {
static ModuleManifest* sModuleDependencies[] = { &gModuleBase, nullptr };
ModuleManifest gModuleContainers = ModuleManifest("Containers", nullptr, nullptr, sModuleDependencies);
void* DefaultAllocator::allocate(ualni size) { return malloc(size); } void* DefaultAllocator::allocate(ualni size) { return malloc(size); }
void DefaultAllocator::deallocate(void* p) { free(p); } void DefaultAllocator::deallocate(void* p) { free(p); }

View file

@ -13,7 +13,12 @@ namespace tp {
class Buffer2D { class Buffer2D {
public: public:
typedef ualni Index; typedef ualni Index;
typedef Pair<Index, Index> Index2D;
struct Index2D {
Index x = 0;
Index y = 0;
};
typedef SelectValueOrReference<tType> tTypeArg; typedef SelectValueOrReference<tType> tTypeArg;
private: private:

View file

@ -4,7 +4,6 @@
#include "Module.hpp" #include "Module.hpp"
namespace tp { namespace tp {
extern ModuleManifest gModuleContainers;
class DefaultAllocator { class DefaultAllocator {
public: public:

View file

@ -164,7 +164,6 @@ namespace tp {
public: public:
Map() { Map() {
MODULE_SANITY_CHECK(gModuleContainers)
mNSlots = next2pow(uhalni(tTableInitialSize - 1)); mNSlots = next2pow(uhalni(tTableInitialSize - 1));
mTable = newTable(mNSlots); mTable = newTable(mNSlots);
} }
@ -243,7 +242,13 @@ namespace tp {
if (this == &in) { if (this == &in) {
return *this; return *this;
} }
removeAll();
for (ualni i = 0; i < mNSlots; i++) {
if (mTable[i] && !isDeletedNode(mTable[i])) {
deleteNode(mTable[i]);
}
}
mNEntries = 0;
mNSlots = in.mNSlots; mNSlots = in.mNSlots;
deleteTable(mTable); deleteTable(mTable);
mTable = newTable(mNSlots); mTable = newTable(mNSlots);

View file

@ -62,7 +62,7 @@ namespace tp {
}; };
public: public:
AvlTree() { MODULE_SANITY_CHECK(gModuleContainers) } AvlTree() {}
~AvlTree() { removeAll(); } ~AvlTree() { removeAll(); }
[[nodiscard]] ualni size() const { return mSize; } [[nodiscard]] ualni size() const { return mSize; }

View file

@ -1,15 +1,13 @@
#include "Buffer2D.hpp" #include "Buffer2D.hpp"
#include "Tests.hpp" #include "Tests.hpp"
#include "HeapAllocator.hpp"
using namespace tp; using namespace tp;
const ualni size = 1000; const ualni size = 1000;
SUITE(Buffer2D) { SUITE(Buffer2D) {
TEST(Simple) { TEST(Simple) {
Buffer2D<int, tp::HeapAlloc> buff; Buffer2D<int, TestAllocator> buff;
buff.reserve({ 4, 4 }); buff.reserve({ 4, 4 });
buff.set({ 2, 2 }, 5); buff.set({ 2, 2 }, 5);
CHECK(buff.get({ 2, 2 }) == 5); CHECK(buff.get({ 2, 2 }) == 5);

View file

@ -7,7 +7,7 @@ const ualni size = 1000;
SUITE(Buffer) { SUITE(Buffer) {
TEST(Simple1) { TEST(Simple1) {
Buffer<TestClass, tp::HeapAlloc> buff; Buffer<TestClass, TestAllocator> buff;
CHECK(buff.size() == 0); CHECK(buff.size() == 0);
for (auto i : Range(size * 10)) { for (auto i : Range(size * 10)) {
buff.append(TestClass(i)); buff.append(TestClass(i));
@ -19,7 +19,7 @@ SUITE(Buffer) {
} }
TEST(Simple2) { TEST(Simple2) {
Buffer<TestClass, tp::HeapAlloc> buff(size); Buffer<TestClass, TestAllocator> buff(size);
CHECK(buff.size() == size); CHECK(buff.size() == size);
for (auto i : Range(size * 10)) for (auto i : Range(size * 10))
buff.append(TestClass(i)); buff.append(TestClass(i));

View file

@ -224,7 +224,7 @@ SUITE(IntervalTree) {
ualni numFound = 0; ualni numFound = 0;
ualni lastDataFound = 0; ualni lastDataFound = 0;
[[nodiscard]] bool isSingleData(ualni aData) const { return numFound == 1 & lastDataFound == aData; } [[nodiscard]] bool isSingleData(ualni aData) const { return numFound == 1 && lastDataFound == aData; }
[[nodiscard]] bool notFound() const { return numFound == 0; } [[nodiscard]] bool notFound() const { return numFound == 0; }
[[nodiscard]] bool found(ualni num) const { return numFound == num; } [[nodiscard]] bool found(ualni num) const { return numFound == num; }
}; };

View file

@ -7,7 +7,7 @@ using namespace tp;
SUITE(DoubleLinkedList) { SUITE(DoubleLinkedList) {
TEST(SimpleReference) { TEST(SimpleReference) {
tp::List<TestClass, tp::HeapAlloc> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) }; tp::List<TestClass, TestAllocator> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) };
list.pushBack(TestClass(5)); list.pushBack(TestClass(5));
list.pushFront(TestClass(0)); list.pushFront(TestClass(0));
@ -24,7 +24,9 @@ SUITE(DoubleLinkedList) {
} }
TEST(SimplePointer) { TEST(SimplePointer) {
tp::List<TestClass*, tp::HeapAlloc> list = { new TestClass(1), new TestClass(2), new TestClass(3), new TestClass(4) }; tp::List<TestClass*, TestAllocator> list = {
new TestClass(1), new TestClass(2), new TestClass(3), new TestClass(4)
};
list.pushBack(new TestClass(5)); list.pushBack(new TestClass(5));
list.pushFront(new TestClass(0)); list.pushFront(new TestClass(0));
@ -45,8 +47,8 @@ SUITE(DoubleLinkedList) {
} }
TEST(Copy) { TEST(Copy) {
tp::List<TestClass, tp::HeapAlloc> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) }; tp::List<TestClass, TestAllocator> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) };
tp::List<TestClass, tp::HeapAlloc> list2 = list; tp::List<TestClass, TestAllocator> list2 = list;
CHECK(list == list2); CHECK(list == list2);
@ -55,7 +57,7 @@ SUITE(DoubleLinkedList) {
} }
TEST(Serialization) { TEST(Serialization) {
tp::List<TestClass, tp::HeapAlloc> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) }; tp::List<TestClass, TestAllocator> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) };
ArchiverExample<1024, false> write; ArchiverExample<1024, false> write;
ArchiverExample<1024, true> read; ArchiverExample<1024, true> read;

View file

@ -6,7 +6,7 @@ using namespace tp;
SUITE(HashTable) { SUITE(HashTable) {
TEST(SimpleReference) { TEST(SimpleReference) {
tp::Map<tp::ualni, TestClass, tp::HeapAlloc> map; tp::Map<tp::ualni, TestClass, TestAllocator> map;
for (auto i : Range(1000, 100000)) { for (auto i : Range(1000, 100000)) {
map.put(i, TestClass(i)); map.put(i, TestClass(i));
@ -40,7 +40,7 @@ SUITE(HashTable) {
} }
TEST(SimplePointer) { TEST(SimplePointer) {
tp::Map<tp::ualni, TestClass*, tp::HeapAlloc> map; tp::Map<tp::ualni, TestClass*, TestAllocator> map;
for (auto i : Range(1000)) { for (auto i : Range(1000)) {
map.put(i, new TestClass(i)); map.put(i, new TestClass(i));
@ -78,13 +78,13 @@ SUITE(HashTable) {
} }
TEST(Copy) { TEST(Copy) {
tp::Map<tp::ualni, TestClass, tp::HeapAlloc> map; tp::Map<tp::ualni, TestClass, TestAllocator> map;
for (auto i : Range(10)) { for (auto i : Range(10)) {
map.put(i, TestClass(i)); map.put(i, TestClass(i));
} }
tp::Map<tp::ualni, TestClass, tp::HeapAlloc> map2 = map; tp::Map<tp::ualni, TestClass, TestAllocator> map2 = map;
CHECK(map == map2); CHECK(map == map2);
@ -93,7 +93,7 @@ SUITE(HashTable) {
} }
TEST(SaveLoad) { TEST(SaveLoad) {
tp::Map<tp::ualni, TestClass, tp::HeapAlloc> map; tp::Map<tp::ualni, TestClass, TestAllocator> map;
for (auto i : Range(10)) { for (auto i : Range(10)) {
map.put(i, TestClass(i)); map.put(i, TestClass(i));

View file

@ -2,18 +2,23 @@
#include "Tests.hpp" #include "Tests.hpp"
#include "UnitTest++/UnitTest++.h" #include "UnitTest++/UnitTest++.h"
int main() { void* TestAllocator::allocate(tp::ualni size) {
count++;
tp::ModuleManifest* deps[] = { &tp::gModuleUtils, &tp::gModuleAllocators, nullptr }; return malloc(size);
tp::ModuleManifest testModule("ContainersTest", nullptr, nullptr, deps); }
if (!testModule.initialize()) { void TestAllocator::deallocate(void* p) {
return 1; if (p) {
} free(p);
count--;
bool res = UnitTest::RunAllTests(); }
}
testModule.deinitialize();
return res; TestAllocator::~TestAllocator() {
ASSERT(!count);
}
int main() {
return UnitTest::RunAllTests();
} }

View file

@ -2,9 +2,20 @@
#include "UnitTest++/UnitTest++.h" #include "UnitTest++/UnitTest++.h"
#include "Allocators.hpp"
#include "Utils.hpp" #include "Utils.hpp"
// counting number of allocations and deallocations
struct TestAllocator {
TestAllocator() { count = 0; };
tp::ualni count = 0;
void* allocate(tp::ualni size);
void deallocate(void*);
~TestAllocator();
};
class TestClass { class TestClass {
tp::ualni val2 = 0; tp::ualni val2 = 0;
tp::ualni val1; tp::ualni val1;

View file

@ -5,7 +5,7 @@ using namespace tp;
SUITE(AvlTree) { SUITE(AvlTree) {
TEST(Simple) { TEST(Simple) {
AvlTree<AvlNumericKey<alni>, TestClass, tp::HeapAlloc> tree; AvlTree<AvlNumericKey<alni>, TestClass, TestAllocator> tree;
CHECK(tree.size() == 0); CHECK(tree.size() == 0);
CHECK(tree.head() == nullptr); CHECK(tree.head() == nullptr);
@ -22,7 +22,7 @@ SUITE(AvlTree) {
} }
TEST(Persistance) { TEST(Persistance) {
AvlTree<AvlNumericKey<alni>, TestClass, tp::HeapAlloc> tree; AvlTree<AvlNumericKey<alni>, TestClass, TestAllocator> tree;
const auto size = 1000; const auto size = 1000;

View file

@ -5,14 +5,6 @@
using namespace tp; using namespace tp;
static bool init(const ModuleManifest* self) {
gEnvironment.log();
return true;
}
static ModuleManifest* deps[] = { nullptr };
ModuleManifest tp::gModuleBase = ModuleManifest("Common", init, nullptr, deps);
ModuleManifest::ModuleManifest(const char* aModuleName, ModuleInit aInit, ModuleDeinit aDeinit, ModuleManifest** aDependencies) { ModuleManifest::ModuleManifest(const char* aModuleName, ModuleInit aInit, ModuleDeinit aDeinit, ModuleManifest** aDependencies) {
mInit = aInit; mInit = aInit;
mDeinit = aDeinit; mDeinit = aDeinit;

View file

@ -47,6 +47,7 @@ namespace tp {
} }
} }
// TODO remove
float Timer::easeIn(time_ms pDuration) { float Timer::easeIn(time_ms pDuration) {
if (!pDuration) { if (!pDuration) {
pDuration = mDuration; pDuration = mDuration;
@ -55,6 +56,7 @@ namespace tp {
return clamp((1.1f * x) / (x + 0.1f), 0.f, 1.f); return clamp((1.1f * x) / (x + 0.1f), 0.f, 1.f);
} }
// TODO remove
float Timer::easeOut(time_ms pDuration) { float Timer::easeOut(time_ms pDuration) {
if (!pDuration) { if (!pDuration) {
pDuration = mDuration; pDuration = mDuration;

View file

@ -1,8 +1,6 @@
#include "Utils.hpp" #include "Utils.hpp"
#include "ContainersCommon.hpp"
#include <ctime> #include <ctime>
#include <random> #include <random>
@ -21,12 +19,7 @@ static void deinitialize(const tp::ModuleManifest*) {
namespace tp { namespace tp {
static ModuleManifest* sModuleUtilsDeps[] = { &gModuleContainers, nullptr };
ModuleManifest gModuleUtils = ModuleManifest("Utils", initialize, deinitialize, sModuleUtilsDeps);
void memSetVal(void* p, uhalni byteSize, uint1 val) { void memSetVal(void* p, uhalni byteSize, uint1 val) {
// MODULE_SANITY_CHECK(gModuleBase)
alni alignedVal = val; alni alignedVal = val;
alignedVal = (alignedVal << 8) | alignedVal; alignedVal = (alignedVal << 8) | alignedVal;
alignedVal = (alignedVal << 16) | alignedVal; alignedVal = (alignedVal << 16) | alignedVal;
@ -44,8 +37,6 @@ namespace tp {
} }
void memCopy(void* left, const void* right, uhalni len) { void memCopy(void* left, const void* right, uhalni len) {
MODULE_SANITY_CHECK(gModuleBase)
ualni alignedLen = len / sizeof(alni); ualni alignedLen = len / sizeof(alni);
for (ualni idx = 0; idx < alignedLen; idx++) { for (ualni idx = 0; idx < alignedLen; idx++) {
((alni*) left)[idx] = ((alni*) right)[idx]; ((alni*) left)[idx] = ((alni*) right)[idx];
@ -58,7 +49,6 @@ namespace tp {
} }
int1 memCompare(const void* left, const void* right, uhalni len) { int1 memCompare(const void* left, const void* right, uhalni len) {
MODULE_SANITY_CHECK(gModuleBase)
if (!len) return 0; if (!len) return 0;
ualni alignedLength = len / sizeof(alni); ualni alignedLength = len / sizeof(alni);
@ -88,7 +78,6 @@ namespace tp {
bool memEqual(const void* left, const void* right, uhalni len) { return memCompare(left, right, len) == 0; } bool memEqual(const void* left, const void* right, uhalni len) { return memCompare(left, right, len) == 0; }
int1 memCompareVal(const void* left, uhalni len, uint1 val) { int1 memCompareVal(const void* left, uhalni len, uint1 val) {
MODULE_SANITY_CHECK(gModuleBase)
if (!len) return 0; if (!len) return 0;
alni valAligned = val; alni valAligned = val;

View file

@ -30,6 +30,4 @@ namespace tp {
uhalni mInitCount = 0; uhalni mInitCount = 0;
void* mCustomData = nullptr; void* mCustomData = nullptr;
}; };
extern ModuleManifest gModuleBase;
}; };

View file

@ -6,9 +6,6 @@
#define MEMBER_OFFSET(s, m) (alni(&(((s*) 0)->m))) #define MEMBER_OFFSET(s, m) (alni(&(((s*) 0)->m)))
namespace tp { namespace tp {
extern ModuleManifest gModuleUtils;
void memSetVal(void* p, uhalni byteSize, uint1 val); void memSetVal(void* p, uhalni byteSize, uint1 val);
void memCopy(void* left, const void* right, uhalni len); void memCopy(void* left, const void* right, uhalni len);
int1 memCompare(const void* left, const void* right, uhalni len); int1 memCompare(const void* left, const void* right, uhalni len);
@ -22,35 +19,6 @@ namespace tp {
namespace tp { namespace tp {
template <typename T1, typename T2>
class Pair {
public:
Pair() = default;
template <typename U1, typename U2>
Pair(U1 t1, U2 t2) :
head(static_cast<T1>(t1)),
tail(static_cast<T2>(t2)) {}
union {
T1 t1;
T1 head;
T1 x;
T1 first;
};
union {
T2 t2;
T2 tail;
T2 y;
T2 second;
};
bool operator==(const Pair& in) const { return in.t1 == t1 && in.t2 == t2; }
bool operator!=(const Pair& in) const { return !this->operator==(in); }
};
template <typename Type = alni> template <typename Type = alni>
class Bits { class Bits {
Type mFlags = 0; Type mFlags = 0;

View file

@ -1,65 +0,0 @@
#include "UnitTest++/UnitTest++.h"
#include "Debugging.hpp"
#include "Utils.hpp"
#include <cstdio>
using namespace tp;
void printSnapshot(const tp::CallStackCapture::CallStack* snapshot) {
printf("CallStack: \n");
for (auto frame : *snapshot) {
auto symbols = gCSCapture->getSymbols(frame.getFrame());
printf(" %s ----- %s:%llu\n", symbols->getFunc(), symbols->getFile(), symbols->getLine());
}
printf("\n");
}
void common() { auto tmp = gCSCapture->getSnapshot(); }
void first() {
common();
common();
common();
}
void second() {
common();
common();
common();
common();
}
void third() {
common();
common();
}
void root() {
first();
second();
third();
}
SUITE(Utils) {
TEST(CallStackCapture) {
tp::ModuleManifest* deps[] = { &tp::gModuleUtils, nullptr };
tp::ModuleManifest testModule("UtilsTest", nullptr, nullptr, deps);
REQUIRE CHECK(testModule.initialize());
root();
gCSCapture->logAll();
testModule.deinitialize();
}
TEST(CAllStackCaptureContent) {
CHECK(false);
}
}
int main() { return UnitTest::RunAllTests(); }