|
@@ -1,23 +1,21 @@
|
|
|
-#include "core/utils/Utility.hpp"
|
|
|
+#include "core/Utility.hpp"
|
|
|
|
|
|
-#include <stdio.h>
|
|
|
-#include <stdlib.h>
|
|
|
-#include <string.h>
|
|
|
+#include <cstdio>
|
|
|
+#include <cstdlib>
|
|
|
+#include <cstring>
|
|
|
+#include <ctime>
|
|
|
|
|
|
-#include "ErrorSimulator.hpp"
|
|
|
-#include "core/utils/Error.hpp"
|
|
|
-#include "core/utils/Logger.hpp"
|
|
|
+#include "core/Logger.hpp"
|
|
|
|
|
|
static Core::ExitHandler exitHandler = nullptr;
|
|
|
static void* exitData = nullptr;
|
|
|
static Core::OutOfMemoryHandler outOfMemoryHandler = nullptr;
|
|
|
static void* outOfMemoryData = nullptr;
|
|
|
|
|
|
-void Core::exitWithHandler(const char* file, int line, int value) {
|
|
|
+[[noreturn]] void Core::exitWithHandler(const char* file, int line, int value) {
|
|
|
if(value != 0) {
|
|
|
- printf("%sExit from %s:%d with value %d%s\n", Core::Logger::COLOR_RED,
|
|
|
- Core::Logger::getFileName(file), line, value,
|
|
|
- Core::Logger::COLOR_RESET);
|
|
|
+ file = getShortFileName(file);
|
|
|
+ LOG_ERROR("Exit from #:# with value #", file, line, value);
|
|
|
}
|
|
|
if(exitHandler != nullptr) {
|
|
|
exitHandler(value, exitData);
|
|
@@ -30,91 +28,209 @@ void Core::setExitHandler(ExitHandler eh, void* data) {
|
|
|
exitData = data;
|
|
|
}
|
|
|
|
|
|
-#define CORE_TO_STRING(type, cast, format) \
|
|
|
- size_t Core::toString(type t, char* buffer, size_t size) { \
|
|
|
- int w = snprintf(buffer, size, format, static_cast<cast>(t)); \
|
|
|
- return w < 0 ? 0 : static_cast<size_t>(w); \
|
|
|
- }
|
|
|
-
|
|
|
-CORE_TO_STRING(signed short, signed short, "%hd")
|
|
|
-CORE_TO_STRING(unsigned short, unsigned short, "%hu")
|
|
|
-CORE_TO_STRING(signed int, signed int, "%d")
|
|
|
-CORE_TO_STRING(unsigned int, unsigned int, "%u")
|
|
|
-CORE_TO_STRING(signed long, signed long, "%ld")
|
|
|
-CORE_TO_STRING(unsigned long, unsigned long, "%lu")
|
|
|
-CORE_TO_STRING(signed long long, signed long long, "%lld")
|
|
|
-CORE_TO_STRING(unsigned long long, unsigned long long, "%llu")
|
|
|
-CORE_TO_STRING(float, double, "%.2f")
|
|
|
-CORE_TO_STRING(double, double, "%.2lf")
|
|
|
-CORE_TO_STRING(long double, long double, "%.2Lf")
|
|
|
-
|
|
|
-void Core::print(int c) {
|
|
|
- if(putchar(c) < 0) {
|
|
|
- CORE_EXIT(ErrorCode::BLOCKED_STDOUT.code); // CoverageIgnore
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void Core::print(const char* s) {
|
|
|
- if(fputs(s, stdout) < 0) {
|
|
|
- CORE_EXIT(ErrorCode::BLOCKED_STDOUT.code); // CoverageIgnore
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void Core::printLine(const char* s) {
|
|
|
- if(puts(s) < 0) {
|
|
|
- CORE_EXIT(ErrorCode::BLOCKED_STDOUT.code); // CoverageIgnore
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
void Core::setOutOfMemoryHandler(OutOfMemoryHandler h, void* data) {
|
|
|
outOfMemoryHandler = h;
|
|
|
outOfMemoryData = data;
|
|
|
}
|
|
|
|
|
|
-void* Core::allocate(size_t n) {
|
|
|
- // deny too large allocations instantly
|
|
|
- // this makes LTO happy
|
|
|
- if(n >= 1024lu * 1024lu * 1024lu * 64lu) {
|
|
|
- CORE_EXIT(ErrorCode::OUT_OF_MEMORY.code); // CoverageIgnore
|
|
|
+static void* exitOnNull(void* p, size_t n) {
|
|
|
+ if(p == nullptr) {
|
|
|
+ LOG_ERROR("Out of memory, requested '#' bytes", n);
|
|
|
+ EXIT(1);
|
|
|
}
|
|
|
+ return p;
|
|
|
+}
|
|
|
+
|
|
|
+static void* realAllocate(size_t n) {
|
|
|
void* p = malloc(n);
|
|
|
-#ifdef ERROR_SIMULATOR
|
|
|
- if(CORE_ALLOC_FAIL && p != nullptr) {
|
|
|
- free(p);
|
|
|
- p = nullptr;
|
|
|
- }
|
|
|
-#endif
|
|
|
while(p == nullptr && outOfMemoryHandler != nullptr) {
|
|
|
outOfMemoryHandler(outOfMemoryData);
|
|
|
p = malloc(n);
|
|
|
}
|
|
|
- if(p == nullptr) {
|
|
|
- CORE_EXIT(ErrorCode::OUT_OF_MEMORY.code); // CoverageIgnore
|
|
|
- }
|
|
|
- return p;
|
|
|
+ return exitOnNull(p, n);
|
|
|
}
|
|
|
|
|
|
-void* Core::reallocate(void* oldP, size_t n) {
|
|
|
+static void* realReallocate(void* oldP, size_t n) {
|
|
|
if(n <= 0) {
|
|
|
free(oldP);
|
|
|
return nullptr;
|
|
|
}
|
|
|
void* p = realloc(oldP, n);
|
|
|
-#ifdef ERROR_SIMULATOR
|
|
|
- if(CORE_ALLOC_FAIL && p != nullptr) {
|
|
|
- oldP = p;
|
|
|
- p = nullptr;
|
|
|
- }
|
|
|
-#endif
|
|
|
- // this double check is to prevent the compiler from complaining
|
|
|
if(p == nullptr) {
|
|
|
while(p == nullptr && outOfMemoryHandler != nullptr) {
|
|
|
outOfMemoryHandler(outOfMemoryData);
|
|
|
p = realloc(oldP, n);
|
|
|
}
|
|
|
}
|
|
|
+ return exitOnNull(p, n);
|
|
|
+}
|
|
|
+
|
|
|
+static void realFree(void* p) {
|
|
|
+ free(p);
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CHECK_MEMORY
|
|
|
+static const u8 CANARY[16] = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF,
|
|
|
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF};
|
|
|
+
|
|
|
+struct MemoryInfo {
|
|
|
+ MemoryInfo* next;
|
|
|
+ MemoryInfo* previous;
|
|
|
+ size_t size;
|
|
|
+ int line;
|
|
|
+ char buffer[64 - 2 * sizeof(void*) - sizeof(size_t) - sizeof(int)];
|
|
|
+ char canary[sizeof(CANARY)];
|
|
|
+};
|
|
|
+
|
|
|
+static_assert(sizeof(MemoryInfo) == 80, "memory info has invalid size");
|
|
|
+static MemoryInfo* headMemoryInfo = nullptr;
|
|
|
+
|
|
|
+static void addMemoryInfo(MemoryInfo* i, const char* file, int line, size_t n) {
|
|
|
+ i->next = nullptr;
|
|
|
+ i->previous = nullptr;
|
|
|
+ i->size = n;
|
|
|
+ i->line = line;
|
|
|
+ snprintf(i->buffer, sizeof(i->buffer), "%s", Core::getShortFileName(file));
|
|
|
+ memcpy(i->canary, CANARY, sizeof(CANARY));
|
|
|
+ memcpy(
|
|
|
+ reinterpret_cast<char*>(i) + n - sizeof(CANARY), CANARY,
|
|
|
+ sizeof(CANARY));
|
|
|
+ if(headMemoryInfo == nullptr) {
|
|
|
+ headMemoryInfo = i;
|
|
|
+ } else {
|
|
|
+ headMemoryInfo->previous = i;
|
|
|
+ i->next = headMemoryInfo;
|
|
|
+ headMemoryInfo = i;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void removeMemoryInfo(MemoryInfo* info) {
|
|
|
+ if(info->previous == nullptr) {
|
|
|
+ if(info->next == nullptr) {
|
|
|
+ headMemoryInfo = nullptr;
|
|
|
+ } else {
|
|
|
+ headMemoryInfo = info->next;
|
|
|
+ info->next->previous = nullptr;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if(info->next == nullptr) {
|
|
|
+ info->previous->next = nullptr;
|
|
|
+ } else {
|
|
|
+ info->previous->next = info->next;
|
|
|
+ info->next->previous = info->previous;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void* Core::debugAllocateRaw(const char* file, int line, size_t n) {
|
|
|
+ n += sizeof(MemoryInfo) + sizeof(CANARY);
|
|
|
+ void* p = realAllocate(n + sizeof(CANARY));
|
|
|
+ addMemoryInfo(static_cast<MemoryInfo*>(p), file, line, n);
|
|
|
+ return static_cast<char*>(p) + sizeof(MemoryInfo);
|
|
|
+}
|
|
|
+
|
|
|
+void* Core::debugZeroAllocateRaw(const char* file, int line, size_t n) {
|
|
|
+ void* p = debugAllocateRaw(file, line, n);
|
|
|
+ memset(p, 0, n);
|
|
|
+ return p;
|
|
|
+}
|
|
|
+
|
|
|
+void* Core::debugReallocateRaw(const char* file, int line, void* p, size_t n) {
|
|
|
+ if(n > 0) {
|
|
|
+ n += sizeof(MemoryInfo) + sizeof(CANARY);
|
|
|
+ }
|
|
|
+ void* rp = p;
|
|
|
+ if(rp != nullptr) {
|
|
|
+ rp = static_cast<char*>(rp) - sizeof(MemoryInfo);
|
|
|
+ removeMemoryInfo(static_cast<MemoryInfo*>(rp));
|
|
|
+ }
|
|
|
+ void* np = realReallocate(rp, n);
|
|
|
+ if(np == nullptr) {
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+ addMemoryInfo(static_cast<MemoryInfo*>(np), file, line, n);
|
|
|
+ return static_cast<char*>(np) + sizeof(MemoryInfo);
|
|
|
+}
|
|
|
+
|
|
|
+static bool checkCanary(void* p) {
|
|
|
+ return memcmp(p, CANARY, sizeof(CANARY)) != 0;
|
|
|
+}
|
|
|
+
|
|
|
+void Core::debugDeallocateRaw(void* p) {
|
|
|
if(p == nullptr) {
|
|
|
- CORE_EXIT(ErrorCode::OUT_OF_MEMORY.code); // CoverageIgnore
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ void* w = static_cast<char*>(p) - sizeof(MemoryInfo);
|
|
|
+ MemoryInfo* rp = static_cast<MemoryInfo*>(w);
|
|
|
+ rp->buffer[sizeof(rp->buffer) - 1] = '\0'; // end might be broken
|
|
|
+ if(checkCanary(rp->canary)) {
|
|
|
+ LOG_ERROR("Free at #:# violated pre canary", rp->buffer, rp->line);
|
|
|
+ EXIT(1);
|
|
|
+ } else if(checkCanary(
|
|
|
+ reinterpret_cast<char*>(rp) + rp->size - sizeof(CANARY))) {
|
|
|
+ LOG_ERROR("Free at #:# violated post canary", rp->buffer, rp->line);
|
|
|
+ EXIT(1);
|
|
|
}
|
|
|
+ removeMemoryInfo(rp);
|
|
|
+ realFree(rp);
|
|
|
+}
|
|
|
+
|
|
|
+void Core::printMemoryReport() {
|
|
|
+ for(MemoryInfo* i = headMemoryInfo; i != nullptr; i = i->next) {
|
|
|
+ LOG_ERROR("#:# was not freed", i->buffer, i->line);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void* operator new(size_t count) {
|
|
|
+ return Core::debugAllocateRaw("unknown", -1, count);
|
|
|
+}
|
|
|
+
|
|
|
+void* operator new(size_t count, const char* file, int line) {
|
|
|
+ return Core::debugAllocateRaw(file, line, count);
|
|
|
+}
|
|
|
+
|
|
|
+void* operator new[](size_t count) {
|
|
|
+ return Core::debugAllocateRaw("unknown", -1, count);
|
|
|
+}
|
|
|
+
|
|
|
+void* operator new[](size_t count, const char* file, int line) {
|
|
|
+ return Core::debugAllocateRaw(file, line, count);
|
|
|
+}
|
|
|
+
|
|
|
+void operator delete(void* p) noexcept {
|
|
|
+ Core::debugDeallocateRaw(p);
|
|
|
+}
|
|
|
+
|
|
|
+void operator delete(void* p, size_t) noexcept {
|
|
|
+ Core::debugDeallocateRaw(p);
|
|
|
+}
|
|
|
+
|
|
|
+void operator delete[](void* p) noexcept {
|
|
|
+ Core::debugDeallocateRaw(p);
|
|
|
+}
|
|
|
+
|
|
|
+void operator delete[](void* p, size_t) noexcept {
|
|
|
+ Core::debugDeallocateRaw(p);
|
|
|
+}
|
|
|
+
|
|
|
+#else
|
|
|
+
|
|
|
+void* Core::allocateRaw(size_t n) {
|
|
|
+ return realAllocate(n);
|
|
|
+}
|
|
|
+
|
|
|
+void* Core::zeroAllocateRaw(size_t n) {
|
|
|
+ void* p = allocateRaw(n);
|
|
|
+ memset(p, 0, n);
|
|
|
return p;
|
|
|
}
|
|
|
+
|
|
|
+void* Core::reallocateRaw(void* p, size_t n) {
|
|
|
+ return realReallocate(p, n);
|
|
|
+}
|
|
|
+
|
|
|
+void Core::deallocateRaw(void* p) {
|
|
|
+ realFree(p);
|
|
|
+}
|
|
|
+
|
|
|
+#endif
|