The module coast
provides a set of functions for coverage and call
statistics analysis of Erlang programs.
Coverage analysis consists of monitoring executing programs, observing if each line of code is executed, and, if so, the number of times.
Call statistics analysis consists of monitoring executing programs, observing the number of times certain modules, and the functions in them, are called. This analysis may be done in various levels of detail.
Before any analysis can take place, the module(s) must be compiled in a special way. Execution may then take place - in this phase executional data is gathered: in an internal database, a counter for each statement in the module(s) is incremented every time that particular statement is executed. In the final phase we analyse the collected data, presenting it in various ways. (The observant reader may here correctly conclude that a module has to be executed, at least partially, before any useful coverage and call statistics analysis can take place.)
compile(Module) -> Result
compile(Module, Options) -> Result
Module = ModuleName | [ModuleName]
ModuleName = atom() | string()
Options = [CompilerOptions]
CompilerOptions = {outdir, OutDir} | {i, IncludeDir} | {d, Def} | OtherOptions
OutDir = atom() | string()
IncludeDir = atom() | string()
Result = {ok, CompiledModules} | {error, Reason}
CompiledModules = CompiledModule | [CompiledModule]
CompiledModule = atom() | string()
Compiles a module for coverage and call statistics analysis. Currently
compile
does not search for modules - if Module
not resides
in the durrent working directory, the complete path has to be specified.
The file extension .erl
may be omitted.
compile/2
makes it possible to pass several options to the compiler.
Some of these options are the tuples {i, IncludeDir}
,
{outdir, OutDir}
, and {d, Def}
; for a complete list, please see
the manual page(s) for compile:file/2
.
The return value Result
is one of the following:
{ok, CompiledModules}
CompiledModule
now
is prepared for coverage and call statistics analysis.
{error,Reason}
Reason
.
The function creates the subdirectories COAST
and COAST/tmp_code
in either the current directory or the directory specified using the
{outdir, OutDir}
option. In COAST/tmp_code
two files,
<File>.COAST.pretty.erl
and <File>.COAST.erl
, will be placed.
<File>.COAST.pretty.erl
is a transformed version (among other things,
containing no comments) of the original file, <File>.erl
.
<File>.COAST.erl
contains the code in <File>.COAST.pretty.erl
, modified
with the counter code necessary to gather coverage and call statistics data during
execution.
In either the current working directory or in the directory specified using the
{outdir, OutDir}
option, the file <File>.beam
will be placed. This file is
the the compiled version of <File>.COAST.erl
.
Note: <File>.COAST.pretty.erl
and <File>.COAST.erl
shall never ever
be renamed or moved, or the coverage and call statistics analysis will fail!
Example:
1> coast:compile(test). {ok,test} 2> coast:compile("../can", [{outdir, "../ebin"}]). {ok,"../can"}
compile_all() -> Result
compile_all(Dir) -> Result
compile_all(Dir, Options) -> Result
Dir = atom() | string()
Options = [CompilerOptions]
CompilerOptions = {outdir, OutDir} | {i, IncludeDir} | {d, Def} | OtherOptions
OutDir = atom() | string()
IncludeDir = atom() | string()
Result = {ok, CompiledModules} | {error, Reason}
CompiledModules = [CompiledModule]
CompiledModule = atom() | string()
compile_all/0
evaluates compile/1
for all .erl
files
found in the current working directory Dir
.
compile_all/1
evaluates compile/1
for all .erl
files
found in the directory Dir
.
compile_all/2
evaluates compile/2
for all .erl
files
found in the directory Dir
.
Example:
3> coast:compile_all(). {ok,["can","test"]}
run(Module, Function, ArgumentList) -> Result
Module = atom()
Function = atom()
ArgumentList = [Args]
Result = term()
run/3
applies (the presumably coast-compiled) Function
in
Module
on ArgumentList
. The function in question must have
been exported from Module
. The length of the ArgumentList
is the arity of the function.
A function in a coast-compiled module cannot be started from the shell directly,
it has to be started either using the function run/3
or from another process
than the shell.
Example:
4> coast:run(can,start,[10]). <0.91.0>
Modules = ModuleName | [ModuleName]
ModuleName = atom()
Result = {module_calls, ModuleResults} | {error, Reason}
ModuleResults = [ModuleResult]
ModuleResult = {ModuleName, TotalCalls, ExternalCalls, InternalNonRecursiveCalls}
TotalCalls = ExternalCalls = InternalNonRecursiveCalls = integer()
Reason = {not_coast_compiled, ModuleName}
mod_calls/1
lists the number of times Modules
have been called.
The listing is presented module by module, with the following data:
TotalCalls
ExternalCalls
InternalNonRecursiveCalls
InternalNonRecursiveCalls
always equals to 0 (zero). (The reason
for nevertheless presenting it is to produce results having the same format from the
mod_calls/1
, func_calls/1
and clause_calls/1
functions.)
Example:
5> coast:mod_calls(can). {module_calls,[{can,37,3,0}]} 6> coast:mod_calls([can,test]). {module_calls,[{can,37,3,0},{test,0,0,0}]}
Modules = ModuleName | [ModuleName]
ModuleName = atom()
Result = {function_calls, FunctionResults} | {error, Reason}
FunctionResults = [FunctionResult]
FunctionResult = {Function, TotalCalls, ExternalCalls, InternalNonRecursiveCalls}
Function = {ModuleName, FunctionName, Arity}
FunctionName = atom()
Arity = integer()
TotalCalls = ExternalCalls = InternalNonRecursiveCalls = integer()
Reason = {not_coast_compiled, ModuleName}
func_calls/1
lists the number of times the functions in Modules
have
been called. The listing is presented in order, module by module and function by function,
with the following data:
TotalCalls
ExternalCalls
InternalNonRecursiveCalls
Example:
7> coast:func_calls(can). {function_calls,[{{can,create_rects,2},1,0,1}, {{can,create_rects,3},11,0,1}, {{can,event_loop,2},20,0,1}, {{can,f,1},1,0,1}, {{can,mk_canvas,1},1,1,0}, {{can,prov,1},2,1,1}, {{can,prov2,1},0,0,0}, {{can,start,1},1,1,0}]}
clause_calls(Modules) -> Result
Modules = ModuleName | [ModuleName]
ModuleName = atom()
Result = {clause_calls, ClauseResults} | {error, Reason}
ClauseResults = [ClauseResult]
ClauseResult = {Clause, TotalCalls, ExternalCalls, InternalNonRecursiveCalls}
Clause = {ModuleName, FunctionName, Arity, ClauseNumber}
FunctionName = atom()
Arity = ClauseNumber = integer()
TotalCalls = ExternalCalls = InternalNonRecursiveCalls = integer()
Reason = {not_coast_compiled, ModuleName}
clause_calls/1
lists the number of times the function clauses in
Modules
have been called. The listing is presented in order, module
by module, function by function and clause by clause. To distinguish between
clauses in a function, they are numbered sequentially, the first clause
encountered getting number 1 (one). For each clause the following data is presented:
TotalCalls
ExternalCalls
InternalNonRecursiveCalls
in the same
function
also is a recursive call!)
Example:
8> coast:clause_calls(can). {clause_calls,[{{can,create_rects,2,1},1,0,1}, {{can,create_rects,3,1},10,0,1}, {{can,create_rects,3,2},1,0,0}, {{can,event_loop,2,1},20,0,1}, {{can,f,1,1},1,0,1}, {{can,mk_canvas,1,1},1,1,0}, {{can,prov,1,1},1,0,1}, {{can,prov,1,2},1,1,0}, {{can,prov2,1,1},0,0,0}, {{can,prov2,1,2},0,0,0}, {{can,start,1,1},1,1,0}]}
mod_coverage(Modules) -> Result
Modules = ModuleName | [ModuleName]
ModuleName = atom()
Result = {module_coverage, ModuleResults} | {error, Reason}
ModuleResults = [ModuleResult]
ModuleResult = {ModuleName, Covered, Uncovered}
Covered = Uncovered = integer()
Reason = {not_coast_compiled, ModuleName}
mod_coverage/1
lists the number of covered and uncovered lines of code
in Modules
.
The listing is presented module by module.
Example:
9> coast:mod_coverage(can). {module_coverage,[{can,22,4}]}
func_coverage(Modules) -> Result
Modules = ModuleName | [ModuleName]
ModuleName = atom()
Result = {function_coverage, FunctionResults} | {error, Reason}
FunctionResults = [FunctionResult]
FunctionResult = {Function, Covered, Uncovered}
Function = {ModuleName, FunctionName, Arity}
FunctionName = atom()
Arity = integer()
Covered = Uncovered = integer()
Reason = {not_coast_compiled, ModuleName}
func_coverage/1
lists, for each function in Modules
, the number of
covered and uncovered lines of code.
Example:
10> coast:func_coverage(can). {function_coverage,[{{can,create_rects,2},1,0}, {{can,create_rects,3},5,0}, {{can,event_loop,2},5,2}, {{can,f,1},1,0}, {{can,mk_canvas,1},6,0}, {{can,prov,1},3,0}, {{can,prov2,1},0,2}, {{can,start,1},1,0}]}
clause_coverage(Modules) -> Result
Modules = ModuleName | [ModuleName]
ModuleName = atom()
Result = {clause_coverage, ClauseResults} | {error, Reason}
ClauseResults = [ClauseResult]
ClauseResult = {Clause, Covered, Uncovered}
Clause = {ModuleName, FunctionName, Arity, ClauseNumber}
FunctionName = atom()
Arity = integer()
ClauseNumber = integer()
Covered = Uncovered = integer()
Reason = {not_coast_compiled, ModuleName}
clause_coverage/1
lists, for each function clause in Modules
,
the number of covered and uncovered lines of code. To distinguish between
clauses in a function, they are numbered sequentially, the first clause
encountered getting number 1 (one).
Example:
11> coast:clause_coverage(can). {clause_coverage,[{{can,create_rects,2,1},1,0}, {{can,create_rects,3,1},4,0}, {{can,create_rects,3,2},1,0}, {{can,event_loop,2,1},5,2}, {{can,f,1,1},1,0}, {{can,mk_canvas,1,1},6,0}, {{can,prov,1,1},1,0}, {{can,prov,1,2},2,0}, {{can,prov2,1,1},0,1}, {{can,prov2,1,2},0,1}, {{can,start,1,1},1,0}]}
analyse_to_file(Modules) -> Result
Modules = ModuleName | [ModuleName]
ModuleName = atom()
Result = {ok, Files} | {error, Reason}
Files = [FileName]
FileName = string()
Reason = {not_coast_compiled, ModuleName} | OtherReason
analyse_to_file/1
performs a detailed coverage analysis, showing the
number of times each line in Modules
has been called so far. The result is
stored in FileNames
(one file for each module).
Example:
12> coast:analyse_to_file([can,test]). {ok,["/clearcase/otp/tools/devtools/tools/ebin/COAST/can.COAST.out", "/clearcase/otp/tools/devtools/tools/ebin/COAST/test.COAST.out"]}
Result = [ModuleName]
ModuleName = atom()
known_modules/0
lists the modules that the coast program is aware of,
i.e., the coast-compiled modules that so far, during this session working with
coast, have been coast-compiled or subject to execution.
The absence
of a module in the list probably means it has never been
coast-compiled. The presence
of an unexpected module in the list probably
means that an old coast-compiled module has been executed.
Example:
13> coast:known_modules(). [can,test]
source_files(Modules) -> Result
Modules = ModuleName | [ModuleName]
ModuleName = atom()
Result = [ModuleResult]
ModuleResult = FileName | {error, Reason}
FileName = string()
Reason = {no_such_module, ModuleName} | {not_coast_compiled, ModuleName} | OtherReason
source_files/1
lists the source files that the coast-compiled modules
specified in Modules
originates from.
Result
is a list containing (for each module in Modules
) either the corresponding
source file found, or an error.
This function is useful if one wants to make sure that the correct module actually is the one being subject to coverage and call statistics analysis.
Example:
14> coast:source_files(coast:known_modules()). ["/clearcase/otp/tools/devtools/tools/ebin/can.erl", "/clearcase/otp/tools/devtools/tools/ebin/test.erl"] 15> c(test). {ok,test} 16> coast:source_files([can,xxx,test]). ["/clearcase/otp/tools/devtools/tools/ebin/can.erl", {error,{no_such_module,xxx}}, {error,{not_coast_compiled,test}}]
Modules = ModuleName | [ModuleName]
ModuleName = atom()
clear/1
discards all coverage and call statistics data, concerning one
or more modules, that has been stored (in the internal database) up to the present.
(Trying to analyse any of the modules cleared will then yield the same result as when
they still not have been subject to any execution.)
clear_all/0
discards all coverage and call statistics data that has been
stored (in the internal database) up to the present.
quit/0
stops the server controlling the collected coverage and call
statistics data.
This module has replaced the cover
module, which is now obsolete.