diff options
Diffstat (limited to 'vpx_mem/vpx_mem_tracker.c')
-rw-r--r-- | vpx_mem/vpx_mem_tracker.c | 822 |
1 files changed, 822 insertions, 0 deletions
diff --git a/vpx_mem/vpx_mem_tracker.c b/vpx_mem/vpx_mem_tracker.c new file mode 100644 index 000000000..4427e27fc --- /dev/null +++ b/vpx_mem/vpx_mem_tracker.c @@ -0,0 +1,822 @@ +/* + * Copyright (c) 2010 The VP8 project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license and patent + * grant that can be found in the LICENSE file in the root of the source + * tree. All contributing project authors may be found in the AUTHORS + * file in the root of the source tree. + */ + + +/* + vpx_mem_tracker.c + + jwz 2003-09-30: + Stores a list of addreses, their size, and file and line they came from. + All exposed lib functions are prefaced by vpx_ and allow the global list + to be thread safe. + Current supported platforms are: + Linux, Win32, win_ce and vx_works + Further support can be added by defining the platform specific mutex + in the memory_tracker struct as well as calls to create/destroy/lock/unlock + the mutex in vpx_memory_tracker_init/Destroy and memory_tracker_lock_mutex/unlock_mutex +*/ +#include "vpx_ports/config.h" + +#if defined(__uClinux__) +# include <lddk.h> +#endif + +#if HAVE_PTHREAD_H +# include <pthread.h> +#elif defined(WIN32) || defined(_WIN32_WCE) +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +# include <winbase.h> +#elif defined(VXWORKS) +# include <sem_lib.h> +#elif defined(NDS_NITRO) +# include <nitro.h> +# include <nitro/os.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> //VXWORKS doesn't have a malloc/memory.h file, +//this should pull in malloc,free,etc. +#include <stdarg.h> + +#include "include/vpx_mem_tracker.h" + +#undef vpx_malloc //undefine any vpx_mem macros that may affect calls to +#undef vpx_free //memory functions in this file +#undef vpx_memcpy +#undef vpx_memset + + +#ifndef USE_GLOBAL_FUNCTION_POINTERS +# define USE_GLOBAL_FUNCTION_POINTERS 0 //use function pointers instead of compiled functions. +#endif + +#if USE_GLOBAL_FUNCTION_POINTERS +static mem_track_malloc_func g_malloc = malloc; +static mem_track_calloc_func g_calloc = calloc; +static mem_track_realloc_func g_realloc = realloc; +static mem_track_free_func g_free = free; +static mem_track_memcpy_func g_memcpy = memcpy; +static mem_track_memset_func g_memset = memset; +static mem_track_memmove_func g_memmove = memmove; +# define MEM_TRACK_MALLOC g_malloc +# define MEM_TRACK_FREE g_free +# define MEM_TRACK_MEMCPY g_memcpy +# define MEM_TRACK_MEMSET g_memset +#else +# define MEM_TRACK_MALLOC vpx_malloc +# define MEM_TRACK_FREE vpx_free +# define MEM_TRACK_MEMCPY vpx_memcpy +# define MEM_TRACK_MEMSET vpx_memset +#endif // USE_GLOBAL_FUNCTION_POINTERS + +/* prototypes for internal library functions */ +static void memtrack_log(const char *fmt, ...); +static void memory_tracker_dump(); +static void memory_tracker_check_integrity(char *file, unsigned int line); +static void memory_tracker_add(size_t addr, unsigned int size, + char *file, unsigned int line, + int padded); +static int memory_tracker_remove(size_t addr); +static struct mem_block *memory_tracker_find(size_t addr); + +#if defined(NO_MUTEX) +# define memory_tracker_lock_mutex() (!g_b_mem_tracker_inited) +# define memory_tracker_unlock_mutex() +#else +static int memory_tracker_lock_mutex(); +static int memory_tracker_unlock_mutex(); +#endif + +#ifndef VPX_NO_GLOBALS +struct memory_tracker +{ + struct mem_block *head, + * tail; + int len, + totalsize; + unsigned int current_allocated, + max_allocated; + +#if HAVE_PTHREAD_H + pthread_mutex_t mutex; +#elif defined(WIN32) || defined(_WIN32_WCE) + HANDLE mutex; +#elif defined(VXWORKS) + SEM_ID mutex; +#elif defined(NDS_NITRO) + OSMutex mutex; +#elif defined(NO_MUTEX) +#else +#error "No mutex type defined for this platform!" +#endif + + int padding_size, + pad_value; +}; + +static struct memory_tracker memtrack; //our global memory allocation list +static int g_b_mem_tracker_inited = 0; //indicates whether the global list has +//been initialized (1:yes/0:no) +static struct +{ + FILE *file; + int type; + void (*func)(void *userdata, const char *fmt, va_list args); + void *userdata; +} g_logging = {NULL, 0, NULL, NULL}; +#else +# include "vpx_global_handling.h" +#define g_b_mem_tracker_inited vpxglobalm(vpxmem,g_b_mem_tracker_inited) +#define g_logging vpxglobalm(vpxmem,g_logging) +#define memtrack vpxglobalm(vpxmem,memtrack) +#endif // #ifndef VPX_NO_GLOBALS + +extern void *vpx_malloc(size_t size); +extern void vpx_free(void *memblk); +extern void *vpx_memcpy(void *dest, const void *src, size_t length); +extern void *vpx_memset(void *dest, int val, size_t length); + +/* + * + * Exposed library functions + * +*/ + +/* + vpx_memory_tracker_init(int padding_size, int pad_value) + padding_size - the size of the padding before and after each mem addr. + Values > 0 indicate that integrity checks can be performed + by inspecting these areas. + pad_value - the initial value within the padding area before and after + each mem addr. + + Initializes global memory tracker structure + Allocates the head of the list +*/ +int vpx_memory_tracker_init(int padding_size, int pad_value) +{ + if (!g_b_mem_tracker_inited) + { + if ((memtrack.head = (struct mem_block *) + MEM_TRACK_MALLOC(sizeof(struct mem_block)))) + { + int ret; + + MEM_TRACK_MEMSET(memtrack.head, 0, sizeof(struct mem_block)); + + memtrack.tail = memtrack.head; + + memtrack.current_allocated = 0; + memtrack.max_allocated = 0; + + memtrack.padding_size = padding_size; + memtrack.pad_value = pad_value; + +#if HAVE_PTHREAD_H + ret = pthread_mutex_init(&memtrack.mutex, + NULL); /*mutex attributes (NULL=default)*/ +#elif defined(WIN32) || defined(_WIN32_WCE) + memtrack.mutex = CreateMutex(NULL, /*security attributes*/ + FALSE, /*we don't want initial ownership*/ + NULL); /*mutex name*/ + ret = !memtrack.mutex; +#elif defined(VXWORKS) + memtrack.mutex = sem_bcreate(SEM_Q_FIFO, /*SEM_Q_FIFO non-priority based mutex*/ + SEM_FULL); /*SEM_FULL initial state is unlocked*/ + ret = !memtrack.mutex; +#elif defined(NDS_NITRO) + os_init_mutex(&memtrack.mutex); + ret = 0; +#elif defined(NO_MUTEX) + ret = 0; +#endif + + if (ret) + { + memtrack_log("vpx_memory_tracker_init: Error creating mutex!\n"); + + MEM_TRACK_FREE(memtrack.head); + memtrack.head = NULL; + } + else + { + memtrack_log("Memory Tracker init'd, v."vpx_mem_tracker_version" pad_size:%d pad_val:0x%x %d\n" + , padding_size + , pad_value + , pad_value); + g_b_mem_tracker_inited = 1; + } + } + } + + return g_b_mem_tracker_inited; +} + +/* + vpx_memory_tracker_destroy() + If our global struct was initialized zeros out all its members, + frees memory and destroys it's mutex +*/ +void vpx_memory_tracker_destroy() +{ + if (!memory_tracker_lock_mutex()) + { + struct mem_block *p = memtrack.head, + * p2 = memtrack.head; + + memory_tracker_dump(); + + while (p) + { + p2 = p; + p = p->next; + + MEM_TRACK_FREE(p2); + } + + memtrack.head = NULL; + memtrack.tail = NULL; + memtrack.len = 0; + memtrack.current_allocated = 0; + memtrack.max_allocated = 0; + + if (!g_logging.type && g_logging.file && g_logging.file != stderr) + { +#if !defined(NDS_NITRO) + fclose(g_logging.file); +#endif + g_logging.file = NULL; + } + + memory_tracker_unlock_mutex(); + + g_b_mem_tracker_inited = 0; + } +} + +/* + vpx_memory_tracker_add(size_t addr, unsigned int size, + char * file, unsigned int line) + addr - memory address to be added to list + size - size of addr + file - the file addr was referenced from + line - the line in file addr was referenced from + Adds memory address addr, it's size, file and line it came from + to the global list via the thread safe internal library function +*/ +void vpx_memory_tracker_add(size_t addr, unsigned int size, + char *file, unsigned int line, + int padded) +{ + memory_tracker_add(addr, size, file, line, padded); +} + +/* + vpx_memory_tracker_remove(size_t addr) + addr - memory address to be removed from list + Removes addr from the global list via the thread safe + internal remove function + Return: + Same as described for memory_tracker_remove +*/ +int vpx_memory_tracker_remove(size_t addr) +{ + return memory_tracker_remove(addr); +} + +/* + vpx_memory_tracker_find(size_t addr) + addr - address to be found in list + Return: + If found, pointer to the memory block that matches addr + NULL otherwise +*/ +struct mem_block *vpx_memory_tracker_find(size_t addr) +{ + struct mem_block *p = NULL; + + if (!memory_tracker_lock_mutex()) + { + p = memory_tracker_find(addr); + memory_tracker_unlock_mutex(); + } + + return p; +} + +/* + vpx_memory_tracker_dump() + Locks the memory tracker's mutex and calls the internal + library function to dump the current contents of the + global memory allocation list +*/ +void vpx_memory_tracker_dump() +{ + if (!memory_tracker_lock_mutex()) + { + memory_tracker_dump(); + memory_tracker_unlock_mutex(); + } +} + +/* + vpx_memory_tracker_check_integrity(char* file, unsigned int line) + file - The file name where the check was placed + line - The line in file where the check was placed + Locks the memory tracker's mutex and calls the internal + integrity check function to inspect every address in the global + memory allocation list +*/ +void vpx_memory_tracker_check_integrity(char *file, unsigned int line) +{ + if (!memory_tracker_lock_mutex()) + { + memory_tracker_check_integrity(file, line); + memory_tracker_unlock_mutex(); + } +} + +/* + vpx_memory_tracker_set_log_type + Sets the logging type for the memory tracker. Based on the value it will + direct its output to the appropriate place. + Return: + 0: on success + -1: if the logging type could not be set, because the value was invalid + or because a file could not be opened +*/ +int vpx_memory_tracker_set_log_type(int type, char *option) +{ + int ret = -1; + + switch (type) + { + case 0: + g_logging.type = 0; + + if (!option) + { + g_logging.file = stderr; + ret = 0; + } + +#if !defined(NDS_NITRO) + else + { + if ((g_logging.file = fopen((char *)option, "w"))) + ret = 0; + } + +#endif + break; +#if defined(WIN32) && !defined(_WIN32_WCE) + case 1: + g_logging.type = type; + ret = 0; + break; +#endif + default: + break; + } + + //output the version to the new logging destination + if (!ret) + memtrack_log("Memory Tracker logging initialized, " + "Memory Tracker v."vpx_mem_tracker_version"\n"); + + return ret; +} + +/* + vpx_memory_tracker_set_log_func + Sets a logging function to be used by the memory tracker. + Return: + 0: on success + -1: if the logging type could not be set because logfunc was NULL +*/ +int vpx_memory_tracker_set_log_func(void *userdata, + void(*logfunc)(void *userdata, + const char *fmt, va_list args)) +{ + int ret = -1; + + if (logfunc) + { + g_logging.type = -1; + g_logging.userdata = userdata; + g_logging.func = logfunc; + ret = 0; + } + + //output the version to the new logging destination + if (!ret) + memtrack_log("Memory Tracker logging initialized, " + "Memory Tracker v."vpx_mem_tracker_version"\n"); + + return ret; +} + +/* + * + * END - Exposed library functions + * +*/ + + +/* + * + * Internal library functions + * +*/ + +static void memtrack_log(const char *fmt, ...) +{ + va_list list; + + va_start(list, fmt); + + switch (g_logging.type) + { + case -1: + + if (g_logging.func) + g_logging.func(g_logging.userdata, fmt, list); + + break; + case 0: + + if (g_logging.file) + { + vfprintf(g_logging.file, fmt, list); + fflush(g_logging.file); + } + + break; +#if defined(WIN32) && !defined(_WIN32_WCE) + case 1: + { + char temp[1024]; + _vsnprintf(temp, sizeof(temp) / sizeof(char) - 1, fmt, list); + OutputDebugString(temp); + } + break; +#endif + default: + break; + } + + va_end(list); +} + +/* + memory_tracker_dump() + Dumps the current contents of the global memory allocation list +*/ +static void memory_tracker_dump() +{ + int i = 0; + struct mem_block *p = (memtrack.head ? memtrack.head->next : NULL); + + memtrack_log("\n_currently Allocated= %d; Max allocated= %d\n", + memtrack.current_allocated, memtrack.max_allocated); + + while (p) + { +#if defined(WIN32) && !defined(_WIN32_WCE) + + /*when using outputdebugstring, output filenames so they + can be clicked to be opened in visual studio*/ + if (g_logging.type == 1) + memtrack_log("memblocks[%d].addr= 0x%.8x, memblocks[%d].size= %d, file:\n" + " %s(%d):\n", i, + p->addr, i, p->size, + p->file, p->line); + else +#endif + memtrack_log("memblocks[%d].addr= 0x%.8x, memblocks[%d].size= %d, file: %s, line: %d\n", i, + p->addr, i, p->size, + p->file, p->line); + +#ifdef NDS_NITRO + + if (!(i % 20)) os_sleep(500); + +#endif + + p = p->next; + ++i; + } + + memtrack_log("\n"); +} + +/* + memory_tracker_check_integrity(char* file, unsigned int file) + file - the file name where the check was placed + line - the line in file where the check was placed + If a padding_size was supplied to vpx_memory_tracker_init() + this function will check ea. addr in the list verifying that + addr-padding_size and addr+padding_size is filled with pad_value +*/ +static void memory_tracker_check_integrity(char *file, unsigned int line) +{ + if (memtrack.padding_size) + { + int i, + index = 0; + unsigned char *p_show_me, + * p_show_me2; + unsigned int tempme = memtrack.pad_value, + dead1, + dead2; + unsigned char *x_bounds; + struct mem_block *p = memtrack.head->next; + + while (p) + { + //x_bounds = (unsigned char*)p->addr; + //back up VPX_BYTE_ALIGNMENT + //x_bounds -= memtrack.padding_size; + + if (p->padded) // can the bounds be checked? + { + /*yes, move to the address that was actually allocated + by the vpx_* calls*/ + x_bounds = (unsigned char *)(((size_t *)p->addr)[-1]); + + for (i = 0; i < memtrack.padding_size; i += sizeof(unsigned int)) + { + p_show_me = (x_bounds + i); + p_show_me2 = (unsigned char *)(p->addr + p->size + i); + + MEM_TRACK_MEMCPY(&dead1, p_show_me, sizeof(unsigned int)); + MEM_TRACK_MEMCPY(&dead2, p_show_me2, sizeof(unsigned int)); + + if ((dead1 != tempme) || (dead2 != tempme)) + { + memtrack_log("\n[vpx_mem integrity check failed]:\n" + " index[%d,%d] {%s:%d} addr=0x%x, size=%d," + " file: %s, line: %d c0:0x%x c1:0x%x\n", + index, i, file, line, p->addr, p->size, p->file, + p->line, dead1, dead2); + } + } + } + + ++index; + p = p->next; + } + } +} + +/* + memory_tracker_add(size_t addr, unsigned int size, + char * file, unsigned int line) + Adds an address (addr), it's size, file and line number to our list. + Adjusts the total bytes allocated and max bytes allocated if necessary. + If memory cannot be allocated the list will be destroyed. +*/ +void memory_tracker_add(size_t addr, unsigned int size, + char *file, unsigned int line, + int padded) +{ + if (!memory_tracker_lock_mutex()) + { + struct mem_block *p; + + p = MEM_TRACK_MALLOC(sizeof(struct mem_block)); + + if (p) + { + p->prev = memtrack.tail; + p->prev->next = p; + p->addr = addr; + p->size = size; + p->line = line; + p->file = file; + p->padded = padded; + p->next = NULL; + + memtrack.tail = p; + + memtrack.current_allocated += size; + + if (memtrack.current_allocated > memtrack.max_allocated) + memtrack.max_allocated = memtrack.current_allocated; + + //memtrack_log("memory_tracker_add: added addr=0x%.8x\n", addr); + + memory_tracker_unlock_mutex(); + } + else + { + memtrack_log("memory_tracker_add: error allocating memory!\n"); + memory_tracker_unlock_mutex(); + vpx_memory_tracker_destroy(); + } + } +} + +/* + memory_tracker_remove(size_t addr) + Removes an address and its corresponding size (if they exist) + from the memory tracker list and adjusts the current number + of bytes allocated. + Return: + 0: on success + -1: if the mutex could not be locked + -2: if the addr was not found in the list +*/ +int memory_tracker_remove(size_t addr) +{ + int ret = -1; + + if (!memory_tracker_lock_mutex()) + { + struct mem_block *p; + + if ((p = memory_tracker_find(addr))) + { + memtrack.current_allocated -= p->size; + + p->prev->next = p->next; + + if (p->next) + p->next->prev = p->prev; + else + memtrack.tail = p->prev; + + ret = 0; + MEM_TRACK_FREE(p); + } + else + { + if (addr) + memtrack_log("memory_tracker_remove(): addr not found in list," + " 0x%.8x\n", addr); + + ret = -2; + } + + memory_tracker_unlock_mutex(); + } + + return ret; +} + +/* + memory_tracker_find(size_t addr) + Finds an address in our addrs list + NOTE: the mutex MUST be locked in the other internal + functions before calling this one. This avoids + the need for repeated locking and unlocking as in Remove + Returns: pointer to the mem block if found, NULL otherwise +*/ +static struct mem_block *memory_tracker_find(size_t addr) +{ + struct mem_block *p = NULL; + + if (memtrack.head) + { + p = memtrack.head->next; + + while (p && (p->addr != addr)) + p = p->next; + } + + return p; +} + + +#if !defined(NO_MUTEX) +/* + memory_tracker_lock_mutex() + Locks the memory tracker mutex with a platform specific call + Returns: + 0: Success + <0: Failure, either the mutex was not initialized + or the call to lock the mutex failed +*/ +static int memory_tracker_lock_mutex() +{ + int ret = -1; + + if (g_b_mem_tracker_inited) + { + +#if HAVE_PTHREAD_H + ret = pthread_mutex_lock(&memtrack.mutex); +#elif defined(WIN32) || defined(_WIN32_WCE) + ret = WaitForSingleObject(memtrack.mutex, INFINITE); +#elif defined(VXWORKS) + ret = sem_take(memtrack.mutex, WAIT_FOREVER); +#elif defined(NDS_NITRO) + os_lock_mutex(&memtrack.mutex); + ret = 0; +#endif + + if (ret) + { + memtrack_log("memory_tracker_lock_mutex: mutex lock failed\n"); + } + } + + return ret; +} + +/* + memory_tracker_unlock_mutex() + Unlocks the memory tracker mutex with a platform specific call + Returns: + 0: Success + <0: Failure, either the mutex was not initialized + or the call to unlock the mutex failed +*/ +static int memory_tracker_unlock_mutex() +{ + int ret = -1; + + if (g_b_mem_tracker_inited) + { + +#if HAVE_PTHREAD_H + ret = pthread_mutex_unlock(&memtrack.mutex); +#elif defined(WIN32) || defined(_WIN32_WCE) + ret = !ReleaseMutex(memtrack.mutex); +#elif defined(VXWORKS) + ret = sem_give(memtrack.mutex); +#elif defined(NDS_NITRO) + os_unlock_mutex(&memtrack.mutex); + ret = 0; +#endif + + if (ret) + { + memtrack_log("memory_tracker_unlock_mutex: mutex unlock failed\n"); + } + } + + return ret; +} +#endif + +/* + vpx_memory_tracker_set_functions + + Sets the function pointers for the standard library functions. + + Return: + 0: on success + -1: if the use global function pointers is not set. +*/ +int vpx_memory_tracker_set_functions(mem_track_malloc_func g_malloc_l + , mem_track_calloc_func g_calloc_l + , mem_track_realloc_func g_realloc_l + , mem_track_free_func g_free_l + , mem_track_memcpy_func g_memcpy_l + , mem_track_memset_func g_memset_l + , mem_track_memmove_func g_memmove_l) +{ +#if USE_GLOBAL_FUNCTION_POINTERS + + if (g_malloc_l) + g_malloc = g_malloc_l; + + if (g_calloc_l) + g_calloc = g_calloc_l; + + if (g_realloc_l) + g_realloc = g_realloc_l; + + if (g_free_l) + g_free = g_free_l; + + if (g_memcpy_l) + g_memcpy = g_memcpy_l; + + if (g_memset_l) + g_memset = g_memset_l; + + if (g_memmove_l) + g_memmove = g_memmove_l; + + return 0; +#else + (void)g_malloc_l; + (void)g_calloc_l; + (void)g_realloc_l; + (void)g_free_l; + (void)g_memcpy_l; + (void)g_memset_l; + (void)g_memmove_l; + return -1; +#endif +} |