[ Reference Manual | Alphabetic Index ]

# library(instrument)

Generic tool for code instrumentation [more]

## Predicates

> defined_modules(+File, -Modules)
>   Retrieve the modules defined by a file.
> erase_all_templates
>   Erase all instrument template stores.
> erase_file_templates(+File)
>   Erase the file local instrument template store for a specific file.
> erase_module_templates
>   Erase all instrument template stores for a specific module.
> file_callsites(+File, ?StartId, ?EndId)
>   Retrieve start and end callsite identifiers for named file.
> file_result(+File)
>   Pretty-print a file, including any instrumentation results
> file_result(+File, +OptionList)
>   Pretty-print a file, including any instrumentation results
> get_callsite_data(+SiteId, ?UserData)
>   Retrieve data associated with an instrumentation callsite from its non-logical store.
> instrument(+File, +ITemplates)
>   Compile a file, inserting predicate instrumentation
> instrument(+File, +ITemplates, +OptionList)
>   Compile a file, inserting predicate instrumentation
> instrument_control(+Mode, +InstrumentPredSpec)
>   Insert or remove instrumentation predicates dynamically at runtime.
> module_callsites(?StartId, ?EndId)
>   Retrieve module start and end callsite identifiers.
> module_result
>   Pretty-print all files in a module, including any instrumentation results
> module_result(+OptionList)
>   Pretty-print all files in a module, including any instrumentation results
> set_callsite_data(+SiteId, ?UserData)
>   Associate arbitrary data with an instrumentation callsite in a non-logical store.

## Structures

> struct itemplate(clause_start, clause_end, clause_fail, clause_redo, block_start, block_end, block_fail, block_redo, subgoal_start, subgoal_end, subgoal_fail, subgoal_redo, call_start, call_end, call_fail, call_redo, fact, inbetween, result, meta_args, exclude, code_weaver, asserted, module_scope, file_local, goal_expansion)
>   The template used to guide predicate instrumentation

## Description

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:

1. Load the instrument library

          ?- lib(instrument).

2. Compile your program with the instrument compiler

          ?- instrument:instrument(my_program, Templates).

3. Run the query for which you wish to generate instrumentation data

          ?- my_query(X,Y,Z).

4. Generate an html file containing the results. E.g. the following will create the result file instrument/my_program.html:

          ?- instrument:results(my_program).

5. 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.

## About

- Status: prototype
- Author: Andrew Cheadle, based on ideas by Joachim Schimpf
- Date: $Date: 2013/02/14 01:31:05 $

Generated from instrument.eci on 2022-09-03 14:26