Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/generic/tclCkalloc.c @ 35

Last change on this file since 35 was 25, checked in by landauf, 17 years ago

added tcl to libs

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