/*
- stacktrace.c
+ memtrace.c
Author: Pekka Riikonen <priikone@silcnet.org>
setting this variable the program will show where the memory was
originally allocated and freed.
+ SILC_MALLOC_NO_ALLOCATED
+
+ When set to value 1, the program will not check if given memory was
+ allocated. This will speed up debugging but will not detect if freeing
+ memory that was never allocated.
+
SILC_MALLOC_DUMP
When set to value 1, in case of fatal error, dumps the memory location,
#include "silcruntime.h"
-#ifdef SILC_STACKTRACE
+#ifdef SILC_MEMTRACE
#include <execinfo.h>
#include <signal.h>
#include <malloc.h>
#include <sys/mman.h>
-static void *st_blocks = NULL;
-static unsigned long st_blocks_count = 0;
+static SilcTree st_blocks;
static unsigned long st_num_malloc = 0;
static SilcBool dump = FALSE;
static SilcBool no_free = FALSE;
static SilcBool dump_mem = FALSE;
+static SilcBool no_allocated = FALSE;
static SilcUInt32 pg = 0;
static SilcMutex lock = NULL;
/* Memory block with stack trace */
typedef struct SilcStBlockStruct {
- struct SilcStBlockStruct *next;
- struct SilcStBlockStruct *prev;
+ SilcTreeHeader h;
void *stack[SILC_ST_DEPTH]; /* Stack trace */
const char *file; /* Allocation file in program */
const char *free_file; /* Free file in program */
((SilcStBlock)(((unsigned char *)p) + (SILC_ST_GET_SIZE_ALIGN(size, pg) - \
SILC_ST_GET_SIZE(size)) + 4))
+SilcCompareValue silc_st_compare(void *val1, void *val2, void *context)
+{
+ if (SILC_PTR_TO_32(val1) > SILC_PTR_TO_32(val2))
+ return SILC_COMPARE_GREATER_THAN;
+ if (SILC_PTR_TO_32(val1) < SILC_PTR_TO_32(val2))
+ return SILC_COMPARE_LESS_THAN;
+ return SILC_COMPARE_EQUAL_TO;
+}
+
void silc_st_abort(SilcStBlock stack, const char *file, int line,
char *fmt, ...)
{
"SILC_MALLOC: access violation (overflow)\n");
}
-void silc_st_stacktrace_init(void)
+void silc_st_memtrace_init(void)
{
const char *var;
+ silc_tree_init(st_blocks, SILC_TREE_AVL, silc_st_compare, NULL,
+ silc_offsetof(struct SilcStBlockStruct, h), FALSE);
+
atexit(silc_st_dump);
dump = TRUE;
if (var && *var == '1')
no_free = TRUE;
+ var = silc_getenv("SILC_MALLOC_NO_ALLOCATED");
+ if (var && *var == '1')
+ no_allocated = TRUE;
+
var = silc_getenv("SILC_MALLOC_DUMP");
if (var && *var == '1')
dump_mem = TRUE;
SilcStBlock stack;
if (silc_unlikely(!dump))
- silc_st_stacktrace_init();
+ silc_st_memtrace_init();
if (pg) {
unsigned char *ptr;
/* Protect the page */
if (mprotect(stack, pg, PROT_NONE))
silc_st_abort(NULL, file, line, "SILC_MALLOC: mprotect() error: %s\n",
- errno == ENOMEM ? "Cannot allocate memory. \nYour program "
- "leaks memory, allocates too much or system \n"
- "is out of memory. The SILC_MALLOC_PROTECT cannot "
- "be used." : strerror(errno));
+ errno == ENOMEM ? "Cannot allocate memory. \nOn Linux, try"
+ " giving 'echo 1000000 >/proc/sys/vm/max_map_count',\n"
+ "to get rid of the problem" : strerror(errno));
/* Get the accessible page */
stack = SILC_ST_GET_STACK_ALIGN(ptr, size, pg);
return NULL;
}
+ memset(&stack->h, 0, sizeof(stack->h));
stack->dumpped = 0;
stack->file = file;
stack->free_file = NULL;
stack->depth = backtrace(stack->stack, SILC_ST_DEPTH);
silc_mutex_lock(lock);
-
- stack->next = st_blocks;
- stack->prev = NULL;
- if (st_blocks)
- ((SilcStBlock)st_blocks)->prev = stack;
- st_blocks = stack;
- st_blocks_count++;
+ silc_tree_add(st_blocks, stack);
st_num_malloc++;
-
silc_mutex_unlock(lock);
if (!pg)
void silc_st_free(void *ptr, const char *file, int line)
{
- SilcStBlock stack, s;
+ SilcStBlock stack;
if (!ptr)
return;
stack = SILC_ST_GET_STACK(ptr);
- silc_mutex_lock(lock);
-
- /* Check if we have ever made this allocation */
- for (s = st_blocks; s; s = s->next)
- if (s == stack)
- break;
- if (s == NULL)
- silc_st_abort(NULL, file, line,
- "SILC_MALLOC: %p was never allocated\n", stack);
+ /* Check for underflow */
+ if (stack->bound != SILC_ST_TOP_BOUND)
+ silc_st_abort(stack, file, line,
+ "SILC_MALLOC: %p was written out of bounds (underflow)\n",
+ stack);
if (!pg) {
- /* Check for underflow */
- if (stack->bound != SILC_ST_TOP_BOUND)
- silc_st_abort(stack, file, line,
- "SILC_MALLOC: %p was written out of bounds (underflow)\n",
- stack);
-
/* Check for overflow */
if (*SILC_ST_GET_BOUND(stack, stack->size) != SILC_ST_BOTTOM_BOUND)
silc_st_abort(stack, file, line,
stack);
}
- if (stack->next)
- stack->next->prev = stack->prev;
- if (stack->prev)
- stack->prev->next = stack->next;
- else
- st_blocks = stack->next;
+ silc_mutex_lock(lock);
- st_blocks_count--;
+ /* Check if we have ever made this allocation */
+ if (!no_allocated && !silc_tree_find(st_blocks, stack))
+ silc_st_abort(NULL, file, line,
+ "SILC_MALLOC: %p was never allocated\n", stack);
+
+ if (!silc_tree_del(st_blocks, stack))
+ silc_st_abort(NULL, file, line,
+ "SILC_MALLOC: %p was never allocated\n", stack);
silc_mutex_unlock(lock);
lock = NULL;
silc_mutex_free(l);
- for (stack = st_blocks; stack; stack = stack->next) {
+ for (stack = silc_tree_enumerate(st_blocks, NULL); stack != NULL;
+ stack = silc_tree_enumerate(st_blocks, stack)) {
bytes = blocks = 0;
if (stack->dumpped)
leaks++;
if (!fp) {
- fp = fopen("stacktrace.log", "wb");
+ fp = fopen("memtrace.log", "wb");
if (!fp)
fp = stderr;
}
syms = backtrace_symbols(stack->stack, stack->depth);
/* Find number of leaks and bytes leaked for this leak */
- for (s = stack; s; s = s->next) {
+ for (s = silc_tree_enumerate(st_blocks, NULL); s != NULL;
+ s = silc_tree_enumerate(st_blocks, s)) {
if (s->file == stack->file && s->line == stack->line &&
s->depth == stack->depth &&
!memcmp(s->stack, stack->stack,
fprintf(stderr, "\nNo memory leaks\n");
} else {
fprintf(stderr,
- "-----------------------------------------\n"
- "-----------------------------------------\n"
- " Memory leaks dumped to 'stacktrace.log'\n"
+ "---------------------------------------\n"
+ "---------------------------------------\n"
+ " Memory leaks dumped to 'memtrace.log'\n"
" Leaks: %lu leaks, %lu blocks\n"
- "-----------------------------------------\n"
- "-----------------------------------------\n",
- leaks, st_blocks_count);
+ "---------------------------------------\n"
+ "---------------------------------------\n",
+ leaks, (unsigned long)silc_tree_count(st_blocks));
fprintf(stderr, "Number of allocations: %lu\n", st_num_malloc);
}
fclose(fp);
}
-#endif /* SILC_STACKTRACE */
+#endif /* SILC_MEMTRACE */
#include <silcruntime.h>
+/* Define to 1 if you want hash table debug enabled */
+#ifndef SILC_AVL_TREE_DEBUG
+#define SILC_AVL_TREE_DEBUG 0
+#endif /* !SILC_AVL_TREE_DEBUG */
+
+#if SILC_ALV_TREE_DEBUG == 1
+#define SILC_TREE_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
+#else
+#define SILC_TREE_DEBUG(fmt)
+#endif
+
/************************ Static utility functions **************************/
/* Rotate left
void *cmp_context = compare ? context : tree->context;
int ret;
- SILC_LOG_DEBUG(("AVL tree %p, find %p", tree, entry));
+ SILC_TREE_DEBUG(("AVL tree %p, find %p", tree, entry));
h = tree->root;
while (h) {
ret = cmp(entry, SILC_TREE_GET_ENTRY(tree, h), cmp_context);
if (ret == SILC_COMPARE_EQUAL_TO) {
- SILC_LOG_DEBUG(("Found %p", SILC_TREE_GET_ENTRY(tree, h)));
+ SILC_TREE_DEBUG(("Found %p", SILC_TREE_GET_ENTRY(tree, h)));
return SILC_TREE_GET_ENTRY(tree, h);
}
if (ret == SILC_COMPARE_STOP)
h = ret > 0 ? h->right : h->left;
}
- SILC_LOG_DEBUG(("Not found"));
- silc_set_errno(SILC_ERR_NOT_FOUND);
+ SILC_TREE_DEBUG(("Not found"));
+ silc_set_errno_nofail(SILC_ERR_NOT_FOUND);
return NULL;
}
SilcTreeHeader *h, *parent = NULL, *q = NULL;
int ret = 0;
- SILC_LOG_DEBUG(("AVL tree %p, adding %p", tree, entry));
+ SILC_TREE_DEBUG(("AVL tree %p, adding %p", tree, entry));
/* If tree is empty, add to root */
if (!tree->root) {
h->t = 0;
tree->root = h;
- SILC_LOG_DEBUG(("Entry %p added as root", entry));
+ SILC_TREE_DEBUG(("Entry %p added as root", entry));
SILC_ASSERT(!tree->count);
tree->count = 1;
while (h) {
/* Same entry context must not be in tree */
if (entry == SILC_TREE_GET_ENTRY(tree, h)) {
- silc_set_errno(SILC_ERR_ALREADY_EXISTS);
+ silc_set_errno_nofail(SILC_ERR_ALREADY_EXISTS);
return FALSE;
}
ret = tree->compare(entry, SILC_TREE_GET_ENTRY(tree, h), tree->context);
if (!tree->duplicates && ret == SILC_COMPARE_EQUAL_TO) {
- silc_set_errno(SILC_ERR_ALREADY_EXISTS);
+ silc_set_errno_nofail(SILC_ERR_ALREADY_EXISTS);
return FALSE;
}
h->dup->parent = q;
q->dup = h->dup;
h->dup = q;
- SILC_LOG_DEBUG(("Entry %p is duplicate to %p", entry,
+ SILC_TREE_DEBUG(("Entry %p is duplicate to %p", entry,
SILC_TREE_GET_ENTRY(tree, h)));
tree->count++;
return TRUE;
else
parent->right = h;
- SILC_LOG_DEBUG(("Entry %p added, parent %p", entry,
- SILC_TREE_GET_ENTRY(tree, parent)));
+ SILC_TREE_DEBUG(("Entry %p added, parent %p", entry,
+ SILC_TREE_GET_ENTRY(tree, parent)));
/* Update the new entry */
h->parent = parent;
SilcTreeHeader *h, *q, *out, *parent = NULL, tmp;
int left;
- SILC_LOG_DEBUG(("AVL tree %p, delete %p", tree, entry));
+ SILC_TREE_DEBUG(("AVL tree %p, delete %p", tree, entry));
if (!tree->root || !entry) {
- silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ silc_set_errno_nofail(SILC_ERR_INVALID_ARGUMENT);
return FALSE;
}
h->right->parent = h->dup;
/* Cleanup the deleted entry */
- SILC_LOG_DEBUG(("Deleted %p", SILC_TREE_GET_ENTRY(tree, h)));
+ SILC_TREE_DEBUG(("Deleted %p", SILC_TREE_GET_ENTRY(tree, h)));
h->parent = h->left = h->right = h->dup = NULL;
h->t = 0;
out->parent = parent;
/* Cleanup the deleted entry */
- SILC_LOG_DEBUG(("Deleted %p", SILC_TREE_GET_ENTRY(tree, h)));
+ SILC_TREE_DEBUG(("Deleted %p", SILC_TREE_GET_ENTRY(tree, h)));
h->parent = h->left = h->right = h->dup = NULL;
h->t = 0;