1 /*
2 * mpatrol
3 * A library for controlling and tracing dynamic memory allocations.
4 * Copyright (C) 1997-2002 Graeme S. Roy <graeme.roy@analog.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library 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 GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19 * MA 02111-1307, USA.
20 */
21
22
23 /*
24 * Memory leak tables. Such tables are used by the mpatrol library to record
25 * where heap memory is allocated and freed so that a summary of where memory
26 * leaks occurred can be made. The hash function comes from P. J. Weinberger's
27 * C compiler and was published in Compilers: Principles, Techniques and Tools,
28 * First Edition by Aho, Sethi and Ullman (Addison-Wesley, 1986, ISBN
29 * 0-201-10194-7).
30 */
31
32
33 #include "leaktab.h"
34 #include "utils.h"
35 #include <string.h>
36
37
38 #if MP_IDENT_SUPPORT
39 #ident "$Id: leaktab.c,v 1.3 2002/01/08 20:13:59 graeme Exp $"
40 #else /* MP_IDENT_SUPPORT */
41 static MP_CONST MP_VOLATILE char *leaktab_id = "$Id: leaktab.c,v 1.3 2002/01/08 20:13:59 graeme Exp $";
42 #endif /* MP_IDENT_SUPPORT */
43
44
45 #ifdef __cplusplus
46 extern "C"
47 {
48 #endif /* __cplusplus */
49
50
51 /* Initialise the fields of a leaktab so that the leak table becomes empty.
52 */
53
54 MP_GLOBAL
55 void
__mp_newleaktab(leaktab * t,heaphead * h)56 __mp_newleaktab(leaktab *t, heaphead *h)
57 {
58 struct { char x; tablenode y; } z;
59 size_t i;
60 long n;
61
62 t->heap = h;
63 /* Determine the minimum alignment for a table node on this system and
64 * force the alignment to be a power of two. This information is used
65 * when initialising the slot table.
66 */
67 n = (char *) &z.y - &z.x;
68 __mp_newslots(&t->table, sizeof(tablenode), __mp_poweroftwo(n));
69 for (i = 0; i < MP_LEAKTAB_SIZE; i++)
70 __mp_newlist(&t->slots[i]);
71 __mp_newlist(&t->list);
72 __mp_newtree(&t->tree);
73 t->isize = t->size = 0;
74 t->prot = MA_NOACCESS;
75 t->protrecur = 0;
76 t->tracing = 0;
77 }
78
79
80 /* Forget all data currently in the leak table.
81 */
82
83 MP_GLOBAL
84 void
__mp_deleteleaktab(leaktab * t)85 __mp_deleteleaktab(leaktab *t)
86 {
87 size_t i;
88
89 /* We don't need to explicitly free any memory as this is dealt with
90 * at a lower level by the heap manager.
91 */
92 t->heap = NULL;
93 t->table.free = NULL;
94 t->table.size = 0;
95 for (i = 0; i < MP_LEAKTAB_SIZE; i++)
96 __mp_newlist(&t->slots[i]);
97 __mp_newlist(&t->list);
98 __mp_newtree(&t->tree);
99 t->isize = t->size = 0;
100 t->prot = MA_NOACCESS;
101 t->protrecur = 0;
102 t->tracing = 0;
103 }
104
105
106 /* Clear all data currently in the leak table.
107 */
108
109 MP_GLOBAL
110 void
__mp_clearleaktab(leaktab * t)111 __mp_clearleaktab(leaktab *t)
112 {
113 tablenode *n;
114 size_t i;
115
116 for (i = 0; i < MP_LEAKTAB_SIZE; i++)
117 while (n = (tablenode *) __mp_remhead(&t->slots[i]))
118 __mp_freeslot(&t->table, n);
119 __mp_newtree(&t->tree);
120 t->size = 0;
121 }
122
123
124 /* Sort the leak table.
125 */
126
127 MP_GLOBAL
128 void
__mp_sortleaktab(leaktab * t,int o,int c)129 __mp_sortleaktab(leaktab *t, int o, int c)
130 {
131 tablenode *n;
132 size_t i;
133 unsigned long k;
134
135 __mp_newtree(&t->tree);
136 for (i = 0; i < MP_LEAKTAB_SIZE; i++)
137 for (n = (tablenode *) t->slots[i].head; n->data.node.next != NULL;
138 n = (tablenode *) n->data.node.next)
139 {
140 if (o == SOPT_ALLOCATED)
141 if (c != 0)
142 k = n->data.acount;
143 else
144 k = n->data.atotal;
145 else if (o == SOPT_FREED)
146 if (c != 0)
147 k = n->data.dcount;
148 else
149 k = n->data.dtotal;
150 else if (c != 0)
151 k = n->data.acount - n->data.dcount;
152 else
153 k = n->data.atotal - n->data.dtotal;
154 if (k > 0)
155 __mp_treeinsert(&t->tree, &n->data.tnode, k);
156 }
157 }
158
159
160 /* Calculate the hash bucket an entry should be placed in.
161 */
162
163 static
164 unsigned long
hashloc(char * f,unsigned long l)165 hashloc(char *f, unsigned long l)
166 {
167 unsigned long g, h;
168
169 if (f != NULL)
170 for (h = 0; *f != '\0'; f++)
171 {
172 h = (h << 4) + *f;
173 if (g = h & (0xFUL << ((sizeof(unsigned long) << 3) - 4)))
174 {
175 h ^= g >> ((sizeof(unsigned long) << 3) - 8);
176 h ^= g;
177 }
178 }
179 else
180 h = 0;
181 return (h + l) % MP_LEAKTAB_SIZE;
182 }
183
184
185 /* Compare the file names and line numbers of two entries.
186 */
187
188 static
189 int
hashcmp(char * f1,unsigned long l1,char * f2,unsigned long l2)190 hashcmp(char *f1, unsigned long l1, char *f2, unsigned long l2)
191 {
192 if ((l1 != l2) || ((f1 == NULL) && (f2 != NULL)) ||
193 ((f1 != NULL) && (f2 == NULL)))
194 return 0;
195 if ((f1 == f2) || (strcmp(f1, f2) == 0))
196 return 1;
197 return 0;
198 }
199
200
201 /* Allocate a new table node.
202 */
203
204 static
205 tablenode *
gettablenode(leaktab * t)206 gettablenode(leaktab *t)
207 {
208 tablenode *n;
209 heapnode *p;
210
211 /* If we have no more table node slots left then we must allocate
212 * some more memory for them. An extra MP_ALLOCFACTOR pages of memory
213 * should suffice.
214 */
215 if ((n = (tablenode *) __mp_getslot(&t->table)) == NULL)
216 {
217 if ((p = __mp_heapalloc(t->heap, t->heap->memory.page * MP_ALLOCFACTOR,
218 t->table.entalign, 1)) == NULL)
219 return NULL;
220 __mp_initslots(&t->table, p->block, p->size);
221 n = (tablenode *) __mp_getslot(&t->table);
222 __mp_addtail(&t->list, &n->index.node);
223 n->index.block = p->block;
224 n->index.size = p->size;
225 t->isize += p->size;
226 n = (tablenode *) __mp_getslot(&t->table);
227 }
228 return n;
229 }
230
231
232 /* Add a memory allocation to the leak table.
233 */
234
235 MP_GLOBAL
236 int
__mp_allocentry(leaktab * t,char * f,unsigned long l,size_t c)237 __mp_allocentry(leaktab *t, char *f, unsigned long l, size_t c)
238 {
239 tablenode *n;
240 unsigned long k;
241
242 k = hashloc(f, l);
243 for (n = (tablenode *) t->slots[k].head; n->data.node.next != NULL;
244 n = (tablenode *) n->data.node.next)
245 if (hashcmp(n->data.file, n->data.line, f, l))
246 {
247 n->data.acount++;
248 n->data.atotal += c;
249 return 1;
250 }
251 if ((n = gettablenode(t)) == NULL)
252 return 0;
253 __mp_addhead(&t->slots[k], &n->data.node);
254 n->data.file = f;
255 n->data.line = l;
256 n->data.acount = 1;
257 n->data.atotal = c;
258 n->data.dcount = 0;
259 n->data.dtotal = 0;
260 t->size++;
261 return 1;
262 }
263
264
265 /* Remove a memory allocation from the leak table.
266 */
267
268 MP_GLOBAL
269 int
__mp_freeentry(leaktab * t,char * f,unsigned long l,size_t c)270 __mp_freeentry(leaktab *t, char *f, unsigned long l, size_t c)
271 {
272 tablenode *n;
273 unsigned long k;
274
275 k = hashloc(f, l);
276 for (n = (tablenode *) t->slots[k].head; n->data.node.next != NULL;
277 n = (tablenode *) n->data.node.next)
278 if (hashcmp(n->data.file, n->data.line, f, l))
279 {
280 n->data.dcount++;
281 if (n->data.dcount > n->data.acount)
282 n->data.dcount = n->data.acount;
283 n->data.dtotal += c;
284 if (n->data.dtotal > n->data.atotal)
285 n->data.dtotal = n->data.atotal;
286 return 1;
287 }
288 return 0;
289 }
290
291
292 /* Protect the memory blocks used by the leak table with the supplied access
293 * permission.
294 */
295
296 MP_GLOBAL
297 int
__mp_protectleaktab(leaktab * t,memaccess a)298 __mp_protectleaktab(leaktab *t, memaccess a)
299 {
300 tablenode *n;
301
302 /* The library already knows what its protection status is so we don't
303 * need to do anything if the request has already been done.
304 */
305 if (t->prot == a)
306 {
307 t->protrecur++;
308 return 1;
309 }
310 else if (t->protrecur > 0)
311 {
312 t->protrecur--;
313 return 1;
314 }
315 t->prot = a;
316 for (n = (tablenode *) t->list.head; n->index.node.next != NULL;
317 n = (tablenode *) n->index.node.next)
318 if (!__mp_memprotect(&t->heap->memory, n->index.block, n->index.size,
319 a))
320 return 0;
321 return 1;
322 }
323
324
325 #ifdef __cplusplus
326 }
327 #endif /* __cplusplus */
328