ScriptBasic Debugger Users' Guide

by Peter Verhas

Table of Contents

[Contents]

1. Introduction

ScriptBasic debugger dbg allows programmers to debug BASIC programs. The debugger provides a console interface that can be used to execute a program line, by line, to a specified line, set break points and while in debug mode examine variables.

The debugger can also be used to debug CGI programs utilizing the Eszter SB Application Engine.

[Contents]

2. Installing the Preprocessor dbg

Installing the preprocessor is usually not needed as this is automatically installed during ScriptBasic installation. In the following sections I describe how to install it in case something is not working or you want to set up some files differently.

The debugger is implemented as an internal preprocessor. This means that the code of the preprocessor is in a .dll file under Windows and .so file under UNIX. Internal preprocessors have to be configured to be invoked by the interpreter.

To do this the file scriba.conf.lsp has to be modified and recompiled using the program cftc in case the default configuration does not fit you.

The preprocessors in the configuration file are configured under the key preproc.

preproc (
  internal (
    dbg "E:\\ScriptBasic\\modules\\dbg.dll"
    )
  )

This is the configuration file fragment on the development station. This names the preprocessor the name dbg and specifies the exact location. You can name the debugger different though this would not be wise. In case you rename the debugger you have to invoke it with the option `-I newname'.

The debugger requires no other configuration.

[Contents]

3. Using the Debugger

This chapter uses an example program to debug. While we debug the code the sections go through the available commands showing the use by example. Thus the recommended reading is going through the sections one after the other.

[Contents]

3.1. Starting Program Debug from Command Line

To start a program in debug mode you should start the program on the command line with the option `-i dbg'. This will load the debugger module into the interpreter before starting to execute the program, and after successful compilation of the code into internal format the execution stops and waits for user intervention.

For example you can say:

scriba -i debugtest.bas

to start the sample program `debugtest.bas' in debug mode. ScriptBasic compiles the program into byte-code and gives you a prompt:

ScriptBasic debugger, executing
  debugtest.bas
For help type 'h'

----------------------------------------------------- >001. print "hello\n" 002. a = 3 003. 004. sub MyFunction(t) 005. local a,b,c

----------------------------------------------------- #_

(The _ character shows the place of the blinking cursor.)

The debugger has already listed some of the code around the actual program line. The > character precedes the line where the execution of the program is waiting.

[Contents]

3.2. Single Stepping

After you have started the program the most frequent thing you will do is single-step. Single step means executing one line of the BASIC program at a time. To do this press s and ENTER. Because this is the command you will issue most frequently it is enough just to press ENTER.

Now what you see is (also recalling the lines that were already presented in the previous section):

ScriptBasic debugger, executing
  debugtest.bas
For help type 'h'

----------------------------------------------------- >001. print "hello\n" 002. a = 3 003. 004. sub MyFunction(t) 005. local a,b,c

----------------------------------------------------- #s hello

----------------------------------------------------- 001. print "hello\n" >002. a = 3 003. 004. sub MyFunction(t) 005. local a,b,c

----------------------------------------------------- #_

You can see that the program has written to the console hello that actually came from the BASIC program and a new listing. Notice that the actual line now is the second line and the character > points this line.

[Contents]

3.3. Listing Lines

As you could see the debugger lists some of the lines for you around the actual execution point. To be precise the debugger automatically displays five lines before executing any line: two lines before the actual line, the actual line and two lines after. This slightly altered for the first and the last two lines.

Each line contains a serial number. This number identifies the line for the debugger and when you issue a command that should specify a certain line you can use this number. For example issuing the command r 22 will run until the line denoted by the serial number 22 is ready for execution.

This serial number is not necessarily the line number in the source file. This is the serial number of the line inside the program after all #include and #import statements were performed.

Sometimes you may want to list other lines. To do this simply issue the command l. (This is lower case l, upper case L lists local variables.)

#l

----------------------------------------------------- 001. print "hello\n" >002. a = 3 003. 004. sub MyFunction(t) 005. local a,b,c

----------------------------------------------------- #_

As you can see you got the same listing. This seems to be useless, unless you have examined several variables and the original listing was scrolled off and you need another peek at the current lines.

However the command l is more useful than just this. You can issue an argument to the command specifying the start and the end lines for the listing.

For example:

#l 2-7

----------------------------------------------------- >002. a = 3 003. 004. sub MyFunction(t) 005. local a,b,c 006. 007. b = t + 2

----------------------------------------------------- #_

The command l n-m lists the lines from line n to line m. If you omit the start number then the listing will start at the first line. If you omit the end number the listing will go to the end of the program. If you omit both numbers the command will list all the program lines, for example:

#l -

----------------------------------------------------- 001. print "hello\n" >002. a = 3 003. 004. sub MyFunction(t) 005. local a,b,c 006. 007. b = t + 2 008. print b 009. print " ",t 010. if t > 1 then MyFunction t-1 011. print " *" 012. end sub 013. 014. MyFunction a 015. 016. a = a + 2 017. 018. MyFunction a

----------------------------------------------------- #_

@examining Variables

Stepping forward and examining where the program is actually executing is useful, but not enough. You can also examine the actual content of variables. To do this the command ? can be used. Type ? A at the prompt!

#? a
undef
#_

Step one step ahead again and repeat the command!

#s

----------------------------------------------------- 002. a = 3 003. >004. sub MyFunction(t) 005. local a,b,c 006.

----------------------------------------------------- #? a 3 #_

[Contents]

3.4. Stepping over Function or Subroutine

Up to now we used the command s to advance the execution of the program. But there is a similar command S. Step ahead one step until you get to the function call calling MyFunction.

#s

----------------------------------------------------- 012. end sub 013. >014. MyFunction a 015. 016. a = a + 2

----------------------------------------------------- #_

Now if you issue the debugger command s then you get into the function and you can debug the function itself. But for now assume that you are quite confident with the function fully debugged and you want to treat the function call if it was a single instruction. Issue the command S!

#S
5 34 23 1 * * *
-----------------------------------------------------
  014. MyFunction a
  015.
 >016. a = a + 2
  017.
  018. MyFunction a

----------------------------------------------------- #_

You can see the printouts that were recursively generated by the function.

[Contents]

3.5. Stopping the program

As you saw in the previous chapter the printout you realize that the numbers 34 and 23 are not OK. You wish you typed s to step into the function instead of stepping over.

No problem. Stop debugging and restart the debugger! To do this issue the command q.

#q
Ok... you have said that... quitting...
(0): error &H37:The preprocessor said to abort program compilation or execution.
#_

This generates a special error that causes the interpreter to finish execution.

[Contents]

3.6. Running to Line

Start the debugger again and without debugging let the program run to the line where the function call starts. To do this the command r should be used.

E:\MyProjects\sb\source\examples>scriba -i dbg debugtest.bas
ScriptBasic debugger, executing
  debugtest.bas
For help type 'h'

----------------------------------------------------- >001. print "hello\n" 002. a = 3 003. 004. sub MyFunction(t) 005. local a,b,c

----------------------------------------------------- #r 14 hello

----------------------------------------------------- 012. end sub 013. >014. MyFunction a 015. 016. a = a + 2

----------------------------------------------------- #_

The command r has one argument, the number of the line to run to. If the argument is missing the program will run until it finishes or until a breakpoint is found.

Now step into the function and step until the first number is printed.

#s

----------------------------------------------------- 002. a = 3 003. >004. sub MyFunction(t) 005. local a,b,c 006.

----------------------------------------------------- #s

----------------------------------------------------- 005. local a,b,c 006. >007. b = t + 2 008. print b 009. print " ",t

----------------------------------------------------- #s

----------------------------------------------------- 006. 007. b = t + 2 >008. print b 009. print " ",t 010. if t > 1 then MyFunction t-1

----------------------------------------------------- #s 5 ----------------------------------------------------- 007. b = t + 2 008. print b >009. print " ",t 010. if t > 1 then MyFunction t-1 011. print " *"

-----------------------------------------------------

Now we want to advance program execution to the line 11 so we issue the command r 11.

#r 11
 34 23 1
-----------------------------------------------------
  009. print " ",t
  010. if t > 1 then MyFunction t-1
 >011. print " *"
  012. end sub
  013.

----------------------------------------------------- #_

You get the numbers printed again, but where are the *? When MyFunction t-1 was called it printed the starts didn't it? The answer is that is did not.

Running to line 11 started to execute the program and stopped the execution as soon the line 11 the first time was about to be executed. Our function called itself with the argument 2 and then even getting deeper with argument 1 before it did not call itself any deeper and execution stopped at line 11.

Quit the program again, start debugging again and first run to line 9, and then issue the command R 11:

#q
Ok... you have said that... quitting...
(0): error &H37:The preprocessor said to abort program compilation or execution.

E:\MyProjects\sb\source\examples>scriba -i dbg debugtest.bas ScriptBasic debugger, executing debugtest.bas For help type 'h'

----------------------------------------------------- >001. print "hello\n" 002. a = 3 003. 004. sub MyFunction(t) 005. local a,b,c

----------------------------------------------------- #r 9 hello 5 ----------------------------------------------------- 007. b = t + 2 008. print b >009. print " ",t 010. if t > 1 then MyFunction t-1 011. print " *"

----------------------------------------------------- #R 11 34 23 1 * * ----------------------------------------------------- 009. print " ",t 010. if t > 1 then MyFunction t-1 >011. print " *" 012. end sub 013.

----------------------------------------------------- #_

This time the program did not stop in the recursive function call.

The command R is similar to command r, but it does not stop at the line in recursive functions.

[Contents]

3.7. Walking Along the Stack

Examine the variable a again.

#R 11
 34 23 1 * *
-----------------------------------------------------
  009. print " ",t
  010. if t > 1 then MyFunction t-1
 >011. print " *"
  012. end sub
  013.

----------------------------------------------------- #? a undef #_

This is undef because now we examine the local variable. Issue the command u and examine the variable again.

#u
done
#?a
3
#_

Issuing the command u we stepped one level up in the call stack. At that point the variable a is global and has value. That is actually a different variable that happens to have the same name.

Issue the command l to see where we are.

#l

----------------------------------------------------- 012. end sub 013. >014. MyFunction a 015. 016. a = a + 2

----------------------------------------------------- #

To get one level deeper in the stack issue the command d.

#d
done
#l

----------------------------------------------------- 009. print " ",t 010. if t > 1 then MyFunction t-1 >011. print " *" 012. end sub 013.

----------------------------------------------------- #_

If you stepped several levels up in the stack you should issue that many commands d or just one D to go to the bottom of the stack.

[Contents]

3.8. Listing Variables

Using the command ? we could examine the value of a variable. There are two other commands to examine variables. These are: L and G. These commands list the Local and the Global variables. Try it!

#L
main::t=->3
main::a=undef
main::b=5
main::c=undef
#G
main::a=3
#_

[Contents]

3.9. Stepping Out of Function

Now that we have debugged the function enough we do not want to step any further inside the function, but go ahead and debug the rest of the main program. You could in our example issue r 14, or better, well ... is it r 16? You have to list the lines, step in the stack up, find the next executable line and issue the command r.

Instead of that just issue the command o, to step out of the current function.

#o
 *
-----------------------------------------------------
  014. MyFunction a
  015.
 >016. a = a + 2
  017.
  018. MyFunction a

----------------------------------------------------- #_

As you see it still printed a character and stopped at the line 16.

[Contents]

3.10. Setting Break Points

In case you have a complex program and you want to debug only a certain part of it you need break points.

The command b sets a so called breakpoint at the line for which the line serial number is given. Issue the commands:

#b8
done
#b11
done
#l -

----------------------------------------------------- 001. print "hello\n" 002. a = 3 003. 004. sub MyFunction(t) 005. local a,b,c 006. 007. b = t + 2 * 008. print b 009. print " ",t 010. if t > 1 then MyFunction t-1 * 011. print " *" 012. end sub 013. 014. MyFunction a 015. >016. a = a + 2 017. 018. MyFunction a

----------------------------------------------------- #_

As you can see in the listing the lines are preceded by a * character. When the program is executed using the command r (or even R) the execution will stop when it reaches one of the break points.

Because many times programmers want to set a breakpoint on the actual line, if you issue the command b without argument it will set the breakpoint on the actual line.

#R

----------------------------------------------------- 006. 007. b = t + 2 *>008. print b 009. print " ",t 010. if t > 1 then MyFunction t-1

----------------------------------------------------- #_

To remove the breakpoints you should issue the command B. The command B n removes the breakpoint from the line n. The command b n-m removes all breakpoints that are on the lines from n to line m including the line n and line m. Both n and m are optional. For example B - removes all breakpoints.

[Contents]

3.11. Getting Help

At any time when the debugger waits for your input the command h prints a help screen.

#h
h help
s step one line, or just press return on the line
S step one line, do not step into functions or subs
o step until getting out of the current function
  (if you stepped into but changed your mind)
? var  print the value of a variable
u step one level up in the stack
d step one level down in the stack (for variable printing)
D step down in the stack to current execution depth
G list all global variables
L list all local variables
l [n-m] list the source lines
r [n] run to line n
R [n] run to line n but do not stop in recursive function call
b [n] set breakpoint on the line n or the current line
B [n-m] remove breakpoints from lines
q quit the program
#_

[Contents]

3.12. Debugging CGI programs

To debug a CGI program you have to execute it in the Eszter SB Application Engine. The Eszter SB Application Engine is a single process, multi-thread httpd daemon that runs ScriptBasic programs inside the process as separate threads. Yoi can install the engine as Windows NT Service, UNIX daemon or start just as a command line tool.

More information how to start the Eszter SB Application Engine read the manual of the program.

When the engine is started from the command line it acts like a normal command line program. Although the BASIC command PRINT is redirected to send HTML output to the browser, and the input functions read the http POST data, the debugger, if started still prints to the console screen and reads the keyboard.

To invoke the debugger, however you can not use the command line option `-i dbg' because in this case the command line is for the Eszter SB Application Engine and not for the individual BASIC program thread. Instead you have to modify the code of your BASIC program to have the first line (or the second line following @code{#! /usr/bin/scriba) to be

use dbg

This will invoke the debugger as soon as the file is read y the application engine.

To make sure that the engine is reading the source instead of the cache file, in which case the preprocessors defined by the statement use are ignored configure the ScriptBasic interpreter with a fake cache directory that does not exist.

Start the engine from the command line

sbhttpd -start

and open a browser with the URL that refers to the program you want to debug. Do not forget to specify `-start' under Windows NT! The engine will stop and wait on the console with the debugger cursor.

At this point you can go and debug your program.

[Contents]

4. Known Bugs

[Contents]

5. Future Development

This debugger is the very first debugger that was developed as soon as the internal preprocessor interface was developed. Later versions will feature graphical interface, networked remote debugging and more comfort.