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