source: trunk/tcl/tclCkalloc.c@ 9

Last change on this file since 9 was 2, checked in by Pavel Demin, 16 years ago

first commit

File size: 21.4 KB
RevLine 
[2]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-1996 Sun Microsystems, Inc.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * This code contributed by Karl Lehenbauer and Mark Diekhans
14 *
15 * RCS: @(#) $Id: tclCkalloc.c,v 1.1 2008-06-04 13:58:04 demin Exp $
16 */
17
18#include "tclInt.h"
19#include "tclPort.h"
20
21#define FALSE 0
22#define TRUE 1
23
24#ifdef TCL_MEM_DEBUG
25
26/*
27 * One of the following structures is allocated each time the
28 * "memory tag" command is invoked, to hold the current tag.
29 */
30
31typedef struct MemTag {
32 int refCount; /* Number of mem_headers referencing
33 * this tag. */
34 char string[4]; /* Actual size of string will be as
35 * large as needed for actual tag. This
36 * must be the last field in the structure. */
37} MemTag;
38
39#define TAG_SIZE(bytesInString) ((unsigned) sizeof(MemTag) + bytesInString - 3)
40
41static MemTag *curTagPtr = NULL;/* Tag to use in all future mem_headers
42 * (set by "memory tag" command). */
43
44/*
45 * One of the following structures is allocated just before each
46 * dynamically allocated chunk of memory, both to record information
47 * about the chunk and to help detect chunk under-runs.
48 */
49
50#define LOW_GUARD_SIZE (8 + (32 - (sizeof(long) + sizeof(int)))%8)
51struct mem_header {
52 struct mem_header *flink;
53 struct mem_header *blink;
54 MemTag *tagPtr; /* Tag from "memory tag" command; may be
55 * NULL. */
56 char *file;
57 long length;
58 int line;
59 unsigned char low_guard[LOW_GUARD_SIZE];
60 /* Aligns body on 8-byte boundary, plus
61 * provides at least 8 additional guard bytes
62 * to detect underruns. */
63 char body[1]; /* First byte of client's space. Actual
64 * size of this field will be larger than
65 * 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
74 * chunk 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
82 * body 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
99 static int validate_memory = TRUE;
100#else
101 static int validate_memory = FALSE;
102#endif
103
104/*
105 * Prototypes for procedures defined in this file:
106 */
107
108static int MemoryCmd _ANSI_ARGS_((ClientData clientData,
109 Tcl_Interp *interp, int argc, char **argv));
110static void ValidateMemory _ANSI_ARGS_((
111 struct mem_header *memHeaderP, char *file,
112 int line, int nukeGuards));
113
114
115/*
116 *----------------------------------------------------------------------
117 *
118 * TclDumpMemoryInfo --
119 * Display the global memory management statistics.
120 *
121 *----------------------------------------------------------------------
122 */
123void
124TclDumpMemoryInfo(outFile)
125 FILE *outFile;
126{
127 fprintf(outFile,"total mallocs %10d\n",
128 total_mallocs);
129 fprintf(outFile,"total frees %10d\n",
130 total_frees);
131 fprintf(outFile,"current packets allocated %10d\n",
132 current_malloc_packets);
133 fprintf(outFile,"current bytes allocated %10d\n",
134 current_bytes_malloced);
135 fprintf(outFile,"maximum packets allocated %10d\n",
136 maximum_malloc_packets);
137 fprintf(outFile,"maximum bytes allocated %10d\n",
138 maximum_bytes_malloced);
139}
140
141
142/*
143 *----------------------------------------------------------------------
144 *
145 * ValidateMemory --
146 * Procedure to validate allocted memory guard zones.
147 *
148 *----------------------------------------------------------------------
149 */
150static void
151ValidateMemory(memHeaderP, file, line, nukeGuards)
152 struct mem_header *memHeaderP;
153 char *file;
154 int line;
155 int nukeGuards;
156{
157 unsigned char *hiPtr;
158 int idx;
159 int guard_failed = FALSE;
160 int byte;
161
162 for (idx = 0; idx < LOW_GUARD_SIZE; idx++) {
163 byte = *(memHeaderP->low_guard + idx);
164 if (byte != GUARD_VALUE) {
165 guard_failed = TRUE;
166 fflush(stdout);
167 byte &= 0xff;
168 fprintf(stderr, "low guard byte %d is 0x%x \t%c\n", idx, byte,
169 (isprint(UCHAR(byte)) ? byte : ' '));
170 }
171 }
172 if (guard_failed) {
173 TclDumpMemoryInfo (stderr);
174 fprintf(stderr, "low guard failed at %lx, %s %d\n",
175 (long unsigned int) memHeaderP->body, file, line);
176 fflush(stderr); /* In case name pointer is bad. */
177 fprintf(stderr, "%ld bytes allocated at (%s %d)\n", memHeaderP->length,
178 memHeaderP->file, memHeaderP->line);
179 panic ("Memory validation failure");
180 }
181
182 hiPtr = (unsigned char *)memHeaderP->body + memHeaderP->length;
183 for (idx = 0; idx < HIGH_GUARD_SIZE; idx++) {
184 byte = *(hiPtr + idx);
185 if (byte != GUARD_VALUE) {
186 guard_failed = TRUE;
187 fflush (stdout);
188 byte &= 0xff;
189 fprintf(stderr, "hi guard byte %d is 0x%x \t%c\n", idx, byte,
190 (isprint(UCHAR(byte)) ? byte : ' '));
191 }
192 }
193
194 if (guard_failed) {
195 TclDumpMemoryInfo (stderr);
196 fprintf(stderr, "high guard failed at %lx, %s %d\n",
197 (long unsigned int) memHeaderP->body, file, line);
198 fflush(stderr); /* In case name pointer is bad. */
199 fprintf(stderr, "%ld bytes allocated at (%s %d)\n",
200 memHeaderP->length, memHeaderP->file,
201 memHeaderP->line);
202 panic("Memory validation failure");
203 }
204
205 if (nukeGuards) {
206 memset ((char *) memHeaderP->low_guard, 0, LOW_GUARD_SIZE);
207 memset ((char *) hiPtr, 0, HIGH_GUARD_SIZE);
208 }
209
210}
211
212
213/*
214 *----------------------------------------------------------------------
215 *
216 * Tcl_ValidateAllMemory --
217 * Validates guard regions for all allocated memory.
218 *
219 *----------------------------------------------------------------------
220 */
221void
222Tcl_ValidateAllMemory (file, line)
223 char *file;
224 int line;
225{
226 struct mem_header *memScanP;
227
228 for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink)
229 ValidateMemory(memScanP, file, line, FALSE);
230
231}
232
233
234/*
235 *----------------------------------------------------------------------
236 *
237 * Tcl_DumpActiveMemory --
238 * Displays all allocated memory to stderr.
239 *
240 * Results:
241 * Return TCL_ERROR if an error accessing the file occures, `errno'
242 * will have the file error number left in it.
243 *----------------------------------------------------------------------
244 */
245int
246Tcl_DumpActiveMemory (fileName)
247 char *fileName;
248{
249 FILE *fileP;
250 struct mem_header *memScanP;
251 char *address;
252
253 fileP = fopen(fileName, "w");
254 if (fileP == NULL)
255 return TCL_ERROR;
256
257 for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
258 address = &memScanP->body [0];
259 fprintf(fileP, "%8lx - %8lx %7ld @ %s %d %s",
260 (long unsigned int) address,
261 (long unsigned int) address + memScanP->length - 1,
262 memScanP->length, memScanP->file, memScanP->line,
263 (memScanP->tagPtr == NULL) ? "" : memScanP->tagPtr->string);
264 (void) fputc('\n', fileP);
265 }
266 fclose (fileP);
267 return TCL_OK;
268}
269
270
271/*
272 *----------------------------------------------------------------------
273 *
274 * Tcl_DbCkalloc - debugging ckalloc
275 *
276 * Allocate the requested amount of space plus some extra for
277 * guard bands at both ends of the request, plus a size, panicing
278 * if there isn't enough space, then write in the guard bands
279 * and return the address of the space in the middle that the
280 * user asked for.
281 *
282 * The second and third arguments are file and line, these contain
283 * the filename and line number corresponding to the caller.
284 * These are sent by the ckalloc macro; it uses the preprocessor
285 * autodefines __FILE__ and __LINE__.
286 *
287 *----------------------------------------------------------------------
288 */
289char *
290Tcl_DbCkalloc(size, file, line)
291 unsigned int size;
292 char *file;
293 int line;
294{
295 struct mem_header *result;
296
297 if (validate_memory)
298 Tcl_ValidateAllMemory (file, line);
299
300 result = (struct mem_header *) TclpAlloc((unsigned)size +
301 sizeof(struct mem_header) + HIGH_GUARD_SIZE);
302 if (result == NULL) {
303 fflush(stdout);
304 TclDumpMemoryInfo(stderr);
305 panic("unable to alloc %d bytes, %s line %d", size, file,
306 line);
307 }
308
309 /*
310 * Fill in guard zones and size. Also initialize the contents of
311 * the block with bogus bytes to detect uses of initialized data.
312 * Link into allocated list.
313 */
314 if (init_malloced_bodies) {
315 memset ((VOID *) result, GUARD_VALUE,
316 size + sizeof(struct mem_header) + HIGH_GUARD_SIZE);
317 } else {
318 memset ((char *) result->low_guard, GUARD_VALUE, LOW_GUARD_SIZE);
319 memset (result->body + size, GUARD_VALUE, HIGH_GUARD_SIZE);
320 }
321 result->length = size;
322 result->tagPtr = curTagPtr;
323 if (curTagPtr != NULL) {
324 curTagPtr->refCount++;
325 }
326 result->file = file;
327 result->line = line;
328 result->flink = allocHead;
329 result->blink = NULL;
330 if (allocHead != NULL)
331 allocHead->blink = result;
332 allocHead = result;
333
334 total_mallocs++;
335 if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
336 (void) fflush(stdout);
337 fprintf(stderr, "reached malloc trace enable point (%d)\n",
338 total_mallocs);
339 fflush(stderr);
340 alloc_tracing = TRUE;
341 trace_on_at_malloc = 0;
342 }
343
344 if (alloc_tracing)
345 fprintf(stderr,"ckalloc %lx %d %s %d\n",
346 (long unsigned int) result->body, size, file, line);
347
348 if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
349 break_on_malloc = 0;
350 (void) fflush(stdout);
351 fprintf(stderr,"reached malloc break limit (%d)\n",
352 total_mallocs);
353 fprintf(stderr, "program will now enter C debugger\n");
354 (void) fflush(stderr);
355 abort();
356 }
357
358 current_malloc_packets++;
359 if (current_malloc_packets > maximum_malloc_packets)
360 maximum_malloc_packets = current_malloc_packets;
361 current_bytes_malloced += size;
362 if (current_bytes_malloced > maximum_bytes_malloced)
363 maximum_bytes_malloced = current_bytes_malloced;
364
365 return result->body;
366}
367
368
369/*
370 *----------------------------------------------------------------------
371 *
372 * Tcl_DbCkfree - debugging ckfree
373 *
374 * Verify that the low and high guards are intact, and if so
375 * then free the buffer else panic.
376 *
377 * The guards are erased after being checked to catch duplicate
378 * frees.
379 *
380 * The second and third arguments are file and line, these contain
381 * the filename and line number corresponding to the caller.
382 * These are sent by the ckfree macro; it uses the preprocessor
383 * autodefines __FILE__ and __LINE__.
384 *
385 *----------------------------------------------------------------------
386 */
387
388int
389Tcl_DbCkfree(ptr, file, line)
390 char * ptr;
391 char *file;
392 int line;
393{
394 /*
395 * The following cast is *very* tricky. Must convert the pointer
396 * to an integer before doing arithmetic on it, because otherwise
397 * the arithmetic will be done differently (and incorrectly) on
398 * word-addressed machines such as Crays (will subtract only bytes,
399 * even though BODY_OFFSET is in words on these machines).
400 */
401
402 struct mem_header *memp = (struct mem_header *)
403 (((unsigned long) ptr) - BODY_OFFSET);
404
405 if (alloc_tracing)
406 fprintf(stderr, "ckfree %lx %ld %s %d\n",
407 (long unsigned int) memp->body, memp->length, file, line);
408
409 if (validate_memory)
410 Tcl_ValidateAllMemory(file, line);
411
412 ValidateMemory(memp, file, line, TRUE);
413 if (init_malloced_bodies) {
414 memset((VOID *) ptr, GUARD_VALUE, (size_t) memp->length);
415 }
416
417 total_frees++;
418 current_malloc_packets--;
419 current_bytes_malloced -= memp->length;
420
421 if (memp->tagPtr != NULL) {
422 memp->tagPtr->refCount--;
423 if ((memp->tagPtr->refCount == 0) && (curTagPtr != memp->tagPtr)) {
424 TclpFree((char *) memp->tagPtr);
425 }
426 }
427
428 /*
429 * Delink from allocated list
430 */
431 if (memp->flink != NULL)
432 memp->flink->blink = memp->blink;
433 if (memp->blink != NULL)
434 memp->blink->flink = memp->flink;
435 if (allocHead == memp)
436 allocHead = memp->flink;
437 TclpFree((char *) memp);
438 return 0;
439}
440
441
442/*
443 *--------------------------------------------------------------------
444 *
445 * Tcl_DbCkrealloc - debugging ckrealloc
446 *
447 * Reallocate a chunk of memory by allocating a new one of the
448 * right size, copying the old data to the new location, and then
449 * freeing the old memory space, using all the memory checking
450 * features of this package.
451 *
452 *--------------------------------------------------------------------
453 */
454char *
455Tcl_DbCkrealloc(ptr, size, file, line)
456 char *ptr;
457 unsigned int size;
458 char *file;
459 int line;
460{
461 char *new;
462 unsigned int copySize;
463
464 /*
465 * See comment from Tcl_DbCkfree before you change the following
466 * line.
467 */
468
469 struct mem_header *memp = (struct mem_header *)
470 (((unsigned long) ptr) - BODY_OFFSET);
471
472 copySize = size;
473 if (copySize > (unsigned int) memp->length) {
474 copySize = memp->length;
475 }
476 new = Tcl_DbCkalloc(size, file, line);
477 memcpy((VOID *) new, (VOID *) ptr, (size_t) copySize);
478 Tcl_DbCkfree(ptr, file, line);
479 return(new);
480}
481
482
483
484/*
485 *----------------------------------------------------------------------
486 *
487 * Tcl_Alloc, et al. --
488 *
489 * These functions are defined in terms of the debugging versions
490 * when TCL_MEM_DEBUG is set.
491 *
492 * Results:
493 * Same as the debug versions.
494 *
495 * Side effects:
496 * Same as the debug versions.
497 *
498 *----------------------------------------------------------------------
499 */
500
501#undef Tcl_Alloc
502#undef Tcl_Free
503#undef Tcl_Realloc
504
505char *
506Tcl_Alloc(size)
507 unsigned int size;
508{
509 return Tcl_DbCkalloc(size, "unknown", 0);
510}
511
512void
513Tcl_Free(ptr)
514 char *ptr;
515{
516 Tcl_DbCkfree(ptr, "unknown", 0);
517}
518
519char *
520Tcl_Realloc(ptr, size)
521 char *ptr;
522 unsigned int size;
523{
524 return Tcl_DbCkrealloc(ptr, size, "unknown", 0);
525}
526
527
528/*
529 *----------------------------------------------------------------------
530 *
531 * MemoryCmd --
532 * Implements the TCL memory command:
533 * memory info
534 * memory display
535 * break_on_malloc count
536 * trace_on_at_malloc count
537 * trace on|off
538 * validate on|off
539 *
540 * Results:
541 * Standard TCL results.
542 *
543 *----------------------------------------------------------------------
544 */
545 /* ARGSUSED */
546static int
547MemoryCmd (clientData, interp, argc, argv)
548 ClientData clientData;
549 Tcl_Interp *interp;
550 int argc;
551 char **argv;
552{
553 char *fileName;
554 Tcl_DString buffer;
555 int result;
556
557 if (argc < 2) {
558 Tcl_AppendResult(interp, "wrong # args: should be \"",
559 argv[0], " option [args..]\"", (char *) NULL);
560 return TCL_ERROR;
561 }
562
563 if (strcmp(argv[1],"active") == 0) {
564 if (argc != 3) {
565 Tcl_AppendResult(interp, "wrong # args: should be \"",
566 argv[0], " active file\"", (char *) NULL);
567 return TCL_ERROR;
568 }
569 fileName = Tcl_TranslateFileName(interp, argv[2], &buffer);
570 if (fileName == NULL) {
571 return TCL_ERROR;
572 }
573 result = Tcl_DumpActiveMemory (fileName);
574 Tcl_DStringFree(&buffer);
575 if (result != TCL_OK) {
576 Tcl_AppendResult(interp, "error accessing ", argv[2],
577 (char *) NULL);
578 return TCL_ERROR;
579 }
580 return TCL_OK;
581 }
582 if (strcmp(argv[1],"break_on_malloc") == 0) {
583 if (argc != 3) {
584 goto argError;
585 }
586 if (Tcl_GetInt(interp, argv[2], &break_on_malloc) != TCL_OK) {
587 return TCL_ERROR;
588 }
589 return TCL_OK;
590 }
591 if (strcmp(argv[1],"info") == 0) {
592 TclDumpMemoryInfo(stdout);
593 return TCL_OK;
594 }
595 if (strcmp(argv[1],"init") == 0) {
596 if (argc != 3) {
597 goto bad_suboption;
598 }
599 init_malloced_bodies = (strcmp(argv[2],"on") == 0);
600 return TCL_OK;
601 }
602 if (strcmp(argv[1],"tag") == 0) {
603 if (argc != 3) {
604 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
605 " tag string\"", (char *) NULL);
606 return TCL_ERROR;
607 }
608 if ((curTagPtr != NULL) && (curTagPtr->refCount == 0)) {
609 TclpFree((char *) curTagPtr);
610 }
611 curTagPtr = (MemTag *) TclpAlloc(TAG_SIZE(strlen(argv[2])));
612 curTagPtr->refCount = 0;
613 strcpy(curTagPtr->string, argv[2]);
614 return TCL_OK;
615 }
616 if (strcmp(argv[1],"trace") == 0) {
617 if (argc != 3) {
618 goto bad_suboption;
619 }
620 alloc_tracing = (strcmp(argv[2],"on") == 0);
621 return TCL_OK;
622 }
623
624 if (strcmp(argv[1],"trace_on_at_malloc") == 0) {
625 if (argc != 3) {
626 goto argError;
627 }
628 if (Tcl_GetInt(interp, argv[2], &trace_on_at_malloc) != TCL_OK) {
629 return TCL_ERROR;
630 }
631 return TCL_OK;
632 }
633 if (strcmp(argv[1],"validate") == 0) {
634 if (argc != 3) {
635 goto bad_suboption;
636 }
637 validate_memory = (strcmp(argv[2],"on") == 0);
638 return TCL_OK;
639 }
640
641 Tcl_AppendResult(interp, "bad option \"", argv[1],
642 "\": should be active, break_on_malloc, info, init, ",
643 "tag, trace, trace_on_at_malloc, or validate", (char *) NULL);
644 return TCL_ERROR;
645
646argError:
647 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
648 " ", argv[1], " count\"", (char *) NULL);
649 return TCL_ERROR;
650
651bad_suboption:
652 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
653 " ", argv[1], " on|off\"", (char *) NULL);
654 return TCL_ERROR;
655}
656
657
658/*
659 *----------------------------------------------------------------------
660 *
661 * Tcl_InitMemory --
662 * Initialize the memory command.
663 *
664 *----------------------------------------------------------------------
665 */
666void
667Tcl_InitMemory(interp)
668 Tcl_Interp *interp;
669{
670 Tcl_CreateCommand (interp, "memory", MemoryCmd, (ClientData) NULL,
671 (Tcl_CmdDeleteProc *) NULL);
672}
673
674#else
675
676
677
678/*
679 *----------------------------------------------------------------------
680 *
681 * Tcl_Alloc --
682 * Interface to TclpAlloc when TCL_MEM_DEBUG is disabled. It does check
683 * that memory was actually allocated.
684 *
685 *----------------------------------------------------------------------
686 */
687
688char *
689Tcl_Alloc (size)
690 unsigned int size;
691{
692 char *result;
693
694 result = TclpAlloc(size);
695 if (result == NULL)
696 panic("unable to alloc %d bytes", size);
697 return result;
698}
699
700char *
701Tcl_DbCkalloc(size, file, line)
702 unsigned int size;
703 char *file;
704 int line;
705{
706 char *result;
707
708 result = (char *) TclpAlloc(size);
709
710 if (result == NULL) {
711 fflush(stdout);
712 panic("unable to alloc %d bytes, %s line %d", size, file,
713 line);
714 }
715 return result;
716}
717
718
719
720/*
721 *----------------------------------------------------------------------
722 *
723 * Tcl_Realloc --
724 * Interface to TclpRealloc when TCL_MEM_DEBUG is disabled. It does
725 * check that memory was actually allocated.
726 *
727 *----------------------------------------------------------------------
728 */
729
730char *
731Tcl_Realloc(ptr, size)
732 char *ptr;
733 unsigned int size;
734{
735 char *result;
736
737 result = TclpRealloc(ptr, size);
738 if (result == NULL)
739 panic("unable to realloc %d bytes", size);
740 return result;
741}
742
743char *
744Tcl_DbCkrealloc(ptr, size, file, line)
745 char *ptr;
746 unsigned int size;
747 char *file;
748 int line;
749{
750 char *result;
751
752 result = (char *) TclpRealloc(ptr, size);
753
754 if (result == NULL) {
755 fflush(stdout);
756 panic("unable to realloc %d bytes, %s line %d", size, file,
757 line);
758 }
759 return result;
760}
761
762
763/*
764 *----------------------------------------------------------------------
765 *
766 * Tcl_Free --
767 * Interface to TclpFree when TCL_MEM_DEBUG is disabled. Done here
768 * rather in the macro to keep some modules from being compiled with
769 * TCL_MEM_DEBUG enabled and some with it disabled.
770 *
771 *----------------------------------------------------------------------
772 */
773
774void
775Tcl_Free (ptr)
776 char *ptr;
777{
778 TclpFree(ptr);
779}
780
781int
782Tcl_DbCkfree(ptr, file, line)
783 char * ptr;
784 char *file;
785 int line;
786{
787 TclpFree(ptr);
788 return 0;
789}
790
791
792/*
793 *----------------------------------------------------------------------
794 *
795 * Tcl_InitMemory --
796 * Dummy initialization for memory command, which is only available
797 * if TCL_MEM_DEBUG is on.
798 *
799 *----------------------------------------------------------------------
800 */
801 /* ARGSUSED */
802void
803Tcl_InitMemory(interp)
804 Tcl_Interp *interp;
805{
806}
807
808#undef Tcl_DumpActiveMemory
809#undef Tcl_ValidateAllMemory
810
811extern int Tcl_DumpActiveMemory _ANSI_ARGS_((char *fileName));
812extern void Tcl_ValidateAllMemory _ANSI_ARGS_((char *file,
813 int line));
814
815int
816Tcl_DumpActiveMemory(fileName)
817 char *fileName;
818{
819 return TCL_OK;
820}
821
822void
823Tcl_ValidateAllMemory(file, line)
824 char *file;
825 int line;
826{
827}
828
829#endif
Note: See TracBrowser for help on using the repository browser.