summaryrefslogtreecommitdiff
path: root/tcl/generic/tclCkalloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'tcl/generic/tclCkalloc.c')
-rw-r--r--tcl/generic/tclCkalloc.c375
1 files changed, 293 insertions, 82 deletions
diff --git a/tcl/generic/tclCkalloc.c b/tcl/generic/tclCkalloc.c
index fa089fccfea..1eb906d2af8 100644
--- a/tcl/generic/tclCkalloc.c
+++ b/tcl/generic/tclCkalloc.c
@@ -5,7 +5,8 @@
* involving overwritten, double freeing memory and loss of memory.
*
* Copyright (c) 1991-1994 The Regents of the University of California.
- * Copyright (c) 1994-1996 Sun Microsystems, Inc.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * Copyright (c) 1998-1999 by Scriptics Corporation.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -102,9 +103,31 @@ static int init_malloced_bodies = TRUE;
#endif
/*
+ * The following variable indicates to TclFinalizeMemorySubsystem()
+ * that it should dump out the state of memory before exiting. If the
+ * value is non-NULL, it gives the name of the file in which to
+ * dump memory usage information.
+ */
+
+char *tclMemDumpFileName = NULL;
+
+static char dumpFile[100]; /* Records where to dump memory allocation
+ * information. */
+
+/*
+ * Mutex to serialize allocations. This is a low-level mutex that must
+ * be explicitly initialized. This is necessary because the self
+ * initializing mutexes use ckalloc...
+ */
+static Tcl_Mutex *ckallocMutexPtr;
+static int ckallocInit = 0;
+
+/*
* Prototypes for procedures defined in this file:
*/
+static int CheckmemCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char *argv[]));
static int MemoryCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
static void ValidateMemory _ANSI_ARGS_((
@@ -114,6 +137,25 @@ static void ValidateMemory _ANSI_ARGS_((
/*
*----------------------------------------------------------------------
*
+ * TclInitDbCkalloc --
+ * Initialize the locks used by the allocator.
+ * This is only appropriate to call in a single threaded environment,
+ * such as during TclInitSubsystems.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+TclInitDbCkalloc()
+{
+ if (!ckallocInit) {
+ ckallocInit = 1;
+ ckallocMutexPtr = Tcl_GetAllocMutex();
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclDumpMemoryInfo --
* Display the global memory management statistics.
*
@@ -123,34 +165,48 @@ void
TclDumpMemoryInfo(outFile)
FILE *outFile;
{
- fprintf(outFile,"total mallocs %10d\n",
- total_mallocs);
- fprintf(outFile,"total frees %10d\n",
- total_frees);
- fprintf(outFile,"current packets allocated %10d\n",
- current_malloc_packets);
- fprintf(outFile,"current bytes allocated %10d\n",
- current_bytes_malloced);
- fprintf(outFile,"maximum packets allocated %10d\n",
- maximum_malloc_packets);
- fprintf(outFile,"maximum bytes allocated %10d\n",
- maximum_bytes_malloced);
+ fprintf(outFile,"total mallocs %10d\n",
+ total_mallocs);
+ fprintf(outFile,"total frees %10d\n",
+ total_frees);
+ fprintf(outFile,"current packets allocated %10d\n",
+ current_malloc_packets);
+ fprintf(outFile,"current bytes allocated %10d\n",
+ current_bytes_malloced);
+ fprintf(outFile,"maximum packets allocated %10d\n",
+ maximum_malloc_packets);
+ fprintf(outFile,"maximum bytes allocated %10d\n",
+ maximum_bytes_malloced);
}
+
/*
*----------------------------------------------------------------------
*
* ValidateMemory --
- * Procedure to validate allocted memory guard zones.
+ *
+ * Validate memory guard zones for a particular chunk of allocated
+ * memory.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Prints validation information about the allocated memory to stderr.
*
*----------------------------------------------------------------------
*/
+
static void
ValidateMemory(memHeaderP, file, line, nukeGuards)
- struct mem_header *memHeaderP;
- char *file;
- int line;
- int nukeGuards;
+ struct mem_header *memHeaderP; /* Memory chunk to validate */
+ char *file; /* File containing the call to
+ * Tcl_ValidateAllMemory */
+ int line; /* Line number of call to
+ * Tcl_ValidateAllMemory */
+ int nukeGuards; /* If non-zero, indicates that the
+ * memory guards are to be reset to 0
+ * after they have been printed */
{
unsigned char *hiPtr;
int idx;
@@ -164,7 +220,7 @@ ValidateMemory(memHeaderP, file, line, nukeGuards)
fflush(stdout);
byte &= 0xff;
fprintf(stderr, "low guard byte %d is 0x%x \t%c\n", idx, byte,
- (isprint(UCHAR(byte)) ? byte : ' '));
+ (isprint(UCHAR(byte)) ? byte : ' ')); /* INTL: bytes */
}
}
if (guard_failed) {
@@ -185,7 +241,7 @@ ValidateMemory(memHeaderP, file, line, nukeGuards)
fflush (stdout);
byte &= 0xff;
fprintf(stderr, "hi guard byte %d is 0x%x \t%c\n", idx, byte,
- (isprint(UCHAR(byte)) ? byte : ' '));
+ (isprint(UCHAR(byte)) ? byte : ' ')); /* INTL: bytes */
}
}
@@ -211,45 +267,65 @@ ValidateMemory(memHeaderP, file, line, nukeGuards)
*----------------------------------------------------------------------
*
* Tcl_ValidateAllMemory --
- * Validates guard regions for all allocated memory.
+ *
+ * Validate memory guard regions for all allocated memory.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Displays memory validation information to stderr.
*
*----------------------------------------------------------------------
*/
void
Tcl_ValidateAllMemory (file, line)
- char *file;
- int line;
+ char *file; /* File from which Tcl_ValidateAllMemory was called */
+ int line; /* Line number of call to Tcl_ValidateAllMemory */
{
struct mem_header *memScanP;
- for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink)
+ if (!ckallocInit) {
+ TclInitDbCkalloc();
+ }
+ Tcl_MutexLock(ckallocMutexPtr);
+ for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
ValidateMemory(memScanP, file, line, FALSE);
-
+ }
+ Tcl_MutexUnlock(ckallocMutexPtr);
}
/*
*----------------------------------------------------------------------
*
* Tcl_DumpActiveMemory --
- * Displays all allocated memory to stderr.
+ *
+ * Displays all allocated memory to a file; if no filename is given,
+ * information will be written to stderr.
*
* Results:
- * Return TCL_ERROR if an error accessing the file occures, `errno'
- * will have the file error number left in it.
+ * Return TCL_ERROR if an error accessing the file occures, `errno'
+ * will have the file error number left in it.
*----------------------------------------------------------------------
*/
int
Tcl_DumpActiveMemory (fileName)
- char *fileName;
+ char *fileName; /* Name of the file to write info to */
{
FILE *fileP;
struct mem_header *memScanP;
char *address;
- fileP = fopen(fileName, "w");
- if (fileP == NULL)
- return TCL_ERROR;
+ if (fileName == NULL) {
+ fileP = stderr;
+ } else {
+ fileP = fopen(fileName, "w");
+ if (fileP == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ Tcl_MutexLock(ckallocMutexPtr);
for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
address = &memScanP->body [0];
fprintf(fileP, "%8lx - %8lx %7ld @ %s %d %s",
@@ -259,7 +335,11 @@ Tcl_DumpActiveMemory (fileName)
(memScanP->tagPtr == NULL) ? "" : memScanP->tagPtr->string);
(void) fputc('\n', fileP);
}
- fclose (fileP);
+ Tcl_MutexUnlock(ckallocMutexPtr);
+
+ if (fileP != stderr) {
+ fclose (fileP);
+ }
return TCL_OK;
}
@@ -297,8 +377,7 @@ Tcl_DbCkalloc(size, file, line)
if (result == NULL) {
fflush(stdout);
TclDumpMemoryInfo(stderr);
- panic("unable to alloc %d bytes, %s line %d", size, file,
- line);
+ panic("unable to alloc %d bytes, %s line %d", size, file, line);
}
/*
@@ -313,6 +392,10 @@ Tcl_DbCkalloc(size, file, line)
memset ((char *) result->low_guard, GUARD_VALUE, LOW_GUARD_SIZE);
memset (result->body + size, GUARD_VALUE, HIGH_GUARD_SIZE);
}
+ if (!ckallocInit) {
+ TclInitDbCkalloc();
+ }
+ Tcl_MutexLock(ckallocMutexPtr);
result->length = size;
result->tagPtr = curTagPtr;
if (curTagPtr != NULL) {
@@ -322,6 +405,7 @@ Tcl_DbCkalloc(size, file, line)
result->line = line;
result->flink = allocHead;
result->blink = NULL;
+
if (allocHead != NULL)
allocHead->blink = result;
allocHead = result;
@@ -357,6 +441,8 @@ Tcl_DbCkalloc(size, file, line)
if (current_bytes_malloced > maximum_bytes_malloced)
maximum_bytes_malloced = current_bytes_malloced;
+ Tcl_MutexUnlock(ckallocMutexPtr);
+
return result->body;
}
@@ -381,10 +467,16 @@ Tcl_DbCkalloc(size, file, line)
int
Tcl_DbCkfree(ptr, file, line)
- char * ptr;
- char *file;
- int line;
+ char *ptr;
+ char *file;
+ int line;
{
+ struct mem_header *memp;
+
+ if (ptr == NULL) {
+ return 0;
+ }
+
/*
* The following cast is *very* tricky. Must convert the pointer
* to an integer before doing arithmetic on it, because otherwise
@@ -393,16 +485,18 @@ Tcl_DbCkfree(ptr, file, line)
* even though BODY_OFFSET is in words on these machines).
*/
- struct mem_header *memp = (struct mem_header *)
- (((unsigned long) ptr) - BODY_OFFSET);
+ memp = (struct mem_header *) (((unsigned long) ptr) - BODY_OFFSET);
- if (alloc_tracing)
+ if (alloc_tracing) {
fprintf(stderr, "ckfree %lx %ld %s %d\n",
(long unsigned int) memp->body, memp->length, file, line);
+ }
- if (validate_memory)
+ if (validate_memory) {
Tcl_ValidateAllMemory(file, line);
+ }
+ Tcl_MutexLock(ckallocMutexPtr);
ValidateMemory(memp, file, line, TRUE);
if (init_malloced_bodies) {
memset((VOID *) ptr, GUARD_VALUE, (size_t) memp->length);
@@ -429,6 +523,8 @@ Tcl_DbCkfree(ptr, file, line)
if (allocHead == memp)
allocHead = memp->flink;
TclpFree((char *) memp);
+ Tcl_MutexUnlock(ckallocMutexPtr);
+
return 0;
}
@@ -453,14 +549,18 @@ Tcl_DbCkrealloc(ptr, size, file, line)
{
char *new;
unsigned int copySize;
+ struct mem_header *memp;
+
+ if (ptr == NULL) {
+ return Tcl_DbCkalloc(size, file, line);
+ }
/*
* See comment from Tcl_DbCkfree before you change the following
* line.
*/
- struct mem_header *memp = (struct mem_header *)
- (((unsigned long) ptr) - BODY_OFFSET);
+ memp = (struct mem_header *) (((unsigned long) ptr) - BODY_OFFSET);
copySize = size;
if (copySize > (unsigned int) memp->length) {
@@ -469,7 +569,7 @@ Tcl_DbCkrealloc(ptr, size, file, line)
new = Tcl_DbCkalloc(size, file, line);
memcpy((VOID *) new, (VOID *) ptr, (size_t) copySize);
Tcl_DbCkfree(ptr, file, line);
- return(new);
+ return new;
}
@@ -520,13 +620,14 @@ Tcl_Realloc(ptr, size)
*----------------------------------------------------------------------
*
* MemoryCmd --
- * Implements the TCL memory command:
- * memory info
- * memory display
- * break_on_malloc count
- * trace_on_at_malloc count
- * trace on|off
- * validate on|off
+ * Implements the Tcl "memory" command, which provides Tcl-level
+ * control of Tcl memory debugging information.
+ * memory info
+ * memory display
+ * memory break_on_malloc count
+ * memory trace_on_at_malloc count
+ * memory trace on|off
+ * memory validate on|off
*
* Results:
* Standard TCL results.
@@ -580,7 +681,14 @@ MemoryCmd (clientData, interp, argc, argv)
return TCL_OK;
}
if (strcmp(argv[1],"info") == 0) {
- TclDumpMemoryInfo(stdout);
+ char buffer[400];
+ sprintf(buffer, "%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n",
+ "total mallocs", total_mallocs, "total frees", total_frees,
+ "current packets allocated", current_malloc_packets,
+ "current bytes allocated", current_bytes_malloced,
+ "maximum packets allocated", maximum_malloc_packets,
+ "maximum bytes allocated", maximum_bytes_malloced);
+ Tcl_SetResult(interp, buffer, TCL_VOLATILE);
return TCL_OK;
}
if (strcmp(argv[1],"init") == 0) {
@@ -648,20 +756,75 @@ bad_suboption:
/*
*----------------------------------------------------------------------
*
+ * CheckmemCmd --
+ *
+ * This is the command procedure for the "checkmem" command, which
+ * causes the application to exit after printing information about
+ * memory usage to the file passed to this command as its first
+ * argument.
+ *
+ * Results:
+ * Returns a standard Tcl completion code.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+CheckmemCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter for evaluation. */
+ int argc; /* Number of arguments. */
+ char *argv[]; /* String values of arguments. */
+{
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " fileName\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ tclMemDumpFileName = dumpFile;
+ strcpy(tclMemDumpFileName, argv[1]);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_InitMemory --
- * Initialize the memory command.
+ *
+ * Create the "memory" and "checkmem" commands in the given
+ * interpreter.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * New commands are added to the interpreter.
*
*----------------------------------------------------------------------
*/
+
void
Tcl_InitMemory(interp)
- Tcl_Interp *interp;
+ Tcl_Interp *interp; /* Interpreter in which commands should be added */
{
+ TclInitDbCkalloc();
Tcl_CreateCommand (interp, "memory", MemoryCmd, (ClientData) NULL,
(Tcl_CmdDeleteProc *) NULL);
+ Tcl_CreateCommand(interp, "checkmem", CheckmemCmd, (ClientData) 0,
+ (Tcl_CmdDeleteProc *) NULL);
}
-#else
+
+#else /* TCL_MEM_DEBUG */
+
+/* This is the !TCL_MEM_DEBUG case */
+
+#undef Tcl_InitMemory
+#undef Tcl_DumpActiveMemory
+#undef Tcl_ValidateAllMemory
/*
@@ -678,14 +841,22 @@ char *
Tcl_Alloc (size)
unsigned int size;
{
- char *result;
-
- result = TclpAlloc(size);
- /* CYGNUS LOCAL -- check that size is not zero */
- if (result == NULL && size )
- panic("unable to alloc %d bytes", size);
- /* End CYGNUS LOCAL */
- return result;
+ char *result;
+
+ result = TclpAlloc(size);
+ /*
+ * Most systems will not alloc(0), instead bumping it to one so
+ * that NULL isn't returned. Some systems (AIX, Tru64) will alloc(0)
+ * by returning NULL, so we have to check that the NULL we get is
+ * not in response to alloc(0).
+ *
+ * The ANSI spec actually says that systems either return NULL *or*
+ * a special pointer on failure, but we only check for NULL
+ */
+ if ((result == NULL) && size) {
+ panic("unable to alloc %d bytes", size);
+ }
+ return result;
}
char *
@@ -698,10 +869,9 @@ Tcl_DbCkalloc(size, file, line)
result = (char *) TclpAlloc(size);
- if (result == NULL) {
+ if ((result == NULL) && size) {
fflush(stdout);
- panic("unable to alloc %d bytes, %s line %d", size, file,
- line);
+ panic("unable to alloc %d bytes, %s line %d", size, file, line);
}
return result;
}
@@ -725,8 +895,10 @@ Tcl_Realloc(ptr, size)
char *result;
result = TclpRealloc(ptr, size);
- if (result == NULL)
+
+ if ((result == NULL) && size) {
panic("unable to realloc %d bytes", size);
+ }
return result;
}
@@ -741,10 +913,9 @@ Tcl_DbCkrealloc(ptr, size, file, line)
result = (char *) TclpRealloc(ptr, size);
- if (result == NULL) {
+ if ((result == NULL) && size) {
fflush(stdout);
- panic("unable to realloc %d bytes, %s line %d", size, file,
- line);
+ panic("unable to realloc %d bytes, %s line %d", size, file, line);
}
return result;
}
@@ -764,14 +935,14 @@ void
Tcl_Free (ptr)
char *ptr;
{
- TclpFree(ptr);
+ TclpFree(ptr);
}
int
Tcl_DbCkfree(ptr, file, line)
- char * ptr;
- char *file;
- int line;
+ char *ptr;
+ char *file;
+ int line;
{
TclpFree(ptr);
return 0;
@@ -793,13 +964,6 @@ Tcl_InitMemory(interp)
{
}
-#undef Tcl_DumpActiveMemory
-#undef Tcl_ValidateAllMemory
-
-extern int Tcl_DumpActiveMemory _ANSI_ARGS_((char *fileName));
-extern void Tcl_ValidateAllMemory _ANSI_ARGS_((char *file,
- int line));
-
int
Tcl_DumpActiveMemory(fileName)
char *fileName;
@@ -814,4 +978,51 @@ Tcl_ValidateAllMemory(file, line)
{
}
+void
+TclDumpMemoryInfo(outFile)
+ FILE *outFile;
+{
+}
+
+#endif /* TCL_MEM_DEBUG */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclFinalizeMemorySubsystem --
+ *
+ * This procedure is called to finalize all the structures that
+ * are used by the memory allocator on a per-process basis.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * This subsystem is self-initializing, since memory can be
+ * allocated before Tcl is formally initialized. After this call,
+ * this subsystem has been reset to its initial state and is
+ * usable again.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+TclFinalizeMemorySubsystem()
+{
+#ifdef TCL_MEM_DEBUG
+ Tcl_MutexLock(ckallocMutexPtr);
+ if (tclMemDumpFileName != NULL) {
+ Tcl_DumpActiveMemory(tclMemDumpFileName);
+ }
+ if (curTagPtr != NULL) {
+ TclpFree((char *) curTagPtr);
+ }
+ allocHead = NULL;
+ Tcl_MutexUnlock(ckallocMutexPtr);
#endif
+
+#if USE_TCLALLOC
+ TclFinalizeAllocSubsystem();
+#endif
+}
+