home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.sources.unix
- From: pefv700@hermes.chpc.utexas.edu (Christopher Phillips)
- Subject: v27i086: leak - quick and dirty code to find memory leaks, Patch01
- Sender: unix-sources-moderator@gw.home.vix.com
- Approved: vixie@gw.home.vix.com
-
- Submitted-By: pefv700@hermes.chpc.utexas.edu (Christopher Phillips)
- Posting-Number: Volume 27, Issue 86
- Archive-Name: leak/patch01
-
- This is patch01 for leak. It includes several changes:
-
- - A manual page
- - External names now start with leak_
- - Logging can be disabled with the external variable leak_logging
- - The #define's got pretty long when adding leak_logging, so they
- now just call new functions
- - Functions are available to clear and dump the memory database
- are included
- - The insertion function now uses the mode argument
- - The logging of reallocs now knows that size = 0 means to free
- - With GNU's dbm, int2file clobbers things such that the addresses
- printed are wrong -- thanks to Hal Peterson of Cray for reporting this
- - LDFLAGS and LIBS have been added to the Makefile for flexibility
-
- Just cd to the source directory and type "patch -p0 < THIS_FILE".
-
- Chris
-
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of shell archive."
- # Contents: patch01
- # Wrapped by pefv700@hermes on Wed Nov 10 14:07:23 1993
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'patch01' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'patch01'\"
- else
- echo shar: Extracting \"'patch01'\" \(14525 characters\)
- sed "s/^X//" >'patch01' <<'END_OF_FILE'
- X*** ../leak.old/Makefile Wed Nov 10 13:55:52 1993
- X--- Makefile Wed Nov 10 13:34:12 1993
- X***************
- X*** 1,10 ****
- X all: test leaktest leakdump
- X
- X leaktest: leaktest.o leak.o
- X! $(CC) -o $@ leaktest.o leak.o
- X
- X leakdump: leakdump.o leak.o
- X! $(CC) -o $@ leakdump.o leak.o
- X
- X test: leaktest leakdump
- X @echo Running leaktest
- X--- 1,12 ----
- X all: test leaktest leakdump
- X
- X leaktest: leaktest.o leak.o
- X! $(CC) $(LDFLAGS) -o $@ leaktest.o leak.o $(LIBS)
- X
- X leakdump: leakdump.o leak.o
- X! $(CC) $(LDFLAGS) -o $@ leakdump.o leak.o $(LIBS)
- X!
- X! leak.o leaktest.o leakdump.o: leak.h
- X
- X test: leaktest leakdump
- X @echo Running leaktest
- X*** ../leak.old/README Wed Nov 10 13:55:52 1993
- X--- README Wed Nov 10 13:33:52 1993
- X***************
- X*** 1,25 ****
- X-
- X LEAK - Quick and dirty code to find memory leaks
- X- (Requires ANSI C and ndbm)
- X
- X! What leak does:
- X
- X! * Logs all malloc/realloc/free calls to dbm files
- X! with filename and line number
- X! * Complains about reallocing/freeing addresses not
- X! the result of a prior malloc/realloc
- X! * Dumps unfreed addresses upon request
- X
- X- How to use leak:
- X-
- X- * #include "leak.h" in all source files that call
- X- malloc/realloc/free
- X- * recompile
- X- * run program
- X- * run leakdump
- X-
- X- That's all there is to it. For example:
- X-
- X % make
- X cc -c leaktest.c -o leaktest.o
- X cc -c leak.c -o leak.o
- X--- 1,12 ----
- X LEAK - Quick and dirty code to find memory leaks
- X
- X! You shouldn't have any problems compiling and using leak as long
- X! as you have an ANSI C compiler and the ndbm library. Just type
- X! ``make'' to compile leak and run a test program. To install leak,
- X! just put leak.h and leak.o in appropriate places.
- X
- X! Here's what I get when making leak:
- X
- X % make
- X cc -c leaktest.c -o leaktest.o
- X cc -c leak.c -o leak.o
- X***************
- X*** 31,53 ****
- X Nonfreed blocks:
- X Address Size Line File
- X ------------------------------------
- X! 140004110 40 16 leaktest.c
- X %
- X-
- X- Notes:
- X- * The dbm files are not automatically deleted
- X- * When using libraries that use malloc/realloc/free
- X- but for which you don't have source, don't have leak
- X- log the use of this memory. For example, if libMRF.a
- X- mallocs memory that you should free, don't have leak
- X- log the free or it will complain.
- X- * If you have library functions whose main function
- X- is to call malloc/realloc, it is usually more relevant
- X- to know which function called the library function.
- X- Unfortunately, determining that information is
- X- platform-specific.
- X- * All malloced/realloced memory should be explicitly
- X- freed or it will show up in the dump.
- X
- X
- X Chris
- X--- 18,25 ----
- X Nonfreed blocks:
- X Address Size Line File
- X ------------------------------------
- X! 140006010 3 24 leaktest.c
- X %
- X
- X
- X Chris
- X*** /dev/null Wed Nov 10 13:56:29 1993
- X--- leak.3 Wed Nov 10 13:33:52 1993
- X***************
- X*** 0 ****
- X--- 1,112 ----
- X+ .\" leak.3
- X+ .TH LEAK 3 "October 25, 1993"
- X+ .SH NAME
- X+ leak_logging, leak_clear, leak_dump \- memory leak detection
- X+ .SH SYNOPSIS
- X+ .LP
- X+ .nf
- X+ .ft B
- X+ #include "leak.h"
- X+ .ft
- X+ .fi
- X+ .LP
- X+ .nf
- X+ .ft B
- X+ extern int leak_logging;
- X+ .ft
- X+ .fi
- X+ .LP
- X+ .nf
- X+ .ft B
- X+ extern void leak_clear(void);
- X+ .ft
- X+ .fi
- X+ .LP
- X+ .nf
- X+ .ft B
- X+ extern void leak_dump(void);
- X+ .ft
- X+ .fi
- X+ .SH DESCRIPTION
- X+ .LP
- X+ This package provides a ``quick-and-dirty'' method for detecting
- X+ memory leaks in ANSI C source files.
- X+ Calls to the dynamic memory allocation functions
- X+ .BR malloc(\|) ,
- X+ .BR realloc(\|) ,
- X+ .BR calloc(\|) ,
- X+ and
- X+ .BR free(\|)
- X+ are logged using the
- X+ .BR ndbm (3)
- X+ database library.
- X+ .LP
- X+ To use the package, source files which contain dynamic memory
- X+ allocation routine calls should
- X+ .ft B
- X+ #include "leak.h"
- X+ .ft
- X+ and be recompiled.
- X+ The object file
- X+ .B leak.o
- X+ must be linked into the executable program.
- X+ .LP
- X+ Two
- X+ .B ndbm
- X+ databases are use by
- X+ .BR leak .
- X+ The first manages filenames and uses the prefix
- X+ .IR filedbm
- X+ while the second stores memory addresses and sizes and uses the prefix
- X+ .IR memdbm .
- X+ The databases are created if necessary and the memory address database
- X+ is cleared when the first logging occurs.
- X+ .LP
- X+ Memory addresses in the database can be printed to standard output
- X+ using
- X+ .BR leak_dump(\|)
- X+ while the database can be cleared using
- X+ .BR leak_clear(\|) .
- X+ The external variable
- X+ .IR leak_logging
- X+ can be set to zero to disable logging and nonzero to enable logging.
- X+ Logging is enabled upon program invocation.
- X+ .SH NOTES
- X+ .LP
- X+ The
- X+ .BR #define
- X+ facility of ANSI C is used to ``trap'' calls to
- X+ memory allocation functions.
- X+ Therefore, when using libraries that manage dynamic memory
- X+ but for which you don't have source, don't have
- X+ .B leak
- X+ log the use of this memory.
- X+ For example, if a library function returns dynamic
- X+ memory that you should free, don't log the call to
- X+ .BR free (\|)
- X+ or
- X+ .B leak
- X+ will complain.
- X+ Also, if you have a library function whose main purpose
- X+ is to manage dynamic memory, it is usually more
- X+ relevant to know which function called the library
- X+ function. Unfortunately, determining that information
- X+ is platform-specific.
- X+ .LP
- X+ All dynamically allocated memory that is logged should be
- X+ explicitly freed or it will show up in the dump.
- X+ .SH SEE ALSO
- X+ .BR calloc (3),
- X+ .BR free (3),
- X+ .BR malloc (3),
- X+ .BR realloc (3).
- X+ .SH DIAGNOSTICS
- X+ .LP
- X+ .B leak
- X+ will complain to standard error if an attempt is made to free an
- X+ address that is not present in the memory address database.
- X+ .SH AUTHOR
- X+ .LP
- X+ .nf
- X+ Christopher G. Phillips
- X+ pefv700@utpe.pe.utexas.edu
- X*** ../leak.old/leak.c Wed Nov 10 13:55:52 1993
- X--- leak.c Wed Nov 10 13:34:12 1993
- X***************
- X*** 22,33 ****
- X #include <string.h>
- X #include <unistd.h>
- X #include <fcntl.h>
- X #include "leak.h"
- X
- X #define FILE_DBMFILENAME "filedbm"
- X #define MEMORY_DBMFILENAME "memdbm"
- X
- X! void *_ptr;
- X
- X typedef char *DPTR;
- X
- X--- 22,39 ----
- X #include <string.h>
- X #include <unistd.h>
- X #include <fcntl.h>
- X+ #include <ndbm.h>
- X #include "leak.h"
- X
- X+ #undef malloc
- X+ #undef realloc
- X+ #undef free
- X+ #undef calloc
- X+
- X #define FILE_DBMFILENAME "filedbm"
- X #define MEMORY_DBMFILENAME "memdbm"
- X
- X! int leak_logging = 1;
- X
- X typedef char *DPTR;
- X
- X***************
- X*** 34,39 ****
- X--- 40,46 ----
- X static DBM *filedbm = NULL;
- X static DBM *memdbm = NULL;
- X static int nextfile = -1;
- X+ static int dbms_zapped = 0;
- X
- X struct memdata {
- X size_t size;
- X***************
- X*** 42,52 ****
- X };
- X
- X static void
- X! opendbmfiles(mode_t mode)
- X {
- X! if (filedbm == NULL && (filedbm = dbm_open(FILE_DBMFILENAME, mode,
- X! S_IRUSR | S_IWUSR)) == NULL
- X! || memdbm == NULL && (memdbm = dbm_open(MEMORY_DBMFILENAME, mode,
- X S_IRUSR | S_IWUSR)) == NULL) {
- X perror("dbm_open");
- X exit(1);
- X--- 49,57 ----
- X };
- X
- X static void
- X! opendbmfile(DBM **dbmp, /*const*/ char *file)
- X {
- X! if (*dbmp == NULL && (*dbmp = dbm_open(file, O_RDWR | O_CREAT,
- X S_IRUSR | S_IWUSR)) == NULL) {
- X perror("dbm_open");
- X exit(1);
- X***************
- X*** 98,111 ****
- X return "???";
- X }
- X
- X void
- X! _dbinsert(void *p, size_t size, const char *file, int line, int mode)
- X {
- X struct memdata md;
- X datum key, data;
- X
- X! if (!filedbm || !memdbm)
- X! opendbmfiles(O_RDWR | O_CREAT | O_TRUNC);
- X
- X md.file = file2int(file);
- X
- X--- 103,144 ----
- X return "???";
- X }
- X
- X+ static void
- X+ leak_fclear(void)
- X+ {
- X+ if (filedbm)
- X+ (void)dbm_close(filedbm);
- X+ (void)remove(FILE_DBMFILENAME ".dir");
- X+ (void)remove(FILE_DBMFILENAME ".pag");
- X+ filedbm = NULL;
- X+ }
- X+
- X void
- X! leak_clear(void)
- X {
- X+ if (memdbm)
- X+ (void)dbm_close(memdbm);
- X+ (void)remove(MEMORY_DBMFILENAME ".dir");
- X+ (void)remove(MEMORY_DBMFILENAME ".pag");
- X+ memdbm = NULL;
- X+ }
- X+
- X+ void
- X+ leak_insert(const void *p, size_t size, const char *file, int line, int mode)
- X+ {
- X struct memdata md;
- X datum key, data;
- X
- X! if (!dbms_zapped) {
- X! leak_clear();
- X! leak_fclear();
- X! dbms_zapped = 1;
- X! }
- X!
- X! if (!filedbm)
- X! opendbmfile(&filedbm, FILE_DBMFILENAME);
- X! if (!memdbm)
- X! opendbmfile(&memdbm, MEMORY_DBMFILENAME);
- X
- X md.file = file2int(file);
- X
- X***************
- X*** 116,122 ****
- X data.dptr = (DPTR)&md;
- X data.dsize = sizeof md;
- X
- X! if (dbm_store(memdbm, key, data, DBM_INSERT) == -1) {
- X perror("dbm_store");
- X exit(1);
- X }
- X--- 149,155 ----
- X data.dptr = (DPTR)&md;
- X data.dsize = sizeof md;
- X
- X! if (dbm_store(memdbm, key, data, mode) == -1) {
- X perror("dbm_store");
- X exit(1);
- X }
- X***************
- X*** 123,157 ****
- X }
- X
- X void
- X! _dbdelete(void *p, const char *file, int line)
- X {
- X struct memdata md;
- X datum key, data;
- X
- X key.dptr = (DPTR)&p;
- X key.dsize = sizeof p;
- X
- X! if (memdbm == NULL) {
- X! fprintf(stderr, "free before malloc from \"%s\", line %d\n",
- X! file, line);
- X! exit(1);
- X! } else if (dbm_delete(memdbm, key) == -1) {
- X fprintf(stderr,
- X "free unmalloced pointer %p from \"%s\", line %d\n", p, file,
- X line);
- X- exit(1);
- X }
- X }
- X
- X void
- X! _dbdump(mode_t mode)
- X {
- X! struct memdata *mdp;
- X datum key, data;
- X int do_title = 1;
- X
- X! if (!filedbm || !memdbm)
- X! opendbmfiles(mode);
- X
- X for (key = dbm_firstkey(memdbm); key.dptr; key = dbm_nextkey(memdbm)) {
- X data = dbm_fetch(memdbm, key);
- X--- 156,196 ----
- X }
- X
- X void
- X! leak_delete(const void *p, const char *file, int line)
- X {
- X struct memdata md;
- X datum key, data;
- X
- X+ if (!dbms_zapped) {
- X+ leak_clear();
- X+ leak_fclear();
- X+ dbms_zapped = 1;
- X+ }
- X+
- X key.dptr = (DPTR)&p;
- X key.dsize = sizeof p;
- X
- X! if (!memdbm)
- X! opendbmfile(&memdbm, MEMORY_DBMFILENAME);
- X! if (dbm_delete(memdbm, key) == -1) {
- X fprintf(stderr,
- X "free unmalloced pointer %p from \"%s\", line %d\n", p, file,
- X line);
- X }
- X }
- X
- X void
- X! leak_dump(void)
- X {
- X! struct memdata md;
- X datum key, data;
- X+ void *addr;
- X int do_title = 1;
- X
- X! if (!filedbm)
- X! opendbmfile(&filedbm, FILE_DBMFILENAME);
- X! if (!memdbm)
- X! opendbmfile(&memdbm, MEMORY_DBMFILENAME);
- X
- X for (key = dbm_firstkey(memdbm); key.dptr; key = dbm_nextkey(memdbm)) {
- X data = dbm_fetch(memdbm, key);
- X***************
- X*** 169,196 ****
- X putchar('\n');
- X do_title = 0;
- X }
- X! mdp = (struct memdata *)data.dptr;
- X #ifndef sun
- X! printf("%08p\t%d\t%d\t%s\n", *(void **)key.dptr,
- X #else
- X! printf("%08x\t%d\t%d\t%s\n", (int)*(void **)key.dptr,
- X #endif
- X! mdp->size, mdp->line, int2file(mdp->file));
- X }
- X }
- X
- X! #if 0
- X void
- X! exit(int status)
- X {
- X! _dbdump(O_RDONLY);
- X! (void)dbm_close(memdbm);
- X! (void)dbm_close(filedbm);
- X! (void)remove("memdbm.dir");
- X! (void)remove("memdbm.pag");
- X! (void)remove("filedbm.dir");
- X! (void)remove("filedbm.pag");
- X! fflush(NULL);
- X! _exit(status);
- X }
- X! #endif
- X--- 208,279 ----
- X putchar('\n');
- X do_title = 0;
- X }
- X! /*
- X! * Copy the key and data
- X! * because they may be overwritten in int2file.
- X! */
- X! md = *(struct memdata *)data.dptr;
- X! addr = *(void **)key.dptr;
- X #ifndef sun
- X! printf("%08p\t%d\t%d\t%s\n", addr,
- X #else
- X! printf("%08x\t%d\t%d\t%s\n", (int)addr,
- X #endif
- X! md.size, md.line, int2file(md.file));
- X }
- X }
- X
- X! void *
- X! leak_malloc(size_t size, const char *file, int line)
- X! {
- X! void *ptr = malloc(size);
- X!
- X! if (leak_logging && ptr)
- X! leak_insert(ptr, size, file, line, DBM_INSERT);
- X!
- X! return ptr;
- X! }
- X!
- X! void *
- X! leak_realloc(void *oldptr, size_t size, const char *file, int line)
- X! {
- X! void *newptr = realloc(oldptr, size);
- X!
- X! if (leak_logging) {
- X! if (!size && oldptr)
- X! leak_delete(oldptr, file, line);
- X! else if (newptr) {
- X! if (newptr == oldptr)
- X! leak_insert(newptr, size, file, line,
- X! DBM_REPLACE);
- X! else {
- X! if (oldptr)
- X! leak_delete(oldptr, file, line);
- X! leak_insert(newptr, size, file, line,
- X! DBM_INSERT);
- X! }
- X! }
- X! }
- X!
- X! return newptr;
- X! }
- X!
- X void
- X! leak_free(void *ptr, const char *file, int line)
- X {
- X! free(ptr);
- X!
- X! if (leak_logging && ptr)
- X! leak_delete(ptr, file, line);
- X }
- X!
- X! void *
- X! leak_calloc(size_t s, size_t t, const char *file, int line)
- X! {
- X! void *ptr = calloc(s, t);
- X!
- X! if (leak_logging && ptr)
- X! leak_insert(ptr, s * t, file, line, DBM_INSERT);
- X!
- X! return ptr;
- X! }
- X*** ../leak.old/leak.h Wed Nov 10 13:55:52 1993
- X--- leak.h Wed Nov 10 13:33:53 1993
- X***************
- X*** 19,43 ****
- X #define H_LEAK
- X
- X #include <sys/types.h>
- X- #include <stdlib.h>
- X- #include <ndbm.h>
- X
- X! extern void *_ptr;
- X! extern void _dbinsert(void *, size_t, const char *, int, int);
- X! extern void _dbdelete(void *, const char *, int);
- X
- X! #define malloc(s) \
- X! (_ptr = malloc(s), _dbinsert(_ptr, s, __FILE__, __LINE__, DBM_INSERT), _ptr)
- X
- X! #define realloc(p, s) \
- X! ((_ptr = realloc(p, s)), \
- X! ((_ptr && _ptr == p) \
- X! ? _dbinsert(_ptr, s, __FILE__, __LINE__, DBM_REPLACE), 0 : 0), \
- X! (_ptr ? \
- X! (p ? _dbdelete(p, __FILE__, __LINE__), 0 : 0), \
- X! _dbinsert(_ptr, s, __FILE__, __LINE__, DBM_INSERT), 0 : 0), \
- X! _ptr)
- X!
- X! #define free(p) (p ? _dbdelete(p, __FILE__, __LINE__), 0 : 0), free(p)
- X
- X #endif /* H_LEAK */
- X--- 19,37 ----
- X #define H_LEAK
- X
- X #include <sys/types.h>
- X
- X! extern int leak_logging;
- X! extern void leak_dump(void);
- X! extern void leak_clear(void);
- X
- X! extern void *leak_malloc(size_t, const char *, int);
- X! extern void *leak_realloc(void *, size_t, const char *, int);
- X! extern void leak_free(void *, const char *, int);
- X! extern void *leak_calloc(size_t, size_t, const char *, int);
- X
- X! #define malloc(s) leak_malloc(s, __FILE__, __LINE__)
- X! #define realloc(p, s) leak_realloc(p, s, __FILE__, __LINE__)
- X! #define free(p) leak_free(p, __FILE__, __LINE__)
- X! #define calloc(s, t) leak_calloc(s, t, __FILE__, __LINE__)
- X
- X #endif /* H_LEAK */
- X*** ../leak.old/leakdump.c Wed Nov 10 13:55:53 1993
- X--- leakdump.c Wed Nov 10 13:33:54 1993
- X***************
- X*** 17,29 ****
- X
- X #include <stdio.h>
- X #include <stdlib.h>
- X- #include <fcntl.h>
- X #include "leak.h"
- X
- X int
- X main(void)
- X {
- X! _dbdump(O_RDONLY);
- X
- X exit(0);
- X }
- X--- 17,28 ----
- X
- X #include <stdio.h>
- X #include <stdlib.h>
- X #include "leak.h"
- X
- X int
- X main(void)
- X {
- X! leak_dump();
- X
- X exit(0);
- X }
- X*** ../leak.old/leaktest.c Wed Nov 10 13:55:53 1993
- X--- leaktest.c Wed Nov 10 13:33:55 1993
- X***************
- X*** 14,20 ****
- X free(p);
- X else
- X free(q);
- X! p = malloc(40);
- X
- X exit(0);
- X }
- X--- 14,27 ----
- X free(p);
- X else
- X free(q);
- X!
- X! leak_logging = 0;
- X! p = malloc(100);
- X! leak_logging = 1;
- X!
- X! p = calloc(40, sizeof(int));
- X! q = realloc(p, 0);
- X! p = malloc(3);
- X
- X exit(0);
- X }
- END_OF_FILE
- if test 14525 -ne `wc -c <'patch01'`; then
- echo shar: \"'patch01'\" unpacked with wrong size!
- fi
- # end of 'patch01'
- fi
- echo shar: End of shell archive.
- exit 0
-