/*
 * @(#)debug_malloc.c	1.11 04/07/27
 * 
 * Copyright (c) 2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 * 
 * -Redistribution in binary form must reproduce the above copyright notice, 
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 * 
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may 
 * be used to endorse or promote products derived from this software without 
 * specific prior written permission.
 * 
 * This software is provided "AS IS," without a warranty of any kind. ALL 
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST 
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, 
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY 
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, 
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * 
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */

/* **************************************************************************
 *
 * Set of malloc/realloc/calloc/strdup/free replacement macros that 
 *    insert some extra words around each allocation for debugging purposes
 *    and also attempt to detect invalid uses of the malloc heap through
 *    various tricks like inserting clobber words at the head and tail of
 *    the user's area, delayed free() calls, and setting the memory to
 *    a fixed pattern on allocation and when freed.  The allocations also
 *    can include warrants so that when an area is clobbered, this
 *    package can report where the allocation took place.
 *    The macros included are:
 *		malloc(size)
 *		realloc(ptr,size)
 *		calloc(nelem,elsize)
 *		strdup(s1)
 *		free(ptr)
 *		malloc_police()   <--- Not a system function
 *    The above macros match the standard behavior of the system functions.
 *
 *    They should be used through the include file "debug_malloc.h".
 *
 *       IMPORTANT: All source files that call any of these macros
 *                  should include debug_malloc.h. This package will
 *                  not work if the memory isn't allocated and freed
 *                  by the macros in debug_malloc.h. The important issue
 *                  is that any malloc() from debug_malloc.h must be
 *                  freed by the free() in debug_malloc.h.
 *
 *    The macros in debug_malloc.h will override the normal use of
 *       malloc, realloc, calloc, strdup, and free with the functions below.
 *
 *    These functions include:
 *         void *debug_malloc(size_t, void*, int);
 *         void *debug_realloc(void*, size_t, void*, int);
 *         void *debug_calloc(size_t, size_t, void*, int);
 *         void  debug_free(void *, void*, int);
 *
 *   In addition the function debug_malloc_police() can be called to
 *      tell you what memory has not been freed.
 *	   void debug_malloc_police(void*, int);
 *      The function debug_malloc_police() is available through the macro
 *      malloc_police(). Normally you would want to call this at exit()
 *      time to find out what memory is still allocated.
 *
 *   The variable malloc_watch determines if the warrants are generated.
 *      warrants are structures that include the filename and line number
 *      of the caller who allocated the memory. This structure is stored
 *      at the tail of the malloc space, which is allocated large enough
 *      to hold some clobber words at the head and tail, the user's request
 *      and the warrant record (if malloc_watch is non-zero).
 *
 *   The macro LEFT_OVER_CHAR is what the trailing bytes of an allocation
 *     are set to (when the allocation is not a multiple of 8) on allocation.
 *     At free(0 time, these bytes are double checked to make sure they were
 *     not clobbered. To remove this feature #undef LEFT_OVER_CHAR.
 *
 *   The memory freed will have the FREED_CHAR put into it. To remove this
 *     feature #undef FREED_CHAR.
 *
 *   The memory allocated (not calloc'd) will have the ALLOC_CHAR put into it
 *     at the time of allocation. To remove this feature #undef ALLOC_CHAR.
 *
 *   The macro MAX_FREE_DELAY_COUNT controls how many free blocks will
 *     be kept around before being freed. This creates a delayed affect
 *     so that free space that gets clobbered just might get detected.
 *     The free() call will immediately set the user space to the FREED_CHAR,
 *     leaving the clobber words and warrant in place (making sure they
 *     haven't been clobbered). Then the free() pointer is added to a
 *     queue of MAX_FREE_DELAY_COUNT long, and if the queue was full, the
 *     oldest free()'d memory is actually freed, getting it's entire
 *     memory length set to the FREED_CHAR.
 *
 *  WARNING: This can significantly slow down an application, depending
 *           on how many allocations are made. Also the additional memory
 *           needed for the clobber words and the warrants can be significant
 *           again, depending on how many allocations are made.
 *           In addition, the delayed free calls can create situations
 *           where you might run out of memory prematurely.
 *
 * **************************************************************************
 */

#ifdef DEBUG

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include "hprof.h"

/* ***************************************************************************
 * Space normally looks like (clobber Word is 64 bits and aligned to 8 bytes):
 *
 *		    -----------------
 * malloc/free get->| clobber Word  |   ---> contains -size requested by user
 *		    -----------------
 *    User gets --->| user space    |
 *		    | 		    |
 *		    |  | left_over  |  ---> left_over bytes will be <= 7
 *		    -----------------
 *		    | clobber Word  |   ---> contains -size requested by user
 *		    -----------------
 *		    |   Warrant     |   ---> Optional (malloc_watch!=0)
 *		    |		    |        Contains filename and line number
 *		    |		    |          where allocation happened
 *		    |		    |
 *		    -----------------
 ***************************************************************************/

/*
 *  Flag that tells debug_malloc/debug_free/debug_realloc to police
 *   heap space usage. (This is a dynamic flag that can be turned on/off)
 */
static int      malloc_watch = 1;

/* Character to stuff into freed space */
#define FREED_CHAR  'F'

/* Character to stuff into allocated space */
#define ALLOC_CHAR  'A'

/* Character to stuff into left over trailing bytes */
#define LEFT_OVER_CHAR  'Z'

/* Number of 'free' calls that will be delayed until the end */
#define MAX_FREE_DELAY_COUNT	1
#undef MAX_FREE_DELAY_COUNT

/* Maximum name of __FILE_ stored in each malloc'd area */
#define WARRANT_NAME_MAX (32-1)	/* 1 less than multiple of 8 is best */

/* Macro to convert a user pointer to the malloc pointer */
#define user2malloc_(uptr)   (((char*)(void*)uptr)-sizeof(Word))

/* Macro to convert a macro pointer to the user pointer */
#define malloc2user_(mptr)   (((char*)(void*)(mptr))+sizeof(Word))

/* Size of the warrant record (this is dynamic) */
#define warrant_space  ( malloc_watch?sizeof(Warrant_Record):0 )

/* Macro to round up a number of bytes to a multiple of sizeof(Word) bytes */
#define round_up_(n) \
	((n)==0?0:(sizeof(Word)+(((n)-1)/sizeof(Word))*sizeof(Word)))

/* Macro to calculate the needed malloc bytes from the user's request. */
#define rbytes_(nbytes) \
    (size_t)( sizeof(Word) + round_up_(nbytes) + sizeof(Word) + warrant_space )

/* Macro to get the -size stored in space through the malloc pointer */
#define nsize1_(mptr) 		(((Word*)(void*)(mptr))->nsize1)
#define nsize2_(mptr) 		(((Word*)(void*)(mptr))->nsize2)

/* Macro to get the -size stored in the tail of the space through */
/*     the malloc pointer */
#define tail_nsize1_(mptr)     \
	nsize1_(((char*)(void*)(mptr))+round_up_(-nsize1_(mptr))+sizeof(Word))
#define tail_nsize2_(mptr)     \
	nsize2_(((char*)(void*)(mptr))+round_up_(-nsize1_(mptr))+sizeof(Word))

/* Macro to get the -size stored in space through the user pointer */
#define user_nsize1_(uptr)    	nsize1_(user2malloc_(uptr))
#define user_nsize2_(uptr)    	nsize2_(user2malloc_(uptr))

/* Macro to get the -size stored in the tail of the space through */
/*     the user pointer */
#define user_tail_nsize1_(uptr)	tail_nsize1_(user2malloc_(uptr))
#define user_tail_nsize2_(uptr)	tail_nsize2_(user2malloc_(uptr))

/* Macro to get the int* of the last 32bit word of user space */
#define last_user_word_(mptr)	\
	((int*)(((char*)(void*)(mptr))+round_up_(-nsize1_(mptr))))

/* Macros to get at the warrant contents from the malloc pointer */
#define warrant_(mptr) \
  (*((Warrant_Record*)(void*)(((char*)(void*)(mptr))+round_up_(-nsize1_(mptr))+sizeof(Word)*2)))

/* This struct is allocated after the tail clobber word if malloc_watch */
/*    is true. */
typedef struct {
    void           *link;	/* Next mptr in list */
    char            name[WARRANT_NAME_MAX + 1];	/* Name of allocator */
    int             line;	/* Line number where allocated */
    int             id;	        /* Nth allocation */
}               Warrant_Record;
#define warrant_link_(mptr) warrant_(mptr).link
#define warrant_name_(mptr) warrant_(mptr).name
#define warrant_line_(mptr) warrant_(mptr).line
#define warrant_id_(mptr)   warrant_(mptr).id
#define MFILE(mptr) (malloc_watch?warrant_name_(mptr):"?")
#define MLINE(mptr) (malloc_watch?warrant_line_(mptr):0)
#define MID(mptr)   (malloc_watch?warrant_id_(mptr):0)

/* This should be one machine word and is also the clobber word struct */
typedef struct {
    int             nsize1;
    int             nsize2;
}               Word;		/* Largest basic type , sizeof(double)? */

/* The first malloc pointer for the warrants */
static void    *first_warrant_mptr = NULL;

/* Counter of allocations */
static int id_counter = 0;
static int largest_size = 0;
static void * largest_addr = NULL;
static void * smallest_addr = NULL;

/* Used to isolate what the error is */
static char *debug_check;
static void *clobbered_ptr;

/* Minumum macro */
#define minimum(a,b) ((a)<(b)?(a):(b))

/* Message routine */
static void
error_message(const char * format, ...)
{
    FILE *error_fp = stderr; /* All debug_malloc.c messages */
    va_list ap;
    va_start(ap, format);
    (void)fprintf(error_fp, "debug_malloc: ");
    (void)vfprintf(error_fp, format, ap);
    (void)fprintf(error_fp, "\n");
    (void)fflush(error_fp);
    va_end(ap);
}

/* This function prints out a memory error for the memory function
 *   'name' which was called in file 'file' at line number 'line'.  The malloc
 *   pointer with the error is in 'mptr'.
 */
static void
memory_error(void *mptr, const char *name, int mid, const char *mfile, int mline, const char *file, int line)
{
    char  nice_words[512];
    char  temp[256];
    int   len;
    void *mptr_walk;

    if (name == NULL)
	name = "UNKNOWN_NAME";
    if (file == NULL)
	file = "UNKNOWN_FILE";
    md_system_error(temp, (int)sizeof(temp));
    (void)strcpy(nice_words, temp);
    if ( debug_check!=NULL ) {
       (void)md_snprintf(nice_words, sizeof(nice_words),
		    "%s The %s at %p appears to have been hit.",
		    temp, debug_check, clobbered_ptr);
    }
    len = -nsize1_(mptr);
    error_message("Error: "
		   "%s The malloc space #%d is at %p [user size=%d(0x%x)],"
		   " and was allocated from file \"%s\" at line %d."
		   " [The debug function %s() detected this error "
		   "in file \"%s\" at line %d.]",
		   nice_words, mid, mptr, len, len, mfile, mline, 
		   name, file, line);
    
    /* Print out contents of this allocation */
    {
	int i;
        void *uptr = malloc2user_(mptr);
	char *pmess;
	pmess = temp;
	for(i=0;i<(int)sizeof(temp);i++) {
	    int ch = ((unsigned char*)uptr)[i];
	    if ( isprint(ch) ) {
		*pmess++ = ch;
	    } else {
		*pmess++ = '\\';
		*pmess++ = 'x';
		(void)sprintf(pmess,"%02x",ch);
		pmess+=2;
	    }
	}
	*pmess = 0;
        error_message("Error: %p contains user data: %s", uptr, temp);
    }

    /* Try and print out table */
    if (!malloc_watch) {
	return;
    }
    mptr_walk = first_warrant_mptr;
    if (mptr_walk != NULL) {
	error_message("Active allocations: "
	   "count=%d, largest_size=%d, address range (%p,%p)", 
			id_counter, largest_size, smallest_addr, largest_addr);
	do {
	    int size1;
	    int size2;
	    char *mfile_walk;

	    if ( mptr_walk > largest_addr || mptr_walk < smallest_addr ) {
		error_message("Terminating list due to pointer corruption");
		break;
	    }
	    size1 = -nsize1_(mptr_walk);
	    size2 = -nsize2_(mptr_walk);
	    mfile_walk = MFILE(mptr_walk);
	    error_message("#%d: addr=%p size1=%d size2=%d file=\"%.*s\" line=%d",
		MID(mptr_walk), mptr_walk, size1, size2, 
		WARRANT_NAME_MAX, mfile_walk, MLINE(mptr_walk));
	    if ( size1 != size2 || size1 > largest_size || size1 < 0 ) {
		error_message("Terminating list due to size corruption");
		break;
	    }
	    mptr_walk = warrant_link_(mptr_walk);
	} while (mptr_walk != NULL);
    }
    abort();
}

/* This function sets the clobber word and sets up the warrant for the input
 *   malloc pointer "mptr".
 */
static void
setup_space_and_issue_warrant(void *mptr, size_t size, const char *file, int line)
{
    register int    nbytes;

    /*LINTED*/
    nbytes = (int)size;
    if ( nbytes > largest_size || largest_addr == NULL ) largest_size = nbytes;
    /*LINTED*/
    if ( mptr > largest_addr ) largest_addr = mptr;
    /*LINTED*/
    if ( mptr < smallest_addr || smallest_addr == NULL ) smallest_addr = mptr;

    /* Must be done first: */
    nsize1_(mptr) = -nbytes;
    nsize2_(mptr) = -nbytes;
    tail_nsize1_(mptr) = -nbytes;
    tail_nsize2_(mptr) = -nbytes;

#ifdef LEFT_OVER_CHAR
    /* Fill in those few extra bytes just before the tail Word structure */
    {
        register int    trailing_extra_bytes;
        /* LINTED */
        trailing_extra_bytes = (int) (round_up_(nbytes) - nbytes);
        if (  trailing_extra_bytes > 0 ) {
            register char  *p;
            register int    i;
            p = ((char *) mptr) + sizeof(Word) + nbytes;
            for (i = 0; i < trailing_extra_bytes; i++)
	        p[i] = LEFT_OVER_CHAR;
        }
    }
#endif

    /* Fill out warrant */
    if (malloc_watch) {
        static Warrant_Record zero_warrant;
        register void  *p1,
                       *p2;
	size_t len;
	int start_pos = 0;
	warrant_(mptr) = zero_warrant;
	p1 = warrant_name_(mptr);
	len = strlen(file);
	if ( len >  WARRANT_NAME_MAX )  {
            /*LINTED*/
	    start_pos = len - WARRANT_NAME_MAX;
	}
	p2 = ((char*)file) + start_pos;
	/*LINTED*/
	(void) memcpy(p1, p2, minimum(((int)len), WARRANT_NAME_MAX));
	warrant_line_(mptr) = line;
	warrant_id_(mptr)   = ++id_counter;
	warrant_link_(mptr) = first_warrant_mptr;
	first_warrant_mptr = mptr;
    }
}

/* This function checks the clobber words at the beginning and end of the
 *   allocated space.
 */
static void
memory_check(void *uptr, int mid, const char *mfile, int mline, const char *file, int line)
{
    int             neg_nbytes;
    int             nbytes;

    debug_check = "pointer value itself";
    clobbered_ptr = uptr;
    if (uptr == NULL)
	memory_error((void *) NULL, "memory_check", mid, mfile, mline, file, line);

    /* Check both Word structures */
    
    debug_check = "first beginning clobber word";
    clobbered_ptr = (char*)&user_nsize1_(uptr);
    neg_nbytes = user_nsize1_(uptr);
    if (neg_nbytes >= 0)
	memory_error(user2malloc_(uptr), "memory_check", mid, mfile, mline, file, line);
    
    debug_check = "second beginning clobber word";
    clobbered_ptr = (char*)&user_nsize2_(uptr);
    if (neg_nbytes != user_nsize2_(uptr))
	memory_error(user2malloc_(uptr), "memory_check", mid, mfile, mline, file, line);
    
    debug_check = "first ending clobber word";
    clobbered_ptr = (char*)&user_tail_nsize1_(uptr);
    if (neg_nbytes != user_tail_nsize1_(uptr))
	memory_error(user2malloc_(uptr), "memory_check", mid, mfile, mline, file, line);
    
    debug_check = "second ending clobber word";
    clobbered_ptr = (char*)&user_tail_nsize2_(uptr);
    if (neg_nbytes != user_tail_nsize2_(uptr))
	memory_error(user2malloc_(uptr), "memory_check", mid, mfile, mline, file, line);

    /* Get a positive count of bytes */
    nbytes = -neg_nbytes;

#ifdef LEFT_OVER_CHAR
    {
        /* Check those few extra bytes just before the tail Word structure */
        register int    trailing_extra_bytes;
        register int    i;
        register char  *p;
        /* LINTED */
        trailing_extra_bytes = (int) (round_up_(nbytes) - nbytes);
        p = ((char *) (uptr)) + nbytes;
        debug_check = "trailing left over area";
        for (i = 0; i < trailing_extra_bytes; i++) {
            clobbered_ptr = p+1;
	    if (p[i] != LEFT_OVER_CHAR) {
	        memory_error(user2malloc_(uptr), "memory_check", mid, mfile, mline, file, line);
	    }
        }
    }
#endif

    /* Make sure debug_check is cleared */
    debug_check = NULL;
}

/* This function looks for the given malloc pointer in the police line up
 *   and removes it from the warrant list.
 *   	mptr		The pointer to the malloc space being removed
 */
static int
remove_warrant(void *mptr)
{
    void           *mptr1,
                   *last_mptr1;

    /* Free it up from the list */
    if (malloc_watch && mptr != NULL) {
	int found;

	found = 0;
	last_mptr1 = NULL;
	mptr1 = first_warrant_mptr;
	while (mptr1 != NULL) {
	    if (mptr1 == mptr) {
		if (last_mptr1 == NULL)
		    first_warrant_mptr = warrant_link_(mptr1);
		else
		    warrant_link_(last_mptr1) = warrant_link_(mptr1);
		found = 1;
		break;
	    }
	    last_mptr1 = mptr1;
	    mptr1 = warrant_link_(mptr1);
	}
	return found;
    }
    return 1;
}

static void
actual_free(void *uptr, const char *file, int line)
{
    void *mptr;
    const char *mfile;
    int mline;
    int mid;
    if ( uptr == NULL )
	return;
    mptr = user2malloc_(uptr);
    memory_check(uptr, (mid=MID(mptr)), (mfile=MFILE(mptr)), (mline=MLINE(mptr)), file, line);
    if (malloc_watch && remove_warrant(mptr)==0 )
	memory_check(uptr, mid, mfile, mline, file, line);
#ifdef FREED_CHAR
    if ( mptr!=NULL ) {
	size_t nbytes = -nsize1_(mptr);
        /* LINTED */
	(void)memset(mptr, FREED_CHAR, rbytes_(nbytes));
    }
#endif
    free(mptr);
}

#ifdef MAX_FREE_DELAY_COUNT

static void *free_delay[MAX_FREE_DELAY_COUNT];
static int free_delay_pos = 0;

static void 
delayed_free(void *uptr, const char* file, int line)
{
    void *mptr;
    void *olduptr = free_delay[free_delay_pos];
    size_t nbytes;
    if ( uptr==NULL )
	return;
    mptr = user2malloc_(uptr);
    memory_check(uptr, MID(mptr), MFILE(mptr), MLINE(mptr), file, line);
    if ( olduptr!=NULL ) {
	actual_free(olduptr, file, line);
    }
    free_delay[free_delay_pos] = uptr;
    free_delay_pos++;
    free_delay_pos = free_delay_pos % MAX_FREE_DELAY_COUNT;
    nbytes = -user_nsize1_(uptr);
#ifdef FREED_CHAR
    (void)memset(uptr, FREED_CHAR, (size_t)nbytes);
#endif
}

static void 
delayed_free_all(const char *file, int line)
{
    int i;
    for ( i=0; i< MAX_FREE_DELAY_COUNT; i++) {
	void *olduptr = free_delay[i];
	free_delay[i] = NULL;
	if ( olduptr!=NULL ) {
	    actual_free(olduptr, file, line);
        }
    }
}

#endif

void
debug_free(void *uptr, const char *file, int line)
{
    int mid = 0;

    if (uptr == NULL)
	memory_error((void *) NULL, "debug_free", mid, file, line, file, line);
#ifdef MAX_FREE_DELAY_COUNT
    delayed_free(uptr, file, line);
#else
    actual_free(uptr, file, line);
#endif
}

/* This function calls malloc(). */
void           *
debug_malloc(size_t nbytes, const char *file, int line)
{
    void           *mptr;
    void           *uptr;
    int mid = id_counter;

    /*LINTED*/
    if ((int)nbytes <= 0)
	memory_error((void *) NULL, "debug_malloc", mid, file, line, file, line);
    /* LINTED */
    mptr = malloc(rbytes_(nbytes));
    if (mptr == NULL)
	memory_error((void *) NULL, "debug_malloc", mid, file, line, file, line);
    setup_space_and_issue_warrant(mptr, nbytes, file, line);
    uptr = malloc2user_(mptr);
#ifdef ALLOC_CHAR
    (void)memset(uptr, ALLOC_CHAR, (size_t)nbytes);
#endif
    return uptr;
}

void           *
debug_realloc(void *uptr, size_t nbytes, const char *file, int line)
{
    void           *mptr;
    void           *oldmptr;
    void           *newuptr;
    size_t         oldnbytes;
    int mid = id_counter;

    oldmptr = user2malloc_(uptr);
    oldnbytes = 0;
    if ((int)nbytes <= 0)
	memory_error(oldmptr, "debug_realloc", mid, file, line, file, line);
    if (uptr != NULL) {
	memory_check(uptr, MID(oldmptr), MFILE(oldmptr), MLINE(oldmptr), file, line);
        oldnbytes = -user_nsize1_(uptr);
	if ( malloc_watch && remove_warrant(oldmptr)==0 )
	    memory_check(uptr, MID(oldmptr), MFILE(oldmptr), MLINE(oldmptr), file, line);
    }
    if (uptr == NULL) {
        /* LINTED */
	mptr = malloc(rbytes_(nbytes));
    } else {
        /* LINTED */
	mptr = realloc(oldmptr, rbytes_(nbytes));
    }
    if (mptr == NULL)
	memory_error(oldmptr, "debug_realloc", mid, file, line, file, line);
    setup_space_and_issue_warrant(mptr, nbytes, file, line);
    newuptr = malloc2user_(mptr);
#ifdef ALLOC_CHAR
    if (uptr == NULL)
        (void)memset(newuptr, ALLOC_CHAR, (size_t)nbytes);
    else if ( nbytes > oldnbytes )
        (void)memset(((char*)newuptr)+oldnbytes, ALLOC_CHAR, (size_t)nbytes-oldnbytes);
#endif
    return newuptr;
}

/* This function calls calloc(). */
void           *
debug_calloc(size_t nelem, size_t elsize, const char *file, int line)
{
    void           *mptr;
    size_t	    nbytes;
    int mid = id_counter;

    nbytes = nelem*elsize;
    /*LINTED*/
    if ((int)nbytes <= 0)
	memory_error((void *) NULL, "debug_calloc", mid, file, line, file, line);
    /* LINTED */
    mptr = calloc(rbytes_(nbytes),1);
    if (mptr == NULL)
	memory_error((void *) NULL, "debug_calloc", mid, file, line, file, line);
    setup_space_and_issue_warrant(mptr, nbytes, file, line);
    return malloc2user_(mptr);
}

/* This function replaces strdup(). */
char           *
debug_strdup(const char *s1, const char *file, int line)
{
    void           *mptr;
    void           *uptr;
    size_t	    nbytes;
    int mid = id_counter;

    if (s1 == NULL)
	memory_error((void *) NULL, "debug_strdup", mid, file, line, file, line);
    nbytes = strlen(s1)+1;
    /*LINTED*/
    if ((int)nbytes < 0)
	memory_error((void *) NULL, "debug_strdup", mid, file, line, file, line);
    /* LINTED */
    mptr = malloc(rbytes_(nbytes));
    if (mptr == NULL)
	memory_error((void *) NULL, "debug_strdup", mid, file, line, file, line);
    setup_space_and_issue_warrant(mptr, nbytes, file, line);
    uptr = malloc2user_(mptr);
    (void)strcpy((char*)uptr, s1);
    return (char*)uptr;
}

void
debug_malloc_verify(const char *file, int line)
{
    void           *mptr;

#ifdef MAX_FREE_DELAY_COUNT
    delayed_free_all(file,line);
#endif

    if (!malloc_watch) {
	return;
    }
    mptr = first_warrant_mptr;
    if (mptr != NULL) {
	/* Check all this memory first */
	do {
	    memory_check(malloc2user_(mptr), MID(mptr), MFILE(mptr), MLINE(mptr), file, line);
	    mptr = warrant_link_(mptr);
	} while (mptr != NULL);
    }
}

/* Report outstanding space warrants to console. */
void
debug_malloc_police(const char *file, int line)
{
    void           *mptr;

#ifdef MAX_FREE_DELAY_COUNT
    delayed_free_all(file,line);
#endif

    if (!malloc_watch) {
	return;
    }

    mptr = first_warrant_mptr;
    if (mptr != NULL) {
        debug_malloc_verify(file, line);
	/* Now issue warrants */
        mptr = first_warrant_mptr;
	do {
	    error_message("Outstanding space warrant: %p (%d bytes) allocated by %s at line %d, allocation #%d",
	       mptr, -nsize1_(mptr), warrant_name_(mptr), 
	       warrant_line_(mptr), warrant_id_(mptr));

	    mptr = warrant_link_(mptr);
	} while (mptr != NULL);
    }
}

#else

void
debug_malloc_verify(const char *file, int line)
{
    file = file;
    line = line;
}

void
debug_malloc_police(const char *file, int line)
{
    file = file;
    line = line;
}

#endif

