Added SILC Thread Queue API
[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
156   memset(stack, 'F', SILC_ST_GET_SIZE(stack->size));
157   free(stack);
158 }
159
160 void *silc_st_memdup(const void *ptr, size_t size, const char *file, int line)
161 {
162   unsigned char *addr = (unsigned char *)silc_st_malloc(size + 1, file, line);
163   memcpy((void *)addr, ptr, size);
164   addr[size] = '\0';
165   return (void *)addr;
166 }
167
168 void *silc_st_strdup(const char *string, const char *file, int line)
169 {
170   return silc_st_memdup(string, strlen(string), file, line);
171 }
172
173 /* Dumps the stack into file if there are leaks.  The file can be read
174    with a special stacktrace tool. */
175
176 void silc_st_dump(void)
177 {
178   SilcStBlock stack, s;
179   unsigned long leaks = 0, blocks, bytes;
180   FILE *fp = NULL;
181   int i;
182
183   for (stack = st_blocks; stack; stack = stack->next) {
184     bytes = blocks = 0;
185
186     if (stack->dumpped)
187       continue;
188
189     leaks++;
190
191     if (!fp) {
192       fp = fopen("stacktrace.log", "wb");
193       if (!fp)
194         fp = stderr;
195     }
196
197     for (s = stack; s; s = s->next) {
198       if (s->file == stack->file && s->line == stack->line &&
199           s->depth == stack->depth &&
200           !memcmp(s->stack, stack->stack,
201                   (s->depth * sizeof(stack->stack[0])))) {
202         blocks++;
203         bytes += s->size;
204         s->dumpped = 1;
205       }
206     }
207
208     if (blocks) {
209       fprintf(fp, "<stacktrace>%s:%d: #blocks=%lu, bytes=%lu\n",
210               stack->file, stack->line, blocks, bytes);
211       for (i = 0; i < stack->depth; i++)
212         fprintf(fp, "\tpc=%p\n", stack->stack[i]);
213     }
214   }
215
216   if (!leaks) {
217     fprintf(stderr, "\nNo memory leaks\n");
218   } else {
219     fprintf(stderr,
220             "-----------------------------------------\n"
221             "-----------------------------------------\n"
222             " Memory leaks dumped to 'stacktrace.log'\n"
223             " Leaks: %lu leaks, %lu blocks\n"
224             "-----------------------------------------\n"
225             "-----------------------------------------\n",
226             leaks, st_blocks_count);
227   }
228
229   if (fp && fp != stderr)
230     fclose(fp);
231 }
232
233 #endif /* SILC_STACKTRACE */