summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvincentdarley <vincentdarley>2003-01-17 14:19:28 (GMT)
committervincentdarley <vincentdarley>2003-01-17 14:19:28 (GMT)
commit9355455bbbdf3472b04c9f8f101a2ad35164baa7 (patch)
treebffe9ba034272937075cc0193fd4baababe3ad82
parentd2419094de4147575f4d89098571adcde80275cd (diff)
downloadtcl-9355455bbbdf3472b04c9f8f101a2ad35164baa7.zip
tcl-9355455bbbdf3472b04c9f8f101a2ad35164baa7.tar.gz
tcl-9355455bbbdf3472b04c9f8f101a2ad35164baa7.tar.bz2
execution trace, command trace and stringObj bug fixes
-rw-r--r--ChangeLog25
-rw-r--r--generic/tclBasic.c48
-rw-r--r--generic/tclCmdMZ.c231
-rw-r--r--generic/tclInt.h6
-rw-r--r--generic/tclStringObj.c70
-rw-r--r--tests/stringObj.test4
-rw-r--r--tests/trace.test129
7 files changed, 367 insertions, 146 deletions
diff --git a/ChangeLog b/ChangeLog
index efc2727..8f3c070 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2003-01-16 Vince Darley <vincentdarley@users.sourceforge.net>
+
+ * generic/tclStringObj.c: Tcl_SetObjLength fix for when
+ the object has a unicode string rep. Fixes [Bug 635200]
+ * tests/stringObj.test: removed 'knownBug' constraint from
+ test 14.1 now that this bug is fixed.
+
+ * generic/tclInt.h:
+ * generic/tclBasic.c:
+ * generic/tclCmdMZ.z:
+ * tests/trace.test: execution and command tracing bug fixes and
+ cleanup. In particular fixed [Bug 655645], [Bug 615043],
+ [Bug 571385]
+ - fixed some subtle cleanup problems with tracing. This
+ required replacing Tcl_Preserve/Tcl_Release with a more
+ robust refCount approach. Solves at least one known crash
+ caused by memory corruption.
+ - fixed some confusion in the code between new style traces
+ (Tcl 8.4) and the very limited 'Tcl_CreateTrace' which existed
+ before.
+ - made behaviour consistent with documentation (several
+ tests even contradicted the documentation before).
+ - fixed some minor error message details
+ - added a number of new tests
+
2003-01-16 Jeff Hobbs <jeffh@ActiveState.com>
* win/tclWinSerial.c (SerialOutputProc): add casts for
diff --git a/generic/tclBasic.c b/generic/tclBasic.c
index 6fe4db2..6702240 100644
--- a/generic/tclBasic.c
+++ b/generic/tclBasic.c
@@ -13,7 +13,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclBasic.c,v 1.70 2002/09/06 00:20:29 dgp Exp $
+ * RCS: @(#) $Id: tclBasic.c,v 1.71 2003/01/17 14:19:40 vincentdarley Exp $
*/
#include "tclInt.h"
@@ -1075,7 +1075,7 @@ DeleteInterpProc(interp)
}
TclFreePackageInfo(iPtr);
while (iPtr->tracePtr != NULL) {
- Tcl_DeleteTrace( (Tcl_Interp*) iPtr, (Tcl_Trace) iPtr->tracePtr );
+ Tcl_DeleteTrace((Tcl_Interp*) iPtr, (Tcl_Trace) iPtr->tracePtr);
}
if (iPtr->execEnvPtr != NULL) {
TclDeleteExecEnv(iPtr->execEnvPtr);
@@ -2420,7 +2420,9 @@ Tcl_DeleteCommandFromToken(interp, cmd)
tracePtr = cmdPtr->tracePtr;
while (tracePtr != NULL) {
CommandTrace *nextPtr = tracePtr->nextPtr;
- Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
+ if ((--tracePtr->refCount) <= 0) {
+ ckfree((char*)tracePtr);
+ }
tracePtr = nextPtr;
}
cmdPtr->tracePtr = NULL;
@@ -2513,6 +2515,7 @@ Tcl_DeleteCommandFromToken(interp, cmd)
TclCleanupCommand(cmdPtr);
return 0;
}
+
static char *
CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags)
Interp *iPtr; /* Interpreter containing command. */
@@ -2562,7 +2565,9 @@ CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags)
flags |= TCL_TRACE_DESTROYED;
}
active.cmdPtr = cmdPtr;
+
Tcl_Preserve((ClientData) iPtr);
+
for (tracePtr = cmdPtr->tracePtr; tracePtr != NULL;
tracePtr = active.nextTracePtr) {
active.nextTracePtr = tracePtr->nextPtr;
@@ -2577,11 +2582,13 @@ CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags)
(Tcl_Command) cmdPtr, oldNamePtr);
oldName = TclGetString(oldNamePtr);
}
- Tcl_Preserve((ClientData) tracePtr);
+ tracePtr->refCount++;
(*tracePtr->traceProc)(tracePtr->clientData,
(Tcl_Interp *) iPtr, oldName, newName, flags);
cmdPtr->flags &= ~tracePtr->flags;
- Tcl_Release((ClientData) tracePtr);
+ if ((--tracePtr->refCount) <= 0) {
+ ckfree((char*)tracePtr);
+ }
}
/*
@@ -2604,7 +2611,6 @@ CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags)
Tcl_Release((ClientData) iPtr);
return result;
}
-
/*
*----------------------------------------------------------------------
@@ -3012,7 +3018,8 @@ TclEvalObjvInternal(interp, objc, objv, command, length, flags)
if ((checkTraces) && (command != NULL)) {
int cmdEpoch = cmdPtr->cmdEpoch;
cmdPtr->refCount++;
- /* If the first set of traces modifies/deletes the command or
+ /*
+ * If the first set of traces modifies/deletes the command or
* any existing traces, then the set checkTraces to 0 and
* go through this while loop one more time.
*/
@@ -4797,9 +4804,8 @@ Tcl_CreateObjTrace( interp, level, flags, proc, clientData, delProc )
/* Test if this trace allows inline compilation of commands */
- if ( ! ( flags & TCL_ALLOW_INLINE_COMPILATION ) ) {
-
- if ( iPtr->tracesForbiddingInline == 0 ) {
+ if (!(flags & TCL_ALLOW_INLINE_COMPILATION)) {
+ if (iPtr->tracesForbiddingInline == 0) {
/*
* When the first trace forbidding inline compilation is
@@ -4815,7 +4821,7 @@ Tcl_CreateObjTrace( interp, level, flags, proc, clientData, delProc )
iPtr->compileEpoch++;
iPtr->flags |= DONT_COMPILE_CMDS_INLINE;
}
- ++ iPtr->tracesForbiddingInline;
+ iPtr->tracesForbiddingInline++;
}
tracePtr = (Trace *) ckalloc(sizeof(Trace));
@@ -4998,17 +5004,17 @@ Tcl_DeleteTrace(interp, trace)
{
Interp *iPtr = (Interp *) interp;
Trace *tracePtr = (Trace *) trace;
- register Trace **tracePtr2 = &( iPtr->tracePtr );
+ register Trace **tracePtr2 = &(iPtr->tracePtr);
/*
* Locate the trace entry in the interpreter's trace list,
* and remove it from the list.
*/
- while ( (*tracePtr2) != NULL && (*tracePtr2) != tracePtr ) {
+ while ((*tracePtr2) != NULL && (*tracePtr2) != tracePtr) {
tracePtr2 = &((*tracePtr2)->nextPtr);
}
- if ( *tracePtr2 == NULL ) {
+ if (*tracePtr2 == NULL) {
return;
}
(*tracePtr2) = (*tracePtr2)->nextPtr;
@@ -5020,11 +5026,11 @@ Tcl_DeleteTrace(interp, trace)
* take advantage of it.
*/
- if ( ! (tracePtr->flags & TCL_ALLOW_INLINE_COMPILATION ) ) {
- -- iPtr->tracesForbiddingInline;
- if ( iPtr->tracesForbiddingInline == 0 ) {
+ if (!(tracePtr->flags & TCL_ALLOW_INLINE_COMPILATION)) {
+ iPtr->tracesForbiddingInline--;
+ if (iPtr->tracesForbiddingInline == 0) {
iPtr->flags &= ~DONT_COMPILE_CMDS_INLINE;
- ++ iPtr->compileEpoch;
+ iPtr->compileEpoch++;
}
}
@@ -5032,13 +5038,13 @@ Tcl_DeleteTrace(interp, trace)
* Execute any delete callback.
*/
- if ( tracePtr->delProc != NULL ) {
- ( tracePtr->delProc )( tracePtr->clientData );
+ if (tracePtr->delProc != NULL) {
+ (tracePtr->delProc)(tracePtr->clientData);
}
/* Delete the trace object */
- Tcl_EventuallyFree( (char*) tracePtr, TCL_DYNAMIC);
+ Tcl_EventuallyFree((char*)tracePtr, TCL_DYNAMIC);
}
/*
diff --git a/generic/tclCmdMZ.c b/generic/tclCmdMZ.c
index 0b2903e..d3deaae 100644
--- a/generic/tclCmdMZ.c
+++ b/generic/tclCmdMZ.c
@@ -14,7 +14,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclCmdMZ.c,v 1.79 2002/11/13 22:11:40 vincentdarley Exp $
+ * RCS: @(#) $Id: tclCmdMZ.c,v 1.80 2003/01/17 14:19:44 vincentdarley Exp $
*/
#include "tclInt.h"
@@ -54,6 +54,10 @@ typedef struct {
* step trace */
int curFlags; /* Trace flags for the current command */
int curCode; /* Return code for the current command */
+ int refCount; /* Used to ensure this structure is
+ * not deleted too early. Keeps track
+ * of how many pieces of code have
+ * a pointer to this structure. */
char command[4]; /* Space for Tcl command to invoke. Actual
* size will be as large as necessary to
* hold command. This field must be the
@@ -288,7 +292,8 @@ Tcl_RegexpObjCmd(dummy, interp, objc, objv)
endOfForLoop:
if ((objc - i) < (2 - about)) {
- Tcl_WrongNumArgs(interp, 1, objv, "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?");
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?");
return TCL_ERROR;
}
objc -= i;
@@ -3181,7 +3186,7 @@ TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
int i, listLen, result;
Tcl_Obj **elemPtrs;
if (objc != 6) {
- Tcl_WrongNumArgs(interp, 3, objv, "name opList execution");
+ Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
return TCL_ERROR;
}
/*
@@ -3196,7 +3201,8 @@ TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
}
if (listLen == 0) {
Tcl_SetResult(interp, "bad operation list \"\": must be "
- "one or more of enter, leave, enterstep, or leavestep", TCL_STATIC);
+ "one or more of enter, leave, enterstep, or leavestep",
+ TCL_STATIC);
return TCL_ERROR;
}
for (i = 0; i < listLen; i++) {
@@ -3231,6 +3237,7 @@ TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
tcmdPtr->startLevel = 0;
tcmdPtr->startCmd = NULL;
tcmdPtr->length = length;
+ tcmdPtr->refCount = 1;
flags |= TCL_TRACE_DELETE;
if (flags & (TRACE_EXEC_ENTER_STEP | TRACE_EXEC_LEAVE_STEP)) {
flags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
@@ -3250,25 +3257,34 @@ TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
*/
TraceCommandInfo *tcmdPtr;
- ClientData clientData;
- clientData = 0;
+ ClientData clientData = NULL;
name = Tcl_GetString(objv[3]);
+
+ /* First ensure the name given is valid */
+ if (Tcl_FindCommand(interp, name, NULL,
+ TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+
while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
- TraceCommandProc, clientData)) != 0) {
+ TraceCommandProc, clientData)) != NULL) {
tcmdPtr = (TraceCommandInfo *) clientData;
/*
- * In checking the 'flags' field we must remove any extraneous
- * flags which may have been temporarily added by various pieces
- * of the trace mechanism.
+ * In checking the 'flags' field we must remove any
+ * extraneous flags which may have been temporarily
+ * added by various pieces of the trace mechanism.
*/
if ((tcmdPtr->length == length)
- && ((tcmdPtr->flags & (TCL_TRACE_ANY_EXEC | TCL_TRACE_RENAME |
+ && ((tcmdPtr->flags & (TCL_TRACE_ANY_EXEC |
+ TCL_TRACE_RENAME |
TCL_TRACE_DELETE)) == flags)
&& (strncmp(command, tcmdPtr->command,
(size_t) length) == 0)) {
flags |= TCL_TRACE_DELETE;
- if (flags & (TRACE_EXEC_ENTER_STEP | TRACE_EXEC_LEAVE_STEP)) {
- flags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
+ if (flags & (TRACE_EXEC_ENTER_STEP |
+ TRACE_EXEC_LEAVE_STEP)) {
+ flags |= (TCL_TRACE_ENTER_EXEC |
+ TCL_TRACE_LEAVE_EXEC);
}
Tcl_UntraceCommand(interp, name,
flags, TraceCommandProc, clientData);
@@ -3283,11 +3299,12 @@ TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
ckfree((char *)tcmdPtr->startCmd);
}
}
- /* Postpone deletion */
if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
+ /* Postpone deletion */
tcmdPtr->flags = 0;
- } else {
- Tcl_EventuallyFree((ClientData) tcmdPtr, TCL_DYNAMIC);
+ }
+ if ((--tcmdPtr->refCount) <= 0) {
+ ckfree((char*)tcmdPtr);
}
break;
}
@@ -3303,11 +3320,18 @@ TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
return TCL_ERROR;
}
- resultListPtr = Tcl_GetObjResult(interp);
- clientData = 0;
+ clientData = NULL;
name = Tcl_GetString(objv[3]);
+
+ /* First ensure the name given is valid */
+ if (Tcl_FindCommand(interp, name, NULL,
+ TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+
+ resultListPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
- TraceCommandProc, clientData)) != 0) {
+ TraceCommandProc, clientData)) != NULL) {
TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
@@ -3323,7 +3347,7 @@ TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
if (tcmdPtr->flags & TCL_TRACE_ENTER_EXEC) {
Tcl_ListObjAppendElement(NULL, elemObjPtr,
- Tcl_NewStringObj("enter",6));
+ Tcl_NewStringObj("enter",5));
}
if (tcmdPtr->flags & TCL_TRACE_LEAVE_EXEC) {
Tcl_ListObjAppendElement(NULL, elemObjPtr,
@@ -3335,12 +3359,13 @@ TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
}
if (tcmdPtr->flags & TCL_TRACE_LEAVE_DURING_EXEC) {
Tcl_ListObjAppendElement(NULL, elemObjPtr,
- Tcl_NewStringObj("leavestep",10));
+ Tcl_NewStringObj("leavestep",9));
}
Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
-
- elemObjPtr = Tcl_NewStringObj(tcmdPtr->command, -1);
- Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
+ elemObjPtr = NULL;
+
+ Tcl_ListObjAppendElement(NULL, eachTraceObjPtr,
+ Tcl_NewStringObj(tcmdPtr->command, -1));
Tcl_ListObjAppendElement(interp, resultListPtr,
eachTraceObjPtr);
}
@@ -3436,6 +3461,7 @@ TclTraceCommandObjCmd(interp, optionIndex, objc, objv)
tcmdPtr->startLevel = 0;
tcmdPtr->startCmd = NULL;
tcmdPtr->length = length;
+ tcmdPtr->refCount = 1;
flags |= TCL_TRACE_DELETE;
strcpy(tcmdPtr->command, command);
name = Tcl_GetString(objv[3]);
@@ -3452,11 +3478,17 @@ TclTraceCommandObjCmd(interp, optionIndex, objc, objv)
*/
TraceCommandInfo *tcmdPtr;
- ClientData clientData;
- clientData = 0;
+ ClientData clientData = NULL;
name = Tcl_GetString(objv[3]);
+
+ /* First ensure the name given is valid */
+ if (Tcl_FindCommand(interp, name, NULL,
+ TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+
while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
- TraceCommandProc, clientData)) != 0) {
+ TraceCommandProc, clientData)) != NULL) {
tcmdPtr = (TraceCommandInfo *) clientData;
if ((tcmdPtr->length == length)
&& (tcmdPtr->flags == flags)
@@ -3465,7 +3497,10 @@ TclTraceCommandObjCmd(interp, optionIndex, objc, objv)
Tcl_UntraceCommand(interp, name,
flags | TCL_TRACE_DELETE,
TraceCommandProc, clientData);
- ckfree((char *) tcmdPtr);
+ tcmdPtr->flags |= TCL_TRACE_DESTROYED;
+ if ((--tcmdPtr->refCount) <= 0) {
+ ckfree((char *) tcmdPtr);
+ }
break;
}
}
@@ -3480,11 +3515,18 @@ TclTraceCommandObjCmd(interp, optionIndex, objc, objv)
return TCL_ERROR;
}
- resultListPtr = Tcl_GetObjResult(interp);
- clientData = 0;
+ clientData = NULL;
name = Tcl_GetString(objv[3]);
+
+ /* First ensure the name given is valid */
+ if (Tcl_FindCommand(interp, name, NULL,
+ TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+
+ resultListPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
- TraceCommandProc, clientData)) != 0) {
+ TraceCommandProc, clientData)) != NULL) {
TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
@@ -3636,8 +3678,8 @@ TclTraceVariableObjCmd(interp, optionIndex, objc, objv)
&& (tvarPtr->flags == flags)
&& (strncmp(command, tvarPtr->command,
(size_t) length) == 0)) {
- Tcl_UntraceVar2(interp, name, NULL,
- flags | TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT,
+ Tcl_UntraceVar2(interp, name, NULL,
+ flags | TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT,
TraceVarProc, clientData);
Tcl_EventuallyFree((ClientData) tvarPtr, TCL_DYNAMIC);
break;
@@ -3719,8 +3761,9 @@ TclTraceVariableObjCmd(interp, optionIndex, objc, objv)
* the clientData argument is NULL then the first such trace is
* returned; otherwise, the next relevant one after the one
* given by clientData will be returned. If the command
- * doesn't exist, or if there are no (more) traces for it,
- * then NULL is returned.
+ * doesn't exist then an error message is left in the interpreter
+ * and NULL is returned. Also, if there are no (more) traces for
+ * the given command, NULL is returned.
*
* Side effects:
* None.
@@ -3826,6 +3869,7 @@ Tcl_TraceCommand(interp, cmdName, flags, proc, clientData)
tracePtr->flags = flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE
| TCL_TRACE_ANY_EXEC);
tracePtr->nextPtr = cmdPtr->tracePtr;
+ tracePtr->refCount = 1;
cmdPtr->tracePtr = tracePtr;
if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
cmdPtr->flags |= CMD_HAS_EXEC_TRACES;
@@ -3881,7 +3925,9 @@ Tcl_UntraceCommand(interp, cmdName, flags, proc, clientData)
if (tracePtr == NULL) {
return;
}
- if ((tracePtr->traceProc == proc) && ((tracePtr->flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE | TCL_TRACE_ANY_EXEC)) == flags)
+ if ((tracePtr->traceProc == proc)
+ && ((tracePtr->flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE |
+ TCL_TRACE_ANY_EXEC)) == flags)
&& (tracePtr->clientData == clientData)) {
if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
hasExecTraces = 1;
@@ -3908,7 +3954,10 @@ Tcl_UntraceCommand(interp, cmdName, flags, proc, clientData)
prevPtr->nextPtr = tracePtr->nextPtr;
}
tracePtr->flags = 0;
- Tcl_EventuallyFree((int*)tracePtr, TCL_DYNAMIC);
+
+ if ((--tracePtr->refCount) <= 0) {
+ ckfree((char*)tracePtr);
+ }
if (hasExecTraces) {
for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL; tracePtr != NULL ;
@@ -3962,7 +4011,7 @@ TraceCommandProc(clientData, interp, oldName, newName, flags)
int code;
Tcl_DString cmd;
- Tcl_Preserve((ClientData) tcmdPtr);
+ tcmdPtr->refCount++;
if ((tcmdPtr->flags & flags) && !(flags & TCL_INTERP_DESTROYED)) {
/*
@@ -4020,14 +4069,14 @@ TraceCommandProc(clientData, interp, oldName, newName, flags)
ckfree((char *)tcmdPtr->startCmd);
}
}
- /* Postpone deletion, until exec trace returns */
if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
+ /* Postpone deletion, until exec trace returns */
tcmdPtr->flags = 0;
- } else {
- Tcl_EventuallyFree((ClientData) tcmdPtr, TCL_DYNAMIC);
}
}
- Tcl_Release((ClientData) tcmdPtr);
+ if ((--tcmdPtr->refCount) <= 0) {
+ ckfree((char*)tcmdPtr);
+ }
return;
}
@@ -4057,7 +4106,8 @@ TraceCommandProc(clientData, interp, oldName, newName, flags)
*----------------------------------------------------------------------
*/
int
-TclCheckExecutionTraces(interp, command, numChars, cmdPtr, code, traceFlags, objc, objv)
+TclCheckExecutionTraces(interp, command, numChars, cmdPtr, code,
+ traceFlags, objc, objv)
Tcl_Interp *interp; /* The current interpreter. */
CONST char *command; /* Pointer to beginning of the current
* command string. */
@@ -4077,7 +4127,7 @@ TclCheckExecutionTraces(interp, command, numChars, cmdPtr, code, traceFlags, obj
TraceCommandInfo* tcmdPtr;
if (command == NULL || cmdPtr->tracePtr == NULL) {
- return(traceCode);
+ return traceCode;
}
curLevel = ((iPtr->varFramePtr == NULL) ? 0 : iPtr->varFramePtr->level);
@@ -4087,9 +4137,9 @@ TclCheckExecutionTraces(interp, command, numChars, cmdPtr, code, traceFlags, obj
active.cmdPtr = cmdPtr;
lastTracePtr = NULL;
- for ( tracePtr = cmdPtr->tracePtr;
- (traceCode == TCL_OK) && (tracePtr != NULL);
- tracePtr = active.nextTracePtr) {
+ for (tracePtr = cmdPtr->tracePtr;
+ (traceCode == TCL_OK) && (tracePtr != NULL);
+ tracePtr = active.nextTracePtr) {
if (traceFlags & TCL_TRACE_LEAVE_EXEC) {
/* execute the trace command in order of creation for "leave" */
active.nextTracePtr = NULL;
@@ -4105,8 +4155,12 @@ TclCheckExecutionTraces(interp, command, numChars, cmdPtr, code, traceFlags, obj
if (tcmdPtr->flags != 0) {
tcmdPtr->curFlags = traceFlags | TCL_TRACE_EXEC_DIRECT;
tcmdPtr->curCode = code;
+ tcmdPtr->refCount++;
traceCode = TraceExecutionProc((ClientData)tcmdPtr, interp,
curLevel, command, (Tcl_Command)cmdPtr, objc, objv);
+ if ((--tcmdPtr->refCount) <= 0) {
+ ckfree((char*)tcmdPtr);
+ }
}
lastTracePtr = tracePtr;
}
@@ -4137,7 +4191,8 @@ TclCheckExecutionTraces(interp, command, numChars, cmdPtr, code, traceFlags, obj
*----------------------------------------------------------------------
*/
int
-TclCheckInterpTraces(interp, command, numChars, cmdPtr, code, traceFlags, objc, objv)
+TclCheckInterpTraces(interp, command, numChars, cmdPtr, code,
+ traceFlags, objc, objv)
Tcl_Interp *interp; /* The current interpreter. */
CONST char *command; /* Pointer to beginning of the current
* command string. */
@@ -4171,9 +4226,10 @@ TclCheckInterpTraces(interp, command, numChars, cmdPtr, code, traceFlags, objc,
(traceCode == TCL_OK) && (tracePtr != NULL);
tracePtr = active.nextTracePtr) {
if (traceFlags & TCL_TRACE_ENTER_EXEC) {
- /* execute the trace command in reverse order of creation
+ /*
+ * Execute the trace command in reverse order of creation
* for "enterstep" operation. The order is changed for
- * ""enterstep" instead of for "leavestep as was done in
+ * "enterstep" instead of for "leavestep" as was done in
* TclCheckExecutionTraces because for step traces,
* Tcl_CreateObjTrace creates one more linked list of traces
* which results in one more reversal of trace invocation.
@@ -4195,22 +4251,28 @@ TclCheckInterpTraces(interp, command, numChars, cmdPtr, code, traceFlags, objc,
* The proc invoked might delete the traced command which
* which might try to free tracePtr. We want to use tracePtr
* until the end of this if section, so we use
- * Tcl_Preserve() and Tcl_Release() to be sureit is not
+ * Tcl_Preserve() and Tcl_Release() to be sure it is not
* freed while we still need it.
*/
Tcl_Preserve((ClientData) tracePtr);
tracePtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
- if ((tracePtr->flags != TCL_TRACE_EXEC_IN_PROGRESS) &&
+
+ if (tracePtr->flags & (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC)) {
+ /* New style trace */
+ if ((tracePtr->flags != TCL_TRACE_EXEC_IN_PROGRESS) &&
((tracePtr->flags & traceFlags) != 0)) {
- tcmdPtr = (TraceCommandInfo*)tracePtr->clientData;
- tcmdPtr->curFlags = traceFlags;
- tcmdPtr->curCode = code;
- traceCode = (tracePtr->proc)((ClientData)tcmdPtr,
- (Tcl_Interp*)interp,
- curLevel, command,
- (Tcl_Command)cmdPtr,
- objc, objv);
+ tcmdPtr = (TraceCommandInfo*)tracePtr->clientData;
+ tcmdPtr->curFlags = traceFlags;
+ tcmdPtr->curCode = code;
+ traceCode = (tracePtr->proc)((ClientData)tcmdPtr,
+ (Tcl_Interp*)interp,
+ curLevel, command,
+ (Tcl_Command)cmdPtr,
+ objc, objv);
+ }
} else {
+ /* Old-style trace */
+
if (traceFlags & TCL_TRACE_ENTER_EXEC) {
/*
* Old-style interpreter-wide traces only trigger
@@ -4287,14 +4349,38 @@ CallTraceProcedure(interp, tracePtr, cmdPtr, command, numChars, objc, objv)
/*
*----------------------------------------------------------------------
*
+ * CommandObjTraceDeleted --
+ *
+ * Ensure the trace is correctly deleted by decrementing its
+ * refCount and only deleting if no other references exist.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May release memory.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+CommandObjTraceDeleted(ClientData clientData) {
+ TraceCommandInfo* tcmdPtr = (TraceCommandInfo*)clientData;
+ if ((--tcmdPtr->refCount) <= 0) {
+ ckfree((char*)tcmdPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TraceExecutionProc --
*
* This procedure is invoked whenever code relevant to a
* 'trace execution' command is executed. It is called in one
* of two ways in Tcl's core:
*
- * (i) by the TclCheckExecutionTraces, when an execution trace has been
- * triggered.
+ * (i) by the TclCheckExecutionTraces, when an execution trace
+ * has been triggered.
* (ii) by TclCheckInterpTraces, when a prior execution trace has
* created a trace of the internals of a procedure, passing in
* this procedure as the one to be called.
@@ -4326,7 +4412,7 @@ TraceExecutionProc(ClientData clientData, Tcl_Interp *interp,
* not allow any further execution trace callbacks to
* be called for the same trace.
*/
- return(traceCode);
+ return traceCode;
}
if (!(flags & TCL_INTERP_DESTROYED)) {
@@ -4339,7 +4425,8 @@ TraceExecutionProc(ClientData clientData, Tcl_Interp *interp,
* operations, but with either of the step operations.
*/
if (flags & TCL_TRACE_EXEC_DIRECT) {
- call = flags & tcmdPtr->flags & (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
+ call = flags & tcmdPtr->flags & (TCL_TRACE_ENTER_EXEC |
+ TCL_TRACE_LEAVE_EXEC);
} else {
call = 1;
}
@@ -4423,7 +4510,7 @@ TraceExecutionProc(ClientData clientData, Tcl_Interp *interp,
tcmdPtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
iPtr->flags |= INTERP_TRACE_IN_PROGRESS;
- Tcl_Preserve((ClientData)tcmdPtr);
+ tcmdPtr->refCount++;
/*
* This line can have quite arbitrary side-effects,
* including deleting the trace, the command being
@@ -4454,14 +4541,17 @@ TraceExecutionProc(ClientData clientData, Tcl_Interp *interp,
* interpreter trace when it reaches the end of this proc.
*/
if ((flags & TCL_TRACE_ENTER_EXEC) && (tcmdPtr->stepTrace == NULL)
- && (tcmdPtr->flags & (TCL_TRACE_ENTER_DURING_EXEC | TCL_TRACE_LEAVE_DURING_EXEC))) {
+ && (tcmdPtr->flags & (TCL_TRACE_ENTER_DURING_EXEC |
+ TCL_TRACE_LEAVE_DURING_EXEC))) {
tcmdPtr->startLevel = level;
tcmdPtr->startCmd =
(char *) ckalloc((unsigned) (strlen(command) + 1));
strcpy(tcmdPtr->startCmd, command);
+ tcmdPtr->refCount++;
tcmdPtr->stepTrace = Tcl_CreateObjTrace(interp, 0,
(tcmdPtr->flags & TCL_TRACE_ANY_EXEC) >> 2,
- TraceExecutionProc, (ClientData)tcmdPtr, NULL);
+ TraceExecutionProc, (ClientData)tcmdPtr,
+ CommandObjTraceDeleted);
}
}
if (flags & TCL_TRACE_DESTROYED) {
@@ -4472,12 +4562,13 @@ TraceExecutionProc(ClientData clientData, Tcl_Interp *interp,
ckfree((char *)tcmdPtr->startCmd);
}
}
- Tcl_EventuallyFree((ClientData)tcmdPtr, TCL_DYNAMIC);
}
if (call) {
- Tcl_Release((ClientData)tcmdPtr);
+ if ((--tcmdPtr->refCount) <= 0) {
+ ckfree((char*)tcmdPtr);
+ }
}
- return(traceCode);
+ return traceCode;
}
/*
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 956ec4d..ff49a21 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -12,7 +12,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclInt.h,v 1.114 2003/01/09 10:38:29 vincentdarley Exp $
+ * RCS: @(#) $Id: tclInt.h,v 1.115 2003/01/17 14:19:49 vincentdarley Exp $
*/
#ifndef _TCLINT
@@ -287,6 +287,10 @@ typedef struct CommandTrace {
* TCL_TRACE_RENAME, TCL_TRACE_DELETE. */
struct CommandTrace *nextPtr; /* Next in list of traces associated with
* a particular command. */
+ int refCount; /* Used to ensure this structure is
+ * not deleted too early. Keeps track
+ * of how many pieces of code have
+ * a pointer to this structure. */
} CommandTrace;
/*
diff --git a/generic/tclStringObj.c b/generic/tclStringObj.c
index c951ae5..436dea6 100644
--- a/generic/tclStringObj.c
+++ b/generic/tclStringObj.c
@@ -33,7 +33,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclStringObj.c,v 1.26 2002/11/13 22:11:41 vincentdarley Exp $ */
+ * RCS: @(#) $Id: tclStringObj.c,v 1.27 2003/01/17 14:19:52 vincentdarley Exp $ */
#include "tclInt.h"
@@ -753,7 +753,6 @@ Tcl_SetObjLength(objPtr, length)
* representation of object, not including
* terminating null byte. */
{
- char *new;
String *stringPtr;
if (Tcl_IsShared(objPtr)) {
@@ -762,34 +761,61 @@ Tcl_SetObjLength(objPtr, length)
SetStringFromAny(NULL, objPtr);
/*
- * Invalidate the unicode data.
+ * We don't want to invalidate the unicode data if it exists, since
+ * if we are handling a Unicode object, objPtr->bytes may actually be
+ * NULL. Therefore either we must create that entry, or we must
+ * assume the object is being re-used as Unicode. For efficiency we
+ * do the latter.
*/
stringPtr = GET_STRING(objPtr);
- stringPtr->numChars = -1;
- stringPtr->uallocated = 0;
- if (length > (int) stringPtr->allocated) {
+ if (stringPtr->uallocated > 0) {
+ stringPtr->numChars = length;
- /*
- * Not enough space in current string. Reallocate the string
- * space and free the old string.
- */
- if (objPtr->bytes != tclEmptyStringRep) {
- new = (char *) ckrealloc((char *)objPtr->bytes,
- (unsigned)(length+1));
- } else {
- new = (char *) ckalloc((unsigned) (length+1));
- if (objPtr->bytes != NULL && objPtr->length != 0) {
- memcpy((VOID *) new, (VOID *) objPtr->bytes,
- (size_t) objPtr->length);
- Tcl_InvalidateStringRep(objPtr);
+ if (length > (int) stringPtr->uallocated) {
+ stringPtr = (String *) ckrealloc((char*) stringPtr,
+ STRING_SIZE(length));
+ stringPtr->uallocated = length;
+ }
+ /* Ensure the string is NULL-terminated */
+ stringPtr->unicode[length] = 0;
+
+ if (objPtr->bytes != NULL && (length > objPtr->length)) {
+ /*
+ * There is a utf-8 representation which is too short -- we
+ * are lengthening the string, and so we must discard it.
+ */
+ Tcl_InvalidateStringRep(objPtr);
+ }
+ } else {
+ stringPtr->numChars = -1;
+ stringPtr->uallocated = 0;
+
+ if (length > (int) stringPtr->allocated) {
+ char *new;
+
+ /*
+ * Not enough space in current string. Reallocate the string
+ * space and free the old string.
+ */
+ if (objPtr->bytes != tclEmptyStringRep) {
+ new = (char *) ckrealloc((char *)objPtr->bytes,
+ (unsigned)(length+1));
+ } else {
+ new = (char *) ckalloc((unsigned) (length+1));
+ if (objPtr->bytes != NULL && objPtr->length != 0) {
+ memcpy((VOID *) new, (VOID *) objPtr->bytes,
+ (size_t) objPtr->length);
+ Tcl_InvalidateStringRep(objPtr);
+ }
}
+ objPtr->bytes = new;
+ stringPtr->allocated = length;
}
- objPtr->bytes = new;
- stringPtr->allocated = length;
+
}
-
+
objPtr->length = length;
if ((objPtr->bytes != NULL) && (objPtr->bytes != tclEmptyStringRep)) {
objPtr->bytes[length] = 0;
diff --git a/tests/stringObj.test b/tests/stringObj.test
index c2db812..b27557d 100644
--- a/tests/stringObj.test
+++ b/tests/stringObj.test
@@ -12,7 +12,7 @@
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
-# RCS: @(#) $Id: stringObj.test,v 1.12 2002/11/13 22:11:41 vincentdarley Exp $
+# RCS: @(#) $Id: stringObj.test,v 1.13 2003/01/17 14:19:54 vincentdarley Exp $
if {[lsearch [namespace children] ::tcltest] == -1} {
package require tcltest
@@ -415,7 +415,7 @@ test stringObj-13.6 {Tcl_GetCharLength with mixed width chars} {
list [string length $a] [string length $a]
} {10 10}
-test stringObj-14.1 {Tcl_SetObjLength on pure unicode object} {knownBug} {
+test stringObj-14.1 {Tcl_SetObjLength on pure unicode object} {
teststringobj set 1 foo
teststringobj getunicode 1
teststringobj append 1 bar -1
diff --git a/tests/trace.test b/tests/trace.test
index 2e8b61b..52a6c4e 100644
--- a/tests/trace.test
+++ b/tests/trace.test
@@ -11,7 +11,7 @@
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
-# RCS: @(#) $Id: trace.test,v 1.24 2002/11/13 22:11:41 vincentdarley Exp $
+# RCS: @(#) $Id: trace.test,v 1.25 2003/01/17 14:19:55 vincentdarley Exp $
if {[lsearch [namespace children] ::tcltest] == -1} {
package require tcltest
@@ -865,27 +865,27 @@ test trace-14.12 {trace command ("remove variable" option)} {
set x 12345
set info
} {1}
-test trace-14.15 {trace command ("list variable" option)} {
+test trace-14.15 {trace command ("info variable" option)} {
catch {unset x}
trace add variable x write {traceTag 1}
trace add variable x write traceProc
trace add variable x write {traceTag 2}
trace info variable x
} {{write {traceTag 2}} {write traceProc} {write {traceTag 1}}}
-test trace-14.16 {trace command ("list variable" option)} {
+test trace-14.16 {trace command ("info variable" option)} {
catch {unset x}
trace info variable x
} {}
-test trace-14.17 {trace command ("list variable" option)} {
+test trace-14.17 {trace command ("info variable" option)} {
catch {unset x}
trace info variable x(0)
} {}
-test trace-14.18 {trace command ("list variable" option)} {
+test trace-14.18 {trace command ("info variable" option)} {
catch {unset x}
set x 44
trace info variable x(0)
} {}
-test trace-14.19 {trace command ("list variable" option)} {
+test trace-14.19 {trace command ("info variable" option)} {
catch {unset x}
set x 44
trace add variable x write {traceTag 1}
@@ -1604,36 +1604,36 @@ test trace-24.1 {delete trace during enter trace} {
set info {}
trace add execution foo enter [list traceDelete foo]
foo 1
- list $info [trace info execution foo]
-} {{{foo 1} enter} {}}
+ list $info [catch {trace info execution foo} res] $res
+} {{{foo 1} enter} 0 {}}
test trace-24.2 {delete trace during leave trace} {
set info {}
trace add execution foo leave [list traceDelete foo]
foo 1
- list $info [trace info execution foo]
-} {{{foo 1} 0 1 leave} {}}
+ list $info [catch {trace info execution foo} res] $res
+} {{{foo 1} 0 1 leave} 0 {}}
test trace-24.3 {delete trace during enter-leave trace} {
set info {}
trace add execution foo {enter leave} [list traceDelete foo]
foo 1
- list $info [trace info execution foo]
-} {{{foo 1} enter} {}}
+ list $info [catch {trace info execution foo} res] $res
+} {{{foo 1} enter} 0 {}}
test trace-24.4 {delete trace during all exec traces} {
set info {}
trace add execution foo {enter leave enterstep leavestep} [list traceDelete foo]
foo 1
- list $info [trace info execution foo]
-} {{{foo 1} enter} {}}
+ list $info [catch {trace info execution foo} res] $res
+} {{{foo 1} enter} 0 {}}
test trace-24.5 {delete trace during all exec traces except enter} {
set info {}
trace add execution foo {leave enterstep leavestep} [list traceDelete foo]
foo 1
- list $info [trace info execution foo]
-} {{{set b 1} enterstep} {}}
+ list $info [catch {trace info execution foo} res] $res
+} {{{set b 1} enterstep} 0 {}}
proc traceDelete {cmd args} {
rename $cmd {}
@@ -1649,8 +1649,8 @@ test trace-25.1 {delete command during enter trace} {
set info {}
trace add execution foo enter [list traceDelete foo]
catch {foo 1} err
- list $err $info [trace info execution foo]
-} {{invalid command name "foo"} {{foo 1} enter} {unknown command "foo"}}
+ list $err $info [catch {trace info execution foo} res] $res
+} {{invalid command name "foo"} {{foo 1} enter} 1 {unknown command "foo"}}
proc foo {a} {
set b $a
@@ -1660,8 +1660,8 @@ test trace-25.2 {delete command during leave trace} {
set info {}
trace add execution foo leave [list traceDelete foo]
foo 1
- list $info [trace info execution foo]
-} {{{foo 1} 0 1 leave} {unknown command "foo"}}
+ list $info [catch {trace info execution foo} res] $res
+} {{{foo 1} 0 1 leave} 1 {unknown command "foo"}}
proc foo {a} {
set b $a
@@ -1672,8 +1672,8 @@ test trace-25.3 {delete command during enter then leave trace} {
trace add execution foo enter [list traceDelete foo]
trace add execution foo leave [list traceDelete foo]
catch {foo 1} err
- list $err $info [trace info execution foo]
-} {{invalid command name "foo"} {{foo 1} enter} {unknown command "foo"}}
+ list $err $info [catch {trace info execution foo} res] $res
+} {{invalid command name "foo"} {{foo 1} enter} 1 {unknown command "foo"}}
proc foo {a} {
set b $a
@@ -1765,8 +1765,8 @@ test trace-25.8 {delete command during enter leave and enter/leave-step traces}
trace add execution foo enterstep [list traceDelete foo]
trace add execution foo leavestep [list traceDelete foo]
catch {foo 1} err
- list $err $info [trace info execution foo]
-} {{invalid command name "foo"} {{foo 1} enter} {unknown command "foo"}}
+ list $err $info [catch {trace info execution foo} res] $res
+} {{invalid command name "foo"} {{foo 1} enter} 1 {unknown command "foo"}}
proc foo {a} {
set b $a
@@ -1778,8 +1778,8 @@ test trace-25.9 {delete command during enter leave and leavestep traces} {
trace add execution foo leave [list traceDelete foo]
trace add execution foo leavestep [list traceDelete foo]
catch {foo 1} err
- list $err $info [trace info execution foo]
-} {{invalid command name "foo"} {{foo 1} enter} {unknown command "foo"}}
+ list $err $info [catch {trace info execution foo} res] $res
+} {{invalid command name "foo"} {{foo 1} enter} 1 {unknown command "foo"}}
proc foo {a} {
set b $a
@@ -1790,8 +1790,8 @@ test trace-25.10 {delete command during leave and leavestep traces} {
trace add execution foo leave [list traceDelete foo]
trace add execution foo leavestep [list traceDelete foo]
catch {foo 1} err
- list $err $info [trace info execution foo]
-} {1 {{set b 1} 0 1 leavestep} {unknown command "foo"}}
+ list $err $info [catch {trace info execution foo} res] $res
+} {1 {{set b 1} 0 1 leavestep} 1 {unknown command "foo"}}
proc foo {a} {
set b $a
@@ -1802,8 +1802,8 @@ test trace-25.11 {delete command during enter and enterstep traces} {
trace add execution foo enter [list traceDelete foo]
trace add execution foo enterstep [list traceDelete foo]
catch {foo 1} err
- list $err $info [trace info execution foo]
-} {{invalid command name "foo"} {{foo 1} enter} {unknown command "foo"}}
+ list $err $info [catch {trace info execution foo} res] $res
+} {{invalid command name "foo"} {{foo 1} enter} 1 {unknown command "foo"}}
test trace-26.1 {trace targetCmd when invoked through an alias} {
proc foo {args} {
@@ -1838,6 +1838,16 @@ test trace-27.1 {memory leak in rename trace (604609)} {
info commands foo
} {}
+test trace-27.2 {command trace remove nonsense} {
+ list [catch {trace remove command thisdoesntexist \
+ {delete rename} bar} res] $res
+} {1 {unknown command "thisdoesntexist"}}
+
+test trace-27.3 {command trace info nonsense} {
+ list [catch {trace info command thisdoesntexist} res] $res
+} {1 {unknown command "thisdoesntexist"}}
+
+
test trace-28.1 {enterstep and leavestep traces with update idletasks (615043)} {
catch {rename foo {}}
proc foo {} {
@@ -2019,6 +2029,65 @@ foo {if {[catch {bar}]} {
}} 2 error leavestep
foo foo 0 error leave}}
+test trace-28.5 {exec traces} {
+ set info {}
+ proc foo {args} { set a 1 }
+ trace add execution foo {enter enterstep leave leavestep} \
+ [list traceExecute foo]
+ after idle [list foo test-28.4]
+ update
+ # Complicated way of removing traces
+ set ti [lindex [eval [list trace info execution ::foo]] 0]
+ if {[llength $ti]} {
+ eval [concat [list trace remove execution foo] $ti]
+ }
+ join $info \n
+} {foo {foo test-28.4} enter
+foo {set a 1} enterstep
+foo {set a 1} 0 1 leavestep
+foo {foo test-28.4} 0 1 leave}
+
+test trace-28.6 {exec traces firing order} {
+ set info {}
+ proc enterStep {cmd op} {lappend ::info "enter $cmd/$op"}
+ proc leaveStep {cmd code result op} {lappend ::info "leave $cmd/$code/$result/$op"}
+
+ proc foo x {
+ set b x=$x
+ incr x
+ }
+ trace add execution foo enterstep enterStep
+ trace add execution foo leavestep leaveStep
+ foo 42
+ rename foo {}
+ join $info \n
+} {enter set b x=42/enterstep
+leave set b x=42/0/x=42/leavestep
+enter incr x/enterstep
+leave incr x/0/43/leavestep}
+
+test trace-28.7 {exec trace information} {
+ set info {}
+ proc foo x { incr x }
+ proc bar {args} {}
+ trace add execution foo {enter leave enterstep leavestep} bar
+ set info [trace info execution foo]
+ trace remove execution foo {enter leave enterstep leavestep} bar
+} {}
+
+test trace-28.8 {exec trace remove nonsense} {
+ list [catch {trace remove execution thisdoesntexist \
+ {enter leave enterstep leavestep} bar} res] $res
+} {1 {unknown command "thisdoesntexist"}}
+
+test trace-28.9 {exec trace info nonsense} {
+ list [catch {trace info execution thisdoesntexist} res] $res
+} {1 {unknown command "thisdoesntexist"}}
+
+test trace-28.10 {exec trace info nonsense} {
+ list [catch {trace remove execution} res] $res
+} {1 {wrong # args: should be "trace remove execution name opList command"}}
+
# Delete procedures when done, so we don't clash with other tests
# (e.g. foobar will clash with 'unknown' tests).
catch {rename foobar {}}