Dbg – an easy to use debugger

David Bailey
Salford Software
db@salford-software.com

The built-in debug functions such as Trace and related functions suffer from several practical drawbacks:
• If used without restrictions they generate enormous quantities of output. Large parts of the code can get output repeatedly in the form of CompoundExpression constructs etc., and the output shows the evaluation of many objects (such as Plus, 42, π, etc.) which evaluate to themselves.
• Trace generates its output in the form of nested lists. This can be very hard to interpret, particularly if the program itself manipulates lists.

• Trace generates no output if the program fails to terminate for one reason or another.
• TracePrint at least works for non-terminating programs, but the output is usually still enormous.
• Facilities for interactive debugging are very sparse.

• TraceScan is really a tool for building a high level debugger. Dbg is based on TraceScan.
The essential problem with TracePrint and related functions is that when a user restricts his output he is likely to remove too much information. Also, the standard functions give very little help in interpreting the output.
Dbg is designed particularly (but certainly not exclusively) for less experienced Mathematica programmers. It uses the facilities of the frontend to help the user navigate its output. The philosophy behind Dbg is that a debugger must be easy to use so as not to distract the user from the details of the code he or she is attempting to correct.  
Note: The use of Dbg inevitably slows the execution of the program under test considerably. Even when Dbg is not reporting information it has to execute a number of Mathematica steps for every step of the program under test. Users should be aware of this when deciding how much time to give a looping program etc.

This section consists of a brief tour of some of the simplest facilities of Dbg. The subsequent sections describe these facilities in detail.

• Consider the following rather naive program, which forms a dot product of two lists. Obviously this task would be better performed by Dot (and a loop is unnecessary here), but we simply want something elementary to debug:

dot˘product[x_List,y_List]:=Module[{k=Length[x],ss=0},
Print["Starting"];
While[k>0,
ss=ss+x[[k]]y[[k]];
k--;
];
ss
]

To explore this code using Dbg we first load the package thus:

<<"debugger.m"

Loading the package will also display a small palette known as the debugger palette. This contains two sets of buttons, the bottom set of buttons are reserved for use when the program is suspended at a breakpoint.

Now we can debug the program thus:

dot˘product[{2,3},{4,5}]//Dbg[]

• Notice that all the debugger output appears on a pink background, which distinguishes it from any actual Print calls that you may have in your code. Every line is labeled with a step number. As you will see shortly, step numbers can be very useful to control the debugger on subsequent runs. By default the debugger only prints ten steps before it simply lets your program run on to completion. As you will see, you can decide how many debug steps to print and at which step number to start. The idea is that you do not become overwhelmed with output from Dbg before you know what you are doing.
• As the program runs under Dbg the step count is maintained in the status bar of the debugger palette. 
• Before the step numbers you will see two buttons. Try pressing the left button on the fourth step above (i.e. the button containing a '?' symbol).  Look carefully at the cell brackets. Two cells have been selected corresponding to steps 3 and 4 of the debug process. This shows  you that the expression Length[{2,3}] evaluated to 2. In general pressing the '?' button will select several steps which will not necessarily be adjacent. These cells represent the various steps in the evaluation of a single sub-object.  This is known as an evaluation chain. Be aware that if cell brackets are suppressed in your notebook, pressing either of these buttons will enable them.
• Now return to step 4 and press the '↑' button. This will select those steps which form the evaluation chain which spawned the calculation of Length[{2,3}].  In this case steps 1 and 2 are selected. Looking up from step 4 we see that step 2 is the nearest highlighted step, so it is the step that called for the Length calculation. As you can see, step 2 is a Module construct and the Length calculation is indeed used to initialise one of the variables.
• Observe the syntax of Dbg carefully. Dbg is a function which returns a function. It is that function which is applied to the expression to be debugged using '//'. Why is Dbg defined in such a seemingly clumsy way? The reason is that it enables the debugger information to be appended to the end of an expression, rather than surrounding it. To see this, consider the built-in TracePrint function. This can be used to obtain a rather less helpful debug printout:

Sin[0.5]//TracePrint

However, as soon as you start using additional arguments to TracePrint you are more or less forced to use a less neat syntax:

TracePrint[Sin[0.5],_Sin]

It is now much less clear what is the expression being debugged and what is extra information supplied to TracePrint.

• As an illustration we will apply two arguments (which will be described fully later) to cause Dbg to print out steps 11-15 (rather than 1-10, which is the default):

dot˘product[{2,3},{4,5}]//Dbg[MaxSteps->5,StartStep->11]

• Now we will debug the program interactively, stepping the process to places of interest. Using the option Step->True means that the program is stopped at each step and is advanved by pressing the "Step" button in the debugger palette. Whenever the program is stopped it is possible to examine program variables (or other expressions) by evaluating expressions in the usual Mathematica way before advancing the program to another point. In this example we print the current value of the local variable k by noting its new name (assigned by Module) and using that explicitly.

dot˘product[{2,3},{4,5}]//Dbg[Step->True]

k$244

• Dbg is also very effective at exploring code written in a more functional style. For example, the fact that Mod can take a list as its first argument (i.e. Mod has attribute Listable) can be made explicitly obvious thus:

Mod[{10,11,12},7]//Dbg[]

• Dbg is controlled by options. These may be set on a per use basis, as in the above example, or until further notice using SetOptions. For example, Dbg is set up to print only ten steps by default. This is to prevent a beginner becoming overwhelmed by output and failing to learn how to control the debugger. To reset this limit to 100 you could use:

SetOptions[Dbg,MaxSteps->100]

Options set explicitly in Dbg override options set using SetOptions in the normal Mathematica way.
Dbg understands the following options:

({{MaxSteps, 10, Maximum number of intermediate steps to print}, {ShowHeads, All, Head or list of heads. Print only expressions 
that match one of these}, {ExcludeHeads, None, List of heads of expressions not to print}, {ContinueExecution, True, Whether to continue execution after print 
limit exceeded }, {StartStep, 1, Number of step at which printing begins }, {TriggerOn, None, Start printing after this pattern is reached}, {TriggerOff, None, Stop printing after this pattern has 
been reached}, {ShowLevels, False, Show the level on the stack of each expression 
(advanced feature)}, {SuppressPackages, True, Do not print steps inside packages }, {HiddenFunctions, None, List of functions whose inner workings are 
not of interest}, {AbortStep, ∞, Abort with traceback after this step number}, {Step, False, Suspend the program at the first step so as to
use the breakpoint facilities}, {BreakAt, Null, Pattern to use as a breakpoint. This option is
usually supplied using the :> operator to
prevent the evaluation of the pattern itself}, {BreakFail, False, Whether to use the breakpoint mechanism if the 
program is aborted by button or generates an 
error message.}, {AbortStepCount, 10, Number of steps to print after  the Abort 
button is pressed and before the process 
goes into traceback and aborts}, {FilterHeads, Null, User-supplied predicate which takes a head as
argument and returns True if Dbg should display 
expressions with that head.}})

• Setting ShowHeads to a head or list of heads usually drastically reduces the output of Dbg – only terms with the given heads will print. Sometimes it is more effective to trim the output using ExludeHeads.
• Setting HiddenFunctions effectively provides a list of functions that should be treated as built-in. Assuming the function concerned does not suppress argument evaluation you will see the arguments being evaluated, but none of the inner workings of the function (except the final result).
• Setting AbortStep can be useful when you know the step number (from a previous run of Dbg) when things start to go wrong, and you want a traceback at that point.
• The FilterHeads option can be useful in more complex situations where for example code in one package is to be debugged, but code in others is to be suppressed. In this case the filter could simply test the context of the head and retturn True or False as required.

• Debugging a Mathematica program can seem like seeking a needle in a haystack! One way that Dbg can help is by colouring a piece of your code in such a way that the colour appears in your trace information. To colour part of your code, select an expression (subject to a few restrictions discussed below) and press the 'Coloured' button in the top part of the debugger palette. Colouring code in any other way (e.g. using the Format menu or option inspector) will not work, even though it may appear identical. Coloured expressions can be used from within Dbg or on their own. Here are some examples of what you can do with coloured expressions:

• Here we use a coloured expression on its own to obtain an intermediate print out. Notice how we see the expression before evaluation as well as its value.

R=With[{x=3,y=4},Sqrt[x^2+y^2]]

• Here is the same calculation run under Dbg. The coloured expression produces the same printout (but embedded in the normal Dbg output) and also causes Dbg to suppress the internal details of its evaluation. Observe also how the coloured expression can be seen in earlier output from Dbg – making it much easier to understand where the various steps have come from.

(R=With[{x=3,y=4},Sqrt[x^2+y^2]])//Dbg[]

• Here we see the function that was used as an example earlier, but with some added colour. After adding colour it is important to re-execute the function definition to cause the colour to become part of the function definition in the kernel.

dot˘product[x_List,y_List]:=Module[{k=Length[x],ss=0},
Print["Starting"];
While[k>0,
ss=ss+x[[k]]y[[k]];
k--;
];
ss
]

• Once again we can use the colour on its own, or under Dbg:

dot˘product[{2,3},{4,5}]

dot˘product[{2,3},{4,5}]//Dbg[StartStep->11]

Observe that the total number of steps is now 60, rather than 84. This is because the evaluation of the coloured expression is performed in one step. Notice also that coloured expressions still print out even after the default MaxSteps value of 10 is exceeded.

• Coloured expressions can also be used as breakpoints. Pressing the "colour break" button while a program is suspended will run it until the next coloured expression is encountered. Remeber that the contents of the coloured expression will be evaluated as one step, and cannot therefore be stepped into, so if you want to run your program until an expression foo[a,b,c] is reached and then to step through that evaluation colour the head foo, rather than the whole expression.

• By colouring something in a function definition with several parts it can become obvious which part was actually used in a particular case:

f[x_?NumberQ]:=True;
f[x_List]:=True;
f[π]:=True;

f[3.1415]//Dbg[]

• Sometimes it is useful to colour the head of an expression – simply to make it stand out in the Dbg output. This case is treated specially and the value printout is suppressed.

dot˘product[x_List,y_List]:=Module[{k=Length[x],ss=0},
Print["Starting"];
While[k>0,
ss=ss+x[[k]]y[[k]];
k--;
];
ss
]

dot˘product[{2,3},{4,5}]//Dbg[]

• Obviously, the expression to be coloured must be a syntactically valid expression. The simplest way to ensure this is to use multi-clicking to select the expression to be coloured. Furthermore, colour must not be added to the left of := or = in a function definition, or in places where it will not get evaluated (e.g. inside Hold, MakeExpression, inside strings, etc.). Note that in an expression such as:

foo[[k+1]]=z

the expression k+1 is evaluated, so it may be coloured even though it is on the left of an assignment. See the help on Set  for full details.

• Coloured expressions are represented using a special debugger function, Dbg˘Coloured, that evaluates to its argument. Ii is possible to save a notebook containing coloured expressions as part of function definitions. If the Dbg package is loaded the next time the contents of the notebook are evaluated, then the colour will work just as before. If however the Dbg package is not loaded the colour will have no special effect - the code will perform just as it would without colour. This can obviously be useful when you need to try some 'production runs' with code that is still under test.

• Colour can be removed from your notebook when no longer required by click selecting the relevant coloured expression and pressing the 'Remove colour' button. This button will remove all the colour within a selection – possibly including several separate coloured expressions. If, say, the entire contents of a notebook are selected, this button will remove all the colour in one go.
• Colour introduced using Dbg is different from that introduced by the Salford Colour Math package for algebraic manipulation (although the mode of operation is broadly similar). Because Dbg colours the background rather than the text itself, these two applications of colour should not become confused.

Dbg has a simple breakpoint mechanism, which can be accessed by setting Step->True to break at the very first step of your code, or by using the BreakAt option to specify a pattern at which to break. Whenever the program is suspended this fact will be observable because the cell bracket of the original input cell will indicate that this calculation is still running. However, it is possible to execute almost any Mathematica calculation while a program is suspended, and the lower set of debugger buttons become available. These buttons have the following effect:

({{Resume, Restarts the program.}, {Step, Advances the program by one step.}, {Step over, Breaks the program when the current expression being
evaluated is complete.}, {Set breakpoint, Prompts for a new pattern to act as a breakpoint, 
then runs the program until an expression matching this
pattern is encountered.}, {Colour break, Runs the program until a coloured expression is encountered.}, {Traceback, Prints a traceback corresponding to the current point.}, {Abort, Abandons this debug session.}})

• While a program is suspended by Dbg, you should not use the top set of debugger buttons. Also, any attempt to start another (nested) Dbg session will be faulted.
• Subject to these minor restrictions, the whole power of Mathematica is available while the program is suspended. In particular, you can type the name of any variable and obtain its current value in the usual way. This can be particularly useful when debugging programs containing explicit loops.
• Bear in mind that local variables in Module constructs etc. will have been renamed in the standard Mathematica way. You must determine the new name (which corresponds to the old name with a '$' and a number appended) in order to interrogate its value. The new name will vary from run to run, so it may be worth setting a breakpoint at a point where the name can be established, or adding a Print call to your program to establish the name in question, for example:

Module[{k},Print[k];k=1;foo[k]]//Dbg[BreakAt:>foo[_]]

k$46

We knew the name k$46 because of the Print statement. Alternatively, the expression k=1 could have been coloured, so that the name of k could be determined from the usual colour printout.
• Note that the BreakAt option uses the ':>' operator to prevent any spurious evaluation of the pattern itself. 
• Currently only one breakpoint can be in effect at any one time. A more general scheme may be implemented later.

• When you load in a package you are adding a quantity of code that has been written by someone else. Usually you have no interest in the inner workings of package code and do not want to debug it with Dbg. For this reason, by default Dbg treats all functions belonging to packages as hidden. You can reverse this behaviour by setting the option SuppressPackages to False. 
• Even some apparently built-in functions, such as CellPrint, are actually implemented using a built-in package. Normally you only become aware of this if you use TracePrint (say) on a CellPrint call. Since such internal code is in a package, Dbg also suppresses this by default. The internal workings of such functions can be interesting, but not usually when you are debugging higher level code.

• When Mathematica generates a kernel message this is usually a sign that your program has failed. Therefore unless a message has been suppressed (e.g. using Off), Dbg treats such messages as fatal errors and prints a traceback of the expressions leading to the error. For example:

f1[x_]:=f2[x];
f2[x_]:=1/x;

f1[0]//Dbg[StartStep->20]

• As you can see, the traceback is generated whether or not steps are being printed at the time of the error. After printing the traceback the program is aborted.

• Some software bugs result in code that runs for ever. Sometimes such code continues to consume some resource, such as memory or disk space, and so fails eventually, in other cases the program has to be explicitly stopped. Mathematica includes certain limits to try to prevent programs running out of control like this, however these limits have to be set quite high, and frequently users must raise them further to get legitimate code to work. Here is a typical problem:

Module[{x},x=x+1]

As you can see, it is rather easy to encounter infinite loops in Mathematica code. In the example the problem is simply that 'x' has no value. If this were C or Fortran code (say) it would either generate a compiler error, a runtime error, or produce an x with an arbitrary numeric value. Because Mathematica handles symbolic as well as numeric data, execution proceeds as follows: First x acquires the symbolic expression x+1, then, because the value of x has changed the expression gets re-evaluated and assigned to x, so x acquires the symbolic value x+2, ... If you are not sure about this try applying Dbg to this expression and observe what happens!

If you press the Abort button in the debugger palette this will send a message to Dbg to print a few steps (regardless of the options used to Dbg), and then print a trace back as if the AbortStep had been reached. If you need more steps printed out to understand the context of the loop, simply run again with StartStep set appropriately, or change the AbortStepCount option. 

• Pressing Abort will only work if your code is actually stuck in a kernel loop. It is easy to see this by looking at the status bar of the debugger palette, where the step number should be changing. Mathematica can stop for a variety of other reasons. A common reason is if your program returns an unreasonable amount of output to the frontend (e.g. a 1000 x 1000 matrix!). 
• If you use the normal built-in abort operation, Alt+., you will abort your program but receive no further information from Dbg.
• Sometimes it is more useful to suspend the program as if a breakpoint had been reached when a program generates an error message or is aborted by pressing "Abort process". This can be achieved by setting the option BreakFail to True. A process that has been interrupted using the button can be resumed or stepped just as if a normal breakpoint had been reached, however programs that had reached an error condition will simply abort if they are resumed.

• Dbg uses TraceScan at its heart to perform its work. However, as you will see if you look at the file DEBUGGER.M, Dbg performs a considerable amount of extra work on top. Dbg achieves a number of important improvements in the output which are discussed in this section. If you are not interested in the built-in Mathematica Trace functions, then you may wish to skip this section. All the Trace functions operate in essentially the same way, so TracePrint is used in the illustrations that follow for ease of comparison with Dbg.

• Consider the following piece of code:

f[x_]:=h[x]+h[2 x];
h[x_]:=If[x>0,(2 x+1)/(2 x-1),0]

• Suppose we want to debug this function without displaying the inner workings of the function h - we type the following.

f[3]//Dbg[HiddenFunctions->h]

Now we compare this with the analogous operation using TracePrint:

TracePrint[f[3],TraceOff->h[_]]

In addition to suppressing the inner workings of the function h, TracePrint has suppressed the details of the evaluation of the arguments of h. Thus we do not see the evaluation of the product (2 3). Clearly, in a more complicated example this omission could be important. Using the HiddenFunctions option of Dbg (or the implicit hiding of packaged functions) causes Dbg to treat the function concerned as if it were totally built-in (such as Sin). Thus the argument evaluation is shown, but the inner workings of the function are suppressed. 
Note that if the function has attributes such as HoldAll, the argument evaluation is delayed until inside the function (if at all), so in this case the details would not be printed.

• As you will see if you examine the code, Dbg succeeds in hiding the workings of hidden functions by altering their DownValues — effectively altering their definition. This alteration is made in the kernel, so the contents of the notebook itself are not changed, also the alteration is reversed before Dbg exits, however care should be taken with certain types of code. Essentially, you should not suppress the operation of functions that will be modified on the fly while the code is executing. For example, if the function tt is used as a lookup table that is created as your code executes, do not pass its name to the HiddenFunctions option of Dbg. This restriction does not apply to deeper functions inside a hidden function (which is obviously the usual case) so it is rarely troublesome.

Clearly Dbg can be developed in various ways. Depending on the feedback I receive, some or all of the following may get implemented:
•  The interactive breakpoint facilities are quite limited at present, partly because they are based on the use of a palette. Clearly if Dbg had a set of commands that could be typed (or maybe one Dbg˘Resume with a variety of options) debugging could become more flexible.
• The lists of constructs which are abbreviated (such as Module) or hidden (such as CompoundExpression) may be augmented as necessary.
• If possible I will reduce the large performance overhead introduced by Dbg. To some extent this is inevitable (unless certain critical routines were moved inside Mathematica itself!) because each step of user code must be processed inside Dbg, even if it is not printed.
• Currently Dbg always treats displayed kernel messages as fatal – resulting in a traceback and a program abort. Where this prevents a valid program from running it is only necessary to use Off to suppress the relevant messages. However it may be that it may be useful to lift this restriction in some way.