7.7 Mercury debugger concepts
The operation of the Mercury debugger ‘mdb’
is based on the following concepts.
- break points
- The user may associate a break point
with some events that occur inside a procedure;
the invocation condition of the break point says which events these are.
The four possible invocation conditions (also called scopes) are:
- the call event,
- all interface events,
- all events, and
- the event at a specific point in the procedure.
The effect of a break point depends on the state of the break point.
- If the state of the break point is ‘stop’,
execution will stop and user interaction will start
at any event within the procedure that matches the invocation conditions,
unless the current debugger command has specifically disabled this behaviour
(see the concept ‘strict commands’ below).
- If the state of the break point is ‘print’,
the debugger will print any event within the procedure
that matches the invocation conditions,
unless the current debugger command has specifically disabled this behaviour
(see the concept ‘print level’ below).
Neither of these will happen if the break point is disabled.
Every break point has a print list.
Every time execution stops at an event that matches the breakpoint,
mdb implicitly executes a print command for each element
in the breakpoint's print list.
A print list element can be the word ‘goal’,
which causes the goal to the printed as if by ‘print goal’;
it can be the word ‘*’,
which causes all the variables to the printed as if by ‘print *’;
or it can be the name or number of a variable,
possibly followed (without white space) by term path,
which causes the specified variable or part thereof to the printed
as if the element were given as an argument to the ‘print’ command.
- strict commands
- When a debugger command steps over some events
without user interaction at those events,
the strictness of the command controls whether
the debugger will stop execution and resume user interaction
at events to which a break point with state ‘stop’ applies.
By default, the debugger will stop at such events.
However, if the debugger is executing a strict command,
it will not stop at an event
just because a break point in the stop state applies to it.
If the debugger receives an interrupt (e.g. if the user presses control-C),
it will stop at the next event regardless of what command it is executing
at the time.
- print level
- When a debugger command steps over some events
without user interaction at those events,
the print level controls under what circumstances
the stepped over events will be printed.
- When the print level is ‘none’,
none of the stepped over events will be printed.
- When the print level is ‘all’,
all the stepped over events will be printed.
- When the print level is ‘some’,
the debugger will print the event only if a break point applies to the event.
Regardless of the print level, the debugger will print
any event that causes execution to stop and user interaction to start.
- default print level
- The debugger maintains a default print level.
The initial value of this variable is ‘some’,
but this value can be overridden by the user.
- current environment
- Whenever execution stops at an event, the current environment
is reset to refer to the stack frame of the call specified by the event.
However, the ‘up’, ‘down’ and ‘level’ commands
can set the current environment
to refer to one of the ancestors of the current call.
This will then be the current environment until another of these commands
changes the environment yet again or execution continues to another event.
- paths in terms
- When browsing or printing a term,
you can use "^‘n’" to refer to the nth subterm of that term.
If the term's type has named fields,
you can use "^‘fname’" to refer to
the subterm of the field named ‘fname’.
You can use several of subterm specifications in a row
to refer to subterms deep within the original term.
For example, when applied to a list,
"^2" refers to the tail of the list
(the second argument of the list constructor),
"^2^2" refers to the tail of the tail of the list,
and "^2^2^1" refers to the head of the tail of the tail,
i.e. to the third element of the list.
You can think of terms as Unix directories,
with constants (function symbols of arity zero) being plain files
and function symbols of arity greater than zero being directories themselves.
Each subterm specification such as "^2" goes one level down in the hierarchy.
The exception is the subterm specification "^..",
which goes one level up, to the parent of the current directory.
- held variables
- Normally, the only variables from the program accessible in the debugger
are the variables in the current environment at the current program point.
However, the user can hold variables,
causing their values -or selected parts of their values-
to stay available for the rest of the debugger session.
All the commands that accept variable names
also accept the names of held variables;
users can ask for a held variable
by prefixing the name of the held variable with a dollar sign.
- user defined events
- Besides the built-in set of events,
the Mercury debugger also supports events defined by the user.
Each event appears in the source code of the Mercury program
as a call prefixed by the keyword ‘event’,
with each argument of the call giving the value of an event attribute.
Users can specify the set of user defined events that can appear in a program,
and the names, types and order of the attributes
of each kind of user defined event,
by giving the name of an event set specification file to the compiler
when compiling that program.
For more details, see User defined events.
- user defined event attributes
- Normally, the only variables from the program accessible in the debugger
are the variables in the current environment at the current program point.
However, if the current event is a user defined event,
then the attributes of that event are also available.
All the commands that accept variable names
also accept the names of attributes;
users can ask for an attribute
by prefixing the name of the attribute with an exclamation point.
- procedure specification
- Some debugger commands, e.g. ‘break’,
require a parameter that specifies a procedure.
The procedure may or may not be a compiler-generated
unify, compare, index or init procedure of a type constructor.
If it is, the procedure specification has
the following components in the following order:
- An optional prefix of the form
‘unif*’, ‘comp*’, ‘indx*’ or ‘init*’,
that specifies whether the procedure belongs
to a unify, compare, index or init predicate.
- An optional prefix of the form ‘module.’ or ‘module__’
that specifies the name of the module that defines the predicate or function
to which the procedure belongs.
- The name of the type constructor.
- An optional suffix of the form ‘/arity’
that specifies the arity of the type constructor.
- An optional suffix of the form ‘-modenum’
that specifies the mode number of the procedure
within the predicate or function to which the procedure belongs.
For other procedures, the procedure specification has
the following components in the following order:
- An optional prefix of the form ‘pred*’ or ‘func*’
that specifies whether the procedure belongs to a predicate or a function.
- An optional prefix of the form ‘module:’, ‘module.’
or ‘module__’ that specifies the name of the module that defines
the predicate or function to which the procedure belongs.
- The name of the predicate or function to which the procedure belongs.
- An optional suffix of the form ‘/arity’
that specifies the arity of the predicate or function
to which the procedure belongs.
- An optional suffix of the form ‘-modenum’
that specifies the mode number of the procedure
within the predicate or function to which the procedure belongs.