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