When you compile a Mercury program, you can specify whether you want to be able to run the Mercury debugger on the program or not. If you do, the compiler embeds calls to the Mercury debugging system into the executable code of the program, at the execution points that represent trace events. At each event, the debugging system decides whether to give control back to the executable immediately, or whether to first give control to you, allowing you to examine the state of the computation and issue commands.
Mercury supports two broad ways of preparing a program for debugging.
The simpler way is to compile a program in a debugging grade,
which you can do directly by specifying a grade
that includes the word “debug” or “decldebug”
(e.g. ‘asm_fast.gc.debug’, or ‘asm_fast.gc.decldebug’),
or indirectly by specifying one of the ‘--debug’ or ‘--decl-debug’
grade options to the compiler, linker, and other tools
If you follow this way,
and accept the default settings of the various compiler options
that control the selection of trace events (which are described below),
you will be assured of being able to get control
at every execution point that represents a potential trace event,
which is very convenient.
The “decldebug” grades improve declarative debugging by allowing the user to track the source of subterms (see Improving the search). Doing this increases the size of executables, so these grades should only be used when you need the subterm dependency tracking feature of the declarative debugger. Note that declarative debugging, with the exception of the subterm dependency tracking features, also works in the .debug grades.
The two drawbacks of using a debugging grade are the large size of the resulting executables, and the fact that often you discover that you need to debug a big program only after having built it in a non-debugging grade. This is why Mercury also supports another way to prepare a program for debugging, one that does not require the use of a debugging grade. With this way, you can decide, individually for each module, which of four trace levels, ‘none’, ‘shallow’, ‘deep’, and ‘rep’ you want to compile them with:
A procedure compiled with trace level ‘none’ will never generate any events.
A procedure compiled with trace level ‘deep’ will always generate all the events requested by the user. By default, this is all possible events, but you can tell the compiler that you are not interested in some kinds of events via compiler options (see below). However, declarative debugging requires all events to be generated if it is to operate properly, so do not disable the generation of any event types if you want to use declarative debugging. For more details see Declarative debugging.
This trace level is the same as trace level ‘deep’, except that a representation of the module is stored in the executable along with the usual debugging information. The declarative debugger can use this extra information to help it avoid asking unnecessary questions, so this trace level has the effect of better declarative debugging at the cost of increased executable size. For more details see Declarative debugging.
A procedure compiled with trace level ‘shallow’ will generate interface events if it is called from a procedure compiled with trace level ‘deep’, but it will never generate any internal events, and it will not generate any interface events either if it is called from a procedure compiled with trace level ‘shallow’. If it is called from a procedure compiled with trace level ‘none’, the way it will behave is dictated by whether its nearest ancestor whose trace level is not ‘none’ has trace level ‘deep’ or ‘shallow’.
The intended uses of these trace levels are as follows.
You should compile a module with trace level ‘deep’ if you suspect there may be a bug in the module, or if you think that being able to examine what happens inside that module can help you locate a bug.
You should compile a module with trace level ‘rep’ if you suspect there may be a bug in the module, you wish to use the full power of the declarative debugger, and you are not concerned about the size of the executable.
You should compile a module with trace level ‘shallow’ if you believe the code of the module is reliable and unlikely to have bugs, but you still want to be able to get control at calls to and returns from any predicates and functions defined in the module, and if you want to be able to see the arguments of those calls.
You should compile a module with trace level ‘none’ only if you are reasonably confident that the module is reliable, and if you believe that knowing what calls other modules make to this module would not significantly benefit you in your debugging.
In general, it is a good idea for most or all modules that can be called from modules compiled with trace level ‘deep’ or ‘rep’ to be compiled with at least trace level ‘shallow’.
You can control what trace level a module is compiled with by giving one of the following compiler options:
This always sets the trace level to ‘shallow’.
This always sets the trace level to ‘deep’.
This always sets the trace level to ‘rep’.
In debugging grades, this sets the trace level to ‘shallow’; in non-debugging grades, it sets the trace level to ‘none’.
In debugging grades, this sets the trace level to ‘deep’; in non-debugging grades, it sets the trace level to ‘none’.
As the name implies, the last alternative is the default, which is why by default you get no debugging capability in non-debugging grades and full debugging capability in debugging grades. The table also shows that in a debugging grade, no module can be compiled with trace level ‘none’.
Important note: If you are not using a debugging grade, but you compile some modules with a trace level other than none, then you must also pass the ‘--trace’ (or ‘-t’) option to c2init and to the Mercury linker. If you’re using Mmake, then you can do this by including ‘--trace’ in the ‘MLFLAGS’ variable.
If you’re using Mmake, then you can also set the compilation options for a single module named Module by setting the Mmake variable ‘MCFLAGS-Module’. For example, to compile the file foo.m with deep tracing, bar.m with shallow tracing, and everything else with no tracing, you could use the following:
MLFLAGS = --trace MCFLAGS-foo = --trace deep MCFLAGS-bar = --trace shallow