aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tools
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tools')
-rw-r--r--lib/tools/doc/src/lcnt.xml104
-rw-r--r--lib/tools/doc/src/lcnt_chapter.xml9
-rw-r--r--lib/tools/doc/src/notes.xml20
-rw-r--r--lib/tools/doc/src/venn2.fig66
-rw-r--r--lib/tools/doc/src/venn2.gifbin3369 -> 3507 bytes
-rw-r--r--lib/tools/doc/src/xref.xml12
-rw-r--r--lib/tools/emacs/erlang.el177
-rw-r--r--lib/tools/src/eprof.erl30
-rw-r--r--lib/tools/src/fprof.erl36
-rw-r--r--lib/tools/src/lcnt.erl77
-rw-r--r--lib/tools/src/make.erl48
-rw-r--r--lib/tools/src/tools.app.src2
-rw-r--r--lib/tools/src/xref_base.erl47
-rw-r--r--lib/tools/src/xref_parser.yrl6
-rw-r--r--lib/tools/src/xref_reader.erl18
-rw-r--r--lib/tools/src/xref_utils.erl13
-rw-r--r--lib/tools/test/lcnt_SUITE.erl5
-rw-r--r--lib/tools/test/make_SUITE.erl43
-rw-r--r--lib/tools/test/xref_SUITE.erl68
-rw-r--r--lib/tools/vsn.mk2
20 files changed, 523 insertions, 260 deletions
diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml
index 31e5c241e9..5bdfc60448 100644
--- a/lib/tools/doc/src/lcnt.xml
+++ b/lib/tools/doc/src/lcnt.xml
@@ -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 c73fcb31e0..24b58136aa 100644
--- a/lib/tools/doc/src/lcnt_chapter.xml
+++ b/lib/tools/doc/src/lcnt_chapter.xml
@@ -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>
@@ -97,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/notes.xml b/lib/tools/doc/src/notes.xml
index bdd5455354..f0df43bf2b 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -31,6 +31,26 @@
</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>
diff --git a/lib/tools/doc/src/venn2.fig b/lib/tools/doc/src/venn2.fig
index 3694c12f0c..233686a729 100644
--- a/lib/tools/doc/src/venn2.fig
+++ b/lib/tools/doc/src/venn2.fig
@@ -1,4 +1,4 @@
-#FIG 3.2
+#FIG 3.2 Produced by xfig version 3.2.5c
Portrait
Center
Inches
@@ -7,34 +7,7 @@ Letter
Single
-2
1200 2
-6 3392 953 5034 3329
-6 3392 953 5034 2595
-6 3392 953 5034 2595
-5 1 0 1 -1 7 0 0 -1 0.000 0 0 0 0 2652.489 1773.500 3518 1357 3613 1774 3518 2190
-5 1 0 1 -1 7 0 0 -1 0.000 0 0 0 0 6306.956 1773.000 4028 2575 3891 1774 4028 971
-5 1 0 1 -1 7 0 0 -1 0.000 0 0 0 0 2105.283 1773.000 4402 971 4538 1774 4402 2575
-1 1 0 1 -1 7 0 0 -1 0.000 1 0.0000 4214 1774 820 821 4214 1774 3659 1171
-2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 1
- 4821 2325
-2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
- 4816 1217 4816 2329
-2 1 0 1 -1 7 0 0 -1 0.000 0 0 7 0 0 2
- 3392 1769 4816 1769
-2 1 0 1 0 0 100 0 1 0.000 0 0 -1 0 0 2
- 4816 1982 5008 1982
--6
-2 3 0 0 0 0 101 0 5 0.000 0 0 -1 0 0 36
- 4026 977 4011 1025 3996 1072 3981 1120 3966 1177 3954 1225
- 3944 1272 3929 1327 3919 1412 3909 1477 3899 1540 3894 1592
- 3894 1642 3891 1697 3889 1742 3889 1770 3394 1767 3396 1717
- 3399 1665 3409 1610 3424 1555 3439 1502 3464 1440 3489 1390
- 3516 1340 3551 1292 3584 1250 3631 1200 3679 1150 3731 1110
- 3801 1065 3869 1030 3931 1005 3986 982 4009 980 4026 977
--6
-4 0 0 101 0 0 11 0.0000 4 105 525 3965 3044 X - XU\001
-4 0 0 101 0 0 11 0.0000 4 150 1110 3688 3299 exports_not_used\001
--6
-6 5850 938 7560 3329
+6 5850 938 8070 3344
6 5884 938 7526 2580
6 5884 938 7526 2580
5 1 0 1 -1 7 0 0 -1 0.000 0 0 0 0 5144.489 1758.500 6010 1342 6105 1759 6010 2175
@@ -63,8 +36,8 @@ Single
7019 1990 7022 1945 7027 1900 7029 1855 7029 1805 7032 1765
7029 1752 7309 1757
-6
-4 0 0 101 0 0 11 0.0000 4 135 1470 6000 3014 L * (UU + (XU - LU))\001
-4 0 0 101 0 0 11 0.0000 4 150 1800 5850 3299 locals_not_used (simplified)\001
+4 0 0 101 0 0 11 0.0000 4 180 2070 6000 3014 (L-OL) * (UU + (XU-LU))\001
+4 0 0 101 0 0 11 0.0000 4 180 2160 5850 3299 locals_not_used (simplified)\001
-6
6 900 900 2550 3600
6 900 900 2550 2625
@@ -91,7 +64,34 @@ Single
2330 1222 2365 1265 2402 1317 2437 1382 2477 1455 2500 1517
2520 1585 2532 1645 2540 1712 2542 1780 2540 1842 2535 1907
2527 1957 2517 1990 2325 1987 2330 1222
-4 0 0 101 0 0 11 0.0000 4 105 780 1331 3044 XU - X - B\001
-4 0 0 101 0 0 11 0.0000 4 150 1260 1113 3314 undefined_functions\001
+4 0 0 101 0 0 11 0.0000 4 135 825 1331 3044 XU - X - B\001
+4 0 0 101 0 0 11 0.0000 4 180 1530 1113 3314 undefined_functions\001
4 0 0 100 0 0 10 0.0000 4 135 1005 1275 3525 (modules mode)\001
-6
+6 3392 953 5034 3329
+6 3392 953 5034 2595
+6 3392 953 5034 2595
+5 1 0 1 -1 7 0 0 -1 0.000 0 0 0 0 2652.489 1773.500 3518 1357 3613 1774 3518 2190
+5 1 0 1 -1 7 0 0 -1 0.000 0 0 0 0 6306.956 1773.000 4028 2575 3891 1774 4028 971
+5 1 0 1 -1 7 0 0 -1 0.000 0 0 0 0 2105.283 1773.000 4402 971 4538 1774 4402 2575
+1 1 0 1 -1 7 0 0 -1 0.000 1 0.0000 4214 1774 820 821 4214 1774 3659 1171
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 1
+ 4821 2325
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 4816 1217 4816 2329
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 7 0 0 2
+ 3392 1769 4816 1769
+2 1 0 1 0 0 100 0 1 0.000 0 0 -1 0 0 2
+ 4816 1982 5008 1982
+-6
+2 3 0 0 0 0 101 0 5 0.000 0 0 -1 0 0 36
+ 4026 977 4011 1025 3996 1072 3981 1120 3966 1177 3954 1225
+ 3944 1272 3929 1327 3919 1412 3909 1477 3899 1540 3894 1592
+ 3894 1642 3891 1697 3889 1742 3889 1770 3394 1767 3396 1717
+ 3399 1665 3409 1610 3424 1555 3439 1502 3464 1440 3489 1390
+ 3516 1340 3551 1292 3584 1250 3631 1200 3679 1150 3731 1110
+ 3801 1065 3869 1030 3931 1005 3986 982 4009 980 4026 977
+-6
+4 0 0 101 0 0 11 0.0000 4 135 555 3965 3044 X - XU\001
+4 0 0 101 0 0 11 0.0000 4 180 1350 3688 3299 exports_not_used\001
+-6
diff --git a/lib/tools/doc/src/venn2.gif b/lib/tools/doc/src/venn2.gif
index 4cfea24646..bb12f4bd1f 100644
--- a/lib/tools/doc/src/venn2.gif
+++ b/lib/tools/doc/src/venn2.gif
Binary files differ
diff --git a/lib/tools/doc/src/xref.xml b/lib/tools/doc/src/xref.xml
index 8c49f3a206..6f833246ad 100644
--- a/lib/tools/doc/src/xref.xml
+++ b/lib/tools/doc/src/xref.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2000</year><year>2016</year>
+ <year>2000</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -347,6 +347,9 @@ represented by
<item>Locally Used Functions (*). Functions of all modules that have
been used in some local call.
</item>
+ <tag><c>OL</c></tag>
+ <item>Functions with an attribute tag <c>on_load</c> (*).
+ </item>
<tag><c>LC</c></tag>
<item>Local Calls (*).</item>
<tag><c>XC</c></tag>
@@ -393,6 +396,7 @@ facts about the
<c>LU</c> and <c>XU</c> may have elements in common. Put in
another way:</item>
<item><c>V</c> is equal to <c>UU + XU + LU</c>.</item>
+ <item><c>OL</c> is a subset of <c>F</c>.</item>
<item><c>E</c> is equal to <c>LC + XC</c>. Note that <c>LC</c>
and <c>XC</c> may have elements in common, namely if some
function is locally and externally used from one and the same
@@ -559,8 +563,10 @@ Two functions (modules,
analyzing operators:
</p>
<list type="bulleted">
- <item>Expression ::= Expression GraphOp Expression</item>
- <item>GraphOp ::= <c>components</c> | <c>condensation</c> | <c>of</c></item>
+ <item>Expression ::= Expression BinaryGraphOp Expression</item>
+ <item>Expression ::= UnaryGraphOp Expression</item>
+ <item>UnaryGraphOp ::= <c>components</c> | <c>condensation</c></item>
+ <item>BinaryGraphOp ::= <c>of</c></item>
</list>
<p>As was mentioned before, the graph analyses operate on
the <c>digraph</c> representation of graphs.
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 438abc2d29..411e0e13df 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -1,10 +1,10 @@
-;;; erlang.el --- Major modes for editing and running Erlang
+;;; erlang.el --- Major modes for editing and running Erlang -*- lexical-binding: t; -*-
;; Copyright (C) 2004 Free Software Foundation, Inc.
;; Author: Anders Lindgren
;; Keywords: erlang, languages, processes
;; Date: 2011-12-11
-;; Version: 2.7.0
+;; Version: 2.8.0
;; Package-Requires: ((emacs "24.1"))
;; %CopyrightBegin%
@@ -84,7 +84,7 @@
"The Erlang programming language."
:group 'languages)
-(defconst erlang-version "2.7"
+(defconst erlang-version "2.8.0"
"The version number of Erlang mode.")
(defcustom erlang-root-dir nil
@@ -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"
@@ -1019,26 +1020,15 @@ files written in other languages than Erlang.")
If nil, the inferior shell replaces the window. This is the traditional
behaviour.")
-(defconst inferior-erlang-use-cmm (boundp 'minor-mode-overriding-map-alist)
- "Non-nil means use `compilation-minor-mode' in Erlang shell.")
-
(defvar erlang-mode-map
(let ((map (make-sparse-keymap)))
- (unless (boundp 'indent-line-function)
- (define-key map "\t" 'erlang-indent-command))
(define-key map ";" 'erlang-electric-semicolon)
(define-key map "," 'erlang-electric-comma)
(define-key map "<" 'erlang-electric-lt)
(define-key map ">" 'erlang-electric-gt)
(define-key map "\C-m" 'erlang-electric-newline)
- (if (not (boundp 'delete-key-deletes-forward))
- (define-key map "\177" 'backward-delete-char-untabify)
- (define-key map [(backspace)] 'backward-delete-char-untabify))
- ;;(unless (boundp 'fill-paragraph-function)
+ (define-key map [(backspace)] 'backward-delete-char-untabify)
(define-key map "\M-q" 'erlang-fill-paragraph)
- (unless (boundp 'beginning-of-defun-function)
- (define-key map "\M-\C-a" 'erlang-beginning-of-function)
- (define-key map "\M-\C-e" 'erlang-end-of-function))
(define-key map "\M-\t" 'erlang-complete-tag)
(define-key map "\C-c\M-\t" 'tempo-complete-tag)
(define-key map "\M-+" 'erlang-find-next-tag)
@@ -1057,8 +1047,6 @@ behaviour.")
(define-key map "\C-c\C-y" 'erlang-clone-arguments)
(define-key map "\C-c\C-a" 'erlang-align-arrows)
(define-key map "\C-c\C-z" 'erlang-shell-display)
- (unless inferior-erlang-use-cmm
- (define-key map "\C-x`" 'erlang-next-error))
map)
"Keymap used in Erlang mode.")
(defvar erlang-mode-abbrev-table nil
@@ -2083,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
@@ -3396,14 +3378,6 @@ at the end."
;;; Information retrieval functions.
-(defun erlang-buffer-substring (beg end)
- "Like `buffer-substring-no-properties'.
-Although, this function works on all versions of Emacs."
- (if (fboundp 'buffer-substring-no-properties)
- (funcall (symbol-function 'buffer-substring-no-properties) beg end)
- (buffer-substring beg end)))
-
-
(defun erlang-get-module ()
"Return the name of the module as specified by `-module'.
@@ -3421,7 +3395,7 @@ Return nil if file contains no `-module' attribute."
"\\)?\\)\\s *)\\s *\\."))
(point-max) t)
(erlang-remove-quotes
- (erlang-buffer-substring (match-beginning 1)
+ (buffer-substring-no-properties (match-beginning 1)
(match-end 1)))
nil)
(store-match-data md))))))
@@ -3475,10 +3449,10 @@ corresponds to the order of the parsed Erlang list."
(setq res (cons
(cons
(erlang-remove-quotes
- (erlang-buffer-substring
+ (buffer-substring-no-properties
(match-beginning 1) (match-end 1)))
(string-to-number
- (erlang-buffer-substring
+ (buffer-substring-no-properties
(match-beginning
(+ 1 erlang-atom-regexp-matches))
(match-end
@@ -3525,7 +3499,7 @@ function and arity as cdr part."
(erlang-skip-blank)
(if (looking-at erlang-atom-regexp)
(let ((module (erlang-remove-quotes
- (erlang-buffer-substring
+ (buffer-substring-no-properties
(match-beginning 0)
(match-end 0)))))
(goto-char (match-end 0))
@@ -3558,7 +3532,7 @@ Normally used in conjunction with `erlang-beginning-of-clause', e.g.:
(let ((n (if arg 0 1)))
(and (looking-at (eval-when-compile
(concat "^" erlang-atom-regexp "\\s *(")))
- (erlang-buffer-substring (match-beginning n) (match-end n)))))
+ (buffer-substring-no-properties (match-beginning n) (match-end n)))))
(defun erlang-get-function-arrow ()
@@ -3572,7 +3546,7 @@ Normally used in conjunction with `erlang-beginning-of-clause', e.g.:
(and
(save-excursion
(re-search-forward "->" (point-max) t)
- (erlang-buffer-substring (- (point) 2) (+ (point) 1)))))
+ (buffer-substring-no-properties (- (point) 2) (+ (point) 1)))))
(defun erlang-get-function-arity ()
"Return the number of arguments of function at point, or nil."
@@ -3638,12 +3612,14 @@ The return value is a string of the form \"foo/1\"."
(let ((start (match-end 0)))
(goto-char (- start 1))
(forward-sexp)
- (erlang-buffer-substring start (- (point) 1)))
+ (buffer-substring-no-properties start (- (point) 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.
@@ -3701,10 +3677,10 @@ of arguments could be found, otherwise nil."
(defun erlang-get-qualified-function-id-at-point ()
(let ((kind 'qualified-function)
(module (erlang-remove-quotes
- (erlang-buffer-substring
+ (buffer-substring-no-properties
(match-beginning 1) (match-end 1))))
(name (erlang-remove-quotes
- (erlang-buffer-substring
+ (buffer-substring-no-properties
(match-beginning (1+ erlang-atom-regexp-matches))
(match-end (1+ erlang-atom-regexp-matches)))))
(arity (progn
@@ -3716,14 +3692,14 @@ of arguments could be found, otherwise nil."
(let ((kind 'module)
(module nil)
(name (erlang-remove-quotes
- (erlang-buffer-substring (match-beginning 1)
+ (buffer-substring-no-properties (match-beginning 1)
(match-end 1))))
(arity nil))
(list kind module name arity)))
(defun erlang-get-some-other-id-at-point ()
(let ((name (erlang-remove-quotes
- (erlang-buffer-substring
+ (buffer-substring-no-properties
(match-beginning 0) (match-end 0))))
(imports (erlang-get-import))
kind module arity)
@@ -3790,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)
@@ -4881,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)
@@ -4992,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
@@ -5010,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
@@ -5113,7 +5114,7 @@ Erlang compilation package.")
"Command to execute to go to the next error.
Change this variable to use your favorite Erlang compilation
-package. Not used in Emacs 21.")
+package.")
;;;###autoload
@@ -5172,6 +5173,13 @@ future, a new shell on an already running host will be started."
(defvar erlang-shell-buffer-name "*erlang*"
"The name of the Erlang link shell buffer.")
+(defcustom erlang-shell-prompt-read-only t
+ "If non-nil, the prompt will be read-only.
+
+Also see the description of `ielm-prompt-read-only'."
+ :type 'boolean
+ :package-version '(erlang . "2.8.0"))
+
(defvar erlang-shell-mode-map nil
"Keymap used by Erlang shells.")
@@ -5212,17 +5220,11 @@ The following special commands are available:
(setq erlang-shell-mode-map (copy-keymap comint-mode-map))
(erlang-shell-mode-commands erlang-shell-mode-map))
(use-local-map erlang-shell-mode-map)
- (unless inferior-erlang-use-cmm
- ;; This was originally not a marker, but it needs to be, at least
- ;; in Emacs 21, and should be backwards-compatible. Otherwise,
- ;; would need to test whether compilation-parsing-end is a marker
- ;; after requiring `compile'.
- (set (make-local-variable 'compilation-parsing-end) (copy-marker 1))
- (set (make-local-variable 'compilation-error-list) nil)
- (set (make-local-variable 'compilation-old-error-list) nil))
;; Needed when compiling directly from the Erlang shell.
(setq compilation-last-buffer (current-buffer))
(setq comint-prompt-regexp "^[^>=]*> *")
+ (make-local-variable 'comint-prompt-read-only)
+ (setq comint-prompt-read-only erlang-shell-prompt-read-only)
(setq comint-eol-on-send t)
(setq comint-input-ignoredups t)
(setq comint-scroll-show-maximum-output t)
@@ -5236,24 +5238,20 @@ The following special commands are available:
(comint-read-input-ring t)
(make-local-variable 'kill-buffer-hook)
(add-hook 'kill-buffer-hook 'comint-write-input-ring)
- ;; At least in Emacs 21, we need to be in `compilation-minor-mode'
- ;; for `next-error' to work. We can avoid it clobbering the shell
- ;; keys thus.
- (when inferior-erlang-use-cmm
- (compilation-minor-mode 1)
- (set (make-local-variable 'minor-mode-overriding-map-alist)
- `((compilation-minor-mode
- . ,(let ((map (make-sparse-keymap)))
- ;; It would be useful to put keymap properties on the
- ;; error lines so that we could use RET and mouse-2
- ;; on them directly.
- (when (boundp 'compilation-skip-threshold) ; new compile.el
- (define-key map [mouse-2] #'erlang-mouse-2-command)
- (define-key map "\C-m" #'erlang-RET-command))
- (if (boundp 'compilation-menu-map)
- (define-key map [menu-bar compilation]
- (cons "Errors" compilation-menu-map)))
- map)))))
+ (compilation-minor-mode 1)
+ (set (make-local-variable 'minor-mode-overriding-map-alist)
+ `((compilation-minor-mode
+ . ,(let ((map (make-sparse-keymap)))
+ ;; It would be useful to put keymap properties on the
+ ;; error lines so that we could use RET and mouse-2
+ ;; on them directly.
+ (when (boundp 'compilation-skip-threshold) ; new compile.el
+ (define-key map [mouse-2] #'erlang-mouse-2-command)
+ (define-key map "\C-m" #'erlang-RET-command))
+ (if (boundp 'compilation-menu-map)
+ (define-key map [menu-bar compilation]
+ (cons "Errors" compilation-menu-map)))
+ map))))
(erlang-tags-init)
(run-hooks 'erlang-shell-mode-hook))
@@ -5282,9 +5280,7 @@ Selects Comint or Compilation mode command as appropriate."
(define-key map "\C-a" 'comint-bol) ; Normally the other way around.
(define-key map "\C-c\C-a" 'beginning-of-line)
(define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof'
- (define-key map "\M-\C-m" 'compile-goto-error)
- (unless inferior-erlang-use-cmm
- (define-key map "\C-x`" 'erlang-next-error)))
+ (define-key map "\M-\C-m" 'compile-goto-error))
;;;
;;; Inferior Erlang -- Run an Erlang shell as a subprocess.
@@ -5895,35 +5891,6 @@ Tab characters are counted by their visual width."
(if (looking-at "[a-z0-9_]+")
(match-string 0))))
-;; Aliases for backward compatibility with older versions of Erlang Mode.
-;;
-;; Unfortuantely, older versions of Emacs doesn't have `defalias' and
-;; `make-obsolete' so we have to define our own `obsolete' function.
-
-(defun erlang-obsolete (sym newdef)
- "Make the obsolete function SYM refer to the defined function NEWDEF.
-
-Simplified version of a combination `defalias' and `make-obsolete',
-it assumes that NEWDEF is loaded."
- (defalias sym (symbol-function newdef))
- (make-obsolete sym newdef "long ago"))
-
-
-(erlang-obsolete 'calculate-erlang-indent 'erlang-calculate-indent)
-(erlang-obsolete 'calculate-erlang-stack-indent
- 'erlang-calculate-stack-indent)
-(erlang-obsolete 'at-erlang-keyword 'erlang-at-keyword)
-(erlang-obsolete 'at-erlang-operator 'erlang-at-operator)
-(erlang-obsolete 'beginning-of-erlang-clause 'erlang-beginning-of-clause)
-(erlang-obsolete 'end-of-erlang-clause 'erlang-end-of-clause)
-(erlang-obsolete 'mark-erlang-clause 'erlang-mark-clause)
-(erlang-obsolete 'beginning-of-erlang-function 'erlang-beginning-of-function)
-(erlang-obsolete 'end-of-erlang-function 'erlang-end-of-function)
-(erlang-obsolete 'mark-erlang-function 'erlang-mark-function)
-(erlang-obsolete 'pass-over-erlang-clause 'erlang-pass-over-function)
-(erlang-obsolete 'name-of-erlang-function 'erlang-name-of-function)
-
-
(defconst erlang-unload-hook
(list (lambda ()
(ad-unadvise 'Man-notify-when-ready)
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 d881fedbd5..139b3d8a4a 100644
--- a/lib/tools/src/lcnt.erl
+++ b/lib/tools/src/lcnt.erl
@@ -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 ce30156db6..6554d338af 100644
--- a/lib/tools/src/make.erl
+++ b/lib/tools/src/make.erl
@@ -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 12f0cfd2df..8beef49bf9 100644
--- a/lib/tools/src/tools.app.src
+++ b/lib/tools/src/tools.app.src
@@ -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..a28c6ee283 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.
@@ -400,26 +400,28 @@ analysis(locals_not_used, functions) ->
%% used (indirectly) from any export: "(domain EE + range EE) * L".
%% But then we only get locals that make some calls, so we add
%% locals that are not used at all: "L * (UU + XU - LU)".
- "L * ((UU + XU - LU) + domain EE + range EE)";
+ %% We also need to exclude functions with the -on_load() attribute:
+ %% (L - OL) is used rather than just L.
+ "(L - OL) * ((UU + XU - LU) + domain EE + range EE)";
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) ->
@@ -918,7 +920,7 @@ do_add_module(S, XMod, Unres, Data) ->
{ok, Ms, Bad, NS}.
prepare_module(_Mode = functions, XMod, Unres0, Data) ->
- {DefAt0, LPreCAt0, XPreCAt0, LC0, XC0, X0, Attrs, Depr} = Data,
+ {DefAt0, LPreCAt0, XPreCAt0, LC0, XC0, X0, Attrs, Depr, OL0} = Data,
%% Bad is a list of bad values of 'xref' attributes.
{ALC0,AXC0,Bad0} = Attrs,
FT = [tspec(func)],
@@ -935,6 +937,7 @@ prepare_module(_Mode = functions, XMod, Unres0, Data) ->
ALC1 = xref_utils:xset(ALC0, PCA),
UnresCalls = xref_utils:xset(Unres0, PCA),
Unres = domain(UnresCalls),
+ OL1 = xref_utils:xset(OL0, FT),
DefinedFuns = domain(DefAt),
{AXC, ALC, Bad1, LPreCAt2, XPreCAt2} =
@@ -955,7 +958,7 @@ prepare_module(_Mode = functions, XMod, Unres0, Data) ->
{DF1,DF_11,DF_21,DF_31,DBad} = depr_mod(Depr, X),
{EE, ECallAt} = inter_graph(X, L, LC, XC, CallAt),
{ok, {functions, XMod, [DefAt,L,X,LCallAt,XCallAt,CallAt,LC,XC,EE,ECallAt,
- DF1,DF_11,DF_21,DF_31], NoCalls, Unres},
+ OL1,DF1,DF_11,DF_21,DF_31], NoCalls, Unres},
DBad++Bad};
prepare_module(_Mode = modules, XMod, _Unres, Data) ->
{X0, I0, Depr} = Data,
@@ -967,7 +970,7 @@ prepare_module(_Mode = modules, XMod, _Unres, Data) ->
finish_module({functions, XMod, List, NoCalls, Unres}, S) ->
ok = check_module(XMod, S),
[DefAt2,L2,X2,LCallAt2,XCallAt2,CallAt2,LC2,XC2,EE2,ECallAt2,
- DF2,DF_12,DF_22,DF_32] = pack(List),
+ OL2,DF2,DF_12,DF_22,DF_32] = pack(List),
LU = range(LC2),
@@ -976,7 +979,7 @@ finish_module({functions, XMod, List, NoCalls, Unres}, S) ->
M = XMod#xref_mod.name,
MS = xref_utils:xset(M, atom),
T = from_sets({MS,DefAt2,L2,X2,LCallAt2,XCallAt2,CallAt2,
- LC2,XC2,LU,EE2,ECallAt2,Unres,LPredefined,
+ LC2,XC2,LU,EE2,ECallAt2,Unres,LPredefined,OL2,
DF2,DF_12,DF_22,DF_32}),
NoUnres = XMod#xref_mod.no_unresolved,
@@ -1220,7 +1223,7 @@ do_set_up(S, VerboseOpt) ->
%% If data has been supplied using add_module/9 (and that is the only
%% sanctioned way), then DefAt, L, X, LCallAt, XCallAt, CallAt, XC, LC,
-%% and LU are guaranteed to be functions (with all supplied
+%% LU and OL are guaranteed to be functions (with all supplied
%% modules as domain (disregarding unknown modules, that is, modules
%% not supplied but hosting unknown functions)).
%% As a consequence, V and E are also functions. V is defined for unknown
@@ -1233,8 +1236,8 @@ do_set_up(S, VerboseOpt) ->
do_set_up(S) when S#xref.mode =:= functions ->
ModDictList = dict:to_list(S#xref.modules),
[DefAt0, L, X0, LCallAt, XCallAt, CallAt, LC, XC, LU,
- EE0, ECallAt, UC, LPredefined,
- Mod_DF,Mod_DF_1,Mod_DF_2,Mod_DF_3] = make_families(ModDictList, 18),
+ EE0, ECallAt, UC, LPredefined, OL,
+ Mod_DF,Mod_DF_1,Mod_DF_2,Mod_DF_3] = make_families(ModDictList, 19),
{XC_1, XU, XPredefined} = do_set_up_1(XC),
LC_1 = user_family(union_of_family(LC)),
@@ -1314,13 +1317,14 @@ do_set_up(S) when S#xref.mode =:= functions ->
UC_1 = user_family(union_of_family(UC)),
?FORMAT("DefAt ~p~n", [DefAt]),
- ?FORMAT("U=~p~nLib=~p~nB=~p~nLU=~p~nXU=~p~nUU=~p~n", [U,Lib,B,LU,XU,UU]),
+ ?FORMAT("U=~p~nLib=~p~nB=~p~nLU=~p~nXU=~p~nUU=~p~nOL=~p~n",
+ [U,Lib,B,LU,XU,UU,OL]),
?FORMAT("E_1=~p~nLC_1=~p~nXC_1=~p~n", [E_1,LC_1,XC_1]),
?FORMAT("EE=~p~nEE_1=~p~nECallAt=~p~n", [EE, EE_1, ECallAt]),
?FORMAT("DF=~p~nDF_1=~p~nDF_2=~p~nDF_3=~p~n", [DF, DF_1, DF_2, DF_3]),
Vs = [{'L',L}, {'X',X},{'F',F},{'U',U},{'B',B},{'UU',UU},
- {'XU',XU},{'LU',LU},{'V',V},{v,V},
+ {'XU',XU},{'LU',LU},{'V',V},{v,V},{'OL',OL},
{'LC',{LC,LC_1}},{'XC',{XC,XC_1}},{'E',{E,E_1}},{e,{E,E_1}},
{'EE',{EE,EE_1}},{'UC',{UC,UC_1}},
{'M',M},{'A',A},{'R',R},
@@ -1405,6 +1409,7 @@ var_type('U') -> {function, vertex};
var_type('UU') -> {function, vertex};
var_type('V') -> {function, vertex};
var_type('X') -> {function, vertex};
+var_type('OL') -> {function, vertex};
var_type('XU') -> {function, vertex};
var_type('DF') -> {function, vertex};
var_type('DF_1') -> {function, vertex};
@@ -1833,9 +1838,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 88f92df35a..d28bdb78db 100644
--- a/lib/tools/src/xref_reader.erl
+++ b/lib/tools/src/xref_reader.erl
@@ -42,7 +42,8 @@
%% experimental; -xref(FunEdge) is recognized.
lattrs=[], % local calls, {{mfa(),mfa()},Line}
xattrs=[], % external calls, -"-
- battrs=[] % badly formed xref attributes, term().
+ battrs=[], % badly formed xref attributes, term().
+ on_load % function name
}).
-include("xref.hrl").
@@ -68,15 +69,26 @@ forms([F | Fs], S) ->
forms([], S) ->
#xrefr{module = M, def_at = DefAt,
l_call_at = LCallAt, x_call_at = XCallAt,
- el = LC, ex = XC, x = X, df = Depr,
+ el = LC, ex = XC, x = X, df = Depr, on_load = OnLoad,
+ lattrs = AL, xattrs = AX, battrs = B, unresolved = U} = S,
+ OL = case OnLoad of
+ undefined -> [];
+ F ->
+ [{M, F, 0}]
+ end,
+ #xrefr{def_at = DefAt,
+ l_call_at = LCallAt, x_call_at = XCallAt,
+ el = LC, ex = XC, x = X, df = Depr, on_load = OnLoad,
lattrs = AL, xattrs = AX, battrs = B, unresolved = U} = S,
Attrs = {lists:reverse(AL), lists:reverse(AX), lists:reverse(B)},
- {ok, M, {DefAt, LCallAt, XCallAt, LC, XC, X, Attrs, Depr}, U}.
+ {ok, M, {DefAt, LCallAt, XCallAt, LC, XC, X, Attrs, Depr, OL}, U}.
form({attribute, Line, xref, Calls}, S) -> % experimental
#xrefr{module = M, function = Fun,
lattrs = L, xattrs = X, battrs = B} = S,
attr(Calls, erl_anno:line(Line), M, Fun, L, X, B, S);
+form({attribute, _, on_load, {F, 0}}, S) ->
+ S#xrefr{on_load = F};
form({attribute, _Line, _Attr, _Val}, S) ->
S;
form({function, _, module_info, 0, _Clauses}, S) ->
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/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl
index af3ce88fdd..146c915087 100644
--- a/lib/tools/test/lcnt_SUITE.erl
+++ b/lib/tools/test/lcnt_SUITE.erl
@@ -151,10 +151,9 @@ t_swap_keys_file([File|Files]) ->
%% Simple smoke test of actual lock-counting, if running on
%% a run-time with lock-counting enabled.
-
smoke_lcnt(Config) ->
- case erlang:system_info(build_type) of
- lcnt ->
+ case catch erlang:system_info(lock_counting) of
+ true ->
do_smoke_lcnt(Config);
_ ->
{skip,"Lock counting is not enabled"}
diff --git a/lib/tools/test/make_SUITE.erl b/lib/tools/test/make_SUITE.erl
index 2f6fe1c732..02da4f4ace 100644
--- a/lib/tools/test/make_SUITE.erl
+++ b/lib/tools/test/make_SUITE.erl
@@ -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 057449d4a2..d651cbcfee 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -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, otp_14344/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, otp_14344]}].
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,60 @@ 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).
+
+%% OTP-14344. -on_load() attribute.
+otp_14344(Conf) when is_list(Conf) ->
+ Dir = ?copydir,
+
+ File1 = fname(Dir, "a.erl"),
+ MFile1 = fname(Dir, "a"),
+ Beam1 = fname(Dir, "a.beam"),
+ Test1 = <<"-module(a).
+ -on_load(doit/0).
+ doit() -> ok.
+ ">>,
+ ok = file:write_file(File1, Test1),
+ {ok, a} = compile:file(File1, [debug_info,{outdir,Dir}]),
+
+ {ok, _} = xref:start(s),
+ {ok, a} = xref:add_module(s, MFile1),
+
+ {ok, [{a,doit,0}]} = xref:q(s, "OL"),
+ {ok, []} = xref:analyze(s, locals_not_used),
+
+ xref:stop(s),
+ ok = file:delete(File1),
+ ok = file:delete(Beam1).
+
%%%
%%% Utilities
%%%
@@ -2483,7 +2539,8 @@ add_module(S, XMod, DefAt, X, LCallAt, XCallAt, XC, LC) ->
Depr0 = {[], [], [], []},
DBad = [],
Depr = {Depr0,DBad},
- Data = {DefAt, LCallAt, XCallAt, LC, XC, X, Attr, Depr},
+ OL = [],
+ Data = {DefAt, LCallAt, XCallAt, LC, XC, X, Attr, Depr, OL},
Unres = [],
{ok, _Module, _Bad, State} =
xref_base:do_add_module(S, XMod, Unres, Data),
@@ -2564,6 +2621,9 @@ functions_mode_check(S, Info) ->
%% UU subset F
{ok, []} = xref:q(S, "UU - F"),
+ %% OL subset F
+ {ok, []} = xref:q(S, "OL - F"),
+
%% ME = (Mod) E
{ok, ME} = xref:q(S, "ME"),
{ok, ME} = xref:q(S, "(Mod) E"),
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index 8aa7814e1d..831d850217 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.10
+TOOLS_VSN = 2.10.1