// BlockyFroggy
// Copyright © 2017 John Ryland.
// All rights reserved.
#include "Utilities.h"
#include <vector>
#include <ctime>
#include <cstdlib>
#include <chrono>
#include <sstream>
#include <new>
#include <dlfcn.h>
namespace AllocationTracking {
int allocationCount = 0;
int deallocationCount = 0;
int frameAllocations = 0;
#if !ENABLE_ALLOCATION_DEBUGGING
bool g_enableAllocTracing;
#else
thread_local bool g_enableAllocTracing = false;
void* realloc(void* ptr, size_t size)
{
static int realloc_hook_active = 0;
static void *(*original_realloc)(void* ptr,size_t size) = (void *(*)(void*,size_t))dlsym(RTLD_NEXT, "realloc");
void *result = original_realloc(ptr, size);
if (g_enableAllocTracing && !realloc_hook_active)
{
realloc_hook_active = 1;
//..
if (ptr != result)
{
deallocationCount++;
allocationCount++;
frameAllocations++;
}
realloc_hook_active = 0;
}
return result;
}
void* malloc(size_t size)
{
static int malloc_hook_active = 0;
static void *(*original_malloc)(size_t size) = (void *(*)(size_t))dlsym(RTLD_NEXT, "malloc");
void *result = original_malloc(size);
if (g_enableAllocTracing && !malloc_hook_active)
{
malloc_hook_active = 1;
//..
allocationCount++;
frameAllocations++;
malloc_hook_active = 0;
}
return result;
}
void free(void* ptr)
{
static int free_hook_active = 0;
static void (*original_free)(void* ptr) = (void (*)(void*))dlsym(RTLD_NEXT, "free");
original_free(ptr);
if (g_enableAllocTracing && !free_hook_active)
{
free_hook_active = 1;
//..
deallocationCount++;
free_hook_active = 0;
}
}
namespace std { enum class align_val_t : std::size_t {}; }
void * operator new(std::size_t n) throw(std::bad_alloc)
{
return malloc(n);
}
void * operator new[](std::size_t n) throw(std::bad_alloc)
{
return malloc(n);
}
void* operator new(std::size_t n, const std::nothrow_t& tag)
{
return malloc(n);
}
void * operator new[](std::size_t n, const std::nothrow_t& tag)
{
return malloc(n);
}
void operator delete[](void * p) throw()
{
free(p);
}
void operator delete(void * p) throw()
{
free(p);
}
/*
// We are missing capturing some allocations because there is an in-balance
// between allocations and deallocation, we see more deallocations than allocations.
// It is none of these functions below as breakpoints on these aren't getting hit.
#include <malloc/malloc.h>
void*reallocf(void *__ptr, size_t __size)
{
return nullptr;
}
void* memalign(size_t alignment, size_t size)
{
return nullptr;
}
void *aligned_alloc(size_t alignment, size_t size)
{
return nullptr;
}
void *valloc(size_t size)
{
return nullptr;
}
void *malloc_zone_memalign(malloc_zone_t *zone, size_t alignment, size_t size)
{
return nullptr;
}
unsigned malloc_zone_batch_malloc(malloc_zone_t *zone, size_t size, void **results, unsigned num_requested)
{
return 0;
}
void* calloc(size_t size)
{
return nullptr;
}
void * operator new(std::size_t n, std::align_val_t) throw(std::bad_alloc)
{
return malloc(n);
}
void * operator new[](std::size_t n, std::align_val_t) throw(std::bad_alloc)
{
return malloc(n);
}
void* operator new[](std::size_t n, const std::nothrow_t& tag)
{
return malloc(n);
}
void* operator new(std::size_t n, std::align_val_t al, const std::nothrow_t&)
{
return malloc(n);
}
void* operator new[](std::size_t n, std::align_val_t al, const std::nothrow_t&)
{
return malloc(n);
}
*/
#endif
} // namespace AllocationTracking
namespace Utilities {
std::vector<std::string> split(const std::string &s, char delim)
{
std::vector<std::string> elems;
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim))
elems.push_back(item);
return elems;
}
std::string g_ProgramName = "Undefined";
const char* baseNameCStr(const char* a_fileName)
{
const char* baseName = a_fileName;
while (*a_fileName != 0)
{
while (*a_fileName != '/' && *a_fileName != 0)
a_fileName++;
if (*a_fileName != 0) {
a_fileName++;
baseName = a_fileName;
}
}
return baseName;
}
std::string baseName(std::string a_fileName)
{
const size_t last_slash_idx = a_fileName.find_last_of("\\/");
return (std::string::npos != last_slash_idx) ? a_fileName.substr(last_slash_idx + 1) : a_fileName;
}
void setProgramName(const char* argv0)
{
g_ProgramName = baseName(argv0);
}
const std::string& programName()
{
return g_ProgramName;
}
std::string vFormattedString(const char* a_fmt, va_list a_args)
{
va_list args2;
va_copy(args2, a_args);
std::vector<char> buf(std::vsnprintf(nullptr, 0, a_fmt, a_args) + 1); // Extra space for '\0'
::vsnprintf(buf.data(), buf.size(), a_fmt, args2);
va_end(args2);
return std::string(buf.data(), buf.data() + buf.size() - 1); // We don't want the '\0' inside
}
std::string formattedString(const char* a_fmt, ...)
{
va_list args;
va_start(args, a_fmt);
std::string ret = vFormattedString(a_fmt, args);
va_end(args);
return ret;
}
std::string longFormatDateTime()
{
std::lldiv_t now = std::lldiv(std::chrono::system_clock::now().time_since_epoch().count(), 1000000LL);
//std::lldiv_t now = std::lldiv(clock_gettime_nsec_np(CLOCK_REALTIME), 1000000000LL);
struct tm tstruct = *localtime((time_t*)&now.quot);
return formattedString("%04d-%02d-%02d %02d:%02d:%02d.%06d+%02d%02d", 1900+tstruct.tm_year, 1+tstruct.tm_mon,
tstruct.tm_mday, tstruct.tm_hour, tstruct.tm_min, tstruct.tm_sec, now.rem,
int(tstruct.tm_gmtoff/3600), int(tstruct.tm_gmtoff%3600)/60);
}
} // namespace Utilities
#if ENABLE_UNIT_TESTS
// StackList idea
template <size_t SIZE>
class StackAlloc
{
public:
char m_array[SIZE];
};
template <size_t S>
StackAlloc<S> functionReturningStackArray()
{
return StackAlloc<S>();
}
#define FormatString(ret, x) \
auto tmp##__LINE__ = Utilities::StackString<0> x ; \
char ret[tmp##__LINE__.formattedSize()]; \
tmp##__LINE__.formatted(ret)
//extern int allocationCount;
//extern int deallocationCount;
// Unit Testing
DECLARE_UNIT_TEST(unitTestUtils)
{
// StackString tests
int oldAllocationCount = AllocationTracking::allocationCount;
int oldDeallocationCount = AllocationTracking::deallocationCount;
int startOfStack;
int *startOfStackPtr = &startOfStack;
FormatString(formattedString, ("x$1y$0z$$a$b--bool:--$2--str:--$3--int:--$4--").arg(0).arg(2).arg(true).arg("blah").arg(INT64_MAX));
int newStackLocation;
int *newStackLocationPtr = &newStackLocation;
int newAllocationCount = AllocationTracking::allocationCount;
int newDeallocationCount = AllocationTracking::deallocationCount;
size_t sizeOfStackUsed = startOfStackPtr - newStackLocationPtr;
CHECK(strcmp(formattedString, "x2y0z$ab--bool:--true--str:--blah--int:--9223372036854775807--") == 0);
CHECK(newAllocationCount == oldAllocationCount);
CHECK(newDeallocationCount == oldDeallocationCount);
CHECK(sizeOfStackUsed < 100);
// Make sure the allocation checking is enabled
delete[](new char[10]);
newAllocationCount = AllocationTracking::allocationCount;
newDeallocationCount = AllocationTracking::deallocationCount;
CHECK(newAllocationCount != oldAllocationCount);
CHECK(newDeallocationCount != oldDeallocationCount);
}
#endif