diff options
Diffstat (limited to 'lib/tools')
37 files changed, 1399 insertions, 1266 deletions
| diff --git a/lib/tools/doc/specs/.gitignore b/lib/tools/doc/specs/.gitignore new file mode 100644 index 0000000000..322eebcb06 --- /dev/null +++ b/lib/tools/doc/specs/.gitignore @@ -0,0 +1 @@ +specs_*.xml diff --git a/lib/tools/doc/src/Makefile b/lib/tools/doc/src/Makefile index b554781382..5ff4fe3113 100644 --- a/lib/tools/doc/src/Makefile +++ b/lib/tools/doc/src/Makefile @@ -1,7 +1,7 @@  #  # %CopyrightBegin%  # -# Copyright Ericsson AB 1997-2017. All Rights Reserved. +# Copyright Ericsson AB 1997-2018. All Rights Reserved.  #  # Licensed under the Apache License, Version 2.0 (the "License");  # you may not use this file except in compliance with the License. @@ -84,11 +84,20 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html  TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf +SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) + +TOP_SPECS_FILE = specs.xml +  # ----------------------------------------------------  # FLAGS  # ----------------------------------------------------  XML_FLAGS += +TOOLS_SRC=$(ERL_TOP)/lib/tools/src +TOOLS_INCLUDE=$(ERL_TOP)/lib/tools/include + +SPECS_FLAGS = -I$(TOOLS_SRC) -I$(TOOLS_INCLUDE) +  # ----------------------------------------------------  # Targets  # ---------------------------------------------------- @@ -111,10 +120,16 @@ debug opt:  clean clean_docs:  	rm -rf $(HTMLDIR)/* +	rm -rf $(XMLDIR)  	rm -f $(MAN3DIR)/*  	rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) +	rm -f $(SPECDIR)/*  	rm -f errs core *~ +# erlang_mode doesn't have erlang source so we generate a dummy file for it. +$(SPECDIR)/specs_erlang_mode.xml: +	echo '<module name="erlang_mode"/>' > $(SPECDIR)/specs_erlang_mode.xml +  # ----------------------------------------------------  # Release Target  # ---------------------------------------------------- diff --git a/lib/tools/doc/src/cover.xml b/lib/tools/doc/src/cover.xml index 15cd784253..64c24cea2a 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,7 +128,7 @@        </desc>      </func>      <func> -      <name>start(Nodes) -> {ok,StartedNodes} | {error,not_main_node}</name> +      <name since="">start(Nodes) -> {ok,StartedNodes} | {error,not_main_node}</name>        <fsummary>Start Cover on remote nodes.</fsummary>        <type>          <v>Nodes = StartedNodes = [atom()]</v> @@ -139,10 +139,10 @@        </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 +176,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 +199,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 +241,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 +260,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 +305,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 +359,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 +384,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 +395,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 +406,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 +416,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 +428,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 +442,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 +458,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 +480,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 +504,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 +523,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 4c9e48045e..4bb8862016 100644 --- a/lib/tools/doc/src/fprof.xml +++ b/lib/tools/doc/src/fprof.xml @@ -4,7 +4,7 @@  <erlref>    <header>      <copyright> -      <year>2001</year><year>2016</year> +      <year>2001</year><year>2018</year>        <holder>Ericsson AB. All Rights Reserved.</holder>      </copyright>      <legalnotice> @@ -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> @@ -328,10 +328,16 @@             purposes. This option is only             allowed with the <c>start</c> option.</item>            <tag><c>cpu_time</c>| <c>{cpu_time, bool()}</c></tag> -          <item>The options <c>cpu_time</c> or <c>{cpu_time, true></c> +          <item>The options <c>cpu_time</c> or <c>{cpu_time, true}</c>             makes the timestamps in the trace be in CPU time instead             of wallclock time which is the default. This option is -           only allowed with the <c>start</c> option.</item> +           only allowed with the <c>start</c> option. +           <warning><p>Getting correct values out of cpu_time can be difficult. +           The best way to get correct values is to run using a single +           scheduler and bind that scheduler to a specific CPU, +           i.e. <c>erl +S 1 +sbt db</c>.</p> +           </warning> +          </item>            <tag><c>{procs, PidSpec}</c>| <c>{procs, [PidSpec]}</c></tag>            <item>Specifies which processes that shall be traced. If             this option is not given, the calling process is @@ -354,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> @@ -364,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> @@ -377,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> @@ -388,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> @@ -401,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> @@ -459,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> @@ -469,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> @@ -482,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> @@ -493,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> @@ -506,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 bb6f9b6100..75be22de9b 100644 --- a/lib/tools/doc/src/instrument.xml +++ b/lib/tools/doc/src/instrument.xml @@ -4,7 +4,7 @@  <erlref>    <header>      <copyright> -      <year>1998</year><year>2016</year> +      <year>1998</year><year>2018</year>        <holder>Ericsson AB. All Rights Reserved.</holder>      </copyright>      <legalnotice> @@ -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 @@ -41,387 +41,190 @@      <note>        <p>Note that this whole module is experimental, and the representations          used as well as the functionality is likely to change in the future.</p> -      <p>The <c>instrument</c> module interface was slightly changed in -        Erlang/OTP R9C.</p>      </note> -    <p>To start an Erlang runtime system with instrumentation, use the -      <c>+Mi*</c> set of command-line arguments to the <c>erl</c> command (see -      the erts_alloc(3) and erl(1) man pages).</p> -    <p>The basic object of study in the case of memory allocation is a memory -      allocation map. A memory allocation map contains a list of descriptors -      for each allocated memory block. Currently, a descriptor is a 4-tuple</p> -    <pre> -        {TypeNo, Address, Size, PidDesc}    </pre> -    <p>where <c>TypeNo</c> is the memory block type number, <c>Address</c> -      is its place in memory, and <c>Size</c> is its size, in bytes. -      <c>PidDesc</c> is either a tuple <c>{X,Y,Z}</c> identifying the -      process which was executing when the block was allocated, or -      <c>undefined</c> if no process was executing. The pid tuple -      <c>{X,Y,Z}</c> can be transformed into a real pid by usage of the -      <c>c:pid/3</c> function.</p> -    <p>Various details about memory allocation:</p> -    <p>Memory blocks are allocated both on the heap segment and on other memory -      segments. This can cause the instrumentation functionality to report -      very large holes. Currently the instrumentation functionality doesn't -      provide any support for distinguishing between holes between memory -      segments, and holes between allocated blocks inside memory segments. -      The current size of the process cannot be obtained from within Erlang, -      but can be seen with one of the system statistics tools, e.g., -      <c>ps</c> or <c>top</c>. The Solaris utility <c>pmap</c> can be -      useful. It reports currently mapped memory segments. </p> -    <p>Overhead for instrumentation: When the emulator has been started with -      the <seealso marker="erts:erts_alloc#Mim">"+Mim true"</seealso> -      flag, each block is preceded by a 24 bytes large -      header on a 32-bit machine and a 48 bytes large header on a 64-bit -      machine. When the emulator has been started with the -      <seealso marker="erts:erts_alloc#Mis">"+Mis true"</seealso> -      flag, each block is preceded by an 8 bytes large header. These are the header -      sizes used by the Erlang 5.3/OTP R9C emulator. Other versions of the -      emulator may use other header sizes. The function -      <seealso marker="#block_header_size/1">block_header_size/1</seealso> -      can be used for retrieving the header size used for a specific memory -      allocation map. The time overhead for managing the instrumentation -      data is small.</p> -    <p>Sizes presented by the instrumentation functionality are (by the -      emulator) requested sizes, i.e. neither instrumentation headers nor -      headers used by allocators are included.</p>    </description> +  <datatypes> +    <datatype> +      <name name="block_histogram"/> +      <desc> +        <p>A histogram of block sizes where each interval's upper bound is  +          twice as high as the one before it.</p> +        <p>The upper bound of the first interval is provided by the function +           that returned the histogram, and the last interval has no upper +           bound.</p> +      </desc> +    </datatype> +    <datatype> +      <name name="allocation_summary"/> +      <desc> +        <p>A summary of allocated block sizes (including their headers) grouped +          by their <c><anno>Origin</anno></c> and <c><anno>Type</anno></c>.</p> +        <p><c><anno>Origin</anno></c> is generally which NIF or driver that +          allocated the blocks, or 'system' if it could not be determined.</p> +        <p><c><anno>Type</anno></c> is the allocation category that the blocks +          belong to, e.g. <c>db_term</c>, <c>message</c> or <c>binary</c>.</p> +        <p>If one or more carriers could not be scanned in full without harming +          the responsiveness of the system, <c><anno>UnscannedSize</anno></c> +          is the number of bytes that had to be skipped.</p> +       </desc> +    </datatype> +    <datatype> +      <name name="carrier_info_list"/> +      <desc> +        <p><c><anno>AllocatorType</anno></c> is the type of the allocator that +          employs this carrier.</p> +        <p><c><anno>TotalSize</anno></c> is the total size of the carrier, +          including its header.</p> +        <p><c><anno>AllocatedSize</anno></c> is the combined size of the +          carrier's allocated blocks, including their headers.</p> +        <p><c><anno>AllocatedCount</anno></c> is the number of allocated +          blocks in the carrier.</p> +        <p><c><anno>InPool</anno></c> is whether the carrier is in the +          migration pool.</p> +        <p><c><anno>FreeBlocks</anno></c> is a histogram of the free block +          sizes in the carrier.</p> +        <p>If the carrier could not be scanned in full without harming the +          responsiveness of the system, <c><anno>UnscannedSize</anno></c> is +          the number of bytes that had to be skipped.</p> +       </desc> +    </datatype> +  </datatypes>    <funcs> +      <func> -      <name>allocator_descr(MemoryData, TypeNo) -> AllocDescr | invalid_type | "unknown"</name> -      <fsummary>Returns a allocator description</fsummary> -      <type> -        <v>MemoryData = {term(), AllocList}</v> -        <v>AllocList = [Desc]</v> -        <v>Desc = {int(), int(), int(), PidDesc}</v> -        <v>PidDesc = {int(), int(), int()} | undefined</v> -        <v>TypeNo = int()</v> -        <v>AllocDescr = atom() | string()</v> -      </type> -      <desc> -        <p>Returns the allocator description of the allocator that -          manages memory blocks of type number <c>TypeNo</c> used in -          <c>MemoryData</c>. -          Valid <c>TypeNo</c>s are in the range returned by -          <seealso marker="#type_no_range/1">type_no_range/1</seealso> on -          this specific memory allocation map. If <c>TypeNo</c> is an -          invalid integer, <c>invalid_type</c> is returned.</p> -      </desc> -    </func> -    <func> -      <name>block_header_size(MemoryData) -> int()</name> -      <fsummary>Returns the memory block header size used by the emulator that generated the memory allocation map</fsummary> -      <type> -        <v>MemoryData = {term(), AllocList}</v> -        <v>AllocList = [Desc]</v> -        <v>Desc = {int(), int(), int(), PidDesc}</v> -        <v>PidDesc = {int(), int(), int()} | undefined</v> -      </type> -      <desc> -        <marker id="block_header_size_1"></marker> -        <p>Returns the memory block header size used by the -          emulator that generated the memory allocation map. The block -          header size may differ between different emulators.</p> -      </desc> -    </func> -    <func> -      <name>class_descr(MemoryData, TypeNo) -> ClassDescr | invalid_type | "unknown"</name> -      <fsummary>Returns a allocator description</fsummary> -      <type> -        <v>MemoryData = {term(), AllocList}</v> -        <v>AllocList = [Desc]</v> -        <v>Desc = {int(), int(), int(), PidDesc}</v> -        <v>PidDesc = {int(), int(), int()} | undefined</v> -        <v>TypeNo = int()</v> -        <v>ClassDescr = atom() | string()</v> -      </type> -      <desc> -        <p>Returns the class description of the class that -          the type number <c>TypeNo</c> used in <c>MemoryData</c> belongs -          to. -          Valid <c>TypeNo</c>s are in the range returned by -          <seealso marker="#type_no_range/1">type_no_range/1</seealso> on -          this specific memory allocation map. If <c>TypeNo</c> is an -          invalid integer, <c>invalid_type</c> is returned.</p> -      </desc> -    </func> -    <func> -      <name>descr(MemoryData) -> DescrMemoryData</name> -      <fsummary>Replace type numbers in memory allocation map with type descriptions</fsummary> -      <type> -        <v>MemoryData = {term(), AllocList}</v> -        <v>AllocList = [Desc]</v> -        <v>Desc = {int(), int(), int(), PidDesc}</v> -        <v>PidDesc = {int(), int(), int()} | undefined</v> -        <v>DescrMemoryData = {term(), DescrAllocList}</v> -        <v>DescrAllocList = [DescrDesc]</v> -        <v>DescrDesc = {TypeDescr, int(), int(), DescrPidDesc}</v> -        <v>TypeDescr = atom() | string()</v> -        <v>DescrPidDesc = pid() | undefined</v> -      </type> -      <desc> -        <p>Returns a memory allocation map where the type numbers (first -          element of <c>Desc</c>) have been replaced by type descriptions, -          and pid tuples (fourth element of <c>Desc</c>) have been -          replaced by real pids.</p> -      </desc> -    </func> -    <func> -      <name>holes(MemoryData) -> ok</name> -      <fsummary>Print out the sizes of unused memory blocks</fsummary> -      <type> -        <v>MemoryData = {term(), AllocList}</v> -        <v>AllocList = [Desc]</v> -        <v>Desc = {int(), int(), int(), PidDesc}</v> -        <v>PidDesc = {int(), int(), int()} | undefined</v> -      </type> -      <desc> -        <p>Prints out the size of each hole (i.e., the space between -          allocated blocks) on the terminal. <em>NOTE:</em> Really large holes -          are probably holes between memory segments. -          The memory allocation map has to be sorted (see -          <seealso marker="#sort/1">sort/1</seealso>).</p> -      </desc> -    </func> -    <func> -      <name>mem_limits(MemoryData) -> {Low, High}</name> -      <fsummary>Return lowest and highest memory address used</fsummary> -      <type> -        <v>MemoryData = {term(), AllocList}</v> -        <v>AllocList = [Desc]</v> -        <v>Desc = {int(), int(), int(), PidDesc}</v> -        <v>PidDesc = {int(), int(), int()} | undefined</v> -        <v>Low = High = int()</v> -      </type> -      <desc> -        <p>Returns a tuple <c>{Low, High}</c> indicating -          the lowest and highest address used. -          The memory allocation map has to be sorted (see -          <seealso marker="#sort/1">sort/1</seealso>).</p> -      </desc> -    </func> -    <func> -      <name>memory_data() -> MemoryData | false</name> -      <fsummary>Return the current memory allocation map</fsummary> -      <type> -        <v>MemoryData = {term(), AllocList}</v> -        <v>AllocList = [Desc]</v> -        <v>Desc = {int(), int(), int(), PidDesc}</v> -        <v>PidDesc = {int(), int(), int()} | undefined</v> -      </type> -      <desc> -        <p>Returns <c>MemoryData</c> (a the memory allocation map) -          if the emulator has been started with the "<c>+Mim true</c>" -          command-line argument; otherwise, <c>false</c>. <em>NOTE:</em><c>memory_data/0</c> blocks execution of other processes while -          the data is collected. The time it takes to collect the data can -          be substantial.</p> -      </desc> -    </func> -    <func> -      <name>memory_status(StatusType) -> [StatusInfo] | false</name> -      <fsummary>Return current memory allocation status</fsummary> -      <type> -        <v>StatusType = total | allocators | classes | types</v> -        <v>StatusInfo = {About, [Info]}</v> -        <v>About = atom()</v> -        <v>Info = {InfoName, Current, MaxSinceLast, MaxEver}</v> -        <v>InfoName = sizes|blocks</v> -        <v>Current = int()</v> -        <v>MaxSinceLast = int()</v> -        <v>MaxEver = int()</v> -      </type> -      <desc> -        <p>Returns a list of <c>StatusInfo</c> if the emulator has been -          started with the "<c>+Mis true</c>" or "<c>+Mim true</c>" -          command-line argument; otherwise, <c>false</c>. </p> -        <p>See the -          <seealso marker="#read_memory_status/1">read_memory_status/1</seealso> -          function for a description of the <c>StatusInfo</c> term.</p> -      </desc> -    </func> -    <func> -      <name>read_memory_data(File) -> MemoryData | {error, Reason}</name> -      <fsummary>Read memory allocation map</fsummary> -      <type> -        <v>File = string()</v> -        <v>MemoryData = {term(), AllocList}</v> -        <v>AllocList = [Desc]</v> -        <v>Desc = {int(), int(), int(), PidDesc}</v> -        <v>PidDesc = {int(), int(), int()} | undefined</v> -      </type> +      <name name="allocations" arity="0" since="OTP 21.0"/> +      <fsummary>Return a summary of all allocations in the system.</fsummary>        <desc> -        <marker id="read_memory_data_1"></marker> -        <p>Reads a memory allocation map from the file <c>File</c> and -          returns it. The file is assumed to have been created by -          <c>store_memory_data/1</c>. The error codes are the same as for -          <c>file:consult/1</c>.</p> +        <p>Shorthand for +          <seealso marker="#allocations/1"><c>allocations(#{})</c>.</seealso></p>        </desc>      </func> +      <func> -      <name>read_memory_status(File) -> MemoryStatus | {error, Reason}</name> -      <fsummary>Read memory allocation status from a file</fsummary> -      <type> -        <v>File = string()</v> -        <v>MemoryStatus = [{StatusType, [StatusInfo]}]</v> -        <v>StatusType = total | allocators | classes | types</v> -        <v>StatusInfo = {About, [Info]}</v> -        <v>About = atom()</v> -        <v>Info = {InfoName, Current, MaxSinceLast, MaxEver}</v> -        <v>InfoName = sizes|blocks</v> -        <v>Current = int()</v> -        <v>MaxSinceLast = int()</v> -        <v>MaxEver = int()</v> -      </type> -      <desc> -        <marker id="read_memory_status_1"></marker> -        <p>Reads memory allocation status from the file <c>File</c> and -          returns it. The file is assumed to have been created by -          <c>store_memory_status/1</c>. The error codes are the same as -          for <c>file:consult/1</c>.</p> -        <p>When <c>StatusType</c> is <c>allocators</c>, <c>About</c> is -          the allocator that the information is about. When -          <c>StatusType</c> is <c>types</c>, <c>About</c> is -          the memory block type that the information is about. Memory -          block types are not described other than by their name and may -          vary between emulators. When <c>StatusType</c> is <c>classes</c>, -          <c>About</c> is the memory block type class that information is -          presented about. Memory block types are classified after their -          use. Currently the following classes exist:</p> +      <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> +        <p>Returns a summary of all tagged allocations in the system, +          optionally filtered by allocator type and scheduler id.</p> +        <p>Only binaries and allocations made by NIFs and drivers are tagged by +          default, but this can be configured an a per-allocator basis with the +          <seealso marker="erts:erts_alloc#M_atags"><c>+M<S>atags</c> +          </seealso> emulator option.</p> +        <p>If tagged allocations are not enabled on any of the specified +          allocator types, the call will fail with +          <c>{error, not_enabled}</c>.</p> +        <p>The following options can be used:</p>          <taglist> -          <tag><c>process_data</c></tag> -          <item>Erlang process specific data.</item> -          <tag><c>binary_data</c></tag> -          <item>Erlang binaries.</item> -          <tag><c>atom_data</c></tag> -          <item>Erlang atoms.</item> -          <tag><c>code_data</c></tag> -          <item>Erlang code.</item> -          <tag><c>system_data</c></tag> -          <item>Other data used by the system</item> +          <tag><c>allocator_types</c></tag> +          <item> +            <p>The allocator types that will be searched. Defaults to all +              <c>alloc_util</c> allocators.</p> +          </item> +          <tag><c>scheduler_ids</c></tag> +          <item> +            <p>The scheduler ids whose allocator instances will be searched. A +              scheduler id of 0 will refer to the global instance that is not +              tied to any particular scheduler. Defaults to all schedulers and +              the global instance.</p> +          </item> +          <tag><c>histogram_start</c></tag> +          <item> +            <p>The upper bound of the first interval in the allocated block +              size histograms. Defaults to 128.</p> +          </item> +          <tag><c>histogram_width</c></tag> +          <item> +            <p>The number of intervals in the allocated block size histograms. +              Defaults to 18.</p> +          </item>          </taglist> -        <p>When <c>InfoName</c> is <c>sizes</c>, <c>Current</c>, -          <c>MaxSinceLast</c>, and <c>MaxEver</c> are, respectively, current -          size, maximum size since last call to -          <c>store_memory_status/1</c> or <c>memory_status/1</c> with the -          specific <c>StatusType</c>, and maximum size since the emulator -          was started. When <c>InfoName</c> is <c>blocks</c>, <c>Current</c>, -          <c>MaxSinceLast</c>, and <c>MaxEver</c> are, respectively, current -          number of blocks, maximum number of blocks since last call to -          <c>store_memory_status/1</c> or <c>memory_status/1</c> with the -          specific <c>StatusType</c>, and maximum number of blocks since the -          emulator was started. </p> -        <p><em>NOTE:</em>A memory block is accounted for at -          "the first level" allocator. E.g. <c>fix_alloc</c> allocates its -          memory pools via <c>ll_alloc</c>. When a <c>fix_alloc</c> block -          is allocated, neither the block nor the pool in which it resides -          are accounted for as memory allocated via <c>ll_alloc</c> even -          though it is.</p> -      </desc> -    </func> -    <func> -      <name>sort(MemoryData) -> MemoryData</name> -      <fsummary>Sort the memory allocation list</fsummary> -      <type> -        <v>MemoryData = {term(), AllocList}</v> -        <v>AllocList = [Desc]</v> -        <v>Desc = {int(), int(), int(), PidDesc}</v> -        <v>PidDesc = {int(), int(), int()} | undefined</v> -      </type> -      <desc> -        <marker id="sort_1"></marker> -        <p>Sorts a memory allocation map so that the addresses are in -          ascending order.</p> -      </desc> -    </func> -    <func> -      <name>store_memory_data(File) -> true|false</name> -      <fsummary>Store the current memory allocation map on a file</fsummary> -      <type> -        <v>File = string()</v> -      </type> -      <desc> -        <p>Stores the current memory allocation map on the file -          <c>File</c>. Returns <c>true</c> if the emulator has been -          started with the "<c>+Mim true</c>" command-line argument, and -          the map was successfully stored; otherwise, <c>false</c>. The -          contents of the file can later be read using -          <seealso marker="#read_memory_data/1">read_memory_data/1</seealso>. -          <em>NOTE:</em><c>store_memory_data/0</c> blocks execution of -          other processes while the data is collected. The time it takes -          to collect the data can be substantial.</p> -      </desc> -    </func> -    <func> -      <name>store_memory_status(File) -> true|false</name> -      <fsummary>Store the current memory allocation status on a file</fsummary> -      <type> -        <v>File = string()</v> -      </type> -      <desc> -        <p>Stores the current memory status on the file -          <c>File</c>. Returns <c>true</c> if the emulator has been -          started with the "<c>+Mis true</c>", or "<c>+Mim true</c>" -          command-line arguments, and the data was successfully stored; -          otherwise, <c>false</c>. The contents of the file can later be -          read using -          <seealso marker="#read_memory_status/1">read_memory_status/1</seealso>.</p> -      </desc> -    </func> -    <func> -      <name>sum_blocks(MemoryData) -> int()</name> -      <fsummary>Return the total amount of memory used</fsummary> -      <type> -        <v>MemoryData = {term(), AllocList}</v> -        <v>AllocList = [Desc]</v> -        <v>Desc = {int(), int(), int(), PidDesc}</v> -        <v>PidDesc = {int(), int(), int()} | undefined</v> -      </type> -      <desc> -        <p>Returns the total size of the memory blocks in the list.</p> +        <p><em>Example:</em></p> +        <code type="none"><![CDATA[ +> instrument:allocations(#{ histogram_start => 128, histogram_width => 15 }). +{ok,{128,0, +     #{udp_inet => +           #{driver_event_state => {0,0,0,0,0,0,0,0,0,1,0,0,0,0,0}}, +       system => +           #{heap => {0,0,0,0,20,4,2,2,2,3,0,1,0,0,1}, +             db_term => {271,3,1,52,80,1,0,0,0,0,0,0,0,0,0}, +             code => {0,0,0,5,3,6,11,22,19,20,10,2,1,0,0}, +             binary => {18,0,0,0,7,0,0,1,0,0,0,0,0,0,0}, +             message => {0,40,78,2,2,0,0,0,0,0,0,0,0,0,0}, +             ... } +       spawn_forker => +           #{driver_select_data_state => +                 {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}, +       ram_file_drv => #{drv_binary => {0,0,0,0,0,0,1,0,0,0,0,0,0,0,0}}, +       prim_file => +           #{process_specific_data => {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +             nif_trap_export_entry => {0,4,0,0,0,0,0,0,0,0,0,0,0,0,0}, +             monitor_extended => {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, +             drv_binary => {0,0,0,0,0,0,1,0,3,5,0,0,0,1,0}, +             binary => {0,4,0,0,0,0,0,0,0,0,0,0,0,0,0}}, +       prim_buffer => +           #{nif_internal => {0,4,0,0,0,0,0,0,0,0,0,0,0,0,0}, +             binary => {0,4,0,0,0,0,0,0,0,0,0,0,0,0,0}}}}} +     ]]></code>        </desc>      </func> +      <func> -      <name>type_descr(MemoryData, TypeNo) -> TypeDescr | invalid_type</name> -      <fsummary>Returns a type description</fsummary> -      <type> -        <v>MemoryData = {term(), AllocList}</v> -        <v>AllocList = [Desc]</v> -        <v>Desc = {int(), int(), int(), PidDesc}</v> -        <v>PidDesc = {int(), int(), int()} | undefined</v> -        <v>TypeNo = int()</v> -        <v>TypeDescr = atom() | string()</v> -      </type> +      <name name="carriers" arity="0" since="OTP 21.0"/> +      <fsummary>Return a list of all carriers in the system.</fsummary>        <desc> -        <p>Returns the type description of a type number used in -          <c>MemoryData</c>. -          Valid <c>TypeNo</c>s are in the range returned by -          <seealso marker="#type_no_range/1">type_no_range/1</seealso> on -          this specific memory allocation map. If <c>TypeNo</c> is an -          invalid integer, <c>invalid_type</c> is returned.</p> +        <p>Shorthand for +          <seealso marker="#carriers/1"><c>carriers(#{})</c>.</seealso></p>        </desc>      </func> +      <func> -      <name>type_no_range(MemoryData) -> {Min, Max}</name> -      <fsummary>Returns the memory block type numbers</fsummary> -      <type> -        <v>MemoryData = {term(), AllocList}</v> -        <v>AllocList = [Desc]</v> -        <v>Desc = {int(), int(), int(), PidDesc}</v> -        <v>PidDesc = {int(), int(), int()} | undefined</v> -        <v>Min = int()</v> -        <v>Max = int()</v> -      </type> +      <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> -        <marker id="type_no_range_1"></marker> -        <p>Returns the memory block type number range used in -          <c>MemoryData</c>. When the memory allocation map was generated -          by an Erlang 5.3/OTP R9C or newer emulator, all integers <c>T</c> -          that satisfy <c>Min</c> <= <c>T</c> <= <c>Max</c> are -          valid type numbers. When the memory allocation map was generated -          by a pre Erlang 5.3/OTP R9C emulator, all integers in the -          range are <em>not</em> valid type numbers.</p> +        <p>Returns a summary of all carriers in the system, optionally filtered +          by allocator type and scheduler id.</p> +        <p>If the specified allocator types are not enabled, the call will fail +          with <c>{error, not_enabled}</c>.</p> +        <p>The following options can be used:</p> +        <taglist> +          <tag><c>allocator_types</c></tag> +          <item> +            <p>The allocator types that will be searched. Defaults to all +              <c>alloc_util</c> allocators.</p> +          </item> +          <tag><c>scheduler_ids</c></tag> +          <item> +            <p>The scheduler ids whose allocator instances will be searched. A +              scheduler id of 0 will refer to the global instance that is not +              tied to any particular scheduler. Defaults to all schedulers and +              the global instance.</p> +          </item> +          <tag><c>histogram_start</c></tag> +          <item> +            <p>The upper bound of the first interval in the free block size +              histograms. Defaults to 512.</p> +          </item> +          <tag><c>histogram_width</c></tag> +          <item> +            <p>The number of intervals in the free block size histograms. +              Defaults to 14.</p> +          </item> +        </taglist> +        <p><em>Example:</em></p> +        <code type="none"><![CDATA[ +> instrument:carriers(#{ histogram_start => 512, histogram_width => 8 }). +{ok,{512, +     [{ll_alloc,1048576,0,1048344,71,false,{0,0,0,0,0,0,0,0}}, +      {binary_alloc,1048576,0,324640,13,false,{3,0,0,1,0,0,0,2}}, +      {eheap_alloc,2097152,0,1037200,45,false,{2,1,1,3,4,3,2,2}}, +      {fix_alloc,32768,0,29544,82,false,{22,0,0,0,0,0,0,0}}, +      {...}|...]}} +     ]]></code>        </desc>      </func> +    </funcs>    <section> diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml index 0c24375b91..1d434decfc 100644 --- a/lib/tools/doc/src/lcnt.xml +++ b/lib/tools/doc/src/lcnt.xml @@ -5,7 +5,7 @@      <header>  	<copyright>  	    <year>2009</year> -	    <year>2017</year> +	    <year>2018</year>  	    <holder>Ericsson AB, All Rights Reserved</holder>  	</copyright>  	<legalnotice> @@ -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/notes.xml b/lib/tools/doc/src/notes.xml index 45f276c09e..a6781dfdb3 100644 --- a/lib/tools/doc/src/notes.xml +++ b/lib/tools/doc/src/notes.xml @@ -4,7 +4,7 @@  <chapter>    <header>      <copyright> -      <year>2004</year><year>2017</year> +      <year>2004</year><year>2018</year>        <holder>Ericsson AB. All Rights Reserved.</holder>      </copyright>      <legalnotice> @@ -31,6 +31,64 @@    </header>    <p>This document describes the changes made to the Tools application.</p> +<section><title>Tools 3.0.2</title> + +    <section><title>Improvements and New Features</title> +      <list> +        <item> +          <p> +	    Remove emacs warnings and added more tests.</p> +          <p> +	    Own Id: OTP-15476</p> +        </item> +      </list> +    </section> + +</section> + +<section><title>Tools 3.0.1</title> + +    <section><title>Improvements and New Features</title> +      <list> +        <item> +          <p> +	    The HTML pages generated by cover:analyse_to_file/1 and +	    related functions is improved for readability.</p> +          <p> +	    Own Id: OTP-15213 Aux Id: PR-1807 </p> +        </item> +        <item> +          <p> +	    Add alignment functionality in emacs.</p> +          <p> +	    Own Id: OTP-15239 Aux Id: PR-1728 </p> +        </item> +      </list> +    </section> + +</section> + +<section><title>Tools 3.0</title> + +    <section><title>Improvements and New Features</title> +      <list> +        <item> +	    <p>Added <c>instrument:allocations</c> and +	    <c>instrument:carriers</c> for retrieving information +	    about memory utilization and fragmentation.</p> +	    <p>The old <c>instrument</c> interface has been removed, +	    as have the related options <c>+Mim</c> and +	    <c>+Mis</c>.</p> +          <p> +	    *** POTENTIAL INCOMPATIBILITY ***</p> +          <p> +	    Own Id: OTP-14961</p> +        </item> +      </list> +    </section> + +</section> +  <section><title>Tools 2.11.2</title>      <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/tools/doc/src/specs.xml b/lib/tools/doc/src/specs.xml new file mode 100644 index 0000000000..0b5b7b171c --- /dev/null +++ b/lib/tools/doc/src/specs.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8" ?> +<specs xmlns:xi="http://www.w3.org/2001/XInclude"> +  <xi:include href="../specs/specs_fprof.xml"/> +  <xi:include href="../specs/specs_make.xml"/> +  <xi:include href="../specs/specs_lcnt.xml"/> +  <xi:include href="../specs/specs_eprof.xml"/> +  <xi:include href="../specs/specs_tags.xml"/> +  <xi:include href="../specs/specs_cover.xml"/> +  <xi:include href="../specs/specs_xref.xml"/> +  <xi:include href="../specs/specs_instrument.xml"/> +  <xi:include href="../specs/specs_erlang_mode.xml"/> +</specs> 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/emacs/Makefile b/lib/tools/emacs/Makefile index ea4d6cb723..b7775d1c8c 100644 --- a/lib/tools/emacs/Makefile +++ b/lib/tools/emacs/Makefile @@ -46,6 +46,7 @@ EMACS_FILES= \  	erlang-eunit \  	erlang-edoc \  	erlang-flymake \ +	erlang-test \  	erlang  README_FILES= README diff --git a/lib/tools/emacs/erlang-edoc.el b/lib/tools/emacs/erlang-edoc.el index d0dcc81028..ea1e263faf 100644 --- a/lib/tools/emacs/erlang-edoc.el +++ b/lib/tools/emacs/erlang-edoc.el @@ -28,6 +28,7 @@  (defcustom erlang-edoc-indent-level 2    "Indentation level of xhtml in Erlang edoc." +  :type '(integer)    :safe 'integerp    :group 'erlang) diff --git a/lib/tools/emacs/erlang-eunit.el b/lib/tools/emacs/erlang-eunit.el index 38c40927f4..53543d7b01 100644 --- a/lib/tools/emacs/erlang-eunit.el +++ b/lib/tools/emacs/erlang-eunit.el @@ -23,6 +23,7 @@  (eval-when-compile    (require 'cl)) +(require 'erlang)  (defvar erlang-eunit-src-candidate-dirs '("../src" ".")    "*Name of directories which to search for source files matching @@ -331,8 +332,7 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."               t)             (apply test-fun test-args)             (if under-cover -               (save-excursion -                 (set-buffer (find-file-noselect src-filename)) +               (with-current-buffer (find-file-noselect src-filename)                   (erlang-eunit-analyze-coverage)))))))  (defun erlang-eunit-compile-and-run-module-tests-under-cover () @@ -348,8 +348,7 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."  (defun erlang-eunit-compile-file (file-path &optional under-cover)    (if (file-readable-p file-path) -      (save-excursion -        (set-buffer (find-file-noselect file-path)) +      (with-current-buffer (find-file-noselect file-path)          ;; In order to run a code coverage analysis on a          ;; module, we have two options:          ;; @@ -376,8 +375,7 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."        (error msg))))  (defun erlang-eunit-last-compilation-successful-p () -  (save-excursion -    (set-buffer inferior-erlang-buffer) +  (with-current-buffer inferior-erlang-buffer      (goto-char compilation-parsing-end)      (erlang-eunit-all-list-elems-fulfill-p       (lambda (re) (let ((continue t) diff --git a/lib/tools/emacs/erlang-pkg.el b/lib/tools/emacs/erlang-pkg.el index 02d6bebbf4..7e95e4050e 100644 --- a/lib/tools/emacs/erlang-pkg.el +++ b/lib/tools/emacs/erlang-pkg.el @@ -1,3 +1,6 @@  (define-package "erlang" "2.7.0"    "Erlang major mode"    '((emacs "24.1"))) +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/lib/tools/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el index 534f50ab33..3ebc6e8e1e 100644 --- a/lib/tools/emacs/erlang-skels.el +++ b/lib/tools/emacs/erlang-skels.el @@ -1985,7 +1985,7 @@ configured off."  The first character of DD is space if the value is less than 10."    (let ((date (current-time-string)))      (format "%2d %s %s" -            (string-to-int (substring date 8 10)) +            (string-to-number (substring date 8 10))              (substring date 4 7)              (substring date -4)))) diff --git a/lib/tools/emacs/erlang-test.el b/lib/tools/emacs/erlang-test.el index efe3d515e9..2ee584d11a 100644 --- a/lib/tools/emacs/erlang-test.el +++ b/lib/tools/emacs/erlang-test.el @@ -29,7 +29,7 @@  ;; This library require GNU Emacs 25 or later.  ;; -;; There are two ways to run emacs unit tests. +;; There are three ways to run the erlang emacs unit tests.  ;;  ;; 1. Within a running emacs process.  Load this file.  Then to run  ;; all defined test cases: @@ -49,11 +49,15 @@  ;;  ;; The -L option adds a directory to the load-path.  It should be the  ;; directory containing erlang.el and erlang-test.el. +;; +;; 3. Call the script test-erlang-mode in this directory.  This script +;; use the second method.  ;;; Code: +(eval-when-compile +  (require 'cl))  (require 'ert) -(require 'cl-lib)  (require 'erlang)  (defvar erlang-test-code @@ -63,7 +67,7 @@      ("SYMBOL" . "-define(SYMBOL, value).")      ("MACRO" . "-define(MACRO(X), X + X).")      ("struct" . "-record(struct, {until,maps,are,everywhere}).") -    ("function". "function() -> #struct{}.")) +    ("function" . "function() -> #struct{}."))    "Alist of erlang test code.  Each entry have the format (TAGNAME . ERLANG_CODE).  If TAGNAME  is nil there is no definitions in the ERLANG_CODE.  The @@ -116,8 +120,8 @@ concatenated to form an erlang file to test on.")  (defun erlang-test-create-erlang-file (erlang-file)    (with-temp-file erlang-file -    (cl-loop for (_ . code) in erlang-test-code -             do (insert code "\n")))) +    (loop for (_ . code) in erlang-test-code +          do (insert code "\n"))))  (defun erlang-test-compile-tags (erlang-file tags-file)    (should (zerop (call-process "etags" nil nil nil @@ -132,19 +136,20 @@ concatenated to form an erlang file to test on.")                   (sort (erlang-expected-completion-table) #'string-lessp))))  (defun erlang-expected-completion-table () -  (append (cl-loop for (symbol . _) in erlang-test-code -                   when (stringp symbol) -                   append (list symbol (concat "erlang_test:" symbol))) +  (append (loop for (symbol . _) in erlang-test-code +                when (stringp symbol) +                append (list symbol (concat "erlang_test:" symbol)))            (list "erlang_test:" "erlang_test:module_info")))  (defun erlang-test-xref-find-definitions (erlang-file erlang-buffer) -  (cl-loop for (tagname . code) in erlang-test-code -           for line = 1 then (1+ line) -           do (when tagname -                (switch-to-buffer erlang-buffer) -                (erlang-test-xref-jump tagname erlang-file line) -                (erlang-test-xref-jump (concat "erlang_test:" tagname) -                                       erlang-file line))) +  (loop for (tagname . code) in erlang-test-code +        for line = 1 then (1+ line) +        do (when tagname +             (switch-to-buffer erlang-buffer) +             (erlang-test-xref-jump tagname erlang-file line) +             (when (string-equal tagname "function") +               (erlang-test-xref-jump (concat "erlang_test:" tagname) +                                      erlang-file line))))    (erlang-test-xref-jump "erlang_test:" erlang-file 1))  (defun erlang-test-xref-jump (id expected-file expected-line) @@ -213,27 +218,27 @@ concatenated to form an erlang file to test on.")  (ert-deftest erlang-test-parse-id () -  (cl-loop for id-string in '("fun/10" -                              "qualified-function module:fun/10" -                              "record reko" -                              "macro _SYMBOL" -                              "macro MACRO/10" -                              "module modula" -                              "macro" -                              nil) -           for id-list in '((nil nil "fun" 10) -                            (qualified-function "module" "fun" 10) -                            (record nil "reko" nil) -                            (macro nil "_SYMBOL" nil) -                            (macro nil "MACRO" 10) -                            (module nil "modula" nil) -                            (nil nil "macro" nil) -                            nil) -           for id-list2 = (erlang-id-to-list id-string) -           do (should (equal id-list id-list2)) -           for id-string2 = (erlang-id-to-string id-list) -           do (should (equal id-string id-string2)) -           collect id-list2)) +  (loop for id-string in '("fun/10" +                           "qualified-function module:fun/10" +                           "record reko" +                           "macro _SYMBOL" +                           "macro MACRO/10" +                           "module modula" +                           "macro" +                           nil) +        for id-list in '((nil nil "fun" 10) +                         (qualified-function "module" "fun" 10) +                         (record nil "reko" nil) +                         (macro nil "_SYMBOL" nil) +                         (macro nil "MACRO" 10) +                         (module nil "modula" nil) +                         (nil nil "macro" nil) +                         nil) +        for id-list2 = (erlang-id-to-list id-string) +        do (should (equal id-list id-list2)) +        for id-string2 = (erlang-id-to-string id-list) +        do (should (equal id-string id-string2)) +        collect id-list2))  (provide 'erlang-test) diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index e4166053d5..3cbe9daa60 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -9,7 +9,7 @@  ;; %CopyrightBegin%  ;; -;; Copyright Ericsson AB 1996-2017. All Rights Reserved. +;; Copyright Ericsson AB 1996-2018. All Rights Reserved.  ;;  ;; Licensed under the Apache License, Version 2.0 (the "License");  ;; you may not use this file except in compliance with the License. @@ -77,6 +77,9 @@  ;;; Code:  (eval-when-compile (require 'cl)) +(require 'align) +(require 'comint) +(require 'tempo)  ;; Variables: @@ -333,6 +336,7 @@ when a new function header is generated.  When nil, no blank line is  inserted between the current line and the new header.  When bound to a  number it represents the number of blank lines which should be  inserted." +  :type '(restricted-sexp :match-alternatives (integerp 'nil))    :group 'erlang)  (defvar erlang-electric-semicolon-criteria @@ -804,6 +808,7 @@ resulting regexp is surrounded by \\_< and \\_>."        "is_integer"        "is_list"        "is_map" +      "is_map_key"        "is_number"        "is_pid"        "is_port" @@ -825,6 +830,7 @@ resulting regexp is surrounded by \\_< and \\_>."        "list_to_tuple"        "load_module"        "make_ref" +      "map_get"        "map_size"        "max"        "min" @@ -884,8 +890,6 @@ resulting regexp is surrounded by \\_< and \\_>."        "alloc_sizes"        "append"        "append_element" -      "await_proc_exit" -      "await_sched_wall_time_modifications"        "bump_reductions"        "call_on_load_function"        "cancel_timer" @@ -895,16 +899,15 @@ resulting regexp is surrounded by \\_< and \\_>."        "decode_packet"        "delay_trap"        "delete_element" -      "dexit" -      "dgroup_leader"        "display"        "display_nl"        "display_string" -      "dist_exit" -      "dlink" +      "dist_get_stat" +      "dist_ctrl_get_data" +      "dist_ctrl_get_data_notification" +      "dist_ctrl_input_handler" +      "dist_ctrl_put_data"        "dmonitor_node" -      "dmonitor_p" -      "dsend"        "dt_append_vm_tag_data"        "dt_get_tag"        "dt_get_tag_data" @@ -912,8 +915,8 @@ resulting regexp is surrounded by \\_< and \\_>."        "dt_put_tag"        "dt_restore_tag"        "dt_spread_tag" -      "dunlink"        "convert_time_unit" +      "exit_signal"        "external_size"        "finish_after_on_load"        "finish_loading" @@ -924,7 +927,6 @@ resulting regexp is surrounded by \\_< and \\_>."        "function_exported"        "garbage_collect_message_area"        "gather_gc_info_result" -      "gather_sched_wall_time_result"        "get_cookie"        "get_module_info"        "get_stacktrace" @@ -1407,6 +1409,19 @@ Other commands:      (add-function :before-until (local 'eldoc-documentation-function)                    #'erldoc-eldoc-function))    (run-hooks 'erlang-mode-hook) + +  ;; Align maps. +  (add-to-list 'align-rules-list +               '(erlang-maps +                 (regexp  . "\\(\\s-*\\)\\(=>\\)\\s-*") +                 (modes   . '(erlang-mode)) +                 (repeat  . t))) +  ;; Align records and :: specs +  (add-to-list 'align-rules-list +               '(erlang-record-specs +                 (regexp  . "\\(\\s-*\\)\\(=\\).*\\(::\\)*\\s-*") +                 (modes   . '(erlang-mode)) +                 (repeat  . t)))    (if (zerop (buffer-size))        (run-hooks 'erlang-new-file-hook))) @@ -1699,10 +1714,10 @@ Personal extensions could be added to `erlang-menu-personal-items'.  This function should be called if any variable describing the  menu configuration is changed." -  (erlang-menu-install "Erlang" erlang-menu-items erlang-mode-map t)) +  (erlang-menu-install "Erlang" erlang-menu-items erlang-mode-map)) -(defun erlang-menu-install (name items keymap &optional popup) +(defun erlang-menu-install (name items keymap)    "Install a menu in Emacs based on an abstract description.  NAME is the name of the menu. @@ -3682,16 +3697,17 @@ retried without regard to module.  4. Arity - Integer in case of functions and macros if the number  of arguments could be found, otherwise nil."    (save-excursion -    (save-match-data -      (if (eq (char-syntax (following-char)) ? ) -          (skip-chars-backward " \t")) -      (skip-chars-backward "[:word:]_:'") -      (cond ((looking-at erlang-module-function-regexp) -             (erlang-get-qualified-function-id-at-point)) -            ((looking-at (concat erlang-atom-regexp ":")) -             (erlang-get-module-id-at-point)) -            ((looking-at erlang-name-regexp) -             (erlang-get-some-other-id-at-point)))))) +    (let (case-fold-search) +      (save-match-data +        (if (eq (char-syntax (following-char)) ? ) +            (skip-chars-backward " \t")) +        (skip-chars-backward "[:word:]_:'") +        (cond ((looking-at erlang-module-function-regexp) +               (erlang-get-qualified-function-id-at-point)) +              ((looking-at (concat erlang-atom-regexp ":")) +               (erlang-get-module-id-at-point)) +              ((looking-at erlang-name-regexp) +               (erlang-get-some-other-id-at-point)))))))  (defun erlang-get-qualified-function-id-at-point ()    (let ((kind 'qualified-function) @@ -4195,22 +4211,18 @@ Return t if criteria fulfilled, nil otherwise."            nil))))) -(defun erlang-in-literal (&optional lim) +(defun erlang-in-literal ()    "Test if point is in string, quoted atom or comment.  Return one of the three atoms `atom', `string', and `comment'.  Should the point be inside none of the above mentioned types of  context, nil is returned."    (save-excursion -    (let* ((lim (or lim (save-excursion -                          (erlang-beginning-of-clause) -                          (point)))) -           (state (funcall (symbol-function 'syntax-ppss)))) -      (cond -       ((eq (nth 3 state) ?') 'atom) -       ((nth 3 state) 'string) -       ((nth 4 state) 'comment) -       (t nil))))) +    (let ((state (funcall (symbol-function 'syntax-ppss)))) +      (cond ((eq (nth 3 state) ?') 'atom) +            ((nth 3 state) 'string) +            ((nth 4 state) 'comment) +            (t nil)))))  (defun erlang-at-end-of-function-p () @@ -5029,7 +5041,10 @@ considered first when it is time to jump to the definition.")  (defun erlang-visit-tags-table-buffer (cont cbuf)    (if (< emacs-major-version 26)        (visit-tags-table-buffer cont) -    (visit-tags-table-buffer cont cbuf))) +    ;; Remove this with-no-warnings when Emacs 26 is the required +    ;; version minimum. +    (with-no-warnings +      (visit-tags-table-buffer cont cbuf))))  (defun erlang-xref-find-definitions-module-tag (module                                                  tag @@ -5524,7 +5539,7 @@ Return the position after the newly inserted command."      (+ insert-point insert-length))) -(defun inferior-erlang-strip-delete (&optional s) +(defun inferior-erlang-strip-delete (&optional _s)    "Remove `^H' (delete) and the characters it was supposed to remove."    (interactive)    (if (and (boundp 'comint-last-input-end) @@ -5542,7 +5557,7 @@ Return the position after the newly inserted command."  ;; Basically `comint-strip-ctrl-m', with a few extra checks. -(defun inferior-erlang-strip-ctrl-m (&optional string) +(defun inferior-erlang-strip-ctrl-m (&optional _string)    "Strip trailing `^M' characters from the current output group."    (interactive)    (if (and (boundp 'comint-last-input-end) @@ -5579,8 +5594,8 @@ There exists two workarounds for this bug:    (let* ((dir (inferior-erlang-compile-outdir))           (noext (substring (erlang-local-buffer-file-name) 0 -4))           (opts (append (list (cons 'outdir dir)) -                       (if current-prefix-arg -                           (list 'debug_info 'export_all)) +                       (when arg +                         (list 'debug_info 'export_all))                         erlang-compile-extra-opts))           end)      (with-current-buffer inferior-erlang-buffer @@ -5629,7 +5644,6 @@ unless the optional NO-DISPLAY is non-nil."  (defun inferior-erlang-compute-compile-command (module-name opts)    (let ((ccfn erlang-compile-command-function-alist) -        (res (inferior-erlang-compute-erl-compile-command module-name opts))          ccfn-entry          done          result) diff --git a/lib/tools/emacs/erlang_appwiz.el b/lib/tools/emacs/erlang_appwiz.el index ecbce66f47..b71c180739 100644 --- a/lib/tools/emacs/erlang_appwiz.el +++ b/lib/tools/emacs/erlang_appwiz.el @@ -103,6 +103,10 @@  ;;    ;; +(defvar appwiz-erlang-modulename "foo") +(defvar appwiz-erlang-ext "_work") + +  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  ;;  ;; Erlang application wizard @@ -245,13 +249,6 @@ creating the root directory and for naming application files."      (insert "Application specification file for " name ".")      (save-buffer))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; These are setq:ed  -;; - -(defvar appwiz-erlang-modulename "foo") -(defvar appwiz-erlang-ext "_work")  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  ;; @@ -468,7 +465,7 @@ Call the function `erlang-menu-init' after modifying this variable.")  The first character of DD is *not* space if the value is less than 10."    (let ((date (current-time-string)))      (format "%d %s %s" -	    (string-to-int (substring date 8 10)) +	    (string-to-number (substring date 8 10))  	    (substring date 4 7)  	    (substring date -4)))) diff --git a/lib/tools/priv/styles.css b/lib/tools/priv/styles.css new file mode 100644 index 0000000000..84f00be9fd --- /dev/null +++ b/lib/tools/priv/styles.css @@ -0,0 +1,96 @@ +body { +  font: 14px/1.6 "Helvetica Neue", Helvetica, Arial, sans-serif; +  margin: 0; +  padding: 0; +  color: #000; +  border-top: 2px solid #ddd; +  background-color: #fff; + +  min-height: 100%; +  display: flex; +  flex-direction: column; +} + +h1 { +  width: 100%; +  border-bottom: 1px solid #eee; +  margin-bottom: 0; +  font-weight: 100; +  font-size: 1.1em; +  letter-spacing: 1px; +} + +h1 code { +  font-size: 0.96em; +} + +code { +  font: 12px monospace; +} + +footer { +  background: #eee; +  width: 100%; +  padding: 10px 0; +  text-align: right; +  border-top: 1px solid #ddd; +  display: flex; +  flex: 1; +  order: 2; +  justify-content: center; +} + +table { +  width: 100%; +  margin-top: 10px; +  border-collapse: collapse; +  border: 1px solid #cbcbcb; +  color: #000; +  -webkit-border-radius: 3px; +  -moz-border-radius: 3px; +} +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; +} +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; +} +tr.hit td { +  background-color: #e6ffed; +} +td.source { +  padding-left: 15px; +  line-height: 15px; +  white-space: pre; +  font: 12px monospace; +} diff --git a/lib/tools/src/Makefile b/lib/tools/src/Makefile index 032bd612db..cc5bee9a8f 100644 --- a/lib/tools/src/Makefile +++ b/lib/tools/src/Makefile @@ -1,7 +1,7 @@  #  # %CopyrightBegin%  # -# Copyright Ericsson AB 1996-2016. All Rights Reserved. +# Copyright Ericsson AB 1996-2018. All Rights Reserved.  #  # Licensed under the Apache License, Version 2.0 (the "License");  # you may not use this file except in compliance with the License. @@ -72,6 +72,9 @@ APP_TARGET = $(EBIN)/$(APP_FILE)  APPUP_SRC = $(APPUP_FILE).src  APPUP_TARGET = $(EBIN)/$(APPUP_FILE) +PRIVDIR = ../priv +CSS = $(PRIVDIR)/styles.css +  # ----------------------------------------------------  # FLAGS  # ---------------------------------------------------- @@ -110,5 +113,7 @@ release_spec: opt  	$(INSTALL_DIR) "$(RELSYSDIR)/ebin"  	$(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) \  		"$(RELSYSDIR)/ebin" +	$(INSTALL_DIR) "$(RELSYSDIR)/priv" +	$(INSTALL_DATA) $(CSS) "$(RELSYSDIR)/priv"  release_docs_spec: diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index 4e64d7aa4e..8d4561ca9e 100644 --- a/lib/tools/src/cover.erl +++ b/lib/tools/src/cover.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2001-2017. All Rights Reserved. +%% Copyright Ericsson AB 2001-2018. All Rights Reserved.  %%  %% Licensed under the Apache License, Version 2.0 (the "License");  %% you may not use this file except in compliance with the License. @@ -144,6 +144,8 @@          end).  -define(SPAWN_DBG(Tag,Value),put(Tag,Value)). +-define(STYLESHEET, "styles.css"). +-define(TOOLS_APP, tools).  -include_lib("stdlib/include/ms_transform.hrl"). @@ -2415,20 +2417,8 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->  	    case file:open(OutFile, [write,raw,delayed_write]) of  		{ok, OutFd} ->                      Enc = encoding(ErlFile), -		    if HTML ->  -                           Header = -                               ["<!DOCTYPE HTML PUBLIC " -                                "\"-//W3C//DTD HTML 3.2 Final//EN\">\n" -                                "<html>\n" -                                "<head>\n" -                                "<meta http-equiv=\"Content-Type\"" -                                " content=\"text/html; charset=", -                                html_encoding(Enc),"\"/>\n" -                                "<title>",OutFile,"</title>\n" -                                "</head>" -                                "<body style='background-color: white;" -                                " color: black'>\n" -                                "<pre>\n"], +		    if HTML -> +                            Header = create_header(OutFile, Enc),                              H1Bin = unicode:characters_to_binary(Header,Enc,Enc),                              ok = file:write(OutFd,H1Bin);  		       true -> ok @@ -2439,20 +2429,25 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->                     Timestamp =                         io_lib:format("~p-~s-~s at ~s:~s:~s",                                       [Y, -                                      string:right(integer_to_list(Mo), 2, $0), -                                      string:right(integer_to_list(D),  2, $0), -                                      string:right(integer_to_list(H),  2, $0), -                                      string:right(integer_to_list(Mi), 2, $0), -                                      string:right(integer_to_list(S),  2, $0)]), - -                    H2Bin = unicode:characters_to_binary( -                              ["File generated from ",ErlFile," by COVER ", -                                Timestamp,"\n\n" -                                "**************************************" -                                "**************************************" -                                "\n\n"], -                              Enc, Enc), -                    ok = file:write(OutFd, H2Bin), +                                      string:pad(integer_to_list(Mo), 2, leading, $0), +                                      string:pad(integer_to_list(D),  2, leading, $0), +                                      string:pad(integer_to_list(H),  2, leading, $0), +                                      string:pad(integer_to_list(Mi), 2, leading, $0), +                                      string:pad(integer_to_list(S),  2, leading, $0)]), + +                   OutFileInfo = +                       if HTML -> +                            create_footer(ErlFile, Timestamp); +                          true -> +                            ["File generated from ",ErlFile," by COVER ", +                             Timestamp, "\n\n", +                             "**************************************" +                             "**************************************" +                             "\n\n"] +                          end, + +                   H2Bin = unicode:characters_to_binary(OutFileInfo,Enc,Enc), +                   ok = file:write(OutFd, H2Bin),  		    Pattern = {#bump{module=Module,line='$1',_='_'},'$2'},  		    MS = [{Pattern,[{is_integer,'$1'},{'>','$1',0}],[{{'$1','$2'}}]}], @@ -2462,7 +2457,7 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->  		    print_lines(Module, CovLines, InFd, OutFd, 1, HTML),  		    if HTML -> -                           ok = file:write(OutFd, "</pre>\n</body>\n</html>\n"); +                           ok = file:write(OutFd, close_html());  		       true -> ok  		    end, @@ -2497,14 +2492,13 @@ print_lines(Module, CovLines, InFd, OutFd, L, HTML) ->  	    case CovLines of  	       [{L,N}|CovLines1] ->                      if N=:=0, HTML=:=true -> -                           LineNoNL = Line -- "\n", -                           Str = "     0", -                           %%Str = string:right("0", 6, 32), -                           RedLine = ["<font color=red>",Str,fill1(), -                                      LineNoNL,"</font>\n"], -                           ok = file:write(OutFd, RedLine); +                           MissedLine = table_row("miss", Line, L, N), +                           ok = file:write(OutFd, MissedLine); +                       HTML=:=true -> +                           HitLine = table_row("hit", Line, L, N), +                           ok = file:write(OutFd, HitLine);                         N < 1000000 -> -                           Str = string:right(integer_to_list(N), 6, 32), +                           Str = string:pad(integer_to_list(N), 6, leading, $\s),                             ok = file:write(OutFd, [Str,fill1(),Line]);                         N < 10000000 ->                             Str = integer_to_list(N), @@ -2515,7 +2509,11 @@ print_lines(Module, CovLines, InFd, OutFd, L, HTML) ->                      end,  		    print_lines(Module, CovLines1, InFd, OutFd, L+1, HTML);  		_ ->                            %Including comment lines -		    ok = file:write(OutFd, [tab(),Line]), +        NonCoveredContent = +                    if HTML -> table_row(Line, L); +                    true -> [tab(),Line] +                    end, +		    ok = file:write(OutFd, NonCoveredContent),  		    print_lines(Module, CovLines, InFd, OutFd, L+1, HTML)  	    end      end. @@ -2525,6 +2523,61 @@ fill1() ->      "..|  ".  fill2() ->       ".|  ".  fill3() ->        "|  ". +%% HTML sections +create_header(OutFile, Enc) -> +    ["<!doctype html>\n" +    "<html>\n" +    "<head>\n" +    "<meta charset=\"",html_encoding(Enc),"\">\n" +    "<title>",OutFile,"</title>\n" +    "<style>"] ++ +    read_stylesheet() ++ +    ["</style>\n", +    "</head>\n" +    "<body>\n" +    "<h1><code>",OutFile,"</code></h1>\n"]. + +create_footer(ErlFile, Timestamp) -> +    ["<footer><p>File generated from <code>",ErlFile, +    "</code> by <a href=\"http://erlang.org/doc/man/cover.html\">cover</a> at ", +    Timestamp,"</p></footer>\n<table>\n<tbody>\n"]. + +close_html() -> +    ["</tbody>\n", +     "<thead>\n", +     "<tr>\n", +     "<th>Line</th>\n", +     "<th>Hits</th>\n", +     "<th>Source</th>\n", +     "</tr>\n", +     "</thead>\n", +     "</table>\n", +     "</body>\n" +     "</html>\n"]. + +table_row(CssClass, Line, L, N) -> +    ["<tr class=\"",CssClass,"\">\n", table_data(Line, L, N)]. +table_row(Line, L) -> +    ["<tr>\n", table_data(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), +    "</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(_) -> "". + +read_stylesheet() -> +    PrivDir = code:priv_dir(?TOOLS_APP), +    {ok, Css} = file:read_file(filename:join(PrivDir, ?STYLESHEET)), +    [Css]. +  %%%--Export--------------------------------------------------------------  do_export(Module, OutFile, From, State) ->      case file:open(OutFile,[write,binary,raw,delayed_write]) of diff --git a/lib/tools/src/eprof.erl b/lib/tools/src/eprof.erl index 535ddbcd04..86e3d3a8b8 100644 --- a/lib/tools/src/eprof.erl +++ b/lib/tools/src/eprof.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. All Rights Reserved.  %%  %% Licensed under the Apache License, Version 2.0 (the "License");  %% you may not use this file except in compliance with the License. @@ -26,11 +26,11 @@  -export([start/0,  	 stop/0, -	 dump/0, +	 dump/0, dump_data/0,  	 start_profiling/1, start_profiling/2, start_profiling/3,  	 profile/1, profile/2, profile/3, profile/4, profile/5,  	 stop_profiling/0, -	 analyze/0, analyze/1, analyze/2, +	 analyze/0, analyze/1, analyze/2, analyze/4,  	 log/1]).  %% Internal exports  @@ -117,6 +117,9 @@ profile(Rootset, M, F, A, Pattern, Options) ->  dump() ->       gen_server:call(?MODULE, dump, infinity). +dump_data() -> +    gen_server:call(?MODULE, dump_data, infinity). +  log(File) ->      gen_server:call(?MODULE, {logfile, File}, infinity). @@ -151,22 +154,18 @@ init([]) ->  %% analyze -handle_call({analyze, _, _}, _, #state{ bpd = #bpd{ p = {0,nil}, us = 0, n = 0} = Bpd } = S) when is_record(Bpd, bpd) -> +handle_call( +  {analyze, _, _}, _, +  #state{ bpd = #bpd{ p = {0,nil}, us = 0, n = 0 } } = S) ->      {reply, nothing_to_analyze, S}; -handle_call({analyze, procs, Opts}, _, #state{ bpd = #bpd{ p = Ps, us = Tus} = Bpd, fd = Fd} = S) when is_record(Bpd, bpd) -> -    lists:foreach(fun -	    ({Pid, Mfas}) -> -		{Pn, Pus} =  sum_bp_total_n_us(Mfas), -		format(Fd, "~n****** Process ~w    -- ~s % of profiled time *** ~n", [Pid, s("~.2f", [100.0*divide(Pus,Tus)])]), -		print_bp_mfa(Mfas, {Pn,Pus}, Fd, Opts), -		ok -	end, gb_trees:to_list(Ps)), -    {reply, ok, S}; +handle_call({analyze, procs, Opts}, _, #state{ bpd = Bpd, fd = Fd } = S) +  when is_record(Bpd, bpd) -> +    {reply, analyze(Fd, procs, Opts, Bpd), S}; -handle_call({analyze, total, Opts}, _, #state{ bpd = #bpd{ mfa = Mfas, n = Tn, us = Tus} = Bpd, fd = Fd} = S) when is_record(Bpd, bpd) -> -    print_bp_mfa(Mfas, {Tn, Tus}, Fd, Opts), -    {reply, ok, S}; +handle_call({analyze, total, Opts}, _, #state{ bpd = Bpd, fd = Fd } = S) +  when is_record(Bpd, bpd) -> +    {reply, analyze(Fd, total, Opts, Bpd), S};  handle_call({analyze, Type, _Opts}, _, S) ->      {reply, {error, {undefined, Type}}, S}; @@ -260,6 +259,10 @@ handle_call({logfile, File}, _From, #state{ fd = OldFd } = S) ->  handle_call(dump, _From, #state{ bpd = Bpd } = S) when is_record(Bpd, bpd) ->      {reply, gb_trees:to_list(Bpd#bpd.p), S}; +handle_call(dump_data, _, #state{ bpd = #bpd{} = Bpd } = S) +  when is_record(Bpd, bpd) -> +    {reply, Bpd, S}; +  handle_call(stop, _FromTag, S) ->      {stop, normal, stopped, S}. @@ -438,6 +441,23 @@ collect_bpdfp(Mfa, Tree, Data) ->  	    {PTno + Ni, PTuso + Time, Ti1}      end, {0,0, Tree}, Data). + + +analyze(Fd, procs, Opts, #bpd{ p = Ps, us = Tus }) -> +    lists:foreach( +      fun +          ({Pid, Mfas}) -> +              {Pn, Pus} =  sum_bp_total_n_us(Mfas), +              format( +                Fd, +                "~n****** Process ~w    -- ~s % of profiled time *** ~n", +                [Pid, s("~.2f", [100.0*divide(Pus, Tus)])]), +              print_bp_mfa(Mfas, {Pn,Pus}, Fd, Opts), +              ok +      end, gb_trees:to_list(Ps)); +analyze(Fd, total, Opts, #bpd{ mfa = Mfas, n = Tn, us = Tus } ) -> +    print_bp_mfa(Mfas, {Tn, Tus}, Fd, Opts). +  %% manipulators  sort_mfa(Bpfs, mfa) when is_list(Bpfs) ->      lists:sort(fun diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl index fb657c2928..36d4828861 100644 --- a/lib/tools/src/fprof.erl +++ b/lib/tools/src/fprof.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %%  -%% Copyright Ericsson AB 2001-2017. All Rights Reserved. +%% Copyright Ericsson AB 2001-2018. All Rights Reserved.  %%   %% Licensed under the Apache License, Version 2.0 (the "License");  %% you may not use this file except in compliance with the License. @@ -1242,8 +1242,7 @@ spawn_3step(Spawn, FunPrelude, FunAck, FunBody)  		    catch Child ! {Parent, Ref, Go},  		    Result  	    catch -		Class:Reason -> -		    Stacktrace = erlang:get_stacktrace(), +		Class:Reason:Stacktrace ->  		    catch exit(Child, kill),  		    erlang:raise(Class, Reason, Stacktrace)  	    end; diff --git a/lib/tools/src/instrument.erl b/lib/tools/src/instrument.erl index 055f4a7afb..0203fefe13 100644 --- a/lib/tools/src/instrument.erl +++ b/lib/tools/src/instrument.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %%  -%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% Copyright Ericsson AB 1998-2018. All Rights Reserved.  %%   %% Licensed under the Apache License, Version 2.0 (the "License");  %% you may not use this file except in compliance with the License. @@ -19,410 +19,140 @@  %%  -module(instrument). --export([holes/1, mem_limits/1, memory_data/0, read_memory_data/1, -	 sort/1, store_memory_data/1, sum_blocks/1, -	 descr/1, type_descr/2, allocator_descr/2, class_descr/2, -	 type_no_range/1, block_header_size/1, store_memory_status/1, -	 read_memory_status/1, memory_status/1]). - - --define(OLD_INFO_SIZE, 32). %% (sizeof(mem_link) in pre R9C utils.c) - --define(IHMARKER(H),  element(1, H)). --define(VSN(H),       element(2, H)). --define(INFO_SIZE(H), element(3, H)). --define(TYPEMAP(H),   element(4, H)). - --define(IHDR(H), is_tuple(H), ?IHMARKER(H) =:= instr_hdr). --define(IHDRVSN(H, V), ?IHDR(H), ?VSN(H) =:= V). - -memory_data() -> -    case catch erlang:system_info(allocated) of -	{'EXIT',{Error,_}} -> -	    erlang:error(Error, []); -	{'EXIT',Error} -> -	    erlang:error(Error, []); -	Res -> -	    Res +-export([allocations/0, allocations/1, +         carriers/0, carriers/1]). + +-type block_histogram() :: tuple(). + +-type allocation_summary() :: +    {HistogramStart :: non_neg_integer(), +     UnscannedSize :: non_neg_integer(), +     Allocations :: #{ Origin :: atom() => +                       #{ Type :: atom() => block_histogram() }}}. + +-spec allocations() -> {ok, Result} | {error, Reason} when +    Result :: allocation_summary(), +    Reason :: not_enabled. +allocations() -> +    allocations(#{}). + +-spec allocations(Options) -> {ok, Result} | {error, Reason} when +    Result :: allocation_summary(), +    Reason :: not_enabled, +    Options :: #{ scheduler_ids => list(non_neg_integer()), +                  allocator_types => list(atom()), +                  histogram_start => pos_integer(), +                  histogram_width => pos_integer() }. +allocations(Options) -> +    Ref = make_ref(), + +    Defaults = #{ scheduler_ids => lists:seq(0, erlang:system_info(schedulers)), +                  allocator_types => erlang:system_info(alloc_util_allocators), +                  histogram_start => 128, +                  histogram_width => 18 }, + +    {HistStart, MsgCount} = +        dispatch_gather(maps:merge(Defaults, Options), Ref, +                        fun erts_internal:gather_alloc_histograms/1), + +    alloc_hist_receive(HistStart, MsgCount, Ref). + +alloc_hist_receive(_HistStart, 0, _Ref) -> +    {error, not_enabled}; +alloc_hist_receive(HistStart, MsgCount, Ref) when MsgCount > 0 -> +    {Unscanned, Histograms} = alloc_hist_receive_1(MsgCount, Ref, 0, #{}), +    {ok, {HistStart, Unscanned, Histograms}}. + +alloc_hist_receive_1(0, _Ref, Unscanned, Result) -> +    {Unscanned, Result}; +alloc_hist_receive_1(MsgCount, Ref, Unscanned0, Result0) -> +    receive +        {Ref, Unscanned, Tags} -> +            Result = lists:foldl(fun alloc_hist_fold_result/2, Result0, Tags), +            alloc_hist_receive_1(MsgCount - 1, Ref, Unscanned0 + Unscanned, Result)      end. -store_memory_data(File) -> -    case catch erlang:system_info({allocated, File}) of -	{'EXIT',{Error,_}} -> -	    erlang:error(Error, [File]); -	{'EXIT',Error} -> -	    erlang:error(Error, [File]); -	Res -> -	    Res +alloc_hist_fold_result({Id, Type, BlockHist}, Result0) -> +    IdAllocs0 = maps:get(Id, Result0, #{}), +    MergedHists = case maps:find(Type, IdAllocs0) of +                      {ok, PrevHist} -> +                          alloc_hist_merge_hist(tuple_size(BlockHist), +                                                BlockHist, +                                                PrevHist); +                      error -> +                          BlockHist +                  end, +    IdAllocs = IdAllocs0#{ Type => MergedHists }, +    Result0#{ Id => IdAllocs }. + +alloc_hist_merge_hist(0, A, _B) -> +    A; +alloc_hist_merge_hist(Index, A, B) -> +    Merged = setelement(Index, A, element(Index, A) + element(Index, B)), +    alloc_hist_merge_hist(Index - 1, Merged, B). + +-type carrier_info_list() :: +    {HistogramStart :: non_neg_integer(), +     Carriers :: [{AllocatorType :: atom(), +                   TotalSize :: non_neg_integer(), +                   UnscannedSize :: non_neg_integer(), +                   AllocatedSize :: non_neg_integer(), +                   AllocatedCount :: non_neg_integer(), +                   InPool :: boolean(), +                   FreeBlocks :: block_histogram()}]}. + +-spec carriers() -> {ok, Result} | {error, Reason} when +    Result :: carrier_info_list(), +    Reason :: not_enabled. +carriers() -> +    carriers(#{}). + +-spec carriers(Options) -> {ok, Result} | {error, Reason} when +    Result :: carrier_info_list(), +    Reason :: not_enabled, +    Options :: #{ scheduler_ids => list(non_neg_integer()), +                  allocator_types => list(atom()), +                  histogram_start => pos_integer(), +                  histogram_width => pos_integer() }. +carriers(Options) -> +    Ref = make_ref(), + +    Defaults = #{ scheduler_ids => lists:seq(0, erlang:system_info(schedulers)), +                  allocator_types => erlang:system_info(alloc_util_allocators), +                  histogram_start => 512, +                  histogram_width => 14 }, + +    {HistStart, MsgCount} = +        dispatch_gather(maps:merge(Defaults, Options), Ref, +                        fun erts_internal:gather_carrier_info/1), + +    carrier_info_receive(HistStart, MsgCount, Ref). + +carrier_info_receive(_HistStart, 0, _Ref) -> +    {error, not_enabled}; +carrier_info_receive(HistStart, MsgCount, Ref) -> +    {ok, {HistStart, carrier_info_receive_1(MsgCount, Ref, [])}}. + +carrier_info_receive_1(0, _Ref, Result) -> +    lists:flatten(Result); +carrier_info_receive_1(MsgCount, Ref, Result0) -> +    receive +        {Ref, Carriers} -> +            carrier_info_receive_1(MsgCount - 1, Ref, [Carriers, Result0])      end. -memory_status(Type) when is_atom(Type) -> -    case catch erlang:system_info({allocated, status, Type}) of -	{'EXIT',{Error,_}} -> -	    erlang:error(Error, [Type]); -	{'EXIT',Error} -> -	    erlang:error(Error, [Type]); -	Res -> -	    Res -    end; -memory_status(Type) -> -    erlang:error(badarg, [Type]). - -store_memory_status(File) when is_list(File) -> -    case catch erlang:system_info({allocated, status, File}) of -	{'EXIT',{Error,_}} -> -	    erlang:error(Error, [File]); -	{'EXIT',Error} -> -	    erlang:error(Error, [File]); -	Res -> -	    Res -    end; -store_memory_status(File) -> -    erlang:error(badarg, [File]). - -read_memory_data(File) when is_list(File) -> -    case file:consult(File) of -	{ok, [Hdr|MD]} when ?IHDR(Hdr) -> -	    {Hdr, MD}; -	{ok, [{T,A,S,undefined}|_] = MD} when is_integer(T), -					      is_integer(A), -					      is_integer(S) -> -	    {{instr_hdr, 1, ?OLD_INFO_SIZE}, MD}; -	{ok, [{T,A,S,{X,Y,Z}}|_] = MD} when is_integer(T), -					    is_integer(A), -					    is_integer(S), -					    is_integer(X), -					    is_integer(Y), -					    is_integer(Z) -> -	    {{instr_hdr, 1, ?OLD_INFO_SIZE}, MD}; -	{ok, _} -> -	    {error, eio}; -	Error -> -	    Error -    end; -read_memory_data(File) -> -    erlang:error(badarg, [File]). - -read_memory_status(File) when is_list(File) -> -    case file:consult(File) of -	{ok, [{instr_vsn, _}|Stat]} -> -	    Stat; -	{ok, _} -> -	    {error, eio}; -	Error -> -	    Error -    end; -read_memory_status(File) -> -    erlang:error(badarg, [File]). - -holes({Hdr, MD}) when ?IHDR(Hdr) -> -    check_holes(?INFO_SIZE(Hdr), MD). - -check_holes(_ISz, []) -> -    ok; -check_holes(ISz, [E | L]) -> -    check_holes(ISz, E, L). - -check_holes(_ISz, _E1, []) -> -    io:format("~n"); -check_holes(ISz, E1, [E2 | Rest]) -> -    check_hole(ISz, E1, E2), -    check_holes(ISz, E2, Rest). - -check_hole(ISz, {_,P1,S1,_}, {_,P2,_,_}) -> -    End = P1+S1, -    Hole = P2 - (End + ISz), -    if -	Hole =< 7 -> -	    ok; -	true -> -	    io:format(" ~p", [Hole]) -    end. - -sum_blocks({Hdr, L}) when ?IHDR(Hdr) -> -    lists:foldl(fun({_,_,S,_}, Sum) -> S+Sum end, -		0, -		L). - -mem_limits({Hdr, L}) when ?IHDR(Hdr) -> -    {_, P1, _, _} = hd(L), -    {_, P2, S2, _} = lists:last(L), -    {P1, P2+S2}. - -sort({Hdr, MD}) when ?IHDR(Hdr) -> -    {Hdr, lists:keysort(2, MD)}. - -descr({Hdr, MD} = ID) when ?IHDR(Hdr) -> -    {Hdr, lists:map(fun ({TN, Addr, Sz, {0, N, S}}) -> -			    {type_descr(ID, TN), -			     Addr, -			     Sz, -			     list_to_pid("<0." -					 ++ integer_to_list(N) -					 ++ "." -					 ++ integer_to_list(S) -					 ++ ">")}; -			({TN, Addr, Sz, undefined}) -> -			    {type_descr(ID, TN), -			     Addr, -			     Sz, -			     undefined} -		    end, -		    MD)}. - -block_header_size({Hdr, _}) when ?IHDR(Hdr) -> -    ?INFO_SIZE(Hdr). - -type_descr({Hdr, _}, TypeNo) when ?IHDRVSN(Hdr, 2), -				  is_integer(TypeNo) -> -    case catch element(1, element(TypeNo, ?TYPEMAP(Hdr))) of -	{'EXIT', _} -> invalid_type; -	Type -> Type -    end; -type_descr({Hdr, _}, TypeNo) when ?IHDRVSN(Hdr, 1), -				  is_integer(TypeNo) -> -    type_string(TypeNo). - - -allocator_descr({Hdr, _}, TypeNo) when ?IHDRVSN(Hdr, 2), is_integer(TypeNo) -> -    case catch element(2, element(TypeNo, ?TYPEMAP(Hdr))) of -	{'EXIT', _} -> invalid_type; -	Type -> Type -    end; -allocator_descr({Hdr, _}, TypeNo) when ?IHDRVSN(Hdr, 1), is_integer(TypeNo) -> -    "unknown". - -class_descr({Hdr, _}, TypeNo) when ?IHDRVSN(Hdr, 2), is_integer(TypeNo) -> -    case catch element(3, element(TypeNo, ?TYPEMAP(Hdr))) of -	{'EXIT', _} -> invalid_type; -	Type -> Type -    end; -class_descr({Hdr, _}, TypeNo) when ?IHDRVSN(Hdr, 1), is_integer(TypeNo) -> -    "unknown". - -type_no_range({Hdr, _}) when ?IHDRVSN(Hdr, 2) -> -    {1, tuple_size(?TYPEMAP(Hdr))}; -type_no_range({Hdr, _}) when ?IHDRVSN(Hdr, 1) -> -    {-1, 1000}. - -type_string(-1) -> -    "unknown"; -type_string(1) -> -    "atom text"; -type_string(11) -> -    "atom desc"; -type_string(2) -> -    "bignum (big_to_list)"; -type_string(31) -> -    "fixalloc"; -type_string(32) -> -    "unknown fixalloc block"; -type_string(33) -> -    "message buffer"; -type_string(34) -> -    "message link"; -type_string(4) -> -    "estack"; -type_string(40) -> -    "db table vec"; -type_string(41) -> -    "db tree select buffer"; -type_string(43) -> -    "db hash select buffer"; -type_string(44) -> -    "db hash select list"; -type_string(45) -> -    "db match prog stack"; -type_string(46) -> -    "db match prog heap data"; -type_string(47) -> -    "db temp buffer"; -type_string(48) -> -    "db error"; -type_string(49) -> -    "db error info"; -type_string(50) -> -    "db trans tab"; -type_string(51) -> -    "db segment"; -type_string(52) -> -    "db term"; -type_string(53) -> -    "db add_counter"; -type_string(54) -> -    "db segment table"; -type_string(55) -> -    "db table (fix)"; -type_string(56) -> -    "db bindings"; -type_string(57) -> -    "db counter"; -type_string(58) -> -    "db trace vec"; -type_string(59) -> -    "db fixed deletion"; -type_string(60) -> -    "binary (external.c)"; -type_string(61) -> -    "binary"; -type_string(62) -> -    "procbin (fix)"; -type_string(70) -> -    "driver alloc (io.c)"; -type_string(71) -> -    "binary (io.c)"; -type_string(72) -> -    "binary vec (io.c)"; -type_string(73) -> -    "binary vec 2 (io.c)"; -type_string(74) -> -    "io vec (io.c)"; -type_string(75) -> -    "io vec 2 (io.c)"; -type_string(76) -> -    "temp io buffer (io.c)"; -type_string(77) -> -    "temp io buffer 2 (io.c)"; -type_string(78) -> -    "line buffer (io.c)"; -type_string(8) -> -    "heap"; -type_string(801) -> -    "heap (1)"; -type_string(802) -> -    "heap (2)"; -type_string(803) -> -    "heap (3)"; -type_string(804) -> -    "heap (4)"; -type_string(805) -> -    "heap (5)"; -type_string(821) -> -    "heap fragment (1)"; -type_string(822) -> -    "heap fragment (2)"; -type_string(830) -> -    "sequential store buffer (for vectors)"; -type_string(91) -> -    "process table"; -type_string(92) -> -    "process desc"; -type_string(110) -> -    "hash buckets"; -type_string(111) -> -    "hash table"; -type_string(120) -> -    "index init"; -type_string(121) -> -    "index table"; -type_string(130) -> -    "temp buffer"; -type_string(140) -> -    "timer wheel"; -type_string(150) -> -    "distribution cache"; -type_string(151) -> -    "dmem"; -type_string(152) -> -    "distribution table"; -type_string(153) -> -    "distribution table buckets"; -type_string(154) -> -    "distribution table entry"; -type_string(155) -> -    "node table"; -type_string(156) -> -    "node table buckets"; -type_string(157) -> -    "node table entry"; -type_string(160) -> -    "port table"; -type_string(161) -> -    "driver entry"; -type_string(162) -> -    "port setup"; -type_string(163) -> -    "port wait"; -type_string(170) -> -    "module"; -type_string(171) -> -    "fundef"; -type_string(180) -> -    "file table"; -type_string(181) -> -    "driver table"; -type_string(182) -> -    "poll struct"; -type_string(190) -> -    "inet driver"; -type_string(200) -> -    "efile driver"; -type_string(210) -> -    "gc root set"; -type_string(220) -> -    "breakpoint data"; -type_string(230) -> -    "async queue"; -type_string(231) -> -    "async (exit)"; -type_string(232) -> -    "async (driver)"; -type_string(240) -> -    "bits buffer"; -type_string(241) -> -    "bits temp buffer"; -type_string(250) -> -    "modules (loader)"; -type_string(251) -> -    "code (loader)"; -type_string(252) -> -    "atom tab (loader)"; -type_string(253) -> -    "import tab (loader)"; -type_string(254) -> -    "export tab (loader)"; -type_string(255) -> -    "lable tab (loader)"; -type_string(256) -> -    "gen op (loader)"; -type_string(257) -> -    "gen op args (loader)"; -type_string(258) -> -    "gen op args 2 (loader)"; -type_string(259) -> -    "gen op args 3 (loader)"; -type_string(260) -> -    "lambdas (loader)"; -type_string(261) -> -    "temp int buffer (loader)"; -type_string(262) -> -    "temp heap (loader)"; -type_string(280) -> -    "dist ctrl msg buffer"; -type_string(281) -> -    "dist_buf"; -type_string(290) -> -    "call trace buffer"; -type_string(300) -> -    "bif timer rec"; -type_string(310) -> -    "argument registers"; -type_string(320) -> -    "compressed binary temp buffer"; -type_string(330) -> -    "term_to_binary temp buffer"; -type_string(340) -> -    "proc dict"; -type_string(350) -> -    "trace to port temp buffer"; -type_string(360) -> -    "lists subtract temp buffer"; -type_string(370) -> -    "link (lh)"; -type_string(380) -> -    "port call buffer"; -type_string(400) -> -    "definite_alloc block"; -type_string(_) -> -    invalid_type. - +dispatch_gather(#{ allocator_types := AllocatorTypes, +                   scheduler_ids := SchedulerIds, +                   histogram_start := HistStart, +                   histogram_width := HistWidth }, Ref, Gather) +        when is_list(AllocatorTypes), +             is_list(SchedulerIds), +             HistStart >= 1, HistStart =< (1 bsl 28), +             HistWidth >= 1, HistWidth =< 32 -> +    MsgCount = lists:sum( +        [Gather({AllocatorType, SchedId, HistWidth, HistStart, Ref}) || +         SchedId <- SchedulerIds, +         AllocatorType <- AllocatorTypes]), +    {HistStart, MsgCount}; +dispatch_gather(_, _, _) -> +    error(badarg). diff --git a/lib/tools/src/lcnt.erl b/lib/tools/src/lcnt.erl index d0152a4915..ee6057e4f5 100644 --- a/lib/tools/src/lcnt.erl +++ b/lib/tools/src/lcnt.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2010-2017. All Rights Reserved. +%% Copyright Ericsson AB 2010-2018. All Rights Reserved.  %%  %% Licensed under the Apache License, Version 2.0 (the "License");  %% you may not use this file except in compliance with the License. @@ -125,7 +125,7 @@  %% -------------------------------------------------------------------- %%  start()  -> gen_server:start({local, ?MODULE}, ?MODULE, [], []). -stop()   -> gen_server:call(?MODULE, stop, infinity). +stop()   -> gen_server:stop(?MODULE, normal, infinity).  init([]) -> {ok, #state{ locks = [], duration = 0 } }.  start_internal() -> @@ -442,9 +442,6 @@ handle_call({save, Filename}, _From, State) ->  	    {reply, {error, Error}, State}      end; -handle_call(stop, _From, State) -> -    {stop, normal, ok, State}; -  handle_call(Command, _From, State) ->      {reply, {error, {undefined, Command}}, State}. diff --git a/lib/tools/src/xref.erl b/lib/tools/src/xref.erl index 32efa36fa2..466ec7d331 100644 --- a/lib/tools/src/xref.erl +++ b/lib/tools/src/xref.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %%  -%% Copyright Ericsson AB 2000-2016. All Rights Reserved. +%% Copyright Ericsson AB 2000-2018. All Rights Reserved.  %%   %% Licensed under the Apache License, Version 2.0 (the "License");  %% you may not use this file except in compliance with the License. @@ -182,7 +182,9 @@ split_args(Opts) ->      end.  stop(Name) -> -    gen_server:call(Name, stop, infinity). +    try gen_server:call(Name, stop, infinity) +    after catch unregister(Name) % ensure the name is gone +    end.  add_release(Name, Dir) ->      gen_server:call(Name, {add_release, Dir}, infinity). diff --git a/lib/tools/src/xref_utils.erl b/lib/tools/src/xref_utils.erl index 02e207d40c..eca751337b 100644 --- a/lib/tools/src/xref_utils.erl +++ b/lib/tools/src/xref_utils.erl @@ -557,12 +557,9 @@ subdir(Dir, SubDir, true) ->  %% Avoid "App-01.01" - the zeroes will be lost.  filename2appl(File) -> -    Pos = string:rstr(File, "-"), -    true = Pos > 1, -    V = string:sub_string(File, Pos+1), -    true = string:len(V) > 0, -    VsnT = string:tokens(V, "."), -    ApplName = string:sub_string(File, 1, Pos-1), +    [ApplName, V] = string:split(File, "-", trailing), +    true = string:length(V) > 0, +    VsnT = string:lexemes(V, "."),      Vsn = [list_to_integer(Vsn) || Vsn <- VsnT],      {list_to_atom(ApplName),Vsn}. diff --git a/lib/tools/test/emacs_SUITE.erl b/lib/tools/test/emacs_SUITE.erl index f4e78da667..a6d43d1816 100644 --- a/lib/tools/test/emacs_SUITE.erl +++ b/lib/tools/test/emacs_SUITE.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2018. All Rights Reserved.  %%  %% Licensed under the Apache License, Version 2.0 (the "License");  %% you may not use this file except in compliance with the License. @@ -23,18 +23,28 @@  -export([all/0, init_per_testcase/2, end_per_testcase/2]). --export([bif_highlight/1, indent/1]). +-export([bif_highlight/1, +         load_interpreted/1,  compile_and_load/1, +         indent/1, +         tests_interpreted/1, tests_compiled/1 +        ]).  all() -> -    [bif_highlight, indent]. +    [bif_highlight, load_interpreted, compile_and_load, +     indent, +     tests_interpreted, tests_compiled +    ]. -init_per_testcase(_Case, Config) -> +init_per_testcase(Case, Config) ->      ErlangEl = filename:join([code:lib_dir(tools),"emacs","erlang.el"]),      case file:read_file_info(ErlangEl) of -	{ok, _} -> -	    [{el, ErlangEl}|Config]; -	_ -> -	    {skip, "Could not find erlang.el"} +        {ok, _} -> +            case Case =:= bif_highlight orelse emacs_version_ok(24.1) of +                false -> {skip, "Old or no emacs found"}; +                _ -> [{el, ErlangEl}|Config] +            end; +        _ -> +            {skip, "Could not find erlang.el"}      end.  end_per_testcase(_Case, _Config) -> @@ -46,26 +56,26 @@ bif_highlight(Config) ->      %% All auto-imported bifs      IntBifs = lists:usort( -		[F  || {F,A} <- erlang:module_info(exports), -		       erl_internal:bif(F,A)]), +                [F  || {F,A} <- erlang:module_info(exports), +                       erl_internal:bif(F,A)]),      %% all bif which need erlang: prefix and are not operands      ExtBifs = lists:usort( -		[F  || {F,A} <- erlang:module_info(exports), -		       not erl_internal:bif(F,A) andalso -			   not is_atom(catch erl_internal:op_type(F,A))]), +                [F  || {F,A} <- erlang:module_info(exports), +                       not erl_internal:bif(F,A) andalso +                           not is_atom(catch erl_internal:op_type(F,A))]),      check_bif_highlight(Bin, <<"erlang-int-bifs">>, IntBifs),      check_bif_highlight(Bin, <<"erlang-ext-bifs">>, ExtBifs). -     +  check_bif_highlight(Bin, Tag, Compare) -> -    [_H,IntMatch,_T] =  -	re:split(Bin,<<"defvar ",Tag/binary, -		       "[^(]*\\(([^)]*)">>,[]), -    EmacsIntBifs = [list_to_atom(S) ||  -		  S <- string:tokens(binary_to_list(IntMatch)," '\"\n")], -     +    [_H,IntMatch,_T] = +        re:split(Bin,<<"defvar ",Tag/binary, +                       "[^(]*\\(([^)]*)">>,[]), +    EmacsIntBifs = [list_to_atom(S) || +                  S <- string:tokens(binary_to_list(IntMatch)," '\"\n")], +      ct:log("Emacs ~p",[EmacsIntBifs]),      ct:log("Int ~p",[Compare]), @@ -73,27 +83,92 @@ check_bif_highlight(Bin, Tag, Compare) ->      ct:log("Diff2 ~p",[EmacsIntBifs -- Compare]),      [] = Compare -- EmacsIntBifs,      [] = EmacsIntBifs -- Compare. -     +  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -indent(Config) -> -    case emacs_version_ok() of +load_interpreted(_Config) -> +    _ = emacs(["-l erlang.el -f erlang-mode"]), +    ok. + +compile_and_load(_Config) -> +    Dir = emacs_dir(), +    Files0 = filelib:wildcard("*.el", Dir), +    Files = case emacs_version_ok(24.3) of +                %% erldoc.el depends on cl-lib which was introduced in 24.3. +                false -> Files0 -- ["erldoc.el"]; +                _ -> Files0 +            end, +    Unforgiving = +        case emacs_version_ok(24) of +            Ver when Ver < 25 -> +                ""; +            Ver when Ver < 26 -> +                %% Workaround byte-compile-error-on-warn which seem broken in +                %% Emacs 25. +                "\"(advice-add #'display-warning :after " +                    "(lambda (_ f _ _) (error \"%s\" f)))\""; +            _ -> +                "\"(setq byte-compile-error-on-warn t)\"" +        end, +    %% Add files here whenever they are cleaned of warnings. +    NoWarn = ["erlang.el", "erlang-test.el", "erlang-edoc.el", "erlang-start.el", "erldoc.el"], +    Compile = fun(File) -> +                      Pedantic = case lists:member(File, NoWarn) andalso Unforgiving /= "" of +                                     true -> ["--eval ", Unforgiving, " "]; +                                     false -> " " +                                 end, +                      emacs([Pedantic, +                             " -f batch-byte-compile ",filename:join(Dir, File)]), +                      true +              end, +    lists:foreach(Compile, Files), +    emacs(["-l erlang.elc -f erlang-mode"]), +    ok. + +tests_interpreted(_Config) -> +    case emacs_version_ok(25) of          false -> {skip, "Old or no emacs found"}; -        true -> -            Def = filename:dirname(code:which(?MODULE)) ++ "/" ++ ?MODULE_STRING ++ "_data", -            Dir = proplists:get_value(data_dir, Config, Def), -            OrigFs = filelib:wildcard(Dir ++ "/*"), -            io:format("Dir: ~s~nFs: ~p~n", [Dir, OrigFs]), -            Fs = [{File, unindent(File)} || File <- OrigFs, -                                            filename:extension(File) =:= ""], -            Indent = fun emacs/1, -            [Indent(File) || {_, File} <- Fs], -            Res = [diff(Orig, File) || {Orig, File} <- Fs], -            [file:delete(File) || {ok, File} <- Res],       %% Cleanup -            [] = [Fail || {fail, Fail} <- Res], +        _ -> +            emacs(["-l erlang.el ", +                   "-l erlang-test.el -f ert-run-tests-batch-and-exit"]),              ok      end. +tests_compiled(_Config) -> +    case emacs_version_ok(25) of +        false -> {skip, "Old or no emacs found"}; +        _ -> +            emacs(["-l erlang.elc ", +                   "-l erlang-test.elc -f ert-run-tests-batch-and-exit"]), +            ok +    end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +indent(Config) -> +    Def = filename:dirname(code:which(?MODULE)) +        ++ "/" +        ++ ?MODULE_STRING +        ++ "_data", +    Dir = proplists:get_value(data_dir, Config, Def), +    OrigFs = filelib:wildcard(Dir ++ "/*"), +    io:format("Dir: ~s~nFs: ~p~n", [Dir, OrigFs]), +    Fs = [{File, unindent(File)} || File <- OrigFs, +                                    filename:extension(File) =:= ""], +    Indent = fun(File) -> +                     emacs([ +                            File, " ", +                            "--eval '(indent-region (point-min) (point-max) nil)' ", +                            "--eval '(save-buffer 0)'" +                           ]), +                     ok +             end, +    [Indent(File) || {_, File} <- Fs], +    Res = [diff(Orig, File) || {Orig, File} <- Fs], +    [file:delete(File) || {ok, File} <- Res],       %% Cleanup +    [] = [Fail || {fail, Fail} <- Res], +    ok. +  unindent(Input) ->      Output = Input ++ ".erl",      {ok, Bin} = file:read_file(Input), @@ -112,14 +187,13 @@ diff(Orig, File) ->              {fail, File}      end. -emacs_version_ok() -> +emacs_version_ok(AcceptVer) ->      case os:cmd("emacs --version | head -1") of          "GNU Emacs " ++ Ver ->              case string:to_float(Ver) of -                {Vsn, _} when Vsn >= 24.1 -> -                    true; +                {Vsn, _} when Vsn >= AcceptVer -> +                    Vsn;                  _ -> -                    io:format("Emacs version fail~n~s~n~n",[Ver]),                      false              end;          Res -> @@ -127,16 +201,19 @@ emacs_version_ok() ->              false      end. -emacs(File) -> -    EmacsErlDir = filename:join([code:lib_dir(tools), "emacs"]), +emacs(EmacsCmds) when is_list(EmacsCmds) ->      Cmd = ["emacs ",             "--batch --quick ", -           "--directory ", EmacsErlDir, " ", -           "--eval \"(require 'erlang-start)\" ", -           File, " ", -           "--eval '(indent-region (point-min) (point-max) nil)' ", -           "--eval '(save-buffer 0)'" -          ], -    _Res = os:cmd(Cmd), -    % io:format("cmd ~s:~n=> ~s~n", [Cmd, _Res]), -    ok. +           "--directory ", emacs_dir(), " ", +           "--eval \"(require 'erlang-start)\" " +           | EmacsCmds], +    Res0 = os:cmd(Cmd ++ " ; echo $?"), +    Rows = string:lexemes(Res0, ["\r\n", $\n]), +    Res = lists:last(Rows), +    Output = string:join(lists:droplast(Rows), "\n"), +    io:format("Cmd ~s:~n  => ~s ~ts~n", [Cmd, Res, Output]), +    "0" = Res, +    Output. + +emacs_dir() -> +    filename:join([code:lib_dir(tools), "emacs"]). diff --git a/lib/tools/test/eprof_SUITE_data/eed.erl b/lib/tools/test/eprof_SUITE_data/eed.erl index 5f2a21aa60..9fe49c6f5c 100644 --- a/lib/tools/test/eprof_SUITE_data/eed.erl +++ b/lib/tools/test/eprof_SUITE_data/eed.erl @@ -54,7 +54,7 @@ edit(Name) ->  loop(St0) ->      {ok, St1, Cmd} = get_line(St0), -    case catch command(lib:nonl(Cmd), St1) of +    case catch command(nonl(Cmd), St1) of  	{'EXIT', Reason} ->  	    %% XXX Should clear outstanding global command here.  	    loop(print_error({'EXIT', Reason}, St1)); @@ -66,6 +66,10 @@ loop(St0) ->  	    loop(St2)      end. +nonl([$\n]) -> []; +nonl([]) -> []; +nonl([H|T]) -> [H|nonl(T)]. +  command(Cmd, St) ->      case parse_command(Cmd, St) of  	quit -> diff --git a/lib/tools/test/fprof_SUITE.erl b/lib/tools/test/fprof_SUITE.erl index 8fd164a4b3..ae0e7253ad 100644 --- a/lib/tools/test/fprof_SUITE.erl +++ b/lib/tools/test/fprof_SUITE.erl @@ -51,7 +51,7 @@  suite() ->      [{ct_hooks,[ts_install_cth]}, -     {timetrap,{seconds,60}}]. +     {timetrap,{seconds,240}}].  all() ->       case test_server:is_native(fprof_SUITE) of @@ -571,7 +571,7 @@ seq_r(Start, Stop, Succ, R) ->  create_file_slow(Name, N) when is_integer(N), N >= 0 ->      {ok, FD} =  -    file:open(Name, [raw, write, delayed_write, binary]), +    file:open(Name, [raw, write, binary]),      if N > 256 ->             ok = file:write(FD,                             lists:map(fun (X) -> <<X:32/unsigned>> end, diff --git a/lib/tools/test/instrument_SUITE.erl b/lib/tools/test/instrument_SUITE.erl index f37d28c277..8c521b2e1a 100644 --- a/lib/tools/test/instrument_SUITE.erl +++ b/lib/tools/test/instrument_SUITE.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% Copyright Ericsson AB 1998-2018. All Rights Reserved.  %%  %% Licensed under the Apache License, Version 2.0 (the "License");  %% you may not use this file except in compliance with the License. @@ -20,89 +20,274 @@  -module(instrument_SUITE).  -export([all/0, suite/0]). --export(['+Mim true'/1, '+Mis true'/1]). + +-export([allocations_enabled/1, allocations_disabled/1, allocations_ramv/1, +         carriers_enabled/1, carriers_disabled/1]). + +-export([test_all_alloc/2, test_per_alloc/2, test_format/3, test_abort/1, +         generate_test_blocks/0, churn_memory/0]).  -include_lib("common_test/include/ct.hrl").  suite() ->      [{ct_hooks,[ts_install_cth]}, -     {timetrap,{seconds,10}}]. +     {timetrap,{minutes,5}}].  all() ->  -    ['+Mim true', '+Mis true']. - - -%% Check that memory data can be read and processed -'+Mim true'(Config) when is_list(Config) -> -    Node = start_slave("+Mim true"), -    MD = rpc:call(Node, instrument, memory_data, []), -    [{total,[{sizes,S1,S2,S3},{blocks,B1,B2,B3}]}] -	= rpc:call(Node, instrument, memory_status, [total]), -    stop_slave(Node), -    true = S1 =< S2, -    true = S2 =< S3, -    true = B1 =< B2, -    true = B2 =< B3, -    MDS = instrument:sort(MD), -    {Low, High} = instrument:mem_limits(MDS), -    true = Low < High, -    {_, AL} = MDS, -    SumBlocks = instrument:sum_blocks(MD), -    case SumBlocks of -        N when is_integer(N) -> -            N = lists:foldl(fun ({_,_,Size,_}, Sum) -> -                                    Size+Sum -                            end, 0, AL), -            true = N =< S3; -        Other -> -            ct:fail(Other) +    [allocations_enabled, allocations_disabled, allocations_ramv, +     carriers_enabled, carriers_disabled]. + +-define(GENERATED_SBC_BLOCK_COUNT, 1000). +-define(GENERATED_MBC_BLOCK_COUNT, ?GENERATED_SBC_BLOCK_COUNT). + +-define(GENERATED_BLOCK_COUNT, (?GENERATED_SBC_BLOCK_COUNT + +                                ?GENERATED_MBC_BLOCK_COUNT)). +-define(GENERATED_CARRIER_COUNT, ?GENERATED_SBC_BLOCK_COUNT). + +allocations_test(Args, Plain, PerAlloc) -> +    run_test(Args, fun(Node) -> +                 ok = rpc:call(Node, ?MODULE, test_all_alloc, +                               [fun instrument:allocations/0, Plain]), +                 ok = rpc:call(Node, ?MODULE, test_per_alloc, +                               [fun instrument:allocations/1, PerAlloc]), +                 ok = rpc:call(Node, ?MODULE, test_format, +                               [#{ histogram_start => 512, +                                   histogram_width => 4 }, +                                fun instrument:allocations/1, +                                fun verify_allocations_output/2]), +                 ok = rpc:call(Node, ?MODULE, test_abort, +                               [fun erts_internal:gather_alloc_histograms/1]) +             end). + +allocations_enabled(Config) when is_list(Config) -> +    allocations_test("+Meamax +Muatags true", +                     fun verify_allocations_enabled/1, +                     fun verify_allocations_enabled/2). + +allocations_disabled(Config) when is_list(Config) -> +    allocations_test("+Meamax +Muatags false", +                     fun verify_allocations_disabled/1, +                     fun verify_allocations_disabled/2). + +allocations_ramv(Config) when is_list(Config) -> +    allocations_test("+Meamax +Muatags true +Muramv true", +                     fun verify_allocations_enabled/1, +                     fun verify_allocations_enabled/2). + +verify_allocations_disabled(_AllocType, Result) -> +    verify_allocations_disabled(Result). + +verify_allocations_disabled({error, not_enabled}) -> +    ok. + +%% Skip types that have unstable results or are unaffected by +Muatags +verify_allocations_enabled(literal_alloc, _Result) -> ok; +verify_allocations_enabled(exec_alloc, _Result) -> ok; +verify_allocations_enabled(temp_alloc, _Result) -> ok; +verify_allocations_enabled(sl_alloc, _Result) -> ok; +verify_allocations_enabled(_AllocType, Result) -> +    verify_allocations_enabled(Result). + +verify_allocations_enabled({ok, {_HistStart, _UnscannedBytes, Allocs}}) -> +    true = Allocs =/= #{}. + +verify_allocations_output(#{ histogram_start := HistStart, +                             histogram_width := HistWidth }, +                    {ok, {HistStart, _UnscannedBytes, ByOrigin}}) -> +    AllHistograms = lists:flatten([maps:values(ByType) || +                                   ByType <- maps:values(ByOrigin)]), + +    %% Do the histograms look alright? +    HistogramSet = ordsets:from_list(AllHistograms), +    Verified = [H || H <- HistogramSet, +                tuple_size(H) =:= HistWidth, +                hist_sum(H) >= 1], +    [] = ordsets:subtract(HistogramSet, Verified), + +    %% Do we have at least as many blocks as we've generated? +    BlockCount = lists:foldl(fun(Hist, Acc) -> +                                 hist_sum(Hist) + Acc +                             end, 0, AllHistograms), +    GenTotalBlockCount = ?GENERATED_BLOCK_COUNT, +    GenSBCBlockCount = ?GENERATED_SBC_BLOCK_COUNT, +    if +        BlockCount < GenSBCBlockCount -> +            ct:fail("Found ~p blocks, required at least ~p (SB)." , +                    [BlockCount, GenSBCBlockCount]); +        BlockCount >= GenTotalBlockCount -> +            ct:pal("Found ~p blocks, expected at least ~p (SB + MB).", +                    [BlockCount, GenTotalBlockCount]); +        BlockCount < GenTotalBlockCount -> +            ct:pal("Found ~p blocks, expected at least ~p (SB + MB), but this " +                    "may be due to MBCs being skipped if they're about to be " +                    "scanned just as they're fetched from the carrier pool.", +                    [BlockCount, GenTotalBlockCount]) +    end, + +    ok; +verify_allocations_output(#{}, {error, not_enabled}) -> +    ok. + +%% %% %% %% %% %% + +carriers_test(Args, Plain, PerAlloc) -> +    run_test(Args, fun(Node) -> +                 ok = rpc:call(Node, ?MODULE, test_all_alloc, +                               [fun instrument:carriers/0, Plain]), +                 ok = rpc:call(Node, ?MODULE, test_per_alloc, +                               [fun instrument:carriers/1, PerAlloc]), +                 ok = rpc:call(Node, ?MODULE, test_format, +                               [#{ histogram_start => 1024, +                                   histogram_width => 4 }, +                                fun instrument:carriers/1, +                                fun verify_carriers_output/2]), +                 ok = rpc:call(Node, ?MODULE, test_abort, +                               [fun erts_internal:gather_carrier_info/1]) +             end). + +carriers_enabled(Config) when is_list(Config) -> +    carriers_test("+Meamax", +                  fun verify_carriers_enabled/1, +                  fun verify_carriers_enabled/2). + +carriers_disabled(Config) when is_list(Config) -> +    carriers_test("+Meamin", +                  fun verify_carriers_disabled/1, +                  fun verify_carriers_disabled/2). + +verify_carriers_disabled(_AllocType, Result) -> +    verify_carriers_disabled(Result). + +verify_carriers_disabled({error, not_enabled}) -> +    ok; +verify_carriers_disabled({ok, {_HistStart, Carriers}}) -> +    verify_carriers_disabled_1(Carriers). + +verify_carriers_disabled_1([]) -> +    ok; +%% literal_alloc, exec_alloc, and temp_alloc can't be disabled, so we have to +%% accept their presence in carriers_disabled/test_all_alloc. +verify_carriers_disabled_1([Carrier | Rest]) when +        element(1, Carrier) =:= literal_alloc; +        element(1, Carrier) =:= exec_alloc; +        element(1, Carrier) =:= temp_alloc -> +    verify_carriers_disabled_1(Rest). + +%% exec_alloc only has a carrier if it's actually used. +verify_carriers_enabled(exec_alloc, _Result) -> ok; +verify_carriers_enabled(_AllocType, Result) -> verify_carriers_enabled(Result). + +verify_carriers_enabled({ok, {_HistStart, Carriers}}) when Carriers =/= [] -> +    ok. + +verify_carriers_output(#{ histogram_start := HistStart, +                          histogram_width := HistWidth }, +                       {ok, {HistStart, AllCarriers}}) -> + +    %% Do the carriers look alright? +    CarrierSet = ordsets:from_list(AllCarriers), +    Verified = [C || {AllocType, +                      TotalSize, +                      UnscannedSize, +                      AllocatedSize, +                      AllocatedCount, +                      InPool, +                      FreeBlockHist} = C <- CarrierSet, +                is_atom(AllocType), +                is_integer(TotalSize), TotalSize >= 1, +                is_integer(UnscannedSize), UnscannedSize < TotalSize, +                                           UnscannedSize >= 0, +                is_integer(AllocatedSize), AllocatedSize < TotalSize, +                                           AllocatedSize >= 0, +                is_integer(AllocatedCount), AllocatedCount =< AllocatedSize, +                                            AllocatedCount >= 0, +                is_boolean(InPool), +                tuple_size(FreeBlockHist) =:= HistWidth, +                carrier_block_check(AllocatedCount, FreeBlockHist)], +    [] = ordsets:subtract(CarrierSet, Verified), + +    %% Do we have at least as many carriers as we've generated? +    CarrierCount = length(AllCarriers), +    GenSBCCount = ?GENERATED_SBC_BLOCK_COUNT, +    if +        CarrierCount < GenSBCCount -> +            ct:fail("Carrier count is ~p, expected at least ~p (SBC).", +                [CarrierCount, GenSBCCount]); +        CarrierCount >= GenSBCCount -> +            ok      end, -    lists:foldl( -      fun ({TDescr,Addr,Size,Proc}, MinAddr) -> -              true = TDescr /= invalid_type, -              true = is_integer(TDescr), -              true = is_integer(Addr), -              true = is_integer(Size), -              true = Addr >= MinAddr, -              case Proc of -                  {0, Number, Serial} -> -                      true = is_integer(Number), -                      true = is_integer(Serial); -                  undefined -> -                      ok; -                  BadProc -> -                      ct:fail({badproc, BadProc}) -              end, -              NextMinAddr = Addr+Size, -              true = NextMinAddr =< High, -              NextMinAddr -      end, Low, AL), -    {_, DAL} = instrument:descr(MDS), -    lists:foreach( -      fun ({TDescr,_,_,Proc}) -> -              true = TDescr /= invalid_type, -              true = is_atom(TDescr) orelse is_list(TDescr), -              true = is_pid(Proc) orelse Proc == undefined -      end, DAL), -    ASL = lists:map(fun ({_,A,S,_}) -> {A,S} end, AL), -    ASL = lists:map(fun ({_,A,S,_}) -> {A,S} end, DAL), -    instrument:holes(MDS), -    {comment, "total status - sum of blocks = " ++ integer_to_list(S1-SumBlocks)}. - -%% Check that memory data can be read and processed -'+Mis true'(Config) when is_list(Config) -> -    Node = start_slave("+Mis true"), -    [{total,[{sizes,S1,S2,S3},{blocks,B1,B2,B3}]}] -	= rpc:call(Node, instrument, memory_status, [total]), -    true = S1 =< S2, -    true = S2 =< S3, -    true = B1 =< B2, -    true = B2 =< B3, -    true = is_list(rpc:call(Node,instrument,memory_status,[allocators])), -    true = is_list(rpc:call(Node,instrument,memory_status,[classes])), -    true = is_list(rpc:call(Node,instrument,memory_status,[types])), + +    ok; +verify_carriers_output(#{}, {error, not_enabled}) -> +    ok. + +carrier_block_check(AllocCount, FreeHist) -> +    %% A carrier must contain at least one block, and th. number of free blocks +    %% must not exceed the number of allocated blocks + 1. +    FreeCount = hist_sum(FreeHist), + +    (AllocCount + FreeCount) >= 1 andalso FreeCount =< (AllocCount + 1). + +%% %% %% %% %% %% + +test_all_alloc(Gather, Verify) -> +    Verify(Gather()),      ok. +test_per_alloc(Gather, Verify) -> +    [begin +         Verify(T, Gather(#{ allocator_types => [T] })) +     end || T <- erlang:system_info(alloc_util_allocators)], +    ok. + +test_format(#{ allocator_types := _ }, _, _) -> +    error(badarg); +test_format(Options0, Gather, Verify) -> +    %% We limit format checking to binary_alloc since we generated the test +    %% vectors there. +    Options = Options0#{ allocator_types => [binary_alloc] }, +    Verify(Options, Gather(Options)), +    ok. + +test_abort(Gather) -> +    %% There's no way for us to tell whether this actually aborted or ran to +    %% completion, but it might catch a few segfaults. +    Runner = self(), +    Ref = make_ref(), +    spawn_opt(fun() -> +                  [Gather({Type, SchedId, 1, 1, Ref}) || +                   Type <- erlang:system_info(alloc_util_allocators), +                   SchedId <- lists:seq(0, erlang:system_info(schedulers))], +                  Runner ! Ref +              end, [{priority, max}]), +    receive +        Ref -> ok +    end. + +hist_sum(H) -> hist_sum_1(H, tuple_size(H), 0). +hist_sum_1(_H, 0, A) -> A; +hist_sum_1(H, N, A) -> hist_sum_1(H, N - 1, element(N, H) + A). + +%% + +run_test(Args0, Test) -> +    %% Override single-block carrier threshold for binaries to ensure we have +    %% coverage for that path. generate_test_blocks builds a few binaries that +    %% crosses this threshold. +    %% +    %% We also set the abandon carrier threshold to 70% to provoke more +    %% activity in the carrier pool. +    Args = Args0 ++ " +MBsbct 1 +Muacul 70", +    Node = start_slave(Args), + +    ok = rpc:call(Node, ?MODULE, generate_test_blocks, []), +    ok = Test(Node), + +    ok = rpc:call(Node, ?MODULE, churn_memory, []), +    ok = Test(Node), + +    true = test_server:stop_node(Node). +  start_slave(Args) ->      MicroSecs = erlang:monotonic_time(),      Name = "instr" ++ integer_to_list(MicroSecs), @@ -112,6 +297,60 @@ start_slave(Args) ->                                          [{args, "-pa " ++ Pa ++ " " ++ Args}]),      Node. +generate_test_blocks() -> +    Runner = self(), +    Ref = make_ref(), +    spawn(fun() -> +              %% We've set the single-block carrier threshold to 1KB so one +              %% ought to land in a SBC and the other in a MBC. Both are kept +              %% alive forever. +              SBCs = [<<I, 0:(1 bsl 10)/unit:8>> || +                      I <- lists:seq(1, ?GENERATED_SBC_BLOCK_COUNT)], +              MBCs = [<<I, 0:64/unit:8>> || +                      I <- lists:seq(1, ?GENERATED_MBC_BLOCK_COUNT)], +              Runner ! Ref, +              receive after infinity -> ok end, +              unreachable ! {SBCs, MBCs} +          end), +    receive +        Ref -> ok +    end. -stop_slave(Node) -> -    true = test_server:stop_node(Node). +churn_memory() -> +    %% All processes spawned from here on have 'low' priority to avoid starving +    %% others (e.g. the rpc process) which could cause the test to time out. +    [begin +         churn_list_to_binary(), +         churn_processes(), +         churn_ets() +     end || _ <- lists:seq(1, erlang:system_info(schedulers))], +    ok. + +churn_processes() -> +    Pid = spawn_opt(fun churn_processes/0, [{priority, low}]), +    [Pid ! <<I, 0:128/unit:8>> || I <- lists:seq(1, 128)]. + +%% Nearly all types have a few allocations at all times but sl_alloc is +%% often empty. list_to_binary on large inputs will yield and spill the +%% state into an 'estack' which is allocated through sl_alloc. +%% +%% This is inherently unstable so we skip the verification step for this +%% type, but there's still a point to hammering it. +churn_list_to_binary() -> +    List = binary_to_list(<<0:(1 bsl 20)/unit:8>>), +    spawn_opt(fun() -> churn_list_to_binary_1(List) end, [{priority, low}]). + +churn_list_to_binary_1(List) -> +    _ = id(list_to_binary(List)), +    churn_list_to_binary_1(List). + +churn_ets() -> +    spawn_opt(fun() -> churn_ets_1(ets:new(gurka, [])) end, [{priority, low}]). + +churn_ets_1(Tab) -> +    ets:insert(Tab, {gaffel, lists:seq(1, 16)}), +    ets:delete_all_objects(Tab), +    churn_ets_1(Tab). + +id(I) -> +    I. diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl index a79572a742..8a2f6bfc89 100644 --- a/lib/tools/test/lcnt_SUITE.erl +++ b/lib/tools/test/lcnt_SUITE.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2010-2017. All Rights Reserved. +%% Copyright Ericsson AB 2010-2018. All Rights Reserved.  %%  %% Licensed under the Apache License, Version 2.0 (the "License");  %% you may not use this file except in compliance with the License. diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl index d651cbcfee..da4f56c09b 100644 --- a/lib/tools/test/xref_SUITE.erl +++ b/lib/tools/test/xref_SUITE.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2000-2017. All Rights Reserved. +%% Copyright Ericsson AB 2000-2018. All Rights Reserved.  %%  %% Licensed under the Apache License, Version 2.0 (the "License");  %% you may not use this file except in compliance with the License. @@ -1112,27 +1112,16 @@ read_expected(Version) ->            {POS7+2,{FF,{erlang,spawn_opt,4}}},            {POS8+1,{FF,{hej,san,1}}},            {POS8+4,{FF,{a,b,1}}}, -          {POS8+4,{FF,{erlang,apply,2}}}, -          {POS8+5,{FF,{erlang,apply,2}}},            {POS8+6,{FF,{m,f,1}}},            {POS9+1,{FF,{read,bi,0}}},            {POS9+2,{FF,{a,b,1}}}, -          {POS9+2,{FF,{erlang,apply,2}}}, -          {POS9+3,{FF,{erlang,apply,2}}}, -          {POS9+4,{FF,{erlang,apply,2}}},            {POS9+4,{FF,{erlang,not_a_function,1}}},            {POS9+5,{FF,{mod,func,2}}},            {POS9+6,{FF,{erlang,apply,1}}}, -          {POS9+7,{FF,{erlang,apply,2}}},            {POS9+7,{FF,{math,add3,1}}},            {POS9+8,{FF,{q,f,1}}}, -          {POS10+4,{FF,{erlang,apply,2}}},            {POS10+5,{FF,{mod1,fun1,1}}}, -          {POS11+6,{FF,{erlang,apply,2}}}, -          {POS12+1,{FF,{erlang,apply,2}}}, -          {POS12+4,{FF,{erlang,apply,2}}},            {POS12+5,{FF,{m3,f3,2}}}, -          {POS12+7,{FF,{erlang,apply,2}}},            {POS13+1,{FF,{dm,df,1}}},            {POS13+6,{{read,bi,0},{foo,module_info,0}}},            {POS13+7,{{read,bi,0},{read,module_info,0}}}, @@ -1162,15 +1151,26 @@ read_expected(Version) ->              {POS3+3,  {FF,{erlang,spawn_link,3}}},              {POS3+4, {FF,{erlang,spawn_link,3}}},              {POS6+4, {FF,{erlang,spawn,3}}}, +            {POS8+4,{FF,{erlang,apply,2}}}, +            {POS8+5,{FF,{erlang,apply,2}}},              {POS8+6,{FF,{erlang,apply,3}}},              {POS8+7,{FF,{erlang,apply,3}}},              {POS9+1,{FF,{erlang,apply,3}}}, +            {POS9+2,{FF,{erlang,apply,2}}}, +            {POS9+3,{FF,{erlang,apply,2}}}, +            {POS9+4,{FF,{erlang,apply,2}}},              {POS9+5,{FF,{erlang,apply,3}}}, +            {POS9+7,{FF,{erlang,apply,2}}}, +            {POS10+4,{FF,{erlang,apply,2}}},              {POS11+1,{FF,{erlang,apply,3}}},              {POS11+2,{FF,{erlang,apply,3}}},              {POS11+3,{FF,{erlang,apply,3}}},              {POS11+4,{FF,{erlang,apply,3}}}, +            {POS11+6,{FF,{erlang,apply,2}}}, +            {POS12+1,{FF,{erlang,apply,2}}}, +            {POS12+4,{FF,{erlang,apply,2}}},              {POS12+5,{FF,{erlang,apply,3}}}, +            {POS12+7,{FF,{erlang,apply,2}}},              {POS12+8,{FF,{erlang,apply,3}}},              {POS13+5, {{read,bi,0},{erlang,length,1}}},              {POS14+3, {{read,bi,0},{erlang,length,1}}}], @@ -2233,18 +2233,18 @@ variables(Conf) when is_list(Conf) ->      {{error, _, _}, _} = xref_base:variables(S108, [{verbose,false}]),      {ok, S109} = xref_base:set_library_path(S108, [], [{verbose,false}]), -    Tabs = length(ets:all()), +    NoOfTables = erlang:system_info(ets_count),      {ok, S110} = eval("Eplus := closure E, TT := Eplus",                        'closure()', S109),      {{ok, [{user, ['Eplus','TT']}]}, S111} = xref_base:variables(S110),      {ok, S112} = xref_base:forget(S111, ['TT','Eplus']), -    true = Tabs =:= length(ets:all()), +    true = NoOfTables =:= erlang:system_info(ets_count),      {ok, NS0} = eval("Eplus := closure E", 'closure()', S112),      {{ok, [{user, ['Eplus']}]}, NS} = xref_base:variables(NS0),      ok = xref_base:delete(NS), -    true = Tabs =:= length(ets:all()), +    true = NoOfTables =:= erlang:system_info(ets_count),      ok = file:delete(Beam),      ok. diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk index f9723c0f9b..bb8305e9f1 100644 --- a/lib/tools/vsn.mk +++ b/lib/tools/vsn.mk @@ -1 +1 @@ -TOOLS_VSN = 2.11.2 +TOOLS_VSN = 3.0.2 | 
