1/*****************************************************************************
    2 * This file is part of the Prolog Development Tool (PDT)
    3 * 
    4 * Author: Andreas Becker
    5 * WWW: http://sewiki.iai.uni-bonn.de/research/pdt/start
    6 * Mail: pdt@lists.iai.uni-bonn.de
    7 * Copyright (C): 2012, CS Dept. III, University of Bonn
    8 * 
    9 * All rights reserved. This program is  made available under the terms
   10 * of the Eclipse Public License v1.0 which accompanies this distribution,
   11 * and is available at http://www.eclipse.org/legal/epl-v10.html
   12 * 
   13 ****************************************************************************/
   14
   15:- module(pdt_call_graph, [ensure_call_graph_generated/0, calls/7, call_type/7, calls_multifile/8, pdt_walk_code/1]).   16
   17:- use_module(pdt_prolog_codewalk).   18:- use_module(library(lists)).   19:- use_module(pdt_prolog_library(compatibility), [
   20	pdt_source_file/2
   21]).   22
   23pdt_walk_code(Options) :-
   24	ensure_call_graph_generated,
   25	pdt_prolog_walk_code(Options).
   26
   27:- dynamic(first_run/0).   28first_run.
   29
   30reset :-
   31	with_mutex(pdt_call_graph, (
   32		(	first_run
   33		->	true
   34		;	assertz(first_run),
   35			retractall(calls_(_, _, _, _, _, _, _, _, _)),
   36			retractall(calls_multifile_(_, _, _, _, _, _, _, _))
   37		)
   38	)).
   39
   40ensure_call_graph_generated :-
   41	with_mutex(pdt_call_graph, (
   42		first_run,
   43		!,
   44		generate_call_graph,
   45		retractall(first_run)
   46	)),
   47	!.
   48ensure_call_graph_generated.
 calls(CalleeModule, CalleeName, CalleeArity, CallerModule, CallerName, CallerArity, NumberOfCalls)
   51calls(CalleeModule, CalleeName, CalleeArity, CallerModule, CallerName, CallerArity, NumberOfCalls) :-
   52	calls_(CalleeModule, CalleeName, CalleeArity, CallerModule, CallerName, CallerArity, NumberOfCalls, _TermPosition, _Info).
   53
   54call_type(CalleeModule, CalleeName, CalleeArity, CallerModule, CallerName, CallerArity, Info) :-
   55	calls_(CalleeModule, CalleeName, CalleeArity, CallerModule, CallerName, CallerArity, _NumberOfCalls, _TermPosition, [Info|_]).
 calls_multifile(CalleeModule, CalleeName, CalleeArity, CallerModule, CallerName, CallerArity, File, NumberOfCalls)
   58calls_multifile(CalleeModule, CalleeName, CalleeArity, CallerModule, CallerName, CallerArity, File, NumberOfCalls) :-
   59	calls_multifile_(CalleeModule, CalleeName, CalleeArity, CallerModule, CallerName, CallerArity, File, NumberOfCalls).
   60
   61:- dynamic(calls_/9).   62:- dynamic(calls_multifile_/8).   63
   64
   65clear([]).
   66clear([Module:Name/Arity|Predicates]) :-
   67	retractall(calls_(_,_,_,Module,Name,Arity,_, _, _)),
   68	retractall(calls_multifile_(_,_,_,Module,Name,Arity,_,_)),
   69	clear(Predicates).
   70
   71:- dynamic(predicates_to_walk/1).   72
   73generate_call_graph :-
   74	with_mutex(pdt_call_graph, generate_call_graph__).
   75
   76generate_call_graph__ :-
   77	pdt_prolog_walk_code([ trace_reference(_),
   78			on_trace(pdt_call_graph:assert_edge),
   79			new_meta_specs(pdt_call_graph:generate_call_graph_new_meta_specs),
   80			reiterate(false),
   81			source(false)
   82			]),
   83	(	predicates_to_walk(NewPredicates)
   84	->	retractall(predicates_to_walk(_)),
   85		clear(NewPredicates),
   86		generate_call_graph__(NewPredicates)
   87	;	true
   88	).
   89
   90generate_call_graph(Predicates) :-
   91	with_mutex(pdt_call_graph, generate_call_graph__(Predicates)).
   92
   93generate_call_graph__(Predicates) :-
   94	pdt_prolog_walk_code([ trace_reference(_),
   95			on_trace(pdt_call_graph:assert_edge),
   96			new_meta_specs(pdt_call_graph:generate_call_graph_new_meta_specs),
   97			reiterate(false),
   98			source(false),
   99			predicates(Predicates)
  100			]),
  101	(	predicates_to_walk(NewPredicates)
  102	->	retractall(predicates_to_walk(_)),
  103		clear(NewPredicates),
  104		generate_call_graph__(NewPredicates)
  105	;	true
  106	).
  107
  108generate_call_graph_new_meta_specs(MetaSpecs) :-
  109	retractall(predicates_to_walk(_)),
  110	findall(Module:Name/Arity, (
  111		member(MetaSpec, MetaSpecs),
  112		pi_of_head(MetaSpec, M, N, A),
  113		calls_(M, N, A, Module, Name, Arity, _, _, _)
  114	), Predicates),
  115	(	Predicates \== []
  116	->	sort(Predicates, PredicatesUnique),
  117		assertz(predicates_to_walk(PredicatesUnique))
  118	;	true
  119	).
  120	
  121assert_edge(M1:Callee, M2:Caller, clause(Ref), Info) :-
  122	assert_edge(M1:Callee, M2:Caller, clause_term_position(Ref, undefined), Info).
  123	
  124assert_edge(M1:Callee, M2:Caller, clause_term_position(Ref, TermPosition), Info) :-
  125	functor(Callee,F1,N1),
  126	(	predicate_property(M1:Callee, imported_from(M0))
  127	->	M = M0
  128	;	M = M1
  129	),
  130	functor(Caller,F2,N2), 
  131	assert_edge_(M,F1,N1, M2,F2,N2,TermPosition,Info),
  132	(	predicate_property(M2:Caller, multifile),
  133		clause_property(Ref, file(File))
  134	->	assert_multifile_edge(M,F1,N1, M2,F2,N2, File)
  135	;	true
  136	).
  137assert_edge(_, '<initialization>', _, _) :- !.
  138
  139assert_edge_(M1,F1,N1, M2,F2,N2,TermPos,Info) :-
  140	retract( calls_(M1,F1,N1, M2,F2,N2, Counter, TermPosTail, InfoTail) ), 
  141	!,
  142	Cnt_plus_1 is Counter + 1,
  143	assertz(calls_(M1,F1,N1, M2,F2,N2, Cnt_plus_1, [TermPos|TermPosTail], [Info|InfoTail])).
  144assert_edge_(M1,F1,N1, M2,F2,N2, TermPos, Info) :-
  145	assertz(calls_(M1,F1,N1, M2,F2,N2, 1, [TermPos], [Info])).
  146
  147assert_multifile_edge(M1,F1,N1, M2,F2,N2, File) :-
  148	retract( calls_multifile_(M1,F1,N1, M2,F2,N2, File, Counter) ), 
  149	!,
  150	Cnt_plus_1 is Counter + 1,
  151	assertz(calls_multifile_(M1,F1,N1, M2,F2,N2, File, Cnt_plus_1)).
  152assert_multifile_edge(M1,F1,N1, M2,F2,N2, File) :-
  153	assertz(calls_multifile_(M1,F1,N1, M2,F2,N2, File, 1)).
  154
  155:- multifile(pdt_reload:pdt_reload_listener/1).  156pdt_reload:pdt_reload_listener(_Files) :-
  157	(	first_run
  158	->	true
  159	;	setof(Module:Name/Arity, Head^File^FileModule^(
  160			(	pdt_reload:reloaded_file(File),
  161				(	pdt_source_file(Module:Head, File)
  162				;	module_property(FileModule, file(File)),
  163					predicate_property(FileModule:Head, imported_from(Module)),
  164					predicate_property(Module:Head, transparent),
  165					\+ predicate_property(Module:Head, meta_predicate(_))
  166				),
  167				functor(Head, Name, Arity)
  168			;	retract(predicate_to_clear(Module, Name, Arity))
  169			)
  170		), Predicates),
  171		!,
  172		clear(Predicates),
  173		generate_call_graph(Predicates)
  174	).
  175
  176:- multifile(user:message_hook/3).  177:- dynamic(user:message_hook/3).  178user:message_hook(load_file(start(_, file(_, File))),_,_) :-
  179	\+ first_run,
  180	(	pdt_source_file(Module:Head, File)
  181	;	module_property(FileModule, file(File)),
  182		predicate_property(FileModule:Head, imported_from(Module)),
  183		predicate_property(Module:Head, transparent),
  184		\+ predicate_property(Module:Head, meta_predicate(_))
  185	),
  186	functor(Head, Name, Arity),
  187	\+ predicate_to_clear(Module, Name, Arity),
  188	assertz(predicate_to_clear(Module, Name, Arity)),
  189	fail.
  190
  191pi_of_head(Module:Head, Module, Name, Arity) :-
  192	functor(Head, Name, Arity).
  193
  194:- dynamic(predicate_to_clear/3).