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