home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tcl2-73c.zip / tcl7.3 / tclCkalloc.c < prev    next >
C/C++ Source or Header  |  1993-09-09  |  18KB  |  608 lines

  1. /* 
  2.  * tclCkalloc.c --
  3.  *    Interface to malloc and free that provides support for debugging problems
  4.  *    involving overwritten, double freeing memory and loss of memory.
  5.  *
  6.  * Copyright (c) 1991-1993 The Regents of the University of California.
  7.  * All rights reserved.
  8.  *
  9.  * Permission is hereby granted, without written agreement and without
  10.  * license or royalty fees, to use, copy, modify, and distribute this
  11.  * software and its documentation for any purpose, provided that the
  12.  * above copyright notice and the following two paragraphs appear in
  13.  * all copies of this software.
  14.  * 
  15.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  16.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  17.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  18.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  19.  *
  20.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  21.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  22.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  23.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  24.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  25.  *
  26.  * This code contributed by Karl Lehenbauer and Mark Diekhans
  27.  *
  28.  */
  29.  
  30. #include "tclInt.h"
  31.  
  32. #define FALSE    0
  33. #define TRUE    1
  34.  
  35. #ifdef TCL_MEM_DEBUG
  36. #ifndef TCL_GENERIC_ONLY
  37. #include "tclUnix.h"
  38. #endif
  39.  
  40. #define GUARD_SIZE 8
  41.  
  42. struct mem_header {
  43.         long               length;
  44.         char              *file;
  45.         int                line;
  46.         struct mem_header *flink;
  47.         struct mem_header *blink;
  48.     int           dummy;    /* Aligns body on 8-byte boundary. */
  49.         unsigned char      low_guard[GUARD_SIZE];
  50.         char               body[1];
  51. };
  52.  
  53. static struct mem_header *allocHead = NULL;  /* List of allocated structures */
  54.  
  55. #define GUARD_VALUE  0341
  56.  
  57. /* static char high_guard[] = {0x89, 0xab, 0xcd, 0xef}; */
  58.  
  59. static int total_mallocs = 0;
  60. static int total_frees = 0;
  61. static int current_bytes_malloced = 0;
  62. static int maximum_bytes_malloced = 0;
  63. static int current_malloc_packets = 0;
  64. static int maximum_malloc_packets = 0;
  65. static int break_on_malloc = 0;
  66. static int trace_on_at_malloc = 0;
  67. static int  alloc_tracing = FALSE;
  68. static int  init_malloced_bodies = TRUE;
  69. #ifdef MEM_VALIDATE
  70.     static int  validate_memory = TRUE;
  71. #else
  72.     static int  validate_memory = FALSE;
  73. #endif
  74.  
  75. /*
  76.  * Prototypes for procedures defined in this file:
  77.  */
  78.  
  79. static int        MemoryCmd _ANSI_ARGS_((ClientData clientData,
  80.                 Tcl_Interp *interp, int argc, char **argv));
  81.  
  82. /*
  83.  *----------------------------------------------------------------------
  84.  *
  85.  * dump_memory_info --
  86.  *     Display the global memory management statistics.
  87.  *
  88.  *----------------------------------------------------------------------
  89.  */
  90. static void
  91. dump_memory_info(outFile) 
  92.     FILE *outFile;
  93. {
  94.         fprintf(outFile,"total mallocs             %10d\n", 
  95.                 total_mallocs);
  96.         fprintf(outFile,"total frees               %10d\n", 
  97.                 total_frees);
  98.         fprintf(outFile,"current packets allocated %10d\n", 
  99.                 current_malloc_packets);
  100.         fprintf(outFile,"current bytes allocated   %10d\n", 
  101.                 current_bytes_malloced);
  102.         fprintf(outFile,"maximum packets allocated %10d\n", 
  103.                 maximum_malloc_packets);
  104.         fprintf(outFile,"maximum bytes allocated   %10d\n", 
  105.                 maximum_bytes_malloced);
  106. }
  107.  
  108. /*
  109.  *----------------------------------------------------------------------
  110.  *
  111.  * ValidateMemory --
  112.  *     Procedure to validate allocted memory guard zones.
  113.  *
  114.  *----------------------------------------------------------------------
  115.  */
  116. static void
  117. ValidateMemory (memHeaderP, file, line, nukeGuards)
  118.     struct mem_header *memHeaderP;
  119.     char              *file;
  120.     int                line;
  121.     int                nukeGuards;
  122. {
  123.     unsigned char *hiPtr;
  124.     int   idx;
  125.     int   guard_failed = FALSE;
  126.     int byte;
  127.     
  128.     for (idx = 0; idx < GUARD_SIZE; idx++) {
  129.         byte = *(memHeaderP->low_guard + idx);
  130.         if (byte != GUARD_VALUE) {
  131.             guard_failed = TRUE;
  132.             fflush (stdout);
  133.         byte &= 0xff;
  134.             fprintf(stderr, "low guard byte %d is 0x%x  \t%c\n", idx, byte,
  135.             (isprint(UCHAR(byte)) ? byte : ' '));
  136.         }
  137.     }
  138.     if (guard_failed) {
  139.         dump_memory_info (stderr);
  140.         fprintf (stderr, "low guard failed at %lx, %s %d\n",
  141.                  memHeaderP->body, file, line);
  142.         fflush (stderr);  /* In case name pointer is bad. */
  143.         fprintf (stderr, "%d bytes allocated at (%s %d)\n", memHeaderP->length,
  144.         memHeaderP->file, memHeaderP->line);
  145.         panic ("Memory validation failure");
  146.     }
  147.  
  148.     hiPtr = (unsigned char *)memHeaderP->body + memHeaderP->length;
  149.     for (idx = 0; idx < GUARD_SIZE; idx++) {
  150.         byte = *(hiPtr + idx);
  151.         if (byte != GUARD_VALUE) {
  152.             guard_failed = TRUE;
  153.             fflush (stdout);
  154.         byte &= 0xff;
  155.             fprintf(stderr, "hi guard byte %d is 0x%x  \t%c\n", idx, byte,
  156.             (isprint(UCHAR(byte)) ? byte : ' '));
  157.         }
  158.     }
  159.  
  160.     if (guard_failed) {
  161.         dump_memory_info (stderr);
  162.         fprintf (stderr, "high guard failed at %lx, %s %d\n",
  163.                  memHeaderP->body, file, line);
  164.         fflush (stderr);  /* In case name pointer is bad. */
  165.         fprintf (stderr, "%d bytes allocated at (%s %d)\n", memHeaderP->length,
  166.         memHeaderP->file, memHeaderP->line);
  167.         panic ("Memory validation failure");
  168.     }
  169.  
  170.     if (nukeGuards) {
  171.         memset ((char *) memHeaderP->low_guard, 0, GUARD_SIZE); 
  172.         memset ((char *) hiPtr, 0, GUARD_SIZE); 
  173.     }
  174.  
  175. }
  176.  
  177. /*
  178.  *----------------------------------------------------------------------
  179.  *
  180.  * Tcl_ValidateAllMemory --
  181.  *     Validates guard regions for all allocated memory.
  182.  *
  183.  *----------------------------------------------------------------------
  184.  */
  185. void
  186. Tcl_ValidateAllMemory (file, line)
  187.     char  *file;
  188.     int    line;
  189. {
  190.     struct mem_header *memScanP;
  191.  
  192.     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink)
  193.         ValidateMemory (memScanP, file, line, FALSE);
  194.  
  195. }
  196.  
  197. /*
  198.  *----------------------------------------------------------------------
  199.  *
  200.  * Tcl_DumpActiveMemory --
  201.  *     Displays all allocated memory to stderr.
  202.  *
  203.  * Results:
  204.  *     Return TCL_ERROR if an error accessing the file occures, `errno' 
  205.  *     will have the file error number left in it.
  206.  *----------------------------------------------------------------------
  207.  */
  208. int
  209. Tcl_DumpActiveMemory (fileName)
  210.     char *fileName;
  211. {
  212.     FILE              *fileP;
  213.     struct mem_header *memScanP;
  214.     char              *address;
  215.  
  216.     fileP = fopen (fileName, "w");
  217.     if (fileP == NULL)
  218.         return TCL_ERROR;
  219.  
  220.     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
  221.         address = &memScanP->body [0];
  222.         fprintf (fileP, "%8lx - %8lx  %7d @ %s %d", address,
  223.                  address + memScanP->length - 1, memScanP->length,
  224.                  memScanP->file, memScanP->line);
  225.     (void) fputc('\n', fileP);
  226.     }
  227.     fclose (fileP);
  228.     return TCL_OK;
  229. }
  230.  
  231. /*
  232.  *----------------------------------------------------------------------
  233.  *
  234.  * Tcl_DbCkalloc - debugging ckalloc
  235.  *
  236.  *        Allocate the requested amount of space plus some extra for
  237.  *        guard bands at both ends of the request, plus a size, panicing 
  238.  *        if there isn't enough space, then write in the guard bands
  239.  *        and return the address of the space in the middle that the
  240.  *        user asked for.
  241.  *
  242.  *        The second and third arguments are file and line, these contain
  243.  *        the filename and line number corresponding to the caller.
  244.  *        These are sent by the ckalloc macro; it uses the preprocessor
  245.  *        autodefines __FILE__ and __LINE__.
  246.  *
  247.  *----------------------------------------------------------------------
  248.  */
  249. char *
  250. Tcl_DbCkalloc(size, file, line)
  251.     unsigned int size;
  252.     char        *file;
  253.     int          line;
  254. {
  255.     struct mem_header *result;
  256.  
  257.     if (validate_memory)
  258.         Tcl_ValidateAllMemory (file, line);
  259.  
  260.     result = (struct mem_header *)malloc((unsigned)size + 
  261.                               sizeof(struct mem_header) + GUARD_SIZE);
  262.     if (result == NULL) {
  263.         fflush(stdout);
  264.         dump_memory_info(stderr);
  265.         panic("unable to alloc %d bytes, %s line %d", size, file, 
  266.               line);
  267.     }
  268.  
  269.     /*
  270.      * Fill in guard zones and size.  Also initialize the contents of
  271.      * the block with bogus bytes to detect uses of initialized data.
  272.      * Link into allocated list.
  273.      */
  274.     if (init_malloced_bodies) {
  275.         memset ((VOID *) result, GUARD_VALUE,
  276.         size + sizeof(struct mem_header) + GUARD_SIZE);
  277.     } else {
  278.     memset ((char *) result->low_guard, GUARD_VALUE, GUARD_SIZE);
  279.     memset (result->body + size, GUARD_VALUE, GUARD_SIZE);
  280.     }
  281.     result->length = size;
  282.     result->file = file;
  283.     result->line = line;
  284.     result->flink = allocHead;
  285.     result->blink = NULL;
  286.     if (allocHead != NULL)
  287.         allocHead->blink = result;
  288.     allocHead = result;
  289.  
  290.     total_mallocs++;
  291.     if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
  292.         (void) fflush(stdout);
  293.         fprintf(stderr, "reached malloc trace enable point (%d)\n",
  294.                 total_mallocs);
  295.         fflush(stderr);
  296.         alloc_tracing = TRUE;
  297.         trace_on_at_malloc = 0;
  298.     }
  299.  
  300.     if (alloc_tracing)
  301.         fprintf(stderr,"ckalloc %lx %d %s %d\n", result->body, size, 
  302.                 file, line);
  303.  
  304.     if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
  305.         break_on_malloc = 0;
  306.         (void) fflush(stdout);
  307.         fprintf(stderr,"reached malloc break limit (%d)\n", 
  308.                 total_mallocs);
  309.         fprintf(stderr, "program will now enter C debugger\n");
  310.         (void) fflush(stderr);
  311.     abort();
  312.     }
  313.  
  314.     current_malloc_packets++;
  315.     if (current_malloc_packets > maximum_malloc_packets)
  316.         maximum_malloc_packets = current_malloc_packets;
  317.     current_bytes_malloced += size;
  318.     if (current_bytes_malloced > maximum_bytes_malloced)
  319.         maximum_bytes_malloced = current_bytes_malloced;
  320.  
  321.     return result->body;
  322. }
  323.  
  324. /*
  325.  *----------------------------------------------------------------------
  326.  *
  327.  * Tcl_DbCkfree - debugging ckfree
  328.  *
  329.  *        Verify that the low and high guards are intact, and if so
  330.  *        then free the buffer else panic.
  331.  *
  332.  *        The guards are erased after being checked to catch duplicate
  333.  *        frees.
  334.  *
  335.  *        The second and third arguments are file and line, these contain
  336.  *        the filename and line number corresponding to the caller.
  337.  *        These are sent by the ckfree macro; it uses the preprocessor
  338.  *        autodefines __FILE__ and __LINE__.
  339.  *
  340.  *----------------------------------------------------------------------
  341.  */
  342.  
  343. int
  344. Tcl_DbCkfree(ptr, file, line)
  345.     char *  ptr;
  346.     char     *file;
  347.     int       line;
  348. {
  349.     struct mem_header *memp = 0;  /* Must be zero for size calc */
  350.  
  351.     /*
  352.      * Since header ptr is zero, body offset will be size
  353.      */
  354. #ifdef _CRAYCOM
  355.     memp = (struct mem_header *)((char *) ptr  - (sizeof(int)*((unsigned)&(memp->body))));
  356. #else
  357.     memp = (struct mem_header *)(((char *) ptr) - (int)memp->body);
  358. #endif
  359.  
  360.     if (alloc_tracing)
  361.         fprintf(stderr, "ckfree %lx %ld %s %d\n", memp->body, 
  362.                 memp->length, file, line);
  363.  
  364.     if (validate_memory)
  365.         Tcl_ValidateAllMemory (file, line);
  366.  
  367.     ValidateMemory (memp, file, line, TRUE);
  368.     if (init_malloced_bodies) {
  369.     memset((VOID *) ptr, GUARD_VALUE, memp->length);
  370.     }
  371.  
  372.     total_frees++;
  373.     current_malloc_packets--;
  374.     current_bytes_malloced -= memp->length;
  375.  
  376.     /*
  377.      * Delink from allocated list
  378.      */
  379.     if (memp->flink != NULL)
  380.         memp->flink->blink = memp->blink;
  381.     if (memp->blink != NULL)
  382.         memp->blink->flink = memp->flink;
  383.     if (allocHead == memp)
  384.         allocHead = memp->flink;
  385.     free((char *) memp);
  386.     return 0;
  387. }
  388.  
  389. /*
  390.  *--------------------------------------------------------------------
  391.  *
  392.  * Tcl_DbCkrealloc - debugging ckrealloc
  393.  *
  394.  *    Reallocate a chunk of memory by allocating a new one of the
  395.  *    right size, copying the old data to the new location, and then
  396.  *    freeing the old memory space, using all the memory checking
  397.  *    features of this package.
  398.  *
  399.  *--------------------------------------------------------------------
  400.  */
  401. char *
  402. Tcl_DbCkrealloc(ptr, size, file, line)
  403.     char *ptr;
  404.     unsigned int size;
  405.     char *file;
  406.     int line;
  407. {
  408.     char *new;
  409.     unsigned int copySize;
  410.     struct mem_header *memp = 0;  /* Must be zero for size calc */
  411.  
  412. #ifdef _CRAYCOM
  413.     memp = (struct mem_header *)((char *) ptr  - (sizeof(int)*((unsigned)&(memp->body))));
  414. #else
  415.     memp = (struct mem_header *)(((char *) ptr) - (int)memp->body);
  416. #endif
  417.     copySize = size;
  418.     if (copySize > memp->length) {
  419.     copySize = memp->length;
  420.     }
  421.     new = Tcl_DbCkalloc(size, file, line);
  422.     memcpy((VOID *) new, (VOID *) ptr, (int) copySize);
  423.     Tcl_DbCkfree(ptr, file, line);
  424.     return(new);
  425. }
  426.  
  427. /*
  428.  *----------------------------------------------------------------------
  429.  *
  430.  * MemoryCmd --
  431.  *     Implements the TCL memory command:
  432.  *       memory info
  433.  *       memory display
  434.  *       break_on_malloc count
  435.  *       trace_on_at_malloc count
  436.  *       trace on|off
  437.  *       validate on|off
  438.  *
  439.  * Results:
  440.  *     Standard TCL results.
  441.  *
  442.  *----------------------------------------------------------------------
  443.  */
  444.     /* ARGSUSED */
  445. static int
  446. MemoryCmd (clientData, interp, argc, argv)
  447.     ClientData  clientData;
  448.     Tcl_Interp *interp;
  449.     int         argc;
  450.     char      **argv;
  451. {
  452.     char *fileName;
  453.     Tcl_DString buffer;
  454.     int result;
  455.  
  456.     if (argc < 2) {
  457.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  458.         argv[0], " option [args..]\"", (char *) NULL);
  459.     return TCL_ERROR;
  460.     }
  461.  
  462.     if (strcmp(argv[1],"trace") == 0) {
  463.         if (argc != 3) 
  464.             goto bad_suboption;
  465.         alloc_tracing = (strcmp(argv[2],"on") == 0);
  466.         return TCL_OK;
  467.     }
  468.     if (strcmp(argv[1],"init") == 0) {
  469.         if (argc != 3)
  470.             goto bad_suboption;
  471.         init_malloced_bodies = (strcmp(argv[2],"on") == 0);
  472.         return TCL_OK;
  473.     }
  474.     if (strcmp(argv[1],"validate") == 0) {
  475.         if (argc != 3)
  476.              goto bad_suboption;
  477.         validate_memory = (strcmp(argv[2],"on") == 0);
  478.         return TCL_OK;
  479.     }
  480.     if (strcmp(argv[1],"trace_on_at_malloc") == 0) {
  481.         if (argc != 3) 
  482.             goto argError;
  483.         if (Tcl_GetInt(interp, argv[2], &trace_on_at_malloc) != TCL_OK)
  484.                 return TCL_ERROR;
  485.          return TCL_OK;
  486.     }
  487.     if (strcmp(argv[1],"break_on_malloc") == 0) {
  488.         if (argc != 3) 
  489.             goto argError;
  490.         if (Tcl_GetInt(interp, argv[2], &break_on_malloc) != TCL_OK)
  491.                 return TCL_ERROR;
  492.         return TCL_OK;
  493.     }
  494.  
  495.     if (strcmp(argv[1],"info") == 0) {
  496.         dump_memory_info(stdout);
  497.         return TCL_OK;
  498.     }
  499.     if (strcmp(argv[1],"active") == 0) {
  500.         if (argc != 3) {
  501.         Tcl_AppendResult(interp, "wrong # args:  should be \"",
  502.             argv[0], " active file", (char *) NULL);
  503.         return TCL_ERROR;
  504.     }
  505.     fileName = Tcl_TildeSubst(interp, argv[2], &buffer);
  506.     if (fileName == NULL) {
  507.         return TCL_ERROR;
  508.     }
  509.     result = Tcl_DumpActiveMemory (fileName);
  510.     Tcl_DStringFree(&buffer);
  511.     if (result != TCL_OK) {
  512.         Tcl_AppendResult(interp, "error accessing ", argv[2], 
  513.             (char *) NULL);
  514.         return TCL_ERROR;
  515.     }
  516.     return TCL_OK;
  517.     }
  518.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  519.         "\":  should be info, init, active, break_on_malloc, ",
  520.         "trace_on_at_malloc, trace, or validate", (char *) NULL);
  521.     return TCL_ERROR;
  522.  
  523. argError:
  524.     Tcl_AppendResult(interp, "wrong # args:  should be \"", argv[0],
  525.         " ", argv[1], "count\"", (char *) NULL);
  526.     return TCL_ERROR;
  527.  
  528. bad_suboption:
  529.     Tcl_AppendResult(interp, "wrong # args:  should be \"", argv[0],
  530.         " ", argv[1], " on|off\"", (char *) NULL);
  531.     return TCL_ERROR;
  532. }
  533.  
  534. /*
  535.  *----------------------------------------------------------------------
  536.  *
  537.  * Tcl_InitMemory --
  538.  *     Initialize the memory command.
  539.  *
  540.  *----------------------------------------------------------------------
  541.  */
  542. void
  543. Tcl_InitMemory(interp)
  544.     Tcl_Interp *interp;
  545. {
  546. Tcl_CreateCommand (interp, "memory", MemoryCmd, (ClientData) NULL, 
  547.                   (Tcl_CmdDeleteProc *) NULL);
  548. }
  549.  
  550. #else
  551.  
  552.  
  553. /*
  554.  *----------------------------------------------------------------------
  555.  *
  556.  * Tcl_Ckalloc --
  557.  *     Interface to malloc when TCL_MEM_DEBUG is disabled.  It does check
  558.  *     that memory was actually allocated.
  559.  *
  560.  *----------------------------------------------------------------------
  561.  */
  562. VOID *
  563. Tcl_Ckalloc (size)
  564.     unsigned int size;
  565. {
  566.         char *result;
  567.  
  568.         result = malloc(size);
  569.         if (result == NULL) 
  570.                 panic("unable to alloc %d bytes", size);
  571.         return result;
  572. }
  573.  
  574. /*
  575.  *----------------------------------------------------------------------
  576.  *
  577.  * TckCkfree --
  578.  *     Interface to free when TCL_MEM_DEBUG is disabled.  Done here rather
  579.  *     in the macro to keep some modules from being compiled with 
  580.  *     TCL_MEM_DEBUG enabled and some with it disabled.
  581.  *
  582.  *----------------------------------------------------------------------
  583.  */
  584. void
  585. Tcl_Ckfree (ptr)
  586.     VOID *ptr;
  587. {
  588.         free (ptr);
  589. }
  590.  
  591. /*
  592.  *----------------------------------------------------------------------
  593.  *
  594.  * Tcl_InitMemory --
  595.  *     Dummy initialization for memory command, which is only available 
  596.  *     if TCL_MEM_DEBUG is on.
  597.  *
  598.  *----------------------------------------------------------------------
  599.  */
  600.     /* ARGSUSED */
  601. void
  602. Tcl_InitMemory(interp)
  603.     Tcl_Interp *interp;
  604. {
  605. }
  606.  
  607. #endif
  608.