tclCkalloc.c

Go to the documentation of this file.
00001 /*
00002  * tclCkalloc.c --
00003  *
00004  *    Interface to malloc and free that provides support for debugging
00005  *    problems involving overwritten, double freeing memory and loss of
00006  *    memory.
00007  *
00008  * Copyright (c) 1991-1994 The Regents of the University of California.
00009  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
00010  * Copyright (c) 1998-1999 by Scriptics Corporation.
00011  *
00012  * See the file "license.terms" for information on usage and redistribution of
00013  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00014  *
00015  * This code contributed by Karl Lehenbauer and Mark Diekhans
00016  *
00017  * RCS: @(#) $Id: tclCkalloc.c,v 1.32 2007/04/23 20:33:56 das Exp $
00018  */
00019 
00020 #include "tclInt.h"
00021 
00022 #define FALSE   0
00023 #define TRUE    1
00024 
00025 #ifdef TCL_MEM_DEBUG
00026 
00027 /*
00028  * One of the following structures is allocated each time the
00029  * "memory tag" command is invoked, to hold the current tag.
00030  */
00031 
00032 typedef struct MemTag {
00033     int refCount;               /* Number of mem_headers referencing this
00034                                  * tag. */
00035     char string[4];             /* Actual size of string will be as large as
00036                                  * needed for actual tag. This must be the
00037                                  * last field in the structure. */
00038 } MemTag;
00039 
00040 #define TAG_SIZE(bytesInString) ((unsigned) sizeof(MemTag) + bytesInString - 3)
00041 
00042 static MemTag *curTagPtr = NULL;/* Tag to use in all future mem_headers (set
00043                                  * by "memory tag" command). */
00044 
00045 /*
00046  * One of the following structures is allocated just before each dynamically
00047  * allocated chunk of memory, both to record information about the chunk and
00048  * to help detect chunk under-runs.
00049  */
00050 
00051 #define LOW_GUARD_SIZE (8 + (32 - (sizeof(long) + sizeof(int)))%8)
00052 struct mem_header {
00053     struct mem_header *flink;
00054     struct mem_header *blink;
00055     MemTag *tagPtr;             /* Tag from "memory tag" command; may be
00056                                  * NULL. */
00057     CONST char *file;
00058     long length;
00059     int line;
00060     unsigned char low_guard[LOW_GUARD_SIZE];
00061                                 /* Aligns body on 8-byte boundary, plus
00062                                  * provides at least 8 additional guard bytes
00063                                  * to detect underruns. */
00064     char body[1];               /* First byte of client's space. Actual size
00065                                  * of this field will be larger than one. */
00066 };
00067 
00068 static struct mem_header *allocHead = NULL;  /* List of allocated structures */
00069 
00070 #define GUARD_VALUE  0141
00071 
00072 /*
00073  * The following macro determines the amount of guard space *above* each chunk
00074  * of memory.
00075  */
00076 
00077 #define HIGH_GUARD_SIZE 8
00078 
00079 /*
00080  * The following macro computes the offset of the "body" field within
00081  * mem_header. It is used to get back to the header pointer from the body
00082  * pointer that's used by clients.
00083  */
00084 
00085 #define BODY_OFFSET \
00086         ((unsigned long) (&((struct mem_header *) 0)->body))
00087 
00088 static int total_mallocs = 0;
00089 static int total_frees = 0;
00090 static int current_bytes_malloced = 0;
00091 static int maximum_bytes_malloced = 0;
00092 static int current_malloc_packets = 0;
00093 static int maximum_malloc_packets = 0;
00094 static int break_on_malloc = 0;
00095 static int trace_on_at_malloc = 0;
00096 static int alloc_tracing = FALSE;
00097 static int init_malloced_bodies = TRUE;
00098 #ifdef MEM_VALIDATE
00099 static int validate_memory = TRUE;
00100 #else
00101 static int validate_memory = FALSE;
00102 #endif
00103 
00104 /*
00105  * The following variable indicates to TclFinalizeMemorySubsystem() that it
00106  * should dump out the state of memory before exiting. If the value is
00107  * non-NULL, it gives the name of the file in which to dump memory usage
00108  * information.
00109  */
00110 
00111 char *tclMemDumpFileName = NULL;
00112 
00113 static char *onExitMemDumpFileName = NULL;
00114 static char dumpFile[100];      /* Records where to dump memory allocation
00115                                  * information. */
00116 
00117 /*
00118  * Mutex to serialize allocations. This is a low-level mutex that must be
00119  * explicitly initialized. This is necessary because the self initializing
00120  * mutexes use ckalloc...
00121  */
00122 
00123 static Tcl_Mutex *ckallocMutexPtr;
00124 static int ckallocInit = 0;
00125 
00126 /*
00127  * Prototypes for procedures defined in this file:
00128  */
00129 
00130 static int              CheckmemCmd(ClientData clientData, Tcl_Interp *interp,
00131                             int argc, CONST char *argv[]);
00132 static int              MemoryCmd(ClientData clientData, Tcl_Interp *interp,
00133                             int argc, CONST char *argv[]);
00134 static void             ValidateMemory(struct mem_header *memHeaderP,
00135                             CONST char *file, int line, int nukeGuards);
00136 
00137 /*
00138  *----------------------------------------------------------------------
00139  *
00140  * TclInitDbCkalloc --
00141  *
00142  *      Initialize the locks used by the allocator. This is only appropriate
00143  *      to call in a single threaded environment, such as during
00144  *      TclInitSubsystems.
00145  *
00146  *----------------------------------------------------------------------
00147  */
00148 
00149 void
00150 TclInitDbCkalloc(void)
00151 {
00152     if (!ckallocInit) {
00153         ckallocInit = 1;
00154         ckallocMutexPtr = Tcl_GetAllocMutex();
00155     }
00156 }
00157 
00158 /*
00159  *----------------------------------------------------------------------
00160  *
00161  * TclDumpMemoryInfo --
00162  *
00163  *      Display the global memory management statistics.
00164  *
00165  *----------------------------------------------------------------------
00166  */
00167 
00168 void
00169 TclDumpMemoryInfo(
00170     FILE *outFile)
00171 {
00172     fprintf(outFile,"total mallocs             %10d\n",
00173             total_mallocs);
00174     fprintf(outFile,"total frees               %10d\n",
00175             total_frees);
00176     fprintf(outFile,"current packets allocated %10d\n",
00177             current_malloc_packets);
00178     fprintf(outFile,"current bytes allocated   %10d\n",
00179             current_bytes_malloced);
00180     fprintf(outFile,"maximum packets allocated %10d\n",
00181             maximum_malloc_packets);
00182     fprintf(outFile,"maximum bytes allocated   %10d\n",
00183             maximum_bytes_malloced);
00184 }
00185 
00186 /*
00187  *----------------------------------------------------------------------
00188  *
00189  * ValidateMemory --
00190  *
00191  *      Validate memory guard zones for a particular chunk of allocated
00192  *      memory.
00193  *
00194  * Results:
00195  *      None.
00196  *
00197  * Side effects:
00198  *      Prints validation information about the allocated memory to stderr.
00199  *
00200  *----------------------------------------------------------------------
00201  */
00202 
00203 static void
00204 ValidateMemory(
00205     struct mem_header *memHeaderP,
00206                                 /* Memory chunk to validate */
00207     CONST char *file,           /* File containing the call to
00208                                  * Tcl_ValidateAllMemory */
00209     int line,                   /* Line number of call to
00210                                  * Tcl_ValidateAllMemory */
00211     int nukeGuards)             /* If non-zero, indicates that the memory
00212                                  * guards are to be reset to 0 after they have
00213                                  * been printed */
00214 {
00215     unsigned char *hiPtr;
00216     size_t idx;
00217     int guard_failed = FALSE;
00218     int byte;
00219 
00220     for (idx = 0; idx < LOW_GUARD_SIZE; idx++) {
00221         byte = *(memHeaderP->low_guard + idx);
00222         if (byte != GUARD_VALUE) {
00223             guard_failed = TRUE;
00224             fflush(stdout);
00225             byte &= 0xff;
00226             fprintf(stderr, "low guard byte %d is 0x%x  \t%c\n", (int)idx, byte,
00227                     (isprint(UCHAR(byte)) ? byte : ' ')); /* INTL: bytes */
00228         }
00229     }
00230     if (guard_failed) {
00231         TclDumpMemoryInfo (stderr);
00232         fprintf(stderr, "low guard failed at %lx, %s %d\n",
00233                 (long unsigned int) memHeaderP->body, file, line);
00234         fflush(stderr);                 /* In case name pointer is bad. */
00235         fprintf(stderr, "%ld bytes allocated at (%s %d)\n", memHeaderP->length,
00236                 memHeaderP->file, memHeaderP->line);
00237         Tcl_Panic("Memory validation failure");
00238     }
00239 
00240     hiPtr = (unsigned char *)memHeaderP->body + memHeaderP->length;
00241     for (idx = 0; idx < HIGH_GUARD_SIZE; idx++) {
00242         byte = *(hiPtr + idx);
00243         if (byte != GUARD_VALUE) {
00244             guard_failed = TRUE;
00245             fflush(stdout);
00246             byte &= 0xff;
00247             fprintf(stderr, "hi guard byte %d is 0x%x  \t%c\n", (int)idx, byte,
00248                     (isprint(UCHAR(byte)) ? byte : ' ')); /* INTL: bytes */
00249         }
00250     }
00251 
00252     if (guard_failed) {
00253         TclDumpMemoryInfo(stderr);
00254         fprintf(stderr, "high guard failed at %lx, %s %d\n",
00255                 (long unsigned int) memHeaderP->body, file, line);
00256         fflush(stderr);                 /* In case name pointer is bad. */
00257         fprintf(stderr, "%ld bytes allocated at (%s %d)\n",
00258                 memHeaderP->length, memHeaderP->file,
00259                 memHeaderP->line);
00260         Tcl_Panic("Memory validation failure");
00261     }
00262 
00263     if (nukeGuards) {
00264         memset(memHeaderP->low_guard, 0, LOW_GUARD_SIZE);
00265         memset(hiPtr, 0, HIGH_GUARD_SIZE);
00266     }
00267 
00268 }
00269 
00270 /*
00271  *----------------------------------------------------------------------
00272  *
00273  * Tcl_ValidateAllMemory --
00274  *
00275  *      Validate memory guard regions for all allocated memory.
00276  *
00277  * Results:
00278  *      None.
00279  *
00280  * Side effects:
00281  *      Displays memory validation information to stderr.
00282  *
00283  *----------------------------------------------------------------------
00284  */
00285 
00286 void
00287 Tcl_ValidateAllMemory(
00288     CONST char *file,           /* File from which Tcl_ValidateAllMemory was
00289                                  * called. */
00290     int line)                   /* Line number of call to
00291                                  * Tcl_ValidateAllMemory */
00292 {
00293     struct mem_header *memScanP;
00294 
00295     if (!ckallocInit) {
00296         TclInitDbCkalloc();
00297     }
00298     Tcl_MutexLock(ckallocMutexPtr);
00299     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
00300         ValidateMemory(memScanP, file, line, FALSE);
00301     }
00302     Tcl_MutexUnlock(ckallocMutexPtr);
00303 }
00304 
00305 /*
00306  *----------------------------------------------------------------------
00307  *
00308  * Tcl_DumpActiveMemory --
00309  *
00310  *      Displays all allocated memory to a file; if no filename is given,
00311  *      information will be written to stderr.
00312  *
00313  * Results:
00314  *      Return TCL_ERROR if an error accessing the file occurs, `errno' will
00315  *      have the file error number left in it.
00316  *
00317  *----------------------------------------------------------------------
00318  */
00319 
00320 int
00321 Tcl_DumpActiveMemory(
00322     CONST char *fileName)       /* Name of the file to write info to */
00323 {
00324     FILE *fileP;
00325     struct mem_header *memScanP;
00326     char *address;
00327 
00328     if (fileName == NULL) {
00329         fileP = stderr;
00330     } else {
00331         fileP = fopen(fileName, "w");
00332         if (fileP == NULL) {
00333             return TCL_ERROR;
00334         }
00335     }
00336 
00337     Tcl_MutexLock(ckallocMutexPtr);
00338     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
00339         address = &memScanP->body [0];
00340         fprintf(fileP, "%8lx - %8lx  %7ld @ %s %d %s",
00341                 (long unsigned int) address,
00342                 (long unsigned int) address + memScanP->length - 1,
00343                 memScanP->length, memScanP->file, memScanP->line,
00344                 (memScanP->tagPtr == NULL) ? "" : memScanP->tagPtr->string);
00345         (void) fputc('\n', fileP);
00346     }
00347     Tcl_MutexUnlock(ckallocMutexPtr);
00348 
00349     if (fileP != stderr) {
00350         fclose(fileP);
00351     }
00352     return TCL_OK;
00353 }
00354 
00355 /*
00356  *----------------------------------------------------------------------
00357  *
00358  * Tcl_DbCkalloc - debugging ckalloc
00359  *
00360  *      Allocate the requested amount of space plus some extra for guard bands
00361  *      at both ends of the request, plus a size, panicing if there isn't
00362  *      enough space, then write in the guard bands and return the address of
00363  *      the space in the middle that the user asked for.
00364  *
00365  *      The second and third arguments are file and line, these contain the
00366  *      filename and line number corresponding to the caller. These are sent
00367  *      by the ckalloc macro; it uses the preprocessor autodefines __FILE__
00368  *      and __LINE__.
00369  *
00370  *----------------------------------------------------------------------
00371  */
00372 
00373 char *
00374 Tcl_DbCkalloc(
00375     unsigned int size,
00376     CONST char *file,
00377     int line)
00378 {
00379     struct mem_header *result;
00380 
00381     if (validate_memory) {
00382         Tcl_ValidateAllMemory(file, line);
00383     }
00384 
00385     result = (struct mem_header *) TclpAlloc((unsigned)size +
00386             sizeof(struct mem_header) + HIGH_GUARD_SIZE);
00387     if (result == NULL) {
00388         fflush(stdout);
00389         TclDumpMemoryInfo(stderr);
00390         Tcl_Panic("unable to alloc %u bytes, %s line %d", size, file, line);
00391     }
00392 
00393     /*
00394      * Fill in guard zones and size. Also initialize the contents of the block
00395      * with bogus bytes to detect uses of initialized data. Link into
00396      * allocated list.
00397      */
00398 
00399     if (init_malloced_bodies) {
00400         memset(result, GUARD_VALUE,
00401                 size + sizeof(struct mem_header) + HIGH_GUARD_SIZE);
00402     } else {
00403         memset(result->low_guard, GUARD_VALUE, LOW_GUARD_SIZE);
00404         memset(result->body + size, GUARD_VALUE, HIGH_GUARD_SIZE);
00405     }
00406     if (!ckallocInit) {
00407         TclInitDbCkalloc();
00408     }
00409     Tcl_MutexLock(ckallocMutexPtr);
00410     result->length = size;
00411     result->tagPtr = curTagPtr;
00412     if (curTagPtr != NULL) {
00413         curTagPtr->refCount++;
00414     }
00415     result->file = file;
00416     result->line = line;
00417     result->flink = allocHead;
00418     result->blink = NULL;
00419 
00420     if (allocHead != NULL) {
00421         allocHead->blink = result;
00422     }
00423     allocHead = result;
00424 
00425     total_mallocs++;
00426     if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
00427         (void) fflush(stdout);
00428         fprintf(stderr, "reached malloc trace enable point (%d)\n",
00429                 total_mallocs);
00430         fflush(stderr);
00431         alloc_tracing = TRUE;
00432         trace_on_at_malloc = 0;
00433     }
00434 
00435     if (alloc_tracing) {
00436         fprintf(stderr,"ckalloc %lx %u %s %d\n",
00437                 (long unsigned int) result->body, size, file, line);
00438     }
00439 
00440     if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
00441         break_on_malloc = 0;
00442         (void) fflush(stdout);
00443         fprintf(stderr,"reached malloc break limit (%d)\n",
00444                 total_mallocs);
00445         fprintf(stderr, "program will now enter C debugger\n");
00446         (void) fflush(stderr);
00447         abort();
00448     }
00449 
00450     current_malloc_packets++;
00451     if (current_malloc_packets > maximum_malloc_packets) {
00452         maximum_malloc_packets = current_malloc_packets;
00453     }
00454     current_bytes_malloced += size;
00455     if (current_bytes_malloced > maximum_bytes_malloced) {
00456         maximum_bytes_malloced = current_bytes_malloced;
00457     }
00458 
00459     Tcl_MutexUnlock(ckallocMutexPtr);
00460 
00461     return result->body;
00462 }
00463 
00464 char *
00465 Tcl_AttemptDbCkalloc(
00466     unsigned int size,
00467     CONST char *file,
00468     int line)
00469 {
00470     struct mem_header *result;
00471 
00472     if (validate_memory) {
00473         Tcl_ValidateAllMemory(file, line);
00474     }
00475 
00476     result = (struct mem_header *) TclpAlloc((unsigned)size +
00477             sizeof(struct mem_header) + HIGH_GUARD_SIZE);
00478     if (result == NULL) {
00479         fflush(stdout);
00480         TclDumpMemoryInfo(stderr);
00481         return NULL;
00482     }
00483 
00484     /*
00485      * Fill in guard zones and size. Also initialize the contents of the block
00486      * with bogus bytes to detect uses of initialized data. Link into
00487      * allocated list.
00488      */
00489     if (init_malloced_bodies) {
00490         memset(result, GUARD_VALUE,
00491                 size + sizeof(struct mem_header) + HIGH_GUARD_SIZE);
00492     } else {
00493         memset(result->low_guard, GUARD_VALUE, LOW_GUARD_SIZE);
00494         memset(result->body + size, GUARD_VALUE, HIGH_GUARD_SIZE);
00495     }
00496     if (!ckallocInit) {
00497         TclInitDbCkalloc();
00498     }
00499     Tcl_MutexLock(ckallocMutexPtr);
00500     result->length = size;
00501     result->tagPtr = curTagPtr;
00502     if (curTagPtr != NULL) {
00503         curTagPtr->refCount++;
00504     }
00505     result->file = file;
00506     result->line = line;
00507     result->flink = allocHead;
00508     result->blink = NULL;
00509 
00510     if (allocHead != NULL) {
00511         allocHead->blink = result;
00512     }
00513     allocHead = result;
00514 
00515     total_mallocs++;
00516     if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
00517         (void) fflush(stdout);
00518         fprintf(stderr, "reached malloc trace enable point (%d)\n",
00519                 total_mallocs);
00520         fflush(stderr);
00521         alloc_tracing = TRUE;
00522         trace_on_at_malloc = 0;
00523     }
00524 
00525     if (alloc_tracing) {
00526         fprintf(stderr,"ckalloc %lx %u %s %d\n",
00527                 (long unsigned int) result->body, size, file, line);
00528     }
00529 
00530     if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
00531         break_on_malloc = 0;
00532         (void) fflush(stdout);
00533         fprintf(stderr,"reached malloc break limit (%d)\n",
00534                 total_mallocs);
00535         fprintf(stderr, "program will now enter C debugger\n");
00536         (void) fflush(stderr);
00537         abort();
00538     }
00539 
00540     current_malloc_packets++;
00541     if (current_malloc_packets > maximum_malloc_packets) {
00542         maximum_malloc_packets = current_malloc_packets;
00543     }
00544     current_bytes_malloced += size;
00545     if (current_bytes_malloced > maximum_bytes_malloced) {
00546         maximum_bytes_malloced = current_bytes_malloced;
00547     }
00548 
00549     Tcl_MutexUnlock(ckallocMutexPtr);
00550 
00551     return result->body;
00552 }
00553 
00554 /*
00555  *----------------------------------------------------------------------
00556  *
00557  * Tcl_DbCkfree - debugging ckfree
00558  *
00559  *      Verify that the low and high guards are intact, and if so then free
00560  *      the buffer else Tcl_Panic.
00561  *
00562  *      The guards are erased after being checked to catch duplicate frees.
00563  *
00564  *      The second and third arguments are file and line, these contain the
00565  *      filename and line number corresponding to the caller. These are sent
00566  *      by the ckfree macro; it uses the preprocessor autodefines __FILE__ and
00567  *      __LINE__.
00568  *
00569  *----------------------------------------------------------------------
00570  */
00571 
00572 int
00573 Tcl_DbCkfree(
00574     char *ptr,
00575     CONST char *file,
00576     int line)
00577 {
00578     struct mem_header *memp;
00579 
00580     if (ptr == NULL) {
00581         return 0;
00582     }
00583 
00584     /*
00585      * The following cast is *very* tricky. Must convert the pointer to an
00586      * integer before doing arithmetic on it, because otherwise the arithmetic
00587      * will be done differently (and incorrectly) on word-addressed machines
00588      * such as Crays (will subtract only bytes, even though BODY_OFFSET is in
00589      * words on these machines).
00590      */
00591 
00592     memp = (struct mem_header *) (((unsigned long) ptr) - BODY_OFFSET);
00593 
00594     if (alloc_tracing) {
00595         fprintf(stderr, "ckfree %lx %ld %s %d\n",
00596                 (long unsigned int) memp->body, memp->length, file, line);
00597     }
00598 
00599     if (validate_memory) {
00600         Tcl_ValidateAllMemory(file, line);
00601     }
00602 
00603     Tcl_MutexLock(ckallocMutexPtr);
00604     ValidateMemory(memp, file, line, TRUE);
00605     if (init_malloced_bodies) {
00606         memset(ptr, GUARD_VALUE, (size_t) memp->length);
00607     }
00608 
00609     total_frees++;
00610     current_malloc_packets--;
00611     current_bytes_malloced -= memp->length;
00612 
00613     if (memp->tagPtr != NULL) {
00614         memp->tagPtr->refCount--;
00615         if ((memp->tagPtr->refCount == 0) && (curTagPtr != memp->tagPtr)) {
00616             TclpFree((char *) memp->tagPtr);
00617         }
00618     }
00619 
00620     /*
00621      * Delink from allocated list
00622      */
00623 
00624     if (memp->flink != NULL) {
00625         memp->flink->blink = memp->blink;
00626     }
00627     if (memp->blink != NULL) {
00628         memp->blink->flink = memp->flink;
00629     }
00630     if (allocHead == memp) {
00631         allocHead = memp->flink;
00632     }
00633     TclpFree((char *) memp);
00634     Tcl_MutexUnlock(ckallocMutexPtr);
00635 
00636     return 0;
00637 }
00638 
00639 /*
00640  *--------------------------------------------------------------------
00641  *
00642  * Tcl_DbCkrealloc - debugging ckrealloc
00643  *
00644  *      Reallocate a chunk of memory by allocating a new one of the right
00645  *      size, copying the old data to the new location, and then freeing the
00646  *      old memory space, using all the memory checking features of this
00647  *      package.
00648  *
00649  *--------------------------------------------------------------------
00650  */
00651 
00652 char *
00653 Tcl_DbCkrealloc(
00654     char *ptr,
00655     unsigned int size,
00656     CONST char *file,
00657     int line)
00658 {
00659     char *newPtr;
00660     unsigned int copySize;
00661     struct mem_header *memp;
00662 
00663     if (ptr == NULL) {
00664         return Tcl_DbCkalloc(size, file, line);
00665     }
00666 
00667     /*
00668      * See comment from Tcl_DbCkfree before you change the following line.
00669      */
00670 
00671     memp = (struct mem_header *) (((unsigned long) ptr) - BODY_OFFSET);
00672 
00673     copySize = size;
00674     if (copySize > (unsigned int) memp->length) {
00675         copySize = memp->length;
00676     }
00677     newPtr = Tcl_DbCkalloc(size, file, line);
00678     memcpy(newPtr, ptr, (size_t) copySize);
00679     Tcl_DbCkfree(ptr, file, line);
00680     return newPtr;
00681 }
00682 
00683 char *
00684 Tcl_AttemptDbCkrealloc(
00685     char *ptr,
00686     unsigned int size,
00687     CONST char *file,
00688     int line)
00689 {
00690     char *newPtr;
00691     unsigned int copySize;
00692     struct mem_header *memp;
00693 
00694     if (ptr == NULL) {
00695         return Tcl_AttemptDbCkalloc(size, file, line);
00696     }
00697 
00698     /*
00699      * See comment from Tcl_DbCkfree before you change the following line.
00700      */
00701 
00702     memp = (struct mem_header *) (((unsigned long) ptr) - BODY_OFFSET);
00703 
00704     copySize = size;
00705     if (copySize > (unsigned int) memp->length) {
00706         copySize = memp->length;
00707     }
00708     newPtr = Tcl_AttemptDbCkalloc(size, file, line);
00709     if (newPtr == NULL) {
00710         return NULL;
00711     }
00712     memcpy(newPtr, ptr, (size_t) copySize);
00713     Tcl_DbCkfree(ptr, file, line);
00714     return newPtr;
00715 }
00716 
00717 
00718 /*
00719  *----------------------------------------------------------------------
00720  *
00721  * Tcl_Alloc, et al. --
00722  *
00723  *      These functions are defined in terms of the debugging versions when
00724  *      TCL_MEM_DEBUG is set.
00725  *
00726  * Results:
00727  *      Same as the debug versions.
00728  *
00729  * Side effects:
00730  *      Same as the debug versions.
00731  *
00732  *----------------------------------------------------------------------
00733  */
00734 
00735 #undef Tcl_Alloc
00736 #undef Tcl_Free
00737 #undef Tcl_Realloc
00738 #undef Tcl_AttemptAlloc
00739 #undef Tcl_AttemptRealloc
00740 
00741 char *
00742 Tcl_Alloc(
00743     unsigned int size)
00744 {
00745     return Tcl_DbCkalloc(size, "unknown", 0);
00746 }
00747 
00748 char *
00749 Tcl_AttemptAlloc(
00750     unsigned int size)
00751 {
00752     return Tcl_AttemptDbCkalloc(size, "unknown", 0);
00753 }
00754 
00755 void
00756 Tcl_Free(
00757     char *ptr)
00758 {
00759     Tcl_DbCkfree(ptr, "unknown", 0);
00760 }
00761 
00762 char *
00763 Tcl_Realloc(
00764     char *ptr,
00765     unsigned int size)
00766 {
00767     return Tcl_DbCkrealloc(ptr, size, "unknown", 0);
00768 }
00769 char *
00770 Tcl_AttemptRealloc(
00771     char *ptr,
00772     unsigned int size)
00773 {
00774     return Tcl_AttemptDbCkrealloc(ptr, size, "unknown", 0);
00775 }
00776 
00777 /*
00778  *----------------------------------------------------------------------
00779  *
00780  * MemoryCmd --
00781  *
00782  *      Implements the Tcl "memory" command, which provides Tcl-level control
00783  *      of Tcl memory debugging information.
00784  *              memory active $file
00785  *              memory break_on_malloc $count
00786  *              memory info
00787  *              memory init on|off
00788  *              memory onexit $file
00789  *              memory tag $string
00790  *              memory trace on|off
00791  *              memory trace_on_at_malloc $count
00792  *              memory validate on|off
00793  *
00794  * Results:
00795  *      Standard TCL results.
00796  *
00797  *----------------------------------------------------------------------
00798  */
00799         /* ARGSUSED */
00800 static int
00801 MemoryCmd(
00802     ClientData clientData,
00803     Tcl_Interp *interp,
00804     int argc,
00805     CONST char *argv[])
00806 {
00807     CONST char *fileName;
00808     Tcl_DString buffer;
00809     int result;
00810 
00811     if (argc < 2) {
00812         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00813                 " option [args..]\"", NULL);
00814         return TCL_ERROR;
00815     }
00816 
00817     if ((strcmp(argv[1],"active") == 0) || (strcmp(argv[1],"display") == 0)) {
00818         if (argc != 3) {
00819             Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00820                     " ", argv[1], " file\"", NULL);
00821             return TCL_ERROR;
00822         }
00823         fileName = Tcl_TranslateFileName(interp, argv[2], &buffer);
00824         if (fileName == NULL) {
00825             return TCL_ERROR;
00826         }
00827         result = Tcl_DumpActiveMemory (fileName);
00828         Tcl_DStringFree(&buffer);
00829         if (result != TCL_OK) {
00830             Tcl_AppendResult(interp, "error accessing ", argv[2], NULL);
00831             return TCL_ERROR;
00832         }
00833         return TCL_OK;
00834     }
00835     if (strcmp(argv[1],"break_on_malloc") == 0) {
00836         if (argc != 3) {
00837             goto argError;
00838         }
00839         if (Tcl_GetInt(interp, argv[2], &break_on_malloc) != TCL_OK) {
00840             return TCL_ERROR;
00841         }
00842         return TCL_OK;
00843     }
00844     if (strcmp(argv[1],"info") == 0) {
00845         Tcl_SetObjResult(interp, Tcl_ObjPrintf(
00846                 "%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n",
00847                 "total mallocs", total_mallocs, "total frees", total_frees,
00848                 "current packets allocated", current_malloc_packets,
00849                 "current bytes allocated", current_bytes_malloced,
00850                 "maximum packets allocated", maximum_malloc_packets,
00851                 "maximum bytes allocated", maximum_bytes_malloced));
00852         return TCL_OK;
00853     }
00854     if (strcmp(argv[1],"init") == 0) {
00855         if (argc != 3) {
00856             goto bad_suboption;
00857         }
00858         init_malloced_bodies = (strcmp(argv[2],"on") == 0);
00859         return TCL_OK;
00860     }
00861     if (strcmp(argv[1],"onexit") == 0) {
00862         if (argc != 3) {
00863             Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00864                     " onexit file\"", NULL);
00865             return TCL_ERROR;
00866         }
00867         fileName = Tcl_TranslateFileName(interp, argv[2], &buffer);
00868         if (fileName == NULL) {
00869             return TCL_ERROR;
00870         }
00871         onExitMemDumpFileName = dumpFile;
00872         strcpy(onExitMemDumpFileName,fileName);
00873         Tcl_DStringFree(&buffer);
00874         return TCL_OK;
00875     }
00876     if (strcmp(argv[1],"tag") == 0) {
00877         if (argc != 3) {
00878             Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00879                     " tag string\"", NULL);
00880             return TCL_ERROR;
00881         }
00882         if ((curTagPtr != NULL) && (curTagPtr->refCount == 0)) {
00883             TclpFree((char *) curTagPtr);
00884         }
00885         curTagPtr = (MemTag *) TclpAlloc(TAG_SIZE(strlen(argv[2])));
00886         curTagPtr->refCount = 0;
00887         strcpy(curTagPtr->string, argv[2]);
00888         return TCL_OK;
00889     }
00890     if (strcmp(argv[1],"trace") == 0) {
00891         if (argc != 3) {
00892             goto bad_suboption;
00893         }
00894         alloc_tracing = (strcmp(argv[2],"on") == 0);
00895         return TCL_OK;
00896     }
00897 
00898     if (strcmp(argv[1],"trace_on_at_malloc") == 0) {
00899         if (argc != 3) {
00900             goto argError;
00901         }
00902         if (Tcl_GetInt(interp, argv[2], &trace_on_at_malloc) != TCL_OK) {
00903             return TCL_ERROR;
00904         }
00905         return TCL_OK;
00906     }
00907     if (strcmp(argv[1],"validate") == 0) {
00908         if (argc != 3) {
00909             goto bad_suboption;
00910         }
00911         validate_memory = (strcmp(argv[2],"on") == 0);
00912         return TCL_OK;
00913     }
00914 
00915     Tcl_AppendResult(interp, "bad option \"", argv[1],
00916             "\": should be active, break_on_malloc, info, init, onexit, "
00917             "tag, trace, trace_on_at_malloc, or validate", NULL);
00918     return TCL_ERROR;
00919 
00920   argError:
00921     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00922             " ", argv[1], " count\"", NULL);
00923     return TCL_ERROR;
00924 
00925   bad_suboption:
00926     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00927             " ", argv[1], " on|off\"", NULL);
00928     return TCL_ERROR;
00929 }
00930 
00931 /*
00932  *----------------------------------------------------------------------
00933  *
00934  * CheckmemCmd --
00935  *
00936  *      This is the command procedure for the "checkmem" command, which causes
00937  *      the application to exit after printing information about memory usage
00938  *      to the file passed to this command as its first argument.
00939  *
00940  * Results:
00941  *      Returns a standard Tcl completion code.
00942  *
00943  * Side effects:
00944  *      None.
00945  *
00946  *----------------------------------------------------------------------
00947  */
00948 
00949 static int
00950 CheckmemCmd(
00951     ClientData clientData,      /* Not used. */
00952     Tcl_Interp *interp,         /* Interpreter for evaluation. */
00953     int argc,                   /* Number of arguments. */
00954     CONST char *argv[])         /* String values of arguments. */
00955 {
00956     if (argc != 2) {
00957         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
00958                 " fileName\"", NULL);
00959         return TCL_ERROR;
00960     }
00961     tclMemDumpFileName = dumpFile;
00962     strcpy(tclMemDumpFileName, argv[1]);
00963     return TCL_OK;
00964 }
00965 
00966 /*
00967  *----------------------------------------------------------------------
00968  *
00969  * Tcl_InitMemory --
00970  *
00971  *      Create the "memory" and "checkmem" commands in the given interpreter.
00972  *
00973  * Results:
00974  *      None.
00975  *
00976  * Side effects:
00977  *      New commands are added to the interpreter.
00978  *
00979  *----------------------------------------------------------------------
00980  */
00981 
00982 void
00983 Tcl_InitMemory(
00984     Tcl_Interp *interp)         /* Interpreter in which commands should be
00985                                  * added */
00986 {
00987     TclInitDbCkalloc();
00988     Tcl_CreateCommand(interp, "memory", MemoryCmd, (ClientData) NULL, NULL);
00989     Tcl_CreateCommand(interp, "checkmem", CheckmemCmd, (ClientData) 0, NULL);
00990 }
00991 
00992 
00993 #else   /* TCL_MEM_DEBUG */
00994 
00995 /* This is the !TCL_MEM_DEBUG case */
00996 
00997 #undef Tcl_InitMemory
00998 #undef Tcl_DumpActiveMemory
00999 #undef Tcl_ValidateAllMemory
01000 
01001 
01002 /*
01003  *----------------------------------------------------------------------
01004  *
01005  * Tcl_Alloc --
01006  *
01007  *      Interface to TclpAlloc when TCL_MEM_DEBUG is disabled. It does check
01008  *      that memory was actually allocated.
01009  *
01010  *----------------------------------------------------------------------
01011  */
01012 
01013 char *
01014 Tcl_Alloc(
01015     unsigned int size)
01016 {
01017     char *result;
01018 
01019     result = TclpAlloc(size);
01020 
01021     /*
01022      * Most systems will not alloc(0), instead bumping it to one so that NULL
01023      * isn't returned. Some systems (AIX, Tru64) will alloc(0) by returning
01024      * NULL, so we have to check that the NULL we get is not in response to
01025      * alloc(0).
01026      *
01027      * The ANSI spec actually says that systems either return NULL *or* a
01028      * special pointer on failure, but we only check for NULL
01029      */
01030 
01031     if ((result == NULL) && size) {
01032         Tcl_Panic("unable to alloc %u bytes", size);
01033     }
01034     return result;
01035 }
01036 
01037 char *
01038 Tcl_DbCkalloc(
01039     unsigned int size,
01040     CONST char *file,
01041     int line)
01042 {
01043     char *result;
01044 
01045     result = (char *) TclpAlloc(size);
01046 
01047     if ((result == NULL) && size) {
01048         fflush(stdout);
01049         Tcl_Panic("unable to alloc %u bytes, %s line %d", size, file, line);
01050     }
01051     return result;
01052 }
01053 
01054 /*
01055  *----------------------------------------------------------------------
01056  *
01057  * Tcl_AttemptAlloc --
01058  *
01059  *      Interface to TclpAlloc when TCL_MEM_DEBUG is disabled. It does not
01060  *      check that memory was actually allocated.
01061  *
01062  *----------------------------------------------------------------------
01063  */
01064 
01065 char *
01066 Tcl_AttemptAlloc(
01067     unsigned int size)
01068 {
01069     char *result;
01070 
01071     result = TclpAlloc(size);
01072     return result;
01073 }
01074 
01075 char *
01076 Tcl_AttemptDbCkalloc(
01077     unsigned int size,
01078     CONST char *file,
01079     int line)
01080 {
01081     char *result;
01082 
01083     result = (char *) TclpAlloc(size);
01084     return result;
01085 }
01086 
01087 /*
01088  *----------------------------------------------------------------------
01089  *
01090  * Tcl_Realloc --
01091  *
01092  *      Interface to TclpRealloc when TCL_MEM_DEBUG is disabled. It does check
01093  *      that memory was actually allocated.
01094  *
01095  *----------------------------------------------------------------------
01096  */
01097 
01098 char *
01099 Tcl_Realloc(
01100     char *ptr,
01101     unsigned int size)
01102 {
01103     char *result;
01104 
01105     result = TclpRealloc(ptr, size);
01106 
01107     if ((result == NULL) && size) {
01108         Tcl_Panic("unable to realloc %u bytes", size);
01109     }
01110     return result;
01111 }
01112 
01113 char *
01114 Tcl_DbCkrealloc(
01115     char *ptr,
01116     unsigned int size,
01117     CONST char *file,
01118     int line)
01119 {
01120     char *result;
01121 
01122     result = (char *) TclpRealloc(ptr, size);
01123 
01124     if ((result == NULL) && size) {
01125         fflush(stdout);
01126         Tcl_Panic("unable to realloc %u bytes, %s line %d", size, file, line);
01127     }
01128     return result;
01129 }
01130 
01131 /*
01132  *----------------------------------------------------------------------
01133  *
01134  * Tcl_AttemptRealloc --
01135  *
01136  *      Interface to TclpRealloc when TCL_MEM_DEBUG is disabled. It does not
01137  *      check that memory was actually allocated.
01138  *
01139  *----------------------------------------------------------------------
01140  */
01141 
01142 char *
01143 Tcl_AttemptRealloc(
01144     char *ptr,
01145     unsigned int size)
01146 {
01147     char *result;
01148 
01149     result = TclpRealloc(ptr, size);
01150     return result;
01151 }
01152 
01153 char *
01154 Tcl_AttemptDbCkrealloc(
01155     char *ptr,
01156     unsigned int size,
01157     CONST char *file,
01158     int line)
01159 {
01160     char *result;
01161 
01162     result = (char *) TclpRealloc(ptr, size);
01163     return result;
01164 }
01165 
01166 /*
01167  *----------------------------------------------------------------------
01168  *
01169  * Tcl_Free --
01170  *
01171  *      Interface to TclpFree when TCL_MEM_DEBUG is disabled. Done here rather
01172  *      in the macro to keep some modules from being compiled with
01173  *      TCL_MEM_DEBUG enabled and some with it disabled.
01174  *
01175  *----------------------------------------------------------------------
01176  */
01177 
01178 void
01179 Tcl_Free(
01180     char *ptr)
01181 {
01182     TclpFree(ptr);
01183 }
01184 
01185 int
01186 Tcl_DbCkfree(
01187     char *ptr,
01188     CONST char *file,
01189     int line)
01190 {
01191     TclpFree(ptr);
01192     return 0;
01193 }
01194 
01195 /*
01196  *----------------------------------------------------------------------
01197  *
01198  * Tcl_InitMemory --
01199  *
01200  *      Dummy initialization for memory command, which is only available if
01201  *      TCL_MEM_DEBUG is on.
01202  *
01203  *----------------------------------------------------------------------
01204  */
01205         /* ARGSUSED */
01206 void
01207 Tcl_InitMemory(
01208     Tcl_Interp *interp)
01209 {
01210 }
01211 
01212 int
01213 Tcl_DumpActiveMemory(
01214     CONST char *fileName)
01215 {
01216     return TCL_OK;
01217 }
01218 
01219 void
01220 Tcl_ValidateAllMemory(
01221     CONST char *file,
01222     int line)
01223 {
01224 }
01225 
01226 void
01227 TclDumpMemoryInfo(
01228     FILE *outFile)
01229 {
01230 }
01231 
01232 #endif  /* TCL_MEM_DEBUG */
01233 
01234 /*
01235  *---------------------------------------------------------------------------
01236  *
01237  * TclFinalizeMemorySubsystem --
01238  *
01239  *      This procedure is called to finalize all the structures that are used
01240  *      by the memory allocator on a per-process basis.
01241  *
01242  * Results:
01243  *      None.
01244  *
01245  * Side effects:
01246  *      This subsystem is self-initializing, since memory can be allocated
01247  *      before Tcl is formally initialized. After this call, this subsystem
01248  *      has been reset to its initial state and is usable again.
01249  *
01250  *---------------------------------------------------------------------------
01251  */
01252 
01253 void
01254 TclFinalizeMemorySubsystem(void)
01255 {
01256 #ifdef TCL_MEM_DEBUG
01257     if (tclMemDumpFileName != NULL) {
01258         Tcl_DumpActiveMemory(tclMemDumpFileName);
01259     } else if (onExitMemDumpFileName != NULL) {
01260         Tcl_DumpActiveMemory(onExitMemDumpFileName);
01261     }
01262 
01263     Tcl_MutexLock(ckallocMutexPtr);
01264 
01265     if (curTagPtr != NULL) {
01266         TclpFree((char *) curTagPtr);
01267         curTagPtr = NULL;
01268     }
01269     allocHead = NULL;
01270 
01271     Tcl_MutexUnlock(ckallocMutexPtr);
01272 #endif
01273 
01274 #if USE_TCLALLOC
01275     TclFinalizeAllocSubsystem();
01276 #endif
01277 }
01278 
01279 /*
01280  * Local Variables:
01281  * mode: c
01282  * c-basic-offset: 4
01283  * fill-column: 78
01284  * End:
01285  */



Generated on Wed Mar 12 12:18:12 2008 by  doxygen 1.5.1