This is some example code that reveals a transformation of a call graph made by using meta_predicate/1.
This is based on the blog Using SWI-Prolog’s modules.
prolog_walk_code/1 is used to create the call graph as calls/2 facts. (Example)
Directory used for example: C:\Users\Groot\Documents
File: add.pl
:- module('add',[
add_all/2,
add_some/3
]).
% :- meta_predicate add_some(-,1,-).
add_all(List,Sum) :-
sum_list(List,Sum).
add_some(List,Goal,Sum) :-
add_some_(List,Goal,Sum).
add_some_([],_,[]).
add_some_([H0|T0],Goal,R) :-
(
call(Goal,H0)
->
R = [H0|T]
;
R = T
),
add_some_(T0,Goal,T).
File: filters.pl
:- module('filters',[
less_than_three/1
]).
less_than_three(N) :-
integer(N),
N < 3
File: broken.pl
:- module('broken',[
]).
:- use_module(add).
:- use_module(filters).
go :-
add_some([1,2,3,4,5], less_than_three, S),
writeln(S).
File: call_graph.pl
:- module('call_graph',[
assert_call_graph/0
]).
:- dynamic
calls/2.
assert_call_graph :-
retractall(calls(_, _)),
prolog_walk_code([ trace_reference(_),
on_trace(assert_edge),
source(true),
infer_meta_predicates(all),
verbose(true)
]),
predicate_property(calls(_,_), number_of_clauses(N)),
format('Got ~D edges~n', [N]).
assert_edge(Callee, Caller, _Where) :-
calls(Caller, Callee), !.
assert_edge(Callee, Caller, _Where) :-
assertz(calls(Caller, Callee)).
Verify the code is broke
C:\Users\Groot>cd "C:\Users\Groot\Documents"
C:\Users\Groot\Documents>swipl -q -t broken:go,halt -f broken.pl
ERROR: add:add_some_/3: Unknown procedure: add:less_than_three/1
Exception: (7) add:less_than_three(1) ?
Load the code and use assert_call_graph/0.
?- working_directory(_,'C:/Users/Groot/Documents').
true.
?- [call_graph,add,filters,broken].
true.
?- assert_call_graph.
% Found new meta-predicates in iteration 1 (5.406 sec)
% :- meta_predicate add:add_some_(*,1,*).
% :- meta_predicate prolog:generated_predicate(:).
% :- meta_predicate user:prolog_exception_hook(*,*,*,:).
% Restarting analysis ...
% Found new meta-predicates in iteration 2 (5.484 sec)
% :- meta_predicate add:add_some(*,1,*).
% Restarting analysis ...
Got 8,703 edges
true.
Save the calls/2 to file calls (no meta).txt
?- tell('./calls (no meta).txt'), listing(call_graph:calls), told.
?- halt.
Thanks Jan W. (ref)
File: calls (no meta).txt
...
calls(add:add_some(A, B, C), add:add_some_(A, B, C)).
calls(add:add_some_([A|_], B, _), add:call(B, A)).
calls(add:add_some_([A|_], _, B), add:(B=[A|_])).
calls(add:add_some_([_|A], B, _), add:add_some_(A, B, _)).
calls(add:add_all(A, B), add:sum_list(A, B)).
...
calls(filters:less_than_three(A), filters:integer(A)).
calls(filters:less_than_three(A), filters:(A<3)).
...
calls(broken:go, broken:add_some([1, 2, 3, 4, 5], less_than_three, _)).
calls(broken:go, broken:writeln(_)).
...
Add meta_predicate to add module.
:- meta_predicate add_some(-,1,-).
Don’t forget to save the change.
Verify the code works
C:\Users\Groot>cd "C:\Users\Groot\Documents"
C:\Users\Groot\Documents>swipl -q -t broken:go,halt -f broken.pl
[1,2]
Load the code and use assert_call_graph/0.
?- working_directory(_,'C:/Users/Groot/Documents').
true.
?- [call_graph,add,filters,broken].
true.
?- assert_call_graph.
% Found new meta-predicates in iteration 1 (6.234 sec)
% :- meta_predicate add:add_some_(*,1,*).
% :- meta_predicate prolog:generated_predicate(:).
% :- meta_predicate user:prolog_exception_hook(*,*,*,:).
% Restarting analysis ...
Got 7,991 edges
true.
Save the calls/2 to file calls (meta).txt
?- tell('./calls (meta).txt'), listing(call_graph:calls), told.
File: calls (meta).txt
...
calls(add:add_some(A, B, C), add:add_some_(A, B, C)).
calls(add:add_some_([A|_], B, _), add:call(B, A)).
calls(add:add_some_([A|_], _, B), add:(B=[A|_])).
calls(add:add_some_([_|A], B, _), add:add_some_(A, B, _)).
calls(add:add_all(A, B), add:sum_list(A, B)).
...
calls(filters:less_than_three(A), filters:integer(A)).
calls(filters:less_than_three(A), filters:(A<3)).
...
calls(broken:go, broken:add_some([1, 2, 3, 4, 5], less_than_three, _)).
calls(broken:go, broken:less_than_three(_)).
calls(broken:go, broken:writeln(_)).
...
NB
So adding
:- meta_predicate add_some(-,1,-).
to the module add
the following call at the binary level (think meta predicate at the source level) is created
calls(broken:go, broken:less_than_three(_)).
.
Was hoping gxref/0 could be used for finding such calls created via meta_predicate, but I don’t know if it is possible or just did not find how it is done with gxref/0.
EDIT
trace/0 and gtrace/0 are aware of meta_predicate/1
File: library.pl
:- module(library, [my_call/1]).
:- meta_predicate(my_call(0)).
my_call(Goal) :-
write('Calling: '),
writeq(Goal), nl,
call(Goal).
me(library).
File: client.pl
:- module(client, [
test_1/1
]).
:- use_module(library, [my_call/1]).
me(client).
% Me = client.
test_1(Me) :-
my_call(me(Me)).
[trace] ?- client:test_1(Me).
Call: (10) client:test_1(_23106)
Unify: (10) client:test_1(_23106)
^ Call: (11) library:my_call(me(_23106))
^ Unify: (11) library:my_call(client:me(_23106))
Call: (12) write('Calling: ')
Calling:
Exit: (12) write('Calling: ')
Call: (12) writeq(client:me(_23106))
client:me(_23106)
Exit: (12) writeq(client:me(_23106))
Call: (12) nl
Exit: (12) nl
Call: (12) client:me(_23106)
Unify: (12) client:me(client)
Exit: (12) client:me(client)
^ Exit: (11) library:my_call(client:me(client))
Exit: (10) client:test_1(client)
Me = client.
The lines marked ^
indicate calls to transparent predicates, AKA meta predicates.
See personal note in this post for table of icons used in Call Stack panel.