Newer
Older
Import / applications / HighwayDash / ports / Framework / Utilities.cpp
//  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