Edit

kc3-lang/libxml2/xmlmemory.c

Branch :

  • Show log

    Commit

  • Author : Daniel Veillard
    Date : 1999-12-29 12:49:06
    Hash : 5e5c6235
    Message : - Push mode for the HTML parser (new calls) - Improved the memory debugger to provide content informations - cleanups, last known mem leak killed Daniel

  • xmlmemory.c
  • /*
     * memory.c:  libxml memory allocator wrapper.
     *
     * Daniel.Veillard@w3.org
     */
    
    #ifdef WIN32
    #include "win32config.h"
    #else
    #include "config.h"
    #endif
    
    #include <stdio.h>
    #include <string.h>
    
    #ifdef HAVE_SYS_TYPES_H
    #include <sys/types.h>
    #endif
    #ifdef HAVE_TIME_H
    #include <time.h>
    #endif
    #ifdef HAVE_MALLOC_H
    #include <malloc.h>
    #endif
    #ifdef HAVE_STDLIB_H
    #include <stdlib.h>
    #endif
    #ifdef HAVE_CTYPE_H
    #include <ctype.h>
    #endif
    
    
    #include "xmlmemory.h"
    
    #ifndef NO_DEBUG_MEMORY
    #ifdef xmlMalloc
    #undef xmlMalloc
    #endif
    #ifdef xmlRealloc
    #undef xmlRealloc
    #endif
    #ifdef xmlMemStrdup
    #undef xmlMemStrdup
    #endif
    
    extern void xmlMemoryDump(void);
    
    /*
     * Each of the blocks allocated begin with a header containing informations
     */
    
    #define MEMTAG 0x5aa5
    
    #define MALLOC_TYPE 1
    #define REALLOC_TYPE 2
    #define STRDUP_TYPE 3
    
    typedef struct memnod {
        unsigned int   mh_tag;
        unsigned int   mh_type;
        unsigned long  mh_number;
        size_t         mh_size;
    #ifdef MEM_LIST
       struct memnod *mh_next;
       struct memnod *mh_prev;
    #endif
       const char    *mh_file;
       unsigned int   mh_line;
    }  MEMHDR;
    
    
    #ifdef SUN4
    #define ALIGN_SIZE  16
    #else
    #define ALIGN_SIZE  sizeof(double)
    #endif
    #define HDR_SIZE    sizeof(MEMHDR)
    #define RESERVE_SIZE (((HDR_SIZE + (ALIGN_SIZE-1)) \
    		      / ALIGN_SIZE ) * ALIGN_SIZE)
    
    
    #define CLIENT_2_HDR(a) ((MEMHDR *) (((char *) (a)) - RESERVE_SIZE))
    #define HDR_2_CLIENT(a)    ((void *) (((char *) (a)) + RESERVE_SIZE))
    
    
    static unsigned long  debugMemSize = 0;
    static unsigned long  debugMaxMemSize = 0;
    static int block=0;
    int xmlMemStopAtBlock = 0;
    int xmlMemInitialized = 0;
    #ifdef MEM_LIST
    static MEMHDR *memlist = NULL;
    #endif
    
    void debugmem_tag_error(void *addr);
    #ifdef MEM_LIST
    void  debugmem_list_add(MEMHDR *);
    void debugmem_list_delete(MEMHDR *);
    #endif
    #define Mem_Tag_Err(a) debugmem_tag_error(a);
    
    #ifndef TEST_POINT
    #define TEST_POINT
    #endif
    
    /**
     * xmlMallocBreakpoint:
     *
     * Breakpoint to use in conjunction with xmlMemStopAtBlock. When the block
     * number reaches the specified value this function is called. One need to add a breakpoint
     * to it to get the context in which the given block is allocated.
     */
    
    void
    xmlMallocBreakpoint(void) {
        fprintf(stderr, "xmlMallocBreakpoint reached on block %d\n", xmlMemStopAtBlock);
    }
    
    /**
     * xmlMallocLoc:
     * @size:  an int specifying the size in byte to allocate.
     * @file:  the file name or NULL
      @file:  the line number
     *
     * a malloc() equivalent, with logging of the allocation info.
     *
     * Returns a pointer to the allocated area or NULL in case of lack of memory.
     */
    
    void *
    xmlMallocLoc(int size, const char * file, int line)
    {
        MEMHDR *p;
        
        if (!xmlMemInitialized) xmlInitMemory();
    #ifdef DEBUG_MEMORY
        fprintf(stderr, "Malloc(%d)\n",size);
    #endif
    
        TEST_POINT
        
        p = (MEMHDR *) malloc(RESERVE_SIZE+size);
    
        if (!p) {
    	fprintf(stderr, "xmlMalloc : Out of free space\n");
    	xmlMemoryDump();
    	return(NULL);
        }   
        p->mh_tag = MEMTAG;
        p->mh_number = ++block;
        p->mh_size = size;
        p->mh_type = MALLOC_TYPE;
        p->mh_file = file;
        p->mh_line = line;
        debugMemSize += size;
        if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
    #ifdef MEM_LIST
        debugmem_list_add(p);
    #endif
    
    #ifdef DEBUG_MEMORY
        fprintf(stderr, "Malloc(%d) Ok\n",size);
    #endif
        
        if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
    
        TEST_POINT
    
        return(HDR_2_CLIENT(p));
    }
    
    /**
     * xmlMalloc:
     * @size:  an int specifying the size in byte to allocate.
     *
     * a malloc() equivalent, with logging of the allocation info.
     *
     * Returns a pointer to the allocated area or NULL in case of lack of memory.
     */
    
    void *
    xmlMalloc(int size)
    {
        return(xmlMallocLoc(size, "none", 0));
    }
    
    /**
     * xmlReallocLoc:
     * @ptr:  the initial memory block pointer
     * @size:  an int specifying the size in byte to allocate.
     * @file:  the file name or NULL
     * @file:  the line number
     *
     * a realloc() equivalent, with logging of the allocation info.
     *
     * Returns a pointer to the allocated area or NULL in case of lack of memory.
     */
    
    void *
    xmlReallocLoc(void *ptr,int size, const char * file, int line)
    {
        MEMHDR *p;
        unsigned long number;
    
        if (!xmlMemInitialized) xmlInitMemory();
        TEST_POINT
    
        p = CLIENT_2_HDR(ptr);
        number = p->mh_number;
        if (p->mh_tag != MEMTAG) {
           Mem_Tag_Err(p);
    	 goto error;
        }
        p->mh_tag = ~MEMTAG;
        debugMemSize -= p->mh_size;
    #ifdef MEM_LIST
        debugmem_list_delete(p);
    #endif
    
        p = (MEMHDR *) realloc(p,RESERVE_SIZE+size);
        if (!p) {
    	 goto error;
        }
        p->mh_tag = MEMTAG;
        p->mh_number = number;
        p->mh_type = REALLOC_TYPE;
        p->mh_size = size;
        p->mh_file = file;
        p->mh_line = line;
        debugMemSize += size;
        if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
    #ifdef MEM_LIST
        debugmem_list_add(p);
    #endif
    
        TEST_POINT
    
        return(HDR_2_CLIENT(p));
        
    error:    
        return(NULL);
    }
    
    /**
     * xmlRealloc:
     * @ptr:  the initial memory block pointer
     * @size:  an int specifying the size in byte to allocate.
     *
     * a realloc() equivalent, with logging of the allocation info.
     *
     * Returns a pointer to the allocated area or NULL in case of lack of memory.
     */
    
    void *
    xmlRealloc(void *ptr,int size) {
        return(xmlReallocLoc(ptr, size, "none", 0));
    }
    
    /**
     * xmlFree:
     * @ptr:  the memory block pointer
     *
     * a free() equivalent, with error checking.
     */
    void
    xmlFree(void *ptr)
    {
        MEMHDR *p;
    
        TEST_POINT
    
        p = CLIENT_2_HDR(ptr);
        if (p->mh_tag != MEMTAG) {
           Mem_Tag_Err(p);
           goto error;
        }
        p->mh_tag = ~MEMTAG;
        debugMemSize -= p->mh_size;
    
    #ifdef MEM_LIST
        debugmem_list_delete(p);
    #endif
        free(p);
    
        TEST_POINT
    
        return;
        
    error:    
        fprintf(stderr, "xmlFree(%X) error\n", (unsigned int) ptr);
        return;
    }
    
    /**
     * xmlMemStrdupLoc:
     * @ptr:  the initial string pointer
     * @file:  the file name or NULL
     * @file:  the line number
     *
     * a strdup() equivalent, with logging of the allocation info.
     *
     * Returns a pointer to the new string or NULL if allocation error occured.
     */
    
    char *
    xmlMemStrdupLoc(const char *str, const char *file, int line)
    {
        char *s;
        size_t size = strlen(str) + 1;
        MEMHDR *p;
    
        if (!xmlMemInitialized) xmlInitMemory();
        TEST_POINT
    
        p = (MEMHDR *) malloc(RESERVE_SIZE+size);
        if (!p) {
          goto error;
        }
        p->mh_tag = MEMTAG;
        p->mh_number = ++block;
        p->mh_size = size;
        p->mh_type = STRDUP_TYPE;
        p->mh_file = file;
        p->mh_line = line;
        debugMemSize += size;
        if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
    #ifdef MEM_LIST
        debugmem_list_add(p);
    #endif
        s = HDR_2_CLIENT(p);
        
        if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
    
        if (s != NULL)
          strcpy(s,str);
        else
          goto error;
        
        TEST_POINT
    
        return(s);
    
    error:
        return(NULL);
    }
    
    /**
     * xmlMemStrdup:
     * @ptr:  the initial string pointer
     *
     * a strdup() equivalent, with logging of the allocation info.
     *
     * Returns a pointer to the new string or NULL if allocation error occured.
     */
    
    char *
    xmlMemStrdup(const char *str) {
        return(xmlMemStrdupLoc(str, "none", 0));
    }
    
    /**
     * xmlMemUsed:
     *
     * returns the amount of memory currenly allocated
     *
     * Returns an int representing the amount of memory allocated.
     */
    
    int
    xmlMemUsed(void) {
         return(debugMemSize);
    }
    
    #ifdef MEM_LIST
    /**
     * xmlMemContentShow:
     * @fp:  a FILE descriptor used as the output file
     * @p:  a memory block header
     *
     * tries to show some content from the memory block
     */
    
    void
    xmlMemContentShow(FILE *fp, MEMHDR *p)
    {
        int i,j,len = p->mh_size;
        const char *buf = HDR_2_CLIENT(p);
    
        for (i = 0;i < len;i++) {
            if (buf[i] == 0) break;
    	if (!isprint(buf[i])) break;
        }
        if ((i < 4) && ((buf[i] != 0) || (i == 0))) {
            if (len >= 4) {
    	    MEMHDR *q;
    	    void *cur;
    
                for (j = 0;j < len -3;j += 4) {
    		cur = *((void **) &buf[j]);
    		q = CLIENT_2_HDR(cur);
    		p = memlist;
    		while (p != NULL) {
    		    if (p == q) break;
    		    p = p->mh_next;
    		}
    		if (p == q) {
    		    fprintf(fp, " pointer to #%lu at index %d",
    		            p->mh_number, j);
    		    return;
    		}
    	    }
    	}
        } else if ((i == 0) && (buf[i] == 0)) {
            fprintf(fp," null");
        } else {
            if (buf[i] == 0) fprintf(fp," \"%.25s\"", buf); 
    	else {
                fprintf(fp," [");
    	    for (j = 0;j < i;j++)
                    fprintf(fp,"%c", buf[j]);
                fprintf(fp,"]");
    	}
        }
    }
    #endif
    
    /**
     * xmlMemShow:
     * @fp:  a FILE descriptor used as the output file
     * @nr:  number of entries to dump
     *
     * show a show display of the memory allocated, and dump
     * the @nr last allocated areas which were not freed
     */
    
    void
    xmlMemShow(FILE *fp, int nr)
    {
    #ifdef MEM_LIST
        MEMHDR *p;
    #endif
    
        if (fp != NULL)
    	fprintf(fp,"      MEMORY ALLOCATED : %lu, MAX was %lu\n",
    		debugMemSize, debugMaxMemSize);
    #ifdef MEM_LIST
        if (nr > 0) {
    	fprintf(fp,"NUMBER   SIZE  TYPE   WHERE\n");
    	p = memlist;
    	while ((p) && nr > 0) {
    	      fprintf(fp,"%6lu %6u ",p->mh_number,p->mh_size);
    	    switch (p->mh_type) {
    	       case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
    	       case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
    	      case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
    			default:fprintf(fp,"   ???    in ");break;
    	    }
    	    if (p->mh_file != NULL)
    	        fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
    	    if (p->mh_tag != MEMTAG)
    		fprintf(fp,"  INVALID");
    	    xmlMemContentShow(fp, p);
    	    fprintf(fp,"\n");
    	    nr--;
    	    p = p->mh_next;
    	}
        }
    #endif /* MEM_LIST */    
    }
    
    /**
     * xmlMemDisplay:
     * @fp:  a FILE descriptor used as the output file, if NULL, the result is
     *       written to the file .memorylist
     *
     * show in-extenso the memory blocks allocated
     */
    
    void
    xmlMemDisplay(FILE *fp)
    {
    #ifdef MEM_LIST
        MEMHDR *p;
        int     idx;
    #if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
        time_t currentTime;
        char buf[500];
        struct tm * tstruct;
    
        currentTime = time(NULL);
        tstruct = localtime(&currentTime);
        strftime(buf, sizeof(buf) - 1, "%c", tstruct);
        fprintf(fp,"      %s\n\n", buf);
    #endif
    
        
        fprintf(fp,"      MEMORY ALLOCATED : %lu, MAX was %lu\n",
                debugMemSize, debugMaxMemSize);
        fprintf(fp,"BLOCK  NUMBER   SIZE  TYPE\n");
        idx = 0;
        p = memlist;
        while (p) {
    	  fprintf(fp,"%-5u  %6lu %6u ",idx++,p->mh_number,p->mh_size);
            switch (p->mh_type) {
               case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
               case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
              case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
                        default:fprintf(fp,"   ???    in ");break;
            }
    	  if (p->mh_file != NULL) fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
            if (p->mh_tag != MEMTAG)
    	      fprintf(fp,"  INVALID");
    	xmlMemContentShow(fp, p);
            fprintf(fp,"\n");
            p = p->mh_next;
        }
    #else
        fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
    #endif
    }
    
    #ifdef MEM_LIST
    
    void debugmem_list_add(MEMHDR *p)
    {
         p->mh_next = memlist;
         p->mh_prev = NULL;
         if (memlist) memlist->mh_prev = p;
         memlist = p;
    #ifdef MEM_LIST_DEBUG
         if (stderr)
         Mem_Display(stderr);
    #endif
    }
    
    void debugmem_list_delete(MEMHDR *p)
    {
         if (p->mh_next)
         p->mh_next->mh_prev = p->mh_prev;
         if (p->mh_prev)
         p->mh_prev->mh_next = p->mh_next;
         else memlist = p->mh_next;
    #ifdef MEM_LIST_DEBUG
         if (stderr)
         Mem_Display(stderr);
    #endif
    }
    
    #endif
    
    /*
     * debugmem_tag_error : internal error function.
     */
     
    void debugmem_tag_error(void *p)
    {
         fprintf(stderr, "Memory tag error occurs :%p \n\t bye\n", p);
    #ifdef MEM_LIST
         if (stderr)
         xmlMemDisplay(stderr);
    #endif
    }
    
    FILE *xmlMemoryDumpFile = NULL;
    
    
    /**
     * xmlMemoryDump:
     *
     * Dump in-extenso the memory blocks allocated to the file .memorylist
     */
    
    void
    xmlMemoryDump(void)
    {
        FILE *dump;
    
        dump = fopen(".memdump", "w");
        if (dump == NULL) xmlMemoryDumpFile = stdout;
        else xmlMemoryDumpFile = dump;
    
        xmlMemDisplay(xmlMemoryDumpFile);
    
        if (dump != NULL) fclose(dump);
    }
    
    
    /****************************************************************
     *								*
     *		Initialization Routines				*
     *								*
     ****************************************************************/
    
    /**
     * xmlInitMemory:
     *
     * Initialize the memory layer.
     *
     * Returns 0 on success
     */
    
    
    int
    xmlInitMemory(void)
    {
         int ret;
         
    #ifdef HAVE_STDLIB_H
         char *breakpoint;
    
         breakpoint = getenv("XML_MEM_BREAKPOINT");
         if (breakpoint != NULL) {
             sscanf(breakpoint, "%d", &xmlMemStopAtBlock);
         }
    #endif     
        
    #ifdef DEBUG_MEMORY
         fprintf(stderr, "xmlInitMemory() Ok\n");
    #endif     
         ret = 0;
         return(ret);
    }
    
    #endif /* ! NO_DEBUG_MEMORY */