From: abuhr@owlnet.rice.edu (Aaron M. Buhr)
Subject: Re: Help me track down memory leakage
Organization: Rice University
Date: Mon, 23 Nov 1992 16:24:43 GMT


      Thanks to all who responded to my earlier post about helping me
track down the memory leak.  Most of the respondents said to either
acquire a commercial product such as Purify, use one of the freely
available routines, or make my own.  Since the Purify product is
priced in the thousands of dollars it's out of my price range.  The
only standard free memory allocation debugging product seemed to be
the malloc_debug routines available on some Suns.  I decided to make
my own routines, both so that I could customize it to fit my uses and
because I thought I might be able to create my own faster than I could
track down, learn, and integrate someone else's routine.

     As it turns out, I am having a strange error that I cannot figure
out.  The dbg_malloc and dbg_free routines seem to be working fine, in
that they return pointers to valid-sized memory blocks and free those
memory blocks, and it seems that those blocks can be manipulated
directly or by some library calls (strcpy for example).  However, for
some reason sscanf() returns a bus error when I try to use it on one
of the blocks the dbg_malloc() routines returned (the sscanf() works
fine with the regular C library malloc() routine).  Here's the gdb
output:

/* error: */
Program received signal 10, Bus error
0xf773cb80 in _doscan ()

/* I made a breakpoint before the fatal call below, and single stepped
up to it: */
484                                             CREATE(index, struct index_data,
 numentries);   /* this calls dbg_calloc(index, sizeof(struct */
                /* index_data) * numentries) */
(gdb)
497                                     sscanf(buf, "#%d", &index[i].virtual);
(gdb) p index
$1 = (struct index_data *) 0xd8129   /* memory was allocated properly */
(gdb) p *index
$2 = {virtual = 0, name = 0x0, pos = 0, number = 0, func = 0, easyActs = 0x0,
  easyActFlags = 0, group = 0}
(gdb) p (char *) 0xd8109
$3 = 0xd8109 "b.c"
(gdb) p (char *) 0xd8108
$4 = 0xd8108 "db.c"            /* the filename seems to have been */
(gdb) p (int *) 0xd811c	       /* properly inserted into the debug */ 
$5 = (int *) 0xd811c           /* block at the beginning of the */
(gdb) p *((int *) 0xd811c)     /* allocated memory block */
$6 = 14942208
(gdb) n

Program received signal 10, Bus error
0xf773cb80 in _doscan ()

/* another run: */
Continuing.

Breakpoint 4, generate_indices (fl=0xf77a201c, top=0xb852c) at db.c:497
497                                     sscanf(buf, "#%d", &index[i].virtual);
(gdb) p index[i].virtual   /* can view allocated mem */
$10 = 0
(gdb) p &index[i].virtual  
$11 = (int *) 0xd8129
(gdb) p i
$12 = 0
(gdb) set index[i].virtual = 1   /* can manipulate allocated mem directly */
(gdb) p index[i].virtual
$13 = 1
(gdb) p index
$14 = (struct index_data *) 0xd8129
(gdb) n

/* but won't work with sscanf() : */
Program received signal 10, Bus error   
0xf773cb80 in _doscan ()



    Below are the C routines to handle everything.  I'm releasing them
into the public domain.  Use them however you wish.


/* dbg_malloc.h - memory allocation routines with embedded debugging info */
/* by Aaron Buhr (abuhr@owlnet.rice.edu) */

#ifndef DBG_MALLOC

#define PTR_TYPE void *


PTR_TYPE dbg_malloc (char *sourcefilename, int sourceline, unsigned size);
PTR_TYPE dbg_calloc (char *sourcefilename, int sourceline,
		     unsigned number, unsigned size_each);
void dbg_free (char *sourcefilename, int sourceline, void *mem);
PTR_TYPE dbg_realloc (char *sourcefilename, int sourceline,
		      PTR_TYPE mem, unsigned size);
int Log_Mem_Status(char *mem_file_name);

#define malloc(size) dbg_malloc(__FILE__, __LINE__, size)
#define calloc(num, size_each) dbg_calloc(__FILE__, __LINE__, num, size_each)
#define free(memptr) dbg_free(__FILE__, __LINE__, memptr)
#define realloc(memptr, size) dbg_realloc(__FILE__, __LINE__, memptr, size)


#define DBG_MALLOC

#endif

/* end dbg_malloc.h */



/* dbg_malloc.c - memory allocation routines with embedded debugging info */
/* by Aaron Buhr (abuhr@owlnet.rice.edu) */

#ifdef malloc
#undef malloc
#endif
#ifdef calloc
#undef calloc
#endif
#ifdef free
#undef free
#endif
#ifdef realloc
#undef realloc
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <memory.h>
#include <sys/time.h>
#include "dbg_malloc.h"


#define FNAME_SIZE 20
#define GET_SFNAME(memptr) ((char *) (memptr))
#define GET_SLINE(memptr) ((int) (*( (((char *) memptr) + FNAME_SIZE + 1))))
#define GET_SIZE(memptr) ((unsigned) (*( (((char *) memptr) + FNAME_SIZE \
					    + 1 + sizeof(int)))))
#define GET_TIME(memptr) ((time_t) (*( (((char *) memptr) + FNAME_SIZE \
					  + 1 + sizeof(int) \
					  + sizeof(unsigned) ))))
#define DEBUG_BLOCK_SIZE (FNAME_SIZE + 1 + sizeof(int) + sizeof(unsigned) \
			  + sizeof(time_t))


struct ptr_node_struct {
    PTR_TYPE mem_ptr;
    struct ptr_node_struct *next;
};


struct mem_realloc_struct {
    PTR_TYPE mem_ptr;
    char *sourcefilename;
    int sourceline;
    unsigned old_size;
    unsigned new_size;
    time_t realloc_time;
    struct mem_realloc_struct *next;
};

struct ptr_node_struct *allocd_mem = 0;
struct mem_realloc_struct *reallocd_mem = 0;


PTR_TYPE dbg_malloc (char *sourcefilename, int sourceline, unsigned size) {

    unsigned new_size;
    PTR_TYPE new_mem;
    struct ptr_node_struct *node;

    new_size = size + DEBUG_BLOCK_SIZE;
    new_mem = malloc(new_size);
    if (!new_mem) return 0;
    if (!(node = malloc(sizeof(struct ptr_node_struct)))) {
	/* not enough mem to allocate mem tracking node */
	free(new_mem);
	return 0;
    }

    strncpy(GET_SFNAME(new_mem), sourcefilename, FNAME_SIZE + 1);
    ((char *) new_mem)[FNAME_SIZE] = 0;
    GET_SLINE(new_mem) = sourceline; 
    
    GET_SIZE(new_mem) = size;
    GET_TIME(new_mem) = time(0);
    
    node->mem_ptr = (PTR_TYPE) (new_mem + DEBUG_BLOCK_SIZE);
    node->next = allocd_mem;
    allocd_mem = node;
    return ( (PTR_TYPE) (new_mem + DEBUG_BLOCK_SIZE));
}




PTR_TYPE dbg_calloc (char *sourcefilename, int sourceline,
		     unsigned number, unsigned size_each) {

    PTR_TYPE *temp;

    temp = dbg_malloc(sourcefilename, sourceline, number * size_each);
    if (temp)
      memset(temp, 0, number * size_each);
    return temp;
}
    


void dbg_free (char *sourcefilename, int sourceline, void *mem) {

    register struct ptr_node_struct *node, *last;
    
    for (last = 0, node = allocd_mem; node; last = node, node = node->next)
      if (node->mem_ptr == mem) {
	  if (!last) {
	      allocd_mem = node->next;
	  } else {
	      last->next = node->next;
	  }
	  free(node);
	  
	  mem = (char *) (mem - DEBUG_BLOCK_SIZE);
	  free(mem);

	  break;
      }

    /* maybe put a log message here that user tried to free memory
       that we don't believe was allocated */
    return;
}
			  



PTR_TYPE dbg_realloc (char *sourcefilename, int sourceline,
		  PTR_TYPE mem, unsigned size) {

    register struct ptr_node_struct *node, *last, *found = 0;
    struct mem_realloc_struct *realloc_node;
    PTR_TYPE real_mem_ptr;
    

    for (last = 0, node = allocd_mem;
	 (node) && (!found);
	 last = node, node = node->next)
      if (node->mem_ptr == mem) found = node;

    if (found) {
	if (!(realloc_node = malloc(sizeof(struct mem_realloc_struct)))) {
	    /* couldn't make new realloc node */
	    return 0;
	}

	real_mem_ptr = (PTR_TYPE) (found->mem_ptr - DEBUG_BLOCK_SIZE);
	realloc_node->mem_ptr = found->mem_ptr;
	realloc_node->sourcefilename = strdup(sourcefilename);
	realloc_node->sourceline = sourceline;
	realloc_node->old_size = GET_SIZE(real_mem_ptr);
	realloc_node->new_size = size;
	realloc_node->realloc_time = time(0);
	realloc_node->next = reallocd_mem;
	reallocd_mem = realloc_node;

	found->mem_ptr = realloc(real_mem_ptr, size + DEBUG_BLOCK_SIZE)
	  + DEBUG_BLOCK_SIZE;
	return(found->mem_ptr);
    }

    /* maybe put a log message here that user tried to realloc memory
       that we don't believe was allocated */
    return;
}


static char *DM_Time_Str(struct tm *timeStruct, char *tmstr) {
        char daystr[3];

        switch (timeStruct->tm_wday) {
                case 0:  strcpy(daystr,"Su"); break;
                case 1:  strcpy(daystr,"Mo"); break;
                case 2:  strcpy(daystr,"Tu"); break;
                case 3:  strcpy(daystr,"We"); break;
                case 4:  strcpy(daystr,"Th"); break;
                case 5:  strcpy(daystr,"Fr"); break;
                case 6:  strcpy(daystr,"Sa"); break;
                default: strcpy(daystr,"FU"); break;
        }

        sprintf(tmstr,"%s %d/%d %2d:%.2d",daystr,timeStruct->tm_mon+1,timeStruc\
t->tm_mday,timeStruct->tm_hour,timeStruct->tm_min);
        return tmstr;
}




int Log_Mem_Status(char *mem_file_name) {

    FILE *mem_file;
    char buf1[120];
    struct ptr_node_struct *node;
    PTR_TYPE real_ptr;
    struct mem_realloc_struct *realloc_node;
    

    if (!(mem_file = fopen(mem_file_name, "w"))) return -1;

    fprintf(mem_file, "Memory allocations:\n");
    for (node = allocd_mem; node; node = node->next) {
	real_ptr = node->mem_ptr - DEBUG_BLOCK_SIZE;
	fprintf(mem_file, "%s: %20.20s -5.5i: %db @ 0x%X\n",
		DM_Time_Str(localtime(GET_TIME(real_ptr)), buf1),
		GET_SFNAME(real_ptr),
		GET_SLINE(real_ptr),
		GET_SIZE(real_ptr),
		node->mem_ptr);
    }

    fprintf(mem_file, "\n\n");
    
    fprintf(mem_file, "Memory reallocations:\n");
    for (realloc_node = reallocd_mem;
	 realloc_node; realloc_node = realloc_node->next) {
	real_ptr = realloc_node->mem_ptr - DEBUG_BLOCK_SIZE;
	fprintf(mem_file, "%s: %20.20s -5.5i: %db @ 0x%X -> %db\n",
		DM_Time_Str(localtime(realloc_node->realloc_time), buf1),
		realloc_node->sourcefilename,
		realloc_node->sourceline,
		realloc_node->old_size,
		realloc_node->mem_ptr,
		realloc_node->new_size);
    }
    fclose(mem_file);
    return 0;
}
		
	    

#define malloc(size) dbg_malloc(__FILE__, __LINE__, size)
#define calloc(num, size_each) dbg_calloc(__FILE__, __LINE__, num, size_each)
#define free(memptr) dbg_free(__FILE__, __LINE__, memptr)
#define realloc(memptr, size) dbg_realloc(__FILE__, __LINE__, memptr, size)

/* end dbg_malloc.c */



     Thanks again to all who responded to my previous message, and
thanks in advance for help on this one.


Aaron Buhr
abuhr@owlnet.rice.edu

Xref: sparky comp.lang.c:16802 comp.lang.c++:16574