diff options
author | 2015-11-08 12:56:12 +0100 | |
---|---|---|
committer | 2015-11-08 12:56:12 +0100 | |
commit | 7c19aac60e12d6f5ea301bdb34d7826a01e0b06f (patch) | |
tree | f310d86aa2c6bfc19d115307dedde4eb0cd52dad /src/lib/util/corealloc.cpp | |
parent | a57b46ae933badd7441ce1644711dbb851e2b504 (diff) |
Rename *.c -> *.cpp in our source (nw)
Diffstat (limited to 'src/lib/util/corealloc.cpp')
-rw-r--r-- | src/lib/util/corealloc.cpp | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/src/lib/util/corealloc.cpp b/src/lib/util/corealloc.cpp new file mode 100644 index 00000000000..a635803e463 --- /dev/null +++ b/src/lib/util/corealloc.cpp @@ -0,0 +1,428 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + corealloc.c + + Memory allocation helpers for the helper library. + +***************************************************************************/ + +#include "corealloc.h" +#include "osdcore.h" + + +//************************************************************************** +// DEBUGGING +//************************************************************************** + +#define LOG_ALLOCS (0) +#define DEBUG_MISMATCHED_ALLOCS (0) + +// define this to initialize allocated memory to a fixed non-0 value +#ifdef MAME_DEBUG +#define INITIALIZE_ALLOCATED_MEMORY +#endif + +// define this to zap memory to a fixed non-0 value before freeing +//#define OVERWRITE_FREED_MEMORY + + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +// number of memory_entries to allocate in a block +const int memory_block_alloc_chunk = 256; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// this struct is allocated in pools to track memory allocations +// it must be a POD type!! +class memory_entry +{ +public: + // internal state + memory_entry * m_next; // link to the next entry + memory_entry * m_prev; // link to the previous entry + size_t m_size; // size of the allocation (not including this header) + void * m_base; // base of the allocation + const char * m_file; // file the allocation was made from + int m_line; // line number within that file + UINT64 m_id; // unique id + bool m_array; // array? + + // hashing prime number + static const int k_hash_prime = 6151; + + // global state + static UINT64 s_curid; // current ID + static osd_lock * s_lock; // lock for managing the list + static bool s_lock_alloc; // set to true temporarily during lock allocation + static bool s_tracking; // set to true when tracking is live + static memory_entry *s_hash[k_hash_prime];// hash table based on pointer + static memory_entry *s_freehead; // pointer to the head of the free list + + // static helpers + static memory_entry *allocate(size_t size, void *base, const char *file, int line, bool array); + static memory_entry *find(void *ptr); + static void release(memory_entry *entry, const char *file, int line); + static void report_unfreed(UINT64 start); + +private: + static void acquire_lock(); + static void release_lock(); +}; + + + +//************************************************************************** +// GLOBALS +//************************************************************************** + +// dummy zeromem object +const zeromem_t zeromem = { }; + +// globals for memory_entry +UINT64 memory_entry::s_curid = 1; +osd_lock *memory_entry::s_lock = NULL; +bool memory_entry::s_lock_alloc = false; +bool memory_entry::s_tracking = false; +memory_entry *memory_entry::s_hash[memory_entry::k_hash_prime] = { NULL }; +memory_entry *memory_entry::s_freehead = NULL; + +//************************************************************************** +// OPERATOR REPLACEMENTS +//************************************************************************** + +#ifndef NO_MEM_TRACKING + +// standard new/delete operators (try to avoid using) +void *operator new(std::size_t size) throw (std::bad_alloc) { return malloc_file_line(size, NULL, 0, false, true, false); } +void *operator new[](std::size_t size) throw (std::bad_alloc) { return malloc_file_line(size, NULL, 0, true, true, false); } +void operator delete(void *ptr) throw() { if (ptr != NULL) free_file_line(ptr, NULL, 0, false); } +void operator delete[](void *ptr) throw() { if (ptr != NULL) free_file_line(ptr, NULL, 0, true); } + +void* operator new(std::size_t size,const std::nothrow_t&) throw() { return malloc_file_line(size, NULL, 0, false, false, false); } +void* operator new[](std::size_t size, const std::nothrow_t&) throw() { return malloc_file_line(size, NULL, 0, true, false, false); } +void operator delete(void* ptr, const std::nothrow_t&) throw() { if (ptr != NULL) free_file_line(ptr, NULL, 0, false); } +void operator delete[](void* ptr, const std::nothrow_t&) throw() { if (ptr != NULL) free_file_line(ptr, NULL, 0, true); } + +#endif + +//************************************************************************** +// OPERATOR OVERLOADS - DEFINITIONS +//************************************************************************** + +// file/line new/delete operators +void *operator new(std::size_t size, const char *file, int line) throw (std::bad_alloc) { return malloc_file_line(size, file, line, false, true, false); } +void *operator new[](std::size_t size, const char *file, int line) throw (std::bad_alloc) { return malloc_file_line(size, file, line, true, true, false); } +void operator delete(void *ptr, const char *file, int line) { if (ptr != NULL) free_file_line(ptr, file, line, false); } +void operator delete[](void *ptr, const char *file, int line) { if (ptr != NULL) free_file_line(ptr, file, line, true); } + +// file/line new/delete operators with zeroing +void *operator new(std::size_t size, const char *file, int line, const zeromem_t &) throw (std::bad_alloc) { return malloc_file_line(size, file, line, false, true, true); } +void *operator new[](std::size_t size, const char *file, int line, const zeromem_t &) throw (std::bad_alloc) { return malloc_file_line(size, file, line, true, true, true); } +void operator delete(void *ptr, const char *file, int line, const zeromem_t &) { if (ptr != NULL) free_file_line(ptr, file, line, false); } +void operator delete[](void *ptr, const char *file, int line, const zeromem_t &) { if (ptr != NULL) free_file_line(ptr, file, line, true); } + + + +//************************************************************************** +// GLOBAL HELPERS +//************************************************************************** + +//------------------------------------------------- +// malloc_file_line - allocate memory with file +// and line number information +//------------------------------------------------- + +void *malloc_file_line(size_t size, const char *file, int line, bool array, bool throw_on_fail, bool clear) +{ + // allocate the memory and fail if we can't + void *result = array ? osd_malloc_array(size) : osd_malloc(size); + if (result == NULL) + { + fprintf(stderr, "Failed to allocate %d bytes (%s:%d)\n", UINT32(size), file, line); + osd_break_into_debugger("Failed to allocate RAM"); + if (throw_on_fail) + throw std::bad_alloc(); + return NULL; + } + + // zap the memory if requested + if (clear) + memset(result, 0, size); + else + { +#if !__has_feature(memory_sanitizer) && defined(INITIALIZE_ALLOCATED_MEMORY) && !defined(MAME_DEBUG_FAST) + memset(result, 0xdd, size); +#endif + } + + // add a new entry + memory_entry::allocate(size, result, file, line, array); + + return result; +} + + +//------------------------------------------------- +// free_file_line - free memory with file +// and line number information +//------------------------------------------------- + +void free_file_line(void *memory, const char *file, int line, bool array) +{ + // find the memory entry + memory_entry *entry = memory_entry::find(memory); + + // warn about untracked frees + if (entry == NULL) + { + fprintf(stderr, "Error: attempt to free untracked memory %p in %s(%d)!\n", memory, file, line); + osd_break_into_debugger("Error: attempt to free untracked memory"); + return; + } + + // warn about mismatched arrays + if (!array && entry->m_array) + { + fprintf(stderr, "Warning: attempt to free array %p with global_free in %s(%d)!\n", memory, file, line); + if (DEBUG_MISMATCHED_ALLOCS) { + osd_break_into_debugger("Error: attempt to free array with global_free"); + } + } + if (array && !entry->m_array) + { +#ifndef __INTEL_COMPILER // todo: fix this properly, it appears some optimization the compiler applies breaks our logic here + fprintf(stderr, "Warning: attempt to free single object %p with global_free_array in %s(%d)!\n", memory, file, line); + if (DEBUG_MISMATCHED_ALLOCS) { + osd_break_into_debugger("Error: attempt to free single object with global_free_array"); + } +#endif + } + +#ifdef OVERWRITE_FREED_MEMORY + // clear memory to a bogus value + memset(memory, 0xfc, entry->m_size); +#endif + + // free the entry and the memory + memory_entry::release(entry, file, line); + osd_free(memory); +} + + +//------------------------------------------------- +// track_memory - enables or disables the memory +// tracking +//------------------------------------------------- + +void track_memory(bool track) +{ + memory_entry::s_tracking = track; +} + + +//------------------------------------------------- +// next_memory_id - return the ID of the next +// allocated block +//------------------------------------------------- + +UINT64 next_memory_id() +{ + return memory_entry::s_curid; +} + + +//------------------------------------------------- +// dump_unfreed_mem - called from the exit path +// of any code that wants to check for unfreed +// memory +//------------------------------------------------- + +void dump_unfreed_mem(UINT64 start) +{ + memory_entry::report_unfreed(start); +} + + + +//************************************************************************** +// MEMORY ENTRY +//************************************************************************** + +//------------------------------------------------- +// acquire_lock - acquire the memory entry lock, +// creating a new one if needed +//------------------------------------------------- + +void memory_entry::acquire_lock() +{ + // allocate a lock on first usage + // note that osd_lock_alloc() may re-enter this path, so protect against recursion! + if (s_lock == NULL) + { + if (s_lock_alloc) + return; + s_lock_alloc = true; + s_lock = osd_lock_alloc(); + s_lock_alloc = false; + } + osd_lock_acquire(s_lock); +} + + +//------------------------------------------------- +// release_lock - release the memory entry lock +//------------------------------------------------- + +void memory_entry::release_lock() +{ + osd_lock_release(s_lock); +} + + +//------------------------------------------------- +// allocate - allocate a new memory entry +//------------------------------------------------- + +memory_entry *memory_entry::allocate(size_t size, void *base, const char *file, int line, bool array) +{ + acquire_lock(); + + // if we're out of free entries, allocate a new chunk + if (s_freehead == NULL) + { + // create a new chunk, and fail if we can't + memory_entry *entry = reinterpret_cast<memory_entry *>(osd_malloc_array(memory_block_alloc_chunk * sizeof(memory_entry))); + if (entry == NULL) + { + release_lock(); + return NULL; + } + + // add all the entries to the list + for (int entrynum = 0; entrynum < memory_block_alloc_chunk; entrynum++) + { + entry->m_next = s_freehead; + s_freehead = entry++; + } + } + + // grab a free entry + memory_entry *entry = s_freehead; + s_freehead = entry->m_next; + + // populate it + entry->m_size = size; + entry->m_base = base; + entry->m_file = s_tracking ? file : NULL; + entry->m_line = s_tracking ? line : 0; + entry->m_id = s_curid++; + entry->m_array = array; + if (LOG_ALLOCS) + fprintf(stderr, "#%06d, alloc %d bytes (%s:%d)\n", (UINT32)entry->m_id, static_cast<UINT32>(entry->m_size), entry->m_file, (int)entry->m_line); + + // add it to the alloc list + int hashval = reinterpret_cast<FPTR>(base) % k_hash_prime; + entry->m_next = s_hash[hashval]; + if (entry->m_next != NULL) + entry->m_next->m_prev = entry; + entry->m_prev = NULL; + s_hash[hashval] = entry; + + release_lock(); + return entry; +} + + +//------------------------------------------------- +// find - find a memory entry +//------------------------------------------------- + +memory_entry *memory_entry::find(void *ptr) +{ + // NULL maps to nothing + if (ptr == NULL) + return NULL; + + // scan the list under the lock + acquire_lock(); + + int hashval = reinterpret_cast<FPTR>(ptr) % k_hash_prime; + memory_entry *entry; + for (entry = s_hash[hashval]; entry != NULL; entry = entry->m_next) + if (entry->m_base == ptr) + break; + + release_lock(); + return entry; +} + + +//------------------------------------------------- +// release - release a memory entry +//------------------------------------------------- + +void memory_entry::release(memory_entry *entry, const char *file, int line) +{ + acquire_lock(); + + if (LOG_ALLOCS) + fprintf(stderr, "#%06d, release %d bytes (%s:%d)\n", (UINT32)entry->m_id, static_cast<UINT32>(entry->m_size), file, line); + + // remove ourselves from the alloc list + int hashval = reinterpret_cast<FPTR>(entry->m_base) % k_hash_prime; + if (entry->m_prev != NULL) + entry->m_prev->m_next = entry->m_next; + else + s_hash[hashval] = entry->m_next; + if (entry->m_next != NULL) + entry->m_next->m_prev = entry->m_prev; + + // add ourself to the free list + entry->m_next = s_freehead; + s_freehead = entry; + + release_lock(); +} + +/** + * @fn void memory_entry::report_unfreed(UINT64 start) + * + * @brief ------------------------------------------------- + * report_unfreed - print a list of unfreed memory to the target file + * -------------------------------------------------. + * + * @param start The start. + */ + +void memory_entry::report_unfreed(UINT64 start) +{ + acquire_lock(); + + // check for leaked memory + UINT32 total = 0; + + for (int hashnum = 0; hashnum < k_hash_prime; hashnum++) + for (memory_entry *entry = s_hash[hashnum]; entry != NULL; entry = entry->m_next) + if (entry->m_file != NULL && entry->m_id >= start) + { + if (total == 0) + fprintf(stderr, "--- memory leak warning ---\n"); + total += entry->m_size; + fprintf(stderr, "#%06d, nofree %d bytes (%s:%d)\n", (UINT32)entry->m_id, static_cast<UINT32>(entry->m_size), entry->m_file, (int)entry->m_line); + } + + release_lock(); + + if (total > 0) + fprintf(stderr, "a total of %u bytes were not freed\n", total); +} |