Debugging
PowerBASIC provides a complete set of tools with which to identify execution/logic errors within the source code. Programs can be walked through line by line and variables watched for value changes as the lines are executed. These tools greatly simplify the process of resolving problems within the program.

In general, there are two sets of capabilities: one which is used as part of a normal compile and execute session, and one which requires using the integrated debugger that is built into the PowerBASIC editor.

As a debugging strategy, using tools during compile and execute is preferred. Once put in place, the debugging statements can be used over and over to help locate program issues. When these fail to provide the information needed, the use of the integrated debugger is appropriate.

See the error trapping tutorial for additional features PowerBASIC offers that can be helpful in debugging an application.

Debugging: During Normal Compile and Execute
There are 3 compiler directives which will provide the programmer with both on-screen displays and log file records of program error/execution details.

The first two, #Debug Display and #Debug Error are perhaps the easiest of the debugging tools to use. Simply place the two compiler directives one time at the top of the program. and the debugging features will be inserted into the compiled program, ready to provide information to the programmer. The third, #Tools, is a switch that turns on 4 other debugging features. The four features take a little more time and effort to set up, but also return the greatest amount of information.

All three compiler directives affect performance of the final application, so their use is normally limited to development only.

The default for all 3 compiler directives is off. Other compiler directives useful for other aspects of debugging that are discussed further down this page.

There are four debugging tools enabled by the #Tools On compiler directive. These are very powerful tools and provide the most details of any of the PowerBASIC debugging capabilities.

Here's additional information on each of these. A sample application is provided further down the page, showing how the examples in the next few paragraphs were generated.

1. Profile
Profile prepares a list of all procedures executed, the number of times the procedures are called, and the total time spent within the procedure. To enable profiling, insert these following statement at the last line of the PBMain() procedure.

    PROFILE "profile.txt"    'use your own file name

When the application is terminated, a file is created which has the following information (the headers are not in the file, they're shown here for information purposes only).

    Procedure       Calls    Time (ms)
    ===========     =====    ========
    MySub_2,         1,         0
    MySub_1,         1,         0
    ButtonProc,      1,         0
    PBMain,          1,         0

It's important to note that the time listed for a procedure includes the time spent in any procedure it calls!

The value of profiling is not only in determining which subroutines take too much time, but also to see if subroutines are inadvertently being called too often - a common problem in event-driven programs.

2. Trace
There are five different statements which make up the PowerBASIC Trace capabilities. Collectively, the statements enable documenting the flow of an application. In particular, there are five types of information captured.

    1. passing through a label
    2. entry and exit from procedures
    3. procedure argument values (on entry only)
    4. run-time errors
    5. custom messages

Here's an example of the output of a series of trace statements. In this example, a button press runs MySub_1 which then calls MySub_2. Each sub has a single Long argument. Both procedures includes a Trace PRINT statement containing "passing through ...", The full code for this example is provided further down this page.

    Trace Begins...
     ButtonProc()
      MySub_1(1508532)
      passing through MySub_1
       MySub_2(1508532)
       passing through MySub_2
       MySub_2 Exit
      MySub_1 Exit
     ButtonProc Exit

In this example, you can see the entry into the button callback function, followed by the program flow through MySub_1 and MySub_2. Both entry and exit from the procedures are logged.

And finally, here are the five Trace statements that may be used in a PowerBASIC application.

    Trace NEW "filename.txt"       - create output file
    Trace ON                       - start logging
    Trace PRINT "custom message"   - log a custom message
    Trace OFF                      - stop logging
    Trace CLOSE                    - close output file

Trace NEW creates the output file, deleting any pre-existing file by that name. Trace ON and Trace OFF pairs can be used as often as necessary - one pair to capture the entire program, or multiple pairs to selective trace only portions of a program. A Trace PRINT, used to print a custom message by the programmer, can only be used between Trace ON and Trace OFF statements. To close the output file, use the Trace CLOSE

The more obvious use of Trace is to see if an application performs as expected, or if it branches off in unexpected directions. Trace will also make it obvious when a procedure is executed more often than expected - a common problem in event-driven programs.

3. CallStk
PowerBASIC keeps track of all nested procedure calls, maintaining a list called the "stack frame". With the CallStk statement, a snapshot of the current stack frame can be written to a file. Here's the code.

    CallStk "stackfile.txt"    'use any file name

And here's an example of the information found in the file.

    PBMain()
    ButtonProc()
    MySub_1(1508532)
    MySub_2(1508532)

When #TOOLS OFF is used, all CallStk statements are ignored.

4. CallStk$
CallStk$ allows a program to capture an item from the call stack, placing it in a variable for use by the program. Here's the code:

    iResult$ = CallStk$(n)   'n is the stack level to return
                             'n=1 is current level
                             'n=2 is level that called this procedure
                             'n=... and so on

    MySub_1(395454)          'example of string returned by CallStk$

When #TOOLS OFF is used, all CallStk$ statements return "".

Example Application
The following example was used to create the stack, trace, and profile examples above.

    #Compile Exe
    #Dim All
    #Debug Error On     'catch array/pointer errors
    #Debug Display On   'display untrapped errors
    #Tools On           'enables Trace/Profile/CallStk/CallStk$
    Global a() As Single
    Function PBMain() As Long
        Trace New "tracetest.txt"
        Trace On
        Local hDlg As Dword
        Dialog New Pixels, 0, "Button Test",300,300,200,200, _
                                   %WS_OverlappedWindow To hDlg
        Control Add Button, hDlg, 100,"Push", 50,10,100,20 _
                                   Call ButtonProc
        Dialog Show Modal hDlg
        Profile "profiletest.txt"
        Trace Off
    End Function

    CallBack Function ButtonProc() As Long
        MySub_1 Cb.Hndl
    End Function

    Sub MySub_1(h As Long)
        Trace Print "passing through MySub_1"
        MySub_2(h)
    End Sub

    Sub MySub_2(h As Long)
        CallStk "stacktest.txt"
        Trace Print "passing through MySub_2"
    End Sub 

Debugging: Using Integrated Debugger (Debug Mode)
The entire (lengthy) discussion on this page so far covered only those debugging tools which can be used during a normal compile and run session.

This next section covers the use of the integrated debugger, which is a part of the PowerBASIC editor. In a debugging session, code is added by the compiler which allows the integrated debugger to control, line by line, execution of the application. Additionally, at each line a programmer can view or change the current value of any in-scope variable(s).

Debug mode can be entered in three ways - two menu options and one toolbar button.

In all three cases, the program will be compiled and execution will begin. When a debug session is started, it stops on the first line within the PBMain() function. This gives the programmer an opportunity to set breakpoints and watch variables.

Breakpoints can also be set at any time during a PowerBASIC editing session.

Once debug mode is started, the PowerBASIC menu and toolbar are changed to reflect additional debug mode features, as discussed in the next two sections.

Breakpoints
In debug mode, execution of a program will at the first line of PBMain() and also at any line of code which has been designated as a breakpoint.

To set a line of code as a breakpoint, use the F9 hot key or the Debug/Toggle Breakpoint menu sequence. Breakpoint lines are highlighted in red.

The value of breakpoints is that they let the programmer evaluate the status of the program in several ways, including changing variable values as needed, before continuing execution of the program.

The following actions can be taken once a breakpoint is reached and program execution has been stopped.

Debug Mode Toolbar
Once PowerBASIC enters debug mode the toolbar changes as shown in the following image, offering features available only in debug mode.

 

The additional toolbar entries are a subset of the complete debug menu options as shown in the next paragraph.

Debug Menu
When in debug mode, the debug menu submenus become active, providing the following options. The options can be broken into the following categories.

And here is the full list of options.

Variable Watcher Window
In debug mode PowerBASIC conveniently provides the ability to specify a list of variables whose values can be watched in a separate window - the Variable Watcher window.

This window is only visible in debug mode when execution is stopped at a breakpoint. It is not visible when the program is running freely in debug mode.

To specify which variables will be in the watch window, enter debug mode and select a variable by double clicking on it. Once selected, use the context menu "Watch" to add a variable to the watch list.

If a variable has already been added to the watch list, the context menu will say "UnWatch" and allow removal of a variable from the watch list.

Variables can also be add/removed from the Variable Watcher list from the Variable Evaluator windows, discussed in the next section.

A sample Variable Watch Window is shown below. It contains the list of variables and their current values.

Not that only variables in-scope will have values listed. When a variables goes out of scope the values will be listed as "<<Cannot Be Displayed>>".

To modify one of the variables on the watch list, simply double click on the variable to open the Variable Evaluator window, which is used to view/change a variable's value.

Variable Evaluator Window
In addition to watching the values of a list of variables, PowerBASIC provides the Variable Evaluator window that allows programmers to select a single variable, then read or modify its value before continuing with execution of the program.

The Variable Evaluator window can be opened using the "Debug/Evaluate Variable" menu sequence, the "Evaluate variable" toolbar button, or by highlighting a variable in the IDE, then using the "Evaluate" context menu selection.

If a variable is a string type, it's length may not be changed in the Variable Evaluator window. If a new value of incorrect length is entered, the Variable Evaluator window will adjust the length as needed.

The watch status of a variable can also be turned on/off in this window.

Register Variables
As a method of improving application performance, PowerBASIC offers a feature called Register Variables. This feature is discussed in more detail elsewhere in the tutorial. The register values can also be viewed while in debug mode by using the Debug/Watch CPU Registers menu sequence.

Register variables can also be on the watch list, making them visible from both windows.

Debug Compiler Directives
One set of compiler directives were discussed at the top of this page, those directives which work during a normal compile and execute session.

There are also two other compiler directives which work only during Debug mode.

Here's an image of a simple application in the PowerBASIC IDE, showing two lines written in the Debug window using the #Debug Print compiler directive.

Code Finder F2
Finally, while not exclusively a debug mode option, PowerBASIC provides a quick way to see all procedures (Sub/Function/Method/Property) in a project. Code Finder, as it is called, can be accessed using the Find (binocular) icon on the toolbar, through the Edit/Code Finder menu sequence or using the F2 hot key.

To display a procedure, just double click on the procedure name within the Code Finder window. This ability to quickly locate and move through code can be very valuable in locating and modifying source code issues.

If you have any suggestions or corrections, please let me know.