************
The Debugger
************

Debugging consists mainly of locating the area where a problem occurs.

Doing this sometimes requires additional information about program
execution, and in these cases the custom HeliOS debugger is very useful.

Beware, however, of the common mistake of using a debugger to generate
a lot of extra information which in fact confuses the issue and makes
the task of debugging harder than it need be.

Often the simple use of output statements within a program, along with
careful inspection and analysis of the code, is all that is required.

HeliOS has a powerful debug facility which can be user-customised to do
almost anything.  This debugger is deceptively simple but very powerful:
it effectively allows you to step through your code checking any aspect
of the system as you go and carrying out any procedure required.

You enter DEBUG mode when you press the "DBug" button in the Interpreter,
and you are then in DEBUG mode until you press the same button again.

In DEBUG mode you lose the "Stack Display" but gain a few new buttons.

The DEBUG functions will be described below in more detail, but first here
is a quick overview of debugger operation.

In DEBUG mode the Interpreter goes through a DEBUG "gate" each time it
executes COLON/SEMICOLON threading operations.  At this point you can have
the debugger run your own user-definable HeliOS mini-program(s) to analyse
what your code is doing and print the results for you to see.

You can change the debug code at any point in the program, and even have
the debug code change its own subsequent execution function depending on
program action.

You can, for example, pause the program, display variables, dump memory,
single step, wait on variable states.........whatever you choose.

The debugger has a STEP function which allows you to specify how many times
to skip between actual debug operations.  By default you will debug every
word, but you can change this interactively while your program is running.

You can send debug output to the screen or to the "Out" buffer, and while
you are debugging you can send the normal output of the program either to
the screen or the "Out" buffer.  These two options are in the form of a
double toggle - you cannot have debugger AND program output to either one
single output stream, for obvious reasons of general confusion.......

The debugger has a fully interactive mode of operation, which means that
you can press any of the debugger control buttons while the program is
executing.  Thus you can press "Dump" to dump a predefined area of memory
at any time while the program executes.  This will halt program execution
as soon as you hit the "Dump" button, and resume when the dump is finished.

You can halt a program by using the debug control panel, or by setting
"breakpoints".  The breakpoint mechanism is very flexible, in that you
can control the debug functions either from the control panel buttons or
from programmable functions within the code.

At any point you can temporarily pause the debug session by using the
<Space> bar, and you can abort at any time by pressing <Esc>.

This is very much a user-definable debugger, which is important since
debugging requires a very flexible approach for particular contingencies.

The debug functions include the ability to list to screen a full nested
display of the names of the HeliOS words in any current "thread".

By default the debugger runs a very simple standard function which lists
all the levels of the current execution thread and displays stack contents.

**************************
Listing the current thread
**************************

The debug control word SHOWDEBUGWORD can display the current execution word
or the complete current execution thread.

To understand this, you need to see how a threaded interpretive language
such as HeliOS strings together nested command words.

Let us assume that you create a word called PRINTHELLO, like this:

: PRINTHELLO ." Hello " ;

Let us assume then that you create a word called SHOWHELLO, like this:

: SHOWHELLO 20 20 CURPUT PRINTHELLO ;

Let us assume then that you create a word called SHOWREDHELLO, like this:

: SHOWREDHELLO 6 FPENSET SHOWHELLO ;

Let us assume then that you create a word called HELLOFRIEND, like this:

: HELLOFRIEND SHOWREDHELLO ." Friend!" ;

Now let us look what happens when you execute HELLOFRIEND:

First the interpreter gets the word HELLOFRIEND and starts to interpret it.

The interpreter finds a new word SHOWREDHELLO, so it remembers where it was
in its execution of the word HELLOFRIEND and tries to interpret the new
word SHOWREDHELLO.

The interpreter is now at a second nested level, and the current thread
consists of the two words HELLOFRIEND and SHOWREDHELLO.

Now the interpreter goes into the word SHOWREDHELLO and starts to interpret
it. Here it will encounter a few execution commands for 6 FPENSET followed
by your word SHOWHELLO.

The interpreter has now found yet another a new word SHOWHELLO, so it once
again remembers where it was in all the previous threaded words and starts
to interpret SHOWHELLO.

In SHOWHELLO, after the cursor positioning commands the interpreter finds
yet another of your words, called PRINTHELLO.

Once again the interpreter remembers where it was and tries to interpret
the latest word PRINTHELLO.

The word PRINTHELLO has a simple execution result and requires with no
further threading, so now the interpreter goes back one level and continues
where it left off on the previous threaded command SHOWHELLO.

The interpreter will now work its way back out through the thread until
all words have completed.

When the interpreter was executing PRINTHELLO, the full thread would look
like this, with a depth of four words:

HELLOFRIEND  SHOWREDHELLO  SHOWHELLO  PRINTHELLO

The debugger would by default use the debug control word SHOWDEBUGWORD to
display the full thread of the current execution word at each threading
point.

Thus if you were to debug the word HELLOFRIEND you would see a series of
statements displaying the threading, step by step, which at one point
would contain "HELLOFRIEND  SHOWREDHELLO  SHOWHELLO  PRINTHELLO" .

Try this in the interpreter, making sure that output is enabled to both the
screen AND the buffer.  Allow the execution of the word HELLOFRIEND to
complete, then look at the results in the "Out" editor.

You will notice that the word LIT appears every time HeliOS handles a pure
numeric value on the stack.

You will also notice some new words used to actually print to the screen.

One of these words is called NONAME, which means that it is an internal
HeliOS word with no vocabulary entry.  There are many of these words with
no user name, so in many cases your debug thread displays will contain
multiple instances of words with NONAME.  Do not worry about this.

In particular, you fill find NONAME cropping up often when executing core
HeliOS words rather than your own constructed threads.

Careful examination of the full thread display in the "Out" editor will
allow you to determine exactly which word is executing at any time and
what the stack contains at this point.


******************
Using the debugger
******************

By default the debugger has a very simple debug execution function.

If you enter DEBUG mode without setting your own debug function, the
sytem will execute the default debug function at every threading point.

The default debug function will, at each threading point:

1. Print out stack contents in red

2. Print out in black a list of the current thread

Pressing the <Space> bar will pause the debug operation.

Pressing <Space> a second time will restart debugging.

Pressing <Esc> will abort debugging at any time.

Use the thread display to determine exactly which part of your code is
currently executing.

The simple stack display used by default may be enough in many cases, but
remember that you can create your own debug functions to check memory
locations, print variables, etc.  

You can write debug words to do whatever you like, and you can use the 
debug control words listed below to control the action of the debugger 
from within your own debug software, allowing great flexibility.

The rest is up to you, and how you wish to approach any particular debug 
task.

****************************
Why NOT to use the debugger!
****************************

Strictly speaking, using a debugger is a matter of personal preference,
but in most cases you can debug code more quickly and efficiently by
the intelligent use of print statements from within your code combined with
the use of WAITSPACE commands to allow you to step through your code as it
is executing.

This can be far quicker and simpler than using a debugger, and we recommend
that you avoid getting too involved with using the debugger unless you 
really need its powerful capabilities to crack a very difficult bug.

You will make more progress in understanding your code if you undertake
to find most bugs by careful inspection of your code along with the display
of vital parameters as your program executes.  In this way you become more
expert in how your own code functions rather than in the intricacies of
setting up complex debugging procedures.

Of course, many programmers just love setting up fantastic sophisticated
debug capabilities just for the sake of the feeling of power they get
by squeezing alarming amounts of esoteric information from recalcitrant
and malicious computers.  This can be great fun of course, and constructing
a subtle and cunning debug system to entrap a really nasty bug can be very
satisfying in its own right.

****************************************************
A few simple hints on debugging without the debugger
****************************************************

HeliOS may indeed have a very powerful user-configurable debugger, but for
most debugging you should never need to use it!  The following hints should
be taken as general programming guidelines, and intelligent programming is
preferable to reliance on the debugger.

There are a few simple techniques which, combined with sensible programming
strategies (for example, do not write 200k of source code altogether with
no testing then expect it to work instantly...), will enable you to debug
code very simply.

When a bug occurs the program will often (probably!) crash.

The first stage of debugging is to find exactly at what point in the code
the program goes wrong.

One good simple debugging technique is to put a simple repetitive "pause
and print" debug line into the code which allows you to step through the 
program slowly until the point where things go wrong.

This "pause and print" line should display the stack status and wait for 
the space bar to be pressed.  

Something like the following will do nicely:

 ." Now doing xxxxx. Stack reads: " .S ." <SPACE> to continue" WAITSPACE CR


A few of these lines included in your code will give you a sequential
talk-through until the crash point is reached.

Having written the program yourself you should be able to work out quite
well just where in the execution of your code the fault is happening.  

Once you know where in the code the problem lies, intelligent examination 
of your code in combination with all the available "clues" will generally
reveal the trouble quite easily.

Start with the high level code first and then work into your subroutines:
in this way you can home in on the actual fault.  It is good practice to
check each subroutine thoroughly as it is completed, but nevertheless the
most unexpected results can occur when even apparently sound subroutines
are used together in complex ways.

The stack status message debug line suggested above will probably mess up
your program's screen display, but that does not really matter in a debug
operation.

Always check for stack aberrations first. Be aware of what the stack should
be reading and check your debug stack readouts scrupulously.

If you like, include DUMP commands in your debug instructions to dump areas
of memory for careful scrutiny.  Displaying variables is also useful, as is
the creation of special debug "flag" variables which you can use to warn
you of various internal states of your program.

There are so many ways for code to fail it is impossible to cover all the
aspects of debugging.  Debugging is a very intuitive thing and everyone
has very personal preferences.

The main thing is to never assume ANYTHING to be OK without double checking,
and be prepared to be patient and check EVERYTHING relating to your problem
with an open mind.

Check your stack dumps minutely, dump and also check any areas of memory
you think may be suspect, check variable values by including them in your
debug lines where appropriate.

One big source of trouble is using address pointers which should really be
pointing at something useful like a screen structure address, but actually
contain zero.  A golden rule on a multitasking machine like the Amiga is
never to assume that an allocation or opening routine must always succeed.
Always check to see if everything has opened correctly, and always take
care to initialise all fields of data structures correctly.


*******
Warning
*******

Sometimes using the debugger can have unpredictable results with certain 
types of code, so use it carefully and ensure that your work is saved 
before starting to debug complex code.

You may often find that you want to debug software where the screen output
of the debugger will cause conflicts with the program execution.  In these
cases you may wish to supply your own debug function or switch off output
to the screen.

It is a good idea to always switch off the debugger except when you are
actually using it.


*****************
Debugger controls
*****************

*****************
The "DBug" button
*****************

This changes the stack display bar into a set of debugger control buttons
and simultaneously enters debug mode.  You will then be in debug mode until
you press the "DBug" button again, which reverts to normal interpretation.


Debug control buttons:

*************
Screen Output
*************
               - These buttons toggle debugger output streams
*************
Buffer Output
*************

The idea here is that when in debug mode, your program's normal screen
output will be stopped, and the screen will be used by the debugger to
print debug messages.  Similarly the "Out" buffer will get debug output.

If you switch off either of the Screen/Buffer output buttons, the normal
program output will be sent to that output stream rather than the debug
output.  Thus you can choose which output you want to see, and can view
one output while sending the other to the "Out" buffer.

These buttons can be pressed as your program is executing so that you can
alternate between output modes at will during a debugging session.  This
gives great flexibility to the system.

What is perhaps even more useful is that the output streams can be changed
under program control by your debug routines within the executing program.

This system gives you a lot of control and choice over the posting of
debugger information.

**********************************************
The "Step" button and associated string gadget
**********************************************

The step string gadget lets you set how many times the debugger will "skip"
between calls to the debug routines.  A setting of "1" will cause the debug
code to be run for every threaded word, a setting of "4" will sample every
fourth threading etc.

Again this can be altered at will as the program executes, and can also be
changed by the debug software.  Thus you could insert debug routines which
step through certain sections of code in large jumps until a "vital" part
of the program is reached, whereupon the debugger could be made under the
control of software to "automatically" change to single stepping.

The "Step" button, if switched "On", will cause the software to pause at
each debug entry point rather than free running.  Thus you can skip parts
of a program and return to debug "paused" mode by entering a certain value
into the string gadget, setting "Step" to "On", then pressing "Run".

Again this can be done interactively while the program is executing or
from within the software under debug program control.

****************
The "Run" button
****************

This starts the program executing after you have paused within debug mode.

This function is also available from program control.

******************
The "Pause" button
******************

This stops the program executing and pauses within debug mode.

Pressing the <Space> bar will also pause the debug operation.

Pressing <Space> a second time will restart debugging.

Pressing <Esc> will abort debugging at any time.

This function is also available from program control.


******************
The "Abort" button
******************

This aborts the debug session and quits the program.

Pressing <Esc> will abort debugging at any time.

Pressing the <Space> bar will pause the debug operation.

Pressing <Space> a second time will restart debugging.

This function is also available from program control.


**********************************************
The "Dump" button and associated string gadget
**********************************************

This button lets you set up an address for a memory dump which can be
carried out by pressing the "Dump" button at any time while the program
is executing.

This function is also available from program control.


**********************
Debugger control words
**********************

There are several HeliOS words which control the action of the debugger
from within your program.  These words can be used to set up breakpoints
and conditional debugging etc.


********
SETDEBUG   ( cfa [or 0] - - - )
********

Used in the form:

FIND MYDEBUGWORD SETDEBUG  -> Makes MYDEBUGWORD the subsequent debug action

0 SETDEBUG                 -> Restores the system default debug action


*************
GETDEBUGLEVEL  ( - - - n1 )
*************

Returns n1, the current depth, or level, of the threaded interpreter.
You can use this word to determine how many nested levels deep your
current execution word lies.

*************
SHOWDEBUGWORD  ( n1 - - - )
*************

This word will print to screen the name of the HeliOS word which lies
at level n1 in the current execution thread.

If you use a value of "n1 = -1", a full list of the full execution thread
will be printed.  This is the default used by the system debug word.

*************
SETDEBUGPAUSE  ( - - - )
*************

Acts like the debug "Pause" button to enter debugger paused state.

*************
SETDEBUGABORT  ( - - - )
*************

Acts like the debug "Abort" button to abort the debug session.

**************
SETDEBUGSCREEN ( n1 - - - ) where n1 = switch (1 for "On", 0 for "Off")
**************

Acts like the debug "Screen" button to toggle screen output mode.

**************
SETDEBUGBUFFER ( n1 - - - ) where n1 = switch (1 for "On", 0 for "Off")
**************

Acts like the debug "Buffer" button to toggle buffer output mode.


*********
SETDEBUG#  ( n1 - - - )
*********

Inserts a number n1 into the debug step number string gadget and updates
the step number.


************
SETDEBUGSTEP ( n1 - - - )  where n1 = switch (1 for "On", 0 for "Off")
************

Sets the current status of the debug "Step" button.
