wiki:Dependency/Functions

Version 2 (modified by manualwiki, 13 years ago) (diff)

dep.func mod, TODO eliminated, figures added

Module and Function Dependencies

Concerning terminology

We say that a module A is dependent on another module B (A -> B) if there is at least one function call from A to B.
A cyclic dependency appears, when B is also dependent directly (B -> A) or indirectly (e.g. B -> C -> A) from A.
Note that it is possible to have a cyclic dependency among the modules while having no cyclic dependencies among the functions.
For example, a function call from A:foo to B:foo, and from B:foo2 to A:foo2 implies a cyclic dependency on the module level.
If one wants to have a deeper analysis and pays more attention to the concerning functions, a function level query should be done.
In our previous example, no function level cycle appears, unless A:foo calls B:foo, and B:foo calls A:foo.

Possible Analysis

The following examinations can be done considering dependencies:

  • Checking whether there are cycles, if so, listing them out
  • Printing out the cycles, meaning the modules/functions will not be represented by their proper graph node, but with their names (instead {'$gn', module, 3} there will be test).
  • Checking for cycle from one or more nodes as starting points
  • Drawing the dependency graph
  • Drawing the dependency graph from a starting node
  • Drawing the cyclic part of the dependency graph if one exists (you can also give a cyclic node as a starting node)


Dependency analysis can be done through two interfaces:

  1. ri (using RefactorErl? through console interface)
  2. using the web interface.

The two interface functions are:

  1. ri:draw_dep/1 - for drawing
  2. ri:print_dep/1 - for listing, printing out to the standard output




To call the desired query, the user should give a proplist, stating the different requirements.

The options and the keys for the functions are:

  • {level, Level}

Level = mod | func

Stating the level of the query, module or function level.

  • {type, Type}

Type = all | cycles

The investigation should be done on the whole graph/table, or just on the cycle part (if it exists).
When listing out the cycles, all gives back the result in their graph node form, while cycles returns with their proper names.

Examples for listing results

  • Checking for cycles in module level.
    ri:print_dep([{level, mod}, {type, all}]).

  • Checking for cycles in function level, and printing out the whole names of the functions (Module:Function/Arity).
    ri:print_dep([{level, func}, {type, cycles}]).

{6 cycle(s),
[['foo:fv4/1','foo:fv4/1'],
['test3:p/1','test:fv6/1','test3:p/1'],
['cycle4:f4/1','cycle3:f3/1','cycle4:f4/1'],
['cycle2:fv2/1','cycle1:fv1/0','cycle2:fv2/1'],
['test:fv5/1','test:fv4/2','test:fv5/1'],
['cycle4:f5/1','cycle3:f6/1','cycle4:f5/1']]

  • Checking for cycles in function level, and printing out the graph nodes of the functions (notice the changed "type" in the proplist)
    ri:print_dep([{level, func}, {type, all}]).

{"6 cycle(s)",

[[{'$gn',func,28},{'$gn',func,28}],
[{'$gn',func,29},{'$gn',func,37},{'$gn',func,29}],
[{'$gn',func,7},{'$gn',func,9},{'$gn',func,7}],
[{'$gn',func,2},{'$gn',func,1},{'$gn',func,2}],
[{'$gn',func,36},{'$gn',func,35},{'$gn',func,36}],
[{'$gn',func,8},{'$gn',func,6},{'$gn',func,8}]]}

  • Checking for cycles in module level from an exact node
    ri:print_dep([{level, mod}, {gnode, {'$gn', module, 24}}]).

{true,[[{'$gn',module,24},
{'$gn',module,25},
{'$gn',module,24}]]}

  • Checking for cycles in function level from an exact node given with its whole name
    ri:print_dep([{level, func}, {gnode, "cycle4:f5/1"}]).

Representation

Function Level

Let's take an example to explain the meaning of the representation of dependency graphs. A ri:draw_dep([{level, func}, {type, all}]) call was made, which generated a Graphviz dot file.
dep_fun_before

Explanation of the figure:

  • ROOT triangle - no actual purpose, functioning as a starting point, only appears in the representation
  • Rectangle/box nodes (eg.: cycle1, a, test2) - representing modules (colour: deep purple)
  • Hexagon nodes (eg.: f1/1, apply/3, test2/2) - representing functions (colour: black)
  • Double octagon nodes - representing opaque nodes (colour: black, label colour: gray)
  • Solid, continuous edge, normal arrowhead - normal edge indicating the modules from the root, and the

modules and their definitions of functions (colour: black)

  • Dashed edge, normal arrowhead - indicates that a function calls another function (funcall) (colour: black)
  • Dashed edge, special arrowhead - indicated a function call, but also that it is a cyclic edge (colour: red)

The next figure was made after a ri:anal_dyn() was run, which is a dynamic analyser. Due to its work the call graph changes a bit, new types of nodes and edges are introduced.

dep_fun_after

Explanation of figure, new nodes, edges:

  • Double octagon nodes - representing opaque, -1 arity nodes (colour: black, label colour: gray)
  • Dotted edge, normal arrowhead - indicates an ambcall, dyncall, may_be edge (colour: black)

Every node and edge have tooltips. In the case of nodes it shows the adequate graph node, while considering edges it depicts the type of function call (funcall, may_be, ambcall, dyncall). Static calls are labelled by funcall.

Dyncall means unambiguous dynamic calls, when the identifiers of the callee may be defined not at the call but at another program part, and their values are can be calculated by data-flow reaching. Ambcall relation denotes the fact that some of the arguments of the dynamic function call can not be detected statically. If the number of parameters is uncertain, then after the arity of the function will be -1. It is also indicated with a special node. For further details about the dynamic calls and their representation can be found in (wiki:DynamicCallAnalysis). When the applied function or module is uncertain, the tool represents it with an opaque node, and connects the possible functions/modules with may_be edges. The representation in the analysis now follows this, indicating the opaque nodes differently (described in the previous section, concerning drawings).

We note here that tooltips may not be shown under certain browsers (for example Mozilla Firefox 7.0.1). If this happens, please try another browser.

Module Level

Similarly, at module level, after calling ri:draw_dep([{level, mod}, {type, all}]), an analogous figure can be achieved.

dep_mod

Explanation of figure:

  • Rectangle/box nodes (eg.: cycle1, erlang, test2) - representing modules (colour: deep purple)
  • Doubleoctagon nodes (no example on the figure) - representing opaque nodes (colour: black, label colour: gray)
  • Dotted edge, normal arrowhead - indicates that a module calls another module (colour: black)
  • Dotted edge, special arrowhead - indicated a module call, but also that it is a cyclic edge (colour: red)

The tooltip for edges shows a list the function pair:
Call = {callee, called}, Tooltip = [] | [Call | Tooltip]
Latter makes a connection between the modules with this call.

Examples for the representation of the results

  • ri:draw_dep([{level, mod}, {gnode, erlang}]).
  • ri:draw_dep([{level, mod}, {gnode, erlang}, {dot, "/home/working/dot/test.dot" }]).
  • ri:draw_dep([{level, func}, {gnode, "lists:hd/1"}]).
  • ri:draw_dep([{level, mod},{gnode, {'$gn', module, 4}}]).
  • ri:draw_dep([{type, cycles}, {level, func}, {gnode, {'$gn', func, 36}}]).

Attachments (3)

Download all attachments as: .zip