To use it, users just need to define 4 things in a file, using the Mercury syntax:
:- pred initialize(collected_type). :- mode initialize(out) is det.
:- pred filter(event, collected_type, collected_type, stop_or_continue). :- mode filter(in, acc_in, acc_out, out) is det.
where `acc_in
' and `acc_out
' have `in
' and `out
'
respectively as default values.
acc_in
' and `acc_out
'
in one want to override their default values.
The event type is defined as follows (for more detail about the meaning of each event attributes, please refer to the Reference Manual):
:- type event_number == int.
:- type call_number == int.
:- type depth_number == int.
:- type trace_port_type
--> call
; exit
; redo
; fail
; ite_cond
; ite_then
; ite_else
; neg_enter
; neg_success
; neg_failure
; disj
; switch
; nondet_pragma_first
; nondet_pragma_later
; exception.
:- type pred_or_func
--> predicate
; function.
:- type declarated_module_name == string.
:- type defined_module_name == string.
:- type proc_name == string.
:- type arity == int.
:- type mode_number == int.
:- type determinism == int.
:- type goal_path_string == string.
:- type procedure --> proc(
pred_or_func,
declarated_module_name,
proc_name,
arity,
mode_number).
:- type arguments == list(univ).
Here are functions that eases the access of the event attributes:
:- func chrono(event::in) = (event_number::out) is det.
:- func call(event::in) = (call_number::out) is det.
:- func depth(event::in) = (depth_number::out) is det.
:- func port(event::in) = (trace_port_type::out) is det.
:- func proc_type(event::in) = (pred_or_func::out) is det.
:- func decl_module(event::in) = (declarated_module_name::out) is det.
:- func def_module(event::in) = (defined_module_name::out) is det.
:- func proc_name(event::in) = (proc_name::out) is det.
:- func proc_arity(event::in) = (arity::out) is det.
:- func proc_mode_number(event::in) = (mode_number::out) is det.
:- func proc(event::in) = (procedure::out) is det.
:- func determinism(event::in) = (determinism::out) is det.
:- func goal_path(event::in) = (goal_path_string::out) is det.
:- func arguments(event::in) = (arguments::out) is det. (*)
(*) To be able to retrieve arguments, you to need to have the opium parameter
`collect_arg' set to yes (`man collect_arg.' for more details).
Then, this file is used to generate the Mercury module `collect.m', which is compiled and dynamically linked with the current execution. When a `collect' request is made from the external debugger, a variable of type collected_type is first initialized (with initialize/1) and then updated (with filter/4) for all the events of the remaining execution. When the fourth argument of filter is equal to yes, or when the end of the execution is reached, the last value of the collecting variable is send to Morphine.
collect/2 can be seen as fold/4 meta-predicate except that (1) the initialization and the updating of the accumulator is done via Mercury predicates defined in a separate file (2) it does not take a list as argument but operates on the fly on a list of events (3) we can stop the process at anytime thanks the fourth argument of filter/4.
Here is an example of a simple monitor that counts calls. If a file `count_call' contains the following statements:
initialize(0).
filter(Event, AccIn, AccOut, continue) :-
( port(Event) = call ->
AccOut = AccIn + 1
;
AccOut = AccIn
).
Then the command `collect(count_call, Result)' will unify Result with the number of calls that occur during the program execution.
File : is_atom_or_string
Result : is_atom_or_var
type of command : opium
current_grade(Grade)
Retrieves the grade the current program execution has been compiled with.
Grade : var
generate_collect(File)
Generates a Mercury module named `collect.m' from file File; File should contain the definition of the accumulator type (collected_type), initialize/1 and filter/4 predicates.
File : is_atom_or_var
dyn_link_collect
Dynamically links the collect module with the currently run program.
run_collect(Result)
Executes the collect command provided that collect.m has been correctly, generated, compiled and dynamically linked with the current program execution.
Result : var