:TITLE: More Debugging - trace ;# ;# RCSID: $Header: /cvsroot/tcl/tcltutorial/original/Tcl37.lsn,v 1.1 2004/11/04 16:01:14 davidw Exp $ ;# Copyright (c) 1995 Clif Flynt ;# 9300 Fleming Rd. ;# Dexter, MI 48130 ;# clif@cflynt.com ;# See file "NOTICE" for licensing terms. ;# :LESSON_TEXT_START_LEVEL 0: The Tcl interpreter supports a method for tracking when and how a variable is accessed. With the trace command, a procedure can be defined to be executed whenever a variable is read, written, or unset.

The trace command executes at the same stack level as the access to the variable. The proc that trace invokes is one stack level lower. Thus, with the uplevel command, a procedure called via a trace can report on the conditions that were set when a variable was accessed.

trace variable variableName operation procname
Places a trace on the variable variableName. Whenever variableName is accessed for the operation specified in the operation argument, the procedure procname will be called.

Operation is one of:

r ...... Read
w .... Write
u ...... Unset

A variable can be unset either explicitly with the unset command, or implicitly when a procedure returns, and all of the local variables are released.

When variableName is accessed, procName will be called with three arguments, variableName, elementName and operation. If the variable being accessed is an associative array, then elementName will contain the name of the element being accessed. If variableName is a simple variable, then elementName will be an empty string. Operation is the operation that was done on variableName to invoke the trace action.

trace vdelete variableName operation procname
Cancels the trace action for operation on variableName.
trace vinfo variableName
Returns information about the traces applied to variableName
:TEXT_END: :LESSON_TEXT_START_LEVEL 1: When you are debugging a program, it's sometimes useful to know when a variable gets changed. The Tcl interpreter supports a method for tracking when and how a variable is accessed. With the trace command, a procedure can be defined to be executed whenever a variable is read, written, or unset. With this command you can determine what proc a variable is modified within, what the value was changed to, and what other variables were at the time.

The trace command executes at the same stack level as the access to the variable. The proc that trace invokes is one stack level lower. Thus, with the uplevel command, a procedure called via a trace can report on the conditions that were set when a variable was accessed.

trace variable variableName operation procname
Places a trace on the variable variableName. Whenever variableName is accessed for the operation specified in the operation argument, the procedure procname will be called.

Operation is one of:

r ...... Read
w .... Write
u ...... Unset

A variable can be unset either explicitly with the unset command, or implicitly when a procedure returns, and all of the local variables are released.

When variableName is accessed, procName will be called with three arguments, variableName, elementName and operation. If the variable being accessed is an associative array, then elementName will contain the name of the element being accessed. If variableName is a simple variable, then elementName will be an empty string. Operation is the operation that was done on variableName to invoke the trace action.

trace vdelete variableName operation procname
Cancels the trace action for operation on variableName.
trace vinfo variableName
Returns information about the traces applied to variableName
:TEXT_END: :LESSON_TEXT_START_LEVEL 2: A variable's value gets changed often during the execution of a computer program. Sometimes the value gets changed in a place or manner that the programmer doesn't expect. For example, if you have a variable that you are using to keep a particular value, and then forget and use that same variable name as a counter in a loop the value of the variable will be lost.

You can avoid this sort of bug by using long, descriptive variable names, instead of easy to type names like i or x.

You can track down this sort of bug by using the trace command.

The trace command lets you set a procedure to be executed whenever a particular variable is read, written, or unset.

When a variable with a trace set on it is accessed, the proc declared in the trace command is executed. The trace command executes at the same stack level as the access to the variable. The proc that trace invokes is one stack level lower. Thus, with the uplevel command, a procedure called via a trace can report on the conditions that were set when a variable was accessed.

When a trace is activated, it calls the procedure with three arguments. These arguments are the variable name that invoked the trace, the element in the array that invoked the trace, if the trace was set on an associative array variable. If the variable that invoked the trace was a normal variable, this will be an empty string. And finally the operation that was done on the variable to cause the trace to be activated.

Run the example, and look at the example code now.

The proc traceproc is defined first. This is the procedure that will be called in this example when a trace is activated. The three arguments to this proc are the variableName, the arrayElement, and the operation.

The first line of the proc just sets a local associative array with the verbose equivalent of the w, u, and r operations that will be passed to this function. This is simply to make the output easier to read.

The next sequence of lines gets the current value of the stack, and reduces it by one. If this value is 0, then the trace was invoked from the main level. If the value is larger, then the trace was invoked from a proc, and info level $level will return the proc name and the arguments for later display.

The final section of this proc checks arrayElement to determine if the variable that triggered the trace was a simple variable, or an associative array. If $arrayElement contains an empty string, then the variable that triggered the trace was a simple variable, and $variableName is reported in the output. If $arrayElement is not equal to the empty string, then $variableName is an associative array, and the array element will be reported.

Next, the testProc proc is defined. This proc sets the value of the first argument, and reads the second argument. It does the upvar commands because Tcl defaults to call by value, instead of call by reference when a variable is passed to a proc. Thus, a copy of the contents of i1 and i2 would be passed to testProc, instead of the actual variable. In that case, the trace would not be triggered, since the variables being traced wouldn't be being accessed.

Next, the trace command is invoked for variables i1 and i2. Note that these variables haven't been defined yet. The trace command has three subcommands, one of which is variable. This is the command that sets trace to activate when a particular variable is accessed. These three lines set a trace to be activated when variable i1 is written, and when variable i2 is read or written.

When variable i2 is initialized to "testvalue", the trace is invoked, and the output displays that i2 was written to at the main level.

Note that when testProc is invoked, the output shows the local names of the variables assigned in the upvar command, instead of the names that were used when the trace was set.

The trace subcommand vinfo will return a list of the traces on a variable. This is demonstrated in the next two lines of the example.

The third subcommand of trace is the vdelete. This command deletes a trace. The last few lines of the example show the read trace on i2 being removed. After the vdelete, the read trace doesn't show in the list of traces from vinfo, and the trace isn't triggered when testProc is called.

trace variable variableName operation procname
Places a trace on the variable variableName. Whenever variableName is accessed for the operation specified in the operation argument, the procedure procname will be called.

Operation is one of:

r ...... Read
w .... Write
u ...... Unset

A variable can be unset either explicitly with the unset command, or implicitly when a procedure returns, and all of the local variables are released.

When variableName is accessed, procName will be called with three arguments, variableName, elementName and operation. If the variable being accessed is an associative array, then elementName will contain the name of the element being accessed. If variableName is a simple variable, then elementName will be an empty string. Operation is the operation that was done on variableName to invoke the trace action.

trace vdelete variableName operation procname
Cancels the trace action for operation on variableName.
trace vinfo variableName
Returns information about the traces applied to variableName
:TEXT_END: :CODE_START: proc traceproc {variableName arrayElement operation} { set op(w) "Write"; set op(u) "Unset"; set op(r) "Read" set level [info level] incr level -1; if {$level > 0} { set procid [info level $level] } else {set procid "main"} if {![string match $arrayElement ""]} { puts "TRACE: $op($operation) $variableName($arrayElement) in $procid" } else { puts "TRACE: $op($operation) $variableName in $procid" } } proc testProc {input1 input2} { upvar $input1 i upvar $input2 j set i 2 set k $j; } trace variable i1 w traceproc trace variable i2 r traceproc trace variable i2 w traceproc set i2 "testvalue" puts "\ncall testProc" testProc i1 i2 puts "\nTraces on i1: [trace vinfo i1]" puts "Traces on i2: [trace vinfo i2]\n" trace vdelete i2 r traceproc puts "Traces on i2 after vdelete: [trace vinfo i2]" puts "\ncall testProc again" testProc i1 i2 :TEXT_END: