You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							670 lines
						
					
					
						
							14 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							670 lines
						
					
					
						
							14 KiB
						
					
					
				| /************************************************************************ | |
|  *									* | |
|  *			Copyright (c) 1985 by				* | |
|  *		Digital Equipment Corporation, Maynard, MA		* | |
|  *			All rights reserved.				* | |
|  *									* | |
|  *   The information in this software is subject to change  without	* | |
|  *   notice  and should not be construed as a commitment by Digital	* | |
|  *   Equipment Corporation.						* | |
|  *									* | |
|  *   Digital assumes no responsibility for the use  or  reliability	* | |
|  *   of its software on equipment which is not supplied by Digital.	* | |
|  *									* | |
|  *   Redistribution and use in source and binary forms are permitted	* | |
|  *   provided that the above copyright notice and this paragraph are	* | |
|  *   duplicated in all such forms and that any documentation,		* | |
|  *   advertising materials, and other materials related to such		* | |
|  *   distribution and use acknowledge that the software was developed	* | |
|  *   by Digital Equipment Corporation. The name of Digital Equipment	* | |
|  *   Corporation may not be used to endorse or promote products derived	* | |
|  *   from this software without specific prior written permission.	* | |
|  *   THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR	* | |
|  *   IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED	* | |
|  *   WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.* | |
|  *   Do not take internally. In case of accidental ingestion, contact	* | |
|  *   your physician immediately.					* | |
|  *									* | |
|  ************************************************************************/ | |
| 
 | |
| /* DO NOT INCLUDE "mnemosyne.h" !!! */ | |
| #include	<stdio.h> | |
| #include	<sys/types.h> | |
| #include	<sys/file.h> | |
|  | |
| /* shared stuff - and decl of struct ptr */ | |
| #include	"mnemconf.h" | |
|  | |
| static	char	rcsid[] = "/fats/tools/hsv/mnemosyne/mnemosyne.c,v 1.1.1.1 1995/06/06 18:18:28 fabio Exp"; | |
| 
 | |
| 
 | |
| /* | |
| malloc() realloc() and family wrappers - these functions manage a set | |
| of data files that are updated whenever a pointer is allocated or freed, | |
| as well as gathering stats about dynamic memory use and leakage. | |
|  | |
| 	Marcus J. Ranum, 1990. (mjr@decuac.dec.com) | |
| */ | |
| 
 | |
| 
 | |
| /* | |
| there is some egregious use of globals, void functions, and whatnot | |
| in this code. it is mostly due to the constraint that nothing must | |
| be visible to the outside, and that the code must be structurally | |
| simple. error checking is pitched out the window in spots, since an | |
| error in the mnemosyne code should not affect whatever is being | |
| instrumented if at all possible. this means no errors, no signals, | |
| nothing. (this message brought to you by my ego, in case you think | |
| I don't know how to write better code than this) :) | |
|  | |
| mjr, hacking on Christmas, 1990. | |
| */ | |
| 
 | |
| #define	REC_UNINIT	000 | |
| #define	REC_INITTED	001 | |
| #define	REC_ERR		002 | |
| #define	REC_ON		010 | |
| #define	REC_ONOFF	020 | |
| static	int	rec_state = REC_UNINIT; | |
| 
 | |
| /* | |
| this method of storing the symbol maps is not the most efficient, but | |
| then that's not important here, since all we're trying to do is find | |
| memory leaks. if you choose to improve the symbol management to use | |
| bucketed hash tables, please contact the author and the code will be | |
| updated :) - besides, since we do file I/O every time you malloc or | |
| free something, there's no way in hell this is going to set any new | |
| records for speed. | |
| */ | |
| 
 | |
| 
 | |
| /* storage for code/line # entry */ | |
| struct	sym	{ | |
| 	char		*labl; | |
| 	int		lineno; | |
| 	int		mapno; | |
| 	int		mallcnt; | |
| 	float		avsiz; | |
| 	struct	sym	*next; | |
| }; | |
| 
 | |
| 
 | |
| 
 | |
| /* static symbol map */ | |
| static	struct	{ | |
| 	FILE	*fp; | |
| 	FILE	*log; | |
| 	int	fd; | |
| 
 | |
| 	long	nalloc;		/* count of allocations */ | |
| 	long	nrlloc;		/* count of re-allocations */ | |
| 	long	nfree;		/* count of frees */ | |
| 	long	nbfree;		/* count of bad frees */ | |
| 	long	ninuse;		/* known allocated memory in use */ | |
| 	float	avgsiz;		/* average malloc size */ | |
| 
 | |
| 	/* one entry per pointer returned by malloc */ | |
| 	int	pmap;		/* current next ptr map to alloc */ | |
| 	struct	ptr	*phash[HASHSIZ]; | |
| 
 | |
| 	/* one entry per line of code that calls malloc/realloc, etc */ | |
| 	int	lmap;		/* current next line map to alloc */ | |
| 	struct	sym	*shash[HASHSIZ];	/* hash access */ | |
| } map; | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* print a locale record with checks for closed log file */ | |
| static	void | |
| ploc(lab,lin,siz) | |
| char	*lab; | |
| int	lin; | |
| int	siz; | |
| { | |
| 	if(map.log == (FILE *)0) | |
| 		return; | |
| 	if(lab != (char *)0) | |
| 		(void)fprintf(map.log," \"%s\"",lab); | |
| 	else | |
| 		(void)fprintf(map.log," unknown"); | |
| 	if(lin != -1) | |
| 		(void)fprintf(map.log," line:%d",lin); | |
| 	if(siz != -1) | |
| 		(void)fprintf(map.log," size:%d",siz); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* print a symbol map entry with checks for closed log file */ | |
| static	void | |
| psym(s) | |
| struct	sym	*s; | |
| { | |
| 	if(map.log == (FILE *)0) | |
| 		return; | |
| 	(void)fprintf(map.log," \"%s\"",s->labl); | |
| 	if(s->lineno != -1) | |
| 		(void)fprintf(map.log," line:%d",s->lineno); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* output a warning message with checks for closed log file */ | |
| static	void | |
| pmsg(s) | |
| char	*s; | |
| { | |
| 	if(map.log == (FILE *)0) | |
| 		return; | |
| 	(void)fprintf(map.log,"%s",s); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* save an entry to the .lines file */ | |
| static	void | |
| savesym(s) | |
| struct	sym	*s; | |
| { | |
| 	if(map.fp == (FILE *)0) | |
| 		return; | |
| 
 | |
| 	(void)fprintf(map.fp,"%d\t%d\t%.1f\t%d\t%s\n", | |
| 		s->mapno,s->mallcnt,s->avsiz,s->lineno,s->labl); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* save an entry in the pointer map file */ | |
| static	void | |
| saveptr(p) | |
| register struct	ptr	*p; | |
| { | |
| 	if(lseek(map.fd,(off_t)(p->map * sizeof(p->dsk)),0) != | |
| 		(off_t)(p->map * sizeof(p->dsk))) { | |
| 		pmsg("mnemosyne: cannot seek in pointer map file\n"); | |
| 		rec_state |= REC_ERR; | |
| 		return; | |
| 	} | |
| 
 | |
| 	if(write(map.fd,(char *)&(p->dsk),sizeof(p->dsk)) != sizeof(p->dsk)) { | |
| 		pmsg("mnemosyne: cannot write in pointer map file\n"); | |
| 		rec_state |= REC_ERR; | |
| 		return; | |
| 	} | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* initialize everything - symbol tables, files, and maps */ | |
| static	void | |
| initmap() | |
| { | |
| 	register int	xp; | |
| 
 | |
| 	if(rec_state & REC_INITTED) | |
| 		return; | |
| 
 | |
| 	if((map.fp = fopen(LINESFILE,"w")) == (FILE *)0) | |
| 		return; | |
| 	if((map.fd = open(PTRFILE,O_RDWR|O_CREAT|O_TRUNC,0600)) < 0) { | |
| 		(void)fclose(map.fp); | |
| 		return; | |
| 	} | |
| 
 | |
| 	map.log = stderr; | |
| 	map.lmap = map.pmap = 0; | |
| 	map.nalloc = map.nrlloc = map.nfree = map.nbfree = 0L; | |
| 	map.ninuse = 0L; | |
| 	map.avgsiz = 0.0; | |
| 
 | |
| 	for(xp = 0; xp < HASHSIZ; xp++) { | |
| 		map.phash[xp] = (struct ptr *)0; | |
| 		map.shash[xp] = (struct sym *)0; | |
| 	} | |
| 
 | |
| 	rec_state = REC_INITTED | REC_ON; | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* set logging to a FILE * */ | |
| void | |
| mnem_setlog(fp) | |
| FILE	*fp; | |
| { | |
| 	map.log = fp; | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* return state of the recorder */ | |
| int | |
| mnem_recording() | |
| { | |
| 	return((rec_state & REC_ON) && !(rec_state & REC_ERR)); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* turn on or off recording */ | |
| int | |
| mnem_setrecording(val) | |
| int	val; | |
| { | |
| 	if(!(rec_state & REC_INITTED)) | |
| 		initmap(); | |
| 
 | |
| 	if(val) | |
| 		rec_state |= REC_ON; | |
| 	else | |
| 		rec_state &= ~REC_ON; | |
| 
 | |
| 	if(map.fp != (FILE *)0) | |
| 		(void)fflush(map.fp); | |
| 
 | |
| 	rec_state |= REC_ONOFF; | |
| 	return(0); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* lookup a pointer record - search pointer hash table */ | |
| static struct	ptr	* | |
| lookupptr(ptr) | |
| mall_t	ptr; | |
| { | |
| 	register struct	ptr	*p; | |
| 
 | |
| 	/* this probably give simply terrible hash performance */ | |
| 	p = map.phash[(unsigned long)ptr % HASHSIZ]; | |
| 	while(p != (struct ptr *)0) { | |
| 		if(ptr == p->ptr) | |
| 			return(p); | |
| 		p = p->next; | |
| 	} | |
| 	return((struct ptr *)0); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* | |
|  * polynomial conversion ignoring overflows | |
|  * [this seems to work remarkably well, in fact better | |
|  * then the ndbm hash function. Replace at your own risk] | |
|  * use: 65599	nice. | |
|  *      65587   even better.  | |
|  * author: oz@nexus.yorku.ca | |
|  */ | |
| static unsigned int | |
| dbm_hash(str) | |
| register char *str; | |
| { | |
| 	register unsigned int n = 0; | |
| 
 | |
| 	while(*str != '\0') | |
| 		n = *str++ + 65599 * n; | |
| 	return(n); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* lookup a line/source entry by name (search hash table) */ | |
| static struct	sym	* | |
| lookupsymbyname(nam,lin) | |
| char	*nam; | |
| int	lin; | |
| { | |
| 	register struct sym	*s; | |
| 	char			*p = nam; | |
| 
 | |
| 	if(p == (char *)0) | |
| 		p = "unknown"; | |
| 
 | |
| 	s = map.shash[(dbm_hash(p) + lin) % HASHSIZ]; | |
| 	while(s != (struct sym *)0) { | |
| 		if(!strcmp(s->labl,nam) && s->lineno == lin) | |
| 			return(s); | |
| 		s = s->next; | |
| 	} | |
| 
 | |
| 	return((struct sym *)0); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* lookup a line/source entry by number (exhaustively search hash table) */ | |
| static struct	sym	* | |
| lookupsymbynum(num) | |
| int	num; | |
| { | |
| 	register struct sym	*s; | |
| 	register int		x; | |
| 
 | |
| 	for(x = 0; x < HASHSIZ; x++) { | |
| 		s = map.shash[x]; | |
| 		while(s != (struct sym *)0) { | |
| 			if(s->mapno == num) | |
| 				return(s); | |
| 			s = s->next; | |
| 		} | |
| 	} | |
| 	return((struct sym *)0); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| /* stuff a pointer's value in the pointer map table */ | |
| static	void | |
| storeptr(ptr,siz,lab,lin) | |
| mall_t	ptr; | |
| int	siz; | |
| char	*lab; | |
| int	lin; | |
| { | |
| 	register struct	ptr	*p; | |
| 	register struct	sym	*s; | |
| 	int			hv; | |
| 
 | |
| 	/* | |
| 	is there is no existing symbol entry for this line of code... | |
| 	we must needs make one - and painful it is... | |
| 	*/ | |
| 	if((s = lookupsymbyname(lab,lin)) == (struct sym *)0) { | |
| 		s = (struct sym *)malloc(sizeof(struct sym)); | |
| 		if(s == (struct sym *)0) { | |
| 			pmsg("mnemosyne: cannot allocate sym entry\n"); | |
| 			rec_state |= REC_ERR; | |
| 			return; | |
| 		} | |
| 
 | |
| 		/* | |
| 		this is funky - since we know the label is (?) | |
| 		compiled-in, we can just keep a pointer to it, | |
| 		rather than copying our own version of it. | |
| 		*/ | |
| 		if(lab != (char *)0) | |
| 			s->labl = lab; | |
| 		else | |
| 			s->labl = "unknown"; | |
| 
 | |
| 		s->mapno = map.lmap++; | |
| 
 | |
| 		/* add sym to hash table */ | |
| 		s->next = map.shash[hv = ((dbm_hash(s->labl) + lin) % HASHSIZ)]; | |
| 		map.shash[hv] = s; | |
|   | |
| 		s->lineno = lin; | |
| 		s->mallcnt = 1; | |
| 		s->avsiz = siz; | |
| 		savesym(s); | |
| 	} else { | |
| 		/* found an already defined symbol. store some averages */ | |
| 		s->avsiz = ((s->avsiz * s->mallcnt) + siz) / (s->mallcnt + 1); | |
| 		(s->mallcnt)++; | |
| 	} | |
| 
 | |
| 	p = lookupptr(ptr); | |
| 	if(p != (struct ptr *)0 && p->dsk.siz != 0) { | |
| 		struct	sym	*x; | |
| 
 | |
| 		pmsg("pointer re-allocated without being freed"); | |
| 		ploc(lab,lin,(int)siz); | |
| 		if((x = lookupsymbynum(p->dsk.smap)) != (struct sym *)0) { | |
| 			pmsg(" last allocated "); | |
| 			psym(x); | |
| 		} | |
| 		pmsg("\n"); | |
| 	} | |
| 
 | |
| 	/* heavy sigh. no entry for this pointer. make one. */ | |
| 	if(p == (struct ptr *)0) { | |
| 		p = (struct ptr *)malloc(sizeof(struct ptr)); | |
| 		if(p == (struct ptr *)0) { | |
| 			pmsg("mnemosyne: cannot expand pointer table\n"); | |
| 			rec_state |= REC_ERR; | |
| 			return; | |
| 		} | |
| 
 | |
| 		/* link it in */ | |
| 		p->next = map.phash[(unsigned long)ptr % HASHSIZ]; | |
| 		map.phash[(unsigned long)ptr % HASHSIZ] = p; | |
| 	} | |
| 
 | |
| 	/* if we get to here (hazoo! hazaa!) both 's' and 'p' are OK */ | |
| 	p->ptr = ptr; | |
| 	p->dsk.siz = siz; | |
| 	p->dsk.smap = s->mapno; | |
| 	p->map = map.pmap++; | |
| 
 | |
| 	/* store the size */ | |
| 	map.ninuse += siz; | |
| 
 | |
| 	saveptr(p); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* | |
| mark a pointer as now being free. note that a 1 is returned IF  | |
| the actual value should NOT be really passed to free() | |
| */ | |
| static	int | |
| freeptr(ptr,lab,lin) | |
| mall_t	ptr; | |
| char	*lab; | |
| int	lin; | |
| { | |
| 	register struct	ptr	*p; | |
| 
 | |
| 	p = lookupptr(ptr); | |
| 	if(p == (struct ptr *)0) { | |
| 		pmsg("pointer freed that was never allocated"); | |
| 		ploc(lab,lin,-1); | |
| 		pmsg("\n"); | |
| 		return(1); | |
| 	} | |
| 
 | |
| 	if(p != (struct ptr *)0 && p->dsk.siz == 0) { | |
| 		struct	sym	*x; | |
| 
 | |
| 		pmsg("pointer re-freed when already free"); | |
| 		ploc(lab,lin,-1); | |
| 		if((x = lookupsymbynum(p->dsk.smap)) != (struct sym *)0) { | |
| 			pmsg(" last allocated:"); | |
| 			psym(x); | |
| 		} | |
| 		pmsg("\n"); | |
| 		return(1); | |
| 	} | |
| 
 | |
| 	/* get some free */ | |
| 	map.ninuse -= p->dsk.siz; | |
| 
 | |
| 	/* write in the map that it is free */ | |
| 	p->dsk.siz = 0; | |
| 	saveptr(p); | |
| 
 | |
| 	return(0); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* pretend we are malloc() */ | |
| mall_t | |
| mnem_malloc(siz,lab,lin) | |
| unsigned	siz; | |
| char		*lab; | |
| int		lin; | |
| { | |
| 	mall_t ret; | |
| 
 | |
| 	if(!(rec_state & REC_INITTED)) | |
| 		initmap(); | |
| 
 | |
| 	if((ret = malloc(siz)) == (mall_t)0) { | |
| 		pmsg("malloc returned null pointer at"); | |
| 		ploc(lab,lin,(int)siz); | |
| 		pmsg("\n"); | |
| 		return(ret); | |
| 	} | |
| 
 | |
| 	if((rec_state & REC_ON) && !(rec_state & REC_ERR)) | |
| 		storeptr(ret,(int)siz,lab,lin); | |
| 
 | |
| 	map.avgsiz = ((map.avgsiz * map.nalloc) + siz) / (map.nalloc + 1); | |
| 	map.nalloc++; | |
| 	return(ret); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* pretend we are calloc() */ | |
| mall_t | |
| mnem_calloc(cnt,siz,lab,lin) | |
| unsigned	cnt; | |
| unsigned	siz; | |
| char		*lab; | |
| int		lin; | |
| { | |
| 	mall_t ret; | |
| 
 | |
| 	if(!(rec_state & REC_INITTED)) | |
| 		initmap(); | |
| 
 | |
| 	if((ret = calloc(cnt,siz)) == (mall_t)0) { | |
| 		pmsg("calloc returned null pointer at"); | |
| 		ploc(lab,lin,(int)(siz * cnt)); | |
| 		pmsg("\n"); | |
| 		return(ret); | |
| 	} | |
| 
 | |
| 	if((rec_state & REC_ON) && !(rec_state & REC_ERR)) | |
| 		storeptr(ret,(int)(cnt * siz),lab,lin); | |
| 
 | |
| 	map.avgsiz = ((map.avgsiz * map.nalloc) + siz) / (map.nalloc + 1); | |
| 	map.nalloc++; | |
| 	return(ret); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* pretend we are realloc() */ | |
| mall_t | |
| mnem_realloc(ptr,siz,lab,lin) | |
| mall_t		ptr; | |
| unsigned	siz; | |
| char		*lab; | |
| int		lin; | |
| { | |
| 	mall_t ret; | |
| 
 | |
| 	if(!(rec_state & REC_INITTED)) | |
| 		initmap(); | |
| 
 | |
| 	if((ret = realloc(ptr,siz)) == (mall_t)0) { | |
| 		pmsg("realloc returned null pointer at"); | |
| 		ploc(lab,lin,(int)siz); | |
| 		pmsg("\n"); | |
| 		return(ret); | |
| 	} | |
| 
 | |
| 	if((rec_state & REC_ON) && !(rec_state & REC_ERR)) { | |
| 		if(!freeptr(ptr,lab,lin)) | |
| 			storeptr(ret,(int)siz,lab,lin); | |
| 	} | |
| 
 | |
| 	map.nrlloc++; | |
| 	return(ret); | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* pretend we are free() */ | |
| void | |
| mnem_free(ptr,lab,lin) | |
| mall_t		ptr; | |
| char		*lab; | |
| int		lin; | |
| { | |
| 	if(!(rec_state & REC_INITTED)) | |
| 		initmap(); | |
| 
 | |
| 	if((rec_state & REC_ON) && !(rec_state & REC_ERR)) | |
| 		if(freeptr(ptr,lab,lin) == 0) { | |
| 			(void)free(ptr); | |
| 			map.nfree++; | |
| 		} else | |
| 			map.nbfree++; | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* dump everything we know about nothing in particular */ | |
| int | |
| mnem_writestats() | |
| { | |
| 	register struct sym	*s; | |
| 	register int		x; | |
| 
 | |
| 	if(map.fp == (FILE *)0) | |
| 		return(-1); | |
| 
 | |
| 	(void)fseek(map.fp,0L,0); | |
| 
 | |
| 	/* dump our life's story */ | |
| 	(void)fprintf(map.fp,"#total allocations:%ld\n",map.nalloc); | |
| 	(void)fprintf(map.fp,"#total re-allocations:%ld\n",map.nrlloc); | |
| 	(void)fprintf(map.fp,"#total frees:%ld\n",map.nfree); | |
| 
 | |
| 	if(map.nbfree != 0L) | |
| 		(void)fprintf(map.fp,"#bad/dup frees:%ld\n",map.nbfree); | |
| 
 | |
| 	(void)fprintf(map.fp,"#total allocated never freed:%ld\n",map.ninuse); | |
| 
 | |
| 	(void)fprintf(map.fp,"#average size of allocations:%.1f\n",map.avgsiz); | |
| 
 | |
| 	/* note if we detected an internal error */ | |
| 	if(rec_state & REC_ERR) | |
| 		(void)fprintf(map.fp, | |
| 			"#(figures likely inaccurate due to error)\n"); | |
| 
 | |
| 	/* note if the system was on all the time ? */ | |
| 	if(!(rec_state & REC_ON) || (rec_state & REC_ONOFF)) | |
| 		(void)fprintf(map.fp, | |
| 			"#(figures likely inaccurate as recording was off)\n"); | |
| 
 | |
| 	/* write the legend */ | |
| 	(void)fprintf(map.fp,"#map#\tcalls\tave\tline#\tfile\n"); | |
| 
 | |
| 	for(x = 0; x < HASHSIZ; x++) { | |
| 		s = map.shash[x]; | |
| 		while(s != (struct sym *)0) { | |
| 			savesym(s); | |
| 			s = s->next; | |
| 		} | |
| 	} | |
| 
 | |
| 	(void)fflush(map.fp); | |
| 	return(0); | |
| }
 |