From: nelson@desktop.nsc.com (Taed Nelson)
Newsgroups: comp.lang.c
Subject: Re: Opinions about Purify
Date: 11 Sep 92 19:38:08 GMT
Organization: Applications Technology, National Semiconductor (Santa Clara, CA)

In article <88568@netnews.upenn.edu>, freeman@gradient.cis.upenn.edu (Jon Freeman) writes:
> I would like to know what the people on this newsgroup think about
> Purify, a product for finding run-time memory errors in C (and C++)
> programs.  I attended a C++ seminar taught by Lippman, the author of
> _C++ Primer_, and he said it was invaluable when he and his colleagues
> were writing the AT&T C++ compiler--a pretty strong endorsement, I'd
> say.  Purify seems a bit expensive to me--do other people think it's
> really worth the money?  (I don't know if it's considered appropriate
> to state exactly how much it costs, so I won't.)  Are there any other
> programs out there with similar functionality that cost, say, no more
> than $1000 for a single-CPU license?

[Allegedly, there is an article in this month's IEEE Software on Purify...]

Is Purify worth the money?  Well, we have 2 licenses for ~20 programmers and
  that works out fine.  For just one or two programmers (ie, a small company),
  it may or may not be worth it...

The following module gives about 25% of the functionality of Purify (it checks
  array bounds, use of free'd memory, and just about nothing else), at 10x the
  slow-down, and is infinitely cheaper.

Personally, I use Purify nearly every day, and think it's wonderful, but I use
  the following in my automatic QA since there is no way for Purify to pass
  back a value to a Makefile at present.  I also used it religiously prior to
  four months ago when we got Purify.

Note that this is _not_ a standard Malloc debugger -- it does its checking by
  mapping and unmapping virtual memory...

Also note that this will probably run on any UNIX system, whereas Purify is
  only for the Sun4 and either the cc or gcc compilers...

------------------------------------------------------------------------------

/*****************************************************************************
       Utility Software for Checking Misuse of allocated memory on UNIX

		Written by Andy Valencia (vandys@sequent.com)
	       Written by Taed Nelson (nelson@berlioz.nsc.com)
 ****************************************************************************/


/* Note that this CANNOT run on DOS since it does not have virtual memory */

/* This module will detect
   -- Use of allocated memory outside its range
   -- Use of memory that was freed
   -- General misuse of memory
*/

/* Define MUNMAP on some non-Sun systems */

#ifndef MUNMAP
#include <sys/mman.h>
#endif

#include <sys/types.h>
#include <memory.h>
#include <errno.h>
#include <stdio.h>
	
/*
 * A brute-force malloc() interface which attempts to back up each
 * allocated piece of data against an invalid page, so that references
 * off the end cause a segmentation violation.
 */
extern caddr_t sbrk();
static caddr_t sbrkCheck();
static unsigned int sizeAllocated (const void *pointer);

static unsigned long pagesize;

static unsigned long numMallocs = 0;
static unsigned long numFrees = 0;
static unsigned long memMalloced = 0;
static unsigned long memFreed = 0;
static unsigned long memVirtual = 0;


char *malloc(size)
	unsigned int size;
{
	static int setup = 0;
	unsigned long l;
	char *p;


	numMallocs++;
	memMalloced += size;

	/*
	 * Adjust up so we can slip a "size" word up front.  Also pad by an
	 * extra longword because the C library expects strings to not fall
	 * into an invalid page within one word from their end.
	 */
	size += sizeof(unsigned) + sizeof(long);

	/*
	 * Round size to longword boundary
	 */
	if (size & (sizeof(long)-1)) {
		size += (sizeof(long) - (size & (sizeof(long)-1)));
	}

	/*
	 * Get some overhead over with once
	 */
	if (!setup) {
		pagesize = getpagesize();
		setup = 1;
	}

	/*
	 * Make sure break is on a page boundry
	 */
	l = (unsigned long) sbrkCheck(0);
	l &= (pagesize-1);
	l = pagesize - l;
	(void) sbrkCheck ((int) l);

	/*
	 * Now allocate one page beyond size needed to hold data
	 */
	l = (size + (pagesize-1)) & ~(pagesize-1);
	l = pagesize * (l/pagesize + 1);
	p = sbrkCheck ((int) l);

	/*
	 * Out of memory--foo
	 */
	if (p == 0)
		return(p);

	/*
	 * Unvirtualize last page, return pointer to data backed
	 * up against top of remaining memory.
	 */
	p += l;
#ifdef MUNMAP
	(void) munmap (p - pagesize, pagesize);
#else
	(void) mprotect (p - pagesize, pagesize, PROT_NONE);
#endif

	/*
	 * Adjust data to back up against the end of the memory
	 */
	p -= (pagesize + size);
	*(unsigned *)p = size;
	
	return (p + sizeof(unsigned));
}

/*
 * Free memory.  Unvirtualize and thus catch stale-memory cheaters.
 */
void
free(ptr)
	char *ptr;
{
	unsigned long l;
	int npages;

	if (ptr == NULL)
		return;

	numFrees++;

	/*
	 * Figure out how big a chunk we're done with
	 */
	ptr -= sizeof(unsigned);
	l = *(unsigned *)ptr;

	memFreed += l;

	l = (l + (pagesize-1)) & ~(pagesize-1);
	npages = l/pagesize;

	/*
	 * Get base of memory, unmap it
	 */
	l = (unsigned long)ptr;
	l &= ~(pagesize-1);
#ifdef MUNMAP
	(void) munmap(l, npages*pagesize);
#else
	(void) mprotect (l, npages*pagesize, PROT_NONE);
#endif
}

char *
realloc(ptr, size)
	char *ptr;
	unsigned size;
{
	register char *p;
	unsigned osize;

	if ((p = malloc(size)) == 0)
		return(p);
	osize = (*((unsigned *)(ptr - sizeof(unsigned)))) -
		(sizeof (unsigned) + sizeof (long));
	memcpy(p, ptr, ((osize <= size) ? osize : size));
	free(ptr);
	return(p);
}


char * calloc (unsigned int nelem, unsigned int elsize)
{
	unsigned int size;
	register char *p;

	size = nelem * elsize;

	if ((p = malloc (size)) != 0) {
		memset (p, '\0', size);
	}

	return (p);
}


static caddr_t sbrkCheck (size)
	int size;
{
	caddr_t ptr;

	memVirtual += size;

	ptr = sbrk (size);
	
	/* crash on an error */
	if ((ptr == (caddr_t) -1) || (ptr == NULL)) {
		*ptr = '\0';
		exit (1);
	}

	return (ptr);
}

	
static unsigned int sizeAllocated (const void *pointer)
{
	return (*((unsigned int *)(((char *) pointer) - sizeof (unsigned))));
}