lib(instrument)


   The instrument library is a tool that enables predicate definitions or all
   calls to a specific predicate to be annotated with user-defined predicates.
   These instrumentation predicates are free to perform such actions as collect
   program statistics or write debugging data to a stream during program
   execution.  Additionally, the instrumentation can be inserted and removed
   dynamically as the program executes.

The usage is as follows:
   
   Load the instrument library
   
   ?- lib(instrument).
   
   Compile your program with the instrument compiler
   
   ?- instrument:instrument(my_program, Templates).
   
   Run the query for which you wish to generate instrumentation data
   
   ?- my_query(X,Y,Z).
   
   Generate an html file containing the results. E.g. the following
   will create the result file instrument/my_program.html:
   
   ?- instrument:results(my_program).
   
   View the result file using any browser. The result file
   contains a pretty-printed form of the source, annotated with
   the instrumentation results.
   
   
   The following is an example of very basic use of the tool.
   Consider instrumenting a simple path finding program saved in a
   file called 'instrument_example.ecl.
   
   list_member(X, [X | _Tail]).
   list_member(X, [_ | Tail]):-
   list_member(X, Tail).
 
   all_edges(Graph, Source, Outgoing):-
       findall((Source-Next),list_member((Source-Next), Graph), Outgoing).
 
   path(_Graph, Source, Source, []).
   path(Graph, Source, Sink, [(Source-Next) | Path]):-
       all_edges(Graph, Source, Outgoing),
       list_member((Source-Next), Outgoing),
       path(Graph, Next, Sink, Path).
 
   test(Path):-
       path([1-2, 1-3, 2-4, 3-4, 4-5, 4-6, 5-7, 5-8, 6-7,
             1-8, 8-9, 8-10, 9-10, 9-11, 10-11],
             1,
             7,
             Path).
   
   The simplest way to instrument your code is to insert predicates around
   every call in the source. The following code demonstrates how to
   print the CPU time before and after every predicate call:
   
   get_time(Cpu):-
       statistics(times, [Cpu, _Sys, _Real]).
 
   time_point:-
       get_time(T),
       writeln(error, T).
 
   go:-
       File = instrument_example,
       GlobalTemplate = itemplate{subgoal_start:time_point/0,
                                         subgoal_end:time_point/0},
       instrument(File, GlobalTemplate),
       test(Path).
   
   Note the same predicate time_point/0 is specified before and
   after goal calls. If we wished to instrument all calls except those
   to list_member/2, the following call to instrument/2 is
   made:
   
        instrument(File, GlobalTemplate - [list_member/2])
   
   In general any number of goals may be specified in this 'exclusion list'.
    
   The following code demonstrates alternative instrumentation
   for this excluded predicate:
   
   :-lib(instrument).

   :-export time_point/0, special_point/0.

   get_time(Cpu):-
       statistics(times,[Cpu, _Sys, _Real]).        

   time_point:-
       get_time(T),
       writeln(error, T).

   special_point:-
       writeln(error, 'start, end, redo or fail').

   go:-
       File = instrument_example,
       GlobalTemplate = itemplate{subgoal_start:time_point/0,
                                        subgoal_end:time_point/0},
       SpecialTemplate = itemplate{call_start:special_point/0,
                                         call_end:special_point/0,
                                         call_fail:special_point/0
                                         call_redo:special_point/0},
       instrument(File,[GlobalTemplate - [list_member/2],
                        (list_member/2) = SpecialTemplate]),
       test(Path).
   
   Notice how the special_point/0 predicate is assigned to the
   call_start, call_end, call_fail and 
   call_redo points in this example. This ensures that the 
   special_point predicate is called if list_member/2 
   fails, or if resatisfiable, executed at the redo.
   
   Using arity-1 predicates (i.e. one argument predicates)
   unique identification of the callsite can be obtained:
   
   :-lib(instrument).

   get_time(Cpu):-
       statistics(times, [Cpu, _Sys, _Real]).        

   time_point(CallSite):-
       get_time(T),
       writeln(error,['Time', T, 'at callsite', CallSite]).

   go:-
       File = instrument_example,
       GlobalTemplate = itemplate{subgoal_start:time_point/1,
                                        subgoal_end:time_point/1},
       instrument(File, GlobalTemplate),
       test(Path).
   
   
   By supplying a predicate to the result field of a template 
   one can specify terms to be printed within a copy of the source code.
   Using this feature along with the utility predicates 
   get_callsite_data/2 and set_callsite_data/2 
   one can create quite varied and useful output.
   
   :-lib(instrument).

   get_time(Cpu):-
       statistics(times, [Cpu, _Sys, _Real]).        

   time_point(CallSite):-
       get_time(T),
       set_callsite_data(CallSite, T).

   result(CallSite, _Type, _Module, Goal, NewGoal):-
       get_callsite_data(CallSite, Time),
       NewGoal='instrument:point_before'(Time, Goal).
   go:-
       File = instrument_example,
       GlobalTemplate = itemplate{subgoal_start:time_point/1,
                                        result:result/5},
       instrument(File, GlobalTemplate),
       test(Path),
       file_result(File).
   
   No data is printed during the running of the test(Path) call, 
   but the file_result(File) call causes the source code to be 
   emitted (color coded) with the given callsite data embedded.


