Subject: malloc etc. on DEC Alpha

From: "Mr. Berkley Shands" <[email-masked]>
Date: Thu, 16 Mar 1995 13:21:25 -0600

It has been my observation that most of the malloc errors are array
overruns/underruns. If you have not used 64 bits machines before, start
by watching your pointers.

	here is a snippet of code to do memory management as suggested
	by Simon Kilvington, [email-masked].....

No warranty expressed, implied, desired, or flames needed.

/* con_memory.c malloc/free shells and common error reporting */

/*@ process system supplied include files @*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
/*@@*/

/*@ process local include files @*/

/* Some useful prototypes */
#define MODULE
#define PROTOTYPE
#define local static
#define FALSE 0

PROTOTYPE char *mem_alloc (int, int, char *);
PROTOTYPE void mem_free (void *, int, char *);
PROTOTYPE char *mem_realloc (void *, int, int, char *);

#define MEM_ALLOC(i) mem_alloc (i, __LINE__, __FILE__)
#define MEM_FREE(i) mem_free (i, __LINE__, __FILE__)
#define MEM_REALLOC(i,j) mem_realloc (i, j, __LINE__, __FILE__)
/*@@*/

/*@ define symbolic parameters for table and debugging arrays @*/
#ifndef SLOWLARIS
PROTOTYPE void bcopy(const char *, char *, int);
#endif

/* define a guard area */

#define PAD_MEM_BYTES 32

/* MAX_ALLOC is the greatest number of mallocs outstanding */

#define MAX_ALLOC 4096

/* PAT_ALLOC and PAT_FREE are memory pool checking values */

#define PAT_ALLOC -87097619	/* clear on allocate to facefeed */
#define PAT_FREE -559038737	/* destroy on release to deadbeef */

/* ******************************************************************* */
/*@@*/
/*@ module specific variable declarations @*/
typedef struct {
   char used_by[16];		/* id of caller */

   char *used_ptrs;		/* data address returned to user */
   char *used_ptrs2;		/* data address of starting block for real */

   int	used_ln;		/* line number in calling source code */
   int	used_size;		/* actual size of bytes in block */
   } mem_type;

local mem_type pmem[MAX_ALLOC + 1];		/* our storage for this program */

local int used_count = -1;
local int error_count = 0;
local int used_alloc;
local int used_free;	/* call sequence ids */
local int sts_enable = FALSE;			/* on to dump cute info */
local int dbg_enable = FALSE;			/* on to dump debug data */
/*@@*/

/* malloc() is defined in stdlib.h */

MODULE void dump_memtables (void)
   {
   /*@ # debugging - dump memory tables to screen @*/
   /*@ declare variables @*/
   int i, j, k;
   char *tp;			/* the returned value */
   int *tp2;
   /*@@*/

   /*@ Spew information on malloc failure @*/
   fprintf (stderr, "Internal table dump follows... (hope you are ready!)\n");
   fprintf (stderr, "Alloc calls %d, Free calls %d, Highest used %d\n", used_alloc, used_free, used_count);
   j = 0;
   k = used_count - 1024;
   if (k < 0)
      k = 0;
   /*@ display last 1024 allocation entries @*/
   for (i = k; i < used_count; i++)
      {
      if (pmem[i].used_size <= 0)
	 continue;
      fprintf (stderr, "Slot %d, size %d, by %s at %d ",
	 i, pmem[i].used_size, pmem[i].used_by, pmem[i].used_ln);
      j += pmem[i].used_size;
      tp = pmem[i].used_ptrs2;
      tp2 = (int *) tp;
      if (pmem[i].used_size < 1024 && (int) strlen (tp) < 80)
	 fprintf (stderr, "%s\n", tp);
      else
	 fprintf (stderr, "(%x %x %x %x)\n", tp2[0], tp2[1], tp2[2], tp2[3]);
      }
   /*@@*/
   fprintf (stderr, "Internal total used %d\n", j);
   /*@@*/
   /*@@*/
   }

MODULE char *mem_alloc (int size, int lineid, char *by_who)
   {
   /*@ # Allocate a block of storage (128 byte minimum)
	 input: The minimum size to allocate
	 output: min 128 bytes + 10% @*/
   /*@ declare variables @*/
   static int i;
   static int slot;
   static char *tp;			/* the returned value */
   static int *clr;
   /*@@*/

   /*@ house keep number of calls @*/
   used_alloc++;	/* count the calls to allocate */
   /*@@*/
   /*@ trap bogus allocate calls - convert to 128 byte requests @*/
   if (size <= 0)
      {
      fprintf (stderr, "Internal null request for memory %s %d %d %d\n", by_who, size, used_alloc, used_free);
      size = 128;
      }
   /*@@*/
   /*@ determine a slot to place request into @*/
   if (used_count < 0)
      {
      /*@ initialize array if in a virgin state @*/
      for (i = 0; i < MAX_ALLOC; i++)
	 {
	 pmem[i].used_by[0] = '\0';	/* null out who field */
	 pmem[i].used_ptrs = NULL;
	 pmem[i].used_ptrs2 = NULL;
	 pmem[i].used_size = 0;
	 pmem[i].used_ln = 0;	/* and line number */
	 }
      slot = 0;
      used_count = 0;	/* initialize */
      /*@@*/
      }
   else
      {
      /*@ find an empty slot @*/
      slot = -1;
      for (i = 0; i <= used_count; i++)
	 {
	 if (pmem[i].used_size <= 0)
	    {
	    slot = i;
	    break;
	    }
	 }
      if (slot < 0)
	 slot = ++used_count;
      /*@@*/
      }
   /*@@*/
   /*@ die if unable to allocate a slot for request @*/
   if (slot >= MAX_ALLOC)
      {
      fprintf (stderr, "Internal allocation past MAX_ALLOC %s %d %d %d\n", by_who, slot, 0, MAX_ALLOC);
      dump_memtables ();
      exit (-1);
      }
   /*@@*/
   /*@ force byte count to 128 min + 10% and a multiple of 8 @*/
   pmem[slot].used_size = size + (size / 10) + PAD_MEM_BYTES + PAD_MEM_BYTES;
   if (pmem[slot].used_size < 128)
      pmem[slot].used_size = 128;
   i = pmem[slot].used_size & 7;
   if (i != 0)
      pmem[slot].used_size += (8 - i);
   /*@@*/
   /*@ stash the id provided by the calling system @*/
   strncpy (pmem[slot].used_by, by_who, 15);
   pmem[slot].used_ln = lineid;
   /*@@*/

   /*@ ask the system for the space @*/
   tp = malloc (pmem[slot].used_size);
   pmem[slot].used_ptrs = tp;
   pmem[slot].used_ptrs2 = tp + PAD_MEM_BYTES;
   if (tp == NULL)
      {
      perror ("Memory allocation failure");
      fprintf (stderr, "Internal malloc failed for slot %d size %d by %s sequence %d\n",
	       slot, pmem[slot].used_size, by_who, used_alloc);
      dump_memtables ();
      exit (-1);
      }
   /*@@*/
   /*@ clear the array to a special poison pool value if debugging @*/
   if (dbg_enable && sts_enable)
      {
      clr = (int *) tp;
      for (i = 0; i < pmem[slot].used_size / 4; i++)
	 clr[i] = PAT_ALLOC;
      }
   /*@@*/
   /*@ force first two bytes to null to make dbx happy @*/
   tp[PAD_MEM_BYTES] = '\0';
   tp[PAD_MEM_BYTES + 1] = '\0';
   /*@@*/

   /*@ return an offset to this pool address to make under/overflows visible @*/
   return (pmem[slot].used_ptrs2);
   /*@@*/
   /*@@*/
   }

MODULE local int find_mem_slot (void *ptr, char *by_who, int lineid)
   {
   /*@ # Find a free or occupied memory slot @*/
   /*@ declare variables @*/
   static int i;
   static int slot;
   static long tpl, tph, tpt;
   /*@@*/

   /*@ initialize variables @*/
   slot = -1;
   /*@@*/

   /*@ attempt a fast lookup of address @*/
   for (i = 0; i <= used_count; i++)
      {
      if (ptr == pmem[i].used_ptrs2)
	 {
	 slot = i;
	 break;
	 }
      }
   /*@@*/
   /*@ fall back to offset pointer recover if not valid still @*/
   if (slot < 0)
      {
      tpt = (long) ptr;
      for (i = 0; i <= used_count; i++)
	 {
	 tpl = (long) pmem[i].used_ptrs;
	 tph = tpl + pmem[i].used_size;
	 if (tpt >= tpl && tpt <= tph)
	    {
	    slot = i;
	    fprintf (stderr, "Internal deallocating offset pointer in slot %d by %s at line %d\n", slot, by_who, lineid);
	    break;
	    }
	 }
      }
   /*@@*/
   /*@ abort on totally invalid pointer @*/
   if (slot < 0)
      {
      fprintf (stderr, "Internal handed illegal pointer %x by %s at line %d ", ptr, by_who, lineid);
      if (ptr != NULL && (int) strlen (ptr) < 80)
	 fprintf (stderr, "%s", ptr);
      fprintf (stderr, "\n");
      }
   /*@@*/

   /*@ return location of data @*/
   return (slot);
   /*@@*/
   /*@@*/
   }

MODULE void mem_free (void *ptr, int lineid, char *by_who)
   {
   /*@ # Release a block of memory @*/
   /*@ declare local variables @*/
   static int i;
   static int slot;
   static int *clr;
   /*@@*/

   /*@ house keep call counts @*/
   used_free++; 	/* account for the release */
   /*@@*/
   /*@ trap call before initialize @*/
   if (used_count < 0)
      {
      fprintf (stderr, "Internal releasing before allocating %x by %s\n", ptr, by_who);
      ptr = NULL;
      return;
      }
   /*@@*/

   /*@ map address to slot and abort if invalid @*/
   slot = find_mem_slot (ptr, by_who, lineid);
   if (slot < 0)
      {
      ptr = NULL;
      return;
      }
   /*@@*/
   /*@ if debugging then verify underrun and overrun status then clear data @*/
   if (dbg_enable && sts_enable)
      {
      clr = (int *) pmem[slot].used_ptrs;
      for (i = 0; i < PAD_MEM_BYTES / 4; i++)
	 {
	 /*@ check and report underflow errors @*/
	 if (clr[i] != PAT_ALLOC)
	    {
	    fprintf (stderr, "Internal array corruption low pointer %x by %s at %d from %s at %d\n",
	    ptr, by_who, lineid, pmem[slot].used_by, pmem[slot].used_ln);
	    fprintf (stderr, "i = %d clr[i] = %x\n",i, clr[i]);
	    break;
	    }
	 /*@@*/
	 }
      for (i = (pmem[slot].used_size - PAD_MEM_BYTES) / 4; i < pmem[slot].used_size / 4; i++)
	 {
	 /*@ check and report overflow errors @*/
	 if (clr[i] != PAT_ALLOC)
	    {
	    fprintf (stderr, "Internal array corruption high pointer %x by %s at %d from %s at %d\n",
	       ptr, by_who, lineid, pmem[slot].used_by, pmem[slot].used_ln);
	    fprintf (stderr, "i = %d clr[i] = %x\n",i, clr[i]);
	    break;
	    }
	 /*@@*/
	 }
      /*@ erase pool of memory @*/
      for (i = 0; i < pmem[slot].used_size / 4; i++)
	 clr[i] = PAT_FREE;
      /*@@*/
      }
   /*@@*/
   /*@ release storage and house keep the counters @*/
   free (pmem[slot].used_ptrs);
   ptr = NULL;
   pmem[slot].used_ptrs = NULL;
   pmem[slot].used_size = 0;
   while (pmem[used_count].used_ptrs == NULL && used_count > 0)
      used_count--;
   /*@@*/
   /*@@*/
   }

MODULE char *mem_realloc (void *ptr, int size, int lineid, char *by_who)
   {
   /*@ # expand or contract a given block of memory @*/
   /*@ declare variables @*/
   int slot;
   char *new_ptr;			/* memory pointer to return */
   /*@@*/

   /*@ null argument defaults to a staight mem_alloc @*/
   if (ptr == NULL)
      {
      return (MEM_ALLOC (size));
      }
   /*@@*/
   /*@ check and handle call before initialize @*/
   if (used_count < 0)
      {
      fprintf (stderr, "Internal realloc before allocating %x at %d by %s\n", ptr, lineid, by_who);
      return (MEM_ALLOC (size));
      }
   /*@@*/

   /*@ find current slot and abort on bad data @*/
   slot = find_mem_slot (ptr, by_who, lineid);
   if (slot < 0)
      {
      return (MEM_ALLOC (size));			/* bad boy pointers are ignored */
      }
   /*@@*/
   /*@ ignore shrinkage of space requests @*/
   if (size <= pmem[slot].used_size)
      {
      return (ptr);					/* no change */
      }
   /*@@*/
   /*@ allocate new storage and copy, then release old data @*/
   new_ptr = MEM_ALLOC (size);
#ifndef SLOWLARIS
   bcopy (ptr, new_ptr, pmem[slot].used_size);
#else
   memcpy (new_ptr, ptr, pmem[slot].used_size); 	/* sun has to be different */
#endif
   MEM_FREE (ptr);
   return (new_ptr);
   /*@@*/
   /*@@*/
   }