aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tools
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tools')
-rw-r--r--lib/tools/doc/src/lcnt.xml116
-rw-r--r--lib/tools/doc/src/lcnt_chapter.xml41
-rw-r--r--lib/tools/doc/src/make.xml2
-rw-r--r--lib/tools/doc/src/notes.xml57
-rw-r--r--lib/tools/emacs/erlang-test.el2
-rw-r--r--lib/tools/emacs/erlang.el51
-rw-r--r--lib/tools/emacs/erldoc.el2
-rw-r--r--lib/tools/src/cover.erl42
-rw-r--r--lib/tools/src/eprof.erl30
-rw-r--r--lib/tools/src/fprof.erl36
-rw-r--r--lib/tools/src/lcnt.erl79
-rw-r--r--lib/tools/src/make.erl50
-rw-r--r--lib/tools/src/tools.app.src4
-rw-r--r--lib/tools/src/xref_base.erl22
-rw-r--r--lib/tools/src/xref_parser.yrl6
-rw-r--r--lib/tools/src/xref_reader.erl2
-rw-r--r--lib/tools/src/xref_utils.erl13
-rw-r--r--lib/tools/test/Makefile2
-rw-r--r--lib/tools/test/lcnt_SUITE.erl86
-rw-r--r--lib/tools/test/make_SUITE.erl45
-rw-r--r--lib/tools/test/xref_SUITE.erl40
-rw-r--r--lib/tools/vsn.mk2
22 files changed, 557 insertions, 173 deletions
diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml
index 9c8ce148e9..5bdfc60448 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>2016</year>
+ <year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -38,7 +38,7 @@
<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
- Erlang Runtime System. With <c>lcnt</c> enabled, Internal counters in the
+ Erlang Runtime System. With <c>lcnt</c> enabled, internal counters in the
runtime system are updated each time a lock is taken. The counters stores
information about the number of acquisition tries and the number of collisions
that has occurred during the acquisition tries. The counters also record the
@@ -47,17 +47,17 @@
<p>
The data produced by the lock counters will give an estimate on how well
the runtime system will behave from a parallelizable view point for the
- scenarios tested. This tool was mainly developed to help erlang runtime
+ scenarios tested. This tool was mainly developed to help Erlang runtime
developers iron out potential and generic bottlenecks.
</p>
<p>Locks in the emulator are named after what type of resource they protect and where
in the emulator they are initialized, those are lock 'classes'. Most of those
locks are also instantiated several times, and given unique identifiers, to increase
locking granularity. Typically an instantiated lock protects a disjunct set of
- the resource, i.e ets-tables, processes or ports. In other cases it protects a
- specific range of a resource, e.g. <c>pix_lock</c> which protects index to process
+ the resource, for example ets tables, processes or ports. In other cases it protects a
+ specific range of a resource, for example <c>pix_lock</c> which protects index to process
mappings, and is given a unique number within the class. A unique lock in <c>lcnt</c>
- is referenced by a name (class) and an identifier, <c>{Name, Id}</c>.
+ is referenced by a name (class) and an identifier: <c>{Name, Id}</c>.
</p>
<p>Some locks in the system are static and protects global resources, for example
<c>bif_timers</c> and the <c>run_queue</c> locks. Other locks are dynamic and not
@@ -109,14 +109,6 @@
statistics. If the server held any lock statistics data before the collect then
that data is lost.
</p>
- <note>
- <p>
- When collection occurs the runtime system transitions to a single thread,
- blocking all other threads. No other tasks will be scheduled during this
- operation. Depending on the size of the data this might take a long time
- (several seconds) and cause timeouts in the system.
- </p>
- </note>
</desc>
</func>
@@ -322,24 +314,22 @@
<func>
<name>apply(Fun) -> term()</name>
<fsummary>Same as <c>apply(Fun, [])</c>.</fsummary>
+ <type>
+ <v>Fun = fun()</v>
+ </type>
<desc>
<p>Same as <c>apply(Fun, [])</c>.</p>
</desc>
</func>
<func>
<name>apply(Fun, Args) -> term()</name>
- <fsummary>Clears counters, applies function and collects the profiling results.</fsummary>
+ <fsummary>Same as <c>apply(Module, Function, Args)</c>.</fsummary>
<type>
<v>Fun = fun()</v>
<v>Args = [term()]</v>
</type>
<desc>
- <p> Clears the lock counters and then setups the instrumentation to save all destroyed locks.
- After setup the fun is called, passing the elements in <c>Args</c> as arguments.
- When the fun returns the statistics are immediately collected to the server. After the
- collection the instrumentation is returned to its previous behavior.
- The result of the applied fun is returned.
- </p>
+ <p>Same as <c>apply(Module, Function, Args)</c>.</p>
</desc>
</func>
<func>
@@ -357,6 +347,13 @@
collection the instrumentation is returned to its previous behavior.
The result of the applied function is returned.
</p>
+ <warning>
+ <p>
+ This function should only be used for micro-benchmarks; it sets <c>copy_save</c>
+ to <c>true</c> for the duration of the call, which can quickly lead to running
+ out of memory.
+ </p>
+ </warning>
</desc>
</func>
@@ -429,6 +426,68 @@
<desc> <p>Clear the internal counters. Same as <c>lcnt:clear(Node)</c>.</p></desc>
</func>
+ <func>
+ <name>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>
+ <fsummary>Returns the current lock category mask.</fsummary>
+ <type>
+ <v>Node = node()</v>
+ </type>
+ <desc>
+ <p>
+ Refer to <c>rt_mask/2</c> for a list of valid categories. All
+ categories are enabled by default.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>rt_mask(Categories) -> ok | {error, copy_save_enabled}</name>
+ <fsummary>Same as <c>rt_mask(node(), Categories)</c>.</fsummary>
+ <type>
+ <v>Categories = [atom()]</v>
+ </type>
+ <desc><p>Same as <c>rt_mask(node(), Categories)</c>.</p></desc>
+ </func>
+
+ <func>
+ <name>rt_mask(Node, Categories) -> ok | {error, copy_save_enabled}</name>
+ <fsummary>Changes the lock category mask.</fsummary>
+ <type>
+ <v>Node = node()</v>
+ <v>Categories = [atom()]</v>
+ </type>
+ <desc>
+ <p>
+ Sets the lock category mask to the given categories.
+ </p>
+ <p>
+ This will fail if the <c>copy_save</c> option is enabled; see
+ <c>lcnt:rt_opt/2</c>.
+ </p>
+ <p>Valid categories are:</p>
+ <list>
+ <item><c>allocator</c></item>
+ <item><c>db</c> (ETS tables)</item>
+ <item><c>debug</c></item>
+ <item><c>distribution</c></item>
+ <item><c>generic</c></item>
+ <item><c>io</c></item>
+ <item><c>process</c></item>
+ <item><c>scheduler</c></item>
+ </list>
+ <p>
+ This list is subject to change at any time, as is the category any given lock
+ may belong to.
+ </p>
+ </desc>
+ </func>
+
<func>
<name>rt_opt({Type, bool()}) -> bool()</name>
<fsummary>Same as <c>rt_opt(node(), {Type, Opt})</c>.</fsummary>
@@ -442,16 +501,25 @@
<v>Type = copy_save | process_locks</v>
</type>
<desc>
- <p>Changes the lock counter behavior and returns the previous behaviour.</p>
<p>Option description:</p>
<taglist>
<tag><c>{copy_save, bool()}</c></tag>
- <item>Enable statistics saving from destroyed locks by copying. This might consume a lot of memory.
+ <item>Retains the statistics of destroyed locks.
<br/>Default: <c>false</c>
+ <warning>
+ <p>
+ This option will use a lot of memory when enabled, which must be
+ reclaimed with <c>lcnt:rt_clear</c>. Note that it makes no distinction
+ between locks that were destroyed and locks for which counting was
+ disabled, so enabling this option will disable changes to the lock
+ category mask.
+ </p>
+ </warning>
</item>
<tag><c>{process_locks, bool()}</c></tag>
- <item>Profile process locks.
+ <item>Profile process locks, equal to adding <c>process</c> to the lock category mask;
+ see <c>lcnt:rt_mask/2</c>
<br/>Default: <c>true</c>
</item>
</taglist>
diff --git a/lib/tools/doc/src/lcnt_chapter.xml b/lib/tools/doc/src/lcnt_chapter.xml
index 6cfdb5cf1b..24b58136aa 100644
--- a/lib/tools/doc/src/lcnt_chapter.xml
+++ b/lib/tools/doc/src/lcnt_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2009</year><year>2016</year>
+ <year>2009</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,7 +29,7 @@
<approved>nobody</approved>
<checked>no</checked>
<date>2009-11-26</date>
- <rev>PA1</rev>
+ <rev>PA2</rev>
<file>lcnt_chapter.xml</file>
</header>
<p>
@@ -42,42 +42,32 @@
completed its access to the resource and unlocked it. The <c>lcnt</c> tool measures these lock conflicts.
</p>
<p>
- Locks has an inherent cost in execution time and memory space. It takes time initialize, destroy, aquiring or releasing locks. To decrease lock contention it
+ Locks have an inherent cost in execution time and memory space. It takes time initialize, destroy, aquiring or releasing locks. To decrease lock contention it
some times necessary to use finer grained locking strategies. This will usually also increase the locking overhead and hence there is a tradeoff
between lock contention and overhead. In general, lock contention increases with the number of threads running concurrently. The <c>lcnt</c> tool does not measure locking overhead.
</p>
<section>
<title> Enabling lock-counting </title>
- <p>For investigation of locks in the emulator we use an internal tool called <c>lcnt</c> (short for lock-count). The VM needs to be compiled with this option enabled. To enable this, use:</p>
+ <p>For investigation of locks in the emulator we use an internal tool called <c>lcnt</c> (short for lock-count). The VM needs to be compiled with this option enabled.
+ To compile a lock-counting VM along with a normal VM, use:</p>
<pre>
cd $ERL_TOP
-./configure --enable-lock-counter
- </pre>
-
- <p>
- Another way to enable this alongside a normal VM is to compile it at emulator directory level, much like a debug build. To compile it this way do the following,
- </p>
- <pre>
-cd $ERL_TOP/erts/emulator
-make lcnt FLAVOR=smp
- </pre>
- <p> and then starting Erlang with,</p>
+./configure --enable-lock-counter</pre>
+ <p>Start the lock-counting VM like this:</p>
<pre>
-$ERL_TOP/bin/cerl -lcnt
- </pre>
- <p>To verify that you lock-counting enabled check that <c>[lock-counting]</c> appears in the status text when the VM is started.</p>
+$ERL_TOP/bin/erl -emu_type lcnt</pre>
+ <p>To verify that lock counting is enabled check that <c>[lock-counting]</c> appears in the status text when the VM is started.</p>
<pre>
-Erlang R13B03 (erts-5.7.4) [source] [64-bit] [smp:8:8] [rq:8] [async-threads:0] [hipe]
- [kernel-poll:false] [lock-counting]
- </pre>
+Erlang/OTP 20 [erts-9.0] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe]
+ [kernel-poll:false] [lock-counting]</pre>
</section>
<section>
<title>Getting started</title>
<p>Once you have a lock counting enabled VM the module <c>lcnt</c> can be used. The module is intended to be used from the current running nodes shell. To access remote nodes use <c>lcnt:clear(Node)</c> and <c>lcnt:collect(Node)</c>. </p>
<p>All locks are continuously monitored and its statistics updated. Use <c>lcnt:clear/0</c> to initially clear all counters before running any specific tests. This command will also reset the duration timer internally.</p>
- <p>To retrieve lock statistics information use, <c>lcnt:collect/0,1</c>. The collect operation will start a <c>lcnt</c> server if it not already started. All collected data will be built into an erlang term and uploaded to the server and a duration time will also be uploaded. This duration is the time between <c>lcnt:clear/0,1</c> and <c>lcnt:collect/0,1</c>.</p>
+ <p>To retrieve lock statistics information, use <c>lcnt:collect/0,1</c>. The collect operation will start a <c>lcnt</c> server if it not already started. All collected data will be built into an Erlang term and uploaded to the server and a duration time will also be uploaded. This duration is the time between <c>lcnt:clear/0,1</c> and <c>lcnt:collect/0,1</c>.</p>
<p>Once the data is collected to the server it can be filtered, sorted and printed in many different ways.</p>
<p>See the <seealso marker="lcnt">reference manual</seealso> for a description of each function.</p>
@@ -107,8 +97,11 @@ ok
ok
</pre>
<p>
- Another way to to profile a specific function is to use <c>lcnt:apply/3</c> or <c>lcnt:apply/1</c> which does <c>lcnt:clear/0</c> before the function and <c>lcnt:collect/0</c> after its invocation.
- It also sets <c>copy_save</c> to <c>true</c> for the duration of the function call
+ Another way to to profile a specific function is to use <c>lcnt:apply/3</c> or <c>lcnt:apply/1</c>
+ which does <c>lcnt:clear/0</c> before the function and <c>lcnt:collect/0</c> after its invocation.
+ This method should only be used in micro-benchmarks since it sets <c>copy_save</c> to <c>true</c>
+ for the duration of the function call, which may cause the emulator to run out of memory if
+ attempted under load.
</p>
<pre>
Erlang R13B03 (erts-5.7.4) [source] [smp:8:8] [rq:8] [async-threads:0] [hipe]
diff --git a/lib/tools/doc/src/make.xml b/lib/tools/doc/src/make.xml
index 6b878f72fb..123fcd4afc 100644
--- a/lib/tools/doc/src/make.xml
+++ b/lib/tools/doc/src/make.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2016</year>
+ <year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index af20200d49..f0df43bf2b 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>2016</year>
+ <year>2004</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,61 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.10.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ In OTP-20.0, the behavior of c, make, and ct_make was
+ changed so that in some cases the beam files by default
+ would be written to the directory where the source files
+ were found. This is now changed back to the old behavior
+ so beam files are by default written to current
+ directory.</p>
+ <p>
+ Own Id: OTP-14489 Aux Id: ERL-438 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Tools 2.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ In some situations, <c>make:all()</c> and friends did not
+ detect changes in include files located in the current
+ directory. This is now corrected.</p>
+ <p>
+ Own Id: OTP-14339 Aux Id: ERL-395 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>The <c>make</c> module now accepts the
+ <c>{emake,Emake}</c> option.</p>
+ <p>
+ Own Id: OTP-14253</p>
+ </item>
+ <item>
+ <p> Miscellaneous updates due to atoms containing
+ arbitrary Unicode characters. </p>
+ <p>
+ Own Id: OTP-14285</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.9.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/tools/emacs/erlang-test.el b/lib/tools/emacs/erlang-test.el
index ea5d637199..efe3d515e9 100644
--- a/lib/tools/emacs/erlang-test.el
+++ b/lib/tools/emacs/erlang-test.el
@@ -8,7 +8,7 @@
;; %CopyrightBegin%
;;
-;; Copyright Ericsson AB 2016. All Rights Reserved.
+;; Copyright Ericsson AB 2016-2017. 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/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 527915f724..411e0e13df 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -9,7 +9,7 @@
;; %CopyrightBegin%
;;
-;; Copyright Ericsson AB 1996-2016. All Rights Reserved.
+;; Copyright Ericsson AB 1996-2017. 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.
@@ -931,6 +931,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"has_prepared_code_on_load"
"hibernate"
"insert_element"
+ "iolist_to_iovec"
"is_builtin"
"load_nif"
"loaded"
@@ -2070,12 +2071,6 @@ This function is aware of imported functions."
(when funcname
(erlang-man-find-function (current-buffer) funcname))))))
-(defun erlang-default-function-or-module ()
- (let ((id (erlang-get-identifier-at-point)))
- (if (eq (erlang-id-kind id) 'qualified-function)
- (format "%s:%s" (erlang-id-module id) (erlang-id-name id))
- (erlang-id-name id))))
-
;; Should the defadvice be at the top level, the package `advice' would
;; be required. Now it is only required when this functionality
@@ -3621,8 +3616,10 @@ The return value is a string of the form \"foo/1\"."
(error nil)))))
-;; Keeping erlang-get-function-under-point for backward compatibility.
-;; It is used by erldoc.el and maybe other code out there.
+;; erlang-get-function-under-point is replaced by
+;; erlang-get-identifier-at-point as far as internal erlang.el usage
+;; is concerned. But it is kept for backward compatibility. It is
+;; used by erldoc.el and maybe other code out there.
(defun erlang-get-function-under-point ()
"Return the module and function under the point, or nil.
@@ -3769,6 +3766,21 @@ of arguments could be found, otherwise nil."
(nth 3 (erlang-id-to-list id)))
+(defun erlang-default-function-or-module ()
+ (erlang-with-id (kind module name) (erlang-get-identifier-at-point)
+ (let ((x (cond ((eq kind 'module)
+ (format "%s:" name))
+ ((eq kind 'record)
+ (format "-record(%s" name))
+ ((eq kind 'macro)
+ (format "-define(%s" name))
+ (t
+ name))))
+ (if module
+ (format "%s:%s" module x)
+ x))))
+
+
;; TODO: Escape single quotes inside the string without
;; replace-regexp-in-string.
(defun erlang-add-quotes-if-needed (str)
@@ -4685,9 +4697,10 @@ for a tag on the form `module:tag'."
(if (fboundp 'advice-add)
;; Emacs 24.4+
- (require 'etags)
- (advice-add 'etags-tags-completion-table :around
- #'erlang-etags-tags-completion-table-advice)
+ (progn
+ (require 'etags)
+ (advice-add 'etags-tags-completion-table :around
+ #'erlang-etags-tags-completion-table-advice))
;; Emacs 23.1-24.3
(defadvice etags-tags-completion-table (around
erlang-replace-tags-table
@@ -4859,7 +4872,12 @@ considered first when it is time to jump to the definition.")
'(progn
(cl-defmethod xref-backend-identifier-at-point
((_backend (eql erlang-etags)))
- (erlang-id-to-string (erlang-get-identifier-at-point)))
+ (if (eq this-command 'xref-find-references)
+ (if (use-region-p)
+ (buffer-substring-no-properties (region-beginning)
+ (region-end))
+ (thing-at-point 'symbol))
+ (erlang-id-to-string (erlang-get-identifier-at-point))))
(cl-defmethod xref-backend-definitions
((_backend (eql erlang-etags)) identifier)
@@ -4970,9 +4988,10 @@ considered first when it is time to jump to the definition.")
(and (fboundp 'xref-make)
(fboundp 'xref-make-file-location)
(let* ((first-time t)
+ (cbuf (current-buffer))
xrefs matching-files)
(save-excursion
- (while (visit-tags-table-buffer (not first-time))
+ (while (erlang-visit-tags-table-buffer (not first-time) cbuf)
(setq first-time nil)
(let ((files (tags-table-files)))
(while files
@@ -4988,6 +5007,10 @@ considered first when it is time to jump to the definition.")
(setq files (cdr files))))))
(nreverse xrefs))))
+(defun erlang-visit-tags-table-buffer (cont cbuf)
+ (if (< emacs-major-version 26)
+ (visit-tags-table-buffer cont)
+ (visit-tags-table-buffer cont cbuf)))
(defun erlang-xref-find-definitions-module-tag (module
tag
diff --git a/lib/tools/emacs/erldoc.el b/lib/tools/emacs/erldoc.el
index 348800f880..770ab299ee 100644
--- a/lib/tools/emacs/erldoc.el
+++ b/lib/tools/emacs/erldoc.el
@@ -2,7 +2,7 @@
;; %CopyrightBegin%
;;
-;; Copyright Ericsson AB 2016. All Rights Reserved.
+;; Copyright Ericsson AB 2016-2017. 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/src/cover.erl b/lib/tools/src/cover.erl
index e2db4f0148..5517882ffa 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2017. 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.
@@ -2414,8 +2414,8 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->
{ok, InFd} ->
case file:open(OutFile, [write,raw,delayed_write]) of
{ok, OutFd} ->
+ Enc = encoding(ErlFile),
if HTML ->
- Encoding = encoding(ErlFile),
Header =
["<!DOCTYPE HTML PUBLIC "
"\"-//W3C//DTD HTML 3.2 Final//EN\">\n"
@@ -2423,13 +2423,14 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->
"<head>\n"
"<meta http-equiv=\"Content-Type\""
" content=\"text/html; charset=",
- Encoding,"\"/>\n"
+ html_encoding(Enc),"\"/>\n"
"<title>",OutFile,"</title>\n"
"</head>"
"<body style='background-color: white;"
" color: black'>\n"
"<pre>\n"],
- ok = file:write(OutFd,Header);
+ H1Bin = unicode:characters_to_binary(Header,Enc,Enc),
+ ok = file:write(OutFd,H1Bin);
true -> ok
end,
@@ -2443,12 +2444,15 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->
string:right(integer_to_list(H), 2, $0),
string:right(integer_to_list(Mi), 2, $0),
string:right(integer_to_list(S), 2, $0)]),
- ok = file:write(OutFd,
- ["File generated from ",ErlFile," by COVER ",
+
+ H2Bin = unicode:characters_to_binary(
+ ["File generated from ",ErlFile," by COVER ",
Timestamp,"\n\n"
"**************************************"
"**************************************"
- "\n\n"]),
+ "\n\n"],
+ Enc, Enc),
+ ok = file:write(OutFd, H2Bin),
Pattern = {#bump{module=Module,line='$1',_='_'},'$2'},
MS = [{Pattern,[{is_integer,'$1'},{'>','$1',0}],[{{'$1','$2'}}]}],
@@ -2752,16 +2756,22 @@ pmap_collect(Mons,Acc) ->
end.
%%%-----------------------------------------------------------------
-%%% Read encoding from source file
+%%% Decide which encoding to use when analyzing to file.
+%%% The target file contains the file path, so if either the file name
+%%% encoding or the encoding of the source file is utf8, then we need
+%%% to use utf8.
encoding(File) ->
- Encoding =
- case epp:read_encoding(File) of
- none ->
- epp:default_encoding();
- E ->
- E
- end,
- html_encoding(Encoding).
+ case file:native_name_encoding() of
+ latin1 ->
+ case epp:read_encoding(File) of
+ none ->
+ epp:default_encoding();
+ E ->
+ E
+ end;
+ utf8 ->
+ utf8
+ end.
html_encoding(latin1) ->
"iso-8859-1";
diff --git a/lib/tools/src/eprof.erl b/lib/tools/src/eprof.erl
index 3ae899a078..535ddbcd04 100644
--- a/lib/tools/src/eprof.erl
+++ b/lib/tools/src/eprof.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -246,7 +246,7 @@ handle_call(profile_stop, _From, #state{ profiling = true } = S) ->
%% logfile
handle_call({logfile, File}, _From, #state{ fd = OldFd } = S) ->
- case file:open(File, [write]) of
+ case file:open(File, [write, {encoding, utf8}]) of
{ok, Fd} ->
case OldFd of
undefined -> ok;
@@ -478,11 +478,11 @@ string_bp_mfa([{Mfa, {Count, Time}}|Mfas], Tus, {MfaW, CountW, PercW, TimeW, TpC
Stpc = s("~.2f", [divide(Time,Count)]),
string_bp_mfa(Mfas, Tus, {
- erlang:max(MfaW, length(Smfa)),
- erlang:max(CountW,length(Scount)),
- erlang:max(PercW, length(Sperc)),
- erlang:max(TimeW, length(Stime)),
- erlang:max(TpCW, length(Stpc))
+ erlang:max(MfaW, string:length(Smfa)),
+ erlang:max(CountW,string:length(Scount)),
+ erlang:max(PercW, string:length(Sperc)),
+ erlang:max(TimeW, string:length(Stime)),
+ erlang:max(TpCW, string:length(Stpc))
}, [[Smfa, Scount, Sperc, Stime, Stpc] | Strings]).
print_bp_mfa(Mfas, {Tn, Tus}, Fd, Opts) ->
@@ -491,11 +491,11 @@ print_bp_mfa(Mfas, {Tn, Tus}, Fd, Opts) ->
TnStr = s(Tn),
TusStr = s(Tus),
TuspcStr = s("~.2f", [divide(Tus,Tn)]),
- Ws = {erlang:max(length("FUNCTION"), MfaW),
- lists:max([length("CALLS"), CountW, length(TnStr)]),
- erlang:max(length(" %"), PercW),
- lists:max([length("TIME"), TimeW, length(TusStr)]),
- lists:max([length("uS / CALLS"), TpCW, length(TuspcStr)])},
+ Ws = {erlang:max(string:length("FUNCTION"), MfaW),
+ lists:max([string:length("CALLS"), CountW, string:length(TnStr)]),
+ erlang:max(string:length(" %"), PercW),
+ lists:max([string:length("TIME"), TimeW, string:length(TusStr)]),
+ lists:max([string:length("uS / CALLS"), TpCW, string:length(TuspcStr)])},
format(Fd, Ws, ["FUNCTION", "CALLS", " %", "TIME", "uS / CALLS"]),
format(Fd, Ws, ["--------", "-----", "-------", "----", "----------"]),
lists:foreach(fun (String) -> format(Fd, Ws, String) end, Strs),
@@ -503,13 +503,13 @@ print_bp_mfa(Mfas, {Tn, Tus}, Fd, Opts) ->
format(Fd, Ws, ["Total:", TnStr, "100.00%", TusStr, TuspcStr]),
ok.
-s({M,F,A}) -> s("~w:~w/~w",[M,F,A]);
-s(Term) -> s("~p", [Term]).
+s({M,F,A}) -> s("~w:~tw/~w",[M,F,A]);
+s(Term) -> s("~tp", [Term]).
s(Format, Terms) -> lists:flatten(io_lib:format(Format, Terms)).
format(Fd, {MfaW, CountW, PercW, TimeW, TpCW}, Strings) ->
- format(Fd, s("~~.~ps ~~~ps ~~~ps ~~~ps [~~~ps]~~n", [MfaW, CountW, PercW, TimeW, TpCW]), Strings);
+ format(Fd, s("~~.~wts ~~~ws ~~~ws ~~~ws [~~~ws]~~n", [MfaW, CountW, PercW, TimeW, TpCW]), Strings);
format(undefined, Format, Strings) ->
io:format(Format, Strings),
ok;
diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl
index d1a4624419..2fe42beb03 100644
--- a/lib/tools/src/fprof.erl
+++ b/lib/tools/src/fprof.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2017. 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.
@@ -1475,7 +1475,7 @@ info_suspect_call(GroupLeader, GroupLeader, _, _) ->
ok;
info_suspect_call(GroupLeader, _, Func, Pid) ->
io:format(GroupLeader,
- "~nWarning: ~p called in ~p - trace may become corrupt!~n",
+ "~nWarning: ~tp called in ~p - trace may become corrupt!~n",
parsify([Func, Pid])).
info(GroupLeader, GroupLeader, _, _) ->
@@ -1498,13 +1498,13 @@ dump_stack(Dump, Stack, Term) ->
{N, length(hd(Stack))}
end
end,
- io:format(Dump, "~s~p.~n", [lists:duplicate(Depth, " "), parsify(Term)]),
+ io:format(Dump, "~s~tp.~n", [lists:duplicate(Depth, " "), parsify(Term)]),
true.
dump(undefined, _) ->
false;
dump(Dump, Term) ->
- io:format(Dump, "~p.~n", [parsify(Term)]),
+ io:format(Dump, "~tp.~n", [parsify(Term)]),
true.
@@ -2603,17 +2603,17 @@ println({Io, [W1, W2, W3, W4]}, Head,
println({Io, _}, Head,
[],
Tail, Comment) ->
- io:format(Io, "~s~s~s~n",
+ io:format(Io, "~s~ts~ts~n",
[pad(Head, $ , 3), Tail, Comment]);
println({Io, _}, Head,
{Tag, Term},
Tail, Comment) ->
- io:format(Io, "~s~p, ~p~s~s~n",
+ io:format(Io, "~s~tp, ~tp~ts~ts~n",
[pad(Head, $ , 3), parsify(Tag), parsify(Term), Tail, Comment]);
println({Io, _}, Head,
Term,
Tail, Comment) ->
- io:format(Io, "~s~p~s~s~n",
+ io:format(Io, "~s~tp~ts~ts~n",
[pad(Head, $ , 3), parsify(Term), Tail, Comment]).
@@ -2636,22 +2636,32 @@ funcstat_pd(Pid, Func1, Func0, Clocks) ->
#funcstat{callers_sum = CallersSum,
callers = Callers} = FuncstatCallers ->
FuncstatCallers#funcstat{
- callers_sum = clocks_sum(CallersSum, Clocks, Func0),
- callers = [Clocks#clocks{id = Func1} | Callers]}
- end),
+ callers_sum = clocks_sum(CallersSum, Clocks, Func0),
+ callers = insert_call(Clocks, Func1, Callers)}
+ end),
put({Pid, Func1},
case get({Pid, Func1}) of
undefined ->
- #funcstat{callers_sum = #clocks{id = Func1},
+ #funcstat{callers_sum = #clocks{id = Func1},
called_sum = Clocks#clocks{id = Func1},
called = [Clocks#clocks{id = Func0}]};
#funcstat{called_sum = CalledSum,
called = Called} = FuncstatCalled ->
FuncstatCalled#funcstat{
called_sum = clocks_sum(CalledSum, Clocks, Func1),
- called = [Clocks#clocks{id = Func0} | Called]}
+ called = insert_call(Clocks, Func0, Called)}
end).
+insert_call(Clocks, Func, ClocksList) ->
+ insert_call(Clocks, Func, ClocksList, []).
+
+insert_call(Clocks, Func, [#clocks{id = Func} = C | T], Acc) ->
+ [clocks_sum(C, Clocks, Func) | T ++ Acc];
+insert_call(Clocks, Func, [H | T], Acc) ->
+ insert_call(Clocks, Func, T, [H | Acc]);
+insert_call(Clocks, Func, [], Acc) ->
+ [Clocks#clocks{id = Func} | Acc].
+
%% Sort a list of funcstat records,
@@ -2710,7 +2720,7 @@ postsort_r([[_|C] | L], R) ->
flat_format(F, Trailer) when is_float(F) ->
lists:flatten([io_lib:format("~.3f", [F]), Trailer]);
flat_format(W, Trailer) ->
- lists:flatten([io_lib:format("~p", [W]), Trailer]).
+ lists:flatten([io_lib:format("~tp", [W]), Trailer]).
%% Format, flatten, and pad.
flat_format(Term, Trailer, Width) ->
diff --git a/lib/tools/src/lcnt.erl b/lib/tools/src/lcnt.erl
index 864d62f6c5..139b3d8a4a 100644
--- a/lib/tools/src/lcnt.erl
+++ b/lib/tools/src/lcnt.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -34,8 +34,11 @@
-export([start/0,
stop/0]).
-%% erts_debug:lock_counters api
--export([rt_collect/0,
+%% erts_debug:lcnt_xxx api
+-export([rt_mask/0,
+ rt_mask/1,
+ rt_mask/2,
+ rt_collect/0,
rt_collect/1,
rt_clear/0,
rt_clear/1,
@@ -134,27 +137,61 @@ start_internal() ->
%% -------------------------------------------------------------------- %%
%%
-%% API erts_debug:lock_counters
+%% API erts_debug:lcnt_xxx
%%
%% -------------------------------------------------------------------- %%
-rt_collect() ->
- erts_debug:lock_counters(info).
+rt_mask(Node, Categories) when is_atom(Node), is_list(Categories) ->
+ rpc:call(Node, lcnt, rt_mask, [Categories]).
+
+rt_mask(Node) when is_atom(Node) ->
+ rpc:call(Node, lcnt, rt_mask, []);
+
+rt_mask(Categories) when is_list(Categories) ->
+ case erts_debug:lcnt_control(copy_save) of
+ false ->
+ erts_debug:lcnt_control(mask, Categories);
+ true ->
+ {error, copy_save_enabled}
+ end.
+
+rt_mask() ->
+ erts_debug:lcnt_control(mask).
rt_collect(Node) ->
- rpc:call(Node, erts_debug, lock_counters, [info]).
+ rpc:call(Node, lcnt, rt_collect, []).
+rt_collect() ->
+ erts_debug:lcnt_collect().
+rt_clear(Node) ->
+ rpc:call(Node, lcnt, rt_clear, []).
rt_clear() ->
- erts_debug:lock_counters(clear).
+ erts_debug:lcnt_clear().
-rt_clear(Node) ->
- rpc:call(Node, erts_debug, lock_counters, [clear]).
+rt_opt(Node, Arg) ->
+ rpc:call(Node, lcnt, rt_opt, [Arg]).
-rt_opt({Type, Opt}) ->
- erts_debug:lock_counters({Type, Opt}).
+%% Compatibility shims for the "process/port_locks" options mentioned in the
+%% manual.
+rt_opt({process_locks, Enable}) ->
+ toggle_category(process, Enable);
+rt_opt({port_locks, Enable}) ->
+ toggle_category(io, Enable);
-rt_opt(Node, {Type, Opt}) ->
- rpc:call(Node, erts_debug, lock_counters, [{Type, Opt}]).
+rt_opt({Type, NewVal}) ->
+ PreviousVal = erts_debug:lcnt_control(Type),
+ erts_debug:lcnt_control(Type, NewVal),
+ PreviousVal.
+
+toggle_category(Category, true) ->
+ PreviousMask = erts_debug:lcnt_control(mask),
+ erts_debug:lcnt_control(mask, [Category | PreviousMask]),
+ lists:member(Category, PreviousMask);
+
+toggle_category(Category, false) ->
+ PreviousMask = erts_debug:lcnt_control(mask),
+ erts_debug:lcnt_control(mask, lists:delete(Category, PreviousMask)),
+ lists:member(Category, PreviousMask).
%% -------------------------------------------------------------------- %%
%%
@@ -192,13 +229,9 @@ call(Msg) -> gen_server:call(?MODULE, Msg, infinity).
%% -------------------------------------------------------------------- %%
apply(M,F,As) when is_atom(M), is_atom(F), is_list(As) ->
- ok = start_internal(),
- Opt = lcnt:rt_opt({copy_save, true}),
- lcnt:clear(),
- Res = erlang:apply(M,F,As),
- lcnt:collect(),
- lcnt:rt_opt({copy_save, Opt}),
- Res.
+ apply(fun() ->
+ erlang:apply(M,F,As)
+ end).
apply(Fun) when is_function(Fun) ->
lcnt:apply(Fun, []).
@@ -209,7 +242,9 @@ apply(Fun, As) when is_function(Fun) ->
lcnt:clear(),
Res = erlang:apply(Fun, As),
lcnt:collect(),
- lcnt:rt_opt({copy_save, Opt}),
+ %% _ is bound to silence a dialyzer warning; it used to fail silently and
+ %% we don't want to change the error semantics.
+ _ = lcnt:rt_opt({copy_save, Opt}),
Res.
all_conflicts() -> all_conflicts(time).
diff --git a/lib/tools/src/make.erl b/lib/tools/src/make.erl
index 0363dee02d..6554d338af 100644
--- a/lib/tools/src/make.erl
+++ b/lib/tools/src/make.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -267,15 +267,47 @@ include_opt([]) ->
recompile(File, true, _Load, _Opts) ->
io:format("Out of date: ~ts\n",[File]);
-recompile(File, false, noload, Opts) ->
+recompile(File, false, Load, Opts) ->
io:format("Recompile: ~ts\n",[File]),
- compile:file(File, [report_errors, report_warnings, error_summary |Opts]);
-recompile(File, false, load, Opts) ->
- io:format("Recompile: ~ts\n",[File]),
- c:c(File, Opts);
-recompile(File, false, netload, Opts) ->
- io:format("Recompile: ~ts\n",[File]),
- c:nc(File, Opts).
+ case compile:file(File, [report_errors, report_warnings |Opts]) of
+ Ok when is_tuple(Ok), element(1,Ok)==ok ->
+ maybe_load(element(2,Ok), Load, Opts);
+ _Error ->
+ error
+ end.
+
+maybe_load(_Mod, noload, _Opts) ->
+ ok;
+maybe_load(Mod, Load, Opts) ->
+ %% We have compiled File with options Opts. Find out where the
+ %% output file went to, and load it.
+ case compile:output_generated(Opts) of
+ true ->
+ Dir = proplists:get_value(outdir,Opts,"."),
+ do_load(Dir, Mod, Load);
+ false ->
+ io:format("** Warning: No object file created - nothing loaded **~n"),
+ ok
+ end.
+
+do_load(Dir, Mod, load) ->
+ code:purge(Mod),
+ case code:load_abs(filename:join(Dir, Mod),Mod) of
+ {module,Mod} ->
+ {ok,Mod};
+ Other ->
+ Other
+ end;
+do_load(Dir, Mod, netload) ->
+ Obj = atom_to_list(Mod) ++ code:objfile_extension(),
+ Fname = filename:join(Dir, Obj),
+ case file:read_file(Fname) of
+ {ok,Bin} ->
+ rpc:eval_everywhere(code,load_binary,[Mod,Fname,Bin]),
+ {ok,Mod};
+ Other ->
+ Other
+ end.
exists(File) ->
case file:read_file_info(File) of
diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src
index 17b1d06686..8beef49bf9 100644
--- a/lib/tools/src/tools.app.src
+++ b/lib/tools/src/tools.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -41,6 +41,6 @@
]
},
{runtime_dependencies, ["stdlib-3.1","runtime_tools-1.8.14",
- "kernel-3.0","erts-7.0","compiler-5.0"]}
+ "kernel-5.4","erts-9.1","compiler-5.0"]}
]
}.
diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl
index 8d2cc07e40..3199b28acb 100644
--- a/lib/tools/src/xref_base.erl
+++ b/lib/tools/src/xref_base.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. 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.
@@ -405,21 +405,21 @@ analysis(exports_not_used, _) ->
%% Local calls are not considered here. "X * UU" would do otherwise.
"X - XU";
analysis({call, F}, functions) ->
- make_query("range (E | ~w : Fun)", [F]);
+ make_query("range (E | ~tw : Fun)", [F]);
analysis({use, F}, functions) ->
- make_query("domain (E || ~w : Fun)", [F]);
+ make_query("domain (E || ~tw : Fun)", [F]);
analysis({module_call, M}, _) ->
- make_query("range (ME | ~w : Mod)", [M]);
+ make_query("range (ME | ~tw : Mod)", [M]);
analysis({module_use, M}, _) ->
- make_query("domain (ME || ~w : Mod)", [M]);
+ make_query("domain (ME || ~tw : Mod)", [M]);
analysis({application_call, A}, _) ->
- make_query("range (AE | ~w : App)", [A]);
+ make_query("range (AE | ~tw : App)", [A]);
analysis({application_use, A}, _) ->
- make_query("domain (AE || ~w : App)", [A]);
+ make_query("domain (AE || ~tw : App)", [A]);
analysis({release_call, R}, _) ->
- make_query("range (RE | ~w : Rel)", [R]);
+ make_query("range (RE | ~tw : Rel)", [R]);
analysis({release_use, R}, _) ->
- make_query("domain (RE || ~w : Rel)", [R]);
+ make_query("domain (RE || ~tw : Rel)", [R]);
analysis(deprecated_function_calls, functions) ->
"XC || DF";
analysis({deprecated_function_calls,Flag}, functions) ->
@@ -1833,9 +1833,9 @@ message(true, What, Arg) ->
unreadable ->
io:format("Skipping ~ts (unreadable)~n", [Arg]);
xref_attr ->
- io:format("~ts: Skipping 'xref' attribute ~w~n", Arg);
+ io:format("~ts: Skipping 'xref' attribute ~tw~n", Arg);
depr_attr ->
- io:format("~ts: Skipping 'deprecated' attribute ~w~n", Arg);
+ io:format("~ts: Skipping 'deprecated' attribute ~tw~n", Arg);
lib_search ->
io:format("Scanning library path for BEAM files... ", []);
lib_check ->
diff --git a/lib/tools/src/xref_parser.yrl b/lib/tools/src/xref_parser.yrl
index 0711da79e2..5ee6419ff5 100644
--- a/lib/tools/src/xref_parser.yrl
+++ b/lib/tools/src/xref_parser.yrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. 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.
@@ -170,7 +170,7 @@ is_prefix_op('#') -> numeric;
is_prefix_op(_) -> false.
check_regexp(String) ->
- case re:compile(String) of
+ case re:compile(String, [unicode]) of
{ok, _Expr} ->
{regexpr, String};
{error, {ErrString, Position}} ->
@@ -274,7 +274,7 @@ mfa2s({M,F,A}) ->
[c2s(M),':',c2s(F),'/',A].
c2s(C) ->
- [S] = io_lib:format("~p", [C]),
+ [S] = io_lib:format("~tp", [C]),
list_to_atom(S).
re(variable) -> ['_'];
diff --git a/lib/tools/src/xref_reader.erl b/lib/tools/src/xref_reader.erl
index 6c4a1c4d8e..88f92df35a 100644
--- a/lib/tools/src/xref_reader.erl
+++ b/lib/tools/src/xref_reader.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. 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/src/xref_utils.erl b/lib/tools/src/xref_utils.erl
index b0c168e018..02e207d40c 100644
--- a/lib/tools/src/xref_utils.erl
+++ b/lib/tools/src/xref_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. 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.
@@ -638,14 +638,14 @@ neighbours([], G, Fun, VT, L, _V, Vs) ->
neighbours(Vs, G, Fun, VT, L).
match_list(L, RExpr) ->
- {ok, Expr} = re:compile(RExpr),
+ {ok, Expr} = re:compile(RExpr, [unicode]),
filter(fun(E) -> match(E, Expr) end, L).
match_one(VarL, Con, Col) ->
select_each(VarL, fun(E) -> Con =:= element(Col, E) end).
match_many(VarL, RExpr, Col) ->
- {ok, Expr} = re:compile(RExpr),
+ {ok, Expr} = re:compile(RExpr, [unicode]),
select_each(VarL, fun(E) -> match(element(Col, E), Expr) end).
match(I, Expr) when is_integer(I) ->
@@ -653,7 +653,12 @@ match(I, Expr) when is_integer(I) ->
{match, [{0,length(S)}]} =:= re:run(S, Expr, [{capture, first}]);
match(A, Expr) when is_atom(A) ->
S = atom_to_list(A),
- {match, [{0,length(S)}]} =:= re:run(S, Expr, [{capture, first}]).
+ case re:run(S, Expr, [{capture, first}]) of
+ {match, [{0,Size}]} ->
+ Size =:= byte_size(unicode:characters_to_binary(S));
+ _ ->
+ false
+ end.
select_each([{Mod,Funs} | L], Pred) ->
case filter(Pred, Funs) of
diff --git a/lib/tools/test/Makefile b/lib/tools/test/Makefile
index fe65d1484d..2b7b17afb3 100644
--- a/lib/tools/test/Makefile
+++ b/lib/tools/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
+# Copyright Ericsson AB 1997-2017. 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/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl
index d39a5deeab..146c915087 100644
--- a/lib/tools/test/lcnt_SUITE.erl
+++ b/lib/tools/test/lcnt_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -29,7 +29,8 @@
-export([t_load/1,
t_conflicts/1,
t_locations/1,
- t_swap_keys/1]).
+ t_swap_keys/1,
+ smoke_lcnt/1]).
init_per_testcase(_Case, Config) ->
Config.
@@ -43,7 +44,8 @@ suite() ->
{timetrap,{minutes,4}}].
all() ->
- [t_load, t_conflicts, t_locations, t_swap_keys].
+ [t_load, t_conflicts, t_locations, t_swap_keys,
+ smoke_lcnt].
%%----------------------------------------------------------------------
%% Tests
@@ -146,3 +148,81 @@ t_swap_keys_file([File|Files]) ->
ok = lcnt:conflicts(),
ok = lcnt:stop(),
t_swap_keys_file(Files).
+
+%% Simple smoke test of actual lock-counting, if running on
+%% a run-time with lock-counting enabled.
+smoke_lcnt(Config) ->
+ case catch erlang:system_info(lock_counting) of
+ true ->
+ do_smoke_lcnt(Config);
+ _ ->
+ {skip,"Lock counting is not enabled"}
+ end.
+
+do_smoke_lcnt(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ SaveFile = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)),
+ {Time,ok} = timer:tc(fun() -> lcnt:apply(fun() -> big_bang(200) end) end),
+ io:format("~p ms\n", [Time]),
+ ok = lcnt:conflicts(),
+ ok = lcnt:save(SaveFile),
+ ok = lcnt:load(SaveFile),
+ ok = lcnt:conflicts(),
+ lcnt:stop().
+
+
+%%%
+%%% A slightly modified version of Rickard Green's Big Bang benchmark.
+%%%
+
+big_bang(N) when is_integer(N) ->
+ Procs = spawn_procs(N),
+ RMsgs = lists:map(fun (P) -> {done, P} end, Procs),
+ send_procs(Procs, {procs, Procs, self()}),
+ receive_msgs(RMsgs),
+ lists:foreach(fun (P) -> exit(P, normal) end, Procs).
+
+pinger([], [], true) ->
+ receive
+ {procs, Procs, ReportTo} ->
+ pinger(Procs, [], ReportTo)
+ end;
+pinger([], [], false) ->
+ receive {ping, From} -> From ! {pong, self()} end,
+ pinger([],[],false);
+pinger([], [], ReportTo) ->
+ ReportTo ! {done, self()},
+ pinger([],[],false);
+pinger([],[Po|Pos] = Pongers, ReportTo) ->
+ receive
+ {ping, From} ->
+ From ! {pong, self()},
+ pinger([], Pongers, ReportTo);
+ {pong, Po} ->
+ pinger([], Pos, ReportTo)
+ end;
+pinger([Pi|Pis], Pongers, ReportTo) ->
+ receive {ping, From} -> From ! {pong, self()}
+ after 0 -> ok
+ end,
+ Pi ! {ping, self()},
+ pinger(Pis, [Pi|Pongers], ReportTo).
+
+spawn_procs(N) when N =< 0 ->
+ [];
+spawn_procs(N) ->
+ [spawn_link(fun () -> pinger([], [], true) end) | spawn_procs(N-1)].
+
+send_procs([], Msg) ->
+ Msg;
+send_procs([P|Ps], Msg) ->
+ P ! Msg,
+ send_procs(Ps, Msg).
+
+receive_msgs([]) ->
+ ok;
+receive_msgs([M|Ms]) ->
+ receive
+ M ->
+ receive_msgs(Ms)
+ end.
diff --git a/lib/tools/test/make_SUITE.erl b/lib/tools/test/make_SUITE.erl
index 2db5c6844a..02da4f4ace 100644
--- a/lib/tools/test/make_SUITE.erl
+++ b/lib/tools/test/make_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -36,7 +36,7 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [make_all, make_files, recompile_on_changed_include,
+ [make_all, make_files, load, netload, recompile_on_changed_include,
emake_opts, {group, otp_6057}].
groups() ->
@@ -55,6 +55,21 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
otp_6057_end(Config).
+init_per_testcase(_,Config) ->
+ Config.
+
+end_per_testcase(netload,_Config) ->
+ %% Stop slave - in case of failure
+ Nodes = nodes(),
+ case [N || N <- Nodes,
+ "make_SUITE_netload" == hd(string:lexemes(atom_to_list(N),"@"))] of
+ [Node] ->
+ ct_slave:stop(Node);
+ _ ->
+ ok
+ end;
+end_per_testcase(_,_Config) ->
+ ok.
test_files() -> ["test1", "test2", "test3", "test4"].
@@ -83,6 +98,32 @@ make_files(Config) when is_list(Config) ->
ensure_no_messages(),
ok.
+load(Config) ->
+ Current = prepare_data_dir(Config),
+ code:purge(test1),
+ code:delete(test1),
+ false = code:is_loaded(test1),
+ up_to_date = make:files([test1], [load]),
+ {file,_} = code:is_loaded(test1),
+ file:set_cwd(Current),
+ ensure_no_messages(),
+ ok.
+
+netload(Config) ->
+ Current = prepare_data_dir(Config),
+ code:purge(test1),
+ code:delete(test1),
+ false = code:is_loaded(test1),
+ {ok,Node} = ct_slave:start(make_SUITE_netload),
+ up_to_date = make:files([test1], [netload]),
+ timer:sleep(1000), % async, so give some time
+ {file,F} = code:is_loaded(test1),
+ {file,F} = rpc:call(Node,code,is_loaded,[test1]),
+ ct_slave:stop(Node),
+ file:set_cwd(Current),
+ ensure_no_messages(),
+ ok.
+
recompile_on_changed_include(Config) ->
Current = prepare_data_dir(Config),
diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index b8e7113595..379a5c09ab 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. 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.
@@ -50,7 +50,8 @@
-export([analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]).
--export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1, otp_13708/1]).
+-export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1, otp_13708/1,
+ otp_14464/1]).
-import(lists, [append/2, flatten/1, keysearch/3, member/2, sort/1, usort/1]).
@@ -81,8 +82,10 @@ groups() ->
update, deprecated, trycatch, fun_mfa,
fun_mfa_r14, fun_mfa_vars, qlc]},
{analyses, [],
+
[analyze, basic, md, q, variables, unused_locals]},
- {misc, [], [format_error, otp_7423, otp_7831, otp_10192, otp_13708]}].
+ {misc, [], [format_error, otp_7423, otp_7831, otp_10192, otp_13708,
+ otp_14464]}].
init_per_suite(Conf) when is_list(Conf) ->
@@ -2396,7 +2399,6 @@ otp_10192(Conf) when is_list(Conf) ->
xref:stop(s),
ok.
-%% OTP-10192. Allow filenames with character codes greater than 126.
otp_13708(Conf) when is_list(Conf) ->
{ok, _} = start(s),
ok = xref:set_default(s, [{verbose, true}]),
@@ -2409,6 +2411,36 @@ otp_13708(Conf) when is_list(Conf) ->
ok = xref:set_library_path(s, [Dir], [{verbose, true}]),
xref:stop(s).
+%% OTP-14464. Unicode atoms.
+otp_14464(Conf) when is_list(Conf) ->
+ Dir = ?copydir,
+
+ File1 = fname(Dir, "a.erl"),
+ MFile1 = fname(Dir, "a"),
+ Beam1 = fname(Dir, "a.beam"),
+ Test1 = "-module(a).
+ -export([ärlig/0, 'кlирилли́ческий атомB'/0]).
+
+ ärlig() ->
+ 'кlирилли́ческий атомB'.
+
+ 'кlирилли́ческий атомB'() ->
+ foo.
+ ",
+ ok = file:write_file(File1, unicode:characters_to_binary(Test1)),
+ {ok, a} = compile:file(File1, [debug_info,{outdir,Dir}]),
+
+ {ok, _} = xref:start(s),
+ {ok, a} = xref:add_module(s, MFile1),
+
+ {ok, [{a,ärlig,0}]} = xref:q(s, 'a:"ärlig"/0'),
+ {ok, [{a,'кlирилли́ческий атомB',0}]} =
+ xref:q(s, 'a:"кlирилли́ческий атомB"/0'),
+
+ xref:stop(s),
+ ok = file:delete(File1),
+ ok = file:delete(Beam1).
+
%%%
%%% Utilities
%%%
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index f60da27c44..831d850217 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.9.1
+TOOLS_VSN = 2.10.1