Sun Mar 11 15:22:42 CET 2007 Jochen Eisinger <coffee@silcnet.org>
[silc.git] / lib / silcutil / stacktrace.c
1 /*
2
3   stacktrace.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002, 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silc.h"
21
22 #ifdef SILC_STACKTRACE
23
24 static void *st_blocks = NULL;
25 static unsigned long st_blocks_count = 0;
26 static SilcBool dump = FALSE;
27 static SilcBool malloc_check = FALSE;
28
29 #ifdef SILC_DEBUG
30 #define SILC_ST_DEPTH 15
31 #else
32 #define SILC_ST_DEPTH 8
33 #endif /* SILC_DEBUG */
34
35 /* Memory block with stack trace */
36 typedef struct SilcStBlockStruct {
37   unsigned int dumpped  : 1;    /* Block is dumpped */
38   unsigned int depth    : 8;    /* Depth of stack trace */
39   unsigned int line     : 23;   /* Allocation line in program */
40   void *stack[SILC_ST_DEPTH];   /* Stack trace */
41   const char *file;             /* Allocation file in program */
42   unsigned long size;           /* Allocated memory size */
43   struct SilcStBlockStruct *next;
44   struct SilcStBlockStruct *prev;
45 } *SilcStBlock;
46
47 /* Get current frame pointer */
48 #define SILC_ST_GET_FP(ret_fp)                  \
49 do {                                            \
50   register void *cfp;                           \
51   asm volatile ("movl %%ebp, %0" : "=r" (cfp)); \
52   (ret_fp) = cfp;                               \
53 } while(0);
54
55 #define SILC_ST_GET_SIZE(size) ((size + sizeof(struct SilcStBlockStruct)))
56 #define SILC_ST_GET_STACK(p) ((SilcStBlock)(((unsigned char *)p) -      \
57                             sizeof(struct SilcStBlockStruct)))
58 #define SILC_ST_GET_PTR(p) (((unsigned char *)p) +              \
59                             sizeof(struct SilcStBlockStruct))
60
61 void silc_st_stacktrace(SilcStBlock stack)
62 {
63   void *fp;
64
65   if (!dump) {
66     atexit(silc_st_dump);
67     dump = TRUE;
68   }
69
70   if (!malloc_check) {
71     /* Linux libc malloc check */
72     setenv("MALLOC_CHECK_", "2", 1);
73
74     /* NetBSD malloc check */
75     setenv("MALLOC_OPTIONS", "AJ", 1);
76
77     malloc_check = TRUE;
78   }
79
80   /* Save the stack */
81   SILC_ST_GET_FP(fp);
82   for (stack->depth = 0; fp; stack->depth++) {
83     if (stack->depth == SILC_ST_DEPTH)
84       break;
85
86     /* Get program pointer and frame pointer from this frame */
87     stack->stack[stack->depth] = *((void **)(((unsigned char *)fp) + 4));
88     fp = *((void **)fp);
89   }
90 }
91
92 void *silc_st_malloc(size_t size, const char *file, int line)
93 {
94   SilcStBlock stack = (SilcStBlock)malloc(SILC_ST_GET_SIZE(size));
95   assert(stack != NULL);
96
97   stack->dumpped = 0;
98   stack->file = file;
99   stack->line = line;
100   stack->size = size;
101   silc_st_stacktrace(stack);
102
103   stack->next = st_blocks;
104   stack->prev = NULL;
105   if (st_blocks)
106     ((SilcStBlock)st_blocks)->prev = stack;
107   st_blocks = stack;
108   st_blocks_count++;
109
110   return SILC_ST_GET_PTR(stack);
111 }
112
113 void *silc_st_calloc(size_t items, size_t size, const char *file, int line)
114 {
115   void *addr = (void *)silc_st_malloc(items * size, file, line);
116   memset(addr, 0, items * size);
117   return addr;
118 }
119
120 void *silc_st_realloc(void *ptr, size_t size, const char *file, int line)
121 {
122   SilcStBlock stack;
123
124   if (!ptr)
125     return silc_st_malloc(size, file, line);
126
127   stack = SILC_ST_GET_STACK(ptr);
128   if (stack->size >= size) {
129     stack->size = size;
130     return ptr;
131   } else {
132     void *addr = (void *)silc_st_malloc(size, file, line);
133     memcpy(addr, ptr, stack->size);
134     silc_st_free(ptr, file, line);
135     return addr;
136   }
137 }
138
139 void silc_st_free(void *ptr, const char *file, int line)
140 {
141   SilcStBlock stack;
142
143   if (!ptr)
144     return;
145
146   stack = SILC_ST_GET_STACK(ptr);
147   if (stack->next)
148     stack->next->prev = stack->prev;
149   if (stack->prev)
150     stack->prev->next = stack->next;
151   else
152     st_blocks = stack->next;
153
154   st_blocks_count--;
155   free(stack);
156 }
157
158 void *silc_st_memdup(const void *ptr, size_t size, const char *file, int line)
159 {
160   unsigned char *addr = (unsigned char *)silc_st_malloc(size + 1, file, line);
161   memcpy((void *)addr, ptr, size);
162   addr[size] = '\0';
163   return (void *)addr;
164 }
165
166 void *silc_st_strdup(const char *string, const char *file, int line)
167 {
168   return silc_st_memdup(string, strlen(string), file, line);
169 }
170
171 /* Dumps the stack into file if there are leaks.  The file can be read
172    with a special stacktrace tool. */
173
174 void silc_st_dump(void)
175 {
176   SilcStBlock stack, s;
177   unsigned long leaks = 0, blocks, bytes;
178   FILE *fp = NULL;
179   int i;
180
181   for (stack = st_blocks; stack; stack = stack->next) {
182     bytes = blocks = 0;
183
184     if (stack->dumpped)
185       continue;
186
187     leaks++;
188
189     if (!fp) {
190       fp = fopen("stacktrace.log", "wb");
191       if (!fp)
192         fp = stderr;
193     }
194
195     for (s = stack; s; s = s->next) {
196       if (s->file == stack->file && s->line == stack->line &&
197           s->depth == stack->depth &&
198           !memcmp(s->stack, stack->stack,
199                   (s->depth * sizeof(stack->stack[0])))) {
200         blocks++;
201         bytes += s->size;
202         s->dumpped = 1;
203       }
204     }
205
206     if (blocks) {
207       fprintf(fp, "<stacktrace>%s:%d: #blocks=%lu, bytes=%lu\n",
208               stack->file, stack->line, blocks, bytes);
209       for (i = 0; i < stack->depth; i++)
210         fprintf(fp, "\tpc=%p\n", stack->stack[i]);
211     }
212   }
213
214   if (!leaks) {
215     fprintf(stderr, "\nNo memory leaks\n");
216   } else {
217     fprintf(stderr,
218             "-----------------------------------------\n"
219             "-----------------------------------------\n"
220             " Memory leaks dumped to 'stacktrace.log'\n"
221             " Leaks: %lu leaks, %lu blocks\n"
222             "-----------------------------------------\n"
223             "-----------------------------------------\n",
224             leaks, st_blocks_count);
225   }
226
227   if (fp && fp != stderr)
228     fclose(fp);
229 }
230
231 #endif /* SILC_STACKTRACE */