os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclCkalloc.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /* 
     2  * tclCkalloc.c --
     3  *
     4  *    Interface to malloc and free that provides support for debugging problems
     5  *    involving overwritten, double freeing memory and loss of memory.
     6  *
     7  * Copyright (c) 1991-1994 The Regents of the University of California.
     8  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
     9  * Copyright (c) 1998-1999 by Scriptics Corporation.
    10  * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.  
    11  *
    12  * See the file "license.terms" for information on usage and redistribution
    13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    14  *
    15  * This code contributed by Karl Lehenbauer and Mark Diekhans
    16  *
    17  * RCS: @(#) $Id: tclCkalloc.c,v 1.19 2003/01/19 07:21:18 hobbs Exp $
    18  */
    19 
    20 #include "tclInt.h"
    21 #include "tclPort.h"
    22 
    23 #define FALSE	0
    24 #define TRUE	1
    25 
    26 #ifdef TCL_MEM_DEBUG
    27 
    28 /*
    29  * One of the following structures is allocated each time the
    30  * "memory tag" command is invoked, to hold the current tag.
    31  */
    32 
    33 typedef struct MemTag {
    34     int refCount;		/* Number of mem_headers referencing
    35 				 * this tag. */
    36     char string[4];		/* Actual size of string will be as
    37 				 * large as needed for actual tag.  This
    38 				 * must be the last field in the structure. */
    39 } MemTag;
    40 
    41 #define TAG_SIZE(bytesInString) ((unsigned) sizeof(MemTag) + bytesInString - 3)
    42 
    43 static MemTag *curTagPtr = NULL;/* Tag to use in all future mem_headers
    44 				 * (set by "memory tag" command). */
    45 
    46 /*
    47  * One of the following structures is allocated just before each
    48  * dynamically allocated chunk of memory, both to record information
    49  * about the chunk and to help detect chunk under-runs.
    50  */
    51 
    52 #define LOW_GUARD_SIZE (8 + (32 - (sizeof(long) + sizeof(int)))%8)
    53 struct mem_header {
    54     struct mem_header *flink;
    55     struct mem_header *blink;
    56     MemTag *tagPtr;		/* Tag from "memory tag" command;  may be
    57 				 * NULL. */
    58     CONST char *file;
    59     long length;
    60     int line;
    61     unsigned char low_guard[LOW_GUARD_SIZE];
    62 				/* Aligns body on 8-byte boundary, plus
    63 				 * provides at least 8 additional guard bytes
    64 				 * to detect underruns. */
    65     char body[1];		/* First byte of client's space.  Actual
    66 				 * size of this field will be larger than
    67 				 * one. */
    68 };
    69 
    70 #if !defined(__SYMBIAN32__) || !defined(__WINSCW__)
    71 static struct mem_header *allocHead = NULL;  /* List of allocated structures */
    72 #else
    73 typedef struct mem_header (*memHeaderPtr);
    74 #define allocHead (*(memHeaderPtr*)get_allocHead())
    75 #endif
    76 
    77 #define GUARD_VALUE  0141
    78 
    79 /*
    80  * The following macro determines the amount of guard space *above* each
    81  * chunk of memory.
    82  */
    83 
    84 #define HIGH_GUARD_SIZE 8
    85 
    86 /*
    87  * The following macro computes the offset of the "body" field within
    88  * mem_header.  It is used to get back to the header pointer from the
    89  * body pointer that's used by clients.
    90  */
    91 
    92 #define BODY_OFFSET \
    93 	((unsigned long) (&((struct mem_header *) 0)->body))
    94 
    95 static int total_mallocs = 0;
    96 static int total_frees = 0;
    97 static int current_bytes_malloced = 0;
    98 static int maximum_bytes_malloced = 0;
    99 static int current_malloc_packets = 0;
   100 static int maximum_malloc_packets = 0;
   101 static int break_on_malloc = 0;
   102 static int trace_on_at_malloc = 0;
   103 static int  alloc_tracing = FALSE;
   104 static int  init_malloced_bodies = TRUE;
   105 #ifdef MEM_VALIDATE
   106     static int  validate_memory = TRUE;
   107 #else
   108     static int  validate_memory = FALSE;
   109 #endif
   110 
   111 /*
   112  * The following variable indicates to TclFinalizeMemorySubsystem() 
   113  * that it should dump out the state of memory before exiting.  If the
   114  * value is non-NULL, it gives the name of the file in which to
   115  * dump memory usage information.
   116  */
   117 
   118 char *tclMemDumpFileName = NULL;
   119 
   120 static char *onExitMemDumpFileName = NULL;
   121 static char dumpFile[100];	/* Records where to dump memory allocation
   122 				 * information. */
   123 
   124 /*
   125  * Mutex to serialize allocations.  This is a low-level mutex that must
   126  * be explicitly initialized.  This is necessary because the self
   127  * initializing mutexes use ckalloc...
   128  */
   129 static Tcl_Mutex *ckallocMutexPtr;
   130 static int ckallocInit = 0;
   131 
   132 /*
   133  * Prototypes for procedures defined in this file:
   134  */
   135 
   136 static int		CheckmemCmd _ANSI_ARGS_((ClientData clientData,
   137 			    Tcl_Interp *interp, int argc, CONST char *argv[]));
   138 static int		MemoryCmd _ANSI_ARGS_((ClientData clientData,
   139 			    Tcl_Interp *interp, int argc, CONST char **argv));
   140 static void		ValidateMemory _ANSI_ARGS_((
   141 			    struct mem_header *memHeaderP, CONST char *file,
   142 			    int line, int nukeGuards));
   143 
   144 /*
   145  *----------------------------------------------------------------------
   146  *
   147  * TclInitDbCkalloc --
   148  *	Initialize the locks used by the allocator.
   149  *	This is only appropriate to call in a single threaded environment,
   150  *	such as during TclInitSubsystems.
   151  *
   152  *----------------------------------------------------------------------
   153  */
   154 void
   155 TclInitDbCkalloc() 
   156 {
   157     if (!ckallocInit) {
   158 	ckallocInit = 1;
   159 	ckallocMutexPtr = Tcl_GetAllocMutex();
   160     }
   161 }
   162 
   163 /*
   164  *----------------------------------------------------------------------
   165  *
   166  * TclDumpMemoryInfo --
   167  *     Display the global memory management statistics.
   168  *
   169  *----------------------------------------------------------------------
   170  */
   171 void
   172 TclDumpMemoryInfo(outFile) 
   173     FILE *outFile;
   174 {
   175     fprintf(outFile,"total mallocs             %10d\n", 
   176 	    total_mallocs);
   177     fprintf(outFile,"total frees               %10d\n", 
   178 	    total_frees);
   179     fprintf(outFile,"current packets allocated %10d\n", 
   180 	    current_malloc_packets);
   181     fprintf(outFile,"current bytes allocated   %10d\n", 
   182 	    current_bytes_malloced);
   183     fprintf(outFile,"maximum packets allocated %10d\n", 
   184 	    maximum_malloc_packets);
   185     fprintf(outFile,"maximum bytes allocated   %10d\n", 
   186 	    maximum_bytes_malloced);
   187 }
   188 
   189 
   190 /*
   191  *----------------------------------------------------------------------
   192  *
   193  * ValidateMemory --
   194  *
   195  *	Validate memory guard zones for a particular chunk of allocated
   196  *	memory.
   197  *
   198  * Results:
   199  *	None.
   200  *
   201  * Side effects:
   202  *	Prints validation information about the allocated memory to stderr.
   203  *
   204  *----------------------------------------------------------------------
   205  */
   206 
   207 static void
   208 ValidateMemory(memHeaderP, file, line, nukeGuards)
   209     struct mem_header *memHeaderP;	/* Memory chunk to validate */
   210     CONST char        *file;		/* File containing the call to
   211 					 * Tcl_ValidateAllMemory */
   212     int                line;		/* Line number of call to
   213 					 * Tcl_ValidateAllMemory */
   214     int                nukeGuards;	/* If non-zero, indicates that the
   215 					 * memory guards are to be reset to 0
   216 					 * after they have been printed */
   217 {
   218     unsigned char *hiPtr;
   219     int   idx;
   220     int   guard_failed = FALSE;
   221     int byte;
   222     
   223     for (idx = 0; idx < LOW_GUARD_SIZE; idx++) {
   224         byte = *(memHeaderP->low_guard + idx);
   225         if (byte != GUARD_VALUE) {
   226             guard_failed = TRUE;
   227             fflush(stdout);
   228 	    byte &= 0xff;
   229             fprintf(stderr, "low guard byte %d is 0x%x  \t%c\n", idx, byte,
   230 		    (isprint(UCHAR(byte)) ? byte : ' ')); /* INTL: bytes */
   231         }
   232     }
   233     if (guard_failed) {
   234         TclDumpMemoryInfo (stderr);
   235         fprintf(stderr, "low guard failed at %lx, %s %d\n",
   236                  (long unsigned int) memHeaderP->body, file, line);
   237         fflush(stderr);  /* In case name pointer is bad. */
   238         fprintf(stderr, "%ld bytes allocated at (%s %d)\n", memHeaderP->length,
   239 		memHeaderP->file, memHeaderP->line);
   240         panic ("Memory validation failure");
   241     }
   242 
   243     hiPtr = (unsigned char *)memHeaderP->body + memHeaderP->length;
   244     for (idx = 0; idx < HIGH_GUARD_SIZE; idx++) {
   245         byte = *(hiPtr + idx);
   246         if (byte != GUARD_VALUE) {
   247             guard_failed = TRUE;
   248             fflush (stdout);
   249 	    byte &= 0xff;
   250             fprintf(stderr, "hi guard byte %d is 0x%x  \t%c\n", idx, byte,
   251 		    (isprint(UCHAR(byte)) ? byte : ' ')); /* INTL: bytes */
   252         }
   253     }
   254 
   255     if (guard_failed) {
   256         TclDumpMemoryInfo (stderr);
   257         fprintf(stderr, "high guard failed at %lx, %s %d\n",
   258                  (long unsigned int) memHeaderP->body, file, line);
   259         fflush(stderr);  /* In case name pointer is bad. */
   260         fprintf(stderr, "%ld bytes allocated at (%s %d)\n",
   261 		memHeaderP->length, memHeaderP->file,
   262 		memHeaderP->line);
   263         panic("Memory validation failure");
   264     }
   265 
   266     if (nukeGuards) {
   267         memset ((char *) memHeaderP->low_guard, 0, LOW_GUARD_SIZE); 
   268         memset ((char *) hiPtr, 0, HIGH_GUARD_SIZE); 
   269     }
   270 
   271 }
   272 
   273 /*
   274  *----------------------------------------------------------------------
   275  *
   276  * Tcl_ValidateAllMemory --
   277  *
   278  *	Validate memory guard regions for all allocated memory.
   279  *
   280  * Results:
   281  *	None.
   282  *
   283  * Side effects:
   284  *	Displays memory validation information to stderr.
   285  *
   286  *----------------------------------------------------------------------
   287  */
   288 EXPORT_C void
   289 Tcl_ValidateAllMemory (file, line)
   290     CONST char  *file;	/* File from which Tcl_ValidateAllMemory was called */
   291     int          line;	/* Line number of call to Tcl_ValidateAllMemory */
   292 {
   293     struct mem_header *memScanP;
   294 
   295     if (!ckallocInit) {
   296 	TclInitDbCkalloc();
   297     }
   298     Tcl_MutexLock(ckallocMutexPtr);
   299     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
   300         ValidateMemory(memScanP, file, line, FALSE);
   301     }
   302     Tcl_MutexUnlock(ckallocMutexPtr);
   303 }
   304 
   305 /*
   306  *----------------------------------------------------------------------
   307  *
   308  * Tcl_DumpActiveMemory --
   309  *
   310  *	Displays all allocated memory to a file; if no filename is given,
   311  *	information will be written to stderr.
   312  *
   313  * Results:
   314  *	Return TCL_ERROR if an error accessing the file occurs, `errno' 
   315  *	will have the file error number left in it.
   316  *----------------------------------------------------------------------
   317  */
   318 EXPORT_C int
   319 Tcl_DumpActiveMemory (fileName)
   320     CONST char *fileName;		/* Name of the file to write info to */
   321 {
   322     FILE              *fileP;
   323     struct mem_header *memScanP;
   324     char              *address;
   325 
   326     if (fileName == NULL) {
   327 	fileP = stderr;
   328     } else {
   329 	fileP = fopen(fileName, "w");
   330 	if (fileP == NULL) {
   331 	    return TCL_ERROR;
   332 	}
   333     }
   334 
   335     Tcl_MutexLock(ckallocMutexPtr);
   336     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
   337         address = &memScanP->body [0];
   338         fprintf(fileP, "%8lx - %8lx  %7ld @ %s %d %s",
   339 		(long unsigned int) address,
   340                  (long unsigned int) address + memScanP->length - 1,
   341 		 memScanP->length, memScanP->file, memScanP->line,
   342 		 (memScanP->tagPtr == NULL) ? "" : memScanP->tagPtr->string);
   343 	(void) fputc('\n', fileP);
   344     }
   345     Tcl_MutexUnlock(ckallocMutexPtr);
   346 
   347     if (fileP != stderr) {
   348 	fclose (fileP);
   349     }
   350     return TCL_OK;
   351 }
   352 
   353 /*
   354  *----------------------------------------------------------------------
   355  *
   356  * Tcl_DbCkalloc - debugging ckalloc
   357  *
   358  *        Allocate the requested amount of space plus some extra for
   359  *        guard bands at both ends of the request, plus a size, panicing 
   360  *        if there isn't enough space, then write in the guard bands
   361  *        and return the address of the space in the middle that the
   362  *        user asked for.
   363  *
   364  *        The second and third arguments are file and line, these contain
   365  *        the filename and line number corresponding to the caller.
   366  *        These are sent by the ckalloc macro; it uses the preprocessor
   367  *        autodefines __FILE__ and __LINE__.
   368  *
   369  *----------------------------------------------------------------------
   370  */
   371 EXPORT_C char *
   372 Tcl_DbCkalloc(size, file, line)
   373     unsigned int size;
   374     CONST char  *file;
   375     int          line;
   376 {
   377     struct mem_header *result;
   378 
   379     if (validate_memory)
   380         Tcl_ValidateAllMemory (file, line);
   381 
   382     result = (struct mem_header *) TclpAlloc((unsigned)size + 
   383                               sizeof(struct mem_header) + HIGH_GUARD_SIZE);
   384     if (result == NULL) {
   385         fflush(stdout);
   386         TclDumpMemoryInfo(stderr);
   387         panic("unable to alloc %u bytes, %s line %d", size, file, line);
   388     }
   389 
   390     /*
   391      * Fill in guard zones and size.  Also initialize the contents of
   392      * the block with bogus bytes to detect uses of initialized data.
   393      * Link into allocated list.
   394      */
   395     if (init_malloced_bodies) {
   396         memset ((VOID *) result, GUARD_VALUE,
   397 		size + sizeof(struct mem_header) + HIGH_GUARD_SIZE);
   398     } else {
   399 	memset ((char *) result->low_guard, GUARD_VALUE, LOW_GUARD_SIZE);
   400 	memset (result->body + size, GUARD_VALUE, HIGH_GUARD_SIZE);
   401     }
   402     if (!ckallocInit) {
   403 	TclInitDbCkalloc();
   404     }
   405     Tcl_MutexLock(ckallocMutexPtr);
   406     result->length = size;
   407     result->tagPtr = curTagPtr;
   408     if (curTagPtr != NULL) {
   409 	curTagPtr->refCount++;
   410     }
   411     result->file = file;
   412     result->line = line;
   413     result->flink = allocHead;
   414     result->blink = NULL;
   415 
   416     if (allocHead != NULL)
   417         allocHead->blink = result;
   418     allocHead = result;
   419 
   420     total_mallocs++;
   421     if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
   422         (void) fflush(stdout);
   423         fprintf(stderr, "reached malloc trace enable point (%d)\n",
   424                 total_mallocs);
   425         fflush(stderr);
   426         alloc_tracing = TRUE;
   427         trace_on_at_malloc = 0;
   428     }
   429 
   430     if (alloc_tracing)
   431         fprintf(stderr,"ckalloc %lx %u %s %d\n",
   432 		(long unsigned int) result->body, size, file, line);
   433 
   434     if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
   435         break_on_malloc = 0;
   436         (void) fflush(stdout);
   437         fprintf(stderr,"reached malloc break limit (%d)\n", 
   438                 total_mallocs);
   439         fprintf(stderr, "program will now enter C debugger\n");
   440         (void) fflush(stderr);
   441 	abort();
   442     }
   443 
   444     current_malloc_packets++;
   445     if (current_malloc_packets > maximum_malloc_packets)
   446         maximum_malloc_packets = current_malloc_packets;
   447     current_bytes_malloced += size;
   448     if (current_bytes_malloced > maximum_bytes_malloced)
   449         maximum_bytes_malloced = current_bytes_malloced;
   450 
   451     Tcl_MutexUnlock(ckallocMutexPtr);
   452 
   453     return result->body;
   454 }
   455 
   456 char *
   457 Tcl_AttemptDbCkalloc(size, file, line)
   458     unsigned int size;
   459     CONST char  *file;
   460     int          line;
   461 {
   462     struct mem_header *result;
   463 
   464     if (validate_memory)
   465         Tcl_ValidateAllMemory (file, line);
   466 
   467     result = (struct mem_header *) TclpAlloc((unsigned)size + 
   468                               sizeof(struct mem_header) + HIGH_GUARD_SIZE);
   469     if (result == NULL) {
   470         fflush(stdout);
   471         TclDumpMemoryInfo(stderr);
   472 	return NULL;
   473     }
   474 
   475     /*
   476      * Fill in guard zones and size.  Also initialize the contents of
   477      * the block with bogus bytes to detect uses of initialized data.
   478      * Link into allocated list.
   479      */
   480     if (init_malloced_bodies) {
   481         memset ((VOID *) result, GUARD_VALUE,
   482 		size + sizeof(struct mem_header) + HIGH_GUARD_SIZE);
   483     } else {
   484 	memset ((char *) result->low_guard, GUARD_VALUE, LOW_GUARD_SIZE);
   485 	memset (result->body + size, GUARD_VALUE, HIGH_GUARD_SIZE);
   486     }
   487     if (!ckallocInit) {
   488 	TclInitDbCkalloc();
   489     }
   490     Tcl_MutexLock(ckallocMutexPtr);
   491     result->length = size;
   492     result->tagPtr = curTagPtr;
   493     if (curTagPtr != NULL) {
   494 	curTagPtr->refCount++;
   495     }
   496     result->file = file;
   497     result->line = line;
   498     result->flink = allocHead;
   499     result->blink = NULL;
   500 
   501     if (allocHead != NULL)
   502         allocHead->blink = result;
   503     allocHead = result;
   504 
   505     total_mallocs++;
   506     if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
   507         (void) fflush(stdout);
   508         fprintf(stderr, "reached malloc trace enable point (%d)\n",
   509                 total_mallocs);
   510         fflush(stderr);
   511         alloc_tracing = TRUE;
   512         trace_on_at_malloc = 0;
   513     }
   514 
   515     if (alloc_tracing)
   516         fprintf(stderr,"ckalloc %lx %u %s %d\n",
   517 		(long unsigned int) result->body, size, file, line);
   518 
   519     if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
   520         break_on_malloc = 0;
   521         (void) fflush(stdout);
   522         fprintf(stderr,"reached malloc break limit (%d)\n", 
   523                 total_mallocs);
   524         fprintf(stderr, "program will now enter C debugger\n");
   525         (void) fflush(stderr);
   526 	abort();
   527     }
   528 
   529     current_malloc_packets++;
   530     if (current_malloc_packets > maximum_malloc_packets)
   531         maximum_malloc_packets = current_malloc_packets;
   532     current_bytes_malloced += size;
   533     if (current_bytes_malloced > maximum_bytes_malloced)
   534         maximum_bytes_malloced = current_bytes_malloced;
   535 
   536     Tcl_MutexUnlock(ckallocMutexPtr);
   537 
   538     return result->body;
   539 }
   540 
   541 
   542 /*
   543  *----------------------------------------------------------------------
   544  *
   545  * Tcl_DbCkfree - debugging ckfree
   546  *
   547  *        Verify that the low and high guards are intact, and if so
   548  *        then free the buffer else panic.
   549  *
   550  *        The guards are erased after being checked to catch duplicate
   551  *        frees.
   552  *
   553  *        The second and third arguments are file and line, these contain
   554  *        the filename and line number corresponding to the caller.
   555  *        These are sent by the ckfree macro; it uses the preprocessor
   556  *        autodefines __FILE__ and __LINE__.
   557  *
   558  *----------------------------------------------------------------------
   559  */
   560 
   561 EXPORT_C int
   562 Tcl_DbCkfree(ptr, file, line)
   563     char       *ptr;
   564     CONST char *file;
   565     int         line;
   566 {
   567     struct mem_header *memp;
   568 
   569     if (ptr == NULL) {
   570 	return 0;
   571     }
   572 
   573     /*
   574      * The following cast is *very* tricky.  Must convert the pointer
   575      * to an integer before doing arithmetic on it, because otherwise
   576      * the arithmetic will be done differently (and incorrectly) on
   577      * word-addressed machines such as Crays (will subtract only bytes,
   578      * even though BODY_OFFSET is in words on these machines).
   579      */
   580 
   581     memp = (struct mem_header *) (((unsigned long) ptr) - BODY_OFFSET);
   582 
   583     if (alloc_tracing) {
   584         fprintf(stderr, "ckfree %lx %ld %s %d\n",
   585 		(long unsigned int) memp->body, memp->length, file, line);
   586     }
   587 
   588     if (validate_memory) {
   589         Tcl_ValidateAllMemory(file, line);
   590     }
   591 
   592     Tcl_MutexLock(ckallocMutexPtr);
   593     ValidateMemory(memp, file, line, TRUE);
   594     if (init_malloced_bodies) {
   595 	memset((VOID *) ptr, GUARD_VALUE, (size_t) memp->length);
   596     }
   597 
   598     total_frees++;
   599     current_malloc_packets--;
   600     current_bytes_malloced -= memp->length;
   601 
   602     if (memp->tagPtr != NULL) {
   603 	memp->tagPtr->refCount--;
   604 	if ((memp->tagPtr->refCount == 0) && (curTagPtr != memp->tagPtr)) {
   605 	    TclpFree((char *) memp->tagPtr);
   606 	}
   607     }
   608 
   609     /*
   610      * Delink from allocated list
   611      */
   612     if (memp->flink != NULL)
   613         memp->flink->blink = memp->blink;
   614     if (memp->blink != NULL)
   615         memp->blink->flink = memp->flink;
   616     if (allocHead == memp)
   617         allocHead = memp->flink;
   618     TclpFree((char *) memp);
   619     Tcl_MutexUnlock(ckallocMutexPtr);
   620 
   621     return 0;
   622 }
   623 
   624 /*
   625  *--------------------------------------------------------------------
   626  *
   627  * Tcl_DbCkrealloc - debugging ckrealloc
   628  *
   629  *	Reallocate a chunk of memory by allocating a new one of the
   630  *	right size, copying the old data to the new location, and then
   631  *	freeing the old memory space, using all the memory checking
   632  *	features of this package.
   633  *
   634  *--------------------------------------------------------------------
   635  */
   636 EXPORT_C char *
   637 Tcl_DbCkrealloc(ptr, size, file, line)
   638     char        *ptr;
   639     unsigned int size;
   640     CONST char  *file;
   641     int          line;
   642 {
   643     char *new;
   644     unsigned int copySize;
   645     struct mem_header *memp;
   646 
   647     if (ptr == NULL) {
   648 	return Tcl_DbCkalloc(size, file, line);
   649     }
   650 
   651     /*
   652      * See comment from Tcl_DbCkfree before you change the following
   653      * line.
   654      */
   655 
   656     memp = (struct mem_header *) (((unsigned long) ptr) - BODY_OFFSET);
   657 
   658     copySize = size;
   659     if (copySize > (unsigned int) memp->length) {
   660 	copySize = memp->length;
   661     }
   662     new = Tcl_DbCkalloc(size, file, line);
   663     memcpy((VOID *) new, (VOID *) ptr, (size_t) copySize);
   664     Tcl_DbCkfree(ptr, file, line);
   665     return new;
   666 }
   667 
   668 char *
   669 Tcl_AttemptDbCkrealloc(ptr, size, file, line)
   670     char        *ptr;
   671     unsigned int size;
   672     CONST char  *file;
   673     int          line;
   674 {
   675     char *new;
   676     unsigned int copySize;
   677     struct mem_header *memp;
   678 
   679     if (ptr == NULL) {
   680 	return Tcl_AttemptDbCkalloc(size, file, line);
   681     }
   682 
   683     /*
   684      * See comment from Tcl_DbCkfree before you change the following
   685      * line.
   686      */
   687 
   688     memp = (struct mem_header *) (((unsigned long) ptr) - BODY_OFFSET);
   689 
   690     copySize = size;
   691     if (copySize > (unsigned int) memp->length) {
   692 	copySize = memp->length;
   693     }
   694     new = Tcl_AttemptDbCkalloc(size, file, line);
   695     if (new == NULL) {
   696 	return NULL;
   697     }
   698     memcpy((VOID *) new, (VOID *) ptr, (size_t) copySize);
   699     Tcl_DbCkfree(ptr, file, line);
   700     return new;
   701 }
   702 
   703 
   704 /*
   705  *----------------------------------------------------------------------
   706  *
   707  * Tcl_Alloc, et al. --
   708  *
   709  *	These functions are defined in terms of the debugging versions
   710  *	when TCL_MEM_DEBUG is set.
   711  *
   712  * Results:
   713  *	Same as the debug versions.
   714  *
   715  * Side effects:
   716  *	Same as the debug versions.
   717  *
   718  *----------------------------------------------------------------------
   719  */
   720 
   721 #undef Tcl_Alloc
   722 #undef Tcl_Free
   723 #undef Tcl_Realloc
   724 #undef Tcl_AttemptAlloc
   725 #undef Tcl_AttemptRealloc
   726 
   727 EXPORT_C char *
   728 Tcl_Alloc(size)
   729     unsigned int size;
   730 {
   731     return Tcl_DbCkalloc(size, "unknown", 0);
   732 }
   733 
   734 char *
   735 Tcl_AttemptAlloc(size)
   736     unsigned int size;
   737 {
   738     return Tcl_AttemptDbCkalloc(size, "unknown", 0);
   739 }
   740 
   741 EXPORT_C void
   742 Tcl_Free(ptr)
   743     char *ptr;
   744 {
   745     Tcl_DbCkfree(ptr, "unknown", 0);
   746 }
   747 
   748 EXPORT_C char *
   749 Tcl_Realloc(ptr, size)
   750     char *ptr;
   751     unsigned int size;
   752 {
   753     return Tcl_DbCkrealloc(ptr, size, "unknown", 0);
   754 }
   755 char *
   756 Tcl_AttemptRealloc(ptr, size)
   757     char *ptr;
   758     unsigned int size;
   759 {
   760     return Tcl_AttemptDbCkrealloc(ptr, size, "unknown", 0);
   761 }
   762 
   763 /*
   764  *----------------------------------------------------------------------
   765  *
   766  * MemoryCmd --
   767  *	Implements the Tcl "memory" command, which provides Tcl-level
   768  *	control of Tcl memory debugging information.
   769  *		memory active $file
   770  *		memory break_on_malloc $count
   771  *		memory info
   772  *		memory init on|off
   773  *		memory onexit $file
   774  *		memory tag $string
   775  *		memory trace on|off
   776  *		memory trace_on_at_malloc $count
   777  *		memory validate on|off
   778  *
   779  * Results:
   780  *     Standard TCL results.
   781  *
   782  *----------------------------------------------------------------------
   783  */
   784 	/* ARGSUSED */
   785 static int
   786 MemoryCmd (clientData, interp, argc, argv)
   787     ClientData  clientData;
   788     Tcl_Interp *interp;
   789     int         argc;
   790     CONST char  **argv;
   791 {
   792     CONST char *fileName;
   793     Tcl_DString buffer;
   794     int result;
   795 
   796     if (argc < 2) {
   797 	Tcl_AppendResult(interp, "wrong # args: should be \"",
   798 		argv[0], " option [args..]\"", (char *) NULL);
   799 	return TCL_ERROR;
   800     }
   801 
   802     if ((strcmp(argv[1],"active") == 0) || (strcmp(argv[1],"display") == 0)) {
   803         if (argc != 3) {
   804 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
   805 		    argv[0], " ", argv[1], " file\"", (char *) NULL);
   806 	    return TCL_ERROR;
   807 	}
   808 	fileName = Tcl_TranslateFileName(interp, argv[2], &buffer);
   809 	if (fileName == NULL) {
   810 	    return TCL_ERROR;
   811 	}
   812 	result = Tcl_DumpActiveMemory (fileName);
   813 	Tcl_DStringFree(&buffer);
   814 	if (result != TCL_OK) {
   815 	    Tcl_AppendResult(interp, "error accessing ", argv[2], 
   816 		    (char *) NULL);
   817 	    return TCL_ERROR;
   818 	}
   819 	return TCL_OK;
   820     }
   821     if (strcmp(argv[1],"break_on_malloc") == 0) {
   822         if (argc != 3) {
   823             goto argError;
   824 	}
   825         if (Tcl_GetInt(interp, argv[2], &break_on_malloc) != TCL_OK) {
   826 	    return TCL_ERROR;
   827 	}
   828         return TCL_OK;
   829     }
   830     if (strcmp(argv[1],"info") == 0) {
   831 	char buf[400];
   832 	sprintf(buf, "%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n",
   833 	    "total mallocs", total_mallocs, "total frees", total_frees,
   834 	    "current packets allocated", current_malloc_packets,
   835 	    "current bytes allocated", current_bytes_malloced,
   836 	    "maximum packets allocated", maximum_malloc_packets,
   837 	    "maximum bytes allocated", maximum_bytes_malloced);
   838 	Tcl_SetResult(interp, buf, TCL_VOLATILE);
   839         return TCL_OK;
   840     }
   841     if (strcmp(argv[1],"init") == 0) {
   842         if (argc != 3) {
   843             goto bad_suboption;
   844 	}
   845         init_malloced_bodies = (strcmp(argv[2],"on") == 0);
   846         return TCL_OK;
   847     }
   848     if (strcmp(argv[1],"onexit") == 0) {
   849         if (argc != 3) {
   850 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
   851 		    argv[0], " onexit file\"", (char *) NULL);
   852 	    return TCL_ERROR;
   853 	}
   854 	fileName = Tcl_TranslateFileName(interp, argv[2], &buffer);
   855 	if (fileName == NULL) {
   856 	    return TCL_ERROR;
   857 	}
   858 	onExitMemDumpFileName = dumpFile;
   859 	strcpy(onExitMemDumpFileName,fileName);
   860 	Tcl_DStringFree(&buffer);
   861 	return TCL_OK;
   862     }
   863     if (strcmp(argv[1],"tag") == 0) {
   864 	if (argc != 3) {
   865 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   866 		    " tag string\"", (char *) NULL);
   867 	    return TCL_ERROR;
   868 	}
   869 	if ((curTagPtr != NULL) && (curTagPtr->refCount == 0)) {
   870 	    TclpFree((char *) curTagPtr);
   871 	}
   872 	curTagPtr = (MemTag *) TclpAlloc(TAG_SIZE(strlen(argv[2])));
   873 	curTagPtr->refCount = 0;
   874 	strcpy(curTagPtr->string, argv[2]);
   875 	return TCL_OK;
   876     }
   877     if (strcmp(argv[1],"trace") == 0) {
   878         if (argc != 3) {
   879             goto bad_suboption;
   880 	}
   881         alloc_tracing = (strcmp(argv[2],"on") == 0);
   882         return TCL_OK;
   883     }
   884 
   885     if (strcmp(argv[1],"trace_on_at_malloc") == 0) {
   886         if (argc != 3) {
   887             goto argError;
   888 	}
   889         if (Tcl_GetInt(interp, argv[2], &trace_on_at_malloc) != TCL_OK) {
   890 	    return TCL_ERROR;
   891 	}
   892 	return TCL_OK;
   893     }
   894     if (strcmp(argv[1],"validate") == 0) {
   895         if (argc != 3) {
   896 	    goto bad_suboption;
   897 	}
   898         validate_memory = (strcmp(argv[2],"on") == 0);
   899         return TCL_OK;
   900     }
   901 
   902     Tcl_AppendResult(interp, "bad option \"", argv[1],
   903 	    "\": should be active, break_on_malloc, info, init, onexit, ",
   904 	    "tag, trace, trace_on_at_malloc, or validate", (char *) NULL);
   905     return TCL_ERROR;
   906 
   907 argError:
   908     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   909 	    " ", argv[1], " count\"", (char *) NULL);
   910     return TCL_ERROR;
   911 
   912 bad_suboption:
   913     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   914 	    " ", argv[1], " on|off\"", (char *) NULL);
   915     return TCL_ERROR;
   916 }
   917 
   918 /*
   919  *----------------------------------------------------------------------
   920  *
   921  * CheckmemCmd --
   922  *
   923  *	This is the command procedure for the "checkmem" command, which
   924  *	causes the application to exit after printing information about
   925  *	memory usage to the file passed to this command as its first
   926  *	argument.
   927  *
   928  * Results:
   929  *	Returns a standard Tcl completion code.
   930  *
   931  * Side effects:
   932  *	None.
   933  *
   934  *----------------------------------------------------------------------
   935  */
   936 
   937 static int
   938 CheckmemCmd(clientData, interp, argc, argv)
   939     ClientData clientData;		/* Not used. */
   940     Tcl_Interp *interp;			/* Interpreter for evaluation. */
   941     int argc;				/* Number of arguments. */
   942     CONST char *argv[];			/* String values of arguments. */
   943 {
   944     if (argc != 2) {
   945 	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   946 		" fileName\"", (char *) NULL);
   947 	return TCL_ERROR;
   948     }
   949     tclMemDumpFileName = dumpFile;
   950     strcpy(tclMemDumpFileName, argv[1]);
   951     return TCL_OK;
   952 }
   953 
   954 /*
   955  *----------------------------------------------------------------------
   956  *
   957  * Tcl_InitMemory --
   958  *
   959  *	Create the "memory" and "checkmem" commands in the given
   960  *	interpreter.
   961  *
   962  * Results:
   963  *	None.
   964  *
   965  * Side effects:
   966  *	New commands are added to the interpreter.
   967  *
   968  *----------------------------------------------------------------------
   969  */
   970 
   971 EXPORT_C void
   972 Tcl_InitMemory(interp)
   973     Tcl_Interp *interp;	/* Interpreter in which commands should be added */
   974 {
   975     TclInitDbCkalloc();
   976     Tcl_CreateCommand (interp, "memory", MemoryCmd, (ClientData) NULL, 
   977 	    (Tcl_CmdDeleteProc *) NULL);
   978     Tcl_CreateCommand(interp, "checkmem", CheckmemCmd, (ClientData) 0,
   979 	    (Tcl_CmdDeleteProc *) NULL);
   980 }
   981 
   982 
   983 #else	/* TCL_MEM_DEBUG */
   984 
   985 /* This is the !TCL_MEM_DEBUG case */
   986 
   987 #undef Tcl_InitMemory
   988 #undef Tcl_DumpActiveMemory
   989 #undef Tcl_ValidateAllMemory
   990 
   991 
   992 /*
   993  *----------------------------------------------------------------------
   994  *
   995  * Tcl_Alloc --
   996  *     Interface to TclpAlloc when TCL_MEM_DEBUG is disabled.  It does check
   997  *     that memory was actually allocated.
   998  *
   999  *----------------------------------------------------------------------
  1000  */
  1001 
  1002 EXPORT_C char *
  1003 Tcl_Alloc (size)
  1004     unsigned int size;
  1005 {
  1006     char *result;
  1007 
  1008     result = TclpAlloc(size);
  1009     /*
  1010      * Most systems will not alloc(0), instead bumping it to one so
  1011      * that NULL isn't returned.  Some systems (AIX, Tru64) will alloc(0)
  1012      * by returning NULL, so we have to check that the NULL we get is
  1013      * not in response to alloc(0).
  1014      *
  1015      * The ANSI spec actually says that systems either return NULL *or*
  1016      * a special pointer on failure, but we only check for NULL
  1017      */
  1018     if ((result == NULL) && size) {
  1019 	panic("unable to alloc %u bytes", size);
  1020     }
  1021     return result;
  1022 }
  1023 
  1024 EXPORT_C char *
  1025 Tcl_DbCkalloc(size, file, line)
  1026     unsigned int size;
  1027     CONST char  *file;
  1028     int          line;
  1029 {
  1030     char *result;
  1031 
  1032     result = (char *) TclpAlloc(size);
  1033 
  1034     if ((result == NULL) && size) {
  1035         fflush(stdout);
  1036         panic("unable to alloc %u bytes, %s line %d", size, file, line);
  1037     }
  1038     return result;
  1039 }
  1040 
  1041 /*
  1042  *----------------------------------------------------------------------
  1043  *
  1044  * Tcl_AttemptAlloc --
  1045  *     Interface to TclpAlloc when TCL_MEM_DEBUG is disabled.  It does not
  1046  *     check that memory was actually allocated.
  1047  *
  1048  *----------------------------------------------------------------------
  1049  */
  1050 
  1051 EXPORT_C char *
  1052 Tcl_AttemptAlloc (size)
  1053     unsigned int size;
  1054 {
  1055     char *result;
  1056 
  1057     result = TclpAlloc(size);
  1058     return result;
  1059 }
  1060 
  1061 EXPORT_C char *
  1062 Tcl_AttemptDbCkalloc(size, file, line)
  1063     unsigned int size;
  1064     CONST char  *file;
  1065     int          line;
  1066 {
  1067     char *result;
  1068 
  1069     result = (char *) TclpAlloc(size);
  1070     return result;
  1071 }
  1072 
  1073 
  1074 /*
  1075  *----------------------------------------------------------------------
  1076  *
  1077  * Tcl_Realloc --
  1078  *     Interface to TclpRealloc when TCL_MEM_DEBUG is disabled.  It does 
  1079  *     check that memory was actually allocated.
  1080  *
  1081  *----------------------------------------------------------------------
  1082  */
  1083 
  1084 EXPORT_C char *
  1085 Tcl_Realloc(ptr, size)
  1086     char *ptr;
  1087     unsigned int size;
  1088 {
  1089     char *result;
  1090 
  1091     result = TclpRealloc(ptr, size);
  1092 
  1093     if ((result == NULL) && size) {
  1094 	panic("unable to realloc %u bytes", size);
  1095     }
  1096     return result;
  1097 }
  1098 
  1099 EXPORT_C char *
  1100 Tcl_DbCkrealloc(ptr, size, file, line)
  1101     char        *ptr;
  1102     unsigned int size;
  1103     CONST char  *file;
  1104     int          line;
  1105 {
  1106     char *result;
  1107 
  1108     result = (char *) TclpRealloc(ptr, size);
  1109 
  1110     if ((result == NULL) && size) {
  1111         fflush(stdout);
  1112         panic("unable to realloc %u bytes, %s line %d", size, file, line);
  1113     }
  1114     return result;
  1115 }
  1116 
  1117 /*
  1118  *----------------------------------------------------------------------
  1119  *
  1120  * Tcl_AttemptRealloc --
  1121  *     Interface to TclpRealloc when TCL_MEM_DEBUG is disabled.  It does 
  1122  *     not check that memory was actually allocated.
  1123  *
  1124  *----------------------------------------------------------------------
  1125  */
  1126 
  1127 EXPORT_C char *
  1128 Tcl_AttemptRealloc(ptr, size)
  1129     char *ptr;
  1130     unsigned int size;
  1131 {
  1132     char *result;
  1133 
  1134     result = TclpRealloc(ptr, size);
  1135     return result;
  1136 }
  1137 
  1138 EXPORT_C char *
  1139 Tcl_AttemptDbCkrealloc(ptr, size, file, line)
  1140     char        *ptr;
  1141     unsigned int size;
  1142     CONST char  *file;
  1143     int          line;
  1144 {
  1145     char *result;
  1146 
  1147     result = (char *) TclpRealloc(ptr, size);
  1148     return result;
  1149 }
  1150 
  1151 /*
  1152  *----------------------------------------------------------------------
  1153  *
  1154  * Tcl_Free --
  1155  *     Interface to TclpFree when TCL_MEM_DEBUG is disabled.  Done here
  1156  *     rather in the macro to keep some modules from being compiled with 
  1157  *     TCL_MEM_DEBUG enabled and some with it disabled.
  1158  *
  1159  *----------------------------------------------------------------------
  1160  */
  1161 
  1162 EXPORT_C void
  1163 Tcl_Free (ptr)
  1164     char *ptr;
  1165 {
  1166     TclpFree(ptr);
  1167 }
  1168 
  1169 EXPORT_C int
  1170 Tcl_DbCkfree(ptr, file, line)
  1171     char       *ptr;
  1172     CONST char *file;
  1173     int         line;
  1174 {
  1175     TclpFree(ptr);
  1176     return 0;
  1177 }
  1178 
  1179 /*
  1180  *----------------------------------------------------------------------
  1181  *
  1182  * Tcl_InitMemory --
  1183  *     Dummy initialization for memory command, which is only available 
  1184  *     if TCL_MEM_DEBUG is on.
  1185  *
  1186  *----------------------------------------------------------------------
  1187  */
  1188 	/* ARGSUSED */
  1189 EXPORT_C void
  1190 Tcl_InitMemory(interp)
  1191     Tcl_Interp *interp;
  1192 {
  1193 }
  1194 
  1195 EXPORT_C int
  1196 Tcl_DumpActiveMemory(fileName)
  1197     CONST char *fileName;
  1198 {
  1199     return TCL_OK;
  1200 }
  1201 
  1202 EXPORT_C void
  1203 Tcl_ValidateAllMemory(file, line)
  1204     CONST char *file;
  1205     int         line;
  1206 {
  1207 }
  1208 
  1209 EXPORT_C void
  1210 TclDumpMemoryInfo(outFile) 
  1211     FILE *outFile;
  1212 {
  1213 }
  1214 
  1215 #endif	/* TCL_MEM_DEBUG */
  1216 
  1217 /*
  1218  *---------------------------------------------------------------------------
  1219  *
  1220  * TclFinalizeMemorySubsystem --
  1221  *
  1222  *	This procedure is called to finalize all the structures that 
  1223  *	are used by the memory allocator on a per-process basis.
  1224  *
  1225  * Results:
  1226  *	None.
  1227  *
  1228  * Side effects:
  1229  *	This subsystem is self-initializing, since memory can be 
  1230  *	allocated before Tcl is formally initialized.  After this call,
  1231  *	this subsystem has been reset to its initial state and is 
  1232  *	usable again.
  1233  *
  1234  *---------------------------------------------------------------------------
  1235  */
  1236 
  1237 void
  1238 TclFinalizeMemorySubsystem()
  1239 {
  1240 #ifdef TCL_MEM_DEBUG
  1241     if (tclMemDumpFileName != NULL) {
  1242 	Tcl_DumpActiveMemory(tclMemDumpFileName);
  1243     } else if (onExitMemDumpFileName != NULL) {
  1244 	Tcl_DumpActiveMemory(onExitMemDumpFileName);
  1245     }
  1246     Tcl_MutexLock(ckallocMutexPtr);
  1247     if (curTagPtr != NULL) {
  1248 	TclpFree((char *) curTagPtr);
  1249 	curTagPtr = NULL;
  1250     }
  1251     allocHead = NULL;
  1252     Tcl_MutexUnlock(ckallocMutexPtr);
  1253 #endif
  1254 
  1255 #if USE_TCLALLOC
  1256     TclFinalizeAllocSubsystem(); 
  1257 #endif
  1258 }