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 evalpoint.
(For detailed information on action points, see the section
Breakpoints.
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 evalpoint. 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 evalpoint.
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
}
Like the previous macros, almost all of the statements are Tcl. The only purely CLI commands are the instances of the dbreak command that set evalpoints 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 evalpoint at an eval line, or executes a command at a cmd line.
Using evalpoints can be confusing because evalpoint 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.