diff options
Diffstat (limited to 'lib/tools')
-rw-r--r-- | lib/tools/c_src/Makefile.in | 8 | ||||
-rw-r--r-- | lib/tools/doc/src/Makefile | 16 | ||||
-rw-r--r-- | lib/tools/doc/src/cover.xml | 20 | ||||
-rw-r--r-- | lib/tools/doc/src/eprof.xml | 12 | ||||
-rw-r--r-- | lib/tools/doc/src/notes.xml | 65 | ||||
-rw-r--r-- | lib/tools/doc/src/xref.xml | 2 | ||||
-rw-r--r-- | lib/tools/doc/src/xref_chapter.xml | 4 | ||||
-rw-r--r-- | lib/tools/emacs/Makefile | 24 | ||||
-rw-r--r-- | lib/tools/emacs/erlang-pkg.el | 3 | ||||
-rw-r--r-- | lib/tools/emacs/erlang.el | 108 | ||||
-rw-r--r-- | lib/tools/emacs/test.erl.indented | 45 | ||||
-rw-r--r-- | lib/tools/emacs/test.erl.orig | 45 | ||||
-rw-r--r-- | lib/tools/emacs/vsn.mk | 3 | ||||
-rw-r--r-- | lib/tools/examples/Makefile | 4 | ||||
-rw-r--r-- | lib/tools/priv/Makefile | 6 | ||||
-rw-r--r-- | lib/tools/src/Makefile | 10 | ||||
-rw-r--r-- | lib/tools/src/cover.erl | 188 | ||||
-rw-r--r-- | lib/tools/src/fprof.erl | 23 | ||||
-rw-r--r-- | lib/tools/src/tools.app.src | 3 | ||||
-rw-r--r-- | lib/tools/src/xref_utils.erl | 4 | ||||
-rw-r--r-- | lib/tools/test/Makefile | 10 | ||||
-rw-r--r-- | lib/tools/test/cover_SUITE.erl | 142 | ||||
-rw-r--r-- | lib/tools/test/cover_SUITE_data/f.erl | 11 | ||||
-rw-r--r-- | lib/tools/test/xref_SUITE.erl | 16 | ||||
-rw-r--r-- | lib/tools/vsn.mk | 2 |
25 files changed, 589 insertions, 185 deletions
diff --git a/lib/tools/c_src/Makefile.in b/lib/tools/c_src/Makefile.in index b8c4aed6e2..0382d3228d 100644 --- a/lib/tools/c_src/Makefile.in +++ b/lib/tools/c_src/Makefile.in @@ -188,11 +188,11 @@ include ../vsn.mk RELSYSDIR = $(RELEASE_PATH)/lib/tools-$(TOOLS_VSN) release_spec: all - $(INSTALL_DIR) $(RELSYSDIR)/c_src - $(INSTALL_DATA) $(EMEM_SRCS) $(EMEM_HEADERS) $(RELSYSDIR)/c_src + $(INSTALL_DIR) "$(RELSYSDIR)/c_src" + $(INSTALL_DATA) $(EMEM_SRCS) $(EMEM_HEADERS) "$(RELSYSDIR)/c_src" ifneq ($(PROGS),) - $(INSTALL_DIR) $(RELSYSDIR)/bin - $(INSTALL_PROGRAM) $(PROGS) $(RELSYSDIR)/bin + $(INSTALL_DIR) "$(RELSYSDIR)/bin" + $(INSTALL_PROGRAM) $(PROGS) "$(RELSYSDIR)/bin" endif release_docs_spec: diff --git a/lib/tools/doc/src/Makefile b/lib/tools/doc/src/Makefile index 433f123ae5..e3a2d64041 100644 --- a/lib/tools/doc/src/Makefile +++ b/lib/tools/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2010. All Rights Reserved. +# Copyright Ericsson AB 1997-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -121,14 +121,14 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/tools/doc/src/cover.xml b/lib/tools/doc/src/cover.xml index 683acc025d..a2444ec947 100644 --- a/lib/tools/doc/src/cover.xml +++ b/lib/tools/doc/src/cover.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2001</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -104,6 +104,13 @@ remove nodes. The same Cover compiled code will be loaded on each node, and analysis will collect and sum up coverage data results from all nodes.</p> + <p>To only collect data from remote nodes without stopping + <c>cover</c> on those nodes, use <c>cover:flush/1</c></p> + <p>If the connection to a remote node goes down, the main node + will mark it as lost. If the node comes back it will be added + again. If the remote node was alive during the disconnected + periode, cover data from before and during this periode will be + included in the analysis.</p> </description> <funcs> <func> @@ -477,6 +484,17 @@ remote nodes is fetched and stored on the main node.</p> </desc> </func> + <func> + <name>flush(Nodes) -> ok | {error,not_main_node}</name> + <fsummary>Collect cover data from remote nodes.</fsummary> + <type> + <v>Nodes = [atom()]</v> + </type> + <desc> + <p>Fetch data from the Cover database on the remote nodes and + stored on the main node.</p> + </desc> + </func> </funcs> <section> diff --git a/lib/tools/doc/src/eprof.xml b/lib/tools/doc/src/eprof.xml index 8b614d8860..82eb8dd284 100644 --- a/lib/tools/doc/src/eprof.xml +++ b/lib/tools/doc/src/eprof.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -67,9 +67,9 @@ <p><c>Rootset</c> is a list of pids and registered names.</p> <p>The function returns <c>profiling</c> if tracing could be enabled for all processes in <c>Rootset</c>, or <c>error</c> otherwise.</p> - <p>A pattern can be selected to narrow the profiling. For instance ca a specific - module be selected and only the code processes executes in that module will be - profiled.</p> + <p>A pattern can be selected to narrow the profiling. For instance a + specific module can be selected, and only the code executed in that + module will be profiled.</p> </desc> </func> <func> @@ -147,8 +147,8 @@ </type> <desc> <p>This function ensures that the results displayed by - <c>analyze/0,1,2</c> are printed both to - the file <c>File</c> and the screen.</p> + <c>analyze/0,1,2</c> are printed both to the file + <c>File</c> and the screen.</p> </desc> </func> <func> diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml index f4da132102..5d7bd464ce 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>2011</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -30,6 +30,69 @@ </header> <p>This document describes the changes made to the Tools application.</p> +<section><title>Tools 2.6.8</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The last tuple fun call has been removed from fprof.</p> + <p> + Own Id: OTP-10091 Aux Id: seq12067 </p> + </item> + <item> + <p> + Fix indentation of record fields in Emacs (Thanks to + Tomas Abrahamsson)</p> + <p> + Own Id: OTP-10120</p> + </item> + <item> + <p> + Documentation fixes (Thanks to Ricardo Catalinas Jim�nez + )</p> + <p> + Own Id: OTP-10121</p> + </item> + <item> + <p> + Remove Erlang-specific compilation error regexp in + erlang.el</p> + <p> + Own Id: OTP-10168</p> + </item> + <item> + <p> + Fix highlighting of atoms ending with a dollar sign</p> + <p> + Like this: 'atom$'. In that example, the last single + quote should be recognised as ending the atom. This needs + a font-lock workaround similar to the one for strings. + Thanks to Magnus Henoch</p> + <p> + Own Id: OTP-10178</p> + </item> + <item> + <p> Xref now accepts filenames with character codes + greater than 126. (Thanks to Emile Joubert for reporting + the issue.) </p> + <p> + Own Id: OTP-10192</p> + </item> + <item> + <p> + Add test_indentation target to lib/tools/emacs/Makefile</p> + <p> + Automatically indent test.erl.orig, save to test.erl, and + compare to test.erl.intended. Thanks to Magnus Henoch.</p> + <p> + Own Id: OTP-10226</p> + </item> + </list> + </section> + +</section> + <section><title>Tools 2.6.7</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/tools/doc/src/xref.xml b/lib/tools/doc/src/xref.xml index 17de66bb22..9706ae6746 100644 --- a/lib/tools/doc/src/xref.xml +++ b/lib/tools/doc/src/xref.xml @@ -1453,7 +1453,7 @@ Evaluates a predefined analysis. Note however that the code path will be traversed once for each used <seealso marker="#library_module">library module</seealso> while setting up module data. On the other hand, if there are only a few modules that are - used by not analyzed, using <c>code_path</c> may be faster + used but not analyzed, using <c>code_path</c> may be faster than setting the library path to <c>code:get_path()</c>. </p> <p>If the library path is set to <c>code_path</c>, the set of diff --git a/lib/tools/doc/src/xref_chapter.xml b/lib/tools/doc/src/xref_chapter.xml index 39c5545af9..566776eab0 100644 --- a/lib/tools/doc/src/xref_chapter.xml +++ b/lib/tools/doc/src/xref_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -301,7 +301,7 @@ and <c>|||</c>) are the only operators that accept both representations. This means that in order to analyze indirect calls using restriction, the <c>closure</c> operator (which creates the - <c>digraph</c> representation of graphs) has to been + <c>digraph</c> representation of graphs) has to be applied explicitly. </p> <p>As an example of analyzing indirect calls, the following Erlang diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile index 8533488463..69946be24a 100644 --- a/lib/tools/emacs/Makefile +++ b/lib/tools/emacs/Makefile @@ -71,9 +71,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/emacs + $(INSTALL_DIR) "$(RELSYSDIR)/emacs" $(INSTALL_DATA) $(EL_FILES) $(README_FILES) $(TEST_FILES) \ - $(RELSYSDIR)/emacs + "$(RELSYSDIR)/emacs" ifeq ($(DOCTYPE),pdf) release_docs_spec: @@ -82,7 +82,23 @@ ifeq ($(DOCTYPE),ps) release_docs_spec: else release_docs_spec: docs - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN_FILES) $(RELEASE_PATH)/man/man3 + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN_FILES) "$(RELEASE_PATH)/man/man3" endif endif + +EMACS ?= emacs + +test_indentation: + @rm -f test.erl + @rm -f test_indent.el + @echo '(load "erlang-start")' >> test_indent.el + @echo '(find-file "test.erl.orig")' >> test_indent.el + @echo "(require 'cl) ; required with Emacs < 23 for ignore-errors" >> test_indent.el + @echo '(erlang-mode)' >> test_indent.el + @echo '(toggle-debug-on-error)' >> test_indent.el + @echo '(erlang-indent-current-buffer)' >> test_indent.el + @echo '(write-file "test.erl")' >> test_indent.el + $(EMACS) --batch -Q -L . -l test_indent.el + diff -u test.erl.indented test.erl + @echo "No differences between expected and actual indentation" diff --git a/lib/tools/emacs/erlang-pkg.el b/lib/tools/emacs/erlang-pkg.el new file mode 100644 index 0000000000..decc696e21 --- /dev/null +++ b/lib/tools/emacs/erlang-pkg.el @@ -0,0 +1,3 @@ +(define-package "erlang" "2.7.0" + "Erlang major mode" + '((flymake-mode "0.4.6"))) diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index bc7a190fb4..e2bcd37def 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -1,7 +1,13 @@ -;; erlang.el --- Major modes for editing and running Erlang +;;; erlang.el --- Major modes for editing and running Erlang + +;; Copyright (C) 2004 Free Software Foundation, Inc. +;; Author: Anders Lindgren +;; Keywords: erlang, languages, processes +;; Date: 2011-12-11 + ;; %CopyrightBegin% ;; -;; Copyright Ericsson AB 1996-2011. All Rights Reserved. +;; Copyright Ericsson AB 1996-2012. All Rights Reserved. ;; ;; The contents of this file are subject to the Erlang Public License, ;; Version 1.1, (the "License"); you may not use this file except in @@ -15,10 +21,7 @@ ;; under the License. ;; ;; %CopyrightEnd% -;; -;; Copyright (C) 2004 Free Software Foundation, Inc. -;; Author: Anders Lindgren -;; Keywords: erlang, languages, processes +;; ;; Lars Thors�n's modifications of 2000-06-07 included. ;; The original version of this package was written by Robert Virding. @@ -483,10 +486,6 @@ function.") "*Non-nil means TAB in Erlang mode should always re-indent the current line, regardless of where in the line point is when the TAB command is used.") -(defvar erlang-error-regexp-alist - '(("^\\([^:( \t\n]+\\)[:(][ \t]*\\([0-9]+\\)[:) \t]" . (1 2))) - "*Patterns for matching Erlang errors.") - (defvar erlang-man-inhibit (eq system-type 'windows-nt) "Inhibit the creation of the Erlang Manual Pages menu. @@ -1327,7 +1326,6 @@ Other commands: (erlang-menu-init) (erlang-mode-variables) (erlang-check-module-name-init) - (erlang-add-compilation-alist erlang-error-regexp-alist) (erlang-man-init) (erlang-tags-init) (erlang-font-lock-init) @@ -1443,31 +1441,6 @@ Other commands: (set (make-local-variable 'outline-level) (lambda () 1)) (set (make-local-variable 'add-log-current-defun-function) 'erlang-current-defun)) - - -;; Compilation. -;; -;; The following code is compatible with the standard package `compilation', -;; making it possible to go to errors using `erlang-next-error' (or just -;; `next-error' in Emacs 21). -;; -;; The normal `compile' command works of course. For best result, please -;; execute `make' with the `-w' flag. -;; -;; Please see the variables named `compiling-..' above. - -(defun erlang-add-compilation-alist (alist) - (require 'compile) - (cond ((boundp 'compilation-error-regexp-alist) ; Emacs 19 - (while alist - (or (assoc (car (car alist)) compilation-error-regexp-alist) - (setq compilation-error-regexp-alist - (cons (car alist) compilation-error-regexp-alist))) - (setq alist (cdr alist)))) - ((boundp 'compilation-error-regexp) - ;; Emacs 18, Only one regexp is allowed. - (funcall (symbol-function 'set) - 'compilation-error-regexp (car (car alist)))))) (defun erlang-font-lock-init () "Initialize Font Lock for Erlang mode." @@ -1516,7 +1489,7 @@ Other commands: ;; A dollar sign right before the double quote that ends a ;; string is not a character escape. ;; - ;; And a "string" has with a double quote not escaped by a + ;; And a "string" consists of a double quote not escaped by a ;; dollar sign, any number of non-backslash non-newline ;; characters or escaped backslashes, a dollar sign ;; (otherwise we wouldn't care) and a double quote. This @@ -1525,6 +1498,8 @@ Other commands: ;; know whether matching started inside a string: limiting ;; search to a single line keeps things sane. . (("\\(?:^\\|[^$]\\)\"\\(?:[^\"\n]\\|\\\\\"\\)*\\(\\$\\)\"" 1 "w") + ;; Likewise for atoms + ("\\(?:^\\|[^$]\\)'\\(?:[^'\n]\\|\\\\'\\)*\\(\\$\\)'" 1 "w") ;; And the dollar sign in $\" escapes two characters, not ;; just one. ("\\(\\$\\)\\\\\\\"" 1 "'")))))) @@ -2986,18 +2961,52 @@ This assumes that the preceding expression is either simple (forward-sexp (- arg)) (let ((col (current-column))) (skip-chars-backward " \t") - ;; Needed to match the colon in "'foo':'bar'". - (if (not (memq (preceding-char) '(?# ?:))) - col - ;; Special hack to handle: (note line break) - ;; [#myrecord{ - ;; foo = foo}] - (or - (ignore-errors - (backward-char 1) - (forward-sexp -1) - (current-column)) - col))))) + ;; Special hack to handle: (note line break) + ;; [#myrecord{ + ;; foo = foo}] + ;; where the call (forward-sexp -1) will fail when point is at the `#'. + (or + (ignore-errors + ;; Needed to match the colon in "'foo':'bar'". + (cond ((eq (preceding-char) ?:) + (backward-char 1) + (forward-sexp -1) + (current-column)) + ((eq (preceding-char) ?#) + ;; We may now be at: + ;; - either a construction of a new record + ;; - or update of a record, in which case we want + ;; the column of the expression to be updated. + ;; + ;; To see which of the two cases we are at, we first + ;; move an expression backwards, check for keywords, + ;; then immediately an expression forwards. Moving + ;; backwards skips past tokens like `,' or `->', but + ;; when moving forwards again, we won't skip past such + ;; tokens. We use this: if, after having moved + ;; forwards, we're back where we started, then it was + ;; a record update. + ;; The check for keywords is to detect cases like: + ;; case Something of #record_construction{...} + (backward-char 1) + (let ((record-start (point)) + (record-start-col (current-column))) + (forward-sexp -1) + (let ((preceding-expr-col (current-column)) + ;; white space definition according to erl_scan + (white-space "\000-\040\200-\240")) + (if (erlang-at-keyword) + ;; The (forward-sexp -1) call moved past a keyword + (1+ record-start-col) + (forward-sexp 1) + (skip-chars-forward white-space record-start) + ;; Are we back where we started? If so, it was an update. + (if (= (point) record-start) + preceding-expr-col + (goto-char record-start) + (1+ (current-column))))))) + (t col))) + col)))) (defun erlang-indent-parenthesis (stack-position) (let ((previous (erlang-indent-find-preceding-expr))) @@ -4860,7 +4869,6 @@ The following special commands are available: (set (make-local-variable 'compilation-old-error-list) nil)) ;; Needed when compiling directly from the Erlang shell. (setq compilation-last-buffer (current-buffer)) - (erlang-add-compilation-alist erlang-error-regexp-alist) (setq comint-prompt-regexp "^[^>=]*> *") (setq comint-eol-on-send t) (setq comint-input-ignoredups t) diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented index 2948ccf1b5..6c9343f6cb 100644 --- a/lib/tools/emacs/test.erl.indented +++ b/lib/tools/emacs/test.erl.indented @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -215,6 +215,11 @@ highlighting(X) % Function definitions should be highlighted "char $in string", atom, + 'atom$', atom, 'atom$', atom, + 'atom\$', atom, + + 'char $in atom', atom, + $[, ${, $\\, atom, ?MACRO_1, ?MACRO_2(foo), @@ -657,3 +662,41 @@ indent_comprehensions() -> foo() -> [#foo{ foo = foo}]. + +%% Record indentation +some_function_with_a_very_long_name() -> + #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b}, + case dummy_function_with_a_very_very_long_name(x) of + #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b} -> + ok; + Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b} -> + Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b}; + #xyz{ + a=1, + b=2} -> + ok + end. + +another_function_with_a_very_very_long_name() -> + #rec{ + field1=1, + field2=1}. + +some_function_name_xyz(xyzzy, #some_record{ + field1=Field1, + field2=Field2}) -> + SomeVariable = f(#'Some-long-record-name'{ + field_a = 1, + 'inter-xyz-parameters' = + #'Some-other-very-long-record-name'{ + field2 = Field1, + field2 = Field2}}), + {ok, SomeVariable}. diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig index 1221c5655e..0f8c4a9175 100644 --- a/lib/tools/emacs/test.erl.orig +++ b/lib/tools/emacs/test.erl.orig @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -215,6 +215,11 @@ highlighting(X) % Function definitions should be highlighted "char $in string", atom, + 'atom$', atom, 'atom$', atom, + 'atom\$', atom, + + 'char $in atom', atom, + $[, ${, $\\, atom, ?MACRO_1, ?MACRO_2(foo), @@ -657,3 +662,41 @@ ok. foo() -> [#foo{ foo = foo}]. + +%% Record indentation +some_function_with_a_very_long_name() -> + #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b}, + case dummy_function_with_a_very_very_long_name(x) of + #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b} -> + ok; + Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b} -> + Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b}; + #xyz{ + a=1, + b=2} -> + ok + end. + +another_function_with_a_very_very_long_name() -> + #rec{ + field1=1, + field2=1}. + +some_function_name_xyz(xyzzy, #some_record{ + field1=Field1, + field2=Field2}) -> + SomeVariable = f(#'Some-long-record-name'{ + field_a = 1, + 'inter-xyz-parameters' = + #'Some-other-very-long-record-name'{ + field2 = Field1, + field2 = Field2}}), + {ok, SomeVariable}. diff --git a/lib/tools/emacs/vsn.mk b/lib/tools/emacs/vsn.mk index f33ea8b519..a495da3453 100644 --- a/lib/tools/emacs/vsn.mk +++ b/lib/tools/emacs/vsn.mk @@ -1,3 +1,2 @@ -EMACS_VSN = 2.4.13 - +EMACS_VSN = 2.7.0 diff --git a/lib/tools/examples/Makefile b/lib/tools/examples/Makefile index 9fb8434633..fc14ee54d6 100644 --- a/lib/tools/examples/Makefile +++ b/lib/tools/examples/Makefile @@ -50,7 +50,7 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(EXAMPLE_FILES) "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/tools/priv/Makefile b/lib/tools/priv/Makefile index 6fea580c00..b32ba5820d 100644 --- a/lib/tools/priv/Makefile +++ b/lib/tools/priv/Makefile @@ -58,9 +58,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(TOOL_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(HTDOCS_FILES) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(TOOL_FILES) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/tools/src/Makefile b/lib/tools/src/Makefile index 360f4f8f29..abe1389771 100644 --- a/lib/tools/src/Makefile +++ b/lib/tools/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2010. All Rights Reserved. +# Copyright Ericsson AB 1996-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -105,10 +105,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) \ - $(RELSYSDIR)/ebin + "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index e21bd1b88c..10f14b0a49 100644 --- a/lib/tools/src/cover.erl +++ b/lib/tools/src/cover.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -26,12 +26,17 @@ %% ARCHITECTURE %% The coverage tool consists of one process on each node involved in %% coverage analysis. The process is registered as 'cover_server' -%% (?SERVER). All cover_servers in the distributed system are linked -%% together. The cover_server on the 'main' node is in charge, and it -%% traps exits so it can detect nodedown or process crashes on the -%% remote nodes. This process is implemented by the functions -%% init_main/1 and main_process_loop/1. The cover_server on the remote -%% nodes are implemented by the functions init_remote/2 and +%% (?SERVER). The cover_server on the 'main' node is in charge, and +%% it monitors the cover_servers on all remote nodes. When it gets a +%% 'DOWN' message for another cover_server, it marks the node as +%% 'lost'. If a nodeup is received for a lost node the main node +%% ensures that the cover compiled modules are loaded again. If the +%% remote node was alive during the disconnected periode, cover data +%% for this periode will also be included in the analysis. +%% +%% The cover_server process on the main node is implemented by the +%% functions init_main/1 and main_process_loop/1. The cover_server on +%% the remote nodes are implemented by the functions init_remote/2 and %% remote_process_loop/1. %% %% TABLES @@ -81,15 +86,17 @@ export/1, export/2, import/1, modules/0, imported/0, imported_modules/0, which_nodes/0, is_compiled/1, reset/1, reset/0, + flush/1, stop/0, stop/1]). --export([remote_start/1]). +-export([remote_start/1,get_main_node/0]). %-export([bump/5]). -export([transform/4]). % for test purposes -record(main_state, {compiled=[], % [{Module,File}] imported=[], % [{Module,File,ImportFile}] stopper, % undefined | pid() - nodes=[]}). % [Node] + nodes=[], % [Node] + lost_nodes=[]}). % [Node] -record(remote_state, {compiled=[], % [{Module,File}] main_node}). % atom() @@ -497,6 +504,19 @@ stop(Node) when is_atom(Node) -> stop(Nodes) -> call({stop,remove_myself(Nodes,[])}). +%% flush(Nodes) -> ok | {error,not_main_node} +%% Nodes = [Node] | Node +%% Node = atom() +%% Error = {not_cover_compiled,Module} +flush(Node) when is_atom(Node) -> + flush([Node]); +flush(Nodes) -> + call({flush,remove_myself(Nodes,[])}). + +%% Used by test_server only. Not documented. +get_main_node() -> + call(get_main_node). + %% bump(Module, Function, Arity, Clause, Line) %% Module = Function = atom() %% Arity = Clause = Line = integer() @@ -541,7 +561,10 @@ remote_call(Node,Request) -> Return = receive {'DOWN', Ref, _Type, _Object, _Info} -> - {error,node_dead}; + case Request of + {remote,stop} -> ok; + _ -> {error,node_dead} + end; {?SERVER,Reply} -> Reply end, @@ -569,40 +592,14 @@ init_main(Starter) -> ets:new(?BINARY_TABLE, [set, named_table]), ets:new(?COLLECTION_TABLE, [set, public, named_table]), ets:new(?COLLECTION_CLAUSE_TABLE, [set, public, named_table]), - process_flag(trap_exit,true), + net_kernel:monitor_nodes(true), Starter ! {?SERVER,started}, main_process_loop(#main_state{}). main_process_loop(State) -> receive {From, {start_nodes,Nodes}} -> - ThisNode = node(), - StartedNodes = - lists:foldl( - fun(Node,Acc) -> - case rpc:call(Node,cover,remote_start,[ThisNode]) of - {ok,RPid} -> - link(RPid), - [Node|Acc]; - Error -> - io:format("Could not start cover on ~w: ~p\n", - [Node,Error]), - Acc - end - end, - [], - Nodes), - - %% In case some of the compiled modules have been unloaded they - %% should not be loaded on the new node. - {_LoadedModules,Compiled} = - get_compiled_still_loaded(State#main_state.nodes, - State#main_state.compiled), - remote_load_compiled(StartedNodes,Compiled), - - State1 = - State#main_state{nodes = State#main_state.nodes ++ StartedNodes, - compiled = Compiled}, + {StartedNodes,State1} = do_start_nodes(Nodes, State), reply(From, {ok,StartedNodes}), main_process_loop(State1); @@ -707,8 +704,13 @@ main_process_loop(State) -> {From, {stop,Nodes}} -> remote_collect('_',Nodes,true), reply(From, ok), - State1 = State#main_state{nodes=State#main_state.nodes--Nodes}, - main_process_loop(State1); + Nodes1 = State#main_state.nodes--Nodes, + main_process_loop(State#main_state{nodes=Nodes1}); + + {From, {flush,Nodes}} -> + remote_collect('_',Nodes,false), + reply(From, ok), + main_process_loop(State); {From, stop} -> lists:foreach( @@ -788,14 +790,30 @@ main_process_loop(State) -> end, main_process_loop(S); - {'EXIT',Pid,_Reason} -> - %% Exit is trapped on the main node only, so this will only happen - %% there. I assume that I'm only linked to cover_servers on remote - %% nodes, so this must be one of them crashing. - %% Remove node from list! - State1 = State#main_state{nodes=State#main_state.nodes--[node(Pid)]}, + {'DOWN', _MRef, process, {?SERVER,Node}, _Info} -> + %% A remote cover_server is down, mark as lost + Nodes = State#main_state.nodes--[Node], + Lost = [Node|State#main_state.lost_nodes], + main_process_loop(State#main_state{nodes=Nodes,lost_nodes=Lost}); + + {nodeup,Node} -> + State1 = + case lists:member(Node,State#main_state.lost_nodes) of + true -> + sync_compiled(Node,State); + false -> + State + end, main_process_loop(State1); + + {nodedown,_} -> + %% Will be taken care of when 'DOWN' message arrives + main_process_loop(State); + {From, get_main_node} -> + reply(From, node()), + main_process_loop(State); + get_status -> io:format("~p~n",[State]), main_process_loop(State) @@ -850,7 +868,16 @@ remote_process_loop(State) -> {remote,stop} -> reload_originals(State#remote_state.compiled), unregister(?SERVER), - remote_reply(State#remote_state.main_node, ok); + ok; % not replying since 'DOWN' message will be received anyway + + {remote,get_compiled} -> + remote_reply(State#remote_state.main_node, + State#remote_state.compiled), + remote_process_loop(State); + + {From, get_main_node} -> + remote_reply(From, State#remote_state.main_node), + remote_process_loop(State); get_status -> io:format("~p~n",[State]), @@ -961,6 +988,36 @@ unload([]) -> %%%--Handling of remote nodes-------------------------------------------- +do_start_nodes(Nodes, State) -> + ThisNode = node(), + StartedNodes = + lists:foldl( + fun(Node,Acc) -> + case rpc:call(Node,cover,remote_start,[ThisNode]) of + {ok,_RPid} -> + erlang:monitor(process,{?SERVER,Node}), + [Node|Acc]; + Error -> + io:format("Could not start cover on ~w: ~p\n", + [Node,Error]), + Acc + end + end, + [], + Nodes), + + %% In case some of the compiled modules have been unloaded they + %% should not be loaded on the new node. + {_LoadedModules,Compiled} = + get_compiled_still_loaded(State#main_state.nodes, + State#main_state.compiled), + remote_load_compiled(StartedNodes,Compiled), + + State1 = + State#main_state{nodes = State#main_state.nodes ++ StartedNodes, + compiled = Compiled}, + {StartedNodes, State1}. + %% start the cover_server on a remote node remote_start(MainNode) -> case whereis(?SERVER) of @@ -984,6 +1041,30 @@ remote_start(MainNode) -> {error,{already_started,Pid}} end. +%% If a lost node comes back, ensure that main and remote node has the +%% same cover compiled modules. Note that no action is taken if the +%% same {Mod,File} eksists on both, i.e. code change is not handled! +sync_compiled(Node,State) -> + #main_state{compiled=Compiled0,nodes=Nodes,lost_nodes=Lost}=State, + State1 = + case remote_call(Node,{remote,get_compiled}) of + {error,node_dead} -> + {_,S} = do_start_nodes([Node],State), + S; + {error,_} -> + State; + RemoteCompiled -> + {_,Compiled} = get_compiled_still_loaded(Nodes,Compiled0), + Unload = [UM || {UM,_}=U <- RemoteCompiled, + false == lists:member(U,Compiled)], + remote_unload([Node],Unload), + Load = [L || L <- Compiled, + false == lists:member(L,RemoteCompiled)], + remote_load_compiled([Node],Load), + State#main_state{compiled=Compiled, nodes=[Node|Nodes]} + end, + State1#main_state{lost_nodes=Lost--[Node]}. + %% Load a set of cover compiled modules on remote nodes, %% We do it ?MAX_MODS modules at a time so that we don't %% run out of memory on the cover_server node. @@ -1094,7 +1175,6 @@ remove_myself([Node|Nodes],Acc) -> remove_myself(Nodes,[Node|Acc]); remove_myself([],Acc) -> Acc. - %%%--Handling of modules state data-------------------------------------- @@ -2254,7 +2334,13 @@ do_reset2([]) -> do_clear(Module) -> ets:match_delete(?COVER_CLAUSE_TABLE, {Module,'_'}), ets:match_delete(?COVER_TABLE, {#bump{module=Module},'_'}), - ets:match_delete(?COLLECTION_TABLE, {#bump{module=Module},'_'}). + case lists:member(?COLLECTION_TABLE, ets:all()) of + true -> + %% We're on the main node + ets:match_delete(?COLLECTION_TABLE, {#bump{module=Module},'_'}); + false -> + ok + end. not_loaded(Module, unloaded, State) -> do_clear(Module), @@ -2307,7 +2393,7 @@ pmap(Fun, [E | Rest], Pids, Limit, Cnt, Acc) when Cnt < Limit -> pmap(Fun, Rest, Pids ++ [Pid], Limit, Cnt + 1, Acc); pmap(Fun, List, [Pid | Pids], Limit, Cnt, Acc) -> receive - {'DOWN', _Ref, process, _, _} -> + {'DOWN', _Ref, process, X, _} when is_pid(X) -> pmap(Fun, List, [Pid | Pids], Limit, Cnt - 1, Acc); {res, Pid, Res} -> pmap(Fun, List, Pids, Limit, Cnt, [Res | Acc]) @@ -2316,6 +2402,6 @@ pmap(_Fun, [], [], _Limit, 0, Acc) -> lists:reverse(Acc); pmap(Fun, [], [], Limit, Cnt, Acc) -> receive - {'DOWN', _Ref, process, _, _} -> + {'DOWN', _Ref, process, X, _} when is_pid(X) -> pmap(Fun, [], [], Limit, Cnt - 1, Acc) end. diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl index 8165a6c13a..4cbb910f11 100644 --- a/lib/tools/src/fprof.erl +++ b/lib/tools/src/fprof.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -89,9 +89,7 @@ dbg(_, _, _) -> apply({M, F}, Args) when is_atom(M), is_atom(F), is_list(Args) -> - Arity = length(Args), - Function = fun M:F/Arity, - apply_1(Function, Args, []); + apply_1(M, F, Args, []); apply(Fun, Args) when is_function(Fun), is_list(Args) -> apply_1(Fun, Args, []); @@ -99,26 +97,25 @@ apply(A, B) -> erlang:error(badarg, [A, B]). apply(M, F, Args) when is_atom(M), is_atom(F), is_list(Args) -> - apply_1({M, F}, Args, []); + apply_1(M, F, Args, []); apply({M, F}, Args, Options) when is_atom(M), is_atom(F), is_list(Args), is_list(Options) -> - Arity = length(Args), - Function = fun M:F/Arity, - apply_1(Function, Args, Options); + apply_1(M, F, Args, Options); apply(Fun, Args, Options) when is_function(Fun), is_list(Args), is_list(Options) -> apply_1(Fun, Args, Options); apply(A, B, C) -> erlang:error(badarg, [A, B, C]). -apply(Module, Function, Args, Options) - when is_atom(Module), is_atom(Function), is_list(Args), is_list(Options) -> - Arity = length(Args), - Fun = fun Module:Function/Arity, - apply_1(Fun, Args, Options); +apply(M, F, Args, Options) + when is_atom(M), is_atom(F), is_list(Args), is_list(Options) -> + apply_1(M, F, Args, Options); apply(A, B, C, D) -> erlang:error(badarg, [A, B, C, D]). +apply_1(M, F, Args, Options) -> + Arity = length(Args), + apply_1(fun M:F/Arity, Args, Options). apply_1(Function, Args, Options) -> {[_, Procs, Continue], Options_1} = diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src index cd9b622f15..553c5eb96b 100644 --- a/lib/tools/src/tools.app.src +++ b/lib/tools/src/tools.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -24,6 +24,7 @@ eprof, fprof, instrument, + lcnt, make, xref, xref_base, diff --git a/lib/tools/src/xref_utils.erl b/lib/tools/src/xref_utils.erl index 9d4a175d88..680563e9df 100644 --- a/lib/tools/src/xref_utils.erl +++ b/lib/tools/src/xref_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -141,7 +141,7 @@ is_string([], _) -> is_string(Term, C) -> is_string1(Term, C). -is_string1([H | T], C) when H > C, H < 127 -> +is_string1([H | T], C) when H > C -> is_string1(T, C); is_string1([], _) -> true; diff --git a/lib/tools/test/Makefile b/lib/tools/test/Makefile index 8019b7269f..86c81217b6 100644 --- a/lib/tools/test/Makefile +++ b/lib/tools/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2011. All Rights Reserved. +# Copyright Ericsson AB 1997-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -84,11 +84,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(EMAKEFILE) \ - $(ERL_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(ERL_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index 576d7e261c..3bf1b44af8 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -23,7 +23,7 @@ init_per_group/2,end_per_group/2]). -export([start/1, compile/1, analyse/1, misc/1, stop/1, - distribution/1, export_import/1, + distribution/1, reconnect/1, die_and_reconnect/1, export_import/1, otp_5031/1, eif/1, otp_5305/1, otp_5418/1, otp_6115/1, otp_7095/1, otp_8188/1, otp_8270/1, otp_8273/1, otp_8340/1]). @@ -45,7 +45,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> case whereis(cover_server) of undefined -> - [start, compile, analyse, misc, stop, distribution, + [start, compile, analyse, misc, stop, + distribution, reconnect, die_and_reconnect, export_import, otp_5031, eif, otp_5305, otp_5418, otp_6115, otp_7095, otp_8188, otp_8270, otp_8273, otp_8340]; @@ -326,14 +327,16 @@ distribution(Config) when is_list(Config) -> ?line {ok,N1} = ?t:start_node(cover_SUITE_distribution1,slave,[]), ?line {ok,N2} = ?t:start_node(cover_SUITE_distribution2,slave,[]), ?line {ok,N3} = ?t:start_node(cover_SUITE_distribution3,slave,[]), + ?line {ok,N4} = ?t:start_node(cover_SUITE_distribution4,slave,[]), %% Check that an already compiled module is loaded on new nodes ?line {ok,f} = cover:compile(f), - ?line {ok,[_,_,_]} = cover:start(nodes()), + ?line {ok,[_,_,_,_]} = cover:start(nodes()), ?line cover_compiled = code:which(f), ?line cover_compiled = rpc:call(N1,code,which,[f]), ?line cover_compiled = rpc:call(N2,code,which,[f]), ?line cover_compiled = rpc:call(N3,code,which,[f]), + ?line cover_compiled = rpc:call(N4,code,which,[f]), %% Check that a node cannot be started twice ?line {ok,[]} = cover:start(N2), @@ -351,6 +354,7 @@ distribution(Config) when is_list(Config) -> ?line cover_compiled = rpc:call(N1,code,which,[v]), ?line cover_compiled = rpc:call(N2,code,which,[v]), ?line cover_compiled = rpc:call(N3,code,which,[v]), + ?line cover_compiled = rpc:call(N4,code,which,[v]), %% this is lost when the node is killed ?line rpc:call(N3,f,f2,[]), @@ -385,6 +389,18 @@ distribution(Config) when is_list(Config) -> %% reset on the remote node(s)) ?line check_f_calls(1,1), + %% Another checn that data is not fetched twice, i.e. when flushed + %% then analyse should not add the same data again. + ?line rpc:call(N4,f,f2,[]), + ?line ok = cover:flush(N4), + ?line check_f_calls(1,2), + + %% Check that flush collects data so calls are not lost if node is killed + ?line rpc:call(N4,f,f2,[]), + ?line ok = cover:flush(N4), + ?line rpc:call(N4,erlang,halt,[]), + ?line check_f_calls(1,3), + %% Check that stop() unloads on all nodes ?line ok = cover:stop(), ?line timer:sleep(100), %% Give nodes time to unload on slow machines. @@ -393,20 +409,117 @@ distribution(Config) when is_list(Config) -> ?line true = is_unloaded(LocalBeam), ?line true = is_unloaded(N2Beam), - %% Check that cover_server on remote node dies if main node dies + %% Check that cover_server on remote node does not die if main node dies ?line {ok,[N1]} = cover:start(N1), - ?line true = is_pid(rpc:call(N1,erlang,whereis,[cover_server])), + ?line true = is_pid(N1Server = rpc:call(N1,erlang,whereis,[cover_server])), ?line exit(whereis(cover_server),kill), - ?line timer:sleep(10), - ?line undefined = rpc:call(N1,erlang,whereis,[cover_server]), - + ?line timer:sleep(100), + ?line N1Server = rpc:call(N1,erlang,whereis,[cover_server]), + %% Cleanup ?line Files = lsfiles(), ?line remove(files(Files, ".beam")), ?line ?t:stop_node(N1), ?line ?t:stop_node(N2). - +%% Test that a lost node is reconnected +reconnect(Config) -> + DataDir = ?config(data_dir, Config), + ok = file:set_cwd(DataDir), + + {ok,a} = compile:file(a), + {ok,b} = compile:file(b), + {ok,f} = compile:file(f), + + {ok,N1} = ?t:start_node(cover_SUITE_reconnect,peer, + [{args," -pa " ++ DataDir},{start_cover,false}]), + {ok,a} = cover:compile(a), + {ok,f} = cover:compile(f), + {ok,[N1]} = cover:start(nodes()), + + %% Some calls to check later + rpc:call(N1,f,f1,[]), + cover:flush(N1), + rpc:call(N1,f,f1,[]), + + %% This will cause a call to f:f2() when nodes()==[] on N1 + rpc:cast(N1,f,call_f2_when_isolated,[]), + + %% Disconnect and check that node is removed from main cover node + net_kernel:disconnect(N1), + [] = cover:which_nodes(), + timer:sleep(500), % allow some time for the f:f2() call + + %% Do some add one module (b) and remove one module (a) + code:purge(a), + {module,a} = code:load_file(a), + {ok,b} = cover:compile(b), + cover_compiled = code:which(b), + + [] = cover:which_nodes(), + check_f_calls(1,0), % only the first call - before the flush + + %% Reconnect the node and check that b and f are cover compiled but not a + net_kernel:connect_node(N1), + timer:sleep(100), + [N1] = cover:which_nodes(), % we are reconnected + cover_compiled = rpc:call(N1,code,which,[b]), + cover_compiled = rpc:call(N1,code,which,[f]), + ABeam = rpc:call(N1,code,which,[a]), + false = (cover_compiled==ABeam), + + %% Ensure that we have: + %% * one f1 call from before the flush, + %% * one f1 call from after the flush but before disconnect + %% * one f2 call when disconnected + check_f_calls(2,1), + + cover:stop(), + ?t:stop_node(N1), + ok. + +%% Test that a lost node is reconnected - also if it has been dead +die_and_reconnect(Config) -> + DataDir = ?config(data_dir, Config), + ok = file:set_cwd(DataDir), + + {ok,f} = compile:file(f), + + NodeName = cover_SUITE_die_and_reconnect, + {ok,N1} = ?t:start_node(NodeName,peer, + [{args," -pa " ++ DataDir},{start_cover,false}]), + %% {ok,a} = cover:compile(a), + {ok,f} = cover:compile(f), + {ok,[N1]} = cover:start(nodes()), + + %% Some calls to check later + rpc:call(N1,f,f1,[]), + cover:flush(N1), + rpc:call(N1,f,f1,[]), + + %% Kill the node + rpc:call(N1,erlang,halt,[]), + [] = cover:which_nodes(), + + check_f_calls(1,0), % only the first call - before the flush + + %% Restart the node and check that cover reconnects + {ok,N1} = ?t:start_node(NodeName,peer, + [{args," -pa " ++ DataDir},{start_cover,false}]), + timer:sleep(100), + [N1] = cover:which_nodes(), % we are reconnected + cover_compiled = rpc:call(N1,code,which,[f]), + + %% One more call... + rpc:call(N1,f,f1,[]), + + %% Ensure that no more calls are counted + check_f_calls(2,0), + + cover:stop(), + ?t:stop_node(N1), + ok. + export_import(suite) -> []; export_import(Config) when is_list(Config) -> ?line DataDir = ?config(data_dir, Config), @@ -574,14 +687,7 @@ otp_5418(Config) when is_list(Config) -> ok. -otp_6115(suite) -> []; otp_6115(Config) when is_list(Config) -> - case erlang:system_info(heap_type) of - hybrid -> {skip,"Hybrid-heap emulator doesn't keep track of funs"}; - _ -> otp_6115_1(Config) - end. - -otp_6115_1(Config) -> ?line {ok, CWD} = file:get_cwd(), ?line Dir = filename:join(?config(data_dir, Config), otp_6115), ?line ok = file:set_cwd(Dir), @@ -1245,4 +1351,4 @@ is_unloaded(What) -> end. check_f_calls(F1,F2) -> - {ok,[{{f,f1,0},F1},{{f,f2,0},F2}]} = cover:analyse(f,calls,function). + {ok,[{{f,f1,0},F1},{{f,f2,0},F2}|_]} = cover:analyse(f,calls,function). diff --git a/lib/tools/test/cover_SUITE_data/f.erl b/lib/tools/test/cover_SUITE_data/f.erl index 1ef8bbdb49..ce2963014a 100644 --- a/lib/tools/test/cover_SUITE_data/f.erl +++ b/lib/tools/test/cover_SUITE_data/f.erl @@ -1,5 +1,5 @@ -module(f). --export([f1/0,f2/0]). +-export([f1/0,f2/0,call_f2_when_isolated/0]). f1() -> f1_line1, @@ -8,3 +8,12 @@ f1() -> f2() -> f2_line1, f2_line2. + +call_f2_when_isolated() -> + case nodes() of + [] -> + f2(); + _ -> + timer:sleep(100), + call_f2_when_isolated() + end. diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl index 78e49044a5..fd3e111d8d 100644 --- a/lib/tools/test/xref_SUITE.erl +++ b/lib/tools/test/xref_SUITE.erl @@ -53,7 +53,7 @@ analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]). -export([ - format_error/1, otp_7423/1, otp_7831/1]). + format_error/1, otp_7423/1, otp_7831/1, otp_10192/1]). -import(lists, [append/2, flatten/1, keysearch/3, member/2, sort/1, usort/1]). @@ -86,7 +86,7 @@ groups() -> fun_mfa_r14, fun_mfa_vars, qlc]}, {analyses, [], [analyze, basic, md, q, variables, unused_locals]}, - {misc, [], [format_error, otp_7423, otp_7831]}]. + {misc, [], [format_error, otp_7423, otp_7831, otp_10192]}]. init_per_suite(Config) -> init(Config). @@ -2515,6 +2515,18 @@ otp_7831(Conf) when is_list(Conf) -> ?line xref:stop(Pid2), ok. +otp_10192(suite) -> []; +otp_10192(doc) -> + ["OTP-10192. Allow filenames with character codes greater than 126."]; +otp_10192(Conf) when is_list(Conf) -> + PrivDir = ?privdir, + {ok, _Pid} = xref:start(s), + Dir = filename:join(PrivDir, "�"), + ok = file:make_dir(Dir), + {ok, []} = xref:add_directory(s, Dir), + xref:stop(s), + ok. + %%% %%% Utilities %%% diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk index 30a6d282fe..788ee12900 100644 --- a/lib/tools/vsn.mk +++ b/lib/tools/vsn.mk @@ -1 +1 @@ -TOOLS_VSN = 2.6.7 +TOOLS_VSN = 2.6.8 |