*** empty log message ***
[bpt/guile.git] / libguile / debug-malloc.c
CommitLineData
f1322139
MD
1/* Copyright (C) 2000 Free Software Foundation, Inc.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; see the file COPYING. If not, write to
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
17 *
18 * As a special exception, the Free Software Foundation gives permission
19 * for additional uses of the text contained in its release of GUILE.
20 *
21 * The exception is that, if you link the GUILE library with other files
22 * to produce an executable, this does not by itself cause the
23 * resulting executable to be covered by the GNU General Public License.
24 * Your use of that executable is in no way restricted on account of
25 * linking the GUILE library code into it.
26 *
27 * This exception does not however invalidate any other reasons why
28 * the executable file might be covered by the GNU General Public License.
29 *
30 * This exception applies only to the code released by the
31 * Free Software Foundation under the name GUILE. If you copy
32 * code from other Free Software Foundation releases into a copy of
33 * GUILE, as the General Public License permits, the exception does
34 * not apply to the code that you add in this way. To avoid misleading
35 * anyone as to the status of such modified files, you must delete
36 * this exception notice from them.
37 *
38 * If you write modifications of your own for GUILE, it is your choice
39 * whether to permit this exception to apply to your modifications.
40 * If you do not wish that, delete this exception notice. */
41
f1322139 42#include <string.h>
f1322139 43
a0599745
MD
44#include "libguile/_scm.h"
45#include "libguile/alist.h"
46#include "libguile/strings.h"
f1322139 47
0e850825 48#include "libguile/debug-malloc.h"
f1322139
MD
49
50/*
eb422a4c
MD
51 * The following code is a hack which I wrote quickly in order to
52 * solve a memory leak problem. Since I wanted to have the
53 * application running at close to normal speed, I prioritized speed
54 * over maintainability. /mdj
f1322139
MD
55 */
56
57typedef struct hash_entry {
58 const void *key;
59 const void *data;
60} hash_entry_t;
61
62#define N_SEEK 8
63
64static int malloc_type_size = 31;
65static hash_entry_t *malloc_type = 0;
0e850825 66static int malloc_object_size = 8191;
f1322139
MD
67static hash_entry_t *malloc_object = 0;
68
69#define TABLE(table) malloc_ ## table
70#define SIZE(table) malloc_ ## table ## _size
71#define HASH(table, key) \
72 &TABLE (table)[((unsigned long) key >> 4UL) * 2654435761UL % SIZE (table)]
73
74#define CREATE_HASH_ENTRY_AT(entry, table, h, k, done) \
75{ \
76 int i; \
77 do \
78 { \
79 for (i = 0; i < N_SEEK; ++i) \
80 if (h[i].key == 0) \
81 goto done; \
82 grow (&TABLE (table), &SIZE (table)); \
83 h = HASH (table, k); \
84 } \
85 while (1); \
86 done: \
87 (entry) = &h[i]; \
88}
89
90#define CREATE_HASH_ENTRY(table, k, d, done) \
91 do \
92 { \
93 hash_entry_t *h = HASH (table, k); \
94 hash_entry_t *entry; \
95 CREATE_HASH_ENTRY_AT (entry, table, h, k, done); \
96 entry->key = (k); \
97 entry->data = (d); \
98 } \
99 while (0)
100
101#define GET_CREATE_HASH_ENTRY(entry, table, k, done) \
102 do \
103 { \
104 hash_entry_t *h = HASH (table, k); \
105 int i; \
106 for (i = 0; i < N_SEEK; ++i) \
107 if (h[i].key == (void *) (k)) \
108 goto done; \
109 CREATE_HASH_ENTRY_AT (entry, table, h, k, gche ## done); \
110 entry->key = (k); \
111 entry->data = 0; \
112 break; \
113 done: \
114 (entry) = &h[i]; \
115 } \
116 while (0)
117
118#ifdef MISSING_BZERO_DECL
119extern void bzero (void *, size_t);
120#endif
121
122static void
123grow (hash_entry_t **table, int *size)
124{
125 hash_entry_t *oldtable = *table;
126 int oldsize = *size + N_SEEK;
127 hash_entry_t *TABLE (new) = 0;
128 int SIZE (new);
129 int i, j;
0e850825 130 SIZE (new) = 2 * (oldsize - N_SEEK + 1) - 1;
f1322139
MD
131 again:
132 TABLE (new) = realloc (TABLE (new),
133 sizeof (hash_entry_t) * (SIZE (new) + N_SEEK));
134 bzero (TABLE (new), sizeof (hash_entry_t) * (SIZE (new) + N_SEEK));
135 for (i = 0; i < oldsize; ++i)
136 if (oldtable[i].key)
137 {
138 hash_entry_t *h = HASH (new, oldtable[i].key);
139 for (j = 0; j < N_SEEK; ++j)
140 if (h[j].key == 0)
141 {
142 h[j] = oldtable[i];
143 goto next;
144 }
145 SIZE (new) *= 2;
146 goto again;
147 next:
148 ;
149 }
150 if (table == &malloc_type)
151 {
152 /* relocate malloc_object entries */
153 for (i = 0; i < oldsize; ++i)
154 if (oldtable[i].key)
155 {
156 hash_entry_t *h = HASH (new, oldtable[i].key);
157 while (h->key != oldtable[i].key)
158 ++h;
159 oldtable[i].data = h;
160 }
161 for (i = 0; i < malloc_object_size + N_SEEK; ++i)
162 if (malloc_object[i].key)
163 malloc_object[i].data
164 = ((hash_entry_t *) malloc_object[i].data)->data;
165 }
166 free (*table);
167 *table = TABLE (new);
168 *size = SIZE (new);
169}
170
171void
172scm_malloc_register (void *obj, const char *what)
173{
174 hash_entry_t *type;
175 GET_CREATE_HASH_ENTRY (type, type, what, l1);
176 type->data = (void *) ((int) type->data + 1);
177 CREATE_HASH_ENTRY (object, obj, type, l2);
178}
179
180void
181scm_malloc_unregister (void *obj)
182{
183 hash_entry_t *object, *type;
184 GET_CREATE_HASH_ENTRY (object, object, obj, l1);
185 type = (hash_entry_t *) object->data;
186 if (type == 0)
187 {
188 fprintf (stderr,
189 "scm_must_free called on object not allocated with scm_must_malloc\n");
190 abort ();
191 }
192 type->data = (void *) ((int) type->data - 1);
193 object->key = 0;
194}
195
196void
197scm_malloc_reregister (void *old, void *new, const char *newwhat)
198{
199 hash_entry_t *object, *type;
200 GET_CREATE_HASH_ENTRY (object, object, old, l1);
201 type = (hash_entry_t *) object->data;
202 if (type == 0)
203 {
204 fprintf (stderr,
205 "scm_must_realloc called on object not allocated with scm_must_malloc\n");
206 abort ();
207 }
208 if (strcmp ((char *) type->key, newwhat) != 0)
209 {
210 if (strcmp (newwhat, "vector-set-length!") != 0)
211 {
212 fprintf (stderr,
213 "scm_must_realloc called with arg %s, was %s\n",
214 newwhat,
215 (char *) type->key);
216 abort ();
217 }
218 }
219 if (new != old)
220 {
221 object->key = 0;
222 CREATE_HASH_ENTRY (object, new, type, l2);
223 }
224}
225
226SCM_DEFINE (scm_malloc_stats, "malloc-stats", 0, 0, 0,
227 (),
6836c87b
MG
228 "Return an alist ((@var{what} . @var{n}) ...) describing number\n"
229 "of malloced objects.\n"
230 "@var{what} is the second argument to @code{scm_must_malloc},\n"
231 "@var{n} is the number of objects of that type currently\n"
232 "allocated.")
f1322139
MD
233#define FUNC_NAME s_scm_malloc_stats
234{
235 SCM res = SCM_EOL;
236 int i;
237 for (i = 0; i < malloc_type_size + N_SEEK; ++i)
238 if (malloc_type[i].key)
239 res = scm_acons (scm_makfrom0str ((char *) malloc_type[i].key),
240 SCM_MAKINUM ((int) malloc_type[i].data),
241 res);
242 return res;
243}
244#undef FUNC_NAME
245
246void
247scm_debug_malloc_prehistory ()
248{
249 malloc_type = malloc (sizeof (hash_entry_t)
250 * (malloc_type_size + N_SEEK));
251 bzero (malloc_type, sizeof (hash_entry_t) * (malloc_type_size + N_SEEK));
252 malloc_object = malloc (sizeof (hash_entry_t)
253 * (malloc_object_size + N_SEEK));
254 bzero (malloc_object, sizeof (hash_entry_t) * (malloc_object_size + N_SEEK));
255}
256
257void
258scm_init_debug_malloc ()
259{
8dc9439f 260#ifndef SCM_MAGIC_SNARFER
0e850825 261#include "libguile/debug-malloc.x"
8dc9439f 262#endif
f1322139 263}
8dc9439f 264