This section illustrates the use of the debugging facilities within CSS.
We will use the example program from the previous section, with the
addition of the two extra lines of code that were added in define
mode. It is important to note that some important aspects of debugging
like breakpoints can only be used when a program was started with the
run
command. Thus, do not set breakpoints if you are going to be
simply calling a function on the command line.
There are three primary debugging tools in CSS: breakpoints, execution tracing, and single-stepping. Because CSS is basically an interpreted system, all of the facilities for examining variables, the stack, source code, etc. which must be added into a real debugger come for "free" in CSS.
To illustrate, we will set a breakpoint in the Decode_Number
function (assuming you have loaded this code already):
css> list Decode_Number (String) Decode_Number((Real) number) { 4 String rval = "The number, " + number + " is: "; 5 if(number < 0) 6 rval += "negative"; 7 else if(number < .5) 8 rval += "less than .5"; 9 else if(number < 1) 10 rval += "less than 1"; 11 else 12 rval += "greater than 1"; 13 return rval; 14 } css> setbp 5 css> showbp Decode_Number 5 if(number < 0) css>
Note that we first listed the function (referring to it by name), and
then set a breakpoint with the setbp
command at line number 5.
We then confirmed this breakpoint with the showbp
command.
Now, when we run
the program, execution will stop at line 5, and
we will be returned to the css>
prompt:
css> run 2 css>
which has changed to 2 css>
, indicating that we are 2 levels deep
into the execution of the program. To see where we are, list
and
status
can be used:
2 css> list Listing of Program: css_example.css 4 String rval = "The number, " + number + " is: "; 5 if(number < 0) 6 rval += "negative"; 7 else if(number < .5) 8 rval += "less than .5"; 9 else if(number < 1) 10 rval += "less than 1"; 11 else 12 rval += "greater than 1"; 13 return rval; 14 } 28 list 2 css> status Status of Program: css_example.css curnt: Decode_Number src_ln: 5 pc: 9 debug: 0 step: 0 depth: 2 conts: 2 lines: 29 list: 28 State: shell: 1 run: 0 cont: 0 defn: 0 runlast: 0 run status: Waiting shell cmd: None 2 css>
The src_ln
of the status output tells us which source-code line
we are at (and what function we are in). Here, we can use the
interactive mode of css to determine the values of our variables:
2 css> print number (Real) number = 0.75 2 css> print rval (String) rval = The number, 0.75 is:
The values of all of the variables for the current "frame" (there is one
frame for every call to a given function, or any expression appearing
between the curly-brackets) can be viewed at once with the frame
command:
2 css> frame Elements of Spaces For Program: Decode_Number (frame = 1) Elements of Space: Autos (3) (String) _retv_this = (Real) number = 0.75 (String) rval = The number, 0.75 is: Elements of Space: Stack (0) Elements of Space: css_example.css.Static (3) (String) Decode_Number((Real) number) (void) Decode_Random((Int) n_numbers) (String) foo = a string
Included in the frame information are the "automatic" variables
(Autos
), and the contents of the stack. To get information on
previous frames in the execution sequence, use the command trace
,
which gives both a trace of processing and can give a dump of the entire
stack up to this point if given a higher "trace level", which is an
optional argument to the trace
command. The default trace level
of 0 just shows the frames, 1 also shows the stack, 2 also shows the
auto variables, and 3 shows all variables. In addition, just the
contents of the current stack can be viewed with the stack
command (note that the saved stack is what is actually used by the
program during execution).
In addition to viewing variables, it is possible to change their values:
2 css> number = 200; 2 css> print number (Real) number = 200
Finally, to continue the program from where it was stopped by the
breakpoint, use the cont
command:
2 css> cont The number, 0.75 is: greater than 1 5 css>
Since we changed the number after it was turned into a string for the
rval
, but before the if
statements, we got the
contradictory result printed above. Also, because the breakpoint is
still in effect, the program has stopped due to the Decode_Random
function calling the Decode_Number
function. We can continue
again, or we can disable the breakpoint first, and then continue.
5 css> cont 0 The number, 0.764017 is: less than 1 5 css> unsetbp 5 5 css> showbp 5 css> cont 1 The number, -1.76456 is: negative 2 The number, 1.59942 is: greater than 1 3 The number, -1.34582 is: negative 4 The number, -1.36371 is: negative css>
Note that the breakpoint is referred to by the source-code line number.
Another way to debug is to get a trace of the running program. This can
be done by setting the debug
level, which can have a value from 0
(normal, quiet operation) through 4, with higher levels giving more
detail than lower levels. For most users, levels 0 and 1 are the only
ones needed, since the higher levels depend on understanding the guts of
the CSS machine code. Debug level 1 shows the source-code line
corresponding to the currently-executing code:
css> debug 1 css> run 31 run 3 String Decode_Number(float number) { 17 void Decode_Random(int n_numbers) { 26 cout << Decode_Number(.75) << "\n"; 4 String rval = "The number, " + number + " is: "; 5 if(number < 0) 5 if(number < 0) 5 if(number < 0) 7 else if(number < .5) 7 else if(number < .5) 7 else if(number < .5) 9 else if(number < 1) 9 else if(number < 1) 9 else if(number < 1) 10 rval += "less than 1"; 13 return rval; 26 cout << Decode_Number(.75) << "\n"; The number, 0.75 is: less than 1 27 Decode_Random(5); 18 int i; 19 for(i=0; i<n_numbers; i++) { . . .
Since running proceeds from the top to the bottom, the definitions of
the functions appear in the trace even though they do not really do
anything. Also, some constructs like if
and for
result in
multiple copies of the same source-code line being printed. This kind
of trace is useful for seeing what branches of the code are being taken,
etc.
The final kind of debugging is single-stepping, which is like having a breakpoint after every line of source code. Execution continues after each point by simply entering a blank line:
css> debug 0 31 debug 0 css> step 1 css> run 31 run 3 String Decode_Number(float number) { 1 css> 17 void Decode_Random(int n_numbers) { 1 css> 26 cout << Decode_Number(.75) << "\n"; 4 String rval = "The number, " + number + " is: "; 2 css> print number 31 print number (Real) number = 0.75 2 css> print rval (String) rval = The number, 0.75 is: 5 if(number < 0) 3 css> 7 else if(number < .5) 4 css> 9 else if(number < 1) 5 css> 10 rval += "less than 1"; 4 css> 13 return rval; 26 cout << Decode_Number(.75) << "\n"; The number, 0.75 is: less than 1 1 css> 27 Decode_Random(5); 18 int i; 2 css> 19 for(i=0; i<n_numbers; i++) { 19 for(i=0; i<n_numbers; i++) { 20 float number = 4.0 * (drand48()-.5); 4 css>
(note that we turned debugging off, since it is redundant with
single-stepping). The step
command takes an argument, which is
the number of lines to step over (typically 1). Then, when we run
the program again, it stops after every line. If you simply want to
continue running, you can just hit return and it will continue to the
next line.
If at any point during debugging you want to stop the execution of the
program and return to the top-level (i.e., get rid of that number in
front of the prompt), use the restart
command.
4 css> restart 34 restart css>
Be sure not to confuse restart
with reset
, as the latter
will erase the current program from memory (you can just reload it with
reload
, so its not so bad if you do).