Paradox Community
Search:

 Welcome |  What is Paradox |  Paradox Folk |  Paradox Solutions |
 Interactive Paradox |  Paradox Programming |  Internet/Intranet Development |
 Support Options |  Classified Ads |  Wish List |  Submissions 


Paradox Programming Articles  |  Beyond Help Articles  |  Tips & Tricks Articles  


Beyond Help: Breakpoints and the Debugger
© 2002 Liz Woodhouse

Paradox Help on Breakpoints and the Debugger

breakpoint

A flag you set in source code that is used in debugging to suspend execution.

When code that is executing reaches a breakpoint, execution is suspended. Breakpoints allow you to inspect the values of selected variables and trace the code statements that have already executed.

Debugger

A component of the ObjectPAL Integrated Development Environment (IDE). The Debugger allows you to interactively find and correct errors in code by testing and tracing the execution of commands.

You can use the Debugger to inspect the values of variables at the breakpoints in your code and to trace the execution of code.

In the Paradox 8 ObjectPAL help, you can go to the help on the debug() procedure and click a link to a more detailed description of the "Debugger". (Due to the length it's not included here.)


Beyond Help

Overview
There are several places in ObjectPAL help which make reference to using breakpoints and the debugger, but you'll have to use the "Find" tab to find most of them, and none is inclusive. Some of the more interesting points are that you set a breakpoint by double-clicking to the left of a line in a code editor window in order to toggle (add/remove) a breakpoint; that when debugging a library, you must set a breakpoint in the calling form and in the library; and that when a breakpoint is hit, code execution stops, the debugger is automatically opened and from there you can execute code one line at a time until you find the problem line.

I recommend you go ahead and search for "breakpoint" in ObjectPAL help and read all the related topics and perhaps even those on the debugger. (In Paradox 8 ObjectPAL help, under the system type debug() method, there's a link on the word "Debugger" at the end of the first sentence. The topic this link takes you too is probably the most useful.)

Introduction
Breakpoints are one of the most useful tools available to the Paradox developer in figuring out why a block of code isn't executing as expected. It may also be the easiest tool to use, especially for beginners. The Tracer in Paradox allows you to select from the built-in event methods and then observe in the tracer window as the code executes and see which lines of code executed and the order in which events occur, but it doesn't allow you the flexibility and options available in the debugger. Therefore, for debugging code, using breakpoints and the Debug window, are, in my opinion, far more useful.

Setting Breakpoints
The first step in utilizing breakpoints and the debug window is to set the breakpoint. This is done by double-clicking the left mouse button at the far left side of a line of code in the code editor window. When you do this, a red 'breakpoint' marker highlights the selected line. (It should be noted that not all lines can have a breakpoint set on them. When this is the case, the breakpoint is set on the next allowable line.)

You can set multiple breakpoints in your code (in multiple objects and multiple events). When you do this, you can opt, in the debug window, to let code run at normal speed, and it will stop again when the next breakpoint is encountered.

Breakpoint in Code Editor
What a breakpoint looks like in a code editor window

If you want to step through code in a library, you must set a breakpoint both in the library and in the form that calls the library (this does not have to be the code that makes a call to the library, the breakpoint can come anywhere before the library call, so that you are stepping through code in the debugger when the library is called). You also have to call the LSL version of the library, as the delivered version doesn't allow stepping through code (since the code is compiled by that time).

The tricky part of setting a breakpoint is deciding where to place the breakpoint. It's best to place the breakpoint as close to the problem code as you can so that you don't have to step through hundreds of lines of code just to get to the problem. The best way to determine where to place your breakpoint (from my experience) is to run the form and observe what object and action (pushing a button, leaving a field) seems to be causing the problem, then put the breakpoint as close to that as possible. (If the cause is so obscure that you can't identify an object, put the breakpoint in the form's init, an object's open or an object's arrive event. The debugger has features that allow you to move through blocks of code and even entire methods without having to execute each line manually.)

Using the Debugger
Once your breakpoints are set, you make use of them by running the form and triggering the suspect code. As soon as the breakpoint is hit, the debugger window is opened.

The Debug Window
What the debugger window looks like

When the debug window first opens, the breakpoint it encountered will be highlighted blue. This line of code has not executed yet. It will execute when you tell it to by pushing either the Step Over Step Over Button or Step Into Step Into Button button (or by pressing the corresponding key sequence: F7 and Shift+F7 respectively). When you press one of these buttons, the highlighted line will execute and the next executable line of code will be highlighted.

Stepping Through Code
After the libPC.open line succeeded, highlighting moved to the next available line of code. Note that the endIf was skipped as were the lines inside the if structure (which would only execute if the library failed to open).

This is the basic operation of the debugger - you click the buttons (or use their keyboard equivalents) and execute one line of code at a time, observing what happens after each line executes until you come to the line of code that's causing the error.

Debugger Options
The debugger has all kinds of features to help you step through code and observe what's going on. First we'll review the toolbar buttons and then the menu options.

Debugger Toolbar
The debug window's toolbar.

Button Name What The Button Does
Stop Execution Stops the code from executing, without executing the highlighted line. An Error window opens giving you the option of hitting OK (form stays in run mode if possible), Design (form goes into design mode) or Help (mostly useless).
Run Code will run normally until another breakpoint is encountered. This is useful if you want to end your debug session without abruptly ending code execution (let's you return to design mode more gracefully).
Run to EndMethod Code will run to the next endMethod (you have to click on one of the "step" buttons to execute the endMethod line). This is useful if you've decided you don't need to see the rest of the code in the current method execute one line at a time.
Step Over Executes the current line of code. If this is a call to another method, the other method will execute entirely. You won't get the chance to step through the code in that other method and the debugger will not move to that other method's code. (I haven't done extensive testing to see what sorts of method calls, if any, might not get "stepped over".) Useful for skipping methods you know you don't need to check.
Step Into Executes the current line of code. If this is a call to another method, the debugger shows the other method's code, highlights the first executable line of that code and waits for you to execute that line. When done (when the other method's endMethod is executed), the debugger returns to the calling method and highlights the next executable line of code. Useful for checking called methods when you're not sure where the exact problem is.
Add Watch Brings up a view dialog allowing you to enter the name of the variable you'd like to watch. (More on watches in the next section.)
Toggle Breakpoint Adds or removes a breakpoint on the line where the cursor is (not necessarily where the blue highlight is.
Inspect Variables Brings up a view dialog where you can enter the name of the variable you'd like to inspect. (More on inspecting variables in the next section.)
Debugger Shows/hides the debug window. I cannot figure out why this button is there except to hide the window. In my experiments, if you click the button while the debugger is waiting for you to execute a line of code, the debugger is hidden and code runs as if you'd hit the Run button. If you click it when the debugger is not waiting for you to execute code, the window is just hidden.
Watches Opens the Watches dialog showing values of watched variables. (More on watches in the next section.)
Breakpoints Opens the Breakpoints dialog showing a list of all breakpoints set in the current file.
Tracer Opens the Tracer window. (Please see ObjectPAL help for info on the Tracer.)
Call Stack Opens the call stack dialog which lists all methods/procedures which have been called (including custom methods/procs).
ObjectPAL Quick Lookup Opens the ObjectPAL Quick Lookup dialog.

The Debug window's menu options are as follows:
File
  • Has one option, Close, to close the window.
Edit
  • Find, Find Next and Incremental Search are exactly like they are in any code editor window.
  • The "Bookmark" options appear to set a bookmark in the code and move to said bookmark. I couldn't get the keyboard shortcuts to work.
  • Matching Parenthesis is enabled when your cursor is just before an open or close parenthesis, and will take you ahead or back to the "partner" of the parenthesis just after your cursor.
  • Go to Line opens a view dialog where you enter the line number you'd like to go to, click OK and the cursor moves to the specified line number (if valid). The method(), endMethod and blank lines are counted.
  • Save Debug State saves the various window/dialog positions.
  • Developer Preferences takes you to said dialog (same as from the Tools > Settings menu).
View
  • This menu is mostly self explanatory. Its options are all on the toolbar except for "Source..." which shows a list of methods which have custom code in them; select a method and the debugger switches to showing that method's code.
Program
  • Again, this menu is mostly self explanatory and most of its options are available on the toolbar. The exceptions are:
    • Run to Cursor. You can position the cursor anywhere else in code (even in another method by using View > Source..., picking the other method, positioning the cursor, then returning to the original method via View > Source...) and then choose this option to have code run at normal speed until the cursor is encountered. The line the cursor is on will not execute until you tell it to.
    • Origin. This option returns the cursor to the line of code which is currently highlighted (awaiting execution).
    • Inspect. If your cursor is positioned just before, in, or if you've selected a variable, this option inspects said variable. Otherwise, it opens a dialog allowing you to enter the name of the variable you'd like to inspect.
    • Add Watch behaves the same as the Inspect option, except that it adds a watch on the variable rather than inspecting it. (More on these two options in the next section.)
    • Toggle Breakpoint adds or removes a breakpoint on first line of executable code at or after the current cursor position.
Right Click menu
  • The right click menu is the same as the Program menu.
Inspecting and Watching Variables
One final nifty thing the debugger allows is checking the value of a variable at any point in code execution. There are two ways to check the value of a variable: inspecting and watching. To inspect a variable, right click on it in the debug window and choose Inspect from the popup menu. A view dialog will open showing you the value of the variable. You can change the value of the variable in the view dialog, click the OK button, and the variable in code will now have the value you entered.

The other method of checking the value of a variable, "watching", is more passive. To "watch" a variable, right click on it in the debug window (or in the code editor while in design mode) and select Add Watch. This opens the Watches window where you can watch the value of the variable change as code executes.

The Watches Window
The Watches window.

The Watches window displays the variable name, its value and its type. The window's right-click menu allows you to:
  • Add a new watch (enter the name of the variable to watch)
  • Edit the currently selected watch (change the name of the variable and the watch is changed to the variable with the name you entered)
  • Change the value of the selected variable (only available when the variable has a value assigned)
  • Remove the currently selected watch or all watches
The values of string and memo-type variables may be "cropped" by the Watches window; to view their full value, inspect the variable. A variable which is presently out of scope shows "<N/A>" rather than its value and type. A variable which is unassigned shows "[Unassigned]" rather than its value and type.

Not all variables can be watched or inspected (e.g. a tcursor will only show "N/A" when watched, but it can be inspected (shows the names and values of all fields in the current record)). I'm too lazy to test all variable types, so try it and see for yourself.

Once you get used to using these tools, you'll find it helpful to assign values to variables in places where you might not otherwise (e.g. instead of assigning the value of one tcursor field to another tcursor's field, you might assign the value to a variable and then to the other tcursor so that you can watch the value in the debugger). You'll also find it helpful in understanding how loops work (e.g. when does the counter in a for loop get incremented, when are the conditions of a while loop evaluated, etc.).

One Warning Note
Sometimes using the debugger doesn't work because the delay in execution it causes also "cures" the error. This can happen with timing errors, in which case, you have to resort to logging, tracing or other methodology to track down the offending code.

Conclusion
Using breakpoints to invoke the debugger and then stepping though your code in the debugger is a quick and easy way to isolate the cause of errors or unexpected behavior. It's also a good way to see which events or what custom code causes other events to occur and exactly how various ObjectPAL methods and procedures work. Using the debugger can greatly reduce the time it takes to fix code that's not working as expected.


Discussion of this article


 Feedback |  Paradox Day |  Who Uses Paradox |  I Use Paradox |  Downloads 


 The information provided on this Web site is not in any way sponsored or endorsed by Corel Corporation.
 Paradox is a registered trademark of Corel Corporation.


 Modified: 15 May 2003
 Terms of Use / Legal Disclaimer


 Copyright © 2001- 2003 Paradox Community. All rights reserved. 
 Company and product names are trademarks or registered trademarks of their respective companies. 
 Authors hold the copyrights to their own works. Please contact the author of any article for details.