1 /************************************************************************
3 * Copyright (c) 1985 by *
4 * Digital Equipment Corporation, Maynard, MA *
5 * All rights reserved. *
7 * The information in this software is subject to change without *
8 * notice and should not be construed as a commitment by Digital *
9 * Equipment Corporation. *
11 * Digital assumes no responsibility for the use or reliability *
12 * of its software on equipment which is not supplied by Digital. *
14 * Redistribution and use in source and binary forms are permitted *
15 * provided that the above copyright notice and this paragraph are *
16 * duplicated in all such forms and that any documentation, *
17 * advertising materials, and other materials related to such *
18 * distribution and use acknowledge that the software was developed *
19 * by Digital Equipment Corporation. The name of Digital Equipment *
20 * Corporation may not be used to endorse or promote products derived *
21 * from this software without specific prior written permission. *
22 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR *
23 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED *
24 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.*
25 * Do not take internally. In case of accidental ingestion, contact *
26 * your physician immediately. *
28 ************************************************************************/
30 /* DO NOT INCLUDE "mnemosyne.h" !!! */
32 #include <sys/types.h>
35 /* shared stuff - and decl of struct ptr */
38 static char rcsid
[] = "/fats/tools/hsv/mnemosyne/mnemosyne.c,v 1.1.1.1 1995/06/06 18:18:28 fabio Exp";
42 malloc() realloc() and family wrappers - these functions manage a set
43 of data files that are updated whenever a pointer is allocated or freed,
44 as well as gathering stats about dynamic memory use and leakage.
46 Marcus J. Ranum, 1990. (mjr@decuac.dec.com)
51 there is some egregious use of globals, void functions, and whatnot
52 in this code. it is mostly due to the constraint that nothing must
53 be visible to the outside, and that the code must be structurally
54 simple. error checking is pitched out the window in spots, since an
55 error in the mnemosyne code should not affect whatever is being
56 instrumented if at all possible. this means no errors, no signals,
57 nothing. (this message brought to you by my ego, in case you think
58 I don't know how to write better code than this) :)
60 mjr, hacking on Christmas, 1990.
63 #define REC_UNINIT 000
64 #define REC_INITTED 001
68 static int rec_state
= REC_UNINIT
;
71 this method of storing the symbol maps is not the most efficient, but
72 then that's not important here, since all we're trying to do is find
73 memory leaks. if you choose to improve the symbol management to use
74 bucketed hash tables, please contact the author and the code will be
75 updated :) - besides, since we do file I/O every time you malloc or
76 free something, there's no way in hell this is going to set any new
81 /* storage for code/line # entry */
93 /* static symbol map */
99 long nalloc
; /* count of allocations */
100 long nrlloc
; /* count of re-allocations */
101 long nfree
; /* count of frees */
102 long nbfree
; /* count of bad frees */
103 long ninuse
; /* known allocated memory in use */
104 float avgsiz
; /* average malloc size */
106 /* one entry per pointer returned by malloc */
107 int pmap
; /* current next ptr map to alloc */
108 struct ptr
*phash
[HASHSIZ
];
110 /* one entry per line of code that calls malloc/realloc, etc */
111 int lmap
; /* current next line map to alloc */
112 struct sym
*shash
[HASHSIZ
]; /* hash access */
118 /* print a locale record with checks for closed log file */
125 if(map
.log
== (FILE *)0)
128 (void)fprintf(map
.log
," \"%s\"",lab
);
130 (void)fprintf(map
.log
," unknown");
132 (void)fprintf(map
.log
," line:%d",lin
);
134 (void)fprintf(map
.log
," size:%d",siz
);
140 /* print a symbol map entry with checks for closed log file */
145 if(map
.log
== (FILE *)0)
147 (void)fprintf(map
.log
," \"%s\"",s
->labl
);
149 (void)fprintf(map
.log
," line:%d",s
->lineno
);
155 /* output a warning message with checks for closed log file */
160 if(map
.log
== (FILE *)0)
162 (void)fprintf(map
.log
,"%s",s
);
168 /* save an entry to the .lines file */
173 if(map
.fp
== (FILE *)0)
176 (void)fprintf(map
.fp
,"%d\t%d\t%.1f\t%d\t%s\n",
177 s
->mapno
,s
->mallcnt
,s
->avsiz
,s
->lineno
,s
->labl
);
183 /* save an entry in the pointer map file */
186 register struct ptr
*p
;
188 if(lseek(map
.fd
,(off_t
)(p
->map
* sizeof(p
->dsk
)),0) !=
189 (off_t
)(p
->map
* sizeof(p
->dsk
))) {
190 pmsg("mnemosyne: cannot seek in pointer map file\n");
191 rec_state
|= REC_ERR
;
195 if(write(map
.fd
,(char *)&(p
->dsk
),sizeof(p
->dsk
)) != sizeof(p
->dsk
)) {
196 pmsg("mnemosyne: cannot write in pointer map file\n");
197 rec_state
|= REC_ERR
;
205 /* initialize everything - symbol tables, files, and maps */
211 if(rec_state
& REC_INITTED
)
214 if((map
.fp
= fopen(LINESFILE
,"w")) == (FILE *)0)
216 if((map
.fd
= open(PTRFILE
,O_RDWR
|O_CREAT
|O_TRUNC
,0600)) < 0) {
217 (void)fclose(map
.fp
);
222 map
.lmap
= map
.pmap
= 0;
223 map
.nalloc
= map
.nrlloc
= map
.nfree
= map
.nbfree
= 0L;
227 for(xp
= 0; xp
< HASHSIZ
; xp
++) {
228 map
.phash
[xp
] = (struct ptr
*)0;
229 map
.shash
[xp
] = (struct sym
*)0;
232 rec_state
= REC_INITTED
| REC_ON
;
238 /* set logging to a FILE * */
249 /* return state of the recorder */
253 return((rec_state
& REC_ON
) && !(rec_state
& REC_ERR
));
259 /* turn on or off recording */
261 mnem_setrecording(val
)
264 if(!(rec_state
& REC_INITTED
))
270 rec_state
&= ~REC_ON
;
272 if(map
.fp
!= (FILE *)0)
273 (void)fflush(map
.fp
);
275 rec_state
|= REC_ONOFF
;
282 /* lookup a pointer record - search pointer hash table */
287 register struct ptr
*p
;
289 /* this probably give simply terrible hash performance */
290 p
= map
.phash
[(unsigned long)ptr
% HASHSIZ
];
291 while(p
!= (struct ptr
*)0) {
296 return((struct ptr
*)0);
303 * polynomial conversion ignoring overflows
304 * [this seems to work remarkably well, in fact better
305 * then the ndbm hash function. Replace at your own risk]
308 * author: oz@nexus.yorku.ca
314 register unsigned int n
= 0;
317 n
= *str
++ + 65599 * n
;
324 /* lookup a line/source entry by name (search hash table) */
326 lookupsymbyname(nam
,lin
)
330 register struct sym
*s
;
336 s
= map
.shash
[(dbm_hash(p
) + lin
) % HASHSIZ
];
337 while(s
!= (struct sym
*)0) {
338 if(!strcmp(s
->labl
,nam
) && s
->lineno
== lin
)
343 return((struct sym
*)0);
349 /* lookup a line/source entry by number (exhaustively search hash table) */
354 register struct sym
*s
;
357 for(x
= 0; x
< HASHSIZ
; x
++) {
359 while(s
!= (struct sym
*)0) {
365 return((struct sym
*)0);
370 /* stuff a pointer's value in the pointer map table */
372 storeptr(ptr
,siz
,lab
,lin
)
378 register struct ptr
*p
;
379 register struct sym
*s
;
383 is there is no existing symbol entry for this line of code...
384 we must needs make one - and painful it is...
386 if((s
= lookupsymbyname(lab
,lin
)) == (struct sym
*)0) {
387 s
= (struct sym
*)malloc(sizeof(struct sym
));
388 if(s
== (struct sym
*)0) {
389 pmsg("mnemosyne: cannot allocate sym entry\n");
390 rec_state
|= REC_ERR
;
395 this is funky - since we know the label is (?)
396 compiled-in, we can just keep a pointer to it,
397 rather than copying our own version of it.
404 s
->mapno
= map
.lmap
++;
406 /* add sym to hash table */
407 s
->next
= map
.shash
[hv
= ((dbm_hash(s
->labl
) + lin
) % HASHSIZ
)];
415 /* found an already defined symbol. store some averages */
416 s
->avsiz
= ((s
->avsiz
* s
->mallcnt
) + siz
) / (s
->mallcnt
+ 1);
421 if(p
!= (struct ptr
*)0 && p
->dsk
.siz
!= 0) {
424 pmsg("pointer re-allocated without being freed");
425 ploc(lab
,lin
,(int)siz
);
426 if((x
= lookupsymbynum(p
->dsk
.smap
)) != (struct sym
*)0) {
427 pmsg(" last allocated ");
433 /* heavy sigh. no entry for this pointer. make one. */
434 if(p
== (struct ptr
*)0) {
435 p
= (struct ptr
*)malloc(sizeof(struct ptr
));
436 if(p
== (struct ptr
*)0) {
437 pmsg("mnemosyne: cannot expand pointer table\n");
438 rec_state
|= REC_ERR
;
443 p
->next
= map
.phash
[(unsigned long)ptr
% HASHSIZ
];
444 map
.phash
[(unsigned long)ptr
% HASHSIZ
] = p
;
447 /* if we get to here (hazoo! hazaa!) both 's' and 'p' are OK */
450 p
->dsk
.smap
= s
->mapno
;
463 mark a pointer as now being free. note that a 1 is returned IF
464 the actual value should NOT be really passed to free()
472 register struct ptr
*p
;
475 if(p
== (struct ptr
*)0) {
476 pmsg("pointer freed that was never allocated");
482 if(p
!= (struct ptr
*)0 && p
->dsk
.siz
== 0) {
485 pmsg("pointer re-freed when already free");
487 if((x
= lookupsymbynum(p
->dsk
.smap
)) != (struct sym
*)0) {
488 pmsg(" last allocated:");
496 map
.ninuse
-= p
->dsk
.siz
;
498 /* write in the map that it is free */
508 /* pretend we are malloc() */
510 mnem_malloc(siz
,lab
,lin
)
517 if(!(rec_state
& REC_INITTED
))
520 if((ret
= malloc(siz
)) == (mall_t
)0) {
521 pmsg("malloc returned null pointer at");
522 ploc(lab
,lin
,(int)siz
);
527 if((rec_state
& REC_ON
) && !(rec_state
& REC_ERR
))
528 storeptr(ret
,(int)siz
,lab
,lin
);
530 map
.avgsiz
= ((map
.avgsiz
* map
.nalloc
) + siz
) / (map
.nalloc
+ 1);
538 /* pretend we are calloc() */
540 mnem_calloc(cnt
,siz
,lab
,lin
)
548 if(!(rec_state
& REC_INITTED
))
551 if((ret
= calloc(cnt
,siz
)) == (mall_t
)0) {
552 pmsg("calloc returned null pointer at");
553 ploc(lab
,lin
,(int)(siz
* cnt
));
558 if((rec_state
& REC_ON
) && !(rec_state
& REC_ERR
))
559 storeptr(ret
,(int)(cnt
* siz
),lab
,lin
);
561 map
.avgsiz
= ((map
.avgsiz
* map
.nalloc
) + siz
) / (map
.nalloc
+ 1);
569 /* pretend we are realloc() */
571 mnem_realloc(ptr
,siz
,lab
,lin
)
579 if(!(rec_state
& REC_INITTED
))
582 if((ret
= realloc(ptr
,siz
)) == (mall_t
)0) {
583 pmsg("realloc returned null pointer at");
584 ploc(lab
,lin
,(int)siz
);
589 if((rec_state
& REC_ON
) && !(rec_state
& REC_ERR
)) {
590 if(!freeptr(ptr
,lab
,lin
))
591 storeptr(ret
,(int)siz
,lab
,lin
);
602 /* pretend we are free() */
604 mnem_free(ptr
,lab
,lin
)
609 if(!(rec_state
& REC_INITTED
))
612 if((rec_state
& REC_ON
) && !(rec_state
& REC_ERR
))
613 if(freeptr(ptr
,lab
,lin
) == 0) {
623 /* dump everything we know about nothing in particular */
627 register struct sym
*s
;
630 if(map
.fp
== (FILE *)0)
633 (void)fseek(map
.fp
,0L,0);
635 /* dump our life's story */
636 (void)fprintf(map
.fp
,"#total allocations:%ld\n",map
.nalloc
);
637 (void)fprintf(map
.fp
,"#total re-allocations:%ld\n",map
.nrlloc
);
638 (void)fprintf(map
.fp
,"#total frees:%ld\n",map
.nfree
);
641 (void)fprintf(map
.fp
,"#bad/dup frees:%ld\n",map
.nbfree
);
643 (void)fprintf(map
.fp
,"#total allocated never freed:%ld\n",map
.ninuse
);
645 (void)fprintf(map
.fp
,"#average size of allocations:%.1f\n",map
.avgsiz
);
647 /* note if we detected an internal error */
648 if(rec_state
& REC_ERR
)
649 (void)fprintf(map
.fp
,
650 "#(figures likely inaccurate due to error)\n");
652 /* note if the system was on all the time ? */
653 if(!(rec_state
& REC_ON
) || (rec_state
& REC_ONOFF
))
654 (void)fprintf(map
.fp
,
655 "#(figures likely inaccurate as recording was off)\n");
657 /* write the legend */
658 (void)fprintf(map
.fp
,"#map#\tcalls\tave\tline#\tfile\n");
660 for(x
= 0; x
< HASHSIZ
; x
++) {
662 while(s
!= (struct sym
*)0) {
668 (void)fflush(map
.fp
);