tclCkalloc.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:33k
源码类别:

通讯编程

开发平台:

Visual C++

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