diff options
Diffstat (limited to 'lib/tools')
26 files changed, 965 insertions, 348 deletions
diff --git a/lib/tools/c_src/Makefile.in b/lib/tools/c_src/Makefile.in index 0382d3228d..aea5686ae9 100644 --- a/lib/tools/c_src/Makefile.in +++ b/lib/tools/c_src/Makefile.in @@ -17,6 +17,7 @@ # %CopyrightEnd% # +include $(ERL_TOP)/make/output.mk include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/erts/include/internal/$(TARGET)/ethread.mk @@ -150,7 +151,7 @@ _create_dirs := $(shell mkdir -p $(CREATE_DIRS)) all: $(PROGS) $(DRIVERS) $(ERTS_LIB): - cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE) + $(make_verbose)cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE) docs: @@ -167,7 +168,7 @@ clean: # $(EMEM_OBJ_DIR)/%.o: %.c - $(CC) $(EMEM_CFLAGS) -o $@ -c $< + $(V_CC) $(EMEM_CFLAGS) -o $@ -c $< # # Driver targets @@ -178,7 +179,7 @@ $(EMEM_OBJ_DIR)/%.o: %.c # $(BIN_DIR)/emem$(TYPEMARKER)@EXEEXT@: $(EMEM_OBJS) $(ERTS_LIB) - $(PRE_LD) $(LD) $(EMEM_LDFLAGS) -o $@ $(EMEM_OBJS) $(EMEM_LIBS) + $(ld_verbose)$(PRE_LD) $(LD) $(EMEM_LDFLAGS) -o $@ $(EMEM_OBJS) $(EMEM_LIBS) # # Release targets @@ -221,7 +222,8 @@ SED_DEPEND=sed '$(SED_REPL_OBJ_DIR);$(SED_REPL_TT_DIR);$(SED_REPL_TARGET);$(SED_ DEPEND_MK=depend.mk dep depend: - @echo "Generating dependency file $(DEPEND_MK)..." + [ $(v_p) == 0 ] && echo " GEN "$(DEPEND_MK) + $(V_colon)@echo "Generating dependency file $(DEPEND_MK)..." @echo "# Generated dependency rules." > $(DEPEND_MK); @echo "# Do *not* edit this file; instead, run 'make depend'." \ >> $(DEPEND_MK); diff --git a/lib/tools/doc/src/cover.xml b/lib/tools/doc/src/cover.xml index 683acc025d..beefd4ee8d 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>2013</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> @@ -207,7 +214,9 @@ <c>{no_abstract_code,BeamFile}</c> is returned. If the abstract code is encrypted, and no key is available for decrypting it, the error reason - <c><![CDATA[{encrypted_abstract_code,BeamFile} is returned. <p>If only the module name (i.e. not the full name of the <c>.beam]]></c> file) is given to this function, the + <c>{encrypted_abstract_code,BeamFile}</c> is returned.</p> + <p>If only the module name (i.e. not the full name of the + <c>.beam</c> file) is given to this function, the <c>.beam</c> file is found by calling <c>code:which(Module)</c>. If no <c>.beam</c> file is found, the error reason <c>non_existing</c> is returned. If the @@ -306,9 +315,15 @@ file, i.e. using <c>compile_beam/1</c> or <c>compile_beam_directory/0,1</c>, it is assumed that the source code can be found in the same directory as the - <c>.beam</c> file, or in <c>../src</c> relative to that - directory. If no source code is found, - <c>,{error,no_source_code_found}</c> is returned.</p> + <c>.beam</c> file, in <c>../src</c> relative to that + directory, or using the source path in + <c>Module:module_info(compile)</c>. When using the latter, + two paths are examined: first the one constructed by + joining <c>../src</c> and the tail of the compiled path + below a trailing <c>src</c> component, then the compiled + path itself. + If no source code is found, + <c>{error,no_source_code_found}</c> is returned.</p> <p>HINT: It is possible to issue multiple analyse_to_file commands at the same time. </p> </desc> @@ -477,6 +492,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/notes.xml b/lib/tools/doc/src/notes.xml index 5d7bd464ce..20dc9679f1 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>2012</year> + <year>2004</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -30,6 +30,114 @@ </header> <p>This document describes the changes made to the Tools application.</p> +<section><title>Tools 2.6.10</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Fix a bug in cover when used with no_auto_import. Thanks + to Jos� Valim.</p> + <p> + Own Id: OTP-10778</p> + </item> + </list> + </section> + +</section> + +<section><title>Tools 2.6.9</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Add missing modules in app-file</p> + <p> + Own Id: OTP-10439</p> + </item> + <item> + <p> + Make erlang-mode more compatible with package.el (Thanks + to Gleb Peregud)</p> + <p> + Own Id: OTP-10465</p> + </item> + <item> + <p> + Fix various typos (thanks to Tuncer Ayaz)</p> + <p> + Own Id: OTP-10611</p> + </item> + <item> + <p> + Add separate face for exported functions (Thanks to + Thomas J�rvstrand)</p> + <p> + Own Id: OTP-10637</p> + </item> + <item> + <p> + The BIF highlighting in the emacs mode has been updated + to correspond with the correct BIFs.</p> + <p> + Own Id: OTP-10774</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> Support for Unicode has been implemented. </p> + <p> + Own Id: OTP-10302</p> + </item> + <item> + <p> + A new function, cover:flush(Nodes), is added which will + fetch data from remote nodes without stopping cover on + those nodes. This is used by test_server and common_test + when it is safe to assume that the node will be + terminated after the test anyway. The purpose is to avoid + processes crashing when re-loading the original beam if + the processes is still running old code.</p> + <p> + Remote nodes will now continue to count code coverage if + the connection to the main node is broken. Earlier, a + broken connection would cause the cover_server on the + remote node to die and thus any still cover compiled + modules would cause process crash when trying to insert + cover data in ets tables that used to exist on the + cover_server. The new functionality also involves + synchronization with the main node if the nodes are + reconnected.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-10523 Aux Id: OTP-10427 </p> + </item> + <item> + <p>Where necessary a comment stating encoding has been + added to Erlang files. The comment is meant to be removed + in Erlang/OTP R17B when UTF-8 becomes the default + encoding. </p> + <p> + Own Id: OTP-10630</p> + </item> + <item> + <p> + Fix syntax highlighting of $\' in Emacs mode. Thanks to + Magnus Henoch.</p> + <p> + Own Id: OTP-10766</p> + </item> + </list> + </section> + +</section> + <section><title>Tools 2.6.8</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..891a81639a 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>2011</year> + <year>2000</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -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/emacs/erlang-pkg.el b/lib/tools/emacs/erlang-pkg.el new file mode 100644 index 0000000000..4d0aa6fcd3 --- /dev/null +++ b/lib/tools/emacs/erlang-pkg.el @@ -0,0 +1,3 @@ +(define-package "erlang" "2.7.0" + "Erlang major mode" + '()) diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index b5da9e79d8..ddd22707dd 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-2012. All Rights Reserved. +;; Copyright Ericsson AB 1996-2013. 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. @@ -724,31 +723,32 @@ resulting regexp is surrounded by \\_< and \\_>." (eval-and-compile (defvar erlang-int-bifs '("abs" - "adler32" - "adler32_combine" - "alive" "apply" "atom_to_binary" "atom_to_list" "binary_to_atom" "binary_to_existing_atom" + "binary_to_float" + "binary_to_integer" "binary_to_list" "binary_to_term" + "binary_part" "bit_size" + "bitsize" "bitstring_to_list" "byte_size" + "check_old_code" "check_process_code" - "contact_binary" - "crc32" - "crc32_combine" "date" - "decode_packet" "delete_module" + "demonitor" "disconnect_node" "element" "erase" + "error" "exit" "float" + "float_to_binary" "float_to_list" "garbage_collect" "get" @@ -757,7 +757,7 @@ resulting regexp is surrounded by \\_< and \\_>." "halt" "hd" "integer_to_list" - "internal_bif" + "integer_to_binary" "iolist_size" "iolist_to_binary" "is_alive" @@ -788,13 +788,13 @@ resulting regexp is surrounded by \\_< and \\_>." "list_to_tuple" "load_module" "make_ref" + "max" + "min" "module_loaded" + "monitor" "monitor_node" "node" - "node_link" - "node_unlink" "nodes" - "notalive" "now" "open_port" "pid_to_list" @@ -838,48 +838,102 @@ resulting regexp is surrounded by \\_< and \\_>." (eval-and-compile (defvar erlang-ext-bifs - '("append_element" + '("adler32" + "adler32_combine" + "alloc_info" + "alloc_sizes" + "append" + "append_element" + "await_proc_exit" + "await_sched_wall_time_modifications" + "bitstr_to_list" "bump_reductions" + "call_on_load_function" "cancel_timer" - "demonitor" + "crasher" + "crc32" + "crc32_combine" + "decode_packet" + "delay_trap" + "delete_element" + "dexit" + "dgroup_leader" "display" + "display_nl" + "display_string" + "dist_exit" + "dlink" + "dmonitor_node" + "dmonitor_p" + "dsend" + "dt_append_vm_tag_data" + "dt_get_tag" + "dt_get_tag_data" + "dt_prepend_vm_tag_data" + "dt_put_tag" + "dt_restore_tag" + "dt_spread_tag" + "dunlink" + "external_size" + "finish_after_on_load" + "finish_loading" + "flush_monitor_message" + "format_cpu_topology" "fun_info" "fun_to_list" "function_exported" + "garbage_collect_message_area" + "gather_sched_wall_time_result" "get_cookie" + "get_module_info" "get_stacktrace" "hash" - "integer_to_list" + "hibernate" + "insert_element" "is_builtin" - "list_to_integer" + "list_to_bitstr" + "load_nif" "loaded" "localtime" "localtime_to_universaltime" + "make_fun" "make_tuple" - "max" + "match_spec_test" "md5" "md5_final" "md5_init" "md5_update" "memory" - "min" - "monitor" + "module_info" "monitor_node" + "nif_error" "phash" "phash2" "port_call" + "port_get_data" "port_info" + "port_set_data" "port_to_list" "ports" + "posixtime_to_universaltime" + "prepare_loading" "process_display" + "raise" "read_timer" "ref_to_list" "resume_process" "send" "send_after" "send_nosuspend" + "seq_trace" + "seq_trace_info" + "seq_trace_print" "set_cookie" + "set_cpu_topology" + "setnode" + "spawn_opt" "start_timer" + "subtract" "suspend_process" "system_flag" "system_info" @@ -891,6 +945,7 @@ resulting regexp is surrounded by \\_< and \\_>." "trace_pattern" "universaltime" "universaltime_to_localtime" + "universaltime_to_posixtime" "yield") "Erlang built-in functions (BIFs) that needs erlang: prefix")) @@ -998,6 +1053,22 @@ behaviour.") 1 'font-lock-function-name-face t)) "Font lock keyword highlighting a function header.") +(defface erlang-font-lock-exported-function-name-face + '((default (:inherit font-lock-function-name-face))) + "Face used for highlighting exported functions.") + +(defvar erlang-font-lock-exported-function-name-face + 'erlang-font-lock-exported-function-name-face) + +(defvar erlang-inhibit-exported-function-name-face nil + "Inhibit separate face for exported functions") + +(defvar erlang-font-lock-keywords-exported-function-header + (list + (list #'erlang-match-next-exported-function + 1 'erlang-font-lock-exported-function-name-face t)) + "Font lock keyword highlighting an exported function header.") + (defvar erlang-font-lock-keywords-int-bifs (list (list (concat erlang-int-bif-regexp "\\s-*(") @@ -1133,7 +1204,8 @@ There exists three levels of Font Lock keywords for Erlang: `erlang-font-lock-keywords-1' - Function headers and reserved keywords. `erlang-font-lock-keywords-2' - Bifs, guards and `single quotes'. `erlang-font-lock-keywords-3' - Variables, macros and records. - `erlang-font-lock-keywords-4' - Function names, Funs, LCs (not Atoms) + `erlang-font-lock-keywords-4' - Exported functions, Function names, + Funs, LCs (not Atoms). To use a specific level, please set the variable `font-lock-maximum-decoration' to the appropriate level. Note that the @@ -1175,6 +1247,7 @@ Example: (defvar erlang-font-lock-keywords-4 (append erlang-font-lock-keywords-3 + erlang-font-lock-keywords-exported-function-header erlang-font-lock-keywords-int-function-calls erlang-font-lock-keywords-ext-function-calls erlang-font-lock-keywords-fun-n @@ -1327,7 +1400,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 +1515,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." @@ -1527,9 +1574,9 @@ Other commands: . (("\\(?:^\\|[^$]\\)\"\\(?:[^\"\n]\\|\\\\\"\\)*\\(\\$\\)\"" 1 "w") ;; Likewise for atoms ("\\(?:^\\|[^$]\\)'\\(?:[^'\n]\\|\\\\'\\)*\\(\\$\\)'" 1 "w") - ;; And the dollar sign in $\" escapes two characters, not - ;; just one. - ("\\(\\$\\)\\\\\\\"" 1 "'")))))) + ;; And the dollar sign in $\" or $\' escapes two + ;; characters, not just one. + ("\\(\\$\\)\\\\[\"']" 1 "'")))))) @@ -3689,6 +3736,38 @@ In the future the list may contain more elements." str) (store-match-data md)))) +(defun erlang-match-next-exported-function (max) + "Returns non-nil if there is an exported function in the current +buffer between point and MAX." + (block nil + (while (and (not erlang-inhibit-exported-function-name-face) + (erlang-match-next-function max)) + (when (erlang-last-match-exported-p) + (return (match-data)))))) + +(defun erlang-match-next-function (max) + "Searches forward in current buffer for the next erlang function, +bounded by position MAX." + (re-search-forward erlang-defun-prompt-regexp max 'move-point)) + +(defun erlang-last-match-exported-p () + "Returns non-nil if match-data describes the name and arity of an +exported function." + (save-excursion + (save-match-data + (goto-char (match-beginning 1)) + (erlang-function-exported-p + (erlang-remove-quotes (erlang-get-function-name)) + (erlang-get-function-arity))))) + +(defun erlang-function-exported-p (name arity) + "Returns non-nil if function of name and arity is exported in current buffer." + (save-excursion + (let* ((old-match-data (match-data)) + (exports (erlang-get-export))) + (store-match-data old-match-data) + (member (cons name arity) exports)))) + ;;; Check module name @@ -4896,7 +4975,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/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/src/Makefile b/lib/tools/src/Makefile index abe1389771..e606b97a48 100644 --- a/lib/tools/src/Makefile +++ b/lib/tools/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2012. All Rights Reserved. +# Copyright Ericsson AB 1996-2013. 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 @@ -75,7 +75,7 @@ APPUP_TARGET = $(EBIN)/$(APPUP_FILE) # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += +ERL_COMPILE_FLAGS += -Werror # ---------------------------------------------------- # Targets @@ -94,10 +94,10 @@ docs: # ---------------------------------------------------- $(APP_TARGET): $(APP_SRC) ../vsn.mk - sed -e 's;%VSN%;$(VSN);' $< > $@ + $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk - sed -e 's;%VSN%;$(VSN);' $< > $@ + $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ # ---------------------------------------------------- # Release Target diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index e21bd1b88c..dfcfc3675f 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-2013. 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,15 @@ 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, + LostNodes1 = State#main_state.lost_nodes--Nodes, + main_process_loop(State#main_state{nodes=Nodes1, + lost_nodes=LostNodes1}); + + {From, {flush,Nodes}} -> + remote_collect('_',Nodes,false), + reply(From, ok), + main_process_loop(State); {From, stop} -> lists:foreach( @@ -788,16 +792,39 @@ 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,Lost} = + case lists:member(Node,State#main_state.nodes) of + true -> + N = State#main_state.nodes--[Node], + L = [Node|State#main_state.lost_nodes], + {N,L}; + false -> % node stopped + {State#main_state.nodes,State#main_state.lost_nodes} + end, + 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]), + io:format("~tp~n",[State]), main_process_loop(State) end. @@ -850,10 +877,19 @@ 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]), + io:format("~tp~n",[State]), remote_process_loop(State); M -> @@ -961,6 +997,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: ~tp\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 +1050,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 +1184,6 @@ remove_myself([Node|Nodes],Acc) -> remove_myself(Nodes,[Node|Acc]); remove_myself([],Acc) -> Acc. - %%%--Handling of modules state data-------------------------------------- @@ -1134,7 +1223,7 @@ do_get_all_importfiles([],Acc) -> imported_info(Text,Module,Imported) -> case lists:keysearch(Module,1,Imported) of {value,{Module,_File,ImportFiles}} -> - io:format("~s includes data from imported files\n~p\n", + io:format("~ts includes data from imported files\n~tp\n", [Text,ImportFiles]); false -> ok @@ -1148,7 +1237,7 @@ add_imported(Module, File, ImportFile, Imported) -> add_imported(M, F1, ImportFile, [{M,_F2,ImportFiles}|Imported], Acc) -> case lists:member(ImportFile,ImportFiles) of true -> - io:fwrite("WARNING: Module ~w already imported from ~p~n" + io:fwrite("WARNING: Module ~w already imported from ~tp~n" "Not importing again!~n",[M,ImportFile]), dont_import; false -> @@ -1166,7 +1255,7 @@ remove_imported(Module,Imported) -> case lists:keysearch(Module,1,Imported) of {value,{Module,_,ImportFiles}} -> io:fwrite("WARNING: Deleting data for module ~w imported from~n" - "~p~n",[Module,ImportFiles]), + "~tp~n",[Module,ImportFiles]), lists:keydelete(Module,1,Imported); false -> Imported @@ -1283,10 +1372,15 @@ do_compile_beam(Module,Beam,UserOptions) -> Forms0 = epp:interpret_file_attribute(Code), {Forms,Vars} = transform(Vsn, Forms0, Module, Beam), + %% We need to recover the source from the compilation + %% info otherwise the newly compiled module will have + %% source pointing to the current directory + SourceInfo = get_source_info(Module, Beam), + %% Compile and load the result %% It's necessary to check the result of loading since it may %% fail, for example if Module resides in a sticky directory - {ok, Module, Binary} = compile:forms(Forms, UserOptions), + {ok, Module, Binary} = compile:forms(Forms, SourceInfo ++ UserOptions), case code:load_binary(Module, ?TAG, Binary) of {module, Module} -> @@ -1314,6 +1408,17 @@ get_abstract_code(Module, Beam) -> Error -> Error end. +get_source_info(Module, Beam) -> + case beam_lib:chunks(Beam, [compile_info]) of + {ok, {Module, [{compile_info, Compile}]}} -> + case lists:keyfind(source, 1, Compile) of + { source, _ } = Tuple -> [Tuple]; + false -> [] + end; + _ -> + [] + end. + transform(Vsn, Code, Module, Beam) when Vsn=:=abstract_v1; Vsn=:=abstract_v2 -> Vars0 = #vars{module=Module, vsn=Vsn}, MainFile=find_main_filename(Code), @@ -1430,12 +1535,6 @@ aux_var(Vars, N) -> %% This way we will be able to exclude functions defined in include files. munge({function,0,module_info,_Arity,_Clauses},_Vars,_MainFile,_Switch) -> ignore; % module_info will be added again when the forms are recompiled -munge(Form={function,_,'MNEMOSYNE QUERY',_,_},Vars,_MainFile,Switch) -> - {Form,Vars,Switch}; % No bumps in Mnemosyne code. -munge(Form={function,_,'MNEMOSYNE RULE',_,_},Vars,_MainFile,Switch) -> - {Form,Vars,Switch}; -munge(Form={function,_,'MNEMOSYNE RECFUNDEF',_,_},Vars,_MainFile,Switch) -> - {Form,Vars,Switch}; munge({function,Line,Function,Arity,Clauses},Vars,_MainFile,on) -> Vars2 = Vars#vars{function=Function, arity=Arity, @@ -1700,17 +1799,11 @@ munge_expr({'catch',Line,Expr}, Vars) -> {MungedExpr, Vars2} = munge_expr(Expr, Vars), {{'catch',Line,MungedExpr}, Vars2}; munge_expr({call,Line1,{remote,Line2,ExprM,ExprF},Exprs}, - Vars) when Vars#vars.is_guard=:=false-> + Vars) -> {MungedExprM, Vars2} = munge_expr(ExprM, Vars), {MungedExprF, Vars3} = munge_expr(ExprF, Vars2), {MungedExprs, Vars4} = munge_exprs(Exprs, Vars3, []), {{call,Line1,{remote,Line2,MungedExprM,MungedExprF},MungedExprs}, Vars4}; -munge_expr({call,Line1,{remote,_Line2,_ExprM,ExprF},Exprs}, - Vars) when Vars#vars.is_guard=:=true -> - %% Difference in abstract format after preprocessing: BIF calls in guards - %% are translated to {remote,...} (which is not allowed as source form) - %% NOT NECESSARY FOR Vsn=raw_abstract_v1 - munge_expr({call,Line1,ExprF,Exprs}, Vars); munge_expr({call,Line,Expr,Exprs}, Vars) -> {MungedExpr, Vars2} = munge_expr(Expr, Vars), {MungedExprs, Vars3} = munge_exprs(Exprs, Vars2, []), @@ -1858,32 +1951,62 @@ move_clauses([{M,F,A,C,_L}|Clauses]) -> move_clauses(Clauses); move_clauses([]) -> ok. - %% Given a .beam file, find the .erl file. Look first in same directory as -%% the .beam file, then in <beamdir>/../src -find_source(File0) -> - case filename:rootname(File0,".beam") of - File0 -> - File0; - File -> - InSameDir = File++".erl", - case filelib:is_file(InSameDir) of - true -> - InSameDir; - false -> - Dir = filename:dirname(File), - Mod = filename:basename(File), - InDotDotSrc = filename:join([Dir,"..","src",Mod++".erl"]), - case filelib:is_file(InDotDotSrc) of - true -> - InDotDotSrc; - false -> - {beam,File0} - end - end +%% the .beam file, then in ../src, then in compile info. +find_source(Module, File0) -> + try + Root = filename:rootname(File0, ".beam"), + Root == File0 andalso throw(File0), %% not .beam + %% Look for .erl in pwd. + File = Root ++ ".erl", + throw_file(File), + %% Not in pwd: look in ../src. + BeamDir = filename:dirname(File), + Base = filename:basename(File), + throw_file(filename:join([BeamDir, "..", "src", Base])), + %% Not in ../src: look for source path in compile info, but + %% first look relative the beam directory. + Info = lists:keyfind(source, 1, Module:module_info(compile)), + false == Info andalso throw({beam, File0}), %% stripped + {source, SrcFile} = Info, + throw_file(splice(BeamDir, SrcFile)), %% below ../src + throw_file(SrcFile), %% or absolute + %% No success means that source is either not under ../src or + %% its relative path differs from that of compile info. (For + %% example, compiled under src/x but installed under src/y.) + %% An option to specify an arbitrary source path explicitly is + %% probably a better solution than either more heuristics or a + %% potentially slow filesystem search. + {beam, File0} + catch + Path -> Path end. +throw_file(Path) -> + false /= Path andalso filelib:is_file(Path) andalso throw(Path). + +%% Splice the tail of a source path, starting from the last "src" +%% component, onto the parent of a beam directory, or return false if +%% no "src" component is found. +%% +%% Eg. splice("/path/to/app-1.0/ebin", "/compiled/path/to/app/src/x/y.erl") +%% --> "/path/to/app-1.0/ebin/../src/x/y.erl" +%% +%% This handles the case of source in subdirectories of ../src with +%% beams that have moved since compilation. +%% +splice(BeamDir, SrcFile) -> + case lists:splitwith(fun(C) -> C /= "src" end, revsplit(SrcFile)) of + {T, [_|_]} -> %% found src component + filename:join([BeamDir, "..", "src" | lists:reverse(T)]); + {_, []} -> %% or not + false + end. + +revsplit(Path) -> + lists:reverse(filename:split(Path)). + do_parallel_analysis(Module, Analysis, Level, Loaded, From, State) -> analyse_info(Module,State#main_state.imported), C = case Loaded of @@ -1987,7 +2110,7 @@ do_parallel_analysis_to_file(Module, OutFile, Opts, Loaded, From, State) -> {imported, File0, _} -> File0 end, - case find_source(File) of + case find_source(Module, File) of {beam,_BeamFile} -> reply(From, {error,no_source_code_found}); ErlFile -> @@ -2007,30 +2130,40 @@ do_analyse_to_file(Module, OutFile, ErlFile, HTML) -> case file:open(OutFile, [write]) of {ok, OutFd} -> if HTML -> - io:format(OutFd, - "<html>\n" - "<head><title>~s</title></head>" - "<body bgcolor=white text=black>\n" - "<pre>\n", - [OutFile]); + Encoding = encoding(ErlFile), + Header = + ["<!DOCTYPE HTML PUBLIC " + "\"-//W3C//DTD HTML 3.2 Final//EN\">\n" + "<html>\n" + "<head>\n" + "<meta http-equiv=\"Content-Type\"" + " content=\"text/html; charset=", + Encoding,"\"/>\n" + "<title>",OutFile,"</title>\n" + "</head>" + "<body style='background-color: white;" + " color: black'>\n" + "<pre>\n"], + file:write(OutFd,Header); true -> ok end, %% Write some initial information to the output file {{Y,Mo,D},{H,Mi,S}} = calendar:local_time(), - io:format(OutFd, "File generated from ~s by COVER " - "~p-~s-~s at ~s:~s:~s~n", - [ErlFile, - Y, - string:right(integer_to_list(Mo), 2, $0), - string:right(integer_to_list(D), 2, $0), - string:right(integer_to_list(H), 2, $0), - string:right(integer_to_list(Mi), 2, $0), - string:right(integer_to_list(S), 2, $0)]), - io:format(OutFd, "~n" - "**************************************" - "**************************************" - "~n~n", []), + Timestamp = + io_lib:format("~p-~s-~s at ~s:~s:~s", + [Y, + string:right(integer_to_list(Mo), 2, $0), + string:right(integer_to_list(D), 2, $0), + string:right(integer_to_list(H), 2, $0), + string:right(integer_to_list(Mi), 2, $0), + string:right(integer_to_list(S), 2, $0)]), + file:write(OutFd, + ["File generated from ",ErlFile," by COVER ", + Timestamp,"\n\n" + "**************************************" + "**************************************" + "\n\n"]), print_lines(Module, InFd, OutFd, 1, HTML), @@ -2254,7 +2387,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 +2446,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 +2455,23 @@ 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. + +%%%----------------------------------------------------------------- +%%% Read encoding from source file +encoding(File) -> + Encoding = + case epp:read_encoding(File) of + none -> + epp:default_encoding(); + E -> + E + end, + html_encoding(Encoding). + +html_encoding(latin1) -> + "iso-8859-1"; +html_encoding(utf8) -> + "utf-8". diff --git a/lib/tools/src/lcnt.erl b/lib/tools/src/lcnt.erl index 989a661b75..f13a297ecf 100644 --- a/lib/tools/src/lcnt.erl +++ b/lib/tools/src/lcnt.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. 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 @@ -271,7 +272,7 @@ handle_call({locations, InOpts}, _From, #state{ locks = Locks } = State) when is Opts = options(InOpts, Default), Printables = filter_print([#print{ name = string_names(Names), - entry = term2string("~p:~p", [Stats#stats.file, Stats#stats.line]), + entry = term2string("~tp:~p", [Stats#stats.file, Stats#stats.line]), colls = Stats#stats.colls, tries = Stats#stats.tries, cr = percent(Stats#stats.colls, Stats#stats.tries), @@ -566,7 +567,7 @@ stats2print(Stats, Duration) -> lists:map(fun (S) -> #print{ - entry = term2string("~p:~p", [S#stats.file, S#stats.line]), + entry = term2string("~tp:~p", [S#stats.file, S#stats.line]), colls = S#stats.colls, tries = S#stats.tries, cr = percent(S#stats.colls, S#stats.tries), @@ -797,20 +798,20 @@ options1([{Key, Value}|Opts], Defaults) -> %%% AUX STRING FORMATTING -print(String) -> io:format("~s~n", [String]). +print(String) -> io:format("~ts~n", [String]). kv(Key, Value) -> kv(Key, Value, 20). kv(Key, Value, Offset) -> term2string(term2string("~~~ps : ~~s", [Offset]),[Key, Value]). s(T) when is_float(T) -> term2string("~.4f", [T]); -s(T) when is_list(T) -> term2string("~s", [T]); +s(T) when is_list(T) -> term2string("~ts", [T]); s(T) -> term2string(T). strings(Strings) -> strings(Strings, []). strings([], Out) -> Out; strings([{space, N, S} | Ss], Out) -> strings(Ss, Out ++ term2string(term2string("~~~ps", [N]), [S])); strings([{format, Format, S} | Ss], Out) -> strings(Ss, Out ++ term2string(Format, [S])); -strings([S|Ss], Out) -> strings(Ss, Out ++ term2string("~s", [S])). +strings([S|Ss], Out) -> strings(Ss, Out ++ term2string("~ts", [S])). term2string({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) -> term2string("~p:~p/~p", [M,F,A]); diff --git a/lib/tools/src/make.erl b/lib/tools/src/make.erl index 5cc8d47faa..c8ef0a04a5 100644 --- a/lib/tools/src/make.erl +++ b/lib/tools/src/make.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -90,7 +90,7 @@ read_emakefile(Emakefile,Opts) -> Mods = [filename:rootname(F) || F <- filelib:wildcard("*.erl")], [{Mods, Opts}]; {error,Other} -> - io:format("make: Trouble reading 'Emakefile':~n~p~n",[Other]), + io:format("make: Trouble reading 'Emakefile':~n~tp~n",[Other]), error end. @@ -145,7 +145,7 @@ get_opts_from_emakefile(Mods,Emakefile,Opts) -> {error,enoent} -> [{Mods, Opts}]; {error,Other} -> - io:format("make: Trouble reading 'Emakefile':~n~p~n",[Other]), + io:format("make: Trouble reading 'Emakefile':~n~tp~n",[Other]), error end. @@ -253,15 +253,15 @@ include_opt([]) -> %% Where load can be netload | load | noload recompile(File, true, _Load, _Opts) -> - io:format("Out of date: ~s\n",[File]); + io:format("Out of date: ~ts\n",[File]); recompile(File, false, noload, Opts) -> - io:format("Recompile: ~s\n",[File]), + io:format("Recompile: ~ts\n",[File]), compile:file(File, [report_errors, report_warnings, error_summary |Opts]); recompile(File, false, load, Opts) -> - io:format("Recompile: ~s\n",[File]), + io:format("Recompile: ~ts\n",[File]), c:c(File, Opts); recompile(File, false, netload, Opts) -> - io:format("Recompile: ~s\n",[File]), + io:format("Recompile: ~ts\n",[File]), c:nc(File, Opts). exists(File) -> diff --git a/lib/tools/src/tags.erl b/lib/tools/src/tags.erl index e740d38c91..1c72ef8db5 100644 --- a/lib/tools/src/tags.erl +++ b/lib/tools/src/tags.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -157,7 +157,7 @@ files_loop([F | Fs], Os) -> ok -> ok; error -> - %% io:format("Could not open ~s~n", [F]), + %% io:format("Could not open ~ts~n", [F]), error end, files_loop(Fs, Os). @@ -315,11 +315,11 @@ close_out(Os) -> pfnote(Str, {LineNo, CharNo}) -> - io_lib:format("~s\177~w,~w~n", [flatrev(Str), LineNo, CharNo]). + io_lib:format("~ts\177~w,~w~n", [flatrev(Str), LineNo, CharNo]). genout(Os, Name, Entries) -> - io:format(Os, "\^l~n~s,~w~n", [Name, reclength(Entries)]), + io:format(Os, "\^l~n~ts,~w~n", [Name, reclength(Entries)]), io:put_chars(Os, lists:reverse(Entries)). diff --git a/lib/tools/src/xref.erl b/lib/tools/src/xref.erl index 0693bec019..abc184c84d 100644 --- a/lib/tools/src/xref.erl +++ b/lib/tools/src/xref.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2013. 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 @@ -297,7 +297,7 @@ set_default(Name, Option, Value) -> format_error({error, Module, Error}) -> Module:format_error(Error); format_error(E) -> - io_lib:format("~p~n", [E]). + io_lib:format("~tp~n", [E]). %%%---------------------------------------------------------------------- %%%Callback functions from gen_server diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl index 93f0e9c0c8..30c5f3d12d 100644 --- a/lib/tools/src/xref_base.erl +++ b/lib/tools/src/xref_base.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2013. 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 @@ -480,43 +480,43 @@ set_default(State, Options) -> format_error({error, Module, Error}) -> Module:format_error(Error); format_error({invalid_options, Options}) -> - io_lib:format("Unknown option(s) or invalid option value(s): ~p~n", + io_lib:format("Unknown option(s) or invalid option value(s): ~tp~n", [Options]); format_error({invalid_filename, Term}) -> - io_lib:format("A file name (a string) was expected: ~p~n", [Term]); + io_lib:format("A file name (a string) was expected: ~tp~n", [Term]); format_error({no_debug_info, FileName}) -> - io_lib:format("The BEAM file ~p has no debug info~n", [FileName]); + io_lib:format("The BEAM file ~tp has no debug info~n", [FileName]); format_error({invalid_path, Term}) -> - io_lib:format("A path (a list of strings) was expected: ~p~n", [Term]); + io_lib:format("A path (a list of strings) was expected: ~tp~n", [Term]); format_error({invalid_query, Term}) -> - io_lib:format("A query (a string or an atom) was expected: ~p~n", [Term]); + io_lib:format("A query (a string or an atom) was expected: ~tp~n", [Term]); format_error({not_user_variable, Variable}) -> - io_lib:format("~p is not a user variable~n", [Variable]); + io_lib:format("~tp is not a user variable~n", [Variable]); format_error({unknown_analysis, Term}) -> - io_lib:format("~p is not a predefined analysis~n", [Term]); + io_lib:format("~tp is not a predefined analysis~n", [Term]); format_error({module_mismatch, Module, ReadModule}) -> - io_lib:format("Name of read module ~p does not match analyzed module ~p~n", + io_lib:format("Name of read module ~tp does not match analyzed module ~tp~n", [ReadModule, Module]); format_error({release_clash, {Release, Dir, OldDir}}) -> - io_lib:format("The release ~p read from ~p clashes with release " - "already read from ~p~n", [Release, Dir, OldDir]); + io_lib:format("The release ~tp read from ~tp clashes with release " + "already read from ~tp~n", [Release, Dir, OldDir]); format_error({application_clash, {Application, Dir, OldDir}}) -> - io_lib:format("The application ~p read from ~p clashes with application " - "already read from ~p~n", [Application, Dir, OldDir]); + io_lib:format("The application ~tp read from ~tp clashes with application " + "already read from ~tp~n", [Application, Dir, OldDir]); format_error({module_clash, {Module, Dir, OldDir}}) -> - io_lib:format("The module ~p read from ~p clashes with module " - "already read from ~p~n", [Module, Dir, OldDir]); + io_lib:format("The module ~tp read from ~tp clashes with module " + "already read from ~tp~n", [Module, Dir, OldDir]); format_error({no_such_release, Name}) -> - io_lib:format("There is no analyzed release ~p~n", [Name]); + io_lib:format("There is no analyzed release ~tp~n", [Name]); format_error({no_such_application, Name}) -> - io_lib:format("There is no analyzed application ~p~n", [Name]); + io_lib:format("There is no analyzed application ~tp~n", [Name]); format_error({no_such_module, Name}) -> - io_lib:format("There is no analyzed module ~p~n", [Name]); + io_lib:format("There is no analyzed module ~tp~n", [Name]); format_error({no_such_info, Term}) -> - io_lib:format("~p is not one of 'modules', 'applications', " + io_lib:format("~tp is not one of 'modules', 'applications', " "'releases' and 'libraries'~n", [Term]); format_error(E) -> - io_lib:format("~p~n", [E]). + io_lib:format("~tp~n", [E]). %% %% Local functions @@ -1506,7 +1506,7 @@ do_variables(State) -> _Else -> {[Name | P], U} end; ({{tmp, V}, _}, A) -> - io:format("Bug in ~p: temporary ~p~n", [?MODULE, V]), A; + io:format("Bug in ~tp: temporary ~tp~n", [?MODULE, V]), A; (_V, A) -> A end, {U,P} = foldl(Fun, {[],[]}, dict:to_list(State#xref.variables)), @@ -1766,23 +1766,23 @@ tpack(T, I, L) -> message(true, What, Arg) -> case What of reading_beam -> - io:format("~s... ", Arg); + io:format("~ts... ", Arg); skipped_beam -> io:format("skipped (no debug information)~n", Arg); no_debug_info -> - io:format("Skipping ~s (no debug information)~n", Arg); + io:format("Skipping ~ts (no debug information)~n", Arg); unresolved_summary1 -> - io:format("~p: 1 unresolved call~n", Arg); + io:format("~tp: 1 unresolved call~n", Arg); unresolved_summary -> - io:format("~p: ~p unresolved calls~n", Arg); + io:format("~tp: ~tp unresolved calls~n", Arg); jam -> - io:format("Skipping ~s (probably JAM file)~n", [Arg]); + io:format("Skipping ~ts (probably JAM file)~n", [Arg]); unreadable -> - io:format("Skipping ~s (unreadable)~n", [Arg]); + io:format("Skipping ~ts (unreadable)~n", [Arg]); xref_attr -> - io:format("~s: Skipping 'xref' attribute ~w~n", Arg); + io:format("~ts: Skipping 'xref' attribute ~w~n", Arg); depr_attr -> - io:format("~s: Skipping 'deprecated' attribute ~w~n", Arg); + io:format("~ts: Skipping 'deprecated' attribute ~w~n", Arg); lib_search -> io:format("Scanning library path for BEAM files... ", []); lib_check -> @@ -1794,7 +1794,7 @@ message(true, What, Arg) -> error -> io:format("error~n", Arg); Else -> - io:format("~p~n", [{Else,Arg}]) + io:format("~tp~n", [{Else,Arg}]) end; message(_, _, _) -> true. diff --git a/lib/tools/src/xref_compiler.erl b/lib/tools/src/xref_compiler.erl index 22312c6754..f0fed502a5 100644 --- a/lib/tools/src/xref_compiler.erl +++ b/lib/tools/src/xref_compiler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-2013. 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 @@ -83,19 +83,19 @@ format_error({error, Module, Error}) -> format_error({parse_error, Line, Error}) -> format_parse_error(Error, format_line(Line)); format_error({variable_reassigned, Expr}) -> - io_lib:format("Variable assigned more than once: ~s~n", [Expr]); + io_lib:format("Variable assigned more than once: ~ts~n", [Expr]); format_error({unknown_variable, Name}) -> - io_lib:format("Variable ~p used before set~n", [Name]); + io_lib:format("Variable ~tp used before set~n", [Name]); format_error({type_error, Expr}) -> io_lib:format("Operator applied to argument(s) of different or " - "invalid type(s): ~s~n", [Expr]); + "invalid type(s): ~ts~n", [Expr]); format_error({type_mismatch, Expr1, Expr2}) -> - io_lib:format("Constants of different types: ~s, ~s~n", + io_lib:format("Constants of different types: ~ts, ~ts~n", [Expr1, Expr2]); format_error({unknown_constant, Constant}) -> - io_lib:format("Unknown constant ~s~n", [Constant]); + io_lib:format("Unknown constant ~ts~n", [Constant]); format_error(E) -> - io_lib:format("~p~n", [E]). + io_lib:format("~tp~n", [E]). %% %% Local functions @@ -908,21 +908,21 @@ fetch_value(V, D) -> Value. format_parse_error(["invalid_regexp", String, Error], Line) -> - io_lib:format("Invalid regular expression \"~s\"~s: ~s~n", + io_lib:format("Invalid regular expression \"~ts\"~s: ~ts~n", [String, Line, lists:flatten(Error)]); format_parse_error(["invalid_regexp_variable", Var], Line) -> - io_lib:format("Invalid wildcard variable ~p~s " + io_lib:format("Invalid wildcard variable ~tp~s " "(only '_' is allowed)~n", [Var, Line]); format_parse_error(["missing_type", Expr], Line) -> - io_lib:format("Missing type of regular expression ~s~s~n", + io_lib:format("Missing type of regular expression ~ts~s~n", [Expr, Line]); format_parse_error(["type_mismatch", Expr], Line) -> - io_lib:format("Type does not match structure of constant~s: ~s~n", + io_lib:format("Type does not match structure of constant~s: ~ts~n", [Line, Expr]); format_parse_error(["invalid_operator", Op], Line) -> - io_lib:format("Invalid operator ~p~s~n", [Op, Line]); + io_lib:format("Invalid operator ~tp~s~n", [Op, Line]); format_parse_error(Error, Line) -> - io_lib:format("Parse error~s: ~s~n", [Line, lists:flatten(Error)]). + io_lib:format("Parse error~s: ~ts~n", [Line, lists:flatten(Error)]). format_line(-1) -> " at end of string"; diff --git a/lib/tools/src/xref_reader.erl b/lib/tools/src/xref_reader.erl index 92f0c45c7b..d3601c6ea0 100644 --- a/lib/tools/src/xref_reader.erl +++ b/lib/tools/src/xref_reader.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2013. 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 @@ -80,12 +80,6 @@ form({attribute, Line, xref, Calls}, S) -> % experimental attr(Calls, Line, M, Fun, L, X, B, S); form({attribute, _Line, _Attr, _Val}, S) -> S; -form({function, 0, 'MNEMOSYNE RULE', 1, _Clauses}, S) -> - S; -form({function, 0, 'MNEMOSYNE QUERY', 2, _Clauses}, S) -> - S; -form({function, 0, 'MNEMOSYNE RECFUNDEF', 1, _Clauses}, S) -> - S; form({function, 0, module_info, 0, _Clauses}, S) -> S; form({function, 0, module_info, 1, _Clauses}, S) -> @@ -331,9 +325,6 @@ handle_call(Locality, Module, Name, Arity, Line, S) -> handle_call(Locality, To, Line, S, false) end. -handle_call(_Locality, {_, 'MNEMOSYNE RULE',1}, _Line, S, _) -> S; -handle_call(_Locality, {_, 'MNEMOSYNE QUERY', 2}, _Line, S, _) -> S; -handle_call(_Locality, {_, 'MNEMOSYNE RECFUNDEF',1}, _Line, S, _) -> S; handle_call(Locality, To0, Line, S, IsUnres) -> From = S#xrefr.function, To = adjust_arity(S, To0), diff --git a/lib/tools/src/xref_utils.erl b/lib/tools/src/xref_utils.erl index 680563e9df..7b72165e6f 100644 --- a/lib/tools/src/xref_utils.erl +++ b/lib/tools/src/xref_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2012. All Rights Reserved. +%% Copyright Ericsson AB 2000-2013. 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 @@ -517,16 +517,16 @@ subprocess(Fun, Opts) -> format_error({error, Module, Error}) -> Module:format_error(Error); format_error({file_error, FileName, Reason}) -> - io_lib:format("~s: ~p~n", [FileName, file:format_error(Reason)]); + io_lib:format("~ts: ~tp~n", [FileName, file:format_error(Reason)]); format_error({unrecognized_file, FileName}) -> - io_lib:format("~p is neither a regular file nor a directory~n", + io_lib:format("~tp is neither a regular file nor a directory~n", [FileName]); format_error({no_such_module, Module}) -> - io_lib:format("Cannot find module ~p using the code path~n", [Module]); + io_lib:format("Cannot find module ~tp using the code path~n", [Module]); format_error({interpreted, Module}) -> - io_lib:format("Cannot use BEAM code of interpreted module ~p~n", [Module]); + io_lib:format("Cannot use BEAM code of interpreted module ~tp~n", [Module]); format_error(E) -> - io_lib:format("~p~n", [E]). + io_lib:format("~tp~n", [E]). %% %% Local functions diff --git a/lib/tools/test/Makefile b/lib/tools/test/Makefile index 86c81217b6..484cfdf53f 100644 --- a/lib/tools/test/Makefile +++ b/lib/tools/test/Makefile @@ -22,6 +22,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES = \ cover_SUITE \ eprof_SUITE \ + emacs_SUITE \ emem_SUITE \ fprof_SUITE \ cprof_SUITE \ diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index c2c708d806..5abc5c41b1 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -23,7 +23,9 @@ 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, + dont_reconnect_after_stop/1, stop_node_after_disconnect/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 +47,9 @@ 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, + dont_reconnect_after_stop, stop_node_after_disconnect, export_import, otp_5031, eif, otp_5305, otp_5418, otp_6115, otp_7095, otp_8188, otp_8270, otp_8273, otp_8340]; @@ -85,8 +89,11 @@ init_per_testcase(TC, Config) when TC =:= misc; init_per_testcase(_TestCase, Config) -> Config. -end_per_testcase(_TestCase, _Config) -> - %cover:stop(), +end_per_testcase(TestCase, _Config) -> + case lists:member(TestCase,[start,compile,analyse,misc]) of + true -> ok; + false -> cover:stop() + end, ok. start(suite) -> []; @@ -142,7 +149,9 @@ compile(Config) when is_list(Config) -> ok = beam_lib:crypto_key_fun(simple_crypto_fun(Key)), {ok,crypt} = cover:compile_beam("crypt.beam") end, + Path = filename:join([?config(data_dir, Config), "compile_beam", "v.erl"]), ?line {ok,v} = cover:compile_beam(v), + {source,Path} = lists:keyfind(source, 1, v:module_info(compile)), ?line {ok,w} = cover:compile_beam("w.beam"), ?line {error,{no_abstract_code,"./x.beam"}} = cover:compile_beam(x), ?line {error,{already_cover_compiled,no_beam_found,a}}=cover:compile_beam(a), @@ -270,12 +279,23 @@ analyse(Config) when is_list(Config) -> ?line f:f2(), ?line {ok, "f.COVER.out"} = cover:analyse_to_file(f), - %% Source code cannot be found by analyse_to_file + %% Source code can be found via source ?line {ok,v} = compile:file("compile_beam/v",[debug_info]), ?line code:purge(v), ?line {module,v} = code:load_file(v), ?line {ok,v} = cover:compile_beam(v), - ?line {error,no_source_code_found} = cover:analyse_to_file(v), + {ok,"v.COVER.out"} = cover:analyse_to_file(v), + + %% Source code cannot be found + {ok,_} = file:copy("compile_beam/z.erl", "z.erl"), + {ok,z} = compile:file(z,[debug_info]), + code:purge(z), + {module,z} = code:load_file(z), + {ok,z} = cover:compile_beam(z), + ok = file:delete("z.erl"), + {error,no_source_code_found} = cover:analyse_to_file(z), + code:purge(z), + code:delete(z), ?line {error,{not_cover_compiled,b}} = cover:analyse(b), ?line {error,{not_cover_compiled,g}} = cover:analyse(g), @@ -326,14 +346,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 +373,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 +408,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 +428,205 @@ 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), + timer:sleep(500), % allow some to detect disconnect and for f:f2() call + [] = cover:which_nodes(), + + %% 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. + +%% Test that a stopped node is not marked as lost, i.e. that it is not +%% reconnected if it is restarted (OTP-10638) +dont_reconnect_after_stop(Config) -> + DataDir = ?config(data_dir, Config), + ok = file:set_cwd(DataDir), + + {ok,f} = compile:file(f), + + NodeName = cover_SUITE_dont_reconnect_after_stop, + {ok,N1} = ?t:start_node(NodeName,peer, + [{args," -pa " ++ DataDir},{start_cover,false}]), + {ok,f} = cover:compile(f), + {ok,[N1]} = cover:start(nodes()), + + %% A call to check later + rpc:call(N1,f,f1,[]), + + %% Stop cover on the node, then terminate the node + cover:stop(N1), + rpc:call(N1,erlang,halt,[]), + [] = cover:which_nodes(), + + check_f_calls(1,0), + + %% Restart the node and check that cover does not reconnect + {ok,N1} = ?t:start_node(NodeName,peer, + [{args," -pa " ++ DataDir},{start_cover,false}]), + timer:sleep(300), + [] = cover:which_nodes(), + Beam = rpc:call(N1,code,which,[f]), + false = (Beam==cover_compiled), + + %% One more call... + rpc:call(N1,f,f1,[]), + cover:flush(N1), + + %% Ensure that the last call is not counted + check_f_calls(1,0), + + cover:stop(), + ?t:stop_node(N1), + ok. + +%% Test that a node which is stopped while it is marked as lost is not +%% reconnected if it is restarted (OTP-10638) +stop_node_after_disconnect(Config) -> + DataDir = ?config(data_dir, Config), + ok = file:set_cwd(DataDir), + + {ok,f} = compile:file(f), + + NodeName = cover_SUITE_stop_node_after_disconnect, + {ok,N1} = ?t:start_node(NodeName,peer, + [{args," -pa " ++ DataDir},{start_cover,false}]), + {ok,f} = cover:compile(f), + {ok,[N1]} = cover:start(nodes()), + + %% A call to check later + rpc:call(N1,f,f1,[]), + + %% Flush the node, then terminate the node to make it marked as lost + cover:flush(N1), + rpc:call(N1,erlang,halt,[]), + + check_f_calls(1,0), + + %% Stop cover on node + cover:stop(N1), + + %% Restart the node and check that cover does not reconnect + {ok,N1} = ?t:start_node(NodeName,peer, + [{args," -pa " ++ DataDir},{start_cover,false}]), + timer:sleep(300), + [] = cover:which_nodes(), + Beam = rpc:call(N1,code,which,[f]), + false = (Beam==cover_compiled), + + %% One more call... + rpc:call(N1,f,f1,[]), + cover:flush(N1), + + %% Ensure that the last call is not counted + check_f_calls(1,0), + + cover:stop(), + ?t:stop_node(N1), + ok. + export_import(suite) -> []; export_import(Config) when is_list(Config) -> ?line DataDir = ?config(data_dir, Config), @@ -795,7 +1015,7 @@ otp_8270(Config) when is_list(Config) -> ok. otp_8273(doc) -> - ["OTP-8270. Bug."]; + ["OTP-8273. Bug."]; otp_8273(suite) -> []; otp_8273(Config) when is_list(Config) -> Test = <<"-module(t). @@ -1238,4 +1458,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/compile_beam/v.erl b/lib/tools/test/cover_SUITE_data/compile_beam/v.erl index 007957297a..7fb0b08d40 100644 --- a/lib/tools/test/cover_SUITE_data/compile_beam/v.erl +++ b/lib/tools/test/cover_SUITE_data/compile_beam/v.erl @@ -1,6 +1,9 @@ -module(v). - --export([f/0]). +-compile({ no_auto_import, [is_integer/1] }). +-export([f/0,f/1]). f() -> ok. + +f(Number) when erlang:is_integer(Number) -> + Number. diff --git a/lib/tools/test/cover_SUITE_data/compile_beam/z.erl b/lib/tools/test/cover_SUITE_data/compile_beam/z.erl new file mode 100644 index 0000000000..7a2b143dde --- /dev/null +++ b/lib/tools/test/cover_SUITE_data/compile_beam/z.erl @@ -0,0 +1 @@ +-module(z). 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/cprof_SUITE.erl b/lib/tools/test/cprof_SUITE.erl index ce5cf66a14..fa4068dade 100644 --- a/lib/tools/test/cprof_SUITE.erl +++ b/lib/tools/test/cprof_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2013. 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 diff --git a/lib/tools/test/emacs_SUITE.erl b/lib/tools/test/emacs_SUITE.erl new file mode 100644 index 0000000000..657a3002a3 --- /dev/null +++ b/lib/tools/test/emacs_SUITE.erl @@ -0,0 +1,76 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2013. 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 +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(emacs_SUITE). + +%%-define(line_trace, 1). + +-export([all/0, init_per_testcase/2, end_per_testcase/2]). + +-export([bif_highlight/1]). + +all() -> + [bif_highlight]. + +init_per_testcase(_Case, Config) -> + ErlangEl = filename:join([code:lib_dir(tools),"emacs","erlang.el"]), + case file:read_file_info(ErlangEl) of + {ok, _} -> + [{el, ErlangEl}|Config]; + _ -> + {skip, "Could not find erlang.el"} + end. + +end_per_testcase(_Case, _Config) -> + ok. + +bif_highlight(Config) -> + ErlangEl = proplists:get_value(el,Config), + {ok, Bin} = file:read_file(ErlangEl), + + %% All auto-imported bifs + IntBifs = lists:usort( + [F || {F,A} <- erlang:module_info(exports), + erl_internal:bif(F,A)]), + + %% all bif which need erlang: prefix and are not operands + ExtBifs = lists:usort( + [F || {F,A} <- erlang:module_info(exports), + not erl_internal:bif(F,A) andalso + not is_atom(catch erl_internal:op_type(F,A))]), + + check_bif_highlight(Bin, <<"erlang-int-bifs">>, IntBifs), + check_bif_highlight(Bin, <<"erlang-ext-bifs">>, ExtBifs). + + +check_bif_highlight(Bin, Tag, Compare) -> + [_H,IntMatch,_T] = + re:split(Bin,<<"defvar ",Tag/binary, + "[^(]*\\(([^)]*)">>,[]), + EmacsIntBifs = [list_to_atom(S) || + S <- string:tokens(binary_to_list(IntMatch)," '\"\n")], + + ct:log("Emacs ~p",[EmacsIntBifs]), + ct:log("Int ~p",[Compare]), + + ct:log("Diff1 ~p",[Compare -- EmacsIntBifs]), + ct:log("Diff2 ~p",[EmacsIntBifs -- Compare]), + [] = Compare -- EmacsIntBifs, + [] = EmacsIntBifs -- Compare. + + diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl index fd3e111d8d..dc06678b8e 100644 --- a/lib/tools/test/xref_SUITE.erl +++ b/lib/tools/test/xref_SUITE.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -46,7 +47,7 @@ -export([ add/1, default/1, info/1, lib/1, read/1, read2/1, remove/1, replace/1, update/1, deprecated/1, trycatch/1, - abstract_modules/1, fun_mfa/1, fun_mfa_r14/1, + fun_mfa/1, fun_mfa_r14/1, fun_mfa_vars/1, qlc/1]). -export([ @@ -82,7 +83,7 @@ groups() -> modules]}, {files, [], [add, default, info, lib, read, read2, remove, replace, - update, deprecated, trycatch, abstract_modules, fun_mfa, + update, deprecated, trycatch, fun_mfa, fun_mfa_r14, fun_mfa_vars, qlc]}, {analyses, [], [analyze, basic, md, q, variables, unused_locals]}, @@ -1668,64 +1669,6 @@ trycatch(Conf) when is_list(Conf) -> ok. -abstract_modules(suite) -> []; -abstract_modules(doc) -> ["OTP-5520: Abstract (parameterized) modules."]; -abstract_modules(Conf) when is_list(Conf) -> - Dir = ?copydir, - File = fname(Dir, "absmod.erl"), - MFile = fname(Dir, "absmod"), - Beam = fname(Dir, "absmod.beam"), - Test = <<"-module(param, [A, B]). - - -export([args/1]). - - args(C) -> - X = local(C), - Y = THIS:new(), % undef - Z = new(A, B), - {X, Y, Z}. - - local(C) -> - module_info(C). - ">>, - - ?line ok = file:write_file(File, Test), - - %% The compiler will no longer allow us to have a mismatch between - %% the module name and the output file, so we must use a trick. - ?line {ok, param, BeamCode} = compile:file(File, [binary,debug_info]), - ?line ok = file:write_file(Beam, BeamCode), - - ?line {ok, _} = xref:start(s), - ?line {ok, param} = xref:add_module(s, MFile, {warnings,false}), - A = param, - ?line {ok, [{{{A,args,1},{'$M_EXPR',new,0}},[7]}, - {{{A,args,1},{A,local,1}},[6]}, - {{{A,args,1},{A,new,2}},[8]}, - {{{A,local,1},{A,module_info,1}},[12]}, - {{{param,new,2},{param,instance,2}},[0]}]} = - xref:q(s, "(Lin) E"), - ?line {ok,[{param,args,1}, - {param,instance,2}, - {param,local,1}, - {param,module_info,1}, - {param,new,2}]} = xref:q(s, "F"), - - ?line ok = check_state(s), - ?line xref:stop(s), - - ?line {ok, _} = xref:start(s, {xref_mode, modules}), - ?line {ok, param} = xref:add_module(s, MFile), - ?line {ok,[{param,args,1}, - {param,instance,2}, - {param,new,2}]} = xref:q(s, "X"), - ?line ok = check_state(s), - ?line xref:stop(s), - - ?line ok = file:delete(File), - ?line ok = file:delete(Beam), - ok. - fun_mfa(suite) -> []; fun_mfa(doc) -> ["OTP-5653: fun M:F/A."]; fun_mfa(Conf) when is_list(Conf) -> @@ -2521,7 +2464,7 @@ otp_10192(doc) -> otp_10192(Conf) when is_list(Conf) -> PrivDir = ?privdir, {ok, _Pid} = xref:start(s), - Dir = filename:join(PrivDir, "�"), + Dir = filename:join(PrivDir, "ä"), ok = file:make_dir(Dir), {ok, []} = xref:add_directory(s, Dir), xref:stop(s), diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk index 788ee12900..4fb2f30e4f 100644 --- a/lib/tools/vsn.mk +++ b/lib/tools/vsn.mk @@ -1 +1 @@ -TOOLS_VSN = 2.6.8 +TOOLS_VSN = 2.6.10 |