diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f7cd248..64508f63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,7 @@ ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug") SET(SOURCES # game sources src/main.cpp + src/allocator.cpp # game net src/protocollogin.cpp diff --git a/src/allocator.cpp b/src/allocator.cpp new file mode 100644 index 00000000..97f48f5d --- /dev/null +++ b/src/allocator.cpp @@ -0,0 +1,327 @@ +#include "allocator.h" + +#include +#include + +#if defined(REG_EIP) +# define MACHINE_X86 +# define REGFORMAT "0x%08x" +# define ADDRTYPE unsigned int +#elif defined(REG_RIP) +# define MACHINE_X86_64 +# define REGFORMAT "0x%016lx" +# define ADDRTYPE long unsigned int +#endif + +Allocator Allocator::m_instance __attribute__((init_priority(101))); + +static void addr2line(ADDRTYPE address, const char* name, bool viewSource = false) +{ + char tmpbuf[1024]; + char *pos; + + printf(REGFORMAT": ", address); + + snprintf(tmpbuf, sizeof(tmpbuf), "addr2line --functions --demangle -e %s "REGFORMAT, name, address); + + FILE *output = popen(tmpbuf, "r"); + if(output) { + char filesource[1024]; + int fileline = 0; + filesource[0] = '\0'; + char *line; + + while(!feof(output)) { + line = fgets(tmpbuf, sizeof(tmpbuf) - 1, output); + if(!line) + break; + + pos = strchr(line, '\n'); + if(pos > 0) + pos[0] = '\0'; + + int func = 1; + pos = strchr(line, ':'); + if(pos > 0 && pos[1] != ':') + func = 0; + + if(func) { + printf("%s ", line); + } else if(strncmp(line,"??:0",4) != 0) { + if(pos > 0 && fileline == 0) { + strncpy(filesource, line, (int)(pos - line)); + filesource[(int)(pos - line)] = '\0'; + fileline = atoi(line + (int)(pos - line) + 1); + } + pos = strrchr(line, '/'); + if(!pos) + pos = line; + else + pos++; + printf("(%s) ", pos); + } + } + pclose(output); + + pos = (char*)strrchr(name, '/'); + if(!pos) + pos = (char*) name; + else + pos++; + printf("(%s)", pos); + + if(viewSource) { + if(filesource[0] && fileline > 0) { + output = fopen(filesource,"rb"); + if(output) { + int i; + for(i=0; i= fileline-1 && strlen(line) > 1) { + pos = strchr(line, '\n'); + if(pos > 0) + pos[0] = '\0'; + printf("\n\t\t%d:%s",i,line); + } + } + + fclose(output); + } + } + } + } + + printf("\n"); +} + +Allocator::Allocator() : m_inside(false) +{ + +} + +Allocator::~Allocator() +{ + boost::recursive_mutex::scoped_lock lock(m_allocatorLock); + + dumpLeaks(); + + m_inside = true; + + for(AllocationBlocksList::iterator it = m_allocationsBlocks.begin(), end = m_allocationsBlocks.end(); it != end; ++it) { + AllocationBlock* block = (*it); + block->~AllocationBlock(); + free(block); + } + m_allocationsBlocks.clear(); + m_allocationAddresses.clear(); + + m_inside = false; +} + +//TODO: dump leaks to a file +void Allocator::dumpLeaks() +{ + boost::recursive_mutex::scoped_lock lock(m_allocatorLock); + + m_inside = true; + + if(m_allocationAddresses.size() > 0) { + uint32_t definitelyLostBytes = 0; + uint32_t blockNumber = 1; + uint32_t countRecords = 0; + uint32_t numberOfLeakedBlocks = 0; + uint32_t numberOfBlocks = m_allocationsBlocks.size(); + + + for(AllocationBlocksList::iterator it = m_allocationsBlocks.begin(), end = m_allocationsBlocks.end(); it != end; ++it) { + AllocationBlock* block = (*it); + if(block->records != 0) { + numberOfLeakedBlocks++; + } + } + + printf("== LOST BLOCKS:\n"); + for(AllocationBlocksList::iterator it = m_allocationsBlocks.begin(), end = m_allocationsBlocks.end(); it != end; ++it) { + AllocationBlock* block = (*it); + if(block->records != 0) { + uint32_t lostBytes = block->bytes * block->records; + + definitelyLostBytes += lostBytes; + countRecords += block->records; + + printf("%d bytes in %d block\'s records are definitely lost in loss %d of %d\n", + lostBytes, block->records, blockNumber, numberOfLeakedBlocks); + + char **strings = backtrace_symbols(block->backtraceBuffer, block->backtraceSize); + for(int i = 0; i < block->backtraceSize; i++) { + if(i == 0) { + printf("\tat "); + } else { + printf("\tby "); + } + std::string str = strings[i]; + addr2line((ADDRTYPE)block->backtraceBuffer[i], str.substr(0, str.find('(')).c_str()); + } + printf("\n"); + free(strings); + blockNumber++; + } + } + + + printf("== LEAK SUMMARY:\n"); + printf("definitely lost: %d bytes in %d blocks\n", definitelyLostBytes, numberOfLeakedBlocks); + printf("unallocated records: %d records in %d blocks\n", countRecords, numberOfLeakedBlocks); + printf("leaked blocks: %d in %d blocks\n", numberOfLeakedBlocks, numberOfBlocks); + } + + m_inside = false; +} + +void printBacktrace() +{ + void *buffer[128]; + int size = backtrace(buffer, 128); + char **strings = backtrace_symbols(buffer, size); + for(int i = 1; i < size; i++) { + if(i == 1) { + printf("\tfrom "); + } else { + printf("\tat "); + } + std::string str = strings[i]; + addr2line((ADDRTYPE)buffer[i], str.substr(0, str.find('(')).c_str()); + } + printf("\n"); + free(strings); +} + +//TODO: dump allocator memory stats by blocks to a file + +AllocationBlock* Allocator::findBlock(void **backtraceBuffer, int backtraceSize, uint32_t bytes) +{ + for(AllocationBlocksList::iterator it = m_allocationsBlocks.begin(), end = m_allocationsBlocks.end(); it != end; ++it) { + AllocationBlock* block = (*it); + + if(block->bytes != bytes || block->backtraceSize != backtraceSize) { + continue; + } + + bool isSame = true; + for(int i = 0; i < backtraceSize; i++) { + if(block->backtraceBuffer[i] != backtraceBuffer[i]) { + isSame = false; + break; + } + } + + if(isSame) { + return block; + } + } + return NULL; +} + +void *Allocator::allocate(size_t bytes) +{ + boost::recursive_mutex::scoped_lock lock(m_allocatorLock); + + if(m_inside) { + return malloc(bytes); + } else { + void* p = malloc(bytes + sizeof(ADDRTYPE)); + void* p2 = (void*)((ADDRTYPE)p + sizeof(ADDRTYPE)); + + m_inside = true; + + static void* buffer[128]; + int size = backtrace(buffer, 128); + + AllocationBlock* block = findBlock(&buffer[1], size-1, bytes); + if(!block) { + block = (AllocationBlock *) malloc(sizeof(AllocationBlock)); + + block->backtraceBuffer = (void**) malloc((sizeof(void*)) * (size - 1)); + block->backtraceSize = size-1; + block->bytes = bytes; + block->records = 0; + + for(int i=0;ibacktraceBuffer[i] = buffer[i+1]; + } + + m_allocationsBlocks.push_front(block); + } + block->records += 1; + + m_allocationAddresses.push_front(block); + *(ADDRTYPE *)p = (ADDRTYPE)m_allocationAddresses.begin()._M_node; + + m_inside = false; + + return p2; + } +} + +void Allocator::deallocate(void* p) +{ + boost::recursive_mutex::scoped_lock lock(m_allocatorLock); + + if(m_inside) { + free(p); + } else if(p == NULL) { + m_inside = true; + + printf("attempt to delete NULL address\n"); + printBacktrace(); + + m_inside = false; + return; + } else { + void *p2 = (void*)((ADDRTYPE)p - sizeof(ADDRTYPE)); + + if(!m_inside) { + m_inside = true; + + AllocationsAddressesMap::iterator it((std::__detail::_List_node_base*)(*(ADDRTYPE*)p2)); + + AllocationBlock* block = (*it); + block->records -= 1; + memset(p2, 0x00, block->bytes); + + m_allocationAddresses.erase(it); + + m_inside = false; + + free(p2); + } else { + free(p); + } + } +} + +void* operator new(size_t bytes, int dummy) +{ + return Allocator::instance()->allocate(bytes); +} + +void* operator new(size_t bytes) +{ + return Allocator::instance()->allocate(bytes); +} + +void* operator new[](size_t bytes) +{ + return Allocator::instance()->allocate(bytes); +} + +void operator delete(void *p) +{ + Allocator::instance()->deallocate(p); +} + +void operator delete[](void* p) +{ + Allocator::instance()->deallocate(p); +} diff --git a/src/allocator.h b/src/allocator.h new file mode 100644 index 00000000..dd428c85 --- /dev/null +++ b/src/allocator.h @@ -0,0 +1,67 @@ +#ifndef __ALLOCATOR_H__ +#define __ALLOCATOR_H__ + +#include +#include +#include +#include + +class Allocator; + +struct AllocationBlock +{ + ~AllocationBlock() { free(backtraceBuffer); } + + uint32_t bytes; + uint32_t records; + void** backtraceBuffer; + uint8_t backtraceSize; + + friend class Allocator; +}; + +//TODO: allocattion tags +/* +struct AllocationTag +{ + std::_List_node_base *listNode; +};*/ + +class Allocator +{ +public: + Allocator(); + ~Allocator(); + + void *allocate(size_t bytes); + void deallocate(void* p); + + void dumpLeaks(); + + static Allocator* instance() { + return &m_instance; + } + +private: + AllocationBlock* findBlock(void **backtrace, int backtraceSize, uint32_t bytes); + + typedef std::list AllocationBlocksList; + AllocationBlocksList m_allocationsBlocks; + + typedef std::list AllocationsAddressesMap; + AllocationsAddressesMap m_allocationAddresses; + + boost::recursive_mutex m_allocatorLock; + bool m_inside; + + static Allocator m_instance; +}; + +void* operator new(size_t bytes, int dummy); +void* operator new(size_t bytes); +void* operator new[](size_t bytes); +void operator delete(void *p); +void operator delete[](void* p); + +#endif + diff --git a/src/framework/core/dispatcher.cpp b/src/framework/core/dispatcher.cpp index e1f763a1..ef279a63 100644 --- a/src/framework/core/dispatcher.cpp +++ b/src/framework/core/dispatcher.cpp @@ -28,6 +28,15 @@ Dispatcher g_dispatcher; +void Dispatcher::cleanup() +{ + while(!m_scheduledTaskList.empty()) { + ScheduledTask *task = m_scheduledTaskList.top(); + m_scheduledTaskList.pop(); + delete task; + } +} + void Dispatcher::poll() { while(!m_taskList.empty()) { diff --git a/src/framework/core/dispatcher.h b/src/framework/core/dispatcher.h index 5dbe7710..e84101f3 100644 --- a/src/framework/core/dispatcher.h +++ b/src/framework/core/dispatcher.h @@ -46,6 +46,9 @@ class Dispatcher public: Dispatcher() { } + /// Cleanup scheduled events + void cleanup(); + /// Execute scheduled events void poll(); diff --git a/src/framework/core/engine.cpp b/src/framework/core/engine.cpp index e8bc445c..0d3bd70e 100644 --- a/src/framework/core/engine.cpp +++ b/src/framework/core/engine.cpp @@ -58,6 +58,7 @@ void Engine::terminate() // terminate stuff g_fonts.terminate(); g_graphics.terminate(); + g_dispatcher.cleanup(); } void Engine::poll() diff --git a/src/framework/graphics/animatedtexture.h b/src/framework/graphics/animatedtexture.h index 2dd1b742..b85bd876 100644 --- a/src/framework/graphics/animatedtexture.h +++ b/src/framework/graphics/animatedtexture.h @@ -32,7 +32,7 @@ class AnimatedTexture : public Texture { public: AnimatedTexture(int width, int height, int channels, int numFrames, uchar *framesPixels, int *framesDelay); - virtual ~AnimatedTexture(); + ~AnimatedTexture(); void enableBilinearFilter(); void processAnimation();