diff --git a/Allocators/public/AllocatorsTypes.hpp b/Allocators/public/AllocatorsTypes.hpp index 99b94bd..5d9dd23 100644 --- a/Allocators/public/AllocatorsTypes.hpp +++ b/Allocators/public/AllocatorsTypes.hpp @@ -1,100 +1,4 @@ #pragma once -#include - -#define DEBUG_ASSERT(x) assert(x) -#define ASSERT(x) assert(x) - -// #define MEM_STACK_TRACE -#define MEM_DEBUG - -namespace tp { - using ualni = unsigned long long int; - using alni = unsigned long long int; - using halnf = float; - using uhalni = unsigned long int; - using int1 = char; - using uint1 = unsigned char; - - enum { - ENV_ALNI_SIZE_B = sizeof(ualni) - }; - - inline int1 memCompare(const void* left, const void* right, uhalni len) { - if (!len) return 0; - - ualni alignedLength = len / sizeof(alni); - for (ualni idx = 0; idx < alignedLength; idx++) { - if (((alni*) left)[idx] == ((alni*) right)[idx]) { - continue; - } - if (((alni*) left)[idx] > ((alni*) right)[idx]) { - return 1; - } - return -1; - } - - ualni unalignedLength = len - (alignedLength * sizeof(alni)); - for (ualni idx = 0; idx < unalignedLength; idx++) { - if (((uint1*) left)[len - idx - 1] == ((uint1*) right)[len - idx - 1]) { - continue; - } - if (((uint1*) left)[len - idx - 1] > ((uint1*) right)[len - idx - 1]) { - return 1; - } - return -1; - } - return 0; - } - - inline bool memEqual(const void* left, const void* right, uhalni len) { return memCompare(left, right, len) == 0; } - - inline int1 memCompareVal(const void* left, uhalni len, uint1 val) { - if (!len) return 0; - - alni valAligned = val; - valAligned = (valAligned << 8) | valAligned; - valAligned = (valAligned << 16) | valAligned; - valAligned = (valAligned << 32) | valAligned; - - ualni alignedLength = len / sizeof(alni); - for (ualni idx = 0; idx < alignedLength; idx++) { - if (((alni*) left)[idx] == valAligned) { - continue; - } - if (((alni*) left)[idx] > valAligned) { - return 1; - } - return -1; - } - - ualni unalignedLength = len - (alignedLength * sizeof(alni)); - for (ualni idx = 0; idx < unalignedLength; idx++) { - if (((uint1*) left)[len - idx - 1] == val) { - continue; - } - if (((uint1*) left)[len - idx - 1] > val) { - return 1; - } - return -1; - } - return 0; - } - - inline void memSetVal(void* p, uhalni byteSize, uint1 val) { - alni alignedVal = val; - alignedVal = (alignedVal << 8) | alignedVal; - alignedVal = (alignedVal << 16) | alignedVal; - alignedVal = (alignedVal << 32) | alignedVal; - - ualni alignedLen = byteSize / sizeof(alni); - for (ualni idx = 0; idx < alignedLen; idx++) { - ((alni*) p)[idx] = alignedVal; - } - - ualni unalignedLen = byteSize - (alignedLen * sizeof(alni)); - for (ualni idx = 0; idx < unalignedLen; idx++) { - ((uint1*) p)[byteSize - idx - 1] = val; - } - } -} +#include "Environment.hpp" +#include "Utils.hpp" diff --git a/CMakeLists.txt b/CMakeLists.txt index 85deb03..9f257cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,9 +18,10 @@ set(WINDOWS_LIBRARIES "../moduleswindowsl" CACHE STRING "Svn repository with win #add_subdirectory(Externals) +add_subdirectory(Modules) +add_subdirectory(Callstack) add_subdirectory(Allocators) -#add_subdirectory(Modules) #add_subdirectory(Containers) #add_subdirectory(Math) # add_subdirectory(Language) diff --git a/Callstack/CMakeLists.txt b/Callstack/CMakeLists.txt new file mode 100644 index 0000000..fc7035c --- /dev/null +++ b/Callstack/CMakeLists.txt @@ -0,0 +1,14 @@ +project(Callstack) + +### ---------------------- Static Library --------------------- ### +file(GLOB SOURCES "./private/*.cpp") + +add_library(${PROJECT_NAME} STATIC ${SOURCES}) +target_include_directories(${PROJECT_NAME} PUBLIC ./public/) +target_link_libraries(${PROJECT_NAME} PUBLIC Modules) + +### -------------------------- Tests -------------------------- ### +file(GLOB TEST_SOURCES "./tests/*.cpp") +add_executable(Tests${PROJECT_NAME} ${TEST_SOURCES}) +target_link_libraries(Tests${PROJECT_NAME} ${PROJECT_NAME} UnitTest++) +add_test(NAME Tests${PROJECT_NAME} COMMAND Tests${PROJECT_NAME}) \ No newline at end of file diff --git a/Callstack/private/Callstack.cpp b/Callstack/private/Callstack.cpp new file mode 100644 index 0000000..4897748 --- /dev/null +++ b/Callstack/private/Callstack.cpp @@ -0,0 +1,338 @@ + +#include "Callstack.hpp" + +#include + +using namespace tp; + +// frame to reference on no sapce left +static void errorNoSpaceLeft() { + // to disable optimization of function + static int dummy; + dummy = 10; +} + +ualni CallStackCapture::CallStack::getDepth() const { + ualni len = 0; + for (long long frame : frames) { + if (!frame) { + break; + } + len++; + } + return len++; +} + +ualni CallStackCapture::hashCallStack(CallStackKey key) { + auto const cs = key.cs; + ualni out = 0; + for (ualni i = 0; cs->frames[i]; i++) { + out += cs->frames[i]; + } + return out; +} + +bool CallStackCapture::CallStackKey::operator==(const CallStackCapture::CallStackKey& in) const { + for (ualni i = 0; i < MAX_CALL_DEPTH_CAPTURE; i++) { + if (cs->frames[i] != in.cs->frames[i]) { + return false; + } + if (cs->frames[i] == 0 && in.cs->frames[i] == 0) { + return true; + } + } + DEBUG_ASSERT(0 && "Must Not Happen") + return true; +} + +CallStackCapture::CallStackCapture() { + static_assert(MAX_CALL_DEPTH_CAPTURE >= 1); + static_assert(MAX_CALL_CAPTURES_MEM_SIZE_MB > 0); + static_assert(MAX_DEBUG_INFO_LEN > sizeof("unresolved")); + static_assert(MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024 > sizeof(CallStack)); + + mBuffLoad = 0; + mBuffLen = STACKS_LENGTH; + mBuff = (CallStack*) malloc(mBuffLen * sizeof(CallStack)); + + mErrorSnapshot.frames[0] = (alni) & errorNoSpaceLeft; + + + platformInit(); + + initialized = true; +} + +const CallStackCapture::CallStack* CallStackCapture::getSnapshot() { + + if (mBuffLoad > mBuffLen) { + return &mErrorSnapshot; + } + + CallStack* cs = &mBuff[mBuffLoad]; + + platformWriteStackTrace(cs); + + auto idx = mSnapshots.presents({ cs }); + if (idx) { + return mSnapshots.getSlotVal(idx); + } + + mSnapshots.put({ cs }, cs); + + mBuffLoad++; + return cs; +} + +const CallStackCapture::DebugSymbols* CallStackCapture::getSymbols(FramePointer frame) { + auto idx = mSymbols.presents(frame); + if (idx) { + return &mSymbols.getSlotVal(idx); + } + + mSymbols.put(frame, {}); + auto symbols = &mSymbols.get(frame); + + platformWriteDebugSymbols(frame, symbols); + return symbols; +} + +void CallStackCapture::clear() { + mBuffLoad = 0; + mSnapshots.removeAll(); + mSymbols.removeAll(); +} + +CallStackCapture::~CallStackCapture() { + free(mBuff); + initialized = false; +} + +// ---------------------------------- Platform Depended ---------------------------------- // + +#if defined(ENV_OS_LINUX) + +#include +#include +#include +#include + +void CallStackCapture::platformInit() { +} + +void CallStackCapture::platformDeinit() { +} + +void CallStackCapture::platformWriteStackTrace(CallStack* stack) { + auto depth = backtrace((void**) stack->frames, (int) MAX_CALL_DEPTH_CAPTURE - 1); + stack->frames[depth] = 0; +} + +static void getGetSourceFromBinaryAddress(const char* binary, const char* address, char* file, ualni* line) { + static char buff[1024]; + + snprintf(buff, sizeof(buff), "addr2line -e %s -a %s", binary, address); + FILE* pipe = popen(buff, "r"); + if (pipe) { + fgets(buff, sizeof(buff), pipe); + fgets(buff, sizeof(buff), pipe); + pclose(pipe); + char* linePtr = strchr(buff, ':'); + + printf("%s\n", buff); + + if (linePtr != nullptr && buff[0] != '?' && buff[1] != '?' && buff[2] != ':') { + *linePtr = '\0'; + auto sourceLen = std::strlen(buff); + std::strcpy(file, buff + ((sourceLen > CallStackCapture::MAX_DEBUG_INFO_LEN) ? (sourceLen - CallStackCapture::MAX_DEBUG_INFO_LEN) : 0)); + *line = strtoul(linePtr + 1, nullptr, 10); + return; + } + } + + std::strcpy(file, "unresolved"); + *line = 0; +} + +static void getDemangledName(const char* func, char* out) { + int status; + size_t funcDemangledSize = CallStackCapture::MAX_DEBUG_INFO_LEN; + char* funcDemangled = (char*) malloc(funcDemangledSize); + char* ret = abi::__cxa_demangle(func, funcDemangled, &funcDemangledSize, &status); + if (status == 0) { + funcDemangled = ret; + auto funcLen = std::strlen(funcDemangled); + std::strcpy(out, funcDemangled + ((funcLen > CallStackCapture::MAX_DEBUG_INFO_LEN) ? (funcLen - CallStackCapture::MAX_DEBUG_INFO_LEN) : 0)); + free(ret); + return; + } + auto funcLen = std::strlen(func); + std::strcpy(out, func + ((funcLen > CallStackCapture::MAX_DEBUG_INFO_LEN) ? (funcLen - CallStackCapture::MAX_DEBUG_INFO_LEN) : 0)); + free(funcDemangled); +} + +void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) { + void* addrList[1] = { (void*) frame }; + auto symbolsArray = backtrace_symbols(addrList, 1); + + // 'bin(fun+addr)' + char* bin = *symbolsArray; + char* func = nullptr; + char* offset = nullptr; + + // 'bin fun+addr' + for (char* p = bin; *p; ++p) { + if (*p == '(') { + *p = 0; + func = p + 1; + } else if (*p == '+') { + offset = p; + } else if (*p == ')' && offset) { + *p = 0; + } + } + + if (func && offset) { + getGetSourceFromBinaryAddress(bin, func, out->file, &out->line); + + if (offset != func) { + *offset = 0; + getDemangledName(func, out->function); + } else { + std::strcpy(out->function, "unresolved"); + } + } else { + std::strcpy(out->file, "unresolved"); + std::strcpy(out->function, "unresolved"); + } + + free(symbolsArray); +} + +void CallStackCapture::printSnapshot(const CallStack* snapshot) { + printf("CallStack: \n"); + if (snapshot) { + for (auto frame : *snapshot) { + auto symbols = getSymbols(frame.getFrame()); + printf(" %s ----- %s:%llu\n", symbols->getFunc(), symbols->getFile(), symbols->getLine()); + } + } + printf("\n"); +} + +void CallStackCapture::logAll() { + for (auto cs : *this) { + printSnapshot(cs.getCallStack()); + } +} + +#else +#include +#include +#include + +#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) { + printf("CallStack: \n"); + 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"); +} + +void CallStackCapture::logAll() { + for (auto cs : *this) { + printSnapshot(cs.getCallStack()); + } +} +#endif \ No newline at end of file diff --git a/Callstack/public/Callstack.hpp b/Callstack/public/Callstack.hpp new file mode 100644 index 0000000..aac0e3c --- /dev/null +++ b/Callstack/public/Callstack.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include "Environment.hpp" +#include "Map.hpp" + +namespace tp { + + class CallStackCapture { + public: + typedef tp::alni FramePointer; + + bool initialized = false; + + enum { + MAX_CALL_DEPTH_CAPTURE = 16, + MAX_CALL_CAPTURES_MEM_SIZE_MB = 32, + MAX_DEBUG_INFO_LEN = 64, + }; + + class CallStack { + friend CallStackCapture; + FramePointer frames[MAX_CALL_DEPTH_CAPTURE]; + + public: + [[nodiscard]] ualni getDepth() const; + + class Iterator { + const FramePointer* mFrame; + + public: + explicit Iterator(const FramePointer* frame) : mFrame(frame){}; + FramePointer getFrame() { return *mFrame; } + bool operator==(const Iterator& in) const { return in.mFrame == mFrame; } + void operator++() { mFrame++; } + const Iterator& operator*() const { return *this; } + }; + + [[nodiscard]] Iterator begin() const { return Iterator(frames); } + [[nodiscard]] Iterator end() const { return Iterator(frames + getDepth()); } + }; + + enum { STACKS_LENGTH = (MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024) / sizeof(CallStack) }; + + class DebugSymbols { + friend CallStackCapture; + char function[MAX_DEBUG_INFO_LEN + 1] = { 0 }; + char file[MAX_DEBUG_INFO_LEN + 1] = { 0 }; + ualni line = 0; + + public: + [[nodiscard]] const char* getFunc() const { return function; } + [[nodiscard]] const char* getFile() const { return file; } + [[nodiscard]] ualni getLine() const { return line; } + }; + + public: + CallStackCapture(); + ~CallStackCapture(); + + [[nodiscard]] const CallStack* getSnapshot(); + const DebugSymbols* getSymbols(FramePointer fp); + + void printSnapshot(const CallStack* snapshot); + void logAll(); + + public: + class Iterator { + const CallStack* mSnapshot; + + public: + explicit Iterator(const CallStack* start) : mSnapshot(start) {}; + const CallStack* getCallStack() { return mSnapshot; } + bool operator==(const Iterator& in) const { return in.mSnapshot == mSnapshot; } + void operator++() { mSnapshot++; } + const Iterator& operator*() const { return *this; } + }; + + [[nodiscard]] Iterator begin() const { return Iterator(mBuff); } + [[nodiscard]] Iterator end() const { return Iterator(mBuff + mBuffLoad); } + + private: + struct CallStackKey { + CallStack* cs; + bool operator==(const CallStackKey& in) const; + }; + + static void platformWriteStackTrace(CallStack* stack); + static void platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out); + [[nodiscard]] static ualni hashCallStack(CallStackKey key); + void platformInit(); + void platformDeinit(); + + void clear(); + + private: + CallStack mErrorSnapshot; + + ualni mBuffLen; + ualni mBuffLoad; + CallStack* mBuff; + + Map mSnapshots; + Map mSymbols; + }; +} diff --git a/Callstack/tests/Test.cpp b/Callstack/tests/Test.cpp new file mode 100644 index 0000000..e5ed3b7 --- /dev/null +++ b/Callstack/tests/Test.cpp @@ -0,0 +1,44 @@ + +#include "UnitTest++/UnitTest++.h" + +#include "Callstack.hpp" + +#include + +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(); } \ No newline at end of file