diff options
Diffstat (limited to 'lib/tools')
-rw-r--r-- | lib/tools/doc/src/cover.xml | 98 | ||||
-rw-r--r-- | lib/tools/doc/src/cprof.xml | 42 | ||||
-rw-r--r-- | lib/tools/doc/src/eprof.xml | 38 | ||||
-rw-r--r-- | lib/tools/doc/src/fprof.xml | 50 | ||||
-rw-r--r-- | lib/tools/doc/src/instrument.xml | 10 | ||||
-rw-r--r-- | lib/tools/doc/src/lcnt.xml | 68 | ||||
-rw-r--r-- | lib/tools/doc/src/make.xml | 10 | ||||
-rw-r--r-- | lib/tools/doc/src/tags.xml | 16 | ||||
-rw-r--r-- | lib/tools/doc/src/xref.xml | 64 | ||||
-rw-r--r-- | lib/tools/priv/styles.css | 5 | ||||
-rw-r--r-- | lib/tools/src/cover.erl | 494 | ||||
-rw-r--r-- | lib/tools/src/tools.app.src | 2 | ||||
-rw-r--r-- | lib/tools/test/cover_SUITE.erl | 40 |
13 files changed, 555 insertions, 382 deletions
diff --git a/lib/tools/doc/src/cover.xml b/lib/tools/doc/src/cover.xml index 6c6b20aad8..e9f782977d 100644 --- a/lib/tools/doc/src/cover.xml +++ b/lib/tools/doc/src/cover.xml @@ -30,7 +30,7 @@ <date></date> <rev></rev> </header> - <module>cover</module> + <module since="">cover</module> <modulesummary>A Coverage Analysis Tool for Erlang</modulesummary> <description> <p>The module <c>cover</c> provides a set of functions for coverage @@ -115,7 +115,7 @@ </description> <funcs> <func> - <name>start() -> {ok,Pid} | {error,Reason}</name> + <name since="">start() -> {ok,Pid} | {error,Reason}</name> <fsummary>Start Cover.</fsummary> <type> <v>Pid = pid()</v> @@ -128,21 +128,33 @@ </desc> </func> <func> - <name>start(Nodes) -> {ok,StartedNodes} | {error,not_main_node}</name> + <name since="OTP 22.0">local_only() -> ok | {error,too_late}</name> + <fsummary>Only support running Cover on the local node.</fsummary> + <desc> + <p>Only support running Cover on the local node. This function + must be called before any modules have been compiled or any + nodes added. When running in this mode, modules will be Cover + compiled in a more efficient way, but the resulting code will + only work on the same node they were compiled on.</p> + </desc> + </func> + <func> + <name since="">start(Nodes) -> {ok,StartedNodes} | {error,not_main_node} | {error,local_only}</name> <fsummary>Start Cover on remote nodes.</fsummary> <type> <v>Nodes = StartedNodes = [atom()]</v> </type> <desc> <p>Starts a Cover server on the each of given nodes, and loads - all cover compiled modules.</p> + all cover compiled modules. This call will fail if + <c>cover:local_only/0</c> has been called.</p> </desc> </func> <func> - <name>compile(ModFiles) -> Result | [Result]</name> - <name>compile(ModFiles, Options) -> Result | [Result]</name> - <name>compile_module(ModFiles) -> Result | [Result]</name> - <name>compile_module(ModFiles, Options) -> Result | [Result]</name> + <name since="">compile(ModFiles) -> Result | [Result]</name> + <name since="">compile(ModFiles, Options) -> Result | [Result]</name> + <name since="">compile_module(ModFiles) -> Result | [Result]</name> + <name since="">compile_module(ModFiles, Options) -> Result | [Result]</name> <fsummary>Compile one or more modules for Cover analysis.</fsummary> <type> <v>ModFiles = ModFile | [ModFile]</v> @@ -176,9 +188,9 @@ </desc> </func> <func> - <name>compile_directory() -> [Result] | {error,Reason}</name> - <name>compile_directory(Dir) -> [Result] | {error,Reason}</name> - <name>compile_directory(Dir, Options) -> [Result] | {error,Reason}</name> + <name since="">compile_directory() -> [Result] | {error,Reason}</name> + <name since="">compile_directory(Dir) -> [Result] | {error,Reason}</name> + <name since="">compile_directory(Dir, Options) -> [Result] | {error,Reason}</name> <fsummary>Compile all modules in a directory for Cover analysis.</fsummary> <type> <v>Dir = string()</v> @@ -199,7 +211,7 @@ </desc> </func> <func> - <name>compile_beam(ModFiles) -> Result | [Result]</name> + <name since="">compile_beam(ModFiles) -> Result | [Result]</name> <fsummary>Compile one or more modules for Cover analysis, using existing beam(s).</fsummary> <type> <v>ModFiles = ModFile | [ModFile]</v> @@ -241,8 +253,8 @@ </desc> </func> <func> - <name>compile_beam_directory() -> [Result] | {error,Reason}</name> - <name>compile_beam_directory(Dir) -> [Result] | {error,Reason}</name> + <name since="">compile_beam_directory() -> [Result] | {error,Reason}</name> + <name since="">compile_beam_directory(Dir) -> [Result] | {error,Reason}</name> <fsummary>Compile all .beam files in a directory for Cover analysis.</fsummary> <type> <v>Dir = string()</v> @@ -260,14 +272,14 @@ </desc> </func> <func> - <name>analyse() -> {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Modules) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Analysis) -> {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Level) -> {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Modules, Analysis) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Modules, Level) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Analysis, Level) -> {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Modules, Analysis, Level) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> + <name since="OTP 18.0">analyse() -> {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Modules) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Analysis) -> {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Level) -> {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Modules, Analysis) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Modules, Level) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Analysis, Level) -> {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Modules, Analysis, Level) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> <fsummary>Analyse one or more Cover compiled modules.</fsummary> <type> <v>Modules = Module | [Module]</v> @@ -305,10 +317,10 @@ </desc> </func> <func> - <name>analyse_to_file() -> {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse_to_file(Modules) -> Answer | {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse_to_file(Options) -> {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse_to_file(Modules,Options) -> Answer | {result,Ok,Fail} | {error,not_main_node}</name> + <name since="OTP 18.0">analyse_to_file() -> {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse_to_file(Modules) -> Answer | {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse_to_file(Options) -> {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse_to_file(Modules,Options) -> Answer | {result,Ok,Fail} | {error,not_main_node}</name> <fsummary>Detailed coverage analysis of one or more Cover compiled modules.</fsummary> <type> <v>Modules = Module | [Module]</v> @@ -359,10 +371,10 @@ </desc> </func> <func> - <name>async_analyse_to_file(Module) -> </name> - <name>async_analyse_to_file(Module,Options) -> </name> - <name>async_analyse_to_file(Module, OutFile) -> </name> - <name>async_analyse_to_file(Module, OutFile, Options) -> pid()</name> + <name since="OTP R14B02">async_analyse_to_file(Module) -> </name> + <name since="OTP R14B02">async_analyse_to_file(Module,Options) -> </name> + <name since="OTP R14B02">async_analyse_to_file(Module, OutFile) -> </name> + <name since="OTP R14B02">async_analyse_to_file(Module, OutFile, Options) -> pid()</name> <fsummary>Asynchronous call to analyse_to_file.</fsummary> <type> <v>Module = atom()</v> @@ -384,7 +396,7 @@ </desc> </func> <func> - <name>modules() -> [Module] | {error,not_main_node}</name> + <name since="">modules() -> [Module] | {error,not_main_node}</name> <fsummary>Return all Cover compiled modules.</fsummary> <type> <v>Module = atom()</v> @@ -395,7 +407,7 @@ </desc> </func> <func> - <name>imported_modules() -> [Module] | {error,not_main_node}</name> + <name since="">imported_modules() -> [Module] | {error,not_main_node}</name> <fsummary>Return all modules for which there are imported data.</fsummary> <type> <v>Module = atom()</v> @@ -406,7 +418,7 @@ </desc> </func> <func> - <name>imported() -> [File] | {error,not_main_node}</name> + <name since="">imported() -> [File] | {error,not_main_node}</name> <fsummary>Return all imported files.</fsummary> <type> <v>File = string()</v> @@ -416,7 +428,7 @@ </desc> </func> <func> - <name>which_nodes() -> [Node] | {error,not_main_node}</name> + <name since="">which_nodes() -> [Node] | {error,not_main_node}</name> <fsummary>Return all nodes that are part of the coverage analysis.</fsummary> <type> <v>Node = atom()</v> @@ -428,7 +440,7 @@ </desc> </func> <func> - <name>is_compiled(Module) -> {file,File} | false | {error,not_main_node}</name> + <name since="">is_compiled(Module) -> {file,File} | false | {error,not_main_node}</name> <fsummary>Check if a module is Cover compiled.</fsummary> <type> <v>Module = atom()</v> @@ -442,8 +454,8 @@ </desc> </func> <func> - <name>reset(Module) -></name> - <name>reset() -> ok | {error,not_main_node}</name> + <name since="">reset(Module) -></name> + <name since="">reset() -> ok | {error,not_main_node}</name> <fsummary>Reset coverage data for Cover compiled modules.</fsummary> <type> <v>Module = atom()</v> @@ -458,8 +470,8 @@ </desc> </func> <func> - <name>export(ExportFile)</name> - <name>export(ExportFile,Module) -> ok | {error,Reason}</name> + <name since="">export(ExportFile)</name> + <name since="">export(ExportFile,Module) -> ok | {error,Reason}</name> <fsummary>Reset coverage data for Cover compiled modules.</fsummary> <type> <v>ExportFile = string()</v> @@ -480,7 +492,7 @@ </desc> </func> <func> - <name>import(ExportFile) -> ok | {error,Reason}</name> + <name since="">import(ExportFile) -> ok | {error,Reason}</name> <fsummary>Reset coverage data for Cover compiled modules.</fsummary> <type> <v>ExportFile = string()</v> @@ -504,14 +516,14 @@ </desc> </func> <func> - <name>stop() -> ok | {error,not_main_node}</name> + <name since="">stop() -> ok | {error,not_main_node}</name> <fsummary>Stop Cover.</fsummary> <desc> <p>Stops the Cover server and unloads all Cover compiled code.</p> </desc> </func> <func> - <name>stop(Nodes) -> ok | {error,not_main_node}</name> + <name since="">stop(Nodes) -> ok | {error,not_main_node}</name> <fsummary>Stop Cover on remote nodes.</fsummary> <type> <v>Nodes = [atom()]</v> @@ -523,7 +535,7 @@ </desc> </func> <func> - <name>flush(Nodes) -> ok | {error,not_main_node}</name> + <name since="OTP R16B">flush(Nodes) -> ok | {error,not_main_node}</name> <fsummary>Collect cover data from remote nodes.</fsummary> <type> <v>Nodes = [atom()]</v> diff --git a/lib/tools/doc/src/cprof.xml b/lib/tools/doc/src/cprof.xml index df0acbe617..b6af8b6d28 100644 --- a/lib/tools/doc/src/cprof.xml +++ b/lib/tools/doc/src/cprof.xml @@ -34,7 +34,7 @@ <rev>PA1</rev> <file>cprof.sgml</file> </header> - <module>cprof</module> + <module since="">cprof</module> <modulesummary>A simple Call Count Profiling Tool using breakpoints for minimal runtime performance impact.</modulesummary> <description> <p>The <c>cprof</c> module is used to profile a program @@ -65,10 +65,10 @@ </description> <funcs> <func> - <name>analyse() -> {AllCallCount, ModAnalysisList}</name> - <name>analyse(Limit) -> {AllCallCount, ModAnalysisList}</name> - <name>analyse(Mod) -> ModAnalysis</name> - <name>analyse(Mod, Limit) -> ModAnalysis</name> + <name since="">analyse() -> {AllCallCount, ModAnalysisList}</name> + <name since="">analyse(Limit) -> {AllCallCount, ModAnalysisList}</name> + <name since="">analyse(Mod) -> ModAnalysis</name> + <name since="">analyse(Mod, Limit) -> ModAnalysis</name> <fsummary>Collect and analyse call counters.</fsummary> <type> <v>Limit = integer()</v> @@ -122,7 +122,7 @@ </desc> </func> <func> - <name>pause() -> integer()</name> + <name since="">pause() -> integer()</name> <fsummary>Pause running call count trace for all functions.</fsummary> <desc> <p>Pause call count tracing for all functions in all modules @@ -137,9 +137,9 @@ </desc> </func> <func> - <name>pause(FuncSpec) -> integer()</name> - <name>pause(Mod, Func) -> integer()</name> - <name>pause(Mod, Func, Arity) -> integer()</name> + <name since="">pause(FuncSpec) -> integer()</name> + <name since="">pause(Mod, Func) -> integer()</name> + <name since="">pause(Mod, Func, Arity) -> integer()</name> <fsummary>Pause running call count trace for matching functions.</fsummary> <type> <v>FuncSpec = Mod | {Mod,Func,Arity}, {FS}</v> @@ -167,10 +167,10 @@ </desc> </func> <func> - <name>restart() -> integer()</name> - <name>restart(FuncSpec) -> integer()</name> - <name>restart(Mod, Func) -> integer()</name> - <name>restart(Mod, Func, Arity) -> integer()</name> + <name since="">restart() -> integer()</name> + <name since="">restart(FuncSpec) -> integer()</name> + <name since="">restart(Mod, Func) -> integer()</name> + <name since="">restart(Mod, Func, Arity) -> integer()</name> <fsummary>Restart existing call counters for matching functions.</fsummary> <type> <v>FuncSpec = Mod | {Mod,Func,Arity}, {FS}</v> @@ -197,7 +197,7 @@ </desc> </func> <func> - <name>start() -> integer()</name> + <name since="">start() -> integer()</name> <fsummary>Start call count tracing for all functions.</fsummary> <desc> <p>Start call count tracing for all functions in all modules, @@ -212,9 +212,9 @@ </desc> </func> <func> - <name>start(FuncSpec) -> integer()</name> - <name>start(Mod, Func) -> integer()</name> - <name>start(Mod, Func, Arity) -> integer()</name> + <name since="">start(FuncSpec) -> integer()</name> + <name since="">start(Mod, Func) -> integer()</name> + <name since="">start(Mod, Func, Arity) -> integer()</name> <fsummary>Start call count tracing for matching functions.</fsummary> <type> <v>FuncSpec = Mod | {Mod,Func,Arity}, {FS}</v> @@ -240,7 +240,7 @@ </desc> </func> <func> - <name>stop() -> integer()</name> + <name since="">stop() -> integer()</name> <fsummary>Stop call count tracing for all functions.</fsummary> <desc> <p>Stop call count tracing for all functions in all modules, @@ -255,9 +255,9 @@ </desc> </func> <func> - <name>stop(FuncSpec) -> integer()</name> - <name>stop(Mod, Func) -> integer()</name> - <name>stop(Mod, Func, Arity) -> integer()</name> + <name since="">stop(FuncSpec) -> integer()</name> + <name since="">stop(Mod, Func) -> integer()</name> + <name since="">stop(Mod, Func, Arity) -> integer()</name> <fsummary>Stop call count tracing for matching functions.</fsummary> <type> <v>FuncSpec = Mod | {Mod,Func,Arity}, {FS}</v> diff --git a/lib/tools/doc/src/eprof.xml b/lib/tools/doc/src/eprof.xml index f098b7d39e..c9e4edd991 100644 --- a/lib/tools/doc/src/eprof.xml +++ b/lib/tools/doc/src/eprof.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>eprof</module> + <module since="">eprof</module> <modulesummary>A Time Profiling Tool for Erlang</modulesummary> <description> <p>The module <c>eprof</c> provides a set of functions for time @@ -40,7 +40,7 @@ </description> <funcs> <func> - <name>start() -> {ok,Pid} | {error,Reason}</name> + <name since="">start() -> {ok,Pid} | {error,Reason}</name> <fsummary>Start Eprof.</fsummary> <type> <v>Pid = pid()</v> @@ -51,9 +51,9 @@ </desc> </func> <func> - <name>start_profiling(Rootset) -> profiling | {error, Reason}</name> - <name>start_profiling(Rootset,Pattern) -> profiling | {error, Reason}</name> - <name>start_profiling(Rootset,Pattern,Options) -> profiling | {error, Reason}</name> + <name since="">start_profiling(Rootset) -> profiling | {error, Reason}</name> + <name since="OTP R14B">start_profiling(Rootset,Pattern) -> profiling | {error, Reason}</name> + <name since="OTP R16B01">start_profiling(Rootset,Pattern,Options) -> profiling | {error, Reason}</name> <fsummary>Start profiling.</fsummary> <type> <v>Rootset = [atom() | pid()]</v> @@ -79,7 +79,7 @@ </desc> </func> <func> - <name>stop_profiling() -> profiling_stopped | profiling_already_stopped</name> + <name since="">stop_profiling() -> profiling_stopped | profiling_already_stopped</name> <fsummary>Stop profiling.</fsummary> <desc> <p>Stops profiling started with <c>start_profiling/1</c> or @@ -87,14 +87,14 @@ </desc> </func> <func> - <name>profile(Fun) -> profiling | {error, Reason}</name> - <name>profile(Fun, Options) -> profiling | {error, Reason}</name> - <name>profile(Rootset) -> profiling | {error, Reason}</name> - <name>profile(Rootset,Fun) -> {ok, Value} | {error,Reason}</name> - <name>profile(Rootset,Fun,Pattern) -> {ok, Value} | {error, Reason}</name> - <name>profile(Rootset,Module,Function,Args) -> {ok, Value} | {error, Reason}</name> - <name>profile(Rootset,Module,Function,Args,Pattern) -> {ok, Value} | {error, Reason}</name> - <name>profile(Rootset,Module,Function,Args,Pattern,Options) -> {ok, Value} | {error, Reason}</name> + <name since="">profile(Fun) -> profiling | {error, Reason}</name> + <name since="">profile(Fun, Options) -> profiling | {error, Reason}</name> + <name since="">profile(Rootset) -> profiling | {error, Reason}</name> + <name since="">profile(Rootset,Fun) -> {ok, Value} | {error,Reason}</name> + <name since="OTP R14B">profile(Rootset,Fun,Pattern) -> {ok, Value} | {error, Reason}</name> + <name since="">profile(Rootset,Module,Function,Args) -> {ok, Value} | {error, Reason}</name> + <name since="OTP R14B">profile(Rootset,Module,Function,Args,Pattern) -> {ok, Value} | {error, Reason}</name> + <name since="OTP R16B01">profile(Rootset,Module,Function,Args,Pattern,Options) -> {ok, Value} | {error, Reason}</name> <fsummary>Start profiling.</fsummary> <type> <v>Rootset = [atom() | pid()]</v> @@ -128,9 +128,9 @@ </desc> </func> <func> - <name>analyze() -> ok</name> - <name>analyze(Type) -> ok</name> - <name>analyze(Type,Options) -> ok</name> + <name since="OTP R14B">analyze() -> ok</name> + <name since="OTP R14B">analyze(Type) -> ok</name> + <name since="OTP R14B">analyze(Type,Options) -> ok</name> <fsummary>Display profiling results per process.</fsummary> <type> <v>Type = procs | total</v> @@ -152,7 +152,7 @@ </desc> </func> <func> - <name>log(File) -> ok</name> + <name since="">log(File) -> ok</name> <fsummary>Activate logging of <c>eprof</c>printouts.</fsummary> <type> <v>File = atom() | string()</v> @@ -164,7 +164,7 @@ </desc> </func> <func> - <name>stop() -> stopped</name> + <name since="">stop() -> stopped</name> <fsummary>Stop Eprof.</fsummary> <desc> <p>Stops the Eprof server.</p> diff --git a/lib/tools/doc/src/fprof.xml b/lib/tools/doc/src/fprof.xml index 1fd828d127..4bb8862016 100644 --- a/lib/tools/doc/src/fprof.xml +++ b/lib/tools/doc/src/fprof.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>fprof.sgml</file> </header> - <module>fprof</module> + <module since="">fprof</module> <modulesummary>A Time Profiling Tool using trace to file for minimal runtime performance impact.</modulesummary> <description> <p>This module is used to profile a program @@ -101,7 +101,7 @@ </description> <funcs> <func> - <name>start() -> {ok, Pid} | {error, {already_started, Pid}}</name> + <name since="">start() -> {ok, Pid} | {error, {already_started, Pid}}</name> <fsummary>Starts the <c>fprof</c> server.</fsummary> <type> <v>Pid = pid()</v> @@ -117,14 +117,14 @@ </desc> </func> <func> - <name>stop() -> ok</name> + <name since="">stop() -> ok</name> <fsummary>Same as <c>stop(normal)</c>.</fsummary> <desc> <p>Same as <c>stop(normal)</c>.</p> </desc> </func> <func> - <name>stop(Reason) -> ok</name> + <name since="">stop(Reason) -> ok</name> <fsummary>Stops the <c>fprof</c> server.</fsummary> <type> <v>Reason = term()</v> @@ -149,7 +149,7 @@ </desc> </func> <func> - <name>apply(Func, Args) -> term()</name> + <name since="">apply(Func, Args) -> term()</name> <fsummary>Same as <c>apply(Func, Args, [])</c>.</fsummary> <type> <v>Func = function() | {Module, Function}</v> @@ -162,7 +162,7 @@ </desc> </func> <func> - <name>apply(Module, Function, Args) -> term()</name> + <name since="">apply(Module, Function, Args) -> term()</name> <fsummary>Same as <c>apply({Module, Function}, Args, [])</c>.</fsummary> <type> <v>Args = [term()]</v> @@ -174,7 +174,7 @@ </desc> </func> <func> - <name>apply(Func, Args, OptionList) -> term()</name> + <name since="">apply(Func, Args, OptionList) -> term()</name> <fsummary>Calls <c>erlang:apply(Func, Args)</c>surrounded by<c>trace([start | OptionList])</c>and<c>trace(stop)</c>.</fsummary> <type> <v>Func = function() | {Module, Function}</v> @@ -210,7 +210,7 @@ </desc> </func> <func> - <name>apply(Module, Function, Args, OptionList) -> term()</name> + <name since="">apply(Module, Function, Args, OptionList) -> term()</name> <fsummary>Same as <c>apply({Module, Function}, Args, OptionList)</c>.</fsummary> <type> <v>Module = atom()</v> @@ -228,7 +228,7 @@ </desc> </func> <func> - <name>trace(start, Filename) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace(start, Filename) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>trace([start, {file, Filename}])</c>.</fsummary> <type> <v>Reason = term()</v> @@ -238,7 +238,7 @@ </desc> </func> <func> - <name>trace(verbose, Filename) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace(verbose, Filename) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>trace([start, verbose, {file, Filename}])</c>.</fsummary> <type> <v>Reason = term()</v> @@ -249,7 +249,7 @@ </desc> </func> <func> - <name>trace(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>trace([{OptionName, OptionValue}])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -262,7 +262,7 @@ </desc> </func> <func> - <name>trace(verbose) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace(verbose) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>trace([start, verbose])</c>.</fsummary> <type> <v>Reason = term()</v> @@ -272,7 +272,7 @@ </desc> </func> <func> - <name>trace(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>trace([OptionName])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -283,7 +283,7 @@ </desc> </func> <func> - <name>trace({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>trace([{OptionName, OptionValue}])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -296,7 +296,7 @@ </desc> </func> <func> - <name>trace([Option]) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace([Option]) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Starts or stops tracing.</fsummary> <type> <v>Option = start | stop | {procs, PidSpec} | {procs, [PidSpec]} | verbose | {verbose, bool()} | file | {file, Filename} | {tracer, Tracer}</v> @@ -360,7 +360,7 @@ </desc> </func> <func> - <name>profile() -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">profile() -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>profile([])</c>.</fsummary> <type> <v>Reason = term()</v> @@ -370,7 +370,7 @@ </desc> </func> <func> - <name>profile(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">profile(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>profile([{OptionName, OptionValue}])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -383,7 +383,7 @@ </desc> </func> <func> - <name>profile(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">profile(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>profile([OptionName])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -394,7 +394,7 @@ </desc> </func> <func> - <name>profile({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">profile({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>profile([{OptionName, OptionValue}])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -407,7 +407,7 @@ </desc> </func> <func> - <name>profile([Option]) -> ok | {ok, Tracer} | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">profile([Option]) -> ok | {ok, Tracer} | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Compiles a trace into raw profile data held by the <c>fprof</c> server.</fsummary> <type> <v>Option = file | {file, Filename} | dump | {dump, Dump} | append | start | stop</v> @@ -465,7 +465,7 @@ </desc> </func> <func> - <name>analyse() -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">analyse() -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>analyse([])</c>.</fsummary> <type> <v>Reason = term()</v> @@ -475,7 +475,7 @@ </desc> </func> <func> - <name>analyse(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">analyse(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>analyse([{OptionName, OptionValue}])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -488,7 +488,7 @@ </desc> </func> <func> - <name>analyse(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">analyse(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>analyse([OptionName])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -499,7 +499,7 @@ </desc> </func> <func> - <name>analyse({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">analyse({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>analyse([{OptionName, OptionValue}])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -512,7 +512,7 @@ </desc> </func> <func> - <name>analyse([Option]) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">analyse([Option]) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Analyses raw profile data in the <c>fprof</c> server.</fsummary> <type> <v>Option = dest | {dest, Dest} | append | {cols, Cols} | callers | {callers, bool()} | no_callers | {sort, SortSpec} | totals | {totals, bool()} | details | {details, bool()} | no_details</v> diff --git a/lib/tools/doc/src/instrument.xml b/lib/tools/doc/src/instrument.xml index 79bacb2927..7e9cbaebb0 100644 --- a/lib/tools/doc/src/instrument.xml +++ b/lib/tools/doc/src/instrument.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>instrument.sgml</file> </header> - <module>instrument</module> + <module since="">instrument</module> <modulesummary>Analysis and Utility Functions for Instrumentation</modulesummary> <description> <p>The module <c>instrument</c> contains support for studying the resource @@ -92,7 +92,7 @@ <funcs> <func> - <name name="allocations" arity="0"/> + <name name="allocations" arity="0" since="OTP 21.0"/> <fsummary>Return a summary of all allocations in the system.</fsummary> <desc> <p>Shorthand for @@ -101,7 +101,7 @@ </func> <func> - <name name="allocations" arity="1"/> + <name name="allocations" arity="1" since="OTP 21.0"/> <fsummary>Return a summary of all allocations filtered by allocator type and scheduler id.</fsummary> <desc> @@ -173,7 +173,7 @@ </func> <func> - <name name="carriers" arity="0"/> + <name name="carriers" arity="0" since="OTP 21.0"/> <fsummary>Return a list of all carriers in the system.</fsummary> <desc> <p>Shorthand for @@ -182,7 +182,7 @@ </func> <func> - <name name="carriers" arity="1"/> + <name name="carriers" arity="1" since="OTP 21.0"/> <fsummary>Return a list of all carriers filtered by allocator type and scheduler id.</fsummary> <desc> diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml index d2595cdb60..1d434decfc 100644 --- a/lib/tools/doc/src/lcnt.xml +++ b/lib/tools/doc/src/lcnt.xml @@ -34,7 +34,7 @@ <rev>PA1</rev> <file>lcnt.xml</file> </header> - <module>lcnt</module> + <module since="OTP R13B04">lcnt</module> <modulesummary>A runtime system Lock Profiling tool.</modulesummary> <description> <p>The <c>lcnt</c> module is used to profile the internal ethread locks in the @@ -71,7 +71,7 @@ <funcs> <func> - <name>start() -> {ok, Pid} | {error, {already_started, Pid}} </name> + <name since="OTP R13B04">start() -> {ok, Pid} | {error, {already_started, Pid}} </name> <fsummary>Starts the lock profiler server.</fsummary> <type> <v>Pid = pid()</v> @@ -84,7 +84,7 @@ </func> <func> - <name>stop() -> ok</name> + <name since="OTP R13B04">stop() -> ok</name> <fsummary>Stops the lock profiler server.</fsummary> <desc> <p>Stops the lock profiler server.</p> @@ -92,13 +92,13 @@ </func> <func> - <name>collect() -> ok</name> + <name since="OTP R13B04">collect() -> ok</name> <fsummary>Same as <c>collect(node())</c>.</fsummary> <desc><p>Same as <c>collect(node())</c>.</p></desc> </func> <func> - <name>collect(Node) -> ok</name> + <name since="OTP R13B04">collect(Node) -> ok</name> <fsummary>Collects lock statistics from the runtime system.</fsummary> <type> <v>Node = node()</v> @@ -113,13 +113,13 @@ </func> <func> - <name>clear() -> ok</name> + <name since="OTP R13B04">clear() -> ok</name> <fsummary>Same as <c>clear(node())</c>.</fsummary> <desc><p>Same as <c>clear(node())</c>.</p></desc> </func> <func> - <name>clear(Node) -> ok</name> + <name since="OTP R13B04">clear(Node) -> ok</name> <fsummary>Clears the internal lock statistics from runtime system.</fsummary> <type> <v>Node = node()</v> @@ -133,12 +133,12 @@ </desc> </func> <func> - <name>conflicts() -> ok</name> + <name since="OTP R13B04">conflicts() -> ok</name> <fsummary>Same as <c>conflicts([])</c>.</fsummary> <desc><p>Same as <c>conflicts([])</c>.</p></desc> </func> <func> - <name>conflicts([Option]) -> ok</name> + <name since="OTP R13B04">conflicts([Option]) -> ok</name> <fsummary>Prints a list of internal lock counters.</fsummary> <type> <v>Option = {sort, Sort} | {reverse, bool()} | {thresholds, [Thresholds]} | {print, [Print | {Print, integer()}]} | {max_locks, MaxLocks} | {combine, bool()}</v> @@ -154,14 +154,14 @@ </func> <func> - <name>locations() -> ok</name> + <name since="OTP R13B04">locations() -> ok</name> <fsummary>Same as <c>locations([])</c>.</fsummary> <desc> <p>Same as <c>locations([])</c>.</p> </desc> </func> <func> - <name>locations([Option]) -> ok</name> + <name since="OTP R13B04">locations([Option]) -> ok</name> <fsummary>Prints a list of internal lock counters by source code locations.</fsummary> <type> <v>Option = {sort, Sort} | {thresholds, [Thresholds]} | {print, [Print | {Print, integer()}]} | {max_locks, MaxLocks} | {combine, bool()}</v> @@ -177,12 +177,12 @@ </func> <func> - <name>inspect(Lock) -> ok</name> + <name since="OTP R13B04">inspect(Lock) -> ok</name> <fsummary>Same as <c>inspect(Lock, [])</c>.</fsummary> <desc><p>Same as <c>inspect(Lock, [])</c>.</p></desc> </func> <func> - <name>inspect(Lock, [Option]) -> ok</name> + <name since="OTP R13B04">inspect(Lock, [Option]) -> ok</name> <fsummary>Prints a list of internal lock counters for a specific lock.</fsummary> <type> <v>Lock = Name | {Name, Id | [Id]}</v> @@ -268,7 +268,7 @@ </func> <func> - <name>information() -> ok</name> + <name since="OTP R13B04">information() -> ok</name> <fsummary>Prints lcnt server state and generic information about collected lock statistics.</fsummary> <desc> <p>Prints lcnt server state and generic information about collected lock statistics.</p> @@ -276,7 +276,7 @@ </func> <func> - <name>swap_pid_keys() -> ok</name> + <name since="OTP R13B04">swap_pid_keys() -> ok</name> <fsummary>Swaps places on <c>Name</c> and <c>Id</c> space for ports and processes.</fsummary> <desc> <p>Swaps places on <c>Name</c> and <c>Id</c> space for ports and processes.</p> @@ -284,7 +284,7 @@ </func> <func> - <name>load(Filename) -> ok</name> + <name since="OTP R13B04">load(Filename) -> ok</name> <fsummary>Restores previously saved data to the server.</fsummary> <type> <v>Filename = filename()</v> @@ -295,7 +295,7 @@ </func> <func> - <name>save(Filename) -> ok</name> + <name since="OTP R13B04">save(Filename) -> ok</name> <fsummary>Saves the collected data to file.</fsummary> <type> <v>Filename = filename()</v> @@ -312,7 +312,7 @@ </section> <funcs> <func> - <name>apply(Fun) -> term()</name> + <name since="OTP R13B04">apply(Fun) -> term()</name> <fsummary>Same as <c>apply(Fun, [])</c>.</fsummary> <type> <v>Fun = fun()</v> @@ -322,7 +322,7 @@ </desc> </func> <func> - <name>apply(Fun, Args) -> term()</name> + <name since="OTP R13B04">apply(Fun, Args) -> term()</name> <fsummary>Same as <c>apply(Module, Function, Args)</c>.</fsummary> <type> <v>Fun = fun()</v> @@ -333,7 +333,7 @@ </desc> </func> <func> - <name>apply(Module, Function, Args) -> term()</name> + <name since="OTP R13B04">apply(Module, Function, Args) -> term()</name> <fsummary>Clears counters, applies function and collects the profiling results.</fsummary> <type> <v>Module = atom()</v> @@ -358,12 +358,12 @@ </func> <func> - <name>pid(Id, Serial) -> pid()</name> + <name since="OTP R13B04">pid(Id, Serial) -> pid()</name> <fsummary>Same as <c>pid(node(), Id, Serial)</c>.</fsummary> <desc><p>Same as <c>pid(node(), Id, Serial)</c>.</p></desc> </func> <func> - <name>pid(Node, Id, Serial) -> pid()</name> + <name since="OTP R13B04">pid(Node, Id, Serial) -> pid()</name> <fsummary>Creates a process id with creation 0.</fsummary> <type> <v>Node = node()</v> @@ -376,12 +376,12 @@ </func> <func> - <name>port(Id) -> port()</name> + <name since="OTP R13B04">port(Id) -> port()</name> <fsummary>Same as <c>port(node(), Id)</c>.</fsummary> <desc><p>Same as <c>port(node(), Id)</c>.</p></desc> </func> <func> - <name>port(Node, Id) -> port()</name> + <name since="OTP R13B04">port(Node, Id) -> port()</name> <fsummary>Creates a port id with creation 0.</fsummary> <type> <v>Node = node()</v> @@ -399,12 +399,12 @@ <funcs> <func> - <name>rt_collect() -> [lock_counter_data()]</name> + <name since="OTP R13B04">rt_collect() -> [lock_counter_data()]</name> <fsummary>Same as <c>rt_collect(node())</c>.</fsummary> <desc> <p>Same as <c>rt_collect(node())</c>.</p> </desc> </func> <func> - <name>rt_collect(Node) -> [lock_counter_data()]</name> + <name since="OTP R13B04">rt_collect(Node) -> [lock_counter_data()]</name> <fsummary>Returns a list of raw lock counter data.</fsummary> <type> <v>Node = node()</v> @@ -413,12 +413,12 @@ </func> <func> - <name>rt_clear() -> ok</name> + <name since="OTP R13B04">rt_clear() -> ok</name> <fsummary>Same as <c>rt_clear(node())</c>.</fsummary> <desc> <p>Same as <c>rt_clear(node())</c>.</p> </desc> </func> <func> - <name>rt_clear(Node) -> ok</name> + <name since="OTP R13B04">rt_clear(Node) -> ok</name> <fsummary>Clears the internal counters.</fsummary> <type> <v>Node = node()</v> @@ -427,13 +427,13 @@ </func> <func> - <name>rt_mask() -> [category_atom()]</name> + <name since="OTP 20.1">rt_mask() -> [category_atom()]</name> <fsummary>Same as <c>rt_mask(node())</c>.</fsummary> <desc><p>Same as <c>rt_mask(node())</c>.</p></desc> </func> <func> - <name>rt_mask(Node) -> [category_atom()]</name> + <name since="OTP 20.1">rt_mask(Node) -> [category_atom()]</name> <fsummary>Returns the current lock category mask.</fsummary> <type> <v>Node = node()</v> @@ -447,7 +447,7 @@ </func> <func> - <name>rt_mask(Categories) -> ok | {error, copy_save_enabled}</name> + <name since="OTP 20.1">rt_mask(Categories) -> ok | {error, copy_save_enabled}</name> <fsummary>Same as <c>rt_mask(node(), Categories)</c>.</fsummary> <type> <v>Categories = [atom()]</v> @@ -456,7 +456,7 @@ </func> <func> - <name>rt_mask(Node, Categories) -> ok | {error, copy_save_enabled}</name> + <name since="OTP 20.1">rt_mask(Node, Categories) -> ok | {error, copy_save_enabled}</name> <fsummary>Changes the lock category mask.</fsummary> <type> <v>Node = node()</v> @@ -489,12 +489,12 @@ </func> <func> - <name>rt_opt({Type, bool()}) -> bool()</name> + <name since="OTP R13B04">rt_opt({Type, bool()}) -> bool()</name> <fsummary>Same as <c>rt_opt(node(), {Type, Opt})</c>.</fsummary> <desc> <p>Same as <c>rt_opt(node(), {Type, Opt})</c>.</p> </desc> </func> <func> - <name>rt_opt(Node, {Type, bool()}) -> bool()</name> + <name since="OTP R13B04">rt_opt(Node, {Type, bool()}) -> bool()</name> <fsummary>Changes the lock counter behavior and returns the previous behaviour.</fsummary> <type> <v>Node = node()</v> diff --git a/lib/tools/doc/src/make.xml b/lib/tools/doc/src/make.xml index 123fcd4afc..af2404707f 100644 --- a/lib/tools/doc/src/make.xml +++ b/lib/tools/doc/src/make.xml @@ -30,7 +30,7 @@ <date></date> <rev></rev> </header> - <module>make</module> + <module since="">make</module> <modulesummary>A Make Utility for Erlang</modulesummary> <description> <p>The module <c>make</c> provides a set of functions similar to @@ -38,8 +38,8 @@ </description> <funcs> <func> - <name>all() -> up_to_date | error</name> - <name>all(Options) -> up_to_date | error</name> + <name since="">all() -> up_to_date | error</name> + <name since="">all(Options) -> up_to_date | error</name> <fsummary>Compile a set of modules.</fsummary> <type> <v>Options = [Option]</v> @@ -87,8 +87,8 @@ </desc> </func> <func> - <name>files(ModFiles) -> up_to_date | error</name> - <name>files(ModFiles, Options) -> up_to_date | error</name> + <name since="">files(ModFiles) -> up_to_date | error</name> + <name since="">files(ModFiles, Options) -> up_to_date | error</name> <fsummary>Compile a set of modules.</fsummary> <type> <v>ModFiles = [Module | File]</v> diff --git a/lib/tools/doc/src/tags.xml b/lib/tools/doc/src/tags.xml index ea0ae5cc4d..90a8b28177 100644 --- a/lib/tools/doc/src/tags.xml +++ b/lib/tools/doc/src/tags.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>tags.sgml</file> </header> - <module>tags</module> + <module since="">tags</module> <modulesummary>Generate Emacs TAGS file from Erlang source files</modulesummary> <description> <p>A <c>TAGS</c> file is used by Emacs to find function and variable @@ -42,14 +42,14 @@ </description> <funcs> <func> - <name>file(File [, Options])</name> + <name since="">file(File [, Options])</name> <fsummary>Create a <c>TAGS</c>file for the file <c>File</c>.</fsummary> <desc> <p>Create a <c>TAGS</c> file for the file <c>File</c>.</p> </desc> </func> <func> - <name>files(FileList [, Options])</name> + <name since="">files(FileList [, Options])</name> <fsummary>Create a TAGS file for the files in the list<c>FileList</c>.</fsummary> <desc> <p>Create a TAGS file for the files in the list @@ -57,7 +57,7 @@ </desc> </func> <func> - <name>dir(Dir [, Options])</name> + <name since="">dir(Dir [, Options])</name> <fsummary>Create a TAGS file for all files in directory<c>Dir</c>.</fsummary> <desc> <p>Create a TAGS file for all files in directory @@ -65,7 +65,7 @@ </desc> </func> <func> - <name>dirs(DirList [, Options])</name> + <name since="">dirs(DirList [, Options])</name> <fsummary>Create a TAGS file for all files in any directory in<c>DirList</c>.</fsummary> <desc> <p>Create a TAGS file for all files in any directory in @@ -73,7 +73,7 @@ </desc> </func> <func> - <name>subdir(Dir [, Options])</name> + <name since="">subdir(Dir [, Options])</name> <fsummary>Descend recursively down the directory <c>Dir</c>and create a <c>TAGS</c>file based on all files found.</fsummary> <desc> <p>Descend recursively down the directory <c>Dir</c> and @@ -81,7 +81,7 @@ </desc> </func> <func> - <name>subdirs(DirList [, Options])</name> + <name since="">subdirs(DirList [, Options])</name> <fsummary>Descend recursively down all the directories in<c>DirList</c>and create a <c>TAGS</c>file based on all files found.</fsummary> <desc> <p>Descend recursively down all the directories in @@ -90,7 +90,7 @@ </desc> </func> <func> - <name>root([Options])</name> + <name since="">root([Options])</name> <fsummary>Create a <c>TAGS</c>file covering all files in the Erlang distribution.</fsummary> <desc> <p>Create a <c>TAGS</c> file covering all files in diff --git a/lib/tools/doc/src/xref.xml b/lib/tools/doc/src/xref.xml index 6f833246ad..ab3641a52f 100644 --- a/lib/tools/doc/src/xref.xml +++ b/lib/tools/doc/src/xref.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>xref.sgml</file> </header> - <module>xref</module> + <module since="">xref</module> <modulesummary>A Cross Reference Tool for analyzing dependencies between functions, modules, applications and releases.</modulesummary> <description> <p>Xref is a cross reference tool that can be used for finding @@ -729,7 +729,7 @@ xref() = atom() | pid() </pre> </description> <funcs> <func> - <name>add_application(Xref, Directory [, Options]) -> {ok, application()} | Error</name> + <name since="">add_application(Xref, Directory [, Options]) -> {ok, application()} | Error</name> <fsummary>Add the modules of an application.</fsummary> <type> <v>Directory = directory()</v> @@ -761,7 +761,7 @@ xref() = atom() | pid() </pre> </desc> </func> <func> - <name>add_directory(Xref, Directory [, Options]) -> {ok, Modules} | Error</name> + <name since="">add_directory(Xref, Directory [, Options]) -> {ok, Modules} | Error</name> <fsummary>Add the modules in a directory.</fsummary> <type> <v>Directory = directory()</v> @@ -791,7 +791,7 @@ xref() = atom() | pid() </pre> </desc> </func> <func> - <name>add_module(Xref, File [, Options]) -> {ok, module()} | Error</name> + <name since="">add_module(Xref, File [, Options]) -> {ok, module()} | Error</name> <fsummary>Add a module.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -814,7 +814,7 @@ xref() = atom() | pid() </pre> </desc> </func> <func> - <name>add_release(Xref, Directory [, Options]) -> {ok, release()} | Error</name> + <name since="">add_release(Xref, Directory [, Options]) -> {ok, release()} | Error</name> <fsummary>Add the modules of a release.</fsummary> <type> <v>Directory = directory()</v> @@ -849,7 +849,7 @@ xref() = atom() | pid() </pre> </desc> </func> <func> - <name>analyze(Xref, Analysis [, Options]) -> {ok, Answer} | Error</name> + <name since="">analyze(Xref, Analysis [, Options]) -> {ok, Answer} | Error</name> <fsummary>Evaluate a predefined analysis.</fsummary> <type> <v>Analysis = undefined_function_calls | undefined_functions | locals_not_used | exports_not_used | deprecated_function_calls | {deprecated_function_calls, DeprFlag} | deprecated_functions | {deprecated_functions, DeprFlag} | {call, FuncSpec} | {use, FuncSpec} | {module_call, ModSpec} | {module_use, ModSpec} | {application_call, AppSpec} | {application_use, AppSpec} | {release_call, RelSpec} | {release_use, RelSpec}</v> @@ -939,7 +939,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>d(Directory) -> [DebugInfoResult] | [NoDebugInfoResult] | Error</name> + <name since="">d(Directory) -> [DebugInfoResult] | [NoDebugInfoResult] | Error</name> <fsummary>Check the modules in a directory using the code path.</fsummary> <type> <v>Directory = directory()</v> @@ -979,8 +979,8 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>forget(Xref) -> ok</name> - <name>forget(Xref, Variables) -> ok | Error</name> + <name since="">forget(Xref) -> ok</name> + <name since="">forget(Xref, Variables) -> ok | Error</name> <fsummary>Remove user variables and their values.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -994,7 +994,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>format_error(Error) -> Chars</name> + <name since="">format_error(Error) -> Chars</name> <fsummary>Return an English description of an Xref error reply.</fsummary> <type> <v>Error = {error, module(), term()}</v> @@ -1008,8 +1008,8 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>get_default(Xref) -> [{Option, Value}]</name> - <name>get_default(Xref, Option) -> {ok, Value} | Error</name> + <name since="">get_default(Xref) -> [{Option, Value}]</name> + <name since="">get_default(Xref, Option) -> {ok, Value} | Error</name> <fsummary>Return the default values of options.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1023,7 +1023,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>get_library_path(Xref) -> {ok, LibraryPath}</name> + <name since="">get_library_path(Xref) -> {ok, LibraryPath}</name> <fsummary>Return the library path.</fsummary> <type> <v>LibraryPath = library_path()</v> @@ -1034,9 +1034,9 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>info(Xref) -> [Info]</name> - <name>info(Xref, Category) -> [{Item, [Info]}]</name> - <name>info(Xref, Category, Items) -> [{Item, [Info]}]</name> + <name since="">info(Xref) -> [Info]</name> + <name since="">info(Xref, Category) -> [{Item, [Info]}]</name> + <name since="">info(Xref, Category, Items) -> [{Item, [Info]}]</name> <fsummary>Return information about an Xref server.</fsummary> <type> <v>Application = [] | [application()]</v> @@ -1220,8 +1220,8 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>m(Module) -> [DebugInfoResult] | [NoDebugInfoResult] | Error</name> - <name>m(File) -> [DebugInfoResult] | [NoDebugInfoResult] | Error</name> + <name since="">m(Module) -> [DebugInfoResult] | [NoDebugInfoResult] | Error</name> + <name since="">m(File) -> [DebugInfoResult] | [NoDebugInfoResult] | Error</name> <fsummary>Check a module using the code path.</fsummary> <type> <v>DebugInfoResult = {deprecated, [funcall()]} | {undefined, [funcall()]} | {unused, [mfa()]}</v> @@ -1263,7 +1263,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>q(Xref, Query [, Options]) -> {ok, Answer} | Error</name> + <name since="">q(Xref, Query [, Options]) -> {ok, Answer} | Error</name> <fsummary>Evaluate a query.</fsummary> <type> <v>Answer = false | [constant()] | [Call] | [Component] | int() | [DefineAt] | [CallAt] | [AllLines]</v> @@ -1322,7 +1322,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>remove_application(Xref, Applications) -> ok | Error</name> + <name since="">remove_application(Xref, Applications) -> ok | Error</name> <fsummary>Remove applications and their modules.</fsummary> <type> <v>Applications = application() | [application()]</v> @@ -1335,7 +1335,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>remove_module(Xref, Modules) -> ok | Error</name> + <name since="">remove_module(Xref, Modules) -> ok | Error</name> <fsummary>Remove analyzed modules.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1348,7 +1348,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>remove_release(Xref, Releases) -> ok | Error</name> + <name since="">remove_release(Xref, Releases) -> ok | Error</name> <fsummary>Remove releases and their applications and modules.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1363,7 +1363,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>replace_application(Xref, Application, Directory [, Options]) -> {ok, application()} | Error</name> + <name since="">replace_application(Xref, Application, Directory [, Options]) -> {ok, application()} | Error</name> <fsummary>Replace an application's modules.</fsummary> <type> <v>Application = application()</v> @@ -1384,7 +1384,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>replace_module(Xref, Module, File [, Options]) -> {ok, module()} | Error</name> + <name since="">replace_module(Xref, Module, File [, Options]) -> {ok, module()} | Error</name> <fsummary>Replace an analyzed module.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1409,8 +1409,8 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>set_default(Xref, Option, Value) -> {ok, OldValue} | Error</name> - <name>set_default(Xref, OptionValues) -> ok | Error</name> + <name since="">set_default(Xref, Option, Value) -> {ok, OldValue} | Error</name> + <name since="">set_default(Xref, OptionValues) -> ok | Error</name> <fsummary>Set the default values of options.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1435,7 +1435,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>set_library_path(Xref, LibraryPath [, Options]) -> ok | Error</name> + <name since="">set_library_path(Xref, LibraryPath [, Options]) -> ok | Error</name> <fsummary>Set the library path and finds the library modules.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1469,7 +1469,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>start(NameOrOptions) -> Return</name> + <name since="">start(NameOrOptions) -> Return</name> <fsummary>Create an Xref server.</fsummary> <type> <v>NameOrOptions = Name | Options</v> @@ -1487,7 +1487,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>start(Name, Options) -> Return</name> + <name since="">start(Name, Options) -> Return</name> <fsummary>Create an Xref server.</fsummary> <type> <v>Name = atom()</v> @@ -1504,7 +1504,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>stop(Xref)</name> + <name since="">stop(Xref)</name> <fsummary>Delete an Xref server.</fsummary> <type> <v>Xref = xref()</v> @@ -1514,7 +1514,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>update(Xref [, Options]) -> {ok, Modules} | Error</name> + <name since="">update(Xref [, Options]) -> {ok, Modules} | Error</name> <fsummary>Replace newly compiled analyzed modules.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1534,7 +1534,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>variables(Xref [, Options]) -> {ok, [VariableInfo]}</name> + <name since="">variables(Xref [, Options]) -> {ok, [VariableInfo]}</name> <fsummary>Return the names of variables.</fsummary> <type> <v>Options = [Option] | Option</v> diff --git a/lib/tools/priv/styles.css b/lib/tools/priv/styles.css index e10e94e3ad..84f00be9fd 100644 --- a/lib/tools/priv/styles.css +++ b/lib/tools/priv/styles.css @@ -53,21 +53,25 @@ table thead { display: none; } table td.line, +table td.line a, table td.hits { width: 20px; background: #eaeaea; text-align: center; + text-decoration: none; font-size: 11px; padding: 0 10px; color: #949494; } table td.hits { width: 10px; + text-align: right; padding: 2px 5px; color: rgba(0, 0, 0, 0.6); background-color: #f0f0f0; } tr.miss td.line, +tr.miss td.line a, tr.miss td.hits { background-color: #ffdce0; border-color: #fdaeb7; @@ -76,6 +80,7 @@ tr.miss td { background-color: #ffeef0; } tr.hit td.line, +tr.hit td.line a, tr.hit td.hits { background-color: #cdffd8; border-color: #bef5cb; diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index d7269e3f27..8fe866cb69 100644 --- a/lib/tools/src/cover.erl +++ b/lib/tools/src/cover.erl @@ -23,6 +23,7 @@ %% This module implements the Erlang coverage tool. %% %% ARCHITECTURE +%% %% The coverage tool consists of one process on each node involved in %% coverage analysis. The process is registered as 'cover_server' %% (?SERVER). The cover_server on the 'main' node is in charge, and @@ -30,45 +31,62 @@ %% 'DOWN' message for another cover_server, it marks the node as %% 'lost'. If a nodeup is received for a lost node the main node %% ensures that the cover compiled modules are loaded again. If the -%% remote node was alive during the disconnected periode, cover data -%% for this periode will also be included in the analysis. +%% remote node was alive during the disconnected period, cover data +%% for this period will also be included in the analysis. %% %% The cover_server process on the main node is implemented by the %% functions init_main/1 and main_process_loop/1. The cover_server on %% the remote nodes are implemented by the functions init_remote/2 and %% remote_process_loop/1. %% +%% COUNTERS +%% +%% The 'counters' modules is used for counting how many time each line +%% executed. Each cover-compiled module will have its own array of +%% counters. +%% +%% The counter reference for module Module is stored in a persistent +%% term with the key {cover,Module}. +%% +%% When the cover:local_only/0 function has been called, the reference +%% for the counter array will be compiled into each cover-compiled +%% module directly (instead of retrieving it from a persistent term). +%% That will be faster, but the resulting code can be only be used on +%% the main node. +%% %% TABLES -%% Each nodes has two tables: cover_internal_data_table (?COVER_TABLE) and. -%% cover_internal_clause_table (?COVER_CLAUSE_TABLE). -%% ?COVER_TABLE contains the bump data i.e. the data about which lines -%% have been executed how many times. +%% +%% Each node has two tables: ?COVER_MAPPING_TABLE and ?COVER_CLAUSE_TABLE. +%% ?COVER_MAPPING_TABLE maps from a #bump{} record to an index in the +%% counter array for the module. It is used both during instrumentation +%% of cover-compiled modules and when collecting the counter values. +%% %% ?COVER_CLAUSE_TABLE contains information about which clauses in which modules %% cover is currently collecting statistics. -%% -%% The main node owns tables named -%% 'cover_collected_remote_data_table' (?COLLECTION_TABLE) and -%% 'cover_collected_remote_clause_table' (?COLLECTION_CLAUSE_TABLE). -%% These tables contain data which is collected from remote nodes (either when a -%% remote node is stopped with cover:stop/1 or when analysing). When -%% analysing, data is even moved from the COVER tables on the main -%% node to the COLLECTION tables. %% -%% The main node also has a table named 'cover_binary_code_table' -%% (?BINARY_TABLE). This table contains the binary code for each cover -%% compiled module. This is necessary so that the code can be loaded -%% on remote nodes that are started after the compilation. +%% The main node owns the tables ?COLLECTION_TABLE and +%% ?COLLECTION_CLAUSE_TABLE. The counter data is consolidated into those +%% tables from the counters on both the main node and from remote nodes. +%% This consolidation is done when a remote node is stopped with +%% cover:stop/1 or just before starting an analysis. +%% +%% The main node also has a table named ?BINARY_TABLE. This table +%% contains the abstract code code for each cover-compiled +%% module. This is necessary so that the code can be loaded on remote +%% nodes that are started after the compilation. %% %% PARALLELISM +%% %% To take advantage of SMP when doing the cover analysis both the data %% collection and analysis has been parallelized. One process is spawned for %% each node when collecting data, and on the remote node when collecting data %% one process is spawned per module. %% -%% When analyzing data it is possible to issue multiple analyse(_to_file)/X -%% calls at once. They are however all calls (for backwards compatibility -%% reasons) so the user of cover will have to spawn several processes to to the -%% calls ( or use async_analyse_to_file ). +%% When analyzing data it is possible to issue multiple +%% analyse(_to_file)/X calls at once. They are, however, all calls +%% (for backwards compatibility reasons), so the user of cover will +%% have to spawn several processes to to the calls (or use +%% async_analyse_to_file/X). %% %% External exports @@ -89,7 +107,8 @@ modules/0, imported/0, imported_modules/0, which_nodes/0, is_compiled/1, reset/1, reset/0, flush/1, - stop/0, stop/1]). + stop/0, stop/1, + local_only/0]). -export([remote_start/1,get_main_node/0]). %% Used internally to ensure we upgrade the code to the latest version. @@ -98,9 +117,16 @@ -record(main_state, {compiled=[], % [{Module,File}] imported=[], % [{Module,File,ImportFile}] stopper, % undefined | pid() + local_only=false, % true | false nodes=[], % [Node] lost_nodes=[]}). % [Node] +-record(remote_data, {module, + file, + code, + mapping, + clauses}). + -record(remote_state, {compiled=[], % [{Module,File}] main_node}). % atom() @@ -126,11 +152,12 @@ is_guard=false % boolean }). --define(COVER_TABLE, 'cover_internal_data_table'). +-define(COVER_MAPPING_TABLE, 'cover_internal_mapping_table'). -define(COVER_CLAUSE_TABLE, 'cover_internal_clause_table'). -define(BINARY_TABLE, 'cover_binary_code_table'). -define(COLLECTION_TABLE, 'cover_collected_remote_data_table'). -define(COLLECTION_CLAUSE_TABLE, 'cover_collected_remote_clause_table'). + -define(TAG, cover_compiled). -define(SERVER, cover_server). @@ -186,6 +213,11 @@ start(Node) when is_atom(Node) -> start(Nodes) -> call({start_nodes,remove_myself(Nodes,[])}). +%% local_only() -> ok | {error,too_late} + +local_only() -> + call(local_only). + %% compile(ModFiles) -> %% compile(ModFiles, Options) -> %% compile_module(ModFiles) -> Result @@ -255,15 +287,8 @@ compile_directory(Dir, Options) when is_list(Dir), is_list(Options) -> compile_modules(Files,Options) -> Options2 = filter_options(Options), - %% compile_modules(Files,Options2,[]). call({compile, Files, Options2}). -%% compile_modules([File|Files], Options, Result) -> -%% R = call({compile, File, Options}), -%% compile_modules(Files,Options,[R|Result]); -%% compile_modules([],_Opts,Result) -> -%% lists:reverse(Result). - filter_options(Options) -> lists:filter(fun(Option) -> case Option of @@ -561,16 +586,6 @@ flush(Nodes) -> get_main_node() -> call(get_main_node). -%% bump(Module, Function, Arity, Clause, Line) -%% Module = Function = atom() -%% Arity = Clause = Line = integer() -%% This function is inserted into Cover compiled modules, once for each -%% executable line. -%bump(Module, Function, Arity, Clause, Line) -> -% Key = #bump{module=Module, function=Function, arity=Arity, clause=Clause, -% line=Line}, -% ets:update_counter(?COVER_TABLE, Key, 1). - call(Request) -> Ref = erlang:monitor(process,?SERVER), receive {'DOWN', Ref, _Type, _Object, noproc} -> @@ -631,10 +646,8 @@ remote_reply(MainNode,Reply) -> init_main(Starter) -> register(?SERVER,self()), - %% Having write concurrancy here gives a 40% performance boost - %% when collect/1 is called. - ?COVER_TABLE = ets:new(?COVER_TABLE, [set, public, named_table, - {write_concurrency, true}]), + ?COVER_MAPPING_TABLE = ets:new(?COVER_MAPPING_TABLE, + [ordered_set, public, named_table]), ?COVER_CLAUSE_TABLE = ets:new(?COVER_CLAUSE_TABLE, [set, public, named_table]), ?BINARY_TABLE = ets:new(?BINARY_TABLE, [set, public, named_table]), @@ -648,10 +661,26 @@ init_main(Starter) -> main_process_loop(State) -> receive + {From, local_only} -> + case State of + #main_state{compiled=[],nodes=[]} -> + reply(From, ok), + main_process_loop(State#main_state{local_only=true}); + #main_state{} -> + reply(From, {error,too_late}), + main_process_loop(State) + end; + {From, {start_nodes,Nodes}} -> - {StartedNodes,State1} = do_start_nodes(Nodes, State), - reply(From, {ok,StartedNodes}), - main_process_loop(State1); + case State#main_state.local_only of + false -> + {StartedNodes,State1} = do_start_nodes(Nodes, State), + reply(From, {ok,StartedNodes}), + main_process_loop(State1); + true -> + reply(From, {error,local_only}), + main_process_loop(State) + end; {From, {compile, Files, Options}} -> {R,S} = do_compile(Files, Options, State), @@ -742,11 +771,12 @@ main_process_loop(State) -> end, State#main_state.nodes), reload_originals(State#main_state.compiled), - ets:delete(?COVER_TABLE), + ets:delete(?COVER_MAPPING_TABLE), ets:delete(?COVER_CLAUSE_TABLE), ets:delete(?BINARY_TABLE), ets:delete(?COLLECTION_TABLE), ets:delete(?COLLECTION_CLAUSE_TABLE), + delete_all_counters(), unregister(?SERVER), reply(From, ok); @@ -878,10 +908,8 @@ main_process_loop(State) -> init_remote(Starter,MainNode) -> register(?SERVER,self()), - %% write_concurrency here makes otp_8270 break :( - ?COVER_TABLE = ets:new(?COVER_TABLE, [set, public, named_table - %,{write_concurrency, true} - ]), + ?COVER_MAPPING_TABLE = ets:new(?COVER_MAPPING_TABLE, + [ordered_set, public, named_table]), ?COVER_CLAUSE_TABLE = ets:new(?COVER_CLAUSE_TABLE, [set, public, named_table]), Starter ! {self(),started}, @@ -904,7 +932,7 @@ remote_process_loop(State) -> remote_process_loop(State#remote_state{compiled=Compiled}); {remote,reset,Module} -> - do_reset(Module), + reset_counters(Module), remote_reply(State#remote_state.main_node, ok), remote_process_loop(State); @@ -925,8 +953,9 @@ remote_process_loop(State) -> {remote,stop} -> reload_originals(State#remote_state.compiled), - ets:delete(?COVER_TABLE), + ets:delete(?COVER_MAPPING_TABLE), ets:delete(?COVER_CLAUSE_TABLE), + delete_all_counters(), unregister(?SERVER), ok; % not replying since 'DOWN' message will be received anyway @@ -961,28 +990,12 @@ remote_process_loop(State) -> end. do_collect(Modules, CollectorPid, From) -> - _ = pmap( - fun(Module) -> - Pattern = {#bump{module=Module, _='_'}, '$1'}, - MatchSpec = [{Pattern,[{'=/=','$1',0}],['$_']}], - Match = ets:select(?COVER_TABLE,MatchSpec,?CHUNK_SIZE), - send_chunks(Match, CollectorPid, []) - end,Modules), + _ = pmap(fun(Module) -> + send_counters(Module, CollectorPid) + end, Modules), CollectorPid ! done, remote_reply(From, ok). -send_chunks('$end_of_table', _CollectorPid, Mons) -> - get_downs(Mons); -send_chunks({Chunk,Continuation}, CollectorPid, Mons) -> - Mon = spawn_monitor( - fun() -> - lists:foreach(fun({Bump,_N}) -> - ets:insert(?COVER_TABLE, {Bump,0}) - end, - Chunk) end), - send_chunk(CollectorPid,Chunk), - send_chunks(ets:select(Continuation), CollectorPid, [Mon|Mons]). - send_chunk(CollectorPid,Chunk) -> CollectorPid ! {chunk,Chunk,self()}, receive continue -> ok end. @@ -1021,10 +1034,15 @@ do_reload_original(Module) -> ignore end. -load_compiled([{Module,File,Binary,InitialTable}|Compiled],Acc) -> - %% Make sure the #bump{} records are available *before* the - %% module is loaded. - insert_initial_data(InitialTable), +load_compiled([Data|Compiled],Acc) -> + %% Make sure the #bump{} records and counters are available *before* + %% compiling and loading the code. + #remote_data{module=Module,file=File,code=Beam, + mapping=InitialMapping,clauses=InitialClauses} = Data, + ets:insert(?COVER_MAPPING_TABLE, InitialMapping), + ets:insert(?COVER_CLAUSE_TABLE, InitialClauses), + maybe_create_counters(Module, true), + Sticky = case code:is_sticky(Module) of true -> code:unstick_mod(Module), @@ -1032,7 +1050,7 @@ load_compiled([{Module,File,Binary,InitialTable}|Compiled],Acc) -> false -> false end, - NewAcc = case code:load_binary(Module, ?TAG, Binary) of + NewAcc = case code:load_binary(Module, ?TAG, Beam) of {module,Module} -> add_compiled(Module, File, Acc); _ -> @@ -1047,16 +1065,6 @@ load_compiled([{Module,File,Binary,InitialTable}|Compiled],Acc) -> load_compiled([],Acc) -> Acc. -insert_initial_data([Item|Items]) when is_atom(element(1,Item)) -> - ets:insert(?COVER_CLAUSE_TABLE, Item), - insert_initial_data(Items); -insert_initial_data([Item|Items]) -> - ets:insert(?COVER_TABLE, Item), - insert_initial_data(Items); -insert_initial_data([]) -> - ok. - - unload([Module|Modules]) -> do_clear(Module), do_reload_original(Module), @@ -1177,7 +1185,7 @@ get_downs_r([]) -> []; get_downs_r(Mons) -> receive - {'DOWN', Ref, _Type, Pid, R={_,_,_,_}} -> + {'DOWN', Ref, _Type, Pid, #remote_data{}=R} -> [R|get_downs_r(lists:delete({Pid,Ref},Mons))]; {'DOWN', Ref, _Type, Pid, Reason} = Down -> case lists:member({Pid,Ref},Mons) of @@ -1196,19 +1204,13 @@ get_downs_r(Mons) -> %% Binary is the beam code for the module and InitialTable is the initial %% data to insert in ?COVER_TABLE. get_data_for_remote_loading({Module,File}) -> - [{Module,Binary}] = ets:lookup(?BINARY_TABLE,Module), + [{Module,Code}] = ets:lookup(?BINARY_TABLE, Module), %%! The InitialTable list will be long if the module is big - what to do?? - InitialBumps = ets:select(?COVER_TABLE,ms(Module)), + Mapping = counters_mapping_table(Module), InitialClauses = ets:lookup(?COVER_CLAUSE_TABLE,Module), - {Module,File,Binary,InitialBumps ++ InitialClauses}. - -%% Create a match spec which returns the clause info {Module,InitInfo} and -%% all #bump keys for the given module with 0 number of calls. -ms(Module) -> - ets:fun2ms(fun({Key,_}) when Key#bump.module=:=Module -> - {Key,0} - end). + #remote_data{module=Module,file=File,code=Code, + mapping=Mapping,clauses=InitialClauses}. %% Unload modules on remote nodes remote_unload(Nodes,UnloadedModules) -> @@ -1464,7 +1466,7 @@ get_compiled_still_loaded(Nodes,Compiled0) -> do_compile_beams(ModsAndFiles, State) -> Result0 = pmap(fun({ok,Module,File}) -> - do_compile_beam(Module,File,State); + do_compile_beam(Module, File, State); (Error) -> Error end, @@ -1476,8 +1478,10 @@ do_compile_beams(ModsAndFiles, State) -> do_compile_beam(Module,BeamFile0,State) -> case get_beam_file(Module,BeamFile0,State#main_state.compiled) of {ok,BeamFile} -> + LocalOnly = State#main_state.local_only, UserOptions = get_compile_options(Module,BeamFile), - case do_compile_beam1(Module,BeamFile,UserOptions) of + case do_compile_beam1(Module,BeamFile, + UserOptions,LocalOnly) of {ok, Module} -> {ok,Module,BeamFile}; error -> @@ -1503,41 +1507,39 @@ fix_state_and_result([],State,Acc) -> do_compile(Files, Options, State) -> + LocalOnly = State#main_state.local_only, Result0 = pmap(fun(File) -> - do_compile(File, Options) + do_compile1(File, Options, LocalOnly) end, Files), Compiled = [{M,F} || {ok,M,F} <- Result0], remote_load_compiled(State#main_state.nodes,Compiled), fix_state_and_result(Result0,State,[]). -do_compile(File, Options) -> - case do_compile1(File, Options) of +do_compile1(File, Options, LocalOnly) -> + case do_compile2(File, Options, LocalOnly) of {ok, Module} -> {ok,Module,File}; error -> {error,File} end. -%% do_compile1(File, Options) -> {ok,Module} | error -do_compile1(File, UserOptions) -> +%% do_compile2(File, Options) -> {ok,Module} | error +do_compile2(File, UserOptions, LocalOnly) -> Options = [debug_info,binary,report_errors,report_warnings] ++ UserOptions, case compile:file(File, Options) of {ok, Module, Binary} -> - do_compile_beam1(Module,Binary,UserOptions); + do_compile_beam1(Module,Binary,UserOptions,LocalOnly); error -> error end. %% Beam is a binary or a .beam file name -do_compile_beam1(Module,Beam,UserOptions) -> +do_compile_beam1(Module,Beam,UserOptions,LocalOnly) -> %% Clear database do_clear(Module), - %% Extract the abstract format and insert calls to bump/6 at - %% every executable line and, as a side effect, initiate - %% the database - + %% Extract the abstract format. case get_abstract_code(Module, Beam) of no_abstract_code=E -> {error,E}; @@ -1547,7 +1549,8 @@ do_compile_beam1(Module,Beam,UserOptions) -> Forms0 = epp:interpret_file_attribute(Code), case find_main_filename(Forms0) of {ok,MainFile} -> - do_compile_beam2(Module,Beam,UserOptions,Forms0,MainFile); + do_compile_beam2(Module,Beam,UserOptions, + Forms0,MainFile,LocalOnly); Error -> Error end; @@ -1566,26 +1569,35 @@ get_abstract_code(Module, Beam) -> Error -> Error end. -do_compile_beam2(Module,Beam,UserOptions,Forms0,MainFile) -> - {Forms,Vars} = transform(Forms0, Module, MainFile), +do_compile_beam2(Module,Beam,UserOptions,Forms0,MainFile,LocalOnly) -> + init_counter_mapping(Module), + + %% Instrument the abstract code by inserting + %% calls to update the counters. + {Forms,Vars} = transform(Forms0, Module, MainFile, LocalOnly), + + %% Create counters. + maybe_create_counters(Module, not LocalOnly), %% We need to recover the source from the compilation %% info otherwise the newly compiled module will have %% source pointing to the current directory SourceInfo = get_source_info(Module, Beam), - %% Compile and load the result + %% Compile and load the result. %% It's necessary to check the result of loading since it may - %% fail, for example if Module resides in a sticky directory - {ok, Module, Binary} = compile:forms(Forms, SourceInfo ++ UserOptions), + %% fail, for example if Module resides in a sticky directory. + Options = SourceInfo ++ UserOptions, + {ok, Module, Binary} = compile:forms(Forms, Options), + case code:load_binary(Module, ?TAG, Binary) of {module, Module} -> - %% Store info about all function clauses in database + %% Store info about all function clauses in database. InitInfo = lists:reverse(Vars#vars.init_info), ets:insert(?COVER_CLAUSE_TABLE, {Module, InitInfo}), - %% Store binary code so it can be loaded on remote nodes + %% Store binary code so it can be loaded on remote nodes. ets:insert(?BINARY_TABLE, {Module, Binary}), {ok, Module}; @@ -1617,11 +1629,12 @@ get_compile_info(Module, Beam) -> [] end. -transform(Code, Module, MainFile) -> +transform(Code, Module, MainFile, LocalOnly) -> Vars0 = #vars{module=Module}, - {ok,MungedForms,Vars} = transform_2(Code,[],Vars0,MainFile,on), + {ok,MungedForms0,Vars} = transform_2(Code, [], Vars0, MainFile, on), + MungedForms = patch_code(Module, MungedForms0, LocalOnly), {MungedForms,Vars}. - + %% Helpfunction which returns the first found file-attribute, which can %% be interpreted as the name of the main erlang source file. find_main_filename([{attribute,_,file,{MainFile,_}}|_]) -> @@ -1788,19 +1801,7 @@ munge_body([Expr|Body], Vars, MungedBody, LastExprBumpLines) -> MungedExprs1 = [MungedExpr|MungedBody1], munge_body(Body, Vars3, MungedExprs1, NewBumps); false -> - ets:insert(?COVER_TABLE, {#bump{module = Vars#vars.module, - function = Vars#vars.function, - arity = Vars#vars.arity, - clause = Vars#vars.clause, - line = Line}, - 0}), Bump = bump_call(Vars, Line), -% Bump = {call, 0, {remote, 0, {atom,0,cover}, {atom,0,bump}}, -% [{atom, 0, Vars#vars.module}, -% {atom, 0, Vars#vars.function}, -% {integer, 0, Vars#vars.arity}, -% {integer, 0, Vars#vars.clause}, -% {integer, 0, Line}]}, Lines2 = [Line|Lines], {MungedExpr, Vars2} = munge_expr(Expr, Vars#vars{lines=Lines2}), NewBumps = new_bumps(Vars2, Vars), @@ -1855,8 +1856,10 @@ maybe_fix_last_expr(MungedExprs, Vars, LastExprBumpLines) -> last_expr_needs_fixing(Vars, LastExprBumpLines) -> case common_elems(Vars#vars.no_bump_lines, LastExprBumpLines) of - [Line] -> {yes, Line}; - _ -> no + [Line] -> + {yes, Line}; + _ -> + no end. fix_last_expr([MungedExpr|MungedExprs], Line, Vars) -> @@ -1921,9 +1924,7 @@ fix_cls([Cl | Cls], Line, Bump) -> bumps_line(E, L) -> try bumps_line1(E, L) catch true -> true end. -bumps_line1({call,_,{remote,_,{atom,_,ets},{atom,_,update_counter}}, - [{atom,_,?COVER_TABLE},{tuple,_,[_,_,_,_,_,{integer,_,Line}]},_]}, - Line) -> +bumps_line1({'BUMP',Line,_}, Line) -> throw(true); bumps_line1([E | Es], Line) -> bumps_line1(E, Line), @@ -1933,19 +1934,12 @@ bumps_line1(T, Line) when is_tuple(T) -> bumps_line1(_, _) -> false. -%%% End of fix of last expression. - +%% Insert a place holder for the call to counters:add/3 in the +%% abstract code. bump_call(Vars, Line) -> - A = erl_anno:new(0), - {call,A,{remote,A,{atom,A,ets},{atom,A,update_counter}}, - [{atom,A,?COVER_TABLE}, - {tuple,A,[{atom,A,?BUMP_REC_NAME}, - {atom,A,Vars#vars.module}, - {atom,A,Vars#vars.function}, - {integer,A,Vars#vars.arity}, - {integer,A,Vars#vars.clause}, - {integer,A,Line}]}, - {integer,A,1}]}. + {'BUMP',Line,counter_index(Vars, Line)}. + +%%% End of fix of last expression. munge_expr({match,Line,ExprL,ExprR}, Vars) -> {MungedExprL, Vars2} = munge_expr(ExprL, Vars), @@ -2105,6 +2099,159 @@ subtract(L1, L2) -> common_elems(L1, L2) -> [E || E <- L1, lists:member(E, L2)]. +%%%--Counters------------------------------------------------------------ + +init_counter_mapping(Mod) -> + true = ets:insert_new(?COVER_MAPPING_TABLE, {Mod,0}), + ok. + +counter_index(Vars, Line) -> + #vars{module=Mod,function=F,arity=A,clause=C} = Vars, + Key = #bump{module=Mod,function=F,arity=A, + clause=C,line=Line}, + case ets:lookup(?COVER_MAPPING_TABLE, Key) of + [] -> + Index = ets:update_counter(?COVER_MAPPING_TABLE, + Mod, {2,1}), + true = ets:insert(?COVER_MAPPING_TABLE, {Key,Index}), + Index; + [{Key,Index}] -> + Index + end. + +%% Create the counter array and store as a persistent term. +maybe_create_counters(Mod, true) -> + Cref = create_counters(Mod), + Key = {?MODULE,Mod}, + persistent_term:put(Key, Cref), + ok; +maybe_create_counters(_Mod, false) -> + ok. + +create_counters(Mod) -> + Size0 = ets:lookup_element(?COVER_MAPPING_TABLE, Mod, 2), + Size = max(1, Size0), %Size must not be 0. + Cref = counters:new(Size, [write_concurrency]), + ets:insert(?COVER_MAPPING_TABLE, {{counters,Mod},Cref}), + Cref. + +patch_code(Mod, Forms, false) -> + A = erl_anno:new(0), + AbstrKey = {tuple,A,[{atom,A,?MODULE},{atom,A,Mod}]}, + patch_code1(Forms, {distributed,AbstrKey}); +patch_code(Mod, Forms, true) -> + Cref = create_counters(Mod), + AbstrCref = cid_to_abstract(Cref), + patch_code1(Forms, {local_only,AbstrCref}). + +%% Go through the abstract code and replace 'BUMP' forms +%% with the actual code to increment the counters. +patch_code1({'BUMP',_Line,Index}, {distributed,AbstrKey}) -> + %% Replace with counters:add(persistent_term:get(Key), Index, 1). + %% This code will work on any node. + A = element(2, AbstrKey), + GetCref = {call,A,{remote,A,{atom,A,persistent_term},{atom,A,get}}, + [AbstrKey]}, + {call,A,{remote,A,{atom,A,counters},{atom,A,add}}, + [GetCref,{integer,A,Index},{integer,A,1}]}; +patch_code1({'BUMP',_Line,Index}, {local_only,AbstrCref}) -> + %% Replace with counters:add(Cref, Index, 1). This code + %% will only work on the local node. + A = element(2, AbstrCref), + {call,A,{remote,A,{atom,A,counters},{atom,A,add}}, + [AbstrCref,{integer,A,Index},{integer,A,1}]}; +patch_code1({clauses,Cs}, Key) -> + {clauses,[patch_code1(El, Key) || El <- Cs]}; +patch_code1([_|_]=List, Key) -> + [patch_code1(El, Key) || El <- List]; +patch_code1(Tuple, Key) when tuple_size(Tuple) >= 3 -> + Acc = [element(2, Tuple),element(1, Tuple)], + patch_code_tuple(3, tuple_size(Tuple), Tuple, Key, Acc); +patch_code1(Other, _Key) -> + Other. + +patch_code_tuple(I, Size, Tuple, Key, Acc) when I =< Size -> + El = patch_code1(element(I, Tuple), Key), + patch_code_tuple(I + 1, Size, Tuple, Key, [El|Acc]); +patch_code_tuple(_I, _Size, _Tuple, _Key, Acc) -> + list_to_tuple(lists:reverse(Acc)). + +%% Don't try this at home! Assumes knowledge of the internal +%% representation of a counter ref. +cid_to_abstract(Cref0) -> + A = erl_anno:new(0), + %% Disable dialyzer warning for breaking opacity. + Cref = binary_to_term(term_to_binary(Cref0)), + {write_concurrency,Ref} = Cref, + {tuple,A,[{atom,A,write_concurrency},{integer,A,Ref}]}. + +%% Called on the remote node. Collect and send counters to +%% the main node. Also zero the counters. +send_counters(Mod, CollectorPid) -> + Process = fun(Chunk) -> send_chunk(CollectorPid, Chunk) end, + move_counters(Mod, Process). + +%% Called on the main node. Collect the counters and consolidate +%% them into the collection table. Also zero the counters. +move_counters(Mod) -> + move_counters(Mod, fun insert_in_collection_table/1). + +move_counters(Mod, Process) -> + Pattern = {#bump{module=Mod,_='_'},'_'}, + Matches = ets:match_object(?COVER_MAPPING_TABLE, Pattern, ?CHUNK_SIZE), + Cref = get_counters_ref(Mod), + move_counters1(Matches, Cref, Process). + +move_counters1({Mappings,Continuation}, Cref, Process) -> + Move = fun({Key,Index}) -> + Count = counters:get(Cref, Index), + ok = counters:sub(Cref, Index, Count), + {Key,Count} + end, + Process(lists:map(Move, Mappings)), + move_counters1(ets:match_object(Continuation), Cref, Process); +move_counters1('$end_of_table', _Cref, _Process) -> + ok. + +counters_mapping_table(Mod) -> + Mapping = counters_mapping(Mod), + Cref = get_counters_ref(Mod), + #{size:=Size} = counters:info(Cref), + [{Mod,Size}|Mapping]. + +get_counters_ref(Mod) -> + ets:lookup_element(?COVER_MAPPING_TABLE, {counters,Mod}, 2). + +counters_mapping(Mod) -> + Pattern = {#bump{module=Mod,_='_'},'_'}, + ets:match_object(?COVER_MAPPING_TABLE, Pattern). + +clear_counters(Mod) -> + _ = persistent_term:erase({?MODULE,Mod}), + ets:delete(?COVER_MAPPING_TABLE, Mod), + Pattern = {#bump{module=Mod,_='_'},'_'}, + _ = ets:match_delete(?COVER_MAPPING_TABLE, Pattern), + ok. + +%% Reset counters (set counters to 0). +reset_counters(Mod) -> + Pattern = {#bump{module=Mod,_='_'},'$1'}, + MatchSpec = [{Pattern,[],['$1']}], + Matches = ets:select(?COVER_MAPPING_TABLE, + MatchSpec, ?CHUNK_SIZE), + Cref = get_counters_ref(Mod), + reset_counters1(Matches, Cref). + +reset_counters1({Indices,Continuation}, Cref) -> + _ = [counters:put(Cref, N, 0) || N <- Indices], + reset_counters1(ets:select(Continuation), Cref); +reset_counters1('$end_of_table', _Cref) -> + ok. + +delete_all_counters() -> + _ = [persistent_term:erase(Key) || {?MODULE,_}=Key <- persistent_term:get()], + ok. + %%%--Analysis------------------------------------------------------------ %% Collect data for all modules @@ -2140,20 +2287,7 @@ collect(Module,Clauses,Nodes) -> %% ?COLLECTION_TABLE. Resetting data in ?COVER_TABLE move_modules({Module,Clauses}) -> ets:insert(?COLLECTION_CLAUSE_TABLE,{Module,Clauses}), - Pattern = {#bump{module=Module, _='_'}, '_'}, - MatchSpec = [{Pattern,[],['$_']}], - Match = ets:select(?COVER_TABLE,MatchSpec,?CHUNK_SIZE), - do_move_module(Match). - -do_move_module({Bumps,Continuation}) -> - lists:foreach(fun({Key,Val}) -> - ets:insert(?COVER_TABLE, {Key,0}), - insert_in_collection_table(Key,Val) - end, - Bumps), - do_move_module(ets:select(Continuation)); -do_move_module('$end_of_table') -> - ok. + move_counters(Module). %% Given a .beam file, find the .erl file. Look first in same directory as %% the .beam file, then in ../src, then in compile info. @@ -2563,11 +2697,13 @@ table_row(Line, L) -> table_data(Line, L, N) -> LineNoNL = Line -- "\n", ["<td class=\"line\" id=\"L",integer_to_list(L),"\">", + "<a href=\"#L",integer_to_list(L),"\">", integer_to_list(L), - "</td>\n", + "</a></td>\n", "<td class=\"hits\">",maybe_integer_to_list(N),"</td>\n", "<td class=\"source\"><code>",LineNoNL,"</code></td>\n</tr>\n"]. +maybe_integer_to_list(0) -> "<pre style=\"display: inline;\">:-(</pre>"; maybe_integer_to_list(N) when is_integer(N) -> integer_to_list(N); maybe_integer_to_list(_) -> "". @@ -2707,7 +2843,7 @@ get_term(Fd) -> %% Reset main node and all remote nodes do_reset_main_node(Module,Nodes) -> - do_reset(Module), + reset_counters(Module), do_reset_collection_table(Module), remote_reset(Module,Nodes). @@ -2715,27 +2851,9 @@ do_reset_collection_table(Module) -> ets:delete(?COLLECTION_CLAUSE_TABLE,Module), ets:match_delete(?COLLECTION_TABLE, {#bump{module=Module},'_'}). -%% do_reset(Module) -> ok -%% The reset is done on ?CHUNK_SIZE number of bumps to avoid building -%% long lists in the case of very large modules -do_reset(Module) -> - Pattern = {#bump{module=Module, _='_'}, '$1'}, - MatchSpec = [{Pattern,[{'=/=','$1',0}],['$_']}], - Match = ets:select(?COVER_TABLE,MatchSpec,?CHUNK_SIZE), - do_reset2(Match). - -do_reset2({Bumps,Continuation}) -> - lists:foreach(fun({Bump,_N}) -> - ets:insert(?COVER_TABLE, {Bump,0}) - end, - Bumps), - do_reset2(ets:select(Continuation)); -do_reset2('$end_of_table') -> - ok. - do_clear(Module) -> ets:match_delete(?COVER_CLAUSE_TABLE, {Module,'_'}), - ets:match_delete(?COVER_TABLE, {#bump{module=Module},'_'}), + clear_counters(Module), case lists:member(?COLLECTION_TABLE, ets:all()) of true -> %% We're on the main node diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src index f8c6aa22cb..f0e0fc4bec 100644 --- a/lib/tools/src/tools.app.src +++ b/lib/tools/src/tools.app.src @@ -21,11 +21,13 @@ [{description, "DEVTOOLS CXC 138 16"}, {vsn, "%VSN%"}, {modules, [cover, + cprof, eprof, fprof, instrument, lcnt, make, + tags, xref, xref_base, xref_compiler, diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index 161b0105b9..ee58fd7a10 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -24,7 +24,8 @@ -include_lib("common_test/include/ct.hrl"). suite() -> - [{ct_hooks,[ts_install_cth]}]. + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,5}}]. all() -> NoStartStop = [eif,otp_5305,otp_5418,otp_7095,otp_8273, @@ -35,7 +36,8 @@ all() -> distribution, reconnect, die_and_reconnect, dont_reconnect_after_stop, stop_node_after_disconnect, export_import, otp_5031, otp_6115, - otp_8270, otp_10979_hanging_node, otp_14817], + otp_8270, otp_10979_hanging_node, otp_14817, + local_only], case whereis(cover_server) of undefined -> [coverage,StartStop ++ NoStartStop]; @@ -1742,6 +1744,40 @@ otp_13289(Config) -> ok = file:delete(File), ok. +local_only(Config) -> + ok = file:set_cwd(proplists:get_value(data_dir, Config)), + + %% Trying restricting to local nodes too late. + cover:start(), + {ok,a} = cover:compile(a), + [a] = cover:modules(), + {error,too_late} = cover:local_only(), + cover:stop(), + + %% Now test local only mode. + cover:start(), + ok = cover:local_only(), + [] = cover:modules(), + {ok,a} = cover:compile(a), + [a] = cover:modules(), + done = a:start(5), + {ok, {a,{17,2}}} = cover:analyse(a, coverage, module), + {ok, [{{a,exit_kalle,0},{1,0}}, + {{a,loop,3},{5,1}}, + {{a,pong,1},{1,0}}, + {{a,start,1},{6,0}}, + {{a,stop,1},{0,1}}, + {{a,trycatch,1},{4,0}}]} = + cover:analyse(a, coverage, function), + + %% Make sure that it is not possible to run cover on + %% slave nodes. + {ok,Name} = test_server:start_node(?FUNCTION_NAME, slave, []), + {error,local_only} = cover:start([Name]), + test_server:stop_node(Name), + + ok. + %%--Auxiliary------------------------------------------------------------ analyse_expr(Expr, Config) -> |