#include #include #include #include #include "GMbase/system.h" #include "GMbase/systems/Allegro/util.h" #include "GMbase/systems/Allegro/error.h" #include "GMbase/systems/Allegro/malloc.h" #undef malloc #undef realloc #undef free typedef struct { void* result; // pointer returned to the caller: this is the pointer to the data without the fences size_t size; // size asked by the caller: this is the size of the data without the fences bool realloced; // whether this pointer was ever realloced int line; char* file; } Malloc_trace; #define nb_max_malloced_pointers 4096 static Malloc_trace traces[nb_max_malloced_pointers]; static int nb_malloced_pointers = 0; static char tmp_string[1024]; static size_t max_size_reached; static size_t current_size; #define fence_size (32 * sizeof(char)) #define fence_value ((char) 223) /** Whether the closing function for this subsystem was called or this subsystem is not initialized. */ static bool proper_end_called = true; static void end() { if (! proper_end_called) { System_internal_error(false, "Malloc subsystem not ended.\n"); PrivateSystem_malloc_end(); } } void System_log_memory_usage() { int nb_bytes; nb_bytes = max_size_reached / sizeof(char); System_log("Maximum memory usage was: "); if (nb_bytes >= 1*1024*1024) { System_log("%.1f MB (%d bytes).\n", ((float) nb_bytes) / (1024 * 1024), nb_bytes); } else if (nb_bytes >= 3*1024) { System_log("%.1f kB (%d bytes).\n", ((float) nb_bytes) / 1024, nb_bytes); } else { System_log("%d bytes.\n", nb_bytes); } } void PrivateSystem_malloc_end() { int i; proper_end_called = true; System_log_memory_usage(); for (i = 0; i < nb_malloced_pointers; i++) { System_internal_error(false, "Memory leak: Area malloced at %s: %d (size: %d) was never freed.\n", traces[i].file, traces[i].line, traces[i].size); } } void PrivateSystem_malloc_init() { current_size = 0; max_size_reached = 0; proper_end_called = false; atexit(end); } void* _System_malloc(char* file, int line, size_t size) { static bool too_many_mallocs = false; void* mem; if (size == 0) { return NULL; } mem = malloc(size + 2*fence_size); //debug_log("malloc(%d) at %s:%d -> %p\n", size, file, line, mem); if (mem != NULL) { // Register mem if (! too_many_mallocs) { if (nb_malloced_pointers >= nb_max_malloced_pointers) { PrivateSystem_internal_error(false, "Too many mallocs.\n"); too_many_mallocs = true; } else { traces[nb_malloced_pointers].result = mem + fence_size; traces[nb_malloced_pointers].size = size; traces[nb_malloced_pointers].realloced = false; traces[nb_malloced_pointers].line = line; traces[nb_malloced_pointers].file = file; current_size += size; if (current_size > max_size_reached) { max_size_reached = current_size; } nb_malloced_pointers++; } } memset(mem, fence_value, fence_size); memset(mem + fence_size + size, fence_value, fence_size); System_check_malloc(mem + fence_size, "Malloc auto-check\n"); return mem + fence_size; } // TODO handle this properly System_error(true, "Not enough memory.\n"); return NULL; // compiler candy } void* _System_realloc(char* file, int line, void* p, size_t size) { void* mem; if (p == NULL) { return System_malloc(size); } System_check_malloc(p, "Realloc check before work\n"); mem = realloc(p - fence_size, size + 2*fence_size); //debug_log("realloc(%p, %d) at %s:%d -> %p\n", p, size, file, line, mem); if (mem != NULL) { int i; for (i = 0; i < nb_malloced_pointers; i++) { if (p == traces[i].result) { current_size += size - traces[i].size; if (current_size > max_size_reached) { max_size_reached = current_size; } traces[i].result = mem + fence_size; traces[i].size = size; traces[i].realloced = true; break; } } if (i == nb_malloced_pointers) { System_internal_error(false, "%s: %d: Bad pointer realloced (this pointer was never malloced).\n", file, line); } memset(mem, fence_value, fence_size); memset(mem + fence_size + size, fence_value, fence_size); System_check_malloc(mem + fence_size, "Realloc auto-check\n"); return mem + fence_size; } // TODO handle this properly System_error(true, "Not enough memory.\n"); return NULL; // compiler candy } void _System_free(char* file, int line, void* p) { int i; //debug_log("free(%p) at %s:%d\n", p, file, line); if (p == NULL) { return; } System_check_malloc(p, "Free check before work\n"); for (i = 0; i < nb_malloced_pointers; i++) { if (p == traces[i].result) { free(p); nb_malloced_pointers--; traces[i] = traces[nb_malloced_pointers]; //debug_log("}free\n"); return; } } //debug_log("}free:ERROR\n"); System_internal_error(false, "%s: %d: Bad pointer freed. This pointer was never malloced.\n", file, line); } /** * If p is inside any previously malloced memory, returns the corresponding trace's index. * Otherwise returns -1. // * Returns whether p is inside any previously malloced memory. Returns true when p == NULL. */ static int _check_malloc(void* p) { int i; if (p == NULL) return -1; for (i = 0; i < nb_malloced_pointers; i++) { if ((p >= traces[i].result) && (p < traces[i].result + traces[i].size)) { return i; } } return -1; } void System_check_malloc(void* p, char* format, ...) { int trace_index; if (p == NULL) return; trace_index = _check_malloc(p); if (trace_index == -1) { sprintf(tmp_string, "Bad pointer: "); sprintf_args(format, tmp_string + strlen(tmp_string)); System_internal_error(true, tmp_string); } else { int i; for (i = 0; i < fence_size / sizeof(char); i++) { if ((((char*) p) - fence_size + i != fence_value) || (((char*) p) + traces[trace_index].size + i != fence_value)) { sprintf(tmp_string, "Malloced area overflown: Memory around area malloced at %s: %d (size: %d) was illegally written into.\n", traces[trace_index].file, traces[trace_index].line, traces[trace_index].size); sprintf_args(format, tmp_string + strlen(tmp_string)); System_internal_error(true, tmp_string); } } } } /* // MEMC static bool is_memc_on = false; void set_memc_on() { is_memc_on = true; } void set_memc_off() { is_memc_on = false; } void memc_check(void* p, char* file, long line) { if (p == NULL) { debug_log("NULL pointer at %s:%d\n", file, line); } if (! is_memc_on) return; if (! _check_malloc(p)) { debug_log("Pointer to non-malloced area at %s:%d\n", file, line); } } */