/* stacktrace.c Author: Pekka Riikonen Copyright (C) 2002 Pekka Riikonen The contents of this file are subject to one of the Licenses specified in the COPYING file; You may not use this file except in compliance with the License. The software distributed under the License is distributed on an "AS IS" basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the COPYING file for more information. */ #include "silcincludes.h" #ifdef SILC_STACKTRACE static void *st_blocks = NULL; static unsigned long st_blocks_count = 0; static bool dump = FALSE; static bool malloc_check = 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; } if (!malloc_check) { /* Linux libc malloc check */ setenv("MALLOC_CHECK_", "2", 1); /* NetBSD malloc check */ setenv("MALLOC_OPTIONS", "AJ", 1); malloc_check = 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 */