Squish the Bugs!
Nobody writes a perfect program. Some claim to be able to, but only one person has proven that he can write bug-free code. But that is a long story. You’d be amazed how many bugs Logo still contains! (Please let us know if you find a bug so we can fix it).
Why bugs? Why not errors, mistakes, or whatever? The story behind the word “bug” goes back to the late 1940s, to the time when computers were big monsters with vacuum tubes instead of silicon chips. These tubes were seething hot and needed a voltage of 300-400 volts. One day, an unfortunate bug managed to crawl into the computer. Eventually, the high voltage killed and fried the bug, causing the computer to crash…
Bugs are often hard to find. This chapter describes various way that Logo helps you to find and squash these bugs.
First of all, there is good news. You can pause your program at any time, or you can even stop your runaway program. The Listener panel has two button for this purpose:
|Click this button to pause your program and to enter the debugger.|
|The Stop button stops the execution of Logo and returns to toplevel.|
You can of course also use the Debug menu commands or hot keys to pause or stop a program.
The Typo Bug
Let us begin with a very common mistake, the typo bug. You simply misspelled a command, for example, and Logo already starts to complain. Enter this in the editor:
TO SQUARE :SIZE REPEAT 4 [FORWARD :SIZE RIGHT 90] END
This procedure draws a square with the requested size. Try
to draw a square.
Now, try to feed a bad value to the procedure. The
FORWARD command expects a number, so
SQUARE TRUE. Logo runs into a problem and displays the debugger:
The Debugger panel has three parts. The left part displays the affected procedure and highlights the offending line containing the error.
The right part displays all active procedures. The first line is the line that you entered into Logo. Then, a few lines follow that show how Logo got to the offending FORWARD command. Finally, the last line shows the command that caused the error. You can actually click on every line, and Logo displays the surrounding code for the line that you clicked. This way, you can quickly see how you got there.
The Listener in the bottom part lets you enter commands. You can enter any command you like. Drawing and editing commands should not be your first choice, because the Debugger dialog covers most of the screen, so the editor is out of reach, and the Graphics panel is most likely hidden behind the debugger.
The interesting part is the access to names. The Listener points to the part of the Logo workspace where you positioned the debugger at. In this case, the bottom line is activated, so feel free to query the value of N:
:N Result: TRUE
You could even alter the value of N, which would not help much in this case, because Logo already used the bad value, and all you can do for now is to stop the program.
You should keep in mind that the debugging Listener’s attention switches with the lines that you select. If you have multiple nested Logo procedure calls, you can switch to a certain point of the stack trace (that’s what this list of lines is called) to find out if Logo can “see” a name at that point, for example.
Pausing a Logo Program
Often, you would want to pause a running Logo program and see what it does exactly. Logo offers several ways to pause a program.
- Click the Pause button.
- Or add a PAUSE command to your procedure.
- Or use the STEP command.
- Or set a breakpoint by clicking a line number.
The latter two require some explanation. If you, for example, would like
to pause the program whenever it enters the
SQUARE procedure, use the
“SQUARE. Alternatively, just
click the line number of the line that you want Logo to paause at. A red
dot appears, and Logo will pause whenever it reaches a line with a red
For hardcore developers, you can even cause Logo to pause when a variable or a property list has changed. See the description of the STEP command for details.
Breakpoints are Your Friends
Most often, you would want Logo to pause at a specific line, like, for example, a line containing the OUTPUT command that will return a calculated value. The Logo editor lets you set so-called breakpoints that do exactly this. Just click the line number of the line where you want Logo to pause. A red dot appears next to the line number, indicating that Logo is aware of your request, and has remembered that line number as a place to pause. Click the line number again to delete the breakpoint. You can do this either in the Editor or in the Debugger dialog.
If you now start your program, Logo will pause at any line that has been marked as a breakpoint, provided that there is some code to execute at that line. It will ignore breakpoints set at lines that do not contain any code. Logo will also not pause at the very first line of a procedure (the line containing the TO command), because the procedure’s code usually begins at the next line.
Even when Logo pauses, you can alter your breakpoints as much as you like before continuing your program. And if you have set a gazillion of breakpoints, and you want to get rid of them altogether, enter the BPCLEAR command.
Breakpoints are incredibly powerful, because setting them does not require you to change any procedure; they are only a click away!
A quick note about breakpoints that you set in the editor. These breakpoints may not work as expected until you actually run your editor contents. The contents of the editor and the contents of the Logo workspace (which also contains the defined procedure) are very different. So, if you, for example, define a procedure and later return to the editor and edit that procedure without redefining it, the line numbers may not match. The same happens after restoring a Logo environment: the editor contents and the contents of the workspace do not match until you redefine the editor contents.
During a PAUSE
Run the above procedure with a breakpoint at line 3 as in the picture
above. Use the command
POLYGON 5 50, which should draw a 5-sided
polygon with 50 pixels per side. Obediently, Logo pauses at line 3 and
pops up the debugger:
During a pause, you can issue any Listener commands that you would like.
It may, for example, be convenient to change the value of a local
variable with the MAKE command. In the
above example, you examine
:SIZE and change its value to 100. Then,
click the green Continue button; and Logo draws the polygon with a side
length of 100 pixels!
You can even use your usual edit commands to redefine your procedure.
EDIT “POLYGON would edit your POLYGON procedure, and
simply re-open the editor. Note that during debugging, the Logo panels
are inaccessible, so the editor will be a dialog box instead. Also note
that Logo does not use a redefined procedure right away; it will use it
when it calls the procedure the next time.
To end a pause, you have several options to continue execution:
- Step Into: Each click on the Step button lets Logo execute the next line, and then pause again. This way, you can step through your program line by line. If your procedure calls another procedure, the debugger steps into that procedure.
- Step Over: This button lets Logo execute the next line, and then pause again. It does not, however, step into and other Logo procedures, but runs them normally while staying in the current procedure.
- Step Out: This button lets Logo execute the whole remainder of the procedure and stop at the END command, displaying any value that the procedure outputs. This is very convenient if you just want to return to the calling procedure.
- Continue: Click this button to continue the execution of your program. This may not help you much if Logo paused because of a runtime error, though.
- Stop: The Stop button stops the execution of Logo and returns to toplevel.
Other debugging commands
The STEP command causes each procedure mentioned in its input to pause as soon as it has been entered.
But you can use STEP for variables and property lists as well! If you STEP a variable, your program pauses whenever the value of the variable changes. If you STEP a property list, your program pauses whenever a property in the property list changes. This is a very powerful feature.
This command would cause Logo to enter debugging mode every time a
“EMPLOYEES changes. Note the use of a contents list, where
the third element is a list of property list names. The first element
would be a list of procedures to step, and the second list element would
be a list of names to step. You can, of course, fill in whatever names
that you want to step.
STEP [  [EMPLOYEES]]
The TRACE command works much in the same way, but instead of pausing your program, it prints messages whenever a procedure starts or exits, a variable is changed, or a property list is changed. But be warned: this can be a lot of output!
Finally, you can set the value of the built-in variable :TRACE. See its description for details; setting this variable causes all procedure calls, and all changes to variables and property lists to be traced.
But, there is more. If you suspect that a loop or a procedure call takes too much time to execute, you can actually measure that time. The MILLISECONDS command outputs a high-resolution timer value; you can subtract two values to determine an execution time. This is an example:
TO SOME.PROC LMAKE “START MILLISECONDS ; some lengthy operation, and afterwards… (PRINT “|It took| (MILLISECONDS - :START) “|milliseconds|) END