= Running dynamic call analysis = Dynamic function calls are either dynamic MFA calls (those MFA calls whose callee is given by means of non-literal expressions) or apply calls (calls to the ''erlang:apply/3'' built-in function). For instance, the following calls inside ''f'' are dynamic: {{{ f(IOModule) -> Foo = ["foo"], IOModule:format("foo"), apply(IOModule, format, Foo). }}} These constructs are likely difficult to be statically analysed, since their callee is determined only at execution-time. Fortunately, in many cases static code analysis is able to find potential callees, but unlike other semantic analyses in !RefactorErl, dynamic call analysis would be very inefficient to be performed incrementally. Therefore, static analysis of dynamic function calls has to be triggered by the user on demand. In order to analyse dynamic call constructs, load all the libraries and files you would like to work with. Then type in the !RefactorErl shell: {{{ ri:anal_dyn(). }}} Dynamic call analysis begins and informs you about the process and its progress. Firstly, all function calls are gathered from the database. Secondly, dynamic ones are filtered and passed to the next step, where a parallel algorithm tries to identify the callee of each call. Finally, the results are merged and stored into the database. For instance, executed on ''Erlang stdlib 1.17.4'': {{{ 24012 function calls found in the database. Looking for dynamic calls... 541 function calls seem to be dynamic. Looking up dynamic calls... (533/541) Identification of 8 dynamic calls timed out. 533 dynamic calls identified in the database. Analysing dynamic calls... (533/533) Analysis completed. }}} As you can see in this example, there may be function calls that are not identifiable in a reasonable time (currently the time limit is set to 5 seconds, and there were 8 calls whose analysis has been ignored). This is due to the complexity of some data-flow path analysis. Users may increase the time bound on their own risk in order to make the analysis process able to identify all the callees. '''Please note that most database modifications (e.g. loading or refactoring a module) may invalidate the results of this dynamic call analysis. Currently this invalidation step is not automatically done, so the user has to invoke it. To clean the dynamic calls (and every corresponding entry) from the database, simply call:''' {{{ ri:clean_dyn(). }}} {{{#!comment == Configurable dynamic function analyzer == Dynamic function analyzer was extended to handle user defined functions that has semantics similar to apply BIF. In Erlang it is sometimes preferable to use custom functions that act as a wrapper for a simple apply. For example for load distribution among different Erlang nodes. Or implementing callback functions. === Small example === Consider the following small example. The module which is going to be analyzed. {{{#!erlang -module(dynfun_example). -compile(export_all). a()-> 1. b()-> erlang:apply(?MODULE, a, []). }}} The configuration file, which is located at the tool root, and is called {{{dynfunref.conf}}}: {{{#!erlang { {erlang, apply}, ['$1', '$2', '$3'], [{'$1', '$2', '$3'}] }. }}} The result: {{{#!erlang (refactorerl@localhost)8> ri:reset(). {ok,[]} ok (refactorerl@localhost)9> ri:add("/Users/V/erlang/dynfun_766"). | 1.22 kB/s >>>>>>>>>>>>>>>>>>>| [ 4/ 4] dynfun_example.erl ok (refactorerl@localhost)10> ri:q("mods.funs.dyncalls"). ok (refactorerl@localhost)11> ri:anal_dyn(). Function reference patterns loaded (1, dynfunref.conf) Analysing local funs... (0 done). Collecting function calls... 1 function calls found in the database. Looking for dynamic references... 1 dynamic function references found. Inspecting dynamic references... (1/1) 1 potential callee successfully spotted. Storing dynamic references... (1/1) Updating the call graph... (1/1) Analysis completed. ok (refactorerl@localhost)12> ri:q("mods.funs.dyncalls"). dynfun_example:b/0 dynfun_example:a/0 ok }}} A user-defined references to link from the original function call was made. === How to specify a valid Erlang match specification === {{{#!erlang { {erlang, apply}, %analyzed module and function ['$1', '$2', '$3'], %match [{'$1', '$2', '$3'}] %where to link (an exact function) }. }}} The first element of the tuple is a tuple, which first element is the name of the module in which the analyzed function is located. The name of the function is at the second element. The second element is a list of match specs. Its length equals to the arity of the analyzed function. The third argument is a list of mappings represented by tuples. One element of a mapping is a 3-tuple representing an exact function. The third element is represent arity rather than agrments. === Another example === Consider the following 2 module. {{{#!erlang -module(router). -export([route/2]). route(ReqID, Args)-> [Function | Args0] = tuple_to_list(Args), Master =self(), Worker = fun()-> Result = try apply(router_backend, Function, [ReqID]++Args0) catch _ -> error end, Master ! {ReqID, Result} end, spawn(Worker), receive {ReqID, Result} -> Result end. ex_call()-> route(1, {a}). ex_call2()-> route(2,{b,1}). }}} {{{#!erlang -module(router_backend). -compile(export_all). a(_)-> 2. b(_,A)-> A+1. }}} The configuration file: {{{#!erlang %will link router_backend:a/1 to router:ex_call/0 {{router,route}, ['_', {'$1'}], [ {router_backend, '$1', 1} ]}. %will link router_backend:b/2 to router:ex_call2/0 {{router,route}, ['_', {'$1','_'}], [ {router_backend, '$1', 2} ]}. }}} The result: {{{#!erlang (refactorerl@localhost)26> ri:reset(). {ok,[]} ok (refactorerl@localhost)27> ri:add("/Users/V/erlang/mini_router"). | 1.12 kB/s >>>>>>>>>>>>>>>>>>>| [ 4/ 4] router.erl | 1.18 kB/s >>>>>>>>>>>>>>>>>>>| [ 4/ 4] router_backend.erl ok (refactorerl@localhost)28> ri:anal_dyn(). Function reference patterns loaded (2, dynfunref.conf) Analysing local funs... (1 done). Collecting function calls... 6 function calls found in the database. Looking for dynamic references... 5 dynamic function references found. Inspecting dynamic references... (5/5) 3 potential callee successfully spotted. Storing dynamic references... (3/3) Updating the call graph... (3/3) Analysis completed. ok (refactorerl@localhost)29> ri:q("mods.funs.dyncalls"). router:ex_call/0 router_backend:a/1 router:ex_call2/0 router_backend:b/2 ok }}} A dynamic function reference were made to link {{{router_backend:a/1}}} to {{{router:ex_call/0 }}}, and to link {{{router_backend:b/2}}} to {{{router:ex_call2/0}}}. }}}