emergency commit
[cl-cudd.git] / distr / mnemosyne / mnemosyne.c
blob0d759da186b7641e29379d948b17a51f859b1603
1 /************************************************************************
2 * *
3 * Copyright (c) 1985 by *
4 * Digital Equipment Corporation, Maynard, MA *
5 * All rights reserved. *
6 * *
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. *
10 * *
11 * Digital assumes no responsibility for the use or reliability *
12 * of its software on equipment which is not supplied by Digital. *
13 * *
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. *
27 * *
28 ************************************************************************/
30 /* DO NOT INCLUDE "mnemosyne.h" !!! */
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <sys/file.h>
35 /* shared stuff - and decl of struct ptr */
36 #include "mnemconf.h"
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
65 #define REC_ERR 002
66 #define REC_ON 010
67 #define REC_ONOFF 020
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
77 records for speed.
81 /* storage for code/line # entry */
82 struct sym {
83 char *labl;
84 int lineno;
85 int mapno;
86 int mallcnt;
87 float avsiz;
88 struct sym *next;
93 /* static symbol map */
94 static struct {
95 FILE *fp;
96 FILE *log;
97 int fd;
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 */
113 } map;
118 /* print a locale record with checks for closed log file */
119 static void
120 ploc(lab,lin,siz)
121 char *lab;
122 int lin;
123 int siz;
125 if(map.log == (FILE *)0)
126 return;
127 if(lab != (char *)0)
128 (void)fprintf(map.log," \"%s\"",lab);
129 else
130 (void)fprintf(map.log," unknown");
131 if(lin != -1)
132 (void)fprintf(map.log," line:%d",lin);
133 if(siz != -1)
134 (void)fprintf(map.log," size:%d",siz);
140 /* print a symbol map entry with checks for closed log file */
141 static void
142 psym(s)
143 struct sym *s;
145 if(map.log == (FILE *)0)
146 return;
147 (void)fprintf(map.log," \"%s\"",s->labl);
148 if(s->lineno != -1)
149 (void)fprintf(map.log," line:%d",s->lineno);
155 /* output a warning message with checks for closed log file */
156 static void
157 pmsg(s)
158 char *s;
160 if(map.log == (FILE *)0)
161 return;
162 (void)fprintf(map.log,"%s",s);
168 /* save an entry to the .lines file */
169 static void
170 savesym(s)
171 struct sym *s;
173 if(map.fp == (FILE *)0)
174 return;
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 */
184 static void
185 saveptr(p)
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;
192 return;
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;
198 return;
205 /* initialize everything - symbol tables, files, and maps */
206 static void
207 initmap()
209 register int xp;
211 if(rec_state & REC_INITTED)
212 return;
214 if((map.fp = fopen(LINESFILE,"w")) == (FILE *)0)
215 return;
216 if((map.fd = open(PTRFILE,O_RDWR|O_CREAT|O_TRUNC,0600)) < 0) {
217 (void)fclose(map.fp);
218 return;
221 map.log = stderr;
222 map.lmap = map.pmap = 0;
223 map.nalloc = map.nrlloc = map.nfree = map.nbfree = 0L;
224 map.ninuse = 0L;
225 map.avgsiz = 0.0;
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 * */
239 void
240 mnem_setlog(fp)
241 FILE *fp;
243 map.log = fp;
249 /* return state of the recorder */
251 mnem_recording()
253 return((rec_state & REC_ON) && !(rec_state & REC_ERR));
259 /* turn on or off recording */
261 mnem_setrecording(val)
262 int val;
264 if(!(rec_state & REC_INITTED))
265 initmap();
267 if(val)
268 rec_state |= REC_ON;
269 else
270 rec_state &= ~REC_ON;
272 if(map.fp != (FILE *)0)
273 (void)fflush(map.fp);
275 rec_state |= REC_ONOFF;
276 return(0);
282 /* lookup a pointer record - search pointer hash table */
283 static struct ptr *
284 lookupptr(ptr)
285 mall_t ptr;
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) {
292 if(ptr == p->ptr)
293 return(p);
294 p = p->next;
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]
306 * use: 65599 nice.
307 * 65587 even better.
308 * author: oz@nexus.yorku.ca
310 static unsigned int
311 dbm_hash(str)
312 register char *str;
314 register unsigned int n = 0;
316 while(*str != '\0')
317 n = *str++ + 65599 * n;
318 return(n);
324 /* lookup a line/source entry by name (search hash table) */
325 static struct sym *
326 lookupsymbyname(nam,lin)
327 char *nam;
328 int lin;
330 register struct sym *s;
331 char *p = nam;
333 if(p == (char *)0)
334 p = "unknown";
336 s = map.shash[(dbm_hash(p) + lin) % HASHSIZ];
337 while(s != (struct sym *)0) {
338 if(!strcmp(s->labl,nam) && s->lineno == lin)
339 return(s);
340 s = s->next;
343 return((struct sym *)0);
349 /* lookup a line/source entry by number (exhaustively search hash table) */
350 static struct sym *
351 lookupsymbynum(num)
352 int num;
354 register struct sym *s;
355 register int x;
357 for(x = 0; x < HASHSIZ; x++) {
358 s = map.shash[x];
359 while(s != (struct sym *)0) {
360 if(s->mapno == num)
361 return(s);
362 s = s->next;
365 return((struct sym *)0);
370 /* stuff a pointer's value in the pointer map table */
371 static void
372 storeptr(ptr,siz,lab,lin)
373 mall_t ptr;
374 int siz;
375 char *lab;
376 int lin;
378 register struct ptr *p;
379 register struct sym *s;
380 int hv;
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;
391 return;
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.
399 if(lab != (char *)0)
400 s->labl = lab;
401 else
402 s->labl = "unknown";
404 s->mapno = map.lmap++;
406 /* add sym to hash table */
407 s->next = map.shash[hv = ((dbm_hash(s->labl) + lin) % HASHSIZ)];
408 map.shash[hv] = s;
410 s->lineno = lin;
411 s->mallcnt = 1;
412 s->avsiz = siz;
413 savesym(s);
414 } else {
415 /* found an already defined symbol. store some averages */
416 s->avsiz = ((s->avsiz * s->mallcnt) + siz) / (s->mallcnt + 1);
417 (s->mallcnt)++;
420 p = lookupptr(ptr);
421 if(p != (struct ptr *)0 && p->dsk.siz != 0) {
422 struct sym *x;
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 ");
428 psym(x);
430 pmsg("\n");
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;
439 return;
442 /* link it in */
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 */
448 p->ptr = ptr;
449 p->dsk.siz = siz;
450 p->dsk.smap = s->mapno;
451 p->map = map.pmap++;
453 /* store the size */
454 map.ninuse += siz;
456 saveptr(p);
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()
466 static int
467 freeptr(ptr,lab,lin)
468 mall_t ptr;
469 char *lab;
470 int lin;
472 register struct ptr *p;
474 p = lookupptr(ptr);
475 if(p == (struct ptr *)0) {
476 pmsg("pointer freed that was never allocated");
477 ploc(lab,lin,-1);
478 pmsg("\n");
479 return(1);
482 if(p != (struct ptr *)0 && p->dsk.siz == 0) {
483 struct sym *x;
485 pmsg("pointer re-freed when already free");
486 ploc(lab,lin,-1);
487 if((x = lookupsymbynum(p->dsk.smap)) != (struct sym *)0) {
488 pmsg(" last allocated:");
489 psym(x);
491 pmsg("\n");
492 return(1);
495 /* get some free */
496 map.ninuse -= p->dsk.siz;
498 /* write in the map that it is free */
499 p->dsk.siz = 0;
500 saveptr(p);
502 return(0);
508 /* pretend we are malloc() */
509 mall_t
510 mnem_malloc(siz,lab,lin)
511 unsigned siz;
512 char *lab;
513 int lin;
515 mall_t ret;
517 if(!(rec_state & REC_INITTED))
518 initmap();
520 if((ret = malloc(siz)) == (mall_t)0) {
521 pmsg("malloc returned null pointer at");
522 ploc(lab,lin,(int)siz);
523 pmsg("\n");
524 return(ret);
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);
531 map.nalloc++;
532 return(ret);
538 /* pretend we are calloc() */
539 mall_t
540 mnem_calloc(cnt,siz,lab,lin)
541 unsigned cnt;
542 unsigned siz;
543 char *lab;
544 int lin;
546 mall_t ret;
548 if(!(rec_state & REC_INITTED))
549 initmap();
551 if((ret = calloc(cnt,siz)) == (mall_t)0) {
552 pmsg("calloc returned null pointer at");
553 ploc(lab,lin,(int)(siz * cnt));
554 pmsg("\n");
555 return(ret);
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);
562 map.nalloc++;
563 return(ret);
569 /* pretend we are realloc() */
570 mall_t
571 mnem_realloc(ptr,siz,lab,lin)
572 mall_t ptr;
573 unsigned siz;
574 char *lab;
575 int lin;
577 mall_t ret;
579 if(!(rec_state & REC_INITTED))
580 initmap();
582 if((ret = realloc(ptr,siz)) == (mall_t)0) {
583 pmsg("realloc returned null pointer at");
584 ploc(lab,lin,(int)siz);
585 pmsg("\n");
586 return(ret);
589 if((rec_state & REC_ON) && !(rec_state & REC_ERR)) {
590 if(!freeptr(ptr,lab,lin))
591 storeptr(ret,(int)siz,lab,lin);
594 map.nrlloc++;
595 return(ret);
602 /* pretend we are free() */
603 void
604 mnem_free(ptr,lab,lin)
605 mall_t ptr;
606 char *lab;
607 int lin;
609 if(!(rec_state & REC_INITTED))
610 initmap();
612 if((rec_state & REC_ON) && !(rec_state & REC_ERR))
613 if(freeptr(ptr,lab,lin) == 0) {
614 (void)free(ptr);
615 map.nfree++;
616 } else
617 map.nbfree++;
623 /* dump everything we know about nothing in particular */
625 mnem_writestats()
627 register struct sym *s;
628 register int x;
630 if(map.fp == (FILE *)0)
631 return(-1);
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);
640 if(map.nbfree != 0L)
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++) {
661 s = map.shash[x];
662 while(s != (struct sym *)0) {
663 savesym(s);
664 s = s->next;
668 (void)fflush(map.fp);
669 return(0);