addition of silc.css
[silc.git] / apps / irssi / src / core / memdebug.c
1 /*
2  memdebug.c : irssi
3
4     Copyright (C) 1999-2000 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <glib.h>
26 #include <gmodule.h>
27
28 /*#define ENABLE_BUFFER_CHECKS*/
29 #define BUFFER_CHECK_SIZE 5
30 #define MIN_BUFFER_CHECK_SIZE 2
31
32 typedef struct {
33         void *p;
34         int size;
35         char *file;
36         int line;
37         char *comment;
38 } MEM_REC;
39
40 static GHashTable *data = NULL, *preallocs = NULL;
41 static const char *comment = "";
42
43 static void add_flow_checks(char *p, unsigned long size)
44 {
45 #ifdef ENABLE_BUFFER_CHECKS
46         int n;
47
48         for (n = 0; n < BUFFER_CHECK_SIZE; n++)
49                 p[n] = n ^ 0x7f;
50         for (n = 0; n < BUFFER_CHECK_SIZE; n++)
51                 p[size-BUFFER_CHECK_SIZE+n] = n ^ 0x7f;
52 #endif
53 }
54
55 void ig_memcheck_rec(void *key, MEM_REC *rec)
56 {
57         guchar *p;
58         int n;
59
60         if (rec->size != INT_MIN){
61                 p = rec->p;
62
63                 for (n = 0; n < MIN_BUFFER_CHECK_SIZE; n++)
64                         if (p[n] != (n ^ 0x7f))
65                                 g_error("buffer underflow, file %s line %d!\n", rec->file, rec->line);
66
67                 for (n = 0; n < MIN_BUFFER_CHECK_SIZE; n++)
68                         if (p[rec->size-BUFFER_CHECK_SIZE+n] != (n ^ 0x7f))
69                                 g_error("buffer overflow, file %s line %d!\n", rec->file, rec->line);
70         }
71 }
72
73 static void mem_check(void)
74 {
75 #ifdef ENABLE_BUFFER_CHECKS
76         g_hash_table_foreach(data, (GHFunc) ig_memcheck_rec, NULL);
77 #endif
78 }
79
80 static void data_add(char *p, int size, const char *file, int line)
81 {
82         MEM_REC *rec;
83
84         if (size <= 0 && size != INT_MIN)
85                 g_error("size = %d, file %s line %d", size, file, line);
86
87         if (data == NULL) {
88                 data = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
89                 preallocs = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
90         }
91
92         if (g_hash_table_lookup(data, p) != NULL)
93                 g_error("data_add() already malloc()'ed %p (in %s:%d)", p, file, line);
94
95         rec = g_new(MEM_REC, 1);
96         g_hash_table_insert(data, p, rec);
97
98         rec->p = p;
99         rec->size = size;
100         rec->file = g_strdup(file);
101         rec->line = line;
102         rec->comment = g_strdup(comment);
103
104         if (size == INT_MIN)
105                 g_hash_table_insert(preallocs, p-BUFFER_CHECK_SIZE, p);
106         else
107                 add_flow_checks(p, size);
108         mem_check();
109 }
110
111 static void data_clear(char *p)
112 {
113         MEM_REC *rec;
114
115         if (g_hash_table_lookup(preallocs, p) != NULL)
116                 p += BUFFER_CHECK_SIZE;
117
118         rec = g_hash_table_lookup(data, p);
119         if (rec != NULL && rec->size > 0)
120                 memset(p, 'F', rec->size);
121 }
122
123 static void *data_remove(char *p, const char *file, int line)
124 {
125         MEM_REC *rec;
126
127         mem_check();
128
129         if (g_hash_table_lookup(preallocs, p) != NULL) {
130                 g_hash_table_remove(preallocs, p);
131                 p += BUFFER_CHECK_SIZE;
132         }
133
134         rec = g_hash_table_lookup(data, p);
135         if (rec == NULL) {
136                 g_warning("data_remove() data %p not found (in %s:%d)", p, file, line);
137                 return p+BUFFER_CHECK_SIZE;
138         }
139
140         g_hash_table_remove(data, p);
141         g_free(rec->file);
142         g_free(rec->comment);
143         g_free(rec);
144
145         return p;
146 }
147
148 void *ig_malloc(int size, const char *file, int line)
149 {
150         char *p;
151
152         size += BUFFER_CHECK_SIZE*2;
153         p = g_malloc(size);
154         data_add(p, size, file, line);
155         return (void *) (p+BUFFER_CHECK_SIZE);
156 }
157
158 void *ig_malloc0(int size, const char *file, int line)
159 {
160         char *p;
161
162         size += BUFFER_CHECK_SIZE*2;
163         p = g_malloc0(size);
164         data_add(p, size, file, line);
165         return (void *) (p+BUFFER_CHECK_SIZE);
166 }
167
168 void *ig_realloc(void *mem, unsigned long size, const char *file, int line)
169 {
170         char *p, *oldmem = mem;
171
172         size += BUFFER_CHECK_SIZE*2;
173         oldmem -= BUFFER_CHECK_SIZE;
174         data_remove(oldmem, file, line);
175         p = g_realloc(oldmem, size);
176         data_add(p, size, file, line);
177         return (void *) (p+BUFFER_CHECK_SIZE);
178 }
179
180 char *ig_strdup(const char *str, const char *file, int line)
181 {
182         void *p;
183
184         if (str == NULL) return NULL;
185
186         p = ig_malloc(strlen(str)+1, file, line);
187         strcpy(p, str);
188
189         return p;
190 }
191
192 char *ig_strndup(const char *str, int count, const char *file, int line)
193 {
194         char *p;
195
196         if (str == NULL) return NULL;
197
198         p = ig_malloc(count+1, file, line);
199         strncpy(p, str, count); p[count] = '\0';
200
201         return p;
202 }
203
204 char *ig_strconcat(const char *file, int line, const char *str, ...)
205 {
206   guint   l;
207   va_list args;
208   char    *s;
209   char    *concat;
210
211   g_return_val_if_fail (str != NULL, NULL);
212
213   l = 1 + strlen (str);
214   va_start (args, str);
215   s = va_arg (args, char*);
216   while (s)
217     {
218       l += strlen (s);
219       s = va_arg (args, char*);
220     }
221   va_end (args);
222
223   concat = ig_malloc(l, file, line);
224   concat[0] = 0;
225
226   strcat (concat, str);
227   va_start (args, str);
228   s = va_arg (args, char*);
229   while (s)
230     {
231       strcat (concat, s);
232       s = va_arg (args, char*);
233     }
234   va_end (args);
235
236   return concat;
237 }
238
239 char *ig_strdup_printf(const char *file, int line, const char *format, ...)
240 {
241         char *buffer, *p;
242         va_list args;
243
244         va_start (args, format);
245         buffer = g_strdup_vprintf (format, args);
246         va_end (args);
247
248         p = ig_malloc(strlen(buffer)+1, file, line);
249         strcpy(p, buffer);
250         g_free(buffer);
251
252         return p;
253 }
254
255 char *ig_strdup_vprintf(const char *file, int line, const char *format, va_list args)
256 {
257         char *buffer, *p;
258
259         buffer = g_strdup_vprintf (format, args);
260
261         p = ig_malloc(strlen(buffer)+1, file, line);
262         strcpy(p, buffer);
263         g_free(buffer);
264
265         return p;
266 }
267
268 void ig_free(void *p)
269 {
270         char *cp = p;
271
272         if (cp == NULL) g_error("ig_free() : trying to free NULL");
273
274         cp -= BUFFER_CHECK_SIZE;
275         data_clear(cp);
276         cp = data_remove(cp, "??", 0);
277         if (cp != NULL) g_free(cp);
278 }
279
280 GString *ig_string_new(const char *file, int line, const char *str)
281 {
282         GString *ret;
283
284         ret = g_string_new(str);
285         data_add((void *) ret, INT_MIN, file, line);
286         return ret;
287 }
288
289 void ig_string_free(const char *file, int line, GString *str, gboolean freeit)
290 {
291         data_remove((void *) str, file, line);
292         if (!freeit)
293                 data_add(str->str, INT_MIN, file, line);
294
295         g_string_free(str, freeit);
296 }
297
298 char *ig_strjoinv(const char *file, int line, const char *sepa, char **array)
299 {
300         char *ret;
301
302         ret = g_strjoinv(sepa, array);
303         data_add(ret, INT_MIN, file, line);
304         return ret;
305 }
306
307 char *ig_dirname(const char *file, int line, const char *fname)
308 {
309         char *ret;
310
311         ret = g_dirname(fname);
312         data_add(ret, INT_MIN, file, line);
313         return ret;
314 }
315
316 char *ig_module_build_path(const char *file, int line, const char *dir, const char *module)
317 {
318         char *ret;
319
320         ret = g_module_build_path(dir, module);
321         data_add(ret, INT_MIN, file, line);
322         return ret;
323 }
324
325 void ig_profile_line(void *key, MEM_REC *rec)
326 {
327         char *data;
328
329         if (*rec->comment == '\0' &&
330             (strcmp(rec->file, "ig_strdup_printf") == 0 ||
331              strcmp(rec->file, "ig_strdup_vprintf") == 0 ||
332              strcmp(rec->file, "ig_strconcat") == 0 ||
333              strcmp(rec->file, "ig_string_free (free = FALSE)") == 0))
334                 data = (char *) rec->p + BUFFER_CHECK_SIZE;
335         else
336                 data = rec->comment;
337         fprintf(stderr, "%s:%d %d bytes (%s)\n", rec->file, rec->line, rec->size, data);
338 }
339
340 void ig_mem_profile(void)
341 {
342     g_hash_table_foreach(data, (GHFunc) ig_profile_line, NULL);
343     g_hash_table_destroy(data);
344     g_hash_table_destroy(preallocs);
345 }
346
347 static MEM_REC *largest[10];
348
349 void ig_profile_largest(void *key, MEM_REC *rec)
350 {
351     int n;
352
353     for (n = 0; n < 10; n++)
354     {
355         if (largest[n] == NULL || rec->size > largest[n]->size)
356         {
357             g_memmove(largest+n+1, largest+n, sizeof(void *)*(9-n));
358             largest[n] = rec;
359         }
360     }
361 }
362
363 void ig_mem_profile_largest(void)
364 {
365     /*int n;*/
366
367     memset(&largest, 0, sizeof(MEM_REC*)*10);
368     /*g_hash_table_foreach(data, (GHFunc) ig_profile_largest, NULL);
369
370     for (n = 0; n < 10 && largest[n] != NULL; n++)
371     {
372         ig_profile_line(NULL, largest[n]);
373     }*/
374 }
375
376 void ig_set_data(const char *data)
377 {
378     comment = data;
379 }