Initial
This commit is contained in:
parent
d4c558a59a
commit
db05d963be
74 changed files with 4473 additions and 3231 deletions
24
Utils/CMakeLists.txt
Normal file
24
Utils/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
|
||||
project(Utils)
|
||||
|
||||
### ---------------------- Static Library --------------------- ###
|
||||
file(GLOB SOURCES "./private/*.cpp")
|
||||
add_library(${PROJECT_NAME} STATIC ${SOURCES})
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
|
||||
|
||||
### ---------------------- Dependencies --------------------- ###
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Containers)
|
||||
|
||||
### -------------------------- Tests -------------------------- ###
|
||||
enable_testing()
|
||||
file(GLOB TEST_SOURCES "./tests/*.cpp")
|
||||
add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES})
|
||||
target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME})
|
||||
add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
|
||||
|
||||
|
||||
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib)
|
||||
207
Utils/private/Debugging.cpp
Normal file
207
Utils/private/Debugging.cpp
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
|
||||
#include "Debugging.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
CallStackCapture* tp::gCSCapture = nullptr;
|
||||
|
||||
void initializeCallStackCapture() {
|
||||
gCSCapture = new CallStackCapture();
|
||||
}
|
||||
|
||||
void deinitializeCallStackCapture() {
|
||||
delete gCSCapture;
|
||||
}
|
||||
|
||||
ualni CallStackCapture::CallStack::getDepth() const {
|
||||
ualni len = 0;
|
||||
for (long long frame : frames) {
|
||||
if (!frame) { break; }
|
||||
len++;
|
||||
}
|
||||
ualni stripedLen = 0;
|
||||
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) {
|
||||
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 (auto i : Range(MAX_CALL_DEPTH_CAPTURE)) {
|
||||
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(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));
|
||||
|
||||
MODULE_SANITY_CHECK(gModuleUtils)
|
||||
mBuffLoad = 0;
|
||||
mBuffLen = (MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024) / sizeof(CallStack);
|
||||
mBuff = (CallStack*) malloc(mBuffLen * sizeof(CallStack));
|
||||
}
|
||||
|
||||
const CallStackCapture::CallStack* CallStackCapture::getSnapshot() {
|
||||
if (mBuffLoad > mBuffLen) {
|
||||
static CallStack cs;
|
||||
cs.frames[0] = 0;
|
||||
return &cs;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// ---------------------------------- Platform Depended ---------------------------------- //
|
||||
|
||||
#if defined(ENV_OS_LINUX)
|
||||
|
||||
#include <malloc.h>
|
||||
#include <execinfo.h>
|
||||
#include <cstring>
|
||||
#include <cxxabi.h>
|
||||
|
||||
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 > MAX_DEBUG_INFO_LEN) ? (sourceLen - 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 = 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 > MAX_DEBUG_INFO_LEN) ? (funcLen - MAX_DEBUG_INFO_LEN) : 0));
|
||||
free(ret);
|
||||
return;
|
||||
}
|
||||
auto funcLen = std::strlen(func);
|
||||
std::strcpy(out, func + ((funcLen > MAX_DEBUG_INFO_LEN) ? (funcLen - 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);
|
||||
}
|
||||
|
||||
#else
|
||||
void CallStackCapture::platformWriteStackTrace(CallStack* stack) { stack->frames[0] = 0; }
|
||||
void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) {
|
||||
std::strcpy(out->file, "unresolved");
|
||||
std::strcpy(out->function, "unresolved");
|
||||
}
|
||||
#endif
|
||||
73
Utils/private/Testing.cpp
Normal file
73
Utils/private/Testing.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
|
||||
#include "Testing.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
Testing tp::gTesting;
|
||||
|
||||
void Testing::startTest(const char* name) {
|
||||
MODULE_SANITY_CHECK(gModuleUtils)
|
||||
mCurrent->mSubTests.pushBack(new TestingNode{ {}, {}, name, mCurrent });
|
||||
mCurrent = mCurrent->mSubTests.last()->data;
|
||||
}
|
||||
|
||||
void Testing::endTest() {
|
||||
if (mCurrent->mParent) {
|
||||
mCurrent = mCurrent->mParent;
|
||||
}
|
||||
}
|
||||
|
||||
void Testing::addFailedCheck(const FailedCheck& info) {
|
||||
mCurrent->mFailedChecks.pushBack(info);
|
||||
}
|
||||
|
||||
void Testing::reportState() {
|
||||
mRootTest.updateState();
|
||||
|
||||
printf("\n");
|
||||
mRootTest.report();
|
||||
}
|
||||
|
||||
bool Testing::hasFailed() {
|
||||
mRootTest.updateState();
|
||||
return mRootTest.mHasFailed;
|
||||
}
|
||||
|
||||
void Testing::setRootName(const char* name) {
|
||||
mRootTest.mName = name;
|
||||
}
|
||||
|
||||
void Testing::TestingNode::updateState() {
|
||||
for (auto child : mSubTests) {
|
||||
child->updateState();
|
||||
mHasFailed = child->mHasFailed;
|
||||
if (mHasFailed) return;
|
||||
}
|
||||
mHasFailed = mFailedChecks.length();
|
||||
}
|
||||
|
||||
void Testing::TestingNode::report(const char* path) const {
|
||||
if (!mHasFailed) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto newPath = path ? std::string(path) + "/" + mName : std::string(mName);
|
||||
|
||||
for (auto check : mFailedChecks) {
|
||||
printf("%s Failed - (%s) %s:%llu\n", newPath.c_str(), check.data().expression, check.data().file, check.data().line);
|
||||
}
|
||||
|
||||
for (const auto& child : mSubTests) {
|
||||
child->report(newPath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Testing::TestingNode::~TestingNode() {
|
||||
for (const auto& child : mSubTests) {
|
||||
delete child.data();
|
||||
}
|
||||
}
|
||||
81
Utils/private/Timing.cpp
Normal file
81
Utils/private/Timing.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "Timing.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#define GETTIMEMSC() \
|
||||
(time_ms)( \
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>( \
|
||||
std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now()) \
|
||||
.time_since_epoch()) \
|
||||
.count())
|
||||
|
||||
#define THREAD_SLEEP(time_ms) std::this_thread::sleep_for(std::chrono::milliseconds(time_ms))
|
||||
|
||||
tp::time_ms tp::gCurrentTime = tp::get_time();
|
||||
|
||||
namespace tp {
|
||||
time_ms get_time() {
|
||||
gCurrentTime = GETTIMEMSC();
|
||||
return gCurrentTime;
|
||||
}
|
||||
|
||||
void sleep(time_ms mDuration) {
|
||||
THREAD_SLEEP(mDuration);
|
||||
}
|
||||
|
||||
|
||||
Timer::Timer() {
|
||||
mDuration = 0;
|
||||
mStart = GETTIMEMSC();
|
||||
}
|
||||
|
||||
Timer::Timer(time_ms mDuration) {
|
||||
mStart = GETTIMEMSC();
|
||||
this->mDuration = mDuration;
|
||||
}
|
||||
|
||||
bool Timer::isTimeout() {
|
||||
return mDuration < GETTIMEMSC() - mStart;
|
||||
}
|
||||
|
||||
void Timer::reset() {
|
||||
mStart = GETTIMEMSC();
|
||||
}
|
||||
|
||||
time_ms Timer::timePassed() {
|
||||
return GETTIMEMSC() - mStart;
|
||||
}
|
||||
|
||||
time_ms Timer::remainder() {
|
||||
return mDuration - (GETTIMEMSC() - mStart);
|
||||
}
|
||||
|
||||
time_ms Timer::start() { return mStart; }
|
||||
time_ms Timer::duration() { return mDuration; }
|
||||
void Timer::setDuration(time_ms dur) { mDuration = dur; }
|
||||
|
||||
void Timer::wait() {
|
||||
if (!isTimeout()) {
|
||||
sleep(remainder());
|
||||
}
|
||||
}
|
||||
|
||||
float Timer::easeIn(time_ms pDuration) {
|
||||
if (!pDuration) {
|
||||
pDuration = mDuration;
|
||||
}
|
||||
float x = (1.f / pDuration) * timePassed();
|
||||
return clamp((1.1f * x) / (x + 0.1f), 0.f, 1.f);
|
||||
}
|
||||
|
||||
float Timer::easeOut(time_ms pDuration) {
|
||||
if (!pDuration) {
|
||||
pDuration = mDuration;
|
||||
}
|
||||
float x = (1.f / pDuration) * timePassed();
|
||||
return clamp((0.1f * (1 - x)) / (x + 0.1f), 0.f, 1.f);
|
||||
}
|
||||
}
|
||||
94
Utils/private/Utils.cpp
Normal file
94
Utils/private/Utils.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include "ContainersCommon.hpp"
|
||||
|
||||
#include "Testing.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
void initializeCallStackCapture();
|
||||
void deinitializeCallStackCapture();
|
||||
|
||||
static bool initialize(const tp::ModuleManifest* self) {
|
||||
initializeCallStackCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void deinitialize(const tp::ModuleManifest* self) {
|
||||
deinitializeCallStackCapture();
|
||||
tp::gTesting.reportState();
|
||||
if (tp::gTesting.hasFailed()) { exit(1); }
|
||||
}
|
||||
|
||||
namespace tp {
|
||||
|
||||
static ModuleManifest* sModuleUtilsDeps[] = { &gModuleContainers, nullptr };
|
||||
ModuleManifest gModuleUtils = ModuleManifest("Utils", initialize, deinitialize, sModuleUtilsDeps);
|
||||
|
||||
void memsetv(void* p, uhalni bytesize, uint1 val) {
|
||||
MODULE_SANITY_CHECK(gModuleBase)
|
||||
|
||||
alni alignedval = 0;
|
||||
for (ualni idx = 0; idx < sizeof(alni); idx++) {
|
||||
((uint1*) &alignedval)[idx] = val;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void memcp(void* left, const void* right, uhalni len) {
|
||||
MODULE_SANITY_CHECK(gModuleBase)
|
||||
|
||||
ualni alignedlen = len / sizeof(alni);
|
||||
for (ualni idx = 0; idx < alignedlen; idx++) {
|
||||
((alni*) left)[idx] = ((alni*) right)[idx];
|
||||
}
|
||||
|
||||
ualni unalignedlen = len - (alignedlen * sizeof(alni));
|
||||
for (ualni idx = 0; idx < unalignedlen; idx++) {
|
||||
((uint1*) left)[len - idx - 1] = ((uint1*) right)[len - idx - 1];
|
||||
}
|
||||
}
|
||||
|
||||
int1 memecomp(const void* left, const void* right, uhalni len) {
|
||||
MODULE_SANITY_CHECK(gModuleBase)
|
||||
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;
|
||||
}
|
||||
|
||||
alnf randf() {
|
||||
alnf r = static_cast<alnf>(std::rand()) / static_cast<alnf>(RAND_MAX);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
122
Utils/public/Debugging.hpp
Normal file
122
Utils/public/Debugging.hpp
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#pragma once
|
||||
|
||||
#include "Environment.hpp"
|
||||
#include "Map.hpp"
|
||||
|
||||
#define MAX_CALL_DEPTH_CAPTURE 16
|
||||
#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 {
|
||||
|
||||
class CallStackCapture {
|
||||
public:
|
||||
typedef tp::alni FramePointer;
|
||||
|
||||
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 + FRAMES_TO_SKIP_START); }
|
||||
[[nodiscard]] Iterator end() const { return Iterator(frames + FRAMES_TO_SKIP_START + getDepth()); }
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
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:
|
||||
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 clear();
|
||||
|
||||
private:
|
||||
ualni mBuffLen;
|
||||
ualni mBuffLoad;
|
||||
CallStack* mBuff;
|
||||
Map<CallStackKey, CallStack*, DefaultAllocator, hashCallStack> mSnapshots;
|
||||
Map<FramePointer, DebugSymbols> mSymbols;
|
||||
};
|
||||
|
||||
extern CallStackCapture* gCSCapture;
|
||||
}
|
||||
97
Utils/public/Sorting.hpp
Normal file
97
Utils/public/Sorting.hpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
template <typename Type>
|
||||
inline bool compare(const Type& val1, const Type& val2) {
|
||||
return val1 > val2;
|
||||
}
|
||||
|
||||
struct SortMerge {
|
||||
|
||||
template <typename Type>
|
||||
static void sort(Type* buff, int length, bool (*grater)(const Type& obj1, const Type& obj2) = &compare) {
|
||||
mergeSort(buff, 0, length - 1, grater);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template <typename Type>
|
||||
static void merge(Type* buff, int left, int middle, int right, bool (*grater)(const Type& obj1, const Type& obj2)) {
|
||||
int n1 = middle - left + 1;
|
||||
int n2 = right - middle;
|
||||
|
||||
Type* Left = new Type[n1];
|
||||
Type* Right = new Type[n2];
|
||||
|
||||
for (int i = 0; i < n1; i++) {
|
||||
Left[i] = buff[left + i];
|
||||
}
|
||||
|
||||
for (int j = 0; j < n2; j++) {
|
||||
Right[j] = buff[middle + 1 + j];
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int k = left;
|
||||
|
||||
while (i < n1 && j < n2) {
|
||||
if (!(grater(Left[i], Right[j]))) {
|
||||
buff[k] = Left[i];
|
||||
i++;
|
||||
} else {
|
||||
buff[k] = Right[j];
|
||||
j++;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
|
||||
while (i < n1) {
|
||||
buff[k] = Left[i];
|
||||
i++;
|
||||
k++;
|
||||
}
|
||||
|
||||
while (j < n2) {
|
||||
buff[k] = Right[j];
|
||||
j++;
|
||||
k++;
|
||||
}
|
||||
|
||||
delete[] Left;
|
||||
delete[] Right;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
static void mergeSort(Type* buff, int left, int right, bool (*grater)(const Type& obj1, const Type& obj2)) {
|
||||
|
||||
if (left >= right) {
|
||||
return;
|
||||
}
|
||||
|
||||
int middle = left + (right - left) / 2;
|
||||
|
||||
mergeSort(buff, left, middle, grater);
|
||||
mergeSort(buff, middle + 1, right, grater);
|
||||
merge(buff, left, middle, right, grater);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct SortInsert {
|
||||
template <typename Type>
|
||||
static void sort(Type* buff, int length, bool (*grater)(const Type& obj1, const Type& obj2) = &compare) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
for (int j = i + 1; j < length; j++) {
|
||||
if (grater(*buff[i], *buff[j])) {
|
||||
swap(buff[i], buff[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
63
Utils/public/Testing.hpp
Normal file
63
Utils/public/Testing.hpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "List.hpp"
|
||||
|
||||
namespace tp {
|
||||
class Testing {
|
||||
public:
|
||||
|
||||
struct FailedCheck {
|
||||
const char* expression = nullptr;
|
||||
const char* file = nullptr;
|
||||
ualni line = 0;
|
||||
};
|
||||
|
||||
Testing() = default;
|
||||
void startTest(const char* name);
|
||||
void endTest();
|
||||
void addFailedCheck(const FailedCheck& info);
|
||||
void reportState();
|
||||
void setRootName(const char* name);
|
||||
[[nodiscard]] bool hasFailed();
|
||||
|
||||
private:
|
||||
struct TestingNode {
|
||||
List<FailedCheck> mFailedChecks;
|
||||
List<TestingNode*> mSubTests;
|
||||
const char* mName = "Unnamed";
|
||||
TestingNode* mParent = nullptr;
|
||||
bool mHasFailed = false;
|
||||
|
||||
void report(const char* path = nullptr) const;
|
||||
void updateState();
|
||||
~TestingNode();
|
||||
};
|
||||
|
||||
TestingNode mRootTest;
|
||||
TestingNode* mCurrent = &mRootTest;
|
||||
};
|
||||
|
||||
extern Testing gTesting;
|
||||
}
|
||||
|
||||
#define TEST_DEF(Name)\
|
||||
static void Name##FunctorBody();\
|
||||
void test##Name() { \
|
||||
tp::gTesting.startTest(#Name);\
|
||||
Name##FunctorBody();\
|
||||
tp::gTesting.endTest();\
|
||||
} \
|
||||
void Name##FunctorBody()
|
||||
|
||||
#define TEST_DEF_STATIC(Name)\
|
||||
static void Name##FunctorBody();\
|
||||
static void test##Name() { \
|
||||
tp::gTesting.startTest(#Name);\
|
||||
Name##FunctorBody();\
|
||||
tp::gTesting.endTest();\
|
||||
} \
|
||||
void Name##FunctorBody()
|
||||
|
||||
#define TEST(expr) if (!(expr)) tp::gTesting.addFailedCheck({ #expr, __FILE__, __LINE__ })
|
||||
#define TEST_EQUAL(l, r) if (!((l) == (r))) tp::gTesting.addFailedCheck({ #l" == "#r, __FILE__, __LINE__ })
|
||||
58
Utils/public/Timing.hpp
Normal file
58
Utils/public/Timing.hpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#pragma once
|
||||
|
||||
#include "Environment.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
typedef alni time_ms;
|
||||
typedef alni time_ns;
|
||||
|
||||
extern time_ms gCurrentTime;
|
||||
|
||||
class Timer {
|
||||
|
||||
time_ms mStart;
|
||||
time_ms mDuration;
|
||||
|
||||
public:
|
||||
|
||||
Timer();
|
||||
explicit Timer(time_ms time);
|
||||
|
||||
time_ms start();
|
||||
time_ms duration();
|
||||
void setDuration(time_ms dur);
|
||||
|
||||
bool isTimeout();
|
||||
void reset();
|
||||
time_ms timePassed();
|
||||
time_ms remainder();
|
||||
void wait();
|
||||
|
||||
float easeIn(time_ms duration = 0);
|
||||
float easeOut(time_ms duration = 0);
|
||||
};
|
||||
|
||||
void sleep(time_ms duration);
|
||||
time_ms get_time();
|
||||
|
||||
struct FpsCounter {
|
||||
halni frames = 0;
|
||||
Timer time;
|
||||
halni fps = 0;
|
||||
|
||||
FpsCounter() : time(1000) {}
|
||||
|
||||
void update(bool log = true) {
|
||||
frames++;
|
||||
if (time.isTimeout()) {
|
||||
fps = frames;
|
||||
if (log) {
|
||||
// printf("fps %i \n", fps);
|
||||
}
|
||||
frames = 0;
|
||||
time.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
79
Utils/public/Utils.hpp
Normal file
79
Utils/public/Utils.hpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
#include "BaseModule.hpp"
|
||||
|
||||
#define PTR_OFFSET(first, offset) (*((&first) + offset))
|
||||
#define MEMBER_OFFSET(s, m) (alni(&(((s*)0)->m)))
|
||||
|
||||
namespace tp {
|
||||
|
||||
extern ModuleManifest gModuleUtils;
|
||||
|
||||
void memsetv(void* p, uhalni bytesize, uint1 val);
|
||||
void memcp(void* left, const void* right, uhalni len);
|
||||
int1 memequal(const void* left, const void* right, uhalni len);
|
||||
}
|
||||
|
||||
namespace tp {
|
||||
[[nodiscard]] alnf randf();
|
||||
}
|
||||
|
||||
namespace tp {
|
||||
|
||||
template <typename T1, typename T2>
|
||||
class Pair {
|
||||
public:
|
||||
Pair() {}
|
||||
Pair(T1 t1, T2 t2) : head(t1), tail(t2) {}
|
||||
union { T1 t1; T1 head; T1 x; };
|
||||
union { T2 t2; T2 tail; T2 y; };
|
||||
};
|
||||
|
||||
template <typename Type = alni>
|
||||
class Bits {
|
||||
Type mFlags = 0;
|
||||
public:
|
||||
Bits() = default;
|
||||
explicit Bits(Type val) { mFlags = val; }
|
||||
explicit Bits(bool val) { for (int bit = 0; bit < sizeof(Type); bit++) { set(bit, val); } }
|
||||
bool get(int1 idx) { return mFlags & (1l << idx); }
|
||||
void set(int1 idx, bool val) {
|
||||
if (val) {
|
||||
mFlags |= (1l << idx);
|
||||
} else {
|
||||
mFlags &= ~(1l << idx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename tType = ualni>
|
||||
class Range {
|
||||
public:
|
||||
class Iterator {
|
||||
public:
|
||||
tType mIndex;
|
||||
explicit Iterator(tType pStartIndex) : mIndex(pStartIndex) {}
|
||||
tType index() const { return mIndex; }
|
||||
inline void operator++() { mIndex++; }
|
||||
inline operator tType() const { return mIndex; }
|
||||
inline bool operator==(Iterator pIndex) { return mIndex == pIndex.mIndex; }
|
||||
inline bool operator!=(Iterator pIndex) { return mIndex != pIndex.mIndex; }
|
||||
inline const Iterator& operator*() { return *this; }
|
||||
};
|
||||
|
||||
tType mBegin = 0;
|
||||
tType mEnd = 0;
|
||||
|
||||
Range() = default;
|
||||
explicit Range(tType pEndIndex) : mBegin(0), mEnd(pEndIndex) {}
|
||||
Range(tType pStartIndex, tType pEndIndex) : mBegin(pStartIndex), mEnd(pEndIndex) {}
|
||||
|
||||
bool valid() { return mBegin < mEnd; }
|
||||
|
||||
tType idxBegin() const { return mBegin; }
|
||||
tType idxEnd() const { return mEnd; }
|
||||
Iterator begin() { return Iterator(mBegin); }
|
||||
Iterator end() { return Iterator(mEnd); }
|
||||
};
|
||||
}
|
||||
67
Utils/tests/Tests.cpp
Normal file
67
Utils/tests/Tests.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
#include "Utils.hpp"
|
||||
#include "Debugging.hpp"
|
||||
#include "Testing.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() {
|
||||
gCSCapture->getSnapshot();
|
||||
}
|
||||
|
||||
void first() {
|
||||
common();
|
||||
common();
|
||||
common();
|
||||
}
|
||||
|
||||
void second() {
|
||||
common();
|
||||
common();
|
||||
common();
|
||||
common();
|
||||
}
|
||||
|
||||
void third() {
|
||||
common();
|
||||
common();
|
||||
}
|
||||
|
||||
void root() {
|
||||
first();
|
||||
second();
|
||||
third();
|
||||
}
|
||||
|
||||
TEST_DEF(Debugging) {
|
||||
root();
|
||||
|
||||
for (auto cs : *gCSCapture) {
|
||||
printSnapshot(cs.getCallStack());
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
tp::ModuleManifest* deps[] = { &tp::gModuleUtils, nullptr };
|
||||
tp::ModuleManifest testModule("UtilsTest", nullptr, nullptr, deps);
|
||||
|
||||
if (!testModule.initialize()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
testDebugging();
|
||||
|
||||
testModule.deinitialize();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue