tools

v4.1.1

Tools Release Notes

This document describes the changes made to the Tools application.

Tools 4.1.1

Fixed Bugs and Malfunctions

Fixed some deprecated errors on emacs-29.

Own Id: OTP-19273 Aux Id: PR-8879

The cover tool could sometimes wrongly report lines as uncovered.

Own Id: OTP-19289 Aux Id: GH-8867, PR-8919

Fixed tprof:format(IoDevice, ...) to not demand unicode encoding supported by IoDevice.

Own Id: OTP-19299 Aux Id: PR-8949

Tools 4.1

Fixed Bugs and Malfunctions

tprof no longer crashes when using pause/restart/continue when profiling all modules.

Own Id: OTP-19136 Aux Id: GH-8472, PR-8472, PR-8541

On systems supporting native coverage, calls to cover could hang or crash if cover-compiled module had been reloaded from outside cover. This has been corrected so that cover now recovers from the error and and sends a report to the logger about the failure to retrieve coverage information.

Own Id: OTP-19203 Aux Id: GH-8661, PR-8742

Improvements and New Features

Figures in the documentation have been improved.

Own Id: OTP-19130 Aux Id: PR-7226

Tools 4.0

Fixed Bugs and Malfunctions

Dialyzer warnings due to type specs added in dbg have been eliminated.

Own Id: OTP-18860

In Erlang/OTP 26, doing a cover analysis on the line level would return multiple entries for lines on which multiple functions were defined.

For example, consider this module:

-module(foo).
-export([bar/0, baz/0]).

bar() -> ok. baz() -> not_ok.

In Erlang/OTP 26, analysing on the line level would return two entries for line 4:

1> cover:compile_module(foo).
{ok,foo}
2> foo:bar().
ok
3> cover:analyse(foo, coverage, line).
{ok,[{{foo,4},{1,0}},{{foo,4},{0,1}}]}
4> cover:analyse(foo, calls, line).
{ok,[{{foo,4},1},{{foo,4},0}]}

In Erlang/OTP 27, there will only be a single entry for line 4:

1> cover:compile_module(foo).
{ok,foo}
2> foo:bar().
ok
3> cover:analyse(foo, coverage, line).
{ok,[{{foo,4},{1,0}}]}
4> cover:analyse(foo, calls, line).
{ok,[{{foo,4},1}]}

Own Id: OTP-18998 Aux Id: GH-8159, PR-8182

Fixed align command in emacs mode.

Own Id: OTP-19026 Aux Id: PR-8155

Improvements and New Features

Triple-Quoted Strings has been implemented as per EEP 64. See String in the Reference Manual.

Example:

1> """
   a
   b
   c
   """.
"a\nb\nc"

Adjacent string literals without intervening white space is now a syntax error, to avoid possible confusion with triple-quoted strings. For example:

1> "abc""xyz".
"xyz".
* 1:6: adjacent string literals without intervening white space

POTENTIAL INCOMPATIBILITY

Own Id: OTP-18750 Aux Id: OTP-18746, PR-7313, PR-7451

There is a new tool tprof, which combines the functionality of eprof and cprof under one interface and adds heap profiling. It also has functionality to help with profiling process hierarchies.

Example:

1> tprof:profile(lists, seq, [1, 16], #{type => call_memory}).

****** Process <0.92.0>  --  100.00% of total ***
FUNCTION          CALLS  WORDS  PER CALL  [     %]
lists:seq_loop/3      5     32      6.40  [100.00]
                            32            [ 100.0]
ok

Own Id: OTP-18756 Aux Id: PR-6639

Native coverage support has been implemented in the JIT. It will automatically be used by the cover tool to reduce the execution overhead when running cover-compiled code.

There are also new APIs to support native coverage without using the cover tool.

To instrument code for native coverage it must be compiled with the line_coverage option.

To enable native coverage in the runtime system, start it like so:

$ erl +JPcover true

There are also the following new functions for supporting native coverage:

code:coverage_support/0

code:get_coverage/2

code:reset_coverage/1

code:get_coverage_mode/0

code:get_coverage_mode/1

code:set_coverage_mode/1

Own Id: OTP-18856 Aux Id: PR-7856

The documentation has been migrated to use Markdown and ExDoc.

Own Id: OTP-18955 Aux Id: PR-8026

Improved the align command in emacs mode.

Own Id: OTP-19080 Aux Id: PR-8288

Tools 3.6

Improvements and New Features

Map comprehensions as suggested in EEP 58 has now been implemented.

Own Id: OTP-18413 Aux Id: EEP-58, PR-6727

The instrument module has been moved from tools to runtime_tools.

Own Id: OTP-18487 Aux Id: PR-6829

Tools 3.5.3

Improvements and New Features

Removed the previously undocumented and unsupported emem tool.

Own Id: OTP-17892 Aux Id: PR-5591

Tools 3.5.2

Fixed Bugs and Malfunctions

Erlang-mode fixed for newer versions of xref using CL-Lib structures instead of EIEIO classes.

Own Id: OTP-17746 Aux Id: GH-5314, PR-5324

Tools 3.5.1

Fixed Bugs and Malfunctions

The cover tool would not work on modules compiled with the tuple_calls option.

Own Id: OTP-17440 Aux Id: GH-4796

Tools 3.5

Fixed Bugs and Malfunctions

For cover-compiled code, the error behaviour of list and binary comprehensions that used andalso/orelse in guards could be changed so that a filter that was supposed be evaluated in guard context was evaluated in body context. That is, there was a possibility that comprehensions that did not raise exceptions could raise exceptions when being run using cover.

Own Id: OTP-17221 Aux Id: PR-4547

Improvements and New Features

Support for handling abstract code created before OTP R15 has been dropped.

Own Id: OTP-16678 Aux Id: PR-2627

Add types and specifications for documentation.

Own Id: OTP-16957

The experimental HiPE application has been removed, together with all related functionality in other applications.

* POTENTIAL INCOMPATIBILITY *

Own Id: OTP-16963

Tools 3.4.4

Fixed Bugs and Malfunctions

cover would crash when compiling a module having an exported function named clauses.

Own Id: OTP-17162 Aux Id: GH-4549, PR-2997, PR-4555, elixir-lang/elixir#10666

If beam_lib is asked to return abstract code for a BEAM file produced by Elixir and Elixir is not installed on the computer, beam_lib will no longer crash, but will return an error tuple. The cover:compile_beam() and cover:compile_beam_directory() functions have been updated to also return an error tuple in that situation.

Own Id: OTP-17194 Aux Id: GH-4353

Make emacs mode work on emacs-27.

Own Id: OTP-17225 Aux Id: PR-4542, GH-4451

Tools 3.4.3

Fixed Bugs and Malfunctions

Correct the Xref analysis undefined_functions to not report internally generated behaviour_info/1.

Own Id: OTP-17191 Aux Id: OTP-16922, ERL-1476, GH-4192

Tools 3.4.2

Fixed Bugs and Malfunctions

Correct the Xref analysis exports_not_used to not report internally generated behaviour_info/1.

Own Id: OTP-16922 Aux Id: PR-2752

Tools 3.4.1

Fixed Bugs and Malfunctions

Correct the Xref analysis locals_not_used to find functions called exclusively from on_load functions.

Own Id: OTP-16854 Aux Id: PR-2750

Tools 3.4

Improvements and New Features

Updates for new erlang:term_to_iovec() BIF.

Own Id: OTP-16128 Aux Id: OTP-15618

Improved the presentation of allocations and carriers in the instrument module.

Own Id: OTP-16327

Minor updates due to the new spawn improvements made.

Own Id: OTP-16368 Aux Id: OTP-15251

Tools 3.3.1.1

Fixed Bugs and Malfunctions

cover would crash when compiling a module having an exported function named clauses.

Own Id: OTP-17162 Aux Id: GH-4549, PR-2997, PR-4555, elixir-lang/elixir#10666

Tools 3.3.1

Fixed Bugs and Malfunctions

An Emacs warning due to lacking type in defcustom declaration has been fixed.

Own Id: OTP-16356

Improve emacs indentation.

Own Id: OTP-16472 Aux Id: ERL-1140

The cover tool could generate instrumented code for a module that would cause warnings to be issued.

Own Id: OTP-16476 Aux Id: ERL-1147

Fixed generated fprof analysis format to also handle data in maps.

Own Id: OTP-16498 Aux Id: ERL-814

Tools 3.3

Fixed Bugs and Malfunctions

Improve -spec indentation in emacs mode.

Own Id: OTP-16164

Improvements and New Features

The Emacs erlang-mode function that lets the user open the documentation for an Erlang/OTP function in an Emacs buffer has been improved. Bugs in this function has been fixed and and the user will now be asked if the man pages should be downloaded automatically by Emacs when they can't be found on the system. To test this functionality, put the cursor over the function name in a call to an Erlang/OTP function (e.g., "io:format("arg")") and type C-c C-d (i.e., Ctrl-key and c-key and then Ctrl-key and d-key). There is also a new menu item under the Erlang menu (labeled "Man - Function Under Cursor").

Own Id: OTP-16174

Tools 3.2.1

Fixed Bugs and Malfunctions

cover would fail to start if two processes tried to start it at the exact same time.

Own Id: OTP-15813 Aux Id: ERL-943

Tools 3.2

Fixed Bugs and Malfunctions

Add cprof and tags modules to .app file so that they are included in releases.

Own Id: OTP-15534 Aux Id: PR-2078

Improved documentation parsing in emacs erldoc functionality.

Own Id: OTP-15699 Aux Id: PR-2184

Improvements and New Features

The cover tool now uses the counters module instead of ets for updating the counters for how many times a line has been executed. By default, Cover will work with distributed nodes, but a new function cover:local_only/0 allows running the Cover in a restricted but faster local-only mode.

The increase in speed will vary depending on the type of code being cover-compiled, but as an example, the compiler test suite runs more than twice as fast with the new Cover.

Own Id: OTP-15575

Tools 3.1.0.1

Fixed Bugs and Malfunctions

cover would fail to start if two processes tried to start it at the exact same time.

Own Id: OTP-15813 Aux Id: ERL-943

Tools 3.1

Fixed Bugs and Malfunctions

Minor fixes for make clean.

Own Id: OTP-15657

Improvements and New Features

In the HTML file generated by cover:analyse_to_file/1,2, a link is now added to the line number. This makes it easier to share pointers to specific lines.

Own Id: OTP-15541

Uncovered lines are now marked with a sad face, :-(, in the HTML output from cover:analyse_to_file/1,2. This is to make these lines easier to find by search.

Own Id: OTP-15542

Tools 3.0.2

Improvements and New Features

Remove emacs warnings and added more tests.

Own Id: OTP-15476

Tools 3.0.1

Improvements and New Features

The HTML pages generated by cover:analyse_to_file/1 and related functions is improved for readability.

Own Id: OTP-15213 Aux Id: PR-1807

Add alignment functionality in emacs.

Own Id: OTP-15239 Aux Id: PR-1728

Tools 3.0

Improvements and New Features

Added instrument:allocations and instrument:carriers for retrieving information about memory utilization and fragmentation.

The old instrument interface has been removed, as have the related options +Mim and +Mis.

* POTENTIAL INCOMPATIBILITY *

Own Id: OTP-14961

Tools 2.11.2.2

Fixed Bugs and Malfunctions

cover would fail to start if two processes tried to start it at the exact same time.

Own Id: OTP-15813 Aux Id: ERL-943

Tools 2.11.2.1

Fixed Bugs and Malfunctions

Minor fixes for make clean.

Own Id: OTP-15657

Tools 2.11.2

Fixed Bugs and Malfunctions

A counting bug is corrected in Cover. The bug was introduced in Erlang/OTP 18.0.

Own Id: OTP-14817 Aux Id: PR 1641

The lcnt server will no longer crash if lcnt:information/0 is called before lcnt:collect/0.

Own Id: OTP-14912

lcnt:collect will now implicitly start the lcnt server, as per the documentation.

Own Id: OTP-14913

Improvements and New Features

Improved indentation in emacs and various other updates.

Own Id: OTP-14944

Tools 2.11.1

Fixed Bugs and Malfunctions

Removed all old unused files in the documentation.

Own Id: OTP-14475 Aux Id: ERL-409, PR-1493

Tools 2.11

Fixed Bugs and Malfunctions

The predefined Xref analysis locals_not_used no longer reports unused functions with the -on_load() attribute.

The new predefined Xref variable OL holds all functions with the -on_load() attribute.

Own Id: OTP-14344

In fprof when sampling multiple processes and analyzing with totals set to true, the output now sums together all caller and callee entries which concerns the same function. Previous behaviour was to report each contributing entry separately.

Own Id: OTP-14500

Improvements and New Features

Lock counting can now be fully toggled at runtime in the lock counting emulator (-emu_type lcnt). Everything is enabled by default to match the old behavior, but specific categories can be toggled at will with minimal runtime overhead when disabled. Refer to the documentation on lcnt:rt_mask/1 for details.

Own Id: OTP-13170

lcnt:collect and lcnt:clear will no longer block all other threads in the runtime system.

Own Id: OTP-14412

General Unicode improvements.

Own Id: OTP-14462

Tools are updated to show Unicode atoms correctly.

Own Id: OTP-14464

Add erlang:iolist_to_iovec/1, which converts an iolist() to an erlang:iovec(), which suitable for use with enif_inspect_iovec.

Own Id: OTP-14520

Tools 2.10.1

Fixed Bugs and Malfunctions

In OTP-20.0, the behavior of c, make, and ct_make was changed so that in some cases the beam files by default would be written to the directory where the source files were found. This is now changed back to the old behavior so beam files are by default written to current directory.

Own Id: OTP-14489 Aux Id: ERL-438

Tools 2.10

Fixed Bugs and Malfunctions

In some situations, make:all() and friends did not detect changes in include files located in the current directory. This is now corrected.

Own Id: OTP-14339 Aux Id: ERL-395

Improvements and New Features

The make module now accepts the {emake,Emake} option.

Own Id: OTP-14253

Miscellaneous updates due to atoms containing arbitrary Unicode characters.

Own Id: OTP-14285

Tools 2.9.1

Improvements and New Features

Improved edoc support in emacs mode.

Own Id: OTP-14217 Aux Id: PR-1282

Tools 2.9

Fixed Bugs and Malfunctions

Fix unhandled trace event send_to_non_existing_process in fprof.

Own Id: OTP-13998

Improvements and New Features

Improved edoc support in emacs erlang-mode.

Own Id: OTP-13945 Aux Id: PR-1157

Added erldoc to emacs mode which opens html documentation in browser from emacs. For example M-x erldoc-browse RET lists:foreach/2.

Own Id: OTP-14018 Aux Id: PR-1197

Tools 2.8.6

Fixed Bugs and Malfunctions

Errors in type specification and Emacs template generation for gen_statem:code_change/4 has been fixed from bugs.erlang.org's Jira cases ERL-172 and ERL-187.

Own Id: OTP-13746 Aux Id: ERL-172, ERL-187

Fix gc_start/gc_end in fprof tags when parsing old trace logs.

Own Id: OTP-13778 Aux Id: PR-1136

make (tools) and ct_make (common_test) would crash if an Erlang source file contained a -warning() directive.

Own Id: OTP-13855

Tools 2.8.5

Fixed Bugs and Malfunctions

Correct a bug when adding multiple modules to an Xref server. The bug was introduced in OTP-19.0.

Own Id: OTP-13708 Aux Id: ERL-173

Tools 2.8.4

Fixed Bugs and Malfunctions

Update fprof to use the new 'spawned' trace event to determine when a process has been created.

Own Id: OTP-13499

Improvements and New Features

Optimize adding multiple modules to an Xref server.

Own Id: OTP-13593

Various emacs mode improvements, such as better tags support.

Own Id: OTP-13610

Tools 2.8.3

Fixed Bugs and Malfunctions

cover:compile_beam/1 and cover:compile_beam_directory/1,2 crashed when trying to compile a beam file without a 'file' attribute. This has been corrected and an error is returned instead.

Thanks to Louis-Philippe Gauthier for reporting this bug.

Own Id: OTP-13200

Fix a bit string comprehension bug in Cover.

Own Id: OTP-13277 Aux Id: PR 856

Tools 2.8.2

Fixed Bugs and Malfunctions

The emacs mode does not add a newline after the arrow on -callback lines anymore.

Own Id: OTP-13042

Tools 2.8.1

Fixed Bugs and Malfunctions

If a module includes eunit.hrl, a parse transform adds the function test/0 on line 0 in the module. A bug in OTP-18.0 caused cover:analyse_to_file/1 to fail to insert cover data in the output file when line 0 existed in the cover data table. This is now corrected.

Own Id: OTP-12981

Tools 2.8

Fixed Bugs and Malfunctions

In order to improve performance of the cover tool, new functions are added for cover compilation and analysis on multiple files. This allows for more parallelisation.

Some improvements of the data base access is also done in order to improve the performance when analysing and resetting cover data.

Minor incompatibility: An error reason from analyse_to_file is changed from no_source_code_found to {no_source_code_found,Module}.

* POTENTIAL INCOMPATIBILITY *

Own Id: OTP-12330 Aux Id: seq12757

Attempting to do a cover analysis when neither source code nor beam file could be found would hang the cover server. Corrected to return a proper error.

Own Id: OTP-12806

Improvements and New Features

Allow maps for supervisor flags and child specs

Earlier, supervisor flags and child specs were given as tuples. While this is kept for backwards compatibility, it is now also allowed to give these parameters as maps, see sup_flags and child_spec.

Own Id: OTP-11043

Remove Mnemosyne rules support.

Own Id: OTP-12511

Add printout of total number of calls and time in eprof

Own Id: OTP-12681

Tools 2.7.2

Fixed Bugs and Malfunctions

Fix lcnt sorting and printout of histograms.

Own Id: OTP-12364

Fix a Unicode bug in the tags module.

Own Id: OTP-12567

Fix tags completion in erlang.el for GNU Emacs 23+

Own Id: OTP-12583

Tools 2.7.1

Fixed Bugs and Malfunctions

Fixed a typo in erlang-mode comment.

Own Id: OTP-12214

Add a skeleton for -spec in Erlang mode for Emacs

Own Id: OTP-12283

Improvements and New Features

Cover no longer crashes when compiling receive and the like with just an after clause. Thanks to José Valim for providing a fix.

Own Id: OTP-12328

Tools 2.7

Improvements and New Features

Add log2 histogram to lcnt for lock wait time

Own Id: OTP-12059

Tools 2.6.15

Fixed Bugs and Malfunctions

Removed erlang:bitstr_to_list/1 and erlang:list_to_bitstr/1. They were added by mistake, and have always raised an undefined exception when called.

Own Id: OTP-11942

Tools 2.6.14

Fixed Bugs and Malfunctions

Removed the support for the query keyword from emacs mode (Thanks to Paul Oliver)

Own Id: OTP-11568

Emacs mode improvements (Thanks to Steve Vinoski)

Own Id: OTP-11601

Application upgrade (appup) files are corrected for the following applications:

asn1, common_test, compiler, crypto, debugger, dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe, inets, observer, odbc, os_mon, otp_mibs, parsetools, percept, public_key, reltool, runtime_tools, ssh, syntax_tools, test_server, tools, typer, webtool, wx, xmerl

A new test utility for testing appup files is added to test_server. This is now used by most applications in OTP.

(Thanks to Tobias Schlager)

Own Id: OTP-11744

The emacs erlang mode now match erlang keywords more carefully (Thanks to Steve Vinoski)

Own Id: OTP-11786

The emacs erlang-mode now auto loads for more file types (Thanks to Phil Hagelberg)

Own Id: OTP-11788

Improvements and New Features

cover can run on itself. Also, support for reading BEAM files produced by ancient OTP versions before R9C has been removed.

Own Id: OTP-11692

Support maps in cover

Own Id: OTP-11764

Tools 2.6.13

Fixed Bugs and Malfunctions

Erlang-specific compilation error regexp is added in erlang-eunit.el. This defvar was earlier in erlang.el, but was erroneously removed in R15B02, while still used by erlang-eunit.el.

Own Id: OTP-11417 Aux Id: seq12447

Take compiler options from beam in cover:compile_beam. Thanks to Péter Gömöri.

Own Id: OTP-11439

Silence warnings (Thanks to Anthony Ramine)

Own Id: OTP-11517

Improvements and New Features

Add iodata, nonempty_string to built-in type highlighting for emacs. Thanks to Paul Oliver.

Own Id: OTP-11394

Tools 2.6.12

Improvements and New Features

Remove trailing spaces in Emacs templates. Thanks to Roberto Aloi.

Own Id: OTP-11198

Fixed the Emacs erlang-mode to accommodate the coding style where lists written across several lines have each line starting with a comma. Thanks to Magnus Henoch.

Own Id: OTP-11242

Make the Emacs Erlang mode TRAMP-aware when compiling. Thanks to Tomas Abrahamsson.

Own Id: OTP-11270

Tools 2.6.11

Fixed Bugs and Malfunctions

When cover:stop(Node) was called on a non-existing node, a process waiting for cover data from the node would hang forever. This has been corrected.

Own Id: OTP-10979

Improvements and New Features

Make cover smarter about finding source from beam.

In particular, search using the source path in module_info if the current heuristic fails.

Own Id: OTP-10902

Remove Flymake dependency in erlang-pkg.el. Thanks to Magnus Henoch.

Own Id: OTP-10930

Erlang-mode: Add autoload cookies for file extension associations. Thanks to Magnus Henoch.

Own Id: OTP-10999

Postscript files no longer needed for the generation of PDF files have been removed.

Own Id: OTP-11016

Fix a race condition when there're several applications in apps directory. Thanks to Manuel Rubio.

Own Id: OTP-11028

New option for eprof, 'set_on_spawn'. This option was previously always on and is also the default.

Own Id: OTP-11144

Tools 2.6.10

Improvements and New Features

Fix a bug in cover when used with no_auto_import. Thanks to José Valim.

Own Id: OTP-10778

Tools 2.6.9

Fixed Bugs and Malfunctions

Add missing modules in app-file

Own Id: OTP-10439

Make erlang-mode more compatible with package.el (Thanks to Gleb Peregud)

Own Id: OTP-10465

Fix various typos (thanks to Tuncer Ayaz)

Own Id: OTP-10611

Add separate face for exported functions (Thanks to Thomas Järvstrand)

Own Id: OTP-10637

The BIF highlighting in the emacs mode has been updated to correspond with the correct BIFs.

Own Id: OTP-10774

Improvements and New Features

Support for Unicode has been implemented.

Own Id: OTP-10302

A new function, cover:flush(Nodes), is added which will fetch data from remote nodes without stopping cover on those nodes. This is used by test_server and common_test when it is safe to assume that the node will be terminated after the test anyway. The purpose is to avoid processes crashing when re-loading the original beam if the processes is still running old code.

Remote nodes will now continue to count code coverage if the connection to the main node is broken. Earlier, a broken connection would cause the cover_server on the remote node to die and thus any still cover compiled modules would cause process crash when trying to insert cover data in ets tables that used to exist on the cover_server. The new functionality also involves synchronization with the main node if the nodes are reconnected.

* POTENTIAL INCOMPATIBILITY *

Own Id: OTP-10523 Aux Id: OTP-10427

Where necessary a comment stating encoding has been added to Erlang files. The comment is meant to be removed in Erlang/OTP R17B when UTF-8 becomes the default encoding.

Own Id: OTP-10630

Fix syntax highlighting of $\' in Emacs mode. Thanks to Magnus Henoch.

Own Id: OTP-10766

Tools 2.6.8

Fixed Bugs and Malfunctions

The last tuple fun call has been removed from fprof.

Own Id: OTP-10091 Aux Id: seq12067

Fix indentation of record fields in Emacs (Thanks to Tomas Abrahamsson)

Own Id: OTP-10120

Documentation fixes (Thanks to Ricardo Catalinas Jiménez )

Own Id: OTP-10121

Remove Erlang-specific compilation error regexp in erlang.el

Own Id: OTP-10168

Fix highlighting of atoms ending with a dollar sign

Like this: 'atom$'. In that example, the last single quote should be recognised as ending the atom. This needs a font-lock workaround similar to the one for strings. Thanks to Magnus Henoch

Own Id: OTP-10178

Xref now accepts filenames with character codes greater than 126. (Thanks to Emile Joubert for reporting the issue.)

Own Id: OTP-10192

Add test_indentation target to lib/tools/emacs/Makefile

Automatically indent test.erl.orig, save to test.erl, and compare to test.erl.intended. Thanks to Magnus Henoch.

Own Id: OTP-10226

Tools 2.6.7

Fixed Bugs and Malfunctions

Makefiles in erts, hipe and tools have been corrected to enable parallel make, i.e MAKEFLAGS=-jX where X is the parallelity number. As a result of this dependencies were corrected since that is what is needed for parallel make to work.

Own Id: OTP-9857 Aux Id: OTP-9451

Minor suppressions and fixes of compilation warnings

Own Id: OTP-10016

Tools 2.6.6.6

Fixed Bugs and Malfunctions

Update system profiling principles to reflect eprof performance improvements.

Own Id: OTP-9656

[cover] fix leftover {'DOWN', ..} msg in callers queue

After stopping cover with cover:stop() there could still be a {'DOWN',...} leftover message in the calling process's message queue. This unexpected leftover could be eliminated if erlang:demonitor/2 with option flush would be used in certain points

Own Id: OTP-9694

Add deps as erlang-flymake include directory.

Update erlang-flymake to recognize the "deps" folder as an include directory. This makes erlang-flymake compatible with the rebar dependency management tool's default folder structure, which puts included dependencies in "deps".(Thanks to Kevin Albrecht)

Own Id: OTP-9791

Improvements and New Features

Variables are now now allowed in 'fun M:F/A' as suggested by Richard O'Keefe in EEP-23.

The representation of 'fun M:F/A' in the abstract format has been changed in an incompatible way. Tools that directly read or manipulate the abstract format (such as parse transforms) may need to be updated. The compiler can handle both the new and the old format (i.e. extracting the abstract format from a pre-R15 BEAM file and compiling it using compile:forms/1,2 will work). The syntax_tools application can also handle both formats.

* POTENTIAL INCOMPATIBILITY *

Own Id: OTP-9643

Tuple funs (a two-element tuple with a module name and a function) are now officially deprecated and will be removed in R16. Use 'fun M:F/A' instead. To make you aware that your system uses tuple funs, the very first time a tuple fun is applied, a warning will be sent to the error logger.

Own Id: OTP-9649

Eliminate use of deprecated regexp module

Own Id: OTP-9810

Tools 2.6.6.5

Fixed Bugs and Malfunctions

Teach the emacs mode to compile yecc and leex files

If visiting a .yrl or .xrl file in emacs with erlang-mode, then the `erlang-compile' function (normally bound to C-c C-k), now knows how to compile yecc and leex files, and then, if that compilation succeeds, also compiles the resulting .erl files.

Also introduce a `erlang-compile-command-function-alist' to make it possible to hook in other functions for computing compilation commands/expressions, depending on file name. (Thanks to Tomas Abrahamsson )

Own Id: OTP-9503

Improvements and New Features

Bugs in xref(3) have been fixed. (Thanks to Matthias Lang.)

Own Id: OTP-9416

Tools 2.6.6.4

Fixed Bugs and Malfunctions

Change make:files to behave more like erlc

This change removes the unnecessary checks on the files when make:files is called and allows the error checking to be done in compile:file, where the error messages are produced. It does not affect the return value.

(Thanks to Sam bobroff)

Own Id: OTP-9179

add user specified compiler options on form reloading

In order to be able to test non-exported functions from another (test) module it is necessary to compile the specific module (at least during the test phase) with the exportall compiler option. This allows complete separation of testing and productive code. At the moment it is not possible to combine this with a test code coverage using the cover module. The problem is that when cover compiling a module using cover:compile* the code is reloaded into the emulator omitting/filtering the passed user options. In my example above the export_all option would be removed and the non-exported functions cannot be called any more. (Thanks to Tobias Schlager)

Own Id: OTP-9204

Inhibit electric newline after "->" when inside a type spec

The Erlang mode for Emacs inserts a newline after every "->", which saves you one keystroke when writing a function, but that is inappropriate when writing a type spec, as you'd normally keep the spec on one line. This change inhibits the automatic insertion when the current line starts with "-spec" or "-type".(Thanks to Magnus Henoch)

Own Id: OTP-9255

Add a check logic to prevent file descriptor leak

cover module handle files as raw in export and import. Assert counts of ports are the same at the beginning and at the end of the test case.(Thanks to Shunichi Shinohara)

Own Id: OTP-9300

Tools 2.6.6.3

Fixed Bugs and Malfunctions

Declare indentation options as "safe" in erlang-mode for Emacs

Emacs has a facility for setting options on a per-file basis based on comments in the source file. By default, all options are considered "unsafe", and the user is queried before the variable is set. This patch declares the variables erlang-indent-level, erlang-indent-guard and erlang-argument-indent to be safe, if the value specified in the source file is valid.

Such declarations usually look like this:

%% -- erlang-indent-level: 2 --

and appear on the first line of the file. (thanks to Magnus Henoch)

Own Id: OTP-9122

Improvements and New Features

Cover has been improved to take less memory and allow parallel analysis of cover data. Data collection from nodes is now done in parallel and it is now possible to issue multiple analyse and analyse_to_file requests at the same time. A new function call async_analyse_to_file has also been introduced, see the documentation for more details.

Own Id: OTP-9043 Aux Id: seq11771

Tools 2.6.6.2

Fixed Bugs and Malfunctions

eprof: API sort mismatch has now been fixed.

Own Id: OTP-8853

eprof: fix division by zero in statistics

Own Id: OTP-8963

Tools 2.6.6.1

Fixed Bugs and Malfunctions

cover will now show ampersand characters in the source code correctly. (Thanks to Tom Moertel.)

Own Id: OTP-8776

Tools 2.6.6

Fixed Bugs and Malfunctions

A race condition affecting Cover has been removed.

Own Id: OTP-8469

Emacs improvements:

Fixed emacs-mode installation problems.

Fixed a couple of -spec and -type indentation and font-lock problems.

Fixed error messages on emacs-21.

Magnus Henoch fixed several issues.

Ralf Doering, Klas Johansson and Chris Bernard contributed various emacs-eunit improvements.

Klas Johansson and Dave Peticolas added emacs-flymake support.

Own Id: OTP-8530

Improvements and New Features

Xref has been updated to use the re module instead of the deprecated regexp module.

* POTENTIAL INCOMPATIBILITY *

Own Id: OTP-8472

When given the option {builtins,true} Xref now adds calls to operators.

Own Id: OTP-8647

eprof has been reimplemented with support in the Erlang virtual machine and is now both faster (i.e. slows down the code being measured less) and scales much better. In measurements we saw speed-ups compared to the old eprof ranging from 6 times (for sequential code that only uses one scheduler/core) up to 84 times (for parallel code that uses 8 cores).

Note: The API for the eprof has been cleaned up and extended. See the documentation.

* POTENTIAL INCOMPATIBILITY *

Own Id: OTP-8706

Tools 2.6.5.1

Fixed Bugs and Malfunctions

A bug concerning bit comprehensions has been fixed in Cover. The bug was introduced in R13B03. (Thanks to Matthew Sackman.)

Own Id: OTP-8340

Improvements and New Features

Add lock profiling tool.

The Lock profiling tool, lcnt, can make use of the internal lock statistics when the runtime system is built with this feature enabled.

This provides a mechanism to examine potential lock bottlenecks within the runtime itself.

- Add erts_debug:lock_counters({copy_save, bool()}). This option enables or disables statistics saving for destroyed processes and ets-tables. Enabling this might consume a lot of memory.

- Add id-numbering for lock classes which is otherwise undefined.

Own Id: OTP-8424

emacs: Moved code skeletons to a separate file and and added a configurable variable to choose skeleton. Thanks Dave Peticolas.

Own Id: OTP-8446

Tools 2.6.5

Fixed Bugs and Malfunctions

The coverage analysis tool cover has been improved when it comes to handling list and bit string comprehensions (a counter for each qualifier), bit syntax expressions (the Value and Size expressions), and try expressions (the body called Exprs in the Reference Manual). A few (not all) situations where several expressions are put on the same line are also handled better than before.

Own Id: OTP-8188 Aux Id: seq11397

When loading Cover compiled code on remote nodes running code in the loaded module, a badarg failure was sometimes the result. This bug has been fixed.

Own Id: OTP-8270 Aux Id: seq11423

The short-circuit operators andalso and orelse are now handled correctly by the coverage analysis tool cover (it is no longer checked that the second argument returns a Boolean value.)

Own Id: OTP-8273

Tools 2.6.4

Fixed Bugs and Malfunctions

cover now properly escapes greater-than and less-than characters in comments in HTML reports. (Thanks to Magnus Henoch.)

Own Id: OTP-7939

Tools 2.6.3

Improvements and New Features

xref:start/1 does now allow anonymous XREF processes to be started

Own Id: OTP-7831

Tools 2.6.2

Fixed Bugs and Malfunctions

A bug in the Xref scanner has been fixed.

Own Id: OTP-7423

A bug in Fprof where the function 'undefined' appeared to call 'undefined' has been corrected.

Own Id: OTP-7509

Tools 2.6.1

Improvements and New Features

The documentation has been updated so as to reflect the last updates of the Erlang shell as well as the minor modifications of the control sequence p of the io_lib module.

Superfluous empty lines have been removed from code examples and from Erlang shell examples.

Own Id: OTP-6944 Aux Id: OTP-6554, OTP-6911

tuple_size/1 and byte_size/1 have been substituted for size/1.

Own Id: OTP-7009

The coverage analysis tool cover now handles the short-circuit Boolean expressions andalso/2 and orelse/2 properly.

Own Id: OTP-7095

Tools 2.6

Fixed Bugs and Malfunctions

The cover tool could use huge amounts of memory when used in a distributed system.

Own Id: OTP-6758

Tools 2.5.5

Fixed Bugs and Malfunctions

Missing buffer-local declaration in erlang.el has been added. Before this fix there could arise problems in other emacs modes after visiting a buffer using the erlang mode.

Own Id: OTP-6721

Key-map for 'backward-delete-char-untabif updated to work properly with Xemacs.

Own Id: OTP-6723

Improvements and New Features

Minor updates of Xref.

Own Id: OTP-6586

Minor Makefile changes.

Own Id: OTP-6689 Aux Id: OTP-6742

"C-u C-c C-k" now does a compile with both "debug_info" and "export_all".

Own Id: OTP-6741

Tools 2.5.4.1

Improvements and New Features

Changes due to internal interface changes in the erts application which are needed at compile-time. No functionality has been changed.

Own Id: OTP-6611 Aux Id: OTP-6580

Tools 2.5.4

Fixed Bugs and Malfunctions

Made change to support the function erlang-find-tag for xemacs and emacs-21.

Own Id: OTP-6512

Improvements and New Features

Minor updates of xref for future compatibility.

Own Id: OTP-6513

Tools 2.5.3

Fixed Bugs and Malfunctions

eprof did not work reliably in the SMP emulator, because the trace receiver process could not process the trace messages fast enough. Therefore, eprof now blocks the other schedulers while profiling.

Own Id: OTP-6373

Tools 2.5.2

Fixed Bugs and Malfunctions

Fprof traces could become truncated for the SMP emulator. This bug has now been corrected.

Own Id: OTP-6246

Tools 2.5.1

Fixed Bugs and Malfunctions

eprof now works somewhat better in the SMP emulator.

Own Id: OTP-6152

Tools 2.5

Fixed Bugs and Malfunctions

Fixed some bugs in make:

make:files/1,2 can now handle a file in another directory as argument, similar to make:all/0,1.

When specifying a file name including the .erl extension in Emakefile, make:all/0,1 looked for the object code in the wrong place.

When specifying a file name including the .erl extension in Emakefile and some compile options for the file, make:files/0,1 did not use the options as it should do.

Own Id: OTP-6057 Aux Id: seq10299

cover: When cover:stop() was called, the cover compiled code was not unloaded (as stated in the documentation) but simply marked as 'old'. This meant that processes lingering in (or with funs referencing to) the cover compiled code would survive even when the cover server and its ETS tables was terminated.

Now the cover compiled code is unloaded, meaning that processes lingering in/with references to it will be killed when cover:stop is called, instead of later crashing with badarg when trying to bump counters in ETS tables no longer existing.

Improvements and New Features

Replaced call to deprecated function file:file_info/1 with call to filelib:is_dir/1 and filelib:is_regular/1 in tags.erl.

Own Id: OTP-6079

Tools 2.4.7

Fixed Bugs and Malfunctions

A bug in fprof profiling causing erroneous inconsistent trace failure has been corrected.

Own Id: OTP-5922 Aux Id: seq10203

Tools 2.4.6

Fixed Bugs and Malfunctions

Emacs: erlang-man-function and erlang-man-module used a pattern matching to find the requested module that sometimes yielded unexpected results. For example, erlang-man-module file would display the man page for CosFileTransfer_File.

Own Id: OTP-5746 Aux Id: seq10096

Some compiler warnings and Dialyzer warnings were eliminated in the Tools application.

When tracing to a port (which fprof does), there could be fake schedule out/schedule in messages sent for a process that had exited.

Own Id: OTP-5757

Tools 2.4.5

Fixed Bugs and Malfunctions

The cross reference tool xref did not handle the new fun M:F/A construct properly. This problem has been fixed.

Own Id: OTP-5653

Tools 2.4.4

Fixed Bugs and Malfunctions

The cover tool did not escape '<' and '>' not being part of HTML tags in HTML log files.

Own Id: OTP-5588

Tools 2.4.3

Improvements and New Features

It is now possible to encrypt the debug information in beam files, to help keep the source code secret. See compile for how to provide the key for encrypting, and beam_lib for how to provide the key for decryption so that tools such as Debugger, xref, or cover can be used.

The beam_lib:chunks/2 functions now accepts an additional chunk type 'compile_info' to retrieve the compilation information directly as a term. (Thanks to Tobias Lindahl.)

Own Id: OTP-5460 Aux Id: seq9787

Tools 2.4.2

Fixed Bugs and Malfunctions

The cover tool could not analyze empty modules on module level.

Own Id: OTP-5418

Tools 2.4.1

Fixed Bugs and Malfunctions

The xref analysis locals_not_used could return too many functions. This problem has been fixed.

Own Id: OTP-5071

The cover tool could not always compile parse transformed modules. This problem has been fixed.

Own Id: OTP-5305

cover - The Coverage Analysis Tool

Introduction

The module cover provides a set of functions for coverage analysis of Erlang programs, counting how many times each executable line is executed.

Coverage analysis can be used to verify test cases, making sure all relevant code is covered, and can be helpful when looking for bottlenecks in the code.

Getting Started With Cover

Example

Assume that a test case for the following program should be verified:

-module(channel).
-behaviour(gen_server).

-export([start_link/0,stop/0]).
-export([alloc/0,free/1]). % client interface
-export([init/1,handle_call/3,terminate/2]). % callback functions

start_link() ->
    gen_server:start_link({local,channel}, channel, [], []).

stop() ->
    gen_server:call(channel, stop).

%%%-Client interface functions-------------------------------------------

alloc() ->
    gen_server:call(channel, alloc).

free(Channel) ->
    gen_server:call(channel, {free,Channel}).

%%%-gen_server callback functions----------------------------------------

init(_Arg) ->
    {ok,channels()}.

handle_call(stop, _Client, Channels) ->
    {stop,normal,ok,Channels};

handle_call(alloc, _Client, Channels) ->
    {Ch,Channels2} = alloc(Channels),
    {reply,{ok,Ch},Channels2};

handle_call({free,Channel}, _Client, Channels) ->
    Channels2 = free(Channel, Channels),
    {reply,ok,Channels2}.

terminate(_Reason, _Channels) ->
    ok.

%%%-Internal functions---------------------------------------------------

channels() ->
    [ch1,ch2,ch3].

alloc([Channel|Channels]) ->
    {Channel,Channels};
alloc([]) ->
    false.

free(Channel, Channels) ->
    [Channel|Channels].

The test case is implemented as follows:

-module(test).
-export([s/0]).

s() ->
    {ok,Pid} = channel:start_link(),
    {ok,Ch1} = channel:alloc(),
    ok = channel:free(Ch1),
    ok = channel:stop().

Preparation

First of all, Cover must be started. This spawns a process which owns the Cover database where all coverage data will be stored.

1> cover:start().
{ok,<0.90.0>}

To include other nodes in the coverage analysis, use cover:start/1. All cover-compiled modules will then be loaded on all nodes, and data from all nodes will be summed up when analysing. For simplicity this example only involves the current node.

Before any analysis can take place, the involved modules must be cover-compiled. This means that some extra information is added to the module before beging compiled into a binary and loaded. The source file of the module is not affected and no .beam file is created.

2> cover:compile_module(channel).
{ok,channel}

Each time a function in the cover-compiled module channel is called, information about the call will be added to the Cover database. Run the test case:

3> test:s().
ok

Cover analysis is performed by examining the contents of the Cover database. The output is determined by two parameters, Level and Analysis. Analysis is either coverage or calls and determines the type of the analysis. Level is either module, function, clause, or line and determines the level of the analysis.

Coverage Analysis

Analysis of type coverage is used to find out how much of the code has been executed and how much has not been executed. Coverage is represented by a tuple {Cov,NotCov}, where Cov is the number of executable lines that have been executed at least once and NotCov is the number of executable lines that have not been executed.

If the analysis is made on module level, the result is given for the entire module as a tuple {Module,{Cov,NotCov}}:

4> cover:analyse(channel, coverage, module).
{ok,{channel,{14,1}}}

For channel, the result shows that 14 lines in the module are covered but one line is not covered.

If the analysis is made on function level, the result is given as a list of tuples {Function,{Cov,NotCov}}, one for each function in the module. A function is specified by its module name, function name and arity:

5> cover:analyse(channel, coverage, function).
{ok,[{{channel,start_link,0},{1,0}},
     {{channel,stop,0},{1,0}},
     {{channel,alloc,0},{1,0}},
     {{channel,free,1},{1,0}},
     {{channel,init,1},{1,0}},
     {{channel,handle_call,3},{5,0}},
     {{channel,terminate,2},{1,0}},
     {{channel,channels,0},{1,0}},
     {{channel,alloc,1},{1,1}},
     {{channel,free,2},{1,0}}]}

For channel, the result shows that the uncovered line is in the function channel:alloc/1.

If the analysis is made on clause level, the result is given as a list of tuples {Clause,{Cov,NotCov}}, one for each function clause in the module. A clause is specified by its module name, function name, arity and position within the function definition:

6> cover:analyse(channel, coverage, clause).
{ok,[{{channel,start_link,0,1},{1,0}},
     {{channel,stop,0,1},{1,0}},
     {{channel,alloc,0,1},{1,0}},
     {{channel,free,1,1},{1,0}},
     {{channel,init,1,1},{1,0}},
     {{channel,handle_call,3,1},{1,0}},
     {{channel,handle_call,3,2},{2,0}},
     {{channel,handle_call,3,3},{2,0}},
     {{channel,terminate,2,1},{1,0}},
     {{channel,channels,0,1},{1,0}},
     {{channel,alloc,1,1},{1,0}},
     {{channel,alloc,1,2},{0,1}},
     {{channel,free,2,1},{1,0}}]}

For channel, the result shows that the uncovered line is in the second clause of channel:alloc/1.

Finally, if the analysis is made on line level, the result is given as a list of tuples {Line,{Cov,NotCov}}, one for each executable line in the source code. A line is specified by its module name and line number.

7> cover:analyse(channel, coverage, line).
{ok,[{{channel,9},{1,0}},
     {{channel,12},{1,0}},
     {{channel,17},{1,0}},
     {{channel,20},{1,0}},
     {{channel,25},{1,0}},
     {{channel,28},{1,0}},
     {{channel,31},{1,0}},
     {{channel,32},{1,0}},
     {{channel,35},{1,0}},
     {{channel,36},{1,0}},
     {{channel,39},{1,0}},
     {{channel,44},{1,0}},
     {{channel,47},{1,0}},
     {{channel,49},{0,1}},
     {{channel,52},{1,0}}]}

For channel, the result shows that the uncovered line is line number 49.

Call Statistics

Analysis of type calls is used to find out how many times something has been called and is represented by an integer Calls.

If the analysis is made on module level, the result is given as a tuple {Module,Calls}. Here Calls is the total number of calls to functions in the module:

8> cover:analyse(channel, calls, module).
{ok,{channel,12}}

For channel, the result shows that a total of twelve calls have been made to functions in the module.

If the analysis is made on function level, the result is given as a list of tuples {Function,Calls}. Here Calls is the number of calls to each function:

9> cover:analyse(channel, calls, function).
{ok,[{{channel,start_link,0},1},
     {{channel,stop,0},1},
     {{channel,alloc,0},1},
     {{channel,free,1},1},
     {{channel,init,1},1},
     {{channel,handle_call,3},3},
     {{channel,terminate,2},1},
     {{channel,channels,0},1},
     {{channel,alloc,1},1},
     {{channel,free,2},1}]}

For channel, the result shows that handle_call/3 is the most called function in the module (three calls). All other functions have been called once.

If the analysis is made on clause level, the result is given as a list of tuples {Clause,Calls}. Here Calls is the number of calls to each function clause:

10> cover:analyse(channel, calls, clause).
{ok,[{{channel,start_link,0,1},1},
     {{channel,stop,0,1},1},
     {{channel,alloc,0,1},1},
     {{channel,free,1,1},1},
     {{channel,init,1,1},1},
     {{channel,handle_call,3,1},1},
     {{channel,handle_call,3,2},1},
     {{channel,handle_call,3,3},1},
     {{channel,terminate,2,1},1},
     {{channel,channels,0,1},1},
     {{channel,alloc,1,1},1},
     {{channel,alloc,1,2},0},
     {{channel,free,2,1},1}]}

For channel, the result shows that all clauses have been called once, except the second clause of channel:alloc/1 which has not been called at all.

Finally, if the analysis is made on line level, the result is given as a list of tuples {Line,Calls}. Here Calls is the number of times each line has been executed:

11> cover:analyse(channel, calls, line).
{ok,[{{channel,9},1},
     {{channel,12},1},
     {{channel,17},1},
     {{channel,20},1},
     {{channel,25},1},
     {{channel,28},1},
     {{channel,31},1},
     {{channel,32},1},
     {{channel,35},1},
     {{channel,36},1},
     {{channel,39},1},
     {{channel,44},1},
     {{channel,47},1},
     {{channel,49},0},
     {{channel,52},1}]}

For channel, the result shows that all lines have been executed once, except line number 49 which has not been executed at all.

Analysis to File

A line level calls analysis of channel can be written to a file using cover:analyse_to_file/1:

12> cover:analyse_to_file(channel).
{ok,"channel.COVER.out"}

The function creates a copy of channel.erl where it for each executable line is specified how many times that line has been executed. The output file is called channel.COVER.out.

File generated from /Users/bjorng/git/otp/channel.erl by COVER 2024-03-20 at 13:25:04

****************************************************************************

        |  -module(channel).
        |  -behaviour(gen_server).
        |
        |  -export([start_link/0,stop/0]).
        |  -export([alloc/0,free/1]). % client interface
        |  -export([init/1,handle_call/3,terminate/2]). % callback functions
        |
        |  start_link() ->
     1..|      gen_server:start_link({local,channel}, channel, [], []).
        |
        |  stop() ->
     1..|      gen_server:call(channel, stop).
        |
        |  %%%-Client interface functions-------------------------------------------
        |
        |  alloc() ->
     1..|      gen_server:call(channel, alloc).
        |
        |  free(Channel) ->
     1..|      gen_server:call(channel, {free,Channel}).
        |
        |  %%%-gen_server callback functions----------------------------------------
        |
        |  init(_Arg) ->
     1..|      {ok,channels()}.
        |
        |  handle_call(stop, _Client, Channels) ->
     1..|      {stop,normal,ok,Channels};
        |
        |  handle_call(alloc, _Client, Channels) ->
     1..|      {Ch,Channels2} = alloc(Channels),
     1..|      {reply,{ok,Ch},Channels2};
        |
        |  handle_call({free,Channel}, _Client, Channels) ->
     1..|      Channels2 = free(Channel, Channels),
     1..|      {reply,ok,Channels2}.
        |
        |  terminate(_Reason, _Channels) ->
     1..|      ok.
        |
        |  %%%-Internal functions---------------------------------------------------
        |
        |  channels() ->
     1..|      [ch1,ch2,ch3].
        |
        |  alloc([Channel|Channels]) ->
     1..|      {Channel,Channels};
        |  alloc([]) ->
     0..|      false.
        |
        |  free(Channel, Channels) ->
     1..|      [Channel|Channels].

Conclusion

By looking at the results from the analyses, it can be deduced that the test case does not cover the case when all channels are allocated and test.erl should be extended accordingly. Incidentally, when the test case is corrected a bug in channel will be discovered.

When the Cover analysis is ready, Cover is stopped and all cover-compiled modules are unloaded. The code for channel is now loaded as usual from a .beam file in the current path.

13> code:which(channel).
cover_compiled
14> cover:stop().
ok
15> code:which(channel).
"./channel.beam"

Miscellaneous

Performance

Execution of code in cover-compiled modules is slower and more memory consuming than for regularly compiled modules. As the Cover database contains information about each executable line in each cover-compiled module, performance decreases proportionally to the size and number of the cover-compiled modules.

To improve performance when analysing cover results it is possible to do multiple calls to analyse and analyse_to_file at once. You can also use the async_analyse_to_file convenience function.

Executable Lines

Cover uses the concept of executable lines, which is code lines containing an executable expression such as a matching or a function call. A blank line or a line containing a comment, function head or pattern in a case or receive statement is not executable.

In the example below, lines number 2, 4, 6, 8, and 11 are executable lines:

1: is_loaded(Module, Compiled) ->
2:   case get_file(Module, Compiled) of
3:     {ok,File} ->
4:       case code:which(Module) of
5:         ?TAG ->
6:           {loaded,File};
7:         _ ->
8:           unloaded
9:       end;
10:    false ->
11:      false
12:  end.

Code Loading Mechanism

When a module is cover-compiled, it is also loaded using the normal code loading mechanism of Erlang. This means that if a cover-compiled module is re-loaded during a Cover session, for example using c(Module), it will no longer be cover-compiled.

Use cover:is_compiled/1 or code:which/1 to see whether or not a module is cover-compiled (and still loaded).

When Cover is stopped, all cover-compiled modules are unloaded.

cprof - The Call Count Profiler

cprof is a profiling tool that can be used to get a picture of how often different functions in the system are called.

cprof uses breakpoints similar to local call trace, but containing counters, to collect profiling data. Therefore there is no need for special compilation of any module to be profiled.

cprof presents all profiled modules in descending total call count order, and for each module presents all profiled functions also in descending call count order. A call count limit can be specified to filter out all functions below the limit.

Profiling is done in the following steps:

cprof:start/* - Starts profiling with zeroed call counters for specified functions by setting call count breakpoints on them.

Mod:Fun() - Runs the code to be profiled.

cprof:pause/* - Pauses the call counters for specified functions. This minimizes the impact of code running in the background or in the shell. Call counters are automatically paused when they "hit the ceiling" of the host machine word size. For a 32 bit host the maximum counter value is 2,147,483,647.

cprof:analyse/* - Collects call counters and computes the result.

cprof:restart/* - Restarts the call counters from zero for specified functions. Can be used to collect a new set of counters without having to stop and start call count profiling.

cprof:stop/0..3 - Stops profiling by removing call count breakpoints from specified functions.

Functions can be specified as either all in the system, all in one module, all arities of one function, one function, or all functions in all modules not yet loaded. BIFs cannot be call-count traced.

The analysis result can either be for a single module or for all modules. In either case a call count limit can be given to filter out the functions with a call count below the limit. The all modules analysis does not contain the module cprof itself; the only way to analyze cprof is by specifying it as a single module to analyse.

Call count tracing is very lightweight compared to other forms of tracing since no trace message has to be generated. Some measurements indicates performance degradations in the vicinity of 10 percent.

The following sections show some examples of profiling with cprof.

Example: Background work

From the Erlang shell:

1> cprof:start(), cprof:pause(). % Stop counters just after start
8492
2> cprof:analyse().
{539,
 [{shell,155,
         [{{shell,prep_check,1},55},
          {{shell,used_records,4},45},
          {{shell,used_records,1},45},
          {{shell,used_record_defs,2},1},
          {{shell,record_defs,2},1},
          {{shell,record_bindings,2},1},
          {{shell,exprs,7},1},
          {{shell,expr,4},1},
          {{shell,expand_records,2},1},
          {{shell,check_command,2},1},
          {{shell,apply_fun,3},1},
          {{shell,'-exprs/7-lc$^0/1-0-',1},1},
          {{shell,'-eval_loop/3-fun-0-',3},1}]},
  %% Information about many modules omitted.
                     .
                     .
                     .
  %% Here is the last part.
  {erts_internal,2,[{{erts_internal,trace_pattern,3},2}]},
  {otp_internal,1,[{{otp_internal,obsolete,3},1}]},
  {maps,1,[{{maps,from_list,1},1}]},
  {erl_internal,1,[{{erl_internal,bif,3},1}]}]}
3> cprof:analyse(cprof).
{cprof,3,[{{cprof,tr,2},2},{{cprof,pause,0},1}]}
4> cprof:stop().
8586

The example showed some of the background work that the shell performs just to interpret the first command line.

What is captured in this example is the part of the work the shell does while interpreting the command line that occurs between the actual calls to cprof:start() and cprof:analyse().

Example: One module

From the Erlang shell:

1> cprof:start(),R=calendar:day_of_the_week(1896,4,27),cprof:pause(),R.
1
2> cprof:analyse(calendar).
{calendar,9,
          [{{calendar,last_day_of_the_month1,2},1},
           {{calendar,last_day_of_the_month,2},1},
           {{calendar,is_leap_year1,1},1},
           {{calendar,is_leap_year,1},1},
           {{calendar,dy,1},1},
           {{calendar,dm,1},1},
           {{calendar,df,2},1},
           {{calendar,day_of_the_week,3},1},
           {{calendar,date_to_gregorian_days,3},1}]}
3> cprof:stop().
8648

The example tells us that "Aktiebolaget LM Ericsson & Co" was registered on a Monday (since the return value of the first command is 1), and that the calendar module needed 9 function calls to calculate that.

Using cprof:analyse() in this example also shows approximately the same background work as in the first example.

Example: In the code

Write a module:

-module(sort).
-export([do/1]).

do(N) ->
    cprof:stop(),
    cprof:start(),
    do(N, []).

do(0, L) ->
    R = lists:sort(L),
    cprof:pause(),
    R;
do(N, L) ->
    do(N-1, [rand:uniform(256)-1 | L]).

From the Erlang shell:

1> c(sort).
{ok,sort}
2> rand:seed(default, 42), ok.
ok.
3> sort:do(1000).
[0,0,0,1,1,1,1,2,2,3,3,4,4,4,4,5,5,5,6,6,6,6,7,7,7,7,7,8,8|...]
4> cprof:analyse().
{13180,
 [{lists,6173,
         [{{lists,rmerge3_1,6},1045},
          {{lists,rmerge3_2,6},977},
          {{lists,split_1,5},652},
          {{lists,merge3_1,6},579},
          {{lists,merge3_2,6},577},
          {{lists,rmerge3_12_3,6},511},
          {{lists,split_1_1,6},347},
          {{lists,merge3_12_3,6},310},
          {{lists,rmerge3_21_3,6},282},
          {{lists,merge3_21_3,6},221},
          {{lists,merge2_1,4},154},
          {{lists,merge2_2,5},138},
          {{lists,reverse,2},106},
          {{lists,rmerge2_2,5},87},
          {{lists,rmergel,2},81},
          {{lists,rmerge2_1,4},75},
          {{lists,mergel,2},28},
          {{lists,keyfind,3},2},
          {{lists,sort,1},1}]},
  {rand,5000,
        [{{rand,uniform_s,2},1000},
         {{rand,uniform,1},1000},
         {{rand,seed_put,1},1000},
         {{rand,seed_get,0},1000},
         {{rand,exsss_uniform,2},1000}]},
  {erlang,1004,
          [{{erlang,put,2},1000},
           {{erlang,trace_pattern,3},2},
           {{erlang,ensure_tracer_module_loaded,2},2}]},
  {sort,1001,[{{sort,do,2},1001}]},
  {erts_internal,2,[{{erts_internal,trace_pattern,3},2}]}]}
5> cprof:stop().
12625

The example shows some details of how lists:sort/1 works. It used 6173 function calls in module lists to complete the work.

This time, since the shell was not involved in starting and stopping cprof, no other work was done in the system during the profiling.

The Erlang mode for Emacs

Purpose

The purpose of this user guide is to introduce you to the Erlang mode for Emacs and gives some relevant background information of the functions and features. See also Erlang mode reference manual The purpose of the Erlang mode itself is to facilitate the developing process for the Erlang programmer.

Pre-requisites

Basic knowledge of Emacs and Erlang/OTP.

Elisp

Two Elisp modules are included in this tool package for Emacs. erlang.el defines the actual Erlang mode and erlang-start.el makes some nice initializations.

Setup on UNIX

To set up the Erlang Emacs mode on a UNIX systems, edit or create the file .emacs in the your home directory.

Below is a complete example of what should be added to a user's .emacs provided that OTP is installed in the directory /usr/local/otp:

(setq load-path (cons  "/usr/local/otp/lib/tools-<ToolsVer>/emacs"
load-path))
(setq erlang-root-dir "/usr/local/otp")
(setq exec-path (cons "/usr/local/otp/bin" exec-path))
(require 'erlang-start)

Setup on Windows

To set up the Erlang Emacs mode on a Windows systems, edit/create the file .emacs, the location of the file depends on the configuration of the system. If the HOME environment variable is set, Emacs will look for the .emacs file in the directory indicated by the HOME variable. If HOME is not set, Emacs will look for the .emacs file in C:\.

Below is a complete example of what should be added to a user's .emacs provided that OTP is installed in the directory C:\Program Files\Erlang OTP:

(setq load-path (cons  "C:/Program Files/Erlang OTP/lib/tools-<ToolsVer>/emacs"
load-path))
(setq erlang-root-dir "C:/Program Files/Erlang OTP")
(setq exec-path (cons "C:/Program Files/Erlang OTP/bin" exec-path))
(require 'erlang-start)

Note

In .emacs, the slash character (/) can be used as path separator. But if you decide to use the backslash character (\), note that backslashes have to be doubled, since they are treated as escape characters by Emacs.

Indentation

The "Oxford Advanced Learners Dictionary of Current English" says the following about the word "indent":

"start (a line of print or writing) farther from the margin than the others".

The Erlang mode does, of course, provide this feature. The layout used is based on the common use of the language.

It is strongly recommended to use this feature and avoid to indent lines in a nonstandard way. Some motivations are:

Code using the same layout is easy to read and maintain.

Since several features of Erlang mode is based on the standard layout they might not work correctly if a nonstandard layout is used.

The indentation features can be used to reindent large sections of a file. If some lines use nonstandard indentation they will be reindented.

Editing

M-x erlang-mode RET - This command activates the Erlang major mode for the current buffer. When this mode is active the mode line contain the word "Erlang".

When the Erlang mode is correctly installed, it is automatically activated when a file ending in .erl or .hrl is opened in Emacs.

When a file is saved the name in the -module(). line is checked against the file name. Should they mismatch Emacs can change the module specifier so that it matches the file name. By default, the user is asked before the change is performed.

An "electric" command is a character that in addition to just inserting the character performs some type of action. For example the ; character is typed in a situation where is ends a function clause a new function header is generated. The electric commands are as follows:

erlang-electric-comma - Insert a comma character and possibly a new indented line.

erlang-electric-semicolon - Insert a semicolon character and possibly a prototype for the next line.

erlang-electric-gt - Insert a > character and possible a new indented line.

To disable all electric commands set the variable erlang-electric-commands to the empty list. In short, place the following line in your .emacs-file:

(setq erlang-electric-commands '())

Syntax highlighting

It is possible for Emacs to use colors when displaying a buffer. By "syntax highlighting", we mean that syntactic components, for example keywords and function names, will be colored.

The basic idea of syntax highlighting is to make the structure of a program clearer. For example, the highlighting will make it easier to spot simple bugs. Have not you ever written a variable in lower-case only? With syntax highlighting a variable will colored while atoms will be shown with the normal text color.

Tags

Tags is a standard Emacs package used to record information about source files in large development projects. In addition to listing the files of a project, a tags file normally contains information about all functions and variables that are defined. By far, the most useful command of the tags system is its ability to find the definition of functions in any file in the project. But the Tags system is not limited to this feature, for example, it is possible to do a text search in all files in a project, or to perform a project-wide search and replace.

In order to use the Tags system a file named TAGS must be created. The file can be seen as a database over all functions, records, and macros in all files in the project. The TAGS file can be created using two different methods for Erlang. The first is the standard Emacs utility "etags", the second is by using the Erlang module tags.

Etags

etags is a program that is part of the Emacs distribution. It is normally executed from a command line, like a Unix shell or a DOS box.

The etags program of fairly modern versions of Emacs and XEmacs has native support for Erlang. To check if your version does include this support, issue the command etags --help at a the command line prompt. At the end of the help text there is a list of supported languages. Unless Erlang is a member of this list I suggest that you should upgrade to a newer version of Emacs.

As seen in the help text — unless you have not upgraded your Emacs yet — etags associate the file extensions .erl and .hrl with Erlang.

Basically, the etags utility is run using the following form:

etags file1.erl file2.erl

This will create a file named TAGS in the current directory.

The etags utility can also read a list of files from its standard input by supplying a single dash in place of the file names. This feature is useful when a project consists of a large number of files. The standard UNIX command find can be used to generate the list of files, for example:

find . -name "*.[he]rl" -print | etags -

The above line will create a TAGS file covering all the Erlang source files in the current directory, and in the subdirectories below.

See the GNU Emacs Manual and the etags man page for more info.

Shell

The look and feel on an Erlang shell inside Emacs should be the same as in a normal Erlang shell. There is just one major difference, the cursor keys will actually move the cursor around just like in any normal Emacs buffer. The command line history can be accessed by the following commands:

C-upor M-p(comint-previous-input) - Move to the previous line in the input history.

C-downor M-n(comint-next-input) - Move to the next line in the input history.

If the Erlang shell buffer would be killed the command line history is saved to a file. The command line history is automatically retrieved when a new Erlang shell is started.

Compilation

The classic edit-compile-bugfix cycle for Erlang is to edit the source file in an editor, save it to a file and switch to an Erlang shell. In the shell the compilation command is given. Should the compilation fail you have to bring out the editor and locate the correct line.

With the Erlang editing mode the entire edit-compile-bugfix cycle can be performed without leaving Emacs. Emacs can order Erlang to compile a file and it can parse the error messages to automatically place the point on the erroneous lines.

fprof - The File Trace Profiler

fprof is a profiling tool that can be used to get a picture of how much processing time different functions consumes and in which processes.

fprof uses tracing with timestamps to collect profiling data. Therefore there is no need for special compilation of any module to be profiled.

fprof presents wall clock times from the host machine OS, with the assumption that OS scheduling will randomly load the profiled functions in a fair way. Both own time, that is, the time used by a function for its own execution, and accumulated time, that is, execution time including called functions.

Profiling is essentially done in 3 steps:

Tracing to a file.

Profiling: the trace file is read and raw profile data is collected into an internal RAM storage on the node. During this step the trace data may be dumped in text format to file or console.

Analysing: the raw profile data is sorted and dumped in text format either to file or console.

Since fprof stores trace data to a file, the runtime performance degradation is minimized, but still far from negligible, especially for programs that themselves use the filesystem heavily. Where the trace file is placed is also important; on Unix systems /tmp is usually a good choice, while any network-mounted disk is a bad choice.

fprof can also skip the file step and trace to a tracer process of its own that does the profiling in runtime.

The following sections show some examples of how to profile with fprof.

Profiling from the source code

If you can edit and recompile the source code, it is convenient to insert fprof:trace(start) and fprof:trace(stop) before and after the code to be profiled. All spawned processes are also traced. If you want some other filename than the default, use fprof:trace(start, "my_fprof.trace").

When execution is finished, the raw profile can be processed using fprof:profile(), or fprof:profile(file, "my_fprof.trace") for a non-default filename.

Finally create an informative table dumped on the console with fprof:analyse(), or on file with fprof:analyse(dest, []), or fprof:analyse([{dest, "my_fprof.analysis"}, {cols, 120}]) for a wider listing of a non-default filename.

Profiling a function

If you have one function that does the task that you want to profile, and the function returns when the profiling should stop, it is convenient to use fprof:apply(Module, Function, Args) for the tracing step.

If the tracing should continue after the function has returned, for example if it is a start function that spawns processes to be profiled, use fprof:apply(M, F, Args, [continue | OtherOpts]). The tracing has to be stopped at a suitable later time using fprof:trace(stop).

Immediate profiling

It is also possible to trace immediately into the profiling process that creates the raw profile data, that is to short circuit the tracing and profiling steps so that the filesystem is not used for tracing.

Do something like this:

{ok, Tracer} = fprof:profile(start),
fprof:trace([start, {tracer, Tracer}]),
%% Run code to profile
fprof:trace(stop);

This puts less load on the filesystem, but much more load on the Erlang runtime system.

lcnt - The Lock Profiler

Internally in the Erlang runtime system locks are used to protect resources from being updated from multiple threads in a fatal way. Locks are necessary to ensure that the runtime system works properly, but it also introduces limitations, namely lock contention and locking overhead.

With lock contention we mean when one thread locks a resource, and another thread (or threads) tries to acquire the same resource at the same time. The lock will deny the other thread access to the resource and the thread will be blocked from continuing its execution. The second thread has to wait until the first thread has completed its access to the resource and unlocked it. The lcnt tool measures these lock conflicts.

Locks have an inherent cost in execution time and memory space. It takes time to initialize, destroy, acquire, or release locks. To decrease lock contention it is sometimes necessary to use finer-grained locking strategies. This usually also increases the locking overhead. Hence there is a tradeoff between lock contention and overhead. In general, lock contention increases with the number of threads running concurrently.

The lcnt tool does not measure locking overhead.

Enabling lock-counting

For investigation of locks in the emulator we use an internal tool called lcnt (short for lock-count). The VM needs to be compiled with this option enabled. To compile a lock-counting VM along with a normal VM, use:

cd $ERL_TOP
./configure --enable-lock-counter
make

Start the lock-counting VM like this:

$ERL_TOP/bin/erl -emu_type lcnt

To verify that lock counting is enabled check that [lock-counting] appears in the status text when the VM is started.

Erlang/OTP 27 [erts-15.0] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit] [lock-counting]

Getting started

Once you have a lock counting enabled VM the module lcnt can be used. The module is intended to be used from the current running nodes shell. To access remote nodes use lcnt:clear(Node) and lcnt:collect(Node).

All locks are continuously monitored and its statistics updated. Use lcnt:clear/0 to initially clear all counters before running any specific tests. This command will also reset the internal duration timer.

To retrieve lock statistics information, use lcnt:collect/0,1. The collect operation will start a lcnt server if it not already started. All collected data will be stored in an Erlang term and uploaded to the server along with the duration time. The duration time is the time between lcnt:clear/0,1 and lcnt:collect/0,1.

Once the data is collected to the server it can be filtered, sorted, and printed in multiple ways.

Example of usage

Here is an example of running the Big Bang Benchmark:

Erlang/OTP 27 [erts-15.0] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit] [lock-counting]

Eshell V15.0 (press Ctrl+G to abort, type help(). for help)
1> lcnt:rt_opt({copy_save, true}).
false
2> lcnt:clear(), big:bang(1000), lcnt:collect().
ok
3> lcnt:conflicts().
      lock   id  #tries  #collisions  collisions [%]  time [us]  duration [%]
     -----  --- ------- ------------ --------------- ---------- -------------
 run_queue   10  590799         8875          1.5022      37906        2.2167
 proc_msgq 1048 2515180         4667          0.1856      20962        1.2258
 proc_main 1048 2195317        23775          1.0830       1664        0.0973
ok

Another way to to profile a specific function is to use lcnt:apply/3 or lcnt:apply/1, which calls lcnt:clear/0 before calling the function and lcnt:collect/0 after its invocation. This method should only be used in micro-benchmarks since it sets copy_save to true for the duration of the function call, which may cause the emulator to run out of memory if attempted under load.

1> lcnt:apply(fun() -> big:bang(1000) end).
1845411
2> lcnt:conflicts().
      lock   id  #tries  #collisions  collisions [%]  time [us]  duration [%]
     -----  --- ------- ------------ --------------- ---------- -------------
 run_queue   10  582789         9237          1.5850      41929        2.2633
 proc_msgq 1047 2494483         4731          0.1897      11173        0.6031
 proc_main 1047 2192806        23283          1.0618       1500        0.0810
ok

The process locks are sorted after its class like all other locks. It is convenient to look at specific processes and ports as classes. We can do this by swapping class and class identifiers with lcnt:swap_pid_keys/0.

3> lcnt:swap_pid_keys().
ok
4> lcnt:conflicts([{print, [name, tries, ratio, time]}]).
                   lock  #tries  collisions [%]  time [us]
                  ----- ------- --------------- ----------
              run_queue  582789          1.5850      41929
 <nonode@nohost.1042.0>    5692          0.5095        484
  <nonode@nohost.465.0>    4989          0.4410        393
  <nonode@nohost.347.0>    6319          2.1839        284
  <nonode@nohost.436.0>    6077          1.9747        198
  <nonode@nohost.307.0>    5071          1.3015        192
  <nonode@nohost.455.0>    5846          1.7106        186
  <nonode@nohost.565.0>    6305          1.2054        179
  <nonode@nohost.461.0>    5820          1.2715        176
  <nonode@nohost.173.0>    6329          1.4852        168
  <nonode@nohost.453.0>    5172          0.8701        167
  <nonode@nohost.741.0>    5306          0.4146        166
  <nonode@nohost.403.0>    5838          1.9870        160
  <nonode@nohost.463.0>    6346          1.5443        143
  <nonode@nohost.184.0>    5542          0.4331        141
  <nonode@nohost.289.0>    5260          0.2662        137
  <nonode@nohost.166.0>    5610          0.9447        127
  <nonode@nohost.189.0>    5354          0.5230        118
  <nonode@nohost.121.0>    5845          0.9239        115
  <nonode@nohost.104.0>    5140          0.7782        108
ok

Example with Mnesia Transaction Benchmark

From the Erlang shell:

Erlang/OTP 27 [erts-15.0] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit] [lock-counting]

Eshell V15.0 (press Ctrl+G to abort, type help(). for help)
1> Conf = [{db_nodes, [node()]}, {driver_nodes, [node()]}, {replica_nodes, [node()]},
    {n_drivers_per_node, 10}, {n_branches, 1000}, {n_accounts_per_branch, 10},
    {replica_type, ram_copies}, {stop_after, 60000}, {reuse_history_id, true}], ok.
ok
2> mnesia_tpcb:init([{use_running_mnesia, false}|Conf]).
    .
    .
    .
ignore

Initial configuring of the benchmark is done. It is time to profile the actual Mnesia benchmark:

3> lcnt:apply(fun() -> {ok,{time, Tps,_,_,_,_}} = mnesia_tpcb:run([{use_running_mnesia,
    true}|Conf]), Tps/60 end).
      .
      .
      .
50204.666666666664

The benchmark runs for 60 seconds (followed by verification and analysis), and then returns the number of transactions per seconds.

4> lcnt:swap_pid_keys().
ok
5> lcnt:conflicts().
                   lock      id    #tries  #collisions  collisions [%]  time [us]  duration [%]
                  -----     ---   ------- ------------ --------------- ---------- -------------
              run_queue      10  89329288      3227515          3.6131    5018119        8.3606
          mnesia_locker       5  64793236      8231226         12.7038      98654        0.1644
                 db_tab 3012324 416847817       140631          0.0337      75308        0.1255
 <nonode@nohost.1158.0>       5  14499900        36934          0.2547       4878        0.0081
 <nonode@nohost.1157.0>       5  14157504        35797          0.2528       4727        0.0079
 <nonode@nohost.1163.0>       5  14194934        34510          0.2431       4403        0.0073
 <nonode@nohost.1164.0>       5  14149447        35326          0.2497       4150        0.0069
 <nonode@nohost.1166.0>       5  14316525        35675          0.2492       4116        0.0069
 <nonode@nohost.1159.0>       5  14241146        35358          0.2483       4101        0.0068
 <nonode@nohost.1162.0>       5  14224491        35182          0.2473       4094        0.0068
 <nonode@nohost.1160.0>       5  14190075        35328          0.2490       4075        0.0068
 <nonode@nohost.1165.0>       5  14308906        35031          0.2448       3896        0.0065
 <nonode@nohost.1161.0>       5  14457330        36182          0.2503       3856        0.0064
              mnesia_tm       5  28149333       179294          0.6369       1057        0.0018
               pix_lock    1024       132            1          0.7576        549        0.0009
   <nonode@nohost.85.0>       5        17            2         11.7647         87        0.0001
 <nonode@nohost.1156.0>       5      1335            6          0.4494          1        0.0000
ok

The id header represents the number of unique identifiers under a class when the option {combine, true} is used (which is enabled by default). It will otherwise show the specific identifier. The db_tab listing shows 3,012,324 unique locks, which is one for each ETS table created. Mnesia creates one ETS table for each transaction.

The listing shows also shows that the mnesia_locker process has highly contended locks. Using lcnt:inspect/1 more information can be displayed for that process:

6> lcnt:inspect(mnesia_locker).
          lock          id   #tries  #collisions  collisions [%]  time [us]  duration [%] histogram [log2(us)]
         -----         ---  ------- ------------ --------------- ---------- ------------- ---------------------
 mnesia_locker   proc_main 19853372      7591248         38.2366      80550        0.1342 |.    ...X........             |
 mnesia_locker   proc_msgq 30917225       639627          2.0688      17126        0.0285 |.    .X.........              |
 mnesia_locker proc_status  9348426          351          0.0038        978        0.0016 |        .xxX. .               |
 mnesia_locker    proc_btm        0            0          0.0000          0        0.0000 |                              |
 mnesia_locker  proc_trace  4674213            0          0.0000          0        0.0000 |                              |
ok

Listing the conflicts without class combiner:

7> lcnt:conflicts([{combine, false}, {print, [name, id, tries, ratio, time]}]).
                   lock                        id   #tries  collisions [%]  time [us]
                  -----                       ---  ------- --------------- ----------
              run_queue                         2 31075249          3.5676    1728233
              run_queue                         1 29738521          3.6348    1683219
              run_queue                         3 27912150          3.6429    1573593
          mnesia_locker                 proc_main 19853372         38.2366      80550
                 db_tab mnesia_transient_decision  3012281          2.5675      55104
              run_queue                         4   512077          3.7041      29486
          mnesia_locker                 proc_msgq 30917225          2.0688      17126
                 db_tab                   account  6044562          0.3599       7909
                 db_tab                    branch  6026659          0.3132       5654
                 db_tab                    teller  6044659          0.2684       4727
 <nonode@nohost.1158.0>                 proc_main  3207155          0.7178       3726
 <nonode@nohost.1163.0>                 proc_main  3138532          0.7485       3593
 <nonode@nohost.1157.0>                 proc_main  3133180          0.7156       3547
 <nonode@nohost.1166.0>                 proc_main  3165128          0.7609       3517
 <nonode@nohost.1164.0>                 proc_main  3128838          0.7525       3477
 <nonode@nohost.1160.0>                 proc_main  3137627          0.7559       3433
 <nonode@nohost.1162.0>                 proc_main  3144886          0.7509       3425
 <nonode@nohost.1159.0>                 proc_main  3149315          0.7487       3372
 <nonode@nohost.1161.0>                 proc_main  3196546          0.7591       3310
 <nonode@nohost.1165.0>                 proc_main  3164333          0.7483       3309
ok

In this scenario the locks for the scheduler's run queues dominate the time waiting for locks. The most contended lock for ETS tables is for the mnesia_transient_decision ETS table.

Here is how to show the information for the ETS tables.

8> lcnt:inspect(db_tab, [{print, [name, id, tries, colls, ratio, duration]}]).
   lock                        id    #tries  #collisions  collisions [%]  duration [%]
  -----                       ---   ------- ------------ --------------- -------------
 db_tab mnesia_transient_decision   3012281        77341          2.5675        0.0918
 db_tab                   account   6044562        21753          0.3599        0.0132
 db_tab                    branch   6026659        18873          0.3132        0.0094
 db_tab                    teller   6044659        16221          0.2684        0.0079
 db_tab                   history   3012281         4005          0.1330        0.0022
 db_tab              mnesia_stats   3071064         2437          0.0794        0.0010
 db_tab        mnesia_trans_store        15            0          0.0000        0.0000
 db_tab           mnesia_decision   3012281            0          0.0000        0.0000
 db_tab                    schema         0            0          0.0000        0.0000
 db_tab                      dets         0            0          0.0000        0.0000
 db_tab               dets_owners         0            0          0.0000        0.0000
 db_tab             dets_registry         0            0          0.0000        0.0000
 db_tab         mnesia_lock_queue  36154974            0          0.0000        0.0000
 db_tab       mnesia_sticky_locks  12108098            0          0.0000        0.0000
 db_tab          mnesia_tid_locks  27176721            0          0.0000        0.0000
 db_tab         mnesia_held_locks  48321870            0          0.0000        0.0000
 db_tab             mnesia_subscr         0            0          0.0000        0.0000
 db_tab               mnesia_gvar 102680683            1          0.0000        0.0000
 db_tab            user_functions         0            0          0.0000        0.0000
 db_tab             shell_records         0            0          0.0000        0.0000
ok

Deciphering the output

Typically high time values are bad and this is often the thing to look for. However, one should also look for high lock acquisition frequencies (#tries) since locks generate overhead and because high frequency could become problematic if they begin to have conflicts even if it is not shown in a particular test.

The Big Bang Benchmark

-module(big).
-export([bang/1]).

pinger([], [], true) ->
    receive
	{procs, Procs, ReportTo} ->
	    pinger(Procs, [], ReportTo)
    end;
pinger([], [], false) ->
    receive {ping, From} -> From ! {pong, self()} end,
    pinger([],[],false);
pinger([], [], ReportTo) ->
    ReportTo ! {done, self()},
    pinger([],[],false);
pinger([], [Po|Pos] = Pongers, ReportTo) ->
    receive
	{ping, From} ->
	    From ! {pong, self()},
	    pinger([], Pongers, ReportTo);
	{pong, Po} ->
	    pinger([], Pos, ReportTo)
    end;
pinger([Pi|Pis], Pongers, ReportTo) ->
    receive {ping, From} -> From ! {pong, self()}
    after 0 -> ok
    end,
    Pi ! {ping, self()},
    pinger(Pis, [Pi|Pongers], ReportTo).

spawn_procs(N) when N =< 0 ->
    [];
spawn_procs(N) ->
    [spawn_link(fun () -> pinger([],[],true) end) | spawn_procs(N-1)].

send_procs([], Msg) ->
    Msg;
send_procs([P|Ps], Msg) ->
    P ! Msg,
    send_procs(Ps, Msg).

receive_msgs([]) ->
    ok;
receive_msgs([M|Ms]) ->
    receive
	M ->
	    receive_msgs(Ms)
    end.

bang(N) when integer(N) ->
    Procs = spawn_procs(N),
    RMsgs = lists:map(fun (P) -> {done, P} end, Procs),
    Start = now(),
    send_procs(Procs, {procs, Procs, self()}),
    receive_msgs(RMsgs),
    Stop = now(),
    lists:foreach(fun (P) -> exit(P, normal) end, Procs),
    timer:now_diff(Stop, Start).

See Also

LCNT Reference Manual

Xref - The Cross Reference Tool

Xref is a cross reference tool that can be used for finding dependencies between functions, modules, applications and releases. It does so by analyzing the defined functions and the function calls.

In order to make Xref easy to use, there are predefined analyses that perform some common tasks. Typically, a module or a release can be checked for calls to undefined functions. For the somewhat more advanced user there is a small but flexible language that can be used for selecting parts of the analyzed system and for doing some simple graph analyses on selected calls.

The following sections show some features of Xref, beginning with a module check and a predefined analysis. Then follow examples that can be skipped on the first reading; not all of the concepts used are explained, and it is assumed that the reference manual has been at least skimmed.

Module Check

Assume we want to check the following module:

-module(my_module).

-export([t/1]).

t(A) ->
  my_module:t2(A).

t2(_) ->
  true.

Cross reference data are read from BEAM files, so the first step when checking an edited module is to compile it:

1> c(my_module, debug_info).
./my_module.erl:10: Warning: function t2/1 is unused
{ok, my_module}

The debug_info option ensures that the BEAM file contains debug information, which makes it possible to find unused local functions.

The module can now be checked for calls to deprecated functions, calls to undefined functions, and for unused local functions:

2> xref:m(my_module)
[{deprecated,[]},
 {undefined,[{{my_module,t,1},{my_module,t2,1}}]},
 {unused,[{my_module,t2,1}]}]

m/1 is also suitable for checking that the BEAM file of a module that is about to be loaded into a running a system does not call any undefined functions. In either case, the code path of the code server (see the module code) is used for finding modules that export externally called functions not exported by the checked module itself, so called library modules.

Predefined Analysis

In the last example the module to analyze was given as an argument to m/1, and the code path was (implicitly) used as library path. In this example an xref server will be used, which makes it possible to analyze applications and releases, and also to select the library path explicitly.

Each Xref server is referred to by a unique name. The name is given when creating the server:

1> xref:start(s).
{ok,<0.27.0>}

Next the system to be analyzed is added to the Xref server. Here the system will be OTP, so no library path will be needed. Otherwise, when analyzing a system that uses OTP, the OTP modules are typically made library modules by setting the library path to the default OTP code path (or to code_path, see the reference manual). By default, the names of read BEAM files and warnings are output when adding analyzed modules, but these messages can be avoided by setting default values of some options:

2> xref:set_default(s, [{verbose,false}, {warnings,false}]).
ok
3> xref:add_release(s, code:lib_dir(), {name, otp}).
{ok,otp}

add_release/3 assumes that all subdirectories of the library directory returned by code:lib_dir() contain applications; the effect is that of reading all BEAM files for the application.

It is now easy to check the release for calls to undefined functions:

4> xref:analyze(s, undefined_function_calls).
{ok, [...]}

We can now continue with further analyses, or we can delete the Xref server:

5> xref:stop(s).

The check for calls to undefined functions is an example of a predefined analysis, probably the most useful one. Other examples are the analyses that find unused local functions, or functions that call some given functions. See the analyze/2,3 functions for a complete list of predefined analyses.

Each predefined analysis is a shorthand for a query, a sentence of a tiny language providing cross reference data as values of predefined variables. The check for calls to undefined functions can thus be stated as a query:

4> xref:q(s, "(XC - UC) || (XU - X - B)").
{ok,[...]}

The query asks for the restriction of external calls except the unresolved calls to calls to functions that are externally used but neither exported nor built-in functions (the || operator restricts the used functions while the | operator restricts the calling functions). The - operator returns the difference of two sets, and the + operator to be used below returns the union of two sets.

The relationships between the predefined variables XU, X, B and a few others are worth elaborating upon. The reference manual mentions two ways of expressing the set of all functions, one that focuses on how they are defined: X + L + B + U, and one that focuses on how they are used: UU + LU + XU. The reference also mentions some facts about the variables:

F is equal to L + X (the defined functions are the local functions and the external functions);

U is a subset of XU (the unknown functions are a subset of the externally used functions since the compiler ensures that locally used functions are defined);

B is a subset of XU (calls to built-in functions are always external by definition, and unused built-in functions are ignored);

LU is a subset of F (the locally used functions are either local functions or exported functions, again ensured by the compiler);

UU is equal to F - (XU + LU) (the unused functions are defined functions that are neither used externally nor locally);

UU is a subset of F (the unused functions are defined in analyzed modules).

Using these facts, the two small circles in the picture below can be combined.

It is often clarifying to mark the variables of a query in such a circle. This is illustrated in the picture below for some of the predefined analyses. Note that local functions used by local functions only are not marked in the locals_not_used circle.

Expressions

The module check and the predefined analyses are useful, but limited. Sometimes more flexibility is needed, for instance one might not need to apply a graph analysis on all calls, but some subset will do equally well. That flexibility is provided with a simple language. Below are some expressions of the language with comments, focusing on elements of the language rather than providing useful examples. The analyzed system is assumed to be OTP, so in order to run the queries, first evaluate these calls:

xref:start(s).
xref:add_release(s, code:root_dir()).

xref:q(s, "(Fun) xref : Mod"). - All functions of the xref module.

xref:q(s, "xref : Mod * X"). - All exported functions of the xref module. The first operand of the intersection operator * is implicitly converted to the more special type of the second operand.

xref:q(s, "(Mod) tools"). - All modules of the Tools application.

xref:q(s, '"xref_.*" : Mod'). - All modules with a name beginning with xref_.

xref:q(s, "# E | X "). - Number of calls from exported functions.

xref:q(s, "XC || L "). - All external calls to local functions.

xref:q(s, "XC * LC"). - All calls that have both an external and a local version.

xref:q(s, "(LLin) (LC * XC)"). - The lines where the local calls of the last example are made.

xref:q(s, "(XLin) (LC * XC)"). - The lines where the external calls of the example before last are made.

xref:q(s, "XC * (ME - strict ME)"). - External calls within some module.

xref:q(s, "E ||| kernel"). - All calls within the Kernel application.

xref:q(s, "closure E | kernel || kernel"). - All direct and indirect calls within the Kernel application. Both the calling and the used functions of indirect calls are defined in modules of the kernel application, but it is possible that some functions outside the kernel application are used by indirect calls.

xref:q(s, "{toolbar,debugger}:Mod of ME"). - A chain of module calls from toolbar to debugger, if there is such a chain, otherwise false. The chain of calls is represented by a list of modules, toolbar being the first element and debuggerthe last element.

xref:q(s, "closure E | toolbar:Mod || debugger:Mod"). - All (in)direct calls from functions in toolbar to functions in debugger.

xref:q(s, "(Fun) xref -> xref_base"). - All function calls from xref to xref_base.

xref:q(s, "E * xref -> xref_base"). - Same interpretation as last expression.

xref:q(s, "E || xref_base | xref"). - Same interpretation as last expression.

xref:q(s, "E * [xref -> lists, xref_base -> digraph]"). - All function calls from xref to lists, and all function calls from xref_base to digraph.

xref:q(s, "E | [xref, xref_base] || [lists, digraph]"). - All function calls from xref and xref_base to lists and digraph.

xref:q(s, "components EE"). - All strongly connected components of the Inter Call Graph. Each component is a set of exported or unused local functions that call each other (in)directly.

xref:q(s, "X * digraph * range (closure (E | digraph) | (L * digraph))"). - All exported functions of the digraph module used (in)directly by some function in digraph.

xref:q(s, "L * yeccparser:Mod - range (closure (E |

yeccparser:Mod) | (X * yeccparser:Mod))"). - The interpretation is left as an exercise.

Graph Analysis

The list representation of graphs is used analyzing direct calls, while the digraph representation is suited for analyzing indirect calls. The restriction operators (|, || and |||) are the only operators that accept both representations. This means that in order to analyze indirect calls using restriction, the closure operator (which creates the digraph representation of graphs) has to be explicitly applied.

As an example of analyzing indirect calls, the following Erlang function tries to answer the question: if we want to know which modules are used indirectly by some module(s), is it worth while using the function graph rather than the module graph? Recall that a module M1 is said to call a module M2 if there is some function in M1 that calls some function in M2. It would be nice if we could use the much smaller module graph, since it is available also in the light weight modulesmode of Xref servers.

t(S) ->
  {ok, _} = xref:q(S, "Eplus := closure E"),
  {ok, Ms} = xref:q(S, "AM"),
  Fun = fun(M, N) ->
      Q = io_lib:format("# (Mod) (Eplus | ~p : Mod)", [M]),
      {ok, N0} = xref:q(S, lists:flatten(Q)),
      N + N0
    end,
  Sum = lists:foldl(Fun, 0, Ms),
  ok = xref:forget(S, 'Eplus'),
  {ok, Tot} = xref:q(S, "# (closure ME | AM)"),
  100 * ((Tot - Sum) / Tot).

Comments on the code:

We want to find the reduction of the closure of the function graph to modules. The direct expression for doing that would be (Mod) (closure E | AM), but then we would have to represent all of the transitive closure of E in memory. Instead the number of indirectly used modules is found for each analyzed module, and the sum over all modules is calculated.

A user variable is employed for holding the digraph representation of the function graph for use in many queries. The reason is efficiency. As opposed to the = operator, the := operator saves a value for subsequent analyses. Here might be the place to note that equal subexpressions within a query are evaluated only once; = cannot be used for speeding things up.

Eplus | ~p : Mod. The | operator converts the second operand to the type of the first operand. In this case the module is converted to all functions of the module. It is necessary to assign a type to the module (: Mod), otherwise modules like kernel would be converted to all functions of the application with the same name; the most general constant is used in cases of ambiguity.

Since we are only interested in a ratio, the unary operator # that counts the elements of the operand is used. It cannot be applied to the digraph representation of graphs.

We could find the size of the closure of the module graph with a loop similar to one used for the function graph, but since the module graph is so much smaller, a more direct method is feasible.

When the Erlang function t/1 was applied to an Xref server loaded with the current version of OTP, the returned value was close to 84 (percent). This means that the number of indirectly used modules is approximately six times greater when using the module graph. So the answer to the above stated question is that it is definitely worth while using the function graph for this particular analysis. Finally, note that in the presence of unresolved calls, the graphs may be incomplete, which means that there may be indirectly used modules that do not show up.

Erlang mode for Emacs

Possibly the most important feature of an editor designed for programmers is the ability to indent a line of code in accordance with the structure of the programming language. The Erlang mode does, of course, provide this feature. The layout used is based on the common use of the language. The mode also provides things as syntax highlighting, electric commands, module name verification, comment support including paragraph filling, skeletons, tags support etc.

In the following descriptions the use of the word Point means: "Point can be seen as the position of the cursor. More precisely, the point is the position between two characters while the cursor is drawn over the character following the point".

Indent

The following command are directly available for indentation.

TAB (erlang-indent-command) - Indents the current line of code.

M-C-\ (indent-region) - Indents all lines in the region.

M-l (indent-for-comment) - Insert a comment character to the right of the code on the line (if any).

Lines containing comment are indented differently depending on the number of %-characters used:

Lines with one %-character is indented to the right of the code. The column is specified by the variable comment-column, by default column 48 is used.

Lines with two %-characters will be indented to the same depth as code would have been in the same situation.

Lines with three of more %-characters are indented to the left margin.

C-c C-q (erlang-indent-function) - Indents the current Erlang function.

M-x erlang-indent-clause RET - Indent the current Erlang clause.

M-x erlang-indent-current-buffer RET - Indent the entire buffer.

Edit - Fill Comment

When editing normal text in text mode you can let Emacs reformat the text by the fill-paragraph command. This command will not work for comments since it will treat the comment characters as words. The Erlang editing mode provides a command that knows about the Erlang comment structure and can be used to fill text paragraphs in comments. Ex:

%% This is   just a very simple test to show
%% how the Erlang fill
%% paragraph   command works.

Clearly, the text is badly formatted. Instead of formatting this paragraph line by line, let's try erlang-fill-paragraph by pressing M-q. The result is:

%% This is just a very simple test to show how the Erlang fill
%% paragraph command works.

Edit - Comment/Uncomment Region

C-c C-c will put comment characters at the beginning of all lines in a marked region. If you want to have two comment characters instead of one you can do C-u 2 C-c C-c

C-c C-u will undo a comment-region command.

Edit - Moving the point

M-C-a (erlang-beginning-of-function) - Move the point to the beginning of the current or preceding Erlang function. With an numeric argument (ex C-u 2 M-C-a) the function skips backwards over this many Erlang functions. Should the argument be negative the point is moved to the beginning of a function below the current function.

C-c M-a (erlang-beginning-of-clause) - As above but move point to the beginning of the current or preceding Erlang clause.

M-C-e (erlang-end-of-function) - Move to the end of the current or following Erlang function. With an numeric argument (ex C-u 2 M-C-e) the function skips backwards over this many Erlang functions. Should the argument be negative the point is moved to the end of a function below the current function.

C-c M-e (erlang-end-of-clause) - As above but move point to the end of the current or following Erlang clause.

Edit - Marking

M-C-h (erlang-mark-function) - Put the region around the current Erlang function. The point is placed in the beginning and the mark at the end of the function.

C-c M-h (erlang-mark-clause) Put the region around the current Erlang clause. The point is placed in the beginning and the mark at the end of the function.

Edit - Function Header Commands

C-c C-j (erlang-generate-new-clause) - Create a new clause in the current Erlang function. The point is placed between the parentheses of the argument list.

C-c C-y (erlang-clone-arguments) - Copy the function arguments of the preceding Erlang clause. This command is useful when defining a new clause with almost the same argument as the preceding.

Edit - Alignment

C-c C-a (align-current) - aligns comments, arrows, assignments, and type annotations around the cursor.

Example:

sum(L) -> sum(L, 0).
sum([H|T], Sum) -> sum(T, Sum + H);  % recurse
sum([], Sum) -> Sum.   % base case

-record { two :: int(), % hello
          three = hello :: string(),    % there
          four = 42 :: int() }.

becomes:

sum(L) -> sum(L, 0).
sum([H|T], Sum) -> sum(T, Sum + H); % recurse
sum([], Sum)    -> Sum.             % base case

-record { two           :: int(),    % hello
          three = hello :: string(), % there
          four  = 42    :: int() }.

Syntax highlighting

The syntax highlighting can be activated from the Erlang menu. There are four different alternatives:

Off: Normal black and white display.

Level 1: Function headers, reserved words, comments, strings, quoted atoms, and character constants will be colored.

Level 2: The above, attributes, Erlang bif:s, guards, and words in comments enclosed in single quotes will be colored.

Level 3: The above, variables, records, and macros will be colored. (This level is also known as the Christmas tree level.)

Tags

For the tag commands to work it requires that you have generated a tag file. See Erlang mode users guide

M-. (find-tag) - Find a function definition. The default value is the function name under the point.

Find Tag (erlang-find-tag) - Like the Elisp-function find-tag'. Capable of retrieving Erlang modules. Tags can be given of the formstag', module:',module:tag'.

M-+ (erlang-find-next-tag) - Find the next occurrence of tag.

M-TAB (erlang-complete-tag) - Perform completion on the tag entered in a tag search. Completes to the set of names listed in the current tags table.

Tags aprops (tags-apropos) - Display list of all tags in tags table REGEXP matches.

C-x t s (tags-search) - Search through all files listed in tags table for match for REGEXP. Stops when a match is found.

Skeletons

A skeleton is a piece of pre-written code that can be inserted into the buffer. Erlang mode comes with a set of predefined skeletons. The skeletons can be accessed either from the Erlang menu of from commands named tempo-template-erlang-*, as the skeletons is defined using the standard Emacs package "tempo". Here follows a brief description of the available skeletons:

Simple skeletons: If, Case, Receive, Receive After, Receive Loop - Basic code constructs.

Header elements: Module, Author - These commands insert lines on the form -module('xxx'). and -author('my@home').. They can be used directly, but are also used as part of the full headers described below.

Full Headers: Small (minimum requirement), Medium (with fields for basic information about the module), and Large Header (medium header with some extra layout structure).

Small Server - skeleton for a simple server not using OTP.

Application - skeletons for the OTP application behavior

Supervisor - skeleton for the OTP supervisor behavior

Supervisor Bridge - skeleton for the OTP supervisor bridge behavior

gen_server - skeleton for the OTP gen_server behavior

gen_event - skeleton for the OTP gen_event behavior

gen_fsm - skeleton for the OTP gen_fsm behavior

gen_statem (StateName/3) - skeleton for the OTP gen_statem behavior using state name functions

gen_statem (handle_event/4) - skeleton for the OTP gen_statem behavior using one state function

Library module - skeleton for a module that does not implement a process.

Corba callback - skeleton for a Corba callback module.

Erlang test suite - skeleton for a callback module for the erlang test server.

Shell

New shell (erlang-shell) - Starts a new Erlang shell.

C-c C-z, (erlang-shell-display) - Displays an Erlang shell, or starts a new one if there is no shell started.

Compile

C-c C-k, (erlang-compile) - Compiles the Erlang module in the current buffer. You can also use C-u C-c C-k to debug compile the module with the debug options debug_info and export_all.

C-c C-l, (erlang-compile-display) - Display compilation output.

C-u C-x` Start parsing the compiler output from the beginning. This command will place the point on the line where the first error was found.

C-x` (erlang-next-error) - Move the point on to the next error. The buffer displaying the compilation errors will be updated so that the current error will be visible.

Man

On unix you can view the manual pages in emacs. In order to find the manual pages, the variable erlang-root-dir should be bound to the name of the directory containing the Erlang installation. The name should not include the final slash. Practically, you should add a line on the following form to your ~/.emacs,

(setq erlang-root-dir "/the/erlang/root/dir/goes/here")

Starting IMenu

M-x imenu-add-to-menubar RET - This command will create the IMenu menu containing all the functions in the current buffer.The command will ask you for a suitable name for the menu. Not supported by Xemacs.

Version

M-x erlang-version RET - This command displays the version number of the Erlang editing mode. Remember to always supply the version number when asking questions about the Erlang mode.

cprof

A simple Call Count Profiling Tool using breakpoints for minimal runtime performance impact.

The cprof module is used to profile a program to find out how many times different functions are called. To minimize runtime performance impact, breakpoints containing counters are used.

Since breakpoints are used there is no need for special compilation of the modules to be profiled. These breakpoints can only be set on BEAM code, so BIFs cannot be call-count traced.

The size of the call counters is the host machine word size. One bit is used when pausing the counter, so the maximum counter value for a 32-bit host is 2,147,483,647.

The profiling result is delivered as a term containing a sorted list of entries, one per module. Each module entry contains a sorted list of functions. The sorting order in both cases is of decreasing call count.

Call count tracing is lightweight compared to other forms of tracing, such as eprof or fprof, since no trace messages have to be generated. Some measurements indicates that the performance degradation is about 10 percent.

For more information and some examples, see the User's Guide for cprof.

Summary

Types

func_analysis_list()

mod_analysis()

mod_analysis_list()

Functions

analyse()

Equivalent to analyse(1).

analyse(ModLimit)

Collect call counters for one or more modules.

analyse(Module, Limit)

Collects and analyses all call counters for module Module.

pause()

Pause call count tracing for all functions in all modules and stop it for all functions in modules to be loaded.

pause(FuncSpec)

If FuncSpec is an atom, it is assumed to be a module name, and this call is equivalent to pause(FuncSpec, '_', '_').

pause(Mod, Func)

Equivalent to pause(Mod, Func, '_').

pause(Mod, Func, Arity)

Pause call counters for matching functions in matching modules.

restart()

Equivalent to restart('_', '_', '_').

restart(FuncSpec)

If FuncSpec is an atom, it is assumed to be a module name, and this call is equivalent to restart(FuncSpec, '_', '_').

restart(Mod, Func)

Equivalent to restart(Mod, Func, '_').

restart(Mod, Func, Arity)

Restart call counters for the matching functions in matching modules that are call-count traced.

start()

Start call count tracing for all functions in all modules, and also for all functions in modules to be loaded.

start(FuncSpec)

If FuncSpec is an atom, it is assumed to be a module name, and this call is equivalent to start(FuncSpec, '_', '_').

start(Mod, Func)

Equivalent to start(Mod, Func, '_').

start(Mod, Func, Arity)

Start call count tracing for matching functions in matching modules.

stop()

Stop call count tracing for all functions in all modules, and also for all functions in modules to be loaded.

stop(FuncSpec)

If FuncSpec is an atom, it is assumed to be a module name, and this call is equivalent to stop(FuncSpec, '_', '_').

stop(Mod, Func)

Equivalent to stop(Mod, Func, '_').

stop(Mod, Func, Arity)

Stop call count tracing for matching functions in matching modules.

Types

func_analysis_list()

(not exported)

-type func_analysis_list() :: [{mfa(), FuncCallCount :: non_neg_integer()}].

mod_analysis()

(not exported)

-type mod_analysis() ::
          {Mod :: module(), ModCallCount :: non_neg_integer(), FuncAnalysisList :: func_analysis_list()}.

mod_analysis_list()

(not exported)

-type mod_analysis_list() :: [mod_analysis()].

Functions

analyse()

-spec analyse() -> {AllCallCount :: non_neg_integer(), ModAnalysisList :: mod_analysis_list()}.

Equivalent to analyse(1).

analyse(ModLimit)

-spec analyse(Limit) -> {AllCallCount :: non_neg_integer(), ModAnalysisList :: mod_analysis_list()}
                 when Limit :: non_neg_integer();
             (Mod) -> ModAnalysis :: mod_analysis() when Mod :: module().

Collect call counters for one or more modules.

If ModLimit is a module name (an atom), this call is equivalent to analyse(ModLimit, 1).

If ModLimit is an integer, this function calls analyse(Module, ModLimit) for each Module that is currently loaded (except the cprof module itself). The result from those calls are returned in a list.

analyse(Module, Limit)

-spec analyse(Mod, Limit) -> ModAnalysis :: mod_analysis()
                 when Mod :: module(), Limit :: non_neg_integer().

Collects and analyses all call counters for module Module.

This function returns:

{Module, ModuleCount, FuncAnalysisList}

where FuncAnalysisList is a list of tuples, one for each function:

{{Module, FunctionName, Arity}, FuncCallCount}

If call counters are still running while analyse/0,1,2 is executing, the result could be inconsistent. This happens if the process executing analyse/0,1,2 is scheduled out so some other process can increment the counters that are being analysed. Calling pause() before analysing takes care of that problem.

All functions with a FuncCallCount lower than Limit are excluded from FuncAnalysisList. They are still included in ModCallCount, though.

pause()

-spec pause() -> non_neg_integer().

Pause call count tracing for all functions in all modules and stop it for all functions in modules to be loaded.

This call is equivalent to pause('_', '_', '_') + stop({on_load}).

pause(FuncSpec)

-spec pause(FuncSpec) -> non_neg_integer() when FuncSpec :: (Mod :: module()) | mfa() | {FS :: term()}.

If FuncSpec is an atom, it is assumed to be a module name, and this call is equivalent to pause(FuncSpec, '_', '_').

If FuncSpec is an MFA tuple, {Module, Name, Arity}, this call is equivalent to pause(Module, Name, Arity).

If FuncSpec is tuple {FS}, FS is the first argument to erlang:trace_pattern/3. For example, if FuncSpec is {on_load}, call counters will be paused for all functions in modules to be loaded.

pause(Mod, Func)

-spec pause(Mod, Func) -> non_neg_integer() when Mod :: module(), Func :: atom().

Equivalent to pause(Mod, Func, '_').

pause(Mod, Func, Arity)

-spec pause(Mod, Func, Arity) -> non_neg_integer()
               when Mod :: module(), Func :: atom(), Arity :: arity().

Pause call counters for matching functions in matching modules.

The call counters for all matching functions that have call count breakpoints are paused at their current count.

Return the number of matching functions that can have call count breakpoints, the same as start/* with the same arguments would have returned.

restart()

-spec restart() -> non_neg_integer().

Equivalent to restart('_', '_', '_').

restart(FuncSpec)

-spec restart(FuncSpec) -> non_neg_integer() when FuncSpec :: (Mod :: module()) | mfa() | {FS :: term()}.

If FuncSpec is an atom, it is assumed to be a module name, and this call is equivalent to restart(FuncSpec, '_', '_').

If FuncSpec is an MFA tuple, {Module, Name, Arity}, this call is equivalent to restart(Module, Name, Arity).

If FuncSpec is tuple {FS}, FS is the first argument to erlang:trace_pattern/3. For example, if FuncSpec is {on_load}, call counters will be set to zero and running for all functions in modules to be loaded.

restart(Mod, Func)

-spec restart(Mod, Func) -> non_neg_integer() when Mod :: module(), Func :: atom().

Equivalent to restart(Mod, Func, '_').

restart(Mod, Func, Arity)

-spec restart(Mod, Func, Arity) -> non_neg_integer()
                 when Mod :: module(), Func :: atom(), Arity :: arity().

Restart call counters for the matching functions in matching modules that are call-count traced.

The call counters for all matching functions that has call count breakpoints are set to zero and running.

Return the number of matching functions that can have call count breakpoints, the same as start/* with the same arguments would have returned.

start()

-spec start() -> non_neg_integer().

Start call count tracing for all functions in all modules, and also for all functions in modules to be loaded.

This is equivalent to start('_', '_', '_') + start({on_load}).

start(FuncSpec)

-spec start(FuncSpec) -> non_neg_integer() when FuncSpec :: (Mod :: module()) | mfa() | {FS :: term()}.

If FuncSpec is an atom, it is assumed to be a module name, and this call is equivalent to start(FuncSpec, '_', '_').

If FuncSpec is an MFA tuple, {Module, Name, Arity}, this call is equivalent to start(Module, Name, Arity).

If FuncSpec is tuple {FS}, FS is the first argument to erlang:trace_pattern/3. For example, if FuncSpec is {on_load}, call counters will be set to zero and running for all functions in modules to be loaded.

start(Mod, Func)

-spec start(Mod, Func) -> non_neg_integer() when Mod :: module(), Func :: atom().

Equivalent to start(Mod, Func, '_').

start(Mod, Func, Arity)

-spec start(Mod, Func, Arity) -> non_neg_integer()
               when Mod :: module(), Func :: atom(), Arity :: arity().

Start call count tracing for matching functions in matching modules.

Set call count breakpoints on the matching functions that has no call count breakpoints. Call counters are set to zero and running for all matching functions.

Return the number of matching functions that has call count breakpoints.

stop()

-spec stop() -> non_neg_integer().

Stop call count tracing for all functions in all modules, and also for all functions in modules to be loaded.

This is equivalent to stop('_', '_', '_') + stop({on_load}).

stop(FuncSpec)

-spec stop(FuncSpec) -> non_neg_integer() when FuncSpec :: (Mod :: module()) | mfa() | {FS :: term()}.

If FuncSpec is an atom, it is assumed to be a module name, and this call is equivalent to stop(FuncSpec, '_', '_').

If FuncSpec is an MFA tuple, {Module, Name, Arity}, this call is equivalent to stop(Module, Name, Arity).

If FuncSpec is tuple {FS}, FS is the first argument to erlang:trace_pattern/3. For example, if FuncSpec is {on_load}, call counters be disabled for all functions in modules to be loaded.

stop(Mod, Func)

-spec stop(Mod, Func) -> non_neg_integer() when Mod :: module(), Func :: atom().

Equivalent to stop(Mod, Func, '_').

stop(Mod, Func, Arity)

-spec stop(Mod, Func, Arity) -> non_neg_integer() when Mod :: module(), Func :: atom(), Arity :: arity().

Stop call count tracing for matching functions in matching modules.

Remove call count breakpoints from the matching functions that has call count breakpoints.

Return the number of matching functions that can have call count breakpoints, which is the same as start/* with the same arguments would have returned.

eprof

A Time Profiling Tool for Erlang

The module eprof provides a set of functions for time profiling of Erlang programs to find out how the execution time is used. The profiling is done using the Erlang trace BIFs. Tracing of local function calls for a specified set of processes is enabled when profiling is begun, and disabled when profiling is stopped.

When using Eprof, expect a slowdown in program execution.

Summary

Types

analyze_type()

trace_pattern_mfa()

Functions

analyze()

Equivalent to analyze(procs, []).

analyze(TypeOpts)

If TypeOpts is an atom, it is assumed to be a module name, and this call is equivalent to analyze(TypeOpts, []).

analyze(Type, Options)

Call this function when profiling has been stopped to display the results.

log(File)

Call this function to ensure that the results displayed by analyze/0,1,2 are printed to the file File as well as to the screen.

profile(FunRootset)

If FunRootset is a fun, this call is equivalent to profile([], FunRootset).

profile(Arg1, Arg2)

If Arg1 is a fun and Arg2 is list, this call is equivalent to profile([], Arg1, {'_','_','_'}, Arg2).

profile(Rootset, Fun, Pattern)

Equivalent to profile(Rootset, Fun, Pattern, [{set_on_spawn, true}]).

profile(Rootset, Arg1, Arg2, Arg3)

This function spawns a process that applies a fun or an an function, and then starts profiling for the spawned proceses as well as the processes in Rootset (and any new processes spawned from them).

profile(Rootset, Module, Function, Args, Pattern)

Equivalent to profile(Rootset, Module, Function, Args, Pattern, [{set_on_spawn, true}]).

profile(Rootset, Module, Function, Args, Pattern, Options)

This function spawns a process P that apply(Module, Function, Args), and then starts profiling for P and the processes in Rootset (and any new processes spawned from them).

start()

Starts the Eprof server which holds the internal state of the collected data.

start_profiling(Rootset)

Equivalent to start_profiling(Rootset, {'_', '_', '_'}).

start_profiling(Rootset, Pattern)

Equivalent to start_profiling(Rootset, Pattern, {'_', '_', '_'}).

start_profiling(Rootset, Pattern, Options)

Starts profiling for the processes in Rootset (and any new processes spawned from them).

stop()

Stops the Eprof server.

stop_profiling()

Stops profiling started with start_profiling/1 or profile/1.

Types

analyze_type()

(not exported)

-type analyze_type() :: procs | total.

trace_pattern_mfa()

(not exported)

-type trace_pattern_mfa() :: {atom(), atom(), arity() | '_'}.

Functions

analyze()

(since OTP R14B)

-spec analyze() -> ok | nothing_to_analyze.

Equivalent to analyze(procs, []).

analyze(TypeOpts)

(since OTP R14B)

-spec analyze(TypeOpts) -> ok | nothing_to_analyze when TypeOpts :: analyze_type().

If TypeOpts is an atom, it is assumed to be a module name, and this call is equivalent to analyze(TypeOpts, []).

Otherwise, if TypeOpts is a list, it assumed to be a list of options, and this call is equivalent to analyze(procs, TypeOpts).

analyze(Type, Options)

(since OTP R14B)

-spec analyze(Type, Options) -> ok | nothing_to_analyze
                 when
                     Type :: analyze_type(),
                     Options :: [Option],
                     Option :: {filter, Filter} | {sort, Sort},
                     Filter :: [{calls, non_neg_integer()} | {time, float()}],
                     Sort :: time | calls | mfa.

Call this function when profiling has been stopped to display the results.

If Type is procs, the time spent in each function is shown separately for each profiled process.

If Type is total, the time spent in each function is shown combined for each profiled process.

Time is shown as percentage of total time and as absolute time in micro seconds.

log(File)

-spec log(File) -> ok when File :: atom() | file:filename().

Call this function to ensure that the results displayed by analyze/0,1,2 are printed to the file File as well as to the screen.

profile(FunRootset)

-spec profile(Fun) -> {ok, Value} | {error, Reason}
                 when Fun :: fun(() -> term()), Value :: term(), Reason :: term();
             (Rootset) -> profiling | {error, Reason} when Rootset :: [atom() | pid()], Reason :: term().

If FunRootset is a fun, this call is equivalent to profile([], FunRootset).

If FunRootset is a list, it is assumed to be a Rootset, and this call is equivalent to start_profiling(Rootset).

profile(Arg1, Arg2)

-spec profile(Fun, Options) -> {ok, Value} | {error, Reason}
                 when
                     Fun :: fun(() -> term()),
                     Options :: [set_on_spawn | {set_on_spawn, boolean()}],
                     Value :: term(),
                     Reason :: term();
             (Rootset, Fun) -> {ok, Value} | {error, Reason}
                 when
                     Rootset :: [atom() | pid()],
                     Fun :: fun(() -> term()),
                     Value :: term(),
                     Reason :: term().

If Arg1 is a fun and Arg2 is list, this call is equivalent to profile([], Arg1, {'_','_','_'}, Arg2).

If Arg1 is a list and Arg2 is a fun, this call is equivalent to profile(Arg1, Arg2, {'_','_','_'}, Arg1).

profile(Rootset, Fun, Pattern)

(since OTP R14B)

-spec profile(Rootset, Fun, Pattern) -> {ok, Value} | {error, Reason}
                 when
                     Rootset :: [atom() | pid()],
                     Fun :: fun(() -> term()),
                     Pattern :: trace_pattern_mfa(),
                     Value :: term(),
                     Reason :: term().

Equivalent to profile(Rootset, Fun, Pattern, [{set_on_spawn, true}]).

profile(Rootset, Arg1, Arg2, Arg3)

-spec profile(Rootset, Module, Function, Args) -> {ok, Value} | {error, Reason}
                 when
                     Rootset :: [atom() | pid()],
                     Module :: module(),
                     Function :: atom(),
                     Args :: [term()],
                     Value :: term(),
                     Reason :: term();
             (Rootset, Fun, Pattern, Options) -> {ok, Value} | {error, Reason}
                 when
                     Rootset :: [atom() | pid()],
                     Fun :: fun(() -> term()),
                     Pattern :: trace_pattern_mfa(),
                     Options :: [set_on_spawn | {set_on_spawn, boolean()}],
                     Value :: term(),
                     Reason :: term().

This function spawns a process that applies a fun or an an function, and then starts profiling for the spawned proceses as well as the processes in Rootset (and any new processes spawned from them).

If Arg1 is a fun, Arg2 is expected to be a trace pattern, and Arg3 a list of options. In that case, this call is equivalent to:

profile(Rootset, erlang, apply, [Arg1, []], Arg2, Arg3)

If Arg1 is an atom, Arg1 is assumed to be a module name, Arg2 the name of the function in that module, and Arg3 a list of arguments to be used when calling that function. In that case, this call is equivalent to:

profile(Rootset, Arg1, Arg2, Arg3, {'_','_','_'}, [{set_on_spawn, true}])

profile(Rootset, Module, Function, Args, Pattern)

(since OTP R14B)

-spec profile(Rootset, Module, Function, Args, Pattern) -> {ok, Value} | {error, Reason}
                 when
                     Rootset :: [atom() | pid()],
                     Module :: module(),
                     Function :: atom(),
                     Args :: [term()],
                     Pattern :: trace_pattern_mfa(),
                     Value :: term(),
                     Reason :: term().

Equivalent to profile(Rootset, Module, Function, Args, Pattern, [{set_on_spawn, true}]).

profile(Rootset, Module, Function, Args, Pattern, Options)

(since OTP R16B01)

-spec profile(Rootset, Module, Function, Args, Pattern, Options) -> {ok, Value} | {error, Reason}
                 when
                     Rootset :: [atom() | pid()],
                     Module :: module(),
                     Function :: atom(),
                     Args :: [term()],
                     Pattern :: trace_pattern_mfa(),
                     Options :: [set_on_spawn | {set_on_spawn, boolean()}],
                     Value :: term(),
                     Reason :: term().

This function spawns a process P that apply(Module, Function, Args), and then starts profiling for P and the processes in Rootset (and any new processes spawned from them).

Rootset is a list of pids and registered names.

Information about activity in any profiled process is stored in the Eprof database.

If tracing could be enabled for P and all processes in Rootset, the function returns {ok,Value} when Fun()/apply returns with the value Value, or {error,Reason} if Fun()/apply fails with exit reason Reason. Otherwise it returns {error, Reason} immediately.

The set_on_spawn option will active call time tracing for all processes spawned by processes in the rootset. This is the default behaviour.

The programmer must ensure that the function given as argument is truly synchronous and that no work continues after the function has returned a value.

start()

-spec start() -> {ok, Pid} | {error, Reason} when Pid :: pid(), Reason :: {already_started, Pid}.

Starts the Eprof server which holds the internal state of the collected data.

start_profiling(Rootset)

-spec start_profiling(Rootset) -> profiling | {error, Reason}
                         when Rootset :: [atom() | pid()], Reason :: term().

Equivalent to start_profiling(Rootset, {'_', '_', '_'}).

start_profiling(Rootset, Pattern)

(since OTP R14B)

-spec start_profiling(Rootset, Pattern) -> profiling | {error, Reason}
                         when
                             Rootset :: [atom() | pid()],
                             Pattern :: trace_pattern_mfa(),
                             Reason :: term().

Equivalent to start_profiling(Rootset, Pattern, {'_', '_', '_'}).

start_profiling(Rootset, Pattern, Options)

(since OTP R16B01)

-spec start_profiling(Rootset, Pattern, Options) -> profiling | {error, Reason}
                         when
                             Rootset :: [atom() | pid()],
                             Pattern :: trace_pattern_mfa(),
                             Options :: [set_on_spawn | {set_on_spawn, boolean()}],
                             Reason :: term().

Starts profiling for the processes in Rootset (and any new processes spawned from them).

Information about activity in any profiled process is stored in the Eprof database.

Rootset is a list of pids and registered names.

The function returns profiling if tracing could be enabled for all processes in Rootset, or error otherwise.

A pattern can be selected to narrow the profiling. For instance a specific module can be selected, and only the code executed in that module will be profiled.

The set_on_spawn option will active call time tracing for all processes spawned by processes in the rootset. This is the default behaviour.

stop()

-spec stop() -> stopped.

Stops the Eprof server.

stop_profiling()

-spec stop_profiling() -> profiling_stopped | profiling_already_stopped.

Stops profiling started with start_profiling/1 or profile/1.

fprof

A Time Profiling Tool using trace to file for minimal runtime performance impact.

This module is used to profile a program to find out how the execution time is used. Tracing to file is used to minimize the runtime performance degradation.

The fprof module uses tracing to collect profiling data, hence there is no need for special compilation of any module to be profiled. When it starts tracing, fprof will erase all previous tracing in the node and set the necessary trace flags on the profiling target processes as well as local call trace on all functions in all loaded modules and all modules to be loaded. fprof disable all tracing in the node when it stops tracing.

fprof presents both own time that is, how much time a function has used for its own execution, and accumulated time that is, including called functions. All presented times are collected using trace timestamps. fprof tries to collect CPU time timestamps, if the host machine OS supports it. Therefore, the times can be wallclock times and OS scheduling will randomly strike all called functions in a presumably fair way.

However, if the profiling time is short, and the host machine OS does not support high resolution CPU time measurements, a few OS schedulings can show up as ridiculously long execution times for functions doing practically nothing. As an example, it has been observed that a function that more or less just composing a tuple, was running 100 times slower than normal. When tracing was repeated, the execution time was normal.

Profiling is essentially done in 3 steps:

Tracing: to a file. The trace data contains entries for function calls, returns to function, process scheduling, other process related events (for example spawn), and garbage collection. All trace entries are timestamped.

Profiling: the trace file is read, the execution call stack is simulated, and raw profile data is calculated from the simulated call stack and the trace timestamps. The profile data is stored in the fprof server state. During this step the trace data may be dumped in text format to file or console.

Analysing: the raw profile data is sorted, filtered and dumped in text format either to file or console. The text format intended to be both readable for a human reader, as well as parsable with the standard erlang parsing tools.

Since fprof sends trace data to afile, the runtime performance degradation is minimized, but still far from negligible, especially for programs that themselves use the filesystem heavily. Where the trace file is placed is also important, for example, on Unix systems /tmp is usually a good choice since it is essentially a RAM disk, while any network-mounted disk is a bad idea.

fprof can also skip the file step and trace to a tracer process that does the profiling in runtime.

Analysis format

This section describes the output format of the analyse/1 function.

The format is parsable with the standard Erlang parsing tools erl_scan and erl_parse, file:consult/1, or io:read/2. The parse format is not described here — it should be easy enough for the interested reader to try it out. Note that some flags to analyse/1 will affect the format.

The following example was run on Erlang/OTP R8 on Solaris 8; all OTP internals in this example are version dependent.

As an example, we will use the following function, which is a slightly modified benchmark function from module file:

-module(foo).
-export([create_file_slow/2]).

create_file_slow(Name, N) when is_integer(N), N >= 0 ->
    {ok, FD} =
        file:open(Name, [raw, write, delayed_write, binary]),
    if N > 256 ->
            ok = file:write(FD,
                            lists:map(fun (X) -> <<X:32/unsigned>> end,
                            lists:seq(0, 255))),
            ok = create_file_slow(FD, 256, N);
       true ->
            ok = create_file_slow(FD, 0, N)
    end,
    ok = file:close(FD).

create_file_slow(FD, M, M) ->
    ok;
create_file_slow(FD, M, N) ->
    ok = file:write(FD, <<M:32/unsigned>>),
    create_file_slow(FD, M+1, N).

Let us have a look at the printout after running:

1> fprof:apply(foo, create_file_slow, [junk, 1024]).
2> fprof:profile().
3> fprof:analyse().

The printout starts with:

%% Analysis results:
{  analysis_options,
 [{callers, true},
  {sort, acc},
  {totals, false},
  {details, true}]}.

%                                       CNT       ACC       OWN
[{ totals,                             9627, 1691.119, 1659.074}].  %%%

The CNT column shows the total number of function calls that was found in the trace. In the ACC column is the total time of the trace from first timestamp to last. And in the OWN column is the sum of the execution time in functions found in the trace, not including called functions. In this case it is very close to the ACC time since the emulator had practically nothing to do except executing our test program.

All time values in the printout are in milliseconds.

The printout continues:

%                                       CNT       ACC       OWN
[{ "<0.28.0>",                         9627,undefined, 1659.074}].   %%

This is the printout header of one process. The printout contains only this one process since we called fprof:apply/3 that traces only the current process. Therefore the CNT and OWN columns perfectly matches the totals above. The ACC column is undefined since summing the ACC times of all calls in the process makes no sense — one would get something like the ACC value from totals above multiplied by the average depth of the call stack.

All paragraphs up to the next process header only concerns function calls within this process.

Now we come to something more interesting:

{[{undefined,                             0, 1691.076,    0.030}],
 { {fprof,apply_start_stop,4},            0, 1691.076,    0.030},     %
 [{{foo,create_file_slow,2},              1, 1691.046,    0.103},
  {suspend,                               1,    0.000,    0.000}]}.

{[{{fprof,apply_start_stop,4},            1, 1691.046,    0.103}],
 { {foo,create_file_slow,2},              1, 1691.046,    0.103},     %
 [{{file,close,1},                        1, 1398.873,    0.019},
  {{foo,create_file_slow,3},              1,  249.678,    0.029},
  {{file,open,2},                         1,   20.778,    0.055},
  {{lists,map,2},                         1,   16.590,    0.043},
  {{lists,seq,2},                         1,    4.708,    0.017},
  {{file,write,2},                        1,    0.316,    0.021}]}.

The printout consists of one paragraph per called function. The function marked with % is the one the paragraph concerns — foo:create_file_slow/2. Above the marked function are the calling functions — those that has called the marked, and below are those called by the marked function.

The paragraphs are per default sorted in descending order of the ACC column for the marked function. The calling list and called list within one paragraph are also per default sorted in descending order of their ACC column.

The columns are:

CNT - the number of times the function has been called

ACC - the time spent in the function including called functions

OWN - the time spent in the function not including called functions

The rows for the calling functions contain statistics for the marked function with the constraint that only the occasions when a call was made from the row's function to the marked function are accounted for.

The row for the marked function simply contains the sum of all calling rows.

The rows for the called functions contains statistics for the row's function with the constraint that only the occasions when a call was made from the marked to the row's function are accounted for.

So, we see that foo:create_file_slow/2 used very little time for its own execution. It spent most of its time in file:close/1. The function foo:create_file_slow/3 that writes 3/4 of the file contents is the second biggest time thief.

We also see that the call to file:write/2 that writes 1/4 of the file contents takes very little time in itself. What takes time is to build the data (lists:seq/2 and lists:map/2).

The function undefined that has called fprof:apply_start_stop/4 is an unknown function because that call was not recorded in the trace. It was only recorded that the execution returned from fprof:apply_start_stop/4 to some other function above in the call stack, or that the process exited from there.

Let us continue down the printout to find:

{[{{foo,create_file_slow,2},              1,  249.678,    0.029},
  {{foo,create_file_slow,3},            768,    0.000,   23.294}],
 { {foo,create_file_slow,3},            769,  249.678,   23.323},     %
 [{{file,write,2},                      768,  220.314,   14.539},
  {suspend,                              57,    6.041,    0.000},
  {{foo,create_file_slow,3},            768,    0.000,   23.294}]}.

If you compare with the code you will see there also that foo:create_file_slow/3 was called only from foo:create_file_slow/2 and itself, and called only file:write/2, note the number of calls to file:write/2. But here we see that suspend was called a few times. This is a pseudo function that indicates that the process was suspended while executing in foo:create_file_slow/3, and since there is no receive or erlang:yield/0 in the code, it must be Erlang scheduling suspensions, or the trace file driver compensating for large file write operations (these are regarded as a schedule out followed by a schedule in to the same process).

Let us find the suspend entry:

{[{{file,write,2},                       53,    6.281,    0.000},
  {{foo,create_file_slow,3},             57,    6.041,    0.000},
  {{prim_file,drv_command,4},            50,    4.582,    0.000},
  {{prim_file,drv_get_response,1},       34,    2.986,    0.000},
  {{lists,map,2},                        10,    2.104,    0.000},
  {{prim_file,write,2},                  17,    1.852,    0.000},
  {{erlang,port_command,2},              15,    1.713,    0.000},
  {{prim_file,drv_command,2},            22,    1.482,    0.000},
  {{prim_file,translate_response,2},     11,    1.441,    0.000},
  {{prim_file,'-drv_command/2-fun-0-',1},  15,    1.340,    0.000},
  {{lists,seq,4},                         3,    0.880,    0.000},
  {{foo,'-create_file_slow/2-fun-0-',1},   5,    0.523,    0.000},
  {{erlang,bump_reductions,1},            4,    0.503,    0.000},
  {{prim_file,open_int_setopts,3},        1,    0.165,    0.000},
  {{prim_file,i32,4},                     1,    0.109,    0.000},
  {{fprof,apply_start_stop,4},            1,    0.000,    0.000}],
 { suspend,                             299,   32.002,    0.000},     %
 [ ]}.

We find no particularly long suspend times, so no function seems to have waited in a receive statement. Actually, prim_file:drv_command/4 contains a receive statement, but in this test program, the message lies in the process receive buffer when the receive statement is entered. We also see that the total suspend time for the test run is small.

The suspend pseudo function has an OWN time of zero. This is to prevent the process total OWN time from including time in suspension. Whether suspend time is really ACC or OWN time is more of a philosophical question.

Now we look at another interesting pseudo function, garbage_collect:

{[{{prim_file,drv_command,4},            25,    0.873,    0.873},
  {{prim_file,write,2},                  16,    0.692,    0.692},
  {{lists,map,2},                         2,    0.195,    0.195}],
 { garbage_collect,                      43,    1.760,    1.760},     %
 [ ]}.

Here we see that no function stands out, which is very normal.

The garbage_collect pseudo function has not an OWN time of zero like suspend, instead it is equal to the ACC time.

Garbage collection often occurs while a process is suspended, but fprof hides this fact by pretending that the suspended function was first unsuspended and then garbage collected. Otherwise the printout would show garbage_collect being called from suspend, but not which function that might have caused the garbage collection.

Let us now get back to the test code:

{[{{foo,create_file_slow,3},            768,  220.314,   14.539},
  {{foo,create_file_slow,2},              1,    0.316,    0.021}],
 { {file,write,2},                      769,  220.630,   14.560},     %
 [{{prim_file,write,2},                 769,  199.789,   22.573},
  {suspend,                              53,    6.281,    0.000}]}.

Not unexpectedly, we see that file:write/2 was called from foo:create_file_slow/3 and foo:create_file_slow/2. The number of calls in each case as well as the used time are also confirms the previous results.

We see that file:write/2 only calls prim_file:write/2, but let us refrain from digging into the internals of the kernel application.

If we nevertheless do dig down we find the call to the linked-in driver that does the file operations towards the host operating system:

{[{{prim_file,drv_command,4},           772, 1458.356, 1456.643}],
 { {erlang,port_command,2},             772, 1458.356, 1456.643},     %
 [{suspend,                              15,    1.713,    0.000}]}.

This is 86 % of the total run time, and as we saw before it is the close operation the absolutely biggest contributor. We find a comparison ratio a little bit up in the call stack:

{[{{prim_file,close,1},                   1, 1398.748,    0.024},
  {{prim_file,write,2},                 769,  174.672,   12.810},
  {{prim_file,open_int,4},                1,   19.755,    0.017},
  {{prim_file,open_int_setopts,3},        1,    0.147,    0.016}],
 { {prim_file,drv_command,2},           772, 1593.322,   12.867},     %
 [{{prim_file,drv_command,4},           772, 1578.973,   27.265},
  {suspend,                              22,    1.482,    0.000}]}.

The time for file operations in the linked in driver distributes itself as 1 % for open, 11 % for write, and 87 % for close. All data is probably buffered in the operating system until the close.

The observant reader may notice that the ACC times for prim_file:drv_command/2 and prim_file:drv_command/4 is not equal between the paragraphs above, even though it is easy to believe that prim_file:drv_command/2 is just a passthrough function.

The missing time can be found in the paragraph for prim_file:drv_command/4 where it is evident that not only prim_file:drv_command/2 is called but also a fun:

{[{{prim_file,drv_command,2},           772, 1578.973,   27.265}],
 { {prim_file,drv_command,4},           772, 1578.973,   27.265},     %
 [{{erlang,port_command,2},             772, 1458.356, 1456.643},
  {{prim_file,'-drv_command/2-fun-0-',1}, 772,   87.897,   12.736},
  {suspend,                              50,    4.582,    0.000},
  {garbage_collect,                      25,    0.873,    0.873}]}.

And some more missing time can be explained by the fact that prim_file:open_int/4 both calls prim_file:drv_command/2 directly as well as through prim_file:open_int_setopts/3, which complicates the picture.

{[{{prim_file,open,2},                    1,   20.309,    0.029},
  {{prim_file,open_int,4},                1,    0.000,    0.057}],
 { {prim_file,open_int,4},                2,   20.309,    0.086},     %
 [{{prim_file,drv_command,2},             1,   19.755,    0.017},
  {{prim_file,open_int_setopts,3},        1,    0.360,    0.032},
  {{prim_file,drv_open,2},                1,    0.071,    0.030},
  {{erlang,list_to_binary,1},             1,    0.020,    0.020},
  {{prim_file,i32,1},                     1,    0.017,    0.017},
  {{prim_file,open_int,4},                1,    0.000,    0.057}]}.
.
.
.
{[{{prim_file,open_int,4},                1,    0.360,    0.032},
  {{prim_file,open_int_setopts,3},        1,    0.000,    0.016}],
 { {prim_file,open_int_setopts,3},        2,    0.360,    0.048},     %
 [{suspend,                               1,    0.165,    0.000},
  {{prim_file,drv_command,2},             1,    0.147,    0.016},
  {{prim_file,open_int_setopts,3},        1,    0.000,    0.016}]}.

Notes

The actual supervision of execution times is in itself a CPU-intensive activity. A message is written on the trace file for every function call that is made by the profiled code.

The ACC time calculation is sometimes difficult to make correct, since it is difficult to define. This happens especially when a function occurs in several instances in the call stack, for example by calling itself perhaps through other functions and perhaps even non-tail recursively.

To produce sensible results, fprof tries not to charge any function more than once for ACC time. The instance highest up (with longest duration) in the call stack is chosen.

Sometimes a function can unexpectedly waste a lot (some 10 ms or more depending on host machine OS) of OWN (and ACC) time, even functions that do practically nothing at all. The problem may be that the OS has chosen to schedule out the Erlang runtime system process for a while, and if the OS does not support high resolution CPU time measurements fprof will use wallclock time for its calculations, and it will appear as if functions are randomly burning virtual machine time.

See Also

fprof - The File Trace Profiler, dbg, eprof

Summary

Types

analyse_option()

apply_option()

pid_spec()

profile_option()

trace_option()

Functions

analyse()

Equivalent to analyse([]).

analyse(Arg)

Analyses raw profile data in the fprof server.

analyse(OptionName, OptionValue)

Equivalent to analyse([{OptionName, OptionValue}]).

apply(Func, Args)

Equivalent to apply(Func, Args, []).

apply(Arg1, Arg2, Arg3)

Calls the given function surrounded by trace([start, ...]) and trace(stop).

apply(Module, Function, Args, OptionList)

Equivalent to apply({Module, Function}, Args, OptionList).

profile()

Equivalent to profile([]).

profile(Arg)

Compiles a trace into raw profile data held by the fprof server.

profile(OptionName, OptionValue)

Equivalent to profile([{OptionName, OptionValue}]).

start()

Starts the fprof server.

stop()

Equivalent to stop(normal).

stop(Reason)

Stops the fprof server.

trace(Arg)

Starts or stops tracing.

trace(What, Value)

Starts or stop tracing.

Types

analyse_option()

(not exported)

-type analyse_option() ::
          append | callers |
          {callers, boolean()} |
          {cols, Cols :: non_neg_integer()} |
          dest |
          {dest, Dest :: pid() | (Destfile :: file:filename())} |
          details |
          {details, boolean()} |
          no_callers | no_details |
          {sort, SortSpec :: acc | own} |
          totals |
          {totals, boolean()}.

apply_option()

(not exported)

-type apply_option() ::
          continue | {procs, PidList :: [pid()]} | start | (TraceStartOption :: trace_option()).

pid_spec()

(not exported)

-type pid_spec() :: pid() | atom().

profile_option()

(not exported)

-type profile_option() ::
          append | dump |
          {dump, pid() | (Dump :: (Dumpfile :: file:filename() | []))} |
          file |
          {file, Filename :: file:filename()} |
          start | stop.

trace_option()

(not exported)

-type trace_option() ::
          cpu_time |
          {cpu_time, boolean()} |
          file |
          {file, Filename :: file:filename()} |
          {procs, PidSpec :: pid_spec()} |
          {procs, [PidSpec :: pid_spec()]} |
          start | stop |
          {tracer, Tracer :: pid() | port()} |
          verbose |
          {verbose, boolean()}.

Functions

analyse()

-spec analyse() -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when ServerPid :: pid(), Reason :: term().

Equivalent to analyse([]).

analyse(Arg)

-spec analyse(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when OptionName :: atom(), ServerPid :: pid(), Reason :: term();
             ({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when OptionName :: atom(), OptionValue :: term(), ServerPid :: pid(), Reason :: term();
             (OptionList) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when
                     OptionList :: [Option],
                     Option :: analyse_option(),
                     ServerPid :: pid(),
                     Reason :: term().

Analyses raw profile data in the fprof server.

If Arg is an atom, this call is equivalent to analyse([Arg]).

If Arg is a a tuple {Option, _}, this call is equivalent to analyse([Option]).

Otherwise Arg must be a list of valid options.

If called when no raw profile data is available, {error, no_profile} is returned.

Destfile is used to call file:open/2.

Option description:

dest | {dest, Dest} - Specifies the destination for the analysis. If this option is not given or it is dest, the destination will be the caller's group leader, otherwise the destination Dest is either the pid/0 of an I/O device or a filename. If the filename is [], "fprof.analysis" is used instead.

append - Causes the analysis to be appended to the destination file. This option is only allowed with the {dest, Destfile} option.

{cols, Cols} - Specifies the number of columns in the analysis text. If this option is not given the number of columns is set to 80.

callers | {callers, true} - Prints callers and called information in the analysis. This is the default.

{callers, false} | no_callers - Suppresses the printing of callers and called information in the analysis.

{sort, SortSpec} - Specifies if the analysis should be sorted according to the ACC column, which is the default, or the OWN column. See Analysis Format below.

totals | {totals, true} - Includes a section containing call statistics for all calls regardless of process, in the analysis.

{totals, false} - Suppresses the totals section in the analysis, which is the default.

details | {details, true} - Prints call statistics for each process in the analysis. This is the default.

{details, false} | no_details - Suppresses the call statistics for each process from the analysis.

analyse(OptionName, OptionValue)

-spec analyse(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when OptionName :: atom(), OptionValue :: term(), ServerPid :: pid(), Reason :: term().

Equivalent to analyse([{OptionName, OptionValue}]).

apply(Func, Args)

-spec apply(Func, Args) -> term()
               when Func :: fun() | {Module :: module(), Function :: atom()}, Args :: [term()].

Equivalent to apply(Func, Args, []).

apply(Arg1, Arg2, Arg3)

-spec apply(Module, Function, Args) -> term()
               when Module :: module(), Function :: atom(), Args :: [term()];
           (Func, Args, OptionList) -> term()
               when
                   Func :: fun() | {Module :: module(), Function :: atom()},
                   Args :: [term()],
                   OptionList :: [Option],
                   Option :: apply_option().

Calls the given function surrounded by trace([start, ...]) and trace(stop).

If the function arguments (Arg1, Arg2, and Arg3) are Module (an atom), Function (an atom), and Args (a list), the function will be called using erlang:apply(Module, Function, Args).

If the function arguments are Func (a fun), Args (a list), and OptionList (a list of options), the fun will be called using erlang:apply(Func, Args).

Some effort is made to keep the trace clean from unnecessary trace messages; tracing is started and stopped from a spawned process while erlang:apply/2 is called in the current process only surrounded by receive and send statements towards the trace starting process. The trace starting process exits when it is not needed any more.

The TraceStartOption is any option allowed for trace/1. The options [start, {procs, [self() | PidList]} | OptList] are given to trace/1, where OptList is OptionList with the continue, start and {procs, _} options removed.

The continue option inhibits the call to trace(stop) and leaves it up to the caller to stop tracing at a suitable time.

apply(Module, Function, Args, OptionList)

-spec apply(Module, Function, Args, OptionList) -> term()
               when
                   Module :: module(),
                   Function :: atom(),
                   Args :: [term()],
                   OptionList :: [Option],
                   Option :: apply_option().

Equivalent to apply({Module, Function}, Args, OptionList).

profile()

-spec profile() -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when ServerPid :: pid(), Reason :: term().

Equivalent to profile([]).

profile(Arg)

-spec profile(OptionName) -> ok | {ok, Tracer} | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when OptionName :: atom(), Tracer :: pid(), ServerPid :: pid(), Reason :: term();
             ({OptionName, OptionValue}) ->
                 ok | {ok, Tracer} | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when
                     OptionName :: atom(),
                     OptionValue :: term(),
                     Tracer :: pid(),
                     ServerPid :: pid(),
                     Reason :: term();
             (OptionList) -> ok | {ok, Tracer} | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when
                     OptionList :: [Option],
                     Option :: profile_option(),
                     Tracer :: pid(),
                     ServerPid :: pid(),
                     Reason :: term().

Compiles a trace into raw profile data held by the fprof server.

If Arg is an atom, this call is equivalent to profile([Arg]).

If Arg is a tuple {OptionName, OptionValue}, this call is equivalent to profile([Arg]).

Otherwise, Arg must be a list of options.

Dumpfile is used to call file:open/2, and Filename is used to call dbg:trace_port(file, Filename).

The following options are supported:

file | {file, Filename} - Reads the file Filename and creates raw profile data that is stored in RAM by the fprof server. If the option file is given, or none of these options are given, the file fprof.trace is read. The call will return when the whole trace has been read with the return value ok if successful. This option is not allowed with the start or stop options.

dump | {dump, Dump} - Specifies the destination for the trace text dump. If this option is not given, no dump is generated, if it is dump the destination will be the caller's group leader, otherwise the destination Dump is either the pid of an I/O device or a filename. If the filename is [], "fprof.dump" is used instead. This option cannot be combined with the stop option.

append - Causes the trace text dump to be appended to the destination file. This option is only allowed with the {dump, Dumpfile} option.

start - Starts a tracer process that profiles trace data in runtime. The call will return immediately with the return value {ok, Tracer} if successful. This option is not allowed with the stop, file, or {file, Filename} options.

stop - Stops the tracer process that profiles trace data in runtime. The return value will be value ok if successful. This option cannot be combined with the start, file, or {file, Filename} options.

profile(OptionName, OptionValue)

-spec profile(OptionName, OptionValue) ->
                 ok | {ok, Tracer} | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when
                     OptionName :: atom(),
                     OptionValue :: term(),
                     Tracer :: pid(),
                     ServerPid :: pid(),
                     Reason :: term().

Equivalent to profile([{OptionName, OptionValue}]).

start()

-spec start() -> {ok, Pid} | {error, {already_started, Pid}} when Pid :: pid().

Starts the fprof server.

Note that there is seldom any need to call this function directly, since the server will be automatically started by any function that will need it.

stop()

-spec stop() -> ok.

Equivalent to stop(normal).

stop(Reason)

-spec stop(Reason) -> ok when Reason :: term().

Stops the fprof server.

The supplied Reason becomes the exit reason for the server process. By default, any Reason other than kill sends a request to the server and waits for it to clean up, reply, and exit. If Reason is kill, the server is bluntly killed.

If the fprof server is not running, this function returns immediately.

Note

When the fprof server is stopped the collected raw profile data is lost.

trace(Arg)

-spec trace(verbose) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when ServerPid :: pid(), Reason :: term();
           (OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when OptionName :: atom(), ServerPid :: pid(), Reason :: term();
           ({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when OptionName :: atom(), OptionValue :: term(), ServerPid :: pid(), Reason :: term();
           (OptionList) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when
                   OptionList :: [Option],
                   Option :: trace_option(),
                   ServerPid :: pid(),
                   Reason :: term().

Starts or stops tracing.

If Arg is atom verbose, this call is equivalent to trace([start, verbose]).

If Arg is an atom, this call is equivalent to trace([Arg]).

If Arg is a tuple {OptionName, OptionValue}, this call is equivalent to trace([Arg]).

Otherwise, Arg has to be a list of trace options.

PidSpec and Tracer are used in calls to erlang:trace(PidSpec, true, [{tracer, Tracer} | Flags]), and Filename is used to call dbg:trace_port(file, Filename).

Option description:

stop - Stops a running fprof trace and clears all tracing from the node. Either option stop or start must be specified, but not both.

start - Clears all tracing from the node and starts a new fprof trace. Either option start or stop must be specified, but not both.

verbose | {verbose, boolean()} - The verbose or {verbose, true} options add some trace flags that fprof does not need, but that can be interesting for general debugging purposes. These options are only allowed with the start option.

cpu_time | {cpu_time, boolean()} - The cpu_time or {cpu_time, true} options make the timestamps in the trace be in CPU time instead of the default wallclock time. These options are only allowed with the start option.

Note

Getting correct values out of cpu_time can be difficult. The best way to get correct values is to run using a single scheduler and bind that scheduler to a specific CPU. For example:

erl +S 1 +sbt db`

{procs, PidSpec} | {procs, [PidSpec]} - Specifies which processes that should be traced. If this option is not given, the calling process is traced. All processes spawned by the traced processes are also traced. This option is only allowed with the start option.

file | {file, Filename} - Specifies the filename of the trace. If the option file is given, or none of these options are given, the file fprof.trace is used. This option is only allowed with the start option, but not with the {tracer, Tracer} option.

{tracer, Tracer} - Specifies that trace to process or port shall be done instead of trace to file. This option is only allowed with the start option, but not with the {file, Filename} option.

trace(What, Value)

-spec trace(start, Filename) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when Filename :: file:filename(), ServerPid :: pid(), Reason :: term();
           (verbose, Filename) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when Filename :: file:filename(), ServerPid :: pid(), Reason :: term();
           (OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when OptionName :: atom(), OptionValue :: term(), ServerPid :: pid(), Reason :: term().

Starts or stop tracing.

If What is atom start, this call is equivalent to trace([start, {file, Value}]).

If What is atom verbose, this call is equivalent to trace([start, verbose, {file, Value}]).

If What is a tuple {OptionName, OptionValue}, this call is equivalent to trace([What]).

lcnt

A runtime system Lock Profiling tool.

The lcnt module is used to profile the internal ethread locks in the Erlang Runtime System. With lcnt enabled, internal counters in the runtime system are updated each time a lock is taken. The counters stores information about the number of acquisition tries and the number of collisions that has occurred during the acquisition tries. The counters also record the waiting time a lock has caused for a blocked thread when a collision has occurred.

The data produced by the lock counters will give an estimate on how well the runtime system will behave from a parallelizable view point for the scenarios tested. This tool was mainly developed to help Erlang runtime developers iron out potential and generic bottlenecks.

Locks in the emulator are named after what type of resource they protect and where in the emulator they are initialized, those are lock 'classes'. Most of those locks are also instantiated several times, and given unique identifiers, to increase locking granularity. Typically an instantiated lock protects a disjunct set of the resource, for example ets tables, processes or ports. In other cases it protects a specific range of a resource, for example pix_lock which protects index to process mappings, and is given a unique number within the class. A unique lock in lcnt is referenced by a name (class) and an identifier: {Name, Id}.

Some locks in the system are static and protects global resources, for example bif_timers and the run_queue locks. Other locks are dynamic and not necessarily long lived, for example process locks and ets-table locks. The statistics data from short lived locks can be stored separately when the locks are deleted. This behavior is by default turned off to save memory but can be turned on via lcnt:rt_opt({copy_save, true}). The lcnt:apply/1,2,3 functions enables this behavior during profiling.

See Also

LCNT User's Guide

Summary

Types

category_atom()

lock_counter_data()

option()

print()

sort()

threshold()

Convenience functions

apply(Fun)

Equivalent to apply(Fun, []).

apply(Fun, Args)

Sets up lock counters, applies Fun with Args, and cleans up.

apply(Module, Function, Args)

Equivalent to apply(fun() -> apply(Module, Function, Args) end).

pid(Id, Serial)

Equivalent to pid(node(), Id, Serial).

pid(Node, Id, Serial)

Creates a process id with creation 0.

port(Id)

Equivalent to port(node(), Id).

port(Node, Id)

Creates a port id with creation 0.

Internal runtime lock counter controllers

rt_clear()

Equivalent to rt_clear(node()).

rt_clear(Node)

Clear the internal counters.

rt_collect()

Equivalent to rt_collect(node()).

rt_collect(Node)

Returns a list of raw lock counter data.

rt_mask()

Return the current category mask for the current node.

rt_mask(Arg)

Sets the current lock category mask for the current node or retrieves the current mask for a remote node.

rt_mask(Node, Categories)

Sets the lock category mask according to Categories on node Node.

rt_opt(Option)

Equivalent to rt_opt(node(), {Type, Value}).

rt_opt(Node, Option)

Sets a single option on node Node.

Functions

clear()

Equivalent to clear(node()).

clear(Node)

Clears the internal lock statistics from the runtime system.

collect()

Equivalent to collect(node()).

collect(Node)

Collects lock statistics from the runtime system.

conflicts()

Equivalent to conflicts([]).

conflicts(Options)

Prints a list of internal locks and its statistics.

information()

Prints lcnt server state and generic information about collected lock statistics.

inspect(Lock)

Equivalent to inspect(Lock, []).

inspect(Lock, Options)

Prints a list of internal lock counters for a specific lock.

load(Filename)

Restores previously saved data to the server.

locations()

Equivalent to locations([]).

locations(Options)

Prints a list of internal lock counters by source code locations.

save(Filename)

Saves the collected data to file.

start()

Starts the lock profiler server.

stop()

Stops the lock profiler server.

swap_pid_keys()

Swaps places on Name and Id space for ports and processes.

Types

category_atom()

(not exported) (since OTP R13B04)

-type category_atom() :: atom().

lock_counter_data()

(not exported) (since OTP R13B04)

-type lock_counter_data() :: term().

option()

(not exported) (since OTP R13B04)

-type option() ::
          {sort, Sort :: sort()} |
          {reverse, boolean()} |
          {locations, boolean()} |
          {thresholds, Thresholds :: [threshold()]} |
          {print, PrintOptions :: [print() | {print(), non_neg_integer()}]} |
          {max_locks, MaxLocks :: non_neg_integer() | none} |
          {combine, boolean()}.

print()

(not exported) (since OTP R13B04)

-type print() :: colls | duration | entry | id | name | ratio | time | tries | type.

sort()

(not exported) (since OTP R13B04)

-type sort() :: colls | entry | id | name | ratio | time | tries | type.

threshold()

(not exported) (since OTP R13B04)

-type threshold() :: {colls, non_neg_integer()} | {time, non_neg_integer()} | {tries, non_neg_integer()}.

Convenience functions

apply(Fun)

(since OTP R13B04)

-spec apply(Fun) -> term() when Fun :: fun().

Equivalent to apply(Fun, []).

apply(Fun, Args)

(since OTP R13B04)

-spec apply(Fun, Args) -> term() when Fun :: fun(), Args :: [term()].

Sets up lock counters, applies Fun with Args, and cleans up.

Clears the lock counters and then setups the instrumentation to save all destroyed locks. After setup the function is called, passing the elements in Args as arguments. When the function returns the statistics are immediately collected to the server. After the collection the instrumentation is returned to its previous behavior. The result of the applied function is returned.

Warning

This function should only be used for micro-benchmarks; it sets copy_save to true for the duration of the call, which can quickly lead to running out of memory.

apply(Module, Function, Args)

(since OTP R13B04)

-spec apply(Module, Function, Args) -> term()
               when Module :: module(), Function :: atom(), Args :: [term()].

Equivalent to apply(fun() -> apply(Module, Function, Args) end).

pid(Id, Serial)

(since OTP R13B04)

-spec pid(Id, Serial) -> pid() when Id :: integer(), Serial :: integer().

Equivalent to pid(node(), Id, Serial).

pid(Node, Id, Serial)

(since OTP R13B04)

-spec pid(Node, Id, Serial) -> pid() when Node :: node(), Id :: integer(), Serial :: integer().

Creates a process id with creation 0.

port(Id)

(since OTP R13B04)

-spec port(Id) -> port() when Id :: integer().

Equivalent to port(node(), Id).

port(Node, Id)

(since OTP R13B04)

-spec port(Node, Id) -> port() when Node :: node(), Id :: integer().

Creates a port id with creation 0.

Internal runtime lock counter controllers

rt_clear()

(since OTP R13B04)

-spec rt_clear() -> ok.

Equivalent to rt_clear(node()).

rt_clear(Node)

(since OTP R13B04)

-spec rt_clear(Node) -> ok when Node :: node().

Clear the internal counters.

Equivalent to lcnt:clear(Node).

rt_collect()

(since OTP R13B04)

-spec rt_collect() -> [lock_counter_data()].

Equivalent to rt_collect(node()).

rt_collect(Node)

(since OTP R13B04)

-spec rt_collect(Node) -> [lock_counter_data()] when Node :: node().

Returns a list of raw lock counter data.

rt_mask()

(since OTP 20.1)

-spec rt_mask() -> [category_atom()].

Return the current category mask for the current node.

rt_mask(Arg)

(since OTP 20.1)

-spec rt_mask(Node) -> [category_atom()] when Node :: node();
             (Categories) -> ok | {error, copy_save_enabled} when Categories :: [category_atom()].

Sets the current lock category mask for the current node or retrieves the current mask for a remote node.

If Arg is an atom, it is assumed to be a node, and this call returns the current lock category mask for node Arg.

If Arg is a list, this call is equivalent to rt_mask(node(), Arg).

rt_mask(Node, Categories)

(since OTP 20.1)

-spec rt_mask(Node, Categories) -> ok | {error, copy_save_enabled}
                 when Node :: node(), Categories :: [category_atom()].

Sets the lock category mask according to Categories on node Node.

This call will fail if the copy_save option is enabled; see lcnt:rt_opt/2.

Valid categories are:

allocator

db (ETS tables)

debug

distribution

generic

io

process

scheduler

This list is subject to change at any time, as is the category any given lock belongs to.

rt_opt(Option)

(since OTP R13B04)

-spec rt_opt(Option) -> boolean()
                when Option :: {Type, Value :: boolean()}, Type :: copy_save | process_locks.

Equivalent to rt_opt(node(), {Type, Value}).

rt_opt(Node, Option)

(since OTP R13B04)

-spec rt_opt(Node, Option) -> boolean()
                when
                    Node :: node(),
                    Option :: {Type, Value :: boolean()},
                    Type :: copy_save | process_locks.

Sets a single option on node Node.

Option description:

{copy_save, boolean()} - Retains the statistics of destroyed locks.
Default: false

Warning

This option will use a lot of memory when enabled, which must be reclaimed with lcnt:rt_clear/0,1. Note that it makes no distinction between locks that were destroyed and locks for which counting was disabled, so enabling this option will disable changes to the lock category mask.

{process_locks, boolean()} - Profile process locks, equal to adding process to the lock category mask; see lcnt:rt_mask/2.
Default: true

Functions

clear()

(since OTP R13B04)

-spec clear() -> ok.

Equivalent to clear(node()).

clear(Node)

(since OTP R13B04)

-spec clear(Node) -> ok when Node :: node().

Clears the internal lock statistics from the runtime system.

This clears the data in the runtime system but not in server. All counters for static locks are zeroed, all dynamic locks currently alive are zeroed and all saved locks now destroyed are removed. It also resets the duration timer.

collect()

(since OTP R13B04)

-spec collect() -> ok.

Equivalent to collect(node()).

collect(Node)

(since OTP R13B04)

-spec collect(Node) -> ok when Node :: node().

Collects lock statistics from the runtime system.

The function starts a server if it is not already started. It then populates the server with lock statistics. If the server held any lock statistics data before the collect then that data is lost.

conflicts()

(since OTP R13B04)

-spec conflicts() -> ok.

Equivalent to conflicts([]).

conflicts(Options)

(since OTP R13B04)

-spec conflicts(Options) -> ok when Options :: [option()].

Prints a list of internal locks and its statistics.

For option description, see lcnt:inspect/2.

information()

(since OTP R13B04)

-spec information() -> ok.

Prints lcnt server state and generic information about collected lock statistics.

inspect(Lock)

(since OTP R13B04)

-spec inspect(Lock) -> ok
                 when
                     Lock :: Name | {Name, Id | [Id]},
                     Name :: atom() | pid() | port(),
                     Id :: atom() | integer() | pid() | port().

Equivalent to inspect(Lock, []).

inspect(Lock, Options)

(since OTP R13B04)

-spec inspect(Lock, Options) -> ok
                 when
                     Lock :: Name | {Name, Id | [Id]},
                     Name :: atom() | pid() | port(),
                     Id :: atom() | integer() | pid() | port(),
                     Options :: [option()].

Prints a list of internal lock counters for a specific lock.

Lock Name and Id for ports and processes are interchangeable with the use of lcnt:swap_pid_keys/0 and is the reason why pid/0 and port/0 options can be used in both Name and Id space. Both pids and ports are special identifiers with stripped creation and can be recreated with lcnt:pid/2,3 and lcnt:port/1,2.

Option description:

{combine, boolean()} - Combine the statistics from different instances of a lock class.
Default: true

{locations, boolean()} - Print the statistics by source file and line numbers.
Default: false

{max_locks, MaxLocks} - Maximum number of locks printed or no limit with none.
Default: 20

{print, PrintOptions} - Printing options:

name - Named lock or named set of locks (classes). The same name used for initializing the lock in the VM.

id - Internal id for set of locks, not always unique. This could be table name for ets tables (db_tab), port id for ports, integer identifiers for allocators, etc.

type - Type of lock: rw_mutex, mutex, spinlock, rw_spinlock or proclock.

entry - In combination with {locations, true} this option prints the lock operations source file and line number entry-points along with statistics for each entry.

tries - Number of acquisitions of this lock.

colls - Number of collisions when a thread tried to acquire this lock. This is when a trylock is EBUSY, a write try on read held rw_lock, a try read on write held rw_lock, a thread tries to lock an already locked lock. (Internal states supervises this.)

ratio - The ratio between the number of collisions and the number of tries (acquisitions) in percentage.

time - Accumulated waiting time for this lock. This could be greater than actual wall clock time, it is accumulated for all threads. Trylock conflicts does not accumulate time.

duration - Percentage of accumulated waiting time of wall clock time. This percentage can be higher than 100% since accumulated time is from all threads.

Default: [name,id,tries,colls,ratio,time,duration]

{reverse, boolean()} - Reverses the order of sorting.
Default: false

{sort, Sort} - Column sorting orders.
Default: time

{thresholds, Thresholds} - Filtering thresholds. Anything values above the threshold value are passed through.
Default: [{tries, 0}, {colls, 0}, {time, 0}]

load(Filename)

(since OTP R13B04)

-spec load(Filename) -> ok when Filename :: file:filename().

Restores previously saved data to the server.

locations()

(since OTP R13B04)

-spec locations() -> ok.

Equivalent to locations([]).

locations(Options)

(since OTP R13B04)

-spec locations(Options) -> ok when Options :: [option()].

Prints a list of internal lock counters by source code locations.

For option description, see lcnt:inspect/2.

save(Filename)

(since OTP R13B04)

-spec save(Filename) -> ok when Filename :: file:filename().

Saves the collected data to file.

start()

(since OTP R13B04)

-spec start() -> {ok, Pid} | {error, {already_started, Pid}} when Pid :: pid().

Starts the lock profiler server.

The server only act as a medium for the user and performs filtering and printing of data collected by lcnt:collect/1.

stop()

(since OTP R13B04)

-spec stop() -> ok.

Stops the lock profiler server.

swap_pid_keys()

(since OTP R13B04)

-spec swap_pid_keys() -> ok.

Swaps places on Name and Id space for ports and processes.

make

A Make Utility for Erlang

The module make provides a set of functions similar to the UNIX type Make functions.

Emakefile

make:all/0,1 and make:files/1,2 first looks for {emake, Emake} in options, then in the current working directory for a file named Emakefile. If present Emake should contain elements like this:

Modules.
{Modules,Options}.

Modules is an atom or a list of atoms. It can be

a module name, for exmaple, file1

a module name in another directory, for exmaple, '../foo/file3'

a set of modules specified with a wildcards, for exmaple, 'file*'

a wildcard indicating all modules in current directory, that is: '*'

a list of any of the above, for exmaple, ['file*','../foo/file3','File4']

Options is a list of compiler options.

Emakefile is read from top to bottom. If a module matches more than one entry, the first match is used. For example, the following Emakefile means that file1 should be compiled with the options [debug_info,{i,"../foo"}], while all other files in the current directory should be compiled with only the debug_info flag.

{'file1',[debug_info,{i,"../foo"}]}.
{'*',[debug_info]}.

See Also

The Compiler Application

Summary

Functions

all()

Equivalent to all([]).

all(Options)

This function determines the set of modules to compile and the compile options to use, by first looking for the emake make option, if not present reads the configuration from a file named Emakefile (see below). If no such file is found, the set of modules to compile defaults to all modules in the current working directory.

files(ModFiles)

Equivalent to files(ModFiles, []).

files(ModFiles, Options)

This function does exactly the same thing as all/0,1, but for the specified ModFiles, which is a list of module or file names.

Functions

all()

-spec all() -> up_to_date | error.

Equivalent to all([]).

all(Options)

-spec all(Options) -> up_to_date | error
             when
                 Options :: [Option],
                 Option :: noexec | load | netload | {emake, Emake} | compile:option(),
                 Emake :: [EmakeElement],
                 EmakeElement :: Modules | {Modules, [compile:option()]},
                 Modules :: atom() | [atom()].

This function determines the set of modules to compile and the compile options to use, by first looking for the emake make option, if not present reads the configuration from a file named Emakefile (see below). If no such file is found, the set of modules to compile defaults to all modules in the current working directory.

Traversing the set of modules, it then recompiles every module for which at least one of the following conditions apply:

there is no object file, or

the source file has been modified since it was last compiled, or,

an include file has been modified since the source file was last compiled.

As a side effect, the function prints the name of each module it tries to compile. If compilation fails for a module, the make procedure stops and error is returned.

Options is a list of options for make and the Erlang compiler. The following make options exist:

noexec No execution mode. Just prints the name of each module that needs to be compiled.

load Load mode. Loads all recompiled modules.

netload Net load mode. Loads all recompiled modules on all known nodes.

{emake, Emake} Rather than reading the Emakefile specify configuration explicitly.

All items in Options that are not make options are assumed to be compiler options and are passed as-is to compile:file/2.

files(ModFiles)

-spec files(ModFiles) -> up_to_date | error
               when ModFiles :: [(Module :: module()) | (File :: file:filename())].

Equivalent to files(ModFiles, []).

files(ModFiles, Options)

-spec files(ModFiles, Options) -> up_to_date | error
               when
                   ModFiles :: [(Module :: module()) | (File :: file:filename())],
                   Options :: [Option],
                   Option :: noexec | load | netload | compile:option().

This function does exactly the same thing as all/0,1, but for the specified ModFiles, which is a list of module or file names.

The file extension .erl can be omitted.

The Emakefile (if it exists) in the current directory is searched for compiler options for each module. If a given module does not exist in Emakefile or if Emakefile does not exist, the module is still compiled.

tags

Generate Emacs TAGS file from Erlang source files

A TAGS file is used by Emacs to find function and variable definitions in any source file in large projects. This module can generate a TAGS file from Erlang source files. It recognises functions, records, and macro definitions.

Options

The functions in this module have an optional argument Options. It is a list which can contain the following elements:

{outfile, NameOfTAGSFile} Create a TAGS file named NameOfTAGSFile.

{outdir, NameOfDirectory} Create a file named TAGS in the directory NameOfDirectory.

The default behaviour is to create a file named TAGS in the current directory.

Examples

tags:root([{outfile, "root.TAGS"}]).

This command will create a file named root.TAGS in the current directory. The file will contain references to all Erlang source files in the Erlang distribution.

tags:files(["foo.erl", "bar.erl", "baz.erl"], [{outdir, "../projectdir"}]).

This command will create a file named TAGS placed it in the directory ../projectdir. The file contains information about the functions, records, and macro definitions of the three files.

See Also

Richard M. Stallman. GNU Emacs Manual, chapter "Editing Programs", section "Tag Tables". Free Software Foundation, 1995.

Anders Lindgren. The Erlang editing mode for Emacs. Ericsson, 1998.

Summary

Types

option()

Functions

dir(Dir)

Equivalent to dir(Dir, []).

dir(Dir, Options)

Create a TAGS file for all files in directory Dir.

dirs(DirList)

Equivalent to dirs(Dirs, []).

dirs(DirList, Options)

Create a TAGS file for all files in any directory in DirList.

file(File)

Equivalent to file(Name, []).

file(File, Options)

Create a TAGS file for the file File.

files(FileList)

Equivalent to files(Files, []).

files(FileList, Options)

Create a TAGS file for the files in the list FileList.

root()

Equivalent to root([]).

root(Options)

Create a TAGS file covering all files in the Erlang distribution.

subdir(Dir)

Equivalent to subdir(Dir, []).

subdir(Dir, Options)

Descend recursively into the directory Dir and create a TAGS file based on all files found.

subdirs(DirList)

Equivalent to subdirs(Dirs, []).

subdirs(DirList, Options)

Descend recursively into the directories in DirList and create a TAGS file based on all files found.

Types

option()

(not exported)

-type option() ::
          {outfile, NameOfTAGSFile :: file:filename()} | {outdir, NameOfDirectory :: file:filename()}.

Functions

dir(Dir)

-spec dir(Dir) -> ok | error when Dir :: file:filename().

Equivalent to dir(Dir, []).

dir(Dir, Options)

-spec dir(Dir, Options) -> ok | error when Dir :: file:filename(), Options :: [option()].

Create a TAGS file for all files in directory Dir.

dirs(DirList)

-spec dirs(DirList) -> ok | error when DirList :: [file:filename()].

Equivalent to dirs(Dirs, []).

dirs(DirList, Options)

-spec dirs(DirList, Options) -> ok | error when DirList :: [file:filename()], Options :: [option()].

Create a TAGS file for all files in any directory in DirList.

file(File)

-spec file(File) -> ok | error when File :: file:filename().

Equivalent to file(Name, []).

file(File, Options)

-spec file(File, Options) -> ok | error when File :: file:filename(), Options :: [option()].

Create a TAGS file for the file File.

files(FileList)

-spec files(FileList) -> ok | error when FileList :: [file:filename()].

Equivalent to files(Files, []).

files(FileList, Options)

-spec files(FileList, Options) -> ok | error when FileList :: [file:filename()], Options :: [option()].

Create a TAGS file for the files in the list FileList.

root()

-spec root() -> ok | error.

Equivalent to root([]).

root(Options)

-spec root(Options) -> ok | error when Options :: [option()].

Create a TAGS file covering all files in the Erlang distribution.

subdir(Dir)

-spec subdir(Dir) -> ok | error when Dir :: file:filename().

Equivalent to subdir(Dir, []).

subdir(Dir, Options)

-spec subdir(Dir, Options) -> ok | error when Dir :: file:filename(), Options :: [option()].

Descend recursively into the directory Dir and create a TAGS file based on all files found.

subdirs(DirList)

-spec subdirs(DirList) -> ok | error when DirList :: [file:filename()].

Equivalent to subdirs(Dirs, []).

subdirs(DirList, Options)

-spec subdirs(DirList, Options) -> ok | error when DirList :: [file:filename()], Options :: [option()].

Descend recursively into the directories in DirList and create a TAGS file based on all files found.

tprof

Process Tracing Profiling Tool

tprof provides convenience helpers for Erlang process profiling using the trace BIFs.

Warning

This module aims to replace eprof and cprof into a unified API for measuring call count, time, and allocation. It is experimental in Erlang/OTP 27.0.

It is possible to analyze the number of calls, the time spent by function, and heap allocations by function. Profiling can be done ad-hoc or run in a server-aided mode for deeper introspection of the code running in production. The server-aided mode can be run using the default tprof server or an isolated server/0 started through start(#{ session => atom() }).

There are three kinds of profiling supported by this module:

call_count

call_time

call_memory

The default is call_count, which has the smallest performance impact and memory footprint, but it does not support per-process profiling. For this reason, all of the examples below uses call_memory, which measures heap allocation, and provide a more complex feature set to demonstrate.

Erlang terms that do not fit in a single machine word are allocated on the process heap. For example, a function returning a tuple of two elements needs to allocate the tuple on the process heap. The actual consumption is three words, because the runtime systems also need an extra word to store the tuple size.

Note

Expect a slowdown in the program execution when profiling is enabled.

For profiling convenience, measurements are accumulated for functions that are not enabled in some trace pattern. Consider this call stack example:

top_traced_function(...)
not_traced_function()
bottom_traced_function()

Allocations that happened within not_traced_function will be added to the allocations for top_traced_function. However, allocations that occurred within bottom_traced_function are not included in the top_traced_function. To only keep track of each function own allocations, it is necessary to trace all functions.

Warning

Avoid hot code reloading for modules participating in the tracing. Reloading a module disables tracing and discards the accumulated statistics. The tprof results will probably be incorrect when the profiled code was reloading during a profiling session.

Ad-hoc profiling

Ad-hoc profiling is convenient for profiling a single function call.

For example:

1> tprof:profile(lists, seq, [1, 16], #{type => call_memory}).

****** Process <0.92.0>  --  100.00% of total ***
FUNCTION          CALLS  WORDS  PER CALL  [     %]
lists:seq_loop/3      5     32      6.40  [100.00]
                            32            [ 100.0]
ok

By default tracing is enabled for all functions in all modules. When funs are created in the interactive shell, parts of shell code are also traced:

1> tprof:profile(fun() -> lists:seq(1, 16) end, #{type => call_memory}).

****** Process <0.95.0>  --  100.00% of total ***
FUNCTION                   CALLS  WORDS  PER CALL  [    %]
erl_eval:do_apply/7            1      3      3.00  [ 3.61]
erl_eval:match_list/6          1      3      3.00  [ 3.61]
lists:reverse/1                1      4      4.00  [ 4.82]
erl_eval:expr_list/7           3      7      2.33  [ 8.43]
erl_eval:ret_expr/3            4     16      4.00  [19.28]
erl_eval:merge_bindings/4      3     18      6.00  [21.69]
lists:seq_loop/3               5     32      6.40  [38.55]
                                     83            [100.0]
ok

However, it is possible to limit the trace to specific functions or modules:

2> tprof:profile(fun() -> lists:seq(1, 16) end,
                 #{type => call_memory, pattern => [{lists, seq_loop, '_'}]}).
****** Process <0.98.0>  --  100.00% of total ***
FUNCTION          CALLS  WORDS  PER CALL  [     %]
lists:seq_loop/3      5     32      6.40  [100.00]
                            32            [ 100.0]

ok

Ad-hoc profiling results can be printed in a few different ways. The following examples use the test module defined like this:

-module(test).
-export([test_spawn/0]).
test_spawn() ->
    {Pid, MRef} = spawn_monitor(fun () -> lists:seq(1, 32) end),
    receive
        {'DOWN', MRef, process, Pid, normal} ->
            done
    end.

By default per-process statistics is shown:

1> tprof:profile(test, test_spawn, [], #{type => call_memory}).

****** Process <0.176.0>    -- 23.66 % of total allocations ***
FUNCTION                CALLS  WORDS  PER CALL  [    %]
erlang:spawn_monitor/1      1      2         2  [ 9.09]
erlang:spawn_opt/4          1      6         6  [27.27]
test:test_spawn/0           1     14        14  [63.64]
                                  22            [100.0]

****** Process <0.177.0>    -- 76.34 % of total allocations ***
FUNCTION           CALLS  WORDS  PER CALL  [    %]
erlang:apply/2         1      7         7  [ 9.86]
lists:seq_loop/3       9     64         7  [90.14]
                             71            [100.0]

The following example prints the combined memory allocation of all processes, sorted by the total number of allocated words in descending order:

2> tprof:profile(test, test_spawn, [],
                 #{type => call_memory, report => {total, {measurement, descending}}}).

FUNCTION                CALLS  WORDS  PER CALL  [    %]
lists:seq_loop/3            9     64         7  [68.82]
test:test_spawn/0           1     14        14  [15.05]
erlang:apply/2              1      7         7  [ 7.53]
erlang:spawn_opt/4          1      6         6  [ 6.45]
erlang:spawn_monitor/1      1      2         2  [ 2.15]
                                  93            [100.0]

The profiling data can also be collected for further inspection:

3> {done, ProfileData} = tprof:profile(fun test:test_spawn/0,
                                       #{type => call_memory, report => return}).
<...>
4> tprof:format(tprof:inspect(ProfileData, process, {percent, descending})).

****** Process <0.223.0>    -- 23.66 % of total allocations ***
FUNCTION                CALLS  WORDS  PER CALL  [    %]
test:test_spawn/0           1     14        14  [63.64]
erlang:spawn_opt/4          1      6         6  [27.27]
erlang:spawn_monitor/1      1      2         2  [ 9.09]
                                  22            [100.0]

****** Process <0.224.0>    -- 76.34 % of total allocations ***
FUNCTION           CALLS  WORDS  PER CALL  [    %]
lists:seq_loop/3       9     64         7  [90.14]
erlang:apply/2         1      7         7  [ 9.86]
                             71            [100.0]

Which processes that are profiled depends on the profiling type.

call_count (default) counts calls in all processes.

call_time and call_memory limits the profiling to the processes spawned from the user-provided function (using the set_on_spawn option for trace:process/4).

call_time and call_memory can be restricted to profile a single process:

2> tprof:profile(test, test_spawn, [],
                 #{type => call_memory, set_on_spawn => false}).

****** Process <0.183.0>    -- 100.00 % of total allocations ***
FUNCTION                CALLS  WORDS  PER CALL  [    %]
erlang:spawn_monitor/1      1      2         2  [ 9.09]
erlang:spawn_opt/4          1      6         6  [27.27]
test:test_spawn/0           1     14        14  [63.64]

Erlang programs can perform expensive operations in other processes than the original one. You can include multiple, new, or even all processes in the trace when measuring time or memory:

7> pg:start_link().
{ok,<0.252.0>}
8> tprof:profile(fun() -> pg:join(group, self()) end,
                 #{type => call_memory, rootset => [pg]}).
****** Process <0.252.0>    -- 52.86 % of total allocations ***
FUNCTION                      CALLS  WORDS  PER CALL  [    %]
pg:leave_local_update_ets/5       1      2         2  [ 1.80]
gen:reply/2                       1      3         3  [ 2.70]
erlang:monitor/2                  1      3         3  [ 2.70]
gen_server:try_handle_call/4      1      3         3  [ 2.70]
gen_server:try_dispatch/4         1      3         3  [ 2.70]
maps:iterator/1                   2      4         2  [ 3.60]
maps:take/2                       1      6         6  [ 5.41]
pg:join_local_update_ets/5        1      8         8  [ 7.21]
pg:handle_info/2                  1      8         8  [ 7.21]
pg:handle_call/3                  1      9         9  [ 8.11]
gen_server:loop/7                 2      9         4  [ 8.11]
ets:lookup/2                      2     10         5  [ 9.01]
pg:join_local/3                   1     11        11  [ 9.91]
pg:notify_group/5                 2     16         8  [14.41]
erlang:setelement/3               2     16         8  [14.41]
                                       111            [100.0]

****** Process <0.255.0>    -- 47.14 % of total allocations ***
FUNCTION                   CALLS  WORDS  PER CALL  [    %]
erl_eval:match_list/6          1      3         3  [ 3.03]
erlang:monitor/2               1      3         3  [ 3.03]
lists:reverse/1                2      4         2  [ 4.04]
pg:join/3                      1      4         4  [ 4.04]
erl_eval:add_bindings/2        1      5         5  [ 5.05]
erl_eval:do_apply/7            2      6         3  [ 6.06]
gen:call/4                     1      8         8  [ 8.08]
erl_eval:expr_list/7           4     10         2  [10.10]
gen:do_call/4                  1     16        16  [16.16]
erl_eval:ret_expr/3            4     16         4  [16.16]
erl_eval:merge_bindings/4      3     24         8  [24.24]
                                     99            [100.0]

By default, there is no limit for the profiling time. For ad-hoc profiling, it is possible to configure a time limit. If the profiled function does not return before that time expires, the process is terminated with reason kill. Any unlinked children processes started by the user-supplied function are kept; it is the responsibility of the developer to take care of such processes.

9> tprof:profile(timer, sleep, [100000], #{timeout => 1000}).

By default, only one ad-hoc or server-aided profiling session is allowed at any point in time. It is possible to force multiple ad-hoc sessions concurrently, but it is the responsibility of the developer to ensure that trace patterns do not overlap:

1> tprof:profile(fun() -> lists:seq(1, 32) end,
    #{registered => false, pattern => [{lists, '_', '_'}]}).

Server-aided profiling

Server-aided profiling can be done on a system that is up and running. To do that, start the tprof server, and then add trace patterns and processes to trace while the system handles actual traffic. Data can extracted, inspected, and printed at any time. The following example traces activity of all processes supervised by the Kernel supervisor:

1> tprof:start(#{type => call_memory}).
{ok,<0.200.0>}
2> tprof:enable_trace({all_children, kernel_sup}).
34
3> tprof:set_pattern('_', '_' , '_').
16728
4> Sample = tprof:collect().
{call_memory,
    [{gen_server,try_dispatch,4,[{<0.154.0>,2,6}]},
     {erlang,iolist_to_iovec,1,[{<0.161.0>,1,8}]},
<...>
5 > tprof:format(tprof:inspect(Sample)).

****** Process <0.154.0>    -- 14.21 % of total allocations ***
FUNCTION                   CALLS  WORDS  PER CALL  [    %]
maps:iterator/1                2      4         2  [15.38]
gen_server:try_dispatch/4      2      6         3  [23.08]
net_kernel:handle_info/2       2     16         8  [61.54]
                                     26            [100.0]

****** Process <0.161.0>    -- 85.79 % of total allocations ***
FUNCTION                        CALLS  WORDS  PER CALL  [    %]
disk_log:handle/2                   2      2         1  [ 1.27]
disk_log_1:maybe_start_timer/1      1      3         3  [ 1.91]
disk_log_1:mf_write_cache/1         1      3         3  [ 1.91]
<...>

It is possible to profile the entire running system, and then examine individual processes:

1> tprof:start(#{type => call_memory}).
2> tprof:enable_trace(all), tprof:set_pattern('_', '_' , '_').
9041
3> timer:sleep(10000), tprof:disable_trace(all), Sample = tprof:collect().
{call_memory,
    [{user_drv,server,3,[{<0.64.0>,12,136}]},
     {user_drv,contains_ctrl_g_or_ctrl_c,1,[{<0.64.0>,80,10}]},
<...>
4> Inspected = tprof:inspect(Sample, process, measurement), Shell = maps:get(self(), Inspected).
{call_memory, 2743,
    [{shell,{enc,0},1,2,2,0.07291286912139992},
<...>
5> tprof:format(Shell).

FUNCTION                           CALLS  WORDS  PER CALL  [    %]
<...>
erl_lint:start/2                       2    300       150  [10.94]
shell:used_records/1                 114    342         3  [12.47]

Summary

Types

column()

Column to sort by inspect/3 or profile/4.

process()

A process identifier (pid) or a registered process name.

profile_line()

Inspected data for a single function of the specified Module.

profile_options()

Ad-hoc profiler options; see profile/4.

profile_result()

Profile of a single process, or combined profile of multiple processes, sorted by a selected column.

rootset()

server()

A tprof server.

sort_by()

start_options()

trace_info()

Raw data extracted from tracing BIFs.

trace_map()

Traced functions (with their arities) grouped by module name, or all if all code is traced.

trace_options()

Options for enabling profiling of the selected processes; see enable_trace/2.

trace_pattern()

trace_type()

The type of profiling that the tprof server will do.

Functions

clear_pattern(Mod, Fun, Arity)

Disables tracing functions matching the supplied pattern.

clear_pattern(Server, Mod, Fun, Arity)

Equivalent to clear_pattern(Mod, Fun, Arity) but uses the provided Server.

collect()

Returns statistics for current trace map.

collect(Server)

Equivalent to collect/0 but uses the provided Server.

continue()

Resumes previously paused profiling.

continue(Server)

Equivalent to continue/0 but uses the provided Server.

disable_trace(Rootset)

Equivalent to disable_trace(Spec, #{set_on_spawn => true}).

disable_trace(Spec, Options)

Stops accumulating traces for specified processes.

disable_trace(Server, Spec, Options)

enable_trace(Rootset)

Equivalent to enable_trace(Spec, #{set_on_spawn => true}).

enable_trace(Spec, Options)

Similar to trace:process/4, but supports a few more options for tracing convenience.

enable_trace(Server, Spec, Options)

Equivalent to enable_trace/2 but uses the provided Server.

format(Inspected)

Formats profile data transformed with inspect/3, outputting to the default output device.

format(IoDevice, Inspected)

Formats profile transformed with inspect/3, outputting to device IoDevice.

get_trace_map()

Returns a map of module names to functions with their arities.

get_trace_map(Server)

Equivalent to get_trace_map/0 but uses the provided Server.

inspect(Profile)

Equivalent to inspect(Profile, process, percent).

inspect(Profile, Type, SortBy)

Transforms raw data returned by tracing BIFs into a form convenient for subsequent analysis and formatting.

pause()

Pauses trace collection for all currently traced functions, retaining existing traces.

pause(Server)

Equivalent to pause/0 but uses the provided Server.

profile(Fun)

Equivalent to profile(Fun, #{}).

profile(Fun, Options)

Does ad-hoc profiling of the call Fun().

profile(Module, Function, Args)

Equivalent to profile(Module, Function, Args, #{}).

profile(Module, Function, Args, Options)

Does ad-hoc profiling for the call apply(Module, Function, Args).

restart()

Clears accumulated profiles and starts profiling if it was paused.

restart(Server)

Equivalent to restart/0 but uses the provided Server.

set_pattern(Mod, Fun, Arity)

Enables tracing for all functions matching the supplied pattern.

set_pattern(Server, Mod, Fun, Arity)

Equivalent to set_pattern/3 but uses the provided Server.

start()

Equivalent to start(#{}).

start(Config)

Starts the server, not supervised.

start_link()

Equivalent to start_link(#{}).

start_link(Config)

Equivalent to start/1 but also links the profiling server to the caller.

stop()

Stops the default tprof server and disable tracing enabled by the server.

stop(Server)

Equivalent to stop/0 but uses the provided Server.

Types

column()

(not exported) (since OTP 27.0)

-type column() :: module | function | calls | measurement | measurement_per_call | percent.

Column to sort by inspect/3 or profile/4.

module - Module name.

function - Function name.

calls - Number of calls to the function.

measurement - Total measurement (call count, time, or heap allocation) throughout all calls to the function.

measurement_per_call - Measurement (call count, time, or heap allocation) on average per function call.

percent - Percentage of measurement to total amount during the entire profile collection.

process()

(not exported) (since OTP 27.0)

-type process() :: pid() | atom().

A process identifier (pid) or a registered process name.

profile_line()

(not exported) (since OTP 27.0)

-type profile_line() ::
          {module(),
           Function :: {atom(), arity()},
           Count :: pos_integer(),
           Measurement :: pos_integer(),
           MeasurementPerCall :: non_neg_integer(),
           Percent :: float()}.

Inspected data for a single function of the specified Module.

profile_options()

(not exported) (since OTP 27.0)

-type profile_options() ::
          #{type => trace_type(),
            timeout => timeout(),
            pattern => trace_pattern() | [trace_pattern()],
            set_on_spawn => boolean(),
            rootset => rootset(),
            report => return | process | total | {process, sort_by()} | {total, sort_by()},
            device => io:device()}.

Ad-hoc profiler options; see profile/4.

profile_result()

(not exported) (since OTP 27.0)

-type profile_result() :: {trace_type(), TotalMeasurement :: non_neg_integer(), [profile_line()]}.

Profile of a single process, or combined profile of multiple processes, sorted by a selected column.

rootset()

(not exported) (since OTP 27.0)

-type rootset() :: [process()] | all | existing | new.

server()

(not exported) (since OTP 27.0)

-type server() :: pid() | tprof.

A tprof server.

Each server uses a separate trace:session/0 in order to keep profiling isolated.

sort_by()

(not exported) (since OTP 27.0)

-type sort_by() :: column() | {column(), ascending} | {column(), descending}.

start_options()

(not exported) (since OTP 27.0)

-type start_options() :: #{type => trace_type(), session => atom()}.

trace_info()

(not exported) (since OTP 27.0)

-type trace_info() ::
          {module(),
           Fun :: atom(),
           Arity :: non_neg_integer(),
           [{pid(), Count :: pos_integer(), Measurement :: pos_integer()}]}.

Raw data extracted from tracing BIFs.

trace_map()

(not exported) (since OTP 27.0)

-type trace_map() :: #{module() => [{Fun :: atom(), arity()}]} | all.

Traced functions (with their arities) grouped by module name, or all if all code is traced.

trace_options()

(not exported) (since OTP 27.0)

-type trace_options() :: #{set_on_spawn => boolean()}.

Options for enabling profiling of the selected processes; see enable_trace/2.

trace_pattern()

(not exported) (since OTP 27.0)

-type trace_pattern() :: {module(), Fun :: atom(), arity() | '_'}.

trace_type()

(not exported) (since OTP 27.0)

-type trace_type() :: call_count | call_time | call_memory.

The type of profiling that the tprof server will do.

call_count - Counts the number of calls made to functions. This is a global profiling event that cannot be limited to specific processes. See call_count in trace:function/4 for more details.

call_time - Counts the accumulated time spent in functions. See call_time in trace:function/4 for more details.

call_memory - Counts the accumulated memory allocated in functions. See call_memory in trace:function/4 for more details.

Functions

clear_pattern(Mod, Fun, Arity)

(since OTP 27.0)

-spec clear_pattern(module(), atom(), arity() | '_') -> ok.

Disables tracing functions matching the supplied pattern.

1> tprof:set_pattern(lists, seq, '_').
2
2> tprof:clear_pattern(lists, seq, 3).
1
3> tprof:get_trace_map().
#{lists => [{seq,2}]}

Requires that the default tprof server has been started.

clear_pattern(Server, Mod, Fun, Arity)

(since OTP 27.0)

-spec clear_pattern(server(), module(), atom(), arity() | '_') -> ok.

Equivalent to clear_pattern(Mod, Fun, Arity) but uses the provided Server.

collect()

(since OTP 27.0)

-spec collect() -> {trace_type(), [trace_info()]}.

Returns statistics for current trace map.

collect(Server)

(since OTP 27.0)

-spec collect(server()) -> {trace_type(), [trace_info()]}.

Equivalent to collect/0 but uses the provided Server.

continue()

(since OTP 27.0)

-spec continue() -> ok | not_paused.

Resumes previously paused profiling.

continue(Server)

(since OTP 27.0)

-spec continue(server()) -> ok | not_paused.

Equivalent to continue/0 but uses the provided Server.

disable_trace(Rootset)

(since OTP 27.0)

-spec disable_trace(Spec) -> non_neg_integer()
                       when Spec :: pid() | all | new | existing | {children | all_children, process()};
                   ([process()]) -> non_neg_integer() | {non_neg_integer(), [process()]}.

Equivalent to disable_trace(Spec, #{set_on_spawn => true}).

disable_trace(Spec, Options)

(since OTP 27.0)

-spec disable_trace(Spec, trace_options()) -> non_neg_integer()
                       when Spec :: pid() | all | new | existing | {children | all_children, process()};
                   ([process()], trace_options()) ->
                       non_neg_integer() | {non_neg_integer(), [process()]}.

Stops accumulating traces for specified processes.

See enable_trace/2 for a description of the options.

The profile data accumulated before the process is removed from the traced list is retained. This makes it possible to enable tracing for many or all processes in the system, sleep for a short period of time, then disable tracing for all processes (to avoid system overload), but keeping profile data.

disable_trace(Server, Spec, Options)

(since OTP 27.0)

-spec disable_trace(server(), Spec, trace_options()) -> non_neg_integer()
                       when Spec :: pid() | all | new | existing | {children | all_children, process()};
                   (server(), [process()], trace_options()) ->
                       non_neg_integer() | {non_neg_integer(), [process()]}.

enable_trace(Rootset)

(since OTP 27.0)

-spec enable_trace(Spec) -> non_neg_integer()
                      when Spec :: pid() | all | new | existing | {children | all_children, process()};
                  ([process()]) -> non_neg_integer() | {non_neg_integer(), [process()]}.

Equivalent to enable_trace(Spec, #{set_on_spawn => true}).

enable_trace(Spec, Options)

(since OTP 27.0)

-spec enable_trace(Spec, trace_options()) -> Traced :: non_neg_integer()
                      when Spec :: pid() | all | new | existing;
                  (Spec, trace_options()) ->
                      Traced :: non_neg_integer() | {Traced :: non_neg_integer(), Failed :: [process()]}
                      when Spec :: [process()] | {children | all_children, process()}.

Similar to trace:process/4, but supports a few more options for tracing convenience.

Tracing per process is not supported by call_count profilers.

Spec is either a process identifier (pid) for a local process, one of the following atoms, or a list of local process identifiers or their registered names:

all - All currently existing processes and all that will be created in the future.

existing - All currently existing processes.

new - All processes that will be created in the future.

children - All currently running processes that were directly spawned by the specified process. This mode is helpful for tracing workers of a single supervisor.

all_children - All currently running processes that were spawned by the specified process, or any recursive descendant of it. This mode is designed to facilitate tracing of supervision trees.

Returns the number of processes for which tracing was enabled.

When a list of pids, children or all_children is used, the processes that tracing failed to be enabled on will also be returned. Tracing can fail to be enabled if the process has terminated before tracing could be enabled.

Note

The profiling server does not keep track of processes that were added to the tracing set. It is permitted to stop the profiling server (wiping out any accumulated data), restart the server, set entirely different tracing pattern keeping the list of traced processes for future use. Use disable_trace(Processes) to clear the list of traced processes.

Specify Options to modify tracing behavior:

set_on_spawn - Automatically start tracing for processes spawned by the traced process. On by default.

enable_trace(Server, Spec, Options)

(since OTP 27.0)

-spec enable_trace(server(), Spec, trace_options()) -> non_neg_integer()
                      when Spec :: pid() | all | new | existing | {children | all_children, process()};
                  (server(), [process()], trace_options()) ->
                      non_neg_integer() | {non_neg_integer(), [process()]}.

Equivalent to enable_trace/2 but uses the provided Server.

format(Inspected)

(since OTP 27.0)

-spec format(profile_result() | #{pid() | all => profile_result()}) -> ok.

Formats profile data transformed with inspect/3, outputting to the default output device.

format(IoDevice, Inspected)

(since OTP 27.0)

-spec format(io:device(), profile_result() | #{pid() | all => profile_result()}) -> ok.

Formats profile transformed with inspect/3, outputting to device IoDevice.

get_trace_map()

(since OTP 27.0)

-spec get_trace_map() -> trace_map().

Returns a map of module names to functions with their arities.

get_trace_map(Server)

(since OTP 27.0)

-spec get_trace_map(server()) -> trace_map().

Equivalent to get_trace_map/0 but uses the provided Server.

inspect(Profile)

(since OTP 27.0)

-spec inspect({trace_type(), [trace_info()]}) -> #{all => profile_result()}.

Equivalent to inspect(Profile, process, percent).

Transforms raw profile into a map of process identifiers to a tuple containing total count of words allocated, and a list of all traced functions sorted in the ascending order by the allocation percentage.

inspect(Profile, Type, SortBy)

(since OTP 27.0)

-spec inspect(Profile :: {trace_type(), [trace_info()]}, Type :: process | total, SortBy :: sort_by()) ->
                 #{pid() | all => profile_result()}.

Transforms raw data returned by tracing BIFs into a form convenient for subsequent analysis and formatting.

When the Type argument is process, this function returns a map of process identifiers with corresponding profiling results sorted by the selected column.

When Type argument is total or when profiling by call_count, this function returns a map with a single all key with profiling results from all processes.

The inspected profile data can be leveraged to print profiling results.

pause()

(since OTP 27.0)

-spec pause() -> ok | not_running.

Pauses trace collection for all currently traced functions, retaining existing traces.

Use continue/0 to resume trace collection.

pause(Server)

(since OTP 27.0)

-spec pause(server()) -> ok | not_running.

Equivalent to pause/0 but uses the provided Server.

profile(Fun)

(since OTP 27.0)

-spec profile(fun(() -> term())) -> ok | {term(), [trace_info()]}.

Equivalent to profile(Fun, #{}).

profile(Fun, Options)

(since OTP 27.0)

-spec profile(fun(() -> term()), profile_options()) -> ok | {term(), {trace_type(), [trace_info()]}}.

Does ad-hoc profiling of the call Fun().

By default, the result is formatted to the output device; use the report option to change this behavior.

Ad-hoc profiling starts a new instance of tprof server, runs the profiling routine, extracts results, and shuts down the server.

See profile/4 for a list of the supported options.

profile(Module, Function, Args)

(since OTP 27.0)

-spec profile(module(), Fun :: atom(), Args :: [term()]) ->
                 ok | {term(), {trace_type(), [trace_info()]}}.

Equivalent to profile(Module, Function, Args, #{}).

profile(Module, Function, Args, Options)

(since OTP 27.0)

-spec profile(module(), Fun :: atom(), Args :: [term()], profile_options()) ->
                 ok | {term(), {trace_type(), [trace_info()]}}.

Does ad-hoc profiling for the call apply(Module, Function, Args).

By default, the result is formatted to the output device; use option report to change this behavior.

Ad-hoc profiling starts a new instance of tprof server, runs the profiling routine, extracts results, and shuts down the server.

The ad-hoc profiler supports the following Options:

type - The type of profiling to perform.

device - Specifies I/O devices to print the profile to. Useful to redirect text output to console or standard_error.

pattern - Specifies a trace pattern, or a list of trace patterns to enable. By default, all functions ({'_', '_', '_'}) are traced.

report - Controls output format. The default is process; printing per-process profiling data sorted by percentage of the total allocation. Specify report => return to suppress printing and get the raw data for further evaluation with inspect/3 and formatting with format/2.

rootset - Includes extra processes in the trace list. Useful for profiling allocations for gen_server, calls, or other allocations caused by inter-process communications. See this example.

set_on_spawn - Automatically start tracing for processes spawned by the traced process. Enabled by default.

timeout - Terminate profiling after the specified amount of time (milliseconds).

restart()

(since OTP 27.0)

-spec restart() -> ok.

Clears accumulated profiles and starts profiling if it was paused.

restart(Server)

(since OTP 27.0)

-spec restart(server()) -> ok.

Equivalent to restart/0 but uses the provided Server.

set_pattern(Mod, Fun, Arity)

(since OTP 27.0)

-spec set_pattern(module(), atom(), arity() | '_') -> ok | {error, {trace_pattern, trace_pattern()}}.

Enables tracing for all functions matching the supplied pattern.

Patterns are additive, following the same rules as trace:function/4. Returns the number of functions matching the supplied pattern.

1> tprof:set_pattern(lists, seq, '_').
2
2> tprof:set_pattern(lists, keyfind, 3).
1
3> tprof:get_trace_map().
#{lists => [{keyfind,3},{seq,2},{seq,3}]}

If no functions match the pattern, an error tuple is returned:

> tprof:set_pattern(no_module, func, '_').
{error,{trace_pattern,no_module,func,'_'}}

Requires that the default tprof server has been started.

set_pattern(Server, Mod, Fun, Arity)

(since OTP 27.0)

-spec set_pattern(server(), module(), atom(), arity() | '_') ->
                     ok | {error, {trace_pattern, trace_pattern()}}.

Equivalent to set_pattern/3 but uses the provided Server.

start()

(since OTP 27.0)

-spec start() -> {ok, Server} | {error, Reason}
               when Server :: server(), Reason :: {already_started, pid()}.

Equivalent to start(#{}).

start(Config)

(since OTP 27.0)

-spec start(Config :: start_options()) -> {ok, Server} | {error, Reason}
               when Server :: server(), Reason :: {already_started, pid()}.

Starts the server, not supervised.

Profiling server stores current trace patterns and owns the trace session used for profiling.

If no session is provided in Config, then a default session called tprof is used and the profiling server is registered as tprof.

If session is provided in Config, then a session with that name is created and all profiling is done within that session. The profiling server is not registered in this case. When using tprof like this the pid/0 returned from this function needs to be provided to the functions in this module.

start_link()

(since OTP 27.0)

-spec start_link() -> {ok, Server} | {error, Reason}
                    when Server :: server(), Reason :: {already_started, pid()}.

Equivalent to start_link(#{}).

start_link(Config)

(since OTP 27.0)

-spec start_link(Config :: start_options()) -> {ok, Server} | {error, Reason}
                    when Server :: server(), Reason :: {already_started, pid()}.

Equivalent to start/1 but also links the profiling server to the caller.

stop()

(since OTP 27.0)

-spec stop() -> ok.

Stops the default tprof server and disable tracing enabled by the server.

stop(Server)

(since OTP 27.0)

-spec stop(server()) -> ok.

Equivalent to stop/0 but uses the provided Server.

xref

A Cross Reference Tool for analyzing dependencies between functions, modules, applications, and releases.

Calls between functions are either local calls such as f(), or external calls such as mod:f().

Module data, which are extracted from BEAM files, include local functions, exported functions, local calls, and external calls. By default, calls to built-in functions (BIF) are ignored, but if the option builtins, accepted by some of this module's functions, is set to true, calls to BIFs are included as well. It is the analyzing OTP version that decides what functions are BIFs. Functional objects are assumed to be called where they are created (and nowhere else).

Unresolved calls are calls to apply or spawn with variable module, variable function, or variable arguments. Examples are M:F(a), apply(M, f, [a]), and spawn(m, f(), Args). Unresolved calls are represented by calls where variable modules have been replaced with the atom '$M_EXPR', variable functions have been replaced with the atom '$F_EXPR', and variable number of arguments have been replaced with the number -1. The above mentioned examples are represented by calls to '$M_EXPR':'$F_EXPR'/1, '$M_EXPR':f/1, and m:'$F_EXPR'/-1. The unresolved calls are a subset of the external calls.

Warning

Unresolved calls make module data incomplete, which implies that the results of analyses may be invalid.

Applications are collections of modules. The BEAM files for the modules are located in the ebin subdirectory of the application directory. The name of the application directory determines the name and version of the application.

Releases are collections of applications located in the lib subdirectory of the release directory. There is more to read about applications and releases in the Design Principles book.

Xref servers are identified by names, supplied when creating new servers. Each Xref server holds a set of releases, a set of applications, and a set of modules with module data. Xref servers are independent of each other, and all analyses are evaluated in the context of one single Xref server (exceptions are the functions m/1 and d/1 which do not use servers at all).

The mode of an Xref server determines what module data are extracted from BEAM files as modules are added to the server. BEAM files compiled with the option debug_info contain "debug information", which is an abstract representation of the code.

In functions mode, which is the default mode, function calls and line numbers are extracted from debug information.

In modules mode, debug information is ignored if present, but dependencies between modules are extracted from other parts of the BEAM files. The modules mode is significantly less time and space consuming than the functions mode, but the analyses that can be done are limited.

An analyzed module is a module that has been added to an Xref server together with its module data. A library module is a module located in some directory mentioned in the library path. A library module is said to be used if some of its exported functions are used by some analyzed module. An unknown module is a module that is neither an analyzed module nor a library module, but whose exported functions are used by some analyzed module.

An unknown function is a used function that is neither local or exported by any analyzed module nor exported by any library module. An undefined function is an externally used function that is not exported by any analyzed module or library module. With this notion, a local function can be an undefined function, namely if it is externally used from some module. All unknown functions are also undefined functions; there is a figure in the User's Guide that illustrates this relationship.

The module attribute tag deprecated can be used to inform Xref about deprecated functions and optionally when functions are planned to be removed. A few examples show the idea:

-deprecated({f,1}). - The exported function f/1 is deprecated. Nothing is said whether f/1 will be removed or not.

-deprecated({f,1,"Use g/1 instead"}). - As above but with a descriptive string. The string is currently unused by xref but other tools can make use of it.

-deprecated({f,'_'}). - All exported functions f/0, f/1, and so on are deprecated.

-deprecated(module). - All exported functions in the module are deprecated. Equivalent to -deprecated({'_','_'})..

-deprecated([{g,1,next_version}]). - The function g/1 is deprecated and will be removed in next version.

-deprecated([{g,2,next_major_release}]). - The function g/2 is deprecated and will be removed in next major release.

-deprecated([{g,3,eventually}]). - The function g/3 is deprecated and will eventually be removed.

-deprecated({'_','_',eventually}). - All exported functions in the module are deprecated and will eventually be removed.

Before any analysis can take place, module data must be set up. For instance, the cross reference and the unknown functions are computed when all module data are known. The functions that need complete data (analyze/2,3, q/2,3, variables/1,2 take care of setting up data automatically. Module data need to be set up (again) after calls to any of the add, replace, remove, set_library_path/2,3, or update/1,2 functions.

The result of setting up module data is the Call Graph. A (directed) graph consists of a set of vertices and a set of (directed) edges. The edges represent calls (From, To) between functions, modules, applications, or releases. From is said to call To, and To is said to be used by From. The vertices of the Call Graph are the functions of all module data: local and exported functions of analyzed modules; used BIFs; used exported functions of library modules; and unknown functions. The functions module_info/0,1 added by the compiler are included among the exported functions, but only when called from some module. The edges are the function calls of all module data. A consequence of the edges being a set is that there is only one edge if a function is locally or externally used several times on one and the same line of code.

The Call Graph is represented by Erlang terms (the sets are lists), which is suitable for many analyses. But for analyses that look at chains of calls, a list representation is much too slow. Instead the representation offered by the digraph module is used. The translation of the list representation of the Call Graph - or a subgraph thereof - to the digraph representation does not come for free, so the language used for expressing queries to be described below has a special operator for this task and a possibility to save the digraph representation for subsequent analyses.

In addition to the Call Graph there is a graph called the Inter Call Graph. This is a graph of calls (From, To) such that there is a chain of calls from From to To in the Call Graph, and every From and To is an exported function or an unused local function. The vertices are the same as for the Call Graph.

Calls between modules, applications and releases are also directed graphs. The types of the vertices and edges of these graphs are (ranging from the most special to the most general): Fun for functions; Mod for modules; App for applications; and Rel for releases. The following paragraphs will describe the different constructs of the language used for selecting and analyzing parts of the graphs, beginning with the constants:

Expression ::= Constants

Constants ::= Consts | Consts : Type | RegExpr

Consts ::= Constant | [Constant, ...] | {Constant, ...}

Constant ::= Call | Const

Call ::= FunSpec -> FunSpec | {MFA, MFA} | AtomConst -> AtomConst | {AtomConst, AtomConst}

Const ::= AtomConst | FunSpec | MFA

AtomConst ::= Application | Module | Release

FunSpec ::= Module : Function / Arity

MFA ::= {Module, Function, Arity}

RegExpr ::= RegString : Type | RegFunc | RegFunc : Type

RegFunc ::= RegModule : RegFunction / RegArity

RegModule ::= RegAtom

RegFunction ::= RegAtom

RegArity ::= RegString | Number | _ | -1

RegAtom ::= RegString | Atom | _

RegString ::= - a regular expression, as described in the re module, enclosed in double quotes -

Type ::= Fun | Mod | App | Rel

Function ::= Atom

Application ::= Atom

Module ::= Atom

Release ::= Atom

Arity ::= Number | -1

Atom ::= - same as Erlang atoms -

Number ::= - same as non-negative Erlang integers -

Examples of constants are: kernel, kernel->stdlib, [kernel, sasl], [pg -> mnesia, {tv, mnesia}] : Mod. It is an error if an instance of Const does not match any vertex of any graph. If there are more than one vertex matching an untyped instance of AtomConst, then the one of the most general type is chosen. A list of constants is interpreted as a set of constants, all of the same type. A tuple of constants constitute a chain of calls (which may, but does not have to, correspond to an actual chain of calls of some graph). Assigning a type to a list or tuple of Constant is equivalent to assigning the type to each Constant.

Regular expressions are used as a means to select some of the vertices of a graph. A RegExpr consisting of a RegString and a type - an example is "xref_.*" : Mod - is interpreted as those modules (or applications or releases, depending on the type) that match the expression. Similarly, a RegFunc is interpreted as those vertices of the Call Graph that match the expression. An example is "xref_.*":"add_.*"/"(2|3)", which matches all add functions of arity two or three of any of the xref modules. Another example, one that matches all functions of arity 10 or more: _:_/"[1-9].+". Here _ is an abbreviation for ".*", that is, the regular expression that matches anything.

The syntax of variables is simple:

Expression ::= Variable

Variable ::= - same as Erlang variables -

There are two kinds of variables:

Predefined variables - hold module data, and cannot be assigned to but only used in queries.

User variables - can be assigned to, and are typically used for temporary results while evaluating a query, and for keeping results of queries for use in subsequent queries.

The predefined variables are (variables marked with (*) are available in functions mode only):

E - Call Graph Edges (*).

V - Call Graph Vertices (*).

M - Modules. All modules: analyzed modules, used library modules, and unknown modules.

A - Applications.

R - Releases.

ME - Module Edges. All module calls.

AE - Application Edges. All application calls.

RE - Release Edges. All release calls.

L - Local Functions (*). All local functions of analyzed modules.

X - Exported Functions. All exported functions of analyzed modules and all used exported functions of library modules.

F - Functions (*).

B - Used BIFs. B is empty if builtins is false for all analyzed modules.

U - Unknown Functions.

UU - Unused Functions (*). All local and exported functions of analyzed modules that have not been used.

XU - Externally Used Functions. Functions of all modules - including local functions - that have been used in some external call.

LU - Locally Used Functions (*). Functions of all modules that have been used in some local call.

OL - Functions with an attribute tag on_load (*).

LC - Local Calls (*).

XC - External Calls (*).

AM - Analyzed Modules.

UM - Unknown Modules.

LM - Used Library Modules.

UC - Unresolved Calls. Empty in modules mode.

EE - Inter Call Graph Edges (*).

DF - Deprecated Functions. All deprecated exported functions and all used deprecated BIFs.

DF_1 - Deprecated Functions. All deprecated functions to be removed in next version.

DF_2 - Deprecated Functions. All deprecated functions to be removed in next version or next major release.

DF_3 - Deprecated Functions. All deprecated functions to be removed in next version, next major release, or later.

These are a few facts about the predefined variables (the set operators + (union) and - (difference) as well as the cast operator (Type) are described below):

F is equal to L + X.

V is equal to X + L + B + U, where X, L, B and U are pairwise disjoint (that is, have no elements in common).

UU is equal to V - (XU + LU), where LU and XU may have elements in common. Put in another way:

V is equal to UU + XU + LU.

OL is a subset of F.

E is equal to LC + XC. Note that LC and XC may have elements in common, namely if some function is locally and externally used from one and the same function.

U is a subset of XU.

B is a subset of XU.

LU is equal to range LC.

XU is equal to range XC.

LU is a subset of F.

UU is a subset of F.

range UC is a subset of U.

M is equal to AM + LM + UM, where AM, LM and UM are pairwise disjoint.

ME is equal to (Mod) E.

AE is equal to (App) E.

RE is equal to (Rel) E.

(Mod) V is a subset of M. Equality holds if all analyzed modules have some local, exported, or unknown function.

(App) M is a subset of A. Equality holds if all applications have some module.

(Rel) A is a subset of R. Equality holds if all releases have some application.

DF_1 is a subset of DF_2.

DF_2 is a subset of DF_3.

DF_3 is a subset of DF.

DF is a subset of X + B.

An important notion is that of conversion of expressions. The syntax of a cast expression is:

Expression ::= ( Type ) Expression

The interpretation of the cast operator depends on the named type Type, the type of Expression, and the structure of the elements of the interpretation of Expression. If the named type is equal to the expression type, no conversion is done. Otherwise, the conversion is done one step at a time; (Fun) (App) RE, for instance, is equivalent to (Fun) (Mod) (App) RE. Now assume that the interpretation of Expression is a set of constants (functions, modules, applications or releases). If the named type is more general than the expression type, say Mod and Fun respectively, then the interpretation of the cast expression is the set of modules that have at least one of their functions mentioned in the interpretation of the expression. If the named type is more special than the expression type, say Fun and Mod, then the interpretation is the set of all the functions of the modules (in modules mode, the conversion is partial since the local functions are not known). The conversions to and from applications and releases work analogously. For instance, (App) "xref_.*" : Mod returns all applications containing at least one module such that xref_ is a prefix of the module name.

Now assume that the interpretation of Expression is a set of calls. If the named type is more general than the expression type, say Mod and Fun respectively, then the interpretation of the cast expression is the set of calls (M1, M2) such that the interpretation of the expression contains a call from some function of M1 to some function of M2. If the named type is more special than the expression type, say Fun and Mod, then the interpretation is the set of all function calls (F1, F2) such that the interpretation of the expression contains a call (M1, M2) and F1 is a function of M1 and F2 is a function of M2 (in modules mode, there are no functions calls, so a cast to Fun always yields an empty set). Again, the conversions to and from applications and releases work analogously.

The interpretation of constants and variables are sets, and those sets can be used as the basis for forming new sets by the application of set operators. The syntax:

Expression ::= Expression BinarySetOp Expression

BinarySetOp ::= + | * | -

+, * and - are interpreted as union, intersection and difference respectively: the union of two sets contains the elements of both sets; the intersection of two sets contains the elements common to both sets; and the difference of two sets contains the elements of the first set that are not members of the second set. The elements of the two sets must be of the same structure; for instance, a function call cannot be combined with a function. But if a cast operator can make the elements compatible, then the more general elements are converted to the less general element type. For instance, M + F is equivalent to (Fun) M + F, and E - AE is equivalent to E - (Fun) AE. One more example: X * xref : Mod is interpreted as the set of functions exported by the module xref; xref : Mod is converted to the more special type of X (Fun, that is) yielding all functions of xref, and the intersection with X (all functions exported by analyzed modules and library modules) is interpreted as those functions that are exported by some module and functions of xref.

There are also unary set operators:

Expression ::= UnarySetOp Expression

UnarySetOp ::= domain | range | strict

Recall that a call is a pair (From, To). domain applied to a set of calls is interpreted as the set of all vertices From, and range as the set of all vertices To. The interpretation of the strict operator is the operand with all calls of the form (A, A) removed.

The interpretation of the restriction operators is a subset of the first operand, a set of calls. The second operand, a set of vertices, is converted to the type of the first operand. The syntax of the restriction operators:

Expression ::= Expression RestrOp Expression

RestrOp ::= |

RestrOp ::= ||

RestrOp ::= |||

The interpretation in some detail for the three operators:

| - The subset of calls from any of the vertices.

|| - The subset of calls to any of the vertices.

||| - The subset of calls to and from any of the vertices. For all sets of calls CS and all sets of vertices VS, CS ||| VS is equivalent to CS | VS * CS || VS.

Two functions (modules, applications, releases) belong to the same strongly connected component if they call each other (in)directly. The interpretation of the components operator is the set of strongly connected components of a set of calls. The condensation of a set of calls is a new set of calls between the strongly connected components such that there is an edge between two components if there is some constant of the first component that calls some constant of the second component.

The interpretation of the of operator is a chain of calls of the second operand (a set of calls) that passes throw all of the vertices of the first operand (a tuple of constants), in the given order. The second operand is converted to the type of the first operand. For instance, the of operator can be used for finding out whether a function calls another function indirectly, and the chain of calls demonstrates how. The syntax of the graph analyzing operators:

Expression ::= Expression BinaryGraphOp Expression

Expression ::= UnaryGraphOp Expression

UnaryGraphOp ::= components | condensation

BinaryGraphOp ::= of

As was mentioned before, the graph analyses operate on the digraph representation of graphs. By default, the digraph representation is created when needed (and deleted when no longer used), but it can also be created explicitly by use of the closure operator:

Expression ::= ClosureOp Expression

ClosureOp ::= closure

The interpretation of the closure operator is the transitive closure of the operand.

The restriction operators are defined for closures as well; closure E | xref : Mod is interpreted as the direct or indirect function calls from the xref module, while the interpretation of E | xref : Mod is the set of direct calls from xref. If some graph is to be used in several graph analyses, it saves time to assign the digraph representation of the graph to a user variable, and then make sure that every graph analysis operates on that variable instead of the list representation of the graph.

The lines where functions are defined (more precisely: where the first clause begins) and the lines where functions are used are available in functions mode. The line numbers refer to the files where the functions are defined. This holds also for files included with the -include and -include_lib directives, which may result in functions defined apparently in the same line. The line operators are used for assigning line numbers to functions and for assigning sets of line numbers to function calls. The syntax is similar to the one of the cast operator:

Expression ::= ( LineOp) Expression

Expression ::= ( XLineOp) Expression

LineOp ::= Lin | ELin | LLin | XLin

XLineOp ::= XXL

The interpretation of the Lin operator applied to a set of functions assigns to each function the line number where the function is defined. Unknown functions and functions of library modules are assigned the number 0.

The interpretation of some LineOp operator applied to a set of function calls assigns to each call the set of line numbers where the first function calls the second function. Not all calls are assigned line numbers by all operators:

the Lin operator is defined for Call Graph Edges;

the LLin operator is defined for Local Calls.

the XLin operator is defined for External Calls.

the ELin operator is defined for Inter Call Graph Edges.

The Lin (LLin, XLin) operator assigns the lines where calls (local calls, external calls) are made. The ELin operator assigns to each call (From, To), for which it is defined, every line L such that there is a chain of calls from From to To beginning with a call on line L.

The XXL operator is defined for the interpretation of any of the LineOp operators applied to a set of function calls. The result is that of replacing the function call with a line numbered function call, that is, each of the two functions of the call is replaced by a pair of the function and the line where the function is defined. The effect of the XXL operator can be undone by the LineOp operators. For instance, (Lin) (XXL) (Lin) E is equivalent to (Lin) E.

The +, -, *, and # operators are defined for line number expressions, provided the operands are compatible. The LineOp operators are also defined for modules, applications, and releases; the operand is implicitly converted to functions. Similarly, the cast operator is defined for the interpretation of the LineOp operators.

The interpretation of the counting operator is the number of elements of a set. The operator is undefined for closures. The +, - and * operators are interpreted as the obvious arithmetical operators when applied to numbers. The syntax of the counting operator:

Expression ::= CountOp Expression

CountOp ::= #

All binary operators are left associative; for instance, A | B  || C is equivalent to (A | B) || C. The following is a list of all operators, in increasing order of precedence:

+, -

*

#

|, ||, |||

of

(Type)

closure, components, condensation, domain, range, strict

Parentheses are used for grouping, either to make an expression more readable or to override the default precedence of operators:

Expression ::= ( Expression )

A query is a non-empty sequence of statements. A statement is either an assignment of a user variable or an expression. The value of an assignment is the value of the right hand side expression. It makes no sense to put a plain expression anywhere else but last in queries. The syntax of queries is summarized by these productions:

Query ::= Statement, ...

Statement ::= Assignment | Expression

Assignment ::= Variable := Expression | Variable = Expression

A variable cannot be assigned a new value unless first removed. Variables assigned to by the = operator are removed at the end of the query, while variables assigned to by the := operator can only be removed by calls to forget. There are no user variables when module data need to be set up again; if any of the functions that make it necessary to set up module data again is called, all user variables are forgotten.

See Also

beam_lib, digraph, digraph_utils, re, User's Guide for Xref

Summary

Types

add_dir_rsn()

add_mod_rsn()

analysis()

analyze_rsn()

answer()

app_spec()

application()

call()

component()

constant()

define_at()

depr_flag()

directory()

file()

file_error()

func_spec()

funcall()

function_name()

info()

library()

library_path()

mod_spec()

mode()

path()

q_rsn()

rel_spec()

release()

string_position()

variable()

xarity()

xmfa()

xref()

Functions

add_application(XrefServer, Directory)

Equivalent to add_application(XrefServer, Directory, []).

add_application(XrefServer, Directory, Options)

Adds an application, the modules of the application, and module data of the modules to an Xref server.

add_directory(XrefServer, Directory)

Equivalent to add_directory(Name, Directory, []).

add_directory(XrefServer, Directory, Options)

Adds the modules found in the given directory and the modules' data to an Xref server.

add_module(XrefServer, File)

Equivalent to add_module(Name, File, []).

add_module(XrefServer, File, Options)

Adds a module and its module data to an Xref server.

add_release(XrefServer, Directory)

Equivalent to add_release(Name, Directory, []).

add_release(XrefServer, Directory, Options)

Adds a release, the applications of the release, the modules of the applications, and module data of the modules to an Xref server.

analyze(XrefServer, Analysis)

Equivalent to analyze(XrefServer, Analysis, []).

analyze(XrefServer, Analysis, Options)

Evaluates a predefined analysis.

d(Directory)

The modules found in the given directory are checked for calls to deprecated functions, calls to undefined functions, and for unused local functions.

forget(XrefServer)

Removes all user variables of an Xref server.

forget(XrefServer, Variables)

Removes the user variables given by Variables from an Xref server.

format_error(Error)

Given the error returned by any function of this module, the function format_error returns a descriptive string of the error in English.

get_default(XrefServer)

Returns a list of all options and their default values.

get_default(XrefServer, Option)

Returns the default value for option Option.

get_library_path(XrefServer)

Returns the library path.

info(XrefServer)

The info/1 function returns information as a list of pairs {Tag, term() in some order about the state and the module data of an Xref server.

info(XrefServer, Category)

Returns information about all items belonging to category Category. See info/3 for details.

info(XrefServer, Category, Items)

The info functions return information as a list of pairs {Tag, term()} in some order about the state and the module data of an Xref server.

m(FileOrModule)

The given BEAM file (with or without the .beam extension) or the file found by calling code:which(Module) is checked for calls to deprecated functions, calls to undefined functions, and for unused local functions.

q(XrefServer, Query)

Equivalent to q(XrefServer, Query, []).

q(XrefServer, Query, Options)

Evaluates a query in the context of an Xref server, and returns the value of the last statement.

remove_application(XrefServer, Applications)

Removes applications and their modules and module data from an Xref server.

remove_module(XrefServer, Modules)

Removes analyzed modules and module data from an Xref server.

remove_release(XrefServer, Releases)

Removes releases and their applications, modules, and module data from an Xref server.

replace_application(XrefServer, Application, Directory)

Equivalent to replace_application(XrefServer, Application, Directory, []).

replace_application(XrefServer, Application, Directory, Options)

Replaces the modules of an application with other modules read from an application directory.

replace_module(XrefServer, Module, File)

Equivalent to replace_module(XrefServer, Module, File, []).

replace_module(XrefServer, Module, File, Options)

Replaces module data of an analyzed module with data read from a BEAM file.

set_default(XrefServer, OptionValues)

Sets default values for multiple options given by OptionValues.

set_default(XrefServer, Option, Value)

Sets the default value of one or more options.

set_library_path(XrefServer, LibraryPath)

Equivalent to set_library_path(XrefServer, LibraryPath, []).

set_library_path(XrefServer, LibraryPath, Options)

Sets the library path.

start(NameOrOptions)

Creates an Xref server.

start(Name, Options)

Creates an Xref server with a given name.

stop(XrefServer)

Stops an Xref server.

update(XrefServer)

Equivalent to update(XrefServer, []).

update(XrefServer, Options)

Replaces the module data of all analyzed modules the BEAM files of which have been modified since last read by an add function or update.

variables(XrefServer)

Equivalent to variables(XrefServer, []).

variables(XrefServer, Options)

Returns a sorted lists of the names of the variables of an Xref server.

Types

add_dir_rsn()

(not exported)

-type add_dir_rsn() ::
          {file_error, file(), file_error()} |
          {invalid_filename, term()} |
          {invalid_options, term()} |
          {unrecognized_file, file()} |
          beam_lib:chnk_rsn().

add_mod_rsn()

(not exported)

-type add_mod_rsn() ::
          {file_error, file(), file_error()} |
          {invalid_filename, term()} |
          {invalid_options, term()} |
          {module_clash, {module(), file(), file()}} |
          {no_debug_info, file()} |
          beam_lib:chnk_rsn().

analysis()

(not exported)

-type analysis() ::
          undefined_function_calls | undefined_functions | locals_not_used | exports_not_used |
          deprecated_function_calls |
          {deprecated_function_calls, DeprFlag :: depr_flag()} |
          deprecated_functions |
          {deprecated_functions, DeprFlag :: depr_flag()} |
          {call, FuncSpec :: func_spec()} |
          {use, FuncSpec :: func_spec()} |
          {module_call, ModSpec :: mod_spec()} |
          {module_use, ModSpec :: mod_spec()} |
          {application_call, AppSpec :: app_spec()} |
          {application_use, AppSpec :: app_spec()} |
          {release_call, RelSpec :: rel_spec()} |
          {release_use, RelSpec :: rel_spec()}.

analyze_rsn()

(not exported)

-type analyze_rsn() ::
          {invalid_options, term()} |
          {parse_error, string_position(), term()} |
          {unavailable_analysis, term()} |
          {unknown_analysis, term()} |
          {unknown_constant, string()} |
          {unknown_variable, variable()}.

answer()

(not exported)

-type answer() ::
          false |
          [constant()] |
          [(Call :: call()) | (ComponentCall :: {component(), component()})] |
          [Component :: component()] |
          non_neg_integer() |
          [DefineAt :: define_at()] |
          [CallAt :: {funcall(), LineNumbers :: [non_neg_integer()]}] |
          [AllLines :: {{define_at(), define_at()}, LineNumbers :: [non_neg_integer()]}].

app_spec()

(not exported)

-type app_spec() :: application() | [application()].

application()

(not exported)

-type application() :: atom().

call()

(not exported)

-type call() :: {atom(), atom()} | funcall().

component()

(not exported)

-type component() :: [constant()].

constant()

(not exported)

-type constant() :: xmfa() | module() | application() | release().

define_at()

(not exported)

-type define_at() :: {xmfa(), LineNumber :: non_neg_integer()}.

depr_flag()

(not exported)

-type depr_flag() :: next_version | next_major_release | eventually.

directory()

(not exported)

-type directory() :: atom() | file:filename().

file()

(not exported)

-type file() :: file:filename().

file_error()

(not exported)

-type file_error() :: atom().

func_spec()

(not exported)

-type func_spec() :: xmfa() | [xmfa()].

funcall()

(not exported)

-type funcall() :: {xmfa(), xmfa()}.

function_name()

(not exported)

-type function_name() :: atom().

info()

(not exported)

-type info() ::
          {application, Application :: [application()]} |
          {builtins, boolean()} |
          {directory, directory()} |
          {library_path, library_path()} |
          {mode, mode()} |
          {no_analyzed_modules, non_neg_integer()} |
          {no_applications, non_neg_integer()} |
          {no_calls, {NoResolved :: non_neg_integer(), NoUnresolved :: non_neg_integer()}} |
          {no_function_calls,
           {NoLocal :: non_neg_integer(),
            NoResolvedExternal :: non_neg_integer(),
            NoUnresolved :: non_neg_integer()}} |
          {no_functions, {NoLocal :: non_neg_integer(), NoExternal :: non_neg_integer()}} |
          {no_inter_function_calls, non_neg_integer()} |
          {no_releases, non_neg_integer()} |
          {release, Release :: [release()]} |
          {version, Version :: [non_neg_integer()]}.

library()

(not exported)

-type library() :: atom().

library_path()

(not exported)

-type library_path() :: path() | code_path.

mod_spec()

(not exported)

-type mod_spec() :: module() | [module()].

mode()

(not exported)

-type mode() :: functions | modules.

path()

(not exported)

-type path() :: [file()].

q_rsn()

(not exported)

-type q_rsn() ::
          {invalid_options, term()} |
          {parse_error, string_position(), term()} |
          {type_error, string()} |
          {type_mismatch, string(), string()} |
          {unknown_analysis, term()} |
          {unknown_constant, string()} |
          {unknown_variable, variable()} |
          {variable_reassigned, string()}.

rel_spec()

(not exported)

-type rel_spec() :: release() | [release()].

release()

(not exported)

-type release() :: atom().

string_position()

(not exported)

-type string_position() :: pos_integer().

variable()

(not exported)

-type variable() :: atom().

xarity()

(not exported)

-type xarity() :: arity() | -1.

xmfa()

(not exported)

-type xmfa() :: {module(), function_name(), xarity()}.

xref()

(not exported)

-type xref() :: atom() | pid().

Functions

add_application(XrefServer, Directory)

-spec add_application(XrefServer, Directory) -> {ok, application()} | {error, module(), Reason}
                         when
                             XrefServer :: xref(),
                             Directory :: directory(),
                             Reason ::
                                 {application_clash, {application(), directory(), directory()}} |
                                 add_dir_rsn().

Equivalent to add_application(XrefServer, Directory, []).

add_application(XrefServer, Directory, Options)

-spec add_application(XrefServer, Directory, Options) -> {ok, application()} | {error, module(), Reason}
                         when
                             XrefServer :: xref(),
                             Directory :: directory(),
                             Options :: Option | [Option],
                             Option ::
                                 {builtins, boolean()} |
                                 {name, application()} |
                                 {verbose, boolean()} |
                                 {warnings, boolean()} |
                                 builtins | verbose | warnings,
                             Reason ::
                                 {application_clash, {application(), directory(), directory()}} |
                                 add_dir_rsn().

Adds an application, the modules of the application, and module data of the modules to an Xref server.

The modules will be members of the application. The default is to use the base name of the directory with the version removed as application name, but this can be overridden by the name option. Returns the name of the application.

If the given directory has a subdirectory named ebin, modules (BEAM files) are searched for in that directory, otherwise modules are searched for in the given directory.

If the mode of the Xref server is functions, BEAM files that contain no debug information are ignored.

add_directory(XrefServer, Directory)

-spec add_directory(XrefServer, Directory) -> {ok, Modules} | {error, module(), Reason}
                       when
                           XrefServer :: xref(),
                           Directory :: directory(),
                           Modules :: [module()],
                           Reason ::
                               {application_clash, {application(), directory(), directory()}} |
                               add_dir_rsn().

Equivalent to add_directory(Name, Directory, []).

add_directory(XrefServer, Directory, Options)

-spec add_directory(XrefServer, Directory, Options) -> {ok, Modules} | {error, module(), Reason}
                       when
                           XrefServer :: xref(),
                           Directory :: directory(),
                           Options :: Option | [Option],
                           Option ::
                               {builtins, boolean()} |
                               {recurse, boolean()} |
                               {verbose, boolean()} |
                               {warnings, boolean()} |
                               builtins | recurse | verbose | warnings,
                           Modules :: [module()],
                           Reason :: add_dir_rsn().

Adds the modules found in the given directory and the modules' data to an Xref server.

The default is not to examine subdirectories, but if the option recurse has the value true, modules are searched for in subdirectories on all levels as well as in the given directory. Returns a sorted list of the names of the added modules.

The modules added will not be members of any applications.

If the mode of the Xref server is functions, BEAM files that contain no debug information are ignored.

add_module(XrefServer, File)

-spec add_module(XrefServer, File) -> {ok, module()} | {error, module(), Reason}
                    when XrefServer :: xref(), File :: file:filename(), Reason :: add_mod_rsn().

Equivalent to add_module(Name, File, []).

add_module(XrefServer, File, Options)

-spec add_module(XrefServer, File, Options) -> {ok, module()} | {error, module(), Reason}
                    when
                        XrefServer :: xref(),
                        File :: file:filename(),
                        Options :: Option | [Option],
                        Option ::
                            {builtins, boolean()} |
                            {verbose, boolean()} |
                            {warnings, boolean()} |
                            builtins | verbose | warnings,
                        Reason :: add_mod_rsn().

Adds a module and its module data to an Xref server.

The module will not be member of any application. Returns the name of the module.

If the mode of the Xref server is functions, and the BEAM file contains no debug information, the error message no_debug_info is returned.

add_release(XrefServer, Directory)

-spec add_release(XrefServer, Directory) -> {ok, release()} | {error, module(), Reason}
                     when
                         XrefServer :: xref(),
                         Directory :: directory(),
                         Reason ::
                             {application_clash, {application(), directory(), directory()}} |
                             {release_clash, {release(), directory(), directory()}} |
                             add_dir_rsn().

Equivalent to add_release(Name, Directory, []).

add_release(XrefServer, Directory, Options)

-spec add_release(XrefServer, Directory, Options) -> {ok, release()} | {error, module(), Reason}
                     when
                         XrefServer :: xref(),
                         Directory :: directory(),
                         Options :: Option | [Option],
                         Option ::
                             {builtins, boolean()} |
                             {name, release()} |
                             {verbose, boolean()} |
                             {warnings, boolean()} |
                             builtins | verbose | warnings,
                         Reason ::
                             {application_clash, {application(), directory(), directory()}} |
                             {release_clash, {release(), directory(), directory()}} |
                             add_dir_rsn().

Adds a release, the applications of the release, the modules of the applications, and module data of the modules to an Xref server.

The applications will be members of the release, and the modules will be members of the applications. The default is to use the base name of the directory as release name, but this can be overridden by the name option. Returns the name of the release.

If the given directory has a subdirectory named lib, the directories in that directory are assumed to be application directories, otherwise all subdirectories of the given directory are assumed to be application directories. If there are several versions of some application, the one with the highest version is chosen.

If the mode of the Xref server is functions, BEAM files that contain no debug information are ignored.

analyze(XrefServer, Analysis)

-spec analyze(XrefServer, Analysis) -> {ok, Answer} | {error, module(), Reason}
                 when
                     XrefServer :: xref(),
                     Analysis :: analysis(),
                     Answer :: [term()],
                     Reason :: analyze_rsn().

Equivalent to analyze(XrefServer, Analysis, []).

analyze(XrefServer, Analysis, Options)

-spec analyze(XrefServer, Analysis, Options) -> {ok, Answer} | {error, module(), Reason}
                 when
                     XrefServer :: xref(),
                     Analysis :: analysis(),
                     Options :: Option | [Option],
                     Option :: {verbose, boolean()} | verbose,
                     Answer :: [term()],
                     Reason :: analyze_rsn().

Evaluates a predefined analysis.

Returns a sorted list without duplicates of call/0 or constant/0, depending on the chosen analysis. The predefined analyses, which operate on all analyzed modules, are (analyses marked with (*) are available only in mode functions):

undefined_function_calls(*) - Returns a list of calls to undefined functions.

undefined_functions - Returns a list of undefined functions.

locals_not_used(*) - Returns a list of local functions that have not been locally used.

exports_not_used - Returns a list of exported functions that have not been externally used. Note that in modules mode, M:behaviour_info/1 is never reported as unused.

deprecated_function_calls(*) - Returns a list of external calls to deprecated functions.

{deprecated_function_calls, DeprFlag}(*) - Returns a list of external calls to deprecated functions. If DeprFlag is equal to next_version, calls to functions to be removed in next version are returned. If DeprFlag is equal to next_major_release, calls to functions to be removed in next major release are returned as well as calls to functions to be removed in next version. Finally, if DeprFlag is equal to eventually, all calls to functions to be removed are returned, including calls to functions to be removed in next version or next major release.

deprecated_functions - Returns a list of externally used deprecated functions.

{deprecated_functions, DeprFlag} - Returns a list of externally used deprecated functions. If DeprFlag is equal to next_version, functions to be removed in next version are returned. If DeprFlag is equal to next_major_release, functions to be removed in next major release are returned as well as functions to be removed in next version. Finally, if DeprFlag is equal to eventually, all functions to be removed are returned, including functions to be removed in next version or next major release.

{call, FuncSpec}(*) - Returns a list of functions called by some of the given functions.

{use, FuncSpec}(*) - Returns a list of functions that use some of the given functions.

{module_call, ModSpec} - Returns a list of modules called by some of the given modules.

{module_use, ModSpec} - Returns a list of modules that use some of the given modules.

{application_call, AppSpec} - Returns a list of applications called by some of the given applications.

{application_use, AppSpec} - Returns a list of applications that use some of the given applications.

{release_call, RelSpec} - Returns a list of releases called by some of the given releases.

{release_use, RelSpec} - Returns a list of releases that use some of the given releases.

d(Directory)

-spec d(Directory) -> [DebugInfoResult] | [NoDebugInfoResult] | {error, module(), Reason}
           when
               Directory :: directory(),
               DebugInfoResult ::
                   {deprecated, [funcall()]} | {undefined, [funcall()]} | {unused, [mfa()]},
               NoDebugInfoResult :: {deprecated, [xmfa()]} | {undefined, [xmfa()]},
               Reason ::
                   {file_error, file(), file_error()} |
                   {invalid_filename, term()} |
                   {unrecognized_file, file()} |
                   beam_lib:chnk_rsn().

The modules found in the given directory are checked for calls to deprecated functions, calls to undefined functions, and for unused local functions.

The code path is used as library path.

If some of the found BEAM files contain debug information, then those modules are checked and a list of tuples is returned. The first element of each tuple is one of:

deprecated, the second element is a sorted list of calls to deprecated functions;

undefined, the second element is a sorted list of calls to undefined functions;

unused, the second element is a sorted list of unused local functions.

If no BEAM file contains debug information, then a list of tuples is returned. The first element of each tuple is one of:

deprecated, the second element is a sorted list of externally used deprecated functions;

undefined, the second element is a sorted list of undefined functions.

forget(XrefServer)

-spec forget(XrefServer) -> ok when XrefServer :: xref().

Removes all user variables of an Xref server.

forget(XrefServer, Variables)

-spec forget(XrefServer, Variables) -> ok | {error, module(), Reason}
                when
                    XrefServer :: xref(),
                    Variables :: variable() | [variable()],
                    Reason :: {not_user_variable, term()}.

Removes the user variables given by Variables from an Xref server.

format_error(Error)

-spec format_error(Error) -> io_lib:chars() when Error :: {error, module(), Reason :: term()}.

Given the error returned by any function of this module, the function format_error returns a descriptive string of the error in English.

For file errors, the function file:format_error/1 is called.

get_default(XrefServer)

-spec get_default(XrefServer) -> [{Option, Value}]
                     when
                         XrefServer :: xref(),
                         Option :: builtins | recurse | verbose | warnings,
                         Value :: boolean().

Returns a list of all options and their default values.

get_default(XrefServer, Option)

-spec get_default(XrefServer, Option) -> {ok, Value} | {error, module(), Reason}
                     when
                         XrefServer :: xref(),
                         Option :: builtins | recurse | verbose | warnings,
                         Value :: boolean(),
                         Reason :: {invalid_options, term()}.

Returns the default value for option Option.

get_library_path(XrefServer)

-spec get_library_path(XrefServer) -> {ok, LibraryPath}
                          when XrefServer :: xref(), LibraryPath :: library_path().

Returns the library path.

info(XrefServer)

-spec info(XrefServer) -> [Info] when XrefServer :: xref(), Info :: info().

The info/1 function returns information as a list of pairs {Tag, term() in some order about the state and the module data of an Xref server.

info/1 returns information with the following tags (tags marked with (*) are only available in functions mode):

library_path, the library path;

mode, the mode;

no_releases, number of releases;

no_applications, total number of applications (of all releases);

no_analyzed_modules, total number of analyzed modules;

no_calls (*), total number of calls (in all modules), regarding instances of one function call in different lines as separate calls;

no_function_calls (*), total number of local calls, resolved external calls and unresolved calls;

no_functions (*), total number of local and exported functions;

no_inter_function_calls (*), total number of calls of the Inter Call Graph.

info(XrefServer, Category)

-spec info(XrefServer, Category) -> [{Item, [Info]}] | {error, module(), {no_such_info, Category}}
              when
                  XrefServer :: xref(),
                  Category :: modules | applications | releases | libraries,
                  Item :: module() | application() | release() | library(),
                  Info :: info().

Returns information about all items belonging to category Category. See info/3 for details.

info(XrefServer, Category, Items)

-spec info(XrefServer, Category, Items) -> [{Item, [Info]}] | {error, module(), Reason}
              when
                  XrefServer :: xref(),
                  Category :: modules | applications | releases | libraries,
                  Items :: Item | [Item],
                  Item :: module() | application() | release() | library(),
                  Info :: info(),
                  Reason ::
                      {no_such_application, Item} |
                      {no_such_info, Category} |
                      {no_such_library, Item} |
                      {no_such_module, Item} |
                      {no_such_release, Item}.

The info functions return information as a list of pairs {Tag, term()} in some order about the state and the module data of an Xref server.

info/2 and info/3 return information about all or some of the analyzed modules, applications, releases, or library modules of an Xref server. The following information is returned for every analyzed module:

application, an empty list if the module does not belong to any application, otherwise a list of the application name;

builtins, whether calls to BIFs are included in the module's data;

directory, the directory where the module's BEAM file is located;

no_calls (*), number of calls, regarding instances of one function call in different lines as separate calls;

no_function_calls (*), number of local calls, resolved external calls and unresolved calls;

no_functions (*), number of local and exported functions;

no_inter_function_calls (*), number of calls of the Inter Call Graph;

The following information is returned for every application:

directory, the directory where the modules' BEAM files are located;

no_analyzed_modules, number of analyzed modules;

no_calls (*), number of calls of the application's modules, regarding instances of one function call in different lines as separate calls;

no_function_calls (*), number of local calls, resolved external calls and unresolved calls of the application's modules;

no_functions (*), number of local and exported functions of the application's modules;

no_inter_function_calls (*), number of calls of the Inter Call Graph of the application's modules;

release, an empty list if the application does not belong to any release, otherwise a list of the release name;

version, the application's version as a list of numbers. For instance, the directory "kernel-2.6" results in the application name kernel and the application version [2,6]; "kernel" yields the name kernel and the version [].

The following information is returned for every release:

directory, the release directory;

no_analyzed_modules, number of analyzed modules;

no_applications, number of applications;

no_calls (*), number of calls of the release's modules, regarding instances of one function call in different lines as separate calls;

no_function_calls (*), number of local calls, resolved external calls and unresolved calls of the release's modules;

no_functions (*), number of local and exported functions of the release's modules;

no_inter_function_calls (*), number of calls of the Inter Call Graph of the release's modules.

The following information is returned for every library module:

directory, the directory where the library module's BEAM file is located.

For every number of calls, functions, and so on returned by the no_ tags, there is a query returning the same number. Listed below are examples of such queries. Some of the queries return the sum of a two or more of the no_ tags numbers. mod (app, rel) refers to any module (application, release).

no_analyzed_modules

"# AM" (info/1)

"# (Mod) app:App" (application)

"# (Mod) rel:Rel" (release)

no_applications

"# A" (info/1)

no_calls. The sum of the number of resolved and unresolved calls:

"# (XLin) E + # (LLin) E" (info/1)

"T = E | mod:Mod, # (LLin) T + # (XLin) T" (module)

"T = E | app:App, # (LLin) T + # (XLin) T" (application)

"T = E | rel:Rel, # (LLin) T + # (XLin) T" (release)

no_functions. Functions in library modules and the functions module_info/0,1 are not counted by info. Assuming that "Extra := _:module_info/\"(0|1)\" + LM" has been evaluated, the sum of the number of local and exported functions are:

"# (F - Extra)" (info/1)

"# (F * mod:Mod - Extra)" (module)

"# (F * app:App - Extra)" (application)

"# (F * rel:Rel - Extra)" (release)

no_function_calls. The sum of the number of local calls, resolved external calls and unresolved calls:

"# LC + # XC" (info/1)

"# LC | mod:Mod + # XC | mod:Mod" (module)

"# LC | app:App + # XC | app:App" (application)

"# LC | rel:Rel + # XC | mod:Rel" (release)

no_inter_function_calls

"# EE" (info/1)

"# EE | mod:Mod" (module)

"# EE | app:App" (application)

"# EE | rel:Rel" (release)

no_releases

"# R" (info/1)

m(FileOrModule)

-spec m(FileOrModule) -> [DebugInfoResult] | [NoDebugInfoResult] | {error, module(), Reason}
           when
               FileOrModule :: file:filename() | module(),
               DebugInfoResult ::
                   {deprecated, [funcall()]} | {undefined, [funcall()]} | {unused, [mfa()]},
               NoDebugInfoResult :: {deprecated, [xmfa()]} | {undefined, [xmfa()]},
               Reason ::
                   {cover_compiled, Module :: module()} |
                   {file_error, file(), file_error()} |
                   {interpreted, Module :: module()} |
                   {invalid_filename, term()} |
                   {no_such_module, Module :: module()} |
                   beam_lib:chnk_rsn().

The given BEAM file (with or without the .beam extension) or the file found by calling code:which(Module) is checked for calls to deprecated functions, calls to undefined functions, and for unused local functions.

The code path is used as library path.

If the BEAM file contains debug information, a list of tuples is returned. The first element of each tuple is one of:

deprecated, the second element is a sorted list of calls to deprecated functions;

undefined, the second element is a sorted list of calls to undefined functions;

unused, the second element is a sorted list of unused local functions.

If the BEAM file does not contain debug information, a list of tuples is returned. The first element of each tuple is one of:

deprecated, the second element is a sorted list of externally used deprecated functions;

undefined, the second element is a sorted list of undefined functions.

q(XrefServer, Query)

-spec q(XrefServer, Query) -> {ok, Answer} | {error, module(), Reason}
           when XrefServer :: xref(), Query :: string() | atom(), Answer :: answer(), Reason :: q_rsn().

Equivalent to q(XrefServer, Query, []).

q(XrefServer, Query, Options)

-spec q(XrefServer, Query, Options) -> {ok, Answer} | {error, module(), Reason}
           when
               XrefServer :: xref(),
               Query :: string() | atom(),
               Options :: Option | [Option],
               Option :: {verbose, boolean()} | verbose,
               Answer :: answer(),
               Reason :: q_rsn().

Evaluates a query in the context of an Xref server, and returns the value of the last statement.

The syntax of the value depends on the expression:

A set of calls is represented by a sorted list without duplicates of call/0.

A set of constants is represented by a sorted list without duplicates of constant/0.

A set of strongly connected components is a sorted list without duplicates of Component.

A set of calls between strongly connected components is a sorted list without duplicates of ComponentCall.

A chain of calls is represented by a list of constant/0. The list contains the From vertex of every call and the To vertex of the last call.

The of operator returns false if no chain of calls between the given constants can be found.

The value of the closure operator (the digraph representation) is represented by the atom 'closure()'.

A set of line numbered functions is represented by a sorted list without duplicates of DefineAt.

A set of line numbered function calls is represented by a sorted list without duplicates of CallAt.

A set of line numbered functions and function calls is represented by a sorted list without duplicates of AllLines.

For both CallAt and AllLines it holds that for no list element is LineNumbers an empty list; such elements have been removed. The constants of component and the integers of LineNumbers are sorted and without duplicates.

remove_application(XrefServer, Applications)

-spec remove_application(XrefServer, Applications) -> ok | {error, module(), Reason}
                            when
                                XrefServer :: xref(),
                                Applications :: application() | [application()],
                                Reason :: {no_such_application, application()}.

Removes applications and their modules and module data from an Xref server.

remove_module(XrefServer, Modules)

-spec remove_module(XrefServer, Modules) -> ok | {error, module(), Reason}
                       when
                           XrefServer :: xref(),
                           Modules :: module() | [module()],
                           Reason :: {no_such_module, module()}.

Removes analyzed modules and module data from an Xref server.

remove_release(XrefServer, Releases)

-spec remove_release(XrefServer, Releases) -> ok | {error, module(), Reason}
                        when
                            XrefServer :: xref(),
                            Releases :: release() | [release()],
                            Reason :: {no_such_release, release()}.

Removes releases and their applications, modules, and module data from an Xref server.

replace_application(XrefServer, Application, Directory)

-spec replace_application(XrefServer, Application, Directory) ->
                             {ok, Application} | {error, module(), Reason}
                             when
                                 XrefServer :: xref(),
                                 Application :: application(),
                                 Directory :: directory(),
                                 Reason :: {no_such_application, Application} | add_dir_rsn().

Equivalent to replace_application(XrefServer, Application, Directory, []).

replace_application(XrefServer, Application, Directory, Options)

-spec replace_application(XrefServer, Application, Directory, Options) ->
                             {ok, Application} | {error, module(), Reason}
                             when
                                 XrefServer :: xref(),
                                 Application :: application(),
                                 Directory :: directory(),
                                 Options :: Option | [Option],
                                 Option ::
                                     {builtins, boolean()} |
                                     {verbose, boolean()} |
                                     {warnings, boolean()} |
                                     builtins | verbose | warnings,
                                 Reason ::
                                     {application_clash, {application(), directory(), directory()}} |
                                     {no_such_application, Application} |
                                     add_dir_rsn().

Replaces the modules of an application with other modules read from an application directory.

Release membership of the application is retained. Note that the name of the application is kept; the name of the given directory is not used.

replace_module(XrefServer, Module, File)

-spec replace_module(XrefServer, Module, File) -> {ok, Module} | {error, module(), Reason}
                        when
                            XrefServer :: xref(),
                            Module :: module(),
                            File :: file(),
                            Reason ::
                                {module_mismatch, Module, ReadModule :: module()} |
                                {no_such_module, Module} |
                                add_mod_rsn().

Equivalent to replace_module(XrefServer, Module, File, []).

replace_module(XrefServer, Module, File, Options)

-spec replace_module(XrefServer, Module, File, Options) -> {ok, Module} | {error, module(), Reason}
                        when
                            XrefServer :: xref(),
                            Module :: module(),
                            File :: file(),
                            Options :: Option | [Option],
                            Option :: {verbose, boolean()} | {warnings, boolean()} | verbose | warnings,
                            Reason ::
                                {module_mismatch, Module, ReadModule :: module()} |
                                {no_such_module, Module} |
                                add_mod_rsn().

Replaces module data of an analyzed module with data read from a BEAM file.

Application membership of the module is retained, and so is the value of the builtins option of the module. An error is returned if the name of the read module differs from the given module.

The update function is an alternative for updating module data of recompiled modules.

set_default(XrefServer, OptionValues)

-spec set_default(XrefServer, OptionValues) -> ok | {error, module(), Reason}
                     when
                         XrefServer :: xref(),
                         OptionValues :: OptionValue | [OptionValue],
                         OptionValue :: {Option, Value},
                         Option :: builtins | recurse | verbose | warnings,
                         Value :: boolean(),
                         Reason :: {invalid_options, term()}.

Sets default values for multiple options given by OptionValues.

See set_default/3 for the name of options and their allowed values.

set_default(XrefServer, Option, Value)

-spec set_default(XrefServer, Option, Value) -> {ok, OldValue} | {error, module(), Reason}
                     when
                         XrefServer :: xref(),
                         Option :: builtins | recurse | verbose | warnings,
                         Value :: boolean(),
                         OldValue :: boolean(),
                         Reason :: {invalid_options, term()}.

Sets the default value of one or more options.

The options that can be set this way are:

builtins, with initial default value false;

recurse, with initial default value false;

verbose, with initial default value false;

warnings, with initial default value true.

The initial default values are set when creating an Xref server.

set_library_path(XrefServer, LibraryPath)

-spec set_library_path(XrefServer, LibraryPath) -> ok | {error, module(), Reason}
                          when
                              XrefServer :: xref(),
                              LibraryPath :: library_path(),
                              Reason ::
                                  {file_error, file(), file_error()} |
                                  {invalid_options, term()} |
                                  {invalid_path, term()}.

Equivalent to set_library_path(XrefServer, LibraryPath, []).

set_library_path(XrefServer, LibraryPath, Options)

-spec set_library_path(XrefServer, LibraryPath, Options) -> ok | {error, module(), Reason}
                          when
                              XrefServer :: xref(),
                              LibraryPath :: library_path(),
                              Options :: Option | [Option],
                              Option :: {verbose, boolean()} | verbose,
                              Reason :: {invalid_options, term()} | {invalid_path, term()}.

Sets the library path.

If the given path is a list of directories, the set of library modules is determined by choosing the first module encountered while traversing the directories in the given order, for those modules that occur in more than one directory. By default, the library path is an empty list.

The library path code_path is used by the functions m/1 and d/1, but can also be set explicitly. However, note that the code path will be traversed once for each used library module while setting up module data. On the other hand, if there are only a few modules that are used but not analyzed, using code_path may be faster than setting the library path to code:get_path/0.

If the library path is set to code_path, the set of library modules is not determined, and the info functions will return empty lists of library modules.

start(NameOrOptions)

-spec start(NameOrOptions) -> {ok, pid()} | {error, {already_started, pid()}}
               when
                   NameOrOptions :: Name | Options,
                   Name :: atom(),
                   Options :: Option | [Option],
                   Option :: {xref_mode, mode()} | term().

Creates an Xref server.

The process can optionally be given a name. The default mode is functions. Options that are not recognized by Xref are passed on to gen_server:start/4.

start(Name, Options)

-spec start(Name, Options) -> {ok, pid()} | {error, {already_started, pid()}}
               when Name :: atom(), Options :: Option | [Option], Option :: {xref_mode, mode()} | term().

Creates an Xref server with a given name.

The default mode is functions. Options that are not recognized by Xref are passed on to gen_server:start/4.

stop(XrefServer)

-spec stop(XrefServer) -> stopped when XrefServer :: xref().

Stops an Xref server.

update(XrefServer)

-spec update(XrefServer) -> {ok, Modules} | {error, module(), Reason}
                when
                    XrefServer :: xref(),
                    Modules :: [module()],
                    Reason :: {module_mismatch, module(), ReadModule :: module()} | add_mod_rsn().

Equivalent to update(XrefServer, []).

update(XrefServer, Options)

-spec update(XrefServer, Options) -> {ok, Modules} | {error, module(), Reason}
                when
                    XrefServer :: xref(),
                    Options :: Option | [Option],
                    Option :: {verbose, boolean()} | {warnings, boolean()} | verbose | warnings,
                    Modules :: [module()],
                    Reason :: {module_mismatch, module(), ReadModule :: module()} | add_mod_rsn().

Replaces the module data of all analyzed modules the BEAM files of which have been modified since last read by an add function or update.

Application membership of the modules is retained, and so is the value of the builtinsoption. Returns a sorted list of the names of the replaced modules.

variables(XrefServer)

-spec variables(XrefServer) -> {ok, [VariableInfo]}
                   when
                       XrefServer :: xref(),
                       VariableInfo :: {predefined, [variable()]} | {user, [variable()]}.

Equivalent to variables(XrefServer, []).

variables(XrefServer, Options)

-spec variables(XrefServer, Options) -> {ok, [VariableInfo]}
                   when
                       XrefServer :: xref(),
                       Options :: Option | [Option],
                       Option :: predefined | user | {verbose, boolean()} | verbose,
                       VariableInfo :: {predefined, [variable()]} | {user, [variable()]}.

Returns a sorted lists of the names of the variables of an Xref server.

The default is to return only the user variables.