|
|
Section 7: Breakpoints, Call Stack, Watch Window, CPU Window
Being able to pause a program by pressing the pause button is not extra specially useful. Whilst it does give you the opportunity to
stop your application during a long processing loop, more often than not, you will find yourself wanting to stop the program in the
confines of a procedure or function that takes less than a millisecond to run. In order to do this, Delphi provides "Breakpoints". These
can be set in a number of ways - the simplest being to click on the left hand margin of the code, level with the line you want the
debugger to stop at when it arrives there.
Doing this will leave a red circle in the margin of the code. If the circle isn't red, then Delphi is trying to tell you that the
line you have placed the breakpoint on will either never be reached, or does not
Figure 13 A Breakpoint
represent a "runnable" line of code (i.e. you have placed the breakpoint on a line that contains no code, or a comment).
Figure 14 A Breakpoint set on an Unreachable Line
Having placed a basic breakpoint in this way, the program will execute as normal if you run it in from within Delphi, until it reaches
the line with the breakpoint set, and then the program will automatically hit the pause button, and wait for you to dictate what happens
next.
NOTE
- Breakpoints are not stored and do not constitute a part of the program, they constitute a setting of the IDE for the specific project
into which they are placed, and hence have no effect whatsoever on a program if it is running outside of the IDE.
Delphi does not automatically switch across to the code window when it hits a breakpoint, which can be confusing (the application
suddenly becomes unresponsive for no apparent reason), but it does highlight its own start bar panel in a different colour to indicate
that it needs attention.
One final warning about Breakpoints... I have yet to uncover the exact circumstances under which it can occur, but on one particular
shared project in which I have been in
volved, the development team has on occasion found that the code consistently stops at a "phantom" breakpoint. (I believe that this is
because somehow, someone copied an .OPT or a .CFG file from one machine to another, and confused the IDE in the process). These "phantom"
breakpoints can be removed by calling up the breakpoint list, and removing all items from the list.
The breakpoint list (which can be accessed by selecting View, Debug windows, Breakpoints, or Ctrl-Alt-B) also allows the programmer
to set conditions on breakpoints. For example - Stop on this line when the loop control variable reaches 16 - A real time saving feature,
if you have a problem that only exposes itself on the 20,000th iteration of a loop. Using the breakpoint list dialog, it is also possible
Figure 15 The Breakpoint List
for the developer to set a breakpoint that is not tied to any particular line of code, but to an event like a the value of a variable
being changed. This can be extremely valuable if you have a value which should be invariant, but is actually changing unexpectedly. We
leave it as an exercise in experimentation to find out how this is done.
Once the program has paused, you will probably want to set about tracing line by line through the code as described in the previous
section. Following the execution of the code, however, is only part of the story, what you really want to know as a developer is what the
variables and properties of your objects and components contain. Delphi provides various methods of gaining this information.
The Watch Window
The most useful of these devices is the watch window. This can be called upon by selecting
View, Debug Windows, Watch Window (or alternatively using the shortcut keystroke Ctrl-Alt-W). The watch window allows the developer
to type nearly any expression (including function calls on variables), and the IDE will evaluate each entry every time the program comes
to a stop (i.e. every time you press the F7 key to step through the program). This means that you can watch not only the progress of the
code, but also its effect on the variables and properties that comprise your application.
To insert an item into the watch window, call it up as described, and press the insert key (or right click and select "Add Watch", or
highlight an empty slot, and press enter). This will present a dialog box, with a space to type in the thing you want to "Watch". There
are various options that allow you to change the way in which the data is displayed, however, Delphi is very good at presenting the
information that you want in a readable format, and it is rarely necessary to deviate from the default. As an alternative, Delphi
provides the facility to add a watch by selecting the item you wish to watch in the text editor, and right clicking on it. The menu that
results contains the option Debug, with a submenu that includes the option "Add Watch at Cursor". (This has a shortcut key of F5)
Delphi provides another useful feature, which in some cases can save you the effort of calling up the watch window and adding an item to
it. By placing the mouse cursor over a variable in the code while the program is stopped, the IDE will after a short pause display a
hint, containing the value of the "thing" over which the mouse cursor is resting.
Local Watches
Delphi also provides a second form of Watch window, which collects and automatically displays all of the variables that are local to
the procedure or function that is currently being executed. This can be displayed by selecting from the menus View, Debug Windows, Local
Variables (or alternatively Ctrl-Alt-L).
Because it requires a lot of work to keep each individual watch updated, having a lot of them on display at one time can slow down
stepwise execution of the code quite noticeably. It is therefore a good idea to try and limit the number of watches that you have at one
time.
There is no particular limit to what you can look at with the watch window. As stated earlier, it is able to evaluate expressions, as
well as to display the current contents of a simple variable. It can also display arrays of data, and the contents of complex record
structures. The only thing it has problems with is Objects, which it can only show individual fields of upon request.
One final limitation which you will notice whilst using the watch window, is that it will often display a message about optimisation,
rather than the value you were hoping to see. This is because Delphi optimises variables out of the code until they are absolutely
needed, and then removes them again as soon as they have served their purpose. It is possible to turn off compiler optimisation, which
alleviates this problem.
The CPU window
For those developers who have experience in programming in Assembler, it is possible to get Delphi to display a view of the CPU, and
to trace through the source code at the assembly language level, rather than at the source code level (multiple assembly language
instructions will in general result from any individual Pascal source code instruction). The CPU window can be called upon by selecting
View, Debug Windows, CPU (Ctrl-Alt-C).
Figure 16 The CPU Window
This view shows the contents of the CPU registers, the assembly language instruction that is about to be executed, and a "load of other
stuff I don't pretend to understand". It's fascinating to look at, but unless you have experience of Assembly language, an inner
knowledge of the Intel CPU architecture, and endless patience, and an intractable coding bug to sort out, you are unlikely to find it
especially useful.
The Call Stack Window
Rather more useful than the CPU window, is the Call Stack Window. This facility allows you to answer the often asked question "How
the **** did
it
get in here!?"... You know that sinking feeling when you realise a procedure that you never expected to be called "here", is suddenly
found to be running. By bringing up the call stack, you can find out the path of nested procedure ca
lls that brought the execution point to its current location.
Figure 17 The Call Stack Window
Note that as with the other debug windows, it is only updated when the code is "stopped", so you will probably have to place a breakpoint
to make use of the facility properly. Delphi goes one better than simply displaying the call stack, it is in fact an active list, and
clicking on any one item in the list, will jump you in the code window to the calling point in the "previous" procedure. The call stack
will also give you an indication of the parameters that were passed at the time of the call. This facility is particularly useful in the
context of an event driven program, where quite often you can find yourself asking the question with the expletive in it above.
Thread Window
When you begin writing more advanced applications, you may find it advantageous to make use of one of the new features of the 32 bit
Windows Operating System - namely support for threads. If you haven't used threads, or come across them before, then you have never used
one of the latest versions of Word. Word employs threads extensively, but most noticeably to spell check as you type. A thread is an
extra "independent front of execution" which exists in your code, and can be used to perform some useful background function, while your
application continues to respond and react to user interaction. Word uses threads, not only for the spell checking feature, but also for
the grammar checker, for the auto-save function, for the display of images embedded in the text, and doubtless a host of less obvious
purposes.
Figure 18 The Thread Status Window
Threads are useful because the amount of processor time they use (their priority) can be set by the developer, and can even be changed
whilst they are executing. On the downside, they can, if programmed incorrectly, lead to some "surprising" effects and some hard to track
bugs. The Thread Window (View, Debug Windows, Threads or Ctrl-Alt-T) displays the threads that are running at any one time, with some
additional information about the state of the thread.
That Completes our brief tour of the debugging facilities available to the Delphi programmer. There is more to it than described here,
but the most important features have all been covered, and for your first foray in to Delphi programming, you should find that you now
have as much information as you need.
In the next section we will look at some of the labour saving devices built in to the IDE, and we will take a brief tour around some of
the more important settings that the IDE provides for the programmer.
|
|
|
|
|