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} | OtherOptionsOutDir = 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} | OtherOptionsOutDir = 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:
TotalCallsExternalCallsInternalNonRecursiveCallsInternalNonRecursiveCalls 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:
TotalCallsExternalCallsInternalNonRecursiveCallsExample:
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:
TotalCallsExternalCallsInternalNonRecursiveCallsin 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} | OtherReasonanalyse_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} | OtherReasonsource_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.