/* stacktrace.c Author: Pekka Riikonen Copyright (C) 2002 Pekka Riikonen 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. */ #include "silcincludes.h" #ifdef SILC_STACKTRACE static void *st_blocks = NULL; static unsigned long st_blocks_count = 0; static int dump = FALSE; #define SILC_ST_DEPTH 10 /* Memory block with stack trace */ typedef struct SilcStBlockStruct { unsigned int dumpped : 1; /* Block is dumpped */ unsigned int depth : 8; /* Depth of stack trace */ unsigned int line : 23; /* Allocation line in program */ void *stack[SILC_ST_DEPTH]; /* Stack trace */ const char *file; /* Allocation file in program */ unsigned long size; /* Allocated memory size */ struct SilcStBlockStruct *next; struct SilcStBlockStruct *prev; } *SilcStBlock; /* Get current frame pointer */ #define SILC_ST_GET_FP(ret_fp) \ do { \ register void *cfp; \ asm volatile ("movl %%ebp, %0" : "=r" (cfp)); \ (ret_fp) = cfp; \ } while(0); #define SILC_ST_GET_SIZE(size) ((size + sizeof(struct SilcStBlockStruct))) #define SILC_ST_GET_STACK(p) ((SilcStBlock)(((unsigned char *)p) - \ sizeof(struct SilcStBlockStruct))) #define SILC_ST_GET_PTR(p) (((unsigned char *)p) + \ sizeof(struct SilcStBlockStruct)) void silc_st_stacktrace(SilcStBlock stack) { void *fp; if (!dump) { atexit(silc_st_dump); dump = TRUE; } /* Save the stack */ SILC_ST_GET_FP(fp); for (stack->depth = 0; fp; stack->depth++) { if (stack->depth == SILC_ST_DEPTH) break; /* Get program pointer and frame pointer from this frame */ stack->stack[stack->depth] = *((void **)(((unsigned char *)fp) + 4)); fp = *((void **)fp); } } void *silc_st_malloc(size_t size, const char *file, int line) { SilcStBlock stack = (SilcStBlock)malloc(SILC_ST_GET_SIZE(size)); assert(stack != NULL); stack->dumpped = 0; stack->file = file; stack->line = line; stack->size = size; silc_st_stacktrace(stack); stack->next = st_blocks; stack->prev = NULL; if (st_blocks) ((SilcStBlock)st_blocks)->prev = stack; st_blocks = stack; st_blocks_count++; return SILC_ST_GET_PTR(stack); } void *silc_st_calloc(size_t items, size_t size, const char *file, int line) { void *addr = (void *)silc_st_malloc(items * size, file, line); memset(addr, 0, items * size); return addr; } void *silc_st_realloc(void *ptr, size_t size, const char *file, int line) { SilcStBlock stack; if (!ptr) return silc_st_malloc(size, file, line); stack = SILC_ST_GET_STACK(ptr); if (stack->size >= size) { stack->size = size; return ptr; } else { void *addr = (void *)silc_st_malloc(size, file, line); memcpy(addr, ptr, stack->size); silc_st_free(ptr, file, line); return addr; } } void silc_st_free(void *ptr, const char *file, int line) { SilcStBlock stack; if (!ptr) return; stack = SILC_ST_GET_STACK(ptr); if (stack->next) stack->next->prev = stack->prev; if (stack->prev) stack->prev->next = stack->next; else st_blocks = stack->next; st_blocks_count--; free(stack); } void *silc_st_memdup(const void *ptr, size_t size, const char *file, int line) { unsigned char *addr = (unsigned char *)silc_st_malloc(size + 1, file, line); memcpy((void *)addr, ptr, size); addr[size] = '\0'; return (void *)addr; } void *silc_st_strdup(const char *string, const char *file, int line) { return silc_st_memdup(string, strlen(string), file, line); } /* Dumps the stack into file if there are leaks. The file can be read with a special stacktrace tool. */ void silc_st_dump(void) { SilcStBlock stack, s; unsigned long leaks = 0, blocks, bytes; FILE *fp = NULL; int i; for (stack = st_blocks; stack; stack = stack->next) { bytes = blocks = 0; if (stack->dumpped) continue; leaks++; if (!fp) { fp = fopen("stacktrace.log", "wb"); if (!fp) fp = stderr; } for (s = stack; s; s = s->next) { if (s->file == stack->file && s->line == stack->line && s->depth == stack->depth && !memcmp(s->stack, stack->stack, (s->depth * sizeof(stack->stack[0])))) { blocks++; bytes += s->size; s->dumpped = 1; } } if (blocks) { fprintf(fp, "%s:%d: #blocks=%lu, bytes=%lu\n", stack->file, stack->line, blocks, bytes); for (i = 0; i < stack->depth; i++) fprintf(fp, "%p\n", stack->stack[i]); } } if (!leaks) { fprintf(stderr, "\nNo memory leaks\n"); } else { fprintf(stderr, "-----------------------------------------\n" "-----------------------------------------\n" " Memory leaks dumped to 'stacktrace.log'\n" " Leaks: %lu leaks, %lu blocks\n" "-----------------------------------------\n" "-----------------------------------------\n", leaks, st_blocks_count); } if (fp && fp != stderr) fclose(fp); } #endif /* SILC_STACKTRACE */