Automatically Setting Breakpoints
In many cases, your knowledge of what a program is doing lets you make predictions as to where problems are occurring. The following CLI macro parses comments that you can include in a source file and, depending on the comment’s text, sets a
breakpoint or an eval point.
(For detailed information on action points, see
“Setting Action Points”.)Following this macro is an excerpt from a program that uses it.
# make_actions: Parse a source file, and insert
# evaluation and breakpoints according to comments.
#
proc make_actions {{filename ""}} {
if {$filename == ""} {
puts "You need to specify a filename"
error "No filename"
}
# Open the program’s source file and initialize a
# few variables.
set fname [set filename]
set fsource [open $fname r]
set lineno 0
set incomment 0
# Look for "signals" that indicate the type of
# action point; they are buried in the comments.
while {[gets $fsource line] != -1} {
incr lineno
set bpline $lineno
# Look for a one-line eval point. The
# format is ... /* EVAL: some_text */.
# The text after EVAL and before the "*/" in
# the comment is assigned to "code".
if [regexp "/\\* EVAL: *(.*)\\*/" $line all code] {
dbreak $fname\#$bpline -e $code continue
}
# Look for a multiline eval point.
if [regexp "/\\* EVAL: *(.*)" $line all code] {
# Append lines to "code".
while {[gets $fsource interiorline] != -1} {
incr lineno
# Tabs will confuse dbreak.
regsub -all \t $interiorline \
" " interiorline
# If "*/" is found, add the text to "code",
# then leave the loop. Otherwise, add the
# text, and continue looping.
if [regexp "(.*)\\*/" $interiorline \
all interiorcode]{
append code \n $interiorcode
break
} else {
append code \n $interiorline
}
}
dbreak $fname\#$bpline -e $code
continue
}
# Look for a breakpoint.
if [regexp "/\\* STOP: .*" $line] {
dbreak $fname\#$bpline
continue
}
# Look for a command to be executed by Tcl.
if [regexp "/\\* *CMD: *(.*)\\*/" $line all cmd] {
puts "CMD: [set cmd]"
eval $cmd
}
}
close $fsource
}
The only similarity between this macro and the previous three is that almost all of the statements are Tcl. The only purely CLI commands are the instances of the
dbreak command that set eval points and breakpoints.
The following excerpt from a larger program shows how to embed comments in a source file that is read by the make_actions macro:
...
struct struct_bit_fields_only {
unsigned f3 : 3;
unsigned f4 : 4;
unsigned f5 : 5;
unsigned f20 : 20;
unsigned f32 : 32;
} sbfo, *sbfop = &sbfo;
...
int main()
{
struct struct_bit_fields_only *lbfop = &sbfo;
...
int i;
int j;
sbfo.f3 = 3;
sbfo.f4 = 4;
sbfo.f5 = 5;
sbfo.f20 = 20;
sbfo.f32 = 32;
...
/* TEST: Check to see if we can access all the
values */
i=i; /* STOP: */
i=1; /* EVAL: if (sbfo.f3 != 3) $stop; */
i=2; /* EVAL: if (sbfo.f4 != 4) $stop; */
i=3; /* EVAL: if (sbfo.f5 != 5) $stop; */
...
return 0;
}
The make_actions macro reads a source file one line at a time. As it reads these lines, the regular expressions look for comments that begin with /* STOP, /* EVAL, and /* CMD. After parsing the comment, it sets a breakpoint at a stop line, an eval point at an eval line, or executes a command at a cmd line.
Using eval points can be confusing because eval point syntax differs from that of Tcl. In this example, the $stop function is built into the CLI. Stated differently, you can end up with Tcl code that also contains C, C++, Fortran, and TotalView functions, variables, and statements. Fortunately, you only use this kind of mixture in a few places and you’ll know what you’re doing.