summaryrefslogtreecommitdiff
path: root/dbug
diff options
context:
space:
mode:
authorSergei Golubchik <sergii@pisem.net>2011-07-10 19:55:54 +0200
committerSergei Golubchik <sergii@pisem.net>2011-07-10 19:55:54 +0200
commit172f5e28ba9efceb3d3cee40c8373d2ee66f7c7a (patch)
tree2699ed6525a405595de40da2ec5e31793ee63f16 /dbug
parent02b8232629807ca3e37b99489f8191c549f7569a (diff)
downloadmariadb-git-172f5e28ba9efceb3d3cee40c8373d2ee66f7c7a.tar.gz
add safemalloc back
... but differently client/mysqltest.cc: my_safe_print_str() don't append \n anymore dbug/dbug.c: restore safemalloc as a part of dbug suite dbug/user.r: restore 'S' flag documentation include/my_dbug.h: restore safemalloc as a part of dbug suite include/my_sys.h: move valgrind defines to a dedicated header mysys/my_malloc.c: use new safemalloc mysys/stacktrace.c: don't append \n. let the calller do it, if needed sql/mysqld.cc: my_safe_print_str() don't append \n anymore
Diffstat (limited to 'dbug')
-rw-r--r--dbug/dbug.c385
-rw-r--r--dbug/user.r8
2 files changed, 368 insertions, 25 deletions
diff --git a/dbug/dbug.c b/dbug/dbug.c
index 3626afa59e6..36252bd707a 100644
--- a/dbug/dbug.c
+++ b/dbug/dbug.c
@@ -96,8 +96,8 @@
#include <process.h>
#endif
-#ifndef DBUG_OFF
-
+#include <my_valgrind.h> /* TRASH */
+#include <my_stacktrace.h> /* my_safe_print_str */
/*
* Manifest constants which may be "tuned" if desired.
@@ -126,6 +126,7 @@
#define TIMESTAMP_ON (1 << 9) /* timestamp every line of output */
#define FLUSH_ON_WRITE (1 << 10) /* Flush on every write */
#define OPEN_APPEND (1 << 11) /* Open for append */
+#define SANITY_CHECK_ON (1 << 12) /* Check memory on every DBUG_ENTER/RETURN */
#define TRACE_ON ((uint)1 << 31) /* Trace enabled. MUST be the highest bit!*/
#define TRACING (cs->stack->flags & TRACE_ON)
@@ -204,6 +205,8 @@ static BOOLEAN init_done= FALSE; /* Set to TRUE when initialization done */
static struct settings init_settings;
static const char *db_process= 0;/* Pointer to process name; argv[0] */
my_bool _dbug_on_= TRUE; /* FALSE if no debugging at all */
+static const char *unknown_func= "?func";
+static const char *unknown_file= "?file";
typedef struct _db_code_state_ {
const char *process; /* Pointer to process name; usually argv[0] */
@@ -284,6 +287,8 @@ static void DbugExit(const char *why);
static const char *DbugStrTok(const char *s);
static void DbugVfprintf(FILE *stream, const char* format, va_list args);
+static void DbugErr(CODE_STATE *, uint, const char* format, ...);
+
/*
* Miscellaneous printf format strings.
*/
@@ -306,6 +311,8 @@ static void DbugVfprintf(FILE *stream, const char* format, va_list args);
#define WRITABLE(pathname) (access(pathname, W_OK) == 0)
#endif
+static int sf_sanity();
+static void sf_terminate();
/*
** Macros to allow dbugging with threads
@@ -314,6 +321,9 @@ static void DbugVfprintf(FILE *stream, const char* format, va_list args);
#include <my_pthread.h>
static pthread_mutex_t THR_LOCK_dbug;
+/* this mutex protects all sf_* variables, and nothing else*/
+static pthread_mutex_t sf_mutex;
+
static CODE_STATE *code_state(void)
{
CODE_STATE *cs, **cs_ptr;
@@ -329,6 +339,7 @@ static CODE_STATE *code_state(void)
{
init_done=TRUE;
pthread_mutex_init(&THR_LOCK_dbug, NULL);
+ pthread_mutex_init(&sf_mutex, NULL);
bzero(&init_settings, sizeof(init_settings));
init_settings.out_file=stderr;
init_settings.flags=OPEN_APPEND;
@@ -341,8 +352,8 @@ static CODE_STATE *code_state(void)
cs=(CODE_STATE*) DbugMalloc(sizeof(*cs));
bzero((uchar*) cs,sizeof(*cs));
cs->process= db_process ? db_process : "dbug";
- cs->func="?func";
- cs->file="?file";
+ cs->func= unknown_func;
+ cs->file= unknown_file;
cs->stack=&init_settings;
*cs_ptr= cs;
}
@@ -625,6 +636,12 @@ int DbugParse(CODE_STATE *cs, const char *control)
else
stack->flags |= TIMESTAMP_ON;
break;
+ case 'S':
+ if (sign < 0)
+ stack->flags &= ~SANITY_CHECK_ON;
+ else
+ stack->flags |= SANITY_CHECK_ON;
+ break;
}
if (!*end)
break;
@@ -988,6 +1005,7 @@ int _db_explain_ (CODE_STATE *cs, char *buf, size_t len)
op_bool_to_buf('r', cs->stack->sub_level != 0);
op_intf_to_buf('t', cs->stack->maxdepth, MAXDEPTH, TRACING);
op_bool_to_buf('T', cs->stack->flags & TIMESTAMP_ON);
+ op_bool_to_buf('S', cs->stack->flags & SANITY_CHECK_ON);
*buf= '\0';
return 0;
@@ -1089,6 +1107,8 @@ void _db_enter_(const char *_func_, const char *_file_,
if (!TRACING) break;
/* fall through */
case DO_TRACE:
+ if ((cs->stack->flags & SANITY_CHECK_ON) && sf_sanity())
+ cs->stack->flags &= ~SANITY_CHECK_ON;
if (TRACING)
{
if (!cs->locked)
@@ -1144,6 +1164,8 @@ void _db_return_(uint _line_, struct _db_stack_frame_ *_stack_frame_)
if (DoTrace(cs) & DO_TRACE)
{
+ if ((cs->stack->flags & SANITY_CHECK_ON) && sf_sanity())
+ cs->stack->flags &= ~SANITY_CHECK_ON;
if (TRACING)
{
if (!cs->locked)
@@ -1592,19 +1614,18 @@ void _db_end_()
called after dbug was initialized
*/
_dbug_on_= 1;
- get_code_state_or_return;
+ cs= code_state();
- while ((discard= cs->stack))
- {
- if (discard == &init_settings)
- break;
- cs->stack= discard->next;
- FreeState(cs, discard, 1);
- }
+ if (cs)
+ while ((discard= cs->stack))
+ {
+ if (discard == &init_settings)
+ break;
+ cs->stack= discard->next;
+ FreeState(cs, discard, 1);
+ }
tmp= init_settings;
- /* Use mutex lock to make it less likely anyone access out_file */
- pthread_mutex_lock(&THR_LOCK_dbug);
init_settings.flags= OPEN_APPEND;
init_settings.out_file= stderr;
init_settings.maxdepth= 0;
@@ -1613,8 +1634,8 @@ void _db_end_()
init_settings.functions= 0;
init_settings.keywords= 0;
init_settings.processes= 0;
- pthread_mutex_unlock(&THR_LOCK_dbug);
FreeState(cs, &tmp, 0);
+ sf_terminate();
}
@@ -1904,7 +1925,7 @@ static void DBUGOpenFile(CODE_STATE *cs,
static void DBUGCloseFile(CODE_STATE *cs, FILE *fp)
{
- if (fp && fp != stderr && fp != stdout && fclose(fp) == EOF)
+ if (cs && fp && fp != stderr && fp != stdout && fclose(fp) == EOF)
{
if (!cs->locked)
pthread_mutex_lock(&THR_LOCK_dbug);
@@ -2140,19 +2161,333 @@ const char* _db_get_func_(void)
return cs->func;
}
+/*
+ prints the error message, followed by a stack trace
+ of the specified depth
+*/
+static void DbugErr(CODE_STATE *cs, uint depth, const char* format, ...)
+{
+ va_list args;
+ va_start(args,format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+
+ if (cs || ((cs= code_state())))
+ {
+ uint i= depth;
+ struct _db_stack_frame_ *frame= cs->framep;
+ while (i-- && frame)
+ {
+ fprintf(stderr, ", at %s", frame->func);
+ frame= frame->prev;
+ }
+ }
-#else
+ fprintf(stderr, "\n");
+}
+
+/********************************************************************
+ memory debugger
+ based on safemalloc, memory sub-system, written by Bjorn Benson
+********************************************************************/
+
+#ifndef SF_REMEMBER_FRAMES
+#define SF_REMEMBER_FRAMES 16
+#endif
/*
- * Dummy function, workaround for MySQL bug#14420 related
- * build failure on a platform where linking with an empty
- * archive fails.
+ 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 */
+ const char *frame[SF_REMEMBER_FRAMES]; /* call stack */
+ uint32 marker; /* Underrun marker value */
+};
+
+/*
+ DBUG_MALLOC/DBUG_REALLOC/DBUG_FREE can be called even
+ before dbug is initialized. We cannot properly take into account
+ these calls, but we can at least wrap allocated memory
+ in st_irem's and check for overrun/underruns.
+ These special irem's - that are not linked into a global list -
+ are distinguished by a special value in the 'next' pointer.
+*/
+#define NOT_LINKED ((struct st_irem *)1)
+
+size_t sf_malloc_mem_limit= (intptr)~0ULL;
+static size_t sf_malloc_cur_memory= 0L; /* Current memory usage */
+static size_t sf_malloc_max_memory= 0L; /* Maximum memory usage */
+
+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);
+
+/*
+ * FUNCTION
+ *
+ * _db_malloc_ allocates memory
+ *
+ * SYNOPSIS
*
- * This block can be removed as soon as a fix for bug#14420
- * is implemented.
+ * void *_db_malloc_(size_t size)
+ * size_t size; Bytes to allocate
*/
-int i_am_a_dummy_function() {
- return 0;
+
+void *_db_malloc_(size_t size)
+{
+ CODE_STATE *cs= code_state();
+ struct st_irem *irem;
+ uchar *data;
+ struct _db_stack_frame_ *frame;
+ int i= 0;
+
+ if (size + sf_malloc_cur_memory > sf_malloc_mem_limit)
+ irem= 0;
+ else
+ irem= (struct st_irem *) malloc (sizeof(struct st_irem) + size + 4);
+
+ if (!irem)
+ return 0;
+
+ 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;
+
+ if (cs && cs->framep)
+ {
+ for (frame= cs->framep;
+ i < SF_REMEMBER_FRAMES && frame->func != unknown_func;
+ i++, frame= frame->prev)
+ irem->frame[i]= frame->func;
+ }
+
+ if (i < SF_REMEMBER_FRAMES)
+ irem->frame[i]= unknown_func;
+ if (i==0)
+ irem->frame[0]= (char*)1;
+
+ if (init_done)
+ {
+ 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++;
+ sf_malloc_cur_memory+= size;
+ set_if_bigger(sf_malloc_max_memory, sf_malloc_cur_memory);
+ set_if_smaller(sf_min_adress, (void*)data);
+ set_if_bigger(sf_max_adress, (void*)data);
+ pthread_mutex_unlock(&sf_mutex);
+ }
+ else
+ {
+ set_if_bigger(sf_malloc_max_memory, sf_malloc_cur_memory);
+ set_if_smaller(sf_min_adress, (void*)data);
+ set_if_bigger(sf_max_adress, (void*)data);
+ irem->next= NOT_LINKED;
+ }
+
+ TRASH_ALLOC(data, size);
+ return data;
}
-#endif
+void *_db_realloc_(void *ptr, size_t size)
+{
+ char *data;
+
+ if (!ptr)
+ return _db_malloc_(size);
+
+ if (bad_ptr("Reallocating", ptr))
+ return 0;
+
+ if ((data= _db_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 _db_free_(void *ptr)
+{
+ if (!ptr || bad_ptr("Freeing", ptr))
+ return;
+
+ free_memory(ptr);
+ return;
+}
+
+static void free_memory(void *ptr)
+{
+ struct st_irem *irem= (struct st_irem *)ptr - 1;
+
+ if (irem->next != NOT_LINKED)
+ {
+ 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_cur_memory-= irem->datasize;
+ 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;
+}
+
+#define SF_ADD_NL 1
+#define SF_USE_SAFE_PRINT 2
+static void print_allocated_at(struct st_irem *irem, int flags)
+{
+ int i;
+ const char *allocated= flags & SF_ADD_NL ? "Allocated" : ", allocated";
+
+ for (i=0;
+ i < SF_REMEMBER_FRAMES && irem->frame[i] != unknown_func;
+ i++)
+ {
+ fprintf(stderr, "%s at ", i ? "," : allocated);
+ if (flags & SF_USE_SAFE_PRINT)
+ my_safe_print_str(irem->frame[i], 80);
+ else
+ fputs(irem->frame[i], stderr);
+ }
+ if (i && (flags & SF_ADD_NL))
+ fprintf(stderr, "\n");
+}
+
+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))
+ {
+ DbugErr(0, SF_REMEMBER_FRAMES, "Error: %s wrong aligned pointer", where);
+ return 1;
+ }
+ if (ptr < sf_min_adress || ptr > sf_max_adress)
+ {
+ DbugErr(0, SF_REMEMBER_FRAMES, "Error: %s pointer out of range", where);
+ return 1;
+ }
+ if (irem->marker != MAGICSTART)
+ {
+ DbugErr(0, SF_REMEMBER_FRAMES,
+ "Error: %s unallocated data or underrun buffer", where);
+ /*
+ we cannot use print_allocated_at here:
+ if the memory was not allocated, there's nothing to print,
+ if it was allocated and underrun, call stack may be corrupted
+ */
+ return 1;
+ }
+
+ magicend= (uchar*)ptr + irem->datasize;
+ if (magicend[0] != MAGICEND0 ||
+ magicend[1] != MAGICEND1 ||
+ magicend[2] != MAGICEND2 ||
+ magicend[3] != MAGICEND3)
+ {
+ DbugErr(0, SF_REMEMBER_FRAMES, "Error: %s overrun buffer", where);
+ print_allocated_at(irem, SF_ADD_NL);
+ 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)
+ {
+ DbugErr(0, SF_REMEMBER_FRAMES, "Error: Safemalloc link list destroyed");
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * FUNCTION
+ *
+ * sf_terminate Report on all the memory pieces that have not been free'd
+ *
+ * SYNOPSIS
+ *
+ * void sf_terminate()
+ */
+
+static void sf_terminate()
+{
+ struct st_irem *irem;
+
+ sf_sanity();
+
+ /* Report on all the memory that was allocated but not free'd */
+ if ((irem= sf_malloc_root))
+ {
+ while (irem)
+ {
+ fprintf(stderr, "Warning: %6lu bytes at %p are not freed", (ulong) irem->datasize, irem + 1);
+ print_allocated_at(irem, SF_USE_SAFE_PRINT);
+ fprintf(stderr, "\n");
+ irem= irem->next;
+ }
+ fprintf(stderr, "Memory lost: %lu bytes in %d chunks\n",
+ (ulong) sf_malloc_cur_memory, sf_malloc_count);
+ }
+
+ return;
+}
diff --git a/dbug/user.r b/dbug/user.r
index e4e27ee8274..8d8a9ce6910 100644
--- a/dbug/user.r
+++ b/dbug/user.r
@@ -988,6 +988,14 @@ Most useful with
.B DBUG_PUSH
macros used to temporarily alter the
debugger state.
+.LI S
+When compiled with
+.I safemalloc
+this flag invokes "sanity" memory checks (for overwrites/underwrites)
+on each
+.B DBUG_ENTER
+and
+.B DBUG_RETURN.
.LI t[,N]
Enable function control flow tracing.
The maximum nesting depth is specified by N, and defaults to