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