diff options
author | Sergei Golubchik <sergii@pisem.net> | 2011-12-12 22:58:24 +0100 |
---|---|---|
committer | Sergei Golubchik <sergii@pisem.net> | 2011-12-12 22:58:24 +0100 |
commit | 6cc9d0ffa0b6d9d0f19cf9445fad0e0ba11e38f8 (patch) | |
tree | 6952bb89985c14f7ad5e362dd350d191cadd0d69 /mysys | |
parent | 37c81d81b3940a21b500aff6aeb70e8b1df0b7e8 (diff) | |
download | mariadb-git-6cc9d0ffa0b6d9d0f19cf9445fad0e0ba11e38f8.tar.gz |
move safemalloc out of dbug.
remeber a real backtrace for every allocation.
make safemalloc to tract C++ new/delete too.
collateral fixes to make the test suite pass.
Diffstat (limited to 'mysys')
-rw-r--r-- | mysys/CMakeLists.txt | 16 | ||||
-rw-r--r-- | mysys/my_malloc.c | 6 | ||||
-rw-r--r-- | mysys/my_new.cc | 15 | ||||
-rw-r--r-- | mysys/mysys_priv.h | 9 | ||||
-rw-r--r-- | mysys/safemalloc.c | 341 | ||||
-rw-r--r-- | mysys/typelib.c | 3 |
6 files changed, 377 insertions, 13 deletions
diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index f258fd7bb7f..ec73140a42e 100644 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -33,7 +33,7 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c default.c rijndael.c sha1.c string.c thr_alarm.c thr_lock.c thr_mutex.c thr_rwlock.c tree.c typelib.c base64.c my_memmem.c my_getpagesize.c lf_alloc-pin.c lf_dynarray.c lf_hash.c - my_addr_resolve.c + my_addr_resolve.c safemalloc.c my_atomic.c my_getncpus.c my_safehash.c my_chmod.c my_rnd.c my_uuid.c wqueue.c waiting_threads.c ma_dyncol.c my_rdtsc.c) @@ -46,8 +46,18 @@ IF(HAVE_ALARM) SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_alarm.c) ENDIF() -IF(NOT HAVE_CXX_NEW) - # gcc as C++ compiler does not have new/delete +IF(WIN32) + SET(DEFAULT_SAFEMALLOC OFF) +ELSE() + SET(DEFAULT_SAFEMALLOC ON) +ENDIF() +OPTION(WITH_SAFEMALLOC "Use safemalloc for debug builds. Will result in slower execution." ${DEFAULT_SAFEMALLOC}) + +IF(WITH_SAFEMALLOC) + ADD_DEFINITIONS( -DSAFEMALLOC) +ENDIF() + +IF(NOT HAVE_CXX_NEW OR WITH_SAFEMALLOC) SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_new.cc) ADD_DEFINITIONS( -DUSE_MYSYS_NEW) ENDIF() diff --git a/mysys/my_malloc.c b/mysys/my_malloc.c index 24bccf37ece..82fbe3a63f7 100644 --- a/mysys/my_malloc.c +++ b/mysys/my_malloc.c @@ -37,7 +37,7 @@ void *my_malloc(size_t size, myf my_flags) if (!size) size=1; - point= DBUG_MALLOC(size); + point= sf_malloc(size); DBUG_EXECUTE_IF("simulate_out_of_memory", { my_free(point); @@ -85,7 +85,7 @@ void *my_realloc(void *oldpoint, size_t size, myf my_flags) DBUG_ASSERT(size > 0); if (!oldpoint && (my_flags & MY_ALLOW_ZERO_PTR)) DBUG_RETURN(my_malloc(size, my_flags)); - if ((point= DBUG_REALLOC(oldpoint, size)) == NULL) + if ((point= sf_realloc(oldpoint, size)) == NULL) { if (my_flags & MY_FREE_ON_ERROR) my_free(oldpoint); @@ -111,7 +111,7 @@ void my_free(void *ptr) { DBUG_ENTER("my_free"); DBUG_PRINT("my",("ptr: %p", ptr)); - DBUG_FREE(ptr); + sf_free(ptr); DBUG_VOID_RETURN; } diff --git a/mysys/my_new.cc b/mysys/my_new.cc index 377d9be22a8..8724f9cc4a4 100644 --- a/mysys/my_new.cc +++ b/mysys/my_new.cc @@ -16,7 +16,10 @@ /* This is a replacement of new/delete operators to be used when compiling - with gcc 3.0.x to avoid including libstdc++ + with gcc 3.0.x to avoid including libstdc++ + + It is also used to make all memory allocations to go through + my_malloc/my_free wrappers (for debugging/safemalloc and accounting) */ #include "mysys_priv.h" @@ -25,24 +28,22 @@ void *operator new (size_t sz) { - return (void *) malloc (sz ? sz : 1); + return (void *) my_malloc (sz ? sz : 1, MYF(0)); } void *operator new[] (size_t sz) { - return (void *) malloc (sz ? sz : 1); + return (void *) my_malloc (sz ? sz : 1, MYF(0)); } void operator delete (void *ptr) { - if (ptr) - free(ptr); + my_free(ptr); } void operator delete[] (void *ptr) throw () { - if (ptr) - free(ptr); + my_free(ptr); } C_MODE_START diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h index 2c0c8bf82f8..f5d2f301837 100644 --- a/mysys/mysys_priv.h +++ b/mysys/mysys_priv.h @@ -69,6 +69,15 @@ extern PSI_file_key key_file_proc_meminfo; extern PSI_file_key key_file_charset, key_file_cnf; #endif /* HAVE_PSI_INTERFACE */ +#ifdef SAFEMALLOC +void *sf_malloc(size_t size); +void *sf_realloc(void *ptr, size_t size); +void sf_free(void *ptr); +#else +#define sf_malloc(X) malloc(X) +#define sf_realloc(X,Y) realloc(X,Y) +#define sf_free(X) free(X) +#endif /* EDQUOT is used only in 3 C files only in mysys/. If it does not exist on diff --git a/mysys/safemalloc.c b/mysys/safemalloc.c new file mode 100644 index 00000000000..61ad9af0631 --- /dev/null +++ b/mysys/safemalloc.c @@ -0,0 +1,341 @@ +/* Copyright (C) 2000 MySQL AB, 2011 Monty Program Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/******************************************************************** + memory debugger + based on safemalloc, memory sub-system, written by Bjorn Benson +********************************************************************/ + + +#include "mysys_priv.h" +#include <my_stacktrace.h> /* my_addr_resolve */ + +#if HAVE_EXECINFO_H +#include <execinfo.h> +#endif + +/* + this can be set to 1 if we leak memory and know it + (to disable memory leak tests on exit) +*/ +int sf_leaking_memory= 0; + +#ifdef SAFEMALLOC + +/* this mutex protects all sf_* variables, and nothing else*/ +static pthread_mutex_t sf_mutex; +static int init_done= 0; + +#ifndef SF_REMEMBER_FRAMES +#define SF_REMEMBER_FRAMES 8 +#endif + +/* ignore the first two frames (sf_malloc itself, and my_malloc) */ +#define SF_FRAMES_SKIP 2 + +/* + Structure that stores information of an allocated memory block + The data is at &struct_adr+sizeof(struct irem) + Note that sizeof(struct st_irem) % sizeof(double) == 0 +*/ +struct st_irem +{ + struct st_irem *next; /* Linked list of structures */ + struct st_irem *prev; /* Other link */ + size_t datasize; /* Size requested */ +#ifdef HAVE_BACKTRACE + void *frame[SF_REMEMBER_FRAMES]; /* call stack */ +#endif + uint32 marker; /* Underrun marker value */ +}; + +static int sf_malloc_count= 0; /* Number of allocated chunks */ + +static void *sf_min_adress= (void*) (intptr)~0ULL, + *sf_max_adress= 0; + +static struct st_irem *sf_malloc_root = 0; + +#define MAGICSTART 0x14235296 /* A magic value for underrun key */ + +#define MAGICEND0 0x68 /* Magic values for overrun keys */ +#define MAGICEND1 0x34 /* " */ +#define MAGICEND2 0x7A /* " */ +#define MAGICEND3 0x15 /* " */ + +static int bad_ptr(const char *where, void *ptr); +static void free_memory(void *ptr); +static void sf_terminate(); + +/** + allocates memory +*/ + +void *sf_malloc(size_t size) +{ + struct st_irem *irem; + uchar *data; + + /* + this style of initialization looks like race conditon prone, + but it is safe under the assumption that a program does + at least one malloc() while still being single threaded. + */ + if (!init_done) + { + pthread_mutex_init(&sf_mutex, NULL); + /* disable deadlock detector, because it calls my_malloc() */ + safe_mutex_setflags(&sf_mutex, MYF_NO_DEADLOCK_DETECTION); + atexit(sf_terminate); + init_done= 1; + } + + irem= (struct st_irem *) malloc (sizeof(struct st_irem) + size + 4); + + if (!irem) + return 0; + + /* we guarantee the alignment */ + compile_time_assert(sizeof(struct st_irem) % sizeof(double) == 0); + + /* Fill up the structure */ + data= (uchar*) (irem + 1); + irem->datasize= size; + irem->prev= 0; + irem->marker= MAGICSTART; + data[size + 0]= MAGICEND0; + data[size + 1]= MAGICEND1; + data[size + 2]= MAGICEND2; + data[size + 3]= MAGICEND3; + +#ifdef HAVE_BACKTRACE + { + void *frame[SF_REMEMBER_FRAMES + SF_FRAMES_SKIP]; + int frames= backtrace(frame, array_elements(frame)); + if (frames < SF_FRAMES_SKIP) + frames= 0; + else + { + frames-= SF_FRAMES_SKIP; + memcpy(irem->frame, frame + SF_FRAMES_SKIP, sizeof(void*)*frames); + } + if (frames < SF_REMEMBER_FRAMES) + irem->frame[frames]= 0; + } +#endif + + pthread_mutex_lock(&sf_mutex); + + /* Add this structure to the linked list */ + if ((irem->next= sf_malloc_root)) + sf_malloc_root->prev= irem; + sf_malloc_root= irem; + + /* Keep the statistics */ + sf_malloc_count++; + set_if_smaller(sf_min_adress, (void*)data); + set_if_bigger(sf_max_adress, (void*)data); + + pthread_mutex_unlock(&sf_mutex); + + TRASH_ALLOC(data, size); + return data; +} + +void *sf_realloc(void *ptr, size_t size) +{ + char *data; + + if (!ptr) + return sf_malloc(size); + + if (bad_ptr("Reallocating", ptr)) + return 0; + + if ((data= sf_malloc(size))) + { + struct st_irem *irem= (struct st_irem *)ptr - 1; + set_if_smaller(size, irem->datasize); + memcpy(data, ptr, size); + free_memory(ptr); + } + return data; +} + +void sf_free(void *ptr) +{ + if (!ptr || bad_ptr("Freeing", ptr)) + return; + + free_memory(ptr); +} + +static void free_memory(void *ptr) +{ + struct st_irem *irem= (struct st_irem *)ptr - 1; + + pthread_mutex_lock(&sf_mutex); + /* Remove this structure from the linked list */ + if (irem->prev) + irem->prev->next= irem->next; + else + sf_malloc_root= irem->next; + + if (irem->next) + irem->next->prev= irem->prev; + + /* Handle the statistics */ + sf_malloc_count--; + pthread_mutex_unlock(&sf_mutex); + + /* only trash the data and magic values, but keep the stack trace */ + TRASH_FREE((uchar*)(irem + 1) - 4, irem->datasize + 8); + free(irem); + return; +} + +#ifdef HAVE_BACKTRACE +static void print_stack(void **frame) +{ + const char *err; + int i; + + if ((err= my_addr_resolve_init())) + { + fprintf(stderr, "(my_addr_resolve failure: %s)\n", err); + return; + } + + for (i=0; i < SF_REMEMBER_FRAMES && frame[i]; i++) + { + my_addr_loc loc; + if (i) + fprintf(stderr, ", "); + + if (my_addr_resolve(frame[i], &loc)) + fprintf(stderr, "..."); + else + fprintf(stderr, "%s:%u", loc.file, loc.line); + } + fprintf(stderr, "\n"); +} +#else +#define print_stack(X) fprintf(stderr, "???\n") +#endif + +static void warn(const char *format,...) +{ + va_list args; + va_start(args,format); + vfprintf(stderr, format, args); + va_end(args); + +#ifdef HAVE_BACKTRACE + { + void *frame[SF_REMEMBER_FRAMES + SF_FRAMES_SKIP]; + int frames= backtrace(frame, array_elements(frame)); + if (frames < SF_REMEMBER_FRAMES + SF_FRAMES_SKIP) + frame[frames]= 0; + print_stack(frame + SF_FRAMES_SKIP); + } +#endif +} + +static int bad_ptr(const char *where, void *ptr) +{ + struct st_irem *irem= (struct st_irem *)ptr - 1; + const uchar *magicend; + + if (((intptr) ptr) % sizeof(double)) + { + warn("Error: %s wrong aligned pointer", where); + return 1; + } + if (ptr < sf_min_adress || ptr > sf_max_adress) + { + warn("Error: %s pointer out of range", where); + return 1; + } + if (irem->marker != MAGICSTART) + { + warn("Error: %s unallocated data or underrun buffer", where); + return 1; + } + + magicend= (uchar*)ptr + irem->datasize; + if (magicend[0] != MAGICEND0 || + magicend[1] != MAGICEND1 || + magicend[2] != MAGICEND2 || + magicend[3] != MAGICEND3) + { + warn("Error: %s overrun buffer", where); + fprintf(stderr, ", allocated at "); + print_stack(irem->frame); + return 1; + } + + return 0; +} + +/* check all allocated memory list for consistency */ +static int sf_sanity() +{ + struct st_irem *irem; + int flag= 0; + int count= 0; + + pthread_mutex_lock(&sf_mutex); + count= sf_malloc_count; + for (irem= sf_malloc_root; irem && count > 0; count--, irem= irem->next) + flag+= bad_ptr("Safemalloc", irem + 1); + pthread_mutex_unlock(&sf_mutex); + if (count || irem) + { + warn("Error: Safemalloc link list destroyed"); + return 1; + } + return 0; +} + +/** + report on all the memory pieces that have not been free'd +*/ + +static void sf_terminate() +{ + size_t total= 0; + struct st_irem *irem; + + sf_sanity(); + + /* Report on all the memory that was allocated but not free'd */ + if (!sf_leaking_memory && sf_malloc_root) + { + for (irem= sf_malloc_root; irem; irem= irem->next) + { + fprintf(stderr, "Warning: %4lu bytes lost, allocated at ", + (ulong) irem->datasize); + print_stack(irem->frame); + total+= irem->datasize; + } + fprintf(stderr, "Memory lost: %lu bytes in %d chunks\n", + (ulong) total, sf_malloc_count); + } + + pthread_mutex_destroy(&sf_mutex); + return; +} + +#endif diff --git a/mysys/typelib.c b/mysys/typelib.c index f724e5b27a2..3b4e1014f15 100644 --- a/mysys/typelib.c +++ b/mysys/typelib.c @@ -47,7 +47,10 @@ int find_type_or_exit(const char *x, TYPELIB *typelib, const char *option) { int res; if ((res= find_type_with_warning(x, typelib, option)) <= 0) + { + sf_leaking_memory= 1; /* no memory leak reports here */ exit(1); + } return res; } |